crewx-agent-cli 0.2.5 → 0.2.7

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/index.js CHANGED
@@ -1,25 +1,30 @@
1
1
  #!/usr/bin/env node
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 { parseAdapter, probeAdapter, runAdapter } from './adapters.js';
9
- import { CrewXApi } from './api.js';
10
- import { attachBridgeWatchdog } from './bridge-watchdog.js';
11
- import { resolveConfig, saveConfig, readStoredConfig } from './config.js';
12
- import { CLI_NAME, CLI_VERSION, DEFAULT_ADAPTER, DEFAULT_POLL_INTERVAL_MS } from './constants.js';
13
- import { runDaemon } from './daemon.js';
14
- import { CliError, errorMessage, redactSecrets } from './errors.js';
15
- import { decodeJoinCode } from './join.js';
16
- import { ui } from './ui.js';
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 !== 'function' || typeof tls.setDefaultCACertificates !== 'function')
19
+ if (typeof tls.getCACertificates !== "function" ||
20
+ typeof tls.setDefaultCACertificates !== "function")
19
21
  return;
20
22
  try {
21
23
  tls.setDefaultCACertificates([
22
- ...new Set([...tls.getCACertificates('default'), ...tls.getCACertificates('system')]),
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('must be a positive integer');
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('must be between 1 and 5');
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('utf8');
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 [...new Set(value.split(',').map((item) => item.trim()).filter(Boolean))];
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 ?? 'the local runtime is unavailable'}. Run \`crewx doctor\` for installation details.`);
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 === 'openclaw') {
75
- console.log(ui.warning('OpenClaw uses its own configured workspace, sandbox, tools, and approvals; CrewX does not rewrite them.'));
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('\nStopping CrewX agent…');
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('SIGINT', stop);
91
- process.on('SIGTERM', stop);
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 ? { codingCommand: 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('SIGINT', stop);
109
- process.removeListener('SIGTERM', stop);
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('Bring your local coding agents into a CrewX workspace.')
133
+ .description("Bring your local coding agents into a CrewX workspace.")
117
134
  .version(CLI_VERSION)
118
- .option('--join <code>', 'join an agent profile using the command copied from CrewX')
119
- .option('-a, --adapter <adapter>', 'override the runtime encoded in a join command')
120
- .option('--coding-cmd <command>', 'advanced: run a local argv command and append the prompt (use {prompt} to position it)')
121
- .option('--once', 'poll once, process available work, then exit')
122
- .option('--poll-interval <milliseconds>', 'delay between polls', positiveInteger, DEFAULT_POLL_INTERVAL_MS)
123
- .option('-C, --cwd <directory>', 'local working directory', process.cwd())
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(' Runs in this terminal. Press Ctrl+C to disconnect.'));
155
+ console.log(ui.muted(" Runs in this terminal. Press Ctrl+C to disconnect."));
139
156
  if (options.codingCmd) {
140
- console.log(ui.warning(' Custom command enabled; CrewX will execute it directly without a shell.'));
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('connect')
154
- .description('Authenticate this machine with a CrewX workspace')
155
- .requiredOption('--url <url>', 'CrewX server URL')
156
- .requiredOption('--token <token>', 'agent connection token')
157
- .option('--name <name>', 'friendly name for this machine')
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('✓ Connected to CrewX'));
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('(mode 0600)')}`);
167
- console.log(`\nStart listening with ${ui.heading('crewx daemon')}.`);
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('status')
171
- .description('Show the current CrewX connection')
172
- .option('--json', 'print machine-readable JSON')
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({ connected: true, url: api.baseUrl, name: config.name, response });
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('● CrewX is reachable'));
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 === 'bridge_pipe'
186
- ? 'one-shot Bridge credential'
187
- : config.tokenSource === 'environment'
188
- ? 'CREWX_TOKEN'
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('doctor')
193
- .description('Check local agent binaries and CrewX connectivity')
194
- .option('--json', 'print machine-readable JSON')
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(['codex', 'claude', 'pi', 'hermes', 'openclaw'].map(async (adapter) => [adapter, await probeAdapter(adapter)]));
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 = { node: process.version, adapters: Object.fromEntries(adapters), connection };
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('CrewX doctor'));
222
- console.log(` Node ${process.version} ${Number(process.versions.node.split('.')[0]) >= 22 ? ui.success('') : ui.warning('requires 22+')}`);
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('') : ui.warning('')} ${probe.version ?? probe.error ?? 'not found'}`);
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('✓ reachable') : connection.configured ? ui.warning(`unreachable — ${connection.error}`) : ui.warning('not configured')}`);
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.command('task').description('List, create, and update CrewX tasks');
254
+ const task = program
255
+ .command("task")
256
+ .description("List, create, and update CrewX tasks");
229
257
  task
230
- .command('list')
231
- .description('List tasks visible to this connected agent')
232
- .option('--status <status>', 'filter by task status')
233
- .option('--assigned-to-me', 'show only tasks assigned to this agent')
234
- .option('--include-epics', 'include epics in the result')
235
- .option('--search <text>', 'search titles and descriptions')
236
- .option('--json', 'print machine-readable JSON')
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('No matching tasks.'));
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 ? ' (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('create')
259
- .description('Create a workspace task')
260
- .requiredOption('--title <title>', 'task title')
261
- .option('--description <markdown>', 'task description')
262
- .option('--status <status>', 'backlog, todo, in_progress, review, done, or cancelled')
263
- .option('--priority <priority>', 'low, medium, high, or urgent')
264
- .option('--labels <labels>', 'comma-separated labels')
265
- .option('--due <date>', 'due date (YYYY-MM-DD)')
266
- .option('--project <channel-id>', 'project channel ID', positiveInteger)
267
- .option('--epic <task-id>', 'epic task ID', positiveInteger)
268
- .option('--assign-to-me', 'assign the new task to this connected agent')
269
- .option('--json', 'print machine-readable JSON')
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 ? { description: options.description } : {}),
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 ? { labels: commaSeparated(options.labels) } : {}),
307
+ ...(options.labels !== undefined
308
+ ? { labels: commaSeparated(options.labels) }
309
+ : {}),
278
310
  ...(options.due ? { due_at: options.due } : {}),
279
- ...(options.project !== undefined ? { project_channel_id: options.project } : {}),
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('update')
291
- .description('Update a task assigned to this connected agent')
292
- .argument('<id>', 'task ID', positiveInteger)
293
- .option('--title <title>', 'replace the task title')
294
- .option('--description <markdown>', 'replace the task description')
295
- .option('--status <status>', 'set task status')
296
- .option('--priority <priority>', 'set task priority')
297
- .option('--labels <labels>', 'replace labels with a comma-separated list')
298
- .option('--due <date>', 'set due date (YYYY-MM-DD)')
299
- .option('--result <markdown>', 'record the work result')
300
- .option('--pull-request <url>', 'record a pull request URL')
301
- .option('--json', 'print machine-readable JSON')
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 ? { description: options.description } : {}),
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 ? { labels: commaSeparated(options.labels) } : {}),
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 ? { pull_request_url: options.pullRequest } : {}),
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('Provide at least one task field to update.');
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.command('doc').description('List, create, and update CrewX documents');
363
+ const doc = program
364
+ .command("doc")
365
+ .description("List, create, and update CrewX documents");
324
366
  doc
325
- .command('list')
326
- .description('List documents readable by connected agents')
327
- .option('--search <text>', 'search document titles and contents')
328
- .option('--json', 'print machine-readable JSON')
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('No matching documents.'));
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 ? ' (protected)' : ''}`);
383
+ console.log(`#${document.id} v${document.current_version} ${document.title}${document.protected ? " (protected)" : ""}`);
342
384
  }
