@bitkyc08/opencodex 2.12.0 → 2.13.0
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/README.md +1 -1
- package/gui/dist/assets/index-BHldBl6_.js +76 -0
- package/gui/dist/index.html +1 -1
- package/package.json +1 -1
- package/src/adapters/anthropic-output-schema.ts +137 -0
- package/src/adapters/anthropic.ts +15 -0
- package/src/adapters/google-antigravity-replay.ts +269 -5
- package/src/adapters/openai-chat.ts +25 -18
- package/src/adapters/openai-responses.ts +63 -17
- package/src/bridge.ts +11 -5
- package/src/claude/inbound.ts +24 -3
- package/src/cli/account-api.ts +2 -0
- package/src/cli/account-extended.ts +262 -1
- package/src/cli/account.ts +3 -1
- package/src/cli/dispatch.ts +532 -0
- package/src/cli/doctor.ts +26 -21
- package/src/cli/help.ts +9 -278
- package/src/cli/index.ts +45 -506
- package/src/cli/lab.ts +171 -65
- package/src/cli/registry.ts +384 -0
- package/src/cli/root.ts +86 -0
- package/src/cli/status.ts +9 -2
- package/src/cli/v2.ts +37 -2
- package/src/codex/account-label.ts +14 -1
- package/src/codex/account-store.ts +12 -1
- package/src/codex/admission.ts +2 -1
- package/src/codex/auth-api.ts +3 -2
- package/src/codex/catalog/metadata.ts +6 -4
- package/src/codex/catalog/parsing.ts +31 -7
- package/src/codex/catalog/provider-fetch.ts +2 -1
- package/src/codex/catalog/sync.ts +65 -11
- package/src/codex/features.ts +437 -31
- package/src/codex/history-job.ts +6 -11
- package/src/codex/history-provider.ts +10 -9
- package/src/codex/inject.ts +14 -1
- package/src/codex/native-profile-startup.ts +49 -3
- package/src/codex/native-residue.ts +14 -2
- package/src/codex/paths.ts +75 -1
- package/src/codex/reset-credit-recovery.ts +1044 -0
- package/src/codex/shim.ts +876 -16
- package/src/codex/sync.ts +27 -2
- package/src/config.ts +148 -10
- package/src/generated/compatibility-version.json +2572 -0
- package/src/images/loop.ts +1 -1
- package/src/lab/artifacts/sanitize.ts +3 -3
- package/src/lab/automation/budgets.ts +78 -0
- package/src/lab/automation/config-persistence.ts +256 -0
- package/src/lab/automation/constants.ts +39 -0
- package/src/lab/automation/cooldown.ts +103 -0
- package/src/lab/automation/dispatch.ts +211 -0
- package/src/lab/automation/index.ts +13 -0
- package/src/lab/automation/orchestrator.ts +480 -0
- package/src/lab/automation/persistence.ts +512 -0
- package/src/lab/automation/planner.ts +371 -0
- package/src/lab/automation/policy.ts +136 -0
- package/src/lab/automation/queue.ts +191 -0
- package/src/lab/automation/recovery.ts +24 -0
- package/src/lab/automation/route-context.ts +21 -0
- package/src/lab/automation/run-key.ts +44 -0
- package/src/lab/automation/runs-query.ts +34 -0
- package/src/lab/automation/types.ts +160 -0
- package/src/lab/conformance/types.ts +23 -0
- package/src/lab/events/types.ts +2 -1
- package/src/lab/events/validate.ts +2 -1
- package/src/lab/fabric/constants.ts +40 -0
- package/src/lab/fabric/executor.ts +492 -0
- package/src/lab/fabric/index.ts +80 -0
- package/src/lab/fabric/manifest.ts +222 -0
- package/src/lab/fabric/observe.ts +454 -0
- package/src/lab/fabric/patch.ts +79 -0
- package/src/lab/fabric/producer-child.ts +139 -0
- package/src/lab/fabric/producer-isolate.ts +276 -0
- package/src/lab/fabric/producer-protocol.ts +61 -0
- package/src/lab/fabric/scratch.ts +439 -0
- package/src/lab/fabric/subject.ts +106 -0
- package/src/lab/fabric/types.ts +134 -0
- package/src/lab/fabric/verifier.ts +98 -0
- package/src/lab/index.ts +2 -0
- package/src/lab/ledger/store.ts +173 -0
- package/src/lab/live/executor.ts +29 -4
- package/src/lab/live/transport.ts +3 -1
- package/src/lab/observe/from-conformance.ts +12 -57
- package/src/lab/observe/from-live.ts +9 -1
- package/src/lab/paths.ts +74 -9
- package/src/lab/projection/schema.ts +14 -1
- package/src/lab/projection/verdicts.ts +89 -53
- package/src/lab/projection/verification.ts +83 -3
- package/src/lab/query/catalog.ts +34 -5
- package/src/lab/query/freshness.ts +53 -0
- package/src/lab/query/index.ts +11 -0
- package/src/lab/query/latest-observation.ts +59 -0
- package/src/lab/query/passive-production.ts +159 -0
- package/src/lab/subject/installation-salt.ts +24 -4
- package/src/lab/subject/protocol-subject.ts +80 -0
- package/src/lab/subject/route-subject.ts +2 -1
- package/src/lib/fabric-task-execution-authority.ts +7 -0
- package/src/lib/fabric-task-host.ts +29 -0
- package/src/lib/lab-live-route-production.ts +130 -0
- package/src/lib/local-management-capability.ts +100 -0
- package/src/lib/server-resource-ownership.ts +71 -0
- package/src/lib/windows-elevation.ts +8 -1
- package/src/lib/windows-user-principal.ts +62 -2
- package/src/lib/winsw.ts +8 -2
- package/src/oauth/account-import/google-antigravity-adapter.ts +74 -0
- package/src/oauth/account-import/index.ts +15 -0
- package/src/oauth/account-import/parser.ts +83 -0
- package/src/oauth/account-import/registry.ts +18 -0
- package/src/oauth/account-import/service.ts +75 -0
- package/src/oauth/account-import/types.ts +91 -0
- package/src/oauth/anthropic.ts +12 -1
- package/src/oauth/callback-server.ts +8 -2
- package/src/oauth/chatgpt.ts +12 -1
- package/src/oauth/google-antigravity.ts +27 -0
- package/src/oauth/health.ts +12 -48
- package/src/oauth/index.ts +122 -0
- package/src/oauth/kimi.ts +16 -2
- package/src/oauth/nous.ts +749 -0
- package/src/oauth/store.ts +56 -3
- package/src/providers/codex-capacity.ts +1 -0
- package/src/providers/registry.ts +161 -16
- package/src/responses/custom-tool-compat.ts +182 -0
- package/src/responses/parser.ts +22 -0
- package/src/responses/reasoning-replay-cache.ts +168 -11
- package/src/responses/schema.ts +1 -1
- package/src/router.ts +5 -20
- package/src/routing/capability.ts +9 -3
- package/src/routing/compatibility/assemble.ts +159 -0
- package/src/routing/compatibility/behavior.ts +224 -0
- package/src/routing/compatibility/catalog.ts +99 -0
- package/src/routing/compatibility/endpoint.ts +52 -0
- package/src/routing/compatibility/policy.ts +181 -0
- package/src/routing/compatibility/reader.ts +110 -0
- package/src/routing/compatibility/subject.ts +191 -0
- package/src/routing/compatibility/types.ts +64 -0
- package/src/routing/compatibility/version.ts +104 -0
- package/src/routing/evaluator.ts +27 -32
- package/src/routing/profile.ts +118 -4
- package/src/routing/trace.ts +82 -28
- package/src/server/auth-cors.ts +7 -0
- package/src/server/background-lifecycle.ts +20 -7
- package/src/server/chat-completions.ts +2 -2
- package/src/server/claude-messages.ts +6 -3
- package/src/server/direct-local-http.ts +343 -0
- package/src/server/index.ts +80 -18
- package/src/server/lifecycle.ts +12 -4
- package/src/server/local-management-read-client.ts +90 -0
- package/src/server/management/agent-settings-routes.ts +27 -3
- package/src/server/management/lab-automation-routes.ts +206 -0
- package/src/server/management/lab-routes.ts +13 -0
- package/src/server/management/logs-usage-routes.ts +1 -0
- package/src/server/management/oauth-account-routes.ts +57 -1
- package/src/server/management/provider-routes.ts +16 -0
- package/src/server/management/routing-profile-routes.ts +12 -24
- package/src/server/management-api.ts +2 -0
- package/src/server/management-auth.ts +68 -3
- package/src/server/proxy-liveness.ts +3 -2
- package/src/server/request-decompress.ts +9 -3
- package/src/server/request-log.ts +53 -4
- package/src/server/responses/collaboration.ts +93 -17
- package/src/server/responses/compact.ts +3 -0
- package/src/server/responses/core.ts +202 -15
- package/src/server/responses/policy-fallback.ts +1 -0
- package/src/server/responses-custom-tool-repair.ts +280 -0
- package/src/server/responses-terminal-repair.ts +342 -0
- package/src/server/startup-action-control.ts +8 -1
- package/src/service.ts +278 -12
- package/src/types.ts +49 -3
- package/src/usage/expected-prices.ts +30 -0
- package/src/usage/log.ts +30 -4
- package/src/usage/summary.ts +153 -2
- package/src/web-search/loop.ts +1 -1
- package/gui/dist/assets/index-Cw1_mxO8.js +0 -70
- package/src/cli/internal-dispatch.ts +0 -20
|
@@ -3,7 +3,7 @@ import { closeSync, existsSync, fsyncSync, mkdirSync, openSync, readFileSync, re
|
|
|
3
3
|
import { dirname, join, resolve } from "node:path";
|
|
4
4
|
import { zstdDecompressSync } from "node:zlib";
|
|
5
5
|
import { Database } from "bun:sqlite";
|
|
6
|
-
import {
|
|
6
|
+
import { resolveCodexStateDbPath } from "./paths";
|
|
7
7
|
import { atomicWriteFile, getConfigDir } from "../config";
|
|
8
8
|
|
|
9
9
|
/**
|
|
@@ -13,7 +13,6 @@ import { atomicWriteFile, getConfigDir } from "../config";
|
|
|
13
13
|
*/
|
|
14
14
|
export const MAX_ROLLOUT_ZST_DECOMPRESSED_BYTES = 64 * 1024 * 1024;
|
|
15
15
|
|
|
16
|
-
const STATE_DB_PATH = join(CODEX_HOME, "state_5.sqlite");
|
|
17
16
|
/**
|
|
18
17
|
* The manifest that shadows one state database.
|
|
19
18
|
*
|
|
@@ -26,7 +25,6 @@ export function historyBackupPathFor(stateDbPath: string): string {
|
|
|
26
25
|
const id = createHash("sha256").update(normalized).digest("hex").slice(0, 16);
|
|
27
26
|
return join(getConfigDir(), `codex-history-backup-${id}.json`);
|
|
28
27
|
}
|
|
29
|
-
const HISTORY_BACKUP_PATH = historyBackupPathFor(STATE_DB_PATH);
|
|
30
28
|
const RESUMABLE_SOURCES = ["cli", "vscode"] as const;
|
|
31
29
|
|
|
32
30
|
/**
|
|
@@ -690,8 +688,8 @@ function openaiRestoreIsNoop(stateDbPath: string, backupPath: string): boolean {
|
|
|
690
688
|
|
|
691
689
|
export function syncCodexHistoryProvider(
|
|
692
690
|
provider: CodexHistoryProvider,
|
|
693
|
-
stateDbPath =
|
|
694
|
-
backupPath =
|
|
691
|
+
stateDbPath = resolveCodexStateDbPath(),
|
|
692
|
+
backupPath = historyBackupPathFor(stateDbPath),
|
|
695
693
|
opts: { skipWhenProvablyNoop?: boolean } = {},
|
|
696
694
|
): CodexHistorySyncResult {
|
|
697
695
|
// Opt-in steady-state gate (Design B loopback callers only): default semantics of
|
|
@@ -824,7 +822,7 @@ function restoreCodexHistoryProvider(stateDbPath: string, backupPath: string): C
|
|
|
824
822
|
}
|
|
825
823
|
}
|
|
826
824
|
|
|
827
|
-
export function restoreLegacyOpenaiHistory(stateDbPath =
|
|
825
|
+
export function restoreLegacyOpenaiHistory(stateDbPath = resolveCodexStateDbPath()): CodexHistorySyncResult {
|
|
828
826
|
if (!existsSync(stateDbPath)) return { rows: 0, files: 0 };
|
|
829
827
|
const retried = withHistoryRetryResult(() => {
|
|
830
828
|
const db = openStateDb(stateDbPath);
|
|
@@ -844,8 +842,8 @@ export function restoreLegacyOpenaiHistory(stateDbPath = STATE_DB_PATH): CodexHi
|
|
|
844
842
|
* per tick so a locked DB never stalls the event loop beyond one sqlite busy wait.
|
|
845
843
|
*/
|
|
846
844
|
export function migrateHistoryToOpenai(
|
|
847
|
-
stateDbPath =
|
|
848
|
-
backupPath =
|
|
845
|
+
stateDbPath = resolveCodexStateDbPath(),
|
|
846
|
+
backupPath = historyBackupPathFor(stateDbPath),
|
|
849
847
|
opts: { attempts?: number; delayMs?: number; sleepFn?: (ms: number) => void } = {},
|
|
850
848
|
): CodexHistorySyncResult {
|
|
851
849
|
if (!existsSync(stateDbPath)) return { rows: 0, files: 0 };
|
|
@@ -948,7 +946,10 @@ export interface PendingHistoryCount {
|
|
|
948
946
|
* pending predicate mirrors ejectRemainingOpencodexHistory exactly — rows eject ignores
|
|
949
947
|
* (empty first_user_message) are not counted, so 0 really means "migration done".
|
|
950
948
|
*/
|
|
951
|
-
export function countPendingOpencodexHistory(
|
|
949
|
+
export function countPendingOpencodexHistory(
|
|
950
|
+
stateDbPath = resolveCodexStateDbPath(),
|
|
951
|
+
backupPath = historyBackupPathFor(stateDbPath),
|
|
952
|
+
): PendingHistoryCount {
|
|
952
953
|
let backupEntries = 0;
|
|
953
954
|
try {
|
|
954
955
|
const manifest = readBackup(backupPath, stateDbPath);
|
package/src/codex/inject.ts
CHANGED
|
@@ -129,6 +129,12 @@ export interface InjectCodexOptions {
|
|
|
129
129
|
* caller that is willing to wait can raise it.
|
|
130
130
|
*/
|
|
131
131
|
lockTimeoutMs?: number;
|
|
132
|
+
/**
|
|
133
|
+
* Validate the same config transformations and write-coordination eligibility without
|
|
134
|
+
* changing the journal, config, profile, catalog, cache, or history. Sync uses this before
|
|
135
|
+
* provider discovery so a deterministic config refusal cannot degrade an existing catalog.
|
|
136
|
+
*/
|
|
137
|
+
validateOnly?: boolean;
|
|
132
138
|
}
|
|
133
139
|
|
|
134
140
|
function configuredManagedSubagentDefaults(
|
|
@@ -660,7 +666,7 @@ export async function injectCodexConfig(
|
|
|
660
666
|
if (activeProvider) {
|
|
661
667
|
// A launcher may have journaled before the provider manager took ownership. Never let shutdown
|
|
662
668
|
// replay that stale snapshot over externally managed config.
|
|
663
|
-
removeJournal();
|
|
669
|
+
if (!options.validateOnly) removeJournal();
|
|
664
670
|
const nativeSubagentDefaultsWarning = configuredManagedSubagentDefaults(
|
|
665
671
|
config,
|
|
666
672
|
)
|
|
@@ -867,6 +873,13 @@ export async function injectCodexConfig(
|
|
|
867
873
|
};
|
|
868
874
|
}
|
|
869
875
|
|
|
876
|
+
if (options.validateOnly) {
|
|
877
|
+
return {
|
|
878
|
+
success: true,
|
|
879
|
+
message: "Codex config injection preflight passed; no files were changed.",
|
|
880
|
+
};
|
|
881
|
+
}
|
|
882
|
+
|
|
870
883
|
const applyNativeArtifacts = (): void => {
|
|
871
884
|
writeJournal({
|
|
872
885
|
currentStateIsNative: !hasInjectedCodexRouting(rawContent),
|
|
@@ -20,8 +20,15 @@ export type NativeMainStartupGateSnapshot =
|
|
|
20
20
|
| { status: "ready"; homeId: string | null }
|
|
21
21
|
| {
|
|
22
22
|
status: "blocked";
|
|
23
|
-
homeId: string;
|
|
24
|
-
reason:
|
|
23
|
+
homeId: string | null;
|
|
24
|
+
reason:
|
|
25
|
+
| "foreign-ownership"
|
|
26
|
+
| "ownership-unknown"
|
|
27
|
+
| "recovery-pending"
|
|
28
|
+
| "manual-recovery"
|
|
29
|
+
| "owner-conflict"
|
|
30
|
+
| "owner-unavailable"
|
|
31
|
+
| "stage-cleanup-required";
|
|
25
32
|
};
|
|
26
33
|
|
|
27
34
|
export interface NativeMainStartupGateDeps {
|
|
@@ -42,6 +49,10 @@ export interface NativeMainStartupLifecycle {
|
|
|
42
49
|
let epoch = 0;
|
|
43
50
|
let snapshot: NativeMainStartupGateSnapshot = { status: "ready", homeId: null };
|
|
44
51
|
let settled: Promise<NativeMainStartupGateSnapshot> = Promise.resolve(snapshot);
|
|
52
|
+
export type NativeMainServiceOwnershipBlockReason =
|
|
53
|
+
| "foreign-ownership"
|
|
54
|
+
| "ownership-unknown";
|
|
55
|
+
const serviceOwnershipRefs = new Map<NativeMainServiceOwnershipBlockReason, number>();
|
|
45
56
|
interface StartupEntry {
|
|
46
57
|
homeId: string;
|
|
47
58
|
refs: number;
|
|
@@ -295,6 +306,37 @@ export function startNativeMainStartupLifecycle(
|
|
|
295
306
|
};
|
|
296
307
|
}
|
|
297
308
|
|
|
309
|
+
function activeServiceOwnershipBlockReason(): NativeMainServiceOwnershipBlockReason | null {
|
|
310
|
+
if ((serviceOwnershipRefs.get("foreign-ownership") ?? 0) > 0) return "foreign-ownership";
|
|
311
|
+
if ((serviceOwnershipRefs.get("ownership-unknown") ?? 0) > 0) return "ownership-unknown";
|
|
312
|
+
return null;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
function serviceOwnershipSnapshot(
|
|
316
|
+
reason: NativeMainServiceOwnershipBlockReason,
|
|
317
|
+
): NativeMainStartupGateSnapshot {
|
|
318
|
+
return { status: "blocked", homeId: null, reason };
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/** Close native-main admission without resolving or creating any CODEX_HOME artifacts. */
|
|
322
|
+
export function blockNativeMainStartupForUnownedServiceHome(
|
|
323
|
+
reason: NativeMainServiceOwnershipBlockReason,
|
|
324
|
+
): NativeMainStartupLifecycle {
|
|
325
|
+
serviceOwnershipRefs.set(reason, (serviceOwnershipRefs.get(reason) ?? 0) + 1);
|
|
326
|
+
let released = false;
|
|
327
|
+
return {
|
|
328
|
+
homeId: null,
|
|
329
|
+
settled: Promise.resolve(serviceOwnershipSnapshot(reason)),
|
|
330
|
+
async release() {
|
|
331
|
+
if (released) return;
|
|
332
|
+
released = true;
|
|
333
|
+
const remaining = Math.max(0, (serviceOwnershipRefs.get(reason) ?? 0) - 1);
|
|
334
|
+
if (remaining === 0) serviceOwnershipRefs.delete(reason);
|
|
335
|
+
else serviceOwnershipRefs.set(reason, remaining);
|
|
336
|
+
},
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
|
|
298
340
|
export function bindNativeMainStartupLifecycle(server: object, lifecycle: NativeMainStartupLifecycle): void {
|
|
299
341
|
serverLifecycles.set(server, lifecycle);
|
|
300
342
|
}
|
|
@@ -307,7 +349,7 @@ export async function releaseNativeMainStartupLifecycle(server: object): Promise
|
|
|
307
349
|
}
|
|
308
350
|
|
|
309
351
|
export function isNativeMainTrafficBlocked(): boolean {
|
|
310
|
-
return snapshot.status === "blocked";
|
|
352
|
+
return activeServiceOwnershipBlockReason() !== null || snapshot.status === "blocked";
|
|
311
353
|
}
|
|
312
354
|
|
|
313
355
|
/**
|
|
@@ -340,9 +382,13 @@ export function completeNativeMainRecovery(homeId: string): boolean {
|
|
|
340
382
|
}
|
|
341
383
|
|
|
342
384
|
export function nativeMainStartupGateSnapshot(): NativeMainStartupGateSnapshot {
|
|
385
|
+
const reason = activeServiceOwnershipBlockReason();
|
|
386
|
+
if (reason) return serviceOwnershipSnapshot(reason);
|
|
343
387
|
return { ...snapshot };
|
|
344
388
|
}
|
|
345
389
|
|
|
346
390
|
export function waitForNativeMainStartupGate(): Promise<NativeMainStartupGateSnapshot> {
|
|
391
|
+
const reason = activeServiceOwnershipBlockReason();
|
|
392
|
+
if (reason) return Promise.resolve(serviceOwnershipSnapshot(reason));
|
|
347
393
|
return settled;
|
|
348
394
|
}
|
|
@@ -30,6 +30,7 @@ import {
|
|
|
30
30
|
DEFAULT_CATALOG_PATH,
|
|
31
31
|
getCodexHome,
|
|
32
32
|
readRootTomlString,
|
|
33
|
+
resolveCodexStateDbPath,
|
|
33
34
|
} from "./paths";
|
|
34
35
|
|
|
35
36
|
export type NativeResidueSurface =
|
|
@@ -77,7 +78,6 @@ const PROFILE_FILE_NAME = basename(CODEX_PROFILE_PATH);
|
|
|
77
78
|
const CATALOG_FILE_NAME = basename(DEFAULT_CATALOG_PATH);
|
|
78
79
|
const MODELS_CACHE_FILE_NAME = basename(CODEX_MODELS_CACHE_PATH);
|
|
79
80
|
const JOURNAL_FILE_NAME = "opencodex-journal.json";
|
|
80
|
-
const HISTORY_DATABASE_FILE_NAME = "state_5.sqlite";
|
|
81
81
|
const ROUTED_CATALOG_DESCRIPTION_PREFIX = "Routed via opencodex → ";
|
|
82
82
|
const MAX_ROLLOUT_INSPECTION_BYTES = 64 * 1024 * 1024;
|
|
83
83
|
const ROLLOUT_READ_CHUNK_BYTES = 64 * 1024;
|
|
@@ -640,8 +640,20 @@ export function classifyNativeRoutedResidue(): NativeRoutedResidueResult {
|
|
|
640
640
|
return indeterminate("partial-write", unresolved, `CODEX_HOME cannot be resolved: ${errorReason(error)}`);
|
|
641
641
|
}
|
|
642
642
|
|
|
643
|
-
const stateDatabasePath = join(codexHome, HISTORY_DATABASE_FILE_NAME);
|
|
644
643
|
const configPath = join(codexHome, CONFIG_FILE_NAME);
|
|
644
|
+
let stateDatabasePath: string;
|
|
645
|
+
try {
|
|
646
|
+
stateDatabasePath = resolveCodexStateDbPath({ codexHome });
|
|
647
|
+
} catch (error) {
|
|
648
|
+
// Residue classification is a total, read-only safety boundary. An
|
|
649
|
+
// indeterminate SQLite authority must refuse coordination without escaping
|
|
650
|
+
// as an exception or falling through to a different state database.
|
|
651
|
+
return indeterminate(
|
|
652
|
+
"config",
|
|
653
|
+
configPath,
|
|
654
|
+
`SQLite home cannot be resolved: ${errorReason(error)}`,
|
|
655
|
+
);
|
|
656
|
+
}
|
|
645
657
|
const profilePath = join(codexHome, PROFILE_FILE_NAME);
|
|
646
658
|
const modelsCachePath = join(codexHome, MODELS_CACHE_FILE_NAME);
|
|
647
659
|
const journalPath = join(codexHome, JOURNAL_FILE_NAME);
|
package/src/codex/paths.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { realpathSync, statSync } from "node:fs";
|
|
1
|
+
import { readFileSync, realpathSync, statSync } from "node:fs";
|
|
2
2
|
import { isAbsolute, join, resolve } from "node:path";
|
|
3
3
|
import { expandUserPath } from "../config";
|
|
4
4
|
import { defaultCodexHome } from "./home";
|
|
@@ -34,6 +34,80 @@ export function getCodexHome(): string {
|
|
|
34
34
|
return resolveCodexHome();
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
+
export interface CodexSqliteHomeDeps {
|
|
38
|
+
env?: NodeJS.ProcessEnv;
|
|
39
|
+
cwd?: () => string;
|
|
40
|
+
codexHome?: string;
|
|
41
|
+
readConfig?: (path: string) => string;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
type RootTomlStringState =
|
|
45
|
+
| { kind: "absent" }
|
|
46
|
+
| { kind: "value"; value: string }
|
|
47
|
+
| { kind: "invalid" };
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Parse the authoritative SQLite setting without changing the tolerant helper
|
|
51
|
+
* used by injection/catalog readers. History ownership must distinguish a
|
|
52
|
+
* missing key from a present value that Codex cannot interpret as a path.
|
|
53
|
+
*/
|
|
54
|
+
function readAuthoritativeRootTomlString(content: string, key: string): RootTomlStringState {
|
|
55
|
+
let parsed: unknown;
|
|
56
|
+
try {
|
|
57
|
+
parsed = Bun.TOML.parse(content);
|
|
58
|
+
} catch {
|
|
59
|
+
return { kind: "invalid" };
|
|
60
|
+
}
|
|
61
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return { kind: "invalid" };
|
|
62
|
+
const root = parsed as Record<string, unknown>;
|
|
63
|
+
if (!Object.prototype.hasOwnProperty.call(root, key)) return { kind: "absent" };
|
|
64
|
+
const value = root[key];
|
|
65
|
+
if (typeof value !== "string" || value.trim() === "") return { kind: "invalid" };
|
|
66
|
+
return { kind: "value", value: value.trim() };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Resolve Codex's SQLite state root at call time.
|
|
71
|
+
*
|
|
72
|
+
* Codex permits SQLite-backed state to live outside CODEX_HOME, especially for
|
|
73
|
+
* Windows Desktop sessions whose app-server runs in WSL. Keep the precedence
|
|
74
|
+
* identical to Codex: root config, then environment, then the effective home.
|
|
75
|
+
*/
|
|
76
|
+
export function resolveCodexSqliteHome(deps: CodexSqliteHomeDeps = {}): string {
|
|
77
|
+
const codexHome = deps.codexHome ?? getCodexHome();
|
|
78
|
+
const readConfig = deps.readConfig ?? (path => readFileSync(path, "utf8"));
|
|
79
|
+
const configPath = join(codexHome, "config.toml");
|
|
80
|
+
let configured: RootTomlStringState = { kind: "absent" };
|
|
81
|
+
try {
|
|
82
|
+
configured = readAuthoritativeRootTomlString(readConfig(configPath), "sqlite_home");
|
|
83
|
+
} catch (cause) {
|
|
84
|
+
if ((cause as NodeJS.ErrnoException | undefined)?.code !== "ENOENT") {
|
|
85
|
+
throw new Error(
|
|
86
|
+
`Codex config could not be read while resolving sqlite_home: ${configPath}`,
|
|
87
|
+
{ cause },
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
// A genuinely absent config cannot contain the authoritative root override,
|
|
91
|
+
// so only ENOENT may continue to the documented environment/home fallback.
|
|
92
|
+
}
|
|
93
|
+
if (configured.kind === "invalid") {
|
|
94
|
+
throw new Error(`Codex config has an invalid sqlite_home setting: ${configPath}`);
|
|
95
|
+
}
|
|
96
|
+
const raw = configured.kind === "value"
|
|
97
|
+
? configured.value
|
|
98
|
+
: (deps.env ?? process.env).CODEX_SQLITE_HOME?.trim() || "";
|
|
99
|
+
if (!raw) return codexHome;
|
|
100
|
+
const expanded = expandUserPath(raw);
|
|
101
|
+
return isAbsolute(expanded)
|
|
102
|
+
? resolve(expanded)
|
|
103
|
+
: resolve((deps.cwd ?? process.cwd)(), expanded);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** Active Codex thread-state database, derived from the call-time SQLite root. */
|
|
107
|
+
export function resolveCodexStateDbPath(deps: CodexSqliteHomeDeps = {}): string {
|
|
108
|
+
return join(resolveCodexSqliteHome(deps), "state_5.sqlite");
|
|
109
|
+
}
|
|
110
|
+
|
|
37
111
|
export function tomlString(value: string): string {
|
|
38
112
|
return JSON.stringify(value);
|
|
39
113
|
}
|