@crewx/cli 0.9.0-rc.7 → 0.9.0-rc.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.
|
@@ -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());
|
package/dist/commands/ps.js
CHANGED
|
@@ -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
|
-
|
|
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)`);
|
package/dist/commands/restart.js
CHANGED
|
@@ -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
|
|
46
|
+
function parseTaskMetadata(metadata) {
|
|
47
47
|
if (!metadata)
|
|
48
|
-
return
|
|
48
|
+
return {};
|
|
49
49
|
try {
|
|
50
50
|
const parsed = JSON.parse(metadata);
|
|
51
|
-
|
|
52
|
-
|
|
51
|
+
return parsed && typeof parsed === 'object' && !Array.isArray(parsed)
|
|
52
|
+
? parsed
|
|
53
|
+
: {};
|
|
53
54
|
}
|
|
54
55
|
catch {
|
|
55
|
-
return
|
|
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
|
|
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
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@crewx/cli",
|
|
3
|
-
"version": "0.9.0-rc.
|
|
3
|
+
"version": "0.9.0-rc.8",
|
|
4
4
|
"license": "UNLICENSED",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": ">=20.19.0"
|
|
@@ -24,16 +24,16 @@
|
|
|
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.
|
|
28
|
-
"@crewx/search": "0.1.10",
|
|
27
|
+
"@crewx/sdk": "0.9.0-rc.8",
|
|
29
28
|
"@crewx/doc": "0.1.9",
|
|
30
|
-
"@crewx/
|
|
31
|
-
"@crewx/
|
|
29
|
+
"@crewx/search": "0.1.10",
|
|
30
|
+
"@crewx/workflow": "0.3.22-rc.54",
|
|
32
31
|
"@crewx/cron": "0.1.10",
|
|
33
32
|
"@crewx/skill": "0.1.20",
|
|
33
|
+
"@crewx/chromex": "0.1.0",
|
|
34
|
+
"@crewx/wbs": "0.1.10",
|
|
34
35
|
"@crewx/memory": "0.1.23",
|
|
35
36
|
"@crewx/wi": "0.1.10",
|
|
36
|
-
"@crewx/chromex": "0.1.0",
|
|
37
37
|
"@crewx/shared": "0.0.6"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|