@nocville/npc 0.0.0-stage → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +268 -2
- package/dist/cjs/cli.js +584 -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/session-filter.js +150 -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 +95 -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 +412 -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 +361 -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 +549 -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/session-filter.d.ts +59 -0
- package/dist/core/session-filter.js +143 -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 +41 -0
- package/dist/index.js +26 -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 +192 -0
- package/dist/npcs/agent-common/session.js +390 -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 +65 -0
- package/dist/npcs/codex/watcher.js +356 -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,139 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CLAUDE_HOOK_SPEC = exports.CLAUDE_ADAPTER = void 0;
|
|
4
|
+
exports.deriveServiceId = deriveServiceId;
|
|
5
|
+
exports.humanizeProjectName = humanizeProjectName;
|
|
6
|
+
exports.lookupSessionNameFromStore = lookupSessionNameFromStore;
|
|
7
|
+
exports.resolveSessionName = resolveSessionName;
|
|
8
|
+
exports.handleHookEvent = handleHookEvent;
|
|
9
|
+
const node_fs_1 = require("node:fs");
|
|
10
|
+
const node_os_1 = require("node:os");
|
|
11
|
+
const node_path_1 = require("node:path");
|
|
12
|
+
const hook_runner_js_1 = require("../agent-common/hook-runner.js");
|
|
13
|
+
const session_js_1 = require("../agent-common/session.js");
|
|
14
|
+
const normalize_js_1 = require("../cursor/normalize.js");
|
|
15
|
+
const hook_handler_js_1 = require("../cursor/hook-handler.js");
|
|
16
|
+
const defaults_js_1 = require("./defaults.js");
|
|
17
|
+
const state_machine_js_1 = require("./state-machine.js");
|
|
18
|
+
exports.CLAUDE_ADAPTER = {
|
|
19
|
+
command: 'claude-code',
|
|
20
|
+
label: 'Claude',
|
|
21
|
+
family: 'claude',
|
|
22
|
+
stateDir: defaults_js_1.DEFAULTS.stateDir,
|
|
23
|
+
heartbeatSeconds: defaults_js_1.DEFAULTS.heartbeatSeconds,
|
|
24
|
+
processNames: ['claude'],
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Derive a serviceId from the session and project directory.
|
|
28
|
+
* Format: "claude-{project_name}-{session_id_prefix}" (≤100 chars).
|
|
29
|
+
*/
|
|
30
|
+
function deriveServiceId(sessionId, cwd) {
|
|
31
|
+
return (0, session_js_1.sanitizeServiceId)(`claude-${(0, session_js_1.projectNameOf)(cwd)}-${sessionId.slice(0, 8)}`);
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Humanize a project dir name into a banner label: split on punctuation and
|
|
35
|
+
* title-case each word ("gridbreak" -> "Gridbreak", "my-cool-app" -> "My Cool App").
|
|
36
|
+
*/
|
|
37
|
+
function humanizeProjectName(cwd) {
|
|
38
|
+
const raw = (0, session_js_1.projectNameOf)(cwd);
|
|
39
|
+
const words = raw
|
|
40
|
+
.replace(/[^a-zA-Z0-9]+/g, ' ')
|
|
41
|
+
.trim()
|
|
42
|
+
.split(/\s+/)
|
|
43
|
+
.filter(Boolean);
|
|
44
|
+
if (words.length === 0)
|
|
45
|
+
return raw;
|
|
46
|
+
return words.map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(' ');
|
|
47
|
+
}
|
|
48
|
+
/** Default location of Claude Code's per-session sidecar files. */
|
|
49
|
+
function sessionsStoreDir() {
|
|
50
|
+
return (0, node_path_1.join)((0, node_os_1.homedir)(), '.claude', 'sessions');
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Correlate a session_id to its user-facing name via the local session store.
|
|
54
|
+
* Claude Code writes ~/.claude/sessions/<pid>.json — keyed by pid, each holding
|
|
55
|
+
* `{ sessionId, name, ... }` — so we scan for the file whose sessionId matches.
|
|
56
|
+
* `dir` is injectable for testing.
|
|
57
|
+
*/
|
|
58
|
+
function lookupSessionNameFromStore(sessionId, dir = sessionsStoreDir()) {
|
|
59
|
+
if (!sessionId)
|
|
60
|
+
return null;
|
|
61
|
+
let files;
|
|
62
|
+
try {
|
|
63
|
+
files = (0, node_fs_1.readdirSync)(dir);
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
for (const file of files) {
|
|
69
|
+
if (!file.endsWith('.json'))
|
|
70
|
+
continue;
|
|
71
|
+
try {
|
|
72
|
+
const obj = JSON.parse((0, node_fs_1.readFileSync)((0, node_path_1.join)(dir, file), 'utf-8'));
|
|
73
|
+
if (obj.sessionId === sessionId && typeof obj.name === 'string' && obj.name.trim()) {
|
|
74
|
+
return obj.name.trim();
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
// Skip unreadable / partially-written session files.
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Resolve the user-facing session name. Checked fresh only on the cheap,
|
|
85
|
+
* low-frequency events (SessionStart carries `session_title`; UserPromptSubmit
|
|
86
|
+
* falls back to the store scan) so renames propagate; otherwise the cached
|
|
87
|
+
* value is reused. Returns null when unknown (banner falls back to the project).
|
|
88
|
+
*/
|
|
89
|
+
function resolveSessionName(event, cached, dir) {
|
|
90
|
+
const name = (0, state_machine_js_1.eventName)(event);
|
|
91
|
+
const shouldRefresh = name === 'SessionStart' || name === 'UserPromptSubmit' || !cached;
|
|
92
|
+
if (!shouldRefresh)
|
|
93
|
+
return cached;
|
|
94
|
+
const fromPayload = typeof event.session_title === 'string' ? event.session_title.trim() : '';
|
|
95
|
+
if (fromPayload)
|
|
96
|
+
return fromPayload;
|
|
97
|
+
return lookupSessionNameFromStore(event.session_id ?? '', dir) ?? cached;
|
|
98
|
+
}
|
|
99
|
+
exports.CLAUDE_HOOK_SPEC = {
|
|
100
|
+
adapter: exports.CLAUDE_ADAPTER,
|
|
101
|
+
serviceIdFor: deriveServiceId,
|
|
102
|
+
transition: (event, prev) => (0, state_machine_js_1.claudeTransition)(event, prev),
|
|
103
|
+
resolveName: (event, prev) => {
|
|
104
|
+
// Only scan the store on the low-frequency events; never on PreToolUse.
|
|
105
|
+
const name = (0, state_machine_js_1.eventName)(event);
|
|
106
|
+
if (prev && name !== 'SessionStart' && name !== 'UserPromptSubmit')
|
|
107
|
+
return undefined;
|
|
108
|
+
return resolveSessionName(event, prev?.name ?? null) ?? undefined;
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
/**
|
|
112
|
+
* Handle a single Claude Code hook event.
|
|
113
|
+
*
|
|
114
|
+
* Reads the JSON payload (from `rawInput` if provided, else stdin), updates the
|
|
115
|
+
* per-session state file, and pushes an NPC status update (banner
|
|
116
|
+
* "Claude · <session name or project>"). Defaults to an in-process push; the CLI
|
|
117
|
+
* passes `mode: 'detach'` so the push runs in a background process. Never throws.
|
|
118
|
+
*/
|
|
119
|
+
async function handleHookEvent(config, rawInput, opts = {}) {
|
|
120
|
+
const input = rawInput ?? (0, hook_runner_js_1.readStdin)();
|
|
121
|
+
let event;
|
|
122
|
+
try {
|
|
123
|
+
event = JSON.parse(input);
|
|
124
|
+
}
|
|
125
|
+
catch {
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
if (!event || typeof event !== 'object' || Array.isArray(event))
|
|
129
|
+
return;
|
|
130
|
+
if ((0, normalize_js_1.isCursorInvocation)(event)) {
|
|
131
|
+
await (0, hook_handler_js_1.handleCursorHookEvent)(config, event, opts, 'third-party');
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
await (0, hook_runner_js_1.runAgentHook)(exports.CLAUDE_HOOK_SPEC, config, event, {
|
|
135
|
+
minPushGapMs: defaults_js_1.DEFAULTS.minPushGapMs,
|
|
136
|
+
...opts,
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
//# sourceMappingURL=hook-handler.js.map
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ClaudeCodeNPC = exports.uninstallHooks = exports.setupHooks = exports.CLAUDE_ADAPTER = exports.handleHookEvent = exports.claudeTransition = exports.processHookEvent = exports.DEFAULTS = void 0;
|
|
4
|
+
exports.handleClaudeCodeCommand = handleClaudeCodeCommand;
|
|
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
|
+
var defaults_js_2 = require("./defaults.js");
|
|
13
|
+
Object.defineProperty(exports, "DEFAULTS", { enumerable: true, get: function () { return defaults_js_2.DEFAULTS; } });
|
|
14
|
+
var state_machine_js_1 = require("./state-machine.js");
|
|
15
|
+
Object.defineProperty(exports, "processHookEvent", { enumerable: true, get: function () { return state_machine_js_1.processHookEvent; } });
|
|
16
|
+
Object.defineProperty(exports, "claudeTransition", { enumerable: true, get: function () { return state_machine_js_1.claudeTransition; } });
|
|
17
|
+
var hook_handler_js_2 = require("./hook-handler.js");
|
|
18
|
+
Object.defineProperty(exports, "handleHookEvent", { enumerable: true, get: function () { return hook_handler_js_2.handleHookEvent; } });
|
|
19
|
+
Object.defineProperty(exports, "CLAUDE_ADAPTER", { enumerable: true, get: function () { return hook_handler_js_2.CLAUDE_ADAPTER; } });
|
|
20
|
+
var setup_js_2 = require("./setup.js");
|
|
21
|
+
Object.defineProperty(exports, "setupHooks", { enumerable: true, get: function () { return setup_js_2.setupHooks; } });
|
|
22
|
+
Object.defineProperty(exports, "uninstallHooks", { enumerable: true, get: function () { return setup_js_2.uninstallHooks; } });
|
|
23
|
+
/** Remove stale session state files. */
|
|
24
|
+
async function cleanupStateFiles() {
|
|
25
|
+
const removed = await (0, session_js_1.cleanupSessionStates)(defaults_js_1.DEFAULTS.stateDir, defaults_js_1.DEFAULTS.stateFileMaxAge);
|
|
26
|
+
console.log(`Cleanup complete: ${removed} stale file(s) removed`);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* claude-code subcommands: setup | uninstall | hook | flush | cleanup.
|
|
30
|
+
* `hook` and `flush` are invoked by Claude Code / ourselves and must stay
|
|
31
|
+
* silent on stdout and never exit non-zero.
|
|
32
|
+
*/
|
|
33
|
+
async function handleClaudeCodeCommand(opts) {
|
|
34
|
+
const { subcommand, config, flags } = opts;
|
|
35
|
+
switch (subcommand) {
|
|
36
|
+
case 'setup': {
|
|
37
|
+
if (flags['uninstall']) {
|
|
38
|
+
(0, setup_js_1.uninstallHooks)();
|
|
39
|
+
break;
|
|
40
|
+
}
|
|
41
|
+
const ok = (0, setup_js_1.setupHooks)({
|
|
42
|
+
apiUrl: (0, config_js_1.stringFlag)(flags, 'api-url') ?? process.env['NOCVILLE_API_URL'],
|
|
43
|
+
apiKey: (0, config_js_1.stringFlag)(flags, 'api-key') ?? process.env['NOCVILLE_API_KEY'],
|
|
44
|
+
map: (0, config_js_1.stringFlag)(flags, 'map'),
|
|
45
|
+
cliPath: process.argv[1],
|
|
46
|
+
debug: config.debug,
|
|
47
|
+
});
|
|
48
|
+
if (!ok)
|
|
49
|
+
process.exitCode = 1;
|
|
50
|
+
break;
|
|
51
|
+
}
|
|
52
|
+
case 'uninstall':
|
|
53
|
+
(0, setup_js_1.uninstallHooks)();
|
|
54
|
+
break;
|
|
55
|
+
case 'hook':
|
|
56
|
+
await (0, hook_handler_js_1.handleHookEvent)(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.CLAUDE_ADAPTER, config, opts.args?.[0]);
|
|
60
|
+
break;
|
|
61
|
+
case 'flush':
|
|
62
|
+
await (0, hook_runner_js_1.runFlushCommand)(hook_handler_js_1.CLAUDE_ADAPTER, config, opts.args?.[0], (0, config_js_1.numberFlag)(flags, 'delay'), flags['stats'] === true);
|
|
63
|
+
break;
|
|
64
|
+
case 'cleanup':
|
|
65
|
+
await cleanupStateFiles();
|
|
66
|
+
break;
|
|
67
|
+
default: {
|
|
68
|
+
console.error(`Unknown claude-code subcommand: ${subcommand}`);
|
|
69
|
+
console.error('Available subcommands: setup, uninstall, hook, cleanup');
|
|
70
|
+
process.exitCode = 1;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* ClaudeCodeNPC class for programmatic use.
|
|
76
|
+
*/
|
|
77
|
+
class ClaudeCodeNPC {
|
|
78
|
+
config;
|
|
79
|
+
constructor(config) {
|
|
80
|
+
this.config = config;
|
|
81
|
+
}
|
|
82
|
+
/** Process a hook event from stdin (in-process push). */
|
|
83
|
+
async handleHook() {
|
|
84
|
+
await (0, hook_handler_js_1.handleHookEvent)(this.config);
|
|
85
|
+
}
|
|
86
|
+
/** Install hooks into Claude Code settings. */
|
|
87
|
+
setup() {
|
|
88
|
+
(0, setup_js_1.setupHooks)({
|
|
89
|
+
apiUrl: this.config.apiUrl,
|
|
90
|
+
apiKey: this.config.apiKey,
|
|
91
|
+
debug: this.config.debug,
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
/** Remove all Nocville hooks from Claude Code settings. */
|
|
95
|
+
uninstall() {
|
|
96
|
+
(0, setup_js_1.uninstallHooks)();
|
|
97
|
+
}
|
|
98
|
+
/** Clean up stale session state files. */
|
|
99
|
+
async cleanup() {
|
|
100
|
+
await cleanupStateFiles();
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
exports.ClaudeCodeNPC = ClaudeCodeNPC;
|
|
104
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.findExistingNocvilleCommand = exports.CLAUDE_HOOK_EVENTS = exports.extractFlag = void 0;
|
|
4
|
+
exports.claudeSettingsPath = claudeSettingsPath;
|
|
5
|
+
exports.installHooksInto = installHooksInto;
|
|
6
|
+
exports.removeHooksFrom = removeHooksFrom;
|
|
7
|
+
exports.buildHookCommand = buildHookCommand;
|
|
8
|
+
exports.legacyCredentials = legacyCredentials;
|
|
9
|
+
exports.setupHooks = setupHooks;
|
|
10
|
+
exports.uninstallHooks = uninstallHooks;
|
|
11
|
+
const node_fs_1 = require("node:fs");
|
|
12
|
+
const node_os_1 = require("node:os");
|
|
13
|
+
const node_path_1 = require("node:path");
|
|
14
|
+
const user_config_js_1 = require("../../core/user-config.js");
|
|
15
|
+
const hook_settings_js_1 = require("../agent-common/hook-settings.js");
|
|
16
|
+
const shim_js_1 = require("../agent-common/shim.js");
|
|
17
|
+
const runtime_js_1 = require("../agent-common/runtime.js");
|
|
18
|
+
const defaults_js_1 = require("./defaults.js");
|
|
19
|
+
var hook_settings_js_2 = require("../agent-common/hook-settings.js");
|
|
20
|
+
Object.defineProperty(exports, "extractFlag", { enumerable: true, get: function () { return hook_settings_js_2.extractFlag; } });
|
|
21
|
+
function claudeSettingsPath() {
|
|
22
|
+
return (0, node_path_1.join)((0, node_os_1.homedir)(), '.claude', 'settings.json');
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Hook events to install. Verified against code.claude.com/docs/en/hooks —
|
|
26
|
+
* all exist; events without matcher support ignore the empty matcher.
|
|
27
|
+
*/
|
|
28
|
+
exports.CLAUDE_HOOK_EVENTS = [
|
|
29
|
+
'SessionStart',
|
|
30
|
+
'SessionEnd',
|
|
31
|
+
'UserPromptSubmit',
|
|
32
|
+
'PreToolUse',
|
|
33
|
+
'PostToolUse',
|
|
34
|
+
'PostToolUseFailure',
|
|
35
|
+
'PermissionRequest',
|
|
36
|
+
'Notification',
|
|
37
|
+
'Stop',
|
|
38
|
+
'SubagentStart',
|
|
39
|
+
'SubagentStop',
|
|
40
|
+
'PreCompact',
|
|
41
|
+
];
|
|
42
|
+
/** Install the nocville group into every Claude Code event. Mutates `settings`. */
|
|
43
|
+
function installHooksInto(settings, command, opts = {}) {
|
|
44
|
+
return (0, hook_settings_js_1.installHookGroups)(settings, command, exports.CLAUDE_HOOK_EVENTS, {
|
|
45
|
+
perEvent: Object.fromEntries(exports.CLAUDE_HOOK_EVENTS.map((event) => [
|
|
46
|
+
event,
|
|
47
|
+
{
|
|
48
|
+
...((opts.platform ?? process.platform) === 'win32' ? { shell: 'powershell' } : {}),
|
|
49
|
+
...(event === 'SessionEnd'
|
|
50
|
+
? { timeout: 5 }
|
|
51
|
+
: { async: true, timeout: opts.timeout ?? 10 }),
|
|
52
|
+
},
|
|
53
|
+
])),
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
/** Remove all nocville entries (nested or legacy flat). Mutates `settings`. */
|
|
57
|
+
function removeHooksFrom(settings) {
|
|
58
|
+
return (0, hook_settings_js_1.removeHookGroups)(settings);
|
|
59
|
+
}
|
|
60
|
+
var hook_settings_js_3 = require("../agent-common/hook-settings.js");
|
|
61
|
+
Object.defineProperty(exports, "findExistingNocvilleCommand", { enumerable: true, get: function () { return hook_settings_js_3.findNocvilleCommand; } });
|
|
62
|
+
/** `'<node>' '<cli.js>' claude-code hook # nocville-npc-hook` — no key on the command line. */
|
|
63
|
+
function buildHookCommand(_cliPath) {
|
|
64
|
+
return (0, hook_settings_js_1.buildShimHookCommand)('claude-code');
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Credentials embedded in a legacy hook command (`--api-url=… --api-key=…`),
|
|
68
|
+
* so upgrading setup can move them into the user config.
|
|
69
|
+
*/
|
|
70
|
+
function legacyCredentials(settings) {
|
|
71
|
+
const prev = (0, hook_settings_js_1.findNocvilleCommand)(settings);
|
|
72
|
+
if (!prev)
|
|
73
|
+
return {};
|
|
74
|
+
return { apiUrl: (0, hook_settings_js_1.extractFlag)(prev, 'api-url'), apiKey: (0, hook_settings_js_1.extractFlag)(prev, 'api-key') };
|
|
75
|
+
}
|
|
76
|
+
function out(opts, msg) {
|
|
77
|
+
if (!opts.quiet)
|
|
78
|
+
console.log(msg);
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Install Nocville hooks into Claude Code settings (matcher-group format,
|
|
82
|
+
* absolute node/cli paths, 10s timeout safety net). Idempotent: migrates and
|
|
83
|
+
* de-duplicates legacy entries and moves any legacy inline credentials into
|
|
84
|
+
* ~/.config/nocville/config.json. Returns false if setup could not complete.
|
|
85
|
+
*/
|
|
86
|
+
function setupHooks(opts) {
|
|
87
|
+
const settingsPath = opts.settingsPath ?? claudeSettingsPath();
|
|
88
|
+
const dir = (0, node_path_1.join)(settingsPath, '..');
|
|
89
|
+
if (!(0, node_fs_1.existsSync)(dir)) {
|
|
90
|
+
console.error(`${dir} does not exist — is Claude Code installed? Skipping Claude Code setup.`);
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
const settings = (0, hook_settings_js_1.readJsonFile)(settingsPath);
|
|
94
|
+
if (settings === null) {
|
|
95
|
+
console.error(`Could not parse ${settingsPath}; fix it (or move it aside) and re-run setup.`);
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
const creds = (0, user_config_js_1.persistCredentials)(opts, legacyCredentials(settings));
|
|
99
|
+
const resolved = (0, hook_settings_js_1.resolveCliPath)(opts.cliPath);
|
|
100
|
+
const cliPath = (0, runtime_js_1.ensureStableCliPath)(resolved);
|
|
101
|
+
(0, shim_js_1.installHookShim)((0, hook_settings_js_1.stableNodePath)(), cliPath);
|
|
102
|
+
const command = buildHookCommand(cliPath);
|
|
103
|
+
const { migrated, installed } = installHooksInto(settings, command, {
|
|
104
|
+
timeout: defaults_js_1.DEFAULTS.hookTimeoutSeconds,
|
|
105
|
+
});
|
|
106
|
+
(0, hook_settings_js_1.writeJsonFile)(settingsPath, settings);
|
|
107
|
+
out(opts, `✓ Claude Code: installed Nocville hooks for ${installed} events in ${settingsPath}`);
|
|
108
|
+
if (migrated > 0)
|
|
109
|
+
out(opts, ` (migrated ${migrated} legacy entr${migrated === 1 ? 'y' : 'ies'})`);
|
|
110
|
+
out(opts, ` Hook runs: ${process.execPath} ${cliPath}`);
|
|
111
|
+
out(opts, ` API: ${creds.apiUrl ?? 'https://nocville.com'} key: ${(0, user_config_js_1.maskKey)(creds.apiKey)}`);
|
|
112
|
+
if (!creds.apiKey) {
|
|
113
|
+
out(opts, ' ⚠ No API key saved yet — run: nocville-npc init --api-key=<your team key>');
|
|
114
|
+
}
|
|
115
|
+
out(opts, ' New Claude Code sessions will appear in your office. Restart running sessions.');
|
|
116
|
+
return true;
|
|
117
|
+
}
|
|
118
|
+
/** Remove all Nocville hooks from Claude Code settings. */
|
|
119
|
+
function uninstallHooks(opts = {}) {
|
|
120
|
+
const o = typeof opts === 'boolean' ? {} : opts;
|
|
121
|
+
const settingsPath = o.settingsPath ?? claudeSettingsPath();
|
|
122
|
+
const settings = (0, hook_settings_js_1.readJsonFile)(settingsPath);
|
|
123
|
+
if (!settings || !(0, node_fs_1.existsSync)(settingsPath)) {
|
|
124
|
+
out(o, 'Claude Code: no settings.json — nothing to remove.');
|
|
125
|
+
return 0;
|
|
126
|
+
}
|
|
127
|
+
const removed = removeHooksFrom(settings);
|
|
128
|
+
if (removed > 0)
|
|
129
|
+
(0, hook_settings_js_1.writeJsonFile)(settingsPath, settings);
|
|
130
|
+
out(o, `✓ Claude Code: removed ${removed} Nocville hook(s) from ${settingsPath}`);
|
|
131
|
+
return removed;
|
|
132
|
+
}
|
|
133
|
+
//# sourceMappingURL=setup.js.map
|
|
@@ -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
|