evolcore 0.0.22 → 0.0.24
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/CHANGELOG.md +74 -14
- package/README.md +4 -0
- package/bin/codex-managed-acl.ps1 +160 -0
- package/bin/codex-managed-hook.mjs +127 -10
- package/bin/ec-safe-output.js +85 -3
- package/bin/install-codex-managed-hooks.mjs +384 -95
- package/dist/agents/baseagent-availability.js +48 -0
- package/dist/agents/baseagent.js +30 -2
- package/dist/agents/claude-runner.js +179 -240
- package/dist/agents/{codex-app-server-client.js → codex/app-server-client.js} +90 -20
- package/dist/{utils/codex-app-server-registry.js → agents/codex/app-server-registry.js} +2 -2
- package/dist/agents/codex/catalog.js +70 -0
- package/dist/agents/codex/diagnostics.js +369 -0
- package/dist/agents/codex-runner.js +839 -192
- package/dist/agents/ecagent-runner.js +27 -34
- package/dist/agents/gemini-runner.js +62 -8
- package/dist/agents/runner-types.js +1 -1
- package/dist/aun/aid/managed-operation.js +28 -6
- package/dist/aun/aid/store.js +43 -0
- package/dist/aun/menu-client.js +272 -0
- package/dist/aun/msg/group.js +65 -4
- package/dist/aun/msg/index.js +1 -1
- package/dist/aun/msg/managed-operation.js +57 -5
- package/dist/aun/outbox.js +56 -18
- package/dist/aun/rpc/client.js +7 -1
- package/dist/channels/aun.js +178 -77
- package/dist/channels/daemon.js +7 -1
- package/dist/channels/dingtalk.js +1 -0
- package/dist/channels/feishu.js +10 -4
- package/dist/channels/wechat.js +81 -10
- package/dist/channels/wecom.js +2 -1
- package/dist/cli/agent-command.js +29 -3
- package/dist/cli/agent-create.js +538 -0
- package/dist/cli/agent.js +26 -675
- package/dist/cli/aun-commands.js +108 -24
- package/dist/cli/command-log.js +21 -2
- package/dist/cli/ctl-command.js +2 -0
- package/dist/cli/daemon-commands.js +289 -61
- package/dist/cli/index.js +22 -2
- package/dist/cli/init.js +2 -10
- package/dist/cli/managed-command-guard.js +3 -0
- package/dist/cli/menu-client.js +95 -0
- package/dist/cli/model.js +23 -8
- package/dist/cli/restart-monitor.js +432 -8
- package/dist/cli/stats.js +110 -0
- package/dist/cli/task-context.js +12 -2
- package/dist/cli/trigger-command.js +9 -1
- package/dist/config/builtin-roles.js +2 -0
- package/dist/config/schema-migration.js +14 -2
- package/dist/core/auth/agent-delegation.js +10 -7
- package/dist/core/auth/auth-gateway.js +3 -2
- package/dist/core/auth/authorization-audit.js +75 -18
- package/dist/core/auth/operation-authorizer.js +63 -3
- package/dist/core/auth/operation-catalog.js +14 -2
- package/dist/core/auth/remote-access-store.js +136 -0
- package/dist/core/baseagent-loader.js +4 -4
- package/dist/core/bootstrap-messages.js +39 -18
- package/dist/core/bootstrap-service.js +32 -26
- package/dist/core/capability/capability-manager.js +8 -4
- package/dist/core/capability/providers/codex-capability-provider.js +1 -1
- package/dist/core/command/agent-control.js +7 -2
- package/dist/core/command/command-handler.js +128 -30
- package/dist/core/command/daemon-diagnostics.js +448 -0
- package/dist/core/command/menu-catalog.js +6 -1
- package/dist/core/command/menu-handler.js +410 -98
- package/dist/core/command/menu-protocol.js +123 -0
- package/dist/core/command/slash-gate.js +4 -6
- package/dist/core/command/slash-handler.js +199 -70
- package/dist/core/data-migration.js +5 -15
- package/dist/core/event-catalog.js +3 -0
- package/dist/core/handoff/runtime.js +3 -1
- package/dist/core/handoff/store.js +7 -1
- package/dist/core/message/im-renderer.js +13 -6
- package/dist/core/message/message-bridge.js +5 -36
- package/dist/core/message/message-queue.js +20 -15
- package/dist/core/message/message-utils.js +6 -3
- package/dist/core/message/response-engine.js +147 -94
- package/dist/core/model/catalog-service.js +310 -0
- package/dist/core/model/model-catalog.js +134 -334
- package/dist/core/model/model-diagnostics.js +4 -5
- package/dist/core/model/model-fallback.js +9 -16
- package/dist/core/model/model-permission.js +5 -2
- package/dist/core/permission/approval-gateway.js +183 -32
- package/dist/core/permission/protected-paths.js +63 -4
- package/dist/core/permission/sandbox-runtime.js +4 -2
- package/dist/core/permission/tool-policy.js +257 -31
- package/dist/core/runtime-lock.js +13 -8
- package/dist/core/session/adapters/codex-session-file-adapter.js +71 -3
- package/dist/core/session/session-manager.js +49 -13
- package/dist/core/session/session-renew.js +1 -0
- package/dist/index.js +214 -57
- package/dist/ipc.js +191 -102
- package/dist/paths.js +23 -1
- package/dist/stats/billing.js +18 -4
- package/dist/trigger/manager.js +3 -1
- package/dist/trigger/parser.js +77 -13
- package/dist/trigger/patch.js +104 -1
- package/dist/utils/atomic-write.js +26 -2
- package/dist/utils/cross-platform.js +436 -1
- package/dist/utils/ecweb-supervisor.js +11 -2
- package/dist/utils/ecweb-utils.js +8 -26
- package/dist/utils/error-utils.js +16 -2
- package/dist/utils/model-prices.jsonl +23 -20
- package/dist/utils/npm-ops.js +70 -15
- package/dist/utils/windows-autostart.js +1 -1
- package/kits/docs/evolcore/INDEX.md +1 -0
- package/kits/docs/evolcore/agent-create.md +13 -0
- package/kits/docs/evolcore/group-collaboration.md +7 -2
- package/kits/docs/evolcore/menu-client.md +34 -0
- package/kits/migrations/migrate-contact-book-v2.mjs +9 -1
- package/kits/migrations/migrate-role-config-v4.mjs +11 -5
- package/kits/migrations/migrate-role-config-v5.mjs +8 -2
- package/kits/templates/roles/admin.json +2 -0
- package/kits/templates/roles/visitor.json +1 -0
- package/kits/templates/system-fragments/bootstrap.md +8 -1
- package/package.json +16 -4
- package/dist/utils/codex-cli.js +0 -43
|
@@ -15,7 +15,7 @@ import { cmdInit, needsControlAidInit, initTail } from './init.js';
|
|
|
15
15
|
import * as platform from '../utils/cross-platform.js';
|
|
16
16
|
import { EventBus } from '../core/event-bus.js';
|
|
17
17
|
import { tryUpgrade, tryUpgradeGlobalPkg, resolveGlobalPkg } from '../utils/npm-ops.js';
|
|
18
|
-
import { ecwebVersionRequirementError,
|
|
18
|
+
import { ecwebVersionRequirementError, resolveEcwebLaunchCommand, fetchRemotePairingCode } from '../utils/ecweb-utils.js';
|
|
19
19
|
import { isHostChinese } from '../utils/locale.js';
|
|
20
20
|
import { scanInstances, cleanupInstances, findOrphanProcesses, killOrphans } from '../utils/instance-registry.js';
|
|
21
21
|
import { filterLogFiles, deriveLogTypes, computePreChecked, validateLogTypes, shortLogName as shortLogNameLocal, isWatchLogFile } from './watch-logs.js';
|
|
@@ -28,9 +28,52 @@ import { WEB_CLI_BIN, WEB_PACKAGE_LATEST, WEB_PACKAGE_NAME } from '../product.js
|
|
|
28
28
|
import { rotateStdoutLog } from '../utils/log-writer.js';
|
|
29
29
|
import { inspectDataMigrationRequirement } from '../core/data-migration.js';
|
|
30
30
|
import { isInitCancelledError, printInitCancelHint } from './init-cancel.js';
|
|
31
|
-
import { listCodexAppServerProcesses, unregisterCodexAppServerProcess } from '../
|
|
31
|
+
import { listCodexAppServerProcesses, unregisterCodexAppServerProcess } from '../agents/codex/app-server-registry.js';
|
|
32
32
|
import { DEFAULT_ECWEB_PORT, ecwebService, setEcwebEnabled } from '../config/daemon-services.js';
|
|
33
|
+
import { checkCodexProjectPath, inspectCodexManagedHook, ensureCodexManagedHook, runCodexHookLaunchDiagnostic, } from '../agents/codex/diagnostics.js';
|
|
33
34
|
const execFileAsync = promisify(execFile);
|
|
35
|
+
/** Run before launch, and before restart stops any existing daemon. */
|
|
36
|
+
export function ensureCodexHookBeforeLifecycle(onRepaired) {
|
|
37
|
+
if (!platform.isWindows)
|
|
38
|
+
return true;
|
|
39
|
+
try {
|
|
40
|
+
const usesCodex = loadDefaults()?.active_baseagent === 'codex'
|
|
41
|
+
|| loadAllAgents().agents.some(agent => {
|
|
42
|
+
try {
|
|
43
|
+
return resolveEffective({ self: agent.aid }).active_baseagent === 'codex';
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
console.warn(`⚠ Agent ${agent.aid} 配置解析失败:${error instanceof Error ? error.message : String(error)}`);
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
if (!usesCodex)
|
|
51
|
+
return true;
|
|
52
|
+
const result = ensureCodexManagedHook({
|
|
53
|
+
packageRoot: getPackageRoot(),
|
|
54
|
+
onInstallRequired: () => console.log('🔧 Codex 运行组件缺失,正在自动安装;Windows 提示管理员授权时请选择“是”。'),
|
|
55
|
+
});
|
|
56
|
+
if (result.status === 'installed') {
|
|
57
|
+
if (result.repaired) {
|
|
58
|
+
console.log('✅ Codex Windows managed hook 已自动安装并验证');
|
|
59
|
+
onRepaired?.();
|
|
60
|
+
}
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
console.error(`❌ Codex Windows managed hook 未就绪:${result.error || result.reason || result.status}`);
|
|
64
|
+
if (result.status === 'missing') {
|
|
65
|
+
console.error(' 本次操作已停止。下次启动或重启会再次自动尝试安装,需要允许 Windows 管理员授权。');
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
console.error(' 本次操作已停止。现有 Codex 策略或文件访问异常,需要管理员核对;自动安装不会覆盖异常策略。');
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
console.error(`❌ Codex 启动检查失败:${error instanceof Error ? error.message : String(error)}`);
|
|
73
|
+
}
|
|
74
|
+
process.exitCode = 1;
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
34
77
|
async function runAutomaticInit(action) {
|
|
35
78
|
try {
|
|
36
79
|
return await action();
|
|
@@ -65,8 +108,8 @@ function printNoSelfAgentHints(options) {
|
|
|
65
108
|
// taskkill, even though the daemon is healthy and able to shut down gracefully.
|
|
66
109
|
const DAEMON_PROBE_TIMEOUT_MS = platform.isWindows ? 5_000 : 1_000;
|
|
67
110
|
export const DAEMON_SHUTDOWN_TIMEOUT_MS = platform.isWindows ? 10_000 : 3_000;
|
|
68
|
-
async function probeDaemon(socketPath, timeoutMs = DAEMON_PROBE_TIMEOUT_MS) {
|
|
69
|
-
const response = await ipcQuery(socketPath, { type: 'ping' }, timeoutMs);
|
|
111
|
+
async function probeDaemon(socketPath, timeoutMs = DAEMON_PROBE_TIMEOUT_MS, throwOnError = false) {
|
|
112
|
+
const response = await ipcQuery(socketPath, { type: 'ping' }, timeoutMs, { throwOnError });
|
|
70
113
|
if (response?.pong !== true || !Number.isInteger(response.pid) || response.pid <= 0)
|
|
71
114
|
return null;
|
|
72
115
|
return response;
|
|
@@ -177,10 +220,15 @@ function sourceBuildNeedsRefresh() {
|
|
|
177
220
|
function runSourceBuild() {
|
|
178
221
|
return new Promise(resolve => {
|
|
179
222
|
console.log('🔧 源码比 daemon 编译产物更新,自动重新构建...');
|
|
180
|
-
|
|
223
|
+
// Windows cannot spawn a .cmd shim directly with shell:false on recent
|
|
224
|
+
// Node versions. Invoke cmd.exe explicitly instead of using spawn's
|
|
225
|
+
// shell:true mode, which also avoids the DEP0190 warning.
|
|
226
|
+
const command = platform.isWindows ? (process.env.ComSpec ?? process.env.COMSPEC ?? 'cmd.exe') : 'npm';
|
|
227
|
+
const args = platform.isWindows ? ['/d', '/c', 'npm.cmd', 'run', 'build'] : ['run', 'build'];
|
|
228
|
+
const child = spawn(command, args, {
|
|
181
229
|
cwd: getPackageRoot(),
|
|
182
230
|
stdio: 'inherit',
|
|
183
|
-
|
|
231
|
+
windowsHide: platform.isWindows,
|
|
184
232
|
});
|
|
185
233
|
child.on('error', err => {
|
|
186
234
|
console.error(`❌ 无法启动构建进程: ${err.message}`);
|
|
@@ -199,22 +247,100 @@ function runSourceBuild() {
|
|
|
199
247
|
});
|
|
200
248
|
});
|
|
201
249
|
}
|
|
250
|
+
/**
|
|
251
|
+
* Spawn the restart monitor and confirm it actually reached its own code.
|
|
252
|
+
*
|
|
253
|
+
* The daemon is already stopped at this point, so a monitor that dies on
|
|
254
|
+
* startup leaves nothing running and nobody watching — the exact silent
|
|
255
|
+
* failure this mechanism exists to prevent.
|
|
256
|
+
*/
|
|
257
|
+
async function spawnRestartMonitorAndConfirm(monitorEntry, reportLine) {
|
|
258
|
+
const result = await platform.launchMonitorAndConfirm([...monitorEntry.args, 'restart-monitor'], { EVOLCORE_HOME: resolvePaths().root, ...(monitorEntry.env ?? {}) }, pid => scanInstances().restartMonitors.some(m => m.alive && m.record.pid === pid), pid => scanInstances().restartMonitors.some(m => m.alive && m.record.pid !== pid));
|
|
259
|
+
if (result.ok)
|
|
260
|
+
return { ok: true };
|
|
261
|
+
for (const line of (result.stderrTail ?? '').split(/\r?\n/).filter(Boolean).slice(-8)) {
|
|
262
|
+
reportLine(` | ${line}`);
|
|
263
|
+
}
|
|
264
|
+
return { ok: false, reason: result.reason ?? 'unknown' };
|
|
265
|
+
}
|
|
202
266
|
async function requireFreshSourceBuild() {
|
|
203
267
|
if (!sourceBuildNeedsRefresh())
|
|
204
268
|
return true;
|
|
205
269
|
return await runSourceBuild();
|
|
206
270
|
}
|
|
207
|
-
function requireCompletedDataMigration(root) {
|
|
271
|
+
function requireCompletedDataMigration(root, action = 'start') {
|
|
208
272
|
const requirement = inspectDataMigrationRequirement(root);
|
|
209
273
|
if (!requirement.required)
|
|
210
274
|
return true;
|
|
211
|
-
console.error(
|
|
275
|
+
console.error(action === 'restart'
|
|
276
|
+
? `❌ 检测到 ${requirement.operationCount} 项待确认的用户数据迁移,本次不停止当前服务。`
|
|
277
|
+
: `❌ 检测到 ${requirement.operationCount} 项待确认的用户数据迁移,服务未启动。`);
|
|
212
278
|
console.error(' 先执行: ec data migrate --dry-run');
|
|
213
279
|
console.error(' 确认后: ec data migrate --apply');
|
|
214
280
|
console.error(' 迁移成功并校验后,再执行 ec start 或 ec restart。');
|
|
281
|
+
if (action === 'restart')
|
|
282
|
+
console.error(' 本次 ec restart 仅完成只读检查,未停止当前服务。');
|
|
215
283
|
process.exitCode = 2;
|
|
216
284
|
return false;
|
|
217
285
|
}
|
|
286
|
+
/**
|
|
287
|
+
* Check package updates before starting or restarting the daemon.
|
|
288
|
+
* A failed upgrade is reported, but the currently installed version remains
|
|
289
|
+
* usable and the caller continues with its normal lifecycle.
|
|
290
|
+
*/
|
|
291
|
+
export async function runUpgradeChecks(upgradeFn = signal => tryUpgrade({ signal }), ecwebUpgradeFn = signal => tryUpgradeGlobalPkg(() => resolveGlobalPkg(WEB_PACKAGE_NAME), WEB_PACKAGE_NAME, { signal }), timeoutMs = 60_000) {
|
|
292
|
+
console.log('📦 Checking for updates...');
|
|
293
|
+
const runWithTimeout = async (label, fn) => {
|
|
294
|
+
const controller = new AbortController();
|
|
295
|
+
let timer;
|
|
296
|
+
try {
|
|
297
|
+
return await Promise.race([
|
|
298
|
+
fn(controller.signal),
|
|
299
|
+
new Promise(resolve => {
|
|
300
|
+
timer = setTimeout(() => {
|
|
301
|
+
resolve({ status: 'failed', error: `${label} upgrade timed out` });
|
|
302
|
+
controller.abort();
|
|
303
|
+
}, timeoutMs);
|
|
304
|
+
}),
|
|
305
|
+
]);
|
|
306
|
+
}
|
|
307
|
+
catch (error) {
|
|
308
|
+
return { status: 'failed', error: error instanceof Error ? error.message : String(error) };
|
|
309
|
+
}
|
|
310
|
+
finally {
|
|
311
|
+
if (timer)
|
|
312
|
+
clearTimeout(timer);
|
|
313
|
+
}
|
|
314
|
+
};
|
|
315
|
+
const upgrade = await runWithTimeout('EvolCore', upgradeFn);
|
|
316
|
+
switch (upgrade.status) {
|
|
317
|
+
case 'upgraded':
|
|
318
|
+
console.log(`✅ Upgraded: ${upgrade.from} → ${upgrade.to}`);
|
|
319
|
+
break;
|
|
320
|
+
case 'no-update':
|
|
321
|
+
console.log(`✓ Already up to date (${upgrade.from})`);
|
|
322
|
+
break;
|
|
323
|
+
case 'skipped':
|
|
324
|
+
console.log(upgrade.error
|
|
325
|
+
? '⏭ Skipped upgrade (network unavailable)'
|
|
326
|
+
: '⏭ Skipped upgrade check (dev mode)');
|
|
327
|
+
break;
|
|
328
|
+
case 'failed':
|
|
329
|
+
console.log(`⚠ Upgrade failed${upgrade.from && upgrade.to ? ` (${upgrade.from} → ${upgrade.to})` : ''}, continuing with current version${upgrade.error ? `: ${upgrade.error}` : ''}`);
|
|
330
|
+
break;
|
|
331
|
+
}
|
|
332
|
+
const ecwebUpgrade = await runWithTimeout(WEB_PACKAGE_NAME, ecwebUpgradeFn);
|
|
333
|
+
switch (ecwebUpgrade.status) {
|
|
334
|
+
case 'upgraded':
|
|
335
|
+
console.log(`✅ ${WEB_PACKAGE_NAME} upgraded: ${ecwebUpgrade.from} → ${ecwebUpgrade.to}`);
|
|
336
|
+
break;
|
|
337
|
+
case 'no-update':
|
|
338
|
+
break;
|
|
339
|
+
case 'failed':
|
|
340
|
+
console.log(`⚠ ${WEB_PACKAGE_NAME} upgrade failed${ecwebUpgrade.from && ecwebUpgrade.to ? ` (${ecwebUpgrade.from} → ${ecwebUpgrade.to})` : ''}${ecwebUpgrade.error ? `: ${ecwebUpgrade.error}` : ''}`);
|
|
341
|
+
break;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
218
344
|
// ==================== Commands ====================
|
|
219
345
|
/**
|
|
220
346
|
* 检测并展示跨 HOME 残留的 evolcore 主进程。
|
|
@@ -322,6 +448,14 @@ export async function cmdStart(opts = {}) {
|
|
|
322
448
|
const p = resolvePaths();
|
|
323
449
|
const existingDaemon = await probeDaemon(p.socket);
|
|
324
450
|
if (existingDaemon) {
|
|
451
|
+
let hookRepaired = false;
|
|
452
|
+
if (!ensureCodexHookBeforeLifecycle(() => { hookRepaired = true; }))
|
|
453
|
+
return;
|
|
454
|
+
if (hookRepaired) {
|
|
455
|
+
console.log('🔄 Codex 运行组件已补齐,正在自动重启服务以加载策略...');
|
|
456
|
+
await cmdRestart({ diagnose: opts.diagnose });
|
|
457
|
+
return;
|
|
458
|
+
}
|
|
325
459
|
console.log(` EvolCore is already running (PID: ${existingDaemon.pid}, IPC reachable)`);
|
|
326
460
|
console.log(` 已在运行;已运行时长: ${resolveRunningDuration(existingDaemon.pid, existingDaemon.uptime)};部署/脚本可直接复用当前实例`);
|
|
327
461
|
return;
|
|
@@ -329,6 +463,9 @@ export async function cmdStart(opts = {}) {
|
|
|
329
463
|
ensureDataDirs();
|
|
330
464
|
if (!await requireFreshSourceBuild())
|
|
331
465
|
return;
|
|
466
|
+
// A stopped container may miss the daily upgrade trigger. Check once on
|
|
467
|
+
// every real start so the service does not stay on an old installation.
|
|
468
|
+
await (opts.upgradeChecks ?? runUpgradeChecks)();
|
|
332
469
|
// 旧配置自动迁移(daemon.json → 新结构)
|
|
333
470
|
const { autoMigrateIfNeeded } = await import('../config-store.js');
|
|
334
471
|
autoMigrateIfNeeded();
|
|
@@ -357,6 +494,11 @@ export async function cmdStart(opts = {}) {
|
|
|
357
494
|
console.log('\n初始化完成,开始启动 EvolCore ....');
|
|
358
495
|
}
|
|
359
496
|
}
|
|
497
|
+
// The initialization path can create the first Codex agent, so perform the
|
|
498
|
+
// machine-hook check after config migration/auto-init and before any daemon
|
|
499
|
+
// process is launched.
|
|
500
|
+
if (!ensureCodexHookBeforeLifecycle())
|
|
501
|
+
return;
|
|
360
502
|
// 控制 AID 门禁:缺 aid 且交互式 → 只补全控制 AID + owners(不重走 baseagent 向导)。
|
|
361
503
|
// 非 TTY(restart-monitor/systemd/管道)不补全(无法交互),只提示后继续启动,daemon 侧 warn 兜底。
|
|
362
504
|
const daemonCfgStart = loadDaemonConfig();
|
|
@@ -691,6 +833,8 @@ export async function cmdStop() {
|
|
|
691
833
|
export async function cmdRestart(opts = {}) {
|
|
692
834
|
const cmdStartedAt = Date.now();
|
|
693
835
|
console.log('🔄 Restarting EvolCore...');
|
|
836
|
+
if (!ensureCodexHookBeforeLifecycle())
|
|
837
|
+
return;
|
|
694
838
|
const initialStatus = scanInstances();
|
|
695
839
|
const initialOrphans = findOrphanProcesses();
|
|
696
840
|
const initialRuntimeOrphans = initialOrphans.filter(orphan => isRuntimeOrphan(orphan));
|
|
@@ -702,40 +846,12 @@ export async function cmdRestart(opts = {}) {
|
|
|
702
846
|
if (initialRuntimeOrphans.length === 0 && rejectPidIsolatedLifecycle('restart', initialStatus, ping))
|
|
703
847
|
return;
|
|
704
848
|
}
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
//
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
case 'upgraded':
|
|
712
|
-
console.log(`✅ Upgraded: ${upgrade.from} → ${upgrade.to}`);
|
|
713
|
-
break;
|
|
714
|
-
case 'no-update':
|
|
715
|
-
console.log(`✓ Already up to date (${upgrade.from})`);
|
|
716
|
-
break;
|
|
717
|
-
case 'skipped':
|
|
718
|
-
console.log(upgrade.error
|
|
719
|
-
? '⏭ Skipped upgrade (network unavailable)'
|
|
720
|
-
: '⏭ Skipped upgrade check (dev mode)');
|
|
721
|
-
break;
|
|
722
|
-
case 'failed':
|
|
723
|
-
console.log(`⚠ Upgrade failed (${upgrade.from} → ${upgrade.to}), continuing with current version`);
|
|
724
|
-
break;
|
|
725
|
-
}
|
|
726
|
-
// EC Web 是独立全局包,已安装时单独检查升级。
|
|
727
|
-
const ecwebUpgrade = await tryUpgradeGlobalPkg(() => resolveGlobalPkg(WEB_PACKAGE_NAME), WEB_PACKAGE_NAME);
|
|
728
|
-
switch (ecwebUpgrade.status) {
|
|
729
|
-
case 'upgraded':
|
|
730
|
-
console.log(`✅ ${WEB_PACKAGE_NAME} upgraded: ${ecwebUpgrade.from} → ${ecwebUpgrade.to}`);
|
|
731
|
-
break;
|
|
732
|
-
case 'no-update':
|
|
733
|
-
break; // silent
|
|
734
|
-
case 'failed':
|
|
735
|
-
console.log(`⚠ ${WEB_PACKAGE_NAME} upgrade failed (${ecwebUpgrade.from} → ${ecwebUpgrade.to})`);
|
|
736
|
-
break;
|
|
737
|
-
}
|
|
738
|
-
if (!requireCompletedDataMigration(resolveRoot()))
|
|
849
|
+
// 构建交给 restart-monitor:它在 daemon 停止之后才编译,dist/ 不会被活进程
|
|
850
|
+
// 占用,失败还能回滚到上一份产物。这里再编译一次会落在事务保护之外。
|
|
851
|
+
// Keep restart behavior identical to startup: both paths use one upgrade
|
|
852
|
+
// check implementation and continue when an upgrade is unavailable.
|
|
853
|
+
await runUpgradeChecks();
|
|
854
|
+
if (!requireCompletedDataMigration(resolveRoot(), 'restart'))
|
|
739
855
|
return;
|
|
740
856
|
// 停止所有活 main 进程(可能不止一个)
|
|
741
857
|
const status = scanInstances();
|
|
@@ -815,11 +931,30 @@ export async function cmdRestart(opts = {}) {
|
|
|
815
931
|
}
|
|
816
932
|
}
|
|
817
933
|
console.log(`⏱ restart prep done in ${((Date.now() - cmdStartedAt) / 1000).toFixed(1)}s, starting...`);
|
|
818
|
-
// 通过 restart-monitor 启动,避免在 agent
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
934
|
+
// 通过 restart-monitor 启动,避免在 agent 内调用时自杀。
|
|
935
|
+
// 开发仓下 monitor 从源码跑;安装包下先复制到 stage 目录,两种情况都让看门狗
|
|
936
|
+
// 不住在会被替换的产物里。
|
|
937
|
+
const monitorEntry = platform.resolveMonitorLaunch(getPackageRoot(), path.join(resolvePaths().root, 'data', 'monitor-stage'));
|
|
938
|
+
if (monitorEntry.note)
|
|
939
|
+
console.log(`ℹ ${monitorEntry.note}`);
|
|
940
|
+
if (!fs.existsSync(monitorEntry.entry)) {
|
|
941
|
+
console.error(`❌ 重启中止:找不到 restart-monitor 入口 ${monitorEntry.entry}`);
|
|
942
|
+
console.error(' 请先执行 npm run build 重建产物');
|
|
943
|
+
process.exitCode = 1;
|
|
944
|
+
return;
|
|
945
|
+
}
|
|
946
|
+
const monitorStarted = await spawnRestartMonitorAndConfirm(monitorEntry, log => console.error(log));
|
|
947
|
+
if (!monitorStarted.ok) {
|
|
948
|
+
if (monitorEntry.stageDir)
|
|
949
|
+
platform.cleanupMonitorStage(monitorEntry.stageDir);
|
|
950
|
+
console.error(`❌ 重启中止:restart-monitor 未能启动(${monitorStarted.reason})`);
|
|
951
|
+
if (fs.existsSync(path.join(getPackageRoot(), 'src', 'cli', 'index.ts'))) {
|
|
952
|
+
console.error(' 源码可能存在语法错误导致 tsx 无法加载。请执行 npx tsc --noEmit 检查。');
|
|
953
|
+
}
|
|
954
|
+
console.error(' 服务已停止,请修复后执行 ec start。');
|
|
955
|
+
process.exitCode = 1;
|
|
956
|
+
return;
|
|
957
|
+
}
|
|
823
958
|
console.log('🔄 Restart monitor started, waiting for service to come back online...');
|
|
824
959
|
await sleep(2000);
|
|
825
960
|
// 代码统计(开发环境)
|
|
@@ -993,7 +1128,11 @@ export async function cmdStatus() {
|
|
|
993
1128
|
// Start all read-only daemon queries together. On a busy Windows host each
|
|
994
1129
|
// query can otherwise wait behind the same IPC/event-loop delay and make the
|
|
995
1130
|
// total `ec status` time the sum of several individual timeouts.
|
|
996
|
-
|
|
1131
|
+
let ipcFailure;
|
|
1132
|
+
const pingPromise = probeDaemon(p.socket, DAEMON_PROBE_TIMEOUT_MS, true).catch(error => {
|
|
1133
|
+
ipcFailure = error;
|
|
1134
|
+
return null;
|
|
1135
|
+
});
|
|
997
1136
|
const statusPromise = ipcQuery(p.socket, { type: 'status' }).catch(() => null);
|
|
998
1137
|
const aidsPromise = ipcQuery(p.socket, { type: 'aun-aids' }).catch(() => null);
|
|
999
1138
|
const agentsPromise = ipcQuery(p.socket, { type: 'evolagent.list' }).catch(() => null);
|
|
@@ -1010,6 +1149,11 @@ export async function cmdStatus() {
|
|
|
1010
1149
|
}
|
|
1011
1150
|
if (pid) {
|
|
1012
1151
|
printStartupInfo({ pid, running: true });
|
|
1152
|
+
if (!ping) {
|
|
1153
|
+
console.log('⚠ 仅确认 daemon PID 存活,IPC 不可用,不能据此判断服务健康。');
|
|
1154
|
+
if (ipcFailure)
|
|
1155
|
+
console.log(` ${ipcFailure instanceof Error ? ipcFailure.message : String(ipcFailure)}`);
|
|
1156
|
+
}
|
|
1013
1157
|
console.log('');
|
|
1014
1158
|
console.log('📊 Process Info:');
|
|
1015
1159
|
if (processVisible) {
|
|
@@ -1120,7 +1264,9 @@ export async function cmdStatus() {
|
|
|
1120
1264
|
}
|
|
1121
1265
|
}
|
|
1122
1266
|
else {
|
|
1123
|
-
console.log('⚠ EvolCore is not running');
|
|
1267
|
+
console.log(ipcFailure ? '⚠ 无法确认 EvolCore 运行状态:IPC 不可用,且没有可见的存活 PID。' : '⚠ EvolCore is not running');
|
|
1268
|
+
if (ipcFailure)
|
|
1269
|
+
console.log(` ${ipcFailure instanceof Error ? ipcFailure.message : String(ipcFailure)}`);
|
|
1124
1270
|
}
|
|
1125
1271
|
// Session & Project statistics (从文件系统读)
|
|
1126
1272
|
if (fs.existsSync(p.sessionsDir)) {
|
|
@@ -2667,7 +2813,7 @@ async function stopEcwebIfRunning(p) {
|
|
|
2667
2813
|
throw new Error(`EC Web old process or port ${port} could not be released`);
|
|
2668
2814
|
}
|
|
2669
2815
|
// 清理 pid 文件(仅 ecweb-<pid>.json / watch-web-<pid>.json,
|
|
2670
|
-
// 不碰
|
|
2816
|
+
// 不碰 daemon-owned control-access-tokens.json 等非 pid 文件,否则会清空凭据库导致每次重启都要重新配对)
|
|
2671
2817
|
removeEcwebInstanceFiles(p);
|
|
2672
2818
|
return pids.size > 0;
|
|
2673
2819
|
}
|
|
@@ -2759,23 +2905,32 @@ export async function startEcwebIfEnabled(p, options = {}) {
|
|
|
2759
2905
|
fs.mkdirSync(p.instanceDir, { recursive: true });
|
|
2760
2906
|
const startedAt = getProcessStartTime(pid) ?? Date.now();
|
|
2761
2907
|
fs.writeFileSync(path.join(p.instanceDir, `ecweb-${pid}.json`), JSON.stringify({ pid, port, startedAt, startedAtIso: new Date(startedAt).toISOString() }, null, 2));
|
|
2762
|
-
// 轮询端口确认 HTTP 服务真正就绪(spawn 成功 ≠
|
|
2908
|
+
// 轮询端口确认 HTTP 服务真正就绪(spawn 成功 ≠ 端口绑定成功)。
|
|
2909
|
+
// 配对码由 daemon 提供,daemon 尚未启动时不应把已经就绪的 ECWeb 杀掉。
|
|
2910
|
+
let ready = false;
|
|
2763
2911
|
let pair = null;
|
|
2764
2912
|
for (let i = 0; i < 20; i++) {
|
|
2765
|
-
|
|
2766
|
-
if (
|
|
2913
|
+
ready = await probeEcwebReady(port);
|
|
2914
|
+
if (ready) {
|
|
2915
|
+
pair = await fetchRemotePairingCode();
|
|
2767
2916
|
break;
|
|
2917
|
+
}
|
|
2768
2918
|
await sleep(250);
|
|
2769
2919
|
}
|
|
2770
|
-
if (
|
|
2920
|
+
if (ready) {
|
|
2771
2921
|
const actualStartedAt = getProcessStartTime(pid);
|
|
2772
2922
|
if (actualStartedAt !== null) {
|
|
2773
2923
|
fs.writeFileSync(path.join(p.instanceDir, `ecweb-${pid}.json`), JSON.stringify({ pid, port, startedAt: actualStartedAt, startedAtIso: new Date(actualStartedAt).toISOString() }, null, 2));
|
|
2774
2924
|
}
|
|
2775
|
-
const mins = Math.max(0, Math.round((pair.expiresAt - Date.now()) / 60000));
|
|
2776
2925
|
const actionLabel = wasRunning ? '已重启' : '启动成功';
|
|
2777
2926
|
console.log(`✓ ECWeb ${actionLabel} (PID: ${pid}) http://localhost:${port}`);
|
|
2778
|
-
|
|
2927
|
+
if (pair) {
|
|
2928
|
+
const mins = Math.max(0, Math.round((pair.expiresAt - Date.now()) / 60000));
|
|
2929
|
+
console.log(` 配对码: ${pair.code} (约 ${mins} 分钟内有效,已配对过的浏览器无需重新配对)`);
|
|
2930
|
+
}
|
|
2931
|
+
else {
|
|
2932
|
+
console.log(' daemon 尚未提供配对码;EvolCore 启动后可通过 /pair 或 ECWeb 页面获取');
|
|
2933
|
+
}
|
|
2779
2934
|
return true;
|
|
2780
2935
|
}
|
|
2781
2936
|
console.log(`❌ ECWeb 启动失败:端口 ${port} 未就绪(进程 PID ${pid} 可能已退出,查看 logs/watch-web.log)`);
|
|
@@ -2801,13 +2956,13 @@ async function printEcwebAccess(port) {
|
|
|
2801
2956
|
console.log(`🔭 ECWeb http://localhost:${port}`);
|
|
2802
2957
|
let pair = null;
|
|
2803
2958
|
for (let i = 0; i < 10 && !pair; i++) {
|
|
2804
|
-
pair = await
|
|
2959
|
+
pair = await fetchRemotePairingCode();
|
|
2805
2960
|
if (!pair)
|
|
2806
2961
|
await sleep(300);
|
|
2807
2962
|
}
|
|
2808
2963
|
if (pair) {
|
|
2809
2964
|
const mins = Math.max(0, Math.round((pair.expiresAt - Date.now()) / 60000));
|
|
2810
|
-
console.log(` 配对码: ${pair.code} (约 ${mins}
|
|
2965
|
+
console.log(` 配对码: ${pair.code} (约 ${mins} 分钟内有效,配对后远程访问 token 缓存 30 天)`);
|
|
2811
2966
|
}
|
|
2812
2967
|
else {
|
|
2813
2968
|
console.log(' 配对码: 暂不可用(稍后重试 ec watch web,或查看 logs/watch-web.log)');
|
|
@@ -2857,7 +3012,12 @@ async function cmdWatchWeb() {
|
|
|
2857
3012
|
const cfg = loadDaemonConfig();
|
|
2858
3013
|
const currentService = ecwebService(cfg.services);
|
|
2859
3014
|
const port = currentService?.port ?? DEFAULT_ECWEB_PORT;
|
|
2860
|
-
|
|
3015
|
+
// Avoid rewriting daemon.json on every invocation once ECWeb is already
|
|
3016
|
+
// enabled on the requested port. The persisted config is the daemon's
|
|
3017
|
+
// lifecycle source of truth, but an unchanged desired state needs no write.
|
|
3018
|
+
if (currentService?.enabled !== true || currentService.port !== port) {
|
|
3019
|
+
saveDaemonConfig({ ...cfg, services: setEcwebEnabled(cfg.services, true, port) });
|
|
3020
|
+
}
|
|
2861
3021
|
const ok = await startEcwebIfEnabled(p, { forceRestart: true });
|
|
2862
3022
|
if (!ok)
|
|
2863
3023
|
process.exit(1); // 失败原因已由 startEcwebIfEnabled 打印
|
|
@@ -2896,9 +3056,10 @@ export async function cmdMv(oldDir, newDir) {
|
|
|
2896
3056
|
}
|
|
2897
3057
|
}
|
|
2898
3058
|
// ==================== Diagnose ====================
|
|
2899
|
-
export async function cmdDiagnose() {
|
|
3059
|
+
export async function cmdDiagnose(args = []) {
|
|
2900
3060
|
const p = resolvePaths();
|
|
2901
3061
|
let hasError = false;
|
|
3062
|
+
const runHookLaunchProbe = args.includes('--codex-hook');
|
|
2902
3063
|
// 1. 检查数据目录
|
|
2903
3064
|
console.log(`[diagnose] EVOLCORE_HOME = ${p.root}`);
|
|
2904
3065
|
if (!fs.existsSync(p.root)) {
|
|
@@ -2924,7 +3085,74 @@ export async function cmdDiagnose() {
|
|
|
2924
3085
|
else {
|
|
2925
3086
|
console.log(`[diagnose] ✓ 已加载 ${agents.length} 个 self-agent`);
|
|
2926
3087
|
}
|
|
2927
|
-
// 3.
|
|
3088
|
+
// 3. 检查实际使用 Codex 的 Agent 项目目录。项目路径属于 Agent 的
|
|
3089
|
+
// effective config,逐个输出 AID,避免只检查第一个 agent 掩盖坏配置。
|
|
3090
|
+
const codexAgents = agents.filter(agent => {
|
|
3091
|
+
try {
|
|
3092
|
+
return resolveEffective({ self: agent.aid }).active_baseagent === 'codex';
|
|
3093
|
+
}
|
|
3094
|
+
catch (error) {
|
|
3095
|
+
console.error(`[diagnose] ❌ Agent ${agent.aid} effective config 解析失败: ${error instanceof Error ? error.message : String(error)}`);
|
|
3096
|
+
hasError = true;
|
|
3097
|
+
return false;
|
|
3098
|
+
}
|
|
3099
|
+
});
|
|
3100
|
+
for (const agent of codexAgents) {
|
|
3101
|
+
try {
|
|
3102
|
+
const effective = resolveEffective({ self: agent.aid });
|
|
3103
|
+
const check = checkCodexProjectPath(effective.projects?.defaultPath);
|
|
3104
|
+
if (check.status === 'ok') {
|
|
3105
|
+
console.log(`[diagnose] ✓ Codex 项目路径 ${agent.aid}: ${check.path}`);
|
|
3106
|
+
}
|
|
3107
|
+
else {
|
|
3108
|
+
console.error(`[diagnose] ❌ Codex 项目路径 ${agent.aid}: ${check.path || '(未配置)'} (${check.reason || check.status})`);
|
|
3109
|
+
hasError = true;
|
|
3110
|
+
}
|
|
3111
|
+
for (const warning of check.warnings)
|
|
3112
|
+
console.warn(`[diagnose] ⚠️ Codex 项目路径 ${agent.aid}: ${warning}`);
|
|
3113
|
+
}
|
|
3114
|
+
catch (error) {
|
|
3115
|
+
console.error(`[diagnose] ❌ Codex 项目路径检查失败 ${agent.aid}: ${error instanceof Error ? error.message : String(error)}`);
|
|
3116
|
+
hasError = true;
|
|
3117
|
+
}
|
|
3118
|
+
}
|
|
3119
|
+
if (codexAgents.length === 0) {
|
|
3120
|
+
console.log('[diagnose] - Codex managed hook: 跳过(没有 Agent 使用 Codex)');
|
|
3121
|
+
}
|
|
3122
|
+
else {
|
|
3123
|
+
const hook = inspectCodexManagedHook({ packageRoot: getPackageRoot() });
|
|
3124
|
+
if (hook.status === 'installed') {
|
|
3125
|
+
console.log(`[diagnose] ✓ Codex managed hook 已安装: ${hook.hookPath}`);
|
|
3126
|
+
if (runHookLaunchProbe) {
|
|
3127
|
+
try {
|
|
3128
|
+
const launch = runCodexHookLaunchDiagnostic({ packageRoot: getPackageRoot() });
|
|
3129
|
+
if (launch.status === 'ok') {
|
|
3130
|
+
console.log('[diagnose] ✓ Codex managed hook 启动探针报告: ' + launch.output);
|
|
3131
|
+
for (const result of launch.results) {
|
|
3132
|
+
console.log('[diagnose] ' + result.label + ': exit=' + result.exitCode + ' pid=' + (result.pid ?? 'unknown'));
|
|
3133
|
+
}
|
|
3134
|
+
}
|
|
3135
|
+
else {
|
|
3136
|
+
console.log('[diagnose] - Codex managed hook 启动探针: 跳过(' + (launch.reason || 'not applicable') + ')');
|
|
3137
|
+
}
|
|
3138
|
+
}
|
|
3139
|
+
catch (error) {
|
|
3140
|
+
console.error('[diagnose] ❌ Codex managed hook 启动探针失败: ' + (error instanceof Error ? error.message : String(error)));
|
|
3141
|
+
hasError = true;
|
|
3142
|
+
}
|
|
3143
|
+
}
|
|
3144
|
+
}
|
|
3145
|
+
else if (hook.status === 'skipped') {
|
|
3146
|
+
console.log(`[diagnose] - Codex managed hook: 跳过(${hook.reason})`);
|
|
3147
|
+
}
|
|
3148
|
+
else {
|
|
3149
|
+
const prefix = hook.status === 'missing' ? '⚠️' : '❌';
|
|
3150
|
+
const writer = hook.status === 'missing' ? console.warn : console.error;
|
|
3151
|
+
writer(`[diagnose] ${prefix} Codex managed hook ${hook.status}: ${hook.reason || 'unknown error'}${hook.requirements ? ` (${hook.requirements})` : ''}`);
|
|
3152
|
+
hasError = true;
|
|
3153
|
+
}
|
|
3154
|
+
}
|
|
3155
|
+
// 4. 检查 Anthropic 配置(用首个 self-agent 的 effective config)
|
|
2928
3156
|
if (agents.length > 0) {
|
|
2929
3157
|
try {
|
|
2930
3158
|
const merged = resolveEffective({ self: agents[0].aid });
|
package/dist/cli/index.js
CHANGED
|
@@ -46,6 +46,7 @@ AUN:
|
|
|
46
46
|
fs AUN 文件系统
|
|
47
47
|
storage 底层对象存储命令
|
|
48
48
|
rpc 调用底层 AUN RPC
|
|
49
|
+
menu-client 向远程 daemon 发送标准 Menu 请求
|
|
49
50
|
contact 联系人黑名单管理
|
|
50
51
|
net AUN 网络链路诊断
|
|
51
52
|
|
|
@@ -79,7 +80,7 @@ function printLifecycleHelp(command) {
|
|
|
79
80
|
logs: `用法: ec logs [选项]\n\n --level <error|warn|info|debug> 最低日志级别\n --module <name> 只显示指定模块\n --raw 原始输出\n --no-color 禁用颜色`,
|
|
80
81
|
watch: `用法: ec watch [logs|log|aid|msg|web|session]\n\n ec watch 打开监控选择菜单\n ec watch logs 选择日志类型后监控\n ec watch log <类型...> 直接监控指定日志类型\n ec watch aid 监控 AID 连接\n ec watch msg 打开消息 TUI\n ec watch web 安装/升级并启动 EC Web\n ec watch session ec watch web 的兼容别名`,
|
|
81
82
|
'restart-monitor': '用法: ec restart-monitor\n\n运行内 daemon 重启与升级监控器。',
|
|
82
|
-
diagnose: '用法: ec diagnose\n\n
|
|
83
|
+
diagnose: '用法: ec diagnose [--codex-hook]\n\n检查配置、数据库、进程和编译产物。\n\n --codex-hook 运行 Windows Codex managed hook 启动探针。',
|
|
83
84
|
mv: '用法: ec mv <old-directory> <new-directory>\n\n迁移项目目录以及 Claude、Codex 和 EvolCore 会话路径。',
|
|
84
85
|
};
|
|
85
86
|
console.log(help[command]);
|
|
@@ -308,7 +309,7 @@ export async function main(args) {
|
|
|
308
309
|
printLifecycleHelp('diagnose');
|
|
309
310
|
break;
|
|
310
311
|
}
|
|
311
|
-
await cmdDiagnose();
|
|
312
|
+
await cmdDiagnose(args.slice(1));
|
|
312
313
|
break;
|
|
313
314
|
case 'data': {
|
|
314
315
|
const { cmdData } = await import('./data-command.js');
|
|
@@ -350,6 +351,25 @@ export async function main(args) {
|
|
|
350
351
|
await cmdRpc(args.slice(1));
|
|
351
352
|
break;
|
|
352
353
|
}
|
|
354
|
+
case 'menu-client': {
|
|
355
|
+
const { suppressSdkLogs } = await import('../aun/aid/index.js');
|
|
356
|
+
suppressSdkLogs();
|
|
357
|
+
const { cmdMenuClient } = await import('./menu-client.js');
|
|
358
|
+
try {
|
|
359
|
+
await cmdMenuClient(args.slice(1));
|
|
360
|
+
}
|
|
361
|
+
catch (error) {
|
|
362
|
+
const payload = {
|
|
363
|
+
ok: false,
|
|
364
|
+
code: typeof error?.code === 'string' ? error.code : 'MENU_CLIENT_FAILED',
|
|
365
|
+
error: error?.message ?? String(error),
|
|
366
|
+
...(error?.data === undefined ? {} : { data: error.data }),
|
|
367
|
+
};
|
|
368
|
+
console.error(JSON.stringify(payload));
|
|
369
|
+
process.exitCode = 1;
|
|
370
|
+
}
|
|
371
|
+
break;
|
|
372
|
+
}
|
|
353
373
|
case 'storage': {
|
|
354
374
|
const { suppressSdkLogs } = await import('../aun/aid/index.js');
|
|
355
375
|
suppressSdkLogs();
|
package/dist/cli/init.js
CHANGED
|
@@ -14,6 +14,7 @@ import { defaultProjectsRoot } from '../utils/project-path.js';
|
|
|
14
14
|
import { WEB_CLI_BIN, WEB_PACKAGE_LATEST } from '../product.js';
|
|
15
15
|
import { autostartInstalled, autostartPlatformLabel, configureAutostart } from '../utils/autostart.js';
|
|
16
16
|
import { askInit, isInitCancelledError, printInitCancelHint } from './init-cancel.js';
|
|
17
|
+
import { AUTO_BASEAGENT_CANDIDATES, BASEAGENT_CANDIDATES, detectAvailableBaseagents, } from '../agents/baseagent-availability.js';
|
|
17
18
|
// ==================== Helpers ====================
|
|
18
19
|
function ask(rl, question) {
|
|
19
20
|
return askInit(rl, question);
|
|
@@ -27,17 +28,8 @@ export function expandLeadingTildePath(value, homeDir = os.homedir()) {
|
|
|
27
28
|
}
|
|
28
29
|
return value;
|
|
29
30
|
}
|
|
30
|
-
const BASEAGENT_CANDIDATES = ['claude', 'codex', 'gemini', 'ecagent'];
|
|
31
|
-
const AUTO_BASEAGENT_CANDIDATES = ['claude', 'codex', 'gemini'];
|
|
32
|
-
function isBaseagentAvailable(baseagent, codexAvailability) {
|
|
33
|
-
if (baseagent === 'codex')
|
|
34
|
-
return (codexAvailability ?? getCodexAppServerAvailability()).available;
|
|
35
|
-
if (baseagent === 'ecagent')
|
|
36
|
-
return true;
|
|
37
|
-
return commandExists(baseagent);
|
|
38
|
-
}
|
|
39
31
|
function detectAvailable(codexAvailability = getCodexAppServerAvailability()) {
|
|
40
|
-
return
|
|
32
|
+
return detectAvailableBaseagents(codexAvailability);
|
|
41
33
|
}
|
|
42
34
|
/** Offer installation only when the probe positively identified an old CLI. */
|
|
43
35
|
export async function maybeUpgradeCodexForInit(availability, dependencies) {
|
|
@@ -12,6 +12,9 @@ const MANAGED_TASK_COMMANDS = new Set([
|
|
|
12
12
|
'msg',
|
|
13
13
|
'queue',
|
|
14
14
|
'response',
|
|
15
|
+
// `stats` reads the usage database directly. It authorizes each managed call
|
|
16
|
+
// against the task's relation role in `resolveManagedStatsScope`.
|
|
17
|
+
'stats',
|
|
15
18
|
'status',
|
|
16
19
|
'storage',
|
|
17
20
|
'trigger',
|