@onebrain-ai/cli 2.0.1 → 2.0.3
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/README.md +48 -0
- package/dist/onebrain +3 -3
- package/package.json +23 -1
- package/src/commands/doctor.test.ts +0 -416
- package/src/commands/doctor.ts +0 -203
- package/src/commands/init.test.ts +0 -318
- package/src/commands/init.ts +0 -477
- package/src/commands/update.test.ts +0 -413
- package/src/commands/update.ts +0 -353
- package/src/index.ts +0 -144
- package/src/internal/__snapshots__/checkpoint.test.ts.snap +0 -12
- package/src/internal/__snapshots__/orphan-scan.test.ts.snap +0 -13
- package/src/internal/__snapshots__/session-init.test.ts.snap +0 -15
- package/src/internal/checkpoint.test.ts +0 -741
- package/src/internal/checkpoint.ts +0 -427
- package/src/internal/migrate.test.ts +0 -301
- package/src/internal/migrate.ts +0 -186
- package/src/internal/orphan-scan.test.ts +0 -271
- package/src/internal/orphan-scan.ts +0 -213
- package/src/internal/qmd-reindex.test.ts +0 -117
- package/src/internal/qmd-reindex.ts +0 -44
- package/src/internal/register-hooks.test.ts +0 -343
- package/src/internal/register-hooks.ts +0 -418
- package/src/internal/session-init.test.ts +0 -318
- package/src/internal/session-init.ts +0 -264
- package/src/internal/vault-sync.test.ts +0 -419
- package/src/internal/vault-sync.ts +0 -764
- package/tests/integration/init.integration.test.ts +0 -304
- package/tests/integration/update.integration.test.ts +0 -306
- package/tsconfig.json +0 -12
|
@@ -1,427 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* checkpoint — internal command
|
|
3
|
-
*
|
|
4
|
-
* Implements stop/precompact/postcompact/reset modes, replacing checkpoint-hook.sh.
|
|
5
|
-
*
|
|
6
|
-
* State file: $TMPDIR/onebrain-{session_token}.state
|
|
7
|
-
* Format: count:last_ts:last_stop_nn[:pending_stub_filename]
|
|
8
|
-
*
|
|
9
|
-
* Exit code always 0. Errors go to stderr only.
|
|
10
|
-
* JSON decision blocks go to process.stdout.write (no console.log).
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
import { readFileSync, writeFileSync } from 'node:fs';
|
|
14
|
-
import { mkdir, writeFile } from 'node:fs/promises';
|
|
15
|
-
import { tmpdir as osTmpdir } from 'node:os';
|
|
16
|
-
import { join } from 'node:path';
|
|
17
|
-
import { loadVaultConfig } from '@onebrain/core';
|
|
18
|
-
|
|
19
|
-
// ---------------------------------------------------------------------------
|
|
20
|
-
// Constants
|
|
21
|
-
// ---------------------------------------------------------------------------
|
|
22
|
-
|
|
23
|
-
const SKIP_WINDOW = 60; // seconds — suppress re-trigger after reset
|
|
24
|
-
const MIN_ACTIVITY = 2; // minimum messages to warrant checkpoint
|
|
25
|
-
const PRECOMPACT_RECENCY = 300; // seconds — treat checkpoint as "recent" for precompact
|
|
26
|
-
|
|
27
|
-
// Default thresholds (used when vault.yml is missing/unreadable)
|
|
28
|
-
const DEFAULT_MESSAGES_THRESHOLD = 15;
|
|
29
|
-
const DEFAULT_MINUTES_THRESHOLD = 30;
|
|
30
|
-
|
|
31
|
-
// ---------------------------------------------------------------------------
|
|
32
|
-
// Types
|
|
33
|
-
// ---------------------------------------------------------------------------
|
|
34
|
-
|
|
35
|
-
export interface CheckpointState {
|
|
36
|
-
count: number;
|
|
37
|
-
last_ts: number;
|
|
38
|
-
last_stop_nn: string;
|
|
39
|
-
pending_stub?: string;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// ---------------------------------------------------------------------------
|
|
43
|
-
// State helpers
|
|
44
|
-
// ---------------------------------------------------------------------------
|
|
45
|
-
|
|
46
|
-
function stateFilePath(token: string, tmpDir: string): string {
|
|
47
|
-
return join(tmpDir, `onebrain-${token}.state`);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Read state from $tmpDir/onebrain-{token}.state.
|
|
52
|
-
* Returns default state if file is missing or malformed (v1 compat: < 3 fields → parse error).
|
|
53
|
-
* Sync — checkpoint hooks must not add async latency.
|
|
54
|
-
*/
|
|
55
|
-
export function readState(token: string, tmpDir: string = osTmpdir()): CheckpointState {
|
|
56
|
-
const path = stateFilePath(token, tmpDir);
|
|
57
|
-
try {
|
|
58
|
-
const raw = readFileSync(path, 'utf8').trim();
|
|
59
|
-
const parts = raw.split(':');
|
|
60
|
-
// v1 compat: fewer than 3 fields → treat as parse error
|
|
61
|
-
if (parts.length < 3) {
|
|
62
|
-
throw new Error('v1 state format');
|
|
63
|
-
}
|
|
64
|
-
const count = Number(parts[0]);
|
|
65
|
-
const last_ts = Number(parts[1]);
|
|
66
|
-
const last_stop_nn = parts[2] ?? '00';
|
|
67
|
-
const pending_stub = parts[3] && parts[3].length > 0 ? parts[3] : undefined;
|
|
68
|
-
|
|
69
|
-
if (!Number.isInteger(count) || !Number.isInteger(last_ts) || !/^\d{2}$/.test(last_stop_nn)) {
|
|
70
|
-
throw new Error('malformed state');
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
return { count, last_ts, last_stop_nn, pending_stub };
|
|
74
|
-
} catch {
|
|
75
|
-
// Missing or malformed → fresh state
|
|
76
|
-
// last_ts=0: avoids SKIP_WINDOW on first run (guard requires last_ts > 0)
|
|
77
|
-
// and avoids false "recent checkpoint" in precompact (guard requires last_ts > 0)
|
|
78
|
-
// Eagerly rewrite the state file so v1/malformed files don't accumulate.
|
|
79
|
-
const now = Math.floor(Date.now() / 1000);
|
|
80
|
-
try {
|
|
81
|
-
writeFileSync(stateFilePath(token, tmpDir), `0:${now}:00`, 'utf8');
|
|
82
|
-
} catch (writeErr) {
|
|
83
|
-
process.stderr.write(
|
|
84
|
-
`checkpoint: failed to rewrite state file for token ${token}: ${writeErr}\n`,
|
|
85
|
-
);
|
|
86
|
-
}
|
|
87
|
-
return {
|
|
88
|
-
count: 0,
|
|
89
|
-
last_ts: 0,
|
|
90
|
-
last_stop_nn: '00',
|
|
91
|
-
};
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* Write state to $tmpDir/onebrain-{token}.state.
|
|
97
|
-
* 3-field when no pending_stub, 4-field when pending_stub is set.
|
|
98
|
-
* Sync.
|
|
99
|
-
*/
|
|
100
|
-
export function writeState(
|
|
101
|
-
token: string,
|
|
102
|
-
state: CheckpointState,
|
|
103
|
-
tmpDir: string = osTmpdir(),
|
|
104
|
-
): void {
|
|
105
|
-
const path = stateFilePath(token, tmpDir);
|
|
106
|
-
const base = `${state.count}:${state.last_ts}:${state.last_stop_nn}`;
|
|
107
|
-
const content = state.pending_stub !== undefined ? `${base}:${state.pending_stub}` : base;
|
|
108
|
-
try {
|
|
109
|
-
writeFileSync(path, content, 'utf8');
|
|
110
|
-
} catch (err) {
|
|
111
|
-
process.stderr.write(`checkpoint: failed to write state file ${path}: ${err}\n`);
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
// ---------------------------------------------------------------------------
|
|
116
|
-
// Config helper
|
|
117
|
-
// ---------------------------------------------------------------------------
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* Load messages and minutes thresholds from vault.yml.
|
|
121
|
-
* Returns defaults if vault.yml is missing or throws.
|
|
122
|
-
* Sync via readFileSync + yaml inline parse — avoids async in hot path.
|
|
123
|
-
*/
|
|
124
|
-
function loadThresholds(vaultRoot: string): {
|
|
125
|
-
messagesThreshold: number;
|
|
126
|
-
minutesThreshold: number;
|
|
127
|
-
} {
|
|
128
|
-
try {
|
|
129
|
-
const vaultYml = join(vaultRoot, 'vault.yml');
|
|
130
|
-
const raw = readFileSync(vaultYml, 'utf8');
|
|
131
|
-
// Find checkpoint block then parse keys within it
|
|
132
|
-
const checkpointBlock = raw.match(/^checkpoint:\s*\n((?:[ \t]+[^\n]+\n?)*)/m);
|
|
133
|
-
let messages = DEFAULT_MESSAGES_THRESHOLD;
|
|
134
|
-
let minutes = DEFAULT_MINUTES_THRESHOLD;
|
|
135
|
-
if (checkpointBlock?.[1]) {
|
|
136
|
-
const block = checkpointBlock[1];
|
|
137
|
-
const msgMatch = block.match(/messages:\s*(\d+)/);
|
|
138
|
-
const minMatch = block.match(/minutes:\s*(\d+)/);
|
|
139
|
-
if (msgMatch?.[1]) messages = Number(msgMatch[1]);
|
|
140
|
-
if (minMatch?.[1]) minutes = Number(minMatch[1]);
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
return { messagesThreshold: messages, minutesThreshold: minutes };
|
|
144
|
-
} catch {
|
|
145
|
-
return {
|
|
146
|
-
messagesThreshold: DEFAULT_MESSAGES_THRESHOLD,
|
|
147
|
-
minutesThreshold: DEFAULT_MINUTES_THRESHOLD,
|
|
148
|
-
};
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
// ---------------------------------------------------------------------------
|
|
153
|
-
// Date helpers
|
|
154
|
-
// ---------------------------------------------------------------------------
|
|
155
|
-
|
|
156
|
-
function formatDate(epochSeconds: number): string {
|
|
157
|
-
const d = new Date(epochSeconds * 1000);
|
|
158
|
-
const yyyy = d.getFullYear().toString();
|
|
159
|
-
const mm = String(d.getMonth() + 1).padStart(2, '0');
|
|
160
|
-
const dd = String(d.getDate()).padStart(2, '0');
|
|
161
|
-
return `${yyyy}-${mm}-${dd}`;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
function formatYYYY(epochSeconds: number): string {
|
|
165
|
-
return new Date(epochSeconds * 1000).getFullYear().toString();
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
function formatMM(epochSeconds: number): string {
|
|
169
|
-
return String(new Date(epochSeconds * 1000).getMonth() + 1).padStart(2, '0');
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
// ---------------------------------------------------------------------------
|
|
173
|
-
// JSON output helper
|
|
174
|
-
// ---------------------------------------------------------------------------
|
|
175
|
-
|
|
176
|
-
function emitBlock(reason: string): void {
|
|
177
|
-
process.stdout.write(`${JSON.stringify({ decision: 'block', reason })}\n`);
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
// ---------------------------------------------------------------------------
|
|
181
|
-
// reset mode
|
|
182
|
-
// ---------------------------------------------------------------------------
|
|
183
|
-
|
|
184
|
-
/**
|
|
185
|
-
* Reset state: write 0:<now>:00 to state file.
|
|
186
|
-
* No stdout. Exit 0 always.
|
|
187
|
-
*/
|
|
188
|
-
export function handleReset(
|
|
189
|
-
token: string,
|
|
190
|
-
now: number = Math.floor(Date.now() / 1000),
|
|
191
|
-
tmpDir: string = osTmpdir(),
|
|
192
|
-
): void {
|
|
193
|
-
writeState(token, { count: 0, last_ts: now, last_stop_nn: '00' }, tmpDir);
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
// ---------------------------------------------------------------------------
|
|
197
|
-
// stop mode
|
|
198
|
-
// ---------------------------------------------------------------------------
|
|
199
|
-
|
|
200
|
-
/**
|
|
201
|
-
* Stop hook: increment message count, check thresholds, emit block if needed.
|
|
202
|
-
* Sync — no async/await.
|
|
203
|
-
*/
|
|
204
|
-
export function handleStop(
|
|
205
|
-
token: string,
|
|
206
|
-
vaultRoot: string,
|
|
207
|
-
now: number = Math.floor(Date.now() / 1000),
|
|
208
|
-
tmpDir: string = osTmpdir(),
|
|
209
|
-
): void {
|
|
210
|
-
const state = readState(token, tmpDir);
|
|
211
|
-
|
|
212
|
-
// SKIP_WINDOW: if count=0 and last_ts is within 60s, this is right after a /wrapup reset
|
|
213
|
-
if (state.count === 0 && state.last_ts > 0 && now - state.last_ts < SKIP_WINDOW) {
|
|
214
|
-
return; // exit 0, state unchanged
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
// Increment count
|
|
218
|
-
state.count += 1;
|
|
219
|
-
|
|
220
|
-
const { messagesThreshold, minutesThreshold } = loadThresholds(vaultRoot);
|
|
221
|
-
const timeThreshold = minutesThreshold * 60;
|
|
222
|
-
|
|
223
|
-
// Elapsed: last_ts=0 is post-compact sentinel → treat as 0 elapsed
|
|
224
|
-
const elapsed = state.last_ts === 0 ? 0 : now - state.last_ts;
|
|
225
|
-
|
|
226
|
-
const thresholdMet = state.count >= messagesThreshold || elapsed >= timeThreshold;
|
|
227
|
-
|
|
228
|
-
if (!thresholdMet) {
|
|
229
|
-
// Update count but preserve last_ts
|
|
230
|
-
writeState(
|
|
231
|
-
token,
|
|
232
|
-
{ count: state.count, last_ts: state.last_ts, last_stop_nn: state.last_stop_nn },
|
|
233
|
-
tmpDir,
|
|
234
|
-
);
|
|
235
|
-
return;
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
// MIN_ACTIVITY guard: threshold fired but not enough messages
|
|
239
|
-
if (state.count < MIN_ACTIVITY) {
|
|
240
|
-
// Preserve last_ts so time clock doesn't restart
|
|
241
|
-
writeState(
|
|
242
|
-
token,
|
|
243
|
-
{ count: state.count, last_ts: state.last_ts, last_stop_nn: state.last_stop_nn },
|
|
244
|
-
tmpDir,
|
|
245
|
-
);
|
|
246
|
-
return;
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
// Emit checkpoint
|
|
250
|
-
const nextNn = String(Number(state.last_stop_nn) + 1).padStart(2, '0');
|
|
251
|
-
const date = formatDate(now);
|
|
252
|
-
const filename = `${date}-${token}-checkpoint-${nextNn}.md`;
|
|
253
|
-
const since =
|
|
254
|
-
state.last_stop_nn === '00' ? ' since start' : ` since checkpoint-${state.last_stop_nn}`;
|
|
255
|
-
emitBlock(`${filename}${since}`);
|
|
256
|
-
|
|
257
|
-
// Reset state
|
|
258
|
-
writeState(token, { count: 0, last_ts: now, last_stop_nn: nextNn }, tmpDir);
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
// ---------------------------------------------------------------------------
|
|
262
|
-
// precompact mode
|
|
263
|
-
// ---------------------------------------------------------------------------
|
|
264
|
-
|
|
265
|
-
const PRECOMPACT_STUB_TEMPLATE = (date: string, nn: string): string => `---
|
|
266
|
-
tags: [checkpoint, session-log]
|
|
267
|
-
date: ${date}
|
|
268
|
-
checkpoint: ${nn}
|
|
269
|
-
trigger: precompact
|
|
270
|
-
merged: false
|
|
271
|
-
---
|
|
272
|
-
|
|
273
|
-
## What We Worked On
|
|
274
|
-
|
|
275
|
-
<!-- stub: written automatically before compact — fill in via postcompact -->
|
|
276
|
-
|
|
277
|
-
## Key Decisions
|
|
278
|
-
|
|
279
|
-
-
|
|
280
|
-
|
|
281
|
-
## Insights & Learnings
|
|
282
|
-
|
|
283
|
-
-
|
|
284
|
-
|
|
285
|
-
## What Worked / Didn't Work
|
|
286
|
-
|
|
287
|
-
-
|
|
288
|
-
|
|
289
|
-
## Action Items
|
|
290
|
-
|
|
291
|
-
-
|
|
292
|
-
|
|
293
|
-
## Open Questions
|
|
294
|
-
|
|
295
|
-
-
|
|
296
|
-
`;
|
|
297
|
-
|
|
298
|
-
/**
|
|
299
|
-
* Precompact hook: ensure a checkpoint exists before compact.
|
|
300
|
-
* If a checkpoint was written within the last 5 minutes, let compact proceed (no-op).
|
|
301
|
-
* Otherwise write a stub file and update state to 4-field.
|
|
302
|
-
* Async (file writes).
|
|
303
|
-
*/
|
|
304
|
-
export async function handlePrecompact(
|
|
305
|
-
token: string,
|
|
306
|
-
vaultRoot: string,
|
|
307
|
-
now: number = Math.floor(Date.now() / 1000),
|
|
308
|
-
tmpDir: string = osTmpdir(),
|
|
309
|
-
): Promise<void> {
|
|
310
|
-
const state = readState(token, tmpDir);
|
|
311
|
-
|
|
312
|
-
// Recency check: if last checkpoint < 5 minutes ago, let compact proceed
|
|
313
|
-
if (state.last_ts > 0 && now - state.last_ts < PRECOMPACT_RECENCY) {
|
|
314
|
-
return; // no-op
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
// Compute stub NN (last_stop_nn + 1, does NOT update last_stop_nn in state)
|
|
318
|
-
const stubNn = String(Number(state.last_stop_nn) + 1).padStart(2, '0');
|
|
319
|
-
const date = formatDate(now);
|
|
320
|
-
const stubFilename = `${date}-${token}-checkpoint-${stubNn}.md`;
|
|
321
|
-
|
|
322
|
-
// Determine logs folder from vault.yml (fallback to '07-logs')
|
|
323
|
-
let logsFolder = '07-logs';
|
|
324
|
-
try {
|
|
325
|
-
const config = await loadVaultConfig(vaultRoot);
|
|
326
|
-
logsFolder = config.folders.logs;
|
|
327
|
-
} catch {
|
|
328
|
-
// use default
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
const yyyy = formatYYYY(now);
|
|
332
|
-
const mm = formatMM(now);
|
|
333
|
-
const stubDir = join(vaultRoot, logsFolder, yyyy, mm);
|
|
334
|
-
const stubPath = join(stubDir, stubFilename);
|
|
335
|
-
|
|
336
|
-
try {
|
|
337
|
-
await mkdir(stubDir, { recursive: true });
|
|
338
|
-
await writeFile(stubPath, PRECOMPACT_STUB_TEMPLATE(date, stubNn), 'utf8');
|
|
339
|
-
} catch (err) {
|
|
340
|
-
process.stderr.write(`checkpoint: failed to write stub file ${stubPath}: ${err}\n`);
|
|
341
|
-
return;
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
// Update state: count=0, last_ts UNCHANGED, last_stop_nn UNCHANGED, pending_stub set
|
|
345
|
-
writeState(
|
|
346
|
-
token,
|
|
347
|
-
{
|
|
348
|
-
count: 0,
|
|
349
|
-
last_ts: state.last_ts,
|
|
350
|
-
last_stop_nn: state.last_stop_nn,
|
|
351
|
-
pending_stub: stubFilename,
|
|
352
|
-
},
|
|
353
|
-
tmpDir,
|
|
354
|
-
);
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
// ---------------------------------------------------------------------------
|
|
358
|
-
// postcompact mode
|
|
359
|
-
// ---------------------------------------------------------------------------
|
|
360
|
-
|
|
361
|
-
/**
|
|
362
|
-
* Postcompact hook: handle pending stub from precompact.
|
|
363
|
-
* If no pending stub: preserve last_ts, write clean 3-field state.
|
|
364
|
-
* If pending stub: emit fill-checkpoint block, clear pending_stub, set last_ts=0.
|
|
365
|
-
* Sync.
|
|
366
|
-
*/
|
|
367
|
-
export function handlePostcompact(
|
|
368
|
-
token: string,
|
|
369
|
-
// _now kept for API symmetry with handleStop/handlePrecompact so call sites can pass now as 2nd arg
|
|
370
|
-
_now: number = Math.floor(Date.now() / 1000),
|
|
371
|
-
tmpDir: string = osTmpdir(),
|
|
372
|
-
): void {
|
|
373
|
-
const state = readState(token, tmpDir);
|
|
374
|
-
|
|
375
|
-
if (!state.pending_stub) {
|
|
376
|
-
// No pending stub — preserve last_ts, write 3-field
|
|
377
|
-
writeState(
|
|
378
|
-
token,
|
|
379
|
-
{ count: 0, last_ts: state.last_ts, last_stop_nn: state.last_stop_nn },
|
|
380
|
-
tmpDir,
|
|
381
|
-
);
|
|
382
|
-
return;
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
// Pending stub found — emit fill-checkpoint block
|
|
386
|
-
const since =
|
|
387
|
-
state.last_stop_nn === '00' ? ' since start' : ` since checkpoint-${state.last_stop_nn}`;
|
|
388
|
-
emitBlock(`fill-checkpoint: ${state.pending_stub}${since}`);
|
|
389
|
-
|
|
390
|
-
// Clear pending_stub, set last_ts=0 sentinel
|
|
391
|
-
writeState(token, { count: 0, last_ts: 0, last_stop_nn: state.last_stop_nn }, tmpDir);
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
// ---------------------------------------------------------------------------
|
|
395
|
-
// CLI entry point
|
|
396
|
-
// ---------------------------------------------------------------------------
|
|
397
|
-
|
|
398
|
-
/**
|
|
399
|
-
* Dispatch to the correct mode handler.
|
|
400
|
-
* Always exits 0 (errors to stderr only).
|
|
401
|
-
*/
|
|
402
|
-
export async function checkpointCommand(
|
|
403
|
-
mode: string,
|
|
404
|
-
token: string,
|
|
405
|
-
vaultRoot: string,
|
|
406
|
-
): Promise<void> {
|
|
407
|
-
try {
|
|
408
|
-
switch (mode) {
|
|
409
|
-
case 'stop':
|
|
410
|
-
handleStop(token, vaultRoot);
|
|
411
|
-
break;
|
|
412
|
-
case 'precompact':
|
|
413
|
-
await handlePrecompact(token, vaultRoot);
|
|
414
|
-
break;
|
|
415
|
-
case 'postcompact':
|
|
416
|
-
handlePostcompact(token);
|
|
417
|
-
break;
|
|
418
|
-
case 'reset':
|
|
419
|
-
handleReset(token);
|
|
420
|
-
break;
|
|
421
|
-
default:
|
|
422
|
-
process.stderr.write(`checkpoint: unknown mode '${mode}'\n`);
|
|
423
|
-
}
|
|
424
|
-
} catch (err) {
|
|
425
|
-
process.stderr.write(`checkpoint: unexpected error in ${mode} mode: ${err}\n`);
|
|
426
|
-
}
|
|
427
|
-
}
|