@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.
Files changed (57) hide show
  1. package/dist/bootstrap/codex-writable-roots.d.ts +58 -0
  2. package/dist/bootstrap/codex-writable-roots.js +113 -0
  3. package/dist/bootstrap/crewx-cli.js +8 -1
  4. package/dist/builtin.js +1 -0
  5. package/dist/commands/agent.js +0 -58
  6. package/dist/commands/cal.d.ts +15 -0
  7. package/dist/commands/cal.js +44 -0
  8. package/dist/commands/db.d.ts +2 -0
  9. package/dist/commands/db.js +381 -1
  10. package/dist/commands/detached.d.ts +65 -0
  11. package/dist/commands/detached.js +507 -0
  12. package/dist/commands/doctor.d.ts +53 -0
  13. package/dist/commands/doctor.js +391 -21
  14. package/dist/commands/emit-trailer.d.ts +25 -0
  15. package/dist/commands/emit-trailer.js +33 -0
  16. package/dist/commands/execute.d.ts +7 -4
  17. package/dist/commands/execute.js +121 -16
  18. package/dist/commands/hook/command-marker.d.ts +2 -0
  19. package/dist/commands/hook/command-marker.js +5 -0
  20. package/dist/commands/hook/install.d.ts +0 -1
  21. package/dist/commands/hook/install.js +60 -63
  22. package/dist/commands/hook/status.js +3 -3
  23. package/dist/commands/hook/uninstall.js +2 -2
  24. package/dist/commands/init.js +22 -1
  25. package/dist/commands/kill.d.ts +2 -8
  26. package/dist/commands/kill.js +6 -47
  27. package/dist/commands/log.js +4 -3
  28. package/dist/commands/parse-common-flags.d.ts +8 -2
  29. package/dist/commands/parse-common-flags.js +12 -3
  30. package/dist/commands/ps.d.ts +2 -2
  31. package/dist/commands/ps.js +69 -11
  32. package/dist/commands/publish.d.ts +1 -0
  33. package/dist/commands/publish.js +303 -0
  34. package/dist/commands/query.d.ts +5 -1
  35. package/dist/commands/query.js +114 -11
  36. package/dist/commands/registry.js +4 -2
  37. package/dist/commands/restart.js +20 -6
  38. package/dist/commands/result.d.ts +8 -3
  39. package/dist/commands/result.js +139 -7
  40. package/dist/commands/shortcut.d.ts +1 -0
  41. package/dist/commands/shortcut.js +267 -0
  42. package/dist/commands/slack.js +2 -1
  43. package/dist/commands/stop.d.ts +8 -0
  44. package/dist/commands/stop.js +119 -0
  45. package/dist/commands/write-output.d.ts +3 -0
  46. package/dist/commands/write-output.js +24 -0
  47. package/dist/logging.d.ts +1 -1
  48. package/dist/logging.js +3 -2
  49. package/dist/main.d.ts +3 -2
  50. package/dist/main.js +70 -14
  51. package/dist/utils/env-defaults.d.ts +2 -5
  52. package/dist/utils/env-defaults.js +10 -5
  53. package/dist/utils/inherited-trace.d.ts +24 -0
  54. package/dist/utils/inherited-trace.js +40 -0
  55. package/dist/utils/sdk-compat.d.ts +24 -0
  56. package/dist/utils/sdk-compat.js +120 -0
  57. 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
+ }
@@ -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.toLocaleString()}`);
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,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/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 { Crewx } from '@crewx/sdk';
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.toLocaleString()}\n` +
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().toLocaleString();
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. Parse command
9
- * 3. Dispatch to handler
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. Parse command
10
- * 3. Dispatch to handler
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 env_defaults_1 = require("./utils/env-defaults");
49
- process.env.CREWX_CLI ??= (0, env_defaults_1.resolveCrewxCli)();
50
- process.env.CREWX_WORKSPACE ??= (0, env_defaults_1.resolveCrewxWorkspace)();
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 kill_1 = require("./commands/kill");
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
- // P0-5: kill
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, kill_1.handleKill)(args.slice(1));
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
- kill <task-id> Kill a running task
275
- kill --all Kill all running tasks
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
- main().catch((err) => {
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
  });