@crewx/cli 0.9.0-rc.2 → 0.9.0-rc.20

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.
@@ -0,0 +1,45 @@
1
+ /**
2
+ * CLI-layer policy: inject CrewX home as an extra writable root for Codex
3
+ * `workspace-write` sandbox invocations.
4
+ *
5
+ * Why this lives in packages/cli (not packages/sdk):
6
+ * CrewX home (`~/.crewx` by default) is a product-specific path. The SDK's
7
+ * `additionalArgsProvider` extension point (WI-20260703-001) exists precisely
8
+ * so that product layers like this CLI can inject such paths without the SDK
9
+ * knowing about them.
10
+ *
11
+ * Why `-c sandbox_workspace_write.writable_roots=[...]` instead of `--add-dir`:
12
+ * Verified against codex-cli 0.139.0 (`codex exec --help` / `codex exec resume --help`):
13
+ * - `codex exec` supports `--add-dir <DIR>`.
14
+ * - `codex exec resume` does NOT support `--add-dir` (only `-c/--config`).
15
+ * Using the `-c` config-override form works identically for both `exec` and
16
+ * `exec resume`, avoiding a resume-incompatible flag.
17
+ */
18
+ import type { AdditionalArgsProvider } from '@crewx/sdk';
19
+ /**
20
+ * Resolve CrewX home directory.
21
+ * Priority: `CREWX_HOME` env var → `~/.crewx`.
22
+ */
23
+ export declare function resolveCrewxHome(): string;
24
+ /**
25
+ * Escape a string for embedding inside a TOML basic string (`"..."`).
26
+ * Sufficient for filesystem paths: handles backslash (Windows paths),
27
+ * double-quote, and control characters that could otherwise break TOML
28
+ * parsing of the `-c key="value"` override.
29
+ */
30
+ export declare function escapeTomlBasicString(value: string): string;
31
+ /**
32
+ * Build the `-c sandbox_workspace_write.writable_roots=["<path>"]` arg pair.
33
+ * Works for both `codex exec` and `codex exec resume` (config-override form).
34
+ */
35
+ export declare function buildCodexWritableRootArgs(crewxHome: string): string[];
36
+ /**
37
+ * Create an `AdditionalArgsProvider` (SDK extension point, WI-20260703-001)
38
+ * that injects CrewX home as a Codex writable root for `workspace-write`
39
+ * equivalent modes (`agent`, `auto`, `undefined`).
40
+ *
41
+ * No-ops for:
42
+ * - Non-codex providers.
43
+ * - Modes in {@link NO_INJECT_MODES} (read-only/plan/agent-full-access/yolo/danger-full-access).
44
+ */
45
+ export declare function createCodexWritableRootsProvider(crewxHome?: string): AdditionalArgsProvider;
@@ -0,0 +1,88 @@
1
+ "use strict";
2
+ /**
3
+ * CLI-layer policy: inject CrewX home as an extra writable root for Codex
4
+ * `workspace-write` sandbox invocations.
5
+ *
6
+ * Why this lives in packages/cli (not packages/sdk):
7
+ * CrewX home (`~/.crewx` by default) is a product-specific path. The SDK's
8
+ * `additionalArgsProvider` extension point (WI-20260703-001) exists precisely
9
+ * so that product layers like this CLI can inject such paths without the SDK
10
+ * knowing about them.
11
+ *
12
+ * Why `-c sandbox_workspace_write.writable_roots=[...]` instead of `--add-dir`:
13
+ * Verified against codex-cli 0.139.0 (`codex exec --help` / `codex exec resume --help`):
14
+ * - `codex exec` supports `--add-dir <DIR>`.
15
+ * - `codex exec resume` does NOT support `--add-dir` (only `-c/--config`).
16
+ * Using the `-c` config-override form works identically for both `exec` and
17
+ * `exec resume`, avoiding a resume-incompatible flag.
18
+ */
19
+ Object.defineProperty(exports, "__esModule", { value: true });
20
+ exports.resolveCrewxHome = resolveCrewxHome;
21
+ exports.escapeTomlBasicString = escapeTomlBasicString;
22
+ exports.buildCodexWritableRootArgs = buildCodexWritableRootArgs;
23
+ exports.createCodexWritableRootsProvider = createCodexWritableRootsProvider;
24
+ const os_1 = require("os");
25
+ const path_1 = require("path");
26
+ /**
27
+ * crewx.yaml / query-option `mode` values for which Codex is NOT running in
28
+ * (or being pushed into) a `workspace-write`-equivalent sandbox. Injecting an
29
+ * extra writable root is meaningless (read-only/plan) or redundant
30
+ * (full-access variants already allow writes everywhere) in these modes.
31
+ */
32
+ const NO_INJECT_MODES = new Set([
33
+ 'read-only',
34
+ 'plan',
35
+ 'agent-full-access',
36
+ 'yolo',
37
+ 'danger-full-access',
38
+ ]);
39
+ /**
40
+ * Resolve CrewX home directory.
41
+ * Priority: `CREWX_HOME` env var → `~/.crewx`.
42
+ */
43
+ function resolveCrewxHome() {
44
+ return process.env.CREWX_HOME ?? (0, path_1.join)((0, os_1.homedir)(), '.crewx');
45
+ }
46
+ /**
47
+ * Escape a string for embedding inside a TOML basic string (`"..."`).
48
+ * Sufficient for filesystem paths: handles backslash (Windows paths),
49
+ * double-quote, and control characters that could otherwise break TOML
50
+ * parsing of the `-c key="value"` override.
51
+ */
52
+ function escapeTomlBasicString(value) {
53
+ return value
54
+ .replace(/\\/g, '\\\\')
55
+ .replace(/"/g, '\\"')
56
+ .replace(/\n/g, '\\n')
57
+ .replace(/\r/g, '\\r')
58
+ .replace(/\t/g, '\\t');
59
+ }
60
+ /**
61
+ * Build the `-c sandbox_workspace_write.writable_roots=["<path>"]` arg pair.
62
+ * Works for both `codex exec` and `codex exec resume` (config-override form).
63
+ */
64
+ function buildCodexWritableRootArgs(crewxHome) {
65
+ const escaped = escapeTomlBasicString(crewxHome);
66
+ return ['-c', `sandbox_workspace_write.writable_roots=["${escaped}"]`];
67
+ }
68
+ function isCodexProvider(ctx) {
69
+ return ctx.providerId === 'codex' || ctx.providerStr === 'cli/codex';
70
+ }
71
+ /**
72
+ * Create an `AdditionalArgsProvider` (SDK extension point, WI-20260703-001)
73
+ * that injects CrewX home as a Codex writable root for `workspace-write`
74
+ * equivalent modes (`agent`, `auto`, `undefined`).
75
+ *
76
+ * No-ops for:
77
+ * - Non-codex providers.
78
+ * - Modes in {@link NO_INJECT_MODES} (read-only/plan/agent-full-access/yolo/danger-full-access).
79
+ */
80
+ function createCodexWritableRootsProvider(crewxHome = resolveCrewxHome()) {
81
+ return (ctx) => {
82
+ if (!isCodexProvider(ctx))
83
+ return [];
84
+ if (ctx.mode !== undefined && NO_INJECT_MODES.has(ctx.mode))
85
+ return [];
86
+ return buildCodexWritableRootArgs(crewxHome);
87
+ };
88
+ }
@@ -9,6 +9,7 @@ const plugins_1 = require("@crewx/sdk/plugins");
9
9
  const repository_1 = require("@crewx/sdk/repository");
10
10
  const register_builtin_tools_1 = require("../register-builtin-tools");
11
11
  const version_1 = require("../utils/version");
12
+ const codex_writable_roots_1 = require("./codex-writable-roots");
12
13
  /**
13
14
  * Build a Crewx instance with CLI-standard plugins (FileLogger + SqliteTracing)
14
15
  * and built-in tools registered. Use this from any CLI command that needs a
@@ -52,6 +53,7 @@ async function createCliCrewx(configPath = process.env.CREWX_CONFIG ?? 'crewx.ya
52
53
  }
53
54
  const crewx = await sdk_1.Crewx.loadYaml(yamlPath, {
54
55
  remoteFactory: createCliCrewx,
56
+ additionalArgsProvider: (0, codex_writable_roots_1.createCodexWritableRootsProvider)(),
55
57
  });
56
58
  (0, register_builtin_tools_1.registerBuiltinToolsIfNeeded)(crewx);
57
59
  await crewx.use(new plugins_1.ConversationPlugin());
@@ -27,6 +27,7 @@ const parse_common_flags_1 = require("./parse-common-flags");
27
27
  const resolve_prompt_1 = require("./resolve-prompt");
28
28
  const crewx_cli_1 = require("../bootstrap/crewx-cli");
29
29
  const inherited_trace_1 = require("../utils/inherited-trace");
30
+ const write_output_1 = require("./write-output");
30
31
  /**
31
32
  * Handle `crewx execute <agentRef> <message>` command.
32
33
  *
@@ -34,7 +35,7 @@ const inherited_trace_1 = require("../utils/inherited-trace");
34
35
  * --verbose: debug info written to stderr, response to stdout.
35
36
  */
36
37
  async function handleExecute(args) {
37
- const { thread, provider, metadata, verbose, config, outputFormat, effort, promptFile, vars, rest } = (0, parse_common_flags_1.parseCommonFlags)(args);
38
+ const { thread, provider, metadata, verbose, config, outputFormat, effort, promptFile, overdrive, vars, out, rest } = (0, parse_common_flags_1.parseCommonFlags)(args);
38
39
  const { agentRef: parsedAgentRef, message } = (0, parse_agent_message_1.parseAgentMessage)(rest);
39
40
  // No @mention → default to @crewx agent (matches cli-bak behaviour)
40
41
  const agentRef = parsedAgentRef || '@crewx';
@@ -62,9 +63,11 @@ async function handleExecute(args) {
62
63
  console.error(' --verbose Debug output mode');
63
64
  console.error(' --config/-c <path> Config file path');
64
65
  console.error(' --output-format <fmt> Output format (json|text|stream-json)');
66
+ console.error(' --out/-o <path> Save result to file (stdout suppressed)');
65
67
  console.error(' --effort <level> Model effort (high|medium|low)');
66
68
  console.error(' -f/--prompt-file <path> Read task body from file');
67
69
  console.error(' --var key=value Template variable (repeatable)');
70
+ console.error(' --overdrive Activate overdrive (boost) profile for this request');
68
71
  process.exit(1);
69
72
  }
70
73
  const configPath = config ?? process.env.CREWX_CONFIG ?? 'crewx.yaml';
@@ -83,6 +86,8 @@ async function handleExecute(args) {
83
86
  process.stderr.write(`📄 Output-format: ${outputFormat}\n`);
84
87
  if (effort)
85
88
  process.stderr.write(`⚡ Effort: ${effort}\n`);
89
+ if (overdrive)
90
+ process.stderr.write(`🚀 Overdrive: ON\n`);
86
91
  process.stderr.write('─'.repeat(60) + '\n');
87
92
  }
88
93
  let parsedMetadata = {};
@@ -99,6 +104,7 @@ async function handleExecute(args) {
99
104
  const result = await crewx.execute(agentRef, finalMessage, {
100
105
  provider,
101
106
  effort: effort || undefined,
107
+ overdrive: overdrive || undefined,
102
108
  threadId: thread,
103
109
  metadata: Object.keys(parsedMetadata).length > 0 ? parsedMetadata : undefined,
104
110
  vars: Object.keys(vars).length > 0 ? vars : undefined,
@@ -107,6 +113,7 @@ async function handleExecute(args) {
107
113
  if (!result.ok) {
108
114
  const errMsg = result.error?.message ?? 'Execute failed';
109
115
  console.error(errMsg);
116
+ (0, write_output_1.appendError)(out, errMsg);
110
117
  exitCode = 1;
111
118
  }
112
119
  else {
@@ -119,7 +126,7 @@ async function handleExecute(args) {
119
126
  process.stderr.write('\n📄 Response:\n');
120
127
  process.stderr.write('─'.repeat(40) + '\n');
121
128
  }
122
- console.log(result.data);
129
+ (0, write_output_1.writeResult)(out, result.data);
123
130
  if (verbose) {
124
131
  process.stderr.write('\n✅ Execute completed successfully\n');
125
132
  }
@@ -128,6 +135,7 @@ async function handleExecute(args) {
128
135
  catch (err) {
129
136
  const errMsg = err instanceof Error ? err.message : String(err);
130
137
  console.error(`Error: ${errMsg}`);
138
+ (0, write_output_1.appendError)(out, `Error: ${errMsg}`);
131
139
  exitCode = 1;
132
140
  }
133
141
  finally {
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Supports both `--flag=value` and `--flag value` forms.
5
5
  * Handles: --thread, --provider, --metadata, --verbose, --config/-c,
6
- * --output-format, --effort, --prompt-file/-f.
6
+ * --output-format, --effort, --prompt-file/-f, --overdrive.
7
7
  *
8
8
  * Strict mode: unknown --xxx tokens after known flags are consumed throw an
9
9
  * error instead of silently leaking into the message prompt.
@@ -26,6 +26,10 @@ export interface CommonFlags {
26
26
  effort?: string;
27
27
  /** Path to a file whose content is used as the prompt body (-f/--prompt-file). */
28
28
  promptFile?: string;
29
+ /** Whether overdrive (boost) is active for this request. */
30
+ overdrive: boolean;
31
+ /** Output file path for saving result (--out/-o). */
32
+ out?: string;
29
33
  /** Template variables from --var key=value flags. */
30
34
  vars: Record<string, string>;
31
35
  /** Remaining non-flag positional arguments. */
@@ -4,7 +4,7 @@
4
4
  *
5
5
  * Supports both `--flag=value` and `--flag value` forms.
6
6
  * Handles: --thread, --provider, --metadata, --verbose, --config/-c,
7
- * --output-format, --effort, --prompt-file/-f.
7
+ * --output-format, --effort, --prompt-file/-f, --overdrive.
8
8
  *
9
9
  * Strict mode: unknown --xxx tokens after known flags are consumed throw an
10
10
  * error instead of silently leaking into the message prompt.
@@ -68,7 +68,9 @@ function parseCommonFlags(args) {
68
68
  const outputFormat = parseFlag(args, '--output-format');
69
69
  const effort = parseFlag(args, '--effort');
70
70
  const promptFile = parseFlag(args, '--prompt-file', '-f');
71
+ const out = parseFlag(args, '--out', '-o');
71
72
  const verbose = hasFlag(args, '--verbose');
73
+ const overdrive = hasFlag(args, '--overdrive');
72
74
  // Collect consumed positions for known flags
73
75
  const consumed = new Set();
74
76
  const flagPairs = [
@@ -79,6 +81,7 @@ function parseCommonFlags(args) {
79
81
  { names: ['--output-format'] },
80
82
  { names: ['--effort'] },
81
83
  { names: ['--prompt-file', '-f'] },
84
+ { names: ['--out', '-o'] },
82
85
  ];
83
86
  // Parse --var key=value flags (multiple allowed; last value wins on duplicate keys)
84
87
  const vars = {};
@@ -88,6 +91,10 @@ function parseCommonFlags(args) {
88
91
  consumed.add(i);
89
92
  continue;
90
93
  }
94
+ if (arg === '--overdrive') {
95
+ consumed.add(i);
96
+ continue;
97
+ }
91
98
  // --var=key=value or --var key=value
92
99
  if (arg.startsWith('--var=') || arg === '--var') {
93
100
  let kv;
@@ -148,7 +155,7 @@ function parseCommonFlags(args) {
148
155
  }
149
156
  rest.push(token);
150
157
  }
151
- return { thread, provider, metadata, verbose, config, outputFormat, effort, promptFile, vars, rest };
158
+ return { thread, provider, metadata, verbose, config, outputFormat, effort, promptFile, overdrive, vars, out, rest };
152
159
  }
153
160
  /**
154
161
  * Parse metadata JSON string from --metadata flag.
@@ -21,14 +21,54 @@ function formatElapsed(ms) {
21
21
  const h = Math.floor(m / 60);
22
22
  return `${h}h ${m % 60}m`;
23
23
  }
24
+ /**
25
+ * Antigravity-family providers (coding_agent_command starting with 'agy',
26
+ * path-included forms like '/usr/local/bin/agy' also count) don't emit
27
+ * parseable logs, so they get a distinct 'n/a' instead of '—'.
28
+ */
29
+ function isAgyCommand(codingAgentCommand) {
30
+ const cmd = (codingAgentCommand ?? '').trim();
31
+ if (!cmd)
32
+ return false;
33
+ const firstToken = cmd.split(/\s+/)[0] ?? '';
34
+ const base = firstToken.split('/').pop() ?? firstToken;
35
+ return base.startsWith('agy');
36
+ }
37
+ function getLastActive(task) {
38
+ try {
39
+ const entries = task.logs ? JSON.parse(task.logs) : [];
40
+ if (Array.isArray(entries) && entries.length > 0) {
41
+ const lastEntry = entries[entries.length - 1];
42
+ const ts = lastEntry?.timestamp;
43
+ const parsedMs = ts ? new Date(ts).getTime() : NaN;
44
+ if (ts && !Number.isNaN(parsedMs)) {
45
+ const diffMs = Math.max(0, Date.now() - parsedMs);
46
+ return {
47
+ display: `${formatElapsed(diffMs)} ago`,
48
+ isoTimestamp: ts,
49
+ logCapable: true,
50
+ };
51
+ }
52
+ }
53
+ }
54
+ catch {
55
+ // fall through to antigravity/dash fallback below
56
+ }
57
+ if (isAgyCommand(task.coding_agent_command)) {
58
+ return { display: 'n/a', isoTimestamp: null, logCapable: false };
59
+ }
60
+ return { display: '—', isoTimestamp: null, logCapable: true };
61
+ }
24
62
  function taskToRow(task) {
25
63
  const elapsed = formatElapsed(Date.now() - new Date(task.started_at).getTime());
64
+ const lastActive = getLastActive(task);
26
65
  return [
27
66
  task.id,
28
67
  task.agent_id ?? '—',
29
68
  task.pid !== null && task.pid !== undefined ? String(task.pid) : '—',
30
69
  elapsed,
31
70
  task.mode ?? '—',
71
+ lastActive.display,
32
72
  ];
33
73
  }
34
74
  function renderTable(headers, rows) {
@@ -49,10 +89,18 @@ async function handlePs(args) {
49
89
  return;
50
90
  }
51
91
  if (args.includes('--json')) {
52
- console.log(JSON.stringify(tasks, null, 2));
92
+ const withLastActive = tasks.map((task) => {
93
+ const lastActive = getLastActive(task);
94
+ return {
95
+ ...task,
96
+ last_active_at: lastActive.isoTimestamp,
97
+ log_capable: lastActive.logCapable,
98
+ };
99
+ });
100
+ console.log(JSON.stringify(withLastActive, null, 2));
53
101
  return;
54
102
  }
55
- const headers = ['TASK ID', 'AGENT', 'PID', 'ELAPSED', 'MODE'];
103
+ const headers = ['TASK ID', 'AGENT', 'PID', 'ELAPSED', 'MODE', 'LAST ACTIVE'];
56
104
  const rows = tasks.map(taskToRow);
57
105
  renderTable(headers, rows);
58
106
  console.log(`\n ${tasks.length} running task(s)`);
@@ -27,6 +27,7 @@ const parse_common_flags_1 = require("./parse-common-flags");
27
27
  const resolve_prompt_1 = require("./resolve-prompt");
28
28
  const crewx_cli_1 = require("../bootstrap/crewx-cli");
29
29
  const inherited_trace_1 = require("../utils/inherited-trace");
30
+ const write_output_1 = require("./write-output");
30
31
  /**
31
32
  * Handle `crewx query <agentRef> <message>` command.
32
33
  *
@@ -34,7 +35,7 @@ const inherited_trace_1 = require("../utils/inherited-trace");
34
35
  * --verbose: debug info written to stderr, response to stdout.
35
36
  */
36
37
  async function handleQuery(args) {
37
- const { thread, provider, metadata, verbose, config, outputFormat, effort, promptFile, vars, rest } = (0, parse_common_flags_1.parseCommonFlags)(args);
38
+ const { thread, provider, metadata, verbose, config, outputFormat, effort, promptFile, overdrive, vars, out, rest } = (0, parse_common_flags_1.parseCommonFlags)(args);
38
39
  const { agentRef: parsedAgentRef, message } = (0, parse_agent_message_1.parseAgentMessage)(rest);
39
40
  // No @mention → default to @crewx agent (matches cli-bak behaviour)
40
41
  const agentRef = parsedAgentRef || '@crewx';
@@ -62,9 +63,11 @@ async function handleQuery(args) {
62
63
  console.error(' --verbose Debug output mode');
63
64
  console.error(' --config/-c <path> Config file path');
64
65
  console.error(' --output-format <fmt> Output format (json|text|stream-json)');
66
+ console.error(' --out/-o <path> Save result to file (stdout suppressed)');
65
67
  console.error(' --effort <level> Model effort (high|medium|low)');
66
68
  console.error(' -f/--prompt-file <path> Read prompt body from file');
67
69
  console.error(' --var key=value Template variable (repeatable)');
70
+ console.error(' --overdrive Activate overdrive (boost) profile for this request');
68
71
  process.exit(1);
69
72
  }
70
73
  const configPath = config ?? process.env.CREWX_CONFIG ?? 'crewx.yaml';
@@ -83,6 +86,8 @@ async function handleQuery(args) {
83
86
  process.stderr.write(`📄 Output-format: ${outputFormat}\n`);
84
87
  if (effort)
85
88
  process.stderr.write(`⚡ Effort: ${effort}\n`);
89
+ if (overdrive)
90
+ process.stderr.write(`🚀 Overdrive: ON\n`);
86
91
  process.stderr.write('─'.repeat(60) + '\n');
87
92
  }
88
93
  let parsedMetadata = {};
@@ -99,6 +104,7 @@ async function handleQuery(args) {
99
104
  const result = await crewx.query(agentRef, finalMessage, {
100
105
  provider,
101
106
  effort: effort || undefined,
107
+ overdrive: overdrive || undefined,
102
108
  threadId: thread,
103
109
  metadata: Object.keys(parsedMetadata).length > 0 ? parsedMetadata : undefined,
104
110
  vars: Object.keys(vars).length > 0 ? vars : undefined,
@@ -107,6 +113,7 @@ async function handleQuery(args) {
107
113
  if (!result.ok) {
108
114
  const errMsg = result.error?.message ?? 'Query failed';
109
115
  console.error(errMsg);
116
+ (0, write_output_1.appendError)(out, errMsg);
110
117
  exitCode = 1;
111
118
  }
112
119
  else {
@@ -119,7 +126,7 @@ async function handleQuery(args) {
119
126
  process.stderr.write('\n📄 Response:\n');
120
127
  process.stderr.write('─'.repeat(40) + '\n');
121
128
  }
122
- console.log(result.data);
129
+ (0, write_output_1.writeResult)(out, result.data);
123
130
  if (verbose) {
124
131
  process.stderr.write('\n✅ Query completed successfully\n');
125
132
  }
@@ -128,6 +135,7 @@ async function handleQuery(args) {
128
135
  catch (err) {
129
136
  const errMsg = err instanceof Error ? err.message : String(err);
130
137
  console.error(`Error: ${errMsg}`);
138
+ (0, write_output_1.appendError)(out, `Error: ${errMsg}`);
131
139
  exitCode = 1;
132
140
  }
133
141
  finally {
@@ -43,18 +43,23 @@ function buildRetryPrefix(originalTaskId) {
43
43
  * the string when present and non-empty, otherwise `undefined` so the
44
44
  * agent's configured provider default is used.
45
45
  */
46
- function extractProvider(metadata) {
46
+ function parseTaskMetadata(metadata) {
47
47
  if (!metadata)
48
- return undefined;
48
+ return {};
49
49
  try {
50
50
  const parsed = JSON.parse(metadata);
51
- const provider = parsed.provider;
52
- return typeof provider === 'string' && provider.length > 0 ? provider : undefined;
51
+ return parsed && typeof parsed === 'object' && !Array.isArray(parsed)
52
+ ? parsed
53
+ : {};
53
54
  }
54
55
  catch {
55
- return undefined;
56
+ return {};
56
57
  }
57
58
  }
59
+ function extractProvider(metadata) {
60
+ const provider = metadata.provider;
61
+ return typeof provider === 'string' && provider.length > 0 ? provider : undefined;
62
+ }
58
63
  async function handleRestart(args) {
59
64
  const verbose = args.includes('--verbose');
60
65
  const taskId = args.find((a) => !a.startsWith('--'));
@@ -96,7 +101,10 @@ async function handleRestart(args) {
96
101
  // provider: recovered from metadata.provider (SDK-persisted). Falls back to
97
102
  // the agent's configured provider when absent. This preserves a
98
103
  // `crewx x --provider ...` choice across restart.
99
- const provider = extractProvider(original.metadata);
104
+ const originalMetadata = parseTaskMetadata(original.metadata);
105
+ const provider = extractProvider(originalMetadata);
106
+ const originalWasOverdrive = originalMetadata.overdrive === true;
107
+ const originalOverdriveMode = originalMetadata.overdriveState === 'latch' ? 'latch' : 'count';
100
108
  (0, sdk_1.setAuditVerbose)(verbose);
101
109
  if (verbose) {
102
110
  process.stderr.write(`🔁 Restart: ${taskId} → ${newTaskId}\n`);
@@ -111,10 +119,16 @@ async function handleRestart(args) {
111
119
  threadId: original.thread_id ?? undefined,
112
120
  model,
113
121
  provider,
122
+ ...(originalWasOverdrive ? { overdrive: { mode: originalOverdriveMode } } : {}),
114
123
  metadata: {
115
124
  restartedFromTaskId: taskId,
116
125
  // Back-compat / search convenience: mirror the Web UI metadata key.
117
126
  retriedFromTaskId: taskId,
127
+ ...(originalWasOverdrive ? {
128
+ overdrive: true,
129
+ overdriveState: originalOverdriveMode,
130
+ overdriveMode: mode,
131
+ } : {}),
118
132
  },
119
133
  trace: (0, inherited_trace_1.readInheritedTrace)(),
120
134
  };
@@ -0,0 +1,3 @@
1
+ export declare function writeResult(out: string | undefined, data: string): void;
2
+ /** On failure: if out is specified, append to file (avoid empty file). stderr is kept by the caller. */
3
+ export declare function appendError(out: string | undefined, message: string): void;
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.writeResult = writeResult;
4
+ exports.appendError = appendError;
5
+ /**
6
+ * EPIPE-safe result output.
7
+ * - out specified: write success result to file (sync, no stream EPIPE)
8
+ * - out not specified: fall back to existing stdout (console.log)
9
+ */
10
+ const fs_1 = require("fs");
11
+ function writeResult(out, data) {
12
+ if (out) {
13
+ (0, fs_1.writeFileSync)(out, data, 'utf8');
14
+ }
15
+ else {
16
+ console.log(data);
17
+ }
18
+ }
19
+ /** On failure: if out is specified, append to file (avoid empty file). stderr is kept by the caller. */
20
+ function appendError(out, message) {
21
+ if (out) {
22
+ (0, fs_1.appendFileSync)(out, message.endsWith('\n') ? message : message + '\n', 'utf8');
23
+ }
24
+ }
package/dist/main.js CHANGED
@@ -46,8 +46,14 @@ Object.defineProperty(exports, "__esModule", { value: true });
46
46
  // ─── P0-1: Env Bootstrap ─────────────────────────────────────────────────────
47
47
  // Must run before any other code that might use process.env.CREWX_CLI.
48
48
  const env_defaults_1 = require("./utils/env-defaults");
49
+ const sdk_1 = require("@crewx/sdk");
49
50
  process.env.CREWX_CLI ??= (0, env_defaults_1.resolveCrewxCli)();
50
51
  process.env.CREWX_WORKSPACE ??= (0, env_defaults_1.resolveCrewxWorkspace)();
52
+ // ─── Pricing remote override (WI-20260701-002) ───────────────────────────────
53
+ // Best-effort, non-blocking: fetch remote model registry so new models get
54
+ // accurate pricing without a CLI republish. Falls back to bundled table on
55
+ // failure / offline. Browser entry does not call this automatically.
56
+ void (0, sdk_1.initPricingRemote)();
51
57
  // ─── Command Imports ──────────────────────────────────────────────────────────
52
58
  const query_1 = require("./commands/query");
53
59
  const execute_1 = require("./commands/execute");
@@ -256,6 +262,7 @@ Query / Execute:
256
262
  --verbose Debug output mode (default: raw response only)
257
263
  --config/-c <path> Config file path (default: CREWX_CONFIG or crewx.yaml)
258
264
  --output-format <fmt> Output format (json|text|stream-json)
265
+ --out/-o <path> Save result to file (stdout suppressed)
259
266
  --effort <level> Model effort (high|medium|low)
260
267
  -f/--prompt-file <path> Read task body from file (bypasses argv length limits)
261
268
  --var key=value Template variable (repeatable). Accessible as {{key}} in agent prompt.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crewx/cli",
3
- "version": "0.9.0-rc.2",
3
+ "version": "0.9.0-rc.20",
4
4
  "license": "UNLICENSED",
5
5
  "engines": {
6
6
  "node": ">=20.19.0"
@@ -24,17 +24,17 @@
24
24
  "@crewx/adapter-slack": "0.1.4",
25
25
  "better-sqlite3": "*",
26
26
  "isomorphic-git": "1.37.1",
27
+ "@crewx/sdk": "0.9.0-rc.20",
27
28
  "@crewx/memory": "0.1.23",
28
29
  "@crewx/wbs": "0.1.10",
29
- "@crewx/doc": "0.1.9",
30
- "@crewx/cron": "0.1.10",
31
- "@crewx/sdk": "0.9.0-rc.2",
32
30
  "@crewx/search": "0.1.10",
33
- "@crewx/workflow": "0.3.22",
34
- "@crewx/wi": "0.1.10",
31
+ "@crewx/doc": "0.1.9",
32
+ "@crewx/workflow": "0.3.22-rc.66",
33
+ "@crewx/wi": "0.1.10-rc.40",
35
34
  "@crewx/skill": "0.1.20",
36
- "@crewx/shared": "0.0.6",
37
- "@crewx/chromex": "0.1.0"
35
+ "@crewx/cron": "0.1.10",
36
+ "@crewx/chromex": "0.1.0",
37
+ "@crewx/shared": "0.0.6"
38
38
  },
39
39
  "devDependencies": {
40
40
  "@types/better-sqlite3": "*",