@cleocode/core 2026.6.19 → 2026.7.2
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/draft-pr.d.ts +93 -9
- package/dist/selfimprove/draft-pr.d.ts.map +1 -1
- package/dist/selfimprove/draft-pr.js +164 -23
- package/dist/selfimprove/draft-pr.js.map +1 -1
- package/dist/selfimprove/fix-gen.js +1209 -381
- package/dist/selfimprove/fix-gen.js.map +3 -3
- package/dist/selfimprove/run-loop.d.ts +9 -1
- package/dist/selfimprove/run-loop.d.ts.map +1 -1
- package/dist/selfimprove/run-loop.js +7 -1
- package/dist/selfimprove/run-loop.js.map +1 -1
- 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
|
@@ -12443,7 +12443,7 @@ var init_lafs = __esm({
|
|
|
12443
12443
|
|
|
12444
12444
|
// packages/contracts/src/lease-ipc/messages.ts
|
|
12445
12445
|
import { z as z11 } from "zod";
|
|
12446
|
-
var LeaseScopeSchema, LeaseLaneSchema, LeaseAcquireRequestSchema, LeaseReleaseRequestSchema, LeaseRenewRequestSchema, RateCheckRequestSchema, ToolGrantRequestSchema, QueuePriorityClassSchema, QueueAdmitRequestSchema, WorkerHeartbeatRequestSchema, LeaseIpcRequestSchema, LeaseGrantedResponseSchema, LeaseQueuedResponseSchema, LeaseDeniedResponseSchema, RateResultResponseSchema, ToolGrantedResponseSchema, LeaseRevokedResponseSchema, ChildKilledUnresponsiveResponseSchema, QueueAdmitDispositionSchema, QueueAdmitResultResponseSchema, HeartbeatAckResponseSchema, LeaseErrorResponseSchema, LeaseIpcResponseSchema, LeaseIpcRequestEnvelopeSchema, LeaseIpcResponseEnvelopeSchema, LeaseIpcEnvelopeSchema, LEASE_IPC_REQUEST_KINDS, LEASE_IPC_RESPONSE_KINDS, LEASE_IPC_MESSAGE_KINDS;
|
|
12446
|
+
var LeaseScopeSchema, LeaseLaneSchema, LeaseAcquireRequestSchema, LeaseReleaseRequestSchema, LeaseRenewRequestSchema, RateCheckRequestSchema, ToolGrantRequestSchema, QueuePriorityClassSchema, QueueAdmitRequestSchema, WorkerHeartbeatRequestSchema, ResourceAdmitRequestSchema, ResourceReleaseRequestSchema, LeaseIpcRequestSchema, LeaseGrantedResponseSchema, LeaseQueuedResponseSchema, LeaseDeniedResponseSchema, RateResultResponseSchema, ToolGrantedResponseSchema, LeaseRevokedResponseSchema, ChildKilledUnresponsiveResponseSchema, QueueAdmitDispositionSchema, QueueAdmitResultResponseSchema, HeartbeatAckResponseSchema, ResourceAdmitDispositionSchema, ResourceAdmitResultResponseSchema, ResourceReleaseResultResponseSchema, LeaseErrorResponseSchema, LeaseIpcResponseSchema, LeaseIpcRequestEnvelopeSchema, LeaseIpcResponseEnvelopeSchema, LeaseIpcEnvelopeSchema, LEASE_IPC_REQUEST_KINDS, LEASE_IPC_RESPONSE_KINDS, LEASE_IPC_MESSAGE_KINDS;
|
|
12447
12447
|
var init_messages2 = __esm({
|
|
12448
12448
|
"packages/contracts/src/lease-ipc/messages.ts"() {
|
|
12449
12449
|
"use strict";
|
|
@@ -12528,6 +12528,24 @@ var init_messages2 = __esm({
|
|
|
12528
12528
|
/** The worker's own view of whether it is currently inside an LLM call. */
|
|
12529
12529
|
in_flight_llm: z11.boolean()
|
|
12530
12530
|
}).strict();
|
|
12531
|
+
ResourceAdmitRequestSchema = z11.object({
|
|
12532
|
+
/** Tag discriminating this request variant. */
|
|
12533
|
+
kind: z11.literal("resource_admit"),
|
|
12534
|
+
/** The resource class (e.g. `"db-heavy"`, `"full-build"`, `"test-run"`). */
|
|
12535
|
+
class: z11.string().min(1),
|
|
12536
|
+
/** The caller holding the slot — process/worktree-unique; the release key. */
|
|
12537
|
+
holder_id: z11.string().min(1),
|
|
12538
|
+
/** The client-computed budget: the max concurrent holders for this class. */
|
|
12539
|
+
budget: z11.number().int().nonnegative()
|
|
12540
|
+
}).strict();
|
|
12541
|
+
ResourceReleaseRequestSchema = z11.object({
|
|
12542
|
+
/** Tag discriminating this request variant. */
|
|
12543
|
+
kind: z11.literal("resource_release"),
|
|
12544
|
+
/** The resource class the slot belongs to. */
|
|
12545
|
+
class: z11.string().min(1),
|
|
12546
|
+
/** The holder that was admitted (matches the admit `holder_id`). */
|
|
12547
|
+
holder_id: z11.string().min(1)
|
|
12548
|
+
}).strict();
|
|
12531
12549
|
LeaseIpcRequestSchema = z11.discriminatedUnion("kind", [
|
|
12532
12550
|
LeaseAcquireRequestSchema,
|
|
12533
12551
|
LeaseReleaseRequestSchema,
|
|
@@ -12535,7 +12553,9 @@ var init_messages2 = __esm({
|
|
|
12535
12553
|
RateCheckRequestSchema,
|
|
12536
12554
|
ToolGrantRequestSchema,
|
|
12537
12555
|
QueueAdmitRequestSchema,
|
|
12538
|
-
WorkerHeartbeatRequestSchema
|
|
12556
|
+
WorkerHeartbeatRequestSchema,
|
|
12557
|
+
ResourceAdmitRequestSchema,
|
|
12558
|
+
ResourceReleaseRequestSchema
|
|
12539
12559
|
]);
|
|
12540
12560
|
LeaseGrantedResponseSchema = z11.object({
|
|
12541
12561
|
/** Tag discriminating this response variant. */
|
|
@@ -12636,6 +12656,25 @@ var init_messages2 = __esm({
|
|
|
12636
12656
|
/** Tag discriminating this response variant. */
|
|
12637
12657
|
kind: z11.literal("heartbeat_ack")
|
|
12638
12658
|
}).strict();
|
|
12659
|
+
ResourceAdmitDispositionSchema = z11.enum(["admitted", "deferred"]);
|
|
12660
|
+
ResourceAdmitResultResponseSchema = z11.object({
|
|
12661
|
+
/** Tag discriminating this response variant. */
|
|
12662
|
+
kind: z11.literal("resource_admit_result"),
|
|
12663
|
+
/** Whether the heavy op was admitted or deferred. */
|
|
12664
|
+
disposition: ResourceAdmitDispositionSchema,
|
|
12665
|
+
/** Back-off in ms before re-requesting (0 when admitted). */
|
|
12666
|
+
retry_after_ms: z11.number().int().nonnegative(),
|
|
12667
|
+
/** Remaining slots for the class after this decision. */
|
|
12668
|
+
slots_remaining: z11.number().int().nonnegative()
|
|
12669
|
+
}).strict();
|
|
12670
|
+
ResourceReleaseResultResponseSchema = z11.object({
|
|
12671
|
+
/** Tag discriminating this response variant. */
|
|
12672
|
+
kind: z11.literal("resource_release_result"),
|
|
12673
|
+
/** Whether a held slot was actually released (false = no such holder). */
|
|
12674
|
+
released: z11.boolean(),
|
|
12675
|
+
/** Remaining in-flight holders for the class after the release. */
|
|
12676
|
+
slots_remaining: z11.number().int().nonnegative()
|
|
12677
|
+
}).strict();
|
|
12639
12678
|
LeaseErrorResponseSchema = z11.object({
|
|
12640
12679
|
/** Tag discriminating this response variant. */
|
|
12641
12680
|
kind: z11.literal("error"),
|
|
@@ -12654,6 +12693,8 @@ var init_messages2 = __esm({
|
|
|
12654
12693
|
ChildKilledUnresponsiveResponseSchema,
|
|
12655
12694
|
QueueAdmitResultResponseSchema,
|
|
12656
12695
|
HeartbeatAckResponseSchema,
|
|
12696
|
+
ResourceAdmitResultResponseSchema,
|
|
12697
|
+
ResourceReleaseResultResponseSchema,
|
|
12657
12698
|
LeaseErrorResponseSchema
|
|
12658
12699
|
]);
|
|
12659
12700
|
LeaseIpcRequestEnvelopeSchema = z11.object({
|
|
@@ -12687,7 +12728,9 @@ var init_messages2 = __esm({
|
|
|
12687
12728
|
"rate_check",
|
|
12688
12729
|
"tool_grant",
|
|
12689
12730
|
"queue_admit",
|
|
12690
|
-
"worker_heartbeat"
|
|
12731
|
+
"worker_heartbeat",
|
|
12732
|
+
"resource_admit",
|
|
12733
|
+
"resource_release"
|
|
12691
12734
|
];
|
|
12692
12735
|
LEASE_IPC_RESPONSE_KINDS = [
|
|
12693
12736
|
"lease_granted",
|
|
@@ -12699,6 +12742,8 @@ var init_messages2 = __esm({
|
|
|
12699
12742
|
"child_killed_unresponsive",
|
|
12700
12743
|
"queue_admit_result",
|
|
12701
12744
|
"heartbeat_ack",
|
|
12745
|
+
"resource_admit_result",
|
|
12746
|
+
"resource_release_result",
|
|
12702
12747
|
"error"
|
|
12703
12748
|
];
|
|
12704
12749
|
LEASE_IPC_MESSAGE_KINDS = [
|
|
@@ -12709,9 +12754,11 @@ var init_messages2 = __esm({
|
|
|
12709
12754
|
});
|
|
12710
12755
|
|
|
12711
12756
|
// packages/contracts/src/lease-ipc/version.ts
|
|
12757
|
+
var LEASE_IPC_PROTOCOL_VERSION;
|
|
12712
12758
|
var init_version2 = __esm({
|
|
12713
12759
|
"packages/contracts/src/lease-ipc/version.ts"() {
|
|
12714
12760
|
"use strict";
|
|
12761
|
+
LEASE_IPC_PROTOCOL_VERSION = "1.2.0";
|
|
12715
12762
|
}
|
|
12716
12763
|
});
|
|
12717
12764
|
|
|
@@ -13971,6 +14018,25 @@ var init_render = __esm({
|
|
|
13971
14018
|
}
|
|
13972
14019
|
});
|
|
13973
14020
|
|
|
14021
|
+
// packages/contracts/src/resource-governor.ts
|
|
14022
|
+
var RESOURCE_CLASSES, DEFAULT_RESOURCE_RETRY_AFTER_MS;
|
|
14023
|
+
var init_resource_governor = __esm({
|
|
14024
|
+
"packages/contracts/src/resource-governor.ts"() {
|
|
14025
|
+
"use strict";
|
|
14026
|
+
RESOURCE_CLASSES = Object.freeze([
|
|
14027
|
+
"interactive-cli",
|
|
14028
|
+
"agent-session",
|
|
14029
|
+
"llm-call",
|
|
14030
|
+
"test-run",
|
|
14031
|
+
"scoped-build",
|
|
14032
|
+
"full-build",
|
|
14033
|
+
"db-heavy",
|
|
14034
|
+
"background-autonomous"
|
|
14035
|
+
]);
|
|
14036
|
+
DEFAULT_RESOURCE_RETRY_AFTER_MS = 2e3;
|
|
14037
|
+
}
|
|
14038
|
+
});
|
|
14039
|
+
|
|
13974
14040
|
// packages/contracts/src/session.ts
|
|
13975
14041
|
var init_session2 = __esm({
|
|
13976
14042
|
"packages/contracts/src/session.ts"() {
|
|
@@ -15721,6 +15787,7 @@ var init_src = __esm({
|
|
|
15721
15787
|
init_evidence_atoms();
|
|
15722
15788
|
init_plan();
|
|
15723
15789
|
init_render();
|
|
15790
|
+
init_resource_governor();
|
|
15724
15791
|
init_session2();
|
|
15725
15792
|
init_session_journal();
|
|
15726
15793
|
init_status_registry();
|
|
@@ -17656,7 +17723,7 @@ var init_sql = __esm({
|
|
|
17656
17723
|
return new SQL([new StringChunk(str)]);
|
|
17657
17724
|
}
|
|
17658
17725
|
_sql.raw = raw;
|
|
17659
|
-
function
|
|
17726
|
+
function join31(chunks, separator) {
|
|
17660
17727
|
const result = [];
|
|
17661
17728
|
for (const [i, chunk] of chunks.entries()) {
|
|
17662
17729
|
if (i > 0 && separator !== void 0) result.push(separator);
|
|
@@ -17664,7 +17731,7 @@ var init_sql = __esm({
|
|
|
17664
17731
|
}
|
|
17665
17732
|
return new SQL(result);
|
|
17666
17733
|
}
|
|
17667
|
-
_sql.join =
|
|
17734
|
+
_sql.join = join31;
|
|
17668
17735
|
function identifier(value) {
|
|
17669
17736
|
return new Name(value);
|
|
17670
17737
|
}
|
|
@@ -20051,7 +20118,7 @@ var init_select2 = __esm({
|
|
|
20051
20118
|
const baseTableName = this.tableName;
|
|
20052
20119
|
const tableName = getTableLikeName(table);
|
|
20053
20120
|
for (const item of extractUsedTable(table)) this.usedTables.add(item);
|
|
20054
|
-
if (typeof tableName === "string" && this.config.joins?.some((
|
|
20121
|
+
if (typeof tableName === "string" && this.config.joins?.some((join31) => join31.alias === tableName)) throw new Error(`Alias "${tableName}" is already used in this query`);
|
|
20055
20122
|
if (!this.isPartialSelect) {
|
|
20056
20123
|
if (Object.keys(this.joinsNotNullableMap).length === 1 && typeof baseTableName === "string") this.config.fields = { [baseTableName]: this.config.fields };
|
|
20057
20124
|
if (typeof tableName === "string" && !is(table, SQL)) {
|
|
@@ -23861,7 +23928,7 @@ var init_dialect = __esm({
|
|
|
23861
23928
|
if (!joins2) return;
|
|
23862
23929
|
const withEntries = Object.entries(joins2).filter(([_, v]) => v);
|
|
23863
23930
|
if (!withEntries.length) return;
|
|
23864
|
-
return sql.join(withEntries.map(([k,
|
|
23931
|
+
return sql.join(withEntries.map(([k, join31]) => {
|
|
23865
23932
|
const relation = tableConfig.relations[k];
|
|
23866
23933
|
const isSingle2 = is(relation, One);
|
|
23867
23934
|
const targetTable = aliasedTable(relation.targetTable, `d${currentDepth + 1}`);
|
|
@@ -23872,7 +23939,7 @@ var init_dialect = __esm({
|
|
|
23872
23939
|
table: targetTable,
|
|
23873
23940
|
mode: isSingle2 ? "first" : "many",
|
|
23874
23941
|
schema,
|
|
23875
|
-
queryConfig:
|
|
23942
|
+
queryConfig: join31,
|
|
23876
23943
|
tableConfig: schema[relation.targetTableName],
|
|
23877
23944
|
relationWhere: filter2,
|
|
23878
23945
|
isNested: true,
|
|
@@ -23886,7 +23953,7 @@ var init_dialect = __esm({
|
|
|
23886
23953
|
key: k,
|
|
23887
23954
|
selection: innerQuery.selection,
|
|
23888
23955
|
isArray: !isSingle2,
|
|
23889
|
-
isOptional: (relation.optional ?? false) ||
|
|
23956
|
+
isOptional: (relation.optional ?? false) || join31 !== true && !!join31.where
|
|
23890
23957
|
});
|
|
23891
23958
|
const jsonColumns = sql.join(innerQuery.selection.map((s) => {
|
|
23892
23959
|
return sql`${sql.raw(this.escapeString(s.key))}, ${s.selection ? sql`${jsonb3}(${sql.identifier(s.key)})` : sql.identifier(s.key)}`;
|
|
@@ -24707,7 +24774,7 @@ var init_update = __esm({
|
|
|
24707
24774
|
createJoin(joinType) {
|
|
24708
24775
|
return ((table, on) => {
|
|
24709
24776
|
const tableName = getTableLikeName(table);
|
|
24710
|
-
if (typeof tableName === "string" && this.config.joins.some((
|
|
24777
|
+
if (typeof tableName === "string" && this.config.joins.some((join31) => join31.alias === tableName)) throw new Error(`Alias "${tableName}" is already used in this query`);
|
|
24711
24778
|
if (typeof on === "function") {
|
|
24712
24779
|
const from = this.config.from ? is(table, SQLiteTable) ? table[Table.Symbol.Columns] : is(table, Subquery) ? table._.selectedFields : is(table, SQLiteViewBase) ? table[ViewBaseConfig].selectedFields : void 0 : void 0;
|
|
24713
24780
|
on = on(new Proxy(this.config.table[Table.Symbol.Columns], new SelectionProxyHandler({
|
|
@@ -25805,8 +25872,8 @@ function probeAndMarkApplied(nativeDb, migration, logSubsystem, eliminatedTables
|
|
|
25805
25872
|
});
|
|
25806
25873
|
if (allAltersPresent && allTablesPresent && allIndexesPresent && allTriggersPresent) {
|
|
25807
25874
|
insertJournalEntry(nativeDb, migration.hash, migration.folderMillis, migration.name ?? "");
|
|
25808
|
-
const
|
|
25809
|
-
|
|
25875
|
+
const log9 = getLogger(logSubsystem);
|
|
25876
|
+
log9.debug(
|
|
25810
25877
|
{
|
|
25811
25878
|
migration: migration.name,
|
|
25812
25879
|
alters: alterTargets.length,
|
|
@@ -25875,14 +25942,14 @@ function reconcileJournal(nativeDb, migrationsFolder, existenceTable2, logSubsys
|
|
|
25875
25942
|
if (hasOrphanedEntries) {
|
|
25876
25943
|
const dbHashes = new Set(dbEntries.map((e) => e.hash));
|
|
25877
25944
|
const allLocalHashesPresentInDb = localMigrations.every((m) => dbHashes.has(m.hash));
|
|
25878
|
-
const
|
|
25945
|
+
const log9 = getLogger(logSubsystem);
|
|
25879
25946
|
if (allLocalHashesPresentInDb) {
|
|
25880
|
-
|
|
25947
|
+
log9.debug(
|
|
25881
25948
|
{ extra: orphanedEntries.length },
|
|
25882
25949
|
`Migration journal has ${orphanedEntries.length} entries for migrations not known to this install (DB is ahead). Skipping reconciliation.`
|
|
25883
25950
|
);
|
|
25884
25951
|
} else {
|
|
25885
|
-
|
|
25952
|
+
log9.warn(
|
|
25886
25953
|
{ orphaned: orphanedEntries.length },
|
|
25887
25954
|
`Detected ${orphanedEntries.length} true-orphan journal entries from a previous CLEO lineage. Reconciling via DDL probe.`
|
|
25888
25955
|
);
|
|
@@ -25918,8 +25985,8 @@ function reconcileJournal(nativeDb, migrationsFolder, existenceTable2, logSubsys
|
|
|
25918
25985
|
if (alterMatches.length === 0) {
|
|
25919
25986
|
const stripped = stripSqlComments(fullSql).trim();
|
|
25920
25987
|
if (stripped === "") {
|
|
25921
|
-
const
|
|
25922
|
-
|
|
25988
|
+
const log9 = getLogger(logSubsystem);
|
|
25989
|
+
log9.debug(
|
|
25923
25990
|
{ migration: migration.name },
|
|
25924
25991
|
`Migration ${migration.name} is a comment-only baseline marker \u2014 marking applied on existing DB.`
|
|
25925
25992
|
);
|
|
@@ -25958,8 +26025,8 @@ function reconcileJournal(nativeDb, migrationsFolder, existenceTable2, logSubsys
|
|
|
25958
26025
|
}
|
|
25959
26026
|
}
|
|
25960
26027
|
if (missingColumns.length === 0) {
|
|
25961
|
-
const
|
|
25962
|
-
|
|
26028
|
+
const log9 = getLogger(logSubsystem);
|
|
26029
|
+
log9.warn(
|
|
25963
26030
|
{ migration: migration.name, columns: alterMatches },
|
|
25964
26031
|
`Detected partially-applied migration ${migration.name} \u2014 columns exist but journal entry missing. Auto-reconciling.`
|
|
25965
26032
|
);
|
|
@@ -25967,8 +26034,8 @@ function reconcileJournal(nativeDb, migrationsFolder, existenceTable2, logSubsys
|
|
|
25967
26034
|
continue;
|
|
25968
26035
|
}
|
|
25969
26036
|
if (existingColumns.length > 0 && missingColumns.length > 0) {
|
|
25970
|
-
const
|
|
25971
|
-
|
|
26037
|
+
const log9 = getLogger(logSubsystem);
|
|
26038
|
+
log9.warn(
|
|
25972
26039
|
{
|
|
25973
26040
|
migration: migration.name,
|
|
25974
26041
|
existingColumns: existingColumns.map((c) => `${c.table}.${c.column}`),
|
|
@@ -25980,12 +26047,12 @@ function reconcileJournal(nativeDb, migrationsFolder, existenceTable2, logSubsys
|
|
|
25980
26047
|
if (!tableExists(nativeDb, table)) continue;
|
|
25981
26048
|
try {
|
|
25982
26049
|
nativeDb.exec(`ALTER TABLE ${table} ADD COLUMN ${column}${ddl ? ` ${ddl}` : ""}`);
|
|
25983
|
-
|
|
26050
|
+
log9.warn(
|
|
25984
26051
|
{ migration: migration.name, table, column },
|
|
25985
26052
|
`T920: Added missing column ${table}.${column} to complete partial migration.`
|
|
25986
26053
|
);
|
|
25987
26054
|
} catch {
|
|
25988
|
-
|
|
26055
|
+
log9.warn(
|
|
25989
26056
|
{ migration: migration.name, table, column },
|
|
25990
26057
|
`T920: Could not add missing column ${table}.${column} \u2014 will let Drizzle migrate() handle it.`
|
|
25991
26058
|
);
|
|
@@ -26005,8 +26072,8 @@ function reconcileJournal(nativeDb, migrationsFolder, existenceTable2, logSubsys
|
|
|
26005
26072
|
for (const entry of unnamedEntries) {
|
|
26006
26073
|
const migrationName = hashToName.get(entry.hash);
|
|
26007
26074
|
if (!migrationName) continue;
|
|
26008
|
-
const
|
|
26009
|
-
|
|
26075
|
+
const log9 = getLogger(logSubsystem);
|
|
26076
|
+
log9.debug(
|
|
26010
26077
|
{ id: entry.id, hash: entry.hash, name: migrationName },
|
|
26011
26078
|
`Backfilling missing name on journal entry id=${entry.id} (Drizzle v1 beta legacy compat).`
|
|
26012
26079
|
);
|
|
@@ -26090,13 +26157,13 @@ function ensureColumns(nativeDb, tableName, requiredColumns, logSubsystem, conte
|
|
|
26090
26157
|
const existingCols = new Set(columns.map((c) => c.name));
|
|
26091
26158
|
for (const req of requiredColumns) {
|
|
26092
26159
|
if (!existingCols.has(req.name)) {
|
|
26093
|
-
const
|
|
26160
|
+
const log9 = getLogger(logSubsystem);
|
|
26094
26161
|
const message = `Adding missing column ${tableName}.${req.name} via ALTER TABLE`;
|
|
26095
26162
|
const fields = { column: req.name, context };
|
|
26096
26163
|
if (context === "fresh") {
|
|
26097
|
-
|
|
26164
|
+
log9.error(fields, `${message} \u2014 MIGRATION DEFECT (fresh DB should not need repair)`);
|
|
26098
26165
|
} else {
|
|
26099
|
-
|
|
26166
|
+
log9.warn(fields, message);
|
|
26100
26167
|
}
|
|
26101
26168
|
nativeDb.exec(`ALTER TABLE ${tableName} ADD COLUMN ${req.name} ${req.ddl}`);
|
|
26102
26169
|
}
|
|
@@ -35177,11 +35244,750 @@ var init_cleo_global = __esm({
|
|
|
35177
35244
|
}
|
|
35178
35245
|
});
|
|
35179
35246
|
|
|
35180
|
-
// packages/core/src/
|
|
35247
|
+
// packages/core/src/resources/linux-backend.ts
|
|
35248
|
+
import { readFile as readFile2, stat } from "node:fs/promises";
|
|
35249
|
+
function parsePressureLine(line2) {
|
|
35250
|
+
const avg10Match = /avg10=([\d.]+)/.exec(line2);
|
|
35251
|
+
const avg60Match = /avg60=([\d.]+)/.exec(line2);
|
|
35252
|
+
const avg300Match = /avg300=([\d.]+)/.exec(line2);
|
|
35253
|
+
const totalMatch = /total=(\d+)/.exec(line2);
|
|
35254
|
+
if (!avg10Match?.[1] || !avg60Match?.[1] || !avg300Match?.[1] || !totalMatch?.[1]) {
|
|
35255
|
+
return null;
|
|
35256
|
+
}
|
|
35257
|
+
return {
|
|
35258
|
+
avg10: parseFloat(avg10Match[1]),
|
|
35259
|
+
avg60: parseFloat(avg60Match[1]),
|
|
35260
|
+
avg300: parseFloat(avg300Match[1]),
|
|
35261
|
+
totalUs: parseInt(totalMatch[1], 10)
|
|
35262
|
+
};
|
|
35263
|
+
}
|
|
35264
|
+
function parsePsiFile(content) {
|
|
35265
|
+
const lines = content.split("\n").map((l) => l.trim()).filter(Boolean);
|
|
35266
|
+
let some = null;
|
|
35267
|
+
let full = null;
|
|
35268
|
+
for (const line2 of lines) {
|
|
35269
|
+
if (line2.startsWith("some ")) {
|
|
35270
|
+
some = parsePressureLine(line2);
|
|
35271
|
+
} else if (line2.startsWith("full ")) {
|
|
35272
|
+
full = parsePressureLine(line2);
|
|
35273
|
+
}
|
|
35274
|
+
}
|
|
35275
|
+
if (!some) {
|
|
35276
|
+
return null;
|
|
35277
|
+
}
|
|
35278
|
+
return { some, full };
|
|
35279
|
+
}
|
|
35280
|
+
function parseMemAvailable(content) {
|
|
35281
|
+
for (const line2 of content.split("\n")) {
|
|
35282
|
+
if (line2.startsWith("MemAvailable:")) {
|
|
35283
|
+
const match = /MemAvailable:\s+(\d+)\s+kB/.exec(line2);
|
|
35284
|
+
if (match?.[1]) {
|
|
35285
|
+
return parseInt(match[1], 10) * 1024;
|
|
35286
|
+
}
|
|
35287
|
+
}
|
|
35288
|
+
}
|
|
35289
|
+
return null;
|
|
35290
|
+
}
|
|
35291
|
+
function parseSmapsRollup(content) {
|
|
35292
|
+
let rssKb = null;
|
|
35293
|
+
let pssKb = null;
|
|
35294
|
+
for (const line2 of content.split("\n")) {
|
|
35295
|
+
if (line2.startsWith("Rss:")) {
|
|
35296
|
+
const m = /Rss:\s+(\d+)\s+kB/.exec(line2);
|
|
35297
|
+
if (m?.[1]) rssKb = parseInt(m[1], 10);
|
|
35298
|
+
} else if (line2.startsWith("Pss:")) {
|
|
35299
|
+
const m = /Pss:\s+(\d+)\s+kB/.exec(line2);
|
|
35300
|
+
if (m?.[1]) pssKb = parseInt(m[1], 10);
|
|
35301
|
+
}
|
|
35302
|
+
}
|
|
35303
|
+
if (rssKb === null || pssKb === null) return null;
|
|
35304
|
+
return { rssBytes: rssKb * 1024, pssBytes: pssKb * 1024 };
|
|
35305
|
+
}
|
|
35306
|
+
var defaultReadFile, defaultStat, LinuxResourceBackend;
|
|
35307
|
+
var init_linux_backend = __esm({
|
|
35308
|
+
"packages/core/src/resources/linux-backend.ts"() {
|
|
35309
|
+
"use strict";
|
|
35310
|
+
defaultReadFile = (path, encoding) => readFile2(path, encoding);
|
|
35311
|
+
defaultStat = async (path) => {
|
|
35312
|
+
try {
|
|
35313
|
+
const s = await stat(path);
|
|
35314
|
+
return { size: s.size };
|
|
35315
|
+
} catch {
|
|
35316
|
+
return null;
|
|
35317
|
+
}
|
|
35318
|
+
};
|
|
35319
|
+
LinuxResourceBackend = class {
|
|
35320
|
+
globalPressurePath;
|
|
35321
|
+
cgroupSlicePressurePath;
|
|
35322
|
+
walPaths;
|
|
35323
|
+
readFileFn;
|
|
35324
|
+
statFileFn;
|
|
35325
|
+
constructor(opts = {}) {
|
|
35326
|
+
this.globalPressurePath = opts.globalPressurePath ?? "/proc/pressure/memory";
|
|
35327
|
+
this.cgroupSlicePressurePath = opts.cgroupSlicePressurePath ?? null;
|
|
35328
|
+
this.walPaths = opts.walPaths ?? [];
|
|
35329
|
+
this.readFileFn = opts.readFileFn ?? defaultReadFile;
|
|
35330
|
+
this.statFileFn = opts.statFileFn ?? defaultStat;
|
|
35331
|
+
}
|
|
35332
|
+
/**
|
|
35333
|
+
* Take a bounded point-in-time resource sample.
|
|
35334
|
+
*
|
|
35335
|
+
* Read-count (bounded by Amendment 1):
|
|
35336
|
+
* - 1 read: `/proc/pressure/memory`
|
|
35337
|
+
* - 0–1 reads: slice `memory.pressure` (if configured)
|
|
35338
|
+
* - 1 read: `/proc/meminfo`
|
|
35339
|
+
* - N stat calls: one per WAL path in `walPaths`
|
|
35340
|
+
*
|
|
35341
|
+
* Never spawns a child process.
|
|
35342
|
+
*/
|
|
35343
|
+
async sample() {
|
|
35344
|
+
const sampledAtMs = Date.now();
|
|
35345
|
+
const globalPressure = await this._readPsi(this.globalPressurePath);
|
|
35346
|
+
const pressureAvailable = globalPressure !== null;
|
|
35347
|
+
let slicePressure = null;
|
|
35348
|
+
if (this.cgroupSlicePressurePath) {
|
|
35349
|
+
slicePressure = await this._readPsi(this.cgroupSlicePressurePath);
|
|
35350
|
+
}
|
|
35351
|
+
const memAvailableBytes = await this._readMemAvailable();
|
|
35352
|
+
const walObservations = await this._readWalObservations();
|
|
35353
|
+
return {
|
|
35354
|
+
sampledAtMs,
|
|
35355
|
+
pressureAvailable,
|
|
35356
|
+
memAvailableBytes,
|
|
35357
|
+
globalPressure,
|
|
35358
|
+
slicePressure,
|
|
35359
|
+
walObservations
|
|
35360
|
+
};
|
|
35361
|
+
}
|
|
35362
|
+
/**
|
|
35363
|
+
* Sweep per-child RSS via `/proc/<pid>/smaps_rollup`.
|
|
35364
|
+
*
|
|
35365
|
+
* **STRUCTURALLY SEPARATED from sample()** — smaps_rollup reads are
|
|
35366
|
+
* ms-scale per multi-GB process and must never appear in the hot path.
|
|
35367
|
+
* Call on a separate low-frequency cadence (e.g. every 60s or on
|
|
35368
|
+
* backoff→hold state transition).
|
|
35369
|
+
*
|
|
35370
|
+
* @param pids - PIDs to sweep. Dead/unknown PIDs are silently skipped.
|
|
35371
|
+
*/
|
|
35372
|
+
async sweepChildRss(pids) {
|
|
35373
|
+
const sampledAtMs = Date.now();
|
|
35374
|
+
const entries = [];
|
|
35375
|
+
for (const pid of pids) {
|
|
35376
|
+
try {
|
|
35377
|
+
const content = await this.readFileFn(`/proc/${pid}/smaps_rollup`, "utf-8");
|
|
35378
|
+
const parsed = parseSmapsRollup(content);
|
|
35379
|
+
if (parsed) {
|
|
35380
|
+
entries.push({ pid, ...parsed });
|
|
35381
|
+
}
|
|
35382
|
+
} catch {
|
|
35383
|
+
}
|
|
35384
|
+
}
|
|
35385
|
+
return { sampledAtMs, entries };
|
|
35386
|
+
}
|
|
35387
|
+
// -------------------------------------------------------------------------
|
|
35388
|
+
// Private helpers
|
|
35389
|
+
// -------------------------------------------------------------------------
|
|
35390
|
+
async _readPsi(path) {
|
|
35391
|
+
try {
|
|
35392
|
+
const content = await this.readFileFn(path, "utf-8");
|
|
35393
|
+
return parsePsiFile(content);
|
|
35394
|
+
} catch {
|
|
35395
|
+
return null;
|
|
35396
|
+
}
|
|
35397
|
+
}
|
|
35398
|
+
async _readMemAvailable() {
|
|
35399
|
+
try {
|
|
35400
|
+
const content = await this.readFileFn("/proc/meminfo", "utf-8");
|
|
35401
|
+
return parseMemAvailable(content);
|
|
35402
|
+
} catch {
|
|
35403
|
+
return null;
|
|
35404
|
+
}
|
|
35405
|
+
}
|
|
35406
|
+
async _readWalObservations() {
|
|
35407
|
+
const results = [];
|
|
35408
|
+
for (const walPath of this.walPaths) {
|
|
35409
|
+
const info = await this.statFileFn(walPath);
|
|
35410
|
+
results.push({ walPath, sizeBytes: info?.size ?? null });
|
|
35411
|
+
}
|
|
35412
|
+
return results;
|
|
35413
|
+
}
|
|
35414
|
+
};
|
|
35415
|
+
}
|
|
35416
|
+
});
|
|
35417
|
+
|
|
35418
|
+
// packages/core/src/resources/monitor.ts
|
|
35419
|
+
import { EventEmitter as EventEmitter2 } from "node:events";
|
|
35420
|
+
function resolveThresholds(cfg) {
|
|
35421
|
+
return {
|
|
35422
|
+
holdSomeAvg10: cfg.psi?.holdSomeAvg10 ?? DEFAULTS.holdSomeAvg10,
|
|
35423
|
+
backoffSomeAvg10: cfg.psi?.backoffSomeAvg10 ?? DEFAULTS.backoffSomeAvg10,
|
|
35424
|
+
holdFullAvg10: cfg.psi?.holdFullAvg10 ?? DEFAULTS.holdFullAvg10,
|
|
35425
|
+
backoffFullAvg10: cfg.psi?.backoffFullAvg10 ?? DEFAULTS.backoffFullAvg10,
|
|
35426
|
+
hysteresisPoints: cfg.hysteresisPoints ?? DEFAULTS.hysteresisPoints,
|
|
35427
|
+
headroomBytes: cfg.headroomBytes ?? DEFAULTS.headroomBytes,
|
|
35428
|
+
walWarnThresholdBytes: cfg.walWarnThresholdBytes ?? DEFAULTS.walWarnThresholdBytes,
|
|
35429
|
+
pollIntervalMs: cfg.pollIntervalMs ?? DEFAULTS.pollIntervalMs
|
|
35430
|
+
};
|
|
35431
|
+
}
|
|
35432
|
+
function evaluateState(sample, thresholds, currentState) {
|
|
35433
|
+
if (!sample.pressureAvailable) {
|
|
35434
|
+
if (sample.memAvailableBytes !== null && sample.memAvailableBytes < thresholds.headroomBytes) {
|
|
35435
|
+
return { state: "hold", reason: "degraded: memAvailable below headroom" };
|
|
35436
|
+
}
|
|
35437
|
+
return { state: "ok", reason: "degraded: PSI unavailable, memAvailable sufficient" };
|
|
35438
|
+
}
|
|
35439
|
+
const somePressure = sample.globalPressure?.some ?? sample.slicePressure?.some;
|
|
35440
|
+
const fullPressure = sample.globalPressure?.full ?? sample.slicePressure?.full;
|
|
35441
|
+
const someAvg102 = somePressure?.avg10 ?? 0;
|
|
35442
|
+
const fullAvg10 = fullPressure?.avg10 ?? 0;
|
|
35443
|
+
if (someAvg102 > thresholds.backoffSomeAvg10 || fullAvg10 > thresholds.backoffFullAvg10) {
|
|
35444
|
+
return {
|
|
35445
|
+
state: "backoff",
|
|
35446
|
+
reason: `some avg10=${someAvg102.toFixed(2)} full avg10=${fullAvg10.toFixed(2)} \u2014 above backoff threshold`
|
|
35447
|
+
};
|
|
35448
|
+
}
|
|
35449
|
+
if (someAvg102 > thresholds.holdSomeAvg10 || fullAvg10 > thresholds.holdFullAvg10) {
|
|
35450
|
+
return {
|
|
35451
|
+
state: "hold",
|
|
35452
|
+
reason: `some avg10=${someAvg102.toFixed(2)} full avg10=${fullAvg10.toFixed(2)} \u2014 above hold threshold`
|
|
35453
|
+
};
|
|
35454
|
+
}
|
|
35455
|
+
if (currentState !== "ok") {
|
|
35456
|
+
const hysteresisFloor = thresholds.holdSomeAvg10 - thresholds.hysteresisPoints;
|
|
35457
|
+
const fullHysteresisFloor = thresholds.holdFullAvg10 - thresholds.hysteresisPoints;
|
|
35458
|
+
if (someAvg102 > hysteresisFloor || fullAvg10 > fullHysteresisFloor) {
|
|
35459
|
+
const clamped = currentState === "backoff" ? "hold" : currentState;
|
|
35460
|
+
return {
|
|
35461
|
+
state: clamped,
|
|
35462
|
+
reason: `some avg10=${someAvg102.toFixed(2)} full avg10=${fullAvg10.toFixed(2)} \u2014 hysteresis hold (floor: some ${hysteresisFloor}, full ${fullHysteresisFloor})`
|
|
35463
|
+
};
|
|
35464
|
+
}
|
|
35465
|
+
}
|
|
35466
|
+
return {
|
|
35467
|
+
state: "ok",
|
|
35468
|
+
reason: `some avg10=${someAvg102.toFixed(2)} full avg10=${fullAvg10.toFixed(2)} \u2014 below thresholds`
|
|
35469
|
+
};
|
|
35470
|
+
}
|
|
35471
|
+
var DEFAULTS, ResourceMonitor;
|
|
35472
|
+
var init_monitor = __esm({
|
|
35473
|
+
"packages/core/src/resources/monitor.ts"() {
|
|
35474
|
+
"use strict";
|
|
35475
|
+
init_linux_backend();
|
|
35476
|
+
DEFAULTS = {
|
|
35477
|
+
holdSomeAvg10: 10,
|
|
35478
|
+
backoffSomeAvg10: 20,
|
|
35479
|
+
holdFullAvg10: 5,
|
|
35480
|
+
backoffFullAvg10: 10,
|
|
35481
|
+
hysteresisPoints: 3,
|
|
35482
|
+
headroomBytes: 256 * 1024 * 1024,
|
|
35483
|
+
walWarnThresholdBytes: 256 * 1024 * 1024,
|
|
35484
|
+
pollIntervalMs: 1500
|
|
35485
|
+
};
|
|
35486
|
+
ResourceMonitor = class extends EventEmitter2 {
|
|
35487
|
+
thresholds;
|
|
35488
|
+
backend;
|
|
35489
|
+
_state = "ok";
|
|
35490
|
+
_timer = null;
|
|
35491
|
+
_running = false;
|
|
35492
|
+
constructor(cfg = {}) {
|
|
35493
|
+
super();
|
|
35494
|
+
this.thresholds = resolveThresholds(cfg);
|
|
35495
|
+
this.backend = cfg.backend ?? new LinuxResourceBackend();
|
|
35496
|
+
}
|
|
35497
|
+
// -------------------------------------------------------------------------
|
|
35498
|
+
// Public API
|
|
35499
|
+
// -------------------------------------------------------------------------
|
|
35500
|
+
/**
|
|
35501
|
+
* Current pressure state (only meaningful after at least one sample).
|
|
35502
|
+
*/
|
|
35503
|
+
get state() {
|
|
35504
|
+
return this._state;
|
|
35505
|
+
}
|
|
35506
|
+
/**
|
|
35507
|
+
* `true` while the continuous loop is running.
|
|
35508
|
+
*/
|
|
35509
|
+
get running() {
|
|
35510
|
+
return this._running;
|
|
35511
|
+
}
|
|
35512
|
+
/**
|
|
35513
|
+
* Take a single point-in-time resource sample.
|
|
35514
|
+
*
|
|
35515
|
+
* Safe to call with the daemon off (no child processes, bounded reads).
|
|
35516
|
+
* Does NOT update `this.state` — use {@link startContinuous} for that.
|
|
35517
|
+
*/
|
|
35518
|
+
async sample() {
|
|
35519
|
+
return this.backend.sample();
|
|
35520
|
+
}
|
|
35521
|
+
/**
|
|
35522
|
+
* Start the continuous polling loop.
|
|
35523
|
+
*
|
|
35524
|
+
* Returns a `stop()` function. Calling `stop()` is idempotent.
|
|
35525
|
+
*
|
|
35526
|
+
* Emits:
|
|
35527
|
+
* - `'sample'` on every poll (even if state did not change)
|
|
35528
|
+
* - `'transition'` when `PressureState` changes
|
|
35529
|
+
* - `'error'` if the backend throws (loop continues regardless)
|
|
35530
|
+
*/
|
|
35531
|
+
startContinuous() {
|
|
35532
|
+
if (this._running) {
|
|
35533
|
+
return () => this._stopLoop();
|
|
35534
|
+
}
|
|
35535
|
+
this._running = true;
|
|
35536
|
+
this._scheduleNext();
|
|
35537
|
+
return () => this._stopLoop();
|
|
35538
|
+
}
|
|
35539
|
+
/**
|
|
35540
|
+
* Stop the continuous polling loop. Idempotent.
|
|
35541
|
+
*/
|
|
35542
|
+
stop() {
|
|
35543
|
+
this._stopLoop();
|
|
35544
|
+
}
|
|
35545
|
+
// -------------------------------------------------------------------------
|
|
35546
|
+
// Private loop machinery
|
|
35547
|
+
// -------------------------------------------------------------------------
|
|
35548
|
+
_scheduleNext() {
|
|
35549
|
+
this._timer = setTimeout(() => {
|
|
35550
|
+
void this._tick();
|
|
35551
|
+
}, this.thresholds.pollIntervalMs);
|
|
35552
|
+
if (typeof this._timer.unref === "function") {
|
|
35553
|
+
this._timer.unref();
|
|
35554
|
+
}
|
|
35555
|
+
}
|
|
35556
|
+
async _tick() {
|
|
35557
|
+
if (!this._running) return;
|
|
35558
|
+
try {
|
|
35559
|
+
const sample = await this.backend.sample();
|
|
35560
|
+
this.emit("sample", sample);
|
|
35561
|
+
const { state: newState, reason } = evaluateState(sample, this.thresholds, this._state);
|
|
35562
|
+
if (newState !== this._state) {
|
|
35563
|
+
const transition = {
|
|
35564
|
+
from: this._state,
|
|
35565
|
+
to: newState,
|
|
35566
|
+
sample,
|
|
35567
|
+
reason
|
|
35568
|
+
};
|
|
35569
|
+
this._state = newState;
|
|
35570
|
+
this.emit("transition", transition);
|
|
35571
|
+
}
|
|
35572
|
+
} catch (err) {
|
|
35573
|
+
this.emit("error", err instanceof Error ? err : new Error(String(err)));
|
|
35574
|
+
}
|
|
35575
|
+
if (this._running) {
|
|
35576
|
+
this._scheduleNext();
|
|
35577
|
+
}
|
|
35578
|
+
}
|
|
35579
|
+
_stopLoop() {
|
|
35580
|
+
this._running = false;
|
|
35581
|
+
if (this._timer !== null) {
|
|
35582
|
+
clearTimeout(this._timer);
|
|
35583
|
+
this._timer = null;
|
|
35584
|
+
}
|
|
35585
|
+
}
|
|
35586
|
+
};
|
|
35587
|
+
}
|
|
35588
|
+
});
|
|
35589
|
+
|
|
35590
|
+
// packages/core/src/resources/supervisor-admit.ts
|
|
35591
|
+
import { connect } from "node:net";
|
|
35592
|
+
import { join as join5 } from "node:path";
|
|
35593
|
+
import { getCleoHome as getCleoHome2 } from "@cleocode/paths";
|
|
35594
|
+
function resolveSupervisorSocketPath() {
|
|
35595
|
+
return process.env.CLEO_SUPERVISOR_SOCKET ?? join5(getCleoHome2(), SUPERVISOR_SOCKET_NAME);
|
|
35596
|
+
}
|
|
35597
|
+
async function sendResourceAdmit(socketPath, cls, holderId, budget) {
|
|
35598
|
+
const result = await sendLeaseRequest(
|
|
35599
|
+
socketPath,
|
|
35600
|
+
{ kind: "resource_admit", class: cls, holder_id: holderId, budget },
|
|
35601
|
+
"resource_admit_result"
|
|
35602
|
+
);
|
|
35603
|
+
if ("unavailable" in result) return result;
|
|
35604
|
+
if (result.kind === "resource_admit_result") return result;
|
|
35605
|
+
return { unavailable: true, reason: `unexpected response kind: ${result.kind}` };
|
|
35606
|
+
}
|
|
35607
|
+
async function sendResourceRelease(socketPath, cls, holderId) {
|
|
35608
|
+
const result = await sendLeaseRequest(
|
|
35609
|
+
socketPath,
|
|
35610
|
+
{ kind: "resource_release", class: cls, holder_id: holderId },
|
|
35611
|
+
"resource_release_result"
|
|
35612
|
+
);
|
|
35613
|
+
if ("unavailable" in result) return result;
|
|
35614
|
+
if (result.kind === "resource_release_result") return result;
|
|
35615
|
+
return { unavailable: true, reason: `unexpected response kind: ${result.kind}` };
|
|
35616
|
+
}
|
|
35617
|
+
function sendLeaseRequest(socketPath, request, expectedKind) {
|
|
35618
|
+
return new Promise((resolve5) => {
|
|
35619
|
+
const id = `ra-${process.pid}-${request.kind}`;
|
|
35620
|
+
const envelope = LeaseIpcRequestEnvelopeSchema.parse({
|
|
35621
|
+
protocol_version: LEASE_IPC_PROTOCOL_VERSION,
|
|
35622
|
+
id,
|
|
35623
|
+
direction: "request",
|
|
35624
|
+
request
|
|
35625
|
+
});
|
|
35626
|
+
const line2 = `${JSON.stringify(envelope)}
|
|
35627
|
+
`;
|
|
35628
|
+
let settled = false;
|
|
35629
|
+
let buffer = "";
|
|
35630
|
+
const socket = connect(socketPath);
|
|
35631
|
+
const done = (value) => {
|
|
35632
|
+
if (settled) return;
|
|
35633
|
+
settled = true;
|
|
35634
|
+
socket.removeAllListeners();
|
|
35635
|
+
socket.destroy();
|
|
35636
|
+
resolve5(value);
|
|
35637
|
+
};
|
|
35638
|
+
const degrade = (reason) => done({ unavailable: true, reason });
|
|
35639
|
+
socket.setTimeout(REQUEST_TIMEOUT_MS);
|
|
35640
|
+
const connectTimer = setTimeout(
|
|
35641
|
+
() => degrade("supervisor connect timeout"),
|
|
35642
|
+
CONNECT_TIMEOUT_MS
|
|
35643
|
+
);
|
|
35644
|
+
socket.on("connect", () => {
|
|
35645
|
+
clearTimeout(connectTimer);
|
|
35646
|
+
socket.write(line2);
|
|
35647
|
+
});
|
|
35648
|
+
socket.on("timeout", () => degrade("supervisor request timeout"));
|
|
35649
|
+
socket.on("error", (err) => {
|
|
35650
|
+
clearTimeout(connectTimer);
|
|
35651
|
+
degrade(err.message);
|
|
35652
|
+
});
|
|
35653
|
+
socket.on("close", () => {
|
|
35654
|
+
if (!settled) degrade("supervisor closed the connection before responding");
|
|
35655
|
+
});
|
|
35656
|
+
socket.on("data", (chunk) => {
|
|
35657
|
+
buffer += chunk.toString("utf8");
|
|
35658
|
+
const nl = buffer.indexOf("\n");
|
|
35659
|
+
if (nl < 0) return;
|
|
35660
|
+
let json3;
|
|
35661
|
+
try {
|
|
35662
|
+
json3 = JSON.parse(buffer.slice(0, nl));
|
|
35663
|
+
} catch (cause) {
|
|
35664
|
+
degrade(`malformed lease-ipc frame: ${cause instanceof Error ? cause.message : cause}`);
|
|
35665
|
+
return;
|
|
35666
|
+
}
|
|
35667
|
+
const parsed = LeaseIpcResponseEnvelopeSchema.safeParse(json3);
|
|
35668
|
+
if (!parsed.success) {
|
|
35669
|
+
degrade(`off-contract lease-ipc response: ${parsed.error.message}`);
|
|
35670
|
+
return;
|
|
35671
|
+
}
|
|
35672
|
+
const { response } = parsed.data;
|
|
35673
|
+
if (response.kind === expectedKind) {
|
|
35674
|
+
done(response);
|
|
35675
|
+
return;
|
|
35676
|
+
}
|
|
35677
|
+
if (response.kind === "error") {
|
|
35678
|
+
degrade(`${response.code}: ${response.message}`);
|
|
35679
|
+
return;
|
|
35680
|
+
}
|
|
35681
|
+
degrade(`unexpected lease-ipc response kind for ${request.kind}: ${response.kind}`);
|
|
35682
|
+
});
|
|
35683
|
+
});
|
|
35684
|
+
}
|
|
35685
|
+
var SUPERVISOR_SOCKET_NAME, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS;
|
|
35686
|
+
var init_supervisor_admit = __esm({
|
|
35687
|
+
"packages/core/src/resources/supervisor-admit.ts"() {
|
|
35688
|
+
"use strict";
|
|
35689
|
+
init_src();
|
|
35690
|
+
SUPERVISOR_SOCKET_NAME = "cleo-supervisor.sock";
|
|
35691
|
+
CONNECT_TIMEOUT_MS = 250;
|
|
35692
|
+
REQUEST_TIMEOUT_MS = 2e3;
|
|
35693
|
+
}
|
|
35694
|
+
});
|
|
35695
|
+
|
|
35696
|
+
// packages/core/src/resources/governor.ts
|
|
35697
|
+
var governor_exports = {};
|
|
35698
|
+
__export(governor_exports, {
|
|
35699
|
+
ResourceGovernor: () => ResourceGovernor,
|
|
35700
|
+
_resetGovernorStateForTest: () => _resetGovernorStateForTest,
|
|
35701
|
+
computeClassBudget: () => computeClassBudget,
|
|
35702
|
+
governor: () => governor,
|
|
35703
|
+
governorSlotDir: () => governorSlotDir,
|
|
35704
|
+
resolveGovernorMode: () => resolveGovernorMode,
|
|
35705
|
+
slotFileCount: () => slotFileCount
|
|
35706
|
+
});
|
|
35707
|
+
import { existsSync as existsSync3, mkdirSync, readdirSync as readdirSync2, writeFileSync } from "node:fs";
|
|
35708
|
+
import { availableParallelism, totalmem } from "node:os";
|
|
35709
|
+
import { join as join6 } from "node:path";
|
|
35181
35710
|
import lockfile from "proper-lockfile";
|
|
35711
|
+
function log2() {
|
|
35712
|
+
if (_log2 === null) _log2 = getLogger("resource-governor");
|
|
35713
|
+
return _log2;
|
|
35714
|
+
}
|
|
35715
|
+
function resolveGovernorMode() {
|
|
35716
|
+
if (_cachedMode2 !== null) return _cachedMode2;
|
|
35717
|
+
const raw = process.env.CLEO_RESOURCES_MODE;
|
|
35718
|
+
_cachedMode2 = raw === "supervisor" || raw === "local" || raw === "off" ? raw : "local";
|
|
35719
|
+
return _cachedMode2;
|
|
35720
|
+
}
|
|
35721
|
+
function logSupervisorDegradeOnce(reason) {
|
|
35722
|
+
if (_supervisorDegradeLogged) return;
|
|
35723
|
+
_supervisorDegradeLogged = true;
|
|
35724
|
+
log2().info(
|
|
35725
|
+
`CLEO_RESOURCES_MODE=supervisor but the arbiter is unreachable (${reason}); degrading to local-mode admission for the process lifetime.`
|
|
35726
|
+
);
|
|
35727
|
+
}
|
|
35728
|
+
function _resetGovernorStateForTest() {
|
|
35729
|
+
_cachedMode2 = null;
|
|
35730
|
+
_supervisorDegradeLogged = false;
|
|
35731
|
+
}
|
|
35732
|
+
function someAvg10(sample) {
|
|
35733
|
+
const some = sample.globalPressure?.some ?? sample.slicePressure?.some;
|
|
35734
|
+
return some?.avg10 ?? 0;
|
|
35735
|
+
}
|
|
35736
|
+
function computeClassBudget(cls, sample, opts = {}) {
|
|
35737
|
+
if (cls === "interactive-cli") return Number.POSITIVE_INFINITY;
|
|
35738
|
+
const cpus = Math.max(1, opts.cpuCount ?? availableParallelism());
|
|
35739
|
+
const totalBytes = opts.totalMemBytes ?? totalmem();
|
|
35740
|
+
const headroomBytes = (opts.headroomMb ?? 2048) * MB;
|
|
35741
|
+
const hold = opts.holdSomeAvg10 ?? 10;
|
|
35742
|
+
const floor = opts.floorSomeAvg10 ?? 25;
|
|
35743
|
+
const some = someAvg10(sample);
|
|
35744
|
+
const availBytes = sample.memAvailableBytes ?? totalBytes;
|
|
35745
|
+
const fullStall = sample.globalPressure?.full?.avg10 ?? sample.slicePressure?.full?.avg10 ?? 0;
|
|
35746
|
+
const backoff = some > floor || fullStall > 10;
|
|
35747
|
+
switch (cls) {
|
|
35748
|
+
case "full-build":
|
|
35749
|
+
return 1;
|
|
35750
|
+
case "agent-session": {
|
|
35751
|
+
const estRamBytes = (opts.agentEstRamMb ?? 4096) * MB;
|
|
35752
|
+
const byMem = Math.floor((availBytes - headroomBytes) / estRamBytes);
|
|
35753
|
+
return clamp(1, byMem, Math.max(1, cpus - 2));
|
|
35754
|
+
}
|
|
35755
|
+
case "llm-call":
|
|
35756
|
+
return Math.max(1, cpus - 2);
|
|
35757
|
+
case "test-run":
|
|
35758
|
+
case "scoped-build": {
|
|
35759
|
+
const base = Math.max(1, Math.floor(cpus / 4));
|
|
35760
|
+
if (some > floor) return 1;
|
|
35761
|
+
if (some > hold) return Math.max(1, Math.floor(base / 2));
|
|
35762
|
+
return base;
|
|
35763
|
+
}
|
|
35764
|
+
case "db-heavy":
|
|
35765
|
+
return backoff ? 0 : 1;
|
|
35766
|
+
case "background-autonomous":
|
|
35767
|
+
return some > hold || fullStall > 5 ? 0 : 1;
|
|
35768
|
+
default:
|
|
35769
|
+
return 1;
|
|
35770
|
+
}
|
|
35771
|
+
}
|
|
35772
|
+
function clamp(lo, v, hi) {
|
|
35773
|
+
return Math.max(lo, Math.min(v, hi));
|
|
35774
|
+
}
|
|
35775
|
+
function governorSlotDir(cls) {
|
|
35776
|
+
return join6(getCleoHome(), "locks", `resource-${cls}`);
|
|
35777
|
+
}
|
|
35778
|
+
function ensureSlotFiles(dir, count) {
|
|
35779
|
+
if (!existsSync3(dir)) mkdirSync(dir, { recursive: true });
|
|
35780
|
+
const paths = [];
|
|
35781
|
+
for (let i = 0; i < count; i++) {
|
|
35782
|
+
const p = join6(dir, `slot-${i}.lock`);
|
|
35783
|
+
if (!existsSync3(p)) writeFileSync(p, "", { flag: "a" });
|
|
35784
|
+
paths.push(p);
|
|
35785
|
+
}
|
|
35786
|
+
return paths;
|
|
35787
|
+
}
|
|
35788
|
+
function sleep(ms) {
|
|
35789
|
+
return new Promise((r) => setTimeout(r, ms));
|
|
35790
|
+
}
|
|
35791
|
+
function passThroughGrant(cls) {
|
|
35792
|
+
return {
|
|
35793
|
+
deferred: false,
|
|
35794
|
+
class: cls,
|
|
35795
|
+
slot: -1,
|
|
35796
|
+
acquiredAtMs: Date.now(),
|
|
35797
|
+
release: async () => {
|
|
35798
|
+
}
|
|
35799
|
+
};
|
|
35800
|
+
}
|
|
35801
|
+
function deferral(cls, reason, retryAfterMs) {
|
|
35802
|
+
return { deferred: true, class: cls, retryAfterMs, reason };
|
|
35803
|
+
}
|
|
35804
|
+
function supervisorHolderId(cls) {
|
|
35805
|
+
_supervisorHolderSeq += 1;
|
|
35806
|
+
return `${process.pid}:${cls}:${_supervisorHolderSeq}`;
|
|
35807
|
+
}
|
|
35808
|
+
function shuffledIndices(n) {
|
|
35809
|
+
const order = Array.from({ length: n }, (_, i) => i);
|
|
35810
|
+
for (let i = order.length - 1; i > 0; i--) {
|
|
35811
|
+
const j = Math.floor(Math.random() * (i + 1));
|
|
35812
|
+
const a = order[i];
|
|
35813
|
+
const b = order[j];
|
|
35814
|
+
if (a !== void 0 && b !== void 0) {
|
|
35815
|
+
order[i] = b;
|
|
35816
|
+
order[j] = a;
|
|
35817
|
+
}
|
|
35818
|
+
}
|
|
35819
|
+
return order;
|
|
35820
|
+
}
|
|
35821
|
+
async function countHeldSlots(cls, budget) {
|
|
35822
|
+
const dir = governorSlotDir(cls);
|
|
35823
|
+
if (!existsSync3(dir)) return 0;
|
|
35824
|
+
const slots = ensureSlotFiles(dir, budget);
|
|
35825
|
+
let held = 0;
|
|
35826
|
+
for (const path of slots) {
|
|
35827
|
+
try {
|
|
35828
|
+
const release = await lockfile.lock(path, { retries: 0, stale: STALE_MS, realpath: false });
|
|
35829
|
+
await release();
|
|
35830
|
+
} catch {
|
|
35831
|
+
held++;
|
|
35832
|
+
}
|
|
35833
|
+
}
|
|
35834
|
+
return held;
|
|
35835
|
+
}
|
|
35836
|
+
function slotFileCount(cls) {
|
|
35837
|
+
const dir = governorSlotDir(cls);
|
|
35838
|
+
if (!existsSync3(dir)) return 0;
|
|
35839
|
+
return readdirSync2(dir).filter((f) => f.startsWith("slot-") && f.endsWith(".lock")).length;
|
|
35840
|
+
}
|
|
35841
|
+
var _log2, _cachedMode2, _supervisorDegradeLogged, MB, STALE_MS, _supervisorHolderSeq, ResourceGovernor, governor;
|
|
35842
|
+
var init_governor = __esm({
|
|
35843
|
+
"packages/core/src/resources/governor.ts"() {
|
|
35844
|
+
"use strict";
|
|
35845
|
+
init_src();
|
|
35846
|
+
init_logger2();
|
|
35847
|
+
init_paths();
|
|
35848
|
+
init_monitor();
|
|
35849
|
+
init_supervisor_admit();
|
|
35850
|
+
_log2 = null;
|
|
35851
|
+
_cachedMode2 = null;
|
|
35852
|
+
_supervisorDegradeLogged = false;
|
|
35853
|
+
MB = 1024 * 1024;
|
|
35854
|
+
STALE_MS = 6e5;
|
|
35855
|
+
_supervisorHolderSeq = 0;
|
|
35856
|
+
ResourceGovernor = class {
|
|
35857
|
+
/**
|
|
35858
|
+
* Acquire one slot of `cls`. Returns a {@link ResourceGrant} on success or a
|
|
35859
|
+
* {@link ResourceDeferral} on denial. Never throws for admission control;
|
|
35860
|
+
* only genuinely unexpected I/O errors propagate.
|
|
35861
|
+
*/
|
|
35862
|
+
async acquire(cls, opts = {}) {
|
|
35863
|
+
const mode = resolveGovernorMode();
|
|
35864
|
+
if (mode === "off" || cls === "interactive-cli") {
|
|
35865
|
+
return passThroughGrant(cls);
|
|
35866
|
+
}
|
|
35867
|
+
const sample = opts.sample ?? await (opts.monitor ?? new ResourceMonitor()).sample();
|
|
35868
|
+
const budget = computeClassBudget(cls, sample, opts);
|
|
35869
|
+
if (!Number.isFinite(budget)) return passThroughGrant(cls);
|
|
35870
|
+
if (budget <= 0) {
|
|
35871
|
+
return deferral(
|
|
35872
|
+
cls,
|
|
35873
|
+
`class '${cls}' budget is 0 under current pressure (some avg10=${someAvg10(sample).toFixed(1)})`,
|
|
35874
|
+
DEFAULT_RESOURCE_RETRY_AFTER_MS
|
|
35875
|
+
);
|
|
35876
|
+
}
|
|
35877
|
+
if (mode === "supervisor") {
|
|
35878
|
+
const viaSupervisor = await this.acquireViaSupervisor(cls, Math.floor(budget));
|
|
35879
|
+
if (viaSupervisor !== null) return viaSupervisor;
|
|
35880
|
+
}
|
|
35881
|
+
const dir = governorSlotDir(cls);
|
|
35882
|
+
const slots = ensureSlotFiles(dir, budget);
|
|
35883
|
+
const blocking = opts.blocking ?? true;
|
|
35884
|
+
const timeoutMs = opts.timeoutMs ?? 36e5;
|
|
35885
|
+
const pollMs = opts.pollMs ?? 200;
|
|
35886
|
+
const startedAt = Date.now();
|
|
35887
|
+
do {
|
|
35888
|
+
const order = shuffledIndices(slots.length);
|
|
35889
|
+
for (const idx of order) {
|
|
35890
|
+
const path = slots[idx];
|
|
35891
|
+
if (!path) continue;
|
|
35892
|
+
try {
|
|
35893
|
+
const release = await lockfile.lock(path, {
|
|
35894
|
+
retries: 0,
|
|
35895
|
+
stale: STALE_MS,
|
|
35896
|
+
realpath: false
|
|
35897
|
+
});
|
|
35898
|
+
let released = false;
|
|
35899
|
+
return {
|
|
35900
|
+
deferred: false,
|
|
35901
|
+
class: cls,
|
|
35902
|
+
slot: idx,
|
|
35903
|
+
acquiredAtMs: Date.now(),
|
|
35904
|
+
release: async () => {
|
|
35905
|
+
if (released) return;
|
|
35906
|
+
released = true;
|
|
35907
|
+
try {
|
|
35908
|
+
await release();
|
|
35909
|
+
} catch {
|
|
35910
|
+
}
|
|
35911
|
+
}
|
|
35912
|
+
};
|
|
35913
|
+
} catch {
|
|
35914
|
+
}
|
|
35915
|
+
}
|
|
35916
|
+
if (!blocking) break;
|
|
35917
|
+
await sleep(pollMs);
|
|
35918
|
+
} while (Date.now() - startedAt < timeoutMs);
|
|
35919
|
+
return deferral(
|
|
35920
|
+
cls,
|
|
35921
|
+
`class '${cls}' is at capacity (${budget} slot(s)); ` + (blocking ? `timed out after ${timeoutMs}ms` : "no slot free"),
|
|
35922
|
+
Math.min(pollMs * 4, DEFAULT_RESOURCE_RETRY_AFTER_MS)
|
|
35923
|
+
);
|
|
35924
|
+
}
|
|
35925
|
+
/**
|
|
35926
|
+
* Route an admission through the supervisor's central `resource_admit` verb.
|
|
35927
|
+
* Returns a grant (whose `release` calls `resource_release`) or a deferral, or
|
|
35928
|
+
* `null` when the supervisor is unreachable so the caller degrades to the
|
|
35929
|
+
* local slot engine. Never throws — a dead arbiter never deadlocks work.
|
|
35930
|
+
*
|
|
35931
|
+
* @task T12001
|
|
35932
|
+
*/
|
|
35933
|
+
async acquireViaSupervisor(cls, budget) {
|
|
35934
|
+
const socketPath = resolveSupervisorSocketPath();
|
|
35935
|
+
const holderId = supervisorHolderId(cls);
|
|
35936
|
+
const reply = await sendResourceAdmit(socketPath, cls, holderId, budget);
|
|
35937
|
+
if ("unavailable" in reply) {
|
|
35938
|
+
logSupervisorDegradeOnce(reply.reason);
|
|
35939
|
+
return null;
|
|
35940
|
+
}
|
|
35941
|
+
if (reply.disposition === "deferred") {
|
|
35942
|
+
return deferral(
|
|
35943
|
+
cls,
|
|
35944
|
+
`class '${cls}' deferred by supervisor (budget ${budget})`,
|
|
35945
|
+
reply.retry_after_ms || DEFAULT_RESOURCE_RETRY_AFTER_MS
|
|
35946
|
+
);
|
|
35947
|
+
}
|
|
35948
|
+
let released = false;
|
|
35949
|
+
return {
|
|
35950
|
+
deferred: false,
|
|
35951
|
+
class: cls,
|
|
35952
|
+
slot: 0,
|
|
35953
|
+
acquiredAtMs: Date.now(),
|
|
35954
|
+
release: async () => {
|
|
35955
|
+
if (released) return;
|
|
35956
|
+
released = true;
|
|
35957
|
+
await sendResourceRelease(socketPath, cls, holderId).catch(() => {
|
|
35958
|
+
});
|
|
35959
|
+
}
|
|
35960
|
+
};
|
|
35961
|
+
}
|
|
35962
|
+
/** Non-blocking single-pass acquire (admission semantics). */
|
|
35963
|
+
async tryAcquire(cls, opts = {}) {
|
|
35964
|
+
return this.acquire(cls, { ...opts, blocking: false });
|
|
35965
|
+
}
|
|
35966
|
+
/**
|
|
35967
|
+
* Currently-grantable slot count for `cls` = budget − held. Held is the count
|
|
35968
|
+
* of slot files currently locked. `Infinity` for ungated classes.
|
|
35969
|
+
*/
|
|
35970
|
+
async available(cls, opts = {}) {
|
|
35971
|
+
if (resolveGovernorMode() === "off" || cls === "interactive-cli") {
|
|
35972
|
+
return Number.POSITIVE_INFINITY;
|
|
35973
|
+
}
|
|
35974
|
+
const sample = opts.sample ?? await (opts.monitor ?? new ResourceMonitor()).sample();
|
|
35975
|
+
const budget = computeClassBudget(cls, sample, opts);
|
|
35976
|
+
if (!Number.isFinite(budget)) return Number.POSITIVE_INFINITY;
|
|
35977
|
+
if (budget <= 0) return 0;
|
|
35978
|
+
const held = await countHeldSlots(cls, budget);
|
|
35979
|
+
return Math.max(0, budget - held);
|
|
35980
|
+
}
|
|
35981
|
+
};
|
|
35982
|
+
governor = new ResourceGovernor();
|
|
35983
|
+
}
|
|
35984
|
+
});
|
|
35985
|
+
|
|
35986
|
+
// packages/core/src/store/lock.ts
|
|
35987
|
+
import lockfile2 from "proper-lockfile";
|
|
35182
35988
|
async function acquireLock(filePath, options) {
|
|
35183
35989
|
try {
|
|
35184
|
-
const release = await
|
|
35990
|
+
const release = await lockfile2.lock(filePath, {
|
|
35185
35991
|
...DEFAULT_LOCK_OPTIONS,
|
|
35186
35992
|
...options?.stale !== void 0 && { stale: options.stale },
|
|
35187
35993
|
...options?.retries !== void 0 && {
|
|
@@ -35227,7 +36033,7 @@ var init_lock = __esm({
|
|
|
35227
36033
|
});
|
|
35228
36034
|
|
|
35229
36035
|
// packages/core/src/store/atomic.ts
|
|
35230
|
-
import { mkdir, readFile as
|
|
36036
|
+
import { mkdir, readFile as readFile3, rename, unlink } from "node:fs/promises";
|
|
35231
36037
|
import { dirname as dirname3 } from "node:path";
|
|
35232
36038
|
import writeFileAtomic from "write-file-atomic";
|
|
35233
36039
|
async function atomicWrite(filePath, data, options) {
|
|
@@ -35243,7 +36049,7 @@ async function atomicWrite(filePath, data, options) {
|
|
|
35243
36049
|
}
|
|
35244
36050
|
async function safeReadFile(filePath) {
|
|
35245
36051
|
try {
|
|
35246
|
-
return await
|
|
36052
|
+
return await readFile3(filePath, "utf8");
|
|
35247
36053
|
} catch (err) {
|
|
35248
36054
|
if (err instanceof Error && "code" in err && err.code === "ENOENT") {
|
|
35249
36055
|
return null;
|
|
@@ -35264,34 +36070,34 @@ var init_atomic = __esm({
|
|
|
35264
36070
|
});
|
|
35265
36071
|
|
|
35266
36072
|
// packages/core/src/store/backup.ts
|
|
35267
|
-
import { copyFile, rename as fsRename, mkdir as mkdir2, readdir, stat, unlink as unlink2 } from "node:fs/promises";
|
|
35268
|
-
import { basename, join as
|
|
36073
|
+
import { copyFile, rename as fsRename, mkdir as mkdir2, readdir, stat as stat2, unlink as unlink2 } from "node:fs/promises";
|
|
36074
|
+
import { basename, join as join7 } from "node:path";
|
|
35269
36075
|
async function createBackup(filePath, backupDir, maxBackups = DEFAULT_MAX_BACKUPS) {
|
|
35270
36076
|
try {
|
|
35271
36077
|
await mkdir2(backupDir, { recursive: true });
|
|
35272
36078
|
const fileName = basename(filePath);
|
|
35273
36079
|
try {
|
|
35274
|
-
await
|
|
36080
|
+
await stat2(filePath);
|
|
35275
36081
|
} catch {
|
|
35276
36082
|
throw new CleoError(3 /* FILE_ERROR */, `Cannot backup: source file not found: ${filePath}`);
|
|
35277
36083
|
}
|
|
35278
36084
|
for (let i = maxBackups; i >= 1; i--) {
|
|
35279
|
-
const current =
|
|
36085
|
+
const current = join7(backupDir, `${fileName}.${i}`);
|
|
35280
36086
|
if (i === maxBackups) {
|
|
35281
36087
|
try {
|
|
35282
36088
|
await unlink2(current);
|
|
35283
36089
|
} catch {
|
|
35284
36090
|
}
|
|
35285
36091
|
} else {
|
|
35286
|
-
const next =
|
|
36092
|
+
const next = join7(backupDir, `${fileName}.${i + 1}`);
|
|
35287
36093
|
try {
|
|
35288
|
-
await
|
|
36094
|
+
await stat2(current);
|
|
35289
36095
|
await fsRename(current, next);
|
|
35290
36096
|
} catch {
|
|
35291
36097
|
}
|
|
35292
36098
|
}
|
|
35293
36099
|
}
|
|
35294
|
-
const backupPath =
|
|
36100
|
+
const backupPath = join7(backupDir, `${fileName}.1`);
|
|
35295
36101
|
await copyFile(filePath, backupPath);
|
|
35296
36102
|
return backupPath;
|
|
35297
36103
|
} catch (err) {
|
|
@@ -35570,8 +36376,8 @@ var init_json2 = __esm({
|
|
|
35570
36376
|
});
|
|
35571
36377
|
|
|
35572
36378
|
// packages/core/src/scaffold/ensure-config.ts
|
|
35573
|
-
import { existsSync as
|
|
35574
|
-
import { basename as basename2, dirname as dirname4, join as
|
|
36379
|
+
import { existsSync as existsSync4, readFileSync } from "node:fs";
|
|
36380
|
+
import { basename as basename2, dirname as dirname4, join as join8, resolve as resolve2 } from "node:path";
|
|
35575
36381
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
35576
36382
|
function getPackageRoot() {
|
|
35577
36383
|
const thisFile = fileURLToPath2(import.meta.url);
|
|
@@ -35579,7 +36385,7 @@ function getPackageRoot() {
|
|
|
35579
36385
|
}
|
|
35580
36386
|
function getCleoVersion() {
|
|
35581
36387
|
try {
|
|
35582
|
-
const pkgPath =
|
|
36388
|
+
const pkgPath = join8(getPackageRoot(), "package.json");
|
|
35583
36389
|
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
35584
36390
|
return pkg.version ?? "0.0.0";
|
|
35585
36391
|
} catch {
|
|
@@ -35598,25 +36404,25 @@ var init_ensure_config = __esm({
|
|
|
35598
36404
|
// packages/core/src/store/exodus/archive.ts
|
|
35599
36405
|
import {
|
|
35600
36406
|
copyFileSync as copyFileSync2,
|
|
35601
|
-
existsSync as
|
|
35602
|
-
mkdirSync,
|
|
36407
|
+
existsSync as existsSync5,
|
|
36408
|
+
mkdirSync as mkdirSync2,
|
|
35603
36409
|
renameSync,
|
|
35604
36410
|
unlinkSync,
|
|
35605
|
-
writeFileSync
|
|
36411
|
+
writeFileSync as writeFileSync2
|
|
35606
36412
|
} from "node:fs";
|
|
35607
|
-
import { basename as basename3, join as
|
|
36413
|
+
import { basename as basename3, join as join9 } from "node:path";
|
|
35608
36414
|
function scopeBaseDir(scope, cwd) {
|
|
35609
36415
|
return scope === "project" ? resolveCleoDir(cwd) : getCleoHome();
|
|
35610
36416
|
}
|
|
35611
36417
|
function exodusArchiveDir(scope, cwd) {
|
|
35612
|
-
return
|
|
36418
|
+
return join9(scopeBaseDir(scope, cwd), ARCHIVE_DIR_NAME);
|
|
35613
36419
|
}
|
|
35614
36420
|
function exodusMarkerPath(scope, cwd) {
|
|
35615
|
-
return
|
|
36421
|
+
return join9(scopeBaseDir(scope, cwd), MARKER_FILENAME_BY_SCOPE[scope]);
|
|
35616
36422
|
}
|
|
35617
36423
|
function hasExodusCompleteMarker(scope, cwd) {
|
|
35618
36424
|
try {
|
|
35619
|
-
return
|
|
36425
|
+
return existsSync5(exodusMarkerPath(scope, cwd));
|
|
35620
36426
|
} catch {
|
|
35621
36427
|
return false;
|
|
35622
36428
|
}
|
|
@@ -35624,7 +36430,7 @@ function hasExodusCompleteMarker(scope, cwd) {
|
|
|
35624
36430
|
function writeExodusCompleteMarker(scope, archivedSources, cwd) {
|
|
35625
36431
|
const markerPath = exodusMarkerPath(scope, cwd);
|
|
35626
36432
|
const baseDir = scopeBaseDir(scope, cwd);
|
|
35627
|
-
|
|
36433
|
+
mkdirSync2(baseDir, { recursive: true });
|
|
35628
36434
|
const marker16 = {
|
|
35629
36435
|
version: 1,
|
|
35630
36436
|
scope,
|
|
@@ -35633,17 +36439,17 @@ function writeExodusCompleteMarker(scope, archivedSources, cwd) {
|
|
|
35633
36439
|
archivedSources: [...archivedSources]
|
|
35634
36440
|
};
|
|
35635
36441
|
const tmpPath = `${markerPath}.tmp`;
|
|
35636
|
-
|
|
36442
|
+
writeFileSync2(tmpPath, JSON.stringify(marker16, null, 2) + "\n", "utf8");
|
|
35637
36443
|
renameSync(tmpPath, markerPath);
|
|
35638
|
-
|
|
36444
|
+
log3.info({ scope, markerPath, archivedSources }, "exodus: wrote completion marker");
|
|
35639
36445
|
return markerPath;
|
|
35640
36446
|
}
|
|
35641
36447
|
function moveFileInto(srcPath, destDir) {
|
|
35642
|
-
|
|
35643
|
-
let dest =
|
|
35644
|
-
if (
|
|
36448
|
+
mkdirSync2(destDir, { recursive: true });
|
|
36449
|
+
let dest = join9(destDir, basename3(srcPath));
|
|
36450
|
+
if (existsSync5(dest)) {
|
|
35645
36451
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "").replace(/Z$/, "Z");
|
|
35646
|
-
dest =
|
|
36452
|
+
dest = join9(destDir, `${basename3(srcPath)}.${stamp}`);
|
|
35647
36453
|
}
|
|
35648
36454
|
try {
|
|
35649
36455
|
renameSync(srcPath, dest);
|
|
@@ -35654,25 +36460,25 @@ function moveFileInto(srcPath, destDir) {
|
|
|
35654
36460
|
return dest;
|
|
35655
36461
|
}
|
|
35656
36462
|
function archiveSourceDb(source, cwd) {
|
|
35657
|
-
if (!
|
|
36463
|
+
if (!existsSync5(source.path)) {
|
|
35658
36464
|
return { name: source.name, sourcePath: source.path, archivedTo: null, action: "absent" };
|
|
35659
36465
|
}
|
|
35660
36466
|
const destDir = exodusArchiveDir(source.targetScope, cwd);
|
|
35661
36467
|
const archivedTo = moveFileInto(source.path, destDir);
|
|
35662
36468
|
for (const suffix of SIDECAR_SUFFIXES) {
|
|
35663
36469
|
const sidecar = `${source.path}${suffix}`;
|
|
35664
|
-
if (
|
|
36470
|
+
if (existsSync5(sidecar)) {
|
|
35665
36471
|
try {
|
|
35666
36472
|
moveFileInto(sidecar, destDir);
|
|
35667
36473
|
} catch (err) {
|
|
35668
|
-
|
|
36474
|
+
log3.warn(
|
|
35669
36475
|
{ err, sidecar, sourceName: source.name },
|
|
35670
36476
|
"exodus-archive: failed to archive sidecar (non-fatal)"
|
|
35671
36477
|
);
|
|
35672
36478
|
}
|
|
35673
36479
|
}
|
|
35674
36480
|
}
|
|
35675
|
-
|
|
36481
|
+
log3.info(
|
|
35676
36482
|
{ sourceName: source.name, sourcePath: source.path, archivedTo, scope: source.targetScope },
|
|
35677
36483
|
"exodus-archive: archived legacy source DB"
|
|
35678
36484
|
);
|
|
@@ -35707,7 +36513,7 @@ function detectStrandedResidue(sources, cwd) {
|
|
|
35707
36513
|
const stranded = [];
|
|
35708
36514
|
for (const source of sources) {
|
|
35709
36515
|
if (!markedScopes.has(source.targetScope)) continue;
|
|
35710
|
-
if (
|
|
36516
|
+
if (existsSync5(source.path)) {
|
|
35711
36517
|
stranded.push({ name: source.name, path: source.path, scope: source.targetScope });
|
|
35712
36518
|
}
|
|
35713
36519
|
}
|
|
@@ -35723,14 +36529,14 @@ function archiveStrandedResidue(stranded, sources, cwd) {
|
|
|
35723
36529
|
}
|
|
35724
36530
|
return results;
|
|
35725
36531
|
}
|
|
35726
|
-
var
|
|
36532
|
+
var log3, ARCHIVE_DIR_NAME, MARKER_FILENAME_BY_SCOPE, SIDECAR_SUFFIXES;
|
|
35727
36533
|
var init_archive2 = __esm({
|
|
35728
36534
|
"packages/core/src/store/exodus/archive.ts"() {
|
|
35729
36535
|
"use strict";
|
|
35730
36536
|
init_logger2();
|
|
35731
36537
|
init_paths();
|
|
35732
36538
|
init_ensure_config();
|
|
35733
|
-
|
|
36539
|
+
log3 = getLogger("exodus-archive");
|
|
35734
36540
|
ARCHIVE_DIR_NAME = "_archive";
|
|
35735
36541
|
MARKER_FILENAME_BY_SCOPE = {
|
|
35736
36542
|
project: "exodus-complete",
|
|
@@ -36124,7 +36930,7 @@ var init_table_name_map = __esm({
|
|
|
36124
36930
|
});
|
|
36125
36931
|
|
|
36126
36932
|
// packages/core/src/store/exodus/count-parity.ts
|
|
36127
|
-
import { existsSync as
|
|
36933
|
+
import { existsSync as existsSync6 } from "node:fs";
|
|
36128
36934
|
function listTables(db) {
|
|
36129
36935
|
return db.prepare(
|
|
36130
36936
|
"SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' AND name NOT LIKE '__drizzle_%' ORDER BY name"
|
|
@@ -36145,14 +36951,14 @@ function rowCount(db, tableName) {
|
|
|
36145
36951
|
function computeCountParity(sources, projectDbPath, globalDbPath) {
|
|
36146
36952
|
const entries = [];
|
|
36147
36953
|
let skipped = 0;
|
|
36148
|
-
if (!
|
|
36954
|
+
if (!existsSync6(projectDbPath) || !existsSync6(globalDbPath)) {
|
|
36149
36955
|
return { ok: false, entries: [], deficits: [], checked: 0, skipped: 0 };
|
|
36150
36956
|
}
|
|
36151
36957
|
const projectSnap = openCleoDbSnapshot(projectDbPath, { readOnly: true });
|
|
36152
36958
|
const globalSnap = openCleoDbSnapshot(globalDbPath, { readOnly: true });
|
|
36153
36959
|
try {
|
|
36154
36960
|
for (const src of sources) {
|
|
36155
|
-
if (!
|
|
36961
|
+
if (!existsSync6(src.path)) continue;
|
|
36156
36962
|
const srcSnap = openCleoDbSnapshot(src.path, { readOnly: true });
|
|
36157
36963
|
try {
|
|
36158
36964
|
for (const legacyTable of listTables(srcSnap.db)) {
|
|
@@ -36191,27 +36997,27 @@ function computeCountParity(sources, projectDbPath, globalDbPath) {
|
|
|
36191
36997
|
}
|
|
36192
36998
|
const deficits = entries.filter((e) => e.deficit > 0);
|
|
36193
36999
|
if (deficits.length > 0) {
|
|
36194
|
-
|
|
37000
|
+
log4.warn(
|
|
36195
37001
|
{ deficitCount: deficits.length, sample: deficits.slice(0, 5) },
|
|
36196
37002
|
`exodus count-parity: ${deficits.length} table(s) have FEWER rows in the consolidated target than the legacy source`
|
|
36197
37003
|
);
|
|
36198
37004
|
}
|
|
36199
37005
|
return { ok: deficits.length === 0, entries, deficits, checked: entries.length, skipped };
|
|
36200
37006
|
}
|
|
36201
|
-
var
|
|
37007
|
+
var log4;
|
|
36202
37008
|
var init_count_parity = __esm({
|
|
36203
37009
|
"packages/core/src/store/exodus/count-parity.ts"() {
|
|
36204
37010
|
"use strict";
|
|
36205
37011
|
init_logger2();
|
|
36206
37012
|
init_open_cleo_db();
|
|
36207
37013
|
init_table_name_map();
|
|
36208
|
-
|
|
37014
|
+
log4 = getLogger("exodus-count-parity");
|
|
36209
37015
|
}
|
|
36210
37016
|
});
|
|
36211
37017
|
|
|
36212
37018
|
// packages/core/src/store/exodus/plan.ts
|
|
36213
|
-
import { existsSync as
|
|
36214
|
-
import { join as
|
|
37019
|
+
import { existsSync as existsSync7, readdirSync as readdirSync3, statfsSync, statSync } from "node:fs";
|
|
37020
|
+
import { join as join10 } from "node:path";
|
|
36215
37021
|
function computeRequiredBytes(totalSourceBytes, largestSourceBytes) {
|
|
36216
37022
|
return Math.ceil(STAGING_HEADROOM_FACTOR * largestSourceBytes) + totalSourceBytes;
|
|
36217
37023
|
}
|
|
@@ -36222,33 +37028,33 @@ function buildSourceDescriptors(cwd) {
|
|
|
36222
37028
|
// Project-tier — go into consolidated project-scope cleo.db
|
|
36223
37029
|
{
|
|
36224
37030
|
name: "tasks",
|
|
36225
|
-
path:
|
|
37031
|
+
path: join10(cleoDir, "tasks.db"),
|
|
36226
37032
|
targetScope: "project"
|
|
36227
37033
|
},
|
|
36228
37034
|
{
|
|
36229
37035
|
name: "brain (project)",
|
|
36230
|
-
path:
|
|
37036
|
+
path: join10(cleoDir, "brain.db"),
|
|
36231
37037
|
targetScope: "project"
|
|
36232
37038
|
},
|
|
36233
37039
|
{
|
|
36234
37040
|
name: "conduit",
|
|
36235
|
-
path:
|
|
37041
|
+
path: join10(cleoDir, "conduit.db"),
|
|
36236
37042
|
targetScope: "project"
|
|
36237
37043
|
},
|
|
36238
37044
|
// Global-tier — go into consolidated global-scope cleo.db
|
|
36239
37045
|
{
|
|
36240
37046
|
name: "nexus",
|
|
36241
|
-
path:
|
|
37047
|
+
path: join10(cleoHome, "nexus.db"),
|
|
36242
37048
|
targetScope: "global"
|
|
36243
37049
|
},
|
|
36244
37050
|
{
|
|
36245
37051
|
name: "signaldock",
|
|
36246
|
-
path:
|
|
37052
|
+
path: join10(cleoHome, "signaldock.db"),
|
|
36247
37053
|
targetScope: "global"
|
|
36248
37054
|
},
|
|
36249
37055
|
{
|
|
36250
37056
|
name: "skills",
|
|
36251
|
-
path:
|
|
37057
|
+
path: join10(cleoHome, "skills.db"),
|
|
36252
37058
|
targetScope: "global"
|
|
36253
37059
|
}
|
|
36254
37060
|
];
|
|
@@ -36274,10 +37080,10 @@ function deriveStagingDirName() {
|
|
|
36274
37080
|
}
|
|
36275
37081
|
function findExistingStaging(cleoDir) {
|
|
36276
37082
|
try {
|
|
36277
|
-
const entries =
|
|
37083
|
+
const entries = readdirSync3(cleoDir, { withFileTypes: true });
|
|
36278
37084
|
const stagingDirs = entries.filter((e) => e.isDirectory() && e.name.startsWith("exodus-staging-")).map((e) => e.name).sort().reverse();
|
|
36279
37085
|
if (stagingDirs.length > 0) {
|
|
36280
|
-
return
|
|
37086
|
+
return join10(cleoDir, stagingDirs[0]);
|
|
36281
37087
|
}
|
|
36282
37088
|
} catch {
|
|
36283
37089
|
}
|
|
@@ -36293,7 +37099,7 @@ function buildExodusPlan(cwd) {
|
|
|
36293
37099
|
const availableBytes = getAvailableBytes(cleoDir);
|
|
36294
37100
|
const diskPreflight = totalSourceBytes === 0 || availableBytes >= requiredBytes;
|
|
36295
37101
|
const existingStaging = findExistingStaging(cleoDir);
|
|
36296
|
-
const stagingDir = existingStaging ??
|
|
37102
|
+
const stagingDir = existingStaging ?? join10(cleoDir, deriveStagingDirName());
|
|
36297
37103
|
const resumeFromStaging = existingStaging !== null;
|
|
36298
37104
|
const projectDbPath = resolveDualScopeDbPath("project", cwd);
|
|
36299
37105
|
const globalDbPath = resolveDualScopeDbPath("global");
|
|
@@ -36312,7 +37118,7 @@ function buildExodusPlan(cwd) {
|
|
|
36312
37118
|
};
|
|
36313
37119
|
}
|
|
36314
37120
|
function sourcesPresent(sources) {
|
|
36315
|
-
return sources.some((s) =>
|
|
37121
|
+
return sources.some((s) => existsSync7(s.path));
|
|
36316
37122
|
}
|
|
36317
37123
|
var STAGING_HEADROOM_FACTOR, STAGING_COPY_SKIP_THRESHOLD_BYTES;
|
|
36318
37124
|
var init_plan2 = __esm({
|
|
@@ -36326,18 +37132,18 @@ var init_plan2 = __esm({
|
|
|
36326
37132
|
});
|
|
36327
37133
|
|
|
36328
37134
|
// packages/core/src/store/exodus/health.ts
|
|
36329
|
-
import { existsSync as
|
|
37135
|
+
import { existsSync as existsSync8, statSync as statSync2 } from "node:fs";
|
|
36330
37136
|
function buildExodusHealth(cwd) {
|
|
36331
37137
|
const plan = buildExodusPlan(cwd);
|
|
36332
|
-
const anyLegacyPresent = plan.sources.some((s) =>
|
|
37138
|
+
const anyLegacyPresent = plan.sources.some((s) => existsSync8(s.path));
|
|
36333
37139
|
const parity = anyLegacyPresent ? computeCountParity(plan.sources, plan.projectDbPath, plan.globalDbPath) : { ok: true, entries: [], deficits: [], checked: 0, skipped: 0 };
|
|
36334
37140
|
const consolidatedExistsByScope = {
|
|
36335
|
-
project:
|
|
36336
|
-
global:
|
|
37141
|
+
project: existsSync8(plan.projectDbPath),
|
|
37142
|
+
global: existsSync8(plan.globalDbPath)
|
|
36337
37143
|
};
|
|
36338
37144
|
const buildScope = (scope) => {
|
|
36339
37145
|
const sources = plan.sources.filter((s) => s.targetScope === scope).map((s) => {
|
|
36340
|
-
const present =
|
|
37146
|
+
const present = existsSync8(s.path);
|
|
36341
37147
|
let bytes = 0;
|
|
36342
37148
|
if (present) {
|
|
36343
37149
|
try {
|
|
@@ -36604,15 +37410,15 @@ var init_types = __esm({
|
|
|
36604
37410
|
// packages/core/src/store/exodus/migrate.ts
|
|
36605
37411
|
import {
|
|
36606
37412
|
copyFileSync as copyFileSync3,
|
|
36607
|
-
existsSync as
|
|
36608
|
-
mkdirSync as
|
|
37413
|
+
existsSync as existsSync9,
|
|
37414
|
+
mkdirSync as mkdirSync3,
|
|
36609
37415
|
readFileSync as readFileSync2,
|
|
36610
37416
|
renameSync as renameSync2,
|
|
36611
37417
|
statSync as statSync3,
|
|
36612
37418
|
unlinkSync as unlinkSync2,
|
|
36613
|
-
writeFileSync as
|
|
37419
|
+
writeFileSync as writeFileSync3
|
|
36614
37420
|
} from "node:fs";
|
|
36615
|
-
import { join as
|
|
37421
|
+
import { join as join11 } from "node:path";
|
|
36616
37422
|
function getSqliteVersion(db) {
|
|
36617
37423
|
try {
|
|
36618
37424
|
const row = db.prepare("SELECT sqlite_version() AS v").get();
|
|
@@ -36628,14 +37434,14 @@ function listTables2(db) {
|
|
|
36628
37434
|
return rows.map((r) => r.name);
|
|
36629
37435
|
}
|
|
36630
37436
|
function writeJournal(stagingDir, journal) {
|
|
36631
|
-
const journalPath =
|
|
37437
|
+
const journalPath = join11(stagingDir, JOURNAL_FILENAME);
|
|
36632
37438
|
const tmpPath = `${journalPath}.tmp`;
|
|
36633
|
-
|
|
37439
|
+
writeFileSync3(tmpPath, JSON.stringify(journal, null, 2) + "\n", "utf8");
|
|
36634
37440
|
renameSync2(tmpPath, journalPath);
|
|
36635
37441
|
}
|
|
36636
37442
|
function readJournal(stagingDir) {
|
|
36637
|
-
const journalPath =
|
|
36638
|
-
if (!
|
|
37443
|
+
const journalPath = join11(stagingDir, JOURNAL_FILENAME);
|
|
37444
|
+
if (!existsSync9(journalPath)) return null;
|
|
36639
37445
|
try {
|
|
36640
37446
|
return JSON.parse(readFileSync2(journalPath, "utf8"));
|
|
36641
37447
|
} catch {
|
|
@@ -36643,17 +37449,17 @@ function readJournal(stagingDir) {
|
|
|
36643
37449
|
}
|
|
36644
37450
|
}
|
|
36645
37451
|
function clearExodusJournal(stagingDir) {
|
|
36646
|
-
const journalPath =
|
|
37452
|
+
const journalPath = join11(stagingDir, JOURNAL_FILENAME);
|
|
36647
37453
|
try {
|
|
36648
|
-
if (!
|
|
37454
|
+
if (!existsSync9(journalPath)) return false;
|
|
36649
37455
|
unlinkSync2(journalPath);
|
|
36650
|
-
|
|
37456
|
+
log5.info(
|
|
36651
37457
|
{ stagingDir },
|
|
36652
37458
|
"exodus: cleared migrate journal after abort/rollback \u2014 a retry will RE-COPY all tables"
|
|
36653
37459
|
);
|
|
36654
37460
|
return true;
|
|
36655
37461
|
} catch (err) {
|
|
36656
|
-
|
|
37462
|
+
log5.warn(
|
|
36657
37463
|
{ err, stagingDir },
|
|
36658
37464
|
'exodus: failed to clear migrate journal after rollback (a retry may skip already-"done" tables)'
|
|
36659
37465
|
);
|
|
@@ -36678,7 +37484,7 @@ function lockPath(dbPath) {
|
|
|
36678
37484
|
}
|
|
36679
37485
|
function acquireAdvisoryLock(dbPath) {
|
|
36680
37486
|
const lp = lockPath(dbPath);
|
|
36681
|
-
|
|
37487
|
+
writeFileSync3(lp, JSON.stringify({ pid: process.pid, ts: (/* @__PURE__ */ new Date()).toISOString() }), "utf8");
|
|
36682
37488
|
}
|
|
36683
37489
|
function releaseAdvisoryLock(dbPath) {
|
|
36684
37490
|
try {
|
|
@@ -36741,7 +37547,7 @@ function buildSelectExpr(attachAlias, legacyTable, targetTableName, col, srcType
|
|
|
36741
37547
|
function copyTableFromAttached(targetNativeDb, srcNativeDb, attachAlias, legacyTableName, sourceName, targetSchema = "main") {
|
|
36742
37548
|
const resolution = resolveConsolidatedTableName(sourceName, legacyTableName);
|
|
36743
37549
|
if (resolution.kind === "skip") {
|
|
36744
|
-
|
|
37550
|
+
log5.warn(
|
|
36745
37551
|
{ legacyTableName, sourceName, reason: resolution.reason },
|
|
36746
37552
|
`Exodus: explicitly skipping table \u2014 ${resolution.reason}`
|
|
36747
37553
|
);
|
|
@@ -36760,7 +37566,7 @@ function copyTableFromAttached(targetNativeDb, srcNativeDb, attachAlias, legacyT
|
|
|
36760
37566
|
).get();
|
|
36761
37567
|
if (!existsRow) {
|
|
36762
37568
|
const reason = `consolidated target '${targetTableName}' not found (mapped from legacy '${legacyTableName}')`;
|
|
36763
|
-
|
|
37569
|
+
log5.warn(
|
|
36764
37570
|
{ legacyTableName, targetTableName, sourceName, attachAlias, targetSchema },
|
|
36765
37571
|
`Exodus: ${reason}`
|
|
36766
37572
|
);
|
|
@@ -36771,13 +37577,13 @@ function copyTableFromAttached(targetNativeDb, srcNativeDb, attachAlias, legacyT
|
|
|
36771
37577
|
const sharedColumns = srcPragma.map((r) => r.name).filter((col) => tgtColMap.has(col));
|
|
36772
37578
|
if (sharedColumns.length === 0) {
|
|
36773
37579
|
const reason = `no overlapping columns between source '${legacyTableName}' and target '${targetTableName}'`;
|
|
36774
|
-
|
|
37580
|
+
log5.warn({ legacyTableName, targetTableName, sourceName }, `Exodus: ${reason}`);
|
|
36775
37581
|
return { rowsCopied: 0, skipped: true, reason };
|
|
36776
37582
|
}
|
|
36777
37583
|
const srcOnlyColumns = srcPragma.map((r) => r.name).filter((c) => !tgtColMap.has(c));
|
|
36778
37584
|
const tgtOnlyColumns = tgtPragma.map((r) => r.name).filter((c) => !srcColumns.has(c));
|
|
36779
37585
|
if (srcOnlyColumns.length > 0 || tgtOnlyColumns.length > 0) {
|
|
36780
|
-
|
|
37586
|
+
log5.info(
|
|
36781
37587
|
{ legacyTableName, targetTableName, sourceName, srcOnlyColumns, tgtOnlyColumns },
|
|
36782
37588
|
"Exodus: column drift detected \u2014 copying intersection, dropping src-only cols, using defaults for tgt-only cols"
|
|
36783
37589
|
);
|
|
@@ -36792,7 +37598,7 @@ function copyTableFromAttached(targetNativeDb, srcNativeDb, attachAlias, legacyT
|
|
|
36792
37598
|
return isoGlobCols.has(col) && (upper.includes("INT") || upper === "" || upper === "NUMERIC");
|
|
36793
37599
|
});
|
|
36794
37600
|
if (coercedCols.length > 0) {
|
|
36795
|
-
|
|
37601
|
+
log5.info(
|
|
36796
37602
|
{
|
|
36797
37603
|
legacyTableName,
|
|
36798
37604
|
targetTableName,
|
|
@@ -36808,7 +37614,7 @@ function copyTableFromAttached(targetNativeDb, srcNativeDb, attachAlias, legacyT
|
|
|
36808
37614
|
(col) => ENUM_NORMALIZATIONS.has(`${targetTableName}.${col}`)
|
|
36809
37615
|
);
|
|
36810
37616
|
if (normalizedCols.length > 0) {
|
|
36811
|
-
|
|
37617
|
+
log5.info(
|
|
36812
37618
|
{ legacyTableName, targetTableName, sourceName, normalizedCols },
|
|
36813
37619
|
`Exodus: applying enum-value normalization for ${normalizedCols.length} column(s) (T11547)`
|
|
36814
37620
|
);
|
|
@@ -36817,7 +37623,7 @@ function copyTableFromAttached(targetNativeDb, srcNativeDb, attachAlias, legacyT
|
|
|
36817
37623
|
(col) => NUMERIC_CLAMPS.has(`${targetTableName}.${col}`)
|
|
36818
37624
|
);
|
|
36819
37625
|
if (clampedCols.length > 0) {
|
|
36820
|
-
|
|
37626
|
+
log5.info(
|
|
36821
37627
|
{ legacyTableName, targetTableName, sourceName, clampedCols },
|
|
36822
37628
|
`Exodus: applying non-finite numeric clamp for ${clampedCols.length} column(s) (T11782)`
|
|
36823
37629
|
);
|
|
@@ -36859,7 +37665,7 @@ function copyTableFromAttached(targetNativeDb, srcNativeDb, attachAlias, legacyT
|
|
|
36859
37665
|
if (rowsCopied < sourceCount) {
|
|
36860
37666
|
const presentAccountedFor = existingBefore + rowsCopied;
|
|
36861
37667
|
if (presentAccountedFor >= sourceCount) {
|
|
36862
|
-
|
|
37668
|
+
log5.info(
|
|
36863
37669
|
{ legacyTableName, targetTableName, sourceName, sourceCount, rowsCopied, existingBefore },
|
|
36864
37670
|
`Exodus: '${legacyTableName}'\u2192'${targetTableName}' \u2014 ${sourceCount - rowsCopied} of ${sourceCount} row(s) already present in target (idempotent PK dedup), ${rowsCopied} newly copied; no loss`
|
|
36865
37671
|
);
|
|
@@ -36867,7 +37673,7 @@ function copyTableFromAttached(targetNativeDb, srcNativeDb, attachAlias, legacyT
|
|
|
36867
37673
|
}
|
|
36868
37674
|
const missing = sourceCount - presentAccountedFor;
|
|
36869
37675
|
const reason = `INSERT OR IGNORE lost ${missing} of ${sourceCount} rows from '${legacyTableName}'\u2192'${targetTableName}' (rowsCopied=${rowsCopied}, existingBefore=${existingBefore}) \u2014 a CHECK/NOT NULL/type/enum constraint rejected them (NOT PK dedup); inspect epoch coercion + enum normalization. verify will confirm.`;
|
|
36870
|
-
|
|
37676
|
+
log5.error(
|
|
36871
37677
|
{
|
|
36872
37678
|
legacyTableName,
|
|
36873
37679
|
targetTableName,
|
|
@@ -36887,10 +37693,10 @@ function checkSchemaVersion(journal, forceCrossVersion) {
|
|
|
36887
37693
|
if (journal.targetSchemaVersion !== EXODUS_TARGET_SCHEMA_VERSION) {
|
|
36888
37694
|
const msg = `Schema version mismatch: journal=${journal.targetSchemaVersion}, expected=${EXODUS_TARGET_SCHEMA_VERSION}`;
|
|
36889
37695
|
if (forceCrossVersion) {
|
|
36890
|
-
|
|
37696
|
+
log5.warn(msg + " (--force-cross-version: continuing anyway)");
|
|
36891
37697
|
return true;
|
|
36892
37698
|
}
|
|
36893
|
-
|
|
37699
|
+
log5.error(msg + " \u2014 pass --force-cross-version to override");
|
|
36894
37700
|
return false;
|
|
36895
37701
|
}
|
|
36896
37702
|
return true;
|
|
@@ -36907,10 +37713,10 @@ async function runExodusMigrate(plan, forceCrossVersion = false, onProgress) {
|
|
|
36907
37713
|
error: `Insufficient disk space for exodus: need \u2265${plan.requiredBytes} bytes (\u2248${STAGING_HEADROOM_FACTOR}\xD7 largest source ${plan.largestSourceBytes} + consolidated estimate ${plan.totalSourceBytes}), but only ${plan.availableBytes} bytes are free on the target filesystem \u2014 ${shortfall} bytes short. Free up at least ${shortfall} bytes (e.g. \`cleo backup prune\`, clear caches), or move the .cleo/ directory to a larger volume, then retry.`
|
|
36908
37714
|
};
|
|
36909
37715
|
}
|
|
36910
|
-
|
|
37716
|
+
mkdirSync3(stagingDir, { recursive: true });
|
|
36911
37717
|
let sqliteVersion = "unknown";
|
|
36912
37718
|
for (const src of sources) {
|
|
36913
|
-
if (
|
|
37719
|
+
if (existsSync9(src.path)) {
|
|
36914
37720
|
const snap = openCleoDbSnapshot(src.path, { readOnly: true });
|
|
36915
37721
|
sqliteVersion = getSqliteVersion(snap.db);
|
|
36916
37722
|
snap.close();
|
|
@@ -36951,15 +37757,15 @@ async function runExodusMigrate(plan, forceCrossVersion = false, onProgress) {
|
|
|
36951
37757
|
};
|
|
36952
37758
|
var extractNativeDb = extractNativeDb2;
|
|
36953
37759
|
for (const src of sources) {
|
|
36954
|
-
if (!
|
|
36955
|
-
const backupDest =
|
|
37760
|
+
if (!existsSync9(src.path)) continue;
|
|
37761
|
+
const backupDest = join11(stagingDir, `${src.name.replace(/[^a-z0-9-]/g, "_")}-backup.db`);
|
|
36956
37762
|
const srcBytes = safeStatBytes(src.path);
|
|
36957
37763
|
const skipStagingCopy = srcBytes > plan.stagingCopyThresholdBytes;
|
|
36958
37764
|
if (skipStagingCopy) {
|
|
36959
37765
|
onProgress?.(
|
|
36960
37766
|
`Skipping full staging copy of ${src.name} (${srcBytes} bytes > ${plan.stagingCopyThresholdBytes} threshold) \u2014 source is archived, not deleted, on success.`
|
|
36961
37767
|
);
|
|
36962
|
-
} else if (!
|
|
37768
|
+
} else if (!existsSync9(backupDest)) {
|
|
36963
37769
|
onProgress?.(`Backing up ${src.name} \u2192 staging dir\u2026`);
|
|
36964
37770
|
copyFileSync3(src.path, backupDest);
|
|
36965
37771
|
backupPaths.push(backupDest);
|
|
@@ -36977,8 +37783,8 @@ async function runExodusMigrate(plan, forceCrossVersion = false, onProgress) {
|
|
|
36977
37783
|
});
|
|
36978
37784
|
const projectNative = extractNativeDb2(projectHandle);
|
|
36979
37785
|
const globalNative = extractNativeDb2(globalHandle);
|
|
36980
|
-
const projectSources = sources.filter((s) => s.targetScope === "project" &&
|
|
36981
|
-
const globalSources = sources.filter((s) => s.targetScope === "global" &&
|
|
37786
|
+
const projectSources = sources.filter((s) => s.targetScope === "project" && existsSync9(s.path));
|
|
37787
|
+
const globalSources = sources.filter((s) => s.targetScope === "global" && existsSync9(s.path));
|
|
36982
37788
|
await migrateScope(
|
|
36983
37789
|
"project",
|
|
36984
37790
|
projectSources,
|
|
@@ -37003,7 +37809,7 @@ async function runExodusMigrate(plan, forceCrossVersion = false, onProgress) {
|
|
|
37003
37809
|
return { ok: true, tables: allTableResults, stagingDir, backupPaths };
|
|
37004
37810
|
} catch (err) {
|
|
37005
37811
|
const error48 = err instanceof Error ? err.message : String(err);
|
|
37006
|
-
|
|
37812
|
+
log5.error({ err }, "Exodus migration failed");
|
|
37007
37813
|
return { ok: false, tables: allTableResults, stagingDir, backupPaths, error: error48 };
|
|
37008
37814
|
} finally {
|
|
37009
37815
|
try {
|
|
@@ -37023,7 +37829,7 @@ async function migrateScope(scope, sources, targetNativeDb, journal, stagingDir,
|
|
|
37023
37829
|
if (sources.length === 0) return;
|
|
37024
37830
|
onProgress?.(`Migrating ${scope}-scope sources\u2026`);
|
|
37025
37831
|
targetNativeDb.exec("PRAGMA foreign_keys = OFF");
|
|
37026
|
-
|
|
37832
|
+
log5.info({ scope }, "Exodus: foreign_keys=OFF for bulk copy (T11533 FK-defer)");
|
|
37027
37833
|
try {
|
|
37028
37834
|
for (let i = 0; i < sources.length; i++) {
|
|
37029
37835
|
const src = sources[i];
|
|
@@ -37090,7 +37896,7 @@ async function migrateScope(scope, sources, targetNativeDb, journal, stagingDir,
|
|
|
37090
37896
|
}
|
|
37091
37897
|
} catch (err) {
|
|
37092
37898
|
const msg = err instanceof Error ? err.message : String(err);
|
|
37093
|
-
|
|
37899
|
+
log5.warn({ tableName, sourceDb: src.name, err }, "Table copy failed \u2014 skipping");
|
|
37094
37900
|
status = "skipped";
|
|
37095
37901
|
errorMsg = msg;
|
|
37096
37902
|
skipped = true;
|
|
@@ -37138,7 +37944,7 @@ async function migrateScope(scope, sources, targetNativeDb, journal, stagingDir,
|
|
|
37138
37944
|
targetNativeDb.exec(`DETACH DATABASE "${attachAlias}"`);
|
|
37139
37945
|
onProgress?.(` [${src.name}] Detached "${attachAlias}"`);
|
|
37140
37946
|
} catch (detachErr) {
|
|
37141
|
-
|
|
37947
|
+
log5.warn(
|
|
37142
37948
|
{ attachAlias, sourceDb: src.name, err: detachErr },
|
|
37143
37949
|
"DETACH failed \u2014 alias will be released on DB close"
|
|
37144
37950
|
);
|
|
@@ -37148,7 +37954,7 @@ async function migrateScope(scope, sources, targetNativeDb, journal, stagingDir,
|
|
|
37148
37954
|
targetNativeDb.exec(`DETACH DATABASE "${crossAlias}"`);
|
|
37149
37955
|
onProgress?.(` [${src.name}] Cross-scope target detached "${crossAlias}"`);
|
|
37150
37956
|
} catch (detachErr) {
|
|
37151
|
-
|
|
37957
|
+
log5.warn(
|
|
37152
37958
|
{ crossAlias, sourceDb: src.name, err: detachErr },
|
|
37153
37959
|
"Cross-scope DETACH failed \u2014 alias will be released on DB close"
|
|
37154
37960
|
);
|
|
@@ -37160,28 +37966,28 @@ async function migrateScope(scope, sources, targetNativeDb, journal, stagingDir,
|
|
|
37160
37966
|
try {
|
|
37161
37967
|
const orphans = targetNativeDb.prepare("PRAGMA foreign_key_check").all();
|
|
37162
37968
|
if (orphans.length > 0) {
|
|
37163
|
-
|
|
37969
|
+
log5.warn(
|
|
37164
37970
|
{ scope, orphanCount: orphans.length, sample: orphans.slice(0, 5) },
|
|
37165
37971
|
`Exodus: PRAGMA foreign_key_check found ${orphans.length} orphan row(s) after bulk copy \u2014 these are genuine data orphans (not ordering artifacts)`
|
|
37166
37972
|
);
|
|
37167
37973
|
} else {
|
|
37168
|
-
|
|
37974
|
+
log5.info({ scope }, "Exodus: PRAGMA foreign_key_check PASSED \u2014 no orphan rows");
|
|
37169
37975
|
}
|
|
37170
37976
|
} catch (checkErr) {
|
|
37171
|
-
|
|
37977
|
+
log5.warn(
|
|
37172
37978
|
{ scope, err: checkErr },
|
|
37173
37979
|
"Exodus: PRAGMA foreign_key_check failed (non-fatal) \u2014 target schema may not have FK constraints enabled"
|
|
37174
37980
|
);
|
|
37175
37981
|
}
|
|
37176
37982
|
try {
|
|
37177
37983
|
targetNativeDb.exec("PRAGMA foreign_keys = ON");
|
|
37178
|
-
|
|
37984
|
+
log5.info({ scope }, "Exodus: foreign_keys=ON restored after bulk copy");
|
|
37179
37985
|
} catch (fkErr) {
|
|
37180
|
-
|
|
37986
|
+
log5.warn({ scope, err: fkErr }, "Exodus: could not restore foreign_keys=ON (non-fatal)");
|
|
37181
37987
|
}
|
|
37182
37988
|
}
|
|
37183
37989
|
}
|
|
37184
|
-
var
|
|
37990
|
+
var log5, LOCK_SENTINEL_SUFFIX, JOURNAL_FILENAME, SOURCE_EPOCH_UNITS;
|
|
37185
37991
|
var init_migrate = __esm({
|
|
37186
37992
|
"packages/core/src/store/exodus/migrate.ts"() {
|
|
37187
37993
|
"use strict";
|
|
@@ -37193,7 +37999,7 @@ var init_migrate = __esm({
|
|
|
37193
37999
|
init_plan2();
|
|
37194
38000
|
init_table_name_map();
|
|
37195
38001
|
init_types();
|
|
37196
|
-
|
|
38002
|
+
log5 = getLogger("exodus-migrate");
|
|
37197
38003
|
LOCK_SENTINEL_SUFFIX = ".exodus-lock";
|
|
37198
38004
|
JOURNAL_FILENAME = "exodus-journal.json";
|
|
37199
38005
|
SOURCE_EPOCH_UNITS = /* @__PURE__ */ new Map([
|
|
@@ -37217,7 +38023,7 @@ function sealExodus(plan, scopeArg, cwd) {
|
|
|
37217
38023
|
const parity = computeCountParity(plan.sources, plan.projectDbPath, plan.globalDbPath);
|
|
37218
38024
|
if (!parity.ok) {
|
|
37219
38025
|
const refusedReason = `Refusing to seal: ${parity.deficits.length} table(s) have FEWER rows in the consolidated cleo.db than the legacy source \u2014 the data is NOT fully migrated. Run \`cleo exodus migrate\` first. Deficits: ${parity.deficits.map((d) => `${d.targetTable}(${d.sourceCount}\u2192${d.targetCount})`).join(", ")}`;
|
|
37220
|
-
|
|
38026
|
+
log6.error({ deficits: parity.deficits.length }, `exodus seal refused \u2014 ${refusedReason}`);
|
|
37221
38027
|
return { ok: false, refusedReason, parity, scopes: [] };
|
|
37222
38028
|
}
|
|
37223
38029
|
const outcomes = [];
|
|
@@ -37233,7 +38039,7 @@ function sealExodus(plan, scopeArg, cwd) {
|
|
|
37233
38039
|
scopeSources.map((s) => s.name),
|
|
37234
38040
|
cwd
|
|
37235
38041
|
);
|
|
37236
|
-
|
|
38042
|
+
log6.info(
|
|
37237
38043
|
{ scope, alreadySealed, archived: archived.filter((a) => a.action === "archived").length },
|
|
37238
38044
|
`exodus seal: scope '${scope}' certified (count-parity verified, ${parity.checked} tables)`
|
|
37239
38045
|
);
|
|
@@ -37241,23 +38047,23 @@ function sealExodus(plan, scopeArg, cwd) {
|
|
|
37241
38047
|
}
|
|
37242
38048
|
return { ok: true, parity, scopes: outcomes };
|
|
37243
38049
|
}
|
|
37244
|
-
var
|
|
38050
|
+
var log6;
|
|
37245
38051
|
var init_seal = __esm({
|
|
37246
38052
|
"packages/core/src/store/exodus/seal.ts"() {
|
|
37247
38053
|
"use strict";
|
|
37248
38054
|
init_logger2();
|
|
37249
38055
|
init_archive2();
|
|
37250
38056
|
init_count_parity();
|
|
37251
|
-
|
|
38057
|
+
log6 = getLogger("exodus-seal");
|
|
37252
38058
|
}
|
|
37253
38059
|
});
|
|
37254
38060
|
|
|
37255
38061
|
// packages/core/src/store/exodus/status.ts
|
|
37256
|
-
import { existsSync as
|
|
37257
|
-
import { join as
|
|
38062
|
+
import { existsSync as existsSync10, readdirSync as readdirSync4, readFileSync as readFileSync3, statSync as statSync4 } from "node:fs";
|
|
38063
|
+
import { join as join12 } from "node:path";
|
|
37258
38064
|
function readJournal2(stagingDir) {
|
|
37259
|
-
const p =
|
|
37260
|
-
if (!
|
|
38065
|
+
const p = join12(stagingDir, JOURNAL_FILENAME2);
|
|
38066
|
+
if (!existsSync10(p)) return null;
|
|
37261
38067
|
try {
|
|
37262
38068
|
return JSON.parse(readFileSync3(p, "utf8"));
|
|
37263
38069
|
} catch {
|
|
@@ -37266,7 +38072,7 @@ function readJournal2(stagingDir) {
|
|
|
37266
38072
|
}
|
|
37267
38073
|
function findStagingDirs(cleoDir) {
|
|
37268
38074
|
try {
|
|
37269
|
-
return
|
|
38075
|
+
return readdirSync4(cleoDir, { withFileTypes: true }).filter((e) => e.isDirectory() && e.name.startsWith("exodus-staging-")).map((e) => join12(cleoDir, e.name)).sort().reverse();
|
|
37270
38076
|
} catch {
|
|
37271
38077
|
return [];
|
|
37272
38078
|
}
|
|
@@ -37289,15 +38095,15 @@ function runExodusStatus(cwd) {
|
|
|
37289
38095
|
const sourcesInfo = plan.sources.map((s) => ({
|
|
37290
38096
|
name: s.name,
|
|
37291
38097
|
path: s.path,
|
|
37292
|
-
exists:
|
|
38098
|
+
exists: existsSync10(s.path),
|
|
37293
38099
|
bytes: safeBytes(s.path)
|
|
37294
38100
|
}));
|
|
37295
38101
|
return {
|
|
37296
38102
|
hasStaging: latestStaging !== null,
|
|
37297
38103
|
stagingDir: latestStaging,
|
|
37298
38104
|
journal,
|
|
37299
|
-
projectDbExists:
|
|
37300
|
-
globalDbExists:
|
|
38105
|
+
projectDbExists: existsSync10(projectDbPath),
|
|
38106
|
+
globalDbExists: existsSync10(globalDbPath),
|
|
37301
38107
|
sourcesPresent: sourcesInfo.some((s) => s.exists),
|
|
37302
38108
|
sources: sourcesInfo
|
|
37303
38109
|
};
|
|
@@ -37314,7 +38120,7 @@ var init_status = __esm({
|
|
|
37314
38120
|
});
|
|
37315
38121
|
|
|
37316
38122
|
// packages/core/src/store/exodus/verify-migration.ts
|
|
37317
|
-
import { existsSync as
|
|
38123
|
+
import { existsSync as existsSync11 } from "node:fs";
|
|
37318
38124
|
import { createRequire as createRequire3 } from "node:module";
|
|
37319
38125
|
function orderByClause(db, tableName) {
|
|
37320
38126
|
try {
|
|
@@ -37334,7 +38140,7 @@ function computeTableDigest(db, tableName, columns, transform2) {
|
|
|
37334
38140
|
count = Number(row?.c ?? 0);
|
|
37335
38141
|
} catch (err) {
|
|
37336
38142
|
const msg = err instanceof Error ? err.message : String(err);
|
|
37337
|
-
|
|
38143
|
+
log7.warn(
|
|
37338
38144
|
{ tableName, err: msg },
|
|
37339
38145
|
"computeTableDigest: COUNT(*) failed (possibly a virtual/FTS table) \u2014 treating as 0 rows"
|
|
37340
38146
|
);
|
|
@@ -37369,7 +38175,7 @@ function computeTableDigest(db, tableName, columns, transform2) {
|
|
|
37369
38175
|
}
|
|
37370
38176
|
} catch (err) {
|
|
37371
38177
|
const msg = err instanceof Error ? err.message : String(err);
|
|
37372
|
-
|
|
38178
|
+
log7.warn(
|
|
37373
38179
|
{ tableName, err: msg },
|
|
37374
38180
|
"computeTableDigest: streamed SELECT failed (possibly a virtual/FTS table) \u2014 digest skipped (COUNT(*) parity still enforced)"
|
|
37375
38181
|
);
|
|
@@ -37495,7 +38301,7 @@ function foreignKeyCheck(db, scope) {
|
|
|
37495
38301
|
try {
|
|
37496
38302
|
const rows = db.prepare("PRAGMA foreign_key_check").all();
|
|
37497
38303
|
if (rows.length > 0) {
|
|
37498
|
-
|
|
38304
|
+
log7.warn(
|
|
37499
38305
|
{ scope, count: rows.length, sample: rows.slice(0, 5) },
|
|
37500
38306
|
`verifyMigration: PRAGMA foreign_key_check found ${rows.length} orphan row(s)`
|
|
37501
38307
|
);
|
|
@@ -37507,7 +38313,7 @@ function foreignKeyCheck(db, scope) {
|
|
|
37507
38313
|
fkid: r.fkid
|
|
37508
38314
|
}));
|
|
37509
38315
|
} catch (err) {
|
|
37510
|
-
|
|
38316
|
+
log7.warn({ scope, err }, "verifyMigration: PRAGMA foreign_key_check failed (non-fatal)");
|
|
37511
38317
|
return [];
|
|
37512
38318
|
}
|
|
37513
38319
|
}
|
|
@@ -37544,13 +38350,13 @@ function sourceOrphanSignatures(db, sourceName, scope) {
|
|
|
37544
38350
|
const rows = db.prepare("PRAGMA foreign_key_check").all();
|
|
37545
38351
|
for (const r of rows) sigs.add(orphanSignature(db, r, sourceName));
|
|
37546
38352
|
if (rows.length > 0) {
|
|
37547
|
-
|
|
38353
|
+
log7.warn(
|
|
37548
38354
|
{ scope, count: rows.length, sample: rows.slice(0, 5) },
|
|
37549
38355
|
`verifyMigration: source already has ${rows.length} pre-existing FK orphan(s) \u2014 these are tolerated (carried forward losslessly, flagged for data-hygiene)`
|
|
37550
38356
|
);
|
|
37551
38357
|
}
|
|
37552
38358
|
} catch (err) {
|
|
37553
|
-
|
|
38359
|
+
log7.warn({ scope, err }, "verifyMigration: source PRAGMA foreign_key_check failed (non-fatal)");
|
|
37554
38360
|
}
|
|
37555
38361
|
return sigs;
|
|
37556
38362
|
}
|
|
@@ -37562,7 +38368,7 @@ function verifyMigration(sources, projectDbPath, globalDbPath, onProgress) {
|
|
|
37562
38368
|
const preExistingForeignKeyViolations = [];
|
|
37563
38369
|
const failureLines = [];
|
|
37564
38370
|
const sourceOrphanSigs = /* @__PURE__ */ new Set();
|
|
37565
|
-
if (!
|
|
38371
|
+
if (!existsSync11(projectDbPath)) {
|
|
37566
38372
|
return {
|
|
37567
38373
|
ok: false,
|
|
37568
38374
|
tables: [],
|
|
@@ -37573,7 +38379,7 @@ function verifyMigration(sources, projectDbPath, globalDbPath, onProgress) {
|
|
|
37573
38379
|
error: `Consolidated project cleo.db not found at ${projectDbPath}. Run 'cleo exodus migrate' first.`
|
|
37574
38380
|
};
|
|
37575
38381
|
}
|
|
37576
|
-
if (!
|
|
38382
|
+
if (!existsSync11(globalDbPath)) {
|
|
37577
38383
|
return {
|
|
37578
38384
|
ok: false,
|
|
37579
38385
|
tables: [],
|
|
@@ -37588,7 +38394,7 @@ function verifyMigration(sources, projectDbPath, globalDbPath, onProgress) {
|
|
|
37588
38394
|
const globalSnap = openCleoDbSnapshot(globalDbPath, { readOnly: true });
|
|
37589
38395
|
try {
|
|
37590
38396
|
for (const src of sources) {
|
|
37591
|
-
if (!
|
|
38397
|
+
if (!existsSync11(src.path)) {
|
|
37592
38398
|
onProgress?.(`Skipping ${src.name} (not present)`);
|
|
37593
38399
|
continue;
|
|
37594
38400
|
}
|
|
@@ -37673,7 +38479,7 @@ function verifyMigration(sources, projectDbPath, globalDbPath, onProgress) {
|
|
|
37673
38479
|
`[${scope}] ${src.name}.${legacyTableName} \u2192 ${targetTableName}: DEFICIT \u2014 source=${srcDigest.count} rows, target=${tgtDigest.count} rows (${srcDigest.count - tgtDigest.count} missing), hashMatch=${hashMatch}`
|
|
37674
38480
|
);
|
|
37675
38481
|
} else if (tgtDigest.count > srcDigest.count) {
|
|
37676
|
-
|
|
38482
|
+
log7.warn(
|
|
37677
38483
|
{
|
|
37678
38484
|
scope,
|
|
37679
38485
|
source: src.name,
|
|
@@ -37723,7 +38529,7 @@ function verifyMigration(sources, projectDbPath, globalDbPath, onProgress) {
|
|
|
37723
38529
|
}
|
|
37724
38530
|
}
|
|
37725
38531
|
if (preExistingForeignKeyViolations.length > 0) {
|
|
37726
|
-
|
|
38532
|
+
log7.warn(
|
|
37727
38533
|
{
|
|
37728
38534
|
count: preExistingForeignKeyViolations.length,
|
|
37729
38535
|
sample: preExistingForeignKeyViolations.slice(0, 5)
|
|
@@ -37733,7 +38539,7 @@ function verifyMigration(sources, projectDbPath, globalDbPath, onProgress) {
|
|
|
37733
38539
|
}
|
|
37734
38540
|
} catch (err) {
|
|
37735
38541
|
const error48 = err instanceof Error ? err.message : String(err);
|
|
37736
|
-
|
|
38542
|
+
log7.error({ err }, "verifyMigration failed");
|
|
37737
38543
|
return {
|
|
37738
38544
|
ok: false,
|
|
37739
38545
|
tables,
|
|
@@ -37750,7 +38556,7 @@ function verifyMigration(sources, projectDbPath, globalDbPath, onProgress) {
|
|
|
37750
38556
|
if (failureLines.length > 0) {
|
|
37751
38557
|
const error48 = `verifyMigration FAILED: ${failureLines.length} issue(s):
|
|
37752
38558
|
${failureLines.map((l) => ` \u2022 ${l}`).join("\n")}`;
|
|
37753
|
-
|
|
38559
|
+
log7.error({ failureCount: failureLines.length }, error48);
|
|
37754
38560
|
return {
|
|
37755
38561
|
ok: false,
|
|
37756
38562
|
tables,
|
|
@@ -37770,7 +38576,7 @@ ${failureLines.map((l) => ` \u2022 ${l}`).join("\n")}`;
|
|
|
37770
38576
|
enumDrift
|
|
37771
38577
|
};
|
|
37772
38578
|
}
|
|
37773
|
-
var
|
|
38579
|
+
var log7, _require, CHECK_ENUM_REGEX;
|
|
37774
38580
|
var init_verify_migration = __esm({
|
|
37775
38581
|
"packages/core/src/store/exodus/verify-migration.ts"() {
|
|
37776
38582
|
"use strict";
|
|
@@ -37779,7 +38585,7 @@ var init_verify_migration = __esm({
|
|
|
37779
38585
|
init_open_cleo_db();
|
|
37780
38586
|
init_column_transforms();
|
|
37781
38587
|
init_table_name_map();
|
|
37782
|
-
|
|
38588
|
+
log7 = getLogger("verify-migration");
|
|
37783
38589
|
_require = createRequire3(import.meta.url);
|
|
37784
38590
|
CHECK_ENUM_REGEX = /CHECK\s*\(\s*"([^"]+)"\s+(?:IS\s+NULL\s+OR\s+"[^"]+"\s+)?IN\s*\(([^)]*)\)\s*\)/gi;
|
|
37785
38591
|
}
|
|
@@ -37871,7 +38677,7 @@ __export(on_open_exports, {
|
|
|
37871
38677
|
isDataContinuityOk: () => isDataContinuityOk,
|
|
37872
38678
|
maybeRunExodusOnOpen: () => maybeRunExodusOnOpen
|
|
37873
38679
|
});
|
|
37874
|
-
import { existsSync as
|
|
38680
|
+
import { existsSync as existsSync12 } from "node:fs";
|
|
37875
38681
|
function isDisabledByEnv() {
|
|
37876
38682
|
const v = process.env.CLEO_DISABLE_EXODUS_ON_OPEN;
|
|
37877
38683
|
return v === "1" || v === "true";
|
|
@@ -37897,7 +38703,7 @@ function rollbackConsolidatedToEmpty(nativeDb, scope) {
|
|
|
37897
38703
|
"SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' AND name NOT LIKE '__drizzle_%'"
|
|
37898
38704
|
).all().map((r) => r.name);
|
|
37899
38705
|
} catch (err) {
|
|
37900
|
-
|
|
38706
|
+
log8.error({ err, scope }, "exodus-on-open: failed to enumerate tables for rollback");
|
|
37901
38707
|
return;
|
|
37902
38708
|
}
|
|
37903
38709
|
try {
|
|
@@ -37907,7 +38713,7 @@ function rollbackConsolidatedToEmpty(nativeDb, scope) {
|
|
|
37907
38713
|
try {
|
|
37908
38714
|
nativeDb.exec(`DELETE FROM "${table}"`);
|
|
37909
38715
|
} catch (err) {
|
|
37910
|
-
|
|
38716
|
+
log8.warn({ err, table, scope }, "exodus-on-open: failed to clear table during rollback");
|
|
37911
38717
|
}
|
|
37912
38718
|
}
|
|
37913
38719
|
nativeDb.exec("COMMIT");
|
|
@@ -37916,7 +38722,7 @@ function rollbackConsolidatedToEmpty(nativeDb, scope) {
|
|
|
37916
38722
|
nativeDb.exec("ROLLBACK");
|
|
37917
38723
|
} catch {
|
|
37918
38724
|
}
|
|
37919
|
-
|
|
38725
|
+
log8.error({ err, scope }, "exodus-on-open: rollback transaction failed");
|
|
37920
38726
|
} finally {
|
|
37921
38727
|
try {
|
|
37922
38728
|
nativeDb.exec("PRAGMA foreign_keys = ON");
|
|
@@ -37936,7 +38742,7 @@ async function rollbackBothScopes(scope, projectDbPath, globalDbPath) {
|
|
|
37936
38742
|
rollbackConsolidatedToEmpty(native, s);
|
|
37937
38743
|
}
|
|
37938
38744
|
} catch (err) {
|
|
37939
|
-
|
|
38745
|
+
log8.warn(
|
|
37940
38746
|
{ err, scope: s, openingScope: scope },
|
|
37941
38747
|
"exodus-on-open: could not roll back scope (best-effort)"
|
|
37942
38748
|
);
|
|
@@ -37952,7 +38758,7 @@ function isDataContinuityOk(result) {
|
|
|
37952
38758
|
const deficits = result.tables.filter((t) => t.targetCount < t.sourceCount);
|
|
37953
38759
|
const surpluses = result.tables.filter((t) => t.targetCount > t.sourceCount);
|
|
37954
38760
|
if (surpluses.length > 0) {
|
|
37955
|
-
|
|
38761
|
+
log8.warn(
|
|
37956
38762
|
{
|
|
37957
38763
|
surpluses: surpluses.map((t) => ({
|
|
37958
38764
|
table: t.targetTable,
|
|
@@ -37986,7 +38792,7 @@ async function maybeRunExodusOnOpen(scope, dbPath, nativeDb, cwd) {
|
|
|
37986
38792
|
const { buildExodusPlan: buildExodusPlan2, runExodusMigrate: runExodusMigrate2, verifyMigration: verifyMigration2, clearExodusJournal: clearExodusJournal2 } = await Promise.resolve().then(() => (init_exodus(), exodus_exports));
|
|
37987
38793
|
const plan = buildExodusPlan2(cwd);
|
|
37988
38794
|
const scopeSources = plan.sources.filter((s) => s.targetScope === scope);
|
|
37989
|
-
if (!scopeSources.some((s) =>
|
|
38795
|
+
if (!scopeSources.some((s) => existsSync12(s.path))) {
|
|
37990
38796
|
return {
|
|
37991
38797
|
outcome: "skipped",
|
|
37992
38798
|
reason: `no legacy ${scope}-scope source DBs present (fresh install or cross-scope-only)`
|
|
@@ -37999,11 +38805,11 @@ async function maybeRunExodusOnOpen(scope, dbPath, nativeDb, cwd) {
|
|
|
37999
38805
|
if (!consolidatedIsEmpty(nativeDb, scope)) {
|
|
38000
38806
|
return { outcome: "skipped", reason: "migrated by a concurrent process (lock winner)" };
|
|
38001
38807
|
}
|
|
38002
|
-
|
|
38808
|
+
log8.info(
|
|
38003
38809
|
{
|
|
38004
38810
|
scope,
|
|
38005
38811
|
dbPath,
|
|
38006
|
-
sources: plan.sources.filter((s) =>
|
|
38812
|
+
sources: plan.sources.filter((s) => existsSync12(s.path)).map((s) => s.name)
|
|
38007
38813
|
},
|
|
38008
38814
|
"exodus-on-open: consolidated cleo.db is empty and legacy data present \u2014 auto-migrating"
|
|
38009
38815
|
);
|
|
@@ -38012,23 +38818,23 @@ async function maybeRunExodusOnOpen(scope, dbPath, nativeDb, cwd) {
|
|
|
38012
38818
|
const migrateResult = await runExodusMigrate2(
|
|
38013
38819
|
plan,
|
|
38014
38820
|
false,
|
|
38015
|
-
(msg) =>
|
|
38821
|
+
(msg) => log8.debug({ scope }, `exodus-on-open: ${msg}`)
|
|
38016
38822
|
);
|
|
38017
38823
|
if (!migrateResult.ok) {
|
|
38018
38824
|
await rollbackBothScopes(scope, plan.projectDbPath, plan.globalDbPath);
|
|
38019
38825
|
clearExodusJournal2(migrateResult.stagingDir);
|
|
38020
38826
|
const reason = `migration failed: ${migrateResult.error ?? "unknown error"} \u2014 legacy DBs kept as source`;
|
|
38021
|
-
|
|
38827
|
+
log8.error({ scope, error: migrateResult.error }, `exodus-on-open: ${reason}`);
|
|
38022
38828
|
return { outcome: "aborted", reason };
|
|
38023
38829
|
}
|
|
38024
38830
|
const verifyResult = verifyMigration2(
|
|
38025
38831
|
plan.sources,
|
|
38026
38832
|
plan.projectDbPath,
|
|
38027
38833
|
plan.globalDbPath,
|
|
38028
|
-
(msg) =>
|
|
38834
|
+
(msg) => log8.debug({ scope }, `exodus-on-open verify: ${msg}`)
|
|
38029
38835
|
);
|
|
38030
38836
|
if (!verifyResult.ok) {
|
|
38031
|
-
|
|
38837
|
+
log8.warn(
|
|
38032
38838
|
{
|
|
38033
38839
|
scope,
|
|
38034
38840
|
enumDrift: verifyResult.enumDrift.length,
|
|
@@ -38042,7 +38848,7 @@ async function maybeRunExodusOnOpen(scope, dbPath, nativeDb, cwd) {
|
|
|
38042
38848
|
clearExodusJournal2(plan.stagingDir);
|
|
38043
38849
|
const deficits = verifyResult.tables.filter((t) => t.targetCount < t.sourceCount).map((t) => `${t.targetTable}(${t.sourceCount}\u2192${t.targetCount})`);
|
|
38044
38850
|
const reason = `parity verification failed \u2014 cutover aborted, legacy DBs kept as source. count deficits: [${deficits.join(", ")}]; INTRODUCED fk orphans: ${verifyResult.introducedForeignKeyViolations.length} (pre-existing source orphans tolerated: ${verifyResult.preExistingForeignKeyViolations.length}). ` + `${verifyResult.error ?? ""}`.trim();
|
|
38045
|
-
|
|
38851
|
+
log8.error(
|
|
38046
38852
|
{
|
|
38047
38853
|
scope,
|
|
38048
38854
|
countDeficits: deficits,
|
|
@@ -38054,14 +38860,14 @@ async function maybeRunExodusOnOpen(scope, dbPath, nativeDb, cwd) {
|
|
|
38054
38860
|
return { outcome: "aborted", reason };
|
|
38055
38861
|
}
|
|
38056
38862
|
const rowsCopied = migrateResult.tables.filter((t) => !t.skipped).reduce((n, t) => n + t.rowsCopied, 0);
|
|
38057
|
-
|
|
38863
|
+
log8.info(
|
|
38058
38864
|
{ scope, rowsCopied, tables: migrateResult.tables.length },
|
|
38059
38865
|
"exodus-on-open: parity verified \u2014 legacy data migrated into consolidated cleo.db"
|
|
38060
38866
|
);
|
|
38061
38867
|
try {
|
|
38062
|
-
const consumed = plan.sources.filter((s) =>
|
|
38868
|
+
const consumed = plan.sources.filter((s) => existsSync12(s.path));
|
|
38063
38869
|
const archiveResult = archiveMigratedSources(consumed, cwd);
|
|
38064
|
-
|
|
38870
|
+
log8.info(
|
|
38065
38871
|
{
|
|
38066
38872
|
scope,
|
|
38067
38873
|
archived: archiveResult.sources.filter((s) => s.action === "archived").length,
|
|
@@ -38070,7 +38876,7 @@ async function maybeRunExodusOnOpen(scope, dbPath, nativeDb, cwd) {
|
|
|
38070
38876
|
"exodus-on-open: archived legacy sources + sealed completion marker(s)"
|
|
38071
38877
|
);
|
|
38072
38878
|
} catch (err) {
|
|
38073
|
-
|
|
38879
|
+
log8.error(
|
|
38074
38880
|
{ err, scope },
|
|
38075
38881
|
"exodus-on-open: post-migration archive/marker step failed (migration itself succeeded \u2014 legacy DBs left in place, will be re-checked by `cleo doctor exodus-residue`)"
|
|
38076
38882
|
);
|
|
@@ -38092,14 +38898,14 @@ async function maybeRunExodusOnOpen(scope, dbPath, nativeDb, cwd) {
|
|
|
38092
38898
|
function _isExodusInProgress() {
|
|
38093
38899
|
return _exodusInProgress;
|
|
38094
38900
|
}
|
|
38095
|
-
var
|
|
38901
|
+
var log8, _exodusInProgress;
|
|
38096
38902
|
var init_on_open = __esm({
|
|
38097
38903
|
"packages/core/src/store/exodus/on-open.ts"() {
|
|
38098
38904
|
"use strict";
|
|
38099
38905
|
init_logger2();
|
|
38100
38906
|
init_lock();
|
|
38101
38907
|
init_archive2();
|
|
38102
|
-
|
|
38908
|
+
log8 = getLogger("exodus-on-open");
|
|
38103
38909
|
_exodusInProgress = false;
|
|
38104
38910
|
}
|
|
38105
38911
|
});
|
|
@@ -38116,9 +38922,9 @@ __export(dual_scope_db_exports, {
|
|
|
38116
38922
|
resolveDualScopeDbPath: () => resolveDualScopeDbPath,
|
|
38117
38923
|
upsertIdempotent: () => upsertIdempotent
|
|
38118
38924
|
});
|
|
38119
|
-
import { existsSync as
|
|
38925
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync4 } from "node:fs";
|
|
38120
38926
|
import { createRequire as createRequire4 } from "node:module";
|
|
38121
|
-
import { dirname as dirname5, join as
|
|
38927
|
+
import { dirname as dirname5, join as join13 } from "node:path";
|
|
38122
38928
|
function assertWriteDurable(handle) {
|
|
38123
38929
|
if (handle.exodusAbort) {
|
|
38124
38930
|
throw new ExodusAbortWriteUnsafeError(handle.exodusAbort);
|
|
@@ -38135,9 +38941,9 @@ function cacheKey(scope, dbPath) {
|
|
|
38135
38941
|
}
|
|
38136
38942
|
function resolveDualScopeDbPath(scope, cwd) {
|
|
38137
38943
|
if (scope === "project") {
|
|
38138
|
-
return
|
|
38944
|
+
return join13(resolveCleoDir(cwd), "cleo.db");
|
|
38139
38945
|
}
|
|
38140
|
-
return
|
|
38946
|
+
return join13(getCleoHome(), "cleo.db");
|
|
38141
38947
|
}
|
|
38142
38948
|
function migrationsSetName(scope) {
|
|
38143
38949
|
return scope === "project" ? "drizzle-cleo-project" : "drizzle-cleo-global";
|
|
@@ -38180,11 +38986,11 @@ async function openDualScopeDb(scope, cwd) {
|
|
|
38180
38986
|
const dbPath = resolveDualScopeDbPath(scope, cwd);
|
|
38181
38987
|
return scope === "project" ? openDualScopeDbAtPath("project", dbPath, cwd) : openDualScopeDbAtPath("global", dbPath, cwd);
|
|
38182
38988
|
}
|
|
38183
|
-
async function openDedicatedDualScopeDb(scope, dbPath,
|
|
38184
|
-
|
|
38989
|
+
async function openDedicatedDualScopeDb(scope, dbPath, log9) {
|
|
38990
|
+
log9.debug({ scope, dbPath }, "opening DEDICATED (non-cached) dual-scope cleo.db (T11782 FIX D)");
|
|
38185
38991
|
const dir = dirname5(dbPath);
|
|
38186
|
-
if (!
|
|
38187
|
-
|
|
38992
|
+
if (!existsSync13(dir)) {
|
|
38993
|
+
mkdirSync4(dir, { recursive: true });
|
|
38188
38994
|
}
|
|
38189
38995
|
const DatabaseSyncCtor = getDatabaseSyncCtor();
|
|
38190
38996
|
const nativeDb = new DatabaseSyncCtor(dbPath, { allowExtension: true });
|
|
@@ -38207,7 +39013,7 @@ async function openDedicatedDualScopeDb(scope, dbPath, log8) {
|
|
|
38207
39013
|
existenceTable(scope),
|
|
38208
39014
|
`dual-scope-db[${scope}]`
|
|
38209
39015
|
);
|
|
38210
|
-
|
|
39016
|
+
log9.debug({ scope, dbPath }, "DEDICATED dual-scope cleo.db ready (T11782 FIX D)");
|
|
38211
39017
|
return {
|
|
38212
39018
|
db,
|
|
38213
39019
|
scope,
|
|
@@ -38232,15 +39038,15 @@ async function openDualScopeDbAtPath(scope, dbPath, exodusCwd, options) {
|
|
|
38232
39038
|
return existing.handle;
|
|
38233
39039
|
}
|
|
38234
39040
|
}
|
|
38235
|
-
const
|
|
39041
|
+
const log9 = getLogger("dual-scope-db");
|
|
38236
39042
|
if (dedicated) {
|
|
38237
|
-
return openDedicatedDualScopeDb(scope, dbPath,
|
|
39043
|
+
return openDedicatedDualScopeDb(scope, dbPath, log9);
|
|
38238
39044
|
}
|
|
38239
39045
|
const initPromise = (async () => {
|
|
38240
|
-
|
|
39046
|
+
log9.debug({ scope, dbPath }, "opening dual-scope cleo.db");
|
|
38241
39047
|
const dir = dirname5(dbPath);
|
|
38242
|
-
if (!
|
|
38243
|
-
|
|
39048
|
+
if (!existsSync13(dir)) {
|
|
39049
|
+
mkdirSync4(dir, { recursive: true });
|
|
38244
39050
|
}
|
|
38245
39051
|
const DatabaseSyncCtor = getDatabaseSyncCtor();
|
|
38246
39052
|
const nativeDb = new DatabaseSyncCtor(dbPath, { allowExtension: true });
|
|
@@ -38267,7 +39073,7 @@ async function openDualScopeDbAtPath(scope, dbPath, exodusCwd, options) {
|
|
|
38267
39073
|
existenceTable(scope),
|
|
38268
39074
|
`dual-scope-db[${scope}]`
|
|
38269
39075
|
);
|
|
38270
|
-
|
|
39076
|
+
log9.debug({ scope, dbPath }, "dual-scope cleo.db ready");
|
|
38271
39077
|
const built = {
|
|
38272
39078
|
db,
|
|
38273
39079
|
scope,
|
|
@@ -38293,6 +39099,25 @@ async function openDualScopeDbAtPath(scope, dbPath, exodusCwd, options) {
|
|
|
38293
39099
|
{ dbPath }
|
|
38294
39100
|
);
|
|
38295
39101
|
if (exodusCwd !== void 0 && dbPath === resolveDualScopeDbPath(scope, exodusCwd)) {
|
|
39102
|
+
let releaseDbHeavy = null;
|
|
39103
|
+
let dbHeavyDeferred = false;
|
|
39104
|
+
try {
|
|
39105
|
+
const { governor: governor2 } = await Promise.resolve().then(() => (init_governor(), governor_exports));
|
|
39106
|
+
const admit = await governor2.tryAcquire("db-heavy");
|
|
39107
|
+
if (admit.deferred) {
|
|
39108
|
+
dbHeavyDeferred = true;
|
|
39109
|
+
} else {
|
|
39110
|
+
releaseDbHeavy = admit.release;
|
|
39111
|
+
}
|
|
39112
|
+
} catch {
|
|
39113
|
+
}
|
|
39114
|
+
if (dbHeavyDeferred) {
|
|
39115
|
+
log9.debug(
|
|
39116
|
+
{ scope, dbPath },
|
|
39117
|
+
"exodus-on-open skipped this open \u2014 db-heavy deferred under memory pressure (re-runs idempotently on a calmer open)"
|
|
39118
|
+
);
|
|
39119
|
+
return handle;
|
|
39120
|
+
}
|
|
38296
39121
|
try {
|
|
38297
39122
|
const { maybeRunExodusOnOpen: maybeRunExodusOnOpen2 } = await Promise.resolve().then(() => (init_on_open(), on_open_exports));
|
|
38298
39123
|
const result = await maybeRunExodusOnOpen2(scope, dbPath, nativeDb, exodusCwd);
|
|
@@ -38305,7 +39130,7 @@ async function openDualScopeDbAtPath(scope, dbPath, exodusCwd, options) {
|
|
|
38305
39130
|
reason: result.reason,
|
|
38306
39131
|
at: Date.now()
|
|
38307
39132
|
};
|
|
38308
|
-
|
|
39133
|
+
log9.warn(
|
|
38309
39134
|
{ scope, reason: result.reason },
|
|
38310
39135
|
"exodus-on-open aborted; consolidated cleo.db left empty, legacy kept as source \u2014 mutating callers must check handle.exodusAbort / call assertWriteDurable (T11828)"
|
|
38311
39136
|
);
|
|
@@ -38318,11 +39143,14 @@ async function openDualScopeDbAtPath(scope, dbPath, exodusCwd, options) {
|
|
|
38318
39143
|
return reopened;
|
|
38319
39144
|
}
|
|
38320
39145
|
} catch (err) {
|
|
38321
|
-
|
|
39146
|
+
log9.warn(
|
|
38322
39147
|
{ err, scope },
|
|
38323
39148
|
"exodus-on-open hook failed (non-fatal); re-opening consolidated handle"
|
|
38324
39149
|
);
|
|
38325
39150
|
return scope === "project" ? openDualScopeDbAtPath("project", dbPath) : openDualScopeDbAtPath("global", dbPath);
|
|
39151
|
+
} finally {
|
|
39152
|
+
if (releaseDbHeavy) await releaseDbHeavy().catch(() => {
|
|
39153
|
+
});
|
|
38326
39154
|
}
|
|
38327
39155
|
}
|
|
38328
39156
|
return handle;
|
|
@@ -38945,8 +39773,8 @@ __export(nexus_sqlite_exports, {
|
|
|
38945
39773
|
resetNexusDbState: () => resetNexusDbState,
|
|
38946
39774
|
resolveNexusMigrationsFolder: () => resolveNexusMigrationsFolder
|
|
38947
39775
|
});
|
|
38948
|
-
import { copyFileSync as copyFileSync4, existsSync as
|
|
38949
|
-
import { join as
|
|
39776
|
+
import { copyFileSync as copyFileSync4, existsSync as existsSync14 } from "node:fs";
|
|
39777
|
+
import { join as join14 } from "node:path";
|
|
38950
39778
|
function getNexusDbPath(cwd) {
|
|
38951
39779
|
return resolveDualScopeDbPath("project", cwd);
|
|
38952
39780
|
}
|
|
@@ -38978,7 +39806,7 @@ function resolveNexusMigrationsFolder() {
|
|
|
38978
39806
|
return resolveCorePackageMigrationsFolder("drizzle-nexus");
|
|
38979
39807
|
}
|
|
38980
39808
|
function getNestedNexusSentinelPath() {
|
|
38981
|
-
return
|
|
39809
|
+
return join14(getCleoHome(), "nexus", NESTED_NEXUS_SENTINEL);
|
|
38982
39810
|
}
|
|
38983
39811
|
function detectAndWarnOnNestedNexus() {
|
|
38984
39812
|
let nestedPath;
|
|
@@ -38987,12 +39815,12 @@ function detectAndWarnOnNestedNexus() {
|
|
|
38987
39815
|
} catch {
|
|
38988
39816
|
return false;
|
|
38989
39817
|
}
|
|
38990
|
-
if (!
|
|
39818
|
+
if (!existsSync14(nestedPath)) return false;
|
|
38991
39819
|
if (_warnedNestedPaths.has(nestedPath)) return false;
|
|
38992
39820
|
_warnedNestedPaths.add(nestedPath);
|
|
38993
39821
|
const canonicalPath = getNexusDbPath();
|
|
38994
|
-
const
|
|
38995
|
-
|
|
39822
|
+
const log9 = getLogger("nexus-sqlite");
|
|
39823
|
+
log9.warn(
|
|
38996
39824
|
{
|
|
38997
39825
|
nestedPath,
|
|
38998
39826
|
canonicalPath,
|
|
@@ -39146,7 +39974,7 @@ function runNexusMigrations(nativeDb, db) {
|
|
|
39146
39974
|
const migrationsFolder = resolveNexusMigrationsFolder();
|
|
39147
39975
|
if (tableExists4(nativeDb, "nexus_nodes") && _nexusDbPath) {
|
|
39148
39976
|
const backupPath = _nexusDbPath.replace(/\.db$/, "-pre-cleo.db.bak");
|
|
39149
|
-
if (!
|
|
39977
|
+
if (!existsSync14(backupPath)) {
|
|
39150
39978
|
try {
|
|
39151
39979
|
copyFileSync4(_nexusDbPath, backupPath);
|
|
39152
39980
|
} catch {
|
|
@@ -39284,10 +40112,10 @@ var init_nexus_sqlite = __esm({
|
|
|
39284
40112
|
|
|
39285
40113
|
// packages/core/src/paths.ts
|
|
39286
40114
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
39287
|
-
import { existsSync as
|
|
40115
|
+
import { existsSync as existsSync15, readFileSync as readFileSync4, statSync as statSync5 } from "node:fs";
|
|
39288
40116
|
import { createRequire as createRequire5 } from "node:module";
|
|
39289
40117
|
import { homedir } from "node:os";
|
|
39290
|
-
import { basename as basename4, dirname as dirname6, join as
|
|
40118
|
+
import { basename as basename4, dirname as dirname6, join as join15, resolve as resolve3 } from "node:path";
|
|
39291
40119
|
import {
|
|
39292
40120
|
computeCanonicalProjectId as _computeCanonicalProjectId,
|
|
39293
40121
|
getCanonicalTemplatesTildePath as _getCanonicalTemplatesTildePath,
|
|
@@ -39377,8 +40205,8 @@ function getCleoDirAbsolute(cwd, opts) {
|
|
|
39377
40205
|
function _resolveProjectByCwdFromNexus(cwd) {
|
|
39378
40206
|
try {
|
|
39379
40207
|
const cleoHome = getCleoHome();
|
|
39380
|
-
const globalDbPath =
|
|
39381
|
-
if (!
|
|
40208
|
+
const globalDbPath = join15(cleoHome, "cleo.db");
|
|
40209
|
+
if (!existsSync15(globalDbPath)) return null;
|
|
39382
40210
|
const start = resolve3(cwd ?? process.cwd());
|
|
39383
40211
|
let current = start;
|
|
39384
40212
|
const DatabaseSync2 = _getDatabaseSyncCtor();
|
|
@@ -39417,7 +40245,7 @@ function _findCleoDirRoot(cwd) {
|
|
|
39417
40245
|
const isDangerousRoot = current === homeRoot || current === "/" || current === "";
|
|
39418
40246
|
if (!isDangerousRoot) {
|
|
39419
40247
|
try {
|
|
39420
|
-
if (statSync5(
|
|
40248
|
+
if (statSync5(join15(current, ".cleo")).isDirectory()) {
|
|
39421
40249
|
return current;
|
|
39422
40250
|
}
|
|
39423
40251
|
} catch {
|
|
@@ -39432,7 +40260,7 @@ function _findCleoDirRoot(cwd) {
|
|
|
39432
40260
|
function resolveCleoDir(cwd) {
|
|
39433
40261
|
const scope = worktreeScope.getStore();
|
|
39434
40262
|
if (scope !== void 0) {
|
|
39435
|
-
return
|
|
40263
|
+
return join15(scope.worktreeRoot, ".cleo");
|
|
39436
40264
|
}
|
|
39437
40265
|
const override = _cleoDirEnvOverride();
|
|
39438
40266
|
if (override !== null) {
|
|
@@ -39440,14 +40268,14 @@ function resolveCleoDir(cwd) {
|
|
|
39440
40268
|
}
|
|
39441
40269
|
const root = _findCleoDirRoot(cwd);
|
|
39442
40270
|
if (root !== null) {
|
|
39443
|
-
return
|
|
40271
|
+
return join15(root, ".cleo");
|
|
39444
40272
|
}
|
|
39445
40273
|
const start = resolve3(cwd ?? process.cwd());
|
|
39446
40274
|
let current = start;
|
|
39447
40275
|
while (true) {
|
|
39448
40276
|
const mainRepo = _resolveMainRepoFromGitlink(current);
|
|
39449
40277
|
if (mainRepo !== null) {
|
|
39450
|
-
return
|
|
40278
|
+
return join15(mainRepo, ".cleo");
|
|
39451
40279
|
}
|
|
39452
40280
|
const parent = dirname6(current);
|
|
39453
40281
|
if (parent === current) {
|
|
@@ -39489,9 +40317,9 @@ function _cwdHasGitAncestor(cwd) {
|
|
|
39489
40317
|
const start = resolve3(cwd ?? process.cwd());
|
|
39490
40318
|
let current = start;
|
|
39491
40319
|
while (true) {
|
|
39492
|
-
const gitMarker =
|
|
40320
|
+
const gitMarker = join15(current, ".git");
|
|
39493
40321
|
try {
|
|
39494
|
-
if (
|
|
40322
|
+
if (existsSync15(gitMarker)) {
|
|
39495
40323
|
return true;
|
|
39496
40324
|
}
|
|
39497
40325
|
} catch {
|
|
@@ -39503,16 +40331,16 @@ function _cwdHasGitAncestor(cwd) {
|
|
|
39503
40331
|
}
|
|
39504
40332
|
function _resolveMainRepoFromGitlink(gitlinkDir) {
|
|
39505
40333
|
try {
|
|
39506
|
-
const gitLinkPath =
|
|
39507
|
-
if (!
|
|
39508
|
-
const
|
|
39509
|
-
if (!
|
|
40334
|
+
const gitLinkPath = join15(gitlinkDir, ".git");
|
|
40335
|
+
if (!existsSync15(gitLinkPath)) return null;
|
|
40336
|
+
const stat3 = statSync5(gitLinkPath);
|
|
40337
|
+
if (!stat3.isFile()) return null;
|
|
39510
40338
|
const gitLinkContent = readFileSync4(gitLinkPath, "utf-8").trim();
|
|
39511
40339
|
const match = gitLinkContent.match(/^gitdir:\s*(.+)$/m);
|
|
39512
40340
|
if (!match) return null;
|
|
39513
40341
|
const gitdir = match[1].trim();
|
|
39514
40342
|
const mainRepo = dirname6(dirname6(dirname6(gitdir)));
|
|
39515
|
-
if (
|
|
40343
|
+
if (existsSync15(join15(mainRepo, ".cleo")) && validateProjectRoot(mainRepo)) {
|
|
39516
40344
|
return mainRepo;
|
|
39517
40345
|
}
|
|
39518
40346
|
} catch {
|
|
@@ -39520,18 +40348,18 @@ function _resolveMainRepoFromGitlink(gitlinkDir) {
|
|
|
39520
40348
|
return null;
|
|
39521
40349
|
}
|
|
39522
40350
|
function validateProjectRoot(candidate) {
|
|
39523
|
-
const cleoDir =
|
|
39524
|
-
if (!
|
|
40351
|
+
const cleoDir = join15(candidate, ".cleo");
|
|
40352
|
+
if (!existsSync15(cleoDir)) {
|
|
39525
40353
|
return false;
|
|
39526
40354
|
}
|
|
39527
|
-
const projectInfoPath =
|
|
39528
|
-
if (
|
|
40355
|
+
const projectInfoPath = join15(cleoDir, "project-info.json");
|
|
40356
|
+
if (existsSync15(projectInfoPath)) {
|
|
39529
40357
|
try {
|
|
39530
40358
|
const raw = readFileSync4(projectInfoPath, "utf-8");
|
|
39531
40359
|
const parsed = JSON.parse(raw);
|
|
39532
40360
|
if (typeof parsed === "object" && parsed !== null && "projectId" in parsed && typeof parsed["projectId"] === "string" && parsed["projectId"] !== "") {
|
|
39533
|
-
const gitMarker =
|
|
39534
|
-
if (
|
|
40361
|
+
const gitMarker = join15(candidate, ".git");
|
|
40362
|
+
if (existsSync15(gitMarker)) {
|
|
39535
40363
|
try {
|
|
39536
40364
|
if (!statSync5(gitMarker).isDirectory()) {
|
|
39537
40365
|
return false;
|
|
@@ -39545,12 +40373,12 @@ function validateProjectRoot(candidate) {
|
|
|
39545
40373
|
} catch {
|
|
39546
40374
|
}
|
|
39547
40375
|
}
|
|
39548
|
-
const gitDir =
|
|
39549
|
-
if (
|
|
40376
|
+
const gitDir = join15(candidate, ".git");
|
|
40377
|
+
if (existsSync15(gitDir)) {
|
|
39550
40378
|
let isRealGitDir = false;
|
|
39551
40379
|
try {
|
|
39552
|
-
const
|
|
39553
|
-
isRealGitDir =
|
|
40380
|
+
const stat3 = statSync5(gitDir);
|
|
40381
|
+
isRealGitDir = stat3.isDirectory();
|
|
39554
40382
|
} catch {
|
|
39555
40383
|
isRealGitDir = false;
|
|
39556
40384
|
}
|
|
@@ -39593,16 +40421,16 @@ function getProjectRoot(cwd) {
|
|
|
39593
40421
|
const homeRoot = homedir();
|
|
39594
40422
|
const skippedCleoDirs = [];
|
|
39595
40423
|
while (true) {
|
|
39596
|
-
const cleoDir =
|
|
39597
|
-
const gitDir =
|
|
40424
|
+
const cleoDir = join15(current, ".cleo");
|
|
40425
|
+
const gitDir = join15(current, ".git");
|
|
39598
40426
|
const isDangerousRoot = current === homeRoot || current === "/" || current === "";
|
|
39599
|
-
if (
|
|
40427
|
+
if (existsSync15(cleoDir) && !isDangerousRoot) {
|
|
39600
40428
|
if (validateProjectRoot(current)) {
|
|
39601
40429
|
return current;
|
|
39602
40430
|
}
|
|
39603
40431
|
skippedCleoDirs.push(current);
|
|
39604
40432
|
}
|
|
39605
|
-
if (
|
|
40433
|
+
if (existsSync15(gitDir) && !isDangerousRoot) {
|
|
39606
40434
|
let isRealGitDir = false;
|
|
39607
40435
|
try {
|
|
39608
40436
|
isRealGitDir = statSync5(gitDir).isDirectory();
|
|
@@ -39647,18 +40475,18 @@ function resolveOrCwd(maybeRoot) {
|
|
|
39647
40475
|
return getProjectRoot();
|
|
39648
40476
|
}
|
|
39649
40477
|
function getConfigPath(cwd) {
|
|
39650
|
-
return
|
|
40478
|
+
return join15(_resolveCleoDir(cwd), "config.json");
|
|
39651
40479
|
}
|
|
39652
40480
|
function getGlobalConfigPath() {
|
|
39653
|
-
return
|
|
40481
|
+
return join15(getCleoHome(), "config.json");
|
|
39654
40482
|
}
|
|
39655
40483
|
function isAbsolutePath(path) {
|
|
39656
40484
|
return _isAbsolutePath(path);
|
|
39657
40485
|
}
|
|
39658
40486
|
function _readProjectNameFromInfo(projectRoot) {
|
|
39659
40487
|
try {
|
|
39660
|
-
const infoPath =
|
|
39661
|
-
if (!
|
|
40488
|
+
const infoPath = join15(projectRoot, ".cleo", "project-info.json");
|
|
40489
|
+
if (!existsSync15(infoPath)) return void 0;
|
|
39662
40490
|
const raw = readFileSync4(infoPath, "utf-8");
|
|
39663
40491
|
const data = JSON.parse(raw);
|
|
39664
40492
|
return typeof data.name === "string" && data.name.length > 0 ? data.name : void 0;
|
|
@@ -39684,8 +40512,8 @@ async function registerProjectOnEncounter(projectRoot, infoProjectId) {
|
|
|
39684
40512
|
if (existingRows.length > 0) {
|
|
39685
40513
|
const existingPath = existingRows[0].projectPath;
|
|
39686
40514
|
if (existingPath !== resolvedPath) {
|
|
39687
|
-
const newBrainDbPath =
|
|
39688
|
-
const newTasksDbPath =
|
|
40515
|
+
const newBrainDbPath = join15(resolvedPath, ".cleo", "brain.db");
|
|
40516
|
+
const newTasksDbPath = join15(resolvedPath, ".cleo", "tasks.db");
|
|
39689
40517
|
await db.update(projectRegistry2).set({
|
|
39690
40518
|
projectPath: resolvedPath,
|
|
39691
40519
|
projectHash: generateProjectHash2(resolvedPath),
|
|
@@ -39716,8 +40544,8 @@ async function registerProjectOnEncounter(projectRoot, infoProjectId) {
|
|
|
39716
40544
|
lastSync: now,
|
|
39717
40545
|
taskCount: 0,
|
|
39718
40546
|
labelsJson: "[]",
|
|
39719
|
-
brainDbPath:
|
|
39720
|
-
tasksDbPath:
|
|
40547
|
+
brainDbPath: join15(resolvedPath, ".cleo", "brain.db"),
|
|
40548
|
+
tasksDbPath: join15(resolvedPath, ".cleo", "tasks.db"),
|
|
39721
40549
|
statsJson: "{}"
|
|
39722
40550
|
}).onConflictDoNothing();
|
|
39723
40551
|
const aliases = canonicalResult.legacyAliases ?? [];
|
|
@@ -39750,46 +40578,46 @@ var init_paths = __esm({
|
|
|
39750
40578
|
// packages/core/src/store/file-utils.ts
|
|
39751
40579
|
import { randomBytes } from "node:crypto";
|
|
39752
40580
|
import {
|
|
39753
|
-
existsSync as
|
|
39754
|
-
mkdirSync as
|
|
39755
|
-
readdirSync as
|
|
40581
|
+
existsSync as existsSync16,
|
|
40582
|
+
mkdirSync as mkdirSync5,
|
|
40583
|
+
readdirSync as readdirSync5,
|
|
39756
40584
|
readFileSync as readFileSync5,
|
|
39757
40585
|
renameSync as renameSync3,
|
|
39758
40586
|
unlinkSync as unlinkSync3,
|
|
39759
|
-
writeFileSync as
|
|
40587
|
+
writeFileSync as writeFileSync4
|
|
39760
40588
|
} from "node:fs";
|
|
39761
|
-
import { basename as basename5, dirname as dirname7, join as
|
|
39762
|
-
import * as
|
|
40589
|
+
import { basename as basename5, dirname as dirname7, join as join16 } from "node:path";
|
|
40590
|
+
import * as lockfile3 from "proper-lockfile";
|
|
39763
40591
|
function rotateBackup(filePath, mode) {
|
|
39764
40592
|
const dir = dirname7(filePath);
|
|
39765
40593
|
const name15 = basename5(filePath);
|
|
39766
|
-
const backupDir =
|
|
40594
|
+
const backupDir = join16(dir, ".backups");
|
|
39767
40595
|
const dirMode = typeof mode === "number" ? modeToDirMode(mode) : void 0;
|
|
39768
|
-
if (!
|
|
39769
|
-
|
|
40596
|
+
if (!existsSync16(backupDir)) {
|
|
40597
|
+
mkdirSync5(backupDir, { recursive: true, mode: dirMode });
|
|
39770
40598
|
}
|
|
39771
40599
|
for (let i = MAX_BACKUPS; i >= 1; i--) {
|
|
39772
|
-
const current =
|
|
40600
|
+
const current = join16(backupDir, `${name15}.${i}`);
|
|
39773
40601
|
if (i === MAX_BACKUPS) {
|
|
39774
40602
|
try {
|
|
39775
40603
|
unlinkSync3(current);
|
|
39776
40604
|
} catch {
|
|
39777
40605
|
}
|
|
39778
40606
|
} else {
|
|
39779
|
-
const next =
|
|
40607
|
+
const next = join16(backupDir, `${name15}.${i + 1}`);
|
|
39780
40608
|
try {
|
|
39781
|
-
if (
|
|
40609
|
+
if (existsSync16(current)) renameSync3(current, next);
|
|
39782
40610
|
} catch {
|
|
39783
40611
|
}
|
|
39784
40612
|
}
|
|
39785
40613
|
}
|
|
39786
40614
|
try {
|
|
39787
40615
|
const content = readFileSync5(filePath, "utf-8");
|
|
39788
|
-
const backupPath =
|
|
40616
|
+
const backupPath = join16(backupDir, `${name15}.1`);
|
|
39789
40617
|
if (typeof mode === "number") {
|
|
39790
|
-
|
|
40618
|
+
writeFileSync4(backupPath, content, { encoding: "utf-8", mode });
|
|
39791
40619
|
} else {
|
|
39792
|
-
|
|
40620
|
+
writeFileSync4(backupPath, content, "utf-8");
|
|
39793
40621
|
}
|
|
39794
40622
|
} catch {
|
|
39795
40623
|
}
|
|
@@ -39806,15 +40634,15 @@ function writeJsonFileAtomic(filePath, data, optsOrIndent = 2) {
|
|
|
39806
40634
|
const indent = opts.indent ?? 2;
|
|
39807
40635
|
const mode = opts.mode;
|
|
39808
40636
|
const dir = dirname7(filePath);
|
|
39809
|
-
const tempPath =
|
|
40637
|
+
const tempPath = join16(dir, `.${basename5(filePath)}.${randomBytes(6).toString("hex")}.tmp`);
|
|
39810
40638
|
const content = JSON.stringify(data, null, indent) + "\n";
|
|
39811
40639
|
if (typeof mode === "number") {
|
|
39812
|
-
|
|
40640
|
+
writeFileSync4(tempPath, content, { encoding: "utf-8", mode });
|
|
39813
40641
|
} else {
|
|
39814
|
-
|
|
40642
|
+
writeFileSync4(tempPath, content, "utf-8");
|
|
39815
40643
|
}
|
|
39816
40644
|
try {
|
|
39817
|
-
if (
|
|
40645
|
+
if (existsSync16(filePath)) {
|
|
39818
40646
|
rotateBackup(filePath, mode);
|
|
39819
40647
|
}
|
|
39820
40648
|
renameSync3(tempPath, filePath);
|
|
@@ -39840,19 +40668,19 @@ function readJsonFile(filePath) {
|
|
|
39840
40668
|
async function withLock2(filePath, transform2, opts = {}) {
|
|
39841
40669
|
const dir = dirname7(filePath);
|
|
39842
40670
|
const dirMode = typeof opts.mode === "number" ? modeToDirMode(opts.mode) : void 0;
|
|
39843
|
-
if (!
|
|
39844
|
-
|
|
40671
|
+
if (!existsSync16(dir)) {
|
|
40672
|
+
mkdirSync5(dir, { recursive: true, mode: dirMode });
|
|
39845
40673
|
}
|
|
39846
|
-
if (!
|
|
40674
|
+
if (!existsSync16(filePath)) {
|
|
39847
40675
|
if (typeof opts.mode === "number") {
|
|
39848
|
-
|
|
40676
|
+
writeFileSync4(filePath, "", { encoding: "utf-8", mode: opts.mode });
|
|
39849
40677
|
} else {
|
|
39850
|
-
|
|
40678
|
+
writeFileSync4(filePath, "", "utf-8");
|
|
39851
40679
|
}
|
|
39852
40680
|
}
|
|
39853
40681
|
let release;
|
|
39854
40682
|
try {
|
|
39855
|
-
release = await
|
|
40683
|
+
release = await lockfile3.lock(filePath, LOCK_OPTIONS);
|
|
39856
40684
|
const current = readJsonFile(filePath);
|
|
39857
40685
|
const updated = transform2(current);
|
|
39858
40686
|
writeJsonFileAtomic(filePath, updated, { mode: opts.mode });
|
|
@@ -39865,15 +40693,15 @@ async function withLock2(filePath, transform2, opts = {}) {
|
|
|
39865
40693
|
}
|
|
39866
40694
|
async function withFileLock(filePath, operation) {
|
|
39867
40695
|
const dir = dirname7(filePath);
|
|
39868
|
-
if (!
|
|
39869
|
-
|
|
40696
|
+
if (!existsSync16(dir)) {
|
|
40697
|
+
mkdirSync5(dir, { recursive: true });
|
|
39870
40698
|
}
|
|
39871
|
-
if (!
|
|
39872
|
-
|
|
40699
|
+
if (!existsSync16(filePath)) {
|
|
40700
|
+
writeFileSync4(filePath, "", "utf-8");
|
|
39873
40701
|
}
|
|
39874
40702
|
let release;
|
|
39875
40703
|
try {
|
|
39876
|
-
release = await
|
|
40704
|
+
release = await lockfile3.lock(filePath, LOCK_OPTIONS);
|
|
39877
40705
|
return await operation();
|
|
39878
40706
|
} finally {
|
|
39879
40707
|
if (release) {
|
|
@@ -39902,10 +40730,10 @@ var init_file_utils = __esm({
|
|
|
39902
40730
|
|
|
39903
40731
|
// packages/core/src/llm/credential-removal.ts
|
|
39904
40732
|
import { unlinkSync as unlinkSync4 } from "node:fs";
|
|
39905
|
-
import { join as
|
|
39906
|
-
import { getCleoHome as
|
|
40733
|
+
import { join as join17 } from "node:path";
|
|
40734
|
+
import { getCleoHome as getCleoHome3 } from "@cleocode/paths";
|
|
39907
40735
|
function suppressionStatePath() {
|
|
39908
|
-
return
|
|
40736
|
+
return join17(getCleoHome3(), SUPPRESSION_FILENAME);
|
|
39909
40737
|
}
|
|
39910
40738
|
function readSuppressionFile() {
|
|
39911
40739
|
const data = readJsonFile(suppressionStatePath());
|
|
@@ -40009,7 +40837,7 @@ var init_credential_removal = __esm({
|
|
|
40009
40837
|
sourceId: "cleo-pkce",
|
|
40010
40838
|
description: "Delete CLEO-issued PKCE token at <CLEO_HOME>/anthropic-oauth.json",
|
|
40011
40839
|
async remove() {
|
|
40012
|
-
const path =
|
|
40840
|
+
const path = join17(getCleoHome3(), "anthropic-oauth.json");
|
|
40013
40841
|
const cleaned = [];
|
|
40014
40842
|
try {
|
|
40015
40843
|
unlinkSync4(path);
|
|
@@ -40088,7 +40916,7 @@ __export(config_exports, {
|
|
|
40088
40916
|
parseConfigValue: () => parseConfigValue,
|
|
40089
40917
|
setConfigValue: () => setConfigValue
|
|
40090
40918
|
});
|
|
40091
|
-
import { existsSync as
|
|
40919
|
+
import { existsSync as existsSync17 } from "node:fs";
|
|
40092
40920
|
function getNestedValue(obj, path) {
|
|
40093
40921
|
const parts = path.split(".");
|
|
40094
40922
|
let current = obj;
|
|
@@ -40136,7 +40964,7 @@ function parseEnvValue(value) {
|
|
|
40136
40964
|
return value;
|
|
40137
40965
|
}
|
|
40138
40966
|
async function loadConfig(cwd) {
|
|
40139
|
-
let merged = JSON.parse(JSON.stringify(
|
|
40967
|
+
let merged = JSON.parse(JSON.stringify(DEFAULTS2));
|
|
40140
40968
|
const globalConfig2 = await readJson(getGlobalConfigPath());
|
|
40141
40969
|
if (globalConfig2) {
|
|
40142
40970
|
merged = deepMerge(merged, globalConfig2);
|
|
@@ -40182,7 +41010,7 @@ async function getConfigValue(path, cwd) {
|
|
|
40182
41010
|
return { value: val, source: "global" };
|
|
40183
41011
|
}
|
|
40184
41012
|
}
|
|
40185
|
-
const defaultVal = getNestedValue({ ...
|
|
41013
|
+
const defaultVal = getNestedValue({ ...DEFAULTS2 }, path);
|
|
40186
41014
|
return { value: defaultVal, source: "default" };
|
|
40187
41015
|
}
|
|
40188
41016
|
async function getRawConfigValue(key, cwd) {
|
|
@@ -40210,7 +41038,7 @@ function parseConfigValue(value) {
|
|
|
40210
41038
|
}
|
|
40211
41039
|
async function setConfigValue(key, value, cwd, opts) {
|
|
40212
41040
|
const configPath = opts?.global ? getGlobalConfigPath() : getConfigPath(cwd);
|
|
40213
|
-
if (!
|
|
41041
|
+
if (!existsSync17(configPath)) {
|
|
40214
41042
|
await atomicWriteJson(configPath, {});
|
|
40215
41043
|
}
|
|
40216
41044
|
const config2 = await readJson(configPath) ?? {};
|
|
@@ -40222,7 +41050,7 @@ async function setConfigValue(key, value, cwd, opts) {
|
|
|
40222
41050
|
async function applyStrictnessPreset(preset, cwd, opts) {
|
|
40223
41051
|
const definition = STRICTNESS_PRESETS[preset];
|
|
40224
41052
|
const configPath = opts?.global ? getGlobalConfigPath() : getConfigPath(cwd);
|
|
40225
|
-
if (!
|
|
41053
|
+
if (!existsSync17(configPath)) {
|
|
40226
41054
|
await atomicWriteJson(configPath, {});
|
|
40227
41055
|
}
|
|
40228
41056
|
const config2 = await readJson(configPath) ?? {};
|
|
@@ -40246,7 +41074,7 @@ function listStrictnessPresets() {
|
|
|
40246
41074
|
})
|
|
40247
41075
|
);
|
|
40248
41076
|
}
|
|
40249
|
-
var
|
|
41077
|
+
var DEFAULTS2, ENV_MAP, STRICTNESS_PRESETS;
|
|
40250
41078
|
var init_config = __esm({
|
|
40251
41079
|
"packages/core/src/config.ts"() {
|
|
40252
41080
|
"use strict";
|
|
@@ -40254,7 +41082,7 @@ var init_config = __esm({
|
|
|
40254
41082
|
init_paths();
|
|
40255
41083
|
init_atomic();
|
|
40256
41084
|
init_json2();
|
|
40257
|
-
|
|
41085
|
+
DEFAULTS2 = {
|
|
40258
41086
|
version: "2.10.0",
|
|
40259
41087
|
output: {
|
|
40260
41088
|
defaultFormat: "json",
|
|
@@ -40429,10 +41257,10 @@ var init_config = __esm({
|
|
|
40429
41257
|
// packages/core/src/llm/credential-seeders/claude-code-seeder.ts
|
|
40430
41258
|
import { readFileSync as readFileSync6 } from "node:fs";
|
|
40431
41259
|
import { homedir as homedir2 } from "node:os";
|
|
40432
|
-
import { join as
|
|
41260
|
+
import { join as join18 } from "node:path";
|
|
40433
41261
|
function defaultReadCredentialFile() {
|
|
40434
41262
|
try {
|
|
40435
|
-
return readFileSync6(
|
|
41263
|
+
return readFileSync6(join18(homedir2(), ".claude", ".credentials.json"), "utf-8");
|
|
40436
41264
|
} catch {
|
|
40437
41265
|
return null;
|
|
40438
41266
|
}
|
|
@@ -40527,9 +41355,9 @@ var init_claude_code_seeder = __esm({
|
|
|
40527
41355
|
|
|
40528
41356
|
// packages/core/src/llm/credential-seeders/cleo-pkce-seeder.ts
|
|
40529
41357
|
import { readFileSync as readFileSync7 } from "node:fs";
|
|
40530
|
-
import { join as
|
|
41358
|
+
import { join as join19 } from "node:path";
|
|
40531
41359
|
function defaultCleoPkcePath() {
|
|
40532
|
-
return
|
|
41360
|
+
return join19(getCleoHome(), "anthropic-oauth.json");
|
|
40533
41361
|
}
|
|
40534
41362
|
function defaultReadCredentialFile2() {
|
|
40535
41363
|
try {
|
|
@@ -40600,13 +41428,13 @@ var init_cleo_pkce_seeder = __esm({
|
|
|
40600
41428
|
});
|
|
40601
41429
|
|
|
40602
41430
|
// packages/core/src/llm/credential-seeders/codex-cli-seeder.ts
|
|
40603
|
-
import { existsSync as
|
|
41431
|
+
import { existsSync as existsSync18, readFileSync as readFileSync8 } from "node:fs";
|
|
40604
41432
|
import { homedir as homedir3 } from "node:os";
|
|
40605
|
-
import { join as
|
|
41433
|
+
import { join as join20 } from "node:path";
|
|
40606
41434
|
function getCodexAuthPath() {
|
|
40607
41435
|
const codexHome = (process.env["CODEX_HOME"] ?? "").trim();
|
|
40608
|
-
const root = codexHome ||
|
|
40609
|
-
return
|
|
41436
|
+
const root = codexHome || join20(homedir3(), ".codex");
|
|
41437
|
+
return join20(root, "auth.json");
|
|
40610
41438
|
}
|
|
40611
41439
|
var CodexCliSeeder, codexCliSeeder;
|
|
40612
41440
|
var init_codex_cli_seeder = __esm({
|
|
@@ -40625,7 +41453,7 @@ var init_codex_cli_seeder = __esm({
|
|
|
40625
41453
|
*/
|
|
40626
41454
|
async seed() {
|
|
40627
41455
|
const authPath = getCodexAuthPath();
|
|
40628
|
-
if (!
|
|
41456
|
+
if (!existsSync18(authPath)) {
|
|
40629
41457
|
return { entries: [] };
|
|
40630
41458
|
}
|
|
40631
41459
|
let raw;
|
|
@@ -40820,11 +41648,11 @@ var init_google_pkce = __esm({
|
|
|
40820
41648
|
});
|
|
40821
41649
|
|
|
40822
41650
|
// packages/core/src/llm/credential-seeders/gemini-cli-seeder.ts
|
|
40823
|
-
import { existsSync as
|
|
40824
|
-
import { join as
|
|
40825
|
-
import { getCleoHome as
|
|
41651
|
+
import { existsSync as existsSync19, readFileSync as readFileSync9 } from "node:fs";
|
|
41652
|
+
import { join as join21 } from "node:path";
|
|
41653
|
+
import { getCleoHome as getCleoHome4 } from "@cleocode/paths";
|
|
40826
41654
|
function getGoogleOauthPath() {
|
|
40827
|
-
return
|
|
41655
|
+
return join21(getCleoHome4(), "google_oauth.json");
|
|
40828
41656
|
}
|
|
40829
41657
|
function parseGoogleOauthFile(raw) {
|
|
40830
41658
|
let json3;
|
|
@@ -40869,7 +41697,7 @@ var init_gemini_cli_seeder = __esm({
|
|
|
40869
41697
|
*/
|
|
40870
41698
|
async seed() {
|
|
40871
41699
|
const path = getGoogleOauthPath();
|
|
40872
|
-
if (!
|
|
41700
|
+
if (!existsSync19(path)) {
|
|
40873
41701
|
return { entries: [] };
|
|
40874
41702
|
}
|
|
40875
41703
|
let raw;
|
|
@@ -41094,23 +41922,23 @@ var init_credential_seeders = __esm({
|
|
|
41094
41922
|
|
|
41095
41923
|
// packages/core/src/llm/global-config-migration.ts
|
|
41096
41924
|
import {
|
|
41097
|
-
existsSync as
|
|
41098
|
-
mkdirSync as
|
|
41925
|
+
existsSync as existsSync20,
|
|
41926
|
+
mkdirSync as mkdirSync6,
|
|
41099
41927
|
readFileSync as readFileSync10,
|
|
41100
41928
|
renameSync as renameSync4,
|
|
41101
41929
|
unlinkSync as unlinkSync5,
|
|
41102
|
-
writeFileSync as
|
|
41930
|
+
writeFileSync as writeFileSync5
|
|
41103
41931
|
} from "node:fs";
|
|
41104
|
-
import { join as
|
|
41105
|
-
import { getCleoHome as
|
|
41932
|
+
import { join as join22 } from "node:path";
|
|
41933
|
+
import { getCleoHome as getCleoHome5, getCleoPlatformPaths as getCleoPlatformPaths2 } from "@cleocode/paths";
|
|
41106
41934
|
function configDirGlobalConfigPath() {
|
|
41107
|
-
return
|
|
41935
|
+
return join22(getCleoPlatformPaths2().config, GLOBAL_CONFIG_FILENAME);
|
|
41108
41936
|
}
|
|
41109
41937
|
function legacyGlobalConfigPath() {
|
|
41110
|
-
return
|
|
41938
|
+
return join22(getCleoHome5(), GLOBAL_CONFIG_FILENAME);
|
|
41111
41939
|
}
|
|
41112
41940
|
function migrationMarkerPath() {
|
|
41113
|
-
return
|
|
41941
|
+
return join22(getCleoHome5(), MIGRATIONS_SUBDIR, MIGRATION_MARKER);
|
|
41114
41942
|
}
|
|
41115
41943
|
function tempTargetPath() {
|
|
41116
41944
|
return `${configDirGlobalConfigPath()}.tmp`;
|
|
@@ -41120,12 +41948,12 @@ function migrateGlobalConfigToConfigDir() {
|
|
|
41120
41948
|
const source = legacyGlobalConfigPath();
|
|
41121
41949
|
const target = configDirGlobalConfigPath();
|
|
41122
41950
|
const marker16 = migrationMarkerPath();
|
|
41123
|
-
if (
|
|
41124
|
-
if (!
|
|
41951
|
+
if (existsSync20(marker16)) return false;
|
|
41952
|
+
if (!existsSync20(source)) {
|
|
41125
41953
|
stampMarker(marker16);
|
|
41126
41954
|
return false;
|
|
41127
41955
|
}
|
|
41128
|
-
if (
|
|
41956
|
+
if (existsSync20(target)) {
|
|
41129
41957
|
backupSourceQuiet(source);
|
|
41130
41958
|
stampMarker(marker16);
|
|
41131
41959
|
return false;
|
|
@@ -41139,9 +41967,9 @@ function migrateGlobalConfigToConfigDir() {
|
|
|
41139
41967
|
);
|
|
41140
41968
|
return false;
|
|
41141
41969
|
}
|
|
41142
|
-
|
|
41970
|
+
mkdirSync6(getCleoPlatformPaths2().config, { recursive: true });
|
|
41143
41971
|
const temp = tempTargetPath();
|
|
41144
|
-
|
|
41972
|
+
writeFileSync5(temp, raw, { mode: 420 });
|
|
41145
41973
|
renameSync4(temp, target);
|
|
41146
41974
|
stampMarker(marker16);
|
|
41147
41975
|
try {
|
|
@@ -41158,7 +41986,7 @@ function migrateGlobalConfigToConfigDir() {
|
|
|
41158
41986
|
} catch (err) {
|
|
41159
41987
|
try {
|
|
41160
41988
|
const temp = tempTargetPath();
|
|
41161
|
-
if (
|
|
41989
|
+
if (existsSync20(temp)) unlinkSync5(temp);
|
|
41162
41990
|
} catch {
|
|
41163
41991
|
}
|
|
41164
41992
|
console.error(
|
|
@@ -41169,8 +41997,8 @@ function migrateGlobalConfigToConfigDir() {
|
|
|
41169
41997
|
}
|
|
41170
41998
|
function stampMarker(markerPath) {
|
|
41171
41999
|
try {
|
|
41172
|
-
|
|
41173
|
-
|
|
42000
|
+
mkdirSync6(join22(getCleoHome5(), MIGRATIONS_SUBDIR), { recursive: true });
|
|
42001
|
+
writeFileSync5(markerPath, `${(/* @__PURE__ */ new Date()).toISOString()}
|
|
41174
42002
|
`, { mode: 420 });
|
|
41175
42003
|
} catch {
|
|
41176
42004
|
}
|
|
@@ -41178,7 +42006,7 @@ function stampMarker(markerPath) {
|
|
|
41178
42006
|
function backupSourceQuiet(source) {
|
|
41179
42007
|
try {
|
|
41180
42008
|
const backup = `${source}${BACKUP_SUFFIX}`;
|
|
41181
|
-
if (
|
|
42009
|
+
if (existsSync20(backup)) return;
|
|
41182
42010
|
renameSync4(source, backup);
|
|
41183
42011
|
} catch {
|
|
41184
42012
|
}
|
|
@@ -41201,20 +42029,20 @@ var init_global_config_migration = __esm({
|
|
|
41201
42029
|
});
|
|
41202
42030
|
|
|
41203
42031
|
// packages/core/src/llm/legacy-flat-key-import.ts
|
|
41204
|
-
import { existsSync as
|
|
41205
|
-
import { join as
|
|
41206
|
-
import { getCleoHome as
|
|
42032
|
+
import { existsSync as existsSync21, readFileSync as readFileSync11, renameSync as renameSync5, writeFileSync as writeFileSync6 } from "node:fs";
|
|
42033
|
+
import { join as join23 } from "node:path";
|
|
42034
|
+
import { getCleoHome as getCleoHome6 } from "@cleocode/paths";
|
|
41207
42035
|
function migrationPaths() {
|
|
41208
|
-
const home =
|
|
42036
|
+
const home = getCleoHome6();
|
|
41209
42037
|
return {
|
|
41210
|
-
flatPath:
|
|
41211
|
-
bakPath:
|
|
41212
|
-
markerPath:
|
|
42038
|
+
flatPath: join23(home, "anthropic-key"),
|
|
42039
|
+
bakPath: join23(home, `anthropic-key${LEGACY_FLAT_KEY_BAK_SUFFIX}`),
|
|
42040
|
+
markerPath: join23(home, LEGACY_FLAT_KEY_MARKER)
|
|
41213
42041
|
};
|
|
41214
42042
|
}
|
|
41215
42043
|
function writeMarker(markerPath) {
|
|
41216
42044
|
try {
|
|
41217
|
-
|
|
42045
|
+
writeFileSync6(markerPath, `${(/* @__PURE__ */ new Date()).toISOString()}
|
|
41218
42046
|
`, {
|
|
41219
42047
|
encoding: "utf-8",
|
|
41220
42048
|
flag: "wx",
|
|
@@ -41230,7 +42058,7 @@ function writeMarker(markerPath) {
|
|
|
41230
42058
|
}
|
|
41231
42059
|
function readFlatKey(flatPath) {
|
|
41232
42060
|
try {
|
|
41233
|
-
if (!
|
|
42061
|
+
if (!existsSync21(flatPath)) return null;
|
|
41234
42062
|
const raw = readFileSync11(flatPath, "utf-8").trim();
|
|
41235
42063
|
return raw || null;
|
|
41236
42064
|
} catch (err) {
|
|
@@ -41243,7 +42071,7 @@ function readFlatKey(flatPath) {
|
|
|
41243
42071
|
}
|
|
41244
42072
|
async function importLegacyFlatAnthropicKey() {
|
|
41245
42073
|
const { flatPath, bakPath, markerPath } = migrationPaths();
|
|
41246
|
-
if (
|
|
42074
|
+
if (existsSync21(markerPath)) {
|
|
41247
42075
|
return { status: "marker-present", flatPath, bakPath: null, markerPath };
|
|
41248
42076
|
}
|
|
41249
42077
|
let existing = null;
|
|
@@ -41260,7 +42088,7 @@ async function importLegacyFlatAnthropicKey() {
|
|
|
41260
42088
|
writeMarker(markerPath);
|
|
41261
42089
|
return { status: "already-imported", flatPath, bakPath: null, markerPath };
|
|
41262
42090
|
}
|
|
41263
|
-
if (!
|
|
42091
|
+
if (!existsSync21(flatPath)) {
|
|
41264
42092
|
writeMarker(markerPath);
|
|
41265
42093
|
return { status: "no-flat-file", flatPath, bakPath: null, markerPath };
|
|
41266
42094
|
}
|
|
@@ -41337,15 +42165,15 @@ __export(credentials_exports, {
|
|
|
41337
42165
|
resolveProviderStatus: () => resolveProviderStatus,
|
|
41338
42166
|
storeAnthropicApiKey: () => storeAnthropicApiKey
|
|
41339
42167
|
});
|
|
41340
|
-
import { existsSync as
|
|
41341
|
-
import { join as
|
|
41342
|
-
import { getCleoHome as
|
|
42168
|
+
import { existsSync as existsSync22, mkdirSync as mkdirSync7, readFileSync as readFileSync12, writeFileSync as writeFileSync7 } from "node:fs";
|
|
42169
|
+
import { join as join24 } from "node:path";
|
|
42170
|
+
import { getCleoHome as getCleoHome7 } from "@cleocode/paths";
|
|
41343
42171
|
function globalConfigPath() {
|
|
41344
42172
|
return configDirGlobalConfigPath();
|
|
41345
42173
|
}
|
|
41346
42174
|
function projectConfigPath(projectRoot) {
|
|
41347
42175
|
const cleoDir = process.env["CLEO_DIR"] ?? ".cleo";
|
|
41348
|
-
return
|
|
42176
|
+
return join24(projectRoot, cleoDir, "config.json");
|
|
41349
42177
|
}
|
|
41350
42178
|
function readGlobalProviderKey(provider) {
|
|
41351
42179
|
ensureGlobalConfigMigrated();
|
|
@@ -41355,8 +42183,8 @@ function readGlobalProviderKey(provider) {
|
|
|
41355
42183
|
}
|
|
41356
42184
|
function readFlatAnthropicKey() {
|
|
41357
42185
|
try {
|
|
41358
|
-
const keyFile =
|
|
41359
|
-
if (!
|
|
42186
|
+
const keyFile = join24(getCleoHome7(), "anthropic-key");
|
|
42187
|
+
if (!existsSync22(keyFile)) return null;
|
|
41360
42188
|
const stored = readFileSync12(keyFile, "utf-8").trim();
|
|
41361
42189
|
return stored || null;
|
|
41362
42190
|
} catch {
|
|
@@ -41365,7 +42193,7 @@ function readFlatAnthropicKey() {
|
|
|
41365
42193
|
}
|
|
41366
42194
|
function readProviderKeyFromConfig(configFile, provider) {
|
|
41367
42195
|
try {
|
|
41368
|
-
if (!
|
|
42196
|
+
if (!existsSync22(configFile)) return null;
|
|
41369
42197
|
const raw = readFileSync12(configFile, "utf-8");
|
|
41370
42198
|
const config2 = JSON.parse(raw);
|
|
41371
42199
|
const llm = config2["llm"];
|
|
@@ -41537,12 +42365,12 @@ function resolveProviderStatus(provider) {
|
|
|
41537
42365
|
return { provider, resolvedSource, hasCredential: true };
|
|
41538
42366
|
}
|
|
41539
42367
|
function storeAnthropicApiKey(apiKey) {
|
|
41540
|
-
const dir =
|
|
41541
|
-
if (!
|
|
41542
|
-
|
|
42368
|
+
const dir = getCleoHome7();
|
|
42369
|
+
if (!existsSync22(dir)) {
|
|
42370
|
+
mkdirSync7(dir, { recursive: true });
|
|
41543
42371
|
}
|
|
41544
|
-
const keyFile =
|
|
41545
|
-
|
|
42372
|
+
const keyFile = join24(dir, "anthropic-key");
|
|
42373
|
+
writeFileSync7(keyFile, apiKey.trim(), { mode: 384 });
|
|
41546
42374
|
}
|
|
41547
42375
|
function clearAnthropicKeyCache() {
|
|
41548
42376
|
}
|
|
@@ -41605,17 +42433,17 @@ __export(credentials_store_exports, {
|
|
|
41605
42433
|
pickCredentialForProviderSync: () => pickCredentialForProviderSync,
|
|
41606
42434
|
removeCredential: () => removeCredential
|
|
41607
42435
|
});
|
|
41608
|
-
import { chmodSync, existsSync as
|
|
41609
|
-
import { dirname as dirname8, join as
|
|
41610
|
-
import { getCleoHome as
|
|
42436
|
+
import { chmodSync, existsSync as existsSync23, mkdirSync as mkdirSync8, readFileSync as readFileSync13, statSync as statSync6, writeFileSync as writeFileSync8 } from "node:fs";
|
|
42437
|
+
import { dirname as dirname8, join as join25 } from "node:path";
|
|
42438
|
+
import { getCleoHome as getCleoHome8 } from "@cleocode/paths";
|
|
41611
42439
|
function credentialsStorePath() {
|
|
41612
|
-
return
|
|
42440
|
+
return join25(getCleoHome8(), "llm-credentials.json");
|
|
41613
42441
|
}
|
|
41614
42442
|
function ensureFileInitialized(path) {
|
|
41615
|
-
if (
|
|
42443
|
+
if (existsSync23(path)) return;
|
|
41616
42444
|
const dir = dirname8(path);
|
|
41617
|
-
if (!
|
|
41618
|
-
|
|
42445
|
+
if (!existsSync23(dir)) {
|
|
42446
|
+
mkdirSync8(dir, { recursive: true, mode: 448 });
|
|
41619
42447
|
} else {
|
|
41620
42448
|
try {
|
|
41621
42449
|
chmodSync(dir, 448);
|
|
@@ -41623,7 +42451,7 @@ function ensureFileInitialized(path) {
|
|
|
41623
42451
|
}
|
|
41624
42452
|
}
|
|
41625
42453
|
try {
|
|
41626
|
-
|
|
42454
|
+
writeFileSync8(path, `${JSON.stringify(emptyStore(), null, 2)}
|
|
41627
42455
|
`, {
|
|
41628
42456
|
encoding: "utf-8",
|
|
41629
42457
|
flag: "wx",
|
|
@@ -41966,13 +42794,13 @@ var init_gemini = __esm({
|
|
|
41966
42794
|
|
|
41967
42795
|
// packages/core/src/llm/stable-device-id.ts
|
|
41968
42796
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
41969
|
-
import { existsSync as
|
|
41970
|
-
import { dirname as dirname9, join as
|
|
41971
|
-
import { getCleoHome as
|
|
42797
|
+
import { existsSync as existsSync24, mkdirSync as mkdirSync9, readFileSync as readFileSync14, renameSync as renameSync6, writeFileSync as writeFileSync9 } from "node:fs";
|
|
42798
|
+
import { dirname as dirname9, join as join26 } from "node:path";
|
|
42799
|
+
import { getCleoHome as getCleoHome9 } from "@cleocode/paths";
|
|
41972
42800
|
function getStableDeviceId() {
|
|
41973
42801
|
if (_cachedDeviceId !== null) return _cachedDeviceId;
|
|
41974
|
-
const path =
|
|
41975
|
-
if (
|
|
42802
|
+
const path = join26(getCleoHome9(), DEVICE_ID_FILE);
|
|
42803
|
+
if (existsSync24(path)) {
|
|
41976
42804
|
try {
|
|
41977
42805
|
const raw = readFileSync14(path, "utf-8").trim();
|
|
41978
42806
|
if (raw.length > 0) {
|
|
@@ -41985,11 +42813,11 @@ function getStableDeviceId() {
|
|
|
41985
42813
|
const fresh = randomUUID2();
|
|
41986
42814
|
try {
|
|
41987
42815
|
const dir = dirname9(path);
|
|
41988
|
-
if (!
|
|
41989
|
-
|
|
42816
|
+
if (!existsSync24(dir)) {
|
|
42817
|
+
mkdirSync9(dir, { recursive: true });
|
|
41990
42818
|
}
|
|
41991
42819
|
const tmpPath = `${path}.tmp.${process.pid}`;
|
|
41992
|
-
|
|
42820
|
+
writeFileSync9(tmpPath, fresh, { mode: 384 });
|
|
41993
42821
|
renameSync6(tmpPath, path);
|
|
41994
42822
|
} catch {
|
|
41995
42823
|
}
|
|
@@ -42287,14 +43115,14 @@ var init_xai = __esm({
|
|
|
42287
43115
|
});
|
|
42288
43116
|
|
|
42289
43117
|
// packages/core/src/llm/provider-registry/loader.ts
|
|
42290
|
-
import { readdirSync as
|
|
42291
|
-
import { join as
|
|
43118
|
+
import { readdirSync as readdirSync6, statSync as statSync7 } from "node:fs";
|
|
43119
|
+
import { join as join27 } from "node:path";
|
|
42292
43120
|
import { pathToFileURL } from "node:url";
|
|
42293
43121
|
function isPluginFile(filename) {
|
|
42294
43122
|
return PLUGIN_EXTENSIONS.some((ext) => filename.endsWith(ext));
|
|
42295
43123
|
}
|
|
42296
43124
|
function getPluginDir() {
|
|
42297
|
-
return
|
|
43125
|
+
return join27(getCleoHome(), "plugins", "model-providers");
|
|
42298
43126
|
}
|
|
42299
43127
|
async function loadOnePlugin(absPath, api) {
|
|
42300
43128
|
let mod;
|
|
@@ -42336,19 +43164,19 @@ async function loadOnePlugin(absPath, api) {
|
|
|
42336
43164
|
async function scanAndLoadPlugins(pluginDir, api) {
|
|
42337
43165
|
let entries;
|
|
42338
43166
|
try {
|
|
42339
|
-
entries =
|
|
43167
|
+
entries = readdirSync6(pluginDir);
|
|
42340
43168
|
} catch {
|
|
42341
43169
|
return;
|
|
42342
43170
|
}
|
|
42343
43171
|
const pluginFiles = entries.filter((name15) => !name15.startsWith(".") && isPluginFile(name15)).filter((name15) => {
|
|
42344
43172
|
try {
|
|
42345
|
-
return statSync7(
|
|
43173
|
+
return statSync7(join27(pluginDir, name15)).isFile();
|
|
42346
43174
|
} catch {
|
|
42347
43175
|
return false;
|
|
42348
43176
|
}
|
|
42349
43177
|
}).sort();
|
|
42350
43178
|
for (const filename of pluginFiles) {
|
|
42351
|
-
await loadOnePlugin(
|
|
43179
|
+
await loadOnePlugin(join27(pluginDir, filename), api);
|
|
42352
43180
|
}
|
|
42353
43181
|
}
|
|
42354
43182
|
async function runDiscovery(registerProvider2, builtins) {
|
|
@@ -42451,15 +43279,15 @@ var init_provider_registry = __esm({
|
|
|
42451
43279
|
});
|
|
42452
43280
|
|
|
42453
43281
|
// packages/core/src/llm/rate-limit-guard.ts
|
|
42454
|
-
import { existsSync as
|
|
42455
|
-
import { join as
|
|
42456
|
-
import { getCleoHome as
|
|
43282
|
+
import { existsSync as existsSync25, mkdirSync as mkdirSync10, unlinkSync as unlinkSync6 } from "node:fs";
|
|
43283
|
+
import { join as join28 } from "node:path";
|
|
43284
|
+
import { getCleoHome as getCleoHome10 } from "@cleocode/paths";
|
|
42457
43285
|
function sanitize(value) {
|
|
42458
43286
|
return value.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
42459
43287
|
}
|
|
42460
43288
|
function rateLimitStatePath(provider, label) {
|
|
42461
|
-
const dir =
|
|
42462
|
-
return
|
|
43289
|
+
const dir = join28(getCleoHome10(), "rate-limit-state");
|
|
43290
|
+
return join28(dir, `${sanitize(provider)}-${sanitize(label)}.json`);
|
|
42463
43291
|
}
|
|
42464
43292
|
function parseResetFromHeaders(headers) {
|
|
42465
43293
|
const get = (k) => {
|
|
@@ -42506,9 +43334,9 @@ async function recordRateLimit(provider, label, opts) {
|
|
|
42506
43334
|
source = "default";
|
|
42507
43335
|
}
|
|
42508
43336
|
const filePath = rateLimitStatePath(provider, label);
|
|
42509
|
-
const dir =
|
|
42510
|
-
if (!
|
|
42511
|
-
|
|
43337
|
+
const dir = join28(getCleoHome10(), "rate-limit-state");
|
|
43338
|
+
if (!existsSync25(dir)) {
|
|
43339
|
+
mkdirSync10(dir, { recursive: true });
|
|
42512
43340
|
}
|
|
42513
43341
|
const state = { resetAt, recordedAt: now, source };
|
|
42514
43342
|
await withFileLock(filePath, () => {
|
|
@@ -43408,28 +44236,28 @@ var init_error_classifier = __esm({
|
|
|
43408
44236
|
|
|
43409
44237
|
// packages/core/src/llm/catalog-cache.ts
|
|
43410
44238
|
import {
|
|
43411
|
-
existsSync as
|
|
43412
|
-
mkdirSync as
|
|
43413
|
-
readdirSync as
|
|
44239
|
+
existsSync as existsSync26,
|
|
44240
|
+
mkdirSync as mkdirSync11,
|
|
44241
|
+
readdirSync as readdirSync7,
|
|
43414
44242
|
readFileSync as readFileSync15,
|
|
43415
44243
|
statSync as statSync8,
|
|
43416
44244
|
symlinkSync,
|
|
43417
44245
|
unlinkSync as unlinkSync7,
|
|
43418
|
-
writeFileSync as
|
|
44246
|
+
writeFileSync as writeFileSync10
|
|
43419
44247
|
} from "node:fs";
|
|
43420
44248
|
import { homedir as homedir4 } from "node:os";
|
|
43421
|
-
import { join as
|
|
44249
|
+
import { join as join29 } from "node:path";
|
|
43422
44250
|
function getCatalogDir() {
|
|
43423
44251
|
if (process.env["CLEO_DATA_DIR"]) {
|
|
43424
|
-
return
|
|
44252
|
+
return join29(process.env["CLEO_DATA_DIR"], "llm-catalog");
|
|
43425
44253
|
}
|
|
43426
|
-
const xdg = process.env["XDG_DATA_HOME"] ??
|
|
43427
|
-
return
|
|
44254
|
+
const xdg = process.env["XDG_DATA_HOME"] ?? join29(homedir4(), ".local", "share");
|
|
44255
|
+
return join29(xdg, "cleo", "llm-catalog");
|
|
43428
44256
|
}
|
|
43429
44257
|
function findLatestCacheFile(dir) {
|
|
43430
|
-
if (!
|
|
43431
|
-
const files =
|
|
43432
|
-
return files.length > 0 ?
|
|
44258
|
+
if (!existsSync26(dir)) return null;
|
|
44259
|
+
const files = readdirSync7(dir).filter((f) => f.endsWith("-models.json")).map((f) => ({ name: f, mtime: statSync8(join29(dir, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime);
|
|
44260
|
+
return files.length > 0 ? join29(dir, files[0].name) : null;
|
|
43433
44261
|
}
|
|
43434
44262
|
function readCacheFile(filePath) {
|
|
43435
44263
|
try {
|
|
@@ -43447,14 +44275,14 @@ var init_catalog_cache = __esm({
|
|
|
43447
44275
|
|
|
43448
44276
|
// packages/core/src/llm/model-metadata.ts
|
|
43449
44277
|
import { readFileSync as readFileSync16 } from "node:fs";
|
|
43450
|
-
import { dirname as dirname10, join as
|
|
44278
|
+
import { dirname as dirname10, join as join30 } from "node:path";
|
|
43451
44279
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
43452
44280
|
function getCuratedTable() {
|
|
43453
44281
|
if (_curated !== void 0) {
|
|
43454
44282
|
return _curated;
|
|
43455
44283
|
}
|
|
43456
44284
|
const thisDir = dirname10(fileURLToPath3(import.meta.url));
|
|
43457
|
-
const jsonPath =
|
|
44285
|
+
const jsonPath = join30(thisDir, "curated-models.json");
|
|
43458
44286
|
const raw = JSON.parse(readFileSync16(jsonPath, "utf-8"));
|
|
43459
44287
|
_curated = raw.models;
|
|
43460
44288
|
return _curated;
|
|
@@ -43539,7 +44367,7 @@ function storedToResolved(stored) {
|
|
|
43539
44367
|
awsProfile: stored.metadata?.["awsProfile"] ?? null
|
|
43540
44368
|
};
|
|
43541
44369
|
}
|
|
43542
|
-
function
|
|
44370
|
+
function sleep2(ms) {
|
|
43543
44371
|
return new Promise((resolve5) => setTimeout(resolve5, ms));
|
|
43544
44372
|
}
|
|
43545
44373
|
function backoffMs(policy, attempt) {
|
|
@@ -43677,7 +44505,7 @@ var init_concrete_session = __esm({
|
|
|
43677
44505
|
throw err;
|
|
43678
44506
|
}
|
|
43679
44507
|
lastErr = err;
|
|
43680
|
-
await
|
|
44508
|
+
await sleep2(backoffMs(policy, attempt));
|
|
43681
44509
|
}
|
|
43682
44510
|
}
|
|
43683
44511
|
throw lastErr;
|
|
@@ -47433,7 +48261,7 @@ function normalizeOllamaToolCall(tc) {
|
|
|
47433
48261
|
arguments: JSON.stringify(tc.function.arguments ?? {})
|
|
47434
48262
|
};
|
|
47435
48263
|
}
|
|
47436
|
-
function
|
|
48264
|
+
function sleep3(ms) {
|
|
47437
48265
|
return new Promise((resolve5) => setTimeout(resolve5, ms));
|
|
47438
48266
|
}
|
|
47439
48267
|
var OLLAMA_DEFAULT_BASE_URL, MAX_RETRIES, INITIAL_BACKOFF_MS, RETRYABLE_STATUS_CODES, OllamaTransport;
|
|
@@ -47642,7 +48470,7 @@ var init_ollama2 = __esm({
|
|
|
47642
48470
|
for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {
|
|
47643
48471
|
if (attempt > 0) {
|
|
47644
48472
|
const delay = INITIAL_BACKOFF_MS * 2 ** (attempt - 1);
|
|
47645
|
-
await
|
|
48473
|
+
await sleep3(delay);
|
|
47646
48474
|
}
|
|
47647
48475
|
let response;
|
|
47648
48476
|
try {
|
|
@@ -75655,7 +76483,7 @@ var init_catalog_model_resolver = __esm({
|
|
|
75655
76483
|
});
|
|
75656
76484
|
|
|
75657
76485
|
// packages/core/src/llm/cross-provider-selector.ts
|
|
75658
|
-
import { totalmem } from "node:os";
|
|
76486
|
+
import { totalmem as totalmem2 } from "node:os";
|
|
75659
76487
|
async function probeOllamaAlive(baseUrl) {
|
|
75660
76488
|
const cached2 = _ollamaProbeCache.get(baseUrl);
|
|
75661
76489
|
if (cached2 && Date.now() - cached2.checkedAt < OLLAMA_PROBE_TTL_MS) {
|
|
@@ -75678,7 +76506,7 @@ async function probeOllamaAlive(baseUrl) {
|
|
|
75678
76506
|
return alive;
|
|
75679
76507
|
}
|
|
75680
76508
|
function ollamaDefaultModelForTier(tier, ramBytesOverride) {
|
|
75681
|
-
const ramBytes = ramBytesOverride ??
|
|
76509
|
+
const ramBytes = ramBytesOverride ?? totalmem2();
|
|
75682
76510
|
const gb = ramBytes / 1024 ** 3;
|
|
75683
76511
|
if (gb >= 8) {
|
|
75684
76512
|
return tier === "fast" || tier === "local" ? "gemma4:e2b" : "gemma4:e4b";
|
|
@@ -75802,7 +76630,7 @@ async function selectBestProvisioned(role, opts) {
|
|
|
75802
76630
|
let model;
|
|
75803
76631
|
if (winner.id === "ollama") {
|
|
75804
76632
|
const ollamaProfile2 = await getProviderProfile2("ollama");
|
|
75805
|
-
const ramBytes =
|
|
76633
|
+
const ramBytes = totalmem2();
|
|
75806
76634
|
const gb = ramBytes / 1024 ** 3;
|
|
75807
76635
|
if (gb >= 8) {
|
|
75808
76636
|
model = (winner.tier === "fast" || taskTier === "fast" ? ollamaProfile2?.defaultAuxModel : ollamaProfile2?.defaultModel) ?? ollamaDefaultModelForTier(winner.tier);
|