@tangle-network/agent-integrations 0.25.4 → 0.25.6

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.
@@ -4,12 +4,10 @@ import {
4
4
  buildTangleCatalogRuntimePackageManifest,
5
5
  renderTangleCatalogRuntimePnpmAddCommand,
6
6
  startTangleCatalogRuntimeNodeServer
7
- } from "../chunk-IOO75SJH.js";
8
- import "../chunk-FQAT4IEE.js";
9
- import "../chunk-6KWCC42J.js";
7
+ } from "../chunk-S54DPRDU.js";
10
8
  import "../chunk-4JQ754PA.js";
11
9
  import "../chunk-376UBTNB.js";
12
- import "../chunk-IDX3KIPA.js";
10
+ import "../chunk-WC63AI4Q.js";
13
11
 
14
12
  // src/bin/tangle-catalog-runtime.ts
15
13
  var args = new Set(process.argv.slice(2));
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/bin/tangle-catalog-runtime.ts"],"sourcesContent":["#!/usr/bin/env node\nimport {\n buildTangleCatalogRuntimePackageManifest,\n renderTangleCatalogRuntimePnpmAddCommand,\n} from '../tangle-catalog.js'\nimport { auditTangleCatalogRuntimePackages } from '../tangle-catalog-runtime.js'\nimport { startTangleCatalogRuntimeNodeServer } from '../tangle-catalog-runtime-server.js'\n\nconst args = new Set(process.argv.slice(2))\nif (args.has('--print-package-json')) {\n console.log(JSON.stringify(buildTangleCatalogRuntimePackageManifest({\n agentIntegrationsVersion: process.env.TANGLE_AGENT_INTEGRATIONS_VERSION,\n }), null, 2))\n process.exit(0)\n}\n\nif (args.has('--print-pnpm-add')) {\n console.log(renderTangleCatalogRuntimePnpmAddCommand({\n agentIntegrationsVersion: process.env.TANGLE_AGENT_INTEGRATIONS_VERSION,\n }))\n process.exit(0)\n}\n\nif (args.has('--audit-packages')) {\n const connectorIds = process.env.TANGLE_CATALOG_AUDIT_CONNECTORS\n ?.split(',')\n .map((id) => id.trim())\n .filter(Boolean)\n console.log(JSON.stringify(await auditTangleCatalogRuntimePackages({ connectorIds }), null, 2))\n process.exit(0)\n}\n\nconst secret = process.env.TANGLE_CATALOG_RUNTIME_SECRET\nif (!secret || secret.length < 32) {\n console.error('TANGLE_CATALOG_RUNTIME_SECRET must be set to at least 32 characters.')\n process.exit(1)\n}\n\nconst authResolverUrl = process.env.TANGLE_CATALOG_AUTH_RESOLVER_URL\nconst authResolverSecret = process.env.TANGLE_CATALOG_AUTH_RESOLVER_SECRET\nif (Boolean(authResolverUrl) !== Boolean(authResolverSecret)) {\n console.error('TANGLE_CATALOG_AUTH_RESOLVER_URL and TANGLE_CATALOG_AUTH_RESOLVER_SECRET must be set together.')\n process.exit(1)\n}\n\nconst port = Number(process.env.PORT ?? process.env.TANGLE_CATALOG_RUNTIME_PORT ?? 4109)\nif (!Number.isInteger(port) || port < 1 || port > 65_535) {\n console.error('PORT must be an integer between 1 and 65535.')\n process.exit(1)\n}\n\nconst server = await startTangleCatalogRuntimeNodeServer({\n secret,\n host: process.env.HOST ?? process.env.TANGLE_CATALOG_RUNTIME_HOST ?? '0.0.0.0',\n port,\n authResolver: authResolverUrl && authResolverSecret\n ? {\n endpoint: authResolverUrl,\n secret: authResolverSecret,\n }\n : false,\n onLog: (event) => {\n const line = JSON.stringify({\n level: event.level,\n message: event.message,\n ...event.metadata,\n })\n if (event.level === 'error') console.error(line)\n else console.log(line)\n },\n})\n\nconsole.log(JSON.stringify({\n level: 'info',\n message: 'Tangle catalog runtime listening.',\n url: server.url,\n}))\n\nfor (const signal of ['SIGINT', 'SIGTERM'] as const) {\n process.once(signal, async () => {\n await server.close()\n process.exit(0)\n })\n}\n"],"mappings":";;;;;;;;;;;;;;AAQA,IAAM,OAAO,IAAI,IAAI,QAAQ,KAAK,MAAM,CAAC,CAAC;AAC1C,IAAI,KAAK,IAAI,sBAAsB,GAAG;AACpC,UAAQ,IAAI,KAAK,UAAU,yCAAyC;AAAA,IAClE,0BAA0B,QAAQ,IAAI;AAAA,EACxC,CAAC,GAAG,MAAM,CAAC,CAAC;AACZ,UAAQ,KAAK,CAAC;AAChB;AAEA,IAAI,KAAK,IAAI,kBAAkB,GAAG;AAChC,UAAQ,IAAI,yCAAyC;AAAA,IACnD,0BAA0B,QAAQ,IAAI;AAAA,EACxC,CAAC,CAAC;AACF,UAAQ,KAAK,CAAC;AAChB;AAEA,IAAI,KAAK,IAAI,kBAAkB,GAAG;AAChC,QAAM,eAAe,QAAQ,IAAI,iCAC7B,MAAM,GAAG,EACV,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC,EACrB,OAAO,OAAO;AACjB,UAAQ,IAAI,KAAK,UAAU,MAAM,kCAAkC,EAAE,aAAa,CAAC,GAAG,MAAM,CAAC,CAAC;AAC9F,UAAQ,KAAK,CAAC;AAChB;AAEA,IAAM,SAAS,QAAQ,IAAI;AAC3B,IAAI,CAAC,UAAU,OAAO,SAAS,IAAI;AACjC,UAAQ,MAAM,sEAAsE;AACpF,UAAQ,KAAK,CAAC;AAChB;AAEA,IAAM,kBAAkB,QAAQ,IAAI;AACpC,IAAM,qBAAqB,QAAQ,IAAI;AACvC,IAAI,QAAQ,eAAe,MAAM,QAAQ,kBAAkB,GAAG;AAC5D,UAAQ,MAAM,gGAAgG;AAC9G,UAAQ,KAAK,CAAC;AAChB;AAEA,IAAM,OAAO,OAAO,QAAQ,IAAI,QAAQ,QAAQ,IAAI,+BAA+B,IAAI;AACvF,IAAI,CAAC,OAAO,UAAU,IAAI,KAAK,OAAO,KAAK,OAAO,OAAQ;AACxD,UAAQ,MAAM,8CAA8C;AAC5D,UAAQ,KAAK,CAAC;AAChB;AAEA,IAAM,SAAS,MAAM,oCAAoC;AAAA,EACvD;AAAA,EACA,MAAM,QAAQ,IAAI,QAAQ,QAAQ,IAAI,+BAA+B;AAAA,EACrE;AAAA,EACA,cAAc,mBAAmB,qBAC7B;AAAA,IACE,UAAU;AAAA,IACV,QAAQ;AAAA,EACV,IACA;AAAA,EACJ,OAAO,CAAC,UAAU;AAChB,UAAM,OAAO,KAAK,UAAU;AAAA,MAC1B,OAAO,MAAM;AAAA,MACb,SAAS,MAAM;AAAA,MACf,GAAG,MAAM;AAAA,IACX,CAAC;AACD,QAAI,MAAM,UAAU,QAAS,SAAQ,MAAM,IAAI;AAAA,QAC1C,SAAQ,IAAI,IAAI;AAAA,EACvB;AACF,CAAC;AAED,QAAQ,IAAI,KAAK,UAAU;AAAA,EACzB,OAAO;AAAA,EACP,SAAS;AAAA,EACT,KAAK,OAAO;AACd,CAAC,CAAC;AAEF,WAAW,UAAU,CAAC,UAAU,SAAS,GAAY;AACnD,UAAQ,KAAK,QAAQ,YAAY;AAC/B,UAAM,OAAO,MAAM;AACnB,YAAQ,KAAK,CAAC;AAAA,EAChB,CAAC;AACH;","names":[]}
1
+ {"version":3,"sources":["../../src/bin/tangle-catalog-runtime.ts"],"sourcesContent":["#!/usr/bin/env node\nimport {\n buildTangleCatalogRuntimePackageManifest,\n renderTangleCatalogRuntimePnpmAddCommand,\n} from '../tangle-catalog.js'\nimport { auditTangleCatalogRuntimePackages } from '../tangle-catalog-runtime.js'\nimport { startTangleCatalogRuntimeNodeServer } from '../tangle-catalog-runtime-server.js'\n\nconst args = new Set(process.argv.slice(2))\nif (args.has('--print-package-json')) {\n console.log(JSON.stringify(buildTangleCatalogRuntimePackageManifest({\n agentIntegrationsVersion: process.env.TANGLE_AGENT_INTEGRATIONS_VERSION,\n }), null, 2))\n process.exit(0)\n}\n\nif (args.has('--print-pnpm-add')) {\n console.log(renderTangleCatalogRuntimePnpmAddCommand({\n agentIntegrationsVersion: process.env.TANGLE_AGENT_INTEGRATIONS_VERSION,\n }))\n process.exit(0)\n}\n\nif (args.has('--audit-packages')) {\n const connectorIds = process.env.TANGLE_CATALOG_AUDIT_CONNECTORS\n ?.split(',')\n .map((id) => id.trim())\n .filter(Boolean)\n console.log(JSON.stringify(await auditTangleCatalogRuntimePackages({ connectorIds }), null, 2))\n process.exit(0)\n}\n\nconst secret = process.env.TANGLE_CATALOG_RUNTIME_SECRET\nif (!secret || secret.length < 32) {\n console.error('TANGLE_CATALOG_RUNTIME_SECRET must be set to at least 32 characters.')\n process.exit(1)\n}\n\nconst authResolverUrl = process.env.TANGLE_CATALOG_AUTH_RESOLVER_URL\nconst authResolverSecret = process.env.TANGLE_CATALOG_AUTH_RESOLVER_SECRET\nif (Boolean(authResolverUrl) !== Boolean(authResolverSecret)) {\n console.error('TANGLE_CATALOG_AUTH_RESOLVER_URL and TANGLE_CATALOG_AUTH_RESOLVER_SECRET must be set together.')\n process.exit(1)\n}\n\nconst port = Number(process.env.PORT ?? process.env.TANGLE_CATALOG_RUNTIME_PORT ?? 4109)\nif (!Number.isInteger(port) || port < 1 || port > 65_535) {\n console.error('PORT must be an integer between 1 and 65535.')\n process.exit(1)\n}\n\nconst server = await startTangleCatalogRuntimeNodeServer({\n secret,\n host: process.env.HOST ?? process.env.TANGLE_CATALOG_RUNTIME_HOST ?? '0.0.0.0',\n port,\n authResolver: authResolverUrl && authResolverSecret\n ? {\n endpoint: authResolverUrl,\n secret: authResolverSecret,\n }\n : false,\n onLog: (event) => {\n const line = JSON.stringify({\n level: event.level,\n message: event.message,\n ...event.metadata,\n })\n if (event.level === 'error') console.error(line)\n else console.log(line)\n },\n})\n\nconsole.log(JSON.stringify({\n level: 'info',\n message: 'Tangle catalog runtime listening.',\n url: server.url,\n}))\n\nfor (const signal of ['SIGINT', 'SIGTERM'] as const) {\n process.once(signal, async () => {\n await server.close()\n process.exit(0)\n })\n}\n"],"mappings":";;;;;;;;;;;;AAQA,IAAM,OAAO,IAAI,IAAI,QAAQ,KAAK,MAAM,CAAC,CAAC;AAC1C,IAAI,KAAK,IAAI,sBAAsB,GAAG;AACpC,UAAQ,IAAI,KAAK,UAAU,yCAAyC;AAAA,IAClE,0BAA0B,QAAQ,IAAI;AAAA,EACxC,CAAC,GAAG,MAAM,CAAC,CAAC;AACZ,UAAQ,KAAK,CAAC;AAChB;AAEA,IAAI,KAAK,IAAI,kBAAkB,GAAG;AAChC,UAAQ,IAAI,yCAAyC;AAAA,IACnD,0BAA0B,QAAQ,IAAI;AAAA,EACxC,CAAC,CAAC;AACF,UAAQ,KAAK,CAAC;AAChB;AAEA,IAAI,KAAK,IAAI,kBAAkB,GAAG;AAChC,QAAM,eAAe,QAAQ,IAAI,iCAC7B,MAAM,GAAG,EACV,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC,EACrB,OAAO,OAAO;AACjB,UAAQ,IAAI,KAAK,UAAU,MAAM,kCAAkC,EAAE,aAAa,CAAC,GAAG,MAAM,CAAC,CAAC;AAC9F,UAAQ,KAAK,CAAC;AAChB;AAEA,IAAM,SAAS,QAAQ,IAAI;AAC3B,IAAI,CAAC,UAAU,OAAO,SAAS,IAAI;AACjC,UAAQ,MAAM,sEAAsE;AACpF,UAAQ,KAAK,CAAC;AAChB;AAEA,IAAM,kBAAkB,QAAQ,IAAI;AACpC,IAAM,qBAAqB,QAAQ,IAAI;AACvC,IAAI,QAAQ,eAAe,MAAM,QAAQ,kBAAkB,GAAG;AAC5D,UAAQ,MAAM,gGAAgG;AAC9G,UAAQ,KAAK,CAAC;AAChB;AAEA,IAAM,OAAO,OAAO,QAAQ,IAAI,QAAQ,QAAQ,IAAI,+BAA+B,IAAI;AACvF,IAAI,CAAC,OAAO,UAAU,IAAI,KAAK,OAAO,KAAK,OAAO,OAAQ;AACxD,UAAQ,MAAM,8CAA8C;AAC5D,UAAQ,KAAK,CAAC;AAChB;AAEA,IAAM,SAAS,MAAM,oCAAoC;AAAA,EACvD;AAAA,EACA,MAAM,QAAQ,IAAI,QAAQ,QAAQ,IAAI,+BAA+B;AAAA,EACrE;AAAA,EACA,cAAc,mBAAmB,qBAC7B;AAAA,IACE,UAAU;AAAA,IACV,QAAQ;AAAA,EACV,IACA;AAAA,EACJ,OAAO,CAAC,UAAU;AAChB,UAAM,OAAO,KAAK,UAAU;AAAA,MAC1B,OAAO,MAAM;AAAA,MACb,SAAS,MAAM;AAAA,MACf,GAAG,MAAM;AAAA,IACX,CAAC;AACD,QAAI,MAAM,UAAU,QAAS,SAAQ,MAAM,IAAI;AAAA,QAC1C,SAAQ,IAAI,IAAI;AAAA,EACvB;AACF,CAAC;AAED,QAAQ,IAAI,KAAK,UAAU;AAAA,EACzB,OAAO;AAAA,EACP,SAAS;AAAA,EACT,KAAK,OAAO;AACd,CAAC,CAAC;AAEF,WAAW,UAAU,CAAC,UAAU,SAAS,GAAY;AACnD,UAAQ,KAAK,QAAQ,YAAY;AAC/B,UAAM,OAAO,MAAM;AACnB,YAAQ,KAAK,CAAC;AAAA,EAChB,CAAC;AACH;","names":[]}
package/dist/catalog.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { I as IntegrationToolDefinition, a as IntegrationToolSearchFilters, b as IntegrationToolSearchResult, M as McpToolDefinition, c as buildIntegrationToolCatalog, i as integrationToolName, p as parseIntegrationToolName, s as searchIntegrationTools, t as toMcpTools } from './registry.js';
2
- import './index-BNb1A0Id.js';
1
+ export { I as IntegrationCatalogView, a as IntegrationToolDefinition, b as IntegrationToolSearchFilters, c as IntegrationToolSearchResult, M as McpToolDefinition, d as buildIntegrationCatalogView, e as buildIntegrationToolCatalog, i as integrationToolName, p as parseIntegrationToolName, s as searchIntegrationTools, t as toMcpTools } from './registry.js';
2
+ import './index-BQY5ry2s.js';
3
3
  import './connectors/index.js';
