happy-coder 0.3.1-beta.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +76 -27
- package/dist/index.mjs +76 -27
- package/dist/lib.cjs +1 -1
- package/dist/lib.d.cts +1 -0
- package/dist/lib.d.mts +1 -0
- package/dist/lib.mjs +1 -1
- package/dist/{types-CzYKFAYa.mjs → types-VkaGP8up.mjs} +1 -1
- package/dist/{types-D7u2DxfV.cjs → types-eN-YHsuj.cjs} +1 -0
- package/package.json +8 -6
- package/dist/index-B2GqfEZV.cjs +0 -1564
- package/dist/index-QItBXhux.mjs +0 -1540
- package/dist/install-B0DnBGS_.mjs +0 -29
- package/dist/install-B2r_gX72.cjs +0 -109
- package/dist/install-C809w0Cj.cjs +0 -31
- package/dist/install-DEPy62QN.mjs +0 -97
- package/dist/install-GZIzyuIE.cjs +0 -99
- package/dist/install-HKe7dyS4.mjs +0 -107
- package/dist/run-BmEaINbl.cjs +0 -250
- package/dist/run-DMbKhYfb.mjs +0 -247
- package/dist/run-FBXkmmN7.mjs +0 -32
- package/dist/run-q2To6b-c.cjs +0 -34
- package/dist/types-BG9AgCI4.mjs +0 -875
- package/dist/types-BRICSarm.mjs +0 -870
- package/dist/types-BTQRfIr3.cjs +0 -892
- package/dist/types-BX4xv8Ty.mjs +0 -881
- package/dist/types-BeUppqJU.cjs +0 -886
- package/dist/types-C6Wx_bRW.cjs +0 -886
- package/dist/types-CEvzGLMI.cjs +0 -882
- package/dist/types-CKUdOV6c.mjs +0 -875
- package/dist/types-CNuBtNA5.cjs +0 -884
- package/dist/types-Cg4664gs.cjs +0 -879
- package/dist/types-CkPUFpfr.cjs +0 -885
- package/dist/types-D39L8JSd.mjs +0 -850
- package/dist/types-DD9P_5rj.mjs +0 -868
- package/dist/types-DNu8okOb.mjs +0 -874
- package/dist/types-DXK5YldG.cjs +0 -892
- package/dist/types-DYBiuNUQ.cjs +0 -883
- package/dist/types-Df5dlWLV.mjs +0 -871
- package/dist/types-fXgEaaqP.mjs +0 -861
- package/dist/types-hotUTaWz.cjs +0 -863
- package/dist/types-ikrrEcJm.mjs +0 -873
- package/dist/types-mykDX2xe.cjs +0 -872
- package/dist/types-tLWMaptR.mjs +0 -879
- package/dist/uninstall-BGgl5V8F.mjs +0 -29
- package/dist/uninstall-BWHglipH.mjs +0 -40
- package/dist/uninstall-C42CoSCI.cjs +0 -53
- package/dist/uninstall-CLkTtlMv.mjs +0 -51
- package/dist/uninstall-CdHMb6wi.cjs +0 -31
- package/dist/uninstall-FXyyAuGU.cjs +0 -42
package/dist/run-BmEaINbl.cjs
DELETED
|
@@ -1,250 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
var types = require('./types-DYBiuNUQ.cjs');
|
|
4
|
-
var node_events = require('node:events');
|
|
5
|
-
var socket_ioClient = require('socket.io-client');
|
|
6
|
-
var child_process = require('child_process');
|
|
7
|
-
var index = require('./index-B2GqfEZV.cjs');
|
|
8
|
-
var os = require('os');
|
|
9
|
-
var fs = require('fs');
|
|
10
|
-
var path = require('path');
|
|
11
|
-
var crypto = require('crypto');
|
|
12
|
-
require('axios');
|
|
13
|
-
require('chalk');
|
|
14
|
-
require('node:os');
|
|
15
|
-
require('node:path');
|
|
16
|
-
require('node:fs/promises');
|
|
17
|
-
require('node:fs');
|
|
18
|
-
require('zod');
|
|
19
|
-
require('node:crypto');
|
|
20
|
-
require('tweetnacl');
|
|
21
|
-
require('expo-server-sdk');
|
|
22
|
-
require('@anthropic-ai/claude-code');
|
|
23
|
-
require('node:child_process');
|
|
24
|
-
require('node:readline');
|
|
25
|
-
require('node:url');
|
|
26
|
-
require('@modelcontextprotocol/sdk/server/mcp.js');
|
|
27
|
-
require('node:http');
|
|
28
|
-
require('@modelcontextprotocol/sdk/server/streamableHttp.js');
|
|
29
|
-
require('node:https');
|
|
30
|
-
require('node:net');
|
|
31
|
-
require('qrcode-terminal');
|
|
32
|
-
|
|
33
|
-
class ApiDaemonSession extends node_events.EventEmitter {
|
|
34
|
-
socket;
|
|
35
|
-
machineIdentity;
|
|
36
|
-
keepAliveInterval = null;
|
|
37
|
-
token;
|
|
38
|
-
secret;
|
|
39
|
-
constructor(token, secret, machineIdentity) {
|
|
40
|
-
super();
|
|
41
|
-
this.token = token;
|
|
42
|
-
this.secret = secret;
|
|
43
|
-
this.machineIdentity = machineIdentity;
|
|
44
|
-
const socket = socket_ioClient.io(types.configuration.serverUrl, {
|
|
45
|
-
auth: {
|
|
46
|
-
token: this.token,
|
|
47
|
-
clientType: "machine-scoped",
|
|
48
|
-
machineId: this.machineIdentity.machineId
|
|
49
|
-
},
|
|
50
|
-
path: "/v1/user-machine-daemon",
|
|
51
|
-
reconnection: true,
|
|
52
|
-
reconnectionAttempts: Infinity,
|
|
53
|
-
reconnectionDelay: 1e3,
|
|
54
|
-
reconnectionDelayMax: 5e3,
|
|
55
|
-
transports: ["websocket"],
|
|
56
|
-
withCredentials: true,
|
|
57
|
-
autoConnect: false
|
|
58
|
-
});
|
|
59
|
-
socket.on("connect", () => {
|
|
60
|
-
types.logger.debug("[DAEMON] Connected to server");
|
|
61
|
-
this.emit("connected");
|
|
62
|
-
socket.emit("machine-connect", {
|
|
63
|
-
token: this.token,
|
|
64
|
-
machineIdentity: types.encodeBase64(types.encrypt(this.machineIdentity, this.secret))
|
|
65
|
-
});
|
|
66
|
-
this.startKeepAlive();
|
|
67
|
-
});
|
|
68
|
-
socket.on("disconnect", () => {
|
|
69
|
-
types.logger.debug("[DAEMON] Disconnected from server");
|
|
70
|
-
this.emit("disconnected");
|
|
71
|
-
this.stopKeepAlive();
|
|
72
|
-
});
|
|
73
|
-
socket.on("spawn-session", async (encryptedData, callback) => {
|
|
74
|
-
try {
|
|
75
|
-
const data2 = types.decrypt(types.decodeBase64(encryptedData), this.secret);
|
|
76
|
-
types.logger.debug("[DAEMON] Received spawn-session request", data2);
|
|
77
|
-
const args = [
|
|
78
|
-
"--directory",
|
|
79
|
-
data2.directory,
|
|
80
|
-
"--happy-starting-mode",
|
|
81
|
-
data2.startingMode
|
|
82
|
-
];
|
|
83
|
-
if (data2.metadata) {
|
|
84
|
-
args.push("--metadata", data2.metadata);
|
|
85
|
-
}
|
|
86
|
-
if (data2.startingMode === "interactive" && process.platform === "darwin") {
|
|
87
|
-
const script = `
|
|
88
|
-
tell application "Terminal"
|
|
89
|
-
activate
|
|
90
|
-
do script "cd ${data2.directory} && happy ${args.join(" ")}"
|
|
91
|
-
end tell
|
|
92
|
-
`;
|
|
93
|
-
child_process.spawn("osascript", ["-e", script], { detached: true });
|
|
94
|
-
} else {
|
|
95
|
-
const child = child_process.spawn("happy", args, {
|
|
96
|
-
detached: true,
|
|
97
|
-
stdio: "ignore",
|
|
98
|
-
cwd: data2.directory
|
|
99
|
-
});
|
|
100
|
-
child.unref();
|
|
101
|
-
}
|
|
102
|
-
const result = { success: true };
|
|
103
|
-
socket.emit("session-spawn-result", {
|
|
104
|
-
requestId: data2.requestId,
|
|
105
|
-
result: types.encodeBase64(types.encrypt(result, this.secret))
|
|
106
|
-
});
|
|
107
|
-
callback(types.encodeBase64(types.encrypt({ success: true }, this.secret)));
|
|
108
|
-
} catch (error) {
|
|
109
|
-
types.logger.debug("[DAEMON] Failed to spawn session", error);
|
|
110
|
-
const errorResult = {
|
|
111
|
-
success: false,
|
|
112
|
-
error: error instanceof Error ? error.message : "Unknown error"
|
|
113
|
-
};
|
|
114
|
-
socket.emit("session-spawn-result", {
|
|
115
|
-
requestId: data.requestId,
|
|
116
|
-
result: types.encodeBase64(types.encrypt(errorResult, this.secret))
|
|
117
|
-
});
|
|
118
|
-
callback(types.encodeBase64(types.encrypt(errorResult, this.secret)));
|
|
119
|
-
}
|
|
120
|
-
});
|
|
121
|
-
socket.on("daemon-command", (data2) => {
|
|
122
|
-
switch (data2.command) {
|
|
123
|
-
case "shutdown":
|
|
124
|
-
this.shutdown();
|
|
125
|
-
break;
|
|
126
|
-
case "status":
|
|
127
|
-
this.emit("status-request");
|
|
128
|
-
break;
|
|
129
|
-
}
|
|
130
|
-
});
|
|
131
|
-
this.socket = socket;
|
|
132
|
-
}
|
|
133
|
-
startKeepAlive() {
|
|
134
|
-
this.stopKeepAlive();
|
|
135
|
-
this.keepAliveInterval = setInterval(() => {
|
|
136
|
-
this.socket.volatile.emit("machine-alive", {
|
|
137
|
-
time: Date.now()
|
|
138
|
-
});
|
|
139
|
-
}, 2e4);
|
|
140
|
-
}
|
|
141
|
-
stopKeepAlive() {
|
|
142
|
-
if (this.keepAliveInterval) {
|
|
143
|
-
clearInterval(this.keepAliveInterval);
|
|
144
|
-
this.keepAliveInterval = null;
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
connect() {
|
|
148
|
-
this.socket.connect();
|
|
149
|
-
}
|
|
150
|
-
shutdown() {
|
|
151
|
-
this.stopKeepAlive();
|
|
152
|
-
this.socket.close();
|
|
153
|
-
this.emit("shutdown");
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
const DAEMON_PID_FILE = path.join(os.homedir(), ".happy", "daemon-pid");
|
|
158
|
-
async function run() {
|
|
159
|
-
if (!process.env.HAPPY_DAEMON_MODE) {
|
|
160
|
-
throw new Error("This function should only be called by the daemon system with HAPPY_DAEMON_MODE environment variable set");
|
|
161
|
-
}
|
|
162
|
-
types.logger.info("Happy CLI daemon started successfully");
|
|
163
|
-
if (isDaemonRunning()) {
|
|
164
|
-
console.log("Happy daemon is already running");
|
|
165
|
-
process.exit(0);
|
|
166
|
-
}
|
|
167
|
-
writePidFile();
|
|
168
|
-
process.on("SIGINT", cleanup);
|
|
169
|
-
process.on("SIGTERM", cleanup);
|
|
170
|
-
process.on("exit", cleanup);
|
|
171
|
-
try {
|
|
172
|
-
const settings = await index.readSettings() || {};
|
|
173
|
-
if (!settings.machineId) {
|
|
174
|
-
settings.machineId = crypto.randomUUID();
|
|
175
|
-
settings.machineHost = os.hostname();
|
|
176
|
-
await index.writeSettings(settings);
|
|
177
|
-
}
|
|
178
|
-
const machineIdentity = {
|
|
179
|
-
machineId: settings.machineId,
|
|
180
|
-
machineHost: settings.machineHost || os.hostname(),
|
|
181
|
-
platform: process.platform,
|
|
182
|
-
version: process.env.npm_package_version || "unknown"
|
|
183
|
-
};
|
|
184
|
-
let credentials = await index.readCredentials();
|
|
185
|
-
if (!credentials) {
|
|
186
|
-
types.logger.debug("[DAEMON] No credentials found, running auth");
|
|
187
|
-
await index.doAuth();
|
|
188
|
-
credentials = await index.readCredentials();
|
|
189
|
-
if (!credentials) {
|
|
190
|
-
throw new Error("Failed to authenticate");
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
const { token, secret } = credentials;
|
|
194
|
-
const daemon = new ApiDaemonSession(token, secret, machineIdentity);
|
|
195
|
-
daemon.on("connected", () => {
|
|
196
|
-
types.logger.debug("[DAEMON] Successfully connected to server");
|
|
197
|
-
});
|
|
198
|
-
daemon.on("disconnected", () => {
|
|
199
|
-
types.logger.debug("[DAEMON] Disconnected from server");
|
|
200
|
-
});
|
|
201
|
-
daemon.on("shutdown", () => {
|
|
202
|
-
types.logger.debug("[DAEMON] Shutdown requested");
|
|
203
|
-
cleanup();
|
|
204
|
-
process.exit(0);
|
|
205
|
-
});
|
|
206
|
-
daemon.connect();
|
|
207
|
-
setInterval(() => {
|
|
208
|
-
}, 1e3);
|
|
209
|
-
} catch (error) {
|
|
210
|
-
types.logger.debug("[DAEMON] Failed to start daemon", error);
|
|
211
|
-
cleanup();
|
|
212
|
-
process.exit(1);
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
function isDaemonRunning() {
|
|
216
|
-
try {
|
|
217
|
-
if (!fs.existsSync(DAEMON_PID_FILE)) {
|
|
218
|
-
return false;
|
|
219
|
-
}
|
|
220
|
-
const pid = parseInt(fs.readFileSync(DAEMON_PID_FILE, "utf-8"));
|
|
221
|
-
try {
|
|
222
|
-
process.kill(pid, 0);
|
|
223
|
-
return true;
|
|
224
|
-
} catch {
|
|
225
|
-
fs.unlinkSync(DAEMON_PID_FILE);
|
|
226
|
-
return false;
|
|
227
|
-
}
|
|
228
|
-
} catch {
|
|
229
|
-
return false;
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
function writePidFile() {
|
|
233
|
-
const happyDir = path.join(os.homedir(), ".happy");
|
|
234
|
-
if (!fs.existsSync(happyDir)) {
|
|
235
|
-
fs.mkdirSync(happyDir, { recursive: true });
|
|
236
|
-
}
|
|
237
|
-
fs.writeFileSync(DAEMON_PID_FILE, process.pid.toString());
|
|
238
|
-
}
|
|
239
|
-
function cleanup() {
|
|
240
|
-
try {
|
|
241
|
-
if (fs.existsSync(DAEMON_PID_FILE)) {
|
|
242
|
-
fs.unlinkSync(DAEMON_PID_FILE);
|
|
243
|
-
}
|
|
244
|
-
} catch (error) {
|
|
245
|
-
types.logger.debug("[DAEMON] Error cleaning up PID file", error);
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
exports.isDaemonRunning = isDaemonRunning;
|
|
250
|
-
exports.run = run;
|
package/dist/run-DMbKhYfb.mjs
DELETED
|
@@ -1,247 +0,0 @@
|
|
|
1
|
-
import { c as configuration, l as logger, e as encodeBase64, d as encrypt, f as decrypt, g as decodeBase64 } from './types-BRICSarm.mjs';
|
|
2
|
-
import { EventEmitter } from 'node:events';
|
|
3
|
-
import { io } from 'socket.io-client';
|
|
4
|
-
import { spawn } from 'child_process';
|
|
5
|
-
import { r as readSettings, w as writeSettings, a as readCredentials, d as doAuth } from './index-QItBXhux.mjs';
|
|
6
|
-
import { hostname, homedir } from 'os';
|
|
7
|
-
import { existsSync, readFileSync, unlinkSync, mkdirSync, writeFileSync } from 'fs';
|
|
8
|
-
import { join } from 'path';
|
|
9
|
-
import crypto from 'crypto';
|
|
10
|
-
import 'axios';
|
|
11
|
-
import 'chalk';
|
|
12
|
-
import 'node:os';
|
|
13
|
-
import 'node:path';
|
|
14
|
-
import 'node:fs/promises';
|
|
15
|
-
import 'node:fs';
|
|
16
|
-
import 'zod';
|
|
17
|
-
import 'node:crypto';
|
|
18
|
-
import 'tweetnacl';
|
|
19
|
-
import 'expo-server-sdk';
|
|
20
|
-
import '@anthropic-ai/claude-code';
|
|
21
|
-
import 'node:child_process';
|
|
22
|
-
import 'node:readline';
|
|
23
|
-
import 'node:url';
|
|
24
|
-
import '@modelcontextprotocol/sdk/server/mcp.js';
|
|
25
|
-
import 'node:http';
|
|
26
|
-
import '@modelcontextprotocol/sdk/server/streamableHttp.js';
|
|
27
|
-
import 'node:https';
|
|
28
|
-
import 'node:net';
|
|
29
|
-
import 'qrcode-terminal';
|
|
30
|
-
|
|
31
|
-
class ApiDaemonSession extends EventEmitter {
|
|
32
|
-
socket;
|
|
33
|
-
machineIdentity;
|
|
34
|
-
keepAliveInterval = null;
|
|
35
|
-
token;
|
|
36
|
-
secret;
|
|
37
|
-
constructor(token, secret, machineIdentity) {
|
|
38
|
-
super();
|
|
39
|
-
this.token = token;
|
|
40
|
-
this.secret = secret;
|
|
41
|
-
this.machineIdentity = machineIdentity;
|
|
42
|
-
const socket = io(configuration.serverUrl, {
|
|
43
|
-
auth: {
|
|
44
|
-
token: this.token,
|
|
45
|
-
clientType: "machine-scoped",
|
|
46
|
-
machineId: this.machineIdentity.machineId
|
|
47
|
-
},
|
|
48
|
-
path: "/v1/user-machine-daemon",
|
|
49
|
-
reconnection: true,
|
|
50
|
-
reconnectionAttempts: Infinity,
|
|
51
|
-
reconnectionDelay: 1e3,
|
|
52
|
-
reconnectionDelayMax: 5e3,
|
|
53
|
-
transports: ["websocket"],
|
|
54
|
-
withCredentials: true,
|
|
55
|
-
autoConnect: false
|
|
56
|
-
});
|
|
57
|
-
socket.on("connect", () => {
|
|
58
|
-
logger.debug("[DAEMON] Connected to server");
|
|
59
|
-
this.emit("connected");
|
|
60
|
-
socket.emit("machine-connect", {
|
|
61
|
-
token: this.token,
|
|
62
|
-
machineIdentity: encodeBase64(encrypt(this.machineIdentity, this.secret))
|
|
63
|
-
});
|
|
64
|
-
this.startKeepAlive();
|
|
65
|
-
});
|
|
66
|
-
socket.on("disconnect", () => {
|
|
67
|
-
logger.debug("[DAEMON] Disconnected from server");
|
|
68
|
-
this.emit("disconnected");
|
|
69
|
-
this.stopKeepAlive();
|
|
70
|
-
});
|
|
71
|
-
socket.on("spawn-session", async (encryptedData, callback) => {
|
|
72
|
-
try {
|
|
73
|
-
const data2 = decrypt(decodeBase64(encryptedData), this.secret);
|
|
74
|
-
logger.debug("[DAEMON] Received spawn-session request", data2);
|
|
75
|
-
const args = [
|
|
76
|
-
"--directory",
|
|
77
|
-
data2.directory,
|
|
78
|
-
"--happy-starting-mode",
|
|
79
|
-
data2.startingMode
|
|
80
|
-
];
|
|
81
|
-
if (data2.metadata) {
|
|
82
|
-
args.push("--metadata", data2.metadata);
|
|
83
|
-
}
|
|
84
|
-
if (data2.startingMode === "interactive" && process.platform === "darwin") {
|
|
85
|
-
const script = `
|
|
86
|
-
tell application "Terminal"
|
|
87
|
-
activate
|
|
88
|
-
do script "cd ${data2.directory} && happy ${args.join(" ")}"
|
|
89
|
-
end tell
|
|
90
|
-
`;
|
|
91
|
-
spawn("osascript", ["-e", script], { detached: true });
|
|
92
|
-
} else {
|
|
93
|
-
const child = spawn("happy", args, {
|
|
94
|
-
detached: true,
|
|
95
|
-
stdio: "ignore",
|
|
96
|
-
cwd: data2.directory
|
|
97
|
-
});
|
|
98
|
-
child.unref();
|
|
99
|
-
}
|
|
100
|
-
const result = { success: true };
|
|
101
|
-
socket.emit("session-spawn-result", {
|
|
102
|
-
requestId: data2.requestId,
|
|
103
|
-
result: encodeBase64(encrypt(result, this.secret))
|
|
104
|
-
});
|
|
105
|
-
callback(encodeBase64(encrypt({ success: true }, this.secret)));
|
|
106
|
-
} catch (error) {
|
|
107
|
-
logger.debug("[DAEMON] Failed to spawn session", error);
|
|
108
|
-
const errorResult = {
|
|
109
|
-
success: false,
|
|
110
|
-
error: error instanceof Error ? error.message : "Unknown error"
|
|
111
|
-
};
|
|
112
|
-
socket.emit("session-spawn-result", {
|
|
113
|
-
requestId: data.requestId,
|
|
114
|
-
result: encodeBase64(encrypt(errorResult, this.secret))
|
|
115
|
-
});
|
|
116
|
-
callback(encodeBase64(encrypt(errorResult, this.secret)));
|
|
117
|
-
}
|
|
118
|
-
});
|
|
119
|
-
socket.on("daemon-command", (data2) => {
|
|
120
|
-
switch (data2.command) {
|
|
121
|
-
case "shutdown":
|
|
122
|
-
this.shutdown();
|
|
123
|
-
break;
|
|
124
|
-
case "status":
|
|
125
|
-
this.emit("status-request");
|
|
126
|
-
break;
|
|
127
|
-
}
|
|
128
|
-
});
|
|
129
|
-
this.socket = socket;
|
|
130
|
-
}
|
|
131
|
-
startKeepAlive() {
|
|
132
|
-
this.stopKeepAlive();
|
|
133
|
-
this.keepAliveInterval = setInterval(() => {
|
|
134
|
-
this.socket.volatile.emit("machine-alive", {
|
|
135
|
-
time: Date.now()
|
|
136
|
-
});
|
|
137
|
-
}, 2e4);
|
|
138
|
-
}
|
|
139
|
-
stopKeepAlive() {
|
|
140
|
-
if (this.keepAliveInterval) {
|
|
141
|
-
clearInterval(this.keepAliveInterval);
|
|
142
|
-
this.keepAliveInterval = null;
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
connect() {
|
|
146
|
-
this.socket.connect();
|
|
147
|
-
}
|
|
148
|
-
shutdown() {
|
|
149
|
-
this.stopKeepAlive();
|
|
150
|
-
this.socket.close();
|
|
151
|
-
this.emit("shutdown");
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
const DAEMON_PID_FILE = join(homedir(), ".happy", "daemon-pid");
|
|
156
|
-
async function run() {
|
|
157
|
-
if (!process.env.HAPPY_DAEMON_MODE) {
|
|
158
|
-
throw new Error("This function should only be called by the daemon system with HAPPY_DAEMON_MODE environment variable set");
|
|
159
|
-
}
|
|
160
|
-
logger.info("Happy CLI daemon started successfully");
|
|
161
|
-
if (isDaemonRunning()) {
|
|
162
|
-
console.log("Happy daemon is already running");
|
|
163
|
-
process.exit(0);
|
|
164
|
-
}
|
|
165
|
-
writePidFile();
|
|
166
|
-
process.on("SIGINT", cleanup);
|
|
167
|
-
process.on("SIGTERM", cleanup);
|
|
168
|
-
process.on("exit", cleanup);
|
|
169
|
-
try {
|
|
170
|
-
const settings = await readSettings() || {};
|
|
171
|
-
if (!settings.machineId) {
|
|
172
|
-
settings.machineId = crypto.randomUUID();
|
|
173
|
-
settings.machineHost = hostname();
|
|
174
|
-
await writeSettings(settings);
|
|
175
|
-
}
|
|
176
|
-
const machineIdentity = {
|
|
177
|
-
machineId: settings.machineId,
|
|
178
|
-
machineHost: settings.machineHost || hostname(),
|
|
179
|
-
platform: process.platform,
|
|
180
|
-
version: process.env.npm_package_version || "unknown"
|
|
181
|
-
};
|
|
182
|
-
let credentials = await readCredentials();
|
|
183
|
-
if (!credentials) {
|
|
184
|
-
logger.debug("[DAEMON] No credentials found, running auth");
|
|
185
|
-
await doAuth();
|
|
186
|
-
credentials = await readCredentials();
|
|
187
|
-
if (!credentials) {
|
|
188
|
-
throw new Error("Failed to authenticate");
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
const { token, secret } = credentials;
|
|
192
|
-
const daemon = new ApiDaemonSession(token, secret, machineIdentity);
|
|
193
|
-
daemon.on("connected", () => {
|
|
194
|
-
logger.debug("[DAEMON] Successfully connected to server");
|
|
195
|
-
});
|
|
196
|
-
daemon.on("disconnected", () => {
|
|
197
|
-
logger.debug("[DAEMON] Disconnected from server");
|
|
198
|
-
});
|
|
199
|
-
daemon.on("shutdown", () => {
|
|
200
|
-
logger.debug("[DAEMON] Shutdown requested");
|
|
201
|
-
cleanup();
|
|
202
|
-
process.exit(0);
|
|
203
|
-
});
|
|
204
|
-
daemon.connect();
|
|
205
|
-
setInterval(() => {
|
|
206
|
-
}, 1e3);
|
|
207
|
-
} catch (error) {
|
|
208
|
-
logger.debug("[DAEMON] Failed to start daemon", error);
|
|
209
|
-
cleanup();
|
|
210
|
-
process.exit(1);
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
function isDaemonRunning() {
|
|
214
|
-
try {
|
|
215
|
-
if (!existsSync(DAEMON_PID_FILE)) {
|
|
216
|
-
return false;
|
|
217
|
-
}
|
|
218
|
-
const pid = parseInt(readFileSync(DAEMON_PID_FILE, "utf-8"));
|
|
219
|
-
try {
|
|
220
|
-
process.kill(pid, 0);
|
|
221
|
-
return true;
|
|
222
|
-
} catch {
|
|
223
|
-
unlinkSync(DAEMON_PID_FILE);
|
|
224
|
-
return false;
|
|
225
|
-
}
|
|
226
|
-
} catch {
|
|
227
|
-
return false;
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
function writePidFile() {
|
|
231
|
-
const happyDir = join(homedir(), ".happy");
|
|
232
|
-
if (!existsSync(happyDir)) {
|
|
233
|
-
mkdirSync(happyDir, { recursive: true });
|
|
234
|
-
}
|
|
235
|
-
writeFileSync(DAEMON_PID_FILE, process.pid.toString());
|
|
236
|
-
}
|
|
237
|
-
function cleanup() {
|
|
238
|
-
try {
|
|
239
|
-
if (existsSync(DAEMON_PID_FILE)) {
|
|
240
|
-
unlinkSync(DAEMON_PID_FILE);
|
|
241
|
-
}
|
|
242
|
-
} catch (error) {
|
|
243
|
-
logger.debug("[DAEMON] Error cleaning up PID file", error);
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
export { isDaemonRunning, run };
|
package/dist/run-FBXkmmN7.mjs
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import { l as logger } from './types-fXgEaaqP.mjs';
|
|
2
|
-
import 'axios';
|
|
3
|
-
import 'chalk';
|
|
4
|
-
import 'fs';
|
|
5
|
-
import 'node:os';
|
|
6
|
-
import 'node:path';
|
|
7
|
-
import 'node:fs/promises';
|
|
8
|
-
import 'node:fs';
|
|
9
|
-
import 'node:events';
|
|
10
|
-
import 'socket.io-client';
|
|
11
|
-
import 'zod';
|
|
12
|
-
import 'node:crypto';
|
|
13
|
-
import 'tweetnacl';
|
|
14
|
-
import 'expo-server-sdk';
|
|
15
|
-
|
|
16
|
-
async function run() {
|
|
17
|
-
if (!process.env.HAPPY_DAEMON_MODE) {
|
|
18
|
-
throw new Error("This function should only be called by the daemon system with HAPPY_DAEMON_MODE environment variable set");
|
|
19
|
-
}
|
|
20
|
-
logger.info("Happy CLI daemon started successfully");
|
|
21
|
-
while (true) {
|
|
22
|
-
try {
|
|
23
|
-
logger.debug("Daemon heartbeat");
|
|
24
|
-
await new Promise((resolve) => setTimeout(resolve, 3e4));
|
|
25
|
-
} catch (error) {
|
|
26
|
-
logger.debug("Error in daemon loop:", error);
|
|
27
|
-
await new Promise((resolve) => setTimeout(resolve, 5e3));
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export { run };
|
package/dist/run-q2To6b-c.cjs
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
var types = require('./types-mykDX2xe.cjs');
|
|
4
|
-
require('axios');
|
|
5
|
-
require('chalk');
|
|
6
|
-
require('fs');
|
|
7
|
-
require('node:os');
|
|
8
|
-
require('node:path');
|
|
9
|
-
require('node:fs/promises');
|
|
10
|
-
require('node:fs');
|
|
11
|
-
require('node:events');
|
|
12
|
-
require('socket.io-client');
|
|
13
|
-
require('zod');
|
|
14
|
-
require('node:crypto');
|
|
15
|
-
require('tweetnacl');
|
|
16
|
-
require('expo-server-sdk');
|
|
17
|
-
|
|
18
|
-
async function run() {
|
|
19
|
-
if (!process.env.HAPPY_DAEMON_MODE) {
|
|
20
|
-
throw new Error("This function should only be called by the daemon system with HAPPY_DAEMON_MODE environment variable set");
|
|
21
|
-
}
|
|
22
|
-
types.logger.info("Happy CLI daemon started successfully");
|
|
23
|
-
while (true) {
|
|
24
|
-
try {
|
|
25
|
-
types.logger.debug("Daemon heartbeat");
|
|
26
|
-
await new Promise((resolve) => setTimeout(resolve, 3e4));
|
|
27
|
-
} catch (error) {
|
|
28
|
-
types.logger.debug("Error in daemon loop:", error);
|
|
29
|
-
await new Promise((resolve) => setTimeout(resolve, 5e3));
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
exports.run = run;
|