@cleocode/core 2026.6.19 → 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 +1160 -329
- package/dist/docs/export-document.js.map +3 -3
- package/dist/llm/plugin-facade.js +1211 -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/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/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 +1209 -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/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/package.json +12 -12
|
@@ -12419,7 +12419,7 @@ var init_lafs = __esm({
|
|
|
12419
12419
|
|
|
12420
12420
|
// packages/contracts/src/lease-ipc/messages.ts
|
|
12421
12421
|
import { z as z11 } from "zod";
|
|
12422
|
-
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;
|
|
12422
|
+
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;
|
|
12423
12423
|
var init_messages2 = __esm({
|
|
12424
12424
|
"packages/contracts/src/lease-ipc/messages.ts"() {
|
|
12425
12425
|
"use strict";
|
|
@@ -12504,6 +12504,24 @@ var init_messages2 = __esm({
|
|
|
12504
12504
|
/** The worker's own view of whether it is currently inside an LLM call. */
|
|
12505
12505
|
in_flight_llm: z11.boolean()
|
|
12506
12506
|
}).strict();
|
|
12507
|
+
ResourceAdmitRequestSchema = z11.object({
|
|
12508
|
+
/** Tag discriminating this request variant. */
|
|
12509
|
+
kind: z11.literal("resource_admit"),
|
|
12510
|
+
/** The resource class (e.g. `"db-heavy"`, `"full-build"`, `"test-run"`). */
|
|
12511
|
+
class: z11.string().min(1),
|
|
12512
|
+
/** The caller holding the slot — process/worktree-unique; the release key. */
|
|
12513
|
+
holder_id: z11.string().min(1),
|
|
12514
|
+
/** The client-computed budget: the max concurrent holders for this class. */
|
|
12515
|
+
budget: z11.number().int().nonnegative()
|
|
12516
|
+
}).strict();
|
|
12517
|
+
ResourceReleaseRequestSchema = z11.object({
|
|
12518
|
+
/** Tag discriminating this request variant. */
|
|
12519
|
+
kind: z11.literal("resource_release"),
|
|
12520
|
+
/** The resource class the slot belongs to. */
|
|
12521
|
+
class: z11.string().min(1),
|
|
12522
|
+
/** The holder that was admitted (matches the admit `holder_id`). */
|
|
12523
|
+
holder_id: z11.string().min(1)
|
|
12524
|
+
}).strict();
|
|
12507
12525
|
LeaseIpcRequestSchema = z11.discriminatedUnion("kind", [
|
|
12508
12526
|
LeaseAcquireRequestSchema,
|
|
12509
12527
|
LeaseReleaseRequestSchema,
|
|
@@ -12511,7 +12529,9 @@ var init_messages2 = __esm({
|
|
|
12511
12529
|
RateCheckRequestSchema,
|
|
12512
12530
|
ToolGrantRequestSchema,
|
|
12513
12531
|
QueueAdmitRequestSchema,
|
|
12514
|
-
WorkerHeartbeatRequestSchema
|
|
12532
|
+
WorkerHeartbeatRequestSchema,
|
|
12533
|
+
ResourceAdmitRequestSchema,
|
|
12534
|
+
ResourceReleaseRequestSchema
|
|
12515
12535
|
]);
|
|
12516
12536
|
LeaseGrantedResponseSchema = z11.object({
|
|
12517
12537
|
/** Tag discriminating this response variant. */
|
|
@@ -12612,6 +12632,25 @@ var init_messages2 = __esm({
|
|
|
12612
12632
|
/** Tag discriminating this response variant. */
|
|
12613
12633
|
kind: z11.literal("heartbeat_ack")
|
|
12614
12634
|
}).strict();
|
|
12635
|
+
ResourceAdmitDispositionSchema = z11.enum(["admitted", "deferred"]);
|
|
12636
|
+
ResourceAdmitResultResponseSchema = z11.object({
|
|
12637
|
+
/** Tag discriminating this response variant. */
|
|
12638
|
+
kind: z11.literal("resource_admit_result"),
|
|
12639
|
+
/** Whether the heavy op was admitted or deferred. */
|
|
12640
|
+
disposition: ResourceAdmitDispositionSchema,
|
|
12641
|
+
/** Back-off in ms before re-requesting (0 when admitted). */
|
|
12642
|
+
retry_after_ms: z11.number().int().nonnegative(),
|
|
12643
|
+
/** Remaining slots for the class after this decision. */
|
|
12644
|
+
slots_remaining: z11.number().int().nonnegative()
|
|
12645
|
+
}).strict();
|
|
12646
|
+
ResourceReleaseResultResponseSchema = z11.object({
|
|
12647
|
+
/** Tag discriminating this response variant. */
|
|
12648
|
+
kind: z11.literal("resource_release_result"),
|
|
12649
|
+
/** Whether a held slot was actually released (false = no such holder). */
|
|
12650
|
+
released: z11.boolean(),
|
|
12651
|
+
/** Remaining in-flight holders for the class after the release. */
|
|
12652
|
+
slots_remaining: z11.number().int().nonnegative()
|
|
12653
|
+
}).strict();
|
|
12615
12654
|
LeaseErrorResponseSchema = z11.object({
|
|
12616
12655
|
/** Tag discriminating this response variant. */
|
|
12617
12656
|
kind: z11.literal("error"),
|
|
@@ -12630,6 +12669,8 @@ var init_messages2 = __esm({
|
|
|
12630
12669
|
ChildKilledUnresponsiveResponseSchema,
|
|
12631
12670
|
QueueAdmitResultResponseSchema,
|
|
12632
12671
|
HeartbeatAckResponseSchema,
|
|
12672
|
+
ResourceAdmitResultResponseSchema,
|
|
12673
|
+
ResourceReleaseResultResponseSchema,
|
|
12633
12674
|
LeaseErrorResponseSchema
|
|
12634
12675
|
]);
|
|
12635
12676
|
LeaseIpcRequestEnvelopeSchema = z11.object({
|
|
@@ -12663,7 +12704,9 @@ var init_messages2 = __esm({
|
|
|
12663
12704
|
"rate_check",
|
|
12664
12705
|
"tool_grant",
|
|
12665
12706
|
"queue_admit",
|
|
12666
|
-
"worker_heartbeat"
|
|
12707
|
+
"worker_heartbeat",
|
|
12708
|
+
"resource_admit",
|
|
12709
|
+
"resource_release"
|
|
12667
12710
|
];
|
|
12668
12711
|
LEASE_IPC_RESPONSE_KINDS = [
|
|
12669
12712
|
"lease_granted",
|
|
@@ -12675,6 +12718,8 @@ var init_messages2 = __esm({
|
|
|
12675
12718
|
"child_killed_unresponsive",
|
|
12676
12719
|
"queue_admit_result",
|
|
12677
12720
|
"heartbeat_ack",
|
|
12721
|
+
"resource_admit_result",
|
|
12722
|
+
"resource_release_result",
|
|
12678
12723
|
"error"
|
|
12679
12724
|
];
|
|
12680
12725
|
LEASE_IPC_MESSAGE_KINDS = [
|
|
@@ -12685,9 +12730,11 @@ var init_messages2 = __esm({
|
|
|
12685
12730
|
});
|
|
12686
12731
|
|
|
12687
12732
|
// packages/contracts/src/lease-ipc/version.ts
|
|
12733
|
+
var LEASE_IPC_PROTOCOL_VERSION;
|
|
12688
12734
|
var init_version2 = __esm({
|
|
12689
12735
|
"packages/contracts/src/lease-ipc/version.ts"() {
|
|
12690
12736
|
"use strict";
|
|
12737
|
+
LEASE_IPC_PROTOCOL_VERSION = "1.2.0";
|
|
12691
12738
|
}
|
|
12692
12739
|
});
|
|
12693
12740
|
|
|
@@ -13891,6 +13938,25 @@ var init_render = __esm({
|
|
|
13891
13938
|
}
|
|
13892
13939
|
});
|
|
13893
13940
|
|
|
13941
|
+
// packages/contracts/src/resource-governor.ts
|
|
13942
|
+
var RESOURCE_CLASSES, DEFAULT_RESOURCE_RETRY_AFTER_MS;
|
|
13943
|
+
var init_resource_governor = __esm({
|
|
13944
|
+
"packages/contracts/src/resource-governor.ts"() {
|
|
13945
|
+
"use strict";
|
|
13946
|
+
RESOURCE_CLASSES = Object.freeze([
|
|
13947
|
+
"interactive-cli",
|
|
13948
|
+
"agent-session",
|
|
13949
|
+
"llm-call",
|
|
13950
|
+
"test-run",
|
|
13951
|
+
"scoped-build",
|
|
13952
|
+
"full-build",
|
|
13953
|
+
"db-heavy",
|
|
13954
|
+
"background-autonomous"
|
|
13955
|
+
]);
|
|
13956
|
+
DEFAULT_RESOURCE_RETRY_AFTER_MS = 2e3;
|
|
13957
|
+
}
|
|
13958
|
+
});
|
|
13959
|
+
|
|
13894
13960
|
// packages/contracts/src/session.ts
|
|
13895
13961
|
var init_session2 = __esm({
|
|
13896
13962
|
"packages/contracts/src/session.ts"() {
|
|
@@ -15642,6 +15708,7 @@ var init_src = __esm({
|
|
|
15642
15708
|
init_evidence_atoms();
|
|
15643
15709
|
init_plan();
|
|
15644
15710
|
init_render();
|
|
15711
|
+
init_resource_governor();
|
|
15645
15712
|
init_session2();
|
|
15646
15713
|
init_session_journal();
|
|
15647
15714
|
init_status_registry();
|
|
@@ -17555,7 +17622,7 @@ var init_sql = __esm({
|
|
|
17555
17622
|
return new SQL([new StringChunk(str)]);
|
|
17556
17623
|
}
|
|
17557
17624
|
_sql.raw = raw;
|
|
17558
|
-
function
|
|
17625
|
+
function join25(chunks, separator) {
|
|
17559
17626
|
const result = [];
|
|
17560
17627
|
for (const [i, chunk] of chunks.entries()) {
|
|
17561
17628
|
if (i > 0 && separator !== void 0) result.push(separator);
|
|
@@ -17563,7 +17630,7 @@ var init_sql = __esm({
|
|
|
17563
17630
|
}
|
|
17564
17631
|
return new SQL(result);
|
|
17565
17632
|
}
|
|
17566
|
-
_sql.join =
|
|
17633
|
+
_sql.join = join25;
|
|
17567
17634
|
function identifier(value) {
|
|
17568
17635
|
return new Name(value);
|
|
17569
17636
|
}
|
|
@@ -19950,7 +20017,7 @@ var init_select2 = __esm({
|
|
|
19950
20017
|
const baseTableName = this.tableName;
|
|
19951
20018
|
const tableName = getTableLikeName(table);
|
|
19952
20019
|
for (const item of extractUsedTable(table)) this.usedTables.add(item);
|
|
19953
|
-
if (typeof tableName === "string" && this.config.joins?.some((
|
|
20020
|
+
if (typeof tableName === "string" && this.config.joins?.some((join25) => join25.alias === tableName)) throw new Error(`Alias "${tableName}" is already used in this query`);
|
|
19954
20021
|
if (!this.isPartialSelect) {
|
|
19955
20022
|
if (Object.keys(this.joinsNotNullableMap).length === 1 && typeof baseTableName === "string") this.config.fields = { [baseTableName]: this.config.fields };
|
|
19956
20023
|
if (typeof tableName === "string" && !is(table, SQL)) {
|
|
@@ -23760,7 +23827,7 @@ var init_dialect = __esm({
|
|
|
23760
23827
|
if (!joins2) return;
|
|
23761
23828
|
const withEntries = Object.entries(joins2).filter(([_, v]) => v);
|
|
23762
23829
|
if (!withEntries.length) return;
|
|
23763
|
-
return sql.join(withEntries.map(([k,
|
|
23830
|
+
return sql.join(withEntries.map(([k, join25]) => {
|
|
23764
23831
|
const relation = tableConfig.relations[k];
|
|
23765
23832
|
const isSingle2 = is(relation, One);
|
|
23766
23833
|
const targetTable = aliasedTable(relation.targetTable, `d${currentDepth + 1}`);
|
|
@@ -23771,7 +23838,7 @@ var init_dialect = __esm({
|
|
|
23771
23838
|
table: targetTable,
|
|
23772
23839
|
mode: isSingle2 ? "first" : "many",
|
|
23773
23840
|
schema,
|
|
23774
|
-
queryConfig:
|
|
23841
|
+
queryConfig: join25,
|
|
23775
23842
|
tableConfig: schema[relation.targetTableName],
|
|
23776
23843
|
relationWhere: filter,
|
|
23777
23844
|
isNested: true,
|
|
@@ -23785,7 +23852,7 @@ var init_dialect = __esm({
|
|
|
23785
23852
|
key: k,
|
|
23786
23853
|
selection: innerQuery.selection,
|
|
23787
23854
|
isArray: !isSingle2,
|
|
23788
|
-
isOptional: (relation.optional ?? false) ||
|
|
23855
|
+
isOptional: (relation.optional ?? false) || join25 !== true && !!join25.where
|
|
23789
23856
|
});
|
|
23790
23857
|
const jsonColumns = sql.join(innerQuery.selection.map((s) => {
|
|
23791
23858
|
return sql`${sql.raw(this.escapeString(s.key))}, ${s.selection ? sql`${jsonb3}(${sql.identifier(s.key)})` : sql.identifier(s.key)}`;
|
|
@@ -24606,7 +24673,7 @@ var init_update = __esm({
|
|
|
24606
24673
|
createJoin(joinType) {
|
|
24607
24674
|
return ((table, on) => {
|
|
24608
24675
|
const tableName = getTableLikeName(table);
|
|
24609
|
-
if (typeof tableName === "string" && this.config.joins.some((
|
|
24676
|
+
if (typeof tableName === "string" && this.config.joins.some((join25) => join25.alias === tableName)) throw new Error(`Alias "${tableName}" is already used in this query`);
|
|
24610
24677
|
if (typeof on === "function") {
|
|
24611
24678
|
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;
|
|
24612
24679
|
on = on(new Proxy(this.config.table[Table.Symbol.Columns], new SelectionProxyHandler({
|
|
@@ -25758,8 +25825,8 @@ function probeAndMarkApplied(nativeDb, migration, logSubsystem, eliminatedTables
|
|
|
25758
25825
|
});
|
|
25759
25826
|
if (allAltersPresent && allTablesPresent && allIndexesPresent && allTriggersPresent) {
|
|
25760
25827
|
insertJournalEntry(nativeDb, migration.hash, migration.folderMillis, migration.name ?? "");
|
|
25761
|
-
const
|
|
25762
|
-
|
|
25828
|
+
const log12 = getLogger(logSubsystem);
|
|
25829
|
+
log12.debug(
|
|
25763
25830
|
{
|
|
25764
25831
|
migration: migration.name,
|
|
25765
25832
|
alters: alterTargets.length,
|
|
@@ -25828,14 +25895,14 @@ function reconcileJournal(nativeDb, migrationsFolder, existenceTable2, logSubsys
|
|
|
25828
25895
|
if (hasOrphanedEntries) {
|
|
25829
25896
|
const dbHashes = new Set(dbEntries.map((e) => e.hash));
|
|
25830
25897
|
const allLocalHashesPresentInDb = localMigrations.every((m) => dbHashes.has(m.hash));
|
|
25831
|
-
const
|
|
25898
|
+
const log12 = getLogger(logSubsystem);
|
|
25832
25899
|
if (allLocalHashesPresentInDb) {
|
|
25833
|
-
|
|
25900
|
+
log12.debug(
|
|
25834
25901
|
{ extra: orphanedEntries.length },
|
|
25835
25902
|
`Migration journal has ${orphanedEntries.length} entries for migrations not known to this install (DB is ahead). Skipping reconciliation.`
|
|
25836
25903
|
);
|
|
25837
25904
|
} else {
|
|
25838
|
-
|
|
25905
|
+
log12.warn(
|
|
25839
25906
|
{ orphaned: orphanedEntries.length },
|
|
25840
25907
|
`Detected ${orphanedEntries.length} true-orphan journal entries from a previous CLEO lineage. Reconciling via DDL probe.`
|
|
25841
25908
|
);
|
|
@@ -25871,8 +25938,8 @@ function reconcileJournal(nativeDb, migrationsFolder, existenceTable2, logSubsys
|
|
|
25871
25938
|
if (alterMatches.length === 0) {
|
|
25872
25939
|
const stripped = stripSqlComments(fullSql).trim();
|
|
25873
25940
|
if (stripped === "") {
|
|
25874
|
-
const
|
|
25875
|
-
|
|
25941
|
+
const log12 = getLogger(logSubsystem);
|
|
25942
|
+
log12.debug(
|
|
25876
25943
|
{ migration: migration.name },
|
|
25877
25944
|
`Migration ${migration.name} is a comment-only baseline marker \u2014 marking applied on existing DB.`
|
|
25878
25945
|
);
|
|
@@ -25911,8 +25978,8 @@ function reconcileJournal(nativeDb, migrationsFolder, existenceTable2, logSubsys
|
|
|
25911
25978
|
}
|
|
25912
25979
|
}
|
|
25913
25980
|
if (missingColumns.length === 0) {
|
|
25914
|
-
const
|
|
25915
|
-
|
|
25981
|
+
const log12 = getLogger(logSubsystem);
|
|
25982
|
+
log12.warn(
|
|
25916
25983
|
{ migration: migration.name, columns: alterMatches },
|
|
25917
25984
|
`Detected partially-applied migration ${migration.name} \u2014 columns exist but journal entry missing. Auto-reconciling.`
|
|
25918
25985
|
);
|
|
@@ -25920,8 +25987,8 @@ function reconcileJournal(nativeDb, migrationsFolder, existenceTable2, logSubsys
|
|
|
25920
25987
|
continue;
|
|
25921
25988
|
}
|
|
25922
25989
|
if (existingColumns.length > 0 && missingColumns.length > 0) {
|
|
25923
|
-
const
|
|
25924
|
-
|
|
25990
|
+
const log12 = getLogger(logSubsystem);
|
|
25991
|
+
log12.warn(
|
|
25925
25992
|
{
|
|
25926
25993
|
migration: migration.name,
|
|
25927
25994
|
existingColumns: existingColumns.map((c) => `${c.table}.${c.column}`),
|
|
@@ -25933,12 +26000,12 @@ function reconcileJournal(nativeDb, migrationsFolder, existenceTable2, logSubsys
|
|
|
25933
26000
|
if (!tableExists(nativeDb, table)) continue;
|
|
25934
26001
|
try {
|
|
25935
26002
|
nativeDb.exec(`ALTER TABLE ${table} ADD COLUMN ${column}${ddl ? ` ${ddl}` : ""}`);
|
|
25936
|
-
|
|
26003
|
+
log12.warn(
|
|
25937
26004
|
{ migration: migration.name, table, column },
|
|
25938
26005
|
`T920: Added missing column ${table}.${column} to complete partial migration.`
|
|
25939
26006
|
);
|
|
25940
26007
|
} catch {
|
|
25941
|
-
|
|
26008
|
+
log12.warn(
|
|
25942
26009
|
{ migration: migration.name, table, column },
|
|
25943
26010
|
`T920: Could not add missing column ${table}.${column} \u2014 will let Drizzle migrate() handle it.`
|
|
25944
26011
|
);
|
|
@@ -25958,8 +26025,8 @@ function reconcileJournal(nativeDb, migrationsFolder, existenceTable2, logSubsys
|
|
|
25958
26025
|
for (const entry of unnamedEntries) {
|
|
25959
26026
|
const migrationName = hashToName.get(entry.hash);
|
|
25960
26027
|
if (!migrationName) continue;
|
|
25961
|
-
const
|
|
25962
|
-
|
|
26028
|
+
const log12 = getLogger(logSubsystem);
|
|
26029
|
+
log12.debug(
|
|
25963
26030
|
{ id: entry.id, hash: entry.hash, name: migrationName },
|
|
25964
26031
|
`Backfilling missing name on journal entry id=${entry.id} (Drizzle v1 beta legacy compat).`
|
|
25965
26032
|
);
|
|
@@ -26109,13 +26176,13 @@ function ensureColumns(nativeDb, tableName, requiredColumns, logSubsystem, conte
|
|
|
26109
26176
|
const existingCols = new Set(columns.map((c) => c.name));
|
|
26110
26177
|
for (const req of requiredColumns) {
|
|
26111
26178
|
if (!existingCols.has(req.name)) {
|
|
26112
|
-
const
|
|
26179
|
+
const log12 = getLogger(logSubsystem);
|
|
26113
26180
|
const message = `Adding missing column ${tableName}.${req.name} via ALTER TABLE`;
|
|
26114
26181
|
const fields = { column: req.name, context };
|
|
26115
26182
|
if (context === "fresh") {
|
|
26116
|
-
|
|
26183
|
+
log12.error(fields, `${message} \u2014 MIGRATION DEFECT (fresh DB should not need repair)`);
|
|
26117
26184
|
} else {
|
|
26118
|
-
|
|
26185
|
+
log12.warn(fields, message);
|
|
26119
26186
|
}
|
|
26120
26187
|
nativeDb.exec(`ALTER TABLE ${tableName} ADD COLUMN ${req.name} ${req.ddl}`);
|
|
26121
26188
|
}
|
|
@@ -35308,11 +35375,750 @@ var init_cleo_global = __esm({
|
|
|
35308
35375
|
}
|
|
35309
35376
|
});
|
|
35310
35377
|
|
|
35311
|
-
// packages/core/src/
|
|
35378
|
+
// packages/core/src/resources/linux-backend.ts
|
|
35379
|
+
import { readFile as readFile2, stat } from "node:fs/promises";
|
|
35380
|
+
function parsePressureLine(line2) {
|
|
35381
|
+
const avg10Match = /avg10=([\d.]+)/.exec(line2);
|
|
35382
|
+
const avg60Match = /avg60=([\d.]+)/.exec(line2);
|
|
35383
|
+
const avg300Match = /avg300=([\d.]+)/.exec(line2);
|
|
35384
|
+
const totalMatch = /total=(\d+)/.exec(line2);
|
|
35385
|
+
if (!avg10Match?.[1] || !avg60Match?.[1] || !avg300Match?.[1] || !totalMatch?.[1]) {
|
|
35386
|
+
return null;
|
|
35387
|
+
}
|
|
35388
|
+
return {
|
|
35389
|
+
avg10: parseFloat(avg10Match[1]),
|
|
35390
|
+
avg60: parseFloat(avg60Match[1]),
|
|
35391
|
+
avg300: parseFloat(avg300Match[1]),
|
|
35392
|
+
totalUs: parseInt(totalMatch[1], 10)
|
|
35393
|
+
};
|
|
35394
|
+
}
|
|
35395
|
+
function parsePsiFile(content) {
|
|
35396
|
+
const lines = content.split("\n").map((l) => l.trim()).filter(Boolean);
|
|
35397
|
+
let some = null;
|
|
35398
|
+
let full = null;
|
|
35399
|
+
for (const line2 of lines) {
|
|
35400
|
+
if (line2.startsWith("some ")) {
|
|
35401
|
+
some = parsePressureLine(line2);
|
|
35402
|
+
} else if (line2.startsWith("full ")) {
|
|
35403
|
+
full = parsePressureLine(line2);
|
|
35404
|
+
}
|
|
35405
|
+
}
|
|
35406
|
+
if (!some) {
|
|
35407
|
+
return null;
|
|
35408
|
+
}
|
|
35409
|
+
return { some, full };
|
|
35410
|
+
}
|
|
35411
|
+
function parseMemAvailable(content) {
|
|
35412
|
+
for (const line2 of content.split("\n")) {
|
|
35413
|
+
if (line2.startsWith("MemAvailable:")) {
|
|
35414
|
+
const match = /MemAvailable:\s+(\d+)\s+kB/.exec(line2);
|
|
35415
|
+
if (match?.[1]) {
|
|
35416
|
+
return parseInt(match[1], 10) * 1024;
|
|
35417
|
+
}
|
|
35418
|
+
}
|
|
35419
|
+
}
|
|
35420
|
+
return null;
|
|
35421
|
+
}
|
|
35422
|
+
function parseSmapsRollup(content) {
|
|
35423
|
+
let rssKb = null;
|
|
35424
|
+
let pssKb = null;
|
|
35425
|
+
for (const line2 of content.split("\n")) {
|
|
35426
|
+
if (line2.startsWith("Rss:")) {
|
|
35427
|
+
const m = /Rss:\s+(\d+)\s+kB/.exec(line2);
|
|
35428
|
+
if (m?.[1]) rssKb = parseInt(m[1], 10);
|
|
35429
|
+
} else if (line2.startsWith("Pss:")) {
|
|
35430
|
+
const m = /Pss:\s+(\d+)\s+kB/.exec(line2);
|
|
35431
|
+
if (m?.[1]) pssKb = parseInt(m[1], 10);
|
|
35432
|
+
}
|
|
35433
|
+
}
|
|
35434
|
+
if (rssKb === null || pssKb === null) return null;
|
|
35435
|
+
return { rssBytes: rssKb * 1024, pssBytes: pssKb * 1024 };
|
|
35436
|
+
}
|
|
35437
|
+
var defaultReadFile, defaultStat, LinuxResourceBackend;
|
|
35438
|
+
var init_linux_backend = __esm({
|
|
35439
|
+
"packages/core/src/resources/linux-backend.ts"() {
|
|
35440
|
+
"use strict";
|
|
35441
|
+
defaultReadFile = (path2, encoding) => readFile2(path2, encoding);
|
|
35442
|
+
defaultStat = async (path2) => {
|
|
35443
|
+
try {
|
|
35444
|
+
const s = await stat(path2);
|
|
35445
|
+
return { size: s.size };
|
|
35446
|
+
} catch {
|
|
35447
|
+
return null;
|
|
35448
|
+
}
|
|
35449
|
+
};
|
|
35450
|
+
LinuxResourceBackend = class {
|
|
35451
|
+
globalPressurePath;
|
|
35452
|
+
cgroupSlicePressurePath;
|
|
35453
|
+
walPaths;
|
|
35454
|
+
readFileFn;
|
|
35455
|
+
statFileFn;
|
|
35456
|
+
constructor(opts = {}) {
|
|
35457
|
+
this.globalPressurePath = opts.globalPressurePath ?? "/proc/pressure/memory";
|
|
35458
|
+
this.cgroupSlicePressurePath = opts.cgroupSlicePressurePath ?? null;
|
|
35459
|
+
this.walPaths = opts.walPaths ?? [];
|
|
35460
|
+
this.readFileFn = opts.readFileFn ?? defaultReadFile;
|
|
35461
|
+
this.statFileFn = opts.statFileFn ?? defaultStat;
|
|
35462
|
+
}
|
|
35463
|
+
/**
|
|
35464
|
+
* Take a bounded point-in-time resource sample.
|
|
35465
|
+
*
|
|
35466
|
+
* Read-count (bounded by Amendment 1):
|
|
35467
|
+
* - 1 read: `/proc/pressure/memory`
|
|
35468
|
+
* - 0–1 reads: slice `memory.pressure` (if configured)
|
|
35469
|
+
* - 1 read: `/proc/meminfo`
|
|
35470
|
+
* - N stat calls: one per WAL path in `walPaths`
|
|
35471
|
+
*
|
|
35472
|
+
* Never spawns a child process.
|
|
35473
|
+
*/
|
|
35474
|
+
async sample() {
|
|
35475
|
+
const sampledAtMs = Date.now();
|
|
35476
|
+
const globalPressure = await this._readPsi(this.globalPressurePath);
|
|
35477
|
+
const pressureAvailable = globalPressure !== null;
|
|
35478
|
+
let slicePressure = null;
|
|
35479
|
+
if (this.cgroupSlicePressurePath) {
|
|
35480
|
+
slicePressure = await this._readPsi(this.cgroupSlicePressurePath);
|
|
35481
|
+
}
|
|
35482
|
+
const memAvailableBytes = await this._readMemAvailable();
|
|
35483
|
+
const walObservations = await this._readWalObservations();
|
|
35484
|
+
return {
|
|
35485
|
+
sampledAtMs,
|
|
35486
|
+
pressureAvailable,
|
|
35487
|
+
memAvailableBytes,
|
|
35488
|
+
globalPressure,
|
|
35489
|
+
slicePressure,
|
|
35490
|
+
walObservations
|
|
35491
|
+
};
|
|
35492
|
+
}
|
|
35493
|
+
/**
|
|
35494
|
+
* Sweep per-child RSS via `/proc/<pid>/smaps_rollup`.
|
|
35495
|
+
*
|
|
35496
|
+
* **STRUCTURALLY SEPARATED from sample()** — smaps_rollup reads are
|
|
35497
|
+
* ms-scale per multi-GB process and must never appear in the hot path.
|
|
35498
|
+
* Call on a separate low-frequency cadence (e.g. every 60s or on
|
|
35499
|
+
* backoff→hold state transition).
|
|
35500
|
+
*
|
|
35501
|
+
* @param pids - PIDs to sweep. Dead/unknown PIDs are silently skipped.
|
|
35502
|
+
*/
|
|
35503
|
+
async sweepChildRss(pids) {
|
|
35504
|
+
const sampledAtMs = Date.now();
|
|
35505
|
+
const entries = [];
|
|
35506
|
+
for (const pid of pids) {
|
|
35507
|
+
try {
|
|
35508
|
+
const content = await this.readFileFn(`/proc/${pid}/smaps_rollup`, "utf-8");
|
|
35509
|
+
const parsed = parseSmapsRollup(content);
|
|
35510
|
+
if (parsed) {
|
|
35511
|
+
entries.push({ pid, ...parsed });
|
|
35512
|
+
}
|
|
35513
|
+
} catch {
|
|
35514
|
+
}
|
|
35515
|
+
}
|
|
35516
|
+
return { sampledAtMs, entries };
|
|
35517
|
+
}
|
|
35518
|
+
// -------------------------------------------------------------------------
|
|
35519
|
+
// Private helpers
|
|
35520
|
+
// -------------------------------------------------------------------------
|
|
35521
|
+
async _readPsi(path2) {
|
|
35522
|
+
try {
|
|
35523
|
+
const content = await this.readFileFn(path2, "utf-8");
|
|
35524
|
+
return parsePsiFile(content);
|
|
35525
|
+
} catch {
|
|
35526
|
+
return null;
|
|
35527
|
+
}
|
|
35528
|
+
}
|
|
35529
|
+
async _readMemAvailable() {
|
|
35530
|
+
try {
|
|
35531
|
+
const content = await this.readFileFn("/proc/meminfo", "utf-8");
|
|
35532
|
+
return parseMemAvailable(content);
|
|
35533
|
+
} catch {
|
|
35534
|
+
return null;
|
|
35535
|
+
}
|
|
35536
|
+
}
|
|
35537
|
+
async _readWalObservations() {
|
|
35538
|
+
const results = [];
|
|
35539
|
+
for (const walPath of this.walPaths) {
|
|
35540
|
+
const info = await this.statFileFn(walPath);
|
|
35541
|
+
results.push({ walPath, sizeBytes: info?.size ?? null });
|
|
35542
|
+
}
|
|
35543
|
+
return results;
|
|
35544
|
+
}
|
|
35545
|
+
};
|
|
35546
|
+
}
|
|
35547
|
+
});
|
|
35548
|
+
|
|
35549
|
+
// packages/core/src/resources/monitor.ts
|
|
35550
|
+
import { EventEmitter as EventEmitter2 } from "node:events";
|
|
35551
|
+
function resolveThresholds(cfg) {
|
|
35552
|
+
return {
|
|
35553
|
+
holdSomeAvg10: cfg.psi?.holdSomeAvg10 ?? DEFAULTS.holdSomeAvg10,
|
|
35554
|
+
backoffSomeAvg10: cfg.psi?.backoffSomeAvg10 ?? DEFAULTS.backoffSomeAvg10,
|
|
35555
|
+
holdFullAvg10: cfg.psi?.holdFullAvg10 ?? DEFAULTS.holdFullAvg10,
|
|
35556
|
+
backoffFullAvg10: cfg.psi?.backoffFullAvg10 ?? DEFAULTS.backoffFullAvg10,
|
|
35557
|
+
hysteresisPoints: cfg.hysteresisPoints ?? DEFAULTS.hysteresisPoints,
|
|
35558
|
+
headroomBytes: cfg.headroomBytes ?? DEFAULTS.headroomBytes,
|
|
35559
|
+
walWarnThresholdBytes: cfg.walWarnThresholdBytes ?? DEFAULTS.walWarnThresholdBytes,
|
|
35560
|
+
pollIntervalMs: cfg.pollIntervalMs ?? DEFAULTS.pollIntervalMs
|
|
35561
|
+
};
|
|
35562
|
+
}
|
|
35563
|
+
function evaluateState(sample, thresholds, currentState) {
|
|
35564
|
+
if (!sample.pressureAvailable) {
|
|
35565
|
+
if (sample.memAvailableBytes !== null && sample.memAvailableBytes < thresholds.headroomBytes) {
|
|
35566
|
+
return { state: "hold", reason: "degraded: memAvailable below headroom" };
|
|
35567
|
+
}
|
|
35568
|
+
return { state: "ok", reason: "degraded: PSI unavailable, memAvailable sufficient" };
|
|
35569
|
+
}
|
|
35570
|
+
const somePressure = sample.globalPressure?.some ?? sample.slicePressure?.some;
|
|
35571
|
+
const fullPressure = sample.globalPressure?.full ?? sample.slicePressure?.full;
|
|
35572
|
+
const someAvg102 = somePressure?.avg10 ?? 0;
|
|
35573
|
+
const fullAvg10 = fullPressure?.avg10 ?? 0;
|
|
35574
|
+
if (someAvg102 > thresholds.backoffSomeAvg10 || fullAvg10 > thresholds.backoffFullAvg10) {
|
|
35575
|
+
return {
|
|
35576
|
+
state: "backoff",
|
|
35577
|
+
reason: `some avg10=${someAvg102.toFixed(2)} full avg10=${fullAvg10.toFixed(2)} \u2014 above backoff threshold`
|
|
35578
|
+
};
|
|
35579
|
+
}
|
|
35580
|
+
if (someAvg102 > thresholds.holdSomeAvg10 || fullAvg10 > thresholds.holdFullAvg10) {
|
|
35581
|
+
return {
|
|
35582
|
+
state: "hold",
|
|
35583
|
+
reason: `some avg10=${someAvg102.toFixed(2)} full avg10=${fullAvg10.toFixed(2)} \u2014 above hold threshold`
|
|
35584
|
+
};
|
|
35585
|
+
}
|
|
35586
|
+
if (currentState !== "ok") {
|
|
35587
|
+
const hysteresisFloor = thresholds.holdSomeAvg10 - thresholds.hysteresisPoints;
|
|
35588
|
+
const fullHysteresisFloor = thresholds.holdFullAvg10 - thresholds.hysteresisPoints;
|
|
35589
|
+
if (someAvg102 > hysteresisFloor || fullAvg10 > fullHysteresisFloor) {
|
|
35590
|
+
const clamped = currentState === "backoff" ? "hold" : currentState;
|
|
35591
|
+
return {
|
|
35592
|
+
state: clamped,
|
|
35593
|
+
reason: `some avg10=${someAvg102.toFixed(2)} full avg10=${fullAvg10.toFixed(2)} \u2014 hysteresis hold (floor: some ${hysteresisFloor}, full ${fullHysteresisFloor})`
|
|
35594
|
+
};
|
|
35595
|
+
}
|
|
35596
|
+
}
|
|
35597
|
+
return {
|
|
35598
|
+
state: "ok",
|
|
35599
|
+
reason: `some avg10=${someAvg102.toFixed(2)} full avg10=${fullAvg10.toFixed(2)} \u2014 below thresholds`
|
|
35600
|
+
};
|
|
35601
|
+
}
|
|
35602
|
+
var DEFAULTS, ResourceMonitor;
|
|
35603
|
+
var init_monitor = __esm({
|
|
35604
|
+
"packages/core/src/resources/monitor.ts"() {
|
|
35605
|
+
"use strict";
|
|
35606
|
+
init_linux_backend();
|
|
35607
|
+
DEFAULTS = {
|
|
35608
|
+
holdSomeAvg10: 10,
|
|
35609
|
+
backoffSomeAvg10: 20,
|
|
35610
|
+
holdFullAvg10: 5,
|
|
35611
|
+
backoffFullAvg10: 10,
|
|
35612
|
+
hysteresisPoints: 3,
|
|
35613
|
+
headroomBytes: 256 * 1024 * 1024,
|
|
35614
|
+
walWarnThresholdBytes: 256 * 1024 * 1024,
|
|
35615
|
+
pollIntervalMs: 1500
|
|
35616
|
+
};
|
|
35617
|
+
ResourceMonitor = class extends EventEmitter2 {
|
|
35618
|
+
thresholds;
|
|
35619
|
+
backend;
|
|
35620
|
+
_state = "ok";
|
|
35621
|
+
_timer = null;
|
|
35622
|
+
_running = false;
|
|
35623
|
+
constructor(cfg = {}) {
|
|
35624
|
+
super();
|
|
35625
|
+
this.thresholds = resolveThresholds(cfg);
|
|
35626
|
+
this.backend = cfg.backend ?? new LinuxResourceBackend();
|
|
35627
|
+
}
|
|
35628
|
+
// -------------------------------------------------------------------------
|
|
35629
|
+
// Public API
|
|
35630
|
+
// -------------------------------------------------------------------------
|
|
35631
|
+
/**
|
|
35632
|
+
* Current pressure state (only meaningful after at least one sample).
|
|
35633
|
+
*/
|
|
35634
|
+
get state() {
|
|
35635
|
+
return this._state;
|
|
35636
|
+
}
|
|
35637
|
+
/**
|
|
35638
|
+
* `true` while the continuous loop is running.
|
|
35639
|
+
*/
|
|
35640
|
+
get running() {
|
|
35641
|
+
return this._running;
|
|
35642
|
+
}
|
|
35643
|
+
/**
|
|
35644
|
+
* Take a single point-in-time resource sample.
|
|
35645
|
+
*
|
|
35646
|
+
* Safe to call with the daemon off (no child processes, bounded reads).
|
|
35647
|
+
* Does NOT update `this.state` — use {@link startContinuous} for that.
|
|
35648
|
+
*/
|
|
35649
|
+
async sample() {
|
|
35650
|
+
return this.backend.sample();
|
|
35651
|
+
}
|
|
35652
|
+
/**
|
|
35653
|
+
* Start the continuous polling loop.
|
|
35654
|
+
*
|
|
35655
|
+
* Returns a `stop()` function. Calling `stop()` is idempotent.
|
|
35656
|
+
*
|
|
35657
|
+
* Emits:
|
|
35658
|
+
* - `'sample'` on every poll (even if state did not change)
|
|
35659
|
+
* - `'transition'` when `PressureState` changes
|
|
35660
|
+
* - `'error'` if the backend throws (loop continues regardless)
|
|
35661
|
+
*/
|
|
35662
|
+
startContinuous() {
|
|
35663
|
+
if (this._running) {
|
|
35664
|
+
return () => this._stopLoop();
|
|
35665
|
+
}
|
|
35666
|
+
this._running = true;
|
|
35667
|
+
this._scheduleNext();
|
|
35668
|
+
return () => this._stopLoop();
|
|
35669
|
+
}
|
|
35670
|
+
/**
|
|
35671
|
+
* Stop the continuous polling loop. Idempotent.
|
|
35672
|
+
*/
|
|
35673
|
+
stop() {
|
|
35674
|
+
this._stopLoop();
|
|
35675
|
+
}
|
|
35676
|
+
// -------------------------------------------------------------------------
|
|
35677
|
+
// Private loop machinery
|
|
35678
|
+
// -------------------------------------------------------------------------
|
|
35679
|
+
_scheduleNext() {
|
|
35680
|
+
this._timer = setTimeout(() => {
|
|
35681
|
+
void this._tick();
|
|
35682
|
+
}, this.thresholds.pollIntervalMs);
|
|
35683
|
+
if (typeof this._timer.unref === "function") {
|
|
35684
|
+
this._timer.unref();
|
|
35685
|
+
}
|
|
35686
|
+
}
|
|
35687
|
+
async _tick() {
|
|
35688
|
+
if (!this._running) return;
|
|
35689
|
+
try {
|
|
35690
|
+
const sample = await this.backend.sample();
|
|
35691
|
+
this.emit("sample", sample);
|
|
35692
|
+
const { state: newState, reason } = evaluateState(sample, this.thresholds, this._state);
|
|
35693
|
+
if (newState !== this._state) {
|
|
35694
|
+
const transition = {
|
|
35695
|
+
from: this._state,
|
|
35696
|
+
to: newState,
|
|
35697
|
+
sample,
|
|
35698
|
+
reason
|
|
35699
|
+
};
|
|
35700
|
+
this._state = newState;
|
|
35701
|
+
this.emit("transition", transition);
|
|
35702
|
+
}
|
|
35703
|
+
} catch (err) {
|
|
35704
|
+
this.emit("error", err instanceof Error ? err : new Error(String(err)));
|
|
35705
|
+
}
|
|
35706
|
+
if (this._running) {
|
|
35707
|
+
this._scheduleNext();
|
|
35708
|
+
}
|
|
35709
|
+
}
|
|
35710
|
+
_stopLoop() {
|
|
35711
|
+
this._running = false;
|
|
35712
|
+
if (this._timer !== null) {
|
|
35713
|
+
clearTimeout(this._timer);
|
|
35714
|
+
this._timer = null;
|
|
35715
|
+
}
|
|
35716
|
+
}
|
|
35717
|
+
};
|
|
35718
|
+
}
|
|
35719
|
+
});
|
|
35720
|
+
|
|
35721
|
+
// packages/core/src/resources/supervisor-admit.ts
|
|
35722
|
+
import { connect } from "node:net";
|
|
35723
|
+
import { join as join5 } from "node:path";
|
|
35724
|
+
import { getCleoHome as getCleoHome2 } from "@cleocode/paths";
|
|
35725
|
+
function resolveSupervisorSocketPath() {
|
|
35726
|
+
return process.env.CLEO_SUPERVISOR_SOCKET ?? join5(getCleoHome2(), SUPERVISOR_SOCKET_NAME);
|
|
35727
|
+
}
|
|
35728
|
+
async function sendResourceAdmit(socketPath, cls, holderId, budget) {
|
|
35729
|
+
const result = await sendLeaseRequest(
|
|
35730
|
+
socketPath,
|
|
35731
|
+
{ kind: "resource_admit", class: cls, holder_id: holderId, budget },
|
|
35732
|
+
"resource_admit_result"
|
|
35733
|
+
);
|
|
35734
|
+
if ("unavailable" in result) return result;
|
|
35735
|
+
if (result.kind === "resource_admit_result") return result;
|
|
35736
|
+
return { unavailable: true, reason: `unexpected response kind: ${result.kind}` };
|
|
35737
|
+
}
|
|
35738
|
+
async function sendResourceRelease(socketPath, cls, holderId) {
|
|
35739
|
+
const result = await sendLeaseRequest(
|
|
35740
|
+
socketPath,
|
|
35741
|
+
{ kind: "resource_release", class: cls, holder_id: holderId },
|
|
35742
|
+
"resource_release_result"
|
|
35743
|
+
);
|
|
35744
|
+
if ("unavailable" in result) return result;
|
|
35745
|
+
if (result.kind === "resource_release_result") return result;
|
|
35746
|
+
return { unavailable: true, reason: `unexpected response kind: ${result.kind}` };
|
|
35747
|
+
}
|
|
35748
|
+
function sendLeaseRequest(socketPath, request, expectedKind) {
|
|
35749
|
+
return new Promise((resolve6) => {
|
|
35750
|
+
const id = `ra-${process.pid}-${request.kind}`;
|
|
35751
|
+
const envelope = LeaseIpcRequestEnvelopeSchema.parse({
|
|
35752
|
+
protocol_version: LEASE_IPC_PROTOCOL_VERSION,
|
|
35753
|
+
id,
|
|
35754
|
+
direction: "request",
|
|
35755
|
+
request
|
|
35756
|
+
});
|
|
35757
|
+
const line2 = `${JSON.stringify(envelope)}
|
|
35758
|
+
`;
|
|
35759
|
+
let settled = false;
|
|
35760
|
+
let buffer = "";
|
|
35761
|
+
const socket = connect(socketPath);
|
|
35762
|
+
const done = (value) => {
|
|
35763
|
+
if (settled) return;
|
|
35764
|
+
settled = true;
|
|
35765
|
+
socket.removeAllListeners();
|
|
35766
|
+
socket.destroy();
|
|
35767
|
+
resolve6(value);
|
|
35768
|
+
};
|
|
35769
|
+
const degrade = (reason) => done({ unavailable: true, reason });
|
|
35770
|
+
socket.setTimeout(REQUEST_TIMEOUT_MS);
|
|
35771
|
+
const connectTimer = setTimeout(
|
|
35772
|
+
() => degrade("supervisor connect timeout"),
|
|
35773
|
+
CONNECT_TIMEOUT_MS
|
|
35774
|
+
);
|
|
35775
|
+
socket.on("connect", () => {
|
|
35776
|
+
clearTimeout(connectTimer);
|
|
35777
|
+
socket.write(line2);
|
|
35778
|
+
});
|
|
35779
|
+
socket.on("timeout", () => degrade("supervisor request timeout"));
|
|
35780
|
+
socket.on("error", (err) => {
|
|
35781
|
+
clearTimeout(connectTimer);
|
|
35782
|
+
degrade(err.message);
|
|
35783
|
+
});
|
|
35784
|
+
socket.on("close", () => {
|
|
35785
|
+
if (!settled) degrade("supervisor closed the connection before responding");
|
|
35786
|
+
});
|
|
35787
|
+
socket.on("data", (chunk) => {
|
|
35788
|
+
buffer += chunk.toString("utf8");
|
|
35789
|
+
const nl = buffer.indexOf("\n");
|
|
35790
|
+
if (nl < 0) return;
|
|
35791
|
+
let json2;
|
|
35792
|
+
try {
|
|
35793
|
+
json2 = JSON.parse(buffer.slice(0, nl));
|
|
35794
|
+
} catch (cause) {
|
|
35795
|
+
degrade(`malformed lease-ipc frame: ${cause instanceof Error ? cause.message : cause}`);
|
|
35796
|
+
return;
|
|
35797
|
+
}
|
|
35798
|
+
const parsed = LeaseIpcResponseEnvelopeSchema.safeParse(json2);
|
|
35799
|
+
if (!parsed.success) {
|
|
35800
|
+
degrade(`off-contract lease-ipc response: ${parsed.error.message}`);
|
|
35801
|
+
return;
|
|
35802
|
+
}
|
|
35803
|
+
const { response } = parsed.data;
|
|
35804
|
+
if (response.kind === expectedKind) {
|
|
35805
|
+
done(response);
|
|
35806
|
+
return;
|
|
35807
|
+
}
|
|
35808
|
+
if (response.kind === "error") {
|
|
35809
|
+
degrade(`${response.code}: ${response.message}`);
|
|
35810
|
+
return;
|
|
35811
|
+
}
|
|
35812
|
+
degrade(`unexpected lease-ipc response kind for ${request.kind}: ${response.kind}`);
|
|
35813
|
+
});
|
|
35814
|
+
});
|
|
35815
|
+
}
|
|
35816
|
+
var SUPERVISOR_SOCKET_NAME, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS;
|
|
35817
|
+
var init_supervisor_admit = __esm({
|
|
35818
|
+
"packages/core/src/resources/supervisor-admit.ts"() {
|
|
35819
|
+
"use strict";
|
|
35820
|
+
init_src();
|
|
35821
|
+
SUPERVISOR_SOCKET_NAME = "cleo-supervisor.sock";
|
|
35822
|
+
CONNECT_TIMEOUT_MS = 250;
|
|
35823
|
+
REQUEST_TIMEOUT_MS = 2e3;
|
|
35824
|
+
}
|
|
35825
|
+
});
|
|
35826
|
+
|
|
35827
|
+
// packages/core/src/resources/governor.ts
|
|
35828
|
+
var governor_exports = {};
|
|
35829
|
+
__export(governor_exports, {
|
|
35830
|
+
ResourceGovernor: () => ResourceGovernor,
|
|
35831
|
+
_resetGovernorStateForTest: () => _resetGovernorStateForTest,
|
|
35832
|
+
computeClassBudget: () => computeClassBudget,
|
|
35833
|
+
governor: () => governor,
|
|
35834
|
+
governorSlotDir: () => governorSlotDir,
|
|
35835
|
+
resolveGovernorMode: () => resolveGovernorMode,
|
|
35836
|
+
slotFileCount: () => slotFileCount
|
|
35837
|
+
});
|
|
35838
|
+
import { existsSync as existsSync3, mkdirSync, readdirSync as readdirSync2, writeFileSync } from "node:fs";
|
|
35839
|
+
import { availableParallelism, totalmem } from "node:os";
|
|
35840
|
+
import { join as join6 } from "node:path";
|
|
35312
35841
|
import lockfile from "proper-lockfile";
|
|
35842
|
+
function log2() {
|
|
35843
|
+
if (_log2 === null) _log2 = getLogger("resource-governor");
|
|
35844
|
+
return _log2;
|
|
35845
|
+
}
|
|
35846
|
+
function resolveGovernorMode() {
|
|
35847
|
+
if (_cachedMode2 !== null) return _cachedMode2;
|
|
35848
|
+
const raw = process.env.CLEO_RESOURCES_MODE;
|
|
35849
|
+
_cachedMode2 = raw === "supervisor" || raw === "local" || raw === "off" ? raw : "local";
|
|
35850
|
+
return _cachedMode2;
|
|
35851
|
+
}
|
|
35852
|
+
function logSupervisorDegradeOnce(reason) {
|
|
35853
|
+
if (_supervisorDegradeLogged) return;
|
|
35854
|
+
_supervisorDegradeLogged = true;
|
|
35855
|
+
log2().info(
|
|
35856
|
+
`CLEO_RESOURCES_MODE=supervisor but the arbiter is unreachable (${reason}); degrading to local-mode admission for the process lifetime.`
|
|
35857
|
+
);
|
|
35858
|
+
}
|
|
35859
|
+
function _resetGovernorStateForTest() {
|
|
35860
|
+
_cachedMode2 = null;
|
|
35861
|
+
_supervisorDegradeLogged = false;
|
|
35862
|
+
}
|
|
35863
|
+
function someAvg10(sample) {
|
|
35864
|
+
const some = sample.globalPressure?.some ?? sample.slicePressure?.some;
|
|
35865
|
+
return some?.avg10 ?? 0;
|
|
35866
|
+
}
|
|
35867
|
+
function computeClassBudget(cls, sample, opts = {}) {
|
|
35868
|
+
if (cls === "interactive-cli") return Number.POSITIVE_INFINITY;
|
|
35869
|
+
const cpus = Math.max(1, opts.cpuCount ?? availableParallelism());
|
|
35870
|
+
const totalBytes = opts.totalMemBytes ?? totalmem();
|
|
35871
|
+
const headroomBytes = (opts.headroomMb ?? 2048) * MB;
|
|
35872
|
+
const hold = opts.holdSomeAvg10 ?? 10;
|
|
35873
|
+
const floor = opts.floorSomeAvg10 ?? 25;
|
|
35874
|
+
const some = someAvg10(sample);
|
|
35875
|
+
const availBytes = sample.memAvailableBytes ?? totalBytes;
|
|
35876
|
+
const fullStall = sample.globalPressure?.full?.avg10 ?? sample.slicePressure?.full?.avg10 ?? 0;
|
|
35877
|
+
const backoff = some > floor || fullStall > 10;
|
|
35878
|
+
switch (cls) {
|
|
35879
|
+
case "full-build":
|
|
35880
|
+
return 1;
|
|
35881
|
+
case "agent-session": {
|
|
35882
|
+
const estRamBytes = (opts.agentEstRamMb ?? 4096) * MB;
|
|
35883
|
+
const byMem = Math.floor((availBytes - headroomBytes) / estRamBytes);
|
|
35884
|
+
return clamp(1, byMem, Math.max(1, cpus - 2));
|
|
35885
|
+
}
|
|
35886
|
+
case "llm-call":
|
|
35887
|
+
return Math.max(1, cpus - 2);
|
|
35888
|
+
case "test-run":
|
|
35889
|
+
case "scoped-build": {
|
|
35890
|
+
const base = Math.max(1, Math.floor(cpus / 4));
|
|
35891
|
+
if (some > floor) return 1;
|
|
35892
|
+
if (some > hold) return Math.max(1, Math.floor(base / 2));
|
|
35893
|
+
return base;
|
|
35894
|
+
}
|
|
35895
|
+
case "db-heavy":
|
|
35896
|
+
return backoff ? 0 : 1;
|
|
35897
|
+
case "background-autonomous":
|
|
35898
|
+
return some > hold || fullStall > 5 ? 0 : 1;
|
|
35899
|
+
default:
|
|
35900
|
+
return 1;
|
|
35901
|
+
}
|
|
35902
|
+
}
|
|
35903
|
+
function clamp(lo, v, hi) {
|
|
35904
|
+
return Math.max(lo, Math.min(v, hi));
|
|
35905
|
+
}
|
|
35906
|
+
function governorSlotDir(cls) {
|
|
35907
|
+
return join6(getCleoHome(), "locks", `resource-${cls}`);
|
|
35908
|
+
}
|
|
35909
|
+
function ensureSlotFiles(dir, count) {
|
|
35910
|
+
if (!existsSync3(dir)) mkdirSync(dir, { recursive: true });
|
|
35911
|
+
const paths = [];
|
|
35912
|
+
for (let i = 0; i < count; i++) {
|
|
35913
|
+
const p = join6(dir, `slot-${i}.lock`);
|
|
35914
|
+
if (!existsSync3(p)) writeFileSync(p, "", { flag: "a" });
|
|
35915
|
+
paths.push(p);
|
|
35916
|
+
}
|
|
35917
|
+
return paths;
|
|
35918
|
+
}
|
|
35919
|
+
function sleep2(ms) {
|
|
35920
|
+
return new Promise((r) => setTimeout(r, ms));
|
|
35921
|
+
}
|
|
35922
|
+
function passThroughGrant(cls) {
|
|
35923
|
+
return {
|
|
35924
|
+
deferred: false,
|
|
35925
|
+
class: cls,
|
|
35926
|
+
slot: -1,
|
|
35927
|
+
acquiredAtMs: Date.now(),
|
|
35928
|
+
release: async () => {
|
|
35929
|
+
}
|
|
35930
|
+
};
|
|
35931
|
+
}
|
|
35932
|
+
function deferral(cls, reason, retryAfterMs) {
|
|
35933
|
+
return { deferred: true, class: cls, retryAfterMs, reason };
|
|
35934
|
+
}
|
|
35935
|
+
function supervisorHolderId(cls) {
|
|
35936
|
+
_supervisorHolderSeq += 1;
|
|
35937
|
+
return `${process.pid}:${cls}:${_supervisorHolderSeq}`;
|
|
35938
|
+
}
|
|
35939
|
+
function shuffledIndices(n) {
|
|
35940
|
+
const order = Array.from({ length: n }, (_, i) => i);
|
|
35941
|
+
for (let i = order.length - 1; i > 0; i--) {
|
|
35942
|
+
const j = Math.floor(Math.random() * (i + 1));
|
|
35943
|
+
const a = order[i];
|
|
35944
|
+
const b = order[j];
|
|
35945
|
+
if (a !== void 0 && b !== void 0) {
|
|
35946
|
+
order[i] = b;
|
|
35947
|
+
order[j] = a;
|
|
35948
|
+
}
|
|
35949
|
+
}
|
|
35950
|
+
return order;
|
|
35951
|
+
}
|
|
35952
|
+
async function countHeldSlots(cls, budget) {
|
|
35953
|
+
const dir = governorSlotDir(cls);
|
|
35954
|
+
if (!existsSync3(dir)) return 0;
|
|
35955
|
+
const slots = ensureSlotFiles(dir, budget);
|
|
35956
|
+
let held = 0;
|
|
35957
|
+
for (const path2 of slots) {
|
|
35958
|
+
try {
|
|
35959
|
+
const release = await lockfile.lock(path2, { retries: 0, stale: STALE_MS, realpath: false });
|
|
35960
|
+
await release();
|
|
35961
|
+
} catch {
|
|
35962
|
+
held++;
|
|
35963
|
+
}
|
|
35964
|
+
}
|
|
35965
|
+
return held;
|
|
35966
|
+
}
|
|
35967
|
+
function slotFileCount(cls) {
|
|
35968
|
+
const dir = governorSlotDir(cls);
|
|
35969
|
+
if (!existsSync3(dir)) return 0;
|
|
35970
|
+
return readdirSync2(dir).filter((f) => f.startsWith("slot-") && f.endsWith(".lock")).length;
|
|
35971
|
+
}
|
|
35972
|
+
var _log2, _cachedMode2, _supervisorDegradeLogged, MB, STALE_MS, _supervisorHolderSeq, ResourceGovernor, governor;
|
|
35973
|
+
var init_governor = __esm({
|
|
35974
|
+
"packages/core/src/resources/governor.ts"() {
|
|
35975
|
+
"use strict";
|
|
35976
|
+
init_src();
|
|
35977
|
+
init_logger2();
|
|
35978
|
+
init_paths();
|
|
35979
|
+
init_monitor();
|
|
35980
|
+
init_supervisor_admit();
|
|
35981
|
+
_log2 = null;
|
|
35982
|
+
_cachedMode2 = null;
|
|
35983
|
+
_supervisorDegradeLogged = false;
|
|
35984
|
+
MB = 1024 * 1024;
|
|
35985
|
+
STALE_MS = 6e5;
|
|
35986
|
+
_supervisorHolderSeq = 0;
|
|
35987
|
+
ResourceGovernor = class {
|
|
35988
|
+
/**
|
|
35989
|
+
* Acquire one slot of `cls`. Returns a {@link ResourceGrant} on success or a
|
|
35990
|
+
* {@link ResourceDeferral} on denial. Never throws for admission control;
|
|
35991
|
+
* only genuinely unexpected I/O errors propagate.
|
|
35992
|
+
*/
|
|
35993
|
+
async acquire(cls, opts = {}) {
|
|
35994
|
+
const mode = resolveGovernorMode();
|
|
35995
|
+
if (mode === "off" || cls === "interactive-cli") {
|
|
35996
|
+
return passThroughGrant(cls);
|
|
35997
|
+
}
|
|
35998
|
+
const sample = opts.sample ?? await (opts.monitor ?? new ResourceMonitor()).sample();
|
|
35999
|
+
const budget = computeClassBudget(cls, sample, opts);
|
|
36000
|
+
if (!Number.isFinite(budget)) return passThroughGrant(cls);
|
|
36001
|
+
if (budget <= 0) {
|
|
36002
|
+
return deferral(
|
|
36003
|
+
cls,
|
|
36004
|
+
`class '${cls}' budget is 0 under current pressure (some avg10=${someAvg10(sample).toFixed(1)})`,
|
|
36005
|
+
DEFAULT_RESOURCE_RETRY_AFTER_MS
|
|
36006
|
+
);
|
|
36007
|
+
}
|
|
36008
|
+
if (mode === "supervisor") {
|
|
36009
|
+
const viaSupervisor = await this.acquireViaSupervisor(cls, Math.floor(budget));
|
|
36010
|
+
if (viaSupervisor !== null) return viaSupervisor;
|
|
36011
|
+
}
|
|
36012
|
+
const dir = governorSlotDir(cls);
|
|
36013
|
+
const slots = ensureSlotFiles(dir, budget);
|
|
36014
|
+
const blocking = opts.blocking ?? true;
|
|
36015
|
+
const timeoutMs = opts.timeoutMs ?? 36e5;
|
|
36016
|
+
const pollMs = opts.pollMs ?? 200;
|
|
36017
|
+
const startedAt = Date.now();
|
|
36018
|
+
do {
|
|
36019
|
+
const order = shuffledIndices(slots.length);
|
|
36020
|
+
for (const idx of order) {
|
|
36021
|
+
const path2 = slots[idx];
|
|
36022
|
+
if (!path2) continue;
|
|
36023
|
+
try {
|
|
36024
|
+
const release = await lockfile.lock(path2, {
|
|
36025
|
+
retries: 0,
|
|
36026
|
+
stale: STALE_MS,
|
|
36027
|
+
realpath: false
|
|
36028
|
+
});
|
|
36029
|
+
let released = false;
|
|
36030
|
+
return {
|
|
36031
|
+
deferred: false,
|
|
36032
|
+
class: cls,
|
|
36033
|
+
slot: idx,
|
|
36034
|
+
acquiredAtMs: Date.now(),
|
|
36035
|
+
release: async () => {
|
|
36036
|
+
if (released) return;
|
|
36037
|
+
released = true;
|
|
36038
|
+
try {
|
|
36039
|
+
await release();
|
|
36040
|
+
} catch {
|
|
36041
|
+
}
|
|
36042
|
+
}
|
|
36043
|
+
};
|
|
36044
|
+
} catch {
|
|
36045
|
+
}
|
|
36046
|
+
}
|
|
36047
|
+
if (!blocking) break;
|
|
36048
|
+
await sleep2(pollMs);
|
|
36049
|
+
} while (Date.now() - startedAt < timeoutMs);
|
|
36050
|
+
return deferral(
|
|
36051
|
+
cls,
|
|
36052
|
+
`class '${cls}' is at capacity (${budget} slot(s)); ` + (blocking ? `timed out after ${timeoutMs}ms` : "no slot free"),
|
|
36053
|
+
Math.min(pollMs * 4, DEFAULT_RESOURCE_RETRY_AFTER_MS)
|
|
36054
|
+
);
|
|
36055
|
+
}
|
|
36056
|
+
/**
|
|
36057
|
+
* Route an admission through the supervisor's central `resource_admit` verb.
|
|
36058
|
+
* Returns a grant (whose `release` calls `resource_release`) or a deferral, or
|
|
36059
|
+
* `null` when the supervisor is unreachable so the caller degrades to the
|
|
36060
|
+
* local slot engine. Never throws — a dead arbiter never deadlocks work.
|
|
36061
|
+
*
|
|
36062
|
+
* @task T12001
|
|
36063
|
+
*/
|
|
36064
|
+
async acquireViaSupervisor(cls, budget) {
|
|
36065
|
+
const socketPath = resolveSupervisorSocketPath();
|
|
36066
|
+
const holderId = supervisorHolderId(cls);
|
|
36067
|
+
const reply = await sendResourceAdmit(socketPath, cls, holderId, budget);
|
|
36068
|
+
if ("unavailable" in reply) {
|
|
36069
|
+
logSupervisorDegradeOnce(reply.reason);
|
|
36070
|
+
return null;
|
|
36071
|
+
}
|
|
36072
|
+
if (reply.disposition === "deferred") {
|
|
36073
|
+
return deferral(
|
|
36074
|
+
cls,
|
|
36075
|
+
`class '${cls}' deferred by supervisor (budget ${budget})`,
|
|
36076
|
+
reply.retry_after_ms || DEFAULT_RESOURCE_RETRY_AFTER_MS
|
|
36077
|
+
);
|
|
36078
|
+
}
|
|
36079
|
+
let released = false;
|
|
36080
|
+
return {
|
|
36081
|
+
deferred: false,
|
|
36082
|
+
class: cls,
|
|
36083
|
+
slot: 0,
|
|
36084
|
+
acquiredAtMs: Date.now(),
|
|
36085
|
+
release: async () => {
|
|
36086
|
+
if (released) return;
|
|
36087
|
+
released = true;
|
|
36088
|
+
await sendResourceRelease(socketPath, cls, holderId).catch(() => {
|
|
36089
|
+
});
|
|
36090
|
+
}
|
|
36091
|
+
};
|
|
36092
|
+
}
|
|
36093
|
+
/** Non-blocking single-pass acquire (admission semantics). */
|
|
36094
|
+
async tryAcquire(cls, opts = {}) {
|
|
36095
|
+
return this.acquire(cls, { ...opts, blocking: false });
|
|
36096
|
+
}
|
|
36097
|
+
/**
|
|
36098
|
+
* Currently-grantable slot count for `cls` = budget − held. Held is the count
|
|
36099
|
+
* of slot files currently locked. `Infinity` for ungated classes.
|
|
36100
|
+
*/
|
|
36101
|
+
async available(cls, opts = {}) {
|
|
36102
|
+
if (resolveGovernorMode() === "off" || cls === "interactive-cli") {
|
|
36103
|
+
return Number.POSITIVE_INFINITY;
|
|
36104
|
+
}
|
|
36105
|
+
const sample = opts.sample ?? await (opts.monitor ?? new ResourceMonitor()).sample();
|
|
36106
|
+
const budget = computeClassBudget(cls, sample, opts);
|
|
36107
|
+
if (!Number.isFinite(budget)) return Number.POSITIVE_INFINITY;
|
|
36108
|
+
if (budget <= 0) return 0;
|
|
36109
|
+
const held = await countHeldSlots(cls, budget);
|
|
36110
|
+
return Math.max(0, budget - held);
|
|
36111
|
+
}
|
|
36112
|
+
};
|
|
36113
|
+
governor = new ResourceGovernor();
|
|
36114
|
+
}
|
|
36115
|
+
});
|
|
36116
|
+
|
|
36117
|
+
// packages/core/src/store/lock.ts
|
|
36118
|
+
import lockfile2 from "proper-lockfile";
|
|
35313
36119
|
async function acquireLock(filePath, options) {
|
|
35314
36120
|
try {
|
|
35315
|
-
const release = await
|
|
36121
|
+
const release = await lockfile2.lock(filePath, {
|
|
35316
36122
|
...DEFAULT_LOCK_OPTIONS,
|
|
35317
36123
|
...options?.stale !== void 0 && { stale: options.stale },
|
|
35318
36124
|
...options?.retries !== void 0 && {
|
|
@@ -35358,7 +36164,7 @@ var init_lock = __esm({
|
|
|
35358
36164
|
});
|
|
35359
36165
|
|
|
35360
36166
|
// packages/core/src/store/atomic.ts
|
|
35361
|
-
import { mkdir, readFile as
|
|
36167
|
+
import { mkdir, readFile as readFile3, rename, unlink } from "node:fs/promises";
|
|
35362
36168
|
import { dirname as dirname3 } from "node:path";
|
|
35363
36169
|
import writeFileAtomic from "write-file-atomic";
|
|
35364
36170
|
async function atomicWrite(filePath, data, options) {
|
|
@@ -35374,7 +36180,7 @@ async function atomicWrite(filePath, data, options) {
|
|
|
35374
36180
|
}
|
|
35375
36181
|
async function safeReadFile(filePath) {
|
|
35376
36182
|
try {
|
|
35377
|
-
return await
|
|
36183
|
+
return await readFile3(filePath, "utf8");
|
|
35378
36184
|
} catch (err) {
|
|
35379
36185
|
if (err instanceof Error && "code" in err && err.code === "ENOENT") {
|
|
35380
36186
|
return null;
|
|
@@ -35395,34 +36201,34 @@ var init_atomic = __esm({
|
|
|
35395
36201
|
});
|
|
35396
36202
|
|
|
35397
36203
|
// packages/core/src/store/backup.ts
|
|
35398
|
-
import { copyFile, rename as fsRename, mkdir as mkdir2, readdir, stat, unlink as unlink2 } from "node:fs/promises";
|
|
35399
|
-
import { basename, join as
|
|
36204
|
+
import { copyFile, rename as fsRename, mkdir as mkdir2, readdir, stat as stat2, unlink as unlink2 } from "node:fs/promises";
|
|
36205
|
+
import { basename, join as join7 } from "node:path";
|
|
35400
36206
|
async function createBackup(filePath, backupDir, maxBackups = DEFAULT_MAX_BACKUPS) {
|
|
35401
36207
|
try {
|
|
35402
36208
|
await mkdir2(backupDir, { recursive: true });
|
|
35403
36209
|
const fileName = basename(filePath);
|
|
35404
36210
|
try {
|
|
35405
|
-
await
|
|
36211
|
+
await stat2(filePath);
|
|
35406
36212
|
} catch {
|
|
35407
36213
|
throw new CleoError(3 /* FILE_ERROR */, `Cannot backup: source file not found: ${filePath}`);
|
|
35408
36214
|
}
|
|
35409
36215
|
for (let i = maxBackups; i >= 1; i--) {
|
|
35410
|
-
const current =
|
|
36216
|
+
const current = join7(backupDir, `${fileName}.${i}`);
|
|
35411
36217
|
if (i === maxBackups) {
|
|
35412
36218
|
try {
|
|
35413
36219
|
await unlink2(current);
|
|
35414
36220
|
} catch {
|
|
35415
36221
|
}
|
|
35416
36222
|
} else {
|
|
35417
|
-
const next =
|
|
36223
|
+
const next = join7(backupDir, `${fileName}.${i + 1}`);
|
|
35418
36224
|
try {
|
|
35419
|
-
await
|
|
36225
|
+
await stat2(current);
|
|
35420
36226
|
await fsRename(current, next);
|
|
35421
36227
|
} catch {
|
|
35422
36228
|
}
|
|
35423
36229
|
}
|
|
35424
36230
|
}
|
|
35425
|
-
const backupPath =
|
|
36231
|
+
const backupPath = join7(backupDir, `${fileName}.1`);
|
|
35426
36232
|
await copyFile(filePath, backupPath);
|
|
35427
36233
|
return backupPath;
|
|
35428
36234
|
} catch (err) {
|
|
@@ -35701,8 +36507,8 @@ var init_json2 = __esm({
|
|
|
35701
36507
|
});
|
|
35702
36508
|
|
|
35703
36509
|
// packages/core/src/scaffold/ensure-config.ts
|
|
35704
|
-
import { existsSync as
|
|
35705
|
-
import { basename as basename2, dirname as dirname4, join as
|
|
36510
|
+
import { existsSync as existsSync4, readFileSync } from "node:fs";
|
|
36511
|
+
import { basename as basename2, dirname as dirname4, join as join8, resolve as resolve2 } from "node:path";
|
|
35706
36512
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
35707
36513
|
function getPackageRoot() {
|
|
35708
36514
|
const thisFile = fileURLToPath2(import.meta.url);
|
|
@@ -35710,7 +36516,7 @@ function getPackageRoot() {
|
|
|
35710
36516
|
}
|
|
35711
36517
|
function getCleoVersion() {
|
|
35712
36518
|
try {
|
|
35713
|
-
const pkgPath =
|
|
36519
|
+
const pkgPath = join8(getPackageRoot(), "package.json");
|
|
35714
36520
|
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
35715
36521
|
return pkg.version ?? "0.0.0";
|
|
35716
36522
|
} catch {
|
|
@@ -35729,25 +36535,25 @@ var init_ensure_config = __esm({
|
|
|
35729
36535
|
// packages/core/src/store/exodus/archive.ts
|
|
35730
36536
|
import {
|
|
35731
36537
|
copyFileSync as copyFileSync2,
|
|
35732
|
-
existsSync as
|
|
35733
|
-
mkdirSync,
|
|
36538
|
+
existsSync as existsSync5,
|
|
36539
|
+
mkdirSync as mkdirSync2,
|
|
35734
36540
|
renameSync,
|
|
35735
36541
|
unlinkSync,
|
|
35736
|
-
writeFileSync
|
|
36542
|
+
writeFileSync as writeFileSync2
|
|
35737
36543
|
} from "node:fs";
|
|
35738
|
-
import { basename as basename3, join as
|
|
36544
|
+
import { basename as basename3, join as join9 } from "node:path";
|
|
35739
36545
|
function scopeBaseDir(scope, cwd) {
|
|
35740
36546
|
return scope === "project" ? resolveCleoDir(cwd) : getCleoHome();
|
|
35741
36547
|
}
|
|
35742
36548
|
function exodusArchiveDir(scope, cwd) {
|
|
35743
|
-
return
|
|
36549
|
+
return join9(scopeBaseDir(scope, cwd), ARCHIVE_DIR_NAME);
|
|
35744
36550
|
}
|
|
35745
36551
|
function exodusMarkerPath(scope, cwd) {
|
|
35746
|
-
return
|
|
36552
|
+
return join9(scopeBaseDir(scope, cwd), MARKER_FILENAME_BY_SCOPE[scope]);
|
|
35747
36553
|
}
|
|
35748
36554
|
function hasExodusCompleteMarker(scope, cwd) {
|
|
35749
36555
|
try {
|
|
35750
|
-
return
|
|
36556
|
+
return existsSync5(exodusMarkerPath(scope, cwd));
|
|
35751
36557
|
} catch {
|
|
35752
36558
|
return false;
|
|
35753
36559
|
}
|
|
@@ -35755,7 +36561,7 @@ function hasExodusCompleteMarker(scope, cwd) {
|
|
|
35755
36561
|
function writeExodusCompleteMarker(scope, archivedSources, cwd) {
|
|
35756
36562
|
const markerPath = exodusMarkerPath(scope, cwd);
|
|
35757
36563
|
const baseDir = scopeBaseDir(scope, cwd);
|
|
35758
|
-
|
|
36564
|
+
mkdirSync2(baseDir, { recursive: true });
|
|
35759
36565
|
const marker = {
|
|
35760
36566
|
version: 1,
|
|
35761
36567
|
scope,
|
|
@@ -35764,17 +36570,17 @@ function writeExodusCompleteMarker(scope, archivedSources, cwd) {
|
|
|
35764
36570
|
archivedSources: [...archivedSources]
|
|
35765
36571
|
};
|
|
35766
36572
|
const tmpPath = `${markerPath}.tmp`;
|
|
35767
|
-
|
|
36573
|
+
writeFileSync2(tmpPath, JSON.stringify(marker, null, 2) + "\n", "utf8");
|
|
35768
36574
|
renameSync(tmpPath, markerPath);
|
|
35769
|
-
|
|
36575
|
+
log3.info({ scope, markerPath, archivedSources }, "exodus: wrote completion marker");
|
|
35770
36576
|
return markerPath;
|
|
35771
36577
|
}
|
|
35772
36578
|
function moveFileInto(srcPath, destDir) {
|
|
35773
|
-
|
|
35774
|
-
let dest =
|
|
35775
|
-
if (
|
|
36579
|
+
mkdirSync2(destDir, { recursive: true });
|
|
36580
|
+
let dest = join9(destDir, basename3(srcPath));
|
|
36581
|
+
if (existsSync5(dest)) {
|
|
35776
36582
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "").replace(/Z$/, "Z");
|
|
35777
|
-
dest =
|
|
36583
|
+
dest = join9(destDir, `${basename3(srcPath)}.${stamp}`);
|
|
35778
36584
|
}
|
|
35779
36585
|
try {
|
|
35780
36586
|
renameSync(srcPath, dest);
|
|
@@ -35785,25 +36591,25 @@ function moveFileInto(srcPath, destDir) {
|
|
|
35785
36591
|
return dest;
|
|
35786
36592
|
}
|
|
35787
36593
|
function archiveSourceDb(source, cwd) {
|
|
35788
|
-
if (!
|
|
36594
|
+
if (!existsSync5(source.path)) {
|
|
35789
36595
|
return { name: source.name, sourcePath: source.path, archivedTo: null, action: "absent" };
|
|
35790
36596
|
}
|
|
35791
36597
|
const destDir = exodusArchiveDir(source.targetScope, cwd);
|
|
35792
36598
|
const archivedTo = moveFileInto(source.path, destDir);
|
|
35793
36599
|
for (const suffix of SIDECAR_SUFFIXES) {
|
|
35794
36600
|
const sidecar = `${source.path}${suffix}`;
|
|
35795
|
-
if (
|
|
36601
|
+
if (existsSync5(sidecar)) {
|
|
35796
36602
|
try {
|
|
35797
36603
|
moveFileInto(sidecar, destDir);
|
|
35798
36604
|
} catch (err) {
|
|
35799
|
-
|
|
36605
|
+
log3.warn(
|
|
35800
36606
|
{ err, sidecar, sourceName: source.name },
|
|
35801
36607
|
"exodus-archive: failed to archive sidecar (non-fatal)"
|
|
35802
36608
|
);
|
|
35803
36609
|
}
|
|
35804
36610
|
}
|
|
35805
36611
|
}
|
|
35806
|
-
|
|
36612
|
+
log3.info(
|
|
35807
36613
|
{ sourceName: source.name, sourcePath: source.path, archivedTo, scope: source.targetScope },
|
|
35808
36614
|
"exodus-archive: archived legacy source DB"
|
|
35809
36615
|
);
|
|
@@ -35838,7 +36644,7 @@ function detectStrandedResidue(sources, cwd) {
|
|
|
35838
36644
|
const stranded = [];
|
|
35839
36645
|
for (const source of sources) {
|
|
35840
36646
|
if (!markedScopes.has(source.targetScope)) continue;
|
|
35841
|
-
if (
|
|
36647
|
+
if (existsSync5(source.path)) {
|
|
35842
36648
|
stranded.push({ name: source.name, path: source.path, scope: source.targetScope });
|
|
35843
36649
|
}
|
|
35844
36650
|
}
|
|
@@ -35854,14 +36660,14 @@ function archiveStrandedResidue(stranded, sources, cwd) {
|
|
|
35854
36660
|
}
|
|
35855
36661
|
return results;
|
|
35856
36662
|
}
|
|
35857
|
-
var
|
|
36663
|
+
var log3, ARCHIVE_DIR_NAME, MARKER_FILENAME_BY_SCOPE, SIDECAR_SUFFIXES;
|
|
35858
36664
|
var init_archive2 = __esm({
|
|
35859
36665
|
"packages/core/src/store/exodus/archive.ts"() {
|
|
35860
36666
|
"use strict";
|
|
35861
36667
|
init_logger2();
|
|
35862
36668
|
init_paths();
|
|
35863
36669
|
init_ensure_config();
|
|
35864
|
-
|
|
36670
|
+
log3 = getLogger("exodus-archive");
|
|
35865
36671
|
ARCHIVE_DIR_NAME = "_archive";
|
|
35866
36672
|
MARKER_FILENAME_BY_SCOPE = {
|
|
35867
36673
|
project: "exodus-complete",
|
|
@@ -35872,13 +36678,13 @@ var init_archive2 = __esm({
|
|
|
35872
36678
|
});
|
|
35873
36679
|
|
|
35874
36680
|
// packages/core/src/project-info.ts
|
|
35875
|
-
import { existsSync as
|
|
35876
|
-
import { join as
|
|
36681
|
+
import { existsSync as existsSync6, readFileSync as readFileSync2, writeFileSync as writeFileSync3 } from "node:fs";
|
|
36682
|
+
import { join as join10 } from "node:path";
|
|
35877
36683
|
function getProjectInfoSync(cwd) {
|
|
35878
36684
|
const projectRoot = resolveOrCwd(cwd);
|
|
35879
36685
|
const cleoDir = getCleoDirAbsolute(projectRoot);
|
|
35880
|
-
const infoPath =
|
|
35881
|
-
if (!
|
|
36686
|
+
const infoPath = join10(cleoDir, "project-info.json");
|
|
36687
|
+
if (!existsSync6(infoPath)) return null;
|
|
35882
36688
|
try {
|
|
35883
36689
|
const raw = readFileSync2(infoPath, "utf-8");
|
|
35884
36690
|
const data = JSON.parse(raw);
|
|
@@ -35905,8 +36711,8 @@ var init_project_info = __esm({
|
|
|
35905
36711
|
});
|
|
35906
36712
|
|
|
35907
36713
|
// packages/core/src/store/worktree-isolation-guard.ts
|
|
35908
|
-
import { existsSync as
|
|
35909
|
-
import { dirname as dirname5, join as
|
|
36714
|
+
import { existsSync as existsSync7, statSync } from "node:fs";
|
|
36715
|
+
import { dirname as dirname5, join as join11 } from "node:path";
|
|
35910
36716
|
function assertDbPathIsNotWorktreeResident(role, cwd) {
|
|
35911
36717
|
let cleoDir;
|
|
35912
36718
|
try {
|
|
@@ -35915,10 +36721,10 @@ function assertDbPathIsNotWorktreeResident(role, cwd) {
|
|
|
35915
36721
|
return;
|
|
35916
36722
|
}
|
|
35917
36723
|
const projectRoot = dirname5(cleoDir);
|
|
35918
|
-
const projectGit =
|
|
36724
|
+
const projectGit = join11(projectRoot, ".git");
|
|
35919
36725
|
let isWorktreeGitlink = false;
|
|
35920
36726
|
try {
|
|
35921
|
-
isWorktreeGitlink =
|
|
36727
|
+
isWorktreeGitlink = existsSync7(projectGit) && statSync(projectGit).isFile();
|
|
35922
36728
|
} catch {
|
|
35923
36729
|
}
|
|
35924
36730
|
if (!isWorktreeGitlink) {
|
|
@@ -36383,7 +37189,7 @@ var init_table_name_map = __esm({
|
|
|
36383
37189
|
});
|
|
36384
37190
|
|
|
36385
37191
|
// packages/core/src/store/exodus/count-parity.ts
|
|
36386
|
-
import { existsSync as
|
|
37192
|
+
import { existsSync as existsSync8 } from "node:fs";
|
|
36387
37193
|
function listTables(db) {
|
|
36388
37194
|
return db.prepare(
|
|
36389
37195
|
"SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' AND name NOT LIKE '__drizzle_%' ORDER BY name"
|
|
@@ -36404,14 +37210,14 @@ function rowCount(db, tableName) {
|
|
|
36404
37210
|
function computeCountParity(sources, projectDbPath, globalDbPath) {
|
|
36405
37211
|
const entries = [];
|
|
36406
37212
|
let skipped = 0;
|
|
36407
|
-
if (!
|
|
37213
|
+
if (!existsSync8(projectDbPath) || !existsSync8(globalDbPath)) {
|
|
36408
37214
|
return { ok: false, entries: [], deficits: [], checked: 0, skipped: 0 };
|
|
36409
37215
|
}
|
|
36410
37216
|
const projectSnap = openCleoDbSnapshot(projectDbPath, { readOnly: true });
|
|
36411
37217
|
const globalSnap = openCleoDbSnapshot(globalDbPath, { readOnly: true });
|
|
36412
37218
|
try {
|
|
36413
37219
|
for (const src of sources) {
|
|
36414
|
-
if (!
|
|
37220
|
+
if (!existsSync8(src.path)) continue;
|
|
36415
37221
|
const srcSnap = openCleoDbSnapshot(src.path, { readOnly: true });
|
|
36416
37222
|
try {
|
|
36417
37223
|
for (const legacyTable of listTables(srcSnap.db)) {
|
|
@@ -36450,27 +37256,27 @@ function computeCountParity(sources, projectDbPath, globalDbPath) {
|
|
|
36450
37256
|
}
|
|
36451
37257
|
const deficits = entries.filter((e) => e.deficit > 0);
|
|
36452
37258
|
if (deficits.length > 0) {
|
|
36453
|
-
|
|
37259
|
+
log4.warn(
|
|
36454
37260
|
{ deficitCount: deficits.length, sample: deficits.slice(0, 5) },
|
|
36455
37261
|
`exodus count-parity: ${deficits.length} table(s) have FEWER rows in the consolidated target than the legacy source`
|
|
36456
37262
|
);
|
|
36457
37263
|
}
|
|
36458
37264
|
return { ok: deficits.length === 0, entries, deficits, checked: entries.length, skipped };
|
|
36459
37265
|
}
|
|
36460
|
-
var
|
|
37266
|
+
var log4;
|
|
36461
37267
|
var init_count_parity = __esm({
|
|
36462
37268
|
"packages/core/src/store/exodus/count-parity.ts"() {
|
|
36463
37269
|
"use strict";
|
|
36464
37270
|
init_logger2();
|
|
36465
37271
|
init_open_cleo_db();
|
|
36466
37272
|
init_table_name_map();
|
|
36467
|
-
|
|
37273
|
+
log4 = getLogger("exodus-count-parity");
|
|
36468
37274
|
}
|
|
36469
37275
|
});
|
|
36470
37276
|
|
|
36471
37277
|
// packages/core/src/store/exodus/plan.ts
|
|
36472
|
-
import { existsSync as
|
|
36473
|
-
import { join as
|
|
37278
|
+
import { existsSync as existsSync9, readdirSync as readdirSync3, statfsSync, statSync as statSync2 } from "node:fs";
|
|
37279
|
+
import { join as join12 } from "node:path";
|
|
36474
37280
|
function computeRequiredBytes(totalSourceBytes, largestSourceBytes) {
|
|
36475
37281
|
return Math.ceil(STAGING_HEADROOM_FACTOR * largestSourceBytes) + totalSourceBytes;
|
|
36476
37282
|
}
|
|
@@ -36481,33 +37287,33 @@ function buildSourceDescriptors(cwd) {
|
|
|
36481
37287
|
// Project-tier — go into consolidated project-scope cleo.db
|
|
36482
37288
|
{
|
|
36483
37289
|
name: "tasks",
|
|
36484
|
-
path:
|
|
37290
|
+
path: join12(cleoDir, "tasks.db"),
|
|
36485
37291
|
targetScope: "project"
|
|
36486
37292
|
},
|
|
36487
37293
|
{
|
|
36488
37294
|
name: "brain (project)",
|
|
36489
|
-
path:
|
|
37295
|
+
path: join12(cleoDir, "brain.db"),
|
|
36490
37296
|
targetScope: "project"
|
|
36491
37297
|
},
|
|
36492
37298
|
{
|
|
36493
37299
|
name: "conduit",
|
|
36494
|
-
path:
|
|
37300
|
+
path: join12(cleoDir, "conduit.db"),
|
|
36495
37301
|
targetScope: "project"
|
|
36496
37302
|
},
|
|
36497
37303
|
// Global-tier — go into consolidated global-scope cleo.db
|
|
36498
37304
|
{
|
|
36499
37305
|
name: "nexus",
|
|
36500
|
-
path:
|
|
37306
|
+
path: join12(cleoHome, "nexus.db"),
|
|
36501
37307
|
targetScope: "global"
|
|
36502
37308
|
},
|
|
36503
37309
|
{
|
|
36504
37310
|
name: "signaldock",
|
|
36505
|
-
path:
|
|
37311
|
+
path: join12(cleoHome, "signaldock.db"),
|
|
36506
37312
|
targetScope: "global"
|
|
36507
37313
|
},
|
|
36508
37314
|
{
|
|
36509
37315
|
name: "skills",
|
|
36510
|
-
path:
|
|
37316
|
+
path: join12(cleoHome, "skills.db"),
|
|
36511
37317
|
targetScope: "global"
|
|
36512
37318
|
}
|
|
36513
37319
|
];
|
|
@@ -36533,10 +37339,10 @@ function deriveStagingDirName() {
|
|
|
36533
37339
|
}
|
|
36534
37340
|
function findExistingStaging(cleoDir) {
|
|
36535
37341
|
try {
|
|
36536
|
-
const entries =
|
|
37342
|
+
const entries = readdirSync3(cleoDir, { withFileTypes: true });
|
|
36537
37343
|
const stagingDirs = entries.filter((e) => e.isDirectory() && e.name.startsWith("exodus-staging-")).map((e) => e.name).sort().reverse();
|
|
36538
37344
|
if (stagingDirs.length > 0) {
|
|
36539
|
-
return
|
|
37345
|
+
return join12(cleoDir, stagingDirs[0]);
|
|
36540
37346
|
}
|
|
36541
37347
|
} catch {
|
|
36542
37348
|
}
|
|
@@ -36552,7 +37358,7 @@ function buildExodusPlan(cwd) {
|
|
|
36552
37358
|
const availableBytes = getAvailableBytes(cleoDir);
|
|
36553
37359
|
const diskPreflight = totalSourceBytes === 0 || availableBytes >= requiredBytes;
|
|
36554
37360
|
const existingStaging = findExistingStaging(cleoDir);
|
|
36555
|
-
const stagingDir = existingStaging ??
|
|
37361
|
+
const stagingDir = existingStaging ?? join12(cleoDir, deriveStagingDirName());
|
|
36556
37362
|
const resumeFromStaging = existingStaging !== null;
|
|
36557
37363
|
const projectDbPath = resolveDualScopeDbPath("project", cwd);
|
|
36558
37364
|
const globalDbPath = resolveDualScopeDbPath("global");
|
|
@@ -36571,7 +37377,7 @@ function buildExodusPlan(cwd) {
|
|
|
36571
37377
|
};
|
|
36572
37378
|
}
|
|
36573
37379
|
function sourcesPresent(sources) {
|
|
36574
|
-
return sources.some((s) =>
|
|
37380
|
+
return sources.some((s) => existsSync9(s.path));
|
|
36575
37381
|
}
|
|
36576
37382
|
var STAGING_HEADROOM_FACTOR, STAGING_COPY_SKIP_THRESHOLD_BYTES;
|
|
36577
37383
|
var init_plan2 = __esm({
|
|
@@ -36585,18 +37391,18 @@ var init_plan2 = __esm({
|
|
|
36585
37391
|
});
|
|
36586
37392
|
|
|
36587
37393
|
// packages/core/src/store/exodus/health.ts
|
|
36588
|
-
import { existsSync as
|
|
37394
|
+
import { existsSync as existsSync10, statSync as statSync3 } from "node:fs";
|
|
36589
37395
|
function buildExodusHealth(cwd) {
|
|
36590
37396
|
const plan = buildExodusPlan(cwd);
|
|
36591
|
-
const anyLegacyPresent = plan.sources.some((s) =>
|
|
37397
|
+
const anyLegacyPresent = plan.sources.some((s) => existsSync10(s.path));
|
|
36592
37398
|
const parity = anyLegacyPresent ? computeCountParity(plan.sources, plan.projectDbPath, plan.globalDbPath) : { ok: true, entries: [], deficits: [], checked: 0, skipped: 0 };
|
|
36593
37399
|
const consolidatedExistsByScope = {
|
|
36594
|
-
project:
|
|
36595
|
-
global:
|
|
37400
|
+
project: existsSync10(plan.projectDbPath),
|
|
37401
|
+
global: existsSync10(plan.globalDbPath)
|
|
36596
37402
|
};
|
|
36597
37403
|
const buildScope = (scope) => {
|
|
36598
37404
|
const sources = plan.sources.filter((s) => s.targetScope === scope).map((s) => {
|
|
36599
|
-
const present =
|
|
37405
|
+
const present = existsSync10(s.path);
|
|
36600
37406
|
let bytes = 0;
|
|
36601
37407
|
if (present) {
|
|
36602
37408
|
try {
|
|
@@ -36863,15 +37669,15 @@ var init_types = __esm({
|
|
|
36863
37669
|
// packages/core/src/store/exodus/migrate.ts
|
|
36864
37670
|
import {
|
|
36865
37671
|
copyFileSync as copyFileSync3,
|
|
36866
|
-
existsSync as
|
|
36867
|
-
mkdirSync as
|
|
37672
|
+
existsSync as existsSync11,
|
|
37673
|
+
mkdirSync as mkdirSync3,
|
|
36868
37674
|
readFileSync as readFileSync3,
|
|
36869
37675
|
renameSync as renameSync2,
|
|
36870
37676
|
statSync as statSync4,
|
|
36871
37677
|
unlinkSync as unlinkSync2,
|
|
36872
|
-
writeFileSync as
|
|
37678
|
+
writeFileSync as writeFileSync4
|
|
36873
37679
|
} from "node:fs";
|
|
36874
|
-
import { join as
|
|
37680
|
+
import { join as join13 } from "node:path";
|
|
36875
37681
|
function getSqliteVersion(db) {
|
|
36876
37682
|
try {
|
|
36877
37683
|
const row = db.prepare("SELECT sqlite_version() AS v").get();
|
|
@@ -36887,14 +37693,14 @@ function listTables2(db) {
|
|
|
36887
37693
|
return rows.map((r) => r.name);
|
|
36888
37694
|
}
|
|
36889
37695
|
function writeJournal(stagingDir, journal) {
|
|
36890
|
-
const journalPath =
|
|
37696
|
+
const journalPath = join13(stagingDir, JOURNAL_FILENAME);
|
|
36891
37697
|
const tmpPath = `${journalPath}.tmp`;
|
|
36892
|
-
|
|
37698
|
+
writeFileSync4(tmpPath, JSON.stringify(journal, null, 2) + "\n", "utf8");
|
|
36893
37699
|
renameSync2(tmpPath, journalPath);
|
|
36894
37700
|
}
|
|
36895
37701
|
function readJournal(stagingDir) {
|
|
36896
|
-
const journalPath =
|
|
36897
|
-
if (!
|
|
37702
|
+
const journalPath = join13(stagingDir, JOURNAL_FILENAME);
|
|
37703
|
+
if (!existsSync11(journalPath)) return null;
|
|
36898
37704
|
try {
|
|
36899
37705
|
return JSON.parse(readFileSync3(journalPath, "utf8"));
|
|
36900
37706
|
} catch {
|
|
@@ -36902,17 +37708,17 @@ function readJournal(stagingDir) {
|
|
|
36902
37708
|
}
|
|
36903
37709
|
}
|
|
36904
37710
|
function clearExodusJournal(stagingDir) {
|
|
36905
|
-
const journalPath =
|
|
37711
|
+
const journalPath = join13(stagingDir, JOURNAL_FILENAME);
|
|
36906
37712
|
try {
|
|
36907
|
-
if (!
|
|
37713
|
+
if (!existsSync11(journalPath)) return false;
|
|
36908
37714
|
unlinkSync2(journalPath);
|
|
36909
|
-
|
|
37715
|
+
log5.info(
|
|
36910
37716
|
{ stagingDir },
|
|
36911
37717
|
"exodus: cleared migrate journal after abort/rollback \u2014 a retry will RE-COPY all tables"
|
|
36912
37718
|
);
|
|
36913
37719
|
return true;
|
|
36914
37720
|
} catch (err) {
|
|
36915
|
-
|
|
37721
|
+
log5.warn(
|
|
36916
37722
|
{ err, stagingDir },
|
|
36917
37723
|
'exodus: failed to clear migrate journal after rollback (a retry may skip already-"done" tables)'
|
|
36918
37724
|
);
|
|
@@ -36937,7 +37743,7 @@ function lockPath(dbPath) {
|
|
|
36937
37743
|
}
|
|
36938
37744
|
function acquireAdvisoryLock(dbPath) {
|
|
36939
37745
|
const lp = lockPath(dbPath);
|
|
36940
|
-
|
|
37746
|
+
writeFileSync4(lp, JSON.stringify({ pid: process.pid, ts: (/* @__PURE__ */ new Date()).toISOString() }), "utf8");
|
|
36941
37747
|
}
|
|
36942
37748
|
function releaseAdvisoryLock(dbPath) {
|
|
36943
37749
|
try {
|
|
@@ -37000,7 +37806,7 @@ function buildSelectExpr(attachAlias, legacyTable, targetTableName, col, srcType
|
|
|
37000
37806
|
function copyTableFromAttached(targetNativeDb, srcNativeDb, attachAlias, legacyTableName, sourceName, targetSchema = "main") {
|
|
37001
37807
|
const resolution = resolveConsolidatedTableName(sourceName, legacyTableName);
|
|
37002
37808
|
if (resolution.kind === "skip") {
|
|
37003
|
-
|
|
37809
|
+
log5.warn(
|
|
37004
37810
|
{ legacyTableName, sourceName, reason: resolution.reason },
|
|
37005
37811
|
`Exodus: explicitly skipping table \u2014 ${resolution.reason}`
|
|
37006
37812
|
);
|
|
@@ -37019,7 +37825,7 @@ function copyTableFromAttached(targetNativeDb, srcNativeDb, attachAlias, legacyT
|
|
|
37019
37825
|
).get();
|
|
37020
37826
|
if (!existsRow) {
|
|
37021
37827
|
const reason = `consolidated target '${targetTableName}' not found (mapped from legacy '${legacyTableName}')`;
|
|
37022
|
-
|
|
37828
|
+
log5.warn(
|
|
37023
37829
|
{ legacyTableName, targetTableName, sourceName, attachAlias, targetSchema },
|
|
37024
37830
|
`Exodus: ${reason}`
|
|
37025
37831
|
);
|
|
@@ -37030,13 +37836,13 @@ function copyTableFromAttached(targetNativeDb, srcNativeDb, attachAlias, legacyT
|
|
|
37030
37836
|
const sharedColumns = srcPragma.map((r) => r.name).filter((col) => tgtColMap.has(col));
|
|
37031
37837
|
if (sharedColumns.length === 0) {
|
|
37032
37838
|
const reason = `no overlapping columns between source '${legacyTableName}' and target '${targetTableName}'`;
|
|
37033
|
-
|
|
37839
|
+
log5.warn({ legacyTableName, targetTableName, sourceName }, `Exodus: ${reason}`);
|
|
37034
37840
|
return { rowsCopied: 0, skipped: true, reason };
|
|
37035
37841
|
}
|
|
37036
37842
|
const srcOnlyColumns = srcPragma.map((r) => r.name).filter((c) => !tgtColMap.has(c));
|
|
37037
37843
|
const tgtOnlyColumns = tgtPragma.map((r) => r.name).filter((c) => !srcColumns.has(c));
|
|
37038
37844
|
if (srcOnlyColumns.length > 0 || tgtOnlyColumns.length > 0) {
|
|
37039
|
-
|
|
37845
|
+
log5.info(
|
|
37040
37846
|
{ legacyTableName, targetTableName, sourceName, srcOnlyColumns, tgtOnlyColumns },
|
|
37041
37847
|
"Exodus: column drift detected \u2014 copying intersection, dropping src-only cols, using defaults for tgt-only cols"
|
|
37042
37848
|
);
|
|
@@ -37051,7 +37857,7 @@ function copyTableFromAttached(targetNativeDb, srcNativeDb, attachAlias, legacyT
|
|
|
37051
37857
|
return isoGlobCols.has(col) && (upper.includes("INT") || upper === "" || upper === "NUMERIC");
|
|
37052
37858
|
});
|
|
37053
37859
|
if (coercedCols.length > 0) {
|
|
37054
|
-
|
|
37860
|
+
log5.info(
|
|
37055
37861
|
{
|
|
37056
37862
|
legacyTableName,
|
|
37057
37863
|
targetTableName,
|
|
@@ -37067,7 +37873,7 @@ function copyTableFromAttached(targetNativeDb, srcNativeDb, attachAlias, legacyT
|
|
|
37067
37873
|
(col) => ENUM_NORMALIZATIONS.has(`${targetTableName}.${col}`)
|
|
37068
37874
|
);
|
|
37069
37875
|
if (normalizedCols.length > 0) {
|
|
37070
|
-
|
|
37876
|
+
log5.info(
|
|
37071
37877
|
{ legacyTableName, targetTableName, sourceName, normalizedCols },
|
|
37072
37878
|
`Exodus: applying enum-value normalization for ${normalizedCols.length} column(s) (T11547)`
|
|
37073
37879
|
);
|
|
@@ -37076,7 +37882,7 @@ function copyTableFromAttached(targetNativeDb, srcNativeDb, attachAlias, legacyT
|
|
|
37076
37882
|
(col) => NUMERIC_CLAMPS.has(`${targetTableName}.${col}`)
|
|
37077
37883
|
);
|
|
37078
37884
|
if (clampedCols.length > 0) {
|
|
37079
|
-
|
|
37885
|
+
log5.info(
|
|
37080
37886
|
{ legacyTableName, targetTableName, sourceName, clampedCols },
|
|
37081
37887
|
`Exodus: applying non-finite numeric clamp for ${clampedCols.length} column(s) (T11782)`
|
|
37082
37888
|
);
|
|
@@ -37118,7 +37924,7 @@ function copyTableFromAttached(targetNativeDb, srcNativeDb, attachAlias, legacyT
|
|
|
37118
37924
|
if (rowsCopied < sourceCount) {
|
|
37119
37925
|
const presentAccountedFor = existingBefore + rowsCopied;
|
|
37120
37926
|
if (presentAccountedFor >= sourceCount) {
|
|
37121
|
-
|
|
37927
|
+
log5.info(
|
|
37122
37928
|
{ legacyTableName, targetTableName, sourceName, sourceCount, rowsCopied, existingBefore },
|
|
37123
37929
|
`Exodus: '${legacyTableName}'\u2192'${targetTableName}' \u2014 ${sourceCount - rowsCopied} of ${sourceCount} row(s) already present in target (idempotent PK dedup), ${rowsCopied} newly copied; no loss`
|
|
37124
37930
|
);
|
|
@@ -37126,7 +37932,7 @@ function copyTableFromAttached(targetNativeDb, srcNativeDb, attachAlias, legacyT
|
|
|
37126
37932
|
}
|
|
37127
37933
|
const missing = sourceCount - presentAccountedFor;
|
|
37128
37934
|
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.`;
|
|
37129
|
-
|
|
37935
|
+
log5.error(
|
|
37130
37936
|
{
|
|
37131
37937
|
legacyTableName,
|
|
37132
37938
|
targetTableName,
|
|
@@ -37146,10 +37952,10 @@ function checkSchemaVersion(journal, forceCrossVersion) {
|
|
|
37146
37952
|
if (journal.targetSchemaVersion !== EXODUS_TARGET_SCHEMA_VERSION) {
|
|
37147
37953
|
const msg = `Schema version mismatch: journal=${journal.targetSchemaVersion}, expected=${EXODUS_TARGET_SCHEMA_VERSION}`;
|
|
37148
37954
|
if (forceCrossVersion) {
|
|
37149
|
-
|
|
37955
|
+
log5.warn(msg + " (--force-cross-version: continuing anyway)");
|
|
37150
37956
|
return true;
|
|
37151
37957
|
}
|
|
37152
|
-
|
|
37958
|
+
log5.error(msg + " \u2014 pass --force-cross-version to override");
|
|
37153
37959
|
return false;
|
|
37154
37960
|
}
|
|
37155
37961
|
return true;
|
|
@@ -37166,10 +37972,10 @@ async function runExodusMigrate(plan, forceCrossVersion = false, onProgress) {
|
|
|
37166
37972
|
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.`
|
|
37167
37973
|
};
|
|
37168
37974
|
}
|
|
37169
|
-
|
|
37975
|
+
mkdirSync3(stagingDir, { recursive: true });
|
|
37170
37976
|
let sqliteVersion = "unknown";
|
|
37171
37977
|
for (const src of sources) {
|
|
37172
|
-
if (
|
|
37978
|
+
if (existsSync11(src.path)) {
|
|
37173
37979
|
const snap = openCleoDbSnapshot(src.path, { readOnly: true });
|
|
37174
37980
|
sqliteVersion = getSqliteVersion(snap.db);
|
|
37175
37981
|
snap.close();
|
|
@@ -37210,15 +38016,15 @@ async function runExodusMigrate(plan, forceCrossVersion = false, onProgress) {
|
|
|
37210
38016
|
};
|
|
37211
38017
|
var extractNativeDb = extractNativeDb2;
|
|
37212
38018
|
for (const src of sources) {
|
|
37213
|
-
if (!
|
|
37214
|
-
const backupDest =
|
|
38019
|
+
if (!existsSync11(src.path)) continue;
|
|
38020
|
+
const backupDest = join13(stagingDir, `${src.name.replace(/[^a-z0-9-]/g, "_")}-backup.db`);
|
|
37215
38021
|
const srcBytes = safeStatBytes(src.path);
|
|
37216
38022
|
const skipStagingCopy = srcBytes > plan.stagingCopyThresholdBytes;
|
|
37217
38023
|
if (skipStagingCopy) {
|
|
37218
38024
|
onProgress?.(
|
|
37219
38025
|
`Skipping full staging copy of ${src.name} (${srcBytes} bytes > ${plan.stagingCopyThresholdBytes} threshold) \u2014 source is archived, not deleted, on success.`
|
|
37220
38026
|
);
|
|
37221
|
-
} else if (!
|
|
38027
|
+
} else if (!existsSync11(backupDest)) {
|
|
37222
38028
|
onProgress?.(`Backing up ${src.name} \u2192 staging dir\u2026`);
|
|
37223
38029
|
copyFileSync3(src.path, backupDest);
|
|
37224
38030
|
backupPaths.push(backupDest);
|
|
@@ -37236,8 +38042,8 @@ async function runExodusMigrate(plan, forceCrossVersion = false, onProgress) {
|
|
|
37236
38042
|
});
|
|
37237
38043
|
const projectNative = extractNativeDb2(projectHandle);
|
|
37238
38044
|
const globalNative = extractNativeDb2(globalHandle);
|
|
37239
|
-
const projectSources = sources.filter((s) => s.targetScope === "project" &&
|
|
37240
|
-
const globalSources = sources.filter((s) => s.targetScope === "global" &&
|
|
38045
|
+
const projectSources = sources.filter((s) => s.targetScope === "project" && existsSync11(s.path));
|
|
38046
|
+
const globalSources = sources.filter((s) => s.targetScope === "global" && existsSync11(s.path));
|
|
37241
38047
|
await migrateScope(
|
|
37242
38048
|
"project",
|
|
37243
38049
|
projectSources,
|
|
@@ -37262,7 +38068,7 @@ async function runExodusMigrate(plan, forceCrossVersion = false, onProgress) {
|
|
|
37262
38068
|
return { ok: true, tables: allTableResults, stagingDir, backupPaths };
|
|
37263
38069
|
} catch (err) {
|
|
37264
38070
|
const error = err instanceof Error ? err.message : String(err);
|
|
37265
|
-
|
|
38071
|
+
log5.error({ err }, "Exodus migration failed");
|
|
37266
38072
|
return { ok: false, tables: allTableResults, stagingDir, backupPaths, error };
|
|
37267
38073
|
} finally {
|
|
37268
38074
|
try {
|
|
@@ -37282,7 +38088,7 @@ async function migrateScope(scope, sources, targetNativeDb, journal, stagingDir,
|
|
|
37282
38088
|
if (sources.length === 0) return;
|
|
37283
38089
|
onProgress?.(`Migrating ${scope}-scope sources\u2026`);
|
|
37284
38090
|
targetNativeDb.exec("PRAGMA foreign_keys = OFF");
|
|
37285
|
-
|
|
38091
|
+
log5.info({ scope }, "Exodus: foreign_keys=OFF for bulk copy (T11533 FK-defer)");
|
|
37286
38092
|
try {
|
|
37287
38093
|
for (let i = 0; i < sources.length; i++) {
|
|
37288
38094
|
const src = sources[i];
|
|
@@ -37349,7 +38155,7 @@ async function migrateScope(scope, sources, targetNativeDb, journal, stagingDir,
|
|
|
37349
38155
|
}
|
|
37350
38156
|
} catch (err) {
|
|
37351
38157
|
const msg = err instanceof Error ? err.message : String(err);
|
|
37352
|
-
|
|
38158
|
+
log5.warn({ tableName, sourceDb: src.name, err }, "Table copy failed \u2014 skipping");
|
|
37353
38159
|
status = "skipped";
|
|
37354
38160
|
errorMsg = msg;
|
|
37355
38161
|
skipped = true;
|
|
@@ -37397,7 +38203,7 @@ async function migrateScope(scope, sources, targetNativeDb, journal, stagingDir,
|
|
|
37397
38203
|
targetNativeDb.exec(`DETACH DATABASE "${attachAlias}"`);
|
|
37398
38204
|
onProgress?.(` [${src.name}] Detached "${attachAlias}"`);
|
|
37399
38205
|
} catch (detachErr) {
|
|
37400
|
-
|
|
38206
|
+
log5.warn(
|
|
37401
38207
|
{ attachAlias, sourceDb: src.name, err: detachErr },
|
|
37402
38208
|
"DETACH failed \u2014 alias will be released on DB close"
|
|
37403
38209
|
);
|
|
@@ -37407,7 +38213,7 @@ async function migrateScope(scope, sources, targetNativeDb, journal, stagingDir,
|
|
|
37407
38213
|
targetNativeDb.exec(`DETACH DATABASE "${crossAlias}"`);
|
|
37408
38214
|
onProgress?.(` [${src.name}] Cross-scope target detached "${crossAlias}"`);
|
|
37409
38215
|
} catch (detachErr) {
|
|
37410
|
-
|
|
38216
|
+
log5.warn(
|
|
37411
38217
|
{ crossAlias, sourceDb: src.name, err: detachErr },
|
|
37412
38218
|
"Cross-scope DETACH failed \u2014 alias will be released on DB close"
|
|
37413
38219
|
);
|
|
@@ -37419,28 +38225,28 @@ async function migrateScope(scope, sources, targetNativeDb, journal, stagingDir,
|
|
|
37419
38225
|
try {
|
|
37420
38226
|
const orphans = targetNativeDb.prepare("PRAGMA foreign_key_check").all();
|
|
37421
38227
|
if (orphans.length > 0) {
|
|
37422
|
-
|
|
38228
|
+
log5.warn(
|
|
37423
38229
|
{ scope, orphanCount: orphans.length, sample: orphans.slice(0, 5) },
|
|
37424
38230
|
`Exodus: PRAGMA foreign_key_check found ${orphans.length} orphan row(s) after bulk copy \u2014 these are genuine data orphans (not ordering artifacts)`
|
|
37425
38231
|
);
|
|
37426
38232
|
} else {
|
|
37427
|
-
|
|
38233
|
+
log5.info({ scope }, "Exodus: PRAGMA foreign_key_check PASSED \u2014 no orphan rows");
|
|
37428
38234
|
}
|
|
37429
38235
|
} catch (checkErr) {
|
|
37430
|
-
|
|
38236
|
+
log5.warn(
|
|
37431
38237
|
{ scope, err: checkErr },
|
|
37432
38238
|
"Exodus: PRAGMA foreign_key_check failed (non-fatal) \u2014 target schema may not have FK constraints enabled"
|
|
37433
38239
|
);
|
|
37434
38240
|
}
|
|
37435
38241
|
try {
|
|
37436
38242
|
targetNativeDb.exec("PRAGMA foreign_keys = ON");
|
|
37437
|
-
|
|
38243
|
+
log5.info({ scope }, "Exodus: foreign_keys=ON restored after bulk copy");
|
|
37438
38244
|
} catch (fkErr) {
|
|
37439
|
-
|
|
38245
|
+
log5.warn({ scope, err: fkErr }, "Exodus: could not restore foreign_keys=ON (non-fatal)");
|
|
37440
38246
|
}
|
|
37441
38247
|
}
|
|
37442
38248
|
}
|
|
37443
|
-
var
|
|
38249
|
+
var log5, LOCK_SENTINEL_SUFFIX, JOURNAL_FILENAME, SOURCE_EPOCH_UNITS;
|
|
37444
38250
|
var init_migrate = __esm({
|
|
37445
38251
|
"packages/core/src/store/exodus/migrate.ts"() {
|
|
37446
38252
|
"use strict";
|
|
@@ -37452,7 +38258,7 @@ var init_migrate = __esm({
|
|
|
37452
38258
|
init_plan2();
|
|
37453
38259
|
init_table_name_map();
|
|
37454
38260
|
init_types();
|
|
37455
|
-
|
|
38261
|
+
log5 = getLogger("exodus-migrate");
|
|
37456
38262
|
LOCK_SENTINEL_SUFFIX = ".exodus-lock";
|
|
37457
38263
|
JOURNAL_FILENAME = "exodus-journal.json";
|
|
37458
38264
|
SOURCE_EPOCH_UNITS = /* @__PURE__ */ new Map([
|
|
@@ -37476,7 +38282,7 @@ function sealExodus(plan, scopeArg, cwd) {
|
|
|
37476
38282
|
const parity = computeCountParity(plan.sources, plan.projectDbPath, plan.globalDbPath);
|
|
37477
38283
|
if (!parity.ok) {
|
|
37478
38284
|
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(", ")}`;
|
|
37479
|
-
|
|
38285
|
+
log6.error({ deficits: parity.deficits.length }, `exodus seal refused \u2014 ${refusedReason}`);
|
|
37480
38286
|
return { ok: false, refusedReason, parity, scopes: [] };
|
|
37481
38287
|
}
|
|
37482
38288
|
const outcomes = [];
|
|
@@ -37492,7 +38298,7 @@ function sealExodus(plan, scopeArg, cwd) {
|
|
|
37492
38298
|
scopeSources.map((s) => s.name),
|
|
37493
38299
|
cwd
|
|
37494
38300
|
);
|
|
37495
|
-
|
|
38301
|
+
log6.info(
|
|
37496
38302
|
{ scope, alreadySealed, archived: archived.filter((a) => a.action === "archived").length },
|
|
37497
38303
|
`exodus seal: scope '${scope}' certified (count-parity verified, ${parity.checked} tables)`
|
|
37498
38304
|
);
|
|
@@ -37500,23 +38306,23 @@ function sealExodus(plan, scopeArg, cwd) {
|
|
|
37500
38306
|
}
|
|
37501
38307
|
return { ok: true, parity, scopes: outcomes };
|
|
37502
38308
|
}
|
|
37503
|
-
var
|
|
38309
|
+
var log6;
|
|
37504
38310
|
var init_seal = __esm({
|
|
37505
38311
|
"packages/core/src/store/exodus/seal.ts"() {
|
|
37506
38312
|
"use strict";
|
|
37507
38313
|
init_logger2();
|
|
37508
38314
|
init_archive2();
|
|
37509
38315
|
init_count_parity();
|
|
37510
|
-
|
|
38316
|
+
log6 = getLogger("exodus-seal");
|
|
37511
38317
|
}
|
|
37512
38318
|
});
|
|
37513
38319
|
|
|
37514
38320
|
// packages/core/src/store/exodus/status.ts
|
|
37515
|
-
import { existsSync as
|
|
37516
|
-
import { join as
|
|
38321
|
+
import { existsSync as existsSync12, readdirSync as readdirSync4, readFileSync as readFileSync4, statSync as statSync5 } from "node:fs";
|
|
38322
|
+
import { join as join14 } from "node:path";
|
|
37517
38323
|
function readJournal2(stagingDir) {
|
|
37518
|
-
const p =
|
|
37519
|
-
if (!
|
|
38324
|
+
const p = join14(stagingDir, JOURNAL_FILENAME2);
|
|
38325
|
+
if (!existsSync12(p)) return null;
|
|
37520
38326
|
try {
|
|
37521
38327
|
return JSON.parse(readFileSync4(p, "utf8"));
|
|
37522
38328
|
} catch {
|
|
@@ -37525,7 +38331,7 @@ function readJournal2(stagingDir) {
|
|
|
37525
38331
|
}
|
|
37526
38332
|
function findStagingDirs(cleoDir) {
|
|
37527
38333
|
try {
|
|
37528
|
-
return
|
|
38334
|
+
return readdirSync4(cleoDir, { withFileTypes: true }).filter((e) => e.isDirectory() && e.name.startsWith("exodus-staging-")).map((e) => join14(cleoDir, e.name)).sort().reverse();
|
|
37529
38335
|
} catch {
|
|
37530
38336
|
return [];
|
|
37531
38337
|
}
|
|
@@ -37548,15 +38354,15 @@ function runExodusStatus(cwd) {
|
|
|
37548
38354
|
const sourcesInfo = plan.sources.map((s) => ({
|
|
37549
38355
|
name: s.name,
|
|
37550
38356
|
path: s.path,
|
|
37551
|
-
exists:
|
|
38357
|
+
exists: existsSync12(s.path),
|
|
37552
38358
|
bytes: safeBytes(s.path)
|
|
37553
38359
|
}));
|
|
37554
38360
|
return {
|
|
37555
38361
|
hasStaging: latestStaging !== null,
|
|
37556
38362
|
stagingDir: latestStaging,
|
|
37557
38363
|
journal,
|
|
37558
|
-
projectDbExists:
|
|
37559
|
-
globalDbExists:
|
|
38364
|
+
projectDbExists: existsSync12(projectDbPath),
|
|
38365
|
+
globalDbExists: existsSync12(globalDbPath),
|
|
37560
38366
|
sourcesPresent: sourcesInfo.some((s) => s.exists),
|
|
37561
38367
|
sources: sourcesInfo
|
|
37562
38368
|
};
|
|
@@ -37573,7 +38379,7 @@ var init_status = __esm({
|
|
|
37573
38379
|
});
|
|
37574
38380
|
|
|
37575
38381
|
// packages/core/src/store/exodus/verify-migration.ts
|
|
37576
|
-
import { existsSync as
|
|
38382
|
+
import { existsSync as existsSync13 } from "node:fs";
|
|
37577
38383
|
import { createRequire as createRequire3 } from "node:module";
|
|
37578
38384
|
function orderByClause(db, tableName) {
|
|
37579
38385
|
try {
|
|
@@ -37593,7 +38399,7 @@ function computeTableDigest(db, tableName, columns, transform) {
|
|
|
37593
38399
|
count = Number(row?.c ?? 0);
|
|
37594
38400
|
} catch (err) {
|
|
37595
38401
|
const msg = err instanceof Error ? err.message : String(err);
|
|
37596
|
-
|
|
38402
|
+
log7.warn(
|
|
37597
38403
|
{ tableName, err: msg },
|
|
37598
38404
|
"computeTableDigest: COUNT(*) failed (possibly a virtual/FTS table) \u2014 treating as 0 rows"
|
|
37599
38405
|
);
|
|
@@ -37628,7 +38434,7 @@ function computeTableDigest(db, tableName, columns, transform) {
|
|
|
37628
38434
|
}
|
|
37629
38435
|
} catch (err) {
|
|
37630
38436
|
const msg = err instanceof Error ? err.message : String(err);
|
|
37631
|
-
|
|
38437
|
+
log7.warn(
|
|
37632
38438
|
{ tableName, err: msg },
|
|
37633
38439
|
"computeTableDigest: streamed SELECT failed (possibly a virtual/FTS table) \u2014 digest skipped (COUNT(*) parity still enforced)"
|
|
37634
38440
|
);
|
|
@@ -37754,7 +38560,7 @@ function foreignKeyCheck(db, scope) {
|
|
|
37754
38560
|
try {
|
|
37755
38561
|
const rows = db.prepare("PRAGMA foreign_key_check").all();
|
|
37756
38562
|
if (rows.length > 0) {
|
|
37757
|
-
|
|
38563
|
+
log7.warn(
|
|
37758
38564
|
{ scope, count: rows.length, sample: rows.slice(0, 5) },
|
|
37759
38565
|
`verifyMigration: PRAGMA foreign_key_check found ${rows.length} orphan row(s)`
|
|
37760
38566
|
);
|
|
@@ -37766,7 +38572,7 @@ function foreignKeyCheck(db, scope) {
|
|
|
37766
38572
|
fkid: r.fkid
|
|
37767
38573
|
}));
|
|
37768
38574
|
} catch (err) {
|
|
37769
|
-
|
|
38575
|
+
log7.warn({ scope, err }, "verifyMigration: PRAGMA foreign_key_check failed (non-fatal)");
|
|
37770
38576
|
return [];
|
|
37771
38577
|
}
|
|
37772
38578
|
}
|
|
@@ -37803,13 +38609,13 @@ function sourceOrphanSignatures(db, sourceName, scope) {
|
|
|
37803
38609
|
const rows = db.prepare("PRAGMA foreign_key_check").all();
|
|
37804
38610
|
for (const r of rows) sigs.add(orphanSignature(db, r, sourceName));
|
|
37805
38611
|
if (rows.length > 0) {
|
|
37806
|
-
|
|
38612
|
+
log7.warn(
|
|
37807
38613
|
{ scope, count: rows.length, sample: rows.slice(0, 5) },
|
|
37808
38614
|
`verifyMigration: source already has ${rows.length} pre-existing FK orphan(s) \u2014 these are tolerated (carried forward losslessly, flagged for data-hygiene)`
|
|
37809
38615
|
);
|
|
37810
38616
|
}
|
|
37811
38617
|
} catch (err) {
|
|
37812
|
-
|
|
38618
|
+
log7.warn({ scope, err }, "verifyMigration: source PRAGMA foreign_key_check failed (non-fatal)");
|
|
37813
38619
|
}
|
|
37814
38620
|
return sigs;
|
|
37815
38621
|
}
|
|
@@ -37821,7 +38627,7 @@ function verifyMigration(sources, projectDbPath, globalDbPath, onProgress) {
|
|
|
37821
38627
|
const preExistingForeignKeyViolations = [];
|
|
37822
38628
|
const failureLines = [];
|
|
37823
38629
|
const sourceOrphanSigs = /* @__PURE__ */ new Set();
|
|
37824
|
-
if (!
|
|
38630
|
+
if (!existsSync13(projectDbPath)) {
|
|
37825
38631
|
return {
|
|
37826
38632
|
ok: false,
|
|
37827
38633
|
tables: [],
|
|
@@ -37832,7 +38638,7 @@ function verifyMigration(sources, projectDbPath, globalDbPath, onProgress) {
|
|
|
37832
38638
|
error: `Consolidated project cleo.db not found at ${projectDbPath}. Run 'cleo exodus migrate' first.`
|
|
37833
38639
|
};
|
|
37834
38640
|
}
|
|
37835
|
-
if (!
|
|
38641
|
+
if (!existsSync13(globalDbPath)) {
|
|
37836
38642
|
return {
|
|
37837
38643
|
ok: false,
|
|
37838
38644
|
tables: [],
|
|
@@ -37847,7 +38653,7 @@ function verifyMigration(sources, projectDbPath, globalDbPath, onProgress) {
|
|
|
37847
38653
|
const globalSnap = openCleoDbSnapshot(globalDbPath, { readOnly: true });
|
|
37848
38654
|
try {
|
|
37849
38655
|
for (const src of sources) {
|
|
37850
|
-
if (!
|
|
38656
|
+
if (!existsSync13(src.path)) {
|
|
37851
38657
|
onProgress?.(`Skipping ${src.name} (not present)`);
|
|
37852
38658
|
continue;
|
|
37853
38659
|
}
|
|
@@ -37932,7 +38738,7 @@ function verifyMigration(sources, projectDbPath, globalDbPath, onProgress) {
|
|
|
37932
38738
|
`[${scope}] ${src.name}.${legacyTableName} \u2192 ${targetTableName}: DEFICIT \u2014 source=${srcDigest.count} rows, target=${tgtDigest.count} rows (${srcDigest.count - tgtDigest.count} missing), hashMatch=${hashMatch}`
|
|
37933
38739
|
);
|
|
37934
38740
|
} else if (tgtDigest.count > srcDigest.count) {
|
|
37935
|
-
|
|
38741
|
+
log7.warn(
|
|
37936
38742
|
{
|
|
37937
38743
|
scope,
|
|
37938
38744
|
source: src.name,
|
|
@@ -37982,7 +38788,7 @@ function verifyMigration(sources, projectDbPath, globalDbPath, onProgress) {
|
|
|
37982
38788
|
}
|
|
37983
38789
|
}
|
|
37984
38790
|
if (preExistingForeignKeyViolations.length > 0) {
|
|
37985
|
-
|
|
38791
|
+
log7.warn(
|
|
37986
38792
|
{
|
|
37987
38793
|
count: preExistingForeignKeyViolations.length,
|
|
37988
38794
|
sample: preExistingForeignKeyViolations.slice(0, 5)
|
|
@@ -37992,7 +38798,7 @@ function verifyMigration(sources, projectDbPath, globalDbPath, onProgress) {
|
|
|
37992
38798
|
}
|
|
37993
38799
|
} catch (err) {
|
|
37994
38800
|
const error = err instanceof Error ? err.message : String(err);
|
|
37995
|
-
|
|
38801
|
+
log7.error({ err }, "verifyMigration failed");
|
|
37996
38802
|
return {
|
|
37997
38803
|
ok: false,
|
|
37998
38804
|
tables,
|
|
@@ -38009,7 +38815,7 @@ function verifyMigration(sources, projectDbPath, globalDbPath, onProgress) {
|
|
|
38009
38815
|
if (failureLines.length > 0) {
|
|
38010
38816
|
const error = `verifyMigration FAILED: ${failureLines.length} issue(s):
|
|
38011
38817
|
${failureLines.map((l) => ` \u2022 ${l}`).join("\n")}`;
|
|
38012
|
-
|
|
38818
|
+
log7.error({ failureCount: failureLines.length }, error);
|
|
38013
38819
|
return {
|
|
38014
38820
|
ok: false,
|
|
38015
38821
|
tables,
|
|
@@ -38029,7 +38835,7 @@ ${failureLines.map((l) => ` \u2022 ${l}`).join("\n")}`;
|
|
|
38029
38835
|
enumDrift
|
|
38030
38836
|
};
|
|
38031
38837
|
}
|
|
38032
|
-
var
|
|
38838
|
+
var log7, _require, CHECK_ENUM_REGEX;
|
|
38033
38839
|
var init_verify_migration = __esm({
|
|
38034
38840
|
"packages/core/src/store/exodus/verify-migration.ts"() {
|
|
38035
38841
|
"use strict";
|
|
@@ -38038,7 +38844,7 @@ var init_verify_migration = __esm({
|
|
|
38038
38844
|
init_open_cleo_db();
|
|
38039
38845
|
init_column_transforms();
|
|
38040
38846
|
init_table_name_map();
|
|
38041
|
-
|
|
38847
|
+
log7 = getLogger("verify-migration");
|
|
38042
38848
|
_require = createRequire3(import.meta.url);
|
|
38043
38849
|
CHECK_ENUM_REGEX = /CHECK\s*\(\s*"([^"]+)"\s+(?:IS\s+NULL\s+OR\s+"[^"]+"\s+)?IN\s*\(([^)]*)\)\s*\)/gi;
|
|
38044
38850
|
}
|
|
@@ -38130,7 +38936,7 @@ __export(on_open_exports, {
|
|
|
38130
38936
|
isDataContinuityOk: () => isDataContinuityOk,
|
|
38131
38937
|
maybeRunExodusOnOpen: () => maybeRunExodusOnOpen
|
|
38132
38938
|
});
|
|
38133
|
-
import { existsSync as
|
|
38939
|
+
import { existsSync as existsSync14 } from "node:fs";
|
|
38134
38940
|
function isDisabledByEnv() {
|
|
38135
38941
|
const v = process.env.CLEO_DISABLE_EXODUS_ON_OPEN;
|
|
38136
38942
|
return v === "1" || v === "true";
|
|
@@ -38156,7 +38962,7 @@ function rollbackConsolidatedToEmpty(nativeDb, scope) {
|
|
|
38156
38962
|
"SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' AND name NOT LIKE '__drizzle_%'"
|
|
38157
38963
|
).all().map((r) => r.name);
|
|
38158
38964
|
} catch (err) {
|
|
38159
|
-
|
|
38965
|
+
log8.error({ err, scope }, "exodus-on-open: failed to enumerate tables for rollback");
|
|
38160
38966
|
return;
|
|
38161
38967
|
}
|
|
38162
38968
|
try {
|
|
@@ -38166,7 +38972,7 @@ function rollbackConsolidatedToEmpty(nativeDb, scope) {
|
|
|
38166
38972
|
try {
|
|
38167
38973
|
nativeDb.exec(`DELETE FROM "${table}"`);
|
|
38168
38974
|
} catch (err) {
|
|
38169
|
-
|
|
38975
|
+
log8.warn({ err, table, scope }, "exodus-on-open: failed to clear table during rollback");
|
|
38170
38976
|
}
|
|
38171
38977
|
}
|
|
38172
38978
|
nativeDb.exec("COMMIT");
|
|
@@ -38175,7 +38981,7 @@ function rollbackConsolidatedToEmpty(nativeDb, scope) {
|
|
|
38175
38981
|
nativeDb.exec("ROLLBACK");
|
|
38176
38982
|
} catch {
|
|
38177
38983
|
}
|
|
38178
|
-
|
|
38984
|
+
log8.error({ err, scope }, "exodus-on-open: rollback transaction failed");
|
|
38179
38985
|
} finally {
|
|
38180
38986
|
try {
|
|
38181
38987
|
nativeDb.exec("PRAGMA foreign_keys = ON");
|
|
@@ -38195,7 +39001,7 @@ async function rollbackBothScopes(scope, projectDbPath, globalDbPath) {
|
|
|
38195
39001
|
rollbackConsolidatedToEmpty(native, s);
|
|
38196
39002
|
}
|
|
38197
39003
|
} catch (err) {
|
|
38198
|
-
|
|
39004
|
+
log8.warn(
|
|
38199
39005
|
{ err, scope: s, openingScope: scope },
|
|
38200
39006
|
"exodus-on-open: could not roll back scope (best-effort)"
|
|
38201
39007
|
);
|
|
@@ -38211,7 +39017,7 @@ function isDataContinuityOk(result) {
|
|
|
38211
39017
|
const deficits = result.tables.filter((t) => t.targetCount < t.sourceCount);
|
|
38212
39018
|
const surpluses = result.tables.filter((t) => t.targetCount > t.sourceCount);
|
|
38213
39019
|
if (surpluses.length > 0) {
|
|
38214
|
-
|
|
39020
|
+
log8.warn(
|
|
38215
39021
|
{
|
|
38216
39022
|
surpluses: surpluses.map((t) => ({
|
|
38217
39023
|
table: t.targetTable,
|
|
@@ -38245,7 +39051,7 @@ async function maybeRunExodusOnOpen(scope, dbPath, nativeDb, cwd) {
|
|
|
38245
39051
|
const { buildExodusPlan: buildExodusPlan2, runExodusMigrate: runExodusMigrate2, verifyMigration: verifyMigration2, clearExodusJournal: clearExodusJournal2 } = await Promise.resolve().then(() => (init_exodus(), exodus_exports));
|
|
38246
39052
|
const plan = buildExodusPlan2(cwd);
|
|
38247
39053
|
const scopeSources = plan.sources.filter((s) => s.targetScope === scope);
|
|
38248
|
-
if (!scopeSources.some((s) =>
|
|
39054
|
+
if (!scopeSources.some((s) => existsSync14(s.path))) {
|
|
38249
39055
|
return {
|
|
38250
39056
|
outcome: "skipped",
|
|
38251
39057
|
reason: `no legacy ${scope}-scope source DBs present (fresh install or cross-scope-only)`
|
|
@@ -38258,11 +39064,11 @@ async function maybeRunExodusOnOpen(scope, dbPath, nativeDb, cwd) {
|
|
|
38258
39064
|
if (!consolidatedIsEmpty(nativeDb, scope)) {
|
|
38259
39065
|
return { outcome: "skipped", reason: "migrated by a concurrent process (lock winner)" };
|
|
38260
39066
|
}
|
|
38261
|
-
|
|
39067
|
+
log8.info(
|
|
38262
39068
|
{
|
|
38263
39069
|
scope,
|
|
38264
39070
|
dbPath,
|
|
38265
|
-
sources: plan.sources.filter((s) =>
|
|
39071
|
+
sources: plan.sources.filter((s) => existsSync14(s.path)).map((s) => s.name)
|
|
38266
39072
|
},
|
|
38267
39073
|
"exodus-on-open: consolidated cleo.db is empty and legacy data present \u2014 auto-migrating"
|
|
38268
39074
|
);
|
|
@@ -38271,23 +39077,23 @@ async function maybeRunExodusOnOpen(scope, dbPath, nativeDb, cwd) {
|
|
|
38271
39077
|
const migrateResult = await runExodusMigrate2(
|
|
38272
39078
|
plan,
|
|
38273
39079
|
false,
|
|
38274
|
-
(msg) =>
|
|
39080
|
+
(msg) => log8.debug({ scope }, `exodus-on-open: ${msg}`)
|
|
38275
39081
|
);
|
|
38276
39082
|
if (!migrateResult.ok) {
|
|
38277
39083
|
await rollbackBothScopes(scope, plan.projectDbPath, plan.globalDbPath);
|
|
38278
39084
|
clearExodusJournal2(migrateResult.stagingDir);
|
|
38279
39085
|
const reason = `migration failed: ${migrateResult.error ?? "unknown error"} \u2014 legacy DBs kept as source`;
|
|
38280
|
-
|
|
39086
|
+
log8.error({ scope, error: migrateResult.error }, `exodus-on-open: ${reason}`);
|
|
38281
39087
|
return { outcome: "aborted", reason };
|
|
38282
39088
|
}
|
|
38283
39089
|
const verifyResult = verifyMigration2(
|
|
38284
39090
|
plan.sources,
|
|
38285
39091
|
plan.projectDbPath,
|
|
38286
39092
|
plan.globalDbPath,
|
|
38287
|
-
(msg) =>
|
|
39093
|
+
(msg) => log8.debug({ scope }, `exodus-on-open verify: ${msg}`)
|
|
38288
39094
|
);
|
|
38289
39095
|
if (!verifyResult.ok) {
|
|
38290
|
-
|
|
39096
|
+
log8.warn(
|
|
38291
39097
|
{
|
|
38292
39098
|
scope,
|
|
38293
39099
|
enumDrift: verifyResult.enumDrift.length,
|
|
@@ -38301,7 +39107,7 @@ async function maybeRunExodusOnOpen(scope, dbPath, nativeDb, cwd) {
|
|
|
38301
39107
|
clearExodusJournal2(plan.stagingDir);
|
|
38302
39108
|
const deficits = verifyResult.tables.filter((t) => t.targetCount < t.sourceCount).map((t) => `${t.targetTable}(${t.sourceCount}\u2192${t.targetCount})`);
|
|
38303
39109
|
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();
|
|
38304
|
-
|
|
39110
|
+
log8.error(
|
|
38305
39111
|
{
|
|
38306
39112
|
scope,
|
|
38307
39113
|
countDeficits: deficits,
|
|
@@ -38313,14 +39119,14 @@ async function maybeRunExodusOnOpen(scope, dbPath, nativeDb, cwd) {
|
|
|
38313
39119
|
return { outcome: "aborted", reason };
|
|
38314
39120
|
}
|
|
38315
39121
|
const rowsCopied = migrateResult.tables.filter((t) => !t.skipped).reduce((n, t) => n + t.rowsCopied, 0);
|
|
38316
|
-
|
|
39122
|
+
log8.info(
|
|
38317
39123
|
{ scope, rowsCopied, tables: migrateResult.tables.length },
|
|
38318
39124
|
"exodus-on-open: parity verified \u2014 legacy data migrated into consolidated cleo.db"
|
|
38319
39125
|
);
|
|
38320
39126
|
try {
|
|
38321
|
-
const consumed = plan.sources.filter((s) =>
|
|
39127
|
+
const consumed = plan.sources.filter((s) => existsSync14(s.path));
|
|
38322
39128
|
const archiveResult = archiveMigratedSources(consumed, cwd);
|
|
38323
|
-
|
|
39129
|
+
log8.info(
|
|
38324
39130
|
{
|
|
38325
39131
|
scope,
|
|
38326
39132
|
archived: archiveResult.sources.filter((s) => s.action === "archived").length,
|
|
@@ -38329,7 +39135,7 @@ async function maybeRunExodusOnOpen(scope, dbPath, nativeDb, cwd) {
|
|
|
38329
39135
|
"exodus-on-open: archived legacy sources + sealed completion marker(s)"
|
|
38330
39136
|
);
|
|
38331
39137
|
} catch (err) {
|
|
38332
|
-
|
|
39138
|
+
log8.error(
|
|
38333
39139
|
{ err, scope },
|
|
38334
39140
|
"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`)"
|
|
38335
39141
|
);
|
|
@@ -38351,14 +39157,14 @@ async function maybeRunExodusOnOpen(scope, dbPath, nativeDb, cwd) {
|
|
|
38351
39157
|
function _isExodusInProgress() {
|
|
38352
39158
|
return _exodusInProgress;
|
|
38353
39159
|
}
|
|
38354
|
-
var
|
|
39160
|
+
var log8, _exodusInProgress;
|
|
38355
39161
|
var init_on_open = __esm({
|
|
38356
39162
|
"packages/core/src/store/exodus/on-open.ts"() {
|
|
38357
39163
|
"use strict";
|
|
38358
39164
|
init_logger2();
|
|
38359
39165
|
init_lock();
|
|
38360
39166
|
init_archive2();
|
|
38361
|
-
|
|
39167
|
+
log8 = getLogger("exodus-on-open");
|
|
38362
39168
|
_exodusInProgress = false;
|
|
38363
39169
|
}
|
|
38364
39170
|
});
|
|
@@ -38375,9 +39181,9 @@ __export(dual_scope_db_exports, {
|
|
|
38375
39181
|
resolveDualScopeDbPath: () => resolveDualScopeDbPath,
|
|
38376
39182
|
upsertIdempotent: () => upsertIdempotent
|
|
38377
39183
|
});
|
|
38378
|
-
import { existsSync as
|
|
39184
|
+
import { existsSync as existsSync15, mkdirSync as mkdirSync4 } from "node:fs";
|
|
38379
39185
|
import { createRequire as createRequire4 } from "node:module";
|
|
38380
|
-
import { dirname as dirname6, join as
|
|
39186
|
+
import { dirname as dirname6, join as join15 } from "node:path";
|
|
38381
39187
|
function assertWriteDurable(handle) {
|
|
38382
39188
|
if (handle.exodusAbort) {
|
|
38383
39189
|
throw new ExodusAbortWriteUnsafeError(handle.exodusAbort);
|
|
@@ -38394,9 +39200,9 @@ function cacheKey(scope, dbPath) {
|
|
|
38394
39200
|
}
|
|
38395
39201
|
function resolveDualScopeDbPath(scope, cwd) {
|
|
38396
39202
|
if (scope === "project") {
|
|
38397
|
-
return
|
|
39203
|
+
return join15(resolveCleoDir(cwd), "cleo.db");
|
|
38398
39204
|
}
|
|
38399
|
-
return
|
|
39205
|
+
return join15(getCleoHome(), "cleo.db");
|
|
38400
39206
|
}
|
|
38401
39207
|
function migrationsSetName(scope) {
|
|
38402
39208
|
return scope === "project" ? "drizzle-cleo-project" : "drizzle-cleo-global";
|
|
@@ -38439,11 +39245,11 @@ async function openDualScopeDb(scope, cwd) {
|
|
|
38439
39245
|
const dbPath = resolveDualScopeDbPath(scope, cwd);
|
|
38440
39246
|
return scope === "project" ? openDualScopeDbAtPath("project", dbPath, cwd) : openDualScopeDbAtPath("global", dbPath, cwd);
|
|
38441
39247
|
}
|
|
38442
|
-
async function openDedicatedDualScopeDb(scope, dbPath,
|
|
38443
|
-
|
|
39248
|
+
async function openDedicatedDualScopeDb(scope, dbPath, log12) {
|
|
39249
|
+
log12.debug({ scope, dbPath }, "opening DEDICATED (non-cached) dual-scope cleo.db (T11782 FIX D)");
|
|
38444
39250
|
const dir = dirname6(dbPath);
|
|
38445
|
-
if (!
|
|
38446
|
-
|
|
39251
|
+
if (!existsSync15(dir)) {
|
|
39252
|
+
mkdirSync4(dir, { recursive: true });
|
|
38447
39253
|
}
|
|
38448
39254
|
const DatabaseSyncCtor = getDatabaseSyncCtor();
|
|
38449
39255
|
const nativeDb = new DatabaseSyncCtor(dbPath, { allowExtension: true });
|
|
@@ -38466,7 +39272,7 @@ async function openDedicatedDualScopeDb(scope, dbPath, log11) {
|
|
|
38466
39272
|
existenceTable(scope),
|
|
38467
39273
|
`dual-scope-db[${scope}]`
|
|
38468
39274
|
);
|
|
38469
|
-
|
|
39275
|
+
log12.debug({ scope, dbPath }, "DEDICATED dual-scope cleo.db ready (T11782 FIX D)");
|
|
38470
39276
|
return {
|
|
38471
39277
|
db,
|
|
38472
39278
|
scope,
|
|
@@ -38491,15 +39297,15 @@ async function openDualScopeDbAtPath(scope, dbPath, exodusCwd, options) {
|
|
|
38491
39297
|
return existing.handle;
|
|
38492
39298
|
}
|
|
38493
39299
|
}
|
|
38494
|
-
const
|
|
39300
|
+
const log12 = getLogger("dual-scope-db");
|
|
38495
39301
|
if (dedicated) {
|
|
38496
|
-
return openDedicatedDualScopeDb(scope, dbPath,
|
|
39302
|
+
return openDedicatedDualScopeDb(scope, dbPath, log12);
|
|
38497
39303
|
}
|
|
38498
39304
|
const initPromise = (async () => {
|
|
38499
|
-
|
|
39305
|
+
log12.debug({ scope, dbPath }, "opening dual-scope cleo.db");
|
|
38500
39306
|
const dir = dirname6(dbPath);
|
|
38501
|
-
if (!
|
|
38502
|
-
|
|
39307
|
+
if (!existsSync15(dir)) {
|
|
39308
|
+
mkdirSync4(dir, { recursive: true });
|
|
38503
39309
|
}
|
|
38504
39310
|
const DatabaseSyncCtor = getDatabaseSyncCtor();
|
|
38505
39311
|
const nativeDb = new DatabaseSyncCtor(dbPath, { allowExtension: true });
|
|
@@ -38526,7 +39332,7 @@ async function openDualScopeDbAtPath(scope, dbPath, exodusCwd, options) {
|
|
|
38526
39332
|
existenceTable(scope),
|
|
38527
39333
|
`dual-scope-db[${scope}]`
|
|
38528
39334
|
);
|
|
38529
|
-
|
|
39335
|
+
log12.debug({ scope, dbPath }, "dual-scope cleo.db ready");
|
|
38530
39336
|
const built = {
|
|
38531
39337
|
db,
|
|
38532
39338
|
scope,
|
|
@@ -38552,6 +39358,25 @@ async function openDualScopeDbAtPath(scope, dbPath, exodusCwd, options) {
|
|
|
38552
39358
|
{ dbPath }
|
|
38553
39359
|
);
|
|
38554
39360
|
if (exodusCwd !== void 0 && dbPath === resolveDualScopeDbPath(scope, exodusCwd)) {
|
|
39361
|
+
let releaseDbHeavy = null;
|
|
39362
|
+
let dbHeavyDeferred = false;
|
|
39363
|
+
try {
|
|
39364
|
+
const { governor: governor2 } = await Promise.resolve().then(() => (init_governor(), governor_exports));
|
|
39365
|
+
const admit = await governor2.tryAcquire("db-heavy");
|
|
39366
|
+
if (admit.deferred) {
|
|
39367
|
+
dbHeavyDeferred = true;
|
|
39368
|
+
} else {
|
|
39369
|
+
releaseDbHeavy = admit.release;
|
|
39370
|
+
}
|
|
39371
|
+
} catch {
|
|
39372
|
+
}
|
|
39373
|
+
if (dbHeavyDeferred) {
|
|
39374
|
+
log12.debug(
|
|
39375
|
+
{ scope, dbPath },
|
|
39376
|
+
"exodus-on-open skipped this open \u2014 db-heavy deferred under memory pressure (re-runs idempotently on a calmer open)"
|
|
39377
|
+
);
|
|
39378
|
+
return handle;
|
|
39379
|
+
}
|
|
38555
39380
|
try {
|
|
38556
39381
|
const { maybeRunExodusOnOpen: maybeRunExodusOnOpen2 } = await Promise.resolve().then(() => (init_on_open(), on_open_exports));
|
|
38557
39382
|
const result = await maybeRunExodusOnOpen2(scope, dbPath, nativeDb, exodusCwd);
|
|
@@ -38564,7 +39389,7 @@ async function openDualScopeDbAtPath(scope, dbPath, exodusCwd, options) {
|
|
|
38564
39389
|
reason: result.reason,
|
|
38565
39390
|
at: Date.now()
|
|
38566
39391
|
};
|
|
38567
|
-
|
|
39392
|
+
log12.warn(
|
|
38568
39393
|
{ scope, reason: result.reason },
|
|
38569
39394
|
"exodus-on-open aborted; consolidated cleo.db left empty, legacy kept as source \u2014 mutating callers must check handle.exodusAbort / call assertWriteDurable (T11828)"
|
|
38570
39395
|
);
|
|
@@ -38577,11 +39402,14 @@ async function openDualScopeDbAtPath(scope, dbPath, exodusCwd, options) {
|
|
|
38577
39402
|
return reopened;
|
|
38578
39403
|
}
|
|
38579
39404
|
} catch (err) {
|
|
38580
|
-
|
|
39405
|
+
log12.warn(
|
|
38581
39406
|
{ err, scope },
|
|
38582
39407
|
"exodus-on-open hook failed (non-fatal); re-opening consolidated handle"
|
|
38583
39408
|
);
|
|
38584
39409
|
return scope === "project" ? openDualScopeDbAtPath("project", dbPath) : openDualScopeDbAtPath("global", dbPath);
|
|
39410
|
+
} finally {
|
|
39411
|
+
if (releaseDbHeavy) await releaseDbHeavy().catch(() => {
|
|
39412
|
+
});
|
|
38585
39413
|
}
|
|
38586
39414
|
}
|
|
38587
39415
|
return handle;
|
|
@@ -39204,8 +40032,8 @@ __export(nexus_sqlite_exports, {
|
|
|
39204
40032
|
resetNexusDbState: () => resetNexusDbState,
|
|
39205
40033
|
resolveNexusMigrationsFolder: () => resolveNexusMigrationsFolder
|
|
39206
40034
|
});
|
|
39207
|
-
import { copyFileSync as copyFileSync4, existsSync as
|
|
39208
|
-
import { join as
|
|
40035
|
+
import { copyFileSync as copyFileSync4, existsSync as existsSync16 } from "node:fs";
|
|
40036
|
+
import { join as join16 } from "node:path";
|
|
39209
40037
|
function getNexusDbPath(cwd) {
|
|
39210
40038
|
return resolveDualScopeDbPath("project", cwd);
|
|
39211
40039
|
}
|
|
@@ -39237,7 +40065,7 @@ function resolveNexusMigrationsFolder() {
|
|
|
39237
40065
|
return resolveCorePackageMigrationsFolder("drizzle-nexus");
|
|
39238
40066
|
}
|
|
39239
40067
|
function getNestedNexusSentinelPath() {
|
|
39240
|
-
return
|
|
40068
|
+
return join16(getCleoHome(), "nexus", NESTED_NEXUS_SENTINEL);
|
|
39241
40069
|
}
|
|
39242
40070
|
function detectAndWarnOnNestedNexus() {
|
|
39243
40071
|
let nestedPath;
|
|
@@ -39246,12 +40074,12 @@ function detectAndWarnOnNestedNexus() {
|
|
|
39246
40074
|
} catch {
|
|
39247
40075
|
return false;
|
|
39248
40076
|
}
|
|
39249
|
-
if (!
|
|
40077
|
+
if (!existsSync16(nestedPath)) return false;
|
|
39250
40078
|
if (_warnedNestedPaths.has(nestedPath)) return false;
|
|
39251
40079
|
_warnedNestedPaths.add(nestedPath);
|
|
39252
40080
|
const canonicalPath = getNexusDbPath();
|
|
39253
|
-
const
|
|
39254
|
-
|
|
40081
|
+
const log12 = getLogger("nexus-sqlite");
|
|
40082
|
+
log12.warn(
|
|
39255
40083
|
{
|
|
39256
40084
|
nestedPath,
|
|
39257
40085
|
canonicalPath,
|
|
@@ -39405,7 +40233,7 @@ function runNexusMigrations(nativeDb, db) {
|
|
|
39405
40233
|
const migrationsFolder = resolveNexusMigrationsFolder();
|
|
39406
40234
|
if (tableExists4(nativeDb, "nexus_nodes") && _nexusDbPath) {
|
|
39407
40235
|
const backupPath = _nexusDbPath.replace(/\.db$/, "-pre-cleo.db.bak");
|
|
39408
|
-
if (!
|
|
40236
|
+
if (!existsSync16(backupPath)) {
|
|
39409
40237
|
try {
|
|
39410
40238
|
copyFileSync4(_nexusDbPath, backupPath);
|
|
39411
40239
|
} catch {
|
|
@@ -39543,10 +40371,10 @@ var init_nexus_sqlite = __esm({
|
|
|
39543
40371
|
|
|
39544
40372
|
// packages/core/src/paths.ts
|
|
39545
40373
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
39546
|
-
import { existsSync as
|
|
40374
|
+
import { existsSync as existsSync17, readFileSync as readFileSync5, statSync as statSync6 } from "node:fs";
|
|
39547
40375
|
import { createRequire as createRequire5 } from "node:module";
|
|
39548
40376
|
import { homedir } from "node:os";
|
|
39549
|
-
import { basename as basename4, dirname as dirname7, join as
|
|
40377
|
+
import { basename as basename4, dirname as dirname7, join as join17, resolve as resolve3 } from "node:path";
|
|
39550
40378
|
import {
|
|
39551
40379
|
computeCanonicalProjectId as _computeCanonicalProjectId,
|
|
39552
40380
|
getCanonicalTemplatesTildePath as _getCanonicalTemplatesTildePath,
|
|
@@ -39636,8 +40464,8 @@ function getCleoDirAbsolute(cwd, opts) {
|
|
|
39636
40464
|
function _resolveProjectByCwdFromNexus(cwd) {
|
|
39637
40465
|
try {
|
|
39638
40466
|
const cleoHome = getCleoHome();
|
|
39639
|
-
const globalDbPath =
|
|
39640
|
-
if (!
|
|
40467
|
+
const globalDbPath = join17(cleoHome, "cleo.db");
|
|
40468
|
+
if (!existsSync17(globalDbPath)) return null;
|
|
39641
40469
|
const start = resolve3(cwd ?? process.cwd());
|
|
39642
40470
|
let current = start;
|
|
39643
40471
|
const DatabaseSync3 = _getDatabaseSyncCtor();
|
|
@@ -39676,7 +40504,7 @@ function _findCleoDirRoot(cwd) {
|
|
|
39676
40504
|
const isDangerousRoot = current === homeRoot || current === "/" || current === "";
|
|
39677
40505
|
if (!isDangerousRoot) {
|
|
39678
40506
|
try {
|
|
39679
|
-
if (statSync6(
|
|
40507
|
+
if (statSync6(join17(current, ".cleo")).isDirectory()) {
|
|
39680
40508
|
return current;
|
|
39681
40509
|
}
|
|
39682
40510
|
} catch {
|
|
@@ -39691,7 +40519,7 @@ function _findCleoDirRoot(cwd) {
|
|
|
39691
40519
|
function resolveCleoDir(cwd) {
|
|
39692
40520
|
const scope = worktreeScope.getStore();
|
|
39693
40521
|
if (scope !== void 0) {
|
|
39694
|
-
return
|
|
40522
|
+
return join17(scope.worktreeRoot, ".cleo");
|
|
39695
40523
|
}
|
|
39696
40524
|
const override = _cleoDirEnvOverride();
|
|
39697
40525
|
if (override !== null) {
|
|
@@ -39699,14 +40527,14 @@ function resolveCleoDir(cwd) {
|
|
|
39699
40527
|
}
|
|
39700
40528
|
const root = _findCleoDirRoot(cwd);
|
|
39701
40529
|
if (root !== null) {
|
|
39702
|
-
return
|
|
40530
|
+
return join17(root, ".cleo");
|
|
39703
40531
|
}
|
|
39704
40532
|
const start = resolve3(cwd ?? process.cwd());
|
|
39705
40533
|
let current = start;
|
|
39706
40534
|
while (true) {
|
|
39707
40535
|
const mainRepo = _resolveMainRepoFromGitlink(current);
|
|
39708
40536
|
if (mainRepo !== null) {
|
|
39709
|
-
return
|
|
40537
|
+
return join17(mainRepo, ".cleo");
|
|
39710
40538
|
}
|
|
39711
40539
|
const parent = dirname7(current);
|
|
39712
40540
|
if (parent === current) {
|
|
@@ -39748,9 +40576,9 @@ function _cwdHasGitAncestor(cwd) {
|
|
|
39748
40576
|
const start = resolve3(cwd ?? process.cwd());
|
|
39749
40577
|
let current = start;
|
|
39750
40578
|
while (true) {
|
|
39751
|
-
const gitMarker =
|
|
40579
|
+
const gitMarker = join17(current, ".git");
|
|
39752
40580
|
try {
|
|
39753
|
-
if (
|
|
40581
|
+
if (existsSync17(gitMarker)) {
|
|
39754
40582
|
return true;
|
|
39755
40583
|
}
|
|
39756
40584
|
} catch {
|
|
@@ -39762,16 +40590,16 @@ function _cwdHasGitAncestor(cwd) {
|
|
|
39762
40590
|
}
|
|
39763
40591
|
function _resolveMainRepoFromGitlink(gitlinkDir) {
|
|
39764
40592
|
try {
|
|
39765
|
-
const gitLinkPath =
|
|
39766
|
-
if (!
|
|
39767
|
-
const
|
|
39768
|
-
if (!
|
|
40593
|
+
const gitLinkPath = join17(gitlinkDir, ".git");
|
|
40594
|
+
if (!existsSync17(gitLinkPath)) return null;
|
|
40595
|
+
const stat3 = statSync6(gitLinkPath);
|
|
40596
|
+
if (!stat3.isFile()) return null;
|
|
39769
40597
|
const gitLinkContent = readFileSync5(gitLinkPath, "utf-8").trim();
|
|
39770
40598
|
const match = gitLinkContent.match(/^gitdir:\s*(.+)$/m);
|
|
39771
40599
|
if (!match) return null;
|
|
39772
40600
|
const gitdir = match[1].trim();
|
|
39773
40601
|
const mainRepo = dirname7(dirname7(dirname7(gitdir)));
|
|
39774
|
-
if (
|
|
40602
|
+
if (existsSync17(join17(mainRepo, ".cleo")) && validateProjectRoot(mainRepo)) {
|
|
39775
40603
|
return mainRepo;
|
|
39776
40604
|
}
|
|
39777
40605
|
} catch {
|
|
@@ -39779,18 +40607,18 @@ function _resolveMainRepoFromGitlink(gitlinkDir) {
|
|
|
39779
40607
|
return null;
|
|
39780
40608
|
}
|
|
39781
40609
|
function validateProjectRoot(candidate) {
|
|
39782
|
-
const cleoDir =
|
|
39783
|
-
if (!
|
|
40610
|
+
const cleoDir = join17(candidate, ".cleo");
|
|
40611
|
+
if (!existsSync17(cleoDir)) {
|
|
39784
40612
|
return false;
|
|
39785
40613
|
}
|
|
39786
|
-
const projectInfoPath =
|
|
39787
|
-
if (
|
|
40614
|
+
const projectInfoPath = join17(cleoDir, "project-info.json");
|
|
40615
|
+
if (existsSync17(projectInfoPath)) {
|
|
39788
40616
|
try {
|
|
39789
40617
|
const raw = readFileSync5(projectInfoPath, "utf-8");
|
|
39790
40618
|
const parsed = JSON.parse(raw);
|
|
39791
40619
|
if (typeof parsed === "object" && parsed !== null && "projectId" in parsed && typeof parsed["projectId"] === "string" && parsed["projectId"] !== "") {
|
|
39792
|
-
const gitMarker =
|
|
39793
|
-
if (
|
|
40620
|
+
const gitMarker = join17(candidate, ".git");
|
|
40621
|
+
if (existsSync17(gitMarker)) {
|
|
39794
40622
|
try {
|
|
39795
40623
|
if (!statSync6(gitMarker).isDirectory()) {
|
|
39796
40624
|
return false;
|
|
@@ -39804,12 +40632,12 @@ function validateProjectRoot(candidate) {
|
|
|
39804
40632
|
} catch {
|
|
39805
40633
|
}
|
|
39806
40634
|
}
|
|
39807
|
-
const gitDir =
|
|
39808
|
-
if (
|
|
40635
|
+
const gitDir = join17(candidate, ".git");
|
|
40636
|
+
if (existsSync17(gitDir)) {
|
|
39809
40637
|
let isRealGitDir = false;
|
|
39810
40638
|
try {
|
|
39811
|
-
const
|
|
39812
|
-
isRealGitDir =
|
|
40639
|
+
const stat3 = statSync6(gitDir);
|
|
40640
|
+
isRealGitDir = stat3.isDirectory();
|
|
39813
40641
|
} catch {
|
|
39814
40642
|
isRealGitDir = false;
|
|
39815
40643
|
}
|
|
@@ -39852,16 +40680,16 @@ function getProjectRoot(cwd) {
|
|
|
39852
40680
|
const homeRoot = homedir();
|
|
39853
40681
|
const skippedCleoDirs = [];
|
|
39854
40682
|
while (true) {
|
|
39855
|
-
const cleoDir =
|
|
39856
|
-
const gitDir =
|
|
40683
|
+
const cleoDir = join17(current, ".cleo");
|
|
40684
|
+
const gitDir = join17(current, ".git");
|
|
39857
40685
|
const isDangerousRoot = current === homeRoot || current === "/" || current === "";
|
|
39858
|
-
if (
|
|
40686
|
+
if (existsSync17(cleoDir) && !isDangerousRoot) {
|
|
39859
40687
|
if (validateProjectRoot(current)) {
|
|
39860
40688
|
return current;
|
|
39861
40689
|
}
|
|
39862
40690
|
skippedCleoDirs.push(current);
|
|
39863
40691
|
}
|
|
39864
|
-
if (
|
|
40692
|
+
if (existsSync17(gitDir) && !isDangerousRoot) {
|
|
39865
40693
|
let isRealGitDir = false;
|
|
39866
40694
|
try {
|
|
39867
40695
|
isRealGitDir = statSync6(gitDir).isDirectory();
|
|
@@ -39906,18 +40734,18 @@ function resolveOrCwd(maybeRoot) {
|
|
|
39906
40734
|
return getProjectRoot();
|
|
39907
40735
|
}
|
|
39908
40736
|
function getConfigPath(cwd) {
|
|
39909
|
-
return
|
|
40737
|
+
return join17(_resolveCleoDir(cwd), "config.json");
|
|
39910
40738
|
}
|
|
39911
40739
|
function getGlobalConfigPath() {
|
|
39912
|
-
return
|
|
40740
|
+
return join17(getCleoHome(), "config.json");
|
|
39913
40741
|
}
|
|
39914
40742
|
function isAbsolutePath(path2) {
|
|
39915
40743
|
return _isAbsolutePath(path2);
|
|
39916
40744
|
}
|
|
39917
40745
|
function _readProjectNameFromInfo(projectRoot) {
|
|
39918
40746
|
try {
|
|
39919
|
-
const infoPath =
|
|
39920
|
-
if (!
|
|
40747
|
+
const infoPath = join17(projectRoot, ".cleo", "project-info.json");
|
|
40748
|
+
if (!existsSync17(infoPath)) return void 0;
|
|
39921
40749
|
const raw = readFileSync5(infoPath, "utf-8");
|
|
39922
40750
|
const data = JSON.parse(raw);
|
|
39923
40751
|
return typeof data.name === "string" && data.name.length > 0 ? data.name : void 0;
|
|
@@ -39943,8 +40771,8 @@ async function registerProjectOnEncounter(projectRoot, infoProjectId) {
|
|
|
39943
40771
|
if (existingRows.length > 0) {
|
|
39944
40772
|
const existingPath = existingRows[0].projectPath;
|
|
39945
40773
|
if (existingPath !== resolvedPath) {
|
|
39946
|
-
const newBrainDbPath =
|
|
39947
|
-
const newTasksDbPath =
|
|
40774
|
+
const newBrainDbPath = join17(resolvedPath, ".cleo", "brain.db");
|
|
40775
|
+
const newTasksDbPath = join17(resolvedPath, ".cleo", "tasks.db");
|
|
39948
40776
|
await db.update(projectRegistry2).set({
|
|
39949
40777
|
projectPath: resolvedPath,
|
|
39950
40778
|
projectHash: generateProjectHash2(resolvedPath),
|
|
@@ -39975,8 +40803,8 @@ async function registerProjectOnEncounter(projectRoot, infoProjectId) {
|
|
|
39975
40803
|
lastSync: now,
|
|
39976
40804
|
taskCount: 0,
|
|
39977
40805
|
labelsJson: "[]",
|
|
39978
|
-
brainDbPath:
|
|
39979
|
-
tasksDbPath:
|
|
40806
|
+
brainDbPath: join17(resolvedPath, ".cleo", "brain.db"),
|
|
40807
|
+
tasksDbPath: join17(resolvedPath, ".cleo", "tasks.db"),
|
|
39980
40808
|
statsJson: "{}"
|
|
39981
40809
|
}).onConflictDoNothing();
|
|
39982
40810
|
const aliases = canonicalResult.legacyAliases ?? [];
|
|
@@ -40193,7 +41021,7 @@ __export(config_exports, {
|
|
|
40193
41021
|
parseConfigValue: () => parseConfigValue,
|
|
40194
41022
|
setConfigValue: () => setConfigValue
|
|
40195
41023
|
});
|
|
40196
|
-
import { existsSync as
|
|
41024
|
+
import { existsSync as existsSync18 } from "node:fs";
|
|
40197
41025
|
function getNestedValue(obj, path2) {
|
|
40198
41026
|
const parts = path2.split(".");
|
|
40199
41027
|
let current = obj;
|
|
@@ -40241,7 +41069,7 @@ function parseEnvValue(value) {
|
|
|
40241
41069
|
return value;
|
|
40242
41070
|
}
|
|
40243
41071
|
async function loadConfig(cwd) {
|
|
40244
|
-
let merged = JSON.parse(JSON.stringify(
|
|
41072
|
+
let merged = JSON.parse(JSON.stringify(DEFAULTS2));
|
|
40245
41073
|
const globalConfig = await readJson(getGlobalConfigPath());
|
|
40246
41074
|
if (globalConfig) {
|
|
40247
41075
|
merged = deepMerge(merged, globalConfig);
|
|
@@ -40287,7 +41115,7 @@ async function getConfigValue(path2, cwd) {
|
|
|
40287
41115
|
return { value: val, source: "global" };
|
|
40288
41116
|
}
|
|
40289
41117
|
}
|
|
40290
|
-
const defaultVal = getNestedValue({ ...
|
|
41118
|
+
const defaultVal = getNestedValue({ ...DEFAULTS2 }, path2);
|
|
40291
41119
|
return { value: defaultVal, source: "default" };
|
|
40292
41120
|
}
|
|
40293
41121
|
async function getRawConfigValue(key, cwd) {
|
|
@@ -40315,7 +41143,7 @@ function parseConfigValue(value) {
|
|
|
40315
41143
|
}
|
|
40316
41144
|
async function setConfigValue(key, value, cwd, opts) {
|
|
40317
41145
|
const configPath = opts?.global ? getGlobalConfigPath() : getConfigPath(cwd);
|
|
40318
|
-
if (!
|
|
41146
|
+
if (!existsSync18(configPath)) {
|
|
40319
41147
|
await atomicWriteJson(configPath, {});
|
|
40320
41148
|
}
|
|
40321
41149
|
const config = await readJson(configPath) ?? {};
|
|
@@ -40327,7 +41155,7 @@ async function setConfigValue(key, value, cwd, opts) {
|
|
|
40327
41155
|
async function applyStrictnessPreset(preset, cwd, opts) {
|
|
40328
41156
|
const definition = STRICTNESS_PRESETS[preset];
|
|
40329
41157
|
const configPath = opts?.global ? getGlobalConfigPath() : getConfigPath(cwd);
|
|
40330
|
-
if (!
|
|
41158
|
+
if (!existsSync18(configPath)) {
|
|
40331
41159
|
await atomicWriteJson(configPath, {});
|
|
40332
41160
|
}
|
|
40333
41161
|
const config = await readJson(configPath) ?? {};
|
|
@@ -40351,7 +41179,7 @@ function listStrictnessPresets() {
|
|
|
40351
41179
|
})
|
|
40352
41180
|
);
|
|
40353
41181
|
}
|
|
40354
|
-
var
|
|
41182
|
+
var DEFAULTS2, ENV_MAP, STRICTNESS_PRESETS;
|
|
40355
41183
|
var init_config = __esm({
|
|
40356
41184
|
"packages/core/src/config.ts"() {
|
|
40357
41185
|
"use strict";
|
|
@@ -40359,7 +41187,7 @@ var init_config = __esm({
|
|
|
40359
41187
|
init_paths();
|
|
40360
41188
|
init_atomic();
|
|
40361
41189
|
init_json2();
|
|
40362
|
-
|
|
41190
|
+
DEFAULTS2 = {
|
|
40363
41191
|
version: "2.10.0",
|
|
40364
41192
|
output: {
|
|
40365
41193
|
defaultFormat: "json",
|
|
@@ -40678,7 +41506,7 @@ function brainTablesAreConsolidatedShape(nativeDb) {
|
|
|
40678
41506
|
return createdAt !== void 0 && createdAt.type.toUpperCase() !== "INTEGER";
|
|
40679
41507
|
}
|
|
40680
41508
|
function establishLegacyBrainSchema(nativeDb, db) {
|
|
40681
|
-
const
|
|
41509
|
+
const log12 = getLogger("brain-schema");
|
|
40682
41510
|
if (brainTablesAreConsolidatedShape(nativeDb)) {
|
|
40683
41511
|
const fkRow = nativeDb.prepare("PRAGMA foreign_keys").get();
|
|
40684
41512
|
const fkWasOn = fkRow?.foreign_keys === 1;
|
|
@@ -40688,7 +41516,7 @@ function establishLegacyBrainSchema(nativeDb, db) {
|
|
|
40688
41516
|
try {
|
|
40689
41517
|
nativeDb.exec(`DROP TABLE IF EXISTS \`${table}\``);
|
|
40690
41518
|
} catch (err) {
|
|
40691
|
-
|
|
41519
|
+
log12.warn(
|
|
40692
41520
|
{ table, err },
|
|
40693
41521
|
"Failed to drop consolidated brain table during legacy rebuild."
|
|
40694
41522
|
);
|
|
@@ -40697,7 +41525,7 @@ function establishLegacyBrainSchema(nativeDb, db) {
|
|
|
40697
41525
|
} finally {
|
|
40698
41526
|
nativeDb.exec(`PRAGMA foreign_keys=${fkWasOn ? "ON" : "OFF"}`);
|
|
40699
41527
|
}
|
|
40700
|
-
|
|
41528
|
+
log12.debug(
|
|
40701
41529
|
{ count: CONSOLIDATED_BRAIN_TABLES.length },
|
|
40702
41530
|
"Dropped consolidated (exodus-target) brain tables \u2014 rebuilding in legacy runtime shape."
|
|
40703
41531
|
);
|
|
@@ -40708,7 +41536,7 @@ function establishLegacyBrainSchema(nativeDb, db) {
|
|
|
40708
41536
|
nativeDb,
|
|
40709
41537
|
migrationsFolder
|
|
40710
41538
|
);
|
|
40711
|
-
|
|
41539
|
+
log12.debug(
|
|
40712
41540
|
{ marked, applied },
|
|
40713
41541
|
"brain consolidated cleo.db reconcile (T11647) \u2014 marked already-present migrations applied + executed the missing unprefixed-table migrations directly."
|
|
40714
41542
|
);
|
|
@@ -41264,7 +42092,7 @@ async function upsertTask(db, row, archiveFields, allowOrphanParent = false) {
|
|
|
41264
42092
|
if (allowOrphanParent) {
|
|
41265
42093
|
row = { ...row, parentId: null };
|
|
41266
42094
|
} else {
|
|
41267
|
-
|
|
42095
|
+
log9.warn(
|
|
41268
42096
|
{ taskId: row.id, parentId: row.parentId },
|
|
41269
42097
|
"upsertTask: parentId references a non-existent task \u2014 parent relationship may be lost"
|
|
41270
42098
|
);
|
|
@@ -41449,13 +42277,13 @@ async function loadRelationsForTasks(db, tasks2) {
|
|
|
41449
42277
|
task.relates = relations && relations.length > 0 ? relations : [];
|
|
41450
42278
|
}
|
|
41451
42279
|
}
|
|
41452
|
-
var
|
|
42280
|
+
var log9;
|
|
41453
42281
|
var init_db_helpers = __esm({
|
|
41454
42282
|
"packages/core/src/store/db-helpers.ts"() {
|
|
41455
42283
|
"use strict";
|
|
41456
42284
|
init_logger2();
|
|
41457
42285
|
init_tasks_schema();
|
|
41458
|
-
|
|
42286
|
+
log9 = getLogger("db-helpers");
|
|
41459
42287
|
}
|
|
41460
42288
|
});
|
|
41461
42289
|
|
|
@@ -41556,10 +42384,10 @@ var init_telemetry_schema = __esm({
|
|
|
41556
42384
|
});
|
|
41557
42385
|
|
|
41558
42386
|
// packages/core/src/telemetry/sqlite.ts
|
|
41559
|
-
import { mkdirSync as
|
|
41560
|
-
import { dirname as dirname8, join as
|
|
42387
|
+
import { mkdirSync as mkdirSync5 } from "node:fs";
|
|
42388
|
+
import { dirname as dirname8, join as join18 } from "node:path";
|
|
41561
42389
|
function getTelemetryDbPath() {
|
|
41562
|
-
return
|
|
42390
|
+
return join18(getCleoHome(), DB_FILENAME);
|
|
41563
42391
|
}
|
|
41564
42392
|
function resolveTelemetryMigrationsFolder() {
|
|
41565
42393
|
return resolveCorePackageMigrationsFolder("drizzle-telemetry");
|
|
@@ -41607,7 +42435,7 @@ async function getTelemetryDb() {
|
|
|
41607
42435
|
_initPromise2 = (async () => {
|
|
41608
42436
|
const dbPath = requestedPath;
|
|
41609
42437
|
_dbPath2 = dbPath;
|
|
41610
|
-
|
|
42438
|
+
mkdirSync5(dirname8(dbPath), { recursive: true });
|
|
41611
42439
|
const nativeDb = openNativeDatabase(dbPath);
|
|
41612
42440
|
_nativeDb2 = nativeDb;
|
|
41613
42441
|
const db = drizzle({ client: nativeDb, schema: telemetry_schema_exports });
|
|
@@ -41640,11 +42468,11 @@ var init_sqlite2 = __esm({
|
|
|
41640
42468
|
});
|
|
41641
42469
|
|
|
41642
42470
|
// packages/core/src/store/agent-registry-store.ts
|
|
41643
|
-
import { existsSync as
|
|
41644
|
-
import { join as
|
|
42471
|
+
import { existsSync as existsSync19 } from "node:fs";
|
|
42472
|
+
import { join as join19 } from "node:path";
|
|
41645
42473
|
function getGlobalAgentRegistryDbPath() {
|
|
41646
42474
|
const cleoHome = getCleoHome();
|
|
41647
|
-
const dbPath =
|
|
42475
|
+
const dbPath = join19(cleoHome, "cleo.db");
|
|
41648
42476
|
if (!dbPath.startsWith(cleoHome)) {
|
|
41649
42477
|
throw new Error(
|
|
41650
42478
|
`BUG: getGlobalAgentRegistryDbPath() resolved to "${dbPath}" which is NOT under getCleoHome() ("${cleoHome}"). The Agent Registry is global-only per ADR-037. This indicates a code path that bypasses path resolution \u2014 fix the caller, do not suppress this error.`
|
|
@@ -41691,8 +42519,8 @@ function writeAgentRegistrySchemaVersionSentinel(db) {
|
|
|
41691
42519
|
}
|
|
41692
42520
|
async function ensureGlobalAgentRegistryDb() {
|
|
41693
42521
|
const dbPath = getGlobalAgentRegistryDbPath();
|
|
41694
|
-
const alreadyExists =
|
|
41695
|
-
const dualHandle = await
|
|
42522
|
+
const alreadyExists = existsSync19(dbPath);
|
|
42523
|
+
const dualHandle = await openDualScopeDbAtPath("global", dbPath);
|
|
41696
42524
|
const nativeDb = dualHandle.db.$client ?? null;
|
|
41697
42525
|
if (!nativeDb) {
|
|
41698
42526
|
throw new Error(
|
|
@@ -41740,7 +42568,7 @@ var init_agent_registry_store = __esm({
|
|
|
41740
42568
|
});
|
|
41741
42569
|
|
|
41742
42570
|
// packages/core/src/store/conduit-sqlite.ts
|
|
41743
|
-
import { existsSync as
|
|
42571
|
+
import { existsSync as existsSync20 } from "node:fs";
|
|
41744
42572
|
import { createRequire as createRequire7 } from "node:module";
|
|
41745
42573
|
function _getDrizzle2() {
|
|
41746
42574
|
if (_drizzle3 === null) {
|
|
@@ -41777,7 +42605,7 @@ async function ensureConduitDb(projectRoot) {
|
|
|
41777
42605
|
}
|
|
41778
42606
|
if (_initPromise3) return _initPromise3;
|
|
41779
42607
|
_initPromise3 = (async () => {
|
|
41780
|
-
const alreadyExists =
|
|
42608
|
+
const alreadyExists = existsSync20(dbPath);
|
|
41781
42609
|
const dualHandle = await openDualScopeDb("project", projectRoot);
|
|
41782
42610
|
const nativeDb = dualHandle.db.$client ?? null;
|
|
41783
42611
|
if (!nativeDb) {
|
|
@@ -41997,11 +42825,11 @@ var init_skills_schema = __esm({
|
|
|
41997
42825
|
});
|
|
41998
42826
|
|
|
41999
42827
|
// packages/core/src/store/skills-db.ts
|
|
42000
|
-
import { join as
|
|
42828
|
+
import { join as join20 } from "node:path";
|
|
42001
42829
|
import { and as and3, eq as eq4 } from "drizzle-orm";
|
|
42002
42830
|
function getDefaultSkillsDbPath() {
|
|
42003
42831
|
const cleoHome = getCleoHome();
|
|
42004
|
-
const dbPath =
|
|
42832
|
+
const dbPath = join20(cleoHome, "cleo.db");
|
|
42005
42833
|
if (!dbPath.startsWith(cleoHome)) {
|
|
42006
42834
|
throw new Error(
|
|
42007
42835
|
`BUG: getDefaultSkillsDbPath() resolved to "${dbPath}" which is NOT under getCleoHome() ("${cleoHome}"). The skills registry is global-only per SG-CLEO-SKILLS-architecture-v3.md \xA74. Fix the caller, do not suppress.`
|
|
@@ -42071,13 +42899,13 @@ var init_skills_db = __esm({
|
|
|
42071
42899
|
import {
|
|
42072
42900
|
chmodSync,
|
|
42073
42901
|
copyFileSync as copyFileSync5,
|
|
42074
|
-
existsSync as
|
|
42075
|
-
mkdirSync as
|
|
42076
|
-
readdirSync as
|
|
42902
|
+
existsSync as existsSync21,
|
|
42903
|
+
mkdirSync as mkdirSync6,
|
|
42904
|
+
readdirSync as readdirSync5,
|
|
42077
42905
|
statSync as statSync7,
|
|
42078
42906
|
unlinkSync as unlinkSync3
|
|
42079
42907
|
} from "node:fs";
|
|
42080
|
-
import { join as
|
|
42908
|
+
import { join as join21 } from "node:path";
|
|
42081
42909
|
function resolveInventoryPath(entry, cwd) {
|
|
42082
42910
|
try {
|
|
42083
42911
|
if (entry.tier === "global") {
|
|
@@ -42112,8 +42940,8 @@ async function openAgentRegistryDbForSnapshot() {
|
|
|
42112
42940
|
}
|
|
42113
42941
|
async function openTelemetryDbForSnapshot() {
|
|
42114
42942
|
try {
|
|
42115
|
-
const path2 =
|
|
42116
|
-
if (!
|
|
42943
|
+
const path2 = join21(getCleoHome(), "telemetry.db");
|
|
42944
|
+
if (!existsSync21(path2)) return null;
|
|
42117
42945
|
} catch {
|
|
42118
42946
|
return null;
|
|
42119
42947
|
}
|
|
@@ -42128,7 +42956,7 @@ function buildRawFileVacuumOpener(entry) {
|
|
|
42128
42956
|
return async (cwd) => {
|
|
42129
42957
|
const path2 = resolveInventoryPath(entry, cwd);
|
|
42130
42958
|
if (!path2) return null;
|
|
42131
|
-
if (!
|
|
42959
|
+
if (!existsSync21(path2)) return null;
|
|
42132
42960
|
try {
|
|
42133
42961
|
const { DatabaseSync: DatabaseSync3 } = await import("node:sqlite");
|
|
42134
42962
|
return new DatabaseSync3(path2, { readOnly: true });
|
|
@@ -42216,10 +43044,10 @@ function snapshotPattern(prefix) {
|
|
|
42216
43044
|
function rotateSnapshots(backupDir, prefix) {
|
|
42217
43045
|
try {
|
|
42218
43046
|
const pattern = snapshotPattern(prefix);
|
|
42219
|
-
const files =
|
|
43047
|
+
const files = readdirSync5(backupDir).filter((f) => pattern.test(f)).map((f) => ({
|
|
42220
43048
|
name: f,
|
|
42221
|
-
path:
|
|
42222
|
-
mtimeMs: statSync7(
|
|
43049
|
+
path: join21(backupDir, f),
|
|
43050
|
+
mtimeMs: statSync7(join21(backupDir, f)).mtimeMs
|
|
42223
43051
|
})).sort((a, b) => a.mtimeMs - b.mtimeMs);
|
|
42224
43052
|
while (files.length >= MAX_SNAPSHOTS) {
|
|
42225
43053
|
const oldest = files.shift();
|
|
@@ -42244,7 +43072,7 @@ async function snapshotOne(target, backupDir, now, cwd) {
|
|
|
42244
43072
|
if (!db) return;
|
|
42245
43073
|
opened = db;
|
|
42246
43074
|
}
|
|
42247
|
-
const dest =
|
|
43075
|
+
const dest = join21(backupDir, `${target.prefix}-${formatTimestamp(now)}.db`);
|
|
42248
43076
|
try {
|
|
42249
43077
|
db.exec("PRAGMA wal_checkpoint(TRUNCATE)");
|
|
42250
43078
|
rotateSnapshots(backupDir, target.prefix);
|
|
@@ -42265,8 +43093,8 @@ async function vacuumIntoBackup(opts = {}) {
|
|
|
42265
43093
|
}
|
|
42266
43094
|
try {
|
|
42267
43095
|
const cleoDir = getCleoDir(opts.cwd);
|
|
42268
|
-
const backupDir =
|
|
42269
|
-
|
|
43096
|
+
const backupDir = join21(cleoDir, "backups", "sqlite");
|
|
43097
|
+
mkdirSync6(backupDir, { recursive: true });
|
|
42270
43098
|
const target = SNAPSHOT_TARGETS.find((t) => t.prefix === prefix);
|
|
42271
43099
|
if (!target) return;
|
|
42272
43100
|
await snapshotOne(target, backupDir, /* @__PURE__ */ new Date(), opts.cwd);
|
|
@@ -42277,13 +43105,13 @@ async function vacuumIntoBackup(opts = {}) {
|
|
|
42277
43105
|
function listSqliteBackupsForPrefix(prefix, cwd) {
|
|
42278
43106
|
try {
|
|
42279
43107
|
const cleoDir = getCleoDir(cwd);
|
|
42280
|
-
const backupDir =
|
|
42281
|
-
if (!
|
|
43108
|
+
const backupDir = join21(cleoDir, "backups", "sqlite");
|
|
43109
|
+
if (!existsSync21(backupDir)) return [];
|
|
42282
43110
|
const pattern = snapshotPattern(prefix);
|
|
42283
|
-
return
|
|
43111
|
+
return readdirSync5(backupDir).filter((f) => pattern.test(f)).map((f) => ({
|
|
42284
43112
|
name: f,
|
|
42285
|
-
path:
|
|
42286
|
-
mtimeMs: statSync7(
|
|
43113
|
+
path: join21(backupDir, f),
|
|
43114
|
+
mtimeMs: statSync7(join21(backupDir, f)).mtimeMs
|
|
42287
43115
|
})).sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
42288
43116
|
} catch {
|
|
42289
43117
|
return [];
|
|
@@ -42339,7 +43167,7 @@ __export(sqlite_native_exports, {
|
|
|
42339
43167
|
import { realpathSync } from "node:fs";
|
|
42340
43168
|
import { createRequire as createRequire8 } from "node:module";
|
|
42341
43169
|
import { tmpdir } from "node:os";
|
|
42342
|
-
import { delimiter, dirname as dirname9, isAbsolute, join as
|
|
43170
|
+
import { delimiter, dirname as dirname9, isAbsolute, join as join22, resolve as resolve4, sep } from "node:path";
|
|
42343
43171
|
function getDbSyncConstructor() {
|
|
42344
43172
|
if (_ctor === null) {
|
|
42345
43173
|
const mod = _require5("node:sqlite");
|
|
@@ -42360,7 +43188,7 @@ function resolveAbsoluteSafe(p) {
|
|
|
42360
43188
|
missingParts.unshift(cursor.slice(parent.length + (parent.endsWith(sep) ? 0 : 1)));
|
|
42361
43189
|
cursor = parent;
|
|
42362
43190
|
try {
|
|
42363
|
-
return
|
|
43191
|
+
return join22(realpathSync(cursor), ...missingParts);
|
|
42364
43192
|
} catch {
|
|
42365
43193
|
}
|
|
42366
43194
|
}
|
|
@@ -42466,7 +43294,7 @@ __export(sqlite_exports, {
|
|
|
42466
43294
|
resolveMigrationsFolder: () => resolveMigrationsFolder,
|
|
42467
43295
|
schema: () => tasks_schema_exports
|
|
42468
43296
|
});
|
|
42469
|
-
import { copyFileSync as copyFileSync6, existsSync as
|
|
43297
|
+
import { copyFileSync as copyFileSync6, existsSync as existsSync22, renameSync as renameSync3, unlinkSync as unlinkSync4 } from "node:fs";
|
|
42470
43298
|
import { createRequire as createRequire9 } from "node:module";
|
|
42471
43299
|
import { eq as eq5 } from "drizzle-orm";
|
|
42472
43300
|
function _getDrizzle3() {
|
|
@@ -42508,7 +43336,7 @@ function countBackupTasks(backupDb) {
|
|
|
42508
43336
|
}
|
|
42509
43337
|
async function autoRecoverFromBackup(nativeDb, dbPath, cwd) {
|
|
42510
43338
|
const { openNativeDatabase: openNativeDatabase2 } = await Promise.resolve().then(() => (init_sqlite_native(), sqlite_native_exports));
|
|
42511
|
-
const
|
|
43339
|
+
const log12 = getLogger("sqlite");
|
|
42512
43340
|
try {
|
|
42513
43341
|
const countResult = nativeDb.prepare("SELECT COUNT(*) as cnt FROM tasks_tasks").get();
|
|
42514
43342
|
const taskCount = countResult?.cnt ?? 0;
|
|
@@ -42534,13 +43362,13 @@ async function autoRecoverFromBackup(nativeDb, dbPath, cwd) {
|
|
|
42534
43362
|
async () => {
|
|
42535
43363
|
const currentTaskCount = await recountTasksFromDisk(dbPath);
|
|
42536
43364
|
if (currentTaskCount > 0) {
|
|
42537
|
-
|
|
43365
|
+
log12.info(
|
|
42538
43366
|
{ dbPath, currentTaskCount },
|
|
42539
43367
|
"Auto-recovery skipped: database was populated by a concurrent process while acquiring the first-open lock (T11662 double-checked re-query)."
|
|
42540
43368
|
);
|
|
42541
43369
|
return;
|
|
42542
43370
|
}
|
|
42543
|
-
|
|
43371
|
+
log12.warn(
|
|
42544
43372
|
{ dbPath, backupPath: newestBackup.path, backupTasks: backupTaskCount },
|
|
42545
43373
|
`Empty database detected with ${backupTaskCount}-task backup available. Auto-recovering from backup. This likely happened because git-tracked WAL/SHM files were overwritten during a branch switch (T5188).`
|
|
42546
43374
|
);
|
|
@@ -42560,7 +43388,7 @@ async function autoRecoverFromBackup(nativeDb, dbPath, cwd) {
|
|
|
42560
43388
|
const tempPath = dbPath + ".recovery-tmp";
|
|
42561
43389
|
copyFileSync6(newestBackup.path, tempPath);
|
|
42562
43390
|
renameSync3(tempPath, dbPath);
|
|
42563
|
-
|
|
43391
|
+
log12.info(
|
|
42564
43392
|
{ dbPath, backupPath: newestBackup.path, restoredTasks: backupTaskCount },
|
|
42565
43393
|
"Database auto-recovered from backup successfully."
|
|
42566
43394
|
);
|
|
@@ -42577,7 +43405,7 @@ async function autoRecoverFromBackup(nativeDb, dbPath, cwd) {
|
|
|
42577
43405
|
{ stale: 6e5, retries: 30 }
|
|
42578
43406
|
);
|
|
42579
43407
|
} catch (err) {
|
|
42580
|
-
|
|
43408
|
+
log12.error({ err, dbPath }, "Auto-recovery from backup failed. Continuing with empty database.");
|
|
42581
43409
|
}
|
|
42582
43410
|
}
|
|
42583
43411
|
async function getDb(cwd) {
|
|
@@ -42586,6 +43414,9 @@ async function getDb(cwd) {
|
|
|
42586
43414
|
if (_db3 && _dbPath3 !== requestedPath) {
|
|
42587
43415
|
resetDbState();
|
|
42588
43416
|
}
|
|
43417
|
+
if (_db3 && (_nativeDb3 === null || !_nativeDb3.isOpen)) {
|
|
43418
|
+
resetDbState();
|
|
43419
|
+
}
|
|
42589
43420
|
if (_db3) return _db3;
|
|
42590
43421
|
if (_initPromise4) return _initPromise4;
|
|
42591
43422
|
_initPromise4 = (async () => {
|
|
@@ -42683,7 +43514,7 @@ async function getSchemaVersion(cwd) {
|
|
|
42683
43514
|
return result[0]?.value ?? null;
|
|
42684
43515
|
}
|
|
42685
43516
|
function dbExists(cwd) {
|
|
42686
|
-
return
|
|
43517
|
+
return existsSync22(resolveDualScopeDbPath("project", cwd));
|
|
42687
43518
|
}
|
|
42688
43519
|
function getNativeDb() {
|
|
42689
43520
|
return _nativeDb3;
|
|
@@ -43913,13 +44744,13 @@ var init_sqlite_data_accessor = __esm({
|
|
|
43913
44744
|
});
|
|
43914
44745
|
|
|
43915
44746
|
// packages/core/src/sequence/index.ts
|
|
43916
|
-
import { existsSync as
|
|
43917
|
-
import { join as
|
|
44747
|
+
import { existsSync as existsSync23, readFileSync as readFileSync6, renameSync as renameSync4 } from "node:fs";
|
|
44748
|
+
import { join as join23 } from "node:path";
|
|
43918
44749
|
function getLegacySequenceJsonPath(cwd) {
|
|
43919
|
-
return
|
|
44750
|
+
return join23(resolveOrCwd(cwd), ".cleo", ".sequence.json");
|
|
43920
44751
|
}
|
|
43921
44752
|
function getLegacySequencePath(cwd) {
|
|
43922
|
-
return
|
|
44753
|
+
return join23(resolveOrCwd(cwd), ".cleo", ".sequence");
|
|
43923
44754
|
}
|
|
43924
44755
|
function isValidSequenceState(value) {
|
|
43925
44756
|
if (!value || typeof value !== "object") return false;
|
|
@@ -43930,7 +44761,7 @@ function isSeedSequence(value) {
|
|
|
43930
44761
|
return value.counter === 0 && value.lastId === "T000" && value.checksum === "seed";
|
|
43931
44762
|
}
|
|
43932
44763
|
function readLegacySequenceFile(path2) {
|
|
43933
|
-
if (!
|
|
44764
|
+
if (!existsSync23(path2)) return null;
|
|
43934
44765
|
try {
|
|
43935
44766
|
const parsed = JSON.parse(readFileSync6(path2, "utf-8"));
|
|
43936
44767
|
return isValidSequenceState(parsed) ? parsed : null;
|
|
@@ -43939,10 +44770,10 @@ function readLegacySequenceFile(path2) {
|
|
|
43939
44770
|
}
|
|
43940
44771
|
}
|
|
43941
44772
|
function renameLegacyFile(path2) {
|
|
43942
|
-
if (!
|
|
44773
|
+
if (!existsSync23(path2)) return;
|
|
43943
44774
|
const migratedPath = `${path2}.migrated`;
|
|
43944
44775
|
try {
|
|
43945
|
-
if (!
|
|
44776
|
+
if (!existsSync23(migratedPath)) {
|
|
43946
44777
|
renameSync4(path2, migratedPath);
|
|
43947
44778
|
return;
|
|
43948
44779
|
}
|
|
@@ -44105,15 +44936,15 @@ var init_sequence = __esm({
|
|
|
44105
44936
|
|
|
44106
44937
|
// packages/core/src/store/git-checkpoint.ts
|
|
44107
44938
|
import { execFile as execFile2 } from "node:child_process";
|
|
44108
|
-
import { existsSync as
|
|
44109
|
-
import { readFile as
|
|
44110
|
-
import { join as
|
|
44939
|
+
import { existsSync as existsSync24 } from "node:fs";
|
|
44940
|
+
import { readFile as readFile4, writeFile } from "node:fs/promises";
|
|
44941
|
+
import { join as join24, resolve as resolve5 } from "node:path";
|
|
44111
44942
|
import { promisify as promisify2 } from "node:util";
|
|
44112
44943
|
function makeCleoGitEnv(cleoDir) {
|
|
44113
44944
|
const abs = resolve5(cleoDir);
|
|
44114
44945
|
return {
|
|
44115
44946
|
...process.env,
|
|
44116
|
-
GIT_DIR:
|
|
44947
|
+
GIT_DIR: join24(abs, ".git"),
|
|
44117
44948
|
GIT_WORK_TREE: abs
|
|
44118
44949
|
};
|
|
44119
44950
|
}
|
|
@@ -44131,7 +44962,7 @@ async function cleoGitCommand(args, cleoDir) {
|
|
|
44131
44962
|
}
|
|
44132
44963
|
}
|
|
44133
44964
|
function isCleoGitInitialized(cleoDir) {
|
|
44134
|
-
return
|
|
44965
|
+
return existsSync24(join24(cleoDir, ".git", "HEAD"));
|
|
44135
44966
|
}
|
|
44136
44967
|
async function loadStateFileAllowlist(cwd) {
|
|
44137
44968
|
try {
|
|
@@ -44175,26 +45006,26 @@ async function isCleoGitRepo(cleoDir) {
|
|
|
44175
45006
|
return result.success && (result.stdout === "true" || isCleoGitInitialized(cleoDir));
|
|
44176
45007
|
}
|
|
44177
45008
|
function isMergeInProgress(cleoDir) {
|
|
44178
|
-
return
|
|
45009
|
+
return existsSync24(join24(cleoDir, ".git", "MERGE_HEAD"));
|
|
44179
45010
|
}
|
|
44180
45011
|
async function isDetachedHead(cleoDir) {
|
|
44181
45012
|
const result = await cleoGitCommand(["symbolic-ref", "HEAD"], cleoDir);
|
|
44182
45013
|
return !result.success;
|
|
44183
45014
|
}
|
|
44184
45015
|
function isRebaseInProgress(cleoDir) {
|
|
44185
|
-
return
|
|
45016
|
+
return existsSync24(join24(cleoDir, ".git", "rebase-merge")) || existsSync24(join24(cleoDir, ".git", "rebase-apply"));
|
|
44186
45017
|
}
|
|
44187
45018
|
async function recordCheckpointTime(cleoDir) {
|
|
44188
45019
|
try {
|
|
44189
|
-
const stateFile =
|
|
45020
|
+
const stateFile = join24(cleoDir, CHECKPOINT_STATE_FILE);
|
|
44190
45021
|
await writeFile(stateFile, String(Math.floor(Date.now() / 1e3)));
|
|
44191
45022
|
} catch {
|
|
44192
45023
|
}
|
|
44193
45024
|
}
|
|
44194
45025
|
async function getLastCheckpointTime(cleoDir) {
|
|
44195
45026
|
try {
|
|
44196
|
-
const stateFile =
|
|
44197
|
-
const content = await
|
|
45027
|
+
const stateFile = join24(cleoDir, CHECKPOINT_STATE_FILE);
|
|
45028
|
+
const content = await readFile4(stateFile, "utf-8");
|
|
44198
45029
|
const epoch = parseInt(content.trim(), 10);
|
|
44199
45030
|
return Number.isNaN(epoch) ? 0 : epoch;
|
|
44200
45031
|
} catch {
|
|
@@ -44205,8 +45036,8 @@ async function getChangedStateFiles(cleoDir, cwd) {
|
|
|
44205
45036
|
const changed = [];
|
|
44206
45037
|
const allStateFiles = await getAllStateFiles(cwd);
|
|
44207
45038
|
for (const stateFile of allStateFiles) {
|
|
44208
|
-
const fullPath =
|
|
44209
|
-
if (!
|
|
45039
|
+
const fullPath = join24(cleoDir, stateFile);
|
|
45040
|
+
if (!existsSync24(fullPath)) continue;
|
|
44210
45041
|
const diffResult = await cleoGitCommand(["diff", "--quiet", "--", stateFile], cleoDir);
|
|
44211
45042
|
const cachedResult = await cleoGitCommand(
|
|
44212
45043
|
["diff", "--cached", "--quiet", "--", stateFile],
|
|
@@ -44233,7 +45064,7 @@ async function shouldCheckpoint(options) {
|
|
|
44233
45064
|
const config = await loadCheckpointConfig(cwd);
|
|
44234
45065
|
if (!config.enabled) return false;
|
|
44235
45066
|
const cleoDir = getCleoDir(cwd);
|
|
44236
|
-
if (!
|
|
45067
|
+
if (!existsSync24(cleoDir)) return false;
|
|
44237
45068
|
if (!isCleoGitInitialized(cleoDir)) return false;
|
|
44238
45069
|
if (!await isCleoGitRepo(cleoDir)) return false;
|
|
44239
45070
|
if (isMergeInProgress(cleoDir)) return false;
|
|
@@ -44311,7 +45142,7 @@ async function ensureSequenceValid(cwd, options) {
|
|
|
44311
45142
|
if (!options?.validateSequence) return;
|
|
44312
45143
|
const check = await checkSequence(cwd);
|
|
44313
45144
|
if (!check.valid) {
|
|
44314
|
-
|
|
45145
|
+
log10.warn({ counter: check.counter, maxId: check.maxIdInData }, "Sequence behind, repairing");
|
|
44315
45146
|
const repair = await repairSequence(cwd);
|
|
44316
45147
|
if (!repair.repaired && options.strict) {
|
|
44317
45148
|
throw new DataSafetyError(`Sequence repair failed: ${repair.message}`, "SEQUENCE_INVALID", {
|
|
@@ -44328,7 +45159,7 @@ async function checkpoint(context, cwd, options) {
|
|
|
44328
45159
|
stats.checkpoints++;
|
|
44329
45160
|
stats.lastCheckpoint = /* @__PURE__ */ new Date();
|
|
44330
45161
|
} catch (err) {
|
|
44331
|
-
|
|
45162
|
+
log10.warn({ err }, "Checkpoint failed (non-fatal)");
|
|
44332
45163
|
}
|
|
44333
45164
|
vacuumIntoBackup({ cwd }).catch(() => {
|
|
44334
45165
|
});
|
|
@@ -44390,7 +45221,7 @@ async function safeAppendLog(accessor, entry, cwd, options) {
|
|
|
44390
45221
|
stats.writes++;
|
|
44391
45222
|
await checkpoint("log entry", cwd, opts);
|
|
44392
45223
|
}
|
|
44393
|
-
var
|
|
45224
|
+
var log10, DataSafetyError, DEFAULT_SAFETY, stats;
|
|
44394
45225
|
var init_data_safety_central = __esm({
|
|
44395
45226
|
"packages/core/src/store/data-safety-central.ts"() {
|
|
44396
45227
|
"use strict";
|
|
@@ -44400,7 +45231,7 @@ var init_data_safety_central = __esm({
|
|
|
44400
45231
|
init_sqlite3();
|
|
44401
45232
|
init_sqlite_backup();
|
|
44402
45233
|
init_tasks_schema();
|
|
44403
|
-
|
|
45234
|
+
log10 = getLogger("data-safety");
|
|
44404
45235
|
DataSafetyError = class extends Error {
|
|
44405
45236
|
constructor(message, code, context) {
|
|
44406
45237
|
super(message);
|
|
@@ -44440,7 +45271,7 @@ function isSafetyDisabled() {
|
|
|
44440
45271
|
}
|
|
44441
45272
|
function wrapWithSafety(accessor, cwd) {
|
|
44442
45273
|
if (isSafetyDisabled()) {
|
|
44443
|
-
|
|
45274
|
+
log11.warn(
|
|
44444
45275
|
"Safety disabled - emergency mode (CLEO_DISABLE_SAFETY=true). Data integrity checks bypassed."
|
|
44445
45276
|
);
|
|
44446
45277
|
return accessor;
|
|
@@ -44461,13 +45292,13 @@ function getSafetyStatus() {
|
|
|
44461
45292
|
enabled: true
|
|
44462
45293
|
};
|
|
44463
45294
|
}
|
|
44464
|
-
var
|
|
45295
|
+
var log11, SafetyDataAccessor;
|
|
44465
45296
|
var init_safety_data_accessor = __esm({
|
|
44466
45297
|
"packages/core/src/store/safety-data-accessor.ts"() {
|
|
44467
45298
|
"use strict";
|
|
44468
45299
|
init_logger2();
|
|
44469
45300
|
init_data_safety_central();
|
|
44470
|
-
|
|
45301
|
+
log11 = getLogger("data-safety");
|
|
44471
45302
|
SafetyDataAccessor = class {
|
|
44472
45303
|
/** The underlying accessor being wrapped. */
|
|
44473
45304
|
inner;
|
|
@@ -44491,7 +45322,7 @@ var init_safety_data_accessor = __esm({
|
|
|
44491
45322
|
...config
|
|
44492
45323
|
};
|
|
44493
45324
|
if (this.config.verbose) {
|
|
44494
|
-
|
|
45325
|
+
log11.debug({ engine: inner.engine }, "SafetyDataAccessor initialized");
|
|
44495
45326
|
}
|
|
44496
45327
|
}
|
|
44497
45328
|
/** The storage engine backing this accessor. */
|
|
@@ -44503,7 +45334,7 @@ var init_safety_data_accessor = __esm({
|
|
|
44503
45334
|
*/
|
|
44504
45335
|
logVerbose(message) {
|
|
44505
45336
|
if (this.config.verbose) {
|
|
44506
|
-
|
|
45337
|
+
log11.debug(message);
|
|
44507
45338
|
}
|
|
44508
45339
|
}
|
|
44509
45340
|
/**
|