@tangle-network/agent-integrations 0.23.1 → 0.24.0

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