happy-coder 0.9.0-6 → 0.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +1044 -1089
- package/dist/index.mjs +797 -824
- package/dist/lib.cjs +4 -4
- package/dist/lib.d.cts +48 -3
- package/dist/lib.d.mts +48 -3
- package/dist/lib.mjs +4 -4
- package/dist/{types-DJOX-XG-.mjs → types-BS8Pr3Im.mjs} +588 -204
- package/dist/{types-a-nJyP-e.cjs → types-DNUk09Np.cjs} +486 -71
- package/package.json +5 -5
|
@@ -1,30 +1,159 @@
|
|
|
1
1
|
import axios from 'axios';
|
|
2
2
|
import chalk from 'chalk';
|
|
3
3
|
import { appendFileSync } from 'fs';
|
|
4
|
+
import { existsSync, mkdirSync, constants, readFileSync, unlinkSync, writeFileSync, readdirSync, statSync } from 'node:fs';
|
|
4
5
|
import { homedir } from 'node:os';
|
|
5
|
-
import { join } from 'node:path';
|
|
6
|
-
import { mkdir } from 'node:fs/promises';
|
|
7
|
-
import
|
|
8
|
-
import {
|
|
9
|
-
import { io } from 'socket.io-client';
|
|
10
|
-
import { z } from 'zod';
|
|
6
|
+
import { join, basename } from 'node:path';
|
|
7
|
+
import { readFile, open, stat, unlink, mkdir, writeFile, rename } from 'node:fs/promises';
|
|
8
|
+
import * as z from 'zod';
|
|
9
|
+
import { z as z$1 } from 'zod';
|
|
11
10
|
import { randomBytes, randomUUID } from 'node:crypto';
|
|
12
11
|
import tweetnacl from 'tweetnacl';
|
|
12
|
+
import { EventEmitter } from 'node:events';
|
|
13
|
+
import { io } from 'socket.io-client';
|
|
13
14
|
import { Expo } from 'expo-server-sdk';
|
|
14
15
|
|
|
16
|
+
var name = "happy-coder";
|
|
17
|
+
var version = "0.9.1";
|
|
18
|
+
var description = "Claude Code session sharing CLI";
|
|
19
|
+
var author = "Kirill Dubovitskiy";
|
|
20
|
+
var license = "MIT";
|
|
21
|
+
var type = "module";
|
|
22
|
+
var homepage = "https://github.com/slopus/happy-cli";
|
|
23
|
+
var bugs = "https://github.com/slopus/happy-cli/issues";
|
|
24
|
+
var repository = "slopus/happy-cli";
|
|
25
|
+
var bin = {
|
|
26
|
+
happy: "./bin/happy.mjs"
|
|
27
|
+
};
|
|
28
|
+
var main = "./dist/index.cjs";
|
|
29
|
+
var module = "./dist/index.mjs";
|
|
30
|
+
var types = "./dist/index.d.cts";
|
|
31
|
+
var exports = {
|
|
32
|
+
".": {
|
|
33
|
+
require: {
|
|
34
|
+
types: "./dist/index.d.cts",
|
|
35
|
+
"default": "./dist/index.cjs"
|
|
36
|
+
},
|
|
37
|
+
"import": {
|
|
38
|
+
types: "./dist/index.d.mts",
|
|
39
|
+
"default": "./dist/index.mjs"
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
"./lib": {
|
|
43
|
+
require: {
|
|
44
|
+
types: "./dist/lib.d.cts",
|
|
45
|
+
"default": "./dist/lib.cjs"
|
|
46
|
+
},
|
|
47
|
+
"import": {
|
|
48
|
+
types: "./dist/lib.d.mts",
|
|
49
|
+
"default": "./dist/lib.mjs"
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
var files = [
|
|
54
|
+
"dist",
|
|
55
|
+
"bin",
|
|
56
|
+
"scripts",
|
|
57
|
+
"ripgrep",
|
|
58
|
+
"package.json"
|
|
59
|
+
];
|
|
60
|
+
var scripts = {
|
|
61
|
+
"why do we need to build before running tests / dev?": "We need the binary to be built so we run daemon commands which directly run the binary - we don't want them to go out of sync or have custom spawn logic depending how we started happy",
|
|
62
|
+
typecheck: "tsc --noEmit",
|
|
63
|
+
build: "shx rm -rf dist && npx tsc --noEmit && pkgroll",
|
|
64
|
+
test: "yarn build && tsx --env-file .env.integration-test node_modules/.bin/vitest run",
|
|
65
|
+
start: "yarn build && ./bin/happy.mjs",
|
|
66
|
+
dev: "yarn build && tsx --env-file .env.dev src/index.ts",
|
|
67
|
+
"dev:local-server": "yarn build && tsx --env-file .env.dev-local-server src/index.ts",
|
|
68
|
+
"dev:integration-test-env": "yarn build && tsx --env-file .env.integration-test src/index.ts",
|
|
69
|
+
prepublishOnly: "yarn build && yarn test",
|
|
70
|
+
release: "release-it"
|
|
71
|
+
};
|
|
72
|
+
var dependencies = {
|
|
73
|
+
"@anthropic-ai/claude-code": "^1.0.89",
|
|
74
|
+
"@anthropic-ai/sdk": "^0.56.0",
|
|
75
|
+
"@modelcontextprotocol/sdk": "^1.15.1",
|
|
76
|
+
"@stablelib/base64": "^2.0.1",
|
|
77
|
+
"@types/http-proxy": "^1.17.16",
|
|
78
|
+
"@types/qrcode-terminal": "^0.12.2",
|
|
79
|
+
"@types/react": "^19.1.9",
|
|
80
|
+
axios: "^1.10.0",
|
|
81
|
+
chalk: "^5.4.1",
|
|
82
|
+
"expo-server-sdk": "^3.15.0",
|
|
83
|
+
fastify: "^5.5.0",
|
|
84
|
+
"fastify-type-provider-zod": "4.0.2",
|
|
85
|
+
"http-proxy": "^1.18.1",
|
|
86
|
+
"http-proxy-middleware": "^3.0.5",
|
|
87
|
+
ink: "^6.1.0",
|
|
88
|
+
open: "^10.2.0",
|
|
89
|
+
"qrcode-terminal": "^0.12.0",
|
|
90
|
+
react: "^19.1.1",
|
|
91
|
+
"socket.io-client": "^4.8.1",
|
|
92
|
+
tweetnacl: "^1.0.3",
|
|
93
|
+
zod: "^3.23.8"
|
|
94
|
+
};
|
|
95
|
+
var devDependencies = {
|
|
96
|
+
"@eslint/compat": "^1",
|
|
97
|
+
"@types/node": ">=20",
|
|
98
|
+
"cross-env": "^10.0.0",
|
|
99
|
+
dotenv: "^16.6.1",
|
|
100
|
+
eslint: "^9",
|
|
101
|
+
"eslint-config-prettier": "^10",
|
|
102
|
+
pkgroll: "^2.14.2",
|
|
103
|
+
"release-it": "^19.0.4",
|
|
104
|
+
shx: "^0.3.3",
|
|
105
|
+
"ts-node": "^10",
|
|
106
|
+
tsx: "^4.20.3",
|
|
107
|
+
typescript: "^5",
|
|
108
|
+
vitest: "^3.2.4"
|
|
109
|
+
};
|
|
110
|
+
var resolutions = {
|
|
111
|
+
"whatwg-url": "14.2.0",
|
|
112
|
+
"parse-path": "7.0.3",
|
|
113
|
+
"@types/parse-path": "7.0.3"
|
|
114
|
+
};
|
|
115
|
+
var publishConfig = {
|
|
116
|
+
registry: "https://registry.npmjs.org"
|
|
117
|
+
};
|
|
118
|
+
var packageManager = "yarn@1.22.22";
|
|
119
|
+
var packageJson = {
|
|
120
|
+
name: name,
|
|
121
|
+
version: version,
|
|
122
|
+
description: description,
|
|
123
|
+
author: author,
|
|
124
|
+
license: license,
|
|
125
|
+
type: type,
|
|
126
|
+
homepage: homepage,
|
|
127
|
+
bugs: bugs,
|
|
128
|
+
repository: repository,
|
|
129
|
+
bin: bin,
|
|
130
|
+
main: main,
|
|
131
|
+
module: module,
|
|
132
|
+
types: types,
|
|
133
|
+
exports: exports,
|
|
134
|
+
files: files,
|
|
135
|
+
scripts: scripts,
|
|
136
|
+
dependencies: dependencies,
|
|
137
|
+
devDependencies: devDependencies,
|
|
138
|
+
resolutions: resolutions,
|
|
139
|
+
publishConfig: publishConfig,
|
|
140
|
+
packageManager: packageManager
|
|
141
|
+
};
|
|
142
|
+
|
|
15
143
|
class Configuration {
|
|
16
144
|
serverUrl;
|
|
17
145
|
isDaemonProcess;
|
|
18
146
|
// Directories and paths (from persistence)
|
|
19
147
|
happyHomeDir;
|
|
20
148
|
logsDir;
|
|
21
|
-
daemonLogsDir;
|
|
22
149
|
settingsFile;
|
|
23
150
|
privateKeyFile;
|
|
24
151
|
daemonStateFile;
|
|
152
|
+
daemonLockFile;
|
|
153
|
+
currentCliVersion;
|
|
25
154
|
isExperimentalEnabled;
|
|
26
155
|
constructor() {
|
|
27
|
-
this.serverUrl = process.env.HAPPY_SERVER_URL || "https://
|
|
156
|
+
this.serverUrl = process.env.HAPPY_SERVER_URL || "https://api.cluster-fluster.com";
|
|
28
157
|
const args = process.argv.slice(2);
|
|
29
158
|
this.isDaemonProcess = args.length >= 2 && args[0] === "daemon" && args[1] === "start-sync";
|
|
30
159
|
if (process.env.HAPPY_HOME_DIR) {
|
|
@@ -34,15 +163,231 @@ class Configuration {
|
|
|
34
163
|
this.happyHomeDir = join(homedir(), ".happy");
|
|
35
164
|
}
|
|
36
165
|
this.logsDir = join(this.happyHomeDir, "logs");
|
|
37
|
-
this.daemonLogsDir = join(this.happyHomeDir, "logs-daemon");
|
|
38
166
|
this.settingsFile = join(this.happyHomeDir, "settings.json");
|
|
39
167
|
this.privateKeyFile = join(this.happyHomeDir, "access.key");
|
|
40
168
|
this.daemonStateFile = join(this.happyHomeDir, "daemon.state.json");
|
|
169
|
+
this.daemonLockFile = join(this.happyHomeDir, "daemon.state.json.lock");
|
|
41
170
|
this.isExperimentalEnabled = ["true", "1", "yes"].includes(process.env.HAPPY_EXPERIMENTAL?.toLowerCase() || "");
|
|
171
|
+
this.currentCliVersion = packageJson.version;
|
|
172
|
+
if (!existsSync(this.happyHomeDir)) {
|
|
173
|
+
mkdirSync(this.happyHomeDir, { recursive: true });
|
|
174
|
+
}
|
|
175
|
+
if (!existsSync(this.logsDir)) {
|
|
176
|
+
mkdirSync(this.logsDir, { recursive: true });
|
|
177
|
+
}
|
|
42
178
|
}
|
|
43
179
|
}
|
|
44
180
|
const configuration = new Configuration();
|
|
45
181
|
|
|
182
|
+
function encodeBase64(buffer, variant = "base64") {
|
|
183
|
+
if (variant === "base64url") {
|
|
184
|
+
return encodeBase64Url(buffer);
|
|
185
|
+
}
|
|
186
|
+
return Buffer.from(buffer).toString("base64");
|
|
187
|
+
}
|
|
188
|
+
function encodeBase64Url(buffer) {
|
|
189
|
+
return Buffer.from(buffer).toString("base64").replaceAll("+", "-").replaceAll("/", "_").replaceAll("=", "");
|
|
190
|
+
}
|
|
191
|
+
function decodeBase64(base64, variant = "base64") {
|
|
192
|
+
if (variant === "base64url") {
|
|
193
|
+
const base64Standard = base64.replaceAll("-", "+").replaceAll("_", "/") + "=".repeat((4 - base64.length % 4) % 4);
|
|
194
|
+
return new Uint8Array(Buffer.from(base64Standard, "base64"));
|
|
195
|
+
}
|
|
196
|
+
return new Uint8Array(Buffer.from(base64, "base64"));
|
|
197
|
+
}
|
|
198
|
+
function getRandomBytes(size) {
|
|
199
|
+
return new Uint8Array(randomBytes(size));
|
|
200
|
+
}
|
|
201
|
+
function encrypt(data, secret) {
|
|
202
|
+
const nonce = getRandomBytes(tweetnacl.secretbox.nonceLength);
|
|
203
|
+
const encrypted = tweetnacl.secretbox(new TextEncoder().encode(JSON.stringify(data)), nonce, secret);
|
|
204
|
+
const result = new Uint8Array(nonce.length + encrypted.length);
|
|
205
|
+
result.set(nonce);
|
|
206
|
+
result.set(encrypted, nonce.length);
|
|
207
|
+
return result;
|
|
208
|
+
}
|
|
209
|
+
function decrypt(data, secret) {
|
|
210
|
+
const nonce = data.slice(0, tweetnacl.secretbox.nonceLength);
|
|
211
|
+
const encrypted = data.slice(tweetnacl.secretbox.nonceLength);
|
|
212
|
+
const decrypted = tweetnacl.secretbox.open(encrypted, nonce, secret);
|
|
213
|
+
if (!decrypted) {
|
|
214
|
+
return null;
|
|
215
|
+
}
|
|
216
|
+
return JSON.parse(new TextDecoder().decode(decrypted));
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const defaultSettings = {
|
|
220
|
+
onboardingCompleted: false
|
|
221
|
+
};
|
|
222
|
+
async function readSettings() {
|
|
223
|
+
if (!existsSync(configuration.settingsFile)) {
|
|
224
|
+
return { ...defaultSettings };
|
|
225
|
+
}
|
|
226
|
+
try {
|
|
227
|
+
const content = await readFile(configuration.settingsFile, "utf8");
|
|
228
|
+
return JSON.parse(content);
|
|
229
|
+
} catch {
|
|
230
|
+
return { ...defaultSettings };
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
async function updateSettings(updater) {
|
|
234
|
+
const LOCK_RETRY_INTERVAL_MS = 100;
|
|
235
|
+
const MAX_LOCK_ATTEMPTS = 50;
|
|
236
|
+
const STALE_LOCK_TIMEOUT_MS = 1e4;
|
|
237
|
+
const lockFile = configuration.settingsFile + ".lock";
|
|
238
|
+
const tmpFile = configuration.settingsFile + ".tmp";
|
|
239
|
+
let fileHandle;
|
|
240
|
+
let attempts = 0;
|
|
241
|
+
while (attempts < MAX_LOCK_ATTEMPTS) {
|
|
242
|
+
try {
|
|
243
|
+
fileHandle = await open(lockFile, constants.O_CREAT | constants.O_EXCL | constants.O_WRONLY);
|
|
244
|
+
break;
|
|
245
|
+
} catch (err) {
|
|
246
|
+
if (err.code === "EEXIST") {
|
|
247
|
+
attempts++;
|
|
248
|
+
await new Promise((resolve) => setTimeout(resolve, LOCK_RETRY_INTERVAL_MS));
|
|
249
|
+
try {
|
|
250
|
+
const stats = await stat(lockFile);
|
|
251
|
+
if (Date.now() - stats.mtimeMs > STALE_LOCK_TIMEOUT_MS) {
|
|
252
|
+
await unlink(lockFile).catch(() => {
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
} catch {
|
|
256
|
+
}
|
|
257
|
+
} else {
|
|
258
|
+
throw err;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
if (!fileHandle) {
|
|
263
|
+
throw new Error(`Failed to acquire settings lock after ${MAX_LOCK_ATTEMPTS * LOCK_RETRY_INTERVAL_MS / 1e3} seconds`);
|
|
264
|
+
}
|
|
265
|
+
try {
|
|
266
|
+
const current = await readSettings() || { ...defaultSettings };
|
|
267
|
+
const updated = await updater(current);
|
|
268
|
+
if (!existsSync(configuration.happyHomeDir)) {
|
|
269
|
+
await mkdir(configuration.happyHomeDir, { recursive: true });
|
|
270
|
+
}
|
|
271
|
+
await writeFile(tmpFile, JSON.stringify(updated, null, 2));
|
|
272
|
+
await rename(tmpFile, configuration.settingsFile);
|
|
273
|
+
return updated;
|
|
274
|
+
} finally {
|
|
275
|
+
await fileHandle.close();
|
|
276
|
+
await unlink(lockFile).catch(() => {
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
const credentialsSchema = z.object({
|
|
281
|
+
secret: z.string().base64(),
|
|
282
|
+
token: z.string()
|
|
283
|
+
});
|
|
284
|
+
async function readCredentials() {
|
|
285
|
+
if (!existsSync(configuration.privateKeyFile)) {
|
|
286
|
+
return null;
|
|
287
|
+
}
|
|
288
|
+
try {
|
|
289
|
+
const keyBase64 = await readFile(configuration.privateKeyFile, "utf8");
|
|
290
|
+
const credentials = credentialsSchema.parse(JSON.parse(keyBase64));
|
|
291
|
+
return {
|
|
292
|
+
secret: new Uint8Array(Buffer.from(credentials.secret, "base64")),
|
|
293
|
+
token: credentials.token
|
|
294
|
+
};
|
|
295
|
+
} catch {
|
|
296
|
+
return null;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
async function writeCredentials(credentials) {
|
|
300
|
+
if (!existsSync(configuration.happyHomeDir)) {
|
|
301
|
+
await mkdir(configuration.happyHomeDir, { recursive: true });
|
|
302
|
+
}
|
|
303
|
+
await writeFile(configuration.privateKeyFile, JSON.stringify({
|
|
304
|
+
secret: encodeBase64(credentials.secret),
|
|
305
|
+
token: credentials.token
|
|
306
|
+
}, null, 2));
|
|
307
|
+
}
|
|
308
|
+
async function clearCredentials() {
|
|
309
|
+
if (existsSync(configuration.privateKeyFile)) {
|
|
310
|
+
await unlink(configuration.privateKeyFile);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
async function clearMachineId() {
|
|
314
|
+
await updateSettings((settings) => ({
|
|
315
|
+
...settings,
|
|
316
|
+
machineId: void 0
|
|
317
|
+
}));
|
|
318
|
+
}
|
|
319
|
+
async function readDaemonState() {
|
|
320
|
+
try {
|
|
321
|
+
if (!existsSync(configuration.daemonStateFile)) {
|
|
322
|
+
return null;
|
|
323
|
+
}
|
|
324
|
+
const content = await readFile(configuration.daemonStateFile, "utf-8");
|
|
325
|
+
return JSON.parse(content);
|
|
326
|
+
} catch (error) {
|
|
327
|
+
console.error(`[PERSISTENCE] Daemon state file corrupted: ${configuration.daemonStateFile}`, error);
|
|
328
|
+
return null;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
function writeDaemonState(state) {
|
|
332
|
+
writeFileSync(configuration.daemonStateFile, JSON.stringify(state, null, 2), "utf-8");
|
|
333
|
+
}
|
|
334
|
+
async function clearDaemonState() {
|
|
335
|
+
if (existsSync(configuration.daemonStateFile)) {
|
|
336
|
+
await unlink(configuration.daemonStateFile);
|
|
337
|
+
}
|
|
338
|
+
if (existsSync(configuration.daemonLockFile)) {
|
|
339
|
+
try {
|
|
340
|
+
await unlink(configuration.daemonLockFile);
|
|
341
|
+
} catch {
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
async function acquireDaemonLock(maxAttempts = 5, delayIncrementMs = 200) {
|
|
346
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
347
|
+
try {
|
|
348
|
+
const fileHandle = await open(
|
|
349
|
+
configuration.daemonLockFile,
|
|
350
|
+
constants.O_CREAT | constants.O_EXCL | constants.O_WRONLY
|
|
351
|
+
);
|
|
352
|
+
await fileHandle.writeFile(String(process.pid));
|
|
353
|
+
return fileHandle;
|
|
354
|
+
} catch (error) {
|
|
355
|
+
if (error.code === "EEXIST") {
|
|
356
|
+
try {
|
|
357
|
+
const lockPid = readFileSync(configuration.daemonLockFile, "utf-8").trim();
|
|
358
|
+
if (lockPid && !isNaN(Number(lockPid))) {
|
|
359
|
+
try {
|
|
360
|
+
process.kill(Number(lockPid), 0);
|
|
361
|
+
} catch {
|
|
362
|
+
unlinkSync(configuration.daemonLockFile);
|
|
363
|
+
continue;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
} catch {
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
if (attempt === maxAttempts) {
|
|
370
|
+
return null;
|
|
371
|
+
}
|
|
372
|
+
const delayMs = attempt * delayIncrementMs;
|
|
373
|
+
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
return null;
|
|
377
|
+
}
|
|
378
|
+
async function releaseDaemonLock(lockHandle) {
|
|
379
|
+
try {
|
|
380
|
+
await lockHandle.close();
|
|
381
|
+
} catch {
|
|
382
|
+
}
|
|
383
|
+
try {
|
|
384
|
+
if (existsSync(configuration.daemonLockFile)) {
|
|
385
|
+
unlinkSync(configuration.daemonLockFile);
|
|
386
|
+
}
|
|
387
|
+
} catch {
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
46
391
|
function createTimestampForFilename(date = /* @__PURE__ */ new Date()) {
|
|
47
392
|
return date.toLocaleString("sv-SE", {
|
|
48
393
|
timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
@@ -52,7 +397,7 @@ function createTimestampForFilename(date = /* @__PURE__ */ new Date()) {
|
|
|
52
397
|
hour: "2-digit",
|
|
53
398
|
minute: "2-digit",
|
|
54
399
|
second: "2-digit"
|
|
55
|
-
}).replace(/[: ]/g, "-").replace(/,/g, "");
|
|
400
|
+
}).replace(/[: ]/g, "-").replace(/,/g, "") + "-pid-" + process.pid;
|
|
56
401
|
}
|
|
57
402
|
function createTimestampForLogEntry(date = /* @__PURE__ */ new Date()) {
|
|
58
403
|
return date.toLocaleTimeString("en-US", {
|
|
@@ -64,17 +409,14 @@ function createTimestampForLogEntry(date = /* @__PURE__ */ new Date()) {
|
|
|
64
409
|
fractionalSecondDigits: 3
|
|
65
410
|
});
|
|
66
411
|
}
|
|
67
|
-
|
|
68
|
-
if (!existsSync(configuration.logsDir)) {
|
|
69
|
-
await mkdir(configuration.logsDir, { recursive: true });
|
|
70
|
-
}
|
|
412
|
+
function getSessionLogPath() {
|
|
71
413
|
const timestamp = createTimestampForFilename();
|
|
72
414
|
const filename = configuration.isDaemonProcess ? `${timestamp}-daemon.log` : `${timestamp}.log`;
|
|
73
415
|
return join(configuration.logsDir, filename);
|
|
74
416
|
}
|
|
75
417
|
class Logger {
|
|
76
|
-
constructor(
|
|
77
|
-
this.
|
|
418
|
+
constructor(logFilePath = getSessionLogPath()) {
|
|
419
|
+
this.logFilePath = logFilePath;
|
|
78
420
|
if (process.env.DANGEROUSLY_LOG_TO_SERVER_FOR_AI_AUTO_DEBUGGING && process.env.HAPPY_SERVER_URL) {
|
|
79
421
|
this.dangerouslyUnencryptedServerLoggingUrl = process.env.HAPPY_SERVER_URL;
|
|
80
422
|
console.log(chalk.yellow("[REMOTE LOGGING] Sending logs to server for AI debugging"));
|
|
@@ -187,218 +529,246 @@ class Logger {
|
|
|
187
529
|
this.sendToRemoteServer(level, message, ...args).catch(() => {
|
|
188
530
|
});
|
|
189
531
|
}
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
} catch (appendError) {
|
|
194
|
-
if (process.env.DEBUG) {
|
|
195
|
-
console.error("Failed to append to log file:", appendError);
|
|
196
|
-
throw appendError;
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
}).catch((error) => {
|
|
532
|
+
try {
|
|
533
|
+
appendFileSync(this.logFilePath, logLine);
|
|
534
|
+
} catch (appendError) {
|
|
200
535
|
if (process.env.DEBUG) {
|
|
201
|
-
console.
|
|
202
|
-
|
|
203
|
-
console.log(prefix, message, ...args);
|
|
536
|
+
console.error("[DEV MODE ONLY THROWING] Failed to append to log file:", appendError);
|
|
537
|
+
throw appendError;
|
|
204
538
|
}
|
|
205
|
-
}
|
|
539
|
+
}
|
|
206
540
|
}
|
|
207
541
|
}
|
|
208
542
|
let logger = new Logger();
|
|
543
|
+
async function listDaemonLogFiles(limit = 50) {
|
|
544
|
+
try {
|
|
545
|
+
const logsDir = configuration.logsDir;
|
|
546
|
+
if (!existsSync(logsDir)) {
|
|
547
|
+
return [];
|
|
548
|
+
}
|
|
549
|
+
const logs = readdirSync(logsDir).filter((file) => file.endsWith("-daemon.log")).map((file) => {
|
|
550
|
+
const fullPath = join(logsDir, file);
|
|
551
|
+
const stats = statSync(fullPath);
|
|
552
|
+
return { file, path: fullPath, modified: stats.mtime };
|
|
553
|
+
}).sort((a, b) => b.modified.getTime() - a.modified.getTime());
|
|
554
|
+
try {
|
|
555
|
+
const state = await readDaemonState();
|
|
556
|
+
if (!state) {
|
|
557
|
+
return logs;
|
|
558
|
+
}
|
|
559
|
+
if (state.daemonLogPath && existsSync(state.daemonLogPath)) {
|
|
560
|
+
const stats = statSync(state.daemonLogPath);
|
|
561
|
+
const persisted = {
|
|
562
|
+
file: basename(state.daemonLogPath),
|
|
563
|
+
path: state.daemonLogPath,
|
|
564
|
+
modified: stats.mtime
|
|
565
|
+
};
|
|
566
|
+
const idx = logs.findIndex((l) => l.path === persisted.path);
|
|
567
|
+
if (idx >= 0) {
|
|
568
|
+
const [found] = logs.splice(idx, 1);
|
|
569
|
+
logs.unshift(found);
|
|
570
|
+
} else {
|
|
571
|
+
logs.unshift(persisted);
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
} catch {
|
|
575
|
+
}
|
|
576
|
+
return logs.slice(0, Math.max(0, limit));
|
|
577
|
+
} catch {
|
|
578
|
+
return [];
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
async function getLatestDaemonLog() {
|
|
582
|
+
const [latest] = await listDaemonLogFiles(1);
|
|
583
|
+
return latest || null;
|
|
584
|
+
}
|
|
209
585
|
|
|
210
|
-
const SessionMessageContentSchema = z.object({
|
|
211
|
-
c: z.string(),
|
|
586
|
+
const SessionMessageContentSchema = z$1.object({
|
|
587
|
+
c: z$1.string(),
|
|
212
588
|
// Base64 encoded encrypted content
|
|
213
|
-
t: z.literal("encrypted")
|
|
589
|
+
t: z$1.literal("encrypted")
|
|
214
590
|
});
|
|
215
|
-
const UpdateBodySchema = z.object({
|
|
216
|
-
message: z.object({
|
|
217
|
-
id: z.string(),
|
|
218
|
-
seq: z.number(),
|
|
591
|
+
const UpdateBodySchema = z$1.object({
|
|
592
|
+
message: z$1.object({
|
|
593
|
+
id: z$1.string(),
|
|
594
|
+
seq: z$1.number(),
|
|
219
595
|
content: SessionMessageContentSchema
|
|
220
596
|
}),
|
|
221
|
-
sid: z.string(),
|
|
597
|
+
sid: z$1.string(),
|
|
222
598
|
// Session ID
|
|
223
|
-
t: z.literal("new-message")
|
|
599
|
+
t: z$1.literal("new-message")
|
|
224
600
|
});
|
|
225
|
-
const UpdateSessionBodySchema = z.object({
|
|
226
|
-
t: z.literal("update-session"),
|
|
227
|
-
sid: z.string(),
|
|
228
|
-
metadata: z.object({
|
|
229
|
-
version: z.number(),
|
|
230
|
-
value: z.string()
|
|
601
|
+
const UpdateSessionBodySchema = z$1.object({
|
|
602
|
+
t: z$1.literal("update-session"),
|
|
603
|
+
sid: z$1.string(),
|
|
604
|
+
metadata: z$1.object({
|
|
605
|
+
version: z$1.number(),
|
|
606
|
+
value: z$1.string()
|
|
231
607
|
}).nullish(),
|
|
232
|
-
agentState: z.object({
|
|
233
|
-
version: z.number(),
|
|
234
|
-
value: z.string()
|
|
608
|
+
agentState: z$1.object({
|
|
609
|
+
version: z$1.number(),
|
|
610
|
+
value: z$1.string()
|
|
235
611
|
}).nullish()
|
|
236
612
|
});
|
|
237
|
-
const UpdateMachineBodySchema = z.object({
|
|
238
|
-
t: z.literal("update-machine"),
|
|
239
|
-
machineId: z.string(),
|
|
240
|
-
metadata: z.object({
|
|
241
|
-
version: z.number(),
|
|
242
|
-
value: z.string()
|
|
613
|
+
const UpdateMachineBodySchema = z$1.object({
|
|
614
|
+
t: z$1.literal("update-machine"),
|
|
615
|
+
machineId: z$1.string(),
|
|
616
|
+
metadata: z$1.object({
|
|
617
|
+
version: z$1.number(),
|
|
618
|
+
value: z$1.string()
|
|
243
619
|
}).nullish(),
|
|
244
|
-
daemonState: z.object({
|
|
245
|
-
version: z.number(),
|
|
246
|
-
value: z.string()
|
|
620
|
+
daemonState: z$1.object({
|
|
621
|
+
version: z$1.number(),
|
|
622
|
+
value: z$1.string()
|
|
247
623
|
}).nullish()
|
|
248
624
|
});
|
|
249
|
-
z.object({
|
|
250
|
-
id: z.string(),
|
|
251
|
-
seq: z.number(),
|
|
252
|
-
body: z.union([
|
|
625
|
+
z$1.object({
|
|
626
|
+
id: z$1.string(),
|
|
627
|
+
seq: z$1.number(),
|
|
628
|
+
body: z$1.union([
|
|
253
629
|
UpdateBodySchema,
|
|
254
630
|
UpdateSessionBodySchema,
|
|
255
631
|
UpdateMachineBodySchema
|
|
256
632
|
]),
|
|
257
|
-
createdAt: z.number()
|
|
633
|
+
createdAt: z$1.number()
|
|
258
634
|
});
|
|
259
|
-
z.object({
|
|
260
|
-
createdAt: z.number(),
|
|
261
|
-
id: z.string(),
|
|
262
|
-
seq: z.number(),
|
|
263
|
-
updatedAt: z.number(),
|
|
264
|
-
metadata: z.any(),
|
|
265
|
-
metadataVersion: z.number(),
|
|
266
|
-
agentState: z.any().nullable(),
|
|
267
|
-
agentStateVersion: z.number()
|
|
635
|
+
z$1.object({
|
|
636
|
+
createdAt: z$1.number(),
|
|
637
|
+
id: z$1.string(),
|
|
638
|
+
seq: z$1.number(),
|
|
639
|
+
updatedAt: z$1.number(),
|
|
640
|
+
metadata: z$1.any(),
|
|
641
|
+
metadataVersion: z$1.number(),
|
|
642
|
+
agentState: z$1.any().nullable(),
|
|
643
|
+
agentStateVersion: z$1.number(),
|
|
644
|
+
// Connectivity tracking (from server)
|
|
645
|
+
connectivityStatus: z$1.union([
|
|
646
|
+
z$1.enum(["neverConnected", "online", "offline"]),
|
|
647
|
+
z$1.string()
|
|
648
|
+
// Forward compatibility
|
|
649
|
+
]).optional(),
|
|
650
|
+
connectivityStatusSince: z$1.number().optional(),
|
|
651
|
+
connectivityStatusReason: z$1.string().optional(),
|
|
652
|
+
// State tracking (from server)
|
|
653
|
+
state: z$1.union([
|
|
654
|
+
z$1.enum(["running", "archiveRequested", "archived"]),
|
|
655
|
+
z$1.string()
|
|
656
|
+
// Forward compatibility
|
|
657
|
+
]).optional(),
|
|
658
|
+
stateSince: z$1.number().optional(),
|
|
659
|
+
stateReason: z$1.string().optional()
|
|
268
660
|
});
|
|
269
|
-
z.object({
|
|
270
|
-
host: z.string(),
|
|
271
|
-
platform: z.string(),
|
|
272
|
-
happyCliVersion: z.string(),
|
|
273
|
-
homeDir: z.string(),
|
|
274
|
-
happyHomeDir: z.string()
|
|
661
|
+
z$1.object({
|
|
662
|
+
host: z$1.string(),
|
|
663
|
+
platform: z$1.string(),
|
|
664
|
+
happyCliVersion: z$1.string(),
|
|
665
|
+
homeDir: z$1.string(),
|
|
666
|
+
happyHomeDir: z$1.string()
|
|
275
667
|
});
|
|
276
|
-
z.object({
|
|
277
|
-
status: z.union([
|
|
278
|
-
z.enum(["running", "shutting-down"]),
|
|
279
|
-
z.string()
|
|
668
|
+
z$1.object({
|
|
669
|
+
status: z$1.union([
|
|
670
|
+
z$1.enum(["running", "shutting-down"]),
|
|
671
|
+
z$1.string()
|
|
280
672
|
// Forward compatibility
|
|
281
673
|
]),
|
|
282
|
-
pid: z.number().optional(),
|
|
283
|
-
httpPort: z.number().optional(),
|
|
284
|
-
startedAt: z.number().optional(),
|
|
285
|
-
shutdownRequestedAt: z.number().optional(),
|
|
286
|
-
shutdownSource: z.union([
|
|
287
|
-
z.enum(["mobile-app", "cli", "os-signal", "unknown"]),
|
|
288
|
-
z.string()
|
|
674
|
+
pid: z$1.number().optional(),
|
|
675
|
+
httpPort: z$1.number().optional(),
|
|
676
|
+
startedAt: z$1.number().optional(),
|
|
677
|
+
shutdownRequestedAt: z$1.number().optional(),
|
|
678
|
+
shutdownSource: z$1.union([
|
|
679
|
+
z$1.enum(["mobile-app", "cli", "os-signal", "unknown"]),
|
|
680
|
+
z$1.string()
|
|
289
681
|
// Forward compatibility
|
|
290
682
|
]).optional()
|
|
291
683
|
});
|
|
292
|
-
z.object({
|
|
293
|
-
id: z.string(),
|
|
294
|
-
metadata: z.any(),
|
|
684
|
+
z$1.object({
|
|
685
|
+
id: z$1.string(),
|
|
686
|
+
metadata: z$1.any(),
|
|
295
687
|
// Decrypted MachineMetadata
|
|
296
|
-
metadataVersion: z.number(),
|
|
297
|
-
daemonState: z.any().nullable(),
|
|
688
|
+
metadataVersion: z$1.number(),
|
|
689
|
+
daemonState: z$1.any().nullable(),
|
|
298
690
|
// Decrypted DaemonState
|
|
299
|
-
daemonStateVersion: z.number(),
|
|
691
|
+
daemonStateVersion: z$1.number(),
|
|
300
692
|
// We don't really care about these on the CLI for now
|
|
301
693
|
// ApiMachineClient will not sync these
|
|
302
|
-
active: z.boolean(),
|
|
303
|
-
activeAt: z.number(),
|
|
304
|
-
createdAt: z.number(),
|
|
305
|
-
updatedAt: z.number()
|
|
694
|
+
active: z$1.boolean(),
|
|
695
|
+
activeAt: z$1.number(),
|
|
696
|
+
createdAt: z$1.number(),
|
|
697
|
+
updatedAt: z$1.number(),
|
|
698
|
+
// Connectivity tracking (from server)
|
|
699
|
+
connectivityStatus: z$1.union([
|
|
700
|
+
z$1.enum(["neverConnected", "online", "offline"]),
|
|
701
|
+
z$1.string()
|
|
702
|
+
// Forward compatibility
|
|
703
|
+
]).optional(),
|
|
704
|
+
connectivityStatusSince: z$1.number().optional(),
|
|
705
|
+
connectivityStatusReason: z$1.string().optional(),
|
|
706
|
+
// State tracking (from server)
|
|
707
|
+
state: z$1.union([
|
|
708
|
+
z$1.enum(["running", "archiveRequested", "archived"]),
|
|
709
|
+
z$1.string()
|
|
710
|
+
// Forward compatibility
|
|
711
|
+
]).optional(),
|
|
712
|
+
stateSince: z$1.number().optional(),
|
|
713
|
+
stateReason: z$1.string().optional()
|
|
306
714
|
});
|
|
307
|
-
z.object({
|
|
715
|
+
z$1.object({
|
|
308
716
|
content: SessionMessageContentSchema,
|
|
309
|
-
createdAt: z.number(),
|
|
310
|
-
id: z.string(),
|
|
311
|
-
seq: z.number(),
|
|
312
|
-
updatedAt: z.number()
|
|
717
|
+
createdAt: z$1.number(),
|
|
718
|
+
id: z$1.string(),
|
|
719
|
+
seq: z$1.number(),
|
|
720
|
+
updatedAt: z$1.number()
|
|
313
721
|
});
|
|
314
|
-
const MessageMetaSchema = z.object({
|
|
315
|
-
sentFrom: z.string().optional(),
|
|
722
|
+
const MessageMetaSchema = z$1.object({
|
|
723
|
+
sentFrom: z$1.string().optional(),
|
|
316
724
|
// Source identifier
|
|
317
|
-
permissionMode: z.string().optional(),
|
|
725
|
+
permissionMode: z$1.string().optional(),
|
|
318
726
|
// Permission mode for this message
|
|
319
|
-
model: z.string().nullable().optional(),
|
|
727
|
+
model: z$1.string().nullable().optional(),
|
|
320
728
|
// Model name for this message (null = reset)
|
|
321
|
-
fallbackModel: z.string().nullable().optional(),
|
|
729
|
+
fallbackModel: z$1.string().nullable().optional(),
|
|
322
730
|
// Fallback model for this message (null = reset)
|
|
323
|
-
customSystemPrompt: z.string().nullable().optional(),
|
|
731
|
+
customSystemPrompt: z$1.string().nullable().optional(),
|
|
324
732
|
// Custom system prompt for this message (null = reset)
|
|
325
|
-
appendSystemPrompt: z.string().nullable().optional(),
|
|
733
|
+
appendSystemPrompt: z$1.string().nullable().optional(),
|
|
326
734
|
// Append to system prompt for this message (null = reset)
|
|
327
|
-
allowedTools: z.array(z.string()).nullable().optional(),
|
|
735
|
+
allowedTools: z$1.array(z$1.string()).nullable().optional(),
|
|
328
736
|
// Allowed tools for this message (null = reset)
|
|
329
|
-
disallowedTools: z.array(z.string()).nullable().optional()
|
|
737
|
+
disallowedTools: z$1.array(z$1.string()).nullable().optional()
|
|
330
738
|
// Disallowed tools for this message (null = reset)
|
|
331
739
|
});
|
|
332
|
-
z.object({
|
|
333
|
-
session: z.object({
|
|
334
|
-
id: z.string(),
|
|
335
|
-
tag: z.string(),
|
|
336
|
-
seq: z.number(),
|
|
337
|
-
createdAt: z.number(),
|
|
338
|
-
updatedAt: z.number(),
|
|
339
|
-
metadata: z.string(),
|
|
340
|
-
metadataVersion: z.number(),
|
|
341
|
-
agentState: z.string().nullable(),
|
|
342
|
-
agentStateVersion: z.number()
|
|
740
|
+
z$1.object({
|
|
741
|
+
session: z$1.object({
|
|
742
|
+
id: z$1.string(),
|
|
743
|
+
tag: z$1.string(),
|
|
744
|
+
seq: z$1.number(),
|
|
745
|
+
createdAt: z$1.number(),
|
|
746
|
+
updatedAt: z$1.number(),
|
|
747
|
+
metadata: z$1.string(),
|
|
748
|
+
metadataVersion: z$1.number(),
|
|
749
|
+
agentState: z$1.string().nullable(),
|
|
750
|
+
agentStateVersion: z$1.number()
|
|
343
751
|
})
|
|
344
752
|
});
|
|
345
|
-
const UserMessageSchema = z.object({
|
|
346
|
-
role: z.literal("user"),
|
|
347
|
-
content: z.object({
|
|
348
|
-
type: z.literal("text"),
|
|
349
|
-
text: z.string()
|
|
753
|
+
const UserMessageSchema = z$1.object({
|
|
754
|
+
role: z$1.literal("user"),
|
|
755
|
+
content: z$1.object({
|
|
756
|
+
type: z$1.literal("text"),
|
|
757
|
+
text: z$1.string()
|
|
350
758
|
}),
|
|
351
|
-
localKey: z.string().optional(),
|
|
759
|
+
localKey: z$1.string().optional(),
|
|
352
760
|
// Mobile messages include this
|
|
353
761
|
meta: MessageMetaSchema.optional()
|
|
354
762
|
});
|
|
355
|
-
const AgentMessageSchema = z.object({
|
|
356
|
-
role: z.literal("agent"),
|
|
357
|
-
content: z.object({
|
|
358
|
-
type: z.literal("output"),
|
|
359
|
-
data: z.any()
|
|
763
|
+
const AgentMessageSchema = z$1.object({
|
|
764
|
+
role: z$1.literal("agent"),
|
|
765
|
+
content: z$1.object({
|
|
766
|
+
type: z$1.literal("output"),
|
|
767
|
+
data: z$1.any()
|
|
360
768
|
}),
|
|
361
769
|
meta: MessageMetaSchema.optional()
|
|
362
770
|
});
|
|
363
|
-
z.union([UserMessageSchema, AgentMessageSchema]);
|
|
364
|
-
|
|
365
|
-
function encodeBase64(buffer, variant = "base64") {
|
|
366
|
-
if (variant === "base64url") {
|
|
367
|
-
return encodeBase64Url(buffer);
|
|
368
|
-
}
|
|
369
|
-
return Buffer.from(buffer).toString("base64");
|
|
370
|
-
}
|
|
371
|
-
function encodeBase64Url(buffer) {
|
|
372
|
-
return Buffer.from(buffer).toString("base64").replaceAll("+", "-").replaceAll("/", "_").replaceAll("=", "");
|
|
373
|
-
}
|
|
374
|
-
function decodeBase64(base64, variant = "base64") {
|
|
375
|
-
if (variant === "base64url") {
|
|
376
|
-
const base64Standard = base64.replaceAll("-", "+").replaceAll("_", "/") + "=".repeat((4 - base64.length % 4) % 4);
|
|
377
|
-
return new Uint8Array(Buffer.from(base64Standard, "base64"));
|
|
378
|
-
}
|
|
379
|
-
return new Uint8Array(Buffer.from(base64, "base64"));
|
|
380
|
-
}
|
|
381
|
-
function getRandomBytes(size) {
|
|
382
|
-
return new Uint8Array(randomBytes(size));
|
|
383
|
-
}
|
|
384
|
-
function encrypt(data, secret) {
|
|
385
|
-
const nonce = getRandomBytes(tweetnacl.secretbox.nonceLength);
|
|
386
|
-
const encrypted = tweetnacl.secretbox(new TextEncoder().encode(JSON.stringify(data)), nonce, secret);
|
|
387
|
-
const result = new Uint8Array(nonce.length + encrypted.length);
|
|
388
|
-
result.set(nonce);
|
|
389
|
-
result.set(encrypted, nonce.length);
|
|
390
|
-
return result;
|
|
391
|
-
}
|
|
392
|
-
function decrypt(data, secret) {
|
|
393
|
-
const nonce = data.slice(0, tweetnacl.secretbox.nonceLength);
|
|
394
|
-
const encrypted = data.slice(tweetnacl.secretbox.nonceLength);
|
|
395
|
-
const decrypted = tweetnacl.secretbox.open(encrypted, nonce, secret);
|
|
396
|
-
if (!decrypted) {
|
|
397
|
-
logger.debug("[ERROR] Decryption failed");
|
|
398
|
-
return null;
|
|
399
|
-
}
|
|
400
|
-
return JSON.parse(new TextDecoder().decode(decrypted));
|
|
401
|
-
}
|
|
771
|
+
z$1.union([UserMessageSchema, AgentMessageSchema]);
|
|
402
772
|
|
|
403
773
|
async function delay(ms) {
|
|
404
774
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
@@ -653,6 +1023,9 @@ class ApiSessionClient extends EventEmitter {
|
|
|
653
1023
|
* Send a ping message to keep the connection alive
|
|
654
1024
|
*/
|
|
655
1025
|
keepAlive(thinking, mode) {
|
|
1026
|
+
if (process.env.DEBUG) {
|
|
1027
|
+
logger.debug(`[API] Sending keep alive message: ${thinking}`);
|
|
1028
|
+
}
|
|
656
1029
|
this.socket.volatile.emit("session-alive", {
|
|
657
1030
|
sid: this.sessionId,
|
|
658
1031
|
time: Date.now(),
|
|
@@ -905,11 +1278,17 @@ class ApiMachineClient {
|
|
|
905
1278
|
if (!session) {
|
|
906
1279
|
throw new Error("Failed to spawn session");
|
|
907
1280
|
}
|
|
908
|
-
|
|
1281
|
+
if (session.error) {
|
|
1282
|
+
throw new Error(session.error);
|
|
1283
|
+
}
|
|
1284
|
+
logger.debug(`[API MACHINE] Spawned session ${session.happySessionId || "WARNING - not session Id recieved in webhook"} with PID ${session.pid}`);
|
|
909
1285
|
if (!session.happySessionId) {
|
|
910
1286
|
throw new Error(`Session spawned (PID ${session.pid}) but no sessionId received from webhook. The session process may still be initializing.`);
|
|
911
1287
|
}
|
|
912
|
-
const response = {
|
|
1288
|
+
const response = {
|
|
1289
|
+
sessionId: session.happySessionId,
|
|
1290
|
+
message: session.message
|
|
1291
|
+
};
|
|
913
1292
|
logger.debug(`[API MACHINE] Sending RPC response:`, response);
|
|
914
1293
|
callback(encodeBase64(encrypt(response, this.secret)));
|
|
915
1294
|
return;
|
|
@@ -995,7 +1374,7 @@ class ApiMachineClient {
|
|
|
995
1374
|
machineId: this.machine.id,
|
|
996
1375
|
time: Date.now()
|
|
997
1376
|
};
|
|
998
|
-
if (process.env.
|
|
1377
|
+
if (process.env.DEBUG) {
|
|
999
1378
|
logger.debugLargeJson(`[API MACHINE] Emitting machine-alive`, payload);
|
|
1000
1379
|
}
|
|
1001
1380
|
this.socket.emit("machine-alive", payload);
|
|
@@ -1023,7 +1402,7 @@ class PushNotificationClient {
|
|
|
1023
1402
|
token;
|
|
1024
1403
|
baseUrl;
|
|
1025
1404
|
expo;
|
|
1026
|
-
constructor(token, baseUrl = "https://
|
|
1405
|
+
constructor(token, baseUrl = "https://api.cluster-fluster.com") {
|
|
1027
1406
|
this.token = token;
|
|
1028
1407
|
this.baseUrl = baseUrl;
|
|
1029
1408
|
this.expo = new Expo();
|
|
@@ -1243,6 +1622,11 @@ class ApiClient {
|
|
|
1243
1622
|
timeout: 5e3
|
|
1244
1623
|
}
|
|
1245
1624
|
);
|
|
1625
|
+
if (response.status !== 200) {
|
|
1626
|
+
console.error(chalk.red(`[API] Failed to create machine: ${response.statusText}`));
|
|
1627
|
+
console.log(chalk.yellow(`[API] Failed to create machine: ${response.statusText}, most likely you have re-authenticated, but you still have a machine associated with the old account. Now we are trying to re-associate the machine with the new account. That is not allowed. Please run 'happy doctor clean' to clean up your happy state, and try your original command again. Please create an issue on github if this is causing you problems. We apologize for the inconvenience.`));
|
|
1628
|
+
process.exit(1);
|
|
1629
|
+
}
|
|
1246
1630
|
const raw = response.data.machine;
|
|
1247
1631
|
logger.debug(`[API] Machine ${opts.machineId} registered/updated with server`);
|
|
1248
1632
|
const machine = {
|
|
@@ -1269,52 +1653,52 @@ class ApiClient {
|
|
|
1269
1653
|
}
|
|
1270
1654
|
}
|
|
1271
1655
|
|
|
1272
|
-
const UsageSchema = z.object({
|
|
1273
|
-
input_tokens: z.number().int().nonnegative(),
|
|
1274
|
-
cache_creation_input_tokens: z.number().int().nonnegative().optional(),
|
|
1275
|
-
cache_read_input_tokens: z.number().int().nonnegative().optional(),
|
|
1276
|
-
output_tokens: z.number().int().nonnegative(),
|
|
1277
|
-
service_tier: z.string().optional()
|
|
1656
|
+
const UsageSchema = z$1.object({
|
|
1657
|
+
input_tokens: z$1.number().int().nonnegative(),
|
|
1658
|
+
cache_creation_input_tokens: z$1.number().int().nonnegative().optional(),
|
|
1659
|
+
cache_read_input_tokens: z$1.number().int().nonnegative().optional(),
|
|
1660
|
+
output_tokens: z$1.number().int().nonnegative(),
|
|
1661
|
+
service_tier: z$1.string().optional()
|
|
1278
1662
|
}).passthrough();
|
|
1279
|
-
const RawJSONLinesSchema = z.discriminatedUnion("type", [
|
|
1663
|
+
const RawJSONLinesSchema = z$1.discriminatedUnion("type", [
|
|
1280
1664
|
// User message - validates uuid and message.content
|
|
1281
|
-
z.object({
|
|
1282
|
-
type: z.literal("user"),
|
|
1283
|
-
isSidechain: z.boolean().optional(),
|
|
1284
|
-
isMeta: z.boolean().optional(),
|
|
1285
|
-
uuid: z.string(),
|
|
1665
|
+
z$1.object({
|
|
1666
|
+
type: z$1.literal("user"),
|
|
1667
|
+
isSidechain: z$1.boolean().optional(),
|
|
1668
|
+
isMeta: z$1.boolean().optional(),
|
|
1669
|
+
uuid: z$1.string(),
|
|
1286
1670
|
// Used in getMessageKey()
|
|
1287
|
-
message: z.object({
|
|
1288
|
-
content: z.union([z.string(), z.any()])
|
|
1671
|
+
message: z$1.object({
|
|
1672
|
+
content: z$1.union([z$1.string(), z$1.any()])
|
|
1289
1673
|
// Used in sessionScanner.ts
|
|
1290
1674
|
}).passthrough()
|
|
1291
1675
|
}).passthrough(),
|
|
1292
1676
|
// Assistant message - validates message object with usage and content
|
|
1293
|
-
z.object({
|
|
1294
|
-
uuid: z.string(),
|
|
1295
|
-
type: z.literal("assistant"),
|
|
1296
|
-
message: z.object({
|
|
1677
|
+
z$1.object({
|
|
1678
|
+
uuid: z$1.string(),
|
|
1679
|
+
type: z$1.literal("assistant"),
|
|
1680
|
+
message: z$1.object({
|
|
1297
1681
|
// Entire message used in getMessageKey()
|
|
1298
1682
|
usage: UsageSchema.optional(),
|
|
1299
1683
|
// Used in apiSession.ts
|
|
1300
|
-
content: z.any()
|
|
1684
|
+
content: z$1.any()
|
|
1301
1685
|
// Used in tests
|
|
1302
1686
|
}).passthrough()
|
|
1303
1687
|
}).passthrough(),
|
|
1304
1688
|
// Summary message - validates summary and leafUuid
|
|
1305
|
-
z.object({
|
|
1306
|
-
type: z.literal("summary"),
|
|
1307
|
-
summary: z.string(),
|
|
1689
|
+
z$1.object({
|
|
1690
|
+
type: z$1.literal("summary"),
|
|
1691
|
+
summary: z$1.string(),
|
|
1308
1692
|
// Used in apiSession.ts
|
|
1309
|
-
leafUuid: z.string()
|
|
1693
|
+
leafUuid: z$1.string()
|
|
1310
1694
|
// Used in getMessageKey()
|
|
1311
1695
|
}).passthrough(),
|
|
1312
1696
|
// System message - validates uuid
|
|
1313
|
-
z.object({
|
|
1314
|
-
type: z.literal("system"),
|
|
1315
|
-
uuid: z.string()
|
|
1697
|
+
z$1.object({
|
|
1698
|
+
type: z$1.literal("system"),
|
|
1699
|
+
uuid: z$1.string()
|
|
1316
1700
|
// Used in getMessageKey()
|
|
1317
1701
|
}).passthrough()
|
|
1318
1702
|
]);
|
|
1319
1703
|
|
|
1320
|
-
export { ApiClient as A, RawJSONLinesSchema as R, ApiSessionClient as a, backoff as b, configuration as c, delay as d, AsyncLock as e,
|
|
1704
|
+
export { ApiClient as A, RawJSONLinesSchema as R, ApiSessionClient as a, backoff as b, configuration as c, delay as d, AsyncLock as e, clearDaemonState as f, readSettings as g, readCredentials as h, encodeBase64 as i, encodeBase64Url as j, decodeBase64 as k, logger as l, acquireDaemonLock as m, writeDaemonState as n, releaseDaemonLock as o, packageJson as p, clearCredentials as q, readDaemonState as r, clearMachineId as s, getLatestDaemonLog as t, updateSettings as u, writeCredentials as w };
|