stitchkit 0.53.2 → 0.55.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -1
- package/dist/browser/socket-io.d.ts +11 -1
- package/dist/browser/socket-io.d.ts.map +1 -1
- package/dist/cli.d.ts +2 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +15 -8
- package/dist/contract/define.d.ts +27 -0
- package/dist/contract/define.d.ts.map +1 -1
- package/dist/contract/file-ref.d.ts +13 -0
- package/dist/contract/file-ref.d.ts.map +1 -0
- package/dist/contract/index.d.ts +2 -1
- package/dist/contract/index.d.ts.map +1 -1
- package/dist/contract/index.js +21 -17
- package/dist/files/boundary.d.ts +52 -0
- package/dist/files/boundary.d.ts.map +1 -0
- package/dist/files.d.ts +3 -0
- package/dist/files.d.ts.map +1 -0
- package/dist/files.js +16 -0
- package/dist/{index-c78w2y09.js → index-0gv7k0ra.js} +515 -156
- package/dist/index-1bx83sw4.js +4 -0
- package/dist/index-1k16zv57.js +269 -0
- package/dist/index-6k1937bx.js +20 -0
- package/dist/{index-bmnhqtya.js → index-7eq9va6e.js} +1 -4
- package/dist/{index-yxpe3phd.js → index-cby4ar3v.js} +1 -18
- package/dist/index-h60df7rj.js +110 -0
- package/dist/{index-yez419px.js → index-js7yexng.js} +19 -6
- package/dist/index-k4dftwf7.js +66 -0
- package/dist/{index-pdv1mjjr.js → index-npye5kt7.js} +30 -59
- package/dist/{index-yydqk4fh.js → index-pzc32v75.js} +90 -3
- package/dist/{index-e1es808r.js → index-qh4xevf5.js} +1 -1
- package/dist/{index-gvha32jh.js → index-qx84bqzk.js} +2 -2
- package/dist/{index-pfqjb5xy.js → index-rgd18hx3.js} +23 -5
- package/dist/index-sbdmyz75.js +18 -0
- package/dist/{index-173tvqcp.js → index-v4bbq3p4.js} +9 -114
- package/dist/index.js +162 -35
- package/dist/node.js +30 -27
- package/dist/observability/event.d.ts +2 -16
- package/dist/observability/event.d.ts.map +1 -1
- package/dist/observability/index.js +28 -25
- package/dist/react.js +2 -2
- package/dist/realtime/contract.d.ts +9 -0
- package/dist/realtime/contract.d.ts.map +1 -1
- package/dist/realtime/index.d.ts +2 -1
- package/dist/realtime/index.d.ts.map +1 -1
- package/dist/realtime/request.d.ts +21 -0
- package/dist/realtime/request.d.ts.map +1 -0
- package/dist/realtime/socket.d.ts +3 -0
- package/dist/realtime/socket.d.ts.map +1 -1
- package/dist/remote.d.ts +9 -0
- package/dist/remote.d.ts.map +1 -0
- package/dist/remote.js +79 -0
- package/dist/server/context-contribution.d.ts +10 -0
- package/dist/server/context-contribution.d.ts.map +1 -0
- package/dist/server/context.d.ts.map +1 -1
- package/dist/server/index.d.ts +2 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +133 -88
- package/dist/server/lifecycle.d.ts +4 -0
- package/dist/server/lifecycle.d.ts.map +1 -0
- package/dist/server/middleware/auth.d.ts +35 -7
- package/dist/server/middleware/auth.d.ts.map +1 -1
- package/dist/server/process-signal-common.d.ts +6 -0
- package/dist/server/process-signal-common.d.ts.map +1 -0
- package/dist/server/process-signals.d.ts.map +1 -1
- package/dist/server/types.d.ts +1 -1
- package/dist/server/types.d.ts.map +1 -1
- package/dist/testing/surface-conformance.d.ts +47 -0
- package/dist/testing/surface-conformance.d.ts.map +1 -0
- package/dist/testing/surface-manifest.d.ts +156 -0
- package/dist/testing/surface-manifest.d.ts.map +1 -0
- package/dist/testing.d.ts +2 -0
- package/dist/testing.d.ts.map +1 -1
- package/dist/testing.js +366 -3
- package/dist/tools/agent.d.ts.map +1 -1
- package/dist/tools/async-operation.d.ts +168 -0
- package/dist/tools/async-operation.d.ts.map +1 -0
- package/dist/tools/cli-args.d.ts +1 -1
- package/dist/tools/cli-args.d.ts.map +1 -1
- package/dist/tools/cli-command.d.ts +29 -0
- package/dist/tools/cli-command.d.ts.map +1 -0
- package/dist/tools/cli-format.d.ts +2 -2
- package/dist/tools/cli-format.d.ts.map +1 -1
- package/dist/tools/cli-wait.d.ts +11 -2
- package/dist/tools/cli-wait.d.ts.map +1 -1
- package/dist/tools/cli.d.ts +13 -6
- package/dist/tools/cli.d.ts.map +1 -1
- package/dist/tools/define-download-tool.d.ts +18 -0
- package/dist/tools/define-download-tool.d.ts.map +1 -0
- package/dist/tools/define-upload-tool.d.ts +16 -0
- package/dist/tools/define-upload-tool.d.ts.map +1 -0
- package/dist/tools/define-view-file-tool.d.ts +10 -0
- package/dist/tools/define-view-file-tool.d.ts.map +1 -0
- package/dist/tools/define-wait-tool.d.ts +22 -0
- package/dist/tools/define-wait-tool.d.ts.map +1 -0
- package/dist/tools/download-core.d.ts +20 -0
- package/dist/tools/download-core.d.ts.map +1 -0
- package/dist/tools/execute.d.ts +3 -1
- package/dist/tools/execute.d.ts.map +1 -1
- package/dist/tools/lifecycle.d.ts +4 -0
- package/dist/tools/lifecycle.d.ts.map +1 -0
- package/dist/tools/mcp-round.d.ts +0 -1
- package/dist/tools/mcp-round.d.ts.map +1 -1
- package/dist/tools/mcp-stdio-signals.d.ts +33 -0
- package/dist/tools/mcp-stdio-signals.d.ts.map +1 -0
- package/dist/tools/mount-download.d.ts +10 -4
- package/dist/tools/mount-download.d.ts.map +1 -1
- package/dist/tools/mount-upload.d.ts +4 -2
- package/dist/tools/mount-upload.d.ts.map +1 -1
- package/dist/tools/mount-wait.d.ts.map +1 -1
- package/dist/tools/native-definition.d.ts +15 -0
- package/dist/tools/native-definition.d.ts.map +1 -0
- package/dist/tools/runtime-tool.d.ts +5 -4
- package/dist/tools/runtime-tool.d.ts.map +1 -1
- package/dist/tools/surface.d.ts.map +1 -1
- package/dist/tools/upload-core.d.ts +4 -0
- package/dist/tools/upload-core.d.ts.map +1 -0
- package/dist/tools/view-file.d.ts +84 -23
- package/dist/tools/view-file.d.ts.map +1 -1
- package/dist/tools/wait-core.d.ts +16 -2
- package/dist/tools/wait-core.d.ts.map +1 -1
- package/dist/tools.d.ts +12 -3
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +827 -495
- package/llms-full.txt +720 -53
- package/package.json +10 -2
|
@@ -1,23 +1,33 @@
|
|
|
1
1
|
import {
|
|
2
|
-
isWithinDir
|
|
2
|
+
isWithinDir
|
|
3
|
+
} from "./index-sbdmyz75.js";
|
|
4
|
+
import {
|
|
5
|
+
assertToolName,
|
|
6
|
+
assertUniqueToolName,
|
|
7
|
+
hasUsableChars,
|
|
8
|
+
toToolName
|
|
9
|
+
} from "./index-k4dftwf7.js";
|
|
10
|
+
import {
|
|
3
11
|
jsonSchemaFields,
|
|
4
12
|
toJsonSchema
|
|
5
|
-
} from "./index-
|
|
13
|
+
} from "./index-cby4ar3v.js";
|
|
6
14
|
import {
|
|
7
|
-
AppError,
|
|
8
|
-
STITCH_ERROR_STATUS,
|
|
9
15
|
formatZodError,
|
|
10
16
|
getRequestContext,
|
|
11
|
-
isStitchErrorCode,
|
|
12
|
-
isUnsafeKey,
|
|
13
17
|
normalizeError,
|
|
14
18
|
runWithRequestContext,
|
|
15
|
-
safeJsonParse,
|
|
16
19
|
validateDeclaredOutput
|
|
17
|
-
} from "./index-
|
|
20
|
+
} from "./index-v4bbq3p4.js";
|
|
21
|
+
import {
|
|
22
|
+
AppError,
|
|
23
|
+
STITCH_ERROR_STATUS,
|
|
24
|
+
isStitchErrorCode,
|
|
25
|
+
isUnsafeKey,
|
|
26
|
+
safeJsonParse
|
|
27
|
+
} from "./index-h60df7rj.js";
|
|
18
28
|
import {
|
|
19
29
|
isRecord
|
|
20
|
-
} from "./index-
|
|
30
|
+
} from "./index-7eq9va6e.js";
|
|
21
31
|
|
|
22
32
|
// src/tools/coerce.ts
|
|
23
33
|
import { z } from "zod";
|
|
@@ -671,7 +681,12 @@ async function runToolMethod(method, toolName, rawArgs, context, hooks, lifecycl
|
|
|
671
681
|
}
|
|
672
682
|
try {
|
|
673
683
|
const resolved = await extension.resolve({ ...rawArgs, ...parsed.data });
|
|
674
|
-
callContext = {
|
|
684
|
+
callContext = {
|
|
685
|
+
...context,
|
|
686
|
+
...resolved,
|
|
687
|
+
source: context.source,
|
|
688
|
+
...context.mcp !== undefined && { mcp: context.mcp }
|
|
689
|
+
};
|
|
675
690
|
hookContext = callContext;
|
|
676
691
|
callArgs = Object.fromEntries(Object.entries(rawArgs).filter(([key]) => !extensionKeys.has(key)));
|
|
677
692
|
} catch (err) {
|
|
@@ -764,71 +779,6 @@ async function runToolMethod(method, toolName, rawArgs, context, hooks, lifecycl
|
|
|
764
779
|
}
|
|
765
780
|
}
|
|
766
781
|
|
|
767
|
-
// src/tools/names.ts
|
|
768
|
-
var SINGULAR_EXCEPTIONS = new Set([
|
|
769
|
-
"analytics",
|
|
770
|
-
"status",
|
|
771
|
-
"stats",
|
|
772
|
-
"settings",
|
|
773
|
-
"media",
|
|
774
|
-
"progress",
|
|
775
|
-
"news"
|
|
776
|
-
]);
|
|
777
|
-
var TOOL_NAME_RE = /^[a-zA-Z0-9_-]{1,64}$/;
|
|
778
|
-
function normalizeService(value) {
|
|
779
|
-
return value.replace(/[^a-zA-Z0-9_]/g, "_");
|
|
780
|
-
}
|
|
781
|
-
function normalizeMethod(value) {
|
|
782
|
-
return value.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
783
|
-
}
|
|
784
|
-
function singularize(name) {
|
|
785
|
-
if (SINGULAR_EXCEPTIONS.has(name))
|
|
786
|
-
return name;
|
|
787
|
-
if (name.endsWith("ies"))
|
|
788
|
-
return `${name.slice(0, -3)}y`;
|
|
789
|
-
if (name.endsWith("s") && !name.endsWith("ss"))
|
|
790
|
-
return name.slice(0, -1);
|
|
791
|
-
return name;
|
|
792
|
-
}
|
|
793
|
-
function singularizeTail(name) {
|
|
794
|
-
const cut = name.lastIndexOf("_");
|
|
795
|
-
if (cut === -1)
|
|
796
|
-
return singularize(name);
|
|
797
|
-
return `${name.slice(0, cut + 1)}${singularize(name.slice(cut + 1))}`;
|
|
798
|
-
}
|
|
799
|
-
function hasUsableChars(value) {
|
|
800
|
-
return /[a-zA-Z0-9]/.test(value);
|
|
801
|
-
}
|
|
802
|
-
function assertToolName(name, serviceName, key) {
|
|
803
|
-
const where = `service "${serviceName}", method "${key}"`;
|
|
804
|
-
if (!TOOL_NAME_RE.test(name)) {
|
|
805
|
-
const why = name.length > 64 ? `is ${name.length} characters (max 64) — set an explicit \`toolName\`` : "must match [a-zA-Z0-9_-]";
|
|
806
|
-
throw new Error(`Tool name "${name}" (${where}) ${why}`);
|
|
807
|
-
}
|
|
808
|
-
}
|
|
809
|
-
function toToolName(serviceName, methodName) {
|
|
810
|
-
const normalized = normalizeService(serviceName);
|
|
811
|
-
const singular = singularizeTail(normalized);
|
|
812
|
-
const method = normalizeMethod(methodName);
|
|
813
|
-
if (method === "list")
|
|
814
|
-
return `list_${normalized}`;
|
|
815
|
-
if (method === "get")
|
|
816
|
-
return `get_${singular}`;
|
|
817
|
-
if (method === "create")
|
|
818
|
-
return `create_${singular}`;
|
|
819
|
-
if (method === "update")
|
|
820
|
-
return `update_${singular}`;
|
|
821
|
-
if (method === "delete")
|
|
822
|
-
return `delete_${singular}`;
|
|
823
|
-
const snake = method.replace(/([A-Z])/g, "_$1").toLowerCase();
|
|
824
|
-
return `${snake}_${singular}`.replace(/^_/, "");
|
|
825
|
-
}
|
|
826
|
-
function assertUniqueToolName(name, taken, surface) {
|
|
827
|
-
if (taken) {
|
|
828
|
-
throw new Error(`Duplicate ${surface} "${name}" across mounted operations`);
|
|
829
|
-
}
|
|
830
|
-
}
|
|
831
|
-
|
|
832
782
|
// src/tools/presentation.ts
|
|
833
783
|
import { z as z3 } from "zod";
|
|
834
784
|
function withoutDialect(schema) {
|
|
@@ -1020,6 +970,100 @@ function formatToolError(result, toolName, errorHint) {
|
|
|
1020
970
|
return err;
|
|
1021
971
|
}
|
|
1022
972
|
|
|
973
|
+
// src/tools/runtime-tool.ts
|
|
974
|
+
function defineRuntimeTool(definition) {
|
|
975
|
+
if (definition.transports?.length === 0) {
|
|
976
|
+
throw new Error(`Runtime tool "${definition.name}" must expose at least one transport`);
|
|
977
|
+
}
|
|
978
|
+
return definition;
|
|
979
|
+
}
|
|
980
|
+
function createRuntimeToolFactory(config) {
|
|
981
|
+
function parseContext(context) {
|
|
982
|
+
const parsed = config.context.parse(context);
|
|
983
|
+
return {
|
|
984
|
+
...context,
|
|
985
|
+
...parsed,
|
|
986
|
+
params: undefined,
|
|
987
|
+
input: context.input,
|
|
988
|
+
...context.mcp !== undefined && { mcp: context.mcp }
|
|
989
|
+
};
|
|
990
|
+
}
|
|
991
|
+
function define(definition) {
|
|
992
|
+
if (definition.output !== undefined) {
|
|
993
|
+
const { action: action2, method: method2, meta: meta2, handler: handler2, ...tool2 } = definition;
|
|
994
|
+
const identity2 = {
|
|
995
|
+
serviceName: config.serviceName,
|
|
996
|
+
action: action2,
|
|
997
|
+
method: method2,
|
|
998
|
+
...config.scope !== undefined && { scope: config.scope },
|
|
999
|
+
...(meta2 ?? config.meta) !== undefined && { meta: meta2 ?? config.meta }
|
|
1000
|
+
};
|
|
1001
|
+
return defineRuntimeTool({
|
|
1002
|
+
...tool2,
|
|
1003
|
+
identity: identity2,
|
|
1004
|
+
handler: (context) => handler2(parseContext(context))
|
|
1005
|
+
});
|
|
1006
|
+
}
|
|
1007
|
+
const { action, method, meta, handler, ...tool } = definition;
|
|
1008
|
+
const identity = {
|
|
1009
|
+
serviceName: config.serviceName,
|
|
1010
|
+
action,
|
|
1011
|
+
method,
|
|
1012
|
+
...config.scope !== undefined && { scope: config.scope },
|
|
1013
|
+
...(meta ?? config.meta) !== undefined && { meta: meta ?? config.meta }
|
|
1014
|
+
};
|
|
1015
|
+
return defineRuntimeTool({
|
|
1016
|
+
...tool,
|
|
1017
|
+
identity,
|
|
1018
|
+
handler: (context) => handler(parseContext(context))
|
|
1019
|
+
});
|
|
1020
|
+
}
|
|
1021
|
+
return { define };
|
|
1022
|
+
}
|
|
1023
|
+
function runtimeToolSupports(definition, transport) {
|
|
1024
|
+
if (definition.transports?.length === 0) {
|
|
1025
|
+
throw new Error(`Runtime tool "${definition.name}" must expose at least one transport`);
|
|
1026
|
+
}
|
|
1027
|
+
if (!definition.transports)
|
|
1028
|
+
return transport !== "CLI";
|
|
1029
|
+
return definition.transports.includes(transport);
|
|
1030
|
+
}
|
|
1031
|
+
function runtimeToolIdentity(definition) {
|
|
1032
|
+
return {
|
|
1033
|
+
method: definition.identity.method,
|
|
1034
|
+
desc: definition.description,
|
|
1035
|
+
serviceName: definition.identity.serviceName,
|
|
1036
|
+
key: definition.identity.action,
|
|
1037
|
+
toolName: definition.name,
|
|
1038
|
+
scope: definition.identity.scope,
|
|
1039
|
+
meta: definition.identity.meta,
|
|
1040
|
+
annotations: definition.annotations,
|
|
1041
|
+
ui: definition.ui,
|
|
1042
|
+
mcp: definition.mcp
|
|
1043
|
+
};
|
|
1044
|
+
}
|
|
1045
|
+
function runtimeToolMountable(definition, assertName = true) {
|
|
1046
|
+
if (assertName) {
|
|
1047
|
+
assertToolName(definition.name, definition.identity.serviceName, definition.identity.action);
|
|
1048
|
+
}
|
|
1049
|
+
const method = {
|
|
1050
|
+
...runtimeToolIdentity(definition),
|
|
1051
|
+
inputSchema: definition.input,
|
|
1052
|
+
outputSchema: definition.output,
|
|
1053
|
+
handler: definition.handler
|
|
1054
|
+
};
|
|
1055
|
+
return {
|
|
1056
|
+
method,
|
|
1057
|
+
name: definition.name,
|
|
1058
|
+
argumentSchema: definition.input,
|
|
1059
|
+
presentationSchema: buildToolPresentationSchema({
|
|
1060
|
+
inputSchema: definition.input,
|
|
1061
|
+
unrepresentable: "any"
|
|
1062
|
+
}),
|
|
1063
|
+
shouldExtend: false
|
|
1064
|
+
};
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1023
1067
|
// src/tools/cli-args.ts
|
|
1024
1068
|
import { z as z5 } from "zod";
|
|
1025
1069
|
var NUMERIC_VALUE = /^-(\d+\.?\d*|\.\d+)$/;
|
|
@@ -1341,6 +1385,50 @@ function parseCliArgs(argv, schema, config = {}) {
|
|
|
1341
1385
|
return { toolArgs, options };
|
|
1342
1386
|
}
|
|
1343
1387
|
|
|
1388
|
+
// src/tools/cli-command.ts
|
|
1389
|
+
function defineCliCommand(definition) {
|
|
1390
|
+
return definition;
|
|
1391
|
+
}
|
|
1392
|
+
function cliCommandPresentationSchema(definition) {
|
|
1393
|
+
return buildToolPresentationSchema({
|
|
1394
|
+
inputSchema: definition.input,
|
|
1395
|
+
unrepresentable: "any"
|
|
1396
|
+
});
|
|
1397
|
+
}
|
|
1398
|
+
async function executeCliCommand(definition, rawArgs, options, writers, coerceJson) {
|
|
1399
|
+
let parsed;
|
|
1400
|
+
try {
|
|
1401
|
+
parsed = definition.input.safeParse(coerceJson ? coerceJsonArgs(rawArgs, definition.input) : rawArgs);
|
|
1402
|
+
} catch (error) {
|
|
1403
|
+
return toolResultFromError(error);
|
|
1404
|
+
}
|
|
1405
|
+
if (!parsed.success) {
|
|
1406
|
+
return {
|
|
1407
|
+
ok: false,
|
|
1408
|
+
code: "VALIDATION_ERROR",
|
|
1409
|
+
details: { message: `Invalid input: ${formatZodError(parsed.error)}` }
|
|
1410
|
+
};
|
|
1411
|
+
}
|
|
1412
|
+
try {
|
|
1413
|
+
const data = await definition.handler({
|
|
1414
|
+
input: parsed.data,
|
|
1415
|
+
options,
|
|
1416
|
+
...writers
|
|
1417
|
+
});
|
|
1418
|
+
const checked = validateDeclaredOutput(definition.output, data);
|
|
1419
|
+
if (!checked.ok) {
|
|
1420
|
+
return {
|
|
1421
|
+
ok: false,
|
|
1422
|
+
code: "INTERNAL_SERVER_ERROR",
|
|
1423
|
+
details: { message: checked.message }
|
|
1424
|
+
};
|
|
1425
|
+
}
|
|
1426
|
+
return { ok: true, data: checked.data };
|
|
1427
|
+
} catch (error) {
|
|
1428
|
+
return toolResultFromError(error);
|
|
1429
|
+
}
|
|
1430
|
+
}
|
|
1431
|
+
|
|
1344
1432
|
// src/tools/cli-format.ts
|
|
1345
1433
|
var DEFAULT_EXIT_CODES = {
|
|
1346
1434
|
VALIDATION_ERROR: 1,
|
|
@@ -1351,19 +1439,21 @@ var DEFAULT_EXIT_CODES = {
|
|
|
1351
1439
|
CONFLICT: 5,
|
|
1352
1440
|
RATE_LIMITED: 6,
|
|
1353
1441
|
TIMEOUT: 7,
|
|
1442
|
+
WAIT_FAILED: 1,
|
|
1354
1443
|
INTERNAL_SERVER_ERROR: 1
|
|
1355
1444
|
};
|
|
1356
1445
|
function emitResult(result, writers, opts) {
|
|
1357
1446
|
if (result.ok) {
|
|
1358
1447
|
if (result.data === undefined)
|
|
1359
1448
|
return 0;
|
|
1360
|
-
const
|
|
1361
|
-
writers.stdout(`${
|
|
1449
|
+
const text2 = opts.json ? JSON.stringify(result.data) : JSON.stringify(result.data, null, 2);
|
|
1450
|
+
writers.stdout(`${text2}
|
|
1362
1451
|
`);
|
|
1363
1452
|
return 0;
|
|
1364
1453
|
}
|
|
1365
1454
|
const error = formatToolError(result, opts.toolName, opts.errorHint);
|
|
1366
|
-
|
|
1455
|
+
const text = opts.json ? JSON.stringify(error) : JSON.stringify(error, null, 2);
|
|
1456
|
+
writers.stderr(`${text}
|
|
1367
1457
|
`);
|
|
1368
1458
|
const codes = opts.exitCodes ?? DEFAULT_EXIT_CODES;
|
|
1369
1459
|
return codes[result.code] ?? 1;
|
|
@@ -1372,45 +1462,140 @@ function emitResult(result, writers, opts) {
|
|
|
1372
1462
|
// src/tools/wait-core.ts
|
|
1373
1463
|
var DEFAULT_BACKOFF = [2, 3, 5, 5, 8, 10];
|
|
1374
1464
|
var DEFAULT_TIMEOUT = 600;
|
|
1375
|
-
|
|
1465
|
+
|
|
1466
|
+
class WaitTimeoutError extends Error {
|
|
1467
|
+
constructor() {
|
|
1468
|
+
super("wait deadline elapsed before the first snapshot");
|
|
1469
|
+
this.name = "WaitTimeoutError";
|
|
1470
|
+
}
|
|
1471
|
+
}
|
|
1472
|
+
var defaultSleep = (ms, signal) => {
|
|
1473
|
+
signal?.throwIfAborted();
|
|
1474
|
+
return new Promise((resolve, reject) => {
|
|
1475
|
+
let settled = false;
|
|
1476
|
+
const settle = (action) => {
|
|
1477
|
+
if (settled)
|
|
1478
|
+
return;
|
|
1479
|
+
settled = true;
|
|
1480
|
+
signal?.removeEventListener("abort", onAbort);
|
|
1481
|
+
action();
|
|
1482
|
+
};
|
|
1483
|
+
const timer = setTimeout(() => {
|
|
1484
|
+
settle(resolve);
|
|
1485
|
+
}, ms);
|
|
1486
|
+
timer.unref?.();
|
|
1487
|
+
const onAbort = () => {
|
|
1488
|
+
clearTimeout(timer);
|
|
1489
|
+
settle(() => reject(signal?.reason ?? new Error("aborted")));
|
|
1490
|
+
};
|
|
1491
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
1492
|
+
if (signal?.aborted)
|
|
1493
|
+
onAbort();
|
|
1494
|
+
});
|
|
1495
|
+
};
|
|
1376
1496
|
async function pollUntil(params) {
|
|
1377
1497
|
const backoff = params.backoff?.length ? params.backoff : DEFAULT_BACKOFF;
|
|
1378
1498
|
const lastBackoff = backoff[backoff.length - 1] ?? 5;
|
|
1379
1499
|
const timeoutSec = params.timeoutSec ?? DEFAULT_TIMEOUT;
|
|
1380
1500
|
const sleep = params.sleepFn ?? defaultSleep;
|
|
1381
|
-
const
|
|
1501
|
+
const now = params.nowFn ?? (() => performance.now());
|
|
1502
|
+
const startedAt = now();
|
|
1503
|
+
const deadline = startedAt + timeoutSec * 1000;
|
|
1504
|
+
let last;
|
|
1382
1505
|
for (let attempt = 0;; attempt++) {
|
|
1383
|
-
|
|
1506
|
+
params.signal?.throwIfAborted();
|
|
1507
|
+
const remainingBeforePoll = Math.max(0, deadline - now());
|
|
1508
|
+
if (remainingBeforePoll === 0 && last) {
|
|
1509
|
+
return { state: last.state, timedOut: true };
|
|
1510
|
+
}
|
|
1511
|
+
const deadlineController = new AbortController;
|
|
1512
|
+
const onCallerAbort = () => deadlineController.abort(params.signal?.reason);
|
|
1513
|
+
params.signal?.addEventListener("abort", onCallerAbort, { once: true });
|
|
1514
|
+
const deadlineTimer = setTimeout(() => deadlineController.abort(new WaitTimeoutError), remainingBeforePoll);
|
|
1515
|
+
deadlineTimer.unref?.();
|
|
1516
|
+
let state;
|
|
1517
|
+
try {
|
|
1518
|
+
state = await params.poll(deadlineController.signal);
|
|
1519
|
+
} catch (error) {
|
|
1520
|
+
if (params.signal?.aborted)
|
|
1521
|
+
throw params.signal.reason ?? error;
|
|
1522
|
+
if (deadlineController.signal.aborted && now() >= deadline) {
|
|
1523
|
+
if (last)
|
|
1524
|
+
return { state: last.state, timedOut: true };
|
|
1525
|
+
throw new WaitTimeoutError;
|
|
1526
|
+
}
|
|
1527
|
+
throw error;
|
|
1528
|
+
} finally {
|
|
1529
|
+
clearTimeout(deadlineTimer);
|
|
1530
|
+
params.signal?.removeEventListener("abort", onCallerAbort);
|
|
1531
|
+
}
|
|
1532
|
+
params.signal?.throwIfAborted();
|
|
1533
|
+
last = { state };
|
|
1384
1534
|
if (params.done(state))
|
|
1385
1535
|
return { state, timedOut: false };
|
|
1386
|
-
const
|
|
1536
|
+
const elapsedMs = now() - startedAt;
|
|
1537
|
+
const elapsedSec = elapsedMs / 1000;
|
|
1387
1538
|
params.onTick?.(attempt + 1, elapsedSec);
|
|
1388
1539
|
if (elapsedSec >= timeoutSec)
|
|
1389
1540
|
return { state, timedOut: true };
|
|
1390
1541
|
const waitSec = backoff[Math.min(attempt, backoff.length - 1)] ?? lastBackoff;
|
|
1391
|
-
await sleep(waitSec * 1000);
|
|
1542
|
+
await sleep(Math.min(waitSec * 1000, Math.max(0, deadline - now())), params.signal);
|
|
1392
1543
|
}
|
|
1393
1544
|
}
|
|
1545
|
+
function runWaitOperation(params) {
|
|
1546
|
+
return pollUntil({
|
|
1547
|
+
poll: (signal) => params.poll(params.input, signal),
|
|
1548
|
+
done: params.done,
|
|
1549
|
+
backoff: params.backoff,
|
|
1550
|
+
timeoutSec: params.timeoutSec,
|
|
1551
|
+
sleepFn: params.sleepFn,
|
|
1552
|
+
onTick: params.onTick,
|
|
1553
|
+
signal: params.signal,
|
|
1554
|
+
nowFn: params.nowFn
|
|
1555
|
+
});
|
|
1556
|
+
}
|
|
1394
1557
|
|
|
1395
1558
|
// src/tools/cli-wait.ts
|
|
1396
1559
|
var DEFAULT_TIMEOUT2 = 600;
|
|
1560
|
+
function waitFailure(tool, result) {
|
|
1561
|
+
return {
|
|
1562
|
+
ok: false,
|
|
1563
|
+
code: "WAIT_FAILED",
|
|
1564
|
+
details: {
|
|
1565
|
+
message: `"${tool}" reached a terminal failed state`,
|
|
1566
|
+
result
|
|
1567
|
+
}
|
|
1568
|
+
};
|
|
1569
|
+
}
|
|
1397
1570
|
async function pollUntilDone(params) {
|
|
1398
1571
|
const { initial, wait, call } = params;
|
|
1399
1572
|
if (!initial.ok)
|
|
1400
1573
|
return initial;
|
|
1574
|
+
if (wait.failed?.(initial.data))
|
|
1575
|
+
return waitFailure(wait.tool, initial.data);
|
|
1401
1576
|
if (wait.done(initial.data))
|
|
1402
1577
|
return initial;
|
|
1403
1578
|
const pollArgs = wait.poll(initial.data);
|
|
1404
1579
|
if (!pollArgs)
|
|
1405
1580
|
return initial;
|
|
1406
1581
|
const timeoutSec = params.timeoutSec ?? wait.timeout ?? DEFAULT_TIMEOUT2;
|
|
1582
|
+
let failedState;
|
|
1407
1583
|
const { state, timedOut } = await pollUntil({
|
|
1408
1584
|
poll: () => call(wait.tool, pollArgs),
|
|
1409
|
-
done: (result) =>
|
|
1585
|
+
done: (result) => {
|
|
1586
|
+
if (!result.ok)
|
|
1587
|
+
return true;
|
|
1588
|
+
if (wait.failed?.(result.data)) {
|
|
1589
|
+
failedState = result;
|
|
1590
|
+
return true;
|
|
1591
|
+
}
|
|
1592
|
+
return wait.done(result.data);
|
|
1593
|
+
},
|
|
1410
1594
|
backoff: wait.backoff,
|
|
1411
1595
|
timeoutSec,
|
|
1412
1596
|
sleepFn: params.sleepFn,
|
|
1413
|
-
onTick: params.onTick
|
|
1597
|
+
onTick: params.onTick,
|
|
1598
|
+
signal: params.signal
|
|
1414
1599
|
});
|
|
1415
1600
|
if (timedOut) {
|
|
1416
1601
|
return {
|
|
@@ -1419,6 +1604,8 @@ async function pollUntilDone(params) {
|
|
|
1419
1604
|
details: { message: `Timed out after ${timeoutSec}s waiting for "${wait.tool}"` }
|
|
1420
1605
|
};
|
|
1421
1606
|
}
|
|
1607
|
+
if (state === failedState && state.ok)
|
|
1608
|
+
return waitFailure(wait.tool, state.data);
|
|
1422
1609
|
return state;
|
|
1423
1610
|
}
|
|
1424
1611
|
|
|
@@ -1733,9 +1920,56 @@ async function writeDownload(root, target, data) {
|
|
|
1733
1920
|
await writeFile(target, data);
|
|
1734
1921
|
}
|
|
1735
1922
|
|
|
1923
|
+
// src/tools/surface.ts
|
|
1924
|
+
function duplicateLabel(transport) {
|
|
1925
|
+
if (transport === "MCP")
|
|
1926
|
+
return "MCP tool name";
|
|
1927
|
+
if (transport === "AGENT")
|
|
1928
|
+
return "agent tool name";
|
|
1929
|
+
return "CLI command";
|
|
1930
|
+
}
|
|
1931
|
+
function collectToolSurface({
|
|
1932
|
+
surface,
|
|
1933
|
+
transport,
|
|
1934
|
+
assertUniqueNames = true,
|
|
1935
|
+
...collectConfig
|
|
1936
|
+
}) {
|
|
1937
|
+
const entries = [];
|
|
1938
|
+
const names = new Set;
|
|
1939
|
+
const append = (entry) => {
|
|
1940
|
+
if (assertUniqueNames) {
|
|
1941
|
+
assertUniqueToolName(entry.mountable.name, names.has(entry.mountable.name), duplicateLabel(transport));
|
|
1942
|
+
}
|
|
1943
|
+
names.add(entry.mountable.name);
|
|
1944
|
+
entries.push(entry);
|
|
1945
|
+
};
|
|
1946
|
+
for (const service of surface.services ?? []) {
|
|
1947
|
+
for (const mountable of collectTools(service, transport, collectConfig)) {
|
|
1948
|
+
append({
|
|
1949
|
+
kind: "contract",
|
|
1950
|
+
service: service.name,
|
|
1951
|
+
action: mountable.method.key,
|
|
1952
|
+
mountable
|
|
1953
|
+
});
|
|
1954
|
+
}
|
|
1955
|
+
}
|
|
1956
|
+
for (const definition of surface.runtimeTools ?? []) {
|
|
1957
|
+
if (!runtimeToolSupports(definition, transport))
|
|
1958
|
+
continue;
|
|
1959
|
+
append({
|
|
1960
|
+
kind: "runtime",
|
|
1961
|
+
service: definition.identity.serviceName,
|
|
1962
|
+
action: definition.identity.action,
|
|
1963
|
+
mountable: runtimeToolMountable(definition, collectConfig.assertNames),
|
|
1964
|
+
definition
|
|
1965
|
+
});
|
|
1966
|
+
}
|
|
1967
|
+
return entries;
|
|
1968
|
+
}
|
|
1969
|
+
|
|
1736
1970
|
// src/tools/cli.ts
|
|
1737
1971
|
var GLOBAL_OPTIONS = [
|
|
1738
|
-
["--json", "Emit
|
|
1972
|
+
["--json", "Emit compact success/error JSON records (for scripts)"],
|
|
1739
1973
|
["--wait", "Block-poll an async result to a terminal state"],
|
|
1740
1974
|
["--wait-timeout <s>", "Override the --wait timeout in seconds"],
|
|
1741
1975
|
["--output-dir <dir>", "Download result media into a directory"],
|
|
@@ -1810,7 +2044,7 @@ function collectPassthrough(toolArgs, field, knownKeys) {
|
|
|
1810
2044
|
const base = passthroughBase(toolArgs[field]);
|
|
1811
2045
|
toolArgs[field] = base ? { ...base, ...bag } : bag;
|
|
1812
2046
|
}
|
|
1813
|
-
function renderTopHelp(name, version,
|
|
2047
|
+
function renderTopHelp(name, version, commands) {
|
|
1814
2048
|
const lines = [
|
|
1815
2049
|
`${name} ${version}`,
|
|
1816
2050
|
"",
|
|
@@ -1818,9 +2052,9 @@ function renderTopHelp(name, version, tools) {
|
|
|
1818
2052
|
"",
|
|
1819
2053
|
"Commands:"
|
|
1820
2054
|
];
|
|
1821
|
-
const width = Math.max(0, ...[...
|
|
1822
|
-
for (const [command,
|
|
1823
|
-
lines.push(` ${padRight(command, width)} ${summarize(
|
|
2055
|
+
const width = Math.max(0, ...[...commands.keys()].map((key) => key.length));
|
|
2056
|
+
for (const [command, descriptor] of commands) {
|
|
2057
|
+
lines.push(` ${padRight(command, width)} ${summarize(descriptor.description)}`);
|
|
1824
2058
|
}
|
|
1825
2059
|
lines.push("", "Global options:");
|
|
1826
2060
|
const optWidth = Math.max(...GLOBAL_OPTIONS.map(([flag]) => flag.length));
|
|
@@ -1831,16 +2065,42 @@ function renderTopHelp(name, version, tools) {
|
|
|
1831
2065
|
`)}
|
|
1832
2066
|
`;
|
|
1833
2067
|
}
|
|
1834
|
-
function renderCommandHelp(name, command,
|
|
1835
|
-
const
|
|
1836
|
-
const
|
|
2068
|
+
function renderCommandHelp(name, command, descriptor) {
|
|
2069
|
+
const fields = jsonSchemaFields(descriptor.presentationSchema);
|
|
2070
|
+
const fieldsByName = new Map(fields.map((field) => [field.name, field]));
|
|
2071
|
+
const positionals = [];
|
|
2072
|
+
for (const [fieldName, info] of describeSchemaFields(descriptor.argumentSchema)) {
|
|
2073
|
+
if (info.kind === "boolean")
|
|
2074
|
+
continue;
|
|
2075
|
+
const field = fieldsByName.get(fieldName);
|
|
2076
|
+
if (field)
|
|
2077
|
+
positionals.push(field);
|
|
2078
|
+
}
|
|
2079
|
+
const positionalSyntax = new Map(positionals.map((field) => [
|
|
2080
|
+
field.name,
|
|
2081
|
+
field.required ? `<${field.name}>` : `[${field.name}]`
|
|
2082
|
+
]));
|
|
2083
|
+
const usage = [
|
|
2084
|
+
`Usage: ${name} ${command}`,
|
|
2085
|
+
...positionals.map((field) => positionalSyntax.get(field.name) ?? field.name),
|
|
2086
|
+
"[--flags]"
|
|
2087
|
+
].join(" ");
|
|
2088
|
+
const lines = [descriptor.description, "", usage, ""];
|
|
1837
2089
|
if (fields.length > 0) {
|
|
1838
2090
|
lines.push("Arguments:");
|
|
1839
|
-
const
|
|
2091
|
+
const labels = new Map(fields.map((field) => {
|
|
2092
|
+
const positional = positionalSyntax.get(field.name);
|
|
2093
|
+
return [
|
|
2094
|
+
field.name,
|
|
2095
|
+
positional ? `${positional} | --${field.name}` : `--${field.name}`
|
|
2096
|
+
];
|
|
2097
|
+
}));
|
|
2098
|
+
const width = Math.max(...fields.map((field) => labels.get(field.name)?.length ?? 0));
|
|
1840
2099
|
for (const f of fields) {
|
|
1841
2100
|
const req = f.required ? " (required)" : "";
|
|
1842
2101
|
const desc = f.description ? ` — ${f.description}` : "";
|
|
1843
|
-
|
|
2102
|
+
const label = labels.get(f.name) ?? `--${f.name}`;
|
|
2103
|
+
lines.push(` ${padRight(label, width)} <${typeLabel(f.schema)}>${req}${desc}`);
|
|
1844
2104
|
}
|
|
1845
2105
|
lines.push("");
|
|
1846
2106
|
}
|
|
@@ -1848,6 +2108,55 @@ function renderCommandHelp(name, command, tool) {
|
|
|
1848
2108
|
`)}
|
|
1849
2109
|
`;
|
|
1850
2110
|
}
|
|
2111
|
+
function managedDescriptor(tool) {
|
|
2112
|
+
return {
|
|
2113
|
+
description: tool.method.desc,
|
|
2114
|
+
argumentSchema: tool.argumentSchema,
|
|
2115
|
+
presentationSchema: tool.presentationSchema
|
|
2116
|
+
};
|
|
2117
|
+
}
|
|
2118
|
+
function nativeDescriptor(definition) {
|
|
2119
|
+
return {
|
|
2120
|
+
description: definition.description,
|
|
2121
|
+
argumentSchema: definition.input,
|
|
2122
|
+
presentationSchema: cliCommandPresentationSchema(definition)
|
|
2123
|
+
};
|
|
2124
|
+
}
|
|
2125
|
+
function assertCommandShape(name, descriptor, exists) {
|
|
2126
|
+
assertUniqueToolName(name, exists, "CLI command");
|
|
2127
|
+
if (name === "help" || name === "version") {
|
|
2128
|
+
throw new Error(`[stitchkit] CLI command "${name}" is reserved`);
|
|
2129
|
+
}
|
|
2130
|
+
const conflicting = jsonSchemaFields(descriptor.presentationSchema).map((field) => field.name).filter((field) => RESERVED_CLI_OPTIONS.has(field));
|
|
2131
|
+
if (conflicting.length > 0) {
|
|
2132
|
+
throw new Error(`[stitchkit] CLI command "${name}" declares reserved option field(s): ${conflicting.join(", ")}`);
|
|
2133
|
+
}
|
|
2134
|
+
}
|
|
2135
|
+
async function prepareInvocation(command, commandArgv, descriptor, config, readStdin) {
|
|
2136
|
+
let parsed;
|
|
2137
|
+
try {
|
|
2138
|
+
parsed = parseCliArgs(commandArgv, descriptor.argumentSchema, {
|
|
2139
|
+
allowUnknown: config.passthrough?.[command] !== undefined,
|
|
2140
|
+
knownFields: jsonSchemaFields(descriptor.presentationSchema).map((field) => field.name)
|
|
2141
|
+
});
|
|
2142
|
+
} catch (error) {
|
|
2143
|
+
if (!(error instanceof CliArgumentError))
|
|
2144
|
+
throw error;
|
|
2145
|
+
return { ok: false, message: error.message };
|
|
2146
|
+
}
|
|
2147
|
+
const { toolArgs, options } = parsed;
|
|
2148
|
+
const firstUnset = jsonSchemaFields(descriptor.presentationSchema).find((field) => field.required && !(field.name in toolArgs) && field.schema.type !== "boolean");
|
|
2149
|
+
if (firstUnset) {
|
|
2150
|
+
const piped = await readStdin();
|
|
2151
|
+
if (piped !== null)
|
|
2152
|
+
toolArgs[firstUnset.name] = piped;
|
|
2153
|
+
}
|
|
2154
|
+
const passthroughField = config.passthrough?.[command];
|
|
2155
|
+
if (passthroughField) {
|
|
2156
|
+
collectPassthrough(toolArgs, passthroughField, objectShapeKeys(descriptor.argumentSchema));
|
|
2157
|
+
}
|
|
2158
|
+
return { ok: true, toolArgs, options };
|
|
2159
|
+
}
|
|
1851
2160
|
var DEFAULT_DOWNLOAD_MAX_BYTES = 100 * 1024 * 1024;
|
|
1852
2161
|
async function downloadResults(files, dir, stderr, quiet, allowPrivate, maxBytes, timeoutMs) {
|
|
1853
2162
|
const root = resolve(dir);
|
|
@@ -1886,66 +2195,121 @@ async function createCli(config) {
|
|
|
1886
2195
|
const exit = config.exit ?? ((code) => void process.exit(code));
|
|
1887
2196
|
const argv = config.argv ?? process.argv.slice(2);
|
|
1888
2197
|
const readStdin = config.stdin ?? readPipedStdin;
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
const context = config.context?.(auth);
|
|
1892
|
-
const tools = new Map;
|
|
1893
|
-
for (const service of services) {
|
|
1894
|
-
for (const mountable of collectTools(service, "CLI")) {
|
|
1895
|
-
assertUniqueToolName(mountable.name, tools.has(mountable.name), "CLI command");
|
|
1896
|
-
if (mountable.name === "help" || mountable.name === "version") {
|
|
1897
|
-
throw new Error(`[stitchkit] CLI command "${mountable.name}" is reserved`);
|
|
1898
|
-
}
|
|
1899
|
-
const conflicting = jsonSchemaFields(mountable.presentationSchema).map((field) => field.name).filter((name) => RESERVED_CLI_OPTIONS.has(name));
|
|
1900
|
-
if (conflicting.length > 0) {
|
|
1901
|
-
throw new Error(`[stitchkit] CLI command "${mountable.name}" declares reserved option field(s): ${conflicting.join(", ")}`);
|
|
1902
|
-
}
|
|
1903
|
-
tools.set(mountable.name, mountable);
|
|
1904
|
-
}
|
|
2198
|
+
if (config.auth !== undefined && config.resolveAuth !== undefined) {
|
|
2199
|
+
throw new Error("[stitchkit] createCli: use either auth or resolveAuth, not both");
|
|
1905
2200
|
}
|
|
1906
|
-
const
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
});
|
|
1914
|
-
const [command, ...commandArgv] = argv;
|
|
1915
|
-
if (command === undefined || command === "help" || command === "--help" || command === "-h") {
|
|
1916
|
-
stdout(renderTopHelp(config.name, config.version, tools));
|
|
1917
|
-
return exit(0);
|
|
2201
|
+
const nativeCommands = new Map;
|
|
2202
|
+
const nativeHelp = new Map;
|
|
2203
|
+
for (const definition of config.commands ?? []) {
|
|
2204
|
+
const descriptor2 = nativeDescriptor(definition);
|
|
2205
|
+
assertCommandShape(definition.name, descriptor2, nativeCommands.has(definition.name));
|
|
2206
|
+
nativeCommands.set(definition.name, definition);
|
|
2207
|
+
nativeHelp.set(definition.name, descriptor2);
|
|
1918
2208
|
}
|
|
2209
|
+
const [command, ...commandArgv] = argv;
|
|
1919
2210
|
if (command === "--version" || command === "version") {
|
|
1920
2211
|
stdout(`${config.name} ${config.version}
|
|
1921
2212
|
`);
|
|
1922
2213
|
return exit(0);
|
|
1923
2214
|
}
|
|
1924
|
-
const
|
|
2215
|
+
const beforeSeparator = commandArgv.indexOf("--") === -1 ? commandArgv : commandArgv.slice(0, commandArgv.indexOf("--"));
|
|
2216
|
+
const helpRequested = beforeSeparator.includes("--help") || beforeSeparator.includes("-h");
|
|
2217
|
+
const native = command === undefined ? undefined : nativeCommands.get(command);
|
|
2218
|
+
if (native && command !== undefined) {
|
|
2219
|
+
const descriptor2 = nativeHelp.get(command);
|
|
2220
|
+
if (!descriptor2)
|
|
2221
|
+
throw new Error("[stitchkit] native CLI descriptor invariant failed");
|
|
2222
|
+
if (helpRequested) {
|
|
2223
|
+
stdout(renderCommandHelp(config.name, command, descriptor2));
|
|
2224
|
+
return exit(0);
|
|
2225
|
+
}
|
|
2226
|
+
const prepared2 = await prepareInvocation(command, commandArgv, descriptor2, config, readStdin);
|
|
2227
|
+
if (!prepared2.ok) {
|
|
2228
|
+
stderr(`${prepared2.message}
|
|
2229
|
+
`);
|
|
2230
|
+
return exit(2);
|
|
2231
|
+
}
|
|
2232
|
+
const { toolArgs: toolArgs2, options: options2 } = prepared2;
|
|
2233
|
+
if (options2.help) {
|
|
2234
|
+
stdout(renderCommandHelp(config.name, command, descriptor2));
|
|
2235
|
+
return exit(0);
|
|
2236
|
+
}
|
|
2237
|
+
if (options2.wait) {
|
|
2238
|
+
stderr(`--wait is not configured for native command "${command}"
|
|
2239
|
+
`);
|
|
2240
|
+
return exit(2);
|
|
2241
|
+
}
|
|
2242
|
+
if (options2.waitTimeout !== undefined) {
|
|
2243
|
+
stderr(`--wait-timeout requires --wait
|
|
2244
|
+
`);
|
|
2245
|
+
return exit(2);
|
|
2246
|
+
}
|
|
2247
|
+
if (options2.outputDir !== undefined) {
|
|
2248
|
+
stderr(`--output-dir is not configured for native commands
|
|
2249
|
+
`);
|
|
2250
|
+
return exit(2);
|
|
2251
|
+
}
|
|
2252
|
+
if (options2.dryRun) {
|
|
2253
|
+
stdout(`${JSON.stringify({ command, args: toolArgs2 }, null, 2)}
|
|
2254
|
+
`);
|
|
2255
|
+
return exit(0);
|
|
2256
|
+
}
|
|
2257
|
+
const result2 = await executeCliCommand(native, toolArgs2, options2, { stdout, stderr }, config.coerceJsonArgs ?? true);
|
|
2258
|
+
const exitCode2 = emitResult(result2, { stdout, stderr }, {
|
|
2259
|
+
json: options2.json,
|
|
2260
|
+
toolName: command,
|
|
2261
|
+
errorHint: config.errorHint,
|
|
2262
|
+
exitCodes: { ...DEFAULT_EXIT_CODES, ...config.exitCodes }
|
|
2263
|
+
});
|
|
2264
|
+
return exit(exitCode2);
|
|
2265
|
+
}
|
|
2266
|
+
let authPromise;
|
|
2267
|
+
const resolveIdentity = () => {
|
|
2268
|
+
authPromise ??= Promise.resolve(config.resolveAuth ? config.resolveAuth() : config.auth);
|
|
2269
|
+
return authPromise;
|
|
2270
|
+
};
|
|
2271
|
+
const dynamicSurface = typeof config.services === "function" || typeof config.runtimeTools === "function";
|
|
2272
|
+
const buildManagedSurface = async (forExecution) => {
|
|
2273
|
+
const auth = dynamicSurface || forExecution ? await resolveIdentity() : undefined;
|
|
2274
|
+
const services = typeof config.services === "function" ? config.services(auth) : config.services ?? [];
|
|
2275
|
+
const runtimeTools = typeof config.runtimeTools === "function" ? config.runtimeTools(auth) : config.runtimeTools ?? [];
|
|
2276
|
+
const tools = new Map;
|
|
2277
|
+
const help = new Map(nativeHelp);
|
|
2278
|
+
for (const { mountable } of collectToolSurface({
|
|
2279
|
+
surface: { services, runtimeTools },
|
|
2280
|
+
transport: "CLI"
|
|
2281
|
+
})) {
|
|
2282
|
+
const descriptor2 = managedDescriptor(mountable);
|
|
2283
|
+
assertCommandShape(mountable.name, descriptor2, help.has(mountable.name));
|
|
2284
|
+
tools.set(mountable.name, mountable);
|
|
2285
|
+
help.set(mountable.name, descriptor2);
|
|
2286
|
+
}
|
|
2287
|
+
return { auth, help, tools };
|
|
2288
|
+
};
|
|
2289
|
+
const topLevelHelp = command === undefined || command === "help" || command === "--help" || command === "-h";
|
|
2290
|
+
const managed = await buildManagedSurface(!topLevelHelp && !helpRequested);
|
|
2291
|
+
if (topLevelHelp) {
|
|
2292
|
+
stdout(renderTopHelp(config.name, config.version, managed.help));
|
|
2293
|
+
return exit(0);
|
|
2294
|
+
}
|
|
2295
|
+
const tool = managed.tools.get(command);
|
|
1925
2296
|
if (!tool) {
|
|
1926
2297
|
stderr(`Unknown command "${command}". Run "${config.name} --help" for the command list.
|
|
1927
2298
|
`);
|
|
1928
2299
|
return exit(1);
|
|
1929
2300
|
}
|
|
1930
|
-
const
|
|
1931
|
-
if (
|
|
1932
|
-
stdout(renderCommandHelp(config.name, command,
|
|
2301
|
+
const descriptor = managedDescriptor(tool);
|
|
2302
|
+
if (helpRequested) {
|
|
2303
|
+
stdout(renderCommandHelp(config.name, command, descriptor));
|
|
1933
2304
|
return exit(0);
|
|
1934
2305
|
}
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
allowUnknown: config.passthrough?.[command] !== undefined,
|
|
1939
|
-
knownFields: jsonSchemaFields(tool.presentationSchema).map((field) => field.name)
|
|
1940
|
-
});
|
|
1941
|
-
} catch (error) {
|
|
1942
|
-
if (!(error instanceof CliArgumentError))
|
|
1943
|
-
throw error;
|
|
1944
|
-
stderr(`${error.message}
|
|
2306
|
+
const prepared = await prepareInvocation(command, commandArgv, descriptor, config, readStdin);
|
|
2307
|
+
if (!prepared.ok) {
|
|
2308
|
+
stderr(`${prepared.message}
|
|
1945
2309
|
`);
|
|
1946
2310
|
return exit(2);
|
|
1947
2311
|
}
|
|
1948
|
-
const { toolArgs, options } =
|
|
2312
|
+
const { toolArgs, options } = prepared;
|
|
1949
2313
|
if (options.wait && !config.wait?.[command]) {
|
|
1950
2314
|
stderr(`--wait is not configured for command "${command}"
|
|
1951
2315
|
`);
|
|
@@ -1962,25 +2326,22 @@ async function createCli(config) {
|
|
|
1962
2326
|
return exit(2);
|
|
1963
2327
|
}
|
|
1964
2328
|
if (options.help) {
|
|
1965
|
-
stdout(renderCommandHelp(config.name, command,
|
|
2329
|
+
stdout(renderCommandHelp(config.name, command, descriptor));
|
|
1966
2330
|
return exit(0);
|
|
1967
2331
|
}
|
|
1968
|
-
const fields = jsonSchemaFields(safeInputSchema(tool));
|
|
1969
|
-
const firstUnset = fields.find((f) => f.required && !(f.name in toolArgs) && f.schema.type !== "boolean");
|
|
1970
|
-
if (firstUnset) {
|
|
1971
|
-
const piped = await readStdin();
|
|
1972
|
-
if (piped !== null)
|
|
1973
|
-
toolArgs[firstUnset.name] = piped;
|
|
1974
|
-
}
|
|
1975
|
-
const passthroughField = config.passthrough?.[command];
|
|
1976
|
-
if (passthroughField) {
|
|
1977
|
-
collectPassthrough(toolArgs, passthroughField, objectShapeKeys(tool.argumentSchema));
|
|
1978
|
-
}
|
|
1979
2332
|
if (options.dryRun) {
|
|
1980
2333
|
stdout(`${JSON.stringify({ command, args: toolArgs }, null, 2)}
|
|
1981
2334
|
`);
|
|
1982
2335
|
return exit(0);
|
|
1983
2336
|
}
|
|
2337
|
+
const runTool = createToolRunner({
|
|
2338
|
+
source: "cli",
|
|
2339
|
+
context: { ...config.context?.(managed.auth), signal: config.signal },
|
|
2340
|
+
hooks: config.hooks,
|
|
2341
|
+
lifecycle: config.lifecycle,
|
|
2342
|
+
errorHint: config.errorHint,
|
|
2343
|
+
coerceJsonArgs: config.coerceJsonArgs
|
|
2344
|
+
});
|
|
1984
2345
|
let result;
|
|
1985
2346
|
try {
|
|
1986
2347
|
result = await runTool(tool, toolArgs);
|
|
@@ -1993,7 +2354,7 @@ async function createCli(config) {
|
|
|
1993
2354
|
initial: result,
|
|
1994
2355
|
wait: waitConfig,
|
|
1995
2356
|
call: async (toolName, args) => {
|
|
1996
|
-
const pollTool = tools.get(toolName);
|
|
2357
|
+
const pollTool = managed.tools.get(toolName);
|
|
1997
2358
|
if (!pollTool) {
|
|
1998
2359
|
return {
|
|
1999
2360
|
ok: false,
|
|
@@ -2005,7 +2366,8 @@ async function createCli(config) {
|
|
|
2005
2366
|
},
|
|
2006
2367
|
timeoutSec: options.waitTimeout,
|
|
2007
2368
|
onTick: options.quiet ? undefined : (attempt) => stderr(`waiting… (poll ${attempt})
|
|
2008
|
-
`)
|
|
2369
|
+
`),
|
|
2370
|
+
signal: config.signal
|
|
2009
2371
|
});
|
|
2010
2372
|
}
|
|
2011
2373
|
let downloadsOk = true;
|
|
@@ -2020,8 +2382,5 @@ async function createCli(config) {
|
|
|
2020
2382
|
});
|
|
2021
2383
|
return exit(downloadsOk ? exitCode : 1);
|
|
2022
2384
|
}
|
|
2023
|
-
function safeInputSchema(tool) {
|
|
2024
|
-
return tool.presentationSchema;
|
|
2025
|
-
}
|
|
2026
2385
|
|
|
2027
|
-
export { coerceJsonArgs, toolResultFromError, toolErrorFromResult, executeToolMethod,
|
|
2386
|
+
export { coerceJsonArgs, toolResultFromError, toolErrorFromResult, executeToolMethod, flattenToolJsonSchema, buildToolPresentationSchema, isObjectPresentationSchema, presentationMetadata, collectTools, createToolRunner, formatToolError, defineRuntimeTool, createRuntimeToolFactory, collectToolSurface, WaitTimeoutError, runWaitOperation, fetchGuarded, fetchPinnedDocument, readCapped, parseCliArgs, defineCliCommand, DEFAULT_EXIT_CODES, emitResult, pollUntilDone, createCli };
|