@agent-commons/cli 0.5.0 → 0.6.1
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 +22 -0
- package/dist/bin.js +993 -300
- package/package.json +2 -2
package/dist/bin.js
CHANGED
|
@@ -24,7 +24,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
24
24
|
));
|
|
25
25
|
|
|
26
26
|
// src/bin.ts
|
|
27
|
-
var
|
|
27
|
+
var import_commander24 = require("commander");
|
|
28
28
|
var import_child_process3 = require("child_process");
|
|
29
29
|
|
|
30
30
|
// src/commands/login.ts
|
|
@@ -178,7 +178,7 @@ var sym = {
|
|
|
178
178
|
bullet: import_chalk.default.dim("\u2022"),
|
|
179
179
|
dot: import_chalk.default.dim("\xB7")
|
|
180
180
|
};
|
|
181
|
-
function banner(version = "0.
|
|
181
|
+
function banner(version = "0.6.1") {
|
|
182
182
|
console.log("");
|
|
183
183
|
console.log(import_chalk.default.cyan(" \u25C7"));
|
|
184
184
|
console.log(import_chalk.default.cyan(" \u256D\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256E"));
|
|
@@ -664,11 +664,11 @@ function agentsCommand() {
|
|
|
664
664
|
process.exit(1);
|
|
665
665
|
}
|
|
666
666
|
});
|
|
667
|
-
cmd.command("get <agentId>").description("Show details for an agent").option("--json", "Output as JSON").action(async (
|
|
667
|
+
cmd.command("get <agentId>").description("Show details for an agent").option("--json", "Output as JSON").action(async (agentId3, opts) => {
|
|
668
668
|
const spinner = spin("Fetching agent\u2026");
|
|
669
669
|
try {
|
|
670
670
|
const client = makeClient();
|
|
671
|
-
const res = await client.agents.get(
|
|
671
|
+
const res = await client.agents.get(agentId3);
|
|
672
672
|
const agent = res?.data ?? res;
|
|
673
673
|
spinner.stop();
|
|
674
674
|
if (opts.json) return jsonOut(agent);
|
|
@@ -747,10 +747,10 @@ ${sym.ok} Agent created`);
|
|
|
747
747
|
}
|
|
748
748
|
});
|
|
749
749
|
const runtime = cmd.command("runtime").description("Manage an agent runtime");
|
|
750
|
-
runtime.command("status <agentId>").description("Show managed runtime status and capabilities").option("--json", "Output as JSON").action(async (
|
|
750
|
+
runtime.command("status <agentId>").description("Show managed runtime status and capabilities").option("--json", "Output as JSON").action(async (agentId3, opts) => {
|
|
751
751
|
const spinner = spin("Fetching runtime status\u2026");
|
|
752
752
|
try {
|
|
753
|
-
const result = await makeClient().agents.getRuntime(
|
|
753
|
+
const result = await makeClient().agents.getRuntime(agentId3);
|
|
754
754
|
spinner.stop();
|
|
755
755
|
if (opts.json) return jsonOut(result.data);
|
|
756
756
|
detail([
|
|
@@ -768,13 +768,13 @@ ${sym.ok} Agent created`);
|
|
|
768
768
|
for (const action of ["deploy", "restart", "sleep"]) {
|
|
769
769
|
runtime.command(`${action} <agentId>`).description(
|
|
770
770
|
`${action[0].toUpperCase()}${action.slice(1)} the managed agent runtime`
|
|
771
|
-
).action(async (
|
|
771
|
+
).action(async (agentId3) => {
|
|
772
772
|
const spinner = spin(
|
|
773
773
|
`${action[0].toUpperCase()}${action.slice(1)}ing runtime\u2026`
|
|
774
774
|
);
|
|
775
775
|
try {
|
|
776
776
|
const client = makeClient();
|
|
777
|
-
const result = action === "deploy" ? await client.agents.deployRuntime(
|
|
777
|
+
const result = action === "deploy" ? await client.agents.deployRuntime(agentId3) : action === "restart" ? await client.agents.restartRuntime(agentId3) : await client.agents.sleepRuntime(agentId3);
|
|
778
778
|
spinner.stop();
|
|
779
779
|
console.log(`
|
|
780
780
|
${sym.ok} Runtime ${result.data.status}`);
|
|
@@ -887,12 +887,12 @@ function sessionsCommand() {
|
|
|
887
887
|
const spinner = spin("Fetching sessions\u2026");
|
|
888
888
|
try {
|
|
889
889
|
const client = makeClient();
|
|
890
|
-
const
|
|
891
|
-
const res =
|
|
890
|
+
const agentId3 = opts.agent;
|
|
891
|
+
const res = agentId3 ? await client.sessions.list(agentId3, cfg.initiator) : await client.sessions.listByUser(cfg.initiator);
|
|
892
892
|
const sessions = res?.data ?? res ?? [];
|
|
893
893
|
spinner.stop();
|
|
894
894
|
if (opts.json) return jsonOut(sessions);
|
|
895
|
-
section(`Sessions (${sessions.length})${
|
|
895
|
+
section(`Sessions (${sessions.length})${agentId3 ? ` \u2014 agent ${agentId3.slice(0, 8)}\u2026` : " \u2014 all agents"}`);
|
|
896
896
|
table(
|
|
897
897
|
sessions.map((s) => ({
|
|
898
898
|
ID: s.sessionId.slice(0, 8) + "\u2026",
|
|
@@ -933,8 +933,8 @@ function sessionsCommand() {
|
|
|
933
933
|
});
|
|
934
934
|
cmd.command("create").description("Create a new session").option("--agent <agentId>", "Agent ID").option("--title <title>", "Session title").option("--model <id>", "Model ID (e.g. gpt-5.4-mini, claude-sonnet-4-6)").option("--provider <provider>", "Model provider").option("--json", "Output as JSON").action(async (opts) => {
|
|
935
935
|
const cfg = loadConfig();
|
|
936
|
-
const
|
|
937
|
-
if (!
|
|
936
|
+
const agentId3 = opts.agent ?? cfg.defaultAgentId;
|
|
937
|
+
if (!agentId3) {
|
|
938
938
|
console.error(c.error("Specify --agent <agentId> or set defaultAgentId"));
|
|
939
939
|
process.exit(1);
|
|
940
940
|
}
|
|
@@ -946,7 +946,7 @@ function sessionsCommand() {
|
|
|
946
946
|
try {
|
|
947
947
|
const client = makeClient();
|
|
948
948
|
const res = await client.sessions.create({
|
|
949
|
-
agentId:
|
|
949
|
+
agentId: agentId3,
|
|
950
950
|
initiator: cfg.initiator,
|
|
951
951
|
title: opts.title,
|
|
952
952
|
...opts.model && { model: { modelId: opts.model, provider: opts.provider } }
|
|
@@ -1102,8 +1102,8 @@ ${sym.ok} Tool created`);
|
|
|
1102
1102
|
});
|
|
1103
1103
|
cmd.command("exec <toolName>").description("Execute a tool directly by name").option("--agent <agentId>", "Agent context for tool execution").option("--args <json>", "Tool arguments as JSON object", "{}").option("--json", "Output result as JSON").action(async (toolName, opts) => {
|
|
1104
1104
|
const cfg = loadConfig();
|
|
1105
|
-
const
|
|
1106
|
-
if (!
|
|
1105
|
+
const agentId3 = opts.agent ?? cfg.defaultAgentId;
|
|
1106
|
+
if (!agentId3) {
|
|
1107
1107
|
console.error(c.error("Specify --agent <agentId> or set defaultAgentId with `agc config set defaultAgentId <id>`"));
|
|
1108
1108
|
process.exit(1);
|
|
1109
1109
|
}
|
|
@@ -1119,7 +1119,7 @@ ${sym.ok} Tool created`);
|
|
|
1119
1119
|
try {
|
|
1120
1120
|
const client = makeClient();
|
|
1121
1121
|
const result = await client.run.once({
|
|
1122
|
-
agentId:
|
|
1122
|
+
agentId: agentId3,
|
|
1123
1123
|
messages: [{ role: "user", content: prompt }],
|
|
1124
1124
|
...cfg.initiator && { initiatorId: cfg.initiator }
|
|
1125
1125
|
});
|
|
@@ -1448,8 +1448,8 @@ ${sym.ok} Workflow created`);
|
|
|
1448
1448
|
}
|
|
1449
1449
|
const templateName = templateNameRaw;
|
|
1450
1450
|
const needsAgent = templateName === "agent-research-summary" || templateName === "multi-agent-field-report";
|
|
1451
|
-
const
|
|
1452
|
-
if (needsAgent && !
|
|
1451
|
+
const agentId3 = opts.agent ?? cfg.defaultAgentId;
|
|
1452
|
+
if (needsAgent && !agentId3) {
|
|
1453
1453
|
console.error(c.error("This template requires --agent <agentId> or a configured defaultAgentId."));
|
|
1454
1454
|
process.exit(1);
|
|
1455
1455
|
}
|
|
@@ -1473,7 +1473,7 @@ ${sym.ok} Workflow created`);
|
|
|
1473
1473
|
const ctx = {
|
|
1474
1474
|
ownerId: cfg.initiator,
|
|
1475
1475
|
prefix,
|
|
1476
|
-
agentId:
|
|
1476
|
+
agentId: agentId3,
|
|
1477
1477
|
reviewerAgentId: opts.reviewerAgent,
|
|
1478
1478
|
childWorkflowId
|
|
1479
1479
|
};
|
|
@@ -1493,7 +1493,7 @@ ${sym.ok} Workflow created`);
|
|
|
1493
1493
|
}
|
|
1494
1494
|
}
|
|
1495
1495
|
execution = await makeClient().workflows.execute(result.workflow.workflowId, {
|
|
1496
|
-
agentId:
|
|
1496
|
+
agentId: agentId3,
|
|
1497
1497
|
inputData,
|
|
1498
1498
|
userId: cfg.initiator
|
|
1499
1499
|
});
|
|
@@ -1564,7 +1564,7 @@ ${sym.ok} Execution started: ${c.id(execution.executionId)}`);
|
|
|
1564
1564
|
});
|
|
1565
1565
|
cmd.command("run <workflowId>").description("Execute a workflow").option("--agent <agentId>", "Agent context").option("--session <sessionId>", "Session context").option("--input <json>", "Input data as JSON string", "{}").option("--watch", "Stream execution progress via SSE").option("--json", "Output result as JSON").action(async (workflowId, opts) => {
|
|
1566
1566
|
const cfg = loadConfig();
|
|
1567
|
-
const
|
|
1567
|
+
const agentId3 = opts.agent ?? cfg.defaultAgentId;
|
|
1568
1568
|
let inputData = {};
|
|
1569
1569
|
try {
|
|
1570
1570
|
inputData = JSON.parse(opts.input);
|
|
@@ -1576,7 +1576,7 @@ ${sym.ok} Execution started: ${c.id(execution.executionId)}`);
|
|
|
1576
1576
|
try {
|
|
1577
1577
|
const client = makeClient();
|
|
1578
1578
|
const execution = await client.workflows.execute(workflowId, {
|
|
1579
|
-
agentId:
|
|
1579
|
+
agentId: agentId3,
|
|
1580
1580
|
sessionId: opts.session,
|
|
1581
1581
|
inputData
|
|
1582
1582
|
});
|
|
@@ -1721,12 +1721,12 @@ function taskCommand() {
|
|
|
1721
1721
|
const cmd = new import_commander7.Command("task").description("Manage and execute tasks").alias("t");
|
|
1722
1722
|
cmd.command("list").description("List tasks").option("--agent <agentId>", "Filter by agent ID").option("--session <sessionId>", "Filter by session ID").option("--json", "Output as JSON").action(async (opts) => {
|
|
1723
1723
|
const cfg = loadConfig();
|
|
1724
|
-
const
|
|
1724
|
+
const agentId3 = opts.agent ?? cfg.defaultAgentId;
|
|
1725
1725
|
const spinner = spin("Fetching tasks\u2026");
|
|
1726
1726
|
try {
|
|
1727
1727
|
const client = makeClient();
|
|
1728
1728
|
const filter = {};
|
|
1729
|
-
if (
|
|
1729
|
+
if (agentId3) filter.agentId = agentId3;
|
|
1730
1730
|
if (opts.session) filter.sessionId = opts.session;
|
|
1731
1731
|
if (cfg.initiator) {
|
|
1732
1732
|
filter.ownerId = cfg.initiator;
|
|
@@ -1782,8 +1782,8 @@ function taskCommand() {
|
|
|
1782
1782
|
});
|
|
1783
1783
|
cmd.command("create").description("Create a new task").requiredOption("--title <title>", "Task title").option("--agent <agentId>", "Agent ID").option("--session <sessionId>", "Session ID").option("--workflow <workflowId>", "Workflow ID to attach").option("--input <json>", "Input data as JSON", "{}").option("--timeout <ms>", "Execution timeout in milliseconds").option("--execute", "Execute immediately after creation").option("--watch", "Stream execution progress (implies --execute)").option("--json", "Output as JSON").action(async (opts) => {
|
|
1784
1784
|
const cfg = loadConfig();
|
|
1785
|
-
const
|
|
1786
|
-
if (!
|
|
1785
|
+
const agentId3 = opts.agent ?? cfg.defaultAgentId;
|
|
1786
|
+
if (!agentId3) {
|
|
1787
1787
|
console.error(c.error("Specify --agent <agentId> or set defaultAgentId with `agc config set defaultAgentId <id>`"));
|
|
1788
1788
|
process.exit(1);
|
|
1789
1789
|
}
|
|
@@ -1799,7 +1799,7 @@ function taskCommand() {
|
|
|
1799
1799
|
const client = makeClient();
|
|
1800
1800
|
const res = await client.tasks.create({
|
|
1801
1801
|
title: opts.title,
|
|
1802
|
-
agentId:
|
|
1802
|
+
agentId: agentId3,
|
|
1803
1803
|
sessionId: opts.session,
|
|
1804
1804
|
workflowId: opts.workflow,
|
|
1805
1805
|
inputData,
|
|
@@ -1905,14 +1905,237 @@ ${sym.fail} ${c.error(event.message ?? event.type)}`);
|
|
|
1905
1905
|
// src/commands/run.ts
|
|
1906
1906
|
var import_commander8 = require("commander");
|
|
1907
1907
|
var readline2 = __toESM(require("readline"));
|
|
1908
|
+
var import_fs6 = require("fs");
|
|
1909
|
+
var import_path4 = require("path");
|
|
1908
1910
|
|
|
1909
1911
|
// src/local-tools.ts
|
|
1910
1912
|
var import_fs5 = require("fs");
|
|
1911
1913
|
var import_path3 = require("path");
|
|
1912
1914
|
var import_child_process2 = require("child_process");
|
|
1915
|
+
var import_crypto = require("crypto");
|
|
1913
1916
|
var readline = __toESM(require("readline"));
|
|
1917
|
+
|
|
1918
|
+
// src/edit-preview.ts
|
|
1919
|
+
function editPreview(before, after, limit = 12e3) {
|
|
1920
|
+
if (before === after) return "(no content change)";
|
|
1921
|
+
const oldLines = before ? before.split("\n") : [];
|
|
1922
|
+
const newLines = after ? after.split("\n") : [];
|
|
1923
|
+
let start = 0;
|
|
1924
|
+
while (start < oldLines.length && start < newLines.length && oldLines[start] === newLines[start]) start++;
|
|
1925
|
+
let oldEnd = oldLines.length, newEnd = newLines.length;
|
|
1926
|
+
while (oldEnd > start && newEnd > start && oldLines[oldEnd - 1] === newLines[newEnd - 1]) {
|
|
1927
|
+
oldEnd--;
|
|
1928
|
+
newEnd--;
|
|
1929
|
+
}
|
|
1930
|
+
const lines = [
|
|
1931
|
+
`@@ -${start + 1},${oldEnd - start} +${start + 1},${newEnd - start} @@`,
|
|
1932
|
+
...oldLines.slice(Math.max(0, start - 3), start).map((line) => ` ${line}`),
|
|
1933
|
+
...oldLines.slice(start, oldEnd).map((line) => `-${line}`),
|
|
1934
|
+
...newLines.slice(start, newEnd).map((line) => `+${line}`),
|
|
1935
|
+
...newLines.slice(newEnd, newEnd + 3).map((line) => ` ${line}`)
|
|
1936
|
+
];
|
|
1937
|
+
const preview = lines.join("\n").replace(/[\x00-\x08\x0b-\x1f\x7f-\x9f]/g, "?");
|
|
1938
|
+
return preview.length > limit ? preview.slice(0, limit) + "\n\u2026 (preview truncated)" : preview;
|
|
1939
|
+
}
|
|
1940
|
+
|
|
1941
|
+
// src/disk-usage.ts
|
|
1942
|
+
var import_node_fs = require("fs");
|
|
1943
|
+
var import_node_path = require("path");
|
|
1944
|
+
async function scanDiskUsage(root, signal, allowPath) {
|
|
1945
|
+
if (!(await import_node_fs.promises.lstat(root)).isDirectory()) throw new Error("disk_usage requires a directory");
|
|
1946
|
+
const deadline = Date.now() + 15e3;
|
|
1947
|
+
const limit = 5e4;
|
|
1948
|
+
let scanned = 0;
|
|
1949
|
+
let skipped = 0;
|
|
1950
|
+
let incomplete = false;
|
|
1951
|
+
const measure = async (start) => {
|
|
1952
|
+
let bytes = 0;
|
|
1953
|
+
const pending = [start];
|
|
1954
|
+
while (pending.length) {
|
|
1955
|
+
if (signal?.aborted) throw new Error("Disk usage scan cancelled");
|
|
1956
|
+
if (scanned >= limit || Date.now() >= deadline) {
|
|
1957
|
+
incomplete = true;
|
|
1958
|
+
break;
|
|
1959
|
+
}
|
|
1960
|
+
const path = pending.pop();
|
|
1961
|
+
try {
|
|
1962
|
+
allowPath(path);
|
|
1963
|
+
const item = await import_node_fs.promises.lstat(path);
|
|
1964
|
+
scanned += 1;
|
|
1965
|
+
if (item.isSymbolicLink()) {
|
|
1966
|
+
skipped += 1;
|
|
1967
|
+
continue;
|
|
1968
|
+
}
|
|
1969
|
+
if (item.isDirectory()) {
|
|
1970
|
+
for await (const child of await import_node_fs.promises.opendir(path)) {
|
|
1971
|
+
if (pending.length + scanned >= limit || Date.now() >= deadline) {
|
|
1972
|
+
incomplete = true;
|
|
1973
|
+
break;
|
|
1974
|
+
}
|
|
1975
|
+
pending.push((0, import_node_path.join)(path, child.name));
|
|
1976
|
+
}
|
|
1977
|
+
} else if (item.isFile()) bytes += item.size;
|
|
1978
|
+
} catch {
|
|
1979
|
+
skipped += 1;
|
|
1980
|
+
}
|
|
1981
|
+
}
|
|
1982
|
+
return bytes;
|
|
1983
|
+
};
|
|
1984
|
+
const entries = [];
|
|
1985
|
+
for await (const child of await import_node_fs.promises.opendir(root)) {
|
|
1986
|
+
if (incomplete) break;
|
|
1987
|
+
if (child.isSymbolicLink()) {
|
|
1988
|
+
skipped += 1;
|
|
1989
|
+
continue;
|
|
1990
|
+
}
|
|
1991
|
+
const path = (0, import_node_path.join)(root, child.name);
|
|
1992
|
+
try {
|
|
1993
|
+
allowPath(path);
|
|
1994
|
+
} catch {
|
|
1995
|
+
skipped += 1;
|
|
1996
|
+
continue;
|
|
1997
|
+
}
|
|
1998
|
+
entries.push({ path: child.name, kind: child.isDirectory() ? "directory" : "file", sizeBytes: await measure(path) });
|
|
1999
|
+
}
|
|
2000
|
+
entries.sort((left, right) => right.sizeBytes - left.sizeBytes);
|
|
2001
|
+
return {
|
|
2002
|
+
scope: root,
|
|
2003
|
+
unit: "logical file bytes",
|
|
2004
|
+
entries: entries.slice(0, 30),
|
|
2005
|
+
scanned,
|
|
2006
|
+
skipped,
|
|
2007
|
+
incomplete,
|
|
2008
|
+
note: incomplete ? "The bounded scan stopped early. Sizes may be underestimates; narrow the path and scan again." : "Sizes cover this selected folder only, not the whole computer."
|
|
2009
|
+
};
|
|
2010
|
+
}
|
|
2011
|
+
|
|
2012
|
+
// src/office-text.ts
|
|
2013
|
+
var import_zlib = require("zlib");
|
|
2014
|
+
var MAX_ENTRY_BYTES = 4e7;
|
|
2015
|
+
function readEntries(buffer) {
|
|
2016
|
+
const floor = Math.max(0, buffer.length - 65557);
|
|
2017
|
+
let end = -1;
|
|
2018
|
+
for (let index = buffer.length - 22; index >= floor; index -= 1) {
|
|
2019
|
+
if (buffer.readUInt32LE(index) === 101010256) {
|
|
2020
|
+
end = index;
|
|
2021
|
+
break;
|
|
2022
|
+
}
|
|
2023
|
+
}
|
|
2024
|
+
if (end < 0) throw new Error("Not a zip archive");
|
|
2025
|
+
const count = buffer.readUInt16LE(end + 10);
|
|
2026
|
+
let cursor = buffer.readUInt32LE(end + 16);
|
|
2027
|
+
const entries = [];
|
|
2028
|
+
for (let index = 0; index < count && cursor + 46 <= buffer.length; index += 1) {
|
|
2029
|
+
if (buffer.readUInt32LE(cursor) !== 33639248) break;
|
|
2030
|
+
const method = buffer.readUInt16LE(cursor + 10);
|
|
2031
|
+
const compressedSize = buffer.readUInt32LE(cursor + 20);
|
|
2032
|
+
const nameLength = buffer.readUInt16LE(cursor + 28);
|
|
2033
|
+
const extraLength = buffer.readUInt16LE(cursor + 30);
|
|
2034
|
+
const commentLength = buffer.readUInt16LE(cursor + 32);
|
|
2035
|
+
const offset = buffer.readUInt32LE(cursor + 42);
|
|
2036
|
+
const name = buffer.toString("utf8", cursor + 46, cursor + 46 + nameLength);
|
|
2037
|
+
entries.push({ name, method, compressedSize, offset });
|
|
2038
|
+
cursor += 46 + nameLength + extraLength + commentLength;
|
|
2039
|
+
}
|
|
2040
|
+
return entries;
|
|
2041
|
+
}
|
|
2042
|
+
function readEntry(buffer, entry) {
|
|
2043
|
+
if (buffer.readUInt32LE(entry.offset) !== 67324752) throw new Error("Corrupt zip entry");
|
|
2044
|
+
const nameLength = buffer.readUInt16LE(entry.offset + 26);
|
|
2045
|
+
const extraLength = buffer.readUInt16LE(entry.offset + 28);
|
|
2046
|
+
const start = entry.offset + 30 + nameLength + extraLength;
|
|
2047
|
+
const data = buffer.subarray(start, start + entry.compressedSize);
|
|
2048
|
+
if (entry.method === 0) return data.toString("utf8");
|
|
2049
|
+
if (entry.method === 8) {
|
|
2050
|
+
return (0, import_zlib.inflateRawSync)(data, { maxOutputLength: MAX_ENTRY_BYTES }).toString("utf8");
|
|
2051
|
+
}
|
|
2052
|
+
throw new Error(`Unsupported zip compression method ${entry.method}`);
|
|
2053
|
+
}
|
|
2054
|
+
function decodeXml(value) {
|
|
2055
|
+
return value.replace(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/'/g, "'").replace(/&#(\d+);/g, (_, code) => String.fromCodePoint(Number(code))).replace(/&#x([0-9a-f]+);/gi, (_, code) => String.fromCodePoint(parseInt(code, 16))).replace(/&/g, "&");
|
|
2056
|
+
}
|
|
2057
|
+
function docxText(xml) {
|
|
2058
|
+
const paragraphs = xml.match(/<w:p[\s>][\s\S]*?<\/w:p>/g) ?? [];
|
|
2059
|
+
const lines = paragraphs.map((paragraph) => {
|
|
2060
|
+
const style = /<w:pStyle w:val="([^"]+)"/.exec(paragraph)?.[1] ?? "";
|
|
2061
|
+
const heading = /^(?:Heading|heading)\s?(\d)$/.exec(style)?.[1] ?? (style === "Title" ? "1" : "");
|
|
2062
|
+
const listItem = /<w:numPr>/.test(paragraph);
|
|
2063
|
+
const text = paragraph.replace(/<w:tab\/>/g, " ").replace(/<w:(?:br|cr)\/>/g, "\n").match(/<w:t(?:\s[^>]*)?>[\s\S]*?<\/w:t>|\t|\n/g)?.map((part) => part === " " || part === "\n" ? part : decodeXml(part.replace(/<[^>]+>/g, ""))).join("") ?? "";
|
|
2064
|
+
if (!text.trim()) return "";
|
|
2065
|
+
if (heading) return `${"#".repeat(Math.min(6, Number(heading)))} ${text.trim()}`;
|
|
2066
|
+
return listItem ? `- ${text.trim()}` : text;
|
|
2067
|
+
});
|
|
2068
|
+
return lines.join("\n").replace(/\n{3,}/g, "\n\n").trim();
|
|
2069
|
+
}
|
|
2070
|
+
function drawingText(xml) {
|
|
2071
|
+
const paragraphs = xml.match(/<a:p[\s>][\s\S]*?<\/a:p>/g) ?? [];
|
|
2072
|
+
return paragraphs.map(
|
|
2073
|
+
(paragraph) => (paragraph.match(/<a:t>[\s\S]*?<\/a:t>/g) ?? []).map((part) => decodeXml(part.replace(/<[^>]+>/g, ""))).join("")
|
|
2074
|
+
).filter((line) => line.trim()).join("\n");
|
|
2075
|
+
}
|
|
2076
|
+
function slideNumber(name) {
|
|
2077
|
+
return Number(/(\d+)\.xml$/.exec(name)?.[1] ?? 0);
|
|
2078
|
+
}
|
|
2079
|
+
function extractOfficeOpenXmlText(buffer, extension) {
|
|
2080
|
+
let entries;
|
|
2081
|
+
try {
|
|
2082
|
+
entries = readEntries(buffer);
|
|
2083
|
+
} catch {
|
|
2084
|
+
return null;
|
|
2085
|
+
}
|
|
2086
|
+
const find = (name) => entries.find((entry) => entry.name === name);
|
|
2087
|
+
try {
|
|
2088
|
+
if (extension === ".docx") {
|
|
2089
|
+
const document = find("word/document.xml");
|
|
2090
|
+
return document ? docxText(readEntry(buffer, document)) : null;
|
|
2091
|
+
}
|
|
2092
|
+
if (extension === ".pptx") {
|
|
2093
|
+
const slides = entries.filter((entry) => /^ppt\/slides\/slide\d+\.xml$/.test(entry.name)).sort((left, right) => slideNumber(left.name) - slideNumber(right.name));
|
|
2094
|
+
if (!slides.length) return null;
|
|
2095
|
+
return slides.map((slide) => `## Slide ${slideNumber(slide.name)}
|
|
2096
|
+
${drawingText(readEntry(buffer, slide))}`).join("\n\n").trim();
|
|
2097
|
+
}
|
|
2098
|
+
if (extension === ".xlsx") {
|
|
2099
|
+
const shared = find("xl/sharedStrings.xml");
|
|
2100
|
+
const strings = shared ? (readEntry(buffer, shared).match(/<si>[\s\S]*?<\/si>/g) ?? []).map(
|
|
2101
|
+
(item) => (item.match(/<t(?:\s[^>]*)?>[\s\S]*?<\/t>/g) ?? []).map((part) => decodeXml(part.replace(/<[^>]+>/g, ""))).join("")
|
|
2102
|
+
) : [];
|
|
2103
|
+
const sheets = entries.filter((entry) => /^xl\/worksheets\/sheet\d+\.xml$/.test(entry.name)).sort((left, right) => slideNumber(left.name) - slideNumber(right.name));
|
|
2104
|
+
if (!sheets.length) return null;
|
|
2105
|
+
return sheets.map((sheet) => {
|
|
2106
|
+
const rows = (readEntry(buffer, sheet).match(/<row[\s\S]*?<\/row>/g) ?? []).slice(0, 2e3).map(
|
|
2107
|
+
(row) => (row.match(/<c[\s\S]*?<\/c>|<c[^>]*\/>/g) ?? []).map((cell) => {
|
|
2108
|
+
const value = /<v>([\s\S]*?)<\/v>/.exec(cell)?.[1] ?? /<t(?:\s[^>]*)?>([\s\S]*?)<\/t>/.exec(cell)?.[1] ?? "";
|
|
2109
|
+
return /\bt="s"/.test(cell) ? strings[Number(value)] ?? "" : decodeXml(value);
|
|
2110
|
+
}).join(" ")
|
|
2111
|
+
);
|
|
2112
|
+
return `## Sheet ${slideNumber(sheet.name)}
|
|
2113
|
+
${rows.join("\n")}`;
|
|
2114
|
+
}).join("\n\n").trim();
|
|
2115
|
+
}
|
|
2116
|
+
} catch {
|
|
2117
|
+
return null;
|
|
2118
|
+
}
|
|
2119
|
+
return null;
|
|
2120
|
+
}
|
|
2121
|
+
|
|
2122
|
+
// src/local-tools.ts
|
|
1914
2123
|
var pdfParse = require("pdf-parse/lib/pdf-parse.js");
|
|
1915
2124
|
var managedProcesses = /* @__PURE__ */ new Map();
|
|
2125
|
+
function stopProcessTree(proc) {
|
|
2126
|
+
const pid = proc.child.pid;
|
|
2127
|
+
if (!pid) return;
|
|
2128
|
+
if (process.platform === "win32") {
|
|
2129
|
+
const killer = (0, import_child_process2.spawn)("taskkill", ["/PID", String(pid), "/T", "/F"], { stdio: "ignore", windowsHide: true });
|
|
2130
|
+
killer.on("error", () => proc.child.kill("SIGTERM"));
|
|
2131
|
+
} else {
|
|
2132
|
+
try {
|
|
2133
|
+
process.kill(-pid, "SIGTERM");
|
|
2134
|
+
} catch {
|
|
2135
|
+
proc.child.kill("SIGTERM");
|
|
2136
|
+
}
|
|
2137
|
+
}
|
|
2138
|
+
}
|
|
1916
2139
|
function capBuffer(existing, chunk, maxBytes) {
|
|
1917
2140
|
const joined = existing + chunk;
|
|
1918
2141
|
if (joined.length <= maxBytes) return joined;
|
|
@@ -1949,12 +2172,8 @@ function buildDirSnapshot(dir, maxDepth = 2) {
|
|
|
1949
2172
|
}
|
|
1950
2173
|
function readFileForContext(rootDir, filePath) {
|
|
1951
2174
|
try {
|
|
1952
|
-
const abs = (
|
|
1953
|
-
|
|
1954
|
-
if (rel.startsWith("..") || rel.startsWith("/")) return `[error: path escapes session root]`;
|
|
1955
|
-
for (const pat of [/\/\.ssh\//, /\/\.aws\//, /\/\.env$/, /\/\.env\./, /id_rsa/, /id_ed25519/]) {
|
|
1956
|
-
if (pat.test(abs)) return `[error: sensitive path blocked]`;
|
|
1957
|
-
}
|
|
2175
|
+
const abs = safePath(rootDir, filePath);
|
|
2176
|
+
assertNotSensitive(abs);
|
|
1958
2177
|
if (!(0, import_fs5.existsSync)(abs)) return `[error: file not found: ${filePath}]`;
|
|
1959
2178
|
const stat = (0, import_fs5.statSync)(abs);
|
|
1960
2179
|
if (stat.isDirectory()) return `[error: "${filePath}" is a directory \u2014 use list_directory]`;
|
|
@@ -1999,9 +2218,11 @@ ${fileSection}
|
|
|
1999
2218
|
| Tool | What it does |
|
|
2000
2219
|
|------|-------------|
|
|
2001
2220
|
| \`cli_list_directory\` | List files and folders at a path |
|
|
2002
|
-
| \`cli_read_file\` | Read a file (PDF and Word docs are extracted to text) |
|
|
2221
|
+
| \`cli_read_file\` | Read a bounded part of a file (PDF and Word docs are extracted to text) |
|
|
2222
|
+
| \`cli_search_file\` | Find passages and character offsets inside one large document |
|
|
2003
2223
|
| \`cli_write_file\` | Write or overwrite a file (user confirmation required) |
|
|
2004
2224
|
| \`cli_search_files\` | Find files matching a pattern |
|
|
2225
|
+
| \`cli_disk_usage\` | Measure and rank file and folder sizes inside the selected root (read only; bounded scan) |
|
|
2005
2226
|
| \`cli_run_command\` | Run a short command and return its output (user confirmation required) |
|
|
2006
2227
|
| \`cli_start_process\` | Start a long-running command in the background; returns a processId immediately |
|
|
2007
2228
|
| \`cli_wait_for_process\` | Block up to N seconds for a background process, then return current output |
|
|
@@ -2060,102 +2281,50 @@ function extractToolCall(text) {
|
|
|
2060
2281
|
}
|
|
2061
2282
|
return null;
|
|
2062
2283
|
}
|
|
2063
|
-
function injectAgcTrailer(command, args,
|
|
2284
|
+
function injectAgcTrailer(command, args, agentId3, agentName) {
|
|
2064
2285
|
if (command !== "git") return args;
|
|
2065
2286
|
if (!args.some((a) => a === "commit")) return args;
|
|
2066
2287
|
if (args.some((a) => a.includes("Co-Authored-By: agc"))) return args;
|
|
2067
|
-
const identity = agentName ? `${agentName} (agc)` :
|
|
2288
|
+
const identity = agentName ? `${agentName} (agc)` : agentId3 ? `agc/${agentId3}` : "agc agent";
|
|
2068
2289
|
return [...args, "--trailer", `Co-Authored-By: ${identity} <agc-agent@users.noreply.github.com>`];
|
|
2069
2290
|
}
|
|
2070
|
-
var AGC_HOOK_MARKER = "# agc-session:";
|
|
2071
|
-
var HOOK_BACKUP_SUFFIX = ".agc-backup";
|
|
2072
|
-
function findGitDir(rootDir) {
|
|
2073
|
-
const gitPath = (0, import_path3.join)(rootDir, ".git");
|
|
2074
|
-
if (!(0, import_fs5.existsSync)(gitPath)) return null;
|
|
2075
|
-
const s = (0, import_fs5.statSync)(gitPath);
|
|
2076
|
-
if (s.isDirectory()) return gitPath;
|
|
2077
|
-
if (s.isFile()) {
|
|
2078
|
-
const content = (0, import_fs5.readFileSync)(gitPath, "utf8");
|
|
2079
|
-
const match = content.match(/^gitdir:\s*(.+)$/m);
|
|
2080
|
-
if (match) return match[1].trim();
|
|
2081
|
-
}
|
|
2082
|
-
return null;
|
|
2083
|
-
}
|
|
2084
|
-
function installGitHook(rootDir, sessionId, agentId2, agentName) {
|
|
2085
|
-
const gitDir = findGitDir(rootDir);
|
|
2086
|
-
if (!gitDir) return;
|
|
2087
|
-
const hooksDir = (0, import_path3.join)(gitDir, "hooks");
|
|
2088
|
-
(0, import_fs5.mkdirSync)(hooksDir, { recursive: true });
|
|
2089
|
-
const hookPath = (0, import_path3.join)(hooksDir, "prepare-commit-msg");
|
|
2090
|
-
if ((0, import_fs5.existsSync)(hookPath)) {
|
|
2091
|
-
const existing = (0, import_fs5.readFileSync)(hookPath, "utf8");
|
|
2092
|
-
if (!existing.includes(AGC_HOOK_MARKER)) {
|
|
2093
|
-
(0, import_fs5.writeFileSync)(hookPath + HOOK_BACKUP_SUFFIX, existing, { mode: 493 });
|
|
2094
|
-
}
|
|
2095
|
-
}
|
|
2096
|
-
const identity = agentName ? `${agentName} (agc)` : agentId2 ? `agc/${agentId2}` : "agc agent";
|
|
2097
|
-
const trailer = `Co-Authored-By: ${identity} <agc-agent@users.noreply.github.com>`;
|
|
2098
|
-
const chainLine = (0, import_fs5.existsSync)(hookPath + HOOK_BACKUP_SUFFIX) ? `
|
|
2099
|
-
# chain pre-existing hook
|
|
2100
|
-
"$(dirname "$0")/prepare-commit-msg${HOOK_BACKUP_SUFFIX}" "$@" 2>/dev/null || true
|
|
2101
|
-
` : "";
|
|
2102
|
-
const hook = `#!/bin/sh
|
|
2103
|
-
${AGC_HOOK_MARKER}${sessionId}
|
|
2104
|
-
COMMIT_MSG_FILE="$1"
|
|
2105
|
-
COMMIT_SOURCE="$2"
|
|
2106
|
-
${chainLine}
|
|
2107
|
-
case "$COMMIT_SOURCE" in merge|squash) exit 0 ;; esac
|
|
2108
|
-
TRAILER="${trailer}"
|
|
2109
|
-
grep -qF "$TRAILER" "$COMMIT_MSG_FILE" 2>/dev/null && exit 0
|
|
2110
|
-
printf '\\n%s\\n' "$TRAILER" >> "$COMMIT_MSG_FILE"
|
|
2111
|
-
`;
|
|
2112
|
-
(0, import_fs5.writeFileSync)(hookPath, hook, { mode: 493 });
|
|
2113
|
-
}
|
|
2114
|
-
function removeGitHook(rootDir) {
|
|
2115
|
-
const gitDir = findGitDir(rootDir);
|
|
2116
|
-
if (!gitDir) return;
|
|
2117
|
-
const hookPath = (0, import_path3.join)(gitDir, "hooks", "prepare-commit-msg");
|
|
2118
|
-
if (!(0, import_fs5.existsSync)(hookPath)) return;
|
|
2119
|
-
const content = (0, import_fs5.readFileSync)(hookPath, "utf8");
|
|
2120
|
-
if (!content.includes(AGC_HOOK_MARKER)) return;
|
|
2121
|
-
const backupPath = hookPath + HOOK_BACKUP_SUFFIX;
|
|
2122
|
-
if ((0, import_fs5.existsSync)(backupPath)) {
|
|
2123
|
-
(0, import_fs5.writeFileSync)(hookPath, (0, import_fs5.readFileSync)(backupPath, "utf8"), { mode: 493 });
|
|
2124
|
-
(0, import_fs5.unlinkSync)(backupPath);
|
|
2125
|
-
} else {
|
|
2126
|
-
(0, import_fs5.unlinkSync)(hookPath);
|
|
2127
|
-
}
|
|
2128
|
-
}
|
|
2129
2291
|
function safePath(root, userPath) {
|
|
2130
|
-
const
|
|
2131
|
-
const
|
|
2132
|
-
|
|
2133
|
-
|
|
2292
|
+
const canonicalRoot = (0, import_fs5.realpathSync)(root);
|
|
2293
|
+
const abs = (0, import_path3.resolve)(canonicalRoot, userPath);
|
|
2294
|
+
const inside = (path) => {
|
|
2295
|
+
const rel = (0, import_path3.relative)(canonicalRoot, path);
|
|
2296
|
+
return rel !== ".." && !rel.startsWith(`..${import_path3.sep}`) && !(0, import_path3.isAbsolute)(rel);
|
|
2297
|
+
};
|
|
2298
|
+
if (!inside(abs) && !(0, import_path3.isAbsolute)(userPath)) throw new Error(`Path "${userPath}" escapes the session root. Access denied.`);
|
|
2299
|
+
let ancestor = abs;
|
|
2300
|
+
while (!(0, import_fs5.existsSync)(ancestor)) {
|
|
2301
|
+
try {
|
|
2302
|
+
if ((0, import_fs5.lstatSync)(ancestor).isSymbolicLink()) throw new Error("Dangling symlink is not allowed.");
|
|
2303
|
+
} catch (error) {
|
|
2304
|
+
if (error.code !== "ENOENT") throw error;
|
|
2305
|
+
}
|
|
2306
|
+
const parent = (0, import_path3.dirname)(ancestor);
|
|
2307
|
+
if (parent === ancestor) throw new Error("Cannot resolve path.");
|
|
2308
|
+
ancestor = parent;
|
|
2134
2309
|
}
|
|
2135
|
-
|
|
2310
|
+
const canonicalAncestor = (0, import_fs5.realpathSync)(ancestor);
|
|
2311
|
+
if (!inside(canonicalAncestor)) throw new Error(`Path "${userPath}" resolves outside the session root. Access denied.`);
|
|
2312
|
+
assertNotSensitive(abs);
|
|
2313
|
+
assertNotSensitive(canonicalAncestor);
|
|
2314
|
+
return (0, import_path3.resolve)(canonicalAncestor, (0, import_path3.relative)(ancestor, abs));
|
|
2136
2315
|
}
|
|
2137
|
-
var BLOCKED_PATTERNS = [
|
|
2138
|
-
/\/\.ssh\//,
|
|
2139
|
-
/\/\.gnupg\//,
|
|
2140
|
-
/\/\.agc\//,
|
|
2141
|
-
/\/\.aws\//,
|
|
2142
|
-
/\/\.env$/,
|
|
2143
|
-
/\/\.env\./,
|
|
2144
|
-
/id_rsa/,
|
|
2145
|
-
/id_ed25519/
|
|
2146
|
-
];
|
|
2147
2316
|
function assertNotSensitive(abs) {
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
}
|
|
2317
|
+
const segments = abs.split(/[\\/]/);
|
|
2318
|
+
if (segments.some((part) => /^(?:\.ssh|\.gnupg|\.agc|\.aws|\.env(?:\..*)?|id_rsa|id_ed25519)$/i.test(part))) {
|
|
2319
|
+
throw new Error(`Access to "${abs}" is blocked for security reasons.`);
|
|
2152
2320
|
}
|
|
2153
2321
|
}
|
|
2154
2322
|
async function confirm(message, config, permissionKey) {
|
|
2155
|
-
if (config.autoApprove) return true;
|
|
2156
2323
|
const cached = config.permissions.get(permissionKey);
|
|
2157
2324
|
if (cached === "allow") return true;
|
|
2158
2325
|
if (cached === "deny") return false;
|
|
2326
|
+
if (config.autoApprove) return true;
|
|
2327
|
+
if (config.confirm) return config.confirm(message, permissionKey);
|
|
2159
2328
|
return new Promise((resolve2) => {
|
|
2160
2329
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
2161
2330
|
process.stdout.write(
|
|
@@ -2164,22 +2333,23 @@ async function confirm(message, config, permissionKey) {
|
|
|
2164
2333
|
\x1B[2m[y] Yes [n] No [A] Always allow this type [N] Never allow this type\x1B[0m
|
|
2165
2334
|
\x1B[36m?\x1B[0m `
|
|
2166
2335
|
);
|
|
2336
|
+
rl.once("close", () => resolve2(false));
|
|
2167
2337
|
rl.once("line", (answer) => {
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
if (a === "a") {
|
|
2338
|
+
const a = answer.trim();
|
|
2339
|
+
if (a === "A") {
|
|
2171
2340
|
config.permissions.set(permissionKey, "allow");
|
|
2172
2341
|
resolve2(true);
|
|
2173
|
-
} else if (a === "
|
|
2342
|
+
} else if (a === "N") {
|
|
2174
2343
|
config.permissions.set(permissionKey, "deny");
|
|
2175
2344
|
resolve2(false);
|
|
2176
2345
|
} else {
|
|
2177
|
-
resolve2(a === "y" || a === "yes"
|
|
2346
|
+
resolve2(a.toLowerCase() === "y" || a.toLowerCase() === "yes");
|
|
2178
2347
|
}
|
|
2348
|
+
rl.close();
|
|
2179
2349
|
});
|
|
2180
2350
|
});
|
|
2181
2351
|
}
|
|
2182
|
-
var OFFICE_EXTS = /* @__PURE__ */ new Set([".docx", ".doc", ".rtf", ".odt", ".pages"]);
|
|
2352
|
+
var OFFICE_EXTS = /* @__PURE__ */ new Set([".docx", ".doc", ".rtf", ".odt", ".pages", ".pptx", ".xlsx"]);
|
|
2183
2353
|
var PDF_EXTS = /* @__PURE__ */ new Set([".pdf"]);
|
|
2184
2354
|
var UNREADABLE_BINARY_EXTS = /* @__PURE__ */ new Set([
|
|
2185
2355
|
".png",
|
|
@@ -2211,9 +2381,7 @@ var UNREADABLE_BINARY_EXTS = /* @__PURE__ */ new Set([
|
|
|
2211
2381
|
".ai",
|
|
2212
2382
|
".sketch",
|
|
2213
2383
|
".figma",
|
|
2214
|
-
".xlsx",
|
|
2215
2384
|
".xls",
|
|
2216
|
-
".pptx",
|
|
2217
2385
|
".ppt"
|
|
2218
2386
|
]);
|
|
2219
2387
|
function extractViaCommand(cmd, cmdArgs) {
|
|
@@ -2224,17 +2392,16 @@ function extractViaCommand(cmd, cmdArgs) {
|
|
|
2224
2392
|
});
|
|
2225
2393
|
});
|
|
2226
2394
|
}
|
|
2227
|
-
async function extractPdfText(abs) {
|
|
2395
|
+
async function extractPdfText(abs, maxChars = 15e4) {
|
|
2228
2396
|
try {
|
|
2229
2397
|
const buffer = (0, import_fs5.readFileSync)(abs);
|
|
2230
2398
|
const data = await pdfParse(buffer);
|
|
2231
2399
|
const text2 = data.text?.trim();
|
|
2232
2400
|
if (text2) {
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
return text2.slice(0, MAX_CHARS) + `
|
|
2401
|
+
if (text2.length > maxChars) {
|
|
2402
|
+
return text2.slice(0, maxChars) + `
|
|
2236
2403
|
|
|
2237
|
-
[\u2026truncated \u2014 showing first ${
|
|
2404
|
+
[\u2026truncated \u2014 showing first ${maxChars.toLocaleString()} characters of ${text2.length.toLocaleString()} total]`;
|
|
2238
2405
|
}
|
|
2239
2406
|
return text2;
|
|
2240
2407
|
}
|
|
@@ -2245,47 +2412,129 @@ async function extractPdfText(abs) {
|
|
|
2245
2412
|
return `[Cannot extract PDF text: the file may be scanned/image-only or password-protected]`;
|
|
2246
2413
|
}
|
|
2247
2414
|
async function extractOfficeText(abs, ext) {
|
|
2415
|
+
const openXml = extractOfficeOpenXmlText((0, import_fs5.readFileSync)(abs), ext);
|
|
2416
|
+
if (openXml) return openXml;
|
|
2248
2417
|
const text = await extractViaCommand("textutil", ["-stdout", "-cat", "txt", abs]);
|
|
2249
2418
|
if (text) return text;
|
|
2250
|
-
return `[Cannot extract ${ext} text:
|
|
2419
|
+
return `[Cannot extract ${ext} text: this document format is not supported on this system]`;
|
|
2420
|
+
}
|
|
2421
|
+
var documentTextCache = /* @__PURE__ */ new Map();
|
|
2422
|
+
async function cachedDocumentText(abs, stat, ext) {
|
|
2423
|
+
const cached = documentTextCache.get(abs);
|
|
2424
|
+
if (cached?.size === stat.size && cached.mtimeMs === stat.mtimeMs) return cached.text;
|
|
2425
|
+
const text = PDF_EXTS.has(ext) ? await extractPdfText(abs, Number.MAX_SAFE_INTEGER) : await extractOfficeText(abs, ext);
|
|
2426
|
+
documentTextCache.delete(abs);
|
|
2427
|
+
if (text.length <= 2e6) documentTextCache.set(abs, { size: stat.size, mtimeMs: stat.mtimeMs, text });
|
|
2428
|
+
while (documentTextCache.size > 4) documentTextCache.delete(documentTextCache.keys().next().value);
|
|
2429
|
+
return text;
|
|
2251
2430
|
}
|
|
2252
2431
|
async function toolReadFile(args, cfg) {
|
|
2253
2432
|
const { path: userPath } = args;
|
|
2254
2433
|
if (!userPath) throw new Error('read_file requires a "path" argument');
|
|
2255
2434
|
const abs = safePath(cfg.rootDir, userPath);
|
|
2256
2435
|
assertNotSensitive(abs);
|
|
2257
|
-
if (!(0, import_fs5.existsSync)(abs)) throw new Error(`File not found: ${userPath}
|
|
2436
|
+
if (!(0, import_fs5.existsSync)(abs)) throw new Error(`File not found: ${userPath} (resolved to ${abs}). List the parent directory and use a path relative to the selected workspace root: ${cfg.rootDir}.`);
|
|
2258
2437
|
const stat = (0, import_fs5.statSync)(abs);
|
|
2259
2438
|
if (stat.isDirectory()) throw new Error(`"${userPath}" is a directory, not a file`);
|
|
2260
2439
|
const ext = (0, import_path3.extname)(abs).toLowerCase();
|
|
2261
2440
|
if (PDF_EXTS.has(ext)) {
|
|
2262
2441
|
if (stat.size > 5e7) throw new Error(`PDF too large to read (${Math.round(stat.size / 1e6)} MB). Max 50 MB.`);
|
|
2263
|
-
return
|
|
2442
|
+
return pageFileText(await cachedDocumentText(abs, stat, ext), args.offset, userPath, true);
|
|
2264
2443
|
}
|
|
2265
2444
|
if (OFFICE_EXTS.has(ext)) {
|
|
2266
2445
|
if (stat.size > 2e7) throw new Error(`Document too large to read (${Math.round(stat.size / 1e6)} MB). Max 20 MB.`);
|
|
2267
|
-
return
|
|
2446
|
+
return pageFileText(await cachedDocumentText(abs, stat, ext), args.offset, userPath, true);
|
|
2268
2447
|
}
|
|
2269
2448
|
if (UNREADABLE_BINARY_EXTS.has(ext)) {
|
|
2270
2449
|
throw new Error(`Cannot read binary file "${userPath}" (${ext} format). Only text, PDF, and Office documents are supported.`);
|
|
2271
2450
|
}
|
|
2272
2451
|
if (stat.size > 5e5) throw new Error(`File too large to read (${Math.round(stat.size / 1024)} KB). Max 500 KB.`);
|
|
2273
|
-
return (0, import_fs5.readFileSync)(abs, "utf8");
|
|
2452
|
+
return pageFileText((0, import_fs5.readFileSync)(abs, "utf8"), args.offset, userPath, false);
|
|
2453
|
+
}
|
|
2454
|
+
async function toolSearchFile(args, cfg) {
|
|
2455
|
+
const path = String(args.path ?? "").trim();
|
|
2456
|
+
const query = String(args.query ?? "").trim().slice(0, 120);
|
|
2457
|
+
if (!path || !query) throw new Error("search_file requires a path and search query");
|
|
2458
|
+
const abs = safePath(cfg.rootDir, path);
|
|
2459
|
+
assertNotSensitive(abs);
|
|
2460
|
+
if (!(0, import_fs5.existsSync)(abs)) throw new Error(`File not found: ${path}`);
|
|
2461
|
+
const stat = (0, import_fs5.statSync)(abs);
|
|
2462
|
+
if (!stat.isFile()) throw new Error(`"${path}" is not a file`);
|
|
2463
|
+
const ext = (0, import_path3.extname)(abs).toLowerCase();
|
|
2464
|
+
if (PDF_EXTS.has(ext) && stat.size > 5e7) throw new Error("PDF exceeds the 50 MB Local read limit");
|
|
2465
|
+
if (OFFICE_EXTS.has(ext) && stat.size > 2e7) throw new Error("Document exceeds the 20 MB Local read limit");
|
|
2466
|
+
if (UNREADABLE_BINARY_EXTS.has(ext)) throw new Error(`Cannot search binary file "${path}"`);
|
|
2467
|
+
if (!PDF_EXTS.has(ext) && !OFFICE_EXTS.has(ext) && stat.size > 5e5) throw new Error("Text file exceeds the 500 KB Local read limit");
|
|
2468
|
+
const text = PDF_EXTS.has(ext) || OFFICE_EXTS.has(ext) ? await cachedDocumentText(abs, stat, ext) : (0, import_fs5.readFileSync)(abs, "utf8");
|
|
2469
|
+
return JSON.stringify(searchTextPassages(text, path, query, "Use cli_read_file with a matching offset for more context."));
|
|
2470
|
+
}
|
|
2471
|
+
function searchTextPassages(text, path, rawQuery, hint) {
|
|
2472
|
+
const query = rawQuery.trim().slice(0, 120);
|
|
2473
|
+
if (!query) throw new Error("A search query is required");
|
|
2474
|
+
const lower = text.toLowerCase();
|
|
2475
|
+
const phrase = query.toLowerCase();
|
|
2476
|
+
const terms = [...new Set(phrase.match(/[\p{L}\p{N}]{3,}/gu) ?? [])].slice(0, 8);
|
|
2477
|
+
const positions = [];
|
|
2478
|
+
const collect2 = (needle) => {
|
|
2479
|
+
let offset = 0;
|
|
2480
|
+
while (positions.length < 5e3) {
|
|
2481
|
+
const found = lower.indexOf(needle, offset);
|
|
2482
|
+
if (found < 0) break;
|
|
2483
|
+
positions.push(found);
|
|
2484
|
+
offset = found + Math.max(needle.length, 1);
|
|
2485
|
+
}
|
|
2486
|
+
};
|
|
2487
|
+
collect2(phrase);
|
|
2488
|
+
if (!positions.length) for (const term of terms) collect2(term);
|
|
2489
|
+
const ranked = positions.map((offset) => {
|
|
2490
|
+
const start = Math.max(0, offset - 180);
|
|
2491
|
+
const end = Math.min(text.length, offset + 320);
|
|
2492
|
+
const excerpt = text.slice(start, end).replace(/\s+/g, " ").trim();
|
|
2493
|
+
const score = terms.filter((term) => excerpt.toLowerCase().includes(term)).length;
|
|
2494
|
+
return { offset, score, excerpt };
|
|
2495
|
+
}).sort((a, b) => b.score - a.score || b.offset - a.offset);
|
|
2496
|
+
const selected = [];
|
|
2497
|
+
for (const match of ranked) {
|
|
2498
|
+
if (selected.some((entry) => Math.abs(entry.offset - match.offset) < 250)) continue;
|
|
2499
|
+
selected.push(match);
|
|
2500
|
+
if (selected.length >= 8) break;
|
|
2501
|
+
}
|
|
2502
|
+
return { path, query, totalChars: text.length, matches: selected.map(({ offset, excerpt }) => ({ offset, excerpt })), hint };
|
|
2503
|
+
}
|
|
2504
|
+
function pageFileText(text, rawOffset, path, includeTail) {
|
|
2505
|
+
const offset = rawOffset === void 0 ? 0 : Number(rawOffset);
|
|
2506
|
+
if (!Number.isSafeInteger(offset) || offset < 0 || offset > text.length) throw new Error("read_file offset must be a character position within the file");
|
|
2507
|
+
if (offset === 0 && text.length <= 7e3) return text;
|
|
2508
|
+
const end = Math.min(text.length, offset + 6e3);
|
|
2509
|
+
const next = end < text.length ? ` Use cli_read_file with path ${JSON.stringify(path)} and offset ${end} to continue.` : "";
|
|
2510
|
+
const tail = includeTail && offset === 0 && end < text.length - 1500 ? `
|
|
2511
|
+
|
|
2512
|
+
[Final 1,500 characters of document]
|
|
2513
|
+
${text.slice(-1500)}` : "";
|
|
2514
|
+
return `[Showing characters ${offset}-${end} of ${text.length}.${next}]
|
|
2515
|
+
${text.slice(offset, end)}${tail}`;
|
|
2274
2516
|
}
|
|
2275
2517
|
async function toolWriteFile(args, cfg) {
|
|
2276
2518
|
const { path: userPath, content } = args;
|
|
2277
2519
|
if (!userPath) throw new Error('write_file requires a "path" argument');
|
|
2278
|
-
if (content
|
|
2520
|
+
if (typeof content !== "string") throw new Error('write_file requires a string "content" argument');
|
|
2279
2521
|
const abs = safePath(cfg.rootDir, userPath);
|
|
2280
2522
|
assertNotSensitive(abs);
|
|
2523
|
+
const before = (0, import_fs5.existsSync)(abs) ? (0, import_fs5.readFileSync)(abs, "utf8") : "";
|
|
2524
|
+
const preview = editPreview(before, content);
|
|
2281
2525
|
const ok = await confirm(
|
|
2282
|
-
`Agent wants to write file: \x1B[1m${abs}\x1B[0m (${String(content).length} chars)
|
|
2526
|
+
`Agent wants to write file: \x1B[1m${abs}\x1B[0m (${String(content).length} chars)
|
|
2527
|
+
${preview}`,
|
|
2283
2528
|
cfg,
|
|
2284
2529
|
"write_file"
|
|
2285
2530
|
);
|
|
2286
2531
|
if (!ok) return "User denied write operation.";
|
|
2287
|
-
|
|
2288
|
-
(0, import_fs5.
|
|
2532
|
+
const checked = safePath(cfg.rootDir, userPath);
|
|
2533
|
+
if (((0, import_fs5.existsSync)(checked) ? (0, import_fs5.readFileSync)(checked, "utf8") : "") !== before) {
|
|
2534
|
+
return "Error: file changed during approval. Read it again before retrying.";
|
|
2535
|
+
}
|
|
2536
|
+
(0, import_fs5.mkdirSync)((0, import_path3.dirname)(checked), { recursive: true });
|
|
2537
|
+
(0, import_fs5.writeFileSync)(checked, content, "utf8");
|
|
2289
2538
|
return `Written ${String(content).length} bytes to ${userPath}`;
|
|
2290
2539
|
}
|
|
2291
2540
|
async function toolListDirectory(args, cfg) {
|
|
@@ -2300,6 +2549,10 @@ async function toolListDirectory(args, cfg) {
|
|
|
2300
2549
|
});
|
|
2301
2550
|
return lines.join("\n") || "(empty directory)";
|
|
2302
2551
|
}
|
|
2552
|
+
async function toolDiskUsage(args, cfg) {
|
|
2553
|
+
const root = safePath(cfg.rootDir, String(args.path ?? "."));
|
|
2554
|
+
return JSON.stringify(await scanDiskUsage(root, cfg.signal, assertNotSensitive));
|
|
2555
|
+
}
|
|
2303
2556
|
async function toolSearchFiles(args, cfg) {
|
|
2304
2557
|
const { pattern, directory } = args;
|
|
2305
2558
|
if (!pattern) throw new Error('search_files requires a "pattern" argument');
|
|
@@ -2359,9 +2612,10 @@ async function toolRunCommand(args, cfg) {
|
|
|
2359
2612
|
});
|
|
2360
2613
|
}
|
|
2361
2614
|
return new Promise((resolve2) => {
|
|
2362
|
-
(0, import_child_process2.execFile)(command, injectedArgs.map(String), { cwd: workDir, timeout: timeoutMs, maxBuffer: 1024 * 1024 }, (err, stdout, stderr) => {
|
|
2615
|
+
(0, import_child_process2.execFile)(command, injectedArgs.map(String), { cwd: workDir, timeout: timeoutMs, maxBuffer: 1024 * 1024, signal: cfg.signal }, (err, stdout, stderr) => {
|
|
2363
2616
|
const out = [stdout, stderr].filter(Boolean).join("\n--- stderr ---\n");
|
|
2364
|
-
if (err
|
|
2617
|
+
if (err) return resolve2(`Error: command failed (${err.code ?? "unknown"}): ${err.message}
|
|
2618
|
+
${out}`);
|
|
2365
2619
|
resolve2(out || "(no output)");
|
|
2366
2620
|
});
|
|
2367
2621
|
});
|
|
@@ -2379,11 +2633,14 @@ async function toolStartProcess(args, cfg) {
|
|
|
2379
2633
|
"start_process"
|
|
2380
2634
|
);
|
|
2381
2635
|
if (!ok) return JSON.stringify({ error: "User denied process start." });
|
|
2382
|
-
const
|
|
2636
|
+
const finished = [...managedProcesses.values()].filter((proc2) => proc2.status !== "running");
|
|
2637
|
+
for (const proc2 of finished.slice(0, Math.max(0, finished.length - 99))) managedProcesses.delete(proc2.id);
|
|
2638
|
+
const id = `proc_${(0, import_crypto.randomUUID)()}`;
|
|
2383
2639
|
const child = (0, import_child_process2.spawn)(command, cmdArgs.map(String), {
|
|
2384
2640
|
cwd: workDir,
|
|
2385
2641
|
stdio: ["ignore", "pipe", "pipe"],
|
|
2386
|
-
detached:
|
|
2642
|
+
detached: process.platform !== "win32",
|
|
2643
|
+
windowsHide: true
|
|
2387
2644
|
});
|
|
2388
2645
|
const proc = {
|
|
2389
2646
|
id,
|
|
@@ -2396,6 +2653,10 @@ async function toolStartProcess(args, cfg) {
|
|
|
2396
2653
|
endedAt: null,
|
|
2397
2654
|
child
|
|
2398
2655
|
};
|
|
2656
|
+
if (cfg.signal) {
|
|
2657
|
+
if (cfg.signal.aborted) stopProcessTree(proc);
|
|
2658
|
+
else cfg.signal.addEventListener("abort", () => stopProcessTree(proc), { once: true });
|
|
2659
|
+
}
|
|
2399
2660
|
child.stdout?.on("data", (chunk) => {
|
|
2400
2661
|
proc.stdout = capBuffer(proc.stdout, chunk.toString(), 2e5);
|
|
2401
2662
|
});
|
|
@@ -2403,7 +2664,7 @@ async function toolStartProcess(args, cfg) {
|
|
|
2403
2664
|
proc.stderr = capBuffer(proc.stderr, chunk.toString(), 5e4);
|
|
2404
2665
|
});
|
|
2405
2666
|
child.on("close", (code) => {
|
|
2406
|
-
proc.status = code === 0 ? "done" : "error";
|
|
2667
|
+
if (proc.status !== "killed") proc.status = code === 0 ? "done" : "error";
|
|
2407
2668
|
proc.exitCode = code;
|
|
2408
2669
|
proc.endedAt = /* @__PURE__ */ new Date();
|
|
2409
2670
|
});
|
|
@@ -2443,7 +2704,8 @@ async function toolWaitForProcess(args, _cfg) {
|
|
|
2443
2704
|
const proc = managedProcesses.get(processId);
|
|
2444
2705
|
if (!proc) return JSON.stringify({ error: `No process found with id "${processId}"` });
|
|
2445
2706
|
if (proc.status !== "running") return processSnapshot(proc);
|
|
2446
|
-
const
|
|
2707
|
+
const requested = typeof wait_seconds === "number" && Number.isFinite(wait_seconds) ? wait_seconds : 60;
|
|
2708
|
+
const maxWait = Math.max(0, Math.min(requested, 60)) * 1e3;
|
|
2447
2709
|
const deadline = Date.now() + maxWait;
|
|
2448
2710
|
await new Promise((resolve2) => {
|
|
2449
2711
|
const tick = setInterval(() => {
|
|
@@ -2461,7 +2723,7 @@ async function toolKillProcess(args, cfg) {
|
|
|
2461
2723
|
const proc = managedProcesses.get(processId);
|
|
2462
2724
|
if (!proc) return JSON.stringify({ error: `No process found with id "${processId}"` });
|
|
2463
2725
|
if (proc.status !== "running") return JSON.stringify({ error: `Process "${processId}" is not running (status: ${proc.status})` });
|
|
2464
|
-
proc
|
|
2726
|
+
stopProcessTree(proc);
|
|
2465
2727
|
proc.status = "killed";
|
|
2466
2728
|
proc.endedAt = /* @__PURE__ */ new Date();
|
|
2467
2729
|
cfg.appendLog({ type: "process_killed", processId, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
@@ -2477,6 +2739,15 @@ async function toolListProcesses(_args, _cfg) {
|
|
|
2477
2739
|
}));
|
|
2478
2740
|
return JSON.stringify(list);
|
|
2479
2741
|
}
|
|
2742
|
+
function stopLocalProcesses() {
|
|
2743
|
+
for (const proc of managedProcesses.values()) {
|
|
2744
|
+
if (proc.status === "running") {
|
|
2745
|
+
stopProcessTree(proc);
|
|
2746
|
+
proc.status = "killed";
|
|
2747
|
+
proc.endedAt = /* @__PURE__ */ new Date();
|
|
2748
|
+
}
|
|
2749
|
+
}
|
|
2750
|
+
}
|
|
2480
2751
|
async function runLocalTool(call, cfg) {
|
|
2481
2752
|
const { tool, args } = call;
|
|
2482
2753
|
cfg.appendLog({
|
|
@@ -2491,6 +2762,9 @@ async function runLocalTool(call, cfg) {
|
|
|
2491
2762
|
case "read_file":
|
|
2492
2763
|
result = await toolReadFile(args, cfg);
|
|
2493
2764
|
break;
|
|
2765
|
+
case "search_file":
|
|
2766
|
+
result = await toolSearchFile(args, cfg);
|
|
2767
|
+
break;
|
|
2494
2768
|
case "write_file":
|
|
2495
2769
|
result = await toolWriteFile(args, cfg);
|
|
2496
2770
|
break;
|
|
@@ -2500,6 +2774,9 @@ async function runLocalTool(call, cfg) {
|
|
|
2500
2774
|
case "search_files":
|
|
2501
2775
|
result = await toolSearchFiles(args, cfg);
|
|
2502
2776
|
break;
|
|
2777
|
+
case "disk_usage":
|
|
2778
|
+
result = await toolDiskUsage(args, cfg);
|
|
2779
|
+
break;
|
|
2503
2780
|
case "run_command":
|
|
2504
2781
|
result = await toolRunCommand(args, cfg);
|
|
2505
2782
|
break;
|
|
@@ -2519,7 +2796,7 @@ async function runLocalTool(call, cfg) {
|
|
|
2519
2796
|
result = await toolListProcesses(args, cfg);
|
|
2520
2797
|
break;
|
|
2521
2798
|
default:
|
|
2522
|
-
result = `Unknown tool: "${tool}". Available: read_file, write_file, list_directory, search_files, run_command, start_process, wait_for_process, process_status, kill_process, list_processes`;
|
|
2799
|
+
result = `Unknown tool: "${tool}". Available: read_file, search_file, write_file, list_directory, search_files, disk_usage, run_command, start_process, wait_for_process, process_status, kill_process, list_processes`;
|
|
2523
2800
|
}
|
|
2524
2801
|
} catch (err) {
|
|
2525
2802
|
result = `Error: ${err?.message ?? String(err)}`;
|
|
@@ -2536,19 +2813,72 @@ async function runLocalTool(call, cfg) {
|
|
|
2536
2813
|
|
|
2537
2814
|
// src/commands/run.ts
|
|
2538
2815
|
function runCommand() {
|
|
2539
|
-
|
|
2816
|
+
const append = (value, previous) => [...previous, value];
|
|
2817
|
+
return new import_commander8.Command("run").description("Send a single prompt to an agent and stream the response").argument("<prompt>", "Prompt text to send").option("--agent <agentId>", "Agent ID").option("--file <path>", "Upload and attach a file (repeatable, up to 25 MB each)", append, []).option("--attach <fileId>", "Attach an existing Library file ID (repeatable)", append, []).option("--session <sessionId>", "Resume an existing session by ID").option(
|
|
2818
|
+
"--new-session",
|
|
2819
|
+
"Create a new session and print its ID for future use"
|
|
2820
|
+
).option(
|
|
2821
|
+
"--computer",
|
|
2822
|
+
"Give the agent access to its persistent cloud computer"
|
|
2823
|
+
).option(
|
|
2824
|
+
"--local",
|
|
2825
|
+
"Enable local file system access (with permission prompts)"
|
|
2826
|
+
).option(
|
|
2827
|
+
"-y, --yes",
|
|
2828
|
+
"Enable local file system access and auto-approve all operations"
|
|
2829
|
+
).option("--no-stream", "Disable streaming (wait for full response)").option("--json", "Output raw event stream as JSON lines").option(
|
|
2830
|
+
"--provenance <mode>",
|
|
2831
|
+
"Provenance capture: off, metadata, or full",
|
|
2832
|
+
"metadata"
|
|
2833
|
+
).option(
|
|
2834
|
+
"--onchain-provenance",
|
|
2835
|
+
"Request an optional on-chain anchor for this run"
|
|
2836
|
+
).action(async (prompt, opts) => {
|
|
2540
2837
|
const cfg = loadConfig();
|
|
2541
|
-
const
|
|
2542
|
-
if (!
|
|
2543
|
-
console.error(
|
|
2838
|
+
const agentId3 = opts.agent ?? cfg.defaultAgentId;
|
|
2839
|
+
if (!agentId3) {
|
|
2840
|
+
console.error(
|
|
2841
|
+
c.error(
|
|
2842
|
+
"Specify --agent <agentId> or set defaultAgentId with `agc config set defaultAgentId <id>`"
|
|
2843
|
+
)
|
|
2844
|
+
);
|
|
2544
2845
|
process.exit(1);
|
|
2545
2846
|
}
|
|
2546
2847
|
if (opts.session && opts.newSession) {
|
|
2547
|
-
console.error(
|
|
2848
|
+
console.error(
|
|
2849
|
+
c.error("Cannot use --session and --new-session together.")
|
|
2850
|
+
);
|
|
2851
|
+
process.exit(1);
|
|
2852
|
+
}
|
|
2853
|
+
if (!["off", "metadata", "full"].includes(opts.provenance)) {
|
|
2854
|
+
console.error(c.error("--provenance must be off, metadata, or full."));
|
|
2855
|
+
process.exit(1);
|
|
2856
|
+
}
|
|
2857
|
+
if (opts.onchainProvenance && opts.provenance === "off") {
|
|
2858
|
+
console.error(
|
|
2859
|
+
c.error("--onchain-provenance requires metadata or full capture.")
|
|
2860
|
+
);
|
|
2548
2861
|
process.exit(1);
|
|
2549
2862
|
}
|
|
2550
2863
|
const client = makeClient();
|
|
2551
2864
|
let sessionId = opts.session;
|
|
2865
|
+
const filePaths = opts.file;
|
|
2866
|
+
const attachmentIds = opts.attach;
|
|
2867
|
+
if (filePaths.length + attachmentIds.length > 10) {
|
|
2868
|
+
console.error(c.error("Attach no more than 10 files to a run."));
|
|
2869
|
+
process.exit(1);
|
|
2870
|
+
}
|
|
2871
|
+
try {
|
|
2872
|
+
for (const path of filePaths) {
|
|
2873
|
+
const stat = (0, import_fs6.statSync)(path);
|
|
2874
|
+
if (!stat.isFile() || stat.size < 1 || stat.size > 25 * 1024 * 1024) {
|
|
2875
|
+
throw new Error(`Choose a non-empty file up to 25 MB: ${path}`);
|
|
2876
|
+
}
|
|
2877
|
+
}
|
|
2878
|
+
} catch (error) {
|
|
2879
|
+
printError(error);
|
|
2880
|
+
process.exit(1);
|
|
2881
|
+
}
|
|
2552
2882
|
if (opts.session) {
|
|
2553
2883
|
const spinner = spin("Loading session\u2026");
|
|
2554
2884
|
try {
|
|
@@ -2564,7 +2894,7 @@ function runCommand() {
|
|
|
2564
2894
|
const spinner = spin("Creating session\u2026");
|
|
2565
2895
|
try {
|
|
2566
2896
|
const res = await client.sessions.create({
|
|
2567
|
-
agentId:
|
|
2897
|
+
agentId: agentId3,
|
|
2568
2898
|
initiator: cfg.initiator ?? "",
|
|
2569
2899
|
title: `agc run ${(/* @__PURE__ */ new Date()).toISOString().slice(0, 16)}`,
|
|
2570
2900
|
source: "cli"
|
|
@@ -2578,6 +2908,24 @@ function runCommand() {
|
|
|
2578
2908
|
process.exit(1);
|
|
2579
2909
|
}
|
|
2580
2910
|
}
|
|
2911
|
+
const attachments = attachmentIds.map((fileId) => ({ fileId }));
|
|
2912
|
+
for (const path of filePaths) {
|
|
2913
|
+
const spinner = spin(`Uploading ${(0, import_path4.basename)(path)}\u2026`);
|
|
2914
|
+
try {
|
|
2915
|
+
const result = await client.files.upload(
|
|
2916
|
+
[{ data: new Blob([new Uint8Array((0, import_fs6.readFileSync)(path))]), name: (0, import_path4.basename)(path) }],
|
|
2917
|
+
{ agentId: agentId3, sessionId }
|
|
2918
|
+
);
|
|
2919
|
+
const fileId = result.data[0]?.fileId;
|
|
2920
|
+
if (!fileId) throw new Error(`The upload did not return a file ID for ${path}.`);
|
|
2921
|
+
attachments.push({ fileId });
|
|
2922
|
+
spinner.stop();
|
|
2923
|
+
} catch (error) {
|
|
2924
|
+
spinner.stop();
|
|
2925
|
+
printError(error);
|
|
2926
|
+
process.exit(1);
|
|
2927
|
+
}
|
|
2928
|
+
}
|
|
2581
2929
|
const localEnabled = opts.yes || opts.local;
|
|
2582
2930
|
const autoApprove = !!opts.yes;
|
|
2583
2931
|
let localToolsCfg = null;
|
|
@@ -2590,11 +2938,16 @@ function runCommand() {
|
|
|
2590
2938
|
appendLog: () => {
|
|
2591
2939
|
},
|
|
2592
2940
|
permissions: /* @__PURE__ */ new Map(),
|
|
2593
|
-
agentId:
|
|
2941
|
+
agentId: agentId3,
|
|
2594
2942
|
autoApprove
|
|
2595
2943
|
};
|
|
2596
2944
|
const snapshot = buildDirSnapshot(rootDir, 2);
|
|
2597
|
-
cliContext = buildLocalToolsManifest(
|
|
2945
|
+
cliContext = buildLocalToolsManifest(
|
|
2946
|
+
rootDir,
|
|
2947
|
+
snapshot,
|
|
2948
|
+
[],
|
|
2949
|
+
autoApprove
|
|
2950
|
+
);
|
|
2598
2951
|
}
|
|
2599
2952
|
if (!opts.json) {
|
|
2600
2953
|
const rows = [];
|
|
@@ -2603,10 +2956,16 @@ function runCommand() {
|
|
|
2603
2956
|
rows.push(["Session", label]);
|
|
2604
2957
|
}
|
|
2605
2958
|
if (localEnabled) {
|
|
2606
|
-
rows.push([
|
|
2959
|
+
rows.push([
|
|
2960
|
+
"Local tools",
|
|
2961
|
+
autoApprove ? c.warn("enabled (auto-approve on)") : c.success("enabled")
|
|
2962
|
+
]);
|
|
2607
2963
|
}
|
|
2608
2964
|
if (opts.computer) {
|
|
2609
|
-
rows.push([
|
|
2965
|
+
rows.push([
|
|
2966
|
+
"Cloud computer",
|
|
2967
|
+
c.success("enabled") + c.dim(" (persistent, remote)")
|
|
2968
|
+
]);
|
|
2610
2969
|
}
|
|
2611
2970
|
if (rows.length) {
|
|
2612
2971
|
detail(rows);
|
|
@@ -2614,12 +2973,17 @@ function runCommand() {
|
|
|
2614
2973
|
}
|
|
2615
2974
|
}
|
|
2616
2975
|
const params = {
|
|
2617
|
-
agentId:
|
|
2976
|
+
agentId: agentId3,
|
|
2618
2977
|
sessionId,
|
|
2619
2978
|
messages: [{ role: "user", content: prompt }],
|
|
2979
|
+
...attachments.length && { attachments },
|
|
2620
2980
|
...cfg.initiator && { initiatorId: cfg.initiator },
|
|
2621
2981
|
...opts.computer && { computerRequest: { enabled: true } },
|
|
2622
|
-
...cliContext && { cliContext }
|
|
2982
|
+
...cliContext && { cliContext },
|
|
2983
|
+
provenance: {
|
|
2984
|
+
mode: opts.provenance,
|
|
2985
|
+
onchain: Boolean(opts.onchainProvenance)
|
|
2986
|
+
}
|
|
2623
2987
|
};
|
|
2624
2988
|
if (opts.noStream && !localEnabled) {
|
|
2625
2989
|
const spinner = spin("Running\u2026");
|
|
@@ -2629,8 +2993,13 @@ function runCommand() {
|
|
|
2629
2993
|
if (opts.json) return jsonOut(result);
|
|
2630
2994
|
const text = result?.content ?? result?.text ?? result?.message ?? JSON.stringify(result);
|
|
2631
2995
|
console.log(text);
|
|
2632
|
-
if (sessionId)
|
|
2633
|
-
|
|
2996
|
+
if (sessionId)
|
|
2997
|
+
console.log(
|
|
2998
|
+
c.dim(
|
|
2999
|
+
`
|
|
3000
|
+
Session: ${sessionId} (resume with: agc run --session ${sessionId} "<prompt>")`
|
|
3001
|
+
)
|
|
3002
|
+
);
|
|
2634
3003
|
} catch (err) {
|
|
2635
3004
|
spinner.stop();
|
|
2636
3005
|
printError(err);
|
|
@@ -2662,7 +3031,10 @@ Session: ${sessionId} (resume with: agc run --session ${sessionId} "<prompt>")`
|
|
|
2662
3031
|
let result;
|
|
2663
3032
|
let toolOk = true;
|
|
2664
3033
|
try {
|
|
2665
|
-
result = await runLocalTool(
|
|
3034
|
+
result = await runLocalTool(
|
|
3035
|
+
{ tool: displayName, args: args ?? {} },
|
|
3036
|
+
localToolsCfg
|
|
3037
|
+
);
|
|
2666
3038
|
} catch (err) {
|
|
2667
3039
|
result = `Error: ${err?.message ?? String(err)}`;
|
|
2668
3040
|
toolOk = false;
|
|
@@ -2670,8 +3042,10 @@ Session: ${sessionId} (resume with: agc run --session ${sessionId} "<prompt>")`
|
|
|
2670
3042
|
const elapsed = ((Date.now() - startMs) / 1e3).toFixed(1);
|
|
2671
3043
|
readline2.cursorTo(process.stdout, 0);
|
|
2672
3044
|
readline2.clearLine(process.stdout, 0);
|
|
2673
|
-
process.stdout.write(
|
|
2674
|
-
`)
|
|
3045
|
+
process.stdout.write(
|
|
3046
|
+
` ${c.dim("\u2500")} ${c.bold(displayName)} ${toolOk ? sym.ok : sym.fail} ${c.dim("(" + elapsed + "s)")}
|
|
3047
|
+
`
|
|
3048
|
+
);
|
|
2675
3049
|
try {
|
|
2676
3050
|
await client.agents.submitCliToolResult(requestId, result);
|
|
2677
3051
|
} catch {
|
|
@@ -2688,20 +3062,29 @@ Session: ${sessionId} (resume with: agc run --session ${sessionId} "<prompt>")`
|
|
|
2688
3062
|
const elapsed = ((Date.now() - toolStartMs) / 1e3).toFixed(1);
|
|
2689
3063
|
readline2.cursorTo(process.stdout, 0);
|
|
2690
3064
|
readline2.clearLine(process.stdout, 0);
|
|
2691
|
-
process.stdout.write(
|
|
2692
|
-
`)
|
|
3065
|
+
process.stdout.write(
|
|
3066
|
+
` ${c.dim("\u2500")} ${c.bold(lastToolName)} ${sym.ok} ${c.dim("(" + elapsed + "s)")}
|
|
3067
|
+
`
|
|
3068
|
+
);
|
|
2693
3069
|
} else if (event.type === "final") {
|
|
2694
3070
|
if (hasOutput) process.stdout.write("\n");
|
|
2695
3071
|
const e = event;
|
|
2696
3072
|
const finalText = e.content ?? e.payload?.content ?? e.payload?.text ?? e.payload?.message;
|
|
2697
3073
|
if (finalText && !hasOutput) console.log(finalText);
|
|
2698
|
-
if (sessionId)
|
|
2699
|
-
|
|
3074
|
+
if (sessionId)
|
|
3075
|
+
console.log(
|
|
3076
|
+
c.dim(
|
|
3077
|
+
`
|
|
3078
|
+
Session: ${sessionId} (resume with: agc run --session ${sessionId} "<prompt>")`
|
|
3079
|
+
)
|
|
3080
|
+
);
|
|
2700
3081
|
break;
|
|
2701
3082
|
} else if (event.type === "error") {
|
|
2702
3083
|
if (hasOutput) process.stdout.write("\n");
|
|
2703
|
-
console.error(
|
|
2704
|
-
|
|
3084
|
+
console.error(
|
|
3085
|
+
`
|
|
3086
|
+
${sym.fail} ${c.error(event.message ?? "Error")}`
|
|
3087
|
+
);
|
|
2705
3088
|
process.exit(1);
|
|
2706
3089
|
}
|
|
2707
3090
|
}
|
|
@@ -2716,18 +3099,19 @@ ${sym.fail} ${c.error(event.message ?? "Error")}`);
|
|
|
2716
3099
|
// src/commands/chat.ts
|
|
2717
3100
|
var import_commander9 = require("commander");
|
|
2718
3101
|
var readline3 = __toESM(require("readline"));
|
|
2719
|
-
var
|
|
2720
|
-
var
|
|
3102
|
+
var import_fs7 = require("fs");
|
|
3103
|
+
var import_path5 = require("path");
|
|
2721
3104
|
var import_os3 = require("os");
|
|
2722
|
-
var SESSIONS_DIR = (0,
|
|
3105
|
+
var SESSIONS_DIR = (0, import_path5.join)((0, import_os3.homedir)(), ".agc", "sessions");
|
|
2723
3106
|
function ensureSessionsDir() {
|
|
2724
|
-
if (!(0,
|
|
3107
|
+
if (!(0, import_fs7.existsSync)(SESSIONS_DIR)) (0, import_fs7.mkdirSync)(SESSIONS_DIR, { recursive: true });
|
|
2725
3108
|
}
|
|
2726
3109
|
function appendSessionLog(sessionId, record) {
|
|
2727
3110
|
try {
|
|
3111
|
+
if (!/^[a-zA-Z0-9_-]{1,128}$/.test(sessionId)) return;
|
|
2728
3112
|
ensureSessionsDir();
|
|
2729
|
-
const file = (0,
|
|
2730
|
-
(0,
|
|
3113
|
+
const file = (0, import_path5.join)(SESSIONS_DIR, `${sessionId}.jsonl`);
|
|
3114
|
+
(0, import_fs7.appendFileSync)(file, JSON.stringify(record) + "\n", { mode: 384 });
|
|
2731
3115
|
} catch {
|
|
2732
3116
|
}
|
|
2733
3117
|
}
|
|
@@ -2750,27 +3134,35 @@ var LOCAL_TOOLS_DISCLAIMER = `
|
|
|
2750
3134
|
${c.dim("and execute shell commands on your machine.")}
|
|
2751
3135
|
|
|
2752
3136
|
${c.dim("Rules:")}
|
|
2753
|
-
${sym.bullet} ${c.dim("
|
|
3137
|
+
${sym.bullet} ${c.dim("File tools are restricted to:")} ${c.primary(process.cwd())}
|
|
2754
3138
|
${sym.bullet} ${c.dim("Sensitive paths (.ssh, .env, .aws, credentials) are always blocked")}
|
|
2755
3139
|
${sym.bullet} ${c.dim("Write and run_command operations require your confirmation")}
|
|
2756
|
-
${sym.bullet} ${c.dim("
|
|
3140
|
+
${sym.bullet} ${c.dim("Approved commands run with your operating-system permissions; they are not sandboxed")}
|
|
2757
3141
|
|
|
2758
3142
|
${c.dim("Session activity is logged to")} ${c.primary("~/.agc/sessions/")}
|
|
2759
3143
|
`;
|
|
2760
3144
|
function chatCommand() {
|
|
2761
|
-
return new import_commander9.Command("chat").description("Start an interactive
|
|
3145
|
+
return new import_commander9.Command("chat").alias("code").description("Start an interactive coding session with an agent").argument("[prompt]", "Initial task to send when the session opens").option("--prompt-file <path>", "Read the initial task from a UTF-8 file (maximum 100 KB)").option("--read-only", "Allow local reads but deny local edits and commands").option("--agent <agentId>", "Agent ID (or set defaultAgentId in config)").option("--resume <sessionId>", "Resume an existing session by ID").option("--computer", "Give the agent access to its persistent cloud computer").option("--no-stream", "Disable token streaming (wait for full response)").option("--no-local", "Disable local file system access for the agent").action(async (prompt, opts) => {
|
|
3146
|
+
let initialPrompt = prompt;
|
|
3147
|
+
if (opts.promptFile) {
|
|
3148
|
+
if (prompt) throw new Error("Use either a prompt argument or --prompt-file, not both.");
|
|
3149
|
+
if ((0, import_fs7.statSync)(opts.promptFile).size > 1e5) throw new Error("Prompt file exceeds 100 KB.");
|
|
3150
|
+
initialPrompt = (0, import_fs7.readFileSync)(opts.promptFile, "utf8").trim();
|
|
3151
|
+
}
|
|
3152
|
+
if (!process.stdin.isTTY) throw new Error("Interactive coding requires a terminal. Use `agc run` for scripts.");
|
|
2762
3153
|
const localEnabled = opts.local !== false;
|
|
3154
|
+
if (localEnabled && opts.stream === false) throw new Error("Local tools require streaming. Use --no-local with --no-stream.");
|
|
2763
3155
|
const cfg = loadConfig();
|
|
2764
|
-
let
|
|
2765
|
-
if (!
|
|
3156
|
+
let agentId3 = opts.agent ?? cfg.defaultAgentId;
|
|
3157
|
+
if (!agentId3 && cfg.initiator) {
|
|
2766
3158
|
try {
|
|
2767
3159
|
const listed = await makeClient().agents.list(cfg.initiator);
|
|
2768
3160
|
const agents = listed?.data ?? listed ?? [];
|
|
2769
|
-
|
|
3161
|
+
agentId3 = agents.find((agent) => agent.isDefault)?.agentId ?? agents[0]?.agentId;
|
|
2770
3162
|
} catch {
|
|
2771
3163
|
}
|
|
2772
3164
|
}
|
|
2773
|
-
if (!
|
|
3165
|
+
if (!agentId3) {
|
|
2774
3166
|
console.error(c.error("No default agent is available. Specify --agent <agentId> or run `agc agents list`."));
|
|
2775
3167
|
process.exit(1);
|
|
2776
3168
|
}
|
|
@@ -2786,7 +3178,7 @@ function chatCommand() {
|
|
|
2786
3178
|
const spinner = spin("Creating session\u2026");
|
|
2787
3179
|
try {
|
|
2788
3180
|
const res = await client.sessions.create({
|
|
2789
|
-
agentId:
|
|
3181
|
+
agentId: agentId3,
|
|
2790
3182
|
initiator,
|
|
2791
3183
|
title: `agc chat ${(/* @__PURE__ */ new Date()).toISOString().slice(0, 16)}`,
|
|
2792
3184
|
source: "cli"
|
|
@@ -2797,7 +3189,7 @@ function chatCommand() {
|
|
|
2797
3189
|
appendSessionLog(sessionId, {
|
|
2798
3190
|
type: "session_start",
|
|
2799
3191
|
sessionId,
|
|
2800
|
-
agentId:
|
|
3192
|
+
agentId: agentId3,
|
|
2801
3193
|
initiator,
|
|
2802
3194
|
source: "cli",
|
|
2803
3195
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -2812,12 +3204,8 @@ function chatCommand() {
|
|
|
2812
3204
|
try {
|
|
2813
3205
|
const res = await client.sessions.get(sessionId);
|
|
2814
3206
|
const session = res?.data ?? res;
|
|
2815
|
-
if (session.agentId
|
|
2816
|
-
|
|
2817
|
-
console.log(c.warn(` Note: session ${sessionId} was created with agent ${session.agentId}, not ${agentId2}`));
|
|
2818
|
-
} else {
|
|
2819
|
-
spinner.stop();
|
|
2820
|
-
}
|
|
3207
|
+
if (session.agentId) agentId3 = session.agentId;
|
|
3208
|
+
spinner.stop();
|
|
2821
3209
|
} catch {
|
|
2822
3210
|
spinner.stop();
|
|
2823
3211
|
console.error(c.error(`Session "${sessionId}" not found.`));
|
|
@@ -2827,10 +3215,10 @@ function chatCommand() {
|
|
|
2827
3215
|
let agentName;
|
|
2828
3216
|
let walletLine = "";
|
|
2829
3217
|
await Promise.allSettled([
|
|
2830
|
-
client.agents.get(
|
|
3218
|
+
client.agents.get(agentId3).then((res) => {
|
|
2831
3219
|
agentName = (res?.data ?? res)?.name;
|
|
2832
3220
|
}),
|
|
2833
|
-
client.wallets.primary(
|
|
3221
|
+
client.wallets.primary(agentId3).then(async (primary) => {
|
|
2834
3222
|
const w = primary?.data ?? primary;
|
|
2835
3223
|
if (w?.id) {
|
|
2836
3224
|
const bal = await client.wallets.balance(w.id).catch(() => null);
|
|
@@ -2842,9 +3230,9 @@ function chatCommand() {
|
|
|
2842
3230
|
})
|
|
2843
3231
|
]);
|
|
2844
3232
|
console.log(`
|
|
2845
|
-
${c.bold("Agent Commons
|
|
3233
|
+
${c.bold("Agent Commons \xB7 Code")}`);
|
|
2846
3234
|
const headerRows = [
|
|
2847
|
-
["Agent", agentName ? `${agentName} ${c.dim(
|
|
3235
|
+
["Agent", agentName ? `${agentName} ${c.dim(agentId3)}` : agentId3],
|
|
2848
3236
|
["Session", c.id(sessionId) + (isResume ? c.dim(" (resumed)") : c.dim(" (new)"))]
|
|
2849
3237
|
];
|
|
2850
3238
|
if (walletLine) headerRows.push(["Wallet", walletLine]);
|
|
@@ -2853,17 +3241,16 @@ ${c.bold("Agent Commons Chat")}`);
|
|
|
2853
3241
|
detail(headerRows);
|
|
2854
3242
|
let localToolsCfg = null;
|
|
2855
3243
|
if (localEnabled) {
|
|
2856
|
-
console.log(LOCAL_TOOLS_DISCLAIMER);
|
|
3244
|
+
console.log(opts.readOnly ? c.dim(" Local read-only mode: edits and commands are denied.\n") : LOCAL_TOOLS_DISCLAIMER);
|
|
2857
3245
|
const rootDir = process.cwd();
|
|
2858
3246
|
localToolsCfg = {
|
|
2859
3247
|
rootDir,
|
|
2860
3248
|
sessionId,
|
|
2861
|
-
agentId:
|
|
3249
|
+
agentId: agentId3,
|
|
2862
3250
|
agentName,
|
|
2863
3251
|
appendLog: (record) => appendSessionLog(sessionId, record),
|
|
2864
|
-
permissions:
|
|
3252
|
+
permissions: new Map(opts.readOnly ? ["write_file", "run_command", "start_process"].map((key) => [key, "deny"]) : [])
|
|
2865
3253
|
};
|
|
2866
|
-
installGitHook(rootDir, sessionId, agentId2, agentName);
|
|
2867
3254
|
appendSessionLog(sessionId, {
|
|
2868
3255
|
type: "local_tools_enabled",
|
|
2869
3256
|
rootDir,
|
|
@@ -2877,8 +3264,10 @@ ${c.bold("Agent Commons Chat")}`);
|
|
|
2877
3264
|
terminal: true,
|
|
2878
3265
|
prompt: c.primary("you") + c.dim(" \u203A ")
|
|
2879
3266
|
});
|
|
2880
|
-
|
|
3267
|
+
let busy = false;
|
|
3268
|
+
if (!initialPrompt) rl.prompt();
|
|
2881
3269
|
rl.on("line", async (line) => {
|
|
3270
|
+
if (busy) return;
|
|
2882
3271
|
const input = line.trim();
|
|
2883
3272
|
if (!input) {
|
|
2884
3273
|
rl.prompt();
|
|
@@ -2931,6 +3320,7 @@ Session saved. Resume with: agc chat --resume ${sessionId}`));
|
|
|
2931
3320
|
rl.prompt();
|
|
2932
3321
|
return;
|
|
2933
3322
|
}
|
|
3323
|
+
busy = true;
|
|
2934
3324
|
rl.pause();
|
|
2935
3325
|
appendSessionLog(sessionId, {
|
|
2936
3326
|
type: "message",
|
|
@@ -2944,6 +3334,11 @@ Session saved. Resume with: agc chat --resume ${sessionId}`));
|
|
|
2944
3334
|
const rootDir = localToolsCfg.rootDir;
|
|
2945
3335
|
const atRefs = [...input.matchAll(/@([\S]+)/g)].map((m) => m[1]);
|
|
2946
3336
|
const fileContextBlocks = [];
|
|
3337
|
+
if ((0, import_fs7.existsSync)((0, import_path5.join)(rootDir, "AGENTS.md"))) {
|
|
3338
|
+
fileContextBlocks.push(`Project instructions (AGENTS.md):
|
|
3339
|
+
${readFileForContext(rootDir, "AGENTS.md")}`);
|
|
3340
|
+
}
|
|
3341
|
+
if (opts.readOnly) fileContextBlocks.push("Local read-only mode: do not write files or execute commands. Local reads are available.");
|
|
2947
3342
|
for (const ref of atRefs) {
|
|
2948
3343
|
const content = readFileForContext(rootDir, ref);
|
|
2949
3344
|
fileContextBlocks.push(`**${ref}**
|
|
@@ -2955,13 +3350,13 @@ ${content}
|
|
|
2955
3350
|
cliContext = buildLocalToolsManifest(rootDir, snapshot, fileContextBlocks);
|
|
2956
3351
|
}
|
|
2957
3352
|
const params = {
|
|
2958
|
-
agentId:
|
|
3353
|
+
agentId: agentId3,
|
|
2959
3354
|
sessionId,
|
|
2960
3355
|
messages: [{ role: "user", content: userMessage }],
|
|
2961
3356
|
...opts.computer && { computerRequest: { enabled: true } },
|
|
2962
3357
|
...cliContext && { cliContext }
|
|
2963
3358
|
};
|
|
2964
|
-
if (opts.
|
|
3359
|
+
if (opts.stream === false) {
|
|
2965
3360
|
process.stdout.write(c.primary("agent") + c.dim(" \u203A "));
|
|
2966
3361
|
const spinner = spin("thinking\u2026");
|
|
2967
3362
|
try {
|
|
@@ -2982,12 +3377,12 @@ ${content}
|
|
|
2982
3377
|
${sym.fail} ${c.error(err.message ?? String(err))}`);
|
|
2983
3378
|
}
|
|
2984
3379
|
} else {
|
|
3380
|
+
const thinkingSpinner = spin("thinking\u2026");
|
|
2985
3381
|
try {
|
|
2986
3382
|
let hasOutput = false;
|
|
2987
3383
|
let agentContent = "";
|
|
2988
3384
|
let toolStartMs = 0;
|
|
2989
3385
|
let lastToolName = "";
|
|
2990
|
-
const thinkingSpinner = spin("thinking\u2026");
|
|
2991
3386
|
for await (const event of client.agents.stream(params)) {
|
|
2992
3387
|
if (event.type === "token") {
|
|
2993
3388
|
if (thinkingSpinner.isSpinning) {
|
|
@@ -3116,36 +3511,53 @@ ${sym.fail} ${c.error(event.message ?? "Stream error")}`);
|
|
|
3116
3511
|
if (thinkingSpinner.isSpinning) thinkingSpinner.stop();
|
|
3117
3512
|
process.stdout.write("\n");
|
|
3118
3513
|
if (localToolsCfg && agentContent) {
|
|
3119
|
-
await handleLocalToolLoop(agentContent, localToolsCfg, client,
|
|
3514
|
+
await handleLocalToolLoop(agentContent, localToolsCfg, client, agentId3, sessionId, appendSessionLog, !!opts.computer);
|
|
3120
3515
|
}
|
|
3121
3516
|
} catch (err) {
|
|
3122
3517
|
process.stdout.write("\n");
|
|
3123
3518
|
console.error(`${sym.fail} ${c.error(err.message ?? String(err))}`);
|
|
3519
|
+
} finally {
|
|
3520
|
+
thinkingSpinner.stop();
|
|
3124
3521
|
}
|
|
3125
3522
|
}
|
|
3126
3523
|
console.log();
|
|
3127
3524
|
readline3.cursorTo(process.stdout, 0);
|
|
3128
3525
|
readline3.clearLine(process.stdout, 0);
|
|
3526
|
+
busy = false;
|
|
3129
3527
|
rl.resume();
|
|
3130
3528
|
rl.prompt();
|
|
3131
3529
|
});
|
|
3132
3530
|
const cleanup = () => {
|
|
3133
|
-
|
|
3531
|
+
stopLocalProcesses();
|
|
3134
3532
|
};
|
|
3135
3533
|
rl.on("close", () => {
|
|
3136
3534
|
cleanup();
|
|
3137
3535
|
process.exit(0);
|
|
3138
3536
|
});
|
|
3537
|
+
rl.on("SIGINT", () => {
|
|
3538
|
+
cleanup();
|
|
3539
|
+
console.log(c.dim(`
|
|
3540
|
+
Session preserved. Resume with: agc code --resume ${sessionId}`));
|
|
3541
|
+
process.exit(130);
|
|
3542
|
+
});
|
|
3139
3543
|
process.on("SIGINT", () => {
|
|
3140
3544
|
cleanup();
|
|
3141
3545
|
console.log(c.dim(`
|
|
3142
3546
|
Session preserved. Resume with: agc chat --resume ${sessionId}`));
|
|
3143
3547
|
process.exit(130);
|
|
3144
3548
|
});
|
|
3549
|
+
process.on("SIGTERM", () => {
|
|
3550
|
+
cleanup();
|
|
3551
|
+
process.exit(143);
|
|
3552
|
+
});
|
|
3553
|
+
if (initialPrompt) {
|
|
3554
|
+
console.log(c.primary("you") + c.dim(" \u203A ") + initialPrompt);
|
|
3555
|
+
rl.emit("line", initialPrompt);
|
|
3556
|
+
}
|
|
3145
3557
|
});
|
|
3146
3558
|
}
|
|
3147
3559
|
var MAX_TOOL_DEPTH = 10;
|
|
3148
|
-
async function handleLocalToolLoop(agentText, cfg, client,
|
|
3560
|
+
async function handleLocalToolLoop(agentText, cfg, client, agentId3, sessionId, appendLog, computerEnabled = false, depth = 0) {
|
|
3149
3561
|
if (depth >= MAX_TOOL_DEPTH) {
|
|
3150
3562
|
console.log(c.dim(`
|
|
3151
3563
|
[local] Max tool depth reached (${MAX_TOOL_DEPTH}). Stopping tool loop.
|
|
@@ -3192,7 +3604,7 @@ ${result}
|
|
|
3192
3604
|
let loopToolName = "";
|
|
3193
3605
|
let loopToolStartMs = 0;
|
|
3194
3606
|
for await (const evt of client.agents.stream({
|
|
3195
|
-
agentId:
|
|
3607
|
+
agentId: agentId3,
|
|
3196
3608
|
sessionId,
|
|
3197
3609
|
messages: [{ role: "user", content: resultMsg }],
|
|
3198
3610
|
...computerEnabled && { computerRequest: { enabled: true } }
|
|
@@ -3238,7 +3650,7 @@ ${sym.fail} ${c.error(evt.message ?? "Stream error")}`);
|
|
|
3238
3650
|
console.error(`${sym.fail} ${c.error(err?.message ?? String(err))}`);
|
|
3239
3651
|
return;
|
|
3240
3652
|
}
|
|
3241
|
-
await handleLocalToolLoop(followContent, cfg, client,
|
|
3653
|
+
await handleLocalToolLoop(followContent, cfg, client, agentId3, sessionId, appendLog, computerEnabled, depth + 1);
|
|
3242
3654
|
}
|
|
3243
3655
|
function truncate(s, max) {
|
|
3244
3656
|
const str = String(s ?? "");
|
|
@@ -3248,6 +3660,8 @@ function toolArgSummary(toolName, args) {
|
|
|
3248
3660
|
switch (toolName) {
|
|
3249
3661
|
case "read_file":
|
|
3250
3662
|
return truncate(args.path ?? "", 60);
|
|
3663
|
+
case "search_file":
|
|
3664
|
+
return truncate(`${args.path ?? ""} \xB7 ${args.query ?? ""}`, 60);
|
|
3251
3665
|
case "write_file":
|
|
3252
3666
|
return truncate(args.path ?? "", 60);
|
|
3253
3667
|
case "delete_file":
|
|
@@ -3283,6 +3697,14 @@ function toolResultPreview(toolName, result) {
|
|
|
3283
3697
|
const lines = result.split("\n").length;
|
|
3284
3698
|
return `${lines} lines`;
|
|
3285
3699
|
}
|
|
3700
|
+
case "search_file": {
|
|
3701
|
+
try {
|
|
3702
|
+
const matches = JSON.parse(result)?.matches;
|
|
3703
|
+
return Array.isArray(matches) ? `${matches.length} passage${matches.length === 1 ? "" : "s"}` : "";
|
|
3704
|
+
} catch {
|
|
3705
|
+
return "";
|
|
3706
|
+
}
|
|
3707
|
+
}
|
|
3286
3708
|
case "write_file":
|
|
3287
3709
|
return "written";
|
|
3288
3710
|
case "delete_file":
|
|
@@ -3885,19 +4307,19 @@ function walletCommand() {
|
|
|
3885
4307
|
const cmd = new import_commander12.Command("wallet").description("Manage agent wallets");
|
|
3886
4308
|
cmd.command("list").description("List all wallets for an agent").option("--agent <agentId>", "Agent ID (or use defaultAgentId from config)").option("--json", "Output as JSON").action(async (opts) => {
|
|
3887
4309
|
const cfg = loadConfig();
|
|
3888
|
-
const
|
|
3889
|
-
if (!
|
|
4310
|
+
const agentId3 = opts.agent ?? cfg.defaultAgentId;
|
|
4311
|
+
if (!agentId3) {
|
|
3890
4312
|
console.error(c.error("Specify --agent <agentId> or set defaultAgentId with `agc config set defaultAgentId <id>`"));
|
|
3891
4313
|
process.exit(1);
|
|
3892
4314
|
}
|
|
3893
4315
|
const spinner = spin("Fetching wallets\u2026");
|
|
3894
4316
|
try {
|
|
3895
4317
|
const client = makeClient();
|
|
3896
|
-
const wallets = await client.wallets.list(
|
|
4318
|
+
const wallets = await client.wallets.list(agentId3);
|
|
3897
4319
|
spinner.stop();
|
|
3898
4320
|
if (opts.json) return jsonOut(wallets);
|
|
3899
4321
|
const list = wallets?.data ?? wallets ?? [];
|
|
3900
|
-
section(`Wallets for agent ${
|
|
4322
|
+
section(`Wallets for agent ${agentId3.slice(0, 8)}\u2026 (${list.length})`);
|
|
3901
4323
|
table(
|
|
3902
4324
|
list.map((w) => ({
|
|
3903
4325
|
ID: w.id.slice(0, 8) + "\u2026",
|
|
@@ -3917,19 +4339,19 @@ function walletCommand() {
|
|
|
3917
4339
|
});
|
|
3918
4340
|
cmd.command("show").description("Show the agent's primary wallet address").option("--agent <agentId>", "Agent ID (or use defaultAgentId from config)").option("--json", "Output as JSON").action(async (opts) => {
|
|
3919
4341
|
const cfg = loadConfig();
|
|
3920
|
-
const
|
|
3921
|
-
if (!
|
|
4342
|
+
const agentId3 = opts.agent ?? cfg.defaultAgentId;
|
|
4343
|
+
if (!agentId3) {
|
|
3922
4344
|
console.error(c.error("Specify --agent <agentId> or set defaultAgentId with `agc config set defaultAgentId <id>`"));
|
|
3923
4345
|
process.exit(1);
|
|
3924
4346
|
}
|
|
3925
4347
|
const spinner = spin("Fetching primary wallet\u2026");
|
|
3926
4348
|
try {
|
|
3927
4349
|
const client = makeClient();
|
|
3928
|
-
const wallet = await client.wallets.primary(
|
|
4350
|
+
const wallet = await client.wallets.primary(agentId3);
|
|
3929
4351
|
spinner.stop();
|
|
3930
4352
|
if (!wallet) {
|
|
3931
|
-
console.log(c.warn(` No wallet found for agent ${
|
|
3932
|
-
console.log(c.dim(` Run: agc wallet create --agent ${
|
|
4353
|
+
console.log(c.warn(` No wallet found for agent ${agentId3}`));
|
|
4354
|
+
console.log(c.dim(` Run: agc wallet create --agent ${agentId3}`));
|
|
3933
4355
|
return;
|
|
3934
4356
|
}
|
|
3935
4357
|
const w = wallet?.data ?? wallet;
|
|
@@ -3950,8 +4372,8 @@ function walletCommand() {
|
|
|
3950
4372
|
});
|
|
3951
4373
|
cmd.command("balance").description("Show the agent's wallet USDC and ETH balance").option("--agent <agentId>", "Agent ID (or use defaultAgentId from config)").option("--wallet <walletId>", "Specific wallet ID (defaults to primary)").option("--json", "Output as JSON").action(async (opts) => {
|
|
3952
4374
|
const cfg = loadConfig();
|
|
3953
|
-
const
|
|
3954
|
-
if (!
|
|
4375
|
+
const agentId3 = opts.agent ?? cfg.defaultAgentId;
|
|
4376
|
+
if (!agentId3) {
|
|
3955
4377
|
console.error(c.error("Specify --agent <agentId> or set defaultAgentId with `agc config set defaultAgentId <id>`"));
|
|
3956
4378
|
process.exit(1);
|
|
3957
4379
|
}
|
|
@@ -3960,11 +4382,11 @@ function walletCommand() {
|
|
|
3960
4382
|
const client = makeClient();
|
|
3961
4383
|
let walletId = opts.wallet;
|
|
3962
4384
|
if (!walletId) {
|
|
3963
|
-
const primary = await client.wallets.primary(
|
|
4385
|
+
const primary = await client.wallets.primary(agentId3);
|
|
3964
4386
|
const w = primary?.data ?? primary;
|
|
3965
4387
|
if (!w) {
|
|
3966
4388
|
spinner.stop();
|
|
3967
|
-
console.log(c.warn(` No wallet found. Run: agc wallet create --agent ${
|
|
4389
|
+
console.log(c.warn(` No wallet found. Run: agc wallet create --agent ${agentId3}`));
|
|
3968
4390
|
return;
|
|
3969
4391
|
}
|
|
3970
4392
|
walletId = w.id;
|
|
@@ -3991,8 +4413,8 @@ function walletCommand() {
|
|
|
3991
4413
|
});
|
|
3992
4414
|
cmd.command("create").description("Create a new wallet for an agent").option("--agent <agentId>", "Agent ID (or use defaultAgentId from config)").option("--type <type>", "Wallet type: eoa | external (default: eoa)", "eoa").option("--label <label>", "Wallet label (default: Primary)", "Primary").option("--address <address>", "For --type external: owner-provided address").option("--json", "Output as JSON").action(async (opts) => {
|
|
3993
4415
|
const cfg = loadConfig();
|
|
3994
|
-
const
|
|
3995
|
-
if (!
|
|
4416
|
+
const agentId3 = opts.agent ?? cfg.defaultAgentId;
|
|
4417
|
+
if (!agentId3) {
|
|
3996
4418
|
console.error(c.error("Specify --agent <agentId> or set defaultAgentId with `agc config set defaultAgentId <id>`"));
|
|
3997
4419
|
process.exit(1);
|
|
3998
4420
|
}
|
|
@@ -4004,7 +4426,7 @@ function walletCommand() {
|
|
|
4004
4426
|
try {
|
|
4005
4427
|
const client = makeClient();
|
|
4006
4428
|
const wallet = await client.wallets.create({
|
|
4007
|
-
agentId:
|
|
4429
|
+
agentId: agentId3,
|
|
4008
4430
|
walletType: opts.type,
|
|
4009
4431
|
label: opts.label,
|
|
4010
4432
|
externalAddress: opts.address
|
|
@@ -4162,22 +4584,22 @@ function memoryCommand() {
|
|
|
4162
4584
|
const cmd = new import_commander14.Command("memory").description("View and manage agent memories");
|
|
4163
4585
|
cmd.command("list").description("List memories for an agent").option("--agent <agentId>", "Agent ID (defaults to configured agent)").option("--type <type>", "Filter by type: episodic | semantic | procedural").option("--limit <n>", "Max results", "50").option("--json", "Output as JSON").action(async (opts) => {
|
|
4164
4586
|
const cfg = loadConfig();
|
|
4165
|
-
const
|
|
4166
|
-
if (!
|
|
4587
|
+
const agentId3 = opts.agent ?? cfg.defaultAgentId;
|
|
4588
|
+
if (!agentId3) {
|
|
4167
4589
|
console.error(c.error("Specify --agent <agentId> or set defaultAgentId"));
|
|
4168
4590
|
process.exit(1);
|
|
4169
4591
|
}
|
|
4170
4592
|
const spinner = spin("Fetching memories\u2026");
|
|
4171
4593
|
try {
|
|
4172
4594
|
const client = makeClient();
|
|
4173
|
-
const res = await client.memory.list(
|
|
4595
|
+
const res = await client.memory.list(agentId3, {
|
|
4174
4596
|
type: opts.type,
|
|
4175
4597
|
limit: parseInt(opts.limit, 10)
|
|
4176
4598
|
});
|
|
4177
4599
|
const memories = res?.data ?? res ?? [];
|
|
4178
4600
|
spinner.stop();
|
|
4179
4601
|
if (opts.json) return jsonOut(memories);
|
|
4180
|
-
section(`Memories for ${
|
|
4602
|
+
section(`Memories for ${agentId3.slice(0, 12)}\u2026 (${memories.length})`);
|
|
4181
4603
|
if (memories.length === 0) {
|
|
4182
4604
|
console.log(c.dim(" No memories yet"));
|
|
4183
4605
|
return;
|
|
@@ -4199,15 +4621,15 @@ function memoryCommand() {
|
|
|
4199
4621
|
});
|
|
4200
4622
|
cmd.command("stats").description("Show memory statistics for an agent").option("--agent <agentId>", "Agent ID").option("--json", "Output as JSON").action(async (opts) => {
|
|
4201
4623
|
const cfg = loadConfig();
|
|
4202
|
-
const
|
|
4203
|
-
if (!
|
|
4624
|
+
const agentId3 = opts.agent ?? cfg.defaultAgentId;
|
|
4625
|
+
if (!agentId3) {
|
|
4204
4626
|
console.error(c.error("Specify --agent <agentId>"));
|
|
4205
4627
|
process.exit(1);
|
|
4206
4628
|
}
|
|
4207
4629
|
const spinner = spin("Fetching stats\u2026");
|
|
4208
4630
|
try {
|
|
4209
4631
|
const client = makeClient();
|
|
4210
|
-
const res = await client.memory.stats(
|
|
4632
|
+
const res = await client.memory.stats(agentId3);
|
|
4211
4633
|
const stats = res?.data ?? res;
|
|
4212
4634
|
spinner.stop();
|
|
4213
4635
|
if (opts.json) return jsonOut(stats);
|
|
@@ -4267,15 +4689,15 @@ ${sym.ok} Memory ${c.id(memoryId)} deleted`);
|
|
|
4267
4689
|
});
|
|
4268
4690
|
cmd.command("search <query>").description("Semantic search over agent memories").option("--agent <agentId>", "Agent ID").option("--limit <n>", "Max results", "10").option("--json", "Output as JSON").action(async (query, opts) => {
|
|
4269
4691
|
const cfg = loadConfig();
|
|
4270
|
-
const
|
|
4271
|
-
if (!
|
|
4692
|
+
const agentId3 = opts.agent ?? cfg.defaultAgentId;
|
|
4693
|
+
if (!agentId3) {
|
|
4272
4694
|
console.error(c.error("Specify --agent <agentId>"));
|
|
4273
4695
|
process.exit(1);
|
|
4274
4696
|
}
|
|
4275
4697
|
const spinner = spin("Searching memories\u2026");
|
|
4276
4698
|
try {
|
|
4277
4699
|
const client = makeClient();
|
|
4278
|
-
const res = await client.memory.retrieve(
|
|
4700
|
+
const res = await client.memory.retrieve(agentId3, query, parseInt(opts.limit, 10));
|
|
4279
4701
|
const memories = res?.data ?? res ?? [];
|
|
4280
4702
|
spinner.stop();
|
|
4281
4703
|
if (opts.json) return jsonOut(memories);
|
|
@@ -4364,18 +4786,18 @@ function usageCommand() {
|
|
|
4364
4786
|
process.exit(1);
|
|
4365
4787
|
}
|
|
4366
4788
|
});
|
|
4367
|
-
cmd.command("agent <agentId>").description("Show detailed usage for a specific agent").option("--from <date>", "Start date (ISO)").option("--to <date>", "End date (ISO)").option("--json", "Output as JSON").action(async (
|
|
4789
|
+
cmd.command("agent <agentId>").description("Show detailed usage for a specific agent").option("--from <date>", "Start date (ISO)").option("--to <date>", "End date (ISO)").option("--json", "Output as JSON").action(async (agentId3, opts) => {
|
|
4368
4790
|
const spinner = spin("Fetching usage\u2026");
|
|
4369
4791
|
try {
|
|
4370
4792
|
const client = makeClient();
|
|
4371
|
-
const res = await client.usage.getAgentUsage(
|
|
4793
|
+
const res = await client.usage.getAgentUsage(agentId3, {
|
|
4372
4794
|
from: opts.from,
|
|
4373
4795
|
to: opts.to
|
|
4374
4796
|
});
|
|
4375
4797
|
const data = res?.data ?? res;
|
|
4376
4798
|
spinner.stop();
|
|
4377
4799
|
if (opts.json) return jsonOut(data);
|
|
4378
|
-
section(`Usage \u2014 ${
|
|
4800
|
+
section(`Usage \u2014 ${agentId3.slice(0, 12)}\u2026`);
|
|
4379
4801
|
detail([
|
|
4380
4802
|
["Calls", (data.callCount ?? 0).toLocaleString()],
|
|
4381
4803
|
["Input tokens", (data.totalInputTokens ?? 0).toLocaleString()],
|
|
@@ -4512,15 +4934,15 @@ function logsCommand() {
|
|
|
4512
4934
|
const cmd = new import_commander17.Command("logs").description("View agent activity logs");
|
|
4513
4935
|
cmd.command("list").alias("ls").description("List recent log entries for an agent").option("--agent <agentId>", "Agent ID (defaults to configured agent)").option("--session <sessionId>", "Filter by session ID").option("--status <status>", "Filter: success | error | warning").option("--limit <n>", "Max entries to show", "50").option("--json", "Output as JSON").action(async (opts) => {
|
|
4514
4936
|
const cfg = loadConfig();
|
|
4515
|
-
const
|
|
4516
|
-
if (!
|
|
4937
|
+
const agentId3 = opts.agent ?? cfg.defaultAgentId;
|
|
4938
|
+
if (!agentId3) {
|
|
4517
4939
|
console.error(c.error("Specify --agent <agentId> or set defaultAgentId"));
|
|
4518
4940
|
process.exit(1);
|
|
4519
4941
|
}
|
|
4520
4942
|
const spinner = spin("Fetching logs\u2026");
|
|
4521
4943
|
try {
|
|
4522
4944
|
const client = makeClient();
|
|
4523
|
-
const res = await client.logs.list(
|
|
4945
|
+
const res = await client.logs.list(agentId3, {
|
|
4524
4946
|
limit: Number(opts.limit),
|
|
4525
4947
|
sessionId: opts.session
|
|
4526
4948
|
});
|
|
@@ -4528,7 +4950,7 @@ function logsCommand() {
|
|
|
4528
4950
|
if (opts.status) logs = logs.filter((l) => l.status === opts.status);
|
|
4529
4951
|
spinner.stop();
|
|
4530
4952
|
if (opts.json) return jsonOut(logs);
|
|
4531
|
-
section(`Logs \u2014 ${
|
|
4953
|
+
section(`Logs \u2014 ${agentId3.slice(0, 12)}\u2026 (${logs.length})`);
|
|
4532
4954
|
if (logs.length === 0) {
|
|
4533
4955
|
console.log(c.dim(" No logs yet"));
|
|
4534
4956
|
return;
|
|
@@ -4553,21 +4975,21 @@ function logsCommand() {
|
|
|
4553
4975
|
});
|
|
4554
4976
|
cmd.command("errors").description("Show only error log entries for an agent").option("--agent <agentId>", "Agent ID").option("--limit <n>", "Max entries", "20").option("--json", "Output as JSON").action(async (opts) => {
|
|
4555
4977
|
const cfg = loadConfig();
|
|
4556
|
-
const
|
|
4557
|
-
if (!
|
|
4978
|
+
const agentId3 = opts.agent ?? cfg.defaultAgentId;
|
|
4979
|
+
if (!agentId3) {
|
|
4558
4980
|
console.error(c.error("Specify --agent <agentId>"));
|
|
4559
4981
|
process.exit(1);
|
|
4560
4982
|
}
|
|
4561
4983
|
const spinner = spin("Fetching error logs\u2026");
|
|
4562
4984
|
try {
|
|
4563
4985
|
const client = makeClient();
|
|
4564
|
-
const res = await client.logs.list(
|
|
4986
|
+
const res = await client.logs.list(agentId3, {
|
|
4565
4987
|
limit: Number(opts.limit)
|
|
4566
4988
|
});
|
|
4567
4989
|
const errors = (res?.data ?? []).filter((l) => l.status === "error");
|
|
4568
4990
|
spinner.stop();
|
|
4569
4991
|
if (opts.json) return jsonOut(errors);
|
|
4570
|
-
section(`Errors \u2014 ${
|
|
4992
|
+
section(`Errors \u2014 ${agentId3.slice(0, 12)}\u2026 (${errors.length})`);
|
|
4571
4993
|
if (errors.length === 0) {
|
|
4572
4994
|
console.log(`${sym.ok} No errors found`);
|
|
4573
4995
|
return;
|
|
@@ -4596,13 +5018,13 @@ var RESOURCE_PROFILES = [
|
|
|
4596
5018
|
];
|
|
4597
5019
|
var RESOURCE_MODES = ["fixed", "elastic"];
|
|
4598
5020
|
function resolveAgentId(opts) {
|
|
4599
|
-
const
|
|
4600
|
-
if (!
|
|
5021
|
+
const agentId3 = opts.agent ?? loadConfig().defaultAgentId;
|
|
5022
|
+
if (!agentId3) {
|
|
4601
5023
|
throw new Error(
|
|
4602
5024
|
"Specify --agent <agentId> or set defaultAgentId with `agc config set defaultAgentId <id>`."
|
|
4603
5025
|
);
|
|
4604
5026
|
}
|
|
4605
|
-
return
|
|
5027
|
+
return agentId3;
|
|
4606
5028
|
}
|
|
4607
5029
|
function unwrap(response) {
|
|
4608
5030
|
return response?.data ?? response;
|
|
@@ -4653,17 +5075,17 @@ function parseNumber(value, name, options) {
|
|
|
4653
5075
|
}
|
|
4654
5076
|
return parsed;
|
|
4655
5077
|
}
|
|
4656
|
-
async function changeEnabled(
|
|
5078
|
+
async function changeEnabled(agentId3, enabled, json) {
|
|
4657
5079
|
const spinner = spin(`${enabled ? "Enabling" : "Disabling"} persistent cloud computer\u2026`);
|
|
4658
5080
|
try {
|
|
4659
|
-
const response = await makeClient().agents.updateComputerConfig(
|
|
5081
|
+
const response = await makeClient().agents.updateComputerConfig(agentId3, { enabled });
|
|
4660
5082
|
const config = unwrap(response);
|
|
4661
5083
|
spinner.stop();
|
|
4662
5084
|
if (json) return jsonOut(config);
|
|
4663
5085
|
console.log(`
|
|
4664
|
-
${sym.ok} Persistent cloud computer ${enabled ? "enabled" : "disabled"} for agent ${c.id(
|
|
5086
|
+
${sym.ok} Persistent cloud computer ${enabled ? "enabled" : "disabled"} for agent ${c.id(agentId3)}`);
|
|
4665
5087
|
if (enabled) {
|
|
4666
|
-
console.log(c.dim(` Wake it now with: agc computer wake --agent ${
|
|
5088
|
+
console.log(c.dim(` Wake it now with: agc computer wake --agent ${agentId3}`));
|
|
4667
5089
|
}
|
|
4668
5090
|
} catch (error) {
|
|
4669
5091
|
spinner.stop();
|
|
@@ -4671,12 +5093,12 @@ ${sym.ok} Persistent cloud computer ${enabled ? "enabled" : "disabled"} for agen
|
|
|
4671
5093
|
process.exitCode = 1;
|
|
4672
5094
|
}
|
|
4673
5095
|
}
|
|
4674
|
-
async function lifecycleAction(action,
|
|
5096
|
+
async function lifecycleAction(action, agentId3, reason, json) {
|
|
4675
5097
|
const verb = action === "wake" ? "Waking" : action === "sleep" ? "Sleeping" : "Restarting";
|
|
4676
5098
|
const spinner = spin(`${verb} persistent cloud computer\u2026`);
|
|
4677
5099
|
try {
|
|
4678
5100
|
const client = makeClient();
|
|
4679
|
-
const response = action === "wake" ? await client.agents.wakeComputer(
|
|
5101
|
+
const response = action === "wake" ? await client.agents.wakeComputer(agentId3, reason ? { reason } : void 0) : action === "sleep" ? await client.agents.sleepComputer(agentId3, reason ? { reason } : void 0) : await client.agents.restartComputer(agentId3, reason ? { reason } : void 0);
|
|
4680
5102
|
const computer = unwrap(response);
|
|
4681
5103
|
spinner.stop();
|
|
4682
5104
|
if (json) return jsonOut(computer);
|
|
@@ -4695,9 +5117,9 @@ function addAgentOption(command) {
|
|
|
4695
5117
|
function computerCommand() {
|
|
4696
5118
|
const command = new import_commander18.Command("computer").description("Manage an agent's one persistent cloud computer");
|
|
4697
5119
|
addAgentOption(command.command("status").description("Show persistent cloud computer status")).option("--json", "Output as JSON").action(async (opts) => {
|
|
4698
|
-
let
|
|
5120
|
+
let agentId3;
|
|
4699
5121
|
try {
|
|
4700
|
-
|
|
5122
|
+
agentId3 = resolveAgentId(opts);
|
|
4701
5123
|
} catch (error) {
|
|
4702
5124
|
printError(error);
|
|
4703
5125
|
process.exitCode = 1;
|
|
@@ -4705,7 +5127,7 @@ function computerCommand() {
|
|
|
4705
5127
|
}
|
|
4706
5128
|
const spinner = spin("Fetching persistent cloud computer\u2026");
|
|
4707
5129
|
try {
|
|
4708
|
-
const computer = unwrap(await makeClient().agents.getComputer(
|
|
5130
|
+
const computer = unwrap(await makeClient().agents.getComputer(agentId3));
|
|
4709
5131
|
spinner.stop();
|
|
4710
5132
|
if (opts.json) return jsonOut(computer);
|
|
4711
5133
|
displayComputer(computer);
|
|
@@ -4747,10 +5169,10 @@ function computerCommand() {
|
|
|
4747
5169
|
});
|
|
4748
5170
|
}
|
|
4749
5171
|
addAgentOption(command.command("resize").description("Resize the persistent cloud computer")).option("--profile <profile>", `Resource profile: ${RESOURCE_PROFILES.join(" | ")}`).option("--mode <mode>", `Resource mode: ${RESOURCE_MODES.join(" | ")}`).option("--vcpu <count>", "Requested virtual CPU count").option("--cpu <count>", "Alias for --vcpu").option("--memory <gib>", "Requested memory in GiB").option("--storage <gib>", "Requested persistent storage in GiB").option("--gpu-type <type>", "GPU type, such as nvidia-h100").option("--gpu-count <count>", "GPU count (0 removes GPU allocation)").option("--json", "Output as JSON").action(async (opts) => {
|
|
4750
|
-
let
|
|
5172
|
+
let agentId3;
|
|
4751
5173
|
let resize;
|
|
4752
5174
|
try {
|
|
4753
|
-
|
|
5175
|
+
agentId3 = resolveAgentId(opts);
|
|
4754
5176
|
if (opts.profile && !RESOURCE_PROFILES.includes(opts.profile)) {
|
|
4755
5177
|
throw new Error(`--profile must be one of: ${RESOURCE_PROFILES.join(", ")}.`);
|
|
4756
5178
|
}
|
|
@@ -4787,7 +5209,7 @@ function computerCommand() {
|
|
|
4787
5209
|
}
|
|
4788
5210
|
const spinner = spin("Resizing persistent cloud computer\u2026");
|
|
4789
5211
|
try {
|
|
4790
|
-
const computer = unwrap(await makeClient().agents.resizeComputer(
|
|
5212
|
+
const computer = unwrap(await makeClient().agents.resizeComputer(agentId3, resize));
|
|
4791
5213
|
spinner.stop();
|
|
4792
5214
|
if (opts.json) return jsonOut(computer);
|
|
4793
5215
|
console.log(`
|
|
@@ -4802,10 +5224,10 @@ ${sym.ok} Persistent cloud computer resize requested`);
|
|
|
4802
5224
|
addAgentOption(
|
|
4803
5225
|
command.command("exec").description("Run a command in the persistent cloud computer").argument("<command...>", "Command and arguments to run")
|
|
4804
5226
|
).option("--cwd <path>", "Working directory").option("--timeout <seconds>", "Command timeout in seconds", "120").option("--json", "Output as JSON").action(async (commandParts, opts) => {
|
|
4805
|
-
let
|
|
5227
|
+
let agentId3;
|
|
4806
5228
|
let timeoutSeconds;
|
|
4807
5229
|
try {
|
|
4808
|
-
|
|
5230
|
+
agentId3 = resolveAgentId(opts);
|
|
4809
5231
|
timeoutSeconds = parseNumber(opts.timeout, "Timeout");
|
|
4810
5232
|
} catch (error) {
|
|
4811
5233
|
printError(error);
|
|
@@ -4814,7 +5236,7 @@ ${sym.ok} Persistent cloud computer resize requested`);
|
|
|
4814
5236
|
}
|
|
4815
5237
|
const spinner = spin("Running command in persistent cloud computer\u2026");
|
|
4816
5238
|
try {
|
|
4817
|
-
const result = unwrap(await makeClient().agents.execComputer(
|
|
5239
|
+
const result = unwrap(await makeClient().agents.execComputer(agentId3, {
|
|
4818
5240
|
command: commandParts.join(" "),
|
|
4819
5241
|
...opts.cwd && { cwd: opts.cwd },
|
|
4820
5242
|
...timeoutSeconds !== void 0 && { timeoutSeconds }
|
|
@@ -4834,10 +5256,10 @@ ${sym.ok} Persistent cloud computer resize requested`);
|
|
|
4834
5256
|
}
|
|
4835
5257
|
});
|
|
4836
5258
|
addAgentOption(command.command("events").description("List recent persistent cloud computer events")).option("--limit <count>", "Maximum events", "50").option("--json", "Output as JSON").action(async (opts) => {
|
|
4837
|
-
let
|
|
5259
|
+
let agentId3;
|
|
4838
5260
|
let limit;
|
|
4839
5261
|
try {
|
|
4840
|
-
|
|
5262
|
+
agentId3 = resolveAgentId(opts);
|
|
4841
5263
|
limit = parseNumber(opts.limit, "Limit", { integer: true });
|
|
4842
5264
|
} catch (error) {
|
|
4843
5265
|
printError(error);
|
|
@@ -4846,7 +5268,7 @@ ${sym.ok} Persistent cloud computer resize requested`);
|
|
|
4846
5268
|
}
|
|
4847
5269
|
const spinner = spin("Fetching persistent cloud computer events\u2026");
|
|
4848
5270
|
try {
|
|
4849
|
-
const events = unwrap(await makeClient().agents.listComputerEvents(
|
|
5271
|
+
const events = unwrap(await makeClient().agents.listComputerEvents(agentId3, limit));
|
|
4850
5272
|
spinner.stop();
|
|
4851
5273
|
if (opts.json) return jsonOut(events);
|
|
4852
5274
|
section(`Cloud computer events (${events.length})`);
|
|
@@ -4870,8 +5292,8 @@ ${sym.ok} Persistent cloud computer resize requested`);
|
|
|
4870
5292
|
|
|
4871
5293
|
// src/commands/library.ts
|
|
4872
5294
|
var import_commander19 = require("commander");
|
|
4873
|
-
var
|
|
4874
|
-
var
|
|
5295
|
+
var import_fs8 = require("fs");
|
|
5296
|
+
var import_path6 = require("path");
|
|
4875
5297
|
function libraryCommand() {
|
|
4876
5298
|
const command = new import_commander19.Command("library").alias("files").description("Upload, find, and manage files in your Commons library");
|
|
4877
5299
|
command.command("list", { isDefault: true }).alias("ls").description("List library items").option("--query <text>", "Search names and descriptions").option("--source <source>", "Filter by source").option("--session <sessionId>", "Filter by session").option("--favorites", "Show favorites only").option("--limit <n>", "Maximum items", "50").option("--json", "Output as JSON").action(async (opts) => {
|
|
@@ -4933,8 +5355,8 @@ function libraryCommand() {
|
|
|
4933
5355
|
throw new Error("--storage must be either s3 or ipfs.");
|
|
4934
5356
|
}
|
|
4935
5357
|
const files = paths.map((path) => ({
|
|
4936
|
-
data: new Blob([new Uint8Array((0,
|
|
4937
|
-
name: (0,
|
|
5358
|
+
data: new Blob([new Uint8Array((0, import_fs8.readFileSync)(path))]),
|
|
5359
|
+
name: (0, import_path6.basename)(path)
|
|
4938
5360
|
}));
|
|
4939
5361
|
const result = await makeClient().files.upload(files, {
|
|
4940
5362
|
agentId: opts.agent,
|
|
@@ -4993,7 +5415,7 @@ ${sym.ok} Uploaded ${result.data.length} file${result.data.length === 1 ? "" : "
|
|
|
4993
5415
|
|
|
4994
5416
|
// src/commands/projects.ts
|
|
4995
5417
|
var import_commander20 = require("commander");
|
|
4996
|
-
var
|
|
5418
|
+
var import_fs9 = require("fs");
|
|
4997
5419
|
function agentId(value) {
|
|
4998
5420
|
const resolved = value ?? loadConfig().defaultAgentId;
|
|
4999
5421
|
if (!resolved) {
|
|
@@ -5005,7 +5427,7 @@ function agentId(value) {
|
|
|
5005
5427
|
}
|
|
5006
5428
|
function projectFiles(path) {
|
|
5007
5429
|
if (!path) return void 0;
|
|
5008
|
-
const parsed = JSON.parse((0,
|
|
5430
|
+
const parsed = JSON.parse((0, import_fs9.readFileSync)(path, "utf8"));
|
|
5009
5431
|
const files = Array.isArray(parsed) ? parsed : parsed.files;
|
|
5010
5432
|
if (!Array.isArray(files)) {
|
|
5011
5433
|
throw new Error("The files document must be an array or an object with a files array.");
|
|
@@ -5166,8 +5588,160 @@ ${sym.ok} Project published.`);
|
|
|
5166
5588
|
return command;
|
|
5167
5589
|
}
|
|
5168
5590
|
|
|
5169
|
-
// src/commands/
|
|
5591
|
+
// src/commands/arcade.ts
|
|
5170
5592
|
var import_commander21 = require("commander");
|
|
5593
|
+
var import_fs10 = require("fs");
|
|
5594
|
+
var import_path7 = require("path");
|
|
5595
|
+
function agentId2(value) {
|
|
5596
|
+
const resolved = value ?? loadConfig().defaultAgentId;
|
|
5597
|
+
if (!resolved) {
|
|
5598
|
+
throw new Error(
|
|
5599
|
+
"Specify --agent <agentId> or set a default with `agc config set defaultAgentId <id>`."
|
|
5600
|
+
);
|
|
5601
|
+
}
|
|
5602
|
+
return resolved;
|
|
5603
|
+
}
|
|
5604
|
+
var SOURCE_EXTENSIONS = /\.(html|css|js|mjs|cjs|ts|tsx|jsx|json|svg|md|txt)$/i;
|
|
5605
|
+
function readGameDirectory(root) {
|
|
5606
|
+
const files = [];
|
|
5607
|
+
const walk = (directory) => {
|
|
5608
|
+
for (const name of (0, import_fs10.readdirSync)(directory)) {
|
|
5609
|
+
if (name.startsWith(".") || name === "node_modules" || name === "dist") continue;
|
|
5610
|
+
const full = (0, import_path7.join)(directory, name);
|
|
5611
|
+
if ((0, import_fs10.statSync)(full).isDirectory()) walk(full);
|
|
5612
|
+
else if (SOURCE_EXTENSIONS.test(name)) {
|
|
5613
|
+
files.push({
|
|
5614
|
+
path: (0, import_path7.relative)(root, full).split(import_path7.sep).join("/"),
|
|
5615
|
+
content: (0, import_fs10.readFileSync)(full, "utf8")
|
|
5616
|
+
});
|
|
5617
|
+
}
|
|
5618
|
+
}
|
|
5619
|
+
};
|
|
5620
|
+
walk(root);
|
|
5621
|
+
if (!files.length) throw new Error(`No game source files found in ${root}.`);
|
|
5622
|
+
return files;
|
|
5623
|
+
}
|
|
5624
|
+
async function run(label, work) {
|
|
5625
|
+
const spinner = spin(label);
|
|
5626
|
+
try {
|
|
5627
|
+
const result = await work();
|
|
5628
|
+
spinner.stop();
|
|
5629
|
+
return result;
|
|
5630
|
+
} catch (error) {
|
|
5631
|
+
spinner.stop();
|
|
5632
|
+
printError(error);
|
|
5633
|
+
process.exit(1);
|
|
5634
|
+
}
|
|
5635
|
+
}
|
|
5636
|
+
function arcadeCommand() {
|
|
5637
|
+
const command = new import_commander21.Command("arcade").description(
|
|
5638
|
+
"Build, test, and publish Common Arcade games in your agent owner's account"
|
|
5639
|
+
);
|
|
5640
|
+
command.command("status").description("Check whether this platform is connected to Common Arcade").option("--agent <agentId>", "Agent ID").option("--json", "Output as JSON").action(async (opts) => {
|
|
5641
|
+
const result = await run(
|
|
5642
|
+
"Checking Arcade connection\u2026",
|
|
5643
|
+
() => makeClient().arcade.status(agentId2(opts.agent))
|
|
5644
|
+
);
|
|
5645
|
+
if (opts.json) return jsonOut(result.data);
|
|
5646
|
+
console.log(
|
|
5647
|
+
result.data.connected ? `${sym.ok} Connected to Common Arcade.` : `${sym.fail} Common Arcade is not connected on this deployment.`
|
|
5648
|
+
);
|
|
5649
|
+
});
|
|
5650
|
+
command.command("list", { isDefault: true }).alias("ls").description("List Arcade game projects").option("--agent <agentId>", "Agent ID").option("--json", "Output as JSON").action(async (opts) => {
|
|
5651
|
+
const result = await run(
|
|
5652
|
+
"Fetching Arcade projects\u2026",
|
|
5653
|
+
() => makeClient().arcade.list(agentId2(opts.agent))
|
|
5654
|
+
);
|
|
5655
|
+
if (opts.json) return jsonOut(result.data);
|
|
5656
|
+
section(`Arcade projects (${result.data.length})`);
|
|
5657
|
+
table(
|
|
5658
|
+
result.data.map((project) => ({
|
|
5659
|
+
ID: project.projectId,
|
|
5660
|
+
Title: project.title ?? "",
|
|
5661
|
+
Published: project.isPublished ? "yes" : "no",
|
|
5662
|
+
Studio: project.studioUrl
|
|
5663
|
+
})),
|
|
5664
|
+
["ID", "Title", "Published", "Studio"]
|
|
5665
|
+
);
|
|
5666
|
+
});
|
|
5667
|
+
command.command("get <projectId>").description("Show an Arcade game project").option("--agent <agentId>", "Agent ID").option("--json", "Output as JSON").action(async (projectId, opts) => {
|
|
5668
|
+
const result = await run(
|
|
5669
|
+
"Fetching Arcade project\u2026",
|
|
5670
|
+
() => makeClient().arcade.get(agentId2(opts.agent), projectId)
|
|
5671
|
+
);
|
|
5672
|
+
if (opts.json) return jsonOut(result.data);
|
|
5673
|
+
const project = result.data;
|
|
5674
|
+
section(project.title ?? project.projectId);
|
|
5675
|
+
detail([
|
|
5676
|
+
["Project ID", c.id(project.projectId)],
|
|
5677
|
+
["Revision", String(project.revision)],
|
|
5678
|
+
["Files", project.document.files.map((file) => file.path).join(", ")],
|
|
5679
|
+
["Published", project.isPublished ? "yes" : "no"],
|
|
5680
|
+
["Thumbnail", project.hasThumbnail ? "yes" : "no"],
|
|
5681
|
+
["Studio", project.studioUrl]
|
|
5682
|
+
]);
|
|
5683
|
+
});
|
|
5684
|
+
command.command("create").description("Create an Arcade game project").requiredOption("--title <title>", "Game title").option("--description <text>", "How to play").option("--agent <agentId>", "Agent ID").option("--json", "Output as JSON").action(async (opts) => {
|
|
5685
|
+
const result = await run(
|
|
5686
|
+
"Creating Arcade project\u2026",
|
|
5687
|
+
() => makeClient().arcade.create(agentId2(opts.agent), {
|
|
5688
|
+
title: opts.title,
|
|
5689
|
+
description: opts.description
|
|
5690
|
+
})
|
|
5691
|
+
);
|
|
5692
|
+
if (opts.json) return jsonOut(result.data);
|
|
5693
|
+
console.log(`
|
|
5694
|
+
${sym.ok} Arcade project created.`);
|
|
5695
|
+
detail([
|
|
5696
|
+
["Project ID", c.id(result.data.projectId)],
|
|
5697
|
+
["Studio", result.data.studioUrl]
|
|
5698
|
+
]);
|
|
5699
|
+
});
|
|
5700
|
+
command.command("push <projectId> <directory>").description("Upload a local game directory to an Arcade project").option("--agent <agentId>", "Agent ID").option("--entry <file>", "HTML entry file (default: index.html)").option("--runtime <file>", "Authoritative rules file, required to publish").option("--thumbnail <url>", "HTTPS thumbnail URL, required to publish").option("--replace", "Remove project files that are not in the directory").option("--json", "Output as JSON").action(async (projectId, directory, opts) => {
|
|
5701
|
+
const files = readGameDirectory(directory);
|
|
5702
|
+
const result = await run(
|
|
5703
|
+
`Uploading ${files.length} files\u2026`,
|
|
5704
|
+
() => makeClient().arcade.write(agentId2(opts.agent), projectId, {
|
|
5705
|
+
files,
|
|
5706
|
+
replaceFiles: Boolean(opts.replace),
|
|
5707
|
+
entryFile: opts.entry,
|
|
5708
|
+
thumbnail: opts.thumbnail,
|
|
5709
|
+
runtime: opts.runtime ? { entryFile: opts.runtime } : void 0
|
|
5710
|
+
})
|
|
5711
|
+
);
|
|
5712
|
+
if (opts.json) return jsonOut(result.data);
|
|
5713
|
+
console.log(`${sym.ok} Saved revision ${result.data.revision}.`);
|
|
5714
|
+
detail([["Studio", result.data.studioUrl]]);
|
|
5715
|
+
});
|
|
5716
|
+
command.command("test <projectId>").description("Run Arcade's validation and runtime harness").option("--agent <agentId>", "Agent ID").option("--steps <n>", "Steps to simulate", (value) => Number(value)).option("--seed <seed>", "Deterministic seed").action(async (projectId, opts) => {
|
|
5717
|
+
const result = await run(
|
|
5718
|
+
"Testing game\u2026",
|
|
5719
|
+
() => makeClient().arcade.test(agentId2(opts.agent), projectId, {
|
|
5720
|
+
steps: opts.steps,
|
|
5721
|
+
seed: opts.seed
|
|
5722
|
+
})
|
|
5723
|
+
);
|
|
5724
|
+
jsonOut(result.data);
|
|
5725
|
+
});
|
|
5726
|
+
command.command("publish <projectId>").description("Publish an Arcade game as an immutable release").option("--agent <agentId>", "Agent ID").option("--json", "Output as JSON").action(async (projectId, opts) => {
|
|
5727
|
+
const result = await run(
|
|
5728
|
+
"Publishing game\u2026",
|
|
5729
|
+
() => makeClient().arcade.publish(agentId2(opts.agent), projectId)
|
|
5730
|
+
);
|
|
5731
|
+
if (opts.json) return jsonOut(result.data);
|
|
5732
|
+
console.log(`
|
|
5733
|
+
${sym.ok} Game published.`);
|
|
5734
|
+
detail([
|
|
5735
|
+
["Release", result.data.releaseId],
|
|
5736
|
+
["Play", result.data.gameUrl],
|
|
5737
|
+
["Studio", result.data.studioUrl]
|
|
5738
|
+
]);
|
|
5739
|
+
});
|
|
5740
|
+
return command;
|
|
5741
|
+
}
|
|
5742
|
+
|
|
5743
|
+
// src/commands/api-keys.ts
|
|
5744
|
+
var import_commander22 = require("commander");
|
|
5171
5745
|
async function resolveProject(projectId) {
|
|
5172
5746
|
const projects = (await makeClient().developer.listProjects()).data;
|
|
5173
5747
|
const project = projectId ? projects.find((candidate) => candidate.id === projectId) : projects[0];
|
|
@@ -5179,7 +5753,7 @@ async function resolveProject(projectId) {
|
|
|
5179
5753
|
return project;
|
|
5180
5754
|
}
|
|
5181
5755
|
function apiKeysCommand() {
|
|
5182
|
-
const command = new
|
|
5756
|
+
const command = new import_commander22.Command("keys").alias("api-keys").description("Create and manage project-scoped developer API keys");
|
|
5183
5757
|
command.command("list", { isDefault: true }).alias("ls").description("List API keys for a developer project").option("--project <projectId>", "Developer project ID (defaults to newest)").option("--json", "Output as JSON").action(async (opts) => {
|
|
5184
5758
|
const spinner = spin("Fetching developer keys\u2026");
|
|
5185
5759
|
try {
|
|
@@ -5333,6 +5907,96 @@ ${sym.ok} Developer project created.`);
|
|
|
5333
5907
|
return command;
|
|
5334
5908
|
}
|
|
5335
5909
|
|
|
5910
|
+
// src/commands/provenance.ts
|
|
5911
|
+
var import_commander23 = require("commander");
|
|
5912
|
+
function printTrajectory(trajectory) {
|
|
5913
|
+
const sources = trajectory.events.flatMap(
|
|
5914
|
+
(event) => event.metadata?.lineage?.sources ?? []
|
|
5915
|
+
);
|
|
5916
|
+
const delegates = trajectory.events.flatMap(
|
|
5917
|
+
(event) => event.metadata?.lineage?.delegation ? [event.metadata.lineage.delegation] : []
|
|
5918
|
+
);
|
|
5919
|
+
const matches = trajectory.events.flatMap(
|
|
5920
|
+
(event) => event.metadata?.lineage?.library?.results ?? []
|
|
5921
|
+
);
|
|
5922
|
+
section("Provenance report");
|
|
5923
|
+
detail([
|
|
5924
|
+
["Runs", String(trajectory.summary?.runs ?? trajectory.runs?.length ?? 0)],
|
|
5925
|
+
[
|
|
5926
|
+
"Events",
|
|
5927
|
+
String(trajectory.summary?.events ?? trajectory.events?.length ?? 0)
|
|
5928
|
+
],
|
|
5929
|
+
[
|
|
5930
|
+
"Model / tool calls",
|
|
5931
|
+
`${trajectory.summary?.modelCalls ?? 0} / ${trajectory.summary?.toolCalls ?? 0}`
|
|
5932
|
+
],
|
|
5933
|
+
["Sources", String(sources.length)],
|
|
5934
|
+
["Agent delegations", String(delegates.length)],
|
|
5935
|
+
["Library matches", String(matches.length)],
|
|
5936
|
+
["Dropped events", String(trajectory.summary?.droppedEvents ?? 0)]
|
|
5937
|
+
]);
|
|
5938
|
+
if (sources.length) {
|
|
5939
|
+
section("Web sources");
|
|
5940
|
+
for (const source of sources)
|
|
5941
|
+
console.log(
|
|
5942
|
+
` ${c.primary(source.domain)} ${source.title ?? ""}
|
|
5943
|
+
${c.dim(source.url)}`
|
|
5944
|
+
);
|
|
5945
|
+
}
|
|
5946
|
+
if (delegates.length) {
|
|
5947
|
+
section("Agent contributions");
|
|
5948
|
+
for (const item of delegates)
|
|
5949
|
+
console.log(
|
|
5950
|
+
` ${item.fromAgentId ?? "workflow"} \u2192 ${c.primary(item.toAgentId)} ${c.dim(item.role ?? "")}`
|
|
5951
|
+
);
|
|
5952
|
+
}
|
|
5953
|
+
if (matches.length) {
|
|
5954
|
+
section("Library matches");
|
|
5955
|
+
for (const item of matches)
|
|
5956
|
+
console.log(
|
|
5957
|
+
` ${c.primary(`${item.percentageMatch}%`)} ${item.name ?? item.itemId} ${c.dim(`#${item.rank}`)}`
|
|
5958
|
+
);
|
|
5959
|
+
}
|
|
5960
|
+
}
|
|
5961
|
+
function provenanceCommand() {
|
|
5962
|
+
const cmd = new import_commander23.Command("provenance").description(
|
|
5963
|
+
"Inspect and export provenance and attribution trails"
|
|
5964
|
+
);
|
|
5965
|
+
cmd.command("session <sessionId>").description("Show a plain-language sources and contributors report").option("--json", "Output the complete machine-readable trajectory").action(async (sessionId, opts) => {
|
|
5966
|
+
const spinner = spin("Building provenance report\u2026");
|
|
5967
|
+
try {
|
|
5968
|
+
const { data } = await makeClient().provenance.session(sessionId);
|
|
5969
|
+
spinner.stop();
|
|
5970
|
+
if (opts.json) return jsonOut(data);
|
|
5971
|
+
printTrajectory(data);
|
|
5972
|
+
} catch (error) {
|
|
5973
|
+
spinner.stop();
|
|
5974
|
+
printError(error);
|
|
5975
|
+
process.exitCode = 1;
|
|
5976
|
+
}
|
|
5977
|
+
});
|
|
5978
|
+
cmd.command("workflow <executionId>").description("Show provenance for a workflow execution").option("--json", "Output the complete machine-readable trajectory").action(async (executionId, opts) => {
|
|
5979
|
+
const spinner = spin("Building workflow provenance report\u2026");
|
|
5980
|
+
try {
|
|
5981
|
+
const { data } = await makeClient().provenance.scope(
|
|
5982
|
+
"workflow",
|
|
5983
|
+
executionId
|
|
5984
|
+
);
|
|
5985
|
+
spinner.stop();
|
|
5986
|
+
if (opts.json) return jsonOut(data);
|
|
5987
|
+
printTrajectory(data);
|
|
5988
|
+
} catch (error) {
|
|
5989
|
+
spinner.stop();
|
|
5990
|
+
printError(error);
|
|
5991
|
+
process.exitCode = 1;
|
|
5992
|
+
}
|
|
5993
|
+
});
|
|
5994
|
+
cmd.command("bundle <traceId>").description("Export the standards-based EAA provenance bundle as JSON").action(
|
|
5995
|
+
async (traceId) => jsonOut((await makeClient().provenance.bundle(traceId)).data)
|
|
5996
|
+
);
|
|
5997
|
+
return cmd;
|
|
5998
|
+
}
|
|
5999
|
+
|
|
5336
6000
|
// src/bin.ts
|
|
5337
6001
|
async function interactiveMenu() {
|
|
5338
6002
|
banner();
|
|
@@ -5340,9 +6004,15 @@ async function interactiveMenu() {
|
|
|
5340
6004
|
const isSetup = !!((cfg.accessToken || cfg.apiKey || cfg.sessionToken) && (cfg.userId || cfg.initiator));
|
|
5341
6005
|
if (!isSetup) {
|
|
5342
6006
|
console.log(c.bold(" Welcome to Agent Commons CLI!"));
|
|
5343
|
-
console.log(
|
|
5344
|
-
|
|
5345
|
-
|
|
6007
|
+
console.log(
|
|
6008
|
+
c.dim(
|
|
6009
|
+
" Looks like this is your first time here \u2014 let's get you set up.\n"
|
|
6010
|
+
)
|
|
6011
|
+
);
|
|
6012
|
+
console.log(
|
|
6013
|
+
` ${sym.arrow} Running ${c.bold("agc login")} to configure your credentials\u2026
|
|
6014
|
+
`
|
|
6015
|
+
);
|
|
5346
6016
|
runSubcommand(["login"]);
|
|
5347
6017
|
return;
|
|
5348
6018
|
}
|
|
@@ -5353,7 +6023,11 @@ async function interactiveMenu() {
|
|
|
5353
6023
|
const action = await select("What would you like to do?", [
|
|
5354
6024
|
{ label: "Chat with an agent", value: "chat", hint: "agc chat" },
|
|
5355
6025
|
{ label: "Run an agent (one-shot)", value: "run", hint: "agc run" },
|
|
5356
|
-
{
|
|
6026
|
+
{
|
|
6027
|
+
label: "Manage an agent cloud computer",
|
|
6028
|
+
value: "computer",
|
|
6029
|
+
hint: "agc computer status"
|
|
6030
|
+
},
|
|
5357
6031
|
{ label: "View sessions", value: "sessions", hint: "agc sessions list" },
|
|
5358
6032
|
{ label: "Manage agents", value: "agents", hint: "agc agents list" },
|
|
5359
6033
|
{ label: "Tasks", value: "tasks", hint: "agc task list" },
|
|
@@ -5373,19 +6047,19 @@ async function interactiveMenu() {
|
|
|
5373
6047
|
process.exit(0);
|
|
5374
6048
|
}
|
|
5375
6049
|
const needsAgent = action === "chat" || action === "run" || action === "computer";
|
|
5376
|
-
const
|
|
5377
|
-
if (needsAgent && !
|
|
6050
|
+
const agentId3 = needsAgent ? cfg.defaultAgentId ?? await pickAgentInteractively(action) : void 0;
|
|
6051
|
+
if (needsAgent && !agentId3) return;
|
|
5378
6052
|
if (action === "run") {
|
|
5379
6053
|
const prompt = await askPrompt("Enter your prompt:");
|
|
5380
6054
|
if (!prompt) return;
|
|
5381
|
-
runSubcommand(["run", "--agent",
|
|
6055
|
+
runSubcommand(["run", "--agent", agentId3, prompt]);
|
|
5382
6056
|
return;
|
|
5383
6057
|
}
|
|
5384
6058
|
const commandMap = {
|
|
5385
|
-
chat: ["chat", "--agent",
|
|
6059
|
+
chat: ["chat", "--agent", agentId3],
|
|
5386
6060
|
run: [],
|
|
5387
6061
|
// handled above
|
|
5388
|
-
computer: ["computer", "status", "--agent",
|
|
6062
|
+
computer: ["computer", "status", "--agent", agentId3],
|
|
5389
6063
|
sessions: ["sessions", "list"],
|
|
5390
6064
|
agents: ["agents", "list"],
|
|
5391
6065
|
tasks: ["task", "list"],
|
|
@@ -5406,7 +6080,10 @@ async function interactiveMenu() {
|
|
|
5406
6080
|
async function askPrompt(question) {
|
|
5407
6081
|
const { createInterface: createInterface3 } = await import("readline");
|
|
5408
6082
|
return new Promise((resolve2) => {
|
|
5409
|
-
const rl = createInterface3({
|
|
6083
|
+
const rl = createInterface3({
|
|
6084
|
+
input: process.stdin,
|
|
6085
|
+
output: process.stdout
|
|
6086
|
+
});
|
|
5410
6087
|
process.stdout.write(`
|
|
5411
6088
|
${c.bold(question)}
|
|
5412
6089
|
${c.primary("\u203A")} `);
|
|
@@ -5434,26 +6111,35 @@ async function pickAgentInteractively(action) {
|
|
|
5434
6111
|
spinner.stop();
|
|
5435
6112
|
} catch {
|
|
5436
6113
|
spinner.stop();
|
|
5437
|
-
console.log(
|
|
6114
|
+
console.log(
|
|
6115
|
+
`
|
|
5438
6116
|
${c.warn("\u26A0")} Could not fetch agents. Check your sign-in and connection.
|
|
5439
|
-
`
|
|
6117
|
+
`
|
|
6118
|
+
);
|
|
5440
6119
|
return null;
|
|
5441
6120
|
}
|
|
5442
6121
|
if (agents.length === 0) {
|
|
5443
6122
|
console.log(`
|
|
5444
6123
|
${c.warn("\u26A0")} You don't have any agents yet.
|
|
5445
6124
|
`);
|
|
5446
|
-
const choice = await select(
|
|
5447
|
-
|
|
5448
|
-
|
|
5449
|
-
|
|
6125
|
+
const choice = await select(
|
|
6126
|
+
"What would you like to do?",
|
|
6127
|
+
[
|
|
6128
|
+
{
|
|
6129
|
+
label: "Create a new agent now",
|
|
6130
|
+
value: "create",
|
|
6131
|
+
hint: "agc agents create"
|
|
6132
|
+
},
|
|
6133
|
+
{ label: "Go back", value: "cancel" }
|
|
6134
|
+
]
|
|
6135
|
+
);
|
|
5450
6136
|
if (choice === "create") {
|
|
5451
6137
|
runSubcommand(["agents", "create"]);
|
|
5452
6138
|
}
|
|
5453
6139
|
return null;
|
|
5454
6140
|
}
|
|
5455
6141
|
console.log();
|
|
5456
|
-
const
|
|
6142
|
+
const agentId3 = await select(
|
|
5457
6143
|
action === "computer" ? "Choose the agent whose cloud computer you want to manage:" : `Choose an agent to ${action} with:`,
|
|
5458
6144
|
agents.map((a) => ({
|
|
5459
6145
|
label: a.name,
|
|
@@ -5466,15 +6152,17 @@ async function pickAgentInteractively(action) {
|
|
|
5466
6152
|
{ label: "No \u2014 just this once", value: false }
|
|
5467
6153
|
]);
|
|
5468
6154
|
if (saveDefault) {
|
|
5469
|
-
saveConfig({ defaultAgentId:
|
|
5470
|
-
const chosen = agents.find((a) => a.agentId ===
|
|
5471
|
-
console.log(
|
|
5472
|
-
`)
|
|
6155
|
+
saveConfig({ defaultAgentId: agentId3 });
|
|
6156
|
+
const chosen = agents.find((a) => a.agentId === agentId3);
|
|
6157
|
+
console.log(
|
|
6158
|
+
` ${sym.ok} ${c.dim("Default agent set to")} ${c.bold(chosen?.name ?? agentId3)}
|
|
6159
|
+
`
|
|
6160
|
+
);
|
|
5473
6161
|
}
|
|
5474
|
-
return
|
|
6162
|
+
return agentId3;
|
|
5475
6163
|
}
|
|
5476
|
-
var program = new
|
|
5477
|
-
program.name("agc").description("Agent Commons CLI \u2014 interact with the Agent Commons platform").version("0.
|
|
6164
|
+
var program = new import_commander24.Command();
|
|
6165
|
+
program.name("agc").description("Agent Commons CLI \u2014 interact with the Agent Commons platform").version("0.6.1", "-v, --version").showHelpAfterError("(run `agc --help` for usage)").configureHelp({
|
|
5478
6166
|
sortOptions: true,
|
|
5479
6167
|
sortSubcommands: true
|
|
5480
6168
|
}).addHelpText(
|
|
@@ -5492,7 +6180,8 @@ Docs: https://docs.agentcommons.io/docs/cli
|
|
|
5492
6180
|
await interactiveMenu();
|
|
5493
6181
|
});
|
|
5494
6182
|
program.hook("preAction", async (_thisCommand, actionCommand) => {
|
|
5495
|
-
if (actionCommand.name() === "login" || actionCommand.name() === "logout")
|
|
6183
|
+
if (actionCommand.name() === "login" || actionCommand.name() === "logout")
|
|
6184
|
+
return;
|
|
5496
6185
|
await ensureAccessToken();
|
|
5497
6186
|
});
|
|
5498
6187
|
program.addCommand(loginCommand());
|
|
@@ -5505,7 +6194,9 @@ program.addCommand(toolsCommand());
|
|
|
5505
6194
|
program.addCommand(connectionsCommand());
|
|
5506
6195
|
program.addCommand(libraryCommand());
|
|
5507
6196
|
program.addCommand(projectsCommand());
|
|
6197
|
+
program.addCommand(arcadeCommand());
|
|
5508
6198
|
program.addCommand(apiKeysCommand());
|
|
6199
|
+
program.addCommand(provenanceCommand());
|
|
5509
6200
|
program.addCommand(workflowCommand());
|
|
5510
6201
|
program.addCommand(taskCommand());
|
|
5511
6202
|
program.addCommand(runCommand());
|
|
@@ -5530,8 +6221,10 @@ program.on("command:*", () => {
|
|
|
5530
6221
|
process.exit(1);
|
|
5531
6222
|
});
|
|
5532
6223
|
program.parseAsync(process.argv).catch((error) => {
|
|
5533
|
-
console.error(
|
|
6224
|
+
console.error(
|
|
6225
|
+
`
|
|
5534
6226
|
${sym.fail} ${c.error(error instanceof Error ? error.message : String(error))}
|
|
5535
|
-
`
|
|
6227
|
+
`
|
|
6228
|
+
);
|
|
5536
6229
|
process.exitCode = 1;
|
|
5537
6230
|
});
|