crewx-agent-cli 0.2.6 → 0.2.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +46 -6
- package/dist/acp-worker.d.ts +3 -0
- package/dist/acp-worker.d.ts.map +1 -0
- package/dist/acp-worker.js +176 -0
- package/dist/acp-worker.js.map +1 -0
- package/dist/adapters.d.ts +12 -10
- package/dist/adapters.d.ts.map +1 -1
- package/dist/adapters.js +570 -310
- package/dist/adapters.js.map +1 -1
- package/dist/assignment-scheduler.d.ts +29 -0
- package/dist/assignment-scheduler.d.ts.map +1 -0
- package/dist/assignment-scheduler.js +137 -0
- package/dist/assignment-scheduler.js.map +1 -0
- package/dist/daemon.d.ts +1 -0
- package/dist/daemon.d.ts.map +1 -1
- package/dist/daemon.js +159 -109
- package/dist/daemon.js.map +1 -1
- package/dist/index.d.ts +7 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +291 -221
- package/dist/index.js.map +1 -1
- package/dist/runtime-proxy.d.ts.map +1 -1
- package/dist/runtime-proxy.js +3 -0
- package/dist/runtime-proxy.js.map +1 -1
- package/package.json +7 -4
package/dist/index.js
CHANGED
|
@@ -1,25 +1,30 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { realpathSync } from
|
|
3
|
-
import { stdin, stdout } from
|
|
4
|
-
import { resolve } from
|
|
5
|
-
import * as tls from
|
|
6
|
-
import { fileURLToPath } from
|
|
7
|
-
import { Command, InvalidArgumentError, Option } from
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
2
|
+
import { realpathSync } from "node:fs";
|
|
3
|
+
import { stdin, stdout } from "node:process";
|
|
4
|
+
import { resolve } from "node:path";
|
|
5
|
+
import * as tls from "node:tls";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
7
|
+
import { Command, InvalidArgumentError, Option } from "commander";
|
|
8
|
+
import { AGENT_ADAPTERS } from "crewx-agent-protocol";
|
|
9
|
+
import { parseAdapter, probeAdapter, runAdapter } from "./adapters.js";
|
|
10
|
+
import { CrewXApi } from "./api.js";
|
|
11
|
+
import { attachBridgeWatchdog } from "./bridge-watchdog.js";
|
|
12
|
+
import { resolveConfig, saveConfig, readStoredConfig, } from "./config.js";
|
|
13
|
+
import { CLI_NAME, CLI_VERSION, DEFAULT_ADAPTER, DEFAULT_POLL_INTERVAL_MS, } from "./constants.js";
|
|
14
|
+
import { runDaemon } from "./daemon.js";
|
|
15
|
+
import { CliError, errorMessage, redactSecrets } from "./errors.js";
|
|
16
|
+
import { decodeJoinCode } from "./join.js";
|
|
17
|
+
import { ui } from "./ui.js";
|
|
17
18
|
function trustSystemCertificateAuthorities() {
|
|
18
|
-
if (typeof tls.getCACertificates !==
|
|
19
|
+
if (typeof tls.getCACertificates !== "function" ||
|
|
20
|
+
typeof tls.setDefaultCACertificates !== "function")
|
|
19
21
|
return;
|
|
20
22
|
try {
|
|
21
23
|
tls.setDefaultCACertificates([
|
|
22
|
-
...new Set([
|
|
24
|
+
...new Set([
|
|
25
|
+
...tls.getCACertificates("default"),
|
|
26
|
+
...tls.getCACertificates("system"),
|
|
27
|
+
]),
|
|
23
28
|
]);
|
|
24
29
|
}
|
|
25
30
|
catch {
|
|
@@ -30,21 +35,26 @@ trustSystemCertificateAuthorities();
|
|
|
30
35
|
function positiveInteger(value) {
|
|
31
36
|
const parsed = Number(value);
|
|
32
37
|
if (!Number.isSafeInteger(parsed) || parsed <= 0) {
|
|
33
|
-
throw new InvalidArgumentError(
|
|
38
|
+
throw new InvalidArgumentError("must be a positive integer");
|
|
34
39
|
}
|
|
35
40
|
return parsed;
|
|
36
41
|
}
|
|
42
|
+
function runtimeTransport(value) {
|
|
43
|
+
if (value === "native" || value === "acp")
|
|
44
|
+
return value;
|
|
45
|
+
throw new InvalidArgumentError("must be native or acp");
|
|
46
|
+
}
|
|
37
47
|
function memoryImportance(value) {
|
|
38
48
|
const parsed = positiveInteger(value);
|
|
39
49
|
if (parsed > 5)
|
|
40
|
-
throw new InvalidArgumentError(
|
|
50
|
+
throw new InvalidArgumentError("must be between 1 and 5");
|
|
41
51
|
return parsed;
|
|
42
52
|
}
|
|
43
53
|
async function readStdin() {
|
|
44
54
|
if (stdin.isTTY)
|
|
45
|
-
return
|
|
46
|
-
stdin.setEncoding(
|
|
47
|
-
let value =
|
|
55
|
+
return "";
|
|
56
|
+
stdin.setEncoding("utf8");
|
|
57
|
+
let value = "";
|
|
48
58
|
for await (const chunk of stdin)
|
|
49
59
|
value += chunk;
|
|
50
60
|
return value;
|
|
@@ -53,7 +63,12 @@ function printJson(value) {
|
|
|
53
63
|
console.log(JSON.stringify(value, null, 2));
|
|
54
64
|
}
|
|
55
65
|
function commaSeparated(value) {
|
|
56
|
-
return [
|
|
66
|
+
return [
|
|
67
|
+
...new Set(value
|
|
68
|
+
.split(",")
|
|
69
|
+
.map((item) => item.trim())
|
|
70
|
+
.filter(Boolean)),
|
|
71
|
+
];
|
|
57
72
|
}
|
|
58
73
|
export function workCliEnvironment(url, token, runnerScope) {
|
|
59
74
|
return {
|
|
@@ -67,12 +82,12 @@ async function listenForWork(options) {
|
|
|
67
82
|
if (!options.codingCommand) {
|
|
68
83
|
const runtime = await probeAdapter(options.adapter);
|
|
69
84
|
if (!runtime.available) {
|
|
70
|
-
throw new CliError(`Cannot start the ${options.adapter} runner: ${runtime.error ??
|
|
85
|
+
throw new CliError(`Cannot start the ${options.adapter} runner: ${runtime.error ?? "the local runtime is unavailable"}. Run \`crewx doctor\` for installation details.`);
|
|
71
86
|
}
|
|
72
|
-
console.log(ui.muted(` Runtime ${options.adapter}${runtime.version ? ` (${runtime.version})` :
|
|
87
|
+
console.log(ui.muted(` Runtime ${options.adapter}${runtime.version ? ` (${runtime.version})` : ""}`));
|
|
73
88
|
}
|
|
74
|
-
if (options.adapter ===
|
|
75
|
-
console.log(ui.warning(
|
|
89
|
+
if (options.adapter === "openclaw") {
|
|
90
|
+
console.log(ui.warning("OpenClaw uses its own configured workspace, sandbox, tools, and approvals; CrewX does not rewrite them."));
|
|
76
91
|
}
|
|
77
92
|
const controller = new AbortController();
|
|
78
93
|
const detachBridgeWatchdog = attachBridgeWatchdog(controller);
|
|
@@ -80,15 +95,15 @@ async function listenForWork(options) {
|
|
|
80
95
|
const stop = () => {
|
|
81
96
|
interrupts += 1;
|
|
82
97
|
if (interrupts === 1) {
|
|
83
|
-
console.log(
|
|
98
|
+
console.log("\nStopping CrewX agent…");
|
|
84
99
|
controller.abort();
|
|
85
100
|
}
|
|
86
101
|
else {
|
|
87
102
|
process.exitCode = 130;
|
|
88
103
|
}
|
|
89
104
|
};
|
|
90
|
-
process.on(
|
|
91
|
-
process.on(
|
|
105
|
+
process.on("SIGINT", stop);
|
|
106
|
+
process.on("SIGTERM", stop);
|
|
92
107
|
try {
|
|
93
108
|
await runDaemon({
|
|
94
109
|
api: options.api,
|
|
@@ -97,7 +112,9 @@ async function listenForWork(options) {
|
|
|
97
112
|
cwd: resolve(options.cwd),
|
|
98
113
|
pollIntervalMs: options.pollIntervalMs,
|
|
99
114
|
once: options.once,
|
|
100
|
-
...(options.codingCommand
|
|
115
|
+
...(options.codingCommand
|
|
116
|
+
? { codingCommand: options.codingCommand }
|
|
117
|
+
: {}),
|
|
101
118
|
...(options.environment ? { environment: options.environment } : {}),
|
|
102
119
|
signal: controller.signal,
|
|
103
120
|
log: (message) => console.log(ui.muted(`[crewx] ${message}`)),
|
|
@@ -105,22 +122,22 @@ async function listenForWork(options) {
|
|
|
105
122
|
}
|
|
106
123
|
finally {
|
|
107
124
|
detachBridgeWatchdog?.();
|
|
108
|
-
process.removeListener(
|
|
109
|
-
process.removeListener(
|
|
125
|
+
process.removeListener("SIGINT", stop);
|
|
126
|
+
process.removeListener("SIGTERM", stop);
|
|
110
127
|
}
|
|
111
128
|
}
|
|
112
129
|
export function createProgram() {
|
|
113
130
|
const program = new Command();
|
|
114
131
|
program
|
|
115
132
|
.name(CLI_NAME)
|
|
116
|
-
.description(
|
|
133
|
+
.description("Bring your local coding agents into a CrewX workspace.")
|
|
117
134
|
.version(CLI_VERSION)
|
|
118
|
-
.option(
|
|
119
|
-
.option(
|
|
120
|
-
.option(
|
|
121
|
-
.option(
|
|
122
|
-
.option(
|
|
123
|
-
.option(
|
|
135
|
+
.option("--join <code>", "join an agent profile using the command copied from CrewX")
|
|
136
|
+
.option("-a, --adapter <adapter>", "override the runtime encoded in a join command")
|
|
137
|
+
.option("--coding-cmd <command>", "advanced: run a local argv command and append the prompt (use {prompt} to position it)")
|
|
138
|
+
.option("--once", "poll once, process available work, then exit")
|
|
139
|
+
.option("--poll-interval <milliseconds>", "delay between polls", positiveInteger, DEFAULT_POLL_INTERVAL_MS)
|
|
140
|
+
.option("-C, --cwd <directory>", "local working directory", process.cwd())
|
|
124
141
|
.showHelpAfterError()
|
|
125
142
|
.configureHelp({ sortSubcommands: true, sortOptions: true })
|
|
126
143
|
.action(async (options) => {
|
|
@@ -135,9 +152,9 @@ export function createProgram() {
|
|
|
135
152
|
console.log(ui.success(`✓ Joined CrewX with ${adapter}`));
|
|
136
153
|
console.log(` Server ${api.baseUrl}`);
|
|
137
154
|
console.log(` Folder ${resolve(options.cwd)}`);
|
|
138
|
-
console.log(ui.muted(
|
|
155
|
+
console.log(ui.muted(" Runs in this terminal. Press Ctrl+C to disconnect."));
|
|
139
156
|
if (options.codingCmd) {
|
|
140
|
-
console.log(ui.warning(
|
|
157
|
+
console.log(ui.warning(" Custom command enabled; CrewX will execute it directly without a shell."));
|
|
141
158
|
}
|
|
142
159
|
await listenForWork({
|
|
143
160
|
api,
|
|
@@ -150,50 +167,55 @@ export function createProgram() {
|
|
|
150
167
|
});
|
|
151
168
|
});
|
|
152
169
|
program
|
|
153
|
-
.command(
|
|
154
|
-
.description(
|
|
155
|
-
.requiredOption(
|
|
156
|
-
.requiredOption(
|
|
157
|
-
.option(
|
|
170
|
+
.command("connect")
|
|
171
|
+
.description("Authenticate this machine with a CrewX workspace")
|
|
172
|
+
.requiredOption("--url <url>", "CrewX server URL")
|
|
173
|
+
.requiredOption("--token <token>", "agent connection token")
|
|
174
|
+
.option("--name <name>", "friendly name for this machine")
|
|
158
175
|
.action(async (options) => {
|
|
159
176
|
const api = new CrewXApi({ url: options.url, token: options.token });
|
|
160
177
|
await api.status(AbortSignal.timeout(10_000));
|
|
161
178
|
const path = await saveConfig(options);
|
|
162
|
-
console.log(ui.success(
|
|
179
|
+
console.log(ui.success("✓ Connected to CrewX"));
|
|
163
180
|
console.log(` Server ${api.baseUrl}`);
|
|
164
181
|
if (options.name)
|
|
165
182
|
console.log(` Name ${options.name}`);
|
|
166
|
-
console.log(` Config ${path} ${ui.muted(
|
|
167
|
-
console.log(`\nStart listening with ${ui.heading(
|
|
183
|
+
console.log(` Config ${path} ${ui.muted("(mode 0600)")}`);
|
|
184
|
+
console.log(`\nStart listening with ${ui.heading("crewx daemon")}.`);
|
|
168
185
|
});
|
|
169
186
|
program
|
|
170
|
-
.command(
|
|
171
|
-
.description(
|
|
172
|
-
.option(
|
|
187
|
+
.command("status")
|
|
188
|
+
.description("Show the current CrewX connection")
|
|
189
|
+
.option("--json", "print machine-readable JSON")
|
|
173
190
|
.action(async (options) => {
|
|
174
191
|
const config = await resolveConfig();
|
|
175
192
|
const api = new CrewXApi(config);
|
|
176
193
|
const response = await api.status(AbortSignal.timeout(10_000));
|
|
177
194
|
if (options.json) {
|
|
178
|
-
printJson({
|
|
195
|
+
printJson({
|
|
196
|
+
connected: true,
|
|
197
|
+
url: api.baseUrl,
|
|
198
|
+
name: config.name,
|
|
199
|
+
response,
|
|
200
|
+
});
|
|
179
201
|
return;
|
|
180
202
|
}
|
|
181
|
-
console.log(ui.success(
|
|
203
|
+
console.log(ui.success("● CrewX is reachable"));
|
|
182
204
|
console.log(` Server ${api.baseUrl}`);
|
|
183
205
|
if (config.name)
|
|
184
206
|
console.log(` Name ${config.name}`);
|
|
185
|
-
console.log(` Auth ${config.tokenSource ===
|
|
186
|
-
?
|
|
187
|
-
: config.tokenSource ===
|
|
188
|
-
?
|
|
207
|
+
console.log(` Auth ${config.tokenSource === "bridge_pipe"
|
|
208
|
+
? "one-shot Bridge credential"
|
|
209
|
+
: config.tokenSource === "environment"
|
|
210
|
+
? "CREWX_TOKEN"
|
|
189
211
|
: config.path}`);
|
|
190
212
|
});
|
|
191
213
|
program
|
|
192
|
-
.command(
|
|
193
|
-
.description(
|
|
194
|
-
.option(
|
|
214
|
+
.command("doctor")
|
|
215
|
+
.description("Check local agent binaries and CrewX connectivity")
|
|
216
|
+
.option("--json", "print machine-readable JSON")
|
|
195
217
|
.action(async (options) => {
|
|
196
|
-
const adapters = await Promise.all(
|
|
218
|
+
const adapters = await Promise.all(AGENT_ADAPTERS.map(async (adapter) => [adapter, await probeAdapter(adapter)]));
|
|
197
219
|
let connection = {
|
|
198
220
|
configured: false,
|
|
199
221
|
reachable: false,
|
|
@@ -213,27 +235,33 @@ export function createProgram() {
|
|
|
213
235
|
catch {
|
|
214
236
|
// An absent connection is a normal doctor result.
|
|
215
237
|
}
|
|
216
|
-
const result = {
|
|
238
|
+
const result = {
|
|
239
|
+
node: process.version,
|
|
240
|
+
adapters: Object.fromEntries(adapters),
|
|
241
|
+
connection,
|
|
242
|
+
};
|
|
217
243
|
if (options.json) {
|
|
218
244
|
printJson(result);
|
|
219
245
|
return;
|
|
220
246
|
}
|
|
221
|
-
console.log(ui.heading(
|
|
222
|
-
console.log(` Node ${process.version} ${Number(process.versions.node.split(
|
|
247
|
+
console.log(ui.heading("CrewX doctor"));
|
|
248
|
+
console.log(` Node ${process.version} ${Number(process.versions.node.split(".")[0]) >= 22 ? ui.success("✓") : ui.warning("requires 22+")}`);
|
|
223
249
|
for (const [adapter, probe] of adapters) {
|
|
224
|
-
console.log(` ${adapter.padEnd(8)} ${probe.available ? ui.success(
|
|
250
|
+
console.log(` ${adapter.padEnd(8)} ${probe.available ? ui.success("✓") : ui.warning("–")} ${probe.version ?? probe.error ?? "not found"}`);
|
|
225
251
|
}
|
|
226
|
-
console.log(` API ${connection.reachable ? ui.success(
|
|
252
|
+
console.log(` API ${connection.reachable ? ui.success("✓ reachable") : connection.configured ? ui.warning(`unreachable — ${connection.error}`) : ui.warning("not configured")}`);
|
|
227
253
|
});
|
|
228
|
-
const task = program
|
|
254
|
+
const task = program
|
|
255
|
+
.command("task")
|
|
256
|
+
.description("List, create, and update CrewX tasks");
|
|
229
257
|
task
|
|
230
|
-
.command(
|
|
231
|
-
.description(
|
|
232
|
-
.option(
|
|
233
|
-
.option(
|
|
234
|
-
.option(
|
|
235
|
-
.option(
|
|
236
|
-
.option(
|
|
258
|
+
.command("list")
|
|
259
|
+
.description("List tasks visible to this connected agent")
|
|
260
|
+
.option("--status <status>", "filter by task status")
|
|
261
|
+
.option("--assigned-to-me", "show only tasks assigned to this agent")
|
|
262
|
+
.option("--include-epics", "include epics in the result")
|
|
263
|
+
.option("--search <text>", "search titles and descriptions")
|
|
264
|
+
.option("--json", "print machine-readable JSON")
|
|
237
265
|
.action(async (options) => {
|
|
238
266
|
const api = new CrewXApi(await resolveConfig());
|
|
239
267
|
const tasks = await api.listTasks({
|
|
@@ -247,36 +275,42 @@ export function createProgram() {
|
|
|
247
275
|
return;
|
|
248
276
|
}
|
|
249
277
|
if (tasks.length === 0) {
|
|
250
|
-
console.log(ui.muted(
|
|
278
|
+
console.log(ui.muted("No matching tasks."));
|
|
251
279
|
return;
|
|
252
280
|
}
|
|
253
281
|
for (const item of tasks) {
|
|
254
|
-
console.log(`#${item.id} [${item.status}] ${item.title}${item.assigned_to_me ?
|
|
282
|
+
console.log(`#${item.id} [${item.status}] ${item.title}${item.assigned_to_me ? " (assigned to me)" : ""}`);
|
|
255
283
|
}
|
|
256
284
|
});
|
|
257
285
|
task
|
|
258
|
-
.command(
|
|
259
|
-
.description(
|
|
260
|
-
.requiredOption(
|
|
261
|
-
.option(
|
|
262
|
-
.option(
|
|
263
|
-
.option(
|
|
264
|
-
.option(
|
|
265
|
-
.option(
|
|
266
|
-
.option(
|
|
267
|
-
.option(
|
|
268
|
-
.option(
|
|
269
|
-
.option(
|
|
286
|
+
.command("create")
|
|
287
|
+
.description("Create a workspace task")
|
|
288
|
+
.requiredOption("--title <title>", "task title")
|
|
289
|
+
.option("--description <markdown>", "task description")
|
|
290
|
+
.option("--status <status>", "backlog, todo, in_progress, review, done, or cancelled")
|
|
291
|
+
.option("--priority <priority>", "low, medium, high, or urgent")
|
|
292
|
+
.option("--labels <labels>", "comma-separated labels")
|
|
293
|
+
.option("--due <date>", "due date (YYYY-MM-DD)")
|
|
294
|
+
.option("--project <channel-id>", "project channel ID", positiveInteger)
|
|
295
|
+
.option("--epic <task-id>", "epic task ID", positiveInteger)
|
|
296
|
+
.option("--assign-to-me", "assign the new task to this connected agent")
|
|
297
|
+
.option("--json", "print machine-readable JSON")
|
|
270
298
|
.action(async (options) => {
|
|
271
299
|
const api = new CrewXApi(await resolveConfig());
|
|
272
300
|
const created = await api.createTask({
|
|
273
301
|
title: options.title,
|
|
274
|
-
...(options.description !== undefined
|
|
302
|
+
...(options.description !== undefined
|
|
303
|
+
? { description: options.description }
|
|
304
|
+
: {}),
|
|
275
305
|
...(options.status ? { status: options.status } : {}),
|
|
276
306
|
...(options.priority ? { priority: options.priority } : {}),
|
|
277
|
-
...(options.labels !== undefined
|
|
307
|
+
...(options.labels !== undefined
|
|
308
|
+
? { labels: commaSeparated(options.labels) }
|
|
309
|
+
: {}),
|
|
278
310
|
...(options.due ? { due_at: options.due } : {}),
|
|
279
|
-
...(options.project !== undefined
|
|
311
|
+
...(options.project !== undefined
|
|
312
|
+
? { project_channel_id: options.project }
|
|
313
|
+
: {}),
|
|
280
314
|
...(options.epic !== undefined ? { epic_id: options.epic } : {}),
|
|
281
315
|
...(options.assignToMe ? { assign_to_me: true } : {}),
|
|
282
316
|
});
|
|
@@ -287,31 +321,37 @@ export function createProgram() {
|
|
|
287
321
|
console.log(ui.success(`✓ Created task #${created.id}: ${created.title}`));
|
|
288
322
|
});
|
|
289
323
|
task
|
|
290
|
-
.command(
|
|
291
|
-
.description(
|
|
292
|
-
.argument(
|
|
293
|
-
.option(
|
|
294
|
-
.option(
|
|
295
|
-
.option(
|
|
296
|
-
.option(
|
|
297
|
-
.option(
|
|
298
|
-
.option(
|
|
299
|
-
.option(
|
|
300
|
-
.option(
|
|
301
|
-
.option(
|
|
324
|
+
.command("update")
|
|
325
|
+
.description("Update a task assigned to this connected agent")
|
|
326
|
+
.argument("<id>", "task ID", positiveInteger)
|
|
327
|
+
.option("--title <title>", "replace the task title")
|
|
328
|
+
.option("--description <markdown>", "replace the task description")
|
|
329
|
+
.option("--status <status>", "set task status")
|
|
330
|
+
.option("--priority <priority>", "set task priority")
|
|
331
|
+
.option("--labels <labels>", "replace labels with a comma-separated list")
|
|
332
|
+
.option("--due <date>", "set due date (YYYY-MM-DD)")
|
|
333
|
+
.option("--result <markdown>", "record the work result")
|
|
334
|
+
.option("--pull-request <url>", "record a pull request URL")
|
|
335
|
+
.option("--json", "print machine-readable JSON")
|
|
302
336
|
.action(async (id, options) => {
|
|
303
337
|
const attributes = {
|
|
304
338
|
...(options.title !== undefined ? { title: options.title } : {}),
|
|
305
|
-
...(options.description !== undefined
|
|
339
|
+
...(options.description !== undefined
|
|
340
|
+
? { description: options.description }
|
|
341
|
+
: {}),
|
|
306
342
|
...(options.status ? { status: options.status } : {}),
|
|
307
343
|
...(options.priority ? { priority: options.priority } : {}),
|
|
308
|
-
...(options.labels !== undefined
|
|
344
|
+
...(options.labels !== undefined
|
|
345
|
+
? { labels: commaSeparated(options.labels) }
|
|
346
|
+
: {}),
|
|
309
347
|
...(options.due ? { due_at: options.due } : {}),
|
|
310
348
|
...(options.result !== undefined ? { result: options.result } : {}),
|
|
311
|
-
...(options.pullRequest !== undefined
|
|
349
|
+
...(options.pullRequest !== undefined
|
|
350
|
+
? { pull_request_url: options.pullRequest }
|
|
351
|
+
: {}),
|
|
312
352
|
};
|
|
313
353
|
if (Object.keys(attributes).length === 0)
|
|
314
|
-
throw new CliError(
|
|
354
|
+
throw new CliError("Provide at least one task field to update.");
|
|
315
355
|
const api = new CrewXApi(await resolveConfig());
|
|
316
356
|
const updated = await api.updateTask(id, attributes);
|
|
317
357
|
if (options.json) {
|
|
@@ -320,12 +360,14 @@ export function createProgram() {
|
|
|
320
360
|
}
|
|
321
361
|
console.log(ui.success(`✓ Updated task #${updated.id}: ${updated.title} [${updated.status}]`));
|
|
322
362
|
});
|
|
323
|
-
const doc = program
|
|
363
|
+
const doc = program
|
|
364
|
+
.command("doc")
|
|
365
|
+
.description("List, create, and update CrewX documents");
|
|
324
366
|
doc
|
|
325
|
-
.command(
|
|
326
|
-
.description(
|
|
327
|
-
.option(
|
|
328
|
-
.option(
|
|
367
|
+
.command("list")
|
|
368
|
+
.description("List documents readable by connected agents")
|
|
369
|
+
.option("--search <text>", "search document titles and contents")
|
|
370
|
+
.option("--json", "print machine-readable JSON")
|
|
329
371
|
.action(async (options) => {
|
|
330
372
|
const api = new CrewXApi(await resolveConfig());
|
|
331
373
|
const documents = await api.listDocuments(options.search ? { search: options.search } : {});
|
|
@@ -334,27 +376,31 @@ export function createProgram() {
|
|
|
334
376
|
return;
|
|
335
377
|
}
|
|
336
378
|
if (documents.length === 0) {
|
|
337
|
-
console.log(ui.muted(
|
|
379
|
+
console.log(ui.muted("No matching documents."));
|
|
338
380
|
return;
|
|
339
381
|
}
|
|
340
382
|
for (const document of documents) {
|
|
341
|
-
console.log(`#${document.id} v${document.current_version} ${document.title}${document.protected ?
|
|
383
|
+
console.log(`#${document.id} v${document.current_version} ${document.title}${document.protected ? " (protected)" : ""}`);
|
|
342
384
|
}
|
|
343
385
|
});
|
|
344
386
|
doc
|
|
345
|
-
.command(
|
|
346
|
-
.description(
|
|
347
|
-
.requiredOption(
|
|
348
|
-
.option(
|
|
349
|
-
.option(
|
|
350
|
-
.option(
|
|
351
|
-
.option(
|
|
387
|
+
.command("create")
|
|
388
|
+
.description("Create an agent-readable workspace document")
|
|
389
|
+
.requiredOption("--title <title>", "document title")
|
|
390
|
+
.option("--content <markdown>", "document content")
|
|
391
|
+
.option("--folder <folder-id>", "folder ID", positiveInteger)
|
|
392
|
+
.option("--summary <summary>", "version summary")
|
|
393
|
+
.option("--json", "print machine-readable JSON")
|
|
352
394
|
.action(async (options) => {
|
|
353
395
|
const api = new CrewXApi(await resolveConfig());
|
|
354
396
|
const created = await api.createDocument({
|
|
355
397
|
title: options.title,
|
|
356
|
-
...(options.content !== undefined
|
|
357
|
-
|
|
398
|
+
...(options.content !== undefined
|
|
399
|
+
? { content: options.content }
|
|
400
|
+
: {}),
|
|
401
|
+
...(options.folder !== undefined
|
|
402
|
+
? { folder_id: options.folder }
|
|
403
|
+
: {}),
|
|
358
404
|
...(options.summary ? { summary: options.summary } : {}),
|
|
359
405
|
});
|
|
360
406
|
if (options.json) {
|
|
@@ -364,25 +410,29 @@ export function createProgram() {
|
|
|
364
410
|
console.log(ui.success(`✓ Created document #${created.id} v${created.current_version}: ${created.title}`));
|
|
365
411
|
});
|
|
366
412
|
doc
|
|
367
|
-
.command(
|
|
368
|
-
.description(
|
|
369
|
-
.argument(
|
|
370
|
-
.requiredOption(
|
|
371
|
-
.option(
|
|
372
|
-
.option(
|
|
373
|
-
.option(
|
|
374
|
-
.option(
|
|
375
|
-
.option(
|
|
413
|
+
.command("update")
|
|
414
|
+
.description("Update an agent-readable, unprotected document")
|
|
415
|
+
.argument("<id>", "document ID", positiveInteger)
|
|
416
|
+
.requiredOption("--expected-version <version>", "current version from doc list", positiveInteger)
|
|
417
|
+
.option("--title <title>", "replace the document title")
|
|
418
|
+
.option("--content <markdown>", "replace the document content")
|
|
419
|
+
.option("--folder <folder-id>", "move to a folder", positiveInteger)
|
|
420
|
+
.option("--summary <summary>", "version summary")
|
|
421
|
+
.option("--json", "print machine-readable JSON")
|
|
376
422
|
.action(async (id, options) => {
|
|
377
423
|
const attributes = {
|
|
378
424
|
expected_version: options.expectedVersion,
|
|
379
425
|
...(options.title !== undefined ? { title: options.title } : {}),
|
|
380
|
-
...(options.content !== undefined
|
|
381
|
-
|
|
426
|
+
...(options.content !== undefined
|
|
427
|
+
? { content: options.content }
|
|
428
|
+
: {}),
|
|
429
|
+
...(options.folder !== undefined
|
|
430
|
+
? { folder_id: options.folder }
|
|
431
|
+
: {}),
|
|
382
432
|
...(options.summary ? { summary: options.summary } : {}),
|
|
383
433
|
};
|
|
384
434
|
if (Object.keys(attributes).length === 1)
|
|
385
|
-
throw new CliError(
|
|
435
|
+
throw new CliError("Provide at least one document field to update.");
|
|
386
436
|
const api = new CrewXApi(await resolveConfig());
|
|
387
437
|
const updated = await api.updateDocument(id, attributes);
|
|
388
438
|
if (options.json) {
|
|
@@ -391,49 +441,56 @@ export function createProgram() {
|
|
|
391
441
|
}
|
|
392
442
|
console.log(ui.success(`✓ Updated document #${updated.id} to v${updated.current_version}: ${updated.title}`));
|
|
393
443
|
});
|
|
394
|
-
const memory = program
|
|
444
|
+
const memory = program
|
|
445
|
+
.command("memory")
|
|
446
|
+
.description("List, create, and update durable CrewX memories");
|
|
395
447
|
memory
|
|
396
|
-
.command(
|
|
397
|
-
.description(
|
|
398
|
-
.option(
|
|
399
|
-
.option(
|
|
400
|
-
.option(
|
|
401
|
-
.option(
|
|
402
|
-
.option(
|
|
448
|
+
.command("list")
|
|
449
|
+
.description("List memories visible to this connected agent")
|
|
450
|
+
.option("--search <text>", "search memory titles, contents, and sources")
|
|
451
|
+
.option("--category <category>", "filter by category")
|
|
452
|
+
.option("--scope <scope>", "workspace or channel")
|
|
453
|
+
.option("--channel <channel-id>", "filter by channel ID", positiveInteger)
|
|
454
|
+
.option("--json", "print machine-readable JSON")
|
|
403
455
|
.action(async (options) => {
|
|
404
|
-
if (options.scope &&
|
|
405
|
-
|
|
456
|
+
if (options.scope &&
|
|
457
|
+
!["workspace", "channel"].includes(options.scope)) {
|
|
458
|
+
throw new CliError("Memory scope must be workspace or channel.");
|
|
406
459
|
}
|
|
407
460
|
const api = new CrewXApi(await resolveConfig());
|
|
408
461
|
const memories = await api.listMemories({
|
|
409
462
|
...(options.search ? { search: options.search } : {}),
|
|
410
463
|
...(options.category ? { category: options.category } : {}),
|
|
411
464
|
...(options.scope ? { scope: options.scope } : {}),
|
|
412
|
-
...(options.channel !== undefined
|
|
465
|
+
...(options.channel !== undefined
|
|
466
|
+
? { channelId: options.channel }
|
|
467
|
+
: {}),
|
|
413
468
|
});
|
|
414
469
|
if (options.json) {
|
|
415
470
|
printJson({ memories });
|
|
416
471
|
return;
|
|
417
472
|
}
|
|
418
473
|
if (memories.length === 0) {
|
|
419
|
-
console.log(ui.muted(
|
|
474
|
+
console.log(ui.muted("No matching memories."));
|
|
420
475
|
return;
|
|
421
476
|
}
|
|
422
477
|
for (const item of memories) {
|
|
423
|
-
const scope = item.scope ===
|
|
478
|
+
const scope = item.scope === "channel"
|
|
479
|
+
? `channel ${item.channel?.name ?? ""}`.trim()
|
|
480
|
+
: "workspace";
|
|
424
481
|
console.log(`#${item.id} [${item.category}] ${item.title} (${scope})`);
|
|
425
482
|
}
|
|
426
483
|
});
|
|
427
484
|
memory
|
|
428
|
-
.command(
|
|
429
|
-
.description(
|
|
430
|
-
.requiredOption(
|
|
431
|
-
.option(
|
|
432
|
-
.option(
|
|
433
|
-
.option(
|
|
434
|
-
.option(
|
|
435
|
-
.option(
|
|
436
|
-
.option(
|
|
485
|
+
.command("create")
|
|
486
|
+
.description("Record a durable learning for this workspace")
|
|
487
|
+
.requiredOption("--content <text>", "memory content")
|
|
488
|
+
.option("--title <title>", "memory title; generated from content when omitted")
|
|
489
|
+
.option("--category <category>", "memory category", "general")
|
|
490
|
+
.option("--importance <level>", "importance from 1 to 5", memoryImportance, 3)
|
|
491
|
+
.option("--source <source>", "memory source", "agent")
|
|
492
|
+
.option("--channel <channel-id>", "scope the memory to a visible channel", positiveInteger)
|
|
493
|
+
.option("--json", "print machine-readable JSON")
|
|
437
494
|
.action(async (options) => {
|
|
438
495
|
const api = new CrewXApi(await resolveConfig());
|
|
439
496
|
const created = await api.createMemory({
|
|
@@ -442,7 +499,9 @@ export function createProgram() {
|
|
|
442
499
|
category: options.category,
|
|
443
500
|
importance: options.importance,
|
|
444
501
|
source: options.source,
|
|
445
|
-
...(options.channel !== undefined
|
|
502
|
+
...(options.channel !== undefined
|
|
503
|
+
? { channel_id: options.channel }
|
|
504
|
+
: {}),
|
|
446
505
|
});
|
|
447
506
|
if (options.json) {
|
|
448
507
|
printJson({ memory: created });
|
|
@@ -451,32 +510,40 @@ export function createProgram() {
|
|
|
451
510
|
console.log(ui.success(`✓ Saved memory #${created.id}: ${created.title}`));
|
|
452
511
|
});
|
|
453
512
|
memory
|
|
454
|
-
.command(
|
|
455
|
-
.description(
|
|
456
|
-
.argument(
|
|
457
|
-
.option(
|
|
458
|
-
.option(
|
|
459
|
-
.option(
|
|
460
|
-
.option(
|
|
461
|
-
.option(
|
|
462
|
-
.option(
|
|
463
|
-
.option(
|
|
464
|
-
.option(
|
|
513
|
+
.command("update")
|
|
514
|
+
.description("Update a memory recorded by this connected agent")
|
|
515
|
+
.argument("<id>", "memory ID", positiveInteger)
|
|
516
|
+
.option("--title <title>", "replace the memory title")
|
|
517
|
+
.option("--content <text>", "replace the memory content")
|
|
518
|
+
.option("--category <category>", "replace the category")
|
|
519
|
+
.option("--importance <level>", "importance from 1 to 5", memoryImportance)
|
|
520
|
+
.option("--source <source>", "replace the source")
|
|
521
|
+
.option("--channel <channel-id>", "move the memory to a visible channel", positiveInteger)
|
|
522
|
+
.option("--workspace-scope", "move the memory to workspace scope")
|
|
523
|
+
.option("--json", "print machine-readable JSON")
|
|
465
524
|
.action(async (id, options) => {
|
|
466
525
|
if (options.channel !== undefined && options.workspaceScope) {
|
|
467
|
-
throw new CliError(
|
|
526
|
+
throw new CliError("Choose either --channel or --workspace-scope, not both.");
|
|
468
527
|
}
|
|
469
528
|
const attributes = {
|
|
470
529
|
...(options.title !== undefined ? { title: options.title } : {}),
|
|
471
|
-
...(options.content !== undefined
|
|
472
|
-
|
|
473
|
-
|
|
530
|
+
...(options.content !== undefined
|
|
531
|
+
? { content: options.content }
|
|
532
|
+
: {}),
|
|
533
|
+
...(options.category !== undefined
|
|
534
|
+
? { category: options.category }
|
|
535
|
+
: {}),
|
|
536
|
+
...(options.importance !== undefined
|
|
537
|
+
? { importance: options.importance }
|
|
538
|
+
: {}),
|
|
474
539
|
...(options.source !== undefined ? { source: options.source } : {}),
|
|
475
|
-
...(options.channel !== undefined
|
|
540
|
+
...(options.channel !== undefined
|
|
541
|
+
? { channel_id: options.channel }
|
|
542
|
+
: {}),
|
|
476
543
|
...(options.workspaceScope ? { channel_id: null } : {}),
|
|
477
544
|
};
|
|
478
545
|
if (Object.keys(attributes).length === 0)
|
|
479
|
-
throw new CliError(
|
|
546
|
+
throw new CliError("Provide at least one memory field to update.");
|
|
480
547
|
const api = new CrewXApi(await resolveConfig());
|
|
481
548
|
const updated = await api.updateMemory(id, attributes);
|
|
482
549
|
if (options.json) {
|
|
@@ -486,12 +553,12 @@ export function createProgram() {
|
|
|
486
553
|
console.log(ui.success(`✓ Updated memory #${updated.id}: ${updated.title}`));
|
|
487
554
|
});
|
|
488
555
|
const integration = program
|
|
489
|
-
.command(
|
|
490
|
-
.description(
|
|
556
|
+
.command("integration")
|
|
557
|
+
.description("Search connected Slack, Notion, and Google Drive knowledge");
|
|
491
558
|
integration
|
|
492
|
-
.command(
|
|
493
|
-
.description(
|
|
494
|
-
.option(
|
|
559
|
+
.command("list")
|
|
560
|
+
.description("List knowledge providers connected to this workspace")
|
|
561
|
+
.option("--json", "print machine-readable JSON")
|
|
495
562
|
.action(async (options) => {
|
|
496
563
|
const api = new CrewXApi(await resolveConfig());
|
|
497
564
|
const integrations = await api.listIntegrations();
|
|
@@ -500,26 +567,26 @@ export function createProgram() {
|
|
|
500
567
|
return;
|
|
501
568
|
}
|
|
502
569
|
if (integrations.length === 0) {
|
|
503
|
-
console.log(ui.muted(
|
|
570
|
+
console.log(ui.muted("No knowledge integrations are connected."));
|
|
504
571
|
return;
|
|
505
572
|
}
|
|
506
573
|
for (const item of integrations) {
|
|
507
|
-
console.log(`${item.provider}${item.account_name ? ` — ${item.account_name}` :
|
|
574
|
+
console.log(`${item.provider}${item.account_name ? ` — ${item.account_name}` : ""}`);
|
|
508
575
|
}
|
|
509
576
|
});
|
|
510
577
|
integration
|
|
511
|
-
.command(
|
|
512
|
-
.description(
|
|
513
|
-
.argument(
|
|
514
|
-
.argument(
|
|
515
|
-
.option(
|
|
578
|
+
.command("search")
|
|
579
|
+
.description("Search one connected knowledge provider")
|
|
580
|
+
.argument("<provider>", "slack, notion, or google_drive")
|
|
581
|
+
.argument("<query...>", "search query")
|
|
582
|
+
.option("--json", "print machine-readable JSON")
|
|
516
583
|
.action(async (provider, queryParts, options) => {
|
|
517
|
-
if (![
|
|
518
|
-
throw new CliError(
|
|
584
|
+
if (!["slack", "notion", "google_drive"].includes(provider)) {
|
|
585
|
+
throw new CliError("Provider must be slack, notion, or google_drive.");
|
|
519
586
|
}
|
|
520
|
-
const query = queryParts.join(
|
|
587
|
+
const query = queryParts.join(" ").trim();
|
|
521
588
|
if (!query)
|
|
522
|
-
throw new CliError(
|
|
589
|
+
throw new CliError("Provide a search query.");
|
|
523
590
|
const api = new CrewXApi(await resolveConfig());
|
|
524
591
|
const results = await api.searchIntegration(provider, query);
|
|
525
592
|
if (options.json) {
|
|
@@ -527,23 +594,23 @@ export function createProgram() {
|
|
|
527
594
|
return;
|
|
528
595
|
}
|
|
529
596
|
if (results.length === 0) {
|
|
530
|
-
console.log(ui.muted(
|
|
597
|
+
console.log(ui.muted("No matching provider results."));
|
|
531
598
|
return;
|
|
532
599
|
}
|
|
533
600
|
for (const item of results) {
|
|
534
|
-
console.log(`${item.title ??
|
|
601
|
+
console.log(`${item.title ?? "Untitled"}${item.url ? `\n ${item.url}` : ""}`);
|
|
535
602
|
}
|
|
536
603
|
});
|
|
537
604
|
program
|
|
538
|
-
.command(
|
|
539
|
-
.description(
|
|
540
|
-
.option(
|
|
541
|
-
.option(
|
|
542
|
-
.option(
|
|
543
|
-
.option(
|
|
544
|
-
.option(
|
|
545
|
-
.addOption(new Option(
|
|
546
|
-
.addOption(new Option(
|
|
605
|
+
.command("daemon")
|
|
606
|
+
.description("Listen for CrewX work and execute it with a local agent")
|
|
607
|
+
.option("-a, --adapter <adapter>", "local harness ID (run `crewx doctor --json` to list)", process.env.CREWX_ADAPTER || DEFAULT_ADAPTER)
|
|
608
|
+
.option("--coding-cmd <command>", "advanced: run a local argv command and append the prompt (use {prompt} to position it)")
|
|
609
|
+
.option("--once", "poll once, process available work, then exit")
|
|
610
|
+
.option("--poll-interval <milliseconds>", "delay between polls", positiveInteger, DEFAULT_POLL_INTERVAL_MS)
|
|
611
|
+
.option("-C, --cwd <directory>", "local working directory", process.cwd())
|
|
612
|
+
.addOption(new Option("--bridge-run-id <uuid>").hideHelp())
|
|
613
|
+
.addOption(new Option("--bridge-run-nonce <nonce>").hideHelp())
|
|
547
614
|
.action(async (options) => {
|
|
548
615
|
// Commander treats the identically named root options as global even
|
|
549
616
|
// when they appear after `daemon`. Read those values explicitly so
|
|
@@ -567,29 +634,31 @@ export function createProgram() {
|
|
|
567
634
|
});
|
|
568
635
|
});
|
|
569
636
|
program
|
|
570
|
-
.command(
|
|
571
|
-
.description(
|
|
572
|
-
.argument(
|
|
573
|
-
.argument(
|
|
574
|
-
.option(
|
|
575
|
-
.option(
|
|
637
|
+
.command("run")
|
|
638
|
+
.description("Run one local agent without connecting to CrewX")
|
|
639
|
+
.argument("<adapter>", "local harness ID")
|
|
640
|
+
.argument("[prompt...]", "prompt (reads stdin when omitted)")
|
|
641
|
+
.option("-C, --cwd <directory>", "local working directory", process.cwd())
|
|
642
|
+
.option("--transport <transport>", "native or embedded ACP runtime", runtimeTransport, "native")
|
|
643
|
+
.option("--coding-cmd <command>", "advanced: run a local argv command and append the prompt (use {prompt} to position it)")
|
|
576
644
|
.action(async (adapterName, promptParts, options) => {
|
|
577
645
|
const globalOptions = program.opts();
|
|
578
646
|
const adapter = parseAdapter(adapterName);
|
|
579
|
-
const argumentPrompt = promptParts.join(
|
|
647
|
+
const argumentPrompt = promptParts.join(" ").trim();
|
|
580
648
|
const prompt = argumentPrompt || (await readStdin()).trim();
|
|
581
649
|
if (!prompt)
|
|
582
|
-
throw new CliError(
|
|
650
|
+
throw new CliError("Provide a prompt argument or pipe a prompt on stdin.");
|
|
583
651
|
let displayedMessages = 0;
|
|
584
652
|
const result = await runAdapter({
|
|
585
653
|
adapter,
|
|
586
654
|
prompt,
|
|
587
655
|
cwd: resolve(globalOptions.cwd ?? options.cwd),
|
|
656
|
+
profile: { transport: options.transport },
|
|
588
657
|
...((globalOptions.codingCmd ?? options.codingCmd)
|
|
589
658
|
? { codingCommand: globalOptions.codingCmd ?? options.codingCmd }
|
|
590
659
|
: {}),
|
|
591
660
|
onMessage(message) {
|
|
592
|
-
stdout.write(message.endsWith(
|
|
661
|
+
stdout.write(message.endsWith("\n") ? message : `${message}\n`);
|
|
593
662
|
displayedMessages += 1;
|
|
594
663
|
},
|
|
595
664
|
onStderr(line) {
|
|
@@ -597,7 +666,7 @@ export function createProgram() {
|
|
|
597
666
|
},
|
|
598
667
|
});
|
|
599
668
|
if (displayedMessages === 0 && result.stdout.length > 0) {
|
|
600
|
-
stdout.write(`${result.stdout.join(
|
|
669
|
+
stdout.write(`${result.stdout.join("\n")}\n`);
|
|
601
670
|
}
|
|
602
671
|
if (result.exitCode !== 0) {
|
|
603
672
|
throw new CliError(`${adapter} exited with code ${String(result.exitCode)}.`, result.exitCode ?? 1);
|
|
@@ -619,7 +688,8 @@ export function isMainModule(moduleUrl, invokedPath = process.argv[1]) {
|
|
|
619
688
|
if (!invokedPath)
|
|
620
689
|
return false;
|
|
621
690
|
try {
|
|
622
|
-
return realpathSync.native(fileURLToPath(moduleUrl)) ===
|
|
691
|
+
return (realpathSync.native(fileURLToPath(moduleUrl)) ===
|
|
692
|
+
realpathSync.native(resolve(invokedPath)));
|
|
623
693
|
}
|
|
624
694
|
catch {
|
|
625
695
|
return false;
|
|
@@ -628,10 +698,10 @@ export function isMainModule(moduleUrl, invokedPath = process.argv[1]) {
|
|
|
628
698
|
if (isMainModule(import.meta.url)) {
|
|
629
699
|
await main();
|
|
630
700
|
}
|
|
631
|
-
export { CrewXApi, } from
|
|
632
|
-
export { configPath, readStoredConfig, resolveConfig, saveConfig } from
|
|
633
|
-
export { decodeJoinCode, encodeJoinCode } from
|
|
634
|
-
export { assemblePrompt, assignmentFromEvent } from
|
|
635
|
-
export { buildAdapterInvocation, parseAdapterLine, parseCodingCommand, runAdapter } from
|
|
636
|
-
export { attachBridgeWatchdog } from
|
|
701
|
+
export { CrewXApi, } from "./api.js";
|
|
702
|
+
export { configPath, readStoredConfig, resolveConfig, saveConfig, } from "./config.js";
|
|
703
|
+
export { decodeJoinCode, encodeJoinCode } from "./join.js";
|
|
704
|
+
export { assemblePrompt, assignmentFromEvent } from "./prompt.js";
|
|
705
|
+
export { buildAdapterInvocation, parseAdapterLine, parseCodingCommand, runAdapter, } from "./adapters.js";
|
|
706
|
+
export { attachBridgeWatchdog } from "./bridge-watchdog.js";
|
|
637
707
|
//# sourceMappingURL=index.js.map
|