@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.
- package/README.md +243 -2
- package/dist/cjs/cli.js +533 -0
- package/dist/cjs/core/api-key.js +69 -0
- package/dist/cjs/core/base-npc.js +160 -0
- package/dist/cjs/core/config.js +108 -0
- package/dist/cjs/core/event-npc.js +14 -0
- package/dist/cjs/core/logger.js +39 -0
- package/dist/cjs/core/polling-npc.js +65 -0
- package/dist/cjs/core/screen.js +28 -0
- package/dist/cjs/core/text.js +15 -0
- package/dist/cjs/core/thresholds.js +48 -0
- package/dist/cjs/core/types.js +10 -0
- package/dist/cjs/core/user-config.js +92 -0
- package/dist/cjs/doctor.js +130 -0
- package/dist/cjs/index.js +90 -0
- package/dist/cjs/npcs/agent-common/activity.js +291 -0
- package/dist/cjs/npcs/agent-common/git-stats.js +71 -0
- package/dist/cjs/npcs/agent-common/hook-runner.js +252 -0
- package/dist/cjs/npcs/agent-common/hook-settings.js +279 -0
- package/dist/cjs/npcs/agent-common/liveness.js +112 -0
- package/dist/cjs/npcs/agent-common/lock.js +99 -0
- package/dist/cjs/npcs/agent-common/runtime.js +154 -0
- package/dist/cjs/npcs/agent-common/session.js +390 -0
- package/dist/cjs/npcs/agent-common/shim.js +96 -0
- package/dist/cjs/npcs/agent-common/watchdog.js +149 -0
- package/dist/cjs/npcs/claude-code/defaults.js +20 -0
- package/dist/cjs/npcs/claude-code/hook-handler.js +139 -0
- package/dist/cjs/npcs/claude-code/index.js +104 -0
- package/dist/cjs/npcs/claude-code/setup.js +133 -0
- package/dist/cjs/npcs/claude-code/state-machine.js +130 -0
- package/dist/cjs/npcs/codex/defaults.js +32 -0
- package/dist/cjs/npcs/codex/hook-handler.js +116 -0
- package/dist/cjs/npcs/codex/index.js +92 -0
- package/dist/cjs/npcs/codex/rollout.js +246 -0
- package/dist/cjs/npcs/codex/setup.js +104 -0
- package/dist/cjs/npcs/codex/watcher.js +346 -0
- package/dist/cjs/npcs/cursor/defaults.js +15 -0
- package/dist/cjs/npcs/cursor/hook-handler.js +65 -0
- package/dist/cjs/npcs/cursor/index.js +72 -0
- package/dist/cjs/npcs/cursor/normalize.js +54 -0
- package/dist/cjs/npcs/cursor/setup.js +98 -0
- package/dist/cjs/npcs/gemini/defaults.js +15 -0
- package/dist/cjs/npcs/gemini/hook-handler.js +93 -0
- package/dist/cjs/npcs/gemini/index.js +69 -0
- package/dist/cjs/npcs/gemini/setup.js +98 -0
- package/dist/cjs/npcs/github-watcher/defaults.js +11 -0
- package/dist/cjs/npcs/github-watcher/format.js +21 -0
- package/dist/cjs/npcs/github-watcher/github-api.js +126 -0
- package/dist/cjs/npcs/github-watcher/index.js +148 -0
- package/dist/cjs/npcs/internet-connectivity/checks.js +92 -0
- package/dist/cjs/npcs/internet-connectivity/defaults.js +22 -0
- package/dist/cjs/npcs/internet-connectivity/index.js +251 -0
- package/dist/cjs/npcs/log-watcher/buffer.js +40 -0
- package/dist/cjs/npcs/log-watcher/defaults.js +12 -0
- package/dist/cjs/npcs/log-watcher/index.js +161 -0
- package/dist/cjs/npcs/log-watcher/tail.js +81 -0
- package/dist/cjs/npcs/process-watcher/defaults.js +9 -0
- package/dist/cjs/npcs/process-watcher/index.js +140 -0
- package/dist/cjs/npcs/process-watcher/process-scanner.js +114 -0
- package/dist/cjs/npcs/receptionist/defaults.js +9 -0
- package/dist/cjs/npcs/receptionist/index.js +120 -0
- package/dist/cjs/npcs/receptionist/weather.js +129 -0
- package/dist/cjs/npcs/system-monitor/collectors.js +68 -0
- package/dist/cjs/npcs/system-monitor/defaults.js +13 -0
- package/dist/cjs/npcs/system-monitor/index.js +105 -0
- package/dist/cjs/npcs/web-page-watcher/basic-mode.js +58 -0
- package/dist/cjs/npcs/web-page-watcher/capture.js +80 -0
- package/dist/cjs/npcs/web-page-watcher/defaults.js +31 -0
- package/dist/cjs/npcs/web-page-watcher/index.js +234 -0
- package/dist/cjs/npcs/web-page-watcher/pixelate.js +181 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.js +498 -0
- package/dist/core/api-key.d.ts +34 -0
- package/dist/core/api-key.js +64 -0
- package/dist/core/base-npc.d.ts +41 -0
- package/dist/core/base-npc.js +156 -0
- package/dist/core/config.d.ts +22 -0
- package/dist/core/config.js +101 -0
- package/dist/core/event-npc.d.ts +8 -0
- package/dist/core/event-npc.js +10 -0
- package/dist/core/logger.d.ts +11 -0
- package/dist/core/logger.js +35 -0
- package/dist/core/polling-npc.d.ts +18 -0
- package/dist/core/polling-npc.js +61 -0
- package/dist/core/screen.d.ts +11 -0
- package/dist/core/screen.js +24 -0
- package/dist/core/text.d.ts +6 -0
- package/dist/core/text.js +12 -0
- package/dist/core/thresholds.d.ts +10 -0
- package/dist/core/thresholds.js +44 -0
- package/dist/core/types.d.ts +60 -0
- package/dist/core/types.js +7 -0
- package/dist/core/user-config.d.ts +43 -0
- package/dist/core/user-config.js +85 -0
- package/dist/doctor.d.ts +8 -0
- package/dist/doctor.js +126 -0
- package/dist/index.d.ts +39 -0
- package/dist/index.js +25 -0
- package/dist/npcs/agent-common/activity.d.ts +42 -0
- package/dist/npcs/agent-common/activity.js +280 -0
- package/dist/npcs/agent-common/git-stats.d.ts +11 -0
- package/dist/npcs/agent-common/git-stats.js +67 -0
- package/dist/npcs/agent-common/hook-runner.d.ts +62 -0
- package/dist/npcs/agent-common/hook-runner.js +245 -0
- package/dist/npcs/agent-common/hook-settings.d.ts +75 -0
- package/dist/npcs/agent-common/hook-settings.js +265 -0
- package/dist/npcs/agent-common/liveness.d.ts +12 -0
- package/dist/npcs/agent-common/liveness.js +105 -0
- package/dist/npcs/agent-common/lock.d.ts +6 -0
- package/dist/npcs/agent-common/lock.js +95 -0
- package/dist/npcs/agent-common/runtime.d.ts +16 -0
- package/dist/npcs/agent-common/runtime.js +149 -0
- package/dist/npcs/agent-common/session.d.ts +184 -0
- package/dist/npcs/agent-common/session.js +368 -0
- package/dist/npcs/agent-common/shim.d.ts +4 -0
- package/dist/npcs/agent-common/shim.js +90 -0
- package/dist/npcs/agent-common/watchdog.d.ts +16 -0
- package/dist/npcs/agent-common/watchdog.js +144 -0
- package/dist/npcs/claude-code/defaults.d.ts +15 -0
- package/dist/npcs/claude-code/defaults.js +17 -0
- package/dist/npcs/claude-code/hook-handler.d.ts +40 -0
- package/dist/npcs/claude-code/hook-handler.js +131 -0
- package/dist/npcs/claude-code/index.d.ts +36 -0
- package/dist/npcs/claude-code/index.js +92 -0
- package/dist/npcs/claude-code/setup.d.ts +58 -0
- package/dist/npcs/claude-code/setup.js +121 -0
- package/dist/npcs/claude-code/state-machine.d.ts +28 -0
- package/dist/npcs/claude-code/state-machine.js +125 -0
- package/dist/npcs/codex/defaults.d.ts +24 -0
- package/dist/npcs/codex/defaults.js +28 -0
- package/dist/npcs/codex/hook-handler.d.ts +24 -0
- package/dist/npcs/codex/hook-handler.js +110 -0
- package/dist/npcs/codex/index.d.ts +21 -0
- package/dist/npcs/codex/index.js +76 -0
- package/dist/npcs/codex/rollout.d.ts +39 -0
- package/dist/npcs/codex/rollout.js +242 -0
- package/dist/npcs/codex/setup.d.ts +37 -0
- package/dist/npcs/codex/setup.js +96 -0
- package/dist/npcs/codex/watcher.d.ts +62 -0
- package/dist/npcs/codex/watcher.js +341 -0
- package/dist/npcs/cursor/defaults.d.ts +9 -0
- package/dist/npcs/cursor/defaults.js +11 -0
- package/dist/npcs/cursor/hook-handler.d.ts +8 -0
- package/dist/npcs/cursor/hook-handler.js +60 -0
- package/dist/npcs/cursor/index.d.ts +18 -0
- package/dist/npcs/cursor/index.js +57 -0
- package/dist/npcs/cursor/normalize.d.ts +6 -0
- package/dist/npcs/cursor/normalize.js +49 -0
- package/dist/npcs/cursor/setup.d.ts +29 -0
- package/dist/npcs/cursor/setup.js +90 -0
- package/dist/npcs/gemini/defaults.d.ts +9 -0
- package/dist/npcs/gemini/defaults.js +11 -0
- package/dist/npcs/gemini/hook-handler.d.ts +10 -0
- package/dist/npcs/gemini/hook-handler.js +87 -0
- package/dist/npcs/gemini/index.d.ts +17 -0
- package/dist/npcs/gemini/index.js +56 -0
- package/dist/npcs/gemini/setup.d.ts +29 -0
- package/dist/npcs/gemini/setup.js +90 -0
- package/dist/npcs/github-watcher/defaults.d.ts +8 -0
- package/dist/npcs/github-watcher/defaults.js +8 -0
- package/dist/npcs/github-watcher/format.d.ts +3 -0
- package/dist/npcs/github-watcher/format.js +18 -0
- package/dist/npcs/github-watcher/github-api.d.ts +55 -0
- package/dist/npcs/github-watcher/github-api.js +120 -0
- package/dist/npcs/github-watcher/index.d.ts +35 -0
- package/dist/npcs/github-watcher/index.js +144 -0
- package/dist/npcs/internet-connectivity/checks.d.ts +24 -0
- package/dist/npcs/internet-connectivity/checks.js +84 -0
- package/dist/npcs/internet-connectivity/defaults.d.ts +20 -0
- package/dist/npcs/internet-connectivity/defaults.js +19 -0
- package/dist/npcs/internet-connectivity/index.d.ts +55 -0
- package/dist/npcs/internet-connectivity/index.js +246 -0
- package/dist/npcs/log-watcher/buffer.d.ts +18 -0
- package/dist/npcs/log-watcher/buffer.js +34 -0
- package/dist/npcs/log-watcher/defaults.d.ts +9 -0
- package/dist/npcs/log-watcher/defaults.js +9 -0
- package/dist/npcs/log-watcher/index.d.ts +45 -0
- package/dist/npcs/log-watcher/index.js +157 -0
- package/dist/npcs/log-watcher/tail.d.ts +9 -0
- package/dist/npcs/log-watcher/tail.js +78 -0
- package/dist/npcs/process-watcher/defaults.d.ts +6 -0
- package/dist/npcs/process-watcher/defaults.js +6 -0
- package/dist/npcs/process-watcher/index.d.ts +26 -0
- package/dist/npcs/process-watcher/index.js +136 -0
- package/dist/npcs/process-watcher/process-scanner.d.ts +19 -0
- package/dist/npcs/process-watcher/process-scanner.js +110 -0
- package/dist/npcs/receptionist/defaults.d.ts +6 -0
- package/dist/npcs/receptionist/defaults.js +6 -0
- package/dist/npcs/receptionist/index.d.ts +29 -0
- package/dist/npcs/receptionist/index.js +116 -0
- package/dist/npcs/receptionist/weather.d.ts +37 -0
- package/dist/npcs/receptionist/weather.js +121 -0
- package/dist/npcs/system-monitor/collectors.d.ts +45 -0
- package/dist/npcs/system-monitor/collectors.js +60 -0
- package/dist/npcs/system-monitor/defaults.d.ts +7 -0
- package/dist/npcs/system-monitor/defaults.js +10 -0
- package/dist/npcs/system-monitor/index.d.ts +27 -0
- package/dist/npcs/system-monitor/index.js +101 -0
- package/dist/npcs/web-page-watcher/basic-mode.d.ts +16 -0
- package/dist/npcs/web-page-watcher/basic-mode.js +52 -0
- package/dist/npcs/web-page-watcher/capture.d.ts +49 -0
- package/dist/npcs/web-page-watcher/capture.js +75 -0
- package/dist/npcs/web-page-watcher/defaults.d.ts +19 -0
- package/dist/npcs/web-page-watcher/defaults.js +27 -0
- package/dist/npcs/web-page-watcher/index.d.ts +42 -0
- package/dist/npcs/web-page-watcher/index.js +230 -0
- package/dist/npcs/web-page-watcher/pixelate.d.ts +36 -0
- package/dist/npcs/web-page-watcher/pixelate.js +172 -0
- package/package.json +73 -3
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
// Unsupported, best-effort fallback: Codex rollout logs are an internal format. Prefer hooks.
|
|
2
|
+
import { THINKING, describeToolUse, editingFiles, patchFiles, shortCommand, } from '../agent-common/activity.js';
|
|
3
|
+
const isObj = (v) => !!v && typeof v === 'object' && !Array.isArray(v);
|
|
4
|
+
const s = (v) => (typeof v === 'string' && v ? v : undefined);
|
|
5
|
+
function stripFileUrl(p) {
|
|
6
|
+
return p.replace(/^file:\/\//, '');
|
|
7
|
+
}
|
|
8
|
+
/** Pull a JS/JSON string property (`cmd:"…"` / `"cmd": "…"`) out of a script. */
|
|
9
|
+
function scriptString(script, key) {
|
|
10
|
+
const re = new RegExp(`["']?${key}["']?\\s*:\\s*"((?:[^"\\\\]|\\\\.)*)"`);
|
|
11
|
+
const m = script.match(re);
|
|
12
|
+
if (!m)
|
|
13
|
+
return undefined;
|
|
14
|
+
try {
|
|
15
|
+
return JSON.parse(`"${m[1]}"`);
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return m[1];
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
/** Tool helpers inside exec scripts that carry nothing worth showing. */
|
|
22
|
+
const QUIET_SCRIPT_TOOLS = new Set(['write_stdin', 'wait', 'clock__curr_time', 'get_goal']);
|
|
23
|
+
/**
|
|
24
|
+
* Describe a Codex "exec" code-mode script (`text(await tools.exec_command({cmd:"…"}))`)
|
|
25
|
+
* by its first tool call. Returns null for no-op helpers.
|
|
26
|
+
*/
|
|
27
|
+
export function describeExecScript(script, cwd) {
|
|
28
|
+
const m = script.match(/tools\.([A-Za-z0-9_]+)\s*\(/);
|
|
29
|
+
if (!m)
|
|
30
|
+
return 'Running code';
|
|
31
|
+
const tool = m[1];
|
|
32
|
+
if (QUIET_SCRIPT_TOOLS.has(tool))
|
|
33
|
+
return null;
|
|
34
|
+
switch (tool) {
|
|
35
|
+
case 'exec_command': {
|
|
36
|
+
const cmd = scriptString(script, 'cmd') ?? scriptString(script, 'command');
|
|
37
|
+
const short = cmd ? shortCommand(cmd) : '';
|
|
38
|
+
return short ? `Running ${short}` : 'Running a command';
|
|
39
|
+
}
|
|
40
|
+
case 'apply_patch': {
|
|
41
|
+
// The patch is embedded as a JS string with escaped newlines.
|
|
42
|
+
return editingFiles(patchFiles(script.replace(/\\n/g, '\n')), cwd);
|
|
43
|
+
}
|
|
44
|
+
case 'view_image':
|
|
45
|
+
return 'Viewing an image';
|
|
46
|
+
case 'update_plan':
|
|
47
|
+
return 'Planning';
|
|
48
|
+
case 'web__run':
|
|
49
|
+
case 'web_search':
|
|
50
|
+
return 'Browsing the web';
|
|
51
|
+
case 'image_gen__imagegen':
|
|
52
|
+
return 'Generating an image';
|
|
53
|
+
case 'spawn_agent':
|
|
54
|
+
return 'Delegating to a subagent';
|
|
55
|
+
default:
|
|
56
|
+
return describeToolUse(tool.includes('__') ? `mcp__${tool}` : tool, {}, cwd);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
function parseArgs(raw) {
|
|
60
|
+
if (isObj(raw))
|
|
61
|
+
return raw;
|
|
62
|
+
if (typeof raw === 'string') {
|
|
63
|
+
try {
|
|
64
|
+
const parsed = JSON.parse(raw);
|
|
65
|
+
if (isObj(parsed))
|
|
66
|
+
return parsed;
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
return { input: raw };
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return {};
|
|
73
|
+
}
|
|
74
|
+
/** Describe a `function_call` / `custom_tool_call` response item. */
|
|
75
|
+
function describeCall(name, rawArgs, cwd) {
|
|
76
|
+
switch (name) {
|
|
77
|
+
case 'exec':
|
|
78
|
+
return typeof rawArgs === 'string' ? describeExecScript(rawArgs, cwd) : 'Running code';
|
|
79
|
+
case 'wait':
|
|
80
|
+
case 'write_stdin':
|
|
81
|
+
case 'list_agents':
|
|
82
|
+
return null;
|
|
83
|
+
case 'send_message':
|
|
84
|
+
case 'followup_task':
|
|
85
|
+
return 'Coordinating subagents';
|
|
86
|
+
case 'apply_patch': {
|
|
87
|
+
const args = parseArgs(rawArgs);
|
|
88
|
+
const patch = s(args['input']) ?? s(args['patch']) ?? (typeof rawArgs === 'string' ? rawArgs : '');
|
|
89
|
+
return editingFiles(patchFiles(patch), cwd);
|
|
90
|
+
}
|
|
91
|
+
case 'shell':
|
|
92
|
+
case 'local_shell':
|
|
93
|
+
case 'exec_command':
|
|
94
|
+
case 'container.exec': {
|
|
95
|
+
const args = parseArgs(rawArgs);
|
|
96
|
+
return describeToolUse('Bash', { command: args['command'] ?? args['cmd'] }, cwd);
|
|
97
|
+
}
|
|
98
|
+
default:
|
|
99
|
+
return describeToolUse(name, parseArgs(rawArgs), cwd);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
function approvalText(payload, cwd) {
|
|
103
|
+
const cmd = payload['command'];
|
|
104
|
+
if (cmd) {
|
|
105
|
+
const short = shortCommand(cmd);
|
|
106
|
+
if (short)
|
|
107
|
+
return `Needs your approval: Running ${short}`;
|
|
108
|
+
}
|
|
109
|
+
const changes = payload['changes'];
|
|
110
|
+
if (isObj(changes)) {
|
|
111
|
+
return `Needs your approval: ${editingFiles(Object.keys(changes).map(stripFileUrl), cwd)}`;
|
|
112
|
+
}
|
|
113
|
+
return 'Needs your approval';
|
|
114
|
+
}
|
|
115
|
+
function parseEventMsg(payload, cwd) {
|
|
116
|
+
const type = s(payload['type']) ?? '';
|
|
117
|
+
switch (type) {
|
|
118
|
+
case 'task_started':
|
|
119
|
+
case 'user_message':
|
|
120
|
+
return { kind: 'working', activity: THINKING };
|
|
121
|
+
case 'task_complete':
|
|
122
|
+
return { kind: 'idle', activity: 'Done — waiting for next task' };
|
|
123
|
+
case 'turn_aborted':
|
|
124
|
+
return { kind: 'idle', activity: 'Interrupted — waiting for you' };
|
|
125
|
+
case 'exec_command_begin': {
|
|
126
|
+
const short = shortCommand(payload['command']);
|
|
127
|
+
return { kind: 'working', activity: short ? `Running ${short}` : 'Running a command' };
|
|
128
|
+
}
|
|
129
|
+
case 'patch_apply_begin': {
|
|
130
|
+
const changes = isObj(payload['changes']) ? Object.keys(payload['changes']) : [];
|
|
131
|
+
return { kind: 'working', activity: editingFiles(changes.map(stripFileUrl), cwd) };
|
|
132
|
+
}
|
|
133
|
+
case 'web_search_begin':
|
|
134
|
+
return { kind: 'working', activity: 'Searching the web' };
|
|
135
|
+
case 'request_user_input':
|
|
136
|
+
return { kind: 'waiting', activity: 'Has a question for you' };
|
|
137
|
+
case 'item_started': {
|
|
138
|
+
const item = isObj(payload['item']) ? payload['item'] : {};
|
|
139
|
+
const itemType = s(item['type']);
|
|
140
|
+
if (itemType === 'CommandExecution') {
|
|
141
|
+
const short = shortCommand(item['command']);
|
|
142
|
+
return { kind: 'working', activity: short ? `Running ${short}` : 'Running a command' };
|
|
143
|
+
}
|
|
144
|
+
if (itemType === 'FileChange' && isObj(item['changes'])) {
|
|
145
|
+
return {
|
|
146
|
+
kind: 'working',
|
|
147
|
+
activity: editingFiles(Object.keys(item['changes']).map(stripFileUrl), cwd),
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
return null;
|
|
151
|
+
}
|
|
152
|
+
case 'item_completed': {
|
|
153
|
+
// Newer builds log file edits only on completion; still the best signal.
|
|
154
|
+
const item = isObj(payload['item']) ? payload['item'] : {};
|
|
155
|
+
if (s(item['type']) === 'FileChange' && isObj(item['changes'])) {
|
|
156
|
+
return {
|
|
157
|
+
kind: 'working',
|
|
158
|
+
activity: editingFiles(Object.keys(item['changes']).map(stripFileUrl), cwd),
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
default:
|
|
164
|
+
if (/approval_request$/.test(type) || type === 'request_permissions') {
|
|
165
|
+
return { kind: 'waiting', activity: approvalText(payload, cwd) };
|
|
166
|
+
}
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
function parseResponseItem(payload, cwd) {
|
|
171
|
+
const type = s(payload['type']) ?? '';
|
|
172
|
+
if (type === 'custom_tool_call' || type === 'function_call') {
|
|
173
|
+
const name = s(payload['name']) ?? '';
|
|
174
|
+
const activity = describeCall(name, payload['input'] ?? payload['arguments'], cwd);
|
|
175
|
+
return activity ? { kind: 'working', activity } : null;
|
|
176
|
+
}
|
|
177
|
+
if (type === 'local_shell_call') {
|
|
178
|
+
const action = isObj(payload['action']) ? payload['action'] : {};
|
|
179
|
+
const short = shortCommand(action['command']);
|
|
180
|
+
return { kind: 'working', activity: short ? `Running ${short}` : 'Running a command' };
|
|
181
|
+
}
|
|
182
|
+
if (type === 'web_search_call')
|
|
183
|
+
return { kind: 'working', activity: 'Searching the web' };
|
|
184
|
+
return null;
|
|
185
|
+
}
|
|
186
|
+
/** Parse one rollout line into a signal (or null when it carries nothing useful). */
|
|
187
|
+
export function parseRolloutLine(line, cwd) {
|
|
188
|
+
const trimmed = line.trim();
|
|
189
|
+
if (!trimmed || trimmed[0] !== '{')
|
|
190
|
+
return null;
|
|
191
|
+
let rec;
|
|
192
|
+
try {
|
|
193
|
+
const parsed = JSON.parse(trimmed);
|
|
194
|
+
if (!isObj(parsed))
|
|
195
|
+
return null;
|
|
196
|
+
rec = parsed;
|
|
197
|
+
}
|
|
198
|
+
catch {
|
|
199
|
+
return null;
|
|
200
|
+
}
|
|
201
|
+
const type = s(rec['type']) ?? '';
|
|
202
|
+
const payload = isObj(rec['payload']) ? rec['payload'] : {};
|
|
203
|
+
switch (type) {
|
|
204
|
+
case 'session_meta': {
|
|
205
|
+
const meta = isObj(payload['meta']) ? payload['meta'] : payload; // older nesting
|
|
206
|
+
const sessionId = s(meta['id']) ?? s(meta['session_id']);
|
|
207
|
+
if (!sessionId)
|
|
208
|
+
return null;
|
|
209
|
+
const source = meta['source'];
|
|
210
|
+
const threadSource = s(meta['thread_source']);
|
|
211
|
+
const subagent = (isObj(source) && 'subagent' in source) ||
|
|
212
|
+
threadSource === 'subagent' ||
|
|
213
|
+
threadSource === 'guardian_review';
|
|
214
|
+
const exec = source === 'exec' || s(meta['originator']) === 'codex_exec';
|
|
215
|
+
return { kind: 'meta', sessionId, cwd: s(meta['cwd']), subagent, exec };
|
|
216
|
+
}
|
|
217
|
+
case 'turn_context': {
|
|
218
|
+
const c = s(payload['cwd']);
|
|
219
|
+
return c ? { kind: 'cwd', cwd: c } : null;
|
|
220
|
+
}
|
|
221
|
+
case 'event_msg':
|
|
222
|
+
return parseEventMsg(payload, cwd);
|
|
223
|
+
case 'response_item':
|
|
224
|
+
return parseResponseItem(payload, cwd);
|
|
225
|
+
case 'function_call':
|
|
226
|
+
case 'custom_tool_call':
|
|
227
|
+
case 'local_shell_call':
|
|
228
|
+
case 'web_search_call':
|
|
229
|
+
// 2025-era rollouts wrote response items unwrapped.
|
|
230
|
+
return parseResponseItem(rec, cwd);
|
|
231
|
+
case '': {
|
|
232
|
+
// 2025-era first line: bare `{ id, timestamp, instructions }` session header.
|
|
233
|
+
const id = s(rec['id']);
|
|
234
|
+
return id && s(rec['timestamp'])
|
|
235
|
+
? { kind: 'meta', sessionId: id, subagent: false, exec: false }
|
|
236
|
+
: null;
|
|
237
|
+
}
|
|
238
|
+
default:
|
|
239
|
+
return null;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
//# sourceMappingURL=rollout.js.map
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { HooksFile } from '../agent-common/hook-settings.js';
|
|
2
|
+
/**
|
|
3
|
+
* Codex hook events we install (developers.openai.com/codex/hooks). PostCompact
|
|
4
|
+
* is omitted — it carries nothing displayable.
|
|
5
|
+
*/
|
|
6
|
+
export declare const CODEX_HOOK_EVENTS: readonly ["SessionStart", "SessionEnd", "UserPromptSubmit", "PreToolUse", "PostToolUse", "PermissionRequest", "Stop", "Interrupt", "PreCompact", "SubagentStart", "SubagentStop"];
|
|
7
|
+
export declare function codexHooksPath(): string;
|
|
8
|
+
export interface CodexSetupOptions {
|
|
9
|
+
apiUrl?: string;
|
|
10
|
+
apiKey?: string;
|
|
11
|
+
map?: string;
|
|
12
|
+
cliPath?: string;
|
|
13
|
+
/** Override ~/.codex/hooks.json (tests). */
|
|
14
|
+
hooksPath?: string;
|
|
15
|
+
quiet?: boolean;
|
|
16
|
+
}
|
|
17
|
+
/** Install the nocville group into every Codex event. Mutates `file`. */
|
|
18
|
+
export declare function installCodexHooksInto(file: HooksFile, command: string, opts?: {
|
|
19
|
+
timeout?: number;
|
|
20
|
+
}): {
|
|
21
|
+
migrated: number;
|
|
22
|
+
installed: number;
|
|
23
|
+
};
|
|
24
|
+
export declare function buildCodexHookCommand(_cliPath: string): string;
|
|
25
|
+
/**
|
|
26
|
+
* Install Nocville hooks into `~/.codex/hooks.json` (same matcher-group JSON
|
|
27
|
+
* format as Claude Code). Merges with existing hooks, idempotent, keeps a
|
|
28
|
+
* one-time `.nocville-backup`. Codex requires new hooks to be reviewed and
|
|
29
|
+
* trusted once via `/hooks` before they run — we print that instruction.
|
|
30
|
+
*/
|
|
31
|
+
export declare function setupCodexHooks(opts?: CodexSetupOptions): boolean;
|
|
32
|
+
/** Remove all Nocville hooks from `~/.codex/hooks.json`. */
|
|
33
|
+
export declare function uninstallCodexHooks(opts?: {
|
|
34
|
+
hooksPath?: string;
|
|
35
|
+
quiet?: boolean;
|
|
36
|
+
}): number;
|
|
37
|
+
//# sourceMappingURL=setup.d.ts.map
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { maskKey, persistCredentials } from '../../core/user-config.js';
|
|
4
|
+
import { buildShimHookCommand, stableNodePath, installHookGroups, readJsonFile, removeHookGroups, resolveCliPath, writeJsonFile, } from '../agent-common/hook-settings.js';
|
|
5
|
+
import { installHookShim } from '../agent-common/shim.js';
|
|
6
|
+
import { ensureStableCliPath } from '../agent-common/runtime.js';
|
|
7
|
+
import { CODEX_DEFAULTS, codexHome } from './defaults.js';
|
|
8
|
+
/**
|
|
9
|
+
* Codex hook events we install (developers.openai.com/codex/hooks). PostCompact
|
|
10
|
+
* is omitted — it carries nothing displayable.
|
|
11
|
+
*/
|
|
12
|
+
export const CODEX_HOOK_EVENTS = [
|
|
13
|
+
'SessionStart',
|
|
14
|
+
'SessionEnd',
|
|
15
|
+
'UserPromptSubmit',
|
|
16
|
+
'PreToolUse',
|
|
17
|
+
'PostToolUse',
|
|
18
|
+
'PermissionRequest',
|
|
19
|
+
'Stop',
|
|
20
|
+
'Interrupt',
|
|
21
|
+
'PreCompact',
|
|
22
|
+
'SubagentStart',
|
|
23
|
+
'SubagentStop',
|
|
24
|
+
];
|
|
25
|
+
export function codexHooksPath() {
|
|
26
|
+
return join(codexHome(), 'hooks.json');
|
|
27
|
+
}
|
|
28
|
+
/** Install the nocville group into every Codex event. Mutates `file`. */
|
|
29
|
+
export function installCodexHooksInto(file, command, opts = {}) {
|
|
30
|
+
return installHookGroups(file, command, CODEX_HOOK_EVENTS, {
|
|
31
|
+
perEvent: Object.fromEntries(CODEX_HOOK_EVENTS.map((event) => [
|
|
32
|
+
event,
|
|
33
|
+
event === 'SessionEnd' || event === 'Interrupt'
|
|
34
|
+
? { timeout: 3 }
|
|
35
|
+
: { async: true, timeout: opts.timeout ?? 10 },
|
|
36
|
+
])),
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
export function buildCodexHookCommand(_cliPath) {
|
|
40
|
+
return buildShimHookCommand('codex');
|
|
41
|
+
}
|
|
42
|
+
function out(opts, msg) {
|
|
43
|
+
if (!opts.quiet)
|
|
44
|
+
console.log(msg);
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Install Nocville hooks into `~/.codex/hooks.json` (same matcher-group JSON
|
|
48
|
+
* format as Claude Code). Merges with existing hooks, idempotent, keeps a
|
|
49
|
+
* one-time `.nocville-backup`. Codex requires new hooks to be reviewed and
|
|
50
|
+
* trusted once via `/hooks` before they run — we print that instruction.
|
|
51
|
+
*/
|
|
52
|
+
export function setupCodexHooks(opts = {}) {
|
|
53
|
+
const hooksPath = opts.hooksPath ?? codexHooksPath();
|
|
54
|
+
const dir = join(hooksPath, '..');
|
|
55
|
+
if (!existsSync(dir)) {
|
|
56
|
+
console.error(`${dir} does not exist — is Codex installed? Skipping Codex setup.`);
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
const file = readJsonFile(hooksPath);
|
|
60
|
+
if (file === null) {
|
|
61
|
+
console.error(`Could not parse ${hooksPath}; fix it (or move it aside) and re-run setup.`);
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
const creds = persistCredentials(opts);
|
|
65
|
+
const resolved = resolveCliPath(opts.cliPath);
|
|
66
|
+
const cliPath = ensureStableCliPath(resolved);
|
|
67
|
+
installHookShim(stableNodePath(), cliPath);
|
|
68
|
+
const { installed } = installCodexHooksInto(file, buildCodexHookCommand(cliPath), {
|
|
69
|
+
timeout: CODEX_DEFAULTS.hookTimeoutSeconds,
|
|
70
|
+
});
|
|
71
|
+
writeJsonFile(hooksPath, file);
|
|
72
|
+
out(opts, `✓ Codex: installed Nocville hooks for ${installed} events in ${hooksPath}`);
|
|
73
|
+
out(opts, ` Hook runs: ${process.execPath} ${cliPath}`);
|
|
74
|
+
out(opts, ` API: ${creds.apiUrl ?? 'https://nocville.com'} key: ${maskKey(creds.apiKey)}`);
|
|
75
|
+
if (!creds.apiKey) {
|
|
76
|
+
out(opts, ' ⚠ No API key saved yet — run: nocville-npc init --api-key=<your team key>');
|
|
77
|
+
}
|
|
78
|
+
out(opts, ' ➜ One-time step: start Codex and run /hooks to review and trust the Nocville');
|
|
79
|
+
out(opts, ' hooks (Codex skips new hooks until trusted).');
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
82
|
+
/** Remove all Nocville hooks from `~/.codex/hooks.json`. */
|
|
83
|
+
export function uninstallCodexHooks(opts = {}) {
|
|
84
|
+
const hooksPath = opts.hooksPath ?? codexHooksPath();
|
|
85
|
+
const file = readJsonFile(hooksPath);
|
|
86
|
+
if (!file || !existsSync(hooksPath)) {
|
|
87
|
+
out(opts, 'Codex: no hooks.json — nothing to remove.');
|
|
88
|
+
return 0;
|
|
89
|
+
}
|
|
90
|
+
const removed = removeHookGroups(file);
|
|
91
|
+
if (removed > 0)
|
|
92
|
+
writeJsonFile(hooksPath, file);
|
|
93
|
+
out(opts, `✓ Codex: removed ${removed} Nocville hook(s) from ${hooksPath}`);
|
|
94
|
+
return removed;
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=setup.js.map
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { StatusUpdate } from '@nocville/node';
|
|
2
|
+
import type { NpcConfig } from '../../core/types.js';
|
|
3
|
+
import type { AgentSessionState } from '../agent-common/session.js';
|
|
4
|
+
/**
|
|
5
|
+
* `nocville-npc codex watch` — zero-config Codex integration that tails the
|
|
6
|
+
* session rollout logs Codex writes under ~/.codex/sessions/YYYY/MM/DD/.
|
|
7
|
+
*
|
|
8
|
+
* One NPC per live interactive session (subagent and guardian threads are
|
|
9
|
+
* skipped; `codex exec` runs only with --include-exec). Phase + activity are
|
|
10
|
+
* derived from the log; a session whose log stops growing for `idleTimeoutMs`
|
|
11
|
+
* despawns. Sessions whose hooks are active (any hook state file exists)
|
|
12
|
+
* are left to the hooks so nothing is pushed twice. Ctrl-C despawns all.
|
|
13
|
+
*/
|
|
14
|
+
export interface CodexWatcherOptions {
|
|
15
|
+
sessionsDir?: string;
|
|
16
|
+
pollMs?: number;
|
|
17
|
+
idleTimeoutMs?: number;
|
|
18
|
+
includeExec?: boolean;
|
|
19
|
+
minPushGapMs?: number;
|
|
20
|
+
/** Push override (tests). */
|
|
21
|
+
push?: (serviceId: string, payload: StatusUpdate) => Promise<{
|
|
22
|
+
success: boolean;
|
|
23
|
+
error?: string;
|
|
24
|
+
}>;
|
|
25
|
+
now?: () => number;
|
|
26
|
+
/** Hook state dir consulted for hook ownership (tests). */
|
|
27
|
+
hookStateDir?: string;
|
|
28
|
+
}
|
|
29
|
+
/** Day directories to scan: today and yesterday (local time, like Codex). */
|
|
30
|
+
export declare function recentDayDirs(root: string, now: number): string[];
|
|
31
|
+
export declare class CodexWatcher {
|
|
32
|
+
private readonly config;
|
|
33
|
+
private readonly opts;
|
|
34
|
+
private readonly sessionsDir;
|
|
35
|
+
private readonly pollMs;
|
|
36
|
+
private readonly idleTimeoutMs;
|
|
37
|
+
private readonly includeExec;
|
|
38
|
+
private readonly minPushGapMs;
|
|
39
|
+
private readonly hookStateDir;
|
|
40
|
+
private readonly now;
|
|
41
|
+
private readonly logger;
|
|
42
|
+
private readonly sessions;
|
|
43
|
+
private interval?;
|
|
44
|
+
private clients;
|
|
45
|
+
constructor(config: NpcConfig, opts?: CodexWatcherOptions);
|
|
46
|
+
/** Active (non-ignored) sessions — exposed for tests/diagnostics. */
|
|
47
|
+
activeSessions(): AgentSessionState[];
|
|
48
|
+
start(): Promise<void>;
|
|
49
|
+
/** Stop polling and despawn every live session. */
|
|
50
|
+
stop(): Promise<void>;
|
|
51
|
+
/** One poll: discover files, read appended lines, push, expire quiet sessions. */
|
|
52
|
+
tick(): Promise<void>;
|
|
53
|
+
private discover;
|
|
54
|
+
private readAppended;
|
|
55
|
+
/** Reduce one signal into the session state. Exposed via tick for tests. */
|
|
56
|
+
private apply;
|
|
57
|
+
private hooksOwn;
|
|
58
|
+
private maybePush;
|
|
59
|
+
private despawn;
|
|
60
|
+
private send;
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=watcher.d.ts.map
|