feat(hub-client): GetInstalledModuleDocs + per-file MIME
- New getInstalledModuleDocs(name, locale) wraps the new gRPC RPC; callers get text + source path + a not-installed flag so Studio can decide whether to show a Documentation section at all. - runSavedFlow gains an optional fileMimeTypes map keyed parallel to fileInputs. Hard-coded application/octet-stream remains as the fallback, but text.extract and other MIME-gating modules now get an accurate type when the caller knows one. - fetchModuleDocs is marked @Deprecated; it still works for older consumers but Studio no longer calls it. - Proto bindings regenerated against fai/platform proto. Signed-off-by: flemming-it <sf@flemming.it> Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
This commit is contained in:
parent
b8bcae4b14
commit
0879a95aba
4 changed files with 240 additions and 9 deletions
|
|
@ -59,6 +59,7 @@ typedef ClearEventLogResponse = pb.ClearEventLogResponse;
|
|||
typedef DaemonPathsResponse = pb.DaemonPathsResponse;
|
||||
typedef UninstallModuleResponse = pb.UninstallModuleResponse;
|
||||
typedef FetchModuleDocsResponse = pb.FetchModuleDocsResponse;
|
||||
typedef GetInstalledModuleDocsResponse = pb.GetInstalledModuleDocsResponse;
|
||||
typedef ListMcpClientsResponse = pb.ListMcpClientsResponse;
|
||||
typedef McpClientStatus = pb.McpClientStatus;
|
||||
typedef McpClientConfigEntry = pb.McpClientConfigEntry;
|
||||
|
|
@ -240,6 +241,10 @@ class HubClient {
|
|||
/// when set, so Studio doesn't have to handle credentials.
|
||||
/// Errors come back inside [FetchModuleDocsResponse.errorKind]
|
||||
/// (not as exceptions) so the UI can render fallbacks.
|
||||
///
|
||||
/// Deprecated: prefer [getInstalledModuleDocs], which reads
|
||||
/// `MODULE.md`/`MODULE.<locale>.md` shipped inside the bundle.
|
||||
@Deprecated('Use getInstalledModuleDocs (reads from bundle, no network)')
|
||||
Future<FetchModuleDocsResponse> fetchModuleDocs(
|
||||
String name, {
|
||||
String locale = '',
|
||||
|
|
@ -249,6 +254,25 @@ class HubClient {
|
|||
);
|
||||
}
|
||||
|
||||
/// Read the installed module's `MODULE.md` (or `MODULE.<locale>.md`)
|
||||
/// from disk under `~/.fai/modules/<module>/`. No network. Works in
|
||||
/// air-gapped installs. The response distinguishes three states via
|
||||
/// [GetInstalledModuleDocsResponse.notInstalled] and an empty
|
||||
/// [GetInstalledModuleDocsResponse.text]:
|
||||
///
|
||||
/// - text non-empty → docs found, render them
|
||||
/// - text empty + notInstalled=false → installed but bundle had
|
||||
/// no inline docs
|
||||
/// - text empty + notInstalled=true → module is not installed
|
||||
Future<GetInstalledModuleDocsResponse> getInstalledModuleDocs(
|
||||
String name, {
|
||||
String locale = '',
|
||||
}) {
|
||||
return _admin.getInstalledModuleDocs(
|
||||
pb.GetInstalledModuleDocsRequest(name: name, locale: locale),
|
||||
);
|
||||
}
|
||||
|
||||
/// List every configured MCP server with the last-discovery
|
||||
/// snapshot. Drives Studio's MCP-clients editor.
|
||||
Future<ListMcpClientsResponse> listMcpClients() {
|
||||
|
|
@ -489,27 +513,33 @@ class HubClient {
|
|||
/// flow that mixes text and binary inputs (e.g. extract
|
||||
/// taking a `document: bytes`) flows through one RPC:
|
||||
/// * [textInputs] — string values wrapped as text Payloads
|
||||
/// * [fileInputs] — raw bytes wrapped as bytes Payloads,
|
||||
/// with `application/octet-stream` MIME
|
||||
/// type. Caller can override the MIME by
|
||||
/// passing a [bytePayloads] entry instead.
|
||||
/// Both maps default to empty so existing text-only callers
|
||||
/// * [fileInputs] — raw bytes wrapped as bytes Payloads.
|
||||
/// MIME type defaults to
|
||||
/// `application/octet-stream`; clients that
|
||||
/// know the real type pass it via
|
||||
/// [fileMimeTypes] (same key). Modules
|
||||
/// typically gate on MIME — e.g.
|
||||
/// `text.extract` rejects octet-stream — so
|
||||
/// accurate types matter.
|
||||
/// All maps default to empty so existing text-only callers
|
||||
/// are backward-compatible. Keys must not collide between
|
||||
/// the maps; on collision the bytes-shaped entry wins (the
|
||||
/// less-common case is more likely to be the operator's
|
||||
/// intent).
|
||||
/// [textInputs] and [fileInputs]; on collision the bytes-shaped
|
||||
/// entry wins (the less-common case is more likely to be the
|
||||
/// operator's intent).
|
||||
Future<SubmitResponse> runSavedFlow({
|
||||
required String name,
|
||||
Map<String, String> textInputs = const {},
|
||||
Map<String, Uint8List> fileInputs = const {},
|
||||
Map<String, String> fileMimeTypes = const {},
|
||||
}) async {
|
||||
final req = pb.RunSavedFlowRequest()..name = name;
|
||||
for (final entry in textInputs.entries) {
|
||||
req.inputs[entry.key] = pb_common.Payload()..text = entry.value;
|
||||
}
|
||||
for (final entry in fileInputs.entries) {
|
||||
final mime = fileMimeTypes[entry.key] ?? 'application/octet-stream';
|
||||
final bytes = pb_common.Bytes()
|
||||
..mimeType = 'application/octet-stream'
|
||||
..mimeType = mime
|
||||
..data = entry.value;
|
||||
req.inputs[entry.key] = pb_common.Payload()..bytes = bytes;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue