terminal-commands 0.1.3 → 0.2.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 +101 -4
- package/dist/agent.js +626 -124
- package/dist/cli.js +875 -140
- package/dist/index.js +1064 -357
- package/dist/native/GO-LICENSE +27 -0
- package/dist/native/darwin-arm64/fs-helper +0 -0
- package/dist/native/darwin-x64/fs-helper +0 -0
- package/dist/native/linux-arm64/fs-helper +0 -0
- package/dist/native/linux-x64/fs-helper +0 -0
- package/dist/native/win32-arm64/fs-helper.exe +0 -0
- package/dist/native/win32-x64/fs-helper.exe +0 -0
- package/dist/stdio.js +784 -179
- package/package.json +5 -4
package/dist/stdio.js
CHANGED
|
@@ -410,11 +410,11 @@ var require_codegen = __commonJS({
|
|
|
410
410
|
const rhs = this.rhs === void 0 ? "" : ` = ${this.rhs}`;
|
|
411
411
|
return `${varKind} ${this.name}${rhs};` + _n;
|
|
412
412
|
}
|
|
413
|
-
optimizeNames(names,
|
|
413
|
+
optimizeNames(names, constants) {
|
|
414
414
|
if (!names[this.name.str])
|
|
415
415
|
return;
|
|
416
416
|
if (this.rhs)
|
|
417
|
-
this.rhs = optimizeExpr(this.rhs, names,
|
|
417
|
+
this.rhs = optimizeExpr(this.rhs, names, constants);
|
|
418
418
|
return this;
|
|
419
419
|
}
|
|
420
420
|
get names() {
|
|
@@ -431,10 +431,10 @@ var require_codegen = __commonJS({
|
|
|
431
431
|
render({ _n }) {
|
|
432
432
|
return `${this.lhs} = ${this.rhs};` + _n;
|
|
433
433
|
}
|
|
434
|
-
optimizeNames(names,
|
|
434
|
+
optimizeNames(names, constants) {
|
|
435
435
|
if (this.lhs instanceof code_1.Name && !names[this.lhs.str] && !this.sideEffects)
|
|
436
436
|
return;
|
|
437
|
-
this.rhs = optimizeExpr(this.rhs, names,
|
|
437
|
+
this.rhs = optimizeExpr(this.rhs, names, constants);
|
|
438
438
|
return this;
|
|
439
439
|
}
|
|
440
440
|
get names() {
|
|
@@ -495,8 +495,8 @@ var require_codegen = __commonJS({
|
|
|
495
495
|
optimizeNodes() {
|
|
496
496
|
return `${this.code}` ? this : void 0;
|
|
497
497
|
}
|
|
498
|
-
optimizeNames(names,
|
|
499
|
-
this.code = optimizeExpr(this.code, names,
|
|
498
|
+
optimizeNames(names, constants) {
|
|
499
|
+
this.code = optimizeExpr(this.code, names, constants);
|
|
500
500
|
return this;
|
|
501
501
|
}
|
|
502
502
|
get names() {
|
|
@@ -525,12 +525,12 @@ var require_codegen = __commonJS({
|
|
|
525
525
|
}
|
|
526
526
|
return nodes.length > 0 ? this : void 0;
|
|
527
527
|
}
|
|
528
|
-
optimizeNames(names,
|
|
528
|
+
optimizeNames(names, constants) {
|
|
529
529
|
const { nodes } = this;
|
|
530
530
|
let i = nodes.length;
|
|
531
531
|
while (i--) {
|
|
532
532
|
const n = nodes[i];
|
|
533
|
-
if (n.optimizeNames(names,
|
|
533
|
+
if (n.optimizeNames(names, constants))
|
|
534
534
|
continue;
|
|
535
535
|
subtractNames(names, n.names);
|
|
536
536
|
nodes.splice(i, 1);
|
|
@@ -583,12 +583,12 @@ var require_codegen = __commonJS({
|
|
|
583
583
|
return void 0;
|
|
584
584
|
return this;
|
|
585
585
|
}
|
|
586
|
-
optimizeNames(names,
|
|
586
|
+
optimizeNames(names, constants) {
|
|
587
587
|
var _a3;
|
|
588
|
-
this.else = (_a3 = this.else) === null || _a3 === void 0 ? void 0 : _a3.optimizeNames(names,
|
|
589
|
-
if (!(super.optimizeNames(names,
|
|
588
|
+
this.else = (_a3 = this.else) === null || _a3 === void 0 ? void 0 : _a3.optimizeNames(names, constants);
|
|
589
|
+
if (!(super.optimizeNames(names, constants) || this.else))
|
|
590
590
|
return;
|
|
591
|
-
this.condition = optimizeExpr(this.condition, names,
|
|
591
|
+
this.condition = optimizeExpr(this.condition, names, constants);
|
|
592
592
|
return this;
|
|
593
593
|
}
|
|
594
594
|
get names() {
|
|
@@ -611,10 +611,10 @@ var require_codegen = __commonJS({
|
|
|
611
611
|
render(opts) {
|
|
612
612
|
return `for(${this.iteration})` + super.render(opts);
|
|
613
613
|
}
|
|
614
|
-
optimizeNames(names,
|
|
615
|
-
if (!super.optimizeNames(names,
|
|
614
|
+
optimizeNames(names, constants) {
|
|
615
|
+
if (!super.optimizeNames(names, constants))
|
|
616
616
|
return;
|
|
617
|
-
this.iteration = optimizeExpr(this.iteration, names,
|
|
617
|
+
this.iteration = optimizeExpr(this.iteration, names, constants);
|
|
618
618
|
return this;
|
|
619
619
|
}
|
|
620
620
|
get names() {
|
|
@@ -650,10 +650,10 @@ var require_codegen = __commonJS({
|
|
|
650
650
|
render(opts) {
|
|
651
651
|
return `for(${this.varKind} ${this.name} ${this.loop} ${this.iterable})` + super.render(opts);
|
|
652
652
|
}
|
|
653
|
-
optimizeNames(names,
|
|
654
|
-
if (!super.optimizeNames(names,
|
|
653
|
+
optimizeNames(names, constants) {
|
|
654
|
+
if (!super.optimizeNames(names, constants))
|
|
655
655
|
return;
|
|
656
|
-
this.iterable = optimizeExpr(this.iterable, names,
|
|
656
|
+
this.iterable = optimizeExpr(this.iterable, names, constants);
|
|
657
657
|
return this;
|
|
658
658
|
}
|
|
659
659
|
get names() {
|
|
@@ -695,11 +695,11 @@ var require_codegen = __commonJS({
|
|
|
695
695
|
(_b = this.finally) === null || _b === void 0 ? void 0 : _b.optimizeNodes();
|
|
696
696
|
return this;
|
|
697
697
|
}
|
|
698
|
-
optimizeNames(names,
|
|
698
|
+
optimizeNames(names, constants) {
|
|
699
699
|
var _a3, _b;
|
|
700
|
-
super.optimizeNames(names,
|
|
701
|
-
(_a3 = this.catch) === null || _a3 === void 0 ? void 0 : _a3.optimizeNames(names,
|
|
702
|
-
(_b = this.finally) === null || _b === void 0 ? void 0 : _b.optimizeNames(names,
|
|
700
|
+
super.optimizeNames(names, constants);
|
|
701
|
+
(_a3 = this.catch) === null || _a3 === void 0 ? void 0 : _a3.optimizeNames(names, constants);
|
|
702
|
+
(_b = this.finally) === null || _b === void 0 ? void 0 : _b.optimizeNames(names, constants);
|
|
703
703
|
return this;
|
|
704
704
|
}
|
|
705
705
|
get names() {
|
|
@@ -1000,7 +1000,7 @@ var require_codegen = __commonJS({
|
|
|
1000
1000
|
function addExprNames(names, from) {
|
|
1001
1001
|
return from instanceof code_1._CodeOrName ? addNames(names, from.names) : names;
|
|
1002
1002
|
}
|
|
1003
|
-
function optimizeExpr(expr, names,
|
|
1003
|
+
function optimizeExpr(expr, names, constants) {
|
|
1004
1004
|
if (expr instanceof code_1.Name)
|
|
1005
1005
|
return replaceName(expr);
|
|
1006
1006
|
if (!canOptimize(expr))
|
|
@@ -1015,14 +1015,14 @@ var require_codegen = __commonJS({
|
|
|
1015
1015
|
return items;
|
|
1016
1016
|
}, []));
|
|
1017
1017
|
function replaceName(n) {
|
|
1018
|
-
const c =
|
|
1018
|
+
const c = constants[n.str];
|
|
1019
1019
|
if (c === void 0 || names[n.str] !== 1)
|
|
1020
1020
|
return n;
|
|
1021
1021
|
delete names[n.str];
|
|
1022
1022
|
return c;
|
|
1023
1023
|
}
|
|
1024
1024
|
function canOptimize(e) {
|
|
1025
|
-
return e instanceof code_1._Code && e._items.some((c) => c instanceof code_1.Name && names[c.str] === 1 &&
|
|
1025
|
+
return e instanceof code_1._Code && e._items.some((c) => c instanceof code_1.Name && names[c.str] === 1 && constants[c.str] !== void 0);
|
|
1026
1026
|
}
|
|
1027
1027
|
}
|
|
1028
1028
|
function subtractNames(names, from) {
|
|
@@ -3262,8 +3262,8 @@ var require_utils = __commonJS({
|
|
|
3262
3262
|
}
|
|
3263
3263
|
return ind;
|
|
3264
3264
|
}
|
|
3265
|
-
function removeDotSegments(
|
|
3266
|
-
let input2 =
|
|
3265
|
+
function removeDotSegments(path4) {
|
|
3266
|
+
let input2 = path4;
|
|
3267
3267
|
const output2 = [];
|
|
3268
3268
|
let nextSlash = -1;
|
|
3269
3269
|
let len = 0;
|
|
@@ -3672,8 +3672,8 @@ var require_schemes = __commonJS({
|
|
|
3672
3672
|
}
|
|
3673
3673
|
if (wsComponent.resourceName) {
|
|
3674
3674
|
const queryIndex = wsComponent.resourceName.indexOf("?");
|
|
3675
|
-
const
|
|
3676
|
-
wsComponent.path =
|
|
3675
|
+
const path4 = queryIndex === -1 ? wsComponent.resourceName : wsComponent.resourceName.slice(0, queryIndex);
|
|
3676
|
+
wsComponent.path = path4 && path4 !== "/" ? path4 : void 0;
|
|
3677
3677
|
wsComponent.query = queryIndex === -1 ? void 0 : wsComponent.resourceName.slice(queryIndex + 1);
|
|
3678
3678
|
wsComponent.resourceName = void 0;
|
|
3679
3679
|
}
|
|
@@ -4148,13 +4148,14 @@ var require_fast_uri = __commonJS({
|
|
|
4148
4148
|
if (!malformedIPLiteral) {
|
|
4149
4149
|
malformedHost = canonicalizeHost(parsed, options, schemeHandler, isIP);
|
|
4150
4150
|
}
|
|
4151
|
-
if (
|
|
4152
|
-
|
|
4153
|
-
|
|
4154
|
-
|
|
4155
|
-
parsed.host = reescapeHostDelimiters(host, isIP);
|
|
4156
|
-
}
|
|
4151
|
+
if (uri.indexOf("%") !== -1 && parsed.host !== void 0 && !malformedIPLiteral) {
|
|
4152
|
+
let host = isIP ? parsed.host : normalizePercentEncoding(parsed.host, true);
|
|
4153
|
+
if (!isIP) {
|
|
4154
|
+
host = normalizePercentEncoding(host.toLowerCase());
|
|
4157
4155
|
}
|
|
4156
|
+
parsed.host = reescapeHostDelimiters(host, isIP);
|
|
4157
|
+
}
|
|
4158
|
+
if (!schemeHandler || schemeHandler && !schemeHandler.skipNormalize) {
|
|
4158
4159
|
if (parsed.path) {
|
|
4159
4160
|
parsed.path = normalizePathEncoding(parsed.path);
|
|
4160
4161
|
}
|
|
@@ -8017,10 +8018,10 @@ function mergeDefs(...defs) {
|
|
|
8017
8018
|
function cloneDef(schema) {
|
|
8018
8019
|
return mergeDefs(schema._zod.def);
|
|
8019
8020
|
}
|
|
8020
|
-
function getElementAtPath(obj,
|
|
8021
|
-
if (!
|
|
8021
|
+
function getElementAtPath(obj, path4) {
|
|
8022
|
+
if (!path4)
|
|
8022
8023
|
return obj;
|
|
8023
|
-
return
|
|
8024
|
+
return path4.reduce((acc, key) => acc?.[key], obj);
|
|
8024
8025
|
}
|
|
8025
8026
|
function promiseAllObject(promisesObj) {
|
|
8026
8027
|
const keys = Object.keys(promisesObj);
|
|
@@ -8360,11 +8361,11 @@ function explicitlyAborted(x, startIndex = 0) {
|
|
|
8360
8361
|
}
|
|
8361
8362
|
return false;
|
|
8362
8363
|
}
|
|
8363
|
-
function prefixIssues(
|
|
8364
|
+
function prefixIssues(path4, issues) {
|
|
8364
8365
|
return issues.map((iss) => {
|
|
8365
8366
|
var _a3;
|
|
8366
8367
|
(_a3 = iss).path ?? (_a3.path = []);
|
|
8367
|
-
iss.path.unshift(
|
|
8368
|
+
iss.path.unshift(path4);
|
|
8368
8369
|
return iss;
|
|
8369
8370
|
});
|
|
8370
8371
|
}
|
|
@@ -8814,16 +8815,16 @@ function flattenError(error62, mapper = (issue2) => issue2.message) {
|
|
|
8814
8815
|
}
|
|
8815
8816
|
function formatError(error62, mapper = (issue2) => issue2.message) {
|
|
8816
8817
|
const fieldErrors = { _errors: [] };
|
|
8817
|
-
const processError = (error63,
|
|
8818
|
+
const processError = (error63, path4 = []) => {
|
|
8818
8819
|
for (const issue2 of error63.issues) {
|
|
8819
8820
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
8820
|
-
issue2.errors.map((issues) => processError({ issues }, [...
|
|
8821
|
+
issue2.errors.map((issues) => processError({ issues }, [...path4, ...issue2.path]));
|
|
8821
8822
|
} else if (issue2.code === "invalid_key") {
|
|
8822
|
-
processError({ issues: issue2.issues }, [...
|
|
8823
|
+
processError({ issues: issue2.issues }, [...path4, ...issue2.path]);
|
|
8823
8824
|
} else if (issue2.code === "invalid_element") {
|
|
8824
|
-
processError({ issues: issue2.issues }, [...
|
|
8825
|
+
processError({ issues: issue2.issues }, [...path4, ...issue2.path]);
|
|
8825
8826
|
} else {
|
|
8826
|
-
const fullpath = [...
|
|
8827
|
+
const fullpath = [...path4, ...issue2.path];
|
|
8827
8828
|
if (fullpath.length === 0) {
|
|
8828
8829
|
fieldErrors._errors.push(mapper(issue2));
|
|
8829
8830
|
} else {
|
|
@@ -8862,17 +8863,17 @@ function formatError(error62, mapper = (issue2) => issue2.message) {
|
|
|
8862
8863
|
}
|
|
8863
8864
|
function treeifyError(error62, mapper = (issue2) => issue2.message) {
|
|
8864
8865
|
const result = { errors: [] };
|
|
8865
|
-
const processError = (error63,
|
|
8866
|
+
const processError = (error63, path4 = []) => {
|
|
8866
8867
|
var _a3;
|
|
8867
8868
|
for (const issue2 of error63.issues) {
|
|
8868
8869
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
8869
|
-
issue2.errors.map((issues) => processError({ issues }, [...
|
|
8870
|
+
issue2.errors.map((issues) => processError({ issues }, [...path4, ...issue2.path]));
|
|
8870
8871
|
} else if (issue2.code === "invalid_key") {
|
|
8871
|
-
processError({ issues: issue2.issues }, [...
|
|
8872
|
+
processError({ issues: issue2.issues }, [...path4, ...issue2.path]);
|
|
8872
8873
|
} else if (issue2.code === "invalid_element") {
|
|
8873
|
-
processError({ issues: issue2.issues }, [...
|
|
8874
|
+
processError({ issues: issue2.issues }, [...path4, ...issue2.path]);
|
|
8874
8875
|
} else {
|
|
8875
|
-
const fullpath = [...
|
|
8876
|
+
const fullpath = [...path4, ...issue2.path];
|
|
8876
8877
|
if (fullpath.length === 0) {
|
|
8877
8878
|
result.errors.push(mapper(issue2));
|
|
8878
8879
|
continue;
|
|
@@ -8911,8 +8912,8 @@ function treeifyError(error62, mapper = (issue2) => issue2.message) {
|
|
|
8911
8912
|
}
|
|
8912
8913
|
function toDotPath(_path) {
|
|
8913
8914
|
const segs = [];
|
|
8914
|
-
const
|
|
8915
|
-
for (const seg of
|
|
8915
|
+
const path4 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
|
|
8916
|
+
for (const seg of path4) {
|
|
8916
8917
|
if (typeof seg === "number")
|
|
8917
8918
|
segs.push(`[${seg}]`);
|
|
8918
8919
|
else if (typeof seg === "symbol")
|
|
@@ -26014,13 +26015,13 @@ function resolveRef(ref, ctx) {
|
|
|
26014
26015
|
if (!ref.startsWith("#")) {
|
|
26015
26016
|
throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
|
|
26016
26017
|
}
|
|
26017
|
-
const
|
|
26018
|
-
if (
|
|
26018
|
+
const path4 = ref.slice(1).split("/").filter(Boolean);
|
|
26019
|
+
if (path4.length === 0) {
|
|
26019
26020
|
return ctx.rootSchema;
|
|
26020
26021
|
}
|
|
26021
26022
|
const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
|
|
26022
|
-
if (
|
|
26023
|
-
const key =
|
|
26023
|
+
if (path4[0] === defsKey) {
|
|
26024
|
+
const key = path4[1] === void 0 ? void 0 : decodeJSONPointerSegment(path4[1]);
|
|
26024
26025
|
if (!key || !ctx.defs[key]) {
|
|
26025
26026
|
throw new Error(`Reference not found: ${ref}`);
|
|
26026
26027
|
}
|
|
@@ -28504,9 +28505,6 @@ var StdioServerTransport = class {
|
|
|
28504
28505
|
}
|
|
28505
28506
|
};
|
|
28506
28507
|
|
|
28507
|
-
// src/server.ts
|
|
28508
|
-
import { readdir, writeFile } from "node:fs/promises";
|
|
28509
|
-
|
|
28510
28508
|
// node_modules/zod/v3/helpers/util.js
|
|
28511
28509
|
var util;
|
|
28512
28510
|
(function(util2) {
|
|
@@ -28881,8 +28879,8 @@ function getErrorMap2() {
|
|
|
28881
28879
|
|
|
28882
28880
|
// node_modules/zod/v3/helpers/parseUtil.js
|
|
28883
28881
|
var makeIssue = (params) => {
|
|
28884
|
-
const { data, path:
|
|
28885
|
-
const fullPath = [...
|
|
28882
|
+
const { data, path: path4, errorMaps, issueData } = params;
|
|
28883
|
+
const fullPath = [...path4, ...issueData.path || []];
|
|
28886
28884
|
const fullIssue = {
|
|
28887
28885
|
...issueData,
|
|
28888
28886
|
path: fullPath
|
|
@@ -28997,11 +28995,11 @@ var errorUtil;
|
|
|
28997
28995
|
|
|
28998
28996
|
// node_modules/zod/v3/types.js
|
|
28999
28997
|
var ParseInputLazyPath = class {
|
|
29000
|
-
constructor(parent, value,
|
|
28998
|
+
constructor(parent, value, path4, key) {
|
|
29001
28999
|
this._cachedPath = [];
|
|
29002
29000
|
this.parent = parent;
|
|
29003
29001
|
this.data = value;
|
|
29004
|
-
this._path =
|
|
29002
|
+
this._path = path4;
|
|
29005
29003
|
this._key = key;
|
|
29006
29004
|
}
|
|
29007
29005
|
get path() {
|
|
@@ -32552,11 +32550,11 @@ function normalizeObjectSchema(schema) {
|
|
|
32552
32550
|
}
|
|
32553
32551
|
return void 0;
|
|
32554
32552
|
}
|
|
32555
|
-
function getDotPath(
|
|
32556
|
-
if (
|
|
32553
|
+
function getDotPath(path4) {
|
|
32554
|
+
if (path4.length === 0) {
|
|
32557
32555
|
return "object root";
|
|
32558
32556
|
}
|
|
32559
|
-
return
|
|
32557
|
+
return path4.reduce((acc, seg, index) => {
|
|
32560
32558
|
if (index === 0) {
|
|
32561
32559
|
return String(seg);
|
|
32562
32560
|
}
|
|
@@ -34000,6 +33998,9 @@ var Protocol = class {
|
|
|
34000
33998
|
this.setRequestHandler(GetTaskPayloadRequestSchema, async (request, extra) => {
|
|
34001
33999
|
const handleTaskResult = async () => {
|
|
34002
34000
|
const taskId = request.params.taskId;
|
|
34001
|
+
if (!await this._taskStore.getTask(taskId, extra.sessionId)) {
|
|
34002
|
+
throw new McpError(ErrorCode.InvalidParams, `Task not found: ${taskId}`);
|
|
34003
|
+
}
|
|
34003
34004
|
if (this._taskMessageQueue) {
|
|
34004
34005
|
let queuedMessage;
|
|
34005
34006
|
while (queuedMessage = await this._taskMessageQueue.dequeue(taskId, extra.sessionId)) {
|
|
@@ -34030,12 +34031,12 @@ var Protocol = class {
|
|
|
34030
34031
|
throw new McpError(ErrorCode.InvalidParams, `Task not found: ${taskId}`);
|
|
34031
34032
|
}
|
|
34032
34033
|
if (!isTerminal(task.status)) {
|
|
34033
|
-
await this._waitForTaskUpdate(taskId, extra.signal);
|
|
34034
|
+
await this._waitForTaskUpdate(taskId, extra.signal, extra.sessionId);
|
|
34034
34035
|
return await handleTaskResult();
|
|
34035
34036
|
}
|
|
34036
34037
|
if (isTerminal(task.status)) {
|
|
34037
34038
|
const result = await this._taskStore.getTaskResult(taskId, extra.sessionId);
|
|
34038
|
-
this._clearTaskQueue(taskId);
|
|
34039
|
+
this._clearTaskQueue(taskId, extra.sessionId);
|
|
34039
34040
|
return {
|
|
34040
34041
|
...result,
|
|
34041
34042
|
_meta: {
|
|
@@ -34072,7 +34073,7 @@ var Protocol = class {
|
|
|
34072
34073
|
throw new McpError(ErrorCode.InvalidParams, `Cannot cancel task in terminal status: ${task.status}`);
|
|
34073
34074
|
}
|
|
34074
34075
|
await this._taskStore.updateTaskStatus(request.params.taskId, "cancelled", "Client cancelled task execution.", extra.sessionId);
|
|
34075
|
-
this._clearTaskQueue(request.params.taskId);
|
|
34076
|
+
this._clearTaskQueue(request.params.taskId, extra.sessionId);
|
|
34076
34077
|
const cancelledTask = await this._taskStore.getTask(request.params.taskId, extra.sessionId);
|
|
34077
34078
|
if (!cancelledTask) {
|
|
34078
34079
|
throw new McpError(ErrorCode.InvalidParams, `Task not found after cancellation: ${request.params.taskId}`);
|
|
@@ -34200,6 +34201,19 @@ var Protocol = class {
|
|
|
34200
34201
|
const handler = this._requestHandlers.get(request.method) ?? this.fallbackRequestHandler;
|
|
34201
34202
|
const capturedTransport = this._transport;
|
|
34202
34203
|
const relatedTaskId = request.params?._meta?.[RELATED_TASK_META_KEY]?.taskId;
|
|
34204
|
+
const sessionId = capturedTransport?.sessionId;
|
|
34205
|
+
const store = this._taskStore;
|
|
34206
|
+
let relatedTaskFound = true;
|
|
34207
|
+
let relatedTaskLookup;
|
|
34208
|
+
if (relatedTaskId && store && this._taskMessageQueue && sessionId !== void 0) {
|
|
34209
|
+
relatedTaskFound = false;
|
|
34210
|
+
relatedTaskLookup = (async () => {
|
|
34211
|
+
if (!await store.getTask(relatedTaskId, sessionId)) {
|
|
34212
|
+
throw new McpError(ErrorCode.InvalidParams, `Task not found: ${relatedTaskId}`);
|
|
34213
|
+
}
|
|
34214
|
+
relatedTaskFound = true;
|
|
34215
|
+
})();
|
|
34216
|
+
}
|
|
34203
34217
|
if (handler === void 0) {
|
|
34204
34218
|
const errorResponse = {
|
|
34205
34219
|
jsonrpc: "2.0",
|
|
@@ -34209,7 +34223,10 @@ var Protocol = class {
|
|
|
34209
34223
|
message: "Method not found"
|
|
34210
34224
|
}
|
|
34211
34225
|
};
|
|
34212
|
-
if (relatedTaskId &&
|
|
34226
|
+
if (relatedTaskId && relatedTaskLookup) {
|
|
34227
|
+
const queuedError = { type: "error", message: errorResponse, timestamp: Date.now() };
|
|
34228
|
+
relatedTaskLookup.then(() => this._enqueueTaskMessage(relatedTaskId, queuedError, sessionId), () => capturedTransport?.send(errorResponse)).catch((error62) => this._onerror(new Error(`Failed to send an error response: ${error62}`)));
|
|
34229
|
+
} else if (relatedTaskId && this._taskMessageQueue) {
|
|
34213
34230
|
this._enqueueTaskMessage(relatedTaskId, {
|
|
34214
34231
|
type: "error",
|
|
34215
34232
|
message: errorResponse,
|
|
@@ -34260,7 +34277,10 @@ var Protocol = class {
|
|
|
34260
34277
|
closeSSEStream: extra?.closeSSEStream,
|
|
34261
34278
|
closeStandaloneSSEStream: extra?.closeStandaloneSSEStream
|
|
34262
34279
|
};
|
|
34263
|
-
Promise.resolve().then(() => {
|
|
34280
|
+
(relatedTaskLookup ?? Promise.resolve()).then(() => {
|
|
34281
|
+
if (relatedTaskLookup && abortController.signal.aborted) {
|
|
34282
|
+
throw new McpError(ErrorCode.ConnectionClosed, "Request was cancelled");
|
|
34283
|
+
}
|
|
34264
34284
|
if (taskCreationParams) {
|
|
34265
34285
|
this.assertTaskHandlerCapability(request.method);
|
|
34266
34286
|
}
|
|
@@ -34295,7 +34315,7 @@ var Protocol = class {
|
|
|
34295
34315
|
...error62["data"] !== void 0 && { data: error62["data"] }
|
|
34296
34316
|
}
|
|
34297
34317
|
};
|
|
34298
|
-
if (relatedTaskId && this._taskMessageQueue) {
|
|
34318
|
+
if (relatedTaskId && this._taskMessageQueue && relatedTaskFound) {
|
|
34299
34319
|
await this._enqueueTaskMessage(relatedTaskId, {
|
|
34300
34320
|
type: "error",
|
|
34301
34321
|
message: errorResponse,
|
|
@@ -34775,7 +34795,7 @@ var Protocol = class {
|
|
|
34775
34795
|
throw new Error("Cannot enqueue task message: taskStore and taskMessageQueue are not configured");
|
|
34776
34796
|
}
|
|
34777
34797
|
const maxQueueSize = this._options?.maxTaskQueueSize;
|
|
34778
|
-
await this._taskMessageQueue.enqueue(taskId, message, sessionId, maxQueueSize);
|
|
34798
|
+
await this._taskMessageQueue.enqueue(taskId, message, sessionId ?? this._transport?.sessionId, maxQueueSize);
|
|
34779
34799
|
}
|
|
34780
34800
|
/**
|
|
34781
34801
|
* Clears the message queue for a task and rejects any pending request resolvers.
|
|
@@ -34804,12 +34824,13 @@ var Protocol = class {
|
|
|
34804
34824
|
* Uses polling to check for updates at the task's configured poll interval.
|
|
34805
34825
|
* @param taskId The task ID to wait for
|
|
34806
34826
|
* @param signal Abort signal to cancel the wait
|
|
34827
|
+
* @param sessionId Session of the request that waits, passed to the task store
|
|
34807
34828
|
* @returns Promise that resolves when an update occurs or rejects if aborted
|
|
34808
34829
|
*/
|
|
34809
|
-
async _waitForTaskUpdate(taskId, signal) {
|
|
34830
|
+
async _waitForTaskUpdate(taskId, signal, sessionId) {
|
|
34810
34831
|
let interval = this._options?.defaultTaskPollInterval ?? 1e3;
|
|
34811
34832
|
try {
|
|
34812
|
-
const task = await this._taskStore?.getTask(taskId);
|
|
34833
|
+
const task = await this._taskStore?.getTask(taskId, sessionId);
|
|
34813
34834
|
if (task?.pollInterval) {
|
|
34814
34835
|
interval = task.pollInterval;
|
|
34815
34836
|
}
|
|
@@ -35688,6 +35709,42 @@ var ExperimentalMcpServerTasks = class {
|
|
|
35688
35709
|
};
|
|
35689
35710
|
|
|
35690
35711
|
// node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js
|
|
35712
|
+
function toolInputElementCount(value, max) {
|
|
35713
|
+
let count = 0;
|
|
35714
|
+
const stack = [value];
|
|
35715
|
+
while (stack.length > 0) {
|
|
35716
|
+
const node2 = stack.pop();
|
|
35717
|
+
if (node2 === null || typeof node2 !== "object")
|
|
35718
|
+
continue;
|
|
35719
|
+
if (Array.isArray(node2)) {
|
|
35720
|
+
for (const child of node2) {
|
|
35721
|
+
if (++count > max)
|
|
35722
|
+
return count;
|
|
35723
|
+
if (child !== null && typeof child === "object")
|
|
35724
|
+
stack.push(child);
|
|
35725
|
+
}
|
|
35726
|
+
} else {
|
|
35727
|
+
for (const key in node2) {
|
|
35728
|
+
if (!Object.prototype.hasOwnProperty.call(node2, key))
|
|
35729
|
+
continue;
|
|
35730
|
+
if (++count > max)
|
|
35731
|
+
return count;
|
|
35732
|
+
const child = node2[key];
|
|
35733
|
+
if (child !== null && typeof child === "object")
|
|
35734
|
+
stack.push(child);
|
|
35735
|
+
}
|
|
35736
|
+
}
|
|
35737
|
+
}
|
|
35738
|
+
return count;
|
|
35739
|
+
}
|
|
35740
|
+
function resolveMaxToolInputElements(value) {
|
|
35741
|
+
if (value === void 0 || value === Infinity)
|
|
35742
|
+
return void 0;
|
|
35743
|
+
if (typeof value !== "number" || Number.isNaN(value) || value < 1) {
|
|
35744
|
+
throw new RangeError(`maxToolInputElements must be a number of at least 1, or Infinity, got ${String(value)}`);
|
|
35745
|
+
}
|
|
35746
|
+
return value;
|
|
35747
|
+
}
|
|
35691
35748
|
var McpServer = class {
|
|
35692
35749
|
constructor(serverInfo, options) {
|
|
35693
35750
|
this._registeredResources = {};
|
|
@@ -35699,6 +35756,7 @@ var McpServer = class {
|
|
|
35699
35756
|
this._resourceHandlersInitialized = false;
|
|
35700
35757
|
this._promptHandlersInitialized = false;
|
|
35701
35758
|
this.server = new Server(serverInfo, options);
|
|
35759
|
+
this._maxToolInputElements = resolveMaxToolInputElements(options?.maxToolInputElements);
|
|
35702
35760
|
}
|
|
35703
35761
|
/**
|
|
35704
35762
|
* Access experimental features.
|
|
@@ -35829,12 +35887,15 @@ var McpServer = class {
|
|
|
35829
35887
|
* Validates tool input arguments against the tool's input schema.
|
|
35830
35888
|
*/
|
|
35831
35889
|
async validateToolInput(tool, args, toolName) {
|
|
35890
|
+
if (this._maxToolInputElements !== void 0 && toolInputElementCount(args, this._maxToolInputElements) > this._maxToolInputElements) {
|
|
35891
|
+
throw new McpError(ErrorCode.InvalidParams, `Invalid arguments for tool ${toolName}: arguments contain more than the maximum of ${this._maxToolInputElements} elements`);
|
|
35892
|
+
}
|
|
35832
35893
|
if (!tool.inputSchema) {
|
|
35833
35894
|
return void 0;
|
|
35834
35895
|
}
|
|
35835
35896
|
const inputObj = normalizeObjectSchema(tool.inputSchema);
|
|
35836
35897
|
const schemaToParse = inputObj ?? tool.inputSchema;
|
|
35837
|
-
const parseResult = await safeParseAsync3(schemaToParse, args);
|
|
35898
|
+
const parseResult = await safeParseAsync3(schemaToParse, args ?? {});
|
|
35838
35899
|
if (!parseResult.success) {
|
|
35839
35900
|
const error62 = "error" in parseResult ? parseResult.error : "Unknown error";
|
|
35840
35901
|
const errorMessage = getParseErrorMessage(error62);
|
|
@@ -36072,7 +36133,7 @@ var McpServer = class {
|
|
|
36072
36133
|
}
|
|
36073
36134
|
if (prompt.argsSchema) {
|
|
36074
36135
|
const argsObj = normalizeObjectSchema(prompt.argsSchema);
|
|
36075
|
-
const parseResult = await safeParseAsync3(argsObj, request.params.arguments);
|
|
36136
|
+
const parseResult = await safeParseAsync3(argsObj, request.params.arguments ?? {});
|
|
36076
36137
|
if (!parseResult.success) {
|
|
36077
36138
|
const error62 = "error" in parseResult ? parseResult.error : "Unknown error";
|
|
36078
36139
|
const errorMessage = getParseErrorMessage(error62);
|
|
@@ -36479,35 +36540,28 @@ var EMPTY_COMPLETION_RESULT = {
|
|
|
36479
36540
|
}
|
|
36480
36541
|
};
|
|
36481
36542
|
|
|
36482
|
-
// src/file-reader.ts
|
|
36483
|
-
import { open as open2 } from "node:fs/promises";
|
|
36484
|
-
async function readBoundedTextFile(filename, maxCharacters) {
|
|
36485
|
-
const handle = await open2(filename, "r");
|
|
36486
|
-
try {
|
|
36487
|
-
const stats = await handle.stat();
|
|
36488
|
-
const buffer = Buffer.allocUnsafe(maxCharacters * 4 + 4);
|
|
36489
|
-
const { bytesRead } = await handle.read(buffer, 0, buffer.length, 0);
|
|
36490
|
-
const hasUnreadBytes = bytesRead < stats.size;
|
|
36491
|
-
const decoded = new TextDecoder().decode(buffer.subarray(0, bytesRead), {
|
|
36492
|
-
stream: hasUnreadBytes
|
|
36493
|
-
});
|
|
36494
|
-
return {
|
|
36495
|
-
contents: decoded.slice(0, maxCharacters),
|
|
36496
|
-
truncated: hasUnreadBytes || decoded.length > maxCharacters
|
|
36497
|
-
};
|
|
36498
|
-
} finally {
|
|
36499
|
-
await handle.close();
|
|
36500
|
-
}
|
|
36501
|
-
}
|
|
36502
|
-
|
|
36503
36543
|
// src/process-manager.ts
|
|
36504
36544
|
import { randomUUID } from "node:crypto";
|
|
36545
|
+
import {
|
|
36546
|
+
closeSync,
|
|
36547
|
+
fsyncSync,
|
|
36548
|
+
mkdirSync,
|
|
36549
|
+
openSync,
|
|
36550
|
+
readFileSync,
|
|
36551
|
+
renameSync,
|
|
36552
|
+
rmSync,
|
|
36553
|
+
writeFileSync
|
|
36554
|
+
} from "node:fs";
|
|
36555
|
+
import path3 from "node:path";
|
|
36556
|
+
import { spawn as spawn2 } from "node:child_process";
|
|
36557
|
+
|
|
36558
|
+
// src/rooted-filesystem.ts
|
|
36505
36559
|
import { spawn } from "node:child_process";
|
|
36560
|
+
import { realpathSync, statSync } from "node:fs";
|
|
36561
|
+
import path2 from "node:path";
|
|
36562
|
+
import { fileURLToPath } from "node:url";
|
|
36506
36563
|
|
|
36507
36564
|
// src/security.ts
|
|
36508
|
-
import { constants } from "node:fs";
|
|
36509
|
-
import { access, lstat, realpath } from "node:fs/promises";
|
|
36510
|
-
import os from "node:os";
|
|
36511
36565
|
import path from "node:path";
|
|
36512
36566
|
var DEFAULT_ENVIRONMENT_KEYS = [
|
|
36513
36567
|
"LANG",
|
|
@@ -36540,34 +36594,7 @@ function configuredRoots(raw = process.env.MACHINE_TERMINAL_ROOTS) {
|
|
|
36540
36594
|
}
|
|
36541
36595
|
function isWithinRoot(candidate, root) {
|
|
36542
36596
|
const relative = path.relative(root, candidate);
|
|
36543
|
-
return relative === "" || !relative.startsWith(`..${path.sep}`) && relative !== "..";
|
|
36544
|
-
}
|
|
36545
|
-
async function resolveReadablePath(requestedPath, roots2, cwd = roots2[0] ?? os.homedir()) {
|
|
36546
|
-
const resolvedRoots = await Promise.all(roots2.map((root) => realpath(root)));
|
|
36547
|
-
const candidate = await realpath(path.resolve(cwd, requestedPath));
|
|
36548
|
-
if (!resolvedRoots.some((root) => isWithinRoot(candidate, root))) {
|
|
36549
|
-
throw new Error("Path is outside MACHINE_TERMINAL_ROOTS.");
|
|
36550
|
-
}
|
|
36551
|
-
await access(candidate, constants.R_OK);
|
|
36552
|
-
return candidate;
|
|
36553
|
-
}
|
|
36554
|
-
async function resolveWritablePath(requestedPath, roots2, cwd = roots2[0] ?? os.homedir()) {
|
|
36555
|
-
const absolute = path.resolve(cwd, requestedPath);
|
|
36556
|
-
const resolvedParent = await realpath(path.dirname(absolute));
|
|
36557
|
-
const resolvedRoots = await Promise.all(roots2.map((root) => realpath(root)));
|
|
36558
|
-
const candidate = path.join(resolvedParent, path.basename(absolute));
|
|
36559
|
-
if (!resolvedRoots.some((root) => isWithinRoot(candidate, root))) {
|
|
36560
|
-
throw new Error("Path is outside MACHINE_TERMINAL_ROOTS.");
|
|
36561
|
-
}
|
|
36562
|
-
try {
|
|
36563
|
-
const stats = await lstat(candidate);
|
|
36564
|
-
if (stats.isSymbolicLink()) {
|
|
36565
|
-
throw new Error("Writing through symbolic links is not allowed.");
|
|
36566
|
-
}
|
|
36567
|
-
} catch (error62) {
|
|
36568
|
-
if (error62.code !== "ENOENT") throw error62;
|
|
36569
|
-
}
|
|
36570
|
-
return candidate;
|
|
36597
|
+
return relative === "" || !path.isAbsolute(relative) && !relative.startsWith(`..${path.sep}`) && relative !== "..";
|
|
36571
36598
|
}
|
|
36572
36599
|
function safeEnvironment(source = process.env) {
|
|
36573
36600
|
return Object.fromEntries(
|
|
@@ -36592,7 +36619,200 @@ function assertProgramAllowed(program) {
|
|
|
36592
36619
|
}
|
|
36593
36620
|
}
|
|
36594
36621
|
|
|
36622
|
+
// src/rooted-filesystem.ts
|
|
36623
|
+
var filesystemHelperPath = fileURLToPath(
|
|
36624
|
+
new URL(
|
|
36625
|
+
`../dist/native/${process.platform}-${process.arch}/fs-helper${process.platform === "win32" ? ".exe" : ""}`,
|
|
36626
|
+
import.meta.url
|
|
36627
|
+
)
|
|
36628
|
+
);
|
|
36629
|
+
var RootedFilesystem = class {
|
|
36630
|
+
roots;
|
|
36631
|
+
#specs;
|
|
36632
|
+
#aliases;
|
|
36633
|
+
#child;
|
|
36634
|
+
#pending = /* @__PURE__ */ new Map();
|
|
36635
|
+
#nextID = 0;
|
|
36636
|
+
#buffer = "";
|
|
36637
|
+
#failure;
|
|
36638
|
+
#initialized = false;
|
|
36639
|
+
ready;
|
|
36640
|
+
constructor(roots2) {
|
|
36641
|
+
if (!roots2.length || roots2.length > 128)
|
|
36642
|
+
throw new Error("ROOTED_FILESYSTEM_ROOT_LIMIT");
|
|
36643
|
+
this.#aliases = roots2.map((root) => path2.resolve(root));
|
|
36644
|
+
this.#specs = roots2.map((root) => {
|
|
36645
|
+
const name = realpathSync(root);
|
|
36646
|
+
const info = statSync(name, { bigint: true });
|
|
36647
|
+
if (!info.isDirectory())
|
|
36648
|
+
throw new Error("ROOTED_FILESYSTEM_EXPECTED_DIRECTORY");
|
|
36649
|
+
return { path: name, dev: info.dev.toString(), ino: info.ino.toString() };
|
|
36650
|
+
});
|
|
36651
|
+
this.roots = this.#specs.map((root) => root.path);
|
|
36652
|
+
this.#child = spawn(filesystemHelperPath, [], {
|
|
36653
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
36654
|
+
windowsHide: true,
|
|
36655
|
+
env: safeEnvironment()
|
|
36656
|
+
});
|
|
36657
|
+
this.#child.stderr.resume();
|
|
36658
|
+
this.#child.on(
|
|
36659
|
+
"error",
|
|
36660
|
+
() => this.#fail(new Error("ROOTED_FILESYSTEM_HELPER_UNAVAILABLE"))
|
|
36661
|
+
);
|
|
36662
|
+
this.#child.on(
|
|
36663
|
+
"close",
|
|
36664
|
+
() => this.#fail(new Error("ROOTED_FILESYSTEM_HELPER_CLOSED"))
|
|
36665
|
+
);
|
|
36666
|
+
this.#child.stdin.on(
|
|
36667
|
+
"error",
|
|
36668
|
+
() => this.#fail(new Error("ROOTED_FILESYSTEM_HELPER_CLOSED"))
|
|
36669
|
+
);
|
|
36670
|
+
this.#child.stdout.on(
|
|
36671
|
+
"error",
|
|
36672
|
+
() => this.#fail(new Error("ROOTED_FILESYSTEM_HELPER_CLOSED"))
|
|
36673
|
+
);
|
|
36674
|
+
this.#child.stderr.on(
|
|
36675
|
+
"error",
|
|
36676
|
+
() => this.#fail(new Error("ROOTED_FILESYSTEM_HELPER_CLOSED"))
|
|
36677
|
+
);
|
|
36678
|
+
this.#child.stdout.setEncoding("utf8");
|
|
36679
|
+
this.#child.stdout.on("data", (data) => {
|
|
36680
|
+
this.#buffer += data;
|
|
36681
|
+
if (Buffer.byteLength(this.#buffer) > 4 * 1024 * 1024) {
|
|
36682
|
+
this.#fail(new Error("ROOTED_FILESYSTEM_REPLY_LIMIT"));
|
|
36683
|
+
return;
|
|
36684
|
+
}
|
|
36685
|
+
let newline;
|
|
36686
|
+
while ((newline = this.#buffer.indexOf("\n")) !== -1) {
|
|
36687
|
+
const line = this.#buffer.slice(0, newline);
|
|
36688
|
+
this.#buffer = this.#buffer.slice(newline + 1);
|
|
36689
|
+
try {
|
|
36690
|
+
const reply = JSON.parse(line);
|
|
36691
|
+
const waiting = this.#pending.get(reply.id);
|
|
36692
|
+
if (!waiting) continue;
|
|
36693
|
+
this.#pending.delete(reply.id);
|
|
36694
|
+
clearTimeout(waiting.timer);
|
|
36695
|
+
if (reply.error) waiting.reject(new Error(reply.error));
|
|
36696
|
+
else waiting.resolve(reply);
|
|
36697
|
+
} catch {
|
|
36698
|
+
this.#fail(new Error("ROOTED_FILESYSTEM_INVALID_REPLY"));
|
|
36699
|
+
return;
|
|
36700
|
+
}
|
|
36701
|
+
}
|
|
36702
|
+
this.#referenceWork();
|
|
36703
|
+
});
|
|
36704
|
+
this.ready = this.#request("init", { roots: this.#specs }).then(() => {
|
|
36705
|
+
this.#initialized = true;
|
|
36706
|
+
});
|
|
36707
|
+
void this.ready.catch(() => void 0);
|
|
36708
|
+
}
|
|
36709
|
+
#referenceWork() {
|
|
36710
|
+
const method = this.#pending.size ? "ref" : "unref";
|
|
36711
|
+
this.#child[method]();
|
|
36712
|
+
for (const stream of [
|
|
36713
|
+
this.#child.stdin,
|
|
36714
|
+
this.#child.stdout,
|
|
36715
|
+
this.#child.stderr
|
|
36716
|
+
])
|
|
36717
|
+
stream[method]();
|
|
36718
|
+
}
|
|
36719
|
+
#fail(error62) {
|
|
36720
|
+
this.#failure ??= error62;
|
|
36721
|
+
for (const waiting of this.#pending.values()) {
|
|
36722
|
+
clearTimeout(waiting.timer);
|
|
36723
|
+
waiting.reject(this.#failure);
|
|
36724
|
+
}
|
|
36725
|
+
this.#pending.clear();
|
|
36726
|
+
this.#child.kill();
|
|
36727
|
+
this.#referenceWork();
|
|
36728
|
+
}
|
|
36729
|
+
#request(operation, values) {
|
|
36730
|
+
if (this.#failure) return Promise.reject(this.#failure);
|
|
36731
|
+
if (this.#pending.size >= 16)
|
|
36732
|
+
return Promise.reject(new Error("ROOTED_FILESYSTEM_PENDING_LIMIT"));
|
|
36733
|
+
const id = ++this.#nextID;
|
|
36734
|
+
const message = JSON.stringify({ id, operation, ...values }) + "\n";
|
|
36735
|
+
if (Buffer.byteLength(message) >= 4 * 1024 * 1024)
|
|
36736
|
+
return Promise.reject(new Error("ROOTED_FILESYSTEM_INPUT_LIMIT"));
|
|
36737
|
+
return new Promise((resolve, reject) => {
|
|
36738
|
+
const timer = setTimeout(
|
|
36739
|
+
() => this.#fail(new Error("ROOTED_FILESYSTEM_TIMEOUT")),
|
|
36740
|
+
1e4
|
|
36741
|
+
);
|
|
36742
|
+
this.#pending.set(id, { resolve, reject, timer });
|
|
36743
|
+
this.#referenceWork();
|
|
36744
|
+
this.#child.stdin.write(message);
|
|
36745
|
+
});
|
|
36746
|
+
}
|
|
36747
|
+
resolve(requestedPath) {
|
|
36748
|
+
const candidate = path2.resolve(this.#aliases[0], requestedPath);
|
|
36749
|
+
const index = this.roots.findIndex(
|
|
36750
|
+
(root, i) => isWithinRoot(candidate, root) || isWithinRoot(candidate, this.#aliases[i])
|
|
36751
|
+
);
|
|
36752
|
+
if (index === -1)
|
|
36753
|
+
throw new Error("Path is outside MACHINE_TERMINAL_ROOTS.");
|
|
36754
|
+
const base = isWithinRoot(candidate, this.roots[index]) ? this.roots[index] : this.#aliases[index];
|
|
36755
|
+
const relative = path2.relative(base, candidate) || ".";
|
|
36756
|
+
if (relative.length > 4096) throw new Error("ROOTED_FILESYSTEM_PATH_LIMIT");
|
|
36757
|
+
return {
|
|
36758
|
+
root: index,
|
|
36759
|
+
path: relative,
|
|
36760
|
+
displayPath: path2.join(this.roots[index], relative)
|
|
36761
|
+
};
|
|
36762
|
+
}
|
|
36763
|
+
async read(target, maxCharacters) {
|
|
36764
|
+
await this.ready;
|
|
36765
|
+
const reply = await this.#request("read", { ...target, maxCharacters });
|
|
36766
|
+
const decoded = new TextDecoder().decode(
|
|
36767
|
+
Buffer.from(reply.bytes ?? "", "base64"),
|
|
36768
|
+
{ stream: reply.hasUnread }
|
|
36769
|
+
);
|
|
36770
|
+
return {
|
|
36771
|
+
contents: decoded.slice(0, maxCharacters),
|
|
36772
|
+
truncated: reply.hasUnread || decoded.length > maxCharacters
|
|
36773
|
+
};
|
|
36774
|
+
}
|
|
36775
|
+
async write(target, contents, overwrite) {
|
|
36776
|
+
await this.ready;
|
|
36777
|
+
return (await this.#request("write", { ...target, contents, overwrite })).bytesWritten;
|
|
36778
|
+
}
|
|
36779
|
+
async list(target) {
|
|
36780
|
+
await this.ready;
|
|
36781
|
+
const reply = await this.#request("list", { ...target });
|
|
36782
|
+
return { entries: reply.entries ?? [], truncated: reply.truncated };
|
|
36783
|
+
}
|
|
36784
|
+
async execution(requestedPath, program, args) {
|
|
36785
|
+
await this.ready;
|
|
36786
|
+
const target = this.resolve(requestedPath);
|
|
36787
|
+
return {
|
|
36788
|
+
roots: [this.#specs[target.root]],
|
|
36789
|
+
path: target.path,
|
|
36790
|
+
program,
|
|
36791
|
+
args
|
|
36792
|
+
};
|
|
36793
|
+
}
|
|
36794
|
+
async close() {
|
|
36795
|
+
if (this.#child.exitCode !== null || this.#child.signalCode !== null)
|
|
36796
|
+
return;
|
|
36797
|
+
const exited = new Promise(
|
|
36798
|
+
(resolve) => this.#child.once("close", () => resolve())
|
|
36799
|
+
);
|
|
36800
|
+
this.#fail(new Error("ROOTED_FILESYSTEM_CLOSED"));
|
|
36801
|
+
await exited;
|
|
36802
|
+
}
|
|
36803
|
+
get available() {
|
|
36804
|
+
return this.#initialized && !this.#failure;
|
|
36805
|
+
}
|
|
36806
|
+
};
|
|
36807
|
+
|
|
36595
36808
|
// src/process-manager.ts
|
|
36809
|
+
var MAX_EVENT_CHARACTERS = 4096;
|
|
36810
|
+
var MAX_RETAINED_EVENTS = 128;
|
|
36811
|
+
var MAX_RETAINED_EVENT_CHARACTERS = 64e3;
|
|
36812
|
+
var LOCAL_SESSION_RETENTION_MS = 60 * 6e4;
|
|
36813
|
+
var MAX_TASK_TOMBSTONES = 2e4;
|
|
36814
|
+
var TASK_RECEIPT_RETENTION_MS = 30 * 24 * 60 * 6e4;
|
|
36815
|
+
var TERMINATION_GRACE_MS = 1500;
|
|
36596
36816
|
function appendBounded(current, value, limit) {
|
|
36597
36817
|
if (current.length >= limit) return current;
|
|
36598
36818
|
return `${current}${value.toString("utf8")}`.slice(0, limit);
|
|
@@ -36600,6 +36820,7 @@ function appendBounded(current, value, limit) {
|
|
|
36600
36820
|
function snapshot(session) {
|
|
36601
36821
|
return {
|
|
36602
36822
|
sessionId: session.sessionId,
|
|
36823
|
+
...session.taskId ? { taskId: session.taskId } : {},
|
|
36603
36824
|
program: session.program,
|
|
36604
36825
|
args: session.args,
|
|
36605
36826
|
cwd: session.cwd,
|
|
@@ -36613,17 +36834,312 @@ function snapshot(session) {
|
|
|
36613
36834
|
};
|
|
36614
36835
|
}
|
|
36615
36836
|
var ProcessManager = class {
|
|
36837
|
+
constructor(limits = {
|
|
36838
|
+
maxRunning: 32,
|
|
36839
|
+
maxRetained: 512
|
|
36840
|
+
}, receiptFile = process.env.MACHINE_TERMINAL_TASK_RECEIPTS_FILE) {
|
|
36841
|
+
this.limits = limits;
|
|
36842
|
+
this.receiptFile = receiptFile;
|
|
36843
|
+
if (!Number.isInteger(limits.maxRunning) || limits.maxRunning < 1 || !Number.isInteger(limits.maxRetained) || limits.maxRetained < limits.maxRunning) {
|
|
36844
|
+
throw new Error("INVALID_PROCESS_LIMITS: Invalid local process limits.");
|
|
36845
|
+
}
|
|
36846
|
+
this.#loadReceipts();
|
|
36847
|
+
}
|
|
36848
|
+
limits;
|
|
36849
|
+
receiptFile;
|
|
36616
36850
|
#sessions = /* @__PURE__ */ new Map();
|
|
36851
|
+
#taskSessions = /* @__PURE__ */ new Map();
|
|
36852
|
+
#taskReceipts = /* @__PURE__ */ new Map();
|
|
36853
|
+
#loadReceipts() {
|
|
36854
|
+
if (!this.receiptFile) return;
|
|
36855
|
+
let raw;
|
|
36856
|
+
try {
|
|
36857
|
+
raw = readFileSync(this.receiptFile, "utf8");
|
|
36858
|
+
} catch (error62) {
|
|
36859
|
+
if (error62.code === "ENOENT") return;
|
|
36860
|
+
throw error62;
|
|
36861
|
+
}
|
|
36862
|
+
const parsed = JSON.parse(raw);
|
|
36863
|
+
if (parsed.version !== 1 || !Array.isArray(parsed.receipts)) {
|
|
36864
|
+
throw new Error(
|
|
36865
|
+
"INVALID_TASK_RECEIPTS: Refusing to drop existing replay receipts."
|
|
36866
|
+
);
|
|
36867
|
+
}
|
|
36868
|
+
for (const receipt of parsed.receipts) {
|
|
36869
|
+
if (typeof receipt.taskId !== "string" || !Number.isSafeInteger(receipt.createdAt)) {
|
|
36870
|
+
throw new Error("INVALID_TASK_RECEIPTS: Corrupt task-receipt record.");
|
|
36871
|
+
}
|
|
36872
|
+
this.#taskReceipts.set(receipt.taskId, receipt.createdAt);
|
|
36873
|
+
}
|
|
36874
|
+
this.#pruneReceipts();
|
|
36875
|
+
}
|
|
36876
|
+
#persistReceipts() {
|
|
36877
|
+
if (!this.receiptFile) return;
|
|
36878
|
+
mkdirSync(path3.dirname(this.receiptFile), { recursive: true, mode: 448 });
|
|
36879
|
+
const temporary = this.receiptFile + "." + process.pid + "." + randomUUID() + ".tmp";
|
|
36880
|
+
try {
|
|
36881
|
+
writeFileSync(
|
|
36882
|
+
temporary,
|
|
36883
|
+
JSON.stringify({
|
|
36884
|
+
version: 1,
|
|
36885
|
+
receipts: [...this.#taskReceipts].map(([taskId, createdAt]) => ({
|
|
36886
|
+
taskId,
|
|
36887
|
+
createdAt
|
|
36888
|
+
}))
|
|
36889
|
+
}),
|
|
36890
|
+
{ mode: 384, flag: "wx", flush: true }
|
|
36891
|
+
);
|
|
36892
|
+
renameSync(temporary, this.receiptFile);
|
|
36893
|
+
if (process.platform !== "win32") {
|
|
36894
|
+
const directory = openSync(path3.dirname(this.receiptFile), "r");
|
|
36895
|
+
try {
|
|
36896
|
+
fsyncSync(directory);
|
|
36897
|
+
} finally {
|
|
36898
|
+
closeSync(directory);
|
|
36899
|
+
}
|
|
36900
|
+
}
|
|
36901
|
+
} finally {
|
|
36902
|
+
rmSync(temporary, { force: true });
|
|
36903
|
+
}
|
|
36904
|
+
}
|
|
36905
|
+
#pruneReceipts() {
|
|
36906
|
+
const cutoff = Date.now() - TASK_RECEIPT_RETENTION_MS;
|
|
36907
|
+
let dirty = false;
|
|
36908
|
+
for (const [taskId, createdAt] of this.#taskReceipts) {
|
|
36909
|
+
if (createdAt < cutoff && !this.#taskSessions.has(taskId)) {
|
|
36910
|
+
this.#taskReceipts.delete(taskId);
|
|
36911
|
+
dirty = true;
|
|
36912
|
+
}
|
|
36913
|
+
}
|
|
36914
|
+
if (dirty) this.#persistReceipts();
|
|
36915
|
+
}
|
|
36916
|
+
#recordTaskReceipt(taskId) {
|
|
36917
|
+
this.#taskReceipts.set(taskId, Date.now());
|
|
36918
|
+
try {
|
|
36919
|
+
this.#persistReceipts();
|
|
36920
|
+
} catch (error62) {
|
|
36921
|
+
this.#taskReceipts.delete(taskId);
|
|
36922
|
+
throw error62;
|
|
36923
|
+
}
|
|
36924
|
+
}
|
|
36925
|
+
#signalProcessTree(session, signal) {
|
|
36926
|
+
const pid = session.child.pid;
|
|
36927
|
+
if (!pid || pid < 1) return;
|
|
36928
|
+
if (process.platform === "win32") {
|
|
36929
|
+
const killer = spawn2("taskkill.exe", ["/PID", String(pid), "/T", "/F"], {
|
|
36930
|
+
windowsHide: true,
|
|
36931
|
+
stdio: "ignore"
|
|
36932
|
+
});
|
|
36933
|
+
killer.on("error", () => session.child.kill("SIGKILL"));
|
|
36934
|
+
killer.on("close", (code) => {
|
|
36935
|
+
if (code !== 0 && session.running) session.child.kill("SIGKILL");
|
|
36936
|
+
});
|
|
36937
|
+
return;
|
|
36938
|
+
}
|
|
36939
|
+
try {
|
|
36940
|
+
process.kill(-pid, signal);
|
|
36941
|
+
return;
|
|
36942
|
+
} catch (error62) {
|
|
36943
|
+
if (error62.code === "ESRCH") return;
|
|
36944
|
+
}
|
|
36945
|
+
session.child.kill(signal);
|
|
36946
|
+
}
|
|
36947
|
+
#unixProcessGroupExists(session) {
|
|
36948
|
+
if (process.platform === "win32") return false;
|
|
36949
|
+
const pid = session.child.pid;
|
|
36950
|
+
if (!pid || pid < 1) return false;
|
|
36951
|
+
try {
|
|
36952
|
+
process.kill(-pid, 0);
|
|
36953
|
+
return true;
|
|
36954
|
+
} catch (error62) {
|
|
36955
|
+
return error62.code !== "ESRCH";
|
|
36956
|
+
}
|
|
36957
|
+
}
|
|
36958
|
+
#finishSession(session, exitCode, signal) {
|
|
36959
|
+
if (session.finishedRecorded) return;
|
|
36960
|
+
session.finishedRecorded = true;
|
|
36961
|
+
session.running = false;
|
|
36962
|
+
session.finishedAt = Date.now();
|
|
36963
|
+
session.exitCode = exitCode;
|
|
36964
|
+
session.signal = signal;
|
|
36965
|
+
if (session.timeout) clearTimeout(session.timeout);
|
|
36966
|
+
if (session.forceKillTimer) {
|
|
36967
|
+
clearTimeout(session.forceKillTimer);
|
|
36968
|
+
session.forceKillTimer = void 0;
|
|
36969
|
+
}
|
|
36970
|
+
this.#record(session, "finished", {
|
|
36971
|
+
exitCode,
|
|
36972
|
+
signal,
|
|
36973
|
+
timedOut: session.timedOut
|
|
36974
|
+
});
|
|
36975
|
+
}
|
|
36976
|
+
#requestTermination(session) {
|
|
36977
|
+
if (!session.running) return;
|
|
36978
|
+
session.terminationRequested = true;
|
|
36979
|
+
this.#signalProcessTree(session, "SIGTERM");
|
|
36980
|
+
if (!session.forceKillTimer) {
|
|
36981
|
+
session.forceKillTimer = setTimeout(() => {
|
|
36982
|
+
session.forceKillTimer = void 0;
|
|
36983
|
+
if (process.platform !== "win32" || session.running) {
|
|
36984
|
+
this.#signalProcessTree(session, "SIGKILL");
|
|
36985
|
+
}
|
|
36986
|
+
const pending = session.pendingTerminationClose;
|
|
36987
|
+
if (pending) {
|
|
36988
|
+
session.pendingTerminationClose = void 0;
|
|
36989
|
+
this.#finishSession(session, pending.exitCode, pending.signal);
|
|
36990
|
+
}
|
|
36991
|
+
}, TERMINATION_GRACE_MS);
|
|
36992
|
+
}
|
|
36993
|
+
}
|
|
36994
|
+
#pruneFinished() {
|
|
36995
|
+
const cutoff = Date.now() - LOCAL_SESSION_RETENTION_MS;
|
|
36996
|
+
for (const [sessionId, session] of this.#sessions) {
|
|
36997
|
+
if (session.finishedAt !== void 0 && session.finishedAt < cutoff && session.watchers.size === 0) {
|
|
36998
|
+
this.#sessions.delete(sessionId);
|
|
36999
|
+
if (session.taskId) this.#taskSessions.delete(session.taskId);
|
|
37000
|
+
}
|
|
37001
|
+
}
|
|
37002
|
+
}
|
|
37003
|
+
#record(session, kind, details = {}) {
|
|
37004
|
+
const event = {
|
|
37005
|
+
sessionId: session.sessionId,
|
|
37006
|
+
...session.taskId ? { taskId: session.taskId } : {},
|
|
37007
|
+
sequence: ++session.nextSequence,
|
|
37008
|
+
kind,
|
|
37009
|
+
...details
|
|
37010
|
+
};
|
|
37011
|
+
session.events.push(event);
|
|
37012
|
+
session.retainedEventCharacters += event.data?.length ?? 0;
|
|
37013
|
+
while (session.events.length > MAX_RETAINED_EVENTS || session.retainedEventCharacters > MAX_RETAINED_EVENT_CHARACTERS) {
|
|
37014
|
+
const removed = session.events.shift();
|
|
37015
|
+
session.retainedEventCharacters -= removed.data?.length ?? 0;
|
|
37016
|
+
}
|
|
37017
|
+
for (const wake of session.watchers) wake();
|
|
37018
|
+
}
|
|
37019
|
+
#output(session, stream, chunk) {
|
|
37020
|
+
const before = session[stream].length;
|
|
37021
|
+
const next = appendBounded(
|
|
37022
|
+
session[stream],
|
|
37023
|
+
chunk,
|
|
37024
|
+
session.maxOutputCharacters
|
|
37025
|
+
);
|
|
37026
|
+
session[stream] = next;
|
|
37027
|
+
const appended = next.slice(before);
|
|
37028
|
+
for (let i = 0; i < appended.length; i += MAX_EVENT_CHARACTERS) {
|
|
37029
|
+
this.#record(session, stream, {
|
|
37030
|
+
offset: before + i,
|
|
37031
|
+
data: appended.slice(i, i + MAX_EVENT_CHARACTERS)
|
|
37032
|
+
});
|
|
37033
|
+
}
|
|
37034
|
+
}
|
|
37035
|
+
async watch(sessionId, afterSequence, timeoutMs = 15e3, maxEvents = 32) {
|
|
37036
|
+
this.#pruneFinished();
|
|
37037
|
+
const session = this.#sessions.get(sessionId);
|
|
37038
|
+
if (!session)
|
|
37039
|
+
throw new Error("The requested process session was not found.");
|
|
37040
|
+
if (!Number.isSafeInteger(afterSequence) || afterSequence < 0) {
|
|
37041
|
+
throw new Error("afterSequence must be a non-negative integer.");
|
|
37042
|
+
}
|
|
37043
|
+
if (!Number.isInteger(timeoutMs) || timeoutMs < 100 || timeoutMs > 2e4) {
|
|
37044
|
+
throw new Error("timeoutMs must be between 100 and 20000.");
|
|
37045
|
+
}
|
|
37046
|
+
if (!Number.isInteger(maxEvents) || maxEvents < 1 || maxEvents > 32) {
|
|
37047
|
+
throw new Error("maxEvents must be between 1 and 32.");
|
|
37048
|
+
}
|
|
37049
|
+
const collect = () => {
|
|
37050
|
+
const first = session.events[0]?.sequence ?? session.nextSequence + 1;
|
|
37051
|
+
const missedEvents = afterSequence + 1 < first;
|
|
37052
|
+
const events = session.events.filter((event) => event.sequence > afterSequence).slice(0, maxEvents);
|
|
37053
|
+
return {
|
|
37054
|
+
sessionId,
|
|
37055
|
+
events,
|
|
37056
|
+
nextSequence: events.at(-1)?.sequence ?? Math.max(afterSequence, session.nextSequence),
|
|
37057
|
+
missedEvents,
|
|
37058
|
+
finished: !session.running
|
|
37059
|
+
};
|
|
37060
|
+
};
|
|
37061
|
+
const initial = collect();
|
|
37062
|
+
if (initial.events.length || initial.finished || initial.missedEvents) {
|
|
37063
|
+
return initial;
|
|
37064
|
+
}
|
|
37065
|
+
return new Promise((resolve) => {
|
|
37066
|
+
let timer;
|
|
37067
|
+
const finish = () => {
|
|
37068
|
+
clearTimeout(timer);
|
|
37069
|
+
session.watchers.delete(finish);
|
|
37070
|
+
resolve(collect());
|
|
37071
|
+
};
|
|
37072
|
+
session.watchers.add(finish);
|
|
37073
|
+
timer = setTimeout(finish, timeoutMs);
|
|
37074
|
+
if (session.nextSequence > afterSequence || !session.running) finish();
|
|
37075
|
+
});
|
|
37076
|
+
}
|
|
36617
37077
|
start(options) {
|
|
37078
|
+
this.#pruneReceipts();
|
|
37079
|
+
if (options.taskId) {
|
|
37080
|
+
const existingSession = this.#taskSessions.get(options.taskId);
|
|
37081
|
+
if (existingSession) {
|
|
37082
|
+
const existing = this.#sessions.get(existingSession);
|
|
37083
|
+
if (!existing) throw new Error("PROCESS_TASK_SESSION_LOST");
|
|
37084
|
+
if (existing.program !== options.program || existing.cwd !== options.cwd || existing.args.join("\0") !== options.args.join("\0")) {
|
|
37085
|
+
throw new Error(
|
|
37086
|
+
"PROCESS_TASK_CONFLICT: Task ID already started with different arguments."
|
|
37087
|
+
);
|
|
37088
|
+
}
|
|
37089
|
+
const result2 = snapshot(existing);
|
|
37090
|
+
return {
|
|
37091
|
+
...result2,
|
|
37092
|
+
nextStdoutOffset: result2.stdout.length,
|
|
37093
|
+
nextStderrOffset: result2.stderr.length
|
|
37094
|
+
};
|
|
37095
|
+
}
|
|
37096
|
+
if (this.#taskReceipts.has(options.taskId)) {
|
|
37097
|
+
throw new Error(
|
|
37098
|
+
"PROCESS_TASK_SESSION_LOST: Task already ran; will not execute again."
|
|
37099
|
+
);
|
|
37100
|
+
}
|
|
37101
|
+
}
|
|
37102
|
+
this.#pruneFinished();
|
|
37103
|
+
if (this.#sessions.size >= this.limits.maxRetained) {
|
|
37104
|
+
throw new Error("PROCESS_LIMIT_REACHED: Local process history is full.");
|
|
37105
|
+
}
|
|
37106
|
+
const running = [...this.#sessions.values()].filter(
|
|
37107
|
+
(session2) => session2.running
|
|
37108
|
+
).length;
|
|
37109
|
+
if (running >= this.limits.maxRunning) {
|
|
37110
|
+
throw new Error(
|
|
37111
|
+
"PROCESS_LIMIT_REACHED: Too many concurrent local processes."
|
|
37112
|
+
);
|
|
37113
|
+
}
|
|
37114
|
+
if (options.taskId && this.#taskReceipts.size >= MAX_TASK_TOMBSTONES) {
|
|
37115
|
+
throw new Error(
|
|
37116
|
+
"PROCESS_LIMIT_REACHED: Local task receipt capacity reached."
|
|
37117
|
+
);
|
|
37118
|
+
}
|
|
36618
37119
|
assertProgramAllowed(options.program);
|
|
36619
|
-
|
|
36620
|
-
|
|
36621
|
-
|
|
36622
|
-
|
|
36623
|
-
|
|
36624
|
-
|
|
37120
|
+
if (options.rootedExecution && (options.rootedExecution.program !== options.program || options.rootedExecution.args.join("\0") !== options.args.join("\0")))
|
|
37121
|
+
throw new Error("ROOTED_EXECUTION_MISMATCH");
|
|
37122
|
+
const rootedPayload = options.rootedExecution ? JSON.stringify(options.rootedExecution) : void 0;
|
|
37123
|
+
if (rootedPayload && Buffer.byteLength(rootedPayload) >= 4 * 1024 * 1024)
|
|
37124
|
+
throw new Error("ROOTED_EXECUTION_INPUT_LIMIT");
|
|
37125
|
+
if (options.taskId) this.#recordTaskReceipt(options.taskId);
|
|
37126
|
+
const child = spawn2(
|
|
37127
|
+
options.rootedExecution ? filesystemHelperPath : options.program,
|
|
37128
|
+
options.rootedExecution ? ["--exec"] : options.args,
|
|
37129
|
+
{
|
|
37130
|
+
cwd: options.rootedExecution ? void 0 : options.cwd,
|
|
37131
|
+
env: safeEnvironment(),
|
|
37132
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
37133
|
+
windowsHide: true,
|
|
37134
|
+
// A separate Unix process group lets us terminate descendants too.
|
|
37135
|
+
detached: process.platform !== "win32"
|
|
37136
|
+
}
|
|
37137
|
+
);
|
|
37138
|
+
child.stdin.on("error", () => void 0);
|
|
37139
|
+
child.stdin.end(rootedPayload);
|
|
36625
37140
|
const session = {
|
|
36626
37141
|
sessionId: randomUUID(),
|
|
37142
|
+
...options.taskId ? { taskId: options.taskId } : {},
|
|
36627
37143
|
program: options.program,
|
|
36628
37144
|
args: [...options.args],
|
|
36629
37145
|
cwd: options.cwd,
|
|
@@ -36634,22 +37150,21 @@ var ProcessManager = class {
|
|
|
36634
37150
|
running: true,
|
|
36635
37151
|
exitCode: null,
|
|
36636
37152
|
signal: null,
|
|
36637
|
-
timedOut: false
|
|
37153
|
+
timedOut: false,
|
|
37154
|
+
nextSequence: 0,
|
|
37155
|
+
events: [],
|
|
37156
|
+
retainedEventCharacters: 0,
|
|
37157
|
+
watchers: /* @__PURE__ */ new Set()
|
|
36638
37158
|
};
|
|
36639
37159
|
this.#sessions.set(session.sessionId, session);
|
|
37160
|
+
if (session.taskId)
|
|
37161
|
+
this.#taskSessions.set(session.taskId, session.sessionId);
|
|
37162
|
+
this.#record(session, "started");
|
|
36640
37163
|
child.stdout.on("data", (chunk) => {
|
|
36641
|
-
session
|
|
36642
|
-
session.stdout,
|
|
36643
|
-
chunk,
|
|
36644
|
-
session.maxOutputCharacters
|
|
36645
|
-
);
|
|
37164
|
+
this.#output(session, "stdout", chunk);
|
|
36646
37165
|
});
|
|
36647
37166
|
child.stderr.on("data", (chunk) => {
|
|
36648
|
-
session
|
|
36649
|
-
session.stderr,
|
|
36650
|
-
chunk,
|
|
36651
|
-
session.maxOutputCharacters
|
|
36652
|
-
);
|
|
37167
|
+
this.#output(session, "stderr", chunk);
|
|
36653
37168
|
});
|
|
36654
37169
|
child.once("error", (error62) => {
|
|
36655
37170
|
session.stderr = appendBounded(
|
|
@@ -36663,15 +37178,17 @@ var ProcessManager = class {
|
|
|
36663
37178
|
if (session.timeout) clearTimeout(session.timeout);
|
|
36664
37179
|
});
|
|
36665
37180
|
child.once("close", (exitCode, signal) => {
|
|
36666
|
-
session.
|
|
36667
|
-
|
|
36668
|
-
|
|
36669
|
-
|
|
37181
|
+
if (session.terminationRequested && session.forceKillTimer && this.#unixProcessGroupExists(session)) {
|
|
37182
|
+
session.pendingTerminationClose = { exitCode, signal };
|
|
37183
|
+
if (session.timeout) clearTimeout(session.timeout);
|
|
37184
|
+
return;
|
|
37185
|
+
}
|
|
37186
|
+
this.#finishSession(session, exitCode, signal);
|
|
36670
37187
|
});
|
|
36671
37188
|
session.timeout = setTimeout(() => {
|
|
36672
37189
|
if (!session.running) return;
|
|
36673
37190
|
session.timedOut = true;
|
|
36674
|
-
|
|
37191
|
+
this.#requestTermination(session);
|
|
36675
37192
|
}, options.timeoutMs);
|
|
36676
37193
|
const result = snapshot(session);
|
|
36677
37194
|
return {
|
|
@@ -36681,6 +37198,7 @@ var ProcessManager = class {
|
|
|
36681
37198
|
};
|
|
36682
37199
|
}
|
|
36683
37200
|
read(sessionId, stdoutOffset, stderrOffset, maxOutputCharacters) {
|
|
37201
|
+
this.#pruneFinished();
|
|
36684
37202
|
const session = this.#sessions.get(sessionId);
|
|
36685
37203
|
if (!session)
|
|
36686
37204
|
throw new Error("The requested process session was not found.");
|
|
@@ -36709,36 +37227,60 @@ var ProcessManager = class {
|
|
|
36709
37227
|
};
|
|
36710
37228
|
}
|
|
36711
37229
|
terminate(sessionId) {
|
|
37230
|
+
this.#pruneFinished();
|
|
36712
37231
|
const session = this.#sessions.get(sessionId);
|
|
36713
37232
|
if (!session)
|
|
36714
37233
|
throw new Error("The requested process session was not found.");
|
|
36715
|
-
|
|
37234
|
+
this.#requestTermination(session);
|
|
36716
37235
|
return snapshot(session);
|
|
36717
37236
|
}
|
|
36718
37237
|
list() {
|
|
37238
|
+
this.#pruneFinished();
|
|
36719
37239
|
return [...this.#sessions.values()].map(snapshot);
|
|
36720
37240
|
}
|
|
36721
37241
|
close() {
|
|
36722
37242
|
for (const session of this.#sessions.values()) {
|
|
36723
37243
|
if (session.timeout) clearTimeout(session.timeout);
|
|
36724
|
-
|
|
37244
|
+
this.#requestTermination(session);
|
|
37245
|
+
}
|
|
37246
|
+
for (const session of this.#sessions.values()) {
|
|
37247
|
+
for (const wake of session.watchers) wake();
|
|
36725
37248
|
}
|
|
36726
37249
|
this.#sessions.clear();
|
|
37250
|
+
this.#taskSessions.clear();
|
|
36727
37251
|
}
|
|
36728
37252
|
};
|
|
36729
37253
|
|
|
36730
37254
|
// src/server.ts
|
|
36731
37255
|
var roots = configuredRoots();
|
|
36732
37256
|
var processManager = new ProcessManager();
|
|
37257
|
+
var sharedFilesystem;
|
|
37258
|
+
function defaultFilesystem() {
|
|
37259
|
+
return sharedFilesystem ??= new RootedFilesystem(roots);
|
|
37260
|
+
}
|
|
37261
|
+
async function initializeFilesystemBoundary() {
|
|
37262
|
+
await defaultFilesystem().ready;
|
|
37263
|
+
}
|
|
37264
|
+
async function closeFilesystemBoundary() {
|
|
37265
|
+
await sharedFilesystem?.close();
|
|
37266
|
+
}
|
|
36733
37267
|
function textResult(value) {
|
|
36734
37268
|
return {
|
|
36735
37269
|
content: [{ type: "text", text: JSON.stringify(value, null, 2) }],
|
|
36736
37270
|
structuredContent: value
|
|
36737
37271
|
};
|
|
36738
37272
|
}
|
|
36739
|
-
function createServer() {
|
|
37273
|
+
function createServer(options = {}) {
|
|
37274
|
+
const filesystem = options.filesystem ?? defaultFilesystem();
|
|
37275
|
+
const requireAuthorizedAgent = () => {
|
|
37276
|
+
if (!options.allowDestructive) {
|
|
37277
|
+
throw new Error(
|
|
37278
|
+
"LOCAL_APPROVAL_REQUIRED: Connect through the approved Terminal Commands agent."
|
|
37279
|
+
);
|
|
37280
|
+
}
|
|
37281
|
+
};
|
|
36740
37282
|
const server2 = new McpServer(
|
|
36741
|
-
{ name: "terminal-commands", version: "0.
|
|
37283
|
+
{ name: "terminal-commands", version: "0.2.0" },
|
|
36742
37284
|
{
|
|
36743
37285
|
instructions: "This server controls the user's machine. Prefer dedicated file tools over commands. Every command and write can modify or expose local data and must receive user approval. Never request secrets or bypass configured roots and safety controls."
|
|
36744
37286
|
}
|
|
@@ -36791,15 +37333,12 @@ function createServer() {
|
|
|
36791
37333
|
}
|
|
36792
37334
|
},
|
|
36793
37335
|
async ({ path: requestedPath }) => {
|
|
36794
|
-
const directory =
|
|
36795
|
-
const entries = await
|
|
37336
|
+
const directory = filesystem.resolve(requestedPath);
|
|
37337
|
+
const { entries, truncated } = await filesystem.list(directory);
|
|
36796
37338
|
return textResult({
|
|
36797
|
-
path: directory,
|
|
36798
|
-
entries
|
|
36799
|
-
|
|
36800
|
-
type: entry.isDirectory() ? "directory" : entry.isFile() ? "file" : "other"
|
|
36801
|
-
})),
|
|
36802
|
-
truncated: entries.length > 1e3
|
|
37339
|
+
path: directory.displayPath,
|
|
37340
|
+
entries,
|
|
37341
|
+
truncated
|
|
36803
37342
|
});
|
|
36804
37343
|
}
|
|
36805
37344
|
);
|
|
@@ -36824,13 +37363,13 @@ function createServer() {
|
|
|
36824
37363
|
}
|
|
36825
37364
|
},
|
|
36826
37365
|
async ({ path: requestedPath, maxCharacters }) => {
|
|
36827
|
-
const filename =
|
|
36828
|
-
const { contents, truncated } = await
|
|
37366
|
+
const filename = filesystem.resolve(requestedPath);
|
|
37367
|
+
const { contents, truncated } = await filesystem.read(
|
|
36829
37368
|
filename,
|
|
36830
37369
|
maxCharacters
|
|
36831
37370
|
);
|
|
36832
37371
|
return textResult({
|
|
36833
|
-
path: filename,
|
|
37372
|
+
path: filename.displayPath,
|
|
36834
37373
|
contents,
|
|
36835
37374
|
truncated
|
|
36836
37375
|
});
|
|
@@ -36857,14 +37396,16 @@ function createServer() {
|
|
|
36857
37396
|
}
|
|
36858
37397
|
},
|
|
36859
37398
|
async ({ path: requestedPath, contents, overwrite }) => {
|
|
36860
|
-
|
|
36861
|
-
|
|
36862
|
-
|
|
36863
|
-
|
|
36864
|
-
|
|
37399
|
+
requireAuthorizedAgent();
|
|
37400
|
+
const filename = filesystem.resolve(requestedPath);
|
|
37401
|
+
const bytesWritten = await filesystem.write(
|
|
37402
|
+
filename,
|
|
37403
|
+
contents,
|
|
37404
|
+
overwrite
|
|
37405
|
+
);
|
|
36865
37406
|
return textResult({
|
|
36866
|
-
path: filename,
|
|
36867
|
-
bytesWritten
|
|
37407
|
+
path: filename.displayPath,
|
|
37408
|
+
bytesWritten
|
|
36868
37409
|
});
|
|
36869
37410
|
}
|
|
36870
37411
|
);
|
|
@@ -36874,6 +37415,7 @@ function createServer() {
|
|
|
36874
37415
|
title: "Start a process",
|
|
36875
37416
|
description: "Start one approved executable without a shell on this machine and return a session ID for reading its output.",
|
|
36876
37417
|
inputSchema: {
|
|
37418
|
+
taskId: external_exports.string().uuid().optional(),
|
|
36877
37419
|
program: external_exports.string().min(1),
|
|
36878
37420
|
args: external_exports.array(external_exports.string()).max(100).default([]),
|
|
36879
37421
|
cwd: external_exports.string().min(1).optional(),
|
|
@@ -36882,6 +37424,7 @@ function createServer() {
|
|
|
36882
37424
|
},
|
|
36883
37425
|
outputSchema: {
|
|
36884
37426
|
sessionId: external_exports.string(),
|
|
37427
|
+
taskId: external_exports.string().uuid().optional(),
|
|
36885
37428
|
program: external_exports.string(),
|
|
36886
37429
|
args: external_exports.array(external_exports.string()),
|
|
36887
37430
|
cwd: external_exports.string(),
|
|
@@ -36901,16 +37444,21 @@ function createServer() {
|
|
|
36901
37444
|
openWorldHint: true
|
|
36902
37445
|
}
|
|
36903
37446
|
},
|
|
36904
|
-
async ({ program, args, cwd, timeoutMs, maxOutputCharacters }) => {
|
|
36905
|
-
|
|
37447
|
+
async ({ taskId, program, args, cwd, timeoutMs, maxOutputCharacters }) => {
|
|
37448
|
+
requireAuthorizedAgent();
|
|
37449
|
+
const workingDirectory = filesystem.resolve(cwd ?? roots[0]);
|
|
37450
|
+
const rootedExecution = await filesystem.execution(
|
|
36906
37451
|
cwd ?? roots[0],
|
|
36907
|
-
|
|
37452
|
+
program,
|
|
37453
|
+
args
|
|
36908
37454
|
);
|
|
36909
37455
|
return textResult(
|
|
36910
37456
|
processManager.start({
|
|
37457
|
+
taskId,
|
|
36911
37458
|
program,
|
|
36912
37459
|
args,
|
|
36913
|
-
cwd: workingDirectory,
|
|
37460
|
+
cwd: workingDirectory.displayPath,
|
|
37461
|
+
rootedExecution,
|
|
36914
37462
|
timeoutMs,
|
|
36915
37463
|
maxOutputCharacters
|
|
36916
37464
|
})
|
|
@@ -36930,6 +37478,7 @@ function createServer() {
|
|
|
36930
37478
|
},
|
|
36931
37479
|
outputSchema: {
|
|
36932
37480
|
sessionId: external_exports.string(),
|
|
37481
|
+
taskId: external_exports.string().uuid().optional(),
|
|
36933
37482
|
program: external_exports.string(),
|
|
36934
37483
|
args: external_exports.array(external_exports.string()),
|
|
36935
37484
|
cwd: external_exports.string(),
|
|
@@ -36958,6 +37507,51 @@ function createServer() {
|
|
|
36958
37507
|
)
|
|
36959
37508
|
)
|
|
36960
37509
|
);
|
|
37510
|
+
server2.registerTool(
|
|
37511
|
+
"watch_process_events",
|
|
37512
|
+
{
|
|
37513
|
+
title: "Watch local process events",
|
|
37514
|
+
description: "Wait for sequenced, bounded process events after a cursor. Used internally by the companion to push progress over WebSocket.",
|
|
37515
|
+
inputSchema: {
|
|
37516
|
+
sessionId: external_exports.string().uuid(),
|
|
37517
|
+
afterSequence: external_exports.number().int().nonnegative().default(0),
|
|
37518
|
+
timeoutMs: external_exports.number().int().min(100).max(2e4).default(1e4),
|
|
37519
|
+
maxEvents: external_exports.number().int().min(1).max(32).default(32)
|
|
37520
|
+
},
|
|
37521
|
+
outputSchema: {
|
|
37522
|
+
sessionId: external_exports.string().uuid(),
|
|
37523
|
+
events: external_exports.array(
|
|
37524
|
+
external_exports.object({
|
|
37525
|
+
sessionId: external_exports.string().uuid(),
|
|
37526
|
+
taskId: external_exports.string().uuid().optional(),
|
|
37527
|
+
sequence: external_exports.number().int().positive(),
|
|
37528
|
+
kind: external_exports.enum(["started", "stdout", "stderr", "finished"]),
|
|
37529
|
+
offset: external_exports.number().int().nonnegative().optional(),
|
|
37530
|
+
data: external_exports.string().max(4096).optional(),
|
|
37531
|
+
exitCode: external_exports.number().int().nullable().optional(),
|
|
37532
|
+
signal: external_exports.string().nullable().optional(),
|
|
37533
|
+
timedOut: external_exports.boolean().optional()
|
|
37534
|
+
})
|
|
37535
|
+
).max(32),
|
|
37536
|
+
nextSequence: external_exports.number().int().nonnegative(),
|
|
37537
|
+
missedEvents: external_exports.boolean(),
|
|
37538
|
+
finished: external_exports.boolean()
|
|
37539
|
+
},
|
|
37540
|
+
annotations: {
|
|
37541
|
+
readOnlyHint: true,
|
|
37542
|
+
destructiveHint: false,
|
|
37543
|
+
openWorldHint: false
|
|
37544
|
+
}
|
|
37545
|
+
},
|
|
37546
|
+
async ({ sessionId, afterSequence, timeoutMs, maxEvents }) => textResult(
|
|
37547
|
+
await processManager.watch(
|
|
37548
|
+
sessionId,
|
|
37549
|
+
afterSequence,
|
|
37550
|
+
timeoutMs,
|
|
37551
|
+
maxEvents
|
|
37552
|
+
)
|
|
37553
|
+
)
|
|
37554
|
+
);
|
|
36961
37555
|
server2.registerTool(
|
|
36962
37556
|
"terminate_process",
|
|
36963
37557
|
{
|
|
@@ -36966,6 +37560,7 @@ function createServer() {
|
|
|
36966
37560
|
inputSchema: { sessionId: external_exports.string().uuid() },
|
|
36967
37561
|
outputSchema: {
|
|
36968
37562
|
sessionId: external_exports.string(),
|
|
37563
|
+
taskId: external_exports.string().uuid().optional(),
|
|
36969
37564
|
program: external_exports.string(),
|
|
36970
37565
|
args: external_exports.array(external_exports.string()),
|
|
36971
37566
|
cwd: external_exports.string(),
|
|
@@ -36983,7 +37578,10 @@ function createServer() {
|
|
|
36983
37578
|
openWorldHint: false
|
|
36984
37579
|
}
|
|
36985
37580
|
},
|
|
36986
|
-
async ({ sessionId }) =>
|
|
37581
|
+
async ({ sessionId }) => {
|
|
37582
|
+
requireAuthorizedAgent();
|
|
37583
|
+
return textResult(processManager.terminate(sessionId));
|
|
37584
|
+
}
|
|
36987
37585
|
);
|
|
36988
37586
|
server2.registerTool(
|
|
36989
37587
|
"list_processes",
|
|
@@ -36995,6 +37593,7 @@ function createServer() {
|
|
|
36995
37593
|
processes: external_exports.array(
|
|
36996
37594
|
external_exports.object({
|
|
36997
37595
|
sessionId: external_exports.string(),
|
|
37596
|
+
taskId: external_exports.string().uuid().optional(),
|
|
36998
37597
|
program: external_exports.string(),
|
|
36999
37598
|
args: external_exports.array(external_exports.string()),
|
|
37000
37599
|
cwd: external_exports.string(),
|
|
@@ -37002,6 +37601,8 @@ function createServer() {
|
|
|
37002
37601
|
running: external_exports.boolean(),
|
|
37003
37602
|
exitCode: external_exports.number().int().nullable(),
|
|
37004
37603
|
signal: external_exports.string().nullable(),
|
|
37604
|
+
stdout: external_exports.string(),
|
|
37605
|
+
stderr: external_exports.string(),
|
|
37005
37606
|
timedOut: external_exports.boolean()
|
|
37006
37607
|
})
|
|
37007
37608
|
)
|
|
@@ -37018,12 +37619,16 @@ function createServer() {
|
|
|
37018
37619
|
}
|
|
37019
37620
|
|
|
37020
37621
|
// src/stdio.ts
|
|
37021
|
-
var server = createServer(
|
|
37622
|
+
var server = createServer({
|
|
37623
|
+
allowDestructive: process.env.MACHINE_TERMINAL_TRUST_STDIO === "1"
|
|
37624
|
+
});
|
|
37022
37625
|
var transport = new StdioServerTransport();
|
|
37626
|
+
await initializeFilesystemBoundary();
|
|
37023
37627
|
await server.connect(transport);
|
|
37024
37628
|
async function shutdown() {
|
|
37025
37629
|
processManager.close();
|
|
37026
37630
|
await server.close();
|
|
37631
|
+
await closeFilesystemBoundary();
|
|
37027
37632
|
process.exit(0);
|
|
37028
37633
|
}
|
|
37029
37634
|
process.on("SIGINT", () => void shutdown());
|