@actant/cli 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/actant.d.ts +1 -0
- package/dist/bin/actant.js +25 -0
- package/dist/bin/actant.js.map +1 -0
- package/dist/chunk-2FKBVXMH.js +2663 -0
- package/dist/chunk-2FKBVXMH.js.map +1 -0
- package/dist/daemon-entry.d.ts +2 -0
- package/dist/daemon-entry.js +15 -0
- package/dist/daemon-entry.js.map +1 -0
- package/dist/index.d.ts +46 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/program-3Y3ULHTY.js +11 -0
- package/dist/program-3Y3ULHTY.js.map +1 -0
- package/dist/repl-JIMJ2V2Y.js +103 -0
- package/dist/repl-JIMJ2V2Y.js.map +1 -0
- package/package.json +48 -0
- package/scripts/postinstall.mjs +46 -0
|
@@ -0,0 +1,2663 @@
|
|
|
1
|
+
// src/program.ts
|
|
2
|
+
import { Command as Command72 } from "commander";
|
|
3
|
+
import { getDefaultIpcPath } from "@actant/shared";
|
|
4
|
+
|
|
5
|
+
// src/client/rpc-client.ts
|
|
6
|
+
import { createConnection } from "net";
|
|
7
|
+
var requestId = 0;
|
|
8
|
+
var RpcClient = class {
|
|
9
|
+
constructor(socketPath) {
|
|
10
|
+
this.socketPath = socketPath;
|
|
11
|
+
}
|
|
12
|
+
async call(method, params, options) {
|
|
13
|
+
const id = ++requestId;
|
|
14
|
+
const request = {
|
|
15
|
+
jsonrpc: "2.0",
|
|
16
|
+
id,
|
|
17
|
+
method,
|
|
18
|
+
params
|
|
19
|
+
};
|
|
20
|
+
const response = await this.send(request, options?.timeoutMs);
|
|
21
|
+
if (response.error) {
|
|
22
|
+
const err = new RpcCallError(response.error.message, response.error.code, response.error.data);
|
|
23
|
+
throw err;
|
|
24
|
+
}
|
|
25
|
+
return response.result;
|
|
26
|
+
}
|
|
27
|
+
async ping() {
|
|
28
|
+
try {
|
|
29
|
+
await this.call("daemon.ping", {});
|
|
30
|
+
return true;
|
|
31
|
+
} catch {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
send(request, timeoutMs) {
|
|
36
|
+
const effectiveTimeout = timeoutMs ?? (process.env["ACTANT_RPC_TIMEOUT_MS"] ? Number(process.env["ACTANT_RPC_TIMEOUT_MS"]) : 1e4);
|
|
37
|
+
return new Promise((resolve9, reject) => {
|
|
38
|
+
const socket = createConnection(this.socketPath, () => {
|
|
39
|
+
socket.write(JSON.stringify(request) + "\n");
|
|
40
|
+
});
|
|
41
|
+
let buffer = "";
|
|
42
|
+
socket.on("data", (chunk) => {
|
|
43
|
+
buffer += chunk.toString();
|
|
44
|
+
const lines = buffer.split("\n");
|
|
45
|
+
for (const line of lines) {
|
|
46
|
+
const trimmed = line.trim();
|
|
47
|
+
if (!trimmed) continue;
|
|
48
|
+
try {
|
|
49
|
+
const response = JSON.parse(trimmed);
|
|
50
|
+
socket.end();
|
|
51
|
+
resolve9(response);
|
|
52
|
+
return;
|
|
53
|
+
} catch {
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
socket.on("error", (err) => {
|
|
58
|
+
reject(new ConnectionError(this.socketPath, err));
|
|
59
|
+
});
|
|
60
|
+
socket.setTimeout(effectiveTimeout, () => {
|
|
61
|
+
socket.destroy();
|
|
62
|
+
reject(new Error(`RPC call timed out after ${effectiveTimeout}ms`));
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
var RpcCallError = class extends Error {
|
|
68
|
+
constructor(message, code, data) {
|
|
69
|
+
super(message);
|
|
70
|
+
this.code = code;
|
|
71
|
+
this.data = data;
|
|
72
|
+
this.name = "RpcCallError";
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
var ConnectionError = class extends Error {
|
|
76
|
+
constructor(socketPath, cause) {
|
|
77
|
+
super(`Cannot connect to daemon at ${socketPath}. Is it running? Start with: actant daemon start`);
|
|
78
|
+
this.socketPath = socketPath;
|
|
79
|
+
this.cause = cause;
|
|
80
|
+
this.name = "ConnectionError";
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
// src/commands/template/index.ts
|
|
85
|
+
import { Command as Command6 } from "commander";
|
|
86
|
+
|
|
87
|
+
// src/commands/template/list.ts
|
|
88
|
+
import { Command } from "commander";
|
|
89
|
+
|
|
90
|
+
// src/output/formatter.ts
|
|
91
|
+
import Table from "cli-table3";
|
|
92
|
+
import chalk from "chalk";
|
|
93
|
+
function formatTemplateList(templates, format) {
|
|
94
|
+
if (format === "json") {
|
|
95
|
+
return JSON.stringify(templates, null, 2);
|
|
96
|
+
}
|
|
97
|
+
if (format === "quiet") {
|
|
98
|
+
return templates.map((t) => t.name).join("\n");
|
|
99
|
+
}
|
|
100
|
+
if (templates.length === 0) {
|
|
101
|
+
return chalk.dim("No templates registered.");
|
|
102
|
+
}
|
|
103
|
+
const table = new Table({
|
|
104
|
+
head: [
|
|
105
|
+
chalk.cyan("Name"),
|
|
106
|
+
chalk.cyan("Version"),
|
|
107
|
+
chalk.cyan("Backend"),
|
|
108
|
+
chalk.cyan("Provider"),
|
|
109
|
+
chalk.cyan("Description")
|
|
110
|
+
]
|
|
111
|
+
});
|
|
112
|
+
for (const t of templates) {
|
|
113
|
+
table.push([
|
|
114
|
+
t.name,
|
|
115
|
+
t.version,
|
|
116
|
+
t.backend.type,
|
|
117
|
+
t.provider.type,
|
|
118
|
+
t.description ?? chalk.dim("\u2014")
|
|
119
|
+
]);
|
|
120
|
+
}
|
|
121
|
+
return table.toString();
|
|
122
|
+
}
|
|
123
|
+
function formatTemplateDetail(template, format) {
|
|
124
|
+
if (format === "json") {
|
|
125
|
+
return JSON.stringify(template, null, 2);
|
|
126
|
+
}
|
|
127
|
+
if (format === "quiet") {
|
|
128
|
+
return template.name;
|
|
129
|
+
}
|
|
130
|
+
const ctx = template.domainContext;
|
|
131
|
+
const lines = [
|
|
132
|
+
`${chalk.bold("Template:")} ${template.name}`,
|
|
133
|
+
`${chalk.bold("Version:")} ${template.version}`,
|
|
134
|
+
`${chalk.bold("Backend:")} ${template.backend.type}`,
|
|
135
|
+
`${chalk.bold("Provider:")} ${template.provider.type}`
|
|
136
|
+
];
|
|
137
|
+
if (template.description) {
|
|
138
|
+
lines.push(`${chalk.bold("Desc:")} ${template.description}`);
|
|
139
|
+
}
|
|
140
|
+
lines.push("");
|
|
141
|
+
lines.push(chalk.bold("Domain Context:"));
|
|
142
|
+
lines.push(` Skills: ${ctx.skills?.length ?? 0} ref(s) ${(ctx.skills ?? []).join(", ") || chalk.dim("none")}`);
|
|
143
|
+
lines.push(` Prompts: ${ctx.prompts?.length ?? 0} ref(s) ${(ctx.prompts ?? []).join(", ") || chalk.dim("none")}`);
|
|
144
|
+
lines.push(` MCP Servers: ${ctx.mcpServers?.length ?? 0} ref(s) ${(ctx.mcpServers ?? []).map((s) => s.name).join(", ") || chalk.dim("none")}`);
|
|
145
|
+
lines.push(` Plugins: ${ctx.plugins?.length ?? 0} ref(s) ${(ctx.plugins ?? []).join(", ") || chalk.dim("none")}`);
|
|
146
|
+
lines.push(` Workflow: ${ctx.workflow ?? chalk.dim("none")}`);
|
|
147
|
+
lines.push(` SubAgents: ${(ctx.subAgents ?? []).join(", ") || chalk.dim("none")}`);
|
|
148
|
+
if (template.metadata && Object.keys(template.metadata).length > 0) {
|
|
149
|
+
lines.push("");
|
|
150
|
+
lines.push(chalk.bold("Metadata:"));
|
|
151
|
+
for (const [k, v] of Object.entries(template.metadata)) {
|
|
152
|
+
lines.push(` ${k}: ${v}`);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return lines.join("\n");
|
|
156
|
+
}
|
|
157
|
+
var STATUS_COLORS = {
|
|
158
|
+
created: chalk.blue,
|
|
159
|
+
starting: chalk.yellow,
|
|
160
|
+
running: chalk.green,
|
|
161
|
+
stopping: chalk.yellow,
|
|
162
|
+
stopped: chalk.gray,
|
|
163
|
+
error: chalk.red
|
|
164
|
+
};
|
|
165
|
+
function colorStatus(status) {
|
|
166
|
+
const fn = STATUS_COLORS[status] ?? chalk.white;
|
|
167
|
+
return fn(status);
|
|
168
|
+
}
|
|
169
|
+
function formatAgentList(agents, format) {
|
|
170
|
+
if (format === "json") {
|
|
171
|
+
return JSON.stringify(agents, null, 2);
|
|
172
|
+
}
|
|
173
|
+
if (format === "quiet") {
|
|
174
|
+
return agents.map((a) => a.name).join("\n");
|
|
175
|
+
}
|
|
176
|
+
if (agents.length === 0) {
|
|
177
|
+
return chalk.dim("No agents found.");
|
|
178
|
+
}
|
|
179
|
+
const table = new Table({
|
|
180
|
+
head: [
|
|
181
|
+
chalk.cyan("Name"),
|
|
182
|
+
chalk.cyan("Template"),
|
|
183
|
+
chalk.cyan("Status"),
|
|
184
|
+
chalk.cyan("Launch Mode"),
|
|
185
|
+
chalk.cyan("PID"),
|
|
186
|
+
chalk.cyan("Created")
|
|
187
|
+
]
|
|
188
|
+
});
|
|
189
|
+
for (const a of agents) {
|
|
190
|
+
table.push([
|
|
191
|
+
a.name,
|
|
192
|
+
`${a.templateName}@${a.templateVersion}`,
|
|
193
|
+
colorStatus(a.status),
|
|
194
|
+
a.launchMode,
|
|
195
|
+
a.pid?.toString() ?? chalk.dim("\u2014"),
|
|
196
|
+
a.createdAt.slice(0, 19).replace("T", " ")
|
|
197
|
+
]);
|
|
198
|
+
}
|
|
199
|
+
return table.toString();
|
|
200
|
+
}
|
|
201
|
+
function formatAgentDetail(agent, format) {
|
|
202
|
+
if (format === "json") {
|
|
203
|
+
return JSON.stringify(agent, null, 2);
|
|
204
|
+
}
|
|
205
|
+
if (format === "quiet") {
|
|
206
|
+
return agent.name;
|
|
207
|
+
}
|
|
208
|
+
const lines = [
|
|
209
|
+
`${chalk.bold("Agent:")} ${agent.name}`,
|
|
210
|
+
`${chalk.bold("ID:")} ${agent.id}`,
|
|
211
|
+
`${chalk.bold("Template:")} ${agent.templateName}@${agent.templateVersion}`,
|
|
212
|
+
`${chalk.bold("Status:")} ${colorStatus(agent.status)}`,
|
|
213
|
+
`${chalk.bold("Launch:")} ${agent.launchMode}`,
|
|
214
|
+
`${chalk.bold("PID:")} ${agent.pid ?? chalk.dim("\u2014")}`,
|
|
215
|
+
`${chalk.bold("Created:")} ${agent.createdAt}`,
|
|
216
|
+
`${chalk.bold("Updated:")} ${agent.updatedAt}`
|
|
217
|
+
];
|
|
218
|
+
if (agent.metadata && Object.keys(agent.metadata).length > 0) {
|
|
219
|
+
lines.push("");
|
|
220
|
+
lines.push(chalk.bold("Metadata:"));
|
|
221
|
+
for (const [k, v] of Object.entries(agent.metadata)) {
|
|
222
|
+
lines.push(` ${k}: ${v}`);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
return lines.join("\n");
|
|
226
|
+
}
|
|
227
|
+
function formatSkillList(skills, format) {
|
|
228
|
+
if (format === "json") return JSON.stringify(skills, null, 2);
|
|
229
|
+
if (format === "quiet") return skills.map((s) => s.name).join("\n");
|
|
230
|
+
if (skills.length === 0) return chalk.dim("No skills loaded.");
|
|
231
|
+
const table = new Table({
|
|
232
|
+
head: [chalk.cyan("Name"), chalk.cyan("Tags"), chalk.cyan("Description")]
|
|
233
|
+
});
|
|
234
|
+
for (const s of skills) {
|
|
235
|
+
table.push([
|
|
236
|
+
s.name,
|
|
237
|
+
s.tags?.join(", ") ?? chalk.dim("\u2014"),
|
|
238
|
+
s.description ?? chalk.dim("\u2014")
|
|
239
|
+
]);
|
|
240
|
+
}
|
|
241
|
+
return table.toString();
|
|
242
|
+
}
|
|
243
|
+
function formatSkillDetail(skill, format) {
|
|
244
|
+
if (format === "json") return JSON.stringify(skill, null, 2);
|
|
245
|
+
if (format === "quiet") return skill.name;
|
|
246
|
+
const lines = [
|
|
247
|
+
`${chalk.bold("Skill:")} ${skill.name}`,
|
|
248
|
+
...skill.description ? [`${chalk.bold("Description:")} ${skill.description}`] : [],
|
|
249
|
+
...skill.tags?.length ? [`${chalk.bold("Tags:")} ${skill.tags.join(", ")}`] : [],
|
|
250
|
+
"",
|
|
251
|
+
chalk.bold("Content:"),
|
|
252
|
+
skill.content
|
|
253
|
+
];
|
|
254
|
+
return lines.join("\n");
|
|
255
|
+
}
|
|
256
|
+
function formatPromptList(prompts, format) {
|
|
257
|
+
if (format === "json") return JSON.stringify(prompts, null, 2);
|
|
258
|
+
if (format === "quiet") return prompts.map((p) => p.name).join("\n");
|
|
259
|
+
if (prompts.length === 0) return chalk.dim("No prompts loaded.");
|
|
260
|
+
const table = new Table({
|
|
261
|
+
head: [chalk.cyan("Name"), chalk.cyan("Variables"), chalk.cyan("Description")]
|
|
262
|
+
});
|
|
263
|
+
for (const p of prompts) {
|
|
264
|
+
table.push([
|
|
265
|
+
p.name,
|
|
266
|
+
p.variables?.join(", ") ?? chalk.dim("\u2014"),
|
|
267
|
+
p.description ?? chalk.dim("\u2014")
|
|
268
|
+
]);
|
|
269
|
+
}
|
|
270
|
+
return table.toString();
|
|
271
|
+
}
|
|
272
|
+
function formatPromptDetail(prompt, format) {
|
|
273
|
+
if (format === "json") return JSON.stringify(prompt, null, 2);
|
|
274
|
+
if (format === "quiet") return prompt.name;
|
|
275
|
+
const lines = [
|
|
276
|
+
`${chalk.bold("Prompt:")} ${prompt.name}`,
|
|
277
|
+
...prompt.description ? [`${chalk.bold("Description:")} ${prompt.description}`] : [],
|
|
278
|
+
...prompt.variables?.length ? [`${chalk.bold("Variables:")} ${prompt.variables.join(", ")}`] : [],
|
|
279
|
+
"",
|
|
280
|
+
chalk.bold("Content:"),
|
|
281
|
+
prompt.content
|
|
282
|
+
];
|
|
283
|
+
return lines.join("\n");
|
|
284
|
+
}
|
|
285
|
+
function formatMcpList(servers, format) {
|
|
286
|
+
if (format === "json") return JSON.stringify(servers, null, 2);
|
|
287
|
+
if (format === "quiet") return servers.map((s) => s.name).join("\n");
|
|
288
|
+
if (servers.length === 0) return chalk.dim("No MCP servers loaded.");
|
|
289
|
+
const table = new Table({
|
|
290
|
+
head: [chalk.cyan("Name"), chalk.cyan("Command"), chalk.cyan("Description")]
|
|
291
|
+
});
|
|
292
|
+
for (const s of servers) {
|
|
293
|
+
const cmd = [s.command, ...s.args ?? []].join(" ");
|
|
294
|
+
table.push([
|
|
295
|
+
s.name,
|
|
296
|
+
cmd,
|
|
297
|
+
s.description ?? chalk.dim("\u2014")
|
|
298
|
+
]);
|
|
299
|
+
}
|
|
300
|
+
return table.toString();
|
|
301
|
+
}
|
|
302
|
+
function formatMcpDetail(server, format) {
|
|
303
|
+
if (format === "json") return JSON.stringify(server, null, 2);
|
|
304
|
+
if (format === "quiet") return server.name;
|
|
305
|
+
const lines = [
|
|
306
|
+
`${chalk.bold("MCP Server:")} ${server.name}`,
|
|
307
|
+
...server.description ? [`${chalk.bold("Description:")} ${server.description}`] : [],
|
|
308
|
+
`${chalk.bold("Command:")} ${server.command}`,
|
|
309
|
+
...server.args?.length ? [`${chalk.bold("Args:")} ${server.args.join(" ")}`] : []
|
|
310
|
+
];
|
|
311
|
+
if (server.env && Object.keys(server.env).length > 0) {
|
|
312
|
+
lines.push("");
|
|
313
|
+
lines.push(chalk.bold("Environment:"));
|
|
314
|
+
for (const [k, v] of Object.entries(server.env)) {
|
|
315
|
+
lines.push(` ${k}=${v}`);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
return lines.join("\n");
|
|
319
|
+
}
|
|
320
|
+
function formatWorkflowList(workflows, format) {
|
|
321
|
+
if (format === "json") return JSON.stringify(workflows, null, 2);
|
|
322
|
+
if (format === "quiet") return workflows.map((w) => w.name).join("\n");
|
|
323
|
+
if (workflows.length === 0) return chalk.dim("No workflows loaded.");
|
|
324
|
+
const table = new Table({
|
|
325
|
+
head: [chalk.cyan("Name"), chalk.cyan("Description")]
|
|
326
|
+
});
|
|
327
|
+
for (const w of workflows) {
|
|
328
|
+
table.push([w.name, w.description ?? chalk.dim("\u2014")]);
|
|
329
|
+
}
|
|
330
|
+
return table.toString();
|
|
331
|
+
}
|
|
332
|
+
function formatWorkflowDetail(workflow, format) {
|
|
333
|
+
if (format === "json") return JSON.stringify(workflow, null, 2);
|
|
334
|
+
if (format === "quiet") return workflow.name;
|
|
335
|
+
const lines = [
|
|
336
|
+
`${chalk.bold("Workflow:")} ${workflow.name}`,
|
|
337
|
+
...workflow.description ? [`${chalk.bold("Description:")} ${workflow.description}`] : [],
|
|
338
|
+
"",
|
|
339
|
+
chalk.bold("Content:"),
|
|
340
|
+
workflow.content
|
|
341
|
+
];
|
|
342
|
+
return lines.join("\n");
|
|
343
|
+
}
|
|
344
|
+
function formatPluginList(plugins, format) {
|
|
345
|
+
if (format === "json") return JSON.stringify(plugins, null, 2);
|
|
346
|
+
if (format === "quiet") return plugins.map((p) => p.name).join("\n");
|
|
347
|
+
if (plugins.length === 0) return chalk.dim("No plugins loaded.");
|
|
348
|
+
const table = new Table({
|
|
349
|
+
head: [chalk.cyan("Name"), chalk.cyan("Type"), chalk.cyan("Source"), chalk.cyan("Enabled"), chalk.cyan("Description")]
|
|
350
|
+
});
|
|
351
|
+
for (const p of plugins) {
|
|
352
|
+
table.push([
|
|
353
|
+
p.name,
|
|
354
|
+
p.type,
|
|
355
|
+
p.source ?? chalk.dim("\u2014"),
|
|
356
|
+
p.enabled !== false ? chalk.green("yes") : chalk.red("no"),
|
|
357
|
+
p.description ?? chalk.dim("\u2014")
|
|
358
|
+
]);
|
|
359
|
+
}
|
|
360
|
+
return table.toString();
|
|
361
|
+
}
|
|
362
|
+
function formatPluginDetail(plugin, format) {
|
|
363
|
+
if (format === "json") return JSON.stringify(plugin, null, 2);
|
|
364
|
+
if (format === "quiet") return plugin.name;
|
|
365
|
+
const lines = [
|
|
366
|
+
`${chalk.bold("Plugin:")} ${plugin.name}`,
|
|
367
|
+
...plugin.description ? [`${chalk.bold("Description:")} ${plugin.description}`] : [],
|
|
368
|
+
`${chalk.bold("Type:")} ${plugin.type}`,
|
|
369
|
+
...plugin.source ? [`${chalk.bold("Source:")} ${plugin.source}`] : [],
|
|
370
|
+
`${chalk.bold("Enabled:")} ${plugin.enabled !== false ? chalk.green("yes") : chalk.red("no")}`
|
|
371
|
+
];
|
|
372
|
+
if (plugin.config && Object.keys(plugin.config).length > 0) {
|
|
373
|
+
lines.push("");
|
|
374
|
+
lines.push(chalk.bold("Config:"));
|
|
375
|
+
for (const [k, v] of Object.entries(plugin.config)) {
|
|
376
|
+
lines.push(` ${k}: ${JSON.stringify(v)}`);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
return lines.join("\n");
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
// src/output/error-presenter.ts
|
|
383
|
+
import chalk3 from "chalk";
|
|
384
|
+
import { ActantError } from "@actant/shared";
|
|
385
|
+
|
|
386
|
+
// src/output/printer.ts
|
|
387
|
+
import chalk2 from "chalk";
|
|
388
|
+
var consoleOutput = {
|
|
389
|
+
log: (msg) => {
|
|
390
|
+
console.log(msg);
|
|
391
|
+
},
|
|
392
|
+
error: (msg) => {
|
|
393
|
+
console.error(msg);
|
|
394
|
+
}
|
|
395
|
+
};
|
|
396
|
+
var CliPrinter = class {
|
|
397
|
+
out;
|
|
398
|
+
constructor(output) {
|
|
399
|
+
this.out = output ?? consoleOutput;
|
|
400
|
+
}
|
|
401
|
+
log(text) {
|
|
402
|
+
this.out.log(text);
|
|
403
|
+
}
|
|
404
|
+
error(text) {
|
|
405
|
+
this.out.error(text);
|
|
406
|
+
}
|
|
407
|
+
success(text) {
|
|
408
|
+
this.out.log(chalk2.green(text));
|
|
409
|
+
}
|
|
410
|
+
warn(text) {
|
|
411
|
+
this.out.log(chalk2.yellow(text));
|
|
412
|
+
}
|
|
413
|
+
dim(text) {
|
|
414
|
+
this.out.log(chalk2.dim(text));
|
|
415
|
+
}
|
|
416
|
+
errorStyled(text) {
|
|
417
|
+
this.out.error(chalk2.red(text));
|
|
418
|
+
}
|
|
419
|
+
errorDim(text) {
|
|
420
|
+
this.out.error(chalk2.dim(text));
|
|
421
|
+
}
|
|
422
|
+
};
|
|
423
|
+
var defaultPrinter = new CliPrinter();
|
|
424
|
+
|
|
425
|
+
// src/output/error-presenter.ts
|
|
426
|
+
function presentError(err, printer = defaultPrinter) {
|
|
427
|
+
if (err instanceof ConnectionError) {
|
|
428
|
+
printer.errorStyled("Cannot connect to daemon.");
|
|
429
|
+
printer.errorDim("Start with: actant daemon start");
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
432
|
+
if (err instanceof RpcCallError) {
|
|
433
|
+
printer.error(`${chalk3.red(`[RPC ${err.code}]`)} ${err.message}`);
|
|
434
|
+
if (err.data && typeof err.data === "object") {
|
|
435
|
+
const data = err.data;
|
|
436
|
+
if (data.context) {
|
|
437
|
+
printer.error(`${chalk3.dim(" Context:")} ${JSON.stringify(data.context)}`);
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
return;
|
|
441
|
+
}
|
|
442
|
+
if (err instanceof ActantError) {
|
|
443
|
+
printer.error(`${chalk3.red(`[${err.code}]`)} ${err.message}`);
|
|
444
|
+
if (err.context && Object.keys(err.context).length > 0) {
|
|
445
|
+
printer.error(`${chalk3.dim(" Context:")} ${JSON.stringify(err.context)}`);
|
|
446
|
+
}
|
|
447
|
+
return;
|
|
448
|
+
}
|
|
449
|
+
if (err instanceof Error) {
|
|
450
|
+
printer.error(`${chalk3.red("Error:")} ${err.message}`);
|
|
451
|
+
} else {
|
|
452
|
+
printer.error(`${chalk3.red("Error:")} ${String(err)}`);
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
// src/output/stream-renderer.ts
|
|
457
|
+
import chalk4 from "chalk";
|
|
458
|
+
async function renderStream(stream, options) {
|
|
459
|
+
const output = options?.output ?? process.stdout;
|
|
460
|
+
const showTools = options?.showTools ?? true;
|
|
461
|
+
const label = options?.agentLabel ?? "agent";
|
|
462
|
+
let started = false;
|
|
463
|
+
let collectedText = "";
|
|
464
|
+
for await (const chunk of stream) {
|
|
465
|
+
if (!started) {
|
|
466
|
+
output.write(chalk4.cyan(`${label}> `));
|
|
467
|
+
started = true;
|
|
468
|
+
}
|
|
469
|
+
switch (chunk.type) {
|
|
470
|
+
case "text":
|
|
471
|
+
output.write(chunk.content);
|
|
472
|
+
collectedText += chunk.content;
|
|
473
|
+
break;
|
|
474
|
+
case "tool_use":
|
|
475
|
+
if (showTools) {
|
|
476
|
+
output.write(chalk4.dim(`
|
|
477
|
+
${chunk.content}
|
|
478
|
+
`));
|
|
479
|
+
}
|
|
480
|
+
break;
|
|
481
|
+
case "result":
|
|
482
|
+
output.write(chunk.content);
|
|
483
|
+
collectedText += chunk.content;
|
|
484
|
+
break;
|
|
485
|
+
case "error":
|
|
486
|
+
output.write(chalk4.red(`
|
|
487
|
+
[Error] ${chunk.content}
|
|
488
|
+
`));
|
|
489
|
+
break;
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
if (started) {
|
|
493
|
+
output.write("\n\n");
|
|
494
|
+
}
|
|
495
|
+
return collectedText;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
// src/commands/template/list.ts
|
|
499
|
+
function createTemplateListCommand(client, printer = defaultPrinter) {
|
|
500
|
+
return new Command("list").alias("ls").description("List all registered templates").option("-f, --format <format>", "Output format: table, json, quiet", "table").action(async (opts) => {
|
|
501
|
+
try {
|
|
502
|
+
const templates = await client.call("template.list", {});
|
|
503
|
+
printer.log(formatTemplateList(templates, opts.format));
|
|
504
|
+
} catch (err) {
|
|
505
|
+
presentError(err, printer);
|
|
506
|
+
process.exitCode = 1;
|
|
507
|
+
}
|
|
508
|
+
});
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
// src/commands/template/show.ts
|
|
512
|
+
import { Command as Command2 } from "commander";
|
|
513
|
+
function createTemplateShowCommand(client, printer = defaultPrinter) {
|
|
514
|
+
return new Command2("show").description("Show template details").argument("<name>", "Template name").option("-f, --format <format>", "Output format: table, json, quiet", "table").action(async (name, opts) => {
|
|
515
|
+
try {
|
|
516
|
+
const template = await client.call("template.get", { name });
|
|
517
|
+
printer.log(formatTemplateDetail(template, opts.format));
|
|
518
|
+
} catch (err) {
|
|
519
|
+
presentError(err, printer);
|
|
520
|
+
process.exitCode = 1;
|
|
521
|
+
}
|
|
522
|
+
});
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
// src/commands/template/validate.ts
|
|
526
|
+
import { resolve } from "path";
|
|
527
|
+
import { Command as Command3 } from "commander";
|
|
528
|
+
import chalk5 from "chalk";
|
|
529
|
+
function createTemplateValidateCommand(client, printer = defaultPrinter) {
|
|
530
|
+
return new Command3("validate").description("Validate a template JSON file").argument("<file>", "Path to template JSON file").action(async (file) => {
|
|
531
|
+
try {
|
|
532
|
+
const result = await client.call("template.validate", { filePath: resolve(file) });
|
|
533
|
+
if (result.valid && result.template) {
|
|
534
|
+
printer.log(`${chalk5.green("Valid")} \u2014 ${result.template.name}@${result.template.version}`);
|
|
535
|
+
if (result.warnings?.length) {
|
|
536
|
+
printer.log(chalk5.yellow(` ${result.warnings.length} warning(s):`));
|
|
537
|
+
for (const w of result.warnings) {
|
|
538
|
+
printer.log(chalk5.yellow(` - ${w.path}: ${w.message}`));
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
} else {
|
|
542
|
+
printer.error(chalk5.red("Invalid template"));
|
|
543
|
+
if (result.errors) {
|
|
544
|
+
for (const e of result.errors) {
|
|
545
|
+
printer.errorDim(` - ${e.path}: ${e.message}`);
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
process.exitCode = 1;
|
|
549
|
+
}
|
|
550
|
+
} catch (err) {
|
|
551
|
+
presentError(err, printer);
|
|
552
|
+
process.exitCode = 1;
|
|
553
|
+
}
|
|
554
|
+
});
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
// src/commands/template/load.ts
|
|
558
|
+
import { resolve as resolve2 } from "path";
|
|
559
|
+
import { Command as Command4 } from "commander";
|
|
560
|
+
import chalk6 from "chalk";
|
|
561
|
+
function createTemplateLoadCommand(client, printer = defaultPrinter) {
|
|
562
|
+
return new Command4("load").description("Load a template from a JSON file into the registry").argument("<file>", "Path to template JSON file").action(async (file) => {
|
|
563
|
+
try {
|
|
564
|
+
const template = await client.call("template.load", { filePath: resolve2(file) });
|
|
565
|
+
printer.log(`${chalk6.green("Loaded")} ${template.name}@${template.version}`);
|
|
566
|
+
} catch (err) {
|
|
567
|
+
presentError(err, printer);
|
|
568
|
+
process.exitCode = 1;
|
|
569
|
+
}
|
|
570
|
+
});
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
// src/commands/template/install.ts
|
|
574
|
+
import { Command as Command5 } from "commander";
|
|
575
|
+
function createTemplateInstallCommand(_client, printer = defaultPrinter) {
|
|
576
|
+
return new Command5("install").description("Install a template from a source (source@name)").argument("<spec>", "Source and template name, e.g. my-source@my-template").action(async (spec) => {
|
|
577
|
+
try {
|
|
578
|
+
const at = spec.indexOf("@");
|
|
579
|
+
if (at < 0) {
|
|
580
|
+
printer.error("Expected format: <source>@<name>");
|
|
581
|
+
process.exitCode = 1;
|
|
582
|
+
return;
|
|
583
|
+
}
|
|
584
|
+
const source = spec.slice(0, at);
|
|
585
|
+
printer.log(
|
|
586
|
+
`Template install not yet implemented via RPC - use "actant source sync ${source}" to sync templates from the source.`
|
|
587
|
+
);
|
|
588
|
+
} catch (err) {
|
|
589
|
+
presentError(err, printer);
|
|
590
|
+
process.exitCode = 1;
|
|
591
|
+
}
|
|
592
|
+
});
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
// src/commands/template/index.ts
|
|
596
|
+
function createTemplateCommand(client, printer) {
|
|
597
|
+
const cmd = new Command6("template").alias("tpl").description("Manage agent templates");
|
|
598
|
+
cmd.addCommand(createTemplateListCommand(client, printer));
|
|
599
|
+
cmd.addCommand(createTemplateShowCommand(client, printer));
|
|
600
|
+
cmd.addCommand(createTemplateValidateCommand(client, printer));
|
|
601
|
+
cmd.addCommand(createTemplateLoadCommand(client, printer));
|
|
602
|
+
cmd.addCommand(createTemplateInstallCommand(client, printer));
|
|
603
|
+
return cmd;
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
// src/commands/agent/index.ts
|
|
607
|
+
import { Command as Command23 } from "commander";
|
|
608
|
+
|
|
609
|
+
// src/commands/agent/create.ts
|
|
610
|
+
import { existsSync } from "fs";
|
|
611
|
+
import { resolve as resolve3 } from "path";
|
|
612
|
+
import { createInterface } from "readline";
|
|
613
|
+
import { Command as Command7 } from "commander";
|
|
614
|
+
import chalk7 from "chalk";
|
|
615
|
+
var VALID_LAUNCH_MODES = /* @__PURE__ */ new Set(["direct", "acp-background", "acp-service", "one-shot"]);
|
|
616
|
+
function askQuestion(question) {
|
|
617
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
618
|
+
return new Promise((res) => {
|
|
619
|
+
rl.question(question, (answer) => {
|
|
620
|
+
rl.close();
|
|
621
|
+
res(answer.trim().toLowerCase());
|
|
622
|
+
});
|
|
623
|
+
});
|
|
624
|
+
}
|
|
625
|
+
function createAgentCreateCommand(client, printer = defaultPrinter) {
|
|
626
|
+
return new Command7("create").description("Create a new agent from a template").argument("<name>", "Agent instance name").requiredOption("-t, --template <template>", "Template name to use").option("--launch-mode <mode>", "Launch mode: direct, acp-background, acp-service, one-shot").option("--work-dir <path>", "Custom workspace directory (absolute or relative path)").option("--workspace <path>", "Same as --work-dir: create instance at external path instead of builtin location").option("--overwrite", "If work-dir exists, remove it and recreate").option("--append", "If work-dir exists, add agent files into it").option("-f, --format <format>", "Output format: table, json, quiet", "table").action(async (name, opts) => {
|
|
627
|
+
try {
|
|
628
|
+
if (opts.launchMode && !VALID_LAUNCH_MODES.has(opts.launchMode)) {
|
|
629
|
+
printer.error(`${chalk7.red(`Invalid launch mode: ${opts.launchMode}`)}`);
|
|
630
|
+
process.exitCode = 1;
|
|
631
|
+
return;
|
|
632
|
+
}
|
|
633
|
+
if (opts.overwrite && opts.append) {
|
|
634
|
+
printer.error(`${chalk7.red("Cannot use both --overwrite and --append")}`);
|
|
635
|
+
process.exitCode = 1;
|
|
636
|
+
return;
|
|
637
|
+
}
|
|
638
|
+
let workDirConflict;
|
|
639
|
+
const workDirPath = opts.workspace ?? opts.workDir;
|
|
640
|
+
const workDir = workDirPath ? resolve3(workDirPath) : void 0;
|
|
641
|
+
if (workDir && existsSync(workDir)) {
|
|
642
|
+
if (opts.overwrite) {
|
|
643
|
+
workDirConflict = "overwrite";
|
|
644
|
+
} else if (opts.append) {
|
|
645
|
+
workDirConflict = "append";
|
|
646
|
+
} else {
|
|
647
|
+
printer.warn(`Directory already exists: ${workDir}`);
|
|
648
|
+
const answer = await askQuestion(
|
|
649
|
+
` ${chalk7.yellow("(o)")}verwrite / ${chalk7.cyan("(a)")}ppend / ${chalk7.dim("(c)")}ancel? `
|
|
650
|
+
);
|
|
651
|
+
if (answer === "o" || answer === "overwrite") {
|
|
652
|
+
workDirConflict = "overwrite";
|
|
653
|
+
} else if (answer === "a" || answer === "append") {
|
|
654
|
+
workDirConflict = "append";
|
|
655
|
+
} else {
|
|
656
|
+
printer.log("Cancelled.");
|
|
657
|
+
return;
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
const overrides = {};
|
|
662
|
+
if (opts.launchMode) overrides.launchMode = opts.launchMode;
|
|
663
|
+
if (workDir) overrides.workDir = workDir;
|
|
664
|
+
if (workDirConflict) overrides.workDirConflict = workDirConflict;
|
|
665
|
+
const meta = await client.call("agent.create", {
|
|
666
|
+
name,
|
|
667
|
+
template: opts.template,
|
|
668
|
+
overrides: Object.keys(overrides).length > 0 ? overrides : void 0
|
|
669
|
+
});
|
|
670
|
+
printer.log(`${chalk7.green("Agent created successfully.")}
|
|
671
|
+
`);
|
|
672
|
+
printer.log(formatAgentDetail(meta, opts.format));
|
|
673
|
+
} catch (err) {
|
|
674
|
+
presentError(err, printer);
|
|
675
|
+
process.exitCode = 1;
|
|
676
|
+
}
|
|
677
|
+
});
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
// src/commands/agent/start.ts
|
|
681
|
+
import { Command as Command8 } from "commander";
|
|
682
|
+
import chalk8 from "chalk";
|
|
683
|
+
function createAgentStartCommand(client, printer = defaultPrinter) {
|
|
684
|
+
return new Command8("start").description("Start an agent").argument("<name>", "Agent name").action(async (name) => {
|
|
685
|
+
try {
|
|
686
|
+
await client.call("agent.start", { name });
|
|
687
|
+
printer.log(`${chalk8.green("Started")} ${name}`);
|
|
688
|
+
} catch (err) {
|
|
689
|
+
presentError(err, printer);
|
|
690
|
+
process.exitCode = 1;
|
|
691
|
+
}
|
|
692
|
+
});
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
// src/commands/agent/stop.ts
|
|
696
|
+
import { Command as Command9 } from "commander";
|
|
697
|
+
import chalk9 from "chalk";
|
|
698
|
+
function createAgentStopCommand(client, printer = defaultPrinter) {
|
|
699
|
+
return new Command9("stop").description("Stop a running agent").argument("<name>", "Agent name").action(async (name) => {
|
|
700
|
+
try {
|
|
701
|
+
await client.call("agent.stop", { name });
|
|
702
|
+
printer.log(`${chalk9.green("Stopped")} ${name}`);
|
|
703
|
+
} catch (err) {
|
|
704
|
+
presentError(err, printer);
|
|
705
|
+
process.exitCode = 1;
|
|
706
|
+
}
|
|
707
|
+
});
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
// src/commands/agent/status.ts
|
|
711
|
+
import { Command as Command10 } from "commander";
|
|
712
|
+
function createAgentStatusCommand(client, printer = defaultPrinter) {
|
|
713
|
+
return new Command10("status").description("Show agent status (all agents if no name given)").argument("[name]", "Agent name (optional)").option("-f, --format <format>", "Output format: table, json, quiet", "table").action(async (name, opts) => {
|
|
714
|
+
try {
|
|
715
|
+
if (name) {
|
|
716
|
+
const agent = await client.call("agent.status", { name });
|
|
717
|
+
printer.log(formatAgentDetail(agent, opts.format));
|
|
718
|
+
} else {
|
|
719
|
+
const agents = await client.call("agent.list", {});
|
|
720
|
+
printer.log(formatAgentList(agents, opts.format));
|
|
721
|
+
}
|
|
722
|
+
} catch (err) {
|
|
723
|
+
presentError(err, printer);
|
|
724
|
+
process.exitCode = 1;
|
|
725
|
+
}
|
|
726
|
+
});
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
// src/commands/agent/list.ts
|
|
730
|
+
import { Command as Command11 } from "commander";
|
|
731
|
+
function createAgentListCommand(client, printer = defaultPrinter) {
|
|
732
|
+
return new Command11("list").alias("ls").description("List all agents").option("-f, --format <format>", "Output format: table, json, quiet", "table").action(async (opts) => {
|
|
733
|
+
try {
|
|
734
|
+
const agents = await client.call("agent.list", {});
|
|
735
|
+
printer.log(formatAgentList(agents, opts.format));
|
|
736
|
+
} catch (err) {
|
|
737
|
+
presentError(err, printer);
|
|
738
|
+
process.exitCode = 1;
|
|
739
|
+
}
|
|
740
|
+
});
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
// src/commands/agent/adopt.ts
|
|
744
|
+
import chalk10 from "chalk";
|
|
745
|
+
import { Command as Command12 } from "commander";
|
|
746
|
+
function formatAdoptResult(result, format) {
|
|
747
|
+
if (format === "json") {
|
|
748
|
+
return JSON.stringify(result, null, 2);
|
|
749
|
+
}
|
|
750
|
+
if (format === "quiet") {
|
|
751
|
+
return result.name;
|
|
752
|
+
}
|
|
753
|
+
const lines = [
|
|
754
|
+
chalk10.green("Agent adopted successfully."),
|
|
755
|
+
"",
|
|
756
|
+
`${chalk10.bold("Name:")} ${result.name}`,
|
|
757
|
+
`${chalk10.bold("Template:")} ${result.template}`,
|
|
758
|
+
`${chalk10.bold("Workspace:")} ${result.workspacePath}`,
|
|
759
|
+
`${chalk10.bold("Location:")} ${result.location}`,
|
|
760
|
+
`${chalk10.bold("Status:")} ${result.status}`,
|
|
761
|
+
`${chalk10.bold("Created:")} ${result.createdAt}`
|
|
762
|
+
];
|
|
763
|
+
return lines.join("\n");
|
|
764
|
+
}
|
|
765
|
+
function createAgentAdoptCommand(client, printer = defaultPrinter) {
|
|
766
|
+
return new Command12("adopt").description("Adopt an existing agent workspace into the instance registry").argument("<path>", "Path to the agent workspace directory (contains .actant.json)").option("--rename <name>", "Register with a different name than in .actant.json").option("-f, --format <format>", "Output format: table, json, quiet", "table").action(async (path, opts) => {
|
|
767
|
+
try {
|
|
768
|
+
const result = await client.call("agent.adopt", {
|
|
769
|
+
path,
|
|
770
|
+
rename: opts.rename
|
|
771
|
+
});
|
|
772
|
+
printer.log(formatAdoptResult(result, opts.format));
|
|
773
|
+
} catch (err) {
|
|
774
|
+
presentError(err, printer);
|
|
775
|
+
process.exitCode = 1;
|
|
776
|
+
}
|
|
777
|
+
});
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
// src/commands/agent/destroy.ts
|
|
781
|
+
import { Command as Command13 } from "commander";
|
|
782
|
+
import chalk11 from "chalk";
|
|
783
|
+
function createAgentDestroyCommand(client, printer = defaultPrinter) {
|
|
784
|
+
return new Command13("destroy").alias("rm").description("Destroy an agent (removes workspace directory)").argument("<name>", "Agent name").option("--force", "Skip confirmation", false).action(async (name, opts) => {
|
|
785
|
+
if (!opts.force) {
|
|
786
|
+
printer.warn(`Destroying agent "${name}" will remove its entire workspace.`);
|
|
787
|
+
printer.warn("Use --force to skip this warning.");
|
|
788
|
+
process.exitCode = 1;
|
|
789
|
+
return;
|
|
790
|
+
}
|
|
791
|
+
try {
|
|
792
|
+
await client.call("agent.destroy", { name });
|
|
793
|
+
printer.log(`${chalk11.green("Destroyed")} ${name}`);
|
|
794
|
+
} catch (err) {
|
|
795
|
+
presentError(err, printer);
|
|
796
|
+
process.exitCode = 1;
|
|
797
|
+
}
|
|
798
|
+
});
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
// src/commands/agent/resolve.ts
|
|
802
|
+
import { Command as Command14 } from "commander";
|
|
803
|
+
import chalk12 from "chalk";
|
|
804
|
+
function createAgentResolveCommand(client, printer = defaultPrinter) {
|
|
805
|
+
return new Command14("resolve").description("Resolve spawn info for an agent (external spawn support)").argument("<name>", "Agent instance name").option("-t, --template <template>", "Template name (auto-creates instance if not found)").option("-f, --format <format>", "Output format: table, json, quiet", "table").action(async (name, opts) => {
|
|
806
|
+
try {
|
|
807
|
+
const result = await client.call("agent.resolve", {
|
|
808
|
+
name,
|
|
809
|
+
template: opts.template
|
|
810
|
+
});
|
|
811
|
+
if (opts.format === "json") {
|
|
812
|
+
printer.log(JSON.stringify(result, null, 2));
|
|
813
|
+
} else if (opts.format === "quiet") {
|
|
814
|
+
printer.log([result.command, ...result.args].join(" "));
|
|
815
|
+
} else {
|
|
816
|
+
if (result.created) {
|
|
817
|
+
printer.log(`${chalk12.green("Instance created.")}
|
|
818
|
+
`);
|
|
819
|
+
}
|
|
820
|
+
printer.log(`${chalk12.bold("Instance:")} ${result.instanceName}`);
|
|
821
|
+
printer.log(`${chalk12.bold("Backend:")} ${result.backendType}`);
|
|
822
|
+
printer.log(`${chalk12.bold("Workspace:")} ${result.workspaceDir}`);
|
|
823
|
+
printer.log(`${chalk12.bold("Command:")} ${result.command}`);
|
|
824
|
+
printer.log(`${chalk12.bold("Args:")} ${result.args.join(" ")}`);
|
|
825
|
+
if (result.env && Object.keys(result.env).length > 0) {
|
|
826
|
+
printer.log(chalk12.bold("Env:"));
|
|
827
|
+
for (const [k, v] of Object.entries(result.env)) {
|
|
828
|
+
printer.log(` ${k}=${v}`);
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
} catch (err) {
|
|
833
|
+
presentError(err, printer);
|
|
834
|
+
process.exitCode = 1;
|
|
835
|
+
}
|
|
836
|
+
});
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
// src/commands/agent/attach.ts
|
|
840
|
+
import { Command as Command15 } from "commander";
|
|
841
|
+
import chalk13 from "chalk";
|
|
842
|
+
function createAgentAttachCommand(client, printer = defaultPrinter) {
|
|
843
|
+
return new Command15("attach").description("Attach an externally-spawned process to an agent").argument("<name>", "Agent instance name").requiredOption("--pid <pid>", "Process ID of the externally-spawned agent").option("--metadata <json>", "Additional metadata as JSON object").option("-f, --format <format>", "Output format: table, json, quiet", "table").action(async (name, opts) => {
|
|
844
|
+
try {
|
|
845
|
+
const pid = parseInt(opts.pid, 10);
|
|
846
|
+
if (isNaN(pid) || pid <= 0) {
|
|
847
|
+
printer.error(chalk13.red("Invalid PID: must be a positive integer"));
|
|
848
|
+
process.exitCode = 1;
|
|
849
|
+
return;
|
|
850
|
+
}
|
|
851
|
+
let metadata;
|
|
852
|
+
if (opts.metadata) {
|
|
853
|
+
try {
|
|
854
|
+
metadata = JSON.parse(opts.metadata);
|
|
855
|
+
} catch {
|
|
856
|
+
printer.error(chalk13.red("Invalid --metadata: must be valid JSON"));
|
|
857
|
+
process.exitCode = 1;
|
|
858
|
+
return;
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
const meta = await client.call("agent.attach", { name, pid, metadata });
|
|
862
|
+
printer.log(`${chalk13.green("Process attached.")}
|
|
863
|
+
`);
|
|
864
|
+
printer.log(formatAgentDetail(meta, opts.format));
|
|
865
|
+
} catch (err) {
|
|
866
|
+
presentError(err, printer);
|
|
867
|
+
process.exitCode = 1;
|
|
868
|
+
}
|
|
869
|
+
});
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
// src/commands/agent/detach.ts
|
|
873
|
+
import { Command as Command16 } from "commander";
|
|
874
|
+
function createAgentDetachCommand(client, printer = defaultPrinter) {
|
|
875
|
+
return new Command16("detach").description("Detach an externally-managed process from an agent").argument("<name>", "Agent instance name").option("--cleanup", "Clean up ephemeral workspace after detach").action(async (name, opts) => {
|
|
876
|
+
try {
|
|
877
|
+
const result = await client.call("agent.detach", {
|
|
878
|
+
name,
|
|
879
|
+
cleanup: opts.cleanup
|
|
880
|
+
});
|
|
881
|
+
printer.success("Process detached.");
|
|
882
|
+
if (result.workspaceCleaned) {
|
|
883
|
+
printer.dim("Ephemeral workspace cleaned up.");
|
|
884
|
+
}
|
|
885
|
+
} catch (err) {
|
|
886
|
+
presentError(err, printer);
|
|
887
|
+
process.exitCode = 1;
|
|
888
|
+
}
|
|
889
|
+
});
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
// src/commands/agent/run.ts
|
|
893
|
+
import { Command as Command17 } from "commander";
|
|
894
|
+
function createAgentRunCommand(client, printer = defaultPrinter) {
|
|
895
|
+
return new Command17("run").description("Send a prompt to an agent and get the response").argument("<name>", "Agent name").requiredOption("--prompt <prompt>", "The prompt to send").option("--model <model>", "Model to use (e.g. sonnet, opus)").option("--max-turns <turns>", "Maximum agentic turns", parseInt).option("--timeout <ms>", "Timeout in milliseconds", parseInt).option("--session-id <id>", "Resume a specific session").option("-f, --format <format>", "Output format: text, json", "text").action(async (name, opts) => {
|
|
896
|
+
try {
|
|
897
|
+
const rpcTimeout = (opts.timeout ?? 3e5) + 5e3;
|
|
898
|
+
const result = await client.call("agent.run", {
|
|
899
|
+
name,
|
|
900
|
+
prompt: opts.prompt,
|
|
901
|
+
options: {
|
|
902
|
+
model: opts.model,
|
|
903
|
+
maxTurns: opts.maxTurns,
|
|
904
|
+
timeoutMs: opts.timeout,
|
|
905
|
+
sessionId: opts.sessionId
|
|
906
|
+
}
|
|
907
|
+
}, { timeoutMs: rpcTimeout });
|
|
908
|
+
if (opts.format === "json") {
|
|
909
|
+
printer.log(JSON.stringify(result, null, 2));
|
|
910
|
+
} else {
|
|
911
|
+
printer.log(result.text);
|
|
912
|
+
}
|
|
913
|
+
} catch (err) {
|
|
914
|
+
presentError(err, printer);
|
|
915
|
+
process.exitCode = 1;
|
|
916
|
+
}
|
|
917
|
+
});
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
// src/commands/agent/prompt.ts
|
|
921
|
+
import { Command as Command18 } from "commander";
|
|
922
|
+
function createAgentPromptCommand(client, printer = defaultPrinter) {
|
|
923
|
+
return new Command18("prompt").description("Send a message to a running agent's ACP session").argument("<name>", "Agent name (must be started with `agent start`)").requiredOption("-m, --message <message>", "The message to send").option("--session-id <id>", "Specific ACP session ID (uses primary session if omitted)").option("-f, --format <format>", "Output format: text, json", "text").action(async (name, opts) => {
|
|
924
|
+
try {
|
|
925
|
+
const result = await client.call("agent.prompt", {
|
|
926
|
+
name,
|
|
927
|
+
message: opts.message,
|
|
928
|
+
sessionId: opts.sessionId
|
|
929
|
+
});
|
|
930
|
+
if (opts.format === "json") {
|
|
931
|
+
printer.log(JSON.stringify(result, null, 2));
|
|
932
|
+
} else {
|
|
933
|
+
printer.log(result.response);
|
|
934
|
+
}
|
|
935
|
+
} catch (err) {
|
|
936
|
+
presentError(err, printer);
|
|
937
|
+
process.exitCode = 1;
|
|
938
|
+
}
|
|
939
|
+
});
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
// src/commands/agent/chat.ts
|
|
943
|
+
import { createInterface as createInterface2 } from "readline";
|
|
944
|
+
import { Command as Command19 } from "commander";
|
|
945
|
+
import chalk14 from "chalk";
|
|
946
|
+
import { AcpConnection } from "@actant/acp";
|
|
947
|
+
function createAgentChatCommand(client, printer = defaultPrinter) {
|
|
948
|
+
return new Command19("chat").description("Start an interactive chat session with an agent").argument("<name>", "Agent name").option("-t, --template <template>", "Template name (auto-creates instance if not found)").action(async (name, opts) => {
|
|
949
|
+
try {
|
|
950
|
+
await runChat(client, name, opts, printer);
|
|
951
|
+
} catch (err) {
|
|
952
|
+
presentError(err, printer);
|
|
953
|
+
process.exitCode = 1;
|
|
954
|
+
}
|
|
955
|
+
});
|
|
956
|
+
}
|
|
957
|
+
async function runChat(client, name, opts, printer) {
|
|
958
|
+
const alive = await client.ping();
|
|
959
|
+
if (!alive) {
|
|
960
|
+
printer.error("Daemon is not running. Start it with: actant daemon start");
|
|
961
|
+
process.exitCode = 1;
|
|
962
|
+
return;
|
|
963
|
+
}
|
|
964
|
+
let daemonManaged = false;
|
|
965
|
+
try {
|
|
966
|
+
const status = await client.call("agent.status", { name });
|
|
967
|
+
if (status.status === "running") {
|
|
968
|
+
daemonManaged = true;
|
|
969
|
+
}
|
|
970
|
+
} catch {
|
|
971
|
+
}
|
|
972
|
+
if (daemonManaged) {
|
|
973
|
+
await runDaemonChat(client, name, printer);
|
|
974
|
+
} else {
|
|
975
|
+
await runDirectBridgeChat(client, name, opts, printer);
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
async function runDaemonChat(client, name, printer) {
|
|
979
|
+
const meta = await client.call("agent.status", { name });
|
|
980
|
+
printer.log(
|
|
981
|
+
chalk14.bold(`Chat with ${meta.name}`) + chalk14.dim(` (${meta.templateName}@${meta.templateVersion}) [daemon-managed]`)
|
|
982
|
+
);
|
|
983
|
+
printer.log(chalk14.dim('Type your message and press Enter. Use "exit" or Ctrl+C to quit.\n'));
|
|
984
|
+
const rl = createReadline();
|
|
985
|
+
rl.prompt();
|
|
986
|
+
let sessionId;
|
|
987
|
+
rl.on("line", async (line) => {
|
|
988
|
+
const trimmed = line.trim();
|
|
989
|
+
if (!trimmed) {
|
|
990
|
+
rl.prompt();
|
|
991
|
+
return;
|
|
992
|
+
}
|
|
993
|
+
if (trimmed === "exit" || trimmed === "quit") {
|
|
994
|
+
rl.close();
|
|
995
|
+
return;
|
|
996
|
+
}
|
|
997
|
+
try {
|
|
998
|
+
process.stdout.write(chalk14.cyan("agent> "));
|
|
999
|
+
const result = await client.call("agent.prompt", {
|
|
1000
|
+
name,
|
|
1001
|
+
message: trimmed,
|
|
1002
|
+
sessionId
|
|
1003
|
+
}, { timeoutMs: 305e3 });
|
|
1004
|
+
process.stdout.write(result.response + "\n\n");
|
|
1005
|
+
sessionId = result.sessionId;
|
|
1006
|
+
} catch (err) {
|
|
1007
|
+
presentError(err, printer);
|
|
1008
|
+
}
|
|
1009
|
+
rl.prompt();
|
|
1010
|
+
});
|
|
1011
|
+
rl.on("close", () => {
|
|
1012
|
+
process.stdout.write(chalk14.dim("\nChat ended.\n"));
|
|
1013
|
+
});
|
|
1014
|
+
return new Promise((resolve9) => {
|
|
1015
|
+
rl.on("close", resolve9);
|
|
1016
|
+
});
|
|
1017
|
+
}
|
|
1018
|
+
async function runDirectBridgeChat(client, name, opts, printer) {
|
|
1019
|
+
const resolved = await client.call("agent.resolve", {
|
|
1020
|
+
name,
|
|
1021
|
+
template: opts.template
|
|
1022
|
+
});
|
|
1023
|
+
const conn = new AcpConnection({ autoApprove: true });
|
|
1024
|
+
let session = null;
|
|
1025
|
+
let attached = false;
|
|
1026
|
+
const cleanup = async () => {
|
|
1027
|
+
await conn.close();
|
|
1028
|
+
if (attached) {
|
|
1029
|
+
try {
|
|
1030
|
+
await client.call("agent.detach", { name: resolved.instanceName, cleanup: false });
|
|
1031
|
+
} catch {
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1034
|
+
};
|
|
1035
|
+
try {
|
|
1036
|
+
await conn.spawn(resolved.command, resolved.args, resolved.workspaceDir);
|
|
1037
|
+
await client.call("agent.attach", {
|
|
1038
|
+
name: resolved.instanceName,
|
|
1039
|
+
pid: process.pid,
|
|
1040
|
+
// register for lifecycle tracking
|
|
1041
|
+
metadata: { proxyMode: "direct-bridge-chat" }
|
|
1042
|
+
});
|
|
1043
|
+
attached = true;
|
|
1044
|
+
const initResult = await conn.initialize();
|
|
1045
|
+
const agentName = initResult.agentInfo?.name ?? name;
|
|
1046
|
+
session = await conn.newSession(resolved.workspaceDir);
|
|
1047
|
+
printer.log(
|
|
1048
|
+
chalk14.bold(`Chat with ${agentName}`) + chalk14.dim(` (direct bridge, session ${session.sessionId.slice(0, 8)}...)`)
|
|
1049
|
+
);
|
|
1050
|
+
printer.log(chalk14.dim('Type your message and press Enter. Use "exit" or Ctrl+C to quit.\n'));
|
|
1051
|
+
const rl = createReadline();
|
|
1052
|
+
process.on("SIGINT", () => {
|
|
1053
|
+
if (session) {
|
|
1054
|
+
conn.cancel(session.sessionId).catch(() => {
|
|
1055
|
+
});
|
|
1056
|
+
}
|
|
1057
|
+
rl.close();
|
|
1058
|
+
});
|
|
1059
|
+
rl.prompt();
|
|
1060
|
+
const activeSession = session;
|
|
1061
|
+
rl.on("line", async (line) => {
|
|
1062
|
+
const trimmed = line.trim();
|
|
1063
|
+
if (!trimmed) {
|
|
1064
|
+
rl.prompt();
|
|
1065
|
+
return;
|
|
1066
|
+
}
|
|
1067
|
+
if (trimmed === "exit" || trimmed === "quit") {
|
|
1068
|
+
rl.close();
|
|
1069
|
+
return;
|
|
1070
|
+
}
|
|
1071
|
+
try {
|
|
1072
|
+
const chunkStream = mapNotificationsToChunks(
|
|
1073
|
+
conn.streamPrompt(activeSession.sessionId, trimmed)
|
|
1074
|
+
);
|
|
1075
|
+
await renderStream(chunkStream, { agentLabel: agentName });
|
|
1076
|
+
} catch (err) {
|
|
1077
|
+
presentError(err, printer);
|
|
1078
|
+
}
|
|
1079
|
+
rl.prompt();
|
|
1080
|
+
});
|
|
1081
|
+
rl.on("close", () => {
|
|
1082
|
+
process.stdout.write(chalk14.dim("\nChat ended.\n"));
|
|
1083
|
+
});
|
|
1084
|
+
await new Promise((resolve9) => {
|
|
1085
|
+
rl.on("close", resolve9);
|
|
1086
|
+
});
|
|
1087
|
+
} finally {
|
|
1088
|
+
await cleanup();
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
function createReadline() {
|
|
1092
|
+
return createInterface2({
|
|
1093
|
+
input: process.stdin,
|
|
1094
|
+
output: process.stdout,
|
|
1095
|
+
prompt: chalk14.green("you> "),
|
|
1096
|
+
terminal: process.stdin.isTTY ?? false
|
|
1097
|
+
});
|
|
1098
|
+
}
|
|
1099
|
+
async function* mapNotificationsToChunks(notifications) {
|
|
1100
|
+
for await (const notification of notifications) {
|
|
1101
|
+
const chunk = notificationToChunk(notification);
|
|
1102
|
+
if (chunk) yield chunk;
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
function notificationToChunk(notification) {
|
|
1106
|
+
const update = notification.update;
|
|
1107
|
+
switch (update.sessionUpdate) {
|
|
1108
|
+
case "agent_message_chunk":
|
|
1109
|
+
if (update.content.type === "text") {
|
|
1110
|
+
return { type: "text", content: update.content.text };
|
|
1111
|
+
}
|
|
1112
|
+
return null;
|
|
1113
|
+
case "tool_call":
|
|
1114
|
+
return {
|
|
1115
|
+
type: "tool_use",
|
|
1116
|
+
content: `[Tool: ${update.title ?? "unknown"}] ${update.toolCallId}`
|
|
1117
|
+
};
|
|
1118
|
+
case "tool_call_update":
|
|
1119
|
+
if (update.status === "completed" && update.content) {
|
|
1120
|
+
const textParts = [];
|
|
1121
|
+
for (const item of update.content) {
|
|
1122
|
+
if (item.type === "content" && item.content.type === "text") {
|
|
1123
|
+
textParts.push(item.content.text);
|
|
1124
|
+
}
|
|
1125
|
+
}
|
|
1126
|
+
if (textParts.length > 0) {
|
|
1127
|
+
return { type: "text", content: textParts.join("") };
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
return null;
|
|
1131
|
+
default:
|
|
1132
|
+
return null;
|
|
1133
|
+
}
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
// src/commands/agent/dispatch.ts
|
|
1137
|
+
import { Command as Command20 } from "commander";
|
|
1138
|
+
function createAgentDispatchCommand(client, printer = defaultPrinter) {
|
|
1139
|
+
return new Command20("dispatch").description("Queue a one-off task for an agent's scheduler").argument("<name>", "Agent name").requiredOption("-m, --message <message>", "The prompt/message to dispatch").option("-p, --priority <priority>", "Task priority: low, normal, high, critical", "normal").action(async (name, opts) => {
|
|
1140
|
+
try {
|
|
1141
|
+
const result = await client.call("agent.dispatch", {
|
|
1142
|
+
name,
|
|
1143
|
+
prompt: opts.message,
|
|
1144
|
+
priority: opts.priority
|
|
1145
|
+
});
|
|
1146
|
+
if (result.queued) {
|
|
1147
|
+
printer.log("Task queued.");
|
|
1148
|
+
} else {
|
|
1149
|
+
printer.dim(`No scheduler for agent "${name}". Task not queued.`);
|
|
1150
|
+
}
|
|
1151
|
+
} catch (err) {
|
|
1152
|
+
presentError(err, printer);
|
|
1153
|
+
process.exitCode = 1;
|
|
1154
|
+
}
|
|
1155
|
+
});
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1158
|
+
// src/commands/agent/tasks.ts
|
|
1159
|
+
import { Command as Command21 } from "commander";
|
|
1160
|
+
function createAgentTasksCommand(client, printer = defaultPrinter) {
|
|
1161
|
+
return new Command21("tasks").description("List queued tasks for an agent's scheduler").argument("<name>", "Agent name").option("-f, --format <format>", "Output format: table, json", "table").action(async (name, opts) => {
|
|
1162
|
+
try {
|
|
1163
|
+
const result = await client.call("agent.tasks", { name });
|
|
1164
|
+
if (opts.format === "json") {
|
|
1165
|
+
printer.log(JSON.stringify(result, null, 2));
|
|
1166
|
+
} else {
|
|
1167
|
+
printer.log(`Queued: ${result.queued} Processing: ${result.processing}`);
|
|
1168
|
+
if (result.tasks.length > 0) {
|
|
1169
|
+
for (const t of result.tasks) {
|
|
1170
|
+
const promptPreview = (t.prompt ?? "").length > 40 ? `${(t.prompt ?? "").slice(0, 40)}...` : t.prompt ?? "";
|
|
1171
|
+
printer.log(` ${t.id?.slice(0, 8) ?? "?"} ${t.priority} ${t.source} ${promptPreview}`);
|
|
1172
|
+
}
|
|
1173
|
+
}
|
|
1174
|
+
}
|
|
1175
|
+
} catch (err) {
|
|
1176
|
+
presentError(err, printer);
|
|
1177
|
+
process.exitCode = 1;
|
|
1178
|
+
}
|
|
1179
|
+
});
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1182
|
+
// src/commands/agent/logs.ts
|
|
1183
|
+
import { Command as Command22 } from "commander";
|
|
1184
|
+
function createAgentLogsCommand(client, printer = defaultPrinter) {
|
|
1185
|
+
return new Command22("logs").description("Show execution logs for an agent's scheduler").argument("<name>", "Agent name").option("--limit <n>", "Maximum number of records to show", parseInt, 20).option("-f, --format <format>", "Output format: table, json", "table").action(async (name, opts) => {
|
|
1186
|
+
try {
|
|
1187
|
+
const result = await client.call("agent.logs", { name, limit: opts.limit });
|
|
1188
|
+
if (opts.format === "json") {
|
|
1189
|
+
printer.log(JSON.stringify(result, null, 2));
|
|
1190
|
+
} else {
|
|
1191
|
+
const records = result;
|
|
1192
|
+
if (records.length === 0) {
|
|
1193
|
+
printer.dim("No execution logs.");
|
|
1194
|
+
} else {
|
|
1195
|
+
for (const r of records) {
|
|
1196
|
+
const dur = r.durationMs != null ? ` ${r.durationMs}ms` : "";
|
|
1197
|
+
const err = r.error ? ` error: ${r.error}` : "";
|
|
1198
|
+
printer.log(` ${r.taskId?.slice(0, 8) ?? "?"} ${r.status} ${r.source} ${r.startedAt}${dur}${err}`);
|
|
1199
|
+
}
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
1202
|
+
} catch (err) {
|
|
1203
|
+
presentError(err, printer);
|
|
1204
|
+
process.exitCode = 1;
|
|
1205
|
+
}
|
|
1206
|
+
});
|
|
1207
|
+
}
|
|
1208
|
+
|
|
1209
|
+
// src/commands/agent/index.ts
|
|
1210
|
+
function createAgentCommand(client, printer) {
|
|
1211
|
+
const cmd = new Command23("agent").description("Manage agent instances");
|
|
1212
|
+
cmd.addCommand(createAgentCreateCommand(client, printer));
|
|
1213
|
+
cmd.addCommand(createAgentStartCommand(client, printer));
|
|
1214
|
+
cmd.addCommand(createAgentStopCommand(client, printer));
|
|
1215
|
+
cmd.addCommand(createAgentStatusCommand(client, printer));
|
|
1216
|
+
cmd.addCommand(createAgentListCommand(client, printer));
|
|
1217
|
+
cmd.addCommand(createAgentAdoptCommand(client, printer));
|
|
1218
|
+
cmd.addCommand(createAgentDestroyCommand(client, printer));
|
|
1219
|
+
cmd.addCommand(createAgentResolveCommand(client, printer));
|
|
1220
|
+
cmd.addCommand(createAgentAttachCommand(client, printer));
|
|
1221
|
+
cmd.addCommand(createAgentDetachCommand(client, printer));
|
|
1222
|
+
cmd.addCommand(createAgentRunCommand(client, printer));
|
|
1223
|
+
cmd.addCommand(createAgentPromptCommand(client, printer));
|
|
1224
|
+
cmd.addCommand(createAgentChatCommand(client, printer));
|
|
1225
|
+
cmd.addCommand(createAgentDispatchCommand(client, printer));
|
|
1226
|
+
cmd.addCommand(createAgentTasksCommand(client, printer));
|
|
1227
|
+
cmd.addCommand(createAgentLogsCommand(client, printer));
|
|
1228
|
+
return cmd;
|
|
1229
|
+
}
|
|
1230
|
+
|
|
1231
|
+
// src/commands/help.ts
|
|
1232
|
+
import { Command as Command24 } from "commander";
|
|
1233
|
+
import chalk15 from "chalk";
|
|
1234
|
+
|
|
1235
|
+
// package.json
|
|
1236
|
+
var package_default = {
|
|
1237
|
+
name: "@actant/cli",
|
|
1238
|
+
version: "0.1.2",
|
|
1239
|
+
description: "CLI for the Actant AI agent platform \u2014 build, manage, and compose AI agents",
|
|
1240
|
+
type: "module",
|
|
1241
|
+
license: "MIT",
|
|
1242
|
+
author: "blackplume <blackplume233@gmail.com>",
|
|
1243
|
+
repository: {
|
|
1244
|
+
type: "git",
|
|
1245
|
+
url: "https://github.com/blackplume233/Actant.git",
|
|
1246
|
+
directory: "packages/cli"
|
|
1247
|
+
},
|
|
1248
|
+
homepage: "https://github.com/blackplume233/Actant#readme",
|
|
1249
|
+
bugs: "https://github.com/blackplume233/Actant/issues",
|
|
1250
|
+
keywords: ["actant", "ai-agent", "cli", "agent-platform"],
|
|
1251
|
+
publishConfig: {
|
|
1252
|
+
access: "public"
|
|
1253
|
+
},
|
|
1254
|
+
files: ["dist", "scripts/postinstall.mjs"],
|
|
1255
|
+
bin: {
|
|
1256
|
+
actant: "./dist/bin/actant.js"
|
|
1257
|
+
},
|
|
1258
|
+
scripts: {
|
|
1259
|
+
build: "tsup",
|
|
1260
|
+
dev: "tsx src/index.ts",
|
|
1261
|
+
"type-check": "tsc --noEmit",
|
|
1262
|
+
test: "vitest run",
|
|
1263
|
+
clean: "rimraf dist",
|
|
1264
|
+
postinstall: "node scripts/postinstall.mjs",
|
|
1265
|
+
prepublishOnly: "tsup"
|
|
1266
|
+
},
|
|
1267
|
+
dependencies: {
|
|
1268
|
+
"@actant/acp": "workspace:*",
|
|
1269
|
+
"@actant/api": "workspace:*",
|
|
1270
|
+
"@actant/core": "workspace:*",
|
|
1271
|
+
"@actant/shared": "workspace:*",
|
|
1272
|
+
chalk: "^5.6.2",
|
|
1273
|
+
"cli-table3": "^0.6.5",
|
|
1274
|
+
commander: "^14.0.3"
|
|
1275
|
+
}
|
|
1276
|
+
};
|
|
1277
|
+
|
|
1278
|
+
// src/commands/help.ts
|
|
1279
|
+
function showOverview() {
|
|
1280
|
+
const version = package_default.version ?? "0.1.0";
|
|
1281
|
+
const lines = [
|
|
1282
|
+
"",
|
|
1283
|
+
chalk15.bold(" Actant \u2014 Build, manage, and compose AI agents"),
|
|
1284
|
+
chalk15.gray(` v${version}`),
|
|
1285
|
+
"",
|
|
1286
|
+
chalk15.bold(" Quick Start:"),
|
|
1287
|
+
` ${chalk15.cyan("actant daemon start")} Start the daemon`,
|
|
1288
|
+
` ${chalk15.cyan("actant agent create my-agent")} Create an Agent`,
|
|
1289
|
+
` ${chalk15.cyan("actant agent start my-agent")} Start an Agent`,
|
|
1290
|
+
` ${chalk15.cyan("actant agent chat my-agent")} Chat with an Agent`,
|
|
1291
|
+
"",
|
|
1292
|
+
chalk15.bold(" Agent Management:"),
|
|
1293
|
+
` ${chalk15.cyan("agent")} create|start|stop|list|chat|run Agent lifecycle`,
|
|
1294
|
+
` ${chalk15.cyan("agent adopt")} <path> Adopt existing workspace`,
|
|
1295
|
+
` ${chalk15.cyan("template")} list|show Agent templates`,
|
|
1296
|
+
` ${chalk15.cyan("proxy")} <name> ACP proxy forwarding`,
|
|
1297
|
+
"",
|
|
1298
|
+
chalk15.bold(" Component Management:"),
|
|
1299
|
+
` ${chalk15.cyan("skill")} list|add|remove|show Manage skills`,
|
|
1300
|
+
` ${chalk15.cyan("prompt")} list|add|remove|show Manage prompts`,
|
|
1301
|
+
` ${chalk15.cyan("mcp")} list|show Manage MCP server configs`,
|
|
1302
|
+
` ${chalk15.cyan("workflow")} list|show Manage workflows`,
|
|
1303
|
+
` ${chalk15.cyan("plugin")} list|add|remove|show Manage plugins`,
|
|
1304
|
+
"",
|
|
1305
|
+
chalk15.bold(" Ecosystem:"),
|
|
1306
|
+
` ${chalk15.cyan("source")} list|add|remove|sync Manage component sources`,
|
|
1307
|
+
` ${chalk15.cyan("preset")} list|apply Manage preset packs`,
|
|
1308
|
+
"",
|
|
1309
|
+
chalk15.bold(" Scheduling:"),
|
|
1310
|
+
` ${chalk15.cyan("schedule")} list Manage employee agent scheduling`,
|
|
1311
|
+
"",
|
|
1312
|
+
chalk15.bold(" System:"),
|
|
1313
|
+
` ${chalk15.cyan("daemon")} start|stop|status Daemon management`,
|
|
1314
|
+
` ${chalk15.cyan("help")} [command] Show help`,
|
|
1315
|
+
` ${chalk15.cyan("--version")} Show version`,
|
|
1316
|
+
"",
|
|
1317
|
+
chalk15.bold(" Tips:"),
|
|
1318
|
+
chalk15.gray(" Use 'actant help <command>' for detailed help on a command"),
|
|
1319
|
+
""
|
|
1320
|
+
];
|
|
1321
|
+
process.stdout.write(lines.join("\n"));
|
|
1322
|
+
}
|
|
1323
|
+
function showCommandHelp(program, commandName) {
|
|
1324
|
+
const subcmd = program.commands.find((c) => c.name() === commandName);
|
|
1325
|
+
if (subcmd) {
|
|
1326
|
+
subcmd.outputHelp();
|
|
1327
|
+
} else {
|
|
1328
|
+
process.stdout.write(
|
|
1329
|
+
chalk15.red(`Unknown command: ${commandName}
|
|
1330
|
+
`) + chalk15.gray("Run 'actant help' to see available commands.\n")
|
|
1331
|
+
);
|
|
1332
|
+
}
|
|
1333
|
+
}
|
|
1334
|
+
function createHelpCommand() {
|
|
1335
|
+
return new Command24("help").argument("[command]", "Command to get help for").description("Show help information").action(function(command) {
|
|
1336
|
+
const program = this.parent;
|
|
1337
|
+
if (command) {
|
|
1338
|
+
showCommandHelp(program, command);
|
|
1339
|
+
} else {
|
|
1340
|
+
showOverview();
|
|
1341
|
+
}
|
|
1342
|
+
});
|
|
1343
|
+
}
|
|
1344
|
+
|
|
1345
|
+
// src/commands/skill/index.ts
|
|
1346
|
+
import { Command as Command30 } from "commander";
|
|
1347
|
+
|
|
1348
|
+
// src/commands/skill/list.ts
|
|
1349
|
+
import { Command as Command25 } from "commander";
|
|
1350
|
+
function createSkillListCommand(client, printer = defaultPrinter) {
|
|
1351
|
+
return new Command25("list").alias("ls").description("List all loaded skills").option("-f, --format <format>", "Output format: table, json, quiet", "table").action(async (opts) => {
|
|
1352
|
+
try {
|
|
1353
|
+
const skills = await client.call("skill.list", {});
|
|
1354
|
+
printer.log(formatSkillList(skills, opts.format));
|
|
1355
|
+
} catch (err) {
|
|
1356
|
+
presentError(err, printer);
|
|
1357
|
+
process.exitCode = 1;
|
|
1358
|
+
}
|
|
1359
|
+
});
|
|
1360
|
+
}
|
|
1361
|
+
|
|
1362
|
+
// src/commands/skill/show.ts
|
|
1363
|
+
import { Command as Command26 } from "commander";
|
|
1364
|
+
function createSkillShowCommand(client, printer = defaultPrinter) {
|
|
1365
|
+
return new Command26("show").description("Show skill details").argument("<name>", "Skill name").option("-f, --format <format>", "Output format: table, json, quiet", "table").action(async (name, opts) => {
|
|
1366
|
+
try {
|
|
1367
|
+
const skill = await client.call("skill.get", { name });
|
|
1368
|
+
printer.log(formatSkillDetail(skill, opts.format));
|
|
1369
|
+
} catch (err) {
|
|
1370
|
+
presentError(err, printer);
|
|
1371
|
+
process.exitCode = 1;
|
|
1372
|
+
}
|
|
1373
|
+
});
|
|
1374
|
+
}
|
|
1375
|
+
|
|
1376
|
+
// src/commands/skill/add.ts
|
|
1377
|
+
import { readFile } from "fs/promises";
|
|
1378
|
+
import { resolve as resolve4 } from "path";
|
|
1379
|
+
import { Command as Command27 } from "commander";
|
|
1380
|
+
function createSkillAddCommand(client, printer = defaultPrinter) {
|
|
1381
|
+
return new Command27("add").description("Add a skill from a JSON file").argument("<file>", "Path to skill definition JSON file").action(async (file) => {
|
|
1382
|
+
try {
|
|
1383
|
+
const raw = await readFile(resolve4(file), "utf-8");
|
|
1384
|
+
const component = JSON.parse(raw);
|
|
1385
|
+
const result = await client.call("skill.add", { component });
|
|
1386
|
+
printer.success(`Skill "${result.name}" added successfully.`);
|
|
1387
|
+
} catch (err) {
|
|
1388
|
+
presentError(err, printer);
|
|
1389
|
+
process.exitCode = 1;
|
|
1390
|
+
}
|
|
1391
|
+
});
|
|
1392
|
+
}
|
|
1393
|
+
|
|
1394
|
+
// src/commands/skill/remove.ts
|
|
1395
|
+
import { Command as Command28 } from "commander";
|
|
1396
|
+
function createSkillRemoveCommand(client, printer = defaultPrinter) {
|
|
1397
|
+
return new Command28("remove").alias("rm").description("Remove a loaded skill").argument("<name>", "Skill name").action(async (name) => {
|
|
1398
|
+
try {
|
|
1399
|
+
const result = await client.call("skill.remove", { name });
|
|
1400
|
+
if (result.success) {
|
|
1401
|
+
printer.success(`Skill "${name}" removed.`);
|
|
1402
|
+
} else {
|
|
1403
|
+
printer.warn(`Skill "${name}" not found.`);
|
|
1404
|
+
}
|
|
1405
|
+
} catch (err) {
|
|
1406
|
+
presentError(err, printer);
|
|
1407
|
+
process.exitCode = 1;
|
|
1408
|
+
}
|
|
1409
|
+
});
|
|
1410
|
+
}
|
|
1411
|
+
|
|
1412
|
+
// src/commands/skill/export.ts
|
|
1413
|
+
import { Command as Command29 } from "commander";
|
|
1414
|
+
function createSkillExportCommand(client, printer = defaultPrinter) {
|
|
1415
|
+
return new Command29("export").description("Export a skill to a JSON file").argument("<name>", "Skill name").option("-o, --out <file>", "Output file path", "./{name}.json").action(async (name, opts) => {
|
|
1416
|
+
try {
|
|
1417
|
+
const outPath = opts.out.replace("{name}", name);
|
|
1418
|
+
await client.call("skill.export", { name, filePath: outPath });
|
|
1419
|
+
printer.success(`Skill "${name}" exported to ${outPath}`);
|
|
1420
|
+
} catch (err) {
|
|
1421
|
+
presentError(err, printer);
|
|
1422
|
+
process.exitCode = 1;
|
|
1423
|
+
}
|
|
1424
|
+
});
|
|
1425
|
+
}
|
|
1426
|
+
|
|
1427
|
+
// src/commands/skill/index.ts
|
|
1428
|
+
function createSkillCommand(client, printer) {
|
|
1429
|
+
const cmd = new Command30("skill").description("Manage loaded skills");
|
|
1430
|
+
cmd.addCommand(createSkillListCommand(client, printer));
|
|
1431
|
+
cmd.addCommand(createSkillShowCommand(client, printer));
|
|
1432
|
+
cmd.addCommand(createSkillAddCommand(client, printer));
|
|
1433
|
+
cmd.addCommand(createSkillRemoveCommand(client, printer));
|
|
1434
|
+
cmd.addCommand(createSkillExportCommand(client, printer));
|
|
1435
|
+
return cmd;
|
|
1436
|
+
}
|
|
1437
|
+
|
|
1438
|
+
// src/commands/prompt/index.ts
|
|
1439
|
+
import { Command as Command36 } from "commander";
|
|
1440
|
+
|
|
1441
|
+
// src/commands/prompt/list.ts
|
|
1442
|
+
import { Command as Command31 } from "commander";
|
|
1443
|
+
function createPromptListCommand(client, printer = defaultPrinter) {
|
|
1444
|
+
return new Command31("list").alias("ls").description("List all loaded prompts").option("-f, --format <format>", "Output format: table, json, quiet", "table").action(async (opts) => {
|
|
1445
|
+
try {
|
|
1446
|
+
const prompts = await client.call("prompt.list", {});
|
|
1447
|
+
printer.log(formatPromptList(prompts, opts.format));
|
|
1448
|
+
} catch (err) {
|
|
1449
|
+
presentError(err, printer);
|
|
1450
|
+
process.exitCode = 1;
|
|
1451
|
+
}
|
|
1452
|
+
});
|
|
1453
|
+
}
|
|
1454
|
+
|
|
1455
|
+
// src/commands/prompt/show.ts
|
|
1456
|
+
import { Command as Command32 } from "commander";
|
|
1457
|
+
function createPromptShowCommand(client, printer = defaultPrinter) {
|
|
1458
|
+
return new Command32("show").description("Show prompt details").argument("<name>", "Prompt name").option("-f, --format <format>", "Output format: table, json, quiet", "table").action(async (name, opts) => {
|
|
1459
|
+
try {
|
|
1460
|
+
const prompt = await client.call("prompt.get", { name });
|
|
1461
|
+
printer.log(formatPromptDetail(prompt, opts.format));
|
|
1462
|
+
} catch (err) {
|
|
1463
|
+
presentError(err, printer);
|
|
1464
|
+
process.exitCode = 1;
|
|
1465
|
+
}
|
|
1466
|
+
});
|
|
1467
|
+
}
|
|
1468
|
+
|
|
1469
|
+
// src/commands/prompt/add.ts
|
|
1470
|
+
import { readFile as readFile2 } from "fs/promises";
|
|
1471
|
+
import { resolve as resolve5 } from "path";
|
|
1472
|
+
import { Command as Command33 } from "commander";
|
|
1473
|
+
function createPromptAddCommand(client, printer = defaultPrinter) {
|
|
1474
|
+
return new Command33("add").description("Add a prompt from a JSON file").argument("<file>", "Path to prompt definition JSON file").action(async (file) => {
|
|
1475
|
+
try {
|
|
1476
|
+
const raw = await readFile2(resolve5(file), "utf-8");
|
|
1477
|
+
const component = JSON.parse(raw);
|
|
1478
|
+
const result = await client.call("prompt.add", { component });
|
|
1479
|
+
printer.success(`Prompt "${result.name}" added successfully.`);
|
|
1480
|
+
} catch (err) {
|
|
1481
|
+
presentError(err, printer);
|
|
1482
|
+
process.exitCode = 1;
|
|
1483
|
+
}
|
|
1484
|
+
});
|
|
1485
|
+
}
|
|
1486
|
+
|
|
1487
|
+
// src/commands/prompt/remove.ts
|
|
1488
|
+
import { Command as Command34 } from "commander";
|
|
1489
|
+
function createPromptRemoveCommand(client, printer = defaultPrinter) {
|
|
1490
|
+
return new Command34("remove").alias("rm").description("Remove a loaded prompt").argument("<name>", "Prompt name").action(async (name) => {
|
|
1491
|
+
try {
|
|
1492
|
+
const result = await client.call("prompt.remove", { name });
|
|
1493
|
+
if (result.success) {
|
|
1494
|
+
printer.success(`Prompt "${name}" removed.`);
|
|
1495
|
+
} else {
|
|
1496
|
+
printer.warn(`Prompt "${name}" not found.`);
|
|
1497
|
+
}
|
|
1498
|
+
} catch (err) {
|
|
1499
|
+
presentError(err, printer);
|
|
1500
|
+
process.exitCode = 1;
|
|
1501
|
+
}
|
|
1502
|
+
});
|
|
1503
|
+
}
|
|
1504
|
+
|
|
1505
|
+
// src/commands/prompt/export.ts
|
|
1506
|
+
import { Command as Command35 } from "commander";
|
|
1507
|
+
function createPromptExportCommand(client, printer = defaultPrinter) {
|
|
1508
|
+
return new Command35("export").description("Export a prompt to a JSON file").argument("<name>", "Prompt name").option("-o, --out <file>", "Output file path", "./{name}.json").action(async (name, opts) => {
|
|
1509
|
+
try {
|
|
1510
|
+
const outPath = opts.out.replace("{name}", name);
|
|
1511
|
+
await client.call("prompt.export", { name, filePath: outPath });
|
|
1512
|
+
printer.success(`Prompt "${name}" exported to ${outPath}`);
|
|
1513
|
+
} catch (err) {
|
|
1514
|
+
presentError(err, printer);
|
|
1515
|
+
process.exitCode = 1;
|
|
1516
|
+
}
|
|
1517
|
+
});
|
|
1518
|
+
}
|
|
1519
|
+
|
|
1520
|
+
// src/commands/prompt/index.ts
|
|
1521
|
+
function createPromptCommand(client, printer) {
|
|
1522
|
+
const cmd = new Command36("prompt").description("Manage loaded prompts");
|
|
1523
|
+
cmd.addCommand(createPromptListCommand(client, printer));
|
|
1524
|
+
cmd.addCommand(createPromptShowCommand(client, printer));
|
|
1525
|
+
cmd.addCommand(createPromptAddCommand(client, printer));
|
|
1526
|
+
cmd.addCommand(createPromptRemoveCommand(client, printer));
|
|
1527
|
+
cmd.addCommand(createPromptExportCommand(client, printer));
|
|
1528
|
+
return cmd;
|
|
1529
|
+
}
|
|
1530
|
+
|
|
1531
|
+
// src/commands/mcp/index.ts
|
|
1532
|
+
import { Command as Command42 } from "commander";
|
|
1533
|
+
|
|
1534
|
+
// src/commands/mcp/list.ts
|
|
1535
|
+
import { Command as Command37 } from "commander";
|
|
1536
|
+
function createMcpListCommand(client, printer = defaultPrinter) {
|
|
1537
|
+
return new Command37("list").alias("ls").description("List all loaded MCP server configs").option("-f, --format <format>", "Output format: table, json, quiet", "table").action(async (opts) => {
|
|
1538
|
+
try {
|
|
1539
|
+
const servers = await client.call("mcp.list", {});
|
|
1540
|
+
printer.log(formatMcpList(servers, opts.format));
|
|
1541
|
+
} catch (err) {
|
|
1542
|
+
presentError(err, printer);
|
|
1543
|
+
process.exitCode = 1;
|
|
1544
|
+
}
|
|
1545
|
+
});
|
|
1546
|
+
}
|
|
1547
|
+
|
|
1548
|
+
// src/commands/mcp/show.ts
|
|
1549
|
+
import { Command as Command38 } from "commander";
|
|
1550
|
+
function createMcpShowCommand(client, printer = defaultPrinter) {
|
|
1551
|
+
return new Command38("show").description("Show MCP server config details").argument("<name>", "MCP server name").option("-f, --format <format>", "Output format: table, json, quiet", "table").action(async (name, opts) => {
|
|
1552
|
+
try {
|
|
1553
|
+
const server = await client.call("mcp.get", { name });
|
|
1554
|
+
printer.log(formatMcpDetail(server, opts.format));
|
|
1555
|
+
} catch (err) {
|
|
1556
|
+
presentError(err, printer);
|
|
1557
|
+
process.exitCode = 1;
|
|
1558
|
+
}
|
|
1559
|
+
});
|
|
1560
|
+
}
|
|
1561
|
+
|
|
1562
|
+
// src/commands/mcp/add.ts
|
|
1563
|
+
import { readFile as readFile3 } from "fs/promises";
|
|
1564
|
+
import { resolve as resolve6 } from "path";
|
|
1565
|
+
import { Command as Command39 } from "commander";
|
|
1566
|
+
function createMcpAddCommand(client, printer = defaultPrinter) {
|
|
1567
|
+
return new Command39("add").description("Add an MCP server config from a JSON file").argument("<file>", "Path to MCP server definition JSON file").action(async (file) => {
|
|
1568
|
+
try {
|
|
1569
|
+
const raw = await readFile3(resolve6(file), "utf-8");
|
|
1570
|
+
const component = JSON.parse(raw);
|
|
1571
|
+
const result = await client.call("mcp.add", { component });
|
|
1572
|
+
printer.success(`MCP "${result.name}" added successfully.`);
|
|
1573
|
+
} catch (err) {
|
|
1574
|
+
presentError(err, printer);
|
|
1575
|
+
process.exitCode = 1;
|
|
1576
|
+
}
|
|
1577
|
+
});
|
|
1578
|
+
}
|
|
1579
|
+
|
|
1580
|
+
// src/commands/mcp/remove.ts
|
|
1581
|
+
import { Command as Command40 } from "commander";
|
|
1582
|
+
function createMcpRemoveCommand(client, printer = defaultPrinter) {
|
|
1583
|
+
return new Command40("remove").alias("rm").description("Remove a loaded MCP server config").argument("<name>", "MCP server name").action(async (name) => {
|
|
1584
|
+
try {
|
|
1585
|
+
const result = await client.call("mcp.remove", { name });
|
|
1586
|
+
if (result.success) {
|
|
1587
|
+
printer.success(`MCP "${name}" removed.`);
|
|
1588
|
+
} else {
|
|
1589
|
+
printer.warn(`MCP "${name}" not found.`);
|
|
1590
|
+
}
|
|
1591
|
+
} catch (err) {
|
|
1592
|
+
presentError(err, printer);
|
|
1593
|
+
process.exitCode = 1;
|
|
1594
|
+
}
|
|
1595
|
+
});
|
|
1596
|
+
}
|
|
1597
|
+
|
|
1598
|
+
// src/commands/mcp/export.ts
|
|
1599
|
+
import { Command as Command41 } from "commander";
|
|
1600
|
+
function createMcpExportCommand(client, printer = defaultPrinter) {
|
|
1601
|
+
return new Command41("export").description("Export an MCP server config to a JSON file").argument("<name>", "MCP server name").option("-o, --out <file>", "Output file path", "./{name}.json").action(async (name, opts) => {
|
|
1602
|
+
try {
|
|
1603
|
+
const outPath = opts.out.replace("{name}", name);
|
|
1604
|
+
await client.call("mcp.export", { name, filePath: outPath });
|
|
1605
|
+
printer.success(`MCP "${name}" exported to ${outPath}`);
|
|
1606
|
+
} catch (err) {
|
|
1607
|
+
presentError(err, printer);
|
|
1608
|
+
process.exitCode = 1;
|
|
1609
|
+
}
|
|
1610
|
+
});
|
|
1611
|
+
}
|
|
1612
|
+
|
|
1613
|
+
// src/commands/mcp/index.ts
|
|
1614
|
+
function createMcpCommand(client, printer) {
|
|
1615
|
+
const cmd = new Command42("mcp").description("Manage loaded MCP server configs");
|
|
1616
|
+
cmd.addCommand(createMcpListCommand(client, printer));
|
|
1617
|
+
cmd.addCommand(createMcpShowCommand(client, printer));
|
|
1618
|
+
cmd.addCommand(createMcpAddCommand(client, printer));
|
|
1619
|
+
cmd.addCommand(createMcpRemoveCommand(client, printer));
|
|
1620
|
+
cmd.addCommand(createMcpExportCommand(client, printer));
|
|
1621
|
+
return cmd;
|
|
1622
|
+
}
|
|
1623
|
+
|
|
1624
|
+
// src/commands/workflow/index.ts
|
|
1625
|
+
import { Command as Command48 } from "commander";
|
|
1626
|
+
|
|
1627
|
+
// src/commands/workflow/list.ts
|
|
1628
|
+
import { Command as Command43 } from "commander";
|
|
1629
|
+
function createWorkflowListCommand(client, printer = defaultPrinter) {
|
|
1630
|
+
return new Command43("list").alias("ls").description("List all loaded workflows").option("-f, --format <format>", "Output format: table, json, quiet", "table").action(async (opts) => {
|
|
1631
|
+
try {
|
|
1632
|
+
const workflows = await client.call("workflow.list", {});
|
|
1633
|
+
printer.log(formatWorkflowList(workflows, opts.format));
|
|
1634
|
+
} catch (err) {
|
|
1635
|
+
presentError(err, printer);
|
|
1636
|
+
process.exitCode = 1;
|
|
1637
|
+
}
|
|
1638
|
+
});
|
|
1639
|
+
}
|
|
1640
|
+
|
|
1641
|
+
// src/commands/workflow/show.ts
|
|
1642
|
+
import { Command as Command44 } from "commander";
|
|
1643
|
+
function createWorkflowShowCommand(client, printer = defaultPrinter) {
|
|
1644
|
+
return new Command44("show").description("Show workflow details").argument("<name>", "Workflow name").option("-f, --format <format>", "Output format: table, json, quiet", "table").action(async (name, opts) => {
|
|
1645
|
+
try {
|
|
1646
|
+
const workflow = await client.call("workflow.get", { name });
|
|
1647
|
+
printer.log(formatWorkflowDetail(workflow, opts.format));
|
|
1648
|
+
} catch (err) {
|
|
1649
|
+
presentError(err, printer);
|
|
1650
|
+
process.exitCode = 1;
|
|
1651
|
+
}
|
|
1652
|
+
});
|
|
1653
|
+
}
|
|
1654
|
+
|
|
1655
|
+
// src/commands/workflow/add.ts
|
|
1656
|
+
import { readFile as readFile4 } from "fs/promises";
|
|
1657
|
+
import { resolve as resolve7 } from "path";
|
|
1658
|
+
import { Command as Command45 } from "commander";
|
|
1659
|
+
function createWorkflowAddCommand(client, printer = defaultPrinter) {
|
|
1660
|
+
return new Command45("add").description("Add a workflow from a JSON file").argument("<file>", "Path to workflow definition JSON file").action(async (file) => {
|
|
1661
|
+
try {
|
|
1662
|
+
const raw = await readFile4(resolve7(file), "utf-8");
|
|
1663
|
+
const component = JSON.parse(raw);
|
|
1664
|
+
const result = await client.call("workflow.add", { component });
|
|
1665
|
+
printer.success(`Workflow "${result.name}" added successfully.`);
|
|
1666
|
+
} catch (err) {
|
|
1667
|
+
presentError(err, printer);
|
|
1668
|
+
process.exitCode = 1;
|
|
1669
|
+
}
|
|
1670
|
+
});
|
|
1671
|
+
}
|
|
1672
|
+
|
|
1673
|
+
// src/commands/workflow/remove.ts
|
|
1674
|
+
import { Command as Command46 } from "commander";
|
|
1675
|
+
function createWorkflowRemoveCommand(client, printer = defaultPrinter) {
|
|
1676
|
+
return new Command46("remove").alias("rm").description("Remove a loaded workflow").argument("<name>", "Workflow name").action(async (name) => {
|
|
1677
|
+
try {
|
|
1678
|
+
const result = await client.call("workflow.remove", { name });
|
|
1679
|
+
if (result.success) {
|
|
1680
|
+
printer.success(`Workflow "${name}" removed.`);
|
|
1681
|
+
} else {
|
|
1682
|
+
printer.warn(`Workflow "${name}" not found.`);
|
|
1683
|
+
}
|
|
1684
|
+
} catch (err) {
|
|
1685
|
+
presentError(err, printer);
|
|
1686
|
+
process.exitCode = 1;
|
|
1687
|
+
}
|
|
1688
|
+
});
|
|
1689
|
+
}
|
|
1690
|
+
|
|
1691
|
+
// src/commands/workflow/export.ts
|
|
1692
|
+
import { Command as Command47 } from "commander";
|
|
1693
|
+
function createWorkflowExportCommand(client, printer = defaultPrinter) {
|
|
1694
|
+
return new Command47("export").description("Export a workflow to a JSON file").argument("<name>", "Workflow name").option("-o, --out <file>", "Output file path", "./{name}.json").action(async (name, opts) => {
|
|
1695
|
+
try {
|
|
1696
|
+
const outPath = opts.out.replace("{name}", name);
|
|
1697
|
+
await client.call("workflow.export", { name, filePath: outPath });
|
|
1698
|
+
printer.success(`Workflow "${name}" exported to ${outPath}`);
|
|
1699
|
+
} catch (err) {
|
|
1700
|
+
presentError(err, printer);
|
|
1701
|
+
process.exitCode = 1;
|
|
1702
|
+
}
|
|
1703
|
+
});
|
|
1704
|
+
}
|
|
1705
|
+
|
|
1706
|
+
// src/commands/workflow/index.ts
|
|
1707
|
+
function createWorkflowCommand(client, printer) {
|
|
1708
|
+
const cmd = new Command48("workflow").description("Manage loaded workflows");
|
|
1709
|
+
cmd.addCommand(createWorkflowListCommand(client, printer));
|
|
1710
|
+
cmd.addCommand(createWorkflowShowCommand(client, printer));
|
|
1711
|
+
cmd.addCommand(createWorkflowAddCommand(client, printer));
|
|
1712
|
+
cmd.addCommand(createWorkflowRemoveCommand(client, printer));
|
|
1713
|
+
cmd.addCommand(createWorkflowExportCommand(client, printer));
|
|
1714
|
+
return cmd;
|
|
1715
|
+
}
|
|
1716
|
+
|
|
1717
|
+
// src/commands/plugin/index.ts
|
|
1718
|
+
import { Command as Command54 } from "commander";
|
|
1719
|
+
|
|
1720
|
+
// src/commands/plugin/list.ts
|
|
1721
|
+
import { Command as Command49 } from "commander";
|
|
1722
|
+
function createPluginListCommand(client, printer = defaultPrinter) {
|
|
1723
|
+
return new Command49("list").alias("ls").description("List all plugins").option("-f, --format <format>", "Output format: table, json, quiet", "table").action(async (opts) => {
|
|
1724
|
+
try {
|
|
1725
|
+
const plugins = await client.call("plugin.list", {});
|
|
1726
|
+
printer.log(formatPluginList(plugins, opts.format));
|
|
1727
|
+
} catch (err) {
|
|
1728
|
+
presentError(err, printer);
|
|
1729
|
+
process.exitCode = 1;
|
|
1730
|
+
}
|
|
1731
|
+
});
|
|
1732
|
+
}
|
|
1733
|
+
|
|
1734
|
+
// src/commands/plugin/show.ts
|
|
1735
|
+
import { Command as Command50 } from "commander";
|
|
1736
|
+
function createPluginShowCommand(client, printer = defaultPrinter) {
|
|
1737
|
+
return new Command50("show").description("Show plugin details").argument("<name>", "Plugin name").option("-f, --format <format>", "Output format: table, json, quiet", "table").action(async (name, opts) => {
|
|
1738
|
+
try {
|
|
1739
|
+
const plugin = await client.call("plugin.get", { name });
|
|
1740
|
+
printer.log(formatPluginDetail(plugin, opts.format));
|
|
1741
|
+
} catch (err) {
|
|
1742
|
+
presentError(err, printer);
|
|
1743
|
+
process.exitCode = 1;
|
|
1744
|
+
}
|
|
1745
|
+
});
|
|
1746
|
+
}
|
|
1747
|
+
|
|
1748
|
+
// src/commands/plugin/add.ts
|
|
1749
|
+
import { readFile as readFile5 } from "fs/promises";
|
|
1750
|
+
import { resolve as resolve8 } from "path";
|
|
1751
|
+
import { Command as Command51 } from "commander";
|
|
1752
|
+
function createPluginAddCommand(client, printer = defaultPrinter) {
|
|
1753
|
+
return new Command51("add").description("Add a plugin from a JSON file").argument("<file>", "Path to plugin definition JSON file").action(async (file) => {
|
|
1754
|
+
try {
|
|
1755
|
+
const raw = await readFile5(resolve8(file), "utf-8");
|
|
1756
|
+
const component = JSON.parse(raw);
|
|
1757
|
+
const result = await client.call("plugin.add", { component });
|
|
1758
|
+
printer.success(`Plugin "${result.name}" added successfully.`);
|
|
1759
|
+
} catch (err) {
|
|
1760
|
+
presentError(err, printer);
|
|
1761
|
+
process.exitCode = 1;
|
|
1762
|
+
}
|
|
1763
|
+
});
|
|
1764
|
+
}
|
|
1765
|
+
|
|
1766
|
+
// src/commands/plugin/remove.ts
|
|
1767
|
+
import { Command as Command52 } from "commander";
|
|
1768
|
+
function createPluginRemoveCommand(client, printer = defaultPrinter) {
|
|
1769
|
+
return new Command52("remove").alias("rm").description("Remove a loaded plugin").argument("<name>", "Plugin name").action(async (name) => {
|
|
1770
|
+
try {
|
|
1771
|
+
const result = await client.call("plugin.remove", { name });
|
|
1772
|
+
if (result.success) {
|
|
1773
|
+
printer.success(`Plugin "${name}" removed.`);
|
|
1774
|
+
} else {
|
|
1775
|
+
printer.warn(`Plugin "${name}" not found.`);
|
|
1776
|
+
}
|
|
1777
|
+
} catch (err) {
|
|
1778
|
+
presentError(err, printer);
|
|
1779
|
+
process.exitCode = 1;
|
|
1780
|
+
}
|
|
1781
|
+
});
|
|
1782
|
+
}
|
|
1783
|
+
|
|
1784
|
+
// src/commands/plugin/export.ts
|
|
1785
|
+
import { Command as Command53 } from "commander";
|
|
1786
|
+
function createPluginExportCommand(client, printer = defaultPrinter) {
|
|
1787
|
+
return new Command53("export").description("Export a plugin to a JSON file").argument("<name>", "Plugin name").option("-o, --out <file>", "Output file path", "./{name}.json").action(async (name, opts) => {
|
|
1788
|
+
try {
|
|
1789
|
+
const outPath = opts.out.replace("{name}", name);
|
|
1790
|
+
await client.call("plugin.export", { name, filePath: outPath });
|
|
1791
|
+
printer.success(`Plugin "${name}" exported to ${outPath}`);
|
|
1792
|
+
} catch (err) {
|
|
1793
|
+
presentError(err, printer);
|
|
1794
|
+
process.exitCode = 1;
|
|
1795
|
+
}
|
|
1796
|
+
});
|
|
1797
|
+
}
|
|
1798
|
+
|
|
1799
|
+
// src/commands/plugin/index.ts
|
|
1800
|
+
function createPluginCommand(client, printer) {
|
|
1801
|
+
const cmd = new Command54("plugin").description("Manage loaded plugins");
|
|
1802
|
+
cmd.addCommand(createPluginListCommand(client, printer));
|
|
1803
|
+
cmd.addCommand(createPluginShowCommand(client, printer));
|
|
1804
|
+
cmd.addCommand(createPluginAddCommand(client, printer));
|
|
1805
|
+
cmd.addCommand(createPluginRemoveCommand(client, printer));
|
|
1806
|
+
cmd.addCommand(createPluginExportCommand(client, printer));
|
|
1807
|
+
return cmd;
|
|
1808
|
+
}
|
|
1809
|
+
|
|
1810
|
+
// src/commands/source/index.ts
|
|
1811
|
+
import { Command as Command59 } from "commander";
|
|
1812
|
+
|
|
1813
|
+
// src/commands/source/list.ts
|
|
1814
|
+
import { Command as Command55 } from "commander";
|
|
1815
|
+
function createSourceListCommand(client, printer = defaultPrinter) {
|
|
1816
|
+
return new Command55("list").alias("ls").description("List registered component sources").option("-f, --format <format>", "Output format: table, json, quiet", "table").action(async (opts) => {
|
|
1817
|
+
try {
|
|
1818
|
+
const sources = await client.call("source.list", {});
|
|
1819
|
+
if (opts.format === "json") {
|
|
1820
|
+
printer.log(JSON.stringify(sources, null, 2));
|
|
1821
|
+
} else if (opts.format === "quiet") {
|
|
1822
|
+
printer.log(sources.map((s) => s.name).join("\n"));
|
|
1823
|
+
} else {
|
|
1824
|
+
if (sources.length === 0) {
|
|
1825
|
+
printer.dim("No sources registered.");
|
|
1826
|
+
} else {
|
|
1827
|
+
for (const s of sources) {
|
|
1828
|
+
const cfg = s.config;
|
|
1829
|
+
const detail = cfg.type === "github" ? cfg.url : cfg.type === "local" ? cfg.path : "unknown";
|
|
1830
|
+
printer.log(` ${s.name} (${cfg.type}) ${detail}`);
|
|
1831
|
+
}
|
|
1832
|
+
}
|
|
1833
|
+
}
|
|
1834
|
+
} catch (err) {
|
|
1835
|
+
presentError(err, printer);
|
|
1836
|
+
process.exitCode = 1;
|
|
1837
|
+
}
|
|
1838
|
+
});
|
|
1839
|
+
}
|
|
1840
|
+
|
|
1841
|
+
// src/commands/source/add.ts
|
|
1842
|
+
import { Command as Command56 } from "commander";
|
|
1843
|
+
function createSourceAddCommand(client, printer = defaultPrinter) {
|
|
1844
|
+
return new Command56("add").description("Register a component source").argument("<url-or-path>", "GitHub URL or local directory path").requiredOption("--name <name>", "Package name (namespace prefix)").option("--type <type>", "Source type: github, local", "github").option("--branch <branch>", "Git branch (for github type)", "main").action(async (urlOrPath, opts) => {
|
|
1845
|
+
try {
|
|
1846
|
+
const config = opts.type === "local" ? { type: "local", path: urlOrPath } : { type: "github", url: urlOrPath, branch: opts.branch };
|
|
1847
|
+
const result = await client.call("source.add", { name: opts.name, config });
|
|
1848
|
+
const c = result.components;
|
|
1849
|
+
printer.success(
|
|
1850
|
+
`Source "${opts.name}" added. Components: ${c.skills} skills, ${c.prompts} prompts, ${c.mcp} mcp, ${c.workflows} workflows, ${c.presets} presets`
|
|
1851
|
+
);
|
|
1852
|
+
} catch (err) {
|
|
1853
|
+
presentError(err, printer);
|
|
1854
|
+
process.exitCode = 1;
|
|
1855
|
+
}
|
|
1856
|
+
});
|
|
1857
|
+
}
|
|
1858
|
+
|
|
1859
|
+
// src/commands/source/remove.ts
|
|
1860
|
+
import { Command as Command57 } from "commander";
|
|
1861
|
+
function createSourceRemoveCommand(client, printer = defaultPrinter) {
|
|
1862
|
+
return new Command57("remove").alias("rm").description("Remove a registered source").argument("<name>", "Source name").action(async (name) => {
|
|
1863
|
+
try {
|
|
1864
|
+
const result = await client.call("source.remove", { name });
|
|
1865
|
+
if (result.success) {
|
|
1866
|
+
printer.success(`Source "${name}" removed.`);
|
|
1867
|
+
} else {
|
|
1868
|
+
printer.warn(`Source "${name}" not found.`);
|
|
1869
|
+
}
|
|
1870
|
+
} catch (err) {
|
|
1871
|
+
presentError(err, printer);
|
|
1872
|
+
process.exitCode = 1;
|
|
1873
|
+
}
|
|
1874
|
+
});
|
|
1875
|
+
}
|
|
1876
|
+
|
|
1877
|
+
// src/commands/source/sync.ts
|
|
1878
|
+
import chalk16 from "chalk";
|
|
1879
|
+
import { Command as Command58 } from "commander";
|
|
1880
|
+
function createSourceSyncCommand(client, printer = defaultPrinter) {
|
|
1881
|
+
return new Command58("sync").description("Sync component source(s)").argument("[name]", "Source name (syncs all if omitted)").action(async (name) => {
|
|
1882
|
+
try {
|
|
1883
|
+
const result = await client.call("source.sync", { name });
|
|
1884
|
+
printer.success(`Synced: ${result.synced.join(", ")}`);
|
|
1885
|
+
const report = result.report;
|
|
1886
|
+
if (report) {
|
|
1887
|
+
const parts = [];
|
|
1888
|
+
if (report.addedCount > 0) parts.push(chalk16.green(`+${report.addedCount} added`));
|
|
1889
|
+
if (report.updatedCount > 0) parts.push(chalk16.yellow(`${report.updatedCount} updated`));
|
|
1890
|
+
if (report.removedCount > 0) parts.push(chalk16.red(`-${report.removedCount} removed`));
|
|
1891
|
+
if (parts.length > 0) {
|
|
1892
|
+
printer.log(` ${parts.join(", ")}`);
|
|
1893
|
+
}
|
|
1894
|
+
if (report.hasBreakingChanges) {
|
|
1895
|
+
printer.warn(" \u26A0 Breaking changes detected (major version bump). Review updated components.");
|
|
1896
|
+
}
|
|
1897
|
+
}
|
|
1898
|
+
} catch (err) {
|
|
1899
|
+
presentError(err, printer);
|
|
1900
|
+
process.exitCode = 1;
|
|
1901
|
+
}
|
|
1902
|
+
});
|
|
1903
|
+
}
|
|
1904
|
+
|
|
1905
|
+
// src/commands/source/index.ts
|
|
1906
|
+
function createSourceCommand(client, printer) {
|
|
1907
|
+
const cmd = new Command59("source").description("Manage component sources (GitHub repos, local dirs)");
|
|
1908
|
+
cmd.addCommand(createSourceListCommand(client, printer));
|
|
1909
|
+
cmd.addCommand(createSourceAddCommand(client, printer));
|
|
1910
|
+
cmd.addCommand(createSourceRemoveCommand(client, printer));
|
|
1911
|
+
cmd.addCommand(createSourceSyncCommand(client, printer));
|
|
1912
|
+
return cmd;
|
|
1913
|
+
}
|
|
1914
|
+
|
|
1915
|
+
// src/commands/preset/index.ts
|
|
1916
|
+
import { Command as Command63 } from "commander";
|
|
1917
|
+
|
|
1918
|
+
// src/commands/preset/list.ts
|
|
1919
|
+
import { Command as Command60 } from "commander";
|
|
1920
|
+
function createPresetListCommand(client, printer = defaultPrinter) {
|
|
1921
|
+
return new Command60("list").alias("ls").description("List available presets from registered sources").argument("[package]", "Filter by source package name").option("-f, --format <format>", "Output format: table, json, quiet", "table").action(async (packageName, opts) => {
|
|
1922
|
+
try {
|
|
1923
|
+
const presets = await client.call("preset.list", { packageName });
|
|
1924
|
+
if (opts.format === "json") {
|
|
1925
|
+
printer.log(JSON.stringify(presets, null, 2));
|
|
1926
|
+
} else if (opts.format === "quiet") {
|
|
1927
|
+
printer.log(presets.map((p) => p.name).join("\n"));
|
|
1928
|
+
} else {
|
|
1929
|
+
if (presets.length === 0) {
|
|
1930
|
+
printer.dim("No presets available.");
|
|
1931
|
+
} else {
|
|
1932
|
+
for (const p of presets) {
|
|
1933
|
+
const desc = p.description ? ` \u2014 ${p.description}` : "";
|
|
1934
|
+
printer.log(` ${p.name}${desc}`);
|
|
1935
|
+
}
|
|
1936
|
+
}
|
|
1937
|
+
}
|
|
1938
|
+
} catch (err) {
|
|
1939
|
+
presentError(err, printer);
|
|
1940
|
+
process.exitCode = 1;
|
|
1941
|
+
}
|
|
1942
|
+
});
|
|
1943
|
+
}
|
|
1944
|
+
|
|
1945
|
+
// src/commands/preset/show.ts
|
|
1946
|
+
import { Command as Command61 } from "commander";
|
|
1947
|
+
function createPresetShowCommand(client, printer = defaultPrinter) {
|
|
1948
|
+
return new Command61("show").description("Show preset details").argument("<qualified-name>", "Preset qualified name (package@preset)").option("-f, --format <format>", "Output format: table, json", "table").action(async (qualifiedName, opts) => {
|
|
1949
|
+
try {
|
|
1950
|
+
const preset = await client.call("preset.show", { qualifiedName });
|
|
1951
|
+
if (opts.format === "json") {
|
|
1952
|
+
printer.log(JSON.stringify(preset, null, 2));
|
|
1953
|
+
} else {
|
|
1954
|
+
printer.log(`Preset: ${preset.name}`);
|
|
1955
|
+
if (preset.description) printer.log(`Description: ${preset.description}`);
|
|
1956
|
+
if (preset.skills?.length) printer.log(`Skills: ${preset.skills.join(", ")}`);
|
|
1957
|
+
if (preset.prompts?.length) printer.log(`Prompts: ${preset.prompts.join(", ")}`);
|
|
1958
|
+
if (preset.mcpServers?.length) printer.log(`MCP Servers: ${preset.mcpServers.join(", ")}`);
|
|
1959
|
+
if (preset.workflows?.length) printer.log(`Workflows: ${preset.workflows.join(", ")}`);
|
|
1960
|
+
}
|
|
1961
|
+
} catch (err) {
|
|
1962
|
+
presentError(err, printer);
|
|
1963
|
+
process.exitCode = 1;
|
|
1964
|
+
}
|
|
1965
|
+
});
|
|
1966
|
+
}
|
|
1967
|
+
|
|
1968
|
+
// src/commands/preset/apply.ts
|
|
1969
|
+
import { Command as Command62 } from "commander";
|
|
1970
|
+
function createPresetApplyCommand(client, printer = defaultPrinter) {
|
|
1971
|
+
return new Command62("apply").description("Apply a preset to a template (adds all preset components)").argument("<qualified-name>", "Preset qualified name (package@preset)").argument("<template>", "Template name to apply to").action(async (qualifiedName, templateName) => {
|
|
1972
|
+
try {
|
|
1973
|
+
const result = await client.call("preset.apply", { qualifiedName, templateName });
|
|
1974
|
+
printer.success(`Preset "${qualifiedName}" applied to template "${result.name}".`);
|
|
1975
|
+
} catch (err) {
|
|
1976
|
+
presentError(err, printer);
|
|
1977
|
+
process.exitCode = 1;
|
|
1978
|
+
}
|
|
1979
|
+
});
|
|
1980
|
+
}
|
|
1981
|
+
|
|
1982
|
+
// src/commands/preset/index.ts
|
|
1983
|
+
function createPresetCommand(client, printer) {
|
|
1984
|
+
const cmd = new Command63("preset").description("Manage component presets (bundled compositions)");
|
|
1985
|
+
cmd.addCommand(createPresetListCommand(client, printer));
|
|
1986
|
+
cmd.addCommand(createPresetShowCommand(client, printer));
|
|
1987
|
+
cmd.addCommand(createPresetApplyCommand(client, printer));
|
|
1988
|
+
return cmd;
|
|
1989
|
+
}
|
|
1990
|
+
|
|
1991
|
+
// src/commands/schedule/index.ts
|
|
1992
|
+
import { Command as Command65 } from "commander";
|
|
1993
|
+
|
|
1994
|
+
// src/commands/schedule/list.ts
|
|
1995
|
+
import { Command as Command64 } from "commander";
|
|
1996
|
+
function createScheduleListCommand(client, printer = defaultPrinter) {
|
|
1997
|
+
return new Command64("list").alias("ls").description("List schedule sources for an agent").argument("<name>", "Agent name").option("-f, --format <format>", "Output format: table, json", "table").action(async (name, opts) => {
|
|
1998
|
+
try {
|
|
1999
|
+
const result = await client.call("schedule.list", { name });
|
|
2000
|
+
if (opts.format === "json") {
|
|
2001
|
+
printer.log(JSON.stringify(result, null, 2));
|
|
2002
|
+
} else {
|
|
2003
|
+
if (result.sources.length === 0 && !result.running) {
|
|
2004
|
+
printer.dim(`No scheduler for agent "${name}".`);
|
|
2005
|
+
} else {
|
|
2006
|
+
printer.log(`Scheduler: ${result.running ? "running" : "stopped"}`);
|
|
2007
|
+
for (const s of result.sources) {
|
|
2008
|
+
printer.log(` ${s.id} (${s.type}) ${s.active ? "active" : "inactive"}`);
|
|
2009
|
+
}
|
|
2010
|
+
}
|
|
2011
|
+
}
|
|
2012
|
+
} catch (err) {
|
|
2013
|
+
presentError(err, printer);
|
|
2014
|
+
process.exitCode = 1;
|
|
2015
|
+
}
|
|
2016
|
+
});
|
|
2017
|
+
}
|
|
2018
|
+
|
|
2019
|
+
// src/commands/schedule/index.ts
|
|
2020
|
+
function createScheduleCommand(client, printer) {
|
|
2021
|
+
const cmd = new Command65("schedule").description("Manage agent schedules (heartbeat, cron, hooks)");
|
|
2022
|
+
cmd.addCommand(createScheduleListCommand(client, printer));
|
|
2023
|
+
return cmd;
|
|
2024
|
+
}
|
|
2025
|
+
|
|
2026
|
+
// src/commands/daemon/index.ts
|
|
2027
|
+
import { Command as Command69 } from "commander";
|
|
2028
|
+
|
|
2029
|
+
// src/commands/daemon/start.ts
|
|
2030
|
+
import { Command as Command66 } from "commander";
|
|
2031
|
+
import { join } from "path";
|
|
2032
|
+
import { fork, spawn } from "child_process";
|
|
2033
|
+
import chalk17 from "chalk";
|
|
2034
|
+
import { onShutdownSignal, isWindows, isSingleExecutable } from "@actant/shared";
|
|
2035
|
+
var SEA_DAEMON_FLAG = "--__actant-daemon";
|
|
2036
|
+
function spawnDaemonChild() {
|
|
2037
|
+
if (isSingleExecutable()) {
|
|
2038
|
+
return spawn(process.execPath, [SEA_DAEMON_FLAG], {
|
|
2039
|
+
detached: true,
|
|
2040
|
+
stdio: ["ignore", "ignore", "pipe"],
|
|
2041
|
+
env: process.env
|
|
2042
|
+
});
|
|
2043
|
+
}
|
|
2044
|
+
const daemonScript = join(import.meta.dirname, "daemon-entry.js");
|
|
2045
|
+
return isWindows() ? spawn(process.execPath, [daemonScript], {
|
|
2046
|
+
detached: true,
|
|
2047
|
+
stdio: ["ignore", "ignore", "pipe"],
|
|
2048
|
+
env: process.env
|
|
2049
|
+
}) : fork(daemonScript, [], {
|
|
2050
|
+
detached: true,
|
|
2051
|
+
stdio: ["ignore", "ignore", "pipe", "ipc"],
|
|
2052
|
+
env: process.env
|
|
2053
|
+
});
|
|
2054
|
+
}
|
|
2055
|
+
function createDaemonStartCommand(printer = defaultPrinter) {
|
|
2056
|
+
return new Command66("start").description("Start the Actant daemon").option("--foreground", "Run in foreground (don't daemonize)", false).action(async (opts) => {
|
|
2057
|
+
const client = new RpcClient(defaultSocketPath());
|
|
2058
|
+
const alive = await client.ping();
|
|
2059
|
+
if (alive) {
|
|
2060
|
+
printer.warn("Daemon is already running.");
|
|
2061
|
+
return;
|
|
2062
|
+
}
|
|
2063
|
+
if (opts.foreground) {
|
|
2064
|
+
try {
|
|
2065
|
+
const { Daemon } = await import("@actant/api");
|
|
2066
|
+
const daemon = new Daemon();
|
|
2067
|
+
onShutdownSignal(async () => {
|
|
2068
|
+
await daemon.stop();
|
|
2069
|
+
process.exit(0);
|
|
2070
|
+
});
|
|
2071
|
+
await daemon.start();
|
|
2072
|
+
printer.log(`${chalk17.green("Daemon started (foreground).")} PID: ${process.pid}`);
|
|
2073
|
+
printer.dim("Press Ctrl+C to stop.");
|
|
2074
|
+
await new Promise(() => {
|
|
2075
|
+
});
|
|
2076
|
+
} catch (err) {
|
|
2077
|
+
presentError(err, printer);
|
|
2078
|
+
process.exitCode = 1;
|
|
2079
|
+
}
|
|
2080
|
+
} else {
|
|
2081
|
+
const child = spawnDaemonChild();
|
|
2082
|
+
const stderrChunks = [];
|
|
2083
|
+
child.stderr?.on("data", (chunk) => {
|
|
2084
|
+
stderrChunks.push(chunk);
|
|
2085
|
+
});
|
|
2086
|
+
let earlyExit = false;
|
|
2087
|
+
let earlyExitCode = null;
|
|
2088
|
+
child.on("exit", (code) => {
|
|
2089
|
+
earlyExit = true;
|
|
2090
|
+
earlyExitCode = code;
|
|
2091
|
+
});
|
|
2092
|
+
await new Promise((resolve9) => setTimeout(resolve9, 2e3));
|
|
2093
|
+
if (earlyExit) {
|
|
2094
|
+
const stderr = Buffer.concat(stderrChunks).toString().trim();
|
|
2095
|
+
printer.error(`Daemon process exited unexpectedly (code: ${earlyExitCode}).`);
|
|
2096
|
+
if (stderr) printer.error(stderr);
|
|
2097
|
+
process.exitCode = 1;
|
|
2098
|
+
return;
|
|
2099
|
+
}
|
|
2100
|
+
let healthy = false;
|
|
2101
|
+
for (let i = 0; i < 3; i++) {
|
|
2102
|
+
healthy = await client.ping();
|
|
2103
|
+
if (healthy) break;
|
|
2104
|
+
await new Promise((resolve9) => setTimeout(resolve9, 500));
|
|
2105
|
+
}
|
|
2106
|
+
child.stderr?.destroy();
|
|
2107
|
+
if ("connected" in child && child.connected) child.disconnect();
|
|
2108
|
+
child.unref();
|
|
2109
|
+
if (healthy) {
|
|
2110
|
+
printer.log(`${chalk17.green("Daemon started.")} PID: ${child.pid}`);
|
|
2111
|
+
} else {
|
|
2112
|
+
const stderr = Buffer.concat(stderrChunks).toString().trim();
|
|
2113
|
+
printer.error("Daemon process started but is not responding.");
|
|
2114
|
+
if (stderr) printer.error(stderr);
|
|
2115
|
+
process.exitCode = 1;
|
|
2116
|
+
}
|
|
2117
|
+
}
|
|
2118
|
+
});
|
|
2119
|
+
}
|
|
2120
|
+
|
|
2121
|
+
// src/commands/daemon/stop.ts
|
|
2122
|
+
import { Command as Command67 } from "commander";
|
|
2123
|
+
function createDaemonStopCommand(printer = defaultPrinter, client) {
|
|
2124
|
+
return new Command67("stop").description("Stop the Actant daemon").action(async () => {
|
|
2125
|
+
const rpc = client ?? new RpcClient(defaultSocketPath());
|
|
2126
|
+
try {
|
|
2127
|
+
await rpc.call("daemon.shutdown", {});
|
|
2128
|
+
printer.success("Daemon stopping...");
|
|
2129
|
+
} catch {
|
|
2130
|
+
printer.warn("Daemon is not running.");
|
|
2131
|
+
}
|
|
2132
|
+
});
|
|
2133
|
+
}
|
|
2134
|
+
|
|
2135
|
+
// src/commands/daemon/status.ts
|
|
2136
|
+
import { Command as Command68 } from "commander";
|
|
2137
|
+
import chalk18 from "chalk";
|
|
2138
|
+
function createDaemonStatusCommand(printer = defaultPrinter) {
|
|
2139
|
+
return new Command68("status").description("Check if the daemon is running").option("-f, --format <format>", "Output format: table, json, quiet", "table").action(async (opts) => {
|
|
2140
|
+
const client = new RpcClient(defaultSocketPath());
|
|
2141
|
+
try {
|
|
2142
|
+
const result = await client.call("daemon.ping", {});
|
|
2143
|
+
if (opts.format === "json") {
|
|
2144
|
+
printer.log(JSON.stringify({ running: true, ...result }, null, 2));
|
|
2145
|
+
} else if (opts.format === "quiet") {
|
|
2146
|
+
printer.log("running");
|
|
2147
|
+
} else {
|
|
2148
|
+
printer.success("Daemon is running.");
|
|
2149
|
+
printer.log(` Version: ${result.version}`);
|
|
2150
|
+
printer.log(` Uptime: ${result.uptime}s`);
|
|
2151
|
+
printer.log(` Agents: ${result.agents}`);
|
|
2152
|
+
}
|
|
2153
|
+
} catch {
|
|
2154
|
+
if (opts.format === "json") {
|
|
2155
|
+
printer.log(JSON.stringify({ running: false }, null, 2));
|
|
2156
|
+
} else if (opts.format === "quiet") {
|
|
2157
|
+
printer.log("stopped");
|
|
2158
|
+
} else {
|
|
2159
|
+
printer.log(chalk18.red("Daemon is not running."));
|
|
2160
|
+
printer.dim("Start with: actant daemon start");
|
|
2161
|
+
}
|
|
2162
|
+
process.exitCode = 1;
|
|
2163
|
+
}
|
|
2164
|
+
});
|
|
2165
|
+
}
|
|
2166
|
+
|
|
2167
|
+
// src/commands/daemon/index.ts
|
|
2168
|
+
function createDaemonCommand(printer) {
|
|
2169
|
+
const cmd = new Command69("daemon").description("Manage the Actant daemon");
|
|
2170
|
+
cmd.addCommand(createDaemonStartCommand(printer));
|
|
2171
|
+
cmd.addCommand(createDaemonStopCommand(printer));
|
|
2172
|
+
cmd.addCommand(createDaemonStatusCommand(printer));
|
|
2173
|
+
return cmd;
|
|
2174
|
+
}
|
|
2175
|
+
|
|
2176
|
+
// src/commands/proxy.ts
|
|
2177
|
+
import { Command as Command70 } from "commander";
|
|
2178
|
+
import { spawn as spawn2 } from "child_process";
|
|
2179
|
+
import { createInterface as createInterface3 } from "readline";
|
|
2180
|
+
function createProxyCommand(printer = defaultPrinter) {
|
|
2181
|
+
return new Command70("proxy").description("Run an ACP proxy for an agent (stdin/stdout ACP protocol)").argument("<name>", "Agent name to proxy").option("--lease", "Use Session Lease mode (requires running agent)", false).option("-t, --template <template>", "Template name (auto-creates instance if not found)").action(async (name, opts) => {
|
|
2182
|
+
if (opts.lease) {
|
|
2183
|
+
await runSessionLease(name, printer);
|
|
2184
|
+
} else {
|
|
2185
|
+
await runDirectBridge(name, opts, printer);
|
|
2186
|
+
}
|
|
2187
|
+
});
|
|
2188
|
+
}
|
|
2189
|
+
async function runDirectBridge(name, opts, printer) {
|
|
2190
|
+
const client = new RpcClient(defaultSocketPath());
|
|
2191
|
+
let child = null;
|
|
2192
|
+
let instanceName = name;
|
|
2193
|
+
let attached = false;
|
|
2194
|
+
let cleaning = false;
|
|
2195
|
+
const cleanup = async () => {
|
|
2196
|
+
if (cleaning) return;
|
|
2197
|
+
cleaning = true;
|
|
2198
|
+
if (child && !child.killed) {
|
|
2199
|
+
child.kill("SIGTERM");
|
|
2200
|
+
await new Promise((resolve9) => {
|
|
2201
|
+
const timer = setTimeout(() => {
|
|
2202
|
+
child?.kill("SIGKILL");
|
|
2203
|
+
resolve9();
|
|
2204
|
+
}, 3e3);
|
|
2205
|
+
child?.once("exit", () => {
|
|
2206
|
+
clearTimeout(timer);
|
|
2207
|
+
resolve9();
|
|
2208
|
+
});
|
|
2209
|
+
});
|
|
2210
|
+
}
|
|
2211
|
+
if (attached) {
|
|
2212
|
+
try {
|
|
2213
|
+
await client.call("agent.detach", { name: instanceName, cleanup: true });
|
|
2214
|
+
} catch {
|
|
2215
|
+
}
|
|
2216
|
+
attached = false;
|
|
2217
|
+
}
|
|
2218
|
+
};
|
|
2219
|
+
try {
|
|
2220
|
+
const alive = await client.ping();
|
|
2221
|
+
if (!alive) {
|
|
2222
|
+
printer.error("Daemon is not running. Start it with: actant daemon start");
|
|
2223
|
+
process.exitCode = 1;
|
|
2224
|
+
return;
|
|
2225
|
+
}
|
|
2226
|
+
const resolved = await client.call("agent.resolve", {
|
|
2227
|
+
name,
|
|
2228
|
+
template: opts.template
|
|
2229
|
+
});
|
|
2230
|
+
instanceName = resolved.instanceName;
|
|
2231
|
+
const targetInstance = await resolveAvailableInstance(
|
|
2232
|
+
client,
|
|
2233
|
+
instanceName,
|
|
2234
|
+
resolved,
|
|
2235
|
+
printer
|
|
2236
|
+
);
|
|
2237
|
+
if (!targetInstance) {
|
|
2238
|
+
process.exitCode = 1;
|
|
2239
|
+
return;
|
|
2240
|
+
}
|
|
2241
|
+
instanceName = targetInstance.instanceName;
|
|
2242
|
+
const workspaceDir = targetInstance.workspaceDir;
|
|
2243
|
+
const command = targetInstance.command;
|
|
2244
|
+
const args = targetInstance.args;
|
|
2245
|
+
const env = { ...process.env, ...targetInstance.env };
|
|
2246
|
+
child = spawn2(command, args, {
|
|
2247
|
+
cwd: workspaceDir,
|
|
2248
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
2249
|
+
env
|
|
2250
|
+
});
|
|
2251
|
+
if (!child.stdin || !child.stdout || child.pid == null) {
|
|
2252
|
+
throw new Error("Failed to create stdio pipes for agent subprocess");
|
|
2253
|
+
}
|
|
2254
|
+
const childPid = child.pid;
|
|
2255
|
+
const childProc = child;
|
|
2256
|
+
await client.call("agent.attach", {
|
|
2257
|
+
name: instanceName,
|
|
2258
|
+
pid: childPid,
|
|
2259
|
+
metadata: { proxyMode: "direct-bridge" }
|
|
2260
|
+
});
|
|
2261
|
+
attached = true;
|
|
2262
|
+
process.on("SIGINT", () => {
|
|
2263
|
+
void cleanup().then(() => process.exit(0));
|
|
2264
|
+
});
|
|
2265
|
+
process.on("SIGTERM", () => {
|
|
2266
|
+
void cleanup().then(() => process.exit(0));
|
|
2267
|
+
});
|
|
2268
|
+
process.stdin.pipe(child.stdin);
|
|
2269
|
+
child.stdout.pipe(process.stdout);
|
|
2270
|
+
child.stderr?.pipe(process.stderr);
|
|
2271
|
+
const exitCode = await new Promise((resolve9) => {
|
|
2272
|
+
childProc.on("exit", (code) => resolve9(code ?? 1));
|
|
2273
|
+
childProc.on("error", (err) => {
|
|
2274
|
+
printer.error(`Agent process error: ${err.message}`);
|
|
2275
|
+
resolve9(1);
|
|
2276
|
+
});
|
|
2277
|
+
});
|
|
2278
|
+
if (attached) {
|
|
2279
|
+
try {
|
|
2280
|
+
await client.call("agent.detach", { name: instanceName, cleanup: true });
|
|
2281
|
+
} catch {
|
|
2282
|
+
}
|
|
2283
|
+
attached = false;
|
|
2284
|
+
}
|
|
2285
|
+
process.exitCode = exitCode;
|
|
2286
|
+
} catch (err) {
|
|
2287
|
+
await cleanup();
|
|
2288
|
+
presentError(err, printer);
|
|
2289
|
+
process.exitCode = 1;
|
|
2290
|
+
}
|
|
2291
|
+
}
|
|
2292
|
+
async function resolveAvailableInstance(client, instanceName, resolved, printer) {
|
|
2293
|
+
try {
|
|
2294
|
+
const status = await client.call("agent.status", { name: instanceName });
|
|
2295
|
+
if (status.status === "running" && status.pid != null) {
|
|
2296
|
+
const templateName = status.templateName;
|
|
2297
|
+
if (!templateName) {
|
|
2298
|
+
printer.error(
|
|
2299
|
+
`Agent "${instanceName}" is already running (pid ${status.pid}) and has no template for auto-instantiation.`
|
|
2300
|
+
);
|
|
2301
|
+
return null;
|
|
2302
|
+
}
|
|
2303
|
+
const ephemeralName = `${instanceName}-proxy-${Date.now()}`;
|
|
2304
|
+
const ephemeral = await client.call("agent.resolve", {
|
|
2305
|
+
name: ephemeralName,
|
|
2306
|
+
template: templateName,
|
|
2307
|
+
overrides: { workspacePolicy: "ephemeral" }
|
|
2308
|
+
});
|
|
2309
|
+
printer.dim(
|
|
2310
|
+
`Instance "${instanceName}" occupied \u2192 created ephemeral "${ephemeralName}"`
|
|
2311
|
+
);
|
|
2312
|
+
return {
|
|
2313
|
+
instanceName: ephemeral.instanceName,
|
|
2314
|
+
workspaceDir: ephemeral.workspaceDir,
|
|
2315
|
+
command: ephemeral.command,
|
|
2316
|
+
args: ephemeral.args,
|
|
2317
|
+
env: ephemeral.env
|
|
2318
|
+
};
|
|
2319
|
+
}
|
|
2320
|
+
} catch {
|
|
2321
|
+
}
|
|
2322
|
+
return {
|
|
2323
|
+
instanceName: resolved.instanceName,
|
|
2324
|
+
workspaceDir: resolved.workspaceDir,
|
|
2325
|
+
command: resolved.command,
|
|
2326
|
+
args: resolved.args,
|
|
2327
|
+
env: resolved.env
|
|
2328
|
+
};
|
|
2329
|
+
}
|
|
2330
|
+
async function runSessionLease(agentName, printer) {
|
|
2331
|
+
const rpcClient = new RpcClient(defaultSocketPath());
|
|
2332
|
+
try {
|
|
2333
|
+
const alive = await rpcClient.ping();
|
|
2334
|
+
if (!alive) {
|
|
2335
|
+
printer.error("Daemon is not running. Start it with: actant daemon start");
|
|
2336
|
+
process.exitCode = 1;
|
|
2337
|
+
return;
|
|
2338
|
+
}
|
|
2339
|
+
const meta = await rpcClient.call("agent.status", { name: agentName });
|
|
2340
|
+
if (meta.status !== "running") {
|
|
2341
|
+
printer.error(
|
|
2342
|
+
`Agent "${agentName}" is not running (status: ${meta.status}). Session Lease requires a running agent. Start with: actant agent start ${agentName}`
|
|
2343
|
+
);
|
|
2344
|
+
process.exitCode = 1;
|
|
2345
|
+
return;
|
|
2346
|
+
}
|
|
2347
|
+
} catch (err) {
|
|
2348
|
+
presentError(err, printer);
|
|
2349
|
+
process.exitCode = 1;
|
|
2350
|
+
return;
|
|
2351
|
+
}
|
|
2352
|
+
let gatewaySocketPath = null;
|
|
2353
|
+
try {
|
|
2354
|
+
const result = await rpcClient.call("gateway.lease", { agentName });
|
|
2355
|
+
gatewaySocketPath = result.socketPath;
|
|
2356
|
+
} catch {
|
|
2357
|
+
}
|
|
2358
|
+
if (gatewaySocketPath) {
|
|
2359
|
+
await runGatewayPipe(gatewaySocketPath, printer);
|
|
2360
|
+
} else {
|
|
2361
|
+
printer.dim("Gateway lease not available, using legacy session lease mode");
|
|
2362
|
+
await runLegacySessionLease(agentName, rpcClient, printer);
|
|
2363
|
+
}
|
|
2364
|
+
}
|
|
2365
|
+
async function runGatewayPipe(gatewaySocketPath, _printer) {
|
|
2366
|
+
const net = await import("net");
|
|
2367
|
+
const socket = net.createConnection(gatewaySocketPath);
|
|
2368
|
+
let cleaning = false;
|
|
2369
|
+
const cleanup = () => {
|
|
2370
|
+
if (cleaning) return;
|
|
2371
|
+
cleaning = true;
|
|
2372
|
+
socket.destroy();
|
|
2373
|
+
};
|
|
2374
|
+
process.on("SIGINT", () => {
|
|
2375
|
+
cleanup();
|
|
2376
|
+
process.exit(0);
|
|
2377
|
+
});
|
|
2378
|
+
process.on("SIGTERM", () => {
|
|
2379
|
+
cleanup();
|
|
2380
|
+
process.exit(0);
|
|
2381
|
+
});
|
|
2382
|
+
socket.on("connect", () => {
|
|
2383
|
+
process.stdin.pipe(socket);
|
|
2384
|
+
socket.pipe(process.stdout);
|
|
2385
|
+
});
|
|
2386
|
+
socket.on("error", (err) => {
|
|
2387
|
+
_printer.error(`Gateway connection error: ${err.message}`);
|
|
2388
|
+
cleanup();
|
|
2389
|
+
process.exitCode = 1;
|
|
2390
|
+
});
|
|
2391
|
+
socket.on("close", () => {
|
|
2392
|
+
cleanup();
|
|
2393
|
+
process.exit(0);
|
|
2394
|
+
});
|
|
2395
|
+
await new Promise(() => {
|
|
2396
|
+
});
|
|
2397
|
+
}
|
|
2398
|
+
async function runLegacySessionLease(agentName, client, _printer) {
|
|
2399
|
+
const clientId = `proxy-${Date.now()}`;
|
|
2400
|
+
let sessionId = null;
|
|
2401
|
+
let closed = false;
|
|
2402
|
+
const cleanup = async () => {
|
|
2403
|
+
if (closed) return;
|
|
2404
|
+
closed = true;
|
|
2405
|
+
if (sessionId) {
|
|
2406
|
+
try {
|
|
2407
|
+
await client.call("session.close", { sessionId });
|
|
2408
|
+
} catch {
|
|
2409
|
+
}
|
|
2410
|
+
}
|
|
2411
|
+
};
|
|
2412
|
+
process.on("SIGINT", () => {
|
|
2413
|
+
void cleanup().then(() => process.exit(0));
|
|
2414
|
+
});
|
|
2415
|
+
process.on("SIGTERM", () => {
|
|
2416
|
+
void cleanup().then(() => process.exit(0));
|
|
2417
|
+
});
|
|
2418
|
+
const rl = createInterface3({ input: process.stdin });
|
|
2419
|
+
rl.on("line", (line) => {
|
|
2420
|
+
if (closed) return;
|
|
2421
|
+
void handleLegacyMessage(line, agentName, clientId, client, {
|
|
2422
|
+
getSessionId: () => sessionId,
|
|
2423
|
+
setSessionId: (id) => {
|
|
2424
|
+
sessionId = id;
|
|
2425
|
+
}
|
|
2426
|
+
}).catch((err) => {
|
|
2427
|
+
writeAcpError(null, -32603, err instanceof Error ? err.message : String(err));
|
|
2428
|
+
});
|
|
2429
|
+
});
|
|
2430
|
+
rl.on("close", () => {
|
|
2431
|
+
void cleanup().then(() => process.exit(0));
|
|
2432
|
+
});
|
|
2433
|
+
}
|
|
2434
|
+
async function handleLegacyMessage(line, agentName, clientId, client, ctx) {
|
|
2435
|
+
let msg;
|
|
2436
|
+
try {
|
|
2437
|
+
msg = JSON.parse(line);
|
|
2438
|
+
} catch {
|
|
2439
|
+
return;
|
|
2440
|
+
}
|
|
2441
|
+
if (!msg.method) return;
|
|
2442
|
+
const params = msg.params ?? {};
|
|
2443
|
+
switch (msg.method) {
|
|
2444
|
+
case "initialize":
|
|
2445
|
+
writeAcpResponse(msg.id, {
|
|
2446
|
+
protocolVersion: 1,
|
|
2447
|
+
agentInfo: { name: "actant-proxy", title: `Actant Proxy: ${agentName} (lease)`, version: "0.1.0" },
|
|
2448
|
+
agentCapabilities: {}
|
|
2449
|
+
});
|
|
2450
|
+
break;
|
|
2451
|
+
case "session/new":
|
|
2452
|
+
try {
|
|
2453
|
+
const result = await client.call("session.create", { agentName, clientId });
|
|
2454
|
+
ctx.setSessionId(result.sessionId);
|
|
2455
|
+
writeAcpResponse(msg.id, { sessionId: result.sessionId, modes: {} });
|
|
2456
|
+
} catch (err) {
|
|
2457
|
+
writeAcpError(msg.id, -32603, err instanceof Error ? err.message : String(err));
|
|
2458
|
+
}
|
|
2459
|
+
break;
|
|
2460
|
+
case "session/prompt": {
|
|
2461
|
+
const sid = params["sessionId"] ?? ctx.getSessionId();
|
|
2462
|
+
if (!sid) {
|
|
2463
|
+
writeAcpError(msg.id, -32602, "No session. Call session/new first.");
|
|
2464
|
+
break;
|
|
2465
|
+
}
|
|
2466
|
+
try {
|
|
2467
|
+
const prompt = params["prompt"];
|
|
2468
|
+
const text = prompt?.find((p) => p.type === "text")?.text ?? "";
|
|
2469
|
+
const result = await client.call("session.prompt", { sessionId: sid, text }, { timeoutMs: 305e3 });
|
|
2470
|
+
writeAcpResponse(msg.id, { stopReason: result.stopReason });
|
|
2471
|
+
} catch (err) {
|
|
2472
|
+
writeAcpError(msg.id, -32603, err instanceof Error ? err.message : String(err));
|
|
2473
|
+
}
|
|
2474
|
+
break;
|
|
2475
|
+
}
|
|
2476
|
+
case "session/cancel": {
|
|
2477
|
+
const sid = params["sessionId"] ?? ctx.getSessionId();
|
|
2478
|
+
if (sid) {
|
|
2479
|
+
try {
|
|
2480
|
+
await client.call("session.cancel", { sessionId: sid });
|
|
2481
|
+
} catch {
|
|
2482
|
+
}
|
|
2483
|
+
}
|
|
2484
|
+
if (msg.id != null) writeAcpResponse(msg.id, { ok: true });
|
|
2485
|
+
break;
|
|
2486
|
+
}
|
|
2487
|
+
default:
|
|
2488
|
+
if (msg.id != null) writeAcpError(msg.id, -32601, `Unsupported method: ${msg.method}`);
|
|
2489
|
+
}
|
|
2490
|
+
}
|
|
2491
|
+
function writeAcpResponse(id, result) {
|
|
2492
|
+
process.stdout.write(JSON.stringify({ jsonrpc: "2.0", id: id ?? null, result }) + "\n");
|
|
2493
|
+
}
|
|
2494
|
+
function writeAcpError(id, code, message) {
|
|
2495
|
+
process.stdout.write(JSON.stringify({ jsonrpc: "2.0", id: id ?? null, error: { code, message } }) + "\n");
|
|
2496
|
+
}
|
|
2497
|
+
|
|
2498
|
+
// src/commands/self-update.ts
|
|
2499
|
+
import { Command as Command71 } from "commander";
|
|
2500
|
+
import { spawn as spawn3 } from "child_process";
|
|
2501
|
+
import { writeFileSync, mkdirSync, readFileSync, existsSync as existsSync2 } from "fs";
|
|
2502
|
+
import { join as join2 } from "path";
|
|
2503
|
+
import { homedir } from "os";
|
|
2504
|
+
import chalk19 from "chalk";
|
|
2505
|
+
function createSelfUpdateCommand() {
|
|
2506
|
+
return new Command71("self-update").description("Update Actant from local source").option("--source <path>", "Source directory path").option("--check", "Only check for updates, don't apply").option("--force", "Skip active session warnings").option("--dry-run", "Show what would be done without doing it").option("--no-agent", "Skip agent supervisor, run script directly").option("--skip-build", "Skip build step (use pre-built dist)").action(async (opts) => {
|
|
2507
|
+
const actantHome = process.env.ACTANT_HOME || join2(homedir(), ".actant");
|
|
2508
|
+
let devSourcePath = opts.source;
|
|
2509
|
+
if (!devSourcePath) {
|
|
2510
|
+
const configPath = join2(actantHome, "config.json");
|
|
2511
|
+
try {
|
|
2512
|
+
const config = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
2513
|
+
devSourcePath = config.devSourcePath;
|
|
2514
|
+
} catch {
|
|
2515
|
+
}
|
|
2516
|
+
}
|
|
2517
|
+
if (!devSourcePath) {
|
|
2518
|
+
console.error(
|
|
2519
|
+
chalk19.red(
|
|
2520
|
+
"No source path specified. Use --source <path> or set devSourcePath in ~/.actant/config.json"
|
|
2521
|
+
)
|
|
2522
|
+
);
|
|
2523
|
+
process.exit(1);
|
|
2524
|
+
}
|
|
2525
|
+
if (opts.check) {
|
|
2526
|
+
showVersionCheck(devSourcePath, actantHome);
|
|
2527
|
+
return;
|
|
2528
|
+
}
|
|
2529
|
+
let installedVersion = "0.1.0";
|
|
2530
|
+
const cliPkgPath = join2(devSourcePath, "packages", "cli", "package.json");
|
|
2531
|
+
if (existsSync2(cliPkgPath)) {
|
|
2532
|
+
try {
|
|
2533
|
+
const cliPkg = JSON.parse(readFileSync(cliPkgPath, "utf-8"));
|
|
2534
|
+
installedVersion = cliPkg.version ?? installedVersion;
|
|
2535
|
+
} catch {
|
|
2536
|
+
}
|
|
2537
|
+
}
|
|
2538
|
+
const updateId = `upd-${(/* @__PURE__ */ new Date()).toISOString().replace(/[-:T.]/g, "").slice(0, 15)}`;
|
|
2539
|
+
const manifestPath = join2(actantHome, "update-manifest.json");
|
|
2540
|
+
const manifest = {
|
|
2541
|
+
updateId,
|
|
2542
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2543
|
+
sourcePath: devSourcePath,
|
|
2544
|
+
installedVersion: { version: installedVersion },
|
|
2545
|
+
backupPath: join2(actantHome, "backups", updateId),
|
|
2546
|
+
runningAgents: [],
|
|
2547
|
+
daemonSocketPath: join2(actantHome, "daemon.sock"),
|
|
2548
|
+
rollbackOnFailure: true,
|
|
2549
|
+
phase: "pending",
|
|
2550
|
+
useAgent: !opts.noAgent
|
|
2551
|
+
};
|
|
2552
|
+
mkdirSync(actantHome, { recursive: true });
|
|
2553
|
+
writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
|
|
2554
|
+
console.log(chalk19.cyan("=== Actant Self-Update ==="));
|
|
2555
|
+
console.log(`Update ID: ${updateId}`);
|
|
2556
|
+
console.log(`Source: ${devSourcePath}`);
|
|
2557
|
+
const scriptPath = join2(devSourcePath, "scripts", "self-update.js");
|
|
2558
|
+
const scriptArgs = ["--manifest", manifestPath];
|
|
2559
|
+
if (opts.dryRun) scriptArgs.push("--dry-run");
|
|
2560
|
+
if (opts.skipBuild) scriptArgs.push("--skip-build");
|
|
2561
|
+
console.log(chalk19.gray(`Spawning: node ${scriptPath} ${scriptArgs.join(" ")}`));
|
|
2562
|
+
const child = spawn3("node", [scriptPath, ...scriptArgs], {
|
|
2563
|
+
detached: !opts.noAgent,
|
|
2564
|
+
stdio: opts.noAgent ? "inherit" : "ignore"
|
|
2565
|
+
});
|
|
2566
|
+
if (!opts.noAgent) {
|
|
2567
|
+
child.unref();
|
|
2568
|
+
console.log(chalk19.green("Update script spawned in background. Check logs at:"));
|
|
2569
|
+
console.log(chalk19.gray(` ${join2(actantHome, "logs", `update-${updateId}.log`)}`));
|
|
2570
|
+
} else {
|
|
2571
|
+
child.on("exit", (code) => {
|
|
2572
|
+
if (code === 0) {
|
|
2573
|
+
console.log(chalk19.green("\nUpdate completed successfully!"));
|
|
2574
|
+
} else if (code === 1) {
|
|
2575
|
+
console.log(chalk19.yellow("\nUpdate failed, rolled back to previous version."));
|
|
2576
|
+
} else {
|
|
2577
|
+
console.log(chalk19.red("\nSevere failure \u2014 manual intervention may be needed."));
|
|
2578
|
+
}
|
|
2579
|
+
});
|
|
2580
|
+
}
|
|
2581
|
+
});
|
|
2582
|
+
}
|
|
2583
|
+
function showVersionCheck(sourcePath, actantHome) {
|
|
2584
|
+
console.log(chalk19.cyan("=== Version Check ==="));
|
|
2585
|
+
try {
|
|
2586
|
+
const pkg = JSON.parse(readFileSync(join2(sourcePath, "package.json"), "utf-8"));
|
|
2587
|
+
console.log(`Source version: ${pkg.version || "unknown"}`);
|
|
2588
|
+
} catch {
|
|
2589
|
+
console.log("Source version: unknown");
|
|
2590
|
+
}
|
|
2591
|
+
const manifestPath = join2(actantHome, "update-manifest.json");
|
|
2592
|
+
try {
|
|
2593
|
+
const manifest = JSON.parse(readFileSync(manifestPath, "utf-8"));
|
|
2594
|
+
console.log(`Last update: ${manifest.updateId} (phase: ${manifest.phase})`);
|
|
2595
|
+
} catch {
|
|
2596
|
+
console.log("Last update: none");
|
|
2597
|
+
}
|
|
2598
|
+
}
|
|
2599
|
+
|
|
2600
|
+
// src/program.ts
|
|
2601
|
+
function defaultSocketPath() {
|
|
2602
|
+
return process.env["ACTANT_SOCKET"] ?? getDefaultIpcPath();
|
|
2603
|
+
}
|
|
2604
|
+
function createProgram(socketPath, printer) {
|
|
2605
|
+
const sock = socketPath ?? defaultSocketPath();
|
|
2606
|
+
const client = new RpcClient(sock);
|
|
2607
|
+
const program = new Command72("actant").version("0.1.0").description("Actant \u2014 Build, manage, and compose AI agents");
|
|
2608
|
+
program.addCommand(createTemplateCommand(client, printer));
|
|
2609
|
+
program.addCommand(createAgentCommand(client, printer));
|
|
2610
|
+
program.addCommand(createSkillCommand(client, printer));
|
|
2611
|
+
program.addCommand(createPromptCommand(client, printer));
|
|
2612
|
+
program.addCommand(createMcpCommand(client, printer));
|
|
2613
|
+
program.addCommand(createWorkflowCommand(client, printer));
|
|
2614
|
+
program.addCommand(createPluginCommand(client, printer));
|
|
2615
|
+
program.addCommand(createSourceCommand(client, printer));
|
|
2616
|
+
program.addCommand(createPresetCommand(client, printer));
|
|
2617
|
+
program.addCommand(createScheduleCommand(client, printer));
|
|
2618
|
+
program.addCommand(createDaemonCommand(printer));
|
|
2619
|
+
program.addCommand(createProxyCommand(printer));
|
|
2620
|
+
program.addCommand(createHelpCommand());
|
|
2621
|
+
program.addCommand(createSelfUpdateCommand());
|
|
2622
|
+
program.exitOverride();
|
|
2623
|
+
return program;
|
|
2624
|
+
}
|
|
2625
|
+
async function run(argv) {
|
|
2626
|
+
const args = argv ?? process.argv;
|
|
2627
|
+
const hasSubcommand = args.length > 2 && !args[2]?.startsWith("-");
|
|
2628
|
+
if (!hasSubcommand) {
|
|
2629
|
+
const versionRequested = args.includes("-V") || args.includes("--version");
|
|
2630
|
+
const helpRequested = args.includes("-h") || args.includes("--help");
|
|
2631
|
+
if (!versionRequested && !helpRequested) {
|
|
2632
|
+
const sock = defaultSocketPath();
|
|
2633
|
+
const client = new RpcClient(sock);
|
|
2634
|
+
const { startRepl } = await import("./repl-JIMJ2V2Y.js");
|
|
2635
|
+
await startRepl(client, sock);
|
|
2636
|
+
return;
|
|
2637
|
+
}
|
|
2638
|
+
}
|
|
2639
|
+
const program = createProgram();
|
|
2640
|
+
try {
|
|
2641
|
+
await program.parseAsync(args);
|
|
2642
|
+
} catch (err) {
|
|
2643
|
+
if (err instanceof Error && "code" in err) {
|
|
2644
|
+
const code = err.code;
|
|
2645
|
+
if (code === "commander.helpDisplayed" || code === "commander.version") {
|
|
2646
|
+
return;
|
|
2647
|
+
}
|
|
2648
|
+
}
|
|
2649
|
+
presentError(err);
|
|
2650
|
+
process.exitCode = 1;
|
|
2651
|
+
}
|
|
2652
|
+
}
|
|
2653
|
+
|
|
2654
|
+
export {
|
|
2655
|
+
RpcClient,
|
|
2656
|
+
RpcCallError,
|
|
2657
|
+
ConnectionError,
|
|
2658
|
+
defaultPrinter,
|
|
2659
|
+
defaultSocketPath,
|
|
2660
|
+
createProgram,
|
|
2661
|
+
run
|
|
2662
|
+
};
|
|
2663
|
+
//# sourceMappingURL=chunk-2FKBVXMH.js.map
|