@misaka-net/misakanet-setup 0.7.0 → 0.8.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.
@@ -20,10 +20,11 @@
20
20
  * Manual test:
21
21
  * echo '{"session_id":"demo"}' | node checkpoint_reminder.mjs prompt
22
22
  */
23
- import { writeFileSync, mkdirSync, renameSync, readFileSync } from 'node:fs';
23
+ import { writeFileSync, mkdirSync, renameSync, readFileSync, realpathSync } from 'node:fs';
24
24
  import { createHash } from 'node:crypto';
25
25
  import { homedir } from 'node:os';
26
26
  import { join, dirname } from 'node:path';
27
+ import { fileURLToPath } from 'node:url';
27
28
 
28
29
  // NOTE: JavaScript does not concatenate adjacent string literals the way Python does.
29
30
  // Writing these as three consecutive strings silently keeps only the first one, so the
@@ -418,14 +419,49 @@ async function failureMode(payload) {
418
419
  process.stdout.write(`${lines.join('\n')}\n`);
419
420
  }
420
421
 
421
- const mode = process.argv[2] || 'prompt';
422
- try {
423
- const payload = parsePayload(await readStdin());
424
- if (!payload) debug('no usable payload - nothing to do');
425
- else if (mode === 'prompt') promptMode(payload);
426
- else if (mode === 'failure') await failureMode(payload);
427
- else debug(`unknown mode ${mode}`);
428
- } catch (err) {
429
- debug(`hook error: ${err}`); // never break the user's session
422
+ /**
423
+ * Is this file the process's entry point?
424
+ *
425
+ * `import.meta.url` is the module's *resolved* URL: Node's ESM loader resolves symlinks, so a
426
+ * script started through a symlink gets the real path there while `process.argv[1]` still holds
427
+ * the symlinked one. Comparing the two strings directly then never matches, and the hook does
428
+ * nothing at all — silently, with exit 0, which is the shape of a tool that looks installed and
429
+ * is not.
430
+ *
431
+ * macOS is where that bites: `/var` is a symlink to `/private/var`, every temporary directory
432
+ * lives under it, and the packaged e2e installs the hook into such a directory. On 2026-10-26
433
+ * all three macOS legs went red with `exit=0 stdout=""` — the hook ran, and did nothing.
434
+ *
435
+ * So compare the real paths. The direct comparison is kept for a loader started with
436
+ * `--preserve-symlinks`, where the symlink survives in `import.meta.url` as well.
437
+ */
438
+ function isEntryPoint() {
439
+ const invoked = process.argv[1];
440
+ if (!invoked) return false;
441
+ const here = fileURLToPath(import.meta.url);
442
+ if (here === invoked) return true;
443
+ try {
444
+ return realpathSync(invoked) === realpathSync(here);
445
+ } catch {
446
+ return false;
447
+ }
430
448
  }
431
- process.exit(0);
449
+
450
+ // Run only when this file is the entry point (`node checkpoint_reminder.mjs …`). Importing it must
451
+ // not read stdin or exit the process: the inner `search()` is the gate that redacts the query on
452
+ // the way out, and #3719 wants a test that can drive it with *raw* text and see the payload.
453
+ if (isEntryPoint()) {
454
+ const mode = process.argv[2] || 'prompt';
455
+ try {
456
+ const payload = parsePayload(await readStdin());
457
+ if (!payload) debug('no usable payload - nothing to do');
458
+ else if (mode === 'prompt') promptMode(payload);
459
+ else if (mode === 'failure') await failureMode(payload);
460
+ else debug(`unknown mode ${mode}`);
461
+ } catch (err) {
462
+ debug(`hook error: ${err}`); // never break the user's session
463
+ }
464
+ process.exit(0);
465
+ }
466
+
467
+ export { search };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@misaka-net/misakanet-setup",
3
- "version": "0.7.0",
3
+ "version": "0.8.0",
4
4
  "description": "MisakaNet failure lessons for your agents: writes an MCP endpoint into each agent config, rules blocks where the client takes one, and a Claude Code hook. At most one anonymous request leaves this machine at install; then only the agent's calls — unless MISAKANET_HOOK_FETCH=1 (off by default) the hook sends the failed call's error text or command.",
5
5
  "bin": {
6
6
  "misakanet-setup": "bin/misakanet-setup.mjs"