terminal-commands 0.1.2 → 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 +102 -0
- package/dist/agent.js +634 -122
- package/dist/cli.js +883 -138
- package/dist/index.js +1281 -364
- 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 +1001 -186
- package/package.json +6 -5
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,14 +36540,28 @@ var EMPTY_COMPLETION_RESULT = {
|
|
|
36479
36540
|
}
|
|
36480
36541
|
};
|
|
36481
36542
|
|
|
36482
|
-
// src/
|
|
36483
|
-
import {
|
|
36484
|
-
import {
|
|
36543
|
+
// src/process-manager.ts
|
|
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
|
|
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";
|
|
36485
36563
|
|
|
36486
36564
|
// src/security.ts
|
|
36487
|
-
import { constants } from "node:fs";
|
|
36488
|
-
import { access, lstat, realpath } from "node:fs/promises";
|
|
36489
|
-
import os from "node:os";
|
|
36490
36565
|
import path from "node:path";
|
|
36491
36566
|
var DEFAULT_ENVIRONMENT_KEYS = [
|
|
36492
36567
|
"LANG",
|
|
@@ -36519,34 +36594,7 @@ function configuredRoots(raw = process.env.MACHINE_TERMINAL_ROOTS) {
|
|
|
36519
36594
|
}
|
|
36520
36595
|
function isWithinRoot(candidate, root) {
|
|
36521
36596
|
const relative = path.relative(root, candidate);
|
|
36522
|
-
return relative === "" || !relative.startsWith(`..${path.sep}`) && relative !== "..";
|
|
36523
|
-
}
|
|
36524
|
-
async function resolveReadablePath(requestedPath, roots2, cwd = roots2[0] ?? os.homedir()) {
|
|
36525
|
-
const resolvedRoots = await Promise.all(roots2.map((root) => realpath(root)));
|
|
36526
|
-
const candidate = await realpath(path.resolve(cwd, requestedPath));
|
|
36527
|
-
if (!resolvedRoots.some((root) => isWithinRoot(candidate, root))) {
|
|
36528
|
-
throw new Error("Path is outside MACHINE_TERMINAL_ROOTS.");
|
|
36529
|
-
}
|
|
36530
|
-
await access(candidate, constants.R_OK);
|
|
36531
|
-
return candidate;
|
|
36532
|
-
}
|
|
36533
|
-
async function resolveWritablePath(requestedPath, roots2, cwd = roots2[0] ?? os.homedir()) {
|
|
36534
|
-
const absolute = path.resolve(cwd, requestedPath);
|
|
36535
|
-
const resolvedParent = await realpath(path.dirname(absolute));
|
|
36536
|
-
const resolvedRoots = await Promise.all(roots2.map((root) => realpath(root)));
|
|
36537
|
-
const candidate = path.join(resolvedParent, path.basename(absolute));
|
|
36538
|
-
if (!resolvedRoots.some((root) => isWithinRoot(candidate, root))) {
|
|
36539
|
-
throw new Error("Path is outside MACHINE_TERMINAL_ROOTS.");
|
|
36540
|
-
}
|
|
36541
|
-
try {
|
|
36542
|
-
const stats = await lstat(candidate);
|
|
36543
|
-
if (stats.isSymbolicLink()) {
|
|
36544
|
-
throw new Error("Writing through symbolic links is not allowed.");
|
|
36545
|
-
}
|
|
36546
|
-
} catch (error62) {
|
|
36547
|
-
if (error62.code !== "ENOENT") throw error62;
|
|
36548
|
-
}
|
|
36549
|
-
return candidate;
|
|
36597
|
+
return relative === "" || !path.isAbsolute(relative) && !relative.startsWith(`..${path.sep}`) && relative !== "..";
|
|
36550
36598
|
}
|
|
36551
36599
|
function safeEnvironment(source = process.env) {
|
|
36552
36600
|
return Object.fromEntries(
|
|
@@ -36571,68 +36619,668 @@ function assertProgramAllowed(program) {
|
|
|
36571
36619
|
}
|
|
36572
36620
|
}
|
|
36573
36621
|
|
|
36574
|
-
// src/
|
|
36575
|
-
var
|
|
36576
|
-
|
|
36577
|
-
|
|
36578
|
-
|
|
36579
|
-
|
|
36580
|
-
|
|
36581
|
-
|
|
36582
|
-
|
|
36583
|
-
|
|
36584
|
-
|
|
36585
|
-
|
|
36586
|
-
|
|
36587
|
-
|
|
36588
|
-
|
|
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
|
+
}
|
|
36589
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])
|
|
36590
36751
|
);
|
|
36591
|
-
|
|
36592
|
-
|
|
36593
|
-
const
|
|
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");
|
|
36594
36757
|
return {
|
|
36595
|
-
|
|
36596
|
-
|
|
36597
|
-
|
|
36598
|
-
stderr: failure2.stderr ?? failure2.message,
|
|
36599
|
-
timedOut: failure2.killed === true && failure2.signal === "SIGTERM"
|
|
36758
|
+
root: index,
|
|
36759
|
+
path: relative,
|
|
36760
|
+
displayPath: path2.join(this.roots[index], relative)
|
|
36600
36761
|
};
|
|
36601
36762
|
}
|
|
36602
|
-
|
|
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
|
+
};
|
|
36603
36807
|
|
|
36604
|
-
// src/
|
|
36605
|
-
|
|
36606
|
-
|
|
36607
|
-
|
|
36608
|
-
|
|
36609
|
-
|
|
36610
|
-
|
|
36611
|
-
|
|
36612
|
-
|
|
36613
|
-
|
|
36614
|
-
|
|
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;
|
|
36816
|
+
function appendBounded(current, value, limit) {
|
|
36817
|
+
if (current.length >= limit) return current;
|
|
36818
|
+
return `${current}${value.toString("utf8")}`.slice(0, limit);
|
|
36819
|
+
}
|
|
36820
|
+
function snapshot(session) {
|
|
36821
|
+
return {
|
|
36822
|
+
sessionId: session.sessionId,
|
|
36823
|
+
...session.taskId ? { taskId: session.taskId } : {},
|
|
36824
|
+
program: session.program,
|
|
36825
|
+
args: session.args,
|
|
36826
|
+
cwd: session.cwd,
|
|
36827
|
+
pid: session.child.pid ?? null,
|
|
36828
|
+
running: session.running,
|
|
36829
|
+
exitCode: session.exitCode,
|
|
36830
|
+
signal: session.signal,
|
|
36831
|
+
stdout: session.stdout,
|
|
36832
|
+
stderr: session.stderr,
|
|
36833
|
+
timedOut: session.timedOut
|
|
36834
|
+
};
|
|
36835
|
+
}
|
|
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;
|
|
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
|
+
}
|
|
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
|
+
}
|
|
37119
|
+
assertProgramAllowed(options.program);
|
|
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);
|
|
37140
|
+
const session = {
|
|
37141
|
+
sessionId: randomUUID(),
|
|
37142
|
+
...options.taskId ? { taskId: options.taskId } : {},
|
|
37143
|
+
program: options.program,
|
|
37144
|
+
args: [...options.args],
|
|
37145
|
+
cwd: options.cwd,
|
|
37146
|
+
child,
|
|
37147
|
+
maxOutputCharacters: options.maxOutputCharacters,
|
|
37148
|
+
stdout: "",
|
|
37149
|
+
stderr: "",
|
|
37150
|
+
running: true,
|
|
37151
|
+
exitCode: null,
|
|
37152
|
+
signal: null,
|
|
37153
|
+
timedOut: false,
|
|
37154
|
+
nextSequence: 0,
|
|
37155
|
+
events: [],
|
|
37156
|
+
retainedEventCharacters: 0,
|
|
37157
|
+
watchers: /* @__PURE__ */ new Set()
|
|
37158
|
+
};
|
|
37159
|
+
this.#sessions.set(session.sessionId, session);
|
|
37160
|
+
if (session.taskId)
|
|
37161
|
+
this.#taskSessions.set(session.taskId, session.sessionId);
|
|
37162
|
+
this.#record(session, "started");
|
|
37163
|
+
child.stdout.on("data", (chunk) => {
|
|
37164
|
+
this.#output(session, "stdout", chunk);
|
|
37165
|
+
});
|
|
37166
|
+
child.stderr.on("data", (chunk) => {
|
|
37167
|
+
this.#output(session, "stderr", chunk);
|
|
36615
37168
|
});
|
|
37169
|
+
child.once("error", (error62) => {
|
|
37170
|
+
session.stderr = appendBounded(
|
|
37171
|
+
session.stderr,
|
|
37172
|
+
error62.message,
|
|
37173
|
+
session.maxOutputCharacters
|
|
37174
|
+
);
|
|
37175
|
+
session.running = false;
|
|
37176
|
+
session.exitCode = null;
|
|
37177
|
+
session.signal = null;
|
|
37178
|
+
if (session.timeout) clearTimeout(session.timeout);
|
|
37179
|
+
});
|
|
37180
|
+
child.once("close", (exitCode, signal) => {
|
|
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);
|
|
37187
|
+
});
|
|
37188
|
+
session.timeout = setTimeout(() => {
|
|
37189
|
+
if (!session.running) return;
|
|
37190
|
+
session.timedOut = true;
|
|
37191
|
+
this.#requestTermination(session);
|
|
37192
|
+
}, options.timeoutMs);
|
|
37193
|
+
const result = snapshot(session);
|
|
36616
37194
|
return {
|
|
36617
|
-
|
|
36618
|
-
|
|
37195
|
+
...result,
|
|
37196
|
+
nextStdoutOffset: result.stdout.length,
|
|
37197
|
+
nextStderrOffset: result.stderr.length
|
|
36619
37198
|
};
|
|
36620
|
-
} finally {
|
|
36621
|
-
await handle.close();
|
|
36622
37199
|
}
|
|
36623
|
-
|
|
37200
|
+
read(sessionId, stdoutOffset, stderrOffset, maxOutputCharacters) {
|
|
37201
|
+
this.#pruneFinished();
|
|
37202
|
+
const session = this.#sessions.get(sessionId);
|
|
37203
|
+
if (!session)
|
|
37204
|
+
throw new Error("The requested process session was not found.");
|
|
37205
|
+
const safeStdoutOffset = Math.max(
|
|
37206
|
+
0,
|
|
37207
|
+
Math.min(stdoutOffset, session.stdout.length)
|
|
37208
|
+
);
|
|
37209
|
+
const safeStderrOffset = Math.max(
|
|
37210
|
+
0,
|
|
37211
|
+
Math.min(stderrOffset, session.stderr.length)
|
|
37212
|
+
);
|
|
37213
|
+
const stdout = session.stdout.slice(
|
|
37214
|
+
safeStdoutOffset,
|
|
37215
|
+
safeStdoutOffset + maxOutputCharacters
|
|
37216
|
+
);
|
|
37217
|
+
const stderr = session.stderr.slice(
|
|
37218
|
+
safeStderrOffset,
|
|
37219
|
+
safeStderrOffset + maxOutputCharacters
|
|
37220
|
+
);
|
|
37221
|
+
return {
|
|
37222
|
+
...snapshot(session),
|
|
37223
|
+
stdout,
|
|
37224
|
+
stderr,
|
|
37225
|
+
nextStdoutOffset: safeStdoutOffset + stdout.length,
|
|
37226
|
+
nextStderrOffset: safeStderrOffset + stderr.length
|
|
37227
|
+
};
|
|
37228
|
+
}
|
|
37229
|
+
terminate(sessionId) {
|
|
37230
|
+
this.#pruneFinished();
|
|
37231
|
+
const session = this.#sessions.get(sessionId);
|
|
37232
|
+
if (!session)
|
|
37233
|
+
throw new Error("The requested process session was not found.");
|
|
37234
|
+
this.#requestTermination(session);
|
|
37235
|
+
return snapshot(session);
|
|
37236
|
+
}
|
|
37237
|
+
list() {
|
|
37238
|
+
this.#pruneFinished();
|
|
37239
|
+
return [...this.#sessions.values()].map(snapshot);
|
|
37240
|
+
}
|
|
37241
|
+
close() {
|
|
37242
|
+
for (const session of this.#sessions.values()) {
|
|
37243
|
+
if (session.timeout) clearTimeout(session.timeout);
|
|
37244
|
+
this.#requestTermination(session);
|
|
37245
|
+
}
|
|
37246
|
+
for (const session of this.#sessions.values()) {
|
|
37247
|
+
for (const wake of session.watchers) wake();
|
|
37248
|
+
}
|
|
37249
|
+
this.#sessions.clear();
|
|
37250
|
+
this.#taskSessions.clear();
|
|
37251
|
+
}
|
|
37252
|
+
};
|
|
36624
37253
|
|
|
36625
37254
|
// src/server.ts
|
|
36626
37255
|
var roots = configuredRoots();
|
|
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
|
+
}
|
|
36627
37267
|
function textResult(value) {
|
|
36628
37268
|
return {
|
|
36629
37269
|
content: [{ type: "text", text: JSON.stringify(value, null, 2) }],
|
|
36630
37270
|
structuredContent: value
|
|
36631
37271
|
};
|
|
36632
37272
|
}
|
|
36633
|
-
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
|
+
};
|
|
36634
37282
|
const server2 = new McpServer(
|
|
36635
|
-
{ name: "terminal-commands", version: "0.
|
|
37283
|
+
{ name: "terminal-commands", version: "0.2.0" },
|
|
36636
37284
|
{
|
|
36637
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."
|
|
36638
37286
|
}
|
|
@@ -36685,15 +37333,12 @@ function createServer() {
|
|
|
36685
37333
|
}
|
|
36686
37334
|
},
|
|
36687
37335
|
async ({ path: requestedPath }) => {
|
|
36688
|
-
const directory =
|
|
36689
|
-
const entries = await
|
|
37336
|
+
const directory = filesystem.resolve(requestedPath);
|
|
37337
|
+
const { entries, truncated } = await filesystem.list(directory);
|
|
36690
37338
|
return textResult({
|
|
36691
|
-
path: directory,
|
|
36692
|
-
entries
|
|
36693
|
-
|
|
36694
|
-
type: entry.isDirectory() ? "directory" : entry.isFile() ? "file" : "other"
|
|
36695
|
-
})),
|
|
36696
|
-
truncated: entries.length > 1e3
|
|
37339
|
+
path: directory.displayPath,
|
|
37340
|
+
entries,
|
|
37341
|
+
truncated
|
|
36697
37342
|
});
|
|
36698
37343
|
}
|
|
36699
37344
|
);
|
|
@@ -36718,13 +37363,13 @@ function createServer() {
|
|
|
36718
37363
|
}
|
|
36719
37364
|
},
|
|
36720
37365
|
async ({ path: requestedPath, maxCharacters }) => {
|
|
36721
|
-
const filename =
|
|
36722
|
-
const { contents, truncated } = await
|
|
37366
|
+
const filename = filesystem.resolve(requestedPath);
|
|
37367
|
+
const { contents, truncated } = await filesystem.read(
|
|
36723
37368
|
filename,
|
|
36724
37369
|
maxCharacters
|
|
36725
37370
|
);
|
|
36726
37371
|
return textResult({
|
|
36727
|
-
path: filename,
|
|
37372
|
+
path: filename.displayPath,
|
|
36728
37373
|
contents,
|
|
36729
37374
|
truncated
|
|
36730
37375
|
});
|
|
@@ -36751,23 +37396,26 @@ function createServer() {
|
|
|
36751
37396
|
}
|
|
36752
37397
|
},
|
|
36753
37398
|
async ({ path: requestedPath, contents, overwrite }) => {
|
|
36754
|
-
|
|
36755
|
-
|
|
36756
|
-
|
|
36757
|
-
|
|
36758
|
-
|
|
37399
|
+
requireAuthorizedAgent();
|
|
37400
|
+
const filename = filesystem.resolve(requestedPath);
|
|
37401
|
+
const bytesWritten = await filesystem.write(
|
|
37402
|
+
filename,
|
|
37403
|
+
contents,
|
|
37404
|
+
overwrite
|
|
37405
|
+
);
|
|
36759
37406
|
return textResult({
|
|
36760
|
-
path: filename,
|
|
36761
|
-
bytesWritten
|
|
37407
|
+
path: filename.displayPath,
|
|
37408
|
+
bytesWritten
|
|
36762
37409
|
});
|
|
36763
37410
|
}
|
|
36764
37411
|
);
|
|
36765
37412
|
server2.registerTool(
|
|
36766
|
-
"
|
|
37413
|
+
"start_process",
|
|
36767
37414
|
{
|
|
36768
|
-
title: "
|
|
36769
|
-
description: "
|
|
37415
|
+
title: "Start a process",
|
|
37416
|
+
description: "Start one approved executable without a shell on this machine and return a session ID for reading its output.",
|
|
36770
37417
|
inputSchema: {
|
|
37418
|
+
taskId: external_exports.string().uuid().optional(),
|
|
36771
37419
|
program: external_exports.string().min(1),
|
|
36772
37420
|
args: external_exports.array(external_exports.string()).max(100).default([]),
|
|
36773
37421
|
cwd: external_exports.string().min(1).optional(),
|
|
@@ -36775,14 +37423,20 @@ function createServer() {
|
|
|
36775
37423
|
maxOutputCharacters: external_exports.number().int().min(1e3).max(2e5).default(5e4)
|
|
36776
37424
|
},
|
|
36777
37425
|
outputSchema: {
|
|
37426
|
+
sessionId: external_exports.string(),
|
|
37427
|
+
taskId: external_exports.string().uuid().optional(),
|
|
36778
37428
|
program: external_exports.string(),
|
|
36779
37429
|
args: external_exports.array(external_exports.string()),
|
|
36780
37430
|
cwd: external_exports.string(),
|
|
37431
|
+
pid: external_exports.number().int().nullable(),
|
|
37432
|
+
running: external_exports.boolean(),
|
|
36781
37433
|
exitCode: external_exports.number().int().nullable(),
|
|
36782
37434
|
signal: external_exports.string().nullable(),
|
|
36783
37435
|
stdout: external_exports.string(),
|
|
36784
37436
|
stderr: external_exports.string(),
|
|
36785
|
-
timedOut: external_exports.boolean()
|
|
37437
|
+
timedOut: external_exports.boolean(),
|
|
37438
|
+
nextStdoutOffset: external_exports.number().int().nonnegative(),
|
|
37439
|
+
nextStderrOffset: external_exports.number().int().nonnegative()
|
|
36786
37440
|
},
|
|
36787
37441
|
annotations: {
|
|
36788
37442
|
readOnlyHint: false,
|
|
@@ -36790,30 +37444,191 @@ function createServer() {
|
|
|
36790
37444
|
openWorldHint: true
|
|
36791
37445
|
}
|
|
36792
37446
|
},
|
|
36793
|
-
async ({ program, args, cwd, timeoutMs, maxOutputCharacters }) => {
|
|
36794
|
-
|
|
37447
|
+
async ({ taskId, program, args, cwd, timeoutMs, maxOutputCharacters }) => {
|
|
37448
|
+
requireAuthorizedAgent();
|
|
37449
|
+
const workingDirectory = filesystem.resolve(cwd ?? roots[0]);
|
|
37450
|
+
const rootedExecution = await filesystem.execution(
|
|
36795
37451
|
cwd ?? roots[0],
|
|
36796
|
-
roots
|
|
36797
|
-
);
|
|
36798
|
-
const result = await runProgram({
|
|
36799
37452
|
program,
|
|
36800
|
-
args
|
|
36801
|
-
|
|
37453
|
+
args
|
|
37454
|
+
);
|
|
37455
|
+
return textResult(
|
|
37456
|
+
processManager.start({
|
|
37457
|
+
taskId,
|
|
37458
|
+
program,
|
|
37459
|
+
args,
|
|
37460
|
+
cwd: workingDirectory.displayPath,
|
|
37461
|
+
rootedExecution,
|
|
37462
|
+
timeoutMs,
|
|
37463
|
+
maxOutputCharacters
|
|
37464
|
+
})
|
|
37465
|
+
);
|
|
37466
|
+
}
|
|
37467
|
+
);
|
|
37468
|
+
server2.registerTool(
|
|
37469
|
+
"read_process_output",
|
|
37470
|
+
{
|
|
37471
|
+
title: "Read process output",
|
|
37472
|
+
description: "Read new stdout and stderr from a previously started process session.",
|
|
37473
|
+
inputSchema: {
|
|
37474
|
+
sessionId: external_exports.string().uuid(),
|
|
37475
|
+
stdoutOffset: external_exports.number().int().min(0).default(0),
|
|
37476
|
+
stderrOffset: external_exports.number().int().min(0).default(0),
|
|
37477
|
+
maxOutputCharacters: external_exports.number().int().min(1e3).max(2e5).default(5e4)
|
|
37478
|
+
},
|
|
37479
|
+
outputSchema: {
|
|
37480
|
+
sessionId: external_exports.string(),
|
|
37481
|
+
taskId: external_exports.string().uuid().optional(),
|
|
37482
|
+
program: external_exports.string(),
|
|
37483
|
+
args: external_exports.array(external_exports.string()),
|
|
37484
|
+
cwd: external_exports.string(),
|
|
37485
|
+
pid: external_exports.number().int().nullable(),
|
|
37486
|
+
running: external_exports.boolean(),
|
|
37487
|
+
exitCode: external_exports.number().int().nullable(),
|
|
37488
|
+
signal: external_exports.string().nullable(),
|
|
37489
|
+
stdout: external_exports.string(),
|
|
37490
|
+
stderr: external_exports.string(),
|
|
37491
|
+
timedOut: external_exports.boolean(),
|
|
37492
|
+
nextStdoutOffset: external_exports.number().int().nonnegative(),
|
|
37493
|
+
nextStderrOffset: external_exports.number().int().nonnegative()
|
|
37494
|
+
},
|
|
37495
|
+
annotations: {
|
|
37496
|
+
readOnlyHint: true,
|
|
37497
|
+
destructiveHint: false,
|
|
37498
|
+
openWorldHint: false
|
|
37499
|
+
}
|
|
37500
|
+
},
|
|
37501
|
+
async ({ sessionId, stdoutOffset, stderrOffset, maxOutputCharacters }) => textResult(
|
|
37502
|
+
processManager.read(
|
|
37503
|
+
sessionId,
|
|
37504
|
+
stdoutOffset,
|
|
37505
|
+
stderrOffset,
|
|
37506
|
+
maxOutputCharacters
|
|
37507
|
+
)
|
|
37508
|
+
)
|
|
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,
|
|
36802
37550
|
timeoutMs,
|
|
36803
|
-
|
|
36804
|
-
|
|
36805
|
-
|
|
37551
|
+
maxEvents
|
|
37552
|
+
)
|
|
37553
|
+
)
|
|
37554
|
+
);
|
|
37555
|
+
server2.registerTool(
|
|
37556
|
+
"terminate_process",
|
|
37557
|
+
{
|
|
37558
|
+
title: "Terminate a process",
|
|
37559
|
+
description: "Stop a previously started process session on this machine.",
|
|
37560
|
+
inputSchema: { sessionId: external_exports.string().uuid() },
|
|
37561
|
+
outputSchema: {
|
|
37562
|
+
sessionId: external_exports.string(),
|
|
37563
|
+
taskId: external_exports.string().uuid().optional(),
|
|
37564
|
+
program: external_exports.string(),
|
|
37565
|
+
args: external_exports.array(external_exports.string()),
|
|
37566
|
+
cwd: external_exports.string(),
|
|
37567
|
+
pid: external_exports.number().int().nullable(),
|
|
37568
|
+
running: external_exports.boolean(),
|
|
37569
|
+
exitCode: external_exports.number().int().nullable(),
|
|
37570
|
+
signal: external_exports.string().nullable(),
|
|
37571
|
+
stdout: external_exports.string(),
|
|
37572
|
+
stderr: external_exports.string(),
|
|
37573
|
+
timedOut: external_exports.boolean()
|
|
37574
|
+
},
|
|
37575
|
+
annotations: {
|
|
37576
|
+
readOnlyHint: false,
|
|
37577
|
+
destructiveHint: true,
|
|
37578
|
+
openWorldHint: false
|
|
37579
|
+
}
|
|
37580
|
+
},
|
|
37581
|
+
async ({ sessionId }) => {
|
|
37582
|
+
requireAuthorizedAgent();
|
|
37583
|
+
return textResult(processManager.terminate(sessionId));
|
|
36806
37584
|
}
|
|
36807
37585
|
);
|
|
37586
|
+
server2.registerTool(
|
|
37587
|
+
"list_processes",
|
|
37588
|
+
{
|
|
37589
|
+
title: "List process sessions",
|
|
37590
|
+
description: "List process sessions started through Terminal Commands.",
|
|
37591
|
+
inputSchema: {},
|
|
37592
|
+
outputSchema: {
|
|
37593
|
+
processes: external_exports.array(
|
|
37594
|
+
external_exports.object({
|
|
37595
|
+
sessionId: external_exports.string(),
|
|
37596
|
+
taskId: external_exports.string().uuid().optional(),
|
|
37597
|
+
program: external_exports.string(),
|
|
37598
|
+
args: external_exports.array(external_exports.string()),
|
|
37599
|
+
cwd: external_exports.string(),
|
|
37600
|
+
pid: external_exports.number().int().nullable(),
|
|
37601
|
+
running: external_exports.boolean(),
|
|
37602
|
+
exitCode: external_exports.number().int().nullable(),
|
|
37603
|
+
signal: external_exports.string().nullable(),
|
|
37604
|
+
stdout: external_exports.string(),
|
|
37605
|
+
stderr: external_exports.string(),
|
|
37606
|
+
timedOut: external_exports.boolean()
|
|
37607
|
+
})
|
|
37608
|
+
)
|
|
37609
|
+
},
|
|
37610
|
+
annotations: {
|
|
37611
|
+
readOnlyHint: true,
|
|
37612
|
+
destructiveHint: false,
|
|
37613
|
+
openWorldHint: false
|
|
37614
|
+
}
|
|
37615
|
+
},
|
|
37616
|
+
async () => textResult({ processes: processManager.list() })
|
|
37617
|
+
);
|
|
36808
37618
|
return server2;
|
|
36809
37619
|
}
|
|
36810
37620
|
|
|
36811
37621
|
// src/stdio.ts
|
|
36812
|
-
var server = createServer(
|
|
37622
|
+
var server = createServer({
|
|
37623
|
+
allowDestructive: process.env.MACHINE_TERMINAL_TRUST_STDIO === "1"
|
|
37624
|
+
});
|
|
36813
37625
|
var transport = new StdioServerTransport();
|
|
37626
|
+
await initializeFilesystemBoundary();
|
|
36814
37627
|
await server.connect(transport);
|
|
36815
37628
|
async function shutdown() {
|
|
37629
|
+
processManager.close();
|
|
36816
37630
|
await server.close();
|
|
37631
|
+
await closeFilesystemBoundary();
|
|
36817
37632
|
process.exit(0);
|
|
36818
37633
|
}
|
|
36819
37634
|
process.on("SIGINT", () => void shutdown());
|