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 +86 -19
- package/dist/index.mjs +86 -19
- package/dist/lib.cjs +1 -1
- package/dist/lib.d.cts +41 -0
- package/dist/lib.d.mts +41 -0
- package/dist/lib.mjs +1 -1
- package/dist/{types-Cezp_n6O.mjs → types-BS8Pr3Im.mjs} +44 -8
- package/dist/{types-CyOnnZ8M.cjs → types-DNUk09Np.cjs} +44 -8
- package/package.json +2 -2
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-
|
|
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
|
|
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 === "--
|
|
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
|
|
5552
|
-
|
|
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
|
|
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-
|
|
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
|
|
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 === "--
|
|
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
|
|
5549
|
-
|
|
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
|
|
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
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-
|
|
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.
|
|
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://
|
|
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
|
-
|
|
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
|
-
|
|
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://
|
|
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.
|
|
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://
|
|
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
|
-
|
|
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
|
-
|
|
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://
|
|
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.
|
|
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
|
+
}
|