4
4
  import 'node:http';
package/dist/catalog.js CHANGED
@@ -1,11 +1,16 @@
1
1
  import {
2
+ buildIntegrationCatalogView,
2
3
  buildIntegrationToolCatalog,
3
4
  integrationToolName,
4
5
  parseIntegrationToolName,
5
6
  searchIntegrationTools,
6
7
  toMcpTools
7
- } from "./chunk-6KWCC42J.js";
8
+ } from "./chunk-S54DPRDU.js";
9
+ import "./chunk-4JQ754PA.js";
10
+ import "./chunk-376UBTNB.js";
11
+ import "./chunk-WC63AI4Q.js";
8
12
  export {
13
+ buildIntegrationCatalogView,
9
14
  buildIntegrationToolCatalog,
10
15
  integrationToolName,
11
16
  parseIntegrationToolName,
@@ -1,7 +1,3 @@
1
- import {
2
- buildIntegrationToolCatalog,
3
- parseIntegrationToolName
4
- } from "./chunk-6KWCC42J.js";
5
1
  import {
6
2
  integrationSpecToConnector,
7
3
  listIntegrationSpecs
@@ -1222,6 +1218,9 @@ async function receiveIntegrationWebhook(input) {
1222
1218
  if (!input.adapter.handleInboundEvent) {
1223
1219
  return { status: 405, body: { ok: false, error: "Connector does not support inbound webhooks." }, received: [], duplicates: [] };
1224
1220
  }
1221
+ if (!input.adapter.verifySignature && !input.allowUnsignedWebhook) {
1222
+ return { status: 401, body: { ok: false, error: "Webhook signature verification is required." }, received: [], duplicates: [] };
1223
+ }
1225
1224
  const signature = input.adapter.verifySignature?.({
1226
1225
  rawBody: input.rawBody,
1227
1226
  headers: input.headers,
@@ -1305,16 +1304,28 @@ var DefaultIntegrationActionGuard = class {
1305
1304
  idempotency;
1306
1305
  audit;
1307
1306
  rateLimiter;
1307
+ requireIdempotencyForMutations;
1308
1308
  now;
1309
1309
  constructor(options = {}) {
1310
1310
  this.idempotency = options.idempotency;
1311
1311
  this.audit = options.audit;
1312
1312
  this.rateLimiter = options.rateLimiter;
1313
+ this.requireIdempotencyForMutations = options.requireIdempotencyForMutations ?? false;
1313
1314
  this.now = options.now ?? (() => /* @__PURE__ */ new Date());
1314
1315
  }
1315
1316
  async invokeAction(ctx, proceed) {
1316
1317
  const idempotencyKey = ctx.request.idempotencyKey;
1317
1318
  const requestHash = hashRequest(ctx);
1319
+ if (this.requireIdempotencyForMutations && ctx.action?.risk !== "read" && !idempotencyKey) {
1320
+ return {
1321
+ ok: false,
1322
+ action: ctx.request.action,
1323
+ output: {
1324
+ idempotencyRequired: true,
1325
+ message: "State-changing integration actions require an idempotency key."
1326
+ }
1327
+ };
1328
+ }
1318
1329
  if (idempotencyKey && this.idempotency) {
1319
1330
  const existing = await this.idempotency.get(idempotencyKey);
1320
1331
  if (existing) {
@@ -2934,6 +2945,263 @@ async function checkActivepiecesPublicCatalog(input) {
2934
2945
  }
2935
2946
  }
2936
2947
 
2948
+ // src/runtime.ts
2949
+ var InMemoryIntegrationGrantStore = class {
2950
+ grants = /* @__PURE__ */ new Map();
2951
+ get(grantId) {
2952
+ return this.grants.get(grantId);
2953
+ }
2954
+ put(grant) {
2955
+ this.grants.set(grant.id, grant);
2956
+ }
2957
+ listByManifest(manifestId, grantee) {
2958
+ return [...this.grants.values()].filter(
2959
+ (grant) => grant.manifestId === manifestId && (!grantee || sameActor(grant.grantee, grantee))
2960
+ );
2961
+ }
2962
+ listByGrantee(grantee) {
2963
+ return [...this.grants.values()].filter((grant) => sameActor(grant.grantee, grantee));
2964
+ }
2965
+ listByIds(grantIds) {
2966
+ const wanted = new Set(grantIds);
2967
+ return [...this.grants.values()].filter((grant) => wanted.has(grant.id));
2968
+ }
2969
+ delete(grantId) {
2970
+ this.grants.delete(grantId);
2971
+ }
2972
+ };
2973
+ var IntegrationRuntime = class {
2974
+ hub;
2975
+ grants;
2976
+ now;
2977
+ constructor(options) {
2978
+ this.hub = options.hub;
2979
+ this.grants = options.grants ?? new InMemoryIntegrationGrantStore();
2980
+ this.now = options.now ?? (() => /* @__PURE__ */ new Date());
2981
+ }
2982
+ async registry() {
2983
+ return this.hub.listRegistry();
2984
+ }
2985
+ async resolveManifest(manifest, owner) {
2986
+ const registry = await this.registry();
2987
+ const connections = await this.hub.listConnections(owner);
2988
+ const resolutions = manifest.requirements.map(
2989
+ (requirement) => resolveRequirement(requirement, owner, registry, connections)
2990
+ );
2991
+ return {
2992
+ manifest,
2993
+ owner,
2994
+ ready: resolutions.filter((resolution) => resolution.status === "ready"),
2995
+ missing: resolutions.filter((resolution) => resolution.status !== "ready" && !resolution.requirement.optional),
2996
+ optionalMissing: resolutions.filter((resolution) => resolution.status !== "ready" && resolution.requirement.optional === true)
2997
+ };
2998
+ }
2999
+ async createGrants(input) {
3000
+ const resolution = await this.resolveManifest(input.manifest, input.owner);
3001
+ if (resolution.missing.length > 0) {
3002
+ throw new Error(`Cannot create integration grants; missing requirements: ${resolution.missing.map((r) => r.requirement.id).join(", ")}`);
3003
+ }
3004
+ const now = this.now().toISOString();
3005
+ const grants = resolution.ready.map((ready) => ({
3006
+ id: `grant_${input.manifest.id}_${ready.requirement.id}_${ready.connection.id}`,
3007
+ manifestId: input.manifest.id,
3008
+ requirementId: ready.requirement.id,
3009
+ owner: input.owner,
3010
+ grantee: input.grantee,
3011
+ connectionId: ready.connection.id,
3012
+ connectorId: ready.connector.id,
3013
+ scopes: requiredScopes(ready.requirement, ready.connector),
3014
+ allowedActions: requiredActions(ready.requirement, ready.connector),
3015
+ allowedTriggers: requiredTriggers(ready.requirement, ready.connector),
3016
+ status: "active",
3017
+ createdAt: now,
3018
+ updatedAt: now,
3019
+ metadata: input.metadata
3020
+ }));
3021
+ for (const grant of grants) await this.grants.put(grant);
3022
+ return grants;
3023
+ }
3024
+ async buildSandboxBundle(input) {
3025
+ const grants = await this.resolveBundleGrants(input);
3026
+ if (grants.length === 0) {
3027
+ throw new Error("Cannot build integration bundle; no active integration grants matched the request.");
3028
+ }
3029
+ const registry = await this.registry();
3030
+ const bindings = [];
3031
+ const connectors = [];
3032
+ let expiresAt = "";
3033
+ for (const grant of grants) {
3034
+ const connection = await this.hub.getConnection(grant.connectionId);
3035
+ if (!connection) {
3036
+ throw new Error(`Cannot build integration bundle; connection ${grant.connectionId} not found.`);
3037
+ }
3038
+ if (!sameActor(connection.owner, input.owner)) {
3039
+ throw new Error(`Cannot build integration bundle; connection ${connection.id} belongs to a different owner.`);
3040
+ }
3041
+ if (connection.status !== "active") {
3042
+ throw new Error(`Cannot build integration bundle; connection ${connection.id} is ${connection.status}.`);
3043
+ }
3044
+ const entry = registry.byId.get(grant.connectorId);
3045
+ if (!entry) continue;
3046
+ const connector = {
3047
+ ...entry.connector,
3048
+ actions: entry.connector.actions.filter((action) => grant.allowedActions.includes(action.id)),
3049
+ triggers: entry.connector.triggers?.filter((trigger2) => grant.allowedTriggers.includes(trigger2.id)),
3050
+ scopes: entry.connector.scopes.filter((scope) => grant.scopes.includes(scope))
3051
+ };
3052
+ const capability = await this.hub.issueCapability({
3053
+ subject: input.subject,
3054
+ connectionId: grant.connectionId,
3055
+ scopes: grant.scopes,
3056
+ allowedActions: grant.allowedActions,
3057
+ ttlMs: input.ttlMs,
3058
+ metadata: {
3059
+ manifestId: grant.manifestId,
3060
+ grantId: grant.id,
3061
+ requirementId: grant.requirementId
3062
+ }
3063
+ });
3064
+ bindings.push({
3065
+ requirementId: grant.requirementId,
3066
+ connectorId: grant.connectorId,
3067
+ connectionId: grant.connectionId,
3068
+ grantId: grant.id,
3069
+ scopes: grant.scopes,
3070
+ allowedActions: grant.allowedActions,
3071
+ allowedTriggers: grant.allowedTriggers,
3072
+ capability
3073
+ });
3074
+ connectors.push(connector);
3075
+ expiresAt = capability.capability.expiresAt;
3076
+ }
3077
+ return {
3078
+ manifestId: input.manifestId ?? grants[0]?.manifestId ?? "explicit-grants",
3079
+ subject: input.subject,
3080
+ capabilities: bindings,
3081
+ connectors,
3082
+ tools: buildIntegrationToolCatalog(connectors),
3083
+ expiresAt
3084
+ };
3085
+ }
3086
+ async resolveBundleGrants(input) {
3087
+ if (input.grantIds?.length) {
3088
+ const uniqueGrantIds = unique5(input.grantIds);
3089
+ const grants = this.grants.listByIds ? await this.grants.listByIds(uniqueGrantIds) : await Promise.all(uniqueGrantIds.map((grantId) => this.grants.get(grantId)));
3090
+ const found = grants.filter((grant) => Boolean(grant));
3091
+ const foundIds = new Set(found.map((grant) => grant.id));
3092
+ const missing2 = uniqueGrantIds.filter((grantId) => !foundIds.has(grantId));
3093
+ if (missing2.length > 0) {
3094
+ throw new Error(`Cannot build integration bundle; unknown grant id(s): ${missing2.join(", ")}`);
3095
+ }
3096
+ const badOwner = found.find((grant) => !sameActor(grant.owner, input.owner));
3097
+ if (badOwner) {
3098
+ throw new Error(`Cannot build integration bundle; grant ${badOwner.id} belongs to a different owner.`);
3099
+ }
3100
+ const badGrantee = input.grantee ? found.find((grant) => !sameActor(grant.grantee, input.grantee)) : void 0;
3101
+ if (badGrantee) {
3102
+ throw new Error(`Cannot build integration bundle; grant ${badGrantee.id} belongs to a different grantee.`);
3103
+ }
3104
+ const badManifest = input.manifestId ? found.find((grant) => grant.manifestId !== input.manifestId) : void 0;
3105
+ if (badManifest) {
3106
+ throw new Error(`Cannot build integration bundle; grant ${badManifest.id} is not for manifest ${input.manifestId}.`);
3107
+ }
3108
+ const inactive = found.find((grant) => grant.status !== "active");
3109
+ if (inactive) {
3110
+ throw new Error(`Cannot build integration bundle; grant ${inactive.id} is ${inactive.status}.`);
3111
+ }
3112
+ return found;
3113
+ }
3114
+ if (!input.manifestId) {
3115
+ throw new Error("Cannot build integration bundle; manifestId or grantIds is required.");
3116
+ }
3117
+ return (await this.grants.listByManifest(input.manifestId, input.grantee)).filter((grant) => sameActor(grant.owner, input.owner)).filter((grant) => grant.status === "active");
3118
+ }
3119
+ };
3120
+ function createIntegrationRuntime(options) {
3121
+ return new IntegrationRuntime(options);
3122
+ }
3123
+ function resolveRequirement(requirement, owner, registry, connections) {
3124
+ const entry = registry.byId.get(requirement.connectorId);
3125
+ if (!entry) {
3126
+ return missing(requirement, "unknown_connector", `Unknown connector ${requirement.connectorId}.`);
3127
+ }
3128
+ const connector = entry.connector;
3129
+ if (connector.actions.length === 0 && (connector.triggers?.length ?? 0) === 0) {
3130
+ return missing(requirement, "not_executable", `${connector.title} is catalog-only and cannot be invoked yet.`, connector, entry);
3131
+ }
3132
+ const scopes = requiredScopes(requirement, connector);
3133
+ const actions = requiredActions(requirement, connector);
3134
+ const triggers = requiredTriggers(requirement, connector);
3135
+ const connection = connections.find(
3136
+ (candidate) => sameActor(candidate.owner, owner) && candidate.status === "active" && (candidate.connectorId === connector.id || entry.aliases.includes(candidate.connectorId)) && scopes.every((scope) => candidate.grantedScopes.includes(scope))
3137
+ );
3138
+ if (!connection) {
3139
+ return {
3140
+ requirement,
3141
+ status: "missing_connection",
3142
+ connector,
3143
+ registryEntry: entry,
3144
+ missingScopes: scopes,
3145
+ missingActions: actions,
3146
+ missingTriggers: triggers,
3147
+ message: `${connector.title} needs an active user connection with the required scopes.`
3148
+ };
3149
+ }
3150
+ return {
3151
+ requirement,
3152
+ status: "ready",
3153
+ connector,
3154
+ registryEntry: entry,
3155
+ connection,
3156
+ missingScopes: [],
3157
+ missingActions: [],
3158
+ missingTriggers: [],
3159
+ message: `${connector.title} is ready.`
3160
+ };
3161
+ }
3162
+ function missing(requirement, status, message, connector, registryEntry2) {
3163
+ return {
3164
+ requirement,
3165
+ status,
3166
+ connector,
3167
+ registryEntry: registryEntry2,
3168
+ missingScopes: [],
3169
+ missingActions: [],
3170
+ missingTriggers: [],
3171
+ message
3172
+ };
3173
+ }
3174
+ function requiredActions(requirement, connector) {
3175
+ if (requirement.mode === "trigger") return [];
3176
+ if (requirement.requiredActions?.length) return unique5(requirement.requiredActions);
3177
+ const actions = connector.actions.filter((action) => {
3178
+ if (requirement.mode === "read") return action.risk === "read";
3179
+ if (requirement.mode === "write") return action.risk === "write";
3180
+ return false;
3181
+ });
3182
+ return unique5(actions.map((action) => action.id));
3183
+ }
3184
+ function requiredTriggers(requirement, connector) {
3185
+ if (requirement.requiredTriggers?.length) return unique5(requirement.requiredTriggers);
3186
+ if (requirement.mode !== "trigger") return [];
3187
+ return unique5((connector.triggers ?? []).map((trigger2) => trigger2.id));
3188
+ }
3189
+ function requiredScopes(requirement, connector) {
3190
+ if (requirement.requiredScopes?.length) return unique5(requirement.requiredScopes);
3191
+ const actionIds = new Set(requiredActions(requirement, connector));
3192
+ const triggerIds = new Set(requiredTriggers(requirement, connector));
3193
+ return unique5([
3194
+ ...connector.actions.filter((action) => actionIds.has(action.id)).flatMap((action) => action.requiredScopes),
3195
+ ...(connector.triggers ?? []).filter((trigger2) => triggerIds.has(trigger2.id)).flatMap((trigger2) => trigger2.requiredScopes)
3196
+ ]);
3197
+ }
3198
+ function sameActor(a, b) {
3199
+ return a.type === b.type && a.id === b.id;
3200
+ }
3201
+ function unique5(values) {
3202
+ return [...new Set(values)];
3203
+ }
3204
+
2937
3205
  // src/workflow.ts
2938
3206
  var InMemoryIntegrationWorkflowStore = class {
2939
3207
  workflows = /* @__PURE__ */ new Map();
@@ -2950,7 +3218,7 @@ var InMemoryIntegrationWorkflowStore = class {
2950
3218
  return [...this.workflows.values()].filter((workflow) => workflow.workflowId === workflowId);
2951
3219
  }
2952
3220
  listByOwner(owner) {
2953
- return [...this.workflows.values()].filter((workflow) => sameActor(workflow.owner, owner));
3221
+ return [...this.workflows.values()].filter((workflow) => sameActor2(workflow.owner, owner));
2954
3222
  }
2955
3223
  };
2956
3224
  var IntegrationWorkflowRuntime = class {
@@ -3012,7 +3280,7 @@ function findTriggerGrant(grants, requirementId, triggerId) {
3012
3280
  if (!grant) throw new Error(`Missing trigger grant ${requirementId}/${triggerId}.`);
3013
3281
  return grant;
3014
3282
  }
3015
- function sameActor(a, b) {
3283
+ function sameActor2(a, b) {
3016
3284
  return a.type === b.type && a.id === b.id;
3017
3285
  }
3018
3286
 
@@ -3091,6 +3359,9 @@ var IntegrationHub = class {
3091
3359
  async listConnections(owner) {
3092
3360
  return this.store.listByOwner(owner);
3093
3361
  }
3362
+ async getConnection(connectionId) {
3363
+ return this.store.get(connectionId);
3364
+ }
3094
3365
  async issueCapability(request) {
3095
3366
  const connection = await this.requireConnection(request.connectionId);
3096
3367
  this.assertConnectionActive(connection);
@@ -3100,8 +3371,8 @@ var IntegrationHub = class {
3100
3371
  id: `cap_${randomUUID5()}`,
3101
3372
  subject: request.subject,
3102
3373
  connectionId: request.connectionId,
3103
- scopes: unique5(request.scopes),
3104
- allowedActions: unique5(request.allowedActions),
3374
+ scopes: unique6(request.scopes),
3375
+ allowedActions: unique6(request.allowedActions),
3105
3376
  issuedAt: now.toISOString(),
3106
3377
  expiresAt: new Date(now.getTime() + request.ttlMs).toISOString(),
3107
3378
  metadata: request.metadata
@@ -3329,9 +3600,9 @@ async function postJson(fetcher, url, body, bearer) {
3329
3600
  if (!response.ok) throw new IntegrationError(`Integration provider returned HTTP ${response.status}.`, "provider_not_found");
3330
3601
  return response.json();
3331
3602
  }
3332
- function assertScopes(connection, requiredScopes) {
3333
- const missing = requiredScopes.filter((scope) => !connection.grantedScopes.includes(scope));
3334
- if (missing.length > 0) throw new IntegrationError(`Missing integration scopes: ${missing.join(", ")}`, "scope_denied");
3603
+ function assertScopes(connection, requiredScopes2) {
3604
+ const missing2 = requiredScopes2.filter((scope) => !connection.grantedScopes.includes(scope));
3605
+ if (missing2.length > 0) throw new IntegrationError(`Missing integration scopes: ${missing2.join(", ")}`, "scope_denied");
3335
3606
  }
3336
3607
  function hmac(payload, secret) {
3337
3608
  return createHmac2("sha256", secret).update(payload).digest("base64url");
@@ -3347,7 +3618,7 @@ function base64UrlEncode(value) {
3347
3618
  function base64UrlDecode(value) {
3348
3619
  return Buffer.from(value, "base64url").toString("utf8");
3349
3620
  }
3350
- function unique5(values) {
3621
+ function unique6(values) {
3351
3622
  return [...new Set(values)];
3352
3623
  }
3353
3624
 
@@ -3770,12 +4041,12 @@ function registryEntry(canonicalId, candidates, precedence, aliases) {
3770
4041
  const primary = ordered[0];
3771
4042
  const actions = mergeActions(ordered);
3772
4043
  const triggers = mergeTriggers(ordered);
3773
- const scopes = unique6(toolBindableCandidates(ordered).flatMap((candidate) => candidate.connector.scopes ?? []));
4044
+ const scopes = unique7(toolBindableCandidates(ordered).flatMap((candidate) => candidate.connector.scopes ?? []));
3774
4045
  const supportTier = ordered.reduce(
3775
4046
  (best, candidate) => SUPPORT_RANK[candidate.supportTier] > SUPPORT_RANK[best] ? candidate.supportTier : best,
3776
4047
  primary.supportTier
3777
4048
  );
3778
- const aliasesForEntry = unique6([
4049
+ const aliasesForEntry = unique7([
3779
4050
  ...ordered.map((candidate) => candidate.connector.id),
3780
4051
  ...Object.entries(aliases).filter(([, target]) => canonicalConnectorId(target, aliases) === canonicalId).map(([alias]) => alias)
3781
4052
  ].map(slug2).filter((id) => id && id !== canonicalId)).sort();
@@ -3871,9 +4142,157 @@ function isSupportTier(value) {
3871
4142
  function slug2(value) {
3872
4143
  return value.trim().toLowerCase().replace(/&/g, "and").replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
3873
4144
  }
3874
- function unique6(values) {
4145
+ function unique7(values) {
4146
+ return [...new Set(values)];
4147
+ }
4148
+
4149
+ // src/catalog.ts
4150
+ var riskRank2 = {
4151
+ read: 0,
4152
+ write: 1,
4153
+ destructive: 2
4154
+ };
4155
+ function integrationToolName(providerId, connectorId, actionId) {
4156
+ return `int_${encodeToolPart(providerId)}_${encodeToolPart(connectorId)}_${encodeToolPart(actionId)}`;
4157
+ }
4158
+ function parseIntegrationToolName(name) {
4159
+ const parts = name.split("_");
4160
+ if (parts.length !== 4 || parts[0] !== "int") {
4161
+ throw new Error(`Invalid integration tool name: ${name}`);
4162
+ }
4163
+ return {
4164
+ providerId: decodeToolPart(parts[1]),
4165
+ connectorId: decodeToolPart(parts[2]),
4166
+ actionId: decodeToolPart(parts[3])
4167
+ };
4168
+ }
4169
+ function buildIntegrationToolCatalog(connectors) {
4170
+ const tools = [];
4171
+ for (const connector of connectors) {
4172
+ for (const action of connector.actions) {
4173
+ const tags = unique8([
4174
+ connector.id,
4175
+ connector.providerId,
4176
+ connector.title,
4177
+ connector.category,
4178
+ action.id,
4179
+ action.title,
4180
+ action.risk,
4181
+ action.dataClass,
4182
+ ...connector.scopes ?? [],
4183
+ ...action.requiredScopes ?? []
4184
+ ].flatMap(tokenize));
4185
+ tools.push({
4186
+ name: integrationToolName(connector.providerId, connector.id, action.id),
4187
+ title: `${connector.title}: ${action.title}`,
4188
+ description: action.description ?? `${action.risk} action ${action.id} on ${connector.title}`,
4189
+ providerId: connector.providerId,
4190
+ connectorId: connector.id,
4191
+ connectorTitle: connector.title,
4192
+ category: connector.category,
4193
+ action,
4194
+ risk: action.risk,
4195
+ dataClass: action.dataClass,
4196
+ requiredScopes: action.requiredScopes,
4197
+ inputSchema: action.inputSchema,
4198
+ outputSchema: action.outputSchema,
4199
+ tags,
4200
+ supportTier: toolSupportTier(connector),
4201
+ runnable: toolRunnable(connector)
4202
+ });
4203
+ }
4204
+ }
4205
+ return tools;
4206
+ }
4207
+ function buildIntegrationCatalogView(input) {
4208
+ const runtimeConnectors = input.executableRegistry?.connectors ?? input.discoveryRegistry.connectors.filter((connector) => toolRunnable(connector));
4209
+ const runtimeTools = buildIntegrationToolCatalog(runtimeConnectors);
4210
+ const discoveryTools = buildIntegrationToolCatalog(input.discoveryRegistry.connectors);
4211
+ return {
4212
+ connectors: input.discoveryRegistry.connectors,
4213
+ conflicts: input.discoveryRegistry.entries.flatMap((entry) => entry.conflicts),
4214
+ summary: summarizeIntegrationRegistry(input.discoveryRegistry),
4215
+ tools: runtimeTools,
4216
+ runtimeTools,
4217
+ discoveryTools
4218
+ };
4219
+ }
4220
+ function searchIntegrationTools(catalog, query, filters = {}) {
4221
+ const terms = tokenize(query);
4222
+ const filtered = catalog.filter((tool) => {
4223
+ if (filters.providerId && tool.providerId !== filters.providerId) return false;
4224
+ if (filters.connectorId && tool.connectorId !== filters.connectorId) return false;
4225
+ if (filters.category && tool.category !== filters.category) return false;
4226
+ if (filters.dataClass && tool.dataClass !== filters.dataClass) return false;
4227
+ if (filters.maxRisk && riskRank2[tool.risk] > riskRank2[filters.maxRisk]) return false;
4228
+ return true;
4229
+ });
4230
+ const scored = filtered.map((tool) => scoreTool(tool, terms));
4231
+ 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);
4232
+ }
4233
+ function toMcpTools(tools) {
4234
+ return tools.map((tool) => ({
4235
+ name: tool.name,
4236
+ description: `${tool.title}. ${tool.description}`,
4237
+ inputSchema: tool.inputSchema ?? {
4238
+ type: "object",
4239
+ additionalProperties: true,
4240
+ properties: {}
4241
+ }
4242
+ }));
4243
+ }
4244
+ function scoreTool(tool, terms) {
4245
+ if (terms.length === 0) return { tool, score: 1, matched: [] };
4246
+ const haystack = new Set(tool.tags);
4247
+ const matched = [];
4248
+ let score = 0;
4249
+ for (const term of terms) {
4250
+ if (haystack.has(term)) {
4251
+ matched.push(term);
4252
+ score += 4;
4253
+ continue;
4254
+ }
4255
+ if (tool.tags.some((tag) => tag.includes(term))) {
4256
+ matched.push(term);
4257
+ score += 1;
4258
+ }
4259
+ }
4260
+ if (tool.risk === "read") score += 0.25;
4261
+ return { tool, score, matched: unique8(matched) };
4262
+ }
4263
+ function tokenize(value) {
4264
+ return value.toLowerCase().split(/[^a-z0-9]+/g).map((part) => part.trim()).filter(Boolean);
4265
+ }
4266
+ function encodeToolPart(value) {
4267
+ return Buffer.from(value, "utf8").toString("base64url").replace(/_/g, ".");
4268
+ }
4269
+ function decodeToolPart(value) {
4270
+ return Buffer.from(value.replace(/\./g, "_"), "base64url").toString("utf8");
4271
+ }
4272
+ function unique8(values) {
3875
4273
  return [...new Set(values)];
3876
4274
  }
4275
+ function toolSupportTier(connector) {
4276
+ const registry = connector.metadata?.registry;
4277
+ if (registry && typeof registry === "object" && !Array.isArray(registry)) {
4278
+ const supportTier2 = registry.supportTier;
4279
+ return isIntegrationSupportTier(supportTier2) ? supportTier2 : void 0;
4280
+ }
4281
+ const supportTier = connector.metadata?.supportTier;
4282
+ return isIntegrationSupportTier(supportTier) ? supportTier : void 0;
4283
+ }
4284
+ function toolRunnable(connector) {
4285
+ const registry = connector.metadata?.registry;
4286
+ if (registry && typeof registry === "object" && !Array.isArray(registry)) {
4287
+ const toolBindable = registry.toolBindable;
4288
+ if (typeof toolBindable === "boolean") return toolBindable;
4289
+ }
4290
+ const tier = toolSupportTier(connector);
4291
+ return tier === "gatewayExecutable" || tier === "firstPartyExecutable" || tier === "sandboxExecutable";
4292
+ }
4293
+ function isIntegrationSupportTier(value) {
4294
+ return value === "catalogOnly" || value === "setupReady" || value === "gatewayExecutable" || value === "firstPartyExecutable" || value === "sandboxExecutable";
4295
+ }
3877
4296
 
3878
4297
  export {
3879
4298
  ACTIVEPIECES_OVERRIDES,
@@ -3882,6 +4301,12 @@ export {
3882
4301
  buildActivepiecesConnectors,
3883
4302
  createCatalogExecutorProvider,
3884
4303
  createActivepiecesExecutorProvider,
4304
+ integrationToolName,
4305
+ parseIntegrationToolName,
4306
+ buildIntegrationToolCatalog,
4307
+ buildIntegrationCatalogView,
4308
+ searchIntegrationTools,
4309
+ toMcpTools,
3885
4310
  ACTIVEPIECES_PUBLIC_CATALOG_URL,
3886
4311
  extractActivepiecesPublicPieceCount,
3887
4312
  auditIntegrationCatalogFreshness,
@@ -3988,6 +4413,9 @@ export {
3988
4413
  tangleCatalogAuthValue,
3989
4414
  createTangleCatalogRuntimeNodeRequestListener,
3990
4415
  startTangleCatalogRuntimeNodeServer,
4416
+ InMemoryIntegrationGrantStore,
4417
+ IntegrationRuntime,
4418
+ createIntegrationRuntime,
3991
4419
  InMemoryIntegrationWorkflowStore,
3992
4420
  IntegrationWorkflowRuntime,
3993
4421
  createIntegrationWorkflowRuntime,
@@ -4000,4 +4428,4 @@ export {
4000
4428
  signCapability,
4001
4429
  verifyCapabilityToken
4002
4430
  };
4003
- //# sourceMappingURL=chunk-IOO75SJH.js.map
4431
+ //# sourceMappingURL=chunk-S54DPRDU.js.map