@cleocode/core 2026.6.18 → 2026.7.1
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/docs/export-document.js +1173 -334
- package/dist/docs/export-document.js.map +3 -3
- package/dist/lifecycle/effective-stage.js +3 -3
- package/dist/lifecycle/rollup.js +3 -3
- package/dist/llm/plugin-facade.js +1218 -383
- package/dist/llm/plugin-facade.js.map +3 -3
- package/dist/memory/brain-writer-handlers.d.ts.map +1 -1
- package/dist/memory/brain-writer-handlers.js +39 -0
- package/dist/memory/brain-writer-handlers.js.map +1 -1
- package/dist/orchestrate/admission.d.ts +65 -0
- package/dist/orchestrate/admission.d.ts.map +1 -0
- package/dist/orchestrate/admission.js +54 -0
- package/dist/orchestrate/admission.js.map +1 -0
- package/dist/orchestrate/lifecycle-ops.d.ts.map +1 -1
- package/dist/orchestrate/lifecycle-ops.js +10 -0
- package/dist/orchestrate/lifecycle-ops.js.map +1 -1
- package/dist/orchestrate/query-ops.d.ts.map +1 -1
- package/dist/orchestrate/query-ops.js +37 -8
- package/dist/orchestrate/query-ops.js.map +1 -1
- package/dist/orchestrate/spawn-ops.d.ts.map +1 -1
- package/dist/orchestrate/spawn-ops.js +24 -0
- package/dist/orchestrate/spawn-ops.js.map +1 -1
- package/dist/release/pr-evidence.d.ts +14 -3
- package/dist/release/pr-evidence.d.ts.map +1 -1
- package/dist/release/pr-evidence.js +44 -4
- package/dist/release/pr-evidence.js.map +1 -1
- package/dist/resources/governor.d.ts +133 -0
- package/dist/resources/governor.d.ts.map +1 -0
- package/dist/resources/governor.js +377 -0
- package/dist/resources/governor.js.map +1 -0
- package/dist/resources/supervisor-admit.d.ts +42 -0
- package/dist/resources/supervisor-admit.d.ts.map +1 -0
- package/dist/resources/supervisor-admit.js +136 -0
- package/dist/resources/supervisor-admit.js.map +1 -0
- package/dist/selfimprove/fix-gen.js +1216 -381
- package/dist/selfimprove/fix-gen.js.map +3 -3
- package/dist/sentient/tick.d.ts.map +1 -1
- package/dist/sentient/tick.js +29 -0
- package/dist/sentient/tick.js.map +1 -1
- package/dist/store/agent-registry-store.d.ts.map +1 -1
- package/dist/store/agent-registry-store.js +17 -2
- package/dist/store/agent-registry-store.js.map +1 -1
- package/dist/store/dual-scope-db.d.ts.map +1 -1
- package/dist/store/dual-scope-db.js +35 -0
- package/dist/store/dual-scope-db.js.map +1 -1
- package/dist/store/sqlite.d.ts.map +1 -1
- package/dist/store/sqlite.js +15 -0
- package/dist/store/sqlite.js.map +1 -1
- package/dist/store/tasks-schema.d.ts +2 -0
- package/dist/store/tasks-schema.d.ts.map +1 -1
- package/dist/store/tasks-schema.js +17 -0
- package/dist/store/tasks-schema.js.map +1 -1
- package/dist/store/validation-schemas.d.ts +98 -98
- package/dist/tasks/compute-task-view.js +2 -2
- package/dist/tasks/evidence.d.ts +19 -0
- package/dist/tasks/evidence.d.ts.map +1 -1
- package/dist/tasks/evidence.js +32 -2
- package/dist/tasks/evidence.js.map +1 -1
- package/dist/tasks/tool-semaphore.d.ts +25 -0
- package/dist/tasks/tool-semaphore.d.ts.map +1 -1
- package/dist/tasks/tool-semaphore.js +68 -1
- package/dist/tasks/tool-semaphore.js.map +1 -1
- package/dist/tasks/update.d.ts +2 -0
- package/dist/tasks/update.d.ts.map +1 -1
- package/dist/tasks/update.js +1 -0
- package/dist/tasks/update.js.map +1 -1
- package/dist/validation/engine-ops.d.ts.map +1 -1
- package/dist/validation/engine-ops.js +33 -7
- package/dist/validation/engine-ops.js.map +1 -1
- package/package.json +12 -12
|
@@ -1914,6 +1914,7 @@ var init_tasks = __esm({
|
|
|
1914
1914
|
severity: { type: "string", enum: ["P0", "P1", "P2", "P3"] },
|
|
1915
1915
|
reason: { type: "string" },
|
|
1916
1916
|
dependsWaiver: { type: "string" },
|
|
1917
|
+
blockedBy: { type: "string" },
|
|
1917
1918
|
clearBlockedBy: { type: "boolean" },
|
|
1918
1919
|
relates: {
|
|
1919
1920
|
type: "array",
|
|
@@ -4613,6 +4614,12 @@ var init_operations_registry = __esm({
|
|
|
4613
4614
|
required: false,
|
|
4614
4615
|
description: "Justification to waive the critical-priority --depends requirement"
|
|
4615
4616
|
},
|
|
4617
|
+
{
|
|
4618
|
+
name: "blockedBy",
|
|
4619
|
+
type: "string",
|
|
4620
|
+
required: false,
|
|
4621
|
+
description: "Set the free-text blockedBy reason (gh#1106)"
|
|
4622
|
+
},
|
|
4616
4623
|
{
|
|
4617
4624
|
name: "clearBlockedBy",
|
|
4618
4625
|
type: "boolean",
|
|
@@ -12436,7 +12443,7 @@ var init_lafs = __esm({
|
|
|
12436
12443
|
|
|
12437
12444
|
// packages/contracts/src/lease-ipc/messages.ts
|
|
12438
12445
|
import { z as z11 } from "zod";
|
|
12439
|
-
var LeaseScopeSchema, LeaseLaneSchema, LeaseAcquireRequestSchema, LeaseReleaseRequestSchema, LeaseRenewRequestSchema, RateCheckRequestSchema, ToolGrantRequestSchema, QueuePriorityClassSchema, QueueAdmitRequestSchema, WorkerHeartbeatRequestSchema, LeaseIpcRequestSchema, LeaseGrantedResponseSchema, LeaseQueuedResponseSchema, LeaseDeniedResponseSchema, RateResultResponseSchema, ToolGrantedResponseSchema, LeaseRevokedResponseSchema, ChildKilledUnresponsiveResponseSchema, QueueAdmitDispositionSchema, QueueAdmitResultResponseSchema, HeartbeatAckResponseSchema, LeaseErrorResponseSchema, LeaseIpcResponseSchema, LeaseIpcRequestEnvelopeSchema, LeaseIpcResponseEnvelopeSchema, LeaseIpcEnvelopeSchema, LEASE_IPC_REQUEST_KINDS, LEASE_IPC_RESPONSE_KINDS, LEASE_IPC_MESSAGE_KINDS;
|
|
12446
|
+
var LeaseScopeSchema, LeaseLaneSchema, LeaseAcquireRequestSchema, LeaseReleaseRequestSchema, LeaseRenewRequestSchema, RateCheckRequestSchema, ToolGrantRequestSchema, QueuePriorityClassSchema, QueueAdmitRequestSchema, WorkerHeartbeatRequestSchema, ResourceAdmitRequestSchema, ResourceReleaseRequestSchema, LeaseIpcRequestSchema, LeaseGrantedResponseSchema, LeaseQueuedResponseSchema, LeaseDeniedResponseSchema, RateResultResponseSchema, ToolGrantedResponseSchema, LeaseRevokedResponseSchema, ChildKilledUnresponsiveResponseSchema, QueueAdmitDispositionSchema, QueueAdmitResultResponseSchema, HeartbeatAckResponseSchema, ResourceAdmitDispositionSchema, ResourceAdmitResultResponseSchema, ResourceReleaseResultResponseSchema, LeaseErrorResponseSchema, LeaseIpcResponseSchema, LeaseIpcRequestEnvelopeSchema, LeaseIpcResponseEnvelopeSchema, LeaseIpcEnvelopeSchema, LEASE_IPC_REQUEST_KINDS, LEASE_IPC_RESPONSE_KINDS, LEASE_IPC_MESSAGE_KINDS;
|
|
12440
12447
|
var init_messages2 = __esm({
|
|
12441
12448
|
"packages/contracts/src/lease-ipc/messages.ts"() {
|
|
12442
12449
|
"use strict";
|
|
@@ -12521,6 +12528,24 @@ var init_messages2 = __esm({
|
|
|
12521
12528
|
/** The worker's own view of whether it is currently inside an LLM call. */
|
|
12522
12529
|
in_flight_llm: z11.boolean()
|
|
12523
12530
|
}).strict();
|
|
12531
|
+
ResourceAdmitRequestSchema = z11.object({
|
|
12532
|
+
/** Tag discriminating this request variant. */
|
|
12533
|
+
kind: z11.literal("resource_admit"),
|
|
12534
|
+
/** The resource class (e.g. `"db-heavy"`, `"full-build"`, `"test-run"`). */
|
|
12535
|
+
class: z11.string().min(1),
|
|
12536
|
+
/** The caller holding the slot — process/worktree-unique; the release key. */
|
|
12537
|
+
holder_id: z11.string().min(1),
|
|
12538
|
+
/** The client-computed budget: the max concurrent holders for this class. */
|
|
12539
|
+
budget: z11.number().int().nonnegative()
|
|
12540
|
+
}).strict();
|
|
12541
|
+
ResourceReleaseRequestSchema = z11.object({
|
|
12542
|
+
/** Tag discriminating this request variant. */
|
|
12543
|
+
kind: z11.literal("resource_release"),
|
|
12544
|
+
/** The resource class the slot belongs to. */
|
|
12545
|
+
class: z11.string().min(1),
|
|
12546
|
+
/** The holder that was admitted (matches the admit `holder_id`). */
|
|
12547
|
+
holder_id: z11.string().min(1)
|
|
12548
|
+
}).strict();
|
|
12524
12549
|
LeaseIpcRequestSchema = z11.discriminatedUnion("kind", [
|
|
12525
12550
|
LeaseAcquireRequestSchema,
|
|
12526
12551
|
LeaseReleaseRequestSchema,
|
|
@@ -12528,7 +12553,9 @@ var init_messages2 = __esm({
|
|
|
12528
12553
|
RateCheckRequestSchema,
|
|
12529
12554
|
ToolGrantRequestSchema,
|
|
12530
12555
|
QueueAdmitRequestSchema,
|
|
12531
|
-
WorkerHeartbeatRequestSchema
|
|
12556
|
+
WorkerHeartbeatRequestSchema,
|
|
12557
|
+
ResourceAdmitRequestSchema,
|
|
12558
|
+
ResourceReleaseRequestSchema
|
|
12532
12559
|
]);
|
|
12533
12560
|
LeaseGrantedResponseSchema = z11.object({
|
|
12534
12561
|
/** Tag discriminating this response variant. */
|
|
@@ -12629,6 +12656,25 @@ var init_messages2 = __esm({
|
|
|
12629
12656
|
/** Tag discriminating this response variant. */
|
|
12630
12657
|
kind: z11.literal("heartbeat_ack")
|
|
12631
12658
|
}).strict();
|
|
12659
|
+
ResourceAdmitDispositionSchema = z11.enum(["admitted", "deferred"]);
|
|
12660
|
+
ResourceAdmitResultResponseSchema = z11.object({
|
|
12661
|
+
/** Tag discriminating this response variant. */
|
|
12662
|
+
kind: z11.literal("resource_admit_result"),
|
|
12663
|
+
/** Whether the heavy op was admitted or deferred. */
|
|
12664
|
+
disposition: ResourceAdmitDispositionSchema,
|
|
12665
|
+
/** Back-off in ms before re-requesting (0 when admitted). */
|
|
12666
|
+
retry_after_ms: z11.number().int().nonnegative(),
|
|
12667
|
+
/** Remaining slots for the class after this decision. */
|
|
12668
|
+
slots_remaining: z11.number().int().nonnegative()
|
|
12669
|
+
}).strict();
|
|
12670
|
+
ResourceReleaseResultResponseSchema = z11.object({
|
|
12671
|
+
/** Tag discriminating this response variant. */
|
|
12672
|
+
kind: z11.literal("resource_release_result"),
|
|
12673
|
+
/** Whether a held slot was actually released (false = no such holder). */
|
|
12674
|
+
released: z11.boolean(),
|
|
12675
|
+
/** Remaining in-flight holders for the class after the release. */
|
|
12676
|
+
slots_remaining: z11.number().int().nonnegative()
|
|
12677
|
+
}).strict();
|
|
12632
12678
|
LeaseErrorResponseSchema = z11.object({
|
|
12633
12679
|
/** Tag discriminating this response variant. */
|
|
12634
12680
|
kind: z11.literal("error"),
|
|
@@ -12647,6 +12693,8 @@ var init_messages2 = __esm({
|
|
|
12647
12693
|
ChildKilledUnresponsiveResponseSchema,
|
|
12648
12694
|
QueueAdmitResultResponseSchema,
|
|
12649
12695
|
HeartbeatAckResponseSchema,
|
|
12696
|
+
ResourceAdmitResultResponseSchema,
|
|
12697
|
+
ResourceReleaseResultResponseSchema,
|
|
12650
12698
|
LeaseErrorResponseSchema
|
|
12651
12699
|
]);
|
|
12652
12700
|
LeaseIpcRequestEnvelopeSchema = z11.object({
|
|
@@ -12680,7 +12728,9 @@ var init_messages2 = __esm({
|
|
|
12680
12728
|
"rate_check",
|
|
12681
12729
|
"tool_grant",
|
|
12682
12730
|
"queue_admit",
|
|
12683
|
-
"worker_heartbeat"
|
|
12731
|
+
"worker_heartbeat",
|
|
12732
|
+
"resource_admit",
|
|
12733
|
+
"resource_release"
|
|
12684
12734
|
];
|
|
12685
12735
|
LEASE_IPC_RESPONSE_KINDS = [
|
|
12686
12736
|
"lease_granted",
|
|
@@ -12692,6 +12742,8 @@ var init_messages2 = __esm({
|
|
|
12692
12742
|
"child_killed_unresponsive",
|
|
12693
12743
|
"queue_admit_result",
|
|
12694
12744
|
"heartbeat_ack",
|
|
12745
|
+
"resource_admit_result",
|
|
12746
|
+
"resource_release_result",
|
|
12695
12747
|
"error"
|
|
12696
12748
|
];
|
|
12697
12749
|
LEASE_IPC_MESSAGE_KINDS = [
|
|
@@ -12702,9 +12754,11 @@ var init_messages2 = __esm({
|
|
|
12702
12754
|
});
|
|
12703
12755
|
|
|
12704
12756
|
// packages/contracts/src/lease-ipc/version.ts
|
|
12757
|
+
var LEASE_IPC_PROTOCOL_VERSION;
|
|
12705
12758
|
var init_version2 = __esm({
|
|
12706
12759
|
"packages/contracts/src/lease-ipc/version.ts"() {
|
|
12707
12760
|
"use strict";
|
|
12761
|
+
LEASE_IPC_PROTOCOL_VERSION = "1.2.0";
|
|
12708
12762
|
}
|
|
12709
12763
|
});
|
|
12710
12764
|
|
|
@@ -13964,6 +14018,25 @@ var init_render = __esm({
|
|
|
13964
14018
|
}
|
|
13965
14019
|
});
|
|
13966
14020
|
|
|
14021
|
+
// packages/contracts/src/resource-governor.ts
|
|
14022
|
+
var RESOURCE_CLASSES, DEFAULT_RESOURCE_RETRY_AFTER_MS;
|
|
14023
|
+
var init_resource_governor = __esm({
|
|
14024
|
+
"packages/contracts/src/resource-governor.ts"() {
|
|
14025
|
+
"use strict";
|
|
14026
|
+
RESOURCE_CLASSES = Object.freeze([
|
|
14027
|
+
"interactive-cli",
|
|
14028
|
+
"agent-session",
|
|
14029
|
+
"llm-call",
|
|
14030
|
+
"test-run",
|
|
14031
|
+
"scoped-build",
|
|
14032
|
+
"full-build",
|
|
14033
|
+
"db-heavy",
|
|
14034
|
+
"background-autonomous"
|
|
14035
|
+
]);
|
|
14036
|
+
DEFAULT_RESOURCE_RETRY_AFTER_MS = 2e3;
|
|
14037
|
+
}
|
|
14038
|
+
});
|
|
14039
|
+
|
|
13967
14040
|
// packages/contracts/src/session.ts
|
|
13968
14041
|
var init_session2 = __esm({
|
|
13969
14042
|
"packages/contracts/src/session.ts"() {
|
|
@@ -15714,6 +15787,7 @@ var init_src = __esm({
|
|
|
15714
15787
|
init_evidence_atoms();
|
|
15715
15788
|
init_plan();
|
|
15716
15789
|
init_render();
|
|
15790
|
+
init_resource_governor();
|
|
15717
15791
|
init_session2();
|
|
15718
15792
|
init_session_journal();
|
|
15719
15793
|
init_status_registry();
|
|
@@ -17649,7 +17723,7 @@ var init_sql = __esm({
|
|
|
17649
17723
|
return new SQL([new StringChunk(str)]);
|
|
17650
17724
|
}
|
|
17651
17725
|
_sql.raw = raw;
|
|
17652
|
-
function
|
|
17726
|
+
function join31(chunks, separator) {
|
|
17653
17727
|
const result = [];
|
|
17654
17728
|
for (const [i, chunk] of chunks.entries()) {
|
|
17655
17729
|
if (i > 0 && separator !== void 0) result.push(separator);
|
|
@@ -17657,7 +17731,7 @@ var init_sql = __esm({
|
|
|
17657
17731
|
}
|
|
17658
17732
|
return new SQL(result);
|
|
17659
17733
|
}
|
|
17660
|
-
_sql.join =
|
|
17734
|
+
_sql.join = join31;
|
|
17661
17735
|
function identifier(value) {
|
|
17662
17736
|
return new Name(value);
|
|
17663
17737
|
}
|
|
@@ -20044,7 +20118,7 @@ var init_select2 = __esm({
|
|
|
20044
20118
|
const baseTableName = this.tableName;
|
|
20045
20119
|
const tableName = getTableLikeName(table);
|
|
20046
20120
|
for (const item of extractUsedTable(table)) this.usedTables.add(item);
|
|
20047
|
-
if (typeof tableName === "string" && this.config.joins?.some((
|
|
20121
|
+
if (typeof tableName === "string" && this.config.joins?.some((join31) => join31.alias === tableName)) throw new Error(`Alias "${tableName}" is already used in this query`);
|
|
20048
20122
|
if (!this.isPartialSelect) {
|
|
20049
20123
|
if (Object.keys(this.joinsNotNullableMap).length === 1 && typeof baseTableName === "string") this.config.fields = { [baseTableName]: this.config.fields };
|
|
20050
20124
|
if (typeof tableName === "string" && !is(table, SQL)) {
|
|
@@ -23854,7 +23928,7 @@ var init_dialect = __esm({
|
|
|
23854
23928
|
if (!joins2) return;
|
|
23855
23929
|
const withEntries = Object.entries(joins2).filter(([_, v]) => v);
|
|
23856
23930
|
if (!withEntries.length) return;
|
|
23857
|
-
return sql.join(withEntries.map(([k,
|
|
23931
|
+
return sql.join(withEntries.map(([k, join31]) => {
|
|
23858
23932
|
const relation = tableConfig.relations[k];
|
|
23859
23933
|
const isSingle2 = is(relation, One);
|
|
23860
23934
|
const targetTable = aliasedTable(relation.targetTable, `d${currentDepth + 1}`);
|
|
@@ -23865,7 +23939,7 @@ var init_dialect = __esm({
|
|
|
23865
23939
|
table: targetTable,
|
|
23866
23940
|
mode: isSingle2 ? "first" : "many",
|
|
23867
23941
|
schema,
|
|
23868
|
-
queryConfig:
|
|
23942
|
+
queryConfig: join31,
|
|
23869
23943
|
tableConfig: schema[relation.targetTableName],
|
|
23870
23944
|
relationWhere: filter2,
|
|
23871
23945
|
isNested: true,
|
|
@@ -23879,7 +23953,7 @@ var init_dialect = __esm({
|
|
|
23879
23953
|
key: k,
|
|
23880
23954
|
selection: innerQuery.selection,
|
|
23881
23955
|
isArray: !isSingle2,
|
|
23882
|
-
isOptional: (relation.optional ?? false) ||
|
|
23956
|
+
isOptional: (relation.optional ?? false) || join31 !== true && !!join31.where
|
|
23883
23957
|
});
|
|
23884
23958
|
const jsonColumns = sql.join(innerQuery.selection.map((s) => {
|
|
23885
23959
|
return sql`${sql.raw(this.escapeString(s.key))}, ${s.selection ? sql`${jsonb3}(${sql.identifier(s.key)})` : sql.identifier(s.key)}`;
|
|
@@ -24700,7 +24774,7 @@ var init_update = __esm({
|
|
|
24700
24774
|
createJoin(joinType) {
|
|
24701
24775
|
return ((table, on) => {
|
|
24702
24776
|
const tableName = getTableLikeName(table);
|
|
24703
|
-
if (typeof tableName === "string" && this.config.joins.some((
|
|
24777
|
+
if (typeof tableName === "string" && this.config.joins.some((join31) => join31.alias === tableName)) throw new Error(`Alias "${tableName}" is already used in this query`);
|
|
24704
24778
|
if (typeof on === "function") {
|
|
24705
24779
|
const from = this.config.from ? is(table, SQLiteTable) ? table[Table.Symbol.Columns] : is(table, Subquery) ? table._.selectedFields : is(table, SQLiteViewBase) ? table[ViewBaseConfig].selectedFields : void 0 : void 0;
|
|
24706
24780
|
on = on(new Proxy(this.config.table[Table.Symbol.Columns], new SelectionProxyHandler({
|
|
@@ -25798,8 +25872,8 @@ function probeAndMarkApplied(nativeDb, migration, logSubsystem, eliminatedTables
|
|
|
25798
25872
|
});
|
|
25799
25873
|
if (allAltersPresent && allTablesPresent && allIndexesPresent && allTriggersPresent) {
|
|
25800
25874
|
insertJournalEntry(nativeDb, migration.hash, migration.folderMillis, migration.name ?? "");
|
|
25801
|
-
const
|
|
25802
|
-
|
|
25875
|
+
const log9 = getLogger(logSubsystem);
|
|
25876
|
+
log9.debug(
|
|
25803
25877
|
{
|
|
25804
25878
|
migration: migration.name,
|
|
25805
25879
|
alters: alterTargets.length,
|
|
@@ -25868,14 +25942,14 @@ function reconcileJournal(nativeDb, migrationsFolder, existenceTable2, logSubsys
|
|
|
25868
25942
|
if (hasOrphanedEntries) {
|
|
25869
25943
|
const dbHashes = new Set(dbEntries.map((e) => e.hash));
|
|
25870
25944
|
const allLocalHashesPresentInDb = localMigrations.every((m) => dbHashes.has(m.hash));
|
|
25871
|
-
const
|
|
25945
|
+
const log9 = getLogger(logSubsystem);
|
|
25872
25946
|
if (allLocalHashesPresentInDb) {
|
|
25873
|
-
|
|
25947
|
+
log9.debug(
|
|
25874
25948
|
{ extra: orphanedEntries.length },
|
|
25875
25949
|
`Migration journal has ${orphanedEntries.length} entries for migrations not known to this install (DB is ahead). Skipping reconciliation.`
|
|
25876
25950
|
);
|
|
25877
25951
|
} else {
|
|
25878
|
-
|
|
25952
|
+
log9.warn(
|
|
25879
25953
|
{ orphaned: orphanedEntries.length },
|
|
25880
25954
|
`Detected ${orphanedEntries.length} true-orphan journal entries from a previous CLEO lineage. Reconciling via DDL probe.`
|
|
25881
25955
|
);
|
|
@@ -25911,8 +25985,8 @@ function reconcileJournal(nativeDb, migrationsFolder, existenceTable2, logSubsys
|
|
|
25911
25985
|
if (alterMatches.length === 0) {
|
|
25912
25986
|
const stripped = stripSqlComments(fullSql).trim();
|
|
25913
25987
|
if (stripped === "") {
|
|
25914
|
-
const
|
|
25915
|
-
|
|
25988
|
+
const log9 = getLogger(logSubsystem);
|
|
25989
|
+
log9.debug(
|
|
25916
25990
|
{ migration: migration.name },
|
|
25917
25991
|
`Migration ${migration.name} is a comment-only baseline marker \u2014 marking applied on existing DB.`
|
|
25918
25992
|
);
|
|
@@ -25951,8 +26025,8 @@ function reconcileJournal(nativeDb, migrationsFolder, existenceTable2, logSubsys
|
|
|
25951
26025
|
}
|
|
25952
26026
|
}
|
|
25953
26027
|
if (missingColumns.length === 0) {
|
|
25954
|
-
const
|
|
25955
|
-
|
|
26028
|
+
const log9 = getLogger(logSubsystem);
|
|
26029
|
+
log9.warn(
|
|
25956
26030
|
{ migration: migration.name, columns: alterMatches },
|
|
25957
26031
|
`Detected partially-applied migration ${migration.name} \u2014 columns exist but journal entry missing. Auto-reconciling.`
|
|
25958
26032
|
);
|
|
@@ -25960,8 +26034,8 @@ function reconcileJournal(nativeDb, migrationsFolder, existenceTable2, logSubsys
|
|
|
25960
26034
|
continue;
|
|
25961
26035
|
}
|
|
25962
26036
|
if (existingColumns.length > 0 && missingColumns.length > 0) {
|
|
25963
|
-
const
|
|
25964
|
-
|
|
26037
|
+
const log9 = getLogger(logSubsystem);
|
|
26038
|
+
log9.warn(
|
|
25965
26039
|
{
|
|
25966
26040
|
migration: migration.name,
|
|
25967
26041
|
existingColumns: existingColumns.map((c) => `${c.table}.${c.column}`),
|
|
@@ -25973,12 +26047,12 @@ function reconcileJournal(nativeDb, migrationsFolder, existenceTable2, logSubsys
|
|
|
25973
26047
|
if (!tableExists(nativeDb, table)) continue;
|
|
25974
26048
|
try {
|
|
25975
26049
|
nativeDb.exec(`ALTER TABLE ${table} ADD COLUMN ${column}${ddl ? ` ${ddl}` : ""}`);
|
|
25976
|
-
|
|
26050
|
+
log9.warn(
|
|
25977
26051
|
{ migration: migration.name, table, column },
|
|
25978
26052
|
`T920: Added missing column ${table}.${column} to complete partial migration.`
|
|
25979
26053
|
);
|
|
25980
26054
|
} catch {
|
|
25981
|
-
|
|
26055
|
+
log9.warn(
|
|
25982
26056
|
{ migration: migration.name, table, column },
|
|
25983
26057
|
`T920: Could not add missing column ${table}.${column} \u2014 will let Drizzle migrate() handle it.`
|
|
25984
26058
|
);
|
|
@@ -25998,8 +26072,8 @@ function reconcileJournal(nativeDb, migrationsFolder, existenceTable2, logSubsys
|
|
|
25998
26072
|
for (const entry of unnamedEntries) {
|
|
25999
26073
|
const migrationName = hashToName.get(entry.hash);
|
|
26000
26074
|
if (!migrationName) continue;
|
|
26001
|
-
const
|
|
26002
|
-
|
|
26075
|
+
const log9 = getLogger(logSubsystem);
|
|
26076
|
+
log9.debug(
|
|
26003
26077
|
{ id: entry.id, hash: entry.hash, name: migrationName },
|
|
26004
26078
|
`Backfilling missing name on journal entry id=${entry.id} (Drizzle v1 beta legacy compat).`
|
|
26005
26079
|
);
|
|
@@ -26083,13 +26157,13 @@ function ensureColumns(nativeDb, tableName, requiredColumns, logSubsystem, conte
|
|
|
26083
26157
|
const existingCols = new Set(columns.map((c) => c.name));
|
|
26084
26158
|
for (const req of requiredColumns) {
|
|
26085
26159
|
if (!existingCols.has(req.name)) {
|
|
26086
|
-
const
|
|
26160
|
+
const log9 = getLogger(logSubsystem);
|
|
26087
26161
|
const message = `Adding missing column ${tableName}.${req.name} via ALTER TABLE`;
|
|
26088
26162
|
const fields = { column: req.name, context };
|
|
26089
26163
|
if (context === "fresh") {
|
|
26090
|
-
|
|
26164
|
+
log9.error(fields, `${message} \u2014 MIGRATION DEFECT (fresh DB should not need repair)`);
|
|
26091
26165
|
} else {
|
|
26092
|
-
|
|
26166
|
+
log9.warn(fields, message);
|
|
26093
26167
|
}
|
|
26094
26168
|
nativeDb.exec(`ALTER TABLE ${tableName} ADD COLUMN ${req.name} ${req.ddl}`);
|
|
26095
26169
|
}
|
|
@@ -35170,11 +35244,750 @@ var init_cleo_global = __esm({
|
|
|
35170
35244
|
}
|
|
35171
35245
|
});
|
|
35172
35246
|
|
|
35173
|
-
// packages/core/src/
|
|
35247
|
+
// packages/core/src/resources/linux-backend.ts
|
|
35248
|
+
import { readFile as readFile2, stat } from "node:fs/promises";
|
|
35249
|
+
function parsePressureLine(line2) {
|
|
35250
|
+
const avg10Match = /avg10=([\d.]+)/.exec(line2);
|
|
35251
|
+
const avg60Match = /avg60=([\d.]+)/.exec(line2);
|
|
35252
|
+
const avg300Match = /avg300=([\d.]+)/.exec(line2);
|
|
35253
|
+
const totalMatch = /total=(\d+)/.exec(line2);
|
|
35254
|
+
if (!avg10Match?.[1] || !avg60Match?.[1] || !avg300Match?.[1] || !totalMatch?.[1]) {
|
|
35255
|
+
return null;
|
|
35256
|
+
}
|
|
35257
|
+
return {
|
|
35258
|
+
avg10: parseFloat(avg10Match[1]),
|
|
35259
|
+
avg60: parseFloat(avg60Match[1]),
|
|
35260
|
+
avg300: parseFloat(avg300Match[1]),
|
|
35261
|
+
totalUs: parseInt(totalMatch[1], 10)
|
|
35262
|
+
};
|
|
35263
|
+
}
|
|
35264
|
+
function parsePsiFile(content) {
|
|
35265
|
+
const lines = content.split("\n").map((l) => l.trim()).filter(Boolean);
|
|
35266
|
+
let some = null;
|
|
35267
|
+
let full = null;
|
|
35268
|
+
for (const line2 of lines) {
|
|
35269
|
+
if (line2.startsWith("some ")) {
|
|
35270
|
+
some = parsePressureLine(line2);
|
|
35271
|
+
} else if (line2.startsWith("full ")) {
|
|
35272
|
+
full = parsePressureLine(line2);
|
|
35273
|
+
}
|
|
35274
|
+
}
|
|
35275
|
+
if (!some) {
|
|
35276
|
+
return null;
|
|
35277
|
+
}
|
|
35278
|
+
return { some, full };
|
|
35279
|
+
}
|
|
35280
|
+
function parseMemAvailable(content) {
|
|
35281
|
+
for (const line2 of content.split("\n")) {
|
|
35282
|
+
if (line2.startsWith("MemAvailable:")) {
|
|
35283
|
+
const match = /MemAvailable:\s+(\d+)\s+kB/.exec(line2);
|
|
35284
|
+
if (match?.[1]) {
|
|
35285
|
+
return parseInt(match[1], 10) * 1024;
|
|
35286
|
+
}
|
|
35287
|
+
}
|
|
35288
|
+
}
|
|
35289
|
+
return null;
|
|
35290
|
+
}
|
|
35291
|
+
function parseSmapsRollup(content) {
|
|
35292
|
+
let rssKb = null;
|
|
35293
|
+
let pssKb = null;
|
|
35294
|
+
for (const line2 of content.split("\n")) {
|
|
35295
|
+
if (line2.startsWith("Rss:")) {
|
|
35296
|
+
const m = /Rss:\s+(\d+)\s+kB/.exec(line2);
|
|
35297
|
+
if (m?.[1]) rssKb = parseInt(m[1], 10);
|
|
35298
|
+
} else if (line2.startsWith("Pss:")) {
|
|
35299
|
+
const m = /Pss:\s+(\d+)\s+kB/.exec(line2);
|
|
35300
|
+
if (m?.[1]) pssKb = parseInt(m[1], 10);
|
|
35301
|
+
}
|
|
35302
|
+
}
|
|
35303
|
+
if (rssKb === null || pssKb === null) return null;
|
|
35304
|
+
return { rssBytes: rssKb * 1024, pssBytes: pssKb * 1024 };
|
|
35305
|
+
}
|
|
35306
|
+
var defaultReadFile, defaultStat, LinuxResourceBackend;
|
|
35307
|
+
var init_linux_backend = __esm({
|
|
35308
|
+
"packages/core/src/resources/linux-backend.ts"() {
|
|
35309
|
+
"use strict";
|
|
35310
|
+
defaultReadFile = (path, encoding) => readFile2(path, encoding);
|
|
35311
|
+
defaultStat = async (path) => {
|
|
35312
|
+
try {
|
|
35313
|
+
const s = await stat(path);
|
|
35314
|
+
return { size: s.size };
|
|
35315
|
+
} catch {
|
|
35316
|
+
return null;
|
|
35317
|
+
}
|
|
35318
|
+
};
|
|
35319
|
+
LinuxResourceBackend = class {
|
|
35320
|
+
globalPressurePath;
|
|
35321
|
+
cgroupSlicePressurePath;
|
|
35322
|
+
walPaths;
|
|
35323
|
+
readFileFn;
|
|
35324
|
+
statFileFn;
|
|
35325
|
+
constructor(opts = {}) {
|
|
35326
|
+
this.globalPressurePath = opts.globalPressurePath ?? "/proc/pressure/memory";
|
|
35327
|
+
this.cgroupSlicePressurePath = opts.cgroupSlicePressurePath ?? null;
|
|
35328
|
+
this.walPaths = opts.walPaths ?? [];
|
|
35329
|
+
this.readFileFn = opts.readFileFn ?? defaultReadFile;
|
|
35330
|
+
this.statFileFn = opts.statFileFn ?? defaultStat;
|
|
35331
|
+
}
|
|
35332
|
+
/**
|
|
35333
|
+
* Take a bounded point-in-time resource sample.
|
|
35334
|
+
*
|
|
35335
|
+
* Read-count (bounded by Amendment 1):
|
|
35336
|
+
* - 1 read: `/proc/pressure/memory`
|
|
35337
|
+
* - 0–1 reads: slice `memory.pressure` (if configured)
|
|
35338
|
+
* - 1 read: `/proc/meminfo`
|
|
35339
|
+
* - N stat calls: one per WAL path in `walPaths`
|
|
35340
|
+
*
|
|
35341
|
+
* Never spawns a child process.
|
|
35342
|
+
*/
|
|
35343
|
+
async sample() {
|
|
35344
|
+
const sampledAtMs = Date.now();
|
|
35345
|
+
const globalPressure = await this._readPsi(this.globalPressurePath);
|
|
35346
|
+
const pressureAvailable = globalPressure !== null;
|
|
35347
|
+
let slicePressure = null;
|
|
35348
|
+
if (this.cgroupSlicePressurePath) {
|
|
35349
|
+
slicePressure = await this._readPsi(this.cgroupSlicePressurePath);
|
|
35350
|
+
}
|
|
35351
|
+
const memAvailableBytes = await this._readMemAvailable();
|
|
35352
|
+
const walObservations = await this._readWalObservations();
|
|
35353
|
+
return {
|
|
35354
|
+
sampledAtMs,
|
|
35355
|
+
pressureAvailable,
|
|
35356
|
+
memAvailableBytes,
|
|
35357
|
+
globalPressure,
|
|
35358
|
+
slicePressure,
|
|
35359
|
+
walObservations
|
|
35360
|
+
};
|
|
35361
|
+
}
|
|
35362
|
+
/**
|
|
35363
|
+
* Sweep per-child RSS via `/proc/<pid>/smaps_rollup`.
|
|
35364
|
+
*
|
|
35365
|
+
* **STRUCTURALLY SEPARATED from sample()** — smaps_rollup reads are
|
|
35366
|
+
* ms-scale per multi-GB process and must never appear in the hot path.
|
|
35367
|
+
* Call on a separate low-frequency cadence (e.g. every 60s or on
|
|
35368
|
+
* backoff→hold state transition).
|
|
35369
|
+
*
|
|
35370
|
+
* @param pids - PIDs to sweep. Dead/unknown PIDs are silently skipped.
|
|
35371
|
+
*/
|
|
35372
|
+
async sweepChildRss(pids) {
|
|
35373
|
+
const sampledAtMs = Date.now();
|
|
35374
|
+
const entries = [];
|
|
35375
|
+
for (const pid of pids) {
|
|
35376
|
+
try {
|
|
35377
|
+
const content = await this.readFileFn(`/proc/${pid}/smaps_rollup`, "utf-8");
|
|
35378
|
+
const parsed = parseSmapsRollup(content);
|
|
35379
|
+
if (parsed) {
|
|
35380
|
+
entries.push({ pid, ...parsed });
|
|
35381
|
+
}
|
|
35382
|
+
} catch {
|
|
35383
|
+
}
|
|
35384
|
+
}
|
|
35385
|
+
return { sampledAtMs, entries };
|
|
35386
|
+
}
|
|
35387
|
+
// -------------------------------------------------------------------------
|
|
35388
|
+
// Private helpers
|
|
35389
|
+
// -------------------------------------------------------------------------
|
|
35390
|
+
async _readPsi(path) {
|
|
35391
|
+
try {
|
|
35392
|
+
const content = await this.readFileFn(path, "utf-8");
|
|
35393
|
+
return parsePsiFile(content);
|
|
35394
|
+
} catch {
|
|
35395
|
+
return null;
|
|
35396
|
+
}
|
|
35397
|
+
}
|
|
35398
|
+
async _readMemAvailable() {
|
|
35399
|
+
try {
|
|
35400
|
+
const content = await this.readFileFn("/proc/meminfo", "utf-8");
|
|
35401
|
+
return parseMemAvailable(content);
|
|
35402
|
+
} catch {
|
|
35403
|
+
return null;
|
|
35404
|
+
}
|
|
35405
|
+
}
|
|
35406
|
+
async _readWalObservations() {
|
|
35407
|
+
const results = [];
|
|
35408
|
+
for (const walPath of this.walPaths) {
|
|
35409
|
+
const info = await this.statFileFn(walPath);
|
|
35410
|
+
results.push({ walPath, sizeBytes: info?.size ?? null });
|
|
35411
|
+
}
|
|
35412
|
+
return results;
|
|
35413
|
+
}
|
|
35414
|
+
};
|
|
35415
|
+
}
|
|
35416
|
+
});
|
|
35417
|
+
|
|
35418
|
+
// packages/core/src/resources/monitor.ts
|
|
35419
|
+
import { EventEmitter as EventEmitter2 } from "node:events";
|
|
35420
|
+
function resolveThresholds(cfg) {
|
|
35421
|
+
return {
|
|
35422
|
+
holdSomeAvg10: cfg.psi?.holdSomeAvg10 ?? DEFAULTS.holdSomeAvg10,
|
|
35423
|
+
backoffSomeAvg10: cfg.psi?.backoffSomeAvg10 ?? DEFAULTS.backoffSomeAvg10,
|
|
35424
|
+
holdFullAvg10: cfg.psi?.holdFullAvg10 ?? DEFAULTS.holdFullAvg10,
|
|
35425
|
+
backoffFullAvg10: cfg.psi?.backoffFullAvg10 ?? DEFAULTS.backoffFullAvg10,
|
|
35426
|
+
hysteresisPoints: cfg.hysteresisPoints ?? DEFAULTS.hysteresisPoints,
|
|
35427
|
+
headroomBytes: cfg.headroomBytes ?? DEFAULTS.headroomBytes,
|
|
35428
|
+
walWarnThresholdBytes: cfg.walWarnThresholdBytes ?? DEFAULTS.walWarnThresholdBytes,
|
|
35429
|
+
pollIntervalMs: cfg.pollIntervalMs ?? DEFAULTS.pollIntervalMs
|
|
35430
|
+
};
|
|
35431
|
+
}
|
|
35432
|
+
function evaluateState(sample, thresholds, currentState) {
|
|
35433
|
+
if (!sample.pressureAvailable) {
|
|
35434
|
+
if (sample.memAvailableBytes !== null && sample.memAvailableBytes < thresholds.headroomBytes) {
|
|
35435
|
+
return { state: "hold", reason: "degraded: memAvailable below headroom" };
|
|
35436
|
+
}
|
|
35437
|
+
return { state: "ok", reason: "degraded: PSI unavailable, memAvailable sufficient" };
|
|
35438
|
+
}
|
|
35439
|
+
const somePressure = sample.globalPressure?.some ?? sample.slicePressure?.some;
|
|
35440
|
+
const fullPressure = sample.globalPressure?.full ?? sample.slicePressure?.full;
|
|
35441
|
+
const someAvg102 = somePressure?.avg10 ?? 0;
|
|
35442
|
+
const fullAvg10 = fullPressure?.avg10 ?? 0;
|
|
35443
|
+
if (someAvg102 > thresholds.backoffSomeAvg10 || fullAvg10 > thresholds.backoffFullAvg10) {
|
|
35444
|
+
return {
|
|
35445
|
+
state: "backoff",
|
|
35446
|
+
reason: `some avg10=${someAvg102.toFixed(2)} full avg10=${fullAvg10.toFixed(2)} \u2014 above backoff threshold`
|
|
35447
|
+
};
|
|
35448
|
+
}
|
|
35449
|
+
if (someAvg102 > thresholds.holdSomeAvg10 || fullAvg10 > thresholds.holdFullAvg10) {
|
|
35450
|
+
return {
|
|
35451
|
+
state: "hold",
|
|
35452
|
+
reason: `some avg10=${someAvg102.toFixed(2)} full avg10=${fullAvg10.toFixed(2)} \u2014 above hold threshold`
|
|
35453
|
+
};
|
|
35454
|
+
}
|
|
35455
|
+
if (currentState !== "ok") {
|
|
35456
|
+
const hysteresisFloor = thresholds.holdSomeAvg10 - thresholds.hysteresisPoints;
|
|
35457
|
+
const fullHysteresisFloor = thresholds.holdFullAvg10 - thresholds.hysteresisPoints;
|
|
35458
|
+
if (someAvg102 > hysteresisFloor || fullAvg10 > fullHysteresisFloor) {
|
|
35459
|
+
const clamped = currentState === "backoff" ? "hold" : currentState;
|
|
35460
|
+
return {
|
|
35461
|
+
state: clamped,
|
|
35462
|
+
reason: `some avg10=${someAvg102.toFixed(2)} full avg10=${fullAvg10.toFixed(2)} \u2014 hysteresis hold (floor: some ${hysteresisFloor}, full ${fullHysteresisFloor})`
|
|
35463
|
+
};
|
|
35464
|
+
}
|
|
35465
|
+
}
|
|
35466
|
+
return {
|
|
35467
|
+
state: "ok",
|
|
35468
|
+
reason: `some avg10=${someAvg102.toFixed(2)} full avg10=${fullAvg10.toFixed(2)} \u2014 below thresholds`
|
|
35469
|
+
};
|
|
35470
|
+
}
|
|
35471
|
+
var DEFAULTS, ResourceMonitor;
|
|
35472
|
+
var init_monitor = __esm({
|
|
35473
|
+
"packages/core/src/resources/monitor.ts"() {
|
|
35474
|
+
"use strict";
|
|
35475
|
+
init_linux_backend();
|
|
35476
|
+
DEFAULTS = {
|
|
35477
|
+
holdSomeAvg10: 10,
|
|
35478
|
+
backoffSomeAvg10: 20,
|
|
35479
|
+
holdFullAvg10: 5,
|
|
35480
|
+
backoffFullAvg10: 10,
|
|
35481
|
+
hysteresisPoints: 3,
|
|
35482
|
+
headroomBytes: 256 * 1024 * 1024,
|
|
35483
|
+
walWarnThresholdBytes: 256 * 1024 * 1024,
|
|
35484
|
+
pollIntervalMs: 1500
|
|
35485
|
+
};
|
|
35486
|
+
ResourceMonitor = class extends EventEmitter2 {
|
|
35487
|
+
thresholds;
|
|
35488
|
+
backend;
|
|
35489
|
+
_state = "ok";
|
|
35490
|
+
_timer = null;
|
|
35491
|
+
_running = false;
|
|
35492
|
+
constructor(cfg = {}) {
|
|
35493
|
+
super();
|
|
35494
|
+
this.thresholds = resolveThresholds(cfg);
|
|
35495
|
+
this.backend = cfg.backend ?? new LinuxResourceBackend();
|
|
35496
|
+
}
|
|
35497
|
+
// -------------------------------------------------------------------------
|
|
35498
|
+
// Public API
|
|
35499
|
+
// -------------------------------------------------------------------------
|
|
35500
|
+
/**
|
|
35501
|
+
* Current pressure state (only meaningful after at least one sample).
|
|
35502
|
+
*/
|
|
35503
|
+
get state() {
|
|
35504
|
+
return this._state;
|
|
35505
|
+
}
|
|
35506
|
+
/**
|
|
35507
|
+
* `true` while the continuous loop is running.
|
|
35508
|
+
*/
|
|
35509
|
+
get running() {
|
|
35510
|
+
return this._running;
|
|
35511
|
+
}
|
|
35512
|
+
/**
|
|
35513
|
+
* Take a single point-in-time resource sample.
|
|
35514
|
+
*
|
|
35515
|
+
* Safe to call with the daemon off (no child processes, bounded reads).
|
|
35516
|
+
* Does NOT update `this.state` — use {@link startContinuous} for that.
|
|
35517
|
+
*/
|
|
35518
|
+
async sample() {
|
|
35519
|
+
return this.backend.sample();
|
|
35520
|
+
}
|
|
35521
|
+
/**
|
|
35522
|
+
* Start the continuous polling loop.
|
|
35523
|
+
*
|
|
35524
|
+
* Returns a `stop()` function. Calling `stop()` is idempotent.
|
|
35525
|
+
*
|
|
35526
|
+
* Emits:
|
|
35527
|
+
* - `'sample'` on every poll (even if state did not change)
|
|
35528
|
+
* - `'transition'` when `PressureState` changes
|
|
35529
|
+
* - `'error'` if the backend throws (loop continues regardless)
|
|
35530
|
+
*/
|
|
35531
|
+
startContinuous() {
|
|
35532
|
+
if (this._running) {
|
|
35533
|
+
return () => this._stopLoop();
|
|
35534
|
+
}
|
|
35535
|
+
this._running = true;
|
|
35536
|
+
this._scheduleNext();
|
|
35537
|
+
return () => this._stopLoop();
|
|
35538
|
+
}
|
|
35539
|
+
/**
|
|
35540
|
+
* Stop the continuous polling loop. Idempotent.
|
|
35541
|
+
*/
|
|
35542
|
+
stop() {
|
|
35543
|
+
this._stopLoop();
|
|
35544
|
+
}
|
|
35545
|
+
// -------------------------------------------------------------------------
|
|
35546
|
+
// Private loop machinery
|
|
35547
|
+
// -------------------------------------------------------------------------
|
|
35548
|
+
_scheduleNext() {
|
|
35549
|
+
this._timer = setTimeout(() => {
|
|
35550
|
+
void this._tick();
|
|
35551
|
+
}, this.thresholds.pollIntervalMs);
|
|
35552
|
+
if (typeof this._timer.unref === "function") {
|
|
35553
|
+
this._timer.unref();
|
|
35554
|
+
}
|
|
35555
|
+
}
|
|
35556
|
+
async _tick() {
|
|
35557
|
+
if (!this._running) return;
|
|
35558
|
+
try {
|
|
35559
|
+
const sample = await this.backend.sample();
|
|
35560
|
+
this.emit("sample", sample);
|
|
35561
|
+
const { state: newState, reason } = evaluateState(sample, this.thresholds, this._state);
|
|
35562
|
+
if (newState !== this._state) {
|
|
35563
|
+
const transition = {
|
|
35564
|
+
from: this._state,
|
|
35565
|
+
to: newState,
|
|
35566
|
+
sample,
|
|
35567
|
+
reason
|
|
35568
|
+
};
|
|
35569
|
+
this._state = newState;
|
|
35570
|
+
this.emit("transition", transition);
|
|
35571
|
+
}
|
|
35572
|
+
} catch (err) {
|
|
35573
|
+
this.emit("error", err instanceof Error ? err : new Error(String(err)));
|
|
35574
|
+
}
|
|
35575
|
+
if (this._running) {
|
|
35576
|
+
this._scheduleNext();
|
|
35577
|
+
}
|
|
35578
|
+
}
|
|
35579
|
+
_stopLoop() {
|
|
35580
|
+
this._running = false;
|
|
35581
|
+
if (this._timer !== null) {
|
|
35582
|
+
clearTimeout(this._timer);
|
|
35583
|
+
this._timer = null;
|
|
35584
|
+
}
|
|
35585
|
+
}
|
|
35586
|
+
};
|
|
35587
|
+
}
|
|
35588
|
+
});
|
|
35589
|
+
|
|
35590
|
+
// packages/core/src/resources/supervisor-admit.ts
|
|
35591
|
+
import { connect } from "node:net";
|
|
35592
|
+
import { join as join5 } from "node:path";
|
|
35593
|
+
import { getCleoHome as getCleoHome2 } from "@cleocode/paths";
|
|
35594
|
+
function resolveSupervisorSocketPath() {
|
|
35595
|
+
return process.env.CLEO_SUPERVISOR_SOCKET ?? join5(getCleoHome2(), SUPERVISOR_SOCKET_NAME);
|
|
35596
|
+
}
|
|
35597
|
+
async function sendResourceAdmit(socketPath, cls, holderId, budget) {
|
|
35598
|
+
const result = await sendLeaseRequest(
|
|
35599
|
+
socketPath,
|
|
35600
|
+
{ kind: "resource_admit", class: cls, holder_id: holderId, budget },
|
|
35601
|
+
"resource_admit_result"
|
|
35602
|
+
);
|
|
35603
|
+
if ("unavailable" in result) return result;
|
|
35604
|
+
if (result.kind === "resource_admit_result") return result;
|
|
35605
|
+
return { unavailable: true, reason: `unexpected response kind: ${result.kind}` };
|
|
35606
|
+
}
|
|
35607
|
+
async function sendResourceRelease(socketPath, cls, holderId) {
|
|
35608
|
+
const result = await sendLeaseRequest(
|
|
35609
|
+
socketPath,
|
|
35610
|
+
{ kind: "resource_release", class: cls, holder_id: holderId },
|
|
35611
|
+
"resource_release_result"
|
|
35612
|
+
);
|
|
35613
|
+
if ("unavailable" in result) return result;
|
|
35614
|
+
if (result.kind === "resource_release_result") return result;
|
|
35615
|
+
return { unavailable: true, reason: `unexpected response kind: ${result.kind}` };
|
|
35616
|
+
}
|
|
35617
|
+
function sendLeaseRequest(socketPath, request, expectedKind) {
|
|
35618
|
+
return new Promise((resolve5) => {
|
|
35619
|
+
const id = `ra-${process.pid}-${request.kind}`;
|
|
35620
|
+
const envelope = LeaseIpcRequestEnvelopeSchema.parse({
|
|
35621
|
+
protocol_version: LEASE_IPC_PROTOCOL_VERSION,
|
|
35622
|
+
id,
|
|
35623
|
+
direction: "request",
|
|
35624
|
+
request
|
|
35625
|
+
});
|
|
35626
|
+
const line2 = `${JSON.stringify(envelope)}
|
|
35627
|
+
`;
|
|
35628
|
+
let settled = false;
|
|
35629
|
+
let buffer = "";
|
|
35630
|
+
const socket = connect(socketPath);
|
|
35631
|
+
const done = (value) => {
|
|
35632
|
+
if (settled) return;
|
|
35633
|
+
settled = true;
|
|
35634
|
+
socket.removeAllListeners();
|
|
35635
|
+
socket.destroy();
|
|
35636
|
+
resolve5(value);
|
|
35637
|
+
};
|
|
35638
|
+
const degrade = (reason) => done({ unavailable: true, reason });
|
|
35639
|
+
socket.setTimeout(REQUEST_TIMEOUT_MS);
|
|
35640
|
+
const connectTimer = setTimeout(
|
|
35641
|
+
() => degrade("supervisor connect timeout"),
|
|
35642
|
+
CONNECT_TIMEOUT_MS
|
|
35643
|
+
);
|
|
35644
|
+
socket.on("connect", () => {
|
|
35645
|
+
clearTimeout(connectTimer);
|
|
35646
|
+
socket.write(line2);
|
|
35647
|
+
});
|
|
35648
|
+
socket.on("timeout", () => degrade("supervisor request timeout"));
|
|
35649
|
+
socket.on("error", (err) => {
|
|
35650
|
+
clearTimeout(connectTimer);
|
|
35651
|
+
degrade(err.message);
|
|
35652
|
+
});
|
|
35653
|
+
socket.on("close", () => {
|
|
35654
|
+
if (!settled) degrade("supervisor closed the connection before responding");
|
|
35655
|
+
});
|
|
35656
|
+
socket.on("data", (chunk) => {
|
|
35657
|
+
buffer += chunk.toString("utf8");
|
|
35658
|
+
const nl = buffer.indexOf("\n");
|
|
35659
|
+
if (nl < 0) return;
|
|
35660
|
+
let json3;
|
|
35661
|
+
try {
|
|
35662
|
+
json3 = JSON.parse(buffer.slice(0, nl));
|
|
35663
|
+
} catch (cause) {
|
|
35664
|
+
degrade(`malformed lease-ipc frame: ${cause instanceof Error ? cause.message : cause}`);
|
|
35665
|
+
return;
|
|
35666
|
+
}
|
|
35667
|
+
const parsed = LeaseIpcResponseEnvelopeSchema.safeParse(json3);
|
|
35668
|
+
if (!parsed.success) {
|
|
35669
|
+
degrade(`off-contract lease-ipc response: ${parsed.error.message}`);
|
|
35670
|
+
return;
|
|
35671
|
+
}
|
|
35672
|
+
const { response } = parsed.data;
|
|
35673
|
+
if (response.kind === expectedKind) {
|
|
35674
|
+
done(response);
|
|
35675
|
+
return;
|
|
35676
|
+
}
|
|
35677
|
+
if (response.kind === "error") {
|
|
35678
|
+
degrade(`${response.code}: ${response.message}`);
|
|
35679
|
+
return;
|
|
35680
|
+
}
|
|
35681
|
+
degrade(`unexpected lease-ipc response kind for ${request.kind}: ${response.kind}`);
|
|
35682
|
+
});
|
|
35683
|
+
});
|
|
35684
|
+
}
|
|
35685
|
+
var SUPERVISOR_SOCKET_NAME, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS;
|
|
35686
|
+
var init_supervisor_admit = __esm({
|
|
35687
|
+
"packages/core/src/resources/supervisor-admit.ts"() {
|
|
35688
|
+
"use strict";
|
|
35689
|
+
init_src();
|
|
35690
|
+
SUPERVISOR_SOCKET_NAME = "cleo-supervisor.sock";
|
|
35691
|
+
CONNECT_TIMEOUT_MS = 250;
|
|
35692
|
+
REQUEST_TIMEOUT_MS = 2e3;
|
|
35693
|
+
}
|
|
35694
|
+
});
|
|
35695
|
+
|
|
35696
|
+
// packages/core/src/resources/governor.ts
|
|
35697
|
+
var governor_exports = {};
|
|
35698
|
+
__export(governor_exports, {
|
|
35699
|
+
ResourceGovernor: () => ResourceGovernor,
|
|
35700
|
+
_resetGovernorStateForTest: () => _resetGovernorStateForTest,
|
|
35701
|
+
computeClassBudget: () => computeClassBudget,
|
|
35702
|
+
governor: () => governor,
|
|
35703
|
+
governorSlotDir: () => governorSlotDir,
|
|
35704
|
+
resolveGovernorMode: () => resolveGovernorMode,
|
|
35705
|
+
slotFileCount: () => slotFileCount
|
|
35706
|
+
});
|
|
35707
|
+
import { existsSync as existsSync3, mkdirSync, readdirSync as readdirSync2, writeFileSync } from "node:fs";
|
|
35708
|
+
import { availableParallelism, totalmem } from "node:os";
|
|
35709
|
+
import { join as join6 } from "node:path";
|
|
35174
35710
|
import lockfile from "proper-lockfile";
|
|
35711
|
+
function log2() {
|
|
35712
|
+
if (_log2 === null) _log2 = getLogger("resource-governor");
|
|
35713
|
+
return _log2;
|
|
35714
|
+
}
|
|
35715
|
+
function resolveGovernorMode() {
|
|
35716
|
+
if (_cachedMode2 !== null) return _cachedMode2;
|
|
35717
|
+
const raw = process.env.CLEO_RESOURCES_MODE;
|
|
35718
|
+
_cachedMode2 = raw === "supervisor" || raw === "local" || raw === "off" ? raw : "local";
|
|
35719
|
+
return _cachedMode2;
|
|
35720
|
+
}
|
|
35721
|
+
function logSupervisorDegradeOnce(reason) {
|
|
35722
|
+
if (_supervisorDegradeLogged) return;
|
|
35723
|
+
_supervisorDegradeLogged = true;
|
|
35724
|
+
log2().info(
|
|
35725
|
+
`CLEO_RESOURCES_MODE=supervisor but the arbiter is unreachable (${reason}); degrading to local-mode admission for the process lifetime.`
|
|
35726
|
+
);
|
|
35727
|
+
}
|
|
35728
|
+
function _resetGovernorStateForTest() {
|
|
35729
|
+
_cachedMode2 = null;
|
|
35730
|
+
_supervisorDegradeLogged = false;
|
|
35731
|
+
}
|
|
35732
|
+
function someAvg10(sample) {
|
|
35733
|
+
const some = sample.globalPressure?.some ?? sample.slicePressure?.some;
|
|
35734
|
+
return some?.avg10 ?? 0;
|
|
35735
|
+
}
|
|
35736
|
+
function computeClassBudget(cls, sample, opts = {}) {
|
|
35737
|
+
if (cls === "interactive-cli") return Number.POSITIVE_INFINITY;
|
|
35738
|
+
const cpus = Math.max(1, opts.cpuCount ?? availableParallelism());
|
|
35739
|
+
const totalBytes = opts.totalMemBytes ?? totalmem();
|
|
35740
|
+
const headroomBytes = (opts.headroomMb ?? 2048) * MB;
|
|
35741
|
+
const hold = opts.holdSomeAvg10 ?? 10;
|
|
35742
|
+
const floor = opts.floorSomeAvg10 ?? 25;
|
|
35743
|
+
const some = someAvg10(sample);
|
|
35744
|
+
const availBytes = sample.memAvailableBytes ?? totalBytes;
|
|
35745
|
+
const fullStall = sample.globalPressure?.full?.avg10 ?? sample.slicePressure?.full?.avg10 ?? 0;
|
|
35746
|
+
const backoff = some > floor || fullStall > 10;
|
|
35747
|
+
switch (cls) {
|
|
35748
|
+
case "full-build":
|
|
35749
|
+
return 1;
|
|
35750
|
+
case "agent-session": {
|
|
35751
|
+
const estRamBytes = (opts.agentEstRamMb ?? 4096) * MB;
|
|
35752
|
+
const byMem = Math.floor((availBytes - headroomBytes) / estRamBytes);
|
|
35753
|
+
return clamp(1, byMem, Math.max(1, cpus - 2));
|
|
35754
|
+
}
|
|
35755
|
+
case "llm-call":
|
|
35756
|
+
return Math.max(1, cpus - 2);
|
|
35757
|
+
case "test-run":
|
|
35758
|
+
case "scoped-build": {
|
|
35759
|
+
const base = Math.max(1, Math.floor(cpus / 4));
|
|
35760
|
+
if (some > floor) return 1;
|
|
35761
|
+
if (some > hold) return Math.max(1, Math.floor(base / 2));
|
|
35762
|
+
return base;
|
|
35763
|
+
}
|
|
35764
|
+
case "db-heavy":
|
|
35765
|
+
return backoff ? 0 : 1;
|
|
35766
|
+
case "background-autonomous":
|
|
35767
|
+
return some > hold || fullStall > 5 ? 0 : 1;
|
|
35768
|
+
default:
|
|
35769
|
+
return 1;
|
|
35770
|
+
}
|
|
35771
|
+
}
|
|
35772
|
+
function clamp(lo, v, hi) {
|
|
35773
|
+
return Math.max(lo, Math.min(v, hi));
|
|
35774
|
+
}
|
|
35775
|
+
function governorSlotDir(cls) {
|
|
35776
|
+
return join6(getCleoHome(), "locks", `resource-${cls}`);
|
|
35777
|
+
}
|
|
35778
|
+
function ensureSlotFiles(dir, count) {
|
|
35779
|
+
if (!existsSync3(dir)) mkdirSync(dir, { recursive: true });
|
|
35780
|
+
const paths = [];
|
|
35781
|
+
for (let i = 0; i < count; i++) {
|
|
35782
|
+
const p = join6(dir, `slot-${i}.lock`);
|
|
35783
|
+
if (!existsSync3(p)) writeFileSync(p, "", { flag: "a" });
|
|
35784
|
+
paths.push(p);
|
|
35785
|
+
}
|
|
35786
|
+
return paths;
|
|
35787
|
+
}
|
|
35788
|
+
function sleep(ms) {
|
|
35789
|
+
return new Promise((r) => setTimeout(r, ms));
|
|
35790
|
+
}
|
|
35791
|
+
function passThroughGrant(cls) {
|
|
35792
|
+
return {
|
|
35793
|
+
deferred: false,
|
|
35794
|
+
class: cls,
|
|
35795
|
+
slot: -1,
|
|
35796
|
+
acquiredAtMs: Date.now(),
|
|
35797
|
+
release: async () => {
|
|
35798
|
+
}
|
|
35799
|
+
};
|
|
35800
|
+
}
|
|
35801
|
+
function deferral(cls, reason, retryAfterMs) {
|
|
35802
|
+
return { deferred: true, class: cls, retryAfterMs, reason };
|
|
35803
|
+
}
|
|
35804
|
+
function supervisorHolderId(cls) {
|
|
35805
|
+
_supervisorHolderSeq += 1;
|
|
35806
|
+
return `${process.pid}:${cls}:${_supervisorHolderSeq}`;
|
|
35807
|
+
}
|
|
35808
|
+
function shuffledIndices(n) {
|
|
35809
|
+
const order = Array.from({ length: n }, (_, i) => i);
|
|
35810
|
+
for (let i = order.length - 1; i > 0; i--) {
|
|
35811
|
+
const j = Math.floor(Math.random() * (i + 1));
|
|
35812
|
+
const a = order[i];
|
|
35813
|
+
const b = order[j];
|
|
35814
|
+
if (a !== void 0 && b !== void 0) {
|
|
35815
|
+
order[i] = b;
|
|
35816
|
+
order[j] = a;
|
|
35817
|
+
}
|
|
35818
|
+
}
|
|
35819
|
+
return order;
|
|
35820
|
+
}
|
|
35821
|
+
async function countHeldSlots(cls, budget) {
|
|
35822
|
+
const dir = governorSlotDir(cls);
|
|
35823
|
+
if (!existsSync3(dir)) return 0;
|
|
35824
|
+
const slots = ensureSlotFiles(dir, budget);
|
|
35825
|
+
let held = 0;
|
|
35826
|
+
for (const path of slots) {
|
|
35827
|
+
try {
|
|
35828
|
+
const release = await lockfile.lock(path, { retries: 0, stale: STALE_MS, realpath: false });
|
|
35829
|
+
await release();
|
|
35830
|
+
} catch {
|
|
35831
|
+
held++;
|
|
35832
|
+
}
|
|
35833
|
+
}
|
|
35834
|
+
return held;
|
|
35835
|
+
}
|
|
35836
|
+
function slotFileCount(cls) {
|
|
35837
|
+
const dir = governorSlotDir(cls);
|
|
35838
|
+
if (!existsSync3(dir)) return 0;
|
|
35839
|
+
return readdirSync2(dir).filter((f) => f.startsWith("slot-") && f.endsWith(".lock")).length;
|
|
35840
|
+
}
|
|
35841
|
+
var _log2, _cachedMode2, _supervisorDegradeLogged, MB, STALE_MS, _supervisorHolderSeq, ResourceGovernor, governor;
|
|
35842
|
+
var init_governor = __esm({
|
|
35843
|
+
"packages/core/src/resources/governor.ts"() {
|
|
35844
|
+
"use strict";
|
|
35845
|
+
init_src();
|
|
35846
|
+
init_logger2();
|
|
35847
|
+
init_paths();
|
|
35848
|
+
init_monitor();
|
|
35849
|
+
init_supervisor_admit();
|
|
35850
|
+
_log2 = null;
|
|
35851
|
+
_cachedMode2 = null;
|
|
35852
|
+
_supervisorDegradeLogged = false;
|
|
35853
|
+
MB = 1024 * 1024;
|
|
35854
|
+
STALE_MS = 6e5;
|
|
35855
|
+
_supervisorHolderSeq = 0;
|
|
35856
|
+
ResourceGovernor = class {
|
|
35857
|
+
/**
|
|
35858
|
+
* Acquire one slot of `cls`. Returns a {@link ResourceGrant} on success or a
|
|
35859
|
+
* {@link ResourceDeferral} on denial. Never throws for admission control;
|
|
35860
|
+
* only genuinely unexpected I/O errors propagate.
|
|
35861
|
+
*/
|
|
35862
|
+
async acquire(cls, opts = {}) {
|
|
35863
|
+
const mode = resolveGovernorMode();
|
|
35864
|
+
if (mode === "off" || cls === "interactive-cli") {
|
|
35865
|
+
return passThroughGrant(cls);
|
|
35866
|
+
}
|
|
35867
|
+
const sample = opts.sample ?? await (opts.monitor ?? new ResourceMonitor()).sample();
|
|
35868
|
+
const budget = computeClassBudget(cls, sample, opts);
|
|
35869
|
+
if (!Number.isFinite(budget)) return passThroughGrant(cls);
|
|
35870
|
+
if (budget <= 0) {
|
|
35871
|
+
return deferral(
|
|
35872
|
+
cls,
|
|
35873
|
+
`class '${cls}' budget is 0 under current pressure (some avg10=${someAvg10(sample).toFixed(1)})`,
|
|
35874
|
+
DEFAULT_RESOURCE_RETRY_AFTER_MS
|
|
35875
|
+
);
|
|
35876
|
+
}
|
|
35877
|
+
if (mode === "supervisor") {
|
|
35878
|
+
const viaSupervisor = await this.acquireViaSupervisor(cls, Math.floor(budget));
|
|
35879
|
+
if (viaSupervisor !== null) return viaSupervisor;
|
|
35880
|
+
}
|
|
35881
|
+
const dir = governorSlotDir(cls);
|
|
35882
|
+
const slots = ensureSlotFiles(dir, budget);
|
|
35883
|
+
const blocking = opts.blocking ?? true;
|
|
35884
|
+
const timeoutMs = opts.timeoutMs ?? 36e5;
|
|
35885
|
+
const pollMs = opts.pollMs ?? 200;
|
|
35886
|
+
const startedAt = Date.now();
|
|
35887
|
+
do {
|
|
35888
|
+
const order = shuffledIndices(slots.length);
|
|
35889
|
+
for (const idx of order) {
|
|
35890
|
+
const path = slots[idx];
|
|
35891
|
+
if (!path) continue;
|
|
35892
|
+
try {
|
|
35893
|
+
const release = await lockfile.lock(path, {
|
|
35894
|
+
retries: 0,
|
|
35895
|
+
stale: STALE_MS,
|
|
35896
|
+
realpath: false
|
|
35897
|
+
});
|
|
35898
|
+
let released = false;
|
|
35899
|
+
return {
|
|
35900
|
+
deferred: false,
|
|
35901
|
+
class: cls,
|
|
35902
|
+
slot: idx,
|
|
35903
|
+
acquiredAtMs: Date.now(),
|
|
35904
|
+
release: async () => {
|
|
35905
|
+
if (released) return;
|
|
35906
|
+
released = true;
|
|
35907
|
+
try {
|
|
35908
|
+
await release();
|
|
35909
|
+
} catch {
|
|
35910
|
+
}
|
|
35911
|
+
}
|
|
35912
|
+
};
|
|
35913
|
+
} catch {
|
|
35914
|
+
}
|
|
35915
|
+
}
|
|
35916
|
+
if (!blocking) break;
|
|
35917
|
+
await sleep(pollMs);
|
|
35918
|
+
} while (Date.now() - startedAt < timeoutMs);
|
|
35919
|
+
return deferral(
|
|
35920
|
+
cls,
|
|
35921
|
+
`class '${cls}' is at capacity (${budget} slot(s)); ` + (blocking ? `timed out after ${timeoutMs}ms` : "no slot free"),
|
|
35922
|
+
Math.min(pollMs * 4, DEFAULT_RESOURCE_RETRY_AFTER_MS)
|
|
35923
|
+
);
|
|
35924
|
+
}
|
|
35925
|
+
/**
|
|
35926
|
+
* Route an admission through the supervisor's central `resource_admit` verb.
|
|
35927
|
+
* Returns a grant (whose `release` calls `resource_release`) or a deferral, or
|
|
35928
|
+
* `null` when the supervisor is unreachable so the caller degrades to the
|
|
35929
|
+
* local slot engine. Never throws — a dead arbiter never deadlocks work.
|
|
35930
|
+
*
|
|
35931
|
+
* @task T12001
|
|
35932
|
+
*/
|
|
35933
|
+
async acquireViaSupervisor(cls, budget) {
|
|
35934
|
+
const socketPath = resolveSupervisorSocketPath();
|
|
35935
|
+
const holderId = supervisorHolderId(cls);
|
|
35936
|
+
const reply = await sendResourceAdmit(socketPath, cls, holderId, budget);
|
|
35937
|
+
if ("unavailable" in reply) {
|
|
35938
|
+
logSupervisorDegradeOnce(reply.reason);
|
|
35939
|
+
return null;
|
|
35940
|
+
}
|
|
35941
|
+
if (reply.disposition === "deferred") {
|
|
35942
|
+
return deferral(
|
|
35943
|
+
cls,
|
|
35944
|
+
`class '${cls}' deferred by supervisor (budget ${budget})`,
|
|
35945
|
+
reply.retry_after_ms || DEFAULT_RESOURCE_RETRY_AFTER_MS
|
|
35946
|
+
);
|
|
35947
|
+
}
|
|
35948
|
+
let released = false;
|
|
35949
|
+
return {
|
|
35950
|
+
deferred: false,
|
|
35951
|
+
class: cls,
|
|
35952
|
+
slot: 0,
|
|
35953
|
+
acquiredAtMs: Date.now(),
|
|
35954
|
+
release: async () => {
|
|
35955
|
+
if (released) return;
|
|
35956
|
+
released = true;
|
|
35957
|
+
await sendResourceRelease(socketPath, cls, holderId).catch(() => {
|
|
35958
|
+
});
|
|
35959
|
+
}
|
|
35960
|
+
};
|
|
35961
|
+
}
|
|
35962
|
+
/** Non-blocking single-pass acquire (admission semantics). */
|
|
35963
|
+
async tryAcquire(cls, opts = {}) {
|
|
35964
|
+
return this.acquire(cls, { ...opts, blocking: false });
|
|
35965
|
+
}
|
|
35966
|
+
/**
|
|
35967
|
+
* Currently-grantable slot count for `cls` = budget − held. Held is the count
|
|
35968
|
+
* of slot files currently locked. `Infinity` for ungated classes.
|
|
35969
|
+
*/
|
|
35970
|
+
async available(cls, opts = {}) {
|
|
35971
|
+
if (resolveGovernorMode() === "off" || cls === "interactive-cli") {
|
|
35972
|
+
return Number.POSITIVE_INFINITY;
|
|
35973
|
+
}
|
|
35974
|
+
const sample = opts.sample ?? await (opts.monitor ?? new ResourceMonitor()).sample();
|
|
35975
|
+
const budget = computeClassBudget(cls, sample, opts);
|
|
35976
|
+
if (!Number.isFinite(budget)) return Number.POSITIVE_INFINITY;
|
|
35977
|
+
if (budget <= 0) return 0;
|
|
35978
|
+
const held = await countHeldSlots(cls, budget);
|
|
35979
|
+
return Math.max(0, budget - held);
|
|
35980
|
+
}
|
|
35981
|
+
};
|
|
35982
|
+
governor = new ResourceGovernor();
|
|
35983
|
+
}
|
|
35984
|
+
});
|
|
35985
|
+
|
|
35986
|
+
// packages/core/src/store/lock.ts
|
|
35987
|
+
import lockfile2 from "proper-lockfile";
|
|
35175
35988
|
async function acquireLock(filePath, options) {
|
|
35176
35989
|
try {
|
|
35177
|
-
const release = await
|
|
35990
|
+
const release = await lockfile2.lock(filePath, {
|
|
35178
35991
|
...DEFAULT_LOCK_OPTIONS,
|
|
35179
35992
|
...options?.stale !== void 0 && { stale: options.stale },
|
|
35180
35993
|
...options?.retries !== void 0 && {
|
|
@@ -35220,7 +36033,7 @@ var init_lock = __esm({
|
|
|
35220
36033
|
});
|
|
35221
36034
|
|
|
35222
36035
|
// packages/core/src/store/atomic.ts
|
|
35223
|
-
import { mkdir, readFile as
|
|
36036
|
+
import { mkdir, readFile as readFile3, rename, unlink } from "node:fs/promises";
|
|
35224
36037
|
import { dirname as dirname3 } from "node:path";
|
|
35225
36038
|
import writeFileAtomic from "write-file-atomic";
|
|
35226
36039
|
async function atomicWrite(filePath, data, options) {
|
|
@@ -35236,7 +36049,7 @@ async function atomicWrite(filePath, data, options) {
|
|
|
35236
36049
|
}
|
|
35237
36050
|
async function safeReadFile(filePath) {
|
|
35238
36051
|
try {
|
|
35239
|
-
return await
|
|
36052
|
+
return await readFile3(filePath, "utf8");
|
|
35240
36053
|
} catch (err) {
|
|
35241
36054
|
if (err instanceof Error && "code" in err && err.code === "ENOENT") {
|
|
35242
36055
|
return null;
|
|
@@ -35257,34 +36070,34 @@ var init_atomic = __esm({
|
|
|
35257
36070
|
});
|
|
35258
36071
|
|
|
35259
36072
|
// packages/core/src/store/backup.ts
|
|
35260
|
-
import { copyFile, rename as fsRename, mkdir as mkdir2, readdir, stat, unlink as unlink2 } from "node:fs/promises";
|
|
35261
|
-
import { basename, join as
|
|
36073
|
+
import { copyFile, rename as fsRename, mkdir as mkdir2, readdir, stat as stat2, unlink as unlink2 } from "node:fs/promises";
|
|
36074
|
+
import { basename, join as join7 } from "node:path";
|
|
35262
36075
|
async function createBackup(filePath, backupDir, maxBackups = DEFAULT_MAX_BACKUPS) {
|
|
35263
36076
|
try {
|
|
35264
36077
|
await mkdir2(backupDir, { recursive: true });
|
|
35265
36078
|
const fileName = basename(filePath);
|
|
35266
36079
|
try {
|
|
35267
|
-
await
|
|
36080
|
+
await stat2(filePath);
|
|
35268
36081
|
} catch {
|
|
35269
36082
|
throw new CleoError(3 /* FILE_ERROR */, `Cannot backup: source file not found: ${filePath}`);
|
|
35270
36083
|
}
|
|
35271
36084
|
for (let i = maxBackups; i >= 1; i--) {
|
|
35272
|
-
const current =
|
|
36085
|
+
const current = join7(backupDir, `${fileName}.${i}`);
|
|
35273
36086
|
if (i === maxBackups) {
|
|
35274
36087
|
try {
|
|
35275
36088
|
await unlink2(current);
|
|
35276
36089
|
} catch {
|
|
35277
36090
|
}
|
|
35278
36091
|
} else {
|
|
35279
|
-
const next =
|
|
36092
|
+
const next = join7(backupDir, `${fileName}.${i + 1}`);
|
|
35280
36093
|
try {
|
|
35281
|
-
await
|
|
36094
|
+
await stat2(current);
|
|
35282
36095
|
await fsRename(current, next);
|
|
35283
36096
|
} catch {
|
|
35284
36097
|
}
|
|
35285
36098
|
}
|
|
35286
36099
|
}
|
|
35287
|
-
const backupPath =
|
|
36100
|
+
const backupPath = join7(backupDir, `${fileName}.1`);
|
|
35288
36101
|
await copyFile(filePath, backupPath);
|
|
35289
36102
|
return backupPath;
|
|
35290
36103
|
} catch (err) {
|
|
@@ -35563,8 +36376,8 @@ var init_json2 = __esm({
|
|
|
35563
36376
|
});
|
|
35564
36377
|
|
|
35565
36378
|
// packages/core/src/scaffold/ensure-config.ts
|
|
35566
|
-
import { existsSync as
|
|
35567
|
-
import { basename as basename2, dirname as dirname4, join as
|
|
36379
|
+
import { existsSync as existsSync4, readFileSync } from "node:fs";
|
|
36380
|
+
import { basename as basename2, dirname as dirname4, join as join8, resolve as resolve2 } from "node:path";
|
|
35568
36381
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
35569
36382
|
function getPackageRoot() {
|
|
35570
36383
|
const thisFile = fileURLToPath2(import.meta.url);
|
|
@@ -35572,7 +36385,7 @@ function getPackageRoot() {
|
|
|
35572
36385
|
}
|
|
35573
36386
|
function getCleoVersion() {
|
|
35574
36387
|
try {
|
|
35575
|
-
const pkgPath =
|
|
36388
|
+
const pkgPath = join8(getPackageRoot(), "package.json");
|
|
35576
36389
|
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
35577
36390
|
return pkg.version ?? "0.0.0";
|
|
35578
36391
|
} catch {
|
|
@@ -35591,25 +36404,25 @@ var init_ensure_config = __esm({
|
|
|
35591
36404
|
// packages/core/src/store/exodus/archive.ts
|
|
35592
36405
|
import {
|
|
35593
36406
|
copyFileSync as copyFileSync2,
|
|
35594
|
-
existsSync as
|
|
35595
|
-
mkdirSync,
|
|
36407
|
+
existsSync as existsSync5,
|
|
36408
|
+
mkdirSync as mkdirSync2,
|
|
35596
36409
|
renameSync,
|
|
35597
36410
|
unlinkSync,
|
|
35598
|
-
writeFileSync
|
|
36411
|
+
writeFileSync as writeFileSync2
|
|
35599
36412
|
} from "node:fs";
|
|
35600
|
-
import { basename as basename3, join as
|
|
36413
|
+
import { basename as basename3, join as join9 } from "node:path";
|
|
35601
36414
|
function scopeBaseDir(scope, cwd) {
|
|
35602
36415
|
return scope === "project" ? resolveCleoDir(cwd) : getCleoHome();
|
|
35603
36416
|
}
|
|
35604
36417
|
function exodusArchiveDir(scope, cwd) {
|
|
35605
|
-
return
|
|
36418
|
+
return join9(scopeBaseDir(scope, cwd), ARCHIVE_DIR_NAME);
|
|
35606
36419
|
}
|
|
35607
36420
|
function exodusMarkerPath(scope, cwd) {
|
|
35608
|
-
return
|
|
36421
|
+
return join9(scopeBaseDir(scope, cwd), MARKER_FILENAME_BY_SCOPE[scope]);
|
|
35609
36422
|
}
|
|
35610
36423
|
function hasExodusCompleteMarker(scope, cwd) {
|
|
35611
36424
|
try {
|
|
35612
|
-
return
|
|
36425
|
+
return existsSync5(exodusMarkerPath(scope, cwd));
|
|
35613
36426
|
} catch {
|
|
35614
36427
|
return false;
|
|
35615
36428
|
}
|
|
@@ -35617,7 +36430,7 @@ function hasExodusCompleteMarker(scope, cwd) {
|
|
|
35617
36430
|
function writeExodusCompleteMarker(scope, archivedSources, cwd) {
|
|
35618
36431
|
const markerPath = exodusMarkerPath(scope, cwd);
|
|
35619
36432
|
const baseDir = scopeBaseDir(scope, cwd);
|
|
35620
|
-
|
|
36433
|
+
mkdirSync2(baseDir, { recursive: true });
|
|
35621
36434
|
const marker16 = {
|
|
35622
36435
|
version: 1,
|
|
35623
36436
|
scope,
|
|
@@ -35626,17 +36439,17 @@ function writeExodusCompleteMarker(scope, archivedSources, cwd) {
|
|
|
35626
36439
|
archivedSources: [...archivedSources]
|
|
35627
36440
|
};
|
|
35628
36441
|
const tmpPath = `${markerPath}.tmp`;
|
|
35629
|
-
|
|
36442
|
+
writeFileSync2(tmpPath, JSON.stringify(marker16, null, 2) + "\n", "utf8");
|
|
35630
36443
|
renameSync(tmpPath, markerPath);
|
|
35631
|
-
|
|
36444
|
+
log3.info({ scope, markerPath, archivedSources }, "exodus: wrote completion marker");
|
|
35632
36445
|
return markerPath;
|
|
35633
36446
|
}
|
|
35634
36447
|
function moveFileInto(srcPath, destDir) {
|
|
35635
|
-
|
|
35636
|
-
let dest =
|
|
35637
|
-
if (
|
|
36448
|
+
mkdirSync2(destDir, { recursive: true });
|
|
36449
|
+
let dest = join9(destDir, basename3(srcPath));
|
|
36450
|
+
if (existsSync5(dest)) {
|
|
35638
36451
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "").replace(/Z$/, "Z");
|
|
35639
|
-
dest =
|
|
36452
|
+
dest = join9(destDir, `${basename3(srcPath)}.${stamp}`);
|
|
35640
36453
|
}
|
|
35641
36454
|
try {
|
|
35642
36455
|
renameSync(srcPath, dest);
|
|
@@ -35647,25 +36460,25 @@ function moveFileInto(srcPath, destDir) {
|
|
|
35647
36460
|
return dest;
|
|
35648
36461
|
}
|
|
35649
36462
|
function archiveSourceDb(source, cwd) {
|
|
35650
|
-
if (!
|
|
36463
|
+
if (!existsSync5(source.path)) {
|
|
35651
36464
|
return { name: source.name, sourcePath: source.path, archivedTo: null, action: "absent" };
|
|
35652
36465
|
}
|
|
35653
36466
|
const destDir = exodusArchiveDir(source.targetScope, cwd);
|
|
35654
36467
|
const archivedTo = moveFileInto(source.path, destDir);
|
|
35655
36468
|
for (const suffix of SIDECAR_SUFFIXES) {
|
|
35656
36469
|
const sidecar = `${source.path}${suffix}`;
|
|
35657
|
-
if (
|
|
36470
|
+
if (existsSync5(sidecar)) {
|
|
35658
36471
|
try {
|
|
35659
36472
|
moveFileInto(sidecar, destDir);
|
|
35660
36473
|
} catch (err) {
|
|
35661
|
-
|
|
36474
|
+
log3.warn(
|
|
35662
36475
|
{ err, sidecar, sourceName: source.name },
|
|
35663
36476
|
"exodus-archive: failed to archive sidecar (non-fatal)"
|
|
35664
36477
|
);
|
|
35665
36478
|
}
|
|
35666
36479
|
}
|
|
35667
36480
|
}
|
|
35668
|
-
|
|
36481
|
+
log3.info(
|
|
35669
36482
|
{ sourceName: source.name, sourcePath: source.path, archivedTo, scope: source.targetScope },
|
|
35670
36483
|
"exodus-archive: archived legacy source DB"
|
|
35671
36484
|
);
|
|
@@ -35700,7 +36513,7 @@ function detectStrandedResidue(sources, cwd) {
|
|
|
35700
36513
|
const stranded = [];
|
|
35701
36514
|
for (const source of sources) {
|
|
35702
36515
|
if (!markedScopes.has(source.targetScope)) continue;
|
|
35703
|
-
if (
|
|
36516
|
+
if (existsSync5(source.path)) {
|
|
35704
36517
|
stranded.push({ name: source.name, path: source.path, scope: source.targetScope });
|
|
35705
36518
|
}
|
|
35706
36519
|
}
|
|
@@ -35716,14 +36529,14 @@ function archiveStrandedResidue(stranded, sources, cwd) {
|
|
|
35716
36529
|
}
|
|
35717
36530
|
return results;
|
|
35718
36531
|
}
|
|
35719
|
-
var
|
|
36532
|
+
var log3, ARCHIVE_DIR_NAME, MARKER_FILENAME_BY_SCOPE, SIDECAR_SUFFIXES;
|
|
35720
36533
|
var init_archive2 = __esm({
|
|
35721
36534
|
"packages/core/src/store/exodus/archive.ts"() {
|
|
35722
36535
|
"use strict";
|
|
35723
36536
|
init_logger2();
|
|
35724
36537
|
init_paths();
|
|
35725
36538
|
init_ensure_config();
|
|
35726
|
-
|
|
36539
|
+
log3 = getLogger("exodus-archive");
|
|
35727
36540
|
ARCHIVE_DIR_NAME = "_archive";
|
|
35728
36541
|
MARKER_FILENAME_BY_SCOPE = {
|
|
35729
36542
|
project: "exodus-complete",
|
|
@@ -36117,7 +36930,7 @@ var init_table_name_map = __esm({
|
|
|
36117
36930
|
});
|
|
36118
36931
|
|
|
36119
36932
|
// packages/core/src/store/exodus/count-parity.ts
|
|
36120
|
-
import { existsSync as
|
|
36933
|
+
import { existsSync as existsSync6 } from "node:fs";
|
|
36121
36934
|
function listTables(db) {
|
|
36122
36935
|
return db.prepare(
|
|
36123
36936
|
"SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' AND name NOT LIKE '__drizzle_%' ORDER BY name"
|
|
@@ -36138,14 +36951,14 @@ function rowCount(db, tableName) {
|
|
|
36138
36951
|
function computeCountParity(sources, projectDbPath, globalDbPath) {
|
|
36139
36952
|
const entries = [];
|
|
36140
36953
|
let skipped = 0;
|
|
36141
|
-
if (!
|
|
36954
|
+
if (!existsSync6(projectDbPath) || !existsSync6(globalDbPath)) {
|
|
36142
36955
|
return { ok: false, entries: [], deficits: [], checked: 0, skipped: 0 };
|
|
36143
36956
|
}
|
|
36144
36957
|
const projectSnap = openCleoDbSnapshot(projectDbPath, { readOnly: true });
|
|
36145
36958
|
const globalSnap = openCleoDbSnapshot(globalDbPath, { readOnly: true });
|
|
36146
36959
|
try {
|
|
36147
36960
|
for (const src of sources) {
|
|
36148
|
-
if (!
|
|
36961
|
+
if (!existsSync6(src.path)) continue;
|
|
36149
36962
|
const srcSnap = openCleoDbSnapshot(src.path, { readOnly: true });
|
|
36150
36963
|
try {
|
|
36151
36964
|
for (const legacyTable of listTables(srcSnap.db)) {
|
|
@@ -36184,27 +36997,27 @@ function computeCountParity(sources, projectDbPath, globalDbPath) {
|
|
|
36184
36997
|
}
|
|
36185
36998
|
const deficits = entries.filter((e) => e.deficit > 0);
|
|
36186
36999
|
if (deficits.length > 0) {
|
|
36187
|
-
|
|
37000
|
+
log4.warn(
|
|
36188
37001
|
{ deficitCount: deficits.length, sample: deficits.slice(0, 5) },
|
|
36189
37002
|
`exodus count-parity: ${deficits.length} table(s) have FEWER rows in the consolidated target than the legacy source`
|
|
36190
37003
|
);
|
|
36191
37004
|
}
|
|
36192
37005
|
return { ok: deficits.length === 0, entries, deficits, checked: entries.length, skipped };
|
|
36193
37006
|
}
|
|
36194
|
-
var
|
|
37007
|
+
var log4;
|
|
36195
37008
|
var init_count_parity = __esm({
|
|
36196
37009
|
"packages/core/src/store/exodus/count-parity.ts"() {
|
|
36197
37010
|
"use strict";
|
|
36198
37011
|
init_logger2();
|
|
36199
37012
|
init_open_cleo_db();
|
|
36200
37013
|
init_table_name_map();
|
|
36201
|
-
|
|
37014
|
+
log4 = getLogger("exodus-count-parity");
|
|
36202
37015
|
}
|
|
36203
37016
|
});
|
|
36204
37017
|
|
|
36205
37018
|
// packages/core/src/store/exodus/plan.ts
|
|
36206
|
-
import { existsSync as
|
|
36207
|
-
import { join as
|
|
37019
|
+
import { existsSync as existsSync7, readdirSync as readdirSync3, statfsSync, statSync } from "node:fs";
|
|
37020
|
+
import { join as join10 } from "node:path";
|
|
36208
37021
|
function computeRequiredBytes(totalSourceBytes, largestSourceBytes) {
|
|
36209
37022
|
return Math.ceil(STAGING_HEADROOM_FACTOR * largestSourceBytes) + totalSourceBytes;
|
|
36210
37023
|
}
|
|
@@ -36215,33 +37028,33 @@ function buildSourceDescriptors(cwd) {
|
|
|
36215
37028
|
// Project-tier — go into consolidated project-scope cleo.db
|
|
36216
37029
|
{
|
|
36217
37030
|
name: "tasks",
|
|
36218
|
-
path:
|
|
37031
|
+
path: join10(cleoDir, "tasks.db"),
|
|
36219
37032
|
targetScope: "project"
|
|
36220
37033
|
},
|
|
36221
37034
|
{
|
|
36222
37035
|
name: "brain (project)",
|
|
36223
|
-
path:
|
|
37036
|
+
path: join10(cleoDir, "brain.db"),
|
|
36224
37037
|
targetScope: "project"
|
|
36225
37038
|
},
|
|
36226
37039
|
{
|
|
36227
37040
|
name: "conduit",
|
|
36228
|
-
path:
|
|
37041
|
+
path: join10(cleoDir, "conduit.db"),
|
|
36229
37042
|
targetScope: "project"
|
|
36230
37043
|
},
|
|
36231
37044
|
// Global-tier — go into consolidated global-scope cleo.db
|
|
36232
37045
|
{
|
|
36233
37046
|
name: "nexus",
|
|
36234
|
-
path:
|
|
37047
|
+
path: join10(cleoHome, "nexus.db"),
|
|
36235
37048
|
targetScope: "global"
|
|
36236
37049
|
},
|
|
36237
37050
|
{
|
|
36238
37051
|
name: "signaldock",
|
|
36239
|
-
path:
|
|
37052
|
+
path: join10(cleoHome, "signaldock.db"),
|
|
36240
37053
|
targetScope: "global"
|
|
36241
37054
|
},
|
|
36242
37055
|
{
|
|
36243
37056
|
name: "skills",
|
|
36244
|
-
path:
|
|
37057
|
+
path: join10(cleoHome, "skills.db"),
|
|
36245
37058
|
targetScope: "global"
|
|
36246
37059
|
}
|
|
36247
37060
|
];
|
|
@@ -36267,10 +37080,10 @@ function deriveStagingDirName() {
|
|
|
36267
37080
|
}
|
|
36268
37081
|
function findExistingStaging(cleoDir) {
|
|
36269
37082
|
try {
|
|
36270
|
-
const entries =
|
|
37083
|
+
const entries = readdirSync3(cleoDir, { withFileTypes: true });
|
|
36271
37084
|
const stagingDirs = entries.filter((e) => e.isDirectory() && e.name.startsWith("exodus-staging-")).map((e) => e.name).sort().reverse();
|
|
36272
37085
|
if (stagingDirs.length > 0) {
|
|
36273
|
-
return
|
|
37086
|
+
return join10(cleoDir, stagingDirs[0]);
|
|
36274
37087
|
}
|
|
36275
37088
|
} catch {
|
|
36276
37089
|
}
|
|
@@ -36286,7 +37099,7 @@ function buildExodusPlan(cwd) {
|
|
|
36286
37099
|
const availableBytes = getAvailableBytes(cleoDir);
|
|
36287
37100
|
const diskPreflight = totalSourceBytes === 0 || availableBytes >= requiredBytes;
|
|
36288
37101
|
const existingStaging = findExistingStaging(cleoDir);
|
|
36289
|
-
const stagingDir = existingStaging ??
|
|
37102
|
+
const stagingDir = existingStaging ?? join10(cleoDir, deriveStagingDirName());
|
|
36290
37103
|
const resumeFromStaging = existingStaging !== null;
|
|
36291
37104
|
const projectDbPath = resolveDualScopeDbPath("project", cwd);
|
|
36292
37105
|
const globalDbPath = resolveDualScopeDbPath("global");
|
|
@@ -36305,7 +37118,7 @@ function buildExodusPlan(cwd) {
|
|
|
36305
37118
|
};
|
|
36306
37119
|
}
|
|
36307
37120
|
function sourcesPresent(sources) {
|
|
36308
|
-
return sources.some((s) =>
|
|
37121
|
+
return sources.some((s) => existsSync7(s.path));
|
|
36309
37122
|
}
|
|
36310
37123
|
var STAGING_HEADROOM_FACTOR, STAGING_COPY_SKIP_THRESHOLD_BYTES;
|
|
36311
37124
|
var init_plan2 = __esm({
|
|
@@ -36319,18 +37132,18 @@ var init_plan2 = __esm({
|
|
|
36319
37132
|
});
|
|
36320
37133
|
|
|
36321
37134
|
// packages/core/src/store/exodus/health.ts
|
|
36322
|
-
import { existsSync as
|
|
37135
|
+
import { existsSync as existsSync8, statSync as statSync2 } from "node:fs";
|
|
36323
37136
|
function buildExodusHealth(cwd) {
|
|
36324
37137
|
const plan = buildExodusPlan(cwd);
|
|
36325
|
-
const anyLegacyPresent = plan.sources.some((s) =>
|
|
37138
|
+
const anyLegacyPresent = plan.sources.some((s) => existsSync8(s.path));
|
|
36326
37139
|
const parity = anyLegacyPresent ? computeCountParity(plan.sources, plan.projectDbPath, plan.globalDbPath) : { ok: true, entries: [], deficits: [], checked: 0, skipped: 0 };
|
|
36327
37140
|
const consolidatedExistsByScope = {
|
|
36328
|
-
project:
|
|
36329
|
-
global:
|
|
37141
|
+
project: existsSync8(plan.projectDbPath),
|
|
37142
|
+
global: existsSync8(plan.globalDbPath)
|
|
36330
37143
|
};
|
|
36331
37144
|
const buildScope = (scope) => {
|
|
36332
37145
|
const sources = plan.sources.filter((s) => s.targetScope === scope).map((s) => {
|
|
36333
|
-
const present =
|
|
37146
|
+
const present = existsSync8(s.path);
|
|
36334
37147
|
let bytes = 0;
|
|
36335
37148
|
if (present) {
|
|
36336
37149
|
try {
|
|
@@ -36597,15 +37410,15 @@ var init_types = __esm({
|
|
|
36597
37410
|
// packages/core/src/store/exodus/migrate.ts
|
|
36598
37411
|
import {
|
|
36599
37412
|
copyFileSync as copyFileSync3,
|
|
36600
|
-
existsSync as
|
|
36601
|
-
mkdirSync as
|
|
37413
|
+
existsSync as existsSync9,
|
|
37414
|
+
mkdirSync as mkdirSync3,
|
|
36602
37415
|
readFileSync as readFileSync2,
|
|
36603
37416
|
renameSync as renameSync2,
|
|
36604
37417
|
statSync as statSync3,
|
|
36605
37418
|
unlinkSync as unlinkSync2,
|
|
36606
|
-
writeFileSync as
|
|
37419
|
+
writeFileSync as writeFileSync3
|
|
36607
37420
|
} from "node:fs";
|
|
36608
|
-
import { join as
|
|
37421
|
+
import { join as join11 } from "node:path";
|
|
36609
37422
|
function getSqliteVersion(db) {
|
|
36610
37423
|
try {
|
|
36611
37424
|
const row = db.prepare("SELECT sqlite_version() AS v").get();
|
|
@@ -36621,14 +37434,14 @@ function listTables2(db) {
|
|
|
36621
37434
|
return rows.map((r) => r.name);
|
|
36622
37435
|
}
|
|
36623
37436
|
function writeJournal(stagingDir, journal) {
|
|
36624
|
-
const journalPath =
|
|
37437
|
+
const journalPath = join11(stagingDir, JOURNAL_FILENAME);
|
|
36625
37438
|
const tmpPath = `${journalPath}.tmp`;
|
|
36626
|
-
|
|
37439
|
+
writeFileSync3(tmpPath, JSON.stringify(journal, null, 2) + "\n", "utf8");
|
|
36627
37440
|
renameSync2(tmpPath, journalPath);
|
|
36628
37441
|
}
|
|
36629
37442
|
function readJournal(stagingDir) {
|
|
36630
|
-
const journalPath =
|
|
36631
|
-
if (!
|
|
37443
|
+
const journalPath = join11(stagingDir, JOURNAL_FILENAME);
|
|
37444
|
+
if (!existsSync9(journalPath)) return null;
|
|
36632
37445
|
try {
|
|
36633
37446
|
return JSON.parse(readFileSync2(journalPath, "utf8"));
|
|
36634
37447
|
} catch {
|
|
@@ -36636,17 +37449,17 @@ function readJournal(stagingDir) {
|
|
|
36636
37449
|
}
|
|
36637
37450
|
}
|
|
36638
37451
|
function clearExodusJournal(stagingDir) {
|
|
36639
|
-
const journalPath =
|
|
37452
|
+
const journalPath = join11(stagingDir, JOURNAL_FILENAME);
|
|
36640
37453
|
try {
|
|
36641
|
-
if (!
|
|
37454
|
+
if (!existsSync9(journalPath)) return false;
|
|
36642
37455
|
unlinkSync2(journalPath);
|
|
36643
|
-
|
|
37456
|
+
log5.info(
|
|
36644
37457
|
{ stagingDir },
|
|
36645
37458
|
"exodus: cleared migrate journal after abort/rollback \u2014 a retry will RE-COPY all tables"
|
|
36646
37459
|
);
|
|
36647
37460
|
return true;
|
|
36648
37461
|
} catch (err) {
|
|
36649
|
-
|
|
37462
|
+
log5.warn(
|
|
36650
37463
|
{ err, stagingDir },
|
|
36651
37464
|
'exodus: failed to clear migrate journal after rollback (a retry may skip already-"done" tables)'
|
|
36652
37465
|
);
|
|
@@ -36671,7 +37484,7 @@ function lockPath(dbPath) {
|
|
|
36671
37484
|
}
|
|
36672
37485
|
function acquireAdvisoryLock(dbPath) {
|
|
36673
37486
|
const lp = lockPath(dbPath);
|
|
36674
|
-
|
|
37487
|
+
writeFileSync3(lp, JSON.stringify({ pid: process.pid, ts: (/* @__PURE__ */ new Date()).toISOString() }), "utf8");
|
|
36675
37488
|
}
|
|
36676
37489
|
function releaseAdvisoryLock(dbPath) {
|
|
36677
37490
|
try {
|
|
@@ -36734,7 +37547,7 @@ function buildSelectExpr(attachAlias, legacyTable, targetTableName, col, srcType
|
|
|
36734
37547
|
function copyTableFromAttached(targetNativeDb, srcNativeDb, attachAlias, legacyTableName, sourceName, targetSchema = "main") {
|
|
36735
37548
|
const resolution = resolveConsolidatedTableName(sourceName, legacyTableName);
|
|
36736
37549
|
if (resolution.kind === "skip") {
|
|
36737
|
-
|
|
37550
|
+
log5.warn(
|
|
36738
37551
|
{ legacyTableName, sourceName, reason: resolution.reason },
|
|
36739
37552
|
`Exodus: explicitly skipping table \u2014 ${resolution.reason}`
|
|
36740
37553
|
);
|
|
@@ -36753,7 +37566,7 @@ function copyTableFromAttached(targetNativeDb, srcNativeDb, attachAlias, legacyT
|
|
|
36753
37566
|
).get();
|
|
36754
37567
|
if (!existsRow) {
|
|
36755
37568
|
const reason = `consolidated target '${targetTableName}' not found (mapped from legacy '${legacyTableName}')`;
|
|
36756
|
-
|
|
37569
|
+
log5.warn(
|
|
36757
37570
|
{ legacyTableName, targetTableName, sourceName, attachAlias, targetSchema },
|
|
36758
37571
|
`Exodus: ${reason}`
|
|
36759
37572
|
);
|
|
@@ -36764,13 +37577,13 @@ function copyTableFromAttached(targetNativeDb, srcNativeDb, attachAlias, legacyT
|
|
|
36764
37577
|
const sharedColumns = srcPragma.map((r) => r.name).filter((col) => tgtColMap.has(col));
|
|
36765
37578
|
if (sharedColumns.length === 0) {
|
|
36766
37579
|
const reason = `no overlapping columns between source '${legacyTableName}' and target '${targetTableName}'`;
|
|
36767
|
-
|
|
37580
|
+
log5.warn({ legacyTableName, targetTableName, sourceName }, `Exodus: ${reason}`);
|
|
36768
37581
|
return { rowsCopied: 0, skipped: true, reason };
|
|
36769
37582
|
}
|
|
36770
37583
|
const srcOnlyColumns = srcPragma.map((r) => r.name).filter((c) => !tgtColMap.has(c));
|
|
36771
37584
|
const tgtOnlyColumns = tgtPragma.map((r) => r.name).filter((c) => !srcColumns.has(c));
|
|
36772
37585
|
if (srcOnlyColumns.length > 0 || tgtOnlyColumns.length > 0) {
|
|
36773
|
-
|
|
37586
|
+
log5.info(
|
|
36774
37587
|
{ legacyTableName, targetTableName, sourceName, srcOnlyColumns, tgtOnlyColumns },
|
|
36775
37588
|
"Exodus: column drift detected \u2014 copying intersection, dropping src-only cols, using defaults for tgt-only cols"
|
|
36776
37589
|
);
|
|
@@ -36785,7 +37598,7 @@ function copyTableFromAttached(targetNativeDb, srcNativeDb, attachAlias, legacyT
|
|
|
36785
37598
|
return isoGlobCols.has(col) && (upper.includes("INT") || upper === "" || upper === "NUMERIC");
|
|
36786
37599
|
});
|
|
36787
37600
|
if (coercedCols.length > 0) {
|
|
36788
|
-
|
|
37601
|
+
log5.info(
|
|
36789
37602
|
{
|
|
36790
37603
|
legacyTableName,
|
|
36791
37604
|
targetTableName,
|
|
@@ -36801,7 +37614,7 @@ function copyTableFromAttached(targetNativeDb, srcNativeDb, attachAlias, legacyT
|
|
|
36801
37614
|
(col) => ENUM_NORMALIZATIONS.has(`${targetTableName}.${col}`)
|
|
36802
37615
|
);
|
|
36803
37616
|
if (normalizedCols.length > 0) {
|
|
36804
|
-
|
|
37617
|
+
log5.info(
|
|
36805
37618
|
{ legacyTableName, targetTableName, sourceName, normalizedCols },
|
|
36806
37619
|
`Exodus: applying enum-value normalization for ${normalizedCols.length} column(s) (T11547)`
|
|
36807
37620
|
);
|
|
@@ -36810,7 +37623,7 @@ function copyTableFromAttached(targetNativeDb, srcNativeDb, attachAlias, legacyT
|
|
|
36810
37623
|
(col) => NUMERIC_CLAMPS.has(`${targetTableName}.${col}`)
|
|
36811
37624
|
);
|
|
36812
37625
|
if (clampedCols.length > 0) {
|
|
36813
|
-
|
|
37626
|
+
log5.info(
|
|
36814
37627
|
{ legacyTableName, targetTableName, sourceName, clampedCols },
|
|
36815
37628
|
`Exodus: applying non-finite numeric clamp for ${clampedCols.length} column(s) (T11782)`
|
|
36816
37629
|
);
|
|
@@ -36852,7 +37665,7 @@ function copyTableFromAttached(targetNativeDb, srcNativeDb, attachAlias, legacyT
|
|
|
36852
37665
|
if (rowsCopied < sourceCount) {
|
|
36853
37666
|
const presentAccountedFor = existingBefore + rowsCopied;
|
|
36854
37667
|
if (presentAccountedFor >= sourceCount) {
|
|
36855
|
-
|
|
37668
|
+
log5.info(
|
|
36856
37669
|
{ legacyTableName, targetTableName, sourceName, sourceCount, rowsCopied, existingBefore },
|
|
36857
37670
|
`Exodus: '${legacyTableName}'\u2192'${targetTableName}' \u2014 ${sourceCount - rowsCopied} of ${sourceCount} row(s) already present in target (idempotent PK dedup), ${rowsCopied} newly copied; no loss`
|
|
36858
37671
|
);
|
|
@@ -36860,7 +37673,7 @@ function copyTableFromAttached(targetNativeDb, srcNativeDb, attachAlias, legacyT
|
|
|
36860
37673
|
}
|
|
36861
37674
|
const missing = sourceCount - presentAccountedFor;
|
|
36862
37675
|
const reason = `INSERT OR IGNORE lost ${missing} of ${sourceCount} rows from '${legacyTableName}'\u2192'${targetTableName}' (rowsCopied=${rowsCopied}, existingBefore=${existingBefore}) \u2014 a CHECK/NOT NULL/type/enum constraint rejected them (NOT PK dedup); inspect epoch coercion + enum normalization. verify will confirm.`;
|
|
36863
|
-
|
|
37676
|
+
log5.error(
|
|
36864
37677
|
{
|
|
36865
37678
|
legacyTableName,
|
|
36866
37679
|
targetTableName,
|
|
@@ -36880,10 +37693,10 @@ function checkSchemaVersion(journal, forceCrossVersion) {
|
|
|
36880
37693
|
if (journal.targetSchemaVersion !== EXODUS_TARGET_SCHEMA_VERSION) {
|
|
36881
37694
|
const msg = `Schema version mismatch: journal=${journal.targetSchemaVersion}, expected=${EXODUS_TARGET_SCHEMA_VERSION}`;
|
|
36882
37695
|
if (forceCrossVersion) {
|
|
36883
|
-
|
|
37696
|
+
log5.warn(msg + " (--force-cross-version: continuing anyway)");
|
|
36884
37697
|
return true;
|
|
36885
37698
|
}
|
|
36886
|
-
|
|
37699
|
+
log5.error(msg + " \u2014 pass --force-cross-version to override");
|
|
36887
37700
|
return false;
|
|
36888
37701
|
}
|
|
36889
37702
|
return true;
|
|
@@ -36900,10 +37713,10 @@ async function runExodusMigrate(plan, forceCrossVersion = false, onProgress) {
|
|
|
36900
37713
|
error: `Insufficient disk space for exodus: need \u2265${plan.requiredBytes} bytes (\u2248${STAGING_HEADROOM_FACTOR}\xD7 largest source ${plan.largestSourceBytes} + consolidated estimate ${plan.totalSourceBytes}), but only ${plan.availableBytes} bytes are free on the target filesystem \u2014 ${shortfall} bytes short. Free up at least ${shortfall} bytes (e.g. \`cleo backup prune\`, clear caches), or move the .cleo/ directory to a larger volume, then retry.`
|
|
36901
37714
|
};
|
|
36902
37715
|
}
|
|
36903
|
-
|
|
37716
|
+
mkdirSync3(stagingDir, { recursive: true });
|
|
36904
37717
|
let sqliteVersion = "unknown";
|
|
36905
37718
|
for (const src of sources) {
|
|
36906
|
-
if (
|
|
37719
|
+
if (existsSync9(src.path)) {
|
|
36907
37720
|
const snap = openCleoDbSnapshot(src.path, { readOnly: true });
|
|
36908
37721
|
sqliteVersion = getSqliteVersion(snap.db);
|
|
36909
37722
|
snap.close();
|
|
@@ -36944,15 +37757,15 @@ async function runExodusMigrate(plan, forceCrossVersion = false, onProgress) {
|
|
|
36944
37757
|
};
|
|
36945
37758
|
var extractNativeDb = extractNativeDb2;
|
|
36946
37759
|
for (const src of sources) {
|
|
36947
|
-
if (!
|
|
36948
|
-
const backupDest =
|
|
37760
|
+
if (!existsSync9(src.path)) continue;
|
|
37761
|
+
const backupDest = join11(stagingDir, `${src.name.replace(/[^a-z0-9-]/g, "_")}-backup.db`);
|
|
36949
37762
|
const srcBytes = safeStatBytes(src.path);
|
|
36950
37763
|
const skipStagingCopy = srcBytes > plan.stagingCopyThresholdBytes;
|
|
36951
37764
|
if (skipStagingCopy) {
|
|
36952
37765
|
onProgress?.(
|
|
36953
37766
|
`Skipping full staging copy of ${src.name} (${srcBytes} bytes > ${plan.stagingCopyThresholdBytes} threshold) \u2014 source is archived, not deleted, on success.`
|
|
36954
37767
|
);
|
|
36955
|
-
} else if (!
|
|
37768
|
+
} else if (!existsSync9(backupDest)) {
|
|
36956
37769
|
onProgress?.(`Backing up ${src.name} \u2192 staging dir\u2026`);
|
|
36957
37770
|
copyFileSync3(src.path, backupDest);
|
|
36958
37771
|
backupPaths.push(backupDest);
|
|
@@ -36970,8 +37783,8 @@ async function runExodusMigrate(plan, forceCrossVersion = false, onProgress) {
|
|
|
36970
37783
|
});
|
|
36971
37784
|
const projectNative = extractNativeDb2(projectHandle);
|
|
36972
37785
|
const globalNative = extractNativeDb2(globalHandle);
|
|
36973
|
-
const projectSources = sources.filter((s) => s.targetScope === "project" &&
|
|
36974
|
-
const globalSources = sources.filter((s) => s.targetScope === "global" &&
|
|
37786
|
+
const projectSources = sources.filter((s) => s.targetScope === "project" && existsSync9(s.path));
|
|
37787
|
+
const globalSources = sources.filter((s) => s.targetScope === "global" && existsSync9(s.path));
|
|
36975
37788
|
await migrateScope(
|
|
36976
37789
|
"project",
|
|
36977
37790
|
projectSources,
|
|
@@ -36996,7 +37809,7 @@ async function runExodusMigrate(plan, forceCrossVersion = false, onProgress) {
|
|
|
36996
37809
|
return { ok: true, tables: allTableResults, stagingDir, backupPaths };
|
|
36997
37810
|
} catch (err) {
|
|
36998
37811
|
const error48 = err instanceof Error ? err.message : String(err);
|
|
36999
|
-
|
|
37812
|
+
log5.error({ err }, "Exodus migration failed");
|
|
37000
37813
|
return { ok: false, tables: allTableResults, stagingDir, backupPaths, error: error48 };
|
|
37001
37814
|
} finally {
|
|
37002
37815
|
try {
|
|
@@ -37016,7 +37829,7 @@ async function migrateScope(scope, sources, targetNativeDb, journal, stagingDir,
|
|
|
37016
37829
|
if (sources.length === 0) return;
|
|
37017
37830
|
onProgress?.(`Migrating ${scope}-scope sources\u2026`);
|
|
37018
37831
|
targetNativeDb.exec("PRAGMA foreign_keys = OFF");
|
|
37019
|
-
|
|
37832
|
+
log5.info({ scope }, "Exodus: foreign_keys=OFF for bulk copy (T11533 FK-defer)");
|
|
37020
37833
|
try {
|
|
37021
37834
|
for (let i = 0; i < sources.length; i++) {
|
|
37022
37835
|
const src = sources[i];
|
|
@@ -37083,7 +37896,7 @@ async function migrateScope(scope, sources, targetNativeDb, journal, stagingDir,
|
|
|
37083
37896
|
}
|
|
37084
37897
|
} catch (err) {
|
|
37085
37898
|
const msg = err instanceof Error ? err.message : String(err);
|
|
37086
|
-
|
|
37899
|
+
log5.warn({ tableName, sourceDb: src.name, err }, "Table copy failed \u2014 skipping");
|
|
37087
37900
|
status = "skipped";
|
|
37088
37901
|
errorMsg = msg;
|
|
37089
37902
|
skipped = true;
|
|
@@ -37131,7 +37944,7 @@ async function migrateScope(scope, sources, targetNativeDb, journal, stagingDir,
|
|
|
37131
37944
|
targetNativeDb.exec(`DETACH DATABASE "${attachAlias}"`);
|
|
37132
37945
|
onProgress?.(` [${src.name}] Detached "${attachAlias}"`);
|
|
37133
37946
|
} catch (detachErr) {
|
|
37134
|
-
|
|
37947
|
+
log5.warn(
|
|
37135
37948
|
{ attachAlias, sourceDb: src.name, err: detachErr },
|
|
37136
37949
|
"DETACH failed \u2014 alias will be released on DB close"
|
|
37137
37950
|
);
|
|
@@ -37141,7 +37954,7 @@ async function migrateScope(scope, sources, targetNativeDb, journal, stagingDir,
|
|
|
37141
37954
|
targetNativeDb.exec(`DETACH DATABASE "${crossAlias}"`);
|
|
37142
37955
|
onProgress?.(` [${src.name}] Cross-scope target detached "${crossAlias}"`);
|
|
37143
37956
|
} catch (detachErr) {
|
|
37144
|
-
|
|
37957
|
+
log5.warn(
|
|
37145
37958
|
{ crossAlias, sourceDb: src.name, err: detachErr },
|
|
37146
37959
|
"Cross-scope DETACH failed \u2014 alias will be released on DB close"
|
|
37147
37960
|
);
|
|
@@ -37153,28 +37966,28 @@ async function migrateScope(scope, sources, targetNativeDb, journal, stagingDir,
|
|
|
37153
37966
|
try {
|
|
37154
37967
|
const orphans = targetNativeDb.prepare("PRAGMA foreign_key_check").all();
|
|
37155
37968
|
if (orphans.length > 0) {
|
|
37156
|
-
|
|
37969
|
+
log5.warn(
|
|
37157
37970
|
{ scope, orphanCount: orphans.length, sample: orphans.slice(0, 5) },
|
|
37158
37971
|
`Exodus: PRAGMA foreign_key_check found ${orphans.length} orphan row(s) after bulk copy \u2014 these are genuine data orphans (not ordering artifacts)`
|
|
37159
37972
|
);
|
|
37160
37973
|
} else {
|
|
37161
|
-
|
|
37974
|
+
log5.info({ scope }, "Exodus: PRAGMA foreign_key_check PASSED \u2014 no orphan rows");
|
|
37162
37975
|
}
|
|
37163
37976
|
} catch (checkErr) {
|
|
37164
|
-
|
|
37977
|
+
log5.warn(
|
|
37165
37978
|
{ scope, err: checkErr },
|
|
37166
37979
|
"Exodus: PRAGMA foreign_key_check failed (non-fatal) \u2014 target schema may not have FK constraints enabled"
|
|
37167
37980
|
);
|
|
37168
37981
|
}
|
|
37169
37982
|
try {
|
|
37170
37983
|
targetNativeDb.exec("PRAGMA foreign_keys = ON");
|
|
37171
|
-
|
|
37984
|
+
log5.info({ scope }, "Exodus: foreign_keys=ON restored after bulk copy");
|
|
37172
37985
|
} catch (fkErr) {
|
|
37173
|
-
|
|
37986
|
+
log5.warn({ scope, err: fkErr }, "Exodus: could not restore foreign_keys=ON (non-fatal)");
|
|
37174
37987
|
}
|
|
37175
37988
|
}
|
|
37176
37989
|
}
|
|
37177
|
-
var
|
|
37990
|
+
var log5, LOCK_SENTINEL_SUFFIX, JOURNAL_FILENAME, SOURCE_EPOCH_UNITS;
|
|
37178
37991
|
var init_migrate = __esm({
|
|
37179
37992
|
"packages/core/src/store/exodus/migrate.ts"() {
|
|
37180
37993
|
"use strict";
|
|
@@ -37186,7 +37999,7 @@ var init_migrate = __esm({
|
|
|
37186
37999
|
init_plan2();
|
|
37187
38000
|
init_table_name_map();
|
|
37188
38001
|
init_types();
|
|
37189
|
-
|
|
38002
|
+
log5 = getLogger("exodus-migrate");
|
|
37190
38003
|
LOCK_SENTINEL_SUFFIX = ".exodus-lock";
|
|
37191
38004
|
JOURNAL_FILENAME = "exodus-journal.json";
|
|
37192
38005
|
SOURCE_EPOCH_UNITS = /* @__PURE__ */ new Map([
|
|
@@ -37210,7 +38023,7 @@ function sealExodus(plan, scopeArg, cwd) {
|
|
|
37210
38023
|
const parity = computeCountParity(plan.sources, plan.projectDbPath, plan.globalDbPath);
|
|
37211
38024
|
if (!parity.ok) {
|
|
37212
38025
|
const refusedReason = `Refusing to seal: ${parity.deficits.length} table(s) have FEWER rows in the consolidated cleo.db than the legacy source \u2014 the data is NOT fully migrated. Run \`cleo exodus migrate\` first. Deficits: ${parity.deficits.map((d) => `${d.targetTable}(${d.sourceCount}\u2192${d.targetCount})`).join(", ")}`;
|
|
37213
|
-
|
|
38026
|
+
log6.error({ deficits: parity.deficits.length }, `exodus seal refused \u2014 ${refusedReason}`);
|
|
37214
38027
|
return { ok: false, refusedReason, parity, scopes: [] };
|
|
37215
38028
|
}
|
|
37216
38029
|
const outcomes = [];
|
|
@@ -37226,7 +38039,7 @@ function sealExodus(plan, scopeArg, cwd) {
|
|
|
37226
38039
|
scopeSources.map((s) => s.name),
|
|
37227
38040
|
cwd
|
|
37228
38041
|
);
|
|
37229
|
-
|
|
38042
|
+
log6.info(
|
|
37230
38043
|
{ scope, alreadySealed, archived: archived.filter((a) => a.action === "archived").length },
|
|
37231
38044
|
`exodus seal: scope '${scope}' certified (count-parity verified, ${parity.checked} tables)`
|
|
37232
38045
|
);
|
|
@@ -37234,23 +38047,23 @@ function sealExodus(plan, scopeArg, cwd) {
|
|
|
37234
38047
|
}
|
|
37235
38048
|
return { ok: true, parity, scopes: outcomes };
|
|
37236
38049
|
}
|
|
37237
|
-
var
|
|
38050
|
+
var log6;
|
|
37238
38051
|
var init_seal = __esm({
|
|
37239
38052
|
"packages/core/src/store/exodus/seal.ts"() {
|
|
37240
38053
|
"use strict";
|
|
37241
38054
|
init_logger2();
|
|
37242
38055
|
init_archive2();
|
|
37243
38056
|
init_count_parity();
|
|
37244
|
-
|
|
38057
|
+
log6 = getLogger("exodus-seal");
|
|
37245
38058
|
}
|
|
37246
38059
|
});
|
|
37247
38060
|
|
|
37248
38061
|
// packages/core/src/store/exodus/status.ts
|
|
37249
|
-
import { existsSync as
|
|
37250
|
-
import { join as
|
|
38062
|
+
import { existsSync as existsSync10, readdirSync as readdirSync4, readFileSync as readFileSync3, statSync as statSync4 } from "node:fs";
|
|
38063
|
+
import { join as join12 } from "node:path";
|
|
37251
38064
|
function readJournal2(stagingDir) {
|
|
37252
|
-
const p =
|
|
37253
|
-
if (!
|
|
38065
|
+
const p = join12(stagingDir, JOURNAL_FILENAME2);
|
|
38066
|
+
if (!existsSync10(p)) return null;
|
|
37254
38067
|
try {
|
|
37255
38068
|
return JSON.parse(readFileSync3(p, "utf8"));
|
|
37256
38069
|
} catch {
|
|
@@ -37259,7 +38072,7 @@ function readJournal2(stagingDir) {
|
|
|
37259
38072
|
}
|
|
37260
38073
|
function findStagingDirs(cleoDir) {
|
|
37261
38074
|
try {
|
|
37262
|
-
return
|
|
38075
|
+
return readdirSync4(cleoDir, { withFileTypes: true }).filter((e) => e.isDirectory() && e.name.startsWith("exodus-staging-")).map((e) => join12(cleoDir, e.name)).sort().reverse();
|
|
37263
38076
|
} catch {
|
|
37264
38077
|
return [];
|
|
37265
38078
|
}
|
|
@@ -37282,15 +38095,15 @@ function runExodusStatus(cwd) {
|
|
|
37282
38095
|
const sourcesInfo = plan.sources.map((s) => ({
|
|
37283
38096
|
name: s.name,
|
|
37284
38097
|
path: s.path,
|
|
37285
|
-
exists:
|
|
38098
|
+
exists: existsSync10(s.path),
|
|
37286
38099
|
bytes: safeBytes(s.path)
|
|
37287
38100
|
}));
|
|
37288
38101
|
return {
|
|
37289
38102
|
hasStaging: latestStaging !== null,
|
|
37290
38103
|
stagingDir: latestStaging,
|
|
37291
38104
|
journal,
|
|
37292
|
-
projectDbExists:
|
|
37293
|
-
globalDbExists:
|
|
38105
|
+
projectDbExists: existsSync10(projectDbPath),
|
|
38106
|
+
globalDbExists: existsSync10(globalDbPath),
|
|
37294
38107
|
sourcesPresent: sourcesInfo.some((s) => s.exists),
|
|
37295
38108
|
sources: sourcesInfo
|
|
37296
38109
|
};
|
|
@@ -37307,7 +38120,7 @@ var init_status = __esm({
|
|
|
37307
38120
|
});
|
|
37308
38121
|
|
|
37309
38122
|
// packages/core/src/store/exodus/verify-migration.ts
|
|
37310
|
-
import { existsSync as
|
|
38123
|
+
import { existsSync as existsSync11 } from "node:fs";
|
|
37311
38124
|
import { createRequire as createRequire3 } from "node:module";
|
|
37312
38125
|
function orderByClause(db, tableName) {
|
|
37313
38126
|
try {
|
|
@@ -37327,7 +38140,7 @@ function computeTableDigest(db, tableName, columns, transform2) {
|
|
|
37327
38140
|
count = Number(row?.c ?? 0);
|
|
37328
38141
|
} catch (err) {
|
|
37329
38142
|
const msg = err instanceof Error ? err.message : String(err);
|
|
37330
|
-
|
|
38143
|
+
log7.warn(
|
|
37331
38144
|
{ tableName, err: msg },
|
|
37332
38145
|
"computeTableDigest: COUNT(*) failed (possibly a virtual/FTS table) \u2014 treating as 0 rows"
|
|
37333
38146
|
);
|
|
@@ -37362,7 +38175,7 @@ function computeTableDigest(db, tableName, columns, transform2) {
|
|
|
37362
38175
|
}
|
|
37363
38176
|
} catch (err) {
|
|
37364
38177
|
const msg = err instanceof Error ? err.message : String(err);
|
|
37365
|
-
|
|
38178
|
+
log7.warn(
|
|
37366
38179
|
{ tableName, err: msg },
|
|
37367
38180
|
"computeTableDigest: streamed SELECT failed (possibly a virtual/FTS table) \u2014 digest skipped (COUNT(*) parity still enforced)"
|
|
37368
38181
|
);
|
|
@@ -37488,7 +38301,7 @@ function foreignKeyCheck(db, scope) {
|
|
|
37488
38301
|
try {
|
|
37489
38302
|
const rows = db.prepare("PRAGMA foreign_key_check").all();
|
|
37490
38303
|
if (rows.length > 0) {
|
|
37491
|
-
|
|
38304
|
+
log7.warn(
|
|
37492
38305
|
{ scope, count: rows.length, sample: rows.slice(0, 5) },
|
|
37493
38306
|
`verifyMigration: PRAGMA foreign_key_check found ${rows.length} orphan row(s)`
|
|
37494
38307
|
);
|
|
@@ -37500,7 +38313,7 @@ function foreignKeyCheck(db, scope) {
|
|
|
37500
38313
|
fkid: r.fkid
|
|
37501
38314
|
}));
|
|
37502
38315
|
} catch (err) {
|
|
37503
|
-
|
|
38316
|
+
log7.warn({ scope, err }, "verifyMigration: PRAGMA foreign_key_check failed (non-fatal)");
|
|
37504
38317
|
return [];
|
|
37505
38318
|
}
|
|
37506
38319
|
}
|
|
@@ -37537,13 +38350,13 @@ function sourceOrphanSignatures(db, sourceName, scope) {
|
|
|
37537
38350
|
const rows = db.prepare("PRAGMA foreign_key_check").all();
|
|
37538
38351
|
for (const r of rows) sigs.add(orphanSignature(db, r, sourceName));
|
|
37539
38352
|
if (rows.length > 0) {
|
|
37540
|
-
|
|
38353
|
+
log7.warn(
|
|
37541
38354
|
{ scope, count: rows.length, sample: rows.slice(0, 5) },
|
|
37542
38355
|
`verifyMigration: source already has ${rows.length} pre-existing FK orphan(s) \u2014 these are tolerated (carried forward losslessly, flagged for data-hygiene)`
|
|
37543
38356
|
);
|
|
37544
38357
|
}
|
|
37545
38358
|
} catch (err) {
|
|
37546
|
-
|
|
38359
|
+
log7.warn({ scope, err }, "verifyMigration: source PRAGMA foreign_key_check failed (non-fatal)");
|
|
37547
38360
|
}
|
|
37548
38361
|
return sigs;
|
|
37549
38362
|
}
|
|
@@ -37555,7 +38368,7 @@ function verifyMigration(sources, projectDbPath, globalDbPath, onProgress) {
|
|
|
37555
38368
|
const preExistingForeignKeyViolations = [];
|
|
37556
38369
|
const failureLines = [];
|
|
37557
38370
|
const sourceOrphanSigs = /* @__PURE__ */ new Set();
|
|
37558
|
-
if (!
|
|
38371
|
+
if (!existsSync11(projectDbPath)) {
|
|
37559
38372
|
return {
|
|
37560
38373
|
ok: false,
|
|
37561
38374
|
tables: [],
|
|
@@ -37566,7 +38379,7 @@ function verifyMigration(sources, projectDbPath, globalDbPath, onProgress) {
|
|
|
37566
38379
|
error: `Consolidated project cleo.db not found at ${projectDbPath}. Run 'cleo exodus migrate' first.`
|
|
37567
38380
|
};
|
|
37568
38381
|
}
|
|
37569
|
-
if (!
|
|
38382
|
+
if (!existsSync11(globalDbPath)) {
|
|
37570
38383
|
return {
|
|
37571
38384
|
ok: false,
|
|
37572
38385
|
tables: [],
|
|
@@ -37581,7 +38394,7 @@ function verifyMigration(sources, projectDbPath, globalDbPath, onProgress) {
|
|
|
37581
38394
|
const globalSnap = openCleoDbSnapshot(globalDbPath, { readOnly: true });
|
|
37582
38395
|
try {
|
|
37583
38396
|
for (const src of sources) {
|
|
37584
|
-
if (!
|
|
38397
|
+
if (!existsSync11(src.path)) {
|
|
37585
38398
|
onProgress?.(`Skipping ${src.name} (not present)`);
|
|
37586
38399
|
continue;
|
|
37587
38400
|
}
|
|
@@ -37666,7 +38479,7 @@ function verifyMigration(sources, projectDbPath, globalDbPath, onProgress) {
|
|
|
37666
38479
|
`[${scope}] ${src.name}.${legacyTableName} \u2192 ${targetTableName}: DEFICIT \u2014 source=${srcDigest.count} rows, target=${tgtDigest.count} rows (${srcDigest.count - tgtDigest.count} missing), hashMatch=${hashMatch}`
|
|
37667
38480
|
);
|
|
37668
38481
|
} else if (tgtDigest.count > srcDigest.count) {
|
|
37669
|
-
|
|
38482
|
+
log7.warn(
|
|
37670
38483
|
{
|
|
37671
38484
|
scope,
|
|
37672
38485
|
source: src.name,
|
|
@@ -37716,7 +38529,7 @@ function verifyMigration(sources, projectDbPath, globalDbPath, onProgress) {
|
|
|
37716
38529
|
}
|
|
37717
38530
|
}
|
|
37718
38531
|
if (preExistingForeignKeyViolations.length > 0) {
|
|
37719
|
-
|
|
38532
|
+
log7.warn(
|
|
37720
38533
|
{
|
|
37721
38534
|
count: preExistingForeignKeyViolations.length,
|
|
37722
38535
|
sample: preExistingForeignKeyViolations.slice(0, 5)
|
|
@@ -37726,7 +38539,7 @@ function verifyMigration(sources, projectDbPath, globalDbPath, onProgress) {
|
|
|
37726
38539
|
}
|
|
37727
38540
|
} catch (err) {
|
|
37728
38541
|
const error48 = err instanceof Error ? err.message : String(err);
|
|
37729
|
-
|
|
38542
|
+
log7.error({ err }, "verifyMigration failed");
|
|
37730
38543
|
return {
|
|
37731
38544
|
ok: false,
|
|
37732
38545
|
tables,
|
|
@@ -37743,7 +38556,7 @@ function verifyMigration(sources, projectDbPath, globalDbPath, onProgress) {
|
|
|
37743
38556
|
if (failureLines.length > 0) {
|
|
37744
38557
|
const error48 = `verifyMigration FAILED: ${failureLines.length} issue(s):
|
|
37745
38558
|
${failureLines.map((l) => ` \u2022 ${l}`).join("\n")}`;
|
|
37746
|
-
|
|
38559
|
+
log7.error({ failureCount: failureLines.length }, error48);
|
|
37747
38560
|
return {
|
|
37748
38561
|
ok: false,
|
|
37749
38562
|
tables,
|
|
@@ -37763,7 +38576,7 @@ ${failureLines.map((l) => ` \u2022 ${l}`).join("\n")}`;
|
|
|
37763
38576
|
enumDrift
|
|
37764
38577
|
};
|
|
37765
38578
|
}
|
|
37766
|
-
var
|
|
38579
|
+
var log7, _require, CHECK_ENUM_REGEX;
|
|
37767
38580
|
var init_verify_migration = __esm({
|
|
37768
38581
|
"packages/core/src/store/exodus/verify-migration.ts"() {
|
|
37769
38582
|
"use strict";
|
|
@@ -37772,7 +38585,7 @@ var init_verify_migration = __esm({
|
|
|
37772
38585
|
init_open_cleo_db();
|
|
37773
38586
|
init_column_transforms();
|
|
37774
38587
|
init_table_name_map();
|
|
37775
|
-
|
|
38588
|
+
log7 = getLogger("verify-migration");
|
|
37776
38589
|
_require = createRequire3(import.meta.url);
|
|
37777
38590
|
CHECK_ENUM_REGEX = /CHECK\s*\(\s*"([^"]+)"\s+(?:IS\s+NULL\s+OR\s+"[^"]+"\s+)?IN\s*\(([^)]*)\)\s*\)/gi;
|
|
37778
38591
|
}
|
|
@@ -37864,7 +38677,7 @@ __export(on_open_exports, {
|
|
|
37864
38677
|
isDataContinuityOk: () => isDataContinuityOk,
|
|
37865
38678
|
maybeRunExodusOnOpen: () => maybeRunExodusOnOpen
|
|
37866
38679
|
});
|
|
37867
|
-
import { existsSync as
|
|
38680
|
+
import { existsSync as existsSync12 } from "node:fs";
|
|
37868
38681
|
function isDisabledByEnv() {
|
|
37869
38682
|
const v = process.env.CLEO_DISABLE_EXODUS_ON_OPEN;
|
|
37870
38683
|
return v === "1" || v === "true";
|
|
@@ -37890,7 +38703,7 @@ function rollbackConsolidatedToEmpty(nativeDb, scope) {
|
|
|
37890
38703
|
"SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' AND name NOT LIKE '__drizzle_%'"
|
|
37891
38704
|
).all().map((r) => r.name);
|
|
37892
38705
|
} catch (err) {
|
|
37893
|
-
|
|
38706
|
+
log8.error({ err, scope }, "exodus-on-open: failed to enumerate tables for rollback");
|
|
37894
38707
|
return;
|
|
37895
38708
|
}
|
|
37896
38709
|
try {
|
|
@@ -37900,7 +38713,7 @@ function rollbackConsolidatedToEmpty(nativeDb, scope) {
|
|
|
37900
38713
|
try {
|
|
37901
38714
|
nativeDb.exec(`DELETE FROM "${table}"`);
|
|
37902
38715
|
} catch (err) {
|
|
37903
|
-
|
|
38716
|
+
log8.warn({ err, table, scope }, "exodus-on-open: failed to clear table during rollback");
|
|
37904
38717
|
}
|
|
37905
38718
|
}
|
|
37906
38719
|
nativeDb.exec("COMMIT");
|
|
@@ -37909,7 +38722,7 @@ function rollbackConsolidatedToEmpty(nativeDb, scope) {
|
|
|
37909
38722
|
nativeDb.exec("ROLLBACK");
|
|
37910
38723
|
} catch {
|
|
37911
38724
|
}
|
|
37912
|
-
|
|
38725
|
+
log8.error({ err, scope }, "exodus-on-open: rollback transaction failed");
|
|
37913
38726
|
} finally {
|
|
37914
38727
|
try {
|
|
37915
38728
|
nativeDb.exec("PRAGMA foreign_keys = ON");
|
|
@@ -37929,7 +38742,7 @@ async function rollbackBothScopes(scope, projectDbPath, globalDbPath) {
|
|
|
37929
38742
|
rollbackConsolidatedToEmpty(native, s);
|
|
37930
38743
|
}
|
|
37931
38744
|
} catch (err) {
|
|
37932
|
-
|
|
38745
|
+
log8.warn(
|
|
37933
38746
|
{ err, scope: s, openingScope: scope },
|
|
37934
38747
|
"exodus-on-open: could not roll back scope (best-effort)"
|
|
37935
38748
|
);
|
|
@@ -37945,7 +38758,7 @@ function isDataContinuityOk(result) {
|
|
|
37945
38758
|
const deficits = result.tables.filter((t) => t.targetCount < t.sourceCount);
|
|
37946
38759
|
const surpluses = result.tables.filter((t) => t.targetCount > t.sourceCount);
|
|
37947
38760
|
if (surpluses.length > 0) {
|
|
37948
|
-
|
|
38761
|
+
log8.warn(
|
|
37949
38762
|
{
|
|
37950
38763
|
surpluses: surpluses.map((t) => ({
|
|
37951
38764
|
table: t.targetTable,
|
|
@@ -37979,7 +38792,7 @@ async function maybeRunExodusOnOpen(scope, dbPath, nativeDb, cwd) {
|
|
|
37979
38792
|
const { buildExodusPlan: buildExodusPlan2, runExodusMigrate: runExodusMigrate2, verifyMigration: verifyMigration2, clearExodusJournal: clearExodusJournal2 } = await Promise.resolve().then(() => (init_exodus(), exodus_exports));
|
|
37980
38793
|
const plan = buildExodusPlan2(cwd);
|
|
37981
38794
|
const scopeSources = plan.sources.filter((s) => s.targetScope === scope);
|
|
37982
|
-
if (!scopeSources.some((s) =>
|
|
38795
|
+
if (!scopeSources.some((s) => existsSync12(s.path))) {
|
|
37983
38796
|
return {
|
|
37984
38797
|
outcome: "skipped",
|
|
37985
38798
|
reason: `no legacy ${scope}-scope source DBs present (fresh install or cross-scope-only)`
|
|
@@ -37992,11 +38805,11 @@ async function maybeRunExodusOnOpen(scope, dbPath, nativeDb, cwd) {
|
|
|
37992
38805
|
if (!consolidatedIsEmpty(nativeDb, scope)) {
|
|
37993
38806
|
return { outcome: "skipped", reason: "migrated by a concurrent process (lock winner)" };
|
|
37994
38807
|
}
|
|
37995
|
-
|
|
38808
|
+
log8.info(
|
|
37996
38809
|
{
|
|
37997
38810
|
scope,
|
|
37998
38811
|
dbPath,
|
|
37999
|
-
sources: plan.sources.filter((s) =>
|
|
38812
|
+
sources: plan.sources.filter((s) => existsSync12(s.path)).map((s) => s.name)
|
|
38000
38813
|
},
|
|
38001
38814
|
"exodus-on-open: consolidated cleo.db is empty and legacy data present \u2014 auto-migrating"
|
|
38002
38815
|
);
|
|
@@ -38005,23 +38818,23 @@ async function maybeRunExodusOnOpen(scope, dbPath, nativeDb, cwd) {
|
|
|
38005
38818
|
const migrateResult = await runExodusMigrate2(
|
|
38006
38819
|
plan,
|
|
38007
38820
|
false,
|
|
38008
|
-
(msg) =>
|
|
38821
|
+
(msg) => log8.debug({ scope }, `exodus-on-open: ${msg}`)
|
|
38009
38822
|
);
|
|
38010
38823
|
if (!migrateResult.ok) {
|
|
38011
38824
|
await rollbackBothScopes(scope, plan.projectDbPath, plan.globalDbPath);
|
|
38012
38825
|
clearExodusJournal2(migrateResult.stagingDir);
|
|
38013
38826
|
const reason = `migration failed: ${migrateResult.error ?? "unknown error"} \u2014 legacy DBs kept as source`;
|
|
38014
|
-
|
|
38827
|
+
log8.error({ scope, error: migrateResult.error }, `exodus-on-open: ${reason}`);
|
|
38015
38828
|
return { outcome: "aborted", reason };
|
|
38016
38829
|
}
|
|
38017
38830
|
const verifyResult = verifyMigration2(
|
|
38018
38831
|
plan.sources,
|
|
38019
38832
|
plan.projectDbPath,
|
|
38020
38833
|
plan.globalDbPath,
|
|
38021
|
-
(msg) =>
|
|
38834
|
+
(msg) => log8.debug({ scope }, `exodus-on-open verify: ${msg}`)
|
|
38022
38835
|
);
|
|
38023
38836
|
if (!verifyResult.ok) {
|
|
38024
|
-
|
|
38837
|
+
log8.warn(
|
|
38025
38838
|
{
|
|
38026
38839
|
scope,
|
|
38027
38840
|
enumDrift: verifyResult.enumDrift.length,
|
|
@@ -38035,7 +38848,7 @@ async function maybeRunExodusOnOpen(scope, dbPath, nativeDb, cwd) {
|
|
|
38035
38848
|
clearExodusJournal2(plan.stagingDir);
|
|
38036
38849
|
const deficits = verifyResult.tables.filter((t) => t.targetCount < t.sourceCount).map((t) => `${t.targetTable}(${t.sourceCount}\u2192${t.targetCount})`);
|
|
38037
38850
|
const reason = `parity verification failed \u2014 cutover aborted, legacy DBs kept as source. count deficits: [${deficits.join(", ")}]; INTRODUCED fk orphans: ${verifyResult.introducedForeignKeyViolations.length} (pre-existing source orphans tolerated: ${verifyResult.preExistingForeignKeyViolations.length}). ` + `${verifyResult.error ?? ""}`.trim();
|
|
38038
|
-
|
|
38851
|
+
log8.error(
|
|
38039
38852
|
{
|
|
38040
38853
|
scope,
|
|
38041
38854
|
countDeficits: deficits,
|
|
@@ -38047,14 +38860,14 @@ async function maybeRunExodusOnOpen(scope, dbPath, nativeDb, cwd) {
|
|
|
38047
38860
|
return { outcome: "aborted", reason };
|
|
38048
38861
|
}
|
|
38049
38862
|
const rowsCopied = migrateResult.tables.filter((t) => !t.skipped).reduce((n, t) => n + t.rowsCopied, 0);
|
|
38050
|
-
|
|
38863
|
+
log8.info(
|
|
38051
38864
|
{ scope, rowsCopied, tables: migrateResult.tables.length },
|
|
38052
38865
|
"exodus-on-open: parity verified \u2014 legacy data migrated into consolidated cleo.db"
|
|
38053
38866
|
);
|
|
38054
38867
|
try {
|
|
38055
|
-
const consumed = plan.sources.filter((s) =>
|
|
38868
|
+
const consumed = plan.sources.filter((s) => existsSync12(s.path));
|
|
38056
38869
|
const archiveResult = archiveMigratedSources(consumed, cwd);
|
|
38057
|
-
|
|
38870
|
+
log8.info(
|
|
38058
38871
|
{
|
|
38059
38872
|
scope,
|
|
38060
38873
|
archived: archiveResult.sources.filter((s) => s.action === "archived").length,
|
|
@@ -38063,7 +38876,7 @@ async function maybeRunExodusOnOpen(scope, dbPath, nativeDb, cwd) {
|
|
|
38063
38876
|
"exodus-on-open: archived legacy sources + sealed completion marker(s)"
|
|
38064
38877
|
);
|
|
38065
38878
|
} catch (err) {
|
|
38066
|
-
|
|
38879
|
+
log8.error(
|
|
38067
38880
|
{ err, scope },
|
|
38068
38881
|
"exodus-on-open: post-migration archive/marker step failed (migration itself succeeded \u2014 legacy DBs left in place, will be re-checked by `cleo doctor exodus-residue`)"
|
|
38069
38882
|
);
|
|
@@ -38085,14 +38898,14 @@ async function maybeRunExodusOnOpen(scope, dbPath, nativeDb, cwd) {
|
|
|
38085
38898
|
function _isExodusInProgress() {
|
|
38086
38899
|
return _exodusInProgress;
|
|
38087
38900
|
}
|
|
38088
|
-
var
|
|
38901
|
+
var log8, _exodusInProgress;
|
|
38089
38902
|
var init_on_open = __esm({
|
|
38090
38903
|
"packages/core/src/store/exodus/on-open.ts"() {
|
|
38091
38904
|
"use strict";
|
|
38092
38905
|
init_logger2();
|
|
38093
38906
|
init_lock();
|
|
38094
38907
|
init_archive2();
|
|
38095
|
-
|
|
38908
|
+
log8 = getLogger("exodus-on-open");
|
|
38096
38909
|
_exodusInProgress = false;
|
|
38097
38910
|
}
|
|
38098
38911
|
});
|
|
@@ -38109,9 +38922,9 @@ __export(dual_scope_db_exports, {
|
|
|
38109
38922
|
resolveDualScopeDbPath: () => resolveDualScopeDbPath,
|
|
38110
38923
|
upsertIdempotent: () => upsertIdempotent
|
|
38111
38924
|
});
|
|
38112
|
-
import { existsSync as
|
|
38925
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync4 } from "node:fs";
|
|
38113
38926
|
import { createRequire as createRequire4 } from "node:module";
|
|
38114
|
-
import { dirname as dirname5, join as
|
|
38927
|
+
import { dirname as dirname5, join as join13 } from "node:path";
|
|
38115
38928
|
function assertWriteDurable(handle) {
|
|
38116
38929
|
if (handle.exodusAbort) {
|
|
38117
38930
|
throw new ExodusAbortWriteUnsafeError(handle.exodusAbort);
|
|
@@ -38128,9 +38941,9 @@ function cacheKey(scope, dbPath) {
|
|
|
38128
38941
|
}
|
|
38129
38942
|
function resolveDualScopeDbPath(scope, cwd) {
|
|
38130
38943
|
if (scope === "project") {
|
|
38131
|
-
return
|
|
38944
|
+
return join13(resolveCleoDir(cwd), "cleo.db");
|
|
38132
38945
|
}
|
|
38133
|
-
return
|
|
38946
|
+
return join13(getCleoHome(), "cleo.db");
|
|
38134
38947
|
}
|
|
38135
38948
|
function migrationsSetName(scope) {
|
|
38136
38949
|
return scope === "project" ? "drizzle-cleo-project" : "drizzle-cleo-global";
|
|
@@ -38173,11 +38986,11 @@ async function openDualScopeDb(scope, cwd) {
|
|
|
38173
38986
|
const dbPath = resolveDualScopeDbPath(scope, cwd);
|
|
38174
38987
|
return scope === "project" ? openDualScopeDbAtPath("project", dbPath, cwd) : openDualScopeDbAtPath("global", dbPath, cwd);
|
|
38175
38988
|
}
|
|
38176
|
-
async function openDedicatedDualScopeDb(scope, dbPath,
|
|
38177
|
-
|
|
38989
|
+
async function openDedicatedDualScopeDb(scope, dbPath, log9) {
|
|
38990
|
+
log9.debug({ scope, dbPath }, "opening DEDICATED (non-cached) dual-scope cleo.db (T11782 FIX D)");
|
|
38178
38991
|
const dir = dirname5(dbPath);
|
|
38179
|
-
if (!
|
|
38180
|
-
|
|
38992
|
+
if (!existsSync13(dir)) {
|
|
38993
|
+
mkdirSync4(dir, { recursive: true });
|
|
38181
38994
|
}
|
|
38182
38995
|
const DatabaseSyncCtor = getDatabaseSyncCtor();
|
|
38183
38996
|
const nativeDb = new DatabaseSyncCtor(dbPath, { allowExtension: true });
|
|
@@ -38200,7 +39013,7 @@ async function openDedicatedDualScopeDb(scope, dbPath, log8) {
|
|
|
38200
39013
|
existenceTable(scope),
|
|
38201
39014
|
`dual-scope-db[${scope}]`
|
|
38202
39015
|
);
|
|
38203
|
-
|
|
39016
|
+
log9.debug({ scope, dbPath }, "DEDICATED dual-scope cleo.db ready (T11782 FIX D)");
|
|
38204
39017
|
return {
|
|
38205
39018
|
db,
|
|
38206
39019
|
scope,
|
|
@@ -38225,15 +39038,15 @@ async function openDualScopeDbAtPath(scope, dbPath, exodusCwd, options) {
|
|
|
38225
39038
|
return existing.handle;
|
|
38226
39039
|
}
|
|
38227
39040
|
}
|
|
38228
|
-
const
|
|
39041
|
+
const log9 = getLogger("dual-scope-db");
|
|
38229
39042
|
if (dedicated) {
|
|
38230
|
-
return openDedicatedDualScopeDb(scope, dbPath,
|
|
39043
|
+
return openDedicatedDualScopeDb(scope, dbPath, log9);
|
|
38231
39044
|
}
|
|
38232
39045
|
const initPromise = (async () => {
|
|
38233
|
-
|
|
39046
|
+
log9.debug({ scope, dbPath }, "opening dual-scope cleo.db");
|
|
38234
39047
|
const dir = dirname5(dbPath);
|
|
38235
|
-
if (!
|
|
38236
|
-
|
|
39048
|
+
if (!existsSync13(dir)) {
|
|
39049
|
+
mkdirSync4(dir, { recursive: true });
|
|
38237
39050
|
}
|
|
38238
39051
|
const DatabaseSyncCtor = getDatabaseSyncCtor();
|
|
38239
39052
|
const nativeDb = new DatabaseSyncCtor(dbPath, { allowExtension: true });
|
|
@@ -38260,7 +39073,7 @@ async function openDualScopeDbAtPath(scope, dbPath, exodusCwd, options) {
|
|
|
38260
39073
|
existenceTable(scope),
|
|
38261
39074
|
`dual-scope-db[${scope}]`
|
|
38262
39075
|
);
|
|
38263
|
-
|
|
39076
|
+
log9.debug({ scope, dbPath }, "dual-scope cleo.db ready");
|
|
38264
39077
|
const built = {
|
|
38265
39078
|
db,
|
|
38266
39079
|
scope,
|
|
@@ -38286,6 +39099,25 @@ async function openDualScopeDbAtPath(scope, dbPath, exodusCwd, options) {
|
|
|
38286
39099
|
{ dbPath }
|
|
38287
39100
|
);
|
|
38288
39101
|
if (exodusCwd !== void 0 && dbPath === resolveDualScopeDbPath(scope, exodusCwd)) {
|
|
39102
|
+
let releaseDbHeavy = null;
|
|
39103
|
+
let dbHeavyDeferred = false;
|
|
39104
|
+
try {
|
|
39105
|
+
const { governor: governor2 } = await Promise.resolve().then(() => (init_governor(), governor_exports));
|
|
39106
|
+
const admit = await governor2.tryAcquire("db-heavy");
|
|
39107
|
+
if (admit.deferred) {
|
|
39108
|
+
dbHeavyDeferred = true;
|
|
39109
|
+
} else {
|
|
39110
|
+
releaseDbHeavy = admit.release;
|
|
39111
|
+
}
|
|
39112
|
+
} catch {
|
|
39113
|
+
}
|
|
39114
|
+
if (dbHeavyDeferred) {
|
|
39115
|
+
log9.debug(
|
|
39116
|
+
{ scope, dbPath },
|
|
39117
|
+
"exodus-on-open skipped this open \u2014 db-heavy deferred under memory pressure (re-runs idempotently on a calmer open)"
|
|
39118
|
+
);
|
|
39119
|
+
return handle;
|
|
39120
|
+
}
|
|
38289
39121
|
try {
|
|
38290
39122
|
const { maybeRunExodusOnOpen: maybeRunExodusOnOpen2 } = await Promise.resolve().then(() => (init_on_open(), on_open_exports));
|
|
38291
39123
|
const result = await maybeRunExodusOnOpen2(scope, dbPath, nativeDb, exodusCwd);
|
|
@@ -38298,7 +39130,7 @@ async function openDualScopeDbAtPath(scope, dbPath, exodusCwd, options) {
|
|
|
38298
39130
|
reason: result.reason,
|
|
38299
39131
|
at: Date.now()
|
|
38300
39132
|
};
|
|
38301
|
-
|
|
39133
|
+
log9.warn(
|
|
38302
39134
|
{ scope, reason: result.reason },
|
|
38303
39135
|
"exodus-on-open aborted; consolidated cleo.db left empty, legacy kept as source \u2014 mutating callers must check handle.exodusAbort / call assertWriteDurable (T11828)"
|
|
38304
39136
|
);
|
|
@@ -38311,11 +39143,14 @@ async function openDualScopeDbAtPath(scope, dbPath, exodusCwd, options) {
|
|
|
38311
39143
|
return reopened;
|
|
38312
39144
|
}
|
|
38313
39145
|
} catch (err) {
|
|
38314
|
-
|
|
39146
|
+
log9.warn(
|
|
38315
39147
|
{ err, scope },
|
|
38316
39148
|
"exodus-on-open hook failed (non-fatal); re-opening consolidated handle"
|
|
38317
39149
|
);
|
|
38318
39150
|
return scope === "project" ? openDualScopeDbAtPath("project", dbPath) : openDualScopeDbAtPath("global", dbPath);
|
|
39151
|
+
} finally {
|
|
39152
|
+
if (releaseDbHeavy) await releaseDbHeavy().catch(() => {
|
|
39153
|
+
});
|
|
38319
39154
|
}
|
|
38320
39155
|
}
|
|
38321
39156
|
return handle;
|
|
@@ -38938,8 +39773,8 @@ __export(nexus_sqlite_exports, {
|
|
|
38938
39773
|
resetNexusDbState: () => resetNexusDbState,
|
|
38939
39774
|
resolveNexusMigrationsFolder: () => resolveNexusMigrationsFolder
|
|
38940
39775
|
});
|
|
38941
|
-
import { copyFileSync as copyFileSync4, existsSync as
|
|
38942
|
-
import { join as
|
|
39776
|
+
import { copyFileSync as copyFileSync4, existsSync as existsSync14 } from "node:fs";
|
|
39777
|
+
import { join as join14 } from "node:path";
|
|
38943
39778
|
function getNexusDbPath(cwd) {
|
|
38944
39779
|
return resolveDualScopeDbPath("project", cwd);
|
|
38945
39780
|
}
|
|
@@ -38971,7 +39806,7 @@ function resolveNexusMigrationsFolder() {
|
|
|
38971
39806
|
return resolveCorePackageMigrationsFolder("drizzle-nexus");
|
|
38972
39807
|
}
|
|
38973
39808
|
function getNestedNexusSentinelPath() {
|
|
38974
|
-
return
|
|
39809
|
+
return join14(getCleoHome(), "nexus", NESTED_NEXUS_SENTINEL);
|
|
38975
39810
|
}
|
|
38976
39811
|
function detectAndWarnOnNestedNexus() {
|
|
38977
39812
|
let nestedPath;
|
|
@@ -38980,12 +39815,12 @@ function detectAndWarnOnNestedNexus() {
|
|
|
38980
39815
|
} catch {
|
|
38981
39816
|
return false;
|
|
38982
39817
|
}
|
|
38983
|
-
if (!
|
|
39818
|
+
if (!existsSync14(nestedPath)) return false;
|
|
38984
39819
|
if (_warnedNestedPaths.has(nestedPath)) return false;
|
|
38985
39820
|
_warnedNestedPaths.add(nestedPath);
|
|
38986
39821
|
const canonicalPath = getNexusDbPath();
|
|
38987
|
-
const
|
|
38988
|
-
|
|
39822
|
+
const log9 = getLogger("nexus-sqlite");
|
|
39823
|
+
log9.warn(
|
|
38989
39824
|
{
|
|
38990
39825
|
nestedPath,
|
|
38991
39826
|
canonicalPath,
|
|
@@ -39139,7 +39974,7 @@ function runNexusMigrations(nativeDb, db) {
|
|
|
39139
39974
|
const migrationsFolder = resolveNexusMigrationsFolder();
|
|
39140
39975
|
if (tableExists4(nativeDb, "nexus_nodes") && _nexusDbPath) {
|
|
39141
39976
|
const backupPath = _nexusDbPath.replace(/\.db$/, "-pre-cleo.db.bak");
|
|
39142
|
-
if (!
|
|
39977
|
+
if (!existsSync14(backupPath)) {
|
|
39143
39978
|
try {
|
|
39144
39979
|
copyFileSync4(_nexusDbPath, backupPath);
|
|
39145
39980
|
} catch {
|
|
@@ -39277,10 +40112,10 @@ var init_nexus_sqlite = __esm({
|
|
|
39277
40112
|
|
|
39278
40113
|
// packages/core/src/paths.ts
|
|
39279
40114
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
39280
|
-
import { existsSync as
|
|
40115
|
+
import { existsSync as existsSync15, readFileSync as readFileSync4, statSync as statSync5 } from "node:fs";
|
|
39281
40116
|
import { createRequire as createRequire5 } from "node:module";
|
|
39282
40117
|
import { homedir } from "node:os";
|
|
39283
|
-
import { basename as basename4, dirname as dirname6, join as
|
|
40118
|
+
import { basename as basename4, dirname as dirname6, join as join15, resolve as resolve3 } from "node:path";
|
|
39284
40119
|
import {
|
|
39285
40120
|
computeCanonicalProjectId as _computeCanonicalProjectId,
|
|
39286
40121
|
getCanonicalTemplatesTildePath as _getCanonicalTemplatesTildePath,
|
|
@@ -39370,8 +40205,8 @@ function getCleoDirAbsolute(cwd, opts) {
|
|
|
39370
40205
|
function _resolveProjectByCwdFromNexus(cwd) {
|
|
39371
40206
|
try {
|
|
39372
40207
|
const cleoHome = getCleoHome();
|
|
39373
|
-
const globalDbPath =
|
|
39374
|
-
if (!
|
|
40208
|
+
const globalDbPath = join15(cleoHome, "cleo.db");
|
|
40209
|
+
if (!existsSync15(globalDbPath)) return null;
|
|
39375
40210
|
const start = resolve3(cwd ?? process.cwd());
|
|
39376
40211
|
let current = start;
|
|
39377
40212
|
const DatabaseSync2 = _getDatabaseSyncCtor();
|
|
@@ -39410,7 +40245,7 @@ function _findCleoDirRoot(cwd) {
|
|
|
39410
40245
|
const isDangerousRoot = current === homeRoot || current === "/" || current === "";
|
|
39411
40246
|
if (!isDangerousRoot) {
|
|
39412
40247
|
try {
|
|
39413
|
-
if (statSync5(
|
|
40248
|
+
if (statSync5(join15(current, ".cleo")).isDirectory()) {
|
|
39414
40249
|
return current;
|
|
39415
40250
|
}
|
|
39416
40251
|
} catch {
|
|
@@ -39425,7 +40260,7 @@ function _findCleoDirRoot(cwd) {
|
|
|
39425
40260
|
function resolveCleoDir(cwd) {
|
|
39426
40261
|
const scope = worktreeScope.getStore();
|
|
39427
40262
|
if (scope !== void 0) {
|
|
39428
|
-
return
|
|
40263
|
+
return join15(scope.worktreeRoot, ".cleo");
|
|
39429
40264
|
}
|
|
39430
40265
|
const override = _cleoDirEnvOverride();
|
|
39431
40266
|
if (override !== null) {
|
|
@@ -39433,14 +40268,14 @@ function resolveCleoDir(cwd) {
|
|
|
39433
40268
|
}
|
|
39434
40269
|
const root = _findCleoDirRoot(cwd);
|
|
39435
40270
|
if (root !== null) {
|
|
39436
|
-
return
|
|
40271
|
+
return join15(root, ".cleo");
|
|
39437
40272
|
}
|
|
39438
40273
|
const start = resolve3(cwd ?? process.cwd());
|
|
39439
40274
|
let current = start;
|
|
39440
40275
|
while (true) {
|
|
39441
40276
|
const mainRepo = _resolveMainRepoFromGitlink(current);
|
|
39442
40277
|
if (mainRepo !== null) {
|
|
39443
|
-
return
|
|
40278
|
+
return join15(mainRepo, ".cleo");
|
|
39444
40279
|
}
|
|
39445
40280
|
const parent = dirname6(current);
|
|
39446
40281
|
if (parent === current) {
|
|
@@ -39482,9 +40317,9 @@ function _cwdHasGitAncestor(cwd) {
|
|
|
39482
40317
|
const start = resolve3(cwd ?? process.cwd());
|
|
39483
40318
|
let current = start;
|
|
39484
40319
|
while (true) {
|
|
39485
|
-
const gitMarker =
|
|
40320
|
+
const gitMarker = join15(current, ".git");
|
|
39486
40321
|
try {
|
|
39487
|
-
if (
|
|
40322
|
+
if (existsSync15(gitMarker)) {
|
|
39488
40323
|
return true;
|
|
39489
40324
|
}
|
|
39490
40325
|
} catch {
|
|
@@ -39496,16 +40331,16 @@ function _cwdHasGitAncestor(cwd) {
|
|
|
39496
40331
|
}
|
|
39497
40332
|
function _resolveMainRepoFromGitlink(gitlinkDir) {
|
|
39498
40333
|
try {
|
|
39499
|
-
const gitLinkPath =
|
|
39500
|
-
if (!
|
|
39501
|
-
const
|
|
39502
|
-
if (!
|
|
40334
|
+
const gitLinkPath = join15(gitlinkDir, ".git");
|
|
40335
|
+
if (!existsSync15(gitLinkPath)) return null;
|
|
40336
|
+
const stat3 = statSync5(gitLinkPath);
|
|
40337
|
+
if (!stat3.isFile()) return null;
|
|
39503
40338
|
const gitLinkContent = readFileSync4(gitLinkPath, "utf-8").trim();
|
|
39504
40339
|
const match = gitLinkContent.match(/^gitdir:\s*(.+)$/m);
|
|
39505
40340
|
if (!match) return null;
|
|
39506
40341
|
const gitdir = match[1].trim();
|
|
39507
40342
|
const mainRepo = dirname6(dirname6(dirname6(gitdir)));
|
|
39508
|
-
if (
|
|
40343
|
+
if (existsSync15(join15(mainRepo, ".cleo")) && validateProjectRoot(mainRepo)) {
|
|
39509
40344
|
return mainRepo;
|
|
39510
40345
|
}
|
|
39511
40346
|
} catch {
|
|
@@ -39513,18 +40348,18 @@ function _resolveMainRepoFromGitlink(gitlinkDir) {
|
|
|
39513
40348
|
return null;
|
|
39514
40349
|
}
|
|
39515
40350
|
function validateProjectRoot(candidate) {
|
|
39516
|
-
const cleoDir =
|
|
39517
|
-
if (!
|
|
40351
|
+
const cleoDir = join15(candidate, ".cleo");
|
|
40352
|
+
if (!existsSync15(cleoDir)) {
|
|
39518
40353
|
return false;
|
|
39519
40354
|
}
|
|
39520
|
-
const projectInfoPath =
|
|
39521
|
-
if (
|
|
40355
|
+
const projectInfoPath = join15(cleoDir, "project-info.json");
|
|
40356
|
+
if (existsSync15(projectInfoPath)) {
|
|
39522
40357
|
try {
|
|
39523
40358
|
const raw = readFileSync4(projectInfoPath, "utf-8");
|
|
39524
40359
|
const parsed = JSON.parse(raw);
|
|
39525
40360
|
if (typeof parsed === "object" && parsed !== null && "projectId" in parsed && typeof parsed["projectId"] === "string" && parsed["projectId"] !== "") {
|
|
39526
|
-
const gitMarker =
|
|
39527
|
-
if (
|
|
40361
|
+
const gitMarker = join15(candidate, ".git");
|
|
40362
|
+
if (existsSync15(gitMarker)) {
|
|
39528
40363
|
try {
|
|
39529
40364
|
if (!statSync5(gitMarker).isDirectory()) {
|
|
39530
40365
|
return false;
|
|
@@ -39538,12 +40373,12 @@ function validateProjectRoot(candidate) {
|
|
|
39538
40373
|
} catch {
|
|
39539
40374
|
}
|
|
39540
40375
|
}
|
|
39541
|
-
const gitDir =
|
|
39542
|
-
if (
|
|
40376
|
+
const gitDir = join15(candidate, ".git");
|
|
40377
|
+
if (existsSync15(gitDir)) {
|
|
39543
40378
|
let isRealGitDir = false;
|
|
39544
40379
|
try {
|
|
39545
|
-
const
|
|
39546
|
-
isRealGitDir =
|
|
40380
|
+
const stat3 = statSync5(gitDir);
|
|
40381
|
+
isRealGitDir = stat3.isDirectory();
|
|
39547
40382
|
} catch {
|
|
39548
40383
|
isRealGitDir = false;
|
|
39549
40384
|
}
|
|
@@ -39586,16 +40421,16 @@ function getProjectRoot(cwd) {
|
|
|
39586
40421
|
const homeRoot = homedir();
|
|
39587
40422
|
const skippedCleoDirs = [];
|
|
39588
40423
|
while (true) {
|
|
39589
|
-
const cleoDir =
|
|
39590
|
-
const gitDir =
|
|
40424
|
+
const cleoDir = join15(current, ".cleo");
|
|
40425
|
+
const gitDir = join15(current, ".git");
|
|
39591
40426
|
const isDangerousRoot = current === homeRoot || current === "/" || current === "";
|
|
39592
|
-
if (
|
|
40427
|
+
if (existsSync15(cleoDir) && !isDangerousRoot) {
|
|
39593
40428
|
if (validateProjectRoot(current)) {
|
|
39594
40429
|
return current;
|
|
39595
40430
|
}
|
|
39596
40431
|
skippedCleoDirs.push(current);
|
|
39597
40432
|
}
|
|
39598
|
-
if (
|
|
40433
|
+
if (existsSync15(gitDir) && !isDangerousRoot) {
|
|
39599
40434
|
let isRealGitDir = false;
|
|
39600
40435
|
try {
|
|
39601
40436
|
isRealGitDir = statSync5(gitDir).isDirectory();
|
|
@@ -39640,18 +40475,18 @@ function resolveOrCwd(maybeRoot) {
|
|
|
39640
40475
|
return getProjectRoot();
|
|
39641
40476
|
}
|
|
39642
40477
|
function getConfigPath(cwd) {
|
|
39643
|
-
return
|
|
40478
|
+
return join15(_resolveCleoDir(cwd), "config.json");
|
|
39644
40479
|
}
|
|
39645
40480
|
function getGlobalConfigPath() {
|
|
39646
|
-
return
|
|
40481
|
+
return join15(getCleoHome(), "config.json");
|
|
39647
40482
|
}
|
|
39648
40483
|
function isAbsolutePath(path) {
|
|
39649
40484
|
return _isAbsolutePath(path);
|
|
39650
40485
|
}
|
|
39651
40486
|
function _readProjectNameFromInfo(projectRoot) {
|
|
39652
40487
|
try {
|
|
39653
|
-
const infoPath =
|
|
39654
|
-
if (!
|
|
40488
|
+
const infoPath = join15(projectRoot, ".cleo", "project-info.json");
|
|
40489
|
+
if (!existsSync15(infoPath)) return void 0;
|
|
39655
40490
|
const raw = readFileSync4(infoPath, "utf-8");
|
|
39656
40491
|
const data = JSON.parse(raw);
|
|
39657
40492
|
return typeof data.name === "string" && data.name.length > 0 ? data.name : void 0;
|
|
@@ -39677,8 +40512,8 @@ async function registerProjectOnEncounter(projectRoot, infoProjectId) {
|
|
|
39677
40512
|
if (existingRows.length > 0) {
|
|
39678
40513
|
const existingPath = existingRows[0].projectPath;
|
|
39679
40514
|
if (existingPath !== resolvedPath) {
|
|
39680
|
-
const newBrainDbPath =
|
|
39681
|
-
const newTasksDbPath =
|
|
40515
|
+
const newBrainDbPath = join15(resolvedPath, ".cleo", "brain.db");
|
|
40516
|
+
const newTasksDbPath = join15(resolvedPath, ".cleo", "tasks.db");
|
|
39682
40517
|
await db.update(projectRegistry2).set({
|
|
39683
40518
|
projectPath: resolvedPath,
|
|
39684
40519
|
projectHash: generateProjectHash2(resolvedPath),
|
|
@@ -39709,8 +40544,8 @@ async function registerProjectOnEncounter(projectRoot, infoProjectId) {
|
|
|
39709
40544
|
lastSync: now,
|
|
39710
40545
|
taskCount: 0,
|
|
39711
40546
|
labelsJson: "[]",
|
|
39712
|
-
brainDbPath:
|
|
39713
|
-
tasksDbPath:
|
|
40547
|
+
brainDbPath: join15(resolvedPath, ".cleo", "brain.db"),
|
|
40548
|
+
tasksDbPath: join15(resolvedPath, ".cleo", "tasks.db"),
|
|
39714
40549
|
statsJson: "{}"
|
|
39715
40550
|
}).onConflictDoNothing();
|
|
39716
40551
|
const aliases = canonicalResult.legacyAliases ?? [];
|
|
@@ -39743,46 +40578,46 @@ var init_paths = __esm({
|
|
|
39743
40578
|
// packages/core/src/store/file-utils.ts
|
|
39744
40579
|
import { randomBytes } from "node:crypto";
|
|
39745
40580
|
import {
|
|
39746
|
-
existsSync as
|
|
39747
|
-
mkdirSync as
|
|
39748
|
-
readdirSync as
|
|
40581
|
+
existsSync as existsSync16,
|
|
40582
|
+
mkdirSync as mkdirSync5,
|
|
40583
|
+
readdirSync as readdirSync5,
|
|
39749
40584
|
readFileSync as readFileSync5,
|
|
39750
40585
|
renameSync as renameSync3,
|
|
39751
40586
|
unlinkSync as unlinkSync3,
|
|
39752
|
-
writeFileSync as
|
|
40587
|
+
writeFileSync as writeFileSync4
|
|
39753
40588
|
} from "node:fs";
|
|
39754
|
-
import { basename as basename5, dirname as dirname7, join as
|
|
39755
|
-
import * as
|
|
40589
|
+
import { basename as basename5, dirname as dirname7, join as join16 } from "node:path";
|
|
40590
|
+
import * as lockfile3 from "proper-lockfile";
|
|
39756
40591
|
function rotateBackup(filePath, mode) {
|
|
39757
40592
|
const dir = dirname7(filePath);
|
|
39758
40593
|
const name15 = basename5(filePath);
|
|
39759
|
-
const backupDir =
|
|
40594
|
+
const backupDir = join16(dir, ".backups");
|
|
39760
40595
|
const dirMode = typeof mode === "number" ? modeToDirMode(mode) : void 0;
|
|
39761
|
-
if (!
|
|
39762
|
-
|
|
40596
|
+
if (!existsSync16(backupDir)) {
|
|
40597
|
+
mkdirSync5(backupDir, { recursive: true, mode: dirMode });
|
|
39763
40598
|
}
|
|
39764
40599
|
for (let i = MAX_BACKUPS; i >= 1; i--) {
|
|
39765
|
-
const current =
|
|
40600
|
+
const current = join16(backupDir, `${name15}.${i}`);
|
|
39766
40601
|
if (i === MAX_BACKUPS) {
|
|
39767
40602
|
try {
|
|
39768
40603
|
unlinkSync3(current);
|
|
39769
40604
|
} catch {
|
|
39770
40605
|
}
|
|
39771
40606
|
} else {
|
|
39772
|
-
const next =
|
|
40607
|
+
const next = join16(backupDir, `${name15}.${i + 1}`);
|
|
39773
40608
|
try {
|
|
39774
|
-
if (
|
|
40609
|
+
if (existsSync16(current)) renameSync3(current, next);
|
|
39775
40610
|
} catch {
|
|
39776
40611
|
}
|
|
39777
40612
|
}
|
|
39778
40613
|
}
|
|
39779
40614
|
try {
|
|
39780
40615
|
const content = readFileSync5(filePath, "utf-8");
|
|
39781
|
-
const backupPath =
|
|
40616
|
+
const backupPath = join16(backupDir, `${name15}.1`);
|
|
39782
40617
|
if (typeof mode === "number") {
|
|
39783
|
-
|
|
40618
|
+
writeFileSync4(backupPath, content, { encoding: "utf-8", mode });
|
|
39784
40619
|
} else {
|
|
39785
|
-
|
|
40620
|
+
writeFileSync4(backupPath, content, "utf-8");
|
|
39786
40621
|
}
|
|
39787
40622
|
} catch {
|
|
39788
40623
|
}
|
|
@@ -39799,15 +40634,15 @@ function writeJsonFileAtomic(filePath, data, optsOrIndent = 2) {
|
|
|
39799
40634
|
const indent = opts.indent ?? 2;
|
|
39800
40635
|
const mode = opts.mode;
|
|
39801
40636
|
const dir = dirname7(filePath);
|
|
39802
|
-
const tempPath =
|
|
40637
|
+
const tempPath = join16(dir, `.${basename5(filePath)}.${randomBytes(6).toString("hex")}.tmp`);
|
|
39803
40638
|
const content = JSON.stringify(data, null, indent) + "\n";
|
|
39804
40639
|
if (typeof mode === "number") {
|
|
39805
|
-
|
|
40640
|
+
writeFileSync4(tempPath, content, { encoding: "utf-8", mode });
|
|
39806
40641
|
} else {
|
|
39807
|
-
|
|
40642
|
+
writeFileSync4(tempPath, content, "utf-8");
|
|
39808
40643
|
}
|
|
39809
40644
|
try {
|
|
39810
|
-
if (
|
|
40645
|
+
if (existsSync16(filePath)) {
|
|
39811
40646
|
rotateBackup(filePath, mode);
|
|
39812
40647
|
}
|
|
39813
40648
|
renameSync3(tempPath, filePath);
|
|
@@ -39833,19 +40668,19 @@ function readJsonFile(filePath) {
|
|
|
39833
40668
|
async function withLock2(filePath, transform2, opts = {}) {
|
|
39834
40669
|
const dir = dirname7(filePath);
|
|
39835
40670
|
const dirMode = typeof opts.mode === "number" ? modeToDirMode(opts.mode) : void 0;
|
|
39836
|
-
if (!
|
|
39837
|
-
|
|
40671
|
+
if (!existsSync16(dir)) {
|
|
40672
|
+
mkdirSync5(dir, { recursive: true, mode: dirMode });
|
|
39838
40673
|
}
|
|
39839
|
-
if (!
|
|
40674
|
+
if (!existsSync16(filePath)) {
|
|
39840
40675
|
if (typeof opts.mode === "number") {
|
|
39841
|
-
|
|
40676
|
+
writeFileSync4(filePath, "", { encoding: "utf-8", mode: opts.mode });
|
|
39842
40677
|
} else {
|
|
39843
|
-
|
|
40678
|
+
writeFileSync4(filePath, "", "utf-8");
|
|
39844
40679
|
}
|
|
39845
40680
|
}
|
|
39846
40681
|
let release;
|
|
39847
40682
|
try {
|
|
39848
|
-
release = await
|
|
40683
|
+
release = await lockfile3.lock(filePath, LOCK_OPTIONS);
|
|
39849
40684
|
const current = readJsonFile(filePath);
|
|
39850
40685
|
const updated = transform2(current);
|
|
39851
40686
|
writeJsonFileAtomic(filePath, updated, { mode: opts.mode });
|
|
@@ -39858,15 +40693,15 @@ async function withLock2(filePath, transform2, opts = {}) {
|
|
|
39858
40693
|
}
|
|
39859
40694
|
async function withFileLock(filePath, operation) {
|
|
39860
40695
|
const dir = dirname7(filePath);
|
|
39861
|
-
if (!
|
|
39862
|
-
|
|
40696
|
+
if (!existsSync16(dir)) {
|
|
40697
|
+
mkdirSync5(dir, { recursive: true });
|
|
39863
40698
|
}
|
|
39864
|
-
if (!
|
|
39865
|
-
|
|
40699
|
+
if (!existsSync16(filePath)) {
|
|
40700
|
+
writeFileSync4(filePath, "", "utf-8");
|
|
39866
40701
|
}
|
|
39867
40702
|
let release;
|
|
39868
40703
|
try {
|
|
39869
|
-
release = await
|
|
40704
|
+
release = await lockfile3.lock(filePath, LOCK_OPTIONS);
|
|
39870
40705
|
return await operation();
|
|
39871
40706
|
} finally {
|
|
39872
40707
|
if (release) {
|
|
@@ -39895,10 +40730,10 @@ var init_file_utils = __esm({
|
|
|
39895
40730
|
|
|
39896
40731
|
// packages/core/src/llm/credential-removal.ts
|
|
39897
40732
|
import { unlinkSync as unlinkSync4 } from "node:fs";
|
|
39898
|
-
import { join as
|
|
39899
|
-
import { getCleoHome as
|
|
40733
|
+
import { join as join17 } from "node:path";
|
|
40734
|
+
import { getCleoHome as getCleoHome3 } from "@cleocode/paths";
|
|
39900
40735
|
function suppressionStatePath() {
|
|
39901
|
-
return
|
|
40736
|
+
return join17(getCleoHome3(), SUPPRESSION_FILENAME);
|
|
39902
40737
|
}
|
|
39903
40738
|
function readSuppressionFile() {
|
|
39904
40739
|
const data = readJsonFile(suppressionStatePath());
|
|
@@ -40002,7 +40837,7 @@ var init_credential_removal = __esm({
|
|
|
40002
40837
|
sourceId: "cleo-pkce",
|
|
40003
40838
|
description: "Delete CLEO-issued PKCE token at <CLEO_HOME>/anthropic-oauth.json",
|
|
40004
40839
|
async remove() {
|
|
40005
|
-
const path =
|
|
40840
|
+
const path = join17(getCleoHome3(), "anthropic-oauth.json");
|
|
40006
40841
|
const cleaned = [];
|
|
40007
40842
|
try {
|
|
40008
40843
|
unlinkSync4(path);
|
|
@@ -40081,7 +40916,7 @@ __export(config_exports, {
|
|
|
40081
40916
|
parseConfigValue: () => parseConfigValue,
|
|
40082
40917
|
setConfigValue: () => setConfigValue
|
|
40083
40918
|
});
|
|
40084
|
-
import { existsSync as
|
|
40919
|
+
import { existsSync as existsSync17 } from "node:fs";
|
|
40085
40920
|
function getNestedValue(obj, path) {
|
|
40086
40921
|
const parts = path.split(".");
|
|
40087
40922
|
let current = obj;
|
|
@@ -40129,7 +40964,7 @@ function parseEnvValue(value) {
|
|
|
40129
40964
|
return value;
|
|
40130
40965
|
}
|
|
40131
40966
|
async function loadConfig(cwd) {
|
|
40132
|
-
let merged = JSON.parse(JSON.stringify(
|
|
40967
|
+
let merged = JSON.parse(JSON.stringify(DEFAULTS2));
|
|
40133
40968
|
const globalConfig2 = await readJson(getGlobalConfigPath());
|
|
40134
40969
|
if (globalConfig2) {
|
|
40135
40970
|
merged = deepMerge(merged, globalConfig2);
|
|
@@ -40175,7 +41010,7 @@ async function getConfigValue(path, cwd) {
|
|
|
40175
41010
|
return { value: val, source: "global" };
|
|
40176
41011
|
}
|
|
40177
41012
|
}
|
|
40178
|
-
const defaultVal = getNestedValue({ ...
|
|
41013
|
+
const defaultVal = getNestedValue({ ...DEFAULTS2 }, path);
|
|
40179
41014
|
return { value: defaultVal, source: "default" };
|
|
40180
41015
|
}
|
|
40181
41016
|
async function getRawConfigValue(key, cwd) {
|
|
@@ -40203,7 +41038,7 @@ function parseConfigValue(value) {
|
|
|
40203
41038
|
}
|
|
40204
41039
|
async function setConfigValue(key, value, cwd, opts) {
|
|
40205
41040
|
const configPath = opts?.global ? getGlobalConfigPath() : getConfigPath(cwd);
|
|
40206
|
-
if (!
|
|
41041
|
+
if (!existsSync17(configPath)) {
|
|
40207
41042
|
await atomicWriteJson(configPath, {});
|
|
40208
41043
|
}
|
|
40209
41044
|
const config2 = await readJson(configPath) ?? {};
|
|
@@ -40215,7 +41050,7 @@ async function setConfigValue(key, value, cwd, opts) {
|
|
|
40215
41050
|
async function applyStrictnessPreset(preset, cwd, opts) {
|
|
40216
41051
|
const definition = STRICTNESS_PRESETS[preset];
|
|
40217
41052
|
const configPath = opts?.global ? getGlobalConfigPath() : getConfigPath(cwd);
|
|
40218
|
-
if (!
|
|
41053
|
+
if (!existsSync17(configPath)) {
|
|
40219
41054
|
await atomicWriteJson(configPath, {});
|
|
40220
41055
|
}
|
|
40221
41056
|
const config2 = await readJson(configPath) ?? {};
|
|
@@ -40239,7 +41074,7 @@ function listStrictnessPresets() {
|
|
|
40239
41074
|
})
|
|
40240
41075
|
);
|
|
40241
41076
|
}
|
|
40242
|
-
var
|
|
41077
|
+
var DEFAULTS2, ENV_MAP, STRICTNESS_PRESETS;
|
|
40243
41078
|
var init_config = __esm({
|
|
40244
41079
|
"packages/core/src/config.ts"() {
|
|
40245
41080
|
"use strict";
|
|
@@ -40247,7 +41082,7 @@ var init_config = __esm({
|
|
|
40247
41082
|
init_paths();
|
|
40248
41083
|
init_atomic();
|
|
40249
41084
|
init_json2();
|
|
40250
|
-
|
|
41085
|
+
DEFAULTS2 = {
|
|
40251
41086
|
version: "2.10.0",
|
|
40252
41087
|
output: {
|
|
40253
41088
|
defaultFormat: "json",
|
|
@@ -40422,10 +41257,10 @@ var init_config = __esm({
|
|
|
40422
41257
|
// packages/core/src/llm/credential-seeders/claude-code-seeder.ts
|
|
40423
41258
|
import { readFileSync as readFileSync6 } from "node:fs";
|
|
40424
41259
|
import { homedir as homedir2 } from "node:os";
|
|
40425
|
-
import { join as
|
|
41260
|
+
import { join as join18 } from "node:path";
|
|
40426
41261
|
function defaultReadCredentialFile() {
|
|
40427
41262
|
try {
|
|
40428
|
-
return readFileSync6(
|
|
41263
|
+
return readFileSync6(join18(homedir2(), ".claude", ".credentials.json"), "utf-8");
|
|
40429
41264
|
} catch {
|
|
40430
41265
|
return null;
|
|
40431
41266
|
}
|
|
@@ -40520,9 +41355,9 @@ var init_claude_code_seeder = __esm({
|
|
|
40520
41355
|
|
|
40521
41356
|
// packages/core/src/llm/credential-seeders/cleo-pkce-seeder.ts
|
|
40522
41357
|
import { readFileSync as readFileSync7 } from "node:fs";
|
|
40523
|
-
import { join as
|
|
41358
|
+
import { join as join19 } from "node:path";
|
|
40524
41359
|
function defaultCleoPkcePath() {
|
|
40525
|
-
return
|
|
41360
|
+
return join19(getCleoHome(), "anthropic-oauth.json");
|
|
40526
41361
|
}
|
|
40527
41362
|
function defaultReadCredentialFile2() {
|
|
40528
41363
|
try {
|
|
@@ -40593,13 +41428,13 @@ var init_cleo_pkce_seeder = __esm({
|
|
|
40593
41428
|
});
|
|
40594
41429
|
|
|
40595
41430
|
// packages/core/src/llm/credential-seeders/codex-cli-seeder.ts
|
|
40596
|
-
import { existsSync as
|
|
41431
|
+
import { existsSync as existsSync18, readFileSync as readFileSync8 } from "node:fs";
|
|
40597
41432
|
import { homedir as homedir3 } from "node:os";
|
|
40598
|
-
import { join as
|
|
41433
|
+
import { join as join20 } from "node:path";
|
|
40599
41434
|
function getCodexAuthPath() {
|
|
40600
41435
|
const codexHome = (process.env["CODEX_HOME"] ?? "").trim();
|
|
40601
|
-
const root = codexHome ||
|
|
40602
|
-
return
|
|
41436
|
+
const root = codexHome || join20(homedir3(), ".codex");
|
|
41437
|
+
return join20(root, "auth.json");
|
|
40603
41438
|
}
|
|
40604
41439
|
var CodexCliSeeder, codexCliSeeder;
|
|
40605
41440
|
var init_codex_cli_seeder = __esm({
|
|
@@ -40618,7 +41453,7 @@ var init_codex_cli_seeder = __esm({
|
|
|
40618
41453
|
*/
|
|
40619
41454
|
async seed() {
|
|
40620
41455
|
const authPath = getCodexAuthPath();
|
|
40621
|
-
if (!
|
|
41456
|
+
if (!existsSync18(authPath)) {
|
|
40622
41457
|
return { entries: [] };
|
|
40623
41458
|
}
|
|
40624
41459
|
let raw;
|
|
@@ -40813,11 +41648,11 @@ var init_google_pkce = __esm({
|
|
|
40813
41648
|
});
|
|
40814
41649
|
|
|
40815
41650
|
// packages/core/src/llm/credential-seeders/gemini-cli-seeder.ts
|
|
40816
|
-
import { existsSync as
|
|
40817
|
-
import { join as
|
|
40818
|
-
import { getCleoHome as
|
|
41651
|
+
import { existsSync as existsSync19, readFileSync as readFileSync9 } from "node:fs";
|
|
41652
|
+
import { join as join21 } from "node:path";
|
|
41653
|
+
import { getCleoHome as getCleoHome4 } from "@cleocode/paths";
|
|
40819
41654
|
function getGoogleOauthPath() {
|
|
40820
|
-
return
|
|
41655
|
+
return join21(getCleoHome4(), "google_oauth.json");
|
|
40821
41656
|
}
|
|
40822
41657
|
function parseGoogleOauthFile(raw) {
|
|
40823
41658
|
let json3;
|
|
@@ -40862,7 +41697,7 @@ var init_gemini_cli_seeder = __esm({
|
|
|
40862
41697
|
*/
|
|
40863
41698
|
async seed() {
|
|
40864
41699
|
const path = getGoogleOauthPath();
|
|
40865
|
-
if (!
|
|
41700
|
+
if (!existsSync19(path)) {
|
|
40866
41701
|
return { entries: [] };
|
|
40867
41702
|
}
|
|
40868
41703
|
let raw;
|
|
@@ -41087,23 +41922,23 @@ var init_credential_seeders = __esm({
|
|
|
41087
41922
|
|
|
41088
41923
|
// packages/core/src/llm/global-config-migration.ts
|
|
41089
41924
|
import {
|
|
41090
|
-
existsSync as
|
|
41091
|
-
mkdirSync as
|
|
41925
|
+
existsSync as existsSync20,
|
|
41926
|
+
mkdirSync as mkdirSync6,
|
|
41092
41927
|
readFileSync as readFileSync10,
|
|
41093
41928
|
renameSync as renameSync4,
|
|
41094
41929
|
unlinkSync as unlinkSync5,
|
|
41095
|
-
writeFileSync as
|
|
41930
|
+
writeFileSync as writeFileSync5
|
|
41096
41931
|
} from "node:fs";
|
|
41097
|
-
import { join as
|
|
41098
|
-
import { getCleoHome as
|
|
41932
|
+
import { join as join22 } from "node:path";
|
|
41933
|
+
import { getCleoHome as getCleoHome5, getCleoPlatformPaths as getCleoPlatformPaths2 } from "@cleocode/paths";
|
|
41099
41934
|
function configDirGlobalConfigPath() {
|
|
41100
|
-
return
|
|
41935
|
+
return join22(getCleoPlatformPaths2().config, GLOBAL_CONFIG_FILENAME);
|
|
41101
41936
|
}
|
|
41102
41937
|
function legacyGlobalConfigPath() {
|
|
41103
|
-
return
|
|
41938
|
+
return join22(getCleoHome5(), GLOBAL_CONFIG_FILENAME);
|
|
41104
41939
|
}
|
|
41105
41940
|
function migrationMarkerPath() {
|
|
41106
|
-
return
|
|
41941
|
+
return join22(getCleoHome5(), MIGRATIONS_SUBDIR, MIGRATION_MARKER);
|
|
41107
41942
|
}
|
|
41108
41943
|
function tempTargetPath() {
|
|
41109
41944
|
return `${configDirGlobalConfigPath()}.tmp`;
|
|
@@ -41113,12 +41948,12 @@ function migrateGlobalConfigToConfigDir() {
|
|
|
41113
41948
|
const source = legacyGlobalConfigPath();
|
|
41114
41949
|
const target = configDirGlobalConfigPath();
|
|
41115
41950
|
const marker16 = migrationMarkerPath();
|
|
41116
|
-
if (
|
|
41117
|
-
if (!
|
|
41951
|
+
if (existsSync20(marker16)) return false;
|
|
41952
|
+
if (!existsSync20(source)) {
|
|
41118
41953
|
stampMarker(marker16);
|
|
41119
41954
|
return false;
|
|
41120
41955
|
}
|
|
41121
|
-
if (
|
|
41956
|
+
if (existsSync20(target)) {
|
|
41122
41957
|
backupSourceQuiet(source);
|
|
41123
41958
|
stampMarker(marker16);
|
|
41124
41959
|
return false;
|
|
@@ -41132,9 +41967,9 @@ function migrateGlobalConfigToConfigDir() {
|
|
|
41132
41967
|
);
|
|
41133
41968
|
return false;
|
|
41134
41969
|
}
|
|
41135
|
-
|
|
41970
|
+
mkdirSync6(getCleoPlatformPaths2().config, { recursive: true });
|
|
41136
41971
|
const temp = tempTargetPath();
|
|
41137
|
-
|
|
41972
|
+
writeFileSync5(temp, raw, { mode: 420 });
|
|
41138
41973
|
renameSync4(temp, target);
|
|
41139
41974
|
stampMarker(marker16);
|
|
41140
41975
|
try {
|
|
@@ -41151,7 +41986,7 @@ function migrateGlobalConfigToConfigDir() {
|
|
|
41151
41986
|
} catch (err) {
|
|
41152
41987
|
try {
|
|
41153
41988
|
const temp = tempTargetPath();
|
|
41154
|
-
if (
|
|
41989
|
+
if (existsSync20(temp)) unlinkSync5(temp);
|
|
41155
41990
|
} catch {
|
|
41156
41991
|
}
|
|
41157
41992
|
console.error(
|
|
@@ -41162,8 +41997,8 @@ function migrateGlobalConfigToConfigDir() {
|
|
|
41162
41997
|
}
|
|
41163
41998
|
function stampMarker(markerPath) {
|
|
41164
41999
|
try {
|
|
41165
|
-
|
|
41166
|
-
|
|
42000
|
+
mkdirSync6(join22(getCleoHome5(), MIGRATIONS_SUBDIR), { recursive: true });
|
|
42001
|
+
writeFileSync5(markerPath, `${(/* @__PURE__ */ new Date()).toISOString()}
|
|
41167
42002
|
`, { mode: 420 });
|
|
41168
42003
|
} catch {
|
|
41169
42004
|
}
|
|
@@ -41171,7 +42006,7 @@ function stampMarker(markerPath) {
|
|
|
41171
42006
|
function backupSourceQuiet(source) {
|
|
41172
42007
|
try {
|
|
41173
42008
|
const backup = `${source}${BACKUP_SUFFIX}`;
|
|
41174
|
-
if (
|
|
42009
|
+
if (existsSync20(backup)) return;
|
|
41175
42010
|
renameSync4(source, backup);
|
|
41176
42011
|
} catch {
|
|
41177
42012
|
}
|
|
@@ -41194,20 +42029,20 @@ var init_global_config_migration = __esm({
|
|
|
41194
42029
|
});
|
|
41195
42030
|
|
|
41196
42031
|
// packages/core/src/llm/legacy-flat-key-import.ts
|
|
41197
|
-
import { existsSync as
|
|
41198
|
-
import { join as
|
|
41199
|
-
import { getCleoHome as
|
|
42032
|
+
import { existsSync as existsSync21, readFileSync as readFileSync11, renameSync as renameSync5, writeFileSync as writeFileSync6 } from "node:fs";
|
|
42033
|
+
import { join as join23 } from "node:path";
|
|
42034
|
+
import { getCleoHome as getCleoHome6 } from "@cleocode/paths";
|
|
41200
42035
|
function migrationPaths() {
|
|
41201
|
-
const home =
|
|
42036
|
+
const home = getCleoHome6();
|
|
41202
42037
|
return {
|
|
41203
|
-
flatPath:
|
|
41204
|
-
bakPath:
|
|
41205
|
-
markerPath:
|
|
42038
|
+
flatPath: join23(home, "anthropic-key"),
|
|
42039
|
+
bakPath: join23(home, `anthropic-key${LEGACY_FLAT_KEY_BAK_SUFFIX}`),
|
|
42040
|
+
markerPath: join23(home, LEGACY_FLAT_KEY_MARKER)
|
|
41206
42041
|
};
|
|
41207
42042
|
}
|
|
41208
42043
|
function writeMarker(markerPath) {
|
|
41209
42044
|
try {
|
|
41210
|
-
|
|
42045
|
+
writeFileSync6(markerPath, `${(/* @__PURE__ */ new Date()).toISOString()}
|
|
41211
42046
|
`, {
|
|
41212
42047
|
encoding: "utf-8",
|
|
41213
42048
|
flag: "wx",
|
|
@@ -41223,7 +42058,7 @@ function writeMarker(markerPath) {
|
|
|
41223
42058
|
}
|
|
41224
42059
|
function readFlatKey(flatPath) {
|
|
41225
42060
|
try {
|
|
41226
|
-
if (!
|
|
42061
|
+
if (!existsSync21(flatPath)) return null;
|
|
41227
42062
|
const raw = readFileSync11(flatPath, "utf-8").trim();
|
|
41228
42063
|
return raw || null;
|
|
41229
42064
|
} catch (err) {
|
|
@@ -41236,7 +42071,7 @@ function readFlatKey(flatPath) {
|
|
|
41236
42071
|
}
|
|
41237
42072
|
async function importLegacyFlatAnthropicKey() {
|
|
41238
42073
|
const { flatPath, bakPath, markerPath } = migrationPaths();
|
|
41239
|
-
if (
|
|
42074
|
+
if (existsSync21(markerPath)) {
|
|
41240
42075
|
return { status: "marker-present", flatPath, bakPath: null, markerPath };
|
|
41241
42076
|
}
|
|
41242
42077
|
let existing = null;
|
|
@@ -41253,7 +42088,7 @@ async function importLegacyFlatAnthropicKey() {
|
|
|
41253
42088
|
writeMarker(markerPath);
|
|
41254
42089
|
return { status: "already-imported", flatPath, bakPath: null, markerPath };
|
|
41255
42090
|
}
|
|
41256
|
-
if (!
|
|
42091
|
+
if (!existsSync21(flatPath)) {
|
|
41257
42092
|
writeMarker(markerPath);
|
|
41258
42093
|
return { status: "no-flat-file", flatPath, bakPath: null, markerPath };
|
|
41259
42094
|
}
|
|
@@ -41330,15 +42165,15 @@ __export(credentials_exports, {
|
|
|
41330
42165
|
resolveProviderStatus: () => resolveProviderStatus,
|
|
41331
42166
|
storeAnthropicApiKey: () => storeAnthropicApiKey
|
|
41332
42167
|
});
|
|
41333
|
-
import { existsSync as
|
|
41334
|
-
import { join as
|
|
41335
|
-
import { getCleoHome as
|
|
42168
|
+
import { existsSync as existsSync22, mkdirSync as mkdirSync7, readFileSync as readFileSync12, writeFileSync as writeFileSync7 } from "node:fs";
|
|
42169
|
+
import { join as join24 } from "node:path";
|
|
42170
|
+
import { getCleoHome as getCleoHome7 } from "@cleocode/paths";
|
|
41336
42171
|
function globalConfigPath() {
|
|
41337
42172
|
return configDirGlobalConfigPath();
|
|
41338
42173
|
}
|
|
41339
42174
|
function projectConfigPath(projectRoot) {
|
|
41340
42175
|
const cleoDir = process.env["CLEO_DIR"] ?? ".cleo";
|
|
41341
|
-
return
|
|
42176
|
+
return join24(projectRoot, cleoDir, "config.json");
|
|
41342
42177
|
}
|
|
41343
42178
|
function readGlobalProviderKey(provider) {
|
|
41344
42179
|
ensureGlobalConfigMigrated();
|
|
@@ -41348,8 +42183,8 @@ function readGlobalProviderKey(provider) {
|
|
|
41348
42183
|
}
|
|
41349
42184
|
function readFlatAnthropicKey() {
|
|
41350
42185
|
try {
|
|
41351
|
-
const keyFile =
|
|
41352
|
-
if (!
|
|
42186
|
+
const keyFile = join24(getCleoHome7(), "anthropic-key");
|
|
42187
|
+
if (!existsSync22(keyFile)) return null;
|
|
41353
42188
|
const stored = readFileSync12(keyFile, "utf-8").trim();
|
|
41354
42189
|
return stored || null;
|
|
41355
42190
|
} catch {
|
|
@@ -41358,7 +42193,7 @@ function readFlatAnthropicKey() {
|
|
|
41358
42193
|
}
|
|
41359
42194
|
function readProviderKeyFromConfig(configFile, provider) {
|
|
41360
42195
|
try {
|
|
41361
|
-
if (!
|
|
42196
|
+
if (!existsSync22(configFile)) return null;
|
|
41362
42197
|
const raw = readFileSync12(configFile, "utf-8");
|
|
41363
42198
|
const config2 = JSON.parse(raw);
|
|
41364
42199
|
const llm = config2["llm"];
|
|
@@ -41530,12 +42365,12 @@ function resolveProviderStatus(provider) {
|
|
|
41530
42365
|
return { provider, resolvedSource, hasCredential: true };
|
|
41531
42366
|
}
|
|
41532
42367
|
function storeAnthropicApiKey(apiKey) {
|
|
41533
|
-
const dir =
|
|
41534
|
-
if (!
|
|
41535
|
-
|
|
42368
|
+
const dir = getCleoHome7();
|
|
42369
|
+
if (!existsSync22(dir)) {
|
|
42370
|
+
mkdirSync7(dir, { recursive: true });
|
|
41536
42371
|
}
|
|
41537
|
-
const keyFile =
|
|
41538
|
-
|
|
42372
|
+
const keyFile = join24(dir, "anthropic-key");
|
|
42373
|
+
writeFileSync7(keyFile, apiKey.trim(), { mode: 384 });
|
|
41539
42374
|
}
|
|
41540
42375
|
function clearAnthropicKeyCache() {
|
|
41541
42376
|
}
|
|
@@ -41598,17 +42433,17 @@ __export(credentials_store_exports, {
|
|
|
41598
42433
|
pickCredentialForProviderSync: () => pickCredentialForProviderSync,
|
|
41599
42434
|
removeCredential: () => removeCredential
|
|
41600
42435
|
});
|
|
41601
|
-
import { chmodSync, existsSync as
|
|
41602
|
-
import { dirname as dirname8, join as
|
|
41603
|
-
import { getCleoHome as
|
|
42436
|
+
import { chmodSync, existsSync as existsSync23, mkdirSync as mkdirSync8, readFileSync as readFileSync13, statSync as statSync6, writeFileSync as writeFileSync8 } from "node:fs";
|
|
42437
|
+
import { dirname as dirname8, join as join25 } from "node:path";
|
|
42438
|
+
import { getCleoHome as getCleoHome8 } from "@cleocode/paths";
|
|
41604
42439
|
function credentialsStorePath() {
|
|
41605
|
-
return
|
|
42440
|
+
return join25(getCleoHome8(), "llm-credentials.json");
|
|
41606
42441
|
}
|
|
41607
42442
|
function ensureFileInitialized(path) {
|
|
41608
|
-
if (
|
|
42443
|
+
if (existsSync23(path)) return;
|
|
41609
42444
|
const dir = dirname8(path);
|
|
41610
|
-
if (!
|
|
41611
|
-
|
|
42445
|
+
if (!existsSync23(dir)) {
|
|
42446
|
+
mkdirSync8(dir, { recursive: true, mode: 448 });
|
|
41612
42447
|
} else {
|
|
41613
42448
|
try {
|
|
41614
42449
|
chmodSync(dir, 448);
|
|
@@ -41616,7 +42451,7 @@ function ensureFileInitialized(path) {
|
|
|
41616
42451
|
}
|
|
41617
42452
|
}
|
|
41618
42453
|
try {
|
|
41619
|
-
|
|
42454
|
+
writeFileSync8(path, `${JSON.stringify(emptyStore(), null, 2)}
|
|
41620
42455
|
`, {
|
|
41621
42456
|
encoding: "utf-8",
|
|
41622
42457
|
flag: "wx",
|
|
@@ -41959,13 +42794,13 @@ var init_gemini = __esm({
|
|
|
41959
42794
|
|
|
41960
42795
|
// packages/core/src/llm/stable-device-id.ts
|
|
41961
42796
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
41962
|
-
import { existsSync as
|
|
41963
|
-
import { dirname as dirname9, join as
|
|
41964
|
-
import { getCleoHome as
|
|
42797
|
+
import { existsSync as existsSync24, mkdirSync as mkdirSync9, readFileSync as readFileSync14, renameSync as renameSync6, writeFileSync as writeFileSync9 } from "node:fs";
|
|
42798
|
+
import { dirname as dirname9, join as join26 } from "node:path";
|
|
42799
|
+
import { getCleoHome as getCleoHome9 } from "@cleocode/paths";
|
|
41965
42800
|
function getStableDeviceId() {
|
|
41966
42801
|
if (_cachedDeviceId !== null) return _cachedDeviceId;
|
|
41967
|
-
const path =
|
|
41968
|
-
if (
|
|
42802
|
+
const path = join26(getCleoHome9(), DEVICE_ID_FILE);
|
|
42803
|
+
if (existsSync24(path)) {
|
|
41969
42804
|
try {
|
|
41970
42805
|
const raw = readFileSync14(path, "utf-8").trim();
|
|
41971
42806
|
if (raw.length > 0) {
|
|
@@ -41978,11 +42813,11 @@ function getStableDeviceId() {
|
|
|
41978
42813
|
const fresh = randomUUID2();
|
|
41979
42814
|
try {
|
|
41980
42815
|
const dir = dirname9(path);
|
|
41981
|
-
if (!
|
|
41982
|
-
|
|
42816
|
+
if (!existsSync24(dir)) {
|
|
42817
|
+
mkdirSync9(dir, { recursive: true });
|
|
41983
42818
|
}
|
|
41984
42819
|
const tmpPath = `${path}.tmp.${process.pid}`;
|
|
41985
|
-
|
|
42820
|
+
writeFileSync9(tmpPath, fresh, { mode: 384 });
|
|
41986
42821
|
renameSync6(tmpPath, path);
|
|
41987
42822
|
} catch {
|
|
41988
42823
|
}
|
|
@@ -42280,14 +43115,14 @@ var init_xai = __esm({
|
|
|
42280
43115
|
});
|
|
42281
43116
|
|
|
42282
43117
|
// packages/core/src/llm/provider-registry/loader.ts
|
|
42283
|
-
import { readdirSync as
|
|
42284
|
-
import { join as
|
|
43118
|
+
import { readdirSync as readdirSync6, statSync as statSync7 } from "node:fs";
|
|
43119
|
+
import { join as join27 } from "node:path";
|
|
42285
43120
|
import { pathToFileURL } from "node:url";
|
|
42286
43121
|
function isPluginFile(filename) {
|
|
42287
43122
|
return PLUGIN_EXTENSIONS.some((ext) => filename.endsWith(ext));
|
|
42288
43123
|
}
|
|
42289
43124
|
function getPluginDir() {
|
|
42290
|
-
return
|
|
43125
|
+
return join27(getCleoHome(), "plugins", "model-providers");
|
|
42291
43126
|
}
|
|
42292
43127
|
async function loadOnePlugin(absPath, api) {
|
|
42293
43128
|
let mod;
|
|
@@ -42329,19 +43164,19 @@ async function loadOnePlugin(absPath, api) {
|
|
|
42329
43164
|
async function scanAndLoadPlugins(pluginDir, api) {
|
|
42330
43165
|
let entries;
|
|
42331
43166
|
try {
|
|
42332
|
-
entries =
|
|
43167
|
+
entries = readdirSync6(pluginDir);
|
|
42333
43168
|
} catch {
|
|
42334
43169
|
return;
|
|
42335
43170
|
}
|
|
42336
43171
|
const pluginFiles = entries.filter((name15) => !name15.startsWith(".") && isPluginFile(name15)).filter((name15) => {
|
|
42337
43172
|
try {
|
|
42338
|
-
return statSync7(
|
|
43173
|
+
return statSync7(join27(pluginDir, name15)).isFile();
|
|
42339
43174
|
} catch {
|
|
42340
43175
|
return false;
|
|
42341
43176
|
}
|
|
42342
43177
|
}).sort();
|
|
42343
43178
|
for (const filename of pluginFiles) {
|
|
42344
|
-
await loadOnePlugin(
|
|
43179
|
+
await loadOnePlugin(join27(pluginDir, filename), api);
|
|
42345
43180
|
}
|
|
42346
43181
|
}
|
|
42347
43182
|
async function runDiscovery(registerProvider2, builtins) {
|
|
@@ -42444,15 +43279,15 @@ var init_provider_registry = __esm({
|
|
|
42444
43279
|
});
|
|
42445
43280
|
|
|
42446
43281
|
// packages/core/src/llm/rate-limit-guard.ts
|
|
42447
|
-
import { existsSync as
|
|
42448
|
-
import { join as
|
|
42449
|
-
import { getCleoHome as
|
|
43282
|
+
import { existsSync as existsSync25, mkdirSync as mkdirSync10, unlinkSync as unlinkSync6 } from "node:fs";
|
|
43283
|
+
import { join as join28 } from "node:path";
|
|
43284
|
+
import { getCleoHome as getCleoHome10 } from "@cleocode/paths";
|
|
42450
43285
|
function sanitize(value) {
|
|
42451
43286
|
return value.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
42452
43287
|
}
|
|
42453
43288
|
function rateLimitStatePath(provider, label) {
|
|
42454
|
-
const dir =
|
|
42455
|
-
return
|
|
43289
|
+
const dir = join28(getCleoHome10(), "rate-limit-state");
|
|
43290
|
+
return join28(dir, `${sanitize(provider)}-${sanitize(label)}.json`);
|
|
42456
43291
|
}
|
|
42457
43292
|
function parseResetFromHeaders(headers) {
|
|
42458
43293
|
const get = (k) => {
|
|
@@ -42499,9 +43334,9 @@ async function recordRateLimit(provider, label, opts) {
|
|
|
42499
43334
|
source = "default";
|
|
42500
43335
|
}
|
|
42501
43336
|
const filePath = rateLimitStatePath(provider, label);
|
|
42502
|
-
const dir =
|
|
42503
|
-
if (!
|
|
42504
|
-
|
|
43337
|
+
const dir = join28(getCleoHome10(), "rate-limit-state");
|
|
43338
|
+
if (!existsSync25(dir)) {
|
|
43339
|
+
mkdirSync10(dir, { recursive: true });
|
|
42505
43340
|
}
|
|
42506
43341
|
const state = { resetAt, recordedAt: now, source };
|
|
42507
43342
|
await withFileLock(filePath, () => {
|
|
@@ -43401,28 +44236,28 @@ var init_error_classifier = __esm({
|
|
|
43401
44236
|
|
|
43402
44237
|
// packages/core/src/llm/catalog-cache.ts
|
|
43403
44238
|
import {
|
|
43404
|
-
existsSync as
|
|
43405
|
-
mkdirSync as
|
|
43406
|
-
readdirSync as
|
|
44239
|
+
existsSync as existsSync26,
|
|
44240
|
+
mkdirSync as mkdirSync11,
|
|
44241
|
+
readdirSync as readdirSync7,
|
|
43407
44242
|
readFileSync as readFileSync15,
|
|
43408
44243
|
statSync as statSync8,
|
|
43409
44244
|
symlinkSync,
|
|
43410
44245
|
unlinkSync as unlinkSync7,
|
|
43411
|
-
writeFileSync as
|
|
44246
|
+
writeFileSync as writeFileSync10
|
|
43412
44247
|
} from "node:fs";
|
|
43413
44248
|
import { homedir as homedir4 } from "node:os";
|
|
43414
|
-
import { join as
|
|
44249
|
+
import { join as join29 } from "node:path";
|
|
43415
44250
|
function getCatalogDir() {
|
|
43416
44251
|
if (process.env["CLEO_DATA_DIR"]) {
|
|
43417
|
-
return
|
|
44252
|
+
return join29(process.env["CLEO_DATA_DIR"], "llm-catalog");
|
|
43418
44253
|
}
|
|
43419
|
-
const xdg = process.env["XDG_DATA_HOME"] ??
|
|
43420
|
-
return
|
|
44254
|
+
const xdg = process.env["XDG_DATA_HOME"] ?? join29(homedir4(), ".local", "share");
|
|
44255
|
+
return join29(xdg, "cleo", "llm-catalog");
|
|
43421
44256
|
}
|
|
43422
44257
|
function findLatestCacheFile(dir) {
|
|
43423
|
-
if (!
|
|
43424
|
-
const files =
|
|
43425
|
-
return files.length > 0 ?
|
|
44258
|
+
if (!existsSync26(dir)) return null;
|
|
44259
|
+
const files = readdirSync7(dir).filter((f) => f.endsWith("-models.json")).map((f) => ({ name: f, mtime: statSync8(join29(dir, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime);
|
|
44260
|
+
return files.length > 0 ? join29(dir, files[0].name) : null;
|
|
43426
44261
|
}
|
|
43427
44262
|
function readCacheFile(filePath) {
|
|
43428
44263
|
try {
|
|
@@ -43440,14 +44275,14 @@ var init_catalog_cache = __esm({
|
|
|
43440
44275
|
|
|
43441
44276
|
// packages/core/src/llm/model-metadata.ts
|
|
43442
44277
|
import { readFileSync as readFileSync16 } from "node:fs";
|
|
43443
|
-
import { dirname as dirname10, join as
|
|
44278
|
+
import { dirname as dirname10, join as join30 } from "node:path";
|
|
43444
44279
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
43445
44280
|
function getCuratedTable() {
|
|
43446
44281
|
if (_curated !== void 0) {
|
|
43447
44282
|
return _curated;
|
|
43448
44283
|
}
|
|
43449
44284
|
const thisDir = dirname10(fileURLToPath3(import.meta.url));
|
|
43450
|
-
const jsonPath =
|
|
44285
|
+
const jsonPath = join30(thisDir, "curated-models.json");
|
|
43451
44286
|
const raw = JSON.parse(readFileSync16(jsonPath, "utf-8"));
|
|
43452
44287
|
_curated = raw.models;
|
|
43453
44288
|
return _curated;
|
|
@@ -43532,7 +44367,7 @@ function storedToResolved(stored) {
|
|
|
43532
44367
|
awsProfile: stored.metadata?.["awsProfile"] ?? null
|
|
43533
44368
|
};
|
|
43534
44369
|
}
|
|
43535
|
-
function
|
|
44370
|
+
function sleep2(ms) {
|
|
43536
44371
|
return new Promise((resolve5) => setTimeout(resolve5, ms));
|
|
43537
44372
|
}
|
|
43538
44373
|
function backoffMs(policy, attempt) {
|
|
@@ -43670,7 +44505,7 @@ var init_concrete_session = __esm({
|
|
|
43670
44505
|
throw err;
|
|
43671
44506
|
}
|
|
43672
44507
|
lastErr = err;
|
|
43673
|
-
await
|
|
44508
|
+
await sleep2(backoffMs(policy, attempt));
|
|
43674
44509
|
}
|
|
43675
44510
|
}
|
|
43676
44511
|
throw lastErr;
|
|
@@ -47426,7 +48261,7 @@ function normalizeOllamaToolCall(tc) {
|
|
|
47426
48261
|
arguments: JSON.stringify(tc.function.arguments ?? {})
|
|
47427
48262
|
};
|
|
47428
48263
|
}
|
|
47429
|
-
function
|
|
48264
|
+
function sleep3(ms) {
|
|
47430
48265
|
return new Promise((resolve5) => setTimeout(resolve5, ms));
|
|
47431
48266
|
}
|
|
47432
48267
|
var OLLAMA_DEFAULT_BASE_URL, MAX_RETRIES, INITIAL_BACKOFF_MS, RETRYABLE_STATUS_CODES, OllamaTransport;
|
|
@@ -47635,7 +48470,7 @@ var init_ollama2 = __esm({
|
|
|
47635
48470
|
for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {
|
|
47636
48471
|
if (attempt > 0) {
|
|
47637
48472
|
const delay = INITIAL_BACKOFF_MS * 2 ** (attempt - 1);
|
|
47638
|
-
await
|
|
48473
|
+
await sleep3(delay);
|
|
47639
48474
|
}
|
|
47640
48475
|
let response;
|
|
47641
48476
|
try {
|
|
@@ -75648,7 +76483,7 @@ var init_catalog_model_resolver = __esm({
|
|
|
75648
76483
|
});
|
|
75649
76484
|
|
|
75650
76485
|
// packages/core/src/llm/cross-provider-selector.ts
|
|
75651
|
-
import { totalmem } from "node:os";
|
|
76486
|
+
import { totalmem as totalmem2 } from "node:os";
|
|
75652
76487
|
async function probeOllamaAlive(baseUrl) {
|
|
75653
76488
|
const cached2 = _ollamaProbeCache.get(baseUrl);
|
|
75654
76489
|
if (cached2 && Date.now() - cached2.checkedAt < OLLAMA_PROBE_TTL_MS) {
|
|
@@ -75671,7 +76506,7 @@ async function probeOllamaAlive(baseUrl) {
|
|
|
75671
76506
|
return alive;
|
|
75672
76507
|
}
|
|
75673
76508
|
function ollamaDefaultModelForTier(tier, ramBytesOverride) {
|
|
75674
|
-
const ramBytes = ramBytesOverride ??
|
|
76509
|
+
const ramBytes = ramBytesOverride ?? totalmem2();
|
|
75675
76510
|
const gb = ramBytes / 1024 ** 3;
|
|
75676
76511
|
if (gb >= 8) {
|
|
75677
76512
|
return tier === "fast" || tier === "local" ? "gemma4:e2b" : "gemma4:e4b";
|
|
@@ -75795,7 +76630,7 @@ async function selectBestProvisioned(role, opts) {
|
|
|
75795
76630
|
let model;
|
|
75796
76631
|
if (winner.id === "ollama") {
|
|
75797
76632
|
const ollamaProfile2 = await getProviderProfile2("ollama");
|
|
75798
|
-
const ramBytes =
|
|
76633
|
+
const ramBytes = totalmem2();
|
|
75799
76634
|
const gb = ramBytes / 1024 ** 3;
|
|
75800
76635
|
if (gb >= 8) {
|
|
75801
76636
|
model = (winner.tier === "fast" || taskTier === "fast" ? ollamaProfile2?.defaultAuxModel : ollamaProfile2?.defaultModel) ?? ollamaDefaultModelForTier(winner.tier);
|