@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,28 @@
|
|
|
1
|
+
import type { AgentHookEvent } from '../agent-common/hook-runner.js';
|
|
2
|
+
import type { AgentPhase, AgentSessionState, AgentTransition, StatusOutput } from '../agent-common/session.js';
|
|
3
|
+
export type SessionMachineState = AgentPhase;
|
|
4
|
+
export type SessionState = AgentSessionState;
|
|
5
|
+
export type { StatusOutput };
|
|
6
|
+
export interface HookEvent extends AgentHookEvent {
|
|
7
|
+
/** User-facing session name — present in the SessionStart payload only. */
|
|
8
|
+
session_title?: string;
|
|
9
|
+
/** Notification events: permission_prompt | idle_prompt | … */
|
|
10
|
+
notification_type?: string;
|
|
11
|
+
}
|
|
12
|
+
/** Resolve the event name from either the real or legacy field. */
|
|
13
|
+
export declare function eventName(event: HookEvent): string;
|
|
14
|
+
/**
|
|
15
|
+
* Map one Claude Code hook event to a phase + activity line.
|
|
16
|
+
* Event names / payload fields per code.claude.com/docs/en/hooks.
|
|
17
|
+
* Prompt text is never read (privacy); tool inputs are summarized, not echoed.
|
|
18
|
+
*/
|
|
19
|
+
export declare function claudeTransition(event: HookEvent, prev: AgentSessionState | null): AgentTransition;
|
|
20
|
+
/**
|
|
21
|
+
* Derive the NPC status output from a hook event (state + presentation).
|
|
22
|
+
* Kept for library consumers; the hook runner uses `claudeTransition` directly.
|
|
23
|
+
*/
|
|
24
|
+
export declare function processHookEvent(event: HookEvent, currentState: SessionState | null): {
|
|
25
|
+
state: SessionMachineState;
|
|
26
|
+
output: StatusOutput;
|
|
27
|
+
};
|
|
28
|
+
//# sourceMappingURL=state-machine.d.ts.map
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { THINKING, describeToolUse, failureText, isQuestionTool, } from '../agent-common/activity.js';
|
|
2
|
+
import { hookEventName } from '../agent-common/hook-runner.js';
|
|
3
|
+
import { phasePresentation } from '../agent-common/session.js';
|
|
4
|
+
/** Resolve the event name from either the real or legacy field. */
|
|
5
|
+
export function eventName(event) {
|
|
6
|
+
return hookEventName(event);
|
|
7
|
+
}
|
|
8
|
+
const working = (activity, warn = false) => ({
|
|
9
|
+
state: 'WORKING',
|
|
10
|
+
activity,
|
|
11
|
+
status: warn ? 'warning' : 'healthy',
|
|
12
|
+
mood: warn ? 'concerned' : 'happy',
|
|
13
|
+
});
|
|
14
|
+
const waiting = (activity) => ({
|
|
15
|
+
state: 'WAITING',
|
|
16
|
+
activity,
|
|
17
|
+
status: 'warning',
|
|
18
|
+
mood: 'concerned',
|
|
19
|
+
});
|
|
20
|
+
const idle = (activity) => ({
|
|
21
|
+
state: 'IDLE',
|
|
22
|
+
activity,
|
|
23
|
+
status: 'healthy',
|
|
24
|
+
mood: 'idle',
|
|
25
|
+
});
|
|
26
|
+
/** Notification types that mean "the human needs to act". */
|
|
27
|
+
const WAITING_NOTIFICATIONS = new Set([
|
|
28
|
+
'permission_prompt',
|
|
29
|
+
'elicitation_dialog',
|
|
30
|
+
'elicitation_url_dialog',
|
|
31
|
+
'agent_needs_input',
|
|
32
|
+
]);
|
|
33
|
+
/**
|
|
34
|
+
* Map one Claude Code hook event to a phase + activity line.
|
|
35
|
+
* Event names / payload fields per code.claude.com/docs/en/hooks.
|
|
36
|
+
* Prompt text is never read (privacy); tool inputs are summarized, not echoed.
|
|
37
|
+
*/
|
|
38
|
+
export function claudeTransition(event, prev) {
|
|
39
|
+
const name = eventName(event);
|
|
40
|
+
const cwd = (typeof event.cwd === 'string' && event.cwd) || prev?.projectDir;
|
|
41
|
+
const keep = prev?.state ?? 'IDLE';
|
|
42
|
+
switch (name) {
|
|
43
|
+
case 'SessionStart':
|
|
44
|
+
return idle('Ready for a task');
|
|
45
|
+
case 'UserPromptSubmit':
|
|
46
|
+
return working(THINKING);
|
|
47
|
+
case 'PreToolUse': {
|
|
48
|
+
const activity = describeToolUse(event.tool_name, event.tool_input, cwd);
|
|
49
|
+
if (isQuestionTool(event.tool_name))
|
|
50
|
+
return waiting(activity);
|
|
51
|
+
return working(activity);
|
|
52
|
+
}
|
|
53
|
+
case 'PostToolUse':
|
|
54
|
+
case 'PostToolBatch':
|
|
55
|
+
// Tool finished — still working; keep showing the same activity.
|
|
56
|
+
return { ...working(undefined), quiet: prev?.state === 'WORKING' };
|
|
57
|
+
case 'PostToolUseFailure':
|
|
58
|
+
return working(failureText(event.tool_name), true);
|
|
59
|
+
case 'PermissionRequest': {
|
|
60
|
+
const what = event.tool_name ? describeToolUse(event.tool_name, event.tool_input, cwd) : '';
|
|
61
|
+
return waiting(what ? `Needs your approval: ${what}` : 'Needs your approval');
|
|
62
|
+
}
|
|
63
|
+
case 'Notification': {
|
|
64
|
+
const type = typeof event.notification_type === 'string' ? event.notification_type : '';
|
|
65
|
+
if (type === 'idle_prompt')
|
|
66
|
+
return idle('Done — waiting for next task');
|
|
67
|
+
if (type === 'permission_prompt')
|
|
68
|
+
return waiting('Needs your approval');
|
|
69
|
+
if (WAITING_NOTIFICATIONS.has(type) || !type)
|
|
70
|
+
return waiting('Waiting for you');
|
|
71
|
+
// auth_success, agent_completed, quota_* … → informational only.
|
|
72
|
+
return {
|
|
73
|
+
state: keep,
|
|
74
|
+
status: prev?.status ?? 'healthy',
|
|
75
|
+
mood: prev?.mood ?? 'idle',
|
|
76
|
+
quiet: true,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
case 'Stop':
|
|
80
|
+
return idle('Done — waiting for next task');
|
|
81
|
+
case 'StopFailure':
|
|
82
|
+
return { ...idle('Stopped with an error'), status: 'warning', mood: 'concerned' };
|
|
83
|
+
case 'SubagentStart':
|
|
84
|
+
return working('Delegating to a subagent');
|
|
85
|
+
case 'SubagentStop':
|
|
86
|
+
return working('Subagent finished');
|
|
87
|
+
case 'PreCompact':
|
|
88
|
+
return working('Compacting context');
|
|
89
|
+
case 'SessionEnd':
|
|
90
|
+
return {
|
|
91
|
+
state: 'IDLE',
|
|
92
|
+
activity: 'Session ended',
|
|
93
|
+
status: 'unknown',
|
|
94
|
+
mood: 'idle',
|
|
95
|
+
offline: true,
|
|
96
|
+
};
|
|
97
|
+
default:
|
|
98
|
+
return {
|
|
99
|
+
state: keep,
|
|
100
|
+
status: prev?.status ?? 'healthy',
|
|
101
|
+
mood: prev?.mood ?? 'idle',
|
|
102
|
+
quiet: true,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Derive the NPC status output from a hook event (state + presentation).
|
|
108
|
+
* Kept for library consumers; the hook runner uses `claudeTransition` directly.
|
|
109
|
+
*/
|
|
110
|
+
export function processHookEvent(event, currentState) {
|
|
111
|
+
const t = claudeTransition(event, currentState);
|
|
112
|
+
const { subStatus, behavior } = phasePresentation(t.state, t.offline);
|
|
113
|
+
return {
|
|
114
|
+
state: t.state,
|
|
115
|
+
output: {
|
|
116
|
+
status: t.status,
|
|
117
|
+
mood: t.mood,
|
|
118
|
+
message: t.activity ?? currentState?.activity?.[0] ?? '',
|
|
119
|
+
subStatus,
|
|
120
|
+
behavior,
|
|
121
|
+
offline: t.offline,
|
|
122
|
+
},
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
//# sourceMappingURL=state-machine.js.map
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export declare const CODEX_DEFAULTS: {
|
|
2
|
+
/** Hook-mode per-session state (shared with the watcher to avoid double pushes). */
|
|
3
|
+
stateDir: string;
|
|
4
|
+
stateFileMaxAge: number;
|
|
5
|
+
/** Watchdog-backed sessions become unknown after three minutes without a heartbeat. */
|
|
6
|
+
heartbeatSeconds: number;
|
|
7
|
+
minPushGapMs: number;
|
|
8
|
+
hookTimeoutSeconds: number;
|
|
9
|
+
/** Watcher: Codex session rollout logs. */
|
|
10
|
+
sessionsDir: string;
|
|
11
|
+
/** Watcher: poll interval for new sessions / appended lines. */
|
|
12
|
+
pollMs: number;
|
|
13
|
+
/**
|
|
14
|
+
* Watcher: a session whose rollout file hasn't grown for this long is treated
|
|
15
|
+
* as ended (offline push). Matches Codex's own 30-minute idle SessionEnd.
|
|
16
|
+
*/
|
|
17
|
+
idleTimeoutMs: number;
|
|
18
|
+
/** Watcher: bytes read from the end of an already-active rollout on discovery. */
|
|
19
|
+
initialTailBytes: number;
|
|
20
|
+
/** Watcher: max bytes read per tick per session before skipping ahead to the tail. */
|
|
21
|
+
maxReadBytes: number;
|
|
22
|
+
};
|
|
23
|
+
export declare function codexHome(): string;
|
|
24
|
+
//# sourceMappingURL=defaults.d.ts.map
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { homedir, tmpdir } from 'node:os';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
export const CODEX_DEFAULTS = {
|
|
4
|
+
/** Hook-mode per-session state (shared with the watcher to avoid double pushes). */
|
|
5
|
+
stateDir: join(tmpdir(), 'nocville-codex'),
|
|
6
|
+
stateFileMaxAge: 24 * 60 * 60 * 1000,
|
|
7
|
+
/** Watchdog-backed sessions become unknown after three minutes without a heartbeat. */
|
|
8
|
+
heartbeatSeconds: 90,
|
|
9
|
+
minPushGapMs: 1500,
|
|
10
|
+
hookTimeoutSeconds: 10,
|
|
11
|
+
/** Watcher: Codex session rollout logs. */
|
|
12
|
+
sessionsDir: join(process.env['CODEX_HOME'] ?? join(homedir(), '.codex'), 'sessions'),
|
|
13
|
+
/** Watcher: poll interval for new sessions / appended lines. */
|
|
14
|
+
pollMs: 1000,
|
|
15
|
+
/**
|
|
16
|
+
* Watcher: a session whose rollout file hasn't grown for this long is treated
|
|
17
|
+
* as ended (offline push). Matches Codex's own 30-minute idle SessionEnd.
|
|
18
|
+
*/
|
|
19
|
+
idleTimeoutMs: 30 * 60 * 1000,
|
|
20
|
+
/** Watcher: bytes read from the end of an already-active rollout on discovery. */
|
|
21
|
+
initialTailBytes: 256 * 1024,
|
|
22
|
+
/** Watcher: max bytes read per tick per session before skipping ahead to the tail. */
|
|
23
|
+
maxReadBytes: 8 * 1024 * 1024,
|
|
24
|
+
};
|
|
25
|
+
export function codexHome() {
|
|
26
|
+
return process.env['CODEX_HOME'] ?? join(homedir(), '.codex');
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=defaults.js.map
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { NpcConfig } from '../../core/types.js';
|
|
2
|
+
import type { AgentHookEvent, AgentHookSpec, HookRunOptions } from '../agent-common/hook-runner.js';
|
|
3
|
+
import type { AgentAdapter, AgentSessionState, AgentTransition } from '../agent-common/session.js';
|
|
4
|
+
export declare const CODEX_ADAPTER: AgentAdapter;
|
|
5
|
+
/**
|
|
6
|
+
* Codex session ids are UUIDv7 (time-ordered): the leading chars are shared by
|
|
7
|
+
* sessions started close together, so key the serviceId on the random tail.
|
|
8
|
+
*/
|
|
9
|
+
export declare function codexServiceId(sessionId: string, cwd: string): string;
|
|
10
|
+
/**
|
|
11
|
+
* Map one Codex hook event to a phase + activity line. Events and payload
|
|
12
|
+
* fields per the Codex hooks reference (developers.openai.com/codex/hooks):
|
|
13
|
+
* SessionStart, SessionEnd, UserPromptSubmit, PreToolUse, PostToolUse,
|
|
14
|
+
* PermissionRequest, Stop, Interrupt, PreCompact, PostCompact, SubagentStart,
|
|
15
|
+
* SubagentStop. Bash/apply_patch carry `tool_input.command`.
|
|
16
|
+
*/
|
|
17
|
+
export declare function codexTransition(event: AgentHookEvent, prev: AgentSessionState | null): AgentTransition;
|
|
18
|
+
export declare const CODEX_HOOK_SPEC: AgentHookSpec;
|
|
19
|
+
/**
|
|
20
|
+
* Handle a single Codex hook event (stdin JSON unless `rawInput` is given).
|
|
21
|
+
* In-process push by default; the CLI passes `mode: 'detach'`. Never throws.
|
|
22
|
+
*/
|
|
23
|
+
export declare function handleCodexHookEvent(config: NpcConfig, rawInput?: string, opts?: HookRunOptions): Promise<void>;
|
|
24
|
+
//# sourceMappingURL=hook-handler.d.ts.map
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { THINKING, describeToolUse, failureText, isQuestionTool, } from '../agent-common/activity.js';
|
|
2
|
+
import { hookEventName, readStdin, runAgentHook } from '../agent-common/hook-runner.js';
|
|
3
|
+
import { projectNameOf, sanitizeServiceId } from '../agent-common/session.js';
|
|
4
|
+
import { CODEX_DEFAULTS } from './defaults.js';
|
|
5
|
+
export const CODEX_ADAPTER = {
|
|
6
|
+
command: 'codex',
|
|
7
|
+
label: 'Codex',
|
|
8
|
+
family: 'codex',
|
|
9
|
+
stateDir: CODEX_DEFAULTS.stateDir,
|
|
10
|
+
heartbeatSeconds: CODEX_DEFAULTS.heartbeatSeconds,
|
|
11
|
+
processNames: ['codex'],
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Codex session ids are UUIDv7 (time-ordered): the leading chars are shared by
|
|
15
|
+
* sessions started close together, so key the serviceId on the random tail.
|
|
16
|
+
*/
|
|
17
|
+
export function codexServiceId(sessionId, cwd) {
|
|
18
|
+
const hex = sessionId.replace(/[^A-Za-z0-9]/g, '');
|
|
19
|
+
return sanitizeServiceId(`codex-${projectNameOf(cwd)}-${hex.slice(-8) || 'session'}`);
|
|
20
|
+
}
|
|
21
|
+
const working = (activity, warn = false) => ({
|
|
22
|
+
state: 'WORKING',
|
|
23
|
+
activity,
|
|
24
|
+
status: warn ? 'warning' : 'healthy',
|
|
25
|
+
mood: warn ? 'concerned' : 'happy',
|
|
26
|
+
});
|
|
27
|
+
const waiting = (activity) => ({
|
|
28
|
+
state: 'WAITING',
|
|
29
|
+
activity,
|
|
30
|
+
status: 'warning',
|
|
31
|
+
mood: 'concerned',
|
|
32
|
+
});
|
|
33
|
+
const idle = (activity) => ({
|
|
34
|
+
state: 'IDLE',
|
|
35
|
+
activity,
|
|
36
|
+
status: 'healthy',
|
|
37
|
+
mood: 'idle',
|
|
38
|
+
});
|
|
39
|
+
/**
|
|
40
|
+
* Map one Codex hook event to a phase + activity line. Events and payload
|
|
41
|
+
* fields per the Codex hooks reference (developers.openai.com/codex/hooks):
|
|
42
|
+
* SessionStart, SessionEnd, UserPromptSubmit, PreToolUse, PostToolUse,
|
|
43
|
+
* PermissionRequest, Stop, Interrupt, PreCompact, PostCompact, SubagentStart,
|
|
44
|
+
* SubagentStop. Bash/apply_patch carry `tool_input.command`.
|
|
45
|
+
*/
|
|
46
|
+
export function codexTransition(event, prev) {
|
|
47
|
+
const name = hookEventName(event);
|
|
48
|
+
const cwd = (typeof event.cwd === 'string' && event.cwd) || prev?.projectDir;
|
|
49
|
+
const keep = prev?.state ?? 'IDLE';
|
|
50
|
+
switch (name) {
|
|
51
|
+
case 'SessionStart':
|
|
52
|
+
return idle('Ready for a task');
|
|
53
|
+
case 'UserPromptSubmit':
|
|
54
|
+
return working(THINKING);
|
|
55
|
+
case 'PreToolUse': {
|
|
56
|
+
const activity = describeToolUse(event.tool_name, event.tool_input, cwd);
|
|
57
|
+
return isQuestionTool(event.tool_name) ? waiting(activity) : working(activity);
|
|
58
|
+
}
|
|
59
|
+
case 'PostToolUse':
|
|
60
|
+
case 'PostCompact':
|
|
61
|
+
return { ...working(undefined), quiet: prev?.state === 'WORKING' };
|
|
62
|
+
case 'PostToolUseFailure':
|
|
63
|
+
return working(failureText(event.tool_name), true);
|
|
64
|
+
case 'PermissionRequest': {
|
|
65
|
+
const what = event.tool_name ? describeToolUse(event.tool_name, event.tool_input, cwd) : '';
|
|
66
|
+
return waiting(what ? `Needs your approval: ${what}` : 'Needs your approval');
|
|
67
|
+
}
|
|
68
|
+
case 'Stop':
|
|
69
|
+
return idle('Done — waiting for next task');
|
|
70
|
+
case 'Interrupt':
|
|
71
|
+
return idle('Interrupted — waiting for you');
|
|
72
|
+
case 'PreCompact':
|
|
73
|
+
return working('Compacting context');
|
|
74
|
+
case 'SubagentStart':
|
|
75
|
+
return working('Delegating to a subagent');
|
|
76
|
+
case 'SubagentStop':
|
|
77
|
+
return working('Subagent finished');
|
|
78
|
+
case 'SessionEnd':
|
|
79
|
+
return {
|
|
80
|
+
state: 'IDLE',
|
|
81
|
+
activity: 'Session ended',
|
|
82
|
+
status: 'unknown',
|
|
83
|
+
mood: 'idle',
|
|
84
|
+
offline: true,
|
|
85
|
+
};
|
|
86
|
+
default:
|
|
87
|
+
return {
|
|
88
|
+
state: keep,
|
|
89
|
+
status: prev?.status ?? 'healthy',
|
|
90
|
+
mood: prev?.mood ?? 'idle',
|
|
91
|
+
quiet: true,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
export const CODEX_HOOK_SPEC = {
|
|
96
|
+
adapter: CODEX_ADAPTER,
|
|
97
|
+
serviceIdFor: codexServiceId,
|
|
98
|
+
transition: codexTransition,
|
|
99
|
+
};
|
|
100
|
+
/**
|
|
101
|
+
* Handle a single Codex hook event (stdin JSON unless `rawInput` is given).
|
|
102
|
+
* In-process push by default; the CLI passes `mode: 'detach'`. Never throws.
|
|
103
|
+
*/
|
|
104
|
+
export async function handleCodexHookEvent(config, rawInput, opts = {}) {
|
|
105
|
+
await runAgentHook(CODEX_HOOK_SPEC, config, rawInput ?? readStdin(), {
|
|
106
|
+
minPushGapMs: CODEX_DEFAULTS.minPushGapMs,
|
|
107
|
+
...opts,
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=hook-handler.js.map
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { NpcConfig } from '../../core/types.js';
|
|
2
|
+
export { CODEX_DEFAULTS } from './defaults.js';
|
|
3
|
+
export { CODEX_ADAPTER, codexTransition, codexServiceId, handleCodexHookEvent, } from './hook-handler.js';
|
|
4
|
+
export { setupCodexHooks, uninstallCodexHooks, installCodexHooksInto, CODEX_HOOK_EVENTS, } from './setup.js';
|
|
5
|
+
export type { CodexSetupOptions } from './setup.js';
|
|
6
|
+
export { parseRolloutLine, describeExecScript } from './rollout.js';
|
|
7
|
+
export type { RolloutSignal } from './rollout.js';
|
|
8
|
+
export { CodexWatcher } from './watcher.js';
|
|
9
|
+
export type { CodexWatcherOptions } from './watcher.js';
|
|
10
|
+
export interface CodexSubcommandOptions {
|
|
11
|
+
subcommand: string;
|
|
12
|
+
config: NpcConfig;
|
|
13
|
+
flags: Record<string, string | boolean>;
|
|
14
|
+
args?: string[];
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* codex subcommands: setup | uninstall | hook | flush | watch | cleanup.
|
|
18
|
+
* `hook`/`flush` are invoked by Codex / ourselves and stay silent on stdout.
|
|
19
|
+
*/
|
|
20
|
+
export declare function handleCodexCommand(opts: CodexSubcommandOptions): Promise<void>;
|
|
21
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { boolFlag, numberFlag, stringFlag } from '../../core/config.js';
|
|
2
|
+
import { readStdin, runFlushCommand } from '../agent-common/hook-runner.js';
|
|
3
|
+
import { runWatchdog } from '../agent-common/watchdog.js';
|
|
4
|
+
import { cleanupSessionStates } from '../agent-common/session.js';
|
|
5
|
+
import { CODEX_DEFAULTS } from './defaults.js';
|
|
6
|
+
import { CODEX_ADAPTER, handleCodexHookEvent } from './hook-handler.js';
|
|
7
|
+
import { setupCodexHooks, uninstallCodexHooks } from './setup.js';
|
|
8
|
+
import { CodexWatcher } from './watcher.js';
|
|
9
|
+
export { CODEX_DEFAULTS } from './defaults.js';
|
|
10
|
+
export { CODEX_ADAPTER, codexTransition, codexServiceId, handleCodexHookEvent, } from './hook-handler.js';
|
|
11
|
+
export { setupCodexHooks, uninstallCodexHooks, installCodexHooksInto, CODEX_HOOK_EVENTS, } from './setup.js';
|
|
12
|
+
export { parseRolloutLine, describeExecScript } from './rollout.js';
|
|
13
|
+
export { CodexWatcher } from './watcher.js';
|
|
14
|
+
/**
|
|
15
|
+
* codex subcommands: setup | uninstall | hook | flush | watch | cleanup.
|
|
16
|
+
* `hook`/`flush` are invoked by Codex / ourselves and stay silent on stdout.
|
|
17
|
+
*/
|
|
18
|
+
export async function handleCodexCommand(opts) {
|
|
19
|
+
const { subcommand, config, flags } = opts;
|
|
20
|
+
switch (subcommand) {
|
|
21
|
+
case 'setup': {
|
|
22
|
+
if (flags['uninstall']) {
|
|
23
|
+
uninstallCodexHooks();
|
|
24
|
+
break;
|
|
25
|
+
}
|
|
26
|
+
const ok = setupCodexHooks({
|
|
27
|
+
apiUrl: stringFlag(flags, 'api-url') ?? process.env['NOCVILLE_API_URL'],
|
|
28
|
+
apiKey: stringFlag(flags, 'api-key') ?? process.env['NOCVILLE_API_KEY'],
|
|
29
|
+
map: stringFlag(flags, 'map'),
|
|
30
|
+
cliPath: process.argv[1],
|
|
31
|
+
});
|
|
32
|
+
if (!ok)
|
|
33
|
+
process.exitCode = 1;
|
|
34
|
+
break;
|
|
35
|
+
}
|
|
36
|
+
case 'uninstall':
|
|
37
|
+
uninstallCodexHooks();
|
|
38
|
+
break;
|
|
39
|
+
case 'hook':
|
|
40
|
+
await handleCodexHookEvent(config, readStdin(), { mode: 'detach' });
|
|
41
|
+
break;
|
|
42
|
+
case 'watchdog':
|
|
43
|
+
await runWatchdog(CODEX_ADAPTER, config, opts.args?.[0]);
|
|
44
|
+
break;
|
|
45
|
+
case 'flush':
|
|
46
|
+
await runFlushCommand(CODEX_ADAPTER, config, opts.args?.[0], numberFlag(flags, 'delay'), flags['stats'] === true);
|
|
47
|
+
break;
|
|
48
|
+
case 'watch': {
|
|
49
|
+
console.error('codex watch is a fallback for Codex builds without hooks; prefer `nocville-npc codex setup`.');
|
|
50
|
+
if (!config.apiKey) {
|
|
51
|
+
console.error('Error: no API key — run `nocville-npc init --api-key=<key>` or pass --api-key');
|
|
52
|
+
process.exitCode = 1;
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
const idleMin = numberFlag(flags, 'idle-timeout');
|
|
56
|
+
const watcher = new CodexWatcher(config, {
|
|
57
|
+
sessionsDir: stringFlag(flags, 'sessions-dir'),
|
|
58
|
+
idleTimeoutMs: idleMin !== undefined ? idleMin * 60_000 : undefined,
|
|
59
|
+
includeExec: boolFlag(flags, 'include-exec') ?? false,
|
|
60
|
+
pollMs: numberFlag(flags, 'poll-interval'),
|
|
61
|
+
});
|
|
62
|
+
await watcher.start();
|
|
63
|
+
break;
|
|
64
|
+
}
|
|
65
|
+
case 'cleanup': {
|
|
66
|
+
const removed = await cleanupSessionStates(CODEX_DEFAULTS.stateDir, CODEX_DEFAULTS.stateFileMaxAge);
|
|
67
|
+
console.log(`Cleanup complete: ${removed} stale file(s) removed`);
|
|
68
|
+
break;
|
|
69
|
+
}
|
|
70
|
+
default:
|
|
71
|
+
console.error(`Unknown codex subcommand: ${subcommand}`);
|
|
72
|
+
console.error('Available subcommands: setup, uninstall, watch, hook, cleanup');
|
|
73
|
+
process.exitCode = 1;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parser for Codex session rollout logs (`~/.codex/sessions/YYYY/MM/DD/rollout-*.jsonl`).
|
|
3
|
+
*
|
|
4
|
+
* Each line is `{ timestamp, type, payload }`. The format is internal to Codex
|
|
5
|
+
* and has shifted between releases, so this is deliberately defensive: it
|
|
6
|
+
* recognises both the older `event_msg` records (exec_command_begin,
|
|
7
|
+
* patch_apply_begin, *_approval_request, user_message) and the newer ones
|
|
8
|
+
* (custom_tool_call "exec" scripts, function_call, item_completed), and
|
|
9
|
+
* ignores everything else. Content (prompts, outputs) is never surfaced.
|
|
10
|
+
*/
|
|
11
|
+
export type RolloutSignal = {
|
|
12
|
+
kind: 'meta';
|
|
13
|
+
sessionId: string;
|
|
14
|
+
cwd?: string;
|
|
15
|
+
/** Spawned subagent / guardian reviewer — not a user-facing session. */
|
|
16
|
+
subagent: boolean;
|
|
17
|
+
/** Non-interactive `codex exec` run. */
|
|
18
|
+
exec: boolean;
|
|
19
|
+
} | {
|
|
20
|
+
kind: 'cwd';
|
|
21
|
+
cwd: string;
|
|
22
|
+
} | {
|
|
23
|
+
kind: 'working';
|
|
24
|
+
activity: string;
|
|
25
|
+
} | {
|
|
26
|
+
kind: 'waiting';
|
|
27
|
+
activity: string;
|
|
28
|
+
} | {
|
|
29
|
+
kind: 'idle';
|
|
30
|
+
activity: string;
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Describe a Codex "exec" code-mode script (`text(await tools.exec_command({cmd:"…"}))`)
|
|
34
|
+
* by its first tool call. Returns null for no-op helpers.
|
|
35
|
+
*/
|
|
36
|
+
export declare function describeExecScript(script: string, cwd?: string): string | null;
|
|
37
|
+
/** Parse one rollout line into a signal (or null when it carries nothing useful). */
|
|
38
|
+
export declare function parseRolloutLine(line: string, cwd?: string): RolloutSignal | null;
|
|
39
|
+
//# sourceMappingURL=rollout.d.ts.map
|