@nocville/npc 0.0.0-stage → 0.2.1

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 (213) hide show
  1. package/README.md +268 -2
  2. package/dist/cjs/cli.js +584 -0
  3. package/dist/cjs/core/api-key.js +69 -0
  4. package/dist/cjs/core/base-npc.js +160 -0
  5. package/dist/cjs/core/config.js +108 -0
  6. package/dist/cjs/core/event-npc.js +14 -0
  7. package/dist/cjs/core/logger.js +39 -0
  8. package/dist/cjs/core/polling-npc.js +65 -0
  9. package/dist/cjs/core/screen.js +28 -0
  10. package/dist/cjs/core/session-filter.js +150 -0
  11. package/dist/cjs/core/text.js +15 -0
  12. package/dist/cjs/core/thresholds.js +48 -0
  13. package/dist/cjs/core/types.js +10 -0
  14. package/dist/cjs/core/user-config.js +92 -0
  15. package/dist/cjs/doctor.js +130 -0
  16. package/dist/cjs/index.js +95 -0
  17. package/dist/cjs/npcs/agent-common/activity.js +291 -0
  18. package/dist/cjs/npcs/agent-common/git-stats.js +71 -0
  19. package/dist/cjs/npcs/agent-common/hook-runner.js +252 -0
  20. package/dist/cjs/npcs/agent-common/hook-settings.js +279 -0
  21. package/dist/cjs/npcs/agent-common/liveness.js +112 -0
  22. package/dist/cjs/npcs/agent-common/lock.js +99 -0
  23. package/dist/cjs/npcs/agent-common/runtime.js +154 -0
  24. package/dist/cjs/npcs/agent-common/session.js +412 -0
  25. package/dist/cjs/npcs/agent-common/shim.js +96 -0
  26. package/dist/cjs/npcs/agent-common/watchdog.js +149 -0
  27. package/dist/cjs/npcs/claude-code/defaults.js +20 -0
  28. package/dist/cjs/npcs/claude-code/hook-handler.js +139 -0
  29. package/dist/cjs/npcs/claude-code/index.js +104 -0
  30. package/dist/cjs/npcs/claude-code/setup.js +133 -0
  31. package/dist/cjs/npcs/claude-code/state-machine.js +130 -0
  32. package/dist/cjs/npcs/codex/defaults.js +32 -0
  33. package/dist/cjs/npcs/codex/hook-handler.js +116 -0
  34. package/dist/cjs/npcs/codex/index.js +92 -0
  35. package/dist/cjs/npcs/codex/rollout.js +246 -0
  36. package/dist/cjs/npcs/codex/setup.js +104 -0
  37. package/dist/cjs/npcs/codex/watcher.js +361 -0
  38. package/dist/cjs/npcs/cursor/defaults.js +15 -0
  39. package/dist/cjs/npcs/cursor/hook-handler.js +65 -0
  40. package/dist/cjs/npcs/cursor/index.js +72 -0
  41. package/dist/cjs/npcs/cursor/normalize.js +54 -0
  42. package/dist/cjs/npcs/cursor/setup.js +98 -0
  43. package/dist/cjs/npcs/gemini/defaults.js +15 -0
  44. package/dist/cjs/npcs/gemini/hook-handler.js +93 -0
  45. package/dist/cjs/npcs/gemini/index.js +69 -0
  46. package/dist/cjs/npcs/gemini/setup.js +98 -0
  47. package/dist/cjs/npcs/github-watcher/defaults.js +11 -0
  48. package/dist/cjs/npcs/github-watcher/format.js +21 -0
  49. package/dist/cjs/npcs/github-watcher/github-api.js +126 -0
  50. package/dist/cjs/npcs/github-watcher/index.js +148 -0
  51. package/dist/cjs/npcs/internet-connectivity/checks.js +92 -0
  52. package/dist/cjs/npcs/internet-connectivity/defaults.js +22 -0
  53. package/dist/cjs/npcs/internet-connectivity/index.js +251 -0
  54. package/dist/cjs/npcs/log-watcher/buffer.js +40 -0
  55. package/dist/cjs/npcs/log-watcher/defaults.js +12 -0
  56. package/dist/cjs/npcs/log-watcher/index.js +161 -0
  57. package/dist/cjs/npcs/log-watcher/tail.js +81 -0
  58. package/dist/cjs/npcs/process-watcher/defaults.js +9 -0
  59. package/dist/cjs/npcs/process-watcher/index.js +140 -0
  60. package/dist/cjs/npcs/process-watcher/process-scanner.js +114 -0
  61. package/dist/cjs/npcs/receptionist/defaults.js +9 -0
  62. package/dist/cjs/npcs/receptionist/index.js +120 -0
  63. package/dist/cjs/npcs/receptionist/weather.js +129 -0
  64. package/dist/cjs/npcs/system-monitor/collectors.js +68 -0
  65. package/dist/cjs/npcs/system-monitor/defaults.js +13 -0
  66. package/dist/cjs/npcs/system-monitor/index.js +105 -0
  67. package/dist/cjs/npcs/web-page-watcher/basic-mode.js +58 -0
  68. package/dist/cjs/npcs/web-page-watcher/capture.js +80 -0
  69. package/dist/cjs/npcs/web-page-watcher/defaults.js +31 -0
  70. package/dist/cjs/npcs/web-page-watcher/index.js +234 -0
  71. package/dist/cjs/npcs/web-page-watcher/pixelate.js +181 -0
  72. package/dist/cjs/package.json +1 -0
  73. package/dist/cli.d.ts +3 -0
  74. package/dist/cli.js +549 -0
  75. package/dist/core/api-key.d.ts +34 -0
  76. package/dist/core/api-key.js +64 -0
  77. package/dist/core/base-npc.d.ts +41 -0
  78. package/dist/core/base-npc.js +156 -0
  79. package/dist/core/config.d.ts +22 -0
  80. package/dist/core/config.js +101 -0
  81. package/dist/core/event-npc.d.ts +8 -0
  82. package/dist/core/event-npc.js +10 -0
  83. package/dist/core/logger.d.ts +11 -0
  84. package/dist/core/logger.js +35 -0
  85. package/dist/core/polling-npc.d.ts +18 -0
  86. package/dist/core/polling-npc.js +61 -0
  87. package/dist/core/screen.d.ts +11 -0
  88. package/dist/core/screen.js +24 -0
  89. package/dist/core/session-filter.d.ts +59 -0
  90. package/dist/core/session-filter.js +143 -0
  91. package/dist/core/text.d.ts +6 -0
  92. package/dist/core/text.js +12 -0
  93. package/dist/core/thresholds.d.ts +10 -0
  94. package/dist/core/thresholds.js +44 -0
  95. package/dist/core/types.d.ts +60 -0
  96. package/dist/core/types.js +7 -0
  97. package/dist/core/user-config.d.ts +43 -0
  98. package/dist/core/user-config.js +85 -0
  99. package/dist/doctor.d.ts +8 -0
  100. package/dist/doctor.js +126 -0
  101. package/dist/index.d.ts +41 -0
  102. package/dist/index.js +26 -0
  103. package/dist/npcs/agent-common/activity.d.ts +42 -0
  104. package/dist/npcs/agent-common/activity.js +280 -0
  105. package/dist/npcs/agent-common/git-stats.d.ts +11 -0
  106. package/dist/npcs/agent-common/git-stats.js +67 -0
  107. package/dist/npcs/agent-common/hook-runner.d.ts +62 -0
  108. package/dist/npcs/agent-common/hook-runner.js +245 -0
  109. package/dist/npcs/agent-common/hook-settings.d.ts +75 -0
  110. package/dist/npcs/agent-common/hook-settings.js +265 -0
  111. package/dist/npcs/agent-common/liveness.d.ts +12 -0
  112. package/dist/npcs/agent-common/liveness.js +105 -0
  113. package/dist/npcs/agent-common/lock.d.ts +6 -0
  114. package/dist/npcs/agent-common/lock.js +95 -0
  115. package/dist/npcs/agent-common/runtime.d.ts +16 -0
  116. package/dist/npcs/agent-common/runtime.js +149 -0
  117. package/dist/npcs/agent-common/session.d.ts +192 -0
  118. package/dist/npcs/agent-common/session.js +390 -0
  119. package/dist/npcs/agent-common/shim.d.ts +4 -0
  120. package/dist/npcs/agent-common/shim.js +90 -0
  121. package/dist/npcs/agent-common/watchdog.d.ts +16 -0
  122. package/dist/npcs/agent-common/watchdog.js +144 -0
  123. package/dist/npcs/claude-code/defaults.d.ts +15 -0
  124. package/dist/npcs/claude-code/defaults.js +17 -0
  125. package/dist/npcs/claude-code/hook-handler.d.ts +40 -0
  126. package/dist/npcs/claude-code/hook-handler.js +131 -0
  127. package/dist/npcs/claude-code/index.d.ts +36 -0
  128. package/dist/npcs/claude-code/index.js +92 -0
  129. package/dist/npcs/claude-code/setup.d.ts +58 -0
  130. package/dist/npcs/claude-code/setup.js +121 -0
  131. package/dist/npcs/claude-code/state-machine.d.ts +28 -0
  132. package/dist/npcs/claude-code/state-machine.js +125 -0
  133. package/dist/npcs/codex/defaults.d.ts +24 -0
  134. package/dist/npcs/codex/defaults.js +28 -0
  135. package/dist/npcs/codex/hook-handler.d.ts +24 -0
  136. package/dist/npcs/codex/hook-handler.js +110 -0
  137. package/dist/npcs/codex/index.d.ts +21 -0
  138. package/dist/npcs/codex/index.js +76 -0
  139. package/dist/npcs/codex/rollout.d.ts +39 -0
  140. package/dist/npcs/codex/rollout.js +242 -0
  141. package/dist/npcs/codex/setup.d.ts +37 -0
  142. package/dist/npcs/codex/setup.js +96 -0
  143. package/dist/npcs/codex/watcher.d.ts +65 -0
  144. package/dist/npcs/codex/watcher.js +356 -0
  145. package/dist/npcs/cursor/defaults.d.ts +9 -0
  146. package/dist/npcs/cursor/defaults.js +11 -0
  147. package/dist/npcs/cursor/hook-handler.d.ts +8 -0
  148. package/dist/npcs/cursor/hook-handler.js +60 -0
  149. package/dist/npcs/cursor/index.d.ts +18 -0
  150. package/dist/npcs/cursor/index.js +57 -0
  151. package/dist/npcs/cursor/normalize.d.ts +6 -0
  152. package/dist/npcs/cursor/normalize.js +49 -0
  153. package/dist/npcs/cursor/setup.d.ts +29 -0
  154. package/dist/npcs/cursor/setup.js +90 -0
  155. package/dist/npcs/gemini/defaults.d.ts +9 -0
  156. package/dist/npcs/gemini/defaults.js +11 -0
  157. package/dist/npcs/gemini/hook-handler.d.ts +10 -0
  158. package/dist/npcs/gemini/hook-handler.js +87 -0
  159. package/dist/npcs/gemini/index.d.ts +17 -0
  160. package/dist/npcs/gemini/index.js +56 -0
  161. package/dist/npcs/gemini/setup.d.ts +29 -0
  162. package/dist/npcs/gemini/setup.js +90 -0
  163. package/dist/npcs/github-watcher/defaults.d.ts +8 -0
  164. package/dist/npcs/github-watcher/defaults.js +8 -0
  165. package/dist/npcs/github-watcher/format.d.ts +3 -0
  166. package/dist/npcs/github-watcher/format.js +18 -0
  167. package/dist/npcs/github-watcher/github-api.d.ts +55 -0
  168. package/dist/npcs/github-watcher/github-api.js +120 -0
  169. package/dist/npcs/github-watcher/index.d.ts +35 -0
  170. package/dist/npcs/github-watcher/index.js +144 -0
  171. package/dist/npcs/internet-connectivity/checks.d.ts +24 -0
  172. package/dist/npcs/internet-connectivity/checks.js +84 -0
  173. package/dist/npcs/internet-connectivity/defaults.d.ts +20 -0
  174. package/dist/npcs/internet-connectivity/defaults.js +19 -0
  175. package/dist/npcs/internet-connectivity/index.d.ts +55 -0
  176. package/dist/npcs/internet-connectivity/index.js +246 -0
  177. package/dist/npcs/log-watcher/buffer.d.ts +18 -0
  178. package/dist/npcs/log-watcher/buffer.js +34 -0
  179. package/dist/npcs/log-watcher/defaults.d.ts +9 -0
  180. package/dist/npcs/log-watcher/defaults.js +9 -0
  181. package/dist/npcs/log-watcher/index.d.ts +45 -0
  182. package/dist/npcs/log-watcher/index.js +157 -0
  183. package/dist/npcs/log-watcher/tail.d.ts +9 -0
  184. package/dist/npcs/log-watcher/tail.js +78 -0
  185. package/dist/npcs/process-watcher/defaults.d.ts +6 -0
  186. package/dist/npcs/process-watcher/defaults.js +6 -0
  187. package/dist/npcs/process-watcher/index.d.ts +26 -0
  188. package/dist/npcs/process-watcher/index.js +136 -0
  189. package/dist/npcs/process-watcher/process-scanner.d.ts +19 -0
  190. package/dist/npcs/process-watcher/process-scanner.js +110 -0
  191. package/dist/npcs/receptionist/defaults.d.ts +6 -0
  192. package/dist/npcs/receptionist/defaults.js +6 -0
  193. package/dist/npcs/receptionist/index.d.ts +29 -0
  194. package/dist/npcs/receptionist/index.js +116 -0
  195. package/dist/npcs/receptionist/weather.d.ts +37 -0
  196. package/dist/npcs/receptionist/weather.js +121 -0
  197. package/dist/npcs/system-monitor/collectors.d.ts +45 -0
  198. package/dist/npcs/system-monitor/collectors.js +60 -0
  199. package/dist/npcs/system-monitor/defaults.d.ts +7 -0
  200. package/dist/npcs/system-monitor/defaults.js +10 -0
  201. package/dist/npcs/system-monitor/index.d.ts +27 -0
  202. package/dist/npcs/system-monitor/index.js +101 -0
  203. package/dist/npcs/web-page-watcher/basic-mode.d.ts +16 -0
  204. package/dist/npcs/web-page-watcher/basic-mode.js +52 -0
  205. package/dist/npcs/web-page-watcher/capture.d.ts +49 -0
  206. package/dist/npcs/web-page-watcher/capture.js +75 -0
  207. package/dist/npcs/web-page-watcher/defaults.d.ts +19 -0
  208. package/dist/npcs/web-page-watcher/defaults.js +27 -0
  209. package/dist/npcs/web-page-watcher/index.d.ts +42 -0
  210. package/dist/npcs/web-page-watcher/index.js +230 -0
  211. package/dist/npcs/web-page-watcher/pixelate.d.ts +36 -0
  212. package/dist/npcs/web-page-watcher/pixelate.js +172 -0
  213. package/package.json +73 -3
