@agent-commons/cli 0.1.18 → 0.2.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/dist/bin.js +498 -63
- package/package.json +2 -2
package/dist/bin.js
CHANGED
|
@@ -45,10 +45,14 @@ var CONFIG_DIR = (0, import_path.join)((0, import_os.homedir)(), ".agc");
|
|
|
45
45
|
var CONFIG_FILE = (0, import_path.join)(CONFIG_DIR, "config.json");
|
|
46
46
|
var DEFAULT_API_URL = process.env.AGC_API_URL ?? "https://api.agentcommons.io";
|
|
47
47
|
var DEFAULT_APP_URL = "https://www.agentcommons.io";
|
|
48
|
+
var DEFAULT_IDENTITY_URL = process.env.COMMONS_IDENTITY_URL ?? "https://auth.agentcommons.io";
|
|
49
|
+
var DEFAULT_IDENTITY_CLIENT_ID = process.env.COMMONS_IDENTITY_CLIENT_ID ?? "commons-cli";
|
|
48
50
|
function loadConfig() {
|
|
49
51
|
const fromEnv = {
|
|
50
52
|
...process.env.AGC_API_URL && { apiUrl: process.env.AGC_API_URL },
|
|
51
53
|
...process.env.AGC_API_KEY && { apiKey: process.env.AGC_API_KEY },
|
|
54
|
+
...process.env.COMMONS_ACCESS_TOKEN && { accessToken: process.env.COMMONS_ACCESS_TOKEN },
|
|
55
|
+
...process.env.COMMONS_IDENTITY_URL && { identityUrl: process.env.COMMONS_IDENTITY_URL },
|
|
52
56
|
...process.env.AGC_INITIATOR && { initiator: process.env.AGC_INITIATOR },
|
|
53
57
|
...process.env.AGC_AGENT_ID && { defaultAgentId: process.env.AGC_AGENT_ID }
|
|
54
58
|
};
|
|
@@ -61,6 +65,8 @@ function loadConfig() {
|
|
|
61
65
|
}
|
|
62
66
|
return {
|
|
63
67
|
apiUrl: DEFAULT_API_URL,
|
|
68
|
+
identityUrl: DEFAULT_IDENTITY_URL,
|
|
69
|
+
identityClientId: DEFAULT_IDENTITY_CLIENT_ID,
|
|
64
70
|
...fromFile,
|
|
65
71
|
...fromEnv
|
|
66
72
|
};
|
|
@@ -73,17 +79,65 @@ function saveConfig(updates) {
|
|
|
73
79
|
}
|
|
74
80
|
function clearConfig() {
|
|
75
81
|
if ((0, import_fs.existsSync)(CONFIG_FILE)) {
|
|
76
|
-
(0, import_fs.writeFileSync)(
|
|
82
|
+
(0, import_fs.writeFileSync)(
|
|
83
|
+
CONFIG_FILE,
|
|
84
|
+
JSON.stringify(
|
|
85
|
+
{
|
|
86
|
+
apiUrl: DEFAULT_API_URL,
|
|
87
|
+
identityUrl: DEFAULT_IDENTITY_URL,
|
|
88
|
+
identityClientId: DEFAULT_IDENTITY_CLIENT_ID
|
|
89
|
+
},
|
|
90
|
+
null,
|
|
91
|
+
2
|
|
92
|
+
),
|
|
93
|
+
{ mode: 384 }
|
|
94
|
+
);
|
|
77
95
|
}
|
|
78
96
|
}
|
|
79
97
|
function makeClient(overrides) {
|
|
80
98
|
const cfg = { ...loadConfig(), ...overrides };
|
|
81
99
|
return new import_sdk.CommonsClient({
|
|
82
100
|
baseUrl: cfg.apiUrl,
|
|
83
|
-
apiKey: cfg.apiKey,
|
|
84
|
-
initiator: cfg.initiator
|
|
101
|
+
apiKey: cfg.accessToken ?? cfg.apiKey,
|
|
102
|
+
initiator: cfg.userId ?? cfg.initiator
|
|
85
103
|
});
|
|
86
104
|
}
|
|
105
|
+
function decodeJwtPayload(token) {
|
|
106
|
+
const [, payload] = token.split(".");
|
|
107
|
+
if (!payload) return {};
|
|
108
|
+
try {
|
|
109
|
+
return JSON.parse(Buffer.from(payload, "base64url").toString("utf8"));
|
|
110
|
+
} catch {
|
|
111
|
+
return {};
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
async function ensureAccessToken() {
|
|
115
|
+
const cfg = loadConfig();
|
|
116
|
+
if (cfg.apiKey && !cfg.sessionToken) return cfg;
|
|
117
|
+
if (cfg.accessToken && cfg.accessTokenExpiresAt && cfg.accessTokenExpiresAt > Date.now() + 3e4) {
|
|
118
|
+
return cfg;
|
|
119
|
+
}
|
|
120
|
+
if (!cfg.sessionToken || !cfg.identityUrl) return cfg;
|
|
121
|
+
const response = await fetch(
|
|
122
|
+
`${cfg.identityUrl.replace(/\/$/, "")}/api/auth/token`,
|
|
123
|
+
{ headers: { Authorization: `Bearer ${cfg.sessionToken}` } }
|
|
124
|
+
);
|
|
125
|
+
if (!response.ok) {
|
|
126
|
+
throw new Error("Your Commons login has expired. Run `agc login` again.");
|
|
127
|
+
}
|
|
128
|
+
const data = await response.json();
|
|
129
|
+
if (!data.token) throw new Error("Commons Identity did not return an access token.");
|
|
130
|
+
const claims = decodeJwtPayload(data.token);
|
|
131
|
+
const updates = {
|
|
132
|
+
accessToken: data.token,
|
|
133
|
+
accessTokenExpiresAt: typeof claims.exp === "number" ? claims.exp * 1e3 : Date.now() + 10 * 60 * 1e3,
|
|
134
|
+
userId: typeof claims.sub === "string" ? claims.sub : cfg.userId,
|
|
135
|
+
workspaceId: typeof claims.workspace_id === "string" ? claims.workspace_id : cfg.workspaceId,
|
|
136
|
+
initiator: typeof claims.sub === "string" ? claims.sub : cfg.initiator
|
|
137
|
+
};
|
|
138
|
+
saveConfig(updates);
|
|
139
|
+
return { ...cfg, ...updates };
|
|
140
|
+
}
|
|
87
141
|
|
|
88
142
|
// src/ui.ts
|
|
89
143
|
var import_chalk = __toESM(require("chalk"));
|
|
@@ -106,7 +160,7 @@ var sym = {
|
|
|
106
160
|
bullet: import_chalk.default.dim("\u2022"),
|
|
107
161
|
dot: import_chalk.default.dim("\xB7")
|
|
108
162
|
};
|
|
109
|
-
function banner(version = "0.1
|
|
163
|
+
function banner(version = "0.2.1") {
|
|
110
164
|
const line = import_chalk.default.cyan(" \u2500".padEnd(2) + "\u2500".repeat(44));
|
|
111
165
|
console.log("");
|
|
112
166
|
console.log(line);
|
|
@@ -282,14 +336,14 @@ function prompt(question, hidden = false) {
|
|
|
282
336
|
}
|
|
283
337
|
function loginCommand() {
|
|
284
338
|
const cmd = new import_commander.Command("login").description("Configure API credentials");
|
|
285
|
-
cmd.option("--api-url <url>", "API base URL", DEFAULT_API_URL).option("--api-key <key>", "API key (or set AGC_API_KEY env var)").option("--initiator <id>", "User/initiator ID (advanced \u2014 usually auto-detected)").action(async (opts) => {
|
|
339
|
+
cmd.option("--api-url <url>", "API base URL", DEFAULT_API_URL).option("--identity-url <url>", "Commons Identity URL", DEFAULT_IDENTITY_URL).option("--api-key <key>", "API key (or set AGC_API_KEY env var)").option("--initiator <id>", "User/initiator ID (advanced \u2014 usually auto-detected)").action(async (opts) => {
|
|
286
340
|
try {
|
|
287
341
|
const current = loadConfig();
|
|
288
342
|
const isFirstRun = !(0, import_fs2.existsSync)(CONFIG_FILE2);
|
|
289
343
|
banner();
|
|
290
344
|
if (isFirstRun) {
|
|
291
345
|
console.log(c.bold(" Welcome to Agent Commons CLI!"));
|
|
292
|
-
console.log(c.dim("
|
|
346
|
+
console.log(c.dim(" Sign in once with your Commons account to get started.\n"));
|
|
293
347
|
} else {
|
|
294
348
|
console.log(c.bold(" Update your credentials"));
|
|
295
349
|
console.log(c.dim(" Press Enter to keep existing values.\n"));
|
|
@@ -308,7 +362,74 @@ function loginCommand() {
|
|
|
308
362
|
}
|
|
309
363
|
const appUrl = apiUrl.includes("localhost") ? "http://localhost:3000" : DEFAULT_APP_URL;
|
|
310
364
|
const apiKeysUrl = `${appUrl}/settings/api-keys`;
|
|
311
|
-
|
|
365
|
+
if (!opts.apiKey) {
|
|
366
|
+
step(1, 1, "Commons account");
|
|
367
|
+
const identityUrl = String(opts.identityUrl).replace(/\/$/, "");
|
|
368
|
+
const clientId = DEFAULT_IDENTITY_CLIENT_ID;
|
|
369
|
+
const deviceResponse = await fetch(`${identityUrl}/api/auth/device/code`, {
|
|
370
|
+
method: "POST",
|
|
371
|
+
headers: { "Content-Type": "application/json" },
|
|
372
|
+
body: JSON.stringify({
|
|
373
|
+
client_id: clientId,
|
|
374
|
+
scope: "openid profile email offline_access agents:read agents:write agents:run compute:read compute:write activity:read usage:read"
|
|
375
|
+
})
|
|
376
|
+
});
|
|
377
|
+
const device = await deviceResponse.json();
|
|
378
|
+
if (!deviceResponse.ok || !device.device_code || !device.user_code) {
|
|
379
|
+
throw new Error(device.error_description || "Could not start Commons login.");
|
|
380
|
+
}
|
|
381
|
+
const verificationUrl = device.verification_uri_complete ?? `${identityUrl}/device?user_code=${encodeURIComponent(device.user_code)}`;
|
|
382
|
+
console.log(` ${c.dim("Authorize this CLI in your browser:")}`);
|
|
383
|
+
console.log(` ${c.primary(verificationUrl)}`);
|
|
384
|
+
console.log(` ${c.dim("Code:")} ${c.bold(device.user_code)}
|
|
385
|
+
`);
|
|
386
|
+
openBrowser(verificationUrl);
|
|
387
|
+
const deadline = Date.now() + (device.expires_in ?? 600) * 1e3;
|
|
388
|
+
let intervalMs = Math.max(device.interval ?? 5, 1) * 1e3;
|
|
389
|
+
let sessionToken;
|
|
390
|
+
while (Date.now() < deadline) {
|
|
391
|
+
await new Promise((resolve2) => setTimeout(resolve2, intervalMs));
|
|
392
|
+
const tokenResponse = await fetch(`${identityUrl}/api/auth/device/token`, {
|
|
393
|
+
method: "POST",
|
|
394
|
+
headers: { "Content-Type": "application/json" },
|
|
395
|
+
body: JSON.stringify({
|
|
396
|
+
grant_type: "urn:ietf:params:oauth:grant-type:device_code",
|
|
397
|
+
device_code: device.device_code,
|
|
398
|
+
client_id: clientId
|
|
399
|
+
})
|
|
400
|
+
});
|
|
401
|
+
const token = await tokenResponse.json();
|
|
402
|
+
if (tokenResponse.ok && token.access_token) {
|
|
403
|
+
sessionToken = token.access_token;
|
|
404
|
+
break;
|
|
405
|
+
}
|
|
406
|
+
if (token.error === "slow_down") {
|
|
407
|
+
intervalMs += 1e3;
|
|
408
|
+
continue;
|
|
409
|
+
}
|
|
410
|
+
if (token.error === "authorization_pending") continue;
|
|
411
|
+
throw new Error(token.error_description || token.error || "Commons login failed.");
|
|
412
|
+
}
|
|
413
|
+
if (!sessionToken) throw new Error("Commons login expired before approval.");
|
|
414
|
+
saveConfig({
|
|
415
|
+
apiUrl,
|
|
416
|
+
identityUrl,
|
|
417
|
+
identityClientId: clientId,
|
|
418
|
+
sessionToken,
|
|
419
|
+
accessToken: void 0,
|
|
420
|
+
accessTokenExpiresAt: void 0,
|
|
421
|
+
apiKey: void 0
|
|
422
|
+
});
|
|
423
|
+
const authenticated = await ensureAccessToken();
|
|
424
|
+
console.log(
|
|
425
|
+
` ${sym.ok} ${c.success("Signed in")} as ${c.id(authenticated.userId ?? "Commons user")}`
|
|
426
|
+
);
|
|
427
|
+
console.log(`
|
|
428
|
+
${sym.ok} ${c.success("All set!")} Credentials saved to ${c.dim("~/.agc/config.json")}
|
|
429
|
+
`);
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
432
|
+
step(1, 1, "Legacy API Key");
|
|
312
433
|
let apiKey = opts.apiKey;
|
|
313
434
|
if (!apiKey) {
|
|
314
435
|
console.log(` ${c.dim("You'll need an API key from your Agent Commons account.")}`);
|
|
@@ -377,15 +498,22 @@ function whoamiCommand() {
|
|
|
377
498
|
return new import_commander.Command("whoami").description("Show current configuration and verify API connectivity").option("--json", "Output as JSON").action(async (opts) => {
|
|
378
499
|
const cfg = loadConfig();
|
|
379
500
|
if (opts.json) {
|
|
380
|
-
console.log(JSON.stringify({
|
|
501
|
+
console.log(JSON.stringify({
|
|
502
|
+
apiUrl: cfg.apiUrl,
|
|
503
|
+
identityUrl: cfg.identityUrl,
|
|
504
|
+
userId: cfg.userId ?? cfg.initiator,
|
|
505
|
+
workspaceId: cfg.workspaceId,
|
|
506
|
+
authenticated: Boolean(cfg.sessionToken || cfg.apiKey)
|
|
507
|
+
}, null, 2));
|
|
381
508
|
return;
|
|
382
509
|
}
|
|
383
510
|
console.log(`
|
|
384
511
|
${c.bold("Current configuration")}`);
|
|
385
512
|
detail([
|
|
386
513
|
["API URL", cfg.apiUrl],
|
|
387
|
-
["
|
|
388
|
-
["
|
|
514
|
+
["Identity", cfg.userId ?? cfg.initiator ?? c.dim("(not set)")],
|
|
515
|
+
["Workspace", cfg.workspaceId ?? c.dim("(not set)")],
|
|
516
|
+
["Auth", cfg.sessionToken ? "Commons account" : cfg.apiKey ? "Legacy API key" : c.dim("(not set)")],
|
|
389
517
|
["Agent ID", cfg.defaultAgentId ?? c.dim("(not set)")]
|
|
390
518
|
]);
|
|
391
519
|
try {
|
|
@@ -482,7 +610,7 @@ function agentsCommand() {
|
|
|
482
610
|
process.exit(1);
|
|
483
611
|
}
|
|
484
612
|
});
|
|
485
|
-
cmd.command("create").description("Create a new agent").requiredOption("--name <name>", "Agent name").option("--instructions <text>", "System instructions").option("--provider <provider>", "Model provider (openai|anthropic|google|groq)", "openai").option("--model <id>", "Model ID", "gpt-
|
|
613
|
+
cmd.command("create").description("Create a new agent").requiredOption("--name <name>", "Agent name").option("--instructions <text>", "System instructions").option("--provider <provider>", "Model provider (openai|anthropic|google|groq|openrouter|xai|ollama|custom)", "openai").option("--model <id>", "Model ID", "gpt-5.4-mini").option("--model-api-key <key>", "Provider API key (BYOK)").option("--model-base-url <url>", "Base URL for custom or local OpenAI-compatible providers").option("--json", "Output as JSON").action(async (opts) => {
|
|
486
614
|
const cfg = loadConfig();
|
|
487
615
|
if (!cfg.initiator) {
|
|
488
616
|
console.error(c.error("No initiator set. Run `agc login` first."));
|
|
@@ -496,7 +624,9 @@ function agentsCommand() {
|
|
|
496
624
|
instructions: opts.instructions,
|
|
497
625
|
owner: cfg.initiator,
|
|
498
626
|
modelProvider: opts.provider,
|
|
499
|
-
modelId: opts.model
|
|
627
|
+
modelId: opts.model,
|
|
628
|
+
modelApiKey: opts.modelApiKey,
|
|
629
|
+
modelBaseUrl: opts.modelBaseUrl
|
|
500
630
|
});
|
|
501
631
|
const agent = res?.data ?? res;
|
|
502
632
|
spinner.stop();
|
|
@@ -645,7 +775,7 @@ function sessionsCommand() {
|
|
|
645
775
|
process.exit(1);
|
|
646
776
|
}
|
|
647
777
|
});
|
|
648
|
-
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-
|
|
778
|
+
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) => {
|
|
649
779
|
const cfg = loadConfig();
|
|
650
780
|
const agentId = opts.agent ?? cfg.defaultAgentId;
|
|
651
781
|
if (!agentId) {
|
|
@@ -685,8 +815,50 @@ ${sym.ok} Session created`);
|
|
|
685
815
|
|
|
686
816
|
// src/commands/tools.ts
|
|
687
817
|
var import_commander4 = require("commander");
|
|
818
|
+
var import_fs3 = require("fs");
|
|
688
819
|
function toolsCommand() {
|
|
689
820
|
const cmd = new import_commander4.Command("tools").description("Discover and manage tools");
|
|
821
|
+
cmd.command("create").description("Create a tool from a JSON file").requiredOption("--file <path>", "Path to a JSON tool definition").option("--json", "Output as JSON").action(async (opts) => {
|
|
822
|
+
const cfg = loadConfig();
|
|
823
|
+
if (!cfg.initiator) {
|
|
824
|
+
console.error(c.error("No initiator set. Run `agc login` first."));
|
|
825
|
+
process.exit(1);
|
|
826
|
+
}
|
|
827
|
+
let payload;
|
|
828
|
+
try {
|
|
829
|
+
payload = JSON.parse((0, import_fs3.readFileSync)(opts.file, "utf8"));
|
|
830
|
+
} catch (error) {
|
|
831
|
+
console.error(c.error(`Could not read tool file: ${error.message}`));
|
|
832
|
+
process.exit(1);
|
|
833
|
+
}
|
|
834
|
+
if (!payload.name || !payload.schema) {
|
|
835
|
+
console.error(c.error('Tool file must include at least "name" and "schema".'));
|
|
836
|
+
process.exit(1);
|
|
837
|
+
}
|
|
838
|
+
const spinner = spin("Creating tool\u2026");
|
|
839
|
+
try {
|
|
840
|
+
const client = makeClient();
|
|
841
|
+
const res = await client.tools.create({
|
|
842
|
+
...payload,
|
|
843
|
+
owner: cfg.initiator,
|
|
844
|
+
ownerType: payload.ownerType ?? "user"
|
|
845
|
+
});
|
|
846
|
+
const tool = res?.data ?? res;
|
|
847
|
+
spinner.stop();
|
|
848
|
+
if (opts.json) return jsonOut(tool);
|
|
849
|
+
console.log(`
|
|
850
|
+
${sym.ok} Tool created`);
|
|
851
|
+
detail([
|
|
852
|
+
["Tool ID", c.id(tool.toolId)],
|
|
853
|
+
["Name", tool.name],
|
|
854
|
+
["Visibility", tool.visibility ?? payload.visibility ?? "private"]
|
|
855
|
+
]);
|
|
856
|
+
} catch (err) {
|
|
857
|
+
spinner.stop();
|
|
858
|
+
printError(err);
|
|
859
|
+
process.exit(1);
|
|
860
|
+
}
|
|
861
|
+
});
|
|
690
862
|
cmd.command("list").description("List available tools").option("--owner <id>", "Filter by owner ID").option("--json", "Output as JSON").action(async (opts) => {
|
|
691
863
|
const cfg = loadConfig();
|
|
692
864
|
const spinner = spin("Fetching tools\u2026");
|
|
@@ -784,8 +956,37 @@ ${sym.ok} ${c.label(toolName)}`);
|
|
|
784
956
|
|
|
785
957
|
// src/commands/workflow.ts
|
|
786
958
|
var import_commander5 = require("commander");
|
|
959
|
+
var import_fs4 = require("fs");
|
|
960
|
+
var import_sdk2 = require("@agent-commons/sdk");
|
|
787
961
|
function workflowCommand() {
|
|
788
962
|
const cmd = new import_commander5.Command("workflow").description("Run and monitor workflows").alias("wf");
|
|
963
|
+
async function createTemplateWorkflow(params) {
|
|
964
|
+
const client = makeClient();
|
|
965
|
+
const template = (0, import_sdk2.buildWorkflowTemplate)(params.templateName, params.ctx);
|
|
966
|
+
const toolIds = {};
|
|
967
|
+
const createdTools = [];
|
|
968
|
+
for (const tool of template.tools) {
|
|
969
|
+
const created = await client.tools.create({
|
|
970
|
+
...tool.payload,
|
|
971
|
+
owner: params.ctx.ownerId,
|
|
972
|
+
ownerType: "user"
|
|
973
|
+
});
|
|
974
|
+
const createdTool = created?.data ?? created;
|
|
975
|
+
toolIds[tool.key] = createdTool.toolId;
|
|
976
|
+
createdTools.push(createdTool);
|
|
977
|
+
}
|
|
978
|
+
const workflow = await client.workflows.create({
|
|
979
|
+
name: template.name,
|
|
980
|
+
description: template.description,
|
|
981
|
+
ownerId: params.ctx.ownerId,
|
|
982
|
+
ownerType: "user",
|
|
983
|
+
isPublic: params.isPublic,
|
|
984
|
+
category: template.category,
|
|
985
|
+
tags: template.tags,
|
|
986
|
+
definition: template.buildDefinition(toolIds, params.ctx)
|
|
987
|
+
});
|
|
988
|
+
return { template, workflow, createdTools };
|
|
989
|
+
}
|
|
789
990
|
cmd.command("list").description("List workflows owned by the current initiator").option("--json", "Output as JSON").action(async (opts) => {
|
|
790
991
|
const cfg = loadConfig();
|
|
791
992
|
if (!cfg.initiator) {
|
|
@@ -815,6 +1016,166 @@ function workflowCommand() {
|
|
|
815
1016
|
process.exit(1);
|
|
816
1017
|
}
|
|
817
1018
|
});
|
|
1019
|
+
cmd.command("create").description("Create a workflow from a JSON file").requiredOption("--file <path>", "Path to a workflow payload or definition JSON file").option("--name <name>", "Workflow name").option("--description <text>", "Workflow description").option("--public", "Make workflow public").option("--json", "Output as JSON").action(async (opts) => {
|
|
1020
|
+
const cfg = loadConfig();
|
|
1021
|
+
if (!cfg.initiator) {
|
|
1022
|
+
console.error(c.error("No initiator set. Run `agc login` first."));
|
|
1023
|
+
process.exit(1);
|
|
1024
|
+
}
|
|
1025
|
+
let fileJson;
|
|
1026
|
+
try {
|
|
1027
|
+
fileJson = JSON.parse((0, import_fs4.readFileSync)(opts.file, "utf8"));
|
|
1028
|
+
} catch (error) {
|
|
1029
|
+
console.error(c.error(`Could not read workflow file: ${error.message}`));
|
|
1030
|
+
process.exit(1);
|
|
1031
|
+
}
|
|
1032
|
+
const definition = fileJson.definition ?? fileJson;
|
|
1033
|
+
if (!Array.isArray(definition.nodes) || !Array.isArray(definition.edges)) {
|
|
1034
|
+
console.error(c.error('Workflow file must include a definition with "nodes" and "edges".'));
|
|
1035
|
+
process.exit(1);
|
|
1036
|
+
}
|
|
1037
|
+
const spinner = spin("Creating workflow\u2026");
|
|
1038
|
+
try {
|
|
1039
|
+
const client = makeClient();
|
|
1040
|
+
const workflow = await client.workflows.create({
|
|
1041
|
+
name: opts.name ?? fileJson.name ?? "CLI Workflow",
|
|
1042
|
+
description: opts.description ?? fileJson.description,
|
|
1043
|
+
definition,
|
|
1044
|
+
ownerId: cfg.initiator,
|
|
1045
|
+
ownerType: "user",
|
|
1046
|
+
isPublic: opts.public ?? fileJson.isPublic,
|
|
1047
|
+
category: fileJson.category,
|
|
1048
|
+
tags: fileJson.tags
|
|
1049
|
+
});
|
|
1050
|
+
spinner.stop();
|
|
1051
|
+
if (opts.json) return jsonOut(workflow);
|
|
1052
|
+
console.log(`
|
|
1053
|
+
${sym.ok} Workflow created`);
|
|
1054
|
+
detail([
|
|
1055
|
+
["Workflow ID", c.id(workflow.workflowId)],
|
|
1056
|
+
["Name", workflow.name],
|
|
1057
|
+
["Nodes", String((workflow.definition?.nodes ?? []).length)]
|
|
1058
|
+
]);
|
|
1059
|
+
} catch (err) {
|
|
1060
|
+
spinner.stop();
|
|
1061
|
+
printError(err);
|
|
1062
|
+
process.exit(1);
|
|
1063
|
+
}
|
|
1064
|
+
});
|
|
1065
|
+
const templates = cmd.command("templates").description("Create workflows from built-in templates");
|
|
1066
|
+
templates.command("list").description("List built-in workflow templates").option("--json", "Output as JSON").action((opts) => {
|
|
1067
|
+
const rows = (0, import_sdk2.listWorkflowTemplates)();
|
|
1068
|
+
if (opts.json) return jsonOut(rows);
|
|
1069
|
+
section(`Workflow Templates (${rows.length})`);
|
|
1070
|
+
table(
|
|
1071
|
+
rows.map((template) => ({
|
|
1072
|
+
Name: template.name,
|
|
1073
|
+
Description: template.description
|
|
1074
|
+
})),
|
|
1075
|
+
["Name", "Description"]
|
|
1076
|
+
);
|
|
1077
|
+
});
|
|
1078
|
+
templates.command("create <templateName>").description("Create a workflow template and its required API tools").option("--prefix <prefix>", "Stable prefix for generated tool/workflow names").option("--agent <agentId>", "Agent ID for agent_processor nodes").option("--reviewer-agent <agentId>", "Second agent ID for multi-agent templates").option("--child-workflow <workflowId>", "Child workflow ID for workflow-invocation-smoke").option("--public", "Make workflow public").option("--run", "Run the workflow after creating it").option("--input <json>", "Run input JSON; defaults to template sample input").option("--json", "Output as JSON").action(async (templateNameRaw, opts) => {
|
|
1079
|
+
const cfg = loadConfig();
|
|
1080
|
+
if (!cfg.initiator) {
|
|
1081
|
+
console.error(c.error("No initiator set. Run `agc login` first."));
|
|
1082
|
+
process.exit(1);
|
|
1083
|
+
}
|
|
1084
|
+
const templateNames = (0, import_sdk2.listWorkflowTemplates)().map((item) => item.name);
|
|
1085
|
+
if (!templateNames.includes(templateNameRaw)) {
|
|
1086
|
+
console.error(c.error(`Unknown template "${templateNameRaw}".`));
|
|
1087
|
+
console.error(c.dim(`Available: ${templateNames.join(", ")}`));
|
|
1088
|
+
process.exit(1);
|
|
1089
|
+
}
|
|
1090
|
+
const templateName = templateNameRaw;
|
|
1091
|
+
const needsAgent = templateName === "agent-research-summary" || templateName === "multi-agent-field-report";
|
|
1092
|
+
const agentId = opts.agent ?? cfg.defaultAgentId;
|
|
1093
|
+
if (needsAgent && !agentId) {
|
|
1094
|
+
console.error(c.error("This template requires --agent <agentId> or a configured defaultAgentId."));
|
|
1095
|
+
process.exit(1);
|
|
1096
|
+
}
|
|
1097
|
+
const prefix = opts.prefix ?? `cli_${templateName.replace(/[^a-z0-9]+/gi, "_")}_${Date.now().toString(36)}`;
|
|
1098
|
+
const spinner = spin("Creating workflow template\u2026");
|
|
1099
|
+
try {
|
|
1100
|
+
let childWorkflowId = opts.childWorkflow;
|
|
1101
|
+
let childResult;
|
|
1102
|
+
if (templateName === "workflow-invocation-smoke" && !childWorkflowId) {
|
|
1103
|
+
const childCtx = {
|
|
1104
|
+
ownerId: cfg.initiator,
|
|
1105
|
+
prefix: `${prefix}_child`
|
|
1106
|
+
};
|
|
1107
|
+
childResult = await createTemplateWorkflow({
|
|
1108
|
+
templateName: "country-weather-brief",
|
|
1109
|
+
ctx: childCtx,
|
|
1110
|
+
isPublic: opts.public
|
|
1111
|
+
});
|
|
1112
|
+
childWorkflowId = childResult.workflow.workflowId;
|
|
1113
|
+
}
|
|
1114
|
+
const ctx = {
|
|
1115
|
+
ownerId: cfg.initiator,
|
|
1116
|
+
prefix,
|
|
1117
|
+
agentId,
|
|
1118
|
+
reviewerAgentId: opts.reviewerAgent,
|
|
1119
|
+
childWorkflowId
|
|
1120
|
+
};
|
|
1121
|
+
const result = await createTemplateWorkflow({
|
|
1122
|
+
templateName,
|
|
1123
|
+
ctx,
|
|
1124
|
+
isPublic: opts.public
|
|
1125
|
+
});
|
|
1126
|
+
let execution;
|
|
1127
|
+
if (opts.run) {
|
|
1128
|
+
let inputData = result.template.sampleInput;
|
|
1129
|
+
if (opts.input) {
|
|
1130
|
+
try {
|
|
1131
|
+
inputData = JSON.parse(opts.input);
|
|
1132
|
+
} catch {
|
|
1133
|
+
throw new Error("--input must be valid JSON");
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
1136
|
+
execution = await makeClient().workflows.execute(result.workflow.workflowId, {
|
|
1137
|
+
agentId,
|
|
1138
|
+
inputData,
|
|
1139
|
+
userId: cfg.initiator
|
|
1140
|
+
});
|
|
1141
|
+
}
|
|
1142
|
+
spinner.stop();
|
|
1143
|
+
const output = { ...result, child: childResult, execution };
|
|
1144
|
+
if (opts.json) return jsonOut(output);
|
|
1145
|
+
console.log(`
|
|
1146
|
+
${sym.ok} Workflow template created`);
|
|
1147
|
+
if (childResult) {
|
|
1148
|
+
detail([
|
|
1149
|
+
["Child workflow", c.id(childResult.workflow.workflowId)],
|
|
1150
|
+
["Parent workflow", c.id(result.workflow.workflowId)],
|
|
1151
|
+
["Template", templateName]
|
|
1152
|
+
]);
|
|
1153
|
+
} else {
|
|
1154
|
+
detail([
|
|
1155
|
+
["Workflow ID", c.id(result.workflow.workflowId)],
|
|
1156
|
+
["Template", templateName],
|
|
1157
|
+
["Tools created", String(result.createdTools.length)]
|
|
1158
|
+
]);
|
|
1159
|
+
}
|
|
1160
|
+
if (execution) {
|
|
1161
|
+
console.log(`
|
|
1162
|
+
${sym.ok} Execution started: ${c.id(execution.executionId)}`);
|
|
1163
|
+
console.log(` Status: ${statusBadge(execution.status)}`);
|
|
1164
|
+
const resultData = execution.result ?? execution.outputData;
|
|
1165
|
+
if (resultData !== void 0) {
|
|
1166
|
+
console.log("\n" + c.label("Result"));
|
|
1167
|
+
console.log(" " + JSON.stringify(resultData, null, 2).replace(/\n/g, "\n "));
|
|
1168
|
+
}
|
|
1169
|
+
} else {
|
|
1170
|
+
console.log(c.dim(`
|
|
1171
|
+
Run it with: agc workflow run ${result.workflow.workflowId} --input '${JSON.stringify(result.template.sampleInput)}'`));
|
|
1172
|
+
}
|
|
1173
|
+
} catch (err) {
|
|
1174
|
+
spinner.stop();
|
|
1175
|
+
printError(err);
|
|
1176
|
+
process.exit(1);
|
|
1177
|
+
}
|
|
1178
|
+
});
|
|
818
1179
|
cmd.command("get <workflowId>").description("Show workflow details").option("--json", "Output as JSON").action(async (workflowId, opts) => {
|
|
819
1180
|
const spinner = spin("Fetching workflow\u2026");
|
|
820
1181
|
try {
|
|
@@ -1187,7 +1548,7 @@ var import_commander7 = require("commander");
|
|
|
1187
1548
|
var readline3 = __toESM(require("readline"));
|
|
1188
1549
|
|
|
1189
1550
|
// src/local-tools.ts
|
|
1190
|
-
var
|
|
1551
|
+
var import_fs5 = require("fs");
|
|
1191
1552
|
var import_path3 = require("path");
|
|
1192
1553
|
var import_child_process2 = require("child_process");
|
|
1193
1554
|
var readline2 = __toESM(require("readline"));
|
|
@@ -1205,7 +1566,7 @@ function buildDirSnapshot(dir, maxDepth = 2) {
|
|
|
1205
1566
|
if (lines.length >= 300) return;
|
|
1206
1567
|
let entries;
|
|
1207
1568
|
try {
|
|
1208
|
-
entries = (0,
|
|
1569
|
+
entries = (0, import_fs5.readdirSync)(d, { withFileTypes: true });
|
|
1209
1570
|
} catch {
|
|
1210
1571
|
return;
|
|
1211
1572
|
}
|
|
@@ -1235,11 +1596,11 @@ function readFileForContext(rootDir, filePath) {
|
|
|
1235
1596
|
for (const pat of [/\/\.ssh\//, /\/\.aws\//, /\/\.env$/, /\/\.env\./, /id_rsa/, /id_ed25519/]) {
|
|
1236
1597
|
if (pat.test(abs)) return `[error: sensitive path blocked]`;
|
|
1237
1598
|
}
|
|
1238
|
-
if (!(0,
|
|
1239
|
-
const stat = (0,
|
|
1599
|
+
if (!(0, import_fs5.existsSync)(abs)) return `[error: file not found: ${filePath}]`;
|
|
1600
|
+
const stat = (0, import_fs5.statSync)(abs);
|
|
1240
1601
|
if (stat.isDirectory()) return `[error: "${filePath}" is a directory \u2014 use list_directory]`;
|
|
1241
1602
|
if (stat.size > 1e5) return `[truncated \u2014 file too large (${Math.round(stat.size / 1024)} KB). Use cli_read_file for full content]`;
|
|
1242
|
-
return (0,
|
|
1603
|
+
return (0, import_fs5.readFileSync)(abs, "utf8");
|
|
1243
1604
|
} catch (err) {
|
|
1244
1605
|
return `[error reading file: ${err?.message}]`;
|
|
1245
1606
|
}
|
|
@@ -1267,11 +1628,12 @@ ${fileSection}
|
|
|
1267
1628
|
### MANDATORY RULES \u2014 READ CAREFULLY
|
|
1268
1629
|
|
|
1269
1630
|
1. **Call cli_* tools immediately and directly.** Do NOT create tasks (createTask) for local file operations. Do NOT delegate to sub-agents. Do NOT ask the user to run commands themselves.
|
|
1270
|
-
2. **
|
|
1271
|
-
3. **
|
|
1272
|
-
4. **
|
|
1273
|
-
5.
|
|
1274
|
-
6. **
|
|
1631
|
+
2. **Own the request through completion.** Continue across tool calls, process polling, retries, debugging, and verification. Do not stop after describing a plan or asking whether to proceed when the request is already clear.
|
|
1632
|
+
3. **Use actual tool output as evidence.** Summarize the important result; do not fabricate success or dump noisy logs unless they help diagnose a failure.
|
|
1633
|
+
4. **Never fabricate results.** Wait for the real tool output before responding.
|
|
1634
|
+
5. **Sensitive paths are blocked** (.ssh, .gnupg, .aws, .env, credentials). Attempting to access them will return an error.
|
|
1635
|
+
6. ${autoApprove ? "**cli_write_file and cli_run_command execute immediately** \u2014 auto-approve is active, no user confirmation is required." : "**cli_write_file and cli_run_command require the user to confirm** before executing \u2014 you will see the result after they approve."}
|
|
1636
|
+
7. **Git commits must carry the agc co-author trailer.** Always include \`--trailer "Co-Authored-By: <AgentName> (agc) <agc-agent@users.noreply.github.com>"\` when running \`git commit\`. The CLI injects this automatically \u2014 do not omit it or pass \`--no-trailer\`.
|
|
1275
1637
|
|
|
1276
1638
|
### Available CLI tools
|
|
1277
1639
|
|
|
@@ -1304,12 +1666,12 @@ ${fileSection}
|
|
|
1304
1666
|
|
|
1305
1667
|
For long commands like \`npx create-next-app@latest my-app --yes\`:
|
|
1306
1668
|
|
|
1307
|
-
1. Call \`cli_start_process\` \u2014 returns \`{processId, status: "running"}\` immediately.
|
|
1308
|
-
2. Call \`cli_wait_for_process\` with \`{"processId": "...", "wait_seconds": 60}
|
|
1309
|
-
3. Repeat step 2 until \`status\` is \`"done"\` or \`"error"
|
|
1310
|
-
4.
|
|
1669
|
+
1. Call \`cli_start_process\` \u2014 it returns \`{processId, status: "running"}\` immediately.
|
|
1670
|
+
2. Call \`cli_wait_for_process\` with \`{"processId": "...", "wait_seconds": 60}\`.
|
|
1671
|
+
3. Repeat step 2 until \`status\` is \`"done"\` or \`"error"\`; diagnose and repair errors when possible.
|
|
1672
|
+
4. Continue with the rest of the assignment and verify the final outcome before responding.
|
|
1311
1673
|
|
|
1312
|
-
|
|
1674
|
+
Do not end the turn merely because a process is still running. Keep polling within the same run. Progress events may be streamed by the client, but the final answer comes only after completion or a genuine blocker.
|
|
1313
1675
|
|
|
1314
1676
|
### Example \u2014 scaffolding a Next.js project
|
|
1315
1677
|
|
|
@@ -1317,18 +1679,14 @@ Never hold the user in silence. Between each \`cli_wait_for_process\` call, tell
|
|
|
1317
1679
|
cli_start_process: {"command": "npx", "args": ["create-next-app@latest", "my-app", "--yes"], "cwd": "Desktop"}
|
|
1318
1680
|
\u2192 {processId: "proc_1a2b", status: "running"}
|
|
1319
1681
|
|
|
1320
|
-
Tell user: "Started! Installing dependencies, this takes a minute or two. Checking in 60s\u2026"
|
|
1321
|
-
|
|
1322
1682
|
cli_wait_for_process: {"processId": "proc_1a2b", "wait_seconds": 60}
|
|
1323
1683
|
\u2192 {status: "running", elapsedSec: 60, stdout: "Creating project...
|
|
1324
1684
|
Installing packages\u2026"}
|
|
1325
1685
|
|
|
1326
|
-
Tell user: "Still installing \u2014 here's output so far: [stdout]. Checking again\u2026"
|
|
1327
|
-
|
|
1328
1686
|
cli_wait_for_process: {"processId": "proc_1a2b", "wait_seconds": 60}
|
|
1329
1687
|
\u2192 {status: "done", exitCode: 0, elapsedSec: 93, stdout: "Success! Created my-app"}
|
|
1330
1688
|
|
|
1331
|
-
|
|
1689
|
+
Continue by running the requested checks and opening/inspecting the app when the assignment requires it.
|
|
1332
1690
|
\`\`\`
|
|
1333
1691
|
`;
|
|
1334
1692
|
}
|
|
@@ -1350,6 +1708,65 @@ function injectAgcTrailer(command, args, agentId, agentName) {
|
|
|
1350
1708
|
const identity = agentName ? `${agentName} (agc)` : agentId ? `agc/${agentId}` : "agc agent";
|
|
1351
1709
|
return [...args, "--trailer", `Co-Authored-By: ${identity} <agc-agent@users.noreply.github.com>`];
|
|
1352
1710
|
}
|
|
1711
|
+
var AGC_HOOK_MARKER = "# agc-session:";
|
|
1712
|
+
var HOOK_BACKUP_SUFFIX = ".agc-backup";
|
|
1713
|
+
function findGitDir(rootDir) {
|
|
1714
|
+
const gitPath = (0, import_path3.join)(rootDir, ".git");
|
|
1715
|
+
if (!(0, import_fs5.existsSync)(gitPath)) return null;
|
|
1716
|
+
const s = (0, import_fs5.statSync)(gitPath);
|
|
1717
|
+
if (s.isDirectory()) return gitPath;
|
|
1718
|
+
if (s.isFile()) {
|
|
1719
|
+
const content = (0, import_fs5.readFileSync)(gitPath, "utf8");
|
|
1720
|
+
const match = content.match(/^gitdir:\s*(.+)$/m);
|
|
1721
|
+
if (match) return match[1].trim();
|
|
1722
|
+
}
|
|
1723
|
+
return null;
|
|
1724
|
+
}
|
|
1725
|
+
function installGitHook(rootDir, sessionId, agentId, agentName) {
|
|
1726
|
+
const gitDir = findGitDir(rootDir);
|
|
1727
|
+
if (!gitDir) return;
|
|
1728
|
+
const hooksDir = (0, import_path3.join)(gitDir, "hooks");
|
|
1729
|
+
(0, import_fs5.mkdirSync)(hooksDir, { recursive: true });
|
|
1730
|
+
const hookPath = (0, import_path3.join)(hooksDir, "prepare-commit-msg");
|
|
1731
|
+
if ((0, import_fs5.existsSync)(hookPath)) {
|
|
1732
|
+
const existing = (0, import_fs5.readFileSync)(hookPath, "utf8");
|
|
1733
|
+
if (!existing.includes(AGC_HOOK_MARKER)) {
|
|
1734
|
+
(0, import_fs5.writeFileSync)(hookPath + HOOK_BACKUP_SUFFIX, existing, { mode: 493 });
|
|
1735
|
+
}
|
|
1736
|
+
}
|
|
1737
|
+
const identity = agentName ? `${agentName} (agc)` : agentId ? `agc/${agentId}` : "agc agent";
|
|
1738
|
+
const trailer = `Co-Authored-By: ${identity} <agc-agent@users.noreply.github.com>`;
|
|
1739
|
+
const chainLine = (0, import_fs5.existsSync)(hookPath + HOOK_BACKUP_SUFFIX) ? `
|
|
1740
|
+
# chain pre-existing hook
|
|
1741
|
+
"$(dirname "$0")/prepare-commit-msg${HOOK_BACKUP_SUFFIX}" "$@" 2>/dev/null || true
|
|
1742
|
+
` : "";
|
|
1743
|
+
const hook = `#!/bin/sh
|
|
1744
|
+
${AGC_HOOK_MARKER}${sessionId}
|
|
1745
|
+
COMMIT_MSG_FILE="$1"
|
|
1746
|
+
COMMIT_SOURCE="$2"
|
|
1747
|
+
${chainLine}
|
|
1748
|
+
case "$COMMIT_SOURCE" in merge|squash) exit 0 ;; esac
|
|
1749
|
+
TRAILER="${trailer}"
|
|
1750
|
+
grep -qF "$TRAILER" "$COMMIT_MSG_FILE" 2>/dev/null && exit 0
|
|
1751
|
+
printf '\\n%s\\n' "$TRAILER" >> "$COMMIT_MSG_FILE"
|
|
1752
|
+
`;
|
|
1753
|
+
(0, import_fs5.writeFileSync)(hookPath, hook, { mode: 493 });
|
|
1754
|
+
}
|
|
1755
|
+
function removeGitHook(rootDir) {
|
|
1756
|
+
const gitDir = findGitDir(rootDir);
|
|
1757
|
+
if (!gitDir) return;
|
|
1758
|
+
const hookPath = (0, import_path3.join)(gitDir, "hooks", "prepare-commit-msg");
|
|
1759
|
+
if (!(0, import_fs5.existsSync)(hookPath)) return;
|
|
1760
|
+
const content = (0, import_fs5.readFileSync)(hookPath, "utf8");
|
|
1761
|
+
if (!content.includes(AGC_HOOK_MARKER)) return;
|
|
1762
|
+
const backupPath = hookPath + HOOK_BACKUP_SUFFIX;
|
|
1763
|
+
if ((0, import_fs5.existsSync)(backupPath)) {
|
|
1764
|
+
(0, import_fs5.writeFileSync)(hookPath, (0, import_fs5.readFileSync)(backupPath, "utf8"), { mode: 493 });
|
|
1765
|
+
(0, import_fs5.unlinkSync)(backupPath);
|
|
1766
|
+
} else {
|
|
1767
|
+
(0, import_fs5.unlinkSync)(hookPath);
|
|
1768
|
+
}
|
|
1769
|
+
}
|
|
1353
1770
|
function safePath(root, userPath) {
|
|
1354
1771
|
const abs = (0, import_path3.resolve)(root, userPath);
|
|
1355
1772
|
const rel = (0, import_path3.relative)(root, abs);
|
|
@@ -1450,7 +1867,7 @@ function extractViaCommand(cmd, cmdArgs) {
|
|
|
1450
1867
|
}
|
|
1451
1868
|
async function extractPdfText(abs) {
|
|
1452
1869
|
try {
|
|
1453
|
-
const buffer = (0,
|
|
1870
|
+
const buffer = (0, import_fs5.readFileSync)(abs);
|
|
1454
1871
|
const data = await pdfParse(buffer);
|
|
1455
1872
|
const text2 = data.text?.trim();
|
|
1456
1873
|
if (text2) {
|
|
@@ -1478,8 +1895,8 @@ async function toolReadFile(args, cfg) {
|
|
|
1478
1895
|
if (!userPath) throw new Error('read_file requires a "path" argument');
|
|
1479
1896
|
const abs = safePath(cfg.rootDir, userPath);
|
|
1480
1897
|
assertNotSensitive(abs);
|
|
1481
|
-
if (!(0,
|
|
1482
|
-
const stat = (0,
|
|
1898
|
+
if (!(0, import_fs5.existsSync)(abs)) throw new Error(`File not found: ${userPath}`);
|
|
1899
|
+
const stat = (0, import_fs5.statSync)(abs);
|
|
1483
1900
|
if (stat.isDirectory()) throw new Error(`"${userPath}" is a directory, not a file`);
|
|
1484
1901
|
const ext = (0, import_path3.extname)(abs).toLowerCase();
|
|
1485
1902
|
if (PDF_EXTS.has(ext)) {
|
|
@@ -1494,7 +1911,7 @@ async function toolReadFile(args, cfg) {
|
|
|
1494
1911
|
throw new Error(`Cannot read binary file "${userPath}" (${ext} format). Only text, PDF, and Office documents are supported.`);
|
|
1495
1912
|
}
|
|
1496
1913
|
if (stat.size > 5e5) throw new Error(`File too large to read (${Math.round(stat.size / 1024)} KB). Max 500 KB.`);
|
|
1497
|
-
return (0,
|
|
1914
|
+
return (0, import_fs5.readFileSync)(abs, "utf8");
|
|
1498
1915
|
}
|
|
1499
1916
|
async function toolWriteFile(args, cfg) {
|
|
1500
1917
|
const { path: userPath, content } = args;
|
|
@@ -1508,16 +1925,16 @@ async function toolWriteFile(args, cfg) {
|
|
|
1508
1925
|
"write_file"
|
|
1509
1926
|
);
|
|
1510
1927
|
if (!ok) return "User denied write operation.";
|
|
1511
|
-
(0,
|
|
1512
|
-
(0,
|
|
1928
|
+
(0, import_fs5.mkdirSync)((0, import_path3.dirname)(abs), { recursive: true });
|
|
1929
|
+
(0, import_fs5.writeFileSync)(abs, content, "utf8");
|
|
1513
1930
|
return `Written ${String(content).length} bytes to ${userPath}`;
|
|
1514
1931
|
}
|
|
1515
1932
|
async function toolListDirectory(args, cfg) {
|
|
1516
1933
|
const userPath = args.path ?? ".";
|
|
1517
1934
|
const abs = safePath(cfg.rootDir, userPath);
|
|
1518
1935
|
assertNotSensitive(abs);
|
|
1519
|
-
if (!(0,
|
|
1520
|
-
const entries = (0,
|
|
1936
|
+
if (!(0, import_fs5.existsSync)(abs)) throw new Error(`Directory not found: ${userPath}`);
|
|
1937
|
+
const entries = (0, import_fs5.readdirSync)(abs, { withFileTypes: true });
|
|
1521
1938
|
const lines = entries.map((e) => {
|
|
1522
1939
|
const type = e.isDirectory() ? "d" : e.isSymbolicLink() ? "l" : "f";
|
|
1523
1940
|
return `[${type}] ${e.name}`;
|
|
@@ -1537,7 +1954,7 @@ async function toolSearchFiles(args, cfg) {
|
|
|
1537
1954
|
function walk(dir, depth = 0) {
|
|
1538
1955
|
if (results.length >= 50 || depth > 10) return;
|
|
1539
1956
|
try {
|
|
1540
|
-
for (const entry of (0,
|
|
1957
|
+
for (const entry of (0, import_fs5.readdirSync)(dir, { withFileTypes: true })) {
|
|
1541
1958
|
if (entry.name.startsWith(".") && depth > 0) continue;
|
|
1542
1959
|
const full = (0, import_path3.join)(dir, entry.name);
|
|
1543
1960
|
const rel = (0, import_path3.relative)(cfg.rootDir, full);
|
|
@@ -1841,7 +2258,7 @@ function runCommand() {
|
|
|
1841
2258
|
...cfg.initiator && { initiatorId: cfg.initiator },
|
|
1842
2259
|
...cliContext && { cliContext }
|
|
1843
2260
|
};
|
|
1844
|
-
if (opts.noStream) {
|
|
2261
|
+
if (opts.noStream && !localEnabled) {
|
|
1845
2262
|
const spinner = spin("Running\u2026");
|
|
1846
2263
|
try {
|
|
1847
2264
|
const result = await client.run.once(params);
|
|
@@ -1917,7 +2334,8 @@ Session: ${sessionId} (resume with: agc run --session ${sessionId} "<prompt>")`
|
|
|
1917
2334
|
} else if (event.type === "final") {
|
|
1918
2335
|
if (hasOutput) process.stdout.write("\n");
|
|
1919
2336
|
const e = event;
|
|
1920
|
-
|
|
2337
|
+
const finalText = e.content ?? e.payload?.content ?? e.payload?.text ?? e.payload?.message;
|
|
2338
|
+
if (finalText && !hasOutput) console.log(finalText);
|
|
1921
2339
|
if (sessionId) console.log(c.dim(`
|
|
1922
2340
|
Session: ${sessionId} (resume with: agc run --session ${sessionId} "<prompt>")`));
|
|
1923
2341
|
break;
|
|
@@ -1939,18 +2357,18 @@ ${sym.fail} ${c.error(event.message ?? "Error")}`);
|
|
|
1939
2357
|
// src/commands/chat.ts
|
|
1940
2358
|
var import_commander8 = require("commander");
|
|
1941
2359
|
var readline4 = __toESM(require("readline"));
|
|
1942
|
-
var
|
|
2360
|
+
var import_fs6 = require("fs");
|
|
1943
2361
|
var import_path4 = require("path");
|
|
1944
2362
|
var import_os3 = require("os");
|
|
1945
2363
|
var SESSIONS_DIR = (0, import_path4.join)((0, import_os3.homedir)(), ".agc", "sessions");
|
|
1946
2364
|
function ensureSessionsDir() {
|
|
1947
|
-
if (!(0,
|
|
2365
|
+
if (!(0, import_fs6.existsSync)(SESSIONS_DIR)) (0, import_fs6.mkdirSync)(SESSIONS_DIR, { recursive: true });
|
|
1948
2366
|
}
|
|
1949
2367
|
function appendSessionLog(sessionId, record) {
|
|
1950
2368
|
try {
|
|
1951
2369
|
ensureSessionsDir();
|
|
1952
2370
|
const file = (0, import_path4.join)(SESSIONS_DIR, `${sessionId}.jsonl`);
|
|
1953
|
-
(0,
|
|
2371
|
+
(0, import_fs6.appendFileSync)(file, JSON.stringify(record) + "\n", { mode: 384 });
|
|
1954
2372
|
} catch {
|
|
1955
2373
|
}
|
|
1956
2374
|
}
|
|
@@ -2039,23 +2457,27 @@ function chatCommand() {
|
|
|
2039
2457
|
process.exit(1);
|
|
2040
2458
|
}
|
|
2041
2459
|
}
|
|
2460
|
+
let agentName;
|
|
2042
2461
|
let walletLine = "";
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
const
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2462
|
+
await Promise.allSettled([
|
|
2463
|
+
client.agents.get(agentId).then((res) => {
|
|
2464
|
+
agentName = (res?.data ?? res)?.name;
|
|
2465
|
+
}),
|
|
2466
|
+
client.wallets.primary(agentId).then(async (primary) => {
|
|
2467
|
+
const w = primary?.data ?? primary;
|
|
2468
|
+
if (w?.id) {
|
|
2469
|
+
const bal = await client.wallets.balance(w.id).catch(() => null);
|
|
2470
|
+
const b = bal?.data ?? bal;
|
|
2471
|
+
const addr = `${w.address.slice(0, 6)}\u2026${w.address.slice(-4)}`;
|
|
2472
|
+
const usdc = b?.usdc ?? "0";
|
|
2473
|
+
walletLine = `${addr} ${c.bold(usdc + " USDC")}`;
|
|
2474
|
+
}
|
|
2475
|
+
})
|
|
2476
|
+
]);
|
|
2055
2477
|
console.log(`
|
|
2056
2478
|
${c.bold("Agent Commons Chat")}`);
|
|
2057
2479
|
const headerRows = [
|
|
2058
|
-
["Agent", agentId],
|
|
2480
|
+
["Agent", agentName ? `${agentName} ${c.dim(agentId)}` : agentId],
|
|
2059
2481
|
["Session", c.id(sessionId) + (isResume ? c.dim(" (resumed)") : c.dim(" (new)"))]
|
|
2060
2482
|
];
|
|
2061
2483
|
if (walletLine) headerRows.push(["Wallet", walletLine]);
|
|
@@ -2068,9 +2490,12 @@ ${c.bold("Agent Commons Chat")}`);
|
|
|
2068
2490
|
localToolsCfg = {
|
|
2069
2491
|
rootDir,
|
|
2070
2492
|
sessionId,
|
|
2493
|
+
agentId,
|
|
2494
|
+
agentName,
|
|
2071
2495
|
appendLog: (record) => appendSessionLog(sessionId, record),
|
|
2072
2496
|
permissions: /* @__PURE__ */ new Map()
|
|
2073
2497
|
};
|
|
2498
|
+
installGitHook(rootDir, sessionId, agentId, agentName);
|
|
2074
2499
|
appendSessionLog(sessionId, {
|
|
2075
2500
|
type: "local_tools_enabled",
|
|
2076
2501
|
rootDir,
|
|
@@ -2334,10 +2759,15 @@ ${sym.fail} ${c.error(event.message ?? "Stream error")}`);
|
|
|
2334
2759
|
rl.resume();
|
|
2335
2760
|
rl.prompt();
|
|
2336
2761
|
});
|
|
2762
|
+
const cleanup = () => {
|
|
2763
|
+
if (localToolsCfg) removeGitHook(localToolsCfg.rootDir);
|
|
2764
|
+
};
|
|
2337
2765
|
rl.on("close", () => {
|
|
2766
|
+
cleanup();
|
|
2338
2767
|
process.exit(0);
|
|
2339
2768
|
});
|
|
2340
2769
|
process.on("SIGINT", () => {
|
|
2770
|
+
cleanup();
|
|
2341
2771
|
console.log(c.dim(`
|
|
2342
2772
|
Session preserved. Resume with: agc chat --resume ${sessionId}`));
|
|
2343
2773
|
process.exit(130);
|
|
@@ -3423,6 +3853,7 @@ function memoryCommand() {
|
|
|
3423
3853
|
const res = await client.memory.create({
|
|
3424
3854
|
agentId: opts.agent,
|
|
3425
3855
|
content: opts.content,
|
|
3856
|
+
summary: String(opts.content).slice(0, 200),
|
|
3426
3857
|
memoryType: opts.type
|
|
3427
3858
|
});
|
|
3428
3859
|
const memory = res?.data ?? res;
|
|
@@ -3673,7 +4104,7 @@ var CONFIG_FILE3 = (0, import_path5.join)((0, import_os4.homedir)(), ".agc", "co
|
|
|
3673
4104
|
async function interactiveMenu() {
|
|
3674
4105
|
banner();
|
|
3675
4106
|
const cfg = loadConfig();
|
|
3676
|
-
const isSetup = !!(cfg.apiKey && cfg.initiator);
|
|
4107
|
+
const isSetup = !!((cfg.accessToken || cfg.apiKey || cfg.sessionToken) && (cfg.userId || cfg.initiator));
|
|
3677
4108
|
if (!isSetup) {
|
|
3678
4109
|
console.log(c.bold(" Welcome to Agent Commons CLI!"));
|
|
3679
4110
|
console.log(c.dim(" Looks like this is your first time here \u2014 let's get you set up.\n"));
|
|
@@ -3683,7 +4114,7 @@ async function interactiveMenu() {
|
|
|
3683
4114
|
return;
|
|
3684
4115
|
}
|
|
3685
4116
|
console.log(
|
|
3686
|
-
` ${c.dim("Connected to")} ${c.primary(cfg.apiUrl)} ${c.dim("\xB7")} ${c.dim("
|
|
4117
|
+
` ${c.dim("Connected to")} ${c.primary(cfg.apiUrl)} ${c.dim("\xB7")} ${c.dim("Identity")} ${c.id((cfg.userId ?? cfg.initiator).slice(0, 8) + "\u2026" + (cfg.userId ?? cfg.initiator).slice(-4))}
|
|
3687
4118
|
`
|
|
3688
4119
|
);
|
|
3689
4120
|
const action = await select("What would you like to do?", [
|
|
@@ -3801,9 +4232,13 @@ async function pickAgentInteractively(action) {
|
|
|
3801
4232
|
return agentId;
|
|
3802
4233
|
}
|
|
3803
4234
|
var program = new import_commander16.Command();
|
|
3804
|
-
program.name("agc").description("Agent Commons CLI \u2014 interact with the Agent Commons platform").version("0.1
|
|
4235
|
+
program.name("agc").description("Agent Commons CLI \u2014 interact with the Agent Commons platform").version("0.2.1", "-v, --version").action(async () => {
|
|
3805
4236
|
await interactiveMenu();
|
|
3806
4237
|
});
|
|
4238
|
+
program.hook("preAction", async (_thisCommand, actionCommand) => {
|
|
4239
|
+
if (actionCommand.name() === "login" || actionCommand.name() === "logout") return;
|
|
4240
|
+
await ensureAccessToken();
|
|
4241
|
+
});
|
|
3807
4242
|
program.addCommand(loginCommand());
|
|
3808
4243
|
program.addCommand(logoutCommand());
|
|
3809
4244
|
program.addCommand(whoamiCommand());
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-commons/cli",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Agent Commons CLI — chat, run, and manage agents from your terminal",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"bin": {
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"commander": "^12.1.0",
|
|
17
17
|
"ora": "^8.1.1",
|
|
18
18
|
"pdf-parse": "^1.1.1",
|
|
19
|
-
"@agent-commons/sdk": "0.1
|
|
19
|
+
"@agent-commons/sdk": "0.2.1"
|
|
20
20
|
},
|
|
21
21
|
"devDependencies": {
|
|
22
22
|
"@types/node": "^22.10.2",
|