@crewx/cli 0.9.0-rc.1 → 0.9.0-rc.100
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/bootstrap/codex-writable-roots.d.ts +58 -0
- package/dist/bootstrap/codex-writable-roots.js +113 -0
- package/dist/bootstrap/crewx-cli.js +8 -1
- package/dist/builtin.js +1 -0
- package/dist/commands/agent.js +0 -58
- package/dist/commands/cal.d.ts +15 -0
- package/dist/commands/cal.js +44 -0
- package/dist/commands/db.d.ts +2 -0
- package/dist/commands/db.js +381 -1
- package/dist/commands/detached.d.ts +65 -0
- package/dist/commands/detached.js +507 -0
- package/dist/commands/doctor.d.ts +53 -0
- package/dist/commands/doctor.js +391 -21
- package/dist/commands/emit-trailer.d.ts +25 -0
- package/dist/commands/emit-trailer.js +33 -0
- package/dist/commands/execute.d.ts +7 -4
- package/dist/commands/execute.js +121 -16
- package/dist/commands/hook/command-marker.d.ts +2 -0
- package/dist/commands/hook/command-marker.js +5 -0
- package/dist/commands/hook/install.d.ts +0 -1
- package/dist/commands/hook/install.js +60 -63
- package/dist/commands/hook/status.js +3 -3
- package/dist/commands/hook/uninstall.js +2 -2
- package/dist/commands/init.js +22 -1
- package/dist/commands/kill.d.ts +2 -8
- package/dist/commands/kill.js +6 -47
- package/dist/commands/log.js +4 -3
- package/dist/commands/parse-common-flags.d.ts +8 -2
- package/dist/commands/parse-common-flags.js +12 -3
- package/dist/commands/ps.d.ts +2 -2
- package/dist/commands/ps.js +69 -11
- package/dist/commands/publish.d.ts +1 -0
- package/dist/commands/publish.js +303 -0
- package/dist/commands/query.d.ts +5 -1
- package/dist/commands/query.js +114 -11
- package/dist/commands/registry.js +4 -2
- package/dist/commands/restart.js +20 -6
- package/dist/commands/result.d.ts +8 -3
- package/dist/commands/result.js +139 -7
- package/dist/commands/shortcut.d.ts +1 -0
- package/dist/commands/shortcut.js +267 -0
- package/dist/commands/slack.js +2 -1
- package/dist/commands/stop.d.ts +8 -0
- package/dist/commands/stop.js +119 -0
- package/dist/commands/write-output.d.ts +3 -0
- package/dist/commands/write-output.js +24 -0
- package/dist/logging.d.ts +1 -1
- package/dist/logging.js +3 -2
- package/dist/main.d.ts +3 -2
- package/dist/main.js +70 -14
- package/dist/utils/env-defaults.d.ts +2 -5
- package/dist/utils/env-defaults.js +10 -5
- package/dist/utils/inherited-trace.d.ts +24 -0
- package/dist/utils/inherited-trace.js +40 -0
- package/dist/utils/sdk-compat.d.ts +24 -0
- package/dist/utils/sdk-compat.js +120 -0
- package/package.json +13 -10
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.handleShortcut = handleShortcut;
|
|
4
|
+
/**
|
|
5
|
+
* crewx shortcut handler — thin CLI wrapper over @crewx/sdk/desktop.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* crewx shortcut status [--json]
|
|
9
|
+
* crewx shortcut install [--force] [--json]
|
|
10
|
+
* crewx shortcut uninstall [--json]
|
|
11
|
+
*
|
|
12
|
+
* All win32/foreign/superseded/force judgment lives in @crewx/sdk/desktop.
|
|
13
|
+
* This module only parses argv, forwards options, and renders the result.
|
|
14
|
+
*/
|
|
15
|
+
const desktop_1 = require("@crewx/sdk/desktop");
|
|
16
|
+
const parse_common_flags_1 = require("./parse-common-flags");
|
|
17
|
+
const NOT_WINDOWS_MESSAGE = 'NOT_WINDOWS: Desktop shortcuts are only supported on Windows.';
|
|
18
|
+
function parseShortcutFlags(args, allowForce) {
|
|
19
|
+
let json = false;
|
|
20
|
+
let force = false;
|
|
21
|
+
for (const arg of args) {
|
|
22
|
+
if (arg === '--json') {
|
|
23
|
+
json = true;
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
if (arg === '--force' && allowForce) {
|
|
27
|
+
force = true;
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
throw new parse_common_flags_1.UnknownOptionError(`Unknown option: ${arg}`);
|
|
31
|
+
}
|
|
32
|
+
return { json, force };
|
|
33
|
+
}
|
|
34
|
+
/** Forwarded verbatim to stderr as each SDK phase starts — never buffered. */
|
|
35
|
+
function onProgress(event) {
|
|
36
|
+
process.stderr.write(`[shortcut] ${event.message}\n`);
|
|
37
|
+
}
|
|
38
|
+
function printJson(payload) {
|
|
39
|
+
console.log(JSON.stringify(payload));
|
|
40
|
+
}
|
|
41
|
+
/** Prints the error and exits 1. Never returns (matches `process.exit`'s `never` type). */
|
|
42
|
+
function failWithError(err, json) {
|
|
43
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
44
|
+
if (json) {
|
|
45
|
+
printJson({ success: false, error: message });
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
console.error(`✗ ${message}`);
|
|
49
|
+
}
|
|
50
|
+
process.exit(1);
|
|
51
|
+
}
|
|
52
|
+
/** The SDK returns `supported:false` (no throw) for install/uninstall on non-Windows. */
|
|
53
|
+
function failNotWindows(json) {
|
|
54
|
+
if (json) {
|
|
55
|
+
printJson({ success: false, supported: false, state: 'unsupported', error: NOT_WINDOWS_MESSAGE });
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
console.error(`✗ ${NOT_WINDOWS_MESSAGE}`);
|
|
59
|
+
}
|
|
60
|
+
process.exit(1);
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Detects the two ways a resolved (non-thrown) install result must NOT be
|
|
64
|
+
* read as success: a `superseded` shortcut (checked via both `state` and the
|
|
65
|
+
* `errors[]` entry, since either alone could otherwise be missed) and an
|
|
66
|
+
* unconfirmed global install (`installedGlobal:false` with
|
|
67
|
+
* `GLOBAL_INSTALL_UNCONFIRMED` in errors — npm exited 0 but re-detection
|
|
68
|
+
* could not confirm it).
|
|
69
|
+
*/
|
|
70
|
+
function installFailureReason(result) {
|
|
71
|
+
const supersededError = result.errors.find((e) => e.startsWith('SUPERSEDED:'));
|
|
72
|
+
if (result.state === 'superseded' || supersededError) {
|
|
73
|
+
return supersededError ?? 'Installed shortcut belongs to a newer CrewX version; refusing to modify it.';
|
|
74
|
+
}
|
|
75
|
+
const unconfirmedError = result.errors.find((e) => e.startsWith('GLOBAL_INSTALL_UNCONFIRMED:'));
|
|
76
|
+
if (!result.installedGlobal && unconfirmedError) {
|
|
77
|
+
return unconfirmedError;
|
|
78
|
+
}
|
|
79
|
+
if (result.errors.length > 0) {
|
|
80
|
+
return result.errors[0];
|
|
81
|
+
}
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Only these two re-detected states mean the Desktop truly has no CrewX
|
|
86
|
+
* shortcut left. Everything else — `foreign`/`superseded` (SDK refused to
|
|
87
|
+
* touch it) and `installed`/`stale` (the `.lnk` unlink threw and was
|
|
88
|
+
* swallowed as "already gone" — install.ts `uninstallShortcut`) — means the
|
|
89
|
+
* file is still on disk. A success ALLOWLIST is used instead of enumerating
|
|
90
|
+
* failure states so an unlink failure (EPERM/EBUSY/locked/AV hook) that
|
|
91
|
+
* re-detects back to `installed` fails closed by construction, rather than
|
|
92
|
+
* requiring every new failure path to be added to an exclusion list.
|
|
93
|
+
*/
|
|
94
|
+
const UNINSTALL_SUCCESS_STATES = new Set(['declined', 'not-installed']);
|
|
95
|
+
function uninstallFailureReason(result) {
|
|
96
|
+
if (UNINSTALL_SUCCESS_STATES.has(result.state)) {
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
const supersededError = result.errors.find((e) => e.startsWith('SUPERSEDED:'));
|
|
100
|
+
if (result.state === 'superseded' || supersededError) {
|
|
101
|
+
return supersededError ?? 'Installed shortcut belongs to a newer CrewX version; nothing was removed.';
|
|
102
|
+
}
|
|
103
|
+
if (result.state === 'foreign') {
|
|
104
|
+
const nsisError = result.errors.find((e) => e.startsWith('NSIS_PRESENT:'));
|
|
105
|
+
return nsisError ?? 'Existing Desktop shortcut is not managed by CrewX; nothing was removed.';
|
|
106
|
+
}
|
|
107
|
+
if (result.errors.length > 0) {
|
|
108
|
+
return result.errors[0];
|
|
109
|
+
}
|
|
110
|
+
return `CrewX desktop shortcut could not be removed (state: ${result.state}).`;
|
|
111
|
+
}
|
|
112
|
+
function printStatusHuman(status) {
|
|
113
|
+
switch (status.state) {
|
|
114
|
+
case 'unsupported':
|
|
115
|
+
console.log('Desktop shortcuts are only supported on Windows.');
|
|
116
|
+
break;
|
|
117
|
+
case 'not-installed':
|
|
118
|
+
console.log('No CrewX desktop shortcut installed yet.');
|
|
119
|
+
console.log('Run `crewx shortcut install` to create one.');
|
|
120
|
+
break;
|
|
121
|
+
case 'declined':
|
|
122
|
+
console.log('CrewX desktop shortcut setup was previously dismissed.');
|
|
123
|
+
console.log('Run `crewx shortcut install` to create one.');
|
|
124
|
+
break;
|
|
125
|
+
case 'installed':
|
|
126
|
+
console.log('✓ CrewX desktop shortcut is installed and up to date.');
|
|
127
|
+
if (status.shortcutPath)
|
|
128
|
+
console.log(` Shortcut: ${status.shortcutPath}`);
|
|
129
|
+
break;
|
|
130
|
+
case 'stale':
|
|
131
|
+
console.log('⚠ CrewX desktop shortcut needs an update.');
|
|
132
|
+
console.log('Run `crewx shortcut install` to refresh it.');
|
|
133
|
+
break;
|
|
134
|
+
case 'foreign':
|
|
135
|
+
console.log('⚠ A Desktop shortcut exists that CrewX does not manage.');
|
|
136
|
+
console.log('Run `crewx shortcut install --force` to replace it.');
|
|
137
|
+
break;
|
|
138
|
+
case 'superseded':
|
|
139
|
+
console.log('⚠ The installed shortcut belongs to a newer CrewX version than the one running.');
|
|
140
|
+
console.log('Update CrewX before touching the shortcut.');
|
|
141
|
+
break;
|
|
142
|
+
}
|
|
143
|
+
for (const e of status.errors) {
|
|
144
|
+
console.log(` note: ${e}`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
async function runStatus(args) {
|
|
148
|
+
const { json } = parseShortcutFlags(args, false);
|
|
149
|
+
let status;
|
|
150
|
+
try {
|
|
151
|
+
status = await (0, desktop_1.getShortcutStatus)();
|
|
152
|
+
}
|
|
153
|
+
catch (err) {
|
|
154
|
+
failWithError(err, json);
|
|
155
|
+
}
|
|
156
|
+
if (json) {
|
|
157
|
+
printJson(status);
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
printStatusHuman(status);
|
|
161
|
+
}
|
|
162
|
+
async function runInstall(args) {
|
|
163
|
+
const { json, force } = parseShortcutFlags(args, true);
|
|
164
|
+
let result;
|
|
165
|
+
try {
|
|
166
|
+
result = await (0, desktop_1.installShortcut)({ force, onProgress });
|
|
167
|
+
}
|
|
168
|
+
catch (err) {
|
|
169
|
+
failWithError(err, json);
|
|
170
|
+
}
|
|
171
|
+
if (!result.supported) {
|
|
172
|
+
failNotWindows(json);
|
|
173
|
+
}
|
|
174
|
+
const failure = installFailureReason(result);
|
|
175
|
+
if (failure) {
|
|
176
|
+
if (json) {
|
|
177
|
+
printJson({ ...result, success: false });
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
console.error(`✗ ${failure}`);
|
|
181
|
+
}
|
|
182
|
+
process.exit(1);
|
|
183
|
+
}
|
|
184
|
+
if (json) {
|
|
185
|
+
printJson({ ...result, success: true });
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
if (result.changed) {
|
|
189
|
+
console.log('✓ CrewX desktop shortcut installed. Double-click "CrewX" on your Desktop to launch it.');
|
|
190
|
+
}
|
|
191
|
+
else {
|
|
192
|
+
console.log('✓ CrewX desktop shortcut is already up to date. Double-click "CrewX" on your Desktop to launch it.');
|
|
193
|
+
}
|
|
194
|
+
if (result.shortcutPath)
|
|
195
|
+
console.log(` Shortcut: ${result.shortcutPath}`);
|
|
196
|
+
}
|
|
197
|
+
async function runUninstall(args) {
|
|
198
|
+
const { json } = parseShortcutFlags(args, false);
|
|
199
|
+
let result;
|
|
200
|
+
try {
|
|
201
|
+
result = await (0, desktop_1.uninstallShortcut)();
|
|
202
|
+
}
|
|
203
|
+
catch (err) {
|
|
204
|
+
failWithError(err, json);
|
|
205
|
+
}
|
|
206
|
+
if (!result.supported) {
|
|
207
|
+
failNotWindows(json);
|
|
208
|
+
}
|
|
209
|
+
const failure = uninstallFailureReason(result);
|
|
210
|
+
if (failure) {
|
|
211
|
+
if (json) {
|
|
212
|
+
printJson({ ...result, success: false });
|
|
213
|
+
}
|
|
214
|
+
else {
|
|
215
|
+
console.error(`✗ ${failure}`);
|
|
216
|
+
for (const e of result.errors) {
|
|
217
|
+
if (e !== failure)
|
|
218
|
+
console.error(` note: ${e}`);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
process.exit(1);
|
|
222
|
+
}
|
|
223
|
+
if (json) {
|
|
224
|
+
printJson({ ...result, success: true });
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
console.log('✓ CrewX desktop shortcut removed.');
|
|
228
|
+
}
|
|
229
|
+
function printHelp() {
|
|
230
|
+
console.log(`
|
|
231
|
+
crewx shortcut — manage the Windows Desktop shortcut for CrewX
|
|
232
|
+
|
|
233
|
+
Usage:
|
|
234
|
+
crewx shortcut status [--json]
|
|
235
|
+
crewx shortcut install [--force] [--json]
|
|
236
|
+
crewx shortcut uninstall [--json]
|
|
237
|
+
|
|
238
|
+
Options:
|
|
239
|
+
--json Print machine-readable JSON to stdout (progress/log always go to stderr)
|
|
240
|
+
--force (install only) Overwrite a Desktop shortcut CrewX does not manage.
|
|
241
|
+
Never overwrites a shortcut created by a newer CrewX version.
|
|
242
|
+
|
|
243
|
+
Notes:
|
|
244
|
+
Windows only. On macOS/Linux, \`status\` reports state "unsupported" (exit 0);
|
|
245
|
+
\`install\`/\`uninstall\` exit 1 with no changes made.
|
|
246
|
+
`.trim());
|
|
247
|
+
}
|
|
248
|
+
async function handleShortcut(args) {
|
|
249
|
+
const subcommand = args[0];
|
|
250
|
+
if (!subcommand || subcommand === 'help' || subcommand === '--help' || subcommand === '-h') {
|
|
251
|
+
printHelp();
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
const rest = args.slice(1);
|
|
255
|
+
switch (subcommand) {
|
|
256
|
+
case 'status':
|
|
257
|
+
return runStatus(rest);
|
|
258
|
+
case 'install':
|
|
259
|
+
return runInstall(rest);
|
|
260
|
+
case 'uninstall':
|
|
261
|
+
return runUninstall(rest);
|
|
262
|
+
default:
|
|
263
|
+
console.error(`Unknown shortcut subcommand: ${subcommand}`);
|
|
264
|
+
console.error('Run `crewx shortcut help` for usage.');
|
|
265
|
+
process.exit(1);
|
|
266
|
+
}
|
|
267
|
+
}
|
package/dist/commands/slack.js
CHANGED
|
@@ -44,6 +44,7 @@ exports.handleSlack = handleSlack;
|
|
|
44
44
|
const fs = __importStar(require("fs"));
|
|
45
45
|
const path = __importStar(require("path"));
|
|
46
46
|
const https = __importStar(require("https"));
|
|
47
|
+
const sdk_1 = require("@crewx/sdk");
|
|
47
48
|
const adapter_slack_1 = require("@crewx/adapter-slack");
|
|
48
49
|
const crewx_cli_1 = require("../bootstrap/crewx-cli");
|
|
49
50
|
const markdown_1 = require("../slack/markdown");
|
|
@@ -186,7 +187,7 @@ async function handleListFiles(threadId) {
|
|
|
186
187
|
console.log(` 📎 ${file.fileName}`);
|
|
187
188
|
console.log(` Size: ${formatFileSize(file.fileSize)}`);
|
|
188
189
|
console.log(` Path: ${file.filePath}`);
|
|
189
|
-
console.log(` Downloaded: ${file.downloadedAt
|
|
190
|
+
console.log(` Downloaded: ${(0, sdk_1.formatDisplayTimestamp)(file.downloadedAt)}`);
|
|
190
191
|
console.log('');
|
|
191
192
|
}
|
|
192
193
|
console.log(`Total: ${files.length} file${files.length > 1 ? 's' : ''}\n`);
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stop a running task through the SDK's cooperative-stop pipeline.
|
|
3
|
+
*
|
|
4
|
+
* `kill` is kept as a compatibility alias; both entry points use this same
|
|
5
|
+
* handler so server, CLI, and future adapters share one stop contract.
|
|
6
|
+
*/
|
|
7
|
+
/** Handle `crewx stop` and the compatibility `crewx kill` alias. */
|
|
8
|
+
export declare function handleStop(args: string[], invokedAs?: 'stop' | 'kill'): Promise<void>;
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Stop a running task through the SDK's cooperative-stop pipeline.
|
|
4
|
+
*
|
|
5
|
+
* `kill` is kept as a compatibility alias; both entry points use this same
|
|
6
|
+
* handler so server, CLI, and future adapters share one stop contract.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.handleStop = handleStop;
|
|
10
|
+
const sdk_1 = require("@crewx/sdk");
|
|
11
|
+
const repository_1 = require("@crewx/sdk/repository");
|
|
12
|
+
const cal_1 = require("./cal");
|
|
13
|
+
function parseGrace(args) {
|
|
14
|
+
const flag = args.find((arg) => arg === '--grace' || arg.startsWith('--grace='));
|
|
15
|
+
if (!flag)
|
|
16
|
+
return { graceMs: 5_000 };
|
|
17
|
+
const raw = flag === '--grace'
|
|
18
|
+
? args[args.indexOf(flag) + 1]
|
|
19
|
+
: flag.slice('--grace='.length);
|
|
20
|
+
const value = Number(raw);
|
|
21
|
+
if (!raw || !Number.isFinite(value) || value < 0) {
|
|
22
|
+
return { graceMs: 5_000, error: `Invalid --grace value: ${raw ?? '(missing)'}` };
|
|
23
|
+
}
|
|
24
|
+
return { graceMs: Math.trunc(value) };
|
|
25
|
+
}
|
|
26
|
+
function taskIdFromArgs(args) {
|
|
27
|
+
return args.find((arg, index) => {
|
|
28
|
+
if (arg === '--grace')
|
|
29
|
+
return false;
|
|
30
|
+
if (index > 0 && args[index - 1] === '--grace')
|
|
31
|
+
return false;
|
|
32
|
+
return !arg.startsWith('--');
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
function writeUsage() {
|
|
36
|
+
process.stderr.write('Error: Please provide a task ID or use --all to stop all running tasks.\n');
|
|
37
|
+
process.stderr.write('\nUsage:\n');
|
|
38
|
+
process.stderr.write(' crewx stop <task-id> Stop a running task\n');
|
|
39
|
+
process.stderr.write(' crewx stop --all Stop all running tasks\n');
|
|
40
|
+
process.stderr.write(' crewx stop <task-id> --grace=<ms>\n');
|
|
41
|
+
}
|
|
42
|
+
function formatGrace(graceMs) {
|
|
43
|
+
return `${graceMs / 1000}s`;
|
|
44
|
+
}
|
|
45
|
+
function reportResult(taskId, result, elapsedMs, graceMs) {
|
|
46
|
+
if (result.status === 'stopped') {
|
|
47
|
+
if (result.method === 'forced') {
|
|
48
|
+
process.stdout.write(`Stopped ${taskId} (forced after ${formatGrace(graceMs)} grace)\n`);
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
process.stdout.write(`Stopped ${taskId} (cooperative, ${(elapsedMs / 1000).toFixed(1)}s)\n`);
|
|
52
|
+
}
|
|
53
|
+
return 0;
|
|
54
|
+
}
|
|
55
|
+
if (result.status === 'not_running') {
|
|
56
|
+
process.stdout.write(`Task ${taskId} is not running (${result.finalStatus ?? 'unknown'})\n`);
|
|
57
|
+
return 0;
|
|
58
|
+
}
|
|
59
|
+
if (result.status === 'already_requested') {
|
|
60
|
+
process.stderr.write(`Task ${taskId} already has a stop request; it remains ${result.finalStatus ?? 'running'}.\n`);
|
|
61
|
+
return 2;
|
|
62
|
+
}
|
|
63
|
+
if (result.status === 'ownership_unverified') {
|
|
64
|
+
const pid = result.pid ? ` (pid ${result.pid})` : '';
|
|
65
|
+
process.stderr.write(`Could not verify the process owner of ${taskId}; not terminated${pid}. ` +
|
|
66
|
+
'Row stays running — the reaper will settle it.\n');
|
|
67
|
+
return 2;
|
|
68
|
+
}
|
|
69
|
+
process.stderr.write(`Could not stop ${taskId}: ${result.error ?? 'unknown error'}\n`);
|
|
70
|
+
return 1;
|
|
71
|
+
}
|
|
72
|
+
/** Handle `crewx stop` and the compatibility `crewx kill` alias. */
|
|
73
|
+
async function handleStop(args, invokedAs = 'stop') {
|
|
74
|
+
if (invokedAs === 'kill')
|
|
75
|
+
process.stderr.write('kill is an alias of stop\n');
|
|
76
|
+
const stopAll = args.includes('--all');
|
|
77
|
+
const { graceMs, error } = parseGrace(args);
|
|
78
|
+
if (error) {
|
|
79
|
+
process.stderr.write(`Error: ${error}\n`);
|
|
80
|
+
process.exitCode = 1;
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
const taskId = taskIdFromArgs(args);
|
|
84
|
+
if (!taskId && !stopAll) {
|
|
85
|
+
writeUsage();
|
|
86
|
+
process.exitCode = 1;
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
const repo = new repository_1.TaskRepository();
|
|
90
|
+
const stopOne = async (id, withFooter) => {
|
|
91
|
+
const startedAt = Date.now();
|
|
92
|
+
const result = await (0, sdk_1.stopTask)(repo, id, { graceMs });
|
|
93
|
+
const exitCode = reportResult(id, result, Date.now() - startedAt, graceMs);
|
|
94
|
+
if (withFooter && (result.status === 'stopped' || result.status === 'not_running')) {
|
|
95
|
+
(0, cal_1.writeCalFooter)({
|
|
96
|
+
commandId: 'crewx.stop.done',
|
|
97
|
+
params: { taskId: id },
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
return exitCode;
|
|
101
|
+
};
|
|
102
|
+
if (stopAll) {
|
|
103
|
+
const running = repo.getRunningTasks();
|
|
104
|
+
if (running.length === 0) {
|
|
105
|
+
process.stdout.write('No running tasks found.\n');
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
let exitCode = 0;
|
|
109
|
+
for (const task of running) {
|
|
110
|
+
exitCode = Math.max(exitCode, await stopOne(task.id, false));
|
|
111
|
+
}
|
|
112
|
+
if (exitCode > 0)
|
|
113
|
+
process.exitCode = exitCode;
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
const exitCode = await stopOne(taskId, true);
|
|
117
|
+
if (exitCode > 0)
|
|
118
|
+
process.exitCode = exitCode;
|
|
119
|
+
}
|
|
@@ -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/logging.d.ts
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
*
|
|
10
10
|
* Format mirrors cli-bak TaskManagementService for backward compatibility.
|
|
11
11
|
*/
|
|
12
|
-
import type
|
|
12
|
+
import { type Crewx } from '@crewx/sdk';
|
|
13
13
|
/**
|
|
14
14
|
* Attach file-based logging to a Crewx instance.
|
|
15
15
|
* Subscribes to task:start and task:end events and writes log files.
|
package/dist/logging.js
CHANGED
|
@@ -14,6 +14,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
14
14
|
exports.attachFileLogger = attachFileLogger;
|
|
15
15
|
const fs_1 = require("fs");
|
|
16
16
|
const path_1 = require("path");
|
|
17
|
+
const sdk_1 = require("@crewx/sdk");
|
|
17
18
|
const CREWX_VERSION = '0.9.0-alpha.1';
|
|
18
19
|
/** Format Date as YYYYMMDDTHHmmss (local time) */
|
|
19
20
|
function formatTimestamp(date) {
|
|
@@ -52,7 +53,7 @@ function attachFileLogger(crewx, workspaceRoot) {
|
|
|
52
53
|
`CrewX Version: ${CREWX_VERSION}\n` +
|
|
53
54
|
`Mode: ${event.mode}\n` +
|
|
54
55
|
`Agent: ${event.agentRef}\n` +
|
|
55
|
-
`Started: ${event.timestamp
|
|
56
|
+
`Started: ${(0, sdk_1.formatDisplayTimestamp)(event.timestamp)}\n` +
|
|
56
57
|
`Message: ${event.message}\n` +
|
|
57
58
|
`\n`;
|
|
58
59
|
(0, fs_1.writeFileSync)(logFile, header, { encoding: 'utf8', mode: 0o600 });
|
|
@@ -66,7 +67,7 @@ function attachFileLogger(crewx, workspaceRoot) {
|
|
|
66
67
|
const logFile = logFiles.get(event.traceId);
|
|
67
68
|
if (!logFile)
|
|
68
69
|
return;
|
|
69
|
-
const ts = new Date()
|
|
70
|
+
const ts = (0, sdk_1.formatDisplayTimestamp)(new Date());
|
|
70
71
|
const status = event.error
|
|
71
72
|
? `failed: ${event.error.message}`
|
|
72
73
|
: 'completed successfully';
|
package/dist/main.d.ts
CHANGED
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
*
|
|
6
6
|
* Boot sequence:
|
|
7
7
|
* 1. Inject CREWX_CLI / CREWX_WORKSPACE env defaults (must be first)
|
|
8
|
-
* 2.
|
|
9
|
-
* 3.
|
|
8
|
+
* 2. Load workspace and ~/.crewx .env files (does not override process.env)
|
|
9
|
+
* 3. Parse command
|
|
10
|
+
* 4. Dispatch to handler
|
|
10
11
|
*/
|
|
11
12
|
export {};
|
package/dist/main.js
CHANGED
|
@@ -6,8 +6,9 @@
|
|
|
6
6
|
*
|
|
7
7
|
* Boot sequence:
|
|
8
8
|
* 1. Inject CREWX_CLI / CREWX_WORKSPACE env defaults (must be first)
|
|
9
|
-
* 2.
|
|
10
|
-
* 3.
|
|
9
|
+
* 2. Load workspace and ~/.crewx .env files (does not override process.env)
|
|
10
|
+
* 3. Parse command
|
|
11
|
+
* 4. Dispatch to handler
|
|
11
12
|
*/
|
|
12
13
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
13
14
|
if (k2 === undefined) k2 = k;
|
|
@@ -45,20 +46,35 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
45
46
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
46
47
|
// ─── P0-1: Env Bootstrap ─────────────────────────────────────────────────────
|
|
47
48
|
// Must run before any other code that might use process.env.CREWX_CLI.
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
|
|
49
|
+
const sdk_compat_1 = require("./utils/sdk-compat");
|
|
50
|
+
const account_1 = require("@crewx/sdk/account");
|
|
51
|
+
const inherited_trace_1 = require("./utils/inherited-trace");
|
|
52
|
+
process.env.CREWX_CLI ??= sdk_compat_1.sdkCompat.resolveCrewxCli();
|
|
53
|
+
process.env.CREWX_WORKSPACE ??= sdk_compat_1.sdkCompat.resolveCrewxWorkspace();
|
|
54
|
+
// Load workspace .env before ~/.crewx/.env. The SDK-owned loader keeps CLI and
|
|
55
|
+
// server bootstrap on the same process > workspace > home precedence contract.
|
|
56
|
+
(0, account_1.loadCrewxEnvFiles)();
|
|
57
|
+
// Receipt runner identity is a one-hop capability. Consume it before any
|
|
58
|
+
// command/provider code can spawn a child process.
|
|
59
|
+
(0, inherited_trace_1.consumeInheritedRunnerIdentity)();
|
|
60
|
+
// ─── Pricing remote override (WI-20260701-002) ───────────────────────────────
|
|
61
|
+
// Best-effort, non-blocking: fetch remote model registry so new models get
|
|
62
|
+
// accurate pricing without a CLI republish. Falls back to bundled table on
|
|
63
|
+
// failure / offline. Browser entry does not call this automatically.
|
|
64
|
+
sdk_compat_1.sdkCompat.initPricingRemote();
|
|
51
65
|
// ─── Command Imports ──────────────────────────────────────────────────────────
|
|
52
66
|
const query_1 = require("./commands/query");
|
|
53
67
|
const execute_1 = require("./commands/execute");
|
|
54
68
|
const agent_1 = require("./commands/agent");
|
|
55
69
|
const ps_1 = require("./commands/ps");
|
|
56
|
-
const
|
|
70
|
+
const stop_1 = require("./commands/stop");
|
|
57
71
|
const result_1 = require("./commands/result");
|
|
58
72
|
const restart_1 = require("./commands/restart");
|
|
59
73
|
const log_1 = require("./commands/log");
|
|
60
74
|
const doctor_1 = require("./commands/doctor");
|
|
61
75
|
const init_1 = require("./commands/init");
|
|
76
|
+
const shortcut_1 = require("./commands/shortcut");
|
|
77
|
+
const publish_1 = require("./commands/publish");
|
|
62
78
|
const builtin_1 = require("./builtin");
|
|
63
79
|
const slack_1 = require("./commands/slack");
|
|
64
80
|
const install_1 = require("./commands/hook/install");
|
|
@@ -69,6 +85,7 @@ const db_1 = require("./commands/db");
|
|
|
69
85
|
const parse_common_flags_1 = require("./commands/parse-common-flags");
|
|
70
86
|
const registry_1 = require("./commands/registry");
|
|
71
87
|
const version_1 = require("./utils/version");
|
|
88
|
+
const emit_trailer_1 = require("./commands/emit-trailer");
|
|
72
89
|
/**
|
|
73
90
|
* Dev-only SSOT assertion: bin/cli-commands.js must be the exact union of
|
|
74
91
|
* KNOWN_COMMANDS ∪ BUILTIN_COMMAND_NAMES ∪ NOT_YET_MIGRATED from registry.ts.
|
|
@@ -93,7 +110,7 @@ async function assertSsotParity() {
|
|
|
93
110
|
(extra.length ? ` CLI_SUBCOMMANDS has, registry missing: ${extra.join(', ')}` : ''));
|
|
94
111
|
}
|
|
95
112
|
}
|
|
96
|
-
async function main() {
|
|
113
|
+
async function main(emitState) {
|
|
97
114
|
if (process.env['NODE_ENV'] === 'development') {
|
|
98
115
|
await assertSsotParity();
|
|
99
116
|
}
|
|
@@ -113,11 +130,11 @@ async function main() {
|
|
|
113
130
|
// P0-2: q/x aliases
|
|
114
131
|
case 'q':
|
|
115
132
|
case 'query':
|
|
116
|
-
await (0, query_1.handleQuery)(args.slice(1));
|
|
133
|
+
await (0, query_1.handleQuery)(args.slice(1), command, emitState);
|
|
117
134
|
return;
|
|
118
135
|
case 'x':
|
|
119
136
|
case 'execute':
|
|
120
|
-
await (0, execute_1.handleExecute)(args.slice(1));
|
|
137
|
+
await (0, execute_1.handleExecute)(args.slice(1), command, emitState);
|
|
121
138
|
return;
|
|
122
139
|
case 'agent':
|
|
123
140
|
await (0, agent_1.handleAgent)(args.slice(1));
|
|
@@ -126,9 +143,12 @@ async function main() {
|
|
|
126
143
|
case 'ps':
|
|
127
144
|
await (0, ps_1.handlePs)(args.slice(1));
|
|
128
145
|
return;
|
|
129
|
-
//
|
|
146
|
+
// Task stop (kill is a compatibility alias)
|
|
147
|
+
case 'stop':
|
|
148
|
+
await (0, stop_1.handleStop)(args.slice(1), 'stop');
|
|
149
|
+
return;
|
|
130
150
|
case 'kill':
|
|
131
|
-
await (0,
|
|
151
|
+
await (0, stop_1.handleStop)(args.slice(1), 'kill');
|
|
132
152
|
return;
|
|
133
153
|
// P0-6: result
|
|
134
154
|
case 'result':
|
|
@@ -175,6 +195,14 @@ async function main() {
|
|
|
175
195
|
case 'db':
|
|
176
196
|
await (0, db_1.handleDb)(args.slice(1));
|
|
177
197
|
return;
|
|
198
|
+
// Windows Desktop shortcut: crewx shortcut status|install|uninstall
|
|
199
|
+
case 'shortcut':
|
|
200
|
+
await (0, shortcut_1.handleShortcut)(args.slice(1));
|
|
201
|
+
return;
|
|
202
|
+
// crewx publish [dir] [--dry-run] [--version <semver>] [--json] [--upload]
|
|
203
|
+
case 'publish':
|
|
204
|
+
await (0, publish_1.handlePublish)(args.slice(1));
|
|
205
|
+
return;
|
|
178
206
|
// SDK-009: slack / slack:files
|
|
179
207
|
case 'slack':
|
|
180
208
|
case 'slack:files':
|
|
@@ -256,12 +284,19 @@ Query / Execute:
|
|
|
256
284
|
--verbose Debug output mode (default: raw response only)
|
|
257
285
|
--config/-c <path> Config file path (default: CREWX_CONFIG or crewx.yaml)
|
|
258
286
|
--output-format <fmt> Output format (json|text|stream-json)
|
|
287
|
+
--out/-o <path> Save result to file (stdout suppressed)
|
|
259
288
|
--effort <level> Model effort (high|medium|low)
|
|
260
289
|
-f/--prompt-file <path> Read task body from file (bypasses argv length limits)
|
|
261
290
|
--var key=value Template variable (repeatable). Accessible as {{key}} in agent prompt.
|
|
262
291
|
-- End of flags; remaining tokens treated as message text
|
|
263
292
|
e.g. crewx q "@agent label" -- --flag-in-message
|
|
264
293
|
|
|
294
|
+
q/query/x/execute:
|
|
295
|
+
--detach/--async Persist a receipt, launch a detached runner, and print task-id
|
|
296
|
+
(automatic inside an agent process; q/x stay synchronous elsewhere).
|
|
297
|
+
--wait Force synchronous execution, including inside an agent process.
|
|
298
|
+
e.g. crewx x "@agent label" --detach
|
|
299
|
+
|
|
265
300
|
Agent Management:
|
|
266
301
|
agent ls [options] List configured agents
|
|
267
302
|
--role <value> Filter by role (comma-separated for OR match)
|
|
@@ -271,9 +306,12 @@ Agent Management:
|
|
|
271
306
|
|
|
272
307
|
Task Management:
|
|
273
308
|
ps List running tasks
|
|
274
|
-
|
|
275
|
-
|
|
309
|
+
stop <task-id> Stop a running task (cooperative, then forced)
|
|
310
|
+
stop --all Stop all running tasks
|
|
311
|
+
kill <task-id> Alias for stop
|
|
276
312
|
result [task-id] Get task result (or list recent tasks)
|
|
313
|
+
--wait=N Poll (1s interval) up to N seconds for the task to
|
|
314
|
+
finish. Exit 124 on timeout. --wait=0 = single check.
|
|
277
315
|
restart <task-id> Restart a failed task as a new task
|
|
278
316
|
|
|
279
317
|
Logs & Diagnostics:
|
|
@@ -285,6 +323,22 @@ Database:
|
|
|
285
323
|
db push Sync DB schema to current code (additive only)
|
|
286
324
|
--force Reset migration history + skip confirmation
|
|
287
325
|
--dry-run Preview changes without applying
|
|
326
|
+
db migrate-task-logs Backfill legacy tasks.logs into task_log_events
|
|
327
|
+
--dry-run Validate and estimate without writing
|
|
328
|
+
--apply Consistent backup + resumable task-by-task apply
|
|
329
|
+
--verify Check event/source invariants and parity
|
|
330
|
+
|
|
331
|
+
Desktop Shortcut (Windows only):
|
|
332
|
+
shortcut status [--json] Show current shortcut status
|
|
333
|
+
shortcut install [--force] [--json] Install/repair the Desktop shortcut
|
|
334
|
+
shortcut uninstall [--json] Remove the Desktop shortcut
|
|
335
|
+
|
|
336
|
+
Publish:
|
|
337
|
+
publish [dir] [options] Package a workspace as a distributable template archive
|
|
338
|
+
--dry-run Scan + build manifest only; do not write a .tgz
|
|
339
|
+
--version <ver> Override manifest version (semver)
|
|
340
|
+
--json Print machine-readable JSON to stdout
|
|
341
|
+
--upload Submit + upload using the SDK account session
|
|
288
342
|
|
|
289
343
|
Built-in Tools:
|
|
290
344
|
memory <args> Memory tool
|
|
@@ -306,7 +360,9 @@ Global Options:
|
|
|
306
360
|
--version, -v Show version
|
|
307
361
|
`.trim());
|
|
308
362
|
}
|
|
309
|
-
|
|
363
|
+
const emitState = (0, emit_trailer_1.createDelegationEmitState)();
|
|
364
|
+
main(emitState).catch((err) => {
|
|
310
365
|
console.error(err instanceof Error ? err.message : String(err));
|
|
366
|
+
(0, emit_trailer_1.emitDelegationResult)({ command: process.argv[2], isError: true, state: emitState });
|
|
311
367
|
process.exit(err instanceof parse_common_flags_1.UnknownOptionError ? 2 : 1);
|
|
312
368
|
});
|