@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,130 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.eventName = eventName;
4
+ exports.claudeTransition = claudeTransition;
5
+ exports.processHookEvent = processHookEvent;
6
+ const activity_js_1 = require("../agent-common/activity.js");
7
+ const hook_runner_js_1 = require("../agent-common/hook-runner.js");
8
+ const session_js_1 = require("../agent-common/session.js");
9
+ /** Resolve the event name from either the real or legacy field. */
10
+ function eventName(event) {
11
+ return (0, hook_runner_js_1.hookEventName)(event);
12
+ }
13
+ const working = (activity, warn = false) => ({
14
+ state: 'WORKING',
15
+ activity,
16
+ status: warn ? 'warning' : 'healthy',
17
+ mood: warn ? 'concerned' : 'happy',
18
+ });
19
+ const waiting = (activity) => ({
20
+ state: 'WAITING',
21
+ activity,
22
+ status: 'warning',
23
+ mood: 'concerned',
24
+ });
25
+ const idle = (activity) => ({
26
+ state: 'IDLE',
27
+ activity,
28
+ status: 'healthy',
29
+ mood: 'idle',
30
+ });
31
+ /** Notification types that mean "the human needs to act". */
32
+ const WAITING_NOTIFICATIONS = new Set([
33
+ 'permission_prompt',
34
+ 'elicitation_dialog',
35
+ 'elicitation_url_dialog',
36
+ 'agent_needs_input',
37
+ ]);
38
+ /**
39
+ * Map one Claude Code hook event to a phase + activity line.
40
+ * Event names / payload fields per code.claude.com/docs/en/hooks.
41
+ * Prompt text is never read (privacy); tool inputs are summarized, not echoed.
42
+ */
43
+ function claudeTransition(event, prev) {
44
+ const name = eventName(event);
45
+ const cwd = (typeof event.cwd === 'string' && event.cwd) || prev?.projectDir;
46
+ const keep = prev?.state ?? 'IDLE';
47
+ switch (name) {
48
+ case 'SessionStart':
49
+ return idle('Ready for a task');
50
+ case 'UserPromptSubmit':
51
+ return working(activity_js_1.THINKING);
52
+ case 'PreToolUse': {
53
+ const activity = (0, activity_js_1.describeToolUse)(event.tool_name, event.tool_input, cwd);
54
+ if ((0, activity_js_1.isQuestionTool)(event.tool_name))
55
+ return waiting(activity);
56
+ return working(activity);
57
+ }
58
+ case 'PostToolUse':
59
+ case 'PostToolBatch':
60
+ // Tool finished — still working; keep showing the same activity.
61
+ return { ...working(undefined), quiet: prev?.state === 'WORKING' };
62
+ case 'PostToolUseFailure':
63
+ return working((0, activity_js_1.failureText)(event.tool_name), true);
64
+ case 'PermissionRequest': {
65
+ const what = event.tool_name ? (0, activity_js_1.describeToolUse)(event.tool_name, event.tool_input, cwd) : '';
66
+ return waiting(what ? `Needs your approval: ${what}` : 'Needs your approval');
67
+ }
68
+ case 'Notification': {
69
+ const type = typeof event.notification_type === 'string' ? event.notification_type : '';
70
+ if (type === 'idle_prompt')
71
+ return idle('Done — waiting for next task');
72
+ if (type === 'permission_prompt')
73
+ return waiting('Needs your approval');
74
+ if (WAITING_NOTIFICATIONS.has(type) || !type)
75
+ return waiting('Waiting for you');
76
+ // auth_success, agent_completed, quota_* … → informational only.
77
+ return {
78
+ state: keep,
79
+ status: prev?.status ?? 'healthy',
80
+ mood: prev?.mood ?? 'idle',
81
+ quiet: true,
82
+ };
83
+ }
84
+ case 'Stop':
85
+ return idle('Done — waiting for next task');
86
+ case 'StopFailure':
87
+ return { ...idle('Stopped with an error'), status: 'warning', mood: 'concerned' };
88
+ case 'SubagentStart':
89
+ return working('Delegating to a subagent');
90
+ case 'SubagentStop':
91
+ return working('Subagent finished');
92
+ case 'PreCompact':
93
+ return working('Compacting context');
94
+ case 'SessionEnd':
95
+ return {
96
+ state: 'IDLE',
97
+ activity: 'Session ended',
98
+ status: 'unknown',
99
+ mood: 'idle',
100
+ offline: true,
101
+ };
102
+ default:
103
+ return {
104
+ state: keep,
105
+ status: prev?.status ?? 'healthy',
106
+ mood: prev?.mood ?? 'idle',
107
+ quiet: true,
108
+ };
109
+ }
110
+ }
111
+ /**
112
+ * Derive the NPC status output from a hook event (state + presentation).
113
+ * Kept for library consumers; the hook runner uses `claudeTransition` directly.
114
+ */
115
+ function processHookEvent(event, currentState) {
116
+ const t = claudeTransition(event, currentState);
117
+ const { subStatus, behavior } = (0, session_js_1.phasePresentation)(t.state, t.offline);
118
+ return {
119
+ state: t.state,
120
+ output: {
121
+ status: t.status,
122
+ mood: t.mood,
123
+ message: t.activity ?? currentState?.activity?.[0] ?? '',
124
+ subStatus,
125
+ behavior,
126
+ offline: t.offline,
127
+ },
128
+ };
129
+ }
130
+ //# sourceMappingURL=state-machine.js.map
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CODEX_DEFAULTS = void 0;
4
+ exports.codexHome = codexHome;
5
+ const node_os_1 = require("node:os");
6
+ const node_path_1 = require("node:path");
7
+ exports.CODEX_DEFAULTS = {
8
+ /** Hook-mode per-session state (shared with the watcher to avoid double pushes). */
9
+ stateDir: (0, node_path_1.join)((0, node_os_1.tmpdir)(), 'nocville-codex'),
10
+ stateFileMaxAge: 24 * 60 * 60 * 1000,
11
+ /** Watchdog-backed sessions become unknown after three minutes without a heartbeat. */
12
+ heartbeatSeconds: 90,
13
+ minPushGapMs: 1500,
14
+ hookTimeoutSeconds: 10,
15
+ /** Watcher: Codex session rollout logs. */
16
+ sessionsDir: (0, node_path_1.join)(process.env['CODEX_HOME'] ?? (0, node_path_1.join)((0, node_os_1.homedir)(), '.codex'), 'sessions'),
17
+ /** Watcher: poll interval for new sessions / appended lines. */
18
+ pollMs: 1000,
19
+ /**
20
+ * Watcher: a session whose rollout file hasn't grown for this long is treated
21
+ * as ended (offline push). Matches Codex's own 30-minute idle SessionEnd.
22
+ */
23
+ idleTimeoutMs: 30 * 60 * 1000,
24
+ /** Watcher: bytes read from the end of an already-active rollout on discovery. */
25
+ initialTailBytes: 256 * 1024,
26
+ /** Watcher: max bytes read per tick per session before skipping ahead to the tail. */
27
+ maxReadBytes: 8 * 1024 * 1024,
28
+ };
29
+ function codexHome() {
30
+ return process.env['CODEX_HOME'] ?? (0, node_path_1.join)((0, node_os_1.homedir)(), '.codex');
31
+ }
32
+ //# sourceMappingURL=defaults.js.map
@@ -0,0 +1,116 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CODEX_HOOK_SPEC = exports.CODEX_ADAPTER = void 0;
4
+ exports.codexServiceId = codexServiceId;
5
+ exports.codexTransition = codexTransition;
6
+ exports.handleCodexHookEvent = handleCodexHookEvent;
7
+ const activity_js_1 = require("../agent-common/activity.js");
8
+ const hook_runner_js_1 = require("../agent-common/hook-runner.js");
9
+ const session_js_1 = require("../agent-common/session.js");
10
+ const defaults_js_1 = require("./defaults.js");
11
+ exports.CODEX_ADAPTER = {
12
+ command: 'codex',
13
+ label: 'Codex',
14
+ family: 'codex',
15
+ stateDir: defaults_js_1.CODEX_DEFAULTS.stateDir,
16
+ heartbeatSeconds: defaults_js_1.CODEX_DEFAULTS.heartbeatSeconds,
17
+ processNames: ['codex'],
18
+ };
19
+ /**
20
+ * Codex session ids are UUIDv7 (time-ordered): the leading chars are shared by
21
+ * sessions started close together, so key the serviceId on the random tail.
22
+ */
23
+ function codexServiceId(sessionId, cwd) {
24
+ const hex = sessionId.replace(/[^A-Za-z0-9]/g, '');
25
+ return (0, session_js_1.sanitizeServiceId)(`codex-${(0, session_js_1.projectNameOf)(cwd)}-${hex.slice(-8) || 'session'}`);
26
+ }
27
+ const working = (activity, warn = false) => ({
28
+ state: 'WORKING',
29
+ activity,
30
+ status: warn ? 'warning' : 'healthy',
31
+ mood: warn ? 'concerned' : 'happy',
32
+ });
33
+ const waiting = (activity) => ({
34
+ state: 'WAITING',
35
+ activity,
36
+ status: 'warning',
37
+ mood: 'concerned',
38
+ });
39
+ const idle = (activity) => ({
40
+ state: 'IDLE',
41
+ activity,
42
+ status: 'healthy',
43
+ mood: 'idle',
44
+ });
45
+ /**
46
+ * Map one Codex hook event to a phase + activity line. Events and payload
47
+ * fields per the Codex hooks reference (developers.openai.com/codex/hooks):
48
+ * SessionStart, SessionEnd, UserPromptSubmit, PreToolUse, PostToolUse,
49
+ * PermissionRequest, Stop, Interrupt, PreCompact, PostCompact, SubagentStart,
50
+ * SubagentStop. Bash/apply_patch carry `tool_input.command`.
51
+ */
52
+ function codexTransition(event, prev) {
53
+ const name = (0, hook_runner_js_1.hookEventName)(event);
54
+ const cwd = (typeof event.cwd === 'string' && event.cwd) || prev?.projectDir;
55
+ const keep = prev?.state ?? 'IDLE';
56
+ switch (name) {
57
+ case 'SessionStart':
58
+ return idle('Ready for a task');
59
+ case 'UserPromptSubmit':
60
+ return working(activity_js_1.THINKING);
61
+ case 'PreToolUse': {
62
+ const activity = (0, activity_js_1.describeToolUse)(event.tool_name, event.tool_input, cwd);
63
+ return (0, activity_js_1.isQuestionTool)(event.tool_name) ? waiting(activity) : working(activity);
64
+ }
65
+ case 'PostToolUse':
66
+ case 'PostCompact':
67
+ return { ...working(undefined), quiet: prev?.state === 'WORKING' };
68
+ case 'PostToolUseFailure':
69
+ return working((0, activity_js_1.failureText)(event.tool_name), true);
70
+ case 'PermissionRequest': {
71
+ const what = event.tool_name ? (0, activity_js_1.describeToolUse)(event.tool_name, event.tool_input, cwd) : '';
72
+ return waiting(what ? `Needs your approval: ${what}` : 'Needs your approval');
73
+ }
74
+ case 'Stop':
75
+ return idle('Done — waiting for next task');
76
+ case 'Interrupt':
77
+ return idle('Interrupted — waiting for you');
78
+ case 'PreCompact':
79
+ return working('Compacting context');
80
+ case 'SubagentStart':
81
+ return working('Delegating to a subagent');
82
+ case 'SubagentStop':
83
+ return working('Subagent finished');
84
+ case 'SessionEnd':
85
+ return {
86
+ state: 'IDLE',
87
+ activity: 'Session ended',
88
+ status: 'unknown',
89
+ mood: 'idle',
90
+ offline: true,
91
+ };
92
+ default:
93
+ return {
94
+ state: keep,
95
+ status: prev?.status ?? 'healthy',
96
+ mood: prev?.mood ?? 'idle',
97
+ quiet: true,
98
+ };
99
+ }
100
+ }
101
+ exports.CODEX_HOOK_SPEC = {
102
+ adapter: exports.CODEX_ADAPTER,
103
+ serviceIdFor: codexServiceId,
104
+ transition: codexTransition,
105
+ };
106
+ /**
107
+ * Handle a single Codex hook event (stdin JSON unless `rawInput` is given).
108
+ * In-process push by default; the CLI passes `mode: 'detach'`. Never throws.
109
+ */
110
+ async function handleCodexHookEvent(config, rawInput, opts = {}) {
111
+ await (0, hook_runner_js_1.runAgentHook)(exports.CODEX_HOOK_SPEC, config, rawInput ?? (0, hook_runner_js_1.readStdin)(), {
112
+ minPushGapMs: defaults_js_1.CODEX_DEFAULTS.minPushGapMs,
113
+ ...opts,
114
+ });
115
+ }
116
+ //# sourceMappingURL=hook-handler.js.map
@@ -0,0 +1,92 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CodexWatcher = exports.describeExecScript = exports.parseRolloutLine = exports.CODEX_HOOK_EVENTS = exports.installCodexHooksInto = exports.uninstallCodexHooks = exports.setupCodexHooks = exports.handleCodexHookEvent = exports.codexServiceId = exports.codexTransition = exports.CODEX_ADAPTER = exports.CODEX_DEFAULTS = void 0;
4
+ exports.handleCodexCommand = handleCodexCommand;
5
+ const config_js_1 = require("../../core/config.js");
6
+ const hook_runner_js_1 = require("../agent-common/hook-runner.js");
7
+ const watchdog_js_1 = require("../agent-common/watchdog.js");
8
+ const session_js_1 = require("../agent-common/session.js");
9
+ const defaults_js_1 = require("./defaults.js");
10
+ const hook_handler_js_1 = require("./hook-handler.js");
11
+ const setup_js_1 = require("./setup.js");
12
+ const watcher_js_1 = require("./watcher.js");
13
+ var defaults_js_2 = require("./defaults.js");
14
+ Object.defineProperty(exports, "CODEX_DEFAULTS", { enumerable: true, get: function () { return defaults_js_2.CODEX_DEFAULTS; } });
15
+ var hook_handler_js_2 = require("./hook-handler.js");
16
+ Object.defineProperty(exports, "CODEX_ADAPTER", { enumerable: true, get: function () { return hook_handler_js_2.CODEX_ADAPTER; } });
17
+ Object.defineProperty(exports, "codexTransition", { enumerable: true, get: function () { return hook_handler_js_2.codexTransition; } });
18
+ Object.defineProperty(exports, "codexServiceId", { enumerable: true, get: function () { return hook_handler_js_2.codexServiceId; } });
19
+ Object.defineProperty(exports, "handleCodexHookEvent", { enumerable: true, get: function () { return hook_handler_js_2.handleCodexHookEvent; } });
20
+ var setup_js_2 = require("./setup.js");
21
+ Object.defineProperty(exports, "setupCodexHooks", { enumerable: true, get: function () { return setup_js_2.setupCodexHooks; } });
22
+ Object.defineProperty(exports, "uninstallCodexHooks", { enumerable: true, get: function () { return setup_js_2.uninstallCodexHooks; } });
23
+ Object.defineProperty(exports, "installCodexHooksInto", { enumerable: true, get: function () { return setup_js_2.installCodexHooksInto; } });
24
+ Object.defineProperty(exports, "CODEX_HOOK_EVENTS", { enumerable: true, get: function () { return setup_js_2.CODEX_HOOK_EVENTS; } });
25
+ var rollout_js_1 = require("./rollout.js");
26
+ Object.defineProperty(exports, "parseRolloutLine", { enumerable: true, get: function () { return rollout_js_1.parseRolloutLine; } });
27
+ Object.defineProperty(exports, "describeExecScript", { enumerable: true, get: function () { return rollout_js_1.describeExecScript; } });
28
+ var watcher_js_2 = require("./watcher.js");
29
+ Object.defineProperty(exports, "CodexWatcher", { enumerable: true, get: function () { return watcher_js_2.CodexWatcher; } });
30
+ /**
31
+ * codex subcommands: setup | uninstall | hook | flush | watch | cleanup.
32
+ * `hook`/`flush` are invoked by Codex / ourselves and stay silent on stdout.
33
+ */
34
+ async function handleCodexCommand(opts) {
35
+ const { subcommand, config, flags } = opts;
36
+ switch (subcommand) {
37
+ case 'setup': {
38
+ if (flags['uninstall']) {
39
+ (0, setup_js_1.uninstallCodexHooks)();
40
+ break;
41
+ }
42
+ const ok = (0, setup_js_1.setupCodexHooks)({
43
+ apiUrl: (0, config_js_1.stringFlag)(flags, 'api-url') ?? process.env['NOCVILLE_API_URL'],
44
+ apiKey: (0, config_js_1.stringFlag)(flags, 'api-key') ?? process.env['NOCVILLE_API_KEY'],
45
+ map: (0, config_js_1.stringFlag)(flags, 'map'),
46
+ cliPath: process.argv[1],
47
+ });
48
+ if (!ok)
49
+ process.exitCode = 1;
50
+ break;
51
+ }
52
+ case 'uninstall':
53
+ (0, setup_js_1.uninstallCodexHooks)();
54
+ break;
55
+ case 'hook':
56
+ await (0, hook_handler_js_1.handleCodexHookEvent)(config, (0, hook_runner_js_1.readStdin)(), { mode: 'detach' });
57
+ break;
58
+ case 'watchdog':
59
+ await (0, watchdog_js_1.runWatchdog)(hook_handler_js_1.CODEX_ADAPTER, config, opts.args?.[0]);
60
+ break;
61
+ case 'flush':
62
+ await (0, hook_runner_js_1.runFlushCommand)(hook_handler_js_1.CODEX_ADAPTER, config, opts.args?.[0], (0, config_js_1.numberFlag)(flags, 'delay'), flags['stats'] === true);
63
+ break;
64
+ case 'watch': {
65
+ console.error('codex watch is a fallback for Codex builds without hooks; prefer `nocville-npc codex setup`.');
66
+ if (!config.apiKey) {
67
+ console.error('Error: no API key — run `nocville-npc init --api-key=<key>` or pass --api-key');
68
+ process.exitCode = 1;
69
+ return;
70
+ }
71
+ const idleMin = (0, config_js_1.numberFlag)(flags, 'idle-timeout');
72
+ const watcher = new watcher_js_1.CodexWatcher(config, {
73
+ sessionsDir: (0, config_js_1.stringFlag)(flags, 'sessions-dir'),
74
+ idleTimeoutMs: idleMin !== undefined ? idleMin * 60_000 : undefined,
75
+ includeExec: (0, config_js_1.boolFlag)(flags, 'include-exec') ?? false,
76
+ pollMs: (0, config_js_1.numberFlag)(flags, 'poll-interval'),
77
+ });
78
+ await watcher.start();
79
+ break;
80
+ }
81
+ case 'cleanup': {
82
+ const removed = await (0, session_js_1.cleanupSessionStates)(defaults_js_1.CODEX_DEFAULTS.stateDir, defaults_js_1.CODEX_DEFAULTS.stateFileMaxAge);
83
+ console.log(`Cleanup complete: ${removed} stale file(s) removed`);
84
+ break;
85
+ }
86
+ default:
87
+ console.error(`Unknown codex subcommand: ${subcommand}`);
88
+ console.error('Available subcommands: setup, uninstall, watch, hook, cleanup');
89
+ process.exitCode = 1;
90
+ }
91
+ }
92
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,246 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.describeExecScript = describeExecScript;
4
+ exports.parseRolloutLine = parseRolloutLine;
5
+ // Unsupported, best-effort fallback: Codex rollout logs are an internal format. Prefer hooks.
6
+ const activity_js_1 = require("../agent-common/activity.js");
7
+ const isObj = (v) => !!v && typeof v === 'object' && !Array.isArray(v);
8
+ const s = (v) => (typeof v === 'string' && v ? v : undefined);
9
+ function stripFileUrl(p) {
10
+ return p.replace(/^file:\/\//, '');
11
+ }
12
+ /** Pull a JS/JSON string property (`cmd:"…"` / `"cmd": "…"`) out of a script. */
13
+ function scriptString(script, key) {
14
+ const re = new RegExp(`["']?${key}["']?\\s*:\\s*"((?:[^"\\\\]|\\\\.)*)"`);
15
+ const m = script.match(re);
16
+ if (!m)
17
+ return undefined;
18
+ try {
19
+ return JSON.parse(`"${m[1]}"`);
20
+ }
21
+ catch {
22
+ return m[1];
23
+ }
24
+ }
25
+ /** Tool helpers inside exec scripts that carry nothing worth showing. */
26
+ const QUIET_SCRIPT_TOOLS = new Set(['write_stdin', 'wait', 'clock__curr_time', 'get_goal']);
27
+ /**
28
+ * Describe a Codex "exec" code-mode script (`text(await tools.exec_command({cmd:"…"}))`)
29
+ * by its first tool call. Returns null for no-op helpers.
30
+ */
31
+ function describeExecScript(script, cwd) {
32
+ const m = script.match(/tools\.([A-Za-z0-9_]+)\s*\(/);
33
+ if (!m)
34
+ return 'Running code';
35
+ const tool = m[1];
36
+ if (QUIET_SCRIPT_TOOLS.has(tool))
37
+ return null;
38
+ switch (tool) {
39
+ case 'exec_command': {
40
+ const cmd = scriptString(script, 'cmd') ?? scriptString(script, 'command');
41
+ const short = cmd ? (0, activity_js_1.shortCommand)(cmd) : '';
42
+ return short ? `Running ${short}` : 'Running a command';
43
+ }
44
+ case 'apply_patch': {
45
+ // The patch is embedded as a JS string with escaped newlines.
46
+ return (0, activity_js_1.editingFiles)((0, activity_js_1.patchFiles)(script.replace(/\\n/g, '\n')), cwd);
47
+ }
48
+ case 'view_image':
49
+ return 'Viewing an image';
50
+ case 'update_plan':
51
+ return 'Planning';
52
+ case 'web__run':
53
+ case 'web_search':
54
+ return 'Browsing the web';
55
+ case 'image_gen__imagegen':
56
+ return 'Generating an image';
57
+ case 'spawn_agent':
58
+ return 'Delegating to a subagent';
59
+ default:
60
+ return (0, activity_js_1.describeToolUse)(tool.includes('__') ? `mcp__${tool}` : tool, {}, cwd);
61
+ }
62
+ }
63
+ function parseArgs(raw) {
64
+ if (isObj(raw))
65
+ return raw;
66
+ if (typeof raw === 'string') {
67
+ try {
68
+ const parsed = JSON.parse(raw);
69
+ if (isObj(parsed))
70
+ return parsed;
71
+ }
72
+ catch {
73
+ return { input: raw };
74
+ }
75
+ }
76
+ return {};
77
+ }
78
+ /** Describe a `function_call` / `custom_tool_call` response item. */
79
+ function describeCall(name, rawArgs, cwd) {
80
+ switch (name) {
81
+ case 'exec':
82
+ return typeof rawArgs === 'string' ? describeExecScript(rawArgs, cwd) : 'Running code';
83
+ case 'wait':
84
+ case 'write_stdin':
85
+ case 'list_agents':
86
+ return null;
87
+ case 'send_message':
88
+ case 'followup_task':
89
+ return 'Coordinating subagents';
90
+ case 'apply_patch': {
91
+ const args = parseArgs(rawArgs);
92
+ const patch = s(args['input']) ?? s(args['patch']) ?? (typeof rawArgs === 'string' ? rawArgs : '');
93
+ return (0, activity_js_1.editingFiles)((0, activity_js_1.patchFiles)(patch), cwd);
94
+ }
95
+ case 'shell':
96
+ case 'local_shell':
97
+ case 'exec_command':
98
+ case 'container.exec': {
99
+ const args = parseArgs(rawArgs);
100
+ return (0, activity_js_1.describeToolUse)('Bash', { command: args['command'] ?? args['cmd'] }, cwd);
101
+ }
102
+ default:
103
+ return (0, activity_js_1.describeToolUse)(name, parseArgs(rawArgs), cwd);
104
+ }
105
+ }
106
+ function approvalText(payload, cwd) {
107
+ const cmd = payload['command'];
108
+ if (cmd) {
109
+ const short = (0, activity_js_1.shortCommand)(cmd);
110
+ if (short)
111
+ return `Needs your approval: Running ${short}`;
112
+ }
113
+ const changes = payload['changes'];
114
+ if (isObj(changes)) {
115
+ return `Needs your approval: ${(0, activity_js_1.editingFiles)(Object.keys(changes).map(stripFileUrl), cwd)}`;
116
+ }
117
+ return 'Needs your approval';
118
+ }
119
+ function parseEventMsg(payload, cwd) {
120
+ const type = s(payload['type']) ?? '';
121
+ switch (type) {
122
+ case 'task_started':
123
+ case 'user_message':
124
+ return { kind: 'working', activity: activity_js_1.THINKING };
125
+ case 'task_complete':
126
+ return { kind: 'idle', activity: 'Done — waiting for next task' };
127
+ case 'turn_aborted':
128
+ return { kind: 'idle', activity: 'Interrupted — waiting for you' };
129
+ case 'exec_command_begin': {
130
+ const short = (0, activity_js_1.shortCommand)(payload['command']);
131
+ return { kind: 'working', activity: short ? `Running ${short}` : 'Running a command' };
132
+ }
133
+ case 'patch_apply_begin': {
134
+ const changes = isObj(payload['changes']) ? Object.keys(payload['changes']) : [];
135
+ return { kind: 'working', activity: (0, activity_js_1.editingFiles)(changes.map(stripFileUrl), cwd) };
136
+ }
137
+ case 'web_search_begin':
138
+ return { kind: 'working', activity: 'Searching the web' };
139
+ case 'request_user_input':
140
+ return { kind: 'waiting', activity: 'Has a question for you' };
141
+ case 'item_started': {
142
+ const item = isObj(payload['item']) ? payload['item'] : {};
143
+ const itemType = s(item['type']);
144
+ if (itemType === 'CommandExecution') {
145
+ const short = (0, activity_js_1.shortCommand)(item['command']);
146
+ return { kind: 'working', activity: short ? `Running ${short}` : 'Running a command' };
147
+ }
148
+ if (itemType === 'FileChange' && isObj(item['changes'])) {
149
+ return {
150
+ kind: 'working',
151
+ activity: (0, activity_js_1.editingFiles)(Object.keys(item['changes']).map(stripFileUrl), cwd),
152
+ };
153
+ }
154
+ return null;
155
+ }
156
+ case 'item_completed': {
157
+ // Newer builds log file edits only on completion; still the best signal.
158
+ const item = isObj(payload['item']) ? payload['item'] : {};
159
+ if (s(item['type']) === 'FileChange' && isObj(item['changes'])) {
160
+ return {
161
+ kind: 'working',
162
+ activity: (0, activity_js_1.editingFiles)(Object.keys(item['changes']).map(stripFileUrl), cwd),
163
+ };
164
+ }
165
+ return null;
166
+ }
167
+ default:
168
+ if (/approval_request$/.test(type) || type === 'request_permissions') {
169
+ return { kind: 'waiting', activity: approvalText(payload, cwd) };
170
+ }
171
+ return null;
172
+ }
173
+ }
174
+ function parseResponseItem(payload, cwd) {
175
+ const type = s(payload['type']) ?? '';
176
+ if (type === 'custom_tool_call' || type === 'function_call') {
177
+ const name = s(payload['name']) ?? '';
178
+ const activity = describeCall(name, payload['input'] ?? payload['arguments'], cwd);
179
+ return activity ? { kind: 'working', activity } : null;
180
+ }
181
+ if (type === 'local_shell_call') {
182
+ const action = isObj(payload['action']) ? payload['action'] : {};
183
+ const short = (0, activity_js_1.shortCommand)(action['command']);
184
+ return { kind: 'working', activity: short ? `Running ${short}` : 'Running a command' };
185
+ }
186
+ if (type === 'web_search_call')
187
+ return { kind: 'working', activity: 'Searching the web' };
188
+ return null;
189
+ }
190
+ /** Parse one rollout line into a signal (or null when it carries nothing useful). */
191
+ function parseRolloutLine(line, cwd) {
192
+ const trimmed = line.trim();
193
+ if (!trimmed || trimmed[0] !== '{')
194
+ return null;
195
+ let rec;
196
+ try {
197
+ const parsed = JSON.parse(trimmed);
198
+ if (!isObj(parsed))
199
+ return null;
200
+ rec = parsed;
201
+ }
202
+ catch {
203
+ return null;
204
+ }
205
+ const type = s(rec['type']) ?? '';
206
+ const payload = isObj(rec['payload']) ? rec['payload'] : {};
207
+ switch (type) {
208
+ case 'session_meta': {
209
+ const meta = isObj(payload['meta']) ? payload['meta'] : payload; // older nesting
210
+ const sessionId = s(meta['id']) ?? s(meta['session_id']);
211
+ if (!sessionId)
212
+ return null;
213
+ const source = meta['source'];
214
+ const threadSource = s(meta['thread_source']);
215
+ const subagent = (isObj(source) && 'subagent' in source) ||
216
+ threadSource === 'subagent' ||
217
+ threadSource === 'guardian_review';
218
+ const exec = source === 'exec' || s(meta['originator']) === 'codex_exec';
219
+ return { kind: 'meta', sessionId, cwd: s(meta['cwd']), subagent, exec };
220
+ }
221
+ case 'turn_context': {
222
+ const c = s(payload['cwd']);
223
+ return c ? { kind: 'cwd', cwd: c } : null;
224
+ }
225
+ case 'event_msg':
226
+ return parseEventMsg(payload, cwd);
227
+ case 'response_item':
228
+ return parseResponseItem(payload, cwd);
229
+ case 'function_call':
230
+ case 'custom_tool_call':
231
+ case 'local_shell_call':
232
+ case 'web_search_call':
233
+ // 2025-era rollouts wrote response items unwrapped.
234
+ return parseResponseItem(rec, cwd);
235
+ case '': {
236
+ // 2025-era first line: bare `{ id, timestamp, instructions }` session header.
237
+ const id = s(rec['id']);
238
+ return id && s(rec['timestamp'])
239
+ ? { kind: 'meta', sessionId: id, subagent: false, exec: false }
240
+ : null;
241
+ }
242
+ default:
243
+ return null;
244
+ }
245
+ }
246
+ //# sourceMappingURL=rollout.js.map