@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.
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  integrationSpecToConnector,
3
3
  listIntegrationSpecs
4
- } from "./chunk-L6WBPDUP.js";
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: buildActivepiecesConnectors({ providerId: "tangle-catalog" }).map((connector) => ({
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 = unique(toolBindableCandidates(ordered).flatMap((candidate) => candidate.connector.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 = unique([
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 unique(values) {
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_${randomUUID()}`,
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 = unique2(requirements.map((requirement) => requirement.connectorId));
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 unique2(values) {
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: unique3([...existing.requiredActions ?? [], action]),
1812
- requiredScopes: unique3([...existing.requiredScopes ?? [], ...typeof item === "string" ? [] : item.scopes ?? []])
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 unique3(values) {
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 randomUUID2 } from "crypto";
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_${randomUUID2()}`,
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 && riskRank(ctx.action.risk) > riskRank(rule.maxRisk)) return false;
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 riskRank(risk) {
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 = createHmac("sha256", secret).update(`${parsed.t}.${rawBody}`).digest("hex");
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 (timingSafeEqual(sigBuf, expectedBuf)) return true;
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=" + createHmac("sha256", secret).update(`v0:${ts}:${rawBody}`).digest("hex");
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 timingSafeEqual(sigBuf, expectedBuf);
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 = createHmac(algorithm, secret).update(rawBody).digest("hex");
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 timingSafeEqual(sigBuf, expectedBuf);
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 = createHmac("sha1", input.authToken).update(data).digest("base64");
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 timingSafeEqual(expectedBuf, sigBuf);
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 createHmac2 } from "crypto";
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 = createHmac2("sha256", creds.secret).update(`${ts}.${body}`).digest("hex");
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
- async subscribeTrigger(connection, triggerId, targetUrl) {
5348
- if (!options.subscribeTrigger) {
5349
- throw new IntegrationError(`Provider ${options.id} does not support trigger subscriptions.`, "action_not_found");
5350
- }
5351
- const connector = byConnector.get(connection.connectorId);
5352
- if (!connector) {
5353
- throw new IntegrationError(`Connector ${connection.connectorId} not found.`, "connector_not_found");
5354
- }
5355
- const trigger2 = connector.triggers?.find((candidate) => candidate.id === triggerId);
5356
- if (!trigger2) {
5357
- throw new IntegrationError(`Trigger ${triggerId} is not defined by connector ${connector.id}.`, "action_not_found");
5358
- }
5359
- return options.subscribeTrigger(connection, trigger2, targetUrl);
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
- unsubscribeTrigger: options.unsubscribeTrigger,
5362
- normalizeTriggerEvent: options.normalizeTriggerEvent
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 titleFromId(id) {
5604
- return id.replace(/([a-z])([A-Z])/g, "$1 $2").split(/[^a-zA-Z0-9]+/g).filter(Boolean).map((part) => part.slice(0, 1).toUpperCase() + part.slice(1)).join(" ");
5605
- }
5606
- function isObject(value) {
5607
- return Boolean(value) && typeof value === "object" && !Array.isArray(value);
5608
- }
5609
- function unique5(values) {
5610
- return [...new Set(values)];
5611
- }
5612
-
5613
- // src/gateway-catalog.ts
5614
- function createGatewayCatalogProvider(options) {
5615
- const now = options.now ?? (() => /* @__PURE__ */ new Date());
5616
- let cachedAt = 0;
5617
- let cached;
5618
- async function listConnectors() {
5619
- const ttl = options.cacheTtlMs ?? 6e4;
5620
- const current = now().getTime();
5621
- if (cached && current - cachedAt < ttl) return cached;
5622
- const entries = await options.fetchCatalog();
5623
- cached = normalizeGatewayCatalog(entries, {
5624
- providerId: options.id,
5625
- providerKind: options.kind
5626
- });
5627
- cachedAt = current;
5628
- return cached;
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
- id: options.id,
5632
- kind: options.kind,
5633
- listConnectors,
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 normalizeGatewayCatalog(entries, options) {
5646
- const out = [];
5647
- const seen = /* @__PURE__ */ new Set();
5648
- for (const entry of entries) {
5649
- const id = slug2(entry.id ?? entry.key ?? entry.name ?? entry.title ?? "");
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
- async function assertKnownGatewayAction(connectors, connectorId, actionId) {
5677
- const connector = connectors.find((candidate) => candidate.id === connectorId);
5678
- if (!connector) throw new IntegrationError(`Connector ${connectorId} not found.`, "connector_not_found");
5679
- if (!connector.actions.some((action) => action.id === actionId)) {
5680
- throw new IntegrationError(`Action ${actionId} is not defined by connector ${connectorId}.`, "action_not_found");
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
- id,
5688
- title: action.title ?? action.name ?? titleFromId2(id),
5689
- risk: normalizeRisk(action.risk),
5690
- requiredScopes: unique6([
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
- }).filter((action) => action.id);
5997
+ }
5998
+ return {
5999
+ status: "ok",
6000
+ action: result.action,
6001
+ output: result.output,
6002
+ metadata: result.metadata
6003
+ };
5702
6004
  }
5703
- function normalizeTriggers(triggers, fallbackScopes) {
5704
- const normalized = triggers.map((trigger2) => {
5705
- const id = slug2(trigger2.id ?? trigger2.key ?? trigger2.name ?? trigger2.title ?? "");
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
- id,
5708
- title: trigger2.title ?? trigger2.name ?? titleFromId2(id),
5709
- requiredScopes: unique6([
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
- }).filter((trigger2) => trigger2.id);
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
- function slug2(value) {
5793
- return value.trim().toLowerCase().replace(/&/g, "and").replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
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 titleFromId2(id) {
5796
- return id.split("-").filter(Boolean).map((part) => part.slice(0, 1).toUpperCase() + part.slice(1)).join(" ");
6043
+ function isNonEmptyString(value) {
6044
+ return typeof value === "string" && value.trim().length > 0;
5797
6045
  }
5798
- function unique6(values) {
5799
- return [...new Set(values)];
6046
+ function isPlainRecord(value) {
6047
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
5800
6048
  }
5801
6049
 
5802
- // src/activepieces-provider.ts
5803
- function createActivepiecesExecutorProvider(options) {
5804
- const providerId = options.id ?? "activepieces";
5805
- const connectors = options.connectors ?? buildActivepiecesConnectors({
5806
- providerId,
5807
- includeCatalogActions: true,
5808
- executable: true
5809
- });
5810
- const byEntry = new Map(listActivepiecesCatalogEntries().map((entry) => [entry.id, entry]));
5811
- return createCatalogExecutorProvider({
5812
- id: providerId,
5813
- kind: "activepieces",
5814
- connectors,
5815
- startAuth: options.startAuth,
5816
- completeAuth: options.completeAuth,
5817
- executeAction: async ({ connection, request, connector, action }) => {
5818
- const catalogEntry = byEntry.get(connector.id);
5819
- if (!catalogEntry) {
5820
- throw new IntegrationError(`Activepieces catalog entry ${connector.id} not found.`, "connector_not_found");
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
- // src/activepieces-runtime.ts
5841
- import { createHmac as createHmac3, randomUUID as randomUUID3, timingSafeEqual as timingSafeEqual2 } from "crypto";
5842
- var ACTIVEPIECES_RUNTIME_SIGNATURE_HEADER = "x-tangle-activepieces-signature";
5843
- var TANGLE_CATALOG_RUNTIME_SIGNATURE_HEADER = "x-tangle-catalog-signature";
5844
- function createActivepiecesHttpExecutor(options) {
5845
- const endpoint = options.endpoint.replace(/\/$/, "");
5846
- const path = options.path ?? "/v1/activepieces/actions/invoke";
5847
- const normalizedPath = path.startsWith("/") ? path : `/${path}`;
5848
- const signatureHeader = options.signatureHeader ?? ACTIVEPIECES_RUNTIME_SIGNATURE_HEADER;
5849
- const fetchImpl = options.fetchImpl ?? fetch;
5850
- const requestId = options.requestId ?? (() => `apexec_${randomUUID3()}`);
5851
- return async (invocation) => {
5852
- const body = buildActivepiecesRuntimeRequest(invocation, requestId());
5853
- const serialized = JSON.stringify(body);
5854
- const response = await fetchImpl(`${endpoint}${normalizedPath}`, {
5855
- method: "POST",
5856
- headers: {
5857
- "content-type": "application/json",
5858
- ...options.headers,
5859
- ...options.secret ? { [signatureHeader]: signActivepiecesRuntimeRequest(serialized, options.secret) } : {}
5860
- },
5861
- body: serialized,
5862
- signal: AbortSignal.timeout(options.timeoutMs ?? 3e4)
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 buildActivepiecesRuntimeRequest(invocation, requestId = `apexec_${randomUUID3()}`) {
6103
+ function connectorFromActions(options, actions) {
6104
+ const scopes = unique5([
6105
+ ...options.scopes ?? [],
6106
+ ...actions.flatMap((action) => action.requiredScopes)
6107
+ ]);
5880
6108
  return {
5881
- version: 1,
5882
- requestId,
5883
- providerId: invocation.connection.providerId,
5884
- connection: invocation.connection,
5885
- connector: {
5886
- id: invocation.connector.id,
5887
- title: invocation.connector.title,
5888
- auth: invocation.connector.auth,
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 signActivepiecesRuntimeRequest(serializedBody, secret) {
5903
- return `sha256=${createHmac3("sha256", secret).update(serializedBody).digest("hex")}`;
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 verifyActivepiecesRuntimeSignature(serializedBody, signature, secret) {
5906
- if (!signature) return false;
5907
- const expected = signActivepiecesRuntimeRequest(serializedBody, secret);
5908
- const left = Buffer.from(signature);
5909
- const right = Buffer.from(expected);
5910
- return left.length === right.length && timingSafeEqual2(left, right);
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 createTangleCatalogHttpExecutor(options) {
5913
- const endpoint = options.endpoint.replace(/\/$/, "");
5914
- const path = options.path ?? "/v1/integration-catalog/actions/invoke";
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 buildTangleCatalogRuntimeRequest(invocation, requestId = `tcat_${randomUUID3()}`) {
6138
+ function openApiInputSchema(path, method, operation) {
5948
6139
  return {
5949
- version: 1,
5950
- requestId,
5951
- providerId: invocation.connection.providerId,
5952
- connection: invocation.connection,
5953
- connector: {
5954
- id: invocation.connector.id,
5955
- title: invocation.connector.title,
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
- var signTangleCatalogRuntimeRequest = signActivepiecesRuntimeRequest;
5971
- var verifyTangleCatalogRuntimeSignature = verifyActivepiecesRuntimeSignature;
5972
-
5973
- // src/catalog-freshness.ts
5974
- var ACTIVEPIECES_PUBLIC_CATALOG_URL = "https://www.activepieces.com/pieces";
5975
- function parseCount(value) {
5976
- if (!value) return void 0;
5977
- const parsed = Number.parseInt(value.replace(/,/g, ""), 10);
5978
- return Number.isFinite(parsed) ? parsed : void 0;
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 extractActivepiecesPublicPieceCount(html) {
5981
- const showingMatch = html.match(/Showing\s+([0-9,]+)\s+pieces/i);
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
- async function auditIntegrationCatalogFreshness(options = {}) {
5986
- const minActivepiecesConnectors = options.minActivepiecesConnectors ?? 600;
5987
- const staleConnectorDelta = options.staleConnectorDelta ?? 25;
5988
- const activepiecesEntries = listActivepiecesCatalogEntries();
5989
- const activepiecesConnectors = buildActivepiecesConnectors({
5990
- includeCatalogActions: true
5991
- });
5992
- const executableActivepiecesProvider = createActivepiecesExecutorProvider({
5993
- executeAction: () => ({ ok: true, action: "audit.noop" })
5994
- });
5995
- const executableActivepiecesConnectors = await executableActivepiecesProvider.listConnectors();
5996
- const executableRegistry = composeIntegrationRegistry([
5997
- {
5998
- id: executableActivepiecesProvider.id,
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
- ok: warnings.length === 0,
6035
- generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
6036
- local: {
6037
- activepiecesEntries: activepiecesEntries.length,
6038
- activepiecesConnectors: activepiecesConnectors.length,
6039
- activepiecesActions: activepiecesConnectors.reduce(
6040
- (sum, connector) => sum + connector.actions.length,
6041
- 0
6042
- ),
6043
- activepiecesTriggers: activepiecesConnectors.reduce(
6044
- (sum, connector) => sum + (connector.triggers?.length ?? 0),
6045
- 0
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
- async function checkActivepiecesPublicCatalog(input) {
6067
- try {
6068
- const res = await (input.fetchImpl ?? fetch)(ACTIVEPIECES_PUBLIC_CATALOG_URL, {
6069
- headers: { accept: "text/html" },
6070
- signal: AbortSignal.timeout(15e3)
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
- if (!res.ok) {
6073
- const warning = `Activepieces freshness request failed with HTTP ${res.status}.`;
6074
- input.warnings.push(warning);
6075
- return {
6076
- checkedUrl: ACTIVEPIECES_PUBLIC_CATALOG_URL,
6077
- warning
6078
- };
6079
- }
6080
- const activepiecesPieces = extractActivepiecesPublicPieceCount(await res.text());
6081
- const activepiecesDelta = activepiecesPieces === void 0 ? void 0 : activepiecesPieces - input.localConnectorCount;
6082
- if (activepiecesDelta !== void 0 && activepiecesDelta > input.staleConnectorDelta) {
6083
- input.warnings.push(
6084
- `Activepieces upstream appears ${activepiecesDelta} connectors ahead of the vendored package catalog.`
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
- activepiecesPieces,
6089
- activepiecesDelta,
6090
- checkedUrl: ACTIVEPIECES_PUBLIC_CATALOG_URL,
6091
- warning: activepiecesPieces === void 0 ? "Could not parse upstream piece count." : void 0
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
- } catch (error) {
6094
- const warning = error instanceof Error ? error.message : "Activepieces freshness request failed.";
6095
- input.warnings.push(warning);
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
- checkedUrl: ACTIVEPIECES_PUBLIC_CATALOG_URL,
6098
- warning
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
- // src/tangle-catalog.ts
6104
- var TANGLE_INTEGRATIONS_CATALOG_PROVIDER_ID = "tangle-catalog";
6105
- var TANGLE_INTEGRATIONS_CATALOG_SOURCE = "tangle-integrations-catalog";
6106
- function listTangleIntegrationCatalogEntries() {
6107
- return listActivepiecesCatalogEntries().map((entry) => sanitizeEntry(entry));
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 listTangleIntegrationCatalogRuntimePackages() {
6110
- return listActivepiecesCatalogEntries().filter((entry) => Boolean(entry.npmPackage)).map((entry) => ({
6111
- connectorId: entry.id,
6112
- packageName: entry.npmPackage,
6113
- version: entry.version
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 buildTangleIntegrationCatalogConnectors(options = {}) {
6117
- const providerId = options.providerId ?? TANGLE_INTEGRATIONS_CATALOG_PROVIDER_ID;
6118
- return buildActivepiecesConnectors({
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 createTangleCatalogExecutorProvider(options) {
6124
- const providerId = options.id ?? TANGLE_INTEGRATIONS_CATALOG_PROVIDER_ID;
6125
- const connectors = options.connectors ?? buildTangleIntegrationCatalogConnectors({
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
- var extractExternalCatalogPublicCount = extractActivepiecesPublicPieceCount;
6182
- async function auditTangleIntegrationCatalogFreshness(options = {}) {
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 sanitizeEntry(entry) {
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 sanitizeConnector(connector, providerId) {
6232
- const metadata = connector.metadata ?? {};
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 findRuntimeAction(piece, invocation, aliases = {}) {
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}.`, "action_not_found");
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-P4BB4CU6.js.map
7556
+ //# sourceMappingURL=chunk-VJ57GPYO.js.map