very-happy-cli 0.2.124 → 0.2.126

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.
Files changed (49) hide show
  1. package/dist/{AcpBackend-CT9TOBEM.mjs → AcpBackend-BzGpBAZ0.mjs} +2 -1
  2. package/dist/{AcpBackend-au0veuIF.cjs → AcpBackend-C9eu6CDu.cjs} +30 -29
  3. package/dist/{AcpSessionManager-p1tj7FxF.mjs → AcpSessionManager-B223yT78.mjs} +1 -1
  4. package/dist/{AcpSessionManager-BCWeBKDd.cjs → AcpSessionManager-DQ1e_0gc.cjs} +9 -9
  5. package/dist/agentGuidance-BR0OFumQ.mjs +224 -0
  6. package/dist/agentGuidance-Tm8Z5HYp.cjs +237 -0
  7. package/dist/codex/happyMcpStdioBridge.cjs +29 -6
  8. package/dist/codex/happyMcpStdioBridge.mjs +25 -2
  9. package/dist/{config-DwMic6AC.mjs → config-Bjd1nG0P.mjs} +11 -11
  10. package/dist/{config-GzzqE5Ev.cjs → config-C0mofvCH.cjs} +11 -11
  11. package/dist/{index-B3-daYaX.mjs → index-BP56asuS.mjs} +8 -8
  12. package/dist/{index-DgN9P02i.cjs → index-CMbKpntk.cjs} +53 -19
  13. package/dist/{index-Ba6Fu6MJ.mjs → index-CV6sAa4a.mjs} +49 -15
  14. package/dist/{index-DI_VC3vJ.cjs → index-Cw9Zj1Zr.cjs} +2191 -1864
  15. package/dist/{index-SQKnWyz-.mjs → index-D4eCRu9x.mjs} +2046 -1719
  16. package/dist/{index-C69uF6Up.cjs → index-HW-KdG6W.cjs} +8 -8
  17. package/dist/index.cjs +5 -5
  18. package/dist/index.mjs +5 -5
  19. package/dist/{installTerminalHooks-CvMt8cBI.cjs → installTerminalHooks-CNp1LExn.cjs} +9 -9
  20. package/dist/{installTerminalHooks-Df4hzzdM.mjs → installTerminalHooks-D-CNSalI.mjs} +8 -8
  21. package/dist/lib.cjs +12 -12
  22. package/dist/lib.d.cts +3 -0
  23. package/dist/lib.d.mts +3 -0
  24. package/dist/lib.mjs +9 -9
  25. package/dist/limits-DG1OQnCb.cjs +1198 -0
  26. package/dist/limits-DlbYYtOw.mjs +1129 -0
  27. package/dist/{mcp-DnyLwSPq.cjs → mcp-BtZoIIIM.cjs} +11 -10
  28. package/dist/{mcp-C0oW5u-b.mjs → mcp-D3oV_dH-.mjs} +7 -6
  29. package/dist/resources-CVl7NdOB.cjs +83 -0
  30. package/dist/resources-D-mn7ptO.mjs +80 -0
  31. package/dist/{runGemini-mqLjz-M7.mjs → runGemini-BJ8bU1pP.mjs} +13 -13
  32. package/dist/{runGemini-_FHrxuHN.cjs → runGemini-BfCSOPL1.cjs} +30 -30
  33. package/dist/{runOpenClaw-C5VZQ6Na.cjs → runOpenClaw-CBlHXEAT.cjs} +24 -24
  34. package/dist/{runOpenClaw-Bs03yQHe.mjs → runOpenClaw-DTgOpJnO.mjs} +9 -9
  35. package/dist/{send-BaLjQPZQ.mjs → send-9duZlpGX.mjs} +10 -10
  36. package/dist/{send-BD2gTVoP.cjs → send-CDK1bZFj.cjs} +10 -10
  37. package/dist/{sessions-B108j_Xj.cjs → sessions-CNkLCorF.cjs} +11 -11
  38. package/dist/{sessions-ggBePfSY.mjs → sessions-jAFOkKvW.mjs} +10 -10
  39. package/dist/{spawn-CORFRb9D.mjs → spawn-BG5oHi_f.mjs} +21 -21
  40. package/dist/{spawn-iI8w8sD7.cjs → spawn-J-A5WGZ7.cjs} +21 -21
  41. package/dist/teams-Cuqsk7Ty.mjs +189 -0
  42. package/dist/teams-MgWeyutG.cjs +191 -0
  43. package/dist/{types-CZIwVSN2.cjs → types-P5deH8Ls.cjs} +464 -1571
  44. package/dist/{types-gDOmwOKw.mjs → types-n_VWu9qe.mjs} +78 -1148
  45. package/package.json +7 -7
  46. package/dist/agentGuidance-BifkGXdE.cjs +0 -46
  47. package/dist/agentGuidance-C2_pkgRI.mjs +0 -37
  48. package/dist/limits-CwFb5S2b.mjs +0 -22
  49. package/dist/limits-KdWKmwtH.cjs +0 -28
@@ -1,625 +1,31 @@
1
1
  import axios from 'axios';
2
- import chalk from 'chalk';
3
- import { inspect } from 'node:util';
4
- import { mkdirSync, chmodSync, readdirSync, statSync, appendFileSync, writeFileSync, existsSync, readFileSync, constants, unlinkSync, renameSync, accessSync, createReadStream, createWriteStream } from 'node:fs';
5
- import os$1, { homedir } from 'node:os';
6
- import { join, basename, isAbsolute, resolve as resolve$1 } from 'node:path';
7
- import { mkdir, chmod, writeFile, stat as stat$1, readdir as readdir$1, lstat, open, unlink as unlink$1, readFile as readFile$1, rename as rename$1, copyFile } from 'node:fs/promises';
2
+ import { l as logger, g as encodeBase64, j as encrypt, k as decrypt, m as decodeBase64, c as configuration, o as decryptBlob, q as prepareClipboardText, f as readSettings, r as readPersistedSessions, s as credentialRelayProblem, p as packageJson, t as readTrackedClaudeSessionIds, u as libsodiumEncryptForPublicKey, v as libsodiumPublicKeyFromSecretKey, x as getRandomBytes, y as redactLogValue } from './limits-DlbYYtOw.mjs';
8
3
  import { EventEmitter } from 'node:events';
9
4
  import { io } from 'socket.io-client';
10
5
  import * as z from 'zod';
11
6
  import { z as z$1 } from 'zod';
12
7
  import { isCuid, createId } from '@paralleldrive/cuid2';
13
- import { randomBytes, createCipheriv, createDecipheriv, createHash, createHmac, randomUUID as randomUUID$1 } from 'node:crypto';
14
- import tweetnacl from 'tweetnacl';
15
- import { p as prepareClipboardText } from './limits-CwFb5S2b.mjs';
8
+ import { createHmac, randomUUID as randomUUID$1, randomBytes, createHash as createHash$1 } from 'node:crypto';
16
9
  import { spawn as spawn$1, exec, execSync } from 'child_process';
17
10
  import { promisify } from 'util';
18
- import { mkdir as mkdir$1, writeFile as writeFile$1, appendFile, stat, rename, unlink, readdir, readFile } from 'fs/promises';
19
- import { randomUUID, createHash as createHash$1 } from 'crypto';
20
- import { dirname, resolve, join as join$1, sep } from 'path';
11
+ import { mkdir, writeFile, appendFile, stat, rename, unlink, readdir, readFile } from 'fs/promises';
12
+ import { randomUUID, createHash } from 'crypto';
13
+ import { dirname, resolve, join, sep } from 'path';
21
14
  import { spawn } from 'cross-spawn';
22
15
  import { fileURLToPath } from 'url';
23
16
  import os, { platform } from 'os';
17
+ import { stat as stat$1, readdir as readdir$1, lstat, open, copyFile, unlink as unlink$1, readFile as readFile$1, rename as rename$1, writeFile as writeFile$1 } from 'node:fs/promises';
18
+ import { join as join$1, isAbsolute, resolve as resolve$1 } from 'node:path';
19
+ import os$1, { homedir } from 'node:os';
24
20
  import { spawn as spawn$2, spawnSync, execSync as execSync$1 } from 'node:child_process';
25
21
  import * as pty from 'node-pty';
22
+ import { existsSync, statSync, writeFileSync, unlinkSync, readFileSync, mkdirSync, accessSync, constants, createReadStream, createWriteStream } from 'node:fs';
26
23
  import { existsSync as existsSync$1 } from 'fs';
24
+ import tweetnacl from 'tweetnacl';
27
25
  import { createInterface } from 'node:readline';
28
26
  import { SandboxManager } from '@anthropic-ai/sandbox-runtime';
29
27
  import { Expo } from 'expo-server-sdk';
