terminal-commands 0.1.1 → 0.1.2
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/dist/agent.js +103 -23
- package/dist/cli.js +96 -88
- package/dist/index.js +36 -12
- package/dist/stdio.js +28 -4
- package/package.json +7 -2
- 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/agent.js
CHANGED
|
@@ -11894,7 +11894,7 @@ function attachSchema(issues, start, inst) {
|
|
|
11894
11894
|
(_a3 = issues[i]).schema ?? (_a3.schema = inst);
|
|
11895
11895
|
}
|
|
11896
11896
|
}
|
|
11897
|
-
function finalizeIssue(iss, ctx,
|
|
11897
|
+
function finalizeIssue(iss, ctx, config3) {
|
|
11898
11898
|
var _a3;
|
|
11899
11899
|
const traits = iss.inst?._zod?.traits;
|
|
11900
11900
|
if (traits?.has("$ZodType")) {
|
|
@@ -11904,7 +11904,7 @@ function finalizeIssue(iss, ctx, config2) {
|
|
|
11904
11904
|
iss.schema = iss.inst;
|
|
11905
11905
|
}
|
|
11906
11906
|
const schemaError = iss.schema !== iss.inst ? iss.schema?._zod.def?.error : void 0;
|
|
11907
|
-
const message = iss.message ? iss.message : unwrapMessage(iss.inst?._zod.def?.error?.(iss)) ?? unwrapMessage(schemaError?.(iss)) ?? unwrapMessage(ctx?.error?.(iss)) ?? unwrapMessage(
|
|
11907
|
+
const message = iss.message ? iss.message : unwrapMessage(iss.inst?._zod.def?.error?.(iss)) ?? unwrapMessage(schemaError?.(iss)) ?? unwrapMessage(ctx?.error?.(iss)) ?? unwrapMessage(config3.customError?.(iss)) ?? unwrapMessage(config3.localeError?.(iss)) ?? "Invalid input";
|
|
11908
11908
|
const full = {};
|
|
11909
11909
|
for (const k of Object.keys(iss)) {
|
|
11910
11910
|
if (k === "inst" || k === "schema" || k === "continue" || k === "input" || k === "__proto__")
|
|
@@ -33537,21 +33537,21 @@ var Client = class extends Protocol {
|
|
|
33537
33537
|
* Handlers are silently skipped if the server doesn't advertise the corresponding listChanged capability.
|
|
33538
33538
|
* @internal
|
|
33539
33539
|
*/
|
|
33540
|
-
_setupListChangedHandlers(
|
|
33541
|
-
if (
|
|
33542
|
-
this._setupListChangedHandler("tools", ToolListChangedNotificationSchema,
|
|
33540
|
+
_setupListChangedHandlers(config3) {
|
|
33541
|
+
if (config3.tools && this._serverCapabilities?.tools?.listChanged) {
|
|
33542
|
+
this._setupListChangedHandler("tools", ToolListChangedNotificationSchema, config3.tools, async () => {
|
|
33543
33543
|
const result = await this.listTools();
|
|
33544
33544
|
return result.tools;
|
|
33545
33545
|
});
|
|
33546
33546
|
}
|
|
33547
|
-
if (
|
|
33548
|
-
this._setupListChangedHandler("prompts", PromptListChangedNotificationSchema,
|
|
33547
|
+
if (config3.prompts && this._serverCapabilities?.prompts?.listChanged) {
|
|
33548
|
+
this._setupListChangedHandler("prompts", PromptListChangedNotificationSchema, config3.prompts, async () => {
|
|
33549
33549
|
const result = await this.listPrompts();
|
|
33550
33550
|
return result.prompts;
|
|
33551
33551
|
});
|
|
33552
33552
|
}
|
|
33553
|
-
if (
|
|
33554
|
-
this._setupListChangedHandler("resources", ResourceListChangedNotificationSchema,
|
|
33553
|
+
if (config3.resources && this._serverCapabilities?.resources?.listChanged) {
|
|
33554
|
+
this._setupListChangedHandler("resources", ResourceListChangedNotificationSchema, config3.resources, async () => {
|
|
33555
33555
|
const result = await this.listResources();
|
|
33556
33556
|
return result.resources;
|
|
33557
33557
|
});
|
|
@@ -34881,12 +34881,12 @@ var CR = 13;
|
|
|
34881
34881
|
var SPACE = 32;
|
|
34882
34882
|
function noop(_arg) {
|
|
34883
34883
|
}
|
|
34884
|
-
function createParser(
|
|
34885
|
-
if (typeof
|
|
34884
|
+
function createParser(config3) {
|
|
34885
|
+
if (typeof config3 == "function")
|
|
34886
34886
|
throw new TypeError(
|
|
34887
34887
|
"`config` must be an object, got a function instead. Did you mean `createParser({onEvent: fn})`?"
|
|
34888
34888
|
);
|
|
34889
|
-
const { onEvent = noop, onError = noop, onRetry = noop, onComment, maxBufferSize } =
|
|
34889
|
+
const { onEvent = noop, onError = noop, onRetry = noop, onComment, maxBufferSize } = config3, pendingFragments = [];
|
|
34890
34890
|
let pendingFragmentsLength = 0, isFirstChunk = true, id, data = "", dataLines = 0, eventType, terminated = false;
|
|
34891
34891
|
function feed(chunk) {
|
|
34892
34892
|
if (terminated)
|
|
@@ -35505,18 +35505,18 @@ async function authorizeToolCall(tool, args, allowDestructive, approve) {
|
|
|
35505
35505
|
}
|
|
35506
35506
|
}
|
|
35507
35507
|
async function connectOnce(options) {
|
|
35508
|
-
const { config:
|
|
35508
|
+
const { config: config3, signal } = options;
|
|
35509
35509
|
const client = new Client({
|
|
35510
35510
|
name: "terminal-commands-cli",
|
|
35511
35511
|
version: "0.1.0"
|
|
35512
35512
|
});
|
|
35513
35513
|
await client.connect(
|
|
35514
35514
|
new StreamableHTTPClientTransport(
|
|
35515
|
-
new URL(
|
|
35515
|
+
new URL(config3.MACHINE_TERMINAL_LOCAL_MCP_URL)
|
|
35516
35516
|
)
|
|
35517
35517
|
);
|
|
35518
|
-
const token = options.getToken ? await options.getToken() :
|
|
35519
|
-
const socket = new wrapper_default(
|
|
35518
|
+
const token = options.getToken ? await options.getToken() : config3.MACHINE_TERMINAL_DEVICE_TOKEN;
|
|
35519
|
+
const socket = new wrapper_default(config3.MACHINE_TERMINAL_GATEWAY_URL, {
|
|
35520
35520
|
headers: { Authorization: `Bearer ${token}` }
|
|
35521
35521
|
});
|
|
35522
35522
|
const stop2 = () => socket.close(1e3, "Client stopping.");
|
|
@@ -35525,8 +35525,8 @@ async function connectOnce(options) {
|
|
|
35525
35525
|
socket.send(
|
|
35526
35526
|
JSON.stringify({
|
|
35527
35527
|
type: "hello",
|
|
35528
|
-
deviceId:
|
|
35529
|
-
name:
|
|
35528
|
+
deviceId: config3.MACHINE_TERMINAL_DEVICE_ID,
|
|
35529
|
+
name: config3.MACHINE_TERMINAL_DEVICE_NAME,
|
|
35530
35530
|
platform: `${os.platform()}-${os.arch()}`
|
|
35531
35531
|
})
|
|
35532
35532
|
);
|
|
@@ -35618,11 +35618,12 @@ var schema = external_exports.object({
|
|
|
35618
35618
|
MACHINE_TERMINAL_DEVICE_ID: external_exports.string().min(1).max(128),
|
|
35619
35619
|
MACHINE_TERMINAL_DEVICE_NAME: external_exports.string().min(1).max(128).default(os2.hostname()),
|
|
35620
35620
|
MACHINE_TERMINAL_LOCAL_MCP_URL: external_exports.string().url().default("http://127.0.0.1:3333/mcp"),
|
|
35621
|
-
MACHINE_TERMINAL_AGENT_ALLOW_DESTRUCTIVE: external_exports.enum(["0", "1"]).default("0")
|
|
35621
|
+
MACHINE_TERMINAL_AGENT_ALLOW_DESTRUCTIVE: external_exports.enum(["0", "1"]).default("0"),
|
|
35622
|
+
MACHINE_TERMINAL_AUTO_APPROVE: external_exports.enum(["0", "1"]).default("0")
|
|
35622
35623
|
});
|
|
35623
35624
|
function loadAgentConfig(environment = process.env) {
|
|
35624
|
-
const
|
|
35625
|
-
const gateway = new URL(
|
|
35625
|
+
const config3 = schema.parse(environment);
|
|
35626
|
+
const gateway = new URL(config3.MACHINE_TERMINAL_GATEWAY_URL);
|
|
35626
35627
|
if (!(/* @__PURE__ */ new Set(["wss:", "ws:"])).has(gateway.protocol)) {
|
|
35627
35628
|
throw new Error("MACHINE_TERMINAL_GATEWAY_URL must use wss:// or ws://.");
|
|
35628
35629
|
}
|
|
@@ -35631,7 +35632,84 @@ function loadAgentConfig(environment = process.env) {
|
|
|
35631
35632
|
"Unencrypted ws:// is allowed only for loopback development."
|
|
35632
35633
|
);
|
|
35633
35634
|
}
|
|
35634
|
-
return
|
|
35635
|
+
return {
|
|
35636
|
+
...config3,
|
|
35637
|
+
MACHINE_TERMINAL_AGENT_ALLOW_DESTRUCTIVE: config3.MACHINE_TERMINAL_AUTO_APPROVE === "1" ? "1" : config3.MACHINE_TERMINAL_AGENT_ALLOW_DESTRUCTIVE
|
|
35638
|
+
};
|
|
35639
|
+
}
|
|
35640
|
+
|
|
35641
|
+
// src/approval.ts
|
|
35642
|
+
import { createInterface } from "node:readline/promises";
|
|
35643
|
+
|
|
35644
|
+
// src/cli-format.ts
|
|
35645
|
+
var isColorEnabled = Boolean(process.stdout.isTTY) && process.env.NO_COLOR === void 0 && process.env.TERM !== "dumb";
|
|
35646
|
+
var CODES = {
|
|
35647
|
+
reset: "\x1B[0m",
|
|
35648
|
+
bold: "\x1B[1m",
|
|
35649
|
+
dim: "\x1B[2m",
|
|
35650
|
+
red: "\x1B[31m",
|
|
35651
|
+
green: "\x1B[32m",
|
|
35652
|
+
yellow: "\x1B[33m",
|
|
35653
|
+
blue: "\x1B[34m",
|
|
35654
|
+
cyan: "\x1B[36m",
|
|
35655
|
+
gray: "\x1B[90m"
|
|
35656
|
+
};
|
|
35657
|
+
function paint(code, text) {
|
|
35658
|
+
if (!isColorEnabled) return text;
|
|
35659
|
+
return `${CODES[code]}${text}${CODES.reset}`;
|
|
35660
|
+
}
|
|
35661
|
+
var bold = (text) => paint("bold", text);
|
|
35662
|
+
var dim = (text) => paint("dim", text);
|
|
35663
|
+
var warn = (text) => paint("yellow", text);
|
|
35664
|
+
|
|
35665
|
+
// src/approval.ts
|
|
35666
|
+
function approvalSummary(tool, args) {
|
|
35667
|
+
if (tool === "write_text_file") {
|
|
35668
|
+
const bytes = Buffer.byteLength(String(args.contents ?? ""));
|
|
35669
|
+
return `Write ${bytes} bytes to ${String(args.path ?? "an unknown path")}${args.overwrite ? " (overwrite)" : ""}`;
|
|
35670
|
+
}
|
|
35671
|
+
if (tool === "run_program") {
|
|
35672
|
+
const command = [
|
|
35673
|
+
String(args.program ?? ""),
|
|
35674
|
+
...Array.isArray(args.args) ? args.args.map(String) : []
|
|
35675
|
+
];
|
|
35676
|
+
return `Run: ${command.join(" ")}`;
|
|
35677
|
+
}
|
|
35678
|
+
return `Run destructive tool: ${tool}`;
|
|
35679
|
+
}
|
|
35680
|
+
function createAutoApprover() {
|
|
35681
|
+
return (tool, args) => {
|
|
35682
|
+
console.log(
|
|
35683
|
+
`
|
|
35684
|
+
${warn("Approval requested:")} ${approvalSummary(tool, args)} ${dim("(auto-approved)")}`
|
|
35685
|
+
);
|
|
35686
|
+
return Promise.resolve(true);
|
|
35687
|
+
};
|
|
35688
|
+
}
|
|
35689
|
+
function createTerminalApprover() {
|
|
35690
|
+
let queue = Promise.resolve(false);
|
|
35691
|
+
return (tool, args) => {
|
|
35692
|
+
queue = queue.catch(() => false).then(async () => {
|
|
35693
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) return false;
|
|
35694
|
+
const prompt = createInterface({
|
|
35695
|
+
input: process.stdin,
|
|
35696
|
+
output: process.stdout
|
|
35697
|
+
});
|
|
35698
|
+
try {
|
|
35699
|
+
console.log(
|
|
35700
|
+
`
|
|
35701
|
+
${warn("Approval requested:")} ${approvalSummary(tool, args)}`
|
|
35702
|
+
);
|
|
35703
|
+
const answer = await prompt.question(
|
|
35704
|
+
`${bold("Approve this action?")} [y/N] `
|
|
35705
|
+
);
|
|
35706
|
+
return (/* @__PURE__ */ new Set(["y", "yes"])).has(answer.trim().toLowerCase());
|
|
35707
|
+
} finally {
|
|
35708
|
+
prompt.close();
|
|
35709
|
+
}
|
|
35710
|
+
});
|
|
35711
|
+
return queue;
|
|
35712
|
+
};
|
|
35635
35713
|
}
|
|
35636
35714
|
|
|
35637
35715
|
// src/agent.ts
|
|
@@ -35639,8 +35717,10 @@ var controller = new AbortController();
|
|
|
35639
35717
|
var stop = () => controller.abort();
|
|
35640
35718
|
process.on("SIGINT", stop);
|
|
35641
35719
|
process.on("SIGTERM", stop);
|
|
35720
|
+
var config2 = loadAgentConfig();
|
|
35642
35721
|
void runAgent({
|
|
35643
|
-
config:
|
|
35722
|
+
config: config2,
|
|
35723
|
+
approve: config2.MACHINE_TERMINAL_AUTO_APPROVE === "1" ? createAutoApprover() : createTerminalApprover(),
|
|
35644
35724
|
signal: controller.signal,
|
|
35645
35725
|
log: (message) => console.error(`Terminal Commands: ${message}`)
|
|
35646
35726
|
}).catch((error62) => {
|
package/dist/cli.js
CHANGED
|
@@ -10984,7 +10984,6 @@ import { spawn } from "node:child_process";
|
|
|
10984
10984
|
import { fileURLToPath } from "node:url";
|
|
10985
10985
|
import os5 from "node:os";
|
|
10986
10986
|
import path3 from "node:path";
|
|
10987
|
-
import { createInterface } from "node:readline/promises";
|
|
10988
10987
|
|
|
10989
10988
|
// src/agent-core.ts
|
|
10990
10989
|
import os from "node:os";
|
|
@@ -35626,7 +35625,8 @@ var schema = external_exports.object({
|
|
|
35626
35625
|
MACHINE_TERMINAL_DEVICE_ID: external_exports.string().min(1).max(128),
|
|
35627
35626
|
MACHINE_TERMINAL_DEVICE_NAME: external_exports.string().min(1).max(128).default(os2.hostname()),
|
|
35628
35627
|
MACHINE_TERMINAL_LOCAL_MCP_URL: external_exports.string().url().default("http://127.0.0.1:3333/mcp"),
|
|
35629
|
-
MACHINE_TERMINAL_AGENT_ALLOW_DESTRUCTIVE: external_exports.enum(["0", "1"]).default("0")
|
|
35628
|
+
MACHINE_TERMINAL_AGENT_ALLOW_DESTRUCTIVE: external_exports.enum(["0", "1"]).default("0"),
|
|
35629
|
+
MACHINE_TERMINAL_AUTO_APPROVE: external_exports.enum(["0", "1"]).default("0")
|
|
35630
35630
|
});
|
|
35631
35631
|
function loadAgentConfig(environment = process.env) {
|
|
35632
35632
|
const config2 = schema.parse(environment);
|
|
@@ -35639,7 +35639,100 @@ function loadAgentConfig(environment = process.env) {
|
|
|
35639
35639
|
"Unencrypted ws:// is allowed only for loopback development."
|
|
35640
35640
|
);
|
|
35641
35641
|
}
|
|
35642
|
-
return
|
|
35642
|
+
return {
|
|
35643
|
+
...config2,
|
|
35644
|
+
MACHINE_TERMINAL_AGENT_ALLOW_DESTRUCTIVE: config2.MACHINE_TERMINAL_AUTO_APPROVE === "1" ? "1" : config2.MACHINE_TERMINAL_AGENT_ALLOW_DESTRUCTIVE
|
|
35645
|
+
};
|
|
35646
|
+
}
|
|
35647
|
+
|
|
35648
|
+
// src/approval.ts
|
|
35649
|
+
import { createInterface } from "node:readline/promises";
|
|
35650
|
+
|
|
35651
|
+
// src/cli-format.ts
|
|
35652
|
+
var isColorEnabled = Boolean(process.stdout.isTTY) && process.env.NO_COLOR === void 0 && process.env.TERM !== "dumb";
|
|
35653
|
+
var CODES = {
|
|
35654
|
+
reset: "\x1B[0m",
|
|
35655
|
+
bold: "\x1B[1m",
|
|
35656
|
+
dim: "\x1B[2m",
|
|
35657
|
+
red: "\x1B[31m",
|
|
35658
|
+
green: "\x1B[32m",
|
|
35659
|
+
yellow: "\x1B[33m",
|
|
35660
|
+
blue: "\x1B[34m",
|
|
35661
|
+
cyan: "\x1B[36m",
|
|
35662
|
+
gray: "\x1B[90m"
|
|
35663
|
+
};
|
|
35664
|
+
function paint(code, text) {
|
|
35665
|
+
if (!isColorEnabled) return text;
|
|
35666
|
+
return `${CODES[code]}${text}${CODES.reset}`;
|
|
35667
|
+
}
|
|
35668
|
+
var bold = (text) => paint("bold", text);
|
|
35669
|
+
var dim = (text) => paint("dim", text);
|
|
35670
|
+
var ok = (text) => paint("green", text);
|
|
35671
|
+
var warn = (text) => paint("yellow", text);
|
|
35672
|
+
var danger = (text) => paint("red", text);
|
|
35673
|
+
var info = (text) => paint("cyan", text);
|
|
35674
|
+
var muted = (text) => paint("gray", text);
|
|
35675
|
+
function heading(text) {
|
|
35676
|
+
const rule = "\u2500".repeat(Math.max(text.length, 3));
|
|
35677
|
+
return `${bold(info(text))}
|
|
35678
|
+
${muted(rule)}`;
|
|
35679
|
+
}
|
|
35680
|
+
function keyValueBlock(rows) {
|
|
35681
|
+
const width = rows.reduce(
|
|
35682
|
+
(widest, [label]) => Math.max(widest, label.length),
|
|
35683
|
+
0
|
|
35684
|
+
);
|
|
35685
|
+
return rows.map(([label, value]) => ` ${dim(label.padEnd(width))} ${value}`).join("\n");
|
|
35686
|
+
}
|
|
35687
|
+
|
|
35688
|
+
// src/approval.ts
|
|
35689
|
+
function approvalSummary(tool, args) {
|
|
35690
|
+
if (tool === "write_text_file") {
|
|
35691
|
+
const bytes = Buffer.byteLength(String(args.contents ?? ""));
|
|
35692
|
+
return `Write ${bytes} bytes to ${String(args.path ?? "an unknown path")}${args.overwrite ? " (overwrite)" : ""}`;
|
|
35693
|
+
}
|
|
35694
|
+
if (tool === "run_program") {
|
|
35695
|
+
const command = [
|
|
35696
|
+
String(args.program ?? ""),
|
|
35697
|
+
...Array.isArray(args.args) ? args.args.map(String) : []
|
|
35698
|
+
];
|
|
35699
|
+
return `Run: ${command.join(" ")}`;
|
|
35700
|
+
}
|
|
35701
|
+
return `Run destructive tool: ${tool}`;
|
|
35702
|
+
}
|
|
35703
|
+
function createAutoApprover() {
|
|
35704
|
+
return (tool, args) => {
|
|
35705
|
+
console.log(
|
|
35706
|
+
`
|
|
35707
|
+
${warn("Approval requested:")} ${approvalSummary(tool, args)} ${dim("(auto-approved)")}`
|
|
35708
|
+
);
|
|
35709
|
+
return Promise.resolve(true);
|
|
35710
|
+
};
|
|
35711
|
+
}
|
|
35712
|
+
function createTerminalApprover() {
|
|
35713
|
+
let queue = Promise.resolve(false);
|
|
35714
|
+
return (tool, args) => {
|
|
35715
|
+
queue = queue.catch(() => false).then(async () => {
|
|
35716
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) return false;
|
|
35717
|
+
const prompt = createInterface({
|
|
35718
|
+
input: process.stdin,
|
|
35719
|
+
output: process.stdout
|
|
35720
|
+
});
|
|
35721
|
+
try {
|
|
35722
|
+
console.log(
|
|
35723
|
+
`
|
|
35724
|
+
${warn("Approval requested:")} ${approvalSummary(tool, args)}`
|
|
35725
|
+
);
|
|
35726
|
+
const answer = await prompt.question(
|
|
35727
|
+
`${bold("Approve this action?")} [y/N] `
|
|
35728
|
+
);
|
|
35729
|
+
return (/* @__PURE__ */ new Set(["y", "yes"])).has(answer.trim().toLowerCase());
|
|
35730
|
+
} finally {
|
|
35731
|
+
prompt.close();
|
|
35732
|
+
}
|
|
35733
|
+
});
|
|
35734
|
+
return queue;
|
|
35735
|
+
};
|
|
35643
35736
|
}
|
|
35644
35737
|
|
|
35645
35738
|
// src/cli-options.ts
|
|
@@ -36021,43 +36114,6 @@ function renderHelp(command = "help") {
|
|
|
36021
36114
|
return lines.join("\n");
|
|
36022
36115
|
}
|
|
36023
36116
|
|
|
36024
|
-
// src/cli-format.ts
|
|
36025
|
-
var isColorEnabled = Boolean(process.stdout.isTTY) && process.env.NO_COLOR === void 0 && process.env.TERM !== "dumb";
|
|
36026
|
-
var CODES = {
|
|
36027
|
-
reset: "\x1B[0m",
|
|
36028
|
-
bold: "\x1B[1m",
|
|
36029
|
-
dim: "\x1B[2m",
|
|
36030
|
-
red: "\x1B[31m",
|
|
36031
|
-
green: "\x1B[32m",
|
|
36032
|
-
yellow: "\x1B[33m",
|
|
36033
|
-
blue: "\x1B[34m",
|
|
36034
|
-
cyan: "\x1B[36m",
|
|
36035
|
-
gray: "\x1B[90m"
|
|
36036
|
-
};
|
|
36037
|
-
function paint(code, text) {
|
|
36038
|
-
if (!isColorEnabled) return text;
|
|
36039
|
-
return `${CODES[code]}${text}${CODES.reset}`;
|
|
36040
|
-
}
|
|
36041
|
-
var bold = (text) => paint("bold", text);
|
|
36042
|
-
var dim = (text) => paint("dim", text);
|
|
36043
|
-
var ok = (text) => paint("green", text);
|
|
36044
|
-
var warn = (text) => paint("yellow", text);
|
|
36045
|
-
var danger = (text) => paint("red", text);
|
|
36046
|
-
var info = (text) => paint("cyan", text);
|
|
36047
|
-
var muted = (text) => paint("gray", text);
|
|
36048
|
-
function heading(text) {
|
|
36049
|
-
const rule = "\u2500".repeat(Math.max(text.length, 3));
|
|
36050
|
-
return `${bold(info(text))}
|
|
36051
|
-
${muted(rule)}`;
|
|
36052
|
-
}
|
|
36053
|
-
function keyValueBlock(rows) {
|
|
36054
|
-
const width = rows.reduce(
|
|
36055
|
-
(widest, [label]) => Math.max(widest, label.length),
|
|
36056
|
-
0
|
|
36057
|
-
);
|
|
36058
|
-
return rows.map(([label, value]) => ` ${dim(label.padEnd(width))} ${value}`).join("\n");
|
|
36059
|
-
}
|
|
36060
|
-
|
|
36061
36117
|
// src/credential-store.ts
|
|
36062
36118
|
import { promises as fs2 } from "node:fs";
|
|
36063
36119
|
import { randomUUID } from "node:crypto";
|
|
@@ -36347,54 +36403,6 @@ function describeSettings(settings, deviceId) {
|
|
|
36347
36403
|
])
|
|
36348
36404
|
].join("\n");
|
|
36349
36405
|
}
|
|
36350
|
-
function approvalSummary(tool, args) {
|
|
36351
|
-
if (tool === "write_text_file") {
|
|
36352
|
-
const bytes = Buffer.byteLength(String(args.contents ?? ""));
|
|
36353
|
-
return `Write ${bytes} bytes to ${String(args.path ?? "an unknown path")}${args.overwrite ? " (overwrite)" : ""}`;
|
|
36354
|
-
}
|
|
36355
|
-
if (tool === "run_program") {
|
|
36356
|
-
const command = [
|
|
36357
|
-
String(args.program ?? ""),
|
|
36358
|
-
...Array.isArray(args.args) ? args.args.map(String) : []
|
|
36359
|
-
];
|
|
36360
|
-
return `Run: ${command.join(" ")}`;
|
|
36361
|
-
}
|
|
36362
|
-
return `Run destructive tool: ${tool}`;
|
|
36363
|
-
}
|
|
36364
|
-
function createAutoApprover() {
|
|
36365
|
-
return (tool, args) => {
|
|
36366
|
-
console.log(
|
|
36367
|
-
`
|
|
36368
|
-
${warn("Approval requested:")} ${approvalSummary(tool, args)} ${dim("(auto-approved)")}`
|
|
36369
|
-
);
|
|
36370
|
-
return Promise.resolve(true);
|
|
36371
|
-
};
|
|
36372
|
-
}
|
|
36373
|
-
function createTerminalApprover() {
|
|
36374
|
-
let queue = Promise.resolve(false);
|
|
36375
|
-
return (tool, args) => {
|
|
36376
|
-
queue = queue.catch(() => false).then(async () => {
|
|
36377
|
-
if (!process.stdin.isTTY || !process.stdout.isTTY) return false;
|
|
36378
|
-
const prompt = createInterface({
|
|
36379
|
-
input: process.stdin,
|
|
36380
|
-
output: process.stdout
|
|
36381
|
-
});
|
|
36382
|
-
try {
|
|
36383
|
-
console.log(
|
|
36384
|
-
`
|
|
36385
|
-
${warn("Approval requested:")} ${approvalSummary(tool, args)}`
|
|
36386
|
-
);
|
|
36387
|
-
const answer = await prompt.question(
|
|
36388
|
-
`${bold("Approve this action?")} [y/N] `
|
|
36389
|
-
);
|
|
36390
|
-
return (/* @__PURE__ */ new Set(["y", "yes"])).has(answer.trim().toLowerCase());
|
|
36391
|
-
} finally {
|
|
36392
|
-
prompt.close();
|
|
36393
|
-
}
|
|
36394
|
-
});
|
|
36395
|
-
return queue;
|
|
36396
|
-
};
|
|
36397
|
-
}
|
|
36398
36406
|
async function login(environment) {
|
|
36399
36407
|
const auth2 = resolveAuth(environment);
|
|
36400
36408
|
const code = await requestDeviceCode(auth2);
|
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;
|
|
@@ -62620,6 +62620,27 @@ async function runProgram(options) {
|
|
|
62620
62620
|
}
|
|
62621
62621
|
}
|
|
62622
62622
|
|
|
62623
|
+
// src/file-reader.ts
|
|
62624
|
+
import { open as open2 } from "node:fs/promises";
|
|
62625
|
+
async function readBoundedTextFile(filename, maxCharacters) {
|
|
62626
|
+
const handle = await open2(filename, "r");
|
|
62627
|
+
try {
|
|
62628
|
+
const stats = await handle.stat();
|
|
62629
|
+
const buffer = Buffer.allocUnsafe(maxCharacters * 4 + 4);
|
|
62630
|
+
const { bytesRead } = await handle.read(buffer, 0, buffer.length, 0);
|
|
62631
|
+
const hasUnreadBytes = bytesRead < stats.size;
|
|
62632
|
+
const decoded = new TextDecoder().decode(buffer.subarray(0, bytesRead), {
|
|
62633
|
+
stream: hasUnreadBytes
|
|
62634
|
+
});
|
|
62635
|
+
return {
|
|
62636
|
+
contents: decoded.slice(0, maxCharacters),
|
|
62637
|
+
truncated: hasUnreadBytes || decoded.length > maxCharacters
|
|
62638
|
+
};
|
|
62639
|
+
} finally {
|
|
62640
|
+
await handle.close();
|
|
62641
|
+
}
|
|
62642
|
+
}
|
|
62643
|
+
|
|
62623
62644
|
// src/server.ts
|
|
62624
62645
|
var roots = configuredRoots();
|
|
62625
62646
|
function textResult(value) {
|
|
@@ -62717,11 +62738,14 @@ function createServer() {
|
|
|
62717
62738
|
},
|
|
62718
62739
|
async ({ path: requestedPath, maxCharacters }) => {
|
|
62719
62740
|
const filename = await resolveReadablePath(requestedPath, roots);
|
|
62720
|
-
const contents = await
|
|
62741
|
+
const { contents, truncated } = await readBoundedTextFile(
|
|
62742
|
+
filename,
|
|
62743
|
+
maxCharacters
|
|
62744
|
+
);
|
|
62721
62745
|
return textResult({
|
|
62722
62746
|
path: filename,
|
|
62723
|
-
contents
|
|
62724
|
-
truncated
|
|
62747
|
+
contents,
|
|
62748
|
+
truncated
|
|
62725
62749
|
});
|
|
62726
62750
|
}
|
|
62727
62751
|
);
|
package/dist/stdio.js
CHANGED
|
@@ -28505,7 +28505,7 @@ var StdioServerTransport = class {
|
|
|
28505
28505
|
};
|
|
28506
28506
|
|
|
28507
28507
|
// src/server.ts
|
|
28508
|
-
import {
|
|
28508
|
+
import { readdir, writeFile } from "node:fs/promises";
|
|
28509
28509
|
|
|
28510
28510
|
// node_modules/zod/v3/helpers/util.js
|
|
28511
28511
|
var util;
|
|
@@ -36601,6 +36601,27 @@ async function runProgram(options) {
|
|
|
36601
36601
|
}
|
|
36602
36602
|
}
|
|
36603
36603
|
|
|
36604
|
+
// src/file-reader.ts
|
|
36605
|
+
import { open as open2 } from "node:fs/promises";
|
|
36606
|
+
async function readBoundedTextFile(filename, maxCharacters) {
|
|
36607
|
+
const handle = await open2(filename, "r");
|
|
36608
|
+
try {
|
|
36609
|
+
const stats = await handle.stat();
|
|
36610
|
+
const buffer = Buffer.allocUnsafe(maxCharacters * 4 + 4);
|
|
36611
|
+
const { bytesRead } = await handle.read(buffer, 0, buffer.length, 0);
|
|
36612
|
+
const hasUnreadBytes = bytesRead < stats.size;
|
|
36613
|
+
const decoded = new TextDecoder().decode(buffer.subarray(0, bytesRead), {
|
|
36614
|
+
stream: hasUnreadBytes
|
|
36615
|
+
});
|
|
36616
|
+
return {
|
|
36617
|
+
contents: decoded.slice(0, maxCharacters),
|
|
36618
|
+
truncated: hasUnreadBytes || decoded.length > maxCharacters
|
|
36619
|
+
};
|
|
36620
|
+
} finally {
|
|
36621
|
+
await handle.close();
|
|
36622
|
+
}
|
|
36623
|
+
}
|
|
36624
|
+
|
|
36604
36625
|
// src/server.ts
|
|
36605
36626
|
var roots = configuredRoots();
|
|
36606
36627
|
function textResult(value) {
|
|
@@ -36698,11 +36719,14 @@ function createServer() {
|
|
|
36698
36719
|
},
|
|
36699
36720
|
async ({ path: requestedPath, maxCharacters }) => {
|
|
36700
36721
|
const filename = await resolveReadablePath(requestedPath, roots);
|
|
36701
|
-
const contents = await
|
|
36722
|
+
const { contents, truncated } = await readBoundedTextFile(
|
|
36723
|
+
filename,
|
|
36724
|
+
maxCharacters
|
|
36725
|
+
);
|
|
36702
36726
|
return textResult({
|
|
36703
36727
|
path: filename,
|
|
36704
|
-
contents
|
|
36705
|
-
truncated
|
|
36728
|
+
contents,
|
|
36729
|
+
truncated
|
|
36706
36730
|
});
|
|
36707
36731
|
}
|
|
36708
36732
|
);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "terminal-commands",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "A cross-platform, approval-gated terminal companion for ChatGPT, Claude, Codex, and any MCP-compatible AI client.",
|
|
@@ -10,6 +10,10 @@
|
|
|
10
10
|
"bin": {
|
|
11
11
|
"terminal-commands": "dist/cli.js"
|
|
12
12
|
},
|
|
13
|
+
"files": [
|
|
14
|
+
"dist",
|
|
15
|
+
"README.md"
|
|
16
|
+
],
|
|
13
17
|
"scripts": {
|
|
14
18
|
"build": "esbuild src/index.ts src/stdio.ts src/agent.ts src/cli.ts --bundle --platform=node --format=esm --target=node20 --outdir=dist --banner:js=\"import { createRequire as __createRequire } from 'module'; const require = __createRequire(import.meta.url);\"",
|
|
15
19
|
"check": "tsc --noEmit",
|
|
@@ -19,7 +23,8 @@
|
|
|
19
23
|
"cli": "node dist/cli.js",
|
|
20
24
|
"cli:dev": "tsx src/cli.ts",
|
|
21
25
|
"test": "tsx --test test/**/*.test.ts",
|
|
22
|
-
"test:smoke": "node test/smoke.mjs"
|
|
26
|
+
"test:smoke": "node test/smoke.mjs",
|
|
27
|
+
"prepack": "npm run build"
|
|
23
28
|
},
|
|
24
29
|
"dependencies": {
|
|
25
30
|
"@modelcontextprotocol/sdk": "1.30.0",
|
package/src/agent-config.ts
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import os from "node:os";
|
|
2
|
-
|
|
3
|
-
import { z } from "zod";
|
|
4
|
-
|
|
5
|
-
const schema = z.object({
|
|
6
|
-
MACHINE_TERMINAL_GATEWAY_URL: z.string().url(),
|
|
7
|
-
MACHINE_TERMINAL_DEVICE_TOKEN: z.string().min(1),
|
|
8
|
-
MACHINE_TERMINAL_DEVICE_ID: z.string().min(1).max(128),
|
|
9
|
-
MACHINE_TERMINAL_DEVICE_NAME: z
|
|
10
|
-
.string()
|
|
11
|
-
.min(1)
|
|
12
|
-
.max(128)
|
|
13
|
-
.default(os.hostname()),
|
|
14
|
-
MACHINE_TERMINAL_LOCAL_MCP_URL: z
|
|
15
|
-
.string()
|
|
16
|
-
.url()
|
|
17
|
-
.default("http://127.0.0.1:3333/mcp"),
|
|
18
|
-
MACHINE_TERMINAL_AGENT_ALLOW_DESTRUCTIVE: z.enum(["0", "1"]).default("0"),
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
export type AgentConfig = z.infer<typeof schema>;
|
|
22
|
-
|
|
23
|
-
export function loadAgentConfig(
|
|
24
|
-
environment: NodeJS.ProcessEnv = process.env,
|
|
25
|
-
): AgentConfig {
|
|
26
|
-
const config = schema.parse(environment);
|
|
27
|
-
const gateway = new URL(config.MACHINE_TERMINAL_GATEWAY_URL);
|
|
28
|
-
if (!new Set(["wss:", "ws:"]).has(gateway.protocol)) {
|
|
29
|
-
throw new Error("MACHINE_TERMINAL_GATEWAY_URL must use wss:// or ws://.");
|
|
30
|
-
}
|
|
31
|
-
if (
|
|
32
|
-
gateway.protocol === "ws:" &&
|
|
33
|
-
!new Set(["127.0.0.1", "localhost", "::1"]).has(gateway.hostname)
|
|
34
|
-
) {
|
|
35
|
-
throw new Error(
|
|
36
|
-
"Unencrypted ws:// is allowed only for loopback development.",
|
|
37
|
-
);
|
|
38
|
-
}
|
|
39
|
-
return config;
|
|
40
|
-
}
|