343
385
  });
344
386
  doc
345
- .command('create')
346
- .description('Create an agent-readable workspace document')
347
- .requiredOption('--title <title>', 'document title')
348
- .option('--content <markdown>', 'document content')
349
- .option('--folder <folder-id>', 'folder ID', positiveInteger)
350
- .option('--summary <summary>', 'version summary')
351
- .option('--json', 'print machine-readable JSON')
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 ? { content: options.content } : {}),
357
- ...(options.folder !== undefined ? { folder_id: options.folder } : {}),
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('update')
368
- .description('Update an agent-readable, unprotected document')
369
- .argument('<id>', 'document ID', positiveInteger)
370
- .requiredOption('--expected-version <version>', 'current version from doc list', positiveInteger)
371
- .option('--title <title>', 'replace the document title')
372
- .option('--content <markdown>', 'replace the document content')
373
- .option('--folder <folder-id>', 'move to a folder', positiveInteger)
374
- .option('--summary <summary>', 'version summary')
375
- .option('--json', 'print machine-readable JSON')
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 ? { content: options.content } : {}),
381
- ...(options.folder !== undefined ? { folder_id: options.folder } : {}),
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('Provide at least one document field to update.');
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.command('memory').description('List, create, and update durable CrewX memories');
444
+ const memory = program
445
+ .command("memory")
446
+ .description("List, create, and update durable CrewX memories");
395
447
  memory
