@cleocode/cleo 2026.4.63 → 2026.4.64
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 +1337 -2666
- package/dist/cli/index.js.map +4 -4
- package/package.json +8 -8
package/dist/cli/index.js
CHANGED
|
@@ -12745,10 +12745,10 @@ function getNexusDbPath() {
|
|
|
12745
12745
|
return nexusPath;
|
|
12746
12746
|
}
|
|
12747
12747
|
function resolveNexusMigrationsFolder() {
|
|
12748
|
-
const
|
|
12749
|
-
const
|
|
12750
|
-
const isBundled =
|
|
12751
|
-
const pkgRoot = isBundled ? join7(
|
|
12748
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
12749
|
+
const __dirname = dirname4(__filename);
|
|
12750
|
+
const isBundled = __dirname.endsWith("/dist") || __dirname.endsWith("\\dist");
|
|
12751
|
+
const pkgRoot = isBundled ? join7(__dirname, "..") : join7(__dirname, "..", "..");
|
|
12752
12752
|
return join7(pkgRoot, "migrations", "drizzle-nexus");
|
|
12753
12753
|
}
|
|
12754
12754
|
function tableExists2(nativeDb, tableName) {
|
|
@@ -14478,10 +14478,10 @@ async function getDb(cwd) {
|
|
|
14478
14478
|
}
|
|
14479
14479
|
}
|
|
14480
14480
|
function resolveMigrationsFolder() {
|
|
14481
|
-
const
|
|
14482
|
-
const
|
|
14483
|
-
const isBundled =
|
|
14484
|
-
const pkgRoot = isBundled ? join10(
|
|
14481
|
+
const __filename = fileURLToPath2(import.meta.url);
|
|
14482
|
+
const __dirname = dirname5(__filename);
|
|
14483
|
+
const isBundled = __dirname.endsWith("/dist") || __dirname.endsWith("\\dist");
|
|
14484
|
+
const pkgRoot = isBundled ? join10(__dirname, "..") : join10(__dirname, "..", "..");
|
|
14485
14485
|
return join10(pkgRoot, "migrations", "drizzle-tasks");
|
|
14486
14486
|
}
|
|
14487
14487
|
function runMigrations(nativeDb, db) {
|
|
@@ -15341,10 +15341,10 @@ function getBrainDbPath(cwd) {
|
|
|
15341
15341
|
return join12(getCleoDirAbsolute(cwd), DB_FILENAME3);
|
|
15342
15342
|
}
|
|
15343
15343
|
function resolveBrainMigrationsFolder() {
|
|
15344
|
-
const
|
|
15345
|
-
const
|
|
15346
|
-
const isBundled =
|
|
15347
|
-
const pkgRoot = isBundled ? join12(
|
|
15344
|
+
const __filename = fileURLToPath3(import.meta.url);
|
|
15345
|
+
const __dirname = dirname8(__filename);
|
|
15346
|
+
const isBundled = __dirname.endsWith("/dist") || __dirname.endsWith("\\dist");
|
|
15347
|
+
const pkgRoot = isBundled ? join12(__dirname, "..") : join12(__dirname, "..", "..");
|
|
15348
15348
|
return join12(pkgRoot, "migrations", "drizzle-brain");
|
|
15349
15349
|
}
|
|
15350
15350
|
function runBrainMigrations(nativeDb, db) {
|
|
@@ -48115,6 +48115,9 @@ async function callLlm(systemPrompt, userContent) {
|
|
|
48115
48115
|
try {
|
|
48116
48116
|
const response = await fetch("https://api.anthropic.com/v1/messages", {
|
|
48117
48117
|
method: "POST",
|
|
48118
|
+
// 30-second hard deadline prevents open network handles from blocking
|
|
48119
|
+
// test process teardown (T753: vitest hang root cause).
|
|
48120
|
+
signal: AbortSignal.timeout(3e4),
|
|
48118
48121
|
headers: {
|
|
48119
48122
|
"Content-Type": "application/json",
|
|
48120
48123
|
"x-api-key": apiKey,
|
|
@@ -84366,8 +84369,8 @@ function getTelemetryDbPath() {
|
|
|
84366
84369
|
return join101(getCleoHome(), DB_FILENAME4);
|
|
84367
84370
|
}
|
|
84368
84371
|
function resolveTelemetryMigrationsFolder() {
|
|
84369
|
-
const
|
|
84370
|
-
const __dir = dirname20(
|
|
84372
|
+
const __filename = fileURLToPath6(import.meta.url);
|
|
84373
|
+
const __dir = dirname20(__filename);
|
|
84371
84374
|
const isBundled = __dir.endsWith("/dist") || __dir.endsWith("\\dist");
|
|
84372
84375
|
const pkgRoot = isBundled ? join101(__dir, "..") : join101(__dir, "..", "..");
|
|
84373
84376
|
return join101(pkgRoot, "migrations", "drizzle-telemetry");
|
|
@@ -90228,6 +90231,7 @@ var init_cleo = __esm({
|
|
|
90228
90231
|
// packages/core/src/index.ts
|
|
90229
90232
|
var init_src3 = __esm({
|
|
90230
90233
|
"packages/core/src/index.ts"() {
|
|
90234
|
+
"use strict";
|
|
90231
90235
|
init_src();
|
|
90232
90236
|
init_adapters();
|
|
90233
90237
|
init_admin();
|
|
@@ -134282,1350 +134286,6 @@ var init_cli = __esm({
|
|
|
134282
134286
|
}
|
|
134283
134287
|
});
|
|
134284
134288
|
|
|
134285
|
-
// node_modules/.pnpm/node-cron@4.2.1/node_modules/node-cron/dist/esm/create-id.js
|
|
134286
|
-
var require_create_id = __commonJS({
|
|
134287
|
-
"node_modules/.pnpm/node-cron@4.2.1/node_modules/node-cron/dist/esm/create-id.js"(exports) {
|
|
134288
|
-
"use strict";
|
|
134289
|
-
var __importDefault = exports && exports.__importDefault || function(mod) {
|
|
134290
|
-
return mod && mod.__esModule ? mod : { "default": mod };
|
|
134291
|
-
};
|
|
134292
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
134293
|
-
exports.createID = createID;
|
|
134294
|
-
var node_crypto_1 = __importDefault(__require("node:crypto"));
|
|
134295
|
-
function createID(prefix = "", length = 16) {
|
|
134296
|
-
const charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
|
134297
|
-
const values = node_crypto_1.default.randomBytes(length);
|
|
134298
|
-
const id = Array.from(values, (v2) => charset[v2 % charset.length]).join("");
|
|
134299
|
-
return prefix ? `${prefix}-${id}` : id;
|
|
134300
|
-
}
|
|
134301
|
-
}
|
|
134302
|
-
});
|
|
134303
|
-
|
|
134304
|
-
// node_modules/.pnpm/node-cron@4.2.1/node_modules/node-cron/dist/esm/logger.js
|
|
134305
|
-
var require_logger = __commonJS({
|
|
134306
|
-
"node_modules/.pnpm/node-cron@4.2.1/node_modules/node-cron/dist/esm/logger.js"(exports) {
|
|
134307
|
-
"use strict";
|
|
134308
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
134309
|
-
var levelColors = {
|
|
134310
|
-
INFO: "\x1B[36m",
|
|
134311
|
-
WARN: "\x1B[33m",
|
|
134312
|
-
ERROR: "\x1B[31m",
|
|
134313
|
-
DEBUG: "\x1B[35m"
|
|
134314
|
-
};
|
|
134315
|
-
var GREEN2 = "\x1B[32m";
|
|
134316
|
-
var RESET = "\x1B[0m";
|
|
134317
|
-
function log13(level, message, extra) {
|
|
134318
|
-
const timestamp2 = (/* @__PURE__ */ new Date()).toISOString();
|
|
134319
|
-
const color = levelColors[level] ?? "";
|
|
134320
|
-
const prefix = `[${timestamp2}] [PID: ${process.pid}] ${GREEN2}[NODE-CRON]${GREEN2} ${color}[${level}]${RESET}`;
|
|
134321
|
-
const output = `${prefix} ${message}`;
|
|
134322
|
-
switch (level) {
|
|
134323
|
-
case "ERROR":
|
|
134324
|
-
console.error(output, extra ?? "");
|
|
134325
|
-
break;
|
|
134326
|
-
case "DEBUG":
|
|
134327
|
-
console.debug(output, extra ?? "");
|
|
134328
|
-
break;
|
|
134329
|
-
case "WARN":
|
|
134330
|
-
console.warn(output);
|
|
134331
|
-
break;
|
|
134332
|
-
case "INFO":
|
|
134333
|
-
default:
|
|
134334
|
-
console.info(output);
|
|
134335
|
-
break;
|
|
134336
|
-
}
|
|
134337
|
-
}
|
|
134338
|
-
var logger = {
|
|
134339
|
-
info(message) {
|
|
134340
|
-
log13("INFO", message);
|
|
134341
|
-
},
|
|
134342
|
-
warn(message) {
|
|
134343
|
-
log13("WARN", message);
|
|
134344
|
-
},
|
|
134345
|
-
error(message, err) {
|
|
134346
|
-
if (message instanceof Error) {
|
|
134347
|
-
log13("ERROR", message.message, message);
|
|
134348
|
-
} else {
|
|
134349
|
-
log13("ERROR", message, err);
|
|
134350
|
-
}
|
|
134351
|
-
},
|
|
134352
|
-
debug(message, err) {
|
|
134353
|
-
if (message instanceof Error) {
|
|
134354
|
-
log13("DEBUG", message.message, message);
|
|
134355
|
-
} else {
|
|
134356
|
-
log13("DEBUG", message, err);
|
|
134357
|
-
}
|
|
134358
|
-
}
|
|
134359
|
-
};
|
|
134360
|
-
exports.default = logger;
|
|
134361
|
-
}
|
|
134362
|
-
});
|
|
134363
|
-
|
|
134364
|
-
// node_modules/.pnpm/node-cron@4.2.1/node_modules/node-cron/dist/esm/promise/tracked-promise.js
|
|
134365
|
-
var require_tracked_promise = __commonJS({
|
|
134366
|
-
"node_modules/.pnpm/node-cron@4.2.1/node_modules/node-cron/dist/esm/promise/tracked-promise.js"(exports) {
|
|
134367
|
-
"use strict";
|
|
134368
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
134369
|
-
exports.TrackedPromise = void 0;
|
|
134370
|
-
var TrackedPromise = class {
|
|
134371
|
-
promise;
|
|
134372
|
-
error;
|
|
134373
|
-
state;
|
|
134374
|
-
value;
|
|
134375
|
-
constructor(executor) {
|
|
134376
|
-
this.state = "pending";
|
|
134377
|
-
this.promise = new Promise((resolve17, reject) => {
|
|
134378
|
-
executor((value) => {
|
|
134379
|
-
this.state = "fulfilled";
|
|
134380
|
-
this.value = value;
|
|
134381
|
-
resolve17(value);
|
|
134382
|
-
}, (error48) => {
|
|
134383
|
-
this.state = "rejected";
|
|
134384
|
-
this.error = error48;
|
|
134385
|
-
reject(error48);
|
|
134386
|
-
});
|
|
134387
|
-
});
|
|
134388
|
-
}
|
|
134389
|
-
getPromise() {
|
|
134390
|
-
return this.promise;
|
|
134391
|
-
}
|
|
134392
|
-
getState() {
|
|
134393
|
-
return this.state;
|
|
134394
|
-
}
|
|
134395
|
-
isPending() {
|
|
134396
|
-
return this.state === "pending";
|
|
134397
|
-
}
|
|
134398
|
-
isFulfilled() {
|
|
134399
|
-
return this.state === "fulfilled";
|
|
134400
|
-
}
|
|
134401
|
-
isRejected() {
|
|
134402
|
-
return this.state === "rejected";
|
|
134403
|
-
}
|
|
134404
|
-
getValue() {
|
|
134405
|
-
return this.value;
|
|
134406
|
-
}
|
|
134407
|
-
getError() {
|
|
134408
|
-
return this.error;
|
|
134409
|
-
}
|
|
134410
|
-
then(onfulfilled, onrejected) {
|
|
134411
|
-
return this.promise.then(onfulfilled, onrejected);
|
|
134412
|
-
}
|
|
134413
|
-
catch(onrejected) {
|
|
134414
|
-
return this.promise.catch(onrejected);
|
|
134415
|
-
}
|
|
134416
|
-
finally(onfinally) {
|
|
134417
|
-
return this.promise.finally(onfinally);
|
|
134418
|
-
}
|
|
134419
|
-
};
|
|
134420
|
-
exports.TrackedPromise = TrackedPromise;
|
|
134421
|
-
}
|
|
134422
|
-
});
|
|
134423
|
-
|
|
134424
|
-
// node_modules/.pnpm/node-cron@4.2.1/node_modules/node-cron/dist/esm/scheduler/runner.js
|
|
134425
|
-
var require_runner = __commonJS({
|
|
134426
|
-
"node_modules/.pnpm/node-cron@4.2.1/node_modules/node-cron/dist/esm/scheduler/runner.js"(exports) {
|
|
134427
|
-
"use strict";
|
|
134428
|
-
var __importDefault = exports && exports.__importDefault || function(mod) {
|
|
134429
|
-
return mod && mod.__esModule ? mod : { "default": mod };
|
|
134430
|
-
};
|
|
134431
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
134432
|
-
exports.Runner = void 0;
|
|
134433
|
-
var create_id_1 = require_create_id();
|
|
134434
|
-
var logger_1 = __importDefault(require_logger());
|
|
134435
|
-
var tracked_promise_1 = require_tracked_promise();
|
|
134436
|
-
function emptyOnFn() {
|
|
134437
|
-
}
|
|
134438
|
-
function emptyHookFn() {
|
|
134439
|
-
return true;
|
|
134440
|
-
}
|
|
134441
|
-
function defaultOnError(date6, error48) {
|
|
134442
|
-
logger_1.default.error("Task failed with error!", error48);
|
|
134443
|
-
}
|
|
134444
|
-
var Runner = class {
|
|
134445
|
-
timeMatcher;
|
|
134446
|
-
onMatch;
|
|
134447
|
-
noOverlap;
|
|
134448
|
-
maxExecutions;
|
|
134449
|
-
maxRandomDelay;
|
|
134450
|
-
runCount;
|
|
134451
|
-
running;
|
|
134452
|
-
heartBeatTimeout;
|
|
134453
|
-
onMissedExecution;
|
|
134454
|
-
onOverlap;
|
|
134455
|
-
onError;
|
|
134456
|
-
beforeRun;
|
|
134457
|
-
onFinished;
|
|
134458
|
-
onMaxExecutions;
|
|
134459
|
-
constructor(timeMatcher, onMatch, options) {
|
|
134460
|
-
this.timeMatcher = timeMatcher;
|
|
134461
|
-
this.onMatch = onMatch;
|
|
134462
|
-
this.noOverlap = options == void 0 || options.noOverlap === void 0 ? false : options.noOverlap;
|
|
134463
|
-
this.maxExecutions = options?.maxExecutions;
|
|
134464
|
-
this.maxRandomDelay = options?.maxRandomDelay || 0;
|
|
134465
|
-
this.onMissedExecution = options?.onMissedExecution || emptyOnFn;
|
|
134466
|
-
this.onOverlap = options?.onOverlap || emptyOnFn;
|
|
134467
|
-
this.onError = options?.onError || defaultOnError;
|
|
134468
|
-
this.onFinished = options?.onFinished || emptyHookFn;
|
|
134469
|
-
this.beforeRun = options?.beforeRun || emptyHookFn;
|
|
134470
|
-
this.onMaxExecutions = options?.onMaxExecutions || emptyOnFn;
|
|
134471
|
-
this.runCount = 0;
|
|
134472
|
-
this.running = false;
|
|
134473
|
-
}
|
|
134474
|
-
start() {
|
|
134475
|
-
this.running = true;
|
|
134476
|
-
let lastExecution;
|
|
134477
|
-
let expectedNextExecution;
|
|
134478
|
-
const scheduleNextHeartBeat = (currentDate) => {
|
|
134479
|
-
if (this.running) {
|
|
134480
|
-
clearTimeout(this.heartBeatTimeout);
|
|
134481
|
-
this.heartBeatTimeout = setTimeout(heartBeat, getDelay(this.timeMatcher, currentDate));
|
|
134482
|
-
}
|
|
134483
|
-
};
|
|
134484
|
-
const runTask = (date6) => {
|
|
134485
|
-
return new Promise(async (resolve17) => {
|
|
134486
|
-
const execution = {
|
|
134487
|
-
id: (0, create_id_1.createID)("exec"),
|
|
134488
|
-
reason: "scheduled"
|
|
134489
|
-
};
|
|
134490
|
-
const shouldExecute = await this.beforeRun(date6, execution);
|
|
134491
|
-
const randomDelay = Math.floor(Math.random() * this.maxRandomDelay);
|
|
134492
|
-
if (shouldExecute) {
|
|
134493
|
-
setTimeout(async () => {
|
|
134494
|
-
try {
|
|
134495
|
-
this.runCount++;
|
|
134496
|
-
execution.startedAt = /* @__PURE__ */ new Date();
|
|
134497
|
-
const result = await this.onMatch(date6, execution);
|
|
134498
|
-
execution.finishedAt = /* @__PURE__ */ new Date();
|
|
134499
|
-
execution.result = result;
|
|
134500
|
-
this.onFinished(date6, execution);
|
|
134501
|
-
if (this.maxExecutions && this.runCount >= this.maxExecutions) {
|
|
134502
|
-
this.onMaxExecutions(date6);
|
|
134503
|
-
this.stop();
|
|
134504
|
-
}
|
|
134505
|
-
} catch (error48) {
|
|
134506
|
-
execution.finishedAt = /* @__PURE__ */ new Date();
|
|
134507
|
-
execution.error = error48;
|
|
134508
|
-
this.onError(date6, error48, execution);
|
|
134509
|
-
}
|
|
134510
|
-
resolve17(true);
|
|
134511
|
-
}, randomDelay);
|
|
134512
|
-
}
|
|
134513
|
-
});
|
|
134514
|
-
};
|
|
134515
|
-
const checkAndRun = (date6) => {
|
|
134516
|
-
return new tracked_promise_1.TrackedPromise(async (resolve17, reject) => {
|
|
134517
|
-
try {
|
|
134518
|
-
if (this.timeMatcher.match(date6)) {
|
|
134519
|
-
await runTask(date6);
|
|
134520
|
-
}
|
|
134521
|
-
resolve17(true);
|
|
134522
|
-
} catch (err) {
|
|
134523
|
-
reject(err);
|
|
134524
|
-
}
|
|
134525
|
-
});
|
|
134526
|
-
};
|
|
134527
|
-
const heartBeat = async () => {
|
|
134528
|
-
const currentDate = nowWithoutMs();
|
|
134529
|
-
if (expectedNextExecution && expectedNextExecution.getTime() < currentDate.getTime()) {
|
|
134530
|
-
while (expectedNextExecution.getTime() < currentDate.getTime()) {
|
|
134531
|
-
logger_1.default.warn(`missed execution at ${expectedNextExecution}! Possible blocking IO or high CPU user at the same process used by node-cron.`);
|
|
134532
|
-
expectedNextExecution = this.timeMatcher.getNextMatch(expectedNextExecution);
|
|
134533
|
-
runAsync(this.onMissedExecution, expectedNextExecution, defaultOnError);
|
|
134534
|
-
}
|
|
134535
|
-
}
|
|
134536
|
-
if (lastExecution && lastExecution.getState() === "pending") {
|
|
134537
|
-
runAsync(this.onOverlap, currentDate, defaultOnError);
|
|
134538
|
-
if (this.noOverlap) {
|
|
134539
|
-
logger_1.default.warn("task still running, new execution blocked by overlap prevention!");
|
|
134540
|
-
expectedNextExecution = this.timeMatcher.getNextMatch(currentDate);
|
|
134541
|
-
scheduleNextHeartBeat(currentDate);
|
|
134542
|
-
return;
|
|
134543
|
-
}
|
|
134544
|
-
}
|
|
134545
|
-
lastExecution = checkAndRun(currentDate);
|
|
134546
|
-
expectedNextExecution = this.timeMatcher.getNextMatch(currentDate);
|
|
134547
|
-
scheduleNextHeartBeat(currentDate);
|
|
134548
|
-
};
|
|
134549
|
-
this.heartBeatTimeout = setTimeout(() => {
|
|
134550
|
-
heartBeat();
|
|
134551
|
-
}, getDelay(this.timeMatcher, nowWithoutMs()));
|
|
134552
|
-
}
|
|
134553
|
-
nextRun() {
|
|
134554
|
-
return this.timeMatcher.getNextMatch(/* @__PURE__ */ new Date());
|
|
134555
|
-
}
|
|
134556
|
-
stop() {
|
|
134557
|
-
this.running = false;
|
|
134558
|
-
if (this.heartBeatTimeout) {
|
|
134559
|
-
clearTimeout(this.heartBeatTimeout);
|
|
134560
|
-
this.heartBeatTimeout = void 0;
|
|
134561
|
-
}
|
|
134562
|
-
}
|
|
134563
|
-
isStarted() {
|
|
134564
|
-
return !!this.heartBeatTimeout && this.running;
|
|
134565
|
-
}
|
|
134566
|
-
isStopped() {
|
|
134567
|
-
return !this.isStarted();
|
|
134568
|
-
}
|
|
134569
|
-
async execute() {
|
|
134570
|
-
const date6 = /* @__PURE__ */ new Date();
|
|
134571
|
-
const execution = {
|
|
134572
|
-
id: (0, create_id_1.createID)("exec"),
|
|
134573
|
-
reason: "invoked"
|
|
134574
|
-
};
|
|
134575
|
-
try {
|
|
134576
|
-
const shouldExecute = await this.beforeRun(date6, execution);
|
|
134577
|
-
if (shouldExecute) {
|
|
134578
|
-
this.runCount++;
|
|
134579
|
-
execution.startedAt = /* @__PURE__ */ new Date();
|
|
134580
|
-
const result = await this.onMatch(date6, execution);
|
|
134581
|
-
execution.finishedAt = /* @__PURE__ */ new Date();
|
|
134582
|
-
execution.result = result;
|
|
134583
|
-
this.onFinished(date6, execution);
|
|
134584
|
-
}
|
|
134585
|
-
} catch (error48) {
|
|
134586
|
-
execution.finishedAt = /* @__PURE__ */ new Date();
|
|
134587
|
-
execution.error = error48;
|
|
134588
|
-
this.onError(date6, error48, execution);
|
|
134589
|
-
}
|
|
134590
|
-
}
|
|
134591
|
-
};
|
|
134592
|
-
exports.Runner = Runner;
|
|
134593
|
-
async function runAsync(fn2, date6, onError) {
|
|
134594
|
-
try {
|
|
134595
|
-
await fn2(date6);
|
|
134596
|
-
} catch (error48) {
|
|
134597
|
-
onError(date6, error48);
|
|
134598
|
-
}
|
|
134599
|
-
}
|
|
134600
|
-
function getDelay(timeMatcher, currentDate) {
|
|
134601
|
-
const maxDelay = 864e5;
|
|
134602
|
-
const nextRun = timeMatcher.getNextMatch(currentDate);
|
|
134603
|
-
const now2 = /* @__PURE__ */ new Date();
|
|
134604
|
-
const delay = nextRun.getTime() - now2.getTime();
|
|
134605
|
-
if (delay > maxDelay) {
|
|
134606
|
-
return maxDelay;
|
|
134607
|
-
}
|
|
134608
|
-
return Math.max(0, delay);
|
|
134609
|
-
}
|
|
134610
|
-
function nowWithoutMs() {
|
|
134611
|
-
const date6 = /* @__PURE__ */ new Date();
|
|
134612
|
-
date6.setMilliseconds(0);
|
|
134613
|
-
return date6;
|
|
134614
|
-
}
|
|
134615
|
-
}
|
|
134616
|
-
});
|
|
134617
|
-
|
|
134618
|
-
// node_modules/.pnpm/node-cron@4.2.1/node_modules/node-cron/dist/esm/pattern/convertion/month-names-conversion.js
|
|
134619
|
-
var require_month_names_conversion = __commonJS({
|
|
134620
|
-
"node_modules/.pnpm/node-cron@4.2.1/node_modules/node-cron/dist/esm/pattern/convertion/month-names-conversion.js"(exports) {
|
|
134621
|
-
"use strict";
|
|
134622
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
134623
|
-
exports.default = /* @__PURE__ */ (() => {
|
|
134624
|
-
const months = [
|
|
134625
|
-
"january",
|
|
134626
|
-
"february",
|
|
134627
|
-
"march",
|
|
134628
|
-
"april",
|
|
134629
|
-
"may",
|
|
134630
|
-
"june",
|
|
134631
|
-
"july",
|
|
134632
|
-
"august",
|
|
134633
|
-
"september",
|
|
134634
|
-
"october",
|
|
134635
|
-
"november",
|
|
134636
|
-
"december"
|
|
134637
|
-
];
|
|
134638
|
-
const shortMonths = [
|
|
134639
|
-
"jan",
|
|
134640
|
-
"feb",
|
|
134641
|
-
"mar",
|
|
134642
|
-
"apr",
|
|
134643
|
-
"may",
|
|
134644
|
-
"jun",
|
|
134645
|
-
"jul",
|
|
134646
|
-
"aug",
|
|
134647
|
-
"sep",
|
|
134648
|
-
"oct",
|
|
134649
|
-
"nov",
|
|
134650
|
-
"dec"
|
|
134651
|
-
];
|
|
134652
|
-
function convertMonthName(expression, items) {
|
|
134653
|
-
for (let i = 0; i < items.length; i++) {
|
|
134654
|
-
expression = expression.replace(new RegExp(items[i], "gi"), i + 1);
|
|
134655
|
-
}
|
|
134656
|
-
return expression;
|
|
134657
|
-
}
|
|
134658
|
-
function interprete(monthExpression) {
|
|
134659
|
-
monthExpression = convertMonthName(monthExpression, months);
|
|
134660
|
-
monthExpression = convertMonthName(monthExpression, shortMonths);
|
|
134661
|
-
return monthExpression;
|
|
134662
|
-
}
|
|
134663
|
-
return interprete;
|
|
134664
|
-
})();
|
|
134665
|
-
}
|
|
134666
|
-
});
|
|
134667
|
-
|
|
134668
|
-
// node_modules/.pnpm/node-cron@4.2.1/node_modules/node-cron/dist/esm/pattern/convertion/week-day-names-conversion.js
|
|
134669
|
-
var require_week_day_names_conversion = __commonJS({
|
|
134670
|
-
"node_modules/.pnpm/node-cron@4.2.1/node_modules/node-cron/dist/esm/pattern/convertion/week-day-names-conversion.js"(exports) {
|
|
134671
|
-
"use strict";
|
|
134672
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
134673
|
-
exports.default = /* @__PURE__ */ (() => {
|
|
134674
|
-
const weekDays = [
|
|
134675
|
-
"sunday",
|
|
134676
|
-
"monday",
|
|
134677
|
-
"tuesday",
|
|
134678
|
-
"wednesday",
|
|
134679
|
-
"thursday",
|
|
134680
|
-
"friday",
|
|
134681
|
-
"saturday"
|
|
134682
|
-
];
|
|
134683
|
-
const shortWeekDays = ["sun", "mon", "tue", "wed", "thu", "fri", "sat"];
|
|
134684
|
-
function convertWeekDayName(expression, items) {
|
|
134685
|
-
for (let i = 0; i < items.length; i++) {
|
|
134686
|
-
expression = expression.replace(new RegExp(items[i], "gi"), i);
|
|
134687
|
-
}
|
|
134688
|
-
return expression;
|
|
134689
|
-
}
|
|
134690
|
-
function convertWeekDays(expression) {
|
|
134691
|
-
expression = expression.replace("7", "0");
|
|
134692
|
-
expression = convertWeekDayName(expression, weekDays);
|
|
134693
|
-
return convertWeekDayName(expression, shortWeekDays);
|
|
134694
|
-
}
|
|
134695
|
-
return convertWeekDays;
|
|
134696
|
-
})();
|
|
134697
|
-
}
|
|
134698
|
-
});
|
|
134699
|
-
|
|
134700
|
-
// node_modules/.pnpm/node-cron@4.2.1/node_modules/node-cron/dist/esm/pattern/convertion/asterisk-to-range-conversion.js
|
|
134701
|
-
var require_asterisk_to_range_conversion = __commonJS({
|
|
134702
|
-
"node_modules/.pnpm/node-cron@4.2.1/node_modules/node-cron/dist/esm/pattern/convertion/asterisk-to-range-conversion.js"(exports) {
|
|
134703
|
-
"use strict";
|
|
134704
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
134705
|
-
exports.default = /* @__PURE__ */ (() => {
|
|
134706
|
-
function convertAsterisk(expression, replecement) {
|
|
134707
|
-
if (expression.indexOf("*") !== -1) {
|
|
134708
|
-
return expression.replace("*", replecement);
|
|
134709
|
-
}
|
|
134710
|
-
return expression;
|
|
134711
|
-
}
|
|
134712
|
-
function convertAsterisksToRanges(expressions) {
|
|
134713
|
-
expressions[0] = convertAsterisk(expressions[0], "0-59");
|
|
134714
|
-
expressions[1] = convertAsterisk(expressions[1], "0-59");
|
|
134715
|
-
expressions[2] = convertAsterisk(expressions[2], "0-23");
|
|
134716
|
-
expressions[3] = convertAsterisk(expressions[3], "1-31");
|
|
134717
|
-
expressions[4] = convertAsterisk(expressions[4], "1-12");
|
|
134718
|
-
expressions[5] = convertAsterisk(expressions[5], "0-6");
|
|
134719
|
-
return expressions;
|
|
134720
|
-
}
|
|
134721
|
-
return convertAsterisksToRanges;
|
|
134722
|
-
})();
|
|
134723
|
-
}
|
|
134724
|
-
});
|
|
134725
|
-
|
|
134726
|
-
// node_modules/.pnpm/node-cron@4.2.1/node_modules/node-cron/dist/esm/pattern/convertion/range-conversion.js
|
|
134727
|
-
var require_range_conversion = __commonJS({
|
|
134728
|
-
"node_modules/.pnpm/node-cron@4.2.1/node_modules/node-cron/dist/esm/pattern/convertion/range-conversion.js"(exports) {
|
|
134729
|
-
"use strict";
|
|
134730
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
134731
|
-
exports.default = /* @__PURE__ */ (() => {
|
|
134732
|
-
function replaceWithRange(expression, text3, init, end, stepTxt) {
|
|
134733
|
-
const step = parseInt(stepTxt);
|
|
134734
|
-
const numbers = [];
|
|
134735
|
-
let last = parseInt(end);
|
|
134736
|
-
let first = parseInt(init);
|
|
134737
|
-
if (first > last) {
|
|
134738
|
-
last = parseInt(init);
|
|
134739
|
-
first = parseInt(end);
|
|
134740
|
-
}
|
|
134741
|
-
for (let i = first; i <= last; i += step) {
|
|
134742
|
-
numbers.push(i);
|
|
134743
|
-
}
|
|
134744
|
-
return expression.replace(new RegExp(text3, "i"), numbers.join());
|
|
134745
|
-
}
|
|
134746
|
-
function convertRange(expression) {
|
|
134747
|
-
const rangeRegEx = /(\d+)-(\d+)(\/(\d+)|)/;
|
|
134748
|
-
let match = rangeRegEx.exec(expression);
|
|
134749
|
-
while (match !== null && match.length > 0) {
|
|
134750
|
-
expression = replaceWithRange(expression, match[0], match[1], match[2], match[4] || "1");
|
|
134751
|
-
match = rangeRegEx.exec(expression);
|
|
134752
|
-
}
|
|
134753
|
-
return expression;
|
|
134754
|
-
}
|
|
134755
|
-
function convertAllRanges(expressions) {
|
|
134756
|
-
for (let i = 0; i < expressions.length; i++) {
|
|
134757
|
-
expressions[i] = convertRange(expressions[i]);
|
|
134758
|
-
}
|
|
134759
|
-
return expressions;
|
|
134760
|
-
}
|
|
134761
|
-
return convertAllRanges;
|
|
134762
|
-
})();
|
|
134763
|
-
}
|
|
134764
|
-
});
|
|
134765
|
-
|
|
134766
|
-
// node_modules/.pnpm/node-cron@4.2.1/node_modules/node-cron/dist/esm/pattern/convertion/index.js
|
|
134767
|
-
var require_convertion = __commonJS({
|
|
134768
|
-
"node_modules/.pnpm/node-cron@4.2.1/node_modules/node-cron/dist/esm/pattern/convertion/index.js"(exports) {
|
|
134769
|
-
"use strict";
|
|
134770
|
-
var __importDefault = exports && exports.__importDefault || function(mod) {
|
|
134771
|
-
return mod && mod.__esModule ? mod : { "default": mod };
|
|
134772
|
-
};
|
|
134773
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
134774
|
-
var month_names_conversion_1 = __importDefault(require_month_names_conversion());
|
|
134775
|
-
var week_day_names_conversion_1 = __importDefault(require_week_day_names_conversion());
|
|
134776
|
-
var asterisk_to_range_conversion_1 = __importDefault(require_asterisk_to_range_conversion());
|
|
134777
|
-
var range_conversion_1 = __importDefault(require_range_conversion());
|
|
134778
|
-
exports.default = /* @__PURE__ */ (() => {
|
|
134779
|
-
function appendSeccondExpression(expressions) {
|
|
134780
|
-
if (expressions.length === 5) {
|
|
134781
|
-
return ["0"].concat(expressions);
|
|
134782
|
-
}
|
|
134783
|
-
return expressions;
|
|
134784
|
-
}
|
|
134785
|
-
function removeSpaces(str) {
|
|
134786
|
-
return str.replace(/\s{2,}/g, " ").trim();
|
|
134787
|
-
}
|
|
134788
|
-
function normalizeIntegers(expressions) {
|
|
134789
|
-
for (let i = 0; i < expressions.length; i++) {
|
|
134790
|
-
const numbers = expressions[i].split(",");
|
|
134791
|
-
for (let j2 = 0; j2 < numbers.length; j2++) {
|
|
134792
|
-
numbers[j2] = parseInt(numbers[j2]);
|
|
134793
|
-
}
|
|
134794
|
-
expressions[i] = numbers;
|
|
134795
|
-
}
|
|
134796
|
-
return expressions;
|
|
134797
|
-
}
|
|
134798
|
-
function interprete(expression) {
|
|
134799
|
-
let expressions = removeSpaces(`${expression}`).split(" ");
|
|
134800
|
-
expressions = appendSeccondExpression(expressions);
|
|
134801
|
-
expressions[4] = (0, month_names_conversion_1.default)(expressions[4]);
|
|
134802
|
-
expressions[5] = (0, week_day_names_conversion_1.default)(expressions[5]);
|
|
134803
|
-
expressions = (0, asterisk_to_range_conversion_1.default)(expressions);
|
|
134804
|
-
expressions = (0, range_conversion_1.default)(expressions);
|
|
134805
|
-
expressions = normalizeIntegers(expressions);
|
|
134806
|
-
return expressions;
|
|
134807
|
-
}
|
|
134808
|
-
return interprete;
|
|
134809
|
-
})();
|
|
134810
|
-
}
|
|
134811
|
-
});
|
|
134812
|
-
|
|
134813
|
-
// node_modules/.pnpm/node-cron@4.2.1/node_modules/node-cron/dist/esm/time/localized-time.js
|
|
134814
|
-
var require_localized_time = __commonJS({
|
|
134815
|
-
"node_modules/.pnpm/node-cron@4.2.1/node_modules/node-cron/dist/esm/time/localized-time.js"(exports) {
|
|
134816
|
-
"use strict";
|
|
134817
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
134818
|
-
exports.LocalizedTime = void 0;
|
|
134819
|
-
var LocalizedTime = class {
|
|
134820
|
-
timestamp;
|
|
134821
|
-
parts;
|
|
134822
|
-
timezone;
|
|
134823
|
-
constructor(date6, timezone) {
|
|
134824
|
-
this.timestamp = date6.getTime();
|
|
134825
|
-
this.timezone = timezone;
|
|
134826
|
-
this.parts = buildDateParts(date6, timezone);
|
|
134827
|
-
}
|
|
134828
|
-
toDate() {
|
|
134829
|
-
return new Date(this.timestamp);
|
|
134830
|
-
}
|
|
134831
|
-
toISO() {
|
|
134832
|
-
const gmt = this.parts.gmt.replace(/^GMT/, "");
|
|
134833
|
-
const offset = gmt ? gmt : "Z";
|
|
134834
|
-
const pad = (n) => String(n).padStart(2, "0");
|
|
134835
|
-
return `${this.parts.year}-${pad(this.parts.month)}-${pad(this.parts.day)}T${pad(this.parts.hour)}:${pad(this.parts.minute)}:${pad(this.parts.second)}.${String(this.parts.milisecond).padStart(3, "0")}` + offset;
|
|
134836
|
-
}
|
|
134837
|
-
getParts() {
|
|
134838
|
-
return this.parts;
|
|
134839
|
-
}
|
|
134840
|
-
set(field, value) {
|
|
134841
|
-
this.parts[field] = value;
|
|
134842
|
-
const newDate = new Date(this.toISO());
|
|
134843
|
-
this.timestamp = newDate.getTime();
|
|
134844
|
-
this.parts = buildDateParts(newDate, this.timezone);
|
|
134845
|
-
}
|
|
134846
|
-
};
|
|
134847
|
-
exports.LocalizedTime = LocalizedTime;
|
|
134848
|
-
function buildDateParts(date6, timezone) {
|
|
134849
|
-
const dftOptions = {
|
|
134850
|
-
year: "numeric",
|
|
134851
|
-
month: "2-digit",
|
|
134852
|
-
day: "2-digit",
|
|
134853
|
-
hour: "2-digit",
|
|
134854
|
-
minute: "2-digit",
|
|
134855
|
-
second: "2-digit",
|
|
134856
|
-
weekday: "short",
|
|
134857
|
-
hour12: false
|
|
134858
|
-
};
|
|
134859
|
-
if (timezone) {
|
|
134860
|
-
dftOptions.timeZone = timezone;
|
|
134861
|
-
}
|
|
134862
|
-
const dateFormat = new Intl.DateTimeFormat("en-US", dftOptions);
|
|
134863
|
-
const parts = dateFormat.formatToParts(date6).filter((part) => {
|
|
134864
|
-
return part.type !== "literal";
|
|
134865
|
-
}).reduce((acc, part) => {
|
|
134866
|
-
acc[part.type] = part.value;
|
|
134867
|
-
return acc;
|
|
134868
|
-
}, {});
|
|
134869
|
-
return {
|
|
134870
|
-
day: parseInt(parts.day),
|
|
134871
|
-
month: parseInt(parts.month),
|
|
134872
|
-
year: parseInt(parts.year),
|
|
134873
|
-
hour: parts.hour === "24" ? 0 : parseInt(parts.hour),
|
|
134874
|
-
minute: parseInt(parts.minute),
|
|
134875
|
-
second: parseInt(parts.second),
|
|
134876
|
-
milisecond: date6.getMilliseconds(),
|
|
134877
|
-
weekday: parts.weekday,
|
|
134878
|
-
gmt: getTimezoneGMT(date6, timezone)
|
|
134879
|
-
};
|
|
134880
|
-
}
|
|
134881
|
-
function getTimezoneGMT(date6, timezone) {
|
|
134882
|
-
const utcDate = new Date(date6.toLocaleString("en-US", { timeZone: "UTC" }));
|
|
134883
|
-
const tzDate = new Date(date6.toLocaleString("en-US", { timeZone: timezone }));
|
|
134884
|
-
let offsetInMinutes = (utcDate.getTime() - tzDate.getTime()) / 6e4;
|
|
134885
|
-
const sign = offsetInMinutes <= 0 ? "+" : "-";
|
|
134886
|
-
offsetInMinutes = Math.abs(offsetInMinutes);
|
|
134887
|
-
if (offsetInMinutes === 0)
|
|
134888
|
-
return "Z";
|
|
134889
|
-
const hours = Math.floor(offsetInMinutes / 60).toString().padStart(2, "0");
|
|
134890
|
-
const minutes = Math.floor(offsetInMinutes % 60).toString().padStart(2, "0");
|
|
134891
|
-
return `GMT${sign}${hours}:${minutes}`;
|
|
134892
|
-
}
|
|
134893
|
-
}
|
|
134894
|
-
});
|
|
134895
|
-
|
|
134896
|
-
// node_modules/.pnpm/node-cron@4.2.1/node_modules/node-cron/dist/esm/time/matcher-walker.js
|
|
134897
|
-
var require_matcher_walker = __commonJS({
|
|
134898
|
-
"node_modules/.pnpm/node-cron@4.2.1/node_modules/node-cron/dist/esm/time/matcher-walker.js"(exports) {
|
|
134899
|
-
"use strict";
|
|
134900
|
-
var __importDefault = exports && exports.__importDefault || function(mod) {
|
|
134901
|
-
return mod && mod.__esModule ? mod : { "default": mod };
|
|
134902
|
-
};
|
|
134903
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
134904
|
-
exports.MatcherWalker = void 0;
|
|
134905
|
-
var convertion_1 = __importDefault(require_convertion());
|
|
134906
|
-
var localized_time_1 = require_localized_time();
|
|
134907
|
-
var time_matcher_1 = require_time_matcher();
|
|
134908
|
-
var week_day_names_conversion_1 = __importDefault(require_week_day_names_conversion());
|
|
134909
|
-
var MatcherWalker = class {
|
|
134910
|
-
cronExpression;
|
|
134911
|
-
baseDate;
|
|
134912
|
-
pattern;
|
|
134913
|
-
expressions;
|
|
134914
|
-
timeMatcher;
|
|
134915
|
-
timezone;
|
|
134916
|
-
constructor(cronExpression, baseDate, timezone) {
|
|
134917
|
-
this.cronExpression = cronExpression;
|
|
134918
|
-
this.baseDate = baseDate;
|
|
134919
|
-
this.timeMatcher = new time_matcher_1.TimeMatcher(cronExpression, timezone);
|
|
134920
|
-
this.timezone = timezone;
|
|
134921
|
-
this.expressions = (0, convertion_1.default)(cronExpression);
|
|
134922
|
-
}
|
|
134923
|
-
isMatching() {
|
|
134924
|
-
return this.timeMatcher.match(this.baseDate);
|
|
134925
|
-
}
|
|
134926
|
-
matchNext() {
|
|
134927
|
-
const findNextDateIgnoringWeekday = () => {
|
|
134928
|
-
const baseDate = new Date(this.baseDate.getTime());
|
|
134929
|
-
baseDate.setMilliseconds(0);
|
|
134930
|
-
const localTime = new localized_time_1.LocalizedTime(baseDate, this.timezone);
|
|
134931
|
-
const dateParts = localTime.getParts();
|
|
134932
|
-
const date7 = new localized_time_1.LocalizedTime(localTime.toDate(), this.timezone);
|
|
134933
|
-
const seconds = this.expressions[0];
|
|
134934
|
-
const nextSecond = availableValue(seconds, dateParts.second);
|
|
134935
|
-
if (nextSecond) {
|
|
134936
|
-
date7.set("second", nextSecond);
|
|
134937
|
-
if (this.timeMatcher.match(date7.toDate())) {
|
|
134938
|
-
return date7;
|
|
134939
|
-
}
|
|
134940
|
-
}
|
|
134941
|
-
date7.set("second", seconds[0]);
|
|
134942
|
-
const minutes = this.expressions[1];
|
|
134943
|
-
const nextMinute = availableValue(minutes, dateParts.minute);
|
|
134944
|
-
if (nextMinute) {
|
|
134945
|
-
date7.set("minute", nextMinute);
|
|
134946
|
-
if (this.timeMatcher.match(date7.toDate())) {
|
|
134947
|
-
return date7;
|
|
134948
|
-
}
|
|
134949
|
-
}
|
|
134950
|
-
date7.set("minute", minutes[0]);
|
|
134951
|
-
const hours = this.expressions[2];
|
|
134952
|
-
const nextHour = availableValue(hours, dateParts.hour);
|
|
134953
|
-
if (nextHour) {
|
|
134954
|
-
date7.set("hour", nextHour);
|
|
134955
|
-
if (this.timeMatcher.match(date7.toDate())) {
|
|
134956
|
-
return date7;
|
|
134957
|
-
}
|
|
134958
|
-
}
|
|
134959
|
-
date7.set("hour", hours[0]);
|
|
134960
|
-
const days = this.expressions[3];
|
|
134961
|
-
const nextDay = availableValue(days, dateParts.day);
|
|
134962
|
-
if (nextDay) {
|
|
134963
|
-
date7.set("day", nextDay);
|
|
134964
|
-
if (this.timeMatcher.match(date7.toDate())) {
|
|
134965
|
-
return date7;
|
|
134966
|
-
}
|
|
134967
|
-
}
|
|
134968
|
-
date7.set("day", days[0]);
|
|
134969
|
-
const months = this.expressions[4];
|
|
134970
|
-
const nextMonth = availableValue(months, dateParts.month);
|
|
134971
|
-
if (nextMonth) {
|
|
134972
|
-
date7.set("month", nextMonth);
|
|
134973
|
-
if (this.timeMatcher.match(date7.toDate())) {
|
|
134974
|
-
return date7;
|
|
134975
|
-
}
|
|
134976
|
-
}
|
|
134977
|
-
date7.set("year", date7.getParts().year + 1);
|
|
134978
|
-
date7.set("month", months[0]);
|
|
134979
|
-
return date7;
|
|
134980
|
-
};
|
|
134981
|
-
const date6 = findNextDateIgnoringWeekday();
|
|
134982
|
-
const weekdays = this.expressions[5];
|
|
134983
|
-
let currentWeekday = parseInt((0, week_day_names_conversion_1.default)(date6.getParts().weekday));
|
|
134984
|
-
while (!(weekdays.indexOf(currentWeekday) > -1)) {
|
|
134985
|
-
date6.set("year", date6.getParts().year + 1);
|
|
134986
|
-
currentWeekday = parseInt((0, week_day_names_conversion_1.default)(date6.getParts().weekday));
|
|
134987
|
-
}
|
|
134988
|
-
return date6;
|
|
134989
|
-
}
|
|
134990
|
-
};
|
|
134991
|
-
exports.MatcherWalker = MatcherWalker;
|
|
134992
|
-
function availableValue(values, currentValue) {
|
|
134993
|
-
const availableValues = values.sort((a, b2) => a - b2).filter((s3) => s3 > currentValue);
|
|
134994
|
-
if (availableValues.length > 0)
|
|
134995
|
-
return availableValues[0];
|
|
134996
|
-
return false;
|
|
134997
|
-
}
|
|
134998
|
-
}
|
|
134999
|
-
});
|
|
135000
|
-
|
|
135001
|
-
// node_modules/.pnpm/node-cron@4.2.1/node_modules/node-cron/dist/esm/time/time-matcher.js
|
|
135002
|
-
var require_time_matcher = __commonJS({
|
|
135003
|
-
"node_modules/.pnpm/node-cron@4.2.1/node_modules/node-cron/dist/esm/time/time-matcher.js"(exports) {
|
|
135004
|
-
"use strict";
|
|
135005
|
-
var __importDefault = exports && exports.__importDefault || function(mod) {
|
|
135006
|
-
return mod && mod.__esModule ? mod : { "default": mod };
|
|
135007
|
-
};
|
|
135008
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
135009
|
-
exports.TimeMatcher = void 0;
|
|
135010
|
-
var index_1 = __importDefault(require_convertion());
|
|
135011
|
-
var week_day_names_conversion_1 = __importDefault(require_week_day_names_conversion());
|
|
135012
|
-
var localized_time_1 = require_localized_time();
|
|
135013
|
-
var matcher_walker_1 = require_matcher_walker();
|
|
135014
|
-
function matchValue(allowedValues, value) {
|
|
135015
|
-
return allowedValues.indexOf(value) !== -1;
|
|
135016
|
-
}
|
|
135017
|
-
var TimeMatcher = class {
|
|
135018
|
-
timezone;
|
|
135019
|
-
pattern;
|
|
135020
|
-
expressions;
|
|
135021
|
-
constructor(pattern, timezone) {
|
|
135022
|
-
this.timezone = timezone;
|
|
135023
|
-
this.pattern = pattern;
|
|
135024
|
-
this.expressions = (0, index_1.default)(pattern);
|
|
135025
|
-
}
|
|
135026
|
-
match(date6) {
|
|
135027
|
-
const localizedTime = new localized_time_1.LocalizedTime(date6, this.timezone);
|
|
135028
|
-
const parts = localizedTime.getParts();
|
|
135029
|
-
const runOnSecond = matchValue(this.expressions[0], parts.second);
|
|
135030
|
-
const runOnMinute = matchValue(this.expressions[1], parts.minute);
|
|
135031
|
-
const runOnHour = matchValue(this.expressions[2], parts.hour);
|
|
135032
|
-
const runOnDay = matchValue(this.expressions[3], parts.day);
|
|
135033
|
-
const runOnMonth = matchValue(this.expressions[4], parts.month);
|
|
135034
|
-
const runOnWeekDay = matchValue(this.expressions[5], parseInt((0, week_day_names_conversion_1.default)(parts.weekday)));
|
|
135035
|
-
return runOnSecond && runOnMinute && runOnHour && runOnDay && runOnMonth && runOnWeekDay;
|
|
135036
|
-
}
|
|
135037
|
-
getNextMatch(date6) {
|
|
135038
|
-
const walker = new matcher_walker_1.MatcherWalker(this.pattern, date6, this.timezone);
|
|
135039
|
-
const next = walker.matchNext();
|
|
135040
|
-
return next.toDate();
|
|
135041
|
-
}
|
|
135042
|
-
};
|
|
135043
|
-
exports.TimeMatcher = TimeMatcher;
|
|
135044
|
-
}
|
|
135045
|
-
});
|
|
135046
|
-
|
|
135047
|
-
// node_modules/.pnpm/node-cron@4.2.1/node_modules/node-cron/dist/esm/tasks/state-machine.js
|
|
135048
|
-
var require_state_machine = __commonJS({
|
|
135049
|
-
"node_modules/.pnpm/node-cron@4.2.1/node_modules/node-cron/dist/esm/tasks/state-machine.js"(exports) {
|
|
135050
|
-
"use strict";
|
|
135051
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
135052
|
-
exports.StateMachine = void 0;
|
|
135053
|
-
var allowedTransitions = {
|
|
135054
|
-
"stopped": ["stopped", "idle", "destroyed"],
|
|
135055
|
-
"idle": ["idle", "running", "stopped", "destroyed"],
|
|
135056
|
-
"running": ["running", "idle", "stopped", "destroyed"],
|
|
135057
|
-
"destroyed": ["destroyed"]
|
|
135058
|
-
};
|
|
135059
|
-
var StateMachine = class {
|
|
135060
|
-
state;
|
|
135061
|
-
constructor(initial = "stopped") {
|
|
135062
|
-
this.state = initial;
|
|
135063
|
-
}
|
|
135064
|
-
changeState(state) {
|
|
135065
|
-
if (allowedTransitions[this.state].includes(state)) {
|
|
135066
|
-
this.state = state;
|
|
135067
|
-
} else {
|
|
135068
|
-
throw new Error(`invalid transition from ${this.state} to ${state}`);
|
|
135069
|
-
}
|
|
135070
|
-
}
|
|
135071
|
-
};
|
|
135072
|
-
exports.StateMachine = StateMachine;
|
|
135073
|
-
}
|
|
135074
|
-
});
|
|
135075
|
-
|
|
135076
|
-
// node_modules/.pnpm/node-cron@4.2.1/node_modules/node-cron/dist/esm/tasks/inline-scheduled-task.js
|
|
135077
|
-
var require_inline_scheduled_task = __commonJS({
|
|
135078
|
-
"node_modules/.pnpm/node-cron@4.2.1/node_modules/node-cron/dist/esm/tasks/inline-scheduled-task.js"(exports) {
|
|
135079
|
-
"use strict";
|
|
135080
|
-
var __importDefault = exports && exports.__importDefault || function(mod) {
|
|
135081
|
-
return mod && mod.__esModule ? mod : { "default": mod };
|
|
135082
|
-
};
|
|
135083
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
135084
|
-
exports.InlineScheduledTask = void 0;
|
|
135085
|
-
var events_1 = __importDefault(__require("events"));
|
|
135086
|
-
var runner_1 = require_runner();
|
|
135087
|
-
var time_matcher_1 = require_time_matcher();
|
|
135088
|
-
var create_id_1 = require_create_id();
|
|
135089
|
-
var state_machine_1 = require_state_machine();
|
|
135090
|
-
var logger_1 = __importDefault(require_logger());
|
|
135091
|
-
var localized_time_1 = require_localized_time();
|
|
135092
|
-
var TaskEmitter = class extends events_1.default {
|
|
135093
|
-
};
|
|
135094
|
-
var InlineScheduledTask = class {
|
|
135095
|
-
emitter;
|
|
135096
|
-
cronExpression;
|
|
135097
|
-
timeMatcher;
|
|
135098
|
-
runner;
|
|
135099
|
-
id;
|
|
135100
|
-
name;
|
|
135101
|
-
stateMachine;
|
|
135102
|
-
timezone;
|
|
135103
|
-
constructor(cronExpression, taskFn, options) {
|
|
135104
|
-
this.emitter = new TaskEmitter();
|
|
135105
|
-
this.cronExpression = cronExpression;
|
|
135106
|
-
this.id = (0, create_id_1.createID)("task", 12);
|
|
135107
|
-
this.name = options?.name || this.id;
|
|
135108
|
-
this.timezone = options?.timezone;
|
|
135109
|
-
this.timeMatcher = new time_matcher_1.TimeMatcher(cronExpression, options?.timezone);
|
|
135110
|
-
this.stateMachine = new state_machine_1.StateMachine();
|
|
135111
|
-
const runnerOptions = {
|
|
135112
|
-
timezone: options?.timezone,
|
|
135113
|
-
noOverlap: options?.noOverlap,
|
|
135114
|
-
maxExecutions: options?.maxExecutions,
|
|
135115
|
-
maxRandomDelay: options?.maxRandomDelay,
|
|
135116
|
-
beforeRun: (date6, execution) => {
|
|
135117
|
-
if (execution.reason === "scheduled") {
|
|
135118
|
-
this.changeState("running");
|
|
135119
|
-
}
|
|
135120
|
-
this.emitter.emit("execution:started", this.createContext(date6, execution));
|
|
135121
|
-
return true;
|
|
135122
|
-
},
|
|
135123
|
-
onFinished: (date6, execution) => {
|
|
135124
|
-
if (execution.reason === "scheduled") {
|
|
135125
|
-
this.changeState("idle");
|
|
135126
|
-
}
|
|
135127
|
-
this.emitter.emit("execution:finished", this.createContext(date6, execution));
|
|
135128
|
-
return true;
|
|
135129
|
-
},
|
|
135130
|
-
onError: (date6, error48, execution) => {
|
|
135131
|
-
logger_1.default.error(error48);
|
|
135132
|
-
this.emitter.emit("execution:failed", this.createContext(date6, execution));
|
|
135133
|
-
this.changeState("idle");
|
|
135134
|
-
},
|
|
135135
|
-
onOverlap: (date6) => {
|
|
135136
|
-
this.emitter.emit("execution:overlap", this.createContext(date6));
|
|
135137
|
-
},
|
|
135138
|
-
onMissedExecution: (date6) => {
|
|
135139
|
-
this.emitter.emit("execution:missed", this.createContext(date6));
|
|
135140
|
-
},
|
|
135141
|
-
onMaxExecutions: (date6) => {
|
|
135142
|
-
this.emitter.emit("execution:maxReached", this.createContext(date6));
|
|
135143
|
-
this.destroy();
|
|
135144
|
-
}
|
|
135145
|
-
};
|
|
135146
|
-
this.runner = new runner_1.Runner(this.timeMatcher, (date6, execution) => {
|
|
135147
|
-
return taskFn(this.createContext(date6, execution));
|
|
135148
|
-
}, runnerOptions);
|
|
135149
|
-
}
|
|
135150
|
-
getNextRun() {
|
|
135151
|
-
if (this.stateMachine.state !== "stopped") {
|
|
135152
|
-
return this.runner.nextRun();
|
|
135153
|
-
}
|
|
135154
|
-
return null;
|
|
135155
|
-
}
|
|
135156
|
-
changeState(state) {
|
|
135157
|
-
if (this.runner.isStarted()) {
|
|
135158
|
-
this.stateMachine.changeState(state);
|
|
135159
|
-
}
|
|
135160
|
-
}
|
|
135161
|
-
start() {
|
|
135162
|
-
if (this.runner.isStopped()) {
|
|
135163
|
-
this.runner.start();
|
|
135164
|
-
this.stateMachine.changeState("idle");
|
|
135165
|
-
this.emitter.emit("task:started", this.createContext(/* @__PURE__ */ new Date()));
|
|
135166
|
-
}
|
|
135167
|
-
}
|
|
135168
|
-
stop() {
|
|
135169
|
-
if (this.runner.isStarted()) {
|
|
135170
|
-
this.runner.stop();
|
|
135171
|
-
this.stateMachine.changeState("stopped");
|
|
135172
|
-
this.emitter.emit("task:stopped", this.createContext(/* @__PURE__ */ new Date()));
|
|
135173
|
-
}
|
|
135174
|
-
}
|
|
135175
|
-
getStatus() {
|
|
135176
|
-
return this.stateMachine.state;
|
|
135177
|
-
}
|
|
135178
|
-
destroy() {
|
|
135179
|
-
if (this.stateMachine.state === "destroyed")
|
|
135180
|
-
return;
|
|
135181
|
-
this.stop();
|
|
135182
|
-
this.stateMachine.changeState("destroyed");
|
|
135183
|
-
this.emitter.emit("task:destroyed", this.createContext(/* @__PURE__ */ new Date()));
|
|
135184
|
-
}
|
|
135185
|
-
execute() {
|
|
135186
|
-
return new Promise((resolve17, reject) => {
|
|
135187
|
-
const onFail = (context) => {
|
|
135188
|
-
this.off("execution:finished", onFail);
|
|
135189
|
-
reject(context.execution?.error);
|
|
135190
|
-
};
|
|
135191
|
-
const onFinished = (context) => {
|
|
135192
|
-
this.off("execution:failed", onFail);
|
|
135193
|
-
resolve17(context.execution?.result);
|
|
135194
|
-
};
|
|
135195
|
-
this.once("execution:finished", onFinished);
|
|
135196
|
-
this.once("execution:failed", onFail);
|
|
135197
|
-
this.runner.execute();
|
|
135198
|
-
});
|
|
135199
|
-
}
|
|
135200
|
-
on(event, fun) {
|
|
135201
|
-
this.emitter.on(event, fun);
|
|
135202
|
-
}
|
|
135203
|
-
off(event, fun) {
|
|
135204
|
-
this.emitter.off(event, fun);
|
|
135205
|
-
}
|
|
135206
|
-
once(event, fun) {
|
|
135207
|
-
this.emitter.once(event, fun);
|
|
135208
|
-
}
|
|
135209
|
-
createContext(executionDate, execution) {
|
|
135210
|
-
const localTime = new localized_time_1.LocalizedTime(executionDate, this.timezone);
|
|
135211
|
-
const ctx = {
|
|
135212
|
-
date: localTime.toDate(),
|
|
135213
|
-
dateLocalIso: localTime.toISO(),
|
|
135214
|
-
triggeredAt: /* @__PURE__ */ new Date(),
|
|
135215
|
-
task: this,
|
|
135216
|
-
execution
|
|
135217
|
-
};
|
|
135218
|
-
return ctx;
|
|
135219
|
-
}
|
|
135220
|
-
};
|
|
135221
|
-
exports.InlineScheduledTask = InlineScheduledTask;
|
|
135222
|
-
}
|
|
135223
|
-
});
|
|
135224
|
-
|
|
135225
|
-
// node_modules/.pnpm/node-cron@4.2.1/node_modules/node-cron/dist/esm/task-registry.js
|
|
135226
|
-
var require_task_registry = __commonJS({
|
|
135227
|
-
"node_modules/.pnpm/node-cron@4.2.1/node_modules/node-cron/dist/esm/task-registry.js"(exports) {
|
|
135228
|
-
"use strict";
|
|
135229
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
135230
|
-
exports.TaskRegistry = void 0;
|
|
135231
|
-
var tasks2 = /* @__PURE__ */ new Map();
|
|
135232
|
-
var TaskRegistry = class {
|
|
135233
|
-
add(task) {
|
|
135234
|
-
if (this.has(task.id)) {
|
|
135235
|
-
throw Error(`task ${task.id} already registred!`);
|
|
135236
|
-
}
|
|
135237
|
-
tasks2.set(task.id, task);
|
|
135238
|
-
task.on("task:destroyed", () => {
|
|
135239
|
-
this.remove(task);
|
|
135240
|
-
});
|
|
135241
|
-
}
|
|
135242
|
-
get(taskId) {
|
|
135243
|
-
return tasks2.get(taskId);
|
|
135244
|
-
}
|
|
135245
|
-
remove(task) {
|
|
135246
|
-
if (this.has(task.id)) {
|
|
135247
|
-
task?.destroy();
|
|
135248
|
-
tasks2.delete(task.id);
|
|
135249
|
-
}
|
|
135250
|
-
}
|
|
135251
|
-
all() {
|
|
135252
|
-
return tasks2;
|
|
135253
|
-
}
|
|
135254
|
-
has(taskId) {
|
|
135255
|
-
return tasks2.has(taskId);
|
|
135256
|
-
}
|
|
135257
|
-
killAll() {
|
|
135258
|
-
tasks2.forEach((id) => this.remove(id));
|
|
135259
|
-
}
|
|
135260
|
-
};
|
|
135261
|
-
exports.TaskRegistry = TaskRegistry;
|
|
135262
|
-
}
|
|
135263
|
-
});
|
|
135264
|
-
|
|
135265
|
-
// node_modules/.pnpm/node-cron@4.2.1/node_modules/node-cron/dist/esm/pattern/validation/pattern-validation.js
|
|
135266
|
-
var require_pattern_validation = __commonJS({
|
|
135267
|
-
"node_modules/.pnpm/node-cron@4.2.1/node_modules/node-cron/dist/esm/pattern/validation/pattern-validation.js"(exports) {
|
|
135268
|
-
"use strict";
|
|
135269
|
-
var __importDefault = exports && exports.__importDefault || function(mod) {
|
|
135270
|
-
return mod && mod.__esModule ? mod : { "default": mod };
|
|
135271
|
-
};
|
|
135272
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
135273
|
-
var index_1 = __importDefault(require_convertion());
|
|
135274
|
-
var validationRegex = /^(?:\d+|\*|\*\/\d+)$/;
|
|
135275
|
-
function isValidExpression(expression, min, max) {
|
|
135276
|
-
const options = expression;
|
|
135277
|
-
for (const option of options) {
|
|
135278
|
-
const optionAsInt = parseInt(option, 10);
|
|
135279
|
-
if (!Number.isNaN(optionAsInt) && (optionAsInt < min || optionAsInt > max) || !validationRegex.test(option))
|
|
135280
|
-
return false;
|
|
135281
|
-
}
|
|
135282
|
-
return true;
|
|
135283
|
-
}
|
|
135284
|
-
function isInvalidSecond(expression) {
|
|
135285
|
-
return !isValidExpression(expression, 0, 59);
|
|
135286
|
-
}
|
|
135287
|
-
function isInvalidMinute(expression) {
|
|
135288
|
-
return !isValidExpression(expression, 0, 59);
|
|
135289
|
-
}
|
|
135290
|
-
function isInvalidHour(expression) {
|
|
135291
|
-
return !isValidExpression(expression, 0, 23);
|
|
135292
|
-
}
|
|
135293
|
-
function isInvalidDayOfMonth(expression) {
|
|
135294
|
-
return !isValidExpression(expression, 1, 31);
|
|
135295
|
-
}
|
|
135296
|
-
function isInvalidMonth(expression) {
|
|
135297
|
-
return !isValidExpression(expression, 1, 12);
|
|
135298
|
-
}
|
|
135299
|
-
function isInvalidWeekDay(expression) {
|
|
135300
|
-
return !isValidExpression(expression, 0, 7);
|
|
135301
|
-
}
|
|
135302
|
-
function validateFields(patterns, executablePatterns) {
|
|
135303
|
-
if (isInvalidSecond(executablePatterns[0]))
|
|
135304
|
-
throw new Error(`${patterns[0]} is a invalid expression for second`);
|
|
135305
|
-
if (isInvalidMinute(executablePatterns[1]))
|
|
135306
|
-
throw new Error(`${patterns[1]} is a invalid expression for minute`);
|
|
135307
|
-
if (isInvalidHour(executablePatterns[2]))
|
|
135308
|
-
throw new Error(`${patterns[2]} is a invalid expression for hour`);
|
|
135309
|
-
if (isInvalidDayOfMonth(executablePatterns[3]))
|
|
135310
|
-
throw new Error(`${patterns[3]} is a invalid expression for day of month`);
|
|
135311
|
-
if (isInvalidMonth(executablePatterns[4]))
|
|
135312
|
-
throw new Error(`${patterns[4]} is a invalid expression for month`);
|
|
135313
|
-
if (isInvalidWeekDay(executablePatterns[5]))
|
|
135314
|
-
throw new Error(`${patterns[5]} is a invalid expression for week day`);
|
|
135315
|
-
}
|
|
135316
|
-
function validate(pattern) {
|
|
135317
|
-
if (typeof pattern !== "string")
|
|
135318
|
-
throw new TypeError("pattern must be a string!");
|
|
135319
|
-
const patterns = pattern.split(" ");
|
|
135320
|
-
const executablePatterns = (0, index_1.default)(pattern);
|
|
135321
|
-
if (patterns.length === 5)
|
|
135322
|
-
patterns.unshift("0");
|
|
135323
|
-
validateFields(patterns, executablePatterns);
|
|
135324
|
-
}
|
|
135325
|
-
exports.default = validate;
|
|
135326
|
-
}
|
|
135327
|
-
});
|
|
135328
|
-
|
|
135329
|
-
// node_modules/.pnpm/node-cron@4.2.1/node_modules/node-cron/dist/esm/tasks/background-scheduled-task/background-scheduled-task.js
|
|
135330
|
-
var require_background_scheduled_task = __commonJS({
|
|
135331
|
-
"node_modules/.pnpm/node-cron@4.2.1/node_modules/node-cron/dist/esm/tasks/background-scheduled-task/background-scheduled-task.js"(exports) {
|
|
135332
|
-
"use strict";
|
|
135333
|
-
var __importDefault = exports && exports.__importDefault || function(mod) {
|
|
135334
|
-
return mod && mod.__esModule ? mod : { "default": mod };
|
|
135335
|
-
};
|
|
135336
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
135337
|
-
var path_1 = __require("path");
|
|
135338
|
-
var child_process_1 = __require("child_process");
|
|
135339
|
-
var create_id_1 = require_create_id();
|
|
135340
|
-
var stream_1 = __require("stream");
|
|
135341
|
-
var state_machine_1 = require_state_machine();
|
|
135342
|
-
var localized_time_1 = require_localized_time();
|
|
135343
|
-
var logger_1 = __importDefault(require_logger());
|
|
135344
|
-
var time_matcher_1 = require_time_matcher();
|
|
135345
|
-
var daemonPath = (0, path_1.resolve)(__dirname, "daemon.js");
|
|
135346
|
-
var TaskEmitter = class extends stream_1.EventEmitter {
|
|
135347
|
-
};
|
|
135348
|
-
var BackgroundScheduledTask = class {
|
|
135349
|
-
emitter;
|
|
135350
|
-
id;
|
|
135351
|
-
name;
|
|
135352
|
-
cronExpression;
|
|
135353
|
-
taskPath;
|
|
135354
|
-
options;
|
|
135355
|
-
forkProcess;
|
|
135356
|
-
stateMachine;
|
|
135357
|
-
constructor(cronExpression, taskPath, options) {
|
|
135358
|
-
this.cronExpression = cronExpression;
|
|
135359
|
-
this.taskPath = taskPath;
|
|
135360
|
-
this.options = options;
|
|
135361
|
-
this.id = (0, create_id_1.createID)("task");
|
|
135362
|
-
this.name = options?.name || this.id;
|
|
135363
|
-
this.emitter = new TaskEmitter();
|
|
135364
|
-
this.stateMachine = new state_machine_1.StateMachine("stopped");
|
|
135365
|
-
this.on("task:stopped", () => {
|
|
135366
|
-
this.forkProcess?.kill();
|
|
135367
|
-
this.forkProcess = void 0;
|
|
135368
|
-
this.stateMachine.changeState("stopped");
|
|
135369
|
-
});
|
|
135370
|
-
this.on("task:destroyed", () => {
|
|
135371
|
-
this.forkProcess?.kill();
|
|
135372
|
-
this.forkProcess = void 0;
|
|
135373
|
-
this.stateMachine.changeState("destroyed");
|
|
135374
|
-
});
|
|
135375
|
-
}
|
|
135376
|
-
getNextRun() {
|
|
135377
|
-
if (this.stateMachine.state !== "stopped") {
|
|
135378
|
-
const timeMatcher = new time_matcher_1.TimeMatcher(this.cronExpression, this.options?.timezone);
|
|
135379
|
-
return timeMatcher.getNextMatch(/* @__PURE__ */ new Date());
|
|
135380
|
-
}
|
|
135381
|
-
return null;
|
|
135382
|
-
}
|
|
135383
|
-
start() {
|
|
135384
|
-
return new Promise((resolve17, reject) => {
|
|
135385
|
-
if (this.forkProcess) {
|
|
135386
|
-
return resolve17(void 0);
|
|
135387
|
-
}
|
|
135388
|
-
const timeout = setTimeout(() => {
|
|
135389
|
-
reject(new Error("Start operation timed out"));
|
|
135390
|
-
}, 5e3);
|
|
135391
|
-
try {
|
|
135392
|
-
this.forkProcess = (0, child_process_1.fork)(daemonPath);
|
|
135393
|
-
this.forkProcess.on("error", (err) => {
|
|
135394
|
-
clearTimeout(timeout);
|
|
135395
|
-
reject(new Error(`Error on daemon: ${err.message}`));
|
|
135396
|
-
});
|
|
135397
|
-
this.forkProcess.on("exit", (code, signal) => {
|
|
135398
|
-
if (code !== 0 && signal !== "SIGTERM") {
|
|
135399
|
-
const erro = new Error(`node-cron daemon exited with code ${code || signal}`);
|
|
135400
|
-
logger_1.default.error(erro);
|
|
135401
|
-
clearTimeout(timeout);
|
|
135402
|
-
reject(erro);
|
|
135403
|
-
}
|
|
135404
|
-
});
|
|
135405
|
-
this.forkProcess.on("message", (message) => {
|
|
135406
|
-
if (message.jsonError) {
|
|
135407
|
-
if (message.context?.execution) {
|
|
135408
|
-
message.context.execution.error = deserializeError(message.jsonError);
|
|
135409
|
-
delete message.jsonError;
|
|
135410
|
-
}
|
|
135411
|
-
}
|
|
135412
|
-
if (message.context?.task?.state) {
|
|
135413
|
-
this.stateMachine.changeState(message.context?.task?.state);
|
|
135414
|
-
}
|
|
135415
|
-
if (message.context) {
|
|
135416
|
-
const execution = message.context?.execution;
|
|
135417
|
-
delete execution?.hasError;
|
|
135418
|
-
const context = this.createContext(new Date(message.context.date), execution);
|
|
135419
|
-
this.emitter.emit(message.event, context);
|
|
135420
|
-
}
|
|
135421
|
-
});
|
|
135422
|
-
this.once("task:started", () => {
|
|
135423
|
-
this.stateMachine.changeState("idle");
|
|
135424
|
-
clearTimeout(timeout);
|
|
135425
|
-
resolve17(void 0);
|
|
135426
|
-
});
|
|
135427
|
-
this.forkProcess.send({
|
|
135428
|
-
command: "task:start",
|
|
135429
|
-
path: this.taskPath,
|
|
135430
|
-
cron: this.cronExpression,
|
|
135431
|
-
options: this.options
|
|
135432
|
-
});
|
|
135433
|
-
} catch (error48) {
|
|
135434
|
-
reject(error48);
|
|
135435
|
-
}
|
|
135436
|
-
});
|
|
135437
|
-
}
|
|
135438
|
-
stop() {
|
|
135439
|
-
return new Promise((resolve17, reject) => {
|
|
135440
|
-
if (!this.forkProcess) {
|
|
135441
|
-
return resolve17(void 0);
|
|
135442
|
-
}
|
|
135443
|
-
const timeoutId = setTimeout(() => {
|
|
135444
|
-
clearTimeout(timeoutId);
|
|
135445
|
-
reject(new Error("Stop operation timed out"));
|
|
135446
|
-
}, 5e3);
|
|
135447
|
-
const cleanupAndResolve = () => {
|
|
135448
|
-
clearTimeout(timeoutId);
|
|
135449
|
-
this.off("task:stopped", onStopped);
|
|
135450
|
-
this.forkProcess = void 0;
|
|
135451
|
-
resolve17(void 0);
|
|
135452
|
-
};
|
|
135453
|
-
const onStopped = () => {
|
|
135454
|
-
cleanupAndResolve();
|
|
135455
|
-
};
|
|
135456
|
-
this.once("task:stopped", onStopped);
|
|
135457
|
-
this.forkProcess.send({
|
|
135458
|
-
command: "task:stop"
|
|
135459
|
-
});
|
|
135460
|
-
});
|
|
135461
|
-
}
|
|
135462
|
-
getStatus() {
|
|
135463
|
-
return this.stateMachine.state;
|
|
135464
|
-
}
|
|
135465
|
-
destroy() {
|
|
135466
|
-
return new Promise((resolve17, reject) => {
|
|
135467
|
-
if (!this.forkProcess) {
|
|
135468
|
-
return resolve17(void 0);
|
|
135469
|
-
}
|
|
135470
|
-
const timeoutId = setTimeout(() => {
|
|
135471
|
-
clearTimeout(timeoutId);
|
|
135472
|
-
reject(new Error("Destroy operation timed out"));
|
|
135473
|
-
}, 5e3);
|
|
135474
|
-
const onDestroy = () => {
|
|
135475
|
-
clearTimeout(timeoutId);
|
|
135476
|
-
this.off("task:destroyed", onDestroy);
|
|
135477
|
-
resolve17(void 0);
|
|
135478
|
-
};
|
|
135479
|
-
this.once("task:destroyed", onDestroy);
|
|
135480
|
-
this.forkProcess.send({
|
|
135481
|
-
command: "task:destroy"
|
|
135482
|
-
});
|
|
135483
|
-
});
|
|
135484
|
-
}
|
|
135485
|
-
execute() {
|
|
135486
|
-
return new Promise((resolve17, reject) => {
|
|
135487
|
-
if (!this.forkProcess) {
|
|
135488
|
-
return reject(new Error("Cannot execute background task because it hasn't been started yet. Please initialize the task using the start() method before attempting to execute it."));
|
|
135489
|
-
}
|
|
135490
|
-
const timeoutId = setTimeout(() => {
|
|
135491
|
-
cleanupListeners();
|
|
135492
|
-
reject(new Error("Execution timeout exceeded"));
|
|
135493
|
-
}, 5e3);
|
|
135494
|
-
const cleanupListeners = () => {
|
|
135495
|
-
clearTimeout(timeoutId);
|
|
135496
|
-
this.off("execution:finished", onFinished);
|
|
135497
|
-
this.off("execution:failed", onFail);
|
|
135498
|
-
};
|
|
135499
|
-
const onFinished = (context) => {
|
|
135500
|
-
cleanupListeners();
|
|
135501
|
-
resolve17(context.execution?.result);
|
|
135502
|
-
};
|
|
135503
|
-
const onFail = (context) => {
|
|
135504
|
-
cleanupListeners();
|
|
135505
|
-
reject(context.execution?.error || new Error("Execution failed without specific error"));
|
|
135506
|
-
};
|
|
135507
|
-
this.once("execution:finished", onFinished);
|
|
135508
|
-
this.once("execution:failed", onFail);
|
|
135509
|
-
this.forkProcess.send({
|
|
135510
|
-
command: "task:execute"
|
|
135511
|
-
});
|
|
135512
|
-
});
|
|
135513
|
-
}
|
|
135514
|
-
on(event, fun) {
|
|
135515
|
-
this.emitter.on(event, fun);
|
|
135516
|
-
}
|
|
135517
|
-
off(event, fun) {
|
|
135518
|
-
this.emitter.off(event, fun);
|
|
135519
|
-
}
|
|
135520
|
-
once(event, fun) {
|
|
135521
|
-
this.emitter.once(event, fun);
|
|
135522
|
-
}
|
|
135523
|
-
createContext(executionDate, execution) {
|
|
135524
|
-
const localTime = new localized_time_1.LocalizedTime(executionDate, this.options?.timezone);
|
|
135525
|
-
const ctx = {
|
|
135526
|
-
date: localTime.toDate(),
|
|
135527
|
-
dateLocalIso: localTime.toISO(),
|
|
135528
|
-
triggeredAt: /* @__PURE__ */ new Date(),
|
|
135529
|
-
task: this,
|
|
135530
|
-
execution
|
|
135531
|
-
};
|
|
135532
|
-
return ctx;
|
|
135533
|
-
}
|
|
135534
|
-
};
|
|
135535
|
-
function deserializeError(str) {
|
|
135536
|
-
const data = JSON.parse(str);
|
|
135537
|
-
const Err = globalThis[data.name] || Error;
|
|
135538
|
-
const err = new Err(data.message);
|
|
135539
|
-
if (data.stack) {
|
|
135540
|
-
err.stack = data.stack;
|
|
135541
|
-
}
|
|
135542
|
-
Object.keys(data).forEach((key) => {
|
|
135543
|
-
if (!["name", "message", "stack"].includes(key)) {
|
|
135544
|
-
err[key] = data[key];
|
|
135545
|
-
}
|
|
135546
|
-
});
|
|
135547
|
-
return err;
|
|
135548
|
-
}
|
|
135549
|
-
exports.default = BackgroundScheduledTask;
|
|
135550
|
-
}
|
|
135551
|
-
});
|
|
135552
|
-
|
|
135553
|
-
// node_modules/.pnpm/node-cron@4.2.1/node_modules/node-cron/dist/esm/node-cron.js
|
|
135554
|
-
var require_node_cron = __commonJS({
|
|
135555
|
-
"node_modules/.pnpm/node-cron@4.2.1/node_modules/node-cron/dist/esm/node-cron.js"(exports) {
|
|
135556
|
-
"use strict";
|
|
135557
|
-
var __importDefault = exports && exports.__importDefault || function(mod) {
|
|
135558
|
-
return mod && mod.__esModule ? mod : { "default": mod };
|
|
135559
|
-
};
|
|
135560
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
135561
|
-
exports.nodeCron = exports.getTask = exports.getTasks = void 0;
|
|
135562
|
-
exports.schedule = schedule;
|
|
135563
|
-
exports.createTask = createTask2;
|
|
135564
|
-
exports.solvePath = solvePath;
|
|
135565
|
-
exports.validate = validate;
|
|
135566
|
-
var inline_scheduled_task_1 = require_inline_scheduled_task();
|
|
135567
|
-
var task_registry_1 = require_task_registry();
|
|
135568
|
-
var pattern_validation_1 = __importDefault(require_pattern_validation());
|
|
135569
|
-
var background_scheduled_task_1 = __importDefault(require_background_scheduled_task());
|
|
135570
|
-
var path_1 = __importDefault(__require("path"));
|
|
135571
|
-
var url_1 = __require("url");
|
|
135572
|
-
var registry2 = new task_registry_1.TaskRegistry();
|
|
135573
|
-
function schedule(expression, func, options) {
|
|
135574
|
-
const task = createTask2(expression, func, options);
|
|
135575
|
-
task.start();
|
|
135576
|
-
return task;
|
|
135577
|
-
}
|
|
135578
|
-
function createTask2(expression, func, options) {
|
|
135579
|
-
let task;
|
|
135580
|
-
if (func instanceof Function) {
|
|
135581
|
-
task = new inline_scheduled_task_1.InlineScheduledTask(expression, func, options);
|
|
135582
|
-
} else {
|
|
135583
|
-
const taskPath = solvePath(func);
|
|
135584
|
-
task = new background_scheduled_task_1.default(expression, taskPath, options);
|
|
135585
|
-
}
|
|
135586
|
-
registry2.add(task);
|
|
135587
|
-
return task;
|
|
135588
|
-
}
|
|
135589
|
-
function solvePath(filePath) {
|
|
135590
|
-
if (path_1.default.isAbsolute(filePath))
|
|
135591
|
-
return (0, url_1.pathToFileURL)(filePath).href;
|
|
135592
|
-
if (filePath.startsWith("file://"))
|
|
135593
|
-
return filePath;
|
|
135594
|
-
const stackLines = new Error().stack?.split("\n");
|
|
135595
|
-
if (stackLines) {
|
|
135596
|
-
stackLines?.shift();
|
|
135597
|
-
const callerLine = stackLines?.find((line2) => {
|
|
135598
|
-
return line2.indexOf(__filename) === -1;
|
|
135599
|
-
});
|
|
135600
|
-
const match = callerLine?.match(/(file:\/\/)?(((\/?)(\w:))?([/\\].+)):\d+:\d+/);
|
|
135601
|
-
if (match) {
|
|
135602
|
-
const dir = `${match[5] ?? ""}${path_1.default.dirname(match[6])}`;
|
|
135603
|
-
return (0, url_1.pathToFileURL)(path_1.default.resolve(dir, filePath)).href;
|
|
135604
|
-
}
|
|
135605
|
-
}
|
|
135606
|
-
throw new Error(`Could not locate task file ${filePath}`);
|
|
135607
|
-
}
|
|
135608
|
-
function validate(expression) {
|
|
135609
|
-
try {
|
|
135610
|
-
(0, pattern_validation_1.default)(expression);
|
|
135611
|
-
return true;
|
|
135612
|
-
} catch (e) {
|
|
135613
|
-
return false;
|
|
135614
|
-
}
|
|
135615
|
-
}
|
|
135616
|
-
exports.getTasks = registry2.all;
|
|
135617
|
-
exports.getTask = registry2.get;
|
|
135618
|
-
exports.nodeCron = {
|
|
135619
|
-
schedule,
|
|
135620
|
-
createTask: createTask2,
|
|
135621
|
-
validate,
|
|
135622
|
-
getTasks: exports.getTasks,
|
|
135623
|
-
getTask: exports.getTask
|
|
135624
|
-
};
|
|
135625
|
-
exports.default = exports.nodeCron;
|
|
135626
|
-
}
|
|
135627
|
-
});
|
|
135628
|
-
|
|
135629
134289
|
// packages/cleo/src/cli/index.ts
|
|
135630
134290
|
init_internal();
|
|
135631
134291
|
import { readFileSync as readFileSync107 } from "node:fs";
|
|
@@ -141103,6 +139763,566 @@ function registerCurrentCommand(program) {
|
|
|
141103
139763
|
});
|
|
141104
139764
|
}
|
|
141105
139765
|
|
|
139766
|
+
// packages/cleo/src/cli/commands/daemon.ts
|
|
139767
|
+
import { homedir as homedir10 } from "node:os";
|
|
139768
|
+
import { join as join133 } from "node:path";
|
|
139769
|
+
|
|
139770
|
+
// packages/cleo/src/gc/daemon.ts
|
|
139771
|
+
import { spawn as spawn2 } from "node:child_process";
|
|
139772
|
+
import { createWriteStream as createWriteStream2 } from "node:fs";
|
|
139773
|
+
import { mkdir as mkdir20 } from "node:fs/promises";
|
|
139774
|
+
import { join as join132 } from "node:path";
|
|
139775
|
+
import { fileURLToPath as fileURLToPath8 } from "node:url";
|
|
139776
|
+
import cron from "node-cron";
|
|
139777
|
+
|
|
139778
|
+
// packages/cleo/src/gc/runner.ts
|
|
139779
|
+
import { lstat as lstat2, readdir as readdir4, rm as rm3, stat as stat4 } from "node:fs/promises";
|
|
139780
|
+
import { homedir as homedir9 } from "node:os";
|
|
139781
|
+
import { join as join131 } from "node:path";
|
|
139782
|
+
|
|
139783
|
+
// node_modules/.pnpm/check-disk-space@3.4.0/node_modules/check-disk-space/dist/check-disk-space.mjs
|
|
139784
|
+
import { execFile as execFile7 } from "node:child_process";
|
|
139785
|
+
import { access as access4 } from "node:fs/promises";
|
|
139786
|
+
import { release as release2 } from "node:os";
|
|
139787
|
+
import { normalize as normalize2, sep as sep2 } from "node:path";
|
|
139788
|
+
import { platform as platform5 } from "node:process";
|
|
139789
|
+
import { promisify as promisify7 } from "node:util";
|
|
139790
|
+
var InvalidPathError = class _InvalidPathError extends Error {
|
|
139791
|
+
constructor(message) {
|
|
139792
|
+
super(message);
|
|
139793
|
+
this.name = "InvalidPathError";
|
|
139794
|
+
Object.setPrototypeOf(this, _InvalidPathError.prototype);
|
|
139795
|
+
}
|
|
139796
|
+
};
|
|
139797
|
+
var NoMatchError = class _NoMatchError extends Error {
|
|
139798
|
+
constructor(message) {
|
|
139799
|
+
super(message);
|
|
139800
|
+
this.name = "NoMatchError";
|
|
139801
|
+
Object.setPrototypeOf(this, _NoMatchError.prototype);
|
|
139802
|
+
}
|
|
139803
|
+
};
|
|
139804
|
+
async function isDirectoryExisting(directoryPath, dependencies) {
|
|
139805
|
+
try {
|
|
139806
|
+
await dependencies.fsAccess(directoryPath);
|
|
139807
|
+
return Promise.resolve(true);
|
|
139808
|
+
} catch (error48) {
|
|
139809
|
+
return Promise.resolve(false);
|
|
139810
|
+
}
|
|
139811
|
+
}
|
|
139812
|
+
async function getFirstExistingParentPath(directoryPath, dependencies) {
|
|
139813
|
+
let parentDirectoryPath = directoryPath;
|
|
139814
|
+
let parentDirectoryFound = await isDirectoryExisting(parentDirectoryPath, dependencies);
|
|
139815
|
+
while (!parentDirectoryFound) {
|
|
139816
|
+
parentDirectoryPath = dependencies.pathNormalize(parentDirectoryPath + "/..");
|
|
139817
|
+
parentDirectoryFound = await isDirectoryExisting(parentDirectoryPath, dependencies);
|
|
139818
|
+
}
|
|
139819
|
+
return parentDirectoryPath;
|
|
139820
|
+
}
|
|
139821
|
+
async function hasPowerShell3(dependencies) {
|
|
139822
|
+
const major = parseInt(dependencies.release.split(".")[0], 10);
|
|
139823
|
+
if (major <= 6) {
|
|
139824
|
+
return false;
|
|
139825
|
+
}
|
|
139826
|
+
try {
|
|
139827
|
+
await dependencies.cpExecFile("where", ["powershell"], { windowsHide: true });
|
|
139828
|
+
return true;
|
|
139829
|
+
} catch (error48) {
|
|
139830
|
+
return false;
|
|
139831
|
+
}
|
|
139832
|
+
}
|
|
139833
|
+
function checkDiskSpace(directoryPath, dependencies = {
|
|
139834
|
+
platform: platform5,
|
|
139835
|
+
release: release2(),
|
|
139836
|
+
fsAccess: access4,
|
|
139837
|
+
pathNormalize: normalize2,
|
|
139838
|
+
pathSep: sep2,
|
|
139839
|
+
cpExecFile: promisify7(execFile7)
|
|
139840
|
+
}) {
|
|
139841
|
+
function mapOutput(stdout2, filter, mapping, coefficient) {
|
|
139842
|
+
const parsed = stdout2.split("\n").map((line2) => line2.trim()).filter((line2) => line2.length !== 0).slice(1).map((line2) => line2.split(/\s+(?=[\d/])/));
|
|
139843
|
+
const filtered = parsed.filter(filter);
|
|
139844
|
+
if (filtered.length === 0) {
|
|
139845
|
+
throw new NoMatchError();
|
|
139846
|
+
}
|
|
139847
|
+
const diskData = filtered[0];
|
|
139848
|
+
return {
|
|
139849
|
+
diskPath: diskData[mapping.diskPath],
|
|
139850
|
+
free: parseInt(diskData[mapping.free], 10) * coefficient,
|
|
139851
|
+
size: parseInt(diskData[mapping.size], 10) * coefficient
|
|
139852
|
+
};
|
|
139853
|
+
}
|
|
139854
|
+
async function check2(cmd, filter, mapping, coefficient = 1) {
|
|
139855
|
+
const [file2, ...args] = cmd;
|
|
139856
|
+
if (file2 === void 0) {
|
|
139857
|
+
return Promise.reject(new Error("cmd must contain at least one item"));
|
|
139858
|
+
}
|
|
139859
|
+
try {
|
|
139860
|
+
const { stdout: stdout2 } = await dependencies.cpExecFile(file2, args, { windowsHide: true });
|
|
139861
|
+
return mapOutput(stdout2, filter, mapping, coefficient);
|
|
139862
|
+
} catch (error48) {
|
|
139863
|
+
return Promise.reject(error48);
|
|
139864
|
+
}
|
|
139865
|
+
}
|
|
139866
|
+
async function checkWin32(directoryPath2) {
|
|
139867
|
+
if (directoryPath2.charAt(1) !== ":") {
|
|
139868
|
+
return Promise.reject(new InvalidPathError(`The following path is invalid (should be X:\\...): ${directoryPath2}`));
|
|
139869
|
+
}
|
|
139870
|
+
const powershellCmd = [
|
|
139871
|
+
"powershell",
|
|
139872
|
+
"Get-CimInstance -ClassName Win32_LogicalDisk | Select-Object Caption, FreeSpace, Size"
|
|
139873
|
+
];
|
|
139874
|
+
const wmicCmd = [
|
|
139875
|
+
"wmic",
|
|
139876
|
+
"logicaldisk",
|
|
139877
|
+
"get",
|
|
139878
|
+
"size,freespace,caption"
|
|
139879
|
+
];
|
|
139880
|
+
const cmd = await hasPowerShell3(dependencies) ? powershellCmd : wmicCmd;
|
|
139881
|
+
return check2(cmd, (driveData) => {
|
|
139882
|
+
const driveLetter = driveData[0];
|
|
139883
|
+
return directoryPath2.toUpperCase().startsWith(driveLetter.toUpperCase());
|
|
139884
|
+
}, {
|
|
139885
|
+
diskPath: 0,
|
|
139886
|
+
free: 1,
|
|
139887
|
+
size: 2
|
|
139888
|
+
});
|
|
139889
|
+
}
|
|
139890
|
+
async function checkUnix(directoryPath2) {
|
|
139891
|
+
if (!dependencies.pathNormalize(directoryPath2).startsWith(dependencies.pathSep)) {
|
|
139892
|
+
return Promise.reject(new InvalidPathError(`The following path is invalid (should start by ${dependencies.pathSep}): ${directoryPath2}`));
|
|
139893
|
+
}
|
|
139894
|
+
const pathToCheck = await getFirstExistingParentPath(directoryPath2, dependencies);
|
|
139895
|
+
return check2(
|
|
139896
|
+
[
|
|
139897
|
+
"df",
|
|
139898
|
+
"-Pk",
|
|
139899
|
+
"--",
|
|
139900
|
+
pathToCheck
|
|
139901
|
+
],
|
|
139902
|
+
() => true,
|
|
139903
|
+
// We should only get one line, so we did not need to filter
|
|
139904
|
+
{
|
|
139905
|
+
diskPath: 5,
|
|
139906
|
+
free: 3,
|
|
139907
|
+
size: 1
|
|
139908
|
+
},
|
|
139909
|
+
1024
|
|
139910
|
+
);
|
|
139911
|
+
}
|
|
139912
|
+
if (dependencies.platform === "win32") {
|
|
139913
|
+
return checkWin32(directoryPath);
|
|
139914
|
+
}
|
|
139915
|
+
return checkUnix(directoryPath);
|
|
139916
|
+
}
|
|
139917
|
+
|
|
139918
|
+
// packages/cleo/src/gc/state.ts
|
|
139919
|
+
import { mkdir as mkdir19, readFile as readFile22, rename as rename2, writeFile as writeFile14 } from "node:fs/promises";
|
|
139920
|
+
import { dirname as dirname29, join as join130 } from "node:path";
|
|
139921
|
+
var GC_STATE_SCHEMA_VERSION = "1.0";
|
|
139922
|
+
var DEFAULT_GC_STATE = {
|
|
139923
|
+
schemaVersion: GC_STATE_SCHEMA_VERSION,
|
|
139924
|
+
lastRunAt: null,
|
|
139925
|
+
lastRunResult: null,
|
|
139926
|
+
lastRunBytesFreed: 0,
|
|
139927
|
+
pendingPrune: null,
|
|
139928
|
+
consecutiveFailures: 0,
|
|
139929
|
+
diskThresholdBreached: false,
|
|
139930
|
+
lastDiskUsedPct: null,
|
|
139931
|
+
escalationNeeded: false,
|
|
139932
|
+
escalationReason: null,
|
|
139933
|
+
daemonPid: null,
|
|
139934
|
+
daemonStartedAt: null
|
|
139935
|
+
};
|
|
139936
|
+
async function readGCState(statePath) {
|
|
139937
|
+
try {
|
|
139938
|
+
const raw = await readFile22(statePath, "utf-8");
|
|
139939
|
+
const parsed = JSON.parse(raw);
|
|
139940
|
+
return { ...DEFAULT_GC_STATE, ...parsed };
|
|
139941
|
+
} catch {
|
|
139942
|
+
return { ...DEFAULT_GC_STATE };
|
|
139943
|
+
}
|
|
139944
|
+
}
|
|
139945
|
+
async function writeGCState(statePath, state) {
|
|
139946
|
+
const dir = dirname29(statePath);
|
|
139947
|
+
await mkdir19(dir, { recursive: true });
|
|
139948
|
+
const tmpPath = join130(dir, `.gc-state-${process.pid}.tmp`);
|
|
139949
|
+
const json3 = JSON.stringify(state, null, 2);
|
|
139950
|
+
await writeFile14(tmpPath, json3, "utf-8");
|
|
139951
|
+
await rename2(tmpPath, statePath);
|
|
139952
|
+
}
|
|
139953
|
+
async function patchGCState(statePath, patch) {
|
|
139954
|
+
const current = await readGCState(statePath);
|
|
139955
|
+
const updated = { ...current, ...patch };
|
|
139956
|
+
await writeGCState(statePath, updated);
|
|
139957
|
+
return updated;
|
|
139958
|
+
}
|
|
139959
|
+
|
|
139960
|
+
// packages/cleo/src/gc/runner.ts
|
|
139961
|
+
var DISK_THRESHOLDS = {
|
|
139962
|
+
WATCH: 70,
|
|
139963
|
+
WARN: 85,
|
|
139964
|
+
URGENT: 90,
|
|
139965
|
+
EMERGENCY: 95
|
|
139966
|
+
};
|
|
139967
|
+
function classifyDiskTier(pct) {
|
|
139968
|
+
if (pct >= DISK_THRESHOLDS.EMERGENCY) return "emergency";
|
|
139969
|
+
if (pct >= DISK_THRESHOLDS.URGENT) return "urgent";
|
|
139970
|
+
if (pct >= DISK_THRESHOLDS.WARN) return "warn";
|
|
139971
|
+
if (pct >= DISK_THRESHOLDS.WATCH) return "watch";
|
|
139972
|
+
return "ok";
|
|
139973
|
+
}
|
|
139974
|
+
function retentionMs(tier) {
|
|
139975
|
+
switch (tier) {
|
|
139976
|
+
case "emergency":
|
|
139977
|
+
return 1 * 24 * 60 * 60 * 1e3;
|
|
139978
|
+
// 1 day
|
|
139979
|
+
case "urgent":
|
|
139980
|
+
return 3 * 24 * 60 * 60 * 1e3;
|
|
139981
|
+
// 3 days
|
|
139982
|
+
case "warn":
|
|
139983
|
+
return 7 * 24 * 60 * 60 * 1e3;
|
|
139984
|
+
// 7 days
|
|
139985
|
+
default:
|
|
139986
|
+
return 30 * 24 * 60 * 60 * 1e3;
|
|
139987
|
+
}
|
|
139988
|
+
}
|
|
139989
|
+
async function getPathBytes(targetPath) {
|
|
139990
|
+
try {
|
|
139991
|
+
const info = await lstat2(targetPath);
|
|
139992
|
+
if (info.isFile()) return info.size;
|
|
139993
|
+
if (!info.isDirectory()) return 0;
|
|
139994
|
+
const entries = await readdir4(targetPath, { withFileTypes: true });
|
|
139995
|
+
let total = 0;
|
|
139996
|
+
for (const entry of entries) {
|
|
139997
|
+
total += await getPathBytes(join131(targetPath, entry.name));
|
|
139998
|
+
}
|
|
139999
|
+
return total;
|
|
140000
|
+
} catch {
|
|
140001
|
+
return 0;
|
|
140002
|
+
}
|
|
140003
|
+
}
|
|
140004
|
+
async function idempotentRm(targetPath) {
|
|
140005
|
+
try {
|
|
140006
|
+
await rm3(targetPath, { recursive: true, force: true });
|
|
140007
|
+
} catch (err) {
|
|
140008
|
+
const nodeErr = err;
|
|
140009
|
+
if (nodeErr.code === "ENOENT") return;
|
|
140010
|
+
throw err;
|
|
140011
|
+
}
|
|
140012
|
+
}
|
|
140013
|
+
async function gatherPruneCandidates(maxAgeMs, projectsDir) {
|
|
140014
|
+
const resolvedProjectsDir = projectsDir ?? join131(homedir9(), ".claude", "projects");
|
|
140015
|
+
const candidates = [];
|
|
140016
|
+
const now2 = Date.now();
|
|
140017
|
+
let projectSlugs;
|
|
140018
|
+
try {
|
|
140019
|
+
const entries = await readdir4(resolvedProjectsDir, { withFileTypes: true });
|
|
140020
|
+
projectSlugs = entries.filter((e) => e.isDirectory()).map((e) => e.name);
|
|
140021
|
+
} catch {
|
|
140022
|
+
return candidates;
|
|
140023
|
+
}
|
|
140024
|
+
for (const slug of projectSlugs) {
|
|
140025
|
+
const slugDir = join131(resolvedProjectsDir, slug);
|
|
140026
|
+
let slugEntries;
|
|
140027
|
+
try {
|
|
140028
|
+
slugEntries = await readdir4(slugDir, { withFileTypes: true });
|
|
140029
|
+
} catch {
|
|
140030
|
+
continue;
|
|
140031
|
+
}
|
|
140032
|
+
for (const entry of slugEntries) {
|
|
140033
|
+
const entryPath = join131(slugDir, entry.name);
|
|
140034
|
+
if (entry.isFile() && entry.name.endsWith(".jsonl")) {
|
|
140035
|
+
try {
|
|
140036
|
+
const info = await stat4(entryPath);
|
|
140037
|
+
const ageMs = now2 - info.mtimeMs;
|
|
140038
|
+
if (ageMs > maxAgeMs) {
|
|
140039
|
+
candidates.push(entryPath);
|
|
140040
|
+
}
|
|
140041
|
+
} catch {
|
|
140042
|
+
}
|
|
140043
|
+
} else if (entry.isDirectory()) {
|
|
140044
|
+
try {
|
|
140045
|
+
const info = await stat4(entryPath);
|
|
140046
|
+
const ageMs = now2 - info.mtimeMs;
|
|
140047
|
+
if (ageMs > maxAgeMs) {
|
|
140048
|
+
candidates.push(entryPath);
|
|
140049
|
+
}
|
|
140050
|
+
} catch {
|
|
140051
|
+
}
|
|
140052
|
+
}
|
|
140053
|
+
}
|
|
140054
|
+
}
|
|
140055
|
+
return candidates;
|
|
140056
|
+
}
|
|
140057
|
+
async function runGC(opts = {}) {
|
|
140058
|
+
const cleoDir = opts.cleoDir ?? join131(homedir9(), ".cleo");
|
|
140059
|
+
const statePath = join131(cleoDir, "gc-state.json");
|
|
140060
|
+
const dryRun = opts.dryRun ?? false;
|
|
140061
|
+
const projectsDir = opts.projectsDir;
|
|
140062
|
+
const initialState = await readGCState(statePath);
|
|
140063
|
+
const resumePaths = opts.resumeFrom ?? initialState.pendingPrune ?? [];
|
|
140064
|
+
let diskUsedPct = 0;
|
|
140065
|
+
try {
|
|
140066
|
+
const { free, size } = await checkDiskSpace(cleoDir);
|
|
140067
|
+
diskUsedPct = size > 0 ? (size - free) / size * 100 : 0;
|
|
140068
|
+
} catch {
|
|
140069
|
+
diskUsedPct = 0;
|
|
140070
|
+
}
|
|
140071
|
+
const tier = classifyDiskTier(diskUsedPct);
|
|
140072
|
+
const maxAgeMs = retentionMs(tier);
|
|
140073
|
+
const candidatesFromScan = resumePaths.length > 0 ? resumePaths : await gatherPruneCandidates(maxAgeMs, projectsDir);
|
|
140074
|
+
if (!dryRun && candidatesFromScan.length > 0) {
|
|
140075
|
+
await patchGCState(statePath, { pendingPrune: candidatesFromScan });
|
|
140076
|
+
}
|
|
140077
|
+
const pruned = [];
|
|
140078
|
+
let bytesFreed = 0;
|
|
140079
|
+
const remaining = [...candidatesFromScan];
|
|
140080
|
+
for (const candidatePath of candidatesFromScan) {
|
|
140081
|
+
const bytes = await getPathBytes(candidatePath);
|
|
140082
|
+
if (dryRun) {
|
|
140083
|
+
pruned.push({ path: candidatePath, bytes });
|
|
140084
|
+
bytesFreed += bytes;
|
|
140085
|
+
continue;
|
|
140086
|
+
}
|
|
140087
|
+
try {
|
|
140088
|
+
await idempotentRm(candidatePath);
|
|
140089
|
+
pruned.push({ path: candidatePath, bytes });
|
|
140090
|
+
bytesFreed += bytes;
|
|
140091
|
+
const idx = remaining.indexOf(candidatePath);
|
|
140092
|
+
if (idx !== -1) remaining.splice(idx, 1);
|
|
140093
|
+
await patchGCState(statePath, {
|
|
140094
|
+
pendingPrune: remaining.length > 0 ? remaining : null
|
|
140095
|
+
});
|
|
140096
|
+
} catch {
|
|
140097
|
+
}
|
|
140098
|
+
}
|
|
140099
|
+
const escalationSet = tier === "warn" || tier === "urgent" || tier === "emergency";
|
|
140100
|
+
let escalationReason = null;
|
|
140101
|
+
if (escalationSet) {
|
|
140102
|
+
escalationReason = `Disk at ${diskUsedPct.toFixed(1)}% (${tier.toUpperCase()}): ${pruned.length} paths pruned, ${bytesFreed} bytes freed`;
|
|
140103
|
+
}
|
|
140104
|
+
const completedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
140105
|
+
if (!dryRun) {
|
|
140106
|
+
await patchGCState(statePath, {
|
|
140107
|
+
lastRunAt: completedAt,
|
|
140108
|
+
lastRunResult: remaining.length === 0 ? "success" : "partial",
|
|
140109
|
+
lastRunBytesFreed: bytesFreed,
|
|
140110
|
+
pendingPrune: remaining.length > 0 ? remaining : null,
|
|
140111
|
+
consecutiveFailures: remaining.length > 0 ? initialState.consecutiveFailures + 1 : 0,
|
|
140112
|
+
diskThresholdBreached: diskUsedPct >= DISK_THRESHOLDS.WATCH,
|
|
140113
|
+
lastDiskUsedPct: diskUsedPct,
|
|
140114
|
+
escalationNeeded: escalationSet || initialState.escalationNeeded,
|
|
140115
|
+
escalationReason: escalationReason ?? initialState.escalationReason
|
|
140116
|
+
});
|
|
140117
|
+
}
|
|
140118
|
+
return {
|
|
140119
|
+
diskUsedPct,
|
|
140120
|
+
threshold: tier,
|
|
140121
|
+
pruned,
|
|
140122
|
+
bytesFreed,
|
|
140123
|
+
escalationSet,
|
|
140124
|
+
escalationReason,
|
|
140125
|
+
completedAt
|
|
140126
|
+
};
|
|
140127
|
+
}
|
|
140128
|
+
|
|
140129
|
+
// packages/cleo/src/gc/daemon.ts
|
|
140130
|
+
var GC_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
140131
|
+
async function spawnGCDaemon(cleoDir) {
|
|
140132
|
+
const logsDir = join132(cleoDir, "logs");
|
|
140133
|
+
await mkdir20(logsDir, { recursive: true });
|
|
140134
|
+
const logPath = join132(logsDir, "gc.log");
|
|
140135
|
+
const errPath = join132(logsDir, "gc.err");
|
|
140136
|
+
const outStream = createWriteStream2(logPath, { flags: "a" });
|
|
140137
|
+
const errStream = createWriteStream2(errPath, { flags: "a" });
|
|
140138
|
+
const daemonEntry = join132(fileURLToPath8(import.meta.url), "..", "daemon-entry.js");
|
|
140139
|
+
const child = spawn2(process.execPath, [daemonEntry, cleoDir], {
|
|
140140
|
+
detached: true,
|
|
140141
|
+
stdio: ["ignore", outStream, errStream],
|
|
140142
|
+
env: { ...process.env, CLEO_GC_DAEMON: "1" }
|
|
140143
|
+
});
|
|
140144
|
+
child.unref();
|
|
140145
|
+
const pid = child.pid ?? 0;
|
|
140146
|
+
await patchGCState(join132(cleoDir, "gc-state.json"), {
|
|
140147
|
+
daemonPid: pid,
|
|
140148
|
+
daemonStartedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
140149
|
+
});
|
|
140150
|
+
return pid;
|
|
140151
|
+
}
|
|
140152
|
+
async function stopGCDaemon(cleoDir) {
|
|
140153
|
+
const statePath = join132(cleoDir, "gc-state.json");
|
|
140154
|
+
const state = await readGCState(statePath);
|
|
140155
|
+
const pid = state.daemonPid;
|
|
140156
|
+
if (!pid) {
|
|
140157
|
+
return { stopped: false, pid: null, reason: "Daemon PID not found in gc-state.json" };
|
|
140158
|
+
}
|
|
140159
|
+
try {
|
|
140160
|
+
process.kill(pid, 0);
|
|
140161
|
+
} catch {
|
|
140162
|
+
await patchGCState(statePath, { daemonPid: null });
|
|
140163
|
+
return {
|
|
140164
|
+
stopped: false,
|
|
140165
|
+
pid,
|
|
140166
|
+
reason: `Daemon PID ${pid} is not running (stale state cleared)`
|
|
140167
|
+
};
|
|
140168
|
+
}
|
|
140169
|
+
try {
|
|
140170
|
+
process.kill(pid, "SIGTERM");
|
|
140171
|
+
await patchGCState(statePath, { daemonPid: null });
|
|
140172
|
+
return { stopped: true, pid, reason: `SIGTERM sent to PID ${pid}` };
|
|
140173
|
+
} catch (err) {
|
|
140174
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
140175
|
+
return { stopped: false, pid, reason: `Failed to send SIGTERM to PID ${pid}: ${msg}` };
|
|
140176
|
+
}
|
|
140177
|
+
}
|
|
140178
|
+
async function getGCDaemonStatus(cleoDir) {
|
|
140179
|
+
const state = await readGCState(join132(cleoDir, "gc-state.json"));
|
|
140180
|
+
const pid = state.daemonPid;
|
|
140181
|
+
let running = false;
|
|
140182
|
+
if (pid) {
|
|
140183
|
+
try {
|
|
140184
|
+
process.kill(pid, 0);
|
|
140185
|
+
running = true;
|
|
140186
|
+
} catch {
|
|
140187
|
+
running = false;
|
|
140188
|
+
}
|
|
140189
|
+
}
|
|
140190
|
+
return {
|
|
140191
|
+
running,
|
|
140192
|
+
pid: running ? pid : null,
|
|
140193
|
+
startedAt: state.daemonStartedAt,
|
|
140194
|
+
lastRunAt: state.lastRunAt,
|
|
140195
|
+
lastDiskUsedPct: state.lastDiskUsedPct,
|
|
140196
|
+
escalationNeeded: state.escalationNeeded
|
|
140197
|
+
};
|
|
140198
|
+
}
|
|
140199
|
+
|
|
140200
|
+
// packages/cleo/src/cli/commands/daemon.ts
|
|
140201
|
+
function registerDaemonCommand(program) {
|
|
140202
|
+
const daemon = program.command("daemon").description("Manage the CLEO GC sidecar daemon for autonomous transcript cleanup");
|
|
140203
|
+
daemon.command("start").description("Spawn the GC daemon as a detached background process").option("--cleo-dir <path>", "Override .cleo/ directory path").option("--json", "Output result as JSON").action(async (opts) => {
|
|
140204
|
+
const cleoDir = opts.cleoDir ?? join133(homedir10(), ".cleo");
|
|
140205
|
+
try {
|
|
140206
|
+
const status = await getGCDaemonStatus(cleoDir);
|
|
140207
|
+
if (status.running && status.pid) {
|
|
140208
|
+
const result2 = {
|
|
140209
|
+
success: false,
|
|
140210
|
+
data: {
|
|
140211
|
+
running: true,
|
|
140212
|
+
pid: status.pid,
|
|
140213
|
+
message: `Daemon already running (PID ${status.pid})`
|
|
140214
|
+
}
|
|
140215
|
+
};
|
|
140216
|
+
if (opts.json) {
|
|
140217
|
+
process.stdout.write(JSON.stringify(result2) + "\n");
|
|
140218
|
+
} else {
|
|
140219
|
+
process.stdout.write(`Daemon already running (PID ${status.pid})
|
|
140220
|
+
`);
|
|
140221
|
+
}
|
|
140222
|
+
return;
|
|
140223
|
+
}
|
|
140224
|
+
const pid = await spawnGCDaemon(cleoDir);
|
|
140225
|
+
const result = {
|
|
140226
|
+
success: true,
|
|
140227
|
+
data: { pid, cleoDir, message: `GC daemon started (PID ${pid})` }
|
|
140228
|
+
};
|
|
140229
|
+
if (opts.json) {
|
|
140230
|
+
process.stdout.write(JSON.stringify(result) + "\n");
|
|
140231
|
+
} else {
|
|
140232
|
+
process.stdout.write(`GC daemon started (PID ${pid})
|
|
140233
|
+
`);
|
|
140234
|
+
process.stdout.write(`Logs: ${join133(cleoDir, "logs", "gc.log")}
|
|
140235
|
+
`);
|
|
140236
|
+
}
|
|
140237
|
+
} catch (err) {
|
|
140238
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
140239
|
+
const result = { success: false, error: { code: "E_INTERNAL", message } };
|
|
140240
|
+
if (opts.json) {
|
|
140241
|
+
process.stdout.write(JSON.stringify(result) + "\n");
|
|
140242
|
+
} else {
|
|
140243
|
+
process.stderr.write(`Error starting daemon: ${message}
|
|
140244
|
+
`);
|
|
140245
|
+
}
|
|
140246
|
+
process.exit(1);
|
|
140247
|
+
}
|
|
140248
|
+
});
|
|
140249
|
+
daemon.command("stop").description("Stop the GC daemon by sending SIGTERM to its PID").option("--cleo-dir <path>", "Override .cleo/ directory path").option("--json", "Output result as JSON").action(async (opts) => {
|
|
140250
|
+
const cleoDir = opts.cleoDir ?? join133(homedir10(), ".cleo");
|
|
140251
|
+
try {
|
|
140252
|
+
const stopResult = await stopGCDaemon(cleoDir);
|
|
140253
|
+
const result = {
|
|
140254
|
+
success: stopResult.stopped,
|
|
140255
|
+
data: stopResult
|
|
140256
|
+
};
|
|
140257
|
+
if (opts.json) {
|
|
140258
|
+
process.stdout.write(JSON.stringify(result) + "\n");
|
|
140259
|
+
} else if (stopResult.stopped) {
|
|
140260
|
+
process.stdout.write(`GC daemon stopped (${stopResult.reason})
|
|
140261
|
+
`);
|
|
140262
|
+
} else {
|
|
140263
|
+
process.stdout.write(`${stopResult.reason}
|
|
140264
|
+
`);
|
|
140265
|
+
}
|
|
140266
|
+
} catch (err) {
|
|
140267
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
140268
|
+
const result = { success: false, error: { code: "E_INTERNAL", message } };
|
|
140269
|
+
if (opts.json) {
|
|
140270
|
+
process.stdout.write(JSON.stringify(result) + "\n");
|
|
140271
|
+
} else {
|
|
140272
|
+
process.stderr.write(`Error stopping daemon: ${message}
|
|
140273
|
+
`);
|
|
140274
|
+
}
|
|
140275
|
+
process.exit(1);
|
|
140276
|
+
}
|
|
140277
|
+
});
|
|
140278
|
+
daemon.command("status").description("Show daemon running state, PID, last GC run, and disk usage").option("--cleo-dir <path>", "Override .cleo/ directory path").option("--json", "Output result as JSON").action(async (opts) => {
|
|
140279
|
+
const cleoDir = opts.cleoDir ?? join133(homedir10(), ".cleo");
|
|
140280
|
+
await showDaemonStatus(cleoDir, opts.json ?? false);
|
|
140281
|
+
});
|
|
140282
|
+
daemon.action(async (opts) => {
|
|
140283
|
+
const cleoDir = opts.cleoDir ?? join133(homedir10(), ".cleo");
|
|
140284
|
+
await showDaemonStatus(cleoDir, opts.json ?? false);
|
|
140285
|
+
});
|
|
140286
|
+
}
|
|
140287
|
+
async function showDaemonStatus(cleoDir, json3) {
|
|
140288
|
+
try {
|
|
140289
|
+
const status = await getGCDaemonStatus(cleoDir);
|
|
140290
|
+
const result = { success: true, data: status };
|
|
140291
|
+
if (json3) {
|
|
140292
|
+
process.stdout.write(JSON.stringify(result) + "\n");
|
|
140293
|
+
} else {
|
|
140294
|
+
const runningStr = status.running ? `running (PID ${status.pid})` : "stopped";
|
|
140295
|
+
process.stdout.write(`Daemon: ${runningStr}
|
|
140296
|
+
`);
|
|
140297
|
+
process.stdout.write(`Started at: ${status.startedAt ?? "never"}
|
|
140298
|
+
`);
|
|
140299
|
+
process.stdout.write(`Last GC run: ${status.lastRunAt ?? "never"}
|
|
140300
|
+
`);
|
|
140301
|
+
const diskStr = status.lastDiskUsedPct !== null ? `${status.lastDiskUsedPct.toFixed(1)}%` : "unknown";
|
|
140302
|
+
process.stdout.write(`Disk used: ${diskStr}
|
|
140303
|
+
`);
|
|
140304
|
+
if (status.escalationNeeded) {
|
|
140305
|
+
process.stdout.write(
|
|
140306
|
+
`
|
|
140307
|
+
WARNING: Disk threshold breached. Run 'cleo gc run' to reclaim space.
|
|
140308
|
+
`
|
|
140309
|
+
);
|
|
140310
|
+
}
|
|
140311
|
+
}
|
|
140312
|
+
} catch (err) {
|
|
140313
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
140314
|
+
if (json3) {
|
|
140315
|
+
process.stdout.write(
|
|
140316
|
+
JSON.stringify({ success: false, error: { code: "E_INTERNAL", message } }) + "\n"
|
|
140317
|
+
);
|
|
140318
|
+
} else {
|
|
140319
|
+
process.stderr.write(`Error reading daemon status: ${message}
|
|
140320
|
+
`);
|
|
140321
|
+
}
|
|
140322
|
+
process.exit(1);
|
|
140323
|
+
}
|
|
140324
|
+
}
|
|
140325
|
+
|
|
141106
140326
|
// packages/cleo/src/cli/commands/dash.ts
|
|
141107
140327
|
init_cli();
|
|
141108
140328
|
function registerDashCommand(program) {
|
|
@@ -141251,14 +140471,14 @@ function registerDetectCommand(program) {
|
|
|
141251
140471
|
init_src();
|
|
141252
140472
|
init_renderers();
|
|
141253
140473
|
import { existsSync as existsSync133, readdirSync as readdirSync41, readFileSync as readFileSync103 } from "node:fs";
|
|
141254
|
-
import { dirname as
|
|
140474
|
+
import { dirname as dirname30, join as join134 } from "node:path";
|
|
141255
140475
|
function findProjectRoot() {
|
|
141256
140476
|
let currentDir = process.cwd();
|
|
141257
140477
|
while (currentDir !== "/") {
|
|
141258
|
-
if (existsSync133(
|
|
140478
|
+
if (existsSync133(join134(currentDir, "package.json"))) {
|
|
141259
140479
|
return currentDir;
|
|
141260
140480
|
}
|
|
141261
|
-
const parent =
|
|
140481
|
+
const parent = dirname30(currentDir);
|
|
141262
140482
|
if (parent === currentDir) break;
|
|
141263
140483
|
currentDir = parent;
|
|
141264
140484
|
}
|
|
@@ -141267,8 +140487,8 @@ function findProjectRoot() {
|
|
|
141267
140487
|
function registerDetectDriftCommand(program) {
|
|
141268
140488
|
program.command("detect-drift").description("Detect documentation drift against TypeScript source of truth").action(async () => {
|
|
141269
140489
|
const projectRoot = findProjectRoot();
|
|
141270
|
-
const isCleoRepo = existsSync133(
|
|
141271
|
-
const cleoSrcRoot = isCleoRepo ?
|
|
140490
|
+
const isCleoRepo = existsSync133(join134(projectRoot, "packages", "cleo", "src"));
|
|
140491
|
+
const cleoSrcRoot = isCleoRepo ? join134(projectRoot, "packages", "cleo", "src") : join134(projectRoot, "src");
|
|
141272
140492
|
const safeRead = (filePath) => {
|
|
141273
140493
|
try {
|
|
141274
140494
|
return readFileSync103(filePath, "utf-8");
|
|
@@ -141282,7 +140502,7 @@ function registerDetectDriftCommand(program) {
|
|
|
141282
140502
|
checks: [],
|
|
141283
140503
|
recommendations: []
|
|
141284
140504
|
};
|
|
141285
|
-
const injPath =
|
|
140505
|
+
const injPath = join134(projectRoot, ".cleo", "templates", "CLEO-INJECTION.md");
|
|
141286
140506
|
if (existsSync133(injPath)) {
|
|
141287
140507
|
const content = safeRead(injPath);
|
|
141288
140508
|
userResult.checks.push({
|
|
@@ -141334,9 +140554,9 @@ function registerDetectDriftCommand(program) {
|
|
|
141334
140554
|
}
|
|
141335
140555
|
};
|
|
141336
140556
|
try {
|
|
141337
|
-
const specPath =
|
|
141338
|
-
const registryPath =
|
|
141339
|
-
const dispatchDomainsDir =
|
|
140557
|
+
const specPath = join134(projectRoot, "docs", "specs", "CLEO-OPERATION-CONSTITUTION.md");
|
|
140558
|
+
const registryPath = join134(cleoSrcRoot, "dispatch", "registry.ts");
|
|
140559
|
+
const dispatchDomainsDir = join134(cleoSrcRoot, "dispatch", "domains");
|
|
141340
140560
|
if (!existsSync133(specPath)) {
|
|
141341
140561
|
addCheck("Gateway-to-spec sync", "fail", "CLEO-OPERATION-CONSTITUTION.md missing", [
|
|
141342
140562
|
{
|
|
@@ -141411,8 +140631,8 @@ function registerDetectDriftCommand(program) {
|
|
|
141411
140631
|
]);
|
|
141412
140632
|
}
|
|
141413
140633
|
try {
|
|
141414
|
-
const cliDir =
|
|
141415
|
-
const coreDir = isCleoRepo ?
|
|
140634
|
+
const cliDir = join134(cleoSrcRoot, "cli", "commands");
|
|
140635
|
+
const coreDir = isCleoRepo ? join134(projectRoot, "packages", "core", "src") : join134(projectRoot, "src", "core");
|
|
141416
140636
|
if (!existsSync133(cliDir)) {
|
|
141417
140637
|
addCheck("CLI-to-core sync", "fail", "CLI commands directory missing", [
|
|
141418
140638
|
{
|
|
@@ -141441,7 +140661,7 @@ function registerDetectDriftCommand(program) {
|
|
|
141441
140661
|
addCheck("CLI-to-core sync", "fail", `Error: ${getErrorMessage(e)}`);
|
|
141442
140662
|
}
|
|
141443
140663
|
try {
|
|
141444
|
-
const domainsDir =
|
|
140664
|
+
const domainsDir = join134(cleoSrcRoot, "dispatch", "domains");
|
|
141445
140665
|
if (!existsSync133(domainsDir)) {
|
|
141446
140666
|
addCheck("Domain handler coverage", "fail", "Dispatch domains directory missing", [
|
|
141447
140667
|
{
|
|
@@ -141459,7 +140679,7 @@ function registerDetectDriftCommand(program) {
|
|
|
141459
140679
|
addCheck("Domain handler coverage", "fail", `Error: ${getErrorMessage(e)}`);
|
|
141460
140680
|
}
|
|
141461
140681
|
try {
|
|
141462
|
-
const matrixPath =
|
|
140682
|
+
const matrixPath = join134(cleoSrcRoot, "dispatch", "lib", "capability-matrix.ts");
|
|
141463
140683
|
if (!existsSync133(matrixPath)) {
|
|
141464
140684
|
addCheck("Capability matrix", "fail", "Capability matrix missing", [
|
|
141465
140685
|
{
|
|
@@ -141476,7 +140696,7 @@ function registerDetectDriftCommand(program) {
|
|
|
141476
140696
|
addCheck("Capability matrix", "fail", `Error: ${getErrorMessage(e)}`);
|
|
141477
140697
|
}
|
|
141478
140698
|
try {
|
|
141479
|
-
const schemaPath =
|
|
140699
|
+
const schemaPath = join134(projectRoot, "src", "store", "schema.ts");
|
|
141480
140700
|
if (!existsSync133(schemaPath)) {
|
|
141481
140701
|
addCheck("Schema validation", "fail", "Schema definition missing", [
|
|
141482
140702
|
{
|
|
@@ -141511,8 +140731,8 @@ function registerDetectDriftCommand(program) {
|
|
|
141511
140731
|
addCheck("Schema validation", "fail", `Error: ${getErrorMessage(e)}`);
|
|
141512
140732
|
}
|
|
141513
140733
|
try {
|
|
141514
|
-
const visionPath =
|
|
141515
|
-
const specPath =
|
|
140734
|
+
const visionPath = join134(projectRoot, "docs", "concepts", "CLEO-VISION.md");
|
|
140735
|
+
const specPath = join134(projectRoot, "docs", "specs", "CLEO-PORTABLE-PROJECT-BRAIN-SPEC.md");
|
|
141516
140736
|
const issues = [];
|
|
141517
140737
|
if (!existsSync133(visionPath)) {
|
|
141518
140738
|
issues.push({
|
|
@@ -141567,7 +140787,7 @@ function registerDetectDriftCommand(program) {
|
|
|
141567
140787
|
addCheck("Canonical identity", "fail", `Error: ${getErrorMessage(e)}`);
|
|
141568
140788
|
}
|
|
141569
140789
|
try {
|
|
141570
|
-
const injectionPath =
|
|
140790
|
+
const injectionPath = join134(projectRoot, ".cleo", "templates", "CLEO-INJECTION.md");
|
|
141571
140791
|
if (!existsSync133(injectionPath)) {
|
|
141572
140792
|
addCheck("Agent injection", "fail", "Agent injection template missing", [
|
|
141573
140793
|
{
|
|
@@ -141597,7 +140817,7 @@ function registerDetectDriftCommand(program) {
|
|
|
141597
140817
|
addCheck("Agent injection", "fail", `Error: ${getErrorMessage(e)}`);
|
|
141598
140818
|
}
|
|
141599
140819
|
try {
|
|
141600
|
-
const exitCodesPath =
|
|
140820
|
+
const exitCodesPath = join134(cleoSrcRoot, "dispatch", "lib", "exit-codes.ts");
|
|
141601
140821
|
if (!existsSync133(exitCodesPath)) {
|
|
141602
140822
|
addCheck("Exit codes", "fail", "Exit codes definition missing", [
|
|
141603
140823
|
{
|
|
@@ -141674,19 +140894,19 @@ function registerDiagnosticsCommand(program) {
|
|
|
141674
140894
|
// packages/cleo/src/cli/commands/docs.ts
|
|
141675
140895
|
init_internal();
|
|
141676
140896
|
init_renderers();
|
|
141677
|
-
import { readdir as
|
|
141678
|
-
import { join as
|
|
140897
|
+
import { readdir as readdir5, readFile as readFile23 } from "node:fs/promises";
|
|
140898
|
+
import { join as join135 } from "node:path";
|
|
141679
140899
|
async function getScriptNames(projectRoot) {
|
|
141680
|
-
const scriptsDir =
|
|
140900
|
+
const scriptsDir = join135(projectRoot, "scripts");
|
|
141681
140901
|
try {
|
|
141682
|
-
const files = await
|
|
140902
|
+
const files = await readdir5(scriptsDir);
|
|
141683
140903
|
return files.filter((f2) => f2.endsWith(".sh")).map((f2) => f2.replace(".sh", "")).sort();
|
|
141684
140904
|
} catch {
|
|
141685
140905
|
return [];
|
|
141686
140906
|
}
|
|
141687
140907
|
}
|
|
141688
140908
|
async function getIndexedCommands(projectRoot) {
|
|
141689
|
-
const indexPath =
|
|
140909
|
+
const indexPath = join135(projectRoot, "docs", "commands", "COMMANDS-INDEX.json");
|
|
141690
140910
|
const index2 = await readJson(indexPath);
|
|
141691
140911
|
if (!index2) return [];
|
|
141692
140912
|
return index2.commands.map((c) => c.name).sort();
|
|
@@ -141715,12 +140935,12 @@ async function runGapCheck(_projectRoot, filterId) {
|
|
|
141715
140935
|
const reviewDir = getAgentOutputsAbsolute();
|
|
141716
140936
|
const results = [];
|
|
141717
140937
|
try {
|
|
141718
|
-
const files = await
|
|
140938
|
+
const files = await readdir5(reviewDir);
|
|
141719
140939
|
const reviewFiles = files.filter((f2) => f2.endsWith(".md"));
|
|
141720
140940
|
for (const file2 of reviewFiles) {
|
|
141721
140941
|
if (filterId && !file2.includes(filterId)) continue;
|
|
141722
|
-
const filePath =
|
|
141723
|
-
const content = await
|
|
140942
|
+
const filePath = join135(reviewDir, file2);
|
|
140943
|
+
const content = await readFile23(filePath, "utf-8");
|
|
141724
140944
|
const taskMatch = file2.match(/^(T\d+)/);
|
|
141725
140945
|
const taskId = taskMatch ? taskMatch[1] : "UNKNOWN";
|
|
141726
140946
|
const gaps = [];
|
|
@@ -142266,13 +141486,106 @@ function registerFindCommand(program) {
|
|
|
142266
141486
|
});
|
|
142267
141487
|
}
|
|
142268
141488
|
|
|
141489
|
+
// packages/cleo/src/cli/commands/gc.ts
|
|
141490
|
+
import { homedir as homedir11 } from "node:os";
|
|
141491
|
+
import { join as join136 } from "node:path";
|
|
141492
|
+
function registerGCCommand(program) {
|
|
141493
|
+
const gc = program.command("gc").description("Transcript garbage collection: manual trigger and status");
|
|
141494
|
+
gc.command("run").description("Run GC immediately (blocking). Prunes old transcripts based on disk pressure.").option("--cleo-dir <path>", "Override .cleo/ directory path").option("--dry-run", "Report what would be pruned without deleting anything").option("--json", "Output result as JSON").action(async (opts) => {
|
|
141495
|
+
const cleoDir = opts.cleoDir ?? join136(homedir11(), ".cleo");
|
|
141496
|
+
const dryRun = opts.dryRun ?? false;
|
|
141497
|
+
try {
|
|
141498
|
+
const gcResult = await runGC({ cleoDir, dryRun });
|
|
141499
|
+
const result = { success: true, data: gcResult };
|
|
141500
|
+
if (opts.json) {
|
|
141501
|
+
process.stdout.write(JSON.stringify(result) + "\n");
|
|
141502
|
+
} else {
|
|
141503
|
+
const dryLabel = dryRun ? " (dry run)" : "";
|
|
141504
|
+
process.stdout.write(`GC run complete${dryLabel}
|
|
141505
|
+
`);
|
|
141506
|
+
process.stdout.write(
|
|
141507
|
+
`Disk: ${gcResult.diskUsedPct.toFixed(1)}% (${gcResult.threshold.toUpperCase()})
|
|
141508
|
+
`
|
|
141509
|
+
);
|
|
141510
|
+
process.stdout.write(
|
|
141511
|
+
`Pruned: ${gcResult.pruned.length} paths, ${formatBytes(gcResult.bytesFreed)} freed
|
|
141512
|
+
`
|
|
141513
|
+
);
|
|
141514
|
+
if (gcResult.escalationSet && gcResult.escalationReason) {
|
|
141515
|
+
process.stdout.write(`
|
|
141516
|
+
WARNING: ${gcResult.escalationReason}
|
|
141517
|
+
`);
|
|
141518
|
+
}
|
|
141519
|
+
}
|
|
141520
|
+
} catch (err) {
|
|
141521
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
141522
|
+
const result = { success: false, error: { code: "E_INTERNAL", message } };
|
|
141523
|
+
if (opts.json) {
|
|
141524
|
+
process.stdout.write(JSON.stringify(result) + "\n");
|
|
141525
|
+
} else {
|
|
141526
|
+
process.stderr.write(`GC run failed: ${message}
|
|
141527
|
+
`);
|
|
141528
|
+
}
|
|
141529
|
+
process.exit(1);
|
|
141530
|
+
}
|
|
141531
|
+
});
|
|
141532
|
+
gc.command("status").description("Show last GC run stats, disk usage, and escalation state").option("--cleo-dir <path>", "Override .cleo/ directory path").option("--json", "Output result as JSON").action(async (opts) => {
|
|
141533
|
+
const cleoDir = opts.cleoDir ?? join136(homedir11(), ".cleo");
|
|
141534
|
+
const statePath = join136(cleoDir, "gc-state.json");
|
|
141535
|
+
try {
|
|
141536
|
+
const state = await readGCState(statePath);
|
|
141537
|
+
const result = { success: true, data: state };
|
|
141538
|
+
if (opts.json) {
|
|
141539
|
+
process.stdout.write(JSON.stringify(result) + "\n");
|
|
141540
|
+
} else {
|
|
141541
|
+
process.stdout.write(`Last run: ${state.lastRunAt ?? "never"}
|
|
141542
|
+
`);
|
|
141543
|
+
process.stdout.write(`Last result: ${state.lastRunResult ?? "none"}
|
|
141544
|
+
`);
|
|
141545
|
+
process.stdout.write(`Bytes freed: ${formatBytes(state.lastRunBytesFreed)}
|
|
141546
|
+
`);
|
|
141547
|
+
const diskStr = state.lastDiskUsedPct !== null ? `${state.lastDiskUsedPct.toFixed(1)}%` : "unknown";
|
|
141548
|
+
process.stdout.write(`Disk used: ${diskStr}
|
|
141549
|
+
`);
|
|
141550
|
+
process.stdout.write(`Failures: ${state.consecutiveFailures}
|
|
141551
|
+
`);
|
|
141552
|
+
process.stdout.write(
|
|
141553
|
+
`Escalation: ${state.escalationNeeded ? "YES \u2014 run cleo gc run" : "no"}
|
|
141554
|
+
`
|
|
141555
|
+
);
|
|
141556
|
+
if (state.escalationReason) {
|
|
141557
|
+
process.stdout.write(`Reason: ${state.escalationReason}
|
|
141558
|
+
`);
|
|
141559
|
+
}
|
|
141560
|
+
}
|
|
141561
|
+
} catch (err) {
|
|
141562
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
141563
|
+
const result = { success: false, error: { code: "E_INTERNAL", message } };
|
|
141564
|
+
if (opts.json) {
|
|
141565
|
+
process.stdout.write(JSON.stringify(result) + "\n");
|
|
141566
|
+
} else {
|
|
141567
|
+
process.stderr.write(`Error reading GC status: ${message}
|
|
141568
|
+
`);
|
|
141569
|
+
}
|
|
141570
|
+
process.exit(1);
|
|
141571
|
+
}
|
|
141572
|
+
});
|
|
141573
|
+
}
|
|
141574
|
+
function formatBytes(bytes) {
|
|
141575
|
+
if (bytes === 0) return "0 B";
|
|
141576
|
+
const units = ["B", "KB", "MB", "GB", "TB"];
|
|
141577
|
+
const exp = Math.floor(Math.log(bytes) / Math.log(1024));
|
|
141578
|
+
const value = bytes / 1024 ** exp;
|
|
141579
|
+
return `${value.toFixed(exp === 0 ? 0 : 1)} ${units[exp] ?? "B"}`;
|
|
141580
|
+
}
|
|
141581
|
+
|
|
142269
141582
|
// packages/cleo/src/cli/commands/generate-changelog.ts
|
|
142270
141583
|
init_src();
|
|
142271
141584
|
init_src3();
|
|
142272
141585
|
init_renderers();
|
|
142273
141586
|
import { execFileSync as execFileSync16 } from "node:child_process";
|
|
142274
141587
|
import { existsSync as existsSync134, mkdirSync as mkdirSync34, readFileSync as readFileSync104, writeFileSync as writeFileSync25 } from "node:fs";
|
|
142275
|
-
import { dirname as
|
|
141588
|
+
import { dirname as dirname31, join as join137 } from "node:path";
|
|
142276
141589
|
function getChangelogSource(cwd) {
|
|
142277
141590
|
const configPath = getConfigPath(cwd);
|
|
142278
141591
|
try {
|
|
@@ -142410,7 +141723,7 @@ function registerGenerateChangelogCommand(program) {
|
|
|
142410
141723
|
const targetPlatform = opts["platform"];
|
|
142411
141724
|
const dryRun = !!opts["dryRun"];
|
|
142412
141725
|
const sourceFile = getChangelogSource();
|
|
142413
|
-
const sourcePath =
|
|
141726
|
+
const sourcePath = join137(getProjectRoot(), sourceFile);
|
|
142414
141727
|
if (!existsSync134(sourcePath)) {
|
|
142415
141728
|
throw new CleoError(4 /* NOT_FOUND */, `Changelog source not found: ${sourcePath}`);
|
|
142416
141729
|
}
|
|
@@ -142423,8 +141736,8 @@ function registerGenerateChangelogCommand(program) {
|
|
|
142423
141736
|
const outputPath = platformConfig?.path ?? getDefaultOutputPath(targetPlatform);
|
|
142424
141737
|
const content = generateForPlatform(targetPlatform, sourceContent, repoSlug, limit);
|
|
142425
141738
|
if (!dryRun) {
|
|
142426
|
-
const fullPath =
|
|
142427
|
-
mkdirSync34(
|
|
141739
|
+
const fullPath = join137(getProjectRoot(), outputPath);
|
|
141740
|
+
mkdirSync34(dirname31(fullPath), { recursive: true });
|
|
142428
141741
|
writeFileSync25(fullPath, content, "utf-8");
|
|
142429
141742
|
}
|
|
142430
141743
|
results.push({ platform: targetPlatform, path: outputPath, written: !dryRun });
|
|
@@ -142444,8 +141757,8 @@ function registerGenerateChangelogCommand(program) {
|
|
|
142444
141757
|
limit
|
|
142445
141758
|
);
|
|
142446
141759
|
if (!dryRun) {
|
|
142447
|
-
const fullPath =
|
|
142448
|
-
mkdirSync34(
|
|
141760
|
+
const fullPath = join137(getProjectRoot(), platformConfig.path);
|
|
141761
|
+
mkdirSync34(dirname31(fullPath), { recursive: true });
|
|
142449
141762
|
writeFileSync25(fullPath, content, "utf-8");
|
|
142450
141763
|
}
|
|
142451
141764
|
results.push({
|
|
@@ -143032,8 +142345,8 @@ init_cli();
|
|
|
143032
142345
|
init_renderers();
|
|
143033
142346
|
import { createHash as createHash20 } from "node:crypto";
|
|
143034
142347
|
import { existsSync as existsSync135, mkdirSync as mkdirSync35, readdirSync as readdirSync42, readFileSync as readFileSync105, writeFileSync as writeFileSync26 } from "node:fs";
|
|
143035
|
-
import { homedir as
|
|
143036
|
-
import { join as
|
|
142348
|
+
import { homedir as homedir12 } from "node:os";
|
|
142349
|
+
import { join as join138 } from "node:path";
|
|
143037
142350
|
function parseMemoryFileFrontmatter(raw) {
|
|
143038
142351
|
const lines = raw.split("\n");
|
|
143039
142352
|
if (!lines[0]?.trim().startsWith("---")) {
|
|
@@ -143775,11 +143088,11 @@ Found ${totalDups} duplicate rows across ${groups.length} groups:`);
|
|
|
143775
143088
|
"--from <dir>",
|
|
143776
143089
|
"Source directory containing *.md memory files (default: ~/.claude/projects/-mnt-projects-cleocode/memory)"
|
|
143777
143090
|
).option("--dry-run", "Print what would be imported without writing to brain.db").option("--json", "Output results as JSON").action(async (opts) => {
|
|
143778
|
-
const sourceDir = opts.from ??
|
|
143091
|
+
const sourceDir = opts.from ?? join138(homedir12(), ".claude", "projects", "-mnt-projects-cleocode", "memory");
|
|
143779
143092
|
const isDryRun = !!opts.dryRun;
|
|
143780
143093
|
const isJson = !!opts.json;
|
|
143781
143094
|
const projectRoot = getProjectRoot();
|
|
143782
|
-
const stateFile =
|
|
143095
|
+
const stateFile = join138(projectRoot, ".cleo", "migrate-memory-hashes.json");
|
|
143783
143096
|
if (!existsSync135(sourceDir)) {
|
|
143784
143097
|
const msg = `Source directory not found: ${sourceDir}`;
|
|
143785
143098
|
if (isJson) {
|
|
@@ -143789,7 +143102,7 @@ Found ${totalDups} duplicate rows across ${groups.length} groups:`);
|
|
|
143789
143102
|
}
|
|
143790
143103
|
process.exit(1);
|
|
143791
143104
|
}
|
|
143792
|
-
const files = readdirSync42(sourceDir).filter((f2) => f2.endsWith(".md") && f2 !== "MEMORY.md").map((f2) =>
|
|
143105
|
+
const files = readdirSync42(sourceDir).filter((f2) => f2.endsWith(".md") && f2 !== "MEMORY.md").map((f2) => join138(sourceDir, f2));
|
|
143793
143106
|
const importedHashes = isDryRun ? /* @__PURE__ */ new Set() : loadImportHashes(stateFile);
|
|
143794
143107
|
const stats2 = { total: files.length, imported: 0, skipped: 0, errors: 0 };
|
|
143795
143108
|
const importedEntries = [];
|
|
@@ -143952,7 +143265,7 @@ Found ${totalDups} duplicate rows across ${groups.length} groups:`);
|
|
|
143952
143265
|
for (const tbl of tables) {
|
|
143953
143266
|
const dateCol = tbl === "brain_patterns" ? "extracted_at" : "created_at";
|
|
143954
143267
|
try {
|
|
143955
|
-
const
|
|
143268
|
+
const rawRows = nativeDb.prepare(
|
|
143956
143269
|
`SELECT id, ${dateCol} as created_at, citation_count, verified, quality_score
|
|
143957
143270
|
FROM ${tbl}
|
|
143958
143271
|
WHERE memory_tier = 'medium'
|
|
@@ -143961,6 +143274,17 @@ Found ${totalDups} duplicate rows across ${groups.length} groups:`);
|
|
|
143961
143274
|
ORDER BY ${dateCol} ASC
|
|
143962
143275
|
LIMIT 20`
|
|
143963
143276
|
).all();
|
|
143277
|
+
const rows = rawRows.map((raw) => {
|
|
143278
|
+
const r = raw;
|
|
143279
|
+
return {
|
|
143280
|
+
id: String(r["id"] ?? ""),
|
|
143281
|
+
tbl,
|
|
143282
|
+
created_at: String(r["created_at"] ?? ""),
|
|
143283
|
+
citation_count: Number(r["citation_count"] ?? 0),
|
|
143284
|
+
verified: Number(r["verified"] ?? 0),
|
|
143285
|
+
quality_score: r["quality_score"] == null ? null : Number(r["quality_score"])
|
|
143286
|
+
};
|
|
143287
|
+
});
|
|
143964
143288
|
for (const r of rows) {
|
|
143965
143289
|
const entryMs = new Date(r.created_at.replace(" ", "T")).getTime();
|
|
143966
143290
|
const promotionMs = entryMs + age7dMs;
|
|
@@ -148152,18 +147476,18 @@ function registerSchemaCommand(program) {
|
|
|
148152
147476
|
// packages/cleo/src/cli/commands/self-update.ts
|
|
148153
147477
|
init_src();
|
|
148154
147478
|
init_internal();
|
|
148155
|
-
import { execFile as
|
|
148156
|
-
import { readFile as
|
|
148157
|
-
import { join as
|
|
147479
|
+
import { execFile as execFile8 } from "node:child_process";
|
|
147480
|
+
import { readFile as readFile24 } from "node:fs/promises";
|
|
147481
|
+
import { join as join139 } from "node:path";
|
|
148158
147482
|
import * as readline2 from "node:readline";
|
|
148159
|
-
import { promisify as
|
|
147483
|
+
import { promisify as promisify8 } from "node:util";
|
|
148160
147484
|
init_renderers();
|
|
148161
|
-
var execAsync =
|
|
147485
|
+
var execAsync = promisify8(execFile8);
|
|
148162
147486
|
var GITHUB_REPO = BUILD_CONFIG.repository.fullName;
|
|
148163
147487
|
async function getCurrentVersion() {
|
|
148164
147488
|
const cleoHome = getCleoHome();
|
|
148165
147489
|
try {
|
|
148166
|
-
const content = await
|
|
147490
|
+
const content = await readFile24(join139(cleoHome, "VERSION"), "utf-8");
|
|
148167
147491
|
return (content.split("\n")[0] ?? "unknown").trim();
|
|
148168
147492
|
} catch {
|
|
148169
147493
|
return "unknown";
|
|
@@ -148217,7 +147541,7 @@ async function writeRuntimeVersionMetadata(mode, source, version2) {
|
|
|
148217
147541
|
];
|
|
148218
147542
|
await import("node:fs/promises").then(
|
|
148219
147543
|
({ writeFile: writeFile16, mkdir: mkdir22 }) => mkdir22(cleoHome, { recursive: true }).then(
|
|
148220
|
-
() => writeFile16(
|
|
147544
|
+
() => writeFile16(join139(cleoHome, "VERSION"), `${lines.join("\n")}
|
|
148221
147545
|
`, "utf-8")
|
|
148222
147546
|
)
|
|
148223
147547
|
);
|
|
@@ -149472,6 +148796,586 @@ function registerTokenCommand(program) {
|
|
|
149472
148796
|
});
|
|
149473
148797
|
}
|
|
149474
148798
|
|
|
148799
|
+
// packages/cleo/src/cli/commands/transcript.ts
|
|
148800
|
+
init_src3();
|
|
148801
|
+
import { homedir as homedir14 } from "node:os";
|
|
148802
|
+
import { join as join141 } from "node:path";
|
|
148803
|
+
|
|
148804
|
+
// packages/cleo/src/gc/transcript.ts
|
|
148805
|
+
import { lstat as lstat3, readdir as readdir6, stat as stat5 } from "node:fs/promises";
|
|
148806
|
+
import { homedir as homedir13 } from "node:os";
|
|
148807
|
+
import { join as join140 } from "node:path";
|
|
148808
|
+
var HOT_MAX_MS = 24 * 60 * 60 * 1e3;
|
|
148809
|
+
var WARM_MAX_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
148810
|
+
function classifyTranscriptTier(ageMs) {
|
|
148811
|
+
if (ageMs < HOT_MAX_MS) return "hot";
|
|
148812
|
+
if (ageMs < WARM_MAX_MS) return "warm";
|
|
148813
|
+
return "cold";
|
|
148814
|
+
}
|
|
148815
|
+
function parseSessionId(filename) {
|
|
148816
|
+
return filename.replace(/\.jsonl$/, "");
|
|
148817
|
+
}
|
|
148818
|
+
async function scanTranscripts(projectsDir) {
|
|
148819
|
+
const resolvedProjectsDir = projectsDir ?? join140(homedir13(), ".claude", "projects");
|
|
148820
|
+
const now2 = Date.now();
|
|
148821
|
+
const hot = [];
|
|
148822
|
+
const warm = [];
|
|
148823
|
+
let totalBytes = 0;
|
|
148824
|
+
let slugs;
|
|
148825
|
+
try {
|
|
148826
|
+
const entries = await readdir6(resolvedProjectsDir, { withFileTypes: true });
|
|
148827
|
+
slugs = entries.filter((e) => e.isDirectory()).map((e) => e.name);
|
|
148828
|
+
} catch {
|
|
148829
|
+
return { totalSessions: 0, hot, warm, totalBytes, projectsDir: resolvedProjectsDir };
|
|
148830
|
+
}
|
|
148831
|
+
for (const slug of slugs) {
|
|
148832
|
+
const slugDir = join140(resolvedProjectsDir, slug);
|
|
148833
|
+
let entries;
|
|
148834
|
+
try {
|
|
148835
|
+
entries = await readdir6(slugDir, { withFileTypes: true });
|
|
148836
|
+
} catch {
|
|
148837
|
+
continue;
|
|
148838
|
+
}
|
|
148839
|
+
for (const entry of entries) {
|
|
148840
|
+
if (!entry.isFile() || !entry.name.endsWith(".jsonl")) continue;
|
|
148841
|
+
const jsonlPath = join140(slugDir, entry.name);
|
|
148842
|
+
const sessionId = parseSessionId(entry.name);
|
|
148843
|
+
let fileInfo;
|
|
148844
|
+
try {
|
|
148845
|
+
fileInfo = await stat5(jsonlPath);
|
|
148846
|
+
} catch {
|
|
148847
|
+
continue;
|
|
148848
|
+
}
|
|
148849
|
+
const mtimeMs = fileInfo.mtimeMs;
|
|
148850
|
+
const ageMs = now2 - mtimeMs;
|
|
148851
|
+
const tier = classifyTranscriptTier(ageMs);
|
|
148852
|
+
const bytes = fileInfo.size;
|
|
148853
|
+
const candidateSessionDir = join140(slugDir, sessionId);
|
|
148854
|
+
let sessionDir = null;
|
|
148855
|
+
let sessionDirBytes = 0;
|
|
148856
|
+
try {
|
|
148857
|
+
const dirInfo = await lstat3(candidateSessionDir);
|
|
148858
|
+
if (dirInfo.isDirectory()) {
|
|
148859
|
+
sessionDir = candidateSessionDir;
|
|
148860
|
+
sessionDirBytes = await getPathBytes(candidateSessionDir);
|
|
148861
|
+
}
|
|
148862
|
+
} catch {
|
|
148863
|
+
}
|
|
148864
|
+
const info = {
|
|
148865
|
+
jsonlPath,
|
|
148866
|
+
projectSlug: slug,
|
|
148867
|
+
sessionId,
|
|
148868
|
+
mtimeMs,
|
|
148869
|
+
ageMs,
|
|
148870
|
+
tier,
|
|
148871
|
+
bytes,
|
|
148872
|
+
sessionDir,
|
|
148873
|
+
sessionDirBytes
|
|
148874
|
+
};
|
|
148875
|
+
totalBytes += bytes + sessionDirBytes;
|
|
148876
|
+
if (tier === "hot") {
|
|
148877
|
+
hot.push(info);
|
|
148878
|
+
} else if (tier === "warm") {
|
|
148879
|
+
warm.push(info);
|
|
148880
|
+
}
|
|
148881
|
+
}
|
|
148882
|
+
}
|
|
148883
|
+
const totalSessions = hot.length + warm.length;
|
|
148884
|
+
return { totalSessions, hot, warm, totalBytes, projectsDir: resolvedProjectsDir };
|
|
148885
|
+
}
|
|
148886
|
+
async function pruneTranscripts(opts) {
|
|
148887
|
+
const { olderThanMs, confirm, projectsDir } = opts;
|
|
148888
|
+
const dryRun = !confirm;
|
|
148889
|
+
const hasApiKey = Boolean(process.env["ANTHROPIC_API_KEY"]);
|
|
148890
|
+
const effectiveMaxAgeMs = hasApiKey ? olderThanMs : Math.max(olderThanMs, 30 * 24 * 60 * 60 * 1e3);
|
|
148891
|
+
const now2 = Date.now();
|
|
148892
|
+
const deletedPaths = [];
|
|
148893
|
+
let bytesFreed = 0;
|
|
148894
|
+
let pruned = 0;
|
|
148895
|
+
const resolvedProjectsDir = projectsDir ?? join140(homedir13(), ".claude", "projects");
|
|
148896
|
+
let slugs;
|
|
148897
|
+
try {
|
|
148898
|
+
const entries = await readdir6(resolvedProjectsDir, { withFileTypes: true });
|
|
148899
|
+
slugs = entries.filter((e) => e.isDirectory()).map((e) => e.name);
|
|
148900
|
+
} catch {
|
|
148901
|
+
return { pruned: 0, bytesFreed: 0, deletedPaths: [], dryRun };
|
|
148902
|
+
}
|
|
148903
|
+
for (const slug of slugs) {
|
|
148904
|
+
const slugDir = join140(resolvedProjectsDir, slug);
|
|
148905
|
+
let entries;
|
|
148906
|
+
try {
|
|
148907
|
+
entries = await readdir6(slugDir, { withFileTypes: true });
|
|
148908
|
+
} catch {
|
|
148909
|
+
continue;
|
|
148910
|
+
}
|
|
148911
|
+
for (const entry of entries) {
|
|
148912
|
+
if (!entry.isFile() || !entry.name.endsWith(".jsonl")) continue;
|
|
148913
|
+
const jsonlPath = join140(slugDir, entry.name);
|
|
148914
|
+
let fileInfo;
|
|
148915
|
+
try {
|
|
148916
|
+
fileInfo = await stat5(jsonlPath);
|
|
148917
|
+
} catch {
|
|
148918
|
+
continue;
|
|
148919
|
+
}
|
|
148920
|
+
const ageMs = now2 - fileInfo.mtimeMs;
|
|
148921
|
+
if (ageMs <= effectiveMaxAgeMs) continue;
|
|
148922
|
+
const sessionId = parseSessionId(entry.name);
|
|
148923
|
+
const sessionDir = join140(slugDir, sessionId);
|
|
148924
|
+
const jsonlBytes = fileInfo.size;
|
|
148925
|
+
let sessionDirBytes = 0;
|
|
148926
|
+
try {
|
|
148927
|
+
const dirInfo = await lstat3(sessionDir);
|
|
148928
|
+
if (dirInfo.isDirectory()) {
|
|
148929
|
+
sessionDirBytes = await getPathBytes(sessionDir);
|
|
148930
|
+
}
|
|
148931
|
+
} catch {
|
|
148932
|
+
}
|
|
148933
|
+
if (dryRun) {
|
|
148934
|
+
deletedPaths.push(jsonlPath);
|
|
148935
|
+
if (sessionDirBytes > 0) deletedPaths.push(sessionDir);
|
|
148936
|
+
bytesFreed += jsonlBytes + sessionDirBytes;
|
|
148937
|
+
pruned++;
|
|
148938
|
+
continue;
|
|
148939
|
+
}
|
|
148940
|
+
try {
|
|
148941
|
+
await idempotentRm(jsonlPath);
|
|
148942
|
+
deletedPaths.push(jsonlPath);
|
|
148943
|
+
bytesFreed += jsonlBytes;
|
|
148944
|
+
pruned++;
|
|
148945
|
+
} catch {
|
|
148946
|
+
continue;
|
|
148947
|
+
}
|
|
148948
|
+
try {
|
|
148949
|
+
const dirInfo = await lstat3(sessionDir);
|
|
148950
|
+
if (dirInfo.isDirectory()) {
|
|
148951
|
+
await idempotentRm(sessionDir);
|
|
148952
|
+
deletedPaths.push(sessionDir);
|
|
148953
|
+
bytesFreed += sessionDirBytes;
|
|
148954
|
+
}
|
|
148955
|
+
} catch {
|
|
148956
|
+
}
|
|
148957
|
+
}
|
|
148958
|
+
}
|
|
148959
|
+
return { pruned, bytesFreed, deletedPaths, dryRun };
|
|
148960
|
+
}
|
|
148961
|
+
function parseDurationMs(duration3) {
|
|
148962
|
+
const match = /^(\d+(\.\d+)?)(d|h|m|s)$/.exec(duration3.trim());
|
|
148963
|
+
if (!match?.[1] || !match[3]) {
|
|
148964
|
+
throw new Error(`Invalid duration format: "${duration3}". Use format like 7d, 24h, 30m, 60s.`);
|
|
148965
|
+
}
|
|
148966
|
+
const value = parseFloat(match[1]);
|
|
148967
|
+
const unit = match[3];
|
|
148968
|
+
const multipliers = {
|
|
148969
|
+
d: 24 * 60 * 60 * 1e3,
|
|
148970
|
+
h: 60 * 60 * 1e3,
|
|
148971
|
+
m: 60 * 1e3,
|
|
148972
|
+
s: 1e3
|
|
148973
|
+
};
|
|
148974
|
+
return value * (multipliers[unit] ?? 1e3);
|
|
148975
|
+
}
|
|
148976
|
+
|
|
148977
|
+
// packages/cleo/src/cli/commands/transcript.ts
|
|
148978
|
+
function registerTranscriptCommand(program) {
|
|
148979
|
+
const transcript = program.command("transcript").description("Transcript lifecycle management: scan, extract, and prune session transcripts");
|
|
148980
|
+
transcript.command("scan").description("Inventory all session transcripts with hot/warm/cold tier breakdown").option("--projects-dir <path>", "Override ~/.claude/projects/ path").option("--pending", "List sessions queued for warm-tier extraction (T732)").option("--json", "Output result as JSON (LAFS envelope)").action(async (opts) => {
|
|
148981
|
+
if (opts.pending) {
|
|
148982
|
+
try {
|
|
148983
|
+
const projectRoot = getProjectRoot();
|
|
148984
|
+
const { scanPendingTranscripts: scanPendingTranscripts2 } = await import("@cleocode/core/memory/transcript-scanner.js");
|
|
148985
|
+
const pending = await scanPendingTranscripts2(projectRoot);
|
|
148986
|
+
const envelope = { success: true, data: { count: pending.length, pending } };
|
|
148987
|
+
if (opts.json) {
|
|
148988
|
+
process.stdout.write(JSON.stringify(envelope) + "\n");
|
|
148989
|
+
} else {
|
|
148990
|
+
process.stdout.write(`Sessions pending extraction: ${pending.length}
|
|
148991
|
+
`);
|
|
148992
|
+
for (const p2 of pending) {
|
|
148993
|
+
process.stdout.write(
|
|
148994
|
+
` ${p2.sessionId} ${p2.filePath || "(file not found)"} queued: ${p2.createdAt}
|
|
148995
|
+
`
|
|
148996
|
+
);
|
|
148997
|
+
}
|
|
148998
|
+
}
|
|
148999
|
+
} catch (err) {
|
|
149000
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
149001
|
+
const envelope = { success: false, error: { code: "E_INTERNAL", message } };
|
|
149002
|
+
if (opts.json) {
|
|
149003
|
+
process.stdout.write(JSON.stringify(envelope) + "\n");
|
|
149004
|
+
} else {
|
|
149005
|
+
process.stderr.write(`Pending scan failed: ${message}
|
|
149006
|
+
`);
|
|
149007
|
+
}
|
|
149008
|
+
process.exit(1);
|
|
149009
|
+
}
|
|
149010
|
+
return;
|
|
149011
|
+
}
|
|
149012
|
+
const projectsDir = opts.projectsDir ?? join141(homedir14(), ".claude", "projects");
|
|
149013
|
+
try {
|
|
149014
|
+
const result = await scanTranscripts(projectsDir);
|
|
149015
|
+
const envelope = {
|
|
149016
|
+
success: true,
|
|
149017
|
+
data: {
|
|
149018
|
+
totalSessions: result.totalSessions,
|
|
149019
|
+
totalBytes: result.totalBytes,
|
|
149020
|
+
totalMB: parseFloat((result.totalBytes / 1024 / 1024).toFixed(2)),
|
|
149021
|
+
hot: {
|
|
149022
|
+
count: result.hot.length,
|
|
149023
|
+
sessions: result.hot.map((s3) => ({
|
|
149024
|
+
sessionId: s3.sessionId,
|
|
149025
|
+
projectSlug: s3.projectSlug,
|
|
149026
|
+
ageHours: parseFloat((s3.ageMs / 36e5).toFixed(1)),
|
|
149027
|
+
bytes: s3.bytes + s3.sessionDirBytes
|
|
149028
|
+
}))
|
|
149029
|
+
},
|
|
149030
|
+
warm: {
|
|
149031
|
+
count: result.warm.length,
|
|
149032
|
+
sessions: result.warm.map((s3) => ({
|
|
149033
|
+
sessionId: s3.sessionId,
|
|
149034
|
+
projectSlug: s3.projectSlug,
|
|
149035
|
+
ageDays: parseFloat((s3.ageMs / 864e5).toFixed(1)),
|
|
149036
|
+
bytes: s3.bytes + s3.sessionDirBytes
|
|
149037
|
+
}))
|
|
149038
|
+
},
|
|
149039
|
+
projectsDir: result.projectsDir
|
|
149040
|
+
}
|
|
149041
|
+
};
|
|
149042
|
+
if (opts.json) {
|
|
149043
|
+
process.stdout.write(JSON.stringify(envelope) + "\n");
|
|
149044
|
+
} else {
|
|
149045
|
+
process.stdout.write(`Transcript scan results
|
|
149046
|
+
`);
|
|
149047
|
+
process.stdout.write(`=======================
|
|
149048
|
+
`);
|
|
149049
|
+
process.stdout.write(`Projects dir: ${result.projectsDir}
|
|
149050
|
+
`);
|
|
149051
|
+
process.stdout.write(`Total sessions: ${result.totalSessions}
|
|
149052
|
+
`);
|
|
149053
|
+
process.stdout.write(
|
|
149054
|
+
`Total size: ${(result.totalBytes / 1024 / 1024).toFixed(1)} MB
|
|
149055
|
+
`
|
|
149056
|
+
);
|
|
149057
|
+
process.stdout.write(`
|
|
149058
|
+
Tier breakdown:
|
|
149059
|
+
`);
|
|
149060
|
+
process.stdout.write(` HOT (0\u201324h): ${result.hot.length} sessions
|
|
149061
|
+
`);
|
|
149062
|
+
process.stdout.write(` WARM (1\u20137d): ${result.warm.length} sessions
|
|
149063
|
+
`);
|
|
149064
|
+
process.stdout.write(` COLD (>7d): (transcripts deleted; brain.db entries only)
|
|
149065
|
+
`);
|
|
149066
|
+
}
|
|
149067
|
+
} catch (err) {
|
|
149068
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
149069
|
+
const envelope = { success: false, error: { code: "E_INTERNAL", message } };
|
|
149070
|
+
if (opts.json) {
|
|
149071
|
+
process.stdout.write(JSON.stringify(envelope) + "\n");
|
|
149072
|
+
} else {
|
|
149073
|
+
process.stderr.write(`Scan failed: ${message}
|
|
149074
|
+
`);
|
|
149075
|
+
}
|
|
149076
|
+
process.exit(1);
|
|
149077
|
+
}
|
|
149078
|
+
});
|
|
149079
|
+
transcript.command("extract").argument("[session-id]", "Session UUID to extract (omit for --all-warm)").description("Run LLM extraction on a session transcript and write memories to brain.db").option("--all-warm", "Extract all warm-tier sessions (1\u20137d old)").option("--tier <tier>", "LLM tier: warm (default) or cold (Sonnet)", "warm").option("--dry-run", "Report without writing to brain.db or deleting JSONL").option("--projects-dir <path>", "Override ~/.claude/projects/ path").option("--json", "Output result as JSON (LAFS envelope)").action(
|
|
149080
|
+
async (sessionId, opts) => {
|
|
149081
|
+
const tier = opts.tier ?? "warm";
|
|
149082
|
+
const dryRun = opts.dryRun ?? false;
|
|
149083
|
+
const projectRoot = getProjectRoot();
|
|
149084
|
+
try {
|
|
149085
|
+
const { extractTranscript } = await import("@cleocode/core/memory/transcript-extractor.js");
|
|
149086
|
+
const { findSessionTranscriptPath: findSessionTranscriptPath2, listAllTranscripts: listAllTranscripts2 } = await import("@cleocode/core/memory/transcript-scanner.js");
|
|
149087
|
+
const sessions2 = [];
|
|
149088
|
+
if (opts.allWarm) {
|
|
149089
|
+
const allTranscripts = await listAllTranscripts2({ olderThanHours: 1 });
|
|
149090
|
+
for (const t of allTranscripts) {
|
|
149091
|
+
sessions2.push({ sessionId: t.sessionId, path: t.path });
|
|
149092
|
+
}
|
|
149093
|
+
} else if (sessionId) {
|
|
149094
|
+
const path12 = await findSessionTranscriptPath2(sessionId);
|
|
149095
|
+
if (!path12) {
|
|
149096
|
+
const envelope2 = {
|
|
149097
|
+
success: false,
|
|
149098
|
+
error: {
|
|
149099
|
+
code: "E_NOT_FOUND",
|
|
149100
|
+
message: `Session JSONL not found for: ${sessionId}`
|
|
149101
|
+
}
|
|
149102
|
+
};
|
|
149103
|
+
if (opts.json) {
|
|
149104
|
+
process.stdout.write(JSON.stringify(envelope2) + "\n");
|
|
149105
|
+
} else {
|
|
149106
|
+
process.stderr.write(`Session not found: ${sessionId}
|
|
149107
|
+
`);
|
|
149108
|
+
}
|
|
149109
|
+
process.exit(4);
|
|
149110
|
+
return;
|
|
149111
|
+
}
|
|
149112
|
+
sessions2.push({ sessionId, path: path12 });
|
|
149113
|
+
} else {
|
|
149114
|
+
process.stderr.write(
|
|
149115
|
+
"Provide a session-id or use --all-warm to extract all warm sessions\n"
|
|
149116
|
+
);
|
|
149117
|
+
process.exit(2);
|
|
149118
|
+
return;
|
|
149119
|
+
}
|
|
149120
|
+
const results = [];
|
|
149121
|
+
let totalExtracted = 0;
|
|
149122
|
+
let totalStored = 0;
|
|
149123
|
+
let totalBytesFreed = 0;
|
|
149124
|
+
for (const session of sessions2) {
|
|
149125
|
+
const result = await extractTranscript({
|
|
149126
|
+
transcriptPath: session.path,
|
|
149127
|
+
projectRoot,
|
|
149128
|
+
tier,
|
|
149129
|
+
dryRun,
|
|
149130
|
+
sessionId: session.sessionId
|
|
149131
|
+
});
|
|
149132
|
+
results.push(result);
|
|
149133
|
+
totalExtracted += result.extractedCount;
|
|
149134
|
+
totalStored += result.storedCount;
|
|
149135
|
+
totalBytesFreed += result.bytesFreed;
|
|
149136
|
+
}
|
|
149137
|
+
const envelope = {
|
|
149138
|
+
success: true,
|
|
149139
|
+
data: {
|
|
149140
|
+
sessionsProcessed: sessions2.length,
|
|
149141
|
+
memoriesExtracted: totalExtracted,
|
|
149142
|
+
memoriesStored: totalStored,
|
|
149143
|
+
bytesFreed: totalBytesFreed,
|
|
149144
|
+
dryRun,
|
|
149145
|
+
results
|
|
149146
|
+
}
|
|
149147
|
+
};
|
|
149148
|
+
if (opts.json) {
|
|
149149
|
+
process.stdout.write(JSON.stringify(envelope) + "\n");
|
|
149150
|
+
} else {
|
|
149151
|
+
const dryLabel = dryRun ? " (dry run)" : "";
|
|
149152
|
+
process.stdout.write(`Transcript extraction complete${dryLabel}
|
|
149153
|
+
`);
|
|
149154
|
+
process.stdout.write(`Sessions: ${sessions2.length}
|
|
149155
|
+
`);
|
|
149156
|
+
process.stdout.write(`Extracted: ${totalExtracted} memories
|
|
149157
|
+
`);
|
|
149158
|
+
process.stdout.write(`Stored: ${totalStored} memories
|
|
149159
|
+
`);
|
|
149160
|
+
process.stdout.write(`Freed: ${(totalBytesFreed / 1024 / 1024).toFixed(1)} MB
|
|
149161
|
+
`);
|
|
149162
|
+
}
|
|
149163
|
+
} catch (err) {
|
|
149164
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
149165
|
+
const envelope = { success: false, error: { code: "E_INTERNAL", message } };
|
|
149166
|
+
if (opts.json) {
|
|
149167
|
+
process.stdout.write(JSON.stringify(envelope) + "\n");
|
|
149168
|
+
} else {
|
|
149169
|
+
process.stderr.write(`Extract failed: ${message}
|
|
149170
|
+
`);
|
|
149171
|
+
}
|
|
149172
|
+
process.exit(1);
|
|
149173
|
+
}
|
|
149174
|
+
}
|
|
149175
|
+
);
|
|
149176
|
+
transcript.command("migrate").description("Backfill extraction from existing ~/.claude/projects/ session JSONLs (T733)").option("--dry-run", "Report without writing to brain.db or deleting JSONLs").option("--tier <tier>", "LLM tier: warm (default) or cold (Sonnet)", "warm").option("--older-than-hours <hours>", "Only process sessions older than N hours", "24").option("--project-filter <slug>", "Limit to a specific Claude project directory slug").option("--limit <n>", "Maximum number of sessions to process").option("--json", "Output result as JSON (LAFS envelope)").action(
|
|
149177
|
+
async (opts) => {
|
|
149178
|
+
const tier = opts.tier ?? "warm";
|
|
149179
|
+
const dryRun = opts.dryRun ?? false;
|
|
149180
|
+
const olderThanHours = opts.olderThanHours ? Number.parseInt(opts.olderThanHours, 10) : 24;
|
|
149181
|
+
const limit = opts.limit ? Number.parseInt(opts.limit, 10) : void 0;
|
|
149182
|
+
const projectRoot = getProjectRoot();
|
|
149183
|
+
try {
|
|
149184
|
+
const { extractTranscript } = await import("@cleocode/core/memory/transcript-extractor.js");
|
|
149185
|
+
const { listAllTranscripts: listAllTranscripts2 } = await import("@cleocode/core/memory/transcript-scanner.js");
|
|
149186
|
+
const transcripts = await listAllTranscripts2({
|
|
149187
|
+
olderThanHours,
|
|
149188
|
+
projectFilter: opts.projectFilter,
|
|
149189
|
+
limit
|
|
149190
|
+
});
|
|
149191
|
+
if (opts.json) {
|
|
149192
|
+
process.stdout.write(
|
|
149193
|
+
JSON.stringify({
|
|
149194
|
+
success: true,
|
|
149195
|
+
data: { discovered: transcripts.length, message: "Starting migration..." }
|
|
149196
|
+
}) + "\n"
|
|
149197
|
+
);
|
|
149198
|
+
} else {
|
|
149199
|
+
process.stdout.write(
|
|
149200
|
+
`Migrating ${transcripts.length} session transcripts${dryRun ? " (dry run)" : ""}...
|
|
149201
|
+
`
|
|
149202
|
+
);
|
|
149203
|
+
}
|
|
149204
|
+
let memoriesExtracted = 0;
|
|
149205
|
+
let memoriesStored = 0;
|
|
149206
|
+
let bytesFreed = 0;
|
|
149207
|
+
let processed = 0;
|
|
149208
|
+
let skipped = 0;
|
|
149209
|
+
let failed = 0;
|
|
149210
|
+
for (const entry of transcripts) {
|
|
149211
|
+
try {
|
|
149212
|
+
const result = await extractTranscript({
|
|
149213
|
+
transcriptPath: entry.path,
|
|
149214
|
+
projectRoot,
|
|
149215
|
+
tier,
|
|
149216
|
+
dryRun,
|
|
149217
|
+
sessionId: entry.sessionId
|
|
149218
|
+
});
|
|
149219
|
+
if (result.warnings.some((w2) => w2.includes("Already extracted"))) {
|
|
149220
|
+
skipped += 1;
|
|
149221
|
+
} else {
|
|
149222
|
+
processed += 1;
|
|
149223
|
+
memoriesExtracted += result.extractedCount;
|
|
149224
|
+
memoriesStored += result.storedCount;
|
|
149225
|
+
bytesFreed += result.bytesFreed;
|
|
149226
|
+
}
|
|
149227
|
+
} catch {
|
|
149228
|
+
failed += 1;
|
|
149229
|
+
}
|
|
149230
|
+
}
|
|
149231
|
+
const envelope = {
|
|
149232
|
+
success: true,
|
|
149233
|
+
data: {
|
|
149234
|
+
sessionsProcessed: processed,
|
|
149235
|
+
sessionsSkipped: skipped,
|
|
149236
|
+
sessionsFailed: failed,
|
|
149237
|
+
memoriesExtracted,
|
|
149238
|
+
memoriesStored,
|
|
149239
|
+
bytesFreed,
|
|
149240
|
+
mbFreed: parseFloat((bytesFreed / 1024 / 1024).toFixed(2)),
|
|
149241
|
+
dryRun
|
|
149242
|
+
}
|
|
149243
|
+
};
|
|
149244
|
+
if (opts.json) {
|
|
149245
|
+
process.stdout.write(JSON.stringify(envelope) + "\n");
|
|
149246
|
+
} else {
|
|
149247
|
+
const dryLabel = dryRun ? " (dry run \u2014 no changes written)" : "";
|
|
149248
|
+
process.stdout.write(`Migration complete${dryLabel}
|
|
149249
|
+
`);
|
|
149250
|
+
process.stdout.write(
|
|
149251
|
+
`Processed: ${processed} Skipped: ${skipped} Failed: ${failed}
|
|
149252
|
+
`
|
|
149253
|
+
);
|
|
149254
|
+
process.stdout.write(`Memories extracted: ${memoriesExtracted}
|
|
149255
|
+
`);
|
|
149256
|
+
process.stdout.write(`Memories stored: ${memoriesStored}
|
|
149257
|
+
`);
|
|
149258
|
+
process.stdout.write(
|
|
149259
|
+
`Bytes freed: ${(bytesFreed / 1024 / 1024).toFixed(1)} MB
|
|
149260
|
+
`
|
|
149261
|
+
);
|
|
149262
|
+
}
|
|
149263
|
+
} catch (err) {
|
|
149264
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
149265
|
+
const envelope = { success: false, error: { code: "E_INTERNAL", message } };
|
|
149266
|
+
if (opts.json) {
|
|
149267
|
+
process.stdout.write(JSON.stringify(envelope) + "\n");
|
|
149268
|
+
} else {
|
|
149269
|
+
process.stderr.write(`Migration failed: ${message}
|
|
149270
|
+
`);
|
|
149271
|
+
}
|
|
149272
|
+
process.exit(1);
|
|
149273
|
+
}
|
|
149274
|
+
}
|
|
149275
|
+
);
|
|
149276
|
+
transcript.command("prune").description("Prune session transcripts older than the specified duration. Dry-run by default.").requiredOption(
|
|
149277
|
+
"--older-than <duration>",
|
|
149278
|
+
"Delete sessions older than this (e.g. 7d, 24h, 30m)"
|
|
149279
|
+
).option(
|
|
149280
|
+
"--confirm",
|
|
149281
|
+
"Perform actual deletion. Without this flag, the command dry-runs and reports what would be deleted."
|
|
149282
|
+
).option("--projects-dir <path>", "Override ~/.claude/projects/ path").option("--json", "Output result as JSON (LAFS envelope)").action(
|
|
149283
|
+
async (opts) => {
|
|
149284
|
+
const isTTY = Boolean(process.stdin.isTTY && process.stdout.isTTY);
|
|
149285
|
+
const confirm = opts.confirm ?? false;
|
|
149286
|
+
if (!confirm && !isTTY) {
|
|
149287
|
+
const envelope = {
|
|
149288
|
+
success: false,
|
|
149289
|
+
error: {
|
|
149290
|
+
code: "E_INVALID_INPUT",
|
|
149291
|
+
message: "Non-interactive mode requires --confirm flag to perform deletion. Use --confirm to bypass the dry-run. Without --confirm, a dry-run report is printed."
|
|
149292
|
+
}
|
|
149293
|
+
};
|
|
149294
|
+
if (opts.json) {
|
|
149295
|
+
process.stdout.write(JSON.stringify(envelope) + "\n");
|
|
149296
|
+
} else {
|
|
149297
|
+
process.stderr.write(
|
|
149298
|
+
`Deletion requires --confirm in non-interactive mode. Re-run with --confirm to delete.
|
|
149299
|
+
`
|
|
149300
|
+
);
|
|
149301
|
+
}
|
|
149302
|
+
process.exit(0);
|
|
149303
|
+
}
|
|
149304
|
+
let olderThanMs;
|
|
149305
|
+
try {
|
|
149306
|
+
olderThanMs = parseDurationMs(opts.olderThan);
|
|
149307
|
+
} catch (parseErr) {
|
|
149308
|
+
const message = parseErr instanceof Error ? parseErr.message : String(parseErr);
|
|
149309
|
+
const envelope = { success: false, error: { code: "E_INVALID_INPUT", message } };
|
|
149310
|
+
if (opts.json) {
|
|
149311
|
+
process.stdout.write(JSON.stringify(envelope) + "\n");
|
|
149312
|
+
} else {
|
|
149313
|
+
process.stderr.write(`Invalid duration: ${message}
|
|
149314
|
+
`);
|
|
149315
|
+
}
|
|
149316
|
+
process.exit(2);
|
|
149317
|
+
return;
|
|
149318
|
+
}
|
|
149319
|
+
const projectsDir = opts.projectsDir ?? join141(homedir14(), ".claude", "projects");
|
|
149320
|
+
try {
|
|
149321
|
+
const pruneResult = await pruneTranscripts({
|
|
149322
|
+
olderThanMs,
|
|
149323
|
+
confirm,
|
|
149324
|
+
projectsDir
|
|
149325
|
+
});
|
|
149326
|
+
const envelope = {
|
|
149327
|
+
success: true,
|
|
149328
|
+
data: {
|
|
149329
|
+
pruned: pruneResult.pruned,
|
|
149330
|
+
bytesFreed: pruneResult.bytesFreed,
|
|
149331
|
+
mbFreed: parseFloat((pruneResult.bytesFreed / 1024 / 1024).toFixed(2)),
|
|
149332
|
+
dryRun: pruneResult.dryRun,
|
|
149333
|
+
deletedPaths: pruneResult.deletedPaths
|
|
149334
|
+
}
|
|
149335
|
+
};
|
|
149336
|
+
if (opts.json) {
|
|
149337
|
+
process.stdout.write(JSON.stringify(envelope) + "\n");
|
|
149338
|
+
} else {
|
|
149339
|
+
const dryLabel = pruneResult.dryRun ? " (dry run \u2014 use --confirm to delete)" : "";
|
|
149340
|
+
process.stdout.write(`Transcript prune${dryLabel}
|
|
149341
|
+
`);
|
|
149342
|
+
process.stdout.write(`Older than: ${opts.olderThan}
|
|
149343
|
+
`);
|
|
149344
|
+
process.stdout.write(`Sessions: ${pruneResult.pruned}
|
|
149345
|
+
`);
|
|
149346
|
+
process.stdout.write(
|
|
149347
|
+
`Bytes freed: ${(pruneResult.bytesFreed / 1024 / 1024).toFixed(1)} MB
|
|
149348
|
+
`
|
|
149349
|
+
);
|
|
149350
|
+
if (pruneResult.deletedPaths.length > 0 && pruneResult.dryRun) {
|
|
149351
|
+
process.stdout.write(`
|
|
149352
|
+
Would delete (${pruneResult.deletedPaths.length} paths):
|
|
149353
|
+
`);
|
|
149354
|
+
for (const p2 of pruneResult.deletedPaths.slice(0, 10)) {
|
|
149355
|
+
process.stdout.write(` ${p2}
|
|
149356
|
+
`);
|
|
149357
|
+
}
|
|
149358
|
+
if (pruneResult.deletedPaths.length > 10) {
|
|
149359
|
+
process.stdout.write(` ... and ${pruneResult.deletedPaths.length - 10} more
|
|
149360
|
+
`);
|
|
149361
|
+
}
|
|
149362
|
+
}
|
|
149363
|
+
}
|
|
149364
|
+
} catch (err) {
|
|
149365
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
149366
|
+
const envelope = { success: false, error: { code: "E_INTERNAL", message } };
|
|
149367
|
+
if (opts.json) {
|
|
149368
|
+
process.stdout.write(JSON.stringify(envelope) + "\n");
|
|
149369
|
+
} else {
|
|
149370
|
+
process.stderr.write(`Prune failed: ${message}
|
|
149371
|
+
`);
|
|
149372
|
+
}
|
|
149373
|
+
process.exit(1);
|
|
149374
|
+
}
|
|
149375
|
+
}
|
|
149376
|
+
);
|
|
149377
|
+
}
|
|
149378
|
+
|
|
149475
149379
|
// packages/cleo/src/cli/commands/update.ts
|
|
149476
149380
|
init_cli();
|
|
149477
149381
|
function registerUpdateCommand(program) {
|
|
@@ -149621,18 +149525,18 @@ function registerVerifyCommand(program) {
|
|
|
149621
149525
|
init_src();
|
|
149622
149526
|
init_src3();
|
|
149623
149527
|
init_renderers();
|
|
149624
|
-
import { execFileSync as execFileSync18, spawn as
|
|
149625
|
-
import { mkdir as
|
|
149626
|
-
import { join as
|
|
149528
|
+
import { execFileSync as execFileSync18, spawn as spawn3 } from "node:child_process";
|
|
149529
|
+
import { mkdir as mkdir21, open, readFile as readFile25, rm as rm4, stat as stat6, writeFile as writeFile15 } from "node:fs/promises";
|
|
149530
|
+
import { join as join142 } from "node:path";
|
|
149627
149531
|
var DEFAULT_PORT = 3456;
|
|
149628
149532
|
var DEFAULT_HOST = "127.0.0.1";
|
|
149629
149533
|
function getWebPaths() {
|
|
149630
149534
|
const cleoHome = getCleoHome();
|
|
149631
149535
|
return {
|
|
149632
|
-
pidFile:
|
|
149633
|
-
configFile:
|
|
149634
|
-
logDir:
|
|
149635
|
-
logFile:
|
|
149536
|
+
pidFile: join142(cleoHome, "web-server.pid"),
|
|
149537
|
+
configFile: join142(cleoHome, "web-server.json"),
|
|
149538
|
+
logDir: join142(cleoHome, "logs"),
|
|
149539
|
+
logFile: join142(cleoHome, "logs", "web-server.log")
|
|
149636
149540
|
};
|
|
149637
149541
|
}
|
|
149638
149542
|
function isProcessRunning(pid) {
|
|
@@ -149646,7 +149550,7 @@ function isProcessRunning(pid) {
|
|
|
149646
149550
|
async function getStatus() {
|
|
149647
149551
|
const { pidFile, configFile } = getWebPaths();
|
|
149648
149552
|
try {
|
|
149649
|
-
const pidStr = (await
|
|
149553
|
+
const pidStr = (await readFile25(pidFile, "utf-8")).trim();
|
|
149650
149554
|
const pid = parseInt(pidStr, 10);
|
|
149651
149555
|
if (Number.isNaN(pid) || !isProcessRunning(pid)) {
|
|
149652
149556
|
return { running: false, pid: null, port: null, host: null, url: null };
|
|
@@ -149654,7 +149558,7 @@ async function getStatus() {
|
|
|
149654
149558
|
let port = DEFAULT_PORT;
|
|
149655
149559
|
let host = DEFAULT_HOST;
|
|
149656
149560
|
try {
|
|
149657
|
-
const config2 = JSON.parse(await
|
|
149561
|
+
const config2 = JSON.parse(await readFile25(configFile, "utf-8"));
|
|
149658
149562
|
port = config2.port ?? DEFAULT_PORT;
|
|
149659
149563
|
host = config2.host ?? DEFAULT_HOST;
|
|
149660
149564
|
} catch {
|
|
@@ -149671,9 +149575,9 @@ async function startWebServer(port, host) {
|
|
|
149671
149575
|
throw new CleoError(1 /* GENERAL_ERROR */, `Server already running (PID: ${status.pid})`);
|
|
149672
149576
|
}
|
|
149673
149577
|
const projectRoot = process.env["CLEO_ROOT"] ?? process.cwd();
|
|
149674
|
-
const studioDir = process.env["CLEO_STUDIO_DIR"] ??
|
|
149675
|
-
await
|
|
149676
|
-
await
|
|
149578
|
+
const studioDir = process.env["CLEO_STUDIO_DIR"] ?? join142(projectRoot, "packages", "studio", "build");
|
|
149579
|
+
await mkdir21(logDir, { recursive: true });
|
|
149580
|
+
await writeFile15(
|
|
149677
149581
|
configFile,
|
|
149678
149582
|
JSON.stringify({
|
|
149679
149583
|
port,
|
|
@@ -149681,9 +149585,9 @@ async function startWebServer(port, host) {
|
|
|
149681
149585
|
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
149682
149586
|
})
|
|
149683
149587
|
);
|
|
149684
|
-
const webIndexPath =
|
|
149588
|
+
const webIndexPath = join142(studioDir, "index.js");
|
|
149685
149589
|
try {
|
|
149686
|
-
await
|
|
149590
|
+
await stat6(webIndexPath);
|
|
149687
149591
|
} catch {
|
|
149688
149592
|
try {
|
|
149689
149593
|
execFileSync18("pnpm", ["--filter", "@cleocode/studio", "run", "build"], {
|
|
@@ -149699,7 +149603,7 @@ Logs: ${logFile}`
|
|
|
149699
149603
|
}
|
|
149700
149604
|
}
|
|
149701
149605
|
const logFileHandle = await open(logFile, "a");
|
|
149702
|
-
const serverProcess =
|
|
149606
|
+
const serverProcess = spawn3("node", [webIndexPath], {
|
|
149703
149607
|
cwd: studioDir,
|
|
149704
149608
|
env: {
|
|
149705
149609
|
...process.env,
|
|
@@ -149713,10 +149617,10 @@ Logs: ${logFile}`
|
|
|
149713
149617
|
});
|
|
149714
149618
|
serverProcess.unref();
|
|
149715
149619
|
const pidFileTmp = `${pidFile}.tmp`;
|
|
149716
|
-
await
|
|
149717
|
-
await
|
|
149718
|
-
await
|
|
149719
|
-
await
|
|
149620
|
+
await writeFile15(pidFileTmp, String(serverProcess.pid));
|
|
149621
|
+
await rm4(pidFile, { force: true });
|
|
149622
|
+
await writeFile15(pidFile, String(serverProcess.pid));
|
|
149623
|
+
await rm4(pidFileTmp, { force: true });
|
|
149720
149624
|
await logFileHandle.close();
|
|
149721
149625
|
const maxAttempts = 30;
|
|
149722
149626
|
let started = false;
|
|
@@ -149736,7 +149640,7 @@ Logs: ${logFile}`
|
|
|
149736
149640
|
process.kill(serverProcess.pid);
|
|
149737
149641
|
} catch {
|
|
149738
149642
|
}
|
|
149739
|
-
await
|
|
149643
|
+
await rm4(pidFile, { force: true });
|
|
149740
149644
|
throw new CleoError(1 /* GENERAL_ERROR */, "Server failed to start within 15 seconds");
|
|
149741
149645
|
}
|
|
149742
149646
|
cliOutput(
|
|
@@ -149770,13 +149674,13 @@ function registerWebCommand(program) {
|
|
|
149770
149674
|
const { pidFile } = getWebPaths();
|
|
149771
149675
|
const status = await getStatus();
|
|
149772
149676
|
if (!status.running || !status.pid) {
|
|
149773
|
-
await
|
|
149677
|
+
await rm4(pidFile, { force: true });
|
|
149774
149678
|
cliOutput({ running: false }, { command: "web", message: "Server is not running" });
|
|
149775
149679
|
return;
|
|
149776
149680
|
}
|
|
149777
149681
|
try {
|
|
149778
149682
|
if (process.platform === "win32") {
|
|
149779
|
-
|
|
149683
|
+
spawn3("taskkill", ["/PID", String(status.pid), "/T"], { stdio: "ignore" });
|
|
149780
149684
|
} else {
|
|
149781
149685
|
process.kill(status.pid, "SIGTERM");
|
|
149782
149686
|
}
|
|
@@ -149789,14 +149693,14 @@ function registerWebCommand(program) {
|
|
|
149789
149693
|
if (isProcessRunning(status.pid)) {
|
|
149790
149694
|
try {
|
|
149791
149695
|
if (process.platform === "win32") {
|
|
149792
|
-
|
|
149696
|
+
spawn3("taskkill", ["/PID", String(status.pid), "/F", "/T"], { stdio: "ignore" });
|
|
149793
149697
|
} else {
|
|
149794
149698
|
process.kill(status.pid, "SIGKILL");
|
|
149795
149699
|
}
|
|
149796
149700
|
} catch {
|
|
149797
149701
|
}
|
|
149798
149702
|
}
|
|
149799
|
-
await
|
|
149703
|
+
await rm4(pidFile, { force: true });
|
|
149800
149704
|
cliOutput({ stopped: true }, { command: "web", message: "CLEO Web UI stopped" });
|
|
149801
149705
|
} catch (err) {
|
|
149802
149706
|
if (err instanceof CleoError) {
|
|
@@ -149813,7 +149717,7 @@ function registerWebCommand(program) {
|
|
|
149813
149717
|
if (status.running && status.pid) {
|
|
149814
149718
|
try {
|
|
149815
149719
|
if (process.platform === "win32") {
|
|
149816
|
-
|
|
149720
|
+
spawn3("taskkill", ["/PID", String(status.pid), "/T"], { stdio: "ignore" });
|
|
149817
149721
|
} else {
|
|
149818
149722
|
process.kill(status.pid, "SIGTERM");
|
|
149819
149723
|
}
|
|
@@ -149826,14 +149730,14 @@ function registerWebCommand(program) {
|
|
|
149826
149730
|
if (isProcessRunning(status.pid)) {
|
|
149827
149731
|
try {
|
|
149828
149732
|
if (process.platform === "win32") {
|
|
149829
|
-
|
|
149733
|
+
spawn3("taskkill", ["/PID", String(status.pid), "/F", "/T"], { stdio: "ignore" });
|
|
149830
149734
|
} else {
|
|
149831
149735
|
process.kill(status.pid, "SIGKILL");
|
|
149832
149736
|
}
|
|
149833
149737
|
} catch {
|
|
149834
149738
|
}
|
|
149835
149739
|
}
|
|
149836
|
-
await
|
|
149740
|
+
await rm4(pidFile, { force: true });
|
|
149837
149741
|
}
|
|
149838
149742
|
const port = parseInt(opts["port"] ?? String(DEFAULT_PORT), 10);
|
|
149839
149743
|
const host = opts["host"] ?? DEFAULT_HOST;
|
|
@@ -149871,11 +149775,11 @@ function registerWebCommand(program) {
|
|
|
149871
149775
|
const platform6 = process.platform;
|
|
149872
149776
|
try {
|
|
149873
149777
|
if (platform6 === "linux") {
|
|
149874
|
-
|
|
149778
|
+
spawn3("xdg-open", [url2], { detached: true, stdio: "ignore" }).unref();
|
|
149875
149779
|
} else if (platform6 === "darwin") {
|
|
149876
|
-
|
|
149780
|
+
spawn3("open", [url2], { detached: true, stdio: "ignore" }).unref();
|
|
149877
149781
|
} else if (platform6 === "win32") {
|
|
149878
|
-
|
|
149782
|
+
spawn3("cmd", ["/c", "start", "", url2], { detached: true, stdio: "ignore" }).unref();
|
|
149879
149783
|
}
|
|
149880
149784
|
} catch {
|
|
149881
149785
|
}
|
|
@@ -149890,1239 +149794,6 @@ function registerWebCommand(program) {
|
|
|
149890
149794
|
});
|
|
149891
149795
|
}
|
|
149892
149796
|
|
|
149893
|
-
// packages/cleo/src/cli/commands/daemon.ts
|
|
149894
|
-
import { homedir as homedir11 } from "node:os";
|
|
149895
|
-
import { join as join139 } from "node:path";
|
|
149896
|
-
|
|
149897
|
-
// packages/cleo/src/gc/daemon.ts
|
|
149898
|
-
var import_node_cron = __toESM(require_node_cron(), 1);
|
|
149899
|
-
import { spawn as spawn3 } from "node:child_process";
|
|
149900
|
-
import { createWriteStream as createWriteStream2 } from "node:fs";
|
|
149901
|
-
import { mkdir as mkdir21 } from "node:fs/promises";
|
|
149902
|
-
import { join as join138 } from "node:path";
|
|
149903
|
-
import { fileURLToPath as fileURLToPath8 } from "node:url";
|
|
149904
|
-
|
|
149905
|
-
// packages/cleo/src/gc/runner.ts
|
|
149906
|
-
import { lstat as lstat2, readdir as readdir5, rm as rm4, stat as stat5 } from "node:fs/promises";
|
|
149907
|
-
import { homedir as homedir10 } from "node:os";
|
|
149908
|
-
import { join as join137 } from "node:path";
|
|
149909
|
-
|
|
149910
|
-
// node_modules/.pnpm/check-disk-space@3.4.0/node_modules/check-disk-space/dist/check-disk-space.mjs
|
|
149911
|
-
import { execFile as execFile8 } from "node:child_process";
|
|
149912
|
-
import { access as access4 } from "node:fs/promises";
|
|
149913
|
-
import { release as release2 } from "node:os";
|
|
149914
|
-
import { normalize as normalize2, sep as sep2 } from "node:path";
|
|
149915
|
-
import { platform as platform5 } from "node:process";
|
|
149916
|
-
import { promisify as promisify8 } from "node:util";
|
|
149917
|
-
var InvalidPathError = class _InvalidPathError extends Error {
|
|
149918
|
-
constructor(message) {
|
|
149919
|
-
super(message);
|
|
149920
|
-
this.name = "InvalidPathError";
|
|
149921
|
-
Object.setPrototypeOf(this, _InvalidPathError.prototype);
|
|
149922
|
-
}
|
|
149923
|
-
};
|
|
149924
|
-
var NoMatchError = class _NoMatchError extends Error {
|
|
149925
|
-
constructor(message) {
|
|
149926
|
-
super(message);
|
|
149927
|
-
this.name = "NoMatchError";
|
|
149928
|
-
Object.setPrototypeOf(this, _NoMatchError.prototype);
|
|
149929
|
-
}
|
|
149930
|
-
};
|
|
149931
|
-
async function isDirectoryExisting(directoryPath, dependencies) {
|
|
149932
|
-
try {
|
|
149933
|
-
await dependencies.fsAccess(directoryPath);
|
|
149934
|
-
return Promise.resolve(true);
|
|
149935
|
-
} catch (error48) {
|
|
149936
|
-
return Promise.resolve(false);
|
|
149937
|
-
}
|
|
149938
|
-
}
|
|
149939
|
-
async function getFirstExistingParentPath(directoryPath, dependencies) {
|
|
149940
|
-
let parentDirectoryPath = directoryPath;
|
|
149941
|
-
let parentDirectoryFound = await isDirectoryExisting(parentDirectoryPath, dependencies);
|
|
149942
|
-
while (!parentDirectoryFound) {
|
|
149943
|
-
parentDirectoryPath = dependencies.pathNormalize(parentDirectoryPath + "/..");
|
|
149944
|
-
parentDirectoryFound = await isDirectoryExisting(parentDirectoryPath, dependencies);
|
|
149945
|
-
}
|
|
149946
|
-
return parentDirectoryPath;
|
|
149947
|
-
}
|
|
149948
|
-
async function hasPowerShell3(dependencies) {
|
|
149949
|
-
const major = parseInt(dependencies.release.split(".")[0], 10);
|
|
149950
|
-
if (major <= 6) {
|
|
149951
|
-
return false;
|
|
149952
|
-
}
|
|
149953
|
-
try {
|
|
149954
|
-
await dependencies.cpExecFile("where", ["powershell"], { windowsHide: true });
|
|
149955
|
-
return true;
|
|
149956
|
-
} catch (error48) {
|
|
149957
|
-
return false;
|
|
149958
|
-
}
|
|
149959
|
-
}
|
|
149960
|
-
function checkDiskSpace(directoryPath, dependencies = {
|
|
149961
|
-
platform: platform5,
|
|
149962
|
-
release: release2(),
|
|
149963
|
-
fsAccess: access4,
|
|
149964
|
-
pathNormalize: normalize2,
|
|
149965
|
-
pathSep: sep2,
|
|
149966
|
-
cpExecFile: promisify8(execFile8)
|
|
149967
|
-
}) {
|
|
149968
|
-
function mapOutput(stdout2, filter, mapping, coefficient) {
|
|
149969
|
-
const parsed = stdout2.split("\n").map((line2) => line2.trim()).filter((line2) => line2.length !== 0).slice(1).map((line2) => line2.split(/\s+(?=[\d/])/));
|
|
149970
|
-
const filtered = parsed.filter(filter);
|
|
149971
|
-
if (filtered.length === 0) {
|
|
149972
|
-
throw new NoMatchError();
|
|
149973
|
-
}
|
|
149974
|
-
const diskData = filtered[0];
|
|
149975
|
-
return {
|
|
149976
|
-
diskPath: diskData[mapping.diskPath],
|
|
149977
|
-
free: parseInt(diskData[mapping.free], 10) * coefficient,
|
|
149978
|
-
size: parseInt(diskData[mapping.size], 10) * coefficient
|
|
149979
|
-
};
|
|
149980
|
-
}
|
|
149981
|
-
async function check2(cmd, filter, mapping, coefficient = 1) {
|
|
149982
|
-
const [file2, ...args] = cmd;
|
|
149983
|
-
if (file2 === void 0) {
|
|
149984
|
-
return Promise.reject(new Error("cmd must contain at least one item"));
|
|
149985
|
-
}
|
|
149986
|
-
try {
|
|
149987
|
-
const { stdout: stdout2 } = await dependencies.cpExecFile(file2, args, { windowsHide: true });
|
|
149988
|
-
return mapOutput(stdout2, filter, mapping, coefficient);
|
|
149989
|
-
} catch (error48) {
|
|
149990
|
-
return Promise.reject(error48);
|
|
149991
|
-
}
|
|
149992
|
-
}
|
|
149993
|
-
async function checkWin32(directoryPath2) {
|
|
149994
|
-
if (directoryPath2.charAt(1) !== ":") {
|
|
149995
|
-
return Promise.reject(new InvalidPathError(`The following path is invalid (should be X:\\...): ${directoryPath2}`));
|
|
149996
|
-
}
|
|
149997
|
-
const powershellCmd = [
|
|
149998
|
-
"powershell",
|
|
149999
|
-
"Get-CimInstance -ClassName Win32_LogicalDisk | Select-Object Caption, FreeSpace, Size"
|
|
150000
|
-
];
|
|
150001
|
-
const wmicCmd = [
|
|
150002
|
-
"wmic",
|
|
150003
|
-
"logicaldisk",
|
|
150004
|
-
"get",
|
|
150005
|
-
"size,freespace,caption"
|
|
150006
|
-
];
|
|
150007
|
-
const cmd = await hasPowerShell3(dependencies) ? powershellCmd : wmicCmd;
|
|
150008
|
-
return check2(cmd, (driveData) => {
|
|
150009
|
-
const driveLetter = driveData[0];
|
|
150010
|
-
return directoryPath2.toUpperCase().startsWith(driveLetter.toUpperCase());
|
|
150011
|
-
}, {
|
|
150012
|
-
diskPath: 0,
|
|
150013
|
-
free: 1,
|
|
150014
|
-
size: 2
|
|
150015
|
-
});
|
|
150016
|
-
}
|
|
150017
|
-
async function checkUnix(directoryPath2) {
|
|
150018
|
-
if (!dependencies.pathNormalize(directoryPath2).startsWith(dependencies.pathSep)) {
|
|
150019
|
-
return Promise.reject(new InvalidPathError(`The following path is invalid (should start by ${dependencies.pathSep}): ${directoryPath2}`));
|
|
150020
|
-
}
|
|
150021
|
-
const pathToCheck = await getFirstExistingParentPath(directoryPath2, dependencies);
|
|
150022
|
-
return check2(
|
|
150023
|
-
[
|
|
150024
|
-
"df",
|
|
150025
|
-
"-Pk",
|
|
150026
|
-
"--",
|
|
150027
|
-
pathToCheck
|
|
150028
|
-
],
|
|
150029
|
-
() => true,
|
|
150030
|
-
// We should only get one line, so we did not need to filter
|
|
150031
|
-
{
|
|
150032
|
-
diskPath: 5,
|
|
150033
|
-
free: 3,
|
|
150034
|
-
size: 1
|
|
150035
|
-
},
|
|
150036
|
-
1024
|
|
150037
|
-
);
|
|
150038
|
-
}
|
|
150039
|
-
if (dependencies.platform === "win32") {
|
|
150040
|
-
return checkWin32(directoryPath);
|
|
150041
|
-
}
|
|
150042
|
-
return checkUnix(directoryPath);
|
|
150043
|
-
}
|
|
150044
|
-
|
|
150045
|
-
// packages/cleo/src/gc/state.ts
|
|
150046
|
-
import { mkdir as mkdir20, readFile as readFile25, rename as rename2, writeFile as writeFile15 } from "node:fs/promises";
|
|
150047
|
-
import { dirname as dirname31, join as join136 } from "node:path";
|
|
150048
|
-
var GC_STATE_SCHEMA_VERSION = "1.0";
|
|
150049
|
-
var DEFAULT_GC_STATE = {
|
|
150050
|
-
schemaVersion: GC_STATE_SCHEMA_VERSION,
|
|
150051
|
-
lastRunAt: null,
|
|
150052
|
-
lastRunResult: null,
|
|
150053
|
-
lastRunBytesFreed: 0,
|
|
150054
|
-
pendingPrune: null,
|
|
150055
|
-
consecutiveFailures: 0,
|
|
150056
|
-
diskThresholdBreached: false,
|
|
150057
|
-
lastDiskUsedPct: null,
|
|
150058
|
-
escalationNeeded: false,
|
|
150059
|
-
escalationReason: null,
|
|
150060
|
-
daemonPid: null,
|
|
150061
|
-
daemonStartedAt: null
|
|
150062
|
-
};
|
|
150063
|
-
async function readGCState(statePath) {
|
|
150064
|
-
try {
|
|
150065
|
-
const raw = await readFile25(statePath, "utf-8");
|
|
150066
|
-
const parsed = JSON.parse(raw);
|
|
150067
|
-
return { ...DEFAULT_GC_STATE, ...parsed };
|
|
150068
|
-
} catch {
|
|
150069
|
-
return { ...DEFAULT_GC_STATE };
|
|
150070
|
-
}
|
|
150071
|
-
}
|
|
150072
|
-
async function writeGCState(statePath, state) {
|
|
150073
|
-
const dir = dirname31(statePath);
|
|
150074
|
-
await mkdir20(dir, { recursive: true });
|
|
150075
|
-
const tmpPath = join136(dir, `.gc-state-${process.pid}.tmp`);
|
|
150076
|
-
const json3 = JSON.stringify(state, null, 2);
|
|
150077
|
-
await writeFile15(tmpPath, json3, "utf-8");
|
|
150078
|
-
await rename2(tmpPath, statePath);
|
|
150079
|
-
}
|
|
150080
|
-
async function patchGCState(statePath, patch) {
|
|
150081
|
-
const current = await readGCState(statePath);
|
|
150082
|
-
const updated = { ...current, ...patch };
|
|
150083
|
-
await writeGCState(statePath, updated);
|
|
150084
|
-
return updated;
|
|
150085
|
-
}
|
|
150086
|
-
|
|
150087
|
-
// packages/cleo/src/gc/runner.ts
|
|
150088
|
-
var DISK_THRESHOLDS = {
|
|
150089
|
-
WATCH: 70,
|
|
150090
|
-
WARN: 85,
|
|
150091
|
-
URGENT: 90,
|
|
150092
|
-
EMERGENCY: 95
|
|
150093
|
-
};
|
|
150094
|
-
function classifyDiskTier(pct) {
|
|
150095
|
-
if (pct >= DISK_THRESHOLDS.EMERGENCY) return "emergency";
|
|
150096
|
-
if (pct >= DISK_THRESHOLDS.URGENT) return "urgent";
|
|
150097
|
-
if (pct >= DISK_THRESHOLDS.WARN) return "warn";
|
|
150098
|
-
if (pct >= DISK_THRESHOLDS.WATCH) return "watch";
|
|
150099
|
-
return "ok";
|
|
150100
|
-
}
|
|
150101
|
-
function retentionMs(tier) {
|
|
150102
|
-
switch (tier) {
|
|
150103
|
-
case "emergency":
|
|
150104
|
-
return 1 * 24 * 60 * 60 * 1e3;
|
|
150105
|
-
// 1 day
|
|
150106
|
-
case "urgent":
|
|
150107
|
-
return 3 * 24 * 60 * 60 * 1e3;
|
|
150108
|
-
// 3 days
|
|
150109
|
-
case "warn":
|
|
150110
|
-
return 7 * 24 * 60 * 60 * 1e3;
|
|
150111
|
-
// 7 days
|
|
150112
|
-
default:
|
|
150113
|
-
return 30 * 24 * 60 * 60 * 1e3;
|
|
150114
|
-
}
|
|
150115
|
-
}
|
|
150116
|
-
async function getPathBytes(targetPath) {
|
|
150117
|
-
try {
|
|
150118
|
-
const info = await lstat2(targetPath);
|
|
150119
|
-
if (info.isFile()) return info.size;
|
|
150120
|
-
if (!info.isDirectory()) return 0;
|
|
150121
|
-
const entries = await readdir5(targetPath, { withFileTypes: true });
|
|
150122
|
-
let total = 0;
|
|
150123
|
-
for (const entry of entries) {
|
|
150124
|
-
total += await getPathBytes(join137(targetPath, entry.name));
|
|
150125
|
-
}
|
|
150126
|
-
return total;
|
|
150127
|
-
} catch {
|
|
150128
|
-
return 0;
|
|
150129
|
-
}
|
|
150130
|
-
}
|
|
150131
|
-
async function idempotentRm(targetPath) {
|
|
150132
|
-
try {
|
|
150133
|
-
await rm4(targetPath, { recursive: true, force: true });
|
|
150134
|
-
} catch (err) {
|
|
150135
|
-
const nodeErr = err;
|
|
150136
|
-
if (nodeErr.code === "ENOENT") return;
|
|
150137
|
-
throw err;
|
|
150138
|
-
}
|
|
150139
|
-
}
|
|
150140
|
-
async function gatherPruneCandidates(maxAgeMs, projectsDir) {
|
|
150141
|
-
const resolvedProjectsDir = projectsDir ?? join137(homedir10(), ".claude", "projects");
|
|
150142
|
-
const candidates = [];
|
|
150143
|
-
const now2 = Date.now();
|
|
150144
|
-
let projectSlugs;
|
|
150145
|
-
try {
|
|
150146
|
-
const entries = await readdir5(resolvedProjectsDir, { withFileTypes: true });
|
|
150147
|
-
projectSlugs = entries.filter((e) => e.isDirectory()).map((e) => e.name);
|
|
150148
|
-
} catch {
|
|
150149
|
-
return candidates;
|
|
150150
|
-
}
|
|
150151
|
-
for (const slug of projectSlugs) {
|
|
150152
|
-
const slugDir = join137(resolvedProjectsDir, slug);
|
|
150153
|
-
let slugEntries;
|
|
150154
|
-
try {
|
|
150155
|
-
slugEntries = await readdir5(slugDir, { withFileTypes: true });
|
|
150156
|
-
} catch {
|
|
150157
|
-
continue;
|
|
150158
|
-
}
|
|
150159
|
-
for (const entry of slugEntries) {
|
|
150160
|
-
const entryPath = join137(slugDir, entry.name);
|
|
150161
|
-
if (entry.isFile() && entry.name.endsWith(".jsonl")) {
|
|
150162
|
-
try {
|
|
150163
|
-
const info = await stat5(entryPath);
|
|
150164
|
-
const ageMs = now2 - info.mtimeMs;
|
|
150165
|
-
if (ageMs > maxAgeMs) {
|
|
150166
|
-
candidates.push(entryPath);
|
|
150167
|
-
}
|
|
150168
|
-
} catch {
|
|
150169
|
-
}
|
|
150170
|
-
} else if (entry.isDirectory()) {
|
|
150171
|
-
try {
|
|
150172
|
-
const info = await stat5(entryPath);
|
|
150173
|
-
const ageMs = now2 - info.mtimeMs;
|
|
150174
|
-
if (ageMs > maxAgeMs) {
|
|
150175
|
-
candidates.push(entryPath);
|
|
150176
|
-
}
|
|
150177
|
-
} catch {
|
|
150178
|
-
}
|
|
150179
|
-
}
|
|
150180
|
-
}
|
|
150181
|
-
}
|
|
150182
|
-
return candidates;
|
|
150183
|
-
}
|
|
150184
|
-
async function runGC(opts = {}) {
|
|
150185
|
-
const cleoDir = opts.cleoDir ?? join137(homedir10(), ".cleo");
|
|
150186
|
-
const statePath = join137(cleoDir, "gc-state.json");
|
|
150187
|
-
const dryRun = opts.dryRun ?? false;
|
|
150188
|
-
const projectsDir = opts.projectsDir;
|
|
150189
|
-
const initialState = await readGCState(statePath);
|
|
150190
|
-
const resumePaths = opts.resumeFrom ?? initialState.pendingPrune ?? [];
|
|
150191
|
-
let diskUsedPct = 0;
|
|
150192
|
-
try {
|
|
150193
|
-
const { free, size } = await checkDiskSpace(cleoDir);
|
|
150194
|
-
diskUsedPct = size > 0 ? (size - free) / size * 100 : 0;
|
|
150195
|
-
} catch {
|
|
150196
|
-
diskUsedPct = 0;
|
|
150197
|
-
}
|
|
150198
|
-
const tier = classifyDiskTier(diskUsedPct);
|
|
150199
|
-
const maxAgeMs = retentionMs(tier);
|
|
150200
|
-
const candidatesFromScan = resumePaths.length > 0 ? resumePaths : await gatherPruneCandidates(maxAgeMs, projectsDir);
|
|
150201
|
-
if (!dryRun && candidatesFromScan.length > 0) {
|
|
150202
|
-
await patchGCState(statePath, { pendingPrune: candidatesFromScan });
|
|
150203
|
-
}
|
|
150204
|
-
const pruned = [];
|
|
150205
|
-
let bytesFreed = 0;
|
|
150206
|
-
const remaining = [...candidatesFromScan];
|
|
150207
|
-
for (const candidatePath of candidatesFromScan) {
|
|
150208
|
-
const bytes = await getPathBytes(candidatePath);
|
|
150209
|
-
if (dryRun) {
|
|
150210
|
-
pruned.push({ path: candidatePath, bytes });
|
|
150211
|
-
bytesFreed += bytes;
|
|
150212
|
-
continue;
|
|
150213
|
-
}
|
|
150214
|
-
try {
|
|
150215
|
-
await idempotentRm(candidatePath);
|
|
150216
|
-
pruned.push({ path: candidatePath, bytes });
|
|
150217
|
-
bytesFreed += bytes;
|
|
150218
|
-
const idx = remaining.indexOf(candidatePath);
|
|
150219
|
-
if (idx !== -1) remaining.splice(idx, 1);
|
|
150220
|
-
await patchGCState(statePath, {
|
|
150221
|
-
pendingPrune: remaining.length > 0 ? remaining : null
|
|
150222
|
-
});
|
|
150223
|
-
} catch {
|
|
150224
|
-
}
|
|
150225
|
-
}
|
|
150226
|
-
const escalationSet = tier === "warn" || tier === "urgent" || tier === "emergency";
|
|
150227
|
-
let escalationReason = null;
|
|
150228
|
-
if (escalationSet) {
|
|
150229
|
-
escalationReason = `Disk at ${diskUsedPct.toFixed(1)}% (${tier.toUpperCase()}): ${pruned.length} paths pruned, ${bytesFreed} bytes freed`;
|
|
150230
|
-
}
|
|
150231
|
-
const completedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
150232
|
-
if (!dryRun) {
|
|
150233
|
-
await patchGCState(statePath, {
|
|
150234
|
-
lastRunAt: completedAt,
|
|
150235
|
-
lastRunResult: remaining.length === 0 ? "success" : "partial",
|
|
150236
|
-
lastRunBytesFreed: bytesFreed,
|
|
150237
|
-
pendingPrune: remaining.length > 0 ? remaining : null,
|
|
150238
|
-
consecutiveFailures: remaining.length > 0 ? initialState.consecutiveFailures + 1 : 0,
|
|
150239
|
-
diskThresholdBreached: diskUsedPct >= DISK_THRESHOLDS.WATCH,
|
|
150240
|
-
lastDiskUsedPct: diskUsedPct,
|
|
150241
|
-
escalationNeeded: escalationSet || initialState.escalationNeeded,
|
|
150242
|
-
escalationReason: escalationReason ?? initialState.escalationReason
|
|
150243
|
-
});
|
|
150244
|
-
}
|
|
150245
|
-
return {
|
|
150246
|
-
diskUsedPct,
|
|
150247
|
-
threshold: tier,
|
|
150248
|
-
pruned,
|
|
150249
|
-
bytesFreed,
|
|
150250
|
-
escalationSet,
|
|
150251
|
-
escalationReason,
|
|
150252
|
-
completedAt
|
|
150253
|
-
};
|
|
150254
|
-
}
|
|
150255
|
-
|
|
150256
|
-
// packages/cleo/src/gc/daemon.ts
|
|
150257
|
-
var GC_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
150258
|
-
async function spawnGCDaemon(cleoDir) {
|
|
150259
|
-
const logsDir = join138(cleoDir, "logs");
|
|
150260
|
-
await mkdir21(logsDir, { recursive: true });
|
|
150261
|
-
const logPath = join138(logsDir, "gc.log");
|
|
150262
|
-
const errPath = join138(logsDir, "gc.err");
|
|
150263
|
-
const outStream = createWriteStream2(logPath, { flags: "a" });
|
|
150264
|
-
const errStream = createWriteStream2(errPath, { flags: "a" });
|
|
150265
|
-
const daemonEntry = join138(fileURLToPath8(import.meta.url), "..", "daemon-entry.js");
|
|
150266
|
-
const child = spawn3(process.execPath, [daemonEntry, cleoDir], {
|
|
150267
|
-
detached: true,
|
|
150268
|
-
stdio: ["ignore", outStream, errStream],
|
|
150269
|
-
env: { ...process.env, CLEO_GC_DAEMON: "1" }
|
|
150270
|
-
});
|
|
150271
|
-
child.unref();
|
|
150272
|
-
const pid = child.pid ?? 0;
|
|
150273
|
-
await patchGCState(join138(cleoDir, "gc-state.json"), {
|
|
150274
|
-
daemonPid: pid,
|
|
150275
|
-
daemonStartedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
150276
|
-
});
|
|
150277
|
-
return pid;
|
|
150278
|
-
}
|
|
150279
|
-
async function stopGCDaemon(cleoDir) {
|
|
150280
|
-
const statePath = join138(cleoDir, "gc-state.json");
|
|
150281
|
-
const state = await readGCState(statePath);
|
|
150282
|
-
const pid = state.daemonPid;
|
|
150283
|
-
if (!pid) {
|
|
150284
|
-
return { stopped: false, pid: null, reason: "Daemon PID not found in gc-state.json" };
|
|
150285
|
-
}
|
|
150286
|
-
try {
|
|
150287
|
-
process.kill(pid, 0);
|
|
150288
|
-
} catch {
|
|
150289
|
-
await patchGCState(statePath, { daemonPid: null });
|
|
150290
|
-
return {
|
|
150291
|
-
stopped: false,
|
|
150292
|
-
pid,
|
|
150293
|
-
reason: `Daemon PID ${pid} is not running (stale state cleared)`
|
|
150294
|
-
};
|
|
150295
|
-
}
|
|
150296
|
-
try {
|
|
150297
|
-
process.kill(pid, "SIGTERM");
|
|
150298
|
-
await patchGCState(statePath, { daemonPid: null });
|
|
150299
|
-
return { stopped: true, pid, reason: `SIGTERM sent to PID ${pid}` };
|
|
150300
|
-
} catch (err) {
|
|
150301
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
150302
|
-
return { stopped: false, pid, reason: `Failed to send SIGTERM to PID ${pid}: ${msg}` };
|
|
150303
|
-
}
|
|
150304
|
-
}
|
|
150305
|
-
async function getGCDaemonStatus(cleoDir) {
|
|
150306
|
-
const state = await readGCState(join138(cleoDir, "gc-state.json"));
|
|
150307
|
-
const pid = state.daemonPid;
|
|
150308
|
-
let running = false;
|
|
150309
|
-
if (pid) {
|
|
150310
|
-
try {
|
|
150311
|
-
process.kill(pid, 0);
|
|
150312
|
-
running = true;
|
|
150313
|
-
} catch {
|
|
150314
|
-
running = false;
|
|
150315
|
-
}
|
|
150316
|
-
}
|
|
150317
|
-
return {
|
|
150318
|
-
running,
|
|
150319
|
-
pid: running ? pid : null,
|
|
150320
|
-
startedAt: state.daemonStartedAt,
|
|
150321
|
-
lastRunAt: state.lastRunAt,
|
|
150322
|
-
lastDiskUsedPct: state.lastDiskUsedPct,
|
|
150323
|
-
escalationNeeded: state.escalationNeeded
|
|
150324
|
-
};
|
|
150325
|
-
}
|
|
150326
|
-
|
|
150327
|
-
// packages/cleo/src/cli/commands/daemon.ts
|
|
150328
|
-
function registerDaemonCommand(program) {
|
|
150329
|
-
const daemon = program.command("daemon").description("Manage the CLEO GC sidecar daemon for autonomous transcript cleanup");
|
|
150330
|
-
daemon.command("start").description("Spawn the GC daemon as a detached background process").option("--cleo-dir <path>", "Override .cleo/ directory path").option("--json", "Output result as JSON").action(async (opts) => {
|
|
150331
|
-
const cleoDir = opts.cleoDir ?? join139(homedir11(), ".cleo");
|
|
150332
|
-
try {
|
|
150333
|
-
const status = await getGCDaemonStatus(cleoDir);
|
|
150334
|
-
if (status.running && status.pid) {
|
|
150335
|
-
const result2 = {
|
|
150336
|
-
success: false,
|
|
150337
|
-
data: {
|
|
150338
|
-
running: true,
|
|
150339
|
-
pid: status.pid,
|
|
150340
|
-
message: `Daemon already running (PID ${status.pid})`
|
|
150341
|
-
}
|
|
150342
|
-
};
|
|
150343
|
-
if (opts.json) {
|
|
150344
|
-
process.stdout.write(JSON.stringify(result2) + "\n");
|
|
150345
|
-
} else {
|
|
150346
|
-
process.stdout.write(`Daemon already running (PID ${status.pid})
|
|
150347
|
-
`);
|
|
150348
|
-
}
|
|
150349
|
-
return;
|
|
150350
|
-
}
|
|
150351
|
-
const pid = await spawnGCDaemon(cleoDir);
|
|
150352
|
-
const result = {
|
|
150353
|
-
success: true,
|
|
150354
|
-
data: { pid, cleoDir, message: `GC daemon started (PID ${pid})` }
|
|
150355
|
-
};
|
|
150356
|
-
if (opts.json) {
|
|
150357
|
-
process.stdout.write(JSON.stringify(result) + "\n");
|
|
150358
|
-
} else {
|
|
150359
|
-
process.stdout.write(`GC daemon started (PID ${pid})
|
|
150360
|
-
`);
|
|
150361
|
-
process.stdout.write(`Logs: ${join139(cleoDir, "logs", "gc.log")}
|
|
150362
|
-
`);
|
|
150363
|
-
}
|
|
150364
|
-
} catch (err) {
|
|
150365
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
150366
|
-
const result = { success: false, error: { code: "E_INTERNAL", message } };
|
|
150367
|
-
if (opts.json) {
|
|
150368
|
-
process.stdout.write(JSON.stringify(result) + "\n");
|
|
150369
|
-
} else {
|
|
150370
|
-
process.stderr.write(`Error starting daemon: ${message}
|
|
150371
|
-
`);
|
|
150372
|
-
}
|
|
150373
|
-
process.exit(1);
|
|
150374
|
-
}
|
|
150375
|
-
});
|
|
150376
|
-
daemon.command("stop").description("Stop the GC daemon by sending SIGTERM to its PID").option("--cleo-dir <path>", "Override .cleo/ directory path").option("--json", "Output result as JSON").action(async (opts) => {
|
|
150377
|
-
const cleoDir = opts.cleoDir ?? join139(homedir11(), ".cleo");
|
|
150378
|
-
try {
|
|
150379
|
-
const stopResult = await stopGCDaemon(cleoDir);
|
|
150380
|
-
const result = {
|
|
150381
|
-
success: stopResult.stopped,
|
|
150382
|
-
data: stopResult
|
|
150383
|
-
};
|
|
150384
|
-
if (opts.json) {
|
|
150385
|
-
process.stdout.write(JSON.stringify(result) + "\n");
|
|
150386
|
-
} else if (stopResult.stopped) {
|
|
150387
|
-
process.stdout.write(`GC daemon stopped (${stopResult.reason})
|
|
150388
|
-
`);
|
|
150389
|
-
} else {
|
|
150390
|
-
process.stdout.write(`${stopResult.reason}
|
|
150391
|
-
`);
|
|
150392
|
-
}
|
|
150393
|
-
} catch (err) {
|
|
150394
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
150395
|
-
const result = { success: false, error: { code: "E_INTERNAL", message } };
|
|
150396
|
-
if (opts.json) {
|
|
150397
|
-
process.stdout.write(JSON.stringify(result) + "\n");
|
|
150398
|
-
} else {
|
|
150399
|
-
process.stderr.write(`Error stopping daemon: ${message}
|
|
150400
|
-
`);
|
|
150401
|
-
}
|
|
150402
|
-
process.exit(1);
|
|
150403
|
-
}
|
|
150404
|
-
});
|
|
150405
|
-
daemon.command("status").description("Show daemon running state, PID, last GC run, and disk usage").option("--cleo-dir <path>", "Override .cleo/ directory path").option("--json", "Output result as JSON").action(async (opts) => {
|
|
150406
|
-
const cleoDir = opts.cleoDir ?? join139(homedir11(), ".cleo");
|
|
150407
|
-
await showDaemonStatus(cleoDir, opts.json ?? false);
|
|
150408
|
-
});
|
|
150409
|
-
daemon.action(async (opts) => {
|
|
150410
|
-
const cleoDir = opts.cleoDir ?? join139(homedir11(), ".cleo");
|
|
150411
|
-
await showDaemonStatus(cleoDir, opts.json ?? false);
|
|
150412
|
-
});
|
|
150413
|
-
}
|
|
150414
|
-
async function showDaemonStatus(cleoDir, json3) {
|
|
150415
|
-
try {
|
|
150416
|
-
const status = await getGCDaemonStatus(cleoDir);
|
|
150417
|
-
const result = { success: true, data: status };
|
|
150418
|
-
if (json3) {
|
|
150419
|
-
process.stdout.write(JSON.stringify(result) + "\n");
|
|
150420
|
-
} else {
|
|
150421
|
-
const runningStr = status.running ? `running (PID ${status.pid})` : "stopped";
|
|
150422
|
-
process.stdout.write(`Daemon: ${runningStr}
|
|
150423
|
-
`);
|
|
150424
|
-
process.stdout.write(`Started at: ${status.startedAt ?? "never"}
|
|
150425
|
-
`);
|
|
150426
|
-
process.stdout.write(`Last GC run: ${status.lastRunAt ?? "never"}
|
|
150427
|
-
`);
|
|
150428
|
-
const diskStr = status.lastDiskUsedPct !== null ? `${status.lastDiskUsedPct.toFixed(1)}%` : "unknown";
|
|
150429
|
-
process.stdout.write(`Disk used: ${diskStr}
|
|
150430
|
-
`);
|
|
150431
|
-
if (status.escalationNeeded) {
|
|
150432
|
-
process.stdout.write(
|
|
150433
|
-
`
|
|
150434
|
-
WARNING: Disk threshold breached. Run 'cleo gc run' to reclaim space.
|
|
150435
|
-
`
|
|
150436
|
-
);
|
|
150437
|
-
}
|
|
150438
|
-
}
|
|
150439
|
-
} catch (err) {
|
|
150440
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
150441
|
-
if (json3) {
|
|
150442
|
-
process.stdout.write(
|
|
150443
|
-
JSON.stringify({ success: false, error: { code: "E_INTERNAL", message } }) + "\n"
|
|
150444
|
-
);
|
|
150445
|
-
} else {
|
|
150446
|
-
process.stderr.write(`Error reading daemon status: ${message}
|
|
150447
|
-
`);
|
|
150448
|
-
}
|
|
150449
|
-
process.exit(1);
|
|
150450
|
-
}
|
|
150451
|
-
}
|
|
150452
|
-
|
|
150453
|
-
// packages/cleo/src/cli/commands/gc.ts
|
|
150454
|
-
import { homedir as homedir12 } from "node:os";
|
|
150455
|
-
import { join as join140 } from "node:path";
|
|
150456
|
-
function registerGCCommand(program) {
|
|
150457
|
-
const gc = program.command("gc").description("Transcript garbage collection: manual trigger and status");
|
|
150458
|
-
gc.command("run").description("Run GC immediately (blocking). Prunes old transcripts based on disk pressure.").option("--cleo-dir <path>", "Override .cleo/ directory path").option("--dry-run", "Report what would be pruned without deleting anything").option("--json", "Output result as JSON").action(async (opts) => {
|
|
150459
|
-
const cleoDir = opts.cleoDir ?? join140(homedir12(), ".cleo");
|
|
150460
|
-
const dryRun = opts.dryRun ?? false;
|
|
150461
|
-
try {
|
|
150462
|
-
const gcResult = await runGC({ cleoDir, dryRun });
|
|
150463
|
-
const result = { success: true, data: gcResult };
|
|
150464
|
-
if (opts.json) {
|
|
150465
|
-
process.stdout.write(JSON.stringify(result) + "\n");
|
|
150466
|
-
} else {
|
|
150467
|
-
const dryLabel = dryRun ? " (dry run)" : "";
|
|
150468
|
-
process.stdout.write(`GC run complete${dryLabel}
|
|
150469
|
-
`);
|
|
150470
|
-
process.stdout.write(
|
|
150471
|
-
`Disk: ${gcResult.diskUsedPct.toFixed(1)}% (${gcResult.threshold.toUpperCase()})
|
|
150472
|
-
`
|
|
150473
|
-
);
|
|
150474
|
-
process.stdout.write(
|
|
150475
|
-
`Pruned: ${gcResult.pruned.length} paths, ${formatBytes(gcResult.bytesFreed)} freed
|
|
150476
|
-
`
|
|
150477
|
-
);
|
|
150478
|
-
if (gcResult.escalationSet && gcResult.escalationReason) {
|
|
150479
|
-
process.stdout.write(`
|
|
150480
|
-
WARNING: ${gcResult.escalationReason}
|
|
150481
|
-
`);
|
|
150482
|
-
}
|
|
150483
|
-
}
|
|
150484
|
-
} catch (err) {
|
|
150485
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
150486
|
-
const result = { success: false, error: { code: "E_INTERNAL", message } };
|
|
150487
|
-
if (opts.json) {
|
|
150488
|
-
process.stdout.write(JSON.stringify(result) + "\n");
|
|
150489
|
-
} else {
|
|
150490
|
-
process.stderr.write(`GC run failed: ${message}
|
|
150491
|
-
`);
|
|
150492
|
-
}
|
|
150493
|
-
process.exit(1);
|
|
150494
|
-
}
|
|
150495
|
-
});
|
|
150496
|
-
gc.command("status").description("Show last GC run stats, disk usage, and escalation state").option("--cleo-dir <path>", "Override .cleo/ directory path").option("--json", "Output result as JSON").action(async (opts) => {
|
|
150497
|
-
const cleoDir = opts.cleoDir ?? join140(homedir12(), ".cleo");
|
|
150498
|
-
const statePath = join140(cleoDir, "gc-state.json");
|
|
150499
|
-
try {
|
|
150500
|
-
const state = await readGCState(statePath);
|
|
150501
|
-
const result = { success: true, data: state };
|
|
150502
|
-
if (opts.json) {
|
|
150503
|
-
process.stdout.write(JSON.stringify(result) + "\n");
|
|
150504
|
-
} else {
|
|
150505
|
-
process.stdout.write(`Last run: ${state.lastRunAt ?? "never"}
|
|
150506
|
-
`);
|
|
150507
|
-
process.stdout.write(`Last result: ${state.lastRunResult ?? "none"}
|
|
150508
|
-
`);
|
|
150509
|
-
process.stdout.write(`Bytes freed: ${formatBytes(state.lastRunBytesFreed)}
|
|
150510
|
-
`);
|
|
150511
|
-
const diskStr = state.lastDiskUsedPct !== null ? `${state.lastDiskUsedPct.toFixed(1)}%` : "unknown";
|
|
150512
|
-
process.stdout.write(`Disk used: ${diskStr}
|
|
150513
|
-
`);
|
|
150514
|
-
process.stdout.write(`Failures: ${state.consecutiveFailures}
|
|
150515
|
-
`);
|
|
150516
|
-
process.stdout.write(
|
|
150517
|
-
`Escalation: ${state.escalationNeeded ? "YES \u2014 run cleo gc run" : "no"}
|
|
150518
|
-
`
|
|
150519
|
-
);
|
|
150520
|
-
if (state.escalationReason) {
|
|
150521
|
-
process.stdout.write(`Reason: ${state.escalationReason}
|
|
150522
|
-
`);
|
|
150523
|
-
}
|
|
150524
|
-
}
|
|
150525
|
-
} catch (err) {
|
|
150526
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
150527
|
-
const result = { success: false, error: { code: "E_INTERNAL", message } };
|
|
150528
|
-
if (opts.json) {
|
|
150529
|
-
process.stdout.write(JSON.stringify(result) + "\n");
|
|
150530
|
-
} else {
|
|
150531
|
-
process.stderr.write(`Error reading GC status: ${message}
|
|
150532
|
-
`);
|
|
150533
|
-
}
|
|
150534
|
-
process.exit(1);
|
|
150535
|
-
}
|
|
150536
|
-
});
|
|
150537
|
-
}
|
|
150538
|
-
function formatBytes(bytes) {
|
|
150539
|
-
if (bytes === 0) return "0 B";
|
|
150540
|
-
const units = ["B", "KB", "MB", "GB", "TB"];
|
|
150541
|
-
const exp = Math.floor(Math.log(bytes) / Math.log(1024));
|
|
150542
|
-
const value = bytes / 1024 ** exp;
|
|
150543
|
-
return `${value.toFixed(exp === 0 ? 0 : 1)} ${units[exp] ?? "B"}`;
|
|
150544
|
-
}
|
|
150545
|
-
|
|
150546
|
-
// packages/cleo/src/cli/commands/transcript.ts
|
|
150547
|
-
init_src3();
|
|
150548
|
-
import { homedir as homedir14 } from "node:os";
|
|
150549
|
-
import { join as join142 } from "node:path";
|
|
150550
|
-
|
|
150551
|
-
// packages/cleo/src/gc/transcript.ts
|
|
150552
|
-
import { lstat as lstat3, readdir as readdir6, stat as stat6 } from "node:fs/promises";
|
|
150553
|
-
import { homedir as homedir13 } from "node:os";
|
|
150554
|
-
import { join as join141 } from "node:path";
|
|
150555
|
-
var HOT_MAX_MS = 24 * 60 * 60 * 1e3;
|
|
150556
|
-
var WARM_MAX_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
150557
|
-
function classifyTranscriptTier(ageMs) {
|
|
150558
|
-
if (ageMs < HOT_MAX_MS) return "hot";
|
|
150559
|
-
if (ageMs < WARM_MAX_MS) return "warm";
|
|
150560
|
-
return "cold";
|
|
150561
|
-
}
|
|
150562
|
-
function parseSessionId(filename) {
|
|
150563
|
-
return filename.replace(/\.jsonl$/, "");
|
|
150564
|
-
}
|
|
150565
|
-
async function scanTranscripts(projectsDir) {
|
|
150566
|
-
const resolvedProjectsDir = projectsDir ?? join141(homedir13(), ".claude", "projects");
|
|
150567
|
-
const now2 = Date.now();
|
|
150568
|
-
const hot = [];
|
|
150569
|
-
const warm = [];
|
|
150570
|
-
let totalBytes = 0;
|
|
150571
|
-
let slugs;
|
|
150572
|
-
try {
|
|
150573
|
-
const entries = await readdir6(resolvedProjectsDir, { withFileTypes: true });
|
|
150574
|
-
slugs = entries.filter((e) => e.isDirectory()).map((e) => e.name);
|
|
150575
|
-
} catch {
|
|
150576
|
-
return { totalSessions: 0, hot, warm, totalBytes, projectsDir: resolvedProjectsDir };
|
|
150577
|
-
}
|
|
150578
|
-
for (const slug of slugs) {
|
|
150579
|
-
const slugDir = join141(resolvedProjectsDir, slug);
|
|
150580
|
-
let entries;
|
|
150581
|
-
try {
|
|
150582
|
-
entries = await readdir6(slugDir, { withFileTypes: true });
|
|
150583
|
-
} catch {
|
|
150584
|
-
continue;
|
|
150585
|
-
}
|
|
150586
|
-
for (const entry of entries) {
|
|
150587
|
-
if (!entry.isFile() || !entry.name.endsWith(".jsonl")) continue;
|
|
150588
|
-
const jsonlPath = join141(slugDir, entry.name);
|
|
150589
|
-
const sessionId = parseSessionId(entry.name);
|
|
150590
|
-
let fileInfo;
|
|
150591
|
-
try {
|
|
150592
|
-
fileInfo = await stat6(jsonlPath);
|
|
150593
|
-
} catch {
|
|
150594
|
-
continue;
|
|
150595
|
-
}
|
|
150596
|
-
const mtimeMs = fileInfo.mtimeMs;
|
|
150597
|
-
const ageMs = now2 - mtimeMs;
|
|
150598
|
-
const tier = classifyTranscriptTier(ageMs);
|
|
150599
|
-
const bytes = fileInfo.size;
|
|
150600
|
-
const candidateSessionDir = join141(slugDir, sessionId);
|
|
150601
|
-
let sessionDir = null;
|
|
150602
|
-
let sessionDirBytes = 0;
|
|
150603
|
-
try {
|
|
150604
|
-
const dirInfo = await lstat3(candidateSessionDir);
|
|
150605
|
-
if (dirInfo.isDirectory()) {
|
|
150606
|
-
sessionDir = candidateSessionDir;
|
|
150607
|
-
sessionDirBytes = await getPathBytes(candidateSessionDir);
|
|
150608
|
-
}
|
|
150609
|
-
} catch {
|
|
150610
|
-
}
|
|
150611
|
-
const info = {
|
|
150612
|
-
jsonlPath,
|
|
150613
|
-
projectSlug: slug,
|
|
150614
|
-
sessionId,
|
|
150615
|
-
mtimeMs,
|
|
150616
|
-
ageMs,
|
|
150617
|
-
tier,
|
|
150618
|
-
bytes,
|
|
150619
|
-
sessionDir,
|
|
150620
|
-
sessionDirBytes
|
|
150621
|
-
};
|
|
150622
|
-
totalBytes += bytes + sessionDirBytes;
|
|
150623
|
-
if (tier === "hot") {
|
|
150624
|
-
hot.push(info);
|
|
150625
|
-
} else if (tier === "warm") {
|
|
150626
|
-
warm.push(info);
|
|
150627
|
-
}
|
|
150628
|
-
}
|
|
150629
|
-
}
|
|
150630
|
-
const totalSessions = hot.length + warm.length;
|
|
150631
|
-
return { totalSessions, hot, warm, totalBytes, projectsDir: resolvedProjectsDir };
|
|
150632
|
-
}
|
|
150633
|
-
async function pruneTranscripts(opts) {
|
|
150634
|
-
const { olderThanMs, confirm, projectsDir } = opts;
|
|
150635
|
-
const dryRun = !confirm;
|
|
150636
|
-
const hasApiKey = Boolean(process.env["ANTHROPIC_API_KEY"]);
|
|
150637
|
-
const effectiveMaxAgeMs = hasApiKey ? olderThanMs : Math.max(olderThanMs, 30 * 24 * 60 * 60 * 1e3);
|
|
150638
|
-
const now2 = Date.now();
|
|
150639
|
-
const deletedPaths = [];
|
|
150640
|
-
let bytesFreed = 0;
|
|
150641
|
-
let pruned = 0;
|
|
150642
|
-
const resolvedProjectsDir = projectsDir ?? join141(homedir13(), ".claude", "projects");
|
|
150643
|
-
let slugs;
|
|
150644
|
-
try {
|
|
150645
|
-
const entries = await readdir6(resolvedProjectsDir, { withFileTypes: true });
|
|
150646
|
-
slugs = entries.filter((e) => e.isDirectory()).map((e) => e.name);
|
|
150647
|
-
} catch {
|
|
150648
|
-
return { pruned: 0, bytesFreed: 0, deletedPaths: [], dryRun };
|
|
150649
|
-
}
|
|
150650
|
-
for (const slug of slugs) {
|
|
150651
|
-
const slugDir = join141(resolvedProjectsDir, slug);
|
|
150652
|
-
let entries;
|
|
150653
|
-
try {
|
|
150654
|
-
entries = await readdir6(slugDir, { withFileTypes: true });
|
|
150655
|
-
} catch {
|
|
150656
|
-
continue;
|
|
150657
|
-
}
|
|
150658
|
-
for (const entry of entries) {
|
|
150659
|
-
if (!entry.isFile() || !entry.name.endsWith(".jsonl")) continue;
|
|
150660
|
-
const jsonlPath = join141(slugDir, entry.name);
|
|
150661
|
-
let fileInfo;
|
|
150662
|
-
try {
|
|
150663
|
-
fileInfo = await stat6(jsonlPath);
|
|
150664
|
-
} catch {
|
|
150665
|
-
continue;
|
|
150666
|
-
}
|
|
150667
|
-
const ageMs = now2 - fileInfo.mtimeMs;
|
|
150668
|
-
if (ageMs <= effectiveMaxAgeMs) continue;
|
|
150669
|
-
const sessionId = parseSessionId(entry.name);
|
|
150670
|
-
const sessionDir = join141(slugDir, sessionId);
|
|
150671
|
-
const jsonlBytes = fileInfo.size;
|
|
150672
|
-
let sessionDirBytes = 0;
|
|
150673
|
-
try {
|
|
150674
|
-
const dirInfo = await lstat3(sessionDir);
|
|
150675
|
-
if (dirInfo.isDirectory()) {
|
|
150676
|
-
sessionDirBytes = await getPathBytes(sessionDir);
|
|
150677
|
-
}
|
|
150678
|
-
} catch {
|
|
150679
|
-
}
|
|
150680
|
-
if (dryRun) {
|
|
150681
|
-
deletedPaths.push(jsonlPath);
|
|
150682
|
-
if (sessionDirBytes > 0) deletedPaths.push(sessionDir);
|
|
150683
|
-
bytesFreed += jsonlBytes + sessionDirBytes;
|
|
150684
|
-
pruned++;
|
|
150685
|
-
continue;
|
|
150686
|
-
}
|
|
150687
|
-
try {
|
|
150688
|
-
await idempotentRm(jsonlPath);
|
|
150689
|
-
deletedPaths.push(jsonlPath);
|
|
150690
|
-
bytesFreed += jsonlBytes;
|
|
150691
|
-
pruned++;
|
|
150692
|
-
} catch {
|
|
150693
|
-
continue;
|
|
150694
|
-
}
|
|
150695
|
-
try {
|
|
150696
|
-
const dirInfo = await lstat3(sessionDir);
|
|
150697
|
-
if (dirInfo.isDirectory()) {
|
|
150698
|
-
await idempotentRm(sessionDir);
|
|
150699
|
-
deletedPaths.push(sessionDir);
|
|
150700
|
-
bytesFreed += sessionDirBytes;
|
|
150701
|
-
}
|
|
150702
|
-
} catch {
|
|
150703
|
-
}
|
|
150704
|
-
}
|
|
150705
|
-
}
|
|
150706
|
-
return { pruned, bytesFreed, deletedPaths, dryRun };
|
|
150707
|
-
}
|
|
150708
|
-
function parseDurationMs(duration3) {
|
|
150709
|
-
const match = /^(\d+(\.\d+)?)(d|h|m|s)$/.exec(duration3.trim());
|
|
150710
|
-
if (!match?.[1] || !match[3]) {
|
|
150711
|
-
throw new Error(`Invalid duration format: "${duration3}". Use format like 7d, 24h, 30m, 60s.`);
|
|
150712
|
-
}
|
|
150713
|
-
const value = parseFloat(match[1]);
|
|
150714
|
-
const unit = match[3];
|
|
150715
|
-
const multipliers = {
|
|
150716
|
-
d: 24 * 60 * 60 * 1e3,
|
|
150717
|
-
h: 60 * 60 * 1e3,
|
|
150718
|
-
m: 60 * 1e3,
|
|
150719
|
-
s: 1e3
|
|
150720
|
-
};
|
|
150721
|
-
return value * (multipliers[unit] ?? 1e3);
|
|
150722
|
-
}
|
|
150723
|
-
|
|
150724
|
-
// packages/cleo/src/cli/commands/transcript.ts
|
|
150725
|
-
function registerTranscriptCommand(program) {
|
|
150726
|
-
const transcript = program.command("transcript").description("Transcript lifecycle management: scan, extract, and prune session transcripts");
|
|
150727
|
-
transcript.command("scan").description("Inventory all session transcripts with hot/warm/cold tier breakdown").option("--projects-dir <path>", "Override ~/.claude/projects/ path").option("--pending", "List sessions queued for warm-tier extraction (T732)").option("--json", "Output result as JSON (LAFS envelope)").action(async (opts) => {
|
|
150728
|
-
if (opts.pending) {
|
|
150729
|
-
try {
|
|
150730
|
-
const projectRoot = getProjectRoot();
|
|
150731
|
-
const { scanPendingTranscripts: scanPendingTranscripts2 } = await import("@cleocode/core/memory/transcript-scanner.js");
|
|
150732
|
-
const pending = await scanPendingTranscripts2(projectRoot);
|
|
150733
|
-
const envelope = { success: true, data: { count: pending.length, pending } };
|
|
150734
|
-
if (opts.json) {
|
|
150735
|
-
process.stdout.write(JSON.stringify(envelope) + "\n");
|
|
150736
|
-
} else {
|
|
150737
|
-
process.stdout.write(`Sessions pending extraction: ${pending.length}
|
|
150738
|
-
`);
|
|
150739
|
-
for (const p2 of pending) {
|
|
150740
|
-
process.stdout.write(
|
|
150741
|
-
` ${p2.sessionId} ${p2.filePath || "(file not found)"} queued: ${p2.createdAt}
|
|
150742
|
-
`
|
|
150743
|
-
);
|
|
150744
|
-
}
|
|
150745
|
-
}
|
|
150746
|
-
} catch (err) {
|
|
150747
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
150748
|
-
const envelope = { success: false, error: { code: "E_INTERNAL", message } };
|
|
150749
|
-
if (opts.json) {
|
|
150750
|
-
process.stdout.write(JSON.stringify(envelope) + "\n");
|
|
150751
|
-
} else {
|
|
150752
|
-
process.stderr.write(`Pending scan failed: ${message}
|
|
150753
|
-
`);
|
|
150754
|
-
}
|
|
150755
|
-
process.exit(1);
|
|
150756
|
-
}
|
|
150757
|
-
return;
|
|
150758
|
-
}
|
|
150759
|
-
const projectsDir = opts.projectsDir ?? join142(homedir14(), ".claude", "projects");
|
|
150760
|
-
try {
|
|
150761
|
-
const result = await scanTranscripts(projectsDir);
|
|
150762
|
-
const envelope = {
|
|
150763
|
-
success: true,
|
|
150764
|
-
data: {
|
|
150765
|
-
totalSessions: result.totalSessions,
|
|
150766
|
-
totalBytes: result.totalBytes,
|
|
150767
|
-
totalMB: parseFloat((result.totalBytes / 1024 / 1024).toFixed(2)),
|
|
150768
|
-
hot: {
|
|
150769
|
-
count: result.hot.length,
|
|
150770
|
-
sessions: result.hot.map((s3) => ({
|
|
150771
|
-
sessionId: s3.sessionId,
|
|
150772
|
-
projectSlug: s3.projectSlug,
|
|
150773
|
-
ageHours: parseFloat((s3.ageMs / 36e5).toFixed(1)),
|
|
150774
|
-
bytes: s3.bytes + s3.sessionDirBytes
|
|
150775
|
-
}))
|
|
150776
|
-
},
|
|
150777
|
-
warm: {
|
|
150778
|
-
count: result.warm.length,
|
|
150779
|
-
sessions: result.warm.map((s3) => ({
|
|
150780
|
-
sessionId: s3.sessionId,
|
|
150781
|
-
projectSlug: s3.projectSlug,
|
|
150782
|
-
ageDays: parseFloat((s3.ageMs / 864e5).toFixed(1)),
|
|
150783
|
-
bytes: s3.bytes + s3.sessionDirBytes
|
|
150784
|
-
}))
|
|
150785
|
-
},
|
|
150786
|
-
projectsDir: result.projectsDir
|
|
150787
|
-
}
|
|
150788
|
-
};
|
|
150789
|
-
if (opts.json) {
|
|
150790
|
-
process.stdout.write(JSON.stringify(envelope) + "\n");
|
|
150791
|
-
} else {
|
|
150792
|
-
process.stdout.write(`Transcript scan results
|
|
150793
|
-
`);
|
|
150794
|
-
process.stdout.write(`=======================
|
|
150795
|
-
`);
|
|
150796
|
-
process.stdout.write(`Projects dir: ${result.projectsDir}
|
|
150797
|
-
`);
|
|
150798
|
-
process.stdout.write(`Total sessions: ${result.totalSessions}
|
|
150799
|
-
`);
|
|
150800
|
-
process.stdout.write(
|
|
150801
|
-
`Total size: ${(result.totalBytes / 1024 / 1024).toFixed(1)} MB
|
|
150802
|
-
`
|
|
150803
|
-
);
|
|
150804
|
-
process.stdout.write(`
|
|
150805
|
-
Tier breakdown:
|
|
150806
|
-
`);
|
|
150807
|
-
process.stdout.write(` HOT (0\u201324h): ${result.hot.length} sessions
|
|
150808
|
-
`);
|
|
150809
|
-
process.stdout.write(` WARM (1\u20137d): ${result.warm.length} sessions
|
|
150810
|
-
`);
|
|
150811
|
-
process.stdout.write(` COLD (>7d): (transcripts deleted; brain.db entries only)
|
|
150812
|
-
`);
|
|
150813
|
-
}
|
|
150814
|
-
} catch (err) {
|
|
150815
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
150816
|
-
const envelope = { success: false, error: { code: "E_INTERNAL", message } };
|
|
150817
|
-
if (opts.json) {
|
|
150818
|
-
process.stdout.write(JSON.stringify(envelope) + "\n");
|
|
150819
|
-
} else {
|
|
150820
|
-
process.stderr.write(`Scan failed: ${message}
|
|
150821
|
-
`);
|
|
150822
|
-
}
|
|
150823
|
-
process.exit(1);
|
|
150824
|
-
}
|
|
150825
|
-
});
|
|
150826
|
-
transcript.command("extract").argument("[session-id]", "Session UUID to extract (omit for --all-warm)").description("Run LLM extraction on a session transcript and write memories to brain.db").option("--all-warm", "Extract all warm-tier sessions (1\u20137d old)").option("--tier <tier>", "LLM tier: warm (default) or cold (Sonnet)", "warm").option("--dry-run", "Report without writing to brain.db or deleting JSONL").option("--projects-dir <path>", "Override ~/.claude/projects/ path").option("--json", "Output result as JSON (LAFS envelope)").action(
|
|
150827
|
-
async (sessionId, opts) => {
|
|
150828
|
-
const tier = opts.tier ?? "warm";
|
|
150829
|
-
const dryRun = opts.dryRun ?? false;
|
|
150830
|
-
const projectRoot = getProjectRoot();
|
|
150831
|
-
try {
|
|
150832
|
-
const { extractTranscript } = await import("@cleocode/core/memory/transcript-extractor.js");
|
|
150833
|
-
const { findSessionTranscriptPath: findSessionTranscriptPath2, listAllTranscripts: listAllTranscripts2 } = await import("@cleocode/core/memory/transcript-scanner.js");
|
|
150834
|
-
const sessions2 = [];
|
|
150835
|
-
if (opts.allWarm) {
|
|
150836
|
-
const allTranscripts = await listAllTranscripts2({ olderThanHours: 1 });
|
|
150837
|
-
for (const t of allTranscripts) {
|
|
150838
|
-
sessions2.push({ sessionId: t.sessionId, path: t.path });
|
|
150839
|
-
}
|
|
150840
|
-
} else if (sessionId) {
|
|
150841
|
-
const path12 = await findSessionTranscriptPath2(sessionId);
|
|
150842
|
-
if (!path12) {
|
|
150843
|
-
const envelope2 = {
|
|
150844
|
-
success: false,
|
|
150845
|
-
error: {
|
|
150846
|
-
code: "E_NOT_FOUND",
|
|
150847
|
-
message: `Session JSONL not found for: ${sessionId}`
|
|
150848
|
-
}
|
|
150849
|
-
};
|
|
150850
|
-
if (opts.json) {
|
|
150851
|
-
process.stdout.write(JSON.stringify(envelope2) + "\n");
|
|
150852
|
-
} else {
|
|
150853
|
-
process.stderr.write(`Session not found: ${sessionId}
|
|
150854
|
-
`);
|
|
150855
|
-
}
|
|
150856
|
-
process.exit(4);
|
|
150857
|
-
return;
|
|
150858
|
-
}
|
|
150859
|
-
sessions2.push({ sessionId, path: path12 });
|
|
150860
|
-
} else {
|
|
150861
|
-
process.stderr.write(
|
|
150862
|
-
"Provide a session-id or use --all-warm to extract all warm sessions\n"
|
|
150863
|
-
);
|
|
150864
|
-
process.exit(2);
|
|
150865
|
-
return;
|
|
150866
|
-
}
|
|
150867
|
-
const results = [];
|
|
150868
|
-
let totalExtracted = 0;
|
|
150869
|
-
let totalStored = 0;
|
|
150870
|
-
let totalBytesFreed = 0;
|
|
150871
|
-
for (const session of sessions2) {
|
|
150872
|
-
const result = await extractTranscript({
|
|
150873
|
-
transcriptPath: session.path,
|
|
150874
|
-
projectRoot,
|
|
150875
|
-
tier,
|
|
150876
|
-
dryRun,
|
|
150877
|
-
sessionId: session.sessionId
|
|
150878
|
-
});
|
|
150879
|
-
results.push(result);
|
|
150880
|
-
totalExtracted += result.extractedCount;
|
|
150881
|
-
totalStored += result.storedCount;
|
|
150882
|
-
totalBytesFreed += result.bytesFreed;
|
|
150883
|
-
}
|
|
150884
|
-
const envelope = {
|
|
150885
|
-
success: true,
|
|
150886
|
-
data: {
|
|
150887
|
-
sessionsProcessed: sessions2.length,
|
|
150888
|
-
memoriesExtracted: totalExtracted,
|
|
150889
|
-
memoriesStored: totalStored,
|
|
150890
|
-
bytesFreed: totalBytesFreed,
|
|
150891
|
-
dryRun,
|
|
150892
|
-
results
|
|
150893
|
-
}
|
|
150894
|
-
};
|
|
150895
|
-
if (opts.json) {
|
|
150896
|
-
process.stdout.write(JSON.stringify(envelope) + "\n");
|
|
150897
|
-
} else {
|
|
150898
|
-
const dryLabel = dryRun ? " (dry run)" : "";
|
|
150899
|
-
process.stdout.write(`Transcript extraction complete${dryLabel}
|
|
150900
|
-
`);
|
|
150901
|
-
process.stdout.write(`Sessions: ${sessions2.length}
|
|
150902
|
-
`);
|
|
150903
|
-
process.stdout.write(`Extracted: ${totalExtracted} memories
|
|
150904
|
-
`);
|
|
150905
|
-
process.stdout.write(`Stored: ${totalStored} memories
|
|
150906
|
-
`);
|
|
150907
|
-
process.stdout.write(`Freed: ${(totalBytesFreed / 1024 / 1024).toFixed(1)} MB
|
|
150908
|
-
`);
|
|
150909
|
-
}
|
|
150910
|
-
} catch (err) {
|
|
150911
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
150912
|
-
const envelope = { success: false, error: { code: "E_INTERNAL", message } };
|
|
150913
|
-
if (opts.json) {
|
|
150914
|
-
process.stdout.write(JSON.stringify(envelope) + "\n");
|
|
150915
|
-
} else {
|
|
150916
|
-
process.stderr.write(`Extract failed: ${message}
|
|
150917
|
-
`);
|
|
150918
|
-
}
|
|
150919
|
-
process.exit(1);
|
|
150920
|
-
}
|
|
150921
|
-
}
|
|
150922
|
-
);
|
|
150923
|
-
transcript.command("migrate").description("Backfill extraction from existing ~/.claude/projects/ session JSONLs (T733)").option("--dry-run", "Report without writing to brain.db or deleting JSONLs").option("--tier <tier>", "LLM tier: warm (default) or cold (Sonnet)", "warm").option("--older-than-hours <hours>", "Only process sessions older than N hours", "24").option("--project-filter <slug>", "Limit to a specific Claude project directory slug").option("--limit <n>", "Maximum number of sessions to process").option("--json", "Output result as JSON (LAFS envelope)").action(
|
|
150924
|
-
async (opts) => {
|
|
150925
|
-
const tier = opts.tier ?? "warm";
|
|
150926
|
-
const dryRun = opts.dryRun ?? false;
|
|
150927
|
-
const olderThanHours = opts.olderThanHours ? Number.parseInt(opts.olderThanHours, 10) : 24;
|
|
150928
|
-
const limit = opts.limit ? Number.parseInt(opts.limit, 10) : void 0;
|
|
150929
|
-
const projectRoot = getProjectRoot();
|
|
150930
|
-
try {
|
|
150931
|
-
const { extractTranscript } = await import("@cleocode/core/memory/transcript-extractor.js");
|
|
150932
|
-
const { listAllTranscripts: listAllTranscripts2 } = await import("@cleocode/core/memory/transcript-scanner.js");
|
|
150933
|
-
const transcripts = await listAllTranscripts2({
|
|
150934
|
-
olderThanHours,
|
|
150935
|
-
projectFilter: opts.projectFilter,
|
|
150936
|
-
limit
|
|
150937
|
-
});
|
|
150938
|
-
if (opts.json) {
|
|
150939
|
-
process.stdout.write(
|
|
150940
|
-
JSON.stringify({
|
|
150941
|
-
success: true,
|
|
150942
|
-
data: { discovered: transcripts.length, message: "Starting migration..." }
|
|
150943
|
-
}) + "\n"
|
|
150944
|
-
);
|
|
150945
|
-
} else {
|
|
150946
|
-
process.stdout.write(
|
|
150947
|
-
`Migrating ${transcripts.length} session transcripts${dryRun ? " (dry run)" : ""}...
|
|
150948
|
-
`
|
|
150949
|
-
);
|
|
150950
|
-
}
|
|
150951
|
-
let memoriesExtracted = 0;
|
|
150952
|
-
let memoriesStored = 0;
|
|
150953
|
-
let bytesFreed = 0;
|
|
150954
|
-
let processed = 0;
|
|
150955
|
-
let skipped = 0;
|
|
150956
|
-
let failed = 0;
|
|
150957
|
-
for (const entry of transcripts) {
|
|
150958
|
-
try {
|
|
150959
|
-
const result = await extractTranscript({
|
|
150960
|
-
transcriptPath: entry.path,
|
|
150961
|
-
projectRoot,
|
|
150962
|
-
tier,
|
|
150963
|
-
dryRun,
|
|
150964
|
-
sessionId: entry.sessionId
|
|
150965
|
-
});
|
|
150966
|
-
if (result.warnings.some((w2) => w2.includes("Already extracted"))) {
|
|
150967
|
-
skipped += 1;
|
|
150968
|
-
} else {
|
|
150969
|
-
processed += 1;
|
|
150970
|
-
memoriesExtracted += result.extractedCount;
|
|
150971
|
-
memoriesStored += result.storedCount;
|
|
150972
|
-
bytesFreed += result.bytesFreed;
|
|
150973
|
-
}
|
|
150974
|
-
} catch {
|
|
150975
|
-
failed += 1;
|
|
150976
|
-
}
|
|
150977
|
-
}
|
|
150978
|
-
const envelope = {
|
|
150979
|
-
success: true,
|
|
150980
|
-
data: {
|
|
150981
|
-
sessionsProcessed: processed,
|
|
150982
|
-
sessionsSkipped: skipped,
|
|
150983
|
-
sessionsFailed: failed,
|
|
150984
|
-
memoriesExtracted,
|
|
150985
|
-
memoriesStored,
|
|
150986
|
-
bytesFreed,
|
|
150987
|
-
mbFreed: parseFloat((bytesFreed / 1024 / 1024).toFixed(2)),
|
|
150988
|
-
dryRun
|
|
150989
|
-
}
|
|
150990
|
-
};
|
|
150991
|
-
if (opts.json) {
|
|
150992
|
-
process.stdout.write(JSON.stringify(envelope) + "\n");
|
|
150993
|
-
} else {
|
|
150994
|
-
const dryLabel = dryRun ? " (dry run \u2014 no changes written)" : "";
|
|
150995
|
-
process.stdout.write(`Migration complete${dryLabel}
|
|
150996
|
-
`);
|
|
150997
|
-
process.stdout.write(
|
|
150998
|
-
`Processed: ${processed} Skipped: ${skipped} Failed: ${failed}
|
|
150999
|
-
`
|
|
151000
|
-
);
|
|
151001
|
-
process.stdout.write(`Memories extracted: ${memoriesExtracted}
|
|
151002
|
-
`);
|
|
151003
|
-
process.stdout.write(`Memories stored: ${memoriesStored}
|
|
151004
|
-
`);
|
|
151005
|
-
process.stdout.write(
|
|
151006
|
-
`Bytes freed: ${(bytesFreed / 1024 / 1024).toFixed(1)} MB
|
|
151007
|
-
`
|
|
151008
|
-
);
|
|
151009
|
-
}
|
|
151010
|
-
} catch (err) {
|
|
151011
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
151012
|
-
const envelope = { success: false, error: { code: "E_INTERNAL", message } };
|
|
151013
|
-
if (opts.json) {
|
|
151014
|
-
process.stdout.write(JSON.stringify(envelope) + "\n");
|
|
151015
|
-
} else {
|
|
151016
|
-
process.stderr.write(`Migration failed: ${message}
|
|
151017
|
-
`);
|
|
151018
|
-
}
|
|
151019
|
-
process.exit(1);
|
|
151020
|
-
}
|
|
151021
|
-
}
|
|
151022
|
-
);
|
|
151023
|
-
transcript.command("prune").description("Prune session transcripts older than the specified duration. Dry-run by default.").requiredOption(
|
|
151024
|
-
"--older-than <duration>",
|
|
151025
|
-
"Delete sessions older than this (e.g. 7d, 24h, 30m)"
|
|
151026
|
-
).option(
|
|
151027
|
-
"--confirm",
|
|
151028
|
-
"Perform actual deletion. Without this flag, the command dry-runs and reports what would be deleted."
|
|
151029
|
-
).option("--projects-dir <path>", "Override ~/.claude/projects/ path").option("--json", "Output result as JSON (LAFS envelope)").action(
|
|
151030
|
-
async (opts) => {
|
|
151031
|
-
const isTTY = Boolean(process.stdin.isTTY && process.stdout.isTTY);
|
|
151032
|
-
const confirm = opts.confirm ?? false;
|
|
151033
|
-
if (!confirm && !isTTY) {
|
|
151034
|
-
const envelope = {
|
|
151035
|
-
success: false,
|
|
151036
|
-
error: {
|
|
151037
|
-
code: "E_INVALID_INPUT",
|
|
151038
|
-
message: "Non-interactive mode requires --confirm flag to perform deletion. Use --confirm to bypass the dry-run. Without --confirm, a dry-run report is printed."
|
|
151039
|
-
}
|
|
151040
|
-
};
|
|
151041
|
-
if (opts.json) {
|
|
151042
|
-
process.stdout.write(JSON.stringify(envelope) + "\n");
|
|
151043
|
-
} else {
|
|
151044
|
-
process.stderr.write(
|
|
151045
|
-
`Deletion requires --confirm in non-interactive mode. Re-run with --confirm to delete.
|
|
151046
|
-
`
|
|
151047
|
-
);
|
|
151048
|
-
}
|
|
151049
|
-
process.exit(0);
|
|
151050
|
-
}
|
|
151051
|
-
let olderThanMs;
|
|
151052
|
-
try {
|
|
151053
|
-
olderThanMs = parseDurationMs(opts.olderThan);
|
|
151054
|
-
} catch (parseErr) {
|
|
151055
|
-
const message = parseErr instanceof Error ? parseErr.message : String(parseErr);
|
|
151056
|
-
const envelope = { success: false, error: { code: "E_INVALID_INPUT", message } };
|
|
151057
|
-
if (opts.json) {
|
|
151058
|
-
process.stdout.write(JSON.stringify(envelope) + "\n");
|
|
151059
|
-
} else {
|
|
151060
|
-
process.stderr.write(`Invalid duration: ${message}
|
|
151061
|
-
`);
|
|
151062
|
-
}
|
|
151063
|
-
process.exit(2);
|
|
151064
|
-
return;
|
|
151065
|
-
}
|
|
151066
|
-
const projectsDir = opts.projectsDir ?? join142(homedir14(), ".claude", "projects");
|
|
151067
|
-
try {
|
|
151068
|
-
const pruneResult = await pruneTranscripts({
|
|
151069
|
-
olderThanMs,
|
|
151070
|
-
confirm,
|
|
151071
|
-
projectsDir
|
|
151072
|
-
});
|
|
151073
|
-
const envelope = {
|
|
151074
|
-
success: true,
|
|
151075
|
-
data: {
|
|
151076
|
-
pruned: pruneResult.pruned,
|
|
151077
|
-
bytesFreed: pruneResult.bytesFreed,
|
|
151078
|
-
mbFreed: parseFloat((pruneResult.bytesFreed / 1024 / 1024).toFixed(2)),
|
|
151079
|
-
dryRun: pruneResult.dryRun,
|
|
151080
|
-
deletedPaths: pruneResult.deletedPaths
|
|
151081
|
-
}
|
|
151082
|
-
};
|
|
151083
|
-
if (opts.json) {
|
|
151084
|
-
process.stdout.write(JSON.stringify(envelope) + "\n");
|
|
151085
|
-
} else {
|
|
151086
|
-
const dryLabel = pruneResult.dryRun ? " (dry run \u2014 use --confirm to delete)" : "";
|
|
151087
|
-
process.stdout.write(`Transcript prune${dryLabel}
|
|
151088
|
-
`);
|
|
151089
|
-
process.stdout.write(`Older than: ${opts.olderThan}
|
|
151090
|
-
`);
|
|
151091
|
-
process.stdout.write(`Sessions: ${pruneResult.pruned}
|
|
151092
|
-
`);
|
|
151093
|
-
process.stdout.write(
|
|
151094
|
-
`Bytes freed: ${(pruneResult.bytesFreed / 1024 / 1024).toFixed(1)} MB
|
|
151095
|
-
`
|
|
151096
|
-
);
|
|
151097
|
-
if (pruneResult.deletedPaths.length > 0 && pruneResult.dryRun) {
|
|
151098
|
-
process.stdout.write(`
|
|
151099
|
-
Would delete (${pruneResult.deletedPaths.length} paths):
|
|
151100
|
-
`);
|
|
151101
|
-
for (const p2 of pruneResult.deletedPaths.slice(0, 10)) {
|
|
151102
|
-
process.stdout.write(` ${p2}
|
|
151103
|
-
`);
|
|
151104
|
-
}
|
|
151105
|
-
if (pruneResult.deletedPaths.length > 10) {
|
|
151106
|
-
process.stdout.write(` ... and ${pruneResult.deletedPaths.length - 10} more
|
|
151107
|
-
`);
|
|
151108
|
-
}
|
|
151109
|
-
}
|
|
151110
|
-
}
|
|
151111
|
-
} catch (err) {
|
|
151112
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
151113
|
-
const envelope = { success: false, error: { code: "E_INTERNAL", message } };
|
|
151114
|
-
if (opts.json) {
|
|
151115
|
-
process.stdout.write(JSON.stringify(envelope) + "\n");
|
|
151116
|
-
} else {
|
|
151117
|
-
process.stderr.write(`Prune failed: ${message}
|
|
151118
|
-
`);
|
|
151119
|
-
}
|
|
151120
|
-
process.exit(1);
|
|
151121
|
-
}
|
|
151122
|
-
}
|
|
151123
|
-
);
|
|
151124
|
-
}
|
|
151125
|
-
|
|
151126
149797
|
// packages/cleo/src/cli/commands/code.ts
|
|
151127
149798
|
async function requireTreeSitter() {
|
|
151128
149799
|
const { isTreeSitterAvailable: isTreeSitterAvailable3 } = await Promise.resolve().then(() => (init_internal(), internal_exports));
|