@nocville/npc 0.0.0-stage → 0.2.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.
Files changed (210) hide show
  1. package/README.md +243 -2
  2. package/dist/cjs/cli.js +533 -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/text.js +15 -0
  11. package/dist/cjs/core/thresholds.js +48 -0
  12. package/dist/cjs/core/types.js +10 -0
  13. package/dist/cjs/core/user-config.js +92 -0
  14. package/dist/cjs/doctor.js +130 -0
  15. package/dist/cjs/index.js +90 -0
  16. package/dist/cjs/npcs/agent-common/activity.js +291 -0
  17. package/dist/cjs/npcs/agent-common/git-stats.js +71 -0
  18. package/dist/cjs/npcs/agent-common/hook-runner.js +252 -0
  19. package/dist/cjs/npcs/agent-common/hook-settings.js +279 -0
  20. package/dist/cjs/npcs/agent-common/liveness.js +112 -0
  21. package/dist/cjs/npcs/agent-common/lock.js +99 -0
  22. package/dist/cjs/npcs/agent-common/runtime.js +154 -0
  23. package/dist/cjs/npcs/agent-common/session.js +390 -0
  24. package/dist/cjs/npcs/agent-common/shim.js +96 -0
  25. package/dist/cjs/npcs/agent-common/watchdog.js +149 -0
  26. package/dist/cjs/npcs/claude-code/defaults.js +20 -0
  27. package/dist/cjs/npcs/claude-code/hook-handler.js +139 -0
  28. package/dist/cjs/npcs/claude-code/index.js +104 -0
  29. package/dist/cjs/npcs/claude-code/setup.js +133 -0
  30. package/dist/cjs/npcs/claude-code/state-machine.js +130 -0
  31. package/dist/cjs/npcs/codex/defaults.js +32 -0
  32. package/dist/cjs/npcs/codex/hook-handler.js +116 -0
  33. package/dist/cjs/npcs/codex/index.js +92 -0
  34. package/dist/cjs/npcs/codex/rollout.js +246 -0
  35. package/dist/cjs/npcs/codex/setup.js +104 -0
  36. package/dist/cjs/npcs/codex/watcher.js +346 -0
  37. package/dist/cjs/npcs/cursor/defaults.js +15 -0
  38. package/dist/cjs/npcs/cursor/hook-handler.js +65 -0
  39. package/dist/cjs/npcs/cursor/index.js +72 -0
  40. package/dist/cjs/npcs/cursor/normalize.js +54 -0
  41. package/dist/cjs/npcs/cursor/setup.js +98 -0
  42. package/dist/cjs/npcs/gemini/defaults.js +15 -0
  43. package/dist/cjs/npcs/gemini/hook-handler.js +93 -0
  44. package/dist/cjs/npcs/gemini/index.js +69 -0
  45. package/dist/cjs/npcs/gemini/setup.js +98 -0
  46. package/dist/cjs/npcs/github-watcher/defaults.js +11 -0
  47. package/dist/cjs/npcs/github-watcher/format.js +21 -0
  48. package/dist/cjs/npcs/github-watcher/github-api.js +126 -0
  49. package/dist/cjs/npcs/github-watcher/index.js +148 -0
  50. package/dist/cjs/npcs/internet-connectivity/checks.js +92 -0
  51. package/dist/cjs/npcs/internet-connectivity/defaults.js +22 -0
  52. package/dist/cjs/npcs/internet-connectivity/index.js +251 -0
  53. package/dist/cjs/npcs/log-watcher/buffer.js +40 -0
  54. package/dist/cjs/npcs/log-watcher/defaults.js +12 -0
  55. package/dist/cjs/npcs/log-watcher/index.js +161 -0
  56. package/dist/cjs/npcs/log-watcher/tail.js +81 -0
  57. package/dist/cjs/npcs/process-watcher/defaults.js +9 -0
  58. package/dist/cjs/npcs/process-watcher/index.js +140 -0
  59. package/dist/cjs/npcs/process-watcher/process-scanner.js +114 -0
  60. package/dist/cjs/npcs/receptionist/defaults.js +9 -0
  61. package/dist/cjs/npcs/receptionist/index.js +120 -0
  62. package/dist/cjs/npcs/receptionist/weather.js +129 -0
  63. package/dist/cjs/npcs/system-monitor/collectors.js +68 -0
  64. package/dist/cjs/npcs/system-monitor/defaults.js +13 -0
  65. package/dist/cjs/npcs/system-monitor/index.js +105 -0
  66. package/dist/cjs/npcs/web-page-watcher/basic-mode.js +58 -0
  67. package/dist/cjs/npcs/web-page-watcher/capture.js +80 -0
  68. package/dist/cjs/npcs/web-page-watcher/defaults.js +31 -0
  69. package/dist/cjs/npcs/web-page-watcher/index.js +234 -0
  70. package/dist/cjs/npcs/web-page-watcher/pixelate.js +181 -0
  71. package/dist/cjs/package.json +1 -0
  72. package/dist/cli.d.ts +3 -0
  73. package/dist/cli.js +498 -0
  74. package/dist/core/api-key.d.ts +34 -0
  75. package/dist/core/api-key.js +64 -0
  76. package/dist/core/base-npc.d.ts +41 -0
  77. package/dist/core/base-npc.js +156 -0
  78. package/dist/core/config.d.ts +22 -0
  79. package/dist/core/config.js +101 -0
  80. package/dist/core/event-npc.d.ts +8 -0
  81. package/dist/core/event-npc.js +10 -0
  82. package/dist/core/logger.d.ts +11 -0
  83. package/dist/core/logger.js +35 -0
  84. package/dist/core/polling-npc.d.ts +18 -0
  85. package/dist/core/polling-npc.js +61 -0
  86. package/dist/core/screen.d.ts +11 -0
  87. package/dist/core/screen.js +24 -0
  88. package/dist/core/text.d.ts +6 -0
  89. package/dist/core/text.js +12 -0
  90. package/dist/core/thresholds.d.ts +10 -0
  91. package/dist/core/thresholds.js +44 -0
  92. package/dist/core/types.d.ts +60 -0
  93. package/dist/core/types.js +7 -0
  94. package/dist/core/user-config.d.ts +43 -0
  95. package/dist/core/user-config.js +85 -0
  96. package/dist/doctor.d.ts +8 -0
  97. package/dist/doctor.js +126 -0
  98. package/dist/index.d.ts +39 -0
  99. package/dist/index.js +25 -0
  100. package/dist/npcs/agent-common/activity.d.ts +42 -0
  101. package/dist/npcs/agent-common/activity.js +280 -0
  102. package/dist/npcs/agent-common/git-stats.d.ts +11 -0
  103. package/dist/npcs/agent-common/git-stats.js +67 -0
  104. package/dist/npcs/agent-common/hook-runner.d.ts +62 -0
  105. package/dist/npcs/agent-common/hook-runner.js +245 -0
  106. package/dist/npcs/agent-common/hook-settings.d.ts +75 -0
  107. package/dist/npcs/agent-common/hook-settings.js +265 -0
  108. package/dist/npcs/agent-common/liveness.d.ts +12 -0
  109. package/dist/npcs/agent-common/liveness.js +105 -0
  110. package/dist/npcs/agent-common/lock.d.ts +6 -0
  111. package/dist/npcs/agent-common/lock.js +95 -0
  112. package/dist/npcs/agent-common/runtime.d.ts +16 -0
  113. package/dist/npcs/agent-common/runtime.js +149 -0
  114. package/dist/npcs/agent-common/session.d.ts +184 -0
  115. package/dist/npcs/agent-common/session.js +368 -0
  116. package/dist/npcs/agent-common/shim.d.ts +4 -0
  117. package/dist/npcs/agent-common/shim.js +90 -0
  118. package/dist/npcs/agent-common/watchdog.d.ts +16 -0
  119. package/dist/npcs/agent-common/watchdog.js +144 -0
  120. package/dist/npcs/claude-code/defaults.d.ts +15 -0
  121. package/dist/npcs/claude-code/defaults.js +17 -0
  122. package/dist/npcs/claude-code/hook-handler.d.ts +40 -0
  123. package/dist/npcs/claude-code/hook-handler.js +131 -0
  124. package/dist/npcs/claude-code/index.d.ts +36 -0
  125. package/dist/npcs/claude-code/index.js +92 -0
  126. package/dist/npcs/claude-code/setup.d.ts +58 -0
  127. package/dist/npcs/claude-code/setup.js +121 -0
  128. package/dist/npcs/claude-code/state-machine.d.ts +28 -0
  129. package/dist/npcs/claude-code/state-machine.js +125 -0
  130. package/dist/npcs/codex/defaults.d.ts +24 -0
  131. package/dist/npcs/codex/defaults.js +28 -0
  132. package/dist/npcs/codex/hook-handler.d.ts +24 -0
  133. package/dist/npcs/codex/hook-handler.js +110 -0
  134. package/dist/npcs/codex/index.d.ts +21 -0
  135. package/dist/npcs/codex/index.js +76 -0
  136. package/dist/npcs/codex/rollout.d.ts +39 -0
  137. package/dist/npcs/codex/rollout.js +242 -0
  138. package/dist/npcs/codex/setup.d.ts +37 -0
  139. package/dist/npcs/codex/setup.js +96 -0
  140. package/dist/npcs/codex/watcher.d.ts +62 -0
  141. package/dist/npcs/codex/watcher.js +341 -0
  142. package/dist/npcs/cursor/defaults.d.ts +9 -0
  143. package/dist/npcs/cursor/defaults.js +11 -0
  144. package/dist/npcs/cursor/hook-handler.d.ts +8 -0
  145. package/dist/npcs/cursor/hook-handler.js +60 -0
  146. package/dist/npcs/cursor/index.d.ts +18 -0
  147. package/dist/npcs/cursor/index.js +57 -0
  148. package/dist/npcs/cursor/normalize.d.ts +6 -0
  149. package/dist/npcs/cursor/normalize.js +49 -0
  150. package/dist/npcs/cursor/setup.d.ts +29 -0
  151. package/dist/npcs/cursor/setup.js +90 -0
  152. package/dist/npcs/gemini/defaults.d.ts +9 -0
  153. package/dist/npcs/gemini/defaults.js +11 -0
  154. package/dist/npcs/gemini/hook-handler.d.ts +10 -0
  155. package/dist/npcs/gemini/hook-handler.js +87 -0
  156. package/dist/npcs/gemini/index.d.ts +17 -0
  157. package/dist/npcs/gemini/index.js +56 -0
  158. package/dist/npcs/gemini/setup.d.ts +29 -0
  159. package/dist/npcs/gemini/setup.js +90 -0
  160. package/dist/npcs/github-watcher/defaults.d.ts +8 -0
  161. package/dist/npcs/github-watcher/defaults.js +8 -0
  162. package/dist/npcs/github-watcher/format.d.ts +3 -0
  163. package/dist/npcs/github-watcher/format.js +18 -0
  164. package/dist/npcs/github-watcher/github-api.d.ts +55 -0
  165. package/dist/npcs/github-watcher/github-api.js +120 -0
  166. package/dist/npcs/github-watcher/index.d.ts +35 -0
  167. package/dist/npcs/github-watcher/index.js +144 -0
  168. package/dist/npcs/internet-connectivity/checks.d.ts +24 -0
  169. package/dist/npcs/internet-connectivity/checks.js +84 -0
  170. package/dist/npcs/internet-connectivity/defaults.d.ts +20 -0
  171. package/dist/npcs/internet-connectivity/defaults.js +19 -0
  172. package/dist/npcs/internet-connectivity/index.d.ts +55 -0
  173. package/dist/npcs/internet-connectivity/index.js +246 -0
  174. package/dist/npcs/log-watcher/buffer.d.ts +18 -0
  175. package/dist/npcs/log-watcher/buffer.js +34 -0
  176. package/dist/npcs/log-watcher/defaults.d.ts +9 -0
  177. package/dist/npcs/log-watcher/defaults.js +9 -0
  178. package/dist/npcs/log-watcher/index.d.ts +45 -0
  179. package/dist/npcs/log-watcher/index.js +157 -0
  180. package/dist/npcs/log-watcher/tail.d.ts +9 -0
  181. package/dist/npcs/log-watcher/tail.js +78 -0
  182. package/dist/npcs/process-watcher/defaults.d.ts +6 -0
  183. package/dist/npcs/process-watcher/defaults.js +6 -0
  184. package/dist/npcs/process-watcher/index.d.ts +26 -0
  185. package/dist/npcs/process-watcher/index.js +136 -0
  186. package/dist/npcs/process-watcher/process-scanner.d.ts +19 -0
  187. package/dist/npcs/process-watcher/process-scanner.js +110 -0
  188. package/dist/npcs/receptionist/defaults.d.ts +6 -0
  189. package/dist/npcs/receptionist/defaults.js +6 -0
  190. package/dist/npcs/receptionist/index.d.ts +29 -0
  191. package/dist/npcs/receptionist/index.js +116 -0
  192. package/dist/npcs/receptionist/weather.d.ts +37 -0
  193. package/dist/npcs/receptionist/weather.js +121 -0
  194. package/dist/npcs/system-monitor/collectors.d.ts +45 -0
  195. package/dist/npcs/system-monitor/collectors.js +60 -0
  196. package/dist/npcs/system-monitor/defaults.d.ts +7 -0
  197. package/dist/npcs/system-monitor/defaults.js +10 -0
  198. package/dist/npcs/system-monitor/index.d.ts +27 -0
  199. package/dist/npcs/system-monitor/index.js +101 -0
  200. package/dist/npcs/web-page-watcher/basic-mode.d.ts +16 -0
  201. package/dist/npcs/web-page-watcher/basic-mode.js +52 -0
  202. package/dist/npcs/web-page-watcher/capture.d.ts +49 -0
  203. package/dist/npcs/web-page-watcher/capture.js +75 -0
  204. package/dist/npcs/web-page-watcher/defaults.d.ts +19 -0
  205. package/dist/npcs/web-page-watcher/defaults.js +27 -0
  206. package/dist/npcs/web-page-watcher/index.d.ts +42 -0
  207. package/dist/npcs/web-page-watcher/index.js +230 -0
  208. package/dist/npcs/web-page-watcher/pixelate.d.ts +36 -0
  209. package/dist/npcs/web-page-watcher/pixelate.js +172 -0
  210. package/package.json +73 -3