@@ -0,0 +1,112 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isAgentAlive = isAgentAlive;
4
+ exports.claudePidFromStore = claudePidFromStore;
5
+ exports.ancestorPid = ancestorPid;
6
+ exports.resolveAgentPid = resolveAgentPid;
7
+ exports.reserveAgentPidAttempt = reserveAgentPidAttempt;
8
+ const node_child_process_1 = require("node:child_process");
9
+ const node_fs_1 = require("node:fs");
10
+ const node_os_1 = require("node:os");
11
+ const node_path_1 = require("node:path");
12
+ function startTime(pid, timeout = 1000) {
13
+ if (process.platform === 'win32')
14
+ return undefined;
15
+ const out = (0, node_child_process_1.spawnSync)('ps', ['-o', 'lstart=', '-p', String(pid)], {
16
+ encoding: 'utf8',
17
+ timeout,
18
+ });
19
+ const value = Date.parse(out.stdout?.trim() ?? '');
20
+ return Number.isFinite(value) ? value : undefined;
21
+ }
22
+ function isAgentAlive(proc) {
23
+ if (!Number.isSafeInteger(proc.pid) || proc.pid <= 0)
24
+ return false;
25
+ try {
26
+ process.kill(proc.pid, 0);
27
+ }
28
+ catch (error) {
29
+ return error.code === 'EPERM';
30
+ }
31
+ if (!proc.startedAt || process.platform === 'win32')
32
+ return true;
33
+ const actual = startTime(proc.pid);
34
+ return actual === undefined || Math.abs(actual - proc.startedAt) < 120_000;
35
+ }
36
+ function claudePidFromStore(sessionId, dir = (0, node_path_1.join)((0, node_os_1.homedir)(), '.claude', 'sessions')) {
37
+ try {
38
+ for (const file of (0, node_fs_1.readdirSync)(dir)) {
39
+ if (!/^\d+\.json$/.test(file))
40
+ continue;
41
+ try {
42
+ const data = JSON.parse((0, node_fs_1.readFileSync)((0, node_path_1.join)(dir, file), 'utf8'));
43
+ if (data.sessionId !== sessionId || !Number.isSafeInteger(data.pid) || data.pid <= 0)
44
+ continue;
45
+ const proc = {
46
+ pid: data.pid,
47
+ startedAt: typeof data.startedAt === 'number' ? data.startedAt : undefined,
48
+ };
49
+ if (isAgentAlive(proc))
50
+ return proc;
51
+ }
52
+ catch {
53
+ /* incomplete or old sidecar */
54
+ }
55
+ }
56
+ }
57
+ catch {
58
+ /* no session store */
59
+ }
60
+ return null;
61
+ }
62
+ function ancestorPid(names, start = process.ppid, nodeCommandPattern) {
63
+ if (process.platform === 'win32')
64
+ return null;
65
+ const deadline = Date.now() + 750;
66
+ let pid = start;
67
+ for (let hop = 0; hop < 5 && pid > 1 && Date.now() < deadline; hop++) {
68
+ const out = (0, node_child_process_1.spawnSync)('ps', ['-o', 'ppid=,comm=', '-p', String(pid)], {
69
+ encoding: 'utf8',
70
+ timeout: Math.max(1, deadline - Date.now()),
71
+ });
72
+ const match = out.stdout?.trim().match(/^(\d+)\s+(.+)$/);
73
+ if (!match)
74
+ return null;
75
+ const command = (0, node_path_1.basename)(match[2]);
76
+ if (names.includes(command)) {
77
+ if (command !== 'node')
78
+ return { pid, startedAt: startTime(pid, Math.max(1, deadline - Date.now())) };
79
+ if (nodeCommandPattern) {
80
+ const argv = (0, node_child_process_1.spawnSync)('ps', ['-o', 'command=', '-p', String(pid)], {
81
+ encoding: 'utf8',
82
+ timeout: Math.max(1, deadline - Date.now()),
83
+ }).stdout?.trim();
84
+ if (argv && nodeCommandPattern.test(argv))
85
+ return { pid, startedAt: startTime(pid, Math.max(1, deadline - Date.now())) };
86
+ }
87
+ }
88
+ const parent = Number(match[1]);
89
+ if (parent === pid)
90
+ return null;
91
+ pid = parent;
92
+ }
93
+ return null;
94
+ }
95
+ function resolveAgentPid(adapter, sessionId) {
96
+ if (adapter.processNames?.length === 0)
97
+ return null;
98
+ return ((adapter.family === 'claude' ? claudePidFromStore(sessionId) : null) ??
99
+ ancestorPid(adapter.processNames ?? [adapter.command], process.ppid, adapter.nodeCommandPattern));
100
+ }
101
+ /** Reserve under the state lock so simultaneous hooks cannot duplicate PID probes. */
102
+ function reserveAgentPidAttempt(state, now) {
103
+ if (state.agentPid ||
104
+ state.offline ||
105
+ (state.agentPidResolveAttempts ?? 0) >= 5 ||
106
+ (state.agentPidLastResolveAt !== undefined && now - state.agentPidLastResolveAt < 60_000))
107
+ return false;
108
+ state.agentPidResolveAttempts = (state.agentPidResolveAttempts ?? 0) + 1;
109
+ state.agentPidLastResolveAt = now;
110
+ return true;
111
+ }
112
+ //# sourceMappingURL=liveness.js.map
@@ -0,0 +1,99 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.LOCK_HARD_STALE_MS = exports.LOCK_WAIT_MS = exports.LOCK_STALE_MS = void 0;
4
+ exports.withStateLock = withStateLock;
5
+ const node_crypto_1 = require("node:crypto");
6
+ const node_fs_1 = require("node:fs");
7
+ const node_path_1 = require("node:path");
8
+ exports.LOCK_STALE_MS = 5000;
9
+ exports.LOCK_WAIT_MS = 2000;
10
+ exports.LOCK_HARD_STALE_MS = 30_000;
11
+ function alive(pid) {
12
+ if (!Number.isSafeInteger(pid) || pid <= 0)
13
+ return false;
14
+ try {
15
+ process.kill(pid, 0);
16
+ return true;
17
+ }
18
+ catch (error) {
19
+ return error.code === 'EPERM';
20
+ }
21
+ }
22
+ /** Exclusive cross-process lock. A timeout skips the hook; it never permits an unlocked write. */
23
+ async function withStateLock(path, fn, waitMs = exports.LOCK_WAIT_MS) {
24
+ (0, node_fs_1.mkdirSync)((0, node_path_1.dirname)(path), { recursive: true, mode: 0o700 });
25
+ const lock = `${path}.lock`;
26
+ const token = `${process.pid}:${(0, node_crypto_1.randomUUID)()}`;
27
+ const deadline = Date.now() + waitMs;
28
+ for (;;) {
29
+ try {
30
+ const fd = (0, node_fs_1.openSync)(lock, 'wx', 0o600);
31
+ try {
32
+ (0, node_fs_1.writeSync)(fd, token);
33
+ }
34
+ finally {
35
+ (0, node_fs_1.closeSync)(fd);
36
+ }
37
+ break;
38
+ }
39
+ catch (error) {
40
+ if (error.code !== 'EEXIST')
41
+ throw error;
42
+ // Serialize stale reclamation so two waiters cannot unlink each other's new lock.
43
+ let reaper;
44
+ try {
45
+ const reapPath = `${lock}.reap`;
46
+ try {
47
+ const oldToken = (0, node_fs_1.readFileSync)(reapPath, 'utf8');
48
+ if (Date.now() - (0, node_fs_1.statSync)(reapPath).mtimeMs > exports.LOCK_STALE_MS &&
49
+ (0, node_fs_1.readFileSync)(reapPath, 'utf8') === oldToken)
50
+ (0, node_fs_1.unlinkSync)(reapPath);
51
+ }
52
+ catch {
53
+ /* no stale reaper */
54
+ }
55
+ reaper = (0, node_fs_1.openSync)(reapPath, 'wx', 0o600);
56
+ (0, node_fs_1.writeSync)(reaper, token);
57
+ const owner = (0, node_fs_1.readFileSync)(lock, 'utf8');
58
+ const age = Date.now() - (0, node_fs_1.statSync)(lock).mtimeMs;
59
+ // PID reuse cannot block forever. State mutations are synchronous; the separate
60
+ // delivery lock bounds its HTTP request well below this hard ceiling.
61
+ if (age > exports.LOCK_HARD_STALE_MS ||
62
+ (age > exports.LOCK_STALE_MS && !alive(Number(owner.split(':')[0]))))
63
+ (0, node_fs_1.unlinkSync)(lock);
64
+ }
65
+ catch {
66
+ /* owner released, or another waiter is reclaiming it */
67
+ }
68
+ finally {
69
+ if (reaper !== undefined) {
70
+ (0, node_fs_1.closeSync)(reaper);
71
+ try {
72
+ if ((0, node_fs_1.readFileSync)(`${lock}.reap`, 'utf8') === token)
73
+ (0, node_fs_1.unlinkSync)(`${lock}.reap`);
74
+ }
75
+ catch {
76
+ /* stale reaper already removed */
77
+ }
78
+ }
79
+ }
80
+ if (Date.now() >= deadline)
81
+ throw new Error('Session state lock timed out');
82
+ await new Promise((resolve) => setTimeout(resolve, 10 + Math.random() * 15));
83
+ }
84
+ }
85
+ try {
86
+ return await fn();
87
+ }
88
+ finally {
89
+ // Never unlink a replacement lock owned by another writer.
90
+ try {
91
+ if ((0, node_fs_1.readFileSync)(lock, 'utf8') === token)
92
+ (0, node_fs_1.unlinkSync)(lock);
93
+ }
94
+ catch {
95
+ /* already gone */
96
+ }
97
+ }
98
+ }
99
+ //# sourceMappingURL=lock.js.map
@@ -0,0 +1,154 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isEphemeralCliPath = isEphemeralCliPath;
4
+ exports.ensureStableCliPath = ensureStableCliPath;
5
+ exports.pruneOldRuntimes = pruneOldRuntimes;
6
+ const node_crypto_1 = require("node:crypto");
7
+ const node_fs_1 = require("node:fs");
8
+ const node_module_1 = require("node:module");
9
+ const node_path_1 = require("node:path");
10
+ const user_config_js_1 = require("../../core/user-config.js");
11
+ /**
12
+ * Hooks must point at a cli.js that will still exist tomorrow. When setup runs
13
+ * from an ephemeral location — `npx @nocville/npc …` executes out of the npm
14
+ * cache (`~/.npm/_npx/<hash>/…`), which npm may prune at any time — we copy
15
+ * the package (dist + its `@nocville/node` dependency) into
16
+ * `~/.config/nocville/runtime/<version>/` and point hooks there instead.
17
+ */
18
+ /** True when the CLI is running from npm's npx cache (or another temp dir). */
19
+ function isEphemeralCliPath(cliPath) {
20
+ const p = cliPath.split(node_path_1.sep).join('/');
21
+ return p.includes('/_npx/') || p.includes('/.pnpm-store/') || p.includes('/dlx-');
22
+ }
23
+ function findPackageRoot(start, name) {
24
+ let dir = start;
25
+ for (let i = 0; i < 8; i++) {
26
+ const pkg = (0, node_path_1.join)(dir, 'package.json');
27
+ if ((0, node_fs_1.existsSync)(pkg)) {
28
+ try {
29
+ const parsed = JSON.parse((0, node_fs_1.readFileSync)(pkg, 'utf-8'));
30
+ if (parsed.name === name)
31
+ return dir;
32
+ }
33
+ catch {
34
+ // keep walking
35
+ }
36
+ }
37
+ const parent = (0, node_path_1.dirname)(dir);
38
+ if (parent === dir)
39
+ break;
40
+ dir = parent;
41
+ }
42
+ return null;
43
+ }
44
+ function readVersion(root) {
45
+ try {
46
+ const parsed = JSON.parse((0, node_fs_1.readFileSync)((0, node_path_1.join)(root, 'package.json'), 'utf-8'));
47
+ return parsed.version ?? '0.0.0';
48
+ }
49
+ catch {
50
+ return '0.0.0';
51
+ }
52
+ }
53
+ /**
54
+ * Always copy a valid installed runtime, including global installs removed by nvm uninstall.
55
+ */
56
+ function ensureStableCliPath(cliPath, _force = true) {
57
+ const pkgRoot = findPackageRoot((0, node_path_1.dirname)(cliPath), '@nocville/npc');
58
+ if (!pkgRoot)
59
+ return cliPath;
60
+ let sdkRoot = null;
61
+ try {
62
+ const req = (0, node_module_1.createRequire)(cliPath);
63
+ sdkRoot = findPackageRoot((0, node_path_1.dirname)(req.resolve('@nocville/node')), '@nocville/node');
64
+ }
65
+ catch {
66
+ sdkRoot = null;
67
+ }
68
+ if (!sdkRoot)
69
+ return cliPath;
70
+ const req = (0, node_module_1.createRequire)(cliPath);
71
+ const pngRoot = findPackageRoot((0, node_path_1.dirname)(req.resolve('pngjs')), 'pngjs');
72
+ if (!pngRoot)
73
+ throw new Error('Cannot install hook runtime: pngjs is missing');
74
+ const version = readVersion(pkgRoot);
75
+ const hash = (0, node_crypto_1.createHash)('sha256');
76
+ const hashTree = (root) => {
77
+ for (const entry of (0, node_fs_1.readdirSync)(root, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
78
+ hash.update(entry.name);
79
+ const path = (0, node_path_1.join)(root, entry.name);
80
+ if (entry.isDirectory())
81
+ hashTree(path);
82
+ else if (entry.isFile())
83
+ hash.update((0, node_fs_1.readFileSync)(path));
84
+ }
85
+ };
86
+ for (const root of [pkgRoot, sdkRoot]) {
87
+ hash.update((0, node_fs_1.readFileSync)((0, node_path_1.join)(root, 'package.json')));
88
+ hashTree((0, node_path_1.join)(root, 'dist'));
89
+ }
90
+ hash.update((0, node_fs_1.readFileSync)((0, node_path_1.join)(pngRoot, 'package.json')));
91
+ hashTree((0, node_path_1.join)(pngRoot, 'lib'));
92
+ // Immutable snapshots also support multiple development builds of one version.
93
+ // Never remove files a running watchdog or the current pointer may still use.
94
+ const dest = (0, node_path_1.join)((0, node_path_1.dirname)((0, user_config_js_1.userConfigPath)()), 'runtime', `${version}-${hash.digest('hex').slice(0, 16)}`);
95
+ if ((0, node_fs_1.existsSync)((0, node_path_1.join)(dest, 'dist', 'cli.js')))
96
+ return (0, node_path_1.join)(dest, 'dist', 'cli.js');
97
+ const stage = `${dest}.${(0, node_crypto_1.randomUUID)()}.tmp`;
98
+ try {
99
+ (0, node_fs_1.mkdirSync)(stage, { recursive: true, mode: 0o700 });
100
+ (0, node_fs_1.cpSync)((0, node_path_1.join)(pkgRoot, 'package.json'), (0, node_path_1.join)(stage, 'package.json'));
101
+ (0, node_fs_1.cpSync)((0, node_path_1.join)(pkgRoot, 'dist'), (0, node_path_1.join)(stage, 'dist'), { recursive: true });
102
+ const sdkDest = (0, node_path_1.join)(stage, 'node_modules', '@nocville', 'node');
103
+ (0, node_fs_1.mkdirSync)(sdkDest, { recursive: true });
104
+ (0, node_fs_1.cpSync)((0, node_path_1.join)(sdkRoot, 'package.json'), (0, node_path_1.join)(sdkDest, 'package.json'));
105
+ (0, node_fs_1.cpSync)((0, node_path_1.join)(sdkRoot, 'dist'), (0, node_path_1.join)(sdkDest, 'dist'), { recursive: true });
106
+ const pngDest = (0, node_path_1.join)(stage, 'node_modules', 'pngjs');
107
+ (0, node_fs_1.mkdirSync)(pngDest, { recursive: true });
108
+ (0, node_fs_1.cpSync)((0, node_path_1.join)(pngRoot, 'package.json'), (0, node_path_1.join)(pngDest, 'package.json'));
109
+ (0, node_fs_1.cpSync)((0, node_path_1.join)(pngRoot, 'lib'), (0, node_path_1.join)(pngDest, 'lib'), { recursive: true });
110
+ try {
111
+ (0, node_fs_1.renameSync)(stage, dest);
112
+ }
113
+ catch (error) {
114
+ if (!(0, node_fs_1.existsSync)((0, node_path_1.join)(dest, 'dist', 'cli.js')))
115
+ throw error;
116
+ }
117
+ return (0, node_path_1.join)(dest, 'dist', 'cli.js');
118
+ }
119
+ finally {
120
+ (0, node_fs_1.rmSync)(stage, { recursive: true, force: true });
121
+ }
122
+ }
123
+ /** Prune only old snapshots, never the active pointer target or unknown directories. */
124
+ function pruneOldRuntimes(runtimeDir, now = Date.now()) {
125
+ let active;
126
+ try {
127
+ const pointer = JSON.parse((0, node_fs_1.readFileSync)((0, node_path_1.join)(runtimeDir, 'current.json'), 'utf8'));
128
+ if (typeof pointer.cli !== 'string')
129
+ return 0;
130
+ active = (0, node_path_1.resolve)(pointer.cli);
131
+ }
132
+ catch {
133
+ return 0;
134
+ }
135
+ let removed = 0;
136
+ for (const entry of (0, node_fs_1.readdirSync)(runtimeDir, { withFileTypes: true })) {
137
+ if (!entry.isDirectory() || !/^\d[^/]*-[a-f0-9]{16}$/.test(entry.name))
138
+ continue;
139
+ const path = (0, node_path_1.resolve)(runtimeDir, entry.name);
140
+ if (active === path || active.startsWith(path + node_path_1.sep))
141
+ continue;
142
+ try {
143
+ if (now - (0, node_fs_1.statSync)(path).mtimeMs <= 7 * 86_400_000)
144
+ continue;
145
+ (0, node_fs_1.rmSync)(path, { recursive: true, force: true });
146
+ removed++;
147
+ }
148
+ catch {
149
+ /* best-effort maintenance must not break setup */
150
+ }
151
+ }
152
+ return removed;
153
+ }
154
+ //# sourceMappingURL=runtime.js.map