@crewx/cli 0.9.0-rc.146 → 0.9.0-rc.148
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/bootstrap/codex-writable-roots.js +1 -113
- package/dist/bootstrap/crewx-cli.js +1 -84
- package/dist/builtin.js +5 -132
- package/dist/commands/agent.js +4 -118
- package/dist/commands/cal.js +2 -44
- package/dist/commands/db.js +4 -602
- package/dist/commands/detached.js +15 -620
- package/dist/commands/doctor.js +4 -649
- package/dist/commands/emit-trailer.js +2 -33
- package/dist/commands/execute.js +15 -294
- package/dist/commands/hook/command-marker.js +1 -5
- package/dist/commands/hook/install.js +8 -189
- package/dist/commands/hook/paths.js +1 -94
- package/dist/commands/hook/status.js +1 -86
- package/dist/commands/hook/uninstall.js +1 -71
- package/dist/commands/hook-dispatch.js +2 -180
- package/dist/commands/init.js +31 -437
- package/dist/commands/kill.js +1 -11
- package/dist/commands/log.js +3 -96
- package/dist/commands/parse-agent-message.js +1 -52
- package/dist/commands/parse-common-flags.js +3 -212
- package/dist/commands/ps.js +2 -117
- package/dist/commands/publish.js +7 -283
- package/dist/commands/query.js +16 -292
- package/dist/commands/registry.js +1 -33
- package/dist/commands/resolve-prompt.js +3 -76
- package/dist/commands/resolve-recipient.js +3 -47
- package/dist/commands/restart.js +18 -172
- package/dist/commands/result.js +1 -209
- package/dist/commands/shortcut.js +4 -253
- package/dist/commands/slack.js +16 -560
- package/dist/commands/stop.js +8 -119
- package/dist/commands/write-output.js +1 -24
- package/dist/index.js +1 -8
- package/dist/logging.js +10 -87
- package/dist/main.js +6 -289
- package/dist/register-builtin-tools.js +1 -9
- package/dist/slack/file-download.js +1 -134
- package/dist/slack/markdown.js +1 -33
- package/dist/utils/env-defaults.js +1 -15
- package/dist/utils/inherited-trace.js +1 -65
- package/dist/utils/product-package-root.js +1 -39
- package/dist/utils/sdk-compat.js +1 -120
- package/dist/utils/version.js +1 -28
- package/package.json +15 -15
package/dist/commands/db.js
CHANGED
|
@@ -1,27 +1,4 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* crewx db push handler.
|
|
4
|
-
*
|
|
5
|
-
* Usage:
|
|
6
|
-
* crewx db push Preview changes → confirm → apply
|
|
7
|
-
* crewx db push --force Apply without confirmation + reset migration history
|
|
8
|
-
* crewx db push --dry-run Show preview only, no changes
|
|
9
|
-
*/
|
|
10
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
11
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
12
|
-
};
|
|
13
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
-
exports.USAGE_BACKFILL_HELP = exports.REQUEST_LOG_MIGRATION_HELP = exports.TASK_LOG_MIGRATION_HELP = void 0;
|
|
15
|
-
exports.handleDb = handleDb;
|
|
16
|
-
const path_1 = __importDefault(require("path"));
|
|
17
|
-
const os_1 = __importDefault(require("os"));
|
|
18
|
-
const readline_1 = __importDefault(require("readline"));
|
|
19
|
-
const repository_1 = require("@crewx/sdk/repository");
|
|
20
|
-
const repository_2 = require("@crewx/sdk/repository");
|
|
21
|
-
const repository_3 = require("@crewx/sdk/repository");
|
|
22
|
-
const repository_4 = require("@crewx/sdk/repository");
|
|
23
|
-
const repository_5 = require("@crewx/sdk/repository");
|
|
24
|
-
exports.TASK_LOG_MIGRATION_HELP = `Usage:
|
|
1
|
+
"use strict";var g=exports&&exports.__importDefault||function(e){return e&&e.__esModule?e:{"default":e}};Object.defineProperty(exports,"__esModule",{value:true});exports.USAGE_BACKFILL_HELP=exports.REQUEST_LOG_MIGRATION_HELP=exports.TASK_LOG_MIGRATION_HELP=void 0;exports.handleDb=P;const $=g(require("path"));const v=g(require("os"));const R=g(require("readline"));const T=require("@crewx/sdk/repository");const m=require("@crewx/sdk/repository");const x=require("@crewx/sdk/repository");const p=require("@crewx/sdk/repository");const _=require("@crewx/sdk/repository");exports.TASK_LOG_MIGRATION_HELP=`Usage:
|
|
25
2
|
crewx db migrate-task-logs --dry-run [--db PATH]
|
|
26
3
|
crewx db migrate-task-logs --apply [--db PATH] [--batch-tasks N]
|
|
27
4
|
crewx db migrate-task-logs --verify [--db PATH]
|
|
@@ -42,8 +19,7 @@ Exit codes:
|
|
|
42
19
|
1 Safe tasks completed but a row/invariant failed, or apply was blocked.
|
|
43
20
|
2 Invalid command-line arguments.
|
|
44
21
|
|
|
45
|
-
The command never runs automatically at server startup and never runs VACUUM.`;
|
|
46
|
-
exports.REQUEST_LOG_MIGRATION_HELP = `Usage:
|
|
22
|
+
The command never runs automatically at server startup and never runs VACUUM.`;exports.REQUEST_LOG_MIGRATION_HELP=`Usage:
|
|
47
23
|
crewx db migrate-request-logs [--batch N] [--dry-run]
|
|
48
24
|
|
|
49
25
|
Moves the request log rows still stored in crewx.db into logs.db, the file next
|
|
@@ -62,8 +38,7 @@ it again at any time. The database is chosen like everywhere else (CREWX_DB, els
|
|
|
62
38
|
Exit codes:
|
|
63
39
|
0 Every row is in logs.db and none is left in crewx.db (or the dry run finished).
|
|
64
40
|
1 Rows are still in crewx.db, or an error stopped the move (progress is kept).
|
|
65
|
-
2 Invalid command-line arguments.`;
|
|
66
|
-
exports.USAGE_BACKFILL_HELP = `Usage:
|
|
41
|
+
2 Invalid command-line arguments.`;exports.USAGE_BACKFILL_HELP=`Usage:
|
|
67
42
|
crewx db backfill-usage [--dry-run|--apply|--verify] [--db PATH] [--backup PATH]
|
|
68
43
|
crewx db backfill-usage --restore SNAPSHOT [--db PATH]
|
|
69
44
|
|
|
@@ -81,577 +56,4 @@ Apply policy:
|
|
|
81
56
|
Exit codes:
|
|
82
57
|
0 Completed successfully.
|
|
83
58
|
1 Apply was blocked or one or more task updates failed.
|
|
84
|
-
2 Invalid command-line arguments.`;
|
|
85
|
-
function defaultDbPath() {
|
|
86
|
-
return path_1.default.join(os_1.default.homedir(), '.crewx', 'crewx.db');
|
|
87
|
-
}
|
|
88
|
-
function formatPreview(result, dbPath) {
|
|
89
|
-
const lines = [];
|
|
90
|
-
lines.push(`[crewx] Database push — comparing schema vs ${dbPath}`);
|
|
91
|
-
lines.push('');
|
|
92
|
-
if (result.created.length > 0) {
|
|
93
|
-
lines.push(` Tables to create:`);
|
|
94
|
-
for (const t of result.created) {
|
|
95
|
-
lines.push(` → ${t}`);
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
else {
|
|
99
|
-
lines.push(' Tables to create: (none)');
|
|
100
|
-
}
|
|
101
|
-
if (result.altered.length > 0) {
|
|
102
|
-
lines.push(' Columns to add:');
|
|
103
|
-
for (const { table, column } of result.altered) {
|
|
104
|
-
lines.push(` → ${table}.${column}`);
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
else {
|
|
108
|
-
lines.push(' Columns to add: (none)');
|
|
109
|
-
}
|
|
110
|
-
if (result.warnings.length > 0) {
|
|
111
|
-
lines.push(' Warnings:');
|
|
112
|
-
for (const w of result.warnings) {
|
|
113
|
-
lines.push(` ⚠ ${w}`);
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
if (result.backupPath) {
|
|
117
|
-
lines.push('');
|
|
118
|
-
lines.push(` Backup: ${result.backupPath}`);
|
|
119
|
-
}
|
|
120
|
-
return lines.join('\n');
|
|
121
|
-
}
|
|
122
|
-
function hasChanges(result) {
|
|
123
|
-
return result.created.length > 0 || result.altered.length > 0;
|
|
124
|
-
}
|
|
125
|
-
function formatStatusCounts(counts) {
|
|
126
|
-
const entries = Object.entries(counts ?? {});
|
|
127
|
-
return entries.length > 0
|
|
128
|
-
? entries.map(([status, count]) => `${status}=${count}`).join(', ')
|
|
129
|
-
: '(none)';
|
|
130
|
-
}
|
|
131
|
-
function prompt(question) {
|
|
132
|
-
const rl = readline_1.default.createInterface({ input: process.stdin, output: process.stdout });
|
|
133
|
-
return new Promise((resolve) => {
|
|
134
|
-
rl.question(question, (answer) => {
|
|
135
|
-
rl.close();
|
|
136
|
-
resolve(answer);
|
|
137
|
-
});
|
|
138
|
-
});
|
|
139
|
-
}
|
|
140
|
-
async function handleDb(args) {
|
|
141
|
-
const subcommand = args[0];
|
|
142
|
-
if (subcommand === 'migrate-task-logs') {
|
|
143
|
-
await handleTaskLogMigration(args.slice(1));
|
|
144
|
-
return;
|
|
145
|
-
}
|
|
146
|
-
if (subcommand === 'migrate-request-logs') {
|
|
147
|
-
await handleRequestLogMigration(args.slice(1));
|
|
148
|
-
return;
|
|
149
|
-
}
|
|
150
|
-
if (subcommand === 'backfill-usage') {
|
|
151
|
-
await handleUsageBackfill(args.slice(1));
|
|
152
|
-
return;
|
|
153
|
-
}
|
|
154
|
-
if (subcommand === '--help' || subcommand === '-h') {
|
|
155
|
-
console.log('Usage: crewx db push [--force] [--dry-run]');
|
|
156
|
-
console.log(exports.TASK_LOG_MIGRATION_HELP);
|
|
157
|
-
console.log(exports.REQUEST_LOG_MIGRATION_HELP);
|
|
158
|
-
return;
|
|
159
|
-
}
|
|
160
|
-
if (!subcommand || subcommand === 'push') {
|
|
161
|
-
await handleDbPush(args.slice(subcommand === 'push' ? 1 : 0));
|
|
162
|
-
return;
|
|
163
|
-
}
|
|
164
|
-
console.error(`Unknown db subcommand: ${subcommand}`);
|
|
165
|
-
console.error('Usage: crewx db push [--force] [--dry-run]');
|
|
166
|
-
console.error(exports.TASK_LOG_MIGRATION_HELP);
|
|
167
|
-
process.exitCode = 2;
|
|
168
|
-
}
|
|
169
|
-
class TaskLogMigrationUsageError extends Error {
|
|
170
|
-
}
|
|
171
|
-
class UsageBackfillUsageError extends Error {
|
|
172
|
-
}
|
|
173
|
-
function parseTaskLogMigrationArgs(args) {
|
|
174
|
-
let mode;
|
|
175
|
-
let dbPath;
|
|
176
|
-
let batchTasks;
|
|
177
|
-
const setMode = (next) => {
|
|
178
|
-
if (mode)
|
|
179
|
-
throw new TaskLogMigrationUsageError('Choose exactly one of --dry-run, --apply, or --verify.');
|
|
180
|
-
mode = next;
|
|
181
|
-
};
|
|
182
|
-
const requireValue = (args, index, flag) => {
|
|
183
|
-
const value = args[index + 1];
|
|
184
|
-
if (!value || value.startsWith('--'))
|
|
185
|
-
throw new TaskLogMigrationUsageError(`${flag} requires a value.`);
|
|
186
|
-
return value;
|
|
187
|
-
};
|
|
188
|
-
for (let index = 0; index < args.length; index += 1) {
|
|
189
|
-
const arg = args[index];
|
|
190
|
-
if (arg === '--help' || arg === '-h') {
|
|
191
|
-
throw new TaskLogMigrationUsageError(exports.TASK_LOG_MIGRATION_HELP);
|
|
192
|
-
}
|
|
193
|
-
if (arg === '--dry-run') {
|
|
194
|
-
setMode('dry-run');
|
|
195
|
-
continue;
|
|
196
|
-
}
|
|
197
|
-
if (arg === '--apply') {
|
|
198
|
-
setMode('apply');
|
|
199
|
-
continue;
|
|
200
|
-
}
|
|
201
|
-
if (arg === '--verify') {
|
|
202
|
-
setMode('verify');
|
|
203
|
-
continue;
|
|
204
|
-
}
|
|
205
|
-
if (arg === '--db') {
|
|
206
|
-
dbPath = requireValue(args, index, '--db');
|
|
207
|
-
index += 1;
|
|
208
|
-
continue;
|
|
209
|
-
}
|
|
210
|
-
if (arg.startsWith('--db=')) {
|
|
211
|
-
dbPath = arg.slice('--db='.length);
|
|
212
|
-
if (!dbPath)
|
|
213
|
-
throw new TaskLogMigrationUsageError('--db requires a value.');
|
|
214
|
-
continue;
|
|
215
|
-
}
|
|
216
|
-
if (arg === '--batch-tasks') {
|
|
217
|
-
const value = requireValue(args, index, '--batch-tasks');
|
|
218
|
-
const parsed = Number(value);
|
|
219
|
-
if (!Number.isSafeInteger(parsed) || parsed < 1) {
|
|
220
|
-
throw new TaskLogMigrationUsageError('--batch-tasks must be a positive integer.');
|
|
221
|
-
}
|
|
222
|
-
batchTasks = parsed;
|
|
223
|
-
index += 1;
|
|
224
|
-
continue;
|
|
225
|
-
}
|
|
226
|
-
if (arg.startsWith('--batch-tasks=')) {
|
|
227
|
-
const value = arg.slice('--batch-tasks='.length);
|
|
228
|
-
const parsed = Number(value);
|
|
229
|
-
if (!Number.isSafeInteger(parsed) || parsed < 1) {
|
|
230
|
-
throw new TaskLogMigrationUsageError('--batch-tasks must be a positive integer.');
|
|
231
|
-
}
|
|
232
|
-
batchTasks = parsed;
|
|
233
|
-
continue;
|
|
234
|
-
}
|
|
235
|
-
throw new TaskLogMigrationUsageError(`Unknown option: ${arg}`);
|
|
236
|
-
}
|
|
237
|
-
if (!mode)
|
|
238
|
-
throw new TaskLogMigrationUsageError('Choose one of --dry-run, --apply, or --verify.');
|
|
239
|
-
if (batchTasks !== undefined && mode !== 'apply') {
|
|
240
|
-
throw new TaskLogMigrationUsageError('--batch-tasks is only valid with --apply.');
|
|
241
|
-
}
|
|
242
|
-
return { mode, dbPath, batchTasks };
|
|
243
|
-
}
|
|
244
|
-
function formatMigrationReport(report) {
|
|
245
|
-
const modeLabel = report.mode === 'dry-run' ? 'Dry-run' : report.mode === 'apply' ? 'Apply' : 'Verify';
|
|
246
|
-
const lines = [
|
|
247
|
-
`[crewx] Task-log migration — ${modeLabel}`,
|
|
248
|
-
` Database: ${report.dbPath}`,
|
|
249
|
-
` Tasks scanned: ${report.taskCount}`,
|
|
250
|
-
` Blob candidates: ${report.candidateTaskCount}`,
|
|
251
|
-
` Eligible terminal blobs: ${report.eligibleTaskCount}`,
|
|
252
|
-
` Status counts: ${formatStatusCounts(report.statusCounts)}`,
|
|
253
|
-
` Deferred blob tasks: ${report.deferredTaskCount}`,
|
|
254
|
-
` Deferred statuses: ${formatStatusCounts(report.deferredStatusCounts)}`,
|
|
255
|
-
` Entries: ${report.entryCount}`,
|
|
256
|
-
` Source bytes: ${report.sourceBytes}`,
|
|
257
|
-
` Estimated free-space need: ${report.estimatedFreeSpaceBytes}`,
|
|
258
|
-
` Required free space (with reserve): ${report.requiredFreeSpaceBytes}`,
|
|
259
|
-
` Available free space: ${report.availableFreeSpaceBytes}`,
|
|
260
|
-
` Free-space gate: ${report.freeSpaceGatePassed ? 'PASS' : 'FAIL'}`,
|
|
261
|
-
` Migrated: ${report.migratedTasks} task(s), ${report.migratedEntries} entr${report.migratedEntries === 1 ? 'y' : 'ies'}`,
|
|
262
|
-
` Skipped event-source tasks: ${report.skippedTasks}`,
|
|
263
|
-
` Failed tasks: ${report.failedTasks}`,
|
|
264
|
-
` Logical source bytes removed: ${report.logicalSourceBytesRemoved}`,
|
|
265
|
-
];
|
|
266
|
-
if (report.remainingTaskCount !== undefined)
|
|
267
|
-
lines.push(` Remaining blob tasks: ${report.remainingTaskCount}`);
|
|
268
|
-
if (report.backupPath) {
|
|
269
|
-
lines.push(` Backup: ${report.backupPath}`);
|
|
270
|
-
if (report.backupMethod)
|
|
271
|
-
lines.push(` Backup method: ${report.backupMethod}`);
|
|
272
|
-
}
|
|
273
|
-
if (report.verification) {
|
|
274
|
-
lines.push(` Event rows checked: ${report.verification.eventCount}`);
|
|
275
|
-
lines.push(` Verification issues: ${report.verification.issues.length}`);
|
|
276
|
-
}
|
|
277
|
-
if (report.blockedReason)
|
|
278
|
-
lines.push(` Blocked: ${report.blockedReason}`);
|
|
279
|
-
for (const warning of report.warnings ?? [])
|
|
280
|
-
lines.push(` Warning: ${warning}`);
|
|
281
|
-
return lines.join('\n');
|
|
282
|
-
}
|
|
283
|
-
async function handleTaskLogMigration(args) {
|
|
284
|
-
let parsed;
|
|
285
|
-
try {
|
|
286
|
-
parsed = parseTaskLogMigrationArgs(args);
|
|
287
|
-
}
|
|
288
|
-
catch (error) {
|
|
289
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
290
|
-
if (message === exports.TASK_LOG_MIGRATION_HELP) {
|
|
291
|
-
console.log(message);
|
|
292
|
-
}
|
|
293
|
-
else {
|
|
294
|
-
console.error(message);
|
|
295
|
-
console.error(exports.TASK_LOG_MIGRATION_HELP);
|
|
296
|
-
process.exitCode = 2;
|
|
297
|
-
}
|
|
298
|
-
return;
|
|
299
|
-
}
|
|
300
|
-
try {
|
|
301
|
-
const report = await (0, repository_3.runTaskLogMigration)(parsed);
|
|
302
|
-
console.log(formatMigrationReport(report));
|
|
303
|
-
if (!report.ok) {
|
|
304
|
-
if (report.blockedReason)
|
|
305
|
-
console.error(report.blockedReason);
|
|
306
|
-
const failures = report.failures ?? report.malformedRows ?? [];
|
|
307
|
-
for (const failure of failures) {
|
|
308
|
-
console.error(` ${failure.taskId}: ${failure.reason}`);
|
|
309
|
-
}
|
|
310
|
-
process.exitCode = 1;
|
|
311
|
-
}
|
|
312
|
-
else {
|
|
313
|
-
process.exitCode = 0;
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
catch (error) {
|
|
317
|
-
console.error(error instanceof Error ? error.message : String(error));
|
|
318
|
-
process.exitCode = 1;
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
class RequestLogMigrationUsageError extends Error {
|
|
322
|
-
}
|
|
323
|
-
function parseRequestLogMigrationArgs(args) {
|
|
324
|
-
let batchRows;
|
|
325
|
-
let dryRun = false;
|
|
326
|
-
const parseBatch = (value) => {
|
|
327
|
-
const parsed = Number(value);
|
|
328
|
-
if (!value || !Number.isSafeInteger(parsed) || parsed < 1) {
|
|
329
|
-
throw new RequestLogMigrationUsageError('--batch must be a positive integer.');
|
|
330
|
-
}
|
|
331
|
-
return parsed;
|
|
332
|
-
};
|
|
333
|
-
for (let index = 0; index < args.length; index += 1) {
|
|
334
|
-
const arg = args[index];
|
|
335
|
-
if (arg === '--help' || arg === '-h')
|
|
336
|
-
throw new RequestLogMigrationUsageError(exports.REQUEST_LOG_MIGRATION_HELP);
|
|
337
|
-
if (arg === '--dry-run') {
|
|
338
|
-
dryRun = true;
|
|
339
|
-
continue;
|
|
340
|
-
}
|
|
341
|
-
if (arg === '--batch') {
|
|
342
|
-
batchRows = parseBatch(args[index + 1]);
|
|
343
|
-
index += 1;
|
|
344
|
-
continue;
|
|
345
|
-
}
|
|
346
|
-
if (arg.startsWith('--batch=')) {
|
|
347
|
-
batchRows = parseBatch(arg.slice('--batch='.length));
|
|
348
|
-
continue;
|
|
349
|
-
}
|
|
350
|
-
throw new RequestLogMigrationUsageError(`Unknown option: ${arg}`);
|
|
351
|
-
}
|
|
352
|
-
return { batchRows, dryRun };
|
|
353
|
-
}
|
|
354
|
-
function formatBytes(bytes) {
|
|
355
|
-
if (bytes < 1024)
|
|
356
|
-
return `${bytes} B`;
|
|
357
|
-
const units = ['KiB', 'MiB', 'GiB', 'TiB'];
|
|
358
|
-
let value = bytes;
|
|
359
|
-
let unit = -1;
|
|
360
|
-
while (value >= 1024 && unit < units.length - 1) {
|
|
361
|
-
value /= 1024;
|
|
362
|
-
unit += 1;
|
|
363
|
-
}
|
|
364
|
-
return `${value.toFixed(value >= 100 ? 0 : 1)} ${units[unit]} (${bytes} bytes)`;
|
|
365
|
-
}
|
|
366
|
-
function percentile(values, p) {
|
|
367
|
-
const sorted = [...values].sort((a, b) => a - b);
|
|
368
|
-
return sorted[Math.min(sorted.length - 1, Math.floor((p / 100) * sorted.length))];
|
|
369
|
-
}
|
|
370
|
-
function formatSeconds(ms) {
|
|
371
|
-
return `${(ms / 1000).toFixed(1)}s`;
|
|
372
|
-
}
|
|
373
|
-
const REQUEST_LOG_PROGRESS_INTERVAL_MS = 10_000;
|
|
374
|
-
function printRequestLogSummary(result, status) {
|
|
375
|
-
if (result.dryRun) {
|
|
376
|
-
const scan = result.scan;
|
|
377
|
-
console.log('[crewx] Dry run finished — nothing was written.');
|
|
378
|
-
console.log(` Rows scanned: ${scan?.rows ?? 0}`);
|
|
379
|
-
console.log(` Bodies: ${scan?.bodies ?? 0} (${formatBytes(scan?.bodyBytes ?? 0)})`);
|
|
380
|
-
console.log(` Distinct bodies: ${scan?.distinctBodies ?? 0} (${formatBytes(scan?.distinctBodyBytes ?? 0)}) — what logs.db would store`);
|
|
381
|
-
console.log(` Elapsed: ${formatSeconds(result.elapsedMs)}`);
|
|
382
|
-
return;
|
|
383
|
-
}
|
|
384
|
-
console.log('[crewx] Request-log move finished.');
|
|
385
|
-
console.log(` Moved rows: ${result.movedRows}`);
|
|
386
|
-
console.log(` Remaining rows in crewx.db: ${status.crewxRows}`);
|
|
387
|
-
console.log(` logs.db request_logs: ${status.logsRows ?? 0}`);
|
|
388
|
-
console.log(` logs.db request_log_bodies: ${status.logsBodies ?? 0}`);
|
|
389
|
-
console.log(` logs.db size: ${formatBytes(status.logsFileBytes)} (+ wal ${formatBytes(status.logsWalBytes)})`);
|
|
390
|
-
console.log(` crewx.db freelist pages: ${status.crewxFreelistPages}`);
|
|
391
|
-
console.log(` Batches: ${result.batches}, bodies added: ${result.bodiesAdded}`);
|
|
392
|
-
if (result.deleteTxMs.length > 0) {
|
|
393
|
-
console.log(` crewx.db delete transaction per batch: p50 ${percentile(result.deleteTxMs, 50).toFixed(1)} ms, p90 ${percentile(result.deleteTxMs, 90).toFixed(1)} ms, max ${result.maxDeleteTxMs.toFixed(1)} ms`);
|
|
394
|
-
console.log(` Largest crewx.db -wal seen after a batch: ${formatBytes(result.maxWalBytes)}`);
|
|
395
|
-
}
|
|
396
|
-
console.log(` Elapsed: ${formatSeconds(result.elapsedMs)}`);
|
|
397
|
-
if (status.crewxRows > 0) {
|
|
398
|
-
console.log(` ${status.crewxRows} row(s) are still in crewx.db (new rows arrived or the run stopped early). Run it again.`);
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
async function handleRequestLogMigration(args) {
|
|
402
|
-
let parsed;
|
|
403
|
-
try {
|
|
404
|
-
parsed = parseRequestLogMigrationArgs(args);
|
|
405
|
-
}
|
|
406
|
-
catch (error) {
|
|
407
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
408
|
-
if (message === exports.REQUEST_LOG_MIGRATION_HELP) {
|
|
409
|
-
console.log(message);
|
|
410
|
-
}
|
|
411
|
-
else {
|
|
412
|
-
console.error(message);
|
|
413
|
-
console.error(exports.REQUEST_LOG_MIGRATION_HELP);
|
|
414
|
-
process.exitCode = 2;
|
|
415
|
-
}
|
|
416
|
-
return;
|
|
417
|
-
}
|
|
418
|
-
try {
|
|
419
|
-
const before = (0, repository_4.inspectRequestLogsMove)();
|
|
420
|
-
console.log(`[crewx] Request logs → logs.db${parsed.dryRun ? ' (dry run)' : ''}`);
|
|
421
|
-
console.log(` crewx.db: ${before.crewxDbPath}`);
|
|
422
|
-
console.log(` logs.db: ${before.logsDbPath}`);
|
|
423
|
-
console.log(` Rows to move: ${before.crewxRows}`);
|
|
424
|
-
let lastPrint = Date.now();
|
|
425
|
-
const onProgress = (progress) => {
|
|
426
|
-
const now = Date.now();
|
|
427
|
-
if (now - lastPrint < REQUEST_LOG_PROGRESS_INTERVAL_MS)
|
|
428
|
-
return;
|
|
429
|
-
lastPrint = now;
|
|
430
|
-
const done = parsed.dryRun ? progress.scannedRows : progress.movedRows;
|
|
431
|
-
const remaining = Math.max(0, before.crewxRows - done);
|
|
432
|
-
console.log(` ${parsed.dryRun ? 'scanned' : 'moved'} ${done} | remaining ~${remaining} | elapsed ${formatSeconds(progress.elapsedMs)}`);
|
|
433
|
-
};
|
|
434
|
-
const result = (0, repository_4.moveRequestLogsToLogsDb)({
|
|
435
|
-
batchRows: parsed.batchRows,
|
|
436
|
-
dryRun: parsed.dryRun,
|
|
437
|
-
onProgress,
|
|
438
|
-
});
|
|
439
|
-
printRequestLogSummary(result, (0, repository_4.inspectRequestLogsMove)());
|
|
440
|
-
process.exitCode = parsed.dryRun || result.remainingRows === 0 ? 0 : 1;
|
|
441
|
-
}
|
|
442
|
-
catch (error) {
|
|
443
|
-
console.error(error instanceof Error ? error.message : String(error));
|
|
444
|
-
process.exitCode = 1;
|
|
445
|
-
}
|
|
446
|
-
}
|
|
447
|
-
function parseUsageBackfillArgs(args) {
|
|
448
|
-
let mode;
|
|
449
|
-
let dbPath;
|
|
450
|
-
let backupPath;
|
|
451
|
-
let restorePath;
|
|
452
|
-
const setMode = (next) => {
|
|
453
|
-
if (mode)
|
|
454
|
-
throw new UsageBackfillUsageError('Choose exactly one of --dry-run, --apply, --verify, or --restore.');
|
|
455
|
-
mode = next;
|
|
456
|
-
};
|
|
457
|
-
const requireValue = (index, flag) => {
|
|
458
|
-
const value = args[index + 1];
|
|
459
|
-
if (!value || value.startsWith('--'))
|
|
460
|
-
throw new UsageBackfillUsageError(`${flag} requires a value.`);
|
|
461
|
-
return value;
|
|
462
|
-
};
|
|
463
|
-
for (let index = 0; index < args.length; index += 1) {
|
|
464
|
-
const arg = args[index];
|
|
465
|
-
if (arg === '--help' || arg === '-h')
|
|
466
|
-
throw new UsageBackfillUsageError(exports.USAGE_BACKFILL_HELP);
|
|
467
|
-
if (arg === '--dry-run') {
|
|
468
|
-
setMode('dry-run');
|
|
469
|
-
continue;
|
|
470
|
-
}
|
|
471
|
-
if (arg === '--apply') {
|
|
472
|
-
setMode('apply');
|
|
473
|
-
continue;
|
|
474
|
-
}
|
|
475
|
-
if (arg === '--verify') {
|
|
476
|
-
setMode('verify');
|
|
477
|
-
continue;
|
|
478
|
-
}
|
|
479
|
-
if (arg === '--restore') {
|
|
480
|
-
setMode('restore');
|
|
481
|
-
restorePath = requireValue(index, '--restore');
|
|
482
|
-
index += 1;
|
|
483
|
-
continue;
|
|
484
|
-
}
|
|
485
|
-
if (arg.startsWith('--restore=')) {
|
|
486
|
-
setMode('restore');
|
|
487
|
-
restorePath = arg.slice('--restore='.length);
|
|
488
|
-
if (!restorePath)
|
|
489
|
-
throw new UsageBackfillUsageError('--restore requires a value.');
|
|
490
|
-
continue;
|
|
491
|
-
}
|
|
492
|
-
if (arg === '--db') {
|
|
493
|
-
dbPath = requireValue(index, '--db');
|
|
494
|
-
index += 1;
|
|
495
|
-
continue;
|
|
496
|
-
}
|
|
497
|
-
if (arg.startsWith('--db=')) {
|
|
498
|
-
dbPath = arg.slice('--db='.length);
|
|
499
|
-
if (!dbPath)
|
|
500
|
-
throw new UsageBackfillUsageError('--db requires a value.');
|
|
501
|
-
continue;
|
|
502
|
-
}
|
|
503
|
-
if (arg === '--backup') {
|
|
504
|
-
backupPath = requireValue(index, '--backup');
|
|
505
|
-
index += 1;
|
|
506
|
-
continue;
|
|
507
|
-
}
|
|
508
|
-
if (arg.startsWith('--backup=')) {
|
|
509
|
-
backupPath = arg.slice('--backup='.length);
|
|
510
|
-
if (!backupPath)
|
|
511
|
-
throw new UsageBackfillUsageError('--backup requires a value.');
|
|
512
|
-
continue;
|
|
513
|
-
}
|
|
514
|
-
throw new UsageBackfillUsageError(`Unknown option: ${arg}`);
|
|
515
|
-
}
|
|
516
|
-
const selectedMode = mode ?? 'dry-run';
|
|
517
|
-
if (backupPath && selectedMode !== 'apply') {
|
|
518
|
-
throw new UsageBackfillUsageError('--backup is only valid with --apply.');
|
|
519
|
-
}
|
|
520
|
-
if (restorePath && selectedMode !== 'restore') {
|
|
521
|
-
throw new UsageBackfillUsageError('--restore is only valid with restore mode.');
|
|
522
|
-
}
|
|
523
|
-
return { mode: selectedMode, dbPath, backupPath, restorePath };
|
|
524
|
-
}
|
|
525
|
-
function formatUsageBackfillReport(report) {
|
|
526
|
-
const modeLabel = report.mode === 'dry-run'
|
|
527
|
-
? 'Dry-run'
|
|
528
|
-
: report.mode === 'apply'
|
|
529
|
-
? 'Apply'
|
|
530
|
-
: report.mode === 'restore'
|
|
531
|
-
? 'Restore'
|
|
532
|
-
: 'Verify';
|
|
533
|
-
const lines = [
|
|
534
|
-
`[crewx] Usage backfill — ${modeLabel}`,
|
|
535
|
-
` Database: ${report.dbPath}`,
|
|
536
|
-
` Tasks scanned: ${report.scannedTasks}`,
|
|
537
|
-
` Terminal tasks: ${report.terminalTasks}`,
|
|
538
|
-
` Raw target tasks: ${report.rawTargetTasks}`,
|
|
539
|
-
` Candidate tasks: ${report.candidateTasks}`,
|
|
540
|
-
` Recovered: ${report.recoveredTasks}`,
|
|
541
|
-
` Unavailable: ${report.unavailableTasks}`,
|
|
542
|
-
` Deferred (within 24h): ${report.deferredTasks}`,
|
|
543
|
-
` Changed: ${report.changedTasks}`,
|
|
544
|
-
` Would change: ${report.wouldChangeTasks}`,
|
|
545
|
-
` Restored: ${report.restored}`,
|
|
546
|
-
` Skipped conflicts: ${report.skippedConflict}`,
|
|
547
|
-
` Remaining target tasks: ${report.remainingTargetTasks}`,
|
|
548
|
-
` Tasks with usageAccounting: ${report.accountingTasks}`,
|
|
549
|
-
' Provider distribution:',
|
|
550
|
-
];
|
|
551
|
-
const providers = Object.entries(report.providerDistribution);
|
|
552
|
-
if (providers.length === 0) {
|
|
553
|
-
lines.push(' (none)');
|
|
554
|
-
}
|
|
555
|
-
else {
|
|
556
|
-
for (const [provider, stats] of providers) {
|
|
557
|
-
lines.push(` ${provider}: candidates=${stats.candidates}, recovered=${stats.recovered}, unavailable=${stats.unavailable}, deferred=${stats.deferred}`);
|
|
558
|
-
}
|
|
559
|
-
}
|
|
560
|
-
lines.push(' Reason counts:');
|
|
561
|
-
for (const [reason, count] of Object.entries(report.reasonCounts))
|
|
562
|
-
lines.push(` ${reason}: ${count}`);
|
|
563
|
-
lines.push(` Estimated free-space need: ${report.estimatedFreeSpaceBytes}`);
|
|
564
|
-
lines.push(` Required free space (with reserve): ${report.requiredFreeSpaceBytes}`);
|
|
565
|
-
lines.push(` Available free space: ${report.availableFreeSpaceBytes}`);
|
|
566
|
-
lines.push(` Free-space gate: ${report.freeSpaceGatePassed ? 'PASS' : 'FAIL'}`);
|
|
567
|
-
const snapshotPath = report.snapshotPath ?? report.backupPath;
|
|
568
|
-
if (snapshotPath) {
|
|
569
|
-
lines.push(` Undo snapshot: ${snapshotPath}`);
|
|
570
|
-
if (report.backupMethod)
|
|
571
|
-
lines.push(` Snapshot method: ${report.backupMethod}`);
|
|
572
|
-
}
|
|
573
|
-
if (report.blockedReason)
|
|
574
|
-
lines.push(` Blocked: ${report.blockedReason}`);
|
|
575
|
-
for (const conflict of report.conflictSamples) {
|
|
576
|
-
lines.push(` Conflict sample: ${conflict.taskId}: ${conflict.fields.join(', ')}`);
|
|
577
|
-
}
|
|
578
|
-
for (const failure of report.failures)
|
|
579
|
-
lines.push(` Failure: ${failure.taskId}: ${failure.reason}`);
|
|
580
|
-
for (const warning of report.warnings)
|
|
581
|
-
lines.push(` Warning: ${warning}`);
|
|
582
|
-
return lines.join('\n');
|
|
583
|
-
}
|
|
584
|
-
async function handleUsageBackfill(args) {
|
|
585
|
-
let parsed;
|
|
586
|
-
try {
|
|
587
|
-
parsed = parseUsageBackfillArgs(args);
|
|
588
|
-
}
|
|
589
|
-
catch (error) {
|
|
590
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
591
|
-
if (message === exports.USAGE_BACKFILL_HELP) {
|
|
592
|
-
console.log(message);
|
|
593
|
-
}
|
|
594
|
-
else {
|
|
595
|
-
console.error(message);
|
|
596
|
-
console.error(exports.USAGE_BACKFILL_HELP);
|
|
597
|
-
process.exitCode = 2;
|
|
598
|
-
}
|
|
599
|
-
return;
|
|
600
|
-
}
|
|
601
|
-
try {
|
|
602
|
-
const report = await (0, repository_5.runUsageBackfill)(parsed);
|
|
603
|
-
console.log(formatUsageBackfillReport(report));
|
|
604
|
-
process.exitCode = report.ok ? 0 : 1;
|
|
605
|
-
}
|
|
606
|
-
catch (error) {
|
|
607
|
-
console.error(error instanceof Error ? error.message : String(error));
|
|
608
|
-
process.exitCode = 1;
|
|
609
|
-
}
|
|
610
|
-
}
|
|
611
|
-
async function handleDbPush(args) {
|
|
612
|
-
const force = args.includes('--force');
|
|
613
|
-
const dryRun = args.includes('--dry-run');
|
|
614
|
-
const dbPath = defaultDbPath();
|
|
615
|
-
const handle = (0, repository_1.openDrizzleDb)(dbPath);
|
|
616
|
-
try {
|
|
617
|
-
// 1. Compute preview (dry-run pass — no changes yet)
|
|
618
|
-
const preview = (0, repository_2.pushSchema)(handle.db, { force, dbPath, dryRun: true });
|
|
619
|
-
console.log(formatPreview(preview, dbPath));
|
|
620
|
-
console.log('');
|
|
621
|
-
if (dryRun) {
|
|
622
|
-
console.log('[crewx] Dry-run mode — no changes applied.');
|
|
623
|
-
return;
|
|
624
|
-
}
|
|
625
|
-
if (!hasChanges(preview)) {
|
|
626
|
-
console.log('[crewx] No changes needed. Database is up to date.');
|
|
627
|
-
return;
|
|
628
|
-
}
|
|
629
|
-
// 2. Confirm unless --force
|
|
630
|
-
if (!force) {
|
|
631
|
-
const answer = await prompt('Apply changes? (y/N): ');
|
|
632
|
-
if (answer.trim().toLowerCase() !== 'y') {
|
|
633
|
-
console.log('[crewx] Aborted.');
|
|
634
|
-
return;
|
|
635
|
-
}
|
|
636
|
-
}
|
|
637
|
-
// 3. Apply
|
|
638
|
-
const result = (0, repository_2.pushSchema)(handle.db, { force, dbPath });
|
|
639
|
-
console.log('');
|
|
640
|
-
console.log('[crewx] Push complete.');
|
|
641
|
-
if (result.created.length > 0) {
|
|
642
|
-
console.log(` Tables created: ${result.created.join(', ')}`);
|
|
643
|
-
}
|
|
644
|
-
if (result.altered.length > 0) {
|
|
645
|
-
console.log(` Columns added: ${result.altered.map((a) => `${a.table}.${a.column}`).join(', ')}`);
|
|
646
|
-
}
|
|
647
|
-
if (result.backupPath) {
|
|
648
|
-
console.log(` Backup: ${result.backupPath}`);
|
|
649
|
-
}
|
|
650
|
-
if (result.created.length === 0 && result.altered.length === 0) {
|
|
651
|
-
console.log(' No changes applied.');
|
|
652
|
-
}
|
|
653
|
-
}
|
|
654
|
-
finally {
|
|
655
|
-
handle.close();
|
|
656
|
-
}
|
|
657
|
-
}
|
|
59
|
+
2 Invalid command-line arguments.`;function E(){return $.default.join(v.default.homedir(),".crewx","crewx.db")}function S(e,o){const s=[];s.push(`[crewx] Database push \u2014 comparing schema vs ${o}`);s.push("");if(e.created.length>0){s.push(` Tables to create:`);for(const n of e.created){s.push(` \u2192 ${n}`)}}else{s.push(" Tables to create: (none)")}if(e.altered.length>0){s.push(" Columns to add:");for(const{table:n,column:r}of e.altered){s.push(` \u2192 ${n}.${r}`)}}else{s.push(" Columns to add: (none)")}if(e.warnings.length>0){s.push(" Warnings:");for(const n of e.warnings){s.push(` \u26A0 ${n}`)}}if(e.backupPath){s.push("");s.push(` Backup: ${e.backupPath}`)}return s.join("\n")}function L(e){return e.created.length>0||e.altered.length>0}function y(e){const o=Object.entries(e??{});return o.length>0?o.map(([s,n])=>`${s}=${n}`).join(", "):"(none)"}function A(e){const o=R.default.createInterface({input:process.stdin,output:process.stdout});return new Promise(s=>{o.question(e,n=>{o.close();s(n)})})}async function P(e){const o=e[0];if(o==="migrate-task-logs"){await B(e.slice(1));return}if(o==="migrate-request-logs"){await G(e.slice(1));return}if(o==="backfill-usage"){await N(e.slice(1));return}if(o==="--help"||o==="-h"){console.log("Usage: crewx db push [--force] [--dry-run]");console.log(exports.TASK_LOG_MIGRATION_HELP);console.log(exports.REQUEST_LOG_MIGRATION_HELP);return}if(!o||o==="push"){await H(e.slice(o==="push"?1:0));return}console.error(`Unknown db subcommand: ${o}`);console.error("Usage: crewx db push [--force] [--dry-run]");console.error(exports.TASK_LOG_MIGRATION_HELP);process.exitCode=2}class d extends Error{}class u extends Error{}function M(e){let o;let s;let n;const r=a=>{if(o)throw new d("Choose exactly one of --dry-run, --apply, or --verify.");o=a};const t=(a,c,l)=>{const i=a[c+1];if(!i||i.startsWith("--"))throw new d(`${l} requires a value.`);return i};for(let a=0;a<e.length;a+=1){const c=e[a];if(c==="--help"||c==="-h"){throw new d(exports.TASK_LOG_MIGRATION_HELP)}if(c==="--dry-run"){r("dry-run");continue}if(c==="--apply"){r("apply");continue}if(c==="--verify"){r("verify");continue}if(c==="--db"){s=t(e,a,"--db");a+=1;continue}if(c.startsWith("--db=")){s=c.slice("--db=".length);if(!s)throw new d("--db requires a value.");continue}if(c==="--batch-tasks"){const l=t(e,a,"--batch-tasks");const i=Number(l);if(!Number.isSafeInteger(i)||i<1){throw new d("--batch-tasks must be a positive integer.")}n=i;a+=1;continue}if(c.startsWith("--batch-tasks=")){const l=c.slice("--batch-tasks=".length);const i=Number(l);if(!Number.isSafeInteger(i)||i<1){throw new d("--batch-tasks must be a positive integer.")}n=i;continue}throw new d(`Unknown option: ${c}`)}if(!o)throw new d("Choose one of --dry-run, --apply, or --verify.");if(n!==void 0&&o!=="apply"){throw new d("--batch-tasks is only valid with --apply.")}return{mode:o,dbPath:s,batchTasks:n}}function C(e){const o=e.mode==="dry-run"?"Dry-run":e.mode==="apply"?"Apply":"Verify";const s=[`[crewx] Task-log migration \u2014 ${o}`,` Database: ${e.dbPath}`,` Tasks scanned: ${e.taskCount}`,` Blob candidates: ${e.candidateTaskCount}`,` Eligible terminal blobs: ${e.eligibleTaskCount}`,` Status counts: ${y(e.statusCounts)}`,` Deferred blob tasks: ${e.deferredTaskCount}`,` Deferred statuses: ${y(e.deferredStatusCounts)}`,` Entries: ${e.entryCount}`,` Source bytes: ${e.sourceBytes}`,` Estimated free-space need: ${e.estimatedFreeSpaceBytes}`,` Required free space (with reserve): ${e.requiredFreeSpaceBytes}`,` Available free space: ${e.availableFreeSpaceBytes}`,` Free-space gate: ${e.freeSpaceGatePassed?"PASS":"FAIL"}`,` Migrated: ${e.migratedTasks} task(s), ${e.migratedEntries} entr${e.migratedEntries===1?"y":"ies"}`,` Skipped event-source tasks: ${e.skippedTasks}`,` Failed tasks: ${e.failedTasks}`,` Logical source bytes removed: ${e.logicalSourceBytesRemoved}`];if(e.remainingTaskCount!==void 0)s.push(` Remaining blob tasks: ${e.remainingTaskCount}`);if(e.backupPath){s.push(` Backup: ${e.backupPath}`);if(e.backupMethod)s.push(` Backup method: ${e.backupMethod}`)}if(e.verification){s.push(` Event rows checked: ${e.verification.eventCount}`);s.push(` Verification issues: ${e.verification.issues.length}`)}if(e.blockedReason)s.push(` Blocked: ${e.blockedReason}`);for(const n of e.warnings??[])s.push(` Warning: ${n}`);return s.join("\n")}async function B(e){let o;try{o=M(e)}catch(s){const n=s instanceof Error?s.message:String(s);if(n===exports.TASK_LOG_MIGRATION_HELP){console.log(n)}else{console.error(n);console.error(exports.TASK_LOG_MIGRATION_HELP);process.exitCode=2}return}try{const s=await(0,x.runTaskLogMigration)(o);console.log(C(s));if(!s.ok){if(s.blockedReason)console.error(s.blockedReason);const n=s.failures??s.malformedRows??[];for(const r of n){console.error(` ${r.taskId}: ${r.reason}`)}process.exitCode=1}else{process.exitCode=0}}catch(s){console.error(s instanceof Error?s.message:String(s));process.exitCode=1}}class b extends Error{}function I(e){let o;let s=false;const n=r=>{const t=Number(r);if(!r||!Number.isSafeInteger(t)||t<1){throw new b("--batch must be a positive integer.")}return t};for(let r=0;r<e.length;r+=1){const t=e[r];if(t==="--help"||t==="-h")throw new b(exports.REQUEST_LOG_MIGRATION_HELP);if(t==="--dry-run"){s=true;continue}if(t==="--batch"){o=n(e[r+1]);r+=1;continue}if(t.startsWith("--batch=")){o=n(t.slice("--batch=".length));continue}throw new b(`Unknown option: ${t}`)}return{batchRows:o,dryRun:s}}function h(e){if(e<1024)return`${e} B`;const o=["KiB","MiB","GiB","TiB"];let s=e;let n=-1;while(s>=1024&&n<o.length-1){s/=1024;n+=1}return`${s.toFixed(s>=100?0:1)} ${o[n]} (${e} bytes)`}function k(e,o){const s=[...e].sort((n,r)=>n-r);return s[Math.min(s.length-1,Math.floor(o/100*s.length))]}function w(e){return`${(e/1e3).toFixed(1)}s`}const q=1e4;function O(e,o){if(e.dryRun){const s=e.scan;console.log("[crewx] Dry run finished \u2014 nothing was written.");console.log(` Rows scanned: ${s?.rows??0}`);console.log(` Bodies: ${s?.bodies??0} (${h(s?.bodyBytes??0)})`);console.log(` Distinct bodies: ${s?.distinctBodies??0} (${h(s?.distinctBodyBytes??0)}) \u2014 what logs.db would store`);console.log(` Elapsed: ${w(e.elapsedMs)}`);return}console.log("[crewx] Request-log move finished.");console.log(` Moved rows: ${e.movedRows}`);console.log(` Remaining rows in crewx.db: ${o.crewxRows}`);console.log(` logs.db request_logs: ${o.logsRows??0}`);console.log(` logs.db request_log_bodies: ${o.logsBodies??0}`);console.log(` logs.db size: ${h(o.logsFileBytes)} (+ wal ${h(o.logsWalBytes)})`);console.log(` crewx.db freelist pages: ${o.crewxFreelistPages}`);console.log(` Batches: ${e.batches}, bodies added: ${e.bodiesAdded}`);if(e.deleteTxMs.length>0){console.log(` crewx.db delete transaction per batch: p50 ${k(e.deleteTxMs,50).toFixed(1)} ms, p90 ${k(e.deleteTxMs,90).toFixed(1)} ms, max ${e.maxDeleteTxMs.toFixed(1)} ms`);console.log(` Largest crewx.db -wal seen after a batch: ${h(e.maxWalBytes)}`)}console.log(` Elapsed: ${w(e.elapsedMs)}`);if(o.crewxRows>0){console.log(` ${o.crewxRows} row(s) are still in crewx.db (new rows arrived or the run stopped early). Run it again.`)}}async function G(e){let o;try{o=I(e)}catch(s){const n=s instanceof Error?s.message:String(s);if(n===exports.REQUEST_LOG_MIGRATION_HELP){console.log(n)}else{console.error(n);console.error(exports.REQUEST_LOG_MIGRATION_HELP);process.exitCode=2}return}try{const s=(0,p.inspectRequestLogsMove)();console.log(`[crewx] Request logs \u2192 logs.db${o.dryRun?" (dry run)":""}`);console.log(` crewx.db: ${s.crewxDbPath}`);console.log(` logs.db: ${s.logsDbPath}`);console.log(` Rows to move: ${s.crewxRows}`);let n=Date.now();const r=a=>{const c=Date.now();if(c-n<q)return;n=c;const l=o.dryRun?a.scannedRows:a.movedRows;const i=Math.max(0,s.crewxRows-l);console.log(` ${o.dryRun?"scanned":"moved"} ${l} | remaining ~${i} | elapsed ${w(a.elapsedMs)}`)};const t=(0,p.moveRequestLogsToLogsDb)({batchRows:o.batchRows,dryRun:o.dryRun,onProgress:r});O(t,(0,p.inspectRequestLogsMove)());process.exitCode=o.dryRun||t.remainingRows===0?0:1}catch(s){console.error(s instanceof Error?s.message:String(s));process.exitCode=1}}function U(e){let o;let s;let n;let r;const t=l=>{if(o)throw new u("Choose exactly one of --dry-run, --apply, --verify, or --restore.");o=l};const a=(l,i)=>{const f=e[l+1];if(!f||f.startsWith("--"))throw new u(`${i} requires a value.`);return f};for(let l=0;l<e.length;l+=1){const i=e[l];if(i==="--help"||i==="-h")throw new u(exports.USAGE_BACKFILL_HELP);if(i==="--dry-run"){t("dry-run");continue}if(i==="--apply"){t("apply");continue}if(i==="--verify"){t("verify");continue}if(i==="--restore"){t("restore");r=a(l,"--restore");l+=1;continue}if(i.startsWith("--restore=")){t("restore");r=i.slice("--restore=".length);if(!r)throw new u("--restore requires a value.");continue}if(i==="--db"){s=a(l,"--db");l+=1;continue}if(i.startsWith("--db=")){s=i.slice("--db=".length);if(!s)throw new u("--db requires a value.");continue}if(i==="--backup"){n=a(l,"--backup");l+=1;continue}if(i.startsWith("--backup=")){n=i.slice("--backup=".length);if(!n)throw new u("--backup requires a value.");continue}throw new u(`Unknown option: ${i}`)}const c=o??"dry-run";if(n&&c!=="apply"){throw new u("--backup is only valid with --apply.")}if(r&&c!=="restore"){throw new u("--restore is only valid with restore mode.")}return{mode:c,dbPath:s,backupPath:n,restorePath:r}}function D(e){const o=e.mode==="dry-run"?"Dry-run":e.mode==="apply"?"Apply":e.mode==="restore"?"Restore":"Verify";const s=[`[crewx] Usage backfill \u2014 ${o}`,` Database: ${e.dbPath}`,` Tasks scanned: ${e.scannedTasks}`,` Terminal tasks: ${e.terminalTasks}`,` Raw target tasks: ${e.rawTargetTasks}`,` Candidate tasks: ${e.candidateTasks}`,` Recovered: ${e.recoveredTasks}`,` Unavailable: ${e.unavailableTasks}`,` Deferred (within 24h): ${e.deferredTasks}`,` Changed: ${e.changedTasks}`,` Would change: ${e.wouldChangeTasks}`,` Restored: ${e.restored}`,` Skipped conflicts: ${e.skippedConflict}`,` Remaining target tasks: ${e.remainingTargetTasks}`,` Tasks with usageAccounting: ${e.accountingTasks}`," Provider distribution:"];const n=Object.entries(e.providerDistribution);if(n.length===0){s.push(" (none)")}else{for(const[t,a]of n){s.push(` ${t}: candidates=${a.candidates}, recovered=${a.recovered}, unavailable=${a.unavailable}, deferred=${a.deferred}`)}}s.push(" Reason counts:");for(const[t,a]of Object.entries(e.reasonCounts))s.push(` ${t}: ${a}`);s.push(` Estimated free-space need: ${e.estimatedFreeSpaceBytes}`);s.push(` Required free space (with reserve): ${e.requiredFreeSpaceBytes}`);s.push(` Available free space: ${e.availableFreeSpaceBytes}`);s.push(` Free-space gate: ${e.freeSpaceGatePassed?"PASS":"FAIL"}`);const r=e.snapshotPath??e.backupPath;if(r){s.push(` Undo snapshot: ${r}`);if(e.backupMethod)s.push(` Snapshot method: ${e.backupMethod}`)}if(e.blockedReason)s.push(` Blocked: ${e.blockedReason}`);for(const t of e.conflictSamples){s.push(` Conflict sample: ${t.taskId}: ${t.fields.join(", ")}`)}for(const t of e.failures)s.push(` Failure: ${t.taskId}: ${t.reason}`);for(const t of e.warnings)s.push(` Warning: ${t}`);return s.join("\n")}async function N(e){let o;try{o=U(e)}catch(s){const n=s instanceof Error?s.message:String(s);if(n===exports.USAGE_BACKFILL_HELP){console.log(n)}else{console.error(n);console.error(exports.USAGE_BACKFILL_HELP);process.exitCode=2}return}try{const s=await(0,_.runUsageBackfill)(o);console.log(D(s));process.exitCode=s.ok?0:1}catch(s){console.error(s instanceof Error?s.message:String(s));process.exitCode=1}}async function H(e){const o=e.includes("--force");const s=e.includes("--dry-run");const n=E();const r=(0,T.openDrizzleDb)(n);try{const t=(0,m.pushSchema)(r.db,{force:o,dbPath:n,dryRun:true});console.log(S(t,n));console.log("");if(s){console.log("[crewx] Dry-run mode \u2014 no changes applied.");return}if(!L(t)){console.log("[crewx] No changes needed. Database is up to date.");return}if(!o){const c=await A("Apply changes? (y/N): ");if(c.trim().toLowerCase()!=="y"){console.log("[crewx] Aborted.");return}}const a=(0,m.pushSchema)(r.db,{force:o,dbPath:n});console.log("");console.log("[crewx] Push complete.");if(a.created.length>0){console.log(` Tables created: ${a.created.join(", ")}`)}if(a.altered.length>0){console.log(` Columns added: ${a.altered.map(c=>`${c.table}.${c.column}`).join(", ")}`)}if(a.backupPath){console.log(` Backup: ${a.backupPath}`)}if(a.created.length===0&&a.altered.length===0){console.log(" No changes applied.")}}finally{r.close()}}
|