396
- .command('list')
397
- .description('List memories visible to this connected agent')
398
- .option('--search <text>', 'search memory titles, contents, and sources')
399
- .option('--category <category>', 'filter by category')
400
- .option('--scope <scope>', 'workspace or channel')
401
- .option('--channel <channel-id>', 'filter by channel ID', positiveInteger)
402
- .option('--json', 'print machine-readable JSON')
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 && !['workspace', 'channel'].includes(options.scope)) {
405
- throw new CliError('Memory scope must be workspace or channel.');
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 ? { channelId: options.channel } : {}),
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('No matching memories.'));
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 === 'channel' ? `channel ${item.channel?.name ?? ''}`.trim() : 'workspace';
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('create')
429
- .description('Record a durable learning for this workspace')
430
- .requiredOption('--content <text>', 'memory content')
431
- .option('--title <title>', 'memory title; generated from content when omitted')
432
- .option('--category <category>', 'memory category', 'general')
433
- .option('--importance <level>', 'importance from 1 to 5', memoryImportance, 3)
434
- .option('--source <source>', 'memory source', 'agent')
435
- .option('--channel <channel-id>', 'scope the memory to a visible channel', positiveInteger)
436
- .option('--json', 'print machine-readable JSON')
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 ? { channel_id: options.channel } : {}),
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('update')
455
- .description('Update a memory recorded by this connected agent')
456
- .argument('<id>', 'memory ID', positiveInteger)
457
- .option('--title <title>', 'replace the memory title')
458
- .option('--content <text>', 'replace the memory content')
459
- .option('--category <category>', 'replace the category')
460
- .option('--importance <level>', 'importance from 1 to 5', memoryImportance)
461
- .option('--source <source>', 'replace the source')
462
- .option('--channel <channel-id>', 'move the memory to a visible channel', positiveInteger)
463
- .option('--workspace-scope', 'move the memory to workspace scope')
464
- .option('--json', 'print machine-readable JSON')
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('Choose either --channel or --workspace-scope, not both.');
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 ? { content: options.content } : {}),
472
- ...(options.category !== undefined ? { category: options.category } : {}),
473
- ...(options.importance !== undefined ? { importance: options.importance } : {}),
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 ? { channel_id: options.channel } : {}),
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('Provide at least one memory field to update.');
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('integration')
490
- .description('Search connected Slack, Notion, and Google Drive knowledge');
556
+ .command("integration")
557
+ .description("Search connected Slack, Notion, and Google Drive knowledge");
491
558
  integration
492
- .command('list')
493
- .description('List knowledge providers connected to this workspace')
494
- .option('--json', 'print machine-readable JSON')
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('No knowledge integrations are connected.'));
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('search')
512
- .description('Search one connected knowledge provider')
513
- .argument('<provider>', 'slack, notion, or google_drive')
514
- .argument('<query...>', 'search query')
515
- .option('--json', 'print machine-readable JSON')
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 (!['slack', 'notion', 'google_drive'].includes(provider)) {
518
- throw new CliError('Provider must be slack, notion, or google_drive.');
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(' ').trim();
587
+ const query = queryParts.join(" ").trim();
521
588
  if (!query)
522
- throw new CliError('Provide a search query.');
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('No matching provider results.'));
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 ?? 'Untitled'}${item.url ? `\n ${item.url}` : ''}`);
601
+ console.log(`${item.title ?? "Untitled"}${item.url ? `\n ${item.url}` : ""}`);
535
602
  }
536
603
  });
537
604
  program
538
- .command('daemon')
539
- .description('Listen for CrewX work and execute it with a local agent')
540
- .option('-a, --adapter <adapter>', 'codex, claude, pi, hermes, or openclaw', process.env.CREWX_ADAPTER || DEFAULT_ADAPTER)
541
- .option('--coding-cmd <command>', 'advanced: run a local argv command and append the prompt (use {prompt} to position it)')
542
- .option('--once', 'poll once, process available work, then exit')
543
- .option('--poll-interval <milliseconds>', 'delay between polls', positiveInteger, DEFAULT_POLL_INTERVAL_MS)
544
- .option('-C, --cwd <directory>', 'local working directory', process.cwd())
545
- .addOption(new Option('--bridge-run-id <uuid>').hideHelp())
546
- .addOption(new Option('--bridge-run-nonce <nonce>').hideHelp())
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('run')
571
- .description('Run one local agent without connecting to CrewX')
572
- .argument('<adapter>', 'codex, claude, pi, hermes, or openclaw')
573
- .argument('[prompt...]', 'prompt (reads stdin when omitted)')
574
- .option('-C, --cwd <directory>', 'local working directory', process.cwd())
575
- .option('--coding-cmd <command>', 'advanced: run a local argv command and append the prompt (use {prompt} to position it)')
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(' ').trim();
647
+ const argumentPrompt = promptParts.join(" ").trim();
580
648
  const prompt = argumentPrompt || (await readStdin()).trim();
581
649
  if (!prompt)
582
- throw new CliError('Provide a prompt argument or pipe a prompt on stdin.');
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('\n') ? message : `${message}\n`);
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('\n')}\n`);
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)) === realpathSync.native(resolve(invokedPath));
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 './api.js';
632
- export { configPath, readStoredConfig, resolveConfig, saveConfig } from './config.js';
633
- export { decodeJoinCode, encodeJoinCode } from './join.js';
634
- export { assemblePrompt, assignmentFromEvent } from './prompt.js';
635
- export { buildAdapterInvocation, parseAdapterLine, parseCodingCommand, runAdapter } from './adapters.js';
636
- export { attachBridgeWatchdog } from './bridge-watchdog.js';
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