@ai-ecoverse/mcpcat 0.18.1 → 0.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -6
- package/dist/host/mcpcat.mjs +147 -64
- package/dist/kernel/mcpcat.mjs +147 -64
- package/package.json +6 -11
package/README.md
CHANGED
|
@@ -22,13 +22,11 @@ Or via npm:
|
|
|
22
22
|
npm install -g @ai-ecoverse/mcpcat
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
-
mcpcat runs natively on macOS, Linux and Windows (Node ≥
|
|
25
|
+
mcpcat runs natively on macOS, Linux and Windows (Node ≥ 26), and inside Sliccy Seven as a kernel program, with the same flags, the same output and the same config file in both places. Orphan-free shutdown of stdio server trees in the kernel needs slicc-kernel ≥ 1.74.0; on older kernels only the direct child is signalled. A SIGKILLed mcpcat runs no handler. On slicc-kernel ≥ 1.88.0, mcpcat spawns stdio servers and served commands with a parent death signal (like Linux `PR_SET_PDEATHSIG`), so the kernel SIGKILLs each server's process group when mcpcat dies. On older kernels, and on the host, where there is no portable equivalent, servers that exit on stdin EOF (most MCP servers) still stop, but servers that ignore it keep running.
|
|
26
26
|
|
|
27
|
-
##
|
|
27
|
+
## CI and releases
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
Silence never passes: every gate in the workflows prints a verdict line, and its caller requires that line, not just an exit code. These are `all-green: PASS|FAIL …`, the cert job's `cert: kernel <v> — N kernel tests passed`, shipped.mjs's `behaviour:`/`not behaviour:`, and the installer tests' `All installer tests passed.`. A gate that exits 0 without its line fails.
|
|
29
|
+
`.github/workflows/ci.yml` is the only workflow. It calls [slicc-shared-web's pipeline](https://github.com/ai-ecoverse/slicc-shared-web#pipeline): lint (Biome, tsc and the shared rules) on every event; on pull requests and in the merge queue the integration suite (`npm test`, every `test/integration/*.test.mjs` on Node 26, split over Linux shards); on pull requests a Claude and a Codex review. The checks to require are `ci / integration` and `ci / review`. Every merge to `main` is released to npm as `latest` by semantic-release, with provenance, through trusted publishing bound to `ci.yml`. When a merge releases (a new `v*` tag at the commit), the `binaries` job packs the published tarball, runs the installer tests on Ubuntu (with and without Node) and Alpine, and attaches the tarball, `install.sh` and `SHA256SUMS` to the GitHub release. `kernel-floor.test.mjs` runs the kernel tests on the `engines.slicc-kernel` floor; `MCPCAT_KERNEL_DIR` points the kernel tests at any installed `@ai-ecoverse/slicc-kernel`.
|
|
32
30
|
|
|
33
31
|
## Quick start
|
|
34
32
|
|
|
@@ -40,7 +38,7 @@ mcpcat serve --http 8808 ./scripts/jira.jsh
|
|
|
40
38
|
|
|
41
39
|
## Agent skill
|
|
42
40
|
|
|
43
|
-
The package ships an [Agent Skill](https://agentskills.io/specification) that teaches an agent mcpcat in one file: `dist/skills/mcpcat/SKILL.md`, built from [`src/skill/SKILL.txt`](src/skill/SKILL.txt) (the repo keeps no Markdown besides README.md and AGENTS.md). It covers targets, listing and calling tools, `mcp.json`, exit codes, and the secrets rule: `${NAME}` placeholders in single quotes, never a value in a command, a file or the conversation, and never a token in `mcp.json`. `mcpcat help --skill` prints the copy that matches the installed version. `package.json` declares it as a pi package skill (`"pi": { "skills": ["dist/skills"] }`). In seven, bios links it into `/usr/share/skills` for the
|
|
41
|
+
The package ships an [Agent Skill](https://agentskills.io/specification) that teaches an agent mcpcat in one file: `dist/skills/mcpcat/SKILL.md`, built from [`src/skill/SKILL.txt`](src/skill/SKILL.txt) (the repo keeps no Markdown besides README.md and AGENTS.md). It covers targets, listing and calling tools, `mcp.json`, exit codes, and the secrets rule: `${NAME}` placeholders in single quotes, never a value in a command, a file or the conversation, and never a token in `mcp.json`. `mcpcat help --skill` prints the copy that matches the installed version. `package.json` declares it as a pi package skill (`"pi": { "skills": ["dist/skills"] }`). In seven, bios links it into `/usr/share/skills` for the catalog install.
|
|
44
42
|
|
|
45
43
|
## License
|
|
46
44
|
|
package/dist/host/mcpcat.mjs
CHANGED
|
@@ -11905,7 +11905,40 @@ function parseRequestLine(buf, endPos) {
|
|
|
11905
11905
|
return { method, target, headers, headerEnd: endPos + 4 };
|
|
11906
11906
|
}
|
|
11907
11907
|
async function handleConnection(readable, writable, handler) {
|
|
11908
|
-
const
|
|
11908
|
+
const raw = readable.getReader();
|
|
11909
|
+
let ahead = null;
|
|
11910
|
+
const parked = [];
|
|
11911
|
+
const reader = {
|
|
11912
|
+
read() {
|
|
11913
|
+
const queued = parked.shift();
|
|
11914
|
+
if (queued) return Promise.resolve(queued);
|
|
11915
|
+
const next = ahead ?? raw.read();
|
|
11916
|
+
ahead = null;
|
|
11917
|
+
return next;
|
|
11918
|
+
}
|
|
11919
|
+
};
|
|
11920
|
+
const watch = async (live, disconnect) => {
|
|
11921
|
+
let held = 0;
|
|
11922
|
+
while (held <= MAX_HEADER_SIZE + MAX_BODY_SIZE) {
|
|
11923
|
+
const pending = ahead ?? raw.read();
|
|
11924
|
+
ahead = pending;
|
|
11925
|
+
let result;
|
|
11926
|
+
try {
|
|
11927
|
+
result = await pending;
|
|
11928
|
+
} catch {
|
|
11929
|
+
if (live.on) disconnect.abort();
|
|
11930
|
+
return;
|
|
11931
|
+
}
|
|
11932
|
+
if (!live.on) return;
|
|
11933
|
+
ahead = null;
|
|
11934
|
+
parked.push(result);
|
|
11935
|
+
if (result.done) {
|
|
11936
|
+
disconnect.abort();
|
|
11937
|
+
return;
|
|
11938
|
+
}
|
|
11939
|
+
held += result.value.length;
|
|
11940
|
+
}
|
|
11941
|
+
};
|
|
11909
11942
|
const writer = writable.getWriter();
|
|
11910
11943
|
let buf = new Uint8Array(4096);
|
|
11911
11944
|
let bufLen = 0;
|
|
@@ -11951,7 +11984,16 @@ async function handleConnection(readable, writable, handler) {
|
|
|
11951
11984
|
headers: parsed.headers,
|
|
11952
11985
|
body
|
|
11953
11986
|
};
|
|
11954
|
-
const
|
|
11987
|
+
const disconnect = new AbortController();
|
|
11988
|
+
const live = { on: true };
|
|
11989
|
+
void watch(live, disconnect);
|
|
11990
|
+
let response;
|
|
11991
|
+
try {
|
|
11992
|
+
response = await handler(request, disconnect.signal);
|
|
11993
|
+
} finally {
|
|
11994
|
+
live.on = false;
|
|
11995
|
+
}
|
|
11996
|
+
if (disconnect.signal.aborted) break;
|
|
11955
11997
|
await writeResponse(writer, response);
|
|
11956
11998
|
const connectionHeader = (parsed.headers.connection ?? "").toLowerCase();
|
|
11957
11999
|
if (connectionHeader === "close") break;
|
|
@@ -11968,7 +12010,7 @@ async function handleConnection(readable, writable, handler) {
|
|
|
11968
12010
|
}
|
|
11969
12011
|
}
|
|
11970
12012
|
} finally {
|
|
11971
|
-
|
|
12013
|
+
raw.releaseLock();
|
|
11972
12014
|
try {
|
|
11973
12015
|
await writer.close();
|
|
11974
12016
|
} catch {
|
|
@@ -12821,7 +12863,8 @@ function capOutput(value, limit) {
|
|
|
12821
12863
|
${value.slice(value.length - half)}`;
|
|
12822
12864
|
}
|
|
12823
12865
|
var inflightProcs = /* @__PURE__ */ new Set();
|
|
12824
|
-
async function runCommand(platform2, cmdPath, argv, childEnv, stdin, timeoutMs, maxOutput, cwd, signal) {
|
|
12866
|
+
async function runCommand(platform2, cmdPath, argv, childEnv, stdin, timeoutMs, maxOutput, cwd, signal, cancel) {
|
|
12867
|
+
if (cancel?.aborted) return { stdout: "", stderr: "cancelled", exitCode: 124 };
|
|
12825
12868
|
let proc;
|
|
12826
12869
|
try {
|
|
12827
12870
|
proc = await platform2.spawn({
|
|
@@ -12834,16 +12877,14 @@ async function runCommand(platform2, cmdPath, argv, childEnv, stdin, timeoutMs,
|
|
|
12834
12877
|
return { stdout: "", stderr: `spawn failed: ${err.message}`, exitCode: 127 };
|
|
12835
12878
|
}
|
|
12836
12879
|
inflightProcs.add(proc);
|
|
12837
|
-
|
|
12880
|
+
const feedStdin = async () => {
|
|
12838
12881
|
const writer = proc.stdin.getWriter();
|
|
12839
|
-
await writer.write(new TextEncoder().encode(stdin));
|
|
12840
|
-
await writer.close();
|
|
12841
|
-
} else {
|
|
12842
12882
|
try {
|
|
12843
|
-
await
|
|
12883
|
+
if (stdin !== void 0) await writer.write(new TextEncoder().encode(stdin));
|
|
12884
|
+
await writer.close();
|
|
12844
12885
|
} catch {
|
|
12845
12886
|
}
|
|
12846
|
-
}
|
|
12887
|
+
};
|
|
12847
12888
|
const stdoutChunks = [];
|
|
12848
12889
|
const stderrChunks = [];
|
|
12849
12890
|
const readStream3 = async (stream, into) => {
|
|
@@ -12860,6 +12901,7 @@ async function runCommand(platform2, cmdPath, argv, childEnv, stdin, timeoutMs,
|
|
|
12860
12901
|
};
|
|
12861
12902
|
let timerId;
|
|
12862
12903
|
let onAbort;
|
|
12904
|
+
let onCancel;
|
|
12863
12905
|
const timer = new Promise((resolve) => {
|
|
12864
12906
|
timerId = setTimeout(() => resolve("timeout"), timeoutMs);
|
|
12865
12907
|
});
|
|
@@ -12871,34 +12913,40 @@ async function runCommand(platform2, cmdPath, argv, childEnv, stdin, timeoutMs,
|
|
|
12871
12913
|
onAbort = () => resolve("timeout");
|
|
12872
12914
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
12873
12915
|
}) : null;
|
|
12916
|
+
const cancelPromise = cancel ? new Promise((resolve) => {
|
|
12917
|
+
if (cancel.aborted) {
|
|
12918
|
+
resolve("cancel");
|
|
12919
|
+
return;
|
|
12920
|
+
}
|
|
12921
|
+
onCancel = () => resolve("cancel");
|
|
12922
|
+
cancel.addEventListener("abort", onCancel, { once: true });
|
|
12923
|
+
}) : null;
|
|
12874
12924
|
const work = Promise.all([
|
|
12875
12925
|
readStream3(proc.stdout, stdoutChunks),
|
|
12876
12926
|
readStream3(proc.stderr, stderrChunks),
|
|
12877
|
-
proc.wait()
|
|
12927
|
+
proc.wait(),
|
|
12928
|
+
feedStdin()
|
|
12878
12929
|
]);
|
|
12879
|
-
const racers = [
|
|
12880
|
-
work,
|
|
12881
|
-
timer
|
|
12882
|
-
];
|
|
12930
|
+
const racers = [work, timer];
|
|
12883
12931
|
if (abortPromise !== null) racers.push(abortPromise);
|
|
12932
|
+
if (cancelPromise !== null) racers.push(cancelPromise);
|
|
12884
12933
|
let result;
|
|
12885
12934
|
try {
|
|
12886
12935
|
result = await Promise.race(racers);
|
|
12887
12936
|
} finally {
|
|
12888
12937
|
clearTimeout(timerId);
|
|
12889
12938
|
if (onAbort) signal?.removeEventListener("abort", onAbort);
|
|
12939
|
+
if (onCancel) cancel?.removeEventListener("abort", onCancel);
|
|
12890
12940
|
}
|
|
12891
|
-
if (result === "timeout") {
|
|
12892
|
-
|
|
12893
|
-
|
|
12894
|
-
|
|
12895
|
-
|
|
12896
|
-
await proc.kill("SIGKILL");
|
|
12897
|
-
} catch {
|
|
12898
|
-
}
|
|
12941
|
+
if (result === "timeout" || result === "cancel") {
|
|
12942
|
+
if (signal?.aborted) {
|
|
12943
|
+
await proc.kill("SIGTERM");
|
|
12944
|
+
} else {
|
|
12945
|
+
await terminate(proc, CALL_KILL_GRACE_MS);
|
|
12899
12946
|
}
|
|
12900
12947
|
inflightProcs.delete(proc);
|
|
12901
|
-
|
|
12948
|
+
const stderr = result === "cancel" ? "cancelled" : "timed out";
|
|
12949
|
+
return { stdout: "", stderr, exitCode: 124 };
|
|
12902
12950
|
}
|
|
12903
12951
|
inflightProcs.delete(proc);
|
|
12904
12952
|
try {
|
|
@@ -12924,6 +12972,31 @@ function concat(chunks) {
|
|
|
12924
12972
|
return out;
|
|
12925
12973
|
}
|
|
12926
12974
|
var KILL_GRACE_MS = 2e3;
|
|
12975
|
+
var CALL_KILL_GRACE_MS = 5e3;
|
|
12976
|
+
async function terminate(proc, graceMs) {
|
|
12977
|
+
try {
|
|
12978
|
+
await proc.kill("SIGTERM");
|
|
12979
|
+
} catch {
|
|
12980
|
+
}
|
|
12981
|
+
let graceId;
|
|
12982
|
+
let exited;
|
|
12983
|
+
try {
|
|
12984
|
+
exited = await Promise.race([
|
|
12985
|
+
proc.wait().then(() => true),
|
|
12986
|
+
new Promise((r) => {
|
|
12987
|
+
graceId = setTimeout(() => r(false), graceMs);
|
|
12988
|
+
})
|
|
12989
|
+
]);
|
|
12990
|
+
} finally {
|
|
12991
|
+
clearTimeout(graceId);
|
|
12992
|
+
}
|
|
12993
|
+
if (!exited) {
|
|
12994
|
+
try {
|
|
12995
|
+
await proc.kill("SIGKILL");
|
|
12996
|
+
} catch {
|
|
12997
|
+
}
|
|
12998
|
+
}
|
|
12999
|
+
}
|
|
12927
13000
|
var SIGNAL_EXIT = { SIGINT: 130, SIGTERM: 143, SIGHUP: 129 };
|
|
12928
13001
|
async function trapShutdown(platform2, handler) {
|
|
12929
13002
|
await Promise.all(
|
|
@@ -12935,24 +13008,7 @@ async function trapShutdown(platform2, handler) {
|
|
|
12935
13008
|
async function killInflight() {
|
|
12936
13009
|
const procs = [...inflightProcs];
|
|
12937
13010
|
if (procs.length === 0) return;
|
|
12938
|
-
await Promise.all(
|
|
12939
|
-
procs.map(async (proc) => {
|
|
12940
|
-
try {
|
|
12941
|
-
await proc.kill("SIGTERM");
|
|
12942
|
-
} catch {
|
|
12943
|
-
}
|
|
12944
|
-
const exited = await Promise.race([
|
|
12945
|
-
proc.wait().then(() => true),
|
|
12946
|
-
new Promise((r) => setTimeout(() => r(false), KILL_GRACE_MS))
|
|
12947
|
-
]);
|
|
12948
|
-
if (!exited) {
|
|
12949
|
-
try {
|
|
12950
|
-
await proc.kill("SIGKILL");
|
|
12951
|
-
} catch {
|
|
12952
|
-
}
|
|
12953
|
-
}
|
|
12954
|
-
})
|
|
12955
|
-
);
|
|
13011
|
+
await Promise.all(procs.map((proc) => terminate(proc, KILL_GRACE_MS)));
|
|
12956
13012
|
inflightProcs.clear();
|
|
12957
13013
|
}
|
|
12958
13014
|
async function runHelpForCli(platform2, filePath, childEnv, cwd, signal) {
|
|
@@ -13049,7 +13105,7 @@ function createToolExecutor(platform2, clis, allTools, childEnv, options, serveC
|
|
|
13049
13105
|
);
|
|
13050
13106
|
return run2;
|
|
13051
13107
|
}
|
|
13052
|
-
function invokeTemplateTool(tool, args2) {
|
|
13108
|
+
function invokeTemplateTool(tool, args2, cancel) {
|
|
13053
13109
|
const templateArgs = {};
|
|
13054
13110
|
if (args2 && typeof args2 === "object") {
|
|
13055
13111
|
for (const [k, v] of Object.entries(args2)) {
|
|
@@ -13067,11 +13123,13 @@ function createToolExecutor(platform2, clis, allTools, childEnv, options, serveC
|
|
|
13067
13123
|
void 0,
|
|
13068
13124
|
options.callTimeout,
|
|
13069
13125
|
options.maxOutput,
|
|
13070
|
-
serveCwd
|
|
13126
|
+
serveCwd,
|
|
13127
|
+
void 0,
|
|
13128
|
+
cancel
|
|
13071
13129
|
)
|
|
13072
13130
|
);
|
|
13073
13131
|
}
|
|
13074
|
-
async function executeTool(tool, cli, args2) {
|
|
13132
|
+
async function executeTool(tool, cli, args2, cancel) {
|
|
13075
13133
|
if (tool.kind === "help") {
|
|
13076
13134
|
const result = await runCommand(
|
|
13077
13135
|
platform2,
|
|
@@ -13081,7 +13139,9 @@ function createToolExecutor(platform2, clis, allTools, childEnv, options, serveC
|
|
|
13081
13139
|
void 0,
|
|
13082
13140
|
options.callTimeout,
|
|
13083
13141
|
options.maxOutput,
|
|
13084
|
-
serveCwd
|
|
13142
|
+
serveCwd,
|
|
13143
|
+
void 0,
|
|
13144
|
+
cancel
|
|
13085
13145
|
);
|
|
13086
13146
|
if (result.exitCode === 0) return result;
|
|
13087
13147
|
return runCommand(
|
|
@@ -13092,7 +13152,9 @@ function createToolExecutor(platform2, clis, allTools, childEnv, options, serveC
|
|
|
13092
13152
|
void 0,
|
|
13093
13153
|
options.callTimeout,
|
|
13094
13154
|
options.maxOutput,
|
|
13095
|
-
serveCwd
|
|
13155
|
+
serveCwd,
|
|
13156
|
+
void 0,
|
|
13157
|
+
cancel
|
|
13096
13158
|
);
|
|
13097
13159
|
}
|
|
13098
13160
|
if (tool.kind === "invoke") {
|
|
@@ -13107,7 +13169,9 @@ function createToolExecutor(platform2, clis, allTools, childEnv, options, serveC
|
|
|
13107
13169
|
parsed2.stdin,
|
|
13108
13170
|
options.callTimeout,
|
|
13109
13171
|
options.maxOutput,
|
|
13110
|
-
serveCwd
|
|
13172
|
+
serveCwd,
|
|
13173
|
+
void 0,
|
|
13174
|
+
cancel
|
|
13111
13175
|
);
|
|
13112
13176
|
}
|
|
13113
13177
|
const parsed = readToolArguments(args2 ?? {});
|
|
@@ -13123,18 +13187,20 @@ function createToolExecutor(platform2, clis, allTools, childEnv, options, serveC
|
|
|
13123
13187
|
planned.stdin,
|
|
13124
13188
|
options.callTimeout,
|
|
13125
13189
|
options.maxOutput,
|
|
13126
|
-
serveCwd
|
|
13190
|
+
serveCwd,
|
|
13191
|
+
void 0,
|
|
13192
|
+
cancel
|
|
13127
13193
|
);
|
|
13128
13194
|
}
|
|
13129
|
-
async function invoke(name, args2) {
|
|
13195
|
+
async function invoke(name, args2, cancel) {
|
|
13130
13196
|
const tool = allTools.find((t) => t.name === name);
|
|
13131
13197
|
if (!tool) return { stdout: "", stderr: `unknown tool ${name}`, exitCode: 1 };
|
|
13132
13198
|
if (tool.cli === "" && tool.commandPath.length === 0) {
|
|
13133
|
-
return invokeTemplateTool(tool, args2);
|
|
13199
|
+
return invokeTemplateTool(tool, args2, cancel);
|
|
13134
13200
|
}
|
|
13135
13201
|
const cli = clis.find((c) => c.name === tool.cli);
|
|
13136
13202
|
if (!cli) return { stdout: "", stderr: `unknown cli ${tool.cli}`, exitCode: 1 };
|
|
13137
|
-
return enqueue(cli.name, () => executeTool(tool, cli, args2));
|
|
13203
|
+
return enqueue(cli.name, () => executeTool(tool, cli, args2, cancel));
|
|
13138
13204
|
}
|
|
13139
13205
|
return { invoke, rpcTools };
|
|
13140
13206
|
}
|
|
@@ -13878,10 +13944,13 @@ async function runHttpServer(platform2, options, tools, call, serverName, versio
|
|
|
13878
13944
|
let tokenFilePath;
|
|
13879
13945
|
let shutdownRequested = false;
|
|
13880
13946
|
let inflightDone;
|
|
13947
|
+
const stopCalls = new AbortController();
|
|
13948
|
+
const activeCalls = /* @__PURE__ */ new Set();
|
|
13881
13949
|
const shutdown = () => {
|
|
13882
13950
|
if (shutdownRequested) return;
|
|
13883
13951
|
shutdownRequested = true;
|
|
13884
13952
|
listener.close();
|
|
13953
|
+
stopCalls.abort();
|
|
13885
13954
|
inflightDone = killInflight();
|
|
13886
13955
|
if (tokenFilePath) {
|
|
13887
13956
|
platform2.fs.unlink?.(tokenFilePath).catch(() => {
|
|
@@ -13911,17 +13980,30 @@ async function runHttpServer(platform2, options, tools, call, serverName, versio
|
|
|
13911
13980
|
await platform2.write(2, `mcpcat serve: listening on ${listenUrl}
|
|
13912
13981
|
`);
|
|
13913
13982
|
}
|
|
13914
|
-
const handler = (request) =>
|
|
13915
|
-
|
|
13916
|
-
|
|
13917
|
-
|
|
13918
|
-
|
|
13919
|
-
|
|
13920
|
-
|
|
13921
|
-
|
|
13922
|
-
|
|
13923
|
-
|
|
13924
|
-
|
|
13983
|
+
const handler = (request, signal) => {
|
|
13984
|
+
const cancel = new AbortController();
|
|
13985
|
+
const relay = () => cancel.abort();
|
|
13986
|
+
signal.addEventListener("abort", relay, { once: true });
|
|
13987
|
+
stopCalls.signal.addEventListener("abort", relay, { once: true });
|
|
13988
|
+
if (signal.aborted || stopCalls.signal.aborted) relay();
|
|
13989
|
+
const pending = handleMcpRequest(
|
|
13990
|
+
request,
|
|
13991
|
+
tools,
|
|
13992
|
+
(name, args2) => call(name, args2, cancel.signal),
|
|
13993
|
+
serverName,
|
|
13994
|
+
version,
|
|
13995
|
+
serverOrigins,
|
|
13996
|
+
allowedOrigins,
|
|
13997
|
+
allowedHosts,
|
|
13998
|
+
resolvedBearer
|
|
13999
|
+
).finally(() => {
|
|
14000
|
+
signal.removeEventListener("abort", relay);
|
|
14001
|
+
stopCalls.signal.removeEventListener("abort", relay);
|
|
14002
|
+
activeCalls.delete(pending);
|
|
14003
|
+
});
|
|
14004
|
+
activeCalls.add(pending);
|
|
14005
|
+
return pending;
|
|
14006
|
+
};
|
|
13925
14007
|
try {
|
|
13926
14008
|
for await (const connection of listener) {
|
|
13927
14009
|
if (shutdownRequested) break;
|
|
@@ -13943,6 +14025,7 @@ async function runHttpServer(platform2, options, tools, call, serverName, versio
|
|
|
13943
14025
|
} catch {
|
|
13944
14026
|
}
|
|
13945
14027
|
}
|
|
14028
|
+
await Promise.allSettled([...activeCalls]);
|
|
13946
14029
|
return 0;
|
|
13947
14030
|
}
|
|
13948
14031
|
async function handleMcpRequest(request, tools, call, serverName, version, serverOrigins, allowedOrigins, allowedHosts, resolvedBearer) {
|
|
@@ -17105,5 +17188,5 @@ try {
|
|
|
17105
17188
|
});
|
|
17106
17189
|
} catch {
|
|
17107
17190
|
}
|
|
17108
|
-
var code = await run(platform, "0.
|
|
17191
|
+
var code = await run(platform, "0.19.0", args);
|
|
17109
17192
|
process.exit(code);
|
package/dist/kernel/mcpcat.mjs
CHANGED
|
@@ -11526,7 +11526,40 @@ function parseRequestLine(buf, endPos) {
|
|
|
11526
11526
|
return { method, target, headers, headerEnd: endPos + 4 };
|
|
11527
11527
|
}
|
|
11528
11528
|
async function handleConnection(readable, writable, handler) {
|
|
11529
|
-
const
|
|
11529
|
+
const raw = readable.getReader();
|
|
11530
|
+
let ahead = null;
|
|
11531
|
+
const parked = [];
|
|
11532
|
+
const reader = {
|
|
11533
|
+
read() {
|
|
11534
|
+
const queued = parked.shift();
|
|
11535
|
+
if (queued) return Promise.resolve(queued);
|
|
11536
|
+
const next = ahead ?? raw.read();
|
|
11537
|
+
ahead = null;
|
|
11538
|
+
return next;
|
|
11539
|
+
}
|
|
11540
|
+
};
|
|
11541
|
+
const watch = async (live, disconnect) => {
|
|
11542
|
+
let held = 0;
|
|
11543
|
+
while (held <= MAX_HEADER_SIZE + MAX_BODY_SIZE) {
|
|
11544
|
+
const pending = ahead ?? raw.read();
|
|
11545
|
+
ahead = pending;
|
|
11546
|
+
let result;
|
|
11547
|
+
try {
|
|
11548
|
+
result = await pending;
|
|
11549
|
+
} catch {
|
|
11550
|
+
if (live.on) disconnect.abort();
|
|
11551
|
+
return;
|
|
11552
|
+
}
|
|
11553
|
+
if (!live.on) return;
|
|
11554
|
+
ahead = null;
|
|
11555
|
+
parked.push(result);
|
|
11556
|
+
if (result.done) {
|
|
11557
|
+
disconnect.abort();
|
|
11558
|
+
return;
|
|
11559
|
+
}
|
|
11560
|
+
held += result.value.length;
|
|
11561
|
+
}
|
|
11562
|
+
};
|
|
11530
11563
|
const writer = writable.getWriter();
|
|
11531
11564
|
let buf = new Uint8Array(4096);
|
|
11532
11565
|
let bufLen = 0;
|
|
@@ -11572,7 +11605,16 @@ async function handleConnection(readable, writable, handler) {
|
|
|
11572
11605
|
headers: parsed.headers,
|
|
11573
11606
|
body
|
|
11574
11607
|
};
|
|
11575
|
-
const
|
|
11608
|
+
const disconnect = new AbortController();
|
|
11609
|
+
const live = { on: true };
|
|
11610
|
+
void watch(live, disconnect);
|
|
11611
|
+
let response;
|
|
11612
|
+
try {
|
|
11613
|
+
response = await handler(request, disconnect.signal);
|
|
11614
|
+
} finally {
|
|
11615
|
+
live.on = false;
|
|
11616
|
+
}
|
|
11617
|
+
if (disconnect.signal.aborted) break;
|
|
11576
11618
|
await writeResponse(writer, response);
|
|
11577
11619
|
const connectionHeader = (parsed.headers.connection ?? "").toLowerCase();
|
|
11578
11620
|
if (connectionHeader === "close") break;
|
|
@@ -11589,7 +11631,7 @@ async function handleConnection(readable, writable, handler) {
|
|
|
11589
11631
|
}
|
|
11590
11632
|
}
|
|
11591
11633
|
} finally {
|
|
11592
|
-
|
|
11634
|
+
raw.releaseLock();
|
|
11593
11635
|
try {
|
|
11594
11636
|
await writer.close();
|
|
11595
11637
|
} catch {
|
|
@@ -12442,7 +12484,8 @@ function capOutput(value, limit) {
|
|
|
12442
12484
|
${value.slice(value.length - half)}`;
|
|
12443
12485
|
}
|
|
12444
12486
|
var inflightProcs = /* @__PURE__ */ new Set();
|
|
12445
|
-
async function runCommand(platform2, cmdPath, argv, childEnv, stdin, timeoutMs, maxOutput, cwd, signal) {
|
|
12487
|
+
async function runCommand(platform2, cmdPath, argv, childEnv, stdin, timeoutMs, maxOutput, cwd, signal, cancel) {
|
|
12488
|
+
if (cancel?.aborted) return { stdout: "", stderr: "cancelled", exitCode: 124 };
|
|
12446
12489
|
let proc;
|
|
12447
12490
|
try {
|
|
12448
12491
|
proc = await platform2.spawn({
|
|
@@ -12455,16 +12498,14 @@ async function runCommand(platform2, cmdPath, argv, childEnv, stdin, timeoutMs,
|
|
|
12455
12498
|
return { stdout: "", stderr: `spawn failed: ${err.message}`, exitCode: 127 };
|
|
12456
12499
|
}
|
|
12457
12500
|
inflightProcs.add(proc);
|
|
12458
|
-
|
|
12501
|
+
const feedStdin = async () => {
|
|
12459
12502
|
const writer = proc.stdin.getWriter();
|
|
12460
|
-
await writer.write(new TextEncoder().encode(stdin));
|
|
12461
|
-
await writer.close();
|
|
12462
|
-
} else {
|
|
12463
12503
|
try {
|
|
12464
|
-
await
|
|
12504
|
+
if (stdin !== void 0) await writer.write(new TextEncoder().encode(stdin));
|
|
12505
|
+
await writer.close();
|
|
12465
12506
|
} catch {
|
|
12466
12507
|
}
|
|
12467
|
-
}
|
|
12508
|
+
};
|
|
12468
12509
|
const stdoutChunks = [];
|
|
12469
12510
|
const stderrChunks = [];
|
|
12470
12511
|
const readStream3 = async (stream, into) => {
|
|
@@ -12481,6 +12522,7 @@ async function runCommand(platform2, cmdPath, argv, childEnv, stdin, timeoutMs,
|
|
|
12481
12522
|
};
|
|
12482
12523
|
let timerId;
|
|
12483
12524
|
let onAbort;
|
|
12525
|
+
let onCancel;
|
|
12484
12526
|
const timer = new Promise((resolve2) => {
|
|
12485
12527
|
timerId = setTimeout(() => resolve2("timeout"), timeoutMs);
|
|
12486
12528
|
});
|
|
@@ -12492,34 +12534,40 @@ async function runCommand(platform2, cmdPath, argv, childEnv, stdin, timeoutMs,
|
|
|
12492
12534
|
onAbort = () => resolve2("timeout");
|
|
12493
12535
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
12494
12536
|
}) : null;
|
|
12537
|
+
const cancelPromise = cancel ? new Promise((resolve2) => {
|
|
12538
|
+
if (cancel.aborted) {
|
|
12539
|
+
resolve2("cancel");
|
|
12540
|
+
return;
|
|
12541
|
+
}
|
|
12542
|
+
onCancel = () => resolve2("cancel");
|
|
12543
|
+
cancel.addEventListener("abort", onCancel, { once: true });
|
|
12544
|
+
}) : null;
|
|
12495
12545
|
const work = Promise.all([
|
|
12496
12546
|
readStream3(proc.stdout, stdoutChunks),
|
|
12497
12547
|
readStream3(proc.stderr, stderrChunks),
|
|
12498
|
-
proc.wait()
|
|
12548
|
+
proc.wait(),
|
|
12549
|
+
feedStdin()
|
|
12499
12550
|
]);
|
|
12500
|
-
const racers = [
|
|
12501
|
-
work,
|
|
12502
|
-
timer
|
|
12503
|
-
];
|
|
12551
|
+
const racers = [work, timer];
|
|
12504
12552
|
if (abortPromise !== null) racers.push(abortPromise);
|
|
12553
|
+
if (cancelPromise !== null) racers.push(cancelPromise);
|
|
12505
12554
|
let result;
|
|
12506
12555
|
try {
|
|
12507
12556
|
result = await Promise.race(racers);
|
|
12508
12557
|
} finally {
|
|
12509
12558
|
clearTimeout(timerId);
|
|
12510
12559
|
if (onAbort) signal?.removeEventListener("abort", onAbort);
|
|
12560
|
+
if (onCancel) cancel?.removeEventListener("abort", onCancel);
|
|
12511
12561
|
}
|
|
12512
|
-
if (result === "timeout") {
|
|
12513
|
-
|
|
12514
|
-
|
|
12515
|
-
|
|
12516
|
-
|
|
12517
|
-
await proc.kill("SIGKILL");
|
|
12518
|
-
} catch {
|
|
12519
|
-
}
|
|
12562
|
+
if (result === "timeout" || result === "cancel") {
|
|
12563
|
+
if (signal?.aborted) {
|
|
12564
|
+
await proc.kill("SIGTERM");
|
|
12565
|
+
} else {
|
|
12566
|
+
await terminate(proc, CALL_KILL_GRACE_MS);
|
|
12520
12567
|
}
|
|
12521
12568
|
inflightProcs.delete(proc);
|
|
12522
|
-
|
|
12569
|
+
const stderr = result === "cancel" ? "cancelled" : "timed out";
|
|
12570
|
+
return { stdout: "", stderr, exitCode: 124 };
|
|
12523
12571
|
}
|
|
12524
12572
|
inflightProcs.delete(proc);
|
|
12525
12573
|
try {
|
|
@@ -12545,6 +12593,31 @@ function concat(chunks) {
|
|
|
12545
12593
|
return out;
|
|
12546
12594
|
}
|
|
12547
12595
|
var KILL_GRACE_MS = 2e3;
|
|
12596
|
+
var CALL_KILL_GRACE_MS = 5e3;
|
|
12597
|
+
async function terminate(proc, graceMs) {
|
|
12598
|
+
try {
|
|
12599
|
+
await proc.kill("SIGTERM");
|
|
12600
|
+
} catch {
|
|
12601
|
+
}
|
|
12602
|
+
let graceId;
|
|
12603
|
+
let exited;
|
|
12604
|
+
try {
|
|
12605
|
+
exited = await Promise.race([
|
|
12606
|
+
proc.wait().then(() => true),
|
|
12607
|
+
new Promise((r) => {
|
|
12608
|
+
graceId = setTimeout(() => r(false), graceMs);
|
|
12609
|
+
})
|
|
12610
|
+
]);
|
|
12611
|
+
} finally {
|
|
12612
|
+
clearTimeout(graceId);
|
|
12613
|
+
}
|
|
12614
|
+
if (!exited) {
|
|
12615
|
+
try {
|
|
12616
|
+
await proc.kill("SIGKILL");
|
|
12617
|
+
} catch {
|
|
12618
|
+
}
|
|
12619
|
+
}
|
|
12620
|
+
}
|
|
12548
12621
|
var SIGNAL_EXIT = { SIGINT: 130, SIGTERM: 143, SIGHUP: 129 };
|
|
12549
12622
|
async function trapShutdown(platform2, handler) {
|
|
12550
12623
|
await Promise.all(
|
|
@@ -12556,24 +12629,7 @@ async function trapShutdown(platform2, handler) {
|
|
|
12556
12629
|
async function killInflight() {
|
|
12557
12630
|
const procs = [...inflightProcs];
|
|
12558
12631
|
if (procs.length === 0) return;
|
|
12559
|
-
await Promise.all(
|
|
12560
|
-
procs.map(async (proc) => {
|
|
12561
|
-
try {
|
|
12562
|
-
await proc.kill("SIGTERM");
|
|
12563
|
-
} catch {
|
|
12564
|
-
}
|
|
12565
|
-
const exited = await Promise.race([
|
|
12566
|
-
proc.wait().then(() => true),
|
|
12567
|
-
new Promise((r) => setTimeout(() => r(false), KILL_GRACE_MS))
|
|
12568
|
-
]);
|
|
12569
|
-
if (!exited) {
|
|
12570
|
-
try {
|
|
12571
|
-
await proc.kill("SIGKILL");
|
|
12572
|
-
} catch {
|
|
12573
|
-
}
|
|
12574
|
-
}
|
|
12575
|
-
})
|
|
12576
|
-
);
|
|
12632
|
+
await Promise.all(procs.map((proc) => terminate(proc, KILL_GRACE_MS)));
|
|
12577
12633
|
inflightProcs.clear();
|
|
12578
12634
|
}
|
|
12579
12635
|
async function runHelpForCli(platform2, filePath, childEnv, cwd, signal) {
|
|
@@ -12670,7 +12726,7 @@ function createToolExecutor(platform2, clis, allTools, childEnv, options, serveC
|
|
|
12670
12726
|
);
|
|
12671
12727
|
return run2;
|
|
12672
12728
|
}
|
|
12673
|
-
function invokeTemplateTool(tool, args) {
|
|
12729
|
+
function invokeTemplateTool(tool, args, cancel) {
|
|
12674
12730
|
const templateArgs = {};
|
|
12675
12731
|
if (args && typeof args === "object") {
|
|
12676
12732
|
for (const [k, v] of Object.entries(args)) {
|
|
@@ -12688,11 +12744,13 @@ function createToolExecutor(platform2, clis, allTools, childEnv, options, serveC
|
|
|
12688
12744
|
void 0,
|
|
12689
12745
|
options.callTimeout,
|
|
12690
12746
|
options.maxOutput,
|
|
12691
|
-
serveCwd
|
|
12747
|
+
serveCwd,
|
|
12748
|
+
void 0,
|
|
12749
|
+
cancel
|
|
12692
12750
|
)
|
|
12693
12751
|
);
|
|
12694
12752
|
}
|
|
12695
|
-
async function executeTool(tool, cli, args) {
|
|
12753
|
+
async function executeTool(tool, cli, args, cancel) {
|
|
12696
12754
|
if (tool.kind === "help") {
|
|
12697
12755
|
const result = await runCommand(
|
|
12698
12756
|
platform2,
|
|
@@ -12702,7 +12760,9 @@ function createToolExecutor(platform2, clis, allTools, childEnv, options, serveC
|
|
|
12702
12760
|
void 0,
|
|
12703
12761
|
options.callTimeout,
|
|
12704
12762
|
options.maxOutput,
|
|
12705
|
-
serveCwd
|
|
12763
|
+
serveCwd,
|
|
12764
|
+
void 0,
|
|
12765
|
+
cancel
|
|
12706
12766
|
);
|
|
12707
12767
|
if (result.exitCode === 0) return result;
|
|
12708
12768
|
return runCommand(
|
|
@@ -12713,7 +12773,9 @@ function createToolExecutor(platform2, clis, allTools, childEnv, options, serveC
|
|
|
12713
12773
|
void 0,
|
|
12714
12774
|
options.callTimeout,
|
|
12715
12775
|
options.maxOutput,
|
|
12716
|
-
serveCwd
|
|
12776
|
+
serveCwd,
|
|
12777
|
+
void 0,
|
|
12778
|
+
cancel
|
|
12717
12779
|
);
|
|
12718
12780
|
}
|
|
12719
12781
|
if (tool.kind === "invoke") {
|
|
@@ -12728,7 +12790,9 @@ function createToolExecutor(platform2, clis, allTools, childEnv, options, serveC
|
|
|
12728
12790
|
parsed2.stdin,
|
|
12729
12791
|
options.callTimeout,
|
|
12730
12792
|
options.maxOutput,
|
|
12731
|
-
serveCwd
|
|
12793
|
+
serveCwd,
|
|
12794
|
+
void 0,
|
|
12795
|
+
cancel
|
|
12732
12796
|
);
|
|
12733
12797
|
}
|
|
12734
12798
|
const parsed = readToolArguments(args ?? {});
|
|
@@ -12744,18 +12808,20 @@ function createToolExecutor(platform2, clis, allTools, childEnv, options, serveC
|
|
|
12744
12808
|
planned.stdin,
|
|
12745
12809
|
options.callTimeout,
|
|
12746
12810
|
options.maxOutput,
|
|
12747
|
-
serveCwd
|
|
12811
|
+
serveCwd,
|
|
12812
|
+
void 0,
|
|
12813
|
+
cancel
|
|
12748
12814
|
);
|
|
12749
12815
|
}
|
|
12750
|
-
async function invoke(name, args) {
|
|
12816
|
+
async function invoke(name, args, cancel) {
|
|
12751
12817
|
const tool = allTools.find((t) => t.name === name);
|
|
12752
12818
|
if (!tool) return { stdout: "", stderr: `unknown tool ${name}`, exitCode: 1 };
|
|
12753
12819
|
if (tool.cli === "" && tool.commandPath.length === 0) {
|
|
12754
|
-
return invokeTemplateTool(tool, args);
|
|
12820
|
+
return invokeTemplateTool(tool, args, cancel);
|
|
12755
12821
|
}
|
|
12756
12822
|
const cli = clis.find((c) => c.name === tool.cli);
|
|
12757
12823
|
if (!cli) return { stdout: "", stderr: `unknown cli ${tool.cli}`, exitCode: 1 };
|
|
12758
|
-
return enqueue(cli.name, () => executeTool(tool, cli, args));
|
|
12824
|
+
return enqueue(cli.name, () => executeTool(tool, cli, args, cancel));
|
|
12759
12825
|
}
|
|
12760
12826
|
return { invoke, rpcTools };
|
|
12761
12827
|
}
|
|
@@ -13499,10 +13565,13 @@ async function runHttpServer(platform2, options, tools, call, serverName, versio
|
|
|
13499
13565
|
let tokenFilePath;
|
|
13500
13566
|
let shutdownRequested = false;
|
|
13501
13567
|
let inflightDone;
|
|
13568
|
+
const stopCalls = new AbortController();
|
|
13569
|
+
const activeCalls = /* @__PURE__ */ new Set();
|
|
13502
13570
|
const shutdown = () => {
|
|
13503
13571
|
if (shutdownRequested) return;
|
|
13504
13572
|
shutdownRequested = true;
|
|
13505
13573
|
listener.close();
|
|
13574
|
+
stopCalls.abort();
|
|
13506
13575
|
inflightDone = killInflight();
|
|
13507
13576
|
if (tokenFilePath) {
|
|
13508
13577
|
platform2.fs.unlink?.(tokenFilePath).catch(() => {
|
|
@@ -13532,17 +13601,30 @@ async function runHttpServer(platform2, options, tools, call, serverName, versio
|
|
|
13532
13601
|
await platform2.write(2, `mcpcat serve: listening on ${listenUrl}
|
|
13533
13602
|
`);
|
|
13534
13603
|
}
|
|
13535
|
-
const handler = (request) =>
|
|
13536
|
-
|
|
13537
|
-
|
|
13538
|
-
|
|
13539
|
-
|
|
13540
|
-
|
|
13541
|
-
|
|
13542
|
-
|
|
13543
|
-
|
|
13544
|
-
|
|
13545
|
-
|
|
13604
|
+
const handler = (request, signal) => {
|
|
13605
|
+
const cancel = new AbortController();
|
|
13606
|
+
const relay = () => cancel.abort();
|
|
13607
|
+
signal.addEventListener("abort", relay, { once: true });
|
|
13608
|
+
stopCalls.signal.addEventListener("abort", relay, { once: true });
|
|
13609
|
+
if (signal.aborted || stopCalls.signal.aborted) relay();
|
|
13610
|
+
const pending = handleMcpRequest(
|
|
13611
|
+
request,
|
|
13612
|
+
tools,
|
|
13613
|
+
(name, args) => call(name, args, cancel.signal),
|
|
13614
|
+
serverName,
|
|
13615
|
+
version,
|
|
13616
|
+
serverOrigins,
|
|
13617
|
+
allowedOrigins,
|
|
13618
|
+
allowedHosts,
|
|
13619
|
+
resolvedBearer
|
|
13620
|
+
).finally(() => {
|
|
13621
|
+
signal.removeEventListener("abort", relay);
|
|
13622
|
+
stopCalls.signal.removeEventListener("abort", relay);
|
|
13623
|
+
activeCalls.delete(pending);
|
|
13624
|
+
});
|
|
13625
|
+
activeCalls.add(pending);
|
|
13626
|
+
return pending;
|
|
13627
|
+
};
|
|
13546
13628
|
try {
|
|
13547
13629
|
for await (const connection of listener) {
|
|
13548
13630
|
if (shutdownRequested) break;
|
|
@@ -13564,6 +13646,7 @@ async function runHttpServer(platform2, options, tools, call, serverName, versio
|
|
|
13564
13646
|
} catch {
|
|
13565
13647
|
}
|
|
13566
13648
|
}
|
|
13649
|
+
await Promise.allSettled([...activeCalls]);
|
|
13567
13650
|
return 0;
|
|
13568
13651
|
}
|
|
13569
13652
|
async function handleMcpRequest(request, tools, call, serverName, version, serverOrigins, allowedOrigins, allowedHosts, resolvedBearer) {
|
|
@@ -16827,7 +16910,7 @@ async function main(ctx) {
|
|
|
16827
16910
|
packageDir: "/node_modules/@ai-ecoverse/mcpcat"
|
|
16828
16911
|
});
|
|
16829
16912
|
const args = ctx.argv.slice(1);
|
|
16830
|
-
return run(platform2, "0.
|
|
16913
|
+
return run(platform2, "0.19.0", args);
|
|
16831
16914
|
}
|
|
16832
16915
|
export {
|
|
16833
16916
|
main as default
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-ecoverse/mcpcat",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.19.0",
|
|
4
4
|
"description": "A curl for the Model Context Protocol",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"mcpcat": "dist/host/mcpcat.mjs"
|
|
17
17
|
},
|
|
18
18
|
"engines": {
|
|
19
|
-
"node": ">=
|
|
19
|
+
"node": ">=26",
|
|
20
20
|
"slicc-kernel": ">=1.65.2"
|
|
21
21
|
},
|
|
22
22
|
"publishConfig": {
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
},
|
|
26
26
|
"fledgling": {
|
|
27
27
|
"provider": "github",
|
|
28
|
-
"workflow": "
|
|
28
|
+
"workflow": "ci.yml",
|
|
29
29
|
"repo": "ai-ecoverse/mcpcat",
|
|
30
30
|
"publish": true
|
|
31
31
|
},
|
|
@@ -51,25 +51,20 @@
|
|
|
51
51
|
"build": "node scripts/build.mjs",
|
|
52
52
|
"lint": "sh scripts/lint.sh",
|
|
53
53
|
"typecheck": "tsc",
|
|
54
|
-
"test": "
|
|
54
|
+
"test": "node --test --test-timeout=30000 'test/integration/*.test.mjs'",
|
|
55
55
|
"test:unit": "mcr -c node_modules/@ai-ecoverse/slicc-shared-web/mcr.config.mjs node --test 'test/unit/**/*.test.mjs' 'test/unit/**/*.test.ts'",
|
|
56
|
-
"test:integration": "node --test --test-timeout=30000 test/integration/*.test.mjs",
|
|
57
|
-
"test:kernel-min": "node scripts/test-kernel-min.mjs",
|
|
58
|
-
"cert": "node scripts/cert.mjs",
|
|
59
56
|
"release": "semantic-release",
|
|
60
57
|
"trust": "npx fledgling sync -y --skip-publish",
|
|
61
58
|
"trust:dry": "npx fledgling sync --dry-run -y --skip-publish"
|
|
62
59
|
},
|
|
63
60
|
"devDependencies": {
|
|
64
|
-
"@ai-ecoverse/slicc-
|
|
65
|
-
"@ai-ecoverse/slicc-
|
|
66
|
-
"@ai-ecoverse/slicc-shared-web": "1.30.1",
|
|
61
|
+
"@ai-ecoverse/slicc-kernel": "1.146.2",
|
|
62
|
+
"@ai-ecoverse/slicc-shared-web": "2.8.5",
|
|
67
63
|
"@biomejs/biome": "2.5.15",
|
|
68
64
|
"@earendil-works/pi-codemode": "1.2.0",
|
|
69
65
|
"@earendil-works/pi-mcp": "1.2.0",
|
|
70
66
|
"@types/node": "26.6.5",
|
|
71
67
|
"esbuild": "0.28.2",
|
|
72
|
-
"kernel-cert-new": "npm:@ai-ecoverse/slicc-kernel@1.143.2",
|
|
73
68
|
"lefthook": "2.2.2",
|
|
74
69
|
"monocart-coverage-reports": "2.13.2",
|
|
75
70
|
"proper-lockfile": "4.1.2",
|