@okxweb3/a2a-node 0.2.2 → 0.2.3-beta-fa7df0d9e7-260810102542
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/cli.js +21515 -20340
- package/dist/index.js +2399 -1080
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3200,7 +3200,14 @@ function resolveCommandPath(command, env = process.env, platform = process.platf
|
|
|
3200
3200
|
}
|
|
3201
3201
|
function resolveCodexCommandPath(env = process.env, storedCommand, platform = process.platform, probe = probeCodexCommand) {
|
|
3202
3202
|
if (platform !== "win32") {
|
|
3203
|
-
|
|
3203
|
+
if (storedCommand && commandExists(storedCommand, env, platform)) {
|
|
3204
|
+
return storedCommand;
|
|
3205
|
+
}
|
|
3206
|
+
const onPath = findExecutable("codex", env, platform);
|
|
3207
|
+
if (onPath) {
|
|
3208
|
+
return onPath;
|
|
3209
|
+
}
|
|
3210
|
+
return collectCodexAppBundledCandidates(env, platform).find(isExecutable) ?? null;
|
|
3204
3211
|
}
|
|
3205
3212
|
for (const candidate of collectCodexCommandCandidates(env, storedCommand, platform)) {
|
|
3206
3213
|
const result = probe(candidate, env, platform);
|
|
@@ -3225,6 +3232,9 @@ function collectCodexCommandCandidates(env = process.env, storedCommand, platfor
|
|
|
3225
3232
|
}
|
|
3226
3233
|
if (platform !== "win32") {
|
|
3227
3234
|
add(findExecutable("codex", env, platform));
|
|
3235
|
+
for (const candidate of collectCodexAppBundledCandidates(env, platform).filter(isExecutable)) {
|
|
3236
|
+
add(candidate);
|
|
3237
|
+
}
|
|
3228
3238
|
return candidates;
|
|
3229
3239
|
}
|
|
3230
3240
|
for (const candidate of localAppDataCodexCandidates(env)) {
|
|
@@ -3246,6 +3256,87 @@ function collectCodexCommandCandidates(env = process.env, storedCommand, platfor
|
|
|
3246
3256
|
}
|
|
3247
3257
|
return candidates;
|
|
3248
3258
|
}
|
|
3259
|
+
function collectCodexAppBundledCandidates(env = process.env, platform = process.platform) {
|
|
3260
|
+
if (platform === "win32") {
|
|
3261
|
+
return localAppDataCodexCandidates(env);
|
|
3262
|
+
}
|
|
3263
|
+
const roots = (env[CODEX_APP_DIRS_ENV] ?? "").split(import_node_path10.delimiter).filter(Boolean);
|
|
3264
|
+
if (platform === "darwin") {
|
|
3265
|
+
roots.push("/Applications", (0, import_node_path10.join)(env.HOME || (0, import_node_os5.homedir)(), "Applications"));
|
|
3266
|
+
}
|
|
3267
|
+
const candidates = [];
|
|
3268
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3269
|
+
for (const root of roots) {
|
|
3270
|
+
const candidate = (0, import_node_path10.join)(root, "Codex.app", "Contents", "Resources", "codex");
|
|
3271
|
+
if (seen.has(candidate)) {
|
|
3272
|
+
continue;
|
|
3273
|
+
}
|
|
3274
|
+
seen.add(candidate);
|
|
3275
|
+
candidates.push(candidate);
|
|
3276
|
+
}
|
|
3277
|
+
return candidates;
|
|
3278
|
+
}
|
|
3279
|
+
function resolveCodexCommandCandidates(env = process.env, storedCommand, platform = process.platform) {
|
|
3280
|
+
const candidates = [];
|
|
3281
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3282
|
+
const add = (command, source) => {
|
|
3283
|
+
if (!command || seen.has(command)) {
|
|
3284
|
+
return;
|
|
3285
|
+
}
|
|
3286
|
+
seen.add(command);
|
|
3287
|
+
candidates.push({ command, source });
|
|
3288
|
+
};
|
|
3289
|
+
add(env[CODEX_COMMAND_OVERRIDE_ENV], "explicit_override");
|
|
3290
|
+
const appBundled = new Set(collectCodexAppBundledCandidates(env, platform));
|
|
3291
|
+
for (const command of collectCodexCommandCandidates(env, storedCommand, platform)) {
|
|
3292
|
+
if (storedCommand && command === storedCommand) {
|
|
3293
|
+
add(command, "persisted");
|
|
3294
|
+
continue;
|
|
3295
|
+
}
|
|
3296
|
+
add(command, appBundled.has(command) ? "app_bundled" : "path");
|
|
3297
|
+
}
|
|
3298
|
+
return candidates;
|
|
3299
|
+
}
|
|
3300
|
+
function resolveCodexCommandSource(env = process.env, storedCommand, platform = process.platform, probe = probeCodexCommand) {
|
|
3301
|
+
const override = env[CODEX_COMMAND_OVERRIDE_ENV];
|
|
3302
|
+
if (override) {
|
|
3303
|
+
return commandExists(override, env, platform) ? { command: override, source: "explicit_override" } : null;
|
|
3304
|
+
}
|
|
3305
|
+
const resolved = resolveCodexCommandPath(env, storedCommand, platform, probe);
|
|
3306
|
+
if (!resolved) {
|
|
3307
|
+
return null;
|
|
3308
|
+
}
|
|
3309
|
+
return {
|
|
3310
|
+
command: resolved,
|
|
3311
|
+
source: classifyResolvedCodexCommand(resolved, storedCommand, env, platform)
|
|
3312
|
+
};
|
|
3313
|
+
}
|
|
3314
|
+
function resolveAiProviderCommandWithSource(provider, env = process.env, storedCommand, platform = process.platform) {
|
|
3315
|
+
const override = env[`OKX_A2A_AI_${provider.toUpperCase()}_COMMAND`];
|
|
3316
|
+
if (override) {
|
|
3317
|
+
return { command: override, source: "explicit_override" };
|
|
3318
|
+
}
|
|
3319
|
+
if (provider === "codex") {
|
|
3320
|
+
const codex = resolveCodexCommandPath(env, storedCommand, platform);
|
|
3321
|
+
if (codex) {
|
|
3322
|
+
return { command: codex, source: classifyResolvedCodexCommand(codex, storedCommand, env, platform) };
|
|
3323
|
+
}
|
|
3324
|
+
}
|
|
3325
|
+
if (storedCommand && commandExists(storedCommand, env, platform)) {
|
|
3326
|
+
return { command: storedCommand, source: "persisted" };
|
|
3327
|
+
}
|
|
3328
|
+
const found = findExecutable(provider, env, platform);
|
|
3329
|
+
return found ? { command: found, source: "path" } : { command: provider, source: null };
|
|
3330
|
+
}
|
|
3331
|
+
function classifyResolvedCodexCommand(resolved, storedCommand, env, platform) {
|
|
3332
|
+
if (storedCommand && resolved === storedCommand) {
|
|
3333
|
+
return "persisted";
|
|
3334
|
+
}
|
|
3335
|
+
if (collectCodexAppBundledCandidates(env, platform).includes(resolved)) {
|
|
3336
|
+
return "app_bundled";
|
|
3337
|
+
}
|
|
3338
|
+
return "path";
|
|
3339
|
+
}
|
|
3249
3340
|
function readAiProviderTimeoutMs(env = process.env) {
|
|
3250
3341
|
const raw = env.OKX_A2A_AI_PROVIDER_TIMEOUT_MS ?? env.OKX_AGENT_TASK_AI_PROVIDER_TIMEOUT_MS;
|
|
3251
3342
|
if (!raw) {
|
|
@@ -3417,7 +3508,7 @@ function isCodexCommand(command) {
|
|
|
3417
3508
|
function isWindowsAppsPath(commandPath) {
|
|
3418
3509
|
return /(?:^|[\\/])WindowsApps(?:[\\/]|$)/i.test(commandPath);
|
|
3419
3510
|
}
|
|
3420
|
-
var import_node_fs7, import_node_os5, import_node_path10, DEFAULT_AI_PROVIDER_TIMEOUT_MS, CODEX_PROBE_TIMEOUT_MS;
|
|
3511
|
+
var import_node_fs7, import_node_os5, import_node_path10, DEFAULT_AI_PROVIDER_TIMEOUT_MS, CODEX_PROBE_TIMEOUT_MS, CODEX_APP_DIRS_ENV, CODEX_COMMAND_OVERRIDE_ENV;
|
|
3421
3512
|
var init_ai_command = __esm({
|
|
3422
3513
|
"src/ai-command.ts"() {
|
|
3423
3514
|
"use strict";
|
|
@@ -3427,918 +3518,238 @@ var init_ai_command = __esm({
|
|
|
3427
3518
|
init_win_spawn();
|
|
3428
3519
|
DEFAULT_AI_PROVIDER_TIMEOUT_MS = null;
|
|
3429
3520
|
CODEX_PROBE_TIMEOUT_MS = 1e4;
|
|
3521
|
+
CODEX_APP_DIRS_ENV = "OKX_A2A_CODEX_APP_DIRS";
|
|
3522
|
+
CODEX_COMMAND_OVERRIDE_ENV = "OKX_A2A_AI_CODEX_COMMAND";
|
|
3430
3523
|
}
|
|
3431
3524
|
});
|
|
3432
3525
|
|
|
3433
|
-
// src/
|
|
3434
|
-
function
|
|
3435
|
-
|
|
3436
|
-
if (normalized === "codex") {
|
|
3437
|
-
return "codex";
|
|
3438
|
-
}
|
|
3439
|
-
if (normalized === "claude" || normalized === "claude-code" || normalized === "claude_code") {
|
|
3440
|
-
return "claude";
|
|
3441
|
-
}
|
|
3442
|
-
if (normalized === "hermes") {
|
|
3443
|
-
return "hermes";
|
|
3444
|
-
}
|
|
3445
|
-
if (normalized === "openclaw") {
|
|
3446
|
-
return "openclaw";
|
|
3447
|
-
}
|
|
3448
|
-
throw new Error(`Unsupported AI provider "${value}". Use one of: ${AI_PROVIDERS.join(", ")}.`);
|
|
3449
|
-
}
|
|
3450
|
-
function detectAiProviders(commandExists2 = commandExists, env = process.env) {
|
|
3451
|
-
const codex = commandExists2(readProviderCommand("codex", env));
|
|
3452
|
-
const claude = commandExists2(readProviderCommand("claude", env));
|
|
3453
|
-
const hermes = commandExists2(readProviderCommand("hermes", env));
|
|
3454
|
-
const openclaw = commandExists2(readProviderCommand("openclaw", env));
|
|
3455
|
-
return {
|
|
3456
|
-
codex,
|
|
3457
|
-
claude,
|
|
3458
|
-
hermes,
|
|
3459
|
-
openclaw,
|
|
3460
|
-
available: [
|
|
3461
|
-
...codex ? ["codex"] : [],
|
|
3462
|
-
...claude ? ["claude"] : [],
|
|
3463
|
-
...hermes ? ["hermes"] : [],
|
|
3464
|
-
...openclaw ? ["openclaw"] : []
|
|
3465
|
-
]
|
|
3466
|
-
};
|
|
3526
|
+
// src/codex-readiness.ts
|
|
3527
|
+
function codexReadinessCacheKey(env = process.env, persistOverrideCommand = false) {
|
|
3528
|
+
return `codex|${env[CODEX_COMMAND_OVERRIDE_ENV2] ?? ""}${persistOverrideCommand ? "|persist-override" : ""}`;
|
|
3467
3529
|
}
|
|
3468
|
-
function
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
return normalizeAiProvider(explicit);
|
|
3472
|
-
}
|
|
3473
|
-
const runtime = detectRuntime(env);
|
|
3474
|
-
return runtime === "unknown" ? null : runtime;
|
|
3475
|
-
}
|
|
3476
|
-
function hasAiRuntimeMarker(env = process.env) {
|
|
3477
|
-
return !!(detectGatewayInvocation(env) || env.CODEX_THREAD_ID || isTruthyRuntimeMarker(env.CODEX_SHELL) || env.CODEX_MANAGED_BY_NPM || isTruthyRuntimeMarker(env.CODEX_CI) || env.CLAUDE_SESSION_ID || isTruthyRuntimeMarker(env.CLAUDECODE) || env.CLAUDE_CODE_SESSION_ID || env.CLAUDE_PLUGIN_DATA || env.HERMES_SESSION_ID && !env.HERMES_DESKTOP_CWD || env.HERMES_SESSION_KEY || env.HERMES_FLOW_ID || env.OPENCLAW_SHELL || env.OPENCLAW_CLI);
|
|
3478
|
-
}
|
|
3479
|
-
function detectRuntime(env = process.env, options = {}) {
|
|
3480
|
-
const gatewayInvocation = detectGatewayInvocation(env);
|
|
3481
|
-
const strongMatches = [
|
|
3482
|
-
...gatewayInvocation ? [gatewayInvocation] : [],
|
|
3483
|
-
...env.CODEX_THREAD_ID || isTruthyRuntimeMarker(env.CODEX_SHELL) || env.CODEX_MANAGED_BY_NPM || isTruthyRuntimeMarker(env.CODEX_CI) ? ["codex"] : [],
|
|
3484
|
-
...env.CLAUDE_SESSION_ID || isTruthyRuntimeMarker(env.CLAUDECODE) || env.CLAUDE_CODE_SESSION_ID || env.CLAUDE_PLUGIN_DATA ? ["claude"] : [],
|
|
3485
|
-
...env.HERMES_SESSION_ID && !env.HERMES_DESKTOP_CWD || env.HERMES_SESSION_KEY || env.HERMES_FLOW_ID ? ["hermes"] : [],
|
|
3486
|
-
...env.OPENCLAW_SHELL || env.OPENCLAW_CLI ? ["openclaw"] : [],
|
|
3487
|
-
...hasOpenClawParentProcess(options.parentPid ?? process.ppid, options.readParentProcessCommand ?? readParentProcessCommand) ? ["openclaw"] : []
|
|
3488
|
-
];
|
|
3489
|
-
const uniqueMatches = [...new Set(strongMatches)];
|
|
3490
|
-
if (uniqueMatches.length === 1) {
|
|
3491
|
-
return uniqueMatches[0];
|
|
3530
|
+
function buildCodexRecoveryGuidance(state, commandSource = null) {
|
|
3531
|
+
if (state === "not_authenticated" && commandSource === "app_bundled") {
|
|
3532
|
+
return { recoveryAction: "run_login", recoveryGuidance: CODEX_APP_BUNDLED_LOGIN_GUIDANCE };
|
|
3492
3533
|
}
|
|
3493
|
-
return
|
|
3534
|
+
return { ...CODEX_RECOVERY_BY_STATE[state] };
|
|
3494
3535
|
}
|
|
3495
|
-
function
|
|
3496
|
-
const
|
|
3497
|
-
|
|
3498
|
-
|
|
3499
|
-
}
|
|
3500
|
-
if (context === "hermes-gateway" || context === "gateway:hermes") {
|
|
3501
|
-
return "hermes";
|
|
3502
|
-
}
|
|
3503
|
-
if (isTruthyRuntimeMarker(env._HERMES_GATEWAY)) {
|
|
3504
|
-
return "hermes";
|
|
3505
|
-
}
|
|
3506
|
-
if ((env.OPENCLAW_SERVICE_KIND ?? "").trim().toLowerCase() === "gateway") {
|
|
3507
|
-
return "openclaw";
|
|
3508
|
-
}
|
|
3509
|
-
if (!isTruthyRuntimeMarker(env.OKX_A2A_IN_GATEWAY)) {
|
|
3510
|
-
return null;
|
|
3511
|
-
}
|
|
3512
|
-
const provider = env.OKX_A2A_GATEWAY_PROVIDER;
|
|
3513
|
-
if (!provider) {
|
|
3514
|
-
return null;
|
|
3515
|
-
}
|
|
3516
|
-
try {
|
|
3517
|
-
const normalized = normalizeAiProvider(provider);
|
|
3518
|
-
return normalized === "openclaw" || normalized === "hermes" ? normalized : null;
|
|
3519
|
-
} catch {
|
|
3520
|
-
return null;
|
|
3521
|
-
}
|
|
3536
|
+
function codexCommandResolvesWithoutOverride(env, storedCommand, platform) {
|
|
3537
|
+
const envWithoutOverride = { ...env };
|
|
3538
|
+
delete envWithoutOverride[CODEX_COMMAND_OVERRIDE_ENV2];
|
|
3539
|
+
return resolveCodexCommandSource(envWithoutOverride, storedCommand, platform) !== null;
|
|
3522
3540
|
}
|
|
3523
|
-
function
|
|
3524
|
-
|
|
3525
|
-
|
|
3526
|
-
|
|
3527
|
-
|
|
3528
|
-
|
|
3529
|
-
|
|
3530
|
-
|
|
3531
|
-
|
|
3532
|
-
ok: true,
|
|
3533
|
-
runtime: "unknown",
|
|
3534
|
-
provider: stored,
|
|
3535
|
-
previousProvider: stored,
|
|
3536
|
-
changed: false,
|
|
3537
|
-
action: "kept_existing",
|
|
3538
|
-
userMessage: `No AI runtime detected for the current environment; keeping the existing default provider (${formatAiProviderDisplayName(stored)}).`
|
|
3539
|
-
};
|
|
3540
|
-
}
|
|
3541
|
-
return {
|
|
3542
|
-
ok: false,
|
|
3543
|
-
runtime: "unknown",
|
|
3544
|
-
reason: "unknown_runtime",
|
|
3545
|
-
state: "blocked",
|
|
3546
|
-
message: "Could not determine the current AI provider.",
|
|
3547
|
-
userMessage: "Could not detect the current AI runtime and no default AI provider is configured. Run this from your AI environment (OpenClaw / Codex / Claude / Hermes), or set a provider manually: `okx-a2a ai-provider set --provider <codex|claude|hermes|openclaw>`."
|
|
3541
|
+
function probeCodexAuthStatus(command, env, platform, timeoutMs) {
|
|
3542
|
+
return new Promise((resolvePromise) => {
|
|
3543
|
+
let settled = false;
|
|
3544
|
+
const settle = (result) => {
|
|
3545
|
+
if (settled) {
|
|
3546
|
+
return;
|
|
3547
|
+
}
|
|
3548
|
+
settled = true;
|
|
3549
|
+
resolvePromise(result);
|
|
3548
3550
|
};
|
|
3549
|
-
|
|
3550
|
-
|
|
3551
|
-
|
|
3552
|
-
|
|
3553
|
-
|
|
3554
|
-
|
|
3555
|
-
|
|
3556
|
-
|
|
3557
|
-
|
|
3558
|
-
|
|
3559
|
-
|
|
3560
|
-
|
|
3561
|
-
|
|
3551
|
+
let child;
|
|
3552
|
+
try {
|
|
3553
|
+
child = spawnCompat(command, ["login", "status"], {
|
|
3554
|
+
env,
|
|
3555
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
3556
|
+
windowsHide: true
|
|
3557
|
+
});
|
|
3558
|
+
} catch (error) {
|
|
3559
|
+
settle({ ok: false, state: spawnErrorReadinessState(error) });
|
|
3560
|
+
return;
|
|
3561
|
+
}
|
|
3562
|
+
child.stdout?.resume();
|
|
3563
|
+
child.stderr?.resume();
|
|
3564
|
+
const timer = setTimeout(() => {
|
|
3565
|
+
killProcessTree(child, "SIGTERM", platform);
|
|
3566
|
+
setTimeout(() => {
|
|
3567
|
+
if (!child.killed || child.exitCode === null) {
|
|
3568
|
+
killProcessTree(child, "SIGKILL", platform);
|
|
3569
|
+
}
|
|
3570
|
+
}, CODEX_PROBE_KILL_GRACE_MS).unref();
|
|
3571
|
+
settle({ ok: false, state: "probe_timeout" });
|
|
3572
|
+
}, timeoutMs);
|
|
3573
|
+
timer.unref();
|
|
3574
|
+
child.once("error", (error) => {
|
|
3575
|
+
clearTimeout(timer);
|
|
3576
|
+
settle({ ok: false, state: spawnErrorReadinessState(error) });
|
|
3577
|
+
});
|
|
3578
|
+
child.once("close", (code) => {
|
|
3579
|
+
clearTimeout(timer);
|
|
3580
|
+
settle(code === 0 ? { ok: true } : { ok: false, state: "not_authenticated" });
|
|
3581
|
+
});
|
|
3582
|
+
});
|
|
3562
3583
|
}
|
|
3563
|
-
function
|
|
3564
|
-
|
|
3565
|
-
|
|
3566
|
-
|
|
3567
|
-
return
|
|
3584
|
+
function readCachedCodexReadiness(options = {}) {
|
|
3585
|
+
const env = options.env ?? process.env;
|
|
3586
|
+
const nowMs = options.nowMs ?? Date.now;
|
|
3587
|
+
const cached = readCacheEntry(options.cacheKey ?? codexReadinessCacheKey(env), nowMs());
|
|
3588
|
+
return cached ? { ...cached, fromCache: true } : null;
|
|
3568
3589
|
}
|
|
3569
|
-
function
|
|
3570
|
-
|
|
3590
|
+
function clearCodexReadinessCache() {
|
|
3591
|
+
readinessCache.clear();
|
|
3592
|
+
inFlightReadiness.clear();
|
|
3571
3593
|
}
|
|
3572
|
-
function
|
|
3573
|
-
|
|
3574
|
-
|
|
3575
|
-
|
|
3576
|
-
|
|
3577
|
-
|
|
3578
|
-
|
|
3579
|
-
|
|
3580
|
-
|
|
3594
|
+
function checkCodexReadiness(options = {}) {
|
|
3595
|
+
const env = options.env ?? process.env;
|
|
3596
|
+
const nowMs = options.nowMs ?? Date.now;
|
|
3597
|
+
const cacheKey2 = options.cacheKey ?? codexReadinessCacheKey(env, options.persistOverrideCommand === true);
|
|
3598
|
+
if (!options.forceRefresh) {
|
|
3599
|
+
const cached = readCacheEntry(cacheKey2, nowMs());
|
|
3600
|
+
if (cached) {
|
|
3601
|
+
return Promise.resolve({ ...cached, fromCache: true });
|
|
3602
|
+
}
|
|
3603
|
+
const inFlight = inFlightReadiness.get(cacheKey2);
|
|
3604
|
+
if (inFlight) {
|
|
3605
|
+
return inFlight;
|
|
3581
3606
|
}
|
|
3582
|
-
|
|
3583
|
-
|
|
3607
|
+
}
|
|
3608
|
+
const pending = resolveCodexReadiness(options, env, cacheKey2, nowMs).finally(() => {
|
|
3609
|
+
if (inFlightReadiness.get(cacheKey2) === pending) {
|
|
3610
|
+
inFlightReadiness.delete(cacheKey2);
|
|
3584
3611
|
}
|
|
3585
|
-
|
|
3586
|
-
|
|
3612
|
+
});
|
|
3613
|
+
inFlightReadiness.set(cacheKey2, pending);
|
|
3614
|
+
return pending;
|
|
3615
|
+
}
|
|
3616
|
+
async function resolveCodexReadiness(options, env, cacheKey2, nowMs) {
|
|
3617
|
+
try {
|
|
3618
|
+
const platform = options.platform ?? process.platform;
|
|
3619
|
+
const store = options.store ?? null;
|
|
3620
|
+
const probeAuth = options.probeAuth ?? probeCodexAuthStatus;
|
|
3621
|
+
const storedCommand = store ? store.getAiProviderCommand("codex") : null;
|
|
3622
|
+
const candidate = resolveCodexCommandSource(env, storedCommand, platform);
|
|
3623
|
+
if (!candidate) {
|
|
3624
|
+
return storeCacheEntry(cacheKey2, nowMs, buildCodexReadinessResult("not_installed", null, null, false));
|
|
3625
|
+
}
|
|
3626
|
+
const probe = await probeAuth(candidate.command, env, platform, CODEX_AUTH_PROBE_TIMEOUT_MS);
|
|
3627
|
+
if (!probe.ok) {
|
|
3628
|
+
return storeCacheEntry(
|
|
3629
|
+
cacheKey2,
|
|
3630
|
+
nowMs,
|
|
3631
|
+
buildCodexReadinessResult(probe.state, candidate.command, candidate.source, false)
|
|
3632
|
+
);
|
|
3587
3633
|
}
|
|
3588
|
-
|
|
3634
|
+
const commandPersisted = persistReadyCodexCommand(
|
|
3635
|
+
candidate,
|
|
3636
|
+
store,
|
|
3637
|
+
options.persist,
|
|
3638
|
+
options.persistOverrideCommand === true
|
|
3639
|
+
);
|
|
3640
|
+
return storeCacheEntry(
|
|
3641
|
+
cacheKey2,
|
|
3642
|
+
nowMs,
|
|
3643
|
+
buildCodexReadinessResult("ready", candidate.command, candidate.source, commandPersisted)
|
|
3644
|
+
);
|
|
3645
|
+
} catch {
|
|
3646
|
+
return storeCacheEntry(cacheKey2, nowMs, buildCodexReadinessResult("probe_failed", null, null, false));
|
|
3589
3647
|
}
|
|
3590
|
-
return false;
|
|
3591
|
-
}
|
|
3592
|
-
function readParentProcessCommand(pid) {
|
|
3593
|
-
return readParentProcessCommandForPlatform(pid, process.platform);
|
|
3594
3648
|
}
|
|
3595
|
-
function
|
|
3596
|
-
if (
|
|
3597
|
-
return
|
|
3649
|
+
function persistReadyCodexCommand(candidate, store, persist, persistOverrideCommand) {
|
|
3650
|
+
if (persist === false || !store) {
|
|
3651
|
+
return false;
|
|
3598
3652
|
}
|
|
3599
|
-
|
|
3600
|
-
|
|
3601
|
-
stdio: ["ignore", "pipe", "ignore"]
|
|
3602
|
-
});
|
|
3603
|
-
const parentResult = (0, import_node_child_process5.spawnSync)("ps", ["-p", String(pid), "-o", "ppid="], {
|
|
3604
|
-
encoding: "utf8",
|
|
3605
|
-
stdio: ["ignore", "pipe", "ignore"]
|
|
3606
|
-
});
|
|
3607
|
-
const command = typeof commandResult.stdout === "string" ? commandResult.stdout.trim() : "";
|
|
3608
|
-
const parentPidRaw = typeof parentResult.stdout === "string" ? parentResult.stdout.trim() : "";
|
|
3609
|
-
const parentPid = Number(parentPidRaw);
|
|
3610
|
-
if (commandResult.status !== 0 || parentResult.status !== 0 || !command || !Number.isFinite(parentPid)) {
|
|
3611
|
-
return null;
|
|
3653
|
+
if (candidate.source === "explicit_override" && !persistOverrideCommand) {
|
|
3654
|
+
return false;
|
|
3612
3655
|
}
|
|
3613
|
-
|
|
3614
|
-
|
|
3615
|
-
|
|
3616
|
-
|
|
3617
|
-
return
|
|
3656
|
+
try {
|
|
3657
|
+
store.setAiProviderCommand("codex", candidate.command);
|
|
3658
|
+
return true;
|
|
3659
|
+
} catch {
|
|
3660
|
+
return false;
|
|
3618
3661
|
}
|
|
3662
|
+
}
|
|
3663
|
+
function buildCodexReadinessResult(state, command, commandSource, commandPersisted) {
|
|
3664
|
+
const { recoveryAction, recoveryGuidance } = buildCodexRecoveryGuidance(state, commandSource);
|
|
3665
|
+
const ready = state === "ready";
|
|
3619
3666
|
return {
|
|
3620
|
-
|
|
3621
|
-
|
|
3622
|
-
|
|
3623
|
-
|
|
3624
|
-
|
|
3625
|
-
|
|
3626
|
-
|
|
3667
|
+
provider: "codex",
|
|
3668
|
+
ready,
|
|
3669
|
+
state,
|
|
3670
|
+
authenticated: ready,
|
|
3671
|
+
command,
|
|
3672
|
+
commandSource,
|
|
3673
|
+
commandPersisted,
|
|
3674
|
+
recoveryAction,
|
|
3675
|
+
recoveryGuidance,
|
|
3676
|
+
fromCache: false
|
|
3627
3677
|
};
|
|
3628
3678
|
}
|
|
3629
|
-
function
|
|
3630
|
-
const
|
|
3631
|
-
if (!
|
|
3632
|
-
return null;
|
|
3633
|
-
}
|
|
3634
|
-
let parsed;
|
|
3635
|
-
try {
|
|
3636
|
-
parsed = JSON.parse(trimmed);
|
|
3637
|
-
} catch {
|
|
3638
|
-
return null;
|
|
3639
|
-
}
|
|
3640
|
-
const record = Array.isArray(parsed) ? parsed[0] : parsed;
|
|
3641
|
-
if (!record || typeof record !== "object") {
|
|
3679
|
+
function readCacheEntry(cacheKey2, atMs) {
|
|
3680
|
+
const entry = readinessCache.get(cacheKey2);
|
|
3681
|
+
if (!entry) {
|
|
3642
3682
|
return null;
|
|
3643
3683
|
}
|
|
3644
|
-
|
|
3645
|
-
|
|
3684
|
+
if (atMs >= entry.expiresAtMs) {
|
|
3685
|
+
readinessCache.delete(cacheKey2);
|
|
3646
3686
|
return null;
|
|
3647
3687
|
}
|
|
3648
|
-
|
|
3649
|
-
const parentPid = Number.isInteger(rawParentPid) && rawParentPid > 0 ? rawParentPid : null;
|
|
3650
|
-
return { command: name.trim(), parentPid };
|
|
3688
|
+
return entry.result;
|
|
3651
3689
|
}
|
|
3652
|
-
function
|
|
3653
|
-
|
|
3654
|
-
|
|
3655
|
-
logWinCompat(`${WIN_COMPAT_LOG_PREFIX} parent-process probe rejected non-integer pid=${String(pid)}`);
|
|
3656
|
-
return null;
|
|
3657
|
-
}
|
|
3658
|
-
const result = (0, import_node_child_process5.spawnSync)(probe.command, probe.args, {
|
|
3659
|
-
encoding: "utf8",
|
|
3660
|
-
stdio: ["ignore", "pipe", "ignore"],
|
|
3661
|
-
timeout: 5e3,
|
|
3662
|
-
windowsHide: true
|
|
3663
|
-
});
|
|
3664
|
-
if (result.error) {
|
|
3665
|
-
const spawnError = result.error;
|
|
3666
|
-
logWinCompat(
|
|
3667
|
-
`${WIN_COMPAT_LOG_PREFIX} parent-process probe failed pid=${pid} command=${probe.command} errorCode=${spawnError.code ?? "unknown"} error=${spawnError.message}`
|
|
3668
|
-
);
|
|
3669
|
-
return null;
|
|
3670
|
-
}
|
|
3671
|
-
if (result.status !== 0) {
|
|
3672
|
-
logWinCompat(
|
|
3673
|
-
`${WIN_COMPAT_LOG_PREFIX} parent-process probe exited pid=${pid} command=${probe.command} status=${String(result.status)} signal=${String(result.signal)}`
|
|
3674
|
-
);
|
|
3675
|
-
return null;
|
|
3676
|
-
}
|
|
3677
|
-
const stdout = typeof result.stdout === "string" ? result.stdout : "";
|
|
3678
|
-
const parsed = parseWindowsParentProcessJson(stdout);
|
|
3679
|
-
if (!parsed) {
|
|
3680
|
-
logWinCompat(
|
|
3681
|
-
`${WIN_COMPAT_LOG_PREFIX} parent-process probe unparsable output pid=${pid} stdout=${JSON.stringify(stdout.slice(0, 200))}`
|
|
3682
|
-
);
|
|
3683
|
-
return null;
|
|
3684
|
-
}
|
|
3685
|
-
if (process.env.OKX_A2A_WIN_COMPAT_VERBOSE === "1") {
|
|
3686
|
-
logWinCompat(
|
|
3687
|
-
`${WIN_COMPAT_LOG_PREFIX} parent-process probe pid=${pid} -> command=${parsed.command} parentPid=${parsed.parentPid ?? "null"}`
|
|
3688
|
-
);
|
|
3689
|
-
}
|
|
3690
|
-
return parsed;
|
|
3690
|
+
function storeCacheEntry(cacheKey2, nowMs, result) {
|
|
3691
|
+
readinessCache.set(cacheKey2, { result, expiresAtMs: nowMs() + CODEX_READINESS_CACHE_TTL_MS });
|
|
3692
|
+
return result;
|
|
3691
3693
|
}
|
|
3692
|
-
|
|
3693
|
-
const
|
|
3694
|
-
|
|
3695
|
-
|
|
3696
|
-
|
|
3697
|
-
|
|
3698
|
-
|
|
3699
|
-
|
|
3700
|
-
|
|
3701
|
-
|
|
3702
|
-
|
|
3703
|
-
|
|
3704
|
-
|
|
3705
|
-
|
|
3706
|
-
|
|
3707
|
-
|
|
3708
|
-
|
|
3709
|
-
|
|
3710
|
-
|
|
3711
|
-
|
|
3712
|
-
|
|
3713
|
-
|
|
3714
|
-
|
|
3715
|
-
|
|
3716
|
-
|
|
3717
|
-
|
|
3718
|
-
|
|
3719
|
-
|
|
3720
|
-
|
|
3721
|
-
|
|
3722
|
-
|
|
3723
|
-
|
|
3694
|
+
function spawnErrorReadinessState(error) {
|
|
3695
|
+
const code = error?.code;
|
|
3696
|
+
return code === "ENOENT" ? "not_installed" : "probe_failed";
|
|
3697
|
+
}
|
|
3698
|
+
var CODEX_AUTH_PROBE_TIMEOUT_MS, CODEX_READINESS_CACHE_TTL_MS, CODEX_PROBE_KILL_GRACE_MS, CODEX_COMMAND_OVERRIDE_ENV2, CODEX_RECOVERY_BY_STATE, readinessCache, inFlightReadiness, CODEX_APP_BUNDLED_LOGIN_GUIDANCE;
|
|
3699
|
+
var init_codex_readiness = __esm({
|
|
3700
|
+
"src/codex-readiness.ts"() {
|
|
3701
|
+
"use strict";
|
|
3702
|
+
init_ai_command();
|
|
3703
|
+
init_win_spawn();
|
|
3704
|
+
CODEX_AUTH_PROBE_TIMEOUT_MS = 1e4;
|
|
3705
|
+
CODEX_READINESS_CACHE_TTL_MS = 6e4;
|
|
3706
|
+
CODEX_PROBE_KILL_GRACE_MS = 5e3;
|
|
3707
|
+
CODEX_COMMAND_OVERRIDE_ENV2 = "OKX_A2A_AI_CODEX_COMMAND";
|
|
3708
|
+
CODEX_RECOVERY_BY_STATE = {
|
|
3709
|
+
ready: { recoveryAction: "none", recoveryGuidance: "" },
|
|
3710
|
+
not_installed: {
|
|
3711
|
+
recoveryAction: "install_cli",
|
|
3712
|
+
recoveryGuidance: "Codex was not found. Install the Codex CLI or the Codex desktop app, then run `okx-a2a setup`."
|
|
3713
|
+
},
|
|
3714
|
+
not_authenticated: {
|
|
3715
|
+
recoveryAction: "run_login",
|
|
3716
|
+
recoveryGuidance: "Codex is installed but not logged in. Run `codex login`, then retry."
|
|
3717
|
+
},
|
|
3718
|
+
probe_timeout: {
|
|
3719
|
+
recoveryAction: "run_setup",
|
|
3720
|
+
recoveryGuidance: `Codex did not report its login status within ${CODEX_AUTH_PROBE_TIMEOUT_MS}ms. Run \`okx-a2a setup\` to re-verify the Codex CLI.`
|
|
3721
|
+
},
|
|
3722
|
+
probe_failed: {
|
|
3723
|
+
recoveryAction: "run_setup",
|
|
3724
|
+
recoveryGuidance: "Codex readiness could not be verified. Run `okx-a2a setup` to re-verify the Codex CLI."
|
|
3725
|
+
}
|
|
3724
3726
|
};
|
|
3727
|
+
readinessCache = /* @__PURE__ */ new Map();
|
|
3728
|
+
inFlightReadiness = /* @__PURE__ */ new Map();
|
|
3729
|
+
CODEX_APP_BUNDLED_LOGIN_GUIDANCE = "Codex was found in the Codex app bundle but is not logged in. Sign in from the Codex app, or run `okx-a2a setup --json` to re-verify Codex. `codex login` will not work: the bundled executable is not on PATH.";
|
|
3725
3730
|
}
|
|
3726
|
-
|
|
3727
|
-
|
|
3728
|
-
|
|
3729
|
-
|
|
3731
|
+
});
|
|
3732
|
+
|
|
3733
|
+
// ../../node_modules/@sentry/utils/cjs/is.js
|
|
3734
|
+
var require_is = __commonJS({
|
|
3735
|
+
"../../node_modules/@sentry/utils/cjs/is.js"(exports2) {
|
|
3736
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
3737
|
+
var objectToString = Object.prototype.toString;
|
|
3738
|
+
function isError(wat) {
|
|
3739
|
+
switch (objectToString.call(wat)) {
|
|
3740
|
+
case "[object Error]":
|
|
3741
|
+
case "[object Exception]":
|
|
3742
|
+
case "[object DOMException]":
|
|
3743
|
+
return true;
|
|
3744
|
+
default:
|
|
3745
|
+
return isInstanceOf(wat, Error);
|
|
3746
|
+
}
|
|
3730
3747
|
}
|
|
3731
|
-
|
|
3732
|
-
|
|
3733
|
-
provider,
|
|
3734
|
-
source: "env",
|
|
3735
|
-
message: isInstalled(provider, detection) ? `default AI provider set to ${provider} from OKX_AGENT_TASK_AI_CLI` : `default AI provider set to ${provider} from OKX_AGENT_TASK_AI_CLI; availability was not verified on PATH`
|
|
3736
|
-
};
|
|
3737
|
-
}
|
|
3738
|
-
if (env.OKX_A2A_AI_PROVIDER) {
|
|
3739
|
-
const provider = normalizeAiProvider(env.OKX_A2A_AI_PROVIDER);
|
|
3740
|
-
if (!options.allowUnavailableProvider) {
|
|
3741
|
-
assertInstalled(provider, detection);
|
|
3748
|
+
function isBuiltin(wat, className) {
|
|
3749
|
+
return objectToString.call(wat) === `[object ${className}]`;
|
|
3742
3750
|
}
|
|
3743
|
-
|
|
3744
|
-
|
|
3745
|
-
source: "env",
|
|
3746
|
-
message: isInstalled(provider, detection) ? `AI provider resolved from OKX_A2A_AI_PROVIDER: ${provider}` : `AI provider resolved from OKX_A2A_AI_PROVIDER: ${provider}; availability was not verified on PATH`
|
|
3747
|
-
};
|
|
3748
|
-
}
|
|
3749
|
-
const stored = options.store.getDefaultAiProvider();
|
|
3750
|
-
if (stored && (options.allowUnavailableProvider || isInstalled(stored, detection))) {
|
|
3751
|
-
return {
|
|
3752
|
-
provider: stored,
|
|
3753
|
-
source: "stored",
|
|
3754
|
-
message: isInstalled(stored, detection) ? `default AI provider is ${stored}` : `default AI provider is ${stored}; availability was not verified on PATH`
|
|
3755
|
-
};
|
|
3756
|
-
}
|
|
3757
|
-
if (stored) {
|
|
3758
|
-
options.stderr?.write(
|
|
3759
|
-
`[okx-agent-task] stored AI provider "${stored}" is not installed or not on PATH; selecting again.
|
|
3760
|
-
`
|
|
3761
|
-
);
|
|
3762
|
-
}
|
|
3763
|
-
if (options.promptIfMissing) {
|
|
3764
|
-
const provider = await promptForAiProvider({
|
|
3765
|
-
detection,
|
|
3766
|
-
stdin: options.stdin,
|
|
3767
|
-
stdout: options.stdout,
|
|
3768
|
-
currentProvider: stored ?? null
|
|
3769
|
-
});
|
|
3770
|
-
options.store.setDefaultAiProvider(provider);
|
|
3771
|
-
return {
|
|
3772
|
-
provider,
|
|
3773
|
-
source: "detected",
|
|
3774
|
-
message: `default AI provider set to ${provider}`
|
|
3775
|
-
};
|
|
3776
|
-
}
|
|
3777
|
-
const current = detectCurrentAiProvider(env);
|
|
3778
|
-
if (current && (options.allowUnavailableProvider || isInstalled(current, detection))) {
|
|
3779
|
-
return {
|
|
3780
|
-
provider: current,
|
|
3781
|
-
source: "env",
|
|
3782
|
-
message: isInstalled(current, detection) ? `AI provider resolved from current environment: ${current}` : `AI provider resolved from current environment: ${current}; availability was not verified on PATH`
|
|
3783
|
-
};
|
|
3784
|
-
}
|
|
3785
|
-
if (detection.available.length === 0) {
|
|
3786
|
-
throw new Error(
|
|
3787
|
-
`No supported AI CLI found. Install one of: ${AI_PROVIDERS.join(", ")}.`
|
|
3788
|
-
);
|
|
3789
|
-
}
|
|
3790
|
-
if (detection.available.length === 1) {
|
|
3791
|
-
const provider = detection.available[0];
|
|
3792
|
-
options.store.setDefaultAiProvider(provider);
|
|
3793
|
-
return {
|
|
3794
|
-
provider,
|
|
3795
|
-
source: "detected",
|
|
3796
|
-
message: `detected only ${provider}; default AI provider set to ${provider}`
|
|
3797
|
-
};
|
|
3798
|
-
}
|
|
3799
|
-
throw new Error(
|
|
3800
|
-
`Multiple AI CLIs are installed (${detection.available.join(", ")}) but no current AI environment was detected. Start the daemon from an AI session, pass --ai-provider <provider>, or set OKX_A2A_AI_PROVIDER.`
|
|
3801
|
-
);
|
|
3802
|
-
}
|
|
3803
|
-
function resolveConfiguredAiProvider(options) {
|
|
3804
|
-
const env = options.env ?? process.env;
|
|
3805
|
-
const explicit = env.OKX_AGENT_TASK_AI_CLI ?? env.OKX_A2A_AI_PROVIDER;
|
|
3806
|
-
if (explicit) {
|
|
3807
|
-
return normalizeAiProvider(explicit);
|
|
3808
|
-
}
|
|
3809
|
-
return options.store.getDefaultAiProvider();
|
|
3810
|
-
}
|
|
3811
|
-
function resolveConfiguredAiProviderForJob(options) {
|
|
3812
|
-
const jobId = normalizeOptionalJobId2(options.jobId);
|
|
3813
|
-
if (jobId) {
|
|
3814
|
-
const binding = options.store.getJobProviderBinding(jobId);
|
|
3815
|
-
if (binding) {
|
|
3816
|
-
bindHermesJobRouteFromEnvIfAvailable(options.store, {
|
|
3817
|
-
jobId,
|
|
3818
|
-
provider: binding.provider,
|
|
3819
|
-
env: options.env
|
|
3820
|
-
});
|
|
3821
|
-
return binding.provider;
|
|
3822
|
-
}
|
|
3823
|
-
}
|
|
3824
|
-
const provider = resolveConfiguredAiProvider({
|
|
3825
|
-
store: options.store,
|
|
3826
|
-
env: options.env
|
|
3827
|
-
});
|
|
3828
|
-
if (jobId && provider) {
|
|
3829
|
-
return bindJobProviderWithRouteIfAvailable(options.store, {
|
|
3830
|
-
jobId,
|
|
3831
|
-
provider,
|
|
3832
|
-
env: options.env
|
|
3833
|
-
}).binding.provider;
|
|
3834
|
-
}
|
|
3835
|
-
return provider;
|
|
3836
|
-
}
|
|
3837
|
-
function bindJobProviderToCurrentDefaultIfMissing(options) {
|
|
3838
|
-
const store = options.store ?? null;
|
|
3839
|
-
const jobId = normalizeOptionalJobId2(options.jobId);
|
|
3840
|
-
if (!store || !jobId) {
|
|
3841
|
-
return null;
|
|
3842
|
-
}
|
|
3843
|
-
const existing = store.getJobProviderBinding(jobId);
|
|
3844
|
-
if (existing) {
|
|
3845
|
-
bindHermesJobRouteFromEnvIfAvailable(store, {
|
|
3846
|
-
jobId,
|
|
3847
|
-
provider: existing.provider,
|
|
3848
|
-
env: options.env
|
|
3849
|
-
});
|
|
3850
|
-
return {
|
|
3851
|
-
provider: existing.provider,
|
|
3852
|
-
created: false
|
|
3853
|
-
};
|
|
3854
|
-
}
|
|
3855
|
-
const provider = store.getDefaultAiProvider();
|
|
3856
|
-
if (!provider) {
|
|
3857
|
-
return null;
|
|
3858
|
-
}
|
|
3859
|
-
const result = bindJobProviderWithRouteIfAvailable(store, {
|
|
3860
|
-
jobId,
|
|
3861
|
-
provider,
|
|
3862
|
-
env: options.env
|
|
3863
|
-
});
|
|
3864
|
-
return {
|
|
3865
|
-
provider: result.binding.provider,
|
|
3866
|
-
created: result.created
|
|
3867
|
-
};
|
|
3868
|
-
}
|
|
3869
|
-
function bindJobProviderToCurrentRuntimeIfMissing(options) {
|
|
3870
|
-
const store = options.store ?? null;
|
|
3871
|
-
const jobId = normalizeOptionalJobId2(options.jobId);
|
|
3872
|
-
if (!store || !jobId) {
|
|
3873
|
-
return {
|
|
3874
|
-
ok: false,
|
|
3875
|
-
jobId: jobId ?? "",
|
|
3876
|
-
provider: null,
|
|
3877
|
-
currentProvider: null,
|
|
3878
|
-
created: false,
|
|
3879
|
-
binding: null,
|
|
3880
|
-
reason: "missing_job_id"
|
|
3881
|
-
};
|
|
3882
|
-
}
|
|
3883
|
-
const existing = store.getJobProviderBinding(jobId);
|
|
3884
|
-
if (existing) {
|
|
3885
|
-
const binding = bindHermesJobRouteFromEnvIfAvailable(store, {
|
|
3886
|
-
jobId,
|
|
3887
|
-
provider: existing.provider,
|
|
3888
|
-
env: options.env
|
|
3889
|
-
}) ?? existing;
|
|
3890
|
-
return {
|
|
3891
|
-
ok: true,
|
|
3892
|
-
jobId,
|
|
3893
|
-
provider: binding.provider,
|
|
3894
|
-
currentProvider: null,
|
|
3895
|
-
created: false,
|
|
3896
|
-
binding,
|
|
3897
|
-
reason: "already_bound"
|
|
3898
|
-
};
|
|
3899
|
-
}
|
|
3900
|
-
const switched = switchProvider({ store, env: options.env ?? process.env });
|
|
3901
|
-
if (!switched.ok || !switched.provider) {
|
|
3902
|
-
return {
|
|
3903
|
-
ok: false,
|
|
3904
|
-
jobId,
|
|
3905
|
-
provider: null,
|
|
3906
|
-
currentProvider: null,
|
|
3907
|
-
created: false,
|
|
3908
|
-
binding: null,
|
|
3909
|
-
reason: "unknown_runtime"
|
|
3910
|
-
};
|
|
3911
|
-
}
|
|
3912
|
-
const currentProvider = switched.provider;
|
|
3913
|
-
const result = bindJobProviderWithRouteIfAvailable(store, {
|
|
3914
|
-
jobId,
|
|
3915
|
-
provider: currentProvider,
|
|
3916
|
-
env: options.env
|
|
3917
|
-
});
|
|
3918
|
-
return {
|
|
3919
|
-
ok: true,
|
|
3920
|
-
jobId,
|
|
3921
|
-
provider: result.binding.provider,
|
|
3922
|
-
currentProvider,
|
|
3923
|
-
created: result.created,
|
|
3924
|
-
binding: result.binding,
|
|
3925
|
-
reason: result.created ? "created" : "already_bound"
|
|
3926
|
-
};
|
|
3927
|
-
}
|
|
3928
|
-
function resolveAiProviderForDispatch(options) {
|
|
3929
|
-
const env = options.env ?? process.env;
|
|
3930
|
-
const commandExists2 = options.commandExists ?? commandExists;
|
|
3931
|
-
const detection = withStoredCodexAvailability(
|
|
3932
|
-
detectAiProviders(commandExists2, env),
|
|
3933
|
-
options.store,
|
|
3934
|
-
commandExists2
|
|
3935
|
-
);
|
|
3936
|
-
const jobId = normalizeOptionalJobId2(options.jobId);
|
|
3937
|
-
if (jobId) {
|
|
3938
|
-
const binding = options.store.getJobProviderBinding(jobId);
|
|
3939
|
-
if (binding) {
|
|
3940
|
-
assertInstalled(binding.provider, detection);
|
|
3941
|
-
return binding.provider;
|
|
3942
|
-
}
|
|
3943
|
-
}
|
|
3944
|
-
if (env.OKX_AGENT_TASK_AI_CLI) {
|
|
3945
|
-
const provider = normalizeAiProvider(env.OKX_AGENT_TASK_AI_CLI);
|
|
3946
|
-
assertInstalled(provider, detection);
|
|
3947
|
-
return bindDispatchProviderIfNeeded(options.store, jobId, env, provider);
|
|
3948
|
-
}
|
|
3949
|
-
if (env.OKX_A2A_AI_PROVIDER) {
|
|
3950
|
-
const provider = normalizeAiProvider(env.OKX_A2A_AI_PROVIDER);
|
|
3951
|
-
assertInstalled(provider, detection);
|
|
3952
|
-
return bindDispatchProviderIfNeeded(options.store, jobId, env, provider);
|
|
3953
|
-
}
|
|
3954
|
-
if (jobId) {
|
|
3955
|
-
const stored2 = options.store.getDefaultAiProvider();
|
|
3956
|
-
if (stored2) {
|
|
3957
|
-
assertInstalled(stored2, detection);
|
|
3958
|
-
return bindDispatchProviderIfNeeded(options.store, jobId, env, stored2);
|
|
3959
|
-
}
|
|
3960
|
-
}
|
|
3961
|
-
const current = detectCurrentAiProvider(env);
|
|
3962
|
-
if (current && isInstalled(current, detection)) {
|
|
3963
|
-
return bindDispatchProviderIfNeeded(options.store, jobId, env, current);
|
|
3964
|
-
}
|
|
3965
|
-
if (options.sessionKey) {
|
|
3966
|
-
const existing = options.store.listAiSessions(options.sessionKey).map((session) => session.provider).filter((provider) => isInstalled(provider, detection));
|
|
3967
|
-
const unique = [...new Set(existing)];
|
|
3968
|
-
if (unique.length === 1) {
|
|
3969
|
-
return bindDispatchProviderIfNeeded(options.store, jobId, env, unique[0]);
|
|
3970
|
-
}
|
|
3971
|
-
}
|
|
3972
|
-
const stored = options.store.getDefaultAiProvider();
|
|
3973
|
-
if (stored) {
|
|
3974
|
-
assertInstalled(stored, detection);
|
|
3975
|
-
return bindDispatchProviderIfNeeded(options.store, jobId, env, stored);
|
|
3976
|
-
}
|
|
3977
|
-
if (detection.available.length === 1) {
|
|
3978
|
-
return bindDispatchProviderIfNeeded(options.store, jobId, env, detection.available[0]);
|
|
3979
|
-
}
|
|
3980
|
-
if (detection.available.length > 1) {
|
|
3981
|
-
throw new Error(
|
|
3982
|
-
`Multiple AI CLIs are installed (${detection.available.join(", ")}) but no current AI environment was detected. Set OKX_A2A_AI_PROVIDER or pass --provider/--ai-provider for this run.`
|
|
3983
|
-
);
|
|
3984
|
-
}
|
|
3985
|
-
throw new Error(`No supported AI CLI found. Install one of: ${AI_PROVIDERS.join(", ")}.`);
|
|
3986
|
-
}
|
|
3987
|
-
function bindDispatchProviderIfNeeded(store, jobId, env, provider) {
|
|
3988
|
-
if (!jobId) {
|
|
3989
|
-
return provider;
|
|
3990
|
-
}
|
|
3991
|
-
return bindJobProviderWithRouteIfAvailable(store, { jobId, provider, env }).binding.provider;
|
|
3992
|
-
}
|
|
3993
|
-
function withStoredCodexAvailability(detection, store, commandExists2) {
|
|
3994
|
-
if (detection.codex) {
|
|
3995
|
-
return detection;
|
|
3996
|
-
}
|
|
3997
|
-
const command = store.getAiProviderCommand("codex");
|
|
3998
|
-
if (!command || !commandExists2(command)) {
|
|
3999
|
-
return detection;
|
|
4000
|
-
}
|
|
4001
|
-
return {
|
|
4002
|
-
...detection,
|
|
4003
|
-
codex: true,
|
|
4004
|
-
available: detection.available.includes("codex") ? detection.available : ["codex", ...detection.available]
|
|
4005
|
-
};
|
|
4006
|
-
}
|
|
4007
|
-
function bindJobProviderWithRouteIfAvailable(store, input) {
|
|
4008
|
-
const result = store.bindJobProviderIfMissing({
|
|
4009
|
-
jobId: input.jobId,
|
|
4010
|
-
provider: input.provider
|
|
4011
|
-
});
|
|
4012
|
-
const routed = bindHermesJobRouteFromEnvIfAvailable(store, {
|
|
4013
|
-
jobId: input.jobId,
|
|
4014
|
-
provider: result.binding.provider,
|
|
4015
|
-
env: input.env
|
|
4016
|
-
});
|
|
4017
|
-
return routed ? { ...result, binding: routed } : result;
|
|
4018
|
-
}
|
|
4019
|
-
function bindHermesJobRouteFromEnvIfAvailable(store, input) {
|
|
4020
|
-
if (input.provider !== "hermes" || typeof store.upsertJobGatewayRoute !== "function") {
|
|
4021
|
-
return null;
|
|
4022
|
-
}
|
|
4023
|
-
const route = currentHermesGatewayRouteFromEnv(input.env ?? process.env);
|
|
4024
|
-
if (!route) {
|
|
4025
|
-
return null;
|
|
4026
|
-
}
|
|
4027
|
-
return store.upsertJobGatewayRoute({
|
|
4028
|
-
jobId: input.jobId,
|
|
4029
|
-
provider: "hermes",
|
|
4030
|
-
route
|
|
4031
|
-
});
|
|
4032
|
-
}
|
|
4033
|
-
function currentHermesGatewayRouteFromEnv(env) {
|
|
4034
|
-
const platform = normalizeOptionalText(env.OKX_A2A_CURRENT_GATEWAY_PLATFORM);
|
|
4035
|
-
const chatId = normalizeOptionalText(env.OKX_A2A_CURRENT_GATEWAY_CHAT_ID);
|
|
4036
|
-
const envGatewaySessionKey = normalizeOptionalText(env.OKX_A2A_CURRENT_GATEWAY_SESSION_KEY);
|
|
4037
|
-
const parsedGatewaySession = parseHermesGatewaySessionKey(envGatewaySessionKey);
|
|
4038
|
-
if (platform && chatId) {
|
|
4039
|
-
const threadId = normalizeOptionalText(env.OKX_A2A_CURRENT_GATEWAY_THREAD_ID) ?? matchingParsedThreadId(parsedGatewaySession, { platform, chatId });
|
|
4040
|
-
return buildHermesGatewayRoute({
|
|
4041
|
-
platform,
|
|
4042
|
-
chatId,
|
|
4043
|
-
threadId,
|
|
4044
|
-
sessionKey: envGatewaySessionKey
|
|
4045
|
-
});
|
|
4046
|
-
}
|
|
4047
|
-
const sessionRoute = routeFromHermesSessionKey(env.HERMES_SESSION_KEY);
|
|
4048
|
-
return sessionRoute ? buildHermesGatewayRoute(sessionRoute) : null;
|
|
4049
|
-
}
|
|
4050
|
-
function buildHermesGatewayRoute(input) {
|
|
4051
|
-
const threadId = normalizeOptionalText(input.threadId) ?? "";
|
|
4052
|
-
const sessionKey = normalizeOptionalText(input.sessionKey) ?? (threadId ? hermesThreadSessionKey(input.platform, input.chatId, threadId) : `agent:main:${input.platform}:dm:${encodeURIComponent(input.chatId)}`);
|
|
4053
|
-
return {
|
|
4054
|
-
platform: input.platform,
|
|
4055
|
-
chatId: input.chatId,
|
|
4056
|
-
chatName: "",
|
|
4057
|
-
chatType: threadId ? "thread" : "dm",
|
|
4058
|
-
threadId,
|
|
4059
|
-
userId: "",
|
|
4060
|
-
userName: "",
|
|
4061
|
-
sessionKey,
|
|
4062
|
-
gatewaySessionKey: sessionKey
|
|
4063
|
-
};
|
|
4064
|
-
}
|
|
4065
|
-
function hermesThreadSessionKey(platform, chatId, threadId) {
|
|
4066
|
-
const encodedChatId = encodeURIComponent(chatId);
|
|
4067
|
-
const encodedThreadId = encodeURIComponent(threadId);
|
|
4068
|
-
return platform === "telegram" ? `agent:main:${platform}:dm:${encodedChatId}:${encodedThreadId}` : `agent:main:${platform}:thread:${encodedChatId}:${encodedThreadId}`;
|
|
4069
|
-
}
|
|
4070
|
-
function routeFromHermesSessionKey(value) {
|
|
4071
|
-
const sessionKey = normalizeOptionalText(value);
|
|
4072
|
-
if (!sessionKey) {
|
|
4073
|
-
return null;
|
|
4074
|
-
}
|
|
4075
|
-
const parsed = parseHermesGatewaySessionKey(sessionKey);
|
|
4076
|
-
return parsed ? { ...parsed, sessionKey } : null;
|
|
4077
|
-
}
|
|
4078
|
-
function parseHermesGatewaySessionKey(sessionKey) {
|
|
4079
|
-
const normalized = normalizeOptionalText(sessionKey);
|
|
4080
|
-
if (!normalized) {
|
|
4081
|
-
return null;
|
|
4082
|
-
}
|
|
4083
|
-
const parts = normalized.startsWith("agent:") ? normalized.split(":").slice(2) : normalized.split(":");
|
|
4084
|
-
if (parts.length < 3) {
|
|
4085
|
-
return null;
|
|
4086
|
-
}
|
|
4087
|
-
const [platform, scope, ...rest] = parts;
|
|
4088
|
-
if (!platform || platform === "okx-a2a" || platform === "backup" || platform === "job") {
|
|
4089
|
-
return null;
|
|
4090
|
-
}
|
|
4091
|
-
if (scope === "dm" && rest[0]) {
|
|
4092
|
-
return {
|
|
4093
|
-
platform,
|
|
4094
|
-
chatId: safeDecodeURIComponent2(rest[0]),
|
|
4095
|
-
...platform === "telegram" && rest[1] ? { threadId: safeDecodeURIComponent2(rest.slice(1).join(":")) } : {}
|
|
4096
|
-
};
|
|
4097
|
-
}
|
|
4098
|
-
if (scope === "thread" && rest[0] && rest[1]) {
|
|
4099
|
-
return {
|
|
4100
|
-
platform,
|
|
4101
|
-
chatId: safeDecodeURIComponent2(rest[0]),
|
|
4102
|
-
threadId: safeDecodeURIComponent2(rest.slice(1).join(":"))
|
|
4103
|
-
};
|
|
4104
|
-
}
|
|
4105
|
-
return null;
|
|
4106
|
-
}
|
|
4107
|
-
function matchingParsedThreadId(parsed, expected) {
|
|
4108
|
-
if (!parsed?.threadId) {
|
|
4109
|
-
return null;
|
|
4110
|
-
}
|
|
4111
|
-
return parsed.platform === expected.platform && parsed.chatId === expected.chatId ? parsed.threadId : null;
|
|
4112
|
-
}
|
|
4113
|
-
function safeDecodeURIComponent2(value) {
|
|
4114
|
-
try {
|
|
4115
|
-
return decodeURIComponent(value);
|
|
4116
|
-
} catch {
|
|
4117
|
-
return value;
|
|
4118
|
-
}
|
|
4119
|
-
}
|
|
4120
|
-
function normalizeOptionalJobId2(value) {
|
|
4121
|
-
const normalized = value?.trim();
|
|
4122
|
-
return normalized ? normalized : null;
|
|
4123
|
-
}
|
|
4124
|
-
function normalizeOptionalText(value) {
|
|
4125
|
-
const normalized = value?.trim();
|
|
4126
|
-
return normalized ? normalized : null;
|
|
4127
|
-
}
|
|
4128
|
-
function isInstalled(provider, detection) {
|
|
4129
|
-
return detection.available.includes(provider);
|
|
4130
|
-
}
|
|
4131
|
-
function assertInstalled(provider, detection) {
|
|
4132
|
-
if (!isInstalled(provider, detection)) {
|
|
4133
|
-
throw new Error(`AI provider "${provider}" is not installed or not on PATH.`);
|
|
4134
|
-
}
|
|
4135
|
-
}
|
|
4136
|
-
function readProviderCommand(provider, env) {
|
|
4137
|
-
return env[`OKX_A2A_AI_${provider.toUpperCase()}_COMMAND`] ?? provider;
|
|
4138
|
-
}
|
|
4139
|
-
async function promptForAiProvider(options) {
|
|
4140
|
-
const stdin = options.stdin ?? process.stdin;
|
|
4141
|
-
const stdout = options.stdout ?? process.stdout;
|
|
4142
|
-
if (!stdin.isTTY || !stdout.isTTY) {
|
|
4143
|
-
throw new Error(
|
|
4144
|
-
`No AI provider is configured. Run \`okx-a2a daemon start --ai-provider <${AI_PROVIDERS.join("|")}>\` or set OKX_A2A_AI_PROVIDER.`
|
|
4145
|
-
);
|
|
4146
|
-
}
|
|
4147
|
-
stdout.write("AI provider is required before starting okx-a2a.\n");
|
|
4148
|
-
stdout.write("Tip: if the selected AI provider is unavailable, task processing cannot proceed.\n\n");
|
|
4149
|
-
if (options.currentProvider) {
|
|
4150
|
-
stdout.write(`Current AI provider: ${options.currentProvider}
|
|
4151
|
-
|
|
4152
|
-
`);
|
|
4153
|
-
}
|
|
4154
|
-
if (options.detection.available.length === 0) {
|
|
4155
|
-
stdout.write("Supported AI providers:\n");
|
|
4156
|
-
AI_PROVIDERS.forEach((provider, index2) => {
|
|
4157
|
-
const status = isInstalled(provider, options.detection) ? "installed" : "not found on PATH";
|
|
4158
|
-
stdout.write(` ${index2 + 1}. ${provider} (${status})
|
|
4159
|
-
`);
|
|
4160
|
-
});
|
|
4161
|
-
stdout.write("\n");
|
|
4162
|
-
throw new Error(`No supported AI CLI found. Install one of: ${AI_PROVIDERS.join(", ")}.`);
|
|
4163
|
-
}
|
|
4164
|
-
if (typeof stdin.setRawMode === "function") {
|
|
4165
|
-
return promptForAiProviderWithArrows({
|
|
4166
|
-
detection: options.detection,
|
|
4167
|
-
stdin,
|
|
4168
|
-
stdout,
|
|
4169
|
-
currentProvider: options.currentProvider
|
|
4170
|
-
});
|
|
4171
|
-
}
|
|
4172
|
-
stdout.write("Supported AI providers:\n");
|
|
4173
|
-
AI_PROVIDERS.forEach((provider, index2) => {
|
|
4174
|
-
const status = isInstalled(provider, options.detection) ? "installed" : "not found on PATH";
|
|
4175
|
-
stdout.write(` ${index2 + 1}. ${provider} (${status})
|
|
4176
|
-
`);
|
|
4177
|
-
});
|
|
4178
|
-
stdout.write("\n");
|
|
4179
|
-
const rl = (0, import_promises6.createInterface)({ input: stdin, output: stdout });
|
|
4180
|
-
try {
|
|
4181
|
-
while (true) {
|
|
4182
|
-
const answer = (await rl.question("Choose an installed AI provider by number or name: ")).trim();
|
|
4183
|
-
const provider = parseProviderChoice(answer);
|
|
4184
|
-
if (!provider) {
|
|
4185
|
-
stdout.write(`Invalid choice. Use one of: ${AI_PROVIDERS.join(", ")}.
|
|
4186
|
-
`);
|
|
4187
|
-
continue;
|
|
4188
|
-
}
|
|
4189
|
-
if (!isInstalled(provider, options.detection)) {
|
|
4190
|
-
stdout.write(`"${provider}" is not installed or not on PATH. Choose an installed provider.
|
|
4191
|
-
`);
|
|
4192
|
-
continue;
|
|
4193
|
-
}
|
|
4194
|
-
return provider;
|
|
4195
|
-
}
|
|
4196
|
-
} finally {
|
|
4197
|
-
rl.close();
|
|
4198
|
-
}
|
|
4199
|
-
}
|
|
4200
|
-
async function promptForAiProviderWithArrows(options) {
|
|
4201
|
-
let selectedIndex = AI_PROVIDERS.findIndex((provider) => provider === options.currentProvider && isInstalled(provider, options.detection));
|
|
4202
|
-
if (selectedIndex < 0) {
|
|
4203
|
-
selectedIndex = AI_PROVIDERS.findIndex((provider) => isInstalled(provider, options.detection));
|
|
4204
|
-
}
|
|
4205
|
-
if (selectedIndex < 0) {
|
|
4206
|
-
selectedIndex = 0;
|
|
4207
|
-
}
|
|
4208
|
-
let renderedLines = 0;
|
|
4209
|
-
const render = (message) => {
|
|
4210
|
-
if (renderedLines > 0) {
|
|
4211
|
-
(0, import_node_readline.moveCursor)(options.stdout, 0, -renderedLines);
|
|
4212
|
-
(0, import_node_readline.cursorTo)(options.stdout, 0);
|
|
4213
|
-
(0, import_node_readline.clearScreenDown)(options.stdout);
|
|
4214
|
-
}
|
|
4215
|
-
const lines = [
|
|
4216
|
-
"Use \u2191/\u2193 to move, Enter to confirm.",
|
|
4217
|
-
"",
|
|
4218
|
-
...AI_PROVIDERS.map((provider, index2) => {
|
|
4219
|
-
const selected = index2 === selectedIndex ? "\u276F" : " ";
|
|
4220
|
-
const status = isInstalled(provider, options.detection) ? "installed" : "not found on PATH";
|
|
4221
|
-
const current = provider === options.currentProvider ? ", current" : "";
|
|
4222
|
-
return `${selected} ${provider} (${status}${current})`;
|
|
4223
|
-
}),
|
|
4224
|
-
...message ? ["", message] : []
|
|
4225
|
-
];
|
|
4226
|
-
options.stdout.write(`${lines.join("\n")}
|
|
4227
|
-
`);
|
|
4228
|
-
renderedLines = lines.length;
|
|
4229
|
-
};
|
|
4230
|
-
(0, import_node_readline.emitKeypressEvents)(options.stdin);
|
|
4231
|
-
options.stdin.setRawMode(true);
|
|
4232
|
-
options.stdin.resume();
|
|
4233
|
-
options.stdout.write("\x1B[?25l");
|
|
4234
|
-
render();
|
|
4235
|
-
return await new Promise((resolve11, reject) => {
|
|
4236
|
-
const cleanup = () => {
|
|
4237
|
-
options.stdin.off("keypress", onKeypress);
|
|
4238
|
-
options.stdin.setRawMode(false);
|
|
4239
|
-
options.stdin.pause();
|
|
4240
|
-
options.stdout.write("\x1B[?25h");
|
|
4241
|
-
};
|
|
4242
|
-
const clearRendered = () => {
|
|
4243
|
-
if (renderedLines > 0) {
|
|
4244
|
-
(0, import_node_readline.moveCursor)(options.stdout, 0, -renderedLines);
|
|
4245
|
-
(0, import_node_readline.cursorTo)(options.stdout, 0);
|
|
4246
|
-
(0, import_node_readline.clearScreenDown)(options.stdout);
|
|
4247
|
-
}
|
|
4248
|
-
};
|
|
4249
|
-
const finish = (provider) => {
|
|
4250
|
-
cleanup();
|
|
4251
|
-
clearRendered();
|
|
4252
|
-
options.stdout.write(`Selected AI provider: ${provider}
|
|
4253
|
-
`);
|
|
4254
|
-
resolve11(provider);
|
|
4255
|
-
};
|
|
4256
|
-
const onKeypress = (_str, key) => {
|
|
4257
|
-
if (key.ctrl && key.name === "c") {
|
|
4258
|
-
cleanup();
|
|
4259
|
-
clearRendered();
|
|
4260
|
-
options.stdout.write(
|
|
4261
|
-
options.currentProvider ? `AI provider selection cancelled. Keeping current provider: ${options.currentProvider}
|
|
4262
|
-
` : "AI provider selection cancelled. No provider was configured.\n"
|
|
4263
|
-
);
|
|
4264
|
-
reject(new UserCancelledAiProviderSelectionError());
|
|
4265
|
-
return;
|
|
4266
|
-
}
|
|
4267
|
-
if (key.name === "up") {
|
|
4268
|
-
selectedIndex = (selectedIndex + AI_PROVIDERS.length - 1) % AI_PROVIDERS.length;
|
|
4269
|
-
render();
|
|
4270
|
-
return;
|
|
4271
|
-
}
|
|
4272
|
-
if (key.name === "down") {
|
|
4273
|
-
selectedIndex = (selectedIndex + 1) % AI_PROVIDERS.length;
|
|
4274
|
-
render();
|
|
4275
|
-
return;
|
|
4276
|
-
}
|
|
4277
|
-
if (key.name === "return" || key.name === "enter") {
|
|
4278
|
-
const provider = AI_PROVIDERS[selectedIndex];
|
|
4279
|
-
if (!isInstalled(provider, options.detection)) {
|
|
4280
|
-
render(`"${provider}" is not installed or not on PATH. Choose an installed provider.`);
|
|
4281
|
-
return;
|
|
4282
|
-
}
|
|
4283
|
-
finish(provider);
|
|
4284
|
-
}
|
|
4285
|
-
};
|
|
4286
|
-
options.stdin.on("keypress", onKeypress);
|
|
4287
|
-
});
|
|
4288
|
-
}
|
|
4289
|
-
function parseProviderChoice(answer) {
|
|
4290
|
-
if (!answer) {
|
|
4291
|
-
return null;
|
|
4292
|
-
}
|
|
4293
|
-
const number = Number(answer);
|
|
4294
|
-
if (Number.isInteger(number) && number >= 1 && number <= AI_PROVIDERS.length) {
|
|
4295
|
-
return AI_PROVIDERS[number - 1];
|
|
4296
|
-
}
|
|
4297
|
-
try {
|
|
4298
|
-
return normalizeAiProvider(answer);
|
|
4299
|
-
} catch {
|
|
4300
|
-
return null;
|
|
4301
|
-
}
|
|
4302
|
-
}
|
|
4303
|
-
var import_node_child_process5, import_promises6, import_node_readline, AI_PROVIDERS, UserCancelledAiProviderSelectionError;
|
|
4304
|
-
var init_ai_provider = __esm({
|
|
4305
|
-
"src/ai-provider.ts"() {
|
|
4306
|
-
"use strict";
|
|
4307
|
-
import_node_child_process5 = require("node:child_process");
|
|
4308
|
-
import_promises6 = require("node:readline/promises");
|
|
4309
|
-
import_node_readline = require("node:readline");
|
|
4310
|
-
init_ai_command();
|
|
4311
|
-
init_win_spawn();
|
|
4312
|
-
AI_PROVIDERS = ["codex", "claude", "hermes", "openclaw"];
|
|
4313
|
-
UserCancelledAiProviderSelectionError = class extends Error {
|
|
4314
|
-
constructor() {
|
|
4315
|
-
super("AI provider selection cancelled by user.");
|
|
4316
|
-
this.name = "UserCancelledAiProviderSelectionError";
|
|
4317
|
-
}
|
|
4318
|
-
};
|
|
4319
|
-
}
|
|
4320
|
-
});
|
|
4321
|
-
|
|
4322
|
-
// ../../node_modules/@sentry/utils/cjs/is.js
|
|
4323
|
-
var require_is = __commonJS({
|
|
4324
|
-
"../../node_modules/@sentry/utils/cjs/is.js"(exports2) {
|
|
4325
|
-
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
4326
|
-
var objectToString = Object.prototype.toString;
|
|
4327
|
-
function isError(wat) {
|
|
4328
|
-
switch (objectToString.call(wat)) {
|
|
4329
|
-
case "[object Error]":
|
|
4330
|
-
case "[object Exception]":
|
|
4331
|
-
case "[object DOMException]":
|
|
4332
|
-
return true;
|
|
4333
|
-
default:
|
|
4334
|
-
return isInstanceOf(wat, Error);
|
|
4335
|
-
}
|
|
4336
|
-
}
|
|
4337
|
-
function isBuiltin(wat, className) {
|
|
4338
|
-
return objectToString.call(wat) === `[object ${className}]`;
|
|
4339
|
-
}
|
|
4340
|
-
function isErrorEvent(wat) {
|
|
4341
|
-
return isBuiltin(wat, "ErrorEvent");
|
|
3751
|
+
function isErrorEvent(wat) {
|
|
3752
|
+
return isBuiltin(wat, "ErrorEvent");
|
|
4342
3753
|
}
|
|
4343
3754
|
function isDOMError(wat) {
|
|
4344
3755
|
return isBuiltin(wat, "DOMError");
|
|
@@ -6182,7 +5593,7 @@ var require_path = __commonJS({
|
|
|
6182
5593
|
return outputParts.join("/");
|
|
6183
5594
|
}
|
|
6184
5595
|
function normalizePath(path2) {
|
|
6185
|
-
const isPathAbsolute =
|
|
5596
|
+
const isPathAbsolute = isAbsolute4(path2);
|
|
6186
5597
|
const trailingSlash = path2.slice(-1) === "/";
|
|
6187
5598
|
let normalizedPath = normalizeArray(
|
|
6188
5599
|
path2.split("/").filter((p) => !!p),
|
|
@@ -6196,7 +5607,7 @@ var require_path = __commonJS({
|
|
|
6196
5607
|
}
|
|
6197
5608
|
return (isPathAbsolute ? "/" : "") + normalizedPath;
|
|
6198
5609
|
}
|
|
6199
|
-
function
|
|
5610
|
+
function isAbsolute4(path2) {
|
|
6200
5611
|
return path2.charAt(0) === "/";
|
|
6201
5612
|
}
|
|
6202
5613
|
function join31(...args) {
|
|
@@ -6223,7 +5634,7 @@ var require_path = __commonJS({
|
|
|
6223
5634
|
}
|
|
6224
5635
|
exports2.basename = basename8;
|
|
6225
5636
|
exports2.dirname = dirname9;
|
|
6226
|
-
exports2.isAbsolute =
|
|
5637
|
+
exports2.isAbsolute = isAbsolute4;
|
|
6227
5638
|
exports2.join = join31;
|
|
6228
5639
|
exports2.normalizePath = normalizePath;
|
|
6229
5640
|
exports2.relative = relative;
|
|
@@ -19627,6 +19038,14 @@ var init_events = __esm({
|
|
|
19627
19038
|
AI_RUN_STARTED: "AI run started",
|
|
19628
19039
|
AI_RUN_COMPLETED: "AI run completed",
|
|
19629
19040
|
AI_RUN_SKIPPED: "AI run skipped",
|
|
19041
|
+
// Codex GUI runtime detection / binding checkpoints. All four are logger.info,
|
|
19042
|
+
// so each one must also be listed in SENTRY_INFO_ALLOWLIST (index.ts): info()
|
|
19043
|
+
// returns early for an unlisted event, which ships a checkpoint that emits
|
|
19044
|
+
// nothing while every code path looks implemented.
|
|
19045
|
+
RUNTIME_DETECTED: "Runtime detected",
|
|
19046
|
+
PROVIDER_READINESS_CHECKED: "Provider readiness checked",
|
|
19047
|
+
PROVIDER_SWITCHED: "Provider switched",
|
|
19048
|
+
JOB_PROVIDER_BOUND: "Job provider bound",
|
|
19630
19049
|
// Hermes user-channel delivery only. The node and openclaw user-channel hops
|
|
19631
19050
|
// keep emitting USER_DISPATCHED / PROMPT_USER_CHECKPOINT — one name per
|
|
19632
19051
|
// transport is the whole point of splitting the send events.
|
|
@@ -19907,23 +19326,23 @@ function appendActiveXmtpTestMetric(level, eventName, extra) {
|
|
|
19907
19326
|
}
|
|
19908
19327
|
try {
|
|
19909
19328
|
const homeDir = resolveA2aTaskHome();
|
|
19910
|
-
const testsDir = (0,
|
|
19911
|
-
const activePath = (0,
|
|
19912
|
-
if (!(0,
|
|
19329
|
+
const testsDir = (0, import_node_path12.join)(homeDir, "xmtp-tests");
|
|
19330
|
+
const activePath = (0, import_node_path12.join)(testsDir, "active.json");
|
|
19331
|
+
if (!(0, import_node_fs8.existsSync)(activePath)) {
|
|
19913
19332
|
return;
|
|
19914
19333
|
}
|
|
19915
|
-
const active = JSON.parse((0,
|
|
19334
|
+
const active = JSON.parse((0, import_node_fs8.readFileSync)(activePath, "utf8"));
|
|
19916
19335
|
const runId = typeof active.runId === "string" ? active.runId : "";
|
|
19917
19336
|
if (!RUN_ID_PATTERN.test(runId)) {
|
|
19918
19337
|
return;
|
|
19919
19338
|
}
|
|
19920
|
-
const runDir = (0,
|
|
19921
|
-
const root = `${(0,
|
|
19339
|
+
const runDir = (0, import_node_path12.resolve)(testsDir, runId);
|
|
19340
|
+
const root = `${(0, import_node_path12.resolve)(testsDir)}${import_node_path12.sep}`;
|
|
19922
19341
|
if (!runDir.startsWith(root)) {
|
|
19923
19342
|
return;
|
|
19924
19343
|
}
|
|
19925
|
-
(0,
|
|
19926
|
-
(0,
|
|
19344
|
+
(0, import_node_fs8.appendFileSync)(
|
|
19345
|
+
(0, import_node_path12.join)(runDir, "events.jsonl"),
|
|
19927
19346
|
`${JSON.stringify({
|
|
19928
19347
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
19929
19348
|
level,
|
|
@@ -19937,12 +19356,12 @@ function appendActiveXmtpTestMetric(level, eventName, extra) {
|
|
|
19937
19356
|
} catch {
|
|
19938
19357
|
}
|
|
19939
19358
|
}
|
|
19940
|
-
var
|
|
19359
|
+
var import_node_fs8, import_node_path12, RUN_ID_PATTERN, XMTP_TEST_EVENTS;
|
|
19941
19360
|
var init_xmtp_test_metrics = __esm({
|
|
19942
19361
|
"../core/src/xmtp-test-metrics.ts"() {
|
|
19943
19362
|
"use strict";
|
|
19944
|
-
|
|
19945
|
-
|
|
19363
|
+
import_node_fs8 = require("node:fs");
|
|
19364
|
+
import_node_path12 = require("node:path");
|
|
19946
19365
|
init_a2a_paths();
|
|
19947
19366
|
RUN_ID_PATTERN = /^[a-zA-Z0-9][a-zA-Z0-9._-]{0,127}$/;
|
|
19948
19367
|
XMTP_TEST_EVENTS = /* @__PURE__ */ new Set([
|
|
@@ -20182,6 +19601,9 @@ function applyFatalEventDefaults(event) {
|
|
|
20182
19601
|
}
|
|
20183
19602
|
return event;
|
|
20184
19603
|
}
|
|
19604
|
+
function isInfoEventAllowlisted(name) {
|
|
19605
|
+
return SENTRY_INFO_ALLOWLIST.has(name);
|
|
19606
|
+
}
|
|
20185
19607
|
function agentExtras(identity) {
|
|
20186
19608
|
return {
|
|
20187
19609
|
walletAddress: identity.walletAddress || UNKNOWN_FIELD,
|
|
@@ -20387,6 +19809,13 @@ var init_sentry_logger = __esm({
|
|
|
20387
19809
|
LogEvent.AI_RUN_STARTED,
|
|
20388
19810
|
LogEvent.AI_RUN_COMPLETED,
|
|
20389
19811
|
LogEvent.AI_RUN_SKIPPED,
|
|
19812
|
+
// Codex runtime-binding checkpoints. Omitting any of these makes info() drop
|
|
19813
|
+
// the event silently, so the whole FR-4 trace would look implemented in code
|
|
19814
|
+
// and emit nothing in production.
|
|
19815
|
+
LogEvent.RUNTIME_DETECTED,
|
|
19816
|
+
LogEvent.PROVIDER_READINESS_CHECKED,
|
|
19817
|
+
LogEvent.PROVIDER_SWITCHED,
|
|
19818
|
+
LogEvent.JOB_PROVIDER_BOUND,
|
|
20390
19819
|
LogEvent.USER_CHANNEL_MESSAGE_DELIVERED,
|
|
20391
19820
|
LogEvent.SYSTEM_NOTIFICATION_RECEIVED,
|
|
20392
19821
|
LogEvent.SYSTEM_NOTIFICATION_ROUTED,
|
|
@@ -20567,120 +19996,1637 @@ var init_sentry_logger = __esm({
|
|
|
20567
19996
|
} catch {
|
|
20568
19997
|
}
|
|
20569
19998
|
}
|
|
20570
|
-
async shutdown() {
|
|
20571
|
-
try {
|
|
20572
|
-
await Sentry.close(5e3);
|
|
20573
|
-
} catch {
|
|
20574
|
-
await Sentry.flush(5e3);
|
|
20575
|
-
}
|
|
19999
|
+
async shutdown() {
|
|
20000
|
+
try {
|
|
20001
|
+
await Sentry.close(5e3);
|
|
20002
|
+
} catch {
|
|
20003
|
+
await Sentry.flush(5e3);
|
|
20004
|
+
}
|
|
20005
|
+
}
|
|
20006
|
+
flush() {
|
|
20007
|
+
while (this.buffer.length > 0) {
|
|
20008
|
+
const entry = this.buffer.shift();
|
|
20009
|
+
if (entry.level === "info") {
|
|
20010
|
+
this.captureInfo(entry.message, entry.extra ?? {});
|
|
20011
|
+
} else {
|
|
20012
|
+
this.captureError(entry.message, entry.error, entry.extra ?? {});
|
|
20013
|
+
}
|
|
20014
|
+
}
|
|
20015
|
+
}
|
|
20016
|
+
static sanitizeExtra(extra) {
|
|
20017
|
+
return Object.fromEntries(
|
|
20018
|
+
Object.entries(extra).filter(([, value]) => value !== void 0).filter(([key]) => !_SentryLogger.isBlockedExtraKey(key)).map(([key, value]) => [key, _SentryLogger.safeValueForKey(key, value)])
|
|
20019
|
+
);
|
|
20020
|
+
}
|
|
20021
|
+
static isBlockedExtraKey(key) {
|
|
20022
|
+
const lower = key.toLowerCase();
|
|
20023
|
+
const normalized = _SentryLogger.normalizeSensitiveKey(key);
|
|
20024
|
+
if (SENTRY_EXTRA_BLOCKLIST.has(lower) || SENTRY_EXTRA_BLOCKLIST.has(normalized)) {
|
|
20025
|
+
return true;
|
|
20026
|
+
}
|
|
20027
|
+
if (_SentryLogger.isSafeMetricKey(normalized)) {
|
|
20028
|
+
return false;
|
|
20029
|
+
}
|
|
20030
|
+
return SENTRY_EXTRA_BLOCKED_KEY_PARTS.some((needle) => normalized.includes(needle));
|
|
20031
|
+
}
|
|
20032
|
+
static isSafeMetricKey(normalized) {
|
|
20033
|
+
return /(bytes|count|length|ms|size)$/.test(normalized);
|
|
20034
|
+
}
|
|
20035
|
+
static safeValueForKey(key, value) {
|
|
20036
|
+
if (typeof value === "string" && SENTRY_FULL_STRING_EXTRA_KEYS.has(_SentryLogger.normalizeSensitiveKey(key))) {
|
|
20037
|
+
return value;
|
|
20038
|
+
}
|
|
20039
|
+
return _SentryLogger.safeValue(value);
|
|
20040
|
+
}
|
|
20041
|
+
static normalizeSensitiveKey(key) {
|
|
20042
|
+
return key.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
20043
|
+
}
|
|
20044
|
+
static safeValue(value) {
|
|
20045
|
+
if (typeof value === "string") {
|
|
20046
|
+
return value.length <= MAX_EXTRA_STRING_LENGTH ? value : `${value.slice(0, MAX_EXTRA_STRING_LENGTH)}...`;
|
|
20047
|
+
}
|
|
20048
|
+
if (value === null || value === void 0 || typeof value === "number" || typeof value === "boolean") {
|
|
20049
|
+
return value;
|
|
20050
|
+
}
|
|
20051
|
+
if (typeof value === "bigint") {
|
|
20052
|
+
return value.toString();
|
|
20053
|
+
}
|
|
20054
|
+
if (value instanceof Error) {
|
|
20055
|
+
return {
|
|
20056
|
+
name: value.name,
|
|
20057
|
+
messageLength: value.message.length
|
|
20058
|
+
};
|
|
20059
|
+
}
|
|
20060
|
+
if (Array.isArray(value)) {
|
|
20061
|
+
return value.slice(0, 20).map((item) => _SentryLogger.safeValue(item));
|
|
20062
|
+
}
|
|
20063
|
+
if (typeof value === "object") {
|
|
20064
|
+
return Object.fromEntries(
|
|
20065
|
+
Object.entries(value).slice(0, 50).filter(([, item]) => item !== void 0).filter(([key]) => !_SentryLogger.isBlockedExtraKey(key)).map(([key, item]) => [key, _SentryLogger.safeValueForKey(key, item)])
|
|
20066
|
+
);
|
|
20067
|
+
}
|
|
20068
|
+
const text = String(value);
|
|
20069
|
+
return text.length <= MAX_EXTRA_STRING_LENGTH ? text : `${text.slice(0, MAX_EXTRA_STRING_LENGTH)}...`;
|
|
20070
|
+
}
|
|
20071
|
+
static tagValue(value) {
|
|
20072
|
+
const text = String(value);
|
|
20073
|
+
return text.length <= MAX_TAG_VALUE_LENGTH ? text : `${text.slice(0, MAX_TAG_VALUE_LENGTH)}...`;
|
|
20074
|
+
}
|
|
20075
|
+
static normalizeTagKey(key) {
|
|
20076
|
+
return key.replace(/[^A-Za-z0-9_.:-]+/g, "_").slice(0, 32) || "unknown";
|
|
20077
|
+
}
|
|
20078
|
+
static applyDiagnostics(scope, eventName, extra) {
|
|
20079
|
+
const enriched = { eventName, ...extra };
|
|
20080
|
+
for (const [key, value] of Object.entries(enriched)) {
|
|
20081
|
+
if (!SENTRY_TAG_KEYS.has(key)) {
|
|
20082
|
+
continue;
|
|
20083
|
+
}
|
|
20084
|
+
scope.setTag(_SentryLogger.normalizeTagKey(key), _SentryLogger.tagValue(value));
|
|
20085
|
+
}
|
|
20086
|
+
const fingerprint = ["a2a", eventName];
|
|
20087
|
+
for (const key of SENTRY_FINGERPRINT_KEYS) {
|
|
20088
|
+
const value = enriched[key];
|
|
20089
|
+
if (value) {
|
|
20090
|
+
fingerprint.push(`${key}:${_SentryLogger.tagValue(value)}`);
|
|
20091
|
+
}
|
|
20092
|
+
}
|
|
20093
|
+
scope.setFingerprint(fingerprint);
|
|
20094
|
+
}
|
|
20095
|
+
static toPascalCase(str) {
|
|
20096
|
+
return str.split(/[\s:]+/).filter(Boolean).map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join("");
|
|
20097
|
+
}
|
|
20098
|
+
static createSentryEvent(eventName) {
|
|
20099
|
+
const err = new Error(eventName);
|
|
20100
|
+
err.name = _SentryLogger.toPascalCase(eventName);
|
|
20101
|
+
err.stack = `${err.name}: ${eventName}`;
|
|
20102
|
+
return err;
|
|
20103
|
+
}
|
|
20104
|
+
};
|
|
20105
|
+
logger = SentryLogger.getInstance();
|
|
20106
|
+
initLogger = (config) => {
|
|
20107
|
+
return logger.init(config);
|
|
20108
|
+
};
|
|
20109
|
+
shutdown = () => {
|
|
20110
|
+
return logger.shutdown();
|
|
20111
|
+
};
|
|
20112
|
+
UNKNOWN_FIELD = "unknown";
|
|
20113
|
+
}
|
|
20114
|
+
});
|
|
20115
|
+
|
|
20116
|
+
// src/ai-provider.ts
|
|
20117
|
+
function normalizeAiProvider(value) {
|
|
20118
|
+
const normalized = value.trim().toLowerCase();
|
|
20119
|
+
if (normalized === "codex") {
|
|
20120
|
+
return "codex";
|
|
20121
|
+
}
|
|
20122
|
+
if (normalized === "claude" || normalized === "claude-code" || normalized === "claude_code") {
|
|
20123
|
+
return "claude";
|
|
20124
|
+
}
|
|
20125
|
+
if (normalized === "hermes") {
|
|
20126
|
+
return "hermes";
|
|
20127
|
+
}
|
|
20128
|
+
if (normalized === "openclaw") {
|
|
20129
|
+
return "openclaw";
|
|
20130
|
+
}
|
|
20131
|
+
throw new Error(`Unsupported AI provider "${value}". Use one of: ${AI_PROVIDERS.join(", ")}.`);
|
|
20132
|
+
}
|
|
20133
|
+
function detectAiProviders(commandExists2 = commandExists, env = process.env) {
|
|
20134
|
+
const codex = commandExists2(readProviderCommand("codex", env));
|
|
20135
|
+
const claude = commandExists2(readProviderCommand("claude", env));
|
|
20136
|
+
const hermes = commandExists2(readProviderCommand("hermes", env));
|
|
20137
|
+
const openclaw = commandExists2(readProviderCommand("openclaw", env));
|
|
20138
|
+
return {
|
|
20139
|
+
codex,
|
|
20140
|
+
claude,
|
|
20141
|
+
hermes,
|
|
20142
|
+
openclaw,
|
|
20143
|
+
available: [
|
|
20144
|
+
...codex ? ["codex"] : [],
|
|
20145
|
+
...claude ? ["claude"] : [],
|
|
20146
|
+
...hermes ? ["hermes"] : [],
|
|
20147
|
+
...openclaw ? ["openclaw"] : []
|
|
20148
|
+
]
|
|
20149
|
+
};
|
|
20150
|
+
}
|
|
20151
|
+
function detectCurrentAiProvider(env = process.env) {
|
|
20152
|
+
const explicit = env.OKX_A2A_AI_PROVIDER ?? env.OKX_AGENT_TASK_AI_CLI;
|
|
20153
|
+
if (explicit) {
|
|
20154
|
+
return normalizeAiProvider(explicit);
|
|
20155
|
+
}
|
|
20156
|
+
const runtime = detectRuntime(env);
|
|
20157
|
+
return runtime === "unknown" ? null : runtime;
|
|
20158
|
+
}
|
|
20159
|
+
function hasAiRuntimeMarker(env = process.env) {
|
|
20160
|
+
return !!(detectGatewayInvocation(env) || env.CODEX_THREAD_ID || isTruthyRuntimeMarker(env.CODEX_SHELL) || env.CODEX_MANAGED_BY_NPM || isTruthyRuntimeMarker(env.CODEX_CI) || env.CLAUDE_SESSION_ID || isTruthyRuntimeMarker(env.CLAUDECODE) || env.CLAUDE_CODE_SESSION_ID || env.CLAUDE_PLUGIN_DATA || env.HERMES_SESSION_ID && !env.HERMES_DESKTOP_CWD || env.HERMES_SESSION_KEY || env.HERMES_FLOW_ID || env.OPENCLAW_SHELL || env.OPENCLAW_CLI);
|
|
20161
|
+
}
|
|
20162
|
+
function detectRuntime(env = process.env, options = {}) {
|
|
20163
|
+
const gatewayInvocation = detectGatewayInvocation(env);
|
|
20164
|
+
const strongMatches = [
|
|
20165
|
+
...gatewayInvocation ? [gatewayInvocation] : [],
|
|
20166
|
+
...env.CODEX_THREAD_ID || isTruthyRuntimeMarker(env.CODEX_SHELL) || env.CODEX_MANAGED_BY_NPM || isTruthyRuntimeMarker(env.CODEX_CI) ? ["codex"] : [],
|
|
20167
|
+
...env.CLAUDE_SESSION_ID || isTruthyRuntimeMarker(env.CLAUDECODE) || env.CLAUDE_CODE_SESSION_ID || env.CLAUDE_PLUGIN_DATA ? ["claude"] : [],
|
|
20168
|
+
...env.HERMES_SESSION_ID && !env.HERMES_DESKTOP_CWD || env.HERMES_SESSION_KEY || env.HERMES_FLOW_ID ? ["hermes"] : [],
|
|
20169
|
+
...env.OPENCLAW_SHELL || env.OPENCLAW_CLI ? ["openclaw"] : [],
|
|
20170
|
+
...hasOpenClawParentProcess(options.parentPid ?? process.ppid, options.readParentProcessCommand ?? readParentProcessCommand) ? ["openclaw"] : []
|
|
20171
|
+
];
|
|
20172
|
+
const uniqueMatches = [...new Set(strongMatches)];
|
|
20173
|
+
if (uniqueMatches.length === 1) {
|
|
20174
|
+
return uniqueMatches[0];
|
|
20175
|
+
}
|
|
20176
|
+
return "unknown";
|
|
20177
|
+
}
|
|
20178
|
+
function detectGatewayInvocation(env = process.env) {
|
|
20179
|
+
const context = (env.OKX_A2A_RUNTIME_CONTEXT ?? "").trim().toLowerCase();
|
|
20180
|
+
if (context === "openclaw-gateway" || context === "gateway:openclaw") {
|
|
20181
|
+
return "openclaw";
|
|
20182
|
+
}
|
|
20183
|
+
if (context === "hermes-gateway" || context === "gateway:hermes") {
|
|
20184
|
+
return "hermes";
|
|
20185
|
+
}
|
|
20186
|
+
if (isTruthyRuntimeMarker(env._HERMES_GATEWAY)) {
|
|
20187
|
+
return "hermes";
|
|
20188
|
+
}
|
|
20189
|
+
if ((env.OPENCLAW_SERVICE_KIND ?? "").trim().toLowerCase() === "gateway") {
|
|
20190
|
+
return "openclaw";
|
|
20191
|
+
}
|
|
20192
|
+
if (!isTruthyRuntimeMarker(env.OKX_A2A_IN_GATEWAY)) {
|
|
20193
|
+
return null;
|
|
20194
|
+
}
|
|
20195
|
+
const provider = env.OKX_A2A_GATEWAY_PROVIDER;
|
|
20196
|
+
if (!provider) {
|
|
20197
|
+
return null;
|
|
20198
|
+
}
|
|
20199
|
+
try {
|
|
20200
|
+
const normalized = normalizeAiProvider(provider);
|
|
20201
|
+
return normalized === "openclaw" || normalized === "hermes" ? normalized : null;
|
|
20202
|
+
} catch {
|
|
20203
|
+
return null;
|
|
20204
|
+
}
|
|
20205
|
+
}
|
|
20206
|
+
function switchProvider(options) {
|
|
20207
|
+
const runtime = detectRuntime(options.env ?? process.env, {
|
|
20208
|
+
parentPid: options.parentPid,
|
|
20209
|
+
readParentProcessCommand: options.readParentProcessCommand
|
|
20210
|
+
});
|
|
20211
|
+
if (runtime === "unknown") {
|
|
20212
|
+
const stored = options.store.getDefaultAiProvider();
|
|
20213
|
+
if (stored) {
|
|
20214
|
+
return {
|
|
20215
|
+
ok: true,
|
|
20216
|
+
runtime: "unknown",
|
|
20217
|
+
provider: stored,
|
|
20218
|
+
previousProvider: stored,
|
|
20219
|
+
changed: false,
|
|
20220
|
+
action: "kept_existing",
|
|
20221
|
+
userMessage: `No AI runtime detected for the current environment; keeping the existing default provider (${formatAiProviderDisplayName(stored)}).`
|
|
20222
|
+
};
|
|
20223
|
+
}
|
|
20224
|
+
return {
|
|
20225
|
+
ok: false,
|
|
20226
|
+
runtime: "unknown",
|
|
20227
|
+
reason: "unknown_runtime",
|
|
20228
|
+
state: "blocked",
|
|
20229
|
+
message: "Could not determine the current AI provider.",
|
|
20230
|
+
userMessage: "Could not detect the current AI runtime and no default AI provider is configured. Run this from your AI environment (OpenClaw / Codex / Claude / Hermes), or set a provider manually: `okx-a2a ai-provider set --provider <codex|claude|hermes|openclaw>`."
|
|
20231
|
+
};
|
|
20232
|
+
}
|
|
20233
|
+
const previousProvider = options.store.getDefaultAiProvider();
|
|
20234
|
+
const changed = previousProvider !== runtime;
|
|
20235
|
+
options.store.setDefaultAiProvider(runtime);
|
|
20236
|
+
return {
|
|
20237
|
+
ok: true,
|
|
20238
|
+
runtime,
|
|
20239
|
+
provider: runtime,
|
|
20240
|
+
previousProvider,
|
|
20241
|
+
changed,
|
|
20242
|
+
action: "switched",
|
|
20243
|
+
userMessage: changed ? `Switched to ${formatAiProviderDisplayName(runtime)}\uFF5CNew tasks will be handled here; unfinished tasks should be continued in their original session` : ""
|
|
20244
|
+
};
|
|
20245
|
+
}
|
|
20246
|
+
function formatAiProviderDisplayName(provider) {
|
|
20247
|
+
if (provider === "openclaw") {
|
|
20248
|
+
return "OpenClaw";
|
|
20249
|
+
}
|
|
20250
|
+
return `${provider.charAt(0).toUpperCase()}${provider.slice(1)}`;
|
|
20251
|
+
}
|
|
20252
|
+
function isTruthyRuntimeMarker(value) {
|
|
20253
|
+
return value === "1" || value === "true" || value === "yes";
|
|
20254
|
+
}
|
|
20255
|
+
function hasOpenClawParentProcess(parentPid, readParentProcessCommand2) {
|
|
20256
|
+
let pid = parentPid;
|
|
20257
|
+
for (let i = 0; i < 8; i++) {
|
|
20258
|
+
if (!pid || pid <= 1) {
|
|
20259
|
+
return false;
|
|
20260
|
+
}
|
|
20261
|
+
const processInfo = readParentProcessCommand2(pid);
|
|
20262
|
+
if (!processInfo) {
|
|
20263
|
+
return false;
|
|
20264
|
+
}
|
|
20265
|
+
if (/openclaw/i.test(processInfo.command)) {
|
|
20266
|
+
return true;
|
|
20267
|
+
}
|
|
20268
|
+
if (!processInfo.parentPid || processInfo.parentPid === pid) {
|
|
20269
|
+
return false;
|
|
20270
|
+
}
|
|
20271
|
+
pid = processInfo.parentPid;
|
|
20272
|
+
}
|
|
20273
|
+
return false;
|
|
20274
|
+
}
|
|
20275
|
+
function readParentProcessCommand(pid) {
|
|
20276
|
+
return readParentProcessCommandForPlatform(pid, process.platform);
|
|
20277
|
+
}
|
|
20278
|
+
function readParentProcessCommandForPlatform(pid, platform) {
|
|
20279
|
+
if (platform === "win32") {
|
|
20280
|
+
return readWindowsParentProcessCommand(pid);
|
|
20281
|
+
}
|
|
20282
|
+
const commandResult = (0, import_node_child_process5.spawnSync)("ps", ["-p", String(pid), "-o", "comm="], {
|
|
20283
|
+
encoding: "utf8",
|
|
20284
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
20285
|
+
});
|
|
20286
|
+
const parentResult = (0, import_node_child_process5.spawnSync)("ps", ["-p", String(pid), "-o", "ppid="], {
|
|
20287
|
+
encoding: "utf8",
|
|
20288
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
20289
|
+
});
|
|
20290
|
+
const command = typeof commandResult.stdout === "string" ? commandResult.stdout.trim() : "";
|
|
20291
|
+
const parentPidRaw = typeof parentResult.stdout === "string" ? parentResult.stdout.trim() : "";
|
|
20292
|
+
const parentPid = Number(parentPidRaw);
|
|
20293
|
+
if (commandResult.status !== 0 || parentResult.status !== 0 || !command || !Number.isFinite(parentPid)) {
|
|
20294
|
+
return null;
|
|
20295
|
+
}
|
|
20296
|
+
return { command, parentPid };
|
|
20297
|
+
}
|
|
20298
|
+
function buildWindowsParentProcessProbe(pid) {
|
|
20299
|
+
if (!Number.isInteger(pid)) {
|
|
20300
|
+
return null;
|
|
20301
|
+
}
|
|
20302
|
+
return {
|
|
20303
|
+
command: "powershell.exe",
|
|
20304
|
+
args: [
|
|
20305
|
+
"-NoProfile",
|
|
20306
|
+
"-NonInteractive",
|
|
20307
|
+
"-Command",
|
|
20308
|
+
`Get-CimInstance Win32_Process -Filter "ProcessId=${pid}" | Select-Object Name,ParentProcessId | ConvertTo-Json -Compress`
|
|
20309
|
+
]
|
|
20310
|
+
};
|
|
20311
|
+
}
|
|
20312
|
+
function parseWindowsParentProcessJson(stdout) {
|
|
20313
|
+
const trimmed = stdout.trim();
|
|
20314
|
+
if (!trimmed) {
|
|
20315
|
+
return null;
|
|
20316
|
+
}
|
|
20317
|
+
let parsed;
|
|
20318
|
+
try {
|
|
20319
|
+
parsed = JSON.parse(trimmed);
|
|
20320
|
+
} catch {
|
|
20321
|
+
return null;
|
|
20322
|
+
}
|
|
20323
|
+
const record = Array.isArray(parsed) ? parsed[0] : parsed;
|
|
20324
|
+
if (!record || typeof record !== "object") {
|
|
20325
|
+
return null;
|
|
20326
|
+
}
|
|
20327
|
+
const name = record.Name;
|
|
20328
|
+
if (typeof name !== "string" || name.trim() === "") {
|
|
20329
|
+
return null;
|
|
20330
|
+
}
|
|
20331
|
+
const rawParentPid = Number(record.ParentProcessId);
|
|
20332
|
+
const parentPid = Number.isInteger(rawParentPid) && rawParentPid > 0 ? rawParentPid : null;
|
|
20333
|
+
return { command: name.trim(), parentPid };
|
|
20334
|
+
}
|
|
20335
|
+
function readWindowsParentProcessCommand(pid) {
|
|
20336
|
+
const probe = buildWindowsParentProcessProbe(pid);
|
|
20337
|
+
if (!probe) {
|
|
20338
|
+
logWinCompat(`${WIN_COMPAT_LOG_PREFIX} parent-process probe rejected non-integer pid=${String(pid)}`);
|
|
20339
|
+
return null;
|
|
20340
|
+
}
|
|
20341
|
+
const result = (0, import_node_child_process5.spawnSync)(probe.command, probe.args, {
|
|
20342
|
+
encoding: "utf8",
|
|
20343
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
20344
|
+
timeout: 5e3,
|
|
20345
|
+
windowsHide: true
|
|
20346
|
+
});
|
|
20347
|
+
if (result.error) {
|
|
20348
|
+
const spawnError = result.error;
|
|
20349
|
+
logWinCompat(
|
|
20350
|
+
`${WIN_COMPAT_LOG_PREFIX} parent-process probe failed pid=${pid} command=${probe.command} errorCode=${spawnError.code ?? "unknown"} error=${spawnError.message}`
|
|
20351
|
+
);
|
|
20352
|
+
return null;
|
|
20353
|
+
}
|
|
20354
|
+
if (result.status !== 0) {
|
|
20355
|
+
logWinCompat(
|
|
20356
|
+
`${WIN_COMPAT_LOG_PREFIX} parent-process probe exited pid=${pid} command=${probe.command} status=${String(result.status)} signal=${String(result.signal)}`
|
|
20357
|
+
);
|
|
20358
|
+
return null;
|
|
20359
|
+
}
|
|
20360
|
+
const stdout = typeof result.stdout === "string" ? result.stdout : "";
|
|
20361
|
+
const parsed = parseWindowsParentProcessJson(stdout);
|
|
20362
|
+
if (!parsed) {
|
|
20363
|
+
logWinCompat(
|
|
20364
|
+
`${WIN_COMPAT_LOG_PREFIX} parent-process probe unparsable output pid=${pid} stdout=${JSON.stringify(stdout.slice(0, 200))}`
|
|
20365
|
+
);
|
|
20366
|
+
return null;
|
|
20367
|
+
}
|
|
20368
|
+
if (process.env.OKX_A2A_WIN_COMPAT_VERBOSE === "1") {
|
|
20369
|
+
logWinCompat(
|
|
20370
|
+
`${WIN_COMPAT_LOG_PREFIX} parent-process probe pid=${pid} -> command=${parsed.command} parentPid=${parsed.parentPid ?? "null"}`
|
|
20371
|
+
);
|
|
20372
|
+
}
|
|
20373
|
+
return parsed;
|
|
20374
|
+
}
|
|
20375
|
+
async function ensureDefaultAiProvider(options) {
|
|
20376
|
+
const env = options.env ?? process.env;
|
|
20377
|
+
const commandExists2 = options.commandExists ?? commandExists;
|
|
20378
|
+
const detection = withStoredCodexAvailability(
|
|
20379
|
+
detectAiProviders(commandExists2, env),
|
|
20380
|
+
options.store,
|
|
20381
|
+
commandExists2,
|
|
20382
|
+
env
|
|
20383
|
+
);
|
|
20384
|
+
if (options.requestedProvider) {
|
|
20385
|
+
const provider = normalizeAiProvider(options.requestedProvider);
|
|
20386
|
+
if (!options.allowUnavailableProvider) {
|
|
20387
|
+
assertInstalled(provider, detection);
|
|
20388
|
+
}
|
|
20389
|
+
options.store.setDefaultAiProvider(provider);
|
|
20390
|
+
return {
|
|
20391
|
+
provider,
|
|
20392
|
+
source: "requested",
|
|
20393
|
+
message: isInstalled(provider, detection) ? `default AI provider set to ${provider}` : `default AI provider set to ${provider}; availability was not verified on PATH`
|
|
20394
|
+
};
|
|
20395
|
+
}
|
|
20396
|
+
if (options.forcePrompt) {
|
|
20397
|
+
const provider = await promptForAiProvider({
|
|
20398
|
+
detection,
|
|
20399
|
+
stdin: options.stdin,
|
|
20400
|
+
stdout: options.stdout,
|
|
20401
|
+
currentProvider: options.store.getDefaultAiProvider()
|
|
20402
|
+
});
|
|
20403
|
+
options.store.setDefaultAiProvider(provider);
|
|
20404
|
+
return {
|
|
20405
|
+
provider,
|
|
20406
|
+
source: "detected",
|
|
20407
|
+
message: `default AI provider set to ${provider}`
|
|
20408
|
+
};
|
|
20409
|
+
}
|
|
20410
|
+
if (env.OKX_AGENT_TASK_AI_CLI) {
|
|
20411
|
+
const provider = normalizeAiProvider(env.OKX_AGENT_TASK_AI_CLI);
|
|
20412
|
+
if (!options.allowUnavailableProvider) {
|
|
20413
|
+
assertInstalled(provider, detection);
|
|
20414
|
+
}
|
|
20415
|
+
options.store.setDefaultAiProvider(provider);
|
|
20416
|
+
return {
|
|
20417
|
+
provider,
|
|
20418
|
+
source: "env",
|
|
20419
|
+
message: isInstalled(provider, detection) ? `default AI provider set to ${provider} from OKX_AGENT_TASK_AI_CLI` : `default AI provider set to ${provider} from OKX_AGENT_TASK_AI_CLI; availability was not verified on PATH`
|
|
20420
|
+
};
|
|
20421
|
+
}
|
|
20422
|
+
if (env.OKX_A2A_AI_PROVIDER) {
|
|
20423
|
+
const provider = normalizeAiProvider(env.OKX_A2A_AI_PROVIDER);
|
|
20424
|
+
if (!options.allowUnavailableProvider) {
|
|
20425
|
+
assertInstalled(provider, detection);
|
|
20426
|
+
}
|
|
20427
|
+
return {
|
|
20428
|
+
provider,
|
|
20429
|
+
source: "env",
|
|
20430
|
+
message: isInstalled(provider, detection) ? `AI provider resolved from OKX_A2A_AI_PROVIDER: ${provider}` : `AI provider resolved from OKX_A2A_AI_PROVIDER: ${provider}; availability was not verified on PATH`
|
|
20431
|
+
};
|
|
20432
|
+
}
|
|
20433
|
+
const stored = options.store.getDefaultAiProvider();
|
|
20434
|
+
if (stored && (options.allowUnavailableProvider || isInstalled(stored, detection))) {
|
|
20435
|
+
return {
|
|
20436
|
+
provider: stored,
|
|
20437
|
+
source: "stored",
|
|
20438
|
+
message: isInstalled(stored, detection) ? `default AI provider is ${stored}` : `default AI provider is ${stored}; availability was not verified on PATH`
|
|
20439
|
+
};
|
|
20440
|
+
}
|
|
20441
|
+
if (stored) {
|
|
20442
|
+
options.stderr?.write(
|
|
20443
|
+
`[okx-agent-task] stored AI provider "${stored}" is not installed or not on PATH; selecting again.
|
|
20444
|
+
`
|
|
20445
|
+
);
|
|
20446
|
+
}
|
|
20447
|
+
if (options.promptIfMissing) {
|
|
20448
|
+
const provider = await promptForAiProvider({
|
|
20449
|
+
detection,
|
|
20450
|
+
stdin: options.stdin,
|
|
20451
|
+
stdout: options.stdout,
|
|
20452
|
+
currentProvider: stored ?? null
|
|
20453
|
+
});
|
|
20454
|
+
options.store.setDefaultAiProvider(provider);
|
|
20455
|
+
return {
|
|
20456
|
+
provider,
|
|
20457
|
+
source: "detected",
|
|
20458
|
+
message: `default AI provider set to ${provider}`
|
|
20459
|
+
};
|
|
20460
|
+
}
|
|
20461
|
+
const current = detectCurrentAiProvider(env);
|
|
20462
|
+
if (current && (options.allowUnavailableProvider || isInstalled(current, detection))) {
|
|
20463
|
+
return {
|
|
20464
|
+
provider: current,
|
|
20465
|
+
source: "env",
|
|
20466
|
+
message: isInstalled(current, detection) ? `AI provider resolved from current environment: ${current}` : `AI provider resolved from current environment: ${current}; availability was not verified on PATH`
|
|
20467
|
+
};
|
|
20468
|
+
}
|
|
20469
|
+
if (detection.available.length === 0) {
|
|
20470
|
+
throw new Error(
|
|
20471
|
+
`No supported AI CLI found. Install one of: ${AI_PROVIDERS.join(", ")}.`
|
|
20472
|
+
);
|
|
20473
|
+
}
|
|
20474
|
+
if (detection.available.length === 1) {
|
|
20475
|
+
const provider = detection.available[0];
|
|
20476
|
+
options.store.setDefaultAiProvider(provider);
|
|
20477
|
+
return {
|
|
20478
|
+
provider,
|
|
20479
|
+
source: "detected",
|
|
20480
|
+
message: `detected only ${provider}; default AI provider set to ${provider}`
|
|
20481
|
+
};
|
|
20482
|
+
}
|
|
20483
|
+
throw new Error(
|
|
20484
|
+
`Multiple AI CLIs are installed (${detection.available.join(", ")}) but no current AI environment was detected. Start the daemon from an AI session, pass --ai-provider <provider>, or set OKX_A2A_AI_PROVIDER.`
|
|
20485
|
+
);
|
|
20486
|
+
}
|
|
20487
|
+
function resolveConfiguredAiProvider(options) {
|
|
20488
|
+
const env = options.env ?? process.env;
|
|
20489
|
+
const explicit = env.OKX_AGENT_TASK_AI_CLI ?? env.OKX_A2A_AI_PROVIDER;
|
|
20490
|
+
if (explicit) {
|
|
20491
|
+
return normalizeAiProvider(explicit);
|
|
20492
|
+
}
|
|
20493
|
+
return options.store.getDefaultAiProvider();
|
|
20494
|
+
}
|
|
20495
|
+
function resolveConfiguredAiProviderForJob(options) {
|
|
20496
|
+
const jobId = normalizeOptionalJobId2(options.jobId);
|
|
20497
|
+
if (jobId) {
|
|
20498
|
+
const binding = options.store.getJobProviderBinding(jobId);
|
|
20499
|
+
if (binding) {
|
|
20500
|
+
bindHermesJobRouteFromEnvIfAvailable(options.store, {
|
|
20501
|
+
jobId,
|
|
20502
|
+
provider: binding.provider,
|
|
20503
|
+
env: options.env
|
|
20504
|
+
});
|
|
20505
|
+
return binding.provider;
|
|
20506
|
+
}
|
|
20507
|
+
}
|
|
20508
|
+
const provider = resolveConfiguredAiProvider({
|
|
20509
|
+
store: options.store,
|
|
20510
|
+
env: options.env
|
|
20511
|
+
});
|
|
20512
|
+
if (jobId && provider) {
|
|
20513
|
+
return bindJobProviderWithRouteIfAvailable(options.store, {
|
|
20514
|
+
jobId,
|
|
20515
|
+
provider,
|
|
20516
|
+
env: options.env
|
|
20517
|
+
}).binding.provider;
|
|
20518
|
+
}
|
|
20519
|
+
return provider;
|
|
20520
|
+
}
|
|
20521
|
+
function bindJobProviderToCurrentDefaultIfMissing(options) {
|
|
20522
|
+
const store = options.store ?? null;
|
|
20523
|
+
const jobId = normalizeOptionalJobId2(options.jobId);
|
|
20524
|
+
if (!store || !jobId) {
|
|
20525
|
+
return null;
|
|
20526
|
+
}
|
|
20527
|
+
const existing = store.getJobProviderBinding(jobId);
|
|
20528
|
+
if (existing) {
|
|
20529
|
+
bindHermesJobRouteFromEnvIfAvailable(store, {
|
|
20530
|
+
jobId,
|
|
20531
|
+
provider: existing.provider,
|
|
20532
|
+
env: options.env
|
|
20533
|
+
});
|
|
20534
|
+
return {
|
|
20535
|
+
provider: existing.provider,
|
|
20536
|
+
created: false
|
|
20537
|
+
};
|
|
20538
|
+
}
|
|
20539
|
+
const provider = store.getDefaultAiProvider();
|
|
20540
|
+
if (!provider) {
|
|
20541
|
+
return null;
|
|
20542
|
+
}
|
|
20543
|
+
const result = bindJobProviderWithRouteIfAvailable(store, {
|
|
20544
|
+
jobId,
|
|
20545
|
+
provider,
|
|
20546
|
+
env: options.env
|
|
20547
|
+
});
|
|
20548
|
+
return {
|
|
20549
|
+
provider: result.binding.provider,
|
|
20550
|
+
created: result.created
|
|
20551
|
+
};
|
|
20552
|
+
}
|
|
20553
|
+
function bindJobProviderToCurrentRuntimeIfMissing(options) {
|
|
20554
|
+
const store = options.store ?? null;
|
|
20555
|
+
const jobId = normalizeOptionalJobId2(options.jobId);
|
|
20556
|
+
if (!store || !jobId) {
|
|
20557
|
+
return {
|
|
20558
|
+
ok: false,
|
|
20559
|
+
jobId: jobId ?? "",
|
|
20560
|
+
provider: null,
|
|
20561
|
+
currentProvider: null,
|
|
20562
|
+
created: false,
|
|
20563
|
+
binding: null,
|
|
20564
|
+
reason: "missing_job_id"
|
|
20565
|
+
};
|
|
20566
|
+
}
|
|
20567
|
+
const existing = store.getJobProviderBinding(jobId);
|
|
20568
|
+
if (existing) {
|
|
20569
|
+
const binding = bindHermesJobRouteFromEnvIfAvailable(store, {
|
|
20570
|
+
jobId,
|
|
20571
|
+
provider: existing.provider,
|
|
20572
|
+
env: options.env
|
|
20573
|
+
}) ?? existing;
|
|
20574
|
+
return {
|
|
20575
|
+
ok: true,
|
|
20576
|
+
jobId,
|
|
20577
|
+
provider: binding.provider,
|
|
20578
|
+
currentProvider: null,
|
|
20579
|
+
created: false,
|
|
20580
|
+
binding,
|
|
20581
|
+
reason: "already_bound"
|
|
20582
|
+
};
|
|
20583
|
+
}
|
|
20584
|
+
const switched = switchProvider({ store, env: options.env ?? process.env });
|
|
20585
|
+
if (!switched.ok || !switched.provider) {
|
|
20586
|
+
return {
|
|
20587
|
+
ok: false,
|
|
20588
|
+
jobId,
|
|
20589
|
+
provider: null,
|
|
20590
|
+
currentProvider: null,
|
|
20591
|
+
created: false,
|
|
20592
|
+
binding: null,
|
|
20593
|
+
reason: "unknown_runtime"
|
|
20594
|
+
};
|
|
20595
|
+
}
|
|
20596
|
+
const currentProvider = switched.provider;
|
|
20597
|
+
const result = bindJobProviderWithRouteIfAvailable(store, {
|
|
20598
|
+
jobId,
|
|
20599
|
+
provider: currentProvider,
|
|
20600
|
+
env: options.env
|
|
20601
|
+
});
|
|
20602
|
+
return {
|
|
20603
|
+
ok: true,
|
|
20604
|
+
jobId,
|
|
20605
|
+
provider: result.binding.provider,
|
|
20606
|
+
currentProvider,
|
|
20607
|
+
created: result.created,
|
|
20608
|
+
binding: result.binding,
|
|
20609
|
+
reason: result.created ? "created" : "already_bound"
|
|
20610
|
+
};
|
|
20611
|
+
}
|
|
20612
|
+
async function ensureProviderReadyForBinding(options) {
|
|
20613
|
+
if (options.provider !== "codex") {
|
|
20614
|
+
return notRequiredReadinessGate(options.provider);
|
|
20615
|
+
}
|
|
20616
|
+
const check = options.readiness ?? checkCodexReadiness;
|
|
20617
|
+
const result = await check({
|
|
20618
|
+
store: options.store ?? null,
|
|
20619
|
+
env: options.env ?? process.env,
|
|
20620
|
+
platform: options.platform,
|
|
20621
|
+
persistOverrideCommand: options.persistOverrideCommand
|
|
20622
|
+
});
|
|
20623
|
+
return {
|
|
20624
|
+
provider: "codex",
|
|
20625
|
+
ready: result.ready,
|
|
20626
|
+
state: result.state,
|
|
20627
|
+
commandSource: result.commandSource,
|
|
20628
|
+
commandPersisted: result.commandPersisted,
|
|
20629
|
+
recoveryAction: result.recoveryAction,
|
|
20630
|
+
recoveryGuidance: result.recoveryGuidance
|
|
20631
|
+
};
|
|
20632
|
+
}
|
|
20633
|
+
async function switchProviderWithReadinessGateCore(options) {
|
|
20634
|
+
const env = options.env ?? process.env;
|
|
20635
|
+
const runtime = detectRuntime(env, {
|
|
20636
|
+
parentPid: options.parentPid,
|
|
20637
|
+
readParentProcessCommand: options.readParentProcessCommand
|
|
20638
|
+
});
|
|
20639
|
+
const delegate = () => switchProvider({
|
|
20640
|
+
store: options.store,
|
|
20641
|
+
env,
|
|
20642
|
+
parentPid: options.parentPid,
|
|
20643
|
+
readParentProcessCommand: options.readParentProcessCommand
|
|
20644
|
+
});
|
|
20645
|
+
if (runtime !== "codex") {
|
|
20646
|
+
const result2 = delegate();
|
|
20647
|
+
return {
|
|
20648
|
+
result: result2.ok ? result2 : { ...result2, reasonCode: "runtime_undetectable" },
|
|
20649
|
+
runtime,
|
|
20650
|
+
readiness: null
|
|
20651
|
+
};
|
|
20652
|
+
}
|
|
20653
|
+
const readiness = await ensureProviderReadyForBinding({
|
|
20654
|
+
provider: "codex",
|
|
20655
|
+
store: options.store,
|
|
20656
|
+
env,
|
|
20657
|
+
platform: options.platform,
|
|
20658
|
+
readiness: options.readiness
|
|
20659
|
+
});
|
|
20660
|
+
if (!readiness.ready) {
|
|
20661
|
+
const previousProvider = options.store.getDefaultAiProvider();
|
|
20662
|
+
const keepingClause = previousProvider ? `keeping ${formatAiProviderDisplayName(previousProvider)} as the default provider` : "the default provider was not changed";
|
|
20663
|
+
return {
|
|
20664
|
+
result: {
|
|
20665
|
+
ok: false,
|
|
20666
|
+
runtime,
|
|
20667
|
+
reason: "provider_not_ready",
|
|
20668
|
+
reasonCode: "provider_not_ready",
|
|
20669
|
+
state: "blocked",
|
|
20670
|
+
message: `Codex is not ready (${readiness.state}); the default AI provider was not changed.`,
|
|
20671
|
+
userMessage: `Codex is not ready (${readiness.state}); ${keepingClause}. ${readiness.recoveryGuidance}`,
|
|
20672
|
+
previousProvider,
|
|
20673
|
+
detectedProvider: "codex",
|
|
20674
|
+
effectiveProvider: previousProvider,
|
|
20675
|
+
readinessState: readiness.state,
|
|
20676
|
+
commandSource: readiness.commandSource,
|
|
20677
|
+
recoveryAction: readiness.recoveryAction,
|
|
20678
|
+
recoveryGuidance: readiness.recoveryGuidance
|
|
20679
|
+
},
|
|
20680
|
+
runtime,
|
|
20681
|
+
readiness
|
|
20682
|
+
};
|
|
20683
|
+
}
|
|
20684
|
+
const result = delegate();
|
|
20685
|
+
if (!result.ok) {
|
|
20686
|
+
return { result, runtime, readiness };
|
|
20687
|
+
}
|
|
20688
|
+
return {
|
|
20689
|
+
result: {
|
|
20690
|
+
...result,
|
|
20691
|
+
detectedProvider: "codex",
|
|
20692
|
+
effectiveProvider: result.provider,
|
|
20693
|
+
readinessState: readiness.state,
|
|
20694
|
+
commandSource: readiness.commandSource,
|
|
20695
|
+
commandPersisted: readiness.commandPersisted
|
|
20696
|
+
},
|
|
20697
|
+
runtime,
|
|
20698
|
+
readiness
|
|
20699
|
+
};
|
|
20700
|
+
}
|
|
20701
|
+
function classifyJobBindingSource(env = process.env, options = {}) {
|
|
20702
|
+
if (env.OKX_AGENT_TASK_AI_CLI ?? env.OKX_A2A_AI_PROVIDER) {
|
|
20703
|
+
return "explicit_env";
|
|
20704
|
+
}
|
|
20705
|
+
return detectRuntime(env, options) === "unknown" ? "default_provider" : "current_runtime";
|
|
20706
|
+
}
|
|
20707
|
+
async function bindJobProviderToCurrentDefaultIfReady(options) {
|
|
20708
|
+
const store = options.store ?? null;
|
|
20709
|
+
const jobId = normalizeOptionalJobId2(options.jobId);
|
|
20710
|
+
if (!store || !jobId) {
|
|
20711
|
+
return null;
|
|
20712
|
+
}
|
|
20713
|
+
const existing = store.getJobProviderBinding(jobId);
|
|
20714
|
+
if (existing) {
|
|
20715
|
+
return alreadyBoundGateResult(store, jobId, existing.provider, options.env);
|
|
20716
|
+
}
|
|
20717
|
+
const provider = store.getDefaultAiProvider();
|
|
20718
|
+
if (!provider) {
|
|
20719
|
+
return null;
|
|
20720
|
+
}
|
|
20721
|
+
return await bindResolvedJobProviderIfReady({
|
|
20722
|
+
store,
|
|
20723
|
+
jobId,
|
|
20724
|
+
provider,
|
|
20725
|
+
bindingSource: "default_provider",
|
|
20726
|
+
env: options.env,
|
|
20727
|
+
platform: options.platform,
|
|
20728
|
+
readiness: options.readiness
|
|
20729
|
+
});
|
|
20730
|
+
}
|
|
20731
|
+
async function bindJobProviderToCurrentRuntimeIfReady(options) {
|
|
20732
|
+
const store = options.store ?? null;
|
|
20733
|
+
const jobId = normalizeOptionalJobId2(options.jobId);
|
|
20734
|
+
if (!store || !jobId) {
|
|
20735
|
+
return {
|
|
20736
|
+
ok: false,
|
|
20737
|
+
jobId: jobId ?? "",
|
|
20738
|
+
provider: null,
|
|
20739
|
+
currentProvider: null,
|
|
20740
|
+
created: false,
|
|
20741
|
+
degraded: false,
|
|
20742
|
+
bindingSource: null,
|
|
20743
|
+
readiness: null,
|
|
20744
|
+
reason: "missing_job_id"
|
|
20745
|
+
};
|
|
20746
|
+
}
|
|
20747
|
+
const existing = store.getJobProviderBinding(jobId);
|
|
20748
|
+
if (existing) {
|
|
20749
|
+
return alreadyBoundGateResult(store, jobId, existing.provider, options.env);
|
|
20750
|
+
}
|
|
20751
|
+
const env = options.env ?? process.env;
|
|
20752
|
+
const explicit = env.OKX_AGENT_TASK_AI_CLI ?? env.OKX_A2A_AI_PROVIDER;
|
|
20753
|
+
if (explicit) {
|
|
20754
|
+
return await bindResolvedJobProviderIfReady({
|
|
20755
|
+
store,
|
|
20756
|
+
jobId,
|
|
20757
|
+
provider: normalizeAiProvider(explicit),
|
|
20758
|
+
bindingSource: "explicit_env",
|
|
20759
|
+
env,
|
|
20760
|
+
platform: options.platform,
|
|
20761
|
+
readiness: options.readiness
|
|
20762
|
+
});
|
|
20763
|
+
}
|
|
20764
|
+
const switched = await switchProviderWithReadinessGateCore({
|
|
20765
|
+
store,
|
|
20766
|
+
env,
|
|
20767
|
+
platform: options.platform,
|
|
20768
|
+
readiness: options.readiness
|
|
20769
|
+
});
|
|
20770
|
+
if (!switched.result.ok) {
|
|
20771
|
+
const notReady = switched.result.reason === "provider_not_ready";
|
|
20772
|
+
if (switched.readiness) {
|
|
20773
|
+
logProviderReadinessChecked(jobId, switched.readiness);
|
|
20774
|
+
}
|
|
20775
|
+
return {
|
|
20776
|
+
ok: false,
|
|
20777
|
+
jobId,
|
|
20778
|
+
provider: null,
|
|
20779
|
+
currentProvider: switched.result.detectedProvider ?? null,
|
|
20780
|
+
created: false,
|
|
20781
|
+
degraded: notReady,
|
|
20782
|
+
bindingSource: null,
|
|
20783
|
+
readiness: switched.readiness,
|
|
20784
|
+
reason: notReady ? "provider_not_ready" : "unknown_runtime"
|
|
20785
|
+
};
|
|
20786
|
+
}
|
|
20787
|
+
return await bindResolvedJobProviderIfReady({
|
|
20788
|
+
store,
|
|
20789
|
+
jobId,
|
|
20790
|
+
provider: switched.result.provider,
|
|
20791
|
+
bindingSource: switched.runtime === "unknown" ? "default_provider" : "current_runtime",
|
|
20792
|
+
env,
|
|
20793
|
+
platform: options.platform,
|
|
20794
|
+
readiness: options.readiness,
|
|
20795
|
+
// Reuse the gate the switch already evaluated instead of probing twice.
|
|
20796
|
+
gate: switched.readiness
|
|
20797
|
+
});
|
|
20798
|
+
}
|
|
20799
|
+
function persistDefaultProviderIfChanged(store, provider) {
|
|
20800
|
+
if (store.getDefaultAiProvider() === provider) {
|
|
20801
|
+
return false;
|
|
20802
|
+
}
|
|
20803
|
+
store.setDefaultAiProvider(provider);
|
|
20804
|
+
return true;
|
|
20805
|
+
}
|
|
20806
|
+
function resolveNewJobProviderCandidate(store, env) {
|
|
20807
|
+
const explicit = env.OKX_AGENT_TASK_AI_CLI ?? env.OKX_A2A_AI_PROVIDER;
|
|
20808
|
+
if (explicit) {
|
|
20809
|
+
return { provider: normalizeAiProvider(explicit), bindingSource: "explicit_env" };
|
|
20810
|
+
}
|
|
20811
|
+
const stored = store.getDefaultAiProvider();
|
|
20812
|
+
return stored ? { provider: stored, bindingSource: "default_provider" } : null;
|
|
20813
|
+
}
|
|
20814
|
+
async function bindNewJobProviderIfReady(options) {
|
|
20815
|
+
const store = options.store ?? null;
|
|
20816
|
+
const jobId = normalizeOptionalJobId2(options.jobId);
|
|
20817
|
+
if (!store || !jobId) {
|
|
20818
|
+
return null;
|
|
20819
|
+
}
|
|
20820
|
+
const existing = store.getJobProviderBinding(jobId);
|
|
20821
|
+
if (existing) {
|
|
20822
|
+
return alreadyBoundGateResult(store, jobId, existing.provider, options.env);
|
|
20823
|
+
}
|
|
20824
|
+
const env = options.env ?? process.env;
|
|
20825
|
+
const candidate = resolveNewJobProviderCandidate(store, env);
|
|
20826
|
+
if (!candidate) {
|
|
20827
|
+
return null;
|
|
20828
|
+
}
|
|
20829
|
+
return await bindResolvedJobProviderIfReady({
|
|
20830
|
+
store,
|
|
20831
|
+
jobId,
|
|
20832
|
+
provider: candidate.provider,
|
|
20833
|
+
bindingSource: candidate.bindingSource,
|
|
20834
|
+
env,
|
|
20835
|
+
platform: options.platform,
|
|
20836
|
+
readiness: options.readiness
|
|
20837
|
+
});
|
|
20838
|
+
}
|
|
20839
|
+
async function persistCurrentRuntimeSelectionForNewJob(options) {
|
|
20840
|
+
const store = options.store ?? null;
|
|
20841
|
+
if (!store) {
|
|
20842
|
+
return runtimeSelectionResult({ reason: "no_provider", ok: false });
|
|
20843
|
+
}
|
|
20844
|
+
const env = options.env ?? process.env;
|
|
20845
|
+
const jobId = normalizeOptionalJobId2(options.jobId);
|
|
20846
|
+
const previousProvider = store.getDefaultAiProvider();
|
|
20847
|
+
if (jobId && store.getJobProviderBinding(jobId)) {
|
|
20848
|
+
return runtimeSelectionResult({ reason: "already_bound", ok: true, previousProvider });
|
|
20849
|
+
}
|
|
20850
|
+
const explicit = env.OKX_AGENT_TASK_AI_CLI ?? env.OKX_A2A_AI_PROVIDER;
|
|
20851
|
+
const explicitProvider = explicit ? normalizeAiProvider(explicit) : null;
|
|
20852
|
+
let candidate = null;
|
|
20853
|
+
let bindingSource = null;
|
|
20854
|
+
if (explicitProvider) {
|
|
20855
|
+
candidate = explicitProvider;
|
|
20856
|
+
bindingSource = "explicit_env";
|
|
20857
|
+
} else {
|
|
20858
|
+
const runtime = detectRuntime(env, {
|
|
20859
|
+
parentPid: options.parentPid,
|
|
20860
|
+
readParentProcessCommand: options.readParentProcessCommand
|
|
20861
|
+
});
|
|
20862
|
+
if (runtime !== "unknown") {
|
|
20863
|
+
candidate = runtime;
|
|
20864
|
+
bindingSource = "current_runtime";
|
|
20865
|
+
} else if (previousProvider === "codex") {
|
|
20866
|
+
candidate = "codex";
|
|
20867
|
+
bindingSource = "default_provider";
|
|
20868
|
+
}
|
|
20869
|
+
}
|
|
20870
|
+
if (!candidate) {
|
|
20871
|
+
return runtimeSelectionResult({ reason: "runtime_undetectable", ok: true, previousProvider });
|
|
20872
|
+
}
|
|
20873
|
+
if (candidate !== "codex") {
|
|
20874
|
+
const changed2 = persistDefaultProviderIfChanged(store, candidate);
|
|
20875
|
+
return runtimeSelectionResult({
|
|
20876
|
+
reason: changed2 ? "persisted" : "unchanged",
|
|
20877
|
+
ok: true,
|
|
20878
|
+
provider: candidate,
|
|
20879
|
+
previousProvider,
|
|
20880
|
+
changed: changed2,
|
|
20881
|
+
bindingSource
|
|
20882
|
+
});
|
|
20883
|
+
}
|
|
20884
|
+
const readiness = await ensureProviderReadyForBinding({
|
|
20885
|
+
provider: "codex",
|
|
20886
|
+
store,
|
|
20887
|
+
env,
|
|
20888
|
+
platform: options.platform,
|
|
20889
|
+
readiness: options.readiness,
|
|
20890
|
+
// A validated override must be persisted here: this is exactly the case
|
|
20891
|
+
// where it has to outlive the current process.
|
|
20892
|
+
persistOverrideCommand: true
|
|
20893
|
+
});
|
|
20894
|
+
if (!readiness.ready) {
|
|
20895
|
+
return runtimeSelectionResult({
|
|
20896
|
+
reason: "provider_not_ready",
|
|
20897
|
+
ok: false,
|
|
20898
|
+
previousProvider,
|
|
20899
|
+
bindingSource,
|
|
20900
|
+
readiness,
|
|
20901
|
+
userMessage: `Codex is not ready (${readiness.state}); the default AI provider was not changed. ${readiness.recoveryGuidance}`.trim()
|
|
20902
|
+
});
|
|
20903
|
+
}
|
|
20904
|
+
const commandDurable = readiness.commandPersisted || codexCommandResolvesWithoutOverride(
|
|
20905
|
+
env,
|
|
20906
|
+
store.getAiProviderCommand("codex"),
|
|
20907
|
+
options.platform ?? process.platform
|
|
20908
|
+
);
|
|
20909
|
+
if (!commandDurable) {
|
|
20910
|
+
return runtimeSelectionResult({
|
|
20911
|
+
reason: "command_not_durable",
|
|
20912
|
+
ok: false,
|
|
20913
|
+
previousProvider,
|
|
20914
|
+
bindingSource,
|
|
20915
|
+
readiness,
|
|
20916
|
+
userMessage: "Codex is ready in this shell but only through OKX_A2A_AI_CODEX_COMMAND, which the background daemon does not inherit. The default AI provider was not changed. Run `okx-a2a setup --json` to persist a Codex command the daemon can resolve."
|
|
20917
|
+
});
|
|
20918
|
+
}
|
|
20919
|
+
const changed = persistDefaultProviderIfChanged(store, "codex");
|
|
20920
|
+
return runtimeSelectionResult({
|
|
20921
|
+
reason: changed ? "persisted" : "unchanged",
|
|
20922
|
+
ok: true,
|
|
20923
|
+
provider: "codex",
|
|
20924
|
+
previousProvider,
|
|
20925
|
+
changed,
|
|
20926
|
+
bindingSource,
|
|
20927
|
+
readiness,
|
|
20928
|
+
commandDurable: true
|
|
20929
|
+
});
|
|
20930
|
+
}
|
|
20931
|
+
async function prepareAndBindCurrentRuntimeForNewJob(options) {
|
|
20932
|
+
const store = options.store ?? null;
|
|
20933
|
+
const jobId = normalizeOptionalJobId2(options.jobId);
|
|
20934
|
+
if (!store || !jobId) {
|
|
20935
|
+
return {
|
|
20936
|
+
ok: false,
|
|
20937
|
+
jobId: jobId ?? "",
|
|
20938
|
+
provider: null,
|
|
20939
|
+
currentProvider: null,
|
|
20940
|
+
created: false,
|
|
20941
|
+
degraded: false,
|
|
20942
|
+
bindingSource: null,
|
|
20943
|
+
readiness: null,
|
|
20944
|
+
reason: "missing_job_id"
|
|
20945
|
+
};
|
|
20946
|
+
}
|
|
20947
|
+
const existing = store.getJobProviderBinding(jobId);
|
|
20948
|
+
if (existing) {
|
|
20949
|
+
return alreadyBoundGateResult(store, jobId, existing.provider, options.env);
|
|
20950
|
+
}
|
|
20951
|
+
const selection = await persistCurrentRuntimeSelectionForNewJob({
|
|
20952
|
+
store,
|
|
20953
|
+
jobId,
|
|
20954
|
+
env: options.env,
|
|
20955
|
+
platform: options.platform,
|
|
20956
|
+
readiness: options.readiness,
|
|
20957
|
+
parentPid: options.parentPid,
|
|
20958
|
+
readParentProcessCommand: options.readParentProcessCommand
|
|
20959
|
+
});
|
|
20960
|
+
if (selection.reason === "already_bound") {
|
|
20961
|
+
const raced = store.getJobProviderBinding(jobId);
|
|
20962
|
+
if (raced) {
|
|
20963
|
+
return alreadyBoundGateResult(store, jobId, raced.provider, options.env);
|
|
20964
|
+
}
|
|
20965
|
+
}
|
|
20966
|
+
if (!selection.ok) {
|
|
20967
|
+
return {
|
|
20968
|
+
ok: false,
|
|
20969
|
+
jobId,
|
|
20970
|
+
provider: null,
|
|
20971
|
+
currentProvider: selection.previousProvider,
|
|
20972
|
+
created: false,
|
|
20973
|
+
degraded: true,
|
|
20974
|
+
bindingSource: null,
|
|
20975
|
+
readiness: selection.readiness,
|
|
20976
|
+
reason: "provider_not_ready"
|
|
20977
|
+
};
|
|
20978
|
+
}
|
|
20979
|
+
if (!selection.provider) {
|
|
20980
|
+
return {
|
|
20981
|
+
ok: false,
|
|
20982
|
+
jobId,
|
|
20983
|
+
provider: null,
|
|
20984
|
+
currentProvider: selection.previousProvider,
|
|
20985
|
+
created: false,
|
|
20986
|
+
degraded: false,
|
|
20987
|
+
bindingSource: null,
|
|
20988
|
+
readiness: selection.readiness,
|
|
20989
|
+
reason: "unknown_runtime"
|
|
20990
|
+
};
|
|
20991
|
+
}
|
|
20992
|
+
return await bindResolvedJobProviderIfReady({
|
|
20993
|
+
store,
|
|
20994
|
+
jobId,
|
|
20995
|
+
provider: normalizeAiProvider(selection.provider),
|
|
20996
|
+
bindingSource: selection.bindingSource ?? "current_runtime",
|
|
20997
|
+
env: options.env,
|
|
20998
|
+
platform: options.platform,
|
|
20999
|
+
readiness: options.readiness,
|
|
21000
|
+
gate: selection.readiness
|
|
21001
|
+
});
|
|
21002
|
+
}
|
|
21003
|
+
function runtimeSelectionResult(input) {
|
|
21004
|
+
return {
|
|
21005
|
+
ok: input.ok,
|
|
21006
|
+
reason: input.reason,
|
|
21007
|
+
provider: input.provider ?? null,
|
|
21008
|
+
previousProvider: input.previousProvider ?? null,
|
|
21009
|
+
changed: input.changed ?? false,
|
|
21010
|
+
bindingSource: input.bindingSource ?? null,
|
|
21011
|
+
readiness: input.readiness ?? null,
|
|
21012
|
+
commandDurable: input.commandDurable ?? false,
|
|
21013
|
+
userMessage: input.userMessage ?? ""
|
|
21014
|
+
};
|
|
21015
|
+
}
|
|
21016
|
+
function notRequiredReadinessGate(provider) {
|
|
21017
|
+
return {
|
|
21018
|
+
provider,
|
|
21019
|
+
ready: true,
|
|
21020
|
+
state: "not_required",
|
|
21021
|
+
commandSource: null,
|
|
21022
|
+
commandPersisted: false,
|
|
21023
|
+
recoveryAction: "none",
|
|
21024
|
+
recoveryGuidance: ""
|
|
21025
|
+
};
|
|
21026
|
+
}
|
|
21027
|
+
function alreadyBoundGateResult(store, jobId, provider, env) {
|
|
21028
|
+
bindHermesJobRouteFromEnvIfAvailable(store, { jobId, provider, env });
|
|
21029
|
+
return {
|
|
21030
|
+
ok: true,
|
|
21031
|
+
jobId,
|
|
21032
|
+
provider,
|
|
21033
|
+
currentProvider: null,
|
|
21034
|
+
created: false,
|
|
21035
|
+
degraded: false,
|
|
21036
|
+
bindingSource: "existing_binding",
|
|
21037
|
+
readiness: null,
|
|
21038
|
+
reason: "already_bound"
|
|
21039
|
+
};
|
|
21040
|
+
}
|
|
21041
|
+
async function bindResolvedJobProviderIfReady(input) {
|
|
21042
|
+
const gate = input.gate ?? await ensureProviderReadyForBinding({
|
|
21043
|
+
provider: input.provider,
|
|
21044
|
+
store: input.store,
|
|
21045
|
+
env: input.env,
|
|
21046
|
+
platform: input.platform,
|
|
21047
|
+
readiness: input.readiness
|
|
21048
|
+
});
|
|
21049
|
+
logProviderReadinessChecked(input.jobId, gate);
|
|
21050
|
+
if (!gate.ready) {
|
|
21051
|
+
return {
|
|
21052
|
+
ok: false,
|
|
21053
|
+
jobId: input.jobId,
|
|
21054
|
+
provider: null,
|
|
21055
|
+
currentProvider: input.provider,
|
|
21056
|
+
created: false,
|
|
21057
|
+
degraded: true,
|
|
21058
|
+
bindingSource: null,
|
|
21059
|
+
readiness: gate,
|
|
21060
|
+
reason: "provider_not_ready"
|
|
21061
|
+
};
|
|
21062
|
+
}
|
|
21063
|
+
const result = bindJobProviderWithRouteIfAvailable(input.store, {
|
|
21064
|
+
jobId: input.jobId,
|
|
21065
|
+
provider: input.provider,
|
|
21066
|
+
env: input.env,
|
|
21067
|
+
// The gate above just returned ready for this exact provider.
|
|
21068
|
+
readinessVerified: true
|
|
21069
|
+
});
|
|
21070
|
+
const created = result.created;
|
|
21071
|
+
if (created) {
|
|
21072
|
+
logJobProviderBound(input.jobId, result.binding.provider, input.bindingSource, gate);
|
|
21073
|
+
}
|
|
21074
|
+
return {
|
|
21075
|
+
ok: true,
|
|
21076
|
+
jobId: input.jobId,
|
|
21077
|
+
provider: result.binding.provider,
|
|
21078
|
+
currentProvider: input.provider,
|
|
21079
|
+
created,
|
|
21080
|
+
degraded: false,
|
|
21081
|
+
// A concurrent writer won the INSERT: report the row as pre-existing rather
|
|
21082
|
+
// than claiming this call's candidate tier produced it.
|
|
21083
|
+
bindingSource: created ? input.bindingSource : "existing_binding",
|
|
21084
|
+
readiness: gate,
|
|
21085
|
+
reason: created ? "created" : "already_bound"
|
|
21086
|
+
};
|
|
21087
|
+
}
|
|
21088
|
+
function logProviderReadinessChecked(jobId, gate) {
|
|
21089
|
+
if (gate.state === "not_required") {
|
|
21090
|
+
return;
|
|
21091
|
+
}
|
|
21092
|
+
logger.info(LogEvent.PROVIDER_READINESS_CHECKED, {
|
|
21093
|
+
component: JOB_PROVIDER_BINDING_COMPONENT,
|
|
21094
|
+
jobId,
|
|
21095
|
+
provider: gate.provider,
|
|
21096
|
+
readinessState: gate.state,
|
|
21097
|
+
commandSource: gate.commandSource ?? "",
|
|
21098
|
+
commandPersisted: String(gate.commandPersisted),
|
|
21099
|
+
recoveryAction: gate.recoveryAction,
|
|
21100
|
+
...logFieldExtras({
|
|
21101
|
+
checkpoint: gate.ready ? "provider_ready" : "provider_not_ready",
|
|
21102
|
+
outcome: gate.ready ? "success" : "failed",
|
|
21103
|
+
transport: "local",
|
|
21104
|
+
eventFamily: "dispatch"
|
|
21105
|
+
})
|
|
21106
|
+
});
|
|
21107
|
+
}
|
|
21108
|
+
function logJobProviderBound(jobId, provider, bindingSource, gate) {
|
|
21109
|
+
logger.info(LogEvent.JOB_PROVIDER_BOUND, {
|
|
21110
|
+
component: JOB_PROVIDER_BINDING_COMPONENT,
|
|
21111
|
+
jobId,
|
|
21112
|
+
provider,
|
|
21113
|
+
bindingSource,
|
|
21114
|
+
readinessState: gate.state,
|
|
21115
|
+
commandSource: gate.commandSource ?? "",
|
|
21116
|
+
...logFieldExtras({
|
|
21117
|
+
checkpoint: "job_provider_bound",
|
|
21118
|
+
outcome: "success",
|
|
21119
|
+
transport: "local",
|
|
21120
|
+
eventFamily: "dispatch"
|
|
21121
|
+
})
|
|
21122
|
+
});
|
|
21123
|
+
}
|
|
21124
|
+
function resolveAiProviderForDispatch(options) {
|
|
21125
|
+
const env = options.env ?? process.env;
|
|
21126
|
+
const jobId = normalizeOptionalJobId2(options.jobId);
|
|
21127
|
+
return resolveDispatchProvider(
|
|
21128
|
+
options,
|
|
21129
|
+
(provider) => bindDispatchProviderIfNeeded(options.store, jobId, env, provider)
|
|
21130
|
+
);
|
|
21131
|
+
}
|
|
21132
|
+
async function resolveAiProviderForDispatchWithReadiness(options) {
|
|
21133
|
+
const env = options.env ?? process.env;
|
|
21134
|
+
const jobId = normalizeOptionalJobId2(options.jobId);
|
|
21135
|
+
if (!jobId || options.store.getJobProviderBinding(jobId)) {
|
|
21136
|
+
return resolveAiProviderForDispatch(options);
|
|
21137
|
+
}
|
|
21138
|
+
const candidate = resolveDispatchProvider(options, (provider) => provider);
|
|
21139
|
+
if (candidate !== "codex") {
|
|
21140
|
+
return resolveAiProviderForDispatch(options);
|
|
21141
|
+
}
|
|
21142
|
+
let gate = options.readiness ? await ensureProviderReadyForBinding({
|
|
21143
|
+
provider: candidate,
|
|
21144
|
+
store: options.store,
|
|
21145
|
+
env,
|
|
21146
|
+
platform: options.platform,
|
|
21147
|
+
readiness: options.readiness
|
|
21148
|
+
}) : cachedProviderReadinessGate(candidate, env);
|
|
21149
|
+
if (!gate) {
|
|
21150
|
+
gate = await ensureProviderReadyForBinding({
|
|
21151
|
+
provider: candidate,
|
|
21152
|
+
store: options.store,
|
|
21153
|
+
env,
|
|
21154
|
+
platform: options.platform
|
|
21155
|
+
});
|
|
21156
|
+
}
|
|
21157
|
+
logProviderReadinessChecked(jobId, gate);
|
|
21158
|
+
if (!gate.ready) {
|
|
21159
|
+
throw new ProviderNotReadyError(candidate, gate);
|
|
21160
|
+
}
|
|
21161
|
+
return resolveDispatchProvider(
|
|
21162
|
+
options,
|
|
21163
|
+
(provider) => bindDispatchProviderIfNeeded(options.store, jobId, env, provider, true)
|
|
21164
|
+
);
|
|
21165
|
+
}
|
|
21166
|
+
function cachedProviderReadinessGate(provider, env) {
|
|
21167
|
+
if (provider !== "codex") {
|
|
21168
|
+
return notRequiredReadinessGate(provider);
|
|
21169
|
+
}
|
|
21170
|
+
const cached = readCachedCodexReadiness({ env });
|
|
21171
|
+
return cached ? readinessGateFromResult(cached) : null;
|
|
21172
|
+
}
|
|
21173
|
+
function assertCodexBindingPermitted(store, input) {
|
|
21174
|
+
if (input.provider !== "codex" || input.readinessVerified) {
|
|
21175
|
+
return;
|
|
21176
|
+
}
|
|
21177
|
+
if (store.getJobProviderBinding?.(input.jobId)) {
|
|
21178
|
+
return;
|
|
21179
|
+
}
|
|
21180
|
+
const cached = readCachedCodexReadiness({ env: input.env ?? process.env });
|
|
21181
|
+
if (cached?.ready) {
|
|
21182
|
+
return;
|
|
21183
|
+
}
|
|
21184
|
+
throw new ProviderNotReadyError(
|
|
21185
|
+
input.provider,
|
|
21186
|
+
cached ? readinessGateFromResult(cached) : unverifiedCodexReadinessGate()
|
|
21187
|
+
);
|
|
21188
|
+
}
|
|
21189
|
+
function unverifiedCodexReadinessGate() {
|
|
21190
|
+
return {
|
|
21191
|
+
provider: "codex",
|
|
21192
|
+
ready: false,
|
|
21193
|
+
state: "probe_failed",
|
|
21194
|
+
commandSource: null,
|
|
21195
|
+
commandPersisted: false,
|
|
21196
|
+
recoveryAction: "run_setup",
|
|
21197
|
+
recoveryGuidance: "Codex readiness has not been verified for this job, so the binding was refused rather than pinned to a provider that may not be able to run. Run `okx-a2a setup --json` to verify Codex."
|
|
21198
|
+
};
|
|
21199
|
+
}
|
|
21200
|
+
function readinessGateFromResult(result) {
|
|
21201
|
+
return {
|
|
21202
|
+
provider: result.provider,
|
|
21203
|
+
ready: result.ready,
|
|
21204
|
+
state: result.state,
|
|
21205
|
+
commandSource: result.commandSource,
|
|
21206
|
+
commandPersisted: result.commandPersisted,
|
|
21207
|
+
recoveryAction: result.recoveryAction,
|
|
21208
|
+
recoveryGuidance: result.recoveryGuidance
|
|
21209
|
+
};
|
|
21210
|
+
}
|
|
21211
|
+
function resolveDispatchProvider(options, bind) {
|
|
21212
|
+
const env = options.env ?? process.env;
|
|
21213
|
+
const commandExists2 = options.commandExists ?? commandExists;
|
|
21214
|
+
const detection = withStoredCodexAvailability(
|
|
21215
|
+
detectAiProviders(commandExists2, env),
|
|
21216
|
+
options.store,
|
|
21217
|
+
commandExists2,
|
|
21218
|
+
env
|
|
21219
|
+
);
|
|
21220
|
+
const jobId = normalizeOptionalJobId2(options.jobId);
|
|
21221
|
+
if (jobId) {
|
|
21222
|
+
const binding = options.store.getJobProviderBinding(jobId);
|
|
21223
|
+
if (binding) {
|
|
21224
|
+
assertInstalled(binding.provider, detection);
|
|
21225
|
+
return binding.provider;
|
|
21226
|
+
}
|
|
21227
|
+
}
|
|
21228
|
+
if (env.OKX_AGENT_TASK_AI_CLI) {
|
|
21229
|
+
const provider = normalizeAiProvider(env.OKX_AGENT_TASK_AI_CLI);
|
|
21230
|
+
assertInstalled(provider, detection);
|
|
21231
|
+
return bind(provider);
|
|
21232
|
+
}
|
|
21233
|
+
if (env.OKX_A2A_AI_PROVIDER) {
|
|
21234
|
+
const provider = normalizeAiProvider(env.OKX_A2A_AI_PROVIDER);
|
|
21235
|
+
assertInstalled(provider, detection);
|
|
21236
|
+
return bind(provider);
|
|
21237
|
+
}
|
|
21238
|
+
if (jobId) {
|
|
21239
|
+
const stored2 = options.store.getDefaultAiProvider();
|
|
21240
|
+
if (stored2) {
|
|
21241
|
+
assertInstalled(stored2, detection);
|
|
21242
|
+
return bind(stored2);
|
|
21243
|
+
}
|
|
21244
|
+
}
|
|
21245
|
+
const current = detectCurrentAiProvider(env);
|
|
21246
|
+
if (current && isInstalled(current, detection)) {
|
|
21247
|
+
return bind(current);
|
|
21248
|
+
}
|
|
21249
|
+
if (options.sessionKey) {
|
|
21250
|
+
const existing = options.store.listAiSessions(options.sessionKey).map((session) => session.provider).filter((provider) => isInstalled(provider, detection));
|
|
21251
|
+
const unique = [...new Set(existing)];
|
|
21252
|
+
if (unique.length === 1) {
|
|
21253
|
+
return bind(unique[0]);
|
|
21254
|
+
}
|
|
21255
|
+
}
|
|
21256
|
+
const stored = options.store.getDefaultAiProvider();
|
|
21257
|
+
if (stored) {
|
|
21258
|
+
assertInstalled(stored, detection);
|
|
21259
|
+
return bind(stored);
|
|
21260
|
+
}
|
|
21261
|
+
if (detection.available.length === 1) {
|
|
21262
|
+
return bind(detection.available[0]);
|
|
21263
|
+
}
|
|
21264
|
+
if (detection.available.length > 1) {
|
|
21265
|
+
throw new Error(
|
|
21266
|
+
`Multiple AI CLIs are installed (${detection.available.join(", ")}) but no current AI environment was detected. Set OKX_A2A_AI_PROVIDER or pass --provider/--ai-provider for this run.`
|
|
21267
|
+
);
|
|
21268
|
+
}
|
|
21269
|
+
throw new Error(`No supported AI CLI found. Install one of: ${AI_PROVIDERS.join(", ")}.`);
|
|
21270
|
+
}
|
|
21271
|
+
function bindDispatchProviderIfNeeded(store, jobId, env, provider, readinessVerified = false) {
|
|
21272
|
+
if (!jobId) {
|
|
21273
|
+
return provider;
|
|
21274
|
+
}
|
|
21275
|
+
return bindJobProviderWithRouteIfAvailable(store, { jobId, provider, env, readinessVerified }).binding.provider;
|
|
21276
|
+
}
|
|
21277
|
+
function withStoredCodexAvailability(detection, store, commandExists2, env = process.env, platform = process.platform) {
|
|
21278
|
+
if (detection.codex) {
|
|
21279
|
+
return detection;
|
|
21280
|
+
}
|
|
21281
|
+
const command = store.getAiProviderCommand("codex");
|
|
21282
|
+
if (command && commandExists2(command)) {
|
|
21283
|
+
return withCodexAvailable(detection);
|
|
21284
|
+
}
|
|
21285
|
+
for (const candidate of collectCodexAppBundledCandidates(env, platform)) {
|
|
21286
|
+
if (commandExists2(candidate)) {
|
|
21287
|
+
return withCodexAvailable(detection);
|
|
21288
|
+
}
|
|
21289
|
+
}
|
|
21290
|
+
return detection;
|
|
21291
|
+
}
|
|
21292
|
+
function withCodexAvailable(detection) {
|
|
21293
|
+
return {
|
|
21294
|
+
...detection,
|
|
21295
|
+
codex: true,
|
|
21296
|
+
available: detection.available.includes("codex") ? detection.available : ["codex", ...detection.available]
|
|
21297
|
+
};
|
|
21298
|
+
}
|
|
21299
|
+
function bindJobProviderWithRouteIfAvailable(store, input) {
|
|
21300
|
+
assertCodexBindingPermitted(store, input);
|
|
21301
|
+
const result = store.bindJobProviderIfMissing({
|
|
21302
|
+
jobId: input.jobId,
|
|
21303
|
+
provider: input.provider
|
|
21304
|
+
});
|
|
21305
|
+
const routed = bindHermesJobRouteFromEnvIfAvailable(store, {
|
|
21306
|
+
jobId: input.jobId,
|
|
21307
|
+
provider: result.binding.provider,
|
|
21308
|
+
env: input.env
|
|
21309
|
+
});
|
|
21310
|
+
return routed ? { ...result, binding: routed } : result;
|
|
21311
|
+
}
|
|
21312
|
+
function bindHermesJobRouteFromEnvIfAvailable(store, input) {
|
|
21313
|
+
if (input.provider !== "hermes" || typeof store.upsertJobGatewayRoute !== "function") {
|
|
21314
|
+
return null;
|
|
21315
|
+
}
|
|
21316
|
+
const route = currentHermesGatewayRouteFromEnv(input.env ?? process.env);
|
|
21317
|
+
if (!route) {
|
|
21318
|
+
return null;
|
|
21319
|
+
}
|
|
21320
|
+
return store.upsertJobGatewayRoute({
|
|
21321
|
+
jobId: input.jobId,
|
|
21322
|
+
provider: "hermes",
|
|
21323
|
+
route
|
|
21324
|
+
});
|
|
21325
|
+
}
|
|
21326
|
+
function currentHermesGatewayRouteFromEnv(env) {
|
|
21327
|
+
const platform = normalizeOptionalText(env.OKX_A2A_CURRENT_GATEWAY_PLATFORM);
|
|
21328
|
+
const chatId = normalizeOptionalText(env.OKX_A2A_CURRENT_GATEWAY_CHAT_ID);
|
|
21329
|
+
const envGatewaySessionKey = normalizeOptionalText(env.OKX_A2A_CURRENT_GATEWAY_SESSION_KEY);
|
|
21330
|
+
const parsedGatewaySession = parseHermesGatewaySessionKey(envGatewaySessionKey);
|
|
21331
|
+
if (platform && chatId) {
|
|
21332
|
+
const threadId = normalizeOptionalText(env.OKX_A2A_CURRENT_GATEWAY_THREAD_ID) ?? matchingParsedThreadId(parsedGatewaySession, { platform, chatId });
|
|
21333
|
+
return buildHermesGatewayRoute({
|
|
21334
|
+
platform,
|
|
21335
|
+
chatId,
|
|
21336
|
+
threadId,
|
|
21337
|
+
sessionKey: envGatewaySessionKey
|
|
21338
|
+
});
|
|
21339
|
+
}
|
|
21340
|
+
const sessionRoute = routeFromHermesSessionKey(env.HERMES_SESSION_KEY);
|
|
21341
|
+
return sessionRoute ? buildHermesGatewayRoute(sessionRoute) : null;
|
|
21342
|
+
}
|
|
21343
|
+
function buildHermesGatewayRoute(input) {
|
|
21344
|
+
const threadId = normalizeOptionalText(input.threadId) ?? "";
|
|
21345
|
+
const sessionKey = normalizeOptionalText(input.sessionKey) ?? (threadId ? hermesThreadSessionKey(input.platform, input.chatId, threadId) : `agent:main:${input.platform}:dm:${encodeURIComponent(input.chatId)}`);
|
|
21346
|
+
return {
|
|
21347
|
+
platform: input.platform,
|
|
21348
|
+
chatId: input.chatId,
|
|
21349
|
+
chatName: "",
|
|
21350
|
+
chatType: threadId ? "thread" : "dm",
|
|
21351
|
+
threadId,
|
|
21352
|
+
userId: "",
|
|
21353
|
+
userName: "",
|
|
21354
|
+
sessionKey,
|
|
21355
|
+
gatewaySessionKey: sessionKey
|
|
21356
|
+
};
|
|
21357
|
+
}
|
|
21358
|
+
function hermesThreadSessionKey(platform, chatId, threadId) {
|
|
21359
|
+
const encodedChatId = encodeURIComponent(chatId);
|
|
21360
|
+
const encodedThreadId = encodeURIComponent(threadId);
|
|
21361
|
+
return platform === "telegram" ? `agent:main:${platform}:dm:${encodedChatId}:${encodedThreadId}` : `agent:main:${platform}:thread:${encodedChatId}:${encodedThreadId}`;
|
|
21362
|
+
}
|
|
21363
|
+
function routeFromHermesSessionKey(value) {
|
|
21364
|
+
const sessionKey = normalizeOptionalText(value);
|
|
21365
|
+
if (!sessionKey) {
|
|
21366
|
+
return null;
|
|
21367
|
+
}
|
|
21368
|
+
const parsed = parseHermesGatewaySessionKey(sessionKey);
|
|
21369
|
+
return parsed ? { ...parsed, sessionKey } : null;
|
|
21370
|
+
}
|
|
21371
|
+
function parseHermesGatewaySessionKey(sessionKey) {
|
|
21372
|
+
const normalized = normalizeOptionalText(sessionKey);
|
|
21373
|
+
if (!normalized) {
|
|
21374
|
+
return null;
|
|
21375
|
+
}
|
|
21376
|
+
const parts = normalized.startsWith("agent:") ? normalized.split(":").slice(2) : normalized.split(":");
|
|
21377
|
+
if (parts.length < 3) {
|
|
21378
|
+
return null;
|
|
21379
|
+
}
|
|
21380
|
+
const [platform, scope, ...rest] = parts;
|
|
21381
|
+
if (!platform || platform === "okx-a2a" || platform === "backup" || platform === "job") {
|
|
21382
|
+
return null;
|
|
21383
|
+
}
|
|
21384
|
+
if (scope === "dm" && rest[0]) {
|
|
21385
|
+
return {
|
|
21386
|
+
platform,
|
|
21387
|
+
chatId: safeDecodeURIComponent2(rest[0]),
|
|
21388
|
+
...platform === "telegram" && rest[1] ? { threadId: safeDecodeURIComponent2(rest.slice(1).join(":")) } : {}
|
|
21389
|
+
};
|
|
21390
|
+
}
|
|
21391
|
+
if (scope === "thread" && rest[0] && rest[1]) {
|
|
21392
|
+
return {
|
|
21393
|
+
platform,
|
|
21394
|
+
chatId: safeDecodeURIComponent2(rest[0]),
|
|
21395
|
+
threadId: safeDecodeURIComponent2(rest.slice(1).join(":"))
|
|
21396
|
+
};
|
|
21397
|
+
}
|
|
21398
|
+
return null;
|
|
21399
|
+
}
|
|
21400
|
+
function matchingParsedThreadId(parsed, expected) {
|
|
21401
|
+
if (!parsed?.threadId) {
|
|
21402
|
+
return null;
|
|
21403
|
+
}
|
|
21404
|
+
return parsed.platform === expected.platform && parsed.chatId === expected.chatId ? parsed.threadId : null;
|
|
21405
|
+
}
|
|
21406
|
+
function safeDecodeURIComponent2(value) {
|
|
21407
|
+
try {
|
|
21408
|
+
return decodeURIComponent(value);
|
|
21409
|
+
} catch {
|
|
21410
|
+
return value;
|
|
21411
|
+
}
|
|
21412
|
+
}
|
|
21413
|
+
function normalizeOptionalJobId2(value) {
|
|
21414
|
+
const normalized = value?.trim();
|
|
21415
|
+
return normalized ? normalized : null;
|
|
21416
|
+
}
|
|
21417
|
+
function normalizeOptionalText(value) {
|
|
21418
|
+
const normalized = value?.trim();
|
|
21419
|
+
return normalized ? normalized : null;
|
|
21420
|
+
}
|
|
21421
|
+
function isInstalled(provider, detection) {
|
|
21422
|
+
return detection.available.includes(provider);
|
|
21423
|
+
}
|
|
21424
|
+
function assertInstalled(provider, detection) {
|
|
21425
|
+
if (!isInstalled(provider, detection)) {
|
|
21426
|
+
throw new Error(`AI provider "${provider}" is not installed or not on PATH.`);
|
|
21427
|
+
}
|
|
21428
|
+
}
|
|
21429
|
+
function readProviderCommand(provider, env) {
|
|
21430
|
+
return env[`OKX_A2A_AI_${provider.toUpperCase()}_COMMAND`] ?? provider;
|
|
21431
|
+
}
|
|
21432
|
+
async function promptForAiProvider(options) {
|
|
21433
|
+
const stdin = options.stdin ?? process.stdin;
|
|
21434
|
+
const stdout = options.stdout ?? process.stdout;
|
|
21435
|
+
if (!stdin.isTTY || !stdout.isTTY) {
|
|
21436
|
+
throw new Error(
|
|
21437
|
+
`No AI provider is configured. Run \`okx-a2a daemon start --ai-provider <${AI_PROVIDERS.join("|")}>\` or set OKX_A2A_AI_PROVIDER.`
|
|
21438
|
+
);
|
|
21439
|
+
}
|
|
21440
|
+
stdout.write("AI provider is required before starting okx-a2a.\n");
|
|
21441
|
+
stdout.write("Tip: if the selected AI provider is unavailable, task processing cannot proceed.\n\n");
|
|
21442
|
+
if (options.currentProvider) {
|
|
21443
|
+
stdout.write(`Current AI provider: ${options.currentProvider}
|
|
21444
|
+
|
|
21445
|
+
`);
|
|
21446
|
+
}
|
|
21447
|
+
if (options.detection.available.length === 0) {
|
|
21448
|
+
stdout.write("Supported AI providers:\n");
|
|
21449
|
+
AI_PROVIDERS.forEach((provider, index2) => {
|
|
21450
|
+
const status = isInstalled(provider, options.detection) ? "installed" : "not found on PATH";
|
|
21451
|
+
stdout.write(` ${index2 + 1}. ${provider} (${status})
|
|
21452
|
+
`);
|
|
21453
|
+
});
|
|
21454
|
+
stdout.write("\n");
|
|
21455
|
+
throw new Error(`No supported AI CLI found. Install one of: ${AI_PROVIDERS.join(", ")}.`);
|
|
21456
|
+
}
|
|
21457
|
+
if (typeof stdin.setRawMode === "function") {
|
|
21458
|
+
return promptForAiProviderWithArrows({
|
|
21459
|
+
detection: options.detection,
|
|
21460
|
+
stdin,
|
|
21461
|
+
stdout,
|
|
21462
|
+
currentProvider: options.currentProvider
|
|
21463
|
+
});
|
|
21464
|
+
}
|
|
21465
|
+
stdout.write("Supported AI providers:\n");
|
|
21466
|
+
AI_PROVIDERS.forEach((provider, index2) => {
|
|
21467
|
+
const status = isInstalled(provider, options.detection) ? "installed" : "not found on PATH";
|
|
21468
|
+
stdout.write(` ${index2 + 1}. ${provider} (${status})
|
|
21469
|
+
`);
|
|
21470
|
+
});
|
|
21471
|
+
stdout.write("\n");
|
|
21472
|
+
const rl = (0, import_promises6.createInterface)({ input: stdin, output: stdout });
|
|
21473
|
+
try {
|
|
21474
|
+
while (true) {
|
|
21475
|
+
const answer = (await rl.question("Choose an installed AI provider by number or name: ")).trim();
|
|
21476
|
+
const provider = parseProviderChoice(answer);
|
|
21477
|
+
if (!provider) {
|
|
21478
|
+
stdout.write(`Invalid choice. Use one of: ${AI_PROVIDERS.join(", ")}.
|
|
21479
|
+
`);
|
|
21480
|
+
continue;
|
|
21481
|
+
}
|
|
21482
|
+
if (!isInstalled(provider, options.detection)) {
|
|
21483
|
+
stdout.write(`"${provider}" is not installed or not on PATH. Choose an installed provider.
|
|
21484
|
+
`);
|
|
21485
|
+
continue;
|
|
20576
21486
|
}
|
|
20577
|
-
|
|
20578
|
-
|
|
20579
|
-
|
|
20580
|
-
|
|
20581
|
-
|
|
20582
|
-
|
|
20583
|
-
|
|
20584
|
-
|
|
20585
|
-
|
|
21487
|
+
return provider;
|
|
21488
|
+
}
|
|
21489
|
+
} finally {
|
|
21490
|
+
rl.close();
|
|
21491
|
+
}
|
|
21492
|
+
}
|
|
21493
|
+
async function promptForAiProviderWithArrows(options) {
|
|
21494
|
+
let selectedIndex = AI_PROVIDERS.findIndex((provider) => provider === options.currentProvider && isInstalled(provider, options.detection));
|
|
21495
|
+
if (selectedIndex < 0) {
|
|
21496
|
+
selectedIndex = AI_PROVIDERS.findIndex((provider) => isInstalled(provider, options.detection));
|
|
21497
|
+
}
|
|
21498
|
+
if (selectedIndex < 0) {
|
|
21499
|
+
selectedIndex = 0;
|
|
21500
|
+
}
|
|
21501
|
+
let renderedLines = 0;
|
|
21502
|
+
const render = (message) => {
|
|
21503
|
+
if (renderedLines > 0) {
|
|
21504
|
+
(0, import_node_readline.moveCursor)(options.stdout, 0, -renderedLines);
|
|
21505
|
+
(0, import_node_readline.cursorTo)(options.stdout, 0);
|
|
21506
|
+
(0, import_node_readline.clearScreenDown)(options.stdout);
|
|
21507
|
+
}
|
|
21508
|
+
const lines = [
|
|
21509
|
+
"Use \u2191/\u2193 to move, Enter to confirm.",
|
|
21510
|
+
"",
|
|
21511
|
+
...AI_PROVIDERS.map((provider, index2) => {
|
|
21512
|
+
const selected = index2 === selectedIndex ? "\u276F" : " ";
|
|
21513
|
+
const status = isInstalled(provider, options.detection) ? "installed" : "not found on PATH";
|
|
21514
|
+
const current = provider === options.currentProvider ? ", current" : "";
|
|
21515
|
+
return `${selected} ${provider} (${status}${current})`;
|
|
21516
|
+
}),
|
|
21517
|
+
...message ? ["", message] : []
|
|
21518
|
+
];
|
|
21519
|
+
options.stdout.write(`${lines.join("\n")}
|
|
21520
|
+
`);
|
|
21521
|
+
renderedLines = lines.length;
|
|
21522
|
+
};
|
|
21523
|
+
(0, import_node_readline.emitKeypressEvents)(options.stdin);
|
|
21524
|
+
options.stdin.setRawMode(true);
|
|
21525
|
+
options.stdin.resume();
|
|
21526
|
+
options.stdout.write("\x1B[?25l");
|
|
21527
|
+
render();
|
|
21528
|
+
return await new Promise((resolve11, reject) => {
|
|
21529
|
+
const cleanup = () => {
|
|
21530
|
+
options.stdin.off("keypress", onKeypress);
|
|
21531
|
+
options.stdin.setRawMode(false);
|
|
21532
|
+
options.stdin.pause();
|
|
21533
|
+
options.stdout.write("\x1B[?25h");
|
|
21534
|
+
};
|
|
21535
|
+
const clearRendered = () => {
|
|
21536
|
+
if (renderedLines > 0) {
|
|
21537
|
+
(0, import_node_readline.moveCursor)(options.stdout, 0, -renderedLines);
|
|
21538
|
+
(0, import_node_readline.cursorTo)(options.stdout, 0);
|
|
21539
|
+
(0, import_node_readline.clearScreenDown)(options.stdout);
|
|
20586
21540
|
}
|
|
20587
|
-
|
|
20588
|
-
|
|
20589
|
-
|
|
21541
|
+
};
|
|
21542
|
+
const finish = (provider) => {
|
|
21543
|
+
cleanup();
|
|
21544
|
+
clearRendered();
|
|
21545
|
+
options.stdout.write(`Selected AI provider: ${provider}
|
|
21546
|
+
`);
|
|
21547
|
+
resolve11(provider);
|
|
21548
|
+
};
|
|
21549
|
+
const onKeypress = (_str, key) => {
|
|
21550
|
+
if (key.ctrl && key.name === "c") {
|
|
21551
|
+
cleanup();
|
|
21552
|
+
clearRendered();
|
|
21553
|
+
options.stdout.write(
|
|
21554
|
+
options.currentProvider ? `AI provider selection cancelled. Keeping current provider: ${options.currentProvider}
|
|
21555
|
+
` : "AI provider selection cancelled. No provider was configured.\n"
|
|
20590
21556
|
);
|
|
21557
|
+
reject(new UserCancelledAiProviderSelectionError());
|
|
21558
|
+
return;
|
|
20591
21559
|
}
|
|
20592
|
-
|
|
20593
|
-
|
|
20594
|
-
|
|
20595
|
-
|
|
20596
|
-
return true;
|
|
20597
|
-
}
|
|
20598
|
-
if (_SentryLogger.isSafeMetricKey(normalized)) {
|
|
20599
|
-
return false;
|
|
20600
|
-
}
|
|
20601
|
-
return SENTRY_EXTRA_BLOCKED_KEY_PARTS.some((needle) => normalized.includes(needle));
|
|
20602
|
-
}
|
|
20603
|
-
static isSafeMetricKey(normalized) {
|
|
20604
|
-
return /(bytes|count|length|ms|size)$/.test(normalized);
|
|
20605
|
-
}
|
|
20606
|
-
static safeValueForKey(key, value) {
|
|
20607
|
-
if (typeof value === "string" && SENTRY_FULL_STRING_EXTRA_KEYS.has(_SentryLogger.normalizeSensitiveKey(key))) {
|
|
20608
|
-
return value;
|
|
20609
|
-
}
|
|
20610
|
-
return _SentryLogger.safeValue(value);
|
|
20611
|
-
}
|
|
20612
|
-
static normalizeSensitiveKey(key) {
|
|
20613
|
-
return key.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
20614
|
-
}
|
|
20615
|
-
static safeValue(value) {
|
|
20616
|
-
if (typeof value === "string") {
|
|
20617
|
-
return value.length <= MAX_EXTRA_STRING_LENGTH ? value : `${value.slice(0, MAX_EXTRA_STRING_LENGTH)}...`;
|
|
20618
|
-
}
|
|
20619
|
-
if (value === null || value === void 0 || typeof value === "number" || typeof value === "boolean") {
|
|
20620
|
-
return value;
|
|
20621
|
-
}
|
|
20622
|
-
if (typeof value === "bigint") {
|
|
20623
|
-
return value.toString();
|
|
20624
|
-
}
|
|
20625
|
-
if (value instanceof Error) {
|
|
20626
|
-
return {
|
|
20627
|
-
name: value.name,
|
|
20628
|
-
messageLength: value.message.length
|
|
20629
|
-
};
|
|
20630
|
-
}
|
|
20631
|
-
if (Array.isArray(value)) {
|
|
20632
|
-
return value.slice(0, 20).map((item) => _SentryLogger.safeValue(item));
|
|
20633
|
-
}
|
|
20634
|
-
if (typeof value === "object") {
|
|
20635
|
-
return Object.fromEntries(
|
|
20636
|
-
Object.entries(value).slice(0, 50).filter(([, item]) => item !== void 0).filter(([key]) => !_SentryLogger.isBlockedExtraKey(key)).map(([key, item]) => [key, _SentryLogger.safeValueForKey(key, item)])
|
|
20637
|
-
);
|
|
20638
|
-
}
|
|
20639
|
-
const text = String(value);
|
|
20640
|
-
return text.length <= MAX_EXTRA_STRING_LENGTH ? text : `${text.slice(0, MAX_EXTRA_STRING_LENGTH)}...`;
|
|
20641
|
-
}
|
|
20642
|
-
static tagValue(value) {
|
|
20643
|
-
const text = String(value);
|
|
20644
|
-
return text.length <= MAX_TAG_VALUE_LENGTH ? text : `${text.slice(0, MAX_TAG_VALUE_LENGTH)}...`;
|
|
21560
|
+
if (key.name === "up") {
|
|
21561
|
+
selectedIndex = (selectedIndex + AI_PROVIDERS.length - 1) % AI_PROVIDERS.length;
|
|
21562
|
+
render();
|
|
21563
|
+
return;
|
|
20645
21564
|
}
|
|
20646
|
-
|
|
20647
|
-
|
|
21565
|
+
if (key.name === "down") {
|
|
21566
|
+
selectedIndex = (selectedIndex + 1) % AI_PROVIDERS.length;
|
|
21567
|
+
render();
|
|
21568
|
+
return;
|
|
20648
21569
|
}
|
|
20649
|
-
|
|
20650
|
-
const
|
|
20651
|
-
|
|
20652
|
-
|
|
20653
|
-
|
|
20654
|
-
}
|
|
20655
|
-
scope.setTag(_SentryLogger.normalizeTagKey(key), _SentryLogger.tagValue(value));
|
|
20656
|
-
}
|
|
20657
|
-
const fingerprint = ["a2a", eventName];
|
|
20658
|
-
for (const key of SENTRY_FINGERPRINT_KEYS) {
|
|
20659
|
-
const value = enriched[key];
|
|
20660
|
-
if (value) {
|
|
20661
|
-
fingerprint.push(`${key}:${_SentryLogger.tagValue(value)}`);
|
|
20662
|
-
}
|
|
21570
|
+
if (key.name === "return" || key.name === "enter") {
|
|
21571
|
+
const provider = AI_PROVIDERS[selectedIndex];
|
|
21572
|
+
if (!isInstalled(provider, options.detection)) {
|
|
21573
|
+
render(`"${provider}" is not installed or not on PATH. Choose an installed provider.`);
|
|
21574
|
+
return;
|
|
20663
21575
|
}
|
|
20664
|
-
|
|
20665
|
-
}
|
|
20666
|
-
static toPascalCase(str) {
|
|
20667
|
-
return str.split(/[\s:]+/).filter(Boolean).map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join("");
|
|
20668
|
-
}
|
|
20669
|
-
static createSentryEvent(eventName) {
|
|
20670
|
-
const err = new Error(eventName);
|
|
20671
|
-
err.name = _SentryLogger.toPascalCase(eventName);
|
|
20672
|
-
err.stack = `${err.name}: ${eventName}`;
|
|
20673
|
-
return err;
|
|
21576
|
+
finish(provider);
|
|
20674
21577
|
}
|
|
20675
21578
|
};
|
|
20676
|
-
|
|
20677
|
-
|
|
20678
|
-
|
|
21579
|
+
options.stdin.on("keypress", onKeypress);
|
|
21580
|
+
});
|
|
21581
|
+
}
|
|
21582
|
+
function parseProviderChoice(answer) {
|
|
21583
|
+
if (!answer) {
|
|
21584
|
+
return null;
|
|
21585
|
+
}
|
|
21586
|
+
const number = Number(answer);
|
|
21587
|
+
if (Number.isInteger(number) && number >= 1 && number <= AI_PROVIDERS.length) {
|
|
21588
|
+
return AI_PROVIDERS[number - 1];
|
|
21589
|
+
}
|
|
21590
|
+
try {
|
|
21591
|
+
return normalizeAiProvider(answer);
|
|
21592
|
+
} catch {
|
|
21593
|
+
return null;
|
|
21594
|
+
}
|
|
21595
|
+
}
|
|
21596
|
+
var import_node_child_process5, import_promises6, import_node_readline, AI_PROVIDERS, UserCancelledAiProviderSelectionError, JOB_PROVIDER_BINDING_COMPONENT, ProviderNotReadyError;
|
|
21597
|
+
var init_ai_provider = __esm({
|
|
21598
|
+
"src/ai-provider.ts"() {
|
|
21599
|
+
"use strict";
|
|
21600
|
+
import_node_child_process5 = require("node:child_process");
|
|
21601
|
+
import_promises6 = require("node:readline/promises");
|
|
21602
|
+
import_node_readline = require("node:readline");
|
|
21603
|
+
init_ai_command();
|
|
21604
|
+
init_codex_readiness();
|
|
21605
|
+
init_sentry_logger();
|
|
21606
|
+
init_log_fields();
|
|
21607
|
+
init_win_spawn();
|
|
21608
|
+
AI_PROVIDERS = ["codex", "claude", "hermes", "openclaw"];
|
|
21609
|
+
UserCancelledAiProviderSelectionError = class extends Error {
|
|
21610
|
+
constructor() {
|
|
21611
|
+
super("AI provider selection cancelled by user.");
|
|
21612
|
+
this.name = "UserCancelledAiProviderSelectionError";
|
|
21613
|
+
}
|
|
20679
21614
|
};
|
|
20680
|
-
|
|
20681
|
-
|
|
21615
|
+
JOB_PROVIDER_BINDING_COMPONENT = "node_job_provider_binding";
|
|
21616
|
+
ProviderNotReadyError = class extends Error {
|
|
21617
|
+
provider;
|
|
21618
|
+
readinessState;
|
|
21619
|
+
recoveryAction;
|
|
21620
|
+
commandSource;
|
|
21621
|
+
constructor(provider, gate) {
|
|
21622
|
+
super(`AI provider "${provider}" is not ready (${gate.state}). ${gate.recoveryGuidance}`.trim());
|
|
21623
|
+
this.name = "ProviderNotReadyError";
|
|
21624
|
+
this.provider = provider;
|
|
21625
|
+
this.readinessState = gate.state;
|
|
21626
|
+
this.recoveryAction = gate.recoveryAction;
|
|
21627
|
+
this.commandSource = gate.commandSource;
|
|
21628
|
+
}
|
|
20682
21629
|
};
|
|
20683
|
-
UNKNOWN_FIELD = "unknown";
|
|
20684
21630
|
}
|
|
20685
21631
|
});
|
|
20686
21632
|
|
|
@@ -31149,7 +32095,7 @@ var init_sentry_config = __esm({
|
|
|
31149
32095
|
environment = process.env.SENTRY_ENV === "dev" ? "dev" : "prod";
|
|
31150
32096
|
SENTRY_CONFIG = {
|
|
31151
32097
|
projectName: "okx/openclaw-okx-a2a-extension",
|
|
31152
|
-
release: "0.2.
|
|
32098
|
+
release: "0.2.3-beta-fa7df0d9e7-260810102542",
|
|
31153
32099
|
environment,
|
|
31154
32100
|
runtimeContainer: normalizeRuntimeContainer(process.env.OKX_A2A_RUNTIME_CONTAINER)
|
|
31155
32101
|
};
|
|
@@ -31362,6 +32308,7 @@ var init_win_native_launcher = __esm({
|
|
|
31362
32308
|
var update_cli_exports = {};
|
|
31363
32309
|
__export(update_cli_exports, {
|
|
31364
32310
|
assertHermesSetupSupportedOnPlatform: () => assertHermesSetupSupportedOnPlatform,
|
|
32311
|
+
authStatusFromReason: () => authStatusFromReason,
|
|
31365
32312
|
buildExternalCommandInvocation: () => buildExternalCommandInvocation,
|
|
31366
32313
|
buildNpmPackageSpec: () => buildNpmPackageSpec,
|
|
31367
32314
|
buildProviderMismatchWarning: () => buildProviderMismatchWarning,
|
|
@@ -31881,7 +32828,7 @@ function authStatusFromReason(reason) {
|
|
|
31881
32828
|
if (reason === "provider_cli_login_failed") {
|
|
31882
32829
|
return "login_failed";
|
|
31883
32830
|
}
|
|
31884
|
-
if (reason === "provider_cli_login_timeout") {
|
|
32831
|
+
if (reason === "provider_cli_login_timeout" || reason === "provider_cli_auth_status_timeout") {
|
|
31885
32832
|
return "login_timeout";
|
|
31886
32833
|
}
|
|
31887
32834
|
return "blocked";
|
|
@@ -31980,8 +32927,13 @@ function quoteUserFacingCommandPath(commandPath) {
|
|
|
31980
32927
|
}
|
|
31981
32928
|
return `"${commandPath}"`;
|
|
31982
32929
|
}
|
|
31983
|
-
async function checkClaudeCliAuthStatus(command) {
|
|
31984
|
-
const result = await runCommandCaptureStatus(command, ["auth", "status", "--json"]
|
|
32930
|
+
async function checkClaudeCliAuthStatus(command, options = {}) {
|
|
32931
|
+
const result = await runCommandCaptureStatus(command, ["auth", "status", "--json"], {
|
|
32932
|
+
timeoutMs: options.timeoutMs ?? PROVIDER_CLI_AUTH_PROBE_TIMEOUT_MS
|
|
32933
|
+
});
|
|
32934
|
+
if (result.errorCode === "ETIMEDOUT") {
|
|
32935
|
+
return { ok: false, reason: "provider_cli_auth_status_timeout", detail: result.detail };
|
|
32936
|
+
}
|
|
31985
32937
|
if (result.errorCode === "ENOENT") {
|
|
31986
32938
|
return { ok: false, reason: "provider_cli_missing", detail: result.detail };
|
|
31987
32939
|
}
|
|
@@ -32000,8 +32952,13 @@ async function checkClaudeCliAuthStatus(command) {
|
|
|
32000
32952
|
${result.stdout}`.trim() };
|
|
32001
32953
|
}
|
|
32002
32954
|
}
|
|
32003
|
-
async function checkCodexCliAuthStatus(command) {
|
|
32004
|
-
const result = await runCommandCaptureStatus(command, ["login", "status"]
|
|
32955
|
+
async function checkCodexCliAuthStatus(command, options = {}) {
|
|
32956
|
+
const result = await runCommandCaptureStatus(command, ["login", "status"], {
|
|
32957
|
+
timeoutMs: options.timeoutMs ?? PROVIDER_CLI_AUTH_PROBE_TIMEOUT_MS
|
|
32958
|
+
});
|
|
32959
|
+
if (result.errorCode === "ETIMEDOUT") {
|
|
32960
|
+
return { ok: false, reason: "provider_cli_auth_status_timeout", detail: result.detail };
|
|
32961
|
+
}
|
|
32005
32962
|
if (result.errorCode === "ENOENT") {
|
|
32006
32963
|
return { ok: false, reason: "provider_cli_missing", detail: result.detail };
|
|
32007
32964
|
}
|
|
@@ -32079,7 +33036,7 @@ async function getCurrentNodeCliVersion() {
|
|
|
32079
33036
|
return await getGlobalNpmPackageVersion(UPDATE_PACKAGES.node) ?? getBundledNodeCliVersion();
|
|
32080
33037
|
}
|
|
32081
33038
|
function getBundledNodeCliVersion() {
|
|
32082
|
-
return true ? "0.2.
|
|
33039
|
+
return true ? "0.2.3-beta-fa7df0d9e7-260810102542" : null;
|
|
32083
33040
|
}
|
|
32084
33041
|
function readConfiguredAiProvider() {
|
|
32085
33042
|
const explicit = process.env.OKX_A2A_AI_PROVIDER || process.env.OKX_AGENT_TASK_AI_CLI;
|
|
@@ -32289,7 +33246,7 @@ async function updateHermes(release, options) {
|
|
|
32289
33246
|
}
|
|
32290
33247
|
}
|
|
32291
33248
|
async function installGatewayPluginForDoctor(target) {
|
|
32292
|
-
const release = isPrereleaseVersion("0.2.
|
|
33249
|
+
const release = isPrereleaseVersion("0.2.3-beta-fa7df0d9e7-260810102542") ? "beta" : "latest";
|
|
32293
33250
|
const insideTargetGateway = detectGatewayInvocation() === target;
|
|
32294
33251
|
const options = {
|
|
32295
33252
|
restart: !insideTargetGateway,
|
|
@@ -32970,7 +33927,7 @@ async function runCommandCaptureOptional(command, args) {
|
|
|
32970
33927
|
});
|
|
32971
33928
|
});
|
|
32972
33929
|
}
|
|
32973
|
-
async function runCommandCaptureStatus(command, args) {
|
|
33930
|
+
async function runCommandCaptureStatus(command, args, options = {}) {
|
|
32974
33931
|
return await new Promise((resolvePromise) => {
|
|
32975
33932
|
const invocation = buildExternalCommandInvocation(command, args);
|
|
32976
33933
|
const child = (0, import_node_child_process8.spawn)(invocation.command, invocation.args, {
|
|
@@ -32979,6 +33936,18 @@ async function runCommandCaptureStatus(command, args) {
|
|
|
32979
33936
|
});
|
|
32980
33937
|
let stdout = "";
|
|
32981
33938
|
let stderr = "";
|
|
33939
|
+
let settled = false;
|
|
33940
|
+
let timeout = null;
|
|
33941
|
+
const settle = (result) => {
|
|
33942
|
+
if (settled) {
|
|
33943
|
+
return;
|
|
33944
|
+
}
|
|
33945
|
+
settled = true;
|
|
33946
|
+
if (timeout) {
|
|
33947
|
+
clearTimeout(timeout);
|
|
33948
|
+
}
|
|
33949
|
+
resolvePromise(result);
|
|
33950
|
+
};
|
|
32982
33951
|
child.stdout.setEncoding("utf8");
|
|
32983
33952
|
child.stderr.setEncoding("utf8");
|
|
32984
33953
|
child.stdout.on("data", (chunk) => {
|
|
@@ -32987,9 +33956,28 @@ async function runCommandCaptureStatus(command, args) {
|
|
|
32987
33956
|
child.stderr.on("data", (chunk) => {
|
|
32988
33957
|
stderr += chunk;
|
|
32989
33958
|
});
|
|
33959
|
+
if (options.timeoutMs && options.timeoutMs > 0) {
|
|
33960
|
+
const timeoutMs = options.timeoutMs;
|
|
33961
|
+
timeout = setTimeout(() => {
|
|
33962
|
+
killProcessTree(child, "SIGTERM");
|
|
33963
|
+
setTimeout(() => {
|
|
33964
|
+
if (!child.killed || child.exitCode === null) {
|
|
33965
|
+
killProcessTree(child, "SIGKILL");
|
|
33966
|
+
}
|
|
33967
|
+
}, 5e3).unref();
|
|
33968
|
+
settle({
|
|
33969
|
+
code: null,
|
|
33970
|
+
stdout,
|
|
33971
|
+
stderr,
|
|
33972
|
+
detail: `timed out after ${timeoutMs}ms`,
|
|
33973
|
+
errorCode: "ETIMEDOUT"
|
|
33974
|
+
});
|
|
33975
|
+
}, timeoutMs);
|
|
33976
|
+
timeout.unref();
|
|
33977
|
+
}
|
|
32990
33978
|
child.on("error", (error) => {
|
|
32991
33979
|
const detail = error.message;
|
|
32992
|
-
|
|
33980
|
+
settle({
|
|
32993
33981
|
code: null,
|
|
32994
33982
|
stdout,
|
|
32995
33983
|
stderr,
|
|
@@ -33000,7 +33988,7 @@ async function runCommandCaptureStatus(command, args) {
|
|
|
33000
33988
|
child.on("close", (code, signal) => {
|
|
33001
33989
|
const signalDetail = signal ? `signal=${signal}` : "";
|
|
33002
33990
|
const detail = [stderr.trim(), stdout.trim(), signalDetail].filter(Boolean).join("\n");
|
|
33003
|
-
|
|
33991
|
+
settle({
|
|
33004
33992
|
code,
|
|
33005
33993
|
stdout,
|
|
33006
33994
|
stderr,
|
|
@@ -33015,7 +34003,7 @@ function buildExternalCommandInvocation(command, args, platform = process.platfo
|
|
|
33015
34003
|
function formatExternalCommand(command, args) {
|
|
33016
34004
|
return [command, ...args].join(" ");
|
|
33017
34005
|
}
|
|
33018
|
-
var import_node_child_process8, import_node_fs22, import_promises11, import_node_os11, import_node_path28, UPDATE_PACKAGES, CODEX_NPM_PACKAGE, OPENCLAW_FORCE_INSTALL_FLAG, OPENCLAW_UNSAFE_INSTALL_FLAG, OKX_A2A_PLUGIN_ID, OPENCLAW_SESSION_DM_SCOPE_CONFIG_PATH, OPENCLAW_SESSION_DM_SCOPE_CONFIG_VALUE, OPENCLAW_PLUGIN_ALLOW_CONFIG_PATH, OPENCLAW_OKX_A2A_CONVERSATION_HOOK_ACCESS_CONFIG_PATH, DEFAULT_AI_PROVIDER_LOGIN_TIMEOUT_MS, SetupBlockedError, redirectCommandStdoutToStderr;
|
|
34006
|
+
var import_node_child_process8, import_node_fs22, import_promises11, import_node_os11, import_node_path28, UPDATE_PACKAGES, CODEX_NPM_PACKAGE, OPENCLAW_FORCE_INSTALL_FLAG, OPENCLAW_UNSAFE_INSTALL_FLAG, OKX_A2A_PLUGIN_ID, OPENCLAW_SESSION_DM_SCOPE_CONFIG_PATH, OPENCLAW_SESSION_DM_SCOPE_CONFIG_VALUE, OPENCLAW_PLUGIN_ALLOW_CONFIG_PATH, OPENCLAW_OKX_A2A_CONVERSATION_HOOK_ACCESS_CONFIG_PATH, DEFAULT_AI_PROVIDER_LOGIN_TIMEOUT_MS, PROVIDER_CLI_AUTH_PROBE_TIMEOUT_MS, SetupBlockedError, redirectCommandStdoutToStderr;
|
|
33019
34007
|
var init_update_cli = __esm({
|
|
33020
34008
|
"src/update-cli.ts"() {
|
|
33021
34009
|
"use strict";
|
|
@@ -33042,6 +34030,7 @@ var init_update_cli = __esm({
|
|
|
33042
34030
|
OPENCLAW_PLUGIN_ALLOW_CONFIG_PATH = "plugins.allow";
|
|
33043
34031
|
OPENCLAW_OKX_A2A_CONVERSATION_HOOK_ACCESS_CONFIG_PATH = "plugins.entries.okx-a2a.hooks.allowConversationAccess";
|
|
33044
34032
|
DEFAULT_AI_PROVIDER_LOGIN_TIMEOUT_MS = 3 * 60 * 1e3;
|
|
34033
|
+
PROVIDER_CLI_AUTH_PROBE_TIMEOUT_MS = CODEX_PROBE_TIMEOUT_MS;
|
|
33045
34034
|
SetupBlockedError = class extends Error {
|
|
33046
34035
|
result;
|
|
33047
34036
|
constructor(result) {
|
|
@@ -42950,6 +43939,13 @@ __export(index_exports, {
|
|
|
42950
43939
|
AgentMessageDirection: () => AgentMessageDirection,
|
|
42951
43940
|
AiRunner: () => AiRunner,
|
|
42952
43941
|
BACKUP_JOB_ID: () => BACKUP_JOB_ID,
|
|
43942
|
+
CODEX_APP_DIRS_ENV: () => CODEX_APP_DIRS_ENV,
|
|
43943
|
+
CODEX_AUTH_PROBE_TIMEOUT_MS: () => CODEX_AUTH_PROBE_TIMEOUT_MS,
|
|
43944
|
+
CODEX_COMMAND_SOURCES: () => CODEX_COMMAND_SOURCES,
|
|
43945
|
+
CODEX_PROBE_TIMEOUT_MS: () => CODEX_PROBE_TIMEOUT_MS,
|
|
43946
|
+
CODEX_READINESS_CACHE_TTL_MS: () => CODEX_READINESS_CACHE_TTL_MS,
|
|
43947
|
+
CODEX_READINESS_STATES: () => CODEX_READINESS_STATES,
|
|
43948
|
+
CODEX_RECOVERY_ACTIONS: () => CODEX_RECOVERY_ACTIONS,
|
|
42953
43949
|
CommandStore: () => CommandStore,
|
|
42954
43950
|
DEFAULT_AI_PERMISSION_PRESET: () => DEFAULT_AI_PERMISSION_PRESET,
|
|
42955
43951
|
DEFAULT_OFFLINE_REPLAY_INTERVAL_SEC: () => DEFAULT_OFFLINE_REPLAY_INTERVAL_SEC,
|
|
@@ -42959,10 +43955,13 @@ __export(index_exports, {
|
|
|
42959
43955
|
HEARTBEAT_GATEWAY_CHECK_TIMEOUT_MS: () => HEARTBEAT_GATEWAY_CHECK_TIMEOUT_MS,
|
|
42960
43956
|
InboundReplayGate: () => InboundReplayGate,
|
|
42961
43957
|
InvalidXmtpMessageStore: () => InvalidXmtpMessageStore,
|
|
43958
|
+
JOB_PROVIDER_BINDING_SOURCES: () => JOB_PROVIDER_BINDING_SOURCES,
|
|
43959
|
+
LogEvent: () => LogEvent,
|
|
42962
43960
|
MESSAGE_ELIGIBLE_OFFLINE_REPLAY_FLAG: () => MESSAGE_ELIGIBLE_OFFLINE_REPLAY_FLAG,
|
|
42963
43961
|
MESSAGE_ELIGIBLE_OFFLINE_REPLAY_HELP_TOKEN: () => MESSAGE_ELIGIBLE_OFFLINE_REPLAY_HELP_TOKEN,
|
|
42964
43962
|
NATIVE_LAUNCHER_EXE_NAME: () => NATIVE_LAUNCHER_EXE_NAME,
|
|
42965
43963
|
OPENCLAW_GATEWAY_ROUTE_GROUP_ID: () => OPENCLAW_GATEWAY_ROUTE_GROUP_ID,
|
|
43964
|
+
ProviderNotReadyError: () => ProviderNotReadyError,
|
|
42966
43965
|
SYSTEM_NOTIFICATION_SESSION_KEY: () => SYSTEM_NOTIFICATION_SESSION_KEY,
|
|
42967
43966
|
SessionBusyTracker: () => SessionBusyTracker,
|
|
42968
43967
|
SessionStore: () => SessionStore,
|
|
@@ -42983,15 +43982,20 @@ __export(index_exports, {
|
|
|
42983
43982
|
activeDaemonPointerPath: () => activeDaemonPointerPath,
|
|
42984
43983
|
aiRunSentryExtra: () => aiRunSentryExtra,
|
|
42985
43984
|
assertHermesSetupSupportedOnPlatform: () => assertHermesSetupSupportedOnPlatform,
|
|
43985
|
+
authStatusFromReason: () => authStatusFromReason,
|
|
42986
43986
|
bindJobProviderToCurrentDefaultIfMissing: () => bindJobProviderToCurrentDefaultIfMissing,
|
|
43987
|
+
bindJobProviderToCurrentDefaultIfReady: () => bindJobProviderToCurrentDefaultIfReady,
|
|
42987
43988
|
bindJobProviderToCurrentRuntimeIfMissing: () => bindJobProviderToCurrentRuntimeIfMissing,
|
|
43989
|
+
bindJobProviderToCurrentRuntimeIfReady: () => bindJobProviderToCurrentRuntimeIfReady,
|
|
42988
43990
|
bindMessageJobProviderToCurrentDefault: () => bindMessageJobProviderToCurrentDefault,
|
|
43991
|
+
bindNewJobProviderIfReady: () => bindNewJobProviderIfReady,
|
|
42989
43992
|
bindOpenClawGatewayRouteFromEnv: () => bindOpenClawGatewayRouteFromEnv,
|
|
42990
43993
|
buildAgentMessageNotice: () => buildAgentMessageNotice,
|
|
42991
43994
|
buildAiAdapterCommand: () => buildAiAdapterCommand,
|
|
42992
43995
|
buildAiProviderEnv: () => buildAiProviderEnv,
|
|
42993
43996
|
buildAutostartVbs: () => buildAutostartVbs,
|
|
42994
43997
|
buildBackupJobSessionKey: () => buildBackupJobSessionKey,
|
|
43998
|
+
buildCodexRecoveryGuidance: () => buildCodexRecoveryGuidance,
|
|
42995
43999
|
buildDispatchSessionKey: () => buildDispatchSessionKey,
|
|
42996
44000
|
buildDoctorSentryExtra: () => buildDoctorSentryExtra,
|
|
42997
44001
|
buildExternalCommandInvocation: () => buildExternalCommandInvocation,
|
|
@@ -43020,7 +44024,15 @@ __export(index_exports, {
|
|
|
43020
44024
|
callSessionsCreate: () => callSessionsCreate,
|
|
43021
44025
|
callSessionsDelete: () => callSessionsDelete,
|
|
43022
44026
|
callSessionsSend: () => callSessionsSend,
|
|
44027
|
+
checkClaudeCliAuthStatus: () => checkClaudeCliAuthStatus,
|
|
44028
|
+
checkCodexCliAuthStatus: () => checkCodexCliAuthStatus,
|
|
44029
|
+
checkCodexReadiness: () => checkCodexReadiness,
|
|
43023
44030
|
claimActiveDaemon: () => claimActiveDaemon,
|
|
44031
|
+
classifyJobBindingSource: () => classifyJobBindingSource,
|
|
44032
|
+
clearCodexReadinessCache: () => clearCodexReadinessCache,
|
|
44033
|
+
codexCommandResolvesWithoutOverride: () => codexCommandResolvesWithoutOverride,
|
|
44034
|
+
codexReadinessCacheKey: () => codexReadinessCacheKey,
|
|
44035
|
+
collectCodexAppBundledCandidates: () => collectCodexAppBundledCandidates,
|
|
43024
44036
|
collectCodexCommandCandidates: () => collectCodexCommandCandidates,
|
|
43025
44037
|
commandExists: () => commandExists,
|
|
43026
44038
|
createAiToolFailureTracker: () => createAiToolFailureTracker,
|
|
@@ -43036,6 +44048,7 @@ __export(index_exports, {
|
|
|
43036
44048
|
ensureDaemonReady: () => ensureDaemonReady,
|
|
43037
44049
|
ensureDefaultAiProvider: () => ensureDefaultAiProvider,
|
|
43038
44050
|
ensureOpenClawOkxA2aPluginConfig: () => ensureOpenClawOkxA2aPluginConfig,
|
|
44051
|
+
ensureProviderReadyForBinding: () => ensureProviderReadyForBinding,
|
|
43039
44052
|
ensureWindowsNativeLauncher: () => ensureWindowsNativeLauncher,
|
|
43040
44053
|
evictPid: () => evictPid,
|
|
43041
44054
|
exportDiagnosticLogs: () => exportDiagnosticLogs,
|
|
@@ -43063,6 +44076,7 @@ __export(index_exports, {
|
|
|
43063
44076
|
isDefaultTaskHome: () => isDefaultTaskHome,
|
|
43064
44077
|
isGatewayAvailableForHeartbeat: () => isGatewayAvailableForHeartbeat,
|
|
43065
44078
|
isHermesGatewayPluginEnabled: () => isHermesGatewayPluginEnabled,
|
|
44079
|
+
isInfoEventAllowlisted: () => isInfoEventAllowlisted,
|
|
43066
44080
|
isOfflineReplayForTrigger: () => isOfflineReplayForTrigger,
|
|
43067
44081
|
isOpenClawA2aPluginAvailable: () => isOpenClawA2aPluginAvailable,
|
|
43068
44082
|
isOpenClawGatewayAvailable: () => isOpenClawGatewayAvailable,
|
|
@@ -43088,11 +44102,15 @@ __export(index_exports, {
|
|
|
43088
44102
|
parsePluginYamlVersion: () => parsePluginYamlVersion,
|
|
43089
44103
|
parseWindowsParentProcessJson: () => parseWindowsParentProcessJson,
|
|
43090
44104
|
performRuntimeSwitch: () => performRuntimeSwitch,
|
|
44105
|
+
persistCurrentRuntimeSelectionForNewJob: () => persistCurrentRuntimeSelectionForNewJob,
|
|
43091
44106
|
pickOnchainosWin32Candidate: () => pickOnchainosWin32Candidate,
|
|
44107
|
+
prepareAndBindCurrentRuntimeForNewJob: () => prepareAndBindCurrentRuntimeForNewJob,
|
|
43092
44108
|
printLogsExportUsage: () => printLogsExportUsage,
|
|
43093
44109
|
printXmtpTestUsage: () => printXmtpTestUsage,
|
|
44110
|
+
probeCodexAuthStatus: () => probeCodexAuthStatus,
|
|
43094
44111
|
processFileMessage: () => processFileMessage,
|
|
43095
44112
|
readAiProviderTimeoutMs: () => readAiProviderTimeoutMs,
|
|
44113
|
+
readCachedCodexReadiness: () => readCachedCodexReadiness,
|
|
43096
44114
|
readLastLines: () => readLastLines,
|
|
43097
44115
|
readParentProcessCommandForPlatform: () => readParentProcessCommandForPlatform,
|
|
43098
44116
|
readUserAttentionWatcherEvent: () => readUserAttentionWatcherEvent,
|
|
@@ -43106,8 +44124,12 @@ __export(index_exports, {
|
|
|
43106
44124
|
resetResolvedOnchainosBinForTests: () => resetResolvedOnchainosBinForTests,
|
|
43107
44125
|
resolveAiPermissionPreset: () => resolveAiPermissionPreset,
|
|
43108
44126
|
resolveAiProviderCommand: () => resolveAiProviderCommand,
|
|
44127
|
+
resolveAiProviderCommandWithSource: () => resolveAiProviderCommandWithSource,
|
|
43109
44128
|
resolveAiProviderForDispatch: () => resolveAiProviderForDispatch,
|
|
44129
|
+
resolveAiProviderForDispatchWithReadiness: () => resolveAiProviderForDispatchWithReadiness,
|
|
44130
|
+
resolveCodexCommandCandidates: () => resolveCodexCommandCandidates,
|
|
43110
44131
|
resolveCodexCommandPath: () => resolveCodexCommandPath,
|
|
44132
|
+
resolveCodexCommandSource: () => resolveCodexCommandSource,
|
|
43111
44133
|
resolveConfiguredAiProvider: () => resolveConfiguredAiProvider,
|
|
43112
44134
|
resolveConfiguredAiProviderForJob: () => resolveConfiguredAiProviderForJob,
|
|
43113
44135
|
resolveDirectCommunicationSessionTarget: () => resolveDirectCommunicationSessionTarget,
|
|
@@ -43119,6 +44141,7 @@ __export(index_exports, {
|
|
|
43119
44141
|
resolveOpenClawGatewayConfig: () => resolveOpenClawGatewayConfig,
|
|
43120
44142
|
resolveOpenClawGatewayRoute: () => resolveOpenClawGatewayRoute,
|
|
43121
44143
|
resolveOpenClawGatewayRoutes: () => resolveOpenClawGatewayRoutes,
|
|
44144
|
+
resolveProviderCommandWithSelfHeal: () => resolveProviderCommandWithSelfHeal,
|
|
43122
44145
|
resolveSystemNotificationTargets: () => resolveSystemNotificationTargets,
|
|
43123
44146
|
resolveTaskConfigPath: () => resolveTaskConfigPath,
|
|
43124
44147
|
resolveTaskHome: () => resolveTaskHome,
|
|
@@ -43146,6 +44169,8 @@ __export(index_exports, {
|
|
|
43146
44169
|
subscribeUserAttentionEvents: () => subscribeUserAttentionEvents,
|
|
43147
44170
|
summarizeXmtpTestEvents: () => summarizeXmtpTestEvents,
|
|
43148
44171
|
switchProvider: () => switchProvider,
|
|
44172
|
+
switchProviderWithReadinessGate: () => switchProviderWithReadinessGate,
|
|
44173
|
+
switchProviderWithReadinessGateCore: () => switchProviderWithReadinessGateCore,
|
|
43149
44174
|
systemdUnitHasCurrentRestartPolicy: () => systemdUnitHasCurrentRestartPolicy,
|
|
43150
44175
|
toOpenClawGatewaySessionKey: () => toOpenClawGatewaySessionKey,
|
|
43151
44176
|
toWindowsInvocation: () => toWindowsInvocation,
|
|
@@ -43455,8 +44480,8 @@ init_paths();
|
|
|
43455
44480
|
init_session_store();
|
|
43456
44481
|
|
|
43457
44482
|
// src/task-config.ts
|
|
43458
|
-
var
|
|
43459
|
-
var
|
|
44483
|
+
var import_node_fs9 = require("node:fs");
|
|
44484
|
+
var import_node_path13 = require("node:path");
|
|
43460
44485
|
init_paths();
|
|
43461
44486
|
var AI_PERMISSION_PRESETS = ["bypass", "auto"];
|
|
43462
44487
|
var DEFAULT_AI_PERMISSION_PRESET = "bypass";
|
|
@@ -43472,23 +44497,23 @@ function resolveAiPermissionPreset(options = {}) {
|
|
|
43472
44497
|
}
|
|
43473
44498
|
function readAiPermissionPresetFromConfig(homeDir) {
|
|
43474
44499
|
const configPath = resolveTaskConfigPath(homeDir);
|
|
43475
|
-
if (!(0,
|
|
44500
|
+
if (!(0, import_node_fs9.existsSync)(configPath)) {
|
|
43476
44501
|
return null;
|
|
43477
44502
|
}
|
|
43478
|
-
const raw = readSimpleTomlStringValue((0,
|
|
44503
|
+
const raw = readSimpleTomlStringValue((0, import_node_fs9.readFileSync)(configPath, "utf8"), "ai.permissions", "preset");
|
|
43479
44504
|
return raw ? normalizeAiPermissionPreset(raw, configPath) : null;
|
|
43480
44505
|
}
|
|
43481
44506
|
function writeAiPermissionPresetToConfig(homeDir, preset) {
|
|
43482
44507
|
const normalized = normalizeAiPermissionPreset(preset, "permission preset");
|
|
43483
44508
|
ensureTaskDir(homeDir);
|
|
43484
44509
|
const configPath = resolveTaskConfigPath(homeDir);
|
|
43485
|
-
const current = (0,
|
|
44510
|
+
const current = (0, import_node_fs9.existsSync)(configPath) ? (0, import_node_fs9.readFileSync)(configPath, "utf8") : "";
|
|
43486
44511
|
const next = upsertSimpleTomlStringValue(current, "ai.permissions", "preset", normalized);
|
|
43487
|
-
(0,
|
|
44512
|
+
(0, import_node_fs9.writeFileSync)(configPath, next, "utf8");
|
|
43488
44513
|
return normalized;
|
|
43489
44514
|
}
|
|
43490
44515
|
function resolveTaskConfigPath(homeDir) {
|
|
43491
|
-
return (0,
|
|
44516
|
+
return (0, import_node_path13.join)(homeDir, "config.toml");
|
|
43492
44517
|
}
|
|
43493
44518
|
function normalizeAiPermissionPreset(value, source) {
|
|
43494
44519
|
const normalized = value.trim().toLowerCase();
|
|
@@ -43598,11 +44623,14 @@ function buildCodexPermissionPrefix(homeDir, cwd, env = process.env, permissionP
|
|
|
43598
44623
|
cwd
|
|
43599
44624
|
];
|
|
43600
44625
|
}
|
|
44626
|
+
function resolveAdapterProviderCommand(options, homeDir) {
|
|
44627
|
+
const storedCommand = options.provider === "codex" && options.homeDir ? readStoredAiProviderCommand(options.provider, homeDir) : null;
|
|
44628
|
+
return resolveAiProviderCommandWithSource(options.provider, options.env ?? process.env, storedCommand);
|
|
44629
|
+
}
|
|
43601
44630
|
function buildAiAdapterCommand(options) {
|
|
43602
44631
|
const env = options.env ?? process.env;
|
|
43603
44632
|
const homeDir = options.homeDir ?? resolveTaskPaths().homeDir;
|
|
43604
|
-
const
|
|
43605
|
-
const command = resolveAiProviderCommand(options.provider, env, storedCommand);
|
|
44633
|
+
const { command } = resolveAdapterProviderCommand(options, homeDir);
|
|
43606
44634
|
const template = readAdapterArgsTemplate(options.provider, !!options.sessionId, env);
|
|
43607
44635
|
if (template) {
|
|
43608
44636
|
return {
|
|
@@ -43774,6 +44802,7 @@ async function runAiAdapter(options) {
|
|
|
43774
44802
|
);
|
|
43775
44803
|
if (timedOut || exitCode !== 0 || !sessionId) {
|
|
43776
44804
|
const reason = timedOut ? "timeout" : !sessionId ? "missing_ai_session_id" : "non_zero_exit";
|
|
44805
|
+
const { source: commandSource } = resolveAdapterProviderCommand({ ...options, homeDir }, homeDir);
|
|
43777
44806
|
logger.error(
|
|
43778
44807
|
timedOut ? LogEvent.AI_RUN_TIMEOUT : LogEvent.AI_RUN_FAILED,
|
|
43779
44808
|
new Error(`${options.provider} AI adapter failed: ${reason}`),
|
|
@@ -43783,6 +44812,8 @@ async function runAiAdapter(options) {
|
|
|
43783
44812
|
provider: options.provider,
|
|
43784
44813
|
stage: "run_adapter",
|
|
43785
44814
|
reason,
|
|
44815
|
+
// Category only — never the resolved command, its path or its arguments.
|
|
44816
|
+
commandSource: commandSource ?? "",
|
|
43786
44817
|
sessionKey: options.sessionKey ?? "",
|
|
43787
44818
|
timeoutMs: timeoutMs === null ? "" : String(timeoutMs),
|
|
43788
44819
|
exitCode: exitCode === null ? "null" : String(exitCode),
|
|
@@ -44171,6 +45202,10 @@ function aiRunSentryExtra(input) {
|
|
|
44171
45202
|
stage: input.stage ?? "",
|
|
44172
45203
|
checkpoint: input.checkpoint ?? "",
|
|
44173
45204
|
outcome: input.outcome ?? "",
|
|
45205
|
+
commandSource: input.commandSource ?? "",
|
|
45206
|
+
jobBindingSource: input.jobBindingSource ?? "",
|
|
45207
|
+
recoveryAction: input.recoveryAction ?? "",
|
|
45208
|
+
readinessState: input.readinessState ?? "",
|
|
44174
45209
|
mode: input.mode ?? "",
|
|
44175
45210
|
pid: input.pid ?? "",
|
|
44176
45211
|
runAttempt: input.runAttempt ?? "",
|
|
@@ -44206,6 +45241,15 @@ function aiRunDeliveryId(input) {
|
|
|
44206
45241
|
sessionKey: input.sessionKey
|
|
44207
45242
|
});
|
|
44208
45243
|
}
|
|
45244
|
+
function resolveProviderCommandWithSelfHeal(options) {
|
|
45245
|
+
const env = options.env ?? process.env;
|
|
45246
|
+
const storedCommand = options.provider === "codex" ? options.store.getAiProviderCommand(options.provider) : null;
|
|
45247
|
+
const resolved = resolveAiProviderCommandWithSource(options.provider, env, storedCommand, options.platform);
|
|
45248
|
+
if (options.provider === "codex" && resolved.source !== "explicit_override" && resolved.command !== storedCommand && (0, import_node_path16.isAbsolute)(resolved.command)) {
|
|
45249
|
+
options.store.setAiProviderCommand(options.provider, resolved.command);
|
|
45250
|
+
}
|
|
45251
|
+
return { command: resolved.command, commandSource: resolved.source };
|
|
45252
|
+
}
|
|
44209
45253
|
var AiRunner = class {
|
|
44210
45254
|
homeDir;
|
|
44211
45255
|
logsDir;
|
|
@@ -44351,11 +45395,41 @@ var AiRunner = class {
|
|
|
44351
45395
|
storeReadMs = storeReadEndedAt - storeReadStartedAt;
|
|
44352
45396
|
}
|
|
44353
45397
|
const resolveStartedAt = Date.now();
|
|
44354
|
-
const
|
|
44355
|
-
|
|
44356
|
-
|
|
44357
|
-
|
|
44358
|
-
|
|
45398
|
+
const jobBinding = this.readJobProviderBindingForTelemetry(request.jobId);
|
|
45399
|
+
let provider;
|
|
45400
|
+
try {
|
|
45401
|
+
provider = await resolveAiProviderForDispatchWithReadiness({
|
|
45402
|
+
store: this.sessionStore,
|
|
45403
|
+
sessionKey: request.sessionKey,
|
|
45404
|
+
jobId: request.jobId
|
|
45405
|
+
});
|
|
45406
|
+
} catch (err) {
|
|
45407
|
+
const notReady = err instanceof ProviderNotReadyError ? err : null;
|
|
45408
|
+
logger.error(
|
|
45409
|
+
LogEvent.AI_RUN_FAILED,
|
|
45410
|
+
err instanceof Error ? err : new Error(String(err)),
|
|
45411
|
+
aiRunSentryExtra({
|
|
45412
|
+
source: request.source,
|
|
45413
|
+
sessionKey: request.sessionKey,
|
|
45414
|
+
jobId: request.jobId,
|
|
45415
|
+
agentId: request.agentId,
|
|
45416
|
+
messageId: request.messageId,
|
|
45417
|
+
deliveryId,
|
|
45418
|
+
provider: notReady?.provider ?? jobBinding.provider ?? "unknown",
|
|
45419
|
+
stage: "resolve_provider",
|
|
45420
|
+
reason: "provider_not_ready",
|
|
45421
|
+
checkpoint: "ai_run/provider_not_ready",
|
|
45422
|
+
outcome: "failed",
|
|
45423
|
+
runAttempt,
|
|
45424
|
+
jobBindingSource: jobBinding.bindingSource,
|
|
45425
|
+
// Categories only — never the resolved command or its path (NFR-3).
|
|
45426
|
+
readinessState: notReady?.readinessState ?? null,
|
|
45427
|
+
commandSource: notReady?.commandSource ?? null,
|
|
45428
|
+
recoveryAction: notReady?.recoveryAction ?? "run_setup"
|
|
45429
|
+
})
|
|
45430
|
+
);
|
|
45431
|
+
throw err;
|
|
45432
|
+
}
|
|
44359
45433
|
const sessionMeta = this.sessionStore.getSession(request.sessionKey);
|
|
44360
45434
|
const existingAiSessionId = this.readRunAiSessionId(provider, request, existing, sessionMeta);
|
|
44361
45435
|
const runMode = existingAiSessionId ? "resume" : "new";
|
|
@@ -44425,7 +45499,7 @@ var AiRunner = class {
|
|
|
44425
45499
|
}
|
|
44426
45500
|
}
|
|
44427
45501
|
};
|
|
44428
|
-
const command = this.resolveProviderCommand(provider);
|
|
45502
|
+
const { command, commandSource } = this.resolveProviderCommand(provider);
|
|
44429
45503
|
let stderrText = "";
|
|
44430
45504
|
let timedOut = false;
|
|
44431
45505
|
const timeoutMs = readAiProviderTimeoutMs();
|
|
@@ -44488,6 +45562,8 @@ var AiRunner = class {
|
|
|
44488
45562
|
provider,
|
|
44489
45563
|
checkpoint: "ai_run/started",
|
|
44490
45564
|
outcome: "pending",
|
|
45565
|
+
commandSource,
|
|
45566
|
+
jobBindingSource: jobBinding.bindingSource,
|
|
44491
45567
|
mode: runMode,
|
|
44492
45568
|
pid: child.pid ?? null,
|
|
44493
45569
|
runAttempt,
|
|
@@ -44601,6 +45677,11 @@ var AiRunner = class {
|
|
|
44601
45677
|
stage: "run_ai",
|
|
44602
45678
|
checkpoint: overrides?.checkpoint ?? "ai_run/failed",
|
|
44603
45679
|
outcome: overrides?.outcome ?? "failed",
|
|
45680
|
+
// Which tier produced the binary that failed, and how the job picked this
|
|
45681
|
+
// provider — the two dimensions that separate "GUI-only Codex was never
|
|
45682
|
+
// discovered" from "the CLI itself is broken".
|
|
45683
|
+
commandSource,
|
|
45684
|
+
jobBindingSource: jobBinding.bindingSource,
|
|
44604
45685
|
runAttempt,
|
|
44605
45686
|
timeoutMs,
|
|
44606
45687
|
exitCode,
|
|
@@ -44946,8 +46027,32 @@ var AiRunner = class {
|
|
|
44946
46027
|
return extractAiSessionId(provider, line);
|
|
44947
46028
|
}
|
|
44948
46029
|
resolveProviderCommand(provider) {
|
|
44949
|
-
|
|
44950
|
-
|
|
46030
|
+
return resolveProviderCommandWithSelfHeal({
|
|
46031
|
+
provider,
|
|
46032
|
+
store: this.sessionStore,
|
|
46033
|
+
env: process.env
|
|
46034
|
+
});
|
|
46035
|
+
}
|
|
46036
|
+
/**
|
|
46037
|
+
* Telemetry-only view of how this run's provider was decided. Never throws: a
|
|
46038
|
+
* store read that fails must not replace the error we are already reporting.
|
|
46039
|
+
*/
|
|
46040
|
+
readJobProviderBindingForTelemetry(jobId) {
|
|
46041
|
+
const normalizedJobId = normalizeOptionalText2(jobId);
|
|
46042
|
+
if (normalizedJobId) {
|
|
46043
|
+
try {
|
|
46044
|
+
const bound = this.sessionStore.getJobProviderBinding(normalizedJobId);
|
|
46045
|
+
if (bound) {
|
|
46046
|
+
return { provider: bound.provider, bindingSource: "existing_binding" };
|
|
46047
|
+
}
|
|
46048
|
+
} catch {
|
|
46049
|
+
}
|
|
46050
|
+
}
|
|
46051
|
+
try {
|
|
46052
|
+
return { provider: this.sessionStore.getDefaultAiProvider(), bindingSource: "default_provider" };
|
|
46053
|
+
} catch {
|
|
46054
|
+
return { provider: null, bindingSource: "default_provider" };
|
|
46055
|
+
}
|
|
44951
46056
|
}
|
|
44952
46057
|
formatRunTarget(request) {
|
|
44953
46058
|
if (request.source === "job-dispatch") {
|
|
@@ -58911,7 +60016,7 @@ function buildConnectParams(config, instanceId, protocolVersion) {
|
|
|
58911
60016
|
client: {
|
|
58912
60017
|
id: "gateway-client",
|
|
58913
60018
|
displayName: "okx-a2a-node",
|
|
58914
|
-
version: "0.2.
|
|
60019
|
+
version: "0.2.3-beta-fa7df0d9e7-260810102542",
|
|
58915
60020
|
platform: "node",
|
|
58916
60021
|
mode: "backend",
|
|
58917
60022
|
instanceId
|
|
@@ -58922,7 +60027,7 @@ function buildConnectParams(config, instanceId, protocolVersion) {
|
|
|
58922
60027
|
commands: [],
|
|
58923
60028
|
permissions: {},
|
|
58924
60029
|
locale: Intl.DateTimeFormat().resolvedOptions().locale || "en-US",
|
|
58925
|
-
userAgent: `okx-a2a-node/${"0.2.
|
|
60030
|
+
userAgent: `okx-a2a-node/${"0.2.3-beta-fa7df0d9e7-260810102542"}`,
|
|
58926
60031
|
auth: {
|
|
58927
60032
|
...config.token ? { token: config.token } : {},
|
|
58928
60033
|
...config.password ? { password: config.password } : {}
|
|
@@ -60519,15 +61624,21 @@ async function createGroupForAddress(params) {
|
|
|
60519
61624
|
{ groupName: params.groupName }
|
|
60520
61625
|
);
|
|
60521
61626
|
}
|
|
60522
|
-
function bindOutboundJobProviderToCurrentDefault(sessionStore, jobId) {
|
|
61627
|
+
async function bindOutboundJobProviderToCurrentDefault(sessionStore, jobId) {
|
|
60523
61628
|
if (!jobId || jobId === BACKUP_JOB_ID) {
|
|
60524
61629
|
return;
|
|
60525
61630
|
}
|
|
60526
61631
|
try {
|
|
60527
|
-
const result =
|
|
61632
|
+
const result = await bindNewJobProviderIfReady({
|
|
60528
61633
|
store: sessionStore,
|
|
60529
61634
|
jobId
|
|
60530
61635
|
});
|
|
61636
|
+
if (result?.degraded) {
|
|
61637
|
+
logWithTimestamp(
|
|
61638
|
+
`[okx-agent-task] job provider not ready from outbound-xmtp: job=${jobId} state=${result.readiness?.state ?? "not_required"} recovery=${result.readiness?.recoveryAction ?? "none"}; binding deferred`
|
|
61639
|
+
);
|
|
61640
|
+
return;
|
|
61641
|
+
}
|
|
60531
61642
|
if (result?.created) {
|
|
60532
61643
|
logWithTimestamp(`[okx-agent-task] job provider bound from outbound-xmtp: job=${jobId} provider=${result.provider}`);
|
|
60533
61644
|
}
|
|
@@ -61251,7 +62362,7 @@ async function handleXmtpSendCommand(params) {
|
|
|
61251
62362
|
const ownedStore2 = params.sessionStore ? null : new SessionStore();
|
|
61252
62363
|
const sessionStore2 = params.sessionStore ?? ownedStore2;
|
|
61253
62364
|
try {
|
|
61254
|
-
bindOutboundJobProviderToCurrentDefault(sessionStore2, command.jobId);
|
|
62365
|
+
await bindOutboundJobProviderToCurrentDefault(sessionStore2, command.jobId);
|
|
61255
62366
|
bindOutboundOpenClawRouteIfCurrent(sessionStore2, command.jobId, command.gatewaySessionKeys);
|
|
61256
62367
|
return await handleSqliteGroupSendCommand({
|
|
61257
62368
|
command,
|
|
@@ -61265,7 +62376,7 @@ async function handleXmtpSendCommand(params) {
|
|
|
61265
62376
|
const ownedStore = params.sessionStore ? null : new SessionStore();
|
|
61266
62377
|
const sessionStore = params.sessionStore ?? ownedStore;
|
|
61267
62378
|
try {
|
|
61268
|
-
bindOutboundJobProviderToCurrentDefault(sessionStore, command.jobId);
|
|
62379
|
+
await bindOutboundJobProviderToCurrentDefault(sessionStore, command.jobId);
|
|
61269
62380
|
bindOutboundOpenClawRouteIfCurrent(sessionStore, command.jobId, command.gatewaySessionKeys);
|
|
61270
62381
|
const { file, sessionAgentId } = await readFileForSend({
|
|
61271
62382
|
command,
|
|
@@ -63050,41 +64161,76 @@ function upsertDirectCommunicationSession(deps, target) {
|
|
|
63050
64161
|
toAgentXmtpAddress: remoteAgent?.communicationAddress ?? null
|
|
63051
64162
|
});
|
|
63052
64163
|
}
|
|
63053
|
-
function
|
|
64164
|
+
function neutralProviderGateResult(currentProvider) {
|
|
64165
|
+
return {
|
|
64166
|
+
allowed: true,
|
|
64167
|
+
created: false,
|
|
64168
|
+
provider: null,
|
|
64169
|
+
currentProvider,
|
|
64170
|
+
degraded: false,
|
|
64171
|
+
bindingSource: null,
|
|
64172
|
+
readinessState: "not_required",
|
|
64173
|
+
recoveryAction: "none"
|
|
64174
|
+
};
|
|
64175
|
+
}
|
|
64176
|
+
async function bindMessageJobProviderToCurrentDefault(deps, jobId, stage = "inbound-xmtp", options = {}) {
|
|
63054
64177
|
const currentProvider = deps.sessionStore?.getDefaultAiProvider() ?? null;
|
|
63055
64178
|
if (!jobId || jobId === BACKUP_JOB_ID) {
|
|
63056
|
-
return
|
|
64179
|
+
return neutralProviderGateResult(currentProvider);
|
|
63057
64180
|
}
|
|
63058
64181
|
try {
|
|
63059
|
-
const result =
|
|
64182
|
+
const result = await bindNewJobProviderIfReady({
|
|
63060
64183
|
store: deps.sessionStore,
|
|
63061
|
-
jobId
|
|
64184
|
+
jobId,
|
|
64185
|
+
env: options.env,
|
|
64186
|
+
platform: options.platform,
|
|
64187
|
+
readiness: options.readiness
|
|
63062
64188
|
});
|
|
63063
64189
|
if (!result) {
|
|
63064
|
-
return
|
|
64190
|
+
return neutralProviderGateResult(currentProvider);
|
|
63065
64191
|
}
|
|
63066
|
-
if (result
|
|
64192
|
+
if (result.degraded) {
|
|
64193
|
+
logWithTimestamp(
|
|
64194
|
+
`[okx-agent-task:${deps.myXmtpAddress}] job provider not ready at ${stage}: job=${shortenLogValue(jobId)} state=${result.readiness?.state ?? "not_required"} recovery=${result.readiness?.recoveryAction ?? "none"}; binding deferred, message still processed`
|
|
64195
|
+
);
|
|
64196
|
+
return {
|
|
64197
|
+
allowed: true,
|
|
64198
|
+
created: false,
|
|
64199
|
+
provider: null,
|
|
64200
|
+
currentProvider,
|
|
64201
|
+
degraded: true,
|
|
64202
|
+
bindingSource: null,
|
|
64203
|
+
readinessState: result.readiness?.state ?? "not_required",
|
|
64204
|
+
recoveryAction: result.readiness?.recoveryAction ?? "none"
|
|
64205
|
+
};
|
|
64206
|
+
}
|
|
64207
|
+
const boundProvider = result.provider ? normalizeAiProvider(result.provider) : null;
|
|
64208
|
+
if (result.created) {
|
|
63067
64209
|
logWithTimestamp(
|
|
63068
|
-
`[okx-agent-task:${deps.myXmtpAddress}] job provider bound from ${stage}: job=${shortenLogValue(jobId)} provider=${
|
|
64210
|
+
`[okx-agent-task:${deps.myXmtpAddress}] job provider bound from ${stage}: job=${shortenLogValue(jobId)} provider=${boundProvider}`
|
|
63069
64211
|
);
|
|
63070
64212
|
}
|
|
63071
|
-
if (currentProvider &&
|
|
64213
|
+
if (currentProvider && boundProvider !== currentProvider) {
|
|
63072
64214
|
logWithTimestamp(
|
|
63073
|
-
`[okx-agent-task:${deps.myXmtpAddress}] job provider owner differs from default at ${stage}: job=${shortenLogValue(jobId)} owner=${
|
|
64215
|
+
`[okx-agent-task:${deps.myXmtpAddress}] job provider owner differs from default at ${stage}: job=${shortenLogValue(jobId)} owner=${boundProvider} default=${currentProvider}; dispatch will use owner binding`
|
|
63074
64216
|
);
|
|
63075
64217
|
}
|
|
63076
64218
|
return {
|
|
63077
64219
|
allowed: true,
|
|
63078
64220
|
created: result.created,
|
|
63079
|
-
provider:
|
|
63080
|
-
currentProvider
|
|
64221
|
+
provider: boundProvider,
|
|
64222
|
+
currentProvider,
|
|
64223
|
+
degraded: false,
|
|
64224
|
+
bindingSource: result.bindingSource,
|
|
64225
|
+
readinessState: result.readiness?.state ?? "not_required",
|
|
64226
|
+
recoveryAction: result.readiness?.recoveryAction ?? "none"
|
|
63081
64227
|
};
|
|
63082
64228
|
} catch (err) {
|
|
63083
64229
|
logWithTimestamp(
|
|
63084
64230
|
`[okx-agent-task:${deps.myXmtpAddress}] job provider bind failed from ${stage}: job=${shortenLogValue(jobId)}`,
|
|
63085
64231
|
err
|
|
63086
64232
|
);
|
|
63087
|
-
return
|
|
64233
|
+
return neutralProviderGateResult(currentProvider);
|
|
63088
64234
|
}
|
|
63089
64235
|
}
|
|
63090
64236
|
function isCanonicalJobPeerSessionKey(sessionKey) {
|
|
@@ -63366,7 +64512,7 @@ async function dispatchDirectToUserNotification(params) {
|
|
|
63366
64512
|
...extra
|
|
63367
64513
|
});
|
|
63368
64514
|
const providerBindStartedAt = Date.now();
|
|
63369
|
-
const providerGate = bindMessageJobProviderToCurrentDefault(deps, notification.jobId, "direct-to-user");
|
|
64515
|
+
const providerGate = await bindMessageJobProviderToCurrentDefault(deps, notification.jobId, "direct-to-user");
|
|
63370
64516
|
timing.mark("providerBind", providerBindStartedAt);
|
|
63371
64517
|
logger.info(LogEvent.SYSTEM_NOTIFICATION_RECEIVED, commonExtras({
|
|
63372
64518
|
...inboundStageExtras("inbound/direct_to_user_received", "success"),
|
|
@@ -63880,7 +65026,7 @@ async function processFileMessage(ctx, deps, options = {}) {
|
|
|
63880
65026
|
return true;
|
|
63881
65027
|
}
|
|
63882
65028
|
const providerBindStartedAt = Date.now();
|
|
63883
|
-
const providerGate = bindMessageJobProviderToCurrentDefault(deps, systemNotification.jobId, "system-notification");
|
|
65029
|
+
const providerGate = await bindMessageJobProviderToCurrentDefault(deps, systemNotification.jobId, "system-notification");
|
|
63884
65030
|
timing.mark("providerBind", providerBindStartedAt);
|
|
63885
65031
|
if (!providerGate.allowed) {
|
|
63886
65032
|
logger.info(LogEvent.INBOUND_IGNORED_NON_BUSINESS, timing.extras({
|
|
@@ -64001,7 +65147,7 @@ async function processFileMessage(ctx, deps, options = {}) {
|
|
|
64001
65147
|
timing.mark("routeResolve", routeStartedAt);
|
|
64002
65148
|
if (route.route === "job") {
|
|
64003
65149
|
const providerBindStartedAt = Date.now();
|
|
64004
|
-
const providerGate = bindMessageJobProviderToCurrentDefault(deps, route.jobId, "inbound-xmtp");
|
|
65150
|
+
const providerGate = await bindMessageJobProviderToCurrentDefault(deps, route.jobId, "inbound-xmtp");
|
|
64005
65151
|
timing.mark("providerBind", providerBindStartedAt);
|
|
64006
65152
|
if (!providerGate.allowed) {
|
|
64007
65153
|
logger.info(LogEvent.INBOUND_IGNORED_NON_BUSINESS, timing.extras({
|
|
@@ -64920,12 +66066,12 @@ async function runListenerWithLock(options, paths) {
|
|
|
64920
66066
|
});
|
|
64921
66067
|
}
|
|
64922
66068
|
});
|
|
64923
|
-
service.setPluginVersion("0.2.
|
|
66069
|
+
service.setPluginVersion("0.2.3-beta-fa7df0d9e7-260810102542");
|
|
64924
66070
|
await service.init();
|
|
64925
66071
|
const pluginVersionStatus = service.pluginVersionStatus;
|
|
64926
66072
|
if (pluginVersionStatus.unavailable) {
|
|
64927
66073
|
throw new Error(
|
|
64928
|
-
`@okxweb3/a2a-node v${"0.2.
|
|
66074
|
+
`@okxweb3/a2a-node v${"0.2.3-beta-fa7df0d9e7-260810102542"} is below the required minimum v${pluginVersionStatus.minVersion}`
|
|
64929
66075
|
);
|
|
64930
66076
|
}
|
|
64931
66077
|
const systemConfig = service.getSystemConfig();
|
|
@@ -64943,7 +66089,7 @@ async function runListenerWithLock(options, paths) {
|
|
|
64943
66089
|
onchainosAgentId: "*",
|
|
64944
66090
|
reason: "system-config missing sentryDsn",
|
|
64945
66091
|
pluginId: "@okxweb3/a2a-node",
|
|
64946
|
-
pluginVersion: "0.2.
|
|
66092
|
+
pluginVersion: "0.2.3-beta-fa7df0d9e7-260810102542"
|
|
64947
66093
|
});
|
|
64948
66094
|
}
|
|
64949
66095
|
logWithTimestamp(
|
|
@@ -66210,6 +67356,32 @@ function validTimestamp(value) {
|
|
|
66210
67356
|
init_session_store();
|
|
66211
67357
|
init_ai_provider();
|
|
66212
67358
|
init_ai_command();
|
|
67359
|
+
init_codex_readiness();
|
|
67360
|
+
|
|
67361
|
+
// src/codex-readiness-types.ts
|
|
67362
|
+
var CODEX_COMMAND_SOURCES = [
|
|
67363
|
+
"explicit_override",
|
|
67364
|
+
"persisted",
|
|
67365
|
+
"path",
|
|
67366
|
+
"app_bundled"
|
|
67367
|
+
];
|
|
67368
|
+
var CODEX_READINESS_STATES = [
|
|
67369
|
+
"ready",
|
|
67370
|
+
"not_installed",
|
|
67371
|
+
"not_authenticated",
|
|
67372
|
+
"probe_timeout",
|
|
67373
|
+
"probe_failed"
|
|
67374
|
+
];
|
|
67375
|
+
var CODEX_RECOVERY_ACTIONS = ["none", "run_setup", "run_login", "install_cli"];
|
|
67376
|
+
var JOB_PROVIDER_BINDING_SOURCES = [
|
|
67377
|
+
"existing_binding",
|
|
67378
|
+
"current_runtime",
|
|
67379
|
+
"default_provider",
|
|
67380
|
+
"explicit_env"
|
|
67381
|
+
];
|
|
67382
|
+
|
|
67383
|
+
// src/index.ts
|
|
67384
|
+
init_sentry_logger();
|
|
66213
67385
|
init_update_cli();
|
|
66214
67386
|
init_win_spawn();
|
|
66215
67387
|
|
|
@@ -67563,7 +68735,7 @@ async function exportDiagnosticLogs(options) {
|
|
|
67563
68735
|
node: process.version,
|
|
67564
68736
|
platform: process.platform,
|
|
67565
68737
|
arch: process.arch,
|
|
67566
|
-
packageVersion: true ? "0.2.
|
|
68738
|
+
packageVersion: true ? "0.2.3-beta-fa7df0d9e7-260810102542" : "unknown",
|
|
67567
68739
|
sensitiveContentIncluded: options.includeSensitiveContent,
|
|
67568
68740
|
listenerAndLlmContentIncluded: true,
|
|
67569
68741
|
credentialsAlwaysRedacted: true,
|
|
@@ -68432,6 +69604,7 @@ init_log();
|
|
|
68432
69604
|
init_win_compat();
|
|
68433
69605
|
init_ai_command();
|
|
68434
69606
|
init_ai_provider();
|
|
69607
|
+
init_codex_readiness();
|
|
68435
69608
|
init_autostart();
|
|
68436
69609
|
init_autostart_windows();
|
|
68437
69610
|
init_daemon();
|
|
@@ -68465,11 +69638,70 @@ async function refreshAgentsAndWait(timeoutMs = 6e4) {
|
|
|
68465
69638
|
|
|
68466
69639
|
// src/runtime-switch.ts
|
|
68467
69640
|
init_ai_provider();
|
|
68468
|
-
|
|
68469
|
-
|
|
69641
|
+
init_sentry_logger();
|
|
69642
|
+
init_log_fields();
|
|
69643
|
+
var RUNTIME_SWITCH_COMPONENT = "node_runtime_switch";
|
|
69644
|
+
async function performRuntimeSwitch(store, options = {}) {
|
|
69645
|
+
const result = await switchProviderWithReadinessGate({
|
|
69646
|
+
store,
|
|
69647
|
+
env: options.env ?? process.env,
|
|
69648
|
+
platform: options.platform,
|
|
69649
|
+
readiness: options.readiness
|
|
69650
|
+
});
|
|
68470
69651
|
await dispatchRuntimeSwitchUserMessage(store, result);
|
|
68471
69652
|
return result;
|
|
68472
69653
|
}
|
|
69654
|
+
async function switchProviderWithReadinessGate(options) {
|
|
69655
|
+
const gated = await switchProviderWithReadinessGateCore(options);
|
|
69656
|
+
emitRuntimeSwitchCheckpoints(gated);
|
|
69657
|
+
return gated.result;
|
|
69658
|
+
}
|
|
69659
|
+
function emitRuntimeSwitchCheckpoints(gated) {
|
|
69660
|
+
const { result, runtime, readiness } = gated;
|
|
69661
|
+
logger.info(LogEvent.RUNTIME_DETECTED, {
|
|
69662
|
+
component: RUNTIME_SWITCH_COMPONENT,
|
|
69663
|
+
provider: runtime,
|
|
69664
|
+
detectedProvider: runtime,
|
|
69665
|
+
...logFieldExtras({
|
|
69666
|
+
checkpoint: "runtime_detected",
|
|
69667
|
+
outcome: runtime === "unknown" ? "ignored" : "success",
|
|
69668
|
+
transport: "cli",
|
|
69669
|
+
eventFamily: "lifecycle"
|
|
69670
|
+
})
|
|
69671
|
+
});
|
|
69672
|
+
if (readiness) {
|
|
69673
|
+
logger.info(LogEvent.PROVIDER_READINESS_CHECKED, {
|
|
69674
|
+
component: RUNTIME_SWITCH_COMPONENT,
|
|
69675
|
+
provider: readiness.provider,
|
|
69676
|
+
readinessState: readiness.state,
|
|
69677
|
+
commandSource: readiness.commandSource ?? "",
|
|
69678
|
+
commandPersisted: String(readiness.commandPersisted),
|
|
69679
|
+
recoveryAction: readiness.recoveryAction,
|
|
69680
|
+
...logFieldExtras({
|
|
69681
|
+
checkpoint: readiness.ready ? "provider_ready" : "provider_not_ready",
|
|
69682
|
+
outcome: readiness.ready ? "success" : "failed",
|
|
69683
|
+
transport: "cli",
|
|
69684
|
+
eventFamily: "lifecycle"
|
|
69685
|
+
})
|
|
69686
|
+
});
|
|
69687
|
+
}
|
|
69688
|
+
if (result.ok && result.action === "switched") {
|
|
69689
|
+
logger.info(LogEvent.PROVIDER_SWITCHED, {
|
|
69690
|
+
component: RUNTIME_SWITCH_COMPONENT,
|
|
69691
|
+
provider: result.provider,
|
|
69692
|
+
previousProvider: result.previousProvider ?? "",
|
|
69693
|
+
changed: String(result.changed),
|
|
69694
|
+
readinessState: result.readinessState ?? "not_required",
|
|
69695
|
+
commandSource: result.commandSource ?? "",
|
|
69696
|
+
...logFieldExtras({
|
|
69697
|
+
checkpoint: "provider_switched",
|
|
69698
|
+
outcome: "success",
|
|
69699
|
+
transport: "cli",
|
|
69700
|
+
eventFamily: "lifecycle"
|
|
69701
|
+
})
|
|
69702
|
+
});
|
|
69703
|
+
}
|
|
69704
|
+
}
|
|
68473
69705
|
async function dispatchRuntimeSwitchUserMessage(store, result) {
|
|
68474
69706
|
if (!result.ok || !result.changed || !result.userMessage) {
|
|
68475
69707
|
return;
|
|
@@ -68803,20 +70035,34 @@ var providerBindingChecker = {
|
|
|
68803
70035
|
}
|
|
68804
70036
|
}
|
|
68805
70037
|
};
|
|
70038
|
+
function resolveProviderCliTarget(env, store) {
|
|
70039
|
+
const runtime = detectRuntime(env);
|
|
70040
|
+
if (runtime === "codex" || runtime === "claude") {
|
|
70041
|
+
return runtime;
|
|
70042
|
+
}
|
|
70043
|
+
if (runtime !== "unknown") {
|
|
70044
|
+
return null;
|
|
70045
|
+
}
|
|
70046
|
+
const storedDefault = store.getDefaultAiProvider();
|
|
70047
|
+
return storedDefault === "codex" || storedDefault === "claude" ? storedDefault : null;
|
|
70048
|
+
}
|
|
70049
|
+
function readProviderCliTarget(env) {
|
|
70050
|
+
const store = new SessionStore();
|
|
70051
|
+
try {
|
|
70052
|
+
const provider = resolveProviderCliTarget(env, store);
|
|
70053
|
+
return {
|
|
70054
|
+
provider,
|
|
70055
|
+
storedCommand: provider ? store.getAiProviderCommand(provider) : null
|
|
70056
|
+
};
|
|
70057
|
+
} finally {
|
|
70058
|
+
store.close();
|
|
70059
|
+
}
|
|
70060
|
+
}
|
|
68806
70061
|
var providerCliChecker = {
|
|
68807
70062
|
id: "provider_cli",
|
|
68808
70063
|
title: "AI provider CLI",
|
|
68809
70064
|
run: async (ctx) => {
|
|
68810
|
-
const
|
|
68811
|
-
let provider;
|
|
68812
|
-
let storedCommand;
|
|
68813
|
-
try {
|
|
68814
|
-
provider = store.getDefaultAiProvider();
|
|
68815
|
-
provider = provider === "codex" || provider === "claude" ? provider : null;
|
|
68816
|
-
storedCommand = provider ? store.getAiProviderCommand(provider) : null;
|
|
68817
|
-
} finally {
|
|
68818
|
-
store.close();
|
|
68819
|
-
}
|
|
70065
|
+
const { provider, storedCommand } = readProviderCliTarget(ctx.env);
|
|
68820
70066
|
if (!provider) {
|
|
68821
70067
|
return null;
|
|
68822
70068
|
}
|
|
@@ -68839,12 +70085,29 @@ var providerCliChecker = {
|
|
|
68839
70085
|
}
|
|
68840
70086
|
const auth = provider === "codex" ? await checkCodexCliAuthStatus(resolved) : await checkClaudeCliAuthStatus(resolved);
|
|
68841
70087
|
if (auth.ok) {
|
|
70088
|
+
if (provider === "codex" && ctx.env.OKX_A2A_AI_CODEX_COMMAND && storedCommand !== resolved) {
|
|
70089
|
+
return {
|
|
70090
|
+
id: "provider_cli",
|
|
70091
|
+
title: "AI provider CLI",
|
|
70092
|
+
status: "fail",
|
|
70093
|
+
severity: "required",
|
|
70094
|
+
detail: "codex CLI is authenticated but its command is not persisted for the background daemon",
|
|
70095
|
+
fix: {
|
|
70096
|
+
kind: "auto",
|
|
70097
|
+
description: "Persist the validated Codex command for background daemon use."
|
|
70098
|
+
},
|
|
70099
|
+
data: { provider }
|
|
70100
|
+
};
|
|
70101
|
+
}
|
|
68842
70102
|
return {
|
|
68843
70103
|
id: "provider_cli",
|
|
68844
70104
|
title: "AI provider CLI",
|
|
68845
70105
|
status: "pass",
|
|
68846
70106
|
severity: "required",
|
|
68847
|
-
detail
|
|
70107
|
+
// `detail` is forwarded to Sentry (doctor-sentry.ts), so it carries no
|
|
70108
|
+
// resolved path or arguments. The exact command stays in `data`, which is
|
|
70109
|
+
// local report output only.
|
|
70110
|
+
detail: `${provider} CLI is installed and logged in`,
|
|
68848
70111
|
data: { provider, command: resolved }
|
|
68849
70112
|
};
|
|
68850
70113
|
}
|
|
@@ -68855,7 +70118,8 @@ var providerCliChecker = {
|
|
|
68855
70118
|
title: "AI provider CLI",
|
|
68856
70119
|
status: "fail",
|
|
68857
70120
|
severity: "required",
|
|
68858
|
-
|
|
70121
|
+
// No resolved path in `detail`: it is forwarded to Sentry.
|
|
70122
|
+
detail: `${provider} CLI is installed but not logged in (${auth.reason})`,
|
|
68859
70123
|
// Logging in needs a human (device/browser auth). In --non-interactive
|
|
68860
70124
|
// mode never launch it — degrade to a manual instruction so unattended
|
|
68861
70125
|
// callers (install scripts) cannot hang waiting on stdin.
|
|
@@ -68872,18 +70136,9 @@ var providerCliChecker = {
|
|
|
68872
70136
|
};
|
|
68873
70137
|
},
|
|
68874
70138
|
applyFix: async (ctx) => {
|
|
68875
|
-
const
|
|
68876
|
-
let provider;
|
|
68877
|
-
let storedCommand;
|
|
68878
|
-
try {
|
|
68879
|
-
provider = store.getDefaultAiProvider();
|
|
68880
|
-
provider = provider === "codex" || provider === "claude" ? provider : null;
|
|
68881
|
-
storedCommand = provider ? store.getAiProviderCommand(provider) : null;
|
|
68882
|
-
} finally {
|
|
68883
|
-
store.close();
|
|
68884
|
-
}
|
|
70139
|
+
const { provider, storedCommand } = readProviderCliTarget(ctx.env);
|
|
68885
70140
|
if (!provider) {
|
|
68886
|
-
throw new Error("no codex/claude provider bound");
|
|
70141
|
+
throw new Error("no codex/claude runtime detected and no codex/claude provider bound");
|
|
68887
70142
|
}
|
|
68888
70143
|
const resolveCli = () => provider === "codex" ? resolveCodexCommandPath(ctx.env, storedCommand, ctx.platform) : resolveCommandPath(resolveAiProviderCommand(provider, ctx.env, storedCommand, ctx.platform), ctx.env, ctx.platform);
|
|
68889
70144
|
let resolved = resolveCli();
|
|
@@ -68918,6 +70173,28 @@ var providerCliChecker = {
|
|
|
68918
70173
|
}
|
|
68919
70174
|
steps.push("completed interactive login");
|
|
68920
70175
|
}
|
|
70176
|
+
if (provider === "codex") {
|
|
70177
|
+
const store = new SessionStore();
|
|
70178
|
+
try {
|
|
70179
|
+
const readiness = await checkCodexReadiness({
|
|
70180
|
+
store,
|
|
70181
|
+
env: ctx.env,
|
|
70182
|
+
platform: ctx.platform,
|
|
70183
|
+
forceRefresh: true,
|
|
70184
|
+
persistOverrideCommand: true
|
|
70185
|
+
});
|
|
70186
|
+
if (!readiness.ready) {
|
|
70187
|
+
throw new Error(
|
|
70188
|
+
`codex readiness could not be confirmed (${readiness.state}); ${readiness.recoveryGuidance}`.trim()
|
|
70189
|
+
);
|
|
70190
|
+
}
|
|
70191
|
+
if (readiness.commandPersisted) {
|
|
70192
|
+
steps.push("persisted the validated codex command");
|
|
70193
|
+
}
|
|
70194
|
+
} finally {
|
|
70195
|
+
store.close();
|
|
70196
|
+
}
|
|
70197
|
+
}
|
|
68921
70198
|
return steps.length > 0 ? steps.join("; ") : "CLI already installed and logged in";
|
|
68922
70199
|
}
|
|
68923
70200
|
};
|
|
@@ -69274,8 +70551,14 @@ var CHECKERS = [
|
|
|
69274
70551
|
npmChecker,
|
|
69275
70552
|
cliVersionChecker,
|
|
69276
70553
|
windowsNativeLauncherChecker,
|
|
69277
|
-
|
|
70554
|
+
// provider_cli BEFORE provider_binding, deliberately: the intended runtime has
|
|
70555
|
+
// to be installed, authenticated and durably persisted before the binding fixer
|
|
70556
|
+
// attempts its readiness-gated runtime switch. In the other order the switch
|
|
70557
|
+
// fails on a not-ready Codex, preserves the stale default, and the CLI checker
|
|
70558
|
+
// then inspects that stale provider instead of the detected one — so Codex is
|
|
70559
|
+
// never repaired and the run never converges.
|
|
69278
70560
|
providerCliChecker,
|
|
70561
|
+
providerBindingChecker,
|
|
69279
70562
|
gatewayPluginChecker,
|
|
69280
70563
|
gatewayConfigChecker,
|
|
69281
70564
|
autostartChecker,
|
|
@@ -69288,7 +70571,7 @@ async function runDoctor(options = {}) {
|
|
|
69288
70571
|
platform: options.platform ?? process.platform,
|
|
69289
70572
|
env: options.env ?? process.env,
|
|
69290
70573
|
target: options.target ?? resolveDoctorTarget(options.env ?? process.env),
|
|
69291
|
-
cliVersion: options.cliVersion ?? (true ? "0.2.
|
|
70574
|
+
cliVersion: options.cliVersion ?? (true ? "0.2.3-beta-fa7df0d9e7-260810102542" : "0.0.0"),
|
|
69292
70575
|
fixMode: options.fix === true,
|
|
69293
70576
|
nonInteractive: options.nonInteractive === true,
|
|
69294
70577
|
packageChanged: false,
|
|
@@ -69513,6 +70796,13 @@ init_autostart_windows();
|
|
|
69513
70796
|
AgentMessageDirection,
|
|
69514
70797
|
AiRunner,
|
|
69515
70798
|
BACKUP_JOB_ID,
|
|
70799
|
+
CODEX_APP_DIRS_ENV,
|
|
70800
|
+
CODEX_AUTH_PROBE_TIMEOUT_MS,
|
|
70801
|
+
CODEX_COMMAND_SOURCES,
|
|
70802
|
+
CODEX_PROBE_TIMEOUT_MS,
|
|
70803
|
+
CODEX_READINESS_CACHE_TTL_MS,
|
|
70804
|
+
CODEX_READINESS_STATES,
|
|
70805
|
+
CODEX_RECOVERY_ACTIONS,
|
|
69516
70806
|
CommandStore,
|
|
69517
70807
|
DEFAULT_AI_PERMISSION_PRESET,
|
|
69518
70808
|
DEFAULT_OFFLINE_REPLAY_INTERVAL_SEC,
|
|
@@ -69522,10 +70812,13 @@ init_autostart_windows();
|
|
|
69522
70812
|
HEARTBEAT_GATEWAY_CHECK_TIMEOUT_MS,
|
|
69523
70813
|
InboundReplayGate,
|
|
69524
70814
|
InvalidXmtpMessageStore,
|
|
70815
|
+
JOB_PROVIDER_BINDING_SOURCES,
|
|
70816
|
+
LogEvent,
|
|
69525
70817
|
MESSAGE_ELIGIBLE_OFFLINE_REPLAY_FLAG,
|
|
69526
70818
|
MESSAGE_ELIGIBLE_OFFLINE_REPLAY_HELP_TOKEN,
|
|
69527
70819
|
NATIVE_LAUNCHER_EXE_NAME,
|
|
69528
70820
|
OPENCLAW_GATEWAY_ROUTE_GROUP_ID,
|
|
70821
|
+
ProviderNotReadyError,
|
|
69529
70822
|
SYSTEM_NOTIFICATION_SESSION_KEY,
|
|
69530
70823
|
SessionBusyTracker,
|
|
69531
70824
|
SessionStore,
|
|
@@ -69546,15 +70839,20 @@ init_autostart_windows();
|
|
|
69546
70839
|
activeDaemonPointerPath,
|
|
69547
70840
|
aiRunSentryExtra,
|
|
69548
70841
|
assertHermesSetupSupportedOnPlatform,
|
|
70842
|
+
authStatusFromReason,
|
|
69549
70843
|
bindJobProviderToCurrentDefaultIfMissing,
|
|
70844
|
+
bindJobProviderToCurrentDefaultIfReady,
|
|
69550
70845
|
bindJobProviderToCurrentRuntimeIfMissing,
|
|
70846
|
+
bindJobProviderToCurrentRuntimeIfReady,
|
|
69551
70847
|
bindMessageJobProviderToCurrentDefault,
|
|
70848
|
+
bindNewJobProviderIfReady,
|
|
69552
70849
|
bindOpenClawGatewayRouteFromEnv,
|
|
69553
70850
|
buildAgentMessageNotice,
|
|
69554
70851
|
buildAiAdapterCommand,
|
|
69555
70852
|
buildAiProviderEnv,
|
|
69556
70853
|
buildAutostartVbs,
|
|
69557
70854
|
buildBackupJobSessionKey,
|
|
70855
|
+
buildCodexRecoveryGuidance,
|
|
69558
70856
|
buildDispatchSessionKey,
|
|
69559
70857
|
buildDoctorSentryExtra,
|
|
69560
70858
|
buildExternalCommandInvocation,
|
|
@@ -69583,7 +70881,15 @@ init_autostart_windows();
|
|
|
69583
70881
|
callSessionsCreate,
|
|
69584
70882
|
callSessionsDelete,
|
|
69585
70883
|
callSessionsSend,
|
|
70884
|
+
checkClaudeCliAuthStatus,
|
|
70885
|
+
checkCodexCliAuthStatus,
|
|
70886
|
+
checkCodexReadiness,
|
|
69586
70887
|
claimActiveDaemon,
|
|
70888
|
+
classifyJobBindingSource,
|
|
70889
|
+
clearCodexReadinessCache,
|
|
70890
|
+
codexCommandResolvesWithoutOverride,
|
|
70891
|
+
codexReadinessCacheKey,
|
|
70892
|
+
collectCodexAppBundledCandidates,
|
|
69587
70893
|
collectCodexCommandCandidates,
|
|
69588
70894
|
commandExists,
|
|
69589
70895
|
createAiToolFailureTracker,
|
|
@@ -69599,6 +70905,7 @@ init_autostart_windows();
|
|
|
69599
70905
|
ensureDaemonReady,
|
|
69600
70906
|
ensureDefaultAiProvider,
|
|
69601
70907
|
ensureOpenClawOkxA2aPluginConfig,
|
|
70908
|
+
ensureProviderReadyForBinding,
|
|
69602
70909
|
ensureWindowsNativeLauncher,
|
|
69603
70910
|
evictPid,
|
|
69604
70911
|
exportDiagnosticLogs,
|
|
@@ -69626,6 +70933,7 @@ init_autostart_windows();
|
|
|
69626
70933
|
isDefaultTaskHome,
|
|
69627
70934
|
isGatewayAvailableForHeartbeat,
|
|
69628
70935
|
isHermesGatewayPluginEnabled,
|
|
70936
|
+
isInfoEventAllowlisted,
|
|
69629
70937
|
isOfflineReplayForTrigger,
|
|
69630
70938
|
isOpenClawA2aPluginAvailable,
|
|
69631
70939
|
isOpenClawGatewayAvailable,
|
|
@@ -69651,11 +70959,15 @@ init_autostart_windows();
|
|
|
69651
70959
|
parsePluginYamlVersion,
|
|
69652
70960
|
parseWindowsParentProcessJson,
|
|
69653
70961
|
performRuntimeSwitch,
|
|
70962
|
+
persistCurrentRuntimeSelectionForNewJob,
|
|
69654
70963
|
pickOnchainosWin32Candidate,
|
|
70964
|
+
prepareAndBindCurrentRuntimeForNewJob,
|
|
69655
70965
|
printLogsExportUsage,
|
|
69656
70966
|
printXmtpTestUsage,
|
|
70967
|
+
probeCodexAuthStatus,
|
|
69657
70968
|
processFileMessage,
|
|
69658
70969
|
readAiProviderTimeoutMs,
|
|
70970
|
+
readCachedCodexReadiness,
|
|
69659
70971
|
readLastLines,
|
|
69660
70972
|
readParentProcessCommandForPlatform,
|
|
69661
70973
|
readUserAttentionWatcherEvent,
|
|
@@ -69669,8 +70981,12 @@ init_autostart_windows();
|
|
|
69669
70981
|
resetResolvedOnchainosBinForTests,
|
|
69670
70982
|
resolveAiPermissionPreset,
|
|
69671
70983
|
resolveAiProviderCommand,
|
|
70984
|
+
resolveAiProviderCommandWithSource,
|
|
69672
70985
|
resolveAiProviderForDispatch,
|
|
70986
|
+
resolveAiProviderForDispatchWithReadiness,
|
|
70987
|
+
resolveCodexCommandCandidates,
|
|
69673
70988
|
resolveCodexCommandPath,
|
|
70989
|
+
resolveCodexCommandSource,
|
|
69674
70990
|
resolveConfiguredAiProvider,
|
|
69675
70991
|
resolveConfiguredAiProviderForJob,
|
|
69676
70992
|
resolveDirectCommunicationSessionTarget,
|
|
@@ -69682,6 +70998,7 @@ init_autostart_windows();
|
|
|
69682
70998
|
resolveOpenClawGatewayConfig,
|
|
69683
70999
|
resolveOpenClawGatewayRoute,
|
|
69684
71000
|
resolveOpenClawGatewayRoutes,
|
|
71001
|
+
resolveProviderCommandWithSelfHeal,
|
|
69685
71002
|
resolveSystemNotificationTargets,
|
|
69686
71003
|
resolveTaskConfigPath,
|
|
69687
71004
|
resolveTaskHome,
|
|
@@ -69709,6 +71026,8 @@ init_autostart_windows();
|
|
|
69709
71026
|
subscribeUserAttentionEvents,
|
|
69710
71027
|
summarizeXmtpTestEvents,
|
|
69711
71028
|
switchProvider,
|
|
71029
|
+
switchProviderWithReadinessGate,
|
|
71030
|
+
switchProviderWithReadinessGateCore,
|
|
69712
71031
|
systemdUnitHasCurrentRestartPolicy,
|
|
69713
71032
|
toOpenClawGatewaySessionKey,
|
|
69714
71033
|
toWindowsInvocation,
|