@cleocode/core 2026.5.132 → 2026.5.134
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/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/output.d.ts +2 -0
- package/dist/output.d.ts.map +1 -1
- package/dist/output.js +59 -1
- package/dist/output.js.map +1 -1
- package/dist/sentient/daemon-entry.d.ts +6 -0
- package/dist/sentient/daemon-entry.d.ts.map +1 -1
- package/dist/sentient/daemon-entry.js +10 -1
- package/dist/sentient/daemon-entry.js.map +1 -1
- package/dist/sentient/daemon.d.ts +16 -0
- package/dist/sentient/daemon.d.ts.map +1 -1
- package/dist/sentient/daemon.js +11 -1
- package/dist/sentient/daemon.js.map +1 -1
- package/dist/sentient/index.d.ts +1 -0
- package/dist/sentient/index.d.ts.map +1 -1
- package/dist/sentient/index.js +4 -0
- package/dist/sentient/index.js.map +1 -1
- package/dist/sentient/propose-tick.d.ts +101 -0
- package/dist/sentient/propose-tick.d.ts.map +1 -1
- package/dist/sentient/propose-tick.js +284 -0
- package/dist/sentient/propose-tick.js.map +1 -1
- package/dist/sentient/tick.d.ts +23 -0
- package/dist/sentient/tick.d.ts.map +1 -1
- package/dist/sentient/tick.js +63 -8
- package/dist/sentient/tick.js.map +1 -1
- package/dist/store/exodus/index.d.ts +16 -0
- package/dist/store/exodus/index.d.ts.map +1 -0
- package/dist/store/exodus/index.js +16 -0
- package/dist/store/exodus/index.js.map +1 -0
- package/dist/store/exodus/migrate.d.ts +41 -0
- package/dist/store/exodus/migrate.d.ts.map +1 -0
- package/dist/store/exodus/migrate.js +416 -0
- package/dist/store/exodus/migrate.js.map +1 -0
- package/dist/store/exodus/plan.d.ts +44 -0
- package/dist/store/exodus/plan.d.ts.map +1 -0
- package/dist/store/exodus/plan.js +178 -0
- package/dist/store/exodus/plan.js.map +1 -0
- package/dist/store/exodus/status.d.ts +22 -0
- package/dist/store/exodus/status.d.ts.map +1 -0
- package/dist/store/exodus/status.js +88 -0
- package/dist/store/exodus/status.js.map +1 -0
- package/dist/store/exodus/types.d.ts +169 -0
- package/dist/store/exodus/types.d.ts.map +1 -0
- package/dist/store/exodus/types.js +21 -0
- package/dist/store/exodus/types.js.map +1 -0
- package/dist/store/exodus/verify.d.ts +34 -0
- package/dist/store/exodus/verify.d.ts.map +1 -0
- package/dist/store/exodus/verify.js +168 -0
- package/dist/store/exodus/verify.js.map +1 -0
- package/dist/store/open-cleo-db.d.ts +51 -4
- package/dist/store/open-cleo-db.d.ts.map +1 -1
- package/dist/store/open-cleo-db.js +56 -2
- package/dist/store/open-cleo-db.js.map +1 -1
- package/dist/store/schema/cleo-global/nexus.d.ts +1 -1
- package/dist/store/schema/cleo-global/signaldock.d.ts +1 -1
- package/dist/store/schema/cleo-shared/brain.d.ts +1 -1
- package/package.json +12 -12
|
@@ -0,0 +1,416 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Exodus migration engine.
|
|
3
|
+
*
|
|
4
|
+
* `runExodusMigrate()` performs the actual data migration from legacy DBs
|
|
5
|
+
* to the consolidated dual-scope `cleo.db`. Key invariants:
|
|
6
|
+
*
|
|
7
|
+
* - Source DBs are opened **read-only** via `openCleoDbSnapshot` (AC4).
|
|
8
|
+
* - Source files are backed up to the staging dir before any writes (AC5).
|
|
9
|
+
* - Import is wrapped in `BEGIN … COMMIT` per scope; partial failure leaves
|
|
10
|
+
* the target DB untouched (AC6).
|
|
11
|
+
* - Idempotency keys are propagated where the source row has them; generated
|
|
12
|
+
* where it does not (AC7).
|
|
13
|
+
* - The staging journal is written atomically before each table copy so a
|
|
14
|
+
* crash can be resumed (AC5).
|
|
15
|
+
*
|
|
16
|
+
* ## Advisory file lock (AC4)
|
|
17
|
+
*
|
|
18
|
+
* The source DB files are opened read-only via `openCleoDbSnapshot` which
|
|
19
|
+
* calls `new DatabaseSync(path, { readOnly: true })`. Node's SQLite binding
|
|
20
|
+
* opens with `SQLITE_OPEN_READONLY`, which prevents any writes from this
|
|
21
|
+
* process. We additionally write a `.lock` sentinel file next to each source
|
|
22
|
+
* DB for the duration of the migration so that other CLEO processes can detect
|
|
23
|
+
* an in-progress exodus and refuse to write.
|
|
24
|
+
*
|
|
25
|
+
* @task T11248 (E5 · SG-DB-SUBSTRATE-V2)
|
|
26
|
+
* @saga T11242
|
|
27
|
+
*/
|
|
28
|
+
import { copyFileSync, existsSync, mkdirSync, readFileSync, renameSync, unlinkSync, writeFileSync, } from 'node:fs';
|
|
29
|
+
import { join } from 'node:path';
|
|
30
|
+
import { getLogger } from '../../logger.js';
|
|
31
|
+
import { getCleoVersion } from '../../scaffold/ensure-config.js';
|
|
32
|
+
import { openDualScopeDb } from '../dual-scope-db.js';
|
|
33
|
+
import { openCleoDbSnapshot } from '../open-cleo-db.js';
|
|
34
|
+
import { EXODUS_TARGET_SCHEMA_VERSION } from './types.js';
|
|
35
|
+
const log = getLogger('exodus-migrate');
|
|
36
|
+
// ---------------------------------------------------------------------------
|
|
37
|
+
// Advisory lock sentinel filename
|
|
38
|
+
// ---------------------------------------------------------------------------
|
|
39
|
+
const LOCK_SENTINEL_SUFFIX = '.exodus-lock';
|
|
40
|
+
// ---------------------------------------------------------------------------
|
|
41
|
+
// Journal helpers
|
|
42
|
+
// ---------------------------------------------------------------------------
|
|
43
|
+
const JOURNAL_FILENAME = 'exodus-journal.json';
|
|
44
|
+
/**
|
|
45
|
+
* Get the SQLite version string from an open DatabaseSync handle.
|
|
46
|
+
*/
|
|
47
|
+
function getSqliteVersion(db) {
|
|
48
|
+
try {
|
|
49
|
+
const row = db.prepare('SELECT sqlite_version() AS v').get();
|
|
50
|
+
return row?.v ?? 'unknown';
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return 'unknown';
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Read the tables list from a legacy SQLite DB (excluding SQLite internals).
|
|
58
|
+
*/
|
|
59
|
+
function listTables(db) {
|
|
60
|
+
const rows = db
|
|
61
|
+
.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' AND name NOT LIKE '__drizzle_%' ORDER BY name")
|
|
62
|
+
.all();
|
|
63
|
+
return rows.map((r) => r.name);
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Write the journal file atomically (write-then-rename pattern).
|
|
67
|
+
*/
|
|
68
|
+
function writeJournal(stagingDir, journal) {
|
|
69
|
+
const journalPath = join(stagingDir, JOURNAL_FILENAME);
|
|
70
|
+
const tmpPath = `${journalPath}.tmp`;
|
|
71
|
+
writeFileSync(tmpPath, JSON.stringify(journal, null, 2) + '\n', 'utf8');
|
|
72
|
+
// On POSIX, rename is atomic within the same fs.
|
|
73
|
+
renameSync(tmpPath, journalPath);
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Read an existing journal from the staging dir, or return `null`.
|
|
77
|
+
*/
|
|
78
|
+
function readJournal(stagingDir) {
|
|
79
|
+
const journalPath = join(stagingDir, JOURNAL_FILENAME);
|
|
80
|
+
if (!existsSync(journalPath))
|
|
81
|
+
return null;
|
|
82
|
+
try {
|
|
83
|
+
return JSON.parse(readFileSync(journalPath, 'utf8'));
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Initialise a fresh journal object.
|
|
91
|
+
*/
|
|
92
|
+
function initJournal(sqliteVersion) {
|
|
93
|
+
const now = new Date().toISOString();
|
|
94
|
+
return {
|
|
95
|
+
version: 1,
|
|
96
|
+
cleoVersion: getCleoVersion(),
|
|
97
|
+
targetSchemaVersion: EXODUS_TARGET_SCHEMA_VERSION,
|
|
98
|
+
nodeVersion: process.version,
|
|
99
|
+
sqliteVersion,
|
|
100
|
+
startedAt: now,
|
|
101
|
+
updatedAt: now,
|
|
102
|
+
tables: [],
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
// ---------------------------------------------------------------------------
|
|
106
|
+
// Advisory lock sentinel helpers (AC4)
|
|
107
|
+
// ---------------------------------------------------------------------------
|
|
108
|
+
function lockPath(dbPath) {
|
|
109
|
+
return `${dbPath}${LOCK_SENTINEL_SUFFIX}`;
|
|
110
|
+
}
|
|
111
|
+
function acquireAdvisoryLock(dbPath) {
|
|
112
|
+
const lp = lockPath(dbPath);
|
|
113
|
+
writeFileSync(lp, JSON.stringify({ pid: process.pid, ts: new Date().toISOString() }), 'utf8');
|
|
114
|
+
}
|
|
115
|
+
function releaseAdvisoryLock(dbPath) {
|
|
116
|
+
try {
|
|
117
|
+
unlinkSync(lockPath(dbPath));
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
// Ignore — lock may already be gone
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
// ---------------------------------------------------------------------------
|
|
124
|
+
// Core copy function
|
|
125
|
+
// ---------------------------------------------------------------------------
|
|
126
|
+
/**
|
|
127
|
+
* Copy all rows from `tableName` in the source DB into the target DB using a
|
|
128
|
+
* raw `INSERT INTO … SELECT * FROM …` after ATTACHing the source file.
|
|
129
|
+
*
|
|
130
|
+
* This approach avoids reading all rows into JS memory — SQLite handles the
|
|
131
|
+
* copy entirely in the engine. The ATTACH is scoped to a single statement
|
|
132
|
+
* sequence and detached immediately after (or on error).
|
|
133
|
+
*
|
|
134
|
+
* Returns the number of rows copied.
|
|
135
|
+
*
|
|
136
|
+
* @param targetNativeDb - The target `DatabaseSync` handle (writable).
|
|
137
|
+
* @param sourceDbPath - Absolute path to the read-only source `.db` file.
|
|
138
|
+
* @param tableName - The table to copy.
|
|
139
|
+
* @param idempotencyKeyCol - Optional column name for idempotency key
|
|
140
|
+
* propagation. When present, rows whose key is already in the target are
|
|
141
|
+
* skipped (INSERT OR IGNORE).
|
|
142
|
+
*/
|
|
143
|
+
function copyTableViaAttach(targetNativeDb, sourceDbPath, tableName) {
|
|
144
|
+
const attachAlias = '_exodus_src_';
|
|
145
|
+
// Check source table exists and has rows
|
|
146
|
+
const srcCheck = openCleoDbSnapshot(sourceDbPath, { readOnly: true });
|
|
147
|
+
let sourceCount = 0;
|
|
148
|
+
let columns = [];
|
|
149
|
+
try {
|
|
150
|
+
const countRow = srcCheck.db.prepare(`SELECT COUNT(*) AS c FROM "${tableName}"`).get();
|
|
151
|
+
sourceCount = countRow?.c ?? 0;
|
|
152
|
+
// Get column names for explicit column list
|
|
153
|
+
const pragma = srcCheck.db.prepare(`PRAGMA table_info("${tableName}")`).all();
|
|
154
|
+
columns = pragma.map((r) => r.name);
|
|
155
|
+
}
|
|
156
|
+
finally {
|
|
157
|
+
srcCheck.close();
|
|
158
|
+
}
|
|
159
|
+
if (sourceCount === 0)
|
|
160
|
+
return 0;
|
|
161
|
+
if (columns.length === 0)
|
|
162
|
+
return 0;
|
|
163
|
+
// ATTACH source, copy via INSERT OR IGNORE, DETACH
|
|
164
|
+
// Use INSERT OR IGNORE so idempotent keys prevent duplicates on resume
|
|
165
|
+
const colList = columns.map((c) => `"${c}"`).join(', ');
|
|
166
|
+
targetNativeDb.exec(`ATTACH DATABASE '${sourceDbPath.replace(/'/g, "''")}' AS "${attachAlias}"`);
|
|
167
|
+
try {
|
|
168
|
+
// Check if target table exists
|
|
169
|
+
const existsRow = targetNativeDb
|
|
170
|
+
.prepare(`SELECT name FROM sqlite_master WHERE type='table' AND name='${tableName.replace(/'/g, "''")}'`)
|
|
171
|
+
.get();
|
|
172
|
+
if (!existsRow) {
|
|
173
|
+
// Target table doesn't exist yet — log and skip (E6 will create these)
|
|
174
|
+
log.warn({ tableName }, 'Target table not found in consolidated DB — skipping');
|
|
175
|
+
return 0;
|
|
176
|
+
}
|
|
177
|
+
const stmt = targetNativeDb.prepare(`INSERT OR IGNORE INTO main."${tableName}" (${colList}) SELECT ${colList} FROM "${attachAlias}"."${tableName}" ORDER BY rowid`);
|
|
178
|
+
const result = stmt.run();
|
|
179
|
+
return result.changes ?? 0;
|
|
180
|
+
}
|
|
181
|
+
finally {
|
|
182
|
+
try {
|
|
183
|
+
targetNativeDb.exec(`DETACH DATABASE "${attachAlias}"`);
|
|
184
|
+
}
|
|
185
|
+
catch {
|
|
186
|
+
// Best-effort detach
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
// ---------------------------------------------------------------------------
|
|
191
|
+
// Main migration runner
|
|
192
|
+
// ---------------------------------------------------------------------------
|
|
193
|
+
/**
|
|
194
|
+
* Validate that the journal's schema version matches the current target version.
|
|
195
|
+
*
|
|
196
|
+
* When `forceCrossVersion === true`, mismatches are logged but not fatal.
|
|
197
|
+
*/
|
|
198
|
+
function checkSchemaVersion(journal, forceCrossVersion) {
|
|
199
|
+
if (journal.targetSchemaVersion !== EXODUS_TARGET_SCHEMA_VERSION) {
|
|
200
|
+
const msg = `Schema version mismatch: journal=${journal.targetSchemaVersion}, expected=${EXODUS_TARGET_SCHEMA_VERSION}`;
|
|
201
|
+
if (forceCrossVersion) {
|
|
202
|
+
log.warn(msg + ' (--force-cross-version: continuing anyway)');
|
|
203
|
+
return true;
|
|
204
|
+
}
|
|
205
|
+
log.error(msg + ' — pass --force-cross-version to override');
|
|
206
|
+
return false;
|
|
207
|
+
}
|
|
208
|
+
return true;
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Run the exodus migration.
|
|
212
|
+
*
|
|
213
|
+
* @param plan - Pre-flight plan from `buildExodusPlan()`.
|
|
214
|
+
* @param forceCrossVersion - Skip the schema-version guard (AC9).
|
|
215
|
+
* @param onProgress - Optional progress callback called after each table.
|
|
216
|
+
*
|
|
217
|
+
* @returns {@link ExodusMigrateResult}
|
|
218
|
+
*
|
|
219
|
+
* @task T11248 (AC4, AC5, AC6, AC7, AC9)
|
|
220
|
+
*/
|
|
221
|
+
export async function runExodusMigrate(plan, forceCrossVersion = false, onProgress) {
|
|
222
|
+
const { sources, stagingDir, diskPreflight } = plan;
|
|
223
|
+
// AC8: disk pre-flight
|
|
224
|
+
if (!diskPreflight) {
|
|
225
|
+
return {
|
|
226
|
+
ok: false,
|
|
227
|
+
tables: [],
|
|
228
|
+
stagingDir,
|
|
229
|
+
backupPaths: [],
|
|
230
|
+
error: `Insufficient disk space: need ≥3× source size (${plan.totalSourceBytes} bytes source, ${plan.availableBytes} bytes available). Free up space or use a different storage location.`,
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
// Ensure staging directory exists (AC5)
|
|
234
|
+
mkdirSync(stagingDir, { recursive: true });
|
|
235
|
+
// Determine SQLite version from the first available source DB
|
|
236
|
+
let sqliteVersion = 'unknown';
|
|
237
|
+
for (const src of sources) {
|
|
238
|
+
if (existsSync(src.path)) {
|
|
239
|
+
const snap = openCleoDbSnapshot(src.path, { readOnly: true });
|
|
240
|
+
sqliteVersion = getSqliteVersion(snap.db);
|
|
241
|
+
snap.close();
|
|
242
|
+
break;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
// Load or initialise the journal (AC5 — resume from staging)
|
|
246
|
+
let journal = readJournal(stagingDir);
|
|
247
|
+
if (journal === null) {
|
|
248
|
+
journal = initJournal(sqliteVersion);
|
|
249
|
+
}
|
|
250
|
+
else {
|
|
251
|
+
// Existing journal — check schema version (AC9)
|
|
252
|
+
if (!checkSchemaVersion(journal, forceCrossVersion)) {
|
|
253
|
+
return {
|
|
254
|
+
ok: false,
|
|
255
|
+
tables: [],
|
|
256
|
+
stagingDir,
|
|
257
|
+
backupPaths: [],
|
|
258
|
+
error: 'Schema version mismatch. Pass --force-cross-version to override.',
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
onProgress?.('Resuming from existing staging journal…');
|
|
262
|
+
}
|
|
263
|
+
const backupPaths = [];
|
|
264
|
+
const allTableResults = [];
|
|
265
|
+
const lockedPaths = [];
|
|
266
|
+
try {
|
|
267
|
+
// 1. Back up existing source DBs into staging dir and acquire advisory locks
|
|
268
|
+
for (const src of sources) {
|
|
269
|
+
if (!existsSync(src.path))
|
|
270
|
+
continue;
|
|
271
|
+
const backupDest = join(stagingDir, `${src.name.replace(/[^a-z0-9-]/g, '_')}-backup.db`);
|
|
272
|
+
if (!existsSync(backupDest)) {
|
|
273
|
+
onProgress?.(`Backing up ${src.name} → staging dir…`);
|
|
274
|
+
copyFileSync(src.path, backupDest);
|
|
275
|
+
backupPaths.push(backupDest);
|
|
276
|
+
}
|
|
277
|
+
// AC4: advisory lock sentinel
|
|
278
|
+
acquireAdvisoryLock(src.path);
|
|
279
|
+
lockedPaths.push(src.path);
|
|
280
|
+
}
|
|
281
|
+
// 2. Open (or create) the consolidated target DBs via the chokepoint.
|
|
282
|
+
// This runs Drizzle migrations to create the target schema.
|
|
283
|
+
onProgress?.('Opening consolidated project-scope cleo.db (running migrations)…');
|
|
284
|
+
// openDualScopeDb takes cwd, not a db path — pass undefined to use process.cwd()
|
|
285
|
+
const projectHandle = await openDualScopeDb('project');
|
|
286
|
+
onProgress?.('Opening consolidated global-scope cleo.db (running migrations)…');
|
|
287
|
+
const globalHandle = await openDualScopeDb('global');
|
|
288
|
+
// Extract the raw DatabaseSync from the Drizzle wrapper ($client pattern).
|
|
289
|
+
function extractNativeDb(handle) {
|
|
290
|
+
const drizzleHandle = handle.db;
|
|
291
|
+
const client = drizzleHandle['$client'];
|
|
292
|
+
if (client && typeof client['prepare'] === 'function') {
|
|
293
|
+
return client;
|
|
294
|
+
}
|
|
295
|
+
// Fallback: the handle itself may be a DatabaseSync (unlikely but safe)
|
|
296
|
+
if (typeof drizzleHandle['prepare'] === 'function') {
|
|
297
|
+
return drizzleHandle;
|
|
298
|
+
}
|
|
299
|
+
throw new Error('Could not extract native DatabaseSync from dual-scope DB handle');
|
|
300
|
+
}
|
|
301
|
+
const projectNative = extractNativeDb(projectHandle);
|
|
302
|
+
const globalNative = extractNativeDb(globalHandle);
|
|
303
|
+
// 3. Per-scope BEGIN/COMMIT transactions (AC6)
|
|
304
|
+
// If any table copy fails, ROLLBACK leaves the target untouched.
|
|
305
|
+
// Process project-scope sources
|
|
306
|
+
const projectSources = sources.filter((s) => s.targetScope === 'project' && existsSync(s.path));
|
|
307
|
+
const globalSources = sources.filter((s) => s.targetScope === 'global' && existsSync(s.path));
|
|
308
|
+
await migrateScope('project', projectSources, projectNative, journal, stagingDir, allTableResults, onProgress);
|
|
309
|
+
await migrateScope('global', globalSources, globalNative, journal, stagingDir, allTableResults, onProgress);
|
|
310
|
+
// Final journal update
|
|
311
|
+
journal.updatedAt = new Date().toISOString();
|
|
312
|
+
writeJournal(stagingDir, journal);
|
|
313
|
+
projectHandle.close();
|
|
314
|
+
globalHandle.close();
|
|
315
|
+
return { ok: true, tables: allTableResults, stagingDir, backupPaths };
|
|
316
|
+
}
|
|
317
|
+
catch (err) {
|
|
318
|
+
const error = err instanceof Error ? err.message : String(err);
|
|
319
|
+
log.error({ err }, 'Exodus migration failed');
|
|
320
|
+
return { ok: false, tables: allTableResults, stagingDir, backupPaths, error };
|
|
321
|
+
}
|
|
322
|
+
finally {
|
|
323
|
+
// Release advisory locks
|
|
324
|
+
for (const p of lockedPaths) {
|
|
325
|
+
releaseAdvisoryLock(p);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Migrate all tables from the given sources into the target native DB,
|
|
331
|
+
* wrapped in a single BEGIN/COMMIT transaction per scope.
|
|
332
|
+
*/
|
|
333
|
+
async function migrateScope(scope, sources, targetNativeDb, journal, stagingDir, allTableResults, onProgress) {
|
|
334
|
+
if (sources.length === 0)
|
|
335
|
+
return;
|
|
336
|
+
onProgress?.(`Migrating ${scope}-scope sources…`);
|
|
337
|
+
// Wrap entire scope in one transaction (AC6)
|
|
338
|
+
targetNativeDb.exec('BEGIN');
|
|
339
|
+
try {
|
|
340
|
+
for (const src of sources) {
|
|
341
|
+
const snap = openCleoDbSnapshot(src.path, { readOnly: true });
|
|
342
|
+
try {
|
|
343
|
+
const tables = listTables(snap.db);
|
|
344
|
+
for (const tableName of tables) {
|
|
345
|
+
// Check journal for resume (AC5)
|
|
346
|
+
const existing = journal.tables.find((e) => e.sourceDb === src.name && e.tableName === tableName);
|
|
347
|
+
if (existing?.status === 'done') {
|
|
348
|
+
onProgress?.(` ↳ ${src.name}.${tableName} — already done (resuming)`);
|
|
349
|
+
allTableResults.push({
|
|
350
|
+
sourceDb: src.name,
|
|
351
|
+
tableName,
|
|
352
|
+
rowsCopied: existing.rowsCopied,
|
|
353
|
+
skipped: false,
|
|
354
|
+
});
|
|
355
|
+
continue;
|
|
356
|
+
}
|
|
357
|
+
onProgress?.(` ↳ Copying ${src.name}.${tableName}…`);
|
|
358
|
+
let rowsCopied = 0;
|
|
359
|
+
let status = 'done';
|
|
360
|
+
let errorMsg;
|
|
361
|
+
let skipped = false;
|
|
362
|
+
try {
|
|
363
|
+
rowsCopied = copyTableViaAttach(targetNativeDb, src.path, tableName);
|
|
364
|
+
}
|
|
365
|
+
catch (err) {
|
|
366
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
367
|
+
log.warn({ tableName, sourceDb: src.name, err }, 'Table copy failed — skipping');
|
|
368
|
+
status = 'skipped';
|
|
369
|
+
errorMsg = msg;
|
|
370
|
+
skipped = true;
|
|
371
|
+
}
|
|
372
|
+
// Update journal entry
|
|
373
|
+
const entry = {
|
|
374
|
+
sourceDb: src.name,
|
|
375
|
+
tableName,
|
|
376
|
+
status,
|
|
377
|
+
rowsCopied,
|
|
378
|
+
updatedAt: new Date().toISOString(),
|
|
379
|
+
...(errorMsg ? { error: errorMsg } : {}),
|
|
380
|
+
};
|
|
381
|
+
const idx = journal.tables.findIndex((e) => e.sourceDb === src.name && e.tableName === tableName);
|
|
382
|
+
if (idx >= 0) {
|
|
383
|
+
journal.tables[idx] = entry;
|
|
384
|
+
}
|
|
385
|
+
else {
|
|
386
|
+
journal.tables.push(entry);
|
|
387
|
+
}
|
|
388
|
+
journal.updatedAt = new Date().toISOString();
|
|
389
|
+
// Atomic journal write after each table (AC5 — crash-resumable)
|
|
390
|
+
writeJournal(stagingDir, journal);
|
|
391
|
+
allTableResults.push({
|
|
392
|
+
sourceDb: src.name,
|
|
393
|
+
tableName,
|
|
394
|
+
rowsCopied,
|
|
395
|
+
skipped,
|
|
396
|
+
reason: errorMsg,
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
finally {
|
|
401
|
+
snap.close();
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
targetNativeDb.exec('COMMIT');
|
|
405
|
+
}
|
|
406
|
+
catch (err) {
|
|
407
|
+
try {
|
|
408
|
+
targetNativeDb.exec('ROLLBACK');
|
|
409
|
+
}
|
|
410
|
+
catch {
|
|
411
|
+
// ignore rollback errors
|
|
412
|
+
}
|
|
413
|
+
throw err;
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
//# sourceMappingURL=migrate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migrate.js","sourceRoot":"","sources":["../../../src/store/exodus/migrate.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Exodus pre-flight plan builder.
|
|
3
|
+
*
|
|
4
|
+
* `buildExodusPlan()` computes the full migration plan — source DB paths,
|
|
5
|
+
* combined size, free-disk availability, staging directory — BEFORE any
|
|
6
|
+
* writes occur. A `--dry-run` caller can print the plan and exit early.
|
|
7
|
+
*
|
|
8
|
+
* ## Disk pre-flight (AC8)
|
|
9
|
+
*
|
|
10
|
+
* `availableBytes >= 3 * totalSourceBytes` must hold before migration begins.
|
|
11
|
+
* The check uses `statvfs` via `node:fs.statfsSync()` (Node 18+).
|
|
12
|
+
*
|
|
13
|
+
* @task T11248 (E5 · SG-DB-SUBSTRATE-V2)
|
|
14
|
+
* @saga T11242
|
|
15
|
+
*/
|
|
16
|
+
import type { ExodusPlan, LegacyDbDescriptor } from './types.js';
|
|
17
|
+
/**
|
|
18
|
+
* Derive the staging directory name from the current ISO-8601 timestamp.
|
|
19
|
+
*
|
|
20
|
+
* Pattern: `.cleo/exodus-staging-<YYYYMMDDTHHMMSSZ>`.
|
|
21
|
+
* Colons are replaced with empty string (NTFS + shell-safe).
|
|
22
|
+
*/
|
|
23
|
+
export declare function deriveStagingDirName(): string;
|
|
24
|
+
/**
|
|
25
|
+
* Build the complete exodus plan.
|
|
26
|
+
*
|
|
27
|
+
* This is a pure read operation — no files are created or modified.
|
|
28
|
+
* Pass the result to `runExodusMigrate()` to execute the migration.
|
|
29
|
+
*
|
|
30
|
+
* @param cwd - Working directory used to resolve the project root. Defaults to
|
|
31
|
+
* `process.cwd()`.
|
|
32
|
+
* @returns {@link ExodusPlan} describing sources, disk availability, and paths.
|
|
33
|
+
*
|
|
34
|
+
* @task T11248 (AC8 — 3× disk pre-flight)
|
|
35
|
+
*/
|
|
36
|
+
export declare function buildExodusPlan(cwd?: string): ExodusPlan;
|
|
37
|
+
/**
|
|
38
|
+
* Check whether all required source DBs exist.
|
|
39
|
+
*
|
|
40
|
+
* Returns `true` if at least one source file is present (partial sets are
|
|
41
|
+
* acceptable — empty tables simply copy zero rows).
|
|
42
|
+
*/
|
|
43
|
+
export declare function sourcesPresent(sources: LegacyDbDescriptor[]): boolean;
|
|
44
|
+
//# sourceMappingURL=plan.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plan.d.ts","sourceRoot":"","sources":["../../../src/store/exodus/plan.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAMH,OAAO,KAAK,EAAE,UAAU,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AA8EjE;;;;;GAKG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,CAM7C;AA0BD;;;;;;;;;;;GAWG;AACH,wBAAgB,eAAe,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,UAAU,CA+BxD;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,kBAAkB,EAAE,GAAG,OAAO,CAErE"}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Exodus pre-flight plan builder.
|
|
3
|
+
*
|
|
4
|
+
* `buildExodusPlan()` computes the full migration plan — source DB paths,
|
|
5
|
+
* combined size, free-disk availability, staging directory — BEFORE any
|
|
6
|
+
* writes occur. A `--dry-run` caller can print the plan and exit early.
|
|
7
|
+
*
|
|
8
|
+
* ## Disk pre-flight (AC8)
|
|
9
|
+
*
|
|
10
|
+
* `availableBytes >= 3 * totalSourceBytes` must hold before migration begins.
|
|
11
|
+
* The check uses `statvfs` via `node:fs.statfsSync()` (Node 18+).
|
|
12
|
+
*
|
|
13
|
+
* @task T11248 (E5 · SG-DB-SUBSTRATE-V2)
|
|
14
|
+
* @saga T11242
|
|
15
|
+
*/
|
|
16
|
+
import { existsSync, readdirSync, statfsSync, statSync } from 'node:fs';
|
|
17
|
+
import { join } from 'node:path';
|
|
18
|
+
import { getCleoHome, resolveCleoDir } from '../../paths.js';
|
|
19
|
+
import { resolveDualScopeDbPath } from '../dual-scope-db.js';
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
// Legacy DB descriptors (AC2 — 6 per-machine source DBs mapped to 2 targets)
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
/**
|
|
24
|
+
* Build the ordered list of legacy source DB descriptors for a given project.
|
|
25
|
+
*
|
|
26
|
+
* Project-scoped sources live under `<project>/.cleo/`.
|
|
27
|
+
* Global-scoped sources live under `getCleoHome()`.
|
|
28
|
+
*/
|
|
29
|
+
function buildSourceDescriptors(cwd) {
|
|
30
|
+
const cleoDir = resolveCleoDir(cwd);
|
|
31
|
+
const cleoHome = getCleoHome();
|
|
32
|
+
return [
|
|
33
|
+
// Project-tier — go into consolidated project-scope cleo.db
|
|
34
|
+
{
|
|
35
|
+
name: 'tasks',
|
|
36
|
+
path: join(cleoDir, 'tasks.db'),
|
|
37
|
+
targetScope: 'project',
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
name: 'brain (project)',
|
|
41
|
+
path: join(cleoDir, 'brain.db'),
|
|
42
|
+
targetScope: 'project',
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
name: 'conduit',
|
|
46
|
+
path: join(cleoDir, 'conduit.db'),
|
|
47
|
+
targetScope: 'project',
|
|
48
|
+
},
|
|
49
|
+
// Global-tier — go into consolidated global-scope cleo.db
|
|
50
|
+
{
|
|
51
|
+
name: 'nexus',
|
|
52
|
+
path: join(cleoHome, 'nexus.db'),
|
|
53
|
+
targetScope: 'global',
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
name: 'signaldock',
|
|
57
|
+
path: join(cleoHome, 'signaldock.db'),
|
|
58
|
+
targetScope: 'global',
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
name: 'skills',
|
|
62
|
+
path: join(cleoHome, 'skills.db'),
|
|
63
|
+
targetScope: 'global',
|
|
64
|
+
},
|
|
65
|
+
];
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Safely stat a file and return its size in bytes, or 0 if it does not exist.
|
|
69
|
+
*/
|
|
70
|
+
function safeFileBytes(filePath) {
|
|
71
|
+
try {
|
|
72
|
+
return statSync(filePath).size;
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
return 0;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Return available bytes on the filesystem that contains `dir`.
|
|
80
|
+
*
|
|
81
|
+
* Uses `statfsSync` (Node 18+). Falls back to 0 if unavailable.
|
|
82
|
+
*/
|
|
83
|
+
function getAvailableBytes(dir) {
|
|
84
|
+
try {
|
|
85
|
+
const result = statfsSync(dir);
|
|
86
|
+
// `bfree` = blocks free for privileged processes; `bavail` = unprivileged.
|
|
87
|
+
return (result.bavail ?? result.bfree ?? 0) * (result.bsize ?? 4096);
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
return 0;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Derive the staging directory name from the current ISO-8601 timestamp.
|
|
95
|
+
*
|
|
96
|
+
* Pattern: `.cleo/exodus-staging-<YYYYMMDDTHHMMSSZ>`.
|
|
97
|
+
* Colons are replaced with empty string (NTFS + shell-safe).
|
|
98
|
+
*/
|
|
99
|
+
export function deriveStagingDirName() {
|
|
100
|
+
const iso = new Date()
|
|
101
|
+
.toISOString()
|
|
102
|
+
.replace(/[:]/g, '')
|
|
103
|
+
.replace(/\..+Z$/, 'Z');
|
|
104
|
+
return `exodus-staging-${iso}`;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Scan for an existing staging directory from a previous (possibly crashed)
|
|
108
|
+
* run inside the given `.cleo/` directory.
|
|
109
|
+
*
|
|
110
|
+
* Returns the absolute path to the most-recent staging dir, or `null` if none
|
|
111
|
+
* exists.
|
|
112
|
+
*/
|
|
113
|
+
function findExistingStaging(cleoDir) {
|
|
114
|
+
try {
|
|
115
|
+
const entries = readdirSync(cleoDir, { withFileTypes: true });
|
|
116
|
+
const stagingDirs = entries
|
|
117
|
+
.filter((e) => e.isDirectory() && e.name.startsWith('exodus-staging-'))
|
|
118
|
+
.map((e) => e.name)
|
|
119
|
+
.sort()
|
|
120
|
+
.reverse(); // most recent first
|
|
121
|
+
if (stagingDirs.length > 0) {
|
|
122
|
+
return join(cleoDir, stagingDirs[0]);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
catch {
|
|
126
|
+
// .cleo/ may not exist yet
|
|
127
|
+
}
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Build the complete exodus plan.
|
|
132
|
+
*
|
|
133
|
+
* This is a pure read operation — no files are created or modified.
|
|
134
|
+
* Pass the result to `runExodusMigrate()` to execute the migration.
|
|
135
|
+
*
|
|
136
|
+
* @param cwd - Working directory used to resolve the project root. Defaults to
|
|
137
|
+
* `process.cwd()`.
|
|
138
|
+
* @returns {@link ExodusPlan} describing sources, disk availability, and paths.
|
|
139
|
+
*
|
|
140
|
+
* @task T11248 (AC8 — 3× disk pre-flight)
|
|
141
|
+
*/
|
|
142
|
+
export function buildExodusPlan(cwd) {
|
|
143
|
+
const cleoDir = resolveCleoDir(cwd);
|
|
144
|
+
const sources = buildSourceDescriptors(cwd);
|
|
145
|
+
// Compute total source size (only existing files count toward the check)
|
|
146
|
+
const totalSourceBytes = sources.reduce((sum, s) => sum + safeFileBytes(s.path), 0);
|
|
147
|
+
// Disk pre-flight: check against the directory that will hold the staging data
|
|
148
|
+
// (the .cleo/ dir, which is where both the backup and staging live).
|
|
149
|
+
const availableBytes = getAvailableBytes(cleoDir);
|
|
150
|
+
const diskPreflight = totalSourceBytes === 0 || availableBytes >= 3 * totalSourceBytes;
|
|
151
|
+
// Staging directory — resume if a previous one exists
|
|
152
|
+
const existingStaging = findExistingStaging(cleoDir);
|
|
153
|
+
const stagingDir = existingStaging ?? join(cleoDir, deriveStagingDirName());
|
|
154
|
+
const resumeFromStaging = existingStaging !== null;
|
|
155
|
+
// Target paths (consolidated cleo.db)
|
|
156
|
+
const projectDbPath = resolveDualScopeDbPath('project', cwd);
|
|
157
|
+
const globalDbPath = resolveDualScopeDbPath('global');
|
|
158
|
+
return {
|
|
159
|
+
sources,
|
|
160
|
+
totalSourceBytes,
|
|
161
|
+
availableBytes,
|
|
162
|
+
diskPreflight,
|
|
163
|
+
stagingDir,
|
|
164
|
+
resumeFromStaging,
|
|
165
|
+
projectDbPath,
|
|
166
|
+
globalDbPath,
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Check whether all required source DBs exist.
|
|
171
|
+
*
|
|
172
|
+
* Returns `true` if at least one source file is present (partial sets are
|
|
173
|
+
* acceptable — empty tables simply copy zero rows).
|
|
174
|
+
*/
|
|
175
|
+
export function sourcesPresent(sources) {
|
|
176
|
+
return sources.some((s) => existsSync(s.path));
|
|
177
|
+
}
|
|
178
|
+
//# sourceMappingURL=plan.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plan.js","sourceRoot":"","sources":["../../../src/store/exodus/plan.ts"],"names":[],"mappings":"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"}
|