happy-coder 0.5.0 → 0.6.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  var chalk = require('chalk');
4
- var types$1 = require('./types-BBpJNhIN.cjs');
4
+ var types$1 = require('./types-Bkw2UUhb.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');
@@ -28,6 +28,7 @@ var child_process = require('child_process');
28
28
  var util = require('util');
29
29
  var crypto = require('crypto');
30
30
  var qrcode = require('qrcode-terminal');
31
+ var open = require('open');
31
32
  var fs = require('fs');
32
33
  var os$1 = require('os');
33
34
 
@@ -2261,7 +2262,7 @@ async function loop(opts) {
2261
2262
  }
2262
2263
 
2263
2264
  var name = "happy-coder";
2264
- var version = "0.5.0";
2265
+ var version = "0.6.0";
2265
2266
  var description = "Claude Code session sharing CLI";
2266
2267
  var author = "Kirill Dubovitskiy";
2267
2268
  var license = "MIT";
@@ -2329,7 +2330,7 @@ var dependencies = {
2329
2330
  "http-proxy": "^1.18.1",
2330
2331
  "http-proxy-middleware": "^3.0.5",
2331
2332
  ink: "^6.1.0",
2332
- "ink-box": "^2.0.0",
2333
+ open: "^10.2.0",
2333
2334
  "qrcode-terminal": "^0.12.0",
2334
2335
  react: "^19.1.1",
2335
2336
  "socket.io-client": "^4.8.1",
@@ -3214,8 +3215,69 @@ function displayQRCode(url) {
3214
3215
  console.log("=".repeat(80));
3215
3216
  }
3216
3217
 
3218
+ function generateWebAuthUrl(publicKey) {
3219
+ const publicKeyBase64 = types$1.encodeBase64(publicKey, "base64url");
3220
+ return `https://app.happy.engineering/terminal/connect#key=${publicKeyBase64}`;
3221
+ }
3222
+
3223
+ async function openBrowser(url) {
3224
+ try {
3225
+ if (!process.stdout.isTTY || process.env.CI || process.env.HEADLESS) {
3226
+ types$1.logger.debug("[browser] Headless environment detected, skipping browser open");
3227
+ return false;
3228
+ }
3229
+ types$1.logger.debug(`[browser] Attempting to open URL: ${url}`);
3230
+ await open(url);
3231
+ types$1.logger.debug("[browser] Browser opened successfully");
3232
+ return true;
3233
+ } catch (error) {
3234
+ types$1.logger.debug("[browser] Failed to open browser:", error);
3235
+ return false;
3236
+ }
3237
+ }
3238
+
3239
+ const AuthSelector = ({ onSelect, onCancel }) => {
3240
+ const [selectedIndex, setSelectedIndex] = React.useState(0);
3241
+ const options = [
3242
+ {
3243
+ method: "mobile",
3244
+ label: "Mobile App"
3245
+ },
3246
+ {
3247
+ method: "web",
3248
+ label: "Web Browser"
3249
+ }
3250
+ ];
3251
+ ink.useInput((input, key) => {
3252
+ if (key.upArrow) {
3253
+ setSelectedIndex((prev) => Math.max(0, prev - 1));
3254
+ } else if (key.downArrow) {
3255
+ setSelectedIndex((prev) => Math.min(options.length - 1, prev + 1));
3256
+ } else if (key.return) {
3257
+ onSelect(options[selectedIndex].method);
3258
+ } else if (key.escape || key.ctrl && input === "c") {
3259
+ onCancel();
3260
+ } else if (input === "1") {
3261
+ setSelectedIndex(0);
3262
+ onSelect("mobile");
3263
+ } else if (input === "2") {
3264
+ setSelectedIndex(1);
3265
+ onSelect("web");
3266
+ }
3267
+ });
3268
+ return /* @__PURE__ */ React.createElement(ink.Box, { flexDirection: "column", paddingY: 1 }, /* @__PURE__ */ React.createElement(ink.Box, { marginBottom: 1 }, /* @__PURE__ */ React.createElement(ink.Text, null, "How would you like to authenticate?")), /* @__PURE__ */ React.createElement(ink.Box, { flexDirection: "column" }, options.map((option, index) => {
3269
+ const isSelected = selectedIndex === index;
3270
+ return /* @__PURE__ */ React.createElement(ink.Box, { key: option.method, marginY: 0 }, /* @__PURE__ */ React.createElement(ink.Text, { color: isSelected ? "cyan" : "gray" }, isSelected ? "\u203A " : " ", index + 1, ". ", option.label));
3271
+ })), /* @__PURE__ */ React.createElement(ink.Box, { marginTop: 1 }, /* @__PURE__ */ React.createElement(ink.Text, { dimColor: true }, "Use arrows or 1-2 to select, Enter to confirm")));
3272
+ };
3273
+
3217
3274
  async function doAuth() {
3218
- console.log("Starting authentication...");
3275
+ console.clear();
3276
+ const authMethod = await selectAuthenticationMethod();
3277
+ if (!authMethod) {
3278
+ console.log("\nAuthentication cancelled.\n");
3279
+ return null;
3280
+ }
3219
3281
  const secret = new Uint8Array(node_crypto.randomBytes(32));
3220
3282
  const keypair = tweetnacl.box.keyPair.fromSecretKey(secret);
3221
3283
  try {
@@ -3226,38 +3288,106 @@ async function doAuth() {
3226
3288
  console.log("Failed to create authentication request, please try again later.");
3227
3289
  return null;
3228
3290
  }
3229
- console.log("Please, authenticate using mobile app");
3291
+ if (authMethod === "mobile") {
3292
+ return await doMobileAuth(keypair);
3293
+ } else {
3294
+ return await doWebAuth(keypair);
3295
+ }
3296
+ }
3297
+ function selectAuthenticationMethod() {
3298
+ return new Promise((resolve) => {
3299
+ let hasResolved = false;
3300
+ const onSelect = (method) => {
3301
+ if (!hasResolved) {
3302
+ hasResolved = true;
3303
+ app.unmount();
3304
+ resolve(method);
3305
+ }
3306
+ };
3307
+ const onCancel = () => {
3308
+ if (!hasResolved) {
3309
+ hasResolved = true;
3310
+ app.unmount();
3311
+ resolve(null);
3312
+ }
3313
+ };
3314
+ const app = ink.render(React.createElement(AuthSelector, { onSelect, onCancel }), {
3315
+ exitOnCtrlC: false,
3316
+ patchConsole: false
3317
+ });
3318
+ });
3319
+ }
3320
+ async function doMobileAuth(keypair) {
3321
+ console.clear();
3322
+ console.log("\nMobile Authentication\n");
3323
+ console.log("Scan this QR code with your Happy mobile app:\n");
3230
3324
  const authUrl = "happy://terminal?" + types$1.encodeBase64Url(keypair.publicKey);
3231
3325
  displayQRCode(authUrl);
3232
- console.log("\n\u{1F4CB} For manual entry, copy this URL:");
3326
+ console.log("\nOr manually enter this URL:");
3233
3327
  console.log(authUrl);
3234
- let credentials = null;
3235
- while (true) {
3236
- try {
3237
- const response = await axios.post(`${types$1.configuration.serverUrl}/v1/auth/request`, {
3238
- publicKey: types$1.encodeBase64(keypair.publicKey)
3239
- });
3240
- if (response.data.state === "authorized") {
3241
- let token = response.data.token;
3242
- let r = types$1.decodeBase64(response.data.response);
3243
- let decrypted = decryptWithEphemeralKey(r, keypair.secretKey);
3244
- if (decrypted) {
3245
- credentials = {
3246
- secret: decrypted,
3247
- token
3248
- };
3249
- await writeCredentials(credentials);
3250
- return credentials;
3251
- } else {
3252
- console.log("Failed to decrypt response, please try again later.");
3253
- return null;
3328
+ console.log("");
3329
+ return await waitForAuthentication(keypair);
3330
+ }
3331
+ async function doWebAuth(keypair) {
3332
+ console.clear();
3333
+ console.log("\nWeb Authentication\n");
3334
+ const webUrl = generateWebAuthUrl(keypair.publicKey);
3335
+ console.log("Opening your browser...");
3336
+ const browserOpened = await openBrowser(webUrl);
3337
+ if (browserOpened) {
3338
+ console.log("\u2713 Browser opened\n");
3339
+ console.log("Complete authentication in your browser window.");
3340
+ } else {
3341
+ console.log("Could not open browser automatically.\n");
3342
+ console.log("Please open this URL manually:");
3343
+ console.log(webUrl);
3344
+ }
3345
+ console.log("");
3346
+ return await waitForAuthentication(keypair);
3347
+ }
3348
+ async function waitForAuthentication(keypair) {
3349
+ process.stdout.write("Waiting for authentication");
3350
+ let dots = 0;
3351
+ let cancelled = false;
3352
+ const handleInterrupt = () => {
3353
+ cancelled = true;
3354
+ console.log("\n\nAuthentication cancelled.");
3355
+ process.exit(0);
3356
+ };
3357
+ process.on("SIGINT", handleInterrupt);
3358
+ try {
3359
+ while (!cancelled) {
3360
+ try {
3361
+ const response = await axios.post(`${types$1.configuration.serverUrl}/v1/auth/request`, {
3362
+ publicKey: types$1.encodeBase64(keypair.publicKey)
3363
+ });
3364
+ if (response.data.state === "authorized") {
3365
+ let token = response.data.token;
3366
+ let r = types$1.decodeBase64(response.data.response);
3367
+ let decrypted = decryptWithEphemeralKey(r, keypair.secretKey);
3368
+ if (decrypted) {
3369
+ const credentials = {
3370
+ secret: decrypted,
3371
+ token
3372
+ };
3373
+ await writeCredentials(credentials);
3374
+ console.log("\n\n\u2713 Authentication successful\n");
3375
+ return credentials;
3376
+ } else {
3377
+ console.log("\n\nFailed to decrypt response. Please try again.");
3378
+ return null;
3379
+ }
3254
3380
  }
3381
+ } catch (error) {
3382
+ console.log("\n\nFailed to check authentication status. Please try again.");
3383
+ return null;
3255
3384
  }
3256
- } catch (error) {
3257
- console.log("Failed to create authentication request, please try again later.");
3258
- return null;
3385
+ process.stdout.write("\rWaiting for authentication" + ".".repeat(dots % 3 + 1) + " ");
3386
+ dots++;
3387
+ await types$1.delay(1e3);
3259
3388
  }
3260
- await types$1.delay(1e3);
3389
+ } finally {
3390
+ process.off("SIGINT", handleInterrupt);
3261
3391
  }
3262
3392
  return null;
3263
3393
  }
@@ -3488,12 +3618,11 @@ class ApiDaemonSession extends node_events.EventEmitter {
3488
3618
  this.stopKeepAlive();
3489
3619
  this.keepAliveInterval = setInterval(() => {
3490
3620
  const payload = {
3491
- type: "machine-scoped",
3492
3621
  machineId: this.machineIdentity.machineId,
3493
3622
  time: Date.now()
3494
3623
  };
3495
- types$1.logger.debugLargeJson(`[DAEMON SESSION] Emitting session-alive`, payload);
3496
- this.socket.emit("session-alive", payload);
3624
+ types$1.logger.debugLargeJson(`[DAEMON SESSION] Emitting machine-alive`, payload);
3625
+ this.socket.emit("machine-alive", payload);
3497
3626
  }, 2e4);
3498
3627
  }
3499
3628
  stopKeepAlive() {
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, j as encrypt, b as initializeConfiguration, i as initLoggerWithGlobalConfiguration } from './types-DDjn6Ovv.mjs';
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-Cqy5Dx2C.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';
@@ -27,6 +27,7 @@ import { spawn as spawn$1, exec, execSync as execSync$1 } from 'child_process';
27
27
  import { promisify } from 'util';
28
28
  import crypto, { createHash } from 'crypto';
29
29
  import qrcode from 'qrcode-terminal';
30
+ import open from 'open';
30
31
  import { existsSync as existsSync$1, readFileSync as readFileSync$1, writeFileSync, unlinkSync, mkdirSync as mkdirSync$1, chmodSync } from 'fs';
31
32
  import { hostname, homedir as homedir$1 } from 'os';
32
33
 
@@ -2240,7 +2241,7 @@ async function loop(opts) {
2240
2241
  }
2241
2242
 
2242
2243
  var name = "happy-coder";
2243
- var version = "0.5.0";
2244
+ var version = "0.6.0";
2244
2245
  var description = "Claude Code session sharing CLI";
2245
2246
  var author = "Kirill Dubovitskiy";
2246
2247
  var license = "MIT";
@@ -2308,7 +2309,7 @@ var dependencies = {
2308
2309
  "http-proxy": "^1.18.1",
2309
2310
  "http-proxy-middleware": "^3.0.5",
2310
2311
  ink: "^6.1.0",
2311
- "ink-box": "^2.0.0",
2312
+ open: "^10.2.0",
2312
2313
  "qrcode-terminal": "^0.12.0",
2313
2314
  react: "^19.1.1",
2314
2315
  "socket.io-client": "^4.8.1",
@@ -3193,8 +3194,69 @@ function displayQRCode(url) {
3193
3194
  console.log("=".repeat(80));
3194
3195
  }
3195
3196
 
3197
+ function generateWebAuthUrl(publicKey) {
3198
+ const publicKeyBase64 = encodeBase64(publicKey, "base64url");
3199
+ return `https://app.happy.engineering/terminal/connect#key=${publicKeyBase64}`;
3200
+ }
3201
+
3202
+ async function openBrowser(url) {
3203
+ try {
3204
+ if (!process.stdout.isTTY || process.env.CI || process.env.HEADLESS) {
3205
+ logger.debug("[browser] Headless environment detected, skipping browser open");
3206
+ return false;
3207
+ }
3208
+ logger.debug(`[browser] Attempting to open URL: ${url}`);
3209
+ await open(url);
3210
+ logger.debug("[browser] Browser opened successfully");
3211
+ return true;
3212
+ } catch (error) {
3213
+ logger.debug("[browser] Failed to open browser:", error);
3214
+ return false;
3215
+ }
3216
+ }
3217
+
3218
+ const AuthSelector = ({ onSelect, onCancel }) => {
3219
+ const [selectedIndex, setSelectedIndex] = useState(0);
3220
+ const options = [
3221
+ {
3222
+ method: "mobile",
3223
+ label: "Mobile App"
3224
+ },
3225
+ {
3226
+ method: "web",
3227
+ label: "Web Browser"
3228
+ }
3229
+ ];
3230
+ useInput((input, key) => {
3231
+ if (key.upArrow) {
3232
+ setSelectedIndex((prev) => Math.max(0, prev - 1));
3233
+ } else if (key.downArrow) {
3234
+ setSelectedIndex((prev) => Math.min(options.length - 1, prev + 1));
3235
+ } else if (key.return) {
3236
+ onSelect(options[selectedIndex].method);
3237
+ } else if (key.escape || key.ctrl && input === "c") {
3238
+ onCancel();
3239
+ } else if (input === "1") {
3240
+ setSelectedIndex(0);
3241
+ onSelect("mobile");
3242
+ } else if (input === "2") {
3243
+ setSelectedIndex(1);
3244
+ onSelect("web");
3245
+ }
3246
+ });
3247
+ return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", paddingY: 1 }, /* @__PURE__ */ React.createElement(Box, { marginBottom: 1 }, /* @__PURE__ */ React.createElement(Text, null, "How would you like to authenticate?")), /* @__PURE__ */ React.createElement(Box, { flexDirection: "column" }, options.map((option, index) => {
3248
+ const isSelected = selectedIndex === index;
3249
+ return /* @__PURE__ */ React.createElement(Box, { key: option.method, marginY: 0 }, /* @__PURE__ */ React.createElement(Text, { color: isSelected ? "cyan" : "gray" }, isSelected ? "\u203A " : " ", index + 1, ". ", option.label));
3250
+ })), /* @__PURE__ */ React.createElement(Box, { marginTop: 1 }, /* @__PURE__ */ React.createElement(Text, { dimColor: true }, "Use arrows or 1-2 to select, Enter to confirm")));
3251
+ };
3252
+
3196
3253
  async function doAuth() {
3197
- console.log("Starting authentication...");
3254
+ console.clear();
3255
+ const authMethod = await selectAuthenticationMethod();
3256
+ if (!authMethod) {
3257
+ console.log("\nAuthentication cancelled.\n");
3258
+ return null;
3259
+ }
3198
3260
  const secret = new Uint8Array(randomBytes(32));
3199
3261
  const keypair = tweetnacl.box.keyPair.fromSecretKey(secret);
3200
3262
  try {
@@ -3205,38 +3267,106 @@ async function doAuth() {
3205
3267
  console.log("Failed to create authentication request, please try again later.");
3206
3268
  return null;
3207
3269
  }
3208
- console.log("Please, authenticate using mobile app");
3270
+ if (authMethod === "mobile") {
3271
+ return await doMobileAuth(keypair);
3272
+ } else {
3273
+ return await doWebAuth(keypair);
3274
+ }
3275
+ }
3276
+ function selectAuthenticationMethod() {
3277
+ return new Promise((resolve) => {
3278
+ let hasResolved = false;
3279
+ const onSelect = (method) => {
3280
+ if (!hasResolved) {
3281
+ hasResolved = true;
3282
+ app.unmount();
3283
+ resolve(method);
3284
+ }
3285
+ };
3286
+ const onCancel = () => {
3287
+ if (!hasResolved) {
3288
+ hasResolved = true;
3289
+ app.unmount();
3290
+ resolve(null);
3291
+ }
3292
+ };
3293
+ const app = render(React.createElement(AuthSelector, { onSelect, onCancel }), {
3294
+ exitOnCtrlC: false,
3295
+ patchConsole: false
3296
+ });
3297
+ });
3298
+ }
3299
+ async function doMobileAuth(keypair) {
3300
+ console.clear();
3301
+ console.log("\nMobile Authentication\n");
3302
+ console.log("Scan this QR code with your Happy mobile app:\n");
3209
3303
  const authUrl = "happy://terminal?" + encodeBase64Url(keypair.publicKey);
3210
3304
  displayQRCode(authUrl);
3211
- console.log("\n\u{1F4CB} For manual entry, copy this URL:");
3305
+ console.log("\nOr manually enter this URL:");
3212
3306
  console.log(authUrl);
3213
- let credentials = null;
3214
- while (true) {
3215
- try {
3216
- const response = await axios.post(`${configuration.serverUrl}/v1/auth/request`, {
3217
- publicKey: encodeBase64(keypair.publicKey)
3218
- });
3219
- if (response.data.state === "authorized") {
3220
- let token = response.data.token;
3221
- let r = decodeBase64(response.data.response);
3222
- let decrypted = decryptWithEphemeralKey(r, keypair.secretKey);
3223
- if (decrypted) {
3224
- credentials = {
3225
- secret: decrypted,
3226
- token
3227
- };
3228
- await writeCredentials(credentials);
3229
- return credentials;
3230
- } else {
3231
- console.log("Failed to decrypt response, please try again later.");
3232
- return null;
3307
+ console.log("");
3308
+ return await waitForAuthentication(keypair);
3309
+ }
3310
+ async function doWebAuth(keypair) {
3311
+ console.clear();
3312
+ console.log("\nWeb Authentication\n");
3313
+ const webUrl = generateWebAuthUrl(keypair.publicKey);
3314
+ console.log("Opening your browser...");
3315
+ const browserOpened = await openBrowser(webUrl);
3316
+ if (browserOpened) {
3317
+ console.log("\u2713 Browser opened\n");
3318
+ console.log("Complete authentication in your browser window.");
3319
+ } else {
3320
+ console.log("Could not open browser automatically.\n");
3321
+ console.log("Please open this URL manually:");
3322
+ console.log(webUrl);
3323
+ }
3324
+ console.log("");
3325
+ return await waitForAuthentication(keypair);
3326
+ }
3327
+ async function waitForAuthentication(keypair) {
3328
+ process.stdout.write("Waiting for authentication");
3329
+ let dots = 0;
3330
+ let cancelled = false;
3331
+ const handleInterrupt = () => {
3332
+ cancelled = true;
3333
+ console.log("\n\nAuthentication cancelled.");
3334
+ process.exit(0);
3335
+ };
3336
+ process.on("SIGINT", handleInterrupt);
3337
+ try {
3338
+ while (!cancelled) {
3339
+ try {
3340
+ const response = await axios.post(`${configuration.serverUrl}/v1/auth/request`, {
3341
+ publicKey: encodeBase64(keypair.publicKey)
3342
+ });
3343
+ if (response.data.state === "authorized") {
3344
+ let token = response.data.token;
3345
+ let r = decodeBase64(response.data.response);
3346
+ let decrypted = decryptWithEphemeralKey(r, keypair.secretKey);
3347
+ if (decrypted) {
3348
+ const credentials = {
3349
+ secret: decrypted,
3350
+ token
3351
+ };
3352
+ await writeCredentials(credentials);
3353
+ console.log("\n\n\u2713 Authentication successful\n");
3354
+ return credentials;
3355
+ } else {
3356
+ console.log("\n\nFailed to decrypt response. Please try again.");
3357
+ return null;
3358
+ }
3233
3359
  }
3360
+ } catch (error) {
3361
+ console.log("\n\nFailed to check authentication status. Please try again.");
3362
+ return null;
3234
3363
  }
3235
- } catch (error) {
3236
- console.log("Failed to create authentication request, please try again later.");
3237
- return null;
3364
+ process.stdout.write("\rWaiting for authentication" + ".".repeat(dots % 3 + 1) + " ");
3365
+ dots++;
3366
+ await delay(1e3);
3238
3367
  }
3239
- await delay(1e3);
3368
+ } finally {
3369
+ process.off("SIGINT", handleInterrupt);
3240
3370
  }
3241
3371
  return null;
3242
3372
  }
@@ -3467,12 +3597,11 @@ class ApiDaemonSession extends EventEmitter {
3467
3597
  this.stopKeepAlive();
3468
3598
  this.keepAliveInterval = setInterval(() => {
3469
3599
  const payload = {
3470
- type: "machine-scoped",
3471
3600
  machineId: this.machineIdentity.machineId,
3472
3601
  time: Date.now()
3473
3602
  };
3474
- logger.debugLargeJson(`[DAEMON SESSION] Emitting session-alive`, payload);
3475
- this.socket.emit("session-alive", payload);
3603
+ logger.debugLargeJson(`[DAEMON SESSION] Emitting machine-alive`, payload);
3604
+ this.socket.emit("machine-alive", payload);
3476
3605
  }, 2e4);
3477
3606
  }
3478
3607
  stopKeepAlive() {
package/dist/lib.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var types = require('./types-BBpJNhIN.cjs');
3
+ var types = require('./types-Bkw2UUhb.cjs');
4
4
  require('axios');
5
5
  require('chalk');
6
6
  require('fs');
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-DDjn6Ovv.mjs';
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-Cqy5Dx2C.mjs';
2
2
  import 'axios';
3
3
  import 'chalk';
4
4
  import 'fs';
@@ -546,7 +546,6 @@ class ApiSessionClient extends node_events.EventEmitter {
546
546
  */
547
547
  keepAlive(thinking, mode) {
548
548
  this.socket.volatile.emit("session-alive", {
549
- type: "session-scoped",
550
549
  sid: this.sessionId,
551
550
  time: Date.now(),
552
551
  thinking,
@@ -544,7 +544,6 @@ class ApiSessionClient extends EventEmitter {
544
544
  */
545
545
  keepAlive(thinking, mode) {
546
546
  this.socket.volatile.emit("session-alive", {
547
- type: "session-scoped",
548
547
  sid: this.sessionId,
549
548
  time: Date.now(),
550
549
  thinking,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "happy-coder",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "Claude Code session sharing CLI",
5
5
  "author": "Kirill Dubovitskiy",
6
6
  "license": "MIT",
@@ -68,7 +68,7 @@
68
68
  "http-proxy": "^1.18.1",
69
69
  "http-proxy-middleware": "^3.0.5",
70
70
  "ink": "^6.1.0",
71
- "ink-box": "^2.0.0",
71
+ "open": "^10.2.0",
72
72
  "qrcode-terminal": "^0.12.0",
73
73
  "react": "^19.1.1",
74
74
  "socket.io-client": "^4.8.1",