happy-coder 0.9.0 → 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 CHANGED
@@ -3,7 +3,7 @@
3
3
  var chalk = require('chalk');
4
4
  var os = require('node:os');
5
5
  var node_crypto = require('node:crypto');
6
- var types = require('./types-CyOnnZ8M.cjs');
6
+ var types = require('./types-DNUk09Np.cjs');
7
7
  var node_child_process = require('node:child_process');
8
8
  var node_path = require('node:path');
9
9
  var node_readline = require('node:readline');
@@ -43,6 +43,7 @@ class Session {
43
43
  queue;
44
44
  claudeEnvVars;
45
45
  claudeArgs;
46
+ // Made mutable to allow filtering
46
47
  mcpServers;
47
48
  allowedTools;
48
49
  _onModeChange;
@@ -77,6 +78,11 @@ class Session {
77
78
  };
78
79
  onSessionFound = (sessionId) => {
79
80
  this.sessionId = sessionId;
81
+ this.client.updateMetadata((metadata) => ({
82
+ ...metadata,
83
+ claudeSessionId: sessionId
84
+ }));
85
+ types.logger.debug(`[Session] Claude Code session ID ${sessionId} added to metadata`);
80
86
  };
81
87
  /**
82
88
  * Clear the current session ID (used by /clear command)
@@ -85,6 +91,33 @@ class Session {
85
91
  this.sessionId = null;
86
92
  types.logger.debug("[Session] Session ID cleared");
87
93
  };
94
+ /**
95
+ * Consume one-time Claude flags from claudeArgs after Claude spawn
96
+ * Currently handles: --resume (with or without session ID)
97
+ */
98
+ consumeOneTimeFlags = () => {
99
+ if (!this.claudeArgs) return;
100
+ const filteredArgs = [];
101
+ for (let i = 0; i < this.claudeArgs.length; i++) {
102
+ if (this.claudeArgs[i] === "--resume") {
103
+ if (i + 1 < this.claudeArgs.length) {
104
+ const nextArg = this.claudeArgs[i + 1];
105
+ if (!nextArg.startsWith("-") && nextArg.includes("-")) {
106
+ i++;
107
+ types.logger.debug(`[Session] Consumed --resume flag with session ID: ${nextArg}`);
108
+ } else {
109
+ types.logger.debug("[Session] Consumed --resume flag (no session ID)");
110
+ }
111
+ } else {
112
+ types.logger.debug("[Session] Consumed --resume flag (no session ID)");
113
+ }
114
+ } else {
115
+ filteredArgs.push(this.claudeArgs[i]);
116
+ }
117
+ }
118
+ this.claudeArgs = filteredArgs.length > 0 ? filteredArgs : void 0;
119
+ types.logger.debug(`[Session] Consumed one-time flags, remaining args:`, this.claudeArgs);
120
+ };
88
121
  }
89
122
 
90
123
  function getProjectPath(workingDirectory) {
@@ -139,6 +172,16 @@ function trimIdent(text) {
139
172
 
140
173
  const systemPrompt = trimIdent(`
141
174
  ALWAYS when you start a new chat - you must call a tool "mcp__happy__change_title" to set a chat title. When you think chat title is not relevant anymore - call the tool again to change it. When chat name is too generic and you have a change to make it more specific - call the tool again to change it. This title is needed to easily find the chat in the future. Help human.
175
+
176
+ When making commit messages, instead of just giving co-credit to Claude, also give credit to Happy like so:
177
+
178
+ <main commit message>
179
+
180
+ Generated with [Claude Code](https://claude.ai/code)
181
+ via [Happy](https://happy.engineering)
182
+
183
+ Co-Authored-By: Claude <noreply@anthropic.com>
184
+ Co-Authored-By: Happy <yesreply@happy.engineering>
142
185
  `);
143
186
 
144
187
  node_path.dirname(node_url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href))));
@@ -598,6 +641,7 @@ async function claudeLocalLauncher(session) {
598
641
  mcpServers: session.mcpServers,
599
642
  allowedTools: session.allowedTools
600
643
  });
