@cleocode/cleo 2026.4.63 → 2026.4.65
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 +1379 -2667
- 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) {
|
|
@@ -13674,6 +13674,7 @@ __export(tasks_schema_exports, {
|
|
|
13674
13674
|
ADR_STATUSES: () => ADR_STATUSES,
|
|
13675
13675
|
AGENT_INSTANCE_STATUSES: () => AGENT_INSTANCE_STATUSES2,
|
|
13676
13676
|
AGENT_TYPES: () => AGENT_TYPES2,
|
|
13677
|
+
BACKGROUND_JOB_STATUSES: () => BACKGROUND_JOB_STATUSES,
|
|
13677
13678
|
EXTERNAL_LINK_TYPES: () => EXTERNAL_LINK_TYPES,
|
|
13678
13679
|
GATE_STATUSES: () => GATE_STATUSES,
|
|
13679
13680
|
LIFECYCLE_EVIDENCE_TYPES: () => LIFECYCLE_EVIDENCE_TYPES,
|
|
@@ -13699,6 +13700,7 @@ __export(tasks_schema_exports, {
|
|
|
13699
13700
|
agentInstances: () => agentInstances,
|
|
13700
13701
|
architectureDecisions: () => architectureDecisions,
|
|
13701
13702
|
auditLog: () => auditLog,
|
|
13703
|
+
backgroundJobs: () => backgroundJobs,
|
|
13702
13704
|
externalTaskLinks: () => externalTaskLinks,
|
|
13703
13705
|
isValidStatus: () => isValidStatus,
|
|
13704
13706
|
lifecycleEvidence: () => lifecycleEvidence,
|
|
@@ -13721,7 +13723,7 @@ __export(tasks_schema_exports, {
|
|
|
13721
13723
|
warpChains: () => warpChains
|
|
13722
13724
|
});
|
|
13723
13725
|
import { sql as sql6 } from "drizzle-orm";
|
|
13724
|
-
var TASK_PRIORITIES, TASK_TYPES, TASK_SIZES, LIFECYCLE_STAGE_NAMES, LIFECYCLE_GATE_RESULTS, LIFECYCLE_EVIDENCE_TYPES, TOKEN_USAGE_METHODS, TOKEN_USAGE_CONFIDENCE, TOKEN_USAGE_TRANSPORTS, TASK_RELATION_TYPES, LIFECYCLE_TRANSITION_TYPES, EXTERNAL_LINK_TYPES, SYNC_DIRECTIONS, tasks, taskDependencies, taskRelations, sessions, taskWorkHistory, lifecyclePipelines, lifecycleStages, lifecycleGateResults, lifecycleEvidence, lifecycleTransitions, manifestEntries, pipelineManifest, releaseManifests, schemaMeta, auditLog, tokenUsage, architectureDecisions, adrTaskLinks, adrRelations, externalTaskLinks, statusRegistryTable;
|
|
13726
|
+
var TASK_PRIORITIES, TASK_TYPES, TASK_SIZES, LIFECYCLE_STAGE_NAMES, LIFECYCLE_GATE_RESULTS, LIFECYCLE_EVIDENCE_TYPES, TOKEN_USAGE_METHODS, TOKEN_USAGE_CONFIDENCE, TOKEN_USAGE_TRANSPORTS, TASK_RELATION_TYPES, LIFECYCLE_TRANSITION_TYPES, EXTERNAL_LINK_TYPES, SYNC_DIRECTIONS, tasks, taskDependencies, taskRelations, sessions, taskWorkHistory, lifecyclePipelines, lifecycleStages, lifecycleGateResults, lifecycleEvidence, lifecycleTransitions, manifestEntries, pipelineManifest, releaseManifests, schemaMeta, auditLog, tokenUsage, architectureDecisions, adrTaskLinks, adrRelations, externalTaskLinks, BACKGROUND_JOB_STATUSES, backgroundJobs, statusRegistryTable;
|
|
13725
13727
|
var init_tasks_schema = __esm({
|
|
13726
13728
|
"packages/core/src/store/tasks-schema.ts"() {
|
|
13727
13729
|
"use strict";
|
|
@@ -14278,6 +14280,45 @@ var init_tasks_schema = __esm({
|
|
|
14278
14280
|
)
|
|
14279
14281
|
]
|
|
14280
14282
|
);
|
|
14283
|
+
BACKGROUND_JOB_STATUSES = [
|
|
14284
|
+
"pending",
|
|
14285
|
+
"running",
|
|
14286
|
+
"complete",
|
|
14287
|
+
"failed",
|
|
14288
|
+
"cancelled",
|
|
14289
|
+
"orphaned"
|
|
14290
|
+
];
|
|
14291
|
+
backgroundJobs = sqliteTable(
|
|
14292
|
+
"background_jobs",
|
|
14293
|
+
{
|
|
14294
|
+
/** Unique job identifier (UUID v4). */
|
|
14295
|
+
id: text("id").primaryKey(),
|
|
14296
|
+
/** Operation name, e.g. "nexus.analyze" or "tasks.sync.reconcile". */
|
|
14297
|
+
operation: text("operation").notNull(),
|
|
14298
|
+
/** Current lifecycle status. */
|
|
14299
|
+
status: text("status", { enum: BACKGROUND_JOB_STATUSES }).notNull().default("pending"),
|
|
14300
|
+
/** When the job was created (ms epoch). */
|
|
14301
|
+
startedAt: integer("started_at").notNull(),
|
|
14302
|
+
/** When the job finished (ms epoch); NULL while running. */
|
|
14303
|
+
completedAt: integer("completed_at"),
|
|
14304
|
+
/** JSON-serialised result payload; NULL on failure or while running. */
|
|
14305
|
+
result: text("result"),
|
|
14306
|
+
/** Human-readable error message; NULL on success or while running. */
|
|
14307
|
+
error: text("error"),
|
|
14308
|
+
/** Execution progress 0-100; NULL until progress is reported. */
|
|
14309
|
+
progress: integer("progress"),
|
|
14310
|
+
/** Last heartbeat timestamp (ms epoch). */
|
|
14311
|
+
heartbeatAt: integer("heartbeat_at").notNull(),
|
|
14312
|
+
/** Agent or session ID that claimed this job; NULL if unclaimed. */
|
|
14313
|
+
claimedBy: text("claimed_by")
|
|
14314
|
+
},
|
|
14315
|
+
(table) => [
|
|
14316
|
+
index("idx_background_jobs_status").on(table.status),
|
|
14317
|
+
index("idx_background_jobs_operation").on(table.operation),
|
|
14318
|
+
index("idx_background_jobs_claimed_by").on(table.claimedBy),
|
|
14319
|
+
index("idx_background_jobs_started_at").on(table.startedAt)
|
|
14320
|
+
]
|
|
14321
|
+
);
|
|
14281
14322
|
statusRegistryTable = sqliteTable(
|
|
14282
14323
|
"status_registry",
|
|
14283
14324
|
{
|
|
@@ -14478,10 +14519,10 @@ async function getDb(cwd) {
|
|
|
14478
14519
|
}
|
|
14479
14520
|
}
|
|
14480
14521
|
function resolveMigrationsFolder() {
|
|
14481
|
-
const
|
|
14482
|
-
const
|
|
14483
|
-
const isBundled =
|
|
14484
|
-
const pkgRoot = isBundled ? join10(
|
|
14522
|
+
const __filename = fileURLToPath2(import.meta.url);
|
|
14523
|
+
const __dirname = dirname5(__filename);
|
|
14524
|
+
const isBundled = __dirname.endsWith("/dist") || __dirname.endsWith("\\dist");
|
|
14525
|
+
const pkgRoot = isBundled ? join10(__dirname, "..") : join10(__dirname, "..", "..");
|
|
14485
14526
|
return join10(pkgRoot, "migrations", "drizzle-tasks");
|
|
14486
14527
|
}
|
|
14487
14528
|
function runMigrations(nativeDb, db) {
|
|
@@ -15341,10 +15382,10 @@ function getBrainDbPath(cwd) {
|
|
|
15341
15382
|
return join12(getCleoDirAbsolute(cwd), DB_FILENAME3);
|
|
15342
15383
|
}
|
|
15343
15384
|
function resolveBrainMigrationsFolder() {
|
|
15344
|
-
const
|
|
15345
|
-
const
|
|
15346
|
-
const isBundled =
|
|
15347
|
-
const pkgRoot = isBundled ? join12(
|
|
15385
|
+
const __filename = fileURLToPath3(import.meta.url);
|
|
15386
|
+
const __dirname = dirname8(__filename);
|
|
15387
|
+
const isBundled = __dirname.endsWith("/dist") || __dirname.endsWith("\\dist");
|
|
15388
|
+
const pkgRoot = isBundled ? join12(__dirname, "..") : join12(__dirname, "..", "..");
|
|
15348
15389
|
return join12(pkgRoot, "migrations", "drizzle-brain");
|
|
15349
15390
|
}
|
|
15350
15391
|
function runBrainMigrations(nativeDb, db) {
|
|
@@ -48115,6 +48156,9 @@ async function callLlm(systemPrompt, userContent) {
|
|
|
48115
48156
|
try {
|
|
48116
48157
|
const response = await fetch("https://api.anthropic.com/v1/messages", {
|
|
48117
48158
|
method: "POST",
|
|
48159
|
+
// 30-second hard deadline prevents open network handles from blocking
|
|
48160
|
+
// test process teardown (T753: vitest hang root cause).
|
|
48161
|
+
signal: AbortSignal.timeout(3e4),
|
|
48118
48162
|
headers: {
|
|
48119
48163
|
"Content-Type": "application/json",
|
|
48120
48164
|
"x-api-key": apiKey,
|
|
@@ -84366,8 +84410,8 @@ function getTelemetryDbPath() {
|
|
|
84366
84410
|
return join101(getCleoHome(), DB_FILENAME4);
|
|
84367
84411
|
}
|
|
84368
84412
|
function resolveTelemetryMigrationsFolder() {
|
|
84369
|
-
const
|
|
84370
|
-
const __dir = dirname20(
|
|
84413
|
+
const __filename = fileURLToPath6(import.meta.url);
|
|
84414
|
+
const __dir = dirname20(__filename);
|
|
84371
84415
|
const isBundled = __dir.endsWith("/dist") || __dir.endsWith("\\dist");
|
|
84372
84416
|
const pkgRoot = isBundled ? join101(__dir, "..") : join101(__dir, "..", "..");
|
|
84373
84417
|
return join101(pkgRoot, "migrations", "drizzle-telemetry");
|
|
@@ -90228,6 +90272,7 @@ var init_cleo = __esm({
|
|
|
90228
90272
|
// packages/core/src/index.ts
|
|
90229
90273
|
var init_src3 = __esm({
|
|
90230
90274
|
"packages/core/src/index.ts"() {
|
|
90275
|
+
"use strict";
|
|
90231
90276
|
init_src();
|
|
90232
90277
|
init_adapters();
|
|
90233
90278
|
init_admin();
|
|
@@ -134282,1350 +134327,6 @@ var init_cli = __esm({
|
|
|
134282
134327
|
}
|
|
134283
134328
|
});
|
|
134284
134329
|
|
|
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
134330
|
// packages/cleo/src/cli/index.ts
|
|
135630
134331
|
init_internal();
|
|
135631
134332
|
import { readFileSync as readFileSync107 } from "node:fs";
|
|
@@ -141103,6 +139804,566 @@ function registerCurrentCommand(program) {
|
|
|
141103
139804
|
});
|
|
141104
139805
|
}
|
|
141105
139806
|
|
|
139807
|
+
// packages/cleo/src/cli/commands/daemon.ts
|
|
139808
|
+
import { homedir as homedir10 } from "node:os";
|
|
139809
|
+
import { join as join133 } from "node:path";
|
|
139810
|
+
|
|
139811
|
+
// packages/cleo/src/gc/daemon.ts
|
|
139812
|
+
import { spawn as spawn2 } from "node:child_process";
|
|
139813
|
+
import { createWriteStream as createWriteStream2 } from "node:fs";
|
|
139814
|
+
import { mkdir as mkdir20 } from "node:fs/promises";
|
|
139815
|
+
import { join as join132 } from "node:path";
|
|
139816
|
+
import { fileURLToPath as fileURLToPath8 } from "node:url";
|
|
139817
|
+
import cron from "node-cron";
|
|
139818
|
+
|
|
139819
|
+
// packages/cleo/src/gc/runner.ts
|
|
139820
|
+
import { lstat as lstat2, readdir as readdir4, rm as rm3, stat as stat4 } from "node:fs/promises";
|
|
139821
|
+
import { homedir as homedir9 } from "node:os";
|
|
139822
|
+
import { join as join131 } from "node:path";
|
|
139823
|
+
|
|
139824
|
+
// node_modules/.pnpm/check-disk-space@3.4.0/node_modules/check-disk-space/dist/check-disk-space.mjs
|
|
139825
|
+
import { execFile as execFile7 } from "node:child_process";
|
|
139826
|
+
import { access as access4 } from "node:fs/promises";
|
|
139827
|
+
import { release as release2 } from "node:os";
|
|
139828
|
+
import { normalize as normalize2, sep as sep2 } from "node:path";
|
|
139829
|
+
import { platform as platform5 } from "node:process";
|
|
139830
|
+
import { promisify as promisify7 } from "node:util";
|
|
139831
|
+
var InvalidPathError = class _InvalidPathError extends Error {
|
|
139832
|
+
constructor(message) {
|
|
139833
|
+
super(message);
|
|
139834
|
+
this.name = "InvalidPathError";
|
|
139835
|
+
Object.setPrototypeOf(this, _InvalidPathError.prototype);
|
|
139836
|
+
}
|
|
139837
|
+
};
|
|
139838
|
+
var NoMatchError = class _NoMatchError extends Error {
|
|
139839
|
+
constructor(message) {
|
|
139840
|
+
super(message);
|
|
139841
|
+
this.name = "NoMatchError";
|
|
139842
|
+
Object.setPrototypeOf(this, _NoMatchError.prototype);
|
|
139843
|
+
}
|
|
139844
|
+
};
|
|
139845
|
+
async function isDirectoryExisting(directoryPath, dependencies) {
|
|
139846
|
+
try {
|
|
139847
|
+
await dependencies.fsAccess(directoryPath);
|
|
139848
|
+
return Promise.resolve(true);
|
|
139849
|
+
} catch (error48) {
|
|
139850
|
+
return Promise.resolve(false);
|
|
139851
|
+
}
|
|
139852
|
+
}
|
|
139853
|
+
async function getFirstExistingParentPath(directoryPath, dependencies) {
|
|
139854
|
+
let parentDirectoryPath = directoryPath;
|
|
139855
|
+
let parentDirectoryFound = await isDirectoryExisting(parentDirectoryPath, dependencies);
|
|
139856
|
+
while (!parentDirectoryFound) {
|
|
139857
|
+
parentDirectoryPath = dependencies.pathNormalize(parentDirectoryPath + "/..");
|
|
139858
|
+
parentDirectoryFound = await isDirectoryExisting(parentDirectoryPath, dependencies);
|
|
139859
|
+
}
|
|
139860
|
+
return parentDirectoryPath;
|
|
139861
|
+
}
|
|
139862
|
+
async function hasPowerShell3(dependencies) {
|
|
139863
|
+
const major = parseInt(dependencies.release.split(".")[0], 10);
|
|
139864
|
+
if (major <= 6) {
|
|
139865
|
+
return false;
|
|
139866
|
+
}
|
|
139867
|
+
try {
|
|
139868
|
+
await dependencies.cpExecFile("where", ["powershell"], { windowsHide: true });
|
|
139869
|
+
return true;
|
|
139870
|
+
} catch (error48) {
|
|
139871
|
+
return false;
|
|
139872
|
+
}
|
|
139873
|
+
}
|
|
139874
|
+
function checkDiskSpace(directoryPath, dependencies = {
|
|
139875
|
+
platform: platform5,
|
|
139876
|
+
release: release2(),
|
|
139877
|
+
fsAccess: access4,
|
|
139878
|
+
pathNormalize: normalize2,
|
|
139879
|
+
pathSep: sep2,
|
|
139880
|
+
cpExecFile: promisify7(execFile7)
|
|
139881
|
+
}) {
|
|
139882
|
+
function mapOutput(stdout2, filter, mapping, coefficient) {
|
|
139883
|
+
const parsed = stdout2.split("\n").map((line2) => line2.trim()).filter((line2) => line2.length !== 0).slice(1).map((line2) => line2.split(/\s+(?=[\d/])/));
|
|
139884
|
+
const filtered = parsed.filter(filter);
|
|
139885
|
+
if (filtered.length === 0) {
|
|
139886
|
+
throw new NoMatchError();
|
|
139887
|
+
}
|
|
139888
|
+
const diskData = filtered[0];
|
|
139889
|
+
return {
|
|
139890
|
+
diskPath: diskData[mapping.diskPath],
|
|
139891
|
+
free: parseInt(diskData[mapping.free], 10) * coefficient,
|
|
139892
|
+
size: parseInt(diskData[mapping.size], 10) * coefficient
|
|
139893
|
+
};
|
|
139894
|
+
}
|
|
139895
|
+
async function check2(cmd, filter, mapping, coefficient = 1) {
|
|
139896
|
+
const [file2, ...args] = cmd;
|
|
139897
|
+
if (file2 === void 0) {
|
|
139898
|
+
return Promise.reject(new Error("cmd must contain at least one item"));
|
|
139899
|
+
}
|
|
139900
|
+
try {
|
|
139901
|
+
const { stdout: stdout2 } = await dependencies.cpExecFile(file2, args, { windowsHide: true });
|
|
139902
|
+
return mapOutput(stdout2, filter, mapping, coefficient);
|
|
139903
|
+
} catch (error48) {
|
|
139904
|
+
return Promise.reject(error48);
|
|
139905
|
+
}
|
|
139906
|
+
}
|
|
139907
|
+
async function checkWin32(directoryPath2) {
|
|
139908
|
+
if (directoryPath2.charAt(1) !== ":") {
|
|
139909
|
+
return Promise.reject(new InvalidPathError(`The following path is invalid (should be X:\\...): ${directoryPath2}`));
|
|
139910
|
+
}
|
|
139911
|
+
const powershellCmd = [
|
|
139912
|
+
"powershell",
|
|
139913
|
+
"Get-CimInstance -ClassName Win32_LogicalDisk | Select-Object Caption, FreeSpace, Size"
|
|
139914
|
+
];
|
|
139915
|
+
const wmicCmd = [
|
|
139916
|
+
"wmic",
|
|
139917
|
+
"logicaldisk",
|
|
139918
|
+
"get",
|
|
139919
|
+
"size,freespace,caption"
|
|
139920
|
+
];
|
|
139921
|
+
const cmd = await hasPowerShell3(dependencies) ? powershellCmd : wmicCmd;
|
|
139922
|
+
return check2(cmd, (driveData) => {
|
|
139923
|
+
const driveLetter = driveData[0];
|
|
139924
|
+
return directoryPath2.toUpperCase().startsWith(driveLetter.toUpperCase());
|
|
139925
|
+
}, {
|
|
139926
|
+
diskPath: 0,
|
|
139927
|
+
free: 1,
|
|
139928
|
+
size: 2
|
|
139929
|
+
});
|
|
139930
|
+
}
|
|
139931
|
+
async function checkUnix(directoryPath2) {
|
|
139932
|
+
if (!dependencies.pathNormalize(directoryPath2).startsWith(dependencies.pathSep)) {
|
|
139933
|
+
return Promise.reject(new InvalidPathError(`The following path is invalid (should start by ${dependencies.pathSep}): ${directoryPath2}`));
|
|
139934
|
+
}
|
|
139935
|
+
const pathToCheck = await getFirstExistingParentPath(directoryPath2, dependencies);
|
|
139936
|
+
return check2(
|
|
139937
|
+
[
|
|
139938
|
+
"df",
|
|
139939
|
+
"-Pk",
|
|
139940
|
+
"--",
|
|
139941
|
+
pathToCheck
|
|
139942
|
+
],
|
|
139943
|
+
() => true,
|
|
139944
|
+
// We should only get one line, so we did not need to filter
|
|
139945
|
+
{
|
|
139946
|
+
diskPath: 5,
|
|
139947
|
+
free: 3,
|
|
139948
|
+
size: 1
|
|
139949
|
+
},
|
|
139950
|
+
1024
|
|
139951
|
+
);
|
|
139952
|
+
}
|
|
139953
|
+
if (dependencies.platform === "win32") {
|
|
139954
|
+
return checkWin32(directoryPath);
|
|
139955
|
+
}
|
|
139956
|
+
return checkUnix(directoryPath);
|
|
139957
|
+
}
|
|
139958
|
+
|
|
139959
|
+
// packages/cleo/src/gc/state.ts
|
|
139960
|
+
import { mkdir as mkdir19, readFile as readFile22, rename as rename2, writeFile as writeFile14 } from "node:fs/promises";
|
|
139961
|
+
import { dirname as dirname29, join as join130 } from "node:path";
|
|
139962
|
+
var GC_STATE_SCHEMA_VERSION = "1.0";
|
|
139963
|
+
var DEFAULT_GC_STATE = {
|
|
139964
|
+
schemaVersion: GC_STATE_SCHEMA_VERSION,
|
|
139965
|
+
lastRunAt: null,
|
|
139966
|
+
lastRunResult: null,
|
|
139967
|
+
lastRunBytesFreed: 0,
|
|
139968
|
+
pendingPrune: null,
|
|
139969
|
+
consecutiveFailures: 0,
|
|
139970
|
+
diskThresholdBreached: false,
|
|
139971
|
+
lastDiskUsedPct: null,
|
|
139972
|
+
escalationNeeded: false,
|
|
139973
|
+
escalationReason: null,
|
|
139974
|
+
daemonPid: null,
|
|
139975
|
+
daemonStartedAt: null
|
|
139976
|
+
};
|
|
139977
|
+
async function readGCState(statePath) {
|
|
139978
|
+
try {
|
|
139979
|
+
const raw = await readFile22(statePath, "utf-8");
|
|
139980
|
+
const parsed = JSON.parse(raw);
|
|
139981
|
+
return { ...DEFAULT_GC_STATE, ...parsed };
|
|
139982
|
+
} catch {
|
|
139983
|
+
return { ...DEFAULT_GC_STATE };
|
|
139984
|
+
}
|
|
139985
|
+
}
|
|
139986
|
+
async function writeGCState(statePath, state) {
|
|
139987
|
+
const dir = dirname29(statePath);
|
|
139988
|
+
await mkdir19(dir, { recursive: true });
|
|
139989
|
+
const tmpPath = join130(dir, `.gc-state-${process.pid}.tmp`);
|
|
139990
|
+
const json3 = JSON.stringify(state, null, 2);
|
|
139991
|
+
await writeFile14(tmpPath, json3, "utf-8");
|
|
139992
|
+
await rename2(tmpPath, statePath);
|
|
139993
|
+
}
|
|
139994
|
+
async function patchGCState(statePath, patch) {
|
|
139995
|
+
const current = await readGCState(statePath);
|
|
139996
|
+
const updated = { ...current, ...patch };
|
|
139997
|
+
await writeGCState(statePath, updated);
|
|
139998
|
+
return updated;
|
|
139999
|
+
}
|
|
140000
|
+
|
|
140001
|
+
// packages/cleo/src/gc/runner.ts
|
|
140002
|
+
var DISK_THRESHOLDS = {
|
|
140003
|
+
WATCH: 70,
|
|
140004
|
+
WARN: 85,
|
|
140005
|
+
URGENT: 90,
|
|
140006
|
+
EMERGENCY: 95
|
|
140007
|
+
};
|
|
140008
|
+
function classifyDiskTier(pct) {
|
|
140009
|
+
if (pct >= DISK_THRESHOLDS.EMERGENCY) return "emergency";
|
|
140010
|
+
if (pct >= DISK_THRESHOLDS.URGENT) return "urgent";
|
|
140011
|
+
if (pct >= DISK_THRESHOLDS.WARN) return "warn";
|
|
140012
|
+
if (pct >= DISK_THRESHOLDS.WATCH) return "watch";
|
|
140013
|
+
return "ok";
|
|
140014
|
+
}
|
|
140015
|
+
function retentionMs(tier) {
|
|
140016
|
+
switch (tier) {
|
|
140017
|
+
case "emergency":
|
|
140018
|
+
return 1 * 24 * 60 * 60 * 1e3;
|
|
140019
|
+
// 1 day
|
|
140020
|
+
case "urgent":
|
|
140021
|
+
return 3 * 24 * 60 * 60 * 1e3;
|
|
140022
|
+
// 3 days
|
|
140023
|
+
case "warn":
|
|
140024
|
+
return 7 * 24 * 60 * 60 * 1e3;
|
|
140025
|
+
// 7 days
|
|
140026
|
+
default:
|
|
140027
|
+
return 30 * 24 * 60 * 60 * 1e3;
|
|
140028
|
+
}
|
|
140029
|
+
}
|
|
140030
|
+
async function getPathBytes(targetPath) {
|
|
140031
|
+
try {
|
|
140032
|
+
const info = await lstat2(targetPath);
|
|
140033
|
+
if (info.isFile()) return info.size;
|
|
140034
|
+
if (!info.isDirectory()) return 0;
|
|
140035
|
+
const entries = await readdir4(targetPath, { withFileTypes: true });
|
|
140036
|
+
let total = 0;
|
|
140037
|
+
for (const entry of entries) {
|
|
140038
|
+
total += await getPathBytes(join131(targetPath, entry.name));
|
|
140039
|
+
}
|
|
140040
|
+
return total;
|
|
140041
|
+
} catch {
|
|
140042
|
+
return 0;
|
|
140043
|
+
}
|
|
140044
|
+
}
|
|
140045
|
+
async function idempotentRm(targetPath) {
|
|
140046
|
+
try {
|
|
140047
|
+
await rm3(targetPath, { recursive: true, force: true });
|
|
140048
|
+
} catch (err) {
|
|
140049
|
+
const nodeErr = err;
|
|
140050
|
+
if (nodeErr.code === "ENOENT") return;
|
|
140051
|
+
throw err;
|
|
140052
|
+
}
|
|
140053
|
+
}
|
|
140054
|
+
async function gatherPruneCandidates(maxAgeMs, projectsDir) {
|
|
140055
|
+
const resolvedProjectsDir = projectsDir ?? join131(homedir9(), ".claude", "projects");
|
|
140056
|
+
const candidates = [];
|
|
140057
|
+
const now2 = Date.now();
|
|
140058
|
+
let projectSlugs;
|
|
140059
|
+
try {
|
|
140060
|
+
const entries = await readdir4(resolvedProjectsDir, { withFileTypes: true });
|
|
140061
|
+
projectSlugs = entries.filter((e) => e.isDirectory()).map((e) => e.name);
|
|
140062
|
+
} catch {
|
|
140063
|
+
return candidates;
|
|
140064
|
+
}
|
|
140065
|
+
for (const slug of projectSlugs) {
|
|
140066
|
+
const slugDir = join131(resolvedProjectsDir, slug);
|
|
140067
|
+
let slugEntries;
|
|
140068
|
+
try {
|
|
140069
|
+
slugEntries = await readdir4(slugDir, { withFileTypes: true });
|
|
140070
|
+
} catch {
|
|
140071
|
+
continue;
|
|
140072
|
+
}
|
|
140073
|
+
for (const entry of slugEntries) {
|
|
140074
|
+
const entryPath = join131(slugDir, entry.name);
|
|
140075
|
+
if (entry.isFile() && entry.name.endsWith(".jsonl")) {
|
|
140076
|
+
try {
|
|
140077
|
+
const info = await stat4(entryPath);
|
|
140078
|
+
const ageMs = now2 - info.mtimeMs;
|
|
140079
|
+
if (ageMs > maxAgeMs) {
|
|
140080
|
+
candidates.push(entryPath);
|
|
140081
|
+
}
|
|
140082
|
+
} catch {
|
|
140083
|
+
}
|
|
140084
|
+
} else if (entry.isDirectory()) {
|
|
140085
|
+
try {
|
|
140086
|
+
const info = await stat4(entryPath);
|
|
140087
|
+
const ageMs = now2 - info.mtimeMs;
|
|
140088
|
+
if (ageMs > maxAgeMs) {
|
|
140089
|
+
candidates.push(entryPath);
|
|
140090
|
+
}
|
|
140091
|
+
} catch {
|
|
140092
|
+
}
|
|
140093
|
+
}
|
|
140094
|
+
}
|
|
140095
|
+
}
|
|
140096
|
+
return candidates;
|
|
140097
|
+
}
|
|
140098
|
+
async function runGC(opts = {}) {
|
|
140099
|
+
const cleoDir = opts.cleoDir ?? join131(homedir9(), ".cleo");
|
|
140100
|
+
const statePath = join131(cleoDir, "gc-state.json");
|
|
140101
|
+
const dryRun = opts.dryRun ?? false;
|
|
140102
|
+
const projectsDir = opts.projectsDir;
|
|
140103
|
+
const initialState = await readGCState(statePath);
|
|
140104
|
+
const resumePaths = opts.resumeFrom ?? initialState.pendingPrune ?? [];
|
|
140105
|
+
let diskUsedPct = 0;
|
|
140106
|
+
try {
|
|
140107
|
+
const { free, size } = await checkDiskSpace(cleoDir);
|
|
140108
|
+
diskUsedPct = size > 0 ? (size - free) / size * 100 : 0;
|
|
140109
|
+
} catch {
|
|
140110
|
+
diskUsedPct = 0;
|
|
140111
|
+
}
|
|
140112
|
+
const tier = classifyDiskTier(diskUsedPct);
|
|
140113
|
+
const maxAgeMs = retentionMs(tier);
|
|
140114
|
+
const candidatesFromScan = resumePaths.length > 0 ? resumePaths : await gatherPruneCandidates(maxAgeMs, projectsDir);
|
|
140115
|
+
if (!dryRun && candidatesFromScan.length > 0) {
|
|
140116
|
+
await patchGCState(statePath, { pendingPrune: candidatesFromScan });
|
|
140117
|
+
}
|
|
140118
|
+
const pruned = [];
|
|
140119
|
+
let bytesFreed = 0;
|
|
140120
|
+
const remaining = [...candidatesFromScan];
|
|
140121
|
+
for (const candidatePath of candidatesFromScan) {
|
|
140122
|
+
const bytes = await getPathBytes(candidatePath);
|
|
140123
|
+
if (dryRun) {
|
|
140124
|
+
pruned.push({ path: candidatePath, bytes });
|
|
140125
|
+
bytesFreed += bytes;
|
|
140126
|
+
continue;
|
|
140127
|
+
}
|
|
140128
|
+
try {
|
|
140129
|
+
await idempotentRm(candidatePath);
|
|
140130
|
+
pruned.push({ path: candidatePath, bytes });
|
|
140131
|
+
bytesFreed += bytes;
|
|
140132
|
+
const idx = remaining.indexOf(candidatePath);
|
|
140133
|
+
if (idx !== -1) remaining.splice(idx, 1);
|
|
140134
|
+
await patchGCState(statePath, {
|
|
140135
|
+
pendingPrune: remaining.length > 0 ? remaining : null
|
|
140136
|
+
});
|
|
140137
|
+
} catch {
|
|
140138
|
+
}
|
|
140139
|
+
}
|
|
140140
|
+
const escalationSet = tier === "warn" || tier === "urgent" || tier === "emergency";
|
|
140141
|
+
let escalationReason = null;
|
|
140142
|
+
if (escalationSet) {
|
|
140143
|
+
escalationReason = `Disk at ${diskUsedPct.toFixed(1)}% (${tier.toUpperCase()}): ${pruned.length} paths pruned, ${bytesFreed} bytes freed`;
|
|
140144
|
+
}
|
|
140145
|
+
const completedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
140146
|
+
if (!dryRun) {
|
|
140147
|
+
await patchGCState(statePath, {
|
|
140148
|
+
lastRunAt: completedAt,
|
|
140149
|
+
lastRunResult: remaining.length === 0 ? "success" : "partial",
|
|
140150
|
+
lastRunBytesFreed: bytesFreed,
|
|
140151
|
+
pendingPrune: remaining.length > 0 ? remaining : null,
|
|
140152
|
+
consecutiveFailures: remaining.length > 0 ? initialState.consecutiveFailures + 1 : 0,
|
|
140153
|
+
diskThresholdBreached: diskUsedPct >= DISK_THRESHOLDS.WATCH,
|
|
140154
|
+
lastDiskUsedPct: diskUsedPct,
|
|
140155
|
+
escalationNeeded: escalationSet || initialState.escalationNeeded,
|
|
140156
|
+
escalationReason: escalationReason ?? initialState.escalationReason
|
|
140157
|
+
});
|
|
140158
|
+
}
|
|
140159
|
+
return {
|
|
140160
|
+
diskUsedPct,
|
|
140161
|
+
threshold: tier,
|
|
140162
|
+
pruned,
|
|
140163
|
+
bytesFreed,
|
|
140164
|
+
escalationSet,
|
|
140165
|
+
escalationReason,
|
|
140166
|
+
completedAt
|
|
140167
|
+
};
|
|
140168
|
+
}
|
|
140169
|
+
|
|
140170
|
+
// packages/cleo/src/gc/daemon.ts
|
|
140171
|
+
var GC_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
140172
|
+
async function spawnGCDaemon(cleoDir) {
|
|
140173
|
+
const logsDir = join132(cleoDir, "logs");
|
|
140174
|
+
await mkdir20(logsDir, { recursive: true });
|
|
140175
|
+
const logPath = join132(logsDir, "gc.log");
|
|
140176
|
+
const errPath = join132(logsDir, "gc.err");
|
|
140177
|
+
const outStream = createWriteStream2(logPath, { flags: "a" });
|
|
140178
|
+
const errStream = createWriteStream2(errPath, { flags: "a" });
|
|
140179
|
+
const daemonEntry = join132(fileURLToPath8(import.meta.url), "..", "daemon-entry.js");
|
|
140180
|
+
const child = spawn2(process.execPath, [daemonEntry, cleoDir], {
|
|
140181
|
+
detached: true,
|
|
140182
|
+
stdio: ["ignore", outStream, errStream],
|
|
140183
|
+
env: { ...process.env, CLEO_GC_DAEMON: "1" }
|
|
140184
|
+
});
|
|
140185
|
+
child.unref();
|
|
140186
|
+
const pid = child.pid ?? 0;
|
|
140187
|
+
await patchGCState(join132(cleoDir, "gc-state.json"), {
|
|
140188
|
+
daemonPid: pid,
|
|
140189
|
+
daemonStartedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
140190
|
+
});
|
|
140191
|
+
return pid;
|
|
140192
|
+
}
|
|
140193
|
+
async function stopGCDaemon(cleoDir) {
|
|
140194
|
+
const statePath = join132(cleoDir, "gc-state.json");
|
|
140195
|
+
const state = await readGCState(statePath);
|
|
140196
|
+
const pid = state.daemonPid;
|
|
140197
|
+
if (!pid) {
|
|
140198
|
+
return { stopped: false, pid: null, reason: "Daemon PID not found in gc-state.json" };
|
|
140199
|
+
}
|
|
140200
|
+
try {
|
|
140201
|
+
process.kill(pid, 0);
|
|
140202
|
+
} catch {
|
|
140203
|
+
await patchGCState(statePath, { daemonPid: null });
|
|
140204
|
+
return {
|
|
140205
|
+
stopped: false,
|
|
140206
|
+
pid,
|
|
140207
|
+
reason: `Daemon PID ${pid} is not running (stale state cleared)`
|
|
140208
|
+
};
|
|
140209
|
+
}
|
|
140210
|
+
try {
|
|
140211
|
+
process.kill(pid, "SIGTERM");
|
|
140212
|
+
await patchGCState(statePath, { daemonPid: null });
|
|
140213
|
+
return { stopped: true, pid, reason: `SIGTERM sent to PID ${pid}` };
|
|
140214
|
+
} catch (err) {
|
|
140215
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
140216
|
+
return { stopped: false, pid, reason: `Failed to send SIGTERM to PID ${pid}: ${msg}` };
|
|
140217
|
+
}
|
|
140218
|
+
}
|
|
140219
|
+
async function getGCDaemonStatus(cleoDir) {
|
|
140220
|
+
const state = await readGCState(join132(cleoDir, "gc-state.json"));
|
|
140221
|
+
const pid = state.daemonPid;
|
|
140222
|
+
let running = false;
|
|
140223
|
+
if (pid) {
|
|
140224
|
+
try {
|
|
140225
|
+
process.kill(pid, 0);
|
|
140226
|
+
running = true;
|
|
140227
|
+
} catch {
|
|
140228
|
+
running = false;
|
|
140229
|
+
}
|
|
140230
|
+
}
|
|
140231
|
+
return {
|
|
140232
|
+
running,
|
|
140233
|
+
pid: running ? pid : null,
|
|
140234
|
+
startedAt: state.daemonStartedAt,
|
|
140235
|
+
lastRunAt: state.lastRunAt,
|
|
140236
|
+
lastDiskUsedPct: state.lastDiskUsedPct,
|
|
140237
|
+
escalationNeeded: state.escalationNeeded
|
|
140238
|
+
};
|
|
140239
|
+
}
|
|
140240
|
+
|
|
140241
|
+
// packages/cleo/src/cli/commands/daemon.ts
|
|
140242
|
+
function registerDaemonCommand(program) {
|
|
140243
|
+
const daemon = program.command("daemon").description("Manage the CLEO GC sidecar daemon for autonomous transcript cleanup");
|
|
140244
|
+
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) => {
|
|
140245
|
+
const cleoDir = opts.cleoDir ?? join133(homedir10(), ".cleo");
|
|
140246
|
+
try {
|
|
140247
|
+
const status = await getGCDaemonStatus(cleoDir);
|
|
140248
|
+
if (status.running && status.pid) {
|
|
140249
|
+
const result2 = {
|
|
140250
|
+
success: false,
|
|
140251
|
+
data: {
|
|
140252
|
+
running: true,
|
|
140253
|
+
pid: status.pid,
|
|
140254
|
+
message: `Daemon already running (PID ${status.pid})`
|
|
140255
|
+
}
|
|
140256
|
+
};
|
|
140257
|
+
if (opts.json) {
|
|
140258
|
+
process.stdout.write(JSON.stringify(result2) + "\n");
|
|
140259
|
+
} else {
|
|
140260
|
+
process.stdout.write(`Daemon already running (PID ${status.pid})
|
|
140261
|
+
`);
|
|
140262
|
+
}
|
|
140263
|
+
return;
|
|
140264
|
+
}
|
|
140265
|
+
const pid = await spawnGCDaemon(cleoDir);
|
|
140266
|
+
const result = {
|
|
140267
|
+
success: true,
|
|
140268
|
+
data: { pid, cleoDir, message: `GC daemon started (PID ${pid})` }
|
|
140269
|
+
};
|
|
140270
|
+
if (opts.json) {
|
|
140271
|
+
process.stdout.write(JSON.stringify(result) + "\n");
|
|
140272
|
+
} else {
|
|
140273
|
+
process.stdout.write(`GC daemon started (PID ${pid})
|
|
140274
|
+
`);
|
|
140275
|
+
process.stdout.write(`Logs: ${join133(cleoDir, "logs", "gc.log")}
|
|
140276
|
+
`);
|
|
140277
|
+
}
|
|
140278
|
+
} catch (err) {
|
|
140279
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
140280
|
+
const result = { success: false, error: { code: "E_INTERNAL", message } };
|
|
140281
|
+
if (opts.json) {
|
|
140282
|
+
process.stdout.write(JSON.stringify(result) + "\n");
|
|
140283
|
+
} else {
|
|
140284
|
+
process.stderr.write(`Error starting daemon: ${message}
|
|
140285
|
+
`);
|
|
140286
|
+
}
|
|
140287
|
+
process.exit(1);
|
|
140288
|
+
}
|
|
140289
|
+
});
|
|
140290
|
+
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) => {
|
|
140291
|
+
const cleoDir = opts.cleoDir ?? join133(homedir10(), ".cleo");
|
|
140292
|
+
try {
|
|
140293
|
+
const stopResult = await stopGCDaemon(cleoDir);
|
|
140294
|
+
const result = {
|
|
140295
|
+
success: stopResult.stopped,
|
|
140296
|
+
data: stopResult
|
|
140297
|
+
};
|
|
140298
|
+
if (opts.json) {
|
|
140299
|
+
process.stdout.write(JSON.stringify(result) + "\n");
|
|
140300
|
+
} else if (stopResult.stopped) {
|
|
140301
|
+
process.stdout.write(`GC daemon stopped (${stopResult.reason})
|
|
140302
|
+
`);
|
|
140303
|
+
} else {
|
|
140304
|
+
process.stdout.write(`${stopResult.reason}
|
|
140305
|
+
`);
|
|
140306
|
+
}
|
|
140307
|
+
} catch (err) {
|
|
140308
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
140309
|
+
const result = { success: false, error: { code: "E_INTERNAL", message } };
|
|
140310
|
+
if (opts.json) {
|
|
140311
|
+
process.stdout.write(JSON.stringify(result) + "\n");
|
|
140312
|
+
} else {
|
|
140313
|
+
process.stderr.write(`Error stopping daemon: ${message}
|
|
140314
|
+
`);
|
|
140315
|
+
}
|
|
140316
|
+
process.exit(1);
|
|
140317
|
+
}
|
|
140318
|
+
});
|
|
140319
|
+
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) => {
|
|
140320
|
+
const cleoDir = opts.cleoDir ?? join133(homedir10(), ".cleo");
|
|
140321
|
+
await showDaemonStatus(cleoDir, opts.json ?? false);
|
|
140322
|
+
});
|
|
140323
|
+
daemon.action(async (opts) => {
|
|
140324
|
+
const cleoDir = opts.cleoDir ?? join133(homedir10(), ".cleo");
|
|
140325
|
+
await showDaemonStatus(cleoDir, opts.json ?? false);
|
|
140326
|
+
});
|
|
140327
|
+
}
|
|
140328
|
+
async function showDaemonStatus(cleoDir, json3) {
|
|
140329
|
+
try {
|
|
140330
|
+
const status = await getGCDaemonStatus(cleoDir);
|
|
140331
|
+
const result = { success: true, data: status };
|
|
140332
|
+
if (json3) {
|
|
140333
|
+
process.stdout.write(JSON.stringify(result) + "\n");
|
|
140334
|
+
} else {
|
|
140335
|
+
const runningStr = status.running ? `running (PID ${status.pid})` : "stopped";
|
|
140336
|
+
process.stdout.write(`Daemon: ${runningStr}
|
|
140337
|
+
`);
|
|
140338
|
+
process.stdout.write(`Started at: ${status.startedAt ?? "never"}
|
|
140339
|
+
`);
|
|
140340
|
+
process.stdout.write(`Last GC run: ${status.lastRunAt ?? "never"}
|
|
140341
|
+
`);
|
|
140342
|
+
const diskStr = status.lastDiskUsedPct !== null ? `${status.lastDiskUsedPct.toFixed(1)}%` : "unknown";
|
|
140343
|
+
process.stdout.write(`Disk used: ${diskStr}
|
|
140344
|
+
`);
|
|
140345
|
+
if (status.escalationNeeded) {
|
|
140346
|
+
process.stdout.write(
|
|
140347
|
+
`
|
|
140348
|
+
WARNING: Disk threshold breached. Run 'cleo gc run' to reclaim space.
|
|
140349
|
+
`
|
|
140350
|
+
);
|
|
140351
|
+
}
|
|
140352
|
+
}
|
|
140353
|
+
} catch (err) {
|
|
140354
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
140355
|
+
if (json3) {
|
|
140356
|
+
process.stdout.write(
|
|
140357
|
+
JSON.stringify({ success: false, error: { code: "E_INTERNAL", message } }) + "\n"
|
|
140358
|
+
);
|
|
140359
|
+
} else {
|
|
140360
|
+
process.stderr.write(`Error reading daemon status: ${message}
|
|
140361
|
+
`);
|
|
140362
|
+
}
|
|
140363
|
+
process.exit(1);
|
|
140364
|
+
}
|
|
140365
|
+
}
|
|
140366
|
+
|
|
141106
140367
|
// packages/cleo/src/cli/commands/dash.ts
|
|
141107
140368
|
init_cli();
|
|
141108
140369
|
function registerDashCommand(program) {
|
|
@@ -141251,14 +140512,14 @@ function registerDetectCommand(program) {
|
|
|
141251
140512
|
init_src();
|
|
141252
140513
|
init_renderers();
|
|
141253
140514
|
import { existsSync as existsSync133, readdirSync as readdirSync41, readFileSync as readFileSync103 } from "node:fs";
|
|
141254
|
-
import { dirname as
|
|
140515
|
+
import { dirname as dirname30, join as join134 } from "node:path";
|
|
141255
140516
|
function findProjectRoot() {
|
|
141256
140517
|
let currentDir = process.cwd();
|
|
141257
140518
|
while (currentDir !== "/") {
|
|
141258
|
-
if (existsSync133(
|
|
140519
|
+
if (existsSync133(join134(currentDir, "package.json"))) {
|
|
141259
140520
|
return currentDir;
|
|
141260
140521
|
}
|
|
141261
|
-
const parent =
|
|
140522
|
+
const parent = dirname30(currentDir);
|
|
141262
140523
|
if (parent === currentDir) break;
|
|
141263
140524
|
currentDir = parent;
|
|
141264
140525
|
}
|
|
@@ -141267,8 +140528,8 @@ function findProjectRoot() {
|
|
|
141267
140528
|
function registerDetectDriftCommand(program) {
|
|
141268
140529
|
program.command("detect-drift").description("Detect documentation drift against TypeScript source of truth").action(async () => {
|
|
141269
140530
|
const projectRoot = findProjectRoot();
|
|
141270
|
-
const isCleoRepo = existsSync133(
|
|
141271
|
-
const cleoSrcRoot = isCleoRepo ?
|
|
140531
|
+
const isCleoRepo = existsSync133(join134(projectRoot, "packages", "cleo", "src"));
|
|
140532
|
+
const cleoSrcRoot = isCleoRepo ? join134(projectRoot, "packages", "cleo", "src") : join134(projectRoot, "src");
|
|
141272
140533
|
const safeRead = (filePath) => {
|
|
141273
140534
|
try {
|
|
141274
140535
|
return readFileSync103(filePath, "utf-8");
|
|
@@ -141282,7 +140543,7 @@ function registerDetectDriftCommand(program) {
|
|
|
141282
140543
|
checks: [],
|
|
141283
140544
|
recommendations: []
|
|
141284
140545
|
};
|
|
141285
|
-
const injPath =
|
|
140546
|
+
const injPath = join134(projectRoot, ".cleo", "templates", "CLEO-INJECTION.md");
|
|
141286
140547
|
if (existsSync133(injPath)) {
|
|
141287
140548
|
const content = safeRead(injPath);
|
|
141288
140549
|
userResult.checks.push({
|
|
@@ -141334,9 +140595,9 @@ function registerDetectDriftCommand(program) {
|
|
|
141334
140595
|
}
|
|
141335
140596
|
};
|
|
141336
140597
|
try {
|
|
141337
|
-
const specPath =
|
|
141338
|
-
const registryPath =
|
|
141339
|
-
const dispatchDomainsDir =
|
|
140598
|
+
const specPath = join134(projectRoot, "docs", "specs", "CLEO-OPERATION-CONSTITUTION.md");
|
|
140599
|
+
const registryPath = join134(cleoSrcRoot, "dispatch", "registry.ts");
|
|
140600
|
+
const dispatchDomainsDir = join134(cleoSrcRoot, "dispatch", "domains");
|
|
141340
140601
|
if (!existsSync133(specPath)) {
|
|
141341
140602
|
addCheck("Gateway-to-spec sync", "fail", "CLEO-OPERATION-CONSTITUTION.md missing", [
|
|
141342
140603
|
{
|
|
@@ -141411,8 +140672,8 @@ function registerDetectDriftCommand(program) {
|
|
|
141411
140672
|
]);
|
|
141412
140673
|
}
|
|
141413
140674
|
try {
|
|
141414
|
-
const cliDir =
|
|
141415
|
-
const coreDir = isCleoRepo ?
|
|
140675
|
+
const cliDir = join134(cleoSrcRoot, "cli", "commands");
|
|
140676
|
+
const coreDir = isCleoRepo ? join134(projectRoot, "packages", "core", "src") : join134(projectRoot, "src", "core");
|
|
141416
140677
|
if (!existsSync133(cliDir)) {
|
|
141417
140678
|
addCheck("CLI-to-core sync", "fail", "CLI commands directory missing", [
|
|
141418
140679
|
{
|
|
@@ -141441,7 +140702,7 @@ function registerDetectDriftCommand(program) {
|
|
|
141441
140702
|
addCheck("CLI-to-core sync", "fail", `Error: ${getErrorMessage(e)}`);
|
|
141442
140703
|
}
|
|
141443
140704
|
try {
|
|
141444
|
-
const domainsDir =
|
|
140705
|
+
const domainsDir = join134(cleoSrcRoot, "dispatch", "domains");
|
|
141445
140706
|
if (!existsSync133(domainsDir)) {
|
|
141446
140707
|
addCheck("Domain handler coverage", "fail", "Dispatch domains directory missing", [
|
|
141447
140708
|
{
|
|
@@ -141459,7 +140720,7 @@ function registerDetectDriftCommand(program) {
|
|
|
141459
140720
|
addCheck("Domain handler coverage", "fail", `Error: ${getErrorMessage(e)}`);
|
|
141460
140721
|
}
|
|
141461
140722
|
try {
|
|
141462
|
-
const matrixPath =
|
|
140723
|
+
const matrixPath = join134(cleoSrcRoot, "dispatch", "lib", "capability-matrix.ts");
|
|
141463
140724
|
if (!existsSync133(matrixPath)) {
|
|
141464
140725
|
addCheck("Capability matrix", "fail", "Capability matrix missing", [
|
|
141465
140726
|
{
|
|
@@ -141476,7 +140737,7 @@ function registerDetectDriftCommand(program) {
|
|
|
141476
140737
|
addCheck("Capability matrix", "fail", `Error: ${getErrorMessage(e)}`);
|
|
141477
140738
|
}
|
|
141478
140739
|
try {
|
|
141479
|
-
const schemaPath =
|
|
140740
|
+
const schemaPath = join134(projectRoot, "src", "store", "schema.ts");
|
|
141480
140741
|
if (!existsSync133(schemaPath)) {
|
|
141481
140742
|
addCheck("Schema validation", "fail", "Schema definition missing", [
|
|
141482
140743
|
{
|
|
@@ -141511,8 +140772,8 @@ function registerDetectDriftCommand(program) {
|
|
|
141511
140772
|
addCheck("Schema validation", "fail", `Error: ${getErrorMessage(e)}`);
|
|
141512
140773
|
}
|
|
141513
140774
|
try {
|
|
141514
|
-
const visionPath =
|
|
141515
|
-
const specPath =
|
|
140775
|
+
const visionPath = join134(projectRoot, "docs", "concepts", "CLEO-VISION.md");
|
|
140776
|
+
const specPath = join134(projectRoot, "docs", "specs", "CLEO-PORTABLE-PROJECT-BRAIN-SPEC.md");
|
|
141516
140777
|
const issues = [];
|
|
141517
140778
|
if (!existsSync133(visionPath)) {
|
|
141518
140779
|
issues.push({
|
|
@@ -141567,7 +140828,7 @@ function registerDetectDriftCommand(program) {
|
|
|
141567
140828
|
addCheck("Canonical identity", "fail", `Error: ${getErrorMessage(e)}`);
|
|
141568
140829
|
}
|
|
141569
140830
|
try {
|
|
141570
|
-
const injectionPath =
|
|
140831
|
+
const injectionPath = join134(projectRoot, ".cleo", "templates", "CLEO-INJECTION.md");
|
|
141571
140832
|
if (!existsSync133(injectionPath)) {
|
|
141572
140833
|
addCheck("Agent injection", "fail", "Agent injection template missing", [
|
|
141573
140834
|
{
|
|
@@ -141597,7 +140858,7 @@ function registerDetectDriftCommand(program) {
|
|
|
141597
140858
|
addCheck("Agent injection", "fail", `Error: ${getErrorMessage(e)}`);
|
|
141598
140859
|
}
|
|
141599
140860
|
try {
|
|
141600
|
-
const exitCodesPath =
|
|
140861
|
+
const exitCodesPath = join134(cleoSrcRoot, "dispatch", "lib", "exit-codes.ts");
|
|
141601
140862
|
if (!existsSync133(exitCodesPath)) {
|
|
141602
140863
|
addCheck("Exit codes", "fail", "Exit codes definition missing", [
|
|
141603
140864
|
{
|
|
@@ -141674,19 +140935,19 @@ function registerDiagnosticsCommand(program) {
|
|
|
141674
140935
|
// packages/cleo/src/cli/commands/docs.ts
|
|
141675
140936
|
init_internal();
|
|
141676
140937
|
init_renderers();
|
|
141677
|
-
import { readdir as
|
|
141678
|
-
import { join as
|
|
140938
|
+
import { readdir as readdir5, readFile as readFile23 } from "node:fs/promises";
|
|
140939
|
+
import { join as join135 } from "node:path";
|
|
141679
140940
|
async function getScriptNames(projectRoot) {
|
|
141680
|
-
const scriptsDir =
|
|
140941
|
+
const scriptsDir = join135(projectRoot, "scripts");
|
|
141681
140942
|
try {
|
|
141682
|
-
const files = await
|
|
140943
|
+
const files = await readdir5(scriptsDir);
|
|
141683
140944
|
return files.filter((f2) => f2.endsWith(".sh")).map((f2) => f2.replace(".sh", "")).sort();
|
|
141684
140945
|
} catch {
|
|
141685
140946
|
return [];
|
|
141686
140947
|
}
|
|
141687
140948
|
}
|
|
141688
140949
|
async function getIndexedCommands(projectRoot) {
|
|
141689
|
-
const indexPath =
|
|
140950
|
+
const indexPath = join135(projectRoot, "docs", "commands", "COMMANDS-INDEX.json");
|
|
141690
140951
|
const index2 = await readJson(indexPath);
|
|
141691
140952
|
if (!index2) return [];
|
|
141692
140953
|
return index2.commands.map((c) => c.name).sort();
|
|
@@ -141715,12 +140976,12 @@ async function runGapCheck(_projectRoot, filterId) {
|
|
|
141715
140976
|
const reviewDir = getAgentOutputsAbsolute();
|
|
141716
140977
|
const results = [];
|
|
141717
140978
|
try {
|
|
141718
|
-
const files = await
|
|
140979
|
+
const files = await readdir5(reviewDir);
|
|
141719
140980
|
const reviewFiles = files.filter((f2) => f2.endsWith(".md"));
|
|
141720
140981
|
for (const file2 of reviewFiles) {
|
|
141721
140982
|
if (filterId && !file2.includes(filterId)) continue;
|
|
141722
|
-
const filePath =
|
|
141723
|
-
const content = await
|
|
140983
|
+
const filePath = join135(reviewDir, file2);
|
|
140984
|
+
const content = await readFile23(filePath, "utf-8");
|
|
141724
140985
|
const taskMatch = file2.match(/^(T\d+)/);
|
|
141725
140986
|
const taskId = taskMatch ? taskMatch[1] : "UNKNOWN";
|
|
141726
140987
|
const gaps = [];
|
|
@@ -142266,13 +141527,106 @@ function registerFindCommand(program) {
|
|
|
142266
141527
|
});
|
|
142267
141528
|
}
|
|
142268
141529
|
|
|
141530
|
+
// packages/cleo/src/cli/commands/gc.ts
|
|
141531
|
+
import { homedir as homedir11 } from "node:os";
|
|
141532
|
+
import { join as join136 } from "node:path";
|
|
141533
|
+
function registerGCCommand(program) {
|
|
141534
|
+
const gc = program.command("gc").description("Transcript garbage collection: manual trigger and status");
|
|
141535
|
+
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) => {
|
|
141536
|
+
const cleoDir = opts.cleoDir ?? join136(homedir11(), ".cleo");
|
|
141537
|
+
const dryRun = opts.dryRun ?? false;
|
|
141538
|
+
try {
|
|
141539
|
+
const gcResult = await runGC({ cleoDir, dryRun });
|
|
141540
|
+
const result = { success: true, data: gcResult };
|
|
141541
|
+
if (opts.json) {
|
|
141542
|
+
process.stdout.write(JSON.stringify(result) + "\n");
|
|
141543
|
+
} else {
|
|
141544
|
+
const dryLabel = dryRun ? " (dry run)" : "";
|
|
141545
|
+
process.stdout.write(`GC run complete${dryLabel}
|
|
141546
|
+
`);
|
|
141547
|
+
process.stdout.write(
|
|
141548
|
+
`Disk: ${gcResult.diskUsedPct.toFixed(1)}% (${gcResult.threshold.toUpperCase()})
|
|
141549
|
+
`
|
|
141550
|
+
);
|
|
141551
|
+
process.stdout.write(
|
|
141552
|
+
`Pruned: ${gcResult.pruned.length} paths, ${formatBytes(gcResult.bytesFreed)} freed
|
|
141553
|
+
`
|
|
141554
|
+
);
|
|
141555
|
+
if (gcResult.escalationSet && gcResult.escalationReason) {
|
|
141556
|
+
process.stdout.write(`
|
|
141557
|
+
WARNING: ${gcResult.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(`GC run failed: ${message}
|
|
141568
|
+
`);
|
|
141569
|
+
}
|
|
141570
|
+
process.exit(1);
|
|
141571
|
+
}
|
|
141572
|
+
});
|
|
141573
|
+
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) => {
|
|
141574
|
+
const cleoDir = opts.cleoDir ?? join136(homedir11(), ".cleo");
|
|
141575
|
+
const statePath = join136(cleoDir, "gc-state.json");
|
|
141576
|
+
try {
|
|
141577
|
+
const state = await readGCState(statePath);
|
|
141578
|
+
const result = { success: true, data: state };
|
|
141579
|
+
if (opts.json) {
|
|
141580
|
+
process.stdout.write(JSON.stringify(result) + "\n");
|
|
141581
|
+
} else {
|
|
141582
|
+
process.stdout.write(`Last run: ${state.lastRunAt ?? "never"}
|
|
141583
|
+
`);
|
|
141584
|
+
process.stdout.write(`Last result: ${state.lastRunResult ?? "none"}
|
|
141585
|
+
`);
|
|
141586
|
+
process.stdout.write(`Bytes freed: ${formatBytes(state.lastRunBytesFreed)}
|
|
141587
|
+
`);
|
|
141588
|
+
const diskStr = state.lastDiskUsedPct !== null ? `${state.lastDiskUsedPct.toFixed(1)}%` : "unknown";
|
|
141589
|
+
process.stdout.write(`Disk used: ${diskStr}
|
|
141590
|
+
`);
|
|
141591
|
+
process.stdout.write(`Failures: ${state.consecutiveFailures}
|
|
141592
|
+
`);
|
|
141593
|
+
process.stdout.write(
|
|
141594
|
+
`Escalation: ${state.escalationNeeded ? "YES \u2014 run cleo gc run" : "no"}
|
|
141595
|
+
`
|
|
141596
|
+
);
|
|
141597
|
+
if (state.escalationReason) {
|
|
141598
|
+
process.stdout.write(`Reason: ${state.escalationReason}
|
|
141599
|
+
`);
|
|
141600
|
+
}
|
|
141601
|
+
}
|
|
141602
|
+
} catch (err) {
|
|
141603
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
141604
|
+
const result = { success: false, error: { code: "E_INTERNAL", message } };
|
|
141605
|
+
if (opts.json) {
|
|
141606
|
+
process.stdout.write(JSON.stringify(result) + "\n");
|
|
141607
|
+
} else {
|
|
141608
|
+
process.stderr.write(`Error reading GC status: ${message}
|
|
141609
|
+
`);
|
|
141610
|
+
}
|
|
141611
|
+
process.exit(1);
|
|
141612
|
+
}
|
|
141613
|
+
});
|
|
141614
|
+
}
|
|
141615
|
+
function formatBytes(bytes) {
|
|
141616
|
+
if (bytes === 0) return "0 B";
|
|
141617
|
+
const units = ["B", "KB", "MB", "GB", "TB"];
|
|
141618
|
+
const exp = Math.floor(Math.log(bytes) / Math.log(1024));
|
|
141619
|
+
const value = bytes / 1024 ** exp;
|
|
141620
|
+
return `${value.toFixed(exp === 0 ? 0 : 1)} ${units[exp] ?? "B"}`;
|
|
141621
|
+
}
|
|
141622
|
+
|
|
142269
141623
|
// packages/cleo/src/cli/commands/generate-changelog.ts
|
|
142270
141624
|
init_src();
|
|
142271
141625
|
init_src3();
|
|
142272
141626
|
init_renderers();
|
|
142273
141627
|
import { execFileSync as execFileSync16 } from "node:child_process";
|
|
142274
141628
|
import { existsSync as existsSync134, mkdirSync as mkdirSync34, readFileSync as readFileSync104, writeFileSync as writeFileSync25 } from "node:fs";
|
|
142275
|
-
import { dirname as
|
|
141629
|
+
import { dirname as dirname31, join as join137 } from "node:path";
|
|
142276
141630
|
function getChangelogSource(cwd) {
|
|
142277
141631
|
const configPath = getConfigPath(cwd);
|
|
142278
141632
|
try {
|
|
@@ -142410,7 +141764,7 @@ function registerGenerateChangelogCommand(program) {
|
|
|
142410
141764
|
const targetPlatform = opts["platform"];
|
|
142411
141765
|
const dryRun = !!opts["dryRun"];
|
|
142412
141766
|
const sourceFile = getChangelogSource();
|
|
142413
|
-
const sourcePath =
|
|
141767
|
+
const sourcePath = join137(getProjectRoot(), sourceFile);
|
|
142414
141768
|
if (!existsSync134(sourcePath)) {
|
|
142415
141769
|
throw new CleoError(4 /* NOT_FOUND */, `Changelog source not found: ${sourcePath}`);
|
|
142416
141770
|
}
|
|
@@ -142423,8 +141777,8 @@ function registerGenerateChangelogCommand(program) {
|
|
|
142423
141777
|
const outputPath = platformConfig?.path ?? getDefaultOutputPath(targetPlatform);
|
|
142424
141778
|
const content = generateForPlatform(targetPlatform, sourceContent, repoSlug, limit);
|
|
142425
141779
|
if (!dryRun) {
|
|
142426
|
-
const fullPath =
|
|
142427
|
-
mkdirSync34(
|
|
141780
|
+
const fullPath = join137(getProjectRoot(), outputPath);
|
|
141781
|
+
mkdirSync34(dirname31(fullPath), { recursive: true });
|
|
142428
141782
|
writeFileSync25(fullPath, content, "utf-8");
|
|
142429
141783
|
}
|
|
142430
141784
|
results.push({ platform: targetPlatform, path: outputPath, written: !dryRun });
|
|
@@ -142444,8 +141798,8 @@ function registerGenerateChangelogCommand(program) {
|
|
|
142444
141798
|
limit
|
|
142445
141799
|
);
|
|
142446
141800
|
if (!dryRun) {
|
|
142447
|
-
const fullPath =
|
|
142448
|
-
mkdirSync34(
|
|
141801
|
+
const fullPath = join137(getProjectRoot(), platformConfig.path);
|
|
141802
|
+
mkdirSync34(dirname31(fullPath), { recursive: true });
|
|
142449
141803
|
writeFileSync25(fullPath, content, "utf-8");
|
|
142450
141804
|
}
|
|
142451
141805
|
results.push({
|
|
@@ -143032,8 +142386,8 @@ init_cli();
|
|
|
143032
142386
|
init_renderers();
|
|
143033
142387
|
import { createHash as createHash20 } from "node:crypto";
|
|
143034
142388
|
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
|
|
142389
|
+
import { homedir as homedir12 } from "node:os";
|
|
142390
|
+
import { join as join138 } from "node:path";
|
|
143037
142391
|
function parseMemoryFileFrontmatter(raw) {
|
|
143038
142392
|
const lines = raw.split("\n");
|
|
143039
142393
|
if (!lines[0]?.trim().startsWith("---")) {
|
|
@@ -143775,11 +143129,11 @@ Found ${totalDups} duplicate rows across ${groups.length} groups:`);
|
|
|
143775
143129
|
"--from <dir>",
|
|
143776
143130
|
"Source directory containing *.md memory files (default: ~/.claude/projects/-mnt-projects-cleocode/memory)"
|
|
143777
143131
|
).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 ??
|
|
143132
|
+
const sourceDir = opts.from ?? join138(homedir12(), ".claude", "projects", "-mnt-projects-cleocode", "memory");
|
|
143779
143133
|
const isDryRun = !!opts.dryRun;
|
|
143780
143134
|
const isJson = !!opts.json;
|
|
143781
143135
|
const projectRoot = getProjectRoot();
|
|
143782
|
-
const stateFile =
|
|
143136
|
+
const stateFile = join138(projectRoot, ".cleo", "migrate-memory-hashes.json");
|
|
143783
143137
|
if (!existsSync135(sourceDir)) {
|
|
143784
143138
|
const msg = `Source directory not found: ${sourceDir}`;
|
|
143785
143139
|
if (isJson) {
|
|
@@ -143789,7 +143143,7 @@ Found ${totalDups} duplicate rows across ${groups.length} groups:`);
|
|
|
143789
143143
|
}
|
|
143790
143144
|
process.exit(1);
|
|
143791
143145
|
}
|
|
143792
|
-
const files = readdirSync42(sourceDir).filter((f2) => f2.endsWith(".md") && f2 !== "MEMORY.md").map((f2) =>
|
|
143146
|
+
const files = readdirSync42(sourceDir).filter((f2) => f2.endsWith(".md") && f2 !== "MEMORY.md").map((f2) => join138(sourceDir, f2));
|
|
143793
143147
|
const importedHashes = isDryRun ? /* @__PURE__ */ new Set() : loadImportHashes(stateFile);
|
|
143794
143148
|
const stats2 = { total: files.length, imported: 0, skipped: 0, errors: 0 };
|
|
143795
143149
|
const importedEntries = [];
|
|
@@ -143952,7 +143306,7 @@ Found ${totalDups} duplicate rows across ${groups.length} groups:`);
|
|
|
143952
143306
|
for (const tbl of tables) {
|
|
143953
143307
|
const dateCol = tbl === "brain_patterns" ? "extracted_at" : "created_at";
|
|
143954
143308
|
try {
|
|
143955
|
-
const
|
|
143309
|
+
const rawRows = nativeDb.prepare(
|
|
143956
143310
|
`SELECT id, ${dateCol} as created_at, citation_count, verified, quality_score
|
|
143957
143311
|
FROM ${tbl}
|
|
143958
143312
|
WHERE memory_tier = 'medium'
|
|
@@ -143961,6 +143315,17 @@ Found ${totalDups} duplicate rows across ${groups.length} groups:`);
|
|
|
143961
143315
|
ORDER BY ${dateCol} ASC
|
|
143962
143316
|
LIMIT 20`
|
|
143963
143317
|
).all();
|
|
143318
|
+
const rows = rawRows.map((raw) => {
|
|
143319
|
+
const r = raw;
|
|
143320
|
+
return {
|
|
143321
|
+
id: String(r["id"] ?? ""),
|
|
143322
|
+
tbl,
|
|
143323
|
+
created_at: String(r["created_at"] ?? ""),
|
|
143324
|
+
citation_count: Number(r["citation_count"] ?? 0),
|
|
143325
|
+
verified: Number(r["verified"] ?? 0),
|
|
143326
|
+
quality_score: r["quality_score"] == null ? null : Number(r["quality_score"])
|
|
143327
|
+
};
|
|
143328
|
+
});
|
|
143964
143329
|
for (const r of rows) {
|
|
143965
143330
|
const entryMs = new Date(r.created_at.replace(" ", "T")).getTime();
|
|
143966
143331
|
const promotionMs = entryMs + age7dMs;
|
|
@@ -148152,18 +147517,18 @@ function registerSchemaCommand(program) {
|
|
|
148152
147517
|
// packages/cleo/src/cli/commands/self-update.ts
|
|
148153
147518
|
init_src();
|
|
148154
147519
|
init_internal();
|
|
148155
|
-
import { execFile as
|
|
148156
|
-
import { readFile as
|
|
148157
|
-
import { join as
|
|
147520
|
+
import { execFile as execFile8 } from "node:child_process";
|
|
147521
|
+
import { readFile as readFile24 } from "node:fs/promises";
|
|
147522
|
+
import { join as join139 } from "node:path";
|
|
148158
147523
|
import * as readline2 from "node:readline";
|
|
148159
|
-
import { promisify as
|
|
147524
|
+
import { promisify as promisify8 } from "node:util";
|
|
148160
147525
|
init_renderers();
|
|
148161
|
-
var execAsync =
|
|
147526
|
+
var execAsync = promisify8(execFile8);
|
|
148162
147527
|
var GITHUB_REPO = BUILD_CONFIG.repository.fullName;
|
|
148163
147528
|
async function getCurrentVersion() {
|
|
148164
147529
|
const cleoHome = getCleoHome();
|
|
148165
147530
|
try {
|
|
148166
|
-
const content = await
|
|
147531
|
+
const content = await readFile24(join139(cleoHome, "VERSION"), "utf-8");
|
|
148167
147532
|
return (content.split("\n")[0] ?? "unknown").trim();
|
|
148168
147533
|
} catch {
|
|
148169
147534
|
return "unknown";
|
|
@@ -148217,7 +147582,7 @@ async function writeRuntimeVersionMetadata(mode, source, version2) {
|
|
|
148217
147582
|
];
|
|
148218
147583
|
await import("node:fs/promises").then(
|
|
148219
147584
|
({ writeFile: writeFile16, mkdir: mkdir22 }) => mkdir22(cleoHome, { recursive: true }).then(
|
|
148220
|
-
() => writeFile16(
|
|
147585
|
+
() => writeFile16(join139(cleoHome, "VERSION"), `${lines.join("\n")}
|
|
148221
147586
|
`, "utf-8")
|
|
148222
147587
|
)
|
|
148223
147588
|
);
|
|
@@ -149472,6 +148837,586 @@ function registerTokenCommand(program) {
|
|
|
149472
148837
|
});
|
|
149473
148838
|
}
|
|
149474
148839
|
|
|
148840
|
+
// packages/cleo/src/cli/commands/transcript.ts
|
|
148841
|
+
init_src3();
|
|
148842
|
+
import { homedir as homedir14 } from "node:os";
|
|
148843
|
+
import { join as join141 } from "node:path";
|
|
148844
|
+
|
|
148845
|
+
// packages/cleo/src/gc/transcript.ts
|
|
148846
|
+
import { lstat as lstat3, readdir as readdir6, stat as stat5 } from "node:fs/promises";
|
|
148847
|
+
import { homedir as homedir13 } from "node:os";
|
|
148848
|
+
import { join as join140 } from "node:path";
|
|
148849
|
+
var HOT_MAX_MS = 24 * 60 * 60 * 1e3;
|
|
148850
|
+
var WARM_MAX_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
148851
|
+
function classifyTranscriptTier(ageMs) {
|
|
148852
|
+
if (ageMs < HOT_MAX_MS) return "hot";
|
|
148853
|
+
if (ageMs < WARM_MAX_MS) return "warm";
|
|
148854
|
+
return "cold";
|
|
148855
|
+
}
|
|
148856
|
+
function parseSessionId(filename) {
|
|
148857
|
+
return filename.replace(/\.jsonl$/, "");
|
|
148858
|
+
}
|
|
148859
|
+
async function scanTranscripts(projectsDir) {
|
|
148860
|
+
const resolvedProjectsDir = projectsDir ?? join140(homedir13(), ".claude", "projects");
|
|
148861
|
+
const now2 = Date.now();
|
|
148862
|
+
const hot = [];
|
|
148863
|
+
const warm = [];
|
|
148864
|
+
let totalBytes = 0;
|
|
148865
|
+
let slugs;
|
|
148866
|
+
try {
|
|
148867
|
+
const entries = await readdir6(resolvedProjectsDir, { withFileTypes: true });
|
|
148868
|
+
slugs = entries.filter((e) => e.isDirectory()).map((e) => e.name);
|
|
148869
|
+
} catch {
|
|
148870
|
+
return { totalSessions: 0, hot, warm, totalBytes, projectsDir: resolvedProjectsDir };
|
|
148871
|
+
}
|
|
148872
|
+
for (const slug of slugs) {
|
|
148873
|
+
const slugDir = join140(resolvedProjectsDir, slug);
|
|
148874
|
+
let entries;
|
|
148875
|
+
try {
|
|
148876
|
+
entries = await readdir6(slugDir, { withFileTypes: true });
|
|
148877
|
+
} catch {
|
|
148878
|
+
continue;
|
|
148879
|
+
}
|
|
148880
|
+
for (const entry of entries) {
|
|
148881
|
+
if (!entry.isFile() || !entry.name.endsWith(".jsonl")) continue;
|
|
148882
|
+
const jsonlPath = join140(slugDir, entry.name);
|
|
148883
|
+
const sessionId = parseSessionId(entry.name);
|
|
148884
|
+
let fileInfo;
|
|
148885
|
+
try {
|
|
148886
|
+
fileInfo = await stat5(jsonlPath);
|
|
148887
|
+
} catch {
|
|
148888
|
+
continue;
|
|
148889
|
+
}
|
|
148890
|
+
const mtimeMs = fileInfo.mtimeMs;
|
|
148891
|
+
const ageMs = now2 - mtimeMs;
|
|
148892
|
+
const tier = classifyTranscriptTier(ageMs);
|
|
148893
|
+
const bytes = fileInfo.size;
|
|
148894
|
+
const candidateSessionDir = join140(slugDir, sessionId);
|
|
148895
|
+
let sessionDir = null;
|
|
148896
|
+
let sessionDirBytes = 0;
|
|
148897
|
+
try {
|
|
148898
|
+
const dirInfo = await lstat3(candidateSessionDir);
|
|
148899
|
+
if (dirInfo.isDirectory()) {
|
|
148900
|
+
sessionDir = candidateSessionDir;
|
|
148901
|
+
sessionDirBytes = await getPathBytes(candidateSessionDir);
|
|
148902
|
+
}
|
|
148903
|
+
} catch {
|
|
148904
|
+
}
|
|
148905
|
+
const info = {
|
|
148906
|
+
jsonlPath,
|
|
148907
|
+
projectSlug: slug,
|
|
148908
|
+
sessionId,
|
|
148909
|
+
mtimeMs,
|
|
148910
|
+
ageMs,
|
|
148911
|
+
tier,
|
|
148912
|
+
bytes,
|
|
148913
|
+
sessionDir,
|
|
148914
|
+
sessionDirBytes
|
|
148915
|
+
};
|
|
148916
|
+
totalBytes += bytes + sessionDirBytes;
|
|
148917
|
+
if (tier === "hot") {
|
|
148918
|
+
hot.push(info);
|
|
148919
|
+
} else if (tier === "warm") {
|
|
148920
|
+
warm.push(info);
|
|
148921
|
+
}
|
|
148922
|
+
}
|
|
148923
|
+
}
|
|
148924
|
+
const totalSessions = hot.length + warm.length;
|
|
148925
|
+
return { totalSessions, hot, warm, totalBytes, projectsDir: resolvedProjectsDir };
|
|
148926
|
+
}
|
|
148927
|
+
async function pruneTranscripts(opts) {
|
|
148928
|
+
const { olderThanMs, confirm, projectsDir } = opts;
|
|
148929
|
+
const dryRun = !confirm;
|
|
148930
|
+
const hasApiKey = Boolean(process.env["ANTHROPIC_API_KEY"]);
|
|
148931
|
+
const effectiveMaxAgeMs = hasApiKey ? olderThanMs : Math.max(olderThanMs, 30 * 24 * 60 * 60 * 1e3);
|
|
148932
|
+
const now2 = Date.now();
|
|
148933
|
+
const deletedPaths = [];
|
|
148934
|
+
let bytesFreed = 0;
|
|
148935
|
+
let pruned = 0;
|
|
148936
|
+
const resolvedProjectsDir = projectsDir ?? join140(homedir13(), ".claude", "projects");
|
|
148937
|
+
let slugs;
|
|
148938
|
+
try {
|
|
148939
|
+
const entries = await readdir6(resolvedProjectsDir, { withFileTypes: true });
|
|
148940
|
+
slugs = entries.filter((e) => e.isDirectory()).map((e) => e.name);
|
|
148941
|
+
} catch {
|
|
148942
|
+
return { pruned: 0, bytesFreed: 0, deletedPaths: [], dryRun };
|
|
148943
|
+
}
|
|
148944
|
+
for (const slug of slugs) {
|
|
148945
|
+
const slugDir = join140(resolvedProjectsDir, slug);
|
|
148946
|
+
let entries;
|
|
148947
|
+
try {
|
|
148948
|
+
entries = await readdir6(slugDir, { withFileTypes: true });
|
|
148949
|
+
} catch {
|
|
148950
|
+
continue;
|
|
148951
|
+
}
|
|
148952
|
+
for (const entry of entries) {
|
|
148953
|
+
if (!entry.isFile() || !entry.name.endsWith(".jsonl")) continue;
|
|
148954
|
+
const jsonlPath = join140(slugDir, entry.name);
|
|
148955
|
+
let fileInfo;
|
|
148956
|
+
try {
|
|
148957
|
+
fileInfo = await stat5(jsonlPath);
|
|
148958
|
+
} catch {
|
|
148959
|
+
continue;
|
|
148960
|
+
}
|
|
148961
|
+
const ageMs = now2 - fileInfo.mtimeMs;
|
|
148962
|
+
if (ageMs <= effectiveMaxAgeMs) continue;
|
|
148963
|
+
const sessionId = parseSessionId(entry.name);
|
|
148964
|
+
const sessionDir = join140(slugDir, sessionId);
|
|
148965
|
+
const jsonlBytes = fileInfo.size;
|
|
148966
|
+
let sessionDirBytes = 0;
|
|
148967
|
+
try {
|
|
148968
|
+
const dirInfo = await lstat3(sessionDir);
|
|
148969
|
+
if (dirInfo.isDirectory()) {
|
|
148970
|
+
sessionDirBytes = await getPathBytes(sessionDir);
|
|
148971
|
+
}
|
|
148972
|
+
} catch {
|
|
148973
|
+
}
|
|
148974
|
+
if (dryRun) {
|
|
148975
|
+
deletedPaths.push(jsonlPath);
|
|
148976
|
+
if (sessionDirBytes > 0) deletedPaths.push(sessionDir);
|
|
148977
|
+
bytesFreed += jsonlBytes + sessionDirBytes;
|
|
148978
|
+
pruned++;
|
|
148979
|
+
continue;
|
|
148980
|
+
}
|
|
148981
|
+
try {
|
|
148982
|
+
await idempotentRm(jsonlPath);
|
|
148983
|
+
deletedPaths.push(jsonlPath);
|
|
148984
|
+
bytesFreed += jsonlBytes;
|
|
148985
|
+
pruned++;
|
|
148986
|
+
} catch {
|
|
148987
|
+
continue;
|
|
148988
|
+
}
|
|
148989
|
+
try {
|
|
148990
|
+
const dirInfo = await lstat3(sessionDir);
|
|
148991
|
+
if (dirInfo.isDirectory()) {
|
|
148992
|
+
await idempotentRm(sessionDir);
|
|
148993
|
+
deletedPaths.push(sessionDir);
|
|
148994
|
+
bytesFreed += sessionDirBytes;
|
|
148995
|
+
}
|
|
148996
|
+
} catch {
|
|
148997
|
+
}
|
|
148998
|
+
}
|
|
148999
|
+
}
|
|
149000
|
+
return { pruned, bytesFreed, deletedPaths, dryRun };
|
|
149001
|
+
}
|
|
149002
|
+
function parseDurationMs(duration3) {
|
|
149003
|
+
const match = /^(\d+(\.\d+)?)(d|h|m|s)$/.exec(duration3.trim());
|
|
149004
|
+
if (!match?.[1] || !match[3]) {
|
|
149005
|
+
throw new Error(`Invalid duration format: "${duration3}". Use format like 7d, 24h, 30m, 60s.`);
|
|
149006
|
+
}
|
|
149007
|
+
const value = parseFloat(match[1]);
|
|
149008
|
+
const unit = match[3];
|
|
149009
|
+
const multipliers = {
|
|
149010
|
+
d: 24 * 60 * 60 * 1e3,
|
|
149011
|
+
h: 60 * 60 * 1e3,
|
|
149012
|
+
m: 60 * 1e3,
|
|
149013
|
+
s: 1e3
|
|
149014
|
+
};
|
|
149015
|
+
return value * (multipliers[unit] ?? 1e3);
|
|
149016
|
+
}
|
|
149017
|
+
|
|
149018
|
+
// packages/cleo/src/cli/commands/transcript.ts
|
|
149019
|
+
function registerTranscriptCommand(program) {
|
|
149020
|
+
const transcript = program.command("transcript").description("Transcript lifecycle management: scan, extract, and prune session transcripts");
|
|
149021
|
+
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) => {
|
|
149022
|
+
if (opts.pending) {
|
|
149023
|
+
try {
|
|
149024
|
+
const projectRoot = getProjectRoot();
|
|
149025
|
+
const { scanPendingTranscripts: scanPendingTranscripts2 } = await import("@cleocode/core/memory/transcript-scanner.js");
|
|
149026
|
+
const pending = await scanPendingTranscripts2(projectRoot);
|
|
149027
|
+
const envelope = { success: true, data: { count: pending.length, pending } };
|
|
149028
|
+
if (opts.json) {
|
|
149029
|
+
process.stdout.write(JSON.stringify(envelope) + "\n");
|
|
149030
|
+
} else {
|
|
149031
|
+
process.stdout.write(`Sessions pending extraction: ${pending.length}
|
|
149032
|
+
`);
|
|
149033
|
+
for (const p2 of pending) {
|
|
149034
|
+
process.stdout.write(
|
|
149035
|
+
` ${p2.sessionId} ${p2.filePath || "(file not found)"} queued: ${p2.createdAt}
|
|
149036
|
+
`
|
|
149037
|
+
);
|
|
149038
|
+
}
|
|
149039
|
+
}
|
|
149040
|
+
} catch (err) {
|
|
149041
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
149042
|
+
const envelope = { success: false, error: { code: "E_INTERNAL", message } };
|
|
149043
|
+
if (opts.json) {
|
|
149044
|
+
process.stdout.write(JSON.stringify(envelope) + "\n");
|
|
149045
|
+
} else {
|
|
149046
|
+
process.stderr.write(`Pending scan failed: ${message}
|
|
149047
|
+
`);
|
|
149048
|
+
}
|
|
149049
|
+
process.exit(1);
|
|
149050
|
+
}
|
|
149051
|
+
return;
|
|
149052
|
+
}
|
|
149053
|
+
const projectsDir = opts.projectsDir ?? join141(homedir14(), ".claude", "projects");
|
|
149054
|
+
try {
|
|
149055
|
+
const result = await scanTranscripts(projectsDir);
|
|
149056
|
+
const envelope = {
|
|
149057
|
+
success: true,
|
|
149058
|
+
data: {
|
|
149059
|
+
totalSessions: result.totalSessions,
|
|
149060
|
+
totalBytes: result.totalBytes,
|
|
149061
|
+
totalMB: parseFloat((result.totalBytes / 1024 / 1024).toFixed(2)),
|
|
149062
|
+
hot: {
|
|
149063
|
+
count: result.hot.length,
|
|
149064
|
+
sessions: result.hot.map((s3) => ({
|
|
149065
|
+
sessionId: s3.sessionId,
|
|
149066
|
+
projectSlug: s3.projectSlug,
|
|
149067
|
+
ageHours: parseFloat((s3.ageMs / 36e5).toFixed(1)),
|
|
149068
|
+
bytes: s3.bytes + s3.sessionDirBytes
|
|
149069
|
+
}))
|
|
149070
|
+
},
|
|
149071
|
+
warm: {
|
|
149072
|
+
count: result.warm.length,
|
|
149073
|
+
sessions: result.warm.map((s3) => ({
|
|
149074
|
+
sessionId: s3.sessionId,
|
|
149075
|
+
projectSlug: s3.projectSlug,
|
|
149076
|
+
ageDays: parseFloat((s3.ageMs / 864e5).toFixed(1)),
|
|
149077
|
+
bytes: s3.bytes + s3.sessionDirBytes
|
|
149078
|
+
}))
|
|
149079
|
+
},
|
|
149080
|
+
projectsDir: result.projectsDir
|
|
149081
|
+
}
|
|
149082
|
+
};
|
|
149083
|
+
if (opts.json) {
|
|
149084
|
+
process.stdout.write(JSON.stringify(envelope) + "\n");
|
|
149085
|
+
} else {
|
|
149086
|
+
process.stdout.write(`Transcript scan results
|
|
149087
|
+
`);
|
|
149088
|
+
process.stdout.write(`=======================
|
|
149089
|
+
`);
|
|
149090
|
+
process.stdout.write(`Projects dir: ${result.projectsDir}
|
|
149091
|
+
`);
|
|
149092
|
+
process.stdout.write(`Total sessions: ${result.totalSessions}
|
|
149093
|
+
`);
|
|
149094
|
+
process.stdout.write(
|
|
149095
|
+
`Total size: ${(result.totalBytes / 1024 / 1024).toFixed(1)} MB
|
|
149096
|
+
`
|
|
149097
|
+
);
|
|
149098
|
+
process.stdout.write(`
|
|
149099
|
+
Tier breakdown:
|
|
149100
|
+
`);
|
|
149101
|
+
process.stdout.write(` HOT (0\u201324h): ${result.hot.length} sessions
|
|
149102
|
+
`);
|
|
149103
|
+
process.stdout.write(` WARM (1\u20137d): ${result.warm.length} sessions
|
|
149104
|
+
`);
|
|
149105
|
+
process.stdout.write(` COLD (>7d): (transcripts deleted; brain.db entries only)
|
|
149106
|
+
`);
|
|
149107
|
+
}
|
|
149108
|
+
} catch (err) {
|
|
149109
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
149110
|
+
const envelope = { success: false, error: { code: "E_INTERNAL", message } };
|
|
149111
|
+
if (opts.json) {
|
|
149112
|
+
process.stdout.write(JSON.stringify(envelope) + "\n");
|
|
149113
|
+
} else {
|
|
149114
|
+
process.stderr.write(`Scan failed: ${message}
|
|
149115
|
+
`);
|
|
149116
|
+
}
|
|
149117
|
+
process.exit(1);
|
|
149118
|
+
}
|
|
149119
|
+
});
|
|
149120
|
+
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(
|
|
149121
|
+
async (sessionId, opts) => {
|
|
149122
|
+
const tier = opts.tier ?? "warm";
|
|
149123
|
+
const dryRun = opts.dryRun ?? false;
|
|
149124
|
+
const projectRoot = getProjectRoot();
|
|
149125
|
+
try {
|
|
149126
|
+
const { extractTranscript } = await import("@cleocode/core/memory/transcript-extractor.js");
|
|
149127
|
+
const { findSessionTranscriptPath: findSessionTranscriptPath2, listAllTranscripts: listAllTranscripts2 } = await import("@cleocode/core/memory/transcript-scanner.js");
|
|
149128
|
+
const sessions2 = [];
|
|
149129
|
+
if (opts.allWarm) {
|
|
149130
|
+
const allTranscripts = await listAllTranscripts2({ olderThanHours: 1 });
|
|
149131
|
+
for (const t of allTranscripts) {
|
|
149132
|
+
sessions2.push({ sessionId: t.sessionId, path: t.path });
|
|
149133
|
+
}
|
|
149134
|
+
} else if (sessionId) {
|
|
149135
|
+
const path12 = await findSessionTranscriptPath2(sessionId);
|
|
149136
|
+
if (!path12) {
|
|
149137
|
+
const envelope2 = {
|
|
149138
|
+
success: false,
|
|
149139
|
+
error: {
|
|
149140
|
+
code: "E_NOT_FOUND",
|
|
149141
|
+
message: `Session JSONL not found for: ${sessionId}`
|
|
149142
|
+
}
|
|
149143
|
+
};
|
|
149144
|
+
if (opts.json) {
|
|
149145
|
+
process.stdout.write(JSON.stringify(envelope2) + "\n");
|
|
149146
|
+
} else {
|
|
149147
|
+
process.stderr.write(`Session not found: ${sessionId}
|
|
149148
|
+
`);
|
|
149149
|
+
}
|
|
149150
|
+
process.exit(4);
|
|
149151
|
+
return;
|
|
149152
|
+
}
|
|
149153
|
+
sessions2.push({ sessionId, path: path12 });
|
|
149154
|
+
} else {
|
|
149155
|
+
process.stderr.write(
|
|
149156
|
+
"Provide a session-id or use --all-warm to extract all warm sessions\n"
|
|
149157
|
+
);
|
|
149158
|
+
process.exit(2);
|
|
149159
|
+
return;
|
|
149160
|
+
}
|
|
149161
|
+
const results = [];
|
|
149162
|
+
let totalExtracted = 0;
|
|
149163
|
+
let totalStored = 0;
|
|
149164
|
+
let totalBytesFreed = 0;
|
|
149165
|
+
for (const session of sessions2) {
|
|
149166
|
+
const result = await extractTranscript({
|
|
149167
|
+
transcriptPath: session.path,
|
|
149168
|
+
projectRoot,
|
|
149169
|
+
tier,
|
|
149170
|
+
dryRun,
|
|
149171
|
+
sessionId: session.sessionId
|
|
149172
|
+
});
|
|
149173
|
+
results.push(result);
|
|
149174
|
+
totalExtracted += result.extractedCount;
|
|
149175
|
+
totalStored += result.storedCount;
|
|
149176
|
+
totalBytesFreed += result.bytesFreed;
|
|
149177
|
+
}
|
|
149178
|
+
const envelope = {
|
|
149179
|
+
success: true,
|
|
149180
|
+
data: {
|
|
149181
|
+
sessionsProcessed: sessions2.length,
|
|
149182
|
+
memoriesExtracted: totalExtracted,
|
|
149183
|
+
memoriesStored: totalStored,
|
|
149184
|
+
bytesFreed: totalBytesFreed,
|
|
149185
|
+
dryRun,
|
|
149186
|
+
results
|
|
149187
|
+
}
|
|
149188
|
+
};
|
|
149189
|
+
if (opts.json) {
|
|
149190
|
+
process.stdout.write(JSON.stringify(envelope) + "\n");
|
|
149191
|
+
} else {
|
|
149192
|
+
const dryLabel = dryRun ? " (dry run)" : "";
|
|
149193
|
+
process.stdout.write(`Transcript extraction complete${dryLabel}
|
|
149194
|
+
`);
|
|
149195
|
+
process.stdout.write(`Sessions: ${sessions2.length}
|
|
149196
|
+
`);
|
|
149197
|
+
process.stdout.write(`Extracted: ${totalExtracted} memories
|
|
149198
|
+
`);
|
|
149199
|
+
process.stdout.write(`Stored: ${totalStored} memories
|
|
149200
|
+
`);
|
|
149201
|
+
process.stdout.write(`Freed: ${(totalBytesFreed / 1024 / 1024).toFixed(1)} MB
|
|
149202
|
+
`);
|
|
149203
|
+
}
|
|
149204
|
+
} catch (err) {
|
|
149205
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
149206
|
+
const envelope = { success: false, error: { code: "E_INTERNAL", message } };
|
|
149207
|
+
if (opts.json) {
|
|
149208
|
+
process.stdout.write(JSON.stringify(envelope) + "\n");
|
|
149209
|
+
} else {
|
|
149210
|
+
process.stderr.write(`Extract failed: ${message}
|
|
149211
|
+
`);
|
|
149212
|
+
}
|
|
149213
|
+
process.exit(1);
|
|
149214
|
+
}
|
|
149215
|
+
}
|
|
149216
|
+
);
|
|
149217
|
+
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(
|
|
149218
|
+
async (opts) => {
|
|
149219
|
+
const tier = opts.tier ?? "warm";
|
|
149220
|
+
const dryRun = opts.dryRun ?? false;
|
|
149221
|
+
const olderThanHours = opts.olderThanHours ? Number.parseInt(opts.olderThanHours, 10) : 24;
|
|
149222
|
+
const limit = opts.limit ? Number.parseInt(opts.limit, 10) : void 0;
|
|
149223
|
+
const projectRoot = getProjectRoot();
|
|
149224
|
+
try {
|
|
149225
|
+
const { extractTranscript } = await import("@cleocode/core/memory/transcript-extractor.js");
|
|
149226
|
+
const { listAllTranscripts: listAllTranscripts2 } = await import("@cleocode/core/memory/transcript-scanner.js");
|
|
149227
|
+
const transcripts = await listAllTranscripts2({
|
|
149228
|
+
olderThanHours,
|
|
149229
|
+
projectFilter: opts.projectFilter,
|
|
149230
|
+
limit
|
|
149231
|
+
});
|
|
149232
|
+
if (opts.json) {
|
|
149233
|
+
process.stdout.write(
|
|
149234
|
+
JSON.stringify({
|
|
149235
|
+
success: true,
|
|
149236
|
+
data: { discovered: transcripts.length, message: "Starting migration..." }
|
|
149237
|
+
}) + "\n"
|
|
149238
|
+
);
|
|
149239
|
+
} else {
|
|
149240
|
+
process.stdout.write(
|
|
149241
|
+
`Migrating ${transcripts.length} session transcripts${dryRun ? " (dry run)" : ""}...
|
|
149242
|
+
`
|
|
149243
|
+
);
|
|
149244
|
+
}
|
|
149245
|
+
let memoriesExtracted = 0;
|
|
149246
|
+
let memoriesStored = 0;
|
|
149247
|
+
let bytesFreed = 0;
|
|
149248
|
+
let processed = 0;
|
|
149249
|
+
let skipped = 0;
|
|
149250
|
+
let failed = 0;
|
|
149251
|
+
for (const entry of transcripts) {
|
|
149252
|
+
try {
|
|
149253
|
+
const result = await extractTranscript({
|
|
149254
|
+
transcriptPath: entry.path,
|
|
149255
|
+
projectRoot,
|
|
149256
|
+
tier,
|
|
149257
|
+
dryRun,
|
|
149258
|
+
sessionId: entry.sessionId
|
|
149259
|
+
});
|
|
149260
|
+
if (result.warnings.some((w2) => w2.includes("Already extracted"))) {
|
|
149261
|
+
skipped += 1;
|
|
149262
|
+
} else {
|
|
149263
|
+
processed += 1;
|
|
149264
|
+
memoriesExtracted += result.extractedCount;
|
|
149265
|
+
memoriesStored += result.storedCount;
|
|
149266
|
+
bytesFreed += result.bytesFreed;
|
|
149267
|
+
}
|
|
149268
|
+
} catch {
|
|
149269
|
+
failed += 1;
|
|
149270
|
+
}
|
|
149271
|
+
}
|
|
149272
|
+
const envelope = {
|
|
149273
|
+
success: true,
|
|
149274
|
+
data: {
|
|
149275
|
+
sessionsProcessed: processed,
|
|
149276
|
+
sessionsSkipped: skipped,
|
|
149277
|
+
sessionsFailed: failed,
|
|
149278
|
+
memoriesExtracted,
|
|
149279
|
+
memoriesStored,
|
|
149280
|
+
bytesFreed,
|
|
149281
|
+
mbFreed: parseFloat((bytesFreed / 1024 / 1024).toFixed(2)),
|
|
149282
|
+
dryRun
|
|
149283
|
+
}
|
|
149284
|
+
};
|
|
149285
|
+
if (opts.json) {
|
|
149286
|
+
process.stdout.write(JSON.stringify(envelope) + "\n");
|
|
149287
|
+
} else {
|
|
149288
|
+
const dryLabel = dryRun ? " (dry run \u2014 no changes written)" : "";
|
|
149289
|
+
process.stdout.write(`Migration complete${dryLabel}
|
|
149290
|
+
`);
|
|
149291
|
+
process.stdout.write(
|
|
149292
|
+
`Processed: ${processed} Skipped: ${skipped} Failed: ${failed}
|
|
149293
|
+
`
|
|
149294
|
+
);
|
|
149295
|
+
process.stdout.write(`Memories extracted: ${memoriesExtracted}
|
|
149296
|
+
`);
|
|
149297
|
+
process.stdout.write(`Memories stored: ${memoriesStored}
|
|
149298
|
+
`);
|
|
149299
|
+
process.stdout.write(
|
|
149300
|
+
`Bytes freed: ${(bytesFreed / 1024 / 1024).toFixed(1)} MB
|
|
149301
|
+
`
|
|
149302
|
+
);
|
|
149303
|
+
}
|
|
149304
|
+
} catch (err) {
|
|
149305
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
149306
|
+
const envelope = { success: false, error: { code: "E_INTERNAL", message } };
|
|
149307
|
+
if (opts.json) {
|
|
149308
|
+
process.stdout.write(JSON.stringify(envelope) + "\n");
|
|
149309
|
+
} else {
|
|
149310
|
+
process.stderr.write(`Migration failed: ${message}
|
|
149311
|
+
`);
|
|
149312
|
+
}
|
|
149313
|
+
process.exit(1);
|
|
149314
|
+
}
|
|
149315
|
+
}
|
|
149316
|
+
);
|
|
149317
|
+
transcript.command("prune").description("Prune session transcripts older than the specified duration. Dry-run by default.").requiredOption(
|
|
149318
|
+
"--older-than <duration>",
|
|
149319
|
+
"Delete sessions older than this (e.g. 7d, 24h, 30m)"
|
|
149320
|
+
).option(
|
|
149321
|
+
"--confirm",
|
|
149322
|
+
"Perform actual deletion. Without this flag, the command dry-runs and reports what would be deleted."
|
|
149323
|
+
).option("--projects-dir <path>", "Override ~/.claude/projects/ path").option("--json", "Output result as JSON (LAFS envelope)").action(
|
|
149324
|
+
async (opts) => {
|
|
149325
|
+
const isTTY = Boolean(process.stdin.isTTY && process.stdout.isTTY);
|
|
149326
|
+
const confirm = opts.confirm ?? false;
|
|
149327
|
+
if (!confirm && !isTTY) {
|
|
149328
|
+
const envelope = {
|
|
149329
|
+
success: false,
|
|
149330
|
+
error: {
|
|
149331
|
+
code: "E_INVALID_INPUT",
|
|
149332
|
+
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."
|
|
149333
|
+
}
|
|
149334
|
+
};
|
|
149335
|
+
if (opts.json) {
|
|
149336
|
+
process.stdout.write(JSON.stringify(envelope) + "\n");
|
|
149337
|
+
} else {
|
|
149338
|
+
process.stderr.write(
|
|
149339
|
+
`Deletion requires --confirm in non-interactive mode. Re-run with --confirm to delete.
|
|
149340
|
+
`
|
|
149341
|
+
);
|
|
149342
|
+
}
|
|
149343
|
+
process.exit(0);
|
|
149344
|
+
}
|
|
149345
|
+
let olderThanMs;
|
|
149346
|
+
try {
|
|
149347
|
+
olderThanMs = parseDurationMs(opts.olderThan);
|
|
149348
|
+
} catch (parseErr) {
|
|
149349
|
+
const message = parseErr instanceof Error ? parseErr.message : String(parseErr);
|
|
149350
|
+
const envelope = { success: false, error: { code: "E_INVALID_INPUT", message } };
|
|
149351
|
+
if (opts.json) {
|
|
149352
|
+
process.stdout.write(JSON.stringify(envelope) + "\n");
|
|
149353
|
+
} else {
|
|
149354
|
+
process.stderr.write(`Invalid duration: ${message}
|
|
149355
|
+
`);
|
|
149356
|
+
}
|
|
149357
|
+
process.exit(2);
|
|
149358
|
+
return;
|
|
149359
|
+
}
|
|
149360
|
+
const projectsDir = opts.projectsDir ?? join141(homedir14(), ".claude", "projects");
|
|
149361
|
+
try {
|
|
149362
|
+
const pruneResult = await pruneTranscripts({
|
|
149363
|
+
olderThanMs,
|
|
149364
|
+
confirm,
|
|
149365
|
+
projectsDir
|
|
149366
|
+
});
|
|
149367
|
+
const envelope = {
|
|
149368
|
+
success: true,
|
|
149369
|
+
data: {
|
|
149370
|
+
pruned: pruneResult.pruned,
|
|
149371
|
+
bytesFreed: pruneResult.bytesFreed,
|
|
149372
|
+
mbFreed: parseFloat((pruneResult.bytesFreed / 1024 / 1024).toFixed(2)),
|
|
149373
|
+
dryRun: pruneResult.dryRun,
|
|
149374
|
+
deletedPaths: pruneResult.deletedPaths
|
|
149375
|
+
}
|
|
149376
|
+
};
|
|
149377
|
+
if (opts.json) {
|
|
149378
|
+
process.stdout.write(JSON.stringify(envelope) + "\n");
|
|
149379
|
+
} else {
|
|
149380
|
+
const dryLabel = pruneResult.dryRun ? " (dry run \u2014 use --confirm to delete)" : "";
|
|
149381
|
+
process.stdout.write(`Transcript prune${dryLabel}
|
|
149382
|
+
`);
|
|
149383
|
+
process.stdout.write(`Older than: ${opts.olderThan}
|
|
149384
|
+
`);
|
|
149385
|
+
process.stdout.write(`Sessions: ${pruneResult.pruned}
|
|
149386
|
+
`);
|
|
149387
|
+
process.stdout.write(
|
|
149388
|
+
`Bytes freed: ${(pruneResult.bytesFreed / 1024 / 1024).toFixed(1)} MB
|
|
149389
|
+
`
|
|
149390
|
+
);
|
|
149391
|
+
if (pruneResult.deletedPaths.length > 0 && pruneResult.dryRun) {
|
|
149392
|
+
process.stdout.write(`
|
|
149393
|
+
Would delete (${pruneResult.deletedPaths.length} paths):
|
|
149394
|
+
`);
|
|
149395
|
+
for (const p2 of pruneResult.deletedPaths.slice(0, 10)) {
|
|
149396
|
+
process.stdout.write(` ${p2}
|
|
149397
|
+
`);
|
|
149398
|
+
}
|
|
149399
|
+
if (pruneResult.deletedPaths.length > 10) {
|
|
149400
|
+
process.stdout.write(` ... and ${pruneResult.deletedPaths.length - 10} more
|
|
149401
|
+
`);
|
|
149402
|
+
}
|
|
149403
|
+
}
|
|
149404
|
+
}
|
|
149405
|
+
} catch (err) {
|
|
149406
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
149407
|
+
const envelope = { success: false, error: { code: "E_INTERNAL", message } };
|
|
149408
|
+
if (opts.json) {
|
|
149409
|
+
process.stdout.write(JSON.stringify(envelope) + "\n");
|
|
149410
|
+
} else {
|
|
149411
|
+
process.stderr.write(`Prune failed: ${message}
|
|
149412
|
+
`);
|
|
149413
|
+
}
|
|
149414
|
+
process.exit(1);
|
|
149415
|
+
}
|
|
149416
|
+
}
|
|
149417
|
+
);
|
|
149418
|
+
}
|
|
149419
|
+
|
|
149475
149420
|
// packages/cleo/src/cli/commands/update.ts
|
|
149476
149421
|
init_cli();
|
|
149477
149422
|
function registerUpdateCommand(program) {
|
|
@@ -149621,18 +149566,18 @@ function registerVerifyCommand(program) {
|
|
|
149621
149566
|
init_src();
|
|
149622
149567
|
init_src3();
|
|
149623
149568
|
init_renderers();
|
|
149624
|
-
import { execFileSync as execFileSync18, spawn as
|
|
149625
|
-
import { mkdir as
|
|
149626
|
-
import { join as
|
|
149569
|
+
import { execFileSync as execFileSync18, spawn as spawn3 } from "node:child_process";
|
|
149570
|
+
import { mkdir as mkdir21, open, readFile as readFile25, rm as rm4, stat as stat6, writeFile as writeFile15 } from "node:fs/promises";
|
|
149571
|
+
import { join as join142 } from "node:path";
|
|
149627
149572
|
var DEFAULT_PORT = 3456;
|
|
149628
149573
|
var DEFAULT_HOST = "127.0.0.1";
|
|
149629
149574
|
function getWebPaths() {
|
|
149630
149575
|
const cleoHome = getCleoHome();
|
|
149631
149576
|
return {
|
|
149632
|
-
pidFile:
|
|
149633
|
-
configFile:
|
|
149634
|
-
logDir:
|
|
149635
|
-
logFile:
|
|
149577
|
+
pidFile: join142(cleoHome, "web-server.pid"),
|
|
149578
|
+
configFile: join142(cleoHome, "web-server.json"),
|
|
149579
|
+
logDir: join142(cleoHome, "logs"),
|
|
149580
|
+
logFile: join142(cleoHome, "logs", "web-server.log")
|
|
149636
149581
|
};
|
|
149637
149582
|
}
|
|
149638
149583
|
function isProcessRunning(pid) {
|
|
@@ -149646,7 +149591,7 @@ function isProcessRunning(pid) {
|
|
|
149646
149591
|
async function getStatus() {
|
|
149647
149592
|
const { pidFile, configFile } = getWebPaths();
|
|
149648
149593
|
try {
|
|
149649
|
-
const pidStr = (await
|
|
149594
|
+
const pidStr = (await readFile25(pidFile, "utf-8")).trim();
|
|
149650
149595
|
const pid = parseInt(pidStr, 10);
|
|
149651
149596
|
if (Number.isNaN(pid) || !isProcessRunning(pid)) {
|
|
149652
149597
|
return { running: false, pid: null, port: null, host: null, url: null };
|
|
@@ -149654,7 +149599,7 @@ async function getStatus() {
|
|
|
149654
149599
|
let port = DEFAULT_PORT;
|
|
149655
149600
|
let host = DEFAULT_HOST;
|
|
149656
149601
|
try {
|
|
149657
|
-
const config2 = JSON.parse(await
|
|
149602
|
+
const config2 = JSON.parse(await readFile25(configFile, "utf-8"));
|
|
149658
149603
|
port = config2.port ?? DEFAULT_PORT;
|
|
149659
149604
|
host = config2.host ?? DEFAULT_HOST;
|
|
149660
149605
|
} catch {
|
|
@@ -149671,9 +149616,9 @@ async function startWebServer(port, host) {
|
|
|
149671
149616
|
throw new CleoError(1 /* GENERAL_ERROR */, `Server already running (PID: ${status.pid})`);
|
|
149672
149617
|
}
|
|
149673
149618
|
const projectRoot = process.env["CLEO_ROOT"] ?? process.cwd();
|
|
149674
|
-
const studioDir = process.env["CLEO_STUDIO_DIR"] ??
|
|
149675
|
-
await
|
|
149676
|
-
await
|
|
149619
|
+
const studioDir = process.env["CLEO_STUDIO_DIR"] ?? join142(projectRoot, "packages", "studio", "build");
|
|
149620
|
+
await mkdir21(logDir, { recursive: true });
|
|
149621
|
+
await writeFile15(
|
|
149677
149622
|
configFile,
|
|
149678
149623
|
JSON.stringify({
|
|
149679
149624
|
port,
|
|
@@ -149681,9 +149626,9 @@ async function startWebServer(port, host) {
|
|
|
149681
149626
|
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
149682
149627
|
})
|
|
149683
149628
|
);
|
|
149684
|
-
const webIndexPath =
|
|
149629
|
+
const webIndexPath = join142(studioDir, "index.js");
|
|
149685
149630
|
try {
|
|
149686
|
-
await
|
|
149631
|
+
await stat6(webIndexPath);
|
|
149687
149632
|
} catch {
|
|
149688
149633
|
try {
|
|
149689
149634
|
execFileSync18("pnpm", ["--filter", "@cleocode/studio", "run", "build"], {
|
|
@@ -149699,7 +149644,7 @@ Logs: ${logFile}`
|
|
|
149699
149644
|
}
|
|
149700
149645
|
}
|
|
149701
149646
|
const logFileHandle = await open(logFile, "a");
|
|
149702
|
-
const serverProcess =
|
|
149647
|
+
const serverProcess = spawn3("node", [webIndexPath], {
|
|
149703
149648
|
cwd: studioDir,
|
|
149704
149649
|
env: {
|
|
149705
149650
|
...process.env,
|
|
@@ -149713,10 +149658,10 @@ Logs: ${logFile}`
|
|
|
149713
149658
|
});
|
|
149714
149659
|
serverProcess.unref();
|
|
149715
149660
|
const pidFileTmp = `${pidFile}.tmp`;
|
|
149716
|
-
await
|
|
149717
|
-
await
|
|
149718
|
-
await
|
|
149719
|
-
await
|
|
149661
|
+
await writeFile15(pidFileTmp, String(serverProcess.pid));
|
|
149662
|
+
await rm4(pidFile, { force: true });
|
|
149663
|
+
await writeFile15(pidFile, String(serverProcess.pid));
|
|
149664
|
+
await rm4(pidFileTmp, { force: true });
|
|
149720
149665
|
await logFileHandle.close();
|
|
149721
149666
|
const maxAttempts = 30;
|
|
149722
149667
|
let started = false;
|
|
@@ -149736,7 +149681,7 @@ Logs: ${logFile}`
|
|
|
149736
149681
|
process.kill(serverProcess.pid);
|
|
149737
149682
|
} catch {
|
|
149738
149683
|
}
|
|
149739
|
-
await
|
|
149684
|
+
await rm4(pidFile, { force: true });
|
|
149740
149685
|
throw new CleoError(1 /* GENERAL_ERROR */, "Server failed to start within 15 seconds");
|
|
149741
149686
|
}
|
|
149742
149687
|
cliOutput(
|
|
@@ -149770,13 +149715,13 @@ function registerWebCommand(program) {
|
|
|
149770
149715
|
const { pidFile } = getWebPaths();
|
|
149771
149716
|
const status = await getStatus();
|
|
149772
149717
|
if (!status.running || !status.pid) {
|
|
149773
|
-
await
|
|
149718
|
+
await rm4(pidFile, { force: true });
|
|
149774
149719
|
cliOutput({ running: false }, { command: "web", message: "Server is not running" });
|
|
149775
149720
|
return;
|
|
149776
149721
|
}
|
|
149777
149722
|
try {
|
|
149778
149723
|
if (process.platform === "win32") {
|
|
149779
|
-
|
|
149724
|
+
spawn3("taskkill", ["/PID", String(status.pid), "/T"], { stdio: "ignore" });
|
|
149780
149725
|
} else {
|
|
149781
149726
|
process.kill(status.pid, "SIGTERM");
|
|
149782
149727
|
}
|
|
@@ -149789,14 +149734,14 @@ function registerWebCommand(program) {
|
|
|
149789
149734
|
if (isProcessRunning(status.pid)) {
|
|
149790
149735
|
try {
|
|
149791
149736
|
if (process.platform === "win32") {
|
|
149792
|
-
|
|
149737
|
+
spawn3("taskkill", ["/PID", String(status.pid), "/F", "/T"], { stdio: "ignore" });
|
|
149793
149738
|
} else {
|
|
149794
149739
|
process.kill(status.pid, "SIGKILL");
|
|
149795
149740
|
}
|
|
149796
149741
|
} catch {
|
|
149797
149742
|
}
|
|
149798
149743
|
}
|
|
149799
|
-
await
|
|
149744
|
+
await rm4(pidFile, { force: true });
|
|
149800
149745
|
cliOutput({ stopped: true }, { command: "web", message: "CLEO Web UI stopped" });
|
|
149801
149746
|
} catch (err) {
|
|
149802
149747
|
if (err instanceof CleoError) {
|
|
@@ -149813,7 +149758,7 @@ function registerWebCommand(program) {
|
|
|
149813
149758
|
if (status.running && status.pid) {
|
|
149814
149759
|
try {
|
|
149815
149760
|
if (process.platform === "win32") {
|
|
149816
|
-
|
|
149761
|
+
spawn3("taskkill", ["/PID", String(status.pid), "/T"], { stdio: "ignore" });
|
|
149817
149762
|
} else {
|
|
149818
149763
|
process.kill(status.pid, "SIGTERM");
|
|
149819
149764
|
}
|
|
@@ -149826,14 +149771,14 @@ function registerWebCommand(program) {
|
|
|
149826
149771
|
if (isProcessRunning(status.pid)) {
|
|
149827
149772
|
try {
|
|
149828
149773
|
if (process.platform === "win32") {
|
|
149829
|
-
|
|
149774
|
+
spawn3("taskkill", ["/PID", String(status.pid), "/F", "/T"], { stdio: "ignore" });
|
|
149830
149775
|
} else {
|
|
149831
149776
|
process.kill(status.pid, "SIGKILL");
|
|
149832
149777
|
}
|
|
149833
149778
|
} catch {
|
|
149834
149779
|
}
|
|
149835
149780
|
}
|
|
149836
|
-
await
|
|
149781
|
+
await rm4(pidFile, { force: true });
|
|
149837
149782
|
}
|
|
149838
149783
|
const port = parseInt(opts["port"] ?? String(DEFAULT_PORT), 10);
|
|
149839
149784
|
const host = opts["host"] ?? DEFAULT_HOST;
|
|
@@ -149871,11 +149816,11 @@ function registerWebCommand(program) {
|
|
|
149871
149816
|
const platform6 = process.platform;
|
|
149872
149817
|
try {
|
|
149873
149818
|
if (platform6 === "linux") {
|
|
149874
|
-
|
|
149819
|
+
spawn3("xdg-open", [url2], { detached: true, stdio: "ignore" }).unref();
|
|
149875
149820
|
} else if (platform6 === "darwin") {
|
|
149876
|
-
|
|
149821
|
+
spawn3("open", [url2], { detached: true, stdio: "ignore" }).unref();
|
|
149877
149822
|
} else if (platform6 === "win32") {
|
|
149878
|
-
|
|
149823
|
+
spawn3("cmd", ["/c", "start", "", url2], { detached: true, stdio: "ignore" }).unref();
|
|
149879
149824
|
}
|
|
149880
149825
|
} catch {
|
|
149881
149826
|
}
|
|
@@ -149890,1239 +149835,6 @@ function registerWebCommand(program) {
|
|
|
149890
149835
|
});
|
|
149891
149836
|
}
|
|
149892
149837
|
|
|
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
149838
|
// packages/cleo/src/cli/commands/code.ts
|
|
151127
149839
|
async function requireTreeSitter() {
|
|
151128
149840
|
const { isTreeSitterAvailable: isTreeSitterAvailable3 } = await Promise.resolve().then(() => (init_internal(), internal_exports));
|