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.
Files changed (57) hide show
  1. package/dist/cli.d.ts +2 -1
  2. package/dist/cli.d.ts.map +1 -1
  3. package/dist/cli.js +3 -1
  4. package/dist/contract/define.d.ts +27 -0
  5. package/dist/contract/define.d.ts.map +1 -1
  6. package/dist/contract/index.d.ts +1 -1
  7. package/dist/contract/index.d.ts.map +1 -1
  8. package/dist/{index-c78w2y09.js → index-7vb44hy8.js} +394 -70
  9. package/dist/{index-yydqk4fh.js → index-b15rtha1.js} +30 -1
  10. package/dist/{index-pdv1mjjr.js → index-z7761jk2.js} +15 -38
  11. package/dist/node.js +2 -2
  12. package/dist/observability/event.d.ts +2 -16
  13. package/dist/observability/event.d.ts.map +1 -1
  14. package/dist/server/index.js +2 -2
  15. package/dist/server/process-signal-common.d.ts +6 -0
  16. package/dist/server/process-signal-common.d.ts.map +1 -0
  17. package/dist/server/process-signals.d.ts.map +1 -1
  18. package/dist/tools/cli-command.d.ts +29 -0
  19. package/dist/tools/cli-command.d.ts.map +1 -0
  20. package/dist/tools/cli.d.ts +11 -6
  21. package/dist/tools/cli.d.ts.map +1 -1
  22. package/dist/tools/define-download-tool.d.ts +17 -0
  23. package/dist/tools/define-download-tool.d.ts.map +1 -0
  24. package/dist/tools/define-upload-tool.d.ts +14 -0
  25. package/dist/tools/define-upload-tool.d.ts.map +1 -0
  26. package/dist/tools/define-view-file-tool.d.ts +10 -0
  27. package/dist/tools/define-view-file-tool.d.ts.map +1 -0
  28. package/dist/tools/define-wait-tool.d.ts +22 -0
  29. package/dist/tools/define-wait-tool.d.ts.map +1 -0
  30. package/dist/tools/download-core.d.ts +25 -0
  31. package/dist/tools/download-core.d.ts.map +1 -0
  32. package/dist/tools/execute.d.ts +3 -1
  33. package/dist/tools/execute.d.ts.map +1 -1
  34. package/dist/tools/mcp-round.d.ts +0 -1
  35. package/dist/tools/mcp-round.d.ts.map +1 -1
  36. package/dist/tools/mcp-stdio-signals.d.ts +33 -0
  37. package/dist/tools/mcp-stdio-signals.d.ts.map +1 -0
  38. package/dist/tools/mount-download.d.ts +5 -0
  39. package/dist/tools/mount-download.d.ts.map +1 -1
  40. package/dist/tools/mount-upload.d.ts.map +1 -1
  41. package/dist/tools/mount-wait.d.ts.map +1 -1
  42. package/dist/tools/native-definition.d.ts +15 -0
  43. package/dist/tools/native-definition.d.ts.map +1 -0
  44. package/dist/tools/runtime-tool.d.ts +5 -4
  45. package/dist/tools/runtime-tool.d.ts.map +1 -1
  46. package/dist/tools/surface.d.ts.map +1 -1
  47. package/dist/tools/upload-core.d.ts +3 -0
  48. package/dist/tools/upload-core.d.ts.map +1 -0
  49. package/dist/tools/view-file.d.ts +80 -20
  50. package/dist/tools/view-file.d.ts.map +1 -1
  51. package/dist/tools/wait-core.d.ts +10 -1
  52. package/dist/tools/wait-core.d.ts.map +1 -1
  53. package/dist/tools.d.ts +10 -2
  54. package/dist/tools.d.ts.map +1 -1
  55. package/dist/tools.js +570 -379
  56. package/llms-full.txt +312 -35
  57. 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 = { ...context, ...resolved, source: context.source };
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) => new Promise((resolve) => setTimeout(resolve, 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, tools) {
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, ...[...tools.keys()].map((k) => k.length));
1822
- for (const [command, tool] of tools) {
1823
- lines.push(` ${padRight(command, width)} ${summarize(tool.method.desc)}`);
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, tool) {
1835
- const lines = [tool.method.desc, "", `Usage: ${name} ${command} [args] [--flags]`, ""];
1836
- const fields = jsonSchemaFields(tool.presentationSchema);
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
- const auth = config.auth === undefined ? undefined : await config.auth;
1890
- const services = typeof config.services === "function" ? config.services(auth) : config.services;
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 runTool = createToolRunner({
1907
- source: "cli",
1908
- context,
1909
- hooks: config.hooks,
1910
- lifecycle: config.lifecycle,
1911
- errorHint: config.errorHint,
1912
- coerceJsonArgs: config.coerceJsonArgs
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 tool = tools.get(command);
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 beforeSeparator = commandArgv.indexOf("--") === -1 ? commandArgv : commandArgv.slice(0, commandArgv.indexOf("--"));
1931
- if (beforeSeparator.includes("--help") || beforeSeparator.includes("-h")) {
1932
- stdout(renderCommandHelp(config.name, command, tool));
2267
+ const descriptor = managedDescriptor(tool);
2268
+ if (helpRequested) {
2269
+ stdout(renderCommandHelp(config.name, command, descriptor));
1933
2270
  return exit(0);
1934
2271
  }
1935
- let parsed;
1936
- try {
1937
- parsed = parseCliArgs(commandArgv, tool.argumentSchema, {
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 } = parsed;
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, tool));
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, pollUntil, pollUntilDone, createCli };
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
- export { DEFAULT_CORS_ALLOW_HEADERS, DEFAULT_CORS_EXPOSE_HEADERS, assertCorsConfig, corsHeaders, corsPreflightResponse, defineMultipartStream, createMultipartStream, contractOnlyService, implement, createImplement, createScopedImplement, createScopedImplementRegistry, implementRegistry, createImplementRegistry };
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 };