@gramatr/mcp 0.11.12 → 0.11.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.
Files changed (77) hide show
  1. package/dist/bin/gramatr-mcp.d.ts +3 -0
  2. package/dist/bin/gramatr-mcp.d.ts.map +1 -1
  3. package/dist/bin/gramatr-mcp.js +38 -0
  4. package/dist/bin/gramatr-mcp.js.map +1 -1
  5. package/dist/bin/login.d.ts.map +1 -1
  6. package/dist/bin/login.js +2 -0
  7. package/dist/bin/login.js.map +1 -1
  8. package/dist/config-runtime.d.ts +12 -0
  9. package/dist/config-runtime.d.ts.map +1 -1
  10. package/dist/config-runtime.js +18 -0
  11. package/dist/config-runtime.js.map +1 -1
  12. package/dist/daemon/db-path.d.ts +20 -0
  13. package/dist/daemon/db-path.d.ts.map +1 -0
  14. package/dist/daemon/db-path.js +28 -0
  15. package/dist/daemon/db-path.js.map +1 -0
  16. package/dist/daemon/http-server.d.ts +29 -0
  17. package/dist/daemon/http-server.d.ts.map +1 -0
  18. package/dist/daemon/http-server.js +70 -0
  19. package/dist/daemon/http-server.js.map +1 -0
  20. package/dist/daemon/index.d.ts +13 -0
  21. package/dist/daemon/index.d.ts.map +1 -0
  22. package/dist/daemon/index.js +126 -0
  23. package/dist/daemon/index.js.map +1 -0
  24. package/dist/daemon/ipc-protocol.d.ts +56 -0
  25. package/dist/daemon/ipc-protocol.d.ts.map +1 -0
  26. package/dist/daemon/ipc-protocol.js +10 -0
  27. package/dist/daemon/ipc-protocol.js.map +1 -0
  28. package/dist/daemon/project-cache.d.ts +63 -0
  29. package/dist/daemon/project-cache.d.ts.map +1 -0
  30. package/dist/daemon/project-cache.js +139 -0
  31. package/dist/daemon/project-cache.js.map +1 -0
  32. package/dist/daemon/server.d.ts +22 -0
  33. package/dist/daemon/server.d.ts.map +1 -0
  34. package/dist/daemon/server.js +322 -0
  35. package/dist/daemon/server.js.map +1 -0
  36. package/dist/daemon/session-registry.d.ts +26 -0
  37. package/dist/daemon/session-registry.d.ts.map +1 -0
  38. package/dist/daemon/session-registry.js +42 -0
  39. package/dist/daemon/session-registry.js.map +1 -0
  40. package/dist/daemon/sqlite-owner.d.ts +55 -0
  41. package/dist/daemon/sqlite-owner.d.ts.map +1 -0
  42. package/dist/daemon/sqlite-owner.js +133 -0
  43. package/dist/daemon/sqlite-owner.js.map +1 -0
  44. package/dist/daemon/startup.d.ts +51 -0
  45. package/dist/daemon/startup.d.ts.map +1 -0
  46. package/dist/daemon/startup.js +182 -0
  47. package/dist/daemon/startup.js.map +1 -0
  48. package/dist/hooks/generated/hook-timeouts.d.ts +3 -1
  49. package/dist/hooks/generated/hook-timeouts.d.ts.map +1 -1
  50. package/dist/hooks/generated/hook-timeouts.js +3 -1
  51. package/dist/hooks/generated/hook-timeouts.js.map +1 -1
  52. package/dist/hooks/lib/hook-state.d.ts +36 -2
  53. package/dist/hooks/lib/hook-state.d.ts.map +1 -1
  54. package/dist/hooks/lib/hook-state.js +119 -5
  55. package/dist/hooks/lib/hook-state.js.map +1 -1
  56. package/dist/hooks/session-end.d.ts.map +1 -1
  57. package/dist/hooks/session-end.js +48 -21
  58. package/dist/hooks/session-end.js.map +1 -1
  59. package/dist/hooks/session-start.d.ts.map +1 -1
  60. package/dist/hooks/session-start.js +42 -23
  61. package/dist/hooks/session-start.js.map +1 -1
  62. package/dist/proxy/local-client.d.ts +30 -20
  63. package/dist/proxy/local-client.d.ts.map +1 -1
  64. package/dist/proxy/local-client.js +185 -50
  65. package/dist/proxy/local-client.js.map +1 -1
  66. package/dist/proxy/remote-client.d.ts.map +1 -1
  67. package/dist/proxy/remote-client.js +13 -4
  68. package/dist/proxy/remote-client.js.map +1 -1
  69. package/dist/server/auth.d.ts +13 -4
  70. package/dist/server/auth.d.ts.map +1 -1
  71. package/dist/server/auth.js +154 -20
  72. package/dist/server/auth.js.map +1 -1
  73. package/dist/setup/generated/platform-hooks.d.ts +1 -1
  74. package/dist/setup/generated/platform-hooks.d.ts.map +1 -1
  75. package/dist/setup/generated/platform-hooks.js +1 -5
  76. package/dist/setup/generated/platform-hooks.js.map +1 -1
  77. package/package.json +3 -2
