@tangle-network/agent-integrations 0.23.0 → 0.24.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.
- package/README.md +9 -3
- package/dist/bin/tangle-catalog-runtime.js +1 -1
- package/dist/{chunk-P4BB4CU6.js → chunk-6SSYWA3J.js} +1140 -1077
- package/dist/chunk-6SSYWA3J.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +3 -1
- package/dist/specs.d.ts +39 -1
- package/docs/integration-execution-audit.md +28 -20
- package/docs/integration-execution-matrix.json +7425 -1334
- package/package.json +2 -2
- package/dist/chunk-P4BB4CU6.js.map +0 -1
|
@@ -218,6 +218,665 @@ function dataClassFor(category) {
|
|
|
218
218
|
return "internal";
|
|
219
219
|
}
|
|
220
220
|
|
|
221
|
+
// src/catalog-executor.ts
|
|
222
|
+
function createCatalogExecutorProvider(options) {
|
|
223
|
+
const byConnector = new Map(options.connectors.map((connector) => [connector.id, connector]));
|
|
224
|
+
return {
|
|
225
|
+
id: options.id,
|
|
226
|
+
kind: options.kind,
|
|
227
|
+
listConnectors: () => options.connectors,
|
|
228
|
+
startAuth: options.startAuth,
|
|
229
|
+
completeAuth: options.completeAuth,
|
|
230
|
+
async invokeAction(connection, request) {
|
|
231
|
+
const connector = byConnector.get(connection.connectorId);
|
|
232
|
+
if (!connector) {
|
|
233
|
+
throw new IntegrationError(`Connector ${connection.connectorId} not found.`, "connector_not_found");
|
|
234
|
+
}
|
|
235
|
+
const action = connector.actions.find((candidate) => candidate.id === request.action);
|
|
236
|
+
if (!action) {
|
|
237
|
+
throw new IntegrationError(`Action ${request.action} is not defined by connector ${connector.id}.`, "action_not_found");
|
|
238
|
+
}
|
|
239
|
+
return options.executeAction({ connection, request, connector, action });
|
|
240
|
+
},
|
|
241
|
+
async subscribeTrigger(connection, triggerId, targetUrl) {
|
|
242
|
+
if (!options.subscribeTrigger) {
|
|
243
|
+
throw new IntegrationError(`Provider ${options.id} does not support trigger subscriptions.`, "action_not_found");
|
|
244
|
+
}
|
|
245
|
+
const connector = byConnector.get(connection.connectorId);
|
|
246
|
+
if (!connector) {
|
|
247
|
+
throw new IntegrationError(`Connector ${connection.connectorId} not found.`, "connector_not_found");
|
|
248
|
+
}
|
|
249
|
+
const trigger2 = connector.triggers?.find((candidate) => candidate.id === triggerId);
|
|
250
|
+
if (!trigger2) {
|
|
251
|
+
throw new IntegrationError(`Trigger ${triggerId} is not defined by connector ${connector.id}.`, "action_not_found");
|
|
252
|
+
}
|
|
253
|
+
return options.subscribeTrigger(connection, trigger2, targetUrl);
|
|
254
|
+
},
|
|
255
|
+
unsubscribeTrigger: options.unsubscribeTrigger,
|
|
256
|
+
normalizeTriggerEvent: options.normalizeTriggerEvent
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// src/activepieces-provider.ts
|
|
261
|
+
function createActivepiecesExecutorProvider(options) {
|
|
262
|
+
const providerId = options.id ?? "activepieces";
|
|
263
|
+
const connectors = options.connectors ?? buildActivepiecesConnectors({
|
|
264
|
+
providerId,
|
|
265
|
+
includeCatalogActions: true,
|
|
266
|
+
executable: true
|
|
267
|
+
});
|
|
268
|
+
const byEntry = new Map(listActivepiecesCatalogEntries().map((entry) => [entry.id, entry]));
|
|
269
|
+
return createCatalogExecutorProvider({
|
|
270
|
+
id: providerId,
|
|
271
|
+
kind: "activepieces",
|
|
272
|
+
connectors,
|
|
273
|
+
startAuth: options.startAuth,
|
|
274
|
+
completeAuth: options.completeAuth,
|
|
275
|
+
executeAction: async ({ connection, request, connector, action }) => {
|
|
276
|
+
const catalogEntry = byEntry.get(connector.id);
|
|
277
|
+
if (!catalogEntry) {
|
|
278
|
+
throw new IntegrationError(`Activepieces catalog entry ${connector.id} not found.`, "connector_not_found");
|
|
279
|
+
}
|
|
280
|
+
const catalogAction = catalogEntry.actions.find((candidate) => candidate.id === action.id);
|
|
281
|
+
return options.executeAction({
|
|
282
|
+
connection,
|
|
283
|
+
request,
|
|
284
|
+
connector,
|
|
285
|
+
catalogEntry,
|
|
286
|
+
piece: {
|
|
287
|
+
id: catalogEntry.id,
|
|
288
|
+
npmPackage: catalogEntry.npmPackage,
|
|
289
|
+
version: catalogEntry.version,
|
|
290
|
+
actionId: action.id,
|
|
291
|
+
upstreamActionName: catalogAction?.upstreamName
|
|
292
|
+
}
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// src/catalog.ts
|
|
299
|
+
var riskRank = {
|
|
300
|
+
read: 0,
|
|
301
|
+
write: 1,
|
|
302
|
+
destructive: 2
|
|
303
|
+
};
|
|
304
|
+
function integrationToolName(providerId, connectorId, actionId) {
|
|
305
|
+
return `int_${encodeToolPart(providerId)}_${encodeToolPart(connectorId)}_${encodeToolPart(actionId)}`;
|
|
306
|
+
}
|
|
307
|
+
function parseIntegrationToolName(name) {
|
|
308
|
+
const parts = name.split("_");
|
|
309
|
+
if (parts.length !== 4 || parts[0] !== "int") {
|
|
310
|
+
throw new Error(`Invalid integration tool name: ${name}`);
|
|
311
|
+
}
|
|
312
|
+
return {
|
|
313
|
+
providerId: decodeToolPart(parts[1]),
|
|
314
|
+
connectorId: decodeToolPart(parts[2]),
|
|
315
|
+
actionId: decodeToolPart(parts[3])
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
function buildIntegrationToolCatalog(connectors) {
|
|
319
|
+
const tools = [];
|
|
320
|
+
for (const connector of connectors) {
|
|
321
|
+
for (const action of connector.actions) {
|
|
322
|
+
const tags = unique([
|
|
323
|
+
connector.id,
|
|
324
|
+
connector.providerId,
|
|
325
|
+
connector.title,
|
|
326
|
+
connector.category,
|
|
327
|
+
action.id,
|
|
328
|
+
action.title,
|
|
329
|
+
action.risk,
|
|
330
|
+
action.dataClass,
|
|
331
|
+
...connector.scopes ?? [],
|
|
332
|
+
...action.requiredScopes ?? []
|
|
333
|
+
].flatMap(tokenize));
|
|
334
|
+
tools.push({
|
|
335
|
+
name: integrationToolName(connector.providerId, connector.id, action.id),
|
|
336
|
+
title: `${connector.title}: ${action.title}`,
|
|
337
|
+
description: action.description ?? `${action.risk} action ${action.id} on ${connector.title}`,
|
|
338
|
+
providerId: connector.providerId,
|
|
339
|
+
connectorId: connector.id,
|
|
340
|
+
connectorTitle: connector.title,
|
|
341
|
+
category: connector.category,
|
|
342
|
+
action,
|
|
343
|
+
risk: action.risk,
|
|
344
|
+
dataClass: action.dataClass,
|
|
345
|
+
requiredScopes: action.requiredScopes,
|
|
346
|
+
inputSchema: action.inputSchema,
|
|
347
|
+
outputSchema: action.outputSchema,
|
|
348
|
+
tags
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
return tools;
|
|
353
|
+
}
|
|
354
|
+
function searchIntegrationTools(catalog, query, filters = {}) {
|
|
355
|
+
const terms = tokenize(query);
|
|
356
|
+
const filtered = catalog.filter((tool) => {
|
|
357
|
+
if (filters.providerId && tool.providerId !== filters.providerId) return false;
|
|
358
|
+
if (filters.connectorId && tool.connectorId !== filters.connectorId) return false;
|
|
359
|
+
if (filters.category && tool.category !== filters.category) return false;
|
|
360
|
+
if (filters.dataClass && tool.dataClass !== filters.dataClass) return false;
|
|
361
|
+
if (filters.maxRisk && riskRank[tool.risk] > riskRank[filters.maxRisk]) return false;
|
|
362
|
+
return true;
|
|
363
|
+
});
|
|
364
|
+
const scored = filtered.map((tool) => scoreTool(tool, terms));
|
|
365
|
+
return scored.filter((result) => terms.length === 0 || result.score > 0).sort((a, b) => b.score - a.score || a.tool.name.localeCompare(b.tool.name)).slice(0, filters.limit ?? 20);
|
|
366
|
+
}
|
|
367
|
+
function toMcpTools(tools) {
|
|
368
|
+
return tools.map((tool) => ({
|
|
369
|
+
name: tool.name,
|
|
370
|
+
description: `${tool.title}. ${tool.description}`,
|
|
371
|
+
inputSchema: tool.inputSchema ?? {
|
|
372
|
+
type: "object",
|
|
373
|
+
additionalProperties: true,
|
|
374
|
+
properties: {}
|
|
375
|
+
}
|
|
376
|
+
}));
|
|
377
|
+
}
|
|
378
|
+
function scoreTool(tool, terms) {
|
|
379
|
+
if (terms.length === 0) return { tool, score: 1, matched: [] };
|
|
380
|
+
const haystack = new Set(tool.tags);
|
|
381
|
+
const matched = [];
|
|
382
|
+
let score = 0;
|
|
383
|
+
for (const term of terms) {
|
|
384
|
+
if (haystack.has(term)) {
|
|
385
|
+
matched.push(term);
|
|
386
|
+
score += 4;
|
|
387
|
+
continue;
|
|
388
|
+
}
|
|
389
|
+
if (tool.tags.some((tag) => tag.includes(term))) {
|
|
390
|
+
matched.push(term);
|
|
391
|
+
score += 1;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
if (tool.risk === "read") score += 0.25;
|
|
395
|
+
return { tool, score, matched: unique(matched) };
|
|
396
|
+
}
|
|
397
|
+
function tokenize(value) {
|
|
398
|
+
return value.toLowerCase().split(/[^a-z0-9]+/g).map((part) => part.trim()).filter(Boolean);
|
|
399
|
+
}
|
|
400
|
+
function encodeToolPart(value) {
|
|
401
|
+
return Buffer.from(value, "utf8").toString("base64url").replace(/_/g, ".");
|
|
402
|
+
}
|
|
403
|
+
function decodeToolPart(value) {
|
|
404
|
+
return Buffer.from(value.replace(/\./g, "_"), "base64url").toString("utf8");
|
|
405
|
+
}
|
|
406
|
+
function unique(values) {
|
|
407
|
+
return [...new Set(values)];
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
// src/catalog-freshness.ts
|
|
411
|
+
var ACTIVEPIECES_PUBLIC_CATALOG_URL = "https://www.activepieces.com/pieces";
|
|
412
|
+
function parseCount(value) {
|
|
413
|
+
if (!value) return void 0;
|
|
414
|
+
const parsed = Number.parseInt(value.replace(/,/g, ""), 10);
|
|
415
|
+
return Number.isFinite(parsed) ? parsed : void 0;
|
|
416
|
+
}
|
|
417
|
+
function extractActivepiecesPublicPieceCount(html) {
|
|
418
|
+
const showingMatch = html.match(/Showing\s+([0-9,]+)\s+pieces/i);
|
|
419
|
+
const integrationMatch = html.match(/([0-9,]+)\+?\s+Integrations/i);
|
|
420
|
+
return parseCount(showingMatch?.[1]) ?? parseCount(integrationMatch?.[1]);
|
|
421
|
+
}
|
|
422
|
+
async function auditIntegrationCatalogFreshness(options = {}) {
|
|
423
|
+
const minActivepiecesConnectors = options.minActivepiecesConnectors ?? 600;
|
|
424
|
+
const staleConnectorDelta = options.staleConnectorDelta ?? 25;
|
|
425
|
+
const activepiecesEntries = listActivepiecesCatalogEntries();
|
|
426
|
+
const activepiecesConnectors = buildActivepiecesConnectors({
|
|
427
|
+
includeCatalogActions: true
|
|
428
|
+
});
|
|
429
|
+
const executableActivepiecesProvider = createActivepiecesExecutorProvider({
|
|
430
|
+
executeAction: () => ({ ok: true, action: "audit.noop" })
|
|
431
|
+
});
|
|
432
|
+
const executableActivepiecesConnectors = await executableActivepiecesProvider.listConnectors();
|
|
433
|
+
const executableRegistry = composeIntegrationRegistry([
|
|
434
|
+
{
|
|
435
|
+
id: executableActivepiecesProvider.id,
|
|
436
|
+
connectors: executableActivepiecesConnectors
|
|
437
|
+
}
|
|
438
|
+
]);
|
|
439
|
+
const executableTools = buildIntegrationToolCatalog(executableRegistry.connectors);
|
|
440
|
+
const unsupportedExecutableConnectorIds = executableActivepiecesConnectors.filter((connector) => connector.actions.length === 0).map((connector) => connector.id);
|
|
441
|
+
const registry = buildDefaultIntegrationRegistry({
|
|
442
|
+
includeSpecs: true,
|
|
443
|
+
includeActivepieces: true
|
|
444
|
+
});
|
|
445
|
+
const warnings = [];
|
|
446
|
+
if (activepiecesConnectors.length < minActivepiecesConnectors) {
|
|
447
|
+
warnings.push(
|
|
448
|
+
`Activepieces catalog has ${activepiecesConnectors.length} connectors, below floor ${minActivepiecesConnectors}.`
|
|
449
|
+
);
|
|
450
|
+
}
|
|
451
|
+
if (unsupportedExecutableConnectorIds.length > 0) {
|
|
452
|
+
warnings.push(
|
|
453
|
+
`Activepieces executable provider has ${unsupportedExecutableConnectorIds.length} connectors without actions.`
|
|
454
|
+
);
|
|
455
|
+
}
|
|
456
|
+
if (executableTools.length < activepiecesEntries.length) {
|
|
457
|
+
warnings.push(
|
|
458
|
+
`Activepieces executable provider produced only ${executableTools.length} tool definitions for ${activepiecesEntries.length} entries.`
|
|
459
|
+
);
|
|
460
|
+
}
|
|
461
|
+
let upstream;
|
|
462
|
+
if (options.liveActivepieces) {
|
|
463
|
+
upstream = await checkActivepiecesPublicCatalog({
|
|
464
|
+
localConnectorCount: activepiecesConnectors.length,
|
|
465
|
+
staleConnectorDelta,
|
|
466
|
+
fetchImpl: options.fetchImpl,
|
|
467
|
+
warnings
|
|
468
|
+
});
|
|
469
|
+
}
|
|
470
|
+
return {
|
|
471
|
+
ok: warnings.length === 0,
|
|
472
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
473
|
+
local: {
|
|
474
|
+
activepiecesEntries: activepiecesEntries.length,
|
|
475
|
+
activepiecesConnectors: activepiecesConnectors.length,
|
|
476
|
+
activepiecesActions: activepiecesConnectors.reduce(
|
|
477
|
+
(sum, connector) => sum + connector.actions.length,
|
|
478
|
+
0
|
|
479
|
+
),
|
|
480
|
+
activepiecesTriggers: activepiecesConnectors.reduce(
|
|
481
|
+
(sum, connector) => sum + (connector.triggers?.length ?? 0),
|
|
482
|
+
0
|
|
483
|
+
),
|
|
484
|
+
executableActivepiecesConnectors: executableActivepiecesConnectors.length,
|
|
485
|
+
executableActivepiecesActions: executableActivepiecesConnectors.reduce(
|
|
486
|
+
(sum, connector) => sum + connector.actions.length,
|
|
487
|
+
0
|
|
488
|
+
),
|
|
489
|
+
executableActivepiecesTriggers: executableActivepiecesConnectors.reduce(
|
|
490
|
+
(sum, connector) => sum + (connector.triggers?.length ?? 0),
|
|
491
|
+
0
|
|
492
|
+
),
|
|
493
|
+
executableToolDefinitions: executableTools.length,
|
|
494
|
+
unsupportedExecutableConnectorIds,
|
|
495
|
+
registryEntries: registry.entries.length,
|
|
496
|
+
registrySummary: summarizeIntegrationRegistry(registry),
|
|
497
|
+
conflictSamples: registry.entries.flatMap((entry) => entry.conflicts).slice(0, 10)
|
|
498
|
+
},
|
|
499
|
+
upstream,
|
|
500
|
+
warnings
|
|
501
|
+
};
|
|
502
|
+
}
|
|
503
|
+
async function checkActivepiecesPublicCatalog(input) {
|
|
504
|
+
try {
|
|
505
|
+
const res = await (input.fetchImpl ?? fetch)(ACTIVEPIECES_PUBLIC_CATALOG_URL, {
|
|
506
|
+
headers: { accept: "text/html" },
|
|
507
|
+
signal: AbortSignal.timeout(15e3)
|
|
508
|
+
});
|
|
509
|
+
if (!res.ok) {
|
|
510
|
+
const warning = `Activepieces freshness request failed with HTTP ${res.status}.`;
|
|
511
|
+
input.warnings.push(warning);
|
|
512
|
+
return {
|
|
513
|
+
checkedUrl: ACTIVEPIECES_PUBLIC_CATALOG_URL,
|
|
514
|
+
warning
|
|
515
|
+
};
|
|
516
|
+
}
|
|
517
|
+
const activepiecesPieces = extractActivepiecesPublicPieceCount(await res.text());
|
|
518
|
+
const activepiecesDelta = activepiecesPieces === void 0 ? void 0 : activepiecesPieces - input.localConnectorCount;
|
|
519
|
+
if (activepiecesDelta !== void 0 && activepiecesDelta > input.staleConnectorDelta) {
|
|
520
|
+
input.warnings.push(
|
|
521
|
+
`Activepieces upstream appears ${activepiecesDelta} connectors ahead of the vendored package catalog.`
|
|
522
|
+
);
|
|
523
|
+
}
|
|
524
|
+
return {
|
|
525
|
+
activepiecesPieces,
|
|
526
|
+
activepiecesDelta,
|
|
527
|
+
checkedUrl: ACTIVEPIECES_PUBLIC_CATALOG_URL,
|
|
528
|
+
warning: activepiecesPieces === void 0 ? "Could not parse upstream piece count." : void 0
|
|
529
|
+
};
|
|
530
|
+
} catch (error) {
|
|
531
|
+
const warning = error instanceof Error ? error.message : "Activepieces freshness request failed.";
|
|
532
|
+
input.warnings.push(warning);
|
|
533
|
+
return {
|
|
534
|
+
checkedUrl: ACTIVEPIECES_PUBLIC_CATALOG_URL,
|
|
535
|
+
warning
|
|
536
|
+
};
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
// src/activepieces-runtime.ts
|
|
541
|
+
import { createHmac, randomUUID, timingSafeEqual } from "crypto";
|
|
542
|
+
var ACTIVEPIECES_RUNTIME_SIGNATURE_HEADER = "x-tangle-activepieces-signature";
|
|
543
|
+
var TANGLE_CATALOG_RUNTIME_SIGNATURE_HEADER = "x-tangle-catalog-signature";
|
|
544
|
+
function createActivepiecesHttpExecutor(options) {
|
|
545
|
+
const endpoint = options.endpoint.replace(/\/$/, "");
|
|
546
|
+
const path = options.path ?? "/v1/activepieces/actions/invoke";
|
|
547
|
+
const normalizedPath = path.startsWith("/") ? path : `/${path}`;
|
|
548
|
+
const signatureHeader = options.signatureHeader ?? ACTIVEPIECES_RUNTIME_SIGNATURE_HEADER;
|
|
549
|
+
const fetchImpl = options.fetchImpl ?? fetch;
|
|
550
|
+
const requestId = options.requestId ?? (() => `apexec_${randomUUID()}`);
|
|
551
|
+
return async (invocation) => {
|
|
552
|
+
const body = buildActivepiecesRuntimeRequest(invocation, requestId());
|
|
553
|
+
const serialized = JSON.stringify(body);
|
|
554
|
+
const response = await fetchImpl(`${endpoint}${normalizedPath}`, {
|
|
555
|
+
method: "POST",
|
|
556
|
+
headers: {
|
|
557
|
+
"content-type": "application/json",
|
|
558
|
+
...options.headers,
|
|
559
|
+
...options.secret ? { [signatureHeader]: signActivepiecesRuntimeRequest(serialized, options.secret) } : {}
|
|
560
|
+
},
|
|
561
|
+
body: serialized,
|
|
562
|
+
signal: AbortSignal.timeout(options.timeoutMs ?? 3e4)
|
|
563
|
+
});
|
|
564
|
+
const parsed = await response.json().catch(() => void 0);
|
|
565
|
+
if (!response.ok) {
|
|
566
|
+
return parsed ?? {
|
|
567
|
+
ok: false,
|
|
568
|
+
action: invocation.request.action,
|
|
569
|
+
output: { message: `Activepieces runtime returned HTTP ${response.status}.` }
|
|
570
|
+
};
|
|
571
|
+
}
|
|
572
|
+
return parsed ?? {
|
|
573
|
+
ok: false,
|
|
574
|
+
action: invocation.request.action,
|
|
575
|
+
output: { message: "Activepieces runtime returned an empty response." }
|
|
576
|
+
};
|
|
577
|
+
};
|
|
578
|
+
}
|
|
579
|
+
function buildActivepiecesRuntimeRequest(invocation, requestId = `apexec_${randomUUID()}`) {
|
|
580
|
+
return {
|
|
581
|
+
version: 1,
|
|
582
|
+
requestId,
|
|
583
|
+
providerId: invocation.connection.providerId,
|
|
584
|
+
connection: invocation.connection,
|
|
585
|
+
connector: {
|
|
586
|
+
id: invocation.connector.id,
|
|
587
|
+
title: invocation.connector.title,
|
|
588
|
+
auth: invocation.connector.auth,
|
|
589
|
+
scopes: invocation.connector.scopes,
|
|
590
|
+
metadata: invocation.connector.metadata
|
|
591
|
+
},
|
|
592
|
+
piece: invocation.piece,
|
|
593
|
+
action: {
|
|
594
|
+
id: invocation.request.action,
|
|
595
|
+
input: invocation.request.input,
|
|
596
|
+
idempotencyKey: invocation.request.idempotencyKey,
|
|
597
|
+
dryRun: invocation.request.dryRun,
|
|
598
|
+
metadata: invocation.request.metadata
|
|
599
|
+
}
|
|
600
|
+
};
|
|
601
|
+
}
|
|
602
|
+
function signActivepiecesRuntimeRequest(serializedBody, secret) {
|
|
603
|
+
return `sha256=${createHmac("sha256", secret).update(serializedBody).digest("hex")}`;
|
|
604
|
+
}
|
|
605
|
+
function verifyActivepiecesRuntimeSignature(serializedBody, signature, secret) {
|
|
606
|
+
if (!signature) return false;
|
|
607
|
+
const expected = signActivepiecesRuntimeRequest(serializedBody, secret);
|
|
608
|
+
const left = Buffer.from(signature);
|
|
609
|
+
const right = Buffer.from(expected);
|
|
610
|
+
return left.length === right.length && timingSafeEqual(left, right);
|
|
611
|
+
}
|
|
612
|
+
function createTangleCatalogHttpExecutor(options) {
|
|
613
|
+
const endpoint = options.endpoint.replace(/\/$/, "");
|
|
614
|
+
const path = options.path ?? "/v1/integration-catalog/actions/invoke";
|
|
615
|
+
const normalizedPath = path.startsWith("/") ? path : `/${path}`;
|
|
616
|
+
const signatureHeader = options.signatureHeader ?? TANGLE_CATALOG_RUNTIME_SIGNATURE_HEADER;
|
|
617
|
+
const fetchImpl = options.fetchImpl ?? fetch;
|
|
618
|
+
const requestId = options.requestId ?? (() => `tcat_${randomUUID()}`);
|
|
619
|
+
return async (invocation) => {
|
|
620
|
+
const body = buildTangleCatalogRuntimeRequest(invocation, requestId());
|
|
621
|
+
const serialized = JSON.stringify(body);
|
|
622
|
+
const response = await fetchImpl(`${endpoint}${normalizedPath}`, {
|
|
623
|
+
method: "POST",
|
|
624
|
+
headers: {
|
|
625
|
+
"content-type": "application/json",
|
|
626
|
+
...options.headers,
|
|
627
|
+
...options.secret ? { [signatureHeader]: signTangleCatalogRuntimeRequest(serialized, options.secret) } : {}
|
|
628
|
+
},
|
|
629
|
+
body: serialized,
|
|
630
|
+
signal: AbortSignal.timeout(options.timeoutMs ?? 3e4)
|
|
631
|
+
});
|
|
632
|
+
const parsed = await response.json().catch(() => void 0);
|
|
633
|
+
if (!response.ok) {
|
|
634
|
+
return parsed ?? {
|
|
635
|
+
ok: false,
|
|
636
|
+
action: invocation.request.action,
|
|
637
|
+
output: { message: `Tangle catalog runtime returned HTTP ${response.status}.` }
|
|
638
|
+
};
|
|
639
|
+
}
|
|
640
|
+
return parsed ?? {
|
|
641
|
+
ok: false,
|
|
642
|
+
action: invocation.request.action,
|
|
643
|
+
output: { message: "Tangle catalog runtime returned an empty response." }
|
|
644
|
+
};
|
|
645
|
+
};
|
|
646
|
+
}
|
|
647
|
+
function buildTangleCatalogRuntimeRequest(invocation, requestId = `tcat_${randomUUID()}`) {
|
|
648
|
+
return {
|
|
649
|
+
version: 1,
|
|
650
|
+
requestId,
|
|
651
|
+
providerId: invocation.connection.providerId,
|
|
652
|
+
connection: invocation.connection,
|
|
653
|
+
connector: {
|
|
654
|
+
id: invocation.connector.id,
|
|
655
|
+
title: invocation.connector.title,
|
|
656
|
+
auth: invocation.connector.auth,
|
|
657
|
+
scopes: invocation.connector.scopes,
|
|
658
|
+
metadata: invocation.connector.metadata
|
|
659
|
+
},
|
|
660
|
+
piece: invocation.piece,
|
|
661
|
+
action: {
|
|
662
|
+
id: invocation.request.action,
|
|
663
|
+
input: invocation.request.input,
|
|
664
|
+
idempotencyKey: invocation.request.idempotencyKey,
|
|
665
|
+
dryRun: invocation.request.dryRun,
|
|
666
|
+
metadata: invocation.request.metadata
|
|
667
|
+
}
|
|
668
|
+
};
|
|
669
|
+
}
|
|
670
|
+
var signTangleCatalogRuntimeRequest = signActivepiecesRuntimeRequest;
|
|
671
|
+
var verifyTangleCatalogRuntimeSignature = verifyActivepiecesRuntimeSignature;
|
|
672
|
+
|
|
673
|
+
// src/tangle-catalog.ts
|
|
674
|
+
var TANGLE_INTEGRATIONS_CATALOG_PROVIDER_ID = "tangle-catalog";
|
|
675
|
+
var TANGLE_INTEGRATIONS_CATALOG_SOURCE = "tangle-integrations-catalog";
|
|
676
|
+
var NATIVE_ADAPTER_IDS = /* @__PURE__ */ new Set([
|
|
677
|
+
"google-calendar",
|
|
678
|
+
"google-sheets",
|
|
679
|
+
"microsoft-calendar",
|
|
680
|
+
"hubspot",
|
|
681
|
+
"slack",
|
|
682
|
+
"notion-database",
|
|
683
|
+
"twilio-sms",
|
|
684
|
+
"stripe-pack",
|
|
685
|
+
"webhook",
|
|
686
|
+
"stripe",
|
|
687
|
+
"slack-inbound",
|
|
688
|
+
"github",
|
|
689
|
+
"gitlab",
|
|
690
|
+
"airtable",
|
|
691
|
+
"asana",
|
|
692
|
+
"salesforce"
|
|
693
|
+
]);
|
|
694
|
+
function listTangleIntegrationCatalogEntries() {
|
|
695
|
+
return listActivepiecesCatalogEntries().map((entry) => sanitizeEntry(entry));
|
|
696
|
+
}
|
|
697
|
+
function listTangleIntegrationContracts() {
|
|
698
|
+
return listActivepiecesCatalogEntries().map((entry) => {
|
|
699
|
+
const nativeAdapter = NATIVE_ADAPTER_IDS.has(entry.id);
|
|
700
|
+
return {
|
|
701
|
+
id: entry.id,
|
|
702
|
+
title: entry.title,
|
|
703
|
+
description: entry.description,
|
|
704
|
+
category: entry.category,
|
|
705
|
+
auth: entry.auth,
|
|
706
|
+
authFields: entry.authFields ?? [],
|
|
707
|
+
actions: entry.actions.map((action) => ({
|
|
708
|
+
id: action.id,
|
|
709
|
+
title: action.title,
|
|
710
|
+
risk: action.risk,
|
|
711
|
+
upstreamName: action.upstreamName ?? action.id
|
|
712
|
+
})),
|
|
713
|
+
triggers: entry.triggers.map((trigger2) => ({
|
|
714
|
+
id: trigger2.id,
|
|
715
|
+
title: trigger2.title,
|
|
716
|
+
upstreamName: trigger2.upstreamName ?? trigger2.id
|
|
717
|
+
})),
|
|
718
|
+
implementation: {
|
|
719
|
+
kind: nativeAdapter ? "native_adapter" : "package_runtime",
|
|
720
|
+
runtimePackage: entry.npmPackage,
|
|
721
|
+
version: entry.version
|
|
722
|
+
},
|
|
723
|
+
status: nativeAdapter ? "native_backed" : entry.npmPackage ? "runtime_backed" : "contract_ready",
|
|
724
|
+
quality: {
|
|
725
|
+
tangleContract: true,
|
|
726
|
+
authFieldsMapped: entry.auth === "none" || Boolean(entry.authFields?.length),
|
|
727
|
+
actionNamesMapped: entry.actions.every((action) => Boolean(action.upstreamName)),
|
|
728
|
+
triggerNamesMapped: entry.triggers.every((trigger2) => Boolean(trigger2.upstreamName)),
|
|
729
|
+
runtimePackageMapped: Boolean(entry.npmPackage),
|
|
730
|
+
nativeAdapter
|
|
731
|
+
}
|
|
732
|
+
};
|
|
733
|
+
});
|
|
734
|
+
}
|
|
735
|
+
function listTangleIntegrationCatalogRuntimePackages() {
|
|
736
|
+
return listActivepiecesCatalogEntries().filter((entry) => Boolean(entry.npmPackage)).map((entry) => ({
|
|
737
|
+
connectorId: entry.id,
|
|
738
|
+
packageName: entry.npmPackage,
|
|
739
|
+
version: entry.version
|
|
740
|
+
}));
|
|
741
|
+
}
|
|
742
|
+
function buildTangleIntegrationCatalogConnectors(options = {}) {
|
|
743
|
+
const providerId = options.providerId ?? TANGLE_INTEGRATIONS_CATALOG_PROVIDER_ID;
|
|
744
|
+
return buildActivepiecesConnectors({
|
|
745
|
+
...options,
|
|
746
|
+
providerId
|
|
747
|
+
}).map((connector) => sanitizeConnector(connector, providerId));
|
|
748
|
+
}
|
|
749
|
+
function createTangleCatalogExecutorProvider(options) {
|
|
750
|
+
const providerId = options.id ?? TANGLE_INTEGRATIONS_CATALOG_PROVIDER_ID;
|
|
751
|
+
const connectors = options.connectors ?? buildTangleIntegrationCatalogConnectors({
|
|
752
|
+
providerId,
|
|
753
|
+
includeCatalogActions: true,
|
|
754
|
+
executable: true
|
|
755
|
+
});
|
|
756
|
+
const byEntry = new Map(listActivepiecesCatalogEntries().map((entry) => [entry.id, entry]));
|
|
757
|
+
return createCatalogExecutorProvider({
|
|
758
|
+
id: providerId,
|
|
759
|
+
kind: "tangle_catalog",
|
|
760
|
+
connectors,
|
|
761
|
+
startAuth: options.startAuth,
|
|
762
|
+
completeAuth: options.completeAuth,
|
|
763
|
+
executeAction: async ({ connection, request, connector, action }) => {
|
|
764
|
+
const importedEntry = byEntry.get(connector.id);
|
|
765
|
+
if (!importedEntry) {
|
|
766
|
+
throw new IntegrationError(`Tangle catalog entry ${connector.id} not found.`, "connector_not_found");
|
|
767
|
+
}
|
|
768
|
+
const catalogAction = importedEntry.actions.find((candidate) => candidate.id === action.id);
|
|
769
|
+
return options.executeAction({
|
|
770
|
+
connection,
|
|
771
|
+
request,
|
|
772
|
+
connector,
|
|
773
|
+
catalogEntry: sanitizeEntry(importedEntry),
|
|
774
|
+
piece: {
|
|
775
|
+
id: importedEntry.id,
|
|
776
|
+
version: importedEntry.version,
|
|
777
|
+
actionId: action.id,
|
|
778
|
+
upstreamActionName: catalogAction?.upstreamName
|
|
779
|
+
}
|
|
780
|
+
});
|
|
781
|
+
},
|
|
782
|
+
subscribeTrigger: options.subscribeTrigger ? async (connection, trigger2, targetUrl) => {
|
|
783
|
+
const connector = connectors.find((candidate) => candidate.id === connection.connectorId);
|
|
784
|
+
const importedEntry = byEntry.get(connection.connectorId);
|
|
785
|
+
if (!connector || !importedEntry) {
|
|
786
|
+
throw new IntegrationError(`Tangle catalog entry ${connection.connectorId} not found.`, "connector_not_found");
|
|
787
|
+
}
|
|
788
|
+
const catalogTrigger = importedEntry.triggers.find((candidate) => candidate.id === trigger2.id);
|
|
789
|
+
return options.subscribeTrigger({
|
|
790
|
+
connection,
|
|
791
|
+
connector,
|
|
792
|
+
catalogEntry: sanitizeEntry(importedEntry),
|
|
793
|
+
trigger: trigger2,
|
|
794
|
+
targetUrl,
|
|
795
|
+
piece: {
|
|
796
|
+
id: importedEntry.id,
|
|
797
|
+
version: importedEntry.version,
|
|
798
|
+
triggerId: trigger2.id,
|
|
799
|
+
upstreamTriggerName: catalogTrigger?.upstreamName
|
|
800
|
+
}
|
|
801
|
+
});
|
|
802
|
+
} : void 0,
|
|
803
|
+
unsubscribeTrigger: options.unsubscribeTrigger,
|
|
804
|
+
normalizeTriggerEvent: options.normalizeTriggerEvent
|
|
805
|
+
});
|
|
806
|
+
}
|
|
807
|
+
var extractExternalCatalogPublicCount = extractActivepiecesPublicPieceCount;
|
|
808
|
+
async function auditTangleIntegrationCatalogFreshness(options = {}) {
|
|
809
|
+
const result = await auditIntegrationCatalogFreshness(options);
|
|
810
|
+
return {
|
|
811
|
+
ok: result.ok,
|
|
812
|
+
generatedAt: result.generatedAt,
|
|
813
|
+
local: {
|
|
814
|
+
catalogEntries: result.local.activepiecesEntries,
|
|
815
|
+
catalogConnectors: result.local.activepiecesConnectors,
|
|
816
|
+
catalogActions: result.local.activepiecesActions,
|
|
817
|
+
catalogTriggers: result.local.activepiecesTriggers,
|
|
818
|
+
executableCatalogConnectors: result.local.executableActivepiecesConnectors,
|
|
819
|
+
executableCatalogActions: result.local.executableActivepiecesActions,
|
|
820
|
+
executableCatalogTriggers: result.local.executableActivepiecesTriggers,
|
|
821
|
+
executableToolDefinitions: result.local.executableToolDefinitions,
|
|
822
|
+
unsupportedExecutableConnectorIds: result.local.unsupportedExecutableConnectorIds,
|
|
823
|
+
registryEntries: result.local.registryEntries,
|
|
824
|
+
registrySummary: result.local.registrySummary,
|
|
825
|
+
conflictSamples: result.local.conflictSamples
|
|
826
|
+
},
|
|
827
|
+
upstream: result.upstream ? {
|
|
828
|
+
externalEntries: result.upstream.activepiecesPieces,
|
|
829
|
+
externalDelta: result.upstream.activepiecesDelta,
|
|
830
|
+
checkedUrl: result.upstream.checkedUrl,
|
|
831
|
+
warning: result.upstream.warning
|
|
832
|
+
} : void 0,
|
|
833
|
+
warnings: result.warnings.map((warning) => warning.replaceAll("Activepieces", "Tangle Integrations Catalog"))
|
|
834
|
+
};
|
|
835
|
+
}
|
|
836
|
+
function sanitizeEntry(entry) {
|
|
837
|
+
return {
|
|
838
|
+
id: entry.id,
|
|
839
|
+
title: entry.title,
|
|
840
|
+
description: entry.description,
|
|
841
|
+
category: entry.category,
|
|
842
|
+
auth: entry.auth,
|
|
843
|
+
authFields: entry.authFields,
|
|
844
|
+
domains: entry.domains.filter((domain) => !domain.toLowerCase().includes("activepieces")),
|
|
845
|
+
actions: entry.actions.map((action) => ({
|
|
846
|
+
id: action.id,
|
|
847
|
+
title: action.title,
|
|
848
|
+
risk: action.risk,
|
|
849
|
+
upstreamName: action.upstreamName
|
|
850
|
+
})),
|
|
851
|
+
triggers: entry.triggers.map((trigger2) => ({
|
|
852
|
+
id: trigger2.id,
|
|
853
|
+
title: trigger2.title,
|
|
854
|
+
upstreamName: trigger2.upstreamName
|
|
855
|
+
}))
|
|
856
|
+
};
|
|
857
|
+
}
|
|
858
|
+
function sanitizeConnector(connector, providerId) {
|
|
859
|
+
const metadata = connector.metadata ?? {};
|
|
860
|
+
return {
|
|
861
|
+
...connector,
|
|
862
|
+
providerId,
|
|
863
|
+
metadata: {
|
|
864
|
+
source: TANGLE_INTEGRATIONS_CATALOG_SOURCE,
|
|
865
|
+
providerId,
|
|
866
|
+
executable: metadata.executable,
|
|
867
|
+
runtime: "tangle-catalog-runtime",
|
|
868
|
+
catalogOnly: metadata.catalogOnly,
|
|
869
|
+
supportTier: metadata.supportTier,
|
|
870
|
+
catalogActionCount: metadata.catalogActionCount,
|
|
871
|
+
catalogTriggerCount: metadata.catalogTriggerCount,
|
|
872
|
+
license: metadata.license,
|
|
873
|
+
version: metadata.version,
|
|
874
|
+
domains: Array.isArray(metadata.domains) ? metadata.domains.filter((domain) => typeof domain === "string" && !domain.toLowerCase().includes("activepieces")) : void 0,
|
|
875
|
+
...metadata.overridden ? { overridden: true } : {}
|
|
876
|
+
}
|
|
877
|
+
};
|
|
878
|
+
}
|
|
879
|
+
|
|
221
880
|
// src/registry.ts
|
|
222
881
|
var DEFAULT_ALIASES = {
|
|
223
882
|
notion: "notion-database",
|
|
@@ -256,26 +915,31 @@ function buildDefaultIntegrationRegistry(options = {}) {
|
|
|
256
915
|
});
|
|
257
916
|
}
|
|
258
917
|
if (includeTangleCatalog) {
|
|
918
|
+
const tangleConnectors = options.tangleCatalogRuntimeExecutable ? buildTangleIntegrationCatalogConnectors({
|
|
919
|
+
providerId: "tangle-catalog",
|
|
920
|
+
includeCatalogActions: true,
|
|
921
|
+
executable: true
|
|
922
|
+
}) : buildActivepiecesConnectors({ providerId: "tangle-catalog" }).map((connector) => ({
|
|
923
|
+
...connector,
|
|
924
|
+
providerId: "tangle-catalog",
|
|
925
|
+
metadata: {
|
|
926
|
+
source: "tangle-integrations-catalog",
|
|
927
|
+
providerId: "tangle-catalog",
|
|
928
|
+
executable: connector.metadata?.executable,
|
|
929
|
+
runtime: "tangle-catalog-runtime",
|
|
930
|
+
catalogOnly: connector.metadata?.catalogOnly,
|
|
931
|
+
supportTier: connector.metadata?.supportTier,
|
|
932
|
+
catalogActionCount: connector.metadata?.catalogActionCount,
|
|
933
|
+
catalogTriggerCount: connector.metadata?.catalogTriggerCount,
|
|
934
|
+
license: connector.metadata?.license,
|
|
935
|
+
version: connector.metadata?.version,
|
|
936
|
+
domains: Array.isArray(connector.metadata?.domains) ? connector.metadata.domains.filter((domain) => typeof domain === "string" && !domain.toLowerCase().includes("activepieces")) : void 0,
|
|
937
|
+
...connector.metadata?.overridden ? { overridden: true } : {}
|
|
938
|
+
}
|
|
939
|
+
}));
|
|
259
940
|
sources.push({
|
|
260
941
|
id: "tangle-catalog",
|
|
261
|
-
connectors:
|
|
262
|
-
...connector,
|
|
263
|
-
providerId: "tangle-catalog",
|
|
264
|
-
metadata: {
|
|
265
|
-
source: "tangle-integrations-catalog",
|
|
266
|
-
providerId: "tangle-catalog",
|
|
267
|
-
executable: connector.metadata?.executable,
|
|
268
|
-
runtime: "tangle-catalog-runtime",
|
|
269
|
-
catalogOnly: connector.metadata?.catalogOnly,
|
|
270
|
-
supportTier: connector.metadata?.supportTier,
|
|
271
|
-
catalogActionCount: connector.metadata?.catalogActionCount,
|
|
272
|
-
catalogTriggerCount: connector.metadata?.catalogTriggerCount,
|
|
273
|
-
license: connector.metadata?.license,
|
|
274
|
-
version: connector.metadata?.version,
|
|
275
|
-
domains: Array.isArray(connector.metadata?.domains) ? connector.metadata.domains.filter((domain) => typeof domain === "string" && !domain.toLowerCase().includes("activepieces")) : void 0,
|
|
276
|
-
...connector.metadata?.overridden ? { overridden: true } : {}
|
|
277
|
-
}
|
|
278
|
-
}))
|
|
942
|
+
connectors: tangleConnectors
|
|
279
943
|
});
|
|
280
944
|
}
|
|
281
945
|
return composeIntegrationRegistry(sources);
|
|
@@ -352,12 +1016,12 @@ function registryEntry(canonicalId, candidates, precedence, aliases) {
|
|
|
352
1016
|
const primary = ordered[0];
|
|
353
1017
|
const actions = mergeActions(ordered);
|
|
354
1018
|
const triggers = mergeTriggers(ordered);
|
|
355
|
-
const scopes =
|
|
1019
|
+
const scopes = unique2(toolBindableCandidates(ordered).flatMap((candidate) => candidate.connector.scopes ?? []));
|
|
356
1020
|
const supportTier = ordered.reduce(
|
|
357
1021
|
(best, candidate) => SUPPORT_RANK[candidate.supportTier] > SUPPORT_RANK[best] ? candidate.supportTier : best,
|
|
358
1022
|
primary.supportTier
|
|
359
1023
|
);
|
|
360
|
-
const aliasesForEntry =
|
|
1024
|
+
const aliasesForEntry = unique2([
|
|
361
1025
|
...ordered.map((candidate) => candidate.connector.id),
|
|
362
1026
|
...Object.entries(aliases).filter(([, target]) => canonicalConnectorId(target, aliases) === canonicalId).map(([alias]) => alias)
|
|
363
1027
|
].map(slug).filter((id) => id && id !== canonicalId)).sort();
|
|
@@ -453,12 +1117,12 @@ function isSupportTier(value) {
|
|
|
453
1117
|
function slug(value) {
|
|
454
1118
|
return value.trim().toLowerCase().replace(/&/g, "and").replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
455
1119
|
}
|
|
456
|
-
function
|
|
1120
|
+
function unique2(values) {
|
|
457
1121
|
return [...new Set(values)];
|
|
458
1122
|
}
|
|
459
1123
|
|
|
460
1124
|
// src/audit.ts
|
|
461
|
-
import { randomUUID } from "crypto";
|
|
1125
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
462
1126
|
var InMemoryIntegrationAuditStore = class {
|
|
463
1127
|
events = [];
|
|
464
1128
|
record(event) {
|
|
@@ -472,7 +1136,7 @@ function createIntegrationAuditEvent(input) {
|
|
|
472
1136
|
const occurredAt = input.occurredAt instanceof Date ? input.occurredAt.toISOString() : input.occurredAt ?? (input.now?.() ?? /* @__PURE__ */ new Date()).toISOString();
|
|
473
1137
|
return {
|
|
474
1138
|
...input,
|
|
475
|
-
id: input.id ?? `audit_${
|
|
1139
|
+
id: input.id ?? `audit_${randomUUID2()}`,
|
|
476
1140
|
occurredAt,
|
|
477
1141
|
metadata: input.metadata ? redactUnknown(input.metadata) : void 0
|
|
478
1142
|
};
|
|
@@ -1121,7 +1785,7 @@ function renderConsentSummary(manifestOrResolution, options = {}) {
|
|
|
1121
1785
|
const appName = options.appName ?? manifest.title ?? manifest.id;
|
|
1122
1786
|
const requirements = manifest.requirements;
|
|
1123
1787
|
const risk = aggregateRisk(requirements, options.connectors);
|
|
1124
|
-
const connectorIds =
|
|
1788
|
+
const connectorIds = unique3(requirements.map((requirement) => requirement.connectorId));
|
|
1125
1789
|
const first = requirements[0];
|
|
1126
1790
|
const body = first ? sentenceForRequirement(appName, first) : `${appName} does not request integrations.`;
|
|
1127
1791
|
return {
|
|
@@ -1184,7 +1848,7 @@ function humanList(values) {
|
|
|
1184
1848
|
function titleize(value) {
|
|
1185
1849
|
return value.split(/[-_.]/g).filter(Boolean).map((part) => part[0].toUpperCase() + part.slice(1)).join(" ");
|
|
1186
1850
|
}
|
|
1187
|
-
function
|
|
1851
|
+
function unique3(values) {
|
|
1188
1852
|
return [...new Set(values)];
|
|
1189
1853
|
}
|
|
1190
1854
|
|
|
@@ -1808,8 +2472,8 @@ function inferIntegrationManifestFromTools(options) {
|
|
|
1808
2472
|
if (existing) {
|
|
1809
2473
|
byConnector.set(id, {
|
|
1810
2474
|
...existing,
|
|
1811
|
-
requiredActions:
|
|
1812
|
-
requiredScopes:
|
|
2475
|
+
requiredActions: unique4([...existing.requiredActions ?? [], action]),
|
|
2476
|
+
requiredScopes: unique4([...existing.requiredScopes ?? [], ...typeof item === "string" ? [] : item.scopes ?? []])
|
|
1813
2477
|
});
|
|
1814
2478
|
} else {
|
|
1815
2479
|
byConnector.set(id, {
|
|
@@ -1863,7 +2527,7 @@ function defaultReason(connectorId, mode) {
|
|
|
1863
2527
|
if (connectorId === "google-calendar" && mode === "write") return "Create or update calendar events after user approval.";
|
|
1864
2528
|
return `${mode === "read" ? "Read from" : mode === "write" ? "Write to" : "Subscribe to"} ${connectorId} for this app.`;
|
|
1865
2529
|
}
|
|
1866
|
-
function
|
|
2530
|
+
function unique4(values) {
|
|
1867
2531
|
return [...new Set(values)];
|
|
1868
2532
|
}
|
|
1869
2533
|
|
|
@@ -1898,7 +2562,7 @@ function validateProviderPassthroughRequest(input, policy) {
|
|
|
1898
2562
|
}
|
|
1899
2563
|
|
|
1900
2564
|
// src/policy.ts
|
|
1901
|
-
import { randomUUID as
|
|
2565
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
1902
2566
|
var StaticIntegrationPolicyEngine = class {
|
|
1903
2567
|
rules;
|
|
1904
2568
|
defaultReadEffect;
|
|
@@ -1941,7 +2605,7 @@ function buildApprovalRequest(ctx, reason, requestedAt) {
|
|
|
1941
2605
|
throw new Error("Cannot build approval request without an action descriptor.");
|
|
1942
2606
|
}
|
|
1943
2607
|
return {
|
|
1944
|
-
id: `approval_${
|
|
2608
|
+
id: `approval_${randomUUID3()}`,
|
|
1945
2609
|
connectionId: ctx.connection.id,
|
|
1946
2610
|
providerId: ctx.connection.providerId,
|
|
1947
2611
|
connectorId: ctx.connection.connectorId,
|
|
@@ -1966,11 +2630,11 @@ function ruleMatches(rule, ctx) {
|
|
|
1966
2630
|
if (rule.connectorId && rule.connectorId !== ctx.connection.connectorId) return false;
|
|
1967
2631
|
if (rule.action && rule.action !== ctx.request.action) return false;
|
|
1968
2632
|
if (rule.risk && rule.risk !== ctx.action.risk) return false;
|
|
1969
|
-
if (rule.maxRisk &&
|
|
2633
|
+
if (rule.maxRisk && riskRank2(ctx.action.risk) > riskRank2(rule.maxRisk)) return false;
|
|
1970
2634
|
if (rule.dataClass && rule.dataClass !== ctx.action.dataClass) return false;
|
|
1971
2635
|
return true;
|
|
1972
2636
|
}
|
|
1973
|
-
function
|
|
2637
|
+
function riskRank2(risk) {
|
|
1974
2638
|
if (risk === "read") return 0;
|
|
1975
2639
|
if (risk === "write") return 1;
|
|
1976
2640
|
return 2;
|
|
@@ -2196,7 +2860,7 @@ function _resetPendingFlowsForTests() {
|
|
|
2196
2860
|
}
|
|
2197
2861
|
|
|
2198
2862
|
// src/connectors/webhooks.ts
|
|
2199
|
-
import { createHmac, timingSafeEqual } from "crypto";
|
|
2863
|
+
import { createHmac as createHmac2, timingSafeEqual as timingSafeEqual2 } from "crypto";
|
|
2200
2864
|
var DEFAULT_SIGNATURE_TOLERANCE_SECONDS = 5 * 60;
|
|
2201
2865
|
function parseStripeSignatureHeader(header) {
|
|
2202
2866
|
const acc = { sigs: [] };
|
|
@@ -2221,12 +2885,12 @@ function verifyStripeSignature(rawBody, signatureHeader, secret, options = {}) {
|
|
|
2221
2885
|
const tolerance = options.toleranceSeconds ?? DEFAULT_SIGNATURE_TOLERANCE_SECONDS;
|
|
2222
2886
|
const now = options.now ?? Math.floor(Date.now() / 1e3);
|
|
2223
2887
|
if (Math.abs(now - parsed.t) > tolerance) return false;
|
|
2224
|
-
const expected =
|
|
2888
|
+
const expected = createHmac2("sha256", secret).update(`${parsed.t}.${rawBody}`).digest("hex");
|
|
2225
2889
|
const expectedBuf = Buffer.from(expected, "utf8");
|
|
2226
2890
|
for (const sig of parsed.sigs) {
|
|
2227
2891
|
const sigBuf = Buffer.from(sig, "utf8");
|
|
2228
2892
|
if (sigBuf.length !== expectedBuf.length) continue;
|
|
2229
|
-
if (
|
|
2893
|
+
if (timingSafeEqual2(sigBuf, expectedBuf)) return true;
|
|
2230
2894
|
}
|
|
2231
2895
|
return false;
|
|
2232
2896
|
}
|
|
@@ -2237,11 +2901,11 @@ function verifySlackSignature(rawBody, signatureHeader, timestampHeader, secret,
|
|
|
2237
2901
|
const tolerance = options.toleranceSeconds ?? DEFAULT_SIGNATURE_TOLERANCE_SECONDS;
|
|
2238
2902
|
const now = options.now ?? Math.floor(Date.now() / 1e3);
|
|
2239
2903
|
if (Math.abs(now - ts) > tolerance) return false;
|
|
2240
|
-
const expected = "v0=" +
|
|
2904
|
+
const expected = "v0=" + createHmac2("sha256", secret).update(`v0:${ts}:${rawBody}`).digest("hex");
|
|
2241
2905
|
const expectedBuf = Buffer.from(expected, "utf8");
|
|
2242
2906
|
const sigBuf = Buffer.from(signatureHeader, "utf8");
|
|
2243
2907
|
if (sigBuf.length !== expectedBuf.length) return false;
|
|
2244
|
-
return
|
|
2908
|
+
return timingSafeEqual2(sigBuf, expectedBuf);
|
|
2245
2909
|
}
|
|
2246
2910
|
function verifyHmacSignature(rawBody, signatureHeader, secret, options = {}) {
|
|
2247
2911
|
const algorithm = options.algorithm ?? "sha256";
|
|
@@ -2253,11 +2917,11 @@ function verifyHmacSignature(rawBody, signatureHeader, secret, options = {}) {
|
|
|
2253
2917
|
candidate = candidate.slice(prefix.length);
|
|
2254
2918
|
}
|
|
2255
2919
|
if (lower) candidate = candidate.toLowerCase();
|
|
2256
|
-
const expected =
|
|
2920
|
+
const expected = createHmac2(algorithm, secret).update(rawBody).digest("hex");
|
|
2257
2921
|
const expectedBuf = Buffer.from(expected, "utf8");
|
|
2258
2922
|
const sigBuf = Buffer.from(candidate, "utf8");
|
|
2259
2923
|
if (sigBuf.length !== expectedBuf.length) return false;
|
|
2260
|
-
return
|
|
2924
|
+
return timingSafeEqual2(sigBuf, expectedBuf);
|
|
2261
2925
|
}
|
|
2262
2926
|
function verifyTwilioSignature(input, options = {}) {
|
|
2263
2927
|
if (!input.authToken) {
|
|
@@ -2267,11 +2931,11 @@ function verifyTwilioSignature(input, options = {}) {
|
|
|
2267
2931
|
if (!signature || Array.isArray(signature)) return false;
|
|
2268
2932
|
if (!input.fullUrl) return false;
|
|
2269
2933
|
const data = options.bodyAsRaw === true ? input.fullUrl + (options.rawBody ?? "") : Object.keys(input.params ?? {}).sort().reduce((acc, key) => acc + key + (input.params[key] ?? ""), input.fullUrl);
|
|
2270
|
-
const expected =
|
|
2934
|
+
const expected = createHmac2("sha1", input.authToken).update(data).digest("base64");
|
|
2271
2935
|
const expectedBuf = Buffer.from(expected);
|
|
2272
2936
|
const sigBuf = Buffer.from(signature);
|
|
2273
2937
|
if (expectedBuf.length !== sigBuf.length) return false;
|
|
2274
|
-
return
|
|
2938
|
+
return timingSafeEqual2(expectedBuf, sigBuf);
|
|
2275
2939
|
}
|
|
2276
2940
|
function firstHeader(headers, name) {
|
|
2277
2941
|
const v = headers[name] ?? headers[name.toLowerCase()] ?? Object.entries(headers).find(([key]) => key.toLowerCase() === name.toLowerCase())?.[1];
|
|
@@ -4633,7 +5297,7 @@ function readApiKey(creds) {
|
|
|
4633
5297
|
}
|
|
4634
5298
|
|
|
4635
5299
|
// src/connectors/adapters/webhook.ts
|
|
4636
|
-
import { createHmac as
|
|
5300
|
+
import { createHmac as createHmac3 } from "crypto";
|
|
4637
5301
|
var webhookConnector = {
|
|
4638
5302
|
manifest: {
|
|
4639
5303
|
kind: "webhook",
|
|
@@ -4747,7 +5411,7 @@ function signHeaders(creds, body, idempotencyKey) {
|
|
|
4747
5411
|
"x-phony-idempotency-key": idempotencyKey
|
|
4748
5412
|
};
|
|
4749
5413
|
if (creds.kind === "hmac" && typeof creds.secret === "string" && creds.secret.length > 0) {
|
|
4750
|
-
const sig =
|
|
5414
|
+
const sig = createHmac3("sha256", creds.secret).update(`${ts}.${body}`).digest("hex");
|
|
4751
5415
|
headers["x-phony-signature"] = `sha256=${sig}`;
|
|
4752
5416
|
}
|
|
4753
5417
|
return headers;
|
|
@@ -5181,187 +5845,36 @@ var salesforceConnector = declarativeRestConnector({
|
|
|
5181
5845
|
required: ["objectName", "recordId"]
|
|
5182
5846
|
},
|
|
5183
5847
|
request: { method: "GET", path: "/services/data/v61.0/sobjects/{objectName}/{recordId}" },
|
|
5184
|
-
requiredScopes: ["api"]
|
|
5185
|
-
},
|
|
5186
|
-
{
|
|
5187
|
-
name: "records.create",
|
|
5188
|
-
class: "mutation",
|
|
5189
|
-
description: "Create a Salesforce sObject record.",
|
|
5190
|
-
parameters: {
|
|
5191
|
-
type: "object",
|
|
5192
|
-
properties: { objectName: { type: "string" }, fields: { type: "object" } },
|
|
5193
|
-
required: ["objectName", "fields"]
|
|
5194
|
-
},
|
|
5195
|
-
request: { method: "POST", path: "/services/data/v61.0/sobjects/{objectName}", body: "{fields}" },
|
|
5196
|
-
cas: "native-idempotency",
|
|
5197
|
-
requiredScopes: ["api"]
|
|
5198
|
-
},
|
|
5199
|
-
{
|
|
5200
|
-
name: "records.update",
|
|
5201
|
-
class: "mutation",
|
|
5202
|
-
description: "Update a Salesforce sObject record.",
|
|
5203
|
-
parameters: {
|
|
5204
|
-
type: "object",
|
|
5205
|
-
properties: { objectName: { type: "string" }, recordId: { type: "string" }, fields: { type: "object" } },
|
|
5206
|
-
required: ["objectName", "recordId", "fields"]
|
|
5207
|
-
},
|
|
5208
|
-
request: { method: "PATCH", path: "/services/data/v61.0/sobjects/{objectName}/{recordId}", body: "{fields}" },
|
|
5209
|
-
cas: "etag-if-match",
|
|
5210
|
-
requiredScopes: ["api"]
|
|
5211
|
-
}
|
|
5212
|
-
]
|
|
5213
|
-
});
|
|
5214
|
-
|
|
5215
|
-
// src/catalog.ts
|
|
5216
|
-
var riskRank2 = {
|
|
5217
|
-
read: 0,
|
|
5218
|
-
write: 1,
|
|
5219
|
-
destructive: 2
|
|
5220
|
-
};
|
|
5221
|
-
function integrationToolName(providerId, connectorId, actionId) {
|
|
5222
|
-
return `int_${encodeToolPart(providerId)}_${encodeToolPart(connectorId)}_${encodeToolPart(actionId)}`;
|
|
5223
|
-
}
|
|
5224
|
-
function parseIntegrationToolName(name) {
|
|
5225
|
-
const parts = name.split("_");
|
|
5226
|
-
if (parts.length !== 4 || parts[0] !== "int") {
|
|
5227
|
-
throw new Error(`Invalid integration tool name: ${name}`);
|
|
5228
|
-
}
|
|
5229
|
-
return {
|
|
5230
|
-
providerId: decodeToolPart(parts[1]),
|
|
5231
|
-
connectorId: decodeToolPart(parts[2]),
|
|
5232
|
-
actionId: decodeToolPart(parts[3])
|
|
5233
|
-
};
|
|
5234
|
-
}
|
|
5235
|
-
function buildIntegrationToolCatalog(connectors) {
|
|
5236
|
-
const tools = [];
|
|
5237
|
-
for (const connector of connectors) {
|
|
5238
|
-
for (const action of connector.actions) {
|
|
5239
|
-
const tags = unique4([
|
|
5240
|
-
connector.id,
|
|
5241
|
-
connector.providerId,
|
|
5242
|
-
connector.title,
|
|
5243
|
-
connector.category,
|
|
5244
|
-
action.id,
|
|
5245
|
-
action.title,
|
|
5246
|
-
action.risk,
|
|
5247
|
-
action.dataClass,
|
|
5248
|
-
...connector.scopes ?? [],
|
|
5249
|
-
...action.requiredScopes ?? []
|
|
5250
|
-
].flatMap(tokenize));
|
|
5251
|
-
tools.push({
|
|
5252
|
-
name: integrationToolName(connector.providerId, connector.id, action.id),
|
|
5253
|
-
title: `${connector.title}: ${action.title}`,
|
|
5254
|
-
description: action.description ?? `${action.risk} action ${action.id} on ${connector.title}`,
|
|
5255
|
-
providerId: connector.providerId,
|
|
5256
|
-
connectorId: connector.id,
|
|
5257
|
-
connectorTitle: connector.title,
|
|
5258
|
-
category: connector.category,
|
|
5259
|
-
action,
|
|
5260
|
-
risk: action.risk,
|
|
5261
|
-
dataClass: action.dataClass,
|
|
5262
|
-
requiredScopes: action.requiredScopes,
|
|
5263
|
-
inputSchema: action.inputSchema,
|
|
5264
|
-
outputSchema: action.outputSchema,
|
|
5265
|
-
tags
|
|
5266
|
-
});
|
|
5267
|
-
}
|
|
5268
|
-
}
|
|
5269
|
-
return tools;
|
|
5270
|
-
}
|
|
5271
|
-
function searchIntegrationTools(catalog, query, filters = {}) {
|
|
5272
|
-
const terms = tokenize(query);
|
|
5273
|
-
const filtered = catalog.filter((tool) => {
|
|
5274
|
-
if (filters.providerId && tool.providerId !== filters.providerId) return false;
|
|
5275
|
-
if (filters.connectorId && tool.connectorId !== filters.connectorId) return false;
|
|
5276
|
-
if (filters.category && tool.category !== filters.category) return false;
|
|
5277
|
-
if (filters.dataClass && tool.dataClass !== filters.dataClass) return false;
|
|
5278
|
-
if (filters.maxRisk && riskRank2[tool.risk] > riskRank2[filters.maxRisk]) return false;
|
|
5279
|
-
return true;
|
|
5280
|
-
});
|
|
5281
|
-
const scored = filtered.map((tool) => scoreTool(tool, terms));
|
|
5282
|
-
return scored.filter((result) => terms.length === 0 || result.score > 0).sort((a, b) => b.score - a.score || a.tool.name.localeCompare(b.tool.name)).slice(0, filters.limit ?? 20);
|
|
5283
|
-
}
|
|
5284
|
-
function toMcpTools(tools) {
|
|
5285
|
-
return tools.map((tool) => ({
|
|
5286
|
-
name: tool.name,
|
|
5287
|
-
description: `${tool.title}. ${tool.description}`,
|
|
5288
|
-
inputSchema: tool.inputSchema ?? {
|
|
5289
|
-
type: "object",
|
|
5290
|
-
additionalProperties: true,
|
|
5291
|
-
properties: {}
|
|
5292
|
-
}
|
|
5293
|
-
}));
|
|
5294
|
-
}
|
|
5295
|
-
function scoreTool(tool, terms) {
|
|
5296
|
-
if (terms.length === 0) return { tool, score: 1, matched: [] };
|
|
5297
|
-
const haystack = new Set(tool.tags);
|
|
5298
|
-
const matched = [];
|
|
5299
|
-
let score = 0;
|
|
5300
|
-
for (const term of terms) {
|
|
5301
|
-
if (haystack.has(term)) {
|
|
5302
|
-
matched.push(term);
|
|
5303
|
-
score += 4;
|
|
5304
|
-
continue;
|
|
5305
|
-
}
|
|
5306
|
-
if (tool.tags.some((tag) => tag.includes(term))) {
|
|
5307
|
-
matched.push(term);
|
|
5308
|
-
score += 1;
|
|
5309
|
-
}
|
|
5310
|
-
}
|
|
5311
|
-
if (tool.risk === "read") score += 0.25;
|
|
5312
|
-
return { tool, score, matched: unique4(matched) };
|
|
5313
|
-
}
|
|
5314
|
-
function tokenize(value) {
|
|
5315
|
-
return value.toLowerCase().split(/[^a-z0-9]+/g).map((part) => part.trim()).filter(Boolean);
|
|
5316
|
-
}
|
|
5317
|
-
function encodeToolPart(value) {
|
|
5318
|
-
return Buffer.from(value, "utf8").toString("base64url").replace(/_/g, ".");
|
|
5319
|
-
}
|
|
5320
|
-
function decodeToolPart(value) {
|
|
5321
|
-
return Buffer.from(value.replace(/\./g, "_"), "base64url").toString("utf8");
|
|
5322
|
-
}
|
|
5323
|
-
function unique4(values) {
|
|
5324
|
-
return [...new Set(values)];
|
|
5325
|
-
}
|
|
5326
|
-
|
|
5327
|
-
// src/catalog-executor.ts
|
|
5328
|
-
function createCatalogExecutorProvider(options) {
|
|
5329
|
-
const byConnector = new Map(options.connectors.map((connector) => [connector.id, connector]));
|
|
5330
|
-
return {
|
|
5331
|
-
id: options.id,
|
|
5332
|
-
kind: options.kind,
|
|
5333
|
-
listConnectors: () => options.connectors,
|
|
5334
|
-
startAuth: options.startAuth,
|
|
5335
|
-
completeAuth: options.completeAuth,
|
|
5336
|
-
async invokeAction(connection, request) {
|
|
5337
|
-
const connector = byConnector.get(connection.connectorId);
|
|
5338
|
-
if (!connector) {
|
|
5339
|
-
throw new IntegrationError(`Connector ${connection.connectorId} not found.`, "connector_not_found");
|
|
5340
|
-
}
|
|
5341
|
-
const action = connector.actions.find((candidate) => candidate.id === request.action);
|
|
5342
|
-
if (!action) {
|
|
5343
|
-
throw new IntegrationError(`Action ${request.action} is not defined by connector ${connector.id}.`, "action_not_found");
|
|
5344
|
-
}
|
|
5345
|
-
return options.executeAction({ connection, request, connector, action });
|
|
5346
|
-
},
|
|
5347
|
-
async subscribeTrigger(connection, triggerId, targetUrl) {
|
|
5348
|
-
if (!options.subscribeTrigger) {
|
|
5349
|
-
throw new IntegrationError(`Provider ${options.id} does not support trigger subscriptions.`, "action_not_found");
|
|
5350
|
-
}
|
|
5351
|
-
const connector = byConnector.get(connection.connectorId);
|
|
5352
|
-
if (!connector) {
|
|
5353
|
-
throw new IntegrationError(`Connector ${connection.connectorId} not found.`, "connector_not_found");
|
|
5354
|
-
}
|
|
5355
|
-
const trigger2 = connector.triggers?.find((candidate) => candidate.id === triggerId);
|
|
5356
|
-
if (!trigger2) {
|
|
5357
|
-
throw new IntegrationError(`Trigger ${triggerId} is not defined by connector ${connector.id}.`, "action_not_found");
|
|
5358
|
-
}
|
|
5359
|
-
return options.subscribeTrigger(connection, trigger2, targetUrl);
|
|
5848
|
+
requiredScopes: ["api"]
|
|
5360
5849
|
},
|
|
5361
|
-
|
|
5362
|
-
|
|
5363
|
-
|
|
5364
|
-
|
|
5850
|
+
{
|
|
5851
|
+
name: "records.create",
|
|
5852
|
+
class: "mutation",
|
|
5853
|
+
description: "Create a Salesforce sObject record.",
|
|
5854
|
+
parameters: {
|
|
5855
|
+
type: "object",
|
|
5856
|
+
properties: { objectName: { type: "string" }, fields: { type: "object" } },
|
|
5857
|
+
required: ["objectName", "fields"]
|
|
5858
|
+
},
|
|
5859
|
+
request: { method: "POST", path: "/services/data/v61.0/sobjects/{objectName}", body: "{fields}" },
|
|
5860
|
+
cas: "native-idempotency",
|
|
5861
|
+
requiredScopes: ["api"]
|
|
5862
|
+
},
|
|
5863
|
+
{
|
|
5864
|
+
name: "records.update",
|
|
5865
|
+
class: "mutation",
|
|
5866
|
+
description: "Update a Salesforce sObject record.",
|
|
5867
|
+
parameters: {
|
|
5868
|
+
type: "object",
|
|
5869
|
+
properties: { objectName: { type: "string" }, recordId: { type: "string" }, fields: { type: "object" } },
|
|
5870
|
+
required: ["objectName", "recordId", "fields"]
|
|
5871
|
+
},
|
|
5872
|
+
request: { method: "PATCH", path: "/services/data/v61.0/sobjects/{objectName}/{recordId}", body: "{fields}" },
|
|
5873
|
+
cas: "etag-if-match",
|
|
5874
|
+
requiredScopes: ["api"]
|
|
5875
|
+
}
|
|
5876
|
+
]
|
|
5877
|
+
});
|
|
5365
5878
|
|
|
5366
5879
|
// src/sandbox.ts
|
|
5367
5880
|
function buildIntegrationInvocationEnvelope(input) {
|
|
@@ -5376,878 +5889,427 @@ function buildIntegrationInvocationEnvelope(input) {
|
|
|
5376
5889
|
dryRun: input.dryRun,
|
|
5377
5890
|
metadata: input.metadata
|
|
5378
5891
|
};
|
|
5379
|
-
validateIntegrationInvocationEnvelope(envelope);
|
|
5380
|
-
return envelope;
|
|
5381
|
-
}
|
|
5382
|
-
function invocationRequestFromEnvelope(envelope) {
|
|
5383
|
-
validateIntegrationInvocationEnvelope(envelope);
|
|
5384
|
-
return {
|
|
5385
|
-
action: envelope.action,
|
|
5386
|
-
input: envelope.input,
|
|
5387
|
-
idempotencyKey: envelope.idempotencyKey,
|
|
5388
|
-
dryRun: envelope.dryRun,
|
|
5389
|
-
metadata: envelope.metadata
|
|
5390
|
-
};
|
|
5391
|
-
}
|
|
5392
|
-
function validateIntegrationInvocationEnvelope(envelope, options = {}) {
|
|
5393
|
-
if (!envelope || typeof envelope !== "object") throw new Error("Integration invocation envelope is required.");
|
|
5394
|
-
if (envelope.kind !== "integration.invocation") throw new Error("Invalid integration invocation envelope kind.");
|
|
5395
|
-
if (!isNonEmptyString(envelope.capabilityToken)) throw new Error("Integration invocation envelope is missing capabilityToken.");
|
|
5396
|
-
if (!isNonEmptyString(envelope.toolName)) throw new Error("Integration invocation envelope is missing toolName.");
|
|
5397
|
-
if (!isNonEmptyString(envelope.action)) throw new Error("Integration invocation envelope is missing action.");
|
|
5398
|
-
if (!isNonEmptyString(envelope.idempotencyKey)) throw new Error("Integration invocation envelope is missing idempotencyKey.");
|
|
5399
|
-
if (envelope.metadata !== void 0 && !isPlainRecord(envelope.metadata)) {
|
|
5400
|
-
throw new Error("Integration invocation envelope metadata must be an object.");
|
|
5401
|
-
}
|
|
5402
|
-
const parsed = parseIntegrationToolName(envelope.toolName);
|
|
5403
|
-
if (parsed.actionId !== envelope.action) {
|
|
5404
|
-
throw new Error(`Integration invocation action ${envelope.action} does not match tool ${parsed.actionId}.`);
|
|
5405
|
-
}
|
|
5406
|
-
const inputBytes = Buffer.byteLength(JSON.stringify(envelope.input ?? null), "utf8");
|
|
5407
|
-
const maxInputBytes = options.maxInputBytes ?? 256 * 1024;
|
|
5408
|
-
if (inputBytes > maxInputBytes) {
|
|
5409
|
-
throw new Error(`Integration invocation input exceeds ${maxInputBytes} bytes.`);
|
|
5410
|
-
}
|
|
5411
|
-
if (options.requireKnownTool || options.connectors) {
|
|
5412
|
-
if (!options.connectors) throw new Error("connectors are required when requireKnownTool is true.");
|
|
5413
|
-
const connector = options.connectors.find(
|
|
5414
|
-
(candidate) => candidate.providerId === parsed.providerId && candidate.id === parsed.connectorId
|
|
5415
|
-
);
|
|
5416
|
-
const action = connector?.actions.find((candidate) => candidate.id === parsed.actionId);
|
|
5417
|
-
if (!connector || !action) throw new Error(`Unknown integration tool ${envelope.toolName}.`);
|
|
5418
|
-
}
|
|
5419
|
-
}
|
|
5420
|
-
function redactInvocationEnvelope(envelope) {
|
|
5421
|
-
return {
|
|
5422
|
-
...envelope,
|
|
5423
|
-
capabilityToken: "[REDACTED]",
|
|
5424
|
-
input: redactUnknown4(envelope.input)
|
|
5425
|
-
};
|
|
5426
|
-
}
|
|
5427
|
-
function redactCapability(capability) {
|
|
5428
|
-
return {
|
|
5429
|
-
...capability,
|
|
5430
|
-
metadata: redactUnknown4(capability.metadata)
|
|
5431
|
-
};
|
|
5432
|
-
}
|
|
5433
|
-
function normalizeIntegrationResult(result) {
|
|
5434
|
-
const output = result.output;
|
|
5435
|
-
if (!result.ok && output?.approvalRequired === true && output.approval) {
|
|
5436
|
-
return {
|
|
5437
|
-
status: "approval_required",
|
|
5438
|
-
action: result.action,
|
|
5439
|
-
approval: output.approval,
|
|
5440
|
-
metadata: result.metadata
|
|
5441
|
-
};
|
|
5442
|
-
}
|
|
5443
|
-
if (!result.ok) {
|
|
5444
|
-
return {
|
|
5445
|
-
status: "failed",
|
|
5446
|
-
action: result.action,
|
|
5447
|
-
error: String(result.output ?? result.warnings?.[0] ?? "integration action failed"),
|
|
5448
|
-
metadata: result.metadata
|
|
5449
|
-
};
|
|
5450
|
-
}
|
|
5451
|
-
return {
|
|
5452
|
-
status: "ok",
|
|
5453
|
-
action: result.action,
|
|
5454
|
-
output: result.output,
|
|
5455
|
-
metadata: result.metadata
|
|
5456
|
-
};
|
|
5457
|
-
}
|
|
5458
|
-
async function dispatchIntegrationInvocation(envelope, options) {
|
|
5459
|
-
try {
|
|
5460
|
-
validateIntegrationInvocationEnvelope(envelope, options);
|
|
5461
|
-
const result = await options.hub.invokeWithCapability(
|
|
5462
|
-
envelope.capabilityToken,
|
|
5463
|
-
invocationRequestFromEnvelope(envelope)
|
|
5464
|
-
);
|
|
5465
|
-
return normalizeIntegrationResult(result);
|
|
5466
|
-
} catch (error) {
|
|
5467
|
-
return {
|
|
5468
|
-
status: "failed",
|
|
5469
|
-
action: typeof envelope?.action === "string" ? envelope.action : "unknown",
|
|
5470
|
-
error: error instanceof Error ? error.message : "Integration invocation failed."
|
|
5471
|
-
};
|
|
5472
|
-
}
|
|
5473
|
-
}
|
|
5474
|
-
var IntegrationSandboxHost = class {
|
|
5475
|
-
options;
|
|
5476
|
-
constructor(options) {
|
|
5477
|
-
this.options = options;
|
|
5478
|
-
}
|
|
5479
|
-
dispatch(envelope) {
|
|
5480
|
-
return dispatchIntegrationInvocation(envelope, this.options);
|
|
5481
|
-
}
|
|
5482
|
-
};
|
|
5483
|
-
function redactUnknown4(value) {
|
|
5484
|
-
if (Array.isArray(value)) return value.map(redactUnknown4);
|
|
5485
|
-
if (!value || typeof value !== "object") return value;
|
|
5486
|
-
const out = {};
|
|
5487
|
-
for (const [key, child] of Object.entries(value)) {
|
|
5488
|
-
if (/token|secret|password|authorization|api[_-]?key|credential/i.test(key)) {
|
|
5489
|
-
out[key] = "[REDACTED]";
|
|
5490
|
-
} else {
|
|
5491
|
-
out[key] = redactUnknown4(child);
|
|
5492
|
-
}
|
|
5493
|
-
}
|
|
5494
|
-
return out;
|
|
5495
|
-
}
|
|
5496
|
-
function isNonEmptyString(value) {
|
|
5497
|
-
return typeof value === "string" && value.trim().length > 0;
|
|
5498
|
-
}
|
|
5499
|
-
function isPlainRecord(value) {
|
|
5500
|
-
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
5501
|
-
}
|
|
5502
|
-
|
|
5503
|
-
// src/importers.ts
|
|
5504
|
-
var HTTP_METHODS = /* @__PURE__ */ new Set(["get", "post", "put", "patch", "delete"]);
|
|
5505
|
-
function importOpenApiConnector(document, options) {
|
|
5506
|
-
const actions = [];
|
|
5507
|
-
for (const [path, methods] of Object.entries(document.paths ?? {})) {
|
|
5508
|
-
for (const [method, rawOperation] of Object.entries(methods)) {
|
|
5509
|
-
const normalizedMethod = method.toLowerCase();
|
|
5510
|
-
if (!HTTP_METHODS.has(normalizedMethod) || !isObject(rawOperation)) continue;
|
|
5511
|
-
const operation = rawOperation;
|
|
5512
|
-
const operationId = operation.operationId ?? `${normalizedMethod}_${path.replace(/[^a-zA-Z0-9]+/g, "_").replace(/^_+|_+$/g, "")}`;
|
|
5513
|
-
actions.push({
|
|
5514
|
-
id: operationId,
|
|
5515
|
-
title: operation.summary ?? titleFromId(operationId),
|
|
5516
|
-
risk: riskFromHttpMethod(normalizedMethod, operation, options.defaultRisk),
|
|
5517
|
-
requiredScopes: scopesFromOpenApiOperation(operation, options.scopes ?? []),
|
|
5518
|
-
dataClass: options.dataClass ?? "private",
|
|
5519
|
-
description: operation.description ?? operation.summary ?? `${normalizedMethod.toUpperCase()} ${path}`,
|
|
5520
|
-
approvalRequired: riskFromHttpMethod(normalizedMethod, operation, options.defaultRisk) !== "read",
|
|
5521
|
-
inputSchema: openApiInputSchema(path, normalizedMethod, operation),
|
|
5522
|
-
outputSchema: operation.responses
|
|
5523
|
-
});
|
|
5524
|
-
}
|
|
5525
|
-
}
|
|
5526
|
-
return connectorFromActions(options, actions);
|
|
5527
|
-
}
|
|
5528
|
-
function importGraphqlConnector(operations, options) {
|
|
5529
|
-
return connectorFromActions(options, operations.map((operation) => ({
|
|
5530
|
-
id: operation.name,
|
|
5531
|
-
title: titleFromId(operation.name),
|
|
5532
|
-
risk: operation.kind === "query" ? "read" : options.defaultRisk ?? "write",
|
|
5533
|
-
requiredScopes: operation.requiredScopes ?? options.scopes ?? [],
|
|
5534
|
-
dataClass: options.dataClass ?? "private",
|
|
5535
|
-
description: operation.description,
|
|
5536
|
-
approvalRequired: operation.kind === "mutation",
|
|
5537
|
-
inputSchema: operation.inputSchema,
|
|
5538
|
-
outputSchema: operation.outputSchema
|
|
5539
|
-
})));
|
|
5540
|
-
}
|
|
5541
|
-
function importMcpConnector(catalog, options) {
|
|
5542
|
-
return connectorFromActions(options, catalog.tools.map((tool) => {
|
|
5543
|
-
const risk = riskFromMcpTool(tool, options.defaultRisk);
|
|
5544
|
-
return {
|
|
5545
|
-
id: tool.name,
|
|
5546
|
-
title: tool.annotations?.title ?? titleFromId(tool.name),
|
|
5547
|
-
risk,
|
|
5548
|
-
requiredScopes: options.scopes ?? [],
|
|
5549
|
-
dataClass: options.dataClass ?? "private",
|
|
5550
|
-
description: tool.description,
|
|
5551
|
-
approvalRequired: risk !== "read",
|
|
5552
|
-
inputSchema: tool.inputSchema
|
|
5553
|
-
};
|
|
5554
|
-
}));
|
|
5555
|
-
}
|
|
5556
|
-
function connectorFromActions(options, actions) {
|
|
5557
|
-
const scopes = unique5([
|
|
5558
|
-
...options.scopes ?? [],
|
|
5559
|
-
...actions.flatMap((action) => action.requiredScopes)
|
|
5560
|
-
]);
|
|
5561
|
-
return {
|
|
5562
|
-
id: options.connectorId,
|
|
5563
|
-
providerId: options.providerId,
|
|
5564
|
-
title: options.connectorTitle,
|
|
5565
|
-
category: options.category ?? "other",
|
|
5566
|
-
auth: options.auth ?? "custom",
|
|
5567
|
-
scopes,
|
|
5568
|
-
actions,
|
|
5569
|
-
metadata: { source: "catalog-importer" }
|
|
5570
|
-
};
|
|
5571
|
-
}
|
|
5572
|
-
function riskFromHttpMethod(method, operation, fallback) {
|
|
5573
|
-
if (method === "get") return "read";
|
|
5574
|
-
if (method === "delete") return "destructive";
|
|
5575
|
-
const text = `${operation.operationId ?? ""} ${operation.summary ?? ""} ${operation.description ?? ""}`.toLowerCase();
|
|
5576
|
-
if (/\b(delete|remove|destroy|cancel|void|revoke|drop)\b/.test(text)) return "destructive";
|
|
5577
|
-
return fallback && fallback !== "read" ? fallback : "write";
|
|
5578
|
-
}
|
|
5579
|
-
function riskFromMcpTool(tool, fallback) {
|
|
5580
|
-
if (tool.annotations?.destructiveHint) return "destructive";
|
|
5581
|
-
if (tool.annotations?.readOnlyHint) return "read";
|
|
5582
|
-
const text = `${tool.name} ${tool.description ?? ""}`.toLowerCase();
|
|
5583
|
-
if (/\b(delete|remove|destroy|cancel|void|revoke|drop)\b/.test(text)) return "destructive";
|
|
5584
|
-
if (/\b(get|list|read|search|find|fetch|query)\b/.test(text)) return "read";
|
|
5585
|
-
return fallback ?? "write";
|
|
5586
|
-
}
|
|
5587
|
-
function scopesFromOpenApiOperation(operation, fallback) {
|
|
5588
|
-
const scopes = (operation.security ?? []).flatMap((entry) => Object.values(entry).flat());
|
|
5589
|
-
return unique5(scopes.length > 0 ? scopes : fallback);
|
|
5590
|
-
}
|
|
5591
|
-
function openApiInputSchema(path, method, operation) {
|
|
5592
|
-
return {
|
|
5593
|
-
type: "object",
|
|
5594
|
-
additionalProperties: true,
|
|
5595
|
-
properties: {
|
|
5596
|
-
path: { const: path },
|
|
5597
|
-
method: { const: method.toUpperCase() },
|
|
5598
|
-
parameters: { type: "object", additionalProperties: true },
|
|
5599
|
-
body: operation.requestBody ?? { type: "object", additionalProperties: true }
|
|
5600
|
-
}
|
|
5601
|
-
};
|
|
5602
|
-
}
|
|
5603
|
-
function titleFromId(id) {
|
|
5604
|
-
return id.replace(/([a-z])([A-Z])/g, "$1 $2").split(/[^a-zA-Z0-9]+/g).filter(Boolean).map((part) => part.slice(0, 1).toUpperCase() + part.slice(1)).join(" ");
|
|
5605
|
-
}
|
|
5606
|
-
function isObject(value) {
|
|
5607
|
-
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
5892
|
+
validateIntegrationInvocationEnvelope(envelope);
|
|
5893
|
+
return envelope;
|
|
5608
5894
|
}
|
|
5609
|
-
function
|
|
5610
|
-
|
|
5895
|
+
function invocationRequestFromEnvelope(envelope) {
|
|
5896
|
+
validateIntegrationInvocationEnvelope(envelope);
|
|
5897
|
+
return {
|
|
5898
|
+
action: envelope.action,
|
|
5899
|
+
input: envelope.input,
|
|
5900
|
+
idempotencyKey: envelope.idempotencyKey,
|
|
5901
|
+
dryRun: envelope.dryRun,
|
|
5902
|
+
metadata: envelope.metadata
|
|
5903
|
+
};
|
|
5611
5904
|
}
|
|
5612
|
-
|
|
5613
|
-
|
|
5614
|
-
|
|
5615
|
-
|
|
5616
|
-
|
|
5617
|
-
|
|
5618
|
-
|
|
5619
|
-
|
|
5620
|
-
|
|
5621
|
-
|
|
5622
|
-
|
|
5623
|
-
|
|
5624
|
-
|
|
5625
|
-
|
|
5626
|
-
|
|
5627
|
-
|
|
5628
|
-
|
|
5905
|
+
function validateIntegrationInvocationEnvelope(envelope, options = {}) {
|
|
5906
|
+
if (!envelope || typeof envelope !== "object") throw new Error("Integration invocation envelope is required.");
|
|
5907
|
+
if (envelope.kind !== "integration.invocation") throw new Error("Invalid integration invocation envelope kind.");
|
|
5908
|
+
if (!isNonEmptyString(envelope.capabilityToken)) throw new Error("Integration invocation envelope is missing capabilityToken.");
|
|
5909
|
+
if (!isNonEmptyString(envelope.toolName)) throw new Error("Integration invocation envelope is missing toolName.");
|
|
5910
|
+
if (!isNonEmptyString(envelope.action)) throw new Error("Integration invocation envelope is missing action.");
|
|
5911
|
+
if (!isNonEmptyString(envelope.idempotencyKey)) throw new Error("Integration invocation envelope is missing idempotencyKey.");
|
|
5912
|
+
if (envelope.metadata !== void 0 && !isPlainRecord(envelope.metadata)) {
|
|
5913
|
+
throw new Error("Integration invocation envelope metadata must be an object.");
|
|
5914
|
+
}
|
|
5915
|
+
const parsed = parseIntegrationToolName(envelope.toolName);
|
|
5916
|
+
if (parsed.actionId !== envelope.action) {
|
|
5917
|
+
throw new Error(`Integration invocation action ${envelope.action} does not match tool ${parsed.actionId}.`);
|
|
5918
|
+
}
|
|
5919
|
+
const inputBytes = Buffer.byteLength(JSON.stringify(envelope.input ?? null), "utf8");
|
|
5920
|
+
const maxInputBytes = options.maxInputBytes ?? 256 * 1024;
|
|
5921
|
+
if (inputBytes > maxInputBytes) {
|
|
5922
|
+
throw new Error(`Integration invocation input exceeds ${maxInputBytes} bytes.`);
|
|
5923
|
+
}
|
|
5924
|
+
if (options.requireKnownTool || options.connectors) {
|
|
5925
|
+
if (!options.connectors) throw new Error("connectors are required when requireKnownTool is true.");
|
|
5926
|
+
const connector = options.connectors.find(
|
|
5927
|
+
(candidate) => candidate.providerId === parsed.providerId && candidate.id === parsed.connectorId
|
|
5928
|
+
);
|
|
5929
|
+
const action = connector?.actions.find((candidate) => candidate.id === parsed.actionId);
|
|
5930
|
+
if (!connector || !action) throw new Error(`Unknown integration tool ${envelope.toolName}.`);
|
|
5629
5931
|
}
|
|
5932
|
+
}
|
|
5933
|
+
function redactInvocationEnvelope(envelope) {
|
|
5630
5934
|
return {
|
|
5631
|
-
|
|
5632
|
-
|
|
5633
|
-
|
|
5634
|
-
startAuth: options.startAuth,
|
|
5635
|
-
completeAuth: options.completeAuth,
|
|
5636
|
-
async invokeAction(connection, request) {
|
|
5637
|
-
if (!options.invokeAction) {
|
|
5638
|
-
throw new IntegrationError(`Gateway provider ${options.id} does not implement action invocation.`, "action_not_found");
|
|
5639
|
-
}
|
|
5640
|
-
await assertKnownGatewayAction(await listConnectors(), connection.connectorId, request.action);
|
|
5641
|
-
return options.invokeAction(connection, request);
|
|
5642
|
-
}
|
|
5935
|
+
...envelope,
|
|
5936
|
+
capabilityToken: "[REDACTED]",
|
|
5937
|
+
input: redactUnknown4(envelope.input)
|
|
5643
5938
|
};
|
|
5644
5939
|
}
|
|
5645
|
-
function
|
|
5646
|
-
|
|
5647
|
-
|
|
5648
|
-
|
|
5649
|
-
|
|
5650
|
-
if (!id || seen.has(id)) continue;
|
|
5651
|
-
seen.add(id);
|
|
5652
|
-
const title = entry.title ?? entry.name ?? titleFromId2(id);
|
|
5653
|
-
const actions = normalizeActions(entry.actions ?? [], entry.scopes ?? []);
|
|
5654
|
-
out.push({
|
|
5655
|
-
id,
|
|
5656
|
-
providerId: options.providerId,
|
|
5657
|
-
title,
|
|
5658
|
-
category: normalizeCategory(entry.category),
|
|
5659
|
-
auth: normalizeAuth(entry.auth),
|
|
5660
|
-
scopes: unique6([
|
|
5661
|
-
...entry.scopes ?? [],
|
|
5662
|
-
...actions.flatMap((action) => action.requiredScopes)
|
|
5663
|
-
]),
|
|
5664
|
-
actions: actions.length > 0 ? actions : defaultActionsFor(entry.category, entry.scopes ?? []),
|
|
5665
|
-
triggers: normalizeTriggers(entry.triggers ?? [], entry.scopes ?? []),
|
|
5666
|
-
metadata: {
|
|
5667
|
-
...entry.metadata ?? {},
|
|
5668
|
-
source: "gateway-catalog",
|
|
5669
|
-
providerKind: options.providerKind,
|
|
5670
|
-
executable: true
|
|
5671
|
-
}
|
|
5672
|
-
});
|
|
5673
|
-
}
|
|
5674
|
-
return out;
|
|
5940
|
+
function redactCapability(capability) {
|
|
5941
|
+
return {
|
|
5942
|
+
...capability,
|
|
5943
|
+
metadata: redactUnknown4(capability.metadata)
|
|
5944
|
+
};
|
|
5675
5945
|
}
|
|
5676
|
-
|
|
5677
|
-
const
|
|
5678
|
-
if (!
|
|
5679
|
-
|
|
5680
|
-
|
|
5946
|
+
function normalizeIntegrationResult(result) {
|
|
5947
|
+
const output = result.output;
|
|
5948
|
+
if (!result.ok && output?.approvalRequired === true && output.approval) {
|
|
5949
|
+
return {
|
|
5950
|
+
status: "approval_required",
|
|
5951
|
+
action: result.action,
|
|
5952
|
+
approval: output.approval,
|
|
5953
|
+
metadata: result.metadata
|
|
5954
|
+
};
|
|
5681
5955
|
}
|
|
5682
|
-
|
|
5683
|
-
function normalizeActions(actions, fallbackScopes) {
|
|
5684
|
-
return actions.map((action) => {
|
|
5685
|
-
const id = slug2(action.id ?? action.key ?? action.name ?? action.title ?? "");
|
|
5956
|
+
if (!result.ok) {
|
|
5686
5957
|
return {
|
|
5687
|
-
|
|
5688
|
-
|
|
5689
|
-
|
|
5690
|
-
|
|
5691
|
-
...action.requiredScopes ?? [],
|
|
5692
|
-
...action.scopes ?? [],
|
|
5693
|
-
...action.requiredScopes?.length || action.scopes?.length ? [] : fallbackScopes
|
|
5694
|
-
]),
|
|
5695
|
-
dataClass: normalizeDataClass(action.dataClass),
|
|
5696
|
-
description: action.description,
|
|
5697
|
-
approvalRequired: action.approvalRequired ?? normalizeRisk(action.risk) !== "read",
|
|
5698
|
-
inputSchema: action.inputSchema,
|
|
5699
|
-
outputSchema: action.outputSchema
|
|
5958
|
+
status: "failed",
|
|
5959
|
+
action: result.action,
|
|
5960
|
+
error: String(result.output ?? result.warnings?.[0] ?? "integration action failed"),
|
|
5961
|
+
metadata: result.metadata
|
|
5700
5962
|
};
|
|
5701
|
-
}
|
|
5963
|
+
}
|
|
5964
|
+
return {
|
|
5965
|
+
status: "ok",
|
|
5966
|
+
action: result.action,
|
|
5967
|
+
output: result.output,
|
|
5968
|
+
metadata: result.metadata
|
|
5969
|
+
};
|
|
5702
5970
|
}
|
|
5703
|
-
function
|
|
5704
|
-
|
|
5705
|
-
|
|
5971
|
+
async function dispatchIntegrationInvocation(envelope, options) {
|
|
5972
|
+
try {
|
|
5973
|
+
validateIntegrationInvocationEnvelope(envelope, options);
|
|
5974
|
+
const result = await options.hub.invokeWithCapability(
|
|
5975
|
+
envelope.capabilityToken,
|
|
5976
|
+
invocationRequestFromEnvelope(envelope)
|
|
5977
|
+
);
|
|
5978
|
+
return normalizeIntegrationResult(result);
|
|
5979
|
+
} catch (error) {
|
|
5706
5980
|
return {
|
|
5707
|
-
|
|
5708
|
-
|
|
5709
|
-
|
|
5710
|
-
...trigger2.requiredScopes ?? [],
|
|
5711
|
-
...trigger2.scopes ?? [],
|
|
5712
|
-
...trigger2.requiredScopes?.length || trigger2.scopes?.length ? [] : fallbackScopes
|
|
5713
|
-
]),
|
|
5714
|
-
dataClass: normalizeDataClass(trigger2.dataClass),
|
|
5715
|
-
description: trigger2.description,
|
|
5716
|
-
payloadSchema: trigger2.payloadSchema
|
|
5981
|
+
status: "failed",
|
|
5982
|
+
action: typeof envelope?.action === "string" ? envelope.action : "unknown",
|
|
5983
|
+
error: error instanceof Error ? error.message : "Integration invocation failed."
|
|
5717
5984
|
};
|
|
5718
|
-
}
|
|
5719
|
-
return normalized.length > 0 ? normalized : void 0;
|
|
5720
|
-
}
|
|
5721
|
-
function defaultActionsFor(category, scopes) {
|
|
5722
|
-
const readScope = scopes.find((scope) => scope.endsWith(".read")) ?? scopes[0];
|
|
5723
|
-
const writeScope = scopes.find((scope) => scope.endsWith(".write")) ?? scopes[1] ?? readScope;
|
|
5724
|
-
const requiredRead = readScope ? [readScope] : [];
|
|
5725
|
-
const requiredWrite = writeScope ? [writeScope] : [];
|
|
5726
|
-
const dataClass = normalizeDataClass(category === "finance" || category === "commerce" || category === "hr" ? "sensitive" : "private");
|
|
5727
|
-
return [
|
|
5728
|
-
{
|
|
5729
|
-
id: "records.search",
|
|
5730
|
-
title: "Search records",
|
|
5731
|
-
risk: "read",
|
|
5732
|
-
requiredScopes: requiredRead,
|
|
5733
|
-
dataClass,
|
|
5734
|
-
description: "Search provider records."
|
|
5735
|
-
},
|
|
5736
|
-
{
|
|
5737
|
-
id: "records.read",
|
|
5738
|
-
title: "Read record",
|
|
5739
|
-
risk: "read",
|
|
5740
|
-
requiredScopes: requiredRead,
|
|
5741
|
-
dataClass,
|
|
5742
|
-
description: "Read a provider record."
|
|
5743
|
-
},
|
|
5744
|
-
{
|
|
5745
|
-
id: "records.upsert",
|
|
5746
|
-
title: "Upsert record",
|
|
5747
|
-
risk: "write",
|
|
5748
|
-
requiredScopes: requiredWrite,
|
|
5749
|
-
dataClass,
|
|
5750
|
-
approvalRequired: true,
|
|
5751
|
-
description: "Create or update a provider record."
|
|
5752
|
-
}
|
|
5753
|
-
];
|
|
5754
|
-
}
|
|
5755
|
-
function normalizeCategory(category) {
|
|
5756
|
-
const value = slug2(category ?? "");
|
|
5757
|
-
if (value === "mail") return "email";
|
|
5758
|
-
if (value === "messaging" || value === "communication" || value === "communications") return "chat";
|
|
5759
|
-
if (value === "file" || value === "files") return "storage";
|
|
5760
|
-
if (value === "project-management" || value === "automation") return "workflow";
|
|
5761
|
-
if (value === "developer" || value === "devops") return "workflow";
|
|
5762
|
-
if (value === "support") return "crm";
|
|
5763
|
-
if ([
|
|
5764
|
-
"email",
|
|
5765
|
-
"calendar",
|
|
5766
|
-
"chat",
|
|
5767
|
-
"crm",
|
|
5768
|
-
"storage",
|
|
5769
|
-
"docs",
|
|
5770
|
-
"database",
|
|
5771
|
-
"webhook",
|
|
5772
|
-
"workflow",
|
|
5773
|
-
"internal",
|
|
5774
|
-
"other"
|
|
5775
|
-
].includes(value)) return value;
|
|
5776
|
-
return "other";
|
|
5777
|
-
}
|
|
5778
|
-
function normalizeAuth(auth) {
|
|
5779
|
-
if (auth === "oauth2") return "oauth2";
|
|
5780
|
-
if (auth === "api_key" || auth === "api-key" || auth === "apikey") return "api_key";
|
|
5781
|
-
if (auth === "none") return "none";
|
|
5782
|
-
return "custom";
|
|
5783
|
-
}
|
|
5784
|
-
function normalizeRisk(risk) {
|
|
5785
|
-
if (risk === "read" || risk === "write" || risk === "destructive") return risk;
|
|
5786
|
-
return "write";
|
|
5787
|
-
}
|
|
5788
|
-
function normalizeDataClass(dataClass) {
|
|
5789
|
-
if (dataClass === "public" || dataClass === "internal" || dataClass === "private" || dataClass === "sensitive" || dataClass === "secret") return dataClass;
|
|
5790
|
-
return "private";
|
|
5985
|
+
}
|
|
5791
5986
|
}
|
|
5792
|
-
|
|
5793
|
-
|
|
5987
|
+
var IntegrationSandboxHost = class {
|
|
5988
|
+
options;
|
|
5989
|
+
constructor(options) {
|
|
5990
|
+
this.options = options;
|
|
5991
|
+
}
|
|
5992
|
+
dispatch(envelope) {
|
|
5993
|
+
return dispatchIntegrationInvocation(envelope, this.options);
|
|
5994
|
+
}
|
|
5995
|
+
};
|
|
5996
|
+
function redactUnknown4(value) {
|
|
5997
|
+
if (Array.isArray(value)) return value.map(redactUnknown4);
|
|
5998
|
+
if (!value || typeof value !== "object") return value;
|
|
5999
|
+
const out = {};
|
|
6000
|
+
for (const [key, child] of Object.entries(value)) {
|
|
6001
|
+
if (/token|secret|password|authorization|api[_-]?key|credential/i.test(key)) {
|
|
6002
|
+
out[key] = "[REDACTED]";
|
|
6003
|
+
} else {
|
|
6004
|
+
out[key] = redactUnknown4(child);
|
|
6005
|
+
}
|
|
6006
|
+
}
|
|
6007
|
+
return out;
|
|
5794
6008
|
}
|
|
5795
|
-
function
|
|
5796
|
-
return
|
|
6009
|
+
function isNonEmptyString(value) {
|
|
6010
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
5797
6011
|
}
|
|
5798
|
-
function
|
|
5799
|
-
return
|
|
6012
|
+
function isPlainRecord(value) {
|
|
6013
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
5800
6014
|
}
|
|
5801
6015
|
|
|
5802
|
-
// src/
|
|
5803
|
-
|
|
5804
|
-
|
|
5805
|
-
const
|
|
5806
|
-
|
|
5807
|
-
|
|
5808
|
-
|
|
5809
|
-
|
|
5810
|
-
|
|
5811
|
-
|
|
5812
|
-
|
|
5813
|
-
|
|
5814
|
-
|
|
5815
|
-
|
|
5816
|
-
|
|
5817
|
-
|
|
5818
|
-
|
|
5819
|
-
|
|
5820
|
-
|
|
5821
|
-
|
|
5822
|
-
const catalogAction = catalogEntry.actions.find((candidate) => candidate.id === action.id);
|
|
5823
|
-
return options.executeAction({
|
|
5824
|
-
connection,
|
|
5825
|
-
request,
|
|
5826
|
-
connector,
|
|
5827
|
-
catalogEntry,
|
|
5828
|
-
piece: {
|
|
5829
|
-
id: catalogEntry.id,
|
|
5830
|
-
npmPackage: catalogEntry.npmPackage,
|
|
5831
|
-
version: catalogEntry.version,
|
|
5832
|
-
actionId: action.id,
|
|
5833
|
-
upstreamActionName: catalogAction?.upstreamName
|
|
5834
|
-
}
|
|
6016
|
+
// src/importers.ts
|
|
6017
|
+
var HTTP_METHODS = /* @__PURE__ */ new Set(["get", "post", "put", "patch", "delete"]);
|
|
6018
|
+
function importOpenApiConnector(document, options) {
|
|
6019
|
+
const actions = [];
|
|
6020
|
+
for (const [path, methods] of Object.entries(document.paths ?? {})) {
|
|
6021
|
+
for (const [method, rawOperation] of Object.entries(methods)) {
|
|
6022
|
+
const normalizedMethod = method.toLowerCase();
|
|
6023
|
+
if (!HTTP_METHODS.has(normalizedMethod) || !isObject(rawOperation)) continue;
|
|
6024
|
+
const operation = rawOperation;
|
|
6025
|
+
const operationId = operation.operationId ?? `${normalizedMethod}_${path.replace(/[^a-zA-Z0-9]+/g, "_").replace(/^_+|_+$/g, "")}`;
|
|
6026
|
+
actions.push({
|
|
6027
|
+
id: operationId,
|
|
6028
|
+
title: operation.summary ?? titleFromId(operationId),
|
|
6029
|
+
risk: riskFromHttpMethod(normalizedMethod, operation, options.defaultRisk),
|
|
6030
|
+
requiredScopes: scopesFromOpenApiOperation(operation, options.scopes ?? []),
|
|
6031
|
+
dataClass: options.dataClass ?? "private",
|
|
6032
|
+
description: operation.description ?? operation.summary ?? `${normalizedMethod.toUpperCase()} ${path}`,
|
|
6033
|
+
approvalRequired: riskFromHttpMethod(normalizedMethod, operation, options.defaultRisk) !== "read",
|
|
6034
|
+
inputSchema: openApiInputSchema(path, normalizedMethod, operation),
|
|
6035
|
+
outputSchema: operation.responses
|
|
5835
6036
|
});
|
|
5836
6037
|
}
|
|
5837
|
-
}
|
|
6038
|
+
}
|
|
6039
|
+
return connectorFromActions(options, actions);
|
|
5838
6040
|
}
|
|
5839
|
-
|
|
5840
|
-
|
|
5841
|
-
|
|
5842
|
-
|
|
5843
|
-
|
|
5844
|
-
|
|
5845
|
-
|
|
5846
|
-
|
|
5847
|
-
|
|
5848
|
-
|
|
5849
|
-
|
|
5850
|
-
|
|
5851
|
-
|
|
5852
|
-
|
|
5853
|
-
|
|
5854
|
-
const
|
|
5855
|
-
|
|
5856
|
-
|
|
5857
|
-
|
|
5858
|
-
|
|
5859
|
-
|
|
5860
|
-
|
|
5861
|
-
|
|
5862
|
-
|
|
5863
|
-
|
|
5864
|
-
const parsed = await response.json().catch(() => void 0);
|
|
5865
|
-
if (!response.ok) {
|
|
5866
|
-
return parsed ?? {
|
|
5867
|
-
ok: false,
|
|
5868
|
-
action: invocation.request.action,
|
|
5869
|
-
output: { message: `Activepieces runtime returned HTTP ${response.status}.` }
|
|
5870
|
-
};
|
|
5871
|
-
}
|
|
5872
|
-
return parsed ?? {
|
|
5873
|
-
ok: false,
|
|
5874
|
-
action: invocation.request.action,
|
|
5875
|
-
output: { message: "Activepieces runtime returned an empty response." }
|
|
6041
|
+
function importGraphqlConnector(operations, options) {
|
|
6042
|
+
return connectorFromActions(options, operations.map((operation) => ({
|
|
6043
|
+
id: operation.name,
|
|
6044
|
+
title: titleFromId(operation.name),
|
|
6045
|
+
risk: operation.kind === "query" ? "read" : options.defaultRisk ?? "write",
|
|
6046
|
+
requiredScopes: operation.requiredScopes ?? options.scopes ?? [],
|
|
6047
|
+
dataClass: options.dataClass ?? "private",
|
|
6048
|
+
description: operation.description,
|
|
6049
|
+
approvalRequired: operation.kind === "mutation",
|
|
6050
|
+
inputSchema: operation.inputSchema,
|
|
6051
|
+
outputSchema: operation.outputSchema
|
|
6052
|
+
})));
|
|
6053
|
+
}
|
|
6054
|
+
function importMcpConnector(catalog, options) {
|
|
6055
|
+
return connectorFromActions(options, catalog.tools.map((tool) => {
|
|
6056
|
+
const risk = riskFromMcpTool(tool, options.defaultRisk);
|
|
6057
|
+
return {
|
|
6058
|
+
id: tool.name,
|
|
6059
|
+
title: tool.annotations?.title ?? titleFromId(tool.name),
|
|
6060
|
+
risk,
|
|
6061
|
+
requiredScopes: options.scopes ?? [],
|
|
6062
|
+
dataClass: options.dataClass ?? "private",
|
|
6063
|
+
description: tool.description,
|
|
6064
|
+
approvalRequired: risk !== "read",
|
|
6065
|
+
inputSchema: tool.inputSchema
|
|
5876
6066
|
};
|
|
5877
|
-
};
|
|
6067
|
+
}));
|
|
5878
6068
|
}
|
|
5879
|
-
function
|
|
6069
|
+
function connectorFromActions(options, actions) {
|
|
6070
|
+
const scopes = unique5([
|
|
6071
|
+
...options.scopes ?? [],
|
|
6072
|
+
...actions.flatMap((action) => action.requiredScopes)
|
|
6073
|
+
]);
|
|
5880
6074
|
return {
|
|
5881
|
-
|
|
5882
|
-
|
|
5883
|
-
|
|
5884
|
-
|
|
5885
|
-
|
|
5886
|
-
|
|
5887
|
-
|
|
5888
|
-
|
|
5889
|
-
scopes: invocation.connector.scopes,
|
|
5890
|
-
metadata: invocation.connector.metadata
|
|
5891
|
-
},
|
|
5892
|
-
piece: invocation.piece,
|
|
5893
|
-
action: {
|
|
5894
|
-
id: invocation.request.action,
|
|
5895
|
-
input: invocation.request.input,
|
|
5896
|
-
idempotencyKey: invocation.request.idempotencyKey,
|
|
5897
|
-
dryRun: invocation.request.dryRun,
|
|
5898
|
-
metadata: invocation.request.metadata
|
|
5899
|
-
}
|
|
6075
|
+
id: options.connectorId,
|
|
6076
|
+
providerId: options.providerId,
|
|
6077
|
+
title: options.connectorTitle,
|
|
6078
|
+
category: options.category ?? "other",
|
|
6079
|
+
auth: options.auth ?? "custom",
|
|
6080
|
+
scopes,
|
|
6081
|
+
actions,
|
|
6082
|
+
metadata: { source: "catalog-importer" }
|
|
5900
6083
|
};
|
|
5901
6084
|
}
|
|
5902
|
-
function
|
|
5903
|
-
|
|
5904
|
-
|
|
5905
|
-
|
|
5906
|
-
if (
|
|
5907
|
-
|
|
5908
|
-
const left = Buffer.from(signature);
|
|
5909
|
-
const right = Buffer.from(expected);
|
|
5910
|
-
return left.length === right.length && timingSafeEqual2(left, right);
|
|
6085
|
+
function riskFromHttpMethod(method, operation, fallback) {
|
|
6086
|
+
if (method === "get") return "read";
|
|
6087
|
+
if (method === "delete") return "destructive";
|
|
6088
|
+
const text = `${operation.operationId ?? ""} ${operation.summary ?? ""} ${operation.description ?? ""}`.toLowerCase();
|
|
6089
|
+
if (/\b(delete|remove|destroy|cancel|void|revoke|drop)\b/.test(text)) return "destructive";
|
|
6090
|
+
return fallback && fallback !== "read" ? fallback : "write";
|
|
5911
6091
|
}
|
|
5912
|
-
function
|
|
5913
|
-
|
|
5914
|
-
|
|
5915
|
-
const
|
|
5916
|
-
|
|
5917
|
-
|
|
5918
|
-
|
|
5919
|
-
return async (invocation) => {
|
|
5920
|
-
const body = buildTangleCatalogRuntimeRequest(invocation, requestId());
|
|
5921
|
-
const serialized = JSON.stringify(body);
|
|
5922
|
-
const response = await fetchImpl(`${endpoint}${normalizedPath}`, {
|
|
5923
|
-
method: "POST",
|
|
5924
|
-
headers: {
|
|
5925
|
-
"content-type": "application/json",
|
|
5926
|
-
...options.headers,
|
|
5927
|
-
...options.secret ? { [signatureHeader]: signTangleCatalogRuntimeRequest(serialized, options.secret) } : {}
|
|
5928
|
-
},
|
|
5929
|
-
body: serialized,
|
|
5930
|
-
signal: AbortSignal.timeout(options.timeoutMs ?? 3e4)
|
|
5931
|
-
});
|
|
5932
|
-
const parsed = await response.json().catch(() => void 0);
|
|
5933
|
-
if (!response.ok) {
|
|
5934
|
-
return parsed ?? {
|
|
5935
|
-
ok: false,
|
|
5936
|
-
action: invocation.request.action,
|
|
5937
|
-
output: { message: `Tangle catalog runtime returned HTTP ${response.status}.` }
|
|
5938
|
-
};
|
|
5939
|
-
}
|
|
5940
|
-
return parsed ?? {
|
|
5941
|
-
ok: false,
|
|
5942
|
-
action: invocation.request.action,
|
|
5943
|
-
output: { message: "Tangle catalog runtime returned an empty response." }
|
|
5944
|
-
};
|
|
5945
|
-
};
|
|
6092
|
+
function riskFromMcpTool(tool, fallback) {
|
|
6093
|
+
if (tool.annotations?.destructiveHint) return "destructive";
|
|
6094
|
+
if (tool.annotations?.readOnlyHint) return "read";
|
|
6095
|
+
const text = `${tool.name} ${tool.description ?? ""}`.toLowerCase();
|
|
6096
|
+
if (/\b(delete|remove|destroy|cancel|void|revoke|drop)\b/.test(text)) return "destructive";
|
|
6097
|
+
if (/\b(get|list|read|search|find|fetch|query)\b/.test(text)) return "read";
|
|
6098
|
+
return fallback ?? "write";
|
|
5946
6099
|
}
|
|
5947
|
-
function
|
|
5948
|
-
|
|
5949
|
-
|
|
5950
|
-
|
|
5951
|
-
|
|
5952
|
-
|
|
5953
|
-
|
|
5954
|
-
|
|
5955
|
-
|
|
5956
|
-
|
|
5957
|
-
|
|
5958
|
-
|
|
5959
|
-
|
|
5960
|
-
piece: invocation.piece,
|
|
5961
|
-
action: {
|
|
5962
|
-
id: invocation.request.action,
|
|
5963
|
-
input: invocation.request.input,
|
|
5964
|
-
idempotencyKey: invocation.request.idempotencyKey,
|
|
5965
|
-
dryRun: invocation.request.dryRun,
|
|
5966
|
-
metadata: invocation.request.metadata
|
|
6100
|
+
function scopesFromOpenApiOperation(operation, fallback) {
|
|
6101
|
+
const scopes = (operation.security ?? []).flatMap((entry) => Object.values(entry).flat());
|
|
6102
|
+
return unique5(scopes.length > 0 ? scopes : fallback);
|
|
6103
|
+
}
|
|
6104
|
+
function openApiInputSchema(path, method, operation) {
|
|
6105
|
+
return {
|
|
6106
|
+
type: "object",
|
|
6107
|
+
additionalProperties: true,
|
|
6108
|
+
properties: {
|
|
6109
|
+
path: { const: path },
|
|
6110
|
+
method: { const: method.toUpperCase() },
|
|
6111
|
+
parameters: { type: "object", additionalProperties: true },
|
|
6112
|
+
body: operation.requestBody ?? { type: "object", additionalProperties: true }
|
|
5967
6113
|
}
|
|
5968
6114
|
};
|
|
5969
6115
|
}
|
|
5970
|
-
|
|
5971
|
-
|
|
5972
|
-
|
|
5973
|
-
// src/catalog-freshness.ts
|
|
5974
|
-
var ACTIVEPIECES_PUBLIC_CATALOG_URL = "https://www.activepieces.com/pieces";
|
|
5975
|
-
function parseCount(value) {
|
|
5976
|
-
if (!value) return void 0;
|
|
5977
|
-
const parsed = Number.parseInt(value.replace(/,/g, ""), 10);
|
|
5978
|
-
return Number.isFinite(parsed) ? parsed : void 0;
|
|
6116
|
+
function titleFromId(id) {
|
|
6117
|
+
return id.replace(/([a-z])([A-Z])/g, "$1 $2").split(/[^a-zA-Z0-9]+/g).filter(Boolean).map((part) => part.slice(0, 1).toUpperCase() + part.slice(1)).join(" ");
|
|
5979
6118
|
}
|
|
5980
|
-
function
|
|
5981
|
-
|
|
5982
|
-
const integrationMatch = html.match(/([0-9,]+)\+?\s+Integrations/i);
|
|
5983
|
-
return parseCount(showingMatch?.[1]) ?? parseCount(integrationMatch?.[1]);
|
|
6119
|
+
function isObject(value) {
|
|
6120
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
5984
6121
|
}
|
|
5985
|
-
|
|
5986
|
-
|
|
5987
|
-
|
|
5988
|
-
|
|
5989
|
-
|
|
5990
|
-
|
|
5991
|
-
|
|
5992
|
-
|
|
5993
|
-
|
|
5994
|
-
|
|
5995
|
-
|
|
5996
|
-
|
|
5997
|
-
|
|
5998
|
-
|
|
5999
|
-
|
|
6000
|
-
|
|
6001
|
-
|
|
6002
|
-
const executableTools = buildIntegrationToolCatalog(executableRegistry.connectors);
|
|
6003
|
-
const unsupportedExecutableConnectorIds = executableActivepiecesConnectors.filter((connector) => connector.actions.length === 0).map((connector) => connector.id);
|
|
6004
|
-
const registry = buildDefaultIntegrationRegistry({
|
|
6005
|
-
includeSpecs: true,
|
|
6006
|
-
includeActivepieces: true
|
|
6007
|
-
});
|
|
6008
|
-
const warnings = [];
|
|
6009
|
-
if (activepiecesConnectors.length < minActivepiecesConnectors) {
|
|
6010
|
-
warnings.push(
|
|
6011
|
-
`Activepieces catalog has ${activepiecesConnectors.length} connectors, below floor ${minActivepiecesConnectors}.`
|
|
6012
|
-
);
|
|
6013
|
-
}
|
|
6014
|
-
if (unsupportedExecutableConnectorIds.length > 0) {
|
|
6015
|
-
warnings.push(
|
|
6016
|
-
`Activepieces executable provider has ${unsupportedExecutableConnectorIds.length} connectors without actions.`
|
|
6017
|
-
);
|
|
6018
|
-
}
|
|
6019
|
-
if (executableTools.length < activepiecesEntries.length) {
|
|
6020
|
-
warnings.push(
|
|
6021
|
-
`Activepieces executable provider produced only ${executableTools.length} tool definitions for ${activepiecesEntries.length} entries.`
|
|
6022
|
-
);
|
|
6023
|
-
}
|
|
6024
|
-
let upstream;
|
|
6025
|
-
if (options.liveActivepieces) {
|
|
6026
|
-
upstream = await checkActivepiecesPublicCatalog({
|
|
6027
|
-
localConnectorCount: activepiecesConnectors.length,
|
|
6028
|
-
staleConnectorDelta,
|
|
6029
|
-
fetchImpl: options.fetchImpl,
|
|
6030
|
-
warnings
|
|
6122
|
+
function unique5(values) {
|
|
6123
|
+
return [...new Set(values)];
|
|
6124
|
+
}
|
|
6125
|
+
|
|
6126
|
+
// src/gateway-catalog.ts
|
|
6127
|
+
function createGatewayCatalogProvider(options) {
|
|
6128
|
+
const now = options.now ?? (() => /* @__PURE__ */ new Date());
|
|
6129
|
+
let cachedAt = 0;
|
|
6130
|
+
let cached;
|
|
6131
|
+
async function listConnectors() {
|
|
6132
|
+
const ttl = options.cacheTtlMs ?? 6e4;
|
|
6133
|
+
const current = now().getTime();
|
|
6134
|
+
if (cached && current - cachedAt < ttl) return cached;
|
|
6135
|
+
const entries = await options.fetchCatalog();
|
|
6136
|
+
cached = normalizeGatewayCatalog(entries, {
|
|
6137
|
+
providerId: options.id,
|
|
6138
|
+
providerKind: options.kind
|
|
6031
6139
|
});
|
|
6140
|
+
cachedAt = current;
|
|
6141
|
+
return cached;
|
|
6032
6142
|
}
|
|
6033
6143
|
return {
|
|
6034
|
-
|
|
6035
|
-
|
|
6036
|
-
|
|
6037
|
-
|
|
6038
|
-
|
|
6039
|
-
|
|
6040
|
-
|
|
6041
|
-
|
|
6042
|
-
|
|
6043
|
-
|
|
6044
|
-
|
|
6045
|
-
|
|
6046
|
-
),
|
|
6047
|
-
executableActivepiecesConnectors: executableActivepiecesConnectors.length,
|
|
6048
|
-
executableActivepiecesActions: executableActivepiecesConnectors.reduce(
|
|
6049
|
-
(sum, connector) => sum + connector.actions.length,
|
|
6050
|
-
0
|
|
6051
|
-
),
|
|
6052
|
-
executableActivepiecesTriggers: executableActivepiecesConnectors.reduce(
|
|
6053
|
-
(sum, connector) => sum + (connector.triggers?.length ?? 0),
|
|
6054
|
-
0
|
|
6055
|
-
),
|
|
6056
|
-
executableToolDefinitions: executableTools.length,
|
|
6057
|
-
unsupportedExecutableConnectorIds,
|
|
6058
|
-
registryEntries: registry.entries.length,
|
|
6059
|
-
registrySummary: summarizeIntegrationRegistry(registry),
|
|
6060
|
-
conflictSamples: registry.entries.flatMap((entry) => entry.conflicts).slice(0, 10)
|
|
6061
|
-
},
|
|
6062
|
-
upstream,
|
|
6063
|
-
warnings
|
|
6144
|
+
id: options.id,
|
|
6145
|
+
kind: options.kind,
|
|
6146
|
+
listConnectors,
|
|
6147
|
+
startAuth: options.startAuth,
|
|
6148
|
+
completeAuth: options.completeAuth,
|
|
6149
|
+
async invokeAction(connection, request) {
|
|
6150
|
+
if (!options.invokeAction) {
|
|
6151
|
+
throw new IntegrationError(`Gateway provider ${options.id} does not implement action invocation.`, "action_not_found");
|
|
6152
|
+
}
|
|
6153
|
+
await assertKnownGatewayAction(await listConnectors(), connection.connectorId, request.action);
|
|
6154
|
+
return options.invokeAction(connection, request);
|
|
6155
|
+
}
|
|
6064
6156
|
};
|
|
6065
6157
|
}
|
|
6066
|
-
|
|
6067
|
-
|
|
6068
|
-
|
|
6069
|
-
|
|
6070
|
-
|
|
6158
|
+
function normalizeGatewayCatalog(entries, options) {
|
|
6159
|
+
const out = [];
|
|
6160
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6161
|
+
for (const entry of entries) {
|
|
6162
|
+
const id = slug2(entry.id ?? entry.key ?? entry.name ?? entry.title ?? "");
|
|
6163
|
+
if (!id || seen.has(id)) continue;
|
|
6164
|
+
seen.add(id);
|
|
6165
|
+
const title = entry.title ?? entry.name ?? titleFromId2(id);
|
|
6166
|
+
const actions = normalizeActions(entry.actions ?? [], entry.scopes ?? []);
|
|
6167
|
+
out.push({
|
|
6168
|
+
id,
|
|
6169
|
+
providerId: options.providerId,
|
|
6170
|
+
title,
|
|
6171
|
+
category: normalizeCategory(entry.category),
|
|
6172
|
+
auth: normalizeAuth(entry.auth),
|
|
6173
|
+
scopes: unique6([
|
|
6174
|
+
...entry.scopes ?? [],
|
|
6175
|
+
...actions.flatMap((action) => action.requiredScopes)
|
|
6176
|
+
]),
|
|
6177
|
+
actions: actions.length > 0 ? actions : defaultActionsFor(entry.category, entry.scopes ?? []),
|
|
6178
|
+
triggers: normalizeTriggers(entry.triggers ?? [], entry.scopes ?? []),
|
|
6179
|
+
metadata: {
|
|
6180
|
+
...entry.metadata ?? {},
|
|
6181
|
+
source: "gateway-catalog",
|
|
6182
|
+
providerKind: options.providerKind,
|
|
6183
|
+
executable: true
|
|
6184
|
+
}
|
|
6071
6185
|
});
|
|
6072
|
-
|
|
6073
|
-
|
|
6074
|
-
|
|
6075
|
-
|
|
6076
|
-
|
|
6077
|
-
|
|
6078
|
-
|
|
6079
|
-
}
|
|
6080
|
-
|
|
6081
|
-
|
|
6082
|
-
|
|
6083
|
-
|
|
6084
|
-
|
|
6085
|
-
|
|
6186
|
+
}
|
|
6187
|
+
return out;
|
|
6188
|
+
}
|
|
6189
|
+
async function assertKnownGatewayAction(connectors, connectorId, actionId) {
|
|
6190
|
+
const connector = connectors.find((candidate) => candidate.id === connectorId);
|
|
6191
|
+
if (!connector) throw new IntegrationError(`Connector ${connectorId} not found.`, "connector_not_found");
|
|
6192
|
+
if (!connector.actions.some((action) => action.id === actionId)) {
|
|
6193
|
+
throw new IntegrationError(`Action ${actionId} is not defined by connector ${connectorId}.`, "action_not_found");
|
|
6194
|
+
}
|
|
6195
|
+
}
|
|
6196
|
+
function normalizeActions(actions, fallbackScopes) {
|
|
6197
|
+
return actions.map((action) => {
|
|
6198
|
+
const id = slug2(action.id ?? action.key ?? action.name ?? action.title ?? "");
|
|
6199
|
+
return {
|
|
6200
|
+
id,
|
|
6201
|
+
title: action.title ?? action.name ?? titleFromId2(id),
|
|
6202
|
+
risk: normalizeRisk(action.risk),
|
|
6203
|
+
requiredScopes: unique6([
|
|
6204
|
+
...action.requiredScopes ?? [],
|
|
6205
|
+
...action.scopes ?? [],
|
|
6206
|
+
...action.requiredScopes?.length || action.scopes?.length ? [] : fallbackScopes
|
|
6207
|
+
]),
|
|
6208
|
+
dataClass: normalizeDataClass(action.dataClass),
|
|
6209
|
+
description: action.description,
|
|
6210
|
+
approvalRequired: action.approvalRequired ?? normalizeRisk(action.risk) !== "read",
|
|
6211
|
+
inputSchema: action.inputSchema,
|
|
6212
|
+
outputSchema: action.outputSchema
|
|
6213
|
+
};
|
|
6214
|
+
}).filter((action) => action.id);
|
|
6215
|
+
}
|
|
6216
|
+
function normalizeTriggers(triggers, fallbackScopes) {
|
|
6217
|
+
const normalized = triggers.map((trigger2) => {
|
|
6218
|
+
const id = slug2(trigger2.id ?? trigger2.key ?? trigger2.name ?? trigger2.title ?? "");
|
|
6219
|
+
return {
|
|
6220
|
+
id,
|
|
6221
|
+
title: trigger2.title ?? trigger2.name ?? titleFromId2(id),
|
|
6222
|
+
requiredScopes: unique6([
|
|
6223
|
+
...trigger2.requiredScopes ?? [],
|
|
6224
|
+
...trigger2.scopes ?? [],
|
|
6225
|
+
...trigger2.requiredScopes?.length || trigger2.scopes?.length ? [] : fallbackScopes
|
|
6226
|
+
]),
|
|
6227
|
+
dataClass: normalizeDataClass(trigger2.dataClass),
|
|
6228
|
+
description: trigger2.description,
|
|
6229
|
+
payloadSchema: trigger2.payloadSchema
|
|
6230
|
+
};
|
|
6231
|
+
}).filter((trigger2) => trigger2.id);
|
|
6232
|
+
return normalized.length > 0 ? normalized : void 0;
|
|
6233
|
+
}
|
|
6234
|
+
function defaultActionsFor(category, scopes) {
|
|
6235
|
+
const readScope = scopes.find((scope) => scope.endsWith(".read")) ?? scopes[0];
|
|
6236
|
+
const writeScope = scopes.find((scope) => scope.endsWith(".write")) ?? scopes[1] ?? readScope;
|
|
6237
|
+
const requiredRead = readScope ? [readScope] : [];
|
|
6238
|
+
const requiredWrite = writeScope ? [writeScope] : [];
|
|
6239
|
+
const dataClass = normalizeDataClass(category === "finance" || category === "commerce" || category === "hr" ? "sensitive" : "private");
|
|
6240
|
+
return [
|
|
6241
|
+
{
|
|
6242
|
+
id: "records.search",
|
|
6243
|
+
title: "Search records",
|
|
6244
|
+
risk: "read",
|
|
6245
|
+
requiredScopes: requiredRead,
|
|
6246
|
+
dataClass,
|
|
6247
|
+
description: "Search provider records."
|
|
6248
|
+
},
|
|
6249
|
+
{
|
|
6250
|
+
id: "records.read",
|
|
6251
|
+
title: "Read record",
|
|
6252
|
+
risk: "read",
|
|
6253
|
+
requiredScopes: requiredRead,
|
|
6254
|
+
dataClass,
|
|
6255
|
+
description: "Read a provider record."
|
|
6256
|
+
},
|
|
6257
|
+
{
|
|
6258
|
+
id: "records.upsert",
|
|
6259
|
+
title: "Upsert record",
|
|
6260
|
+
risk: "write",
|
|
6261
|
+
requiredScopes: requiredWrite,
|
|
6262
|
+
dataClass,
|
|
6263
|
+
approvalRequired: true,
|
|
6264
|
+
description: "Create or update a provider record."
|
|
6086
6265
|
}
|
|
6087
|
-
|
|
6088
|
-
activepiecesPieces,
|
|
6089
|
-
activepiecesDelta,
|
|
6090
|
-
checkedUrl: ACTIVEPIECES_PUBLIC_CATALOG_URL,
|
|
6091
|
-
warning: activepiecesPieces === void 0 ? "Could not parse upstream piece count." : void 0
|
|
6092
|
-
};
|
|
6093
|
-
} catch (error) {
|
|
6094
|
-
const warning = error instanceof Error ? error.message : "Activepieces freshness request failed.";
|
|
6095
|
-
input.warnings.push(warning);
|
|
6096
|
-
return {
|
|
6097
|
-
checkedUrl: ACTIVEPIECES_PUBLIC_CATALOG_URL,
|
|
6098
|
-
warning
|
|
6099
|
-
};
|
|
6100
|
-
}
|
|
6266
|
+
];
|
|
6101
6267
|
}
|
|
6102
|
-
|
|
6103
|
-
|
|
6104
|
-
|
|
6105
|
-
|
|
6106
|
-
|
|
6107
|
-
|
|
6268
|
+
function normalizeCategory(category) {
|
|
6269
|
+
const value = slug2(category ?? "");
|
|
6270
|
+
if (value === "mail") return "email";
|
|
6271
|
+
if (value === "messaging" || value === "communication" || value === "communications") return "chat";
|
|
6272
|
+
if (value === "file" || value === "files") return "storage";
|
|
6273
|
+
if (value === "project-management" || value === "automation") return "workflow";
|
|
6274
|
+
if (value === "developer" || value === "devops") return "workflow";
|
|
6275
|
+
if (value === "support") return "crm";
|
|
6276
|
+
if ([
|
|
6277
|
+
"email",
|
|
6278
|
+
"calendar",
|
|
6279
|
+
"chat",
|
|
6280
|
+
"crm",
|
|
6281
|
+
"storage",
|
|
6282
|
+
"docs",
|
|
6283
|
+
"database",
|
|
6284
|
+
"webhook",
|
|
6285
|
+
"workflow",
|
|
6286
|
+
"internal",
|
|
6287
|
+
"other"
|
|
6288
|
+
].includes(value)) return value;
|
|
6289
|
+
return "other";
|
|
6108
6290
|
}
|
|
6109
|
-
function
|
|
6110
|
-
|
|
6111
|
-
|
|
6112
|
-
|
|
6113
|
-
|
|
6114
|
-
}));
|
|
6291
|
+
function normalizeAuth(auth) {
|
|
6292
|
+
if (auth === "oauth2") return "oauth2";
|
|
6293
|
+
if (auth === "api_key" || auth === "api-key" || auth === "apikey") return "api_key";
|
|
6294
|
+
if (auth === "none") return "none";
|
|
6295
|
+
return "custom";
|
|
6115
6296
|
}
|
|
6116
|
-
function
|
|
6117
|
-
|
|
6118
|
-
return
|
|
6119
|
-
...options,
|
|
6120
|
-
providerId
|
|
6121
|
-
}).map((connector) => sanitizeConnector(connector, providerId));
|
|
6297
|
+
function normalizeRisk(risk) {
|
|
6298
|
+
if (risk === "read" || risk === "write" || risk === "destructive") return risk;
|
|
6299
|
+
return "write";
|
|
6122
6300
|
}
|
|
6123
|
-
function
|
|
6124
|
-
|
|
6125
|
-
|
|
6126
|
-
providerId,
|
|
6127
|
-
includeCatalogActions: true,
|
|
6128
|
-
executable: true
|
|
6129
|
-
});
|
|
6130
|
-
const byEntry = new Map(listActivepiecesCatalogEntries().map((entry) => [entry.id, entry]));
|
|
6131
|
-
return createCatalogExecutorProvider({
|
|
6132
|
-
id: providerId,
|
|
6133
|
-
kind: "tangle_catalog",
|
|
6134
|
-
connectors,
|
|
6135
|
-
startAuth: options.startAuth,
|
|
6136
|
-
completeAuth: options.completeAuth,
|
|
6137
|
-
executeAction: async ({ connection, request, connector, action }) => {
|
|
6138
|
-
const importedEntry = byEntry.get(connector.id);
|
|
6139
|
-
if (!importedEntry) {
|
|
6140
|
-
throw new IntegrationError(`Tangle catalog entry ${connector.id} not found.`, "connector_not_found");
|
|
6141
|
-
}
|
|
6142
|
-
const catalogAction = importedEntry.actions.find((candidate) => candidate.id === action.id);
|
|
6143
|
-
return options.executeAction({
|
|
6144
|
-
connection,
|
|
6145
|
-
request,
|
|
6146
|
-
connector,
|
|
6147
|
-
catalogEntry: sanitizeEntry(importedEntry),
|
|
6148
|
-
piece: {
|
|
6149
|
-
id: importedEntry.id,
|
|
6150
|
-
version: importedEntry.version,
|
|
6151
|
-
actionId: action.id,
|
|
6152
|
-
upstreamActionName: catalogAction?.upstreamName
|
|
6153
|
-
}
|
|
6154
|
-
});
|
|
6155
|
-
},
|
|
6156
|
-
subscribeTrigger: options.subscribeTrigger ? async (connection, trigger2, targetUrl) => {
|
|
6157
|
-
const connector = connectors.find((candidate) => candidate.id === connection.connectorId);
|
|
6158
|
-
const importedEntry = byEntry.get(connection.connectorId);
|
|
6159
|
-
if (!connector || !importedEntry) {
|
|
6160
|
-
throw new IntegrationError(`Tangle catalog entry ${connection.connectorId} not found.`, "connector_not_found");
|
|
6161
|
-
}
|
|
6162
|
-
const catalogTrigger = importedEntry.triggers.find((candidate) => candidate.id === trigger2.id);
|
|
6163
|
-
return options.subscribeTrigger({
|
|
6164
|
-
connection,
|
|
6165
|
-
connector,
|
|
6166
|
-
catalogEntry: sanitizeEntry(importedEntry),
|
|
6167
|
-
trigger: trigger2,
|
|
6168
|
-
targetUrl,
|
|
6169
|
-
piece: {
|
|
6170
|
-
id: importedEntry.id,
|
|
6171
|
-
version: importedEntry.version,
|
|
6172
|
-
triggerId: trigger2.id,
|
|
6173
|
-
upstreamTriggerName: catalogTrigger?.upstreamName
|
|
6174
|
-
}
|
|
6175
|
-
});
|
|
6176
|
-
} : void 0,
|
|
6177
|
-
unsubscribeTrigger: options.unsubscribeTrigger,
|
|
6178
|
-
normalizeTriggerEvent: options.normalizeTriggerEvent
|
|
6179
|
-
});
|
|
6301
|
+
function normalizeDataClass(dataClass) {
|
|
6302
|
+
if (dataClass === "public" || dataClass === "internal" || dataClass === "private" || dataClass === "sensitive" || dataClass === "secret") return dataClass;
|
|
6303
|
+
return "private";
|
|
6180
6304
|
}
|
|
6181
|
-
|
|
6182
|
-
|
|
6183
|
-
const result = await auditIntegrationCatalogFreshness(options);
|
|
6184
|
-
return {
|
|
6185
|
-
ok: result.ok,
|
|
6186
|
-
generatedAt: result.generatedAt,
|
|
6187
|
-
local: {
|
|
6188
|
-
catalogEntries: result.local.activepiecesEntries,
|
|
6189
|
-
catalogConnectors: result.local.activepiecesConnectors,
|
|
6190
|
-
catalogActions: result.local.activepiecesActions,
|
|
6191
|
-
catalogTriggers: result.local.activepiecesTriggers,
|
|
6192
|
-
executableCatalogConnectors: result.local.executableActivepiecesConnectors,
|
|
6193
|
-
executableCatalogActions: result.local.executableActivepiecesActions,
|
|
6194
|
-
executableCatalogTriggers: result.local.executableActivepiecesTriggers,
|
|
6195
|
-
executableToolDefinitions: result.local.executableToolDefinitions,
|
|
6196
|
-
unsupportedExecutableConnectorIds: result.local.unsupportedExecutableConnectorIds,
|
|
6197
|
-
registryEntries: result.local.registryEntries,
|
|
6198
|
-
registrySummary: result.local.registrySummary,
|
|
6199
|
-
conflictSamples: result.local.conflictSamples
|
|
6200
|
-
},
|
|
6201
|
-
upstream: result.upstream ? {
|
|
6202
|
-
externalEntries: result.upstream.activepiecesPieces,
|
|
6203
|
-
externalDelta: result.upstream.activepiecesDelta,
|
|
6204
|
-
checkedUrl: result.upstream.checkedUrl,
|
|
6205
|
-
warning: result.upstream.warning
|
|
6206
|
-
} : void 0,
|
|
6207
|
-
warnings: result.warnings.map((warning) => warning.replaceAll("Activepieces", "Tangle Integrations Catalog"))
|
|
6208
|
-
};
|
|
6305
|
+
function slug2(value) {
|
|
6306
|
+
return value.trim().toLowerCase().replace(/&/g, "and").replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
6209
6307
|
}
|
|
6210
|
-
function
|
|
6211
|
-
return
|
|
6212
|
-
id: entry.id,
|
|
6213
|
-
title: entry.title,
|
|
6214
|
-
description: entry.description,
|
|
6215
|
-
category: entry.category,
|
|
6216
|
-
auth: entry.auth,
|
|
6217
|
-
authFields: entry.authFields,
|
|
6218
|
-
domains: entry.domains.filter((domain) => !domain.toLowerCase().includes("activepieces")),
|
|
6219
|
-
actions: entry.actions.map((action) => ({
|
|
6220
|
-
id: action.id,
|
|
6221
|
-
title: action.title,
|
|
6222
|
-
risk: action.risk
|
|
6223
|
-
})),
|
|
6224
|
-
triggers: entry.triggers.map((trigger2) => ({
|
|
6225
|
-
id: trigger2.id,
|
|
6226
|
-
title: trigger2.title,
|
|
6227
|
-
upstreamName: trigger2.upstreamName
|
|
6228
|
-
}))
|
|
6229
|
-
};
|
|
6308
|
+
function titleFromId2(id) {
|
|
6309
|
+
return id.split("-").filter(Boolean).map((part) => part.slice(0, 1).toUpperCase() + part.slice(1)).join(" ");
|
|
6230
6310
|
}
|
|
6231
|
-
function
|
|
6232
|
-
|
|
6233
|
-
return {
|
|
6234
|
-
...connector,
|
|
6235
|
-
providerId,
|
|
6236
|
-
metadata: {
|
|
6237
|
-
source: TANGLE_INTEGRATIONS_CATALOG_SOURCE,
|
|
6238
|
-
providerId,
|
|
6239
|
-
executable: metadata.executable,
|
|
6240
|
-
runtime: "tangle-catalog-runtime",
|
|
6241
|
-
catalogOnly: metadata.catalogOnly,
|
|
6242
|
-
supportTier: metadata.supportTier,
|
|
6243
|
-
catalogActionCount: metadata.catalogActionCount,
|
|
6244
|
-
catalogTriggerCount: metadata.catalogTriggerCount,
|
|
6245
|
-
license: metadata.license,
|
|
6246
|
-
version: metadata.version,
|
|
6247
|
-
domains: Array.isArray(metadata.domains) ? metadata.domains.filter((domain) => typeof domain === "string" && !domain.toLowerCase().includes("activepieces")) : void 0,
|
|
6248
|
-
...metadata.overridden ? { overridden: true } : {}
|
|
6249
|
-
}
|
|
6250
|
-
};
|
|
6311
|
+
function unique6(values) {
|
|
6312
|
+
return [...new Set(values)];
|
|
6251
6313
|
}
|
|
6252
6314
|
|
|
6253
6315
|
// src/tangle-catalog-runtime.ts
|
|
@@ -7231,6 +7293,35 @@ export {
|
|
|
7231
7293
|
getActivepiecesOverride,
|
|
7232
7294
|
listActivepiecesCatalogEntries,
|
|
7233
7295
|
buildActivepiecesConnectors,
|
|
7296
|
+
createCatalogExecutorProvider,
|
|
7297
|
+
createActivepiecesExecutorProvider,
|
|
7298
|
+
integrationToolName,
|
|
7299
|
+
parseIntegrationToolName,
|
|
7300
|
+
buildIntegrationToolCatalog,
|
|
7301
|
+
searchIntegrationTools,
|
|
7302
|
+
toMcpTools,
|
|
7303
|
+
ACTIVEPIECES_PUBLIC_CATALOG_URL,
|
|
7304
|
+
extractActivepiecesPublicPieceCount,
|
|
7305
|
+
auditIntegrationCatalogFreshness,
|
|
7306
|
+
ACTIVEPIECES_RUNTIME_SIGNATURE_HEADER,
|
|
7307
|
+
TANGLE_CATALOG_RUNTIME_SIGNATURE_HEADER,
|
|
7308
|
+
createActivepiecesHttpExecutor,
|
|
7309
|
+
buildActivepiecesRuntimeRequest,
|
|
7310
|
+
signActivepiecesRuntimeRequest,
|
|
7311
|
+
verifyActivepiecesRuntimeSignature,
|
|
7312
|
+
createTangleCatalogHttpExecutor,
|
|
7313
|
+
buildTangleCatalogRuntimeRequest,
|
|
7314
|
+
signTangleCatalogRuntimeRequest,
|
|
7315
|
+
verifyTangleCatalogRuntimeSignature,
|
|
7316
|
+
TANGLE_INTEGRATIONS_CATALOG_PROVIDER_ID,
|
|
7317
|
+
TANGLE_INTEGRATIONS_CATALOG_SOURCE,
|
|
7318
|
+
listTangleIntegrationCatalogEntries,
|
|
7319
|
+
listTangleIntegrationContracts,
|
|
7320
|
+
listTangleIntegrationCatalogRuntimePackages,
|
|
7321
|
+
buildTangleIntegrationCatalogConnectors,
|
|
7322
|
+
createTangleCatalogExecutorProvider,
|
|
7323
|
+
extractExternalCatalogPublicCount,
|
|
7324
|
+
auditTangleIntegrationCatalogFreshness,
|
|
7234
7325
|
buildDefaultIntegrationRegistry,
|
|
7235
7326
|
composeIntegrationRegistry,
|
|
7236
7327
|
summarizeIntegrationRegistry,
|
|
@@ -7326,12 +7417,6 @@ export {
|
|
|
7326
7417
|
airtableConnector,
|
|
7327
7418
|
asanaConnector,
|
|
7328
7419
|
salesforceConnector,
|
|
7329
|
-
integrationToolName,
|
|
7330
|
-
parseIntegrationToolName,
|
|
7331
|
-
buildIntegrationToolCatalog,
|
|
7332
|
-
searchIntegrationTools,
|
|
7333
|
-
toMcpTools,
|
|
7334
|
-
createCatalogExecutorProvider,
|
|
7335
7420
|
buildIntegrationInvocationEnvelope,
|
|
7336
7421
|
invocationRequestFromEnvelope,
|
|
7337
7422
|
validateIntegrationInvocationEnvelope,
|
|
@@ -7345,28 +7430,6 @@ export {
|
|
|
7345
7430
|
importMcpConnector,
|
|
7346
7431
|
createGatewayCatalogProvider,
|
|
7347
7432
|
normalizeGatewayCatalog,
|
|
7348
|
-
createActivepiecesExecutorProvider,
|
|
7349
|
-
ACTIVEPIECES_RUNTIME_SIGNATURE_HEADER,
|
|
7350
|
-
TANGLE_CATALOG_RUNTIME_SIGNATURE_HEADER,
|
|
7351
|
-
createActivepiecesHttpExecutor,
|
|
7352
|
-
buildActivepiecesRuntimeRequest,
|
|
7353
|
-
signActivepiecesRuntimeRequest,
|
|
7354
|
-
verifyActivepiecesRuntimeSignature,
|
|
7355
|
-
createTangleCatalogHttpExecutor,
|
|
7356
|
-
buildTangleCatalogRuntimeRequest,
|
|
7357
|
-
signTangleCatalogRuntimeRequest,
|
|
7358
|
-
verifyTangleCatalogRuntimeSignature,
|
|
7359
|
-
ACTIVEPIECES_PUBLIC_CATALOG_URL,
|
|
7360
|
-
extractActivepiecesPublicPieceCount,
|
|
7361
|
-
auditIntegrationCatalogFreshness,
|
|
7362
|
-
TANGLE_INTEGRATIONS_CATALOG_PROVIDER_ID,
|
|
7363
|
-
TANGLE_INTEGRATIONS_CATALOG_SOURCE,
|
|
7364
|
-
listTangleIntegrationCatalogEntries,
|
|
7365
|
-
listTangleIntegrationCatalogRuntimePackages,
|
|
7366
|
-
buildTangleIntegrationCatalogConnectors,
|
|
7367
|
-
createTangleCatalogExecutorProvider,
|
|
7368
|
-
extractExternalCatalogPublicCount,
|
|
7369
|
-
auditTangleIntegrationCatalogFreshness,
|
|
7370
7433
|
createTangleCatalogRuntimeHandler,
|
|
7371
7434
|
createTangleCatalogInstalledPackageExecutor,
|
|
7372
7435
|
createTangleCatalogCredentialAuthResolver,
|
|
@@ -7389,4 +7452,4 @@ export {
|
|
|
7389
7452
|
signCapability,
|
|
7390
7453
|
verifyCapabilityToken
|
|
7391
7454
|
};
|
|
7392
|
-
//# sourceMappingURL=chunk-
|
|
7455
|
+
//# sourceMappingURL=chunk-6SSYWA3J.js.map
|