@jshookmcp/jshook 0.1.5 → 0.1.7

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 (61) hide show
  1. package/README.md +72 -40
  2. package/README.zh.md +77 -40
  3. package/dist/constants.d.ts +1 -0
  4. package/dist/constants.js +13 -1
  5. package/dist/index.js +0 -0
  6. package/dist/modules/analyzer/IntelligentAnalyzer.js +19 -11
  7. package/dist/modules/browser/BrowserModeManager.d.ts +5 -0
  8. package/dist/modules/browser/BrowserModeManager.js +96 -10
  9. package/dist/modules/browser/CamoufoxBrowserManager.d.ts +4 -0
  10. package/dist/modules/browser/CamoufoxBrowserManager.js +64 -3
  11. package/dist/modules/browser/TabRegistry.js +3 -2
  12. package/dist/modules/browser/UnifiedBrowserManager.d.ts +5 -0
  13. package/dist/modules/browser/UnifiedBrowserManager.js +62 -9
  14. package/dist/modules/debugger/DebuggerSessionManager.d.ts +4 -0
  15. package/dist/modules/debugger/DebuggerSessionManager.js +29 -19
  16. package/dist/modules/debugger/ScriptManager.impl.class.d.ts +4 -0
  17. package/dist/modules/debugger/ScriptManager.impl.class.js +46 -21
  18. package/dist/modules/emulator/EnvironmentEmulator.js +2 -2
  19. package/dist/modules/monitor/NetworkMonitor.impl.d.ts +1 -0
  20. package/dist/modules/monitor/NetworkMonitor.impl.js +22 -15
  21. package/dist/modules/monitor/PerformanceMonitor.js +64 -32
  22. package/dist/modules/process/LinuxProcessManager.d.ts +3 -1
  23. package/dist/modules/process/LinuxProcessManager.js +7 -3
  24. package/dist/modules/process/MacProcessManager.d.ts +3 -1
  25. package/dist/modules/process/MacProcessManager.js +7 -3
  26. package/dist/modules/process/ProcessManager.impl.d.ts +5 -1
  27. package/dist/modules/process/ProcessManager.impl.js +54 -13
  28. package/dist/modules/process/index.d.ts +3 -1
  29. package/dist/modules/process/index.js +2 -2
  30. package/dist/modules/process/memory/AuditTrail.d.ts +25 -0
  31. package/dist/modules/process/memory/AuditTrail.js +44 -0
  32. package/dist/modules/process/memory/linux/mapsParser.d.ts +16 -0
  33. package/dist/modules/process/memory/linux/mapsParser.js +28 -0
  34. package/dist/modules/process/memory/regions.enumerate.js +45 -1
  35. package/dist/modules/process/memory/regions.protection.js +48 -2
  36. package/dist/modules/process/memory/scanner.d.ts +4 -1
  37. package/dist/modules/process/memory/scanner.js +225 -24
  38. package/dist/native/NativeMemoryManager.impl.d.ts +4 -0
  39. package/dist/native/NativeMemoryManager.impl.js +72 -24
  40. package/dist/native/NativeMemoryManager.utils.d.ts +1 -0
  41. package/dist/native/NativeMemoryManager.utils.js +44 -1
  42. package/dist/server/MCPServer.search.d.ts +3 -0
  43. package/dist/server/MCPServer.search.js +21 -2
  44. package/dist/server/ToolCallContextGuard.d.ts +2 -0
  45. package/dist/server/ToolCallContextGuard.js +29 -14
  46. package/dist/server/ToolSearch.js +11 -5
  47. package/dist/server/domains/browser/handlers/tab-workflow.js +6 -4
  48. package/dist/server/domains/maintenance/handlers.extensions.js +46 -26
  49. package/dist/server/domains/process/definitions.js +20 -7
  50. package/dist/server/domains/process/handlers.impl.core.runtime.base.d.ts +35 -0
  51. package/dist/server/domains/process/handlers.impl.core.runtime.base.js +107 -1
  52. package/dist/server/domains/process/handlers.impl.core.runtime.inject.js +111 -2
  53. package/dist/server/domains/process/handlers.impl.core.runtime.memory.d.ts +9 -0
  54. package/dist/server/domains/process/handlers.impl.core.runtime.memory.js +282 -31
  55. package/dist/server/domains/process/manifest.js +1 -0
  56. package/dist/server/domains/workflow/handlers.impl.workflow-api.js +14 -4
  57. package/dist/server/registry/discovery.js +17 -12
  58. package/dist/server/registry/index.js +10 -2
  59. package/dist/utils/TokenBudgetManager.d.ts +1 -0
  60. package/dist/utils/TokenBudgetManager.js +22 -0
  61. package/package.json +28 -41
