hatchee 0.1.3 → 0.1.4

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 +72 -21
  2. package/package.json +1 -1
package/dist/cli.mjs CHANGED
@@ -11942,7 +11942,12 @@ 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 jsonPost = req.method === "POST" && String(req.headers["content-type"] ?? "").includes("application/json");
11946
+ if (url === "/pair") {
11947
+ if (!jsonPost) {
11948
+ res.writeHead(415).end("expected application/json");
11949
+ return;
11950
+ }
11946
11951
  try {
11947
11952
  res.writeHead(200, { "content-type": "application/json" });
11948
11953
  res.end(JSON.stringify(self.pairingPayload()));
@@ -11952,7 +11957,7 @@ class Daemon {
11952
11957
  }
11953
11958
  return;
11954
11959
  }
11955
- if (req.method !== "POST" || url !== "/hook") {
11960
+ if (!jsonPost || url !== "/hook") {
11956
11961
  res.writeHead(404).end("not found");
11957
11962
  return;
11958
11963
  }
@@ -12140,7 +12145,12 @@ function installHooks(droverBin) {
12140
12145
  return settingsPath();
12141
12146
  }
12142
12147
  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"));
12148
+ if (typeof x?.command !== "string")
12149
+ return false;
12150
+ const cmd = x.command.trimEnd();
12151
+ if (!/\bhook'?\s*$/.test(cmd))
12152
+ return false;
12153
+ return cmd.includes("hatchee") || cmd.includes("drover") || cmd.includes("daemon/src/cli.ts");
12144
12154
  }
12145
12155
  function uninstallHooks() {
12146
12156
  if (!existsSync2(settingsPath()))
@@ -12158,12 +12168,22 @@ function uninstallHooks() {
12158
12168
  function readStdin() {
12159
12169
  return new Promise((resolve) => {
12160
12170
  let data = "";
12171
+ let done = false;
12172
+ const finish = () => {
12173
+ if (!done) {
12174
+ done = true;
12175
+ resolve(data);
12176
+ }
12177
+ };
12161
12178
  process.stdin.setEncoding("utf8");
12162
12179
  process.stdin.on("data", (c) => {
12163
12180
  data += c;
12164
12181
  });
12165
- process.stdin.on("end", () => resolve(data));
12166
- setTimeout(() => resolve(data), 2000);
12182
+ process.stdin.on("end", finish);
12183
+ setTimeout(() => {
12184
+ if (data === "")
12185
+ finish();
12186
+ }, 2000);
12167
12187
  });
12168
12188
  }
12169
12189
  async function runHook(hookPort) {
@@ -12213,17 +12233,20 @@ var UNIT = join3(HOME, ".config", "systemd", "user", "hatchee.service");
12213
12233
  function servicePath(node) {
12214
12234
  return [
12215
12235
  dirname(node),
12216
- join3(HOME, ".bun/bin"),
12217
- join3(HOME, ".npm-global/bin"),
12218
- join3(HOME, ".local/bin"),
12219
- "/opt/homebrew/bin",
12220
- "/usr/local/bin",
12221
12236
  "/usr/bin",
12222
12237
  "/bin",
12223
12238
  "/usr/sbin",
12224
- "/sbin"
12239
+ "/sbin",
12240
+ "/opt/homebrew/bin",
12241
+ "/usr/local/bin",
12242
+ join3(HOME, ".bun/bin"),
12243
+ join3(HOME, ".npm-global/bin"),
12244
+ join3(HOME, ".local/bin")
12225
12245
  ].join(":");
12226
12246
  }
12247
+ function xmlEsc(s) {
12248
+ return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
12249
+ }
12227
12250
  function copyStableBin() {
12228
12251
  mkdirSync3(BIN_DIR, { recursive: true });
12229
12252
  const src = process.argv[1];
@@ -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.4";
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.4",
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" },