@ai-ecoverse/mcpcat 0.15.6 → 0.15.8

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.
@@ -1854,6 +1854,16 @@ var init_version = __esm({
1854
1854
  });
1855
1855
 
1856
1856
  // node_modules/quickjs-wasi/dist/index.js
1857
+ var dist_exports = {};
1858
+ __export(dist_exports, {
1859
+ CompileFlags: () => CompileFlags,
1860
+ EvalFlags: () => EvalFlags,
1861
+ Intrinsics: () => Intrinsics,
1862
+ JSException: () => JSException,
1863
+ JSValueHandle: () => JSValueHandle,
1864
+ MAX_STACK_SIZE: () => MAX_STACK_SIZE,
1865
+ QuickJS: () => QuickJS
1866
+ });
1857
1867
  function stringKeyNeedsValuePath(key) {
1858
1868
  return key.includes("\0") || LONE_SURROGATE_RE.test(key);
1859
1869
  }
@@ -5365,8 +5375,8 @@ SOURCE: /[\s\S]+/
5365
5375
  });
5366
5376
 
5367
5377
  // node_modules/@earendil-works/pi-codemode/dist/index.js
5368
- var dist_exports = {};
5369
- __export(dist_exports, {
5378
+ var dist_exports2 = {};
5379
+ __export(dist_exports2, {
5370
5380
  CODEMODE_OPTIONS_PREFIX: () => CODEMODE_OPTIONS_PREFIX,
5371
5381
  CODEMODE_SOURCE_GRAMMAR: () => CODEMODE_SOURCE_GRAMMAR,
5372
5382
  CodemodeSandbox: () => CodemodeSandbox,
@@ -8109,37 +8119,55 @@ var StreamableHttpTransport = class extends TransportEvents {
8109
8119
  }
8110
8120
  };
8111
8121
 
8122
+ // src/client/reap.ts
8123
+ var POLL_MS = 25;
8124
+ async function gone(proc, exited, ms) {
8125
+ const end = Date.now() + ms;
8126
+ for (; ; ) {
8127
+ if (exited() && !await proc.groupAlive?.()) return true;
8128
+ if (Date.now() >= end) return false;
8129
+ await new Promise((r) => setTimeout(r, POLL_MS));
8130
+ }
8131
+ }
8132
+ async function reap(proc, graceMs) {
8133
+ let exited = false;
8134
+ void proc.wait().then(
8135
+ () => {
8136
+ exited = true;
8137
+ },
8138
+ () => {
8139
+ exited = true;
8140
+ }
8141
+ );
8142
+ try {
8143
+ await proc.kill("SIGTERM");
8144
+ } catch {
8145
+ }
8146
+ if (await gone(proc, () => exited, graceMs)) return;
8147
+ try {
8148
+ await proc.kill("SIGKILL");
8149
+ } catch {
8150
+ }
8151
+ await gone(proc, () => exited, graceMs);
8152
+ }
8153
+
8112
8154
  // src/client/stdio-registry.ts
8113
8155
  var GRACE_MS = 2e3;
8114
8156
  var registry = /* @__PURE__ */ new Set();
8115
8157
  function trackStdio(proc) {
8116
8158
  registry.add(proc);
8117
- void proc.wait().then(
8118
- () => registry.delete(proc),
8119
- () => registry.delete(proc)
8120
- );
8159
+ }
8160
+ async function reapStdio(proc, graceMs = GRACE_MS) {
8161
+ try {
8162
+ await reap(proc, graceMs);
8163
+ } finally {
8164
+ registry.delete(proc);
8165
+ }
8121
8166
  }
8122
8167
  async function killAllStdio() {
8123
8168
  const procs = [...registry];
8124
8169
  if (procs.length === 0) return;
8125
- await Promise.all(
8126
- procs.map(async (proc) => {
8127
- try {
8128
- await proc.kill("SIGTERM");
8129
- } catch {
8130
- }
8131
- const exited = await Promise.race([
8132
- proc.wait().then(() => true),
8133
- new Promise((r) => setTimeout(() => r(false), GRACE_MS))
8134
- ]);
8135
- if (!exited) {
8136
- try {
8137
- await proc.kill("SIGKILL");
8138
- } catch {
8139
- }
8140
- }
8141
- })
8142
- );
8170
+ await Promise.all(procs.map((proc) => reapStdio(proc)));
8143
8171
  registry.clear();
8144
8172
  }
8145
8173
 
@@ -8245,16 +8273,8 @@ var PlatformStdioTransport = class {
8245
8273
  await proc.stdin.close();
8246
8274
  } catch {
8247
8275
  }
8248
- const exited = await Promise.race([done, delay(grace)]);
8249
- if (!exited) {
8250
- await proc.kill("SIGTERM");
8251
- const exitedAfterTerm = await Promise.race([done, delay(closeTimeoutMs)]);
8252
- if (!exitedAfterTerm) {
8253
- await proc.kill("SIGKILL");
8254
- await done;
8255
- }
8256
- }
8257
- await proc.kill("SIGTERM");
8276
+ await Promise.race([done, delay(grace)]);
8277
+ await reapStdio(proc, closeTimeoutMs);
8258
8278
  this.emitClose();
8259
8279
  }
8260
8280
  async readStdout(pending) {
@@ -8938,10 +8958,7 @@ function buildConnectionMethods(rpc, discoverResult2, proc) {
8938
8958
  },
8939
8959
  async close() {
8940
8960
  rpc.close();
8941
- try {
8942
- await proc.kill("SIGTERM");
8943
- } catch {
8944
- }
8961
+ await reapStdio(proc);
8945
8962
  }
8946
8963
  };
8947
8964
  }
@@ -9367,7 +9384,7 @@ function toolDeclaration(tool) {
9367
9384
  };
9368
9385
  }
