terminal-commands 0.1.1 → 0.1.3
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 +5 -0
- package/dist/agent.js +115 -25
- package/dist/cli.js +108 -90
- package/dist/index.js +286 -52
- package/dist/stdio.js +278 -44
- package/package.json +8 -3
- package/src/agent-config.ts +0 -40
- package/src/agent-core.ts +0 -173
- package/src/agent.ts +0 -16
- package/src/cli-format.ts +0 -57
- package/src/cli-options.ts +0 -459
- package/src/cli.ts +0 -475
- package/src/credential-store.ts +0 -98
- package/src/device-auth.ts +0 -163
- package/src/index.ts +0 -83
- package/src/runner.ts +0 -55
- package/src/security.ts +0 -112
- package/src/server.ts +0 -217
- package/src/stdio.ts +0 -16
- package/test/agent-config.test.ts +0 -32
- package/test/agent-core.test.ts +0 -37
- package/test/cli-options.test.ts +0 -121
- package/test/credential-store.test.ts +0 -44
- package/test/device-auth.test.ts +0 -88
- package/test/runner.test.ts +0 -29
- package/test/security.test.ts +0 -113
- package/test/smoke.mjs +0 -34
- package/tsconfig.json +0 -13
package/dist/index.js
CHANGED
|
@@ -18390,11 +18390,11 @@ var require_parse = __commonJS({
|
|
|
18390
18390
|
segments[segments.length] = parent;
|
|
18391
18391
|
}
|
|
18392
18392
|
var n = key.length;
|
|
18393
|
-
var
|
|
18393
|
+
var open3 = first;
|
|
18394
18394
|
var collected = 0;
|
|
18395
|
-
while (
|
|
18395
|
+
while (open3 >= 0 && collected < options.depth) {
|
|
18396
18396
|
var level = 1;
|
|
18397
|
-
var i =
|
|
18397
|
+
var i = open3 + 1;
|
|
18398
18398
|
var close = -1;
|
|
18399
18399
|
while (i < n && close < 0) {
|
|
18400
18400
|
var cu = key.charCodeAt(i);
|
|
@@ -18409,23 +18409,23 @@ var require_parse = __commonJS({
|
|
|
18409
18409
|
i += 1;
|
|
18410
18410
|
}
|
|
18411
18411
|
if (close < 0) {
|
|
18412
|
-
segments[segments.length] = "[" + key.slice(
|
|
18412
|
+
segments[segments.length] = "[" + key.slice(open3) + "]";
|
|
18413
18413
|
return segments;
|
|
18414
18414
|
}
|
|
18415
|
-
var seg = key.slice(
|
|
18415
|
+
var seg = key.slice(open3, close + 1);
|
|
18416
18416
|
var content = seg.slice(1, -1);
|
|
18417
18417
|
if (!options.plainObjects && has.call(Object.prototype, content) && !options.allowPrototypes) {
|
|
18418
18418
|
return;
|
|
18419
18419
|
}
|
|
18420
18420
|
segments[segments.length] = seg;
|
|
18421
18421
|
collected += 1;
|
|
18422
|
-
|
|
18422
|
+
open3 = key.indexOf("[", close + 1);
|
|
18423
18423
|
}
|
|
18424
|
-
if (
|
|
18424
|
+
if (open3 >= 0) {
|
|
18425
18425
|
if (options.strictDepth === true) {
|
|
18426
18426
|
throw new RangeError("Input depth exceeded depth option of " + options.depth + " and strictDepth is true");
|
|
18427
18427
|
}
|
|
18428
|
-
segments[segments.length] = "[" + key.slice(
|
|
18428
|
+
segments[segments.length] = "[" + key.slice(open3) + "]";
|
|
18429
18429
|
}
|
|
18430
18430
|
return segments;
|
|
18431
18431
|
};
|
|
@@ -54524,7 +54524,7 @@ var StreamableHTTPServerTransport = class {
|
|
|
54524
54524
|
};
|
|
54525
54525
|
|
|
54526
54526
|
// src/server.ts
|
|
54527
|
-
import {
|
|
54527
|
+
import { readdir, writeFile } from "node:fs/promises";
|
|
54528
54528
|
|
|
54529
54529
|
// node_modules/zod/v3/helpers/util.js
|
|
54530
54530
|
var util;
|
|
@@ -62498,9 +62498,30 @@ var EMPTY_COMPLETION_RESULT = {
|
|
|
62498
62498
|
}
|
|
62499
62499
|
};
|
|
62500
62500
|
|
|
62501
|
-
// src/
|
|
62502
|
-
import {
|
|
62503
|
-
|
|
62501
|
+
// src/file-reader.ts
|
|
62502
|
+
import { open as open2 } from "node:fs/promises";
|
|
62503
|
+
async function readBoundedTextFile(filename, maxCharacters) {
|
|
62504
|
+
const handle = await open2(filename, "r");
|
|
62505
|
+
try {
|
|
62506
|
+
const stats = await handle.stat();
|
|
62507
|
+
const buffer = Buffer.allocUnsafe(maxCharacters * 4 + 4);
|
|
62508
|
+
const { bytesRead } = await handle.read(buffer, 0, buffer.length, 0);
|
|
62509
|
+
const hasUnreadBytes = bytesRead < stats.size;
|
|
62510
|
+
const decoded = new TextDecoder().decode(buffer.subarray(0, bytesRead), {
|
|
62511
|
+
stream: hasUnreadBytes
|
|
62512
|
+
});
|
|
62513
|
+
return {
|
|
62514
|
+
contents: decoded.slice(0, maxCharacters),
|
|
62515
|
+
truncated: hasUnreadBytes || decoded.length > maxCharacters
|
|
62516
|
+
};
|
|
62517
|
+
} finally {
|
|
62518
|
+
await handle.close();
|
|
62519
|
+
}
|
|
62520
|
+
}
|
|
62521
|
+
|
|
62522
|
+
// src/process-manager.ts
|
|
62523
|
+
import { randomUUID } from "node:crypto";
|
|
62524
|
+
import { spawn } from "node:child_process";
|
|
62504
62525
|
|
|
62505
62526
|
// src/security.ts
|
|
62506
62527
|
import { constants as constants2 } from "node:fs";
|
|
@@ -62590,38 +62611,144 @@ function assertProgramAllowed(program) {
|
|
|
62590
62611
|
}
|
|
62591
62612
|
}
|
|
62592
62613
|
|
|
62593
|
-
// src/
|
|
62594
|
-
|
|
62595
|
-
|
|
62596
|
-
|
|
62597
|
-
|
|
62598
|
-
|
|
62599
|
-
|
|
62600
|
-
|
|
62601
|
-
|
|
62602
|
-
|
|
62603
|
-
|
|
62604
|
-
|
|
62605
|
-
|
|
62606
|
-
|
|
62607
|
-
|
|
62608
|
-
|
|
62614
|
+
// src/process-manager.ts
|
|
62615
|
+
function appendBounded(current, value, limit) {
|
|
62616
|
+
if (current.length >= limit) return current;
|
|
62617
|
+
return `${current}${value.toString("utf8")}`.slice(0, limit);
|
|
62618
|
+
}
|
|
62619
|
+
function snapshot(session) {
|
|
62620
|
+
return {
|
|
62621
|
+
sessionId: session.sessionId,
|
|
62622
|
+
program: session.program,
|
|
62623
|
+
args: session.args,
|
|
62624
|
+
cwd: session.cwd,
|
|
62625
|
+
pid: session.child.pid ?? null,
|
|
62626
|
+
running: session.running,
|
|
62627
|
+
exitCode: session.exitCode,
|
|
62628
|
+
signal: session.signal,
|
|
62629
|
+
stdout: session.stdout,
|
|
62630
|
+
stderr: session.stderr,
|
|
62631
|
+
timedOut: session.timedOut
|
|
62632
|
+
};
|
|
62633
|
+
}
|
|
62634
|
+
var ProcessManager = class {
|
|
62635
|
+
#sessions = /* @__PURE__ */ new Map();
|
|
62636
|
+
start(options) {
|
|
62637
|
+
assertProgramAllowed(options.program);
|
|
62638
|
+
const child = spawn(options.program, options.args, {
|
|
62639
|
+
cwd: options.cwd,
|
|
62640
|
+
env: safeEnvironment(),
|
|
62641
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
62642
|
+
windowsHide: true
|
|
62643
|
+
});
|
|
62644
|
+
const session = {
|
|
62645
|
+
sessionId: randomUUID(),
|
|
62646
|
+
program: options.program,
|
|
62647
|
+
args: [...options.args],
|
|
62648
|
+
cwd: options.cwd,
|
|
62649
|
+
child,
|
|
62650
|
+
maxOutputCharacters: options.maxOutputCharacters,
|
|
62651
|
+
stdout: "",
|
|
62652
|
+
stderr: "",
|
|
62653
|
+
running: true,
|
|
62654
|
+
exitCode: null,
|
|
62655
|
+
signal: null,
|
|
62656
|
+
timedOut: false
|
|
62657
|
+
};
|
|
62658
|
+
this.#sessions.set(session.sessionId, session);
|
|
62659
|
+
child.stdout.on("data", (chunk) => {
|
|
62660
|
+
session.stdout = appendBounded(
|
|
62661
|
+
session.stdout,
|
|
62662
|
+
chunk,
|
|
62663
|
+
session.maxOutputCharacters
|
|
62664
|
+
);
|
|
62665
|
+
});
|
|
62666
|
+
child.stderr.on("data", (chunk) => {
|
|
62667
|
+
session.stderr = appendBounded(
|
|
62668
|
+
session.stderr,
|
|
62669
|
+
chunk,
|
|
62670
|
+
session.maxOutputCharacters
|
|
62671
|
+
);
|
|
62672
|
+
});
|
|
62673
|
+
child.once("error", (error62) => {
|
|
62674
|
+
session.stderr = appendBounded(
|
|
62675
|
+
session.stderr,
|
|
62676
|
+
error62.message,
|
|
62677
|
+
session.maxOutputCharacters
|
|
62678
|
+
);
|
|
62679
|
+
session.running = false;
|
|
62680
|
+
session.exitCode = null;
|
|
62681
|
+
session.signal = null;
|
|
62682
|
+
if (session.timeout) clearTimeout(session.timeout);
|
|
62683
|
+
});
|
|
62684
|
+
child.once("close", (exitCode, signal) => {
|
|
62685
|
+
session.running = false;
|
|
62686
|
+
session.exitCode = exitCode;
|
|
62687
|
+
session.signal = signal;
|
|
62688
|
+
if (session.timeout) clearTimeout(session.timeout);
|
|
62689
|
+
});
|
|
62690
|
+
session.timeout = setTimeout(() => {
|
|
62691
|
+
if (!session.running) return;
|
|
62692
|
+
session.timedOut = true;
|
|
62693
|
+
child.kill("SIGTERM");
|
|
62694
|
+
}, options.timeoutMs);
|
|
62695
|
+
const result = snapshot(session);
|
|
62696
|
+
return {
|
|
62697
|
+
...result,
|
|
62698
|
+
nextStdoutOffset: result.stdout.length,
|
|
62699
|
+
nextStderrOffset: result.stderr.length
|
|
62700
|
+
};
|
|
62701
|
+
}
|
|
62702
|
+
read(sessionId, stdoutOffset, stderrOffset, maxOutputCharacters) {
|
|
62703
|
+
const session = this.#sessions.get(sessionId);
|
|
62704
|
+
if (!session)
|
|
62705
|
+
throw new Error("The requested process session was not found.");
|
|
62706
|
+
const safeStdoutOffset = Math.max(
|
|
62707
|
+
0,
|
|
62708
|
+
Math.min(stdoutOffset, session.stdout.length)
|
|
62709
|
+
);
|
|
62710
|
+
const safeStderrOffset = Math.max(
|
|
62711
|
+
0,
|
|
62712
|
+
Math.min(stderrOffset, session.stderr.length)
|
|
62713
|
+
);
|
|
62714
|
+
const stdout = session.stdout.slice(
|
|
62715
|
+
safeStdoutOffset,
|
|
62716
|
+
safeStdoutOffset + maxOutputCharacters
|
|
62717
|
+
);
|
|
62718
|
+
const stderr = session.stderr.slice(
|
|
62719
|
+
safeStderrOffset,
|
|
62720
|
+
safeStderrOffset + maxOutputCharacters
|
|
62609
62721
|
);
|
|
62610
|
-
return { exitCode: 0, signal: null, stdout, stderr, timedOut: false };
|
|
62611
|
-
} catch (error62) {
|
|
62612
|
-
const failure2 = error62;
|
|
62613
62722
|
return {
|
|
62614
|
-
|
|
62615
|
-
|
|
62616
|
-
|
|
62617
|
-
|
|
62618
|
-
|
|
62723
|
+
...snapshot(session),
|
|
62724
|
+
stdout,
|
|
62725
|
+
stderr,
|
|
62726
|
+
nextStdoutOffset: safeStdoutOffset + stdout.length,
|
|
62727
|
+
nextStderrOffset: safeStderrOffset + stderr.length
|
|
62619
62728
|
};
|
|
62620
62729
|
}
|
|
62621
|
-
|
|
62730
|
+
terminate(sessionId) {
|
|
62731
|
+
const session = this.#sessions.get(sessionId);
|
|
62732
|
+
if (!session)
|
|
62733
|
+
throw new Error("The requested process session was not found.");
|
|
62734
|
+
if (session.running) session.child.kill("SIGTERM");
|
|
62735
|
+
return snapshot(session);
|
|
62736
|
+
}
|
|
62737
|
+
list() {
|
|
62738
|
+
return [...this.#sessions.values()].map(snapshot);
|
|
62739
|
+
}
|
|
62740
|
+
close() {
|
|
62741
|
+
for (const session of this.#sessions.values()) {
|
|
62742
|
+
if (session.timeout) clearTimeout(session.timeout);
|
|
62743
|
+
if (session.running) session.child.kill("SIGTERM");
|
|
62744
|
+
}
|
|
62745
|
+
this.#sessions.clear();
|
|
62746
|
+
}
|
|
62747
|
+
};
|
|
62622
62748
|
|
|
62623
62749
|
// src/server.ts
|
|
62624
62750
|
var roots = configuredRoots();
|
|
62751
|
+
var processManager = new ProcessManager();
|
|
62625
62752
|
function textResult(value) {
|
|
62626
62753
|
return {
|
|
62627
62754
|
content: [{ type: "text", text: JSON.stringify(value, null, 2) }],
|
|
@@ -62717,11 +62844,14 @@ function createServer() {
|
|
|
62717
62844
|
},
|
|
62718
62845
|
async ({ path: requestedPath, maxCharacters }) => {
|
|
62719
62846
|
const filename = await resolveReadablePath(requestedPath, roots);
|
|
62720
|
-
const contents = await
|
|
62847
|
+
const { contents, truncated } = await readBoundedTextFile(
|
|
62848
|
+
filename,
|
|
62849
|
+
maxCharacters
|
|
62850
|
+
);
|
|
62721
62851
|
return textResult({
|
|
62722
62852
|
path: filename,
|
|
62723
|
-
contents
|
|
62724
|
-
truncated
|
|
62853
|
+
contents,
|
|
62854
|
+
truncated
|
|
62725
62855
|
});
|
|
62726
62856
|
}
|
|
62727
62857
|
);
|
|
@@ -62758,10 +62888,10 @@ function createServer() {
|
|
|
62758
62888
|
}
|
|
62759
62889
|
);
|
|
62760
62890
|
server.registerTool(
|
|
62761
|
-
"
|
|
62891
|
+
"start_process",
|
|
62762
62892
|
{
|
|
62763
|
-
title: "
|
|
62764
|
-
description: "
|
|
62893
|
+
title: "Start a process",
|
|
62894
|
+
description: "Start one approved executable without a shell on this machine and return a session ID for reading its output.",
|
|
62765
62895
|
inputSchema: {
|
|
62766
62896
|
program: external_exports.string().min(1),
|
|
62767
62897
|
args: external_exports.array(external_exports.string()).max(100).default([]),
|
|
@@ -62770,14 +62900,19 @@ function createServer() {
|
|
|
62770
62900
|
maxOutputCharacters: external_exports.number().int().min(1e3).max(2e5).default(5e4)
|
|
62771
62901
|
},
|
|
62772
62902
|
outputSchema: {
|
|
62903
|
+
sessionId: external_exports.string(),
|
|
62773
62904
|
program: external_exports.string(),
|
|
62774
62905
|
args: external_exports.array(external_exports.string()),
|
|
62775
62906
|
cwd: external_exports.string(),
|
|
62907
|
+
pid: external_exports.number().int().nullable(),
|
|
62908
|
+
running: external_exports.boolean(),
|
|
62776
62909
|
exitCode: external_exports.number().int().nullable(),
|
|
62777
62910
|
signal: external_exports.string().nullable(),
|
|
62778
62911
|
stdout: external_exports.string(),
|
|
62779
62912
|
stderr: external_exports.string(),
|
|
62780
|
-
timedOut: external_exports.boolean()
|
|
62913
|
+
timedOut: external_exports.boolean(),
|
|
62914
|
+
nextStdoutOffset: external_exports.number().int().nonnegative(),
|
|
62915
|
+
nextStderrOffset: external_exports.number().int().nonnegative()
|
|
62781
62916
|
},
|
|
62782
62917
|
annotations: {
|
|
62783
62918
|
readOnlyHint: false,
|
|
@@ -62790,16 +62925,114 @@ function createServer() {
|
|
|
62790
62925
|
cwd ?? roots[0],
|
|
62791
62926
|
roots
|
|
62792
62927
|
);
|
|
62793
|
-
|
|
62794
|
-
|
|
62795
|
-
|
|
62796
|
-
|
|
62797
|
-
|
|
62798
|
-
|
|
62799
|
-
|
|
62800
|
-
|
|
62928
|
+
return textResult(
|
|
62929
|
+
processManager.start({
|
|
62930
|
+
program,
|
|
62931
|
+
args,
|
|
62932
|
+
cwd: workingDirectory,
|
|
62933
|
+
timeoutMs,
|
|
62934
|
+
maxOutputCharacters
|
|
62935
|
+
})
|
|
62936
|
+
);
|
|
62801
62937
|
}
|
|
62802
62938
|
);
|
|
62939
|
+
server.registerTool(
|
|
62940
|
+
"read_process_output",
|
|
62941
|
+
{
|
|
62942
|
+
title: "Read process output",
|
|
62943
|
+
description: "Read new stdout and stderr from a previously started process session.",
|
|
62944
|
+
inputSchema: {
|
|
62945
|
+
sessionId: external_exports.string().uuid(),
|
|
62946
|
+
stdoutOffset: external_exports.number().int().min(0).default(0),
|
|
62947
|
+
stderrOffset: external_exports.number().int().min(0).default(0),
|
|
62948
|
+
maxOutputCharacters: external_exports.number().int().min(1e3).max(2e5).default(5e4)
|
|
62949
|
+
},
|
|
62950
|
+
outputSchema: {
|
|
62951
|
+
sessionId: external_exports.string(),
|
|
62952
|
+
program: external_exports.string(),
|
|
62953
|
+
args: external_exports.array(external_exports.string()),
|
|
62954
|
+
cwd: external_exports.string(),
|
|
62955
|
+
pid: external_exports.number().int().nullable(),
|
|
62956
|
+
running: external_exports.boolean(),
|
|
62957
|
+
exitCode: external_exports.number().int().nullable(),
|
|
62958
|
+
signal: external_exports.string().nullable(),
|
|
62959
|
+
stdout: external_exports.string(),
|
|
62960
|
+
stderr: external_exports.string(),
|
|
62961
|
+
timedOut: external_exports.boolean(),
|
|
62962
|
+
nextStdoutOffset: external_exports.number().int().nonnegative(),
|
|
62963
|
+
nextStderrOffset: external_exports.number().int().nonnegative()
|
|
62964
|
+
},
|
|
62965
|
+
annotations: {
|
|
62966
|
+
readOnlyHint: true,
|
|
62967
|
+
destructiveHint: false,
|
|
62968
|
+
openWorldHint: false
|
|
62969
|
+
}
|
|
62970
|
+
},
|
|
62971
|
+
async ({ sessionId, stdoutOffset, stderrOffset, maxOutputCharacters }) => textResult(
|
|
62972
|
+
processManager.read(
|
|
62973
|
+
sessionId,
|
|
62974
|
+
stdoutOffset,
|
|
62975
|
+
stderrOffset,
|
|
62976
|
+
maxOutputCharacters
|
|
62977
|
+
)
|
|
62978
|
+
)
|
|
62979
|
+
);
|
|
62980
|
+
server.registerTool(
|
|
62981
|
+
"terminate_process",
|
|
62982
|
+
{
|
|
62983
|
+
title: "Terminate a process",
|
|
62984
|
+
description: "Stop a previously started process session on this machine.",
|
|
62985
|
+
inputSchema: { sessionId: external_exports.string().uuid() },
|
|
62986
|
+
outputSchema: {
|
|
62987
|
+
sessionId: external_exports.string(),
|
|
62988
|
+
program: external_exports.string(),
|
|
62989
|
+
args: external_exports.array(external_exports.string()),
|
|
62990
|
+
cwd: external_exports.string(),
|
|
62991
|
+
pid: external_exports.number().int().nullable(),
|
|
62992
|
+
running: external_exports.boolean(),
|
|
62993
|
+
exitCode: external_exports.number().int().nullable(),
|
|
62994
|
+
signal: external_exports.string().nullable(),
|
|
62995
|
+
stdout: external_exports.string(),
|
|
62996
|
+
stderr: external_exports.string(),
|
|
62997
|
+
timedOut: external_exports.boolean()
|
|
62998
|
+
},
|
|
62999
|
+
annotations: {
|
|
63000
|
+
readOnlyHint: false,
|
|
63001
|
+
destructiveHint: true,
|
|
63002
|
+
openWorldHint: false
|
|
63003
|
+
}
|
|
63004
|
+
},
|
|
63005
|
+
async ({ sessionId }) => textResult(processManager.terminate(sessionId))
|
|
63006
|
+
);
|
|
63007
|
+
server.registerTool(
|
|
63008
|
+
"list_processes",
|
|
63009
|
+
{
|
|
63010
|
+
title: "List process sessions",
|
|
63011
|
+
description: "List process sessions started through Terminal Commands.",
|
|
63012
|
+
inputSchema: {},
|
|
63013
|
+
outputSchema: {
|
|
63014
|
+
processes: external_exports.array(
|
|
63015
|
+
external_exports.object({
|
|
63016
|
+
sessionId: external_exports.string(),
|
|
63017
|
+
program: external_exports.string(),
|
|
63018
|
+
args: external_exports.array(external_exports.string()),
|
|
63019
|
+
cwd: external_exports.string(),
|
|
63020
|
+
pid: external_exports.number().int().nullable(),
|
|
63021
|
+
running: external_exports.boolean(),
|
|
63022
|
+
exitCode: external_exports.number().int().nullable(),
|
|
63023
|
+
signal: external_exports.string().nullable(),
|
|
63024
|
+
timedOut: external_exports.boolean()
|
|
63025
|
+
})
|
|
63026
|
+
)
|
|
63027
|
+
},
|
|
63028
|
+
annotations: {
|
|
63029
|
+
readOnlyHint: true,
|
|
63030
|
+
destructiveHint: false,
|
|
63031
|
+
openWorldHint: false
|
|
63032
|
+
}
|
|
63033
|
+
},
|
|
63034
|
+
async () => textResult({ processes: processManager.list() })
|
|
63035
|
+
);
|
|
62803
63036
|
return server;
|
|
62804
63037
|
}
|
|
62805
63038
|
|
|
@@ -62860,6 +63093,7 @@ var listener = app.listen(port, host, () => {
|
|
|
62860
63093
|
});
|
|
62861
63094
|
function shutdown(signal) {
|
|
62862
63095
|
console.log(`Received ${signal}; shutting down.`);
|
|
63096
|
+
processManager.close();
|
|
62863
63097
|
listener.close((error62) => {
|
|
62864
63098
|
if (error62) {
|
|
62865
63099
|
console.error(error62);
|