happy-coder 0.6.0 → 0.6.2

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-Bkw2UUhb.cjs');
4
+ var types$1 = require('./types-iMUxaPkI.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');
@@ -2262,7 +2262,7 @@ async function loop(opts) {
2262
2262
  }
2263
2263
 
2264
2264
  var name = "happy-coder";
2265
- var version = "0.6.0";
2265
+ var version = "0.6.2";
2266
2266
  var description = "Claude Code session sharing CLI";
2267
2267
  var author = "Kirill Dubovitskiy";
2268
2268
  var license = "MIT";
@@ -3087,6 +3087,10 @@ async function start(credentials, options = {}) {
3087
3087
  const logPath = await types$1.logger.logFilePathPromise;
3088
3088
  types$1.logger.infoDeveloper(`Session: ${response.id}`);
3089
3089
  types$1.logger.infoDeveloper(`Logs: ${logPath}`);
3090
+ session.updateAgentState((currentState) => ({
3091
+ ...currentState,
3092
+ controlledByUser: options.startingMode === "local"
3093
+ }));
3090
3094
  const caffeinateStarted = startCaffeinate();
3091
3095
  if (caffeinateStarted) {
3092
3096
  types$1.logger.infoDeveloper("Sleep prevention enabled (macOS)");
@@ -3185,7 +3189,7 @@ async function start(credentials, options = {}) {
3185
3189
  session.sendSessionEvent({ type: "switch", mode: newMode });
3186
3190
  session.updateAgentState((currentState) => ({
3187
3191
  ...currentState,
3188
- controlledByUser: false
3192
+ controlledByUser: newMode === "local"
3189
3193
  }));
3190
3194
  },
3191
3195
  mcpServers: {},
@@ -3656,7 +3660,7 @@ class ApiDaemonSession extends node_events.EventEmitter {
3656
3660
  };
3657
3661
  const encrypted = types$1.encrypt(JSON.stringify(metadata), this.secret);
3658
3662
  const encryptedMetadata = types$1.encodeBase64(encrypted);
3659
- this.socket.emit("update-machine-metadata", { metadata: encryptedMetadata });
3663
+ this.socket.emit("update-machine", { metadata: encryptedMetadata });
3660
3664
  }
3661
3665
  shutdown() {
3662
3666
  types$1.logger.debug(`[DAEMON SESSION] Shutting down daemon, killing ${this.spawnedProcesses.size} spawned processes`);
@@ -4090,6 +4094,17 @@ async function uninstall() {
4090
4094
  process.exit(1);
4091
4095
  }
4092
4096
  return;
4097
+ } else if (subcommand === "notify") {
4098
+ try {
4099
+ await handleNotifyCommand(args.slice(1));
4100
+ } catch (error) {
4101
+ console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Unknown error");
4102
+ if (process.env.DEBUG) {
4103
+ console.error(error);
4104
+ }
4105
+ process.exit(1);
4106
+ }
4107
+ return;
4093
4108
  } else if (subcommand === "daemon") {
4094
4109
  const daemonSubcommand = args[1];
4095
4110
  if (daemonSubcommand === "start") {
@@ -4170,6 +4185,7 @@ ${chalk.bold("happy")} - Claude Code On the Go
4170
4185
 
4171
4186
  ${chalk.bold("Usage:")}
4172
4187
  happy [options]
4188
+ happy notify Send notification
4173
4189
  happy logout Logs out of your account and removes data directory
4174
4190
  happy daemon Manage the background daemon (macOS only)
4175
4191
 
@@ -4202,6 +4218,7 @@ ${chalk.bold("Examples:")}
4202
4218
  happy -m opus Use Claude Opus model
4203
4219
  happy -p plan Use plan permission mode
4204
4220
  happy --auth Force re-authentication before starting session
4221
+ happy notify -p "Hello!" Send notification
4205
4222
  happy --claude-env KEY=VALUE
4206
4223
  Set environment variable for Claude Code
4207
4224
  happy --claude-arg --option
@@ -4316,3 +4333,71 @@ async function cleanKey() {
4316
4333
  console.log(chalk.blue("Operation cancelled"));
4317
4334
  }
4318
4335
  }
4336
+ async function handleNotifyCommand(args) {
4337
+ let message = "";
4338
+ let title = "";
4339
+ let showHelp = false;
4340
+ for (let i = 0; i < args.length; i++) {
4341
+ const arg = args[i];
4342
+ if (arg === "-p" && i + 1 < args.length) {
4343
+ message = args[++i];
4344
+ } else if (arg === "-t" && i + 1 < args.length) {
4345
+ title = args[++i];
4346
+ } else if (arg === "-h" || arg === "--help") {
4347
+ showHelp = true;
4348
+ } else {
4349
+ console.error(chalk.red(`Unknown argument for notify command: ${arg}`));
4350
+ process.exit(1);
4351
+ }
4352
+ }
4353
+ if (showHelp) {
4354
+ console.log(`
4355
+ ${chalk.bold("happy notify")} - Send notification
4356
+
4357
+ ${chalk.bold("Usage:")}
4358
+ happy notify -p <message> [-t <title>] Send notification with custom message and optional title
4359
+ happy notify -h, --help Show this help
4360
+
4361
+ ${chalk.bold("Options:")}
4362
+ -p <message> Notification message (required)
4363
+ -t <title> Notification title (optional, defaults to "Happy")
4364
+
4365
+ ${chalk.bold("Examples:")}
4366
+ happy notify -p "Deployment complete!"
4367
+ happy notify -p "System update complete" -t "Server Status"
4368
+ happy notify -t "Alert" -p "Database connection restored"
4369
+ `);
4370
+ return;
4371
+ }
4372
+ if (!message) {
4373
+ console.error(chalk.red('Error: Message is required. Use -p "your message" to specify the notification text.'));
4374
+ console.log(chalk.gray('Run "happy notify --help" for usage information.'));
4375
+ process.exit(1);
4376
+ }
4377
+ let credentials = await readCredentials();
4378
+ if (!credentials) {
4379
+ console.error(chalk.red('Error: Not authenticated. Please run "happy --auth" first.'));
4380
+ process.exit(1);
4381
+ }
4382
+ console.log(chalk.blue("\u{1F4F1} Sending push notification..."));
4383
+ try {
4384
+ const api = new types$1.ApiClient(credentials.token, credentials.secret);
4385
+ const notificationTitle = title || "Happy";
4386
+ api.push().sendToAllDevices(
4387
+ notificationTitle,
4388
+ message,
4389
+ {
4390
+ source: "cli",
4391
+ timestamp: Date.now()
4392
+ }
4393
+ );
4394
+ console.log(chalk.green("\u2713 Push notification sent successfully!"));
4395
+ console.log(chalk.gray(` Title: ${notificationTitle}`));
4396
+ console.log(chalk.gray(` Message: ${message}`));
4397
+ console.log(chalk.gray(" Check your mobile device for the notification."));
4398
+ await new Promise((resolve) => setTimeout(resolve, 1e3));
4399
+ } catch (error) {
4400
+ console.error(chalk.red("\u2717 Failed to send push notification"));
4401
+ throw error;
4402
+ }
4403
+ }
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-Cqy5Dx2C.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-DKVMGtcN.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';
@@ -2241,7 +2241,7 @@ async function loop(opts) {
2241
2241
  }
2242
2242
 
2243
2243
  var name = "happy-coder";
2244
- var version = "0.6.0";
2244
+ var version = "0.6.2";
2245
2245
  var description = "Claude Code session sharing CLI";
2246
2246
  var author = "Kirill Dubovitskiy";
2247
2247
  var license = "MIT";
@@ -3066,6 +3066,10 @@ async function start(credentials, options = {}) {
3066
3066
  const logPath = await logger.logFilePathPromise;
3067
3067
  logger.infoDeveloper(`Session: ${response.id}`);
3068
3068
  logger.infoDeveloper(`Logs: ${logPath}`);
3069
+ session.updateAgentState((currentState) => ({
3070
+ ...currentState,
3071
+ controlledByUser: options.startingMode === "local"
3072
+ }));
3069
3073
  const caffeinateStarted = startCaffeinate();
3070
3074
  if (caffeinateStarted) {
3071
3075
  logger.infoDeveloper("Sleep prevention enabled (macOS)");
@@ -3164,7 +3168,7 @@ async function start(credentials, options = {}) {
3164
3168
  session.sendSessionEvent({ type: "switch", mode: newMode });
3165
3169
  session.updateAgentState((currentState) => ({
3166
3170
  ...currentState,
3167
- controlledByUser: false
3171
+ controlledByUser: newMode === "local"
3168
3172
  }));
3169
3173
  },
3170
3174
  mcpServers: {},
@@ -3635,7 +3639,7 @@ class ApiDaemonSession extends EventEmitter {
3635
3639
  };
3636
3640
  const encrypted = encrypt(JSON.stringify(metadata), this.secret);
3637
3641
  const encryptedMetadata = encodeBase64(encrypted);
3638
- this.socket.emit("update-machine-metadata", { metadata: encryptedMetadata });
3642
+ this.socket.emit("update-machine", { metadata: encryptedMetadata });
3639
3643
  }
3640
3644
  shutdown() {
3641
3645
  logger.debug(`[DAEMON SESSION] Shutting down daemon, killing ${this.spawnedProcesses.size} spawned processes`);
@@ -4069,6 +4073,17 @@ async function uninstall() {
4069
4073
  process.exit(1);
4070
4074
  }
4071
4075
  return;
4076
+ } else if (subcommand === "notify") {
4077
+ try {
4078
+ await handleNotifyCommand(args.slice(1));
4079
+ } catch (error) {
4080
+ console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Unknown error");
4081
+ if (process.env.DEBUG) {
4082
+ console.error(error);
4083
+ }
4084
+ process.exit(1);
4085
+ }
4086
+ return;
4072
4087
  } else if (subcommand === "daemon") {
4073
4088
  const daemonSubcommand = args[1];
4074
4089
  if (daemonSubcommand === "start") {
@@ -4149,6 +4164,7 @@ ${chalk.bold("happy")} - Claude Code On the Go
4149
4164
 
4150
4165
  ${chalk.bold("Usage:")}
4151
4166
  happy [options]
4167
+ happy notify Send notification
4152
4168
  happy logout Logs out of your account and removes data directory
4153
4169
  happy daemon Manage the background daemon (macOS only)
4154
4170
 
@@ -4181,6 +4197,7 @@ ${chalk.bold("Examples:")}
4181
4197
  happy -m opus Use Claude Opus model
4182
4198
  happy -p plan Use plan permission mode
4183
4199
  happy --auth Force re-authentication before starting session
4200
+ happy notify -p "Hello!" Send notification
4184
4201
  happy --claude-env KEY=VALUE
4185
4202
  Set environment variable for Claude Code
4186
4203
  happy --claude-arg --option
@@ -4295,3 +4312,71 @@ async function cleanKey() {
4295
4312
  console.log(chalk.blue("Operation cancelled"));
4296
4313
  }
4297
4314
  }
4315
+ async function handleNotifyCommand(args) {
4316
+ let message = "";
4317
+ let title = "";
4318
+ let showHelp = false;
4319
+ for (let i = 0; i < args.length; i++) {
4320
+ const arg = args[i];
4321
+ if (arg === "-p" && i + 1 < args.length) {
4322
+ message = args[++i];
4323
+ } else if (arg === "-t" && i + 1 < args.length) {
4324
+ title = args[++i];
4325
+ } else if (arg === "-h" || arg === "--help") {
4326
+ showHelp = true;
4327
+ } else {
4328
+ console.error(chalk.red(`Unknown argument for notify command: ${arg}`));
4329
+ process.exit(1);
4330
+ }
4331
+ }
4332
+ if (showHelp) {
4333
+ console.log(`
4334
+ ${chalk.bold("happy notify")} - Send notification
4335
+
4336
+ ${chalk.bold("Usage:")}
4337
+ happy notify -p <message> [-t <title>] Send notification with custom message and optional title
4338
+ happy notify -h, --help Show this help
4339
+
4340
+ ${chalk.bold("Options:")}
4341
+ -p <message> Notification message (required)
4342
+ -t <title> Notification title (optional, defaults to "Happy")
4343
+
4344
+ ${chalk.bold("Examples:")}
4345
+ happy notify -p "Deployment complete!"
4346
+ happy notify -p "System update complete" -t "Server Status"
4347
+ happy notify -t "Alert" -p "Database connection restored"
4348
+ `);
4349
+ return;
4350
+ }
4351
+ if (!message) {
4352
+ console.error(chalk.red('Error: Message is required. Use -p "your message" to specify the notification text.'));
4353
+ console.log(chalk.gray('Run "happy notify --help" for usage information.'));
4354
+ process.exit(1);
4355
+ }
4356
+ let credentials = await readCredentials();
4357
+ if (!credentials) {
4358
+ console.error(chalk.red('Error: Not authenticated. Please run "happy --auth" first.'));
4359
+ process.exit(1);
4360
+ }
4361
+ console.log(chalk.blue("\u{1F4F1} Sending push notification..."));
4362
+ try {
4363
+ const api = new ApiClient(credentials.token, credentials.secret);
4364
+ const notificationTitle = title || "Happy";
4365
+ api.push().sendToAllDevices(
4366
+ notificationTitle,
4367
+ message,
4368
+ {
4369
+ source: "cli",
4370
+ timestamp: Date.now()
4371
+ }
4372
+ );
4373
+ console.log(chalk.green("\u2713 Push notification sent successfully!"));
4374
+ console.log(chalk.gray(` Title: ${notificationTitle}`));
4375
+ console.log(chalk.gray(` Message: ${message}`));
4376
+ console.log(chalk.gray(" Check your mobile device for the notification."));
4377
+ await new Promise((resolve) => setTimeout(resolve, 1e3));
4378
+ } catch (error) {
4379
+ console.error(chalk.red("\u2717 Failed to send push notification"));
4380
+ throw error;
4381
+ }
4382
+ }
package/dist/lib.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var types = require('./types-Bkw2UUhb.cjs');
3
+ var types = require('./types-iMUxaPkI.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-Cqy5Dx2C.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-DKVMGtcN.mjs';
2
2
  import 'axios';
3
3
  import 'chalk';
4
4
  import 'fs';
@@ -692,6 +692,9 @@ class PushNotificationClient {
692
692
  }
693
693
  );
694
694
  logger.debug(`Fetched ${response.data.tokens.length} push tokens`);
695
+ response.data.tokens.forEach((token, index) => {
696
+ logger.debug(`[PUSH] Token ${index + 1}: id=${token.id}, token=${token.token}, created=${new Date(token.createdAt).toISOString()}, updated=${new Date(token.updatedAt).toISOString()}`);
697
+ });
695
698
  return response.data.tokens;
696
699
  } catch (error) {
697
700
  logger.debug("[PUSH] [ERROR] Failed to fetch push tokens:", error);
@@ -724,7 +727,8 @@ class PushNotificationClient {
724
727
  const ticketChunk = await this.expo.sendPushNotificationsAsync(chunk);
725
728
  const errors = ticketChunk.filter((ticket) => ticket.status === "error");
726
729
  if (errors.length > 0) {
727
- logger.debug("[PUSH] Some notifications failed:", errors);
730
+ const errorDetails = errors.map((e) => ({ message: e.message, details: e.details }));
731
+ logger.debug("[PUSH] Some notifications failed:", errorDetails);
728
732
  }
729
733
  if (errors.length === ticketChunk.length) {
730
734
  throw new Error("All push notifications in chunk failed");
@@ -756,22 +760,33 @@ class PushNotificationClient {
756
760
  * @param data - Additional data to send with the notification
757
761
  */
758
762
  sendToAllDevices(title, body, data) {
763
+ logger.debug(`[PUSH] sendToAllDevices called with title: "${title}", body: "${body}"`);
759
764
  (async () => {
760
765
  try {
766
+ logger.debug("[PUSH] Fetching push tokens...");
761
767
  const tokens = await this.fetchPushTokens();
768
+ logger.debug(`[PUSH] Fetched ${tokens.length} push tokens`);
769
+ tokens.forEach((token, index) => {
770
+ logger.debug(`[PUSH] Using token ${index + 1}: id=${token.id}, token=${token.token}`);
771
+ });
762
772
  if (tokens.length === 0) {
763
773
  logger.debug("No push tokens found for user");
764
774
  return;
765
775
  }
766
- const messages = tokens.map((token) => ({
767
- to: token.token,
768
- title,
769
- body,
770
- data,
771
- sound: "default",
772
- priority: "high"
773
- }));
776
+ const messages = tokens.map((token, index) => {
777
+ logger.debug(`[PUSH] Creating message ${index + 1} for token: ${token.token}`);
778
+ return {
779
+ to: token.token,
780
+ title,
781
+ body,
782
+ data,
783
+ sound: "default",
784
+ priority: "high"
785
+ };
786
+ });
787
+ logger.debug(`[PUSH] Sending ${messages.length} push notifications...`);
774
788
  await this.sendPushNotifications(messages);
789
+ logger.debug("[PUSH] Push notifications sent successfully");
775
790
  } catch (error) {
776
791
  logger.debug("[PUSH] Error sending to all devices:", error);
777
792
  }
@@ -694,6 +694,9 @@ class PushNotificationClient {
694
694
  }
695
695
  );
696
696
  exports.logger.debug(`Fetched ${response.data.tokens.length} push tokens`);
697
+ response.data.tokens.forEach((token, index) => {
698
+ exports.logger.debug(`[PUSH] Token ${index + 1}: id=${token.id}, token=${token.token}, created=${new Date(token.createdAt).toISOString()}, updated=${new Date(token.updatedAt).toISOString()}`);
699
+ });
697
700
  return response.data.tokens;
698
701
  } catch (error) {
699
702
  exports.logger.debug("[PUSH] [ERROR] Failed to fetch push tokens:", error);
@@ -726,7 +729,8 @@ class PushNotificationClient {
726
729
  const ticketChunk = await this.expo.sendPushNotificationsAsync(chunk);
727
730
  const errors = ticketChunk.filter((ticket) => ticket.status === "error");
728
731
  if (errors.length > 0) {
729
- exports.logger.debug("[PUSH] Some notifications failed:", errors);
732
+ const errorDetails = errors.map((e) => ({ message: e.message, details: e.details }));
733
+ exports.logger.debug("[PUSH] Some notifications failed:", errorDetails);
730
734
  }
731
735
  if (errors.length === ticketChunk.length) {
732
736
  throw new Error("All push notifications in chunk failed");
@@ -758,22 +762,33 @@ class PushNotificationClient {
758
762
  * @param data - Additional data to send with the notification
759
763
  */
760
764
  sendToAllDevices(title, body, data) {
765
+ exports.logger.debug(`[PUSH] sendToAllDevices called with title: "${title}", body: "${body}"`);
761
766
  (async () => {
762
767
  try {
768
+ exports.logger.debug("[PUSH] Fetching push tokens...");
763
769
  const tokens = await this.fetchPushTokens();
770
+ exports.logger.debug(`[PUSH] Fetched ${tokens.length} push tokens`);
771
+ tokens.forEach((token, index) => {
772
+ exports.logger.debug(`[PUSH] Using token ${index + 1}: id=${token.id}, token=${token.token}`);
773
+ });
764
774
  if (tokens.length === 0) {
765
775
  exports.logger.debug("No push tokens found for user");
766
776
  return;
767
777
  }
768
- const messages = tokens.map((token) => ({
769
- to: token.token,
770
- title,
771
- body,
772
- data,
773
- sound: "default",
774
- priority: "high"
775
- }));
778
+ const messages = tokens.map((token, index) => {
779
+ exports.logger.debug(`[PUSH] Creating message ${index + 1} for token: ${token.token}`);
780
+ return {
781
+ to: token.token,
782
+ title,
783
+ body,
784
+ data,
785
+ sound: "default",
786
+ priority: "high"
787
+ };
788
+ });
789
+ exports.logger.debug(`[PUSH] Sending ${messages.length} push notifications...`);
776
790
  await this.sendPushNotifications(messages);
791
+ exports.logger.debug("[PUSH] Push notifications sent successfully");
777
792
  } catch (error) {
778
793
  exports.logger.debug("[PUSH] Error sending to all devices:", error);
779
794
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "happy-coder",
3
- "version": "0.6.0",
3
+ "version": "0.6.2",
4
4
  "description": "Claude Code session sharing CLI",
5
5
  "author": "Kirill Dubovitskiy",
6
6
  "license": "MIT",
@@ -1,6 +1,9 @@
1
1
  const crypto = require('crypto');
2
2
  const fs = require('fs');
3
3
 
4
+ // Disable autoupdater (never works really)
5
+ process.env.DISABLE_AUTOUPDATER = '1';
6
+
4
7
  // Helper to write JSON messages to fd 3
5
8
  function writeMessage(message) {
6
9
  try {