@cleocode/cleo 2026.3.0 → 2026.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +196 -118
- package/dist/cli/index.js.map +4 -4
- package/dist/mcp/index.js +173 -94
- package/dist/mcp/index.js.map +4 -4
- package/package.json +1 -1
package/dist/mcp/index.js
CHANGED
|
@@ -939,6 +939,65 @@ var init_node_sqlite_adapter = __esm({
|
|
|
939
939
|
}
|
|
940
940
|
});
|
|
941
941
|
|
|
942
|
+
// src/store/sqlite-backup.ts
|
|
943
|
+
import { existsSync as existsSync3, mkdirSync, readdirSync, statSync, unlinkSync } from "node:fs";
|
|
944
|
+
import { join as join3 } from "node:path";
|
|
945
|
+
function formatTimestamp(d) {
|
|
946
|
+
const pad = (n, len = 2) => String(n).padStart(len, "0");
|
|
947
|
+
return `${d.getFullYear()}${pad(d.getMonth() + 1)}${pad(d.getDate())}-${pad(d.getHours())}${pad(d.getMinutes())}${pad(d.getSeconds())}`;
|
|
948
|
+
}
|
|
949
|
+
function rotateSnapshots(backupDir) {
|
|
950
|
+
try {
|
|
951
|
+
const files = readdirSync(backupDir).filter((f) => f.match(/^tasks-\d{8}-\d{6}\.db$/)).map((f) => ({ name: f, path: join3(backupDir, f), mtimeMs: statSync(join3(backupDir, f)).mtimeMs })).sort((a, b) => a.mtimeMs - b.mtimeMs);
|
|
952
|
+
while (files.length >= MAX_SNAPSHOTS) {
|
|
953
|
+
const oldest = files.shift();
|
|
954
|
+
unlinkSync(oldest.path);
|
|
955
|
+
}
|
|
956
|
+
} catch {
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
async function vacuumIntoBackup(opts = {}) {
|
|
960
|
+
const now = Date.now();
|
|
961
|
+
if (!opts.force && now - _lastBackupEpoch < DEBOUNCE_MS) {
|
|
962
|
+
return;
|
|
963
|
+
}
|
|
964
|
+
try {
|
|
965
|
+
const cleoDir = getCleoDir(opts.cwd);
|
|
966
|
+
const backupDir = join3(cleoDir, "backups", "sqlite");
|
|
967
|
+
mkdirSync(backupDir, { recursive: true });
|
|
968
|
+
const db = getNativeDb();
|
|
969
|
+
if (!db) return;
|
|
970
|
+
const dest = join3(backupDir, `tasks-${formatTimestamp(/* @__PURE__ */ new Date())}.db`);
|
|
971
|
+
db.exec("PRAGMA wal_checkpoint(TRUNCATE)");
|
|
972
|
+
rotateSnapshots(backupDir);
|
|
973
|
+
const safeDest = dest.replace(/'/g, "''");
|
|
974
|
+
db.exec(`VACUUM INTO '${safeDest}'`);
|
|
975
|
+
_lastBackupEpoch = Date.now();
|
|
976
|
+
} catch {
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
function listSqliteBackups(cwd) {
|
|
980
|
+
try {
|
|
981
|
+
const cleoDir = getCleoDir(cwd);
|
|
982
|
+
const backupDir = join3(cleoDir, "backups", "sqlite");
|
|
983
|
+
if (!existsSync3(backupDir)) return [];
|
|
984
|
+
return readdirSync(backupDir).filter((f) => f.match(/^tasks-\d{8}-\d{6}\.db$/)).map((f) => ({ name: f, path: join3(backupDir, f), mtimeMs: statSync(join3(backupDir, f)).mtimeMs })).sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
985
|
+
} catch {
|
|
986
|
+
return [];
|
|
987
|
+
}
|
|
988
|
+
}
|
|
989
|
+
var MAX_SNAPSHOTS, DEBOUNCE_MS, _lastBackupEpoch;
|
|
990
|
+
var init_sqlite_backup = __esm({
|
|
991
|
+
"src/store/sqlite-backup.ts"() {
|
|
992
|
+
"use strict";
|
|
993
|
+
init_paths();
|
|
994
|
+
init_sqlite();
|
|
995
|
+
MAX_SNAPSHOTS = 10;
|
|
996
|
+
DEBOUNCE_MS = 3e4;
|
|
997
|
+
_lastBackupEpoch = 0;
|
|
998
|
+
}
|
|
999
|
+
});
|
|
1000
|
+
|
|
942
1001
|
// src/store/sqlite.ts
|
|
943
1002
|
var sqlite_exports = {};
|
|
944
1003
|
__export(sqlite_exports, {
|
|
@@ -954,16 +1013,74 @@ __export(sqlite_exports, {
|
|
|
954
1013
|
resolveMigrationsFolder: () => resolveMigrationsFolder,
|
|
955
1014
|
schema: () => schema_exports
|
|
956
1015
|
});
|
|
957
|
-
import { existsSync as
|
|
1016
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync2, copyFileSync, unlinkSync as unlinkSync2, renameSync as renameSync2 } from "node:fs";
|
|
958
1017
|
import { createRequire as createRequire2 } from "node:module";
|
|
959
|
-
import { dirname as dirname2, join as
|
|
1018
|
+
import { dirname as dirname2, join as join4, resolve as resolve3 } from "node:path";
|
|
960
1019
|
import { fileURLToPath } from "node:url";
|
|
961
1020
|
import { eq } from "drizzle-orm";
|
|
962
1021
|
import { readMigrationFiles } from "drizzle-orm/migrator";
|
|
963
1022
|
import { drizzle } from "drizzle-orm/sqlite-proxy";
|
|
964
1023
|
import { migrate } from "drizzle-orm/sqlite-proxy/migrator";
|
|
965
1024
|
function getDbPath(cwd) {
|
|
966
|
-
return
|
|
1025
|
+
return join4(getCleoDirAbsolute(cwd), DB_FILENAME);
|
|
1026
|
+
}
|
|
1027
|
+
async function autoRecoverFromBackup(nativeDb, dbPath, cwd) {
|
|
1028
|
+
const log5 = getLogger("sqlite");
|
|
1029
|
+
try {
|
|
1030
|
+
const countResult = nativeDb.prepare("SELECT COUNT(*) as cnt FROM tasks").get();
|
|
1031
|
+
const taskCount = countResult?.cnt ?? 0;
|
|
1032
|
+
if (taskCount > 0) return;
|
|
1033
|
+
const backups = listSqliteBackups(cwd);
|
|
1034
|
+
if (backups.length === 0) {
|
|
1035
|
+
return;
|
|
1036
|
+
}
|
|
1037
|
+
const newestBackup = backups[0];
|
|
1038
|
+
const backupDb = new DatabaseSync2(newestBackup.path, { readOnly: true });
|
|
1039
|
+
let backupTaskCount = 0;
|
|
1040
|
+
try {
|
|
1041
|
+
const backupCount = backupDb.prepare("SELECT COUNT(*) as cnt FROM tasks").get();
|
|
1042
|
+
backupTaskCount = backupCount?.cnt ?? 0;
|
|
1043
|
+
} finally {
|
|
1044
|
+
backupDb.close();
|
|
1045
|
+
}
|
|
1046
|
+
if (backupTaskCount < MIN_BACKUP_TASK_COUNT) {
|
|
1047
|
+
return;
|
|
1048
|
+
}
|
|
1049
|
+
log5.warn(
|
|
1050
|
+
{ dbPath, backupPath: newestBackup.path, backupTasks: backupTaskCount },
|
|
1051
|
+
`Empty database detected with ${backupTaskCount}-task backup available. Auto-recovering from backup. This likely happened because git-tracked WAL/SHM files were overwritten during a branch switch (T5188).`
|
|
1052
|
+
);
|
|
1053
|
+
nativeDb.close();
|
|
1054
|
+
const walPath = dbPath + "-wal";
|
|
1055
|
+
const shmPath = dbPath + "-shm";
|
|
1056
|
+
try {
|
|
1057
|
+
unlinkSync2(walPath);
|
|
1058
|
+
} catch {
|
|
1059
|
+
}
|
|
1060
|
+
try {
|
|
1061
|
+
unlinkSync2(shmPath);
|
|
1062
|
+
} catch {
|
|
1063
|
+
}
|
|
1064
|
+
const tempPath = dbPath + ".recovery-tmp";
|
|
1065
|
+
copyFileSync(newestBackup.path, tempPath);
|
|
1066
|
+
renameSync2(tempPath, dbPath);
|
|
1067
|
+
log5.info(
|
|
1068
|
+
{ dbPath, backupPath: newestBackup.path, restoredTasks: backupTaskCount },
|
|
1069
|
+
"Database auto-recovered from backup successfully."
|
|
1070
|
+
);
|
|
1071
|
+
const restoredNativeDb = openNativeDatabase(dbPath);
|
|
1072
|
+
_nativeDb = restoredNativeDb;
|
|
1073
|
+
const callback = createDrizzleCallback(restoredNativeDb);
|
|
1074
|
+
const batchCb = createBatchCallback(restoredNativeDb);
|
|
1075
|
+
const restoredDb = drizzle(callback, batchCb, { schema: schema_exports });
|
|
1076
|
+
await runMigrations(restoredNativeDb, restoredDb);
|
|
1077
|
+
_db = restoredDb;
|
|
1078
|
+
} catch (err) {
|
|
1079
|
+
log5.error(
|
|
1080
|
+
{ err, dbPath },
|
|
1081
|
+
"Auto-recovery from backup failed. Continuing with empty database."
|
|
1082
|
+
);
|
|
1083
|
+
}
|
|
967
1084
|
}
|
|
968
1085
|
async function getDb(cwd) {
|
|
969
1086
|
const requestedPath = getDbPath(cwd);
|
|
@@ -975,7 +1092,7 @@ async function getDb(cwd) {
|
|
|
975
1092
|
_initPromise = (async () => {
|
|
976
1093
|
const dbPath = requestedPath;
|
|
977
1094
|
_dbPath = dbPath;
|
|
978
|
-
|
|
1095
|
+
mkdirSync2(dirname2(dbPath), { recursive: true });
|
|
979
1096
|
const nativeDb = openNativeDatabase(dbPath);
|
|
980
1097
|
_nativeDb = nativeDb;
|
|
981
1098
|
const callback = createDrizzleCallback(nativeDb);
|
|
@@ -988,19 +1105,28 @@ async function getDb(cwd) {
|
|
|
988
1105
|
nativeDb.exec(
|
|
989
1106
|
`INSERT OR IGNORE INTO schema_meta (key, value) VALUES ('task_id_sequence', '{"counter":0,"lastId":"T000","checksum":"seed"}')`
|
|
990
1107
|
);
|
|
1108
|
+
await autoRecoverFromBackup(nativeDb, dbPath, cwd);
|
|
991
1109
|
if (!_gitTrackingChecked) {
|
|
992
1110
|
_gitTrackingChecked = true;
|
|
993
1111
|
try {
|
|
994
1112
|
const { execFileSync: execFileSync6 } = await import("node:child_process");
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
stdio: "pipe"
|
|
998
|
-
});
|
|
1113
|
+
const gitCwd = resolve3(dbPath, "..", "..");
|
|
1114
|
+
const filesToCheck = [dbPath, dbPath + "-wal", dbPath + "-shm"];
|
|
999
1115
|
const log5 = getLogger("sqlite");
|
|
1000
|
-
|
|
1001
|
-
{
|
|
1002
|
-
|
|
1003
|
-
|
|
1116
|
+
for (const fileToCheck of filesToCheck) {
|
|
1117
|
+
try {
|
|
1118
|
+
execFileSync6("git", ["ls-files", "--error-unmatch", fileToCheck], {
|
|
1119
|
+
cwd: gitCwd,
|
|
1120
|
+
stdio: "pipe"
|
|
1121
|
+
});
|
|
1122
|
+
const basename7 = fileToCheck.split("/").pop();
|
|
1123
|
+
log5.warn(
|
|
1124
|
+
{ path: fileToCheck },
|
|
1125
|
+
`${basename7} is tracked by project git \u2014 this risks data loss on branch switch. Run: git rm --cached ${fileToCheck.replace(gitCwd + "/", "")} (see ADR-013, T5188)`
|
|
1126
|
+
);
|
|
1127
|
+
} catch {
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1004
1130
|
} catch {
|
|
1005
1131
|
}
|
|
1006
1132
|
}
|
|
@@ -1016,7 +1142,7 @@ async function getDb(cwd) {
|
|
|
1016
1142
|
function resolveMigrationsFolder() {
|
|
1017
1143
|
const __filename = fileURLToPath(import.meta.url);
|
|
1018
1144
|
const __dirname2 = dirname2(__filename);
|
|
1019
|
-
return
|
|
1145
|
+
return join4(__dirname2, "..", "..", "drizzle");
|
|
1020
1146
|
}
|
|
1021
1147
|
function tableExists(nativeDb, tableName) {
|
|
1022
1148
|
const result = nativeDb.prepare(
|
|
@@ -1110,12 +1236,12 @@ async function getSchemaVersion(cwd) {
|
|
|
1110
1236
|
return result[0]?.value ?? null;
|
|
1111
1237
|
}
|
|
1112
1238
|
function dbExists(cwd) {
|
|
1113
|
-
return
|
|
1239
|
+
return existsSync4(getDbPath(cwd));
|
|
1114
1240
|
}
|
|
1115
1241
|
function getNativeDb() {
|
|
1116
1242
|
return _nativeDb;
|
|
1117
1243
|
}
|
|
1118
|
-
var _require2, DatabaseSync2, DB_FILENAME, SQLITE_SCHEMA_VERSION, SCHEMA_VERSION, _db, _nativeDb, _dbPath, _initPromise, _gitTrackingChecked, MAX_MIGRATION_RETRIES, MIGRATION_RETRY_BASE_DELAY_MS, MIGRATION_RETRY_MAX_DELAY_MS;
|
|
1244
|
+
var _require2, DatabaseSync2, DB_FILENAME, SQLITE_SCHEMA_VERSION, SCHEMA_VERSION, _db, _nativeDb, _dbPath, _initPromise, _gitTrackingChecked, MIN_BACKUP_TASK_COUNT, MAX_MIGRATION_RETRIES, MIGRATION_RETRY_BASE_DELAY_MS, MIGRATION_RETRY_MAX_DELAY_MS;
|
|
1119
1245
|
var init_sqlite = __esm({
|
|
1120
1246
|
"src/store/sqlite.ts"() {
|
|
1121
1247
|
"use strict";
|
|
@@ -1123,6 +1249,7 @@ var init_sqlite = __esm({
|
|
|
1123
1249
|
init_paths();
|
|
1124
1250
|
init_node_sqlite_adapter();
|
|
1125
1251
|
init_logger();
|
|
1252
|
+
init_sqlite_backup();
|
|
1126
1253
|
_require2 = createRequire2(import.meta.url);
|
|
1127
1254
|
({ DatabaseSync: DatabaseSync2 } = _require2("node:sqlite"));
|
|
1128
1255
|
DB_FILENAME = "tasks.db";
|
|
@@ -1133,6 +1260,7 @@ var init_sqlite = __esm({
|
|
|
1133
1260
|
_dbPath = null;
|
|
1134
1261
|
_initPromise = null;
|
|
1135
1262
|
_gitTrackingChecked = false;
|
|
1263
|
+
MIN_BACKUP_TASK_COUNT = 10;
|
|
1136
1264
|
MAX_MIGRATION_RETRIES = 5;
|
|
1137
1265
|
MIGRATION_RETRY_BASE_DELAY_MS = 100;
|
|
1138
1266
|
MIGRATION_RETRY_MAX_DELAY_MS = 2e3;
|
|
@@ -1397,7 +1525,7 @@ var init_atomic = __esm({
|
|
|
1397
1525
|
|
|
1398
1526
|
// src/store/backup.ts
|
|
1399
1527
|
import { copyFile, rename as fsRename, readdir, unlink as unlink2, stat, mkdir as mkdir2 } from "node:fs/promises";
|
|
1400
|
-
import { join as
|
|
1528
|
+
import { join as join5, basename } from "node:path";
|
|
1401
1529
|
async function createBackup(filePath, backupDir, maxBackups = DEFAULT_MAX_BACKUPS) {
|
|
1402
1530
|
try {
|
|
1403
1531
|
await mkdir2(backupDir, { recursive: true });
|
|
@@ -1411,14 +1539,14 @@ async function createBackup(filePath, backupDir, maxBackups = DEFAULT_MAX_BACKUP
|
|
|
1411
1539
|
);
|
|
1412
1540
|
}
|
|
1413
1541
|
for (let i = maxBackups; i >= 1; i--) {
|
|
1414
|
-
const current =
|
|
1542
|
+
const current = join5(backupDir, `${fileName}.${i}`);
|
|
1415
1543
|
if (i === maxBackups) {
|
|
1416
1544
|
try {
|
|
1417
1545
|
await unlink2(current);
|
|
1418
1546
|
} catch {
|
|
1419
1547
|
}
|
|
1420
1548
|
} else {
|
|
1421
|
-
const next =
|
|
1549
|
+
const next = join5(backupDir, `${fileName}.${i + 1}`);
|
|
1422
1550
|
try {
|
|
1423
1551
|
await stat(current);
|
|
1424
1552
|
await fsRename(current, next);
|
|
@@ -1426,7 +1554,7 @@ async function createBackup(filePath, backupDir, maxBackups = DEFAULT_MAX_BACKUP
|
|
|
1426
1554
|
}
|
|
1427
1555
|
}
|
|
1428
1556
|
}
|
|
1429
|
-
const backupPath =
|
|
1557
|
+
const backupPath = join5(backupDir, `${fileName}.1`);
|
|
1430
1558
|
await copyFile(filePath, backupPath);
|
|
1431
1559
|
return backupPath;
|
|
1432
1560
|
} catch (err) {
|
|
@@ -2196,15 +2324,15 @@ var init_sqlite_data_accessor = __esm({
|
|
|
2196
2324
|
|
|
2197
2325
|
// src/store/git-checkpoint.ts
|
|
2198
2326
|
import { readFile as readFile2, writeFile } from "node:fs/promises";
|
|
2199
|
-
import { existsSync as
|
|
2327
|
+
import { existsSync as existsSync5 } from "node:fs";
|
|
2200
2328
|
import { execFile } from "node:child_process";
|
|
2201
2329
|
import { promisify } from "node:util";
|
|
2202
|
-
import { join as
|
|
2330
|
+
import { join as join6, resolve as resolve4 } from "node:path";
|
|
2203
2331
|
function makeCleoGitEnv(cleoDir) {
|
|
2204
2332
|
const abs = resolve4(cleoDir);
|
|
2205
2333
|
return {
|
|
2206
2334
|
...process.env,
|
|
2207
|
-
GIT_DIR:
|
|
2335
|
+
GIT_DIR: join6(abs, ".git"),
|
|
2208
2336
|
GIT_WORK_TREE: abs
|
|
2209
2337
|
};
|
|
2210
2338
|
}
|
|
@@ -2222,7 +2350,7 @@ async function cleoGitCommand(args, cleoDir) {
|
|
|
2222
2350
|
}
|
|
2223
2351
|
}
|
|
2224
2352
|
function isCleoGitInitialized(cleoDir) {
|
|
2225
|
-
return
|
|
2353
|
+
return existsSync5(join6(cleoDir, ".git", "HEAD"));
|
|
2226
2354
|
}
|
|
2227
2355
|
async function loadCheckpointConfig(cwd) {
|
|
2228
2356
|
try {
|
|
@@ -2249,25 +2377,25 @@ async function isCleoGitRepo(cleoDir) {
|
|
|
2249
2377
|
return result.success && (result.stdout === "true" || isCleoGitInitialized(cleoDir));
|
|
2250
2378
|
}
|
|
2251
2379
|
function isMergeInProgress(cleoDir) {
|
|
2252
|
-
return
|
|
2380
|
+
return existsSync5(join6(cleoDir, ".git", "MERGE_HEAD"));
|
|
2253
2381
|
}
|
|
2254
2382
|
async function isDetachedHead(cleoDir) {
|
|
2255
2383
|
const result = await cleoGitCommand(["symbolic-ref", "HEAD"], cleoDir);
|
|
2256
2384
|
return !result.success;
|
|
2257
2385
|
}
|
|
2258
2386
|
function isRebaseInProgress(cleoDir) {
|
|
2259
|
-
return
|
|
2387
|
+
return existsSync5(join6(cleoDir, ".git", "rebase-merge")) || existsSync5(join6(cleoDir, ".git", "rebase-apply"));
|
|
2260
2388
|
}
|
|
2261
2389
|
async function recordCheckpointTime(cleoDir) {
|
|
2262
2390
|
try {
|
|
2263
|
-
const stateFile =
|
|
2391
|
+
const stateFile = join6(cleoDir, CHECKPOINT_STATE_FILE);
|
|
2264
2392
|
await writeFile(stateFile, String(Math.floor(Date.now() / 1e3)));
|
|
2265
2393
|
} catch {
|
|
2266
2394
|
}
|
|
2267
2395
|
}
|
|
2268
2396
|
async function getLastCheckpointTime(cleoDir) {
|
|
2269
2397
|
try {
|
|
2270
|
-
const stateFile =
|
|
2398
|
+
const stateFile = join6(cleoDir, CHECKPOINT_STATE_FILE);
|
|
2271
2399
|
const content = await readFile2(stateFile, "utf-8");
|
|
2272
2400
|
const epoch = parseInt(content.trim(), 10);
|
|
2273
2401
|
return isNaN(epoch) ? 0 : epoch;
|
|
@@ -2278,8 +2406,8 @@ async function getLastCheckpointTime(cleoDir) {
|
|
|
2278
2406
|
async function getChangedStateFiles(cleoDir) {
|
|
2279
2407
|
const changed = [];
|
|
2280
2408
|
for (const stateFile of STATE_FILES) {
|
|
2281
|
-
const fullPath =
|
|
2282
|
-
if (!
|
|
2409
|
+
const fullPath = join6(cleoDir, stateFile);
|
|
2410
|
+
if (!existsSync5(fullPath)) continue;
|
|
2283
2411
|
const diffResult = await cleoGitCommand(["diff", "--quiet", "--", stateFile], cleoDir);
|
|
2284
2412
|
const cachedResult = await cleoGitCommand(["diff", "--cached", "--quiet", "--", stateFile], cleoDir);
|
|
2285
2413
|
const untrackedResult = await cleoGitCommand(
|
|
@@ -2303,7 +2431,7 @@ async function shouldCheckpoint(options) {
|
|
|
2303
2431
|
const config = await loadCheckpointConfig(cwd);
|
|
2304
2432
|
if (!config.enabled) return false;
|
|
2305
2433
|
const cleoDir = getCleoDir(cwd);
|
|
2306
|
-
if (!
|
|
2434
|
+
if (!existsSync5(cleoDir)) return false;
|
|
2307
2435
|
if (!isCleoGitInitialized(cleoDir)) return false;
|
|
2308
2436
|
if (!await isCleoGitRepo(cleoDir)) return false;
|
|
2309
2437
|
if (isMergeInProgress(cleoDir)) return false;
|
|
@@ -2375,55 +2503,6 @@ var init_git_checkpoint = __esm({
|
|
|
2375
2503
|
}
|
|
2376
2504
|
});
|
|
2377
2505
|
|
|
2378
|
-
// src/store/sqlite-backup.ts
|
|
2379
|
-
import { existsSync as existsSync5, mkdirSync as mkdirSync2, readdirSync, statSync, unlinkSync } from "node:fs";
|
|
2380
|
-
import { join as join6 } from "node:path";
|
|
2381
|
-
function formatTimestamp(d) {
|
|
2382
|
-
const pad = (n, len = 2) => String(n).padStart(len, "0");
|
|
2383
|
-
return `${d.getFullYear()}${pad(d.getMonth() + 1)}${pad(d.getDate())}-${pad(d.getHours())}${pad(d.getMinutes())}${pad(d.getSeconds())}`;
|
|
2384
|
-
}
|
|
2385
|
-
function rotateSnapshots(backupDir) {
|
|
2386
|
-
try {
|
|
2387
|
-
const files = readdirSync(backupDir).filter((f) => f.match(/^tasks-\d{8}-\d{6}\.db$/)).map((f) => ({ name: f, path: join6(backupDir, f), mtimeMs: statSync(join6(backupDir, f)).mtimeMs })).sort((a, b) => a.mtimeMs - b.mtimeMs);
|
|
2388
|
-
while (files.length >= MAX_SNAPSHOTS) {
|
|
2389
|
-
const oldest = files.shift();
|
|
2390
|
-
unlinkSync(oldest.path);
|
|
2391
|
-
}
|
|
2392
|
-
} catch {
|
|
2393
|
-
}
|
|
2394
|
-
}
|
|
2395
|
-
async function vacuumIntoBackup(opts = {}) {
|
|
2396
|
-
const now = Date.now();
|
|
2397
|
-
if (!opts.force && now - _lastBackupEpoch < DEBOUNCE_MS) {
|
|
2398
|
-
return;
|
|
2399
|
-
}
|
|
2400
|
-
try {
|
|
2401
|
-
const cleoDir = getCleoDir(opts.cwd);
|
|
2402
|
-
const backupDir = join6(cleoDir, "backups", "sqlite");
|
|
2403
|
-
mkdirSync2(backupDir, { recursive: true });
|
|
2404
|
-
const db = getNativeDb();
|
|
2405
|
-
if (!db) return;
|
|
2406
|
-
const dest = join6(backupDir, `tasks-${formatTimestamp(/* @__PURE__ */ new Date())}.db`);
|
|
2407
|
-
db.exec("PRAGMA wal_checkpoint(TRUNCATE)");
|
|
2408
|
-
rotateSnapshots(backupDir);
|
|
2409
|
-
const safeDest = dest.replace(/'/g, "''");
|
|
2410
|
-
db.exec(`VACUUM INTO '${safeDest}'`);
|
|
2411
|
-
_lastBackupEpoch = Date.now();
|
|
2412
|
-
} catch {
|
|
2413
|
-
}
|
|
2414
|
-
}
|
|
2415
|
-
var MAX_SNAPSHOTS, DEBOUNCE_MS, _lastBackupEpoch;
|
|
2416
|
-
var init_sqlite_backup = __esm({
|
|
2417
|
-
"src/store/sqlite-backup.ts"() {
|
|
2418
|
-
"use strict";
|
|
2419
|
-
init_paths();
|
|
2420
|
-
init_sqlite();
|
|
2421
|
-
MAX_SNAPSHOTS = 10;
|
|
2422
|
-
DEBOUNCE_MS = 3e4;
|
|
2423
|
-
_lastBackupEpoch = 0;
|
|
2424
|
-
}
|
|
2425
|
-
});
|
|
2426
|
-
|
|
2427
2506
|
// src/core/sequence/index.ts
|
|
2428
2507
|
var sequence_exports = {};
|
|
2429
2508
|
__export(sequence_exports, {
|
|
@@ -2432,7 +2511,7 @@ __export(sequence_exports, {
|
|
|
2432
2511
|
repairSequence: () => repairSequence,
|
|
2433
2512
|
showSequence: () => showSequence
|
|
2434
2513
|
});
|
|
2435
|
-
import { existsSync as existsSync6, readFileSync as readFileSync3, renameSync as
|
|
2514
|
+
import { existsSync as existsSync6, readFileSync as readFileSync3, renameSync as renameSync3 } from "node:fs";
|
|
2436
2515
|
import { join as join7 } from "node:path";
|
|
2437
2516
|
import { eq as eq4 } from "drizzle-orm";
|
|
2438
2517
|
function getLegacySequenceJsonPath(cwd) {
|
|
@@ -2463,10 +2542,10 @@ function renameLegacyFile(path) {
|
|
|
2463
2542
|
const migratedPath = `${path}.migrated`;
|
|
2464
2543
|
try {
|
|
2465
2544
|
if (!existsSync6(migratedPath)) {
|
|
2466
|
-
|
|
2545
|
+
renameSync3(path, migratedPath);
|
|
2467
2546
|
return;
|
|
2468
2547
|
}
|
|
2469
|
-
|
|
2548
|
+
renameSync3(path, `${migratedPath}.${Date.now()}`);
|
|
2470
2549
|
} catch {
|
|
2471
2550
|
}
|
|
2472
2551
|
}
|
|
@@ -3028,7 +3107,7 @@ var init_data_accessor = __esm({
|
|
|
3028
3107
|
});
|
|
3029
3108
|
|
|
3030
3109
|
// src/store/file-utils.ts
|
|
3031
|
-
import { readFileSync as readFileSync4, writeFileSync, renameSync as
|
|
3110
|
+
import { readFileSync as readFileSync4, writeFileSync, renameSync as renameSync4, existsSync as existsSync8, unlinkSync as unlinkSync3, mkdirSync as mkdirSync3, readdirSync as readdirSync2 } from "node:fs";
|
|
3032
3111
|
import { join as join8, dirname as dirname5, basename as basename2 } from "node:path";
|
|
3033
3112
|
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
3034
3113
|
import * as lockfile2 from "proper-lockfile";
|
|
@@ -3043,13 +3122,13 @@ function rotateBackup(filePath) {
|
|
|
3043
3122
|
const current = join8(backupDir, `${name}.${i}`);
|
|
3044
3123
|
if (i === MAX_BACKUPS) {
|
|
3045
3124
|
try {
|
|
3046
|
-
|
|
3125
|
+
unlinkSync3(current);
|
|
3047
3126
|
} catch {
|
|
3048
3127
|
}
|
|
3049
3128
|
} else {
|
|
3050
3129
|
const next = join8(backupDir, `${name}.${i + 1}`);
|
|
3051
3130
|
try {
|
|
3052
|
-
if (existsSync8(current))
|
|
3131
|
+
if (existsSync8(current)) renameSync4(current, next);
|
|
3053
3132
|
} catch {
|
|
3054
3133
|
}
|
|
3055
3134
|
}
|
|
@@ -3069,10 +3148,10 @@ function writeJsonFileAtomic(filePath, data, indent = 2) {
|
|
|
3069
3148
|
if (existsSync8(filePath)) {
|
|
3070
3149
|
rotateBackup(filePath);
|
|
3071
3150
|
}
|
|
3072
|
-
|
|
3151
|
+
renameSync4(tempPath, filePath);
|
|
3073
3152
|
} catch (error) {
|
|
3074
3153
|
try {
|
|
3075
|
-
|
|
3154
|
+
unlinkSync3(tempPath);
|
|
3076
3155
|
} catch {
|
|
3077
3156
|
}
|
|
3078
3157
|
throw error;
|
|
@@ -12761,7 +12840,7 @@ import {
|
|
|
12761
12840
|
writeFileSync as writeFileSync2,
|
|
12762
12841
|
mkdirSync as mkdirSync6,
|
|
12763
12842
|
readdirSync as readdirSync3,
|
|
12764
|
-
copyFileSync,
|
|
12843
|
+
copyFileSync as copyFileSync2,
|
|
12765
12844
|
statSync as statSync3,
|
|
12766
12845
|
rmSync
|
|
12767
12846
|
} from "node:fs";
|
|
@@ -12842,7 +12921,7 @@ function copyDirContents(srcDir, dstDir, manifestLines, copiedFiles) {
|
|
|
12842
12921
|
for (const sf of readdirSync3(srcPath)) {
|
|
12843
12922
|
if (!copiedFiles.has(sf)) {
|
|
12844
12923
|
try {
|
|
12845
|
-
|
|
12924
|
+
copyFileSync2(join13(srcPath, sf), join13(dstPath, sf));
|
|
12846
12925
|
copiedFiles.add(sf);
|
|
12847
12926
|
count2++;
|
|
12848
12927
|
} catch {
|
|
@@ -12850,7 +12929,7 @@ function copyDirContents(srcDir, dstDir, manifestLines, copiedFiles) {
|
|
|
12850
12929
|
}
|
|
12851
12930
|
}
|
|
12852
12931
|
} else if (!copiedFiles.has(entry)) {
|
|
12853
|
-
|
|
12932
|
+
copyFileSync2(srcPath, dstPath);
|
|
12854
12933
|
copiedFiles.add(entry);
|
|
12855
12934
|
count2++;
|
|
12856
12935
|
}
|
|
@@ -13159,7 +13238,7 @@ function getMigrationStatus(projectRoot, opts) {
|
|
|
13159
13238
|
// src/core/system/cleanup.ts
|
|
13160
13239
|
init_errors();
|
|
13161
13240
|
init_exit_codes();
|
|
13162
|
-
import { readFileSync as readFileSync11, writeFileSync as writeFileSync4, existsSync as existsSync17, readdirSync as readdirSync4, unlinkSync as
|
|
13241
|
+
import { readFileSync as readFileSync11, writeFileSync as writeFileSync4, existsSync as existsSync17, readdirSync as readdirSync4, unlinkSync as unlinkSync4 } from "node:fs";
|
|
13163
13242
|
import { join as join17 } from "node:path";
|
|
13164
13243
|
function cleanupSystem(projectRoot, params) {
|
|
13165
13244
|
if (!params.target) {
|
|
@@ -13210,11 +13289,11 @@ function cleanupSystem(projectRoot, params) {
|
|
|
13210
13289
|
if (params.olderThan && meta.timestamp < params.olderThan) {
|
|
13211
13290
|
items.push(file.replace(".meta.json", ""));
|
|
13212
13291
|
if (!dryRun) {
|
|
13213
|
-
|
|
13292
|
+
unlinkSync4(metaFilePath);
|
|
13214
13293
|
for (const bf of readdirSync4(fullDir)) {
|
|
13215
13294
|
if (bf.includes(meta.backupId)) {
|
|
13216
13295
|
try {
|
|
13217
|
-
|
|
13296
|
+
unlinkSync4(join17(fullDir, bf));
|
|
13218
13297
|
} catch {
|
|
13219
13298
|
}
|
|
13220
13299
|
}
|
|
@@ -13239,7 +13318,7 @@ function cleanupSystem(projectRoot, params) {
|
|
|
13239
13318
|
items.push(file);
|
|
13240
13319
|
if (!dryRun) {
|
|
13241
13320
|
try {
|
|
13242
|
-
|
|
13321
|
+
unlinkSync4(join17(cleoDir, file));
|
|
13243
13322
|
} catch {
|
|
13244
13323
|
}
|
|
13245
13324
|
}
|