hatchee 0.1.3 → 0.1.5

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.
Files changed (2) hide show
  1. package/dist/cli.mjs +75 -24
  2. package/package.json +1 -1
package/dist/cli.mjs CHANGED
@@ -11942,7 +11942,13 @@ class Daemon {
11942
11942
  });
11943
11943
  const hooks = createServer((req, res) => {
11944
11944
  const url = req.url ?? "";
11945
- if (req.method === "POST" && url === "/pair") {
11945
+ const ctEssence = String(req.headers["content-type"] ?? "").split(";")[0].trim().toLowerCase();
11946
+ const jsonPost = req.method === "POST" && ctEssence === "application/json";
11947
+ if (url === "/pair") {
11948
+ if (!jsonPost) {
11949
+ res.writeHead(415).end("expected application/json");
11950
+ return;
11951
+ }
11946
11952
  try {
11947
11953
  res.writeHead(200, { "content-type": "application/json" });
11948
11954
  res.end(JSON.stringify(self.pairingPayload()));
@@ -11952,7 +11958,7 @@ class Daemon {
11952
11958
  }
11953
11959
  return;
11954
11960
  }
11955
- if (req.method !== "POST" || url !== "/hook") {
11961
+ if (!jsonPost || url !== "/hook") {
11956
11962
  res.writeHead(404).end("not found");
11957
11963
  return;
11958
11964
  }
@@ -12140,7 +12146,12 @@ function installHooks(droverBin) {
12140
12146
  return settingsPath();
12141
12147
  }
12142
12148
  function isOurs(x) {
12143
- return typeof x?.command === "string" && /\bhook'?\s*$/.test(x.command.trimEnd()) && (x.command.includes("hatchee") || x.command.includes("cli.ts") || x.command.includes("cli.mjs") || x.command.includes("drover"));
12149
+ if (typeof x?.command !== "string")
12150
+ return false;
12151
+ const cmd = x.command.trimEnd();
12152
+ if (!/\bhook'?\s*$/.test(cmd))
12153
+ return false;
12154
+ return cmd.includes("hatchee") || cmd.includes("drover") || cmd.includes("daemon/src/cli.ts");
12144
12155
  }
12145
12156
  function uninstallHooks() {
12146
12157
  if (!existsSync2(settingsPath()))
@@ -12158,12 +12169,22 @@ function uninstallHooks() {
12158
12169
  function readStdin() {
12159
12170
  return new Promise((resolve) => {
12160
12171
  let data = "";
12172
+ let done = false;
12173
+ const finish = () => {
12174
+ if (!done) {
12175
+ done = true;
12176
+ resolve(data);
12177
+ }
12178
+ };
12161
12179
  process.stdin.setEncoding("utf8");
12162
12180
  process.stdin.on("data", (c) => {
12163
12181
  data += c;
12164
12182
  });
12165
- process.stdin.on("end", () => resolve(data));
12166
- setTimeout(() => resolve(data), 2000);
12183
+ process.stdin.on("end", finish);
12184
+ setTimeout(() => {
12185
+ if (data === "")
12186
+ finish();
12187
+ }, 2000);
12167
12188
  });
12168
12189
  }
12169
12190
  async function runHook(hookPort) {
@@ -12211,18 +12232,20 @@ var LABEL = "cloud.hatchee.daemon";
12211
12232
  var PLIST = join3(HOME, "Library", "LaunchAgents", `${LABEL}.plist`);
12212
12233
  var UNIT = join3(HOME, ".config", "systemd", "user", "hatchee.service");
12213
12234
  function servicePath(node) {
12214
- return [
12235
+ const system = ["/usr/bin", "/bin", "/usr/sbin", "/sbin"];
12236
+ const userTool = [
12215
12237
  dirname(node),
12216
- join3(HOME, ".bun/bin"),
12217
- join3(HOME, ".npm-global/bin"),
12218
- join3(HOME, ".local/bin"),
12219
12238
  "/opt/homebrew/bin",
12220
12239
  "/usr/local/bin",
12221
- "/usr/bin",
12222
- "/bin",
12223
- "/usr/sbin",
12224
- "/sbin"
12225
- ].join(":");
12240
+ join3(HOME, ".bun/bin"),
12241
+ join3(HOME, ".npm-global/bin"),
12242
+ join3(HOME, ".local/bin")
12243
+ ];
12244
+ const seen = new Set;
12245
+ return [...system, ...userTool].filter((p) => p && !seen.has(p) && seen.add(p)).join(":");
12246
+ }
12247
+ function xmlEsc(s) {
12248
+ return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
12226
12249
  }
12227
12250
  function copyStableBin() {
12228
12251
  mkdirSync3(BIN_DIR, { recursive: true });
@@ -12233,7 +12256,7 @@ function copyStableBin() {
12233
12256
  }
12234
12257
  }
12235
12258
  function plistContent(node, bin) {
12236
- const args = [node, bin, "up"].map((s) => ` <string>${s}</string>`).join(`
12259
+ const args = [node, bin, "up"].map((s) => ` <string>${xmlEsc(s)}</string>`).join(`
12237
12260
  `);
12238
12261
  return `<?xml version="1.0" encoding="UTF-8"?>
12239
12262
  <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
@@ -12247,10 +12270,10 @@ ${args}
12247
12270
  <key>RunAtLoad</key><true/>
12248
12271
  <key>KeepAlive</key><true/>
12249
12272
  <key>ThrottleInterval</key><integer>10</integer>
12250
- <key>StandardOutPath</key><string>${LOG}</string>
12251
- <key>StandardErrorPath</key><string>${LOG}</string>
12273
+ <key>StandardOutPath</key><string>${xmlEsc(LOG)}</string>
12274
+ <key>StandardErrorPath</key><string>${xmlEsc(LOG)}</string>
12252
12275
  <key>EnvironmentVariables</key>
12253
- <dict><key>PATH</key><string>${servicePath(node)}</string></dict>
12276
+ <dict><key>PATH</key><string>${xmlEsc(servicePath(node))}</string></dict>
12254
12277
  </dict>
12255
12278
  </plist>
12256
12279
  `;
@@ -12261,10 +12284,10 @@ Description=Hatchee — watch & approve your coding agents from your phone
12261
12284
  After=network-online.target
12262
12285
 
12263
12286
  [Service]
12264
- ExecStart=${node} ${bin} up
12287
+ ExecStart="${node}" "${bin}" up
12265
12288
  Restart=always
12266
12289
  RestartSec=10
12267
- Environment=PATH=${servicePath(node)}
12290
+ Environment="PATH=${servicePath(node)}"
12268
12291
 
12269
12292
  [Install]
12270
12293
  WantedBy=default.target
@@ -12273,12 +12296,22 @@ WantedBy=default.target
12273
12296
  function run(cmd, args) {
12274
12297
  try {
12275
12298
  execFileSync(cmd, args, { stdio: "pipe" });
12299
+ return true;
12276
12300
  } catch (e) {
12277
12301
  console.log(` · ${cmd} ${args.join(" ")} → ${String(e?.stderr || e?.message || e).trim().slice(0, 200)}`);
12302
+ return false;
12278
12303
  }
12279
12304
  }
12280
12305
  function installService(print = false) {
12281
12306
  const node = process.execPath;
12307
+ const src = process.argv[1] ?? "";
12308
+ if (!print && src && !/\.(mjs|cjs|js)$/.test(src)) {
12309
+ console.log(`
12310
+ ⚠ running from source (${src}); the service needs the built bundle.`);
12311
+ console.log(` use \`npx hatchee install-service\` for a real install.
12312
+ `);
12313
+ return;
12314
+ }
12282
12315
  if (process.platform === "darwin") {
12283
12316
  const content = plistContent(node, STABLE_BIN);
12284
12317
  if (print) {
@@ -12296,8 +12329,15 @@ ${content}
12296
12329
  writeFileSync3(PLIST, content);
12297
12330
  const uid = String(process.getuid?.() ?? "");
12298
12331
  run("launchctl", ["bootout", `gui/${uid}/${LABEL}`]);
12299
- run("launchctl", ["bootstrap", `gui/${uid}`, PLIST]);
12332
+ const ok = run("launchctl", ["bootstrap", `gui/${uid}`, PLIST]);
12300
12333
  run("launchctl", ["enable", `gui/${uid}/${LABEL}`]);
12334
+ if (!ok) {
12335
+ console.log(`
12336
+ ⚠ wrote ${PLIST} but launchctl bootstrap failed (see the message above).`);
12337
+ console.log(` try manually: launchctl bootstrap gui/${uid} ${PLIST}
12338
+ `);
12339
+ return;
12340
+ }
12301
12341
  console.log(`
12302
12342
  ✓ Hatchee will now run in the background (launchd: ${LABEL})`);
12303
12343
  console.log(` logs ${LOG}`);
@@ -12324,8 +12364,15 @@ ${content}
12324
12364
  mkdirSync3(dirname(UNIT), { recursive: true });
12325
12365
  writeFileSync3(UNIT, content);
12326
12366
  run("systemctl", ["--user", "daemon-reload"]);
12327
- run("systemctl", ["--user", "enable", "--now", "hatchee.service"]);
12367
+ const ok = run("systemctl", ["--user", "enable", "--now", "hatchee.service"]);
12328
12368
  run("loginctl", ["enable-linger", process.env.USER ?? ""]);
12369
+ if (!ok) {
12370
+ console.log(`
12371
+ ⚠ wrote ${UNIT} but \`systemctl --user enable --now\` failed (see above).`);
12372
+ console.log(` is the user systemd bus available? try manually: systemctl --user enable --now hatchee.service
12373
+ `);
12374
+ return;
12375
+ }
12329
12376
  console.log(`
12330
12377
  ✓ Hatchee will now run in the background (systemd --user: hatchee.service)`);
12331
12378
  console.log(` logs journalctl --user -u hatchee.service -f`);
@@ -12361,7 +12408,7 @@ function uninstallService() {
12361
12408
  }
12362
12409
 
12363
12410
  // src/cli.ts
12364
- var VERSION2 = "0.1.3";
12411
+ var VERSION2 = "0.1.5";
12365
12412
  var cmd = process.argv[2] ?? "help";
12366
12413
  switch (cmd) {
12367
12414
  case "up": {
@@ -12473,7 +12520,11 @@ function banner() {
12473
12520
  }
12474
12521
  async function remotePair(hookPort) {
12475
12522
  try {
12476
- const r = await fetch(`http://127.0.0.1:${hookPort}/pair`, { method: "POST", signal: AbortSignal.timeout(2500) });
12523
+ const r = await fetch(`http://127.0.0.1:${hookPort}/pair`, {
12524
+ method: "POST",
12525
+ headers: { "content-type": "application/json" },
12526
+ signal: AbortSignal.timeout(2500)
12527
+ });
12477
12528
  if (!r.ok)
12478
12529
  return null;
12479
12530
  const info = await r.json();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hatchee",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "description": "Your coding agents, alive on your iPhone lock screen — Hatchee daemon. Approve Claude Code / Codex from your phone.",
5
5
  "type": "module",
6
6
  "bin": { "hatchee": "dist/cli.mjs" },