@blen/fedreg-mcp-server 2.0.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.
Files changed (161) hide show
  1. package/CHANGELOG.md +125 -0
  2. package/LICENSE +202 -0
  3. package/NOTICE +15 -0
  4. package/README.md +219 -0
  5. package/SECURITY.md +99 -0
  6. package/dist/auth/embedded.d.ts +2 -0
  7. package/dist/auth/embedded.d.ts.map +1 -0
  8. package/dist/auth/embedded.js +17 -0
  9. package/dist/auth/embedded.js.map +1 -0
  10. package/dist/auth/index.d.ts +2 -0
  11. package/dist/auth/index.d.ts.map +1 -0
  12. package/dist/auth/index.js +2 -0
  13. package/dist/auth/index.js.map +1 -0
  14. package/dist/bin.d.ts +3 -0
  15. package/dist/bin.d.ts.map +1 -0
  16. package/dist/bin.js +120 -0
  17. package/dist/bin.js.map +1 -0
  18. package/dist/index.d.ts +11 -0
  19. package/dist/index.d.ts.map +1 -0
  20. package/dist/index.js +8 -0
  21. package/dist/index.js.map +1 -0
  22. package/dist/sandbox/deno.d.ts +13 -0
  23. package/dist/sandbox/deno.d.ts.map +1 -0
  24. package/dist/sandbox/deno.js +203 -0
  25. package/dist/sandbox/deno.js.map +1 -0
  26. package/dist/sandbox/index.d.ts +20 -0
  27. package/dist/sandbox/index.d.ts.map +1 -0
  28. package/dist/sandbox/index.js +46 -0
  29. package/dist/sandbox/index.js.map +1 -0
  30. package/dist/sandbox/isolate.d.ts +7 -0
  31. package/dist/sandbox/isolate.d.ts.map +1 -0
  32. package/dist/sandbox/isolate.js +100 -0
  33. package/dist/sandbox/isolate.js.map +1 -0
  34. package/dist/sandbox/policy.d.ts +7 -0
  35. package/dist/sandbox/policy.d.ts.map +1 -0
  36. package/dist/sandbox/policy.js +70 -0
  37. package/dist/sandbox/policy.js.map +1 -0
  38. package/dist/sandbox/types.d.ts +41 -0
  39. package/dist/sandbox/types.d.ts.map +1 -0
  40. package/dist/sandbox/types.js +2 -0
  41. package/dist/sandbox/types.js.map +1 -0
  42. package/dist/sdk/bindings.d.ts +10 -0
  43. package/dist/sdk/bindings.d.ts.map +1 -0
  44. package/dist/sdk/bindings.js +15 -0
  45. package/dist/sdk/bindings.js.map +1 -0
  46. package/dist/sdk/ecfr-client.d.ts +61 -0
  47. package/dist/sdk/ecfr-client.d.ts.map +1 -0
  48. package/dist/sdk/ecfr-client.js +45 -0
  49. package/dist/sdk/ecfr-client.js.map +1 -0
  50. package/dist/sdk/fr-client.d.ts +81 -0
  51. package/dist/sdk/fr-client.d.ts.map +1 -0
  52. package/dist/sdk/fr-client.js +77 -0
  53. package/dist/sdk/fr-client.js.map +1 -0
  54. package/dist/sdk/regs-client.d.ts +34 -0
  55. package/dist/sdk/regs-client.d.ts.map +1 -0
  56. package/dist/sdk/regs-client.js +44 -0
  57. package/dist/sdk/regs-client.js.map +1 -0
  58. package/dist/sdk/runtime.d.ts +21 -0
  59. package/dist/sdk/runtime.d.ts.map +1 -0
  60. package/dist/sdk/runtime.js +31 -0
  61. package/dist/sdk/runtime.js.map +1 -0
  62. package/dist/sdk/sources/corpus-loader.d.ts +6 -0
  63. package/dist/sdk/sources/corpus-loader.d.ts.map +1 -0
  64. package/dist/sdk/sources/corpus-loader.js +9 -0
  65. package/dist/sdk/sources/corpus-loader.js.map +1 -0
  66. package/dist/sdk/sources/ecfr.d.ts +3 -0
  67. package/dist/sdk/sources/ecfr.d.ts.map +1 -0
  68. package/dist/sdk/sources/ecfr.js +12 -0
  69. package/dist/sdk/sources/ecfr.js.map +1 -0
  70. package/dist/sdk/sources/fr.d.ts +3 -0
  71. package/dist/sdk/sources/fr.d.ts.map +1 -0
  72. package/dist/sdk/sources/fr.js +12 -0
  73. package/dist/sdk/sources/fr.js.map +1 -0
  74. package/dist/sdk/sources/index.d.ts +7 -0
  75. package/dist/sdk/sources/index.d.ts.map +1 -0
  76. package/dist/sdk/sources/index.js +25 -0
  77. package/dist/sdk/sources/index.js.map +1 -0
  78. package/dist/sdk/sources/regs.d.ts +3 -0
  79. package/dist/sdk/sources/regs.d.ts.map +1 -0
  80. package/dist/sdk/sources/regs.js +24 -0
  81. package/dist/sdk/sources/regs.js.map +1 -0
  82. package/dist/sdk/sources/source.d.ts +32 -0
  83. package/dist/sdk/sources/source.d.ts.map +1 -0
  84. package/dist/sdk/sources/source.js +2 -0
  85. package/dist/sdk/sources/source.js.map +1 -0
  86. package/dist/search/bm25.d.ts +25 -0
  87. package/dist/search/bm25.d.ts.map +1 -0
  88. package/dist/search/bm25.js +67 -0
  89. package/dist/search/bm25.js.map +1 -0
  90. package/dist/search/corpus.d.ts +24 -0
  91. package/dist/search/corpus.d.ts.map +1 -0
  92. package/dist/search/corpus.js +23 -0
  93. package/dist/search/corpus.js.map +1 -0
  94. package/dist/server/authz.d.ts +18 -0
  95. package/dist/server/authz.d.ts.map +1 -0
  96. package/dist/server/authz.js +42 -0
  97. package/dist/server/authz.js.map +1 -0
  98. package/dist/server/http.d.ts +21 -0
  99. package/dist/server/http.d.ts.map +1 -0
  100. package/dist/server/http.js +159 -0
  101. package/dist/server/http.js.map +1 -0
  102. package/dist/server/ipRateLimiter.d.ts +11 -0
  103. package/dist/server/ipRateLimiter.d.ts.map +1 -0
  104. package/dist/server/ipRateLimiter.js +27 -0
  105. package/dist/server/ipRateLimiter.js.map +1 -0
  106. package/dist/server/mcpServer.d.ts +15 -0
  107. package/dist/server/mcpServer.d.ts.map +1 -0
  108. package/dist/server/mcpServer.js +67 -0
  109. package/dist/server/mcpServer.js.map +1 -0
  110. package/dist/server/stdio.d.ts +3 -0
  111. package/dist/server/stdio.d.ts.map +1 -0
  112. package/dist/server/stdio.js +16 -0
  113. package/dist/server/stdio.js.map +1 -0
  114. package/dist/server/toolCatalog.d.ts +20 -0
  115. package/dist/server/toolCatalog.d.ts.map +1 -0
  116. package/dist/server/toolCatalog.js +29 -0
  117. package/dist/server/toolCatalog.js.map +1 -0
  118. package/dist/server/wellKnown.d.ts +16 -0
  119. package/dist/server/wellKnown.d.ts.map +1 -0
  120. package/dist/server/wellKnown.js +17 -0
  121. package/dist/server/wellKnown.js.map +1 -0
  122. package/dist/server/zodToJsonSchema.d.ts +7 -0
  123. package/dist/server/zodToJsonSchema.d.ts.map +1 -0
  124. package/dist/server/zodToJsonSchema.js +44 -0
  125. package/dist/server/zodToJsonSchema.js.map +1 -0
  126. package/dist/supervisor/index.d.ts +19 -0
  127. package/dist/supervisor/index.d.ts.map +1 -0
  128. package/dist/supervisor/index.js +22 -0
  129. package/dist/supervisor/index.js.map +1 -0
  130. package/dist/tools/describeSchema.d.ts +35 -0
  131. package/dist/tools/describeSchema.d.ts.map +1 -0
  132. package/dist/tools/describeSchema.js +14 -0
  133. package/dist/tools/describeSchema.js.map +1 -0
  134. package/dist/tools/execute.d.ts +33 -0
  135. package/dist/tools/execute.d.ts.map +1 -0
  136. package/dist/tools/execute.js +28 -0
  137. package/dist/tools/execute.js.map +1 -0
  138. package/dist/tools/searchApi.d.ts +27 -0
  139. package/dist/tools/searchApi.d.ts.map +1 -0
  140. package/dist/tools/searchApi.js +14 -0
  141. package/dist/tools/searchApi.js.map +1 -0
  142. package/dist/util/httpClient.d.ts +46 -0
  143. package/dist/util/httpClient.d.ts.map +1 -0
  144. package/dist/util/httpClient.js +139 -0
  145. package/dist/util/httpClient.js.map +1 -0
  146. package/dist/util/logger.d.ts +7 -0
  147. package/dist/util/logger.d.ts.map +1 -0
  148. package/dist/util/logger.js +19 -0
  149. package/dist/util/logger.js.map +1 -0
  150. package/dist/util/quotas.d.ts +13 -0
  151. package/dist/util/quotas.d.ts.map +1 -0
  152. package/dist/util/quotas.js +29 -0
  153. package/dist/util/quotas.js.map +1 -0
  154. package/dist/util/tokenBucket.d.ts +11 -0
  155. package/dist/util/tokenBucket.d.ts.map +1 -0
  156. package/dist/util/tokenBucket.js +30 -0
  157. package/dist/util/tokenBucket.js.map +1 -0
  158. package/package.json +95 -0
  159. package/schema/ecfr.json +148 -0
  160. package/schema/fr.json +194 -0
  161. package/schema/regs.json +67 -0