30
-
31
- var name = "very-happy-cli";
32
- var version = "0.2.124";
33
- var description = "Remote multi-agent workspace CLI with deep Claude integration, native terminal streaming, and self-hosting.";
34
- var author = "Kirill Dubovitskiy";
35
- var contributors = [
36
- "Mereith (very-happy-cli fork)"
37
- ];
38
- var license = "MIT";
39
- var engines = {
40
- node: "^20.19.0 || ^22.13.0 || >=24.0.0"
41
- };
42
- var type = "module";
43
- var homepage = "https://github.com/Mereithhh/very-happy";
44
- var bugs = "https://github.com/Mereithhh/very-happy/issues";
45
- var repository = {
46
- type: "git",
47
- url: "git+https://github.com/Mereithhh/very-happy.git"
48
- };
49
- var bin = {
50
- "very-happy": "./bin/very-happy.mjs",
51
- "very-happy-mcp": "./bin/very-happy-mcp.mjs"
52
- };
53
- var main = "./dist/index.cjs";
54
- var module$1 = "./dist/index.mjs";
55
- var types = "./dist/index.d.cts";
56
- var exports$1 = {
57
- ".": {
58
- require: {
59
- types: "./dist/index.d.cts",
60
- "default": "./dist/index.cjs"
61
- },
62
- "import": {
63
- types: "./dist/index.d.mts",
64
- "default": "./dist/index.mjs"
65
- }
66
- },
67
- "./lib": {
68
- require: {
69
- types: "./dist/lib.d.cts",
70
- "default": "./dist/lib.cjs"
71
- },
72
- "import": {
73
- types: "./dist/lib.d.mts",
74
- "default": "./dist/lib.mjs"
75
- }
76
- },
77
- "./codex/happyMcpStdioBridge": {
78
- require: {
79
- types: "./dist/codex/happyMcpStdioBridge.d.cts",
80
- "default": "./dist/codex/happyMcpStdioBridge.cjs"
81
- },
82
- "import": {
83
- types: "./dist/codex/happyMcpStdioBridge.d.mts",
84
- "default": "./dist/codex/happyMcpStdioBridge.mjs"
85
- }
86
- }
87
- };
88
- var files = [
89
- "dist",
90
- "bin",
91
- "scripts",
92
- "tools/licenses",
93
- "package.json",
94
- "README.md",
95
- "LICENSE"
96
- ];
97
- var scripts = {
98
- typecheck: "tsc --noEmit",
99
- "bundle:server": "node scripts/bundle-server.cjs",
100
- "bundle:server:all": "node scripts/bundle-server.cjs --all-platforms",
101
- "bundle:webapp": "node scripts/bundle-webapp.cjs",
102
- build: "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\" && tsc --noEmit && pkgroll",
103
- test: "pnpm run build && vitest run --project unit",
104
- "test:integration": "pnpm run build && vitest run --project integration-empty --project integration-plan-mode --project integration-authenticated",
105
- "cli:install": "node scripts/install-local.cjs",
106
- prepublishOnly: "pnpm test",
107
- postinstall: "node scripts/unpack-tools.cjs && node scripts/fix-pty-perms.cjs"
108
- };
109
- var dependencies = {
110
- "@agentclientprotocol/sdk": "^0.14.1",
111
- "@anthropic-ai/claude-agent-sdk": "^0.3.232",
112
- "@anthropic-ai/sandbox-runtime": "^0.0.37",
113
- "@modelcontextprotocol/sdk": "1.30.0",
114
- "@noble/ed25519": "^3.0.0",
115
- "@noble/hashes": "^2.0.1",
116
- "@paralleldrive/cuid2": "^2.2.2",
117
- "@stablelib/base64": "^2.0.1",
118
- "@stablelib/hex": "^2.0.1",
119
- "@types/cross-spawn": "^6.0.6",
120
- "@types/http-proxy": "^1.17.17",
121
- "@types/react": "^19.2.7",
122
- "@types/tmp": "^0.2.6",
123
- axios: "^1.13.2",
124
- chalk: "^5.6.2",
125
- "cross-spawn": "^7.0.6",
126
- "expo-server-sdk": "^3.15.0",
127
- fastify: "5.8.5",
128
- "fastify-type-provider-zod": "^6.1.0",
129
- "http-proxy": "^1.18.1",
130
- "http-proxy-middleware": "^3.0.7",
131
- ink: "^6.5.1",
132
- inquirer: "^13.2.2",
133
- "node-pty": "^1.0.0",
134
- open: "^10.2.0",
135
- "ps-list": "^8.1.1",
136
- react: "^19.2.0",
137
- "socket.io-client": "^4.8.1",
138
- tar: "^7.5.22",
139
- tmp: "^0.2.7",
140
- tweetnacl: "^1.0.3",
141
- ws: "^8.21.3",
142
- zod: "^4.0.0"
143
- };
144
- var optionalDependencies = {
145
- "very-happy-tools-arm64-darwin": "0.2.124",
146
- "very-happy-tools-x64-darwin": "0.2.124",
147
- "very-happy-tools-arm64-linux": "0.2.124",
148
- "very-happy-tools-x64-linux": "0.2.124",
149
- "very-happy-tools-arm64-win32": "0.2.124",
150
- "very-happy-tools-x64-win32": "0.2.124"
151
- };
152
- var devDependencies = {
153
- "@slopus/happy-wire": "workspace:*",
154
- "@xterm/addon-serialize": "0.13.0",
155
- "@xterm/headless": "5.5.0",
156
- "@types/inquirer": "^9.0.9",
157
- "@types/node": ">=20",
158
- "@types/ws": "^8.18.1",
159
- "cross-env": "^10.1.0",
160
- dotenv: "^16.6.1",
161
- pkgroll: "^2.14.2",
162
- "ts-node": "^10",
163
- tsx: "^4.20.6",
164
- typescript: "5.9.3",
165
- vitest: "3.2.6"
166
- };
167
- var publishConfig = {
168
- registry: "https://registry.npmjs.org"
169
- };
170
- var packageManager = "pnpm@10.11.0";
171
- var packageJson = {
172
- name: name,
173
- version: version,
174
- description: description,
175
- author: author,
176
- contributors: contributors,
177
- license: license,
178
- engines: engines,
179
- type: type,
180
- homepage: homepage,
181
- bugs: bugs,
182
- repository: repository,
183
- bin: bin,
184
- main: main,
185
- module: module$1,
186
- types: types,
187
- exports: exports$1,
188
- files: files,
189
- scripts: scripts,
190
- dependencies: dependencies,
191
- optionalDependencies: optionalDependencies,
192
- devDependencies: devDependencies,
193
- publishConfig: publishConfig,
194
- packageManager: packageManager
195
- };
196
-
197
- const PRIVATE_DIRECTORY_MODE = 448;
198
- const PRIVATE_FILE_MODE = 384;
199
- function ignoreUnsupportedPermissions(error) {
200
- if (process.platform !== "win32") throw error;
201
- }
202
- function ensurePrivateDirectorySync(path) {
203
- mkdirSync(path, { recursive: true, mode: PRIVATE_DIRECTORY_MODE });
204
- try {
205
- chmodSync(path, PRIVATE_DIRECTORY_MODE);
206
- } catch (error) {
207
- ignoreUnsupportedPermissions(error);
208
- }
209
- }
210
- async function ensurePrivateDirectory(path) {
211
- await mkdir(path, { recursive: true, mode: PRIVATE_DIRECTORY_MODE });
212
- try {
213
- await chmod(path, PRIVATE_DIRECTORY_MODE);
214
- } catch (error) {
215
- ignoreUnsupportedPermissions(error);
216
- }
217
- }
218
- function hardenPrivateFileSync(path) {
219
- try {
220
- chmodSync(path, PRIVATE_FILE_MODE);
221
- } catch (error) {
222
- ignoreUnsupportedPermissions(error);
223
- }
224
- }
225
- function hardenPrivateDirectoryFilesSync(path) {
226
- for (const name of readdirSync(path)) {
227
- const candidate = join(path, name);
228
- try {
229
- if (statSync(candidate).isFile()) hardenPrivateFileSync(candidate);
230
- } catch {
231
- }
232
- }
233
- }
234
- async function hardenPrivateFile(path) {
235
- try {
236
- await chmod(path, PRIVATE_FILE_MODE);
237
- } catch (error) {
238
- ignoreUnsupportedPermissions(error);
239
- }
240
- }
241
- function writePrivateFileSync(path, data) {
242
- writeFileSync(path, data, { encoding: "utf8", mode: PRIVATE_FILE_MODE });
243
- hardenPrivateFileSync(path);
244
- }
245
- async function writePrivateFile(path, data) {
246
- await writeFile(path, data, { encoding: "utf8", mode: PRIVATE_FILE_MODE });
247
- await hardenPrivateFile(path);
248
- }
249
- function appendPrivateFileSync(path, data) {
250
- appendFileSync(path, data, { encoding: "utf8", mode: PRIVATE_FILE_MODE });
251
- hardenPrivateFileSync(path);
252
- }
253
-
254
- const DEFAULT_RELAY_URL = "https://veryhappy.dev";
255
- function normalizeHttpEndpoint(value, source) {
256
- const trimmed = value.trim();
257
- let parsed;
258
- try {
259
- parsed = new URL(trimmed);
260
- } catch {
261
- throw new Error(`${source} must be an absolute http(s) origin`);
262
- }
263
- const onlyOriginPath = /^\/+$/u.test(parsed.pathname);
264
- if (parsed.protocol !== "http:" && parsed.protocol !== "https:" || parsed.username.length > 0 || parsed.password.length > 0 || !onlyOriginPath || parsed.search.length > 0 || parsed.hash.length > 0) {
265
- throw new Error(`${source} must be an http(s) origin without credentials, path, query, or fragment`);
266
- }
267
- return parsed.origin;
268
- }
269
- function resolveHttpEndpoint(environmentValue, settingsValue, environmentName, settingsName) {
270
- if (environmentValue !== void 0) {
271
- return normalizeHttpEndpoint(environmentValue, environmentName);
272
- }
273
- if (settingsValue !== void 0) {
274
- return normalizeHttpEndpoint(settingsValue, `settings.${settingsName}`);
275
- }
276
- return DEFAULT_RELAY_URL;
277
- }
278
- class Configuration {
279
- serverUrl;
280
- webappUrl;
281
- isDaemonProcess;
282
- // Directories and paths (from persistence)
283
- happyHomeDir;
284
- logsDir;
285
- settingsFile;
286
- privateKeyFile;
287
- daemonStateFile;
288
- daemonLockFile;
289
- sessionsFile;
290
- currentCliVersion;
291
- isExperimentalEnabled;
292
- disableCaffeinate;
293
- constructor() {
294
- const args = process.argv.slice(2);
295
- this.isDaemonProcess = args.length >= 2 && args[0] === "daemon" && args[1] === "start-sync";
296
- if (process.env.HAPPY_HOME_DIR) {
297
- const expandedPath = process.env.HAPPY_HOME_DIR.replace(/^~/, homedir());
298
- this.happyHomeDir = expandedPath;
299
- } else {
300
- this.happyHomeDir = join(homedir(), ".happy");
301
- }
302
- this.logsDir = join(this.happyHomeDir, "logs");
303
- this.settingsFile = join(this.happyHomeDir, "settings.json");
304
- this.privateKeyFile = join(this.happyHomeDir, "access.key");
305
- this.daemonStateFile = join(this.happyHomeDir, "daemon.state.json");
306
- this.daemonLockFile = join(this.happyHomeDir, "daemon.state.json.lock");
307
- this.sessionsFile = join(this.happyHomeDir, "sessions.json");
308
- try {
309
- this.serverUrl = resolveHttpEndpoint(
310
- process.env.HAPPY_SERVER_URL,
311
- readSettingsStringSync(this.settingsFile, "serverUrl"),
312
- "HAPPY_SERVER_URL",
313
- "serverUrl"
314
- );
315
- this.webappUrl = resolveHttpEndpoint(
316
- process.env.HAPPY_WEBAPP_URL,
317
- readSettingsStringSync(this.settingsFile, "webappUrl"),
318
- "HAPPY_WEBAPP_URL",
319
- "webappUrl"
320
- );
321
- } catch (error) {
322
- const message = error instanceof Error ? error.message : "invalid client endpoint";
323
- process.stderr.write(`[very-happy] Configuration error: ${message}
324
- `);
325
- process.exit(1);
326
- }
327
- this.isExperimentalEnabled = ["true", "1", "yes"].includes(process.env.HAPPY_EXPERIMENTAL?.toLowerCase() || "");
328
- this.disableCaffeinate = ["true", "1", "yes"].includes(process.env.HAPPY_DISABLE_CAFFEINATE?.toLowerCase() || "");
329
- this.currentCliVersion = packageJson.version;
330
- const variant = process.env.HAPPY_VARIANT || "stable";
331
- if (!this.isDaemonProcess && variant === "dev") {
332
- console.log("\x1B[33m\u{1F527} DEV MODE\x1B[0m - Data: " + this.happyHomeDir);
333
- }
334
- ensurePrivateDirectorySync(this.happyHomeDir);
335
- ensurePrivateDirectorySync(this.logsDir);
336
- for (const privateFile of [
337
- this.settingsFile,
338
- this.privateKeyFile,
339
- this.daemonStateFile,
340
- this.daemonLockFile,
341
- this.sessionsFile
342
- ]) {
343
- if (existsSync(privateFile)) hardenPrivateFileSync(privateFile);
344
- }
345
- hardenPrivateDirectoryFilesSync(this.logsDir);
346
- }
347
- }
348
- function readSettingsStringSync(settingsFile, key) {
349
- try {
350
- if (!existsSync(settingsFile)) return void 0;
351
- const raw = JSON.parse(readFileSync(settingsFile, "utf8"));
352
- const value = raw?.[key];
353
- return typeof value === "string" && value.length > 0 ? value : void 0;
354
- } catch {
355
- return void 0;
356
- }
357
- }
358
- const configuration = new Configuration();
359
-
360
- function sensitiveKey(key) {
361
- const normalized = key.toLowerCase().replace(/[_\-\s]/g, "");
362
- return /(token|secret|password|authorization|cookie|credential|apikey|privatekey|encryptionkey|accesskey|claimsecret)/.test(normalized);
363
- }
364
- function redactLogString(value) {
365
- return value.replace(/(Bearer\s+)[A-Za-z0-9._~+/=-]+/gi, "$1[REDACTED]").replace(/([?&](?:token|secret|password|api[_-]?key)=)[^&#\s]+/gi, "$1[REDACTED]").replace(/((?:token|secret|password|api[_-]?key)\s*[=:]\s*)[^\s,;]+/gi, "$1[REDACTED]");
366
- }
367
- function redactLogValue(value, depth = 0, seen = /* @__PURE__ */ new WeakSet()) {
368
- if (typeof value === "string") return redactLogString(value);
369
- if (value === null || typeof value !== "object") return value;
370
- if (depth >= 6) return "[TRUNCATED]";
371
- if (seen.has(value)) return "[CIRCULAR]";
372
- seen.add(value);
373
- if (value instanceof Error) {
374
- const error = value;
375
- return {
376
- name: error.name,
377
- message: redactLogString(error.message),
378
- ...typeof error.code === "string" || typeof error.code === "number" ? { code: error.code } : {},
379
- ...typeof (error.status ?? error.response?.status) === "number" ? { status: error.status ?? error.response?.status } : {}
380
- };
381
- }
382
- if (Array.isArray(value)) return value.map((item) => redactLogValue(item, depth + 1, seen));
383
- const result = {};
384
- for (const [key, child] of Object.entries(value)) {
385
- if (sensitiveKey(key)) {
386
- result[key] = "[REDACTED]";
387
- } else if (key === "environmentVariables" && child && typeof child === "object") {
388
- result[key] = { names: Object.keys(child).sort() };
389
- } else {
390
- result[key] = redactLogValue(child, depth + 1, seen);
391
- }
392
- }
393
- return result;
394
- }
395
-
396
- function createTimestampForFilename(date = /* @__PURE__ */ new Date()) {
397
- return date.toLocaleString("sv-SE", {
398
- timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone,
399
- year: "numeric",
400
- month: "2-digit",
401
- day: "2-digit",
402
- hour: "2-digit",
403
- minute: "2-digit",
404
- second: "2-digit"
405
- }).replace(/[: ]/g, "-").replace(/,/g, "") + "-pid-" + process.pid;
406
- }
407
- function createTimestampForLogEntry(date = /* @__PURE__ */ new Date()) {
408
- return date.toLocaleTimeString("en-US", {
409
- timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone,
410
- hour12: false,
411
- hour: "2-digit",
412
- minute: "2-digit",
413
- second: "2-digit",
414
- fractionalSecondDigits: 3
415
- });
416
- }
417
- function getSessionLogPath() {
418
- const timestamp = createTimestampForFilename();
419
- const filename = configuration.isDaemonProcess ? `${timestamp}-daemon.log` : `${timestamp}.log`;
420
- return join(configuration.logsDir, filename);
421
- }
422
- function safeRemoteLogUrl(raw) {
423
- if (!raw) return void 0;
424
- try {
425
- const url = new URL(raw);
426
- const loopback = ["localhost", "127.0.0.1", "[::1]"].includes(url.hostname);
427
- if (url.protocol !== "https:" && !(url.protocol === "http:" && loopback)) return void 0;
428
- if (url.username || url.password || url.search || url.hash || url.pathname !== "/" && url.pathname !== "") return void 0;
429
- return url.origin;
430
- } catch {
431
- return void 0;
432
- }
433
- }
434
- class Logger {
435
- constructor(logFilePath = getSessionLogPath()) {
436
- this.logFilePath = logFilePath;
437
- const remoteUrl = safeRemoteLogUrl(process.env.HAPPY_SERVER_URL);
438
- const remoteToken = process.env.DANGEROUSLY_LOG_TO_SERVER_FOR_AI_AUTO_DEBUGGING_TOKEN;
439
- if (process.env.DANGEROUSLY_LOG_TO_SERVER_FOR_AI_AUTO_DEBUGGING && remoteUrl && remoteToken && Buffer.byteLength(remoteToken) >= 32) {
440
- this.dangerouslyUnencryptedServerLoggingUrl = remoteUrl;
441
- this.dangerouslyUnencryptedServerLoggingToken = remoteToken;
442
- console.log(chalk.yellow("[REMOTE LOGGING] Sending logs to server for AI debugging"));
443
- }
444
- }
445
- dangerouslyUnencryptedServerLoggingUrl;
446
- dangerouslyUnencryptedServerLoggingToken;
447
- // Use local timezone for simplicity of locating the logs,
448
- // in practice you will not need absolute timestamps
449
- localTimezoneTimestamp() {
450
- return createTimestampForLogEntry();
451
- }
452
- debug(message, ...args) {
453
- this.logToFile(`[${this.localTimezoneTimestamp()}]`, message, ...args);
454
- }
455
- debugLargeJson(message, object, maxStringLength = 100, maxArrayLength = 10) {
456
- if (!process.env.DEBUG) {
457
- this.debug(`In production, skipping message inspection`);
458
- return;
459
- }
460
- const truncateStrings = (obj) => {
461
- if (typeof obj === "string") {
462
- return obj.length > maxStringLength ? obj.substring(0, maxStringLength) + "... [truncated for logs]" : obj;
463
- }
464
- if (Array.isArray(obj)) {
465
- const truncatedArray = obj.map((item) => truncateStrings(item)).slice(0, maxArrayLength);
466
- if (obj.length > maxArrayLength) {
467
- truncatedArray.push(`... [truncated array for logs up to ${maxArrayLength} items]`);
468
- }
469
- return truncatedArray;
470
- }
471
- if (obj && typeof obj === "object") {
472
- const result = {};
473
- for (const [key, value] of Object.entries(obj)) {
474
- if (key === "usage") {
475
- continue;
476
- }
477
- result[key] = truncateStrings(value);
478
- }
479
- return result;
480
- }
481
- return obj;
482
- };
483
- const truncatedObject = truncateStrings(redactLogValue(object));
484
- const json = JSON.stringify(truncatedObject, null, 2);
485
- this.logToFile(`[${this.localTimezoneTimestamp()}]`, redactLogString(message), "\n", json);
486
- }
487
- info(message, ...args) {
488
- this.logToConsole("info", "", message, ...args);
489
- this.debug(message, args);
490
- }
491
- infoDeveloper(message, ...args) {
492
- this.debug(message, ...args);
493
- if (process.env.DEBUG) {
494
- this.logToConsole("info", "[DEV]", message, ...args);
495
- }
496
- }
497
- warn(message, ...args) {
498
- this.logToConsole("warn", "", message, ...args);
499
- this.debug(`[WARN] ${message}`, ...args);
500
- }
501
- getLogPath() {
502
- return this.logFilePath;
503
- }
504
- logToConsole(level, prefix, message, ...args) {
505
- message = redactLogString(message);
506
- args = args.map((arg) => redactLogValue(arg));
507
- switch (level) {
508
- case "debug": {
509
- console.log(chalk.gray(prefix), message, ...args);
510
- break;
511
- }
512
- case "error": {
513
- console.error(chalk.red(prefix), message, ...args);
514
- break;
515
- }
516
- case "info": {
517
- console.log(chalk.blue(prefix), message, ...args);
518
- break;
519
- }
520
- case "warn": {
521
- console.log(chalk.yellow(prefix), message, ...args);
522
- break;
523
- }
524
- default: {
525
- this.debug("Unknown log level:", level);
526
- console.log(chalk.blue(prefix), message, ...args);
527
- break;
528
- }
529
- }
530
- }
531
- async sendToRemoteServer(level, message, ...args) {
532
- if (!this.dangerouslyUnencryptedServerLoggingUrl) return;
533
- try {
534
- await fetch(this.dangerouslyUnencryptedServerLoggingUrl + "/logs-combined-from-cli-and-mobile-for-simple-ai-debugging", {
535
- method: "POST",
536
- headers: {
537
- "Content-Type": "application/json",
538
- "Authorization": `Bearer ${this.dangerouslyUnencryptedServerLoggingToken}`
539
- },
540
- body: JSON.stringify({
541
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
542
- level,
543
- message: `${redactLogString(message)} ${args.map((raw) => {
544
- const safe = redactLogValue(raw);
545
- return typeof safe === "object" ? JSON.stringify(safe, null, 2) : String(safe);
546
- }).join(" ")}`,
547
- source: "cli",
548
- platform: process.platform
549
- })
550
- });
551
- } catch (error) {
552
- }
553
- }
554
- logToFile(prefix, message, ...args) {
555
- const safeMessage = redactLogString(message);
556
- const safeArgs = args.map((arg) => redactLogValue(arg));
557
- const logLine = `${prefix} ${safeMessage} ${safeArgs.map(
558
- (arg) => typeof arg === "string" ? arg : inspect(arg, { depth: 5, breakLength: 120 })
559
- ).join(" ")}
560
- `;
561
- if (this.dangerouslyUnencryptedServerLoggingUrl) {
562
- let level = "info";
563
- if (prefix.includes(this.localTimezoneTimestamp())) {
564
- level = "debug";
565
- }
566
- this.sendToRemoteServer(level, safeMessage, ...safeArgs).catch(() => {
567
- });
568
- }
569
- try {
570
- appendPrivateFileSync(this.logFilePath, logLine);
571
- } catch (appendError) {
572
- if (process.env.DEBUG) {
573
- console.error("[DEV MODE ONLY THROWING] Failed to append to log file:", appendError);
574
- throw appendError;
575
- }
576
- }
577
- }
578
- }
579
- let logger = new Logger();
580
- async function listDaemonLogFiles(limit = 50) {
581
- try {
582
- const logsDir = configuration.logsDir;
583
- if (!existsSync(logsDir)) {
584
- return [];
585
- }
586
- const logs = readdirSync(logsDir).filter((file) => file.endsWith("-daemon.log")).map((file) => {
587
- const fullPath = join(logsDir, file);
588
- const stats = statSync(fullPath);
589
- return { file, path: fullPath, modified: stats.mtime };
590
- }).sort((a, b) => b.modified.getTime() - a.modified.getTime());
591
- try {
592
- const { readDaemonState } = await Promise.resolve().then(function () { return persistence; });
593
- const state = await readDaemonState();
594
- if (!state) {
595
- return logs;
596
- }
597
- if (state.daemonLogPath && existsSync(state.daemonLogPath)) {
598
- const stats = statSync(state.daemonLogPath);
599
- const persisted = {
600
- file: basename(state.daemonLogPath),
601
- path: state.daemonLogPath,
602
- modified: stats.mtime
603
- };
604
- const idx = logs.findIndex((l) => l.path === persisted.path);
605
- if (idx >= 0) {
606
- const [found] = logs.splice(idx, 1);
607
- logs.unshift(found);
608
- } else {
609
- logs.unshift(persisted);
610
- }
611
- }
612
- } catch {
613
- }
614
- return logs.slice(0, Math.max(0, limit));
615
- } catch {
616
- return [];
617
- }
618
- }
619
- async function getLatestDaemonLog() {
620
- const [latest] = await listDaemonLogFiles(1);
621
- return latest || null;
622
- }
28
+ import chalk from 'chalk';
623
29
 
624
30
  const sessionRoleSchema = z.enum(["user", "agent"]);
625
31
  const sessionTextEventSchema = z.object({
@@ -1026,8 +432,36 @@ const ConnectionDiagnosticEventSchema = z$1.object({
1026
432
  z$1.object({
1027
433
  events: z$1.array(ConnectionDiagnosticEventSchema).min(1).max(32)
1028
434
  }).strict();
435
+ const TeamPermissionModeSchema = z$1.enum(["default", "bypassPermissions"]);
436
+ const id = z$1.string().min(1).max(128);
437
+ const text = z$1.string().min(1).max(32e3);
438
+ const TeamActionSchema = z$1.discriminatedUnion("type", [
439
+ z$1.object({ type: z$1.literal("archive") }),
440
+ z$1.object({ type: z$1.literal("set-permission-mode"), permissionMode: TeamPermissionModeSchema }),
441
+ z$1.object({ type: z$1.literal("schedule-create"), name: id, botId: id, body: text, runAt: z$1.number().int().positive().max(864e13), intervalMs: z$1.number().int().min(6e4).max(366 * 864e5).optional() }),
442
+ z$1.object({ type: z$1.literal("schedule-pause"), scheduleId: id, version: z$1.number().int().positive() }),
443
+ z$1.object({ type: z$1.literal("schedule-resume"), scheduleId: id, version: z$1.number().int().positive() }),
444
+ z$1.object({ type: z$1.literal("schedule-cancel"), scheduleId: id, version: z$1.number().int().positive() }),
445
+ z$1.object({ type: z$1.literal("reconcile-operation"), operationId: id, claimId: id, note: z$1.string().trim().min(1).max(4e3) }),
446
+ z$1.object({ type: z$1.literal("join"), name: id, sessionId: id, botId: id.optional() }),
447
+ z$1.object({ type: z$1.literal("delegate"), goal: text, acceptance: z$1.array(text).min(1).max(32), parentTaskId: id.optional(), assigneeBotId: id.optional(), botName: id.optional(), directory: z$1.string().min(1).max(4096).optional(), assistant: z$1.enum(["claude", "codex", "pi-acp"]).optional() }),
448
+ z$1.object({ type: z$1.literal("message"), taskId: id, body: text, recipientBotId: id.optional() }),
449
+ z$1.object({ type: z$1.literal("submit"), taskId: id, attemptId: id, goalVersion: z$1.number().int().positive(), result: text }),
450
+ z$1.object({ type: z$1.literal("accept"), taskId: id, attemptId: id, goalVersion: z$1.number().int().positive() }),
451
+ z$1.object({ type: z$1.literal("return"), taskId: id, attemptId: id, goalVersion: z$1.number().int().positive(), reason: text }),
452
+ z$1.object({ type: z$1.literal("cancel"), taskId: id, attemptId: id, goalVersion: z$1.number().int().positive(), reason: text }),
453
+ z$1.object({ type: z$1.literal("handoff"), taskId: id, attemptId: id, goalVersion: z$1.number().int().positive(), assigneeBotId: id }),
454
+ z$1.object({ type: z$1.literal("claim-operation"), operationId: id, machineId: id }),
455
+ z$1.object({ type: z$1.literal("complete-operation"), operationId: id, machineId: id, claimId: id, sessionId: id.optional() }),
456
+ z$1.object({ type: z$1.literal("fail-operation"), operationId: id, machineId: id, claimId: id, error: text, unknown: z$1.boolean().optional() }),
457
+ z$1.object({ type: z$1.literal("session-event"), sessionId: id, event: z$1.enum(["idle", "blocked", "exited"]) }),
458
+ z$1.object({ type: z$1.literal("message-delivered"), messageId: id, recipientBotId: id, generation: z$1.number().int().positive(), sessionId: id })
459
+ ]);
460
+ z$1.object({ requestId: id, action: TeamActionSchema });
461
+ z$1.object({ name: id, machineId: id, requestId: id.optional() });
1029
462
 
1030
463
  z$1.object({
464
+ teamsVersion: z$1.number().int().positive().optional(),
1031
465
  host: z$1.string(),
1032
466
  platform: z$1.string(),
1033
467
  happyCliVersion: z$1.string(),
@@ -1302,125 +736,6 @@ const AgentMessageSchema = z$1.object({
1302
736
  });
1303
737
  z$1.union([UserMessageSchema, AgentMessageSchema]);
1304
738
 
1305
- function encodeBase64(buffer, variant = "base64") {
1306
- if (variant === "base64url") {
1307
- return encodeBase64Url(buffer);
1308
- }
1309
- return Buffer.from(buffer).toString("base64");
1310
- }
1311
- function encodeBase64Url(buffer) {
1312
- return Buffer.from(buffer).toString("base64").replaceAll("+", "-").replaceAll("/", "_").replaceAll("=", "");
1313
- }
1314
- function decodeBase64(base64, variant = "base64") {
1315
- if (variant === "base64url") {
1316
- const base64Standard = base64.replaceAll("-", "+").replaceAll("_", "/") + "=".repeat((4 - base64.length % 4) % 4);
1317
- return new Uint8Array(Buffer.from(base64Standard, "base64"));
1318
- }
1319
- return new Uint8Array(Buffer.from(base64, "base64"));
1320
- }
1321
- function getRandomBytes(size) {
1322
- return new Uint8Array(randomBytes(size));
1323
- }
1324
- function libsodiumPublicKeyFromSecretKey(seed) {
1325
- const hashedSeed = new Uint8Array(createHash("sha512").update(seed).digest());
1326
- const secretKey = hashedSeed.slice(0, 32);
1327
- return new Uint8Array(tweetnacl.box.keyPair.fromSecretKey(secretKey).publicKey);
1328
- }
1329
- function libsodiumEncryptForPublicKey(data, recipientPublicKey) {
1330
- const ephemeralKeyPair = tweetnacl.box.keyPair();
1331
- const nonce = getRandomBytes(tweetnacl.box.nonceLength);
1332
- const encrypted = tweetnacl.box(data, nonce, recipientPublicKey, ephemeralKeyPair.secretKey);
1333
- const result = new Uint8Array(ephemeralKeyPair.publicKey.length + nonce.length + encrypted.length);
1334
- result.set(ephemeralKeyPair.publicKey, 0);
1335
- result.set(nonce, ephemeralKeyPair.publicKey.length);
1336
- result.set(encrypted, ephemeralKeyPair.publicKey.length + nonce.length);
1337
- return result;
1338
- }
1339
- function encryptLegacy(data, secret) {
1340
- const nonce = getRandomBytes(tweetnacl.secretbox.nonceLength);
1341
- const encrypted = tweetnacl.secretbox(new TextEncoder().encode(JSON.stringify(data)), nonce, secret);
1342
- const result = new Uint8Array(nonce.length + encrypted.length);
1343
- result.set(nonce);
1344
- result.set(encrypted, nonce.length);
1345
- return result;
1346
- }
1347
- function decryptLegacy(data, secret) {
1348
- const nonce = data.slice(0, tweetnacl.secretbox.nonceLength);
1349
- const encrypted = data.slice(tweetnacl.secretbox.nonceLength);
1350
- const decrypted = tweetnacl.secretbox.open(encrypted, nonce, secret);
1351
- if (!decrypted) {
1352
- return null;
1353
- }
1354
- return JSON.parse(new TextDecoder().decode(decrypted));
1355
- }
1356
- function decryptBlob(bundle, key) {
1357
- if (bundle.length < tweetnacl.secretbox.nonceLength + 16) {
1358
- return null;
1359
- }
1360
- const nonce = bundle.slice(0, tweetnacl.secretbox.nonceLength);
1361
- const ciphertext = bundle.slice(tweetnacl.secretbox.nonceLength);
1362
- const decrypted = tweetnacl.secretbox.open(ciphertext, nonce, key);
1363
- if (!decrypted) {
1364
- return null;
1365
- }
1366
- return new Uint8Array(decrypted);
1367
- }
1368
- function encryptWithDataKey(data, dataKey) {
1369
- const nonce = getRandomBytes(12);
1370
- const cipher = createCipheriv("aes-256-gcm", dataKey, nonce);
1371
- const plaintext = new TextEncoder().encode(JSON.stringify(data));
1372
- const encrypted = Buffer.concat([
1373
- cipher.update(plaintext),
1374
- cipher.final()
1375
- ]);
1376
- const authTag = cipher.getAuthTag();
1377
- const bundle = new Uint8Array(12 + encrypted.length + 16 + 1);
1378
- bundle.set([0], 0);
1379
- bundle.set(nonce, 1);
1380
- bundle.set(new Uint8Array(encrypted), 13);
1381
- bundle.set(new Uint8Array(authTag), 13 + encrypted.length);
1382
- return bundle;
1383
- }
1384
- function decryptWithDataKey(bundle, dataKey) {
1385
- if (bundle.length < 1) {
1386
- return null;
1387
- }
1388
- if (bundle[0] !== 0) {
1389
- return null;
1390
- }
1391
- if (bundle.length < 12 + 16 + 1) {
1392
- return null;
1393
- }
1394
- const nonce = bundle.slice(1, 13);
1395
- const authTag = bundle.slice(bundle.length - 16);
1396
- const ciphertext = bundle.slice(13, bundle.length - 16);
1397
- try {
1398
- const decipher = createDecipheriv("aes-256-gcm", dataKey, nonce);
1399
- decipher.setAuthTag(authTag);
1400
- const decrypted = Buffer.concat([
1401
- decipher.update(ciphertext),
1402
- decipher.final()
1403
- ]);
1404
- return JSON.parse(new TextDecoder().decode(decrypted));
1405
- } catch (error) {
1406
- return null;
1407
- }
1408
- }
1409
- function encrypt(key, variant, data) {
1410
- if (variant === "legacy") {
1411
- return encryptLegacy(data, key);
1412
- } else {
1413
- return encryptWithDataKey(data, key);
1414
- }
1415
- }
1416
- function decrypt(key, variant, data) {
1417
- if (variant === "legacy") {
1418
- return decryptLegacy(data, key);
1419
- } else {
1420
- return decryptWithDataKey(data, key);
1421
- }
1422
- }
1423
-
1424
739
  async function delay(ms) {
1425
740
  return new Promise((resolve) => setTimeout(resolve, ms));
1426
741
  }
@@ -1693,7 +1008,7 @@ function projectPath() {
1693
1008
  }
1694
1009
 
1695
1010
  function run$1(args, options) {
1696
- const RUNNER_PATH = resolve(join$1(projectPath(), "scripts", "ripgrep_launcher.cjs"));
1011
+ const RUNNER_PATH = resolve(join(projectPath(), "scripts", "ripgrep_launcher.cjs"));
1697
1012
  return new Promise((resolve2, reject) => {
1698
1013
  const child = spawn("node", [RUNNER_PATH, JSON.stringify(args)], {
1699
1014
  stdio: ["pipe", "pipe", "pipe"],
@@ -1724,7 +1039,7 @@ function run$1(args, options) {
1724
1039
  function getBinaryPath() {
1725
1040
  const platformName = platform();
1726
1041
  const binaryName = platformName === "win32" ? "difft.exe" : "difft";
1727
- return resolve(join$1(projectPath(), "tools", "unpacked", binaryName));
1042
+ return resolve(join(projectPath(), "tools", "unpacked", binaryName));
1728
1043
  }
1729
1044
  function run(args, options) {
1730
1045
  const binaryPath = getBinaryPath();
@@ -1836,7 +1151,7 @@ function createUploadFileHandlers(happyHomeDir, now = Date.now, pathQuoteStyle =
1836
1151
  const entries = await readdir(dir, { withFileTypes: true }).catch(() => []);
1837
1152
  await Promise.all(entries.map(async (entry) => {
1838
1153
  if (!entry.isFile() || !/^\..+\.[A-Za-z0-9_-]{8,80}\.part$/.test(entry.name)) return;
1839
- const path = join$1(dir, entry.name);
1154
+ const path = join(dir, entry.name);
1840
1155
  const fileStat = await stat(path).catch(() => null);
1841
1156
  if (fileStat && fileStat.mtimeMs < cutoff) await unlink(path).catch(() => {
1842
1157
  });
@@ -1846,13 +1161,13 @@ function createUploadFileHandlers(happyHomeDir, now = Date.now, pathQuoteStyle =
1846
1161
  let tempPath = null;
1847
1162
  try {
1848
1163
  const buffer = decodeUploadBase64(data.content, MAX_USER_UPLOAD_BYTES);
1849
- const dir = join$1(happyHomeDir, "uploads", safeSegment(data.subdir, "misc", 80));
1850
- await mkdir$1(dir, { recursive: true });
1164
+ const dir = join(happyHomeDir, "uploads", safeSegment(data.subdir, "misc", 80));
1165
+ await mkdir(dir, { recursive: true });
1851
1166
  const name = safeFileName(data.name);
1852
1167
  const token = randomUUID().replace(/-/g, "").slice(0, 12);
1853
- const target = join$1(dir, uniqueFileName(name, token));
1854
- tempPath = join$1(dir, `.${name}.legacy_${randomUUID().replace(/-/g, "")}.part`);
1855
- await writeFile$1(tempPath, buffer, { flag: "wx" });
1168
+ const target = join(dir, uniqueFileName(name, token));
1169
+ tempPath = join(dir, `.${name}.legacy_${randomUUID().replace(/-/g, "")}.part`);
1170
+ await writeFile(tempPath, buffer, { flag: "wx" });
1856
1171
  await rename(tempPath, target);
1857
1172
  tempPath = null;
1858
1173
  return { success: true, path: target, pathQuoteStyle, size: buffer.length };
@@ -1872,13 +1187,13 @@ function createUploadFileHandlers(happyHomeDir, now = Date.now, pathQuoteStyle =
1872
1187
  }
1873
1188
  if (active.has(uploadId)) throw new Error("Upload id is already active");
1874
1189
  if (active.size >= MAX_ACTIVE_UPLOADS) throw new Error("Too many active uploads");
1875
- const dir = join$1(happyHomeDir, "uploads", safeSegment(data.subdir, "misc", 80));
1876
- await mkdir$1(dir, { recursive: true });
1190
+ const dir = join(happyHomeDir, "uploads", safeSegment(data.subdir, "misc", 80));
1191
+ await mkdir(dir, { recursive: true });
1877
1192
  await sweepExpiredFiles(dir);
1878
1193
  const name = safeFileName(data.name);
1879
- const targetPath = join$1(dir, uniqueFileName(name, randomUUID().replace(/-/g, "").slice(0, 12)));
1880
- const tempPath = join$1(dir, `.${name}.${uploadId}.part`);
1881
- await writeFile$1(tempPath, Buffer.alloc(0), { flag: "wx" });
1194
+ const targetPath = join(dir, uniqueFileName(name, randomUUID().replace(/-/g, "").slice(0, 12)));
1195
+ const tempPath = join(dir, `.${name}.${uploadId}.part`);
1196
+ await writeFile(tempPath, Buffer.alloc(0), { flag: "wx" });
1882
1197
  active.set(uploadId, { tempPath, targetPath, totalSize: data.totalSize, received: 0, touchedAt: now() });
1883
1198
  return { success: true, size: 0 };
1884
1199
  }
@@ -2014,7 +1329,7 @@ function registerCommonHandlers(rpcHandlerManager, workingDirectory) {
2014
1329
  if (data.expectedHash !== null && data.expectedHash !== void 0) {
2015
1330
  try {
2016
1331
  const existingBuffer = await readFile(validation.resolvedPath);
2017
- const existingHash = createHash$1("sha256").update(existingBuffer).digest("hex");
1332
+ const existingHash = createHash("sha256").update(existingBuffer).digest("hex");
2018
1333
  if (existingHash !== data.expectedHash) {
2019
1334
  return {
2020
1335
  success: false,
@@ -2046,8 +1361,8 @@ function registerCommonHandlers(rpcHandlerManager, workingDirectory) {
2046
1361
  }
2047
1362
  }
2048
1363
  const buffer = Buffer.from(data.content, "base64");
2049
- await writeFile$1(validation.resolvedPath, buffer);
2050
- const hash = createHash$1("sha256").update(buffer).digest("hex");
1364
+ await writeFile(validation.resolvedPath, buffer);
1365
+ const hash = createHash("sha256").update(buffer).digest("hex");
2051
1366
  return { success: true, hash };
2052
1367
  } catch (error) {
2053
1368
  logger.debug("Failed to write file:", error);
@@ -2068,7 +1383,7 @@ function registerCommonHandlers(rpcHandlerManager, workingDirectory) {
2068
1383
  const entries = await readdir(directoryPath, { withFileTypes: true });
2069
1384
  const directoryEntries = await Promise.all(
2070
1385
  entries.map(async (entry) => {
2071
- const fullPath = join$1(directoryPath, entry.name);
1386
+ const fullPath = join(directoryPath, entry.name);
2072
1387
  let type = "other";
2073
1388
  let size;
2074
1389
  let modified;
@@ -2125,10 +1440,10 @@ function registerCommonHandlers(rpcHandlerManager, workingDirectory) {
2125
1440
  await Promise.all(
2126
1441
  entries.map(async (entry) => {
2127
1442
  if (entry.isSymbolicLink()) {
2128
- logger.debug(`Skipping symlink: ${join$1(path, entry.name)}`);
1443
+ logger.debug(`Skipping symlink: ${join(path, entry.name)}`);
2129
1444
  return;
2130
1445
  }
2131
- const childPath = join$1(path, entry.name);
1446
+ const childPath = join(path, entry.name);
2132
1447
  const childNode = await buildTree(childPath, entry.name, currentDepth + 1);
2133
1448
  if (childNode) {
2134
1449
  children.push(childNode);
@@ -4423,10 +3738,10 @@ function normalizeFsPath(input, homeDir) {
4423
3738
  if (p === "~") {
4424
3739
  p = homeDir;
4425
3740
  } else if (p.startsWith("~/")) {
4426
- p = join(homeDir, p.slice(2));
3741
+ p = join$1(homeDir, p.slice(2));
4427
3742
  }
4428
3743
  if (!isAbsolute(p)) {
4429
- p = join(homeDir, p);
3744
+ p = join$1(homeDir, p);
4430
3745
  }
4431
3746
  return resolve$1(p);
4432
3747
  }
@@ -4482,7 +3797,7 @@ async function fsList(inputPath, homeDir = homedir()) {
4482
3797
  dirents.map(async (d) => {
4483
3798
  const entry = { name: d.name, type: entryTypeOf(d) };
4484
3799
  try {
4485
- const s = await lstat(join(path, d.name));
3800
+ const s = await lstat(join$1(path, d.name));
4486
3801
  entry.size = s.size;
4487
3802
  entry.mtimeMs = Math.round(s.mtimeMs);
4488
3803
  } catch {
@@ -4539,391 +3854,6 @@ function registerFsHandlers(rpcHandlerManager) {
4539
3854
  });
4540
3855
  }
4541
3856
 
4542
- const DEFAULT_SERVER_URL = "https://veryhappy.dev";
4543
- const LEGACY_CLOUD_SERVER_URL = "https://happy.mereith.com";
4544
- const CLOUD_SERVER_URLS = [DEFAULT_SERVER_URL, LEGACY_CLOUD_SERVER_URL];
4545
- function sameRelay(left, right) {
4546
- try {
4547
- return new URL(left).origin === new URL(right).origin;
4548
- } catch {
4549
- return left.replace(/\/+$/, "") === right.replace(/\/+$/, "");
4550
- }
4551
- }
4552
- function credentialRelayProblem(credentialServerUrl, configuredServerUrl) {
4553
- if (credentialServerUrl) {
4554
- return sameRelay(credentialServerUrl, configuredServerUrl) ? void 0 : `Credentials belong to ${credentialServerUrl}, but the active relay is ${configuredServerUrl}.`;
4555
- }
4556
- if (CLOUD_SERVER_URLS.some((cloudUrl) => sameRelay(configuredServerUrl, cloudUrl))) {
4557
- return void 0;
4558
- }
4559
- return `These credentials predate relay tracking, so they cannot be safely reused with ${configuredServerUrl}.`;
4560
- }
4561
-
4562
- const SandboxConfigSchema = z.object({
4563
- enabled: z.boolean().default(false),
4564
- workspaceRoot: z.string().optional(),
4565
- sessionIsolation: z.enum(["strict", "workspace", "custom"]).default("workspace"),
4566
- customWritePaths: z.array(z.string()).default([]),
4567
- denyReadPaths: z.array(z.string()).default(["~/.ssh", "~/.aws", "~/.gnupg"]),
4568
- extraWritePaths: z.array(z.string()).default(["/tmp"]),
4569
- denyWritePaths: z.array(z.string()).default([".env"]),
4570
- networkMode: z.enum(["blocked", "allowed", "custom"]).default("allowed"),
4571
- allowedDomains: z.array(z.string()).default([]),
4572
- deniedDomains: z.array(z.string()).default([]),
4573
- allowLocalBinding: z.boolean().default(true)
4574
- });
4575
- const SUPPORTED_SCHEMA_VERSION = 2;
4576
- const defaultSettings = {
4577
- schemaVersion: SUPPORTED_SCHEMA_VERSION,
4578
- onboardingCompleted: false,
4579
- sandboxConfig: void 0
4580
- };
4581
- function migrateSettings(raw, fromVersion) {
4582
- let migrated = { ...raw };
4583
- return migrated;
4584
- }
4585
- async function readSettingsOutcome() {
4586
- if (!existsSync(configuration.settingsFile)) {
4587
- return { kind: "absent" };
4588
- }
4589
- try {
4590
- return { kind: "ok", settings: await parseSettingsFile() };
4591
- } catch (error) {
4592
- return { kind: "unreadable", error: error?.message ?? String(error) };
4593
- }
4594
- }
4595
- async function readSettings() {
4596
- const outcome = await readSettingsOutcome();
4597
- if (outcome.kind === "ok") return outcome.settings;
4598
- if (outcome.kind === "unreadable") {
4599
- logger.warn(`Failed to read settings: ${outcome.error}`);
4600
- }
4601
- return { ...defaultSettings };
4602
- }
4603
- async function parseSettingsFile() {
4604
- const content = await readFile$1(configuration.settingsFile, "utf8");
4605
- const raw = JSON.parse(content);
4606
- const schemaVersion = raw.schemaVersion ?? 1;
4607
- if (schemaVersion > SUPPORTED_SCHEMA_VERSION) {
4608
- logger.warn(
4609
- `\u26A0\uFE0F Settings schema v${schemaVersion} > supported v${SUPPORTED_SCHEMA_VERSION}. Update happy-cli for full functionality.`
4610
- );
4611
- }
4612
- const migrated = migrateSettings(raw);
4613
- if (migrated.sandboxConfig !== void 0) {
4614
- try {
4615
- migrated.sandboxConfig = SandboxConfigSchema.parse(migrated.sandboxConfig);
4616
- } catch (error) {
4617
- logger.warn(`\u26A0\uFE0F Invalid sandbox config - skipping. Error: ${error.message}`);
4618
- migrated.sandboxConfig = void 0;
4619
- }
4620
- }
4621
- return { ...defaultSettings, ...migrated };
4622
- }
4623
- async function writeSettings(settings) {
4624
- await ensurePrivateDirectory(configuration.happyHomeDir);
4625
- const settingsWithVersion = {
4626
- ...settings,
4627
- schemaVersion: settings.schemaVersion ?? SUPPORTED_SCHEMA_VERSION
4628
- };
4629
- await writePrivateFile(configuration.settingsFile, JSON.stringify(settingsWithVersion, null, 2));
4630
- }
4631
- async function updateSettings(updater) {
4632
- const LOCK_RETRY_INTERVAL_MS = 100;
4633
- const MAX_LOCK_ATTEMPTS = 50;
4634
- const STALE_LOCK_TIMEOUT_MS = 1e4;
4635
- const lockFile = configuration.settingsFile + ".lock";
4636
- const tmpFile = configuration.settingsFile + ".tmp";
4637
- let fileHandle;
4638
- let attempts = 0;
4639
- while (attempts < MAX_LOCK_ATTEMPTS) {
4640
- try {
4641
- fileHandle = await open(lockFile, constants.O_CREAT | constants.O_EXCL | constants.O_WRONLY, PRIVATE_FILE_MODE);
4642
- break;
4643
- } catch (err) {
4644
- if (err.code === "EEXIST") {
4645
- attempts++;
4646
- await new Promise((resolve) => setTimeout(resolve, LOCK_RETRY_INTERVAL_MS));
4647
- try {
4648
- const stats = await stat$1(lockFile);
4649
- if (Date.now() - stats.mtimeMs > STALE_LOCK_TIMEOUT_MS) {
4650
- await unlink$1(lockFile).catch(() => {
4651
- });
4652
- }
4653
- } catch {
4654
- }
4655
- } else {
4656
- throw err;
4657
- }
4658
- }
4659
- }
4660
- if (!fileHandle) {
4661
- throw new Error(`Failed to acquire settings lock after ${MAX_LOCK_ATTEMPTS * LOCK_RETRY_INTERVAL_MS / 1e3} seconds`);
4662
- }
4663
- try {
4664
- const outcome = await readSettingsOutcome();
4665
- if (outcome.kind === "unreadable") {
4666
- throw new Error(
4667
- `Settings file ${configuration.settingsFile} exists but could not be read (${outcome.error}). Refusing to overwrite it \u2014 fix or remove the file (a removed file is recreated with defaults).`
4668
- );
4669
- }
4670
- const current = outcome.kind === "ok" ? outcome.settings : { ...defaultSettings };
4671
- const updated = await updater(current);
4672
- await ensurePrivateDirectory(configuration.happyHomeDir);
4673
- await writePrivateFile(tmpFile, JSON.stringify(updated, null, 2));
4674
- await rename$1(tmpFile, configuration.settingsFile);
4675
- await hardenPrivateFile(configuration.settingsFile);
4676
- return updated;
4677
- } finally {
4678
- await fileHandle.close();
4679
- await unlink$1(lockFile).catch(() => {
4680
- });
4681
- }
4682
- }
4683
- const credentialsSchema = z.object({
4684
- token: z.string(),
4685
- authServerUrl: z.string().optional(),
4686
- secret: z.string().base64().nullish(),
4687
- // Legacy
4688
- encryption: z.object({
4689
- publicKey: z.string().base64(),
4690
- machineKey: z.string().base64()
4691
- }).nullish()
4692
- });
4693
- async function readCredentials() {
4694
- if (!existsSync(configuration.privateKeyFile)) {
4695
- return null;
4696
- }
4697
- try {
4698
- const keyBase64 = await readFile$1(configuration.privateKeyFile, "utf8");
4699
- const credentials = credentialsSchema.parse(JSON.parse(keyBase64));
4700
- if (credentials.secret) {
4701
- return {
4702
- token: credentials.token,
4703
- authServerUrl: credentials.authServerUrl,
4704
- encryption: {
4705
- type: "legacy",
4706
- secret: new Uint8Array(Buffer.from(credentials.secret, "base64"))
4707
- }
4708
- };
4709
- } else if (credentials.encryption) {
4710
- return {
4711
- token: credentials.token,
4712
- authServerUrl: credentials.authServerUrl,
4713
- encryption: {
4714
- type: "dataKey",
4715
- publicKey: new Uint8Array(Buffer.from(credentials.encryption.publicKey, "base64")),
4716
- machineKey: new Uint8Array(Buffer.from(credentials.encryption.machineKey, "base64"))
4717
- }
4718
- };
4719
- }
4720
- } catch {
4721
- return null;
4722
- }
4723
- return null;
4724
- }
4725
- async function readCredentialsForConfiguredRelay() {
4726
- const credentials = await readCredentials();
4727
- if (!credentials) return null;
4728
- const relayProblem = credentialRelayProblem(credentials.authServerUrl, configuration.serverUrl);
4729
- if (relayProblem) {
4730
- throw new Error(
4731
- `${relayProblem} Use a separate HAPPY_HOME_DIR for this relay, or run \`very-happy auth login --force\` before making relay requests.`
4732
- );
4733
- }
4734
- return credentials;
4735
- }
4736
- async function writeCredentialsLegacy(credentials) {
4737
- await ensurePrivateDirectory(configuration.happyHomeDir);
4738
- await writePrivateFile(configuration.privateKeyFile, JSON.stringify({
4739
- secret: encodeBase64(credentials.secret),
4740
- token: credentials.token,
4741
- authServerUrl: configuration.serverUrl
4742
- }, null, 2));
4743
- }
4744
- async function writeCredentialsDataKey(credentials) {
4745
- await ensurePrivateDirectory(configuration.happyHomeDir);
4746
- await writePrivateFile(configuration.privateKeyFile, JSON.stringify({
4747
- encryption: { publicKey: encodeBase64(credentials.publicKey), machineKey: encodeBase64(credentials.machineKey) },
4748
- token: credentials.token,
4749
- authServerUrl: configuration.serverUrl
4750
- }, null, 2));
4751
- }
4752
- async function clearCredentials() {
4753
- if (existsSync(configuration.privateKeyFile)) {
4754
- await unlink$1(configuration.privateKeyFile);
4755
- }
4756
- }
4757
- async function clearMachineId() {
4758
- await updateSettings((settings) => ({
4759
- ...settings,
4760
- machineId: void 0
4761
- }));
4762
- }
4763
- async function readDaemonState() {
4764
- try {
4765
- if (!existsSync(configuration.daemonStateFile)) {
4766
- return null;
4767
- }
4768
- const content = await readFile$1(configuration.daemonStateFile, "utf-8");
4769
- return JSON.parse(content);
4770
- } catch (error) {
4771
- console.error(`[PERSISTENCE] Daemon state file corrupted: ${configuration.daemonStateFile}`, error);
4772
- return null;
4773
- }
4774
- }
4775
- function writeDaemonState(state) {
4776
- writePrivateFileSync(configuration.daemonStateFile, JSON.stringify(state, null, 2));
4777
- }
4778
- async function clearDaemonState() {
4779
- if (existsSync(configuration.daemonStateFile)) {
4780
- await unlink$1(configuration.daemonStateFile);
4781
- }
4782
- if (existsSync(configuration.daemonLockFile)) {
4783
- try {
4784
- await unlink$1(configuration.daemonLockFile);
4785
- } catch {
4786
- }
4787
- }
4788
- }
4789
- async function acquireDaemonLock(maxAttempts = 5, delayIncrementMs = 200) {
4790
- for (let attempt = 1; attempt <= maxAttempts; attempt++) {
4791
- try {
4792
- const fileHandle = await open(
4793
- configuration.daemonLockFile,
4794
- constants.O_CREAT | constants.O_EXCL | constants.O_WRONLY,
4795
- PRIVATE_FILE_MODE
4796
- );
4797
- await fileHandle.writeFile(String(process.pid));
4798
- return fileHandle;
4799
- } catch (error) {
4800
- if (error.code === "EEXIST") {
4801
- try {
4802
- const lockPid = readFileSync(configuration.daemonLockFile, "utf-8").trim();
4803
- if (lockPid && !isNaN(Number(lockPid))) {
4804
- try {
4805
- process.kill(Number(lockPid), 0);
4806
- } catch {
4807
- unlinkSync(configuration.daemonLockFile);
4808
- continue;
4809
- }
4810
- }
4811
- } catch {
4812
- }
4813
- }
4814
- if (attempt === maxAttempts) {
4815
- return null;
4816
- }
4817
- const delayMs = attempt * delayIncrementMs;
4818
- await new Promise((resolve) => setTimeout(resolve, delayMs));
4819
- }
4820
- }
4821
- return null;
4822
- }
4823
- async function releaseDaemonLock(lockHandle) {
4824
- try {
4825
- await lockHandle.close();
4826
- } catch {
4827
- }
4828
- try {
4829
- if (existsSync(configuration.daemonLockFile)) {
4830
- unlinkSync(configuration.daemonLockFile);
4831
- }
4832
- } catch {
4833
- }
4834
- }
4835
- const SESSION_MAX_AGE_MS = 14 * 24 * 60 * 60 * 1e3;
4836
- function readPersistedSessions() {
4837
- try {
4838
- if (!existsSync(configuration.sessionsFile)) return {};
4839
- const data = JSON.parse(readFileSync(configuration.sessionsFile, "utf-8"));
4840
- if (!data?.sessions || typeof data.sessions !== "object") return {};
4841
- const now = Date.now();
4842
- const sessions = {};
4843
- for (const [id, session] of Object.entries(data.sessions)) {
4844
- if (now - session.savedAt < SESSION_MAX_AGE_MS) {
4845
- sessions[id] = session;
4846
- }
4847
- }
4848
- return sessions;
4849
- } catch {
4850
- return {};
4851
- }
4852
- }
4853
- function readTrackedClaudeSessionIds() {
4854
- try {
4855
- if (!existsSync(configuration.sessionsFile)) return [];
4856
- const data = JSON.parse(readFileSync(configuration.sessionsFile, "utf-8"));
4857
- if (!data?.sessions || typeof data.sessions !== "object") return [];
4858
- const ids = /* @__PURE__ */ new Set();
4859
- for (const session of Object.values(data.sessions)) {
4860
- const metadata = (session ?? {}).metadata;
4861
- for (const id of [metadata?.claudeSessionId, metadata?.importedFromClaudeSessionId]) {
4862
- if (typeof id === "string" && id.length > 0) ids.add(id.toLowerCase());
4863
- }
4864
- }
4865
- return [...ids];
4866
- } catch {
4867
- return [];
4868
- }
4869
- }
4870
- function persistSession(sessionId, session) {
4871
- try {
4872
- const existing = readPersistedSessions();
4873
- existing[sessionId] = session;
4874
- const tmpFile = configuration.sessionsFile + ".tmp";
4875
- writePrivateFileSync(tmpFile, JSON.stringify({ sessions: existing }, null, 2));
4876
- renameSync(tmpFile, configuration.sessionsFile);
4877
- hardenPrivateFileSync(configuration.sessionsFile);
4878
- } catch (error) {
4879
- logger.debug(`[PERSISTENCE] Failed to persist session ${sessionId}:`, error);
4880
- }
4881
- }
4882
- function deletePersistedSessions(sessionIds) {
4883
- try {
4884
- const existing = readPersistedSessions();
4885
- let changed = false;
4886
- for (const id of sessionIds) {
4887
- if (id in existing) {
4888
- delete existing[id];
4889
- changed = true;
4890
- }
4891
- }
4892
- if (!changed) return;
4893
- const tmpFile = configuration.sessionsFile + ".tmp";
4894
- writePrivateFileSync(tmpFile, JSON.stringify({ sessions: existing }, null, 2));
4895
- renameSync(tmpFile, configuration.sessionsFile);
4896
- hardenPrivateFileSync(configuration.sessionsFile);
4897
- } catch (error) {
4898
- logger.debug(`[PERSISTENCE] Failed to delete persisted sessions:`, error);
4899
- }
4900
- }
4901
-
4902
- var persistence = /*#__PURE__*/Object.freeze({
4903
- __proto__: null,
4904
- SUPPORTED_SCHEMA_VERSION: SUPPORTED_SCHEMA_VERSION,
4905
- SandboxConfigSchema: SandboxConfigSchema,
4906
- acquireDaemonLock: acquireDaemonLock,
4907
- clearCredentials: clearCredentials,
4908
- clearDaemonState: clearDaemonState,
4909
- clearMachineId: clearMachineId,
4910
- deletePersistedSessions: deletePersistedSessions,
4911
- persistSession: persistSession,
4912
- readCredentials: readCredentials,
4913
- readCredentialsForConfiguredRelay: readCredentialsForConfiguredRelay,
4914
- readDaemonState: readDaemonState,
4915
- readPersistedSessions: readPersistedSessions,
4916
- readSettings: readSettings,
4917
- readSettingsOutcome: readSettingsOutcome,
4918
- readTrackedClaudeSessionIds: readTrackedClaudeSessionIds,
4919
- releaseDaemonLock: releaseDaemonLock,
4920
- updateSettings: updateSettings,
4921
- writeCredentialsDataKey: writeCredentialsDataKey,
4922
- writeCredentialsLegacy: writeCredentialsLegacy,
4923
- writeDaemonState: writeDaemonState,
4924
- writeSettings: writeSettings
4925
- });
4926
-
4927
3857
  const TODO_LIST_MAX_ITEMS = 500;
4928
3858
  const TODO_TITLE_MAX_CHARS = 500;
4929
3859
  const FIELD_MAX_CHARS = 200;
@@ -5282,14 +4212,14 @@ function scrubTmuxClientEnv(env) {
5282
4212
  }
5283
4213
 
5284
4214
  function getClaudeConfigDir() {
5285
- return process.env.CLAUDE_CONFIG_DIR || join(homedir(), ".claude");
4215
+ return process.env.CLAUDE_CONFIG_DIR || join$1(homedir(), ".claude");
5286
4216
  }
5287
4217
  function getClaudeProjectsRoot() {
5288
- return join(getClaudeConfigDir(), "projects");
4218
+ return join$1(getClaudeConfigDir(), "projects");
5289
4219
  }
5290
4220
  function getProjectPath(workingDirectory) {
5291
4221
  const projectId = resolve$1(workingDirectory).replace(/[^a-zA-Z0-9-]/g, "-");
5292
- return join(getClaudeProjectsRoot(), projectId);
4222
+ return join$1(getClaudeProjectsRoot(), projectId);
5293
4223
  }
5294
4224
 
5295
4225
  var xtermHeadless = {};
@@ -10521,7 +9451,7 @@ function pruneTombstones(map, now, ttlMs = TOMBSTONE_TTL_MS) {
10521
9451
  return out;
10522
9452
  }
10523
9453
  function tombstoneFile() {
10524
- return join(configuration.happyHomeDir, "terminal-tombstones.json");
9454
+ return join$1(configuration.happyHomeDir, "terminal-tombstones.json");
10525
9455
  }
10526
9456
  function loadTombstones() {
10527
9457
  try {
@@ -10540,7 +9470,7 @@ function saveTombstones(map) {
10540
9470
  }
10541
9471
  }
10542
9472
  function closedTerminalsFile() {
10543
- return join(configuration.happyHomeDir, "closed-terminals.json");
9473
+ return join$1(configuration.happyHomeDir, "closed-terminals.json");
10544
9474
  }
10545
9475
  function loadClosedTerminals() {
10546
9476
  try {
@@ -10558,7 +9488,7 @@ function saveClosedTerminals(list) {
10558
9488
  }
10559
9489
  }
10560
9490
  function liveTerminalsFile() {
10561
- return join(configuration.happyHomeDir, "live-terminals.json");
9491
+ return join$1(configuration.happyHomeDir, "live-terminals.json");
10562
9492
  }
10563
9493
  function loadLiveSnapshot() {
10564
9494
  try {
@@ -10833,7 +9763,7 @@ function tmuxSupportsEnvFlag() {
10833
9763
  }
10834
9764
  const PASTE_FILE_TTL_MS = 5e3;
10835
9765
  function pasteSpoolDir() {
10836
- const dir = join(configuration.happyHomeDir, "paste-spool");
9766
+ const dir = join$1(configuration.happyHomeDir, "paste-spool");
10837
9767
  try {
10838
9768
  mkdirSync(dir, { recursive: true, mode: 448 });
10839
9769
  } catch {
@@ -11662,7 +10592,7 @@ class WebTerminalManager {
11662
10592
  return false;
11663
10593
  }
11664
10594
  },
11665
- conversationExists: (cwd, claudeSessionId) => existsSync(join(getProjectPath(cwd), `${claudeSessionId}.jsonl`)),
10595
+ conversationExists: (cwd, claudeSessionId) => existsSync(join$1(getProjectPath(cwd), `${claudeSessionId}.jsonl`)),
11666
10596
  // B-273: only consulted for records that were attach terminals.
11667
10597
  // Unbounded: the panel's 50-row cap must not hide a restore target.
11668
10598
  userSessions: record.attachTmux ? this.listUserTmuxSessions(Infinity) : [],
@@ -12907,7 +11837,7 @@ function detectPosix() {
12907
11837
  const codex = commandExists("codex");
12908
11838
  const gemini = commandExists("gemini");
12909
11839
  const openclawCommand = commandExists("openclaw");
12910
- const openclawConfig = existsSync$1(join$1(os.homedir(), ".openclaw", "openclaw.json"));
11840
+ const openclawConfig = existsSync$1(join(os.homedir(), ".openclaw", "openclaw.json"));
12911
11841
  const openclawEnv = !!process.env.OPENCLAW_GATEWAY_URL;
12912
11842
  const openclaw = openclawCommand || openclawConfig || openclawEnv;
12913
11843
  const pi = commandExists("pi") && commandExists("pi-acp");
@@ -12919,7 +11849,7 @@ function detectWindows() {
12919
11849
  const codex = checkCommand("codex");
12920
11850
  const gemini = checkCommand("gemini");
12921
11851
  const openclawCommand = checkCommand("openclaw");
12922
- const openclawConfig = existsSync$1(join$1(process.env.USERPROFILE || os.homedir(), ".openclaw", "openclaw.json"));
11852
+ const openclawConfig = existsSync$1(join(process.env.USERPROFILE || os.homedir(), ".openclaw", "openclaw.json"));
12923
11853
  const openclawEnv = !!process.env.OPENCLAW_GATEWAY_URL;
12924
11854
  const openclaw = openclawCommand || openclawConfig || openclawEnv;
12925
11855
  const pi = checkCommand("pi") && checkCommand("pi-acp");
@@ -12954,7 +11884,7 @@ function deriveKey(master, usage, path) {
12954
11884
  }
12955
11885
  function deriveContentKeyPair(secret) {
12956
11886
  const seed = deriveKey(secret, "Happy EnCoder", ["content"]);
12957
- const hashedSeed = new Uint8Array(createHash("sha512").update(seed).digest());
11887
+ const hashedSeed = new Uint8Array(createHash$1("sha512").update(seed).digest());
12958
11888
  const secretKey = hashedSeed.slice(0, 32);
12959
11889
  const keyPair = tweetnacl.box.keyPair.fromSecretKey(secretKey);
12960
11890
  return {
@@ -12963,7 +11893,7 @@ function deriveContentKeyPair(secret) {
12963
11893
  };
12964
11894
  }
12965
11895
  function getLocalHappyAgentCredentialPath(happyHomeDir = configuration.happyHomeDir) {
12966
- return join(happyHomeDir, "agent.key");
11896
+ return join$1(happyHomeDir, "agent.key");
12967
11897
  }
12968
11898
  function readLocalHappyAgentCredentials(happyHomeDir = configuration.happyHomeDir, serverUrl = configuration.serverUrl) {
12969
11899
  const credentialPath = getLocalHappyAgentCredentialPath(happyHomeDir);
@@ -13021,7 +11951,7 @@ async function listClaudeProjectDirs(projectsRoot) {
13021
11951
  }
13022
11952
  const dirs = [];
13023
11953
  for (const name of names) {
13024
- const full = join(projectsRoot, name);
11954
+ const full = join$1(projectsRoot, name);
13025
11955
  try {
13026
11956
  if ((await stat$1(full)).isDirectory()) dirs.push(full);
13027
11957
  } catch {
@@ -13042,7 +11972,7 @@ async function collectCandidates(projectDirs, exclude) {
13042
11972
  if (!name.endsWith(".jsonl")) continue;
13043
11973
  const id = name.slice(0, -".jsonl".length);
13044
11974
  if (!UUID_RE$1.test(id) || exclude.has(id.toLowerCase())) continue;
13045
- const path = join(dir, name);
11975
+ const path = join$1(dir, name);
13046
11976
  try {
13047
11977
  const s = await stat$1(path);
13048
11978
  if (!s.isFile() || s.size === 0) continue;
@@ -13191,7 +12121,7 @@ class ForkSourceMissingError extends Error {
13191
12121
  }
13192
12122
  }
13193
12123
  function jsonlPath(projectDir, sessionId) {
13194
- return join(projectDir, `${sessionId}.jsonl`);
12124
+ return join$1(projectDir, `${sessionId}.jsonl`);
13195
12125
  }
13196
12126
  function isUserPrompt(parsed) {
13197
12127
  if (!parsed || parsed.type !== "user") return false;
@@ -13364,7 +12294,7 @@ async function forkBeforeUserMessage(projectDir, sourceClaudeSessionId, cutBefor
13364
12294
  const destination = jsonlPath(projectDir, newId);
13365
12295
  const temporary = `${destination}.tmp-${process.pid}`;
13366
12296
  try {
13367
- await writeFile(temporary, lines.slice(0, cut).join("\n") + "\n", { encoding: "utf-8", flag: "wx" });
12297
+ await writeFile$1(temporary, lines.slice(0, cut).join("\n") + "\n", { encoding: "utf-8", flag: "wx" });
13368
12298
  await rename$1(temporary, destination);
13369
12299
  } catch (error) {
13370
12300
  await unlink$1(temporary).catch(() => void 0);
@@ -16937,4 +15867,4 @@ const RawJSONLinesSchema = z$1.discriminatedUnion("type", [
16937
15867
  }).passthrough()
16938
15868
  ]);
16939
15869
 
16940
- export { tmuxArgs as $, ApiClient as A, commandOnPath as B, readCredentials as C, credentialRelayProblem as D, deriveLocalCliUpdateSummary as E, readSettingsOutcome as F, updateSettings as G, encodeBase64Url as H, InvalidateSync as I, decodeBase64 as J, writeCredentialsLegacy as K, writeCredentialsDataKey as L, MAX_CHAT_ATTACHMENT_SOURCE_BYTES as M, readCredentialsForConfiguredRelay as N, encrypt as O, isClaudeSessionId as P, getLocalHappyAgentCredentialPath as Q, RawJSONLinesSchema as R, STREAM_DELTA_MAX_CHARS as S, readLocalHappyAgentCredentials as T, decryptWithDataKey as U, decryptLegacy as V, parseExactVersion as W, compareExactVersions as X, extractCompleteLines as Y, decideBackfill as Z, parseTerminalHookPayload as _, ApiSessionClient as a, scrubTmuxClientEnv as a0, decideMirrorBinding as a1, MIRROR_BACKFILL_LINES_DEFAULT as a2, persistSession as a3, mirrorLineKey as a4, mirrorLocalId as a5, detectCLIAvailability as a6, detectResumeSupport as a7, acquireDaemonLock as a8, summarizeSpawnSessionForLog as a9, deletePersistedSessions as aa, shellescape as ab, decrypt as ac, writeDaemonState as ad, writeSettings as ae, fetchCliUpdateState as af, resolveCliUpdateCheckInterval as ag, DEFAULT_CLI_UPDATE_CHECK_INTERVAL_MS as ah, releaseDaemonLock as ai, listVhTerminals as aj, isValidTerminalId as ak, readVhTerminal as al, sendToVhTerminal as am, TITLE_PROMPT_PREFIX as an, BOARD_ANALYZER_PROMPT_PREFIX as ao, startOfflineReconnection as ap, clearCredentials as aq, clearMachineId as ar, SandboxConfigSchema as as, CodexAppServerClient as at, getLatestDaemonLog as au, persistence as av, api as aw, createEnvelope as b, configuration as c, connectionState as d, readSettings as e, encodeBase64 as f, contentLogMetadata as g, errorLogMetadata as h, isValidSessionId as i, packageJson as j, delay as k, logger as l, ensurePrivateDirectorySync as m, hardenPrivateFileSync as n, readDaemonState as o, projectPath as p, clearDaemonState as q, readPersistedSessions as r, streamKeyOf as s, getProjectPath as t, initializeSandbox as u, wrapCommand as v, writePrivateFileSync as w, AsyncLock as x, ensurePrivateDirectory as y, hardenPrivateFile as z };
15870
+ export { ApiClient as A, MIRROR_BACKFILL_LINES_DEFAULT as B, mirrorLineKey as C, mirrorLocalId as D, detectCLIAvailability as E, detectResumeSupport as F, summarizeSpawnSessionForLog as G, shellescape as H, InvalidateSync as I, fetchCliUpdateState as J, resolveCliUpdateCheckInterval as K, DEFAULT_CLI_UPDATE_CHECK_INTERVAL_MS as L, MAX_CHAT_ATTACHMENT_SOURCE_BYTES as M, listVhTerminals as N, isValidTerminalId as O, readVhTerminal as P, sendToVhTerminal as Q, RawJSONLinesSchema as R, STREAM_DELTA_MAX_CHARS as S, TITLE_PROMPT_PREFIX as T, BOARD_ANALYZER_PROMPT_PREFIX as U, startOfflineReconnection as V, CodexAppServerClient as W, api as X, ApiSessionClient as a, contentLogMetadata as b, createEnvelope as c, delay as d, errorLogMetadata as e, connectionState as f, getProjectPath as g, initializeSandbox as h, isValidSessionId as i, AsyncLock as j, commandOnPath as k, deriveLocalCliUpdateSummary as l, isClaudeSessionId as m, getLocalHappyAgentCredentialPath as n, parseExactVersion as o, projectPath as p, compareExactVersions as q, readLocalHappyAgentCredentials as r, streamKeyOf as s, extractCompleteLines as t, decideBackfill as u, parseTerminalHookPayload as v, wrapCommand as w, tmuxArgs as x, scrubTmuxClientEnv as y, decideMirrorBinding as z };