@gramatr/mcp 0.10.12 → 0.10.14
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/dist/hooks/lib/feedback.d.ts.map +1 -1
- package/dist/hooks/lib/feedback.js +0 -1
- package/dist/hooks/lib/feedback.js.map +1 -1
- package/dist/hooks/lib/hook-state.d.ts +53 -1
- package/dist/hooks/lib/hook-state.d.ts.map +1 -1
- package/dist/hooks/lib/hook-state.js +274 -81
- package/dist/hooks/lib/hook-state.js.map +1 -1
- package/dist/hooks/rating-capture.d.ts.map +1 -1
- package/dist/hooks/rating-capture.js +47 -31
- package/dist/hooks/rating-capture.js.map +1 -1
- package/dist/hooks/session-start.d.ts.map +1 -1
- package/dist/hooks/session-start.js +154 -147
- package/dist/hooks/session-start.js.map +1 -1
- package/dist/hooks/user-prompt-submit.d.ts.map +1 -1
- package/dist/hooks/user-prompt-submit.js +96 -38
- package/dist/hooks/user-prompt-submit.js.map +1 -1
- package/dist/server/hooks-listener.d.ts +13 -16
- package/dist/server/hooks-listener.d.ts.map +1 -1
- package/dist/server/hooks-listener.js +75 -27
- package/dist/server/hooks-listener.js.map +1 -1
- package/dist/tools/local-tools.d.ts +1 -1
- package/dist/tools/local-tools.d.ts.map +1 -1
- package/dist/tools/local-tools.js +69 -2
- package/dist/tools/local-tools.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { getClaudeModelFromEnv, getGramatrTimeoutFromEnv, isGramatrEnrichEnabledFromEnv, } from
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { appendOpHistory, appendTurn, getSessionContext, hydrateSessionContextFromServer, isFilesystemAvailable, setLatestClassification, } from
|
|
6
|
-
import {
|
|
7
|
-
import { appendPacketDebugLog } from
|
|
8
|
-
import {
|
|
9
|
-
import { resolveLocalProjectUuid } from
|
|
1
|
+
import { getClaudeModelFromEnv, getGramatrTimeoutFromEnv, isGramatrEnrichEnabledFromEnv, } from "../config-runtime.js";
|
|
2
|
+
import { HOOK_ROUTE_TIMEOUT_CAP_MS, HOOK_ROUTE_TIMEOUT_DEFAULT_MS, } from "./generated/hook-timeouts.js";
|
|
3
|
+
import { resolveHookClientRuntime } from "./lib/client-runtime.js";
|
|
4
|
+
import { deriveProjectId, getGitContext, readHookInput } from "./lib/gramatr-hook-utils.js";
|
|
5
|
+
import { appendOpHistory, appendTurn, getCachedDirective, getSessionContext, hydrateSessionContextFromServer, isFilesystemAvailable, setCachedDirective, setLatestClassification, } from "./lib/hook-state.js";
|
|
6
|
+
import { emitStatus, formatFailureWarning, formatIntelligence, } from "./lib/intelligence.js";
|
|
7
|
+
import { appendPacketDebugLog } from "./lib/packet-debug-log.js";
|
|
8
|
+
import { persistClassificationResult, routePrompt, shouldSkipPromptRouting, } from "./lib/routing.js";
|
|
9
|
+
import { resolveLocalProjectUuid } from "./lib/session.js";
|
|
10
10
|
let turnCounter = 0;
|
|
11
11
|
const TIMEOUT_MS = getGramatrTimeoutFromEnv(HOOK_ROUTE_TIMEOUT_DEFAULT_MS);
|
|
12
12
|
const ENABLED = isGramatrEnrichEnabledFromEnv();
|
|
@@ -27,19 +27,19 @@ function persistLastClassification(prompt, sessionId, route, downstreamModel, cl
|
|
|
27
27
|
}
|
|
28
28
|
function mapRoutingFailure(reason, detail) {
|
|
29
29
|
switch (reason) {
|
|
30
|
-
case
|
|
31
|
-
return { reason:
|
|
32
|
-
case
|
|
33
|
-
return { reason:
|
|
34
|
-
case
|
|
35
|
-
return { reason:
|
|
36
|
-
case
|
|
37
|
-
case
|
|
38
|
-
return { reason:
|
|
39
|
-
case
|
|
40
|
-
return { reason:
|
|
30
|
+
case "auth":
|
|
31
|
+
return { reason: "auth", detail };
|
|
32
|
+
case "timeout":
|
|
33
|
+
return { reason: "timeout", detail };
|
|
34
|
+
case "network_error":
|
|
35
|
+
return { reason: "server_down", detail };
|
|
36
|
+
case "http_error":
|
|
37
|
+
case "mcp_error":
|
|
38
|
+
return { reason: "server_error", detail };
|
|
39
|
+
case "parse_error":
|
|
40
|
+
return { reason: "parse_error", detail };
|
|
41
41
|
default:
|
|
42
|
-
return { reason:
|
|
42
|
+
return { reason: "unknown", detail };
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
45
|
export async function runUserPromptSubmitHook(_args = []) {
|
|
@@ -50,17 +50,15 @@ export async function runUserPromptSubmitHook(_args = []) {
|
|
|
50
50
|
}
|
|
51
51
|
try {
|
|
52
52
|
const input = await readHookInput();
|
|
53
|
-
const prompt = input.prompt ||
|
|
54
|
-
const sessionId = input.session_id ||
|
|
53
|
+
const prompt = input.prompt || "";
|
|
54
|
+
const sessionId = input.session_id || "unknown";
|
|
55
55
|
if (!prompt || shouldSkipPromptRouting(prompt)) {
|
|
56
56
|
if (prompt)
|
|
57
|
-
process.stderr.write(
|
|
57
|
+
process.stderr.write("[gramatr] enricher: trivial prompt, skipped\n");
|
|
58
58
|
process.stdout.write(JSON.stringify({}));
|
|
59
59
|
return 0;
|
|
60
60
|
}
|
|
61
|
-
const downstreamModel = getClaudeModelFromEnv() ||
|
|
62
|
-
(input.model ?? '') ||
|
|
63
|
-
'';
|
|
61
|
+
const downstreamModel = getClaudeModelFromEnv() || (input.model ?? "") || "";
|
|
64
62
|
const git = getGitContext();
|
|
65
63
|
// In filesystem-locked sandboxes, SQLite can't bridge separate hook processes.
|
|
66
64
|
// Fetch session context from the server so we have project_id + interaction_id.
|
|
@@ -75,15 +73,15 @@ export async function runUserPromptSubmitHook(_args = []) {
|
|
|
75
73
|
const localUuid = git
|
|
76
74
|
? resolveLocalProjectUuid({ directory: git.root, projectName: git.projectName })
|
|
77
75
|
: null;
|
|
78
|
-
const projectId = localUuid
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
76
|
+
const projectId = localUuid ||
|
|
77
|
+
sessionContext?.project_id ||
|
|
78
|
+
(git ? deriveProjectId(git.remote, git.projectName) : null) ||
|
|
79
|
+
undefined;
|
|
82
80
|
// gramatr interaction_id is the cross-agent session key — use it for
|
|
83
81
|
// both session_id (server history) and interaction_id (explicit resolution).
|
|
84
82
|
const gramatrInteractionId = sessionContext?.interaction_id || null;
|
|
85
83
|
const gramatrSessionId = gramatrInteractionId || sessionId;
|
|
86
|
-
process.stderr.write(
|
|
84
|
+
process.stderr.write("[gramatr] classifying...\n");
|
|
87
85
|
const t0 = Date.now();
|
|
88
86
|
const routed = await routePrompt({
|
|
89
87
|
prompt,
|
|
@@ -107,8 +105,12 @@ export async function runUserPromptSubmitHook(_args = []) {
|
|
|
107
105
|
agent_name: runtime.agentName,
|
|
108
106
|
downstream_model: downstreamModel,
|
|
109
107
|
status: !result && routed.error
|
|
110
|
-
?
|
|
111
|
-
|
|
108
|
+
? routed.error.reason === "timeout"
|
|
109
|
+
? "timeout"
|
|
110
|
+
: "error"
|
|
111
|
+
: result
|
|
112
|
+
? "ok"
|
|
113
|
+
: "skipped",
|
|
112
114
|
elapsed_ms: elapsed,
|
|
113
115
|
packet: result ?? null,
|
|
114
116
|
error: routed.error ? { reason: routed.error.reason, detail: routed.error.detail } : null,
|
|
@@ -146,7 +148,7 @@ export async function runUserPromptSubmitHook(_args = []) {
|
|
|
146
148
|
if (tokensSaved > 0) {
|
|
147
149
|
appendOpHistory({
|
|
148
150
|
session_id: gramatrSessionId,
|
|
149
|
-
tool:
|
|
151
|
+
tool: "classification",
|
|
150
152
|
time_ms: es.classifier_time_ms || 0,
|
|
151
153
|
tokens_saved: tokensSaved,
|
|
152
154
|
timestamp: Date.now(),
|
|
@@ -160,8 +162,12 @@ export async function runUserPromptSubmitHook(_args = []) {
|
|
|
160
162
|
try {
|
|
161
163
|
const cl = result?.classification || {};
|
|
162
164
|
const _es = result?.execution_summary || {};
|
|
165
|
+
const hookInput = input;
|
|
163
166
|
appendTurn({
|
|
164
167
|
session_id: gramatrSessionId,
|
|
168
|
+
client_session_id: hookInput.session_id ?? null,
|
|
169
|
+
project_id: sessionContext?.project_id ?? (projectId || null),
|
|
170
|
+
agent_name: runtime.agentName || null,
|
|
165
171
|
turn_number: turnCounter++,
|
|
166
172
|
timestamp: new Date().toISOString(),
|
|
167
173
|
prompt: prompt.substring(0, 500),
|
|
@@ -174,12 +180,64 @@ export async function runUserPromptSubmitHook(_args = []) {
|
|
|
174
180
|
catch {
|
|
175
181
|
// Non-critical
|
|
176
182
|
}
|
|
183
|
+
// #1214: cache directives + phase_template + project context from the v2
|
|
184
|
+
// packet so a later degraded-mode response can still respect hard gates
|
|
185
|
+
// and phase sequencing when the server is unreachable.
|
|
186
|
+
try {
|
|
187
|
+
if (result) {
|
|
188
|
+
const userId = sessionContext?.entity_id || "default";
|
|
189
|
+
const behavioralRules = result.behavioral_rules;
|
|
190
|
+
const behavioralDirectives = result.behavioral_directives;
|
|
191
|
+
if (behavioralRules || behavioralDirectives) {
|
|
192
|
+
setCachedDirective("directives", userId, {
|
|
193
|
+
behavioral_rules: behavioralRules ?? null,
|
|
194
|
+
behavioral_directives: behavioralDirectives ?? null,
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
const phaseTemplate = result.phase_template ?? (result.packet_1 && result.packet_1.phase_template) ?? null;
|
|
198
|
+
if (phaseTemplate) {
|
|
199
|
+
setCachedDirective("phase_template", userId, phaseTemplate);
|
|
200
|
+
}
|
|
201
|
+
const manifestProjectId = result.manifest?.project_id ?? projectId ?? null;
|
|
202
|
+
if (manifestProjectId) {
|
|
203
|
+
setCachedDirective("project_context", userId, {
|
|
204
|
+
project_id: manifestProjectId,
|
|
205
|
+
interaction_id: result.manifest?.interaction_id ?? gramatrInteractionId ?? null,
|
|
206
|
+
session_id: result.manifest?.session_id ?? gramatrSessionId,
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
catch {
|
|
212
|
+
// Non-critical — cache is best-effort.
|
|
213
|
+
}
|
|
177
214
|
if (!result || !result.classification) {
|
|
178
215
|
if (lastFailure) {
|
|
216
|
+
// #1214: degraded mode — use cached directives + phase template so the
|
|
217
|
+
// model still sees hard gates and the phase sequence when the server
|
|
218
|
+
// call errors or times out.
|
|
219
|
+
let degradedNotes = "";
|
|
220
|
+
try {
|
|
221
|
+
const userId = sessionContext?.entity_id || "default";
|
|
222
|
+
const cachedDirectives = getCachedDirective("directives", userId);
|
|
223
|
+
const cachedPhases = getCachedDirective("phase_template", userId);
|
|
224
|
+
if (cachedDirectives || cachedPhases) {
|
|
225
|
+
const degradedPacket = {
|
|
226
|
+
schema: "gmtr.intelligence.contract.v2.degraded",
|
|
227
|
+
note: "Server unavailable — directives loaded from local cache.",
|
|
228
|
+
...(cachedDirectives ? { cached_directives: cachedDirectives } : {}),
|
|
229
|
+
...(cachedPhases ? { cached_phase_template: cachedPhases } : {}),
|
|
230
|
+
};
|
|
231
|
+
degradedNotes = `\n\n${JSON.stringify(degradedPacket)}`;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
catch {
|
|
235
|
+
// Ignore cache read errors — degraded-mode fallback is best effort.
|
|
236
|
+
}
|
|
179
237
|
process.stdout.write(JSON.stringify({
|
|
180
238
|
hookSpecificOutput: {
|
|
181
|
-
hookEventName:
|
|
182
|
-
additionalContext: formatFailureWarning(lastFailure),
|
|
239
|
+
hookEventName: "UserPromptSubmit",
|
|
240
|
+
additionalContext: formatFailureWarning(lastFailure) + degradedNotes,
|
|
183
241
|
},
|
|
184
242
|
}));
|
|
185
243
|
}
|
|
@@ -190,7 +248,7 @@ export async function runUserPromptSubmitHook(_args = []) {
|
|
|
190
248
|
}
|
|
191
249
|
process.stdout.write(JSON.stringify({
|
|
192
250
|
hookSpecificOutput: {
|
|
193
|
-
hookEventName:
|
|
251
|
+
hookEventName: "UserPromptSubmit",
|
|
194
252
|
additionalContext: formatIntelligence(result, null),
|
|
195
253
|
},
|
|
196
254
|
}));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"user-prompt-submit.js","sourceRoot":"","sources":["../../src/hooks/user-prompt-submit.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,qBAAqB,EACrB,wBAAwB,EACxB,6BAA6B,GAC9B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,
|
|
1
|
+
{"version":3,"file":"user-prompt-submit.js","sourceRoot":"","sources":["../../src/hooks/user-prompt-submit.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,23 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* hooks-listener.ts — Local HTTP IPC server for hook subprocesses.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* this port and route calls through the local server instead of calling the
|
|
8
|
-
* remote server directly.
|
|
4
|
+
* The local MCP proxy server opens a lightweight HTTP listener on a random
|
|
5
|
+
* localhost port at startup. Callers (statusline, any separate-process tool)
|
|
6
|
+
* discover the port via ~/.gramatr/.hooks-port.
|
|
9
7
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* any call reaches the remote API.
|
|
15
|
-
* - Cross-hook session context via in-process memory (no SQLite file needed).
|
|
16
|
-
* - Faster: localhost vs. internet round-trip for session context reads.
|
|
8
|
+
* Security: every request must present X-Hooks-Secret matching the value in
|
|
9
|
+
* ~/.gramatr/.hooks-secret (written at startup, mode 0o600). This prevents
|
|
10
|
+
* other processes on the same machine from hijacking the channel. The secret
|
|
11
|
+
* is regenerated on each server start.
|
|
17
12
|
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
13
|
+
* Auth policy: callers never carry a JWT. The local server holds the token
|
|
14
|
+
* and manages all outbound calls to the central server. No fallback to direct
|
|
15
|
+
* remote calls — if the local server is unavailable, callers show offline.
|
|
20
16
|
*/
|
|
17
|
+
/** Return the current IPC secret. Null until startHooksListener() has run. */
|
|
18
|
+
export declare function getHooksSecret(): string | null;
|
|
21
19
|
/**
|
|
22
20
|
* Start the hooks IPC listener on a random localhost port.
|
|
23
21
|
* Writes the port to ~/.gramatr/.hooks-port so hook subprocesses can find it.
|
|
@@ -25,8 +23,7 @@
|
|
|
25
23
|
*/
|
|
26
24
|
export declare function startHooksListener(): Promise<number>;
|
|
27
25
|
/**
|
|
28
|
-
* Stop the hooks listener and remove the port
|
|
29
|
-
* Called on server shutdown so stale hooks know to fall back to direct remote.
|
|
26
|
+
* Stop the hooks listener and remove the port + secret files.
|
|
30
27
|
*/
|
|
31
28
|
export declare function stopHooksListener(): void;
|
|
32
29
|
//# sourceMappingURL=hooks-listener.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hooks-listener.d.ts","sourceRoot":"","sources":["../../src/server/hooks-listener.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"hooks-listener.d.ts","sourceRoot":"","sources":["../../src/server/hooks-listener.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAkBH,8EAA8E;AAC9E,wBAAgB,cAAc,IAAI,MAAM,GAAG,IAAI,CAE9C;AAyHD;;;;GAIG;AACH,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,MAAM,CAAC,CA8B1D;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,IAAI,CAMxC"}
|
|
@@ -1,36 +1,45 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* hooks-listener.ts — Local HTTP IPC server for hook subprocesses.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* this port and route calls through the local server instead of calling the
|
|
8
|
-
* remote server directly.
|
|
4
|
+
* The local MCP proxy server opens a lightweight HTTP listener on a random
|
|
5
|
+
* localhost port at startup. Callers (statusline, any separate-process tool)
|
|
6
|
+
* discover the port via ~/.gramatr/.hooks-port.
|
|
9
7
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* any call reaches the remote API.
|
|
15
|
-
* - Cross-hook session context via in-process memory (no SQLite file needed).
|
|
16
|
-
* - Faster: localhost vs. internet round-trip for session context reads.
|
|
8
|
+
* Security: every request must present X-Hooks-Secret matching the value in
|
|
9
|
+
* ~/.gramatr/.hooks-secret (written at startup, mode 0o600). This prevents
|
|
10
|
+
* other processes on the same machine from hijacking the channel. The secret
|
|
11
|
+
* is regenerated on each server start.
|
|
17
12
|
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
13
|
+
* Auth policy: callers never carry a JWT. The local server holds the token
|
|
14
|
+
* and manages all outbound calls to the central server. No fallback to direct
|
|
15
|
+
* remote calls — if the local server is unavailable, callers show offline.
|
|
20
16
|
*/
|
|
21
17
|
import { createServer } from 'node:http';
|
|
22
18
|
import { existsSync, mkdirSync, unlinkSync, writeFileSync } from 'node:fs';
|
|
23
19
|
import { join } from 'node:path';
|
|
20
|
+
import { randomBytes } from 'node:crypto';
|
|
24
21
|
import { getGramatrDirFromEnv, getHomeDir } from '../config-runtime.js';
|
|
25
22
|
import { callRemoteTool } from '../proxy/remote-client.js';
|
|
23
|
+
import { getToken, getServerUrl } from './auth.js';
|
|
26
24
|
// ── In-memory session context store ──
|
|
27
25
|
// Keys: session_id (string) + '__latest__' sentinel for the most recent write.
|
|
28
26
|
// Hooks that don't know the session_id (e.g. a status query) fall back to __latest__.
|
|
29
27
|
const sessionStore = new Map();
|
|
30
|
-
// ──
|
|
28
|
+
// ── Per-server-instance shared secret ──
|
|
29
|
+
let _secret = null;
|
|
30
|
+
/** Return the current IPC secret. Null until startHooksListener() has run. */
|
|
31
|
+
export function getHooksSecret() {
|
|
32
|
+
return _secret;
|
|
33
|
+
}
|
|
34
|
+
// ── Port + secret file paths ──
|
|
35
|
+
function getGramatrDir() {
|
|
36
|
+
return getGramatrDirFromEnv() || join(getHomeDir(), '.gramatr');
|
|
37
|
+
}
|
|
31
38
|
function getPortFilePath() {
|
|
32
|
-
|
|
33
|
-
|
|
39
|
+
return join(getGramatrDir(), '.hooks-port');
|
|
40
|
+
}
|
|
41
|
+
function getSecretFilePath() {
|
|
42
|
+
return join(getGramatrDir(), '.hooks-secret');
|
|
34
43
|
}
|
|
35
44
|
// ── Request helpers ──
|
|
36
45
|
async function readBody(req) {
|
|
@@ -49,6 +58,12 @@ function json(res, status, body) {
|
|
|
49
58
|
async function handleRequest(req, res) {
|
|
50
59
|
const url = new URL(req.url || '/', 'http://localhost');
|
|
51
60
|
const method = (req.method || 'GET').toUpperCase();
|
|
61
|
+
// ── Secret check — reject any caller that doesn't know the secret ──
|
|
62
|
+
const presented = req.headers['x-hooks-secret'];
|
|
63
|
+
if (!_secret || presented !== _secret) {
|
|
64
|
+
json(res, 401, { error: 'Unauthorized' });
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
52
67
|
// ── POST /hooks/tool — proxy a gramatr tool call with server-managed auth ──
|
|
53
68
|
if (url.pathname === '/hooks/tool' && method === 'POST') {
|
|
54
69
|
const raw = await readBody(req);
|
|
@@ -91,6 +106,33 @@ async function handleRequest(req, res) {
|
|
|
91
106
|
}
|
|
92
107
|
return;
|
|
93
108
|
}
|
|
109
|
+
// ── POST /hooks/statusline — proxy statusline render with server-managed auth ──
|
|
110
|
+
// The statusline binary calls this instead of hitting the central server directly.
|
|
111
|
+
// Auth token is held by the local server; the caller needs no credentials.
|
|
112
|
+
if (url.pathname === '/hooks/statusline' && method === 'POST') {
|
|
113
|
+
const raw = await readBody(req);
|
|
114
|
+
try {
|
|
115
|
+
const token = getToken();
|
|
116
|
+
if (!token) {
|
|
117
|
+
json(res, 503, { error: 'Local server not authenticated — run npx gramatr login' });
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
const baseUrl = getServerUrl().replace(/\/mcp\/?$/, '');
|
|
121
|
+
const upstream = await fetch(`${baseUrl}/api/v1/statusline/render`, {
|
|
122
|
+
method: 'POST',
|
|
123
|
+
headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' },
|
|
124
|
+
body: raw,
|
|
125
|
+
signal: AbortSignal.timeout(2000),
|
|
126
|
+
});
|
|
127
|
+
const text = await upstream.text();
|
|
128
|
+
res.writeHead(upstream.status, { 'Content-Type': 'application/json' });
|
|
129
|
+
res.end(text);
|
|
130
|
+
}
|
|
131
|
+
catch (err) {
|
|
132
|
+
json(res, 502, { error: err instanceof Error ? err.message : String(err) });
|
|
133
|
+
}
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
94
136
|
json(res, 404, { error: 'Not found' });
|
|
95
137
|
}
|
|
96
138
|
// ── Lifecycle ──
|
|
@@ -101,13 +143,18 @@ let _server = null;
|
|
|
101
143
|
* Returns the assigned port number.
|
|
102
144
|
*/
|
|
103
145
|
export async function startHooksListener() {
|
|
104
|
-
|
|
105
|
-
const dir = getGramatrDirFromEnv() || join(getHomeDir(), '.gramatr');
|
|
146
|
+
const dir = getGramatrDir();
|
|
106
147
|
try {
|
|
107
148
|
if (!existsSync(dir))
|
|
108
149
|
mkdirSync(dir, { recursive: true });
|
|
109
150
|
}
|
|
110
|
-
catch { /* non-critical
|
|
151
|
+
catch { /* non-critical */ }
|
|
152
|
+
// Generate a fresh per-run secret and persist it so callers can read it.
|
|
153
|
+
_secret = randomBytes(32).toString('hex');
|
|
154
|
+
try {
|
|
155
|
+
writeFileSync(getSecretFilePath(), _secret, { mode: 0o600 });
|
|
156
|
+
}
|
|
157
|
+
catch { /* non-critical — callers will get 401 if they can't read the secret */ }
|
|
111
158
|
return new Promise((resolve, reject) => {
|
|
112
159
|
_server = createServer((req, res) => {
|
|
113
160
|
handleRequest(req, res).catch((err) => {
|
|
@@ -118,27 +165,28 @@ export async function startHooksListener() {
|
|
|
118
165
|
const addr = _server.address();
|
|
119
166
|
const port = typeof addr === 'object' && addr !== null ? addr.port : 0;
|
|
120
167
|
try {
|
|
121
|
-
// mode 0o600: readable only by the current user
|
|
122
168
|
writeFileSync(getPortFilePath(), String(port), { mode: 0o600 });
|
|
123
169
|
}
|
|
124
|
-
catch {
|
|
125
|
-
// Non-critical — hooks will fall back to SQLite + direct remote
|
|
126
|
-
}
|
|
170
|
+
catch { /* non-critical */ }
|
|
127
171
|
resolve(port);
|
|
128
172
|
});
|
|
129
173
|
_server.once('error', reject);
|
|
130
174
|
});
|
|
131
175
|
}
|
|
132
176
|
/**
|
|
133
|
-
* Stop the hooks listener and remove the port
|
|
134
|
-
* Called on server shutdown so stale hooks know to fall back to direct remote.
|
|
177
|
+
* Stop the hooks listener and remove the port + secret files.
|
|
135
178
|
*/
|
|
136
179
|
export function stopHooksListener() {
|
|
137
180
|
_server?.close();
|
|
138
181
|
_server = null;
|
|
182
|
+
_secret = null;
|
|
139
183
|
try {
|
|
140
184
|
unlinkSync(getPortFilePath());
|
|
141
185
|
}
|
|
142
|
-
catch { /* best-effort
|
|
186
|
+
catch { /* best-effort */ }
|
|
187
|
+
try {
|
|
188
|
+
unlinkSync(getSecretFilePath());
|
|
189
|
+
}
|
|
190
|
+
catch { /* best-effort */ }
|
|
143
191
|
}
|
|
144
192
|
//# sourceMappingURL=hooks-listener.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hooks-listener.js","sourceRoot":"","sources":["../../src/server/hooks-listener.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"hooks-listener.js","sourceRoot":"","sources":["../../src/server/hooks-listener.ts"],"names":[],"mappings":"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"}
|
|
@@ -9,7 +9,7 @@ import type { ToolDefinition } from '../proxy/tool-registry.js';
|
|
|
9
9
|
*/
|
|
10
10
|
export declare function getLocalToolDefinitions(): ToolDefinition[];
|
|
11
11
|
/**
|
|
12
|
-
* Check if a tool name is
|
|
12
|
+
* Check if a tool name is handled locally (fully or local-first).
|
|
13
13
|
*/
|
|
14
14
|
export declare function isLocalTool(name: string): boolean;
|
|
15
15
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"local-tools.d.ts","sourceRoot":"","sources":["../../src/tools/local-tools.ts"],"names":[],"mappings":"AAAA;;;GAGG;AASH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC7D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"local-tools.d.ts","sourceRoot":"","sources":["../../src/tools/local-tools.ts"],"names":[],"mappings":"AAAA;;;GAGG;AASH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC7D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAShE;;GAEG;AACH,wBAAgB,uBAAuB,IAAI,cAAc,EAAE,CA+C1D;AAYD;;GAEG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEjD;AAED;;GAEG;AACH,wBAAsB,eAAe,CACnC,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,OAAO,CAAC,cAAc,CAAC,CAuBzB"}
|
|
@@ -10,6 +10,8 @@ import { queueSize } from '../queue/offline-queue.js';
|
|
|
10
10
|
import { getWebToolDefinitions, isWebTool, handleWebTool } from './web-tools.js';
|
|
11
11
|
import { getMetrics, resetMetrics } from '../metrics/collector.js';
|
|
12
12
|
import { VERSION } from '../hooks/lib/version.js';
|
|
13
|
+
import { getLocalProjectBySlug, getLocalProjectByDirectory, listLocalProjects, } from '../hooks/lib/hook-state.js';
|
|
14
|
+
import { callRemoteTool } from '../proxy/remote-client.js';
|
|
13
15
|
/**
|
|
14
16
|
* Local tool definitions — these get merged into tools/list alongside remote tools.
|
|
15
17
|
*/
|
|
@@ -43,15 +45,37 @@ export function getLocalToolDefinitions() {
|
|
|
43
45
|
},
|
|
44
46
|
annotations: { readOnlyHint: true },
|
|
45
47
|
},
|
|
48
|
+
{
|
|
49
|
+
name: 'gramatr_resolve_project',
|
|
50
|
+
description: 'Resolve or list projects. Reads from local SQLite cache first; falls back to server for misses or search. Actions: list (recent projects), resolve (by slug or directory), search (text search — always remote).',
|
|
51
|
+
inputSchema: {
|
|
52
|
+
type: 'object',
|
|
53
|
+
properties: {
|
|
54
|
+
action: { type: 'string', enum: ['list', 'resolve', 'search'], description: 'list | resolve | search' },
|
|
55
|
+
slug: { type: 'string', description: 'Project slug for resolve action.' },
|
|
56
|
+
directory: { type: 'string', description: 'Absolute directory path for resolve action.' },
|
|
57
|
+
query: { type: 'string', description: 'Search string for search action.' },
|
|
58
|
+
limit: { type: 'number', description: 'Max results for list (default 20).' },
|
|
59
|
+
},
|
|
60
|
+
required: ['action'],
|
|
61
|
+
},
|
|
62
|
+
},
|
|
46
63
|
...getWebToolDefinitions(),
|
|
47
64
|
];
|
|
48
65
|
}
|
|
49
66
|
const startTime = Date.now();
|
|
67
|
+
const LOCAL_TOOL_NAMES = new Set([
|
|
68
|
+
'gramatr_local_status',
|
|
69
|
+
'gramatr_local_clear_cache',
|
|
70
|
+
'gramatr_local_config',
|
|
71
|
+
'gramatr_local_metrics',
|
|
72
|
+
'gramatr_resolve_project',
|
|
73
|
+
]);
|
|
50
74
|
/**
|
|
51
|
-
* Check if a tool name is
|
|
75
|
+
* Check if a tool name is handled locally (fully or local-first).
|
|
52
76
|
*/
|
|
53
77
|
export function isLocalTool(name) {
|
|
54
|
-
return name.startsWith('gramatr_local_');
|
|
78
|
+
return name.startsWith('gramatr_local_') || LOCAL_TOOL_NAMES.has(name);
|
|
55
79
|
}
|
|
56
80
|
/**
|
|
57
81
|
* Handle a local tool call.
|
|
@@ -70,6 +94,8 @@ export async function handleLocalTool(toolName, args) {
|
|
|
70
94
|
return handleConfig();
|
|
71
95
|
case 'gramatr_local_metrics':
|
|
72
96
|
return handleMetrics(args);
|
|
97
|
+
case 'gramatr_resolve_project':
|
|
98
|
+
return handleResolveProject(args);
|
|
73
99
|
default:
|
|
74
100
|
return {
|
|
75
101
|
content: [{ type: 'text', text: JSON.stringify({ error: `Unknown local tool: ${toolName}` }) }],
|
|
@@ -159,4 +185,45 @@ function handleMetrics(args) {
|
|
|
159
185
|
content: [{ type: 'text', text: JSON.stringify(snapshot, null, 2) }],
|
|
160
186
|
};
|
|
161
187
|
}
|
|
188
|
+
async function handleResolveProject(args) {
|
|
189
|
+
const action = args.action;
|
|
190
|
+
// list — return all local projects, sorted by updated_at desc
|
|
191
|
+
if (action === 'list') {
|
|
192
|
+
const limit = typeof args.limit === 'number' ? args.limit : 20;
|
|
193
|
+
try {
|
|
194
|
+
const rows = listLocalProjects(limit);
|
|
195
|
+
return {
|
|
196
|
+
content: [{ type: 'text', text: JSON.stringify({ action: 'list', projects: rows, source: 'local', total: rows.length }, null, 2) }],
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
catch {
|
|
200
|
+
// DB unavailable — fall through to remote
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
// resolve — try local first (slug, then directory), then remote on miss
|
|
204
|
+
if (action === 'resolve') {
|
|
205
|
+
const slug = typeof args.slug === 'string' ? args.slug : null;
|
|
206
|
+
const directory = typeof args.directory === 'string' ? args.directory : null;
|
|
207
|
+
let local = slug ? getLocalProjectBySlug(slug) : null;
|
|
208
|
+
if (!local && directory)
|
|
209
|
+
local = getLocalProjectByDirectory(directory);
|
|
210
|
+
if (local) {
|
|
211
|
+
return {
|
|
212
|
+
content: [{ type: 'text', text: JSON.stringify({ action: 'resolve', project: local, source: 'local' }, null, 2) }],
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
// Miss — fall through to remote below
|
|
216
|
+
}
|
|
217
|
+
// search always goes remote; resolve on local miss falls through here
|
|
218
|
+
try {
|
|
219
|
+
const result = await callRemoteTool('gramatr_resolve_project', args);
|
|
220
|
+
return result;
|
|
221
|
+
}
|
|
222
|
+
catch (err) {
|
|
223
|
+
return {
|
|
224
|
+
content: [{ type: 'text', text: JSON.stringify({ error: err instanceof Error ? err.message : String(err) }) }],
|
|
225
|
+
isError: true,
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
}
|
|
162
229
|
//# sourceMappingURL=local-tools.js.map
|