stitchkit 0.53.2 → 0.54.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/dist/cli.d.ts +2 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +3 -1
- package/dist/contract/define.d.ts +27 -0
- package/dist/contract/define.d.ts.map +1 -1
- package/dist/contract/index.d.ts +1 -1
- package/dist/contract/index.d.ts.map +1 -1
- package/dist/{index-c78w2y09.js → index-7vb44hy8.js} +394 -70
- package/dist/{index-yydqk4fh.js → index-b15rtha1.js} +30 -1
- package/dist/{index-pdv1mjjr.js → index-z7761jk2.js} +15 -38
- package/dist/node.js +2 -2
- package/dist/observability/event.d.ts +2 -16
- package/dist/observability/event.d.ts.map +1 -1
- package/dist/server/index.js +2 -2
- 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/tools/cli-command.d.ts +29 -0
- package/dist/tools/cli-command.d.ts.map +1 -0
- package/dist/tools/cli.d.ts +11 -6
- package/dist/tools/cli.d.ts.map +1 -1
- package/dist/tools/define-download-tool.d.ts +17 -0
- package/dist/tools/define-download-tool.d.ts.map +1 -0
- package/dist/tools/define-upload-tool.d.ts +14 -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 +25 -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/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 +5 -0
- package/dist/tools/mount-download.d.ts.map +1 -1
- 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 +3 -0
- package/dist/tools/upload-core.d.ts.map +1 -0
- package/dist/tools/view-file.d.ts +80 -20
- package/dist/tools/view-file.d.ts.map +1 -1
- package/dist/tools/wait-core.d.ts +10 -1
- package/dist/tools/wait-core.d.ts.map +1 -1
- package/dist/tools.d.ts +10 -2
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +570 -379
- package/llms-full.txt +312 -35
- package/package.json +1 -1
|
@@ -671,7 +671,12 @@ async function runToolMethod(method, toolName, rawArgs, context, hooks, lifecycl
|
|
|
671
671
|
}
|
|
672
672
|
try {
|
|
673
673
|
const resolved = await extension.resolve({ ...rawArgs, ...parsed.data });
|
|
674
|
-
callContext = {
|
|
674
|
+
callContext = {
|
|
675
|
+
...context,
|
|
676
|
+
...resolved,
|
|
677
|
+
source: context.source,
|
|
678
|
+
...context.mcp !== undefined && { mcp: context.mcp }
|
|
679
|
+
};
|
|
675
680
|
hookContext = callContext;
|
|
676
681
|
callArgs = Object.fromEntries(Object.entries(rawArgs).filter(([key]) => !extensionKeys.has(key)));
|
|
677
682
|
} catch (err) {
|
|
@@ -1020,6 +1025,100 @@ function formatToolError(result, toolName, errorHint) {
|
|
|
1020
1025
|
return err;
|
|
1021
1026
|
}
|
|
1022
1027
|
|
|
1028
|
+
// src/tools/runtime-tool.ts
|
|
1029
|
+
function defineRuntimeTool(definition) {
|
|
1030
|
+
if (definition.transports?.length === 0) {
|
|
1031
|
+
throw new Error(`Runtime tool "${definition.name}" must expose at least one transport`);
|
|
1032
|
+
}
|
|
1033
|
+
return definition;
|
|
1034
|
+
}
|
|
1035
|
+
function createRuntimeToolFactory(config) {
|
|
1036
|
+
function parseContext(context) {
|
|
1037
|
+
const parsed = config.context.parse(context);
|
|
1038
|
+
return {
|
|
1039
|
+
...context,
|
|
1040
|
+
...parsed,
|
|
1041
|
+
params: undefined,
|
|
1042
|
+
input: context.input,
|
|
1043
|
+
...context.mcp !== undefined && { mcp: context.mcp }
|
|
1044
|
+
};
|
|
1045
|
+
}
|
|
1046
|
+
function define(definition) {
|
|
1047
|
+
if (definition.output !== undefined) {
|
|
1048
|
+
const { action: action2, method: method2, meta: meta2, handler: handler2, ...tool2 } = definition;
|
|
1049
|
+
const identity2 = {
|
|
1050
|
+
serviceName: config.serviceName,
|
|
1051
|
+
action: action2,
|
|
1052
|
+
method: method2,
|
|
1053
|
+
...config.scope !== undefined && { scope: config.scope },
|
|
1054
|
+
...(meta2 ?? config.meta) !== undefined && { meta: meta2 ?? config.meta }
|
|
1055
|
+
};
|
|
1056
|
+
return defineRuntimeTool({
|
|
1057
|
+
...tool2,
|
|
1058
|
+
identity: identity2,
|
|
1059
|
+
handler: (context) => handler2(parseContext(context))
|
|
1060
|
+
});
|
|
1061
|
+
}
|
|
1062
|
+
const { action, method, meta, handler, ...tool } = definition;
|
|
1063
|
+
const identity = {
|
|
1064
|
+
serviceName: config.serviceName,
|
|
1065
|
+
action,
|
|
1066
|
+
method,
|
|
1067
|
+
...config.scope !== undefined && { scope: config.scope },
|
|
1068
|
+
...(meta ?? config.meta) !== undefined && { meta: meta ?? config.meta }
|
|
1069
|
+
};
|
|
1070
|
+
return defineRuntimeTool({
|
|
1071
|
+
...tool,
|
|
1072
|
+
identity,
|
|
1073
|
+
handler: (context) => handler(parseContext(context))
|
|
1074
|
+
});
|
|
1075
|
+
}
|
|
1076
|
+
return { define };
|
|
1077
|
+
}
|
|
1078
|
+
function runtimeToolSupports(definition, transport) {
|
|
1079
|
+
if (definition.transports?.length === 0) {
|
|
1080
|
+
throw new Error(`Runtime tool "${definition.name}" must expose at least one transport`);
|
|
1081
|
+
}
|
|
1082
|
+
if (!definition.transports)
|
|
1083
|
+
return transport !== "CLI";
|
|
1084
|
+
return definition.transports.includes(transport);
|
|
1085
|
+
}
|
|
1086
|
+
function runtimeToolIdentity(definition) {
|
|
1087
|
+
return {
|
|
1088
|
+
method: definition.identity.method,
|
|
1089
|
+
desc: definition.description,
|
|
1090
|
+
serviceName: definition.identity.serviceName,
|
|
1091
|
+
key: definition.identity.action,
|
|
1092
|
+
toolName: definition.name,
|
|
1093
|
+
scope: definition.identity.scope,
|
|
1094
|
+
meta: definition.identity.meta,
|
|
1095
|
+
annotations: definition.annotations,
|
|
1096
|
+
ui: definition.ui,
|
|
1097
|
+
mcp: definition.mcp
|
|
1098
|
+
};
|
|
1099
|
+
}
|
|
1100
|
+
function runtimeToolMountable(definition, assertName = true) {
|
|
1101
|
+
if (assertName) {
|
|
1102
|
+
assertToolName(definition.name, definition.identity.serviceName, definition.identity.action);
|
|
1103
|
+
}
|
|
1104
|
+
const method = {
|
|
1105
|
+
...runtimeToolIdentity(definition),
|
|
1106
|
+
inputSchema: definition.input,
|
|
1107
|
+
outputSchema: definition.output,
|
|
1108
|
+
handler: definition.handler
|
|
1109
|
+
};
|
|
1110
|
+
return {
|
|
1111
|
+
method,
|
|
1112
|
+
name: definition.name,
|
|
1113
|
+
argumentSchema: definition.input,
|
|
1114
|
+
presentationSchema: buildToolPresentationSchema({
|
|
1115
|
+
inputSchema: definition.input,
|
|
1116
|
+
unrepresentable: "any"
|
|
1117
|
+
}),
|
|
1118
|
+
shouldExtend: false
|
|
1119
|
+
};
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1023
1122
|
// src/tools/cli-args.ts
|
|
1024
1123
|
import { z as z5 } from "zod";
|
|
1025
1124
|
var NUMERIC_VALUE = /^-(\d+\.?\d*|\.\d+)$/;
|
|
@@ -1341,6 +1440,50 @@ function parseCliArgs(argv, schema, config = {}) {
|
|
|
1341
1440
|
return { toolArgs, options };
|
|
1342
1441
|
}
|
|
1343
1442
|
|
|
1443
|
+
// src/tools/cli-command.ts
|
|
1444
|
+
function defineCliCommand(definition) {
|
|
1445
|
+
return definition;
|
|
1446
|
+
}
|
|
1447
|
+
function cliCommandPresentationSchema(definition) {
|
|
1448
|
+
return buildToolPresentationSchema({
|
|
1449
|
+
inputSchema: definition.input,
|
|
1450
|
+
unrepresentable: "any"
|
|
1451
|
+
});
|
|
1452
|
+
}
|
|
1453
|
+
async function executeCliCommand(definition, rawArgs, options, writers, coerceJson) {
|
|
1454
|
+
let parsed;
|
|
1455
|
+
try {
|
|
1456
|
+
parsed = definition.input.safeParse(coerceJson ? coerceJsonArgs(rawArgs, definition.input) : rawArgs);
|
|
1457
|
+
} catch (error) {
|
|
1458
|
+
return toolResultFromError(error);
|
|
1459
|
+
}
|
|
1460
|
+
if (!parsed.success) {
|
|
1461
|
+
return {
|
|
1462
|
+
ok: false,
|
|
1463
|
+
code: "VALIDATION_ERROR",
|
|
1464
|
+
details: { message: `Invalid input: ${formatZodError(parsed.error)}` }
|
|
1465
|
+
};
|
|
1466
|
+
}
|
|
1467
|
+
try {
|
|
1468
|
+
const data = await definition.handler({
|
|
1469
|
+
input: parsed.data,
|
|
1470
|
+
options,
|
|
1471
|
+
...writers
|
|
1472
|
+
});
|
|
1473
|
+
const checked = validateDeclaredOutput(definition.output, data);
|
|
1474
|
+
if (!checked.ok) {
|
|
1475
|
+
return {
|
|
1476
|
+
ok: false,
|
|
1477
|
+
code: "INTERNAL_SERVER_ERROR",
|
|
1478
|
+
details: { message: checked.message }
|
|
1479
|
+
};
|
|
1480
|
+
}
|
|
1481
|
+
return { ok: true, data: checked.data };
|
|
1482
|
+
} catch (error) {
|
|
1483
|
+
return toolResultFromError(error);
|
|
1484
|
+
}
|
|
1485
|
+
}
|
|
1486
|
+
|
|
1344
1487
|
// src/tools/cli-format.ts
|
|
1345
1488
|
var DEFAULT_EXIT_CODES = {
|
|
1346
1489
|
VALIDATION_ERROR: 1,
|
|
@@ -1372,7 +1515,30 @@ function emitResult(result, writers, opts) {
|
|
|
1372
1515
|
// src/tools/wait-core.ts
|
|
1373
1516
|
var DEFAULT_BACKOFF = [2, 3, 5, 5, 8, 10];
|
|
1374
1517
|
var DEFAULT_TIMEOUT = 600;
|
|
1375
|
-
var defaultSleep = (ms
|
|
1518
|
+
var defaultSleep = (ms, signal) => {
|
|
1519
|
+
signal?.throwIfAborted();
|
|
1520
|
+
return new Promise((resolve, reject) => {
|
|
1521
|
+
let settled = false;
|
|
1522
|
+
const settle = (action) => {
|
|
1523
|
+
if (settled)
|
|
1524
|
+
return;
|
|
1525
|
+
settled = true;
|
|
1526
|
+
signal?.removeEventListener("abort", onAbort);
|
|
1527
|
+
action();
|
|
1528
|
+
};
|
|
1529
|
+
const timer = setTimeout(() => {
|
|
1530
|
+
settle(resolve);
|
|
1531
|
+
}, ms);
|
|
1532
|
+
timer.unref?.();
|
|
1533
|
+
const onAbort = () => {
|
|
1534
|
+
clearTimeout(timer);
|
|
1535
|
+
settle(() => reject(signal?.reason ?? new Error("aborted")));
|
|
1536
|
+
};
|
|
1537
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
1538
|
+
if (signal?.aborted)
|
|
1539
|
+
onAbort();
|
|
1540
|
+
});
|
|
1541
|
+
};
|
|
1376
1542
|
async function pollUntil(params) {
|
|
1377
1543
|
const backoff = params.backoff?.length ? params.backoff : DEFAULT_BACKOFF;
|
|
1378
1544
|
const lastBackoff = backoff[backoff.length - 1] ?? 5;
|
|
@@ -1380,7 +1546,9 @@ async function pollUntil(params) {
|
|
|
1380
1546
|
const sleep = params.sleepFn ?? defaultSleep;
|
|
1381
1547
|
const startedAt = Date.now();
|
|
1382
1548
|
for (let attempt = 0;; attempt++) {
|
|
1549
|
+
params.signal?.throwIfAborted();
|
|
1383
1550
|
const state = await params.poll();
|
|
1551
|
+
params.signal?.throwIfAborted();
|
|
1384
1552
|
if (params.done(state))
|
|
1385
1553
|
return { state, timedOut: false };
|
|
1386
1554
|
const elapsedSec = (Date.now() - startedAt) / 1000;
|
|
@@ -1388,9 +1556,20 @@ async function pollUntil(params) {
|
|
|
1388
1556
|
if (elapsedSec >= timeoutSec)
|
|
1389
1557
|
return { state, timedOut: true };
|
|
1390
1558
|
const waitSec = backoff[Math.min(attempt, backoff.length - 1)] ?? lastBackoff;
|
|
1391
|
-
await sleep(waitSec * 1000);
|
|
1559
|
+
await sleep(waitSec * 1000, params.signal);
|
|
1392
1560
|
}
|
|
1393
1561
|
}
|
|
1562
|
+
function runWaitOperation(params) {
|
|
1563
|
+
return pollUntil({
|
|
1564
|
+
poll: () => params.poll(params.input, params.signal),
|
|
1565
|
+
done: params.done,
|
|
1566
|
+
backoff: params.backoff,
|
|
1567
|
+
timeoutSec: params.timeoutSec,
|
|
1568
|
+
sleepFn: params.sleepFn,
|
|
1569
|
+
onTick: params.onTick,
|
|
1570
|
+
signal: params.signal
|
|
1571
|
+
});
|
|
1572
|
+
}
|
|
1394
1573
|
|
|
1395
1574
|
// src/tools/cli-wait.ts
|
|
1396
1575
|
var DEFAULT_TIMEOUT2 = 600;
|
|
@@ -1733,6 +1912,53 @@ async function writeDownload(root, target, data) {
|
|
|
1733
1912
|
await writeFile(target, data);
|
|
1734
1913
|
}
|
|
1735
1914
|
|
|
1915
|
+
// src/tools/surface.ts
|
|
1916
|
+
function duplicateLabel(transport) {
|
|
1917
|
+
if (transport === "MCP")
|
|
1918
|
+
return "MCP tool name";
|
|
1919
|
+
if (transport === "AGENT")
|
|
1920
|
+
return "agent tool name";
|
|
1921
|
+
return "CLI command";
|
|
1922
|
+
}
|
|
1923
|
+
function collectToolSurface({
|
|
1924
|
+
surface,
|
|
1925
|
+
transport,
|
|
1926
|
+
assertUniqueNames = true,
|
|
1927
|
+
...collectConfig
|
|
1928
|
+
}) {
|
|
1929
|
+
const entries = [];
|
|
1930
|
+
const names = new Set;
|
|
1931
|
+
const append = (entry) => {
|
|
1932
|
+
if (assertUniqueNames) {
|
|
1933
|
+
assertUniqueToolName(entry.mountable.name, names.has(entry.mountable.name), duplicateLabel(transport));
|
|
1934
|
+
}
|
|
1935
|
+
names.add(entry.mountable.name);
|
|
1936
|
+
entries.push(entry);
|
|
1937
|
+
};
|
|
1938
|
+
for (const service of surface.services ?? []) {
|
|
1939
|
+
for (const mountable of collectTools(service, transport, collectConfig)) {
|
|
1940
|
+
append({
|
|
1941
|
+
kind: "contract",
|
|
1942
|
+
service: service.name,
|
|
1943
|
+
action: mountable.method.key,
|
|
1944
|
+
mountable
|
|
1945
|
+
});
|
|
1946
|
+
}
|
|
1947
|
+
}
|
|
1948
|
+
for (const definition of surface.runtimeTools ?? []) {
|
|
1949
|
+
if (!runtimeToolSupports(definition, transport))
|
|
1950
|
+
continue;
|
|
1951
|
+
append({
|
|
1952
|
+
kind: "runtime",
|
|
1953
|
+
service: definition.identity.serviceName,
|
|
1954
|
+
action: definition.identity.action,
|
|
1955
|
+
mountable: runtimeToolMountable(definition, collectConfig.assertNames),
|
|
1956
|
+
definition
|
|
1957
|
+
});
|
|
1958
|
+
}
|
|
1959
|
+
return entries;
|
|
1960
|
+
}
|
|
1961
|
+
|
|
1736
1962
|
// src/tools/cli.ts
|
|
1737
1963
|
var GLOBAL_OPTIONS = [
|
|
1738
1964
|
["--json", "Emit raw JSON on stdout (for piping / scripts)"],
|
|
@@ -1810,7 +2036,7 @@ function collectPassthrough(toolArgs, field, knownKeys) {
|
|
|
1810
2036
|
const base = passthroughBase(toolArgs[field]);
|
|
1811
2037
|
toolArgs[field] = base ? { ...base, ...bag } : bag;
|
|
1812
2038
|
}
|
|
1813
|
-
function renderTopHelp(name, version,
|
|
2039
|
+
function renderTopHelp(name, version, commands) {
|
|
1814
2040
|
const lines = [
|
|
1815
2041
|
`${name} ${version}`,
|
|
1816
2042
|
"",
|
|
@@ -1818,9 +2044,9 @@ function renderTopHelp(name, version, tools) {
|
|
|
1818
2044
|
"",
|
|
1819
2045
|
"Commands:"
|
|
1820
2046
|
];
|
|
1821
|
-
const width = Math.max(0, ...[...
|
|
1822
|
-
for (const [command,
|
|
1823
|
-
lines.push(` ${padRight(command, width)} ${summarize(
|
|
2047
|
+
const width = Math.max(0, ...[...commands.keys()].map((key) => key.length));
|
|
2048
|
+
for (const [command, descriptor] of commands) {
|
|
2049
|
+
lines.push(` ${padRight(command, width)} ${summarize(descriptor.description)}`);
|
|
1824
2050
|
}
|
|
1825
2051
|
lines.push("", "Global options:");
|
|
1826
2052
|
const optWidth = Math.max(...GLOBAL_OPTIONS.map(([flag]) => flag.length));
|
|
@@ -1831,9 +2057,9 @@ function renderTopHelp(name, version, tools) {
|
|
|
1831
2057
|
`)}
|
|
1832
2058
|
`;
|
|
1833
2059
|
}
|
|
1834
|
-
function renderCommandHelp(name, command,
|
|
1835
|
-
const lines = [
|
|
1836
|
-
const fields = jsonSchemaFields(
|
|
2060
|
+
function renderCommandHelp(name, command, descriptor) {
|
|
2061
|
+
const lines = [descriptor.description, "", `Usage: ${name} ${command} [args] [--flags]`, ""];
|
|
2062
|
+
const fields = jsonSchemaFields(descriptor.presentationSchema);
|
|
1837
2063
|
if (fields.length > 0) {
|
|
1838
2064
|
lines.push("Arguments:");
|
|
1839
2065
|
const width = Math.max(...fields.map((f) => f.name.length));
|
|
@@ -1848,6 +2074,55 @@ function renderCommandHelp(name, command, tool) {
|
|
|
1848
2074
|
`)}
|
|
1849
2075
|
`;
|
|
1850
2076
|
}
|
|
2077
|
+
function managedDescriptor(tool) {
|
|
2078
|
+
return {
|
|
2079
|
+
description: tool.method.desc,
|
|
2080
|
+
argumentSchema: tool.argumentSchema,
|
|
2081
|
+
presentationSchema: tool.presentationSchema
|
|
2082
|
+
};
|
|
2083
|
+
}
|
|
2084
|
+
function nativeDescriptor(definition) {
|
|
2085
|
+
return {
|
|
2086
|
+
description: definition.description,
|
|
2087
|
+
argumentSchema: definition.input,
|
|
2088
|
+
presentationSchema: cliCommandPresentationSchema(definition)
|
|
2089
|
+
};
|
|
2090
|
+
}
|
|
2091
|
+
function assertCommandShape(name, descriptor, exists) {
|
|
2092
|
+
assertUniqueToolName(name, exists, "CLI command");
|
|
2093
|
+
if (name === "help" || name === "version") {
|
|
2094
|
+
throw new Error(`[stitchkit] CLI command "${name}" is reserved`);
|
|
2095
|
+
}
|
|
2096
|
+
const conflicting = jsonSchemaFields(descriptor.presentationSchema).map((field) => field.name).filter((field) => RESERVED_CLI_OPTIONS.has(field));
|
|
2097
|
+
if (conflicting.length > 0) {
|
|
2098
|
+
throw new Error(`[stitchkit] CLI command "${name}" declares reserved option field(s): ${conflicting.join(", ")}`);
|
|
2099
|
+
}
|
|
2100
|
+
}
|
|
2101
|
+
async function prepareInvocation(command, commandArgv, descriptor, config, readStdin) {
|
|
2102
|
+
let parsed;
|
|
2103
|
+
try {
|
|
2104
|
+
parsed = parseCliArgs(commandArgv, descriptor.argumentSchema, {
|
|
2105
|
+
allowUnknown: config.passthrough?.[command] !== undefined,
|
|
2106
|
+
knownFields: jsonSchemaFields(descriptor.presentationSchema).map((field) => field.name)
|
|
2107
|
+
});
|
|
2108
|
+
} catch (error) {
|
|
2109
|
+
if (!(error instanceof CliArgumentError))
|
|
2110
|
+
throw error;
|
|
2111
|
+
return { ok: false, message: error.message };
|
|
2112
|
+
}
|
|
2113
|
+
const { toolArgs, options } = parsed;
|
|
2114
|
+
const firstUnset = jsonSchemaFields(descriptor.presentationSchema).find((field) => field.required && !(field.name in toolArgs) && field.schema.type !== "boolean");
|
|
2115
|
+
if (firstUnset) {
|
|
2116
|
+
const piped = await readStdin();
|
|
2117
|
+
if (piped !== null)
|
|
2118
|
+
toolArgs[firstUnset.name] = piped;
|
|
2119
|
+
}
|
|
2120
|
+
const passthroughField = config.passthrough?.[command];
|
|
2121
|
+
if (passthroughField) {
|
|
2122
|
+
collectPassthrough(toolArgs, passthroughField, objectShapeKeys(descriptor.argumentSchema));
|
|
2123
|
+
}
|
|
2124
|
+
return { ok: true, toolArgs, options };
|
|
2125
|
+
}
|
|
1851
2126
|
var DEFAULT_DOWNLOAD_MAX_BYTES = 100 * 1024 * 1024;
|
|
1852
2127
|
async function downloadResults(files, dir, stderr, quiet, allowPrivate, maxBytes, timeoutMs) {
|
|
1853
2128
|
const root = resolve(dir);
|
|
@@ -1886,66 +2161,121 @@ async function createCli(config) {
|
|
|
1886
2161
|
const exit = config.exit ?? ((code) => void process.exit(code));
|
|
1887
2162
|
const argv = config.argv ?? process.argv.slice(2);
|
|
1888
2163
|
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
|
-
}
|
|
2164
|
+
if (config.auth !== undefined && config.resolveAuth !== undefined) {
|
|
2165
|
+
throw new Error("[stitchkit] createCli: use either auth or resolveAuth, not both");
|
|
1905
2166
|
}
|
|
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);
|
|
2167
|
+
const nativeCommands = new Map;
|
|
2168
|
+
const nativeHelp = new Map;
|
|
2169
|
+
for (const definition of config.commands ?? []) {
|
|
2170
|
+
const descriptor2 = nativeDescriptor(definition);
|
|
2171
|
+
assertCommandShape(definition.name, descriptor2, nativeCommands.has(definition.name));
|
|
2172
|
+
nativeCommands.set(definition.name, definition);
|
|
2173
|
+
nativeHelp.set(definition.name, descriptor2);
|
|
1918
2174
|
}
|
|
2175
|
+
const [command, ...commandArgv] = argv;
|
|
1919
2176
|
if (command === "--version" || command === "version") {
|
|
1920
2177
|
stdout(`${config.name} ${config.version}
|
|
1921
2178
|
`);
|
|
1922
2179
|
return exit(0);
|
|
1923
2180
|
}
|
|
1924
|
-
const
|
|
2181
|
+
const beforeSeparator = commandArgv.indexOf("--") === -1 ? commandArgv : commandArgv.slice(0, commandArgv.indexOf("--"));
|
|
2182
|
+
const helpRequested = beforeSeparator.includes("--help") || beforeSeparator.includes("-h");
|
|
2183
|
+
const native = command === undefined ? undefined : nativeCommands.get(command);
|
|
2184
|
+
if (native && command !== undefined) {
|
|
2185
|
+
const descriptor2 = nativeHelp.get(command);
|
|
2186
|
+
if (!descriptor2)
|
|
2187
|
+
throw new Error("[stitchkit] native CLI descriptor invariant failed");
|
|
2188
|
+
if (helpRequested) {
|
|
2189
|
+
stdout(renderCommandHelp(config.name, command, descriptor2));
|
|
2190
|
+
return exit(0);
|
|
2191
|
+
}
|
|
2192
|
+
const prepared2 = await prepareInvocation(command, commandArgv, descriptor2, config, readStdin);
|
|
2193
|
+
if (!prepared2.ok) {
|
|
2194
|
+
stderr(`${prepared2.message}
|
|
2195
|
+
`);
|
|
2196
|
+
return exit(2);
|
|
2197
|
+
}
|
|
2198
|
+
const { toolArgs: toolArgs2, options: options2 } = prepared2;
|
|
2199
|
+
if (options2.help) {
|
|
2200
|
+
stdout(renderCommandHelp(config.name, command, descriptor2));
|
|
2201
|
+
return exit(0);
|
|
2202
|
+
}
|
|
2203
|
+
if (options2.wait) {
|
|
2204
|
+
stderr(`--wait is not configured for native command "${command}"
|
|
2205
|
+
`);
|
|
2206
|
+
return exit(2);
|
|
2207
|
+
}
|
|
2208
|
+
if (options2.waitTimeout !== undefined) {
|
|
2209
|
+
stderr(`--wait-timeout requires --wait
|
|
2210
|
+
`);
|
|
2211
|
+
return exit(2);
|
|
2212
|
+
}
|
|
2213
|
+
if (options2.outputDir !== undefined) {
|
|
2214
|
+
stderr(`--output-dir is not configured for native commands
|
|
2215
|
+
`);
|
|
2216
|
+
return exit(2);
|
|
2217
|
+
}
|
|
2218
|
+
if (options2.dryRun) {
|
|
2219
|
+
stdout(`${JSON.stringify({ command, args: toolArgs2 }, null, 2)}
|
|
2220
|
+
`);
|
|
2221
|
+
return exit(0);
|
|
2222
|
+
}
|
|
2223
|
+
const result2 = await executeCliCommand(native, toolArgs2, options2, { stdout, stderr }, config.coerceJsonArgs ?? true);
|
|
2224
|
+
const exitCode2 = emitResult(result2, { stdout, stderr }, {
|
|
2225
|
+
json: options2.json,
|
|
2226
|
+
toolName: command,
|
|
2227
|
+
errorHint: config.errorHint,
|
|
2228
|
+
exitCodes: { ...DEFAULT_EXIT_CODES, ...config.exitCodes }
|
|
2229
|
+
});
|
|
2230
|
+
return exit(exitCode2);
|
|
2231
|
+
}
|
|
2232
|
+
let authPromise;
|
|
2233
|
+
const resolveIdentity = () => {
|
|
2234
|
+
authPromise ??= Promise.resolve(config.resolveAuth ? config.resolveAuth() : config.auth);
|
|
2235
|
+
return authPromise;
|
|
2236
|
+
};
|
|
2237
|
+
const dynamicSurface = typeof config.services === "function" || typeof config.runtimeTools === "function";
|
|
2238
|
+
const buildManagedSurface = async (forExecution) => {
|
|
2239
|
+
const auth = dynamicSurface || forExecution ? await resolveIdentity() : undefined;
|
|
2240
|
+
const services = typeof config.services === "function" ? config.services(auth) : config.services ?? [];
|
|
2241
|
+
const runtimeTools = typeof config.runtimeTools === "function" ? config.runtimeTools(auth) : config.runtimeTools ?? [];
|
|
2242
|
+
const tools = new Map;
|
|
2243
|
+
const help = new Map(nativeHelp);
|
|
2244
|
+
for (const { mountable } of collectToolSurface({
|
|
2245
|
+
surface: { services, runtimeTools },
|
|
2246
|
+
transport: "CLI"
|
|
2247
|
+
})) {
|
|
2248
|
+
const descriptor2 = managedDescriptor(mountable);
|
|
2249
|
+
assertCommandShape(mountable.name, descriptor2, help.has(mountable.name));
|
|
2250
|
+
tools.set(mountable.name, mountable);
|
|
2251
|
+
help.set(mountable.name, descriptor2);
|
|
2252
|
+
}
|
|
2253
|
+
return { auth, help, tools };
|
|
2254
|
+
};
|
|
2255
|
+
const topLevelHelp = command === undefined || command === "help" || command === "--help" || command === "-h";
|
|
2256
|
+
const managed = await buildManagedSurface(!topLevelHelp && !helpRequested);
|
|
2257
|
+
if (topLevelHelp) {
|
|
2258
|
+
stdout(renderTopHelp(config.name, config.version, managed.help));
|
|
2259
|
+
return exit(0);
|
|
2260
|
+
}
|
|
2261
|
+
const tool = managed.tools.get(command);
|
|
1925
2262
|
if (!tool) {
|
|
1926
2263
|
stderr(`Unknown command "${command}". Run "${config.name} --help" for the command list.
|
|
1927
2264
|
`);
|
|
1928
2265
|
return exit(1);
|
|
1929
2266
|
}
|
|
1930
|
-
const
|
|
1931
|
-
if (
|
|
1932
|
-
stdout(renderCommandHelp(config.name, command,
|
|
2267
|
+
const descriptor = managedDescriptor(tool);
|
|
2268
|
+
if (helpRequested) {
|
|
2269
|
+
stdout(renderCommandHelp(config.name, command, descriptor));
|
|
1933
2270
|
return exit(0);
|
|
1934
2271
|
}
|
|
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}
|
|
2272
|
+
const prepared = await prepareInvocation(command, commandArgv, descriptor, config, readStdin);
|
|
2273
|
+
if (!prepared.ok) {
|
|
2274
|
+
stderr(`${prepared.message}
|
|
1945
2275
|
`);
|
|
1946
2276
|
return exit(2);
|
|
1947
2277
|
}
|
|
1948
|
-
const { toolArgs, options } =
|
|
2278
|
+
const { toolArgs, options } = prepared;
|
|
1949
2279
|
if (options.wait && !config.wait?.[command]) {
|
|
1950
2280
|
stderr(`--wait is not configured for command "${command}"
|
|
1951
2281
|
`);
|
|
@@ -1962,25 +2292,22 @@ async function createCli(config) {
|
|
|
1962
2292
|
return exit(2);
|
|
1963
2293
|
}
|
|
1964
2294
|
if (options.help) {
|
|
1965
|
-
stdout(renderCommandHelp(config.name, command,
|
|
2295
|
+
stdout(renderCommandHelp(config.name, command, descriptor));
|
|
1966
2296
|
return exit(0);
|
|
1967
2297
|
}
|
|
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
2298
|
if (options.dryRun) {
|
|
1980
2299
|
stdout(`${JSON.stringify({ command, args: toolArgs }, null, 2)}
|
|
1981
2300
|
`);
|
|
1982
2301
|
return exit(0);
|
|
1983
2302
|
}
|
|
2303
|
+
const runTool = createToolRunner({
|
|
2304
|
+
source: "cli",
|
|
2305
|
+
context: config.context?.(managed.auth),
|
|
2306
|
+
hooks: config.hooks,
|
|
2307
|
+
lifecycle: config.lifecycle,
|
|
2308
|
+
errorHint: config.errorHint,
|
|
2309
|
+
coerceJsonArgs: config.coerceJsonArgs
|
|
2310
|
+
});
|
|
1984
2311
|
let result;
|
|
1985
2312
|
try {
|
|
1986
2313
|
result = await runTool(tool, toolArgs);
|
|
@@ -1993,7 +2320,7 @@ async function createCli(config) {
|
|
|
1993
2320
|
initial: result,
|
|
1994
2321
|
wait: waitConfig,
|
|
1995
2322
|
call: async (toolName, args) => {
|
|
1996
|
-
const pollTool = tools.get(toolName);
|
|
2323
|
+
const pollTool = managed.tools.get(toolName);
|
|
1997
2324
|
if (!pollTool) {
|
|
1998
2325
|
return {
|
|
1999
2326
|
ok: false,
|
|
@@ -2020,8 +2347,5 @@ async function createCli(config) {
|
|
|
2020
2347
|
});
|
|
2021
2348
|
return exit(downloadsOk ? exitCode : 1);
|
|
2022
2349
|
}
|
|
2023
|
-
function safeInputSchema(tool) {
|
|
2024
|
-
return tool.presentationSchema;
|
|
2025
|
-
}
|
|
2026
2350
|
|
|
2027
|
-
export { coerceJsonArgs, toolResultFromError, toolErrorFromResult, executeToolMethod, assertToolName, assertUniqueToolName, flattenToolJsonSchema, buildToolPresentationSchema, isObjectPresentationSchema, presentationMetadata, collectTools, createToolRunner, formatToolError, fetchGuarded, fetchPinnedDocument, readCapped, writeDownload, parseCliArgs, DEFAULT_EXIT_CODES, emitResult,
|
|
2351
|
+
export { coerceJsonArgs, toolResultFromError, toolErrorFromResult, executeToolMethod, assertToolName, assertUniqueToolName, flattenToolJsonSchema, buildToolPresentationSchema, isObjectPresentationSchema, presentationMetadata, collectTools, createToolRunner, formatToolError, defineRuntimeTool, createRuntimeToolFactory, collectToolSurface, fetchGuarded, fetchPinnedDocument, readCapped, writeDownload, parseCliArgs, defineCliCommand, DEFAULT_EXIT_CODES, emitResult, runWaitOperation, pollUntilDone, createCli };
|
|
@@ -246,4 +246,33 @@ function createImplementRegistry() {
|
|
|
246
246
|
return (contracts, handlers) => transportResult(bindRegistry(contracts, handlers));
|
|
247
247
|
}
|
|
248
248
|
|
|
249
|
-
|
|
249
|
+
// src/server/process-signal-common.ts
|
|
250
|
+
var DEFAULT_PROCESS_SIGNALS = ["SIGINT", "SIGTERM"];
|
|
251
|
+
var defaultSignalSource = {
|
|
252
|
+
on: (signal, handler) => {
|
|
253
|
+
process.on(signal, handler);
|
|
254
|
+
},
|
|
255
|
+
off: (signal, handler) => {
|
|
256
|
+
process.off(signal, handler);
|
|
257
|
+
},
|
|
258
|
+
raiseDefault: (signal) => {
|
|
259
|
+
if (process.listenerCount(signal) > 0)
|
|
260
|
+
return false;
|
|
261
|
+
process.kill(process.pid, signal);
|
|
262
|
+
return true;
|
|
263
|
+
}
|
|
264
|
+
};
|
|
265
|
+
function reportSignalError(phase, error, onError) {
|
|
266
|
+
try {
|
|
267
|
+
onError?.(phase, error);
|
|
268
|
+
} catch {}
|
|
269
|
+
}
|
|
270
|
+
function guardSignalCallback(run, phase, onError) {
|
|
271
|
+
try {
|
|
272
|
+
run();
|
|
273
|
+
} catch (error) {
|
|
274
|
+
reportSignalError(phase, error, onError);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
export { DEFAULT_CORS_ALLOW_HEADERS, DEFAULT_CORS_EXPOSE_HEADERS, assertCorsConfig, corsHeaders, corsPreflightResponse, defineMultipartStream, createMultipartStream, contractOnlyService, implement, createImplement, createScopedImplement, createScopedImplementRegistry, implementRegistry, createImplementRegistry, DEFAULT_PROCESS_SIGNALS, defaultSignalSource, reportSignalError, guardSignalCallback };
|