happy-coder 0.3.1-beta.1 → 0.4.0
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 +76 -27
- package/dist/index.mjs +76 -27
- package/dist/lib.cjs +1 -1
- package/dist/lib.d.cts +1 -0
- package/dist/lib.d.mts +1 -0
- package/dist/lib.mjs +1 -1
- package/dist/{types-CzYKFAYa.mjs → types-VkaGP8up.mjs} +1 -1
- package/dist/{types-D7u2DxfV.cjs → types-eN-YHsuj.cjs} +1 -0
- package/package.json +8 -6
- package/dist/index-B2GqfEZV.cjs +0 -1564
- package/dist/index-QItBXhux.mjs +0 -1540
- package/dist/install-B0DnBGS_.mjs +0 -29
- package/dist/install-B2r_gX72.cjs +0 -109
- package/dist/install-C809w0Cj.cjs +0 -31
- package/dist/install-DEPy62QN.mjs +0 -97
- package/dist/install-GZIzyuIE.cjs +0 -99
- package/dist/install-HKe7dyS4.mjs +0 -107
- package/dist/run-BmEaINbl.cjs +0 -250
- package/dist/run-DMbKhYfb.mjs +0 -247
- package/dist/run-FBXkmmN7.mjs +0 -32
- package/dist/run-q2To6b-c.cjs +0 -34
- package/dist/types-BG9AgCI4.mjs +0 -875
- package/dist/types-BRICSarm.mjs +0 -870
- package/dist/types-BTQRfIr3.cjs +0 -892
- package/dist/types-BX4xv8Ty.mjs +0 -881
- package/dist/types-BeUppqJU.cjs +0 -886
- package/dist/types-C6Wx_bRW.cjs +0 -886
- package/dist/types-CEvzGLMI.cjs +0 -882
- package/dist/types-CKUdOV6c.mjs +0 -875
- package/dist/types-CNuBtNA5.cjs +0 -884
- package/dist/types-Cg4664gs.cjs +0 -879
- package/dist/types-CkPUFpfr.cjs +0 -885
- package/dist/types-D39L8JSd.mjs +0 -850
- package/dist/types-DD9P_5rj.mjs +0 -868
- package/dist/types-DNu8okOb.mjs +0 -874
- package/dist/types-DXK5YldG.cjs +0 -892
- package/dist/types-DYBiuNUQ.cjs +0 -883
- package/dist/types-Df5dlWLV.mjs +0 -871
- package/dist/types-fXgEaaqP.mjs +0 -861
- package/dist/types-hotUTaWz.cjs +0 -863
- package/dist/types-ikrrEcJm.mjs +0 -873
- package/dist/types-mykDX2xe.cjs +0 -872
- package/dist/types-tLWMaptR.mjs +0 -879
- package/dist/uninstall-BGgl5V8F.mjs +0 -29
- package/dist/uninstall-BWHglipH.mjs +0 -40
- package/dist/uninstall-C42CoSCI.cjs +0 -53
- package/dist/uninstall-CLkTtlMv.mjs +0 -51
- package/dist/uninstall-CdHMb6wi.cjs +0 -31
- package/dist/uninstall-FXyyAuGU.cjs +0 -42
package/dist/index.cjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var chalk = require('chalk');
|
|
4
|
-
var types$1 = require('./types-
|
|
4
|
+
var types$1 = require('./types-eN-YHsuj.cjs');
|
|
5
5
|
var node_crypto = require('node:crypto');
|
|
6
6
|
var node_child_process = require('node:child_process');
|
|
7
7
|
var node_path = require('node:path');
|
|
@@ -2248,7 +2248,7 @@ async function loop(opts) {
|
|
|
2248
2248
|
}
|
|
2249
2249
|
|
|
2250
2250
|
var name = "happy-coder";
|
|
2251
|
-
var version = "0.
|
|
2251
|
+
var version = "0.4.0";
|
|
2252
2252
|
var description = "Claude Code session sharing CLI";
|
|
2253
2253
|
var author = "Kirill Dubovitskiy";
|
|
2254
2254
|
var license = "MIT";
|
|
@@ -2294,15 +2294,16 @@ var files = [
|
|
|
2294
2294
|
var scripts = {
|
|
2295
2295
|
test: "vitest run",
|
|
2296
2296
|
"test:watch": "vitest",
|
|
2297
|
-
build: "tsc --noEmit && pkgroll",
|
|
2297
|
+
build: "shx rm -rf dist && tsc --noEmit && pkgroll",
|
|
2298
2298
|
prepublishOnly: "yarn build && yarn test",
|
|
2299
2299
|
typecheck: "tsc --noEmit",
|
|
2300
|
-
dev: "npx tsx --env-file .env.sample src/index.ts",
|
|
2301
|
-
"dev:local-server": "HANDY_SERVER_URL=http://localhost:3005
|
|
2302
|
-
prerelease: "npm version prerelease --preid=beta"
|
|
2300
|
+
dev: "yarn build && npx tsx --env-file .env.sample src/index.ts",
|
|
2301
|
+
"dev:local-server": "yarn build && cross-env HANDY_SERVER_URL=http://localhost:3005 tsx --env-file .env.sample src/index.ts",
|
|
2302
|
+
"version:prerelease": "npm version prerelease --preid=beta",
|
|
2303
|
+
"publish:prerelease": "npm publish --tag beta"
|
|
2303
2304
|
};
|
|
2304
2305
|
var dependencies = {
|
|
2305
|
-
"@anthropic-ai/claude-code": "^1.0.
|
|
2306
|
+
"@anthropic-ai/claude-code": "^1.0.73",
|
|
2306
2307
|
"@anthropic-ai/sdk": "^0.56.0",
|
|
2307
2308
|
"@modelcontextprotocol/sdk": "^1.15.1",
|
|
2308
2309
|
"@stablelib/base64": "^2.0.1",
|
|
@@ -2325,6 +2326,7 @@ var dependencies = {
|
|
|
2325
2326
|
var devDependencies = {
|
|
2326
2327
|
"@eslint/compat": "^1",
|
|
2327
2328
|
"@types/node": ">=18",
|
|
2329
|
+
"cross-env": "^10.0.0",
|
|
2328
2330
|
eslint: "^9",
|
|
2329
2331
|
"eslint-config-prettier": "^10",
|
|
2330
2332
|
pkgroll: "^2.14.2",
|
|
@@ -2960,7 +2962,8 @@ async function start(credentials, options = {}) {
|
|
|
2960
2962
|
host: os.hostname(),
|
|
2961
2963
|
version: packageJson.version,
|
|
2962
2964
|
os: os.platform(),
|
|
2963
|
-
machineId: settings.machineId
|
|
2965
|
+
machineId: settings.machineId,
|
|
2966
|
+
homeDir: os.homedir()
|
|
2964
2967
|
};
|
|
2965
2968
|
const response = await api.getOrCreateSession({ tag: sessionTag, metadata, state });
|
|
2966
2969
|
types$1.logger.debug(`Session created: ${response.id}`);
|
|
@@ -3114,6 +3117,7 @@ class ApiDaemonSession extends node_events.EventEmitter {
|
|
|
3114
3117
|
token;
|
|
3115
3118
|
secret;
|
|
3116
3119
|
spawnedProcesses = /* @__PURE__ */ new Set();
|
|
3120
|
+
machineRegistered = false;
|
|
3117
3121
|
constructor(token, secret, machineIdentity) {
|
|
3118
3122
|
super();
|
|
3119
3123
|
this.token = token;
|
|
@@ -3135,9 +3139,12 @@ class ApiDaemonSession extends node_events.EventEmitter {
|
|
|
3135
3139
|
withCredentials: true,
|
|
3136
3140
|
autoConnect: false
|
|
3137
3141
|
});
|
|
3138
|
-
socket.on("connect", () => {
|
|
3142
|
+
socket.on("connect", async () => {
|
|
3139
3143
|
types$1.logger.debug("[DAEMON SESSION] Socket connected");
|
|
3140
3144
|
types$1.logger.debug(`[DAEMON SESSION] Connected with auth - token: ${this.token.substring(0, 10)}..., machineId: ${this.machineIdentity.machineId}`);
|
|
3145
|
+
if (!this.machineRegistered) {
|
|
3146
|
+
await this.registerMachine();
|
|
3147
|
+
}
|
|
3141
3148
|
const rpcMethod = `${this.machineIdentity.machineId}:spawn-happy-session`;
|
|
3142
3149
|
socket.emit("rpc-register", { method: rpcMethod });
|
|
3143
3150
|
types$1.logger.debug(`[DAEMON SESSION] Emitted RPC registration: ${rpcMethod}`);
|
|
@@ -3164,16 +3171,11 @@ class ApiDaemonSession extends node_events.EventEmitter {
|
|
|
3164
3171
|
args.push("--local");
|
|
3165
3172
|
}
|
|
3166
3173
|
types$1.logger.debug(`[DAEMON SESSION] Spawning happy in directory: ${directory} with args: ${args.join(" ")}`);
|
|
3167
|
-
const
|
|
3168
|
-
|
|
3169
|
-
|
|
3170
|
-
|
|
3171
|
-
|
|
3172
|
-
spawnArgs = args;
|
|
3173
|
-
} else {
|
|
3174
|
-
executable = "npx";
|
|
3175
|
-
spawnArgs = ["tsx", happyPath, ...args];
|
|
3176
|
-
}
|
|
3174
|
+
const happyBinPath = path.join(projectPath(), "bin", "happy.mjs");
|
|
3175
|
+
types$1.logger.debug(`[DAEMON SESSION] Using happy binary at: ${happyBinPath}`);
|
|
3176
|
+
const executable = happyBinPath;
|
|
3177
|
+
const spawnArgs = args;
|
|
3178
|
+
types$1.logger.debug(`[DAEMON SESSION] Spawn: executable=${executable}, args=${JSON.stringify(spawnArgs)}, cwd=${directory}`);
|
|
3177
3179
|
const happyProcess = child_process.spawn(executable, spawnArgs, {
|
|
3178
3180
|
cwd: directory,
|
|
3179
3181
|
detached: true,
|
|
@@ -3266,7 +3268,7 @@ class ApiDaemonSession extends node_events.EventEmitter {
|
|
|
3266
3268
|
types$1.logger.debug(`[DAEMON SESSION] RPC error: ${JSON.stringify(data)}`);
|
|
3267
3269
|
});
|
|
3268
3270
|
socket.onAny((event, ...args) => {
|
|
3269
|
-
if (!event.startsWith("
|
|
3271
|
+
if (!event.startsWith("session-alive") && event !== "ephemeral") {
|
|
3270
3272
|
types$1.logger.debug(`[DAEMON SESSION] Socket event: ${event}, args: ${JSON.stringify(args)}`);
|
|
3271
3273
|
}
|
|
3272
3274
|
});
|
|
@@ -3289,12 +3291,47 @@ class ApiDaemonSession extends node_events.EventEmitter {
|
|
|
3289
3291
|
});
|
|
3290
3292
|
this.socket = socket;
|
|
3291
3293
|
}
|
|
3294
|
+
async registerMachine() {
|
|
3295
|
+
try {
|
|
3296
|
+
const metadata = {
|
|
3297
|
+
host: this.machineIdentity.machineHost,
|
|
3298
|
+
platform: this.machineIdentity.platform,
|
|
3299
|
+
happyCliVersion: this.machineIdentity.happyCliVersion,
|
|
3300
|
+
happyHomeDirectory: this.machineIdentity.happyHomeDirectory
|
|
3301
|
+
};
|
|
3302
|
+
const encrypted = types$1.encrypt(JSON.stringify(metadata), this.secret);
|
|
3303
|
+
const encryptedMetadata = types$1.encodeBase64(encrypted);
|
|
3304
|
+
const response = await fetch(`${types$1.configuration.serverUrl}/v1/machines`, {
|
|
3305
|
+
method: "POST",
|
|
3306
|
+
headers: {
|
|
3307
|
+
"Authorization": `Bearer ${this.token}`,
|
|
3308
|
+
"Content-Type": "application/json"
|
|
3309
|
+
},
|
|
3310
|
+
body: JSON.stringify({
|
|
3311
|
+
id: this.machineIdentity.machineId,
|
|
3312
|
+
metadata: encryptedMetadata
|
|
3313
|
+
})
|
|
3314
|
+
});
|
|
3315
|
+
if (response.ok) {
|
|
3316
|
+
types$1.logger.debug("[DAEMON SESSION] Machine registered/updated successfully");
|
|
3317
|
+
this.machineRegistered = true;
|
|
3318
|
+
} else {
|
|
3319
|
+
types$1.logger.debug(`[DAEMON SESSION] Failed to register machine: ${response.status}`);
|
|
3320
|
+
}
|
|
3321
|
+
} catch (error) {
|
|
3322
|
+
types$1.logger.debug("[DAEMON SESSION] Failed to register machine:", error);
|
|
3323
|
+
}
|
|
3324
|
+
}
|
|
3292
3325
|
startKeepAlive() {
|
|
3293
3326
|
this.stopKeepAlive();
|
|
3294
3327
|
this.keepAliveInterval = setInterval(() => {
|
|
3295
|
-
|
|
3328
|
+
const payload = {
|
|
3329
|
+
type: "machine-scoped",
|
|
3330
|
+
machineId: this.machineIdentity.machineId,
|
|
3296
3331
|
time: Date.now()
|
|
3297
|
-
}
|
|
3332
|
+
};
|
|
3333
|
+
types$1.logger.debugLargeJson(`[DAEMON SESSION] Emitting session-alive`, payload);
|
|
3334
|
+
this.socket.emit("session-alive", payload);
|
|
3298
3335
|
}, 2e4);
|
|
3299
3336
|
}
|
|
3300
3337
|
stopKeepAlive() {
|
|
@@ -3319,15 +3356,26 @@ class ApiDaemonSession extends node_events.EventEmitter {
|
|
|
3319
3356
|
connect() {
|
|
3320
3357
|
this.socket.connect();
|
|
3321
3358
|
}
|
|
3359
|
+
updateMetadata(updates) {
|
|
3360
|
+
const metadata = {
|
|
3361
|
+
host: this.machineIdentity.machineHost,
|
|
3362
|
+
platform: this.machineIdentity.platform,
|
|
3363
|
+
happyCliVersion: updates.happyCliVersion || this.machineIdentity.happyCliVersion,
|
|
3364
|
+
happyHomeDirectory: updates.happyHomeDirectory || this.machineIdentity.happyHomeDirectory
|
|
3365
|
+
};
|
|
3366
|
+
const encrypted = types$1.encrypt(JSON.stringify(metadata), this.secret);
|
|
3367
|
+
const encryptedMetadata = types$1.encodeBase64(encrypted);
|
|
3368
|
+
this.socket.emit("update-machine-metadata", { metadata: encryptedMetadata });
|
|
3369
|
+
}
|
|
3322
3370
|
shutdown() {
|
|
3323
3371
|
types$1.logger.debug(`[DAEMON SESSION] Shutting down daemon, killing ${this.spawnedProcesses.size} spawned processes`);
|
|
3324
|
-
for (const
|
|
3372
|
+
for (const process of this.spawnedProcesses) {
|
|
3325
3373
|
try {
|
|
3326
|
-
types$1.logger.debug(`[DAEMON SESSION] Killing spawned process with PID: ${
|
|
3327
|
-
|
|
3374
|
+
types$1.logger.debug(`[DAEMON SESSION] Killing spawned process with PID: ${process.pid}`);
|
|
3375
|
+
process.kill("SIGTERM");
|
|
3328
3376
|
setTimeout(() => {
|
|
3329
3377
|
try {
|
|
3330
|
-
|
|
3378
|
+
process.kill("SIGKILL");
|
|
3331
3379
|
} catch (e) {
|
|
3332
3380
|
}
|
|
3333
3381
|
}, 1e3);
|
|
@@ -3403,7 +3451,8 @@ async function startDaemon() {
|
|
|
3403
3451
|
machineId: settings.machineId,
|
|
3404
3452
|
machineHost: settings.machineHost || os$1.hostname(),
|
|
3405
3453
|
platform: process.platform,
|
|
3406
|
-
|
|
3454
|
+
happyCliVersion: packageJson.version,
|
|
3455
|
+
happyHomeDirectory: process.cwd()
|
|
3407
3456
|
};
|
|
3408
3457
|
let credentials = await readCredentials();
|
|
3409
3458
|
if (!credentials) {
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
|
-
import { l as logger, d as backoff, e as delay, R as RawJSONLinesSchema, c as configuration, f as encodeBase64, A as ApiClient, g as encodeBase64Url, h as decodeBase64, b as initializeConfiguration, i as initLoggerWithGlobalConfiguration } from './types-
|
|
2
|
+
import { l as logger, d as backoff, e as delay, R as RawJSONLinesSchema, c as configuration, f as encodeBase64, A as ApiClient, g as encodeBase64Url, h as decodeBase64, j as encrypt, b as initializeConfiguration, i as initLoggerWithGlobalConfiguration } from './types-VkaGP8up.mjs';
|
|
3
3
|
import { randomUUID, randomBytes } from 'node:crypto';
|
|
4
4
|
import { spawn, execSync } from 'node:child_process';
|
|
5
5
|
import { resolve, join, dirname as dirname$1 } from 'node:path';
|
|
@@ -2227,7 +2227,7 @@ async function loop(opts) {
|
|
|
2227
2227
|
}
|
|
2228
2228
|
|
|
2229
2229
|
var name = "happy-coder";
|
|
2230
|
-
var version = "0.
|
|
2230
|
+
var version = "0.4.0";
|
|
2231
2231
|
var description = "Claude Code session sharing CLI";
|
|
2232
2232
|
var author = "Kirill Dubovitskiy";
|
|
2233
2233
|
var license = "MIT";
|
|
@@ -2273,15 +2273,16 @@ var files = [
|
|
|
2273
2273
|
var scripts = {
|
|
2274
2274
|
test: "vitest run",
|
|
2275
2275
|
"test:watch": "vitest",
|
|
2276
|
-
build: "tsc --noEmit && pkgroll",
|
|
2276
|
+
build: "shx rm -rf dist && tsc --noEmit && pkgroll",
|
|
2277
2277
|
prepublishOnly: "yarn build && yarn test",
|
|
2278
2278
|
typecheck: "tsc --noEmit",
|
|
2279
|
-
dev: "npx tsx --env-file .env.sample src/index.ts",
|
|
2280
|
-
"dev:local-server": "HANDY_SERVER_URL=http://localhost:3005
|
|
2281
|
-
prerelease: "npm version prerelease --preid=beta"
|
|
2279
|
+
dev: "yarn build && npx tsx --env-file .env.sample src/index.ts",
|
|
2280
|
+
"dev:local-server": "yarn build && cross-env HANDY_SERVER_URL=http://localhost:3005 tsx --env-file .env.sample src/index.ts",
|
|
2281
|
+
"version:prerelease": "npm version prerelease --preid=beta",
|
|
2282
|
+
"publish:prerelease": "npm publish --tag beta"
|
|
2282
2283
|
};
|
|
2283
2284
|
var dependencies = {
|
|
2284
|
-
"@anthropic-ai/claude-code": "^1.0.
|
|
2285
|
+
"@anthropic-ai/claude-code": "^1.0.73",
|
|
2285
2286
|
"@anthropic-ai/sdk": "^0.56.0",
|
|
2286
2287
|
"@modelcontextprotocol/sdk": "^1.15.1",
|
|
2287
2288
|
"@stablelib/base64": "^2.0.1",
|
|
@@ -2304,6 +2305,7 @@ var dependencies = {
|
|
|
2304
2305
|
var devDependencies = {
|
|
2305
2306
|
"@eslint/compat": "^1",
|
|
2306
2307
|
"@types/node": ">=18",
|
|
2308
|
+
"cross-env": "^10.0.0",
|
|
2307
2309
|
eslint: "^9",
|
|
2308
2310
|
"eslint-config-prettier": "^10",
|
|
2309
2311
|
pkgroll: "^2.14.2",
|
|
@@ -2939,7 +2941,8 @@ async function start(credentials, options = {}) {
|
|
|
2939
2941
|
host: os.hostname(),
|
|
2940
2942
|
version: packageJson.version,
|
|
2941
2943
|
os: os.platform(),
|
|
2942
|
-
machineId: settings.machineId
|
|
2944
|
+
machineId: settings.machineId,
|
|
2945
|
+
homeDir: os.homedir()
|
|
2943
2946
|
};
|
|
2944
2947
|
const response = await api.getOrCreateSession({ tag: sessionTag, metadata, state });
|
|
2945
2948
|
logger.debug(`Session created: ${response.id}`);
|
|
@@ -3093,6 +3096,7 @@ class ApiDaemonSession extends EventEmitter {
|
|
|
3093
3096
|
token;
|
|
3094
3097
|
secret;
|
|
3095
3098
|
spawnedProcesses = /* @__PURE__ */ new Set();
|
|
3099
|
+
machineRegistered = false;
|
|
3096
3100
|
constructor(token, secret, machineIdentity) {
|
|
3097
3101
|
super();
|
|
3098
3102
|
this.token = token;
|
|
@@ -3114,9 +3118,12 @@ class ApiDaemonSession extends EventEmitter {
|
|
|
3114
3118
|
withCredentials: true,
|
|
3115
3119
|
autoConnect: false
|
|
3116
3120
|
});
|
|
3117
|
-
socket.on("connect", () => {
|
|
3121
|
+
socket.on("connect", async () => {
|
|
3118
3122
|
logger.debug("[DAEMON SESSION] Socket connected");
|
|
3119
3123
|
logger.debug(`[DAEMON SESSION] Connected with auth - token: ${this.token.substring(0, 10)}..., machineId: ${this.machineIdentity.machineId}`);
|
|
3124
|
+
if (!this.machineRegistered) {
|
|
3125
|
+
await this.registerMachine();
|
|
3126
|
+
}
|
|
3120
3127
|
const rpcMethod = `${this.machineIdentity.machineId}:spawn-happy-session`;
|
|
3121
3128
|
socket.emit("rpc-register", { method: rpcMethod });
|
|
3122
3129
|
logger.debug(`[DAEMON SESSION] Emitted RPC registration: ${rpcMethod}`);
|
|
@@ -3143,16 +3150,11 @@ class ApiDaemonSession extends EventEmitter {
|
|
|
3143
3150
|
args.push("--local");
|
|
3144
3151
|
}
|
|
3145
3152
|
logger.debug(`[DAEMON SESSION] Spawning happy in directory: ${directory} with args: ${args.join(" ")}`);
|
|
3146
|
-
const
|
|
3147
|
-
|
|
3148
|
-
|
|
3149
|
-
|
|
3150
|
-
|
|
3151
|
-
spawnArgs = args;
|
|
3152
|
-
} else {
|
|
3153
|
-
executable = "npx";
|
|
3154
|
-
spawnArgs = ["tsx", happyPath, ...args];
|
|
3155
|
-
}
|
|
3153
|
+
const happyBinPath = join$1(projectPath(), "bin", "happy.mjs");
|
|
3154
|
+
logger.debug(`[DAEMON SESSION] Using happy binary at: ${happyBinPath}`);
|
|
3155
|
+
const executable = happyBinPath;
|
|
3156
|
+
const spawnArgs = args;
|
|
3157
|
+
logger.debug(`[DAEMON SESSION] Spawn: executable=${executable}, args=${JSON.stringify(spawnArgs)}, cwd=${directory}`);
|
|
3156
3158
|
const happyProcess = spawn$1(executable, spawnArgs, {
|
|
3157
3159
|
cwd: directory,
|
|
3158
3160
|
detached: true,
|
|
@@ -3245,7 +3247,7 @@ class ApiDaemonSession extends EventEmitter {
|
|
|
3245
3247
|
logger.debug(`[DAEMON SESSION] RPC error: ${JSON.stringify(data)}`);
|
|
3246
3248
|
});
|
|
3247
3249
|
socket.onAny((event, ...args) => {
|
|
3248
|
-
if (!event.startsWith("
|
|
3250
|
+
if (!event.startsWith("session-alive") && event !== "ephemeral") {
|
|
3249
3251
|
logger.debug(`[DAEMON SESSION] Socket event: ${event}, args: ${JSON.stringify(args)}`);
|
|
3250
3252
|
}
|
|
3251
3253
|
});
|
|
@@ -3268,12 +3270,47 @@ class ApiDaemonSession extends EventEmitter {
|
|
|
3268
3270
|
});
|
|
3269
3271
|
this.socket = socket;
|
|
3270
3272
|
}
|
|
3273
|
+
async registerMachine() {
|
|
3274
|
+
try {
|
|
3275
|
+
const metadata = {
|
|
3276
|
+
host: this.machineIdentity.machineHost,
|
|
3277
|
+
platform: this.machineIdentity.platform,
|
|
3278
|
+
happyCliVersion: this.machineIdentity.happyCliVersion,
|
|
3279
|
+
happyHomeDirectory: this.machineIdentity.happyHomeDirectory
|
|
3280
|
+
};
|
|
3281
|
+
const encrypted = encrypt(JSON.stringify(metadata), this.secret);
|
|
3282
|
+
const encryptedMetadata = encodeBase64(encrypted);
|
|
3283
|
+
const response = await fetch(`${configuration.serverUrl}/v1/machines`, {
|
|
3284
|
+
method: "POST",
|
|
3285
|
+
headers: {
|
|
3286
|
+
"Authorization": `Bearer ${this.token}`,
|
|
3287
|
+
"Content-Type": "application/json"
|
|
3288
|
+
},
|
|
3289
|
+
body: JSON.stringify({
|
|
3290
|
+
id: this.machineIdentity.machineId,
|
|
3291
|
+
metadata: encryptedMetadata
|
|
3292
|
+
})
|
|
3293
|
+
});
|
|
3294
|
+
if (response.ok) {
|
|
3295
|
+
logger.debug("[DAEMON SESSION] Machine registered/updated successfully");
|
|
3296
|
+
this.machineRegistered = true;
|
|
3297
|
+
} else {
|
|
3298
|
+
logger.debug(`[DAEMON SESSION] Failed to register machine: ${response.status}`);
|
|
3299
|
+
}
|
|
3300
|
+
} catch (error) {
|
|
3301
|
+
logger.debug("[DAEMON SESSION] Failed to register machine:", error);
|
|
3302
|
+
}
|
|
3303
|
+
}
|
|
3271
3304
|
startKeepAlive() {
|
|
3272
3305
|
this.stopKeepAlive();
|
|
3273
3306
|
this.keepAliveInterval = setInterval(() => {
|
|
3274
|
-
|
|
3307
|
+
const payload = {
|
|
3308
|
+
type: "machine-scoped",
|
|
3309
|
+
machineId: this.machineIdentity.machineId,
|
|
3275
3310
|
time: Date.now()
|
|
3276
|
-
}
|
|
3311
|
+
};
|
|
3312
|
+
logger.debugLargeJson(`[DAEMON SESSION] Emitting session-alive`, payload);
|
|
3313
|
+
this.socket.emit("session-alive", payload);
|
|
3277
3314
|
}, 2e4);
|
|
3278
3315
|
}
|
|
3279
3316
|
stopKeepAlive() {
|
|
@@ -3298,15 +3335,26 @@ class ApiDaemonSession extends EventEmitter {
|
|
|
3298
3335
|
connect() {
|
|
3299
3336
|
this.socket.connect();
|
|
3300
3337
|
}
|
|
3338
|
+
updateMetadata(updates) {
|
|
3339
|
+
const metadata = {
|
|
3340
|
+
host: this.machineIdentity.machineHost,
|
|
3341
|
+
platform: this.machineIdentity.platform,
|
|
3342
|
+
happyCliVersion: updates.happyCliVersion || this.machineIdentity.happyCliVersion,
|
|
3343
|
+
happyHomeDirectory: updates.happyHomeDirectory || this.machineIdentity.happyHomeDirectory
|
|
3344
|
+
};
|
|
3345
|
+
const encrypted = encrypt(JSON.stringify(metadata), this.secret);
|
|
3346
|
+
const encryptedMetadata = encodeBase64(encrypted);
|
|
3347
|
+
this.socket.emit("update-machine-metadata", { metadata: encryptedMetadata });
|
|
3348
|
+
}
|
|
3301
3349
|
shutdown() {
|
|
3302
3350
|
logger.debug(`[DAEMON SESSION] Shutting down daemon, killing ${this.spawnedProcesses.size} spawned processes`);
|
|
3303
|
-
for (const
|
|
3351
|
+
for (const process of this.spawnedProcesses) {
|
|
3304
3352
|
try {
|
|
3305
|
-
logger.debug(`[DAEMON SESSION] Killing spawned process with PID: ${
|
|
3306
|
-
|
|
3353
|
+
logger.debug(`[DAEMON SESSION] Killing spawned process with PID: ${process.pid}`);
|
|
3354
|
+
process.kill("SIGTERM");
|
|
3307
3355
|
setTimeout(() => {
|
|
3308
3356
|
try {
|
|
3309
|
-
|
|
3357
|
+
process.kill("SIGKILL");
|
|
3310
3358
|
} catch (e) {
|
|
3311
3359
|
}
|
|
3312
3360
|
}, 1e3);
|
|
@@ -3382,7 +3430,8 @@ async function startDaemon() {
|
|
|
3382
3430
|
machineId: settings.machineId,
|
|
3383
3431
|
machineHost: settings.machineHost || hostname(),
|
|
3384
3432
|
platform: process.platform,
|
|
3385
|
-
|
|
3433
|
+
happyCliVersion: packageJson.version,
|
|
3434
|
+
happyHomeDirectory: process.cwd()
|
|
3386
3435
|
};
|
|
3387
3436
|
let credentials = await readCredentials();
|
|
3388
3437
|
if (!credentials) {
|
package/dist/lib.cjs
CHANGED
package/dist/lib.d.cts
CHANGED
package/dist/lib.d.mts
CHANGED
package/dist/lib.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { A as ApiClient, a as ApiSessionClient, R as RawJSONLinesSchema, c as configuration, i as initLoggerWithGlobalConfiguration, b as initializeConfiguration, l as logger } from './types-
|
|
1
|
+
export { A as ApiClient, a as ApiSessionClient, R as RawJSONLinesSchema, c as configuration, i as initLoggerWithGlobalConfiguration, b as initializeConfiguration, l as logger } from './types-VkaGP8up.mjs';
|
|
2
2
|
import 'axios';
|
|
3
3
|
import 'chalk';
|
|
4
4
|
import 'fs';
|
|
@@ -879,4 +879,4 @@ const RawJSONLinesSchema = z.discriminatedUnion("type", [
|
|
|
879
879
|
}).passthrough()
|
|
880
880
|
]);
|
|
881
881
|
|
|
882
|
-
export { ApiClient as A, RawJSONLinesSchema as R, ApiSessionClient as a, initializeConfiguration as b, configuration as c, backoff as d, delay as e, encodeBase64 as f, encodeBase64Url as g, decodeBase64 as h, initLoggerWithGlobalConfiguration as i, logger as l };
|
|
882
|
+
export { ApiClient as A, RawJSONLinesSchema as R, ApiSessionClient as a, initializeConfiguration as b, configuration as c, backoff as d, delay as e, encodeBase64 as f, encodeBase64Url as g, decodeBase64 as h, initLoggerWithGlobalConfiguration as i, encrypt as j, logger as l };
|
|
@@ -889,5 +889,6 @@ exports.decodeBase64 = decodeBase64;
|
|
|
889
889
|
exports.delay = delay;
|
|
890
890
|
exports.encodeBase64 = encodeBase64;
|
|
891
891
|
exports.encodeBase64Url = encodeBase64Url;
|
|
892
|
+
exports.encrypt = encrypt;
|
|
892
893
|
exports.initLoggerWithGlobalConfiguration = initLoggerWithGlobalConfiguration;
|
|
893
894
|
exports.initializeConfiguration = initializeConfiguration;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "happy-coder",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Claude Code session sharing CLI",
|
|
5
5
|
"author": "Kirill Dubovitskiy",
|
|
6
6
|
"license": "MIT",
|
|
@@ -46,15 +46,16 @@
|
|
|
46
46
|
"scripts": {
|
|
47
47
|
"test": "vitest run",
|
|
48
48
|
"test:watch": "vitest",
|
|
49
|
-
"build": "tsc --noEmit && pkgroll",
|
|
49
|
+
"build": "shx rm -rf dist && tsc --noEmit && pkgroll",
|
|
50
50
|
"prepublishOnly": "yarn build && yarn test",
|
|
51
51
|
"typecheck": "tsc --noEmit",
|
|
52
|
-
"dev": "npx tsx --env-file .env.sample src/index.ts",
|
|
53
|
-
"dev:local-server": "HANDY_SERVER_URL=http://localhost:3005
|
|
54
|
-
"prerelease": "npm version prerelease --preid=beta"
|
|
52
|
+
"dev": "yarn build && npx tsx --env-file .env.sample src/index.ts",
|
|
53
|
+
"dev:local-server": "yarn build && cross-env HANDY_SERVER_URL=http://localhost:3005 tsx --env-file .env.sample src/index.ts",
|
|
54
|
+
"version:prerelease": "npm version prerelease --preid=beta",
|
|
55
|
+
"publish:prerelease": "npm publish --tag beta"
|
|
55
56
|
},
|
|
56
57
|
"dependencies": {
|
|
57
|
-
"@anthropic-ai/claude-code": "^1.0.
|
|
58
|
+
"@anthropic-ai/claude-code": "^1.0.73",
|
|
58
59
|
"@anthropic-ai/sdk": "^0.56.0",
|
|
59
60
|
"@modelcontextprotocol/sdk": "^1.15.1",
|
|
60
61
|
"@stablelib/base64": "^2.0.1",
|
|
@@ -77,6 +78,7 @@
|
|
|
77
78
|
"devDependencies": {
|
|
78
79
|
"@eslint/compat": "^1",
|
|
79
80
|
"@types/node": ">=18",
|
|
81
|
+
"cross-env": "^10.0.0",
|
|
80
82
|
"eslint": "^9",
|
|
81
83
|
"eslint-config-prettier": "^10",
|
|
82
84
|
"pkgroll": "^2.14.2",
|