@@ -0,0 +1,533 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
+ if (k2 === undefined) k2 = k;
5
+ var desc = Object.getOwnPropertyDescriptor(m, k);
6
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
+ desc = { enumerable: true, get: function() { return m[k]; } };
8
+ }
9
+ Object.defineProperty(o, k2, desc);
10
+ }) : (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ o[k2] = m[k];
13
+ }));
14
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
15
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
16
+ }) : function(o, v) {
17
+ o["default"] = v;
18
+ });
19
+ var __importStar = (this && this.__importStar) || (function () {
20
+ var ownKeys = function(o) {
21
+ ownKeys = Object.getOwnPropertyNames || function (o) {
22
+ var ar = [];
23
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
24
+ return ar;
25
+ };
26
+ return ownKeys(o);
27
+ };
28
+ return function (mod) {
29
+ if (mod && mod.__esModule) return mod;
30
+ var result = {};
31
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
32
+ __setModuleDefault(result, mod);
33
+ return result;
34
+ };
35
+ })();
36
+ Object.defineProperty(exports, "__esModule", { value: true });
37
+ const node_fs_1 = require("node:fs");
38
+ const node_os_1 = require("node:os");
39
+ const node_path_1 = require("node:path");
40
+ const config_js_1 = require("./core/config.js");
41
+ const logger_js_1 = require("./core/logger.js");
42
+ // NPC modules are imported lazily per command: `claude-code hook` / `codex hook`
43
+ // run on every tool call, so they must not pay for loading every NPC (and pngjs).
44
+ const USAGE = `
45
+ nocville-npc <command> [options]
46
+
47
+ Quick start (AI coworkers):
48
+ nocville-npc setup Paste your agent key (hidden prompt),
49
+ then hook up installed coding agents
50
+ (whichever are installed)
51
+ nocville-npc setup --api-key=<agent key> Same, non-interactive
52
+ nocville-npc uninstall Remove all Nocville hooks
53
+
54
+ Config:
55
+ init [--api-key=<key>] [--api-url=<url>] [--map=<name>]
56
+ Save credentials (prompts for the key if omitted) to ~/.config/nocville/config.json (0600)
57
+ so no other command needs --api-key
58
+ doctor Check API connectivity and a test status push
59
+
60
+ AI coding agents (one NPC per session, named "Claude · <project>"):
61
+ claude-code setup Install hooks into ~/.claude/settings.json
62
+ claude-code uninstall Remove them
63
+ codex setup Install hooks into ~/.codex/hooks.json (then trust
64
+ them once with /hooks inside Codex)
65
+ codex uninstall Remove them
66
+ gemini setup Install hooks into ~/.gemini/settings.json
67
+ gemini uninstall Remove them
68
+ cursor setup Install native hooks into ~/.cursor/hooks.json
69
+ cursor uninstall Remove them
70
+ codex watch No-hooks fallback: follow ~/.codex/sessions logs
71
+ --idle-timeout=<min> Despawn a session after this long quiet (default 30)
72
+ --include-exec Also show non-interactive \`codex exec\` runs
73
+
74
+ Monitoring NPC types:
75
+ system-monitor Monitor CPU, memory, and disk usage
76
+ internet-connectivity Monitor network connectivity
77
+ process-watcher Watch for running processes
78
+ log-watcher Watch log files for errors
79
+ receptionist Greeter showing time + local weather
80
+ github-watcher Call out new commits on a repo
81
+ web-page-watcher Render a watched web page as a pixel-art TV
82
+
83
+ Common Options:
84
+ --api-key=<key> Team API key (or NOCVILLE_API_KEY env)
85
+ --api-url=<url> Nocville API URL (or NOCVILLE_API_URL env).
86
+ Default: https://nocville.com. For local dev use
87
+ --api-url=http://localhost:3601
88
+ --service-id=<id> Service ID (or NOCVILLE_SERVICE_ID env)
89
+ --name=<display name> Friendly NPC banner name (or NOCVILLE_NAME env).
90
+ Default: a per-NPC name (e.g. "System Monitor").
91
+ --map=<id-or-name> Target map by id or name (or NOCVILLE_MAP env).
92
+ Default: the team's default map.
93
+ --debug Enable debug logging
94
+ --help Show this help message
95
+
96
+ System Monitor Options:
97
+ --poll-interval=<ms> Poll interval in ms (default: 15000)
98
+ --disk-path=<path> Disk path to monitor (default: /)
99
+ --cpu-warn=<n> CPU warning threshold % (default: 75)
100
+ --cpu-crit=<n> CPU critical threshold % (default: 90)
101
+ --mem-warn=<n> Memory warning threshold % (default: 75)
102
+ --mem-crit=<n> Memory critical threshold % (default: 90)
103
+ --disk-warn=<n> Disk warning threshold % (default: 75)
104
+ --disk-crit=<n> Disk critical threshold % (default: 90)
105
+
106
+ Internet Connectivity Options:
107
+ --poll-interval=<ms> Poll interval in ms (default: 30000)
108
+ --dns-hosts=<list> Comma-separated DNS hosts (default: google.com,cloudflare.com)
109
+ --http-urls=<list> Comma-separated HTTP URLs to check
110
+ --latency-hosts=<list> Comma-separated hosts for latency checks
111
+ --slow-threshold=<ms> Slow response threshold in ms (default: 1000)
112
+
113
+ Process Watcher Options:
114
+ --poll-interval=<ms> Poll interval in ms (default: 15000)
115
+ --processes=<list> Comma-separated process names to watch
116
+ --cpu-warn=<n> CPU warning threshold % (default: 80)
117
+ --mem-warn=<n> Memory warning threshold MB (default: 500)
118
+
119
+ Log Watcher Options:
120
+ --files=<list> Comma-separated log file paths
121
+ --error-pattern=<regex> Error pattern (default: ERROR|FATAL)
122
+ --warning-pattern=<regex> Warning pattern (default: WARN)
123
+ --window=<seconds> Sliding window in seconds (default: 300)
124
+ --error-warn=<n> Error count for warning status (default: 5)
125
+ --error-crit=<n> Error count for critical status (default: 20)
126
+ --report-interval=<ms> Report interval in ms (default: 15000)
127
+
128
+ Receptionist Options:
129
+ --poll-interval=<ms> Clock tick in ms (default: 60000)
130
+ --lat=<n> --lon=<n> Explicit coordinates for weather
131
+ --location=<name> Place name to geocode (else IP geo)
132
+ --fahrenheit Show temperature in °F (default: °C)
133
+
134
+ GitHub Watcher Options:
135
+ --repo=<owner/name> Repository to watch (required)
136
+ --branch=<name> Branch to watch (default: repo default branch)
137
+ --token=<token> GitHub token (or GITHUB_TOKEN env)
138
+ --poll-interval=<ms> Poll interval in ms (default: 60000)
139
+
140
+ Web Page Watcher Options:
141
+ --url=<url> Page to watch (required)
142
+ --mode=<auto|screenshot|basic> Render mode (default: auto)
143
+ --interval=<ms> Poll interval in ms (default: 60000, floor 15000)
144
+ --viewport=<WxH> Browser viewport (default: screen x 8)
145
+ --screen=<WxH> TV pixel resolution (default: 160x120; e.g. 160x100)
146
+ --wait-until=<event> Puppeteer waitUntil (default: networkidle2)
147
+ --chrome-path=<path> Chrome executable (or CHROME_PATH env)
148
+ (needs puppeteer-core + Chrome for the pixel-art TV; falls back to basic)
149
+ `.trim();
150
+ async function main() {
151
+ const parsed = (0, config_js_1.parseArgs)(process.argv);
152
+ if (parsed.flags['version']) {
153
+ console.log(JSON.parse((0, node_fs_1.readFileSync)((0, node_path_1.join)((0, node_path_1.dirname)((0, node_fs_1.realpathSync)(process.argv[1])), '..', 'package.json'), 'utf8')).version);
154
+ return;
155
+ }
156
+ if (parsed.flags['help'] || parsed.flags['h']) {
157
+ console.log(USAGE);
158
+ process.exit(0);
159
+ }
160
+ if (!parsed.command) {
161
+ console.error('Error: NPC type required\n');
162
+ console.log(USAGE);
163
+ process.exit(1);
164
+ }
165
+ const config = (0, config_js_1.resolveConfig)(parsed.flags);
166
+ const logger = new logger_js_1.Logger('cli', config.debug);
167
+ // Setup-style commands resolve the key: --api-key > env > saved > hidden prompt.
168
+ const isAgentSetup = ['claude-code', 'codex', 'gemini', 'cursor'].includes(parsed.command) &&
169
+ parsed.subcommand === 'setup' &&
170
+ !parsed.flags['uninstall'];
171
+ if (isAgentSetup || parsed.command === 'setup') {
172
+ if (parsed.command === 'setup') {
173
+ const { codexHome } = await Promise.resolve().then(() => __importStar(require('./npcs/codex/defaults.js')));
174
+ if (!(0, node_fs_1.existsSync)((0, node_path_1.join)((0, node_os_1.homedir)(), '.claude')) &&
175
+ !(0, node_fs_1.existsSync)(codexHome()) &&
176
+ !(0, node_fs_1.existsSync)((0, node_path_1.join)((0, node_os_1.homedir)(), '.gemini')) &&
177
+ !(0, node_fs_1.existsSync)((0, node_path_1.join)((0, node_os_1.homedir)(), '.cursor'))) {
178
+ console.error('Nothing to set up: install Claude Code, Codex, Gemini CLI or Cursor first.');
179
+ process.exitCode = 1;
180
+ return;
181
+ }
182
+ }
183
+ const key = await resolveSetupKey(parsed.flags);
184
+ if (key === null)
185
+ return;
186
+ if (key !== undefined)
187
+ parsed.flags['api-key'] = key;
188
+ }
189
+ // AI-agent integrations have their own config handling (hooks read the saved config).
190
+ if (parsed.command === 'claude-code') {
191
+ const { handleClaudeCodeCommand } = await Promise.resolve().then(() => __importStar(require('./npcs/claude-code/index.js')));
192
+ await handleClaudeCodeCommand({
193
+ subcommand: parsed.subcommand ?? 'hook',
194
+ config,
195
+ flags: parsed.flags,
196
+ args: parsed.args,
197
+ });
198
+ return;
199
+ }
200
+ if (parsed.command === 'codex') {
201
+ const { handleCodexCommand } = await Promise.resolve().then(() => __importStar(require('./npcs/codex/index.js')));
202
+ await handleCodexCommand({
203
+ subcommand: parsed.subcommand ?? 'hook',
204
+ config,
205
+ flags: parsed.flags,
206
+ args: parsed.args,
207
+ });
208
+ return;
209
+ }
210
+ if (parsed.command === 'gemini') {
211
+ const { handleGeminiCommand } = await Promise.resolve().then(() => __importStar(require('./npcs/gemini/index.js')));
212
+ await handleGeminiCommand({
213
+ subcommand: parsed.subcommand ?? 'hook',
214
+ config,
215
+ flags: parsed.flags,
216
+ args: parsed.args,
217
+ });
218
+ return;
219
+ }
220
+ if (parsed.command === 'cursor') {
221
+ const { handleCursorCommand } = await Promise.resolve().then(() => __importStar(require('./npcs/cursor/index.js')));
222
+ await handleCursorCommand({
223
+ subcommand: parsed.subcommand ?? 'hook',
224
+ config,
225
+ flags: parsed.flags,
226
+ args: parsed.args,
227
+ });
228
+ return;
229
+ }
230
+ if (parsed.command === 'init') {
231
+ await runInit(parsed.flags, config);
232
+ return;
233
+ }
234
+ if (parsed.command === 'setup') {
235
+ await runSetupAll(parsed.flags);
236
+ return;
237
+ }
238
+ if (parsed.command === 'uninstall') {
239
+ const { uninstallHooks } = await Promise.resolve().then(() => __importStar(require('./npcs/claude-code/index.js')));
240
+ const { uninstallCodexHooks } = await Promise.resolve().then(() => __importStar(require('./npcs/codex/index.js')));
241
+ uninstallHooks();
242
+ uninstallCodexHooks();
243
+ const { uninstallGeminiHooks } = await Promise.resolve().then(() => __importStar(require('./npcs/gemini/index.js')));
244
+ uninstallGeminiHooks();
245
+ const { uninstallCursorHooks } = await Promise.resolve().then(() => __importStar(require('./npcs/cursor/index.js')));
246
+ uninstallCursorHooks();
247
+ return;
248
+ }
249
+ // Doctor checks connectivity; it works without an API key (push step is skipped).
250
+ if (parsed.command === 'doctor') {
251
+ const { runDoctor } = await Promise.resolve().then(() => __importStar(require('./doctor.js')));
252
+ await runDoctor(config);
253
+ return;
254
+ }
255
+ // All other NPC types require api-url and token
256
+ if (!config.apiKey) {
257
+ console.error('Error: no API key — run `nocville-npc init --api-key=<key>`, or pass --api-key / NOCVILLE_API_KEY');
258
+ process.exit(1);
259
+ }
260
+ switch (parsed.command) {
261
+ case 'system-monitor': {
262
+ if (!config.serviceId) {
263
+ config.serviceId = 'system-monitor';
264
+ }
265
+ const { SystemMonitorNPC } = await Promise.resolve().then(() => __importStar(require('./npcs/system-monitor/index.js')));
266
+ const npc = new SystemMonitorNPC({
267
+ ...config,
268
+ pollInterval: (0, config_js_1.numberFlag)(parsed.flags, 'poll-interval'),
269
+ diskPath: (0, config_js_1.stringFlag)(parsed.flags, 'disk-path'),
270
+ cpuThreshold: {
271
+ warning: (0, config_js_1.numberFlag)(parsed.flags, 'cpu-warn') ?? 75,
272
+ critical: (0, config_js_1.numberFlag)(parsed.flags, 'cpu-crit') ?? 90,
273
+ direction: 'above',
274
+ },
275
+ memThreshold: {
276
+ warning: (0, config_js_1.numberFlag)(parsed.flags, 'mem-warn') ?? 75,
277
+ critical: (0, config_js_1.numberFlag)(parsed.flags, 'mem-crit') ?? 90,
278
+ direction: 'above',
279
+ },
280
+ diskThreshold: {
281
+ warning: (0, config_js_1.numberFlag)(parsed.flags, 'disk-warn') ?? 75,
282
+ critical: (0, config_js_1.numberFlag)(parsed.flags, 'disk-crit') ?? 90,
283
+ direction: 'above',
284
+ },
285
+ });
286
+ logger.info('Starting system-monitor NPC');
287
+ await npc.start();
288
+ break;
289
+ }
290
+ case 'internet-connectivity': {
291
+ if (!config.serviceId) {
292
+ config.serviceId = 'internet-connectivity';
293
+ }
294
+ const dnsHosts = (0, config_js_1.stringFlag)(parsed.flags, 'dns-hosts')?.split(',');
295
+ const httpUrls = (0, config_js_1.stringFlag)(parsed.flags, 'http-urls')?.split(',');
296
+ const latencyHostsRaw = (0, config_js_1.stringFlag)(parsed.flags, 'latency-hosts')?.split(',');
297
+ const latencyHosts = latencyHostsRaw?.map((h) => ({ host: h.trim(), port: 80 }));
298
+ const { InternetConnectivityNPC } = await Promise.resolve().then(() => __importStar(require('./npcs/internet-connectivity/index.js')));
299
+ const npc = new InternetConnectivityNPC({
300
+ ...config,
301
+ pollInterval: (0, config_js_1.numberFlag)(parsed.flags, 'poll-interval'),
302
+ dnsHosts,
303
+ httpUrls,
304
+ latencyHosts,
305
+ slowThreshold: (0, config_js_1.numberFlag)(parsed.flags, 'slow-threshold'),
306
+ });
307
+ logger.info('Starting internet-connectivity NPC');
308
+ await npc.start();
309
+ break;
310
+ }
311
+ case 'process-watcher': {
312
+ if (!config.serviceId) {
313
+ config.serviceId = 'process-watcher';
314
+ }
315
+ const processesStr = (0, config_js_1.stringFlag)(parsed.flags, 'processes');
316
+ if (!processesStr) {
317
+ console.error('Error: --processes is required (comma-separated list)');
318
+ process.exit(1);
319
+ }
320
+ const { ProcessWatcherNPC } = await Promise.resolve().then(() => __importStar(require('./npcs/process-watcher/index.js')));
321
+ const npc = new ProcessWatcherNPC({
322
+ ...config,
323
+ pollInterval: (0, config_js_1.numberFlag)(parsed.flags, 'poll-interval'),
324
+ processes: processesStr.split(','),
325
+ cpuWarn: (0, config_js_1.numberFlag)(parsed.flags, 'cpu-warn'),
326
+ memWarn: (0, config_js_1.numberFlag)(parsed.flags, 'mem-warn'),
327
+ });
328
+ logger.info('Starting process-watcher NPC');
329
+ await npc.start();
330
+ break;
331
+ }
332
+ case 'log-watcher': {
333
+ const filesStr = (0, config_js_1.stringFlag)(parsed.flags, 'files');
334
+ if (!filesStr) {
335
+ console.error('Error: --files is required (comma-separated list)');
336
+ process.exit(1);
337
+ }
338
+ if (!config.serviceId) {
339
+ config.serviceId = 'log-watcher';
340
+ }
341
+ const { LogWatcherNPC } = await Promise.resolve().then(() => __importStar(require('./npcs/log-watcher/index.js')));
342
+ const npc = new LogWatcherNPC({
343
+ ...config,
344
+ files: filesStr.split(','),
345
+ errorPattern: (0, config_js_1.stringFlag)(parsed.flags, 'error-pattern'),
346
+ warningPattern: (0, config_js_1.stringFlag)(parsed.flags, 'warning-pattern'),
347
+ windowSeconds: (0, config_js_1.numberFlag)(parsed.flags, 'window'),
348
+ errorWarn: (0, config_js_1.numberFlag)(parsed.flags, 'error-warn'),
349
+ errorCrit: (0, config_js_1.numberFlag)(parsed.flags, 'error-crit'),
350
+ reportInterval: (0, config_js_1.numberFlag)(parsed.flags, 'report-interval'),
351
+ });
352
+ logger.info('Starting log-watcher NPC');
353
+ await npc.start();
354
+ break;
355
+ }
356
+ case 'receptionist': {
357
+ if (!config.serviceId) {
358
+ config.serviceId = 'receptionist';
359
+ }
360
+ const { ReceptionistNPC } = await Promise.resolve().then(() => __importStar(require('./npcs/receptionist/index.js')));
361
+ const npc = new ReceptionistNPC({
362
+ ...config,
363
+ pollInterval: (0, config_js_1.numberFlag)(parsed.flags, 'poll-interval'),
364
+ lat: (0, config_js_1.numberFlag)(parsed.flags, 'lat'),
365
+ lon: (0, config_js_1.numberFlag)(parsed.flags, 'lon'),
366
+ location: (0, config_js_1.stringFlag)(parsed.flags, 'location'),
367
+ fahrenheit: (0, config_js_1.boolFlag)(parsed.flags, 'fahrenheit') ?? false,
368
+ });
369
+ logger.info('Starting receptionist NPC');
370
+ await npc.start();
371
+ break;
372
+ }
373
+ case 'github-watcher': {
374
+ const repo = (0, config_js_1.stringFlag)(parsed.flags, 'repo');
375
+ if (!repo) {
376
+ console.error('Error: --repo=<owner/name> is required');
377
+ process.exit(1);
378
+ }
379
+ if (!config.serviceId) {
380
+ const shortName = repo.split('/').pop() || repo;
381
+ config.serviceId = `github-${shortName.replace(/[^a-zA-Z0-9_-]/g, '-')}`.slice(0, 100);
382
+ }
383
+ const { GitHubWatcherNPC } = await Promise.resolve().then(() => __importStar(require('./npcs/github-watcher/index.js')));
384
+ const npc = new GitHubWatcherNPC({
385
+ ...config,
386
+ repo,
387
+ branch: (0, config_js_1.stringFlag)(parsed.flags, 'branch'),
388
+ token: (0, config_js_1.stringFlag)(parsed.flags, 'token') ?? process.env['GITHUB_TOKEN'],
389
+ pollInterval: (0, config_js_1.numberFlag)(parsed.flags, 'poll-interval'),
390
+ });
391
+ logger.info('Starting github-watcher NPC');
392
+ await npc.start();
393
+ break;
394
+ }
395
+ case 'web-page-watcher': {
396
+ const url = (0, config_js_1.stringFlag)(parsed.flags, 'url');
397
+ if (!url) {
398
+ console.error('Error: --url=<url> is required');
399
+ process.exit(1);
400
+ }
401
+ const { hostnameOf } = await Promise.resolve().then(() => __importStar(require('./npcs/web-page-watcher/basic-mode.js')));
402
+ const { parseDimensions } = await Promise.resolve().then(() => __importStar(require('./npcs/web-page-watcher/defaults.js')));
403
+ const { WebPageWatcherNPC } = await Promise.resolve().then(() => __importStar(require('./npcs/web-page-watcher/index.js')));
404
+ const parseWxH = (v) => v ? (parseDimensions(v) ?? undefined) : undefined;
405
+ if (!config.serviceId) {
406
+ config.serviceId = `webpage-${hostnameOf(url)}`
407
+ .replace(/[^a-zA-Z0-9_.-]/g, '-')
408
+ .slice(0, 100);
409
+ }
410
+ const modeFlag = (0, config_js_1.stringFlag)(parsed.flags, 'mode');
411
+ const mode = modeFlag === 'auto' || modeFlag === 'screenshot' || modeFlag === 'basic'
412
+ ? modeFlag
413
+ : undefined;
414
+ const viewport = parseWxH((0, config_js_1.stringFlag)(parsed.flags, 'viewport'));
415
+ const screen = parseWxH((0, config_js_1.stringFlag)(parsed.flags, 'screen'));
416
+ const npc = new WebPageWatcherNPC({
417
+ ...config,
418
+ url,
419
+ mode,
420
+ chromePath: (0, config_js_1.stringFlag)(parsed.flags, 'chrome-path'),
421
+ waitUntil: (0, config_js_1.stringFlag)(parsed.flags, 'wait-until'),
422
+ viewportWidth: viewport?.width,
423
+ viewportHeight: viewport?.height,
424
+ screenWidth: screen?.width,
425
+ screenHeight: screen?.height,
426
+ pollInterval: (0, config_js_1.numberFlag)(parsed.flags, 'interval') ?? (0, config_js_1.numberFlag)(parsed.flags, 'poll-interval'),
427
+ });
428
+ logger.info('Starting web-page-watcher NPC');
429
+ await npc.start();
430
+ break;
431
+ }
432
+ default:
433
+ console.error(`Error: Unknown NPC type "${parsed.command}"\n`);
434
+ console.log(USAGE);
435
+ process.exit(1);
436
+ }
437
+ }
438
+ async function runInit(flags, config) {
439
+ const { saveUserConfig, loadUserConfig, maskKey } = await Promise.resolve().then(() => __importStar(require('./core/user-config.js')));
440
+ let apiKey = (0, config_js_1.stringFlag)(flags, 'api-key');
441
+ const apiUrl = (0, config_js_1.stringFlag)(flags, 'api-url');
442
+ const map = (0, config_js_1.stringFlag)(flags, 'map');
443
+ if (!apiKey && !apiUrl && !map) {
444
+ const saved = loadUserConfig();
445
+ if (saved.apiKey && !process.env['NOCVILLE_API_KEY']?.trim()) {
446
+ console.log('Usage: nocville-npc init --api-key=<key> [--api-url=<url>] [--map=<name>]');
447
+ console.log(`Current: api ${saved.apiUrl ?? config.apiUrl} key ${maskKey(saved.apiKey)}`);
448
+ return;
449
+ }
450
+ const { resolveApiKey } = await Promise.resolve().then(() => __importStar(require('./core/api-key.js')));
451
+ const resolved = await resolveApiKey({ flags, savedKey: undefined });
452
+ if (!resolved.ok) {
453
+ console.error(resolved.error);
454
+ process.exitCode = 1;
455
+ return;
456
+ }
457
+ apiKey = resolved.key;
458
+ }
459
+ const path = saveUserConfig({ apiKey, apiUrl, map });
460
+ const saved = loadUserConfig();
461
+ console.log(`✓ Saved ${path} (mode 0600)`);
462
+ console.log(` API: ${saved.apiUrl ?? 'https://nocville.com'} key: ${maskKey(saved.apiKey)}`);
463
+ console.log(' Next: nocville-npc setup (hooks up installed coding agents)');
464
+ }
465
+ /**
466
+ * Resolve the API key for a setup-style command. Returns the key to save, or
467
+ * `undefined` when the already-saved key should be kept, or `null` (after
468
+ * printing an actionable error and setting a non-zero exit code) when none.
469
+ */
470
+ async function resolveSetupKey(flags) {
471
+ const { resolveApiKey } = await Promise.resolve().then(() => __importStar(require('./core/api-key.js')));
472
+ const resolved = await resolveApiKey({ flags });
473
+ if (!resolved.ok) {
474
+ console.error(resolved.error);
475
+ process.exitCode = 1;
476
+ return null;
477
+ }
478
+ return resolved.source === 'saved' ? undefined : resolved.key;
479
+ }
480
+ /** `setup`: save credentials, then install Claude Code + Codex hooks where present. */
481
+ async function runSetupAll(flags) {
482
+ const { setupHooks } = await Promise.resolve().then(() => __importStar(require('./npcs/claude-code/index.js')));
483
+ const { setupCodexHooks } = await Promise.resolve().then(() => __importStar(require('./npcs/codex/index.js')));
484
+ const { codexHome } = await Promise.resolve().then(() => __importStar(require('./npcs/codex/defaults.js')));
485
+ const opts = {
486
+ apiUrl: (0, config_js_1.stringFlag)(flags, 'api-url') ?? process.env['NOCVILLE_API_URL'],
487
+ apiKey: (0, config_js_1.stringFlag)(flags, 'api-key') ?? process.env['NOCVILLE_API_KEY'],
488
+ map: (0, config_js_1.stringFlag)(flags, 'map'),
489
+ cliPath: process.argv[1],
490
+ };
491
+ let any = false;
492
+ if ((0, node_fs_1.existsSync)((0, node_path_1.join)((0, node_os_1.homedir)(), '.claude'))) {
493
+ any = setupHooks(opts) || any;
494
+ }
495
+ else {
496
+ console.log('· Claude Code not found (~/.claude missing) — skipped');
497
+ }
498
+ if ((0, node_fs_1.existsSync)(codexHome())) {
499
+ any = setupCodexHooks(opts) || any;
500
+ }
501
+ else {
502
+ console.log('· Codex not found (~/.codex missing) — skipped');
503
+ }
504
+ if ((0, node_fs_1.existsSync)((0, node_path_1.join)((0, node_os_1.homedir)(), '.gemini'))) {
505
+ const { setupGeminiHooks } = await Promise.resolve().then(() => __importStar(require('./npcs/gemini/index.js')));
506
+ any = setupGeminiHooks(opts) || any;
507
+ }
508
+ else {
509
+ console.log('· Gemini CLI not found (~/.gemini missing) — skipped');
510
+ }
511
+ if ((0, node_fs_1.existsSync)((0, node_path_1.join)((0, node_os_1.homedir)(), '.cursor'))) {
512
+ const { setupCursorHooks } = await Promise.resolve().then(() => __importStar(require('./npcs/cursor/index.js')));
513
+ any = setupCursorHooks(opts) || any;
514
+ }
515
+ else {
516
+ console.log('· Cursor not found (~/.cursor missing) — skipped');
517
+ }
518
+ if (!any) {
519
+ console.error('Nothing to set up: install Claude Code, Codex, Gemini CLI or Cursor first.');
520
+ process.exitCode = 1;
521
+ }
522
+ }
523
+ main().catch((err) => {
524
+ const parsed = (0, config_js_1.parseArgs)(process.argv);
525
+ if (['gemini', 'cursor'].includes(parsed.command ?? '') &&
526
+ (!parsed.subcommand || parsed.subcommand === 'hook')) {
527
+ process.stdout.write('{}\n');
528
+ return;
529
+ }
530
+ console.error('Fatal error:', err);
531
+ process.exit(1);
532
+ });
533
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1,69 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MISSING_API_KEY_ERROR = exports.API_KEY_PROMPT = void 0;
4
+ exports.resolveApiKey = resolveApiKey;
5
+ exports.promptHidden = promptHidden;
6
+ const node_readline_1 = require("node:readline");
7
+ const node_stream_1 = require("node:stream");
8
+ const config_js_1 = require("./config.js");
9
+ const user_config_js_1 = require("./user-config.js");
10
+ exports.API_KEY_PROMPT = 'Paste your Nocville API key: ';
11
+ exports.MISSING_API_KEY_ERROR = 'Error: no API key. Pass --api-key=<agent key>, set NOCVILLE_API_KEY, or run this ' +
12
+ 'command in an interactive terminal to paste the key (input is hidden). ' +
13
+ 'Your team admin can generate a key in Nocville team settings.';
14
+ async function resolveApiKey(opts) {
15
+ const fromFlag = (0, config_js_1.stringFlag)(opts.flags, 'api-key')?.trim();
16
+ if (fromFlag)
17
+ return { ok: true, key: fromFlag, source: 'flag' };
18
+ const fromEnv = (opts.env ?? process.env)['NOCVILLE_API_KEY']?.trim();
19
+ if (fromEnv)
20
+ return { ok: true, key: fromEnv, source: 'env' };
21
+ const saved = 'savedKey' in opts ? opts.savedKey : (0, user_config_js_1.loadUserConfig)().apiKey;
22
+ if (saved)
23
+ return { ok: true, key: saved, source: 'saved' };
24
+ const isTTY = opts.isTTY ?? Boolean(process.stdin.isTTY);
25
+ if (!isTTY)
26
+ return { ok: false, error: exports.MISSING_API_KEY_ERROR };
27
+ let answer;
28
+ try {
29
+ answer = (await (opts.prompt ?? promptHidden)(exports.API_KEY_PROMPT)).trim();
30
+ }
31
+ catch {
32
+ return { ok: false, error: 'Cancelled — no API key entered.' };
33
+ }
34
+ if (!answer)
35
+ return { ok: false, error: exports.MISSING_API_KEY_ERROR };
36
+ return { ok: true, key: answer, source: 'prompt' };
37
+ }
38
+ /**
39
+ * Ask a question on the terminal without echoing the answer. The question is
40
+ * written directly; readline's own echo goes to a muted stream.
41
+ */
42
+ function promptHidden(question, input = process.stdin, output = process.stdout) {
43
+ return new Promise((resolve, reject) => {
44
+ const muted = new node_stream_1.Writable({
45
+ write(_chunk, _enc, cb) {
46
+ cb();
47
+ },
48
+ });
49
+ output.write(question);
50
+ const rl = (0, node_readline_1.createInterface)({ input, output: muted, terminal: true });
51
+ let done = false;
52
+ rl.on('line', (line) => {
53
+ done = true;
54
+ rl.close();
55
+ output.write('\n');
56
+ resolve(line);
57
+ });
58
+ rl.on('SIGINT', () => {
59
+ rl.close();
60
+ });
61
+ rl.on('close', () => {
62
+ if (!done) {
63
+ output.write('\n');
64
+ reject(new Error('cancelled'));
65
+ }
66
+ });
67
+ });
68
+ }
69
+ //# sourceMappingURL=api-key.js.map