very-happy-cli 0.2.124 → 0.2.125

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-DOQGItcS.mjs} +2 -1
  2. package/dist/{AcpBackend-au0veuIF.cjs → AcpBackend-ssQpTLks.cjs} +30 -29
  3. package/dist/{AcpSessionManager-p1tj7FxF.mjs → AcpSessionManager-D4WkNsRs.mjs} +1 -1
  4. package/dist/{AcpSessionManager-BCWeBKDd.cjs → AcpSessionManager-DHLc-D50.cjs} +9 -9
  5. package/dist/agentGuidance-Cg9ZG1pP.cjs +236 -0
  6. package/dist/agentGuidance-QpYwykb9.mjs +224 -0
  7. package/dist/codex/happyMcpStdioBridge.cjs +29 -6
  8. package/dist/codex/happyMcpStdioBridge.mjs +25 -2
  9. package/dist/{config-GzzqE5Ev.cjs → config-BxHOGtEK.cjs} +11 -11
  10. package/dist/{config-DwMic6AC.mjs → config-CZNIz3W0.mjs} +11 -11
  11. package/dist/{index-DgN9P02i.cjs → index-CBj99SO6.cjs} +53 -19
  12. package/dist/{index-SQKnWyz-.mjs → index-CrSifgJM.mjs} +2046 -1719
  13. package/dist/{index-B3-daYaX.mjs → index-D-BgvQi5.mjs} +8 -8
  14. package/dist/{index-C69uF6Up.cjs → index-D_Rm9dw9.cjs} +8 -8
  15. package/dist/{index-DI_VC3vJ.cjs → index-Do-kjc30.cjs} +2191 -1864
  16. package/dist/{index-Ba6Fu6MJ.mjs → index-Jrk6ud3g.mjs} +49 -15
  17. package/dist/index.cjs +5 -5
  18. package/dist/index.mjs +5 -5
  19. package/dist/{installTerminalHooks-CvMt8cBI.cjs → installTerminalHooks-CPsdeOM4.cjs} +9 -9
  20. package/dist/{installTerminalHooks-Df4hzzdM.mjs → installTerminalHooks-Z1Jb6yjb.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-DViGHwSj.cjs +1198 -0
  26. package/dist/limits-FjbPJPsF.mjs +1129 -0
  27. package/dist/{mcp-DnyLwSPq.cjs → mcp-BbQ5FWcQ.cjs} +11 -10
  28. package/dist/{mcp-C0oW5u-b.mjs → mcp-zqPJ94Bk.mjs} +7 -6
  29. package/dist/resources-CVl7NdOB.cjs +83 -0
  30. package/dist/resources-D-mn7ptO.mjs +80 -0
  31. package/dist/{runGemini-_FHrxuHN.cjs → runGemini-BnHGYX-H.cjs} +30 -30
  32. package/dist/{runGemini-mqLjz-M7.mjs → runGemini-Bu49s_bN.mjs} +13 -13
  33. package/dist/{runOpenClaw-C5VZQ6Na.cjs → runOpenClaw-3Ld1eoWV.cjs} +24 -24
  34. package/dist/{runOpenClaw-Bs03yQHe.mjs → runOpenClaw-CznAYPCh.mjs} +9 -9
  35. package/dist/{send-BaLjQPZQ.mjs → send-BevgS1mf.mjs} +10 -10
  36. package/dist/{send-BD2gTVoP.cjs → send-DCzyBxLl.cjs} +10 -10
  37. package/dist/{sessions-ggBePfSY.mjs → sessions-Ba2oRTmw.mjs} +10 -10
  38. package/dist/{sessions-B108j_Xj.cjs → sessions-DSoaBHwA.cjs} +11 -11
  39. package/dist/{spawn-iI8w8sD7.cjs → spawn-CqMLY6dE.cjs} +21 -21
  40. package/dist/{spawn-CORFRb9D.mjs → spawn-j9KBfj4e.mjs} +21 -21
  41. package/dist/teams-C0ggJTJ-.mjs +164 -0
  42. package/dist/teams-CmU3K9vB.cjs +166 -0
  43. package/dist/{types-CZIwVSN2.cjs → types-BGHnQXl5.cjs} +462 -1571
  44. package/dist/{types-gDOmwOKw.mjs → types-DLDkfrTC.mjs} +76 -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-FjbPJPsF.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,34 @@ 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 id = z$1.string().min(1).max(128);
436
+ const text = z$1.string().min(1).max(32e3);
437
+ const TeamActionSchema = z$1.discriminatedUnion("type", [
438
+ z$1.object({ type: z$1.literal("archive") }),
439
+ 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() }),
440
+ z$1.object({ type: z$1.literal("schedule-pause"), scheduleId: id, version: z$1.number().int().positive() }),
441
+ z$1.object({ type: z$1.literal("schedule-resume"), scheduleId: id, version: z$1.number().int().positive() }),
442
+ z$1.object({ type: z$1.literal("schedule-cancel"), scheduleId: id, version: z$1.number().int().positive() }),
443
+ z$1.object({ type: z$1.literal("reconcile-operation"), operationId: id, claimId: id, note: z$1.string().trim().min(1).max(4e3) }),
444
+ z$1.object({ type: z$1.literal("join"), name: id, sessionId: id, botId: id.optional() }),
445
+ 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() }),
446
+ z$1.object({ type: z$1.literal("message"), taskId: id, body: text, recipientBotId: id.optional() }),
447
+ z$1.object({ type: z$1.literal("submit"), taskId: id, attemptId: id, goalVersion: z$1.number().int().positive(), result: text }),
448
+ z$1.object({ type: z$1.literal("accept"), taskId: id, attemptId: id, goalVersion: z$1.number().int().positive() }),
449
+ z$1.object({ type: z$1.literal("return"), taskId: id, attemptId: id, goalVersion: z$1.number().int().positive(), reason: text }),
450
+ z$1.object({ type: z$1.literal("cancel"), taskId: id, attemptId: id, goalVersion: z$1.number().int().positive(), reason: text }),
451
+ z$1.object({ type: z$1.literal("handoff"), taskId: id, attemptId: id, goalVersion: z$1.number().int().positive(), assigneeBotId: id }),
452
+ z$1.object({ type: z$1.literal("claim-operation"), operationId: id, machineId: id }),
453
+ z$1.object({ type: z$1.literal("complete-operation"), operationId: id, machineId: id, claimId: id, sessionId: id.optional() }),
454
+ z$1.object({ type: z$1.literal("fail-operation"), operationId: id, machineId: id, claimId: id, error: text, unknown: z$1.boolean().optional() }),
455
+ z$1.object({ type: z$1.literal("session-event"), sessionId: id, event: z$1.enum(["idle", "blocked", "exited"]) }),
456
+ z$1.object({ type: z$1.literal("message-delivered"), messageId: id, recipientBotId: id, generation: z$1.number().int().positive(), sessionId: id })
457
+ ]);
458
+ z$1.object({ requestId: id, action: TeamActionSchema });
459
+ z$1.object({ name: id, machineId: id, requestId: id.optional() });
1029
460
 