@@ -1,5 +1,8 @@
1
+ import type { AuditEntry } from '../../../modules/process/memory/AuditTrail.js';
1
2
  import { ProcessToolHandlersBase } from '../../domains/process/handlers.impl.core.runtime.base.js';
2
3
  export declare class ProcessToolHandlersMemory extends ProcessToolHandlersBase {
4
+ private safeBuildMemoryDiagnostics;
5
+ protected recordMemoryAudit(entry: Omit<AuditEntry, 'timestamp' | 'user'>): void;
3
6
  handleMemoryRead(args: Record<string, unknown>): Promise<{
4
7
  content: {
5
8
  type: string;
@@ -18,6 +21,12 @@ export declare class ProcessToolHandlersMemory extends ProcessToolHandlersBase {
18
21
  text: string;
19
22
  }[];
20
23
  }>;
24
+ handleMemoryAuditExport(args: Record<string, unknown>): Promise<{
25
+ content: {
26
+ type: string;
27
+ text: string;
28
+ }[];
29
+ }>;
21
30
  handleMemoryCheckProtection(args: Record<string, unknown>): Promise<{
22
31
  content: {
23
32
  type: string;
@@ -14,14 +14,67 @@ function normalizePatternType(value) {
14
14
  }
15
15
  return 'hex';
16
16
  }
17
+ function getOptionalPid(value) {
18
+ const pid = Number(value);
19
+ return Number.isInteger(pid) && pid > 0 ? pid : undefined;
20
+ }
21
+ function getOptionalString(value) {
22
+ return typeof value === 'string' && value.length > 0 ? value : undefined;
23
+ }
24
+ function getOptionalPositiveNumber(value) {
25
+ const size = Number(value);
26
+ return Number.isFinite(size) && size > 0 ? size : undefined;
27
+ }
28
+ function getWriteSize(data, encoding) {
29
+ if (encoding === 'hex') {
30
+ const normalized = data.replace(/\s+/g, '');
31
+ return Math.ceil(normalized.length / 2);
32
+ }
33
+ return Buffer.from(data, 'base64').length;
34
+ }
17
35
  export class ProcessToolHandlersMemory extends ProcessToolHandlersBase {
36
+ async safeBuildMemoryDiagnostics(input) {
37
+ try {
38
+ return await this.buildMemoryDiagnostics(input);
39
+ }
40
+ catch (diagnosticError) {
41
+ logger.warn('Memory diagnostics generation failed:', diagnosticError);
42
+ return undefined;
43
+ }
44
+ }
45
+ recordMemoryAudit(entry) {
46
+ try {
47
+ this.auditTrail.record(entry);
48
+ }
49
+ catch (auditError) {
50
+ logger.warn('Memory audit trail recording failed:', auditError);
51
+ }
52
+ }
18
53
  async handleMemoryRead(args) {
54
+ const startedAt = Date.now();
19
55
  try {
20
56
  const pid = validatePid(args.pid);
21
57
  const address = requireString(args.address, 'address');
22
58
  const size = requirePositiveNumber(args.size, 'size');
23
59
  const availability = await this.memoryManager.checkAvailability();
24
60
  if (!availability.available) {
61
+ const errorMessage = availability.reason ?? 'Memory operations not available';
62
+ const diagnostics = await this.safeBuildMemoryDiagnostics({
63
+ pid,
64
+ address,
65
+ size,
66
+ operation: 'memory_read',
67
+ error: errorMessage,
68
+ });
69
+ this.recordMemoryAudit({
70
+ operation: 'memory_read',
71
+ pid,
72
+ address,
73
+ size,
74
+ result: 'failure',
75
+ error: errorMessage,
76
+ durationMs: Date.now() - startedAt,
77
+ });
25
78
  return {
26
79
  content: [
27
80
  {
@@ -34,38 +87,82 @@ export class ProcessToolHandlersMemory extends ProcessToolHandlersBase {
34
87
  requestedAddress: address,
35
88
  requestedSize: size,
36
89
  pid,
90
+ diagnostics,
37
91
  }, null, 2),
38
92
  },
39
93
  ],
40
94
  };
41
95
  }
42
96
  const result = await this.memoryManager.readMemory(pid, address, size);
97
+ const diagnostics = !result.success
98
+ ? await this.safeBuildMemoryDiagnostics({
99
+ pid,
100
+ address,
101
+ size,
102
+ operation: 'memory_read',
103
+ error: result.error,
104
+ })
105
+ : undefined;
106
+ this.recordMemoryAudit({
107
+ operation: 'memory_read',
108
+ pid,
109
+ address,
110
+ size,
111
+ result: result.success ? 'success' : 'failure',
112
+ error: result.error,
113
+ durationMs: Date.now() - startedAt,
114
+ });
115
+ const payload = {
116
+ success: result.success,
117
+ data: result.data,
118
+ error: result.error,
119
+ pid,
120
+ address,
121
+ size,
122
+ platform: this.platform,
123
+ };
124
+ if (!result.success) {
125
+ payload.diagnostics = diagnostics;
126
+ }
43
127
  return {
44
128
  content: [
45
129
  {
46
130
  type: 'text',
47
- text: JSON.stringify({
48
- success: result.success,
49
- data: result.data,
50
- error: result.error,
51
- pid,
52
- address,
53
- size,
54
- platform: this.platform,
55
- }, null, 2),
131
+ text: JSON.stringify(payload, null, 2),
56
132
  },
57
133
  ],
58
134
  };
59
135
  }
60
136
  catch (error) {
61
137
  logger.error('Memory read failed:', error);
138
+ const errorMessage = error instanceof Error ? error.message : String(error);
139
+ const pid = getOptionalPid(args.pid);
140
+ const address = getOptionalString(args.address);
141
+ const size = getOptionalPositiveNumber(args.size);
142
+ const diagnostics = await this.safeBuildMemoryDiagnostics({
143
+ pid,
144
+ address,
145
+ size,
146
+ operation: 'memory_read',
147
+ error: errorMessage,
148
+ });
149
+ this.recordMemoryAudit({
150
+ operation: 'memory_read',
151
+ pid: pid ?? null,
152
+ address: address ?? null,
153
+ size: size ?? null,
154
+ result: 'failure',
155
+ error: errorMessage,
156
+ durationMs: Date.now() - startedAt,
157
+ });
62
158
  return {
63
159
  content: [
64
160
  {
65
161
  type: 'text',
66
162
  text: JSON.stringify({
67
163
  success: false,
68
- error: error instanceof Error ? error.message : String(error),
164
+ error: errorMessage,
165
+ diagnostics,
69
166
  }, null, 2),
70
167
  },
71
168
  ],
@@ -73,13 +170,32 @@ export class ProcessToolHandlersMemory extends ProcessToolHandlersBase {
73
170
  }
74
171
  }
75
172
  async handleMemoryWrite(args) {
173
+ const startedAt = Date.now();
76
174
  try {
77
175
  const pid = validatePid(args.pid);
78
176
  const address = requireString(args.address, 'address');
79
177
  const data = requireString(args.data, 'data');
80
178
  const encoding = args.encoding || 'hex';
179
+ const size = getWriteSize(data, encoding);
81
180
  const availability = await this.memoryManager.checkAvailability();
82
181
  if (!availability.available) {
182
+ const errorMessage = availability.reason ?? 'Memory operations not available';
183
+ const diagnostics = await this.safeBuildMemoryDiagnostics({
184
+ pid,
185
+ address,
186
+ size,
187
+ operation: 'memory_write',
188
+ error: errorMessage,
189
+ });
190
+ this.recordMemoryAudit({
191
+ operation: 'memory_write',
192
+ pid,
193
+ address,
194
+ size,
195
+ result: 'failure',
196
+ error: errorMessage,
197
+ durationMs: Date.now() - startedAt,
198
+ });
83
199
  return {
84
200
  content: [
85
201
  {
@@ -93,39 +209,85 @@ export class ProcessToolHandlersMemory extends ProcessToolHandlersBase {
93
209
  dataLength: data != null ? data.length : 0,
94
210
  encoding,
95
211
  pid,
212
+ diagnostics,
96
213
  }, null, 2),
97
214
  },
98
215
  ],
99
216
  };
100
217
  }
101
218
  const result = await this.memoryManager.writeMemory(pid, address, data, encoding);
219
+ const diagnostics = !result.success
220
+ ? await this.safeBuildMemoryDiagnostics({
221
+ pid,
222
+ address,
223
+ size,
224
+ operation: 'memory_write',
225
+ error: result.error,
226
+ })
227
+ : undefined;
228
+ this.recordMemoryAudit({
229
+ operation: 'memory_write',
230
+ pid,
231
+ address,
232
+ size,
233
+ result: result.success ? 'success' : 'failure',
234
+ error: result.error,
235
+ durationMs: Date.now() - startedAt,
236
+ });
237
+ const payload = {
238
+ success: result.success,
239
+ bytesWritten: result.bytesWritten,
240
+ error: result.error,
241
+ pid,
242
+ address,
243
+ dataLength: data.length,
244
+ encoding,
245
+ platform: this.platform,
246
+ };
247
+ if (!result.success) {
248
+ payload.diagnostics = diagnostics;
249
+ }
102
250
  return {
103
251
  content: [
104
252
  {
105
253
  type: 'text',
106
- text: JSON.stringify({
107
- success: result.success,
108
- bytesWritten: result.bytesWritten,
109
- error: result.error,
110
- pid,
111
- address,
112
- dataLength: data.length,
113
- encoding,
114
- platform: this.platform,
115
- }, null, 2),
254
+ text: JSON.stringify(payload, null, 2),
116
255
  },
117
256
  ],
118
257
  };
119
258
  }
120
259
  catch (error) {
121
260
  logger.error('Memory write failed:', error);
261
+ const errorMessage = error instanceof Error ? error.message : String(error);
262
+ const pid = getOptionalPid(args.pid);
263
+ const address = getOptionalString(args.address);
264
+ const data = getOptionalString(args.data);
265
+ const encoding = args.encoding || 'hex';
266
+ const size = data ? getWriteSize(data, encoding) : undefined;
267
+ const diagnostics = await this.safeBuildMemoryDiagnostics({
268
+ pid,
269
+ address,
270
+ size,
271
+ operation: 'memory_write',
272
+ error: errorMessage,
273
+ });
274
+ this.recordMemoryAudit({
275
+ operation: 'memory_write',
276
+ pid: pid ?? null,
277
+ address: address ?? null,
278
+ size: size ?? null,
279
+ result: 'failure',
280
+ error: errorMessage,
281
+ durationMs: Date.now() - startedAt,
282
+ });
122
283
  return {
123
284
  content: [
124
285
  {
125
286
  type: 'text',
126
287
  text: JSON.stringify({
127
288
  success: false,
128
- error: error instanceof Error ? error.message : String(error),
289
+ error: errorMessage,
290
+ diagnostics,
129
291
  }, null, 2),
130
292
  },
131
293
  ],
@@ -133,12 +295,28 @@ export class ProcessToolHandlersMemory extends ProcessToolHandlersBase {
133
295
  }
134
296
  }
135
297
  async handleMemoryScan(args) {
298
+ const startedAt = Date.now();
136
299
  try {
137
300
  const pid = validatePid(args.pid);
138
301
  const pattern = requireString(args.pattern, 'pattern');
139
302
  const patternType = normalizePatternType(args.patternType);
140
303
  const availability = await this.memoryManager.checkAvailability();
141
304
  if (!availability.available) {
305
+ const errorMessage = availability.reason ?? 'Memory operations not available';
306
+ const diagnostics = await this.safeBuildMemoryDiagnostics({
307
+ pid,
308
+ operation: 'memory_scan',
309
+ error: errorMessage,
310
+ });
311
+ this.recordMemoryAudit({
312
+ operation: 'memory_scan',
313
+ pid,
314
+ address: null,
315
+ size: null,
316
+ result: 'failure',
317
+ error: errorMessage,
318
+ durationMs: Date.now() - startedAt,
319
+ });
142
320
  return {
143
321
  content: [
144
322
  {
@@ -151,43 +329,116 @@ export class ProcessToolHandlersMemory extends ProcessToolHandlersBase {
151
329
  requestedPattern: pattern,
152
330
  patternType,
153
331
  pid,
332
+ diagnostics,
154
333
  }, null, 2),
155
334
  },
156
335
  ],
157
336
  };
158
337
  }
159
338
  const result = await this.memoryManager.scanMemory(pid, pattern, patternType);
339
+ const diagnostics = !result.success
340
+ ? await this.safeBuildMemoryDiagnostics({
341
+ pid,
342
+ operation: 'memory_scan',
343
+ error: result.error,
344
+ })
345
+ : undefined;
346
+ this.recordMemoryAudit({
347
+ operation: 'memory_scan',
348
+ pid,
349
+ address: null,
350
+ size: null,
351
+ result: result.success ? 'success' : 'failure',
352
+ error: result.error,
353
+ durationMs: Date.now() - startedAt,
354
+ });
355
+ const payload = {
356
+ success: result.success,
357
+ addresses: result.addresses,
358
+ error: result.error,
359
+ pid,
360
+ pattern,
361
+ patternType,
362
+ platform: this.platform,
363
+ };
364
+ if (!result.success) {
365
+ payload.diagnostics = diagnostics;
366
+ }
160
367
  return {
161
368
  content: [
162
369
  {
163
370
  type: 'text',
164
- text: JSON.stringify({
165
- success: result.success,
166
- addresses: result.addresses,
167
- error: result.error,
168
- pid,
169
- pattern,
170
- patternType,
171
- platform: this.platform,
172
- }, null, 2),
371
+ text: JSON.stringify(payload, null, 2),
173
372
  },
174
373
  ],
175
374
  };
176
375
  }
177
376
  catch (error) {
178
377
  logger.error('Memory scan failed:', error);
378
+ const errorMessage = error instanceof Error ? error.message : String(error);
379
+ const pid = getOptionalPid(args.pid);
380
+ const diagnostics = await this.safeBuildMemoryDiagnostics({
381
+ pid,
382
+ operation: 'memory_scan',
383
+ error: errorMessage,
384
+ });
385
+ this.recordMemoryAudit({
386
+ operation: 'memory_scan',
387
+ pid: pid ?? null,
388
+ address: null,
389
+ size: null,
390
+ result: 'failure',
391
+ error: errorMessage,
392
+ durationMs: Date.now() - startedAt,
393
+ });
179
394
  return {
180
395
  content: [
181
396
  {
182
397
  type: 'text',
183
398
  text: JSON.stringify({
184
399
  success: false,
185
- error: error instanceof Error ? error.message : String(error),
400
+ error: errorMessage,
401
+ diagnostics,
402
+ }, null, 2),
403
+ },
404
+ ],
405
+ };
406
+ }
407
+ }
408
+ async handleMemoryAuditExport(args) {
409
+ try {
410
+ const exportedJson = this.auditTrail.exportJson();
411
+ const entries = JSON.parse(exportedJson);
412
+ const clear = args.clear === true;
413
+ const count = this.auditTrail.size();
414
+ if (clear) {
415
+ this.auditTrail.clear();
416
+ }
417
+ return {
418
+ content: [
419
+ {
420
+ type: 'text',
421
+ text: JSON.stringify({
422
+ success: true,
423
+ count,
424
+ cleared: clear,
425
+ entries,
186
426
  }, null, 2),
187
427
  },
188
428
  ],
189
429
  };
190
430
  }
431
+ catch (error) {
432
+ logger.error('Memory audit export failed:', error);
433
+ return {
434
+ content: [
435
+ {
436
+ type: 'text',
437
+ text: JSON.stringify({ success: false, error: error instanceof Error ? error.message : String(error) }, null, 2),
438
+ },
439
+ ],
440
+ };
441
+ }
191
442
  }
192
443
  async handleMemoryCheckProtection(args) {
193
444
  try {
@@ -36,6 +36,7 @@ const manifest = {
36
36
  { tool: t('memory_batch_write'), domain: DOMAIN, bind: b((h, a) => h.handleMemoryBatchWrite(a)) },
37
37
  { tool: t('memory_dump_region'), domain: DOMAIN, bind: b((h, a) => h.handleMemoryDumpRegion(a)) },
38
38
  { tool: t('memory_list_regions'), domain: DOMAIN, bind: b((h, a) => h.handleMemoryListRegions(a)) },
39
+ { tool: t('memory_audit_export'), domain: DOMAIN, bind: b((h, a) => h.handleMemoryAuditExport(a)) },
39
40
  { tool: t('inject_dll'), domain: DOMAIN, bind: b((h, a) => h.handleInjectDll(a)) },
40
41
  { tool: t('module_inject_dll'), domain: DOMAIN, bind: b((h, a) => h.handleInjectDll(a)) },
41
42
  { tool: t('inject_shellcode'), domain: DOMAIN, bind: b((h, a) => h.handleInjectShellcode(a)) },
@@ -119,8 +119,7 @@ export class WorkflowHandlersApi extends WorkflowHandlersBase {
119
119
  }
120
120
 
121
121
  var results = {};
122
- for (var i = 0; i < paths.length; i++) {
123
- var path = paths[i];
122
+ async function probePath(path) {
124
123
  try {
125
124
  var opts = {method: method, headers: headers};
126
125
  if (bodyTemplate && (method === 'POST' || method === 'PUT' || method === 'PATCH')) {
@@ -137,11 +136,22 @@ export class WorkflowHandlersApi extends WorkflowHandlersBase {
137
136
  snippet = '[HTML/XML response suppressed]';
138
137
  }
139
138
  }
140
- results[path] = {status: resp.status, contentType: ct.split(';')[0].trim(), snippet: snippet};
139
+ return [path, {status: resp.status, contentType: ct.split(';')[0].trim(), snippet: snippet}];
141
140
  } catch(e) {
142
- results[path] = {status: -1, error: e.message};
141
+ return [path, {status: -1, error: e instanceof Error ? e.message : String(e)}];
143
142
  }
144
143
  }
144
+
145
+ var nextIndex = 0;
146
+ var maxConcurrency = Math.min(paths.length, 6);
147
+ await Promise.all(Array.from({ length: maxConcurrency }, async function() {
148
+ while (nextIndex < paths.length) {
149
+ var currentIndex = nextIndex++;
150
+ var currentPath = paths[currentIndex];
151
+ var entry = await probePath(currentPath);
152
+ results[entry[0]] = entry[1];
153
+ }
154
+ }));
145
155
  return {probed: paths.length, method: method, baseUrl: baseUrl, results: results};
146
156
  })()`;
147
157
  try {
@@ -1,6 +1,6 @@
1
1
  import { readdir, stat } from 'node:fs/promises';
2
- import { join } from 'node:path';
3
- import { fileURLToPath, pathToFileURL } from 'node:url';
2
+ import { dirname, join, relative, sep } from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
4
  import { logger } from '../../utils/logger.js';
5
5
  function isDomainManifest(value) {
6
6
  if (!value || typeof value !== 'object')
@@ -35,24 +35,29 @@ async function discoverManifestPaths() {
35
35
  logger.error('[discovery] Cannot read domains directory:', err);
36
36
  return [];
37
37
  }
38
- const paths = [];
39
- for (const entry of entries) {
40
- if (!entry.isDirectory())
41
- continue;
38
+ const directories = entries.filter(e => e.isDirectory());
39
+ const resolved = await Promise.all(directories.map(async (entry) => {
42
40
  for (const ext of ['manifest.js', 'manifest.ts']) {
43
41
  const manifestPath = join(domainsDir, entry.name, ext);
44
42
  try {
45
43
  const s = await stat(manifestPath);
46
- if (s.isFile()) {
47
- paths.push(manifestPath);
48
- break;
49
- }
44
+ if (s.isFile())
45
+ return manifestPath;
50
46
  }
51
47
  catch {
52
48
  }
53
49
  }
50
+ return null;
51
+ }));
52
+ return resolved.filter((value) => value !== null);
53
+ }
54
+ function toImportSpecifier(absPath) {
55
+ const currentDir = dirname(fileURLToPath(import.meta.url));
56
+ const relPath = relative(currentDir, absPath).split(sep).join('/');
57
+ if (relPath.startsWith('.')) {
58
+ return relPath;
54
59
  }
55
- return paths;
60
+ return `./${relPath}`;
56
61
  }
57
62
  export async function discoverDomainManifests() {
58
63
  const files = await discoverManifestPaths();
@@ -61,7 +66,7 @@ export async function discoverDomainManifests() {
61
66
  const seenDepKeys = new Set();
62
67
  for (const absPath of files) {
63
68
  try {
64
- const mod = await import(pathToFileURL(absPath).href);
69
+ const mod = await import(toImportSpecifier(absPath));
65
70
  const manifest = extractManifest(mod);
66
71
  if (!manifest) {
67
72
  logger.warn('[discovery] Skipping ' + absPath + ': no valid DomainManifest export');
@@ -3,6 +3,8 @@ import { logger } from '../../utils/logger.js';
3
3
  let _manifests = null;
4
4
  let _registrations = null;
5
5
  let _initPromise = null;
6
+ let _domainsView = null;
7
+ let _toolNamesView = null;
6
8
  async function init() {
7
9
  if (_manifests !== null)
8
10
  return;
@@ -26,6 +28,8 @@ async function init() {
26
28
  }
27
29
  }
28
30
  _registrations = [...uniqueByToolName.values()];
31
+ _domainsView = new Set(_manifests.map(m => m.domain));
32
+ _toolNamesView = new Set(_registrations.map(r => r.tool.name));
29
33
  })();
30
34
  await _initPromise;
31
35
  }
@@ -49,10 +53,14 @@ export function getAllRegistrations() {
49
53
  return getRegistrations();
50
54
  }
51
55
  export function getAllDomains() {
52
- return new Set(getManifests().map(m => m.domain));
56
+ if (!_domainsView)
57
+ throw new Error('[registry] Not initialised - call initRegistry() first.');
58
+ return _domainsView;
53
59
  }
54
60
  export function getAllToolNames() {
55
- return new Set(getRegistrations().map(r => r.tool.name));
61
+ if (!_toolNamesView)
62
+ throw new Error('[registry] Not initialised - call initRegistry() first.');
63
+ return _toolNamesView;
56
64
  }
57
65
  export function buildToolGroups() {
58
66
  const groups = {};
@@ -45,6 +45,7 @@ export declare class TokenBudgetManager {
45
45
  recordToolCall(toolName: string, request: unknown, response: unknown): void;
46
46
  private isRecord;
47
47
  private hasDetailedSummarySize;
48
+ private tryEstimateMcpEnvelope;
48
49
  private calculateSize;
49
50
  private normalizeForSizeEstimate;
50
51
  private estimateTokens;
@@ -74,11 +74,33 @@ export class TokenBudgetManager {
74
74
  const { size } = summary;
75
75
  return typeof size === 'number' && Number.isFinite(size) && size > 0;
76
76
  }
77
+ tryEstimateMcpEnvelope(data) {
78
+ if (!this.isRecord(data))
79
+ return null;
80
+ const content = data['content'];
81
+ if (!Array.isArray(content) || content.length === 0)
82
+ return null;
83
+ const first = content[0];
84
+ if (!this.isRecord(first))
85
+ return null;
86
+ if (first['type'] !== 'text' || typeof first['text'] !== 'string')
87
+ return null;
88
+ const text = first['text'];
89
+ const truncated = text.length > this.MAX_ESTIMATION_STRING_LENGTH
90
+ ? `${text.slice(0, this.MAX_ESTIMATION_STRING_LENGTH)}...[truncated:${text.length}]`
91
+ : text;
92
+ const overhead = 42 + (data['isError'] === true ? 14 : 0);
93
+ const textBytes = Buffer.byteLength(truncated, 'utf8');
94
+ return Math.min(overhead + textBytes, this.MAX_ESTIMATION_BYTES);
95
+ }
77
96
  calculateSize(data) {
78
97
  try {
79
98
  if (this.hasDetailedSummarySize(data)) {
80
99
  return Math.min(data.summary.size, this.MAX_ESTIMATION_BYTES);
81
100
  }
101
+ const mcpSize = this.tryEstimateMcpEnvelope(data);
102
+ if (mcpSize !== null)
103
+ return mcpSize;
82
104
  const normalized = this.normalizeForSizeEstimate(data, 0, new WeakSet());
83
105
  const serialized = JSON.stringify(normalized);
84
106
  if (!serialized) {