@tangle-network/agent-integrations 0.23.1 → 0.25.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 +13 -5
- package/dist/bin/tangle-catalog-runtime.js +23 -2
- package/dist/bin/tangle-catalog-runtime.js.map +1 -1
- package/dist/{chunk-L6WBPDUP.js → chunk-4JQ754PA.js} +2 -2
- package/dist/chunk-4JQ754PA.js.map +1 -0
- package/dist/{chunk-P4BB4CU6.js → chunk-VJ57GPYO.js} +1232 -1068
- package/dist/chunk-VJ57GPYO.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +10 -2
- package/dist/specs.d.ts +131 -31
- package/dist/specs.js +1 -1
- package/docs/adapter-triage.md +19 -10
- package/docs/catalog-registry.md +6 -3
- package/docs/generated-integration-coverage-checklist.md +3 -3
- package/docs/integration-coverage-checklist.md +3 -3
- package/docs/integration-execution-audit.md +45 -39
- package/docs/integration-execution-matrix.json +8100 -2009
- package/docs/production-completion-checklist.md +3 -2
- package/docs/repo-structure.md +2 -2
- package/docs/third-party/activepieces.md +6 -2
- package/package.json +1 -1
- package/dist/chunk-L6WBPDUP.js.map +0 -1
- package/dist/chunk-P4BB4CU6.js.map +0 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
integrationSpecToConnector,
|
|
3
3
|
listIntegrationSpecs
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-4JQ754PA.js";
|
|
5
5
|
|
|
6
6
|
// src/index.ts
|
|
7
7
|
import { createHmac as createHmac4, randomUUID as randomUUID5, timingSafeEqual as timingSafeEqual3 } from "crypto";
|
|
@@ -218,6 +218,699 @@ 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.`, "trigger_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}.`, "trigger_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 buildTangleCatalogRuntimePackageManifest(options = {}) {
|
|
743
|
+
const dependencies = {};
|
|
744
|
+
if (options.includeAgentIntegrationsPackage ?? true) {
|
|
745
|
+
dependencies["@tangle-network/agent-integrations"] = options.agentIntegrationsVersion ?? "latest";
|
|
746
|
+
}
|
|
747
|
+
for (const pkg of listTangleIntegrationCatalogRuntimePackages()) {
|
|
748
|
+
dependencies[pkg.packageName] = pkg.version ?? "latest";
|
|
749
|
+
}
|
|
750
|
+
Object.assign(dependencies, options.additionalDependencies);
|
|
751
|
+
return {
|
|
752
|
+
name: options.name ?? "@tangle-network/agent-integrations-runtime-bundle",
|
|
753
|
+
private: true,
|
|
754
|
+
type: "module",
|
|
755
|
+
dependencies: Object.fromEntries(Object.entries(dependencies).sort(([a], [b]) => a.localeCompare(b))),
|
|
756
|
+
tangle: {
|
|
757
|
+
integrationContracts: listTangleIntegrationContracts().length,
|
|
758
|
+
packageRuntimeBackends: listTangleIntegrationContracts().filter((contract) => contract.implementation.kind === "package_runtime").length,
|
|
759
|
+
generatedFrom: TANGLE_INTEGRATIONS_CATALOG_SOURCE
|
|
760
|
+
}
|
|
761
|
+
};
|
|
762
|
+
}
|
|
763
|
+
function renderTangleCatalogRuntimePnpmAddCommand(options = {}) {
|
|
764
|
+
const manifest = buildTangleCatalogRuntimePackageManifest({
|
|
765
|
+
includeAgentIntegrationsPackage: options.includeAgentIntegrationsPackage,
|
|
766
|
+
agentIntegrationsVersion: options.agentIntegrationsVersion
|
|
767
|
+
});
|
|
768
|
+
return [
|
|
769
|
+
"pnpm",
|
|
770
|
+
"add",
|
|
771
|
+
...Object.entries(manifest.dependencies).map(([name, version]) => `${name}@${version}`)
|
|
772
|
+
].join(" ");
|
|
773
|
+
}
|
|
774
|
+
function buildTangleIntegrationCatalogConnectors(options = {}) {
|
|
775
|
+
const providerId = options.providerId ?? TANGLE_INTEGRATIONS_CATALOG_PROVIDER_ID;
|
|
776
|
+
return buildActivepiecesConnectors({
|
|
777
|
+
...options,
|
|
778
|
+
providerId
|
|
779
|
+
}).map((connector) => sanitizeConnector(connector, providerId));
|
|
780
|
+
}
|
|
781
|
+
function createTangleCatalogExecutorProvider(options) {
|
|
782
|
+
const providerId = options.id ?? TANGLE_INTEGRATIONS_CATALOG_PROVIDER_ID;
|
|
783
|
+
const connectors = options.connectors ?? buildTangleIntegrationCatalogConnectors({
|
|
784
|
+
providerId,
|
|
785
|
+
includeCatalogActions: true,
|
|
786
|
+
executable: true
|
|
787
|
+
});
|
|
788
|
+
const byEntry = new Map(listActivepiecesCatalogEntries().map((entry) => [entry.id, entry]));
|
|
789
|
+
return createCatalogExecutorProvider({
|
|
790
|
+
id: providerId,
|
|
791
|
+
kind: "tangle_catalog",
|
|
792
|
+
connectors,
|
|
793
|
+
startAuth: options.startAuth,
|
|
794
|
+
completeAuth: options.completeAuth,
|
|
795
|
+
executeAction: async ({ connection, request, connector, action }) => {
|
|
796
|
+
const importedEntry = byEntry.get(connector.id);
|
|
797
|
+
if (!importedEntry) {
|
|
798
|
+
throw new IntegrationError(`Tangle catalog entry ${connector.id} not found.`, "connector_not_found");
|
|
799
|
+
}
|
|
800
|
+
const catalogAction = importedEntry.actions.find((candidate) => candidate.id === action.id);
|
|
801
|
+
return options.executeAction({
|
|
802
|
+
connection,
|
|
803
|
+
request,
|
|
804
|
+
connector,
|
|
805
|
+
catalogEntry: sanitizeEntry(importedEntry),
|
|
806
|
+
piece: {
|
|
807
|
+
id: importedEntry.id,
|
|
808
|
+
packageName: importedEntry.npmPackage,
|
|
809
|
+
version: importedEntry.version,
|
|
810
|
+
actionId: action.id,
|
|
811
|
+
upstreamActionName: catalogAction?.upstreamName
|
|
812
|
+
}
|
|
813
|
+
});
|
|
814
|
+
},
|
|
815
|
+
subscribeTrigger: options.subscribeTrigger ? async (connection, trigger2, targetUrl) => {
|
|
816
|
+
const connector = connectors.find((candidate) => candidate.id === connection.connectorId);
|
|
817
|
+
const importedEntry = byEntry.get(connection.connectorId);
|
|
818
|
+
if (!connector || !importedEntry) {
|
|
819
|
+
throw new IntegrationError(`Tangle catalog entry ${connection.connectorId} not found.`, "connector_not_found");
|
|
820
|
+
}
|
|
821
|
+
const catalogTrigger = importedEntry.triggers.find((candidate) => candidate.id === trigger2.id);
|
|
822
|
+
return options.subscribeTrigger({
|
|
823
|
+
connection,
|
|
824
|
+
connector,
|
|
825
|
+
catalogEntry: sanitizeEntry(importedEntry),
|
|
826
|
+
trigger: trigger2,
|
|
827
|
+
targetUrl,
|
|
828
|
+
piece: {
|
|
829
|
+
id: importedEntry.id,
|
|
830
|
+
packageName: importedEntry.npmPackage,
|
|
831
|
+
version: importedEntry.version,
|
|
832
|
+
triggerId: trigger2.id,
|
|
833
|
+
upstreamTriggerName: catalogTrigger?.upstreamName
|
|
834
|
+
}
|
|
835
|
+
});
|
|
836
|
+
} : void 0,
|
|
837
|
+
unsubscribeTrigger: options.unsubscribeTrigger,
|
|
838
|
+
normalizeTriggerEvent: options.normalizeTriggerEvent
|
|
839
|
+
});
|
|
840
|
+
}
|
|
841
|
+
var extractExternalCatalogPublicCount = extractActivepiecesPublicPieceCount;
|
|
842
|
+
async function auditTangleIntegrationCatalogFreshness(options = {}) {
|
|
843
|
+
const result = await auditIntegrationCatalogFreshness(options);
|
|
844
|
+
return {
|
|
845
|
+
ok: result.ok,
|
|
846
|
+
generatedAt: result.generatedAt,
|
|
847
|
+
local: {
|
|
848
|
+
catalogEntries: result.local.activepiecesEntries,
|
|
849
|
+
catalogConnectors: result.local.activepiecesConnectors,
|
|
850
|
+
catalogActions: result.local.activepiecesActions,
|
|
851
|
+
catalogTriggers: result.local.activepiecesTriggers,
|
|
852
|
+
executableCatalogConnectors: result.local.executableActivepiecesConnectors,
|
|
853
|
+
executableCatalogActions: result.local.executableActivepiecesActions,
|
|
854
|
+
executableCatalogTriggers: result.local.executableActivepiecesTriggers,
|
|
855
|
+
executableToolDefinitions: result.local.executableToolDefinitions,
|
|
856
|
+
unsupportedExecutableConnectorIds: result.local.unsupportedExecutableConnectorIds,
|
|
857
|
+
registryEntries: result.local.registryEntries,
|
|
858
|
+
registrySummary: result.local.registrySummary,
|
|
859
|
+
conflictSamples: result.local.conflictSamples
|
|
860
|
+
},
|
|
861
|
+
upstream: result.upstream ? {
|
|
862
|
+
externalEntries: result.upstream.activepiecesPieces,
|
|
863
|
+
externalDelta: result.upstream.activepiecesDelta,
|
|
864
|
+
checkedUrl: result.upstream.checkedUrl,
|
|
865
|
+
warning: result.upstream.warning
|
|
866
|
+
} : void 0,
|
|
867
|
+
warnings: result.warnings.map((warning) => warning.replaceAll("Activepieces", "Tangle Integrations Catalog"))
|
|
868
|
+
};
|
|
869
|
+
}
|
|
870
|
+
function sanitizeEntry(entry) {
|
|
871
|
+
return {
|
|
872
|
+
id: entry.id,
|
|
873
|
+
title: entry.title,
|
|
874
|
+
description: entry.description,
|
|
875
|
+
category: entry.category,
|
|
876
|
+
auth: entry.auth,
|
|
877
|
+
authFields: entry.authFields,
|
|
878
|
+
domains: entry.domains.filter((domain) => !domain.toLowerCase().includes("activepieces")),
|
|
879
|
+
actions: entry.actions.map((action) => ({
|
|
880
|
+
id: action.id,
|
|
881
|
+
title: action.title,
|
|
882
|
+
risk: action.risk,
|
|
883
|
+
upstreamName: action.upstreamName
|
|
884
|
+
})),
|
|
885
|
+
triggers: entry.triggers.map((trigger2) => ({
|
|
886
|
+
id: trigger2.id,
|
|
887
|
+
title: trigger2.title,
|
|
888
|
+
upstreamName: trigger2.upstreamName
|
|
889
|
+
}))
|
|
890
|
+
};
|
|
891
|
+
}
|
|
892
|
+
function sanitizeConnector(connector, providerId) {
|
|
893
|
+
const metadata = connector.metadata ?? {};
|
|
894
|
+
return {
|
|
895
|
+
...connector,
|
|
896
|
+
providerId,
|
|
897
|
+
metadata: {
|
|
898
|
+
source: TANGLE_INTEGRATIONS_CATALOG_SOURCE,
|
|
899
|
+
providerId,
|
|
900
|
+
executable: metadata.executable,
|
|
901
|
+
runtime: "tangle-catalog-runtime",
|
|
902
|
+
catalogOnly: metadata.catalogOnly,
|
|
903
|
+
supportTier: metadata.supportTier,
|
|
904
|
+
catalogActionCount: metadata.catalogActionCount,
|
|
905
|
+
catalogTriggerCount: metadata.catalogTriggerCount,
|
|
906
|
+
license: metadata.license,
|
|
907
|
+
version: metadata.version,
|
|
908
|
+
domains: Array.isArray(metadata.domains) ? metadata.domains.filter((domain) => typeof domain === "string" && !domain.toLowerCase().includes("activepieces")) : void 0,
|
|
909
|
+
...metadata.overridden ? { overridden: true } : {}
|
|
910
|
+
}
|
|
911
|
+
};
|
|
912
|
+
}
|
|
913
|
+
|
|
221
914
|
// src/registry.ts
|
|
222
915
|
var DEFAULT_ALIASES = {
|
|
223
916
|
notion: "notion-database",
|
|
@@ -256,26 +949,31 @@ function buildDefaultIntegrationRegistry(options = {}) {
|
|
|
256
949
|
});
|
|
257
950
|
}
|
|
258
951
|
if (includeTangleCatalog) {
|
|
952
|
+
const tangleConnectors = options.tangleCatalogRuntimeExecutable ? buildTangleIntegrationCatalogConnectors({
|
|
953
|
+
providerId: "tangle-catalog",
|
|
954
|
+
includeCatalogActions: true,
|
|
955
|
+
executable: true
|
|
956
|
+
}) : buildActivepiecesConnectors({ providerId: "tangle-catalog" }).map((connector) => ({
|
|
957
|
+
...connector,
|
|
958
|
+
providerId: "tangle-catalog",
|
|
959
|
+
metadata: {
|
|
960
|
+
source: "tangle-integrations-catalog",
|
|
961
|
+
providerId: "tangle-catalog",
|
|
962
|
+
executable: connector.metadata?.executable,
|
|
963
|
+
runtime: "tangle-catalog-runtime",
|
|
964
|
+
catalogOnly: connector.metadata?.catalogOnly,
|
|
965
|
+
supportTier: connector.metadata?.supportTier,
|
|
966
|
+
catalogActionCount: connector.metadata?.catalogActionCount,
|
|
967
|
+
catalogTriggerCount: connector.metadata?.catalogTriggerCount,
|
|
968
|
+
license: connector.metadata?.license,
|
|
969
|
+
version: connector.metadata?.version,
|
|
970
|
+
domains: Array.isArray(connector.metadata?.domains) ? connector.metadata.domains.filter((domain) => typeof domain === "string" && !domain.toLowerCase().includes("activepieces")) : void 0,
|
|
971
|
+
...connector.metadata?.overridden ? { overridden: true } : {}
|
|
972
|
+
}
|
|
973
|
+
}));
|
|
259
974
|
sources.push({
|
|
260
975
|
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
|
-
}))
|
|
976
|
+
connectors: tangleConnectors
|
|
279
977
|
});
|
|
280
978
|
}
|
|
281
979
|
return composeIntegrationRegistry(sources);
|
|
@@ -352,12 +1050,12 @@ function registryEntry(canonicalId, candidates, precedence, aliases) {
|
|
|
352
1050
|
const primary = ordered[0];
|
|
353
1051
|
const actions = mergeActions(ordered);
|
|
354
1052
|
const triggers = mergeTriggers(ordered);
|
|
355
|
-
const scopes =
|
|
1053
|
+
const scopes = unique2(toolBindableCandidates(ordered).flatMap((candidate) => candidate.connector.scopes ?? []));
|
|
356
1054
|
const supportTier = ordered.reduce(
|
|
357
1055
|
(best, candidate) => SUPPORT_RANK[candidate.supportTier] > SUPPORT_RANK[best] ? candidate.supportTier : best,
|
|
358
1056
|
primary.supportTier
|
|
359
1057
|
);
|
|
360
|
-
const aliasesForEntry =
|
|
1058
|
+
const aliasesForEntry = unique2([
|
|
361
1059
|
...ordered.map((candidate) => candidate.connector.id),
|
|
362
1060
|
...Object.entries(aliases).filter(([, target]) => canonicalConnectorId(target, aliases) === canonicalId).map(([alias]) => alias)
|
|
363
1061
|
].map(slug).filter((id) => id && id !== canonicalId)).sort();
|
|
@@ -453,12 +1151,12 @@ function isSupportTier(value) {
|
|
|
453
1151
|
function slug(value) {
|
|
454
1152
|
return value.trim().toLowerCase().replace(/&/g, "and").replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
455
1153
|
}
|
|
456
|
-
function
|
|
1154
|
+
function unique2(values) {
|
|
457
1155
|
return [...new Set(values)];
|
|
458
1156
|
}
|
|
459
1157
|
|
|
460
1158
|
// src/audit.ts
|
|
461
|
-
import { randomUUID } from "crypto";
|
|
1159
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
462
1160
|
var InMemoryIntegrationAuditStore = class {
|
|
463
1161
|
events = [];
|
|
464
1162
|
record(event) {
|
|
@@ -472,7 +1170,7 @@ function createIntegrationAuditEvent(input) {
|
|
|
472
1170
|
const occurredAt = input.occurredAt instanceof Date ? input.occurredAt.toISOString() : input.occurredAt ?? (input.now?.() ?? /* @__PURE__ */ new Date()).toISOString();
|
|
473
1171
|
return {
|
|
474
1172
|
...input,
|
|
475
|
-
id: input.id ?? `audit_${
|
|
1173
|
+
id: input.id ?? `audit_${randomUUID2()}`,
|
|
476
1174
|
occurredAt,
|
|
477
1175
|
metadata: input.metadata ? redactUnknown(input.metadata) : void 0
|
|
478
1176
|
};
|
|
@@ -1015,7 +1713,7 @@ function statusForCode(code) {
|
|
|
1015
1713
|
if (code === "approval_denied") return 403;
|
|
1016
1714
|
if (code === "connection_revoked" || code === "connection_expired" || code === "provider_auth_failed") return 401;
|
|
1017
1715
|
if (code === "scope_missing" || code === "action_denied" || code === "passthrough_disabled") return 403;
|
|
1018
|
-
if (code === "action_not_found" || code === "manifest_invalid" || code === "input_invalid") return 400;
|
|
1716
|
+
if (code === "action_not_found" || code === "trigger_not_found" || code === "manifest_invalid" || code === "input_invalid") return 400;
|
|
1019
1717
|
if (code === "provider_rate_limited") return 429;
|
|
1020
1718
|
if (code === "provider_unavailable") return 503;
|
|
1021
1719
|
if (code === "capability_expired" || code === "capability_invalid") return 401;
|
|
@@ -1121,7 +1819,7 @@ function renderConsentSummary(manifestOrResolution, options = {}) {
|
|
|
1121
1819
|
const appName = options.appName ?? manifest.title ?? manifest.id;
|
|
1122
1820
|
const requirements = manifest.requirements;
|
|
1123
1821
|
const risk = aggregateRisk(requirements, options.connectors);
|
|
1124
|
-
const connectorIds =
|
|
1822
|
+
const connectorIds = unique3(requirements.map((requirement) => requirement.connectorId));
|
|
1125
1823
|
const first = requirements[0];
|
|
1126
1824
|
const body = first ? sentenceForRequirement(appName, first) : `${appName} does not request integrations.`;
|
|
1127
1825
|
return {
|
|
@@ -1184,7 +1882,7 @@ function humanList(values) {
|
|
|
1184
1882
|
function titleize(value) {
|
|
1185
1883
|
return value.split(/[-_.]/g).filter(Boolean).map((part) => part[0].toUpperCase() + part.slice(1)).join(" ");
|
|
1186
1884
|
}
|
|
1187
|
-
function
|
|
1885
|
+
function unique3(values) {
|
|
1188
1886
|
return [...new Set(values)];
|
|
1189
1887
|
}
|
|
1190
1888
|
|
|
@@ -1808,8 +2506,8 @@ function inferIntegrationManifestFromTools(options) {
|
|
|
1808
2506
|
if (existing) {
|
|
1809
2507
|
byConnector.set(id, {
|
|
1810
2508
|
...existing,
|
|
1811
|
-
requiredActions:
|
|
1812
|
-
requiredScopes:
|
|
2509
|
+
requiredActions: unique4([...existing.requiredActions ?? [], action]),
|
|
2510
|
+
requiredScopes: unique4([...existing.requiredScopes ?? [], ...typeof item === "string" ? [] : item.scopes ?? []])
|
|
1813
2511
|
});
|
|
1814
2512
|
} else {
|
|
1815
2513
|
byConnector.set(id, {
|
|
@@ -1863,7 +2561,7 @@ function defaultReason(connectorId, mode) {
|
|
|
1863
2561
|
if (connectorId === "google-calendar" && mode === "write") return "Create or update calendar events after user approval.";
|
|
1864
2562
|
return `${mode === "read" ? "Read from" : mode === "write" ? "Write to" : "Subscribe to"} ${connectorId} for this app.`;
|
|
1865
2563
|
}
|
|
1866
|
-
function
|
|
2564
|
+
function unique4(values) {
|
|
1867
2565
|
return [...new Set(values)];
|
|
1868
2566
|
}
|
|
1869
2567
|
|
|
@@ -1898,7 +2596,7 @@ function validateProviderPassthroughRequest(input, policy) {
|
|
|
1898
2596
|
}
|
|
1899
2597
|
|
|
1900
2598
|
// src/policy.ts
|
|
1901
|
-
import { randomUUID as
|
|
2599
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
1902
2600
|
var StaticIntegrationPolicyEngine = class {
|
|
1903
2601
|
rules;
|
|
1904
2602
|
defaultReadEffect;
|
|
@@ -1941,7 +2639,7 @@ function buildApprovalRequest(ctx, reason, requestedAt) {
|
|
|
1941
2639
|
throw new Error("Cannot build approval request without an action descriptor.");
|
|
1942
2640
|
}
|
|
1943
2641
|
return {
|
|
1944
|
-
id: `approval_${
|
|
2642
|
+
id: `approval_${randomUUID3()}`,
|
|
1945
2643
|
connectionId: ctx.connection.id,
|
|
1946
2644
|
providerId: ctx.connection.providerId,
|
|
1947
2645
|
connectorId: ctx.connection.connectorId,
|
|
@@ -1966,11 +2664,11 @@ function ruleMatches(rule, ctx) {
|
|
|
1966
2664
|
if (rule.connectorId && rule.connectorId !== ctx.connection.connectorId) return false;
|
|
1967
2665
|
if (rule.action && rule.action !== ctx.request.action) return false;
|
|
1968
2666
|
if (rule.risk && rule.risk !== ctx.action.risk) return false;
|
|
1969
|
-
if (rule.maxRisk &&
|
|
2667
|
+
if (rule.maxRisk && riskRank2(ctx.action.risk) > riskRank2(rule.maxRisk)) return false;
|
|
1970
2668
|
if (rule.dataClass && rule.dataClass !== ctx.action.dataClass) return false;
|
|
1971
2669
|
return true;
|
|
1972
2670
|
}
|
|
1973
|
-
function
|
|
2671
|
+
function riskRank2(risk) {
|
|
1974
2672
|
if (risk === "read") return 0;
|
|
1975
2673
|
if (risk === "write") return 1;
|
|
1976
2674
|
return 2;
|
|
@@ -2196,7 +2894,7 @@ function _resetPendingFlowsForTests() {
|
|
|
2196
2894
|
}
|
|
2197
2895
|
|
|
2198
2896
|
// src/connectors/webhooks.ts
|
|
2199
|
-
import { createHmac, timingSafeEqual } from "crypto";
|
|
2897
|
+
import { createHmac as createHmac2, timingSafeEqual as timingSafeEqual2 } from "crypto";
|
|
2200
2898
|
var DEFAULT_SIGNATURE_TOLERANCE_SECONDS = 5 * 60;
|
|
2201
2899
|
function parseStripeSignatureHeader(header) {
|
|
2202
2900
|
const acc = { sigs: [] };
|
|
@@ -2221,12 +2919,12 @@ function verifyStripeSignature(rawBody, signatureHeader, secret, options = {}) {
|
|
|
2221
2919
|
const tolerance = options.toleranceSeconds ?? DEFAULT_SIGNATURE_TOLERANCE_SECONDS;
|
|
2222
2920
|
const now = options.now ?? Math.floor(Date.now() / 1e3);
|
|
2223
2921
|
if (Math.abs(now - parsed.t) > tolerance) return false;
|
|
2224
|
-
const expected =
|
|
2922
|
+
const expected = createHmac2("sha256", secret).update(`${parsed.t}.${rawBody}`).digest("hex");
|
|
2225
2923
|
const expectedBuf = Buffer.from(expected, "utf8");
|
|
2226
2924
|
for (const sig of parsed.sigs) {
|
|
2227
2925
|
const sigBuf = Buffer.from(sig, "utf8");
|
|
2228
2926
|
if (sigBuf.length !== expectedBuf.length) continue;
|
|
2229
|
-
if (
|
|
2927
|
+
if (timingSafeEqual2(sigBuf, expectedBuf)) return true;
|
|
2230
2928
|
}
|
|
2231
2929
|
return false;
|
|
2232
2930
|
}
|
|
@@ -2237,11 +2935,11 @@ function verifySlackSignature(rawBody, signatureHeader, timestampHeader, secret,
|
|
|
2237
2935
|
const tolerance = options.toleranceSeconds ?? DEFAULT_SIGNATURE_TOLERANCE_SECONDS;
|
|
2238
2936
|
const now = options.now ?? Math.floor(Date.now() / 1e3);
|
|
2239
2937
|
if (Math.abs(now - ts) > tolerance) return false;
|
|
2240
|
-
const expected = "v0=" +
|
|
2938
|
+
const expected = "v0=" + createHmac2("sha256", secret).update(`v0:${ts}:${rawBody}`).digest("hex");
|
|
2241
2939
|
const expectedBuf = Buffer.from(expected, "utf8");
|
|
2242
2940
|
const sigBuf = Buffer.from(signatureHeader, "utf8");
|
|
2243
2941
|
if (sigBuf.length !== expectedBuf.length) return false;
|
|
2244
|
-
return
|
|
2942
|
+
return timingSafeEqual2(sigBuf, expectedBuf);
|
|
2245
2943
|
}
|
|
2246
2944
|
function verifyHmacSignature(rawBody, signatureHeader, secret, options = {}) {
|
|
2247
2945
|
const algorithm = options.algorithm ?? "sha256";
|
|
@@ -2253,11 +2951,11 @@ function verifyHmacSignature(rawBody, signatureHeader, secret, options = {}) {
|
|
|
2253
2951
|
candidate = candidate.slice(prefix.length);
|
|
2254
2952
|
}
|
|
2255
2953
|
if (lower) candidate = candidate.toLowerCase();
|
|
2256
|
-
const expected =
|
|
2954
|
+
const expected = createHmac2(algorithm, secret).update(rawBody).digest("hex");
|
|
2257
2955
|
const expectedBuf = Buffer.from(expected, "utf8");
|
|
2258
2956
|
const sigBuf = Buffer.from(candidate, "utf8");
|
|
2259
2957
|
if (sigBuf.length !== expectedBuf.length) return false;
|
|
2260
|
-
return
|
|
2958
|
+
return timingSafeEqual2(sigBuf, expectedBuf);
|
|
2261
2959
|
}
|
|
2262
2960
|
function verifyTwilioSignature(input, options = {}) {
|
|
2263
2961
|
if (!input.authToken) {
|
|
@@ -2267,11 +2965,11 @@ function verifyTwilioSignature(input, options = {}) {
|
|
|
2267
2965
|
if (!signature || Array.isArray(signature)) return false;
|
|
2268
2966
|
if (!input.fullUrl) return false;
|
|
2269
2967
|
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 =
|
|
2968
|
+
const expected = createHmac2("sha1", input.authToken).update(data).digest("base64");
|
|
2271
2969
|
const expectedBuf = Buffer.from(expected);
|
|
2272
2970
|
const sigBuf = Buffer.from(signature);
|
|
2273
2971
|
if (expectedBuf.length !== sigBuf.length) return false;
|
|
2274
|
-
return
|
|
2972
|
+
return timingSafeEqual2(expectedBuf, sigBuf);
|
|
2275
2973
|
}
|
|
2276
2974
|
function firstHeader(headers, name) {
|
|
2277
2975
|
const v = headers[name] ?? headers[name.toLowerCase()] ?? Object.entries(headers).find(([key]) => key.toLowerCase() === name.toLowerCase())?.[1];
|
|
@@ -4633,7 +5331,7 @@ function readApiKey(creds) {
|
|
|
4633
5331
|
}
|
|
4634
5332
|
|
|
4635
5333
|
// src/connectors/adapters/webhook.ts
|
|
4636
|
-
import { createHmac as
|
|
5334
|
+
import { createHmac as createHmac3 } from "crypto";
|
|
4637
5335
|
var webhookConnector = {
|
|
4638
5336
|
manifest: {
|
|
4639
5337
|
kind: "webhook",
|
|
@@ -4747,7 +5445,7 @@ function signHeaders(creds, body, idempotencyKey) {
|
|
|
4747
5445
|
"x-phony-idempotency-key": idempotencyKey
|
|
4748
5446
|
};
|
|
4749
5447
|
if (creds.kind === "hmac" && typeof creds.secret === "string" && creds.secret.length > 0) {
|
|
4750
|
-
const sig =
|
|
5448
|
+
const sig = createHmac3("sha256", creds.secret).update(`${ts}.${body}`).digest("hex");
|
|
4751
5449
|
headers["x-phony-signature"] = `sha256=${sig}`;
|
|
4752
5450
|
}
|
|
4753
5451
|
return headers;
|
|
@@ -5180,188 +5878,37 @@ var salesforceConnector = declarativeRestConnector({
|
|
|
5180
5878
|
properties: { objectName: { type: "string" }, recordId: { type: "string" } },
|
|
5181
5879
|
required: ["objectName", "recordId"]
|
|
5182
5880
|
},
|
|
5183
|
-
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 });
|
|
5881
|
+
request: { method: "GET", path: "/services/data/v61.0/sobjects/{objectName}/{recordId}" },
|
|
5882
|
+
requiredScopes: ["api"]
|
|
5346
5883
|
},
|
|
5347
|
-
|
|
5348
|
-
|
|
5349
|
-
|
|
5350
|
-
|
|
5351
|
-
|
|
5352
|
-
|
|
5353
|
-
|
|
5354
|
-
|
|
5355
|
-
|
|
5356
|
-
|
|
5357
|
-
|
|
5358
|
-
|
|
5359
|
-
return options.subscribeTrigger(connection, trigger2, targetUrl);
|
|
5884
|
+
{
|
|
5885
|
+
name: "records.create",
|
|
5886
|
+
class: "mutation",
|
|
5887
|
+
description: "Create a Salesforce sObject record.",
|
|
5888
|
+
parameters: {
|
|
5889
|
+
type: "object",
|
|
5890
|
+
properties: { objectName: { type: "string" }, fields: { type: "object" } },
|
|
5891
|
+
required: ["objectName", "fields"]
|
|
5892
|
+
},
|
|
5893
|
+
request: { method: "POST", path: "/services/data/v61.0/sobjects/{objectName}", body: "{fields}" },
|
|
5894
|
+
cas: "native-idempotency",
|
|
5895
|
+
requiredScopes: ["api"]
|
|
5360
5896
|
},
|
|
5361
|
-
|
|
5362
|
-
|
|
5363
|
-
|
|
5364
|
-
|
|
5897
|
+
{
|
|
5898
|
+
name: "records.update",
|
|
5899
|
+
class: "mutation",
|
|
5900
|
+
description: "Update a Salesforce sObject record.",
|
|
5901
|
+
parameters: {
|
|
5902
|
+
type: "object",
|
|
5903
|
+
properties: { objectName: { type: "string" }, recordId: { type: "string" }, fields: { type: "object" } },
|
|
5904
|
+
required: ["objectName", "recordId", "fields"]
|
|
5905
|
+
},
|
|
5906
|
+
request: { method: "PATCH", path: "/services/data/v61.0/sobjects/{objectName}/{recordId}", body: "{fields}" },
|
|
5907
|
+
cas: "etag-if-match",
|
|
5908
|
+
requiredScopes: ["api"]
|
|
5909
|
+
}
|
|
5910
|
+
]
|
|
5911
|
+
});
|
|
5365
5912
|
|
|
5366
5913
|
// src/sandbox.ts
|
|
5367
5914
|
function buildIntegrationInvocationEnvelope(input) {
|
|
@@ -5386,868 +5933,417 @@ function invocationRequestFromEnvelope(envelope) {
|
|
|
5386
5933
|
input: envelope.input,
|
|
5387
5934
|
idempotencyKey: envelope.idempotencyKey,
|
|
5388
5935
|
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
|
-
}
|
|
5936
|
+
metadata: envelope.metadata
|
|
5601
5937
|
};
|
|
5602
5938
|
}
|
|
5603
|
-
function
|
|
5604
|
-
|
|
5605
|
-
|
|
5606
|
-
|
|
5607
|
-
|
|
5608
|
-
|
|
5609
|
-
|
|
5610
|
-
|
|
5611
|
-
|
|
5612
|
-
|
|
5613
|
-
|
|
5614
|
-
|
|
5615
|
-
|
|
5616
|
-
|
|
5617
|
-
|
|
5618
|
-
|
|
5619
|
-
|
|
5620
|
-
|
|
5621
|
-
|
|
5622
|
-
|
|
5623
|
-
|
|
5624
|
-
|
|
5625
|
-
|
|
5626
|
-
|
|
5627
|
-
|
|
5628
|
-
|
|
5939
|
+
function validateIntegrationInvocationEnvelope(envelope, options = {}) {
|
|
5940
|
+
if (!envelope || typeof envelope !== "object") throw new Error("Integration invocation envelope is required.");
|
|
5941
|
+
if (envelope.kind !== "integration.invocation") throw new Error("Invalid integration invocation envelope kind.");
|
|
5942
|
+
if (!isNonEmptyString(envelope.capabilityToken)) throw new Error("Integration invocation envelope is missing capabilityToken.");
|
|
5943
|
+
if (!isNonEmptyString(envelope.toolName)) throw new Error("Integration invocation envelope is missing toolName.");
|
|
5944
|
+
if (!isNonEmptyString(envelope.action)) throw new Error("Integration invocation envelope is missing action.");
|
|
5945
|
+
if (!isNonEmptyString(envelope.idempotencyKey)) throw new Error("Integration invocation envelope is missing idempotencyKey.");
|
|
5946
|
+
if (envelope.metadata !== void 0 && !isPlainRecord(envelope.metadata)) {
|
|
5947
|
+
throw new Error("Integration invocation envelope metadata must be an object.");
|
|
5948
|
+
}
|
|
5949
|
+
const parsed = parseIntegrationToolName(envelope.toolName);
|
|
5950
|
+
if (parsed.actionId !== envelope.action) {
|
|
5951
|
+
throw new Error(`Integration invocation action ${envelope.action} does not match tool ${parsed.actionId}.`);
|
|
5952
|
+
}
|
|
5953
|
+
const inputBytes = Buffer.byteLength(JSON.stringify(envelope.input ?? null), "utf8");
|
|
5954
|
+
const maxInputBytes = options.maxInputBytes ?? 256 * 1024;
|
|
5955
|
+
if (inputBytes > maxInputBytes) {
|
|
5956
|
+
throw new Error(`Integration invocation input exceeds ${maxInputBytes} bytes.`);
|
|
5957
|
+
}
|
|
5958
|
+
if (options.requireKnownTool || options.connectors) {
|
|
5959
|
+
if (!options.connectors) throw new Error("connectors are required when requireKnownTool is true.");
|
|
5960
|
+
const connector = options.connectors.find(
|
|
5961
|
+
(candidate) => candidate.providerId === parsed.providerId && candidate.id === parsed.connectorId
|
|
5962
|
+
);
|
|
5963
|
+
const action = connector?.actions.find((candidate) => candidate.id === parsed.actionId);
|
|
5964
|
+
if (!connector || !action) throw new Error(`Unknown integration tool ${envelope.toolName}.`);
|
|
5629
5965
|
}
|
|
5966
|
+
}
|
|
5967
|
+
function redactInvocationEnvelope(envelope) {
|
|
5630
5968
|
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
|
-
}
|
|
5969
|
+
...envelope,
|
|
5970
|
+
capabilityToken: "[REDACTED]",
|
|
5971
|
+
input: redactUnknown4(envelope.input)
|
|
5643
5972
|
};
|
|
5644
5973
|
}
|
|
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;
|
|
5974
|
+
function redactCapability(capability) {
|
|
5975
|
+
return {
|
|
5976
|
+
...capability,
|
|
5977
|
+
metadata: redactUnknown4(capability.metadata)
|
|
5978
|
+
};
|
|
5675
5979
|
}
|
|
5676
|
-
|
|
5677
|
-
const
|
|
5678
|
-
if (!
|
|
5679
|
-
|
|
5680
|
-
|
|
5980
|
+
function normalizeIntegrationResult(result) {
|
|
5981
|
+
const output = result.output;
|
|
5982
|
+
if (!result.ok && output?.approvalRequired === true && output.approval) {
|
|
5983
|
+
return {
|
|
5984
|
+
status: "approval_required",
|
|
5985
|
+
action: result.action,
|
|
5986
|
+
approval: output.approval,
|
|
5987
|
+
metadata: result.metadata
|
|
5988
|
+
};
|
|
5681
5989
|
}
|
|
5682
|
-
|
|
5683
|
-
function normalizeActions(actions, fallbackScopes) {
|
|
5684
|
-
return actions.map((action) => {
|
|
5685
|
-
const id = slug2(action.id ?? action.key ?? action.name ?? action.title ?? "");
|
|
5990
|
+
if (!result.ok) {
|
|
5686
5991
|
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
|
|
5992
|
+
status: "failed",
|
|
5993
|
+
action: result.action,
|
|
5994
|
+
error: String(result.output ?? result.warnings?.[0] ?? "integration action failed"),
|
|
5995
|
+
metadata: result.metadata
|
|
5700
5996
|
};
|
|
5701
|
-
}
|
|
5997
|
+
}
|
|
5998
|
+
return {
|
|
5999
|
+
status: "ok",
|
|
6000
|
+
action: result.action,
|
|
6001
|
+
output: result.output,
|
|
6002
|
+
metadata: result.metadata
|
|
6003
|
+
};
|
|
5702
6004
|
}
|
|
5703
|
-
function
|
|
5704
|
-
|
|
5705
|
-
|
|
6005
|
+
async function dispatchIntegrationInvocation(envelope, options) {
|
|
6006
|
+
try {
|
|
6007
|
+
validateIntegrationInvocationEnvelope(envelope, options);
|
|
6008
|
+
const result = await options.hub.invokeWithCapability(
|
|
6009
|
+
envelope.capabilityToken,
|
|
6010
|
+
invocationRequestFromEnvelope(envelope)
|
|
6011
|
+
);
|
|
6012
|
+
return normalizeIntegrationResult(result);
|
|
6013
|
+
} catch (error) {
|
|
5706
6014
|
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
|
|
6015
|
+
status: "failed",
|
|
6016
|
+
action: typeof envelope?.action === "string" ? envelope.action : "unknown",
|
|
6017
|
+
error: error instanceof Error ? error.message : "Integration invocation failed."
|
|
5717
6018
|
};
|
|
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";
|
|
6019
|
+
}
|
|
5791
6020
|
}
|
|
5792
|
-
|
|
5793
|
-
|
|
6021
|
+
var IntegrationSandboxHost = class {
|
|
6022
|
+
options;
|
|
6023
|
+
constructor(options) {
|
|
6024
|
+
this.options = options;
|
|
6025
|
+
}
|
|
6026
|
+
dispatch(envelope) {
|
|
6027
|
+
return dispatchIntegrationInvocation(envelope, this.options);
|
|
6028
|
+
}
|
|
6029
|
+
};
|
|
6030
|
+
function redactUnknown4(value) {
|
|
6031
|
+
if (Array.isArray(value)) return value.map(redactUnknown4);
|
|
6032
|
+
if (!value || typeof value !== "object") return value;
|
|
6033
|
+
const out = {};
|
|
6034
|
+
for (const [key, child] of Object.entries(value)) {
|
|
6035
|
+
if (/token|secret|password|authorization|api[_-]?key|credential/i.test(key)) {
|
|
6036
|
+
out[key] = "[REDACTED]";
|
|
6037
|
+
} else {
|
|
6038
|
+
out[key] = redactUnknown4(child);
|
|
6039
|
+
}
|
|
6040
|
+
}
|
|
6041
|
+
return out;
|
|
5794
6042
|
}
|
|
5795
|
-
function
|
|
5796
|
-
return
|
|
6043
|
+
function isNonEmptyString(value) {
|
|
6044
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
5797
6045
|
}
|
|
5798
|
-
function
|
|
5799
|
-
return
|
|
6046
|
+
function isPlainRecord(value) {
|
|
6047
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
5800
6048
|
}
|
|
5801
6049
|
|
|
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
|
-
}
|
|
6050
|
+
// src/importers.ts
|
|
6051
|
+
var HTTP_METHODS = /* @__PURE__ */ new Set(["get", "post", "put", "patch", "delete"]);
|
|
6052
|
+
function importOpenApiConnector(document, options) {
|
|
6053
|
+
const actions = [];
|
|
6054
|
+
for (const [path, methods] of Object.entries(document.paths ?? {})) {
|
|
6055
|
+
for (const [method, rawOperation] of Object.entries(methods)) {
|
|
6056
|
+
const normalizedMethod = method.toLowerCase();
|
|
6057
|
+
if (!HTTP_METHODS.has(normalizedMethod) || !isObject(rawOperation)) continue;
|
|
6058
|
+
const operation = rawOperation;
|
|
6059
|
+
const operationId = operation.operationId ?? `${normalizedMethod}_${path.replace(/[^a-zA-Z0-9]+/g, "_").replace(/^_+|_+$/g, "")}`;
|
|
6060
|
+
actions.push({
|
|
6061
|
+
id: operationId,
|
|
6062
|
+
title: operation.summary ?? titleFromId(operationId),
|
|
6063
|
+
risk: riskFromHttpMethod(normalizedMethod, operation, options.defaultRisk),
|
|
6064
|
+
requiredScopes: scopesFromOpenApiOperation(operation, options.scopes ?? []),
|
|
6065
|
+
dataClass: options.dataClass ?? "private",
|
|
6066
|
+
description: operation.description ?? operation.summary ?? `${normalizedMethod.toUpperCase()} ${path}`,
|
|
6067
|
+
approvalRequired: riskFromHttpMethod(normalizedMethod, operation, options.defaultRisk) !== "read",
|
|
6068
|
+
inputSchema: openApiInputSchema(path, normalizedMethod, operation),
|
|
6069
|
+
outputSchema: operation.responses
|
|
5835
6070
|
});
|
|
5836
6071
|
}
|
|
5837
|
-
}
|
|
6072
|
+
}
|
|
6073
|
+
return connectorFromActions(options, actions);
|
|
5838
6074
|
}
|
|
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." }
|
|
6075
|
+
function importGraphqlConnector(operations, options) {
|
|
6076
|
+
return connectorFromActions(options, operations.map((operation) => ({
|
|
6077
|
+
id: operation.name,
|
|
6078
|
+
title: titleFromId(operation.name),
|
|
6079
|
+
risk: operation.kind === "query" ? "read" : options.defaultRisk ?? "write",
|
|
6080
|
+
requiredScopes: operation.requiredScopes ?? options.scopes ?? [],
|
|
6081
|
+
dataClass: options.dataClass ?? "private",
|
|
6082
|
+
description: operation.description,
|
|
6083
|
+
approvalRequired: operation.kind === "mutation",
|
|
6084
|
+
inputSchema: operation.inputSchema,
|
|
6085
|
+
outputSchema: operation.outputSchema
|
|
6086
|
+
})));
|
|
6087
|
+
}
|
|
6088
|
+
function importMcpConnector(catalog, options) {
|
|
6089
|
+
return connectorFromActions(options, catalog.tools.map((tool) => {
|
|
6090
|
+
const risk = riskFromMcpTool(tool, options.defaultRisk);
|
|
6091
|
+
return {
|
|
6092
|
+
id: tool.name,
|
|
6093
|
+
title: tool.annotations?.title ?? titleFromId(tool.name),
|
|
6094
|
+
risk,
|
|
6095
|
+
requiredScopes: options.scopes ?? [],
|
|
6096
|
+
dataClass: options.dataClass ?? "private",
|
|
6097
|
+
description: tool.description,
|
|
6098
|
+
approvalRequired: risk !== "read",
|
|
6099
|
+
inputSchema: tool.inputSchema
|
|
5876
6100
|
};
|
|
5877
|
-
};
|
|
6101
|
+
}));
|
|
5878
6102
|
}
|
|
5879
|
-
function
|
|
6103
|
+
function connectorFromActions(options, actions) {
|
|
6104
|
+
const scopes = unique5([
|
|
6105
|
+
...options.scopes ?? [],
|
|
6106
|
+
...actions.flatMap((action) => action.requiredScopes)
|
|
6107
|
+
]);
|
|
5880
6108
|
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
|
-
}
|
|
6109
|
+
id: options.connectorId,
|
|
6110
|
+
providerId: options.providerId,
|
|
6111
|
+
title: options.connectorTitle,
|
|
6112
|
+
category: options.category ?? "other",
|
|
6113
|
+
auth: options.auth ?? "custom",
|
|
6114
|
+
scopes,
|
|
6115
|
+
actions,
|
|
6116
|
+
metadata: { source: "catalog-importer" }
|
|
5900
6117
|
};
|
|
5901
6118
|
}
|
|
5902
|
-
function
|
|
5903
|
-
|
|
6119
|
+
function riskFromHttpMethod(method, operation, fallback) {
|
|
6120
|
+
if (method === "get") return "read";
|
|
6121
|
+
if (method === "delete") return "destructive";
|
|
6122
|
+
const text = `${operation.operationId ?? ""} ${operation.summary ?? ""} ${operation.description ?? ""}`.toLowerCase();
|
|
6123
|
+
if (/\b(delete|remove|destroy|cancel|void|revoke|drop)\b/.test(text)) return "destructive";
|
|
6124
|
+
return fallback && fallback !== "read" ? fallback : "write";
|
|
5904
6125
|
}
|
|
5905
|
-
function
|
|
5906
|
-
if (
|
|
5907
|
-
|
|
5908
|
-
const
|
|
5909
|
-
|
|
5910
|
-
|
|
6126
|
+
function riskFromMcpTool(tool, fallback) {
|
|
6127
|
+
if (tool.annotations?.destructiveHint) return "destructive";
|
|
6128
|
+
if (tool.annotations?.readOnlyHint) return "read";
|
|
6129
|
+
const text = `${tool.name} ${tool.description ?? ""}`.toLowerCase();
|
|
6130
|
+
if (/\b(delete|remove|destroy|cancel|void|revoke|drop)\b/.test(text)) return "destructive";
|
|
6131
|
+
if (/\b(get|list|read|search|find|fetch|query)\b/.test(text)) return "read";
|
|
6132
|
+
return fallback ?? "write";
|
|
5911
6133
|
}
|
|
5912
|
-
function
|
|
5913
|
-
const
|
|
5914
|
-
|
|
5915
|
-
const normalizedPath = path.startsWith("/") ? path : `/${path}`;
|
|
5916
|
-
const signatureHeader = options.signatureHeader ?? TANGLE_CATALOG_RUNTIME_SIGNATURE_HEADER;
|
|
5917
|
-
const fetchImpl = options.fetchImpl ?? fetch;
|
|
5918
|
-
const requestId = options.requestId ?? (() => `tcat_${randomUUID3()}`);
|
|
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
|
-
};
|
|
6134
|
+
function scopesFromOpenApiOperation(operation, fallback) {
|
|
6135
|
+
const scopes = (operation.security ?? []).flatMap((entry) => Object.values(entry).flat());
|
|
6136
|
+
return unique5(scopes.length > 0 ? scopes : fallback);
|
|
5946
6137
|
}
|
|
5947
|
-
function
|
|
6138
|
+
function openApiInputSchema(path, method, operation) {
|
|
5948
6139
|
return {
|
|
5949
|
-
|
|
5950
|
-
|
|
5951
|
-
|
|
5952
|
-
|
|
5953
|
-
|
|
5954
|
-
|
|
5955
|
-
|
|
5956
|
-
auth: invocation.connector.auth,
|
|
5957
|
-
scopes: invocation.connector.scopes,
|
|
5958
|
-
metadata: invocation.connector.metadata
|
|
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
|
|
6140
|
+
type: "object",
|
|
6141
|
+
additionalProperties: true,
|
|
6142
|
+
properties: {
|
|
6143
|
+
path: { const: path },
|
|
6144
|
+
method: { const: method.toUpperCase() },
|
|
6145
|
+
parameters: { type: "object", additionalProperties: true },
|
|
6146
|
+
body: operation.requestBody ?? { type: "object", additionalProperties: true }
|
|
5967
6147
|
}
|
|
5968
6148
|
};
|
|
5969
6149
|
}
|
|
5970
|
-
|
|
5971
|
-
|
|
5972
|
-
|
|
5973
|
-
|
|
5974
|
-
|
|
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;
|
|
6150
|
+
function titleFromId(id) {
|
|
6151
|
+
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(" ");
|
|
6152
|
+
}
|
|
6153
|
+
function isObject(value) {
|
|
6154
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
5979
6155
|
}
|
|
5980
|
-
function
|
|
5981
|
-
|
|
5982
|
-
const integrationMatch = html.match(/([0-9,]+)\+?\s+Integrations/i);
|
|
5983
|
-
return parseCount(showingMatch?.[1]) ?? parseCount(integrationMatch?.[1]);
|
|
6156
|
+
function unique5(values) {
|
|
6157
|
+
return [...new Set(values)];
|
|
5984
6158
|
}
|
|
5985
|
-
|
|
5986
|
-
|
|
5987
|
-
|
|
5988
|
-
const
|
|
5989
|
-
|
|
5990
|
-
|
|
5991
|
-
|
|
5992
|
-
|
|
5993
|
-
|
|
5994
|
-
|
|
5995
|
-
|
|
5996
|
-
|
|
5997
|
-
|
|
5998
|
-
|
|
5999
|
-
connectors: executableActivepiecesConnectors
|
|
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
|
|
6159
|
+
|
|
6160
|
+
// src/gateway-catalog.ts
|
|
6161
|
+
function createGatewayCatalogProvider(options) {
|
|
6162
|
+
const now = options.now ?? (() => /* @__PURE__ */ new Date());
|
|
6163
|
+
let cachedAt = 0;
|
|
6164
|
+
let cached;
|
|
6165
|
+
async function listConnectors() {
|
|
6166
|
+
const ttl = options.cacheTtlMs ?? 6e4;
|
|
6167
|
+
const current = now().getTime();
|
|
6168
|
+
if (cached && current - cachedAt < ttl) return cached;
|
|
6169
|
+
const entries = await options.fetchCatalog();
|
|
6170
|
+
cached = normalizeGatewayCatalog(entries, {
|
|
6171
|
+
providerId: options.id,
|
|
6172
|
+
providerKind: options.kind
|
|
6031
6173
|
});
|
|
6174
|
+
cachedAt = current;
|
|
6175
|
+
return cached;
|
|
6032
6176
|
}
|
|
6033
6177
|
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
|
|
6178
|
+
id: options.id,
|
|
6179
|
+
kind: options.kind,
|
|
6180
|
+
listConnectors,
|
|
6181
|
+
startAuth: options.startAuth,
|
|
6182
|
+
completeAuth: options.completeAuth,
|
|
6183
|
+
async invokeAction(connection, request) {
|
|
6184
|
+
if (!options.invokeAction) {
|
|
6185
|
+
throw new IntegrationError(`Gateway provider ${options.id} does not implement action invocation.`, "action_not_found");
|
|
6186
|
+
}
|
|
6187
|
+
await assertKnownGatewayAction(await listConnectors(), connection.connectorId, request.action);
|
|
6188
|
+
return options.invokeAction(connection, request);
|
|
6189
|
+
}
|
|
6064
6190
|
};
|
|
6065
6191
|
}
|
|
6066
|
-
|
|
6067
|
-
|
|
6068
|
-
|
|
6069
|
-
|
|
6070
|
-
|
|
6192
|
+
function normalizeGatewayCatalog(entries, options) {
|
|
6193
|
+
const out = [];
|
|
6194
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6195
|
+
for (const entry of entries) {
|
|
6196
|
+
const id = slug2(entry.id ?? entry.key ?? entry.name ?? entry.title ?? "");
|
|
6197
|
+
if (!id || seen.has(id)) continue;
|
|
6198
|
+
seen.add(id);
|
|
6199
|
+
const title = entry.title ?? entry.name ?? titleFromId2(id);
|
|
6200
|
+
const actions = normalizeActions(entry.actions ?? [], entry.scopes ?? []);
|
|
6201
|
+
out.push({
|
|
6202
|
+
id,
|
|
6203
|
+
providerId: options.providerId,
|
|
6204
|
+
title,
|
|
6205
|
+
category: normalizeCategory(entry.category),
|
|
6206
|
+
auth: normalizeAuth(entry.auth),
|
|
6207
|
+
scopes: unique6([
|
|
6208
|
+
...entry.scopes ?? [],
|
|
6209
|
+
...actions.flatMap((action) => action.requiredScopes)
|
|
6210
|
+
]),
|
|
6211
|
+
actions: actions.length > 0 ? actions : defaultActionsFor(entry.category, entry.scopes ?? []),
|
|
6212
|
+
triggers: normalizeTriggers(entry.triggers ?? [], entry.scopes ?? []),
|
|
6213
|
+
metadata: {
|
|
6214
|
+
...entry.metadata ?? {},
|
|
6215
|
+
source: "gateway-catalog",
|
|
6216
|
+
providerKind: options.providerKind,
|
|
6217
|
+
executable: true
|
|
6218
|
+
}
|
|
6071
6219
|
});
|
|
6072
|
-
|
|
6073
|
-
|
|
6074
|
-
|
|
6075
|
-
|
|
6076
|
-
|
|
6077
|
-
|
|
6078
|
-
|
|
6079
|
-
}
|
|
6080
|
-
|
|
6081
|
-
|
|
6082
|
-
|
|
6083
|
-
|
|
6084
|
-
|
|
6085
|
-
);
|
|
6086
|
-
}
|
|
6220
|
+
}
|
|
6221
|
+
return out;
|
|
6222
|
+
}
|
|
6223
|
+
async function assertKnownGatewayAction(connectors, connectorId, actionId) {
|
|
6224
|
+
const connector = connectors.find((candidate) => candidate.id === connectorId);
|
|
6225
|
+
if (!connector) throw new IntegrationError(`Connector ${connectorId} not found.`, "connector_not_found");
|
|
6226
|
+
if (!connector.actions.some((action) => action.id === actionId)) {
|
|
6227
|
+
throw new IntegrationError(`Action ${actionId} is not defined by connector ${connectorId}.`, "action_not_found");
|
|
6228
|
+
}
|
|
6229
|
+
}
|
|
6230
|
+
function normalizeActions(actions, fallbackScopes) {
|
|
6231
|
+
return actions.map((action) => {
|
|
6232
|
+
const id = slug2(action.id ?? action.key ?? action.name ?? action.title ?? "");
|
|
6087
6233
|
return {
|
|
6088
|
-
|
|
6089
|
-
|
|
6090
|
-
|
|
6091
|
-
|
|
6234
|
+
id,
|
|
6235
|
+
title: action.title ?? action.name ?? titleFromId2(id),
|
|
6236
|
+
risk: normalizeRisk(action.risk),
|
|
6237
|
+
requiredScopes: unique6([
|
|
6238
|
+
...action.requiredScopes ?? [],
|
|
6239
|
+
...action.scopes ?? [],
|
|
6240
|
+
...action.requiredScopes?.length || action.scopes?.length ? [] : fallbackScopes
|
|
6241
|
+
]),
|
|
6242
|
+
dataClass: normalizeDataClass(action.dataClass),
|
|
6243
|
+
description: action.description,
|
|
6244
|
+
approvalRequired: action.approvalRequired ?? normalizeRisk(action.risk) !== "read",
|
|
6245
|
+
inputSchema: action.inputSchema,
|
|
6246
|
+
outputSchema: action.outputSchema
|
|
6092
6247
|
};
|
|
6093
|
-
}
|
|
6094
|
-
|
|
6095
|
-
|
|
6248
|
+
}).filter((action) => action.id);
|
|
6249
|
+
}
|
|
6250
|
+
function normalizeTriggers(triggers, fallbackScopes) {
|
|
6251
|
+
const normalized = triggers.map((trigger2) => {
|
|
6252
|
+
const id = slug2(trigger2.id ?? trigger2.key ?? trigger2.name ?? trigger2.title ?? "");
|
|
6096
6253
|
return {
|
|
6097
|
-
|
|
6098
|
-
|
|
6254
|
+
id,
|
|
6255
|
+
title: trigger2.title ?? trigger2.name ?? titleFromId2(id),
|
|
6256
|
+
requiredScopes: unique6([
|
|
6257
|
+
...trigger2.requiredScopes ?? [],
|
|
6258
|
+
...trigger2.scopes ?? [],
|
|
6259
|
+
...trigger2.requiredScopes?.length || trigger2.scopes?.length ? [] : fallbackScopes
|
|
6260
|
+
]),
|
|
6261
|
+
dataClass: normalizeDataClass(trigger2.dataClass),
|
|
6262
|
+
description: trigger2.description,
|
|
6263
|
+
payloadSchema: trigger2.payloadSchema
|
|
6099
6264
|
};
|
|
6100
|
-
}
|
|
6265
|
+
}).filter((trigger2) => trigger2.id);
|
|
6266
|
+
return normalized.length > 0 ? normalized : void 0;
|
|
6267
|
+
}
|
|
6268
|
+
function defaultActionsFor(category, scopes) {
|
|
6269
|
+
const readScope = scopes.find((scope) => scope.endsWith(".read")) ?? scopes[0];
|
|
6270
|
+
const writeScope = scopes.find((scope) => scope.endsWith(".write")) ?? scopes[1] ?? readScope;
|
|
6271
|
+
const requiredRead = readScope ? [readScope] : [];
|
|
6272
|
+
const requiredWrite = writeScope ? [writeScope] : [];
|
|
6273
|
+
const dataClass = normalizeDataClass(category === "finance" || category === "commerce" || category === "hr" ? "sensitive" : "private");
|
|
6274
|
+
return [
|
|
6275
|
+
{
|
|
6276
|
+
id: "records.search",
|
|
6277
|
+
title: "Search records",
|
|
6278
|
+
risk: "read",
|
|
6279
|
+
requiredScopes: requiredRead,
|
|
6280
|
+
dataClass,
|
|
6281
|
+
description: "Search provider records."
|
|
6282
|
+
},
|
|
6283
|
+
{
|
|
6284
|
+
id: "records.read",
|
|
6285
|
+
title: "Read record",
|
|
6286
|
+
risk: "read",
|
|
6287
|
+
requiredScopes: requiredRead,
|
|
6288
|
+
dataClass,
|
|
6289
|
+
description: "Read a provider record."
|
|
6290
|
+
},
|
|
6291
|
+
{
|
|
6292
|
+
id: "records.upsert",
|
|
6293
|
+
title: "Upsert record",
|
|
6294
|
+
risk: "write",
|
|
6295
|
+
requiredScopes: requiredWrite,
|
|
6296
|
+
dataClass,
|
|
6297
|
+
approvalRequired: true,
|
|
6298
|
+
description: "Create or update a provider record."
|
|
6299
|
+
}
|
|
6300
|
+
];
|
|
6101
6301
|
}
|
|
6102
|
-
|
|
6103
|
-
|
|
6104
|
-
|
|
6105
|
-
|
|
6106
|
-
|
|
6107
|
-
|
|
6302
|
+
function normalizeCategory(category) {
|
|
6303
|
+
const value = slug2(category ?? "");
|
|
6304
|
+
if (value === "mail") return "email";
|
|
6305
|
+
if (value === "messaging" || value === "communication" || value === "communications") return "chat";
|
|
6306
|
+
if (value === "file" || value === "files") return "storage";
|
|
6307
|
+
if (value === "project-management" || value === "automation") return "workflow";
|
|
6308
|
+
if (value === "developer" || value === "devops") return "workflow";
|
|
6309
|
+
if (value === "support") return "crm";
|
|
6310
|
+
if ([
|
|
6311
|
+
"email",
|
|
6312
|
+
"calendar",
|
|
6313
|
+
"chat",
|
|
6314
|
+
"crm",
|
|
6315
|
+
"storage",
|
|
6316
|
+
"docs",
|
|
6317
|
+
"database",
|
|
6318
|
+
"webhook",
|
|
6319
|
+
"workflow",
|
|
6320
|
+
"internal",
|
|
6321
|
+
"other"
|
|
6322
|
+
].includes(value)) return value;
|
|
6323
|
+
return "other";
|
|
6108
6324
|
}
|
|
6109
|
-
function
|
|
6110
|
-
|
|
6111
|
-
|
|
6112
|
-
|
|
6113
|
-
|
|
6114
|
-
}));
|
|
6325
|
+
function normalizeAuth(auth) {
|
|
6326
|
+
if (auth === "oauth2") return "oauth2";
|
|
6327
|
+
if (auth === "api_key" || auth === "api-key" || auth === "apikey") return "api_key";
|
|
6328
|
+
if (auth === "none") return "none";
|
|
6329
|
+
return "custom";
|
|
6115
6330
|
}
|
|
6116
|
-
function
|
|
6117
|
-
|
|
6118
|
-
return
|
|
6119
|
-
...options,
|
|
6120
|
-
providerId
|
|
6121
|
-
}).map((connector) => sanitizeConnector(connector, providerId));
|
|
6331
|
+
function normalizeRisk(risk) {
|
|
6332
|
+
if (risk === "read" || risk === "write" || risk === "destructive") return risk;
|
|
6333
|
+
return "write";
|
|
6122
6334
|
}
|
|
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
|
-
});
|
|
6335
|
+
function normalizeDataClass(dataClass) {
|
|
6336
|
+
if (dataClass === "public" || dataClass === "internal" || dataClass === "private" || dataClass === "sensitive" || dataClass === "secret") return dataClass;
|
|
6337
|
+
return "private";
|
|
6180
6338
|
}
|
|
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
|
-
};
|
|
6339
|
+
function slug2(value) {
|
|
6340
|
+
return value.trim().toLowerCase().replace(/&/g, "and").replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
6209
6341
|
}
|
|
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
|
-
};
|
|
6342
|
+
function titleFromId2(id) {
|
|
6343
|
+
return id.split("-").filter(Boolean).map((part) => part.slice(0, 1).toUpperCase() + part.slice(1)).join(" ");
|
|
6230
6344
|
}
|
|
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
|
-
};
|
|
6345
|
+
function unique6(values) {
|
|
6346
|
+
return [...new Set(values)];
|
|
6251
6347
|
}
|
|
6252
6348
|
|
|
6253
6349
|
// src/tangle-catalog-runtime.ts
|
|
@@ -6278,6 +6374,9 @@ function createTangleCatalogRuntimeHandler(options) {
|
|
|
6278
6374
|
const parsed = parseRuntimeRequest(serialized);
|
|
6279
6375
|
if (!parsed.ok) return parsed.response;
|
|
6280
6376
|
const request = parsed.request;
|
|
6377
|
+
if (request.providerId !== request.connection.providerId) {
|
|
6378
|
+
return errorResponse(400, request.action.id, "provider_mismatch", "Request providerId does not match connection providerId.");
|
|
6379
|
+
}
|
|
6281
6380
|
const connector = byConnector.get(request.connector.id);
|
|
6282
6381
|
if (!connector) {
|
|
6283
6382
|
return errorResponse(404, request.action.id, "connector_not_found", `Connector ${request.connector.id} is not in the Tangle catalog runtime.`);
|
|
@@ -6316,6 +6415,13 @@ function createTangleCatalogInstalledPackageExecutor(options = {}) {
|
|
|
6316
6415
|
if (!packageName) {
|
|
6317
6416
|
return runtimeFailure(invocation.action.id, "runtime_not_available", `No installed runtime package is known for connector ${invocation.connector.id}.`);
|
|
6318
6417
|
}
|
|
6418
|
+
if (invocation.request.piece.packageName && invocation.request.piece.packageName !== packageName) {
|
|
6419
|
+
return runtimeFailure(
|
|
6420
|
+
invocation.action.id,
|
|
6421
|
+
"runtime_package_mismatch",
|
|
6422
|
+
`Runtime package ${invocation.request.piece.packageName} does not match catalog package ${packageName}.`
|
|
6423
|
+
);
|
|
6424
|
+
}
|
|
6319
6425
|
const loaded = await loadRuntimeModule(packageName, options.moduleLoader, moduleCache);
|
|
6320
6426
|
if (!loaded.ok) {
|
|
6321
6427
|
return runtimeFailure(invocation.action.id, "runtime_not_installed", loaded.message);
|
|
@@ -6324,7 +6430,7 @@ function createTangleCatalogInstalledPackageExecutor(options = {}) {
|
|
|
6324
6430
|
if (!piece) {
|
|
6325
6431
|
return runtimeFailure(invocation.action.id, "runtime_invalid", `Runtime package ${packageName} does not export a recognizable piece for ${invocation.connector.id}.`);
|
|
6326
6432
|
}
|
|
6327
|
-
const action = findRuntimeAction(piece, invocation, options.actionAliases);
|
|
6433
|
+
const action = findRuntimeAction(piece, invocation, options.actionAliases, options.allowFuzzyActionMatch ?? false);
|
|
6328
6434
|
if (!action?.run) {
|
|
6329
6435
|
return runtimeFailure(invocation.action.id, "action_not_implemented", `Runtime package ${packageName} does not expose executable action ${invocation.action.id}.`);
|
|
6330
6436
|
}
|
|
@@ -6350,6 +6456,56 @@ function createTangleCatalogInstalledPackageExecutor(options = {}) {
|
|
|
6350
6456
|
}
|
|
6351
6457
|
};
|
|
6352
6458
|
}
|
|
6459
|
+
async function auditTangleCatalogRuntimePackages(options = {}) {
|
|
6460
|
+
const only = options.connectorIds ? new Set(options.connectorIds) : void 0;
|
|
6461
|
+
const rows = [];
|
|
6462
|
+
const moduleCache = /* @__PURE__ */ new Map();
|
|
6463
|
+
const entries = listActivepiecesCatalogEntries().filter((entry) => entry.npmPackage && (!only || only.has(entry.id)));
|
|
6464
|
+
for (const entry of entries) {
|
|
6465
|
+
const packageName = entry.npmPackage;
|
|
6466
|
+
const base = {
|
|
6467
|
+
connectorId: entry.id,
|
|
6468
|
+
packageName,
|
|
6469
|
+
actionMappingsTotal: entry.actions.length,
|
|
6470
|
+
triggerMappingsTotal: entry.triggers.length
|
|
6471
|
+
};
|
|
6472
|
+
const loaded = await loadRuntimeModule(packageName, options.moduleLoader, moduleCache);
|
|
6473
|
+
if (!loaded.ok) {
|
|
6474
|
+
rows.push({
|
|
6475
|
+
...base,
|
|
6476
|
+
packageInstalled: false,
|
|
6477
|
+
packageLoads: false,
|
|
6478
|
+
pieceExportFound: false,
|
|
6479
|
+
actionMappingsVerified: 0,
|
|
6480
|
+
triggerMappingsFound: 0,
|
|
6481
|
+
triggerHostingSupported: false,
|
|
6482
|
+
error: loaded.message
|
|
6483
|
+
});
|
|
6484
|
+
continue;
|
|
6485
|
+
}
|
|
6486
|
+
const piece = findPieceExport(loaded.module, entry.id);
|
|
6487
|
+
const actions = piece?.actions ?? [];
|
|
6488
|
+
const triggers = piece?.triggers ?? [];
|
|
6489
|
+
rows.push({
|
|
6490
|
+
...base,
|
|
6491
|
+
packageInstalled: true,
|
|
6492
|
+
packageLoads: true,
|
|
6493
|
+
pieceExportFound: Boolean(piece),
|
|
6494
|
+
actionMappingsVerified: entry.actions.filter((action) => hasRuntimeName(actions, [
|
|
6495
|
+
action.id,
|
|
6496
|
+
action.title,
|
|
6497
|
+
action.upstreamName
|
|
6498
|
+
])).length,
|
|
6499
|
+
triggerMappingsFound: entry.triggers.filter((trigger2) => hasRuntimeName(triggers, [
|
|
6500
|
+
trigger2.id,
|
|
6501
|
+
trigger2.title,
|
|
6502
|
+
trigger2.upstreamName
|
|
6503
|
+
])).length,
|
|
6504
|
+
triggerHostingSupported: entry.triggers.length === 0 || triggers.length > 0
|
|
6505
|
+
});
|
|
6506
|
+
}
|
|
6507
|
+
return rows;
|
|
6508
|
+
}
|
|
6353
6509
|
function createTangleCatalogCredentialAuthResolver(options) {
|
|
6354
6510
|
return async function resolveTangleCatalogAuth(connection) {
|
|
6355
6511
|
if (!connection.secretRef) return void 0;
|
|
@@ -6436,7 +6592,11 @@ function findPieceExport(moduleValue, connectorId) {
|
|
|
6436
6592
|
];
|
|
6437
6593
|
return values.find((value) => Boolean(value) && typeof value === "object" && Array.isArray(value.actions));
|
|
6438
6594
|
}
|
|
6439
|
-
function
|
|
6595
|
+
function hasRuntimeName(values, candidates) {
|
|
6596
|
+
const expected = new Set(candidates.filter((value) => Boolean(value)));
|
|
6597
|
+
return values.filter((value) => Boolean(value) && typeof value === "object").some((value) => [value.name, value.displayName].some((name) => typeof name === "string" && expected.has(name)));
|
|
6598
|
+
}
|
|
6599
|
+
function findRuntimeAction(piece, invocation, aliases = {}, allowFuzzyActionMatch = false) {
|
|
6440
6600
|
const actions = (piece.actions ?? []).filter((action) => Boolean(action) && typeof action === "object");
|
|
6441
6601
|
const explicit = aliases[invocation.connector.id]?.[invocation.action.id];
|
|
6442
6602
|
const candidates = new Set([
|
|
@@ -6448,7 +6608,7 @@ function findRuntimeAction(piece, invocation, aliases = {}) {
|
|
|
6448
6608
|
for (const action of actions) {
|
|
6449
6609
|
const names = [action.name, action.displayName].filter((value) => Boolean(value));
|
|
6450
6610
|
if (names.some((name) => candidates.has(name))) return action;
|
|
6451
|
-
if (names.some((name) => [...candidates].some((candidate) => comparable(name) === comparable(candidate)))) return action;
|
|
6611
|
+
if (allowFuzzyActionMatch && names.some((name) => [...candidates].some((candidate) => comparable(name) === comparable(candidate)))) return action;
|
|
6452
6612
|
}
|
|
6453
6613
|
return void 0;
|
|
6454
6614
|
}
|
|
@@ -7035,7 +7195,7 @@ var IntegrationHub = class {
|
|
|
7035
7195
|
const provider = this.requireProvider(connection.providerId);
|
|
7036
7196
|
const connector = await this.requireConnector(provider, connection.connectorId);
|
|
7037
7197
|
const spec = connector.triggers?.find((candidate) => candidate.id === trigger2);
|
|
7038
|
-
if (!spec) throw new IntegrationError(`Trigger ${trigger2} is not defined by connector ${connector.id}.`, "
|
|
7198
|
+
if (!spec) throw new IntegrationError(`Trigger ${trigger2} is not defined by connector ${connector.id}.`, "trigger_not_found");
|
|
7039
7199
|
assertScopes(connection, spec.requiredScopes);
|
|
7040
7200
|
if (!provider.subscribeTrigger) {
|
|
7041
7201
|
throw new IntegrationError(`Provider ${provider.id} does not support triggers.`, "auth_not_supported");
|
|
@@ -7231,6 +7391,37 @@ export {
|
|
|
7231
7391
|
getActivepiecesOverride,
|
|
7232
7392
|
listActivepiecesCatalogEntries,
|
|
7233
7393
|
buildActivepiecesConnectors,
|
|
7394
|
+
createCatalogExecutorProvider,
|
|
7395
|
+
createActivepiecesExecutorProvider,
|
|
7396
|
+
integrationToolName,
|
|
7397
|
+
parseIntegrationToolName,
|
|
7398
|
+
buildIntegrationToolCatalog,
|
|
7399
|
+
searchIntegrationTools,
|
|
7400
|
+
toMcpTools,
|
|
7401
|
+
ACTIVEPIECES_PUBLIC_CATALOG_URL,
|
|
7402
|
+
extractActivepiecesPublicPieceCount,
|
|
7403
|
+
auditIntegrationCatalogFreshness,
|
|
7404
|
+
ACTIVEPIECES_RUNTIME_SIGNATURE_HEADER,
|
|
7405
|
+
TANGLE_CATALOG_RUNTIME_SIGNATURE_HEADER,
|
|
7406
|
+
createActivepiecesHttpExecutor,
|
|
7407
|
+
buildActivepiecesRuntimeRequest,
|
|
7408
|
+
signActivepiecesRuntimeRequest,
|
|
7409
|
+
verifyActivepiecesRuntimeSignature,
|
|
7410
|
+
createTangleCatalogHttpExecutor,
|
|
7411
|
+
buildTangleCatalogRuntimeRequest,
|
|
7412
|
+
signTangleCatalogRuntimeRequest,
|
|
7413
|
+
verifyTangleCatalogRuntimeSignature,
|
|
7414
|
+
TANGLE_INTEGRATIONS_CATALOG_PROVIDER_ID,
|
|
7415
|
+
TANGLE_INTEGRATIONS_CATALOG_SOURCE,
|
|
7416
|
+
listTangleIntegrationCatalogEntries,
|
|
7417
|
+
listTangleIntegrationContracts,
|
|
7418
|
+
listTangleIntegrationCatalogRuntimePackages,
|
|
7419
|
+
buildTangleCatalogRuntimePackageManifest,
|
|
7420
|
+
renderTangleCatalogRuntimePnpmAddCommand,
|
|
7421
|
+
buildTangleIntegrationCatalogConnectors,
|
|
7422
|
+
createTangleCatalogExecutorProvider,
|
|
7423
|
+
extractExternalCatalogPublicCount,
|
|
7424
|
+
auditTangleIntegrationCatalogFreshness,
|
|
7234
7425
|
buildDefaultIntegrationRegistry,
|
|
7235
7426
|
composeIntegrationRegistry,
|
|
7236
7427
|
summarizeIntegrationRegistry,
|
|
@@ -7326,12 +7517,6 @@ export {
|
|
|
7326
7517
|
airtableConnector,
|
|
7327
7518
|
asanaConnector,
|
|
7328
7519
|
salesforceConnector,
|
|
7329
|
-
integrationToolName,
|
|
7330
|
-
parseIntegrationToolName,
|
|
7331
|
-
buildIntegrationToolCatalog,
|
|
7332
|
-
searchIntegrationTools,
|
|
7333
|
-
toMcpTools,
|
|
7334
|
-
createCatalogExecutorProvider,
|
|
7335
7520
|
buildIntegrationInvocationEnvelope,
|
|
7336
7521
|
invocationRequestFromEnvelope,
|
|
7337
7522
|
validateIntegrationInvocationEnvelope,
|
|
@@ -7345,30 +7530,9 @@ export {
|
|
|
7345
7530
|
importMcpConnector,
|
|
7346
7531
|
createGatewayCatalogProvider,
|
|
7347
7532
|
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
7533
|
createTangleCatalogRuntimeHandler,
|
|
7371
7534
|
createTangleCatalogInstalledPackageExecutor,
|
|
7535
|
+
auditTangleCatalogRuntimePackages,
|
|
7372
7536
|
createTangleCatalogCredentialAuthResolver,
|
|
7373
7537
|
createTangleCatalogHttpAuthResolver,
|
|
7374
7538
|
tangleCatalogAuthValue,
|
|
@@ -7389,4 +7553,4 @@ export {
|
|
|
7389
7553
|
signCapability,
|
|
7390
7554
|
verifyCapabilityToken
|
|
7391
7555
|
};
|
|
7392
|
-
//# sourceMappingURL=chunk-
|
|
7556
|
+
//# sourceMappingURL=chunk-VJ57GPYO.js.map
|