644
+ session.consumeOneTimeFlags();
601
645
  if (!exitReason) {
602
646
  exitReason = "exit";
603
647
  break;
@@ -1401,6 +1445,26 @@ async function claudeRemote(opts) {
1401
1445
  if (opts.sessionId && !claudeCheckSession(opts.sessionId, opts.path)) {
1402
1446
  startFrom = null;
1403
1447
  }
1448
+ if (!startFrom && opts.claudeArgs) {
1449
+ for (let i = 0; i < opts.claudeArgs.length; i++) {
1450
+ if (opts.claudeArgs[i] === "--resume") {
1451
+ if (i + 1 < opts.claudeArgs.length) {
1452
+ const nextArg = opts.claudeArgs[i + 1];
1453
+ if (!nextArg.startsWith("-") && nextArg.includes("-")) {
1454
+ startFrom = nextArg;
1455
+ types.logger.debug(`[claudeRemote] Found --resume with session ID: ${startFrom}`);
1456
+ break;
1457
+ } else {
1458
+ types.logger.debug("[claudeRemote] Found --resume without session ID - not supported in remote mode");
1459
+ break;
1460
+ }
1461
+ } else {
1462
+ types.logger.debug("[claudeRemote] Found --resume without session ID - not supported in remote mode");
1463
+ break;
1464
+ }
1465
+ }
1466
+ }
1467
+ }
1404
1468
  if (opts.claudeEnvVars) {
1405
1469
  Object.entries(opts.claudeEnvVars).forEach(([key, value]) => {
1406
1470
  process.env[key] = value;
@@ -2589,7 +2653,7 @@ async function claudeRemoteLauncher(session) {
2589
2653
  let modeHash = null;
2590
2654
  let mode = null;
2591
2655
  try {
2592
- await claudeRemote({
2656
+ const remoteResult = await claudeRemote({
2593
2657
  sessionId: session.sessionId,
2594
2658
  path: session.path,
2595
2659
  allowedTools: session.allowedTools ?? [],
@@ -2650,6 +2714,7 @@ async function claudeRemoteLauncher(session) {
2650
2714
  },
2651
2715
  signal: abortController.signal
2652
2716
  });
2717
+ session.consumeOneTimeFlags();
2653
2718
  if (!exitReason && abortController.signal.aborted) {
2654
2719
  session.client.sendSessionEvent({ type: "message", message: "Aborted by user" });
2655
2720
  }
@@ -4772,7 +4837,10 @@ async function start(credentials, options = {}) {
4772
4837
  happyHomeDir: types.configuration.happyHomeDir,
4773
4838
  startedFromDaemon: options.startedBy === "daemon",
4774
4839
  hostPid: process.pid,
4775
- startedBy: options.startedBy || "terminal"
4840
+ startedBy: options.startedBy || "terminal",
4841
+ // Initialize lifecycle state
4842
+ lifecycleState: "running",
4843
+ lifecycleStateSince: Date.now()
4776
4844
  };
4777
4845
  const response = await api.getOrCreateSession({ tag: sessionTag, metadata, state });
4778
4846
  types.logger.debug(`Session created: ${response.id}`);
@@ -4940,6 +5008,13 @@ async function start(credentials, options = {}) {
4940
5008
  types.logger.debug("[START] Received termination signal, cleaning up...");
4941
5009
  try {
4942
5010
  if (session) {
5011
+ session.updateMetadata((currentMetadata) => ({
5012
+ ...currentMetadata,
5013
+ lifecycleState: "archived",
5014
+ lifecycleStateSince: Date.now(),
5015
+ archivedBy: "cli",
5016
+ archiveReason: "User terminated"
5017
+ }));
4943
5018
  session.sendSessionDeath();
4944
5019
  await session.flush();
4945
5020
  await session.close();
@@ -5167,19 +5242,12 @@ ${chalk.bold("Usage:")}
5167
5242
  happy auth login [--force] Authenticate with Happy
5168
5243
  happy auth logout Remove authentication and machine data
5169
5244
  happy auth status Show authentication status
5170
- happy auth show-backup Display backup key for mobile/web clients
5245
+ happy auth backup Display backup key for mobile/web clients
5171
5246
  happy auth help Show this help message
5172
5247
 
5173
5248
  ${chalk.bold("Options:")}
5174
5249
  --force Clear credentials, machine ID, and stop daemon before re-auth
5175
5250
 
5176
- ${chalk.bold("Examples:")}
5177
- happy auth login Authenticate if not already logged in
5178
- happy auth login --force Force re-authentication (complete reset)
5179
- happy auth status Check authentication and machine status
5180
- happy auth show-backup Get backup key to link other devices
5181
- happy auth logout Remove all authentication data
5182
-
5183
5251
  ${chalk.bold("Notes:")}
5184
5252
  \u2022 Use 'auth login --force' when you need to re-register your machine
5185
5253
  \u2022 'auth show-backup' displays the key format expected by mobile/web clients
@@ -5519,7 +5587,7 @@ ${chalk.bold("To clean up runaway processes:")} Use ${chalk.cyan("happy doctor c
5519
5587
  } else if (arg === "-v" || arg === "--version") {
5520
5588
  showVersion = true;
5521
5589
  unknownArgs.push(arg);
5522
- } else if (arg === "--auth" || arg === "--login") ; else if (arg === "--force-auth") ; else if (arg === "--happy-starting-mode") {
5590
+ } else if (arg === "--happy-starting-mode") {
5523
5591
  options.startingMode = z.z.enum(["local", "remote"]).parse(args[++i]);
5524
5592
  } else if (arg === "--yolo") {
5525
5593
  unknownArgs.push("--dangerously-skip-permissions");
@@ -5548,15 +5616,15 @@ ${chalk.bold("Usage:")}
5548
5616
 
5549
5617
  ${chalk.bold("Examples:")}
5550
5618
  happy Start session
5551
- happy --yolo Bypass permissions
5552
- happy --verbose Enable verbose mode
5619
+ happy --yolo Start with bypassing permissions
5620
+ happy sugar for --dangerously-skip-permissions
5553
5621
  happy auth login --force Authenticate
5554
5622
  happy doctor Run diagnostics
5555
5623
 
5556
- ${chalk.bold("Happy is a wrapper around Claude Code that enables remote control via mobile app.")}
5557
-
5558
5624
  ${chalk.bold("Happy supports ALL Claude options!")}
5559
- Use any claude flag exactly as you normally would.
5625
+ Use any claude flag with happy as you would with claude. Our favorite:
5626
+
5627
+ happy --resume
5560
5628
 
5561
5629
  ${chalk.gray("\u2500".repeat(60))}
5562
5630
  ${chalk.bold.cyan("Claude Code Options (from `claude --help`):")}
@@ -5571,8 +5639,7 @@ ${chalk.bold.cyan("Claude Code Options (from `claude --help`):")}
5571
5639
  process.exit(0);
5572
5640
  }
5573
5641
  if (showVersion) {
5574
- console.log(types.packageJson.version);
5575
- process.exit(0);
5642
+ console.log(`happy version: ${types.packageJson.version}`);
5576
5643
  }
5577
5644
  const {
5578
5645
  credentials
package/dist/index.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import chalk from 'chalk';
2
2
  import os$1, { homedir } from 'node:os';
3
3
  import { randomUUID, randomBytes } from 'node:crypto';
4
- import { l as logger, b as backoff, d as delay, R as RawJSONLinesSchema, e as AsyncLock, r as readDaemonState, c as configuration, f as clearDaemonState, p as packageJson, g as readSettings, h as readCredentials, i as encodeBase64, u as updateSettings, j as encodeBase64Url, k as decodeBase64, w as writeCredentials, m as acquireDaemonLock, n as writeDaemonState, A as ApiClient, o as releaseDaemonLock, q as clearCredentials, s as clearMachineId, t as getLatestDaemonLog } from './types-Cezp_n6O.mjs';
4
+ import { l as logger, b as backoff, d as delay, R as RawJSONLinesSchema, e as AsyncLock, r as readDaemonState, c as configuration, f as clearDaemonState, p as packageJson, g as readSettings, h as readCredentials, i as encodeBase64, u as updateSettings, j as encodeBase64Url, k as decodeBase64, w as writeCredentials, m as acquireDaemonLock, n as writeDaemonState, A as ApiClient, o as releaseDaemonLock, q as clearCredentials, s as clearMachineId, t as getLatestDaemonLog } from './types-BS8Pr3Im.mjs';
5
5
  import { spawn, execSync } from 'node:child_process';
6
6
  import { resolve, join, dirname as dirname$1 } from 'node:path';
7
7
  import { createInterface } from 'node:readline';
@@ -40,6 +40,7 @@ class Session {
40
40
  queue;
41
41
  claudeEnvVars;
42
42
  claudeArgs;
43
+ // Made mutable to allow filtering
43
44
  mcpServers;
44
45
  allowedTools;
45
46
  _onModeChange;
@@ -74,6 +75,11 @@ class Session {
74
75
  };
75
76
  onSessionFound = (sessionId) => {
76
77
  this.sessionId = sessionId;
78
+ this.client.updateMetadata((metadata) => ({
79
+ ...metadata,
80
+ claudeSessionId: sessionId
81
+ }));
82
+ logger.debug(`[Session] Claude Code session ID ${sessionId} added to metadata`);
77
83
  };
78
84
  /**
79
85
  * Clear the current session ID (used by /clear command)
@@ -82,6 +88,33 @@ class Session {
82
88
  this.sessionId = null;
83
89
  logger.debug("[Session] Session ID cleared");
84
90
  };
91
+ /**
92
+ * Consume one-time Claude flags from claudeArgs after Claude spawn
93
+ * Currently handles: --resume (with or without session ID)
94
+ */
95
+ consumeOneTimeFlags = () => {
96
+ if (!this.claudeArgs) return;
97
+ const filteredArgs = [];
98
+ for (let i = 0; i < this.claudeArgs.length; i++) {
99
+ if (this.claudeArgs[i] === "--resume") {
100
+ if (i + 1 < this.claudeArgs.length) {
101
+ const nextArg = this.claudeArgs[i + 1];
102
+ if (!nextArg.startsWith("-") && nextArg.includes("-")) {
103
+ i++;
104
+ logger.debug(`[Session] Consumed --resume flag with session ID: ${nextArg}`);
105
+ } else {
106
+ logger.debug("[Session] Consumed --resume flag (no session ID)");
107
+ }
108
+ } else {
109
+ logger.debug("[Session] Consumed --resume flag (no session ID)");
110
+ }
111
+ } else {
112
+ filteredArgs.push(this.claudeArgs[i]);
113
+ }
114
+ }
115
+ this.claudeArgs = filteredArgs.length > 0 ? filteredArgs : void 0;
116
+ logger.debug(`[Session] Consumed one-time flags, remaining args:`, this.claudeArgs);
117
+ };
85
118
  }
86
119
 
87
120
  function getProjectPath(workingDirectory) {
@@ -136,6 +169,16 @@ function trimIdent(text) {
136
169
 
137
170
  const systemPrompt = trimIdent(`
138
171
  ALWAYS when you start a new chat - you must call a tool "mcp__happy__change_title" to set a chat title. When you think chat title is not relevant anymore - call the tool again to change it. When chat name is too generic and you have a change to make it more specific - call the tool again to change it. This title is needed to easily find the chat in the future. Help human.
172
+
173
+ When making commit messages, instead of just giving co-credit to Claude, also give credit to Happy like so:
174
+
175
+ <main commit message>
176
+
177
+ Generated with [Claude Code](https://claude.ai/code)
178
+ via [Happy](https://happy.engineering)
179
+
180
+ Co-Authored-By: Claude <noreply@anthropic.com>
181
+ Co-Authored-By: Happy <yesreply@happy.engineering>
139
182
  `);
140
183
 
141
184
  dirname$1(fileURLToPath$1(import.meta.url));
@@ -595,6 +638,7 @@ async function claudeLocalLauncher(session) {
595
638
  mcpServers: session.mcpServers,
596
639
  allowedTools: session.allowedTools
597
640
  });
641
+ session.consumeOneTimeFlags();
598
642
  if (!exitReason) {
599
643
  exitReason = "exit";
600
644
  break;
@@ -1398,6 +1442,26 @@ async function claudeRemote(opts) {
1398
1442
  if (opts.sessionId && !claudeCheckSession(opts.sessionId, opts.path)) {
1399
1443
  startFrom = null;
1400
1444
  }
1445
+ if (!startFrom && opts.claudeArgs) {
1446
+ for (let i = 0; i < opts.claudeArgs.length; i++) {
1447
+ if (opts.claudeArgs[i] === "--resume") {
1448
+ if (i + 1 < opts.claudeArgs.length) {
1449
+ const nextArg = opts.claudeArgs[i + 1];
1450
+ if (!nextArg.startsWith("-") && nextArg.includes("-")) {
1451
+ startFrom = nextArg;
1452
+ logger.debug(`[claudeRemote] Found --resume with session ID: ${startFrom}`);
1453
+ break;
1454
+ } else {
1455
+ logger.debug("[claudeRemote] Found --resume without session ID - not supported in remote mode");
1456
+ break;
1457
+ }
1458
+ } else {
1459
+ logger.debug("[claudeRemote] Found --resume without session ID - not supported in remote mode");
1460
+ break;
1461
+ }
1462
+ }
1463
+ }
1464
+ }
1401
1465
  if (opts.claudeEnvVars) {
1402
1466
  Object.entries(opts.claudeEnvVars).forEach(([key, value]) => {
1403
1467
  process.env[key] = value;
@@ -2586,7 +2650,7 @@ async function claudeRemoteLauncher(session) {
2586
2650
  let modeHash = null;
2587
2651
  let mode = null;
2588
2652
  try {
2589
- await claudeRemote({
2653
+ const remoteResult = await claudeRemote({
2590
2654
  sessionId: session.sessionId,
2591
2655
  path: session.path,
2592
2656
  allowedTools: session.allowedTools ?? [],
@@ -2647,6 +2711,7 @@ async function claudeRemoteLauncher(session) {
2647
2711
  },
2648
2712
  signal: abortController.signal
2649
2713
  });
2714
+ session.consumeOneTimeFlags();
2650
2715
  if (!exitReason && abortController.signal.aborted) {
2651
2716
  session.client.sendSessionEvent({ type: "message", message: "Aborted by user" });
2652
2717
  }
@@ -4769,7 +4834,10 @@ async function start(credentials, options = {}) {
4769
4834
  happyHomeDir: configuration.happyHomeDir,
4770
4835
  startedFromDaemon: options.startedBy === "daemon",
4771
4836
  hostPid: process.pid,
4772
- startedBy: options.startedBy || "terminal"
4837
+ startedBy: options.startedBy || "terminal",
4838
+ // Initialize lifecycle state
4839
+ lifecycleState: "running",
4840
+ lifecycleStateSince: Date.now()
4773
4841
  };
4774
4842
  const response = await api.getOrCreateSession({ tag: sessionTag, metadata, state });
4775
4843
  logger.debug(`Session created: ${response.id}`);
@@ -4937,6 +5005,13 @@ async function start(credentials, options = {}) {
4937
5005
  logger.debug("[START] Received termination signal, cleaning up...");
4938
5006
  try {
4939
5007
  if (session) {
5008
+ session.updateMetadata((currentMetadata) => ({
5009
+ ...currentMetadata,
5010
+ lifecycleState: "archived",
5011
+ lifecycleStateSince: Date.now(),
5012
+ archivedBy: "cli",
5013
+ archiveReason: "User terminated"
5014
+ }));
4940
5015
  session.sendSessionDeath();
4941
5016
  await session.flush();
4942
5017
  await session.close();
@@ -5164,19 +5239,12 @@ ${chalk.bold("Usage:")}
5164
5239
  happy auth login [--force] Authenticate with Happy
5165
5240
  happy auth logout Remove authentication and machine data
5166
5241
  happy auth status Show authentication status
5167
- happy auth show-backup Display backup key for mobile/web clients
5242
+ happy auth backup Display backup key for mobile/web clients
5168
5243
  happy auth help Show this help message
5169
5244
 
5170
5245
  ${chalk.bold("Options:")}
5171
5246
  --force Clear credentials, machine ID, and stop daemon before re-auth
5172
5247
 
5173
- ${chalk.bold("Examples:")}
5174
- happy auth login Authenticate if not already logged in
5175
- happy auth login --force Force re-authentication (complete reset)
5176
- happy auth status Check authentication and machine status
5177
- happy auth show-backup Get backup key to link other devices
5178
- happy auth logout Remove all authentication data
5179
-
5180
5248
  ${chalk.bold("Notes:")}
5181
5249
  \u2022 Use 'auth login --force' when you need to re-register your machine
5182
5250
  \u2022 'auth show-backup' displays the key format expected by mobile/web clients
@@ -5516,7 +5584,7 @@ ${chalk.bold("To clean up runaway processes:")} Use ${chalk.cyan("happy doctor c
5516
5584
  } else if (arg === "-v" || arg === "--version") {
5517
5585
  showVersion = true;
5518
5586
  unknownArgs.push(arg);
5519
- } else if (arg === "--auth" || arg === "--login") ; else if (arg === "--force-auth") ; else if (arg === "--happy-starting-mode") {
5587
+ } else if (arg === "--happy-starting-mode") {
5520
5588
  options.startingMode = z.enum(["local", "remote"]).parse(args[++i]);
5521
5589
  } else if (arg === "--yolo") {
5522
5590
  unknownArgs.push("--dangerously-skip-permissions");
@@ -5545,15 +5613,15 @@ ${chalk.bold("Usage:")}
5545
5613
 
5546
5614
  ${chalk.bold("Examples:")}
5547
5615
  happy Start session
5548
- happy --yolo Bypass permissions
5549
- happy --verbose Enable verbose mode
5616
+ happy --yolo Start with bypassing permissions
5617
+ happy sugar for --dangerously-skip-permissions
5550
5618
  happy auth login --force Authenticate
5551
5619
  happy doctor Run diagnostics
5552
5620
 
5553
- ${chalk.bold("Happy is a wrapper around Claude Code that enables remote control via mobile app.")}
5554
-
5555
5621
  ${chalk.bold("Happy supports ALL Claude options!")}
5556
- Use any claude flag exactly as you normally would.
5622
+ Use any claude flag with happy as you would with claude. Our favorite:
5623
+
5624
+ happy --resume
5557
5625
 
5558
5626
  ${chalk.gray("\u2500".repeat(60))}
5559
5627
  ${chalk.bold.cyan("Claude Code Options (from `claude --help`):")}
@@ -5568,8 +5636,7 @@ ${chalk.bold.cyan("Claude Code Options (from `claude --help`):")}
5568
5636
  process.exit(0);
5569
5637
  }
5570
5638
  if (showVersion) {
5571
- console.log(packageJson.version);
5572
- process.exit(0);
5639
+ console.log(`happy version: ${packageJson.version}`);
5573
5640
  }
5574
5641
  const {
5575
5642
  credentials
package/dist/lib.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var types = require('./types-CyOnnZ8M.cjs');
3
+ var types = require('./types-DNUk09Np.cjs');
4
4
  require('axios');
5
5
  require('chalk');
6
6
  require('fs');
package/dist/lib.d.cts CHANGED
@@ -379,6 +379,12 @@ declare const SessionSchema: z.ZodObject<{
379
379
  metadataVersion: z.ZodNumber;
380
380
  agentState: z.ZodNullable<z.ZodAny>;
381
381
  agentStateVersion: z.ZodNumber;
382
+ connectivityStatus: z.ZodOptional<z.ZodUnion<[z.ZodEnum<["neverConnected", "online", "offline"]>, z.ZodString]>>;
383
+ connectivityStatusSince: z.ZodOptional<z.ZodNumber>;
384
+ connectivityStatusReason: z.ZodOptional<z.ZodString>;
385
+ state: z.ZodOptional<z.ZodUnion<[z.ZodEnum<["running", "archiveRequested", "archived"]>, z.ZodString]>>;
386
+ stateSince: z.ZodOptional<z.ZodNumber>;
387
+ stateReason: z.ZodOptional<z.ZodString>;
382
388
  }, "strip", z.ZodTypeAny, {
383
389
  id: string;
384
390
  seq: number;
@@ -388,6 +394,12 @@ declare const SessionSchema: z.ZodObject<{
388
394
  agentStateVersion: number;
389
395
  metadata?: any;
390
396
  agentState?: any;
397
+ connectivityStatus?: string | undefined;
398
+ connectivityStatusSince?: number | undefined;
399
+ connectivityStatusReason?: string | undefined;
400
+ state?: string | undefined;
401
+ stateSince?: number | undefined;
402
+ stateReason?: string | undefined;
391
403
  }, {
392
404
  id: string;
393
405
  seq: number;
@@ -397,6 +409,12 @@ declare const SessionSchema: z.ZodObject<{
397
409
  agentStateVersion: number;
398
410
  metadata?: any;
399
411
  agentState?: any;
412
+ connectivityStatus?: string | undefined;
413
+ connectivityStatusSince?: number | undefined;
414
+ connectivityStatusReason?: string | undefined;
415
+ state?: string | undefined;
416
+ stateSince?: number | undefined;
417
+ stateReason?: string | undefined;
400
418
  }>;
401
419
  type Session = z.infer<typeof SessionSchema>;
402
420
  /**
@@ -461,6 +479,12 @@ declare const MachineSchema: z.ZodObject<{
461
479
  activeAt: z.ZodNumber;
462
480
  createdAt: z.ZodNumber;
463
481
  updatedAt: z.ZodNumber;
482
+ connectivityStatus: z.ZodOptional<z.ZodUnion<[z.ZodEnum<["neverConnected", "online", "offline"]>, z.ZodString]>>;
483
+ connectivityStatusSince: z.ZodOptional<z.ZodNumber>;
484
+ connectivityStatusReason: z.ZodOptional<z.ZodString>;
485
+ state: z.ZodOptional<z.ZodUnion<[z.ZodEnum<["running", "archiveRequested", "archived"]>, z.ZodString]>>;
486
+ stateSince: z.ZodOptional<z.ZodNumber>;
487
+ stateReason: z.ZodOptional<z.ZodString>;
464
488
  }, "strip", z.ZodTypeAny, {
465
489
  id: string;
466
490
  createdAt: number;
@@ -471,6 +495,12 @@ declare const MachineSchema: z.ZodObject<{
471
495
  activeAt: number;
472
496
  metadata?: any;
473
497
  daemonState?: any;
498
+ connectivityStatus?: string | undefined;
499
+ connectivityStatusSince?: number | undefined;
500
+ connectivityStatusReason?: string | undefined;
501
+ state?: string | undefined;
502
+ stateSince?: number | undefined;
503
+ stateReason?: string | undefined;
474
504
  }, {
475
505
  id: string;
476
506
  createdAt: number;
@@ -481,6 +511,12 @@ declare const MachineSchema: z.ZodObject<{
481
511
  activeAt: number;
482
512
  metadata?: any;
483
513
  daemonState?: any;
514
+ connectivityStatus?: string | undefined;
515
+ connectivityStatusSince?: number | undefined;
516
+ connectivityStatusReason?: string | undefined;
517
+ state?: string | undefined;
518
+ stateSince?: number | undefined;
519
+ stateReason?: string | undefined;
484
520
  }>;
485
521
  type Machine = z.infer<typeof MachineSchema>;
486
522
  declare const UserMessageSchema: z.ZodObject<{
@@ -571,6 +607,7 @@ type Metadata = {
571
607
  updatedAt: number;
572
608
  };
573
609
  machineId?: string;
610
+ claudeSessionId?: string;
574
611
  tools?: string[];
575
612
  slashCommands?: string[];
576
613
  homeDir?: string;
@@ -578,6 +615,10 @@ type Metadata = {
578
615
  startedFromDaemon?: boolean;
579
616
  hostPid?: number;
580
617
  startedBy?: 'daemon' | 'terminal';
618
+ lifecycleState?: 'running' | 'archiveRequested' | 'archived' | string;
619
+ lifecycleStateSince?: number;
620
+ archivedBy?: string;
621
+ archiveReason?: string;
581
622
  };
582
623
  type AgentState = {
583
624
  controlledByUser?: boolean | null | undefined;
package/dist/lib.d.mts CHANGED
@@ -379,6 +379,12 @@ declare const SessionSchema: z.ZodObject<{
379
379
  metadataVersion: z.ZodNumber;
380
380
  agentState: z.ZodNullable<z.ZodAny>;
381
381
  agentStateVersion: z.ZodNumber;
382
+ connectivityStatus: z.ZodOptional<z.ZodUnion<[z.ZodEnum<["neverConnected", "online", "offline"]>, z.ZodString]>>;
383
+ connectivityStatusSince: z.ZodOptional<z.ZodNumber>;
384
+ connectivityStatusReason: z.ZodOptional<z.ZodString>;
385
+ state: z.ZodOptional<z.ZodUnion<[z.ZodEnum<["running", "archiveRequested", "archived"]>, z.ZodString]>>;
386
+ stateSince: z.ZodOptional<z.ZodNumber>;
387
+ stateReason: z.ZodOptional<z.ZodString>;
382
388
  }, "strip", z.ZodTypeAny, {
383
389
  id: string;
384
390
  seq: number;
@@ -388,6 +394,12 @@ declare const SessionSchema: z.ZodObject<{
388
394
  agentStateVersion: number;
389
395
  metadata?: any;
390
396
  agentState?: any;
397
+ connectivityStatus?: string | undefined;
398
+ connectivityStatusSince?: number | undefined;
399
+ connectivityStatusReason?: string | undefined;
400
+ state?: string | undefined;
401
+ stateSince?: number | undefined;
402
+ stateReason?: string | undefined;
391
403
  }, {
392
404
  id: string;
393
405
  seq: number;
@@ -397,6 +409,12 @@ declare const SessionSchema: z.ZodObject<{
397
409
  agentStateVersion: number;
398
410
  metadata?: any;
399
411
  agentState?: any;
412
+ connectivityStatus?: string | undefined;
413
+ connectivityStatusSince?: number | undefined;
414
+ connectivityStatusReason?: string | undefined;
415
+ state?: string | undefined;
416
+ stateSince?: number | undefined;
417
+ stateReason?: string | undefined;
400
418
  }>;
401
419
  type Session = z.infer<typeof SessionSchema>;
402
420
  /**
@@ -461,6 +479,12 @@ declare const MachineSchema: z.ZodObject<{
461
479
  activeAt: z.ZodNumber;
462
480
  createdAt: z.ZodNumber;
463
481
  updatedAt: z.ZodNumber;
482
+ connectivityStatus: z.ZodOptional<z.ZodUnion<[z.ZodEnum<["neverConnected", "online", "offline"]>, z.ZodString]>>;
483
+ connectivityStatusSince: z.ZodOptional<z.ZodNumber>;
484
+ connectivityStatusReason: z.ZodOptional<z.ZodString>;
485
+ state: z.ZodOptional<z.ZodUnion<[z.ZodEnum<["running", "archiveRequested", "archived"]>, z.ZodString]>>;
486
+ stateSince: z.ZodOptional<z.ZodNumber>;
487
+ stateReason: z.ZodOptional<z.ZodString>;
464
488
  }, "strip", z.ZodTypeAny, {
465
489
  id: string;
466
490
  createdAt: number;
@@ -471,6 +495,12 @@ declare const MachineSchema: z.ZodObject<{
471
495
  activeAt: number;
472
496
  metadata?: any;
473
497
  daemonState?: any;
498
+ connectivityStatus?: string | undefined;
499
+ connectivityStatusSince?: number | undefined;
500
+ connectivityStatusReason?: string | undefined;
501
+ state?: string | undefined;
502
+ stateSince?: number | undefined;
503
+ stateReason?: string | undefined;
474
504
  }, {
475
505
  id: string;
476
506
  createdAt: number;
@@ -481,6 +511,12 @@ declare const MachineSchema: z.ZodObject<{
481
511
  activeAt: number;
482
512
  metadata?: any;
483
513
  daemonState?: any;
514
+ connectivityStatus?: string | undefined;
515
+ connectivityStatusSince?: number | undefined;
516
+ connectivityStatusReason?: string | undefined;
517
+ state?: string | undefined;
518
+ stateSince?: number | undefined;
519
+ stateReason?: string | undefined;
484
520
  }>;
485
521
  type Machine = z.infer<typeof MachineSchema>;
486
522
  declare const UserMessageSchema: z.ZodObject<{
@@ -571,6 +607,7 @@ type Metadata = {
571
607
  updatedAt: number;
572
608
  };
573
609
  machineId?: string;
610
+ claudeSessionId?: string;
574
611
  tools?: string[];
575
612
  slashCommands?: string[];
576
613
  homeDir?: string;
@@ -578,6 +615,10 @@ type Metadata = {
578
615
  startedFromDaemon?: boolean;
579
616
  hostPid?: number;
580
617
  startedBy?: 'daemon' | 'terminal';
618
+ lifecycleState?: 'running' | 'archiveRequested' | 'archived' | string;
619
+ lifecycleStateSince?: number;
620
+ archivedBy?: string;
621
+ archiveReason?: string;
581
622
  };
582
623
  type AgentState = {
583
624
  controlledByUser?: boolean | null | undefined;
package/dist/lib.mjs CHANGED
@@ -1,4 +1,4 @@
1
- export { A as ApiClient, a as ApiSessionClient, R as RawJSONLinesSchema, c as configuration, l as logger } from './types-Cezp_n6O.mjs';
1
+ export { A as ApiClient, a as ApiSessionClient, R as RawJSONLinesSchema, c as configuration, l as logger } from './types-BS8Pr3Im.mjs';
2
2
  import 'axios';
3
3
  import 'chalk';
4
4
  import 'fs';
@@ -14,7 +14,7 @@ import { io } from 'socket.io-client';
14
14
  import { Expo } from 'expo-server-sdk';
15
15
 
16
16
  var name = "happy-coder";
17
- var version = "0.9.0";
17
+ var version = "0.9.1";
18
18
  var description = "Claude Code session sharing CLI";
19
19
  var author = "Kirill Dubovitskiy";
20
20
  var license = "MIT";
@@ -153,7 +153,7 @@ class Configuration {
153
153
  currentCliVersion;
154
154
  isExperimentalEnabled;
155
155
  constructor() {
156
- this.serverUrl = process.env.HAPPY_SERVER_URL || "https://handy-api.korshakov.org";
156
+ this.serverUrl = process.env.HAPPY_SERVER_URL || "https://api.cluster-fluster.com";
157
157
  const args = process.argv.slice(2);
158
158
  this.isDaemonProcess = args.length >= 2 && args[0] === "daemon" && args[1] === "start-sync";
159
159
  if (process.env.HAPPY_HOME_DIR) {
@@ -533,7 +533,7 @@ class Logger {
533
533
  appendFileSync(this.logFilePath, logLine);
534
534
  } catch (appendError) {
535
535
  if (process.env.DEBUG) {
536
- console.error("Failed to append to log file:", appendError);
536
+ console.error("[DEV MODE ONLY THROWING] Failed to append to log file:", appendError);
537
537
  throw appendError;
538
538
  }
539
539
  }
@@ -640,7 +640,23 @@ z$1.object({
640
640
  metadata: z$1.any(),
641
641
  metadataVersion: z$1.number(),
642
642
  agentState: z$1.any().nullable(),
643
- agentStateVersion: z$1.number()
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()
644
660
  });
645
661
  z$1.object({
646
662
  host: z$1.string(),
@@ -678,7 +694,23 @@ z$1.object({
678
694
  active: z$1.boolean(),
679
695
  activeAt: z$1.number(),
680
696
  createdAt: z$1.number(),
681
- updatedAt: 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()
682
714
  });
683
715
  z$1.object({
684
716
  content: SessionMessageContentSchema,
@@ -991,7 +1023,9 @@ class ApiSessionClient extends EventEmitter {
991
1023
  * Send a ping message to keep the connection alive
992
1024
  */
993
1025
  keepAlive(thinking, mode) {
994
- logger.debug(`[API] Sending keep alive message: ${thinking}`);
1026
+ if (process.env.DEBUG) {
1027
+ logger.debug(`[API] Sending keep alive message: ${thinking}`);
1028
+ }
995
1029
  this.socket.volatile.emit("session-alive", {
996
1030
  sid: this.sessionId,
997
1031
  time: Date.now(),
@@ -1340,7 +1374,9 @@ class ApiMachineClient {
1340
1374
  machineId: this.machine.id,
1341
1375
  time: Date.now()
1342
1376
  };
1343
- logger.debugLargeJson(`[API MACHINE] Emitting machine-alive`, payload);
1377
+ if (process.env.DEBUG) {
1378
+ logger.debugLargeJson(`[API MACHINE] Emitting machine-alive`, payload);
1379
+ }
1344
1380
  this.socket.emit("machine-alive", payload);
1345
1381
  }, 2e4);
1346
1382
  logger.debug("[API MACHINE] Keep-alive started (20s interval)");
@@ -1366,7 +1402,7 @@ class PushNotificationClient {
1366
1402
  token;
1367
1403
  baseUrl;
1368
1404
  expo;
1369
- constructor(token, baseUrl = "https://handy-api.korshakov.org") {
1405
+ constructor(token, baseUrl = "https://api.cluster-fluster.com") {
1370
1406
  this.token = token;
1371
1407
  this.baseUrl = baseUrl;
1372
1408
  this.expo = new Expo();
@@ -34,7 +34,7 @@ function _interopNamespaceDefault(e) {
34
34
  var z__namespace = /*#__PURE__*/_interopNamespaceDefault(z);
35
35
 
36
36
  var name = "happy-coder";
37
- var version = "0.9.0";
37
+ var version = "0.9.1";
38
38
  var description = "Claude Code session sharing CLI";
39
39
  var author = "Kirill Dubovitskiy";
40
40
  var license = "MIT";
@@ -173,7 +173,7 @@ class Configuration {
173
173
  currentCliVersion;
174
174
  isExperimentalEnabled;
175
175
  constructor() {
176
- this.serverUrl = process.env.HAPPY_SERVER_URL || "https://handy-api.korshakov.org";
176
+ this.serverUrl = process.env.HAPPY_SERVER_URL || "https://api.cluster-fluster.com";
177
177
  const args = process.argv.slice(2);
178
178
  this.isDaemonProcess = args.length >= 2 && args[0] === "daemon" && args[1] === "start-sync";
179
179
  if (process.env.HAPPY_HOME_DIR) {
@@ -553,7 +553,7 @@ class Logger {
553
553
  fs.appendFileSync(this.logFilePath, logLine);
554
554
  } catch (appendError) {
555
555
  if (process.env.DEBUG) {
556
- console.error("Failed to append to log file:", appendError);
556
+ console.error("[DEV MODE ONLY THROWING] Failed to append to log file:", appendError);
557
557
  throw appendError;
558
558
  }
559
559
  }
@@ -660,7 +660,23 @@ z.z.object({
660
660
  metadata: z.z.any(),
661
661
  metadataVersion: z.z.number(),
662
662
  agentState: z.z.any().nullable(),
663
- agentStateVersion: z.z.number()
663
+ agentStateVersion: z.z.number(),
664
+ // Connectivity tracking (from server)
665
+ connectivityStatus: z.z.union([
666
+ z.z.enum(["neverConnected", "online", "offline"]),
667
+ z.z.string()
668
+ // Forward compatibility
669
+ ]).optional(),
670
+ connectivityStatusSince: z.z.number().optional(),
671
+ connectivityStatusReason: z.z.string().optional(),
672
+ // State tracking (from server)
673
+ state: z.z.union([
674
+ z.z.enum(["running", "archiveRequested", "archived"]),
675
+ z.z.string()
676
+ // Forward compatibility
677
+ ]).optional(),
678
+ stateSince: z.z.number().optional(),
679
+ stateReason: z.z.string().optional()
664
680
  });
665
681
  z.z.object({
666
682
  host: z.z.string(),
@@ -698,7 +714,23 @@ z.z.object({
698
714
  active: z.z.boolean(),
699
715
  activeAt: z.z.number(),
700
716
  createdAt: z.z.number(),
701
- updatedAt: z.z.number()
717
+ updatedAt: z.z.number(),
718
+ // Connectivity tracking (from server)
719
+ connectivityStatus: z.z.union([
720
+ z.z.enum(["neverConnected", "online", "offline"]),
721
+ z.z.string()
722
+ // Forward compatibility
723
+ ]).optional(),
724
+ connectivityStatusSince: z.z.number().optional(),
725
+ connectivityStatusReason: z.z.string().optional(),
726
+ // State tracking (from server)
727
+ state: z.z.union([
728
+ z.z.enum(["running", "archiveRequested", "archived"]),
729
+ z.z.string()
730
+ // Forward compatibility
731
+ ]).optional(),
732
+ stateSince: z.z.number().optional(),
733
+ stateReason: z.z.string().optional()
702
734
  });
703
735
  z.z.object({
704
736
  content: SessionMessageContentSchema,
@@ -1011,7 +1043,9 @@ class ApiSessionClient extends node_events.EventEmitter {
1011
1043
  * Send a ping message to keep the connection alive
1012
1044
  */
1013
1045
  keepAlive(thinking, mode) {
1014
- logger.debug(`[API] Sending keep alive message: ${thinking}`);
1046
+ if (process.env.DEBUG) {
1047
+ logger.debug(`[API] Sending keep alive message: ${thinking}`);
1048
+ }
1015
1049
  this.socket.volatile.emit("session-alive", {
1016
1050
  sid: this.sessionId,
1017
1051
  time: Date.now(),
@@ -1360,7 +1394,9 @@ class ApiMachineClient {
1360
1394
  machineId: this.machine.id,
1361
1395
  time: Date.now()
1362
1396
  };
1363
- logger.debugLargeJson(`[API MACHINE] Emitting machine-alive`, payload);
1397
+ if (process.env.DEBUG) {
1398
+ logger.debugLargeJson(`[API MACHINE] Emitting machine-alive`, payload);
1399
+ }
1364
1400
  this.socket.emit("machine-alive", payload);
1365
1401
  }, 2e4);
1366
1402
  logger.debug("[API MACHINE] Keep-alive started (20s interval)");
@@ -1386,7 +1422,7 @@ class PushNotificationClient {
1386
1422
  token;
1387
1423
  baseUrl;
1388
1424
  expo;
1389
- constructor(token, baseUrl = "https://handy-api.korshakov.org") {
1425
+ constructor(token, baseUrl = "https://api.cluster-fluster.com") {
1390
1426
  this.token = token;
1391
1427
  this.baseUrl = baseUrl;
1392
1428
  this.expo = new expoServerSdk.Expo();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "happy-coder",
3
- "version": "0.9.0",
3
+ "version": "0.9.1",
4
4
  "description": "Claude Code session sharing CLI",
5
5
  "author": "Kirill Dubovitskiy",
6
6
  "license": "MIT",
@@ -102,4 +102,4 @@
102
102
  "registry": "https://registry.npmjs.org"
103
103
  },
104
104
  "packageManager": "yarn@1.22.22"
105
- }
105
+ }