crewx 0.9.0-rc.111 → 0.9.0-rc.112
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/bin/crewx-ui.js +0 -6
- package/bin/crewx.js +0 -6
- package/dist-server/common/task-log/public-log-projection.js +29 -0
- package/dist-server/domain/cron/cron-bootstrap.service.js +12 -3
- package/dist-server/domain/task/task.service.js +1 -17
- package/dist-server/domain/thread/thread.service.js +1 -17
- package/package.json +15 -15
- package/packages/cli/dist/commands/detached.js +6 -0
- package/packages/cli/dist/commands/doctor.d.ts +24 -0
- package/packages/cli/dist/commands/doctor.js +93 -10
- package/packages/cli/dist/commands/hook/install.js +20 -3
- package/packages/cli/package.json +1 -1
package/bin/crewx-ui.js
CHANGED
|
@@ -53,12 +53,6 @@ if (isElectron || isOverlay) {
|
|
|
53
53
|
? join(__dirname, '..', 'dist-electron', 'overlay.js')
|
|
54
54
|
: join(__dirname, '..', 'dist-electron', 'main.js');
|
|
55
55
|
|
|
56
|
-
if (process.env.ELECTRON_RUN_AS_NODE) {
|
|
57
|
-
console.warn(
|
|
58
|
-
'[crewx-ui] Ignoring ELECTRON_RUN_AS_NODE for Electron UI launch (it forces Electron into Node mode).',
|
|
59
|
-
);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
56
|
const child = spawn(String(electronPath), [entryFile], {
|
|
63
57
|
stdio: 'inherit',
|
|
64
58
|
env: getElectronSpawnEnv(),
|
package/bin/crewx.js
CHANGED
|
@@ -55,12 +55,6 @@ function launchElectron(entryFile, routeArgs = []) {
|
|
|
55
55
|
return false; // Electron not installed
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
if (process.env.ELECTRON_RUN_AS_NODE) {
|
|
59
|
-
console.warn(
|
|
60
|
-
'[crewx] Ignoring ELECTRON_RUN_AS_NODE for Electron UI launch (it forces Electron into Node mode).',
|
|
61
|
-
);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
58
|
const child = spawn(String(electronPath), [entryFile, ...routeArgs], {
|
|
65
59
|
stdio: 'inherit',
|
|
66
60
|
env: getElectronSpawnEnv(),
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.OUTPUT_UNAVAILABLE = void 0;
|
|
4
4
|
exports.isProviderProtocolOnlyText = isProviderProtocolOnlyText;
|
|
5
|
+
exports.parsePublicStdoutLogEntries = parsePublicStdoutLogEntries;
|
|
5
6
|
exports.isUnsupportedStructuredInfoLog = isUnsupportedStructuredInfoLog;
|
|
6
7
|
exports.createOutputUnavailableLogEntry = createOutputUnavailableLogEntry;
|
|
7
8
|
exports.projectPublicTaskLogEntry = projectPublicTaskLogEntry;
|
|
@@ -25,6 +26,34 @@ const STRUCTURED_INFO_LOG_TYPES = new Set([
|
|
|
25
26
|
function isProviderProtocolOnlyText(message, adapterKey) {
|
|
26
27
|
return (0, sdk_1.isProtocolOnlyText)(message, adapterKey);
|
|
27
28
|
}
|
|
29
|
+
/**
|
|
30
|
+
* Classify one persisted stdout row before the shared log pipeline runs.
|
|
31
|
+
*
|
|
32
|
+
* Displayable adapter output always wins. A confirmed Claude metadata line is
|
|
33
|
+
* omitted before merge, truncation, and log-tail slicing so protocol chatter
|
|
34
|
+
* cannot consume the public log budget. All other protocol-only rows retain
|
|
35
|
+
* the existing content-free placeholder and uncertain input remains raw text.
|
|
36
|
+
*/
|
|
37
|
+
function parsePublicStdoutLogEntries(timestamp, message, adapterKey) {
|
|
38
|
+
let parsed = [];
|
|
39
|
+
try {
|
|
40
|
+
parsed = (0, sdk_1.parseStdoutEvent)(timestamp, message, adapterKey);
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
// Preserve the historical raw-text fallback when an adapter parser fails.
|
|
44
|
+
}
|
|
45
|
+
if (parsed.length > 0)
|
|
46
|
+
return parsed;
|
|
47
|
+
// Metadata classification is intentionally opt-in. Without a registered
|
|
48
|
+
// adapter key, do not infer that an otherwise protocol-shaped line is safe
|
|
49
|
+
// to drop.
|
|
50
|
+
if (adapterKey && (0, sdk_1.isMetadataOnlyProtocolLine)(message, adapterKey))
|
|
51
|
+
return [];
|
|
52
|
+
if ((0, sdk_1.isProtocolOnlyText)(message, adapterKey)) {
|
|
53
|
+
return [createOutputUnavailableLogEntry(timestamp)];
|
|
54
|
+
}
|
|
55
|
+
return [{ timestamp, type: 'text', content: message }];
|
|
56
|
+
}
|
|
28
57
|
/**
|
|
29
58
|
* Return true for a JSON ACP/info message whose type is outside the accepted
|
|
30
59
|
* structured activity allowlist. Plain info text and untyped JSON remain text.
|
|
@@ -64,8 +64,10 @@ const DEFAULT_LEGACY_TERM_TIMEOUT_MS = 1_000;
|
|
|
64
64
|
const DEFAULT_LEGACY_POLL_INTERVAL_MS = 50;
|
|
65
65
|
// macOS `ps lstart` is second-granularity while the plain legacy PID marker
|
|
66
66
|
// has millisecond mtime. This is only a coarse PID-reuse guard, not proof of
|
|
67
|
-
// process creation identity
|
|
68
|
-
|
|
67
|
+
// process creation identity. `ps lstart` is quantized down to whole seconds,
|
|
68
|
+
// so only the marker-write-after-start direction is valid. Keep the window
|
|
69
|
+
// bounded to one quantization second plus one marker-write second.
|
|
70
|
+
const PROCESS_START_IDENTITY_MAX_DELTA_MS = 2_000;
|
|
69
71
|
const EXPECTED_COMMAND_UNAVAILABLE_REASON = 'expected full process command is unavailable';
|
|
70
72
|
/** Nest token for injectable host seams used by tests and embedders. */
|
|
71
73
|
var cron_runtime_sync_port_js_2 = require("./cron-runtime-sync.port.js");
|
|
@@ -423,7 +425,8 @@ function startIdentitiesMatch(marker, processStart) {
|
|
|
423
425
|
const markerNumber = typeof marker === 'number' ? marker : Number(marker);
|
|
424
426
|
const processNumber = typeof processStart === 'number' ? processStart : Number(processStart);
|
|
425
427
|
if (Number.isFinite(markerNumber) && Number.isFinite(processNumber)) {
|
|
426
|
-
|
|
428
|
+
const delta = markerNumber - processNumber;
|
|
429
|
+
return delta >= 0 && delta <= PROCESS_START_IDENTITY_MAX_DELTA_MS;
|
|
427
430
|
}
|
|
428
431
|
return String(marker) === String(processStart);
|
|
429
432
|
}
|
|
@@ -729,6 +732,12 @@ let CronBootstrapService = CronBootstrapService_1 = class CronBootstrapService {
|
|
|
729
732
|
alert: 'expected-command-unavailable',
|
|
730
733
|
};
|
|
731
734
|
}
|
|
735
|
+
if (process.platform === 'win32') {
|
|
736
|
+
// Win32_Process does not provide a trusted cwd in the default
|
|
737
|
+
// inspector. Do not apply the Unix ps/lstart numeric window to an
|
|
738
|
+
// injected observation that happens to include a cwd.
|
|
739
|
+
return { kind: 'unknown', reason: 'process cwd is not trustworthy on Windows' };
|
|
740
|
+
}
|
|
732
741
|
if (!observation.commandLine)
|
|
733
742
|
return { kind: 'unknown', reason: 'process command line is unknown' };
|
|
734
743
|
const daemonKind = classifyCronDaemonCommand(observation.commandLine);
|
|
@@ -1247,23 +1247,7 @@ let TaskService = TaskService_1 = class TaskService {
|
|
|
1247
1247
|
const entries = [];
|
|
1248
1248
|
for (const log of rawLogs) {
|
|
1249
1249
|
if (log.level === 'stdout') {
|
|
1250
|
-
|
|
1251
|
-
try {
|
|
1252
|
-
parsed = (0, sdk_1.parseStdoutEvent)(log.timestamp, log.message, providerId);
|
|
1253
|
-
}
|
|
1254
|
-
catch { /* adapter parse failure — classify the raw row below */ }
|
|
1255
|
-
if (parsed.length > 0) {
|
|
1256
|
-
entries.push(...parsed);
|
|
1257
|
-
continue;
|
|
1258
|
-
}
|
|
1259
|
-
// Parsed successfully but no displayable entries (e.g. thinking-only assistant message).
|
|
1260
|
-
// Keep a safe row for confirmed provider protocol envelopes instead of
|
|
1261
|
-
// exposing the raw JSON fallback.
|
|
1262
|
-
if ((0, public_log_projection_js_1.isProviderProtocolOnlyText)(log.message, providerId)) {
|
|
1263
|
-
entries.push((0, public_log_projection_js_1.createOutputUnavailableLogEntry)(log.timestamp));
|
|
1264
|
-
continue;
|
|
1265
|
-
}
|
|
1266
|
-
entries.push({ timestamp: log.timestamp, type: 'text', content: log.message });
|
|
1250
|
+
entries.push(...(0, public_log_projection_js_1.parsePublicStdoutLogEntries)(log.timestamp, log.message, providerId));
|
|
1267
1251
|
}
|
|
1268
1252
|
else if (log.level === 'info') {
|
|
1269
1253
|
const infoEntries = (0, sdk_1.defaultParseEvent)(log.message, log.timestamp);
|
|
@@ -1123,23 +1123,7 @@ let ThreadService = ThreadService_1 = class ThreadService {
|
|
|
1123
1123
|
const entries = [];
|
|
1124
1124
|
for (const log of rawLogs) {
|
|
1125
1125
|
if (log.level === 'stdout') {
|
|
1126
|
-
|
|
1127
|
-
try {
|
|
1128
|
-
parsed = (0, sdk_1.parseStdoutEvent)(log.timestamp, log.message, providerId);
|
|
1129
|
-
}
|
|
1130
|
-
catch { /* adapter parse failure — classify the raw row below */ }
|
|
1131
|
-
if (parsed.length > 0) {
|
|
1132
|
-
entries.push(...parsed);
|
|
1133
|
-
continue;
|
|
1134
|
-
}
|
|
1135
|
-
// Parsed successfully but no displayable entries (e.g. thinking-only assistant message).
|
|
1136
|
-
// Keep a safe row for confirmed provider protocol envelopes instead of
|
|
1137
|
-
// exposing the raw JSON fallback.
|
|
1138
|
-
if ((0, public_log_projection_js_1.isProviderProtocolOnlyText)(log.message, providerId)) {
|
|
1139
|
-
entries.push((0, public_log_projection_js_1.createOutputUnavailableLogEntry)(log.timestamp));
|
|
1140
|
-
continue;
|
|
1141
|
-
}
|
|
1142
|
-
entries.push({ timestamp: log.timestamp, type: 'text', content: log.message });
|
|
1126
|
+
entries.push(...(0, public_log_projection_js_1.parsePublicStdoutLogEntries)(log.timestamp, log.message, providerId));
|
|
1143
1127
|
}
|
|
1144
1128
|
else if (log.level === 'info') {
|
|
1145
1129
|
const infoEntries = (0, sdk_1.defaultParseEvent)(log.message, log.timestamp);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "crewx",
|
|
3
|
-
"version": "0.9.0-rc.
|
|
3
|
+
"version": "0.9.0-rc.112",
|
|
4
4
|
"description": "CrewX — AI agent team dashboard with Electron UI and CLI (Web + Electron + Global CLI)",
|
|
5
5
|
"main": "server.js",
|
|
6
6
|
"bin": {
|
|
@@ -150,20 +150,20 @@
|
|
|
150
150
|
"ws": "8.20.0",
|
|
151
151
|
"yargs": "17.7.0",
|
|
152
152
|
"zod": "3.25.76",
|
|
153
|
-
"@crewx/chromex": "0.1.0-rc.
|
|
154
|
-
"@crewx/cli": "0.9.0-rc.
|
|
155
|
-
"@crewx/cron": "0.1.10-rc.
|
|
156
|
-
"@crewx/
|
|
157
|
-
"@crewx/
|
|
158
|
-
"@crewx/
|
|
159
|
-
"@crewx/
|
|
160
|
-
"@crewx/
|
|
161
|
-
"@crewx/
|
|
162
|
-
"@crewx/
|
|
163
|
-
"@crewx/
|
|
164
|
-
"@crewx/
|
|
165
|
-
"@crewx/
|
|
166
|
-
"@crewx/
|
|
153
|
+
"@crewx/chromex": "0.1.0-rc.145",
|
|
154
|
+
"@crewx/cli": "0.9.0-rc.112",
|
|
155
|
+
"@crewx/cron": "0.1.10-rc.147",
|
|
156
|
+
"@crewx/dreaming": "0.1.0-rc.138",
|
|
157
|
+
"@crewx/knowledge-core": "0.1.17-rc.122",
|
|
158
|
+
"@crewx/doc": "0.1.9-rc.105",
|
|
159
|
+
"@crewx/memory": "0.1.23-rc.129",
|
|
160
|
+
"@crewx/shared": "0.0.6-rc.27",
|
|
161
|
+
"@crewx/search": "0.1.10-rc.108",
|
|
162
|
+
"@crewx/skill": "0.1.20-rc.6",
|
|
163
|
+
"@crewx/sdk": "0.9.0-rc.112",
|
|
164
|
+
"@crewx/workflow": "0.3.22-rc.158",
|
|
165
|
+
"@crewx/wi": "0.1.10-rc.132",
|
|
166
|
+
"@crewx/wbs": "0.1.10-rc.138"
|
|
167
167
|
},
|
|
168
168
|
"devDependencies": {
|
|
169
169
|
"@anthropic-ai/claude-agent-sdk": "0.2.132",
|
|
@@ -293,6 +293,9 @@ function runnerCommand(command) {
|
|
|
293
293
|
function buildRunnerEnvironment(taskId) {
|
|
294
294
|
const env = { ...process.env };
|
|
295
295
|
delete env[INTERNAL_TOKEN_ENV];
|
|
296
|
+
if (process.versions.electron) {
|
|
297
|
+
env.ELECTRON_RUN_AS_NODE = '1';
|
|
298
|
+
}
|
|
296
299
|
env.CREWX_DETACHED_RUNNER = '1';
|
|
297
300
|
env.CREWX_ASSIGNED_TASK_ID = taskId;
|
|
298
301
|
return env;
|
|
@@ -328,6 +331,9 @@ function readAssignedTaskActor(taskRepo, taskId) {
|
|
|
328
331
|
}
|
|
329
332
|
function buildMiddleEnvironment(token) {
|
|
330
333
|
const env = { ...process.env };
|
|
334
|
+
if (process.versions.electron) {
|
|
335
|
+
env.ELECTRON_RUN_AS_NODE = '1';
|
|
336
|
+
}
|
|
331
337
|
env[INTERNAL_TOKEN_ENV] = token;
|
|
332
338
|
return env;
|
|
333
339
|
}
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* crewx doctor Run full diagnosis
|
|
7
7
|
* crewx doctor --config <path> Use specific config file
|
|
8
8
|
*/
|
|
9
|
+
import type { CrewxExecutableResolution, CrewxExecutableStage } from '@crewx/sdk';
|
|
9
10
|
interface DiagnosticResult {
|
|
10
11
|
name: string;
|
|
11
12
|
status: 'success' | 'warning' | 'error';
|
|
@@ -44,6 +45,25 @@ export interface CrewxShimReport {
|
|
|
44
45
|
fingerprint: string;
|
|
45
46
|
}>;
|
|
46
47
|
}
|
|
48
|
+
export interface CrewxExecutableReport {
|
|
49
|
+
platform: NodeJS.Platform;
|
|
50
|
+
pathCandidates: string[];
|
|
51
|
+
stage?: CrewxExecutableStage;
|
|
52
|
+
argv?: string[];
|
|
53
|
+
electron: boolean;
|
|
54
|
+
requiredEnvKeys: string[];
|
|
55
|
+
binResolution: 'global_first' | 'local_first';
|
|
56
|
+
adoptedPath?: string;
|
|
57
|
+
resolutionError?: string;
|
|
58
|
+
}
|
|
59
|
+
export interface CrewxExecutableInspectionOptions {
|
|
60
|
+
platform?: NodeJS.Platform;
|
|
61
|
+
cwd?: string;
|
|
62
|
+
env?: NodeJS.ProcessEnv;
|
|
63
|
+
binResolution?: 'global_first' | 'local_first';
|
|
64
|
+
pathCandidates?: readonly string[];
|
|
65
|
+
resolution?: CrewxExecutableResolution;
|
|
66
|
+
}
|
|
47
67
|
/** CLI diagnostics keyed by the bare provider name used in PROVIDER_ORDER. */
|
|
48
68
|
export declare const CLI_PROVIDER_INFO: Record<string, CliProviderInfo>;
|
|
49
69
|
/** Format a byte count for diagnostic output. */
|
|
@@ -52,6 +72,10 @@ export declare function formatBytes(bytes: number): string;
|
|
|
52
72
|
export declare function inspectNpxCache(cacheRoot?: string): NpxCacheReport;
|
|
53
73
|
/** Inspect installed CrewX hooks without rewriting provider settings. */
|
|
54
74
|
export declare function inspectHookCommands(projectRoot: string): HookCommandReport;
|
|
75
|
+
/** Inspect the SDK-selected CrewX executable without printing environment values. */
|
|
76
|
+
export declare function inspectCrewxExecutable(options?: CrewxExecutableInspectionOptions & {
|
|
77
|
+
configPath?: string;
|
|
78
|
+
}): CrewxExecutableReport;
|
|
55
79
|
/** Inspect orphaned CrewX PATH shim directories without modifying them. */
|
|
56
80
|
export declare function inspectCrewxShims(crewxHome?: string, resolvedFingerprint?: string | undefined): CrewxShimReport;
|
|
57
81
|
/**
|
|
@@ -12,10 +12,10 @@ exports.CLI_PROVIDER_INFO = void 0;
|
|
|
12
12
|
exports.formatBytes = formatBytes;
|
|
13
13
|
exports.inspectNpxCache = inspectNpxCache;
|
|
14
14
|
exports.inspectHookCommands = inspectHookCommands;
|
|
15
|
+
exports.inspectCrewxExecutable = inspectCrewxExecutable;
|
|
15
16
|
exports.inspectCrewxShims = inspectCrewxShims;
|
|
16
17
|
exports.checkCliProviders = checkCliProviders;
|
|
17
18
|
exports.handleDoctor = handleDoctor;
|
|
18
|
-
const node_crypto_1 = require("node:crypto");
|
|
19
19
|
const node_child_process_1 = require("node:child_process");
|
|
20
20
|
const node_fs_1 = require("node:fs");
|
|
21
21
|
const node_os_1 = require("node:os");
|
|
@@ -255,17 +255,98 @@ function inspectHookCommands(projectRoot) {
|
|
|
255
255
|
}
|
|
256
256
|
return { inspectedFiles, issues };
|
|
257
257
|
}
|
|
258
|
-
function
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
258
|
+
function configuredBinResolution(configPath, fallback = 'global_first') {
|
|
259
|
+
try {
|
|
260
|
+
const config = (0, sdk_1.loadYamlFile)(configPath);
|
|
261
|
+
const value = config.settings?.bin_resolution;
|
|
262
|
+
return value === 'local_first' || value === 'global_first' ? value : fallback;
|
|
263
|
+
}
|
|
264
|
+
catch {
|
|
265
|
+
return fallback;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
function readWindowsCrewxPathCandidates(cwd, env, platform) {
|
|
269
|
+
if (platform !== 'win32')
|
|
270
|
+
return [];
|
|
271
|
+
try {
|
|
272
|
+
(0, sdk_1.primeWindowsConsoleCodePage)();
|
|
273
|
+
const output = (0, node_child_process_1.execFileSync)('where.exe', ['crewx'], {
|
|
274
|
+
cwd,
|
|
275
|
+
env,
|
|
276
|
+
encoding: null,
|
|
277
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
278
|
+
});
|
|
279
|
+
return (0, sdk_1.decodeConsoleBuffer)(output, { platform })
|
|
280
|
+
.split(/\r?\n/)
|
|
281
|
+
.map((line) => line.trim())
|
|
282
|
+
.filter((line) => line.length > 0);
|
|
283
|
+
}
|
|
284
|
+
catch {
|
|
285
|
+
return [];
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
/** Inspect the SDK-selected CrewX executable without printing environment values. */
|
|
289
|
+
function inspectCrewxExecutable(options = {}) {
|
|
290
|
+
const platform = options.platform ?? process.platform;
|
|
291
|
+
const cwd = (0, node_path_1.resolve)(options.cwd ?? process.cwd());
|
|
292
|
+
const env = options.env ?? process.env;
|
|
293
|
+
const configPath = options.configPath ?? (0, node_path_1.join)(cwd, 'crewx.yaml');
|
|
294
|
+
const resolution = options.resolution ?? (0, sdk_1.resolveCrewxExecutable)({ env, cwd, platform });
|
|
295
|
+
const pathCandidates = options.pathCandidates
|
|
296
|
+
? [...options.pathCandidates]
|
|
297
|
+
: readWindowsCrewxPathCandidates(cwd, env, platform);
|
|
298
|
+
const requiredEnvKeys = resolution.ok
|
|
299
|
+
? Object.keys(resolution.requiredEnv ?? {})
|
|
300
|
+
.filter((key) => key === 'ELECTRON_RUN_AS_NODE')
|
|
301
|
+
: [];
|
|
302
|
+
return {
|
|
303
|
+
platform,
|
|
304
|
+
pathCandidates,
|
|
305
|
+
...(resolution.ok
|
|
306
|
+
? {
|
|
307
|
+
stage: resolution.stage,
|
|
308
|
+
argv: [...resolution.argv],
|
|
309
|
+
adoptedPath: resolution.argv[0],
|
|
310
|
+
}
|
|
311
|
+
: { resolutionError: resolution.reason }),
|
|
312
|
+
electron: requiredEnvKeys.includes('ELECTRON_RUN_AS_NODE'),
|
|
313
|
+
requiredEnvKeys,
|
|
314
|
+
binResolution: options.binResolution ?? configuredBinResolution(configPath),
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
function checkCrewxExecutable(report) {
|
|
318
|
+
const details = [
|
|
319
|
+
`platform: ${report.platform}`,
|
|
320
|
+
`stage: ${report.stage ?? '(unresolved)'}`,
|
|
321
|
+
`argv: ${report.argv ? (0, sdk_1.formatCrewxExecutableArgv)(report.argv) : '(unresolved)'}`,
|
|
322
|
+
`adopted path: ${report.adoptedPath ?? '(none)'}`,
|
|
323
|
+
`Electron runtime: ${report.electron ? 'yes' : 'no'}`,
|
|
324
|
+
`required environment keys: ${report.requiredEnvKeys.length > 0 ? report.requiredEnvKeys.join(', ') : '(none)'}`,
|
|
325
|
+
`bin_resolution setting: ${report.binResolution}`,
|
|
326
|
+
...(report.platform === 'win32'
|
|
327
|
+
? [`where.exe crewx candidates: ${report.pathCandidates.length > 0 ? report.pathCandidates.join(', ') : '(none)'}`]
|
|
328
|
+
: []),
|
|
329
|
+
];
|
|
330
|
+
if (report.resolutionError) {
|
|
331
|
+
return {
|
|
332
|
+
name: 'CrewX Executable',
|
|
333
|
+
status: 'warning',
|
|
334
|
+
message: 'Could not resolve the CrewX executable',
|
|
335
|
+
details: `${details.join('\n')}\nResolution failed; no environment values were printed.`,
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
return {
|
|
339
|
+
name: 'CrewX Executable',
|
|
340
|
+
status: 'success',
|
|
341
|
+
message: 'SDK child executable contract is available',
|
|
342
|
+
details: details.join('\n'),
|
|
343
|
+
};
|
|
266
344
|
}
|
|
267
345
|
/** Inspect orphaned CrewX PATH shim directories without modifying them. */
|
|
268
|
-
function inspectCrewxShims(crewxHome = (0, sdk_1.getCrewxHome)(), resolvedFingerprint =
|
|
346
|
+
function inspectCrewxShims(crewxHome = (0, sdk_1.getCrewxHome)(), resolvedFingerprint = (() => {
|
|
347
|
+
const resolution = (0, sdk_1.resolveCrewxExecutable)();
|
|
348
|
+
return resolution.ok ? (0, sdk_1.crewxCliShimFingerprint)(resolution) : undefined;
|
|
349
|
+
})()) {
|
|
269
350
|
const shimRoot = (0, node_path_1.join)((0, node_path_1.resolve)(crewxHome), 'bin-shim');
|
|
270
351
|
const staleDirectories = [];
|
|
271
352
|
if (resolvedFingerprint) {
|
|
@@ -526,6 +607,7 @@ async function handleDoctor(args) {
|
|
|
526
607
|
let shims = inspectCrewxShims();
|
|
527
608
|
const projectRoot = (0, node_path_1.dirname)((0, node_path_1.resolve)(configPath));
|
|
528
609
|
const hooks = inspectHookCommands(projectRoot);
|
|
610
|
+
const executable = inspectCrewxExecutable({ configPath, cwd: process.cwd() });
|
|
529
611
|
if (cleanNpxCache) {
|
|
530
612
|
cleanDoctorArtifacts(npxCache, shims);
|
|
531
613
|
npxCache = inspectNpxCache();
|
|
@@ -535,6 +617,7 @@ async function handleDoctor(args) {
|
|
|
535
617
|
checkConfig(configPath),
|
|
536
618
|
checkLogsDir(),
|
|
537
619
|
checkEnvVars(),
|
|
620
|
+
checkCrewxExecutable(executable),
|
|
538
621
|
...checkCliProviders(),
|
|
539
622
|
checkNpxCache(npxCache),
|
|
540
623
|
checkHookCommands(hooks),
|
|
@@ -32,9 +32,26 @@ function resolveCrewxBinary() {
|
|
|
32
32
|
if (!resolution.ok) {
|
|
33
33
|
throw new Error((0, sdk_1.formatCrewxExecutableFailure)(resolution));
|
|
34
34
|
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
35
|
+
let stableEntrypoint;
|
|
36
|
+
try {
|
|
37
|
+
stableEntrypoint = (0, sdk_1.materializeCrewxStableEntrypoint)(resolution, { allowEnvStage: true });
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
if (resolution.requiredEnv && Object.keys(resolution.requiredEnv).length > 0) {
|
|
41
|
+
throw new Error(`CrewX hook entrypoint could not preserve required environment: ${error instanceof Error ? error.message : String(error)}`);
|
|
42
|
+
}
|
|
43
|
+
throw error;
|
|
44
|
+
}
|
|
45
|
+
if (stableEntrypoint) {
|
|
46
|
+
return (0, sdk_1.formatCrewxExecutableArgv)([stableEntrypoint]);
|
|
47
|
+
}
|
|
48
|
+
// A required runtime environment cannot be represented by a raw argv string,
|
|
49
|
+
// and the fingerprint shim directory is doctor-GC managed. Refuse to write
|
|
50
|
+
// either representation when the stable hook path was not eligible.
|
|
51
|
+
if (resolution.requiredEnv && Object.keys(resolution.requiredEnv).length > 0) {
|
|
52
|
+
throw new Error('CrewX hook entrypoint could not preserve required environment: stable entrypoint is not eligible for this executable');
|
|
53
|
+
}
|
|
54
|
+
return (0, sdk_1.formatCrewxExecutableArgv)(resolution.argv);
|
|
38
55
|
}
|
|
39
56
|
function readSettingsTyped(settingsPath) {
|
|
40
57
|
return (0, paths_1.readSettings)(settingsPath);
|