stitchkit 0.53.1 → 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-6j4j5wf9.js → index-7vb44hy8.js} +404 -72
  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;
@@ -1423,6 +1602,7 @@ async function pollUntilDone(params) {
1423
1602
  }
1424
1603
 
1425
1604
  // src/tools/cli.ts
1605
+ import { writeSync } from "node:fs";
1426
1606
  import { basename, resolve } from "node:path";
1427
1607
  import process from "node:process";
1428
1608
 
@@ -1732,6 +1912,53 @@ async function writeDownload(root, target, data) {
1732
1912
  await writeFile(target, data);
1733
1913
  }
1734
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
+
1735
1962
  // src/tools/cli.ts
1736
1963
  var GLOBAL_OPTIONS = [
1737
1964
  ["--json", "Emit raw JSON on stdout (for piping / scripts)"],
@@ -1809,7 +2036,7 @@ function collectPassthrough(toolArgs, field, knownKeys) {
1809
2036
  const base = passthroughBase(toolArgs[field]);
1810
2037
  toolArgs[field] = base ? { ...base, ...bag } : bag;
1811
2038
  }
1812
- function renderTopHelp(name, version, tools) {
2039
+ function renderTopHelp(name, version, commands) {
1813
2040
  const lines = [
1814
2041
  `${name} ${version}`,
1815
2042
  "",
@@ -1817,9 +2044,9 @@ function renderTopHelp(name, version, tools) {
1817
2044
  "",
1818
2045
  "Commands:"
1819
2046
  ];
1820
- const width = Math.max(0, ...[...tools.keys()].map((k) => k.length));
1821
- for (const [command, tool] of tools) {
1822
- 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)}`);
1823
2050
  }
1824
2051
  lines.push("", "Global options:");
1825
2052
  const optWidth = Math.max(...GLOBAL_OPTIONS.map(([flag]) => flag.length));
@@ -1830,9 +2057,9 @@ function renderTopHelp(name, version, tools) {
1830
2057
  `)}
1831
2058
  `;
1832
2059
  }
1833
- function renderCommandHelp(name, command, tool) {
1834
- const lines = [tool.method.desc, "", `Usage: ${name} ${command} [args] [--flags]`, ""];
1835
- 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);
1836
2063
  if (fields.length > 0) {
1837
2064
  lines.push("Arguments:");
1838
2065
  const width = Math.max(...fields.map((f) => f.name.length));
@@ -1847,6 +2074,55 @@ function renderCommandHelp(name, command, tool) {
1847
2074
  `)}
1848
2075
  `;
1849
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
+ }
1850
2126
  var DEFAULT_DOWNLOAD_MAX_BYTES = 100 * 1024 * 1024;
1851
2127
  async function downloadResults(files, dir, stderr, quiet, allowPrivate, maxBytes, timeoutMs) {
1852
2128
  const root = resolve(dir);
@@ -1873,71 +2149,133 @@ async function downloadResults(files, dir, stderr, quiet, allowPrivate, maxBytes
1873
2149
  return succeeded;
1874
2150
  }
1875
2151
  async function createCli(config) {
1876
- const stdout = config.stdout ?? ((text) => void process.stdout.write(text));
1877
- const stderr = config.stderr ?? ((text) => void process.stderr.write(text));
2152
+ const writeFd = (fd, text) => {
2153
+ try {
2154
+ writeSync(fd, text);
2155
+ } catch {
2156
+ (fd === 1 ? process.stdout : process.stderr).write(text);
2157
+ }
2158
+ };
2159
+ const stdout = config.stdout ?? ((text) => writeFd(1, text));
2160
+ const stderr = config.stderr ?? ((text) => writeFd(2, text));
1878
2161
  const exit = config.exit ?? ((code) => void process.exit(code));
1879
2162
  const argv = config.argv ?? process.argv.slice(2);
1880
2163
  const readStdin = config.stdin ?? readPipedStdin;
1881
- const auth = config.auth === undefined ? undefined : await config.auth;
1882
- const services = typeof config.services === "function" ? config.services(auth) : config.services;
1883
- const context = config.context?.(auth);
1884
- const tools = new Map;
1885
- for (const service of services) {
1886
- for (const mountable of collectTools(service, "CLI")) {
1887
- assertUniqueToolName(mountable.name, tools.has(mountable.name), "CLI command");
1888
- if (mountable.name === "help" || mountable.name === "version") {
1889
- throw new Error(`[stitchkit] CLI command "${mountable.name}" is reserved`);
1890
- }
1891
- const conflicting = jsonSchemaFields(mountable.presentationSchema).map((field) => field.name).filter((name) => RESERVED_CLI_OPTIONS.has(name));
1892
- if (conflicting.length > 0) {
1893
- throw new Error(`[stitchkit] CLI command "${mountable.name}" declares reserved option field(s): ${conflicting.join(", ")}`);
1894
- }
1895
- tools.set(mountable.name, mountable);
1896
- }
2164
+ if (config.auth !== undefined && config.resolveAuth !== undefined) {
2165
+ throw new Error("[stitchkit] createCli: use either auth or resolveAuth, not both");
1897
2166
  }
1898
- const runTool = createToolRunner({
1899
- source: "cli",
1900
- context,
1901
- hooks: config.hooks,
1902
- lifecycle: config.lifecycle,
1903
- errorHint: config.errorHint,
1904
- coerceJsonArgs: config.coerceJsonArgs
1905
- });
1906
- const [command, ...commandArgv] = argv;
1907
- if (command === undefined || command === "help" || command === "--help" || command === "-h") {
1908
- stdout(renderTopHelp(config.name, config.version, tools));
1909
- 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);
1910
2174
  }
2175
+ const [command, ...commandArgv] = argv;
1911
2176
  if (command === "--version" || command === "version") {
1912
2177
  stdout(`${config.name} ${config.version}