@@ -1,60 +1,175 @@
1
1
  /**
2
- * local-client.ts — Hook-side MCP client.
2
+ * local-client.ts — Hook-side daemon IPC client.
3
3
  *
4
- * Spawns `npx -y @gramatr/mcp` via the MCP stdio transport so hooks call
5
- * gramatr tools through the same server that Claude Code uses. Auth and
6
- * session state are handled by that server process (reads from
7
- * ~/.gramatr/settings.json). No direct HTTP calls or in-process proxy.
4
+ * Sprint 1: replaces the previous "spawn npx @gramatr/mcp via StdioClientTransport"
5
+ * model. Tool calls now go through the persistent daemon over a Unix socket (~1ms)
6
+ * instead of spawning a new process (~200-700ms).
8
7
  *
9
- * Connection is lazy-initialized on first callTool and reused for the
10
- * lifetime of the hook process. Falls back to an error result (never
11
- * throws) so hooks always degrade gracefully when the server is unavailable.
8
+ * Call path:
9
+ * callTool(name, args)
10
+ * callViaDaemon() [try first, 8ms connect timeout]
11
+ * → callRemoteTool() [fallback if daemon unavailable]
12
12
  *
13
- * pushSessionContextToLocal / pullSessionContextFromLocal are retained as
14
- * no-ops for call-site compatibility.
13
+ * Connection-per-call pattern is correct here — hooks are short-lived processes
14
+ * and don't benefit from connection pooling.
15
15
  */
