@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,361 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CodexWatcher = void 0;
|
|
4
|
+
exports.recentDayDirs = recentDayDirs;
|
|
5
|
+
const node_fs_1 = require("node:fs");
|
|
6
|
+
const node_path_1 = require("node:path");
|
|
7
|
+
const node_1 = require("@nocville/node");
|
|
8
|
+
const logger_js_1 = require("../../core/logger.js");
|
|
9
|
+
const session_filter_js_1 = require("../../core/session-filter.js");
|
|
10
|
+
const session_js_1 = require("../agent-common/session.js");
|
|
11
|
+
const defaults_js_1 = require("./defaults.js");
|
|
12
|
+
const hook_handler_js_1 = require("./hook-handler.js");
|
|
13
|
+
const rollout_js_1 = require("./rollout.js");
|
|
14
|
+
const pad = (n) => String(n).padStart(2, '0');
|
|
15
|
+
/** Day directories to scan: today and yesterday (local time, like Codex). */
|
|
16
|
+
function recentDayDirs(root, now) {
|
|
17
|
+
const out = [];
|
|
18
|
+
for (const offset of [0, 1]) {
|
|
19
|
+
const d = new Date(now - offset * 24 * 60 * 60 * 1000);
|
|
20
|
+
out.push((0, node_path_1.join)(root, String(d.getFullYear()), pad(d.getMonth() + 1), pad(d.getDate())));
|
|
21
|
+
}
|
|
22
|
+
return out;
|
|
23
|
+
}
|
|
24
|
+
/** Read the first line of a file (session_meta can be tens of KB). */
|
|
25
|
+
function readFirstLine(file, maxBytes = 4 * 1024 * 1024) {
|
|
26
|
+
const fd = (0, node_fs_1.openSync)(file, 'r');
|
|
27
|
+
try {
|
|
28
|
+
const chunk = Buffer.alloc(64 * 1024);
|
|
29
|
+
let acc = '';
|
|
30
|
+
let pos = 0;
|
|
31
|
+
while (pos < maxBytes) {
|
|
32
|
+
const n = (0, node_fs_1.readSync)(fd, chunk, 0, chunk.length, pos);
|
|
33
|
+
if (n <= 0)
|
|
34
|
+
break;
|
|
35
|
+
const text = chunk.subarray(0, n).toString('utf-8');
|
|
36
|
+
const nl = text.indexOf('\n');
|
|
37
|
+
if (nl >= 0)
|
|
38
|
+
return acc + text.slice(0, nl);
|
|
39
|
+
acc += text;
|
|
40
|
+
pos += n;
|
|
41
|
+
}
|
|
42
|
+
return acc;
|
|
43
|
+
}
|
|
44
|
+
finally {
|
|
45
|
+
(0, node_fs_1.closeSync)(fd);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function readRange(file, start, end) {
|
|
49
|
+
if (end <= start)
|
|
50
|
+
return '';
|
|
51
|
+
const fd = (0, node_fs_1.openSync)(file, 'r');
|
|
52
|
+
try {
|
|
53
|
+
const buf = Buffer.alloc(end - start);
|
|
54
|
+
const n = (0, node_fs_1.readSync)(fd, buf, 0, buf.length, start);
|
|
55
|
+
return buf.subarray(0, n).toString('utf-8');
|
|
56
|
+
}
|
|
57
|
+
finally {
|
|
58
|
+
(0, node_fs_1.closeSync)(fd);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
class CodexWatcher {
|
|
62
|
+
config;
|
|
63
|
+
opts;
|
|
64
|
+
sessionsDir;
|
|
65
|
+
pollMs;
|
|
66
|
+
idleTimeoutMs;
|
|
67
|
+
includeExec;
|
|
68
|
+
minPushGapMs;
|
|
69
|
+
hookStateDir;
|
|
70
|
+
now;
|
|
71
|
+
logger;
|
|
72
|
+
sessions = new Map();
|
|
73
|
+
interval;
|
|
74
|
+
clients = new Map();
|
|
75
|
+
constructor(config, opts = {}) {
|
|
76
|
+
this.config = config;
|
|
77
|
+
this.opts = opts;
|
|
78
|
+
this.sessionsDir = opts.sessionsDir ?? defaults_js_1.CODEX_DEFAULTS.sessionsDir;
|
|
79
|
+
this.pollMs = opts.pollMs ?? defaults_js_1.CODEX_DEFAULTS.pollMs;
|
|
80
|
+
this.idleTimeoutMs = opts.idleTimeoutMs ?? defaults_js_1.CODEX_DEFAULTS.idleTimeoutMs;
|
|
81
|
+
this.includeExec = opts.includeExec ?? false;
|
|
82
|
+
this.minPushGapMs = opts.minPushGapMs ?? defaults_js_1.CODEX_DEFAULTS.minPushGapMs;
|
|
83
|
+
this.hookStateDir = opts.hookStateDir ?? hook_handler_js_1.CODEX_ADAPTER.stateDir;
|
|
84
|
+
this.now = opts.now ?? Date.now;
|
|
85
|
+
this.logger = new logger_js_1.Logger('codex-watch', config.debug);
|
|
86
|
+
}
|
|
87
|
+
/** Active (non-ignored) sessions — exposed for tests/diagnostics. */
|
|
88
|
+
activeSessions() {
|
|
89
|
+
return [...this.sessions.values()].filter((s) => !s.ignored).map((s) => s.state);
|
|
90
|
+
}
|
|
91
|
+
async start() {
|
|
92
|
+
(0, node_fs_1.mkdirSync)(this.hookStateDir, { recursive: true, mode: 0o700 });
|
|
93
|
+
(0, node_fs_1.writeFileSync)((0, node_path_1.join)(this.hookStateDir, `watcher-${process.pid}.pid`), String(process.pid), {
|
|
94
|
+
mode: 0o600,
|
|
95
|
+
});
|
|
96
|
+
if (!(0, node_fs_1.existsSync)(this.sessionsDir)) {
|
|
97
|
+
this.logger.warn(`${this.sessionsDir} not found yet — waiting for Codex sessions`);
|
|
98
|
+
}
|
|
99
|
+
this.logger.info(`Watching Codex sessions in ${this.sessionsDir}`);
|
|
100
|
+
await this.tick();
|
|
101
|
+
this.interval = setInterval(() => {
|
|
102
|
+
void this.tick().catch((err) => this.logger.warn('Watch tick failed', { error: err?.message }));
|
|
103
|
+
}, this.pollMs);
|
|
104
|
+
const onSignal = () => {
|
|
105
|
+
void this.stop().then(() => process.exit(0));
|
|
106
|
+
};
|
|
107
|
+
process.once('SIGINT', onSignal);
|
|
108
|
+
process.once('SIGTERM', onSignal);
|
|
109
|
+
}
|
|
110
|
+
/** Stop polling and despawn every live session. */
|
|
111
|
+
async stop() {
|
|
112
|
+
(0, node_fs_1.rmSync)((0, node_path_1.join)(this.hookStateDir, `watcher-${process.pid}.pid`), { force: true });
|
|
113
|
+
if (this.interval)
|
|
114
|
+
clearInterval(this.interval);
|
|
115
|
+
this.interval = undefined;
|
|
116
|
+
const live = [...this.sessions.values()].filter((s) => !s.ignored && s.spawned);
|
|
117
|
+
await Promise.all(live.map((s) => this.despawn(s)));
|
|
118
|
+
this.sessions.clear();
|
|
119
|
+
}
|
|
120
|
+
/** One poll: discover files, read appended lines, push, expire quiet sessions. */
|
|
121
|
+
async tick() {
|
|
122
|
+
const now = this.now();
|
|
123
|
+
for (const dir of recentDayDirs(this.sessionsDir, now)) {
|
|
124
|
+
let files;
|
|
125
|
+
try {
|
|
126
|
+
files = (0, node_fs_1.readdirSync)(dir);
|
|
127
|
+
}
|
|
128
|
+
catch {
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
for (const name of files) {
|
|
132
|
+
if (!name.startsWith('rollout-') || !name.endsWith('.jsonl'))
|
|
133
|
+
continue;
|
|
134
|
+
const file = (0, node_path_1.join)(dir, name);
|
|
135
|
+
let st;
|
|
136
|
+
try {
|
|
137
|
+
st = (0, node_fs_1.statSync)(file);
|
|
138
|
+
}
|
|
139
|
+
catch {
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
const tracked = this.sessions.get(file);
|
|
143
|
+
if (!tracked) {
|
|
144
|
+
if (now - st.mtimeMs > this.idleTimeoutMs)
|
|
145
|
+
continue;
|
|
146
|
+
this.discover(file, st.size, st.mtimeMs);
|
|
147
|
+
}
|
|
148
|
+
else if (st.size > tracked.offset) {
|
|
149
|
+
if (!tracked.ignored) {
|
|
150
|
+
if (st.size - tracked.offset > defaults_js_1.CODEX_DEFAULTS.maxReadBytes) {
|
|
151
|
+
// Huge burst (rollouts can reach hundreds of MB): jump to the tail.
|
|
152
|
+
tracked.offset = st.size - defaults_js_1.CODEX_DEFAULTS.initialTailBytes;
|
|
153
|
+
tracked.partial = '';
|
|
154
|
+
this.readAppended(tracked, st.size, true);
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
this.readAppended(tracked, st.size);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
tracked.offset = st.size;
|
|
162
|
+
}
|
|
163
|
+
tracked.lastGrowthAt = now;
|
|
164
|
+
}
|
|
165
|
+
else if (st.size < tracked.offset) {
|
|
166
|
+
// Truncated/rewritten: restart from the top.
|
|
167
|
+
tracked.offset = 0;
|
|
168
|
+
tracked.partial = '';
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
const pushes = [];
|
|
173
|
+
for (const [file, s] of this.sessions) {
|
|
174
|
+
if (s.ignored) {
|
|
175
|
+
if (now - s.lastGrowthAt > this.idleTimeoutMs * 2)
|
|
176
|
+
this.sessions.delete(file);
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
179
|
+
if (now - s.lastGrowthAt > this.idleTimeoutMs) {
|
|
180
|
+
if (s.spawned)
|
|
181
|
+
pushes.push(this.despawn(s));
|
|
182
|
+
this.sessions.delete(file);
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
pushes.push(this.maybePush(s));
|
|
186
|
+
}
|
|
187
|
+
await Promise.all(pushes);
|
|
188
|
+
}
|
|
189
|
+
discover(file, size, mtimeMs) {
|
|
190
|
+
let meta = null;
|
|
191
|
+
try {
|
|
192
|
+
const sig = (0, rollout_js_1.parseRolloutLine)(readFirstLine(file));
|
|
193
|
+
if (sig?.kind === 'meta')
|
|
194
|
+
meta = sig;
|
|
195
|
+
}
|
|
196
|
+
catch {
|
|
197
|
+
meta = null;
|
|
198
|
+
}
|
|
199
|
+
const sessionId = meta?.sessionId ?? file;
|
|
200
|
+
const cwd = meta?.cwd ?? '';
|
|
201
|
+
const ignored = !meta || meta.subagent || (meta.exec && !this.includeExec);
|
|
202
|
+
const tracked = {
|
|
203
|
+
file,
|
|
204
|
+
offset: 0,
|
|
205
|
+
partial: '',
|
|
206
|
+
lastGrowthAt: Math.min(this.now(), mtimeMs),
|
|
207
|
+
ignored,
|
|
208
|
+
spawned: false,
|
|
209
|
+
lastPushAt: 0,
|
|
210
|
+
pushedRev: 0,
|
|
211
|
+
state: {
|
|
212
|
+
sessionId,
|
|
213
|
+
serviceId: (0, hook_handler_js_1.codexServiceId)(sessionId, cwd || 'codex'),
|
|
214
|
+
state: 'IDLE',
|
|
215
|
+
projectDir: cwd,
|
|
216
|
+
lastEvent: 'discover',
|
|
217
|
+
lastUpdate: this.now(),
|
|
218
|
+
activity: ['Ready for a task'],
|
|
219
|
+
status: 'healthy',
|
|
220
|
+
mood: 'idle',
|
|
221
|
+
rev: 1,
|
|
222
|
+
},
|
|
223
|
+
};
|
|
224
|
+
this.sessions.set(file, tracked);
|
|
225
|
+
if (ignored) {
|
|
226
|
+
tracked.offset = size;
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
// Replay only the tail of a long-running session to establish its state.
|
|
230
|
+
const start = Math.max(0, size - defaults_js_1.CODEX_DEFAULTS.initialTailBytes);
|
|
231
|
+
tracked.offset = start;
|
|
232
|
+
tracked.partial = '';
|
|
233
|
+
const skipFirst = start > 0;
|
|
234
|
+
this.readAppended(tracked, size, skipFirst);
|
|
235
|
+
this.logger.info(`Tracking Codex session ${sessionId} (${tracked.state.projectDir || '?'})`);
|
|
236
|
+
}
|
|
237
|
+
readAppended(s, size, skipFirstLine = false) {
|
|
238
|
+
let text;
|
|
239
|
+
try {
|
|
240
|
+
text = s.partial + readRange(s.file, s.offset, size);
|
|
241
|
+
}
|
|
242
|
+
catch {
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
s.offset = size;
|
|
246
|
+
const lines = text.split('\n');
|
|
247
|
+
s.partial = lines.pop() ?? '';
|
|
248
|
+
if (skipFirstLine)
|
|
249
|
+
lines.shift();
|
|
250
|
+
for (const line of lines) {
|
|
251
|
+
const sig = (0, rollout_js_1.parseRolloutLine)(line, s.state.projectDir || undefined);
|
|
252
|
+
if (sig)
|
|
253
|
+
this.apply(s, sig);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
/** Reduce one signal into the session state. Exposed via tick for tests. */
|
|
257
|
+
apply(s, sig) {
|
|
258
|
+
const st = s.state;
|
|
259
|
+
const set = (phase, activity, mood) => {
|
|
260
|
+
const history = (0, session_js_1.pushHistory)(st.activity, activity);
|
|
261
|
+
if (st.state === phase && history[0] === st.activity?.[0])
|
|
262
|
+
return;
|
|
263
|
+
st.state = phase;
|
|
264
|
+
st.activity = history;
|
|
265
|
+
st.status = phase === 'WAITING' ? 'warning' : 'healthy';
|
|
266
|
+
st.mood = mood;
|
|
267
|
+
st.lastUpdate = this.now();
|
|
268
|
+
st.rev = (st.rev ?? 0) + 1;
|
|
269
|
+
};
|
|
270
|
+
switch (sig.kind) {
|
|
271
|
+
case 'meta':
|
|
272
|
+
return;
|
|
273
|
+
case 'cwd':
|
|
274
|
+
if (sig.cwd && sig.cwd !== st.projectDir) {
|
|
275
|
+
st.projectDir = sig.cwd;
|
|
276
|
+
if (!s.spawned)
|
|
277
|
+
st.serviceId = (0, hook_handler_js_1.codexServiceId)(st.sessionId, sig.cwd);
|
|
278
|
+
st.rev = (st.rev ?? 0) + 1;
|
|
279
|
+
}
|
|
280
|
+
return;
|
|
281
|
+
case 'working':
|
|
282
|
+
return set('WORKING', sig.activity, 'happy');
|
|
283
|
+
case 'waiting':
|
|
284
|
+
return set('WAITING', sig.activity, 'concerned');
|
|
285
|
+
case 'idle':
|
|
286
|
+
return set('IDLE', sig.activity, 'idle');
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
hooksOwn(sessionId) {
|
|
290
|
+
return (0, node_fs_1.existsSync)((0, session_js_1.stateFilePath)(this.hookStateDir, sessionId));
|
|
291
|
+
}
|
|
292
|
+
async maybePush(s) {
|
|
293
|
+
const rev = s.state.rev ?? 0;
|
|
294
|
+
if (rev <= s.pushedRev)
|
|
295
|
+
return;
|
|
296
|
+
if (this.hooksOwn(s.state.sessionId)) {
|
|
297
|
+
s.pushedRev = rev;
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
const verdict = (0, session_filter_js_1.sessionPublishDecision)(this.opts.sessionFilter ?? (0, session_filter_js_1.loadSessionFilter)(), {
|
|
301
|
+
projectDir: s.state.projectDir,
|
|
302
|
+
name: s.state.name,
|
|
303
|
+
});
|
|
304
|
+
if (!verdict.publish) {
|
|
305
|
+
// Filtered out by sessions.json: never show it, and remove it if it was showing.
|
|
306
|
+
s.pushedRev = rev;
|
|
307
|
+
if (s.spawned) {
|
|
308
|
+
s.spawned = false;
|
|
309
|
+
const offline = { ...s.state, offline: true };
|
|
310
|
+
await this.send(s.state.serviceId, (0, session_js_1.buildAgentPayload)(hook_handler_js_1.CODEX_ADAPTER, offline, this.config));
|
|
311
|
+
}
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
const now = this.now();
|
|
315
|
+
if (s.spawned && now - s.lastPushAt < this.minPushGapMs) {
|
|
316
|
+
// Coalesce; the next tick after the gap pushes the latest state.
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
s.pushedRev = rev;
|
|
320
|
+
s.lastPushAt = now;
|
|
321
|
+
s.spawned = true;
|
|
322
|
+
await this.send(s.state.serviceId, (0, session_js_1.buildAgentPayload)(hook_handler_js_1.CODEX_ADAPTER, s.state, this.config));
|
|
323
|
+
}
|
|
324
|
+
async despawn(s) {
|
|
325
|
+
if (this.hooksOwn(s.state.sessionId) || !s.spawned)
|
|
326
|
+
return;
|
|
327
|
+
const offline = { ...s.state, offline: true };
|
|
328
|
+
await this.send(s.state.serviceId, (0, session_js_1.buildAgentPayload)(hook_handler_js_1.CODEX_ADAPTER, offline, this.config));
|
|
329
|
+
this.logger.info(`Codex session ${s.state.sessionId} ended`);
|
|
330
|
+
}
|
|
331
|
+
async send(serviceId, payload) {
|
|
332
|
+
try {
|
|
333
|
+
if (this.opts.push) {
|
|
334
|
+
await this.opts.push(serviceId, payload);
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
if (!this.config.apiKey)
|
|
338
|
+
return;
|
|
339
|
+
let client = this.clients.get(serviceId);
|
|
340
|
+
if (!client) {
|
|
341
|
+
client = new node_1.NocvilleClient({
|
|
342
|
+
apiUrl: this.config.apiUrl,
|
|
343
|
+
apiKey: this.config.apiKey,
|
|
344
|
+
serviceId,
|
|
345
|
+
timeout: 5000,
|
|
346
|
+
retries: 1,
|
|
347
|
+
debug: this.config.debug,
|
|
348
|
+
});
|
|
349
|
+
this.clients.set(serviceId, client);
|
|
350
|
+
}
|
|
351
|
+
const result = await client.pushStatus(payload);
|
|
352
|
+
if (!result.success)
|
|
353
|
+
this.logger.warn('Push failed', { serviceId, error: result.error });
|
|
354
|
+
}
|
|
355
|
+
catch (err) {
|
|
356
|
+
this.logger.warn('Push error', { serviceId, error: err?.message });
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
exports.CodexWatcher = CodexWatcher;
|
|
361
|
+
//# sourceMappingURL=watcher.js.map
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.cursorHome = exports.CURSOR_DEFAULTS = void 0;
|
|
4
|
+
const node_os_1 = require("node:os");
|
|
5
|
+
const node_path_1 = require("node:path");
|
|
6
|
+
exports.CURSOR_DEFAULTS = {
|
|
7
|
+
stateDir: (0, node_path_1.join)((0, node_os_1.tmpdir)(), 'nocville-cursor'),
|
|
8
|
+
stateFileMaxAge: 24 * 60 * 60 * 1000,
|
|
9
|
+
heartbeatSeconds: 90,
|
|
10
|
+
minPushGapMs: 1500,
|
|
11
|
+
hookTimeoutSeconds: 10,
|
|
12
|
+
};
|
|
13
|
+
const cursorHome = () => (0, node_path_1.join)((0, node_os_1.homedir)(), '.cursor');
|
|
14
|
+
exports.cursorHome = cursorHome;
|
|
15
|
+
//# sourceMappingURL=defaults.js.map
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CURSOR_HOOK_SPEC = exports.CURSOR_ADAPTER = void 0;
|
|
4
|
+
exports.cursorServiceId = cursorServiceId;
|
|
5
|
+
exports.handleCursorHookEvent = handleCursorHookEvent;
|
|
6
|
+
const node_crypto_1 = require("node:crypto");
|
|
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 state_machine_js_1 = require("../claude-code/state-machine.js");
|
|
11
|
+
const defaults_js_1 = require("./defaults.js");
|
|
12
|
+
const normalize_js_1 = require("./normalize.js");
|
|
13
|
+
exports.CURSOR_ADAPTER = {
|
|
14
|
+
command: 'cursor',
|
|
15
|
+
label: 'Cursor',
|
|
16
|
+
family: 'cursor',
|
|
17
|
+
stateDir: defaults_js_1.CURSOR_DEFAULTS.stateDir,
|
|
18
|
+
heartbeatSeconds: 90,
|
|
19
|
+
processNames: [],
|
|
20
|
+
};
|
|
21
|
+
function cursorServiceId(sessionId, cwd) {
|
|
22
|
+
return (0, session_js_1.sanitizeServiceId)(`cursor-${(0, session_js_1.projectNameOf)(cwd)}-${sessionId.slice(0, 8)}`);
|
|
23
|
+
}
|
|
24
|
+
exports.CURSOR_HOOK_SPEC = {
|
|
25
|
+
adapter: exports.CURSOR_ADAPTER,
|
|
26
|
+
serviceIdFor: cursorServiceId,
|
|
27
|
+
transition: (event, previous) => event.hook_event_name === 'PostToolUse'
|
|
28
|
+
? {
|
|
29
|
+
state: 'WORKING',
|
|
30
|
+
status: 'healthy',
|
|
31
|
+
mood: 'happy',
|
|
32
|
+
activity: (0, activity_js_1.describeToolUse)(event.tool_name, event.tool_input, event.cwd),
|
|
33
|
+
}
|
|
34
|
+
: (0, state_machine_js_1.claudeTransition)(event, previous),
|
|
35
|
+
deliveryIdentity: (event) => ({
|
|
36
|
+
source: event['nocvilleHookSource'] === 'third-party' ? 'third-party' : 'native',
|
|
37
|
+
key: (0, node_crypto_1.createHash)('sha256')
|
|
38
|
+
.update(JSON.stringify([
|
|
39
|
+
event.hook_event_name,
|
|
40
|
+
event['generation_id'],
|
|
41
|
+
(0, activity_js_1.describeToolUse)(event.tool_name, event.tool_input, event.cwd),
|
|
42
|
+
]))
|
|
43
|
+
.digest('hex'),
|
|
44
|
+
}),
|
|
45
|
+
};
|
|
46
|
+
async function handleCursorHookEvent(config, rawInput, opts = {}, source = 'native') {
|
|
47
|
+
try {
|
|
48
|
+
const input = rawInput ?? (0, hook_runner_js_1.readStdin)();
|
|
49
|
+
const event = typeof input === 'string' ? JSON.parse(input) : input;
|
|
50
|
+
if (!event || typeof event !== 'object' || Array.isArray(event))
|
|
51
|
+
return;
|
|
52
|
+
await (0, hook_runner_js_1.runAgentHook)(exports.CURSOR_HOOK_SPEC, config, {
|
|
53
|
+
...(0, normalize_js_1.normalizeCursorEvent)(event),
|
|
54
|
+
nocvilleHookSource: source,
|
|
55
|
+
}, { minPushGapMs: defaults_js_1.CURSOR_DEFAULTS.minPushGapMs, ...opts });
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
/* Hook failures never affect Cursor. */
|
|
59
|
+
}
|
|
60
|
+
finally {
|
|
61
|
+
// A schema-neutral response, including malformed/failed direct invocations.
|
|
62
|
+
process.stdout.write('{}\n');
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=hook-handler.js.map
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CURSOR_TO_CANON = exports.normalizeCursorEvent = exports.isCursorInvocation = exports.CURSOR_HOOK_EVENTS = exports.installCursorHooksInto = exports.uninstallCursorHooks = exports.setupCursorHooks = exports.handleCursorHookEvent = exports.cursorServiceId = exports.CURSOR_ADAPTER = exports.CURSOR_DEFAULTS = void 0;
|
|
4
|
+
exports.handleCursorCommand = handleCursorCommand;
|
|
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, "CURSOR_DEFAULTS", { enumerable: true, get: function () { return defaults_js_2.CURSOR_DEFAULTS; } });
|
|
14
|
+
var hook_handler_js_2 = require("./hook-handler.js");
|
|
15
|
+
Object.defineProperty(exports, "CURSOR_ADAPTER", { enumerable: true, get: function () { return hook_handler_js_2.CURSOR_ADAPTER; } });
|
|
16
|
+
Object.defineProperty(exports, "cursorServiceId", { enumerable: true, get: function () { return hook_handler_js_2.cursorServiceId; } });
|
|
17
|
+
Object.defineProperty(exports, "handleCursorHookEvent", { enumerable: true, get: function () { return hook_handler_js_2.handleCursorHookEvent; } });
|
|
18
|
+
var setup_js_2 = require("./setup.js");
|
|
19
|
+
Object.defineProperty(exports, "setupCursorHooks", { enumerable: true, get: function () { return setup_js_2.setupCursorHooks; } });
|
|
20
|
+
Object.defineProperty(exports, "uninstallCursorHooks", { enumerable: true, get: function () { return setup_js_2.uninstallCursorHooks; } });
|
|
21
|
+
Object.defineProperty(exports, "installCursorHooksInto", { enumerable: true, get: function () { return setup_js_2.installCursorHooksInto; } });
|
|
22
|
+
Object.defineProperty(exports, "CURSOR_HOOK_EVENTS", { enumerable: true, get: function () { return setup_js_2.CURSOR_HOOK_EVENTS; } });
|
|
23
|
+
/**
|
|
24
|
+
* cursor subcommands: setup | uninstall | hook | flush | watchdog | cleanup.
|
|
25
|
+
* All hooks and detached helpers stay silent.
|
|
26
|
+
*/
|
|
27
|
+
async function handleCursorCommand(opts) {
|
|
28
|
+
const { subcommand, config, flags } = opts;
|
|
29
|
+
switch (subcommand) {
|
|
30
|
+
case 'setup': {
|
|
31
|
+
if (flags['uninstall']) {
|
|
32
|
+
(0, setup_js_1.uninstallCursorHooks)();
|
|
33
|
+
break;
|
|
34
|
+
}
|
|
35
|
+
const ok = (0, setup_js_1.setupCursorHooks)({
|
|
36
|
+
apiUrl: (0, config_js_1.stringFlag)(flags, 'api-url') ?? process.env['NOCVILLE_API_URL'],
|
|
37
|
+
apiKey: (0, config_js_1.stringFlag)(flags, 'api-key') ?? process.env['NOCVILLE_API_KEY'],
|
|
38
|
+
map: (0, config_js_1.stringFlag)(flags, 'map'),
|
|
39
|
+
cliPath: process.argv[1],
|
|
40
|
+
});
|
|
41
|
+
if (!ok)
|
|
42
|
+
process.exitCode = 1;
|
|
43
|
+
break;
|
|
44
|
+
}
|
|
45
|
+
case 'uninstall':
|
|
46
|
+
(0, setup_js_1.uninstallCursorHooks)();
|
|
47
|
+
break;
|
|
48
|
+
case 'hook':
|
|
49
|
+
await (0, hook_handler_js_1.handleCursorHookEvent)(config, (0, hook_runner_js_1.readStdin)(), { mode: 'detach' });
|
|
50
|
+
break;
|
|
51
|
+
case 'watchdog':
|
|
52
|
+
await (0, watchdog_js_1.runWatchdog)(hook_handler_js_1.CURSOR_ADAPTER, config, opts.args?.[0]);
|
|
53
|
+
break;
|
|
54
|
+
case 'flush':
|
|
55
|
+
await (0, hook_runner_js_1.runFlushCommand)(hook_handler_js_1.CURSOR_ADAPTER, config, opts.args?.[0], (0, config_js_1.numberFlag)(flags, 'delay'), flags['stats'] === true);
|
|
56
|
+
break;
|
|
57
|
+
case 'cleanup': {
|
|
58
|
+
const removed = await (0, session_js_1.cleanupSessionStates)(defaults_js_1.CURSOR_DEFAULTS.stateDir, defaults_js_1.CURSOR_DEFAULTS.stateFileMaxAge);
|
|
59
|
+
console.log(`Cleanup complete: ${removed} stale file(s) removed`);
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
default:
|
|
63
|
+
console.error(`Unknown cursor subcommand: ${subcommand}`);
|
|
64
|
+
console.error('Available subcommands: setup, uninstall, hook, cleanup');
|
|
65
|
+
process.exitCode = 1;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
var normalize_js_1 = require("./normalize.js");
|
|
69
|
+
Object.defineProperty(exports, "isCursorInvocation", { enumerable: true, get: function () { return normalize_js_1.isCursorInvocation; } });
|
|
70
|
+
Object.defineProperty(exports, "normalizeCursorEvent", { enumerable: true, get: function () { return normalize_js_1.normalizeCursorEvent; } });
|
|
71
|
+
Object.defineProperty(exports, "CURSOR_TO_CANON", { enumerable: true, get: function () { return normalize_js_1.CURSOR_TO_CANON; } });
|
|
72
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CURSOR_TO_CANON = void 0;
|
|
4
|
+
exports.isCursorInvocation = isCursorInvocation;
|
|
5
|
+
exports.normalizeCursorEvent = normalizeCursorEvent;
|
|
6
|
+
exports.CURSOR_TO_CANON = {
|
|
7
|
+
sessionStart: 'SessionStart',
|
|
8
|
+
sessionEnd: 'SessionEnd',
|
|
9
|
+
beforeSubmitPrompt: 'UserPromptSubmit',
|
|
10
|
+
preToolUse: 'PreToolUse',
|
|
11
|
+
postToolUse: 'PostToolUse',
|
|
12
|
+
postToolUseFailure: 'PostToolUseFailure',
|
|
13
|
+
beforeShellExecution: 'PreToolUse',
|
|
14
|
+
afterFileEdit: 'PostToolUse',
|
|
15
|
+
stop: 'Stop',
|
|
16
|
+
subagentStart: 'SubagentStart',
|
|
17
|
+
subagentStop: 'SubagentStop',
|
|
18
|
+
preCompact: 'PreCompact',
|
|
19
|
+
};
|
|
20
|
+
function isCursorInvocation(event) {
|
|
21
|
+
const transcript = typeof event['transcript_path'] === 'string'
|
|
22
|
+
? event['transcript_path'].replace(/\\/g, '/')
|
|
23
|
+
: '';
|
|
24
|
+
return (typeof event['cursor_version'] === 'string' ||
|
|
25
|
+
(typeof event['conversation_id'] === 'string' && !/(?:^|\/)\.claude(?:\/|$)/.test(transcript)));
|
|
26
|
+
}
|
|
27
|
+
/** Copy only telemetry fields; never retain prompts, model responses or tool output. */
|
|
28
|
+
function normalizeCursorEvent(event) {
|
|
29
|
+
const name = event.hook_event_name ?? event.event ?? '';
|
|
30
|
+
const roots = event['workspace_roots'];
|
|
31
|
+
const shell = name === 'beforeShellExecution';
|
|
32
|
+
const tools = {
|
|
33
|
+
Shell: 'Bash',
|
|
34
|
+
ReadFile: 'Read',
|
|
35
|
+
Write: 'Write',
|
|
36
|
+
StrReplace: 'Edit',
|
|
37
|
+
Grep: 'Grep',
|
|
38
|
+
Delete: 'Delete',
|
|
39
|
+
Task: 'Task',
|
|
40
|
+
};
|
|
41
|
+
return {
|
|
42
|
+
session_id: typeof event['conversation_id'] === 'string' ? event['conversation_id'] : event.session_id,
|
|
43
|
+
cwd: Array.isArray(roots) && typeof roots[0] === 'string' ? roots[0] : event.cwd,
|
|
44
|
+
hook_event_name: exports.CURSOR_TO_CANON[name] ?? name,
|
|
45
|
+
tool_name: shell
|
|
46
|
+
? 'Bash'
|
|
47
|
+
: event.tool_name?.startsWith('MCP:')
|
|
48
|
+
? `mcp__cursor__${event.tool_name.slice(4)}`
|
|
49
|
+
: (tools[event.tool_name ?? ''] ?? event.tool_name),
|
|
50
|
+
tool_input: shell ? { command: event['command'] } : event.tool_input,
|
|
51
|
+
generation_id: typeof event['generation_id'] === 'string' ? event['generation_id'] : undefined,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=normalize.js.map
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CURSOR_HOOK_EVENTS = void 0;
|
|
4
|
+
exports.cursorSettingsPath = cursorSettingsPath;
|
|
5
|
+
exports.installCursorHooksInto = installCursorHooksInto;
|
|
6
|
+
exports.buildCursorHookCommand = buildCursorHookCommand;
|
|
7
|
+
exports.setupCursorHooks = setupCursorHooks;
|
|
8
|
+
exports.uninstallCursorHooks = uninstallCursorHooks;
|
|
9
|
+
const node_fs_1 = require("node:fs");
|
|
10
|
+
const node_path_1 = require("node:path");
|
|
11
|
+
const user_config_js_1 = require("../../core/user-config.js");
|
|
12
|
+
const hook_settings_js_1 = require("../agent-common/hook-settings.js");
|
|
13
|
+
const shim_js_1 = require("../agent-common/shim.js");
|
|
14
|
+
const runtime_js_1 = require("../agent-common/runtime.js");
|
|
15
|
+
const defaults_js_1 = require("./defaults.js");
|
|
16
|
+
/** Telemetry hooks only; model output and prompt text never enter Nocville. */
|
|
17
|
+
exports.CURSOR_HOOK_EVENTS = [
|
|
18
|
+
'sessionStart',
|
|
19
|
+
'sessionEnd',
|
|
20
|
+
'beforeSubmitPrompt',
|
|
21
|
+
'postToolUse',
|
|
22
|
+
'postToolUseFailure',
|
|
23
|
+
'afterFileEdit',
|
|
24
|
+
'stop',
|
|
25
|
+
'subagentStop',
|
|
26
|
+
'preCompact',
|
|
27
|
+
];
|
|
28
|
+
function cursorSettingsPath() {
|
|
29
|
+
return (0, node_path_1.join)((0, defaults_js_1.cursorHome)(), 'hooks.json');
|
|
30
|
+
}
|
|
31
|
+
/** Install the nocville group into every Cursor event. Mutates `file`. */
|
|
32
|
+
function installCursorHooksInto(file, command, opts = {}) {
|
|
33
|
+
const migrated = (0, hook_settings_js_1.removeHookGroups)(file);
|
|
34
|
+
file.version = 1;
|
|
35
|
+
file.hooks ??= {};
|
|
36
|
+
for (const event of exports.CURSOR_HOOK_EVENTS) {
|
|
37
|
+
const existing = Array.isArray(file.hooks[event]) ? file.hooks[event] : [];
|
|
38
|
+
file.hooks[event] = [
|
|
39
|
+
...existing,
|
|
40
|
+
{ command, timeout: opts.timeout ?? defaults_js_1.CURSOR_DEFAULTS.hookTimeoutSeconds },
|
|
41
|
+
];
|
|
42
|
+
}
|
|
43
|
+
return { migrated, installed: exports.CURSOR_HOOK_EVENTS.length };
|
|
44
|
+
}
|
|
45
|
+
function buildCursorHookCommand(_cliPath) {
|
|
46
|
+
return (0, hook_settings_js_1.buildShimHookCommand)('cursor');
|
|
47
|
+
}
|
|
48
|
+
function out(opts, msg) {
|
|
49
|
+
if (!opts.quiet)
|
|
50
|
+
console.log(msg);
|
|
51
|
+
}
|
|
52
|
+
/** Merge into Cursor settings, preserving user hooks and a one-time backup. */
|
|
53
|
+
function setupCursorHooks(opts = {}) {
|
|
54
|
+
const settingsPath = opts.settingsPath ?? cursorSettingsPath();
|
|
55
|
+
const dir = (0, node_path_1.join)(settingsPath, '..');
|
|
56
|
+
if (!(0, node_fs_1.existsSync)(dir)) {
|
|
57
|
+
console.error(`${dir} does not exist — is Cursor installed? Skipping Cursor setup.`);
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
const file = (0, hook_settings_js_1.readJsonFile)(settingsPath);
|
|
61
|
+
if (file === null ||
|
|
62
|
+
(file.version !== undefined && file.version !== 1) ||
|
|
63
|
+
(file.hooks !== undefined &&
|
|
64
|
+
(!file.hooks || typeof file.hooks !== 'object' || Array.isArray(file.hooks)))) {
|
|
65
|
+
console.error(`Unsupported or invalid Cursor hook settings in ${settingsPath}; no settings changed.`);
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
const creds = (0, user_config_js_1.persistCredentials)(opts);
|
|
69
|
+
const resolved = (0, hook_settings_js_1.resolveCliPath)(opts.cliPath);
|
|
70
|
+
const cliPath = (0, runtime_js_1.ensureStableCliPath)(resolved);
|
|
71
|
+
(0, shim_js_1.installHookShim)((0, hook_settings_js_1.stableNodePath)(), cliPath);
|
|
72
|
+
const { installed } = installCursorHooksInto(file, buildCursorHookCommand(cliPath), {
|
|
73
|
+
timeout: defaults_js_1.CURSOR_DEFAULTS.hookTimeoutSeconds,
|
|
74
|
+
});
|
|
75
|
+
(0, hook_settings_js_1.writeJsonFile)(settingsPath, file);
|
|
76
|
+
out(opts, `✓ Cursor: installed Nocville hooks for ${installed} events in ${settingsPath}`);
|
|
77
|
+
out(opts, ` Hook runs: ${process.execPath} ${cliPath}`);
|
|
78
|
+
out(opts, ` API: ${creds.apiUrl ?? 'https://nocville.com'} key: ${(0, user_config_js_1.maskKey)(creds.apiKey)}`);
|
|
79
|
+
if (!creds.apiKey) {
|
|
80
|
+
out(opts, ' ⚠ No API key saved yet — run: nocville-npc init --api-key=<your team key>');
|
|
81
|
+
}
|
|
82
|
+
return true;
|
|
83
|
+
}
|
|
84
|
+
/** Remove all Nocville hooks from `~/.cursor/hooks.json`. */
|
|
85
|
+
function uninstallCursorHooks(opts = {}) {
|
|
86
|
+
const settingsPath = opts.settingsPath ?? cursorSettingsPath();
|
|
87
|
+
const file = (0, hook_settings_js_1.readJsonFile)(settingsPath);
|
|
88
|
+
if (!file || !(0, node_fs_1.existsSync)(settingsPath)) {
|
|
89
|
+
out(opts, 'Cursor: no hooks.json — nothing to remove.');
|
|
90
|
+
return 0;
|
|
91
|
+
}
|
|
92
|
+
const removed = (0, hook_settings_js_1.removeHookGroups)(file);
|
|
93
|
+
if (removed > 0)
|
|
94
|
+
(0, hook_settings_js_1.writeJsonFile)(settingsPath, file);
|
|
95
|
+
out(opts, `✓ Cursor: removed ${removed} Nocville hook(s) from ${settingsPath}`);
|
|
96
|
+
return removed;
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=setup.js.map
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.geminiHome = exports.GEMINI_DEFAULTS = void 0;
|
|
4
|
+
const node_os_1 = require("node:os");
|
|
5
|
+
const node_path_1 = require("node:path");
|
|
6
|
+
exports.GEMINI_DEFAULTS = {
|
|
7
|
+
stateDir: (0, node_path_1.join)((0, node_os_1.tmpdir)(), 'nocville-gemini'),
|
|
8
|
+
stateFileMaxAge: 24 * 60 * 60 * 1000,
|
|
9
|
+
heartbeatSeconds: 90,
|
|
10
|
+
minPushGapMs: 1500,
|
|
11
|
+
hookTimeoutMs: 5000,
|
|
12
|
+
};
|
|
13
|
+
const geminiHome = () => (0, node_path_1.join)((0, node_os_1.homedir)(), '.gemini');
|
|
14
|
+
exports.geminiHome = geminiHome;
|
|
15
|
+
//# sourceMappingURL=defaults.js.map
|