@@ -0,0 +1,2 @@
1
+ export declare function mintDevToken(secret: string, subject: string, audience: string, ttlSec?: number): Promise<string>;
2
+ //# sourceMappingURL=embedded.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"embedded.d.ts","sourceRoot":"","sources":["../../src/auth/embedded.ts"],"names":[],"mappings":"AAMA,wBAAsB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,SAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAUpH"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Embedded auth: a dev-only HS256 issuer for local testing.
3
+ * Do not use in production.
4
+ */
5
+ import { SignJWT } from 'jose';
6
+ export async function mintDevToken(secret, subject, audience, ttlSec = 3600) {
7
+ const key = new TextEncoder().encode(secret);
8
+ return await new SignJWT({})
9
+ .setProtectedHeader({ alg: 'HS256' })
10
+ .setIssuer('fedreg-mcp-server/embedded')
11
+ .setSubject(subject)
12
+ .setAudience(audience)
13
+ .setIssuedAt()
14
+ .setExpirationTime(Math.floor(Date.now() / 1000) + ttlSec)
15
+ .sign(key);
16
+ }
17
+ //# sourceMappingURL=embedded.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"embedded.js","sourceRoot":"","sources":["../../src/auth/embedded.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAE/B,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,MAAc,EAAE,OAAe,EAAE,QAAgB,EAAE,MAAM,GAAG,IAAI;IACjG,MAAM,GAAG,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAC7C,OAAO,MAAM,IAAI,OAAO,CAAC,EAAE,CAAC;SACzB,kBAAkB,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC;SACpC,SAAS,CAAC,4BAA4B,CAAC;SACvC,UAAU,CAAC,OAAO,CAAC;SACnB,WAAW,CAAC,QAAQ,CAAC;SACrB,WAAW,EAAE;SACb,iBAAiB,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,GAAG,MAAM,CAAC;SACzD,IAAI,CAAC,GAAG,CAAC,CAAC;AACf,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { buildVerifier, type AuthConfig, type AuthContext, type Verifier } from '../server/authz.js';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,KAAK,UAAU,EAAE,KAAK,WAAW,EAAE,KAAK,QAAQ,EAAE,MAAM,oBAAoB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { buildVerifier } from '../server/authz.js';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAoD,MAAM,oBAAoB,CAAC"}
package/dist/bin.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=bin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bin.d.ts","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":""}
package/dist/bin.js ADDED
@@ -0,0 +1,120 @@
1
+ #!/usr/bin/env node
2
+ import { startStdio } from './server/stdio.js';
3
+ import { startHttp } from './server/http.js';
4
+ import { buildSupervisor } from './supervisor/index.js';
5
+ import { log } from './util/logger.js';
6
+ function parseArgs(argv) {
7
+ const args = {
8
+ http: false,
9
+ port: Number(process.env.PORT ?? 8080),
10
+ host: process.env.HOST ?? '0.0.0.0',
11
+ sandbox: process.env.FEDREG_SANDBOX ?? 'auto',
12
+ insecure: false,
13
+ };
14
+ for (let i = 0; i < argv.length; i++) {
15
+ const a = argv[i];
16
+ if (a === '--http')
17
+ args.http = true;
18
+ else if (a === '--insecure')
19
+ args.insecure = true;
20
+ else if (a === '--port')
21
+ args.port = Number(argv[++i]);
22
+ else if (a === '--host')
23
+ args.host = String(argv[++i]);
24
+ else if (a === '--sandbox')
25
+ args.sandbox = argv[++i];
26
+ else if (a === '--help' || a === '-h') {
27
+ printHelp();
28
+ process.exit(0);
29
+ }
30
+ }
31
+ return args;
32
+ }
33
+ function printHelp() {
34
+ process.stdout.write(`fedreg-mcp-server [options]
35
+
36
+ --http Run Streamable HTTP transport (default: stdio)
37
+ --port N HTTP port (default 8080, env PORT)
38
+ --host H HTTP bind host (default 0.0.0.0, env HOST)
39
+ --sandbox auto|isolate|deno
40
+ Sandbox runner (default auto, env FEDREG_SANDBOX)
41
+ --insecure HTTP without auth (DEV ONLY)
42
+ -h, --help Show this help
43
+ `);
44
+ }
45
+ async function main() {
46
+ const args = parseArgs(process.argv.slice(2));
47
+ const deps = await buildSupervisor({
48
+ frBaseUrl: process.env.FEDREG_FR_BASE_URL ?? 'https://www.federalregister.gov/api/v1',
49
+ ecfrBaseUrl: process.env.FEDREG_ECFR_BASE_URL ?? 'https://www.ecfr.gov/api',
50
+ regsBaseUrl: process.env.FEDREG_REGS_BASE_URL ?? 'https://api.regulations.gov',
51
+ regsApiKey: process.env.FEDREG_REGS_API_KEY,
52
+ userAgent: process.env.FEDREG_USER_AGENT ?? 'fedreg-mcp-server/2.0 (+https://github.com/blen-labs/fedreg-mcp-server)',
53
+ upstreamTimeoutMs: Number(process.env.FEDREG_UPSTREAM_TIMEOUT_MS ?? 20_000),
54
+ upstreamRetries: Number(process.env.FEDREG_UPSTREAM_RETRIES ?? 3),
55
+ cacheTtlMs: Number(process.env.FEDREG_CACHE_TTL_MS ?? 300_000),
56
+ cacheMaxItems: Number(process.env.FEDREG_CACHE_MAX_ITEMS ?? 2000),
57
+ sandbox: args.sandbox,
58
+ regsMaxCallsPerExecute: (() => {
59
+ const n = Number(process.env.FEDREG_REGS_MAX_CALLS_PER_EXECUTE ?? 30);
60
+ return Number.isFinite(n) && n >= 0 ? n : 30;
61
+ })(),
62
+ regsRatePerHour: (() => {
63
+ // Require >= 1: a rate of 0 would silently block ALL regs calls (it is NOT
64
+ // "unlimited"). To disable regs, leave FEDREG_REGS_API_KEY unset instead.
65
+ const n = Number(process.env.FEDREG_REGS_RATE_PER_HOUR ?? 1000);
66
+ return Number.isFinite(n) && n >= 1 ? n : 1000;
67
+ })(),
68
+ regsSubjectRatePerHour: (() => {
69
+ // Require >= 1; per-subject hourly cap, not "unlimited".
70
+ const n = Number(process.env.FEDREG_REGS_SUBJECT_RATE_PER_HOUR ?? 500);
71
+ return Number.isFinite(n) && n >= 1 ? n : 500;
72
+ })(),
73
+ });
74
+ let handle;
75
+ if (args.http) {
76
+ const allowedHosts = process.env.FEDREG_ALLOWED_HOSTS
77
+ ? process.env.FEDREG_ALLOWED_HOSTS.split(',').map(s => s.trim().toLowerCase()).filter(Boolean)
78
+ : undefined;
79
+ handle = await startHttp(deps, {
80
+ port: args.port,
81
+ host: args.host,
82
+ rps: Number(process.env.FEDREG_IP_RPS ?? 5),
83
+ burst: Number(process.env.FEDREG_IP_BURST ?? 20),
84
+ subjectDailyQuota: Number(process.env.FEDREG_SUBJECT_DAILY_QUOTA ?? 10_000),
85
+ insecure: args.insecure,
86
+ allowedHosts,
87
+ publicOrigin: process.env.FEDREG_PUBLIC_ORIGIN,
88
+ auth: {
89
+ provider: process.env.FEDREG_AUTH_PROVIDER ?? 'none',
90
+ issuer: process.env.FEDREG_AUTH_ISSUER,
91
+ audience: process.env.FEDREG_AUTH_AUDIENCE,
92
+ jwksUrl: process.env.FEDREG_AUTH_JWKS_URL,
93
+ resource: process.env.FEDREG_AUTH_RESOURCE,
94
+ scopes: process.env.FEDREG_AUTH_SCOPES?.split(',').map(s => s.trim()).filter(Boolean),
95
+ },
96
+ });
97
+ }
98
+ else {
99
+ await startStdio(deps);
100
+ }
101
+ // Graceful shutdown (Railway sends SIGTERM, K8s sends SIGTERM, Ctrl-C sends SIGINT)
102
+ const shutdown = async (signal) => {
103
+ log.info('shutdown.start', { signal });
104
+ try {
105
+ if (handle)
106
+ await handle.close();
107
+ }
108
+ catch (err) {
109
+ log.warn('shutdown.error', { message: err.message });
110
+ }
111
+ process.exit(0);
112
+ };
113
+ process.on('SIGTERM', () => void shutdown('SIGTERM'));
114
+ process.on('SIGINT', () => void shutdown('SIGINT'));
115
+ }
116
+ main().catch(err => {
117
+ log.error('bin.fatal', { message: err.message, stack: err.stack });
118
+ process.exit(1);
119
+ });
120
+ //# sourceMappingURL=bin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bin.js","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ export { buildMcpServer } from './server/mcpServer.js';
2
+ export { startStdio } from './server/stdio.js';
3
+ export { startHttp } from './server/http.js';
4
+ export { buildSupervisor } from './supervisor/index.js';
5
+ export { buildSdk } from './sdk/bindings.js';
6
+ export { pickSandbox } from './sandbox/index.js';
7
+ export { buildVerifier } from './server/authz.js';
8
+ export type { Sdk } from './sdk/bindings.js';
9
+ export type { SandboxKind, SandboxRunner, ExecuteOptions, ExecuteResult } from './sandbox/types.js';
10
+ export type { AuthConfig, AuthContext, Verifier } from './server/authz.js';
11
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,YAAY,EAAE,GAAG,EAAE,MAAM,mBAAmB,CAAC;AAC7C,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACpG,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,8 @@
1
+ export { buildMcpServer } from './server/mcpServer.js';
2
+ export { startStdio } from './server/stdio.js';
3
+ export { startHttp } from './server/http.js';
4
+ export { buildSupervisor } from './supervisor/index.js';
5
+ export { buildSdk } from './sdk/bindings.js';
6
+ export { pickSandbox } from './sandbox/index.js';
7
+ export { buildVerifier } from './server/authz.js';
8
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC"}
@@ -0,0 +1,13 @@
1
+ import type { ExecuteOptions, ExecuteResult, RpcBridge, SandboxRunner } from './types.js';
2
+ /**
3
+ * Deno fallback sandbox. We launch `deno run --no-prompt <runner>` with NO
4
+ * `--allow-*` flags, so Deno's deny-by-default grants no net/fs/env/ffi/subprocess
5
+ * access, and bridge RPC over stdin/stdout using a length-prefixed JSON protocol.
6
+ */
7
+ export declare class DenoRunner implements SandboxRunner {
8
+ kind: "deno";
9
+ available(): Promise<boolean>;
10
+ execute(opts: ExecuteOptions, rpc: RpcBridge): Promise<ExecuteResult>;
11
+ }
12
+ export declare function buildDenoRunner(userCode: string, timeoutMs: number, bindings: string[]): string;
13
+ //# sourceMappingURL=deno.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deno.d.ts","sourceRoot":"","sources":["../../src/sandbox/deno.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAG1F;;;;GAIG;AACH,qBAAa,UAAW,YAAW,aAAa;IAC9C,IAAI,EAAG,MAAM,CAAU;IAEjB,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC;IAQ7B,OAAO,CAAC,IAAI,EAAE,cAAc,EAAE,GAAG,EAAE,SAAS,GAAG,OAAO,CAAC,aAAa,CAAC;CAwF5E;AAWD,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,MAAM,CAgF/F"}
@@ -0,0 +1,203 @@
1
+ import { spawn } from 'node:child_process';
2
+ import { mkdtempSync, rmSync, writeFileSync } from 'node:fs';
3
+ import { tmpdir } from 'node:os';
4
+ import { join } from 'node:path';
5
+ import { preflight } from './policy.js';
6
+ /**
7
+ * Deno fallback sandbox. We launch `deno run --no-prompt <runner>` with NO
8
+ * `--allow-*` flags, so Deno's deny-by-default grants no net/fs/env/ffi/subprocess
9
+ * access, and bridge RPC over stdin/stdout using a length-prefixed JSON protocol.
10
+ */
11
+ export class DenoRunner {
12
+ kind = 'deno';
13
+ async available() {
14
+ return await new Promise(resolve => {
15
+ const p = spawn('deno', ['--version'], { stdio: 'ignore' });
16
+ p.on('error', () => resolve(false));
17
+ p.on('exit', code => resolve(code === 0));
18
+ });
19
+ }
20
+ async execute(opts, rpc) {
21
+ const started = Date.now();
22
+ const policy = preflight(opts.code);
23
+ if (!policy.ok) {
24
+ return { ok: false, logs: [], error: { name: 'PolicyError', message: policy.errors.join('; ') }, durationMs: Date.now() - started };
25
+ }
26
+ const runner = buildDenoRunner(opts.code, opts.timeoutMs ?? 15_000, opts.bindings ?? []);
27
+ // Write the runner to a temp file rather than piping it on stdin: `deno run -`
28
+ // reads the program from stdin until EOF, but we keep stdin open as the RPC
29
+ // channel, so it would never start. A file path leaves stdin free for RPC.
30
+ const tmpDir = mkdtempSync(join(tmpdir(), 'fedreg-deno-'));
31
+ const runnerPath = join(tmpDir, 'runner.ts');
32
+ writeFileSync(runnerPath, runner);
33
+ const cleanup = () => { try {
34
+ rmSync(tmpDir, { recursive: true, force: true });
35
+ }
36
+ catch { /* ignore */ } };
37
+ const child = spawn('deno', [
38
+ 'run',
39
+ '--no-prompt',
40
+ runnerPath,
41
+ ], { stdio: ['pipe', 'pipe', 'pipe'] });
42
+ const logs = [];
43
+ let stdoutBuf = Buffer.alloc(0);
44
+ return await new Promise((resolve) => {
45
+ const timeout = setTimeout(() => {
46
+ try {
47
+ child.kill('SIGKILL');
48
+ }
49
+ catch { /* ignore */ }
50
+ cleanup();
51
+ resolve({
52
+ ok: false, logs,
53
+ error: { name: 'TimeoutError', message: `Execution exceeded ${opts.timeoutMs ?? 15_000}ms` },
54
+ durationMs: Date.now() - started,
55
+ });
56
+ }, (opts.timeoutMs ?? 15_000) + 1_000);
57
+ child.stdout.on('data', async (chunk) => {
58
+ stdoutBuf = Buffer.concat([stdoutBuf, chunk]);
59
+ // length-prefixed JSON frames: 8-byte ascii hex length + payload
60
+ while (stdoutBuf.length >= 9) {
61
+ const lenHex = stdoutBuf.subarray(0, 8).toString('ascii');
62
+ if (stdoutBuf[8] !== 0x0a) {
63
+ logs.push(stdoutBuf.toString('utf8'));
64
+ stdoutBuf = Buffer.alloc(0);
65
+ break;
66
+ }
67
+ const len = parseInt(lenHex, 16);
68
+ if (Number.isNaN(len))
69
+ break;
70
+ if (stdoutBuf.length < 9 + len)
71
+ break;
72
+ const payload = stdoutBuf.subarray(9, 9 + len).toString('utf8');
73
+ stdoutBuf = stdoutBuf.subarray(9 + len);
74
+ const msg = JSON.parse(payload);
75
+ if (msg.kind === 'log') {
76
+ logs.push(msg.text);
77
+ }
78
+ else if (msg.kind === 'rpc') {
79
+ const res = await rpc.dispatch({ binding: msg.binding, path: msg.path, args: msg.args });
80
+ writeFrame(child.stdin, { kind: 'rpc-result', id: msg.id, ok: res.ok, value: res.value, error: res.error });
81
+ }
82
+ else if (msg.kind === 'result') {
83
+ clearTimeout(timeout);
84
+ try {
85
+ child.stdin.end();
86
+ }
87
+ catch { /* ignore */ }
88
+ resolve({
89
+ ok: msg.ok,
90
+ logs,
91
+ value: msg.value,
92
+ error: msg.error,
93
+ durationMs: Date.now() - started,
94
+ });
95
+ }
96
+ }
97
+ });
98
+ child.stderr.on('data', (b) => logs.push(b.toString('utf8')));
99
+ child.on('error', err => {
100
+ clearTimeout(timeout);
101
+ cleanup();
102
+ resolve({ ok: false, logs, error: { name: 'SpawnError', message: err.message }, durationMs: Date.now() - started });
103
+ });
104
+ child.on('exit', code => {
105
+ cleanup();
106
+ if (code !== 0 && code !== null) {
107
+ clearTimeout(timeout);
108
+ resolve({ ok: false, logs, error: { name: 'ExitError', message: `deno exited with ${code}` }, durationMs: Date.now() - started });
109
+ }
110
+ });
111
+ });
112
+ }
113
+ }
114
+ function writeFrame(stream, obj) {
115
+ const payload = JSON.stringify(obj);
116
+ const len = Buffer.byteLength(payload, 'utf8').toString(16).padStart(8, '0');
117
+ stream.write(len + '\n' + payload);
118
+ }
119
+ // `bindings` is interpolated into the generated source via JSON.stringify; it is safe
120
+ // only because names are pre-validated as JS identifiers by getSources()
121
+ // (src/sdk/sources/index.ts). Do NOT pass unvalidated strings here.
122
+ export function buildDenoRunner(userCode, timeoutMs, bindings) {
123
+ // Runs inside deno. Talks to host over stdin/stdout with the same framing.
124
+ return `
125
+ const enc = new TextEncoder();
126
+ const dec = new TextDecoder();
127
+
128
+ function writeFrame(obj) {
129
+ const s = JSON.stringify(obj);
130
+ const len = (new TextEncoder().encode(s)).byteLength.toString(16).padStart(8, '0');
131
+ Deno.stdout.writeSync(enc.encode(len + '\\n' + s));
132
+ }
133
+
134
+ let inbuf = new Uint8Array(0);
135
+ const pending = new Map();
136
+ let nextId = 1;
137
+
138
+ async function readLoop() {
139
+ const reader = Deno.stdin.readable.getReader();
140
+ while (true) {
141
+ const { value, done } = await reader.read();
142
+ if (done) return;
143
+ const merged = new Uint8Array(inbuf.length + value.length);
144
+ merged.set(inbuf); merged.set(value, inbuf.length);
145
+ inbuf = merged;
146
+ while (inbuf.length >= 9) {
147
+ const lenHex = dec.decode(inbuf.subarray(0, 8));
148
+ const len = parseInt(lenHex, 16);
149
+ if (isNaN(len)) { inbuf = new Uint8Array(0); break; }
150
+ if (inbuf.length < 9 + len) break;
151
+ const payload = dec.decode(inbuf.subarray(9, 9 + len));
152
+ inbuf = inbuf.subarray(9 + len);
153
+ const msg = JSON.parse(payload);
154
+ if (msg.kind === 'rpc-result') {
155
+ const cb = pending.get(msg.id);
156
+ if (cb) { pending.delete(msg.id); cb(msg); }
157
+ }
158
+ }
159
+ }
160
+ }
161
+ readLoop();
162
+
163
+ function call(binding, path, args) {
164
+ return new Promise((resolve, reject) => {
165
+ const id = nextId++;
166
+ pending.set(id, (r) => {
167
+ if (!r.ok) { const e = new Error(r.error.message); e.name = r.error.name; reject(e); }
168
+ else resolve(r.value);
169
+ });
170
+ writeFrame({ kind: 'rpc', id, binding, path, args });
171
+ });
172
+ }
173
+
174
+ function makeProxy(binding) {
175
+ const handler = (path) => new Proxy(function(){}, {
176
+ get(_, key) { return handler([...path, String(key)]); },
177
+ apply(_, __, args) { return call(binding, path, args); },
178
+ });
179
+ return handler([]);
180
+ }
181
+
182
+ const __bindings = ${JSON.stringify(bindings)};
183
+ for (const name of __bindings) { globalThis[name] = makeProxy(name); }
184
+
185
+ const origLog = console.log;
186
+ console.log = (...a) => writeFrame({ kind: 'log', text: a.map(String).join(' ') });
187
+ console.info = console.log; console.warn = console.log; console.error = console.log;
188
+
189
+ const userFn = async () => {
190
+ ${userCode}
191
+ };
192
+
193
+ const timer = setTimeout(() => {
194
+ writeFrame({ kind: 'result', ok: false, error: { name: 'TimeoutError', message: 'Execution exceeded ${timeoutMs}ms' }});
195
+ Deno.exit(0);
196
+ }, ${timeoutMs});
197
+
198
+ userFn()
199
+ .then(value => { clearTimeout(timer); writeFrame({ kind: 'result', ok: true, value }); Deno.exit(0); })
200
+ .catch(err => { clearTimeout(timer); writeFrame({ kind: 'result', ok: false, error: { name: err.name || 'Error', message: err.message || String(err) }}); Deno.exit(0); });
201
+ `;
202
+ }
203
+ //# sourceMappingURL=deno.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deno.js","sourceRoot":"","sources":["../../src/sandbox/deno.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ import type { ExecuteOptions, ExecuteResult, RpcBridge, SandboxKind, SandboxRunner } from './types.js';
2
+ import { IsolateRunner } from './isolate.js';
3
+ import { DenoRunner } from './deno.js';
4
+ export { IsolateRunner, DenoRunner };
5
+ export type { SandboxKind, SandboxRunner } from './types.js';
6
+ export type { ExecuteOptions, ExecuteResult, RpcBridge, RpcCall } from './types.js';
7
+ /**
8
+ * Placeholder runner that returns a clear error when `execute` is invoked.
9
+ * Keeps `search_api` and `describe_schema` functional in environments where
10
+ * neither `isolated-vm` nor `deno` is available.
11
+ */
12
+ export declare class UnavailableRunner implements SandboxRunner {
13
+ private readonly reason;
14
+ kind: "unavailable";
15
+ constructor(reason: string);
16
+ available(): Promise<boolean>;
17
+ execute(_opts: ExecuteOptions, _rpc: RpcBridge): Promise<ExecuteResult>;
18
+ }
19
+ export declare function pickSandbox(preferred: SandboxKind): Promise<SandboxRunner>;
20
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/sandbox/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,SAAS,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AACvG,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAEvC,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,CAAC;AACrC,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC7D,YAAY,EAAE,cAAc,EAAE,aAAa,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAEpF;;;;GAIG;AACH,qBAAa,iBAAkB,YAAW,aAAa;IAEzC,OAAO,CAAC,QAAQ,CAAC,MAAM;IADnC,IAAI,EAAG,aAAa,CAAU;gBACD,MAAM,EAAE,MAAM;IACrC,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC;IAC7B,OAAO,CAAC,KAAK,EAAE,cAAc,EAAE,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC,aAAa,CAAC;CAQ9E;AAED,wBAAsB,WAAW,CAAC,SAAS,EAAE,WAAW,GAAG,OAAO,CAAC,aAAa,CAAC,CAgBhF"}
@@ -0,0 +1,46 @@
1
+ import { IsolateRunner } from './isolate.js';
2
+ import { DenoRunner } from './deno.js';
3
+ export { IsolateRunner, DenoRunner };
4
+ /**
5
+ * Placeholder runner that returns a clear error when `execute` is invoked.
6
+ * Keeps `search_api` and `describe_schema` functional in environments where
7
+ * neither `isolated-vm` nor `deno` is available.
8
+ */
9
+ export class UnavailableRunner {
10
+ reason;
11
+ kind = 'unavailable';
12
+ constructor(reason) {
13
+ this.reason = reason;
14
+ }
15
+ async available() { return false; }
16
+ async execute(_opts, _rpc) {
17
+ return {
18
+ ok: false,
19
+ logs: [],
20
+ error: { name: 'SandboxUnavailable', message: this.reason },
21
+ durationMs: 0,
22
+ };
23
+ }
24
+ }
25
+ export async function pickSandbox(preferred) {
26
+ if (preferred === 'isolate') {
27
+ const r = new IsolateRunner();
28
+ if (await r.available())
29
+ return r;
30
+ return new UnavailableRunner('isolated-vm is not installed on this platform; install it or run with --sandbox deno');
31
+ }
32
+ if (preferred === 'deno') {
33
+ const r = new DenoRunner();
34
+ if (await r.available())
35
+ return r;
36
+ return new UnavailableRunner('Deno is not available on PATH; install Deno or run with --sandbox isolate');
37
+ }
38
+ const iso = new IsolateRunner();
39
+ if (await iso.available())
40
+ return iso;
41
+ const deno = new DenoRunner();
42
+ if (await deno.available())
43
+ return deno;
44
+ return new UnavailableRunner('No sandbox runner available. Install isolated-vm or Deno to use the `execute` tool. search_api and describe_schema still work.');
45
+ }
46
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/sandbox/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAEvC,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,CAAC;AAIrC;;;;GAIG;AACH,MAAM,OAAO,iBAAiB;IAEC;IAD7B,IAAI,GAAG,aAAsB,CAAC;IAC9B,YAA6B,MAAc;QAAd,WAAM,GAAN,MAAM,CAAQ;IAAG,CAAC;IAC/C,KAAK,CAAC,SAAS,KAAuB,OAAO,KAAK,CAAC,CAAC,CAAC;IACrD,KAAK,CAAC,OAAO,CAAC,KAAqB,EAAE,IAAe;QAClD,OAAO;YACL,EAAE,EAAE,KAAK;YACT,IAAI,EAAE,EAAE;YACR,KAAK,EAAE,EAAE,IAAI,EAAE,oBAAoB,EAAE,OAAO,EAAE,IAAI,CAAC,MAAM,EAAE;YAC3D,UAAU,EAAE,CAAC;SACd,CAAC;IACJ,CAAC;CACF;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,SAAsB;IACtD,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;QAC5B,MAAM,CAAC,GAAG,IAAI,aAAa,EAAE,CAAC;QAC9B,IAAI,MAAM,CAAC,CAAC,SAAS,EAAE;YAAE,OAAO,CAAC,CAAC;QAClC,OAAO,IAAI,iBAAiB,CAAC,sFAAsF,CAAC,CAAC;IACvH,CAAC;IACD,IAAI,SAAS,KAAK,MAAM,EAAE,CAAC;QACzB,MAAM,CAAC,GAAG,IAAI,UAAU,EAAE,CAAC;QAC3B,IAAI,MAAM,CAAC,CAAC,SAAS,EAAE;YAAE,OAAO,CAAC,CAAC;QAClC,OAAO,IAAI,iBAAiB,CAAC,2EAA2E,CAAC,CAAC;IAC5G,CAAC;IACD,MAAM,GAAG,GAAG,IAAI,aAAa,EAAE,CAAC;IAChC,IAAI,MAAM,GAAG,CAAC,SAAS,EAAE;QAAE,OAAO,GAAG,CAAC;IACtC,MAAM,IAAI,GAAG,IAAI,UAAU,EAAE,CAAC;IAC9B,IAAI,MAAM,IAAI,CAAC,SAAS,EAAE;QAAE,OAAO,IAAI,CAAC;IACxC,OAAO,IAAI,iBAAiB,CAAC,gIAAgI,CAAC,CAAC;AACjK,CAAC"}
@@ -0,0 +1,7 @@
1
+ import type { ExecuteOptions, ExecuteResult, RpcBridge, SandboxRunner } from './types.js';
2
+ export declare class IsolateRunner implements SandboxRunner {
3
+ kind: "isolate";
4
+ available(): Promise<boolean>;
5
+ execute(opts: ExecuteOptions, rpc: RpcBridge): Promise<ExecuteResult>;
6
+ }
7
+ //# sourceMappingURL=isolate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"isolate.d.ts","sourceRoot":"","sources":["../../src/sandbox/isolate.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAoB1F,qBAAa,aAAc,YAAW,aAAa;IACjD,IAAI,EAAG,SAAS,CAAU;IAEpB,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC;IAY7B,OAAO,CAAC,IAAI,EAAE,cAAc,EAAE,GAAG,EAAE,SAAS,GAAG,OAAO,CAAC,aAAa,CAAC;CAoF5E"}
@@ -0,0 +1,100 @@
1
+ import { preflight } from './policy.js';
2
+ async function loadIvm() {
3
+ const mod = await import('isolated-vm');
4
+ const candidate = (typeof mod.Isolate === 'function' ? mod : mod.default ?? {});
5
+ if (typeof candidate.Isolate !== 'function')
6
+ throw new Error('isolated-vm native binding unavailable');
7
+ return candidate;
8
+ }
9
+ export class IsolateRunner {
10
+ kind = 'isolate';
11
+ async available() {
12
+ try {
13
+ const ivm = await loadIvm();
14
+ // Confirm the native binding actually loaded by exercising it.
15
+ const iso = new ivm.Isolate({ memoryLimit: 16 });
16
+ iso.dispose();
17
+ return true;
18
+ }
19
+ catch {
20
+ return false;
21
+ }
22
+ }
23
+ async execute(opts, rpc) {
24
+ const started = Date.now();
25
+ const policy = preflight(opts.code);
26
+ if (!policy.ok) {
27
+ return {
28
+ ok: false,
29
+ logs: [],
30
+ error: { name: 'PolicyError', message: policy.errors.join('; ') },
31
+ durationMs: Date.now() - started,
32
+ };
33
+ }
34
+ // dynamic import — keeps the optional dep optional at runtime.
35
+ const ivm = await loadIvm();
36
+ const isolate = new ivm.Isolate({ memoryLimit: opts.memoryMb ?? 64 });
37
+ const context = await isolate.createContext();
38
+ const jail = context.global;
39
+ await jail.set('global', jail.derefInto());
40
+ const logs = [];
41
+ // host-side log sink
42
+ await context.evalClosure(`globalThis.console = { log: (...a) => $0.applyIgnored(undefined, [a.map(String).join(' ')]),
43
+ error: (...a) => $0.applyIgnored(undefined, [a.map(String).join(' ')]),
44
+ warn: (...a) => $0.applyIgnored(undefined, [a.map(String).join(' ')]),
45
+ info: (...a) => $0.applyIgnored(undefined, [a.map(String).join(' ')]) };`, [(line) => logs.push(String(line))], { arguments: { reference: true } });
46
+ // RPC bridge — pass the host function directly; `arguments: { reference: true }`
47
+ // auto-wraps each argument as a Reference inside the isolate.
48
+ const hostRpc = async (binding, path, args) => {
49
+ const result = await rpc.dispatch({ binding, path, args });
50
+ return new ivm.ExternalCopy(result).copyInto();
51
+ };
52
+ // Safe to interpolate: binding names are pre-validated as JS identifiers by
53
+ // getSources() (src/sdk/sources/index.ts). Do NOT interpolate unvalidated strings here.
54
+ const namesLiteral = JSON.stringify(opts.bindings ?? []);
55
+ await context.evalClosure(`function makeProxy(binding, rpc) {
56
+ const handler = (path) => new Proxy(function(){}, {
57
+ get(_, key) { return handler([...path, String(key)]); },
58
+ apply(_, __, args) {
59
+ return rpc.apply(
60
+ undefined,
61
+ [binding, path, args],
62
+ { arguments: { copy: true }, result: { promise: true, copy: true } },
63
+ ).then(r => {
64
+ if (!r.ok) { const e = new Error(r.error.message); e.name = r.error.name; throw e; }
65
+ return r.value;
66
+ });
67
+ },
68
+ });
69
+ return handler([]);
70
+ }
71
+ for (const name of ${namesLiteral}) { globalThis[name] = makeProxy(name, $0); }`, [hostRpc], { arguments: { reference: true } });
72
+ try {
73
+ const wrapped = `(async () => { ${opts.code}\n })()`;
74
+ const script = await isolate.compileScript(wrapped);
75
+ const result = await script.run(context, {
76
+ timeout: opts.timeoutMs ?? 15_000,
77
+ promise: true,
78
+ copy: true,
79
+ });
80
+ return { ok: true, value: result, logs, durationMs: Date.now() - started };
81
+ }
82
+ catch (err) {
83
+ const e = err;
84
+ return {
85
+ ok: false,
86
+ logs,
87
+ error: { name: e.name ?? 'Error', message: e.message ?? String(err), stack: e.stack },
88
+ durationMs: Date.now() - started,
89
+ };
90
+ }
91
+ finally {
92
+ try {
93
+ context.release();
94
+ isolate.dispose();
95
+ }
96
+ catch { /* ignore */ }
97
+ }
98
+ }
99
+ }
100
+ //# sourceMappingURL=isolate.js.map