9369
9386
  async function runDescribeTs(platform2, targets, budget) {
9370
- const { renderDeclarations: renderDeclarations2 } = await Promise.resolve().then(() => (init_dist2(), dist_exports));
9387
+ const { renderDeclarations: renderDeclarations2 } = await Promise.resolve().then(() => (init_dist2(), dist_exports2));
9371
9388
  const allTools = [];
9372
9389
  for (const target of targets) {
9373
9390
  const tools = await target.conn.listTools();
@@ -9438,6 +9455,39 @@ async function runGrep(platform2, pattern, targets, options) {
9438
9455
  // src/codemode/run.ts
9439
9456
  init_dist2();
9440
9457
  init_source();
9458
+
9459
+ // src/codemode/deadline.ts
9460
+ var expiries = /* @__PURE__ */ new WeakMap();
9461
+ var hooked = /* @__PURE__ */ new WeakSet();
9462
+ function hookDeadlines(quickjs) {
9463
+ if (hooked.has(quickjs)) return;
9464
+ hooked.add(quickjs);
9465
+ const create = quickjs.create.bind(quickjs);
9466
+ quickjs.create = (options) => {
9467
+ const expired = options.wasm ? expiries.get(options.wasm) : void 0;
9468
+ const inner = options.interruptHandler;
9469
+ if (!expired || !inner) return create(options);
9470
+ return create({ ...options, interruptHandler: () => expired() || inner() });
9471
+ };
9472
+ }
9473
+ function bindDeadline(wasm, timeoutMs, now = Date.now) {
9474
+ let endsAt = Number.POSITIVE_INFINITY;
9475
+ let tripped = false;
9476
+ expiries.set(wasm, () => {
9477
+ if (!tripped && now() >= endsAt) tripped = true;
9478
+ return tripped;
9479
+ });
9480
+ return {
9481
+ start() {
9482
+ endsAt = now() + timeoutMs;
9483
+ },
9484
+ get tripped() {
9485
+ return tripped;
9486
+ }
9487
+ };
9488
+ }
9489
+
9490
+ // src/codemode/run.ts
9441
9491
  async function loadStore(platform2, stateDir2) {
9442
9492
  const storePath = `${stateDir2}/codemode-store.json`;
9443
9493
  try {
@@ -9623,9 +9673,23 @@ async function runCode(targets, source, options) {
9623
9673
  ...buildDiscoveryGlobals(allDeclaredTools, namespaces, searchDocs),
9624
9674
  ...options.allowShell ? [buildShellGlobal(platform2)] : []
9625
9675
  ];
9626
- const sandbox = await createSandbox(platform2, toolImpls, globals, timeoutMs, memoryLimitBytes);
9676
+ const { sandbox, deadline } = await createSandbox(
9677
+ platform2,
9678
+ toolImpls,
9679
+ globals,
9680
+ timeoutMs,
9681
+ memoryLimitBytes
9682
+ );
9627
9683
  try {
9628
- const result = await sandbox.execute(code2, { store });
9684
+ deadline?.start();
9685
+ const executed = await sandbox.execute(code2, { store });
9686
+ const result = deadline?.tripped && !executed.ok ? {
9687
+ ...executed,
9688
+ error: {
9689
+ kind: "timeout",
9690
+ message: `Execution timed out after ${timeoutMs} ms`
9691
+ }
9692
+ } : executed;
9629
9693
  if (result.ok) {
9630
9694
  const writes = result.storeWrites;
9631
9695
  if (Object.keys(writes.set).length > 0 || writes.delete.length > 0) {
@@ -9671,12 +9735,23 @@ async function createSandbox(platform2, tools, globals, timeoutMs, memoryLimitBy
9671
9735
  const isKernel2 = platform2.packageDir !== void 0;
9672
9736
  if (isKernel2) {
9673
9737
  const { InlineCodemodeSandbox: InlineCodemodeSandbox2 } = await Promise.resolve().then(() => (init_portable(), portable_exports));
9738
+ const { QuickJS: QuickJS2 } = await Promise.resolve().then(() => (init_dist(), dist_exports));
9739
+ hookDeadlines(QuickJS2);
9674
9740
  const wasm = await loadWasmKernel(platform2);
9675
- return new InlineCodemodeSandbox2({ tools, globals, timeoutMs, wasm, memoryLimitBytes });
9741
+ const deadline = bindDeadline(wasm, timeoutMs);
9742
+ const sandbox2 = new InlineCodemodeSandbox2({
9743
+ tools,
9744
+ globals,
9745
+ timeoutMs,
9746
+ wasm,
9747
+ memoryLimitBytes,
9748
+ interruptBudget: Number.POSITIVE_INFINITY
9749
+ });
9750
+ return { sandbox: sandbox2, deadline };
9676
9751
  }
9677
- const { CodemodeSandbox: CodemodeSandbox2, loadQuickJSWasm: loadQuickJSWasm2 } = await Promise.resolve().then(() => (init_dist2(), dist_exports));
9752
+ const { CodemodeSandbox: CodemodeSandbox2, loadQuickJSWasm: loadQuickJSWasm2 } = await Promise.resolve().then(() => (init_dist2(), dist_exports2));
9678
9753
  const wasmPath = await resolveWasmPath(platform2);
9679
- return new CodemodeSandbox2({
9754
+ const sandbox = new CodemodeSandbox2({
9680
9755
  tools,
9681
9756
  globals,
9682
9757
  timeoutMs,
@@ -9684,6 +9759,7 @@ async function createSandbox(platform2, tools, globals, timeoutMs, memoryLimitBy
9684
9759
  wasm: loadQuickJSWasm2(wasmPath),
9685
9760
  workerUrl: new URL("./codemode-worker.mjs", import.meta.url)
9686
9761
  });
9762
+ return { sandbox };
9687
9763
  }
9688
9764
  async function resolveWasmPath(platform2) {
9689
9765
  const candidates = [
@@ -12752,6 +12828,11 @@ function concat(chunks) {
12752
12828
  return out;
12753
12829
  }
12754
12830
  var KILL_GRACE_MS = 2e3;
12831
+ async function trapShutdown(platform2, handler) {
12832
+ await Promise.all(
12833
+ ["SIGINT", "SIGTERM", "SIGHUP"].map((sig) => platform2.onSignal?.(sig, handler))
12834
+ );
12835
+ }
12755
12836
  async function killInflight() {
12756
12837
  const procs = [...inflightProcs];
12757
12838
  if (procs.length === 0) return;
@@ -12968,8 +13049,7 @@ async function discoverWithSignal(platform2, commands, childEnv, cwd) {
12968
13049
  earlyAbort.abort();
12969
13050
  inflightDone = killInflight();
12970
13051
  };
12971
- platform2.onSignal?.("SIGTERM", onShutdown);
12972
- platform2.onSignal?.("SIGINT", onShutdown);
13052
+ await trapShutdown(platform2, onShutdown);
12973
13053
  for (const cmd of commands) {
12974
13054
  try {
12975
13055
  const cli = await discoverCli(platform2, cmd.path, cmd.name, childEnv, cwd, earlyAbort.signal);
@@ -13300,6 +13380,17 @@ async function runWebMcpHttp(platform2, options, getTools, callTool, serverName,
13300
13380
  const address = serveAddress(platform2, options.host, actualPort);
13301
13381
  const listenUrl = address.url;
13302
13382
  let tokenFilePath;
13383
+ let shutdownRequested = false;
13384
+ const shutdown = () => {
13385
+ if (shutdownRequested) return;
13386
+ shutdownRequested = true;
13387
+ listener.close();
13388
+ if (tokenFilePath) {
13389
+ platform2.fs.unlink?.(tokenFilePath).catch(() => {
13390
+ });
13391
+ }
13392
+ };
13393
+ await trapShutdown(platform2, shutdown);
13303
13394
  if (generatedBearer && resolvedBearer) {
13304
13395
  tokenFilePath = await publishBearer(
13305
13396
  platform2,
@@ -13308,31 +13399,22 @@ async function runWebMcpHttp(platform2, options, getTools, callTool, serverName,
13308
13399
  address,
13309
13400
  options.json
13310
13401
  );
13402
+ if (shutdownRequested && tokenFilePath) {
13403
+ await platform2.fs.unlink?.(tokenFilePath).catch(() => {
13404
+ });
13405
+ }
13311
13406
  } else {
13312
13407
  if (options.json) {
13313
13408
  await platform2.write(1, `${JSON.stringify(address)}
13314
13409
  `);
13315
13410
  }
13316
13411
  }
13317
- await platform2.write(2, `mcpcat serve --webmcp: listening on ${listenUrl}
13412
+ if (!shutdownRequested) {
13413
+ await platform2.write(2, `mcpcat serve --webmcp: listening on ${listenUrl}
13318
13414
  `);
13319
- if (options.onListening) {
13320
- await options.onListening(actualPort);
13321
- }
13322
- let shutdownRequested = false;
13323
- const shutdown = () => {
13324
- if (shutdownRequested) return;
13325
- shutdownRequested = true;
13326
- listener.close();
13327
- if (tokenFilePath) {
13328
- platform2.fs.unlink?.(tokenFilePath).catch(() => {
13329
- });
13415
+ if (options.onListening) {
13416
+ await options.onListening(actualPort);
13330
13417
  }
13331
- };
13332
- if (platform2.onSignal) {
13333
- platform2.onSignal("SIGINT", shutdown);
13334
- platform2.onSignal("SIGTERM", shutdown);
13335
- platform2.onSignal("SIGHUP", shutdown);
13336
13418
  }
13337
13419
  const handler = async (request) => {
13338
13420
  if (request.target !== "/mcp") {
@@ -13496,6 +13578,19 @@ async function runProxyHttp(platform2, options, proxy, resolvedBearer, generated
13496
13578
  const address = serveAddress(platform2, options.host, actualPort);
13497
13579
  const listenUrl = address.url;
13498
13580
  let tokenFilePath;
13581
+ let shutdownRequested = false;
13582
+ let proxyDone;
13583
+ const shutdown = () => {
13584
+ if (shutdownRequested) return;
13585
+ shutdownRequested = true;
13586
+ listener.close();
13587
+ proxyDone = proxy.shutdown();
13588
+ if (tokenFilePath) {
13589
+ platform2.fs.unlink?.(tokenFilePath).catch(() => {
13590
+ });
13591
+ }
13592
+ };
13593
+ await trapShutdown(platform2, shutdown);
13499
13594
  if (generatedBearer && resolvedBearer) {
13500
13595
  tokenFilePath = await publishBearer(
13501
13596
  platform2,
@@ -13504,32 +13599,22 @@ async function runProxyHttp(platform2, options, proxy, resolvedBearer, generated
13504
13599
  address,
13505
13600
  options.json
13506
13601
  );
13602
+ if (shutdownRequested && tokenFilePath) {
13603
+ await platform2.fs.unlink?.(tokenFilePath).catch(() => {
13604
+ });
13605
+ }
13507
13606
  } else {
13508
13607
  if (options.json) {
13509
13608
  await platform2.write(1, `${JSON.stringify(address)}
13510
13609
  `);
13511
13610
  }
13512
13611
  }
13513
- await platform2.write(2, `mcpcat serve --proxy: listening on ${listenUrl}
13612
+ if (!shutdownRequested) {
13613
+ await platform2.write(2, `mcpcat serve --proxy: listening on ${listenUrl}
13514
13614
  `);
13515
- if (options.onListening) {
13516
- await options.onListening(actualPort);
13517
- }
13518
- let shutdownRequested = false;
13519
- const shutdown = () => {
13520
- if (shutdownRequested) return;
13521
- shutdownRequested = true;
13522
- listener.close();
13523
- void proxy.shutdown();
13524
- if (tokenFilePath) {
13525
- platform2.fs.unlink?.(tokenFilePath).catch(() => {
13526
- });
13615
+ if (options.onListening) {
13616
+ await options.onListening(actualPort);
13527
13617
  }
13528
- };
13529
- if (platform2.onSignal) {
13530
- platform2.onSignal("SIGINT", shutdown);
13531
- platform2.onSignal("SIGTERM", shutdown);
13532
- platform2.onSignal("SIGHUP", shutdown);
13533
13618
  }
13534
13619
  const handler = (request) => handleProxyMcpRequest(
13535
13620
  request,
@@ -13554,6 +13639,7 @@ async function runProxyHttp(platform2, options, proxy, resolvedBearer, generated
13554
13639
  } catch {
13555
13640
  if (!shutdownRequested) throw new Error("listener closed unexpectedly");
13556
13641
  }
13642
+ await proxyDone;
13557
13643
  return 0;
13558
13644
  }
13559
13645
  async function handleProxyMcpRequest(request, proxy, serverOrigins, allowedOrigins, allowedHosts, resolvedBearer) {
@@ -13673,6 +13759,19 @@ async function runHttpServer(platform2, options, tools, call, serverName, versio
13673
13759
  const address = serveAddress(platform2, options.host, actualPort);
13674
13760
  const listenUrl = address.url;
13675
13761
  let tokenFilePath;
13762
+ let shutdownRequested = false;
13763
+ let inflightDone;
13764
+ const shutdown = () => {
13765
+ if (shutdownRequested) return;
13766
+ shutdownRequested = true;
13767
+ listener.close();
13768
+ inflightDone = killInflight();
13769
+ if (tokenFilePath) {
13770
+ platform2.fs.unlink?.(tokenFilePath).catch(() => {
13771
+ });
13772
+ }
13773
+ };
13774
+ await trapShutdown(platform2, shutdown);
13676
13775
  if (generatedBearer && resolvedBearer) {
13677
13776
  tokenFilePath = await publishBearer(
13678
13777
  platform2,
@@ -13681,30 +13780,19 @@ async function runHttpServer(platform2, options, tools, call, serverName, versio
13681
13780
  address,
13682
13781
  options.json
13683
13782
  );
13783
+ if (shutdownRequested && tokenFilePath) {
13784
+ await platform2.fs.unlink?.(tokenFilePath).catch(() => {
13785
+ });
13786
+ }
13684
13787
  } else {
13685
13788
  if (options.json) {
13686
13789
  await platform2.write(1, `${JSON.stringify(address)}
13687
13790
  `);
13688
13791
  }
13689
13792
  }
13690
- await platform2.write(2, `mcpcat serve: listening on ${listenUrl}
13793
+ if (!shutdownRequested) {
13794
+ await platform2.write(2, `mcpcat serve: listening on ${listenUrl}
13691
13795
  `);
13692
- let shutdownRequested = false;
13693
- let inflightDone;
13694
- const shutdown = () => {
13695
- if (shutdownRequested) return;
13696
- shutdownRequested = true;
13697
- listener.close();
13698
- inflightDone = killInflight();
13699
- if (tokenFilePath) {
13700
- platform2.fs.unlink?.(tokenFilePath).catch(() => {
13701
- });
13702
- }
13703
- };
13704
- if (platform2.onSignal) {
13705
- platform2.onSignal("SIGINT", shutdown);
13706
- platform2.onSignal("SIGTERM", shutdown);
13707
- platform2.onSignal("SIGHUP", shutdown);
13708
13796
  }
13709
13797
  const handler = (request) => handleMcpRequest(
13710
13798
  request,
@@ -16084,8 +16172,10 @@ async function dispatchMultiTarget(platform2, parsed, version) {
16084
16172
  });
16085
16173
  platform2.exit(exitCode);
16086
16174
  }
16087
- platform2.onSignal?.("SIGTERM", () => void killTreeAndExit(143));
16088
- platform2.onSignal?.("SIGHUP", () => void killTreeAndExit(129));
16175
+ await Promise.all([
16176
+ platform2.onSignal?.("SIGTERM", () => void killTreeAndExit(143)),
16177
+ platform2.onSignal?.("SIGHUP", () => void killTreeAndExit(129))
16178
+ ]);
16089
16179
  const config = await MergedConfigSource.load(platform2);
16090
16180
  if (parsed.verb === "code") {
16091
16181
  let source;
@@ -16446,12 +16536,14 @@ async function runClientVerb(platform2, version, parsed) {
16446
16536
  });
16447
16537
  platform2.exit(exitCode);
16448
16538
  }
16449
- platform2.onSignal?.("SIGINT", () => {
16450
- interrupted = true;
16451
- sigintController.abort();
16452
- });
16453
- platform2.onSignal?.("SIGTERM", () => void killTreeAndExit(143));
16454
- platform2.onSignal?.("SIGHUP", () => void killTreeAndExit(129));
16539
+ await Promise.all([
16540
+ platform2.onSignal?.("SIGINT", () => {
16541
+ interrupted = true;
16542
+ sigintController.abort();
16543
+ }),
16544
+ platform2.onSignal?.("SIGTERM", () => void killTreeAndExit(143)),
16545
+ platform2.onSignal?.("SIGHUP", () => void killTreeAndExit(129))
16546
+ ]);
16455
16547
  const showProgress = !parsed.silent && (parsed.verbose || platform2.isatty(2));
16456
16548
  const onProgress = (progress, total, message) => {
16457
16549
  if (!showProgress) return;
@@ -16678,6 +16770,15 @@ function nodeSpawn(options) {
16678
16770
  child.kill(sig);
16679
16771
  } catch {
16680
16772
  }
16773
+ },
16774
+ async groupAlive() {
16775
+ if (!USE_PROCESS_GROUPS2 || child.pid === void 0) return false;
16776
+ try {
16777
+ process.kill(-child.pid, 0);
16778
+ return true;
16779
+ } catch {
16780
+ return false;
16781
+ }
16681
16782
  }
16682
16783
  });
16683
16784
  }
@@ -16856,5 +16957,5 @@ try {
16856
16957
  });
16857
16958
  } catch {
16858
16959
  }
16859
- var code = await run(platform, "0.15.6", args);
16960
+ var code = await run(platform, "0.15.8", args);
16860
16961
  process.exit(code);
@@ -1475,6 +1475,16 @@ var init_version = __esm({
1475
1475
  });
1476
1476
 
1477
1477
  // node_modules/quickjs-wasi/dist/index.js
1478
+ var dist_exports = {};
1479
+ __export(dist_exports, {
1480
+ CompileFlags: () => CompileFlags,
1481
+ EvalFlags: () => EvalFlags,
1482
+ Intrinsics: () => Intrinsics,
1483
+ JSException: () => JSException,
1484
+ JSValueHandle: () => JSValueHandle,
1485
+ MAX_STACK_SIZE: () => MAX_STACK_SIZE,
1486
+ QuickJS: () => QuickJS
1487
+ });
1478
1488
  function stringKeyNeedsValuePath(key) {
1479
1489
  return key.includes("\0") || LONE_SURROGATE_RE.test(key);
1480
1490
  }
@@ -4986,8 +4996,8 @@ SOURCE: /[\s\S]+/
4986
4996
  });
4987
4997
 
4988
4998
  // node_modules/@earendil-works/pi-codemode/dist/index.js
4989
- var dist_exports = {};
4990
- __export(dist_exports, {
4999
+ var dist_exports2 = {};
5000
+ __export(dist_exports2, {
4991
5001
  CODEMODE_OPTIONS_PREFIX: () => CODEMODE_OPTIONS_PREFIX,
4992
5002
  CODEMODE_SOURCE_GRAMMAR: () => CODEMODE_SOURCE_GRAMMAR,
4993
5003
  CodemodeSandbox: () => CodemodeSandbox,
@@ -7730,37 +7740,55 @@ var StreamableHttpTransport = class extends TransportEvents {
7730
7740
  }
7731
7741
  };
7732
7742
 
7743
+ // src/client/reap.ts
7744
+ var POLL_MS = 25;
7745
+ async function gone(proc, exited, ms) {
7746
+ const end = Date.now() + ms;
7747
+ for (; ; ) {
7748
+ if (exited() && !await proc.groupAlive?.()) return true;
7749
+ if (Date.now() >= end) return false;
7750
+ await new Promise((r) => setTimeout(r, POLL_MS));
7751
+ }
7752
+ }
7753
+ async function reap(proc, graceMs) {
7754
+ let exited = false;
7755
+ void proc.wait().then(
7756
+ () => {
7757
+ exited = true;
7758
+ },
7759
+ () => {
7760
+ exited = true;
7761
+ }
7762
+ );
7763
+ try {
7764
+ await proc.kill("SIGTERM");
7765
+ } catch {
7766
+ }
7767
+ if (await gone(proc, () => exited, graceMs)) return;
7768
+ try {
7769
+ await proc.kill("SIGKILL");
7770
+ } catch {
7771
+ }
7772
+ await gone(proc, () => exited, graceMs);
7773
+ }
7774
+
7733
7775
  // src/client/stdio-registry.ts
7734
7776
  var GRACE_MS = 2e3;
7735
7777
  var registry = /* @__PURE__ */ new Set();
7736
7778
  function trackStdio(proc) {
7737
7779
  registry.add(proc);
7738
- void proc.wait().then(
7739
- () => registry.delete(proc),
7740
- () => registry.delete(proc)
7741
- );
7780
+ }
7781
+ async function reapStdio(proc, graceMs = GRACE_MS) {
7782
+ try {
7783
+ await reap(proc, graceMs);
7784
+ } finally {
7785
+ registry.delete(proc);
7786
+ }
7742
7787
  }
7743
7788
  async function killAllStdio() {
7744
7789
  const procs = [...registry];
7745
7790
  if (procs.length === 0) return;
7746
- await Promise.all(
7747
- procs.map(async (proc) => {
7748
- try {
7749
- await proc.kill("SIGTERM");
7750
- } catch {
7751
- }
7752
- const exited = await Promise.race([
7753
- proc.wait().then(() => true),
7754
- new Promise((r) => setTimeout(() => r(false), GRACE_MS))
7755
- ]);
7756
- if (!exited) {
7757
- try {
7758
- await proc.kill("SIGKILL");
7759
- } catch {
7760
- }
7761
- }
7762
- })
7763
- );
7791
+ await Promise.all(procs.map((proc) => reapStdio(proc)));
7764
7792
  registry.clear();
7765
7793
  }
7766
7794
 
@@ -7866,16 +7894,8 @@ var PlatformStdioTransport = class {
7866
7894
  await proc.stdin.close();
7867
7895
  } catch {
7868
7896
  }
7869
- const exited = await Promise.race([done, delay(grace)]);
7870
- if (!exited) {
7871
- await proc.kill("SIGTERM");
7872
- const exitedAfterTerm = await Promise.race([done, delay(closeTimeoutMs)]);
7873
- if (!exitedAfterTerm) {
7874
- await proc.kill("SIGKILL");
7875
- await done;
7876
- }
7877
- }
7878
- await proc.kill("SIGTERM");
7897
+ await Promise.race([done, delay(grace)]);
7898
+ await reapStdio(proc, closeTimeoutMs);
7879
7899
  this.emitClose();
7880
7900
  }
7881
7901
  async readStdout(pending) {
@@ -8559,10 +8579,7 @@ function buildConnectionMethods(rpc, discoverResult2, proc) {
8559
8579
  },
8560
8580
  async close() {
8561
8581
  rpc.close();
8562
- try {
8563
- await proc.kill("SIGTERM");
8564
- } catch {
8565
- }
8582
+ await reapStdio(proc);
8566
8583
  }
8567
8584
  };
8568
8585
  }
@@ -8988,7 +9005,7 @@ function toolDeclaration(tool) {
8988
9005
  };
8989
9006
  }
8990
9007
  async function runDescribeTs(platform2, targets, budget) {
8991
- const { renderDeclarations: renderDeclarations2 } = await Promise.resolve().then(() => (init_dist2(), dist_exports));
9008
+ const { renderDeclarations: renderDeclarations2 } = await Promise.resolve().then(() => (init_dist2(), dist_exports2));
8992
9009
  const allTools = [];
8993
9010
  for (const target of targets) {
8994
9011
  const tools = await target.conn.listTools();
@@ -9059,6 +9076,39 @@ async function runGrep(platform2, pattern, targets, options) {
9059
9076
  // src/codemode/run.ts
9060
9077
  init_dist2();
9061
9078
  init_source();
9079
+
9080
+ // src/codemode/deadline.ts
9081
+ var expiries = /* @__PURE__ */ new WeakMap();
9082
+ var hooked = /* @__PURE__ */ new WeakSet();
9083
+ function hookDeadlines(quickjs) {
9084
+ if (hooked.has(quickjs)) return;
9085
+ hooked.add(quickjs);
9086
+ const create = quickjs.create.bind(quickjs);
9087
+ quickjs.create = (options) => {
9088
+ const expired = options.wasm ? expiries.get(options.wasm) : void 0;
9089
+ const inner = options.interruptHandler;
9090
+ if (!expired || !inner) return create(options);
9091
+ return create({ ...options, interruptHandler: () => expired() || inner() });
9092
+ };
9093
+ }
9094
+ function bindDeadline(wasm, timeoutMs, now = Date.now) {
9095
+ let endsAt = Number.POSITIVE_INFINITY;
9096
+ let tripped = false;
9097
+ expiries.set(wasm, () => {
9098
+ if (!tripped && now() >= endsAt) tripped = true;
9099
+ return tripped;
9100
+ });
9101
+ return {
9102
+ start() {
9103
+ endsAt = now() + timeoutMs;
9104
+ },
9105
+ get tripped() {
9106
+ return tripped;
9107
+ }
9108
+ };
9109
+ }
9110
+
9111
+ // src/codemode/run.ts
9062
9112
  async function loadStore(platform2, stateDir2) {
9063
9113
  const storePath = `${stateDir2}/codemode-store.json`;
9064
9114
  try {
@@ -9244,9 +9294,23 @@ async function runCode(targets, source, options) {
9244
9294
  ...buildDiscoveryGlobals(allDeclaredTools, namespaces, searchDocs),
9245
9295
  ...options.allowShell ? [buildShellGlobal(platform2)] : []
9246
9296
  ];
9247
- const sandbox = await createSandbox(platform2, toolImpls, globals, timeoutMs, memoryLimitBytes);
9297
+ const { sandbox, deadline } = await createSandbox(
9298
+ platform2,
9299
+ toolImpls,
9300
+ globals,
9301
+ timeoutMs,
9302
+ memoryLimitBytes
9303
+ );
9248
9304
  try {
9249
- const result = await sandbox.execute(code, { store });
9305
+ deadline?.start();
9306
+ const executed = await sandbox.execute(code, { store });
9307
+ const result = deadline?.tripped && !executed.ok ? {
9308
+ ...executed,
9309
+ error: {
9310
+ kind: "timeout",
9311
+ message: `Execution timed out after ${timeoutMs} ms`
9312
+ }
9313
+ } : executed;
9250
9314
  if (result.ok) {
9251
9315
  const writes = result.storeWrites;
9252
9316
  if (Object.keys(writes.set).length > 0 || writes.delete.length > 0) {
@@ -9292,12 +9356,23 @@ async function createSandbox(platform2, tools, globals, timeoutMs, memoryLimitBy
9292
9356
  const isKernel2 = platform2.packageDir !== void 0;
9293
9357
  if (isKernel2) {
9294
9358
  const { InlineCodemodeSandbox: InlineCodemodeSandbox2 } = await Promise.resolve().then(() => (init_portable(), portable_exports));
9359
+ const { QuickJS: QuickJS2 } = await Promise.resolve().then(() => (init_dist(), dist_exports));
9360
+ hookDeadlines(QuickJS2);
9295
9361
  const wasm = await loadWasmKernel(platform2);
9296
- return new InlineCodemodeSandbox2({ tools, globals, timeoutMs, wasm, memoryLimitBytes });
9362
+ const deadline = bindDeadline(wasm, timeoutMs);
9363
+ const sandbox2 = new InlineCodemodeSandbox2({
9364
+ tools,
9365
+ globals,
9366
+ timeoutMs,
9367
+ wasm,
9368
+ memoryLimitBytes,
9369
+ interruptBudget: Number.POSITIVE_INFINITY
9370
+ });
9371
+ return { sandbox: sandbox2, deadline };
9297
9372
  }
9298
- const { CodemodeSandbox: CodemodeSandbox2, loadQuickJSWasm: loadQuickJSWasm2 } = await Promise.resolve().then(() => (init_dist2(), dist_exports));
9373
+ const { CodemodeSandbox: CodemodeSandbox2, loadQuickJSWasm: loadQuickJSWasm2 } = await Promise.resolve().then(() => (init_dist2(), dist_exports2));
9299
9374
  const wasmPath = await resolveWasmPath(platform2);
9300
- return new CodemodeSandbox2({
9375
+ const sandbox = new CodemodeSandbox2({
9301
9376
  tools,
9302
9377
  globals,
9303
9378
  timeoutMs,
@@ -9305,6 +9380,7 @@ async function createSandbox(platform2, tools, globals, timeoutMs, memoryLimitBy
9305
9380
  wasm: loadQuickJSWasm2(wasmPath),
9306
9381
  workerUrl: new URL("./codemode-worker.mjs", import.meta.url)
9307
9382
  });
9383
+ return { sandbox };
9308
9384
  }
9309
9385
  async function resolveWasmPath(platform2) {
9310
9386
  const candidates = [
@@ -12373,6 +12449,11 @@ function concat(chunks) {
12373
12449
  return out;
12374
12450
  }
12375
12451
  var KILL_GRACE_MS = 2e3;
12452
+ async function trapShutdown(platform2, handler) {
12453
+ await Promise.all(
12454
+ ["SIGINT", "SIGTERM", "SIGHUP"].map((sig) => platform2.onSignal?.(sig, handler))
12455
+ );
12456
+ }
12376
12457
  async function killInflight() {
12377
12458
  const procs = [...inflightProcs];
12378
12459
  if (procs.length === 0) return;
@@ -12589,8 +12670,7 @@ async function discoverWithSignal(platform2, commands, childEnv, cwd) {
12589
12670
  earlyAbort.abort();
12590
12671
  inflightDone = killInflight();
12591
12672
  };
12592
- platform2.onSignal?.("SIGTERM", onShutdown);
12593
- platform2.onSignal?.("SIGINT", onShutdown);
12673
+ await trapShutdown(platform2, onShutdown);
12594
12674
  for (const cmd of commands) {
12595
12675
  try {
12596
12676
  const cli = await discoverCli(platform2, cmd.path, cmd.name, childEnv, cwd, earlyAbort.signal);
@@ -12921,6 +13001,17 @@ async function runWebMcpHttp(platform2, options, getTools, callTool, serverName,
12921
13001
  const address = serveAddress(platform2, options.host, actualPort);
12922
13002
  const listenUrl = address.url;
12923
13003
  let tokenFilePath;
13004
+ let shutdownRequested = false;
13005
+ const shutdown = () => {
13006
+ if (shutdownRequested) return;
13007
+ shutdownRequested = true;
13008
+ listener.close();
13009
+ if (tokenFilePath) {
13010
+ platform2.fs.unlink?.(tokenFilePath).catch(() => {
13011
+ });
13012
+ }
13013
+ };
13014
+ await trapShutdown(platform2, shutdown);
12924
13015
  if (generatedBearer && resolvedBearer) {
12925
13016
  tokenFilePath = await publishBearer(
12926
13017
  platform2,
@@ -12929,31 +13020,22 @@ async function runWebMcpHttp(platform2, options, getTools, callTool, serverName,
12929
13020
  address,
12930
13021
  options.json
12931
13022
  );
13023
+ if (shutdownRequested && tokenFilePath) {
13024
+ await platform2.fs.unlink?.(tokenFilePath).catch(() => {
13025
+ });
13026
+ }
12932
13027
  } else {
12933
13028
  if (options.json) {
12934
13029
  await platform2.write(1, `${JSON.stringify(address)}
12935
13030
  `);
12936
13031
  }
12937
13032
  }
12938
- await platform2.write(2, `mcpcat serve --webmcp: listening on ${listenUrl}
13033
+ if (!shutdownRequested) {
13034
+ await platform2.write(2, `mcpcat serve --webmcp: listening on ${listenUrl}
12939
13035
  `);
12940
- if (options.onListening) {
12941
- await options.onListening(actualPort);
12942
- }
12943
- let shutdownRequested = false;
12944
- const shutdown = () => {
12945
- if (shutdownRequested) return;
12946
- shutdownRequested = true;
12947
- listener.close();
12948
- if (tokenFilePath) {
12949
- platform2.fs.unlink?.(tokenFilePath).catch(() => {
12950
- });
13036
+ if (options.onListening) {
13037
+ await options.onListening(actualPort);
12951
13038
  }
12952
- };
12953
- if (platform2.onSignal) {
12954
- platform2.onSignal("SIGINT", shutdown);
12955
- platform2.onSignal("SIGTERM", shutdown);
12956
- platform2.onSignal("SIGHUP", shutdown);
12957
13039
  }
12958
13040
  const handler = async (request) => {
12959
13041
  if (request.target !== "/mcp") {
@@ -13117,6 +13199,19 @@ async function runProxyHttp(platform2, options, proxy, resolvedBearer, generated
13117
13199
  const address = serveAddress(platform2, options.host, actualPort);
13118
13200
  const listenUrl = address.url;
13119
13201
  let tokenFilePath;
13202
+ let shutdownRequested = false;
13203
+ let proxyDone;
13204
+ const shutdown = () => {
13205
+ if (shutdownRequested) return;
13206
+ shutdownRequested = true;
13207
+ listener.close();
13208
+ proxyDone = proxy.shutdown();
13209
+ if (tokenFilePath) {
13210
+ platform2.fs.unlink?.(tokenFilePath).catch(() => {
13211
+ });
13212
+ }
13213
+ };
13214
+ await trapShutdown(platform2, shutdown);
13120
13215
  if (generatedBearer && resolvedBearer) {
13121
13216
  tokenFilePath = await publishBearer(
13122
13217
  platform2,
@@ -13125,32 +13220,22 @@ async function runProxyHttp(platform2, options, proxy, resolvedBearer, generated
13125
13220
  address,
13126
13221
  options.json
13127
13222
  );
13223
+ if (shutdownRequested && tokenFilePath) {
13224
+ await platform2.fs.unlink?.(tokenFilePath).catch(() => {
13225
+ });
13226
+ }
13128
13227
  } else {
13129
13228
  if (options.json) {
13130
13229
  await platform2.write(1, `${JSON.stringify(address)}
13131
13230
  `);
13132
13231
  }
13133
13232
  }
13134
- await platform2.write(2, `mcpcat serve --proxy: listening on ${listenUrl}
13233
+ if (!shutdownRequested) {
13234
+ await platform2.write(2, `mcpcat serve --proxy: listening on ${listenUrl}
13135
13235
  `);
13136
- if (options.onListening) {
13137
- await options.onListening(actualPort);
13138
- }
13139
- let shutdownRequested = false;
13140
- const shutdown = () => {
13141
- if (shutdownRequested) return;
13142
- shutdownRequested = true;
13143
- listener.close();
13144
- void proxy.shutdown();
13145
- if (tokenFilePath) {
13146
- platform2.fs.unlink?.(tokenFilePath).catch(() => {
13147
- });
13236
+ if (options.onListening) {
13237
+ await options.onListening(actualPort);
13148
13238
  }
13149
- };
13150
- if (platform2.onSignal) {
13151
- platform2.onSignal("SIGINT", shutdown);
13152
- platform2.onSignal("SIGTERM", shutdown);
13153
- platform2.onSignal("SIGHUP", shutdown);
13154
13239
  }
13155
13240
  const handler = (request) => handleProxyMcpRequest(
13156
13241
  request,
@@ -13175,6 +13260,7 @@ async function runProxyHttp(platform2, options, proxy, resolvedBearer, generated
13175
13260
  } catch {
13176
13261
  if (!shutdownRequested) throw new Error("listener closed unexpectedly");
13177
13262
  }
13263
+ await proxyDone;
13178
13264
  return 0;
13179
13265
  }
13180
13266
  async function handleProxyMcpRequest(request, proxy, serverOrigins, allowedOrigins, allowedHosts, resolvedBearer) {
@@ -13294,6 +13380,19 @@ async function runHttpServer(platform2, options, tools, call, serverName, versio
13294
13380
  const address = serveAddress(platform2, options.host, actualPort);
13295
13381
  const listenUrl = address.url;
13296
13382
  let tokenFilePath;
13383
+ let shutdownRequested = false;
13384
+ let inflightDone;
13385
+ const shutdown = () => {
13386
+ if (shutdownRequested) return;
13387
+ shutdownRequested = true;
13388
+ listener.close();
13389
+ inflightDone = killInflight();
13390
+ if (tokenFilePath) {
13391
+ platform2.fs.unlink?.(tokenFilePath).catch(() => {
13392
+ });
13393
+ }
13394
+ };
13395
+ await trapShutdown(platform2, shutdown);
13297
13396
  if (generatedBearer && resolvedBearer) {
13298
13397
  tokenFilePath = await publishBearer(
13299
13398
  platform2,
@@ -13302,30 +13401,19 @@ async function runHttpServer(platform2, options, tools, call, serverName, versio
13302
13401
  address,
13303
13402
  options.json
13304
13403
  );
13404
+ if (shutdownRequested && tokenFilePath) {
13405
+ await platform2.fs.unlink?.(tokenFilePath).catch(() => {
13406
+ });
13407
+ }
13305
13408
  } else {
13306
13409
  if (options.json) {
13307
13410
  await platform2.write(1, `${JSON.stringify(address)}
13308
13411
  `);
13309
13412
  }
13310
13413
  }
13311
- await platform2.write(2, `mcpcat serve: listening on ${listenUrl}
13414
+ if (!shutdownRequested) {
13415
+ await platform2.write(2, `mcpcat serve: listening on ${listenUrl}
13312
13416
  `);
13313
- let shutdownRequested = false;
13314
- let inflightDone;
13315
- const shutdown = () => {
13316
- if (shutdownRequested) return;
13317
- shutdownRequested = true;
13318
- listener.close();
13319
- inflightDone = killInflight();
13320
- if (tokenFilePath) {
13321
- platform2.fs.unlink?.(tokenFilePath).catch(() => {
13322
- });
13323
- }
13324
- };
13325
- if (platform2.onSignal) {
13326
- platform2.onSignal("SIGINT", shutdown);
13327
- platform2.onSignal("SIGTERM", shutdown);
13328
- platform2.onSignal("SIGHUP", shutdown);
13329
13417
  }
13330
13418
  const handler = (request) => handleMcpRequest(
13331
13419
  request,
@@ -15705,8 +15793,10 @@ async function dispatchMultiTarget(platform2, parsed, version) {
15705
15793
  });
15706
15794
  platform2.exit(exitCode);
15707
15795
  }
15708
- platform2.onSignal?.("SIGTERM", () => void killTreeAndExit(143));
15709
- platform2.onSignal?.("SIGHUP", () => void killTreeAndExit(129));
15796
+ await Promise.all([
15797
+ platform2.onSignal?.("SIGTERM", () => void killTreeAndExit(143)),
15798
+ platform2.onSignal?.("SIGHUP", () => void killTreeAndExit(129))
15799
+ ]);
15710
15800
  const config = await MergedConfigSource.load(platform2);
15711
15801
  if (parsed.verb === "code") {
15712
15802
  let source;
@@ -16067,12 +16157,14 @@ async function runClientVerb(platform2, version, parsed) {
16067
16157
  });
16068
16158
  platform2.exit(exitCode);
16069
16159
  }
16070
- platform2.onSignal?.("SIGINT", () => {
16071
- interrupted = true;
16072
- sigintController.abort();
16073
- });
16074
- platform2.onSignal?.("SIGTERM", () => void killTreeAndExit(143));
16075
- platform2.onSignal?.("SIGHUP", () => void killTreeAndExit(129));
16160
+ await Promise.all([
16161
+ platform2.onSignal?.("SIGINT", () => {
16162
+ interrupted = true;
16163
+ sigintController.abort();
16164
+ }),
16165
+ platform2.onSignal?.("SIGTERM", () => void killTreeAndExit(143)),
16166
+ platform2.onSignal?.("SIGHUP", () => void killTreeAndExit(129))
16167
+ ]);
16076
16168
  const showProgress = !parsed.silent && (parsed.verbose || platform2.isatty(2));
16077
16169
  const onProgress = (progress, total, message) => {
16078
16170
  if (!showProgress) return;
@@ -16320,6 +16412,15 @@ async function kernelSpawn(ctx, options) {
16320
16412
  await ctx.kill(childPid, signal);
16321
16413
  } catch {
16322
16414
  }
16415
+ },
16416
+ async groupAlive() {
16417
+ if (!useGroupKill) return false;
16418
+ try {
16419
+ await ctx.kill(-childPid, "SIGCONT");
16420
+ return true;
16421
+ } catch {
16422
+ return false;
16423
+ }
16323
16424
  }
16324
16425
  };
16325
16426
  }
@@ -16555,8 +16656,8 @@ function createKernelPlatform(ctx, options) {
16555
16656
  exit(code) {
16556
16657
  ctx.exit(code);
16557
16658
  },
16558
- onSignal(signal, handler) {
16559
- ctx.signals.on(signal, handler);
16659
+ async onSignal(signal, handler) {
16660
+ await ctx.signals.on(signal, handler);
16560
16661
  },
16561
16662
  async seal(request) {
16562
16663
  return kernelSeal(ctx, request);
@@ -16577,7 +16678,7 @@ async function main(ctx) {
16577
16678
  packageDir: "/node_modules/@ai-ecoverse/mcpcat"
16578
16679
  });
16579
16680
  const args = ctx.argv.slice(1);
16580
- return run(platform2, "0.15.6", args);
16681
+ return run(platform2, "0.15.8", args);
16581
16682
  }
16582
16683
  export {
16583
16684
  main as default
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-ecoverse/mcpcat",
3
- "version": "0.15.6",
3
+ "version": "0.15.8",
4
4
  "description": "A curl for the Model Context Protocol",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -56,8 +56,9 @@
56
56
  "trust:dry": "npx fledgling sync --dry-run -y --skip-publish"
57
57
  },
58
58
  "devDependencies": {
59
- "@ai-ecoverse/slicc-kernel": "1.94.1",
60
- "@ai-ecoverse/slicc-shared-web": "1.17.0",
59
+ "@ai-ecoverse/slicc-bios": "1.1.2",
60
+ "@ai-ecoverse/slicc-kernel": "1.94.4",
61
+ "@ai-ecoverse/slicc-shared-web": "1.20.0",
61
62
  "@biomejs/biome": "2.5.15",
62
63
  "@earendil-works/pi-codemode": "1.2.0",
63
64
  "@earendil-works/pi-mcp": "1.2.0",