hatchee 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.mjs +196 -7
- package/package.json +1 -1
package/dist/cli.mjs
CHANGED
|
@@ -12165,8 +12165,171 @@ async function runHook(hookPort) {
|
|
|
12165
12165
|
process.exit(0);
|
|
12166
12166
|
}
|
|
12167
12167
|
|
|
12168
|
+
// src/service.ts
|
|
12169
|
+
import { homedir as homedir3 } from "node:os";
|
|
12170
|
+
import { join as join3, dirname } from "node:path";
|
|
12171
|
+
import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync3, copyFileSync, chmodSync, existsSync as existsSync3, rmSync } from "node:fs";
|
|
12172
|
+
import { execFileSync } from "node:child_process";
|
|
12173
|
+
var HOME = homedir3();
|
|
12174
|
+
var HATCHEE_DIR = join3(HOME, ".hatchee");
|
|
12175
|
+
var BIN_DIR = join3(HATCHEE_DIR, "bin");
|
|
12176
|
+
var STABLE_BIN = join3(BIN_DIR, "hatchee.mjs");
|
|
12177
|
+
var LOG = join3(HATCHEE_DIR, "daemon.log");
|
|
12178
|
+
var LABEL = "cloud.hatchee.daemon";
|
|
12179
|
+
var PLIST = join3(HOME, "Library", "LaunchAgents", `${LABEL}.plist`);
|
|
12180
|
+
var UNIT = join3(HOME, ".config", "systemd", "user", "hatchee.service");
|
|
12181
|
+
function servicePath(node) {
|
|
12182
|
+
return [
|
|
12183
|
+
dirname(node),
|
|
12184
|
+
join3(HOME, ".bun/bin"),
|
|
12185
|
+
join3(HOME, ".npm-global/bin"),
|
|
12186
|
+
join3(HOME, ".local/bin"),
|
|
12187
|
+
"/opt/homebrew/bin",
|
|
12188
|
+
"/usr/local/bin",
|
|
12189
|
+
"/usr/bin",
|
|
12190
|
+
"/bin",
|
|
12191
|
+
"/usr/sbin",
|
|
12192
|
+
"/sbin"
|
|
12193
|
+
].join(":");
|
|
12194
|
+
}
|
|
12195
|
+
function copyStableBin() {
|
|
12196
|
+
mkdirSync3(BIN_DIR, { recursive: true });
|
|
12197
|
+
const src = process.argv[1];
|
|
12198
|
+
if (src && src !== STABLE_BIN) {
|
|
12199
|
+
copyFileSync(src, STABLE_BIN);
|
|
12200
|
+
chmodSync(STABLE_BIN, 493);
|
|
12201
|
+
}
|
|
12202
|
+
}
|
|
12203
|
+
function plistContent(node, bin) {
|
|
12204
|
+
const args = [node, bin, "up"].map((s) => ` <string>${s}</string>`).join(`
|
|
12205
|
+
`);
|
|
12206
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
12207
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
12208
|
+
<plist version="1.0">
|
|
12209
|
+
<dict>
|
|
12210
|
+
<key>Label</key><string>${LABEL}</string>
|
|
12211
|
+
<key>ProgramArguments</key>
|
|
12212
|
+
<array>
|
|
12213
|
+
${args}
|
|
12214
|
+
</array>
|
|
12215
|
+
<key>RunAtLoad</key><true/>
|
|
12216
|
+
<key>KeepAlive</key><true/>
|
|
12217
|
+
<key>ThrottleInterval</key><integer>10</integer>
|
|
12218
|
+
<key>StandardOutPath</key><string>${LOG}</string>
|
|
12219
|
+
<key>StandardErrorPath</key><string>${LOG}</string>
|
|
12220
|
+
<key>EnvironmentVariables</key>
|
|
12221
|
+
<dict><key>PATH</key><string>${servicePath(node)}</string></dict>
|
|
12222
|
+
</dict>
|
|
12223
|
+
</plist>
|
|
12224
|
+
`;
|
|
12225
|
+
}
|
|
12226
|
+
function unitContent(node, bin) {
|
|
12227
|
+
return `[Unit]
|
|
12228
|
+
Description=Hatchee — watch & approve your coding agents from your phone
|
|
12229
|
+
After=network-online.target
|
|
12230
|
+
|
|
12231
|
+
[Service]
|
|
12232
|
+
ExecStart=${node} ${bin} up
|
|
12233
|
+
Restart=always
|
|
12234
|
+
RestartSec=10
|
|
12235
|
+
Environment=PATH=${servicePath(node)}
|
|
12236
|
+
|
|
12237
|
+
[Install]
|
|
12238
|
+
WantedBy=default.target
|
|
12239
|
+
`;
|
|
12240
|
+
}
|
|
12241
|
+
function run(cmd, args) {
|
|
12242
|
+
try {
|
|
12243
|
+
execFileSync(cmd, args, { stdio: "pipe" });
|
|
12244
|
+
} catch (e) {
|
|
12245
|
+
console.log(` · ${cmd} ${args.join(" ")} → ${String(e?.stderr || e?.message || e).trim().slice(0, 200)}`);
|
|
12246
|
+
}
|
|
12247
|
+
}
|
|
12248
|
+
function installService(print = false) {
|
|
12249
|
+
const node = process.execPath;
|
|
12250
|
+
if (process.platform === "darwin") {
|
|
12251
|
+
const content = plistContent(node, STABLE_BIN);
|
|
12252
|
+
if (print) {
|
|
12253
|
+
console.log(`
|
|
12254
|
+
would write ${PLIST}:
|
|
12255
|
+
|
|
12256
|
+
${content}
|
|
12257
|
+
bundle → ${STABLE_BIN}
|
|
12258
|
+
load → launchctl bootstrap gui/$(id -u) ${PLIST}
|
|
12259
|
+
`);
|
|
12260
|
+
return;
|
|
12261
|
+
}
|
|
12262
|
+
copyStableBin();
|
|
12263
|
+
mkdirSync3(dirname(PLIST), { recursive: true });
|
|
12264
|
+
writeFileSync3(PLIST, content);
|
|
12265
|
+
const uid = String(process.getuid?.() ?? "");
|
|
12266
|
+
run("launchctl", ["bootout", `gui/${uid}/${LABEL}`]);
|
|
12267
|
+
run("launchctl", ["bootstrap", `gui/${uid}`, PLIST]);
|
|
12268
|
+
run("launchctl", ["enable", `gui/${uid}/${LABEL}`]);
|
|
12269
|
+
console.log(`
|
|
12270
|
+
✓ Hatchee will now run in the background (launchd: ${LABEL})`);
|
|
12271
|
+
console.log(` logs ${LOG}`);
|
|
12272
|
+
console.log(` pair npx hatchee pair (one terminal, once — to add a phone)`);
|
|
12273
|
+
console.log(` stop npx hatchee uninstall-service`);
|
|
12274
|
+
console.log(`
|
|
12275
|
+
Don't also run \`hatchee up\` manually — the service is the single instance.
|
|
12276
|
+
`);
|
|
12277
|
+
return;
|
|
12278
|
+
}
|
|
12279
|
+
if (process.platform === "linux") {
|
|
12280
|
+
const content = unitContent(node, STABLE_BIN);
|
|
12281
|
+
if (print) {
|
|
12282
|
+
console.log(`
|
|
12283
|
+
would write ${UNIT}:
|
|
12284
|
+
|
|
12285
|
+
${content}
|
|
12286
|
+
bundle → ${STABLE_BIN}
|
|
12287
|
+
enable → systemctl --user enable --now hatchee.service (+ loginctl enable-linger)
|
|
12288
|
+
`);
|
|
12289
|
+
return;
|
|
12290
|
+
}
|
|
12291
|
+
copyStableBin();
|
|
12292
|
+
mkdirSync3(dirname(UNIT), { recursive: true });
|
|
12293
|
+
writeFileSync3(UNIT, content);
|
|
12294
|
+
run("systemctl", ["--user", "daemon-reload"]);
|
|
12295
|
+
run("systemctl", ["--user", "enable", "--now", "hatchee.service"]);
|
|
12296
|
+
run("loginctl", ["enable-linger", process.env.USER ?? ""]);
|
|
12297
|
+
console.log(`
|
|
12298
|
+
✓ Hatchee will now run in the background (systemd --user: hatchee.service)`);
|
|
12299
|
+
console.log(` logs journalctl --user -u hatchee.service -f`);
|
|
12300
|
+
console.log(` pair npx hatchee pair`);
|
|
12301
|
+
console.log(` stop npx hatchee uninstall-service
|
|
12302
|
+
`);
|
|
12303
|
+
return;
|
|
12304
|
+
}
|
|
12305
|
+
console.log(`
|
|
12306
|
+
Auto-start service isn't supported on ${process.platform} yet.`);
|
|
12307
|
+
console.log(` Run \`npx hatchee up\` in a terminal you keep open (or use WSL on Windows).
|
|
12308
|
+
`);
|
|
12309
|
+
}
|
|
12310
|
+
function uninstallService() {
|
|
12311
|
+
if (process.platform === "darwin") {
|
|
12312
|
+
const uid = String(process.getuid?.() ?? "");
|
|
12313
|
+
run("launchctl", ["bootout", `gui/${uid}/${LABEL}`]);
|
|
12314
|
+
if (existsSync3(PLIST))
|
|
12315
|
+
rmSync(PLIST);
|
|
12316
|
+
console.log(` ✓ background service removed (launchd: ${LABEL})`);
|
|
12317
|
+
} else if (process.platform === "linux") {
|
|
12318
|
+
run("systemctl", ["--user", "disable", "--now", "hatchee.service"]);
|
|
12319
|
+
if (existsSync3(UNIT)) {
|
|
12320
|
+
rmSync(UNIT);
|
|
12321
|
+
run("systemctl", ["--user", "daemon-reload"]);
|
|
12322
|
+
}
|
|
12323
|
+
console.log(` ✓ background service removed (systemd --user: hatchee.service)`);
|
|
12324
|
+
} else {
|
|
12325
|
+
console.log(` no background service on ${process.platform}.`);
|
|
12326
|
+
}
|
|
12327
|
+
if (existsSync3(STABLE_BIN))
|
|
12328
|
+
rmSync(STABLE_BIN);
|
|
12329
|
+
}
|
|
12330
|
+
|
|
12168
12331
|
// src/cli.ts
|
|
12169
|
-
var VERSION2 = "0.1.
|
|
12332
|
+
var VERSION2 = "0.1.2";
|
|
12170
12333
|
var cmd = process.argv[2] ?? "help";
|
|
12171
12334
|
switch (cmd) {
|
|
12172
12335
|
case "up": {
|
|
@@ -12192,6 +12355,7 @@ switch (cmd) {
|
|
|
12192
12355
|
}
|
|
12193
12356
|
console.log(`
|
|
12194
12357
|
watching for Claude Code sessions\u2026 (run \`claude\` anywhere)`);
|
|
12358
|
+
tips();
|
|
12195
12359
|
break;
|
|
12196
12360
|
}
|
|
12197
12361
|
case "pair": {
|
|
@@ -12227,6 +12391,7 @@ switch (cmd) {
|
|
|
12227
12391
|
console.log(` code ${code} (valid 5 minutes, single use)
|
|
12228
12392
|
`);
|
|
12229
12393
|
console.log(` keeping the daemon running after pairing\u2026 (^C to stop)`);
|
|
12394
|
+
tips();
|
|
12230
12395
|
});
|
|
12231
12396
|
break;
|
|
12232
12397
|
}
|
|
@@ -12257,6 +12422,12 @@ switch (cmd) {
|
|
|
12257
12422
|
console.log(`revoked ${dev.name}`);
|
|
12258
12423
|
break;
|
|
12259
12424
|
}
|
|
12425
|
+
case "install-service":
|
|
12426
|
+
installService(process.argv[3] === "--print" || process.argv[3] === "--dry-run");
|
|
12427
|
+
break;
|
|
12428
|
+
case "uninstall-service":
|
|
12429
|
+
uninstallService();
|
|
12430
|
+
break;
|
|
12260
12431
|
case "uninstall-hooks":
|
|
12261
12432
|
uninstallHooks();
|
|
12262
12433
|
console.log("hatchee hooks removed from ~/.claude/settings.json");
|
|
@@ -12269,18 +12440,36 @@ switch (cmd) {
|
|
|
12269
12440
|
default:
|
|
12270
12441
|
banner();
|
|
12271
12442
|
console.log(` usage:
|
|
12272
|
-
hatchee
|
|
12273
|
-
hatchee
|
|
12274
|
-
hatchee
|
|
12275
|
-
hatchee
|
|
12276
|
-
hatchee
|
|
12277
|
-
hatchee
|
|
12443
|
+
hatchee pair pair a phone \u2014 prints a QR + 6-digit code (do this first)
|
|
12444
|
+
hatchee up start watching (no new pairing) \u2014 installs Claude Code hooks
|
|
12445
|
+
hatchee install-service run in the background across logins/reboots (recommended)
|
|
12446
|
+
hatchee uninstall-service stop running in the background
|
|
12447
|
+
hatchee devices list paired phones
|
|
12448
|
+
hatchee revoke <id> revoke a lost phone
|
|
12449
|
+
hatchee uninstall-hooks remove Hatchee's hooks from ~/.claude/settings.json
|
|
12450
|
+
hatchee version
|
|
12451
|
+
|
|
12452
|
+
keep it on npx hatchee install-service (so it survives closing the terminal)
|
|
12453
|
+
update npx hatchee@latest pair (npx always fetches the newest)
|
|
12454
|
+
uninstall npx hatchee uninstall-service && npx hatchee uninstall-hooks && rm -rf ~/.hatchee
|
|
12455
|
+
config ~/.hatchee/config.json (your daemon identity + paired devices)`);
|
|
12278
12456
|
}
|
|
12279
12457
|
function banner() {
|
|
12280
12458
|
console.log(`
|
|
12281
12459
|
\uD83D\uDC23 hatchee ${VERSION2} \u2014 your coding agents, alive on your lock screen
|
|
12282
12460
|
`);
|
|
12283
12461
|
}
|
|
12462
|
+
function tips() {
|
|
12463
|
+
console.log(`
|
|
12464
|
+
\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
12465
|
+
keep it on npx hatchee install-service (survives closing the terminal / reboot)
|
|
12466
|
+
stop ^C (your coding agents keep running \u2014 you just stop watching)
|
|
12467
|
+
update npx hatchee@latest pair
|
|
12468
|
+
uninstall npx hatchee uninstall-service && npx hatchee uninstall-hooks
|
|
12469
|
+
rm -rf ~/.hatchee (also wipes pairing + identity)
|
|
12470
|
+
help npx hatchee
|
|
12471
|
+
\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500`);
|
|
12472
|
+
}
|
|
12284
12473
|
function safeListen(daemon) {
|
|
12285
12474
|
try {
|
|
12286
12475
|
daemon.listen((err) => {
|
package/package.json
CHANGED