1030
461
  z$1.object({
462
+ teamsVersion: z$1.number().int().positive().optional(),
1031
463
  host: z$1.string(),
1032
464
  platform: z$1.string(),
1033
465
  happyCliVersion: z$1.string(),
@@ -1302,125 +734,6 @@ const AgentMessageSchema = z$1.object({
1302
734
  });
1303
735
  z$1.union([UserMessageSchema, AgentMessageSchema]);
1304
736
 
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
737
  async function delay(ms) {
1425
738
  return new Promise((resolve) => setTimeout(resolve, ms));
1426
739
  }
@@ -1693,7 +1006,7 @@ function projectPath() {
1693
1006
  }
1694
1007
 
1695
1008
  function run$1(args, options) {
1696
- const RUNNER_PATH = resolve(join$1(projectPath(), "scripts", "ripgrep_launcher.cjs"));
1009
+ const RUNNER_PATH = resolve(join(projectPath(), "scripts", "ripgrep_launcher.cjs"));
1697
1010
  return new Promise((resolve2, reject) => {
1698
1011
  const child = spawn("node", [RUNNER_PATH, JSON.stringify(args)], {
1699
1012
  stdio: ["pipe", "pipe", "pipe"],
@@ -1724,7 +1037,7 @@ function run$1(args, options) {
1724
1037
  function getBinaryPath() {
1725
1038
  const platformName = platform();
1726
1039
  const binaryName = platformName === "win32" ? "difft.exe" : "difft";
1727
- return resolve(join$1(projectPath(), "tools", "unpacked", binaryName));
1040
+ return resolve(join(projectPath(), "tools", "unpacked", binaryName));
1728
1041
  }
1729
1042
  function run(args, options) {
1730
1043
  const binaryPath = getBinaryPath();
@@ -1836,7 +1149,7 @@ function createUploadFileHandlers(happyHomeDir, now = Date.now, pathQuoteStyle =
1836
1149
  const entries = await readdir(dir, { withFileTypes: true }).catch(() => []);
1837
1150
  await Promise.all(entries.map(async (entry) => {
1838
1151
  if (!entry.isFile() || !/^\..+\.[A-Za-z0-9_-]{8,80}\.part$/.test(entry.name)) return;
1839
- const path = join$1(dir, entry.name);
1152
+ const path = join(dir, entry.name);
1840
1153
  const fileStat = await stat(path).catch(() => null);
1841
1154
  if (fileStat && fileStat.mtimeMs < cutoff) await unlink(path).catch(() => {
1842
1155
  });
@@ -1846,13 +1159,13 @@ function createUploadFileHandlers(happyHomeDir, now = Date.now, pathQuoteStyle =
1846
1159
  let tempPath = null;
1847
1160
  try {
1848
1161
  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 });
1162
+ const dir = join(happyHomeDir, "uploads", safeSegment(data.subdir, "misc", 80));
1163
+ await mkdir(dir, { recursive: true });
1851
1164
  const name = safeFileName(data.name);
1852
1165
  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" });
1166
+ const target = join(dir, uniqueFileName(name, token));
1167
+ tempPath = join(dir, `.${name}.legacy_${randomUUID().replace(/-/g, "")}.part`);
1168
+ await writeFile(tempPath, buffer, { flag: "wx" });
1856
1169
  await rename(tempPath, target);
1857
1170
  tempPath = null;
1858
1171
  return { success: true, path: target, pathQuoteStyle, size: buffer.length };
@@ -1872,13 +1185,13 @@ function createUploadFileHandlers(happyHomeDir, now = Date.now, pathQuoteStyle =
1872
1185
  }
1873
1186
  if (active.has(uploadId)) throw new Error("Upload id is already active");
1874
1187
  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 });
1188
+ const dir = join(happyHomeDir, "uploads", safeSegment(data.subdir, "misc", 80));
1189
+ await mkdir(dir, { recursive: true });
1877
1190
  await sweepExpiredFiles(dir);
1878
1191
  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" });
1192
+ const targetPath = join(dir, uniqueFileName(name, randomUUID().replace(/-/g, "").slice(0, 12)));
1193
+ const tempPath = join(dir, `.${name}.${uploadId}.part`);
1194
+ await writeFile(tempPath, Buffer.alloc(0), { flag: "wx" });
1882
1195
  active.set(uploadId, { tempPath, targetPath, totalSize: data.totalSize, received: 0, touchedAt: now() });
1883
1196
  return { success: true, size: 0 };
1884
1197
  }
@@ -2014,7 +1327,7 @@ function registerCommonHandlers(rpcHandlerManager, workingDirectory) {
2014
1327
  if (data.expectedHash !== null && data.expectedHash !== void 0) {
2015
1328
  try {
2016
1329
  const existingBuffer = await readFile(validation.resolvedPath);
2017
- const existingHash = createHash$1("sha256").update(existingBuffer).digest("hex");
1330
+ const existingHash = createHash("sha256").update(existingBuffer).digest("hex");
2018
1331
  if (existingHash !== data.expectedHash) {
2019
1332
  return {
2020
1333
  success: false,
@@ -2046,8 +1359,8 @@ function registerCommonHandlers(rpcHandlerManager, workingDirectory) {
2046
1359
  }
2047
1360
  }
2048
1361
  const buffer = Buffer.from(data.content, "base64");
2049
- await writeFile$1(validation.resolvedPath, buffer);
2050
- const hash = createHash$1("sha256").update(buffer).digest("hex");
1362
+ await writeFile(validation.resolvedPath, buffer);
1363
+ const hash = createHash("sha256").update(buffer).digest("hex");
2051
1364
  return { success: true, hash };
2052
1365
  } catch (error) {
2053
1366
  logger.debug("Failed to write file:", error);
@@ -2068,7 +1381,7 @@ function registerCommonHandlers(rpcHandlerManager, workingDirectory) {
2068
1381
  const entries = await readdir(directoryPath, { withFileTypes: true });
2069
1382
  const directoryEntries = await Promise.all(
2070
1383
  entries.map(async (entry) => {
2071
- const fullPath = join$1(directoryPath, entry.name);
1384
+ const fullPath = join(directoryPath, entry.name);
2072
1385
  let type = "other";
2073
1386
  let size;
2074
1387
  let modified;
@@ -2125,10 +1438,10 @@ function registerCommonHandlers(rpcHandlerManager, workingDirectory) {
2125
1438
  await Promise.all(
2126
1439
  entries.map(async (entry) => {
2127
1440
  if (entry.isSymbolicLink()) {
2128
- logger.debug(`Skipping symlink: ${join$1(path, entry.name)}`);
1441
+ logger.debug(`Skipping symlink: ${join(path, entry.name)}`);
2129
1442
  return;
2130
1443
  }
2131
- const childPath = join$1(path, entry.name);
1444
+ const childPath = join(path, entry.name);
2132
1445
  const childNode = await buildTree(childPath, entry.name, currentDepth + 1);
2133
1446
  if (childNode) {
2134
1447
  children.push(childNode);
@@ -4423,10 +3736,10 @@ function normalizeFsPath(input, homeDir) {
4423
3736
  if (p === "~") {
4424
3737
  p = homeDir;
4425
3738
  } else if (p.startsWith("~/")) {
4426
- p = join(homeDir, p.slice(2));
3739
+ p = join$1(homeDir, p.slice(2));
4427
3740
  }
4428
3741
  if (!isAbsolute(p)) {
4429
- p = join(homeDir, p);
3742
+ p = join$1(homeDir, p);
4430
3743
  }
4431
3744
  return resolve$1(p);
4432
3745
  }
@@ -4482,7 +3795,7 @@ async function fsList(inputPath, homeDir = homedir()) {
4482
3795
  dirents.map(async (d) => {
4483
3796
  const entry = { name: d.name, type: entryTypeOf(d) };
4484
3797
  try {
4485
- const s = await lstat(join(path, d.name));
3798
+ const s = await lstat(join$1(path, d.name));
4486
3799
  entry.size = s.size;
4487
3800
  entry.mtimeMs = Math.round(s.mtimeMs);
4488
3801
  } catch {
@@ -4539,391 +3852,6 @@ function registerFsHandlers(rpcHandlerManager) {
4539
3852
  });
4540
3853
  }
4541
3854
 
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
3855
  const TODO_LIST_MAX_ITEMS = 500;
4928
3856
  const TODO_TITLE_MAX_CHARS = 500;
4929
3857
  const FIELD_MAX_CHARS = 200;
@@ -5282,14 +4210,14 @@ function scrubTmuxClientEnv(env) {
5282
4210
  }
5283
4211
 
5284
4212
  function getClaudeConfigDir() {
5285
- return process.env.CLAUDE_CONFIG_DIR || join(homedir(), ".claude");
4213
+ return process.env.CLAUDE_CONFIG_DIR || join$1(homedir(), ".claude");
5286
4214
  }
5287
4215
  function getClaudeProjectsRoot() {
5288
- return join(getClaudeConfigDir(), "projects");
4216
+ return join$1(getClaudeConfigDir(), "projects");
5289
4217
  }
5290
4218
  function getProjectPath(workingDirectory) {
5291
4219
  const projectId = resolve$1(workingDirectory).replace(/[^a-zA-Z0-9-]/g, "-");
5292
- return join(getClaudeProjectsRoot(), projectId);
4220
+ return join$1(getClaudeProjectsRoot(), projectId);
5293
4221
  }
5294
4222
 
5295
4223
  var xtermHeadless = {};
@@ -10521,7 +9449,7 @@ function pruneTombstones(map, now, ttlMs = TOMBSTONE_TTL_MS) {
10521
9449
  return out;
10522
9450
  }
10523
9451
  function tombstoneFile() {
10524
- return join(configuration.happyHomeDir, "terminal-tombstones.json");
9452
+ return join$1(configuration.happyHomeDir, "terminal-tombstones.json");
10525
9453
  }
10526
9454
  function loadTombstones() {
10527
9455
  try {
@@ -10540,7 +9468,7 @@ function saveTombstones(map) {
10540
9468
  }
10541
9469
  }
10542
9470
  function closedTerminalsFile() {
10543
- return join(configuration.happyHomeDir, "closed-terminals.json");
9471
+ return join$1(configuration.happyHomeDir, "closed-terminals.json");
10544
9472
  }
10545
9473
  function loadClosedTerminals() {
10546
9474
  try {
@@ -10558,7 +9486,7 @@ function saveClosedTerminals(list) {
10558
9486
  }
10559
9487
  }
10560
9488
  function liveTerminalsFile() {
10561
- return join(configuration.happyHomeDir, "live-terminals.json");
9489
+ return join$1(configuration.happyHomeDir, "live-terminals.json");
10562
9490
  }
10563
9491
  function loadLiveSnapshot() {
10564
9492
  try {
@@ -10833,7 +9761,7 @@ function tmuxSupportsEnvFlag() {
10833
9761
  }
10834
9762
  const PASTE_FILE_TTL_MS = 5e3;
10835
9763
  function pasteSpoolDir() {
10836
- const dir = join(configuration.happyHomeDir, "paste-spool");
9764
+ const dir = join$1(configuration.happyHomeDir, "paste-spool");
10837
9765
  try {
10838
9766
  mkdirSync(dir, { recursive: true, mode: 448 });
10839
9767
  } catch {
@@ -11662,7 +10590,7 @@ class WebTerminalManager {
11662
10590
  return false;
11663
10591
  }
11664
10592
  },
11665
- conversationExists: (cwd, claudeSessionId) => existsSync(join(getProjectPath(cwd), `${claudeSessionId}.jsonl`)),
10593
+ conversationExists: (cwd, claudeSessionId) => existsSync(join$1(getProjectPath(cwd), `${claudeSessionId}.jsonl`)),
11666
10594
  // B-273: only consulted for records that were attach terminals.
11667
10595
  // Unbounded: the panel's 50-row cap must not hide a restore target.
11668
10596
  userSessions: record.attachTmux ? this.listUserTmuxSessions(Infinity) : [],
@@ -12907,7 +11835,7 @@ function detectPosix() {
12907
11835
  const codex = commandExists("codex");
12908
11836
  const gemini = commandExists("gemini");
12909
11837
  const openclawCommand = commandExists("openclaw");
12910
- const openclawConfig = existsSync$1(join$1(os.homedir(), ".openclaw", "openclaw.json"));
11838
+ const openclawConfig = existsSync$1(join(os.homedir(), ".openclaw", "openclaw.json"));
12911
11839
  const openclawEnv = !!process.env.OPENCLAW_GATEWAY_URL;
12912
11840
  const openclaw = openclawCommand || openclawConfig || openclawEnv;
12913
11841
  const pi = commandExists("pi") && commandExists("pi-acp");
@@ -12919,7 +11847,7 @@ function detectWindows() {
12919
11847
  const codex = checkCommand("codex");
12920
11848
  const gemini = checkCommand("gemini");
12921
11849
  const openclawCommand = checkCommand("openclaw");
12922
- const openclawConfig = existsSync$1(join$1(process.env.USERPROFILE || os.homedir(), ".openclaw", "openclaw.json"));
11850
+ const openclawConfig = existsSync$1(join(process.env.USERPROFILE || os.homedir(), ".openclaw", "openclaw.json"));
12923
11851
  const openclawEnv = !!process.env.OPENCLAW_GATEWAY_URL;
12924
11852
  const openclaw = openclawCommand || openclawConfig || openclawEnv;
12925
11853
  const pi = checkCommand("pi") && checkCommand("pi-acp");
@@ -12954,7 +11882,7 @@ function deriveKey(master, usage, path) {
12954
11882
  }
12955
11883
  function deriveContentKeyPair(secret) {
12956
11884
  const seed = deriveKey(secret, "Happy EnCoder", ["content"]);
12957
- const hashedSeed = new Uint8Array(createHash("sha512").update(seed).digest());
11885
+ const hashedSeed = new Uint8Array(createHash$1("sha512").update(seed).digest());
12958
11886
  const secretKey = hashedSeed.slice(0, 32);
12959
11887
  const keyPair = tweetnacl.box.keyPair.fromSecretKey(secretKey);
12960
11888
  return {
@@ -12963,7 +11891,7 @@ function deriveContentKeyPair(secret) {
12963
11891
  };
12964
11892
  }
12965
11893
  function getLocalHappyAgentCredentialPath(happyHomeDir = configuration.happyHomeDir) {
12966
- return join(happyHomeDir, "agent.key");
11894
+ return join$1(happyHomeDir, "agent.key");
12967
11895
  }
12968
11896
  function readLocalHappyAgentCredentials(happyHomeDir = configuration.happyHomeDir, serverUrl = configuration.serverUrl) {
12969
11897
  const credentialPath = getLocalHappyAgentCredentialPath(happyHomeDir);
@@ -13021,7 +11949,7 @@ async function listClaudeProjectDirs(projectsRoot) {
13021
11949
  }
13022
11950
  const dirs = [];
13023
11951
  for (const name of names) {
13024
- const full = join(projectsRoot, name);
11952
+ const full = join$1(projectsRoot, name);
13025
11953
  try {
13026
11954
  if ((await stat$1(full)).isDirectory()) dirs.push(full);
13027
11955
  } catch {
@@ -13042,7 +11970,7 @@ async function collectCandidates(projectDirs, exclude) {
13042
11970
  if (!name.endsWith(".jsonl")) continue;
13043
11971
  const id = name.slice(0, -".jsonl".length);
13044
11972
  if (!UUID_RE$1.test(id) || exclude.has(id.toLowerCase())) continue;
13045
- const path = join(dir, name);
11973
+ const path = join$1(dir, name);
13046
11974
  try {
13047
11975
  const s = await stat$1(path);
13048
11976
  if (!s.isFile() || s.size === 0) continue;
@@ -13191,7 +12119,7 @@ class ForkSourceMissingError extends Error {
13191
12119
  }
13192
12120
  }
13193
12121
  function jsonlPath(projectDir, sessionId) {
13194
- return join(projectDir, `${sessionId}.jsonl`);
12122
+ return join$1(projectDir, `${sessionId}.jsonl`);
13195
12123
  }
13196
12124
  function isUserPrompt(parsed) {
13197
12125
  if (!parsed || parsed.type !== "user") return false;
@@ -13364,7 +12292,7 @@ async function forkBeforeUserMessage(projectDir, sourceClaudeSessionId, cutBefor
13364
12292
  const destination = jsonlPath(projectDir, newId);
13365
12293
  const temporary = `${destination}.tmp-${process.pid}`;
13366
12294
  try {
13367
- await writeFile(temporary, lines.slice(0, cut).join("\n") + "\n", { encoding: "utf-8", flag: "wx" });
12295
+ await writeFile$1(temporary, lines.slice(0, cut).join("\n") + "\n", { encoding: "utf-8", flag: "wx" });
13368
12296
  await rename$1(temporary, destination);
13369
12297
  } catch (error) {
13370
12298
  await unlink$1(temporary).catch(() => void 0);
@@ -16937,4 +15865,4 @@ const RawJSONLinesSchema = z$1.discriminatedUnion("type", [
16937
15865
  }).passthrough()
16938
15866
  ]);
16939
15867
 
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 };
15868
+ 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 };