@letta-ai/dreams 0.0.3 → 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 +56 -34
- package/bin/dreams-codex-hook.cjs +91 -0
- package/bin/dreams.cjs +21 -0
- package/dist/auth/commands.js +21 -1
- package/dist/auth/credentials.js +73 -14
- package/dist/auth/index.js +106 -13
- package/dist/cli.js +48 -12
- package/dist/local/backfill.js +20 -3
- package/dist/local/claude-hooks.js +169 -0
- package/dist/local/claude.js +76 -20
- package/dist/local/codex-hooks.js +235 -0
- package/dist/local/codex.js +298 -101
- package/dist/local/commands.js +39 -24
- package/dist/local/db.js +4 -0
- package/dist/local/detect.js +6 -2
- package/dist/local/hooks-install.js +102 -0
- package/dist/local/source-adapters.js +13 -0
- package/dist/local/sync-control.js +73 -22
- package/dist/skill.js +202 -110
- package/dist/suppress-sqlite-warning.js +34 -0
- package/dist/sync.js +191 -31
- package/package.json +1 -1
package/dist/local/detect.js
CHANGED
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
* `dreams source detect` — register a harness as a detected Cloud source
|
|
4
4
|
* without reading transcripts, approving roots, scanning, or uploading.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
6
|
+
* CLI defaults to the Claude adapter; pass an explicit adapter for Codex
|
|
7
|
+
* (`dreams source detect --source codex`) ([LET-10313] / [LET-10315]).
|
|
7
8
|
*/
|
|
8
9
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
10
|
exports.detectClaudeCodeHarness = detectClaudeCodeHarness;
|
|
@@ -16,6 +17,7 @@ const version_1 = require("../version");
|
|
|
16
17
|
const skill_1 = require("../skill");
|
|
17
18
|
const bindings_1 = require("./bindings");
|
|
18
19
|
const claude_1 = require("./claude");
|
|
20
|
+
const codex_1 = require("./codex");
|
|
19
21
|
const db_1 = require("./db");
|
|
20
22
|
const leases_1 = require("./leases");
|
|
21
23
|
/**
|
|
@@ -59,7 +61,9 @@ async function runSourceDetect(db, options) {
|
|
|
59
61
|
registration: "skipped",
|
|
60
62
|
message: adapter.sourceType === claude_1.claudeSourceAdapter.sourceType
|
|
61
63
|
? "Claude Code was not detected under ~/.claude"
|
|
62
|
-
:
|
|
64
|
+
: adapter.sourceType === codex_1.CODEX_SOURCE_TYPE
|
|
65
|
+
? "Codex was not detected under ~/.codex"
|
|
66
|
+
: `${adapter.displayName} was not detected`,
|
|
63
67
|
};
|
|
64
68
|
}
|
|
65
69
|
const localId = ensureLocalDetectedSource(db, options.leaseOwner, adapter);
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* `dreams hooks install [--dry-run] [--source <type>] [--json]`
|
|
4
|
+
*
|
|
5
|
+
* CLI owns the mutation; the setup skill owns consent and showing the dry-run.
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.commandHooksInstall = commandHooksInstall;
|
|
9
|
+
const claude_1 = require("./claude");
|
|
10
|
+
const claude_hooks_1 = require("./claude-hooks");
|
|
11
|
+
const codex_1 = require("./codex");
|
|
12
|
+
const codex_hooks_1 = require("./codex-hooks");
|
|
13
|
+
const source_adapters_1 = require("./source-adapters");
|
|
14
|
+
const skill_1 = require("../skill");
|
|
15
|
+
const managed_install_1 = require("../managed-install");
|
|
16
|
+
function emit(json, payload, human) {
|
|
17
|
+
if (json)
|
|
18
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
19
|
+
else
|
|
20
|
+
console.log(human());
|
|
21
|
+
}
|
|
22
|
+
function commandHooksInstall(options) {
|
|
23
|
+
let sources;
|
|
24
|
+
try {
|
|
25
|
+
if (options.sourceType) {
|
|
26
|
+
const adapter = (0, source_adapters_1.resolveSourceAdapter)(options.sourceType);
|
|
27
|
+
if (adapter.sourceType !== "claude-code" && adapter.sourceType !== "codex") {
|
|
28
|
+
process.stderr.write(`hooks install does not support source type ${adapter.sourceType}\n`);
|
|
29
|
+
return 1;
|
|
30
|
+
}
|
|
31
|
+
sources = [adapter.sourceType];
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
sources = ["claude-code", "codex"];
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
|
|
39
|
+
return 1;
|
|
40
|
+
}
|
|
41
|
+
const cliPath = (0, managed_install_1.stableCliPath)();
|
|
42
|
+
const results = [];
|
|
43
|
+
const humanLines = [
|
|
44
|
+
options.dryRun ? "Dreams hooks install (dry-run — no files written)" : "Dreams hooks install",
|
|
45
|
+
"",
|
|
46
|
+
` CLI path: ${cliPath}`,
|
|
47
|
+
];
|
|
48
|
+
for (const source of sources) {
|
|
49
|
+
if (source === "claude-code") {
|
|
50
|
+
const present = (0, claude_1.detectClaudeCodeHarness)().present;
|
|
51
|
+
if (!present && options.sourceType === undefined) {
|
|
52
|
+
humanLines.push("", "Claude Code: harness not detected — skipped");
|
|
53
|
+
results.push({ source: "claude-code", status: "skipped", reason: "harness_not_detected" });
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
const plan = (0, claude_hooks_1.planClaudeHooksInstall)({ cliPath });
|
|
57
|
+
if (!options.dryRun)
|
|
58
|
+
(0, claude_hooks_1.installClaudeHooks)({ cliPath });
|
|
59
|
+
results.push({
|
|
60
|
+
source: "claude-code",
|
|
61
|
+
status: options.dryRun ? "dry_run" : plan.changed ? "updated" : "unchanged",
|
|
62
|
+
settings_path: plan.settings_path,
|
|
63
|
+
changed: plan.changed,
|
|
64
|
+
session_start_command: plan.session_start_command,
|
|
65
|
+
stop_command: plan.stop_command,
|
|
66
|
+
next_contents: options.dryRun ? plan.next_contents : undefined,
|
|
67
|
+
});
|
|
68
|
+
humanLines.push("", `Claude Code (${options.dryRun ? "dry-run" : plan.changed ? "updated" : "unchanged"}):`, ` Settings: ${plan.settings_path}`, ` SessionStart → wake: ${plan.session_start_command}`, ` Stop → sync: ${plan.stop_command}`);
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
const present = (0, codex_1.detectCodexHarness)().present;
|
|
72
|
+
if (!present && options.sourceType === undefined) {
|
|
73
|
+
humanLines.push("", "Codex: harness not detected — skipped");
|
|
74
|
+
results.push({ source: "codex", status: "skipped", reason: "harness_not_detected" });
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
const hookScriptPath = (0, skill_1.codexSyncHookScriptPath)();
|
|
78
|
+
const plan = (0, codex_hooks_1.planCodexHooksInstall)({ cliPath, hookScriptPath });
|
|
79
|
+
if (!options.dryRun)
|
|
80
|
+
(0, codex_hooks_1.installCodexHooks)({ cliPath, hookScriptPath });
|
|
81
|
+
results.push({
|
|
82
|
+
source: "codex",
|
|
83
|
+
status: options.dryRun ? "dry_run" : plan.hooks_changed || plan.config_changed ? "updated" : "unchanged",
|
|
84
|
+
hooks_path: plan.hooks_path,
|
|
85
|
+
config_path: plan.config_path,
|
|
86
|
+
hooks_changed: plan.hooks_changed,
|
|
87
|
+
config_changed: plan.config_changed,
|
|
88
|
+
session_start_command: plan.session_start_command,
|
|
89
|
+
stop_command: plan.stop_command,
|
|
90
|
+
next_hooks_contents: options.dryRun ? plan.next_hooks_contents : undefined,
|
|
91
|
+
next_config_contents: options.dryRun ? plan.next_config_contents : undefined,
|
|
92
|
+
});
|
|
93
|
+
humanLines.push("", `Codex (${options.dryRun ? "dry-run" : plan.hooks_changed || plan.config_changed ? "updated" : "unchanged"}):`, ` Hooks: ${plan.hooks_path}`, ` Config: ${plan.config_path}`, ` SessionStart → wake: ${plan.session_start_command}`, ` Stop → sync: ${plan.stop_command}`, options.dryRun ? " (Also ensures [features].hooks = true in config.toml)" : "");
|
|
94
|
+
}
|
|
95
|
+
emit(options.json, {
|
|
96
|
+
status: "ok",
|
|
97
|
+
dry_run: options.dryRun,
|
|
98
|
+
cli_path: cliPath,
|
|
99
|
+
results,
|
|
100
|
+
}, () => humanLines.filter((line) => line !== "").join("\n"));
|
|
101
|
+
return 0;
|
|
102
|
+
}
|
|
@@ -11,6 +11,7 @@ exports.registerSourceAdapter = registerSourceAdapter;
|
|
|
11
11
|
exports.getSourceAdapterById = getSourceAdapterById;
|
|
12
12
|
exports.getSourceAdapterByType = getSourceAdapterByType;
|
|
13
13
|
exports.defaultSourceAdapter = defaultSourceAdapter;
|
|
14
|
+
exports.resolveSourceAdapter = resolveSourceAdapter;
|
|
14
15
|
exports.listSourceAdapters = listSourceAdapters;
|
|
15
16
|
const claude_1 = require("./claude");
|
|
16
17
|
const codex_1 = require("./codex");
|
|
@@ -51,6 +52,18 @@ function defaultSourceAdapter() {
|
|
|
51
52
|
throw new Error("no default source adapter registered");
|
|
52
53
|
return getSourceAdapterById(defaultSourceId);
|
|
53
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
|
+
}
|
|
54
67
|
function listSourceAdapters() {
|
|
55
68
|
return [...bySourceId.values()];
|
|
56
69
|
}
|
|
@@ -10,7 +10,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
10
10
|
exports.DEFAULT_UPLOAD_ROW_BUDGET = exports.MAX_TRAILING_PASSES = exports.RETRY_BACKOFF_MS = exports.CADENCE_MS = exports.DEFAULT_SOURCE_ID = void 0;
|
|
11
11
|
exports.uploadRowBudget = uploadRowBudget;
|
|
12
12
|
exports.readTrigger = readTrigger;
|
|
13
|
+
exports.readActiveSourceId = readActiveSourceId;
|
|
13
14
|
exports.readRuntime = readRuntime;
|
|
15
|
+
exports.peekRuntime = peekRuntime;
|
|
16
|
+
exports.sourceExists = sourceExists;
|
|
14
17
|
exports.recordSyncRequest = recordSyncRequest;
|
|
15
18
|
exports.setPaused = setPaused;
|
|
16
19
|
exports.isPaused = isPaused;
|
|
@@ -20,6 +23,7 @@ exports.completeSyncPass = completeSyncPass;
|
|
|
20
23
|
exports.markRuntimeDegraded = markRuntimeDegraded;
|
|
21
24
|
exports.earliestUploadRetryAt = earliestUploadRetryAt;
|
|
22
25
|
exports.markRuntimeRunning = markRuntimeRunning;
|
|
26
|
+
exports.clearActiveSourceId = clearActiveSourceId;
|
|
23
27
|
exports.normalizeStaleRunning = normalizeStaleRunning;
|
|
24
28
|
exports.triggerPending = triggerPending;
|
|
25
29
|
exports.sourceIsRunnable = sourceIsRunnable;
|
|
@@ -102,31 +106,60 @@ function globalWorkerState(db) {
|
|
|
102
106
|
* Per-source schedule view + global pause. Cadence/retry fields come from
|
|
103
107
|
* `sync_triggers`; `paused` and global `running` come from `sync_runtime`.
|
|
104
108
|
*/
|
|
105
|
-
function
|
|
109
|
+
function runtimeFromTriggerRow(db, sourceId, row) {
|
|
106
110
|
ensureRuntime(db);
|
|
107
|
-
ensureTrigger(db, sourceId);
|
|
108
111
|
const paused = globalPaused(db);
|
|
109
|
-
|
|
110
|
-
const row = db
|
|
111
|
-
.prepare(`SELECT state, last_attempt_at, last_success_at, last_error_at, last_error_code, last_error_message, next_due_at
|
|
112
|
-
FROM sync_triggers WHERE source_id = ?`)
|
|
113
|
-
.get(sourceId);
|
|
114
|
-
let state = row.state || "idle";
|
|
112
|
+
let state = row?.state || "idle";
|
|
115
113
|
if (paused)
|
|
116
114
|
state = "paused";
|
|
117
|
-
else if (
|
|
115
|
+
else if (readActiveSourceId(db) === sourceId && globalWorkerState(db) === "running") {
|
|
116
|
+
// Only the source that owns the singleton worker is "running".
|
|
118
117
|
state = "running";
|
|
118
|
+
}
|
|
119
119
|
return {
|
|
120
120
|
paused,
|
|
121
121
|
state,
|
|
122
|
-
last_attempt_at: row
|
|
123
|
-
last_success_at: row
|
|
124
|
-
last_error_at: row
|
|
125
|
-
last_error_code: row
|
|
126
|
-
last_error_message: row
|
|
127
|
-
next_due_at: row
|
|
122
|
+
last_attempt_at: row?.last_attempt_at ?? null,
|
|
123
|
+
last_success_at: row?.last_success_at ?? null,
|
|
124
|
+
last_error_at: row?.last_error_at ?? null,
|
|
125
|
+
last_error_code: row?.last_error_code ?? null,
|
|
126
|
+
last_error_message: row?.last_error_message ?? null,
|
|
127
|
+
next_due_at: row?.next_due_at ?? null,
|
|
128
128
|
};
|
|
129
129
|
}
|
|
130
|
+
function readActiveSourceId(db) {
|
|
131
|
+
ensureRuntime(db);
|
|
132
|
+
try {
|
|
133
|
+
const row = db.prepare("SELECT active_source_id FROM sync_runtime WHERE id = 1").get();
|
|
134
|
+
return row?.active_source_id ?? null;
|
|
135
|
+
}
|
|
136
|
+
catch {
|
|
137
|
+
// Pre-migration databases in the middle of openDb won't hit this path.
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
function readRuntime(db, sourceId = exports.DEFAULT_SOURCE_ID) {
|
|
142
|
+
ensureTrigger(db, sourceId);
|
|
143
|
+
const row = db
|
|
144
|
+
.prepare(`SELECT state, last_attempt_at, last_success_at, last_error_at, last_error_code, last_error_message, next_due_at
|
|
145
|
+
FROM sync_triggers WHERE source_id = ?`)
|
|
146
|
+
.get(sourceId);
|
|
147
|
+
return runtimeFromTriggerRow(db, sourceId, row);
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Status-safe runtime peek: never creates source/trigger rows. Missing sources
|
|
151
|
+
* report as idle/not configured ([LET-10317]).
|
|
152
|
+
*/
|
|
153
|
+
function peekRuntime(db, sourceId) {
|
|
154
|
+
const row = db
|
|
155
|
+
.prepare(`SELECT state, last_attempt_at, last_success_at, last_error_at, last_error_code, last_error_message, next_due_at
|
|
156
|
+
FROM sync_triggers WHERE source_id = ?`)
|
|
157
|
+
.get(sourceId);
|
|
158
|
+
return runtimeFromTriggerRow(db, sourceId, row);
|
|
159
|
+
}
|
|
160
|
+
function sourceExists(db, sourceId) {
|
|
161
|
+
return Boolean(db.prepare("SELECT id FROM sources WHERE id = ?").get(sourceId));
|
|
162
|
+
}
|
|
130
163
|
/** Atomic pause check + trigger record. Returns null when paused. */
|
|
131
164
|
function recordSyncRequest(db, options = {}) {
|
|
132
165
|
const sourceId = options.sourceId ?? exports.DEFAULT_SOURCE_ID;
|
|
@@ -272,10 +305,22 @@ function earliestUploadRetryAt(db, sourceId = exports.DEFAULT_SOURCE_ID) {
|
|
|
272
305
|
.get(sourceId);
|
|
273
306
|
return row?.next_at ?? null;
|
|
274
307
|
}
|
|
275
|
-
function markRuntimeRunning(db) {
|
|
308
|
+
function markRuntimeRunning(db, sourceId = exports.DEFAULT_SOURCE_ID) {
|
|
276
309
|
(0, db_1.tx)(db, () => {
|
|
277
310
|
ensureRuntime(db);
|
|
278
|
-
db.prepare(`UPDATE sync_runtime
|
|
311
|
+
db.prepare(`UPDATE sync_runtime
|
|
312
|
+
SET state = CASE WHEN paused = 1 THEN 'paused' ELSE 'running' END,
|
|
313
|
+
active_source_id = ?,
|
|
314
|
+
last_attempt_at = ?,
|
|
315
|
+
updated_at = ?
|
|
316
|
+
WHERE id = 1`).run(sourceId, (0, db_1.nowIso)(), (0, db_1.nowIso)());
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
/** Clear singleton-worker source ownership (call on worker shutdown after lease release). */
|
|
320
|
+
function clearActiveSourceId(db) {
|
|
321
|
+
(0, db_1.tx)(db, () => {
|
|
322
|
+
ensureRuntime(db);
|
|
323
|
+
db.prepare(`UPDATE sync_runtime SET active_source_id = NULL, updated_at = ? WHERE id = 1`).run((0, db_1.nowIso)());
|
|
279
324
|
});
|
|
280
325
|
}
|
|
281
326
|
/** Normalize stale running state when no worker lease is held. */
|
|
@@ -285,11 +330,17 @@ function normalizeStaleRunning(db, workerLeaseHeld) {
|
|
|
285
330
|
(0, db_1.tx)(db, () => {
|
|
286
331
|
ensureRuntime(db);
|
|
287
332
|
const row = db.prepare("SELECT state, paused FROM sync_runtime WHERE id = 1").get();
|
|
288
|
-
if (row.state
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
333
|
+
if (row.state === "running") {
|
|
334
|
+
db.prepare(`UPDATE sync_runtime
|
|
335
|
+
SET state = CASE WHEN paused = 1 THEN 'paused' ELSE 'idle' END,
|
|
336
|
+
active_source_id = NULL,
|
|
337
|
+
updated_at = ?
|
|
338
|
+
WHERE id = 1`).run((0, db_1.nowIso)());
|
|
339
|
+
}
|
|
340
|
+
else {
|
|
341
|
+
// Pass completion may already have set idle while ownership lingered.
|
|
342
|
+
db.prepare(`UPDATE sync_runtime SET active_source_id = NULL, updated_at = ? WHERE id = 1`).run((0, db_1.nowIso)());
|
|
343
|
+
}
|
|
293
344
|
});
|
|
294
345
|
}
|
|
295
346
|
function triggerPending(db, sourceId = exports.DEFAULT_SOURCE_ID) {
|