@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
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Codex live-sync hook helpers ([LET-10315]).
|
|
4
|
+
*
|
|
5
|
+
* Merge Dreams SessionStart/Stop handlers into ~/.codex/hooks.json and enable
|
|
6
|
+
* `[features].hooks = true` in ~/.codex/config.toml without a full TOML parser.
|
|
7
|
+
* Runtime stdout contract lives in bin/dreams-codex-hook.cjs (always `{}`).
|
|
8
|
+
*/
|
|
9
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
12
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
13
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
14
|
+
}
|
|
15
|
+
Object.defineProperty(o, k2, desc);
|
|
16
|
+
}) : (function(o, m, k, k2) {
|
|
17
|
+
if (k2 === undefined) k2 = k;
|
|
18
|
+
o[k2] = m[k];
|
|
19
|
+
}));
|
|
20
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
21
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
22
|
+
}) : function(o, v) {
|
|
23
|
+
o["default"] = v;
|
|
24
|
+
});
|
|
25
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
26
|
+
var ownKeys = function(o) {
|
|
27
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
28
|
+
var ar = [];
|
|
29
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
30
|
+
return ar;
|
|
31
|
+
};
|
|
32
|
+
return ownKeys(o);
|
|
33
|
+
};
|
|
34
|
+
return function (mod) {
|
|
35
|
+
if (mod && mod.__esModule) return mod;
|
|
36
|
+
var result = {};
|
|
37
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
38
|
+
__setModuleDefault(result, mod);
|
|
39
|
+
return result;
|
|
40
|
+
};
|
|
41
|
+
})();
|
|
42
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
43
|
+
exports.CODEX_HOOK_MARKER = void 0;
|
|
44
|
+
exports.codexHooksJsonPath = codexHooksJsonPath;
|
|
45
|
+
exports.codexConfigTomlPath = codexConfigTomlPath;
|
|
46
|
+
exports.shellQuote = shellQuote;
|
|
47
|
+
exports.buildCodexHookCommand = buildCodexHookCommand;
|
|
48
|
+
exports.mergeCodexHooksJson = mergeCodexHooksJson;
|
|
49
|
+
exports.ensureCodexFeaturesHooks = ensureCodexFeaturesHooks;
|
|
50
|
+
exports.planCodexHooksInstall = planCodexHooksInstall;
|
|
51
|
+
exports.installCodexHooks = installCodexHooks;
|
|
52
|
+
const fs = __importStar(require("node:fs"));
|
|
53
|
+
const os = __importStar(require("node:os"));
|
|
54
|
+
const path = __importStar(require("node:path"));
|
|
55
|
+
exports.CODEX_HOOK_MARKER = "dreams-sync-hook-codex";
|
|
56
|
+
function codexHooksJsonPath(home = os.homedir()) {
|
|
57
|
+
return path.join(home, ".codex", "hooks.json");
|
|
58
|
+
}
|
|
59
|
+
function codexConfigTomlPath(home = os.homedir()) {
|
|
60
|
+
return path.join(home, ".codex", "config.toml");
|
|
61
|
+
}
|
|
62
|
+
/** Quote a path for Codex hook `command` strings (POSIX + Windows-friendly). */
|
|
63
|
+
function shellQuote(value) {
|
|
64
|
+
return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Build the command Codex should run for Dreams.
|
|
68
|
+
* `hookScriptPath` is the absolute dreams-codex-hook.cjs path;
|
|
69
|
+
* `cliPath` is the durable Dreams launcher from onboard.
|
|
70
|
+
* SessionStart uses `wake`; Stop uses `sync`.
|
|
71
|
+
*/
|
|
72
|
+
function buildCodexHookCommand(hookScriptPath, cliPath, mode = "sync") {
|
|
73
|
+
return `node ${shellQuote(hookScriptPath)} ${shellQuote(cliPath)} ${mode} # ${exports.CODEX_HOOK_MARKER}`;
|
|
74
|
+
}
|
|
75
|
+
function isDreamsCodexHandler(command) {
|
|
76
|
+
if (typeof command !== "string")
|
|
77
|
+
return false;
|
|
78
|
+
return (command.includes(exports.CODEX_HOOK_MARKER) ||
|
|
79
|
+
/\b(?:wake|sync)\s+--source\s+codex\b/.test(command) ||
|
|
80
|
+
/\bsync\s+--source\s+codex\b/.test(command));
|
|
81
|
+
}
|
|
82
|
+
function upsertEventHandlers(matchers, command) {
|
|
83
|
+
const next = matchers.map((matcher) => ({ ...matcher, hooks: [...(matcher.hooks ?? [])] }));
|
|
84
|
+
let updated = false;
|
|
85
|
+
for (const matcher of next) {
|
|
86
|
+
const hooks = matcher.hooks ?? [];
|
|
87
|
+
for (let i = 0; i < hooks.length; i++) {
|
|
88
|
+
if (isDreamsCodexHandler(hooks[i]?.command)) {
|
|
89
|
+
hooks[i] = { ...hooks[i], type: "command", command };
|
|
90
|
+
updated = true;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
matcher.hooks = hooks;
|
|
94
|
+
}
|
|
95
|
+
if (updated)
|
|
96
|
+
return next;
|
|
97
|
+
return [...next, { hooks: [{ type: "command", command }] }];
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Merge Dreams Codex handlers into an existing hooks.json document.
|
|
101
|
+
* Preserves unrelated top-level keys and non-Dreams handlers.
|
|
102
|
+
* SessionStart → wake; Stop → sync.
|
|
103
|
+
*/
|
|
104
|
+
function mergeCodexHooksJson(existingRaw, hookScriptPath, cliPath) {
|
|
105
|
+
const wakeCommand = buildCodexHookCommand(hookScriptPath, cliPath, "wake");
|
|
106
|
+
const syncCommand = buildCodexHookCommand(hookScriptPath, cliPath, "sync");
|
|
107
|
+
let doc = {};
|
|
108
|
+
if (existingRaw !== null && existingRaw.trim() !== "") {
|
|
109
|
+
const parsed = JSON.parse(existingRaw);
|
|
110
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
111
|
+
throw new Error("codex hooks.json must be a JSON object");
|
|
112
|
+
}
|
|
113
|
+
doc = { ...parsed };
|
|
114
|
+
}
|
|
115
|
+
const hooksValue = doc.hooks;
|
|
116
|
+
const hooks = hooksValue && typeof hooksValue === "object" && !Array.isArray(hooksValue)
|
|
117
|
+
? { ...hooksValue }
|
|
118
|
+
: {};
|
|
119
|
+
const sessionStart = Array.isArray(hooks.SessionStart) ? hooks.SessionStart : [];
|
|
120
|
+
const stop = Array.isArray(hooks.Stop) ? hooks.Stop : [];
|
|
121
|
+
hooks.SessionStart = upsertEventHandlers(sessionStart, wakeCommand);
|
|
122
|
+
hooks.Stop = upsertEventHandlers(stop, syncCommand);
|
|
123
|
+
doc.hooks = hooks;
|
|
124
|
+
return `${JSON.stringify(doc, null, 2)}\n`;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Ensure `[features].hooks = true` surgically. Does not write deprecated
|
|
128
|
+
* `codex_hooks`. Preserves unrelated keys, comments, and ordering where possible.
|
|
129
|
+
*/
|
|
130
|
+
function ensureCodexFeaturesHooks(toml) {
|
|
131
|
+
const lines = toml.replace(/\r\n/g, "\n").split("\n");
|
|
132
|
+
let featuresStart = -1;
|
|
133
|
+
let featuresEnd = lines.length;
|
|
134
|
+
let hooksLine = -1;
|
|
135
|
+
for (let i = 0; i < lines.length; i++) {
|
|
136
|
+
const line = lines[i];
|
|
137
|
+
const section = line.match(/^\s*\[([^\]]+)\]\s*(#.*)?$/);
|
|
138
|
+
if (!section)
|
|
139
|
+
continue;
|
|
140
|
+
const name = section[1].trim();
|
|
141
|
+
if (name === "features") {
|
|
142
|
+
featuresStart = i;
|
|
143
|
+
featuresEnd = lines.length;
|
|
144
|
+
for (let j = i + 1; j < lines.length; j++) {
|
|
145
|
+
if (/^\s*\[[^\]]+\]\s*(#.*)?$/.test(lines[j])) {
|
|
146
|
+
featuresEnd = j;
|
|
147
|
+
break;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
for (let j = i + 1; j < featuresEnd; j++) {
|
|
151
|
+
if (/^\s*hooks\s*=/.test(lines[j]))
|
|
152
|
+
hooksLine = j;
|
|
153
|
+
}
|
|
154
|
+
break;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
if (featuresStart === -1) {
|
|
158
|
+
const body = toml.replace(/\s*$/, "");
|
|
159
|
+
const prefix = body.length === 0 ? "" : `${body}\n\n`;
|
|
160
|
+
return `${prefix}[features]\nhooks = true\n`;
|
|
161
|
+
}
|
|
162
|
+
if (hooksLine !== -1) {
|
|
163
|
+
const comment = lines[hooksLine].match(/\s*(#.*)$/);
|
|
164
|
+
lines[hooksLine] = `hooks = true${comment ? ` ${comment[1]}` : ""}`;
|
|
165
|
+
return normalizeTrailingNewline(lines.join("\n"));
|
|
166
|
+
}
|
|
167
|
+
// Insert after [features] header (and any blank line immediately following).
|
|
168
|
+
let insertAt = featuresStart + 1;
|
|
169
|
+
while (insertAt < featuresEnd && lines[insertAt].trim() === "")
|
|
170
|
+
insertAt++;
|
|
171
|
+
lines.splice(insertAt, 0, "hooks = true");
|
|
172
|
+
return normalizeTrailingNewline(lines.join("\n"));
|
|
173
|
+
}
|
|
174
|
+
function normalizeTrailingNewline(text) {
|
|
175
|
+
return text.endsWith("\n") ? text : `${text}\n`;
|
|
176
|
+
}
|
|
177
|
+
function planCodexHooksInstall(options) {
|
|
178
|
+
const home = options.home ?? os.homedir();
|
|
179
|
+
const hooksPath = codexHooksJsonPath(home);
|
|
180
|
+
const configPath = codexConfigTomlPath(home);
|
|
181
|
+
let existingHooks = null;
|
|
182
|
+
try {
|
|
183
|
+
existingHooks = fs.readFileSync(hooksPath, "utf8");
|
|
184
|
+
}
|
|
185
|
+
catch {
|
|
186
|
+
existingHooks = null;
|
|
187
|
+
}
|
|
188
|
+
const nextHooks = mergeCodexHooksJson(existingHooks, options.hookScriptPath, options.cliPath);
|
|
189
|
+
let existingConfig = "";
|
|
190
|
+
try {
|
|
191
|
+
existingConfig = fs.readFileSync(configPath, "utf8");
|
|
192
|
+
}
|
|
193
|
+
catch {
|
|
194
|
+
existingConfig = "";
|
|
195
|
+
}
|
|
196
|
+
const nextConfig = ensureCodexFeaturesHooks(existingConfig);
|
|
197
|
+
const stopCommand = buildCodexHookCommand(options.hookScriptPath, options.cliPath, "sync");
|
|
198
|
+
return {
|
|
199
|
+
hooks_path: hooksPath,
|
|
200
|
+
config_path: configPath,
|
|
201
|
+
hooks_changed: existingHooks !== nextHooks,
|
|
202
|
+
config_changed: existingConfig !== nextConfig,
|
|
203
|
+
session_start_command: buildCodexHookCommand(options.hookScriptPath, options.cliPath, "wake"),
|
|
204
|
+
stop_command: stopCommand,
|
|
205
|
+
command: stopCommand,
|
|
206
|
+
next_hooks_contents: nextHooks,
|
|
207
|
+
next_config_contents: nextConfig,
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
/** Idempotent install/repair of user-level Codex Dreams hooks + feature flag. */
|
|
211
|
+
function installCodexHooks(options) {
|
|
212
|
+
const plan = planCodexHooksInstall(options);
|
|
213
|
+
fs.mkdirSync(path.dirname(plan.hooks_path), { recursive: true });
|
|
214
|
+
if (plan.hooks_changed)
|
|
215
|
+
writeAtomic(plan.hooks_path, plan.next_hooks_contents);
|
|
216
|
+
if (plan.config_changed)
|
|
217
|
+
writeAtomic(plan.config_path, plan.next_config_contents);
|
|
218
|
+
return plan;
|
|
219
|
+
}
|
|
220
|
+
function writeAtomic(file, contents) {
|
|
221
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
222
|
+
const temporary = `${file}.tmp-${process.pid}-${Math.random().toString(16).slice(2)}`;
|
|
223
|
+
try {
|
|
224
|
+
fs.writeFileSync(temporary, contents, { encoding: "utf8" });
|
|
225
|
+
fs.renameSync(temporary, file);
|
|
226
|
+
}
|
|
227
|
+
finally {
|
|
228
|
+
try {
|
|
229
|
+
fs.rmSync(temporary, { force: true });
|
|
230
|
+
}
|
|
231
|
+
catch {
|
|
232
|
+
// ignore
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|