@zonease/happy 0.6.38 → 0.6.40
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/bin/happy.mjs +10 -0
- package/dist/{types-DvTsFaqR.cjs → api-C7FyJfZ1.cjs} +400 -1964
- package/dist/{types-BWpRU1VN.mjs → api-D6WL40-U.mjs} +220 -1729
- package/dist/auth-C7ZScWEw.cjs +274 -0
- package/dist/auth-DCsIoZLT.cjs +183 -0
- package/dist/auth-DO9GexAR.mjs +181 -0
- package/dist/auth-DxJ3VvrX.mjs +270 -0
- package/dist/browser-AoVGRX7y.mjs +20 -0
- package/dist/browser-DzgO4oaD.cjs +22 -0
- package/dist/claudeLocal-C6BdzhDx.cjs +352 -0
- package/dist/claudeLocal-CWNGpJ0-.mjs +344 -0
- package/dist/codex/happyMcpStdioBridge.cjs +4 -1
- package/dist/codex/happyMcpStdioBridge.mjs +4 -1
- package/dist/common-BdNgghUJ.mjs +75 -0
- package/dist/common-CZvSRU4p.cjs +83 -0
- package/dist/{config-0Xnc8ARM.cjs → config-CclB3QW9.cjs} +21 -34
- package/dist/{config-_GW4YBCP.mjs → config-DZk-mJ0K.mjs} +7 -20
- package/dist/connect-BXLUKJ5P.mjs +679 -0
- package/dist/connect-k9CqDJ-T.cjs +681 -0
- package/dist/{constants-B4x1K7by.cjs → constants-E_UWYTo3.cjs} +2 -2
- package/dist/{constants-BO9OAFOT.mjs → constants-pmXZGibX.mjs} +1 -1
- package/dist/controlClient-B23M-WPp.cjs +177 -0
- package/dist/controlClient-DB5tDU7V.mjs +167 -0
- package/dist/deviceKeys-BTdo34Uz.mjs +175 -0
- package/dist/deviceKeys-C540DcXl.cjs +182 -0
- package/dist/diagnose-B3D1a_v0.cjs +60 -0
- package/dist/diagnose-CuRKXxt1.mjs +58 -0
- package/dist/doctor-BQGLhrcI.cjs +214 -0
- package/dist/doctor-C08m5Z9f.cjs +78 -0
- package/dist/doctor-CU4JLGQ1.mjs +211 -0
- package/dist/doctor-ON7pN5G5.mjs +74 -0
- package/dist/downloadImage-DZXI3mXL.mjs +887 -0
- package/dist/downloadImage-cQ1Z7hkC.cjs +897 -0
- package/dist/enroll-B55BQR56.mjs +113 -0
- package/dist/enroll-Bo-CNio1.cjs +115 -0
- package/dist/evidence-DQp8QWcv.cjs +129 -0
- package/dist/evidence-DTYRAaOZ.mjs +127 -0
- package/dist/formatImageMessage-CixDYY6G.cjs +46 -0
- package/dist/formatImageMessage-DbKSSjMx.mjs +43 -0
- package/dist/github-DKcyF1zp.mjs +57 -0
- package/dist/github-h9GhJJ_C.cjs +59 -0
- package/dist/index-CDTNgfUf.cjs +578 -0
- package/dist/index-CPKdvi7J.mjs +554 -0
- package/dist/index.cjs +812 -40
- package/dist/index.mjs +813 -40
- package/dist/install-B1e6rdy7.mjs +94 -0
- package/dist/install-CgUPGIoF.mjs +95 -0
- package/dist/install-D7Ir7ciZ.cjs +96 -0
- package/dist/install-DSDufaPd.cjs +98 -0
- package/dist/lib.cjs +18 -12
- package/dist/lib.d.cts +77 -0
- package/dist/lib.d.mts +77 -0
- package/dist/lib.mjs +13 -7
- package/dist/{loadableToolOutput-va3RLplh.mjs → loadableToolOutput-Bfei9wPq.mjs} +4 -2
- package/dist/{loadableToolOutput-5vdQB98M.cjs → loadableToolOutput-CbLGtvST.cjs} +63 -61
- package/dist/mcpToolSchemas-CjeTfrAs.mjs +62 -0
- package/dist/mcpToolSchemas-DK42yoZ_.cjs +64 -0
- package/dist/persistence-CBTtC_dm.cjs +996 -0
- package/dist/persistence-DKfF81pd.mjs +944 -0
- package/dist/projectPath-BTBIMB57.mjs +10 -0
- package/dist/projectPath-ChLhdOyt.cjs +13 -0
- package/dist/prompt-BTWDZZO0.cjs +25 -0
- package/dist/prompt-BsAZsPbo.mjs +22 -0
- package/dist/release-BMEzNONE.mjs +168 -0
- package/dist/release-EhOJeAZe.cjs +171 -0
- package/dist/run-BVZsMNwN.mjs +2164 -0
- package/dist/run-CURx7LhK.cjs +2188 -0
- package/dist/runClaude-6Gi0sYQy.mjs +3763 -0
- package/dist/runClaude-CL3Kt5ex.cjs +3766 -0
- package/dist/{runCodex-DiKzBf9q.mjs → runCodex-ClwEgJ7M.mjs} +36 -20
- package/dist/{runCodex-skq0j05d.cjs → runCodex-YKyVBkJ3.cjs} +169 -153
- package/dist/{runGemini-BeqrVq8R.mjs → runGemini-BPWxVr60.mjs} +34 -17
- package/dist/{runGemini-CVp-CEIh.cjs → runGemini-Xz2W1spn.cjs} +212 -195
- package/dist/{runOneShot-ltCpHbBB.mjs → runOneShot-CdCWiiLr.mjs} +14 -38
- package/dist/{runOneShot-BQNkNW61.cjs → runOneShot-DjZyCvDI.cjs} +31 -55
- package/dist/{serve-BNdoEDVx.mjs → serve-B48JttQ3.mjs} +12 -35
- package/dist/{serve-BECdtivY.cjs → serve-B8vaWQA0.cjs} +35 -58
- package/dist/session-BXboDn2V.mjs +108 -0
- package/dist/session-BZO25zxp.cjs +110 -0
- package/dist/sessionKeyCache-B6cGadYL.mjs +33 -0
- package/dist/sessionKeyCache-CDmexJ9p.cjs +36 -0
- package/dist/sessionList-CgAYZpNj.mjs +51 -0
- package/dist/sessionList-lQ-nqEhc.cjs +54 -0
- package/dist/sessionSummary-CcORiQSq.cjs +393 -0
- package/dist/sessionSummary-DCWQoaDP.mjs +387 -0
- package/dist/spawnHappyCLI-KiazE3kL.cjs +70 -0
- package/dist/spawnHappyCLI-yBIP_Oqi.mjs +66 -0
- package/dist/ssh-q5uZhqBf.mjs +359 -0
- package/dist/ssh-yMllSHu5.cjs +365 -0
- package/dist/systemdScope-BeOtAkhk.mjs +25 -0
- package/dist/systemdScope-DW3-UoKB.cjs +29 -0
- package/dist/task-_nr7J19j.cjs +167 -0
- package/dist/task-wNX1N2i4.mjs +165 -0
- package/dist/time-Bj2HRkKH.cjs +33 -0
- package/dist/time-DgsBLJA_.mjs +29 -0
- package/dist/trimIdent-4gvk9Wk6.cjs +22 -0
- package/dist/trimIdent-BAjmv_TG.mjs +20 -0
- package/dist/types-BoJh4Qfv.mjs +57 -0
- package/dist/types-DWeB7aqh.cjs +59 -0
- package/dist/uninstall-Buaz7d-X.cjs +77 -0
- package/dist/uninstall-Dxf6_4mQ.mjs +75 -0
- package/dist/update-CGlIfa8e.cjs +111 -0
- package/dist/update-CnnoX2cV.mjs +109 -0
- package/dist/workflowCommon-B0d01Y_a.mjs +56 -0
- package/dist/workflowCommon-Bl568-rt.cjs +62 -0
- package/dist/workflowReports-BEWuQesA.cjs +113 -0
- package/dist/workflowReports-Dd8-4-8f.mjs +108 -0
- package/package.json +1 -1
- package/dist/index-BbCDDvoB.cjs +0 -11376
- package/dist/index-vvZXdswy.mjs +0 -11336
- package/dist/mcpToolSchemas-CdPXJQmb.mjs +0 -175
- package/dist/mcpToolSchemas-f-kttjFw.cjs +0 -186
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { l as logger, r as readCredentials } from './persistence-DKfF81pd.mjs';
|
|
2
|
+
import { i as install$2 } from './install-CgUPGIoF.mjs';
|
|
3
|
+
import { existsSync, mkdirSync, writeFileSync } from 'fs';
|
|
4
|
+
import { execSync } from 'child_process';
|
|
5
|
+
import path from 'path';
|
|
6
|
+
import os from 'os';
|
|
7
|
+
import { t as trimIdent } from './trimIdent-BAjmv_TG.mjs';
|
|
8
|
+
import { p as projectPath } from './projectPath-BTBIMB57.mjs';
|
|
9
|
+
import { s as systemdScopeFor, i as isRootUser, S as SERVICE_NAME } from './systemdScope-BeOtAkhk.mjs';
|
|
10
|
+
import 'node:fs/promises';
|
|
11
|
+
import 'node:fs';
|
|
12
|
+
import 'node:os';
|
|
13
|
+
import 'node:path';
|
|
14
|
+
import 'zod';
|
|
15
|
+
import 'node:crypto';
|
|
16
|
+
import 'tweetnacl';
|
|
17
|
+
import 'chalk';
|
|
18
|
+
import 'url';
|
|
19
|
+
|
|
20
|
+
function environmentLines() {
|
|
21
|
+
const lines = ["Environment=HAPPY_DAEMON_SUPERVISED=1"];
|
|
22
|
+
if (process.env.HAPPY_SERVER_URL) lines.push(`Environment=HAPPY_SERVER_URL=${process.env.HAPPY_SERVER_URL}`);
|
|
23
|
+
if (process.env.HAPPY_HOME_DIR) lines.push(`Environment=HAPPY_HOME_DIR=${process.env.HAPPY_HOME_DIR}`);
|
|
24
|
+
if (process.env.HAPPY_WEBAPP_URL) lines.push(`Environment=HAPPY_WEBAPP_URL=${process.env.HAPPY_WEBAPP_URL}`);
|
|
25
|
+
return lines.length ? "\n " + lines.join("\n ") : "";
|
|
26
|
+
}
|
|
27
|
+
async function install$1() {
|
|
28
|
+
const daemonEnvironmentLines = environmentLines();
|
|
29
|
+
const runtime = process.execPath;
|
|
30
|
+
const entrypoint = path.join(projectPath(), "dist", "index.mjs");
|
|
31
|
+
if (!existsSync(entrypoint)) {
|
|
32
|
+
throw new Error(`Entrypoint not found: ${entrypoint}. Please build the project first.`);
|
|
33
|
+
}
|
|
34
|
+
const homedir = os.homedir();
|
|
35
|
+
const scope = systemdScopeFor(isRootUser());
|
|
36
|
+
const servicePath = scope.unitPath;
|
|
37
|
+
const serviceContent = trimIdent(`
|
|
38
|
+
[Unit]
|
|
39
|
+
Description=Happy AI CLI Daemon
|
|
40
|
+
After=network-online.target
|
|
41
|
+
Wants=network-online.target
|
|
42
|
+
|
|
43
|
+
[Service]
|
|
44
|
+
Type=simple
|
|
45
|
+
ExecStart=${runtime} --no-warnings --no-deprecation ${entrypoint} daemon start-sync
|
|
46
|
+
Restart=on-failure
|
|
47
|
+
RestartSec=30
|
|
48
|
+
Environment=HOME=${homedir}${daemonEnvironmentLines}
|
|
49
|
+
|
|
50
|
+
[Install]
|
|
51
|
+
WantedBy=${scope.wantedBy}
|
|
52
|
+
`);
|
|
53
|
+
mkdirSync(path.dirname(servicePath), { recursive: true });
|
|
54
|
+
writeFileSync(servicePath, serviceContent + "\n");
|
|
55
|
+
logger.info(`Created systemd ${scope.system ? "system" : "user"} service at ${servicePath}`);
|
|
56
|
+
try {
|
|
57
|
+
execSync(`systemctl ${scope.systemctlFlag} daemon-reload`.trim(), { stdio: "pipe" });
|
|
58
|
+
execSync(`systemctl ${scope.systemctlFlag} enable --now ${SERVICE_NAME}`.trim(), { stdio: "pipe" });
|
|
59
|
+
} catch (error) {
|
|
60
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
61
|
+
throw new Error(
|
|
62
|
+
scope.system ? `Failed to enable the systemd service: ${detail}` : `Failed to enable the systemd user service: ${detail}
|
|
63
|
+
A user service needs a systemd user session (systemctl --user status must work).
|
|
64
|
+
On a server, install it as root instead so it runs without a logged-in session: sudo happy daemon install`
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
if (!scope.system) {
|
|
68
|
+
try {
|
|
69
|
+
execSync(`loginctl enable-linger ${os.userInfo().username}`, { stdio: "pipe" });
|
|
70
|
+
} catch {
|
|
71
|
+
logger.info("Could not enable lingering \u2014 the daemon may stop when you log out. Fix with: sudo loginctl enable-linger $USER");
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
logger.info(scope.system ? "Daemon enabled and started. It will auto-start on boot." : "Daemon enabled and started. It will auto-start on login.");
|
|
75
|
+
logger.info("To disable: happy daemon disable");
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async function install() {
|
|
79
|
+
const credentials = await readCredentials();
|
|
80
|
+
if (!credentials) {
|
|
81
|
+
throw new Error('Not authenticated. Please run "happy auth login" first.');
|
|
82
|
+
}
|
|
83
|
+
if (process.platform === "darwin") {
|
|
84
|
+
logger.info("Setting up daemon auto-start for macOS...");
|
|
85
|
+
await install$2();
|
|
86
|
+
} else if (process.platform === "linux") {
|
|
87
|
+
logger.info("Setting up daemon auto-start for Linux...");
|
|
88
|
+
await install$1();
|
|
89
|
+
} else {
|
|
90
|
+
throw new Error(`Unsupported platform: ${process.platform}. Only macOS and Linux are supported.`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export { install };
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, writeFileSync } from 'fs';
|
|
2
|
+
import { execSync } from 'child_process';
|
|
3
|
+
import os from 'os';
|
|
4
|
+
import { join } from 'path';
|
|
5
|
+
import { l as logger } from './persistence-DKfF81pd.mjs';
|
|
6
|
+
import { t as trimIdent } from './trimIdent-BAjmv_TG.mjs';
|
|
7
|
+
import { p as projectPath } from './projectPath-BTBIMB57.mjs';
|
|
8
|
+
|
|
9
|
+
const PLIST_LABEL = "com.happy-cli.daemon";
|
|
10
|
+
const PLIST_DIR = join(os.homedir(), "Library", "LaunchAgents");
|
|
11
|
+
const PLIST_FILE = join(PLIST_DIR, `${PLIST_LABEL}.plist`);
|
|
12
|
+
const LOG_DIR = join(os.homedir(), ".happy");
|
|
13
|
+
function plistEnvironment() {
|
|
14
|
+
const entries = [["HAPPY_DAEMON_SUPERVISED", "1"]];
|
|
15
|
+
if (process.env.HAPPY_SERVER_URL) entries.push(["HAPPY_SERVER_URL", process.env.HAPPY_SERVER_URL]);
|
|
16
|
+
if (process.env.HAPPY_HOME_DIR) entries.push(["HAPPY_HOME_DIR", process.env.HAPPY_HOME_DIR]);
|
|
17
|
+
if (process.env.HAPPY_WEBAPP_URL) entries.push(["HAPPY_WEBAPP_URL", process.env.HAPPY_WEBAPP_URL]);
|
|
18
|
+
return entries.map(([key, value]) => `
|
|
19
|
+
<key>${key}</key>
|
|
20
|
+
<string>${value}</string>`).join("");
|
|
21
|
+
}
|
|
22
|
+
async function install() {
|
|
23
|
+
const plistEnvironmentEntries = plistEnvironment();
|
|
24
|
+
const runtime = process.execPath;
|
|
25
|
+
const entrypoint = join(projectPath(), "dist", "index.mjs");
|
|
26
|
+
if (!existsSync(PLIST_DIR)) {
|
|
27
|
+
mkdirSync(PLIST_DIR, { recursive: true });
|
|
28
|
+
}
|
|
29
|
+
if (!existsSync(LOG_DIR)) {
|
|
30
|
+
mkdirSync(LOG_DIR, { recursive: true });
|
|
31
|
+
}
|
|
32
|
+
if (existsSync(PLIST_FILE)) {
|
|
33
|
+
logger.info("Daemon plist already exists. Unloading first...");
|
|
34
|
+
try {
|
|
35
|
+
execSync(`launchctl unload ${PLIST_FILE}`, { stdio: "ignore" });
|
|
36
|
+
} catch {
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
const plistContent = trimIdent(`
|
|
40
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
41
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
42
|
+
<plist version="1.0">
|
|
43
|
+
<dict>
|
|
44
|
+
<key>Label</key>
|
|
45
|
+
<string>${PLIST_LABEL}</string>
|
|
46
|
+
|
|
47
|
+
<key>ProgramArguments</key>
|
|
48
|
+
<array>
|
|
49
|
+
<string>${runtime}</string>
|
|
50
|
+
<string>--no-warnings</string>
|
|
51
|
+
<string>--no-deprecation</string>
|
|
52
|
+
<string>${entrypoint}</string>
|
|
53
|
+
<string>daemon</string>
|
|
54
|
+
<string>start-sync</string>
|
|
55
|
+
</array>
|
|
56
|
+
|
|
57
|
+
<key>EnvironmentVariables</key>
|
|
58
|
+
<dict>${plistEnvironmentEntries}
|
|
59
|
+
</dict>
|
|
60
|
+
|
|
61
|
+
<key>RunAtLoad</key>
|
|
62
|
+
<true/>
|
|
63
|
+
|
|
64
|
+
<key>KeepAlive</key>
|
|
65
|
+
<dict>
|
|
66
|
+
<key>SuccessfulExit</key>
|
|
67
|
+
<false/>
|
|
68
|
+
</dict>
|
|
69
|
+
|
|
70
|
+
<key>ThrottleInterval</key>
|
|
71
|
+
<integer>30</integer>
|
|
72
|
+
|
|
73
|
+
<key>StandardOutPath</key>
|
|
74
|
+
<string>${join(LOG_DIR, "daemon.log")}</string>
|
|
75
|
+
|
|
76
|
+
<key>StandardErrorPath</key>
|
|
77
|
+
<string>${join(LOG_DIR, "daemon.err")}</string>
|
|
78
|
+
|
|
79
|
+
<key>WorkingDirectory</key>
|
|
80
|
+
<string>/tmp</string>
|
|
81
|
+
</dict>
|
|
82
|
+
</plist>
|
|
83
|
+
`);
|
|
84
|
+
writeFileSync(PLIST_FILE, plistContent);
|
|
85
|
+
logger.info(`Created LaunchAgent plist at ${PLIST_FILE}`);
|
|
86
|
+
try {
|
|
87
|
+
execSync(`launchctl load ${PLIST_FILE}`, { stdio: "pipe" });
|
|
88
|
+
} catch (error) {
|
|
89
|
+
throw new Error(`Failed to load LaunchAgent. You can try manually: launchctl load ${PLIST_FILE}`);
|
|
90
|
+
}
|
|
91
|
+
logger.info("Daemon enabled and started. It will auto-start on login.");
|
|
92
|
+
logger.info("To disable: happy daemon disable");
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export { PLIST_LABEL as P, install as i };
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var logger = require('./persistence-CBTtC_dm.cjs');
|
|
4
|
+
var install$2 = require('./install-DSDufaPd.cjs');
|
|
5
|
+
var fs = require('fs');
|
|
6
|
+
var child_process = require('child_process');
|
|
7
|
+
var path = require('path');
|
|
8
|
+
var os = require('os');
|
|
9
|
+
var trimIdent = require('./trimIdent-4gvk9Wk6.cjs');
|
|
10
|
+
var projectPath = require('./projectPath-ChLhdOyt.cjs');
|
|
11
|
+
var systemdScope = require('./systemdScope-DW3-UoKB.cjs');
|
|
12
|
+
require('node:fs/promises');
|
|
13
|
+
require('node:fs');
|
|
14
|
+
require('node:os');
|
|
15
|
+
require('node:path');
|
|
16
|
+
require('zod');
|
|
17
|
+
require('node:crypto');
|
|
18
|
+
require('tweetnacl');
|
|
19
|
+
require('chalk');
|
|
20
|
+
require('url');
|
|
21
|
+
|
|
22
|
+
function environmentLines() {
|
|
23
|
+
const lines = ["Environment=HAPPY_DAEMON_SUPERVISED=1"];
|
|
24
|
+
if (process.env.HAPPY_SERVER_URL) lines.push(`Environment=HAPPY_SERVER_URL=${process.env.HAPPY_SERVER_URL}`);
|
|
25
|
+
if (process.env.HAPPY_HOME_DIR) lines.push(`Environment=HAPPY_HOME_DIR=${process.env.HAPPY_HOME_DIR}`);
|
|
26
|
+
if (process.env.HAPPY_WEBAPP_URL) lines.push(`Environment=HAPPY_WEBAPP_URL=${process.env.HAPPY_WEBAPP_URL}`);
|
|
27
|
+
return lines.length ? "\n " + lines.join("\n ") : "";
|
|
28
|
+
}
|
|
29
|
+
async function install$1() {
|
|
30
|
+
const daemonEnvironmentLines = environmentLines();
|
|
31
|
+
const runtime = process.execPath;
|
|
32
|
+
const entrypoint = path.join(projectPath.projectPath(), "dist", "index.mjs");
|
|
33
|
+
if (!fs.existsSync(entrypoint)) {
|
|
34
|
+
throw new Error(`Entrypoint not found: ${entrypoint}. Please build the project first.`);
|
|
35
|
+
}
|
|
36
|
+
const homedir = os.homedir();
|
|
37
|
+
const scope = systemdScope.systemdScopeFor(systemdScope.isRootUser());
|
|
38
|
+
const servicePath = scope.unitPath;
|
|
39
|
+
const serviceContent = trimIdent.trimIdent(`
|
|
40
|
+
[Unit]
|
|
41
|
+
Description=Happy AI CLI Daemon
|
|
42
|
+
After=network-online.target
|
|
43
|
+
Wants=network-online.target
|
|
44
|
+
|
|
45
|
+
[Service]
|
|
46
|
+
Type=simple
|
|
47
|
+
ExecStart=${runtime} --no-warnings --no-deprecation ${entrypoint} daemon start-sync
|
|
48
|
+
Restart=on-failure
|
|
49
|
+
RestartSec=30
|
|
50
|
+
Environment=HOME=${homedir}${daemonEnvironmentLines}
|
|
51
|
+
|
|
52
|
+
[Install]
|
|
53
|
+
WantedBy=${scope.wantedBy}
|
|
54
|
+
`);
|
|
55
|
+
fs.mkdirSync(path.dirname(servicePath), { recursive: true });
|
|
56
|
+
fs.writeFileSync(servicePath, serviceContent + "\n");
|
|
57
|
+
logger.logger.info(`Created systemd ${scope.system ? "system" : "user"} service at ${servicePath}`);
|
|
58
|
+
try {
|
|
59
|
+
child_process.execSync(`systemctl ${scope.systemctlFlag} daemon-reload`.trim(), { stdio: "pipe" });
|
|
60
|
+
child_process.execSync(`systemctl ${scope.systemctlFlag} enable --now ${systemdScope.SERVICE_NAME}`.trim(), { stdio: "pipe" });
|
|
61
|
+
} catch (error) {
|
|
62
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
63
|
+
throw new Error(
|
|
64
|
+
scope.system ? `Failed to enable the systemd service: ${detail}` : `Failed to enable the systemd user service: ${detail}
|
|
65
|
+
A user service needs a systemd user session (systemctl --user status must work).
|
|
66
|
+
On a server, install it as root instead so it runs without a logged-in session: sudo happy daemon install`
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
if (!scope.system) {
|
|
70
|
+
try {
|
|
71
|
+
child_process.execSync(`loginctl enable-linger ${os.userInfo().username}`, { stdio: "pipe" });
|
|
72
|
+
} catch {
|
|
73
|
+
logger.logger.info("Could not enable lingering \u2014 the daemon may stop when you log out. Fix with: sudo loginctl enable-linger $USER");
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
logger.logger.info(scope.system ? "Daemon enabled and started. It will auto-start on boot." : "Daemon enabled and started. It will auto-start on login.");
|
|
77
|
+
logger.logger.info("To disable: happy daemon disable");
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async function install() {
|
|
81
|
+
const credentials = await logger.readCredentials();
|
|
82
|
+
if (!credentials) {
|
|
83
|
+
throw new Error('Not authenticated. Please run "happy auth login" first.');
|
|
84
|
+
}
|
|
85
|
+
if (process.platform === "darwin") {
|
|
86
|
+
logger.logger.info("Setting up daemon auto-start for macOS...");
|
|
87
|
+
await install$2.install();
|
|
88
|
+
} else if (process.platform === "linux") {
|
|
89
|
+
logger.logger.info("Setting up daemon auto-start for Linux...");
|
|
90
|
+
await install$1();
|
|
91
|
+
} else {
|
|
92
|
+
throw new Error(`Unsupported platform: ${process.platform}. Only macOS and Linux are supported.`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
exports.install = install;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var fs = require('fs');
|
|
4
|
+
var child_process = require('child_process');
|
|
5
|
+
var os = require('os');
|
|
6
|
+
var path = require('path');
|
|
7
|
+
var logger = require('./persistence-CBTtC_dm.cjs');
|
|
8
|
+
var trimIdent = require('./trimIdent-4gvk9Wk6.cjs');
|
|
9
|
+
var projectPath = require('./projectPath-ChLhdOyt.cjs');
|
|
10
|
+
|
|
11
|
+
const PLIST_LABEL = "com.happy-cli.daemon";
|
|
12
|
+
const PLIST_DIR = path.join(os.homedir(), "Library", "LaunchAgents");
|
|
13
|
+
const PLIST_FILE = path.join(PLIST_DIR, `${PLIST_LABEL}.plist`);
|
|
14
|
+
const LOG_DIR = path.join(os.homedir(), ".happy");
|
|
15
|
+
function plistEnvironment() {
|
|
16
|
+
const entries = [["HAPPY_DAEMON_SUPERVISED", "1"]];
|
|
17
|
+
if (process.env.HAPPY_SERVER_URL) entries.push(["HAPPY_SERVER_URL", process.env.HAPPY_SERVER_URL]);
|
|
18
|
+
if (process.env.HAPPY_HOME_DIR) entries.push(["HAPPY_HOME_DIR", process.env.HAPPY_HOME_DIR]);
|
|
19
|
+
if (process.env.HAPPY_WEBAPP_URL) entries.push(["HAPPY_WEBAPP_URL", process.env.HAPPY_WEBAPP_URL]);
|
|
20
|
+
return entries.map(([key, value]) => `
|
|
21
|
+
<key>${key}</key>
|
|
22
|
+
<string>${value}</string>`).join("");
|
|
23
|
+
}
|
|
24
|
+
async function install() {
|
|
25
|
+
const plistEnvironmentEntries = plistEnvironment();
|
|
26
|
+
const runtime = process.execPath;
|
|
27
|
+
const entrypoint = path.join(projectPath.projectPath(), "dist", "index.mjs");
|
|
28
|
+
if (!fs.existsSync(PLIST_DIR)) {
|
|
29
|
+
fs.mkdirSync(PLIST_DIR, { recursive: true });
|
|
30
|
+
}
|
|
31
|
+
if (!fs.existsSync(LOG_DIR)) {
|
|
32
|
+
fs.mkdirSync(LOG_DIR, { recursive: true });
|
|
33
|
+
}
|
|
34
|
+
if (fs.existsSync(PLIST_FILE)) {
|
|
35
|
+
logger.logger.info("Daemon plist already exists. Unloading first...");
|
|
36
|
+
try {
|
|
37
|
+
child_process.execSync(`launchctl unload ${PLIST_FILE}`, { stdio: "ignore" });
|
|
38
|
+
} catch {
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
const plistContent = trimIdent.trimIdent(`
|
|
42
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
43
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
44
|
+
<plist version="1.0">
|
|
45
|
+
<dict>
|
|
46
|
+
<key>Label</key>
|
|
47
|
+
<string>${PLIST_LABEL}</string>
|
|
48
|
+
|
|
49
|
+
<key>ProgramArguments</key>
|
|
50
|
+
<array>
|
|
51
|
+
<string>${runtime}</string>
|
|
52
|
+
<string>--no-warnings</string>
|
|
53
|
+
<string>--no-deprecation</string>
|
|
54
|
+
<string>${entrypoint}</string>
|
|
55
|
+
<string>daemon</string>
|
|
56
|
+
<string>start-sync</string>
|
|
57
|
+
</array>
|
|
58
|
+
|
|
59
|
+
<key>EnvironmentVariables</key>
|
|
60
|
+
<dict>${plistEnvironmentEntries}
|
|
61
|
+
</dict>
|
|
62
|
+
|
|
63
|
+
<key>RunAtLoad</key>
|
|
64
|
+
<true/>
|
|
65
|
+
|
|
66
|
+
<key>KeepAlive</key>
|
|
67
|
+
<dict>
|
|
68
|
+
<key>SuccessfulExit</key>
|
|
69
|
+
<false/>
|
|
70
|
+
</dict>
|
|
71
|
+
|
|
72
|
+
<key>ThrottleInterval</key>
|
|
73
|
+
<integer>30</integer>
|
|
74
|
+
|
|
75
|
+
<key>StandardOutPath</key>
|
|
76
|
+
<string>${path.join(LOG_DIR, "daemon.log")}</string>
|
|
77
|
+
|
|
78
|
+
<key>StandardErrorPath</key>
|
|
79
|
+
<string>${path.join(LOG_DIR, "daemon.err")}</string>
|
|
80
|
+
|
|
81
|
+
<key>WorkingDirectory</key>
|
|
82
|
+
<string>/tmp</string>
|
|
83
|
+
</dict>
|
|
84
|
+
</plist>
|
|
85
|
+
`);
|
|
86
|
+
fs.writeFileSync(PLIST_FILE, plistContent);
|
|
87
|
+
logger.logger.info(`Created LaunchAgent plist at ${PLIST_FILE}`);
|
|
88
|
+
try {
|
|
89
|
+
child_process.execSync(`launchctl load ${PLIST_FILE}`, { stdio: "pipe" });
|
|
90
|
+
} catch (error) {
|
|
91
|
+
throw new Error(`Failed to load LaunchAgent. You can try manually: launchctl load ${PLIST_FILE}`);
|
|
92
|
+
}
|
|
93
|
+
logger.logger.info("Daemon enabled and started. It will auto-start on login.");
|
|
94
|
+
logger.logger.info("To disable: happy daemon disable");
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
exports.PLIST_LABEL = PLIST_LABEL;
|
|
98
|
+
exports.install = install;
|
package/dist/lib.cjs
CHANGED
|
@@ -1,37 +1,43 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var api = require('./api-C7FyJfZ1.cjs');
|
|
4
|
+
var logger = require('./persistence-CBTtC_dm.cjs');
|
|
5
|
+
var types = require('./types-DWeB7aqh.cjs');
|
|
4
6
|
require('axios');
|
|
5
|
-
require('chalk');
|
|
6
|
-
require('fs');
|
|
7
|
-
require('node:fs');
|
|
8
|
-
require('node:os');
|
|
9
|
-
require('node:path');
|
|
10
7
|
require('node:events');
|
|
11
8
|
require('socket.io-client');
|
|
12
9
|
require('zod');
|
|
10
|
+
require('./index-CDTNgfUf.cjs');
|
|
13
11
|
require('@paralleldrive/cuid2');
|
|
12
|
+
require('./time-Bj2HRkKH.cjs');
|
|
14
13
|
require('node:crypto');
|
|
15
|
-
require('
|
|
14
|
+
require('node:fs');
|
|
15
|
+
require('node:path');
|
|
16
16
|
require('node:child_process');
|
|
17
17
|
require('node:fs/promises');
|
|
18
|
+
require('node:os');
|
|
18
19
|
require('node:util');
|
|
19
20
|
require('child_process');
|
|
20
21
|
require('util');
|
|
21
22
|
require('fs/promises');
|
|
22
23
|
require('path');
|
|
24
|
+
require('./projectPath-ChLhdOyt.cjs');
|
|
23
25
|
require('url');
|
|
24
26
|
require('node:module');
|
|
25
27
|
require('ws');
|
|
26
28
|
require('crypto');
|
|
29
|
+
require('tweetnacl');
|
|
27
30
|
require('@stablelib/base64');
|
|
28
31
|
require('node:readline');
|
|
29
32
|
require('expo-server-sdk');
|
|
33
|
+
require('./sessionKeyCache-CDmexJ9p.cjs');
|
|
34
|
+
require('chalk');
|
|
35
|
+
require('fs');
|
|
30
36
|
|
|
31
37
|
|
|
32
38
|
|
|
33
|
-
exports.ApiClient =
|
|
34
|
-
exports.ApiSessionClient =
|
|
35
|
-
exports.
|
|
36
|
-
exports.
|
|
37
|
-
exports.
|
|
39
|
+
exports.ApiClient = api.ApiClient;
|
|
40
|
+
exports.ApiSessionClient = api.ApiSessionClient;
|
|
41
|
+
exports.configuration = logger.configuration;
|
|
42
|
+
exports.logger = logger.logger;
|
|
43
|
+
exports.RawJSONLinesSchema = types.RawJSONLinesSchema;
|
package/dist/lib.d.cts
CHANGED
|
@@ -1606,6 +1606,80 @@ type SpawnSessionResult = {
|
|
|
1606
1606
|
* Similar to ApiSessionClient but for machine-scoped connections
|
|
1607
1607
|
*/
|
|
1608
1608
|
|
|
1609
|
+
interface ServerToDaemonEvents {
|
|
1610
|
+
update: (data: Update) => void;
|
|
1611
|
+
'rpc-request': (data: {
|
|
1612
|
+
method: string;
|
|
1613
|
+
params: string;
|
|
1614
|
+
}, callback: (response: string) => void) => void;
|
|
1615
|
+
'rpc-registered': (data: {
|
|
1616
|
+
method: string;
|
|
1617
|
+
}) => void;
|
|
1618
|
+
'rpc-unregistered': (data: {
|
|
1619
|
+
method: string;
|
|
1620
|
+
}) => void;
|
|
1621
|
+
'rpc-error': (data: {
|
|
1622
|
+
type: string;
|
|
1623
|
+
error: string;
|
|
1624
|
+
}) => void;
|
|
1625
|
+
auth: (data: {
|
|
1626
|
+
success: boolean;
|
|
1627
|
+
user: string;
|
|
1628
|
+
}) => void;
|
|
1629
|
+
error: (data: {
|
|
1630
|
+
message: string;
|
|
1631
|
+
}) => void;
|
|
1632
|
+
}
|
|
1633
|
+
interface DaemonToServerEvents {
|
|
1634
|
+
'machine-alive': (data: {
|
|
1635
|
+
machineId: string;
|
|
1636
|
+
time: number;
|
|
1637
|
+
}) => void;
|
|
1638
|
+
'machine-update-metadata': (data: {
|
|
1639
|
+
machineId: string;
|
|
1640
|
+
metadata: string;
|
|
1641
|
+
expectedVersion: number;
|
|
1642
|
+
}, cb: (answer: {
|
|
1643
|
+
result: 'error';
|
|
1644
|
+
} | {
|
|
1645
|
+
result: 'version-mismatch';
|
|
1646
|
+
version: number;
|
|
1647
|
+
metadata: string;
|
|
1648
|
+
} | {
|
|
1649
|
+
result: 'success';
|
|
1650
|
+
version: number;
|
|
1651
|
+
metadata: string;
|
|
1652
|
+
}) => void) => void;
|
|
1653
|
+
'machine-update-state': (data: {
|
|
1654
|
+
machineId: string;
|
|
1655
|
+
daemonState: string;
|
|
1656
|
+
expectedVersion: number;
|
|
1657
|
+
}, cb: (answer: {
|
|
1658
|
+
result: 'error';
|
|
1659
|
+
} | {
|
|
1660
|
+
result: 'version-mismatch';
|
|
1661
|
+
version: number;
|
|
1662
|
+
daemonState: string;
|
|
1663
|
+
} | {
|
|
1664
|
+
result: 'success';
|
|
1665
|
+
version: number;
|
|
1666
|
+
daemonState: string;
|
|
1667
|
+
}) => void) => void;
|
|
1668
|
+
'rpc-register': (data: {
|
|
1669
|
+
method: string;
|
|
1670
|
+
}) => void;
|
|
1671
|
+
'rpc-unregister': (data: {
|
|
1672
|
+
method: string;
|
|
1673
|
+
}) => void;
|
|
1674
|
+
'rpc-call': (data: {
|
|
1675
|
+
method: string;
|
|
1676
|
+
params: any;
|
|
1677
|
+
}, callback: (response: {
|
|
1678
|
+
ok: boolean;
|
|
1679
|
+
result?: any;
|
|
1680
|
+
error?: string;
|
|
1681
|
+
}) => void) => void;
|
|
1682
|
+
}
|
|
1609
1683
|
type MachineRpcHandlers = {
|
|
1610
1684
|
spawnSession: (options: SpawnSessionOptions) => Promise<SpawnSessionResult>;
|
|
1611
1685
|
stopSession: (sessionId: string) => boolean;
|
|
@@ -1641,6 +1715,9 @@ declare class ApiMachineClient {
|
|
|
1641
1715
|
private machine;
|
|
1642
1716
|
private currentRuntimeDaemonState?;
|
|
1643
1717
|
private socket;
|
|
1718
|
+
/** The daemon's live connection. Reused for device RPC so a CLI command does
|
|
1719
|
+
* not spend ~500ms building its own TLS + WebSocket handshake. */
|
|
1720
|
+
get rpcSocket(): Socket<ServerToDaemonEvents, DaemonToServerEvents>;
|
|
1644
1721
|
private keepAliveInterval;
|
|
1645
1722
|
private rpcHandlerManager;
|
|
1646
1723
|
private claudeCache;
|
package/dist/lib.d.mts
CHANGED
|
@@ -1606,6 +1606,80 @@ type SpawnSessionResult = {
|
|
|
1606
1606
|
* Similar to ApiSessionClient but for machine-scoped connections
|
|
1607
1607
|
*/
|
|
1608
1608
|
|
|
1609
|
+
interface ServerToDaemonEvents {
|
|
1610
|
+
update: (data: Update) => void;
|
|
1611
|
+
'rpc-request': (data: {
|
|
1612
|
+
method: string;
|
|
1613
|
+
params: string;
|
|
1614
|
+
}, callback: (response: string) => void) => void;
|
|
1615
|
+
'rpc-registered': (data: {
|
|
1616
|
+
method: string;
|
|
1617
|
+
}) => void;
|
|
1618
|
+
'rpc-unregistered': (data: {
|
|
1619
|
+
method: string;
|
|
1620
|
+
}) => void;
|
|
1621
|
+
'rpc-error': (data: {
|
|
1622
|
+
type: string;
|
|
1623
|
+
error: string;
|
|
1624
|
+
}) => void;
|
|
1625
|
+
auth: (data: {
|
|
1626
|
+
success: boolean;
|
|
1627
|
+
user: string;
|
|
1628
|
+
}) => void;
|
|
1629
|
+
error: (data: {
|
|
1630
|
+
message: string;
|
|
1631
|
+
}) => void;
|
|
1632
|
+
}
|
|
1633
|
+
interface DaemonToServerEvents {
|
|
1634
|
+
'machine-alive': (data: {
|
|
1635
|
+
machineId: string;
|
|
1636
|
+
time: number;
|
|
1637
|
+
}) => void;
|
|
1638
|
+
'machine-update-metadata': (data: {
|
|
1639
|
+
machineId: string;
|
|
1640
|
+
metadata: string;
|
|
1641
|
+
expectedVersion: number;
|
|
1642
|
+
}, cb: (answer: {
|
|
1643
|
+
result: 'error';
|
|
1644
|
+
} | {
|
|
1645
|
+
result: 'version-mismatch';
|
|
1646
|
+
version: number;
|
|
1647
|
+
metadata: string;
|
|
1648
|
+
} | {
|
|
1649
|
+
result: 'success';
|
|
1650
|
+
version: number;
|
|
1651
|
+
metadata: string;
|
|
1652
|
+
}) => void) => void;
|
|
1653
|
+
'machine-update-state': (data: {
|
|
1654
|
+
machineId: string;
|
|
1655
|
+
daemonState: string;
|
|
1656
|
+
expectedVersion: number;
|
|
1657
|
+
}, cb: (answer: {
|
|
1658
|
+
result: 'error';
|
|
1659
|
+
} | {
|
|
1660
|
+
result: 'version-mismatch';
|
|
1661
|
+
version: number;
|
|
1662
|
+
daemonState: string;
|
|
1663
|
+
} | {
|
|
1664
|
+
result: 'success';
|
|
1665
|
+
version: number;
|
|
1666
|
+
daemonState: string;
|
|
1667
|
+
}) => void) => void;
|
|
1668
|
+
'rpc-register': (data: {
|
|
1669
|
+
method: string;
|
|
1670
|
+
}) => void;
|
|
1671
|
+
'rpc-unregister': (data: {
|
|
1672
|
+
method: string;
|
|
1673
|
+
}) => void;
|
|
1674
|
+
'rpc-call': (data: {
|
|
1675
|
+
method: string;
|
|
1676
|
+
params: any;
|
|
1677
|
+
}, callback: (response: {
|
|
1678
|
+
ok: boolean;
|
|
1679
|
+
result?: any;
|
|
1680
|
+
error?: string;
|
|
1681
|
+
}) => void) => void;
|
|
1682
|
+
}
|
|
1609
1683
|
type MachineRpcHandlers = {
|
|
1610
1684
|
spawnSession: (options: SpawnSessionOptions) => Promise<SpawnSessionResult>;
|
|
1611
1685
|
stopSession: (sessionId: string) => boolean;
|
|
@@ -1641,6 +1715,9 @@ declare class ApiMachineClient {
|
|
|
1641
1715
|
private machine;
|
|
1642
1716
|
private currentRuntimeDaemonState?;
|
|
1643
1717
|
private socket;
|
|
1718
|
+
/** The daemon's live connection. Reused for device RPC so a CLI command does
|
|
1719
|
+
* not spend ~500ms building its own TLS + WebSocket handshake. */
|
|
1720
|
+
get rpcSocket(): Socket<ServerToDaemonEvents, DaemonToServerEvents>;
|
|
1644
1721
|
private keepAliveInterval;
|
|
1645
1722
|
private rpcHandlerManager;
|
|
1646
1723
|
private claudeCache;
|
package/dist/lib.mjs
CHANGED
|
@@ -1,27 +1,33 @@
|
|
|
1
|
-
export { A as ApiClient, a as ApiSessionClient
|
|
1
|
+
export { A as ApiClient, a as ApiSessionClient } from './api-D6WL40-U.mjs';
|
|
2
|
+
export { c as configuration, l as logger } from './persistence-DKfF81pd.mjs';
|
|
3
|
+
export { R as RawJSONLinesSchema } from './types-BoJh4Qfv.mjs';
|
|
2
4
|
import 'axios';
|
|
3
|
-
import 'chalk';
|
|
4
|
-
import 'fs';
|
|
5
|
-
import 'node:fs';
|
|
6
|
-
import 'node:os';
|
|
7
|
-
import 'node:path';
|
|
8
5
|
import 'node:events';
|
|
9
6
|
import 'socket.io-client';
|
|
10
7
|
import 'zod';
|
|
8
|
+
import './index-CPKdvi7J.mjs';
|
|
11
9
|
import '@paralleldrive/cuid2';
|
|
10
|
+
import './time-DgsBLJA_.mjs';
|
|
12
11
|
import 'node:crypto';
|
|
13
|
-
import '
|
|
12
|
+
import 'node:fs';
|
|
13
|
+
import 'node:path';
|
|
14
14
|
import 'node:child_process';
|
|
15
15
|
import 'node:fs/promises';
|
|
16
|
+
import 'node:os';
|
|
16
17
|
import 'node:util';
|
|
17
18
|
import 'child_process';
|
|
18
19
|
import 'util';
|
|
19
20
|
import 'fs/promises';
|
|
20
21
|
import 'path';
|
|
22
|
+
import './projectPath-BTBIMB57.mjs';
|
|
21
23
|
import 'url';
|
|
22
24
|
import 'node:module';
|
|
23
25
|
import 'ws';
|
|
24
26
|
import 'crypto';
|
|
27
|
+
import 'tweetnacl';
|
|
25
28
|
import '@stablelib/base64';
|
|
26
29
|
import 'node:readline';
|
|
27
30
|
import 'expo-server-sdk';
|
|
31
|
+
import './sessionKeyCache-B6cGadYL.mjs';
|
|
32
|
+
import 'chalk';
|
|
33
|
+
import 'fs';
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import { l as logger,
|
|
1
|
+
import { l as logger, c as configuration, p as packageJson } from './persistence-DKfF81pd.mjs';
|
|
2
2
|
import { randomUUID } from 'node:crypto';
|
|
3
3
|
import { readFileSync, existsSync, readdirSync } from 'node:fs';
|
|
4
4
|
import os, { homedir } from 'node:os';
|
|
5
5
|
import { join, basename, dirname } from 'node:path';
|
|
6
|
-
import {
|
|
6
|
+
import { p as projectPath } from './projectPath-BTBIMB57.mjs';
|
|
7
|
+
import { r as resolveToolsDir, s as startOfflineReconnection, b as saveToolOutputRecord } from './api-D6WL40-U.mjs';
|
|
8
|
+
import { a as detectGitWorktree } from './downloadImage-DZXI3mXL.mjs';
|
|
7
9
|
|
|
8
10
|
class BasePermissionHandler {
|
|
9
11
|
pendingRequests = /* @__PURE__ */ new Map();
|