@toolrelay/cli 1.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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/api-client.ts"],"names":[],"mappings":"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","file":"chunk-3LR6JESE.js","sourcesContent":["import type { ResolvedAuth } from './credentials.js';\n\n// ─── Types ──────────────────────────────────────────────────────────────────\n\nexport interface ApiApp {\n id: string;\n name: string;\n slug: string;\n description: string;\n base_url: string;\n auth_type: string;\n auth_config?: Record<string, unknown>;\n is_published: boolean;\n tool_count?: number;\n created_at: string;\n updated_at: string;\n}\n\nexport interface ApiTool {\n id: string;\n app_id: string;\n name: string;\n slug: string;\n description: string;\n http_method: string;\n endpoint_path: string;\n parameter_mapping: unknown[];\n headers_template?: Record<string, string>;\n permission_level: string;\n request_schema?: Record<string, unknown>;\n response_schema?: Record<string, unknown>;\n mcp_annotations?: Record<string, unknown>;\n is_active: boolean;\n sort_order: number;\n created_at: string;\n updated_at: string;\n}\n\nexport interface ApiUser {\n id: string;\n email: string;\n name: string;\n tier: string;\n}\n\nexport interface ApiError {\n status: number;\n message: string;\n error?: string;\n}\n\n// ─── Client ─────────────────────────────────────────────────────────────────\n\nexport class ToolRelayApiClient {\n private baseUrl: string;\n private token: string;\n\n constructor(auth: ResolvedAuth) {\n this.baseUrl = auth.api_url.replace(/\\/$/, '');\n this.token = auth.token;\n }\n\n // ─── Auth ───────────────────────────────────────────────────────────\n\n async whoami(): Promise<ApiUser> {\n return this.request<ApiUser>('GET', '/api/auth/me');\n }\n\n // ─── Apps ───────────────────────────────────────────────────────────\n\n async listApps(): Promise<{ apps: ApiApp[] }> {\n return this.request<{ apps: ApiApp[] }>('GET', '/api/apps');\n }\n\n async getApp(appId: string): Promise<{ app: ApiApp }> {\n return this.request<{ app: ApiApp }>('GET', `/api/apps/${appId}`);\n }\n\n async createApp(body: Record<string, unknown>): Promise<{ app: ApiApp }> {\n return this.request<{ app: ApiApp }>('POST', '/api/apps', body);\n }\n\n async updateApp(appId: string, body: Record<string, unknown>): Promise<{ app: ApiApp }> {\n return this.request<{ app: ApiApp }>('PUT', `/api/apps/${appId}`, body);\n }\n\n async publishApp(appId: string): Promise<{ app: ApiApp; message: string }> {\n return this.request<{ app: ApiApp; message: string }>('POST', `/api/apps/${appId}/publish`);\n }\n\n // ─── Tools ──────────────────────────────────────────────────────────\n\n async listTools(appId: string): Promise<{ tools: ApiTool[] }> {\n return this.request<{ tools: ApiTool[] }>('GET', `/api/apps/${appId}/tools`);\n }\n\n async createTool(appId: string, body: Record<string, unknown>): Promise<{ tool: ApiTool }> {\n return this.request<{ tool: ApiTool }>('POST', `/api/apps/${appId}/tools`, body);\n }\n\n async updateTool(appId: string, toolId: string, body: Record<string, unknown>): Promise<{ tool: ApiTool }> {\n return this.request<{ tool: ApiTool }>('PUT', `/api/apps/${appId}/tools/${toolId}`, body);\n }\n\n async deleteTool(appId: string, toolId: string): Promise<void> {\n await this.request<{ message: string }>('DELETE', `/api/apps/${appId}/tools/${toolId}`);\n }\n\n // ─── HTTP ───────────────────────────────────────────────────────────\n\n private async request<T>(method: string, path: string, body?: Record<string, unknown>): Promise<T> {\n const url = `${this.baseUrl}${path}`;\n const headers: Record<string, string> = {\n 'Authorization': `Bearer ${this.token}`,\n 'Content-Type': 'application/json',\n 'User-Agent': 'toolrelay-cli/0.1.0',\n };\n\n const response = await fetch(url, {\n method,\n headers,\n body: body ? JSON.stringify(body) : undefined,\n });\n\n if (!response.ok) {\n let errorMessage: string;\n try {\n const errorBody = await response.json() as { error?: string; message?: string };\n errorMessage = errorBody.error || errorBody.message || response.statusText;\n } catch {\n errorMessage = response.statusText;\n }\n\n const err: ApiError = {\n status: response.status,\n message: errorMessage,\n };\n throw err;\n }\n\n // DELETE may return empty body\n const text = await response.text();\n if (!text) return {} as T;\n return JSON.parse(text) as T;\n }\n}\n"]}
@@ -0,0 +1,371 @@
1
+ #!/usr/bin/env node
2
+ import { URL } from 'url';
3
+ import { isIP } from 'net';
4
+ import 'dns/promises';
5
+ import { z } from 'zod';
6
+
7
+ var PRIVATE_IP_RANGES = [
8
+ // IPv4 private ranges
9
+ { start: ip4ToInt("10.0.0.0"), end: ip4ToInt("10.255.255.255") },
10
+ { start: ip4ToInt("172.16.0.0"), end: ip4ToInt("172.31.255.255") },
11
+ { start: ip4ToInt("192.168.0.0"), end: ip4ToInt("192.168.255.255") },
12
+ // Loopback
13
+ { start: ip4ToInt("127.0.0.0"), end: ip4ToInt("127.255.255.255") },
14
+ // Link-local
15
+ { start: ip4ToInt("169.254.0.0"), end: ip4ToInt("169.254.255.255") },
16
+ // CGNAT / shared address space
17
+ { start: ip4ToInt("100.64.0.0"), end: ip4ToInt("100.127.255.255") },
18
+ // AWS metadata endpoint
19
+ { start: ip4ToInt("169.254.169.254"), end: ip4ToInt("169.254.169.254") },
20
+ // 0.0.0.0/8
21
+ { start: ip4ToInt("0.0.0.0"), end: ip4ToInt("0.255.255.255") }
22
+ ];
23
+ var BLOCKED_HOSTNAMES = /* @__PURE__ */ new Set([
24
+ "localhost",
25
+ "metadata.google.internal",
26
+ "metadata.google.com",
27
+ "kubernetes.default.svc"
28
+ ]);
29
+ var BLOCKED_HEADERS = /* @__PURE__ */ new Set([
30
+ "host",
31
+ "transfer-encoding",
32
+ "content-length",
33
+ "connection",
34
+ "upgrade",
35
+ "x-forwarded-for",
36
+ "x-forwarded-host",
37
+ "x-forwarded-proto",
38
+ "x-real-ip",
39
+ "via",
40
+ "te",
41
+ "trailer",
42
+ "x-toolrelay-verify"
43
+ ]);
44
+ function ip4ToInt(ip) {
45
+ const parts = ip.split(".").map(Number);
46
+ return (parts[0] << 24 | parts[1] << 16 | parts[2] << 8 | parts[3]) >>> 0;
47
+ }
48
+ function isPrivateIPv4(ip) {
49
+ const num = ip4ToInt(ip);
50
+ return PRIVATE_IP_RANGES.some((range) => num >= range.start && num <= range.end);
51
+ }
52
+ function isPrivateIPv6(ip) {
53
+ const normalized = ip.toLowerCase();
54
+ if (normalized === "::1" || normalized === "0:0:0:0:0:0:0:1") return true;
55
+ if (normalized === "::" || normalized === "0:0:0:0:0:0:0:0") return true;
56
+ if (normalized.startsWith("fc") || normalized.startsWith("fd")) return true;
57
+ if (normalized.startsWith("fe80")) return true;
58
+ const v4Mapped = normalized.match(/^::ffff:(\d+\.\d+\.\d+\.\d+)$/);
59
+ if (v4Mapped) return isPrivateIPv4(v4Mapped[1]);
60
+ return false;
61
+ }
62
+ function isPrivateIP(ip) {
63
+ if (isIP(ip) === 4) return isPrivateIPv4(ip);
64
+ if (isIP(ip) === 6) return isPrivateIPv6(ip);
65
+ return false;
66
+ }
67
+ function validateUrlNotPrivate(urlString) {
68
+ let parsed;
69
+ try {
70
+ parsed = new URL(urlString);
71
+ } catch {
72
+ return { valid: false, error: "Invalid URL" };
73
+ }
74
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
75
+ return { valid: false, error: "Only http and https URLs are allowed" };
76
+ }
77
+ const hostname = parsed.hostname;
78
+ if (BLOCKED_HOSTNAMES.has(hostname.toLowerCase())) {
79
+ return { valid: false, error: "URLs pointing to internal services are not allowed" };
80
+ }
81
+ const lowerHost = hostname.toLowerCase();
82
+ if (lowerHost.endsWith(".internal") || lowerHost.endsWith(".local") || lowerHost.endsWith(".localhost")) {
83
+ return { valid: false, error: "URLs pointing to internal services are not allowed" };
84
+ }
85
+ if (isIP(hostname)) {
86
+ if (isPrivateIP(hostname)) {
87
+ return { valid: false, error: "URLs pointing to private IP addresses are not allowed" };
88
+ }
89
+ }
90
+ return { valid: true };
91
+ }
92
+
93
+ // ../shared/src/types.ts
94
+ var AuthType = /* @__PURE__ */ ((AuthType2) => {
95
+ AuthType2["none"] = "none";
96
+ AuthType2["static_token"] = "static_token";
97
+ AuthType2["oauth2"] = "oauth2";
98
+ AuthType2["api_key_relay"] = "api_key_relay";
99
+ AuthType2["custom_header"] = "custom_header";
100
+ return AuthType2;
101
+ })(AuthType || {});
102
+ var HttpMethod = /* @__PURE__ */ ((HttpMethod2) => {
103
+ HttpMethod2["GET"] = "GET";
104
+ HttpMethod2["POST"] = "POST";
105
+ HttpMethod2["PUT"] = "PUT";
106
+ HttpMethod2["PATCH"] = "PATCH";
107
+ HttpMethod2["DELETE"] = "DELETE";
108
+ return HttpMethod2;
109
+ })(HttpMethod || {});
110
+ var PermissionLevel = /* @__PURE__ */ ((PermissionLevel2) => {
111
+ PermissionLevel2["read"] = "read";
112
+ PermissionLevel2["write"] = "write";
113
+ PermissionLevel2["admin"] = "admin";
114
+ return PermissionLevel2;
115
+ })(PermissionLevel || {});
116
+ var ApiKeyTier = /* @__PURE__ */ ((ApiKeyTier2) => {
117
+ ApiKeyTier2["free"] = "free";
118
+ ApiKeyTier2["basic"] = "basic";
119
+ ApiKeyTier2["pro"] = "pro";
120
+ ApiKeyTier2["enterprise"] = "enterprise";
121
+ return ApiKeyTier2;
122
+ })(ApiKeyTier || {});
123
+ var X402_MIN_PRICE = 2e3;
124
+
125
+ // ../shared/src/schemas.ts
126
+ z.object({
127
+ email: z.string().email(),
128
+ password: z.string().min(8),
129
+ name: z.string().min(1)
130
+ });
131
+ z.object({
132
+ email: z.string().email(),
133
+ password: z.string().min(1)
134
+ });
135
+ var SLUG_REGEX = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
136
+ var SLUG_MESSAGE = "Lowercase letters, numbers, and hyphens only (no leading/trailing hyphens)";
137
+ var TOOL_NAME_REGEX = /^[a-z][a-z0-9_]*$/;
138
+ var TOOL_NAME_MESSAGE = "Tool name must be snake_case (lowercase letters, numbers, underscores)";
139
+ var createAppSchema = z.object({
140
+ name: z.string().min(1).max(100),
141
+ slug: z.string().min(2).max(100).regex(SLUG_REGEX, SLUG_MESSAGE).optional(),
142
+ description: z.string().max(500),
143
+ base_url: z.string().url(),
144
+ auth_type: z.nativeEnum(AuthType),
145
+ auth_config: z.record(z.unknown()).optional(),
146
+ logo_url: z.string().url().nullable().optional(),
147
+ auto_approve_keys: z.boolean().optional(),
148
+ default_key_tier: z.nativeEnum(ApiKeyTier).optional(),
149
+ dormant_key_days: z.number().int().min(7).max(365).nullable().optional(),
150
+ global_headers: z.record(z.string(), z.string()).optional(),
151
+ privacy_policy_url: z.string().url().nullable().optional(),
152
+ support_email: z.string().email().nullable().optional(),
153
+ documentation_url: z.string().url().nullable().optional(),
154
+ // OAuth 2.0 fields (required when auth_type is 'oauth2')
155
+ oauth_authorize_url: z.string().url().optional(),
156
+ oauth_token_url: z.string().url().optional(),
157
+ oauth_client_id: z.string().min(1).nullable().optional(),
158
+ oauth_client_secret: z.string().min(1).nullable().optional(),
159
+ oauth_scopes: z.string().nullable().optional(),
160
+ enable_cli_access: z.boolean().optional(),
161
+ // x402 payment gateway
162
+ x402_enabled: z.boolean().optional(),
163
+ x402_wallet_address: z.string().regex(/^0x[0-9a-fA-F]{40}$/, "Must be a valid Ethereum address (0x + 40 hex chars)").nullable().optional(),
164
+ x402_default_price: z.number().int().min(X402_MIN_PRICE, `Minimum price is ${X402_MIN_PRICE} micro-units ($${(X402_MIN_PRICE / 1e6).toFixed(4)} USDC) to cover settlement costs`).nullable().optional(),
165
+ x402_asset: z.string().max(255).optional(),
166
+ x402_network: z.string().max(50).optional(),
167
+ x402_bazaar_listed: z.boolean().optional()
168
+ });
169
+ createAppSchema.partial();
170
+ z.object({
171
+ base_url: z.string().url(),
172
+ auth_type: z.nativeEnum(AuthType),
173
+ auth_config: z.record(z.unknown()).optional(),
174
+ oauth_token_url: z.string().url().optional(),
175
+ oauth_authorize_url: z.string().url().optional()
176
+ });
177
+ var parameterMappingSchema = z.object({
178
+ name: z.string().min(1),
179
+ type: z.enum(["string", "number", "boolean", "object", "array"]),
180
+ required: z.boolean(),
181
+ description: z.string().optional(),
182
+ target: z.enum(["path", "query", "body", "header"]),
183
+ backend_key: z.string().optional(),
184
+ default_value: z.unknown().optional()
185
+ });
186
+ var mcpAnnotationsSchema = z.object({
187
+ readOnlyHint: z.boolean().optional(),
188
+ destructiveHint: z.boolean().optional(),
189
+ idempotentHint: z.boolean().optional(),
190
+ openWorldHint: z.boolean().optional()
191
+ });
192
+ var createToolSchema = z.object({
193
+ name: z.string().min(1),
194
+ description: z.string(),
195
+ http_method: z.nativeEnum(HttpMethod),
196
+ endpoint_path: z.string(),
197
+ request_schema: z.record(z.unknown()).optional(),
198
+ response_schema: z.record(z.unknown()).optional(),
199
+ parameter_mapping: z.array(parameterMappingSchema).optional(),
200
+ headers_template: z.record(z.string()).optional(),
201
+ permission_level: z.nativeEnum(PermissionLevel),
202
+ rate_limit_override: z.number().int().positive().nullable().optional(),
203
+ cache_ttl_seconds: z.number().int().min(0).optional(),
204
+ mcp_annotations: mcpAnnotationsSchema.optional(),
205
+ // x402 payment gateway
206
+ x402_enabled: z.boolean().nullable().optional(),
207
+ x402_price_override: z.number().int().min(X402_MIN_PRICE, `Minimum price is ${X402_MIN_PRICE} micro-units ($${(X402_MIN_PRICE / 1e6).toFixed(4)} USDC) to cover settlement costs`).nullable().optional()
208
+ });
209
+ createToolSchema.partial();
210
+ z.object({
211
+ consumer_name: z.string().min(1),
212
+ consumer_email: z.string().email(),
213
+ tier: z.nativeEnum(ApiKeyTier).optional(),
214
+ scopes: z.array(z.string()).optional(),
215
+ expires_at: z.string().datetime().optional()
216
+ });
217
+ z.object({
218
+ name: z.string().min(1),
219
+ email: z.string().email(),
220
+ message: z.string().optional()
221
+ });
222
+ z.object({
223
+ consumer_email: z.string().email(),
224
+ reason: z.string().max(500).optional()
225
+ });
226
+ z.object({
227
+ consumer_email: z.string().email()
228
+ });
229
+ z.object({
230
+ spec: z.union([z.string(), z.record(z.unknown())]),
231
+ overwrite: z.boolean().optional()
232
+ });
233
+ z.object({
234
+ name: z.string().min(1).max(100),
235
+ expires_at: z.string().datetime().optional()
236
+ });
237
+
238
+ // ../shared/src/parameter-mapping.ts
239
+ function setNested(obj, path, value) {
240
+ const keys = path.split(".");
241
+ let current = obj;
242
+ for (let i = 0; i < keys.length - 1; i++) {
243
+ const key = keys[i];
244
+ if (!(key in current) || typeof current[key] !== "object" || current[key] === null) {
245
+ current[key] = {};
246
+ }
247
+ current = current[key];
248
+ }
249
+ const lastKey = keys[keys.length - 1];
250
+ current[lastKey] = value;
251
+ }
252
+ function resolveValue(mapping, input) {
253
+ if (mapping.name in input) {
254
+ return input[mapping.name];
255
+ }
256
+ return mapping.default_value;
257
+ }
258
+ function buildBackendRequest(tool, app, inputPayload, consumerToken) {
259
+ let url = app.base_url.replace(/\/+$/, "") + tool.endpoint_path;
260
+ const headers = {};
261
+ const queryParams = [];
262
+ const body = {};
263
+ let hasBodyParams = false;
264
+ for (const mapping of tool.parameter_mapping) {
265
+ if (mapping.required) {
266
+ const value = resolveValue(mapping, inputPayload);
267
+ if (value === void 0) {
268
+ throw new Error(`Missing required parameter: ${mapping.name}`);
269
+ }
270
+ }
271
+ }
272
+ for (const mapping of tool.parameter_mapping) {
273
+ const value = resolveValue(mapping, inputPayload);
274
+ if (value === void 0) {
275
+ continue;
276
+ }
277
+ const backendKey = mapping.backend_key ?? mapping.name;
278
+ switch (mapping.target) {
279
+ case "path":
280
+ url = url.split(`{${backendKey}}`).join(encodeURIComponent(String(value)));
281
+ break;
282
+ case "query":
283
+ queryParams.push([backendKey, String(value)]);
284
+ break;
285
+ case "body":
286
+ setNested(body, backendKey, value);
287
+ hasBodyParams = true;
288
+ break;
289
+ case "header":
290
+ if (BLOCKED_HEADERS.has(backendKey.toLowerCase())) {
291
+ throw new Error(`Parameter mapping cannot set restricted header: ${backendKey}`);
292
+ }
293
+ headers[backendKey] = String(value);
294
+ break;
295
+ }
296
+ }
297
+ if (queryParams.length > 0) {
298
+ const qs = queryParams.map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`).join("&");
299
+ url += (url.includes("?") ? "&" : "?") + qs;
300
+ }
301
+ if (tool.headers_template) {
302
+ for (const [key, value] of Object.entries(tool.headers_template)) {
303
+ if (!(key in headers)) {
304
+ headers[key] = value;
305
+ }
306
+ }
307
+ }
308
+ if (app.global_headers) {
309
+ for (const [key, value] of Object.entries(app.global_headers)) {
310
+ if (BLOCKED_HEADERS.has(key.toLowerCase())) continue;
311
+ if (!(key in headers)) {
312
+ headers[key] = value;
313
+ }
314
+ }
315
+ }
316
+ const authConfig = app.auth_config;
317
+ switch (app.auth_type) {
318
+ case "static_token" /* static_token */: {
319
+ const token = authConfig["token"];
320
+ if (token) {
321
+ headers["Authorization"] = `Bearer ${token}`;
322
+ }
323
+ break;
324
+ }
325
+ case "oauth2" /* oauth2 */: {
326
+ const token = consumerToken ?? authConfig["access_token"];
327
+ if (token) {
328
+ headers["Authorization"] = `Bearer ${token}`;
329
+ }
330
+ break;
331
+ }
332
+ case "api_key_relay" /* api_key_relay */: {
333
+ const headerName = authConfig["header_name"];
334
+ const apiKey = authConfig["api_key"];
335
+ if (headerName && apiKey) {
336
+ headers[headerName] = apiKey;
337
+ }
338
+ break;
339
+ }
340
+ case "custom_header" /* custom_header */: {
341
+ const customHeaders = authConfig["headers"];
342
+ if (customHeaders) {
343
+ for (const [key, value] of Object.entries(customHeaders)) {
344
+ if (BLOCKED_HEADERS.has(key.toLowerCase())) continue;
345
+ headers[key] = value;
346
+ }
347
+ }
348
+ break;
349
+ }
350
+ case "none" /* none */:
351
+ default:
352
+ if (authConfig["verify_header"]) {
353
+ headers["X-ToolRelay-Verify"] = authConfig["verify_header"];
354
+ }
355
+ break;
356
+ }
357
+ const request = {
358
+ url,
359
+ method: tool.http_method,
360
+ headers
361
+ };
362
+ if (hasBodyParams) {
363
+ headers["Content-Type"] = headers["Content-Type"] ?? "application/json";
364
+ request.body = body;
365
+ }
366
+ return request;
367
+ }
368
+
369
+ export { AuthType, HttpMethod, PermissionLevel, SLUG_MESSAGE, SLUG_REGEX, TOOL_NAME_MESSAGE, TOOL_NAME_REGEX, buildBackendRequest, mcpAnnotationsSchema, validateUrlNotPrivate };
370
+ //# sourceMappingURL=chunk-7AYNBNB4.js.map
371
+ //# sourceMappingURL=chunk-7AYNBNB4.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../shared/src/url-validation.ts","../../shared/src/types.ts","../../shared/src/constants.ts","../../shared/src/schemas.ts","../../shared/src/parameter-mapping.ts"],"names":["AuthType","HttpMethod","PermissionLevel","ApiKeyTier"],"mappings":"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","file":"chunk-7AYNBNB4.js","sourcesContent":["import { URL } from 'node:url';\nimport { isIP } from 'node:net';\nimport dns from 'node:dns/promises';\n\n/**\n * SSRF protection: blocks requests to private/internal IP ranges.\n *\n * Checks the hostname of a URL against known private, loopback, link-local,\n * and reserved IP ranges. Also blocks common internal hostnames.\n * For non-IP hostnames, resolves DNS and validates the resolved addresses\n * to prevent DNS rebinding attacks.\n */\n\nconst PRIVATE_IP_RANGES = [\n // IPv4 private ranges\n { start: ip4ToInt('10.0.0.0'), end: ip4ToInt('10.255.255.255') },\n { start: ip4ToInt('172.16.0.0'), end: ip4ToInt('172.31.255.255') },\n { start: ip4ToInt('192.168.0.0'), end: ip4ToInt('192.168.255.255') },\n // Loopback\n { start: ip4ToInt('127.0.0.0'), end: ip4ToInt('127.255.255.255') },\n // Link-local\n { start: ip4ToInt('169.254.0.0'), end: ip4ToInt('169.254.255.255') },\n // CGNAT / shared address space\n { start: ip4ToInt('100.64.0.0'), end: ip4ToInt('100.127.255.255') },\n // AWS metadata endpoint\n { start: ip4ToInt('169.254.169.254'), end: ip4ToInt('169.254.169.254') },\n // 0.0.0.0/8\n { start: ip4ToInt('0.0.0.0'), end: ip4ToInt('0.255.255.255') },\n];\n\nconst BLOCKED_HOSTNAMES = new Set([\n 'localhost',\n 'metadata.google.internal',\n 'metadata.google.com',\n 'kubernetes.default.svc',\n]);\n\n// Headers that must never be set via user-controlled parameter mappings or custom_header auth\nexport const BLOCKED_HEADERS = new Set([\n 'host',\n 'transfer-encoding',\n 'content-length',\n 'connection',\n 'upgrade',\n 'x-forwarded-for',\n 'x-forwarded-host',\n 'x-forwarded-proto',\n 'x-real-ip',\n 'via',\n 'te',\n 'trailer',\n 'x-toolrelay-verify',\n]);\n\nfunction ip4ToInt(ip: string): number {\n const parts = ip.split('.').map(Number);\n return ((parts[0]! << 24) | (parts[1]! << 16) | (parts[2]! << 8) | parts[3]!) >>> 0;\n}\n\nfunction isPrivateIPv4(ip: string): boolean {\n const num = ip4ToInt(ip);\n return PRIVATE_IP_RANGES.some((range) => num >= range.start && num <= range.end);\n}\n\nfunction isPrivateIPv6(ip: string): boolean {\n const normalized = ip.toLowerCase();\n // Loopback\n if (normalized === '::1' || normalized === '0:0:0:0:0:0:0:1') return true;\n // Unspecified\n if (normalized === '::' || normalized === '0:0:0:0:0:0:0:0') return true;\n // Unique local (fc00::/7)\n if (normalized.startsWith('fc') || normalized.startsWith('fd')) return true;\n // Link-local (fe80::/10)\n if (normalized.startsWith('fe80')) return true;\n // IPv4-mapped IPv6 (::ffff:x.x.x.x)\n const v4Mapped = normalized.match(/^::ffff:(\\d+\\.\\d+\\.\\d+\\.\\d+)$/);\n if (v4Mapped) return isPrivateIPv4(v4Mapped[1]!);\n return false;\n}\n\nfunction isPrivateIP(ip: string): boolean {\n if (isIP(ip) === 4) return isPrivateIPv4(ip);\n if (isIP(ip) === 6) return isPrivateIPv6(ip);\n return false;\n}\n\nexport interface UrlValidationResult {\n valid: boolean;\n error?: string;\n}\n\n/**\n * Validates that a URL does not point to a private/internal network address.\n * Synchronous check — validates IP literals and hostname patterns only.\n * Use `validateUrlNotPrivateAsync` for full DNS resolution checks.\n */\nexport function validateUrlNotPrivate(urlString: string): UrlValidationResult {\n let parsed: URL;\n try {\n parsed = new URL(urlString);\n } catch {\n return { valid: false, error: 'Invalid URL' };\n }\n\n // Only allow http and https schemes\n if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {\n return { valid: false, error: 'Only http and https URLs are allowed' };\n }\n\n const hostname = parsed.hostname;\n\n // Block known internal hostnames\n if (BLOCKED_HOSTNAMES.has(hostname.toLowerCase())) {\n return { valid: false, error: 'URLs pointing to internal services are not allowed' };\n }\n\n // Block hostnames ending with .internal or .local\n const lowerHost = hostname.toLowerCase();\n if (lowerHost.endsWith('.internal') || lowerHost.endsWith('.local') || lowerHost.endsWith('.localhost')) {\n return { valid: false, error: 'URLs pointing to internal services are not allowed' };\n }\n\n // If the hostname is an IP address, check it directly\n if (isIP(hostname)) {\n if (isPrivateIP(hostname)) {\n return { valid: false, error: 'URLs pointing to private IP addresses are not allowed' };\n }\n }\n\n return { valid: true };\n}\n\n/**\n * Async SSRF validation: resolves DNS and checks all resolved addresses\n * against the private IP denylist. Prevents DNS rebinding attacks.\n */\nexport async function validateUrlNotPrivateAsync(urlString: string): Promise<UrlValidationResult> {\n // Run synchronous checks first\n const syncResult = validateUrlNotPrivate(urlString);\n if (!syncResult.valid) return syncResult;\n\n const parsed = new URL(urlString);\n const hostname = parsed.hostname;\n\n // If it's already an IP literal, the sync check already covered it\n if (isIP(hostname)) return { valid: true };\n\n // Resolve DNS and check all resulting IPs\n try {\n const addresses = await dns.resolve4(hostname).catch(() => [] as string[]);\n const addresses6 = await dns.resolve6(hostname).catch(() => [] as string[]);\n const allAddresses = [...addresses, ...addresses6];\n\n if (allAddresses.length === 0) {\n return { valid: false, error: 'Could not resolve hostname' };\n }\n\n for (const addr of allAddresses) {\n if (isPrivateIP(addr)) {\n return {\n valid: false,\n error: `Hostname resolves to private IP address (${addr})`,\n };\n }\n }\n } catch {\n return { valid: false, error: 'DNS resolution failed' };\n }\n\n return { valid: true };\n}\n","// ─── Enums ───────────────────────────────────────────────────────────────────\n\nexport enum Tier {\n free = 'free',\n growth = 'growth',\n business = 'business',\n enterprise = 'enterprise',\n}\n\nexport enum AuthType {\n none = 'none',\n static_token = 'static_token',\n oauth2 = 'oauth2',\n api_key_relay = 'api_key_relay',\n custom_header = 'custom_header',\n}\n\nexport enum HttpMethod {\n GET = 'GET',\n POST = 'POST',\n PUT = 'PUT',\n PATCH = 'PATCH',\n DELETE = 'DELETE',\n}\n\nexport enum PermissionLevel {\n read = 'read',\n write = 'write',\n admin = 'admin',\n}\n\nexport enum ApiKeyTier {\n free = 'free',\n basic = 'basic',\n pro = 'pro',\n enterprise = 'enterprise',\n}\n\nexport enum ApiKeyStatus {\n active = 'active',\n pending_approval = 'pending_approval',\n pending_oauth = 'pending_oauth',\n revoked = 'revoked',\n}\n\n// ─── Interfaces ──────────────────────────────────────────────────────────────\n\nexport interface User {\n id: string;\n email: string;\n password_hash: string;\n name: string;\n stripe_customer_id?: string;\n stripe_subscription_id?: string;\n tier: Tier;\n downgraded_at?: Date;\n previous_tier?: string;\n grace_period_ends_at?: Date;\n created_at: Date;\n updated_at: Date;\n}\n\nexport interface App {\n id: string;\n user_id: string;\n name: string;\n slug: string;\n description: string;\n base_url: string;\n auth_type: AuthType;\n auth_config: Record<string, unknown>;\n logo_url?: string;\n is_published: boolean;\n auto_approve_keys: boolean;\n default_key_tier: ApiKeyTier;\n dormant_key_days?: number;\n oauth_authorize_url?: string;\n oauth_token_url?: string;\n oauth_client_id?: string;\n oauth_client_secret_enc?: string;\n oauth_scopes?: string;\n oauth_redirect_uri?: string;\n enable_cli_access: boolean;\n global_headers?: Record<string, string>;\n privacy_policy_url?: string;\n support_email?: string;\n documentation_url?: string;\n // x402 payment gateway\n x402_enabled: boolean;\n x402_wallet_address?: string;\n x402_default_price?: number;\n x402_asset?: string;\n x402_network?: string;\n x402_bazaar_listed: boolean;\n created_at: Date;\n updated_at: Date;\n}\n\nexport interface ParameterMapping {\n name: string;\n type: 'string' | 'number' | 'boolean' | 'object' | 'array';\n required: boolean;\n description?: string;\n target: 'path' | 'query' | 'body' | 'header';\n backend_key?: string;\n default_value?: unknown;\n}\n\nexport interface McpToolAnnotations {\n readOnlyHint?: boolean;\n destructiveHint?: boolean;\n idempotentHint?: boolean;\n openWorldHint?: boolean;\n}\n\nexport interface Tool {\n id: string;\n app_id: string;\n name: string;\n slug: string;\n description: string;\n http_method: HttpMethod;\n endpoint_path: string;\n request_schema: Record<string, unknown>;\n response_schema?: Record<string, unknown>;\n parameter_mapping: ParameterMapping[];\n headers_template?: Record<string, string>;\n permission_level: PermissionLevel;\n rate_limit_override?: number;\n cache_ttl_seconds: number;\n sort_order: number;\n is_active: boolean;\n mcp_annotations?: McpToolAnnotations;\n // x402 payment gateway\n x402_enabled?: boolean;\n x402_price_override?: number;\n created_at: Date;\n updated_at: Date;\n}\n\nexport interface ApiKey {\n id: string;\n app_id: string;\n key_hash: string;\n key_prefix: string;\n consumer_name: string;\n consumer_email: string;\n tier: ApiKeyTier;\n scopes: string[];\n status: ApiKeyStatus;\n request_message?: string;\n approved_at?: Date;\n expires_at?: Date;\n last_used_at?: Date;\n total_calls: number;\n created_at: Date;\n}\n\nexport interface ConsumerBan {\n id: string;\n app_id: string;\n consumer_email: string;\n reason?: string;\n banned_by: string;\n created_at: Date;\n}\n\nexport interface ApiCall {\n id: string;\n api_key_id: string;\n tool_id: string;\n request_timestamp: Date;\n response_status: number;\n response_time_ms: number;\n request_body_size: number;\n response_body_size: number;\n error_message?: string;\n}\n\nexport interface ConsumerToken {\n id: string;\n api_key_id: string;\n access_token_enc: string;\n refresh_token_enc: string;\n token_expires_at: Date;\n scopes: string[];\n created_at: Date;\n}\n\n// ─── Config file types (toolrelay.json) ──────────────────────────────────────\n// Shared between CLI, web export/import, and deploy — the canonical shape of\n// a .toolrelay.json config file. Uses snake_case to match the API layer.\n\nexport interface ConfigApp {\n name: string;\n description?: string;\n slug?: string;\n base_url: string;\n auth_type: AuthType;\n auth_config?: Record<string, unknown>;\n global_headers?: Record<string, string>;\n}\n\nexport interface ConfigTool {\n name: string;\n description?: string;\n http_method: HttpMethod;\n endpoint_path: string;\n parameter_mapping: ParameterMapping[];\n permission_level?: PermissionLevel;\n headers_template?: Record<string, string>;\n request_schema?: Record<string, unknown>;\n response_schema?: Record<string, unknown>;\n mcp_annotations?: McpToolAnnotations;\n}\n\nexport interface ToolRelayConfig {\n app: ConfigApp;\n tools: ConfigTool[];\n}\n\n// ─── Utility types ───────────────────────────────────────────────────────────\n\nexport interface TierLimits {\n maxApps: number;\n maxToolsPerApp: number;\n maxKeysPerApp: number;\n monthlyApiCalls: number;\n monthlyX402Calls: number;\n}\n\nexport interface RateLimits {\n reqPerMin: number;\n reqPerDay: number;\n burst: number;\n}\n\nexport interface BackendRequest {\n url: string;\n method: HttpMethod;\n headers: Record<string, string>;\n body?: unknown;\n}\n\n/** Returned by GET /api/billing/tier-status to power the dashboard downgrade banner. */\nexport interface TierStatus {\n tier: Tier;\n appCount: number;\n appLimit: number;\n excessAppCount: number;\n /** IDs of apps that exceed the tier limit (sorted oldest-first, excess = last N). */\n readOnlyAppIds: string[];\n /** True when user is still within the grace period after a downgrade. */\n inGracePeriod: boolean;\n gracePeriodEndsAt: string | null;\n downgraded: boolean;\n}\n","import { Tier, ApiKeyTier } from './types.js';\nimport type { TierLimits, RateLimits } from './types.js';\n\n// ─── Tier limits (publisher account tiers) ───────────────────────────────────\n\nexport const TIER_LIMITS: Record<Tier, TierLimits> = {\n [Tier.free]: {\n maxApps: 1,\n maxToolsPerApp: 5,\n maxKeysPerApp: 25,\n monthlyApiCalls: 10_000,\n monthlyX402Calls: 1_000,\n },\n [Tier.growth]: {\n maxApps: 5,\n maxToolsPerApp: 50,\n maxKeysPerApp: 500,\n monthlyApiCalls: 500_000,\n monthlyX402Calls: 500_000,\n },\n [Tier.business]: {\n maxApps: Infinity,\n maxToolsPerApp: Infinity,\n maxKeysPerApp: 5_000,\n monthlyApiCalls: 5_000_000,\n monthlyX402Calls: 5_000_000,\n },\n [Tier.enterprise]: {\n maxApps: Infinity,\n maxToolsPerApp: Infinity,\n maxKeysPerApp: Infinity,\n monthlyApiCalls: Infinity,\n monthlyX402Calls: Infinity,\n },\n};\n\n// ─── Rate limits (consumer API key tiers) ────────────────────────────────────\n\nexport const RATE_LIMITS: Record<ApiKeyTier, RateLimits> = {\n [ApiKeyTier.free]: {\n reqPerMin: 10,\n reqPerDay: 1_000,\n burst: 20,\n },\n [ApiKeyTier.basic]: {\n reqPerMin: 60,\n reqPerDay: 50_000,\n burst: 100,\n },\n [ApiKeyTier.pro]: {\n reqPerMin: 300,\n reqPerDay: 500_000,\n burst: 500,\n },\n [ApiKeyTier.enterprise]: {\n reqPerMin: 1_000,\n reqPerDay: Infinity,\n burst: 2_000,\n },\n};\n\n// ─── x402 rate limits (by payer address, not API key tier) ──────────────────\n\nexport const X402_RATE_LIMITS: RateLimits = {\n reqPerMin: 100,\n reqPerDay: Infinity,\n burst: 200,\n};\n\n/**\n * Minimum x402 price per call in USDC micro-units (1 USDC = 1,000,000).\n *\n * After the Coinbase facilitator free tier (1,000 settlements/month), each\n * settlement costs $0.001 in facilitator fees. This floor of 2,000 micro-units\n * ($0.002) ensures positive margin per request.\n */\nexport const X402_MIN_PRICE = 2_000;\n\n// ─── Key constants ───────────────────────────────────────────────────────────\n\n/** Prefix prepended to every generated API key */\nexport const KEY_PREFIX = 'tr_live_';\n\n/** Prefix prepended to every generated deploy token */\nexport const DEPLOY_TOKEN_PREFIX = 'tr_deploy_';\n\n/** How long (in seconds) to cache a resolved API key in Redis */\nexport const KEY_CACHE_TTL = 60;\n\n/** How long (in seconds) to cache app metadata in Redis */\nexport const APP_CACHE_TTL = 300;\n\n/** JWT token expiry duration */\nexport const JWT_EXPIRY = '24h';\n\n/** Number of days a user has to remediate after a tier downgrade */\nexport const DOWNGRADE_GRACE_PERIOD_DAYS = 14;\n\n/** Tier ranking for comparison (higher = more features) */\nexport const TIER_RANK: Record<string, number> = {\n free: 0,\n growth: 1,\n business: 2,\n enterprise: 3,\n};\n","import { z } from 'zod';\nimport { AuthType, HttpMethod, PermissionLevel, ApiKeyTier } from './types.js';\nimport { X402_MIN_PRICE } from './constants.js';\n\n// ─── Auth schemas ────────────────────────────────────────────────────────────\n\nexport const registerSchema = z.object({\n email: z.string().email(),\n password: z.string().min(8),\n name: z.string().min(1),\n});\n\nexport type RegisterInput = z.infer<typeof registerSchema>;\n\nexport const loginSchema = z.object({\n email: z.string().email(),\n password: z.string().min(1),\n});\n\nexport type LoginInput = z.infer<typeof loginSchema>;\n\n// ─── Shared validation constants ─────────────────────────────────────────────\n\nexport const SLUG_REGEX = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;\nexport const SLUG_MESSAGE = 'Lowercase letters, numbers, and hyphens only (no leading/trailing hyphens)';\n\nexport const TOOL_NAME_REGEX = /^[a-z][a-z0-9_]*$/;\nexport const TOOL_NAME_MESSAGE = 'Tool name must be snake_case (lowercase letters, numbers, underscores)';\n\n// ─── App schemas ─────────────────────────────────────────────────────────────\n\nexport const createAppSchema = z.object({\n name: z.string().min(1).max(100),\n slug: z.string().min(2).max(100).regex(SLUG_REGEX, SLUG_MESSAGE).optional(),\n description: z.string().max(500),\n base_url: z.string().url(),\n auth_type: z.nativeEnum(AuthType),\n auth_config: z.record(z.unknown()).optional(),\n logo_url: z.string().url().nullable().optional(),\n auto_approve_keys: z.boolean().optional(),\n default_key_tier: z.nativeEnum(ApiKeyTier).optional(),\n dormant_key_days: z.number().int().min(7).max(365).nullable().optional(),\n global_headers: z.record(z.string(), z.string()).optional(),\n privacy_policy_url: z.string().url().nullable().optional(),\n support_email: z.string().email().nullable().optional(),\n documentation_url: z.string().url().nullable().optional(),\n // OAuth 2.0 fields (required when auth_type is 'oauth2')\n oauth_authorize_url: z.string().url().optional(),\n oauth_token_url: z.string().url().optional(),\n oauth_client_id: z.string().min(1).nullable().optional(),\n oauth_client_secret: z.string().min(1).nullable().optional(),\n oauth_scopes: z.string().nullable().optional(),\n enable_cli_access: z.boolean().optional(),\n // x402 payment gateway\n x402_enabled: z.boolean().optional(),\n x402_wallet_address: z.string()\n .regex(/^0x[0-9a-fA-F]{40}$/, 'Must be a valid Ethereum address (0x + 40 hex chars)')\n .nullable().optional(),\n x402_default_price: z.number().int().min(X402_MIN_PRICE, `Minimum price is ${X402_MIN_PRICE} micro-units ($${(X402_MIN_PRICE / 1_000_000).toFixed(4)} USDC) to cover settlement costs`).nullable().optional(),\n x402_asset: z.string().max(255).optional(),\n x402_network: z.string().max(50).optional(),\n x402_bazaar_listed: z.boolean().optional(),\n});\n\nexport type CreateAppInput = z.infer<typeof createAppSchema>;\n\nexport const updateAppSchema = createAppSchema.partial();\n\nexport type UpdateAppInput = z.infer<typeof updateAppSchema>;\n\n// ─── Connectivity test schema ────────────────────────────────────────────────\n\nexport const testConnectivitySchema = z.object({\n base_url: z.string().url(),\n auth_type: z.nativeEnum(AuthType),\n auth_config: z.record(z.unknown()).optional(),\n oauth_token_url: z.string().url().optional(),\n oauth_authorize_url: z.string().url().optional(),\n});\n\nexport type TestConnectivityInput = z.infer<typeof testConnectivitySchema>;\n\n// ─── Parameter mapping schema ────────────────────────────────────────────────\n\nconst parameterMappingSchema = z.object({\n name: z.string().min(1),\n type: z.enum(['string', 'number', 'boolean', 'object', 'array']),\n required: z.boolean(),\n description: z.string().optional(),\n target: z.enum(['path', 'query', 'body', 'header']),\n backend_key: z.string().optional(),\n default_value: z.unknown().optional(),\n});\n\n// ─── MCP annotations schema ─────────────────────────────────────────────────\n\nexport const mcpAnnotationsSchema = z.object({\n readOnlyHint: z.boolean().optional(),\n destructiveHint: z.boolean().optional(),\n idempotentHint: z.boolean().optional(),\n openWorldHint: z.boolean().optional(),\n});\n\n// ─── Tool schemas ────────────────────────────────────────────────────────────\n\nexport const createToolSchema = z.object({\n name: z.string().min(1),\n description: z.string(),\n http_method: z.nativeEnum(HttpMethod),\n endpoint_path: z.string(),\n request_schema: z.record(z.unknown()).optional(),\n response_schema: z.record(z.unknown()).optional(),\n parameter_mapping: z.array(parameterMappingSchema).optional(),\n headers_template: z.record(z.string()).optional(),\n permission_level: z.nativeEnum(PermissionLevel),\n rate_limit_override: z.number().int().positive().nullable().optional(),\n cache_ttl_seconds: z.number().int().min(0).optional(),\n mcp_annotations: mcpAnnotationsSchema.optional(),\n // x402 payment gateway\n x402_enabled: z.boolean().nullable().optional(),\n x402_price_override: z.number().int().min(X402_MIN_PRICE, `Minimum price is ${X402_MIN_PRICE} micro-units ($${(X402_MIN_PRICE / 1_000_000).toFixed(4)} USDC) to cover settlement costs`).nullable().optional(),\n});\n\nexport type CreateToolInput = z.infer<typeof createToolSchema>;\n\nexport const updateToolSchema = createToolSchema.partial();\n\nexport type UpdateToolInput = z.infer<typeof updateToolSchema>;\n\n// ─── API key schemas ─────────────────────────────────────────────────────────\n\nexport const createKeySchema = z.object({\n consumer_name: z.string().min(1),\n consumer_email: z.string().email(),\n tier: z.nativeEnum(ApiKeyTier).optional(),\n scopes: z.array(z.string()).optional(),\n expires_at: z.string().datetime().optional(),\n});\n\nexport type CreateKeyInput = z.infer<typeof createKeySchema>;\n\nexport const requestKeySchema = z.object({\n name: z.string().min(1),\n email: z.string().email(),\n message: z.string().optional(),\n});\n\nexport type RequestKeyInput = z.infer<typeof requestKeySchema>;\n\n// ─── Consumer ban schemas ───────────────────────────────────────────────────\n\nexport const banConsumerSchema = z.object({\n consumer_email: z.string().email(),\n reason: z.string().max(500).optional(),\n});\n\nexport type BanConsumerInput = z.infer<typeof banConsumerSchema>;\n\n// ─── Consumer auth schemas ──────────────────────────────────────────────────\n\nexport const consumerAuthRequestSchema = z.object({\n consumer_email: z.string().email(),\n});\n\nexport type ConsumerAuthRequestInput = z.infer<typeof consumerAuthRequestSchema>;\n\n// ─── OpenAPI import schema ──────────────────────────────────────────────────\n\nexport const importOpenApiSchema = z.object({\n spec: z.union([z.string(), z.record(z.unknown())]),\n overwrite: z.boolean().optional(),\n});\n\nexport type ImportOpenApiInput = z.infer<typeof importOpenApiSchema>;\n\n// ─── Deploy token schemas ──────────────────────────────────────────────────\n\nexport const createDeployTokenSchema = z.object({\n name: z.string().min(1).max(100),\n expires_at: z.string().datetime().optional(),\n});\n\nexport type CreateDeployTokenInput = z.infer<typeof createDeployTokenSchema>;\n","import { AuthType } from './types.js';\nimport type { App, Tool, ParameterMapping, BackendRequest } from './types.js';\nimport { BLOCKED_HEADERS } from './url-validation.js';\n\n/**\n * Set a value on a nested object using dot-notation keys.\n *\n * Example: setNested(obj, 'address.city', 'NYC')\n * => obj.address.city === 'NYC'\n */\nfunction setNested(obj: Record<string, unknown>, path: string, value: unknown): void {\n const keys = path.split('.');\n let current: Record<string, unknown> = obj;\n\n for (let i = 0; i < keys.length - 1; i++) {\n const key = keys[i]!;\n if (!(key in current) || typeof current[key] !== 'object' || current[key] === null) {\n current[key] = {};\n }\n current = current[key] as Record<string, unknown>;\n }\n\n const lastKey = keys[keys.length - 1]!;\n current[lastKey] = value;\n}\n\n/**\n * Resolve the value for a given parameter from the input payload, falling back\n * to the mapping's default_value if the key is absent.\n */\nfunction resolveValue(mapping: ParameterMapping, input: Record<string, unknown>): unknown {\n if (mapping.name in input) {\n return input[mapping.name];\n }\n return mapping.default_value;\n}\n\n/**\n * Build a fully-formed backend HTTP request from a Tool definition, its parent\n * App, and the caller-supplied input payload.\n *\n * Optionally accepts a `consumerToken` -- when the app uses OAuth 2 and a\n * per-consumer token is available it will be used as the Bearer token instead\n * of any service-level credential.\n */\nexport function buildBackendRequest(\n tool: Tool,\n app: App,\n inputPayload: Record<string, unknown>,\n consumerToken?: string,\n): BackendRequest {\n // Start with the base URL and endpoint path\n let url = app.base_url.replace(/\\/+$/, '') + tool.endpoint_path;\n\n const headers: Record<string, string> = {};\n const queryParams: Array<[string, string]> = [];\n const body: Record<string, unknown> = {};\n let hasBodyParams = false;\n\n // ── Validate required parameters before processing ─────────────────────\n\n for (const mapping of tool.parameter_mapping) {\n if (mapping.required) {\n const value = resolveValue(mapping, inputPayload);\n if (value === undefined) {\n throw new Error(`Missing required parameter: ${mapping.name}`);\n }\n }\n }\n\n // ── Process parameter mappings ───────────────────────────────────────────\n\n for (const mapping of tool.parameter_mapping) {\n const value = resolveValue(mapping, inputPayload);\n\n // Skip undefined values (parameter was not provided and has no default)\n if (value === undefined) {\n continue;\n }\n\n // Determine the key to use on the backend side\n const backendKey = mapping.backend_key ?? mapping.name;\n\n switch (mapping.target) {\n case 'path':\n // Replace ALL {paramName} placeholders in the URL (not just the first)\n url = url.split(`{${backendKey}}`).join(encodeURIComponent(String(value)));\n break;\n\n case 'query':\n queryParams.push([backendKey, String(value)]);\n break;\n\n case 'body':\n // Support dot-notation for nested body keys (e.g. 'address.city')\n setNested(body, backendKey, value);\n hasBodyParams = true;\n break;\n\n case 'header':\n // Block dangerous headers from user-controlled parameter mappings\n if (BLOCKED_HEADERS.has(backendKey.toLowerCase())) {\n throw new Error(`Parameter mapping cannot set restricted header: ${backendKey}`);\n }\n headers[backendKey] = String(value);\n break;\n }\n }\n\n // ── Append query string ──────────────────────────────────────────────────\n\n if (queryParams.length > 0) {\n const qs = queryParams\n .map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`)\n .join('&');\n url += (url.includes('?') ? '&' : '?') + qs;\n }\n\n // ── Merge static headers template ────────────────────────────────────────\n\n if (tool.headers_template) {\n for (const [key, value] of Object.entries(tool.headers_template)) {\n // Do not overwrite headers that were set by parameter mappings\n if (!(key in headers)) {\n headers[key] = value;\n }\n }\n }\n\n // ── Merge app-level global headers (lowest priority) ─────────────────────\n\n if (app.global_headers) {\n for (const [key, value] of Object.entries(app.global_headers)) {\n if (BLOCKED_HEADERS.has(key.toLowerCase())) continue;\n if (!(key in headers)) {\n headers[key] = value;\n }\n }\n }\n\n // ── Inject authentication ────────────────────────────────────────────────\n\n const authConfig = app.auth_config as Record<string, unknown>;\n\n switch (app.auth_type) {\n case AuthType.static_token: {\n const token = authConfig['token'] as string | undefined;\n if (token) {\n headers['Authorization'] = `Bearer ${token}`;\n }\n break;\n }\n\n case AuthType.oauth2: {\n // Prefer the per-consumer token when available; otherwise fall back to\n // the service account token stored in auth_config.\n const token =\n consumerToken ?? (authConfig['access_token'] as string | undefined);\n if (token) {\n headers['Authorization'] = `Bearer ${token}`;\n }\n break;\n }\n\n case AuthType.api_key_relay: {\n // auth_config is expected to contain { header_name, api_key }\n const headerName = authConfig['header_name'] as string | undefined;\n const apiKey = authConfig['api_key'] as string | undefined;\n if (headerName && apiKey) {\n headers[headerName] = apiKey;\n }\n break;\n }\n\n case AuthType.custom_header: {\n // auth_config is a flat map of header name -> value pairs to inject\n const customHeaders = authConfig['headers'] as Record<string, string> | undefined;\n if (customHeaders) {\n for (const [key, value] of Object.entries(customHeaders)) {\n // Block dangerous headers from custom_header auth\n if (BLOCKED_HEADERS.has(key.toLowerCase())) continue;\n headers[key] = value;\n }\n }\n break;\n }\n\n case AuthType.none:\n default:\n // Inject verification secret for x402 apps (auth_type='none' with verify_header)\n if (authConfig['verify_header']) {\n headers['X-ToolRelay-Verify'] = authConfig['verify_header'] as string;\n }\n break;\n }\n\n // ── Build final request ──────────────────────────────────────────────────\n\n const request: BackendRequest = {\n url,\n method: tool.http_method,\n headers,\n };\n\n if (hasBodyParams) {\n headers['Content-Type'] = headers['Content-Type'] ?? 'application/json';\n request.body = body;\n }\n\n return request;\n}\n"]}
@@ -0,0 +1,53 @@
1
+ #!/usr/bin/env node
2
+ import { existsSync, readFileSync, mkdirSync, writeFileSync, unlinkSync } from 'fs';
3
+ import { join } from 'path';
4
+ import { homedir } from 'os';
5
+
6
+ var CONFIG_DIR = join(homedir(), ".toolrelay");
7
+ var CREDENTIALS_FILE = join(CONFIG_DIR, "credentials.json");
8
+ var ENV_DEPLOY_TOKEN = "TOOLRELAY_DEPLOY_TOKEN";
9
+ var ENV_API_URL = "TOOLRELAY_API_URL";
10
+ var DEFAULT_API_URL = "https://api.toolrelay.io";
11
+ function resolveAuth() {
12
+ const apiUrl = process.env[ENV_API_URL] || DEFAULT_API_URL;
13
+ const envToken = process.env[ENV_DEPLOY_TOKEN];
14
+ if (envToken) {
15
+ return { token: envToken, api_url: apiUrl, source: "env" };
16
+ }
17
+ const creds = loadCredentials();
18
+ if (creds) {
19
+ return { token: creds.token, api_url: creds.api_url, source: "credentials" };
20
+ }
21
+ return null;
22
+ }
23
+ function loadCredentials() {
24
+ try {
25
+ if (!existsSync(CREDENTIALS_FILE)) return null;
26
+ const raw = readFileSync(CREDENTIALS_FILE, "utf-8");
27
+ return JSON.parse(raw);
28
+ } catch {
29
+ return null;
30
+ }
31
+ }
32
+ function saveCredentials(creds) {
33
+ mkdirSync(CONFIG_DIR, { recursive: true });
34
+ writeFileSync(CREDENTIALS_FILE, JSON.stringify(creds, null, 2) + "\n", { mode: 384 });
35
+ }
36
+ function clearCredentials() {
37
+ try {
38
+ if (existsSync(CREDENTIALS_FILE)) {
39
+ unlinkSync(CREDENTIALS_FILE);
40
+ return true;
41
+ }
42
+ return false;
43
+ } catch {
44
+ return false;
45
+ }
46
+ }
47
+ function getApiUrl() {
48
+ return process.env[ENV_API_URL] || DEFAULT_API_URL;
49
+ }
50
+
51
+ export { clearCredentials, getApiUrl, loadCredentials, resolveAuth, saveCredentials };
52
+ //# sourceMappingURL=chunk-CTTPIXB3.js.map
53
+ //# sourceMappingURL=chunk-CTTPIXB3.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/credentials.ts"],"names":[],"mappings":";;;;;AAMA,IAAM,UAAA,GAAa,IAAA,CAAK,OAAA,EAAQ,EAAG,YAAY,CAAA;AAC/C,IAAM,gBAAA,GAAmB,IAAA,CAAK,UAAA,EAAY,kBAAkB,CAAA;AAE5D,IAAM,gBAAA,GAAmB,wBAAA;AACzB,IAAM,WAAA,GAAc,mBAAA;AAEpB,IAAM,eAAA,GAAkB,0BAAA;AAmBjB,SAAS,WAAA,GAAmC;AACjD,EAAA,MAAM,MAAA,GAAS,OAAA,CAAQ,GAAA,CAAI,WAAW,CAAA,IAAK,eAAA;AAG3C,EAAA,MAAM,QAAA,GAAW,OAAA,CAAQ,GAAA,CAAI,gBAAgB,CAAA;AAC7C,EAAA,IAAI,QAAA,EAAU;AACZ,IAAA,OAAO,EAAE,KAAA,EAAO,QAAA,EAAU,OAAA,EAAS,MAAA,EAAQ,QAAQ,KAAA,EAAM;AAAA,EAC3D;AAGA,EAAA,MAAM,QAAQ,eAAA,EAAgB;AAC9B,EAAA,IAAI,KAAA,EAAO;AACT,IAAA,OAAO,EAAE,OAAO,KAAA,CAAM,KAAA,EAAO,SAAS,KAAA,CAAM,OAAA,EAAS,QAAQ,aAAA,EAAc;AAAA,EAC7E;AAEA,EAAA,OAAO,IAAA;AACT;AAIO,SAAS,eAAA,GAA4C;AAC1D,EAAA,IAAI;AACF,IAAA,IAAI,CAAC,UAAA,CAAW,gBAAgB,CAAA,EAAG,OAAO,IAAA;AAC1C,IAAA,MAAM,GAAA,GAAM,YAAA,CAAa,gBAAA,EAAkB,OAAO,CAAA;AAClD,IAAA,OAAO,IAAA,CAAK,MAAM,GAAG,CAAA;AAAA,EACvB,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,IAAA;AAAA,EACT;AACF;AAEO,SAAS,gBAAgB,KAAA,EAAgC;AAC9D,EAAA,SAAA,CAAU,UAAA,EAAY,EAAE,SAAA,EAAW,IAAA,EAAM,CAAA;AACzC,EAAA,aAAA,CAAc,gBAAA,EAAkB,IAAA,CAAK,SAAA,CAAU,KAAA,EAAO,IAAA,EAAM,CAAC,CAAA,GAAI,IAAA,EAAM,EAAE,IAAA,EAAM,GAAA,EAAO,CAAA;AACxF;AAEO,SAAS,gBAAA,GAA4B;AAC1C,EAAA,IAAI;AACF,IAAA,IAAI,UAAA,CAAW,gBAAgB,CAAA,EAAG;AAChC,MAAA,UAAA,CAAW,gBAAgB,CAAA;AAC3B,MAAA,OAAO,IAAA;AAAA,IACT;AACA,IAAA,OAAO,KAAA;AAAA,EACT,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,KAAA;AAAA,EACT;AACF;AAEO,SAAS,SAAA,GAAoB;AAClC,EAAA,OAAO,OAAA,CAAQ,GAAA,CAAI,WAAW,CAAA,IAAK,eAAA;AACrC","file":"chunk-CTTPIXB3.js","sourcesContent":["import { readFileSync, writeFileSync, mkdirSync, unlinkSync, existsSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { homedir } from 'node:os';\n\n// ─── Constants ──────────────────────────────────────────────────────────────\n\nconst CONFIG_DIR = join(homedir(), '.toolrelay');\nconst CREDENTIALS_FILE = join(CONFIG_DIR, 'credentials.json');\n\nconst ENV_DEPLOY_TOKEN = 'TOOLRELAY_DEPLOY_TOKEN';\nconst ENV_API_URL = 'TOOLRELAY_API_URL';\n\nconst DEFAULT_API_URL = 'https://api.toolrelay.io';\n\n// ─── Types ──────────────────────────────────────────────────────────────────\n\nexport interface StoredCredentials {\n token: string;\n email?: string;\n api_url: string;\n created_at: string;\n}\n\nexport interface ResolvedAuth {\n token: string;\n api_url: string;\n source: 'env' | 'credentials';\n}\n\n// ─── Resolve auth (env var takes priority) ──────────────────────────────────\n\nexport function resolveAuth(): ResolvedAuth | null {\n const apiUrl = process.env[ENV_API_URL] || DEFAULT_API_URL;\n\n // 1. Check env var first (CI/CD)\n const envToken = process.env[ENV_DEPLOY_TOKEN];\n if (envToken) {\n return { token: envToken, api_url: apiUrl, source: 'env' };\n }\n\n // 2. Check credentials file (interactive login)\n const creds = loadCredentials();\n if (creds) {\n return { token: creds.token, api_url: creds.api_url, source: 'credentials' };\n }\n\n return null;\n}\n\n// ─── Credentials file management ────────────────────────────────────────────\n\nexport function loadCredentials(): StoredCredentials | null {\n try {\n if (!existsSync(CREDENTIALS_FILE)) return null;\n const raw = readFileSync(CREDENTIALS_FILE, 'utf-8');\n return JSON.parse(raw) as StoredCredentials;\n } catch {\n return null;\n }\n}\n\nexport function saveCredentials(creds: StoredCredentials): void {\n mkdirSync(CONFIG_DIR, { recursive: true });\n writeFileSync(CREDENTIALS_FILE, JSON.stringify(creds, null, 2) + '\\n', { mode: 0o600 });\n}\n\nexport function clearCredentials(): boolean {\n try {\n if (existsSync(CREDENTIALS_FILE)) {\n unlinkSync(CREDENTIALS_FILE);\n return true;\n }\n return false;\n } catch {\n return false;\n }\n}\n\nexport function getApiUrl(): string {\n return process.env[ENV_API_URL] || DEFAULT_API_URL;\n}\n"]}
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env node
2
+ export { clearCredentials, getApiUrl, loadCredentials, resolveAuth, saveCredentials } from './chunk-CTTPIXB3.js';
3
+ //# sourceMappingURL=credentials-KWHZKJ5O.js.map
4
+ //# sourceMappingURL=credentials-KWHZKJ5O.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"credentials-KWHZKJ5O.js"}