16
- import { Client } from '@modelcontextprotocol/sdk/client/index.js';
17
- import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
18
- const SERVER_COMMAND = 'npx';
19
- const SERVER_ARGS = ['-y', '@gramatr/mcp'];
20
- let _client = null;
21
- let _connecting = null;
22
- /** Reset singleton for testing. Not for production use. */
23
- export function _resetClientForTest() {
24
- _client = null;
25
- _connecting = null;
16
+ import { createConnection } from 'node:net';
17
+ import { createInterface } from 'node:readline';
18
+ import { existsSync } from 'node:fs';
19
+ import { callRemoteTool } from './remote-client.js';
20
+ import { getDaemonSocketPath, readHttpCredentials } from '../daemon/startup.js';
21
+ import { DAEMON_UNAVAILABLE } from '../daemon/ipc-protocol.js';
22
+ // ── Request ID counter ──
23
+ let _requestId = 0;
24
+ // ── Socket utilities ──────────────────────────────────────────────────────────
25
+ /**
26
+ * Open a Unix socket connection with a hard timeout.
27
+ * Rejects with ENOENT, ECONNREFUSED, or 'connect timeout' — all map to DAEMON_UNAVAILABLE.
28
+ */
29
+ async function openSocketWithTimeout(path, timeoutMs) {
30
+ return new Promise((resolve, reject) => {
31
+ const socket = createConnection(path);
32
+ const timer = setTimeout(() => {
33
+ socket.destroy();
34
+ reject(new Error('connect timeout'));
35
+ }, timeoutMs);
36
+ socket.once('connect', () => {
37
+ clearTimeout(timer);
38
+ resolve(socket);
39
+ });
40
+ socket.once('error', (err) => {
41
+ clearTimeout(timer);
42
+ reject(err);
43
+ });
44
+ });
26
45
  }
27
- async function getClient() {
28
- if (_client)
29
- return _client;
30
- if (_connecting)
31
- return _connecting;
32
- _connecting = (async () => {
33
- const transport = new StdioClientTransport({
34
- command: SERVER_COMMAND,
35
- args: SERVER_ARGS,
36
- stderr: 'inherit',
46
+ /**
47
+ * Read a single newline-terminated line from the socket, with timeout.
48
+ * Rejects with 'read timeout' or 'socket closed' — both map to DAEMON_UNAVAILABLE.
49
+ */
50
+ async function readOneLine(socket, timeoutMs) {
51
+ return new Promise((resolve, reject) => {
52
+ const rl = createInterface({ input: socket, crlfDelay: Infinity });
53
+ const timer = setTimeout(() => {
54
+ rl.close();
55
+ reject(new Error('read timeout'));
56
+ }, timeoutMs);
57
+ rl.once('line', (line) => {
58
+ clearTimeout(timer);
59
+ rl.close();
60
+ resolve(line);
61
+ });
62
+ rl.once('close', () => {
63
+ clearTimeout(timer);
64
+ reject(new Error('socket closed'));
37
65
  });
38
- const client = new Client({ name: 'gramatr-hook', version: '1.0.0' }, { capabilities: {} });
39
- await client.connect(transport);
40
- _client = client;
41
- _connecting = null;
42
- return client;
43
- })();
44
- return _connecting;
66
+ rl.once('error', (err) => {
67
+ clearTimeout(timer);
68
+ reject(err);
69
+ });
70
+ });
45
71
  }
46
- /** Always true the stdio server is available on demand via npx. */
72
+ // ── Daemon IPC ────────────────────────────────────────────────────────────────
73
+ /**
74
+ * Send a JSON-RPC request to the daemon over the Unix socket.
75
+ * Returns DAEMON_UNAVAILABLE if the socket is unreachable or the call fails.
76
+ */
77
+ export async function callViaDaemon(method, params) {
78
+ const sockPath = getDaemonSocketPath();
79
+ let socket;
80
+ try {
81
+ socket = await openSocketWithTimeout(sockPath, 8);
82
+ const req = {
83
+ jsonrpc: '2.0',
84
+ id: ++_requestId,
85
+ method: method,
86
+ params,
87
+ };
88
+ socket.write(JSON.stringify(req) + '\n');
89
+ const line = await readOneLine(socket, 8000);
90
+ const resp = JSON.parse(line);
91
+ if (resp.error) {
92
+ // gramatr-allow: B1 — standalone proxy package, no @gramatr/core dependency
93
+ throw new Error(resp.error.message);
94
+ }
95
+ return resp.result;
96
+ }
97
+ catch {
98
+ return DAEMON_UNAVAILABLE;
99
+ }
100
+ finally {
101
+ try {
102
+ socket?.destroy();
103
+ }
104
+ catch { /* ignore */ }
105
+ }
106
+ }
107
+ // ── Tier 2: Localhost HTTP fallback ──────────────────────────────────────────
108
+ /**
109
+ * Send a JSON-RPC request to the daemon via localhost HTTP (Tier 2 fallback).
110
+ * Used when the IPC socket is unavailable (e.g. Windows named pipe not supported
111
+ * in the current environment, or socket file missing but daemon is running).
112
+ * Returns DAEMON_UNAVAILABLE if the HTTP server is unreachable or auth fails.
113
+ */
114
+ async function callViaLocalHttp(method, params) {
115
+ const creds = readHttpCredentials();
116
+ if (!creds)
117
+ return DAEMON_UNAVAILABLE;
118
+ try {
119
+ const req = {
120
+ jsonrpc: '2.0',
121
+ id: ++_requestId,
122
+ method: method,
123
+ params,
124
+ };
125
+ const response = await fetch(`http://127.0.0.1:${creds.port}/`, {
126
+ method: 'POST',
127
+ headers: {
128
+ 'Content-Type': 'application/json',
129
+ 'Authorization': `Bearer ${creds.token}`,
130
+ },
131
+ body: JSON.stringify(req),
132
+ signal: AbortSignal.timeout(9_000),
133
+ });
134
+ if (!response.ok)
135
+ return DAEMON_UNAVAILABLE;
136
+ const resp = await response.json();
137
+ if (resp.error) {
138
+ // gramatr-allow: B1 — standalone proxy package, no @gramatr/core dependency
139
+ throw new Error(resp.error.message);
140
+ }
141
+ return resp.result;
142
+ }
143
+ catch {
144
+ return DAEMON_UNAVAILABLE;
145
+ }
146
+ }
147
+ // ── Public API ────────────────────────────────────────────────────────────────
148
+ /**
149
+ * Returns true if the daemon socket file exists.
150
+ * Does NOT guarantee the daemon process is alive — use isDaemonRunning() for that.
151
+ */
47
152
  export function isLocalHooksServerAvailable() {
48
- return true;
153
+ return existsSync(getDaemonSocketPath());
49
154
  }
50
155
  /**
51
- * Call a gramatr tool via the local MCP stdio server.
156
+ * Call a gramatr tool via the daemon (Tier 1 socket → Tier 2 HTTP → Tier 3 remote).
52
157
  * Never throws — returns an isError result on failure so hooks degrade gracefully.
53
158
  */
54
159
  export async function callTool(name, args) {
160
+ // Tier 1: IPC socket (Unix or Windows named pipe)
161
+ const socketResult = await callViaDaemon('tool/call', { name, arguments: args });
162
+ if (socketResult !== DAEMON_UNAVAILABLE) {
163
+ return socketResult;
164
+ }
165
+ // Tier 2: localhost HTTP with shared secret
166
+ const httpResult = await callViaLocalHttp('tool/call', { name, arguments: args });
167
+ if (httpResult !== DAEMON_UNAVAILABLE) {
168
+ return httpResult;
169
+ }
170
+ // Fallback: call the remote MCP server directly
55
171
  try {
56
- const client = await getClient();
57
- const result = await client.callTool({ name, arguments: args });
172
+ const result = await callRemoteTool(name, args);
58
173
  return result;
59
174
  }
60
175
  catch (err) {
@@ -66,17 +181,37 @@ export async function callTool(name, args) {
66
181
  }
67
182
  }
68
183
  /**
69
- * No-op session context is persisted to SQLite via setSessionContext in hook-state.ts.
70
- * Retained for call-site compatibility.
184
+ * Push session context to the daemon's in-memory store so other hooks in the
185
+ * same session can retrieve it without hitting the DB.
186
+ * Returns false if the daemon is unavailable (non-fatal).
71
187
  */
72
- export async function pushSessionContextToLocal(_ctx) {
73
- return false;
188
+ export async function pushSessionContextToLocal(ctx) {
189
+ if (ctx === null || typeof ctx !== 'object')
190
+ return false;
191
+ const sessionId = ctx.session_id;
192
+ if (!sessionId)
193
+ return false;
194
+ const result = await callViaDaemon('session/context/set', {
195
+ session_id: sessionId,
196
+ context: ctx,
197
+ });
198
+ return result !== DAEMON_UNAVAILABLE;
74
199
  }
75
200
  /**
76
- * No-op callers fall through to remote REST hydration via hydrateSessionContextFromServer.
77
- * Retained for call-site compatibility.
201
+ * Pull session context from the daemon's in-memory store.
202
+ * Returns null if the daemon is unavailable or has no context for this session.
78
203
  */
79
- export async function pullSessionContextFromLocal(_sessionId) {
80
- return null;
204
+ export async function pullSessionContextFromLocal(sessionId) {
205
+ const result = await callViaDaemon('session/context/get', { session_id: sessionId });
206
+ if (result === DAEMON_UNAVAILABLE)
207
+ return null;
208
+ const typed = result;
209
+ if (typed.value === null || typed.value === undefined)
210
+ return null;
211
+ return typed.value;
212
+ }
213
+ /** Reset module-level request ID counter for testing. Not for production use. */
214
+ export function _resetClientForTest() {
215
+ _requestId = 0;
81
216
  }
82
217
  //# sourceMappingURL=local-client.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"local-client.js","sourceRoot":"","sources":["../../src/proxy/local-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AAKjF,MAAM,cAAc,GAAG,KAAK,CAAC;AAC7B,MAAM,WAAW,GAAG,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC;AAE3C,IAAI,OAAO,GAAkB,IAAI,CAAC;AAClC,IAAI,WAAW,GAA2B,IAAI,CAAC;AAE/C,2DAA2D;AAC3D,MAAM,UAAU,mBAAmB;IACjC,OAAO,GAAG,IAAI,CAAC;IACf,WAAW,GAAG,IAAI,CAAC;AACrB,CAAC;AAED,KAAK,UAAU,SAAS;IACtB,IAAI,OAAO;QAAE,OAAO,OAAO,CAAC;IAC5B,IAAI,WAAW;QAAE,OAAO,WAAW,CAAC;IAEpC,WAAW,GAAG,CAAC,KAAK,IAAI,EAAE;QACxB,MAAM,SAAS,GAAG,IAAI,oBAAoB,CAAC;YACzC,OAAO,EAAE,cAAc;YACvB,IAAI,EAAE,WAAW;YACjB,MAAM,EAAE,SAAS;SAClB,CAAC,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,MAAM,CACvB,EAAE,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,OAAO,EAAE,EAC1C,EAAE,YAAY,EAAE,EAAE,EAAE,CACrB,CAAC;QACF,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QAChC,OAAO,GAAG,MAAM,CAAC;QACjB,WAAW,GAAG,IAAI,CAAC;QACnB,OAAO,MAAM,CAAC;IAChB,CAAC,CAAC,EAAE,CAAC;IAEL,OAAO,WAAW,CAAC;AACrB,CAAC;AAED,qEAAqE;AACrE,MAAM,UAAU,2BAA2B;IACzC,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,QAAQ,CAC5B,IAAY,EACZ,IAA6B;IAE7B,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,SAAS,EAAE,CAAC;QACjC,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAChE,OAAO,MAAwB,CAAC;IAClC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACjE,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,oCAAoC,OAAO,EAAE,EAAE,CAAC;YAChF,OAAO,EAAE,IAAI;SACd,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAAC,IAAa;IAC3D,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,2BAA2B,CAC/C,UAAkB;IAElB,OAAO,IAAI,CAAC;AACd,CAAC"}
1
+ {"version":3,"file":"local-client.js","sourceRoot":"","sources":["../../src/proxy/local-client.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"remote-client.d.ts","sourceRoot":"","sources":["../../src/proxy/remote-client.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AA6BH;;GAEG;AACH,wBAAsB,cAAc,CAClC,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,OAAO,CAAC,OAAO,CAAC,CA4BlB;AAED;;GAEG;AACH,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,OAAO,CAAC,CAqB5D;AAED;;GAEG;AACH,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,OAAO,CAAC,CAqB9D;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,CAqBrG;AAED;;GAEG;AACH,wBAAsB,uBAAuB,IAAI,OAAO,CAAC,OAAO,CAAC,CAqBhE;AAED;;GAEG;AACH,wBAAsB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAqBvE"}
1
+ {"version":3,"file":"remote-client.d.ts","sourceRoot":"","sources":["../../src/proxy/remote-client.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AA6BH;;GAEG;AACH,wBAAsB,cAAc,CAClC,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,OAAO,CAAC,OAAO,CAAC,CAqClB;AAED;;GAEG;AACH,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,OAAO,CAAC,CAqB5D;AAED;;GAEG;AACH,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,OAAO,CAAC,CAqB9D;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,CAqBrG;AAED;;GAEG;AACH,wBAAsB,uBAAuB,IAAI,OAAO,CAAC,OAAO,CAAC,CAqBhE;AAED;;GAEG;AACH,wBAAsB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAqBvE"}
@@ -5,7 +5,7 @@
5
5
  * All requests include the Bearer token from auth.ts.
6
6
  * Handles 401 by refreshing the token once and retrying.
7
7
  */
8
- import { getToken, refreshToken, getServerUrl } from '../server/auth.js';
8
+ import { getToken, refreshToken, renewToken, getServerUrl } from '../server/auth.js';
9
9
  import { REMOTE_TOOL_CALL_TIMEOUT_MS } from '../hooks/generated/hook-timeouts.js';
10
10
  const DEBUG = !!process.env.GRAMATR_DEBUG;
11
11
  function debugLog(label, data) {
@@ -26,10 +26,19 @@ export async function callRemoteTool(toolName, args) {
26
26
  params: { name: toolName, arguments: args },
27
27
  };
28
28
  let response = await postToRemote(payload);
29
- // Handle 401 — refresh token and retry once
29
+ // Handle 401 — (1) re-read disk, (2) server renewal, (3) give up
30
30
  if (response.status === 401) {
31
- refreshToken();
32
- response = await postToRemote(payload);
31
+ const refreshed = refreshToken(); // re-reads disk; returns null if token expired
32
+ if (refreshed) {
33
+ response = await postToRemote(payload);
34
+ }
35
+ // Disk token was also expired — try server-side renewal (#526)
36
+ if (response.status === 401) {
37
+ const renewed = await renewToken();
38
+ if (renewed) {
39
+ response = await postToRemote(payload);
40
+ }
41
+ }
33
42
  }
34
43
  if (!response.ok) {
35
44
  // gramatr-allow: B1 — standalone proxy package, no @gramatr/core dependency
@@ -1 +1 @@
1
- {"version":3,"file":"remote-client.js","sourceRoot":"","sources":["../../src/proxy/remote-client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"remote-client.js","sourceRoot":"","sources":["../../src/proxy/remote-client.ts"],"names":[],"mappings":"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"}
@@ -9,20 +9,29 @@
9
9
  *
10
10
  * Injects as Authorization: Bearer on every proxied request.
11
11
  * Re-reads on 401 from remote (token may have been refreshed).
12
+ * Auto-renews when token is within RENEWAL_WINDOW_MS of expiry (#526).
12
13
  */
13
14
  /**
14
- * Read the auth token from ~/.gramatr.json.
15
- * Caches in memory call refreshToken() to force re-read.
15
+ * Read the auth token. Returns null if token is expired.
16
+ * Triggers background renewal when token is near expiry.
16
17
  */
17
18
  export declare function getToken(): string | null;
18
19
  /**
19
20
  * Force re-read of the auth token from disk.
20
- * Called on startup and on 401 from remote.
21
+ * Called on 401 from remote and by getToken() on first call or expiry.
21
22
  */
22
23
  export declare function refreshToken(): string | null;
24
+ /**
25
+ * Explicitly renew the access token via POST /auth/token/renew.
26
+ * Writes the new token to disk and updates in-memory cache on success.
27
+ * Returns the new token, or null if renewal failed.
28
+ * Used by remote-client.ts on 401 when disk refresh also yields null.
29
+ */
30
+ export declare function renewToken(): Promise<string | null>;
23
31
  /**
24
32
  * Get the remote server base URL.
25
- * Reads from env or config file.
26
33
  */
27
34
  export declare function getServerUrl(): string;
35
+ /** Exposed for testing only. */
36
+ export declare function _resetCacheForTest(): void;
28
37
  //# sourceMappingURL=auth.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/server/auth.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAuCH;;;GAGG;AACH,wBAAgB,QAAQ,IAAI,MAAM,GAAG,IAAI,CAGxC;AAED;;;GAGG;AACH,wBAAgB,YAAY,IAAI,MAAM,GAAG,IAAI,CAgB5C;AAED;;;GAGG;AACH,wBAAgB,YAAY,IAAI,MAAM,CAQrC"}
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/server/auth.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAgEH;;;GAGG;AACH,wBAAgB,QAAQ,IAAI,MAAM,GAAG,IAAI,CA4BxC;AAED;;;GAGG;AACH,wBAAgB,YAAY,IAAI,MAAM,GAAG,IAAI,CAqB5C;AAED;;;;;GAKG;AACH,wBAAsB,UAAU,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAgCzD;AA+CD;;GAEG;AACH,wBAAgB,YAAY,IAAI,MAAM,CAOrC;AAED,gCAAgC;AAChC,wBAAgB,kBAAkB,IAAI,IAAI,CAKzC"}
@@ -9,18 +9,24 @@
9
9
  *
10
10
  * Injects as Authorization: Bearer on every proxied request.
11
11
  * Re-reads on 401 from remote (token may have been refreshed).
12
+ * Auto-renews when token is within RENEWAL_WINDOW_MS of expiry (#526).
12
13
  */
13
- import { readFileSync } from 'node:fs';
14
+ import { readFileSync, writeFileSync } from 'node:fs';
14
15
  import { dirname, join } from 'node:path';
15
16
  import { getGramatrDirFromEnv, getGramatrTokenFromEnv, getGramatrUrlFromEnv, getHomeDir, } from '../config-runtime.js';
17
+ /** Emit renewal warnings once per process to avoid log spam. */
18
+ const WARNED_EXPIRY = new Set();
19
+ /** Start renewing 7 days before expiry. */
20
+ const RENEWAL_WINDOW_MS = 7 * 24 * 60 * 60 * 1000;
16
21
  let cachedToken = null;
22
+ let cachedExpiresAt = null;
23
+ let renewalInProgress = false;
17
24
  function getConfigPath() {
18
25
  const gramatrDir = getGramatrDirFromEnv();
19
26
  if (gramatrDir) {
20
27
  return join(dirname(gramatrDir), '.gramatr.json');
21
28
  }
22
- const home = getHomeDir();
23
- return join(home, '.gramatr.json');
29
+ return join(getHomeDir(), '.gramatr.json');
24
30
  }
25
31
  function readConfig() {
26
32
  try {
@@ -31,37 +37,158 @@ function readConfig() {
31
37
  return null;
32
38
  }
33
39
  }
40
+ function writeConfig(config) {
41
+ try {
42
+ writeFileSync(getConfigPath(), JSON.stringify(config, null, 2), { mode: 0o600 });
43
+ }
44
+ catch {
45
+ // Non-fatal — token renewal succeeds even if we can't persist
46
+ }
47
+ }
48
+ function isExpired(expiresAt) {
49
+ if (expiresAt === null)
50
+ return false;
51
+ return Date.now() >= expiresAt;
52
+ }
53
+ function isNearExpiry(expiresAt) {
54
+ if (expiresAt === null)
55
+ return false;
56
+ return Date.now() >= expiresAt - RENEWAL_WINDOW_MS;
57
+ }
34
58
  /**
35
- * Read the auth token from ~/.gramatr.json.
36
- * Caches in memory call refreshToken() to force re-read.
59
+ * Read the auth token. Returns null if token is expired.
60
+ * Triggers background renewal when token is near expiry.
37
61
  */
38
62
  export function getToken() {
39
- if (cachedToken)
40
- return cachedToken;
41
- return refreshToken();
63
+ // Env vars take priority and never expire
64
+ const envKey = process.env.GRAMATR_API_KEY;
65
+ if (envKey)
66
+ return envKey;
67
+ const envToken = getGramatrTokenFromEnv();
68
+ if (envToken)
69
+ return envToken;
70
+ // If cached token is expired, force re-read
71
+ if (cachedToken && isExpired(cachedExpiresAt)) {
72
+ cachedToken = null;
73
+ cachedExpiresAt = null;
74
+ }
75
+ if (!cachedToken) {
76
+ refreshToken();
77
+ }
78
+ // Trigger background renewal if near expiry (non-blocking)
79
+ if (cachedToken && isNearExpiry(cachedExpiresAt) && !renewalInProgress) {
80
+ const tokenSnap = cachedToken;
81
+ if (!WARNED_EXPIRY.has(tokenSnap.slice(-8))) {
82
+ WARNED_EXPIRY.add(tokenSnap.slice(-8));
83
+ process.stderr.write('[gramatr] Token nearing expiry — renewing in background\n');
84
+ }
85
+ void renewTokenBackground();
86
+ }
87
+ return cachedToken;
42
88
  }
43
89
  /**
44
90
  * Force re-read of the auth token from disk.
45
- * Called on startup and on 401 from remote.
91
+ * Called on 401 from remote and by getToken() on first call or expiry.
46
92
  */
47
93
  export function refreshToken() {
48
- const envKey = process.env.GRAMATR_API_KEY;
49
- if (envKey) {
50
- cachedToken = envKey;
51
- return cachedToken;
94
+ const config = readConfig();
95
+ if (!config?.token) {
96
+ cachedToken = null;
97
+ cachedExpiresAt = null;
98
+ return null;
52
99
  }
53
- const envToken = getGramatrTokenFromEnv();
54
- if (envToken) {
55
- cachedToken = envToken;
56
- return cachedToken;
100
+ const expiresAt = config.token_expires_at ? Date.parse(config.token_expires_at) : null;
101
+ // Don't cache an expired disk token
102
+ if (isExpired(expiresAt)) {
103
+ cachedToken = null;
104
+ cachedExpiresAt = null;
105
+ process.stderr.write('[gramatr] Stored token is expired — run `npx @gramatr/mcp login` to re-authenticate\n');
106
+ return null;
57
107
  }
58
- const config = readConfig();
59
- cachedToken = config?.token || null;
108
+ cachedToken = config.token;
109
+ cachedExpiresAt = expiresAt;
60
110
  return cachedToken;
61
111
  }
112
+ /**
113
+ * Explicitly renew the access token via POST /auth/token/renew.
114
+ * Writes the new token to disk and updates in-memory cache on success.
115
+ * Returns the new token, or null if renewal failed.
116
+ * Used by remote-client.ts on 401 when disk refresh also yields null.
117
+ */
118
+ export async function renewToken() {
119
+ const currentToken = cachedToken ?? readConfig()?.token;
120
+ if (!currentToken)
121
+ return null;
122
+ try {
123
+ const serverUrl = getServerUrl();
124
+ const response = await fetch(`${serverUrl}/auth/token/renew`, {
125
+ method: 'POST',
126
+ headers: { Authorization: `Bearer ${currentToken}` },
127
+ signal: AbortSignal.timeout(10_000),
128
+ });
129
+ if (!response.ok)
130
+ return null;
131
+ const body = await response.json();
132
+ if (!body.access_token)
133
+ return null;
134
+ const expiresIn = body.expires_in ?? 31536000;
135
+ const newExpiresAt = new Date(Date.now() + expiresIn * 1000).toISOString();
136
+ const config = readConfig() ?? {};
137
+ config.token = body.access_token;
138
+ config.token_expires_at = newExpiresAt;
139
+ writeConfig(config);
140
+ cachedToken = body.access_token;
141
+ cachedExpiresAt = Date.parse(newExpiresAt);
142
+ renewalInProgress = false;
143
+ return cachedToken;
144
+ }
145
+ catch {
146
+ return null;
147
+ }
148
+ }
149
+ /**
150
+ * Attempt to renew the access token via POST /auth/token/renew.
151
+ * Writes the new token to disk on success. Non-throwing.
152
+ */
153
+ async function renewTokenBackground() {
154
+ if (renewalInProgress || !cachedToken)
155
+ return;
156
+ renewalInProgress = true;
157
+ try {
158
+ const serverUrl = getServerUrl();
159
+ const response = await fetch(`${serverUrl}/auth/token/renew`, {
160
+ method: 'POST',
161
+ headers: { Authorization: `Bearer ${cachedToken}` },
162
+ signal: AbortSignal.timeout(10_000),
163
+ });
164
+ if (!response.ok) {
165
+ process.stderr.write(`[gramatr] Token renewal failed (HTTP ${response.status}) — re-login may be required\n`);
166
+ return;
167
+ }
168
+ const body = await response.json();
169
+ if (!body.access_token)
170
+ return;
171
+ const expiresIn = body.expires_in ?? 31536000;
172
+ const newExpiresAt = new Date(Date.now() + expiresIn * 1000).toISOString();
173
+ // Update disk config with new token
174
+ const config = readConfig() ?? {};
175
+ config.token = body.access_token;
176
+ config.token_expires_at = newExpiresAt;
177
+ writeConfig(config);
178
+ // Update in-memory cache
179
+ cachedToken = body.access_token;
180
+ cachedExpiresAt = Date.parse(newExpiresAt);
181
+ process.stderr.write('[gramatr] Token renewed successfully\n');
182
+ }
183
+ catch {
184
+ // Non-fatal — will retry on next call
185
+ }
186
+ finally {
187
+ renewalInProgress = false;
188
+ }
189
+ }
62
190
  /**
63
191
  * Get the remote server base URL.
64
- * Reads from env or config file.
65
192
  */
66
193
  export function getServerUrl() {
67
194
  const envUrl = getGramatrUrlFromEnv();
@@ -71,4 +198,11 @@ export function getServerUrl() {
71
198
  const config = readConfig();
72
199
  return (config?.server_url || 'https://api.gramatr.com').replace(/\/mcp\/?$/, '');
73
200
  }
201
+ /** Exposed for testing only. */
202
+ export function _resetCacheForTest() {
203
+ cachedToken = null;
204
+ cachedExpiresAt = null;
205
+ renewalInProgress = false;
206
+ WARNED_EXPIRY.clear();
207
+ }
74
208
  //# sourceMappingURL=auth.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/server/auth.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EACL,oBAAoB,EACpB,sBAAsB,EACtB,oBAAoB,EACpB,UAAU,GACX,MAAM,sBAAsB,CAAC;AAS9B,IAAI,WAAW,GAAkB,IAAI,CAAC;AAEtC,SAAS,aAAa;IACpB,MAAM,UAAU,GAAG,oBAAoB,EAAE,CAAC;IAC1C,IAAI,UAAU,EAAE,CAAC;QACf,OAAO,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,eAAe,CAAC,CAAC;IACpD,CAAC;IAED,MAAM,IAAI,GAAG,UAAU,EAAE,CAAC;IAC1B,OAAO,IAAI,CAAC,IAAI,EAAE,eAAe,CAAC,CAAC;AACrC,CAAC;AAED,SAAS,UAAU;IACjB,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,YAAY,CAAC,aAAa,EAAE,EAAE,MAAM,CAAC,CAAC;QAClD,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAkB,CAAC;IAC1C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,QAAQ;IACtB,IAAI,WAAW;QAAE,OAAO,WAAW,CAAC;IACpC,OAAO,YAAY,EAAE,CAAC;AACxB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,YAAY;IAC1B,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC;IAC3C,IAAI,MAAM,EAAE,CAAC;QACX,WAAW,GAAG,MAAM,CAAC;QACrB,OAAO,WAAW,CAAC;IACrB,CAAC;IAED,MAAM,QAAQ,GAAG,sBAAsB,EAAE,CAAC;IAC1C,IAAI,QAAQ,EAAE,CAAC;QACb,WAAW,GAAG,QAAQ,CAAC;QACvB,OAAO,WAAW,CAAC;IACrB,CAAC;IAED,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAC5B,WAAW,GAAG,MAAM,EAAE,KAAK,IAAI,IAAI,CAAC;IACpC,OAAO,WAAW,CAAC;AACrB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,YAAY;IAC1B,MAAM,MAAM,GAAG,oBAAoB,EAAE,CAAC;IACtC,IAAI,MAAM,EAAE,CAAC;QACX,OAAO,MAAM,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;IACzC,CAAC;IAED,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAC5B,OAAO,CAAC,MAAM,EAAE,UAAU,IAAI,yBAAyB,CAAC,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;AACpF,CAAC"}
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/server/auth.ts"],"names":[],"mappings":"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"}
@@ -2,7 +2,7 @@
2
2
  * AUTO-GENERATED — do not edit manually.
3
3
  * Source: contracts/platforms/*.yaml
4
4
  * Generator: contracts/generator/generate.ts
5
- * Generated: 2026-04-20T22:10:09.297Z
5
+ * Generated: 2026-04-21T15:46:48.865Z
6
6
  */
7
7
  export interface PlatformHookEntry {
8
8
  hook: string;