@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,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
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.GEMINI_HOOK_SPEC = exports.GEMINI_ADAPTER = void 0;
|
|
4
|
+
exports.geminiServiceId = geminiServiceId;
|
|
5
|
+
exports.geminiTransition = geminiTransition;
|
|
6
|
+
exports.handleGeminiHookEvent = handleGeminiHookEvent;
|
|
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.GEMINI_ADAPTER = {
|
|
12
|
+
command: 'gemini',
|
|
13
|
+
label: 'Gemini',
|
|
14
|
+
family: 'gemini',
|
|
15
|
+
stateDir: defaults_js_1.GEMINI_DEFAULTS.stateDir,
|
|
16
|
+
heartbeatSeconds: defaults_js_1.GEMINI_DEFAULTS.heartbeatSeconds,
|
|
17
|
+
processNames: ['gemini', 'node'],
|
|
18
|
+
nodeCommandPattern: /[\/\\](?:@google[\/\\])?gemini(?:-cli[\/\\]|(?:\.[cm]?js)?(?:\s|$))/i,
|
|
19
|
+
};
|
|
20
|
+
function geminiServiceId(sessionId, cwd) {
|
|
21
|
+
return (0, session_js_1.sanitizeServiceId)(`gemini-${(0, session_js_1.projectNameOf)(cwd)}-${sessionId.slice(0, 8)}`);
|
|
22
|
+
}
|
|
23
|
+
const idle = (activity) => ({
|
|
24
|
+
state: 'IDLE',
|
|
25
|
+
activity,
|
|
26
|
+
status: 'healthy',
|
|
27
|
+
mood: 'idle',
|
|
28
|
+
});
|
|
29
|
+
const working = (activity, warning = false) => ({
|
|
30
|
+
state: 'WORKING',
|
|
31
|
+
activity,
|
|
32
|
+
status: warning ? 'warning' : 'healthy',
|
|
33
|
+
mood: warning ? 'concerned' : 'happy',
|
|
34
|
+
});
|
|
35
|
+
/** Only phase and tool metadata are used; prompts/responses/transcripts are never read. */
|
|
36
|
+
function geminiTransition(event, prev) {
|
|
37
|
+
const cwd = typeof event.cwd === 'string' ? event.cwd : prev?.projectDir;
|
|
38
|
+
switch ((0, hook_runner_js_1.hookEventName)(event)) {
|
|
39
|
+
case 'SessionStart':
|
|
40
|
+
return idle('Ready for a task');
|
|
41
|
+
case 'BeforeAgent':
|
|
42
|
+
return working(activity_js_1.THINKING);
|
|
43
|
+
case 'BeforeTool':
|
|
44
|
+
return working((0, activity_js_1.describeToolUse)(event.tool_name, event.tool_input, cwd));
|
|
45
|
+
case 'AfterTool': {
|
|
46
|
+
const response = event['tool_response'];
|
|
47
|
+
if (response && typeof response === 'object' && 'error' in response && response.error)
|
|
48
|
+
return working((0, activity_js_1.failureText)(event.tool_name), true);
|
|
49
|
+
return { ...working(), quiet: prev?.state === 'WORKING' };
|
|
50
|
+
}
|
|
51
|
+
case 'Notification':
|
|
52
|
+
if (event['notification_type'] === 'ToolPermission')
|
|
53
|
+
return {
|
|
54
|
+
state: 'WAITING',
|
|
55
|
+
status: 'warning',
|
|
56
|
+
mood: 'concerned',
|
|
57
|
+
activity: 'Needs your approval',
|
|
58
|
+
};
|
|
59
|
+
break;
|
|
60
|
+
case 'AfterAgent':
|
|
61
|
+
return idle('Done — waiting for next task');
|
|
62
|
+
case 'PreCompress':
|
|
63
|
+
return working('Compacting context');
|
|
64
|
+
case 'SessionEnd':
|
|
65
|
+
return {
|
|
66
|
+
state: 'IDLE',
|
|
67
|
+
activity: 'Session ended',
|
|
68
|
+
status: 'unknown',
|
|
69
|
+
mood: 'idle',
|
|
70
|
+
offline: true,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
return {
|
|
74
|
+
state: prev?.state ?? 'IDLE',
|
|
75
|
+
status: prev?.status ?? 'healthy',
|
|
76
|
+
mood: prev?.mood ?? 'idle',
|
|
77
|
+
quiet: true,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
exports.GEMINI_HOOK_SPEC = {
|
|
81
|
+
adapter: exports.GEMINI_ADAPTER,
|
|
82
|
+
serviceIdFor: geminiServiceId,
|
|
83
|
+
transition: geminiTransition,
|
|
84
|
+
stdoutOnSuccess: '{}',
|
|
85
|
+
statsEvents: ['AfterAgent'],
|
|
86
|
+
};
|
|
87
|
+
async function handleGeminiHookEvent(config, rawInput, opts = {}) {
|
|
88
|
+
await (0, hook_runner_js_1.runAgentHook)(exports.GEMINI_HOOK_SPEC, config, rawInput ?? (0, hook_runner_js_1.readStdin)(), {
|
|
89
|
+
minPushGapMs: defaults_js_1.GEMINI_DEFAULTS.minPushGapMs,
|
|
90
|
+
...opts,
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=hook-handler.js.map
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.GEMINI_HOOK_EVENTS = exports.installGeminiHooksInto = exports.uninstallGeminiHooks = exports.setupGeminiHooks = exports.handleGeminiHookEvent = exports.geminiServiceId = exports.geminiTransition = exports.GEMINI_ADAPTER = exports.GEMINI_DEFAULTS = void 0;
|
|
4
|
+
exports.handleGeminiCommand = handleGeminiCommand;
|
|
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, "GEMINI_DEFAULTS", { enumerable: true, get: function () { return defaults_js_2.GEMINI_DEFAULTS; } });
|
|
14
|
+
var hook_handler_js_2 = require("./hook-handler.js");
|
|
15
|
+
Object.defineProperty(exports, "GEMINI_ADAPTER", { enumerable: true, get: function () { return hook_handler_js_2.GEMINI_ADAPTER; } });
|
|
16
|
+
Object.defineProperty(exports, "geminiTransition", { enumerable: true, get: function () { return hook_handler_js_2.geminiTransition; } });
|
|
17
|
+
Object.defineProperty(exports, "geminiServiceId", { enumerable: true, get: function () { return hook_handler_js_2.geminiServiceId; } });
|
|
18
|
+
Object.defineProperty(exports, "handleGeminiHookEvent", { enumerable: true, get: function () { return hook_handler_js_2.handleGeminiHookEvent; } });
|
|
19
|
+
var setup_js_2 = require("./setup.js");
|
|
20
|
+
Object.defineProperty(exports, "setupGeminiHooks", { enumerable: true, get: function () { return setup_js_2.setupGeminiHooks; } });
|
|
21
|
+
Object.defineProperty(exports, "uninstallGeminiHooks", { enumerable: true, get: function () { return setup_js_2.uninstallGeminiHooks; } });
|
|
22
|
+
Object.defineProperty(exports, "installGeminiHooksInto", { enumerable: true, get: function () { return setup_js_2.installGeminiHooksInto; } });
|
|
23
|
+
Object.defineProperty(exports, "GEMINI_HOOK_EVENTS", { enumerable: true, get: function () { return setup_js_2.GEMINI_HOOK_EVENTS; } });
|
|
24
|
+
/**
|
|
25
|
+
* gemini subcommands: setup | uninstall | hook | flush | watchdog | cleanup.
|
|
26
|
+
* `hook` always emits {}; detached helpers stay silent.
|
|
27
|
+
*/
|
|
28
|
+
async function handleGeminiCommand(opts) {
|
|
29
|
+
const { subcommand, config, flags } = opts;
|
|
30
|
+
switch (subcommand) {
|
|
31
|
+
case 'setup': {
|
|
32
|
+
if (flags['uninstall']) {
|
|
33
|
+
(0, setup_js_1.uninstallGeminiHooks)();
|
|
34
|
+
break;
|
|
35
|
+
}
|
|
36
|
+
const ok = (0, setup_js_1.setupGeminiHooks)({
|
|
37
|
+
apiUrl: (0, config_js_1.stringFlag)(flags, 'api-url') ?? process.env['NOCVILLE_API_URL'],
|
|
38
|
+
apiKey: (0, config_js_1.stringFlag)(flags, 'api-key') ?? process.env['NOCVILLE_API_KEY'],
|
|
39
|
+
map: (0, config_js_1.stringFlag)(flags, 'map'),
|
|
40
|
+
cliPath: process.argv[1],
|
|
41
|
+
});
|
|
42
|
+
if (!ok)
|
|
43
|
+
process.exitCode = 1;
|
|
44
|
+
break;
|
|
45
|
+
}
|
|
46
|
+
case 'uninstall':
|
|
47
|
+
(0, setup_js_1.uninstallGeminiHooks)();
|
|
48
|
+
break;
|
|
49
|
+
case 'hook':
|
|
50
|
+
await (0, hook_handler_js_1.handleGeminiHookEvent)(config, (0, hook_runner_js_1.readStdin)(), { mode: 'detach' });
|
|
51
|
+
break;
|
|
52
|
+
case 'watchdog':
|
|
53
|
+
await (0, watchdog_js_1.runWatchdog)(hook_handler_js_1.GEMINI_ADAPTER, config, opts.args?.[0]);
|
|
54
|
+
break;
|
|
55
|
+
case 'flush':
|
|
56
|
+
await (0, hook_runner_js_1.runFlushCommand)(hook_handler_js_1.GEMINI_ADAPTER, config, opts.args?.[0], (0, config_js_1.numberFlag)(flags, 'delay'), flags['stats'] === true);
|
|
57
|
+
break;
|
|
58
|
+
case 'cleanup': {
|
|
59
|
+
const removed = await (0, session_js_1.cleanupSessionStates)(defaults_js_1.GEMINI_DEFAULTS.stateDir, defaults_js_1.GEMINI_DEFAULTS.stateFileMaxAge);
|
|
60
|
+
console.log(`Cleanup complete: ${removed} stale file(s) removed`);
|
|
61
|
+
break;
|
|
62
|
+
}
|
|
63
|
+
default:
|
|
64
|
+
console.error(`Unknown gemini subcommand: ${subcommand}`);
|
|
65
|
+
console.error('Available subcommands: setup, uninstall, hook, cleanup');
|
|
66
|
+
process.exitCode = 1;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.GEMINI_HOOK_EVENTS = void 0;
|
|
4
|
+
exports.geminiSettingsPath = geminiSettingsPath;
|
|
5
|
+
exports.installGeminiHooksInto = installGeminiHooksInto;
|
|
6
|
+
exports.buildGeminiHookCommand = buildGeminiHookCommand;
|
|
7
|
+
exports.setupGeminiHooks = setupGeminiHooks;
|
|
8
|
+
exports.uninstallGeminiHooks = uninstallGeminiHooks;
|
|
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.GEMINI_HOOK_EVENTS = [
|
|
18
|
+
'SessionStart',
|
|
19
|
+
'SessionEnd',
|
|
20
|
+
'BeforeAgent',
|
|
21
|
+
'BeforeTool',
|
|
22
|
+
'AfterTool',
|
|
23
|
+
'Notification',
|
|
24
|
+
'AfterAgent',
|
|
25
|
+
'PreCompress',
|
|
26
|
+
];
|
|
27
|
+
function geminiSettingsPath() {
|
|
28
|
+
return (0, node_path_1.join)((0, defaults_js_1.geminiHome)(), 'settings.json');
|
|
29
|
+
}
|
|
30
|
+
/** Install the nocville group into every Gemini event. Mutates `file`. */
|
|
31
|
+
function installGeminiHooksInto(file, command, opts = {}) {
|
|
32
|
+
const result = (0, hook_settings_js_1.installHookGroups)(file, command, exports.GEMINI_HOOK_EVENTS, {
|
|
33
|
+
perEvent: Object.fromEntries(exports.GEMINI_HOOK_EVENTS.map((event) => [
|
|
34
|
+
event,
|
|
35
|
+
{
|
|
36
|
+
name: 'nocville',
|
|
37
|
+
timeout: opts.timeout ?? defaults_js_1.GEMINI_DEFAULTS.hookTimeoutMs,
|
|
38
|
+
},
|
|
39
|
+
])),
|
|
40
|
+
});
|
|
41
|
+
// Omit the optional matcher so lifecycle sources/reasons are all observed too.
|
|
42
|
+
for (const event of exports.GEMINI_HOOK_EVENTS) {
|
|
43
|
+
const group = file.hooks[event].at(-1);
|
|
44
|
+
delete group.matcher;
|
|
45
|
+
}
|
|
46
|
+
return result;
|
|
47
|
+
}
|
|
48
|
+
function buildGeminiHookCommand(_cliPath) {
|
|
49
|
+
return (0, hook_settings_js_1.buildShimHookCommand)('gemini');
|
|
50
|
+
}
|
|
51
|
+
function out(opts, msg) {
|
|
52
|
+
if (!opts.quiet)
|
|
53
|
+
console.log(msg);
|
|
54
|
+
}
|
|
55
|
+
/** Merge into Gemini settings, preserving user hooks and a one-time backup. */
|
|
56
|
+
function setupGeminiHooks(opts = {}) {
|
|
57
|
+
const settingsPath = opts.settingsPath ?? geminiSettingsPath();
|
|
58
|
+
const dir = (0, node_path_1.join)(settingsPath, '..');
|
|
59
|
+
if (!(0, node_fs_1.existsSync)(dir)) {
|
|
60
|
+
console.error(`${dir} does not exist — is Gemini installed? Skipping Gemini setup.`);
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
const file = (0, hook_settings_js_1.readJsonFile)(settingsPath);
|
|
64
|
+
if (file === null) {
|
|
65
|
+
console.error(`Could not parse ${settingsPath}; fix it (or move it aside) and re-run setup.`);
|
|
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 } = installGeminiHooksInto(file, buildGeminiHookCommand(cliPath), {
|
|
73
|
+
timeout: defaults_js_1.GEMINI_DEFAULTS.hookTimeoutMs,
|
|
74
|
+
});
|
|
75
|
+
(0, hook_settings_js_1.writeJsonFile)(settingsPath, file);
|
|
76
|
+
out(opts, `✓ Gemini: 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 `~/.gemini/settings.json`. */
|
|
85
|
+
function uninstallGeminiHooks(opts = {}) {
|
|
86
|
+
const settingsPath = opts.settingsPath ?? geminiSettingsPath();
|
|
87
|
+
const file = (0, hook_settings_js_1.readJsonFile)(settingsPath);
|
|
88
|
+
if (!file || !(0, node_fs_1.existsSync)(settingsPath)) {
|
|
89
|
+
out(opts, 'Gemini: no settings.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, `✓ Gemini: removed ${removed} Nocville hook(s) from ${settingsPath}`);
|
|
96
|
+
return removed;
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=setup.js.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DEFAULTS = void 0;
|
|
4
|
+
exports.DEFAULTS = {
|
|
5
|
+
pollInterval: 60000, // 60s
|
|
6
|
+
fetchTimeoutMs: 5000,
|
|
7
|
+
perPage: 10,
|
|
8
|
+
/** Polls to keep the 'new-commit' sub-status active after a fresh commit. */
|
|
9
|
+
newCommitPolls: 2,
|
|
10
|
+
};
|
|
11
|
+
//# sourceMappingURL=defaults.js.map
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.relativeTime = relativeTime;
|
|
4
|
+
/** Short relative-time string like "3m ago", "2h ago", "1d ago". */
|
|
5
|
+
function relativeTime(iso, now = Date.now()) {
|
|
6
|
+
const t = Date.parse(iso);
|
|
7
|
+
if (!Number.isFinite(t))
|
|
8
|
+
return 'unknown';
|
|
9
|
+
const secs = Math.max(0, Math.round((now - t) / 1000));
|
|
10
|
+
if (secs < 60)
|
|
11
|
+
return `${secs}s ago`;
|
|
12
|
+
const mins = Math.round(secs / 60);
|
|
13
|
+
if (mins < 60)
|
|
14
|
+
return `${mins}m ago`;
|
|
15
|
+
const hrs = Math.round(mins / 60);
|
|
16
|
+
if (hrs < 24)
|
|
17
|
+
return `${hrs}h ago`;
|
|
18
|
+
const days = Math.round(hrs / 24);
|
|
19
|
+
return `${days}d ago`;
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=format.js.map
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.fetchDefaultBranch = fetchDefaultBranch;
|
|
4
|
+
exports.fetchCommits = fetchCommits;
|
|
5
|
+
exports.commitsWithin = commitsWithin;
|
|
6
|
+
exports.newCommitsSince = newCommitsSince;
|
|
7
|
+
function toNum(v) {
|
|
8
|
+
if (v === null)
|
|
9
|
+
return null;
|
|
10
|
+
const n = Number(v);
|
|
11
|
+
return Number.isNaN(n) ? null : n;
|
|
12
|
+
}
|
|
13
|
+
function ghHeaders(opts) {
|
|
14
|
+
const headers = {
|
|
15
|
+
Accept: 'application/vnd.github+json',
|
|
16
|
+
'User-Agent': 'nocville-npc',
|
|
17
|
+
'X-GitHub-Api-Version': '2022-11-28',
|
|
18
|
+
};
|
|
19
|
+
if (opts.token)
|
|
20
|
+
headers['Authorization'] = `Bearer ${opts.token}`;
|
|
21
|
+
return headers;
|
|
22
|
+
}
|
|
23
|
+
/** Look up a repo's default branch (one call at startup). Null on failure. */
|
|
24
|
+
async function fetchDefaultBranch(repo, opts = {}) {
|
|
25
|
+
const doFetch = opts.fetchFn ?? globalThis.fetch;
|
|
26
|
+
const controller = new AbortController();
|
|
27
|
+
const timer = setTimeout(() => controller.abort(), opts.timeoutMs ?? 5000);
|
|
28
|
+
try {
|
|
29
|
+
const res = await doFetch(`https://api.github.com/repos/${repo}`, {
|
|
30
|
+
headers: ghHeaders(opts),
|
|
31
|
+
signal: controller.signal,
|
|
32
|
+
});
|
|
33
|
+
if (!res.ok)
|
|
34
|
+
return null;
|
|
35
|
+
const body = (await res.json());
|
|
36
|
+
return body.default_branch ?? null;
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
finally {
|
|
42
|
+
clearTimeout(timer);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Fetch recent commits from the GitHub REST API using conditional requests.
|
|
47
|
+
* Passing the previous ETag yields a 304 (which does not count against the
|
|
48
|
+
* unauthenticated 60/hour limit) when nothing has changed. Never throws —
|
|
49
|
+
* network errors surface as `status: 0`.
|
|
50
|
+
*/
|
|
51
|
+
async function fetchCommits(repo, opts = {}) {
|
|
52
|
+
const perPage = opts.perPage ?? 10;
|
|
53
|
+
const doFetch = opts.fetchFn ?? globalThis.fetch;
|
|
54
|
+
let url = `https://api.github.com/repos/${repo}/commits?per_page=${perPage}`;
|
|
55
|
+
if (opts.branch)
|
|
56
|
+
url += `&sha=${encodeURIComponent(opts.branch)}`;
|
|
57
|
+
const headers = ghHeaders(opts);
|
|
58
|
+
if (opts.etag)
|
|
59
|
+
headers['If-None-Match'] = opts.etag;
|
|
60
|
+
const controller = new AbortController();
|
|
61
|
+
const timer = setTimeout(() => controller.abort(), opts.timeoutMs ?? 5000);
|
|
62
|
+
try {
|
|
63
|
+
const res = await doFetch(url, { headers, signal: controller.signal });
|
|
64
|
+
const rateRemaining = toNum(res.headers.get('x-ratelimit-remaining'));
|
|
65
|
+
const etag = res.headers.get('etag');
|
|
66
|
+
if (res.status === 304) {
|
|
67
|
+
return {
|
|
68
|
+
notModified: true,
|
|
69
|
+
commits: [],
|
|
70
|
+
etag: opts.etag ?? null,
|
|
71
|
+
rateRemaining,
|
|
72
|
+
status: 304,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
if (!res.ok) {
|
|
76
|
+
await res.text().catch(() => undefined);
|
|
77
|
+
return { notModified: false, commits: [], etag, rateRemaining, status: res.status };
|
|
78
|
+
}
|
|
79
|
+
const body = (await res.json());
|
|
80
|
+
const commits = body.map((c) => parseCommit(c));
|
|
81
|
+
return { notModified: false, commits, etag, rateRemaining, status: res.status };
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
return { notModified: false, commits: [], etag: null, rateRemaining: null, status: 0 };
|
|
85
|
+
}
|
|
86
|
+
finally {
|
|
87
|
+
clearTimeout(timer);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
function parseCommit(c) {
|
|
91
|
+
const commit = c['commit'] ?? {};
|
|
92
|
+
const commitAuthor = commit['author'] ?? {};
|
|
93
|
+
const ghAuthor = c['author'] ?? {};
|
|
94
|
+
const message = String(commit['message'] ?? '').split('\n')[0] ?? '';
|
|
95
|
+
return {
|
|
96
|
+
sha: String(c['sha'] ?? ''),
|
|
97
|
+
author: String(commitAuthor['name'] ?? ghAuthor['login'] ?? 'unknown'),
|
|
98
|
+
message,
|
|
99
|
+
date: String(commitAuthor['date'] ?? ''),
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
/** Count how many commits landed within the last `hours`. */
|
|
103
|
+
function commitsWithin(commits, hours, now = Date.now()) {
|
|
104
|
+
const cutoff = now - hours * 3600_000;
|
|
105
|
+
return commits.filter((c) => {
|
|
106
|
+
const t = Date.parse(c.date);
|
|
107
|
+
return Number.isFinite(t) && t >= cutoff;
|
|
108
|
+
}).length;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Return the commits newer than `lastSeenSha`, newest-first. When the sha isn't
|
|
112
|
+
* present in the window (more than a page of new commits, or rewritten history)
|
|
113
|
+
* every fetched commit is treated as new. An undefined sha means "not seeded
|
|
114
|
+
* yet" — returns empty so the caller can seed without a spurious announcement.
|
|
115
|
+
*/
|
|
116
|
+
function newCommitsSince(commits, lastSeenSha) {
|
|
117
|
+
if (!lastSeenSha)
|
|
118
|
+
return [];
|
|
119
|
+
const idx = commits.findIndex((c) => c.sha === lastSeenSha);
|
|
120
|
+
if (idx === 0)
|
|
121
|
+
return []; // newest is already seen
|
|
122
|
+
if (idx === -1)
|
|
123
|
+
return commits; // fell out of the window — treat all as new
|
|
124
|
+
return commits.slice(0, idx);
|
|
125
|
+
}
|
|
126
|
+
//# sourceMappingURL=github-api.js.map
|