@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,60 @@
1
+ import os from 'node:os';
2
+ import { promises as fsp } from 'node:fs';
3
+ function aggregate(cpus) {
4
+ let idle = 0;
5
+ let total = 0;
6
+ for (const cpu of cpus) {
7
+ const t = cpu.times;
8
+ idle += t.idle;
9
+ total += t.user + t.nice + t.sys + t.irq + t.idle;
10
+ }
11
+ return { idle, total };
12
+ }
13
+ /**
14
+ * Stateful CPU sampler that computes usage from the delta between consecutive
15
+ * `os.cpus()` snapshots — no blocking sleep. The first sample reports usage
16
+ * since system boot; subsequent samples report usage since the previous call.
17
+ *
18
+ * `readCpus`/`readLoad` are injectable for testing.
19
+ */
20
+ export function createCpuSampler(readCpus = () => os.cpus(), readLoad = () => os.loadavg()[0] ?? 0) {
21
+ let prev = null;
22
+ return {
23
+ sample() {
24
+ const cur = aggregate(readCpus());
25
+ const loadAvg1m = Math.round(readLoad() * 100) / 100;
26
+ const base = prev ?? { idle: 0, total: 0 };
27
+ const idleDiff = cur.idle - base.idle;
28
+ const totalDiff = cur.total - base.total;
29
+ prev = cur;
30
+ const percent = totalDiff > 0 ? Math.round((1 - idleDiff / totalDiff) * 100) : 0;
31
+ return { percent, loadAvg1m };
32
+ },
33
+ };
34
+ }
35
+ /**
36
+ * Collect memory usage from OS.
37
+ */
38
+ export function collectMemory() {
39
+ const totalBytes = os.totalmem();
40
+ const freeBytes = os.freemem();
41
+ const usedBytes = totalBytes - freeBytes;
42
+ const totalGb = Math.round((totalBytes / 1024 ** 3) * 100) / 100;
43
+ const usedGb = Math.round((usedBytes / 1024 ** 3) * 100) / 100;
44
+ const percent = Math.round((usedBytes / totalBytes) * 100);
45
+ return { percent, usedGb, totalGb };
46
+ }
47
+ /**
48
+ * Collect disk usage via fs.statfs (Node 18.15+).
49
+ */
50
+ export async function collectDisk(path) {
51
+ const stats = await fsp.statfs(path);
52
+ const totalBytes = stats.blocks * stats.bsize;
53
+ const freeBytes = stats.bavail * stats.bsize;
54
+ const usedBytes = totalBytes - freeBytes;
55
+ const totalGb = Math.round((totalBytes / 1024 ** 3) * 100) / 100;
56
+ const usedGb = Math.round((usedBytes / 1024 ** 3) * 100) / 100;
57
+ const percent = totalBytes > 0 ? Math.round((usedBytes / totalBytes) * 100) : 0;
58
+ return { percent, usedGb, totalGb };
59
+ }
60
+ //# sourceMappingURL=collectors.js.map
@@ -0,0 +1,7 @@
1
+ import type { ThresholdConfig } from '../../core/types.js';
2
+ export declare const DEFAULTS: {
3
+ pollInterval: number;
4
+ diskPath: string;
5
+ };
6
+ export declare const DEFAULT_THRESHOLDS: Record<string, ThresholdConfig>;
7
+ //# sourceMappingURL=defaults.d.ts.map
@@ -0,0 +1,10 @@
1
+ export const DEFAULTS = {
2
+ pollInterval: 15000,
3
+ diskPath: '/',
4
+ };
5
+ export const DEFAULT_THRESHOLDS = {
6
+ cpu: { warning: 75, critical: 90, direction: 'above' },
7
+ mem: { warning: 75, critical: 90, direction: 'above' },
8
+ disk: { warning: 75, critical: 90, direction: 'above' },
9
+ };
10
+ //# sourceMappingURL=defaults.js.map
@@ -0,0 +1,27 @@
1
+ import { PollingNPC } from '../../core/polling-npc.js';
2
+ import type { PollingNpcConfig } from '../../core/polling-npc.js';
3
+ import type { CollectedMetrics, ThresholdConfig, Archetype, BehaviorHints, CompanionDecl } from '../../core/types.js';
4
+ export interface SystemMonitorConfig extends PollingNpcConfig {
5
+ diskPath?: string;
6
+ cpuThreshold?: ThresholdConfig;
7
+ memThreshold?: ThresholdConfig;
8
+ diskThreshold?: ThresholdConfig;
9
+ }
10
+ export declare class SystemMonitorNPC extends PollingNPC {
11
+ private diskPath;
12
+ private cpuThreshold;
13
+ private memThreshold;
14
+ private diskThreshold;
15
+ private cpuSampler;
16
+ private cpuHistory;
17
+ private memHistory;
18
+ constructor(config: SystemMonitorConfig);
19
+ get name(): string;
20
+ protected get defaultDisplayName(): string;
21
+ get archetype(): Archetype;
22
+ get heartbeatSeconds(): number;
23
+ get behavior(): BehaviorHints;
24
+ get companion(): CompanionDecl;
25
+ protected collect(): Promise<CollectedMetrics>;
26
+ }
27
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,101 @@
1
+ import { PollingNPC } from '../../core/polling-npc.js';
2
+ import { evaluateThreshold, worstStatus } from '../../core/thresholds.js';
3
+ import { statusColor, pushHistory } from '../../core/screen.js';
4
+ import { createCpuSampler, collectMemory, collectDisk } from './collectors.js';
5
+ import { DEFAULTS, DEFAULT_THRESHOLDS } from './defaults.js';
6
+ export class SystemMonitorNPC extends PollingNPC {
7
+ diskPath;
8
+ cpuThreshold;
9
+ memThreshold;
10
+ diskThreshold;
11
+ cpuSampler = createCpuSampler();
12
+ cpuHistory = [];
13
+ memHistory = [];
14
+ constructor(config) {
15
+ super({
16
+ ...config,
17
+ pollInterval: config.pollInterval ?? DEFAULTS.pollInterval,
18
+ });
19
+ this.diskPath = config.diskPath ?? DEFAULTS.diskPath;
20
+ this.cpuThreshold = config.cpuThreshold ?? DEFAULT_THRESHOLDS['cpu'];
21
+ this.memThreshold = config.memThreshold ?? DEFAULT_THRESHOLDS['mem'];
22
+ this.diskThreshold = config.diskThreshold ?? DEFAULT_THRESHOLDS['disk'];
23
+ }
24
+ get name() {
25
+ return 'system-monitor';
26
+ }
27
+ get defaultDisplayName() {
28
+ return 'System Monitor';
29
+ }
30
+ get archetype() {
31
+ return 'service';
32
+ }
33
+ get heartbeatSeconds() {
34
+ return 30;
35
+ }
36
+ get behavior() {
37
+ return { idle: 'wander' };
38
+ }
39
+ get companion() {
40
+ return {
41
+ screenType: 'dashboard-wall',
42
+ size: { w: 2, h: 2 },
43
+ placement: 'adjacent-wall',
44
+ label: 'System',
45
+ };
46
+ }
47
+ async collect() {
48
+ const cpu = this.cpuSampler.sample();
49
+ const mem = collectMemory();
50
+ const disk = await collectDisk(this.diskPath);
51
+ const cpuStatus = evaluateThreshold(cpu.percent, this.cpuThreshold);
52
+ const memStatus = evaluateThreshold(mem.percent, this.memThreshold);
53
+ const diskStatus = evaluateThreshold(disk.percent, this.diskThreshold);
54
+ const status = worstStatus([cpuStatus, memStatus, diskStatus]);
55
+ const mood = status === 'critical'
56
+ ? 'stressed'
57
+ : status === 'warning'
58
+ ? 'concerned'
59
+ : 'happy';
60
+ pushHistory(this.cpuHistory, cpu.percent);
61
+ pushHistory(this.memHistory, mem.percent);
62
+ const screenData = {
63
+ layout: 'multi-metric',
64
+ values: [
65
+ {
66
+ label: 'CPU',
67
+ value: `${cpu.percent}%`,
68
+ color: statusColor(cpuStatus),
69
+ sparkline: [...this.cpuHistory],
70
+ },
71
+ {
72
+ label: 'MEM',
73
+ value: `${mem.percent}%`,
74
+ color: statusColor(memStatus),
75
+ sparkline: [...this.memHistory],
76
+ },
77
+ { label: 'DISK', value: `${disk.percent}%`, color: statusColor(diskStatus) },
78
+ { label: 'LOAD', value: cpu.loadAvg1m.toFixed(2), color: 'gray' },
79
+ ],
80
+ };
81
+ return {
82
+ status,
83
+ mood,
84
+ message: `CPU: ${cpu.percent}% | Mem: ${mem.percent}% | Disk: ${disk.percent}%`,
85
+ screenData,
86
+ // When strained, go find a desk and get to work; otherwise wander.
87
+ behavior: status !== 'healthy' ? { busy: 'seek-desk' } : undefined,
88
+ metrics: {
89
+ cpu_percent: cpu.percent,
90
+ mem_percent: mem.percent,
91
+ mem_used_gb: mem.usedGb,
92
+ mem_total_gb: mem.totalGb,
93
+ disk_percent: disk.percent,
94
+ disk_used_gb: disk.usedGb,
95
+ disk_total_gb: disk.totalGb,
96
+ load_avg_1m: cpu.loadAvg1m,
97
+ },
98
+ };
99
+ }
100
+ }
101
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,16 @@
1
+ import type { Status } from '../../core/types.js';
2
+ export interface BasicCheck {
3
+ status: number;
4
+ title: string;
5
+ ok: boolean;
6
+ timedOut: boolean;
7
+ }
8
+ /** Extract the <title> text from an HTML string (empty if none). */
9
+ export declare function extractTitle(html: string): string;
10
+ /** Map an HTTP status to NPC status: 2xx/3xx ok, 4xx/timeout warn, 5xx/refused crit. */
11
+ export declare function statusFromHttp(code: number, timedOut?: boolean): Status;
12
+ /** Fetch a page and extract HTTP status + <title> (basic, no browser). */
13
+ export declare function basicCheck(url: string, timeoutMs?: number): Promise<BasicCheck>;
14
+ /** Best-effort hostname for a URL, falling back to the raw string. */
15
+ export declare function hostnameOf(url: string): string;
16
+ //# sourceMappingURL=basic-mode.d.ts.map
@@ -0,0 +1,52 @@
1
+ /** Extract the <title> text from an HTML string (empty if none). */
2
+ export function extractTitle(html) {
3
+ const m = html.match(/<title[^>]*>([\s\S]*?)<\/title>/i);
4
+ return m ? m[1].replace(/\s+/g, ' ').trim().slice(0, 80) : '';
5
+ }
6
+ /** Map an HTTP status to NPC status: 2xx/3xx ok, 4xx/timeout warn, 5xx/refused crit. */
7
+ export function statusFromHttp(code, timedOut = false) {
8
+ if (code >= 200 && code < 400)
9
+ return 'healthy';
10
+ if (code >= 400 && code < 500)
11
+ return 'warning';
12
+ if (code >= 500)
13
+ return 'critical';
14
+ return timedOut ? 'warning' : 'critical'; // 0 = network error
15
+ }
16
+ /** Fetch a page and extract HTTP status + <title> (basic, no browser). */
17
+ export async function basicCheck(url, timeoutMs = 8000) {
18
+ const controller = new AbortController();
19
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
20
+ try {
21
+ const res = await fetch(url, {
22
+ signal: controller.signal,
23
+ headers: { 'User-Agent': 'nocville-npc' },
24
+ redirect: 'follow',
25
+ });
26
+ let title = '';
27
+ try {
28
+ title = extractTitle(await res.text());
29
+ }
30
+ catch {
31
+ // body unavailable; leave title blank
32
+ }
33
+ return { status: res.status, title, ok: res.ok, timedOut: false };
34
+ }
35
+ catch (err) {
36
+ const timedOut = err instanceof Error && err.name === 'AbortError';
37
+ return { status: 0, title: '', ok: false, timedOut };
38
+ }
39
+ finally {
40
+ clearTimeout(timer);
41
+ }
42
+ }
43
+ /** Best-effort hostname for a URL, falling back to the raw string. */
44
+ export function hostnameOf(url) {
45
+ try {
46
+ return new URL(url).hostname;
47
+ }
48
+ catch {
49
+ return url.slice(0, 40);
50
+ }
51
+ }
52
+ //# sourceMappingURL=basic-mode.js.map
@@ -0,0 +1,49 @@
1
+ export interface PuppeteerViewport {
2
+ width: number;
3
+ height: number;
4
+ deviceScaleFactor?: number;
5
+ }
6
+ export interface PuppeteerResponse {
7
+ status(): number;
8
+ }
9
+ export interface PuppeteerPage {
10
+ setViewport(v: PuppeteerViewport): Promise<void>;
11
+ goto(url: string, opts?: {
12
+ waitUntil?: string;
13
+ timeout?: number;
14
+ }): Promise<PuppeteerResponse | null>;
15
+ screenshot(opts: {
16
+ type?: string;
17
+ fullPage?: boolean;
18
+ }): Promise<Buffer | Uint8Array>;
19
+ close(): Promise<void>;
20
+ }
21
+ export interface PuppeteerBrowser {
22
+ newPage(): Promise<PuppeteerPage>;
23
+ close(): Promise<void>;
24
+ /** puppeteer >= 20 exposes `connected`; older versions expose isConnected(). */
25
+ connected?: boolean;
26
+ isConnected?(): boolean;
27
+ on(event: string, cb: () => void): void;
28
+ }
29
+ export interface PuppeteerModule {
30
+ launch(opts: {
31
+ executablePath?: string;
32
+ headless?: boolean | 'new';
33
+ args?: string[];
34
+ }): Promise<PuppeteerBrowser>;
35
+ }
36
+ /**
37
+ * Find a usable Chrome/Chromium executable: explicit path → CHROME_PATH env →
38
+ * platform-specific known locations (mac app bundles; `which` on linux).
39
+ */
40
+ export declare function findChrome(explicit?: string): string | null;
41
+ /**
42
+ * Load the optional `puppeteer-core` peer dependency without a static import,
43
+ * so the package builds and runs when it isn't installed. Returns null if the
44
+ * module is absent.
45
+ */
46
+ export declare function loadPuppeteer(): Promise<PuppeteerModule | null>;
47
+ /** True if the browser reports a live connection. */
48
+ export declare function browserConnected(browser: PuppeteerBrowser): boolean;
49
+ //# sourceMappingURL=capture.d.ts.map
@@ -0,0 +1,75 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { execFileSync } from 'node:child_process';
3
+ const MAC_PATHS = [
4
+ '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
5
+ '/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary',
6
+ '/Applications/Chromium.app/Contents/MacOS/Chromium',
7
+ '/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge',
8
+ '/Applications/Brave Browser.app/Contents/MacOS/Brave Browser',
9
+ ];
10
+ const LINUX_BINARIES = [
11
+ 'google-chrome',
12
+ 'google-chrome-stable',
13
+ 'chromium',
14
+ 'chromium-browser',
15
+ 'brave-browser',
16
+ ];
17
+ function which(bin) {
18
+ try {
19
+ const out = execFileSync('which', [bin], { encoding: 'utf-8' }).trim();
20
+ return out || null;
21
+ }
22
+ catch {
23
+ return null;
24
+ }
25
+ }
26
+ /**
27
+ * Find a usable Chrome/Chromium executable: explicit path → CHROME_PATH env →
28
+ * platform-specific known locations (mac app bundles; `which` on linux).
29
+ */
30
+ export function findChrome(explicit) {
31
+ const envPath = process.env['CHROME_PATH'];
32
+ if (explicit && existsSync(explicit))
33
+ return explicit;
34
+ if (envPath && existsSync(envPath))
35
+ return envPath;
36
+ if (process.platform === 'darwin') {
37
+ for (const p of MAC_PATHS)
38
+ if (existsSync(p))
39
+ return p;
40
+ return null;
41
+ }
42
+ if (process.platform === 'linux') {
43
+ for (const bin of LINUX_BINARIES) {
44
+ const resolved = which(bin);
45
+ if (resolved && existsSync(resolved))
46
+ return resolved;
47
+ }
48
+ return null;
49
+ }
50
+ return null;
51
+ }
52
+ /**
53
+ * Load the optional `puppeteer-core` peer dependency without a static import,
54
+ * so the package builds and runs when it isn't installed. Returns null if the
55
+ * module is absent.
56
+ */
57
+ export async function loadPuppeteer() {
58
+ try {
59
+ const dynImport = new Function('m', 'return import(m)');
60
+ const mod = await dynImport('puppeteer-core');
61
+ return (mod.default ?? mod);
62
+ }
63
+ catch {
64
+ return null;
65
+ }
66
+ }
67
+ /** True if the browser reports a live connection. */
68
+ export function browserConnected(browser) {
69
+ if (typeof browser.connected === 'boolean')
70
+ return browser.connected;
71
+ if (typeof browser.isConnected === 'function')
72
+ return browser.isConnected();
73
+ return true;
74
+ }
75
+ //# sourceMappingURL=capture.js.map
@@ -0,0 +1,19 @@
1
+ export declare const DEFAULTS: {
2
+ pollInterval: number;
3
+ minPollInterval: number;
4
+ navTimeoutMs: number;
5
+ maxColors: number;
6
+ /** Polls to keep the 'changed' sub-status active after a page change. */
7
+ changedPolls: number;
8
+ screenW: number;
9
+ screenH: number;
10
+ scaleDivisor: number;
11
+ waitUntil: string;
12
+ };
13
+ export type WatchMode = 'auto' | 'screenshot' | 'basic';
14
+ /** Parse a "WxH" string into a {width, height} pair, or null if malformed. */
15
+ export declare function parseDimensions(s: string): {
16
+ width: number;
17
+ height: number;
18
+ } | null;
19
+ //# sourceMappingURL=defaults.d.ts.map
@@ -0,0 +1,27 @@
1
+ export const DEFAULTS = {
2
+ pollInterval: 60000, // 60s
3
+ minPollInterval: 15000, // floor
4
+ navTimeoutMs: 15000,
5
+ maxColors: 32,
6
+ /** Polls to keep the 'changed' sub-status active after a page change. */
7
+ changedPolls: 2,
8
+ // Default TV resolution (4:3, matches the 4×3 web-view companion). The client
9
+ // reads WxH from the pixel-image `fmt` string, so any size is self-consistent.
10
+ screenW: 160,
11
+ screenH: 120,
12
+ // deviceScaleFactor is 1/8, so the viewport defaults to screen × 8 → Chrome
13
+ // rasterizes the screen size natively (overridable via --viewport).
14
+ scaleDivisor: 8,
15
+ waitUntil: 'networkidle2',
16
+ };
17
+ /** Parse a "WxH" string into a {width, height} pair, or null if malformed. */
18
+ export function parseDimensions(s) {
19
+ const m = s
20
+ .trim()
21
+ .toLowerCase()
22
+ .match(/^(\d+)x(\d+)$/);
23
+ if (!m)
24
+ return null;
25
+ return { width: parseInt(m[1], 10), height: parseInt(m[2], 10) };
26
+ }
27
+ //# sourceMappingURL=defaults.js.map
@@ -0,0 +1,42 @@
1
+ import { PollingNPC } from '../../core/polling-npc.js';
2
+ import type { PollingNpcConfig } from '../../core/polling-npc.js';
3
+ import type { CollectedMetrics, Archetype, CompanionDecl } from '../../core/types.js';
4
+ import type { WatchMode } from './defaults.js';
5
+ export interface WebPageWatcherConfig extends PollingNpcConfig {
6
+ url: string;
7
+ mode?: WatchMode;
8
+ chromePath?: string;
9
+ viewportWidth?: number;
10
+ viewportHeight?: number;
11
+ screenWidth?: number;
12
+ screenHeight?: number;
13
+ waitUntil?: string;
14
+ }
15
+ export declare class WebPageWatcherNPC extends PollingNPC {
16
+ private url;
17
+ private mode;
18
+ private chromePath?;
19
+ private viewportW;
20
+ private viewportH;
21
+ private screenW;
22
+ private screenH;
23
+ private waitUntil;
24
+ private puppeteer;
25
+ private browser;
26
+ private browserUnavailable;
27
+ private chromeHintShown;
28
+ private lastHash;
29
+ private changedPollsLeft;
30
+ constructor(config: WebPageWatcherConfig);
31
+ get name(): string;
32
+ protected get defaultDisplayName(): string;
33
+ get archetype(): Archetype;
34
+ get companion(): CompanionDecl;
35
+ protected collect(): Promise<CollectedMetrics>;
36
+ protected onStop(): Promise<void>;
37
+ private collectScreenshot;
38
+ private ensureBrowser;
39
+ private maybeShowChromeHint;
40
+ private collectBasic;
41
+ }
42
+ //# sourceMappingURL=index.d.ts.map