1913
2178
  `);
1914
2179
  return exit(0);
1915
2180
  }
1916
- 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);
1917
2262
  if (!tool) {
1918
2263
  stderr(`Unknown command "${command}". Run "${config.name} --help" for the command list.
1919
2264
  `);
1920
2265
  return exit(1);
1921
2266
  }
1922
- const beforeSeparator = commandArgv.indexOf("--") === -1 ? commandArgv : commandArgv.slice(0, commandArgv.indexOf("--"));
1923
- if (beforeSeparator.includes("--help") || beforeSeparator.includes("-h")) {
1924
- stdout(renderCommandHelp(config.name, command, tool));
2267
+ const descriptor = managedDescriptor(tool);
2268
+ if (helpRequested) {
2269
+ stdout(renderCommandHelp(config.name, command, descriptor));
1925
2270
  return exit(0);
1926
2271
  }
1927
- let parsed;
1928
- try {
1929
- parsed = parseCliArgs(commandArgv, tool.argumentSchema, {
1930
- allowUnknown: config.passthrough?.[command] !== undefined,
1931
- knownFields: jsonSchemaFields(tool.presentationSchema).map((field) => field.name)
1932
- });
1933
- } catch (error) {
1934
- if (!(error instanceof CliArgumentError))
1935
- throw error;
1936
- stderr(`${error.message}
2272
+ const prepared = await prepareInvocation(command, commandArgv, descriptor, config, readStdin);
2273
+ if (!prepared.ok) {
2274
+ stderr(`${prepared.message}
1937
2275
  `);
1938
2276
  return exit(2);
1939
2277
  }
1940
- const { toolArgs, options } = parsed;
2278
+ const { toolArgs, options } = prepared;
1941
2279
  if (options.wait && !config.wait?.[command]) {
1942
2280
  stderr(`--wait is not configured for command "${command}"
1943
2281
  `);
@@ -1954,25 +2292,22 @@ async function createCli(config) {
1954
2292
  return exit(2);
1955
2293
  }
1956
2294
  if (options.help) {
1957
- stdout(renderCommandHelp(config.name, command, tool));
2295
+ stdout(renderCommandHelp(config.name, command, descriptor));
1958
2296
  return exit(0);
1959
2297
  }
1960
- const fields = jsonSchemaFields(safeInputSchema(tool));
1961
- const firstUnset = fields.find((f) => !(f.name in toolArgs) && f.schema.type !== "boolean");
1962
- if (firstUnset) {
1963
- const piped = await readStdin();
1964
- if (piped !== null)
1965
- toolArgs[firstUnset.name] = piped;
1966
- }
1967
- const passthroughField = config.passthrough?.[command];
1968
- if (passthroughField) {
1969
- collectPassthrough(toolArgs, passthroughField, objectShapeKeys(tool.argumentSchema));
1970
- }
1971
2298
  if (options.dryRun) {
1972
2299
  stdout(`${JSON.stringify({ command, args: toolArgs }, null, 2)}
1973
2300
  `);
1974
2301
  return exit(0);
1975
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
+ });
1976
2311
  let result;
1977
2312
  try {
1978
2313
  result = await runTool(tool, toolArgs);
@@ -1985,7 +2320,7 @@ async function createCli(config) {
1985
2320
  initial: result,
1986
2321
  wait: waitConfig,
1987
2322
  call: async (toolName, args) => {
1988
- const pollTool = tools.get(toolName);
2323
+ const pollTool = managed.tools.get(toolName);
1989
2324
  if (!pollTool) {
1990
2325
  return {
1991
2326
  ok: false,
@@ -2012,8 +2347,5 @@ async function createCli(config) {
2012
2347
  });
2013
2348
  return exit(downloadsOk ? exitCode : 1);
2014
2349
  }
2015
- function safeInputSchema(tool) {
2016
- return tool.presentationSchema;
2017
- }
2018
2350
 
2019
- 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 };