@agent-native/core 0.46.0 → 0.47.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent/production-agent.d.ts +28 -0
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +14 -7
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/cli/skills.d.ts +2 -2
- package/dist/cli/skills.d.ts.map +1 -1
- package/dist/cli/skills.js +33 -0
- package/dist/cli/skills.js.map +1 -1
- package/dist/coding-tools/run-code.d.ts +40 -0
- package/dist/coding-tools/run-code.d.ts.map +1 -0
- package/dist/coding-tools/run-code.js +511 -0
- package/dist/coding-tools/run-code.js.map +1 -0
- package/dist/extensions/fetch-tool.d.ts.map +1 -1
- package/dist/extensions/fetch-tool.js +62 -7
- package/dist/extensions/fetch-tool.js.map +1 -1
- package/dist/extensions/web-search-tool.d.ts +41 -0
- package/dist/extensions/web-search-tool.d.ts.map +1 -0
- package/dist/extensions/web-search-tool.js +200 -0
- package/dist/extensions/web-search-tool.js.map +1 -0
- package/dist/provider-api/custom-registry.d.ts +92 -0
- package/dist/provider-api/custom-registry.d.ts.map +1 -0
- package/dist/provider-api/custom-registry.js +289 -0
- package/dist/provider-api/custom-registry.js.map +1 -0
- package/dist/provider-api/index.d.ts +80 -44
- package/dist/provider-api/index.d.ts.map +1 -1
- package/dist/provider-api/index.js +569 -18
- package/dist/provider-api/index.js.map +1 -1
- package/dist/secrets/register-framework-secrets.d.ts.map +1 -1
- package/dist/secrets/register-framework-secrets.js +36 -3
- package/dist/secrets/register-framework-secrets.js.map +1 -1
- package/dist/server/agent-chat-plugin.d.ts +36 -0
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +119 -0
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/workspace-files/index.d.ts +4 -0
- package/dist/workspace-files/index.d.ts.map +1 -0
- package/dist/workspace-files/index.js +4 -0
- package/dist/workspace-files/index.js.map +1 -0
- package/dist/workspace-files/schema.d.ts +195 -0
- package/dist/workspace-files/schema.d.ts.map +1 -0
- package/dist/workspace-files/schema.js +48 -0
- package/dist/workspace-files/schema.js.map +1 -0
- package/dist/workspace-files/store.d.ts +89 -0
- package/dist/workspace-files/store.d.ts.map +1 -0
- package/dist/workspace-files/store.js +298 -0
- package/dist/workspace-files/store.js.map +1 -0
- package/dist/workspace-files/tool.d.ts +15 -0
- package/dist/workspace-files/tool.d.ts.map +1 -0
- package/dist/workspace-files/tool.js +226 -0
- package/dist/workspace-files/tool.js.map +1 -0
- package/package.json +2 -1
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Custom provider registry — runtime storage and resolution.
|
|
3
|
+
*
|
|
4
|
+
* Lets apps register arbitrary HTTP API providers at runtime without touching
|
|
5
|
+
* the static PROVIDER_CONFIGS list. Provider rows live in the
|
|
6
|
+
* `custom_api_providers` SQL table (created on first use). Credentials
|
|
7
|
+
* referenced in the provider row continue to live in the existing secrets /
|
|
8
|
+
* credentials store — this table stores only key NAMES, never values.
|
|
9
|
+
*
|
|
10
|
+
* Scoping mirrors the `app_secrets` table: each row is scoped to a user
|
|
11
|
+
* (by email) or an organisation (by orgId).
|
|
12
|
+
*
|
|
13
|
+
* Supported auth kinds for custom providers:
|
|
14
|
+
* - bearer → Authorization: Bearer <credential>
|
|
15
|
+
* - basic → Authorization: Basic base64(user:pass)
|
|
16
|
+
* - api-key-header → custom header: <credential>
|
|
17
|
+
*
|
|
18
|
+
* google-service-account and oauth-bearer are intentionally unsupported for
|
|
19
|
+
* custom providers because they require additional out-of-band setup that
|
|
20
|
+
* cannot be expressed in the simple header/key model.
|
|
21
|
+
*/
|
|
22
|
+
import { getDbExec, isPostgres } from "../db/client.js";
|
|
23
|
+
import { isBlockedExtensionUrlWithDns } from "../extensions/url-safety.js";
|
|
24
|
+
// ---------------------------------------------------------------------------
|
|
25
|
+
// Table bootstrap
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
const CREATE_SQL = `CREATE TABLE IF NOT EXISTS custom_api_providers (
|
|
28
|
+
id TEXT NOT NULL,
|
|
29
|
+
scope TEXT NOT NULL,
|
|
30
|
+
scope_id TEXT NOT NULL,
|
|
31
|
+
label TEXT NOT NULL,
|
|
32
|
+
base_url TEXT NOT NULL,
|
|
33
|
+
auth_json TEXT NOT NULL,
|
|
34
|
+
docs_urls_json TEXT NOT NULL,
|
|
35
|
+
allowed_host_suffixes_json TEXT NOT NULL,
|
|
36
|
+
default_headers_json TEXT NOT NULL,
|
|
37
|
+
notes TEXT NOT NULL DEFAULT '',
|
|
38
|
+
created_at INTEGER NOT NULL,
|
|
39
|
+
updated_at INTEGER NOT NULL,
|
|
40
|
+
PRIMARY KEY (scope, scope_id, id)
|
|
41
|
+
)`;
|
|
42
|
+
let _initPromise;
|
|
43
|
+
async function ensureTable() {
|
|
44
|
+
if (!_initPromise) {
|
|
45
|
+
_initPromise = (async () => {
|
|
46
|
+
const client = getDbExec();
|
|
47
|
+
const sql = isPostgres()
|
|
48
|
+
? CREATE_SQL.replace(/\bINTEGER\b/g, "BIGINT")
|
|
49
|
+
: CREATE_SQL;
|
|
50
|
+
await client.execute(sql);
|
|
51
|
+
})().catch((err) => {
|
|
52
|
+
_initPromise = undefined;
|
|
53
|
+
throw err;
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
return _initPromise;
|
|
57
|
+
}
|
|
58
|
+
// ---------------------------------------------------------------------------
|
|
59
|
+
// Validation
|
|
60
|
+
// ---------------------------------------------------------------------------
|
|
61
|
+
const PROVIDER_ID_RE = /^[a-z0-9][a-z0-9-]{0,62}[a-z0-9]$|^[a-z0-9]$/;
|
|
62
|
+
const HEADER_NAME_RE = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;
|
|
63
|
+
/**
|
|
64
|
+
* Validate and normalise a custom provider base URL. Throws when the URL is
|
|
65
|
+
* invalid, uses a non-http(s) scheme, or resolves to a private/internal host.
|
|
66
|
+
*/
|
|
67
|
+
export async function validateCustomBaseUrl(rawUrl) {
|
|
68
|
+
let url;
|
|
69
|
+
try {
|
|
70
|
+
url = new URL(rawUrl);
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
throw new Error(`Invalid base URL: ${rawUrl}`);
|
|
74
|
+
}
|
|
75
|
+
if (url.protocol !== "https:" && url.protocol !== "http:") {
|
|
76
|
+
throw new Error(`Custom provider base URL must use https: or http: (got ${url.protocol})`);
|
|
77
|
+
}
|
|
78
|
+
if (await isBlockedExtensionUrlWithDns(url.href)) {
|
|
79
|
+
throw new Error(`Custom provider base URL resolves to a private/internal address — SSRF not allowed.`);
|
|
80
|
+
}
|
|
81
|
+
return url;
|
|
82
|
+
}
|
|
83
|
+
function validateProviderId(id) {
|
|
84
|
+
if (!PROVIDER_ID_RE.test(id)) {
|
|
85
|
+
throw new Error(`Custom provider id must be 1–64 lowercase letters, digits, or hyphens (got "${id}").`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
function validateAuth(auth) {
|
|
89
|
+
if (auth.type === "bearer") {
|
|
90
|
+
if (!auth.credentialKey)
|
|
91
|
+
throw new Error("bearer auth requires credentialKey");
|
|
92
|
+
}
|
|
93
|
+
else if (auth.type === "basic") {
|
|
94
|
+
if (!auth.usernameKey || !auth.passwordKey)
|
|
95
|
+
throw new Error("basic auth requires usernameKey and passwordKey");
|
|
96
|
+
}
|
|
97
|
+
else if (auth.type === "api-key-header") {
|
|
98
|
+
if (!auth.credentialKey || !auth.headerName)
|
|
99
|
+
throw new Error("api-key-header auth requires credentialKey and headerName");
|
|
100
|
+
if (!HEADER_NAME_RE.test(auth.headerName)) {
|
|
101
|
+
throw new Error(`Invalid header name for api-key-header auth: ${auth.headerName}`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Public suffixes that must never be used as an allowed host suffix — a
|
|
107
|
+
* suffix like "com" would attach the provider's credentials to requests for
|
|
108
|
+
* any host under that TLD, creating a credential-exfiltration vector. Not an
|
|
109
|
+
* exhaustive public-suffix list; combined with the structural checks below.
|
|
110
|
+
*/
|
|
111
|
+
const FORBIDDEN_HOST_SUFFIXES = new Set([
|
|
112
|
+
"com",
|
|
113
|
+
"net",
|
|
114
|
+
"org",
|
|
115
|
+
"io",
|
|
116
|
+
"co",
|
|
117
|
+
"dev",
|
|
118
|
+
"app",
|
|
119
|
+
"ai",
|
|
120
|
+
"cloud",
|
|
121
|
+
"info",
|
|
122
|
+
"biz",
|
|
123
|
+
"xyz",
|
|
124
|
+
"me",
|
|
125
|
+
"us",
|
|
126
|
+
"uk",
|
|
127
|
+
"eu",
|
|
128
|
+
"co.uk",
|
|
129
|
+
"org.uk",
|
|
130
|
+
"ac.uk",
|
|
131
|
+
"gov.uk",
|
|
132
|
+
"com.au",
|
|
133
|
+
"net.au",
|
|
134
|
+
"org.au",
|
|
135
|
+
"co.nz",
|
|
136
|
+
"co.jp",
|
|
137
|
+
"com.br",
|
|
138
|
+
"com.cn",
|
|
139
|
+
"co.in",
|
|
140
|
+
"com.mx",
|
|
141
|
+
"co.za",
|
|
142
|
+
"github.io",
|
|
143
|
+
"herokuapp.com",
|
|
144
|
+
"vercel.app",
|
|
145
|
+
"netlify.app",
|
|
146
|
+
"pages.dev",
|
|
147
|
+
"workers.dev",
|
|
148
|
+
"azurewebsites.net",
|
|
149
|
+
"cloudfront.net",
|
|
150
|
+
"amazonaws.com",
|
|
151
|
+
"ngrok.io",
|
|
152
|
+
"ngrok.app",
|
|
153
|
+
]);
|
|
154
|
+
const HOST_SUFFIX_RE = /^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)+$/;
|
|
155
|
+
/**
|
|
156
|
+
* Validate user-supplied allowed host suffixes. Each suffix must look like a
|
|
157
|
+
* real registrable domain (at least two labels) and must not be a bare TLD or
|
|
158
|
+
* shared-hosting public suffix where unrelated parties control subdomains.
|
|
159
|
+
*/
|
|
160
|
+
export function validateAllowedHostSuffixes(suffixes) {
|
|
161
|
+
const normalized = [];
|
|
162
|
+
for (const raw of suffixes) {
|
|
163
|
+
const suffix = String(raw).trim().toLowerCase().replace(/^\.+/, "");
|
|
164
|
+
if (!suffix)
|
|
165
|
+
continue;
|
|
166
|
+
if (!HOST_SUFFIX_RE.test(suffix)) {
|
|
167
|
+
throw new Error(`Invalid allowed host suffix "${raw}": must be a domain like "api.example.com" with at least two labels.`);
|
|
168
|
+
}
|
|
169
|
+
if (FORBIDDEN_HOST_SUFFIXES.has(suffix)) {
|
|
170
|
+
throw new Error(`Allowed host suffix "${raw}" is too broad: it would attach this provider's credentials to any host under a public suffix. Use the provider's own registrable domain (e.g. "example.com").`);
|
|
171
|
+
}
|
|
172
|
+
normalized.push(suffix);
|
|
173
|
+
}
|
|
174
|
+
return normalized;
|
|
175
|
+
}
|
|
176
|
+
// ---------------------------------------------------------------------------
|
|
177
|
+
// CRUD
|
|
178
|
+
// ---------------------------------------------------------------------------
|
|
179
|
+
/**
|
|
180
|
+
* Create or update a custom provider. Validates the base URL against SSRF
|
|
181
|
+
* rules at write time. Returns the provider id.
|
|
182
|
+
*/
|
|
183
|
+
export async function upsertCustomProvider(args) {
|
|
184
|
+
await ensureTable();
|
|
185
|
+
validateProviderId(args.id);
|
|
186
|
+
validateAuth(args.auth);
|
|
187
|
+
const url = await validateCustomBaseUrl(args.baseUrl);
|
|
188
|
+
const baseUrl = url.href.replace(/\/+$/, "");
|
|
189
|
+
const allowedHostSuffixes = validateAllowedHostSuffixes(args.allowedHostSuffixes ?? []);
|
|
190
|
+
const now = Date.now();
|
|
191
|
+
const client = getDbExec();
|
|
192
|
+
const { rows } = await client.execute({
|
|
193
|
+
sql: `SELECT created_at FROM custom_api_providers WHERE scope = ? AND scope_id = ? AND id = ?`,
|
|
194
|
+
args: [args.scope, args.scopeId, args.id],
|
|
195
|
+
});
|
|
196
|
+
if (rows.length > 0) {
|
|
197
|
+
await client.execute({
|
|
198
|
+
sql: `UPDATE custom_api_providers SET label=?, base_url=?, auth_json=?, docs_urls_json=?, allowed_host_suffixes_json=?, default_headers_json=?, notes=?, updated_at=? WHERE scope=? AND scope_id=? AND id=?`,
|
|
199
|
+
args: [
|
|
200
|
+
args.label,
|
|
201
|
+
baseUrl,
|
|
202
|
+
JSON.stringify(args.auth),
|
|
203
|
+
JSON.stringify(args.docsUrls ?? []),
|
|
204
|
+
JSON.stringify(allowedHostSuffixes),
|
|
205
|
+
JSON.stringify(args.defaultHeaders ?? {}),
|
|
206
|
+
args.notes ?? "",
|
|
207
|
+
now,
|
|
208
|
+
args.scope,
|
|
209
|
+
args.scopeId,
|
|
210
|
+
args.id,
|
|
211
|
+
],
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
else {
|
|
215
|
+
await client.execute({
|
|
216
|
+
sql: `INSERT INTO custom_api_providers (id, scope, scope_id, label, base_url, auth_json, docs_urls_json, allowed_host_suffixes_json, default_headers_json, notes, created_at, updated_at) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)`,
|
|
217
|
+
args: [
|
|
218
|
+
args.id,
|
|
219
|
+
args.scope,
|
|
220
|
+
args.scopeId,
|
|
221
|
+
args.label,
|
|
222
|
+
baseUrl,
|
|
223
|
+
JSON.stringify(args.auth),
|
|
224
|
+
JSON.stringify(args.docsUrls ?? []),
|
|
225
|
+
JSON.stringify(allowedHostSuffixes),
|
|
226
|
+
JSON.stringify(args.defaultHeaders ?? {}),
|
|
227
|
+
args.notes ?? "",
|
|
228
|
+
now,
|
|
229
|
+
now,
|
|
230
|
+
],
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
return args.id;
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Delete a custom provider. Returns true if a row was deleted.
|
|
237
|
+
*/
|
|
238
|
+
export async function deleteCustomProvider(scope, scopeId, id) {
|
|
239
|
+
await ensureTable();
|
|
240
|
+
const client = getDbExec();
|
|
241
|
+
const { rowsAffected } = await client.execute({
|
|
242
|
+
sql: `DELETE FROM custom_api_providers WHERE scope=? AND scope_id=? AND id=?`,
|
|
243
|
+
args: [scope, scopeId, id],
|
|
244
|
+
});
|
|
245
|
+
return rowsAffected > 0;
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* List all custom providers visible to a given (scope, scopeId) pair.
|
|
249
|
+
*/
|
|
250
|
+
export async function listCustomProviders(scope, scopeId) {
|
|
251
|
+
await ensureTable();
|
|
252
|
+
const client = getDbExec();
|
|
253
|
+
const { rows } = await client.execute({
|
|
254
|
+
sql: `SELECT id, scope, scope_id, label, base_url, auth_json, docs_urls_json, allowed_host_suffixes_json, default_headers_json, notes, created_at, updated_at FROM custom_api_providers WHERE scope=? AND scope_id=? ORDER BY updated_at DESC`,
|
|
255
|
+
args: [scope, scopeId],
|
|
256
|
+
});
|
|
257
|
+
return rows.map(rowToConfig);
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Look up one custom provider. Returns null when not found.
|
|
261
|
+
*/
|
|
262
|
+
export async function getCustomProvider(scope, scopeId, id) {
|
|
263
|
+
await ensureTable();
|
|
264
|
+
const client = getDbExec();
|
|
265
|
+
const { rows } = await client.execute({
|
|
266
|
+
sql: `SELECT id, scope, scope_id, label, base_url, auth_json, docs_urls_json, allowed_host_suffixes_json, default_headers_json, notes, created_at, updated_at FROM custom_api_providers WHERE scope=? AND scope_id=? AND id=? LIMIT 1`,
|
|
267
|
+
args: [scope, scopeId, id],
|
|
268
|
+
});
|
|
269
|
+
if (rows.length === 0)
|
|
270
|
+
return null;
|
|
271
|
+
return rowToConfig(rows[0]);
|
|
272
|
+
}
|
|
273
|
+
function rowToConfig(row) {
|
|
274
|
+
return {
|
|
275
|
+
id: row.id,
|
|
276
|
+
scope: row.scope,
|
|
277
|
+
scopeId: row.scope_id,
|
|
278
|
+
label: row.label,
|
|
279
|
+
baseUrl: row.base_url,
|
|
280
|
+
auth: JSON.parse(row.auth_json),
|
|
281
|
+
docsUrls: JSON.parse(row.docs_urls_json),
|
|
282
|
+
allowedHostSuffixes: JSON.parse(row.allowed_host_suffixes_json),
|
|
283
|
+
defaultHeaders: JSON.parse(row.default_headers_json),
|
|
284
|
+
notes: row.notes,
|
|
285
|
+
createdAt: Number(row.created_at),
|
|
286
|
+
updatedAt: Number(row.updated_at),
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
//# sourceMappingURL=custom-registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"custom-registry.js","sourceRoot":"","sources":["../../src/provider-api/custom-registry.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Custom provider registry — runtime storage and resolution.\n *\n * Lets apps register arbitrary HTTP API providers at runtime without touching\n * the static PROVIDER_CONFIGS list. Provider rows live in the\n * `custom_api_providers` SQL table (created on first use). Credentials\n * referenced in the provider row continue to live in the existing secrets /\n * credentials store — this table stores only key NAMES, never values.\n *\n * Scoping mirrors the `app_secrets` table: each row is scoped to a user\n * (by email) or an organisation (by orgId).\n *\n * Supported auth kinds for custom providers:\n * - bearer → Authorization: Bearer <credential>\n * - basic → Authorization: Basic base64(user:pass)\n * - api-key-header → custom header: <credential>\n *\n * google-service-account and oauth-bearer are intentionally unsupported for\n * custom providers because they require additional out-of-band setup that\n * cannot be expressed in the simple header/key model.\n */\n\nimport { randomUUID } from \"node:crypto\";\nimport { getDbExec, isPostgres } from \"../db/client.js\";\nimport { isBlockedExtensionUrlWithDns } from \"../extensions/url-safety.js\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport type CustomProviderScope = \"user\" | \"org\";\n\nexport type CustomProviderAuthKind =\n | { type: \"bearer\"; credentialKey: string }\n | { type: \"basic\"; usernameKey: string; passwordKey: string }\n | { type: \"api-key-header\"; credentialKey: string; headerName: string }\n | { type: \"none\" };\n\nexport interface CustomProviderConfig {\n id: string;\n scope: CustomProviderScope;\n scopeId: string;\n label: string;\n baseUrl: string;\n auth: CustomProviderAuthKind;\n docsUrls: string[];\n allowedHostSuffixes: string[];\n defaultHeaders: Record<string, string>;\n notes: string;\n createdAt: number;\n updatedAt: number;\n}\n\nexport interface UpsertCustomProviderArgs {\n scope: CustomProviderScope;\n scopeId: string;\n /** Slug used as the provider id (e.g. \"my-api\"). Must be lowercase, letters/digits/hyphens. */\n id: string;\n label: string;\n baseUrl: string;\n auth: CustomProviderAuthKind;\n docsUrls?: string[];\n allowedHostSuffixes?: string[];\n defaultHeaders?: Record<string, string>;\n notes?: string;\n}\n\n// ---------------------------------------------------------------------------\n// Table bootstrap\n// ---------------------------------------------------------------------------\n\nconst CREATE_SQL = `CREATE TABLE IF NOT EXISTS custom_api_providers (\n id TEXT NOT NULL,\n scope TEXT NOT NULL,\n scope_id TEXT NOT NULL,\n label TEXT NOT NULL,\n base_url TEXT NOT NULL,\n auth_json TEXT NOT NULL,\n docs_urls_json TEXT NOT NULL,\n allowed_host_suffixes_json TEXT NOT NULL,\n default_headers_json TEXT NOT NULL,\n notes TEXT NOT NULL DEFAULT '',\n created_at INTEGER NOT NULL,\n updated_at INTEGER NOT NULL,\n PRIMARY KEY (scope, scope_id, id)\n)`;\n\nlet _initPromise: Promise<void> | undefined;\n\nasync function ensureTable(): Promise<void> {\n if (!_initPromise) {\n _initPromise = (async () => {\n const client = getDbExec();\n const sql = isPostgres()\n ? CREATE_SQL.replace(/\\bINTEGER\\b/g, \"BIGINT\")\n : CREATE_SQL;\n await client.execute(sql);\n })().catch((err) => {\n _initPromise = undefined;\n throw err;\n });\n }\n return _initPromise;\n}\n\n// ---------------------------------------------------------------------------\n// Validation\n// ---------------------------------------------------------------------------\n\nconst PROVIDER_ID_RE = /^[a-z0-9][a-z0-9-]{0,62}[a-z0-9]$|^[a-z0-9]$/;\nconst HEADER_NAME_RE = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;\n\n/**\n * Validate and normalise a custom provider base URL. Throws when the URL is\n * invalid, uses a non-http(s) scheme, or resolves to a private/internal host.\n */\nexport async function validateCustomBaseUrl(rawUrl: string): Promise<URL> {\n let url: URL;\n try {\n url = new URL(rawUrl);\n } catch {\n throw new Error(`Invalid base URL: ${rawUrl}`);\n }\n if (url.protocol !== \"https:\" && url.protocol !== \"http:\") {\n throw new Error(\n `Custom provider base URL must use https: or http: (got ${url.protocol})`,\n );\n }\n if (await isBlockedExtensionUrlWithDns(url.href)) {\n throw new Error(\n `Custom provider base URL resolves to a private/internal address — SSRF not allowed.`,\n );\n }\n return url;\n}\n\nfunction validateProviderId(id: string): void {\n if (!PROVIDER_ID_RE.test(id)) {\n throw new Error(\n `Custom provider id must be 1–64 lowercase letters, digits, or hyphens (got \"${id}\").`,\n );\n }\n}\n\nfunction validateAuth(auth: CustomProviderAuthKind): void {\n if (auth.type === \"bearer\") {\n if (!auth.credentialKey)\n throw new Error(\"bearer auth requires credentialKey\");\n } else if (auth.type === \"basic\") {\n if (!auth.usernameKey || !auth.passwordKey)\n throw new Error(\"basic auth requires usernameKey and passwordKey\");\n } else if (auth.type === \"api-key-header\") {\n if (!auth.credentialKey || !auth.headerName)\n throw new Error(\n \"api-key-header auth requires credentialKey and headerName\",\n );\n if (!HEADER_NAME_RE.test(auth.headerName)) {\n throw new Error(\n `Invalid header name for api-key-header auth: ${auth.headerName}`,\n );\n }\n }\n}\n\n/**\n * Public suffixes that must never be used as an allowed host suffix — a\n * suffix like \"com\" would attach the provider's credentials to requests for\n * any host under that TLD, creating a credential-exfiltration vector. Not an\n * exhaustive public-suffix list; combined with the structural checks below.\n */\nconst FORBIDDEN_HOST_SUFFIXES = new Set([\n \"com\",\n \"net\",\n \"org\",\n \"io\",\n \"co\",\n \"dev\",\n \"app\",\n \"ai\",\n \"cloud\",\n \"info\",\n \"biz\",\n \"xyz\",\n \"me\",\n \"us\",\n \"uk\",\n \"eu\",\n \"co.uk\",\n \"org.uk\",\n \"ac.uk\",\n \"gov.uk\",\n \"com.au\",\n \"net.au\",\n \"org.au\",\n \"co.nz\",\n \"co.jp\",\n \"com.br\",\n \"com.cn\",\n \"co.in\",\n \"com.mx\",\n \"co.za\",\n \"github.io\",\n \"herokuapp.com\",\n \"vercel.app\",\n \"netlify.app\",\n \"pages.dev\",\n \"workers.dev\",\n \"azurewebsites.net\",\n \"cloudfront.net\",\n \"amazonaws.com\",\n \"ngrok.io\",\n \"ngrok.app\",\n]);\n\nconst HOST_SUFFIX_RE =\n /^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)+$/;\n\n/**\n * Validate user-supplied allowed host suffixes. Each suffix must look like a\n * real registrable domain (at least two labels) and must not be a bare TLD or\n * shared-hosting public suffix where unrelated parties control subdomains.\n */\nexport function validateAllowedHostSuffixes(suffixes: string[]): string[] {\n const normalized: string[] = [];\n for (const raw of suffixes) {\n const suffix = String(raw).trim().toLowerCase().replace(/^\\.+/, \"\");\n if (!suffix) continue;\n if (!HOST_SUFFIX_RE.test(suffix)) {\n throw new Error(\n `Invalid allowed host suffix \"${raw}\": must be a domain like \"api.example.com\" with at least two labels.`,\n );\n }\n if (FORBIDDEN_HOST_SUFFIXES.has(suffix)) {\n throw new Error(\n `Allowed host suffix \"${raw}\" is too broad: it would attach this provider's credentials to any host under a public suffix. Use the provider's own registrable domain (e.g. \"example.com\").`,\n );\n }\n normalized.push(suffix);\n }\n return normalized;\n}\n\n// ---------------------------------------------------------------------------\n// CRUD\n// ---------------------------------------------------------------------------\n\n/**\n * Create or update a custom provider. Validates the base URL against SSRF\n * rules at write time. Returns the provider id.\n */\nexport async function upsertCustomProvider(\n args: UpsertCustomProviderArgs,\n): Promise<string> {\n await ensureTable();\n validateProviderId(args.id);\n validateAuth(args.auth);\n const url = await validateCustomBaseUrl(args.baseUrl);\n const baseUrl = url.href.replace(/\\/+$/, \"\");\n const allowedHostSuffixes = validateAllowedHostSuffixes(\n args.allowedHostSuffixes ?? [],\n );\n\n const now = Date.now();\n const client = getDbExec();\n\n const { rows } = await client.execute({\n sql: `SELECT created_at FROM custom_api_providers WHERE scope = ? AND scope_id = ? AND id = ?`,\n args: [args.scope, args.scopeId, args.id],\n });\n\n if (rows.length > 0) {\n await client.execute({\n sql: `UPDATE custom_api_providers SET label=?, base_url=?, auth_json=?, docs_urls_json=?, allowed_host_suffixes_json=?, default_headers_json=?, notes=?, updated_at=? WHERE scope=? AND scope_id=? AND id=?`,\n args: [\n args.label,\n baseUrl,\n JSON.stringify(args.auth),\n JSON.stringify(args.docsUrls ?? []),\n JSON.stringify(allowedHostSuffixes),\n JSON.stringify(args.defaultHeaders ?? {}),\n args.notes ?? \"\",\n now,\n args.scope,\n args.scopeId,\n args.id,\n ],\n });\n } else {\n await client.execute({\n sql: `INSERT INTO custom_api_providers (id, scope, scope_id, label, base_url, auth_json, docs_urls_json, allowed_host_suffixes_json, default_headers_json, notes, created_at, updated_at) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)`,\n args: [\n args.id,\n args.scope,\n args.scopeId,\n args.label,\n baseUrl,\n JSON.stringify(args.auth),\n JSON.stringify(args.docsUrls ?? []),\n JSON.stringify(allowedHostSuffixes),\n JSON.stringify(args.defaultHeaders ?? {}),\n args.notes ?? \"\",\n now,\n now,\n ],\n });\n }\n\n return args.id;\n}\n\n/**\n * Delete a custom provider. Returns true if a row was deleted.\n */\nexport async function deleteCustomProvider(\n scope: CustomProviderScope,\n scopeId: string,\n id: string,\n): Promise<boolean> {\n await ensureTable();\n const client = getDbExec();\n const { rowsAffected } = await client.execute({\n sql: `DELETE FROM custom_api_providers WHERE scope=? AND scope_id=? AND id=?`,\n args: [scope, scopeId, id],\n });\n return rowsAffected > 0;\n}\n\n/**\n * List all custom providers visible to a given (scope, scopeId) pair.\n */\nexport async function listCustomProviders(\n scope: CustomProviderScope,\n scopeId: string,\n): Promise<CustomProviderConfig[]> {\n await ensureTable();\n const client = getDbExec();\n const { rows } = await client.execute({\n sql: `SELECT id, scope, scope_id, label, base_url, auth_json, docs_urls_json, allowed_host_suffixes_json, default_headers_json, notes, created_at, updated_at FROM custom_api_providers WHERE scope=? AND scope_id=? ORDER BY updated_at DESC`,\n args: [scope, scopeId],\n });\n return rows.map(rowToConfig);\n}\n\n/**\n * Look up one custom provider. Returns null when not found.\n */\nexport async function getCustomProvider(\n scope: CustomProviderScope,\n scopeId: string,\n id: string,\n): Promise<CustomProviderConfig | null> {\n await ensureTable();\n const client = getDbExec();\n const { rows } = await client.execute({\n sql: `SELECT id, scope, scope_id, label, base_url, auth_json, docs_urls_json, allowed_host_suffixes_json, default_headers_json, notes, created_at, updated_at FROM custom_api_providers WHERE scope=? AND scope_id=? AND id=? LIMIT 1`,\n args: [scope, scopeId, id],\n });\n if (rows.length === 0) return null;\n return rowToConfig(rows[0]);\n}\n\nfunction rowToConfig(row: Record<string, unknown>): CustomProviderConfig {\n return {\n id: row.id as string,\n scope: row.scope as CustomProviderScope,\n scopeId: row.scope_id as string,\n label: row.label as string,\n baseUrl: row.base_url as string,\n auth: JSON.parse(row.auth_json as string) as CustomProviderAuthKind,\n docsUrls: JSON.parse(row.docs_urls_json as string) as string[],\n allowedHostSuffixes: JSON.parse(\n row.allowed_host_suffixes_json as string,\n ) as string[],\n defaultHeaders: JSON.parse(row.default_headers_json as string) as Record<\n string,\n string\n >,\n notes: row.notes as string,\n createdAt: Number(row.created_at),\n updatedAt: Number(row.updated_at),\n };\n}\n"]}
|
|
@@ -1,8 +1,33 @@
|
|
|
1
1
|
import { type CredentialContext } from "../credentials/index.js";
|
|
2
2
|
import type { WorkspaceConnectionTemplateUse } from "../connections/catalog.js";
|
|
3
|
+
import type { CustomProviderConfig } from "./custom-registry.js";
|
|
4
|
+
export type { CustomProviderConfig, CustomProviderScope, CustomProviderAuthKind, UpsertCustomProviderArgs, } from "./custom-registry.js";
|
|
5
|
+
export { upsertCustomProvider, deleteCustomProvider, listCustomProviders, getCustomProvider, validateCustomBaseUrl, } from "./custom-registry.js";
|
|
3
6
|
export declare const PROVIDER_API_IDS: readonly ["amplitude", "apollo", "bigquery", "commonroom", "dataforseo", "ga4", "gcloud", "github", "gmail", "gong", "google_calendar", "google_drive", "granola", "grafana", "hubspot", "jira", "mixpanel", "notion", "posthog", "prometheus", "pylon", "sentry", "slack", "stripe", "twitter"];
|
|
4
7
|
export type ProviderApiId = (typeof PROVIDER_API_IDS)[number];
|
|
5
8
|
export type ProviderApiMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "HEAD";
|
|
9
|
+
/** Cursor-pagination config for fetchAllPages. */
|
|
10
|
+
export interface FetchAllPagesConfig {
|
|
11
|
+
/**
|
|
12
|
+
* Dot-path into the JSON response body where the next-page cursor lives,
|
|
13
|
+
* e.g. "meta.next_cursor" or "pagination.next_page_token".
|
|
14
|
+
*/
|
|
15
|
+
cursorPath: string;
|
|
16
|
+
/**
|
|
17
|
+
* Query parameter name to pass the cursor on the next request,
|
|
18
|
+
* e.g. "cursor" or "page_token".
|
|
19
|
+
*/
|
|
20
|
+
cursorParam: string;
|
|
21
|
+
/**
|
|
22
|
+
* Dot-path to the items array in each response body.
|
|
23
|
+
* When omitted, the whole response body is appended to the items array.
|
|
24
|
+
*/
|
|
25
|
+
itemsPath?: string;
|
|
26
|
+
/**
|
|
27
|
+
* Maximum number of pages to fetch. Default 10, max 50.
|
|
28
|
+
*/
|
|
29
|
+
maxPages?: number;
|
|
30
|
+
}
|
|
6
31
|
export interface ProviderApiRequestArgs {
|
|
7
32
|
provider: ProviderApiId | string;
|
|
8
33
|
method?: ProviderApiMethod;
|
|
@@ -15,6 +40,18 @@ export interface ProviderApiRequestArgs {
|
|
|
15
40
|
maxBytes?: number;
|
|
16
41
|
connectionId?: string | null;
|
|
17
42
|
accountId?: string | null;
|
|
43
|
+
/**
|
|
44
|
+
* When set, write the full response body to this workspace file path instead
|
|
45
|
+
* of returning it in context. Returns a compact summary with status, bytes,
|
|
46
|
+
* path, and a preview. Allows up to 20 MB (vs the normal 4 MB context limit).
|
|
47
|
+
*/
|
|
48
|
+
saveToFile?: string;
|
|
49
|
+
/**
|
|
50
|
+
* When set, automatically paginate by cursor until the cursor field is empty
|
|
51
|
+
* or maxPages is reached. Accumulates items from itemsPath (or whole bodies)
|
|
52
|
+
* across all pages. Combine with saveToFile to write the full dataset.
|
|
53
|
+
*/
|
|
54
|
+
fetchAllPages?: FetchAllPagesConfig;
|
|
18
55
|
}
|
|
19
56
|
export type ProviderApiAuthKind = {
|
|
20
57
|
type: "none";
|
|
@@ -100,10 +137,16 @@ export interface ProviderApiRuntimeOptions {
|
|
|
100
137
|
localCredentialSource?: string;
|
|
101
138
|
getCredentialContext?: () => CredentialContext | null;
|
|
102
139
|
resolveCredential?: ProviderApiCredentialResolver;
|
|
140
|
+
/**
|
|
141
|
+
* Optional loader for custom providers registered at runtime. When provided,
|
|
142
|
+
* custom providers are merged with the static built-in registry for catalog,
|
|
143
|
+
* docs, and request operations. Custom providers cannot shadow built-in ids.
|
|
144
|
+
*/
|
|
145
|
+
getCustomProviders?: () => Promise<CustomProviderConfig[]>;
|
|
103
146
|
}
|
|
104
147
|
interface ProviderApiRuntime {
|
|
105
148
|
providerIds: readonly ProviderApiId[];
|
|
106
|
-
listCatalog(provider?: ProviderApiId | string): ReturnType<typeof listProviderApiCatalog>;
|
|
149
|
+
listCatalog(provider?: ProviderApiId | string): ReturnType<typeof listProviderApiCatalog> | Promise<unknown[]>;
|
|
107
150
|
fetchDocs(options: {
|
|
108
151
|
provider: ProviderApiId | string;
|
|
109
152
|
url?: string;
|
|
@@ -138,7 +181,7 @@ export declare function fetchProviderApiDocs(options: {
|
|
|
138
181
|
url?: string;
|
|
139
182
|
maxBytes?: number;
|
|
140
183
|
}, runtime?: ProviderApiRuntimeOptions): Promise<{
|
|
141
|
-
provider:
|
|
184
|
+
provider: string;
|
|
142
185
|
catalog: {
|
|
143
186
|
id: "amplitude" | "bigquery" | "sentry" | "posthog" | "mixpanel" | "github" | "slack" | "twitter" | "notion" | "gmail" | "google_drive" | "hubspot" | "granola" | "apollo" | "commonroom" | "dataforseo" | "ga4" | "gcloud" | "gong" | "google_calendar" | "grafana" | "jira" | "prometheus" | "pylon" | "stripe";
|
|
144
187
|
label: string;
|
|
@@ -154,11 +197,28 @@ export declare function fetchProviderApiDocs(options: {
|
|
|
154
197
|
examples: readonly ProviderApiExample[];
|
|
155
198
|
notes: readonly string[];
|
|
156
199
|
templateUses: readonly WorkspaceConnectionTemplateUse[];
|
|
200
|
+
} | {
|
|
201
|
+
id: string;
|
|
202
|
+
label: string;
|
|
203
|
+
defaultBaseUrl: string;
|
|
204
|
+
baseUrlCredentialKey: any;
|
|
205
|
+
auth: string;
|
|
206
|
+
credentialKeys: string[];
|
|
207
|
+
docsUrls: string[];
|
|
208
|
+
specUrls: string[];
|
|
209
|
+
allowedHostSuffixes: string[];
|
|
210
|
+
placeholders: unknown[];
|
|
211
|
+
defaultHeaders: Record<string, string>;
|
|
212
|
+
examples: unknown[];
|
|
213
|
+
notes: string[];
|
|
214
|
+
templateUses: string[];
|
|
215
|
+
custom: boolean;
|
|
157
216
|
};
|
|
217
|
+
guidance: string;
|
|
158
218
|
request?: undefined;
|
|
159
219
|
response?: undefined;
|
|
160
220
|
} | {
|
|
161
|
-
provider:
|
|
221
|
+
provider: string;
|
|
162
222
|
catalog: {
|
|
163
223
|
id: "amplitude" | "bigquery" | "sentry" | "posthog" | "mixpanel" | "github" | "slack" | "twitter" | "notion" | "gmail" | "google_drive" | "hubspot" | "granola" | "apollo" | "commonroom" | "dataforseo" | "ga4" | "gcloud" | "gong" | "google_calendar" | "grafana" | "jira" | "prometheus" | "pylon" | "stripe";
|
|
164
224
|
label: string;
|
|
@@ -174,49 +234,25 @@ export declare function fetchProviderApiDocs(options: {
|
|
|
174
234
|
examples: readonly ProviderApiExample[];
|
|
175
235
|
notes: readonly string[];
|
|
176
236
|
templateUses: readonly WorkspaceConnectionTemplateUse[];
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
url: string;
|
|
180
|
-
};
|
|
181
|
-
response: {
|
|
182
|
-
status: number;
|
|
183
|
-
statusText: string;
|
|
184
|
-
ok: boolean;
|
|
185
|
-
elapsedMs: number;
|
|
186
|
-
headers: Record<string, string>;
|
|
187
|
-
contentType: string;
|
|
188
|
-
size: number;
|
|
189
|
-
truncated: boolean;
|
|
190
|
-
text: string;
|
|
191
|
-
json: unknown;
|
|
192
|
-
};
|
|
193
|
-
}>;
|
|
194
|
-
export declare function executeProviderApiRequest(args: ProviderApiRequestArgs, runtime: ProviderApiRuntimeOptions): Promise<{
|
|
195
|
-
provider: {
|
|
196
|
-
id: "amplitude" | "bigquery" | "sentry" | "posthog" | "mixpanel" | "github" | "slack" | "twitter" | "notion" | "gmail" | "google_drive" | "hubspot" | "granola" | "apollo" | "commonroom" | "dataforseo" | "ga4" | "gcloud" | "gong" | "google_calendar" | "grafana" | "jira" | "prometheus" | "pylon" | "stripe";
|
|
237
|
+
} | {
|
|
238
|
+
id: string;
|
|
197
239
|
label: string;
|
|
198
|
-
|
|
199
|
-
|
|
240
|
+
defaultBaseUrl: string;
|
|
241
|
+
baseUrlCredentialKey: any;
|
|
242
|
+
auth: string;
|
|
243
|
+
credentialKeys: string[];
|
|
244
|
+
docsUrls: string[];
|
|
245
|
+
specUrls: string[];
|
|
246
|
+
allowedHostSuffixes: string[];
|
|
247
|
+
placeholders: unknown[];
|
|
248
|
+
defaultHeaders: Record<string, string>;
|
|
249
|
+
examples: unknown[];
|
|
250
|
+
notes: string[];
|
|
251
|
+
templateUses: string[];
|
|
252
|
+
custom: boolean;
|
|
200
253
|
};
|
|
201
254
|
request: {
|
|
202
|
-
connectionId?: string;
|
|
203
|
-
accountId?: string;
|
|
204
|
-
method: ProviderApiMethod;
|
|
205
255
|
url: string;
|
|
206
|
-
path: string;
|
|
207
|
-
auth: string;
|
|
208
|
-
credentialSources: {
|
|
209
|
-
fingerprint: string;
|
|
210
|
-
source: string;
|
|
211
|
-
key: string;
|
|
212
|
-
provider: string;
|
|
213
|
-
scope?: string;
|
|
214
|
-
accountId?: string;
|
|
215
|
-
accountLabel?: string | null;
|
|
216
|
-
connectionId?: string;
|
|
217
|
-
connectionLabel?: string;
|
|
218
|
-
}[];
|
|
219
|
-
headerNames: string[];
|
|
220
256
|
};
|
|
221
257
|
response: {
|
|
222
258
|
status: number;
|
|
@@ -230,8 +266,8 @@ export declare function executeProviderApiRequest(args: ProviderApiRequestArgs,
|
|
|
230
266
|
text: string;
|
|
231
267
|
json: unknown;
|
|
232
268
|
};
|
|
233
|
-
guidance
|
|
269
|
+
guidance?: undefined;
|
|
234
270
|
}>;
|
|
271
|
+
export declare function executeProviderApiRequest(args: ProviderApiRequestArgs, runtime: ProviderApiRuntimeOptions): Promise<unknown>;
|
|
235
272
|
export declare function defaultProviderApiCredentialResolver(options: ProviderApiCredentialLookupOptions): Promise<ProviderApiResolvedCredential | null>;
|
|
236
|
-
export {};
|
|
237
273
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/provider-api/index.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,KAAK,iBAAiB,EACvB,MAAM,yBAAyB,CAAC;AAWjC,OAAO,KAAK,EAAE,8BAA8B,EAAE,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/provider-api/index.ts"],"names":[],"mappings":"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"}
|