@letta-ai/dreams 0.0.1 → 0.0.4
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 +111 -35
- package/bin/dreams-codex-hook.cjs +91 -0
- package/bin/dreams.cjs +21 -0
- package/dist/auth/commands.js +67 -5
- package/dist/auth/credentials.js +101 -22
- package/dist/auth/index.js +106 -13
- package/dist/cli.js +239 -13
- package/dist/cloud/client.js +89 -5
- package/dist/cloud/upload.js +150 -70
- package/dist/development-reset.js +359 -0
- package/dist/launcher.js +311 -0
- package/dist/local/backfill.js +625 -0
- package/dist/local/bindings.js +30 -0
- package/dist/local/claude-hooks.js +169 -0
- package/dist/local/claude.js +130 -176
- package/dist/local/codex-hooks.js +235 -0
- package/dist/local/codex.js +510 -0
- package/dist/local/commands.js +153 -19
- package/dist/local/db.js +68 -0
- package/dist/local/detect.js +148 -0
- package/dist/local/discovered.js +6 -0
- package/dist/local/hooks-install.js +102 -0
- package/dist/local/leases.js +3 -1
- package/dist/local/scan.js +99 -17
- package/dist/local/source-adapter.js +8 -0
- package/dist/local/source-adapters.js +71 -0
- package/dist/local/state-lock.js +88 -0
- package/dist/local/sync-control.js +432 -0
- package/dist/local/transcript-bytes.js +204 -0
- package/dist/managed-install.js +74 -86
- package/dist/onboard.js +26 -72
- package/dist/skill.js +373 -16
- package/dist/snapshots.js +210 -0
- package/dist/suppress-sqlite-warning.js +34 -0
- package/dist/sync.js +950 -0
- package/package.json +1 -1
package/dist/local/scan.js
CHANGED
|
@@ -46,7 +46,7 @@ exports.reconcile = reconcile;
|
|
|
46
46
|
exports.listPendingQueue = listPendingQueue;
|
|
47
47
|
const path = __importStar(require("node:path"));
|
|
48
48
|
const blobs_1 = require("./blobs");
|
|
49
|
-
const
|
|
49
|
+
const transcript_bytes_1 = require("./transcript-bytes");
|
|
50
50
|
const config_1 = require("./config");
|
|
51
51
|
const db_1 = require("./db");
|
|
52
52
|
const dreamignore_1 = require("./dreamignore");
|
|
@@ -59,6 +59,13 @@ class PolicyChangedError extends Error {
|
|
|
59
59
|
this.name = "PolicyChangedError";
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
|
+
/** Raised when shouldCancel trips mid-session so pause can stop a large scan. */
|
|
63
|
+
class ScanCancelledError extends Error {
|
|
64
|
+
constructor() {
|
|
65
|
+
super("scan cancelled");
|
|
66
|
+
this.name = "ScanCancelledError";
|
|
67
|
+
}
|
|
68
|
+
}
|
|
62
69
|
/**
|
|
63
70
|
* Deterministic fingerprint of the policy that governs a session's root: the full
|
|
64
71
|
* approved-root set plus that root's `.dreamignore`. Stored on each queued row so
|
|
@@ -67,9 +74,31 @@ class PolicyChangedError extends Error {
|
|
|
67
74
|
function policyFingerprint(roots, approvedRoot, ignoreText) {
|
|
68
75
|
return (0, ids_1.sha256Hex)(Buffer.from(JSON.stringify({ roots: [...roots].sort(), approvedRoot, ignore: ignoreText }), "utf8"));
|
|
69
76
|
}
|
|
77
|
+
/**
|
|
78
|
+
* Soft packing target (`targetSegmentBytes` in design notes): pack ordinary
|
|
79
|
+
* complete JSONL lines until the next line would exceed this. Shared by every
|
|
80
|
+
* SourceAdapter — not Codex-specific.
|
|
81
|
+
*/
|
|
70
82
|
const DEFAULT_MAX_SEGMENT_BYTES = 1024 * 1024;
|
|
71
|
-
/**
|
|
72
|
-
|
|
83
|
+
/**
|
|
84
|
+
* Hard per-record safety limit for a single complete JSONL line. Lines above
|
|
85
|
+
* the soft target but at/under this cap flush the preceding segment and queue
|
|
86
|
+
* alone; only larger lines isolate the session. The final transport gate is
|
|
87
|
+
* Cloud's gzip+base64 content char cap (enforced at upload time).
|
|
88
|
+
*/
|
|
89
|
+
const DEFAULT_MAX_RECORD_BYTES = 8 * 1024 * 1024;
|
|
90
|
+
/**
|
|
91
|
+
* Pure helper retained for unit callers.
|
|
92
|
+
* Lines above `maxSegmentBytes` but at/under `maxRecordBytes` become standalone
|
|
93
|
+
* segments; only lines above `maxRecordBytes` throw.
|
|
94
|
+
*/
|
|
95
|
+
function chunkCompleteLines(tail, maxSegmentBytes, maxRecordBytes = maxSegmentBytes) {
|
|
96
|
+
if (!Number.isSafeInteger(maxSegmentBytes) || maxSegmentBytes <= 0) {
|
|
97
|
+
throw new Error("maxSegmentBytes must be a positive integer");
|
|
98
|
+
}
|
|
99
|
+
if (!Number.isSafeInteger(maxRecordBytes) || maxRecordBytes < maxSegmentBytes) {
|
|
100
|
+
throw new Error("maxRecordBytes must be an integer >= maxSegmentBytes");
|
|
101
|
+
}
|
|
73
102
|
const segments = [];
|
|
74
103
|
let segmentStart = 0;
|
|
75
104
|
let segmentEnd = 0;
|
|
@@ -79,8 +108,18 @@ function chunkCompleteLines(tail, maxSegmentBytes) {
|
|
|
79
108
|
continue;
|
|
80
109
|
const lineEnd = i + 1;
|
|
81
110
|
const lineLength = lineEnd - lineStart;
|
|
82
|
-
if (lineLength >
|
|
83
|
-
throw new RangeError(`JSONL record is ${lineLength} bytes; maximum is ${
|
|
111
|
+
if (lineLength > maxRecordBytes) {
|
|
112
|
+
throw new RangeError(`JSONL record is ${lineLength} bytes; maximum is ${maxRecordBytes}`);
|
|
113
|
+
}
|
|
114
|
+
if (lineLength > maxSegmentBytes) {
|
|
115
|
+
if (segmentEnd > segmentStart)
|
|
116
|
+
segments.push({ relStart: segmentStart, relEnd: segmentEnd });
|
|
117
|
+
segments.push({ relStart: lineStart, relEnd: lineEnd });
|
|
118
|
+
segmentStart = lineEnd;
|
|
119
|
+
segmentEnd = lineEnd;
|
|
120
|
+
lineStart = lineEnd;
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
84
123
|
if (segmentEnd > segmentStart && lineEnd - segmentStart > maxSegmentBytes) {
|
|
85
124
|
segments.push({ relStart: segmentStart, relEnd: segmentEnd });
|
|
86
125
|
segmentStart = lineStart;
|
|
@@ -92,15 +131,15 @@ function chunkCompleteLines(tail, maxSegmentBytes) {
|
|
|
92
131
|
segments.push({ relStart: segmentStart, relEnd: segmentEnd });
|
|
93
132
|
return segments;
|
|
94
133
|
}
|
|
95
|
-
function getOrCreateSource(db, owner) {
|
|
96
|
-
const id =
|
|
134
|
+
function getOrCreateSource(db, owner, adapter) {
|
|
135
|
+
const id = adapter.sourceId;
|
|
97
136
|
return (0, db_1.tx)(db, () => {
|
|
98
137
|
(0, leases_1.assertLeaseHeld)(db, leases_1.SOURCE_LEASE, owner);
|
|
99
138
|
const existing = db.prepare("SELECT id FROM sources WHERE id = ?").get(id);
|
|
100
139
|
if (!existing) {
|
|
101
140
|
const now = (0, db_1.nowIso)();
|
|
102
141
|
db.prepare(`INSERT INTO sources (id, source_type, source_key, display_name, adapter_version, status, created_at, updated_at)
|
|
103
|
-
VALUES (?, ?, ?, ?, ?, '
|
|
142
|
+
VALUES (?, ?, ?, ?, ?, 'detected', ?, ?)`).run(id, adapter.sourceType, adapter.sourceKey, adapter.displayName, adapter.adapterVersion, now, now);
|
|
104
143
|
}
|
|
105
144
|
return id;
|
|
106
145
|
});
|
|
@@ -131,7 +170,7 @@ function isReplacement(stored, cursor, session, onProgress) {
|
|
|
131
170
|
return true;
|
|
132
171
|
const statChanged = session.size !== stored.size || session.mtime_ms !== stored.mtime_ms;
|
|
133
172
|
if (statChanged && cursor.queued_through_offset > 0 && stored.prefix_fp) {
|
|
134
|
-
if ((0,
|
|
173
|
+
if ((0, transcript_bytes_1.prefixFingerprint)(session.real_path, cursor.queued_through_offset, onProgress) !== stored.prefix_fp)
|
|
135
174
|
return true;
|
|
136
175
|
}
|
|
137
176
|
if (stored.first_chunk_fp && session.first_chunk_fp && stored.first_chunk_fp !== session.first_chunk_fp)
|
|
@@ -267,21 +306,43 @@ function recordSessionFailure(db, fileId, code, message, owner) {
|
|
|
267
306
|
db.prepare("UPDATE source_files SET last_error_code = ?, last_error_message = ?, last_error_at = ?, updated_at = ? WHERE id = ?").run(code, message.slice(0, 500), (0, db_1.nowIso)(), (0, db_1.nowIso)(), fileId);
|
|
268
307
|
});
|
|
269
308
|
}
|
|
309
|
+
function clearSessionFailure(db, fileId, owner) {
|
|
310
|
+
(0, leases_1.assertLeaseHeld)(db, leases_1.SOURCE_LEASE, owner);
|
|
311
|
+
db.prepare("UPDATE source_files SET last_error_code = NULL, last_error_message = NULL, last_error_at = NULL, updated_at = ? WHERE id = ?").run((0, db_1.nowIso)(), fileId);
|
|
312
|
+
}
|
|
270
313
|
function reconcile(db, options) {
|
|
271
314
|
const maxSegmentBytes = options.maxSegmentBytes ?? DEFAULT_MAX_SEGMENT_BYTES;
|
|
272
315
|
if (!Number.isSafeInteger(maxSegmentBytes) || maxSegmentBytes <= 0)
|
|
273
316
|
throw new Error("maxSegmentBytes must be a positive integer");
|
|
317
|
+
const maxRecordBytes = options.maxRecordBytes ?? DEFAULT_MAX_RECORD_BYTES;
|
|
318
|
+
if (!Number.isSafeInteger(maxRecordBytes) || maxRecordBytes < maxSegmentBytes) {
|
|
319
|
+
throw new Error("maxRecordBytes must be an integer >= maxSegmentBytes");
|
|
320
|
+
}
|
|
274
321
|
const priority = options.priority ?? "live";
|
|
275
322
|
const owner = options.leaseOwner;
|
|
276
|
-
const
|
|
323
|
+
const adapter = options.adapter;
|
|
324
|
+
const sourceId = getOrCreateSource(db, owner, adapter);
|
|
277
325
|
const roots = (0, config_1.getApprovedRoots)(db);
|
|
278
326
|
const rulesByRoot = new Map();
|
|
279
327
|
reconcileOrphanBlobs(db, owner);
|
|
280
328
|
const objectsCanceled = reconcilePendingPrivacy(db, sourceId, roots, rulesByRoot, owner);
|
|
281
|
-
const
|
|
329
|
+
const discovered = adapter.discoverSessions();
|
|
330
|
+
const mode = options.mode ?? "all";
|
|
331
|
+
let sessions = discovered;
|
|
332
|
+
if (mode === "live") {
|
|
333
|
+
const tracked = new Set(db
|
|
334
|
+
.prepare("SELECT session_id FROM source_files WHERE source_id = ? AND is_current = 1")
|
|
335
|
+
.all(sourceId).map((row) => row.session_id));
|
|
336
|
+
const hints = new Set(options.hintSessionIds ?? []);
|
|
337
|
+
sessions = discovered.filter((session) => tracked.has(session.session_id) || hints.has(session.session_id));
|
|
338
|
+
}
|
|
339
|
+
else if (mode === "backfill") {
|
|
340
|
+
const hints = new Set(options.hintSessionIds ?? []);
|
|
341
|
+
sessions = discovered.filter((session) => hints.has(session.session_id));
|
|
342
|
+
}
|
|
282
343
|
const summary = {
|
|
283
344
|
source_id: sourceId,
|
|
284
|
-
sessions_discovered:
|
|
345
|
+
sessions_discovered: discovered.length,
|
|
285
346
|
sessions_queued: 0,
|
|
286
347
|
sessions_excluded: 0,
|
|
287
348
|
sessions_skipped_unapproved: 0,
|
|
@@ -291,17 +352,26 @@ function reconcile(db, options) {
|
|
|
291
352
|
segments_enqueued: 0,
|
|
292
353
|
bytes_enqueued: 0,
|
|
293
354
|
queued: [],
|
|
355
|
+
processed_sessions: [],
|
|
294
356
|
};
|
|
295
357
|
for (const session of sessions) {
|
|
358
|
+
if (options.shouldCancel?.())
|
|
359
|
+
break;
|
|
360
|
+
options.onProgress?.();
|
|
296
361
|
if (!(0, leases_1.renewLease)(db, leases_1.SOURCE_LEASE, owner))
|
|
297
362
|
throw new leases_1.LeaseLostError(leases_1.SOURCE_LEASE);
|
|
298
363
|
let lastRenewedAt = Date.now();
|
|
299
364
|
const keepLeaseAlive = () => {
|
|
365
|
+
// Cancel checks are cheap and must run during large single-session scans.
|
|
366
|
+
if (options.shouldCancel?.())
|
|
367
|
+
throw new ScanCancelledError();
|
|
300
368
|
if (Date.now() - lastRenewedAt < 5_000)
|
|
301
369
|
return;
|
|
302
370
|
if (!(0, leases_1.renewLease)(db, leases_1.SOURCE_LEASE, owner))
|
|
303
371
|
throw new leases_1.LeaseLostError(leases_1.SOURCE_LEASE);
|
|
304
372
|
lastRenewedAt = Date.now();
|
|
373
|
+
// Heartbeat only on the throttled renewal path (not once per chunk).
|
|
374
|
+
options.onProgress?.();
|
|
305
375
|
};
|
|
306
376
|
let fileId;
|
|
307
377
|
let generation;
|
|
@@ -358,7 +428,7 @@ function reconcile(db, options) {
|
|
|
358
428
|
prepared.push({
|
|
359
429
|
upload_id: (0, ids_1.segmentId)({
|
|
360
430
|
installation_id: options.installationId,
|
|
361
|
-
source_key:
|
|
431
|
+
source_key: adapter.sourceKey,
|
|
362
432
|
file_identity: session.session_id,
|
|
363
433
|
generation,
|
|
364
434
|
start_offset: segmentStart,
|
|
@@ -375,7 +445,7 @@ function reconcile(db, options) {
|
|
|
375
445
|
segmentLines = [];
|
|
376
446
|
segmentLength = 0;
|
|
377
447
|
};
|
|
378
|
-
const verified = (0,
|
|
448
|
+
const verified = (0, transcript_bytes_1.scanCompleteLinesVerified)(session.real_path, scanStart, session.inode, maxRecordBytes, (line) => {
|
|
379
449
|
while (exclusionIndex < explicitExclusions.length && explicitExclusions[exclusionIndex].end_offset <= line.start_offset) {
|
|
380
450
|
exclusionIndex++;
|
|
381
451
|
}
|
|
@@ -387,6 +457,10 @@ function reconcile(db, options) {
|
|
|
387
457
|
coalesceRange(excludedLines, line.start_offset, line.end_offset);
|
|
388
458
|
return;
|
|
389
459
|
}
|
|
460
|
+
// Soft packing target: flush before adding a line that would overflow.
|
|
461
|
+
// A single complete record above the target (but under the hard cap)
|
|
462
|
+
// becomes its own segment so real Codex multimodal lines (~2.7 MiB)
|
|
463
|
+
// still upload.
|
|
390
464
|
if (segmentLength > 0 && segmentLength + line.bytes.length > maxSegmentBytes)
|
|
391
465
|
flushSegment();
|
|
392
466
|
if (segmentLength === 0)
|
|
@@ -394,6 +468,8 @@ function reconcile(db, options) {
|
|
|
394
468
|
segmentLines.push(line.bytes);
|
|
395
469
|
segmentLength += line.bytes.length;
|
|
396
470
|
segmentEnd = line.end_offset;
|
|
471
|
+
if (line.bytes.length > maxSegmentBytes)
|
|
472
|
+
flushSegment();
|
|
397
473
|
}, keepLeaseAlive);
|
|
398
474
|
flushSegment();
|
|
399
475
|
const newOffset = verified.complete_through_offset;
|
|
@@ -401,7 +477,9 @@ function reconcile(db, options) {
|
|
|
401
477
|
(0, db_1.tx)(db, () => {
|
|
402
478
|
(0, leases_1.assertLeaseHeld)(db, leases_1.SOURCE_LEASE, owner);
|
|
403
479
|
updateFileMeta(db, fileId, session, verified);
|
|
480
|
+
clearSessionFailure(db, fileId, owner);
|
|
404
481
|
});
|
|
482
|
+
summary.processed_sessions.push({ session_id: session.session_id, generation });
|
|
405
483
|
continue;
|
|
406
484
|
}
|
|
407
485
|
const enqueued = [];
|
|
@@ -435,6 +513,7 @@ function reconcile(db, options) {
|
|
|
435
513
|
}
|
|
436
514
|
db.prepare("UPDATE source_cursors SET queued_through_offset = ?, updated_at = ? WHERE source_file_id = ?").run(newOffset, (0, db_1.nowIso)(), fileId);
|
|
437
515
|
updateFileMeta(db, fileId, session, verified);
|
|
516
|
+
clearSessionFailure(db, fileId, owner);
|
|
438
517
|
});
|
|
439
518
|
cleanupUnreferencedBlobs(db, prepared.map((entry) => entry.content_sha256), owner);
|
|
440
519
|
if (excludedLines.length > 0)
|
|
@@ -447,6 +526,7 @@ function reconcile(db, options) {
|
|
|
447
526
|
summary.queued.push(segment);
|
|
448
527
|
}
|
|
449
528
|
}
|
|
529
|
+
summary.processed_sessions.push({ session_id: session.session_id, generation });
|
|
450
530
|
}
|
|
451
531
|
catch (error) {
|
|
452
532
|
// Blobs published before an aborted commit become orphans; remove the ones
|
|
@@ -454,10 +534,12 @@ function reconcile(db, options) {
|
|
|
454
534
|
cleanupUnreferencedBlobs(db, prepared.map((entry) => entry.content_sha256), owner);
|
|
455
535
|
if (error instanceof leases_1.LeaseLostError)
|
|
456
536
|
throw error;
|
|
457
|
-
if (error instanceof
|
|
537
|
+
if (error instanceof ScanCancelledError)
|
|
538
|
+
break;
|
|
539
|
+
if (error instanceof transcript_bytes_1.IdentityChangedError || error instanceof PolicyChangedError)
|
|
458
540
|
continue;
|
|
459
|
-
if (error instanceof
|
|
460
|
-
const code = error instanceof
|
|
541
|
+
if (error instanceof transcript_bytes_1.OversizedLineError || isFilesystemError(error)) {
|
|
542
|
+
const code = error instanceof transcript_bytes_1.OversizedLineError ? "oversized_record" : error.code ?? "fs_error";
|
|
461
543
|
recordSessionFailure(db, fileId, code, error.message, owner);
|
|
462
544
|
summary.sessions_failed++;
|
|
463
545
|
continue;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Multi-source adapter types ([LET-10313]).
|
|
4
|
+
*
|
|
5
|
+
* Types only — registration lives in `source-adapters.ts` so default selection
|
|
6
|
+
* is deterministic and not import-order dependent.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Deterministic source-adapter registry ([LET-10313]).
|
|
4
|
+
*
|
|
5
|
+
* Imports and registers Claude as the explicit default. Callers that need the
|
|
6
|
+
* default (CLI/worker boundaries, tests) import this module — not `claude.ts`
|
|
7
|
+
* for side effects.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.registerSourceAdapter = registerSourceAdapter;
|
|
11
|
+
exports.getSourceAdapterById = getSourceAdapterById;
|
|
12
|
+
exports.getSourceAdapterByType = getSourceAdapterByType;
|
|
13
|
+
exports.defaultSourceAdapter = defaultSourceAdapter;
|
|
14
|
+
exports.resolveSourceAdapter = resolveSourceAdapter;
|
|
15
|
+
exports.listSourceAdapters = listSourceAdapters;
|
|
16
|
+
const claude_1 = require("./claude");
|
|
17
|
+
const codex_1 = require("./codex");
|
|
18
|
+
const bySourceId = new Map();
|
|
19
|
+
const bySourceType = new Map();
|
|
20
|
+
let defaultSourceId = null;
|
|
21
|
+
function registerSourceAdapter(adapter, options = {}) {
|
|
22
|
+
// Validate every conflict before mutating registry state.
|
|
23
|
+
if (bySourceId.has(adapter.sourceId)) {
|
|
24
|
+
throw new Error(`duplicate source id registration: ${adapter.sourceId}`);
|
|
25
|
+
}
|
|
26
|
+
if (bySourceType.has(adapter.sourceType)) {
|
|
27
|
+
throw new Error(`duplicate source type registration: ${adapter.sourceType}`);
|
|
28
|
+
}
|
|
29
|
+
if (options.default && defaultSourceId !== null && defaultSourceId !== adapter.sourceId) {
|
|
30
|
+
throw new Error(`default source adapter already set: ${defaultSourceId}`);
|
|
31
|
+
}
|
|
32
|
+
bySourceId.set(adapter.sourceId, adapter);
|
|
33
|
+
bySourceType.set(adapter.sourceType, adapter);
|
|
34
|
+
if (options.default)
|
|
35
|
+
defaultSourceId = adapter.sourceId;
|
|
36
|
+
}
|
|
37
|
+
function getSourceAdapterById(sourceId) {
|
|
38
|
+
const adapter = bySourceId.get(sourceId);
|
|
39
|
+
if (!adapter)
|
|
40
|
+
throw new Error(`unknown source id: ${sourceId}`);
|
|
41
|
+
return adapter;
|
|
42
|
+
}
|
|
43
|
+
function getSourceAdapterByType(sourceType) {
|
|
44
|
+
const adapter = bySourceType.get(sourceType);
|
|
45
|
+
if (!adapter)
|
|
46
|
+
throw new Error(`unknown source type: ${sourceType}`);
|
|
47
|
+
return adapter;
|
|
48
|
+
}
|
|
49
|
+
/** CLI / worker default — Claude, registered explicitly below. */
|
|
50
|
+
function defaultSourceAdapter() {
|
|
51
|
+
if (!defaultSourceId)
|
|
52
|
+
throw new Error("no default source adapter registered");
|
|
53
|
+
return getSourceAdapterById(defaultSourceId);
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Resolve `--source <type>` at command boundaries (stdin never selects source).
|
|
57
|
+
* Omitted / undefined preserves the Claude default; empty/unknown fail closed.
|
|
58
|
+
*/
|
|
59
|
+
function resolveSourceAdapter(sourceType) {
|
|
60
|
+
if (sourceType === undefined)
|
|
61
|
+
return defaultSourceAdapter();
|
|
62
|
+
const trimmed = sourceType.trim();
|
|
63
|
+
if (!trimmed)
|
|
64
|
+
throw new Error("unknown source type: (empty)");
|
|
65
|
+
return getSourceAdapterByType(trimmed);
|
|
66
|
+
}
|
|
67
|
+
function listSourceAdapters() {
|
|
68
|
+
return [...bySourceId.values()];
|
|
69
|
+
}
|
|
70
|
+
registerSourceAdapter(claude_1.claudeSourceAdapter, { default: true });
|
|
71
|
+
registerSourceAdapter(codex_1.codexSourceAdapter);
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.localStateLockFilePath = localStateLockFilePath;
|
|
37
|
+
exports.acquireLocalStateLock = acquireLocalStateLock;
|
|
38
|
+
exports.waitForLocalStateLock = waitForLocalStateLock;
|
|
39
|
+
exports.releaseLocalStateLock = releaseLocalStateLock;
|
|
40
|
+
const node_sqlite_1 = require("node:sqlite");
|
|
41
|
+
const fs = __importStar(require("node:fs"));
|
|
42
|
+
const path = __importStar(require("node:path"));
|
|
43
|
+
const store_1 = require("../store");
|
|
44
|
+
function localStateLockFilePath() {
|
|
45
|
+
return path.join((0, store_1.dreamsDir)(), "state-lock.db");
|
|
46
|
+
}
|
|
47
|
+
function acquireLocalStateLock(options = {}) {
|
|
48
|
+
const file = localStateLockFilePath();
|
|
49
|
+
const directory = path.dirname(file);
|
|
50
|
+
fs.mkdirSync(directory, { recursive: true, mode: 0o700 });
|
|
51
|
+
const db = new node_sqlite_1.DatabaseSync(file);
|
|
52
|
+
try {
|
|
53
|
+
db.exec("PRAGMA busy_timeout = 0; BEGIN IMMEDIATE");
|
|
54
|
+
if (options.allowAbandonedReset !== true &&
|
|
55
|
+
fs.readdirSync(directory).some((name) => name.startsWith(".reset-staging-") || name.startsWith(".reset-delete-"))) {
|
|
56
|
+
db.exec("ROLLBACK");
|
|
57
|
+
db.close();
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
return { db };
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
db.close();
|
|
64
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
65
|
+
if (/busy|locked/i.test(message))
|
|
66
|
+
return null;
|
|
67
|
+
throw error;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
async function waitForLocalStateLock(timeoutMs = 5_000) {
|
|
71
|
+
const deadline = Date.now() + timeoutMs;
|
|
72
|
+
for (;;) {
|
|
73
|
+
const lock = acquireLocalStateLock();
|
|
74
|
+
if (lock !== null)
|
|
75
|
+
return lock;
|
|
76
|
+
if (Date.now() >= deadline)
|
|
77
|
+
return null;
|
|
78
|
+
await new Promise((resolve) => setTimeout(resolve, 25));
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
function releaseLocalStateLock(lock) {
|
|
82
|
+
try {
|
|
83
|
+
lock.db.exec("ROLLBACK");
|
|
84
|
+
}
|
|
85
|
+
finally {
|
|
86
|
+
lock.db.close();
|
|
87
|
+
}
|
|
88
|
+
}
|