@tangle-network/agent-integrations 0.14.0 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -0
- package/dist/index.d.ts +552 -248
- package/dist/index.js +938 -164
- package/dist/index.js.map +1 -1
- package/docs/architecture.md +7 -0
- package/docs/production-completion-checklist.md +63 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
|
-
import { createHmac as createHmac3, randomUUID as
|
|
2
|
+
import { createHmac as createHmac3, randomUUID as randomUUID3, timingSafeEqual as timingSafeEqual2 } from "crypto";
|
|
3
3
|
|
|
4
4
|
// src/activepieces-catalog.ts
|
|
5
5
|
import { readFileSync } from "fs";
|
|
@@ -1290,6 +1290,869 @@ function unique(values) {
|
|
|
1290
1290
|
return [...new Set(values)];
|
|
1291
1291
|
}
|
|
1292
1292
|
|
|
1293
|
+
// src/audit.ts
|
|
1294
|
+
import { randomUUID } from "crypto";
|
|
1295
|
+
var InMemoryIntegrationAuditStore = class {
|
|
1296
|
+
events = [];
|
|
1297
|
+
record(event) {
|
|
1298
|
+
this.events.push(event);
|
|
1299
|
+
}
|
|
1300
|
+
list(filter = {}) {
|
|
1301
|
+
return this.events.filter((event) => matchesFilter(event, filter));
|
|
1302
|
+
}
|
|
1303
|
+
};
|
|
1304
|
+
function createIntegrationAuditEvent(input) {
|
|
1305
|
+
const occurredAt = input.occurredAt instanceof Date ? input.occurredAt.toISOString() : input.occurredAt ?? (input.now?.() ?? /* @__PURE__ */ new Date()).toISOString();
|
|
1306
|
+
return {
|
|
1307
|
+
...input,
|
|
1308
|
+
id: input.id ?? `audit_${randomUUID()}`,
|
|
1309
|
+
occurredAt,
|
|
1310
|
+
metadata: input.metadata ? redactUnknown(input.metadata) : void 0
|
|
1311
|
+
};
|
|
1312
|
+
}
|
|
1313
|
+
function createAuditingActionGuard(options) {
|
|
1314
|
+
const now = options.now ?? (() => /* @__PURE__ */ new Date());
|
|
1315
|
+
return {
|
|
1316
|
+
async invokeAction(ctx, proceed) {
|
|
1317
|
+
const startedAt = now();
|
|
1318
|
+
try {
|
|
1319
|
+
const result = await proceed();
|
|
1320
|
+
await options.sink.record(actionEvent({
|
|
1321
|
+
ctx,
|
|
1322
|
+
request: ctx.request,
|
|
1323
|
+
result,
|
|
1324
|
+
type: result.ok ? "action.invoked" : "action.failed",
|
|
1325
|
+
subject: options.subject,
|
|
1326
|
+
occurredAt: startedAt,
|
|
1327
|
+
includeInputPreview: options.includeInputPreview
|
|
1328
|
+
}));
|
|
1329
|
+
return result;
|
|
1330
|
+
} catch (error) {
|
|
1331
|
+
await options.sink.record(actionEvent({
|
|
1332
|
+
ctx,
|
|
1333
|
+
request: ctx.request,
|
|
1334
|
+
type: "action.failed",
|
|
1335
|
+
subject: options.subject,
|
|
1336
|
+
occurredAt: startedAt,
|
|
1337
|
+
includeInputPreview: options.includeInputPreview,
|
|
1338
|
+
message: error instanceof Error ? error.message : "Integration action failed."
|
|
1339
|
+
}));
|
|
1340
|
+
throw error;
|
|
1341
|
+
}
|
|
1342
|
+
}
|
|
1343
|
+
};
|
|
1344
|
+
}
|
|
1345
|
+
function sanitizeAuditConnection(connection) {
|
|
1346
|
+
return {
|
|
1347
|
+
id: connection.id,
|
|
1348
|
+
owner: connection.owner,
|
|
1349
|
+
providerId: connection.providerId,
|
|
1350
|
+
connectorId: connection.connectorId,
|
|
1351
|
+
status: connection.status,
|
|
1352
|
+
grantedScopes: connection.grantedScopes,
|
|
1353
|
+
account: connection.account,
|
|
1354
|
+
hasSecretRef: Boolean(connection.secretRef),
|
|
1355
|
+
createdAt: connection.createdAt,
|
|
1356
|
+
updatedAt: connection.updatedAt,
|
|
1357
|
+
expiresAt: connection.expiresAt,
|
|
1358
|
+
lastUsedAt: connection.lastUsedAt
|
|
1359
|
+
};
|
|
1360
|
+
}
|
|
1361
|
+
function actionEvent(input) {
|
|
1362
|
+
return createIntegrationAuditEvent({
|
|
1363
|
+
type: input.type,
|
|
1364
|
+
occurredAt: input.occurredAt,
|
|
1365
|
+
actor: input.subject ?? input.ctx.connection.owner,
|
|
1366
|
+
connectionId: input.ctx.connection.id,
|
|
1367
|
+
providerId: input.ctx.connection.providerId,
|
|
1368
|
+
connectorId: input.ctx.connection.connectorId,
|
|
1369
|
+
action: input.request.action,
|
|
1370
|
+
risk: input.ctx.action?.risk,
|
|
1371
|
+
dataClass: input.ctx.action?.dataClass,
|
|
1372
|
+
ok: input.result?.ok ?? false,
|
|
1373
|
+
message: input.message,
|
|
1374
|
+
metadata: {
|
|
1375
|
+
idempotencyKey: input.request.idempotencyKey,
|
|
1376
|
+
dryRun: input.request.dryRun,
|
|
1377
|
+
externalId: input.result?.externalId,
|
|
1378
|
+
warnings: input.result?.warnings,
|
|
1379
|
+
inputPreview: input.includeInputPreview ? redactUnknown(input.request.input) : void 0
|
|
1380
|
+
}
|
|
1381
|
+
});
|
|
1382
|
+
}
|
|
1383
|
+
function matchesFilter(event, filter) {
|
|
1384
|
+
if (filter.type && event.type !== filter.type) return false;
|
|
1385
|
+
if (filter.actor && (!event.actor || event.actor.type !== filter.actor.type || event.actor.id !== filter.actor.id)) return false;
|
|
1386
|
+
if (filter.connectionId && event.connectionId !== filter.connectionId) return false;
|
|
1387
|
+
if (filter.providerId && event.providerId !== filter.providerId) return false;
|
|
1388
|
+
if (filter.connectorId && event.connectorId !== filter.connectorId) return false;
|
|
1389
|
+
if (filter.action && event.action !== filter.action) return false;
|
|
1390
|
+
return true;
|
|
1391
|
+
}
|
|
1392
|
+
function redactUnknown(value) {
|
|
1393
|
+
if (Array.isArray(value)) return value.map(redactUnknown);
|
|
1394
|
+
if (!value || typeof value !== "object") return value;
|
|
1395
|
+
const out = {};
|
|
1396
|
+
for (const [key, child] of Object.entries(value)) {
|
|
1397
|
+
if (/token|secret|password|authorization|api[_-]?key|credential|refresh/i.test(key)) {
|
|
1398
|
+
out[key] = "[REDACTED]";
|
|
1399
|
+
} else {
|
|
1400
|
+
out[key] = redactUnknown(child);
|
|
1401
|
+
}
|
|
1402
|
+
}
|
|
1403
|
+
return out;
|
|
1404
|
+
}
|
|
1405
|
+
|
|
1406
|
+
// src/approval.ts
|
|
1407
|
+
import { createHash } from "crypto";
|
|
1408
|
+
var InMemoryIntegrationApprovalStore = class {
|
|
1409
|
+
records = /* @__PURE__ */ new Map();
|
|
1410
|
+
get(approvalId) {
|
|
1411
|
+
return this.records.get(approvalId);
|
|
1412
|
+
}
|
|
1413
|
+
put(record) {
|
|
1414
|
+
this.records.set(record.id, record);
|
|
1415
|
+
}
|
|
1416
|
+
list(filter = {}) {
|
|
1417
|
+
return [...this.records.values()].filter((record) => matchesFilter2(record, filter));
|
|
1418
|
+
}
|
|
1419
|
+
};
|
|
1420
|
+
var ApprovalBackedPolicyEngine = class {
|
|
1421
|
+
base;
|
|
1422
|
+
store;
|
|
1423
|
+
audit;
|
|
1424
|
+
now;
|
|
1425
|
+
approvalTtlMs;
|
|
1426
|
+
constructor(options) {
|
|
1427
|
+
this.base = options.base;
|
|
1428
|
+
this.store = options.store;
|
|
1429
|
+
this.audit = options.audit;
|
|
1430
|
+
this.now = options.now ?? (() => /* @__PURE__ */ new Date());
|
|
1431
|
+
this.approvalTtlMs = options.approvalTtlMs;
|
|
1432
|
+
}
|
|
1433
|
+
async decide(ctx) {
|
|
1434
|
+
const approved = await this.findApprovedRecord(ctx);
|
|
1435
|
+
if (approved) return { decision: "allow", reason: `Approved by ${approved.resolvedBy?.type ?? "actor"} ${approved.resolvedBy?.id ?? "unknown"}.`, metadata: { approvalId: approved.id } };
|
|
1436
|
+
const decision = await this.base.decide(ctx);
|
|
1437
|
+
if (decision.decision !== "require_approval") return decision;
|
|
1438
|
+
const requestedAt = decision.approval.requestedAt;
|
|
1439
|
+
const expiresAt = this.approvalTtlMs ? new Date(Date.parse(requestedAt) + this.approvalTtlMs).toISOString() : void 0;
|
|
1440
|
+
const record = {
|
|
1441
|
+
id: decision.approval.id,
|
|
1442
|
+
request: decision.approval,
|
|
1443
|
+
status: "pending",
|
|
1444
|
+
requestedAt,
|
|
1445
|
+
expiresAt,
|
|
1446
|
+
metadata: { ...decision.metadata ?? {}, inputHash: approvalInputHash(ctx.request.input) }
|
|
1447
|
+
};
|
|
1448
|
+
await this.store.put(record);
|
|
1449
|
+
await this.audit?.record(createIntegrationAuditEvent({
|
|
1450
|
+
type: "approval.requested",
|
|
1451
|
+
actor: ctx.subject,
|
|
1452
|
+
connectionId: ctx.connection.id,
|
|
1453
|
+
providerId: ctx.connection.providerId,
|
|
1454
|
+
connectorId: ctx.connection.connectorId,
|
|
1455
|
+
action: ctx.request.action,
|
|
1456
|
+
risk: ctx.action?.risk,
|
|
1457
|
+
dataClass: ctx.action?.dataClass,
|
|
1458
|
+
message: decision.reason,
|
|
1459
|
+
metadata: { approvalId: record.id },
|
|
1460
|
+
now: this.now
|
|
1461
|
+
}));
|
|
1462
|
+
return decision;
|
|
1463
|
+
}
|
|
1464
|
+
async findApprovedRecord(ctx) {
|
|
1465
|
+
const approvalId = typeof ctx.request.metadata?.approvalId === "string" ? ctx.request.metadata.approvalId : void 0;
|
|
1466
|
+
if (!approvalId) return void 0;
|
|
1467
|
+
const record = await this.store.get(approvalId);
|
|
1468
|
+
if (!record || record.status !== "approved") return void 0;
|
|
1469
|
+
if (record.expiresAt && Date.parse(record.expiresAt) <= this.now().getTime()) {
|
|
1470
|
+
await this.store.put({ ...record, status: "expired" });
|
|
1471
|
+
return void 0;
|
|
1472
|
+
}
|
|
1473
|
+
if (!approvalMatches(record, ctx)) return void 0;
|
|
1474
|
+
return record;
|
|
1475
|
+
}
|
|
1476
|
+
};
|
|
1477
|
+
function createApprovalBackedPolicyEngine(options) {
|
|
1478
|
+
return new ApprovalBackedPolicyEngine(options);
|
|
1479
|
+
}
|
|
1480
|
+
async function resolveIntegrationApproval(input) {
|
|
1481
|
+
const record = await input.store.get(input.approvalId);
|
|
1482
|
+
if (!record) throw new Error(`Approval ${input.approvalId} not found.`);
|
|
1483
|
+
const now = input.now ?? (() => /* @__PURE__ */ new Date());
|
|
1484
|
+
const next = {
|
|
1485
|
+
...record,
|
|
1486
|
+
status: input.approved ? "approved" : "denied",
|
|
1487
|
+
resolvedAt: now().toISOString(),
|
|
1488
|
+
resolvedBy: input.resolvedBy,
|
|
1489
|
+
reason: input.reason,
|
|
1490
|
+
metadata: { ...record.metadata ?? {}, ...input.metadata ?? {} }
|
|
1491
|
+
};
|
|
1492
|
+
await input.store.put(next);
|
|
1493
|
+
await input.audit?.record(createIntegrationAuditEvent({
|
|
1494
|
+
type: "approval.resolved",
|
|
1495
|
+
actor: input.resolvedBy,
|
|
1496
|
+
connectionId: record.request.connectionId,
|
|
1497
|
+
providerId: record.request.providerId,
|
|
1498
|
+
connectorId: record.request.connectorId,
|
|
1499
|
+
action: record.request.action,
|
|
1500
|
+
risk: record.request.risk,
|
|
1501
|
+
dataClass: record.request.dataClass,
|
|
1502
|
+
ok: input.approved,
|
|
1503
|
+
message: input.reason,
|
|
1504
|
+
metadata: { approvalId: record.id, status: next.status },
|
|
1505
|
+
now
|
|
1506
|
+
}));
|
|
1507
|
+
return next;
|
|
1508
|
+
}
|
|
1509
|
+
function approvalMatches(record, ctx) {
|
|
1510
|
+
return record.request.connectionId === ctx.connection.id && record.request.providerId === ctx.connection.providerId && record.request.connectorId === ctx.connection.connectorId && record.request.action === ctx.request.action && record.request.actor.type === ctx.subject.type && record.request.actor.id === ctx.subject.id && record.metadata?.inputHash === approvalInputHash(ctx.request.input);
|
|
1511
|
+
}
|
|
1512
|
+
function matchesFilter2(record, filter) {
|
|
1513
|
+
if (filter.status && record.status !== filter.status) return false;
|
|
1514
|
+
if (filter.connectionId && record.request.connectionId !== filter.connectionId) return false;
|
|
1515
|
+
if (filter.connectorId && record.request.connectorId !== filter.connectorId) return false;
|
|
1516
|
+
if (filter.action && record.request.action !== filter.action) return false;
|
|
1517
|
+
if (filter.actor && (record.request.actor.type !== filter.actor.type || record.request.actor.id !== filter.actor.id)) return false;
|
|
1518
|
+
return true;
|
|
1519
|
+
}
|
|
1520
|
+
function approvalInputHash(input) {
|
|
1521
|
+
return createHash("sha256").update(JSON.stringify(input ?? null)).digest("base64url");
|
|
1522
|
+
}
|
|
1523
|
+
|
|
1524
|
+
// src/bridge.ts
|
|
1525
|
+
var DEFAULT_INTEGRATION_BRIDGE_ENV = "TANGLE_INTEGRATION_BUNDLE";
|
|
1526
|
+
function buildIntegrationBridgePayload(bundle) {
|
|
1527
|
+
return {
|
|
1528
|
+
version: 1,
|
|
1529
|
+
manifestId: bundle.manifestId,
|
|
1530
|
+
subject: bundle.subject,
|
|
1531
|
+
expiresAt: bundle.expiresAt,
|
|
1532
|
+
tools: bundle.tools.flatMap((tool) => {
|
|
1533
|
+
const binding = bundle.capabilities.find(
|
|
1534
|
+
(candidate) => candidate.connectorId === tool.connectorId && candidate.connectionId && candidate.allowedActions.includes(tool.action.id)
|
|
1535
|
+
);
|
|
1536
|
+
if (!binding) return [];
|
|
1537
|
+
return [{
|
|
1538
|
+
name: tool.name,
|
|
1539
|
+
title: tool.title,
|
|
1540
|
+
connectorId: tool.connectorId,
|
|
1541
|
+
connectionId: binding.connectionId,
|
|
1542
|
+
action: tool.action.id,
|
|
1543
|
+
risk: tool.risk,
|
|
1544
|
+
dataClass: tool.dataClass,
|
|
1545
|
+
requiredScopes: tool.requiredScopes,
|
|
1546
|
+
capabilityToken: binding.capability.token
|
|
1547
|
+
}];
|
|
1548
|
+
})
|
|
1549
|
+
};
|
|
1550
|
+
}
|
|
1551
|
+
function encodeIntegrationBridgePayload(payload) {
|
|
1552
|
+
return Buffer.from(JSON.stringify(payload), "utf8").toString("base64url");
|
|
1553
|
+
}
|
|
1554
|
+
function decodeIntegrationBridgePayload(encoded) {
|
|
1555
|
+
const parsed = JSON.parse(Buffer.from(encoded, "base64url").toString("utf8"));
|
|
1556
|
+
assertBridgePayload(parsed);
|
|
1557
|
+
return parsed;
|
|
1558
|
+
}
|
|
1559
|
+
function buildIntegrationBridgeEnvironment(bundle, options = {}) {
|
|
1560
|
+
const envVar = options.envVar ?? DEFAULT_INTEGRATION_BRIDGE_ENV;
|
|
1561
|
+
return {
|
|
1562
|
+
[envVar]: encodeIntegrationBridgePayload(buildIntegrationBridgePayload(bundle))
|
|
1563
|
+
};
|
|
1564
|
+
}
|
|
1565
|
+
function parseIntegrationBridgeEnvironment(env, options = {}) {
|
|
1566
|
+
const envVar = options.envVar ?? DEFAULT_INTEGRATION_BRIDGE_ENV;
|
|
1567
|
+
const encoded = env[envVar];
|
|
1568
|
+
if (!encoded) throw new Error(`Missing ${envVar}.`);
|
|
1569
|
+
return decodeIntegrationBridgePayload(encoded);
|
|
1570
|
+
}
|
|
1571
|
+
function redactIntegrationBridgePayload(payload) {
|
|
1572
|
+
return {
|
|
1573
|
+
...payload,
|
|
1574
|
+
tools: payload.tools.map((tool) => ({
|
|
1575
|
+
...tool,
|
|
1576
|
+
capabilityToken: "[REDACTED]"
|
|
1577
|
+
}))
|
|
1578
|
+
};
|
|
1579
|
+
}
|
|
1580
|
+
function assertBridgePayload(value) {
|
|
1581
|
+
if (!value || typeof value !== "object") throw new Error("Invalid integration bridge payload.");
|
|
1582
|
+
const payload = value;
|
|
1583
|
+
if (payload.version !== 1) throw new Error("Unsupported integration bridge payload version.");
|
|
1584
|
+
if (typeof payload.manifestId !== "string") throw new Error("Invalid integration bridge manifestId.");
|
|
1585
|
+
if (typeof payload.expiresAt !== "string") throw new Error("Invalid integration bridge expiresAt.");
|
|
1586
|
+
if (!Array.isArray(payload.tools)) throw new Error("Invalid integration bridge tools.");
|
|
1587
|
+
}
|
|
1588
|
+
|
|
1589
|
+
// src/adapter-provider.ts
|
|
1590
|
+
function createConnectorAdapterProvider(options) {
|
|
1591
|
+
const providerId = options.id ?? "first-party";
|
|
1592
|
+
const now = options.now ?? (() => /* @__PURE__ */ new Date());
|
|
1593
|
+
const adapters = /* @__PURE__ */ new Map();
|
|
1594
|
+
for (const adapter of options.adapters) {
|
|
1595
|
+
adapters.set(adapter.manifest.kind, adapter);
|
|
1596
|
+
}
|
|
1597
|
+
return {
|
|
1598
|
+
id: providerId,
|
|
1599
|
+
kind: options.kind ?? "first_party",
|
|
1600
|
+
listConnectors: () => [...adapters.values()].map((adapter) => manifestToConnector(providerId, adapter)),
|
|
1601
|
+
async invokeAction(connection, request) {
|
|
1602
|
+
const adapter = adapters.get(connection.connectorId);
|
|
1603
|
+
if (!adapter) {
|
|
1604
|
+
throw new IntegrationError(`Connector adapter ${connection.connectorId} not found.`, "connector_not_found");
|
|
1605
|
+
}
|
|
1606
|
+
const capability = adapter.manifest.capabilities.find((candidate) => candidate.name === request.action);
|
|
1607
|
+
if (!capability) {
|
|
1608
|
+
throw new IntegrationError(`Capability ${request.action} is not defined by ${connection.connectorId}.`, "action_not_found");
|
|
1609
|
+
}
|
|
1610
|
+
const source = await options.resolveDataSource(connection);
|
|
1611
|
+
const invocation = {
|
|
1612
|
+
source,
|
|
1613
|
+
capabilityName: request.action,
|
|
1614
|
+
args: toRecord(request.input),
|
|
1615
|
+
idempotencyKey: request.idempotencyKey ?? `idem_${connection.id}_${request.action}_${now().getTime()}`,
|
|
1616
|
+
expectedEtag: typeof request.metadata?.expectedEtag === "string" ? request.metadata.expectedEtag : void 0,
|
|
1617
|
+
callSessionId: typeof request.metadata?.callSessionId === "string" ? request.metadata.callSessionId : void 0
|
|
1618
|
+
};
|
|
1619
|
+
if (capability.class === "read") {
|
|
1620
|
+
if (!adapter.executeRead) {
|
|
1621
|
+
throw new IntegrationError(`Connector ${connection.connectorId} does not implement reads.`, "action_not_found");
|
|
1622
|
+
}
|
|
1623
|
+
const result = await adapter.executeRead(invocation);
|
|
1624
|
+
return readResultToAction(request, result);
|
|
1625
|
+
}
|
|
1626
|
+
if (capability.class === "mutation") {
|
|
1627
|
+
if (!adapter.executeMutation) {
|
|
1628
|
+
throw new IntegrationError(`Connector ${connection.connectorId} does not implement mutations.`, "action_not_found");
|
|
1629
|
+
}
|
|
1630
|
+
const result = await adapter.executeMutation(invocation);
|
|
1631
|
+
return mutationResultToAction(request, result);
|
|
1632
|
+
}
|
|
1633
|
+
throw new IntegrationError(`Capability ${request.action} is not invokable as an action.`, "action_not_found");
|
|
1634
|
+
}
|
|
1635
|
+
};
|
|
1636
|
+
}
|
|
1637
|
+
function manifestToConnector(providerId, adapter) {
|
|
1638
|
+
const manifest = adapter.manifest;
|
|
1639
|
+
return {
|
|
1640
|
+
id: manifest.kind,
|
|
1641
|
+
providerId,
|
|
1642
|
+
title: manifest.displayName,
|
|
1643
|
+
category: mapCategory(manifest.category),
|
|
1644
|
+
auth: mapAuth(manifest.auth.kind),
|
|
1645
|
+
scopes: manifest.auth.kind === "oauth2" ? manifest.auth.scopes : [],
|
|
1646
|
+
actions: manifest.capabilities.filter((capability) => capability.class === "read" || capability.class === "mutation").map((capability) => ({
|
|
1647
|
+
id: capability.name,
|
|
1648
|
+
title: titleFromName(capability.name),
|
|
1649
|
+
risk: capability.class === "read" ? "read" : capability.externalEffect ? "destructive" : "write",
|
|
1650
|
+
requiredScopes: capability.requiredScopes ?? [],
|
|
1651
|
+
dataClass: inferDataClass(manifest.category),
|
|
1652
|
+
description: capability.description,
|
|
1653
|
+
approvalRequired: capability.class === "mutation",
|
|
1654
|
+
inputSchema: capability.parameters
|
|
1655
|
+
})),
|
|
1656
|
+
metadata: {
|
|
1657
|
+
source: "first-party-adapter",
|
|
1658
|
+
supportTier: "firstPartyExecutable",
|
|
1659
|
+
executable: true
|
|
1660
|
+
}
|
|
1661
|
+
};
|
|
1662
|
+
}
|
|
1663
|
+
function readResultToAction(request, result) {
|
|
1664
|
+
return {
|
|
1665
|
+
ok: true,
|
|
1666
|
+
action: request.action,
|
|
1667
|
+
output: result.data,
|
|
1668
|
+
metadata: {
|
|
1669
|
+
etag: result.etag,
|
|
1670
|
+
fetchedAt: result.fetchedAt
|
|
1671
|
+
}
|
|
1672
|
+
};
|
|
1673
|
+
}
|
|
1674
|
+
function mutationResultToAction(request, result) {
|
|
1675
|
+
if (result.status === "committed") {
|
|
1676
|
+
return {
|
|
1677
|
+
ok: true,
|
|
1678
|
+
action: request.action,
|
|
1679
|
+
output: result.data,
|
|
1680
|
+
metadata: {
|
|
1681
|
+
etagAfter: result.etagAfter,
|
|
1682
|
+
committedAt: result.committedAt,
|
|
1683
|
+
idempotentReplay: result.idempotentReplay
|
|
1684
|
+
}
|
|
1685
|
+
};
|
|
1686
|
+
}
|
|
1687
|
+
if (result.status === "conflict") {
|
|
1688
|
+
return {
|
|
1689
|
+
ok: false,
|
|
1690
|
+
action: request.action,
|
|
1691
|
+
output: {
|
|
1692
|
+
conflict: true,
|
|
1693
|
+
message: result.message,
|
|
1694
|
+
alternatives: result.alternatives,
|
|
1695
|
+
currentState: result.currentState
|
|
1696
|
+
}
|
|
1697
|
+
};
|
|
1698
|
+
}
|
|
1699
|
+
return {
|
|
1700
|
+
ok: false,
|
|
1701
|
+
action: request.action,
|
|
1702
|
+
output: {
|
|
1703
|
+
rateLimited: true,
|
|
1704
|
+
retryAfterMs: result.retryAfterMs,
|
|
1705
|
+
message: result.message
|
|
1706
|
+
}
|
|
1707
|
+
};
|
|
1708
|
+
}
|
|
1709
|
+
function mapAuth(kind) {
|
|
1710
|
+
if (kind === "oauth2") return "oauth2";
|
|
1711
|
+
if (kind === "api-key") return "api_key";
|
|
1712
|
+
if (kind === "none") return "none";
|
|
1713
|
+
return "custom";
|
|
1714
|
+
}
|
|
1715
|
+
function mapCategory(category) {
|
|
1716
|
+
if (category === "comms") return "chat";
|
|
1717
|
+
if (category === "spreadsheet") return "database";
|
|
1718
|
+
if (category === "doc") return "docs";
|
|
1719
|
+
if (category === "commerce") return "workflow";
|
|
1720
|
+
return category === "other" ? "other" : category;
|
|
1721
|
+
}
|
|
1722
|
+
function inferDataClass(category) {
|
|
1723
|
+
if (category === "commerce") return "sensitive";
|
|
1724
|
+
if (category === "webhook") return "internal";
|
|
1725
|
+
return "private";
|
|
1726
|
+
}
|
|
1727
|
+
function titleFromName(name) {
|
|
1728
|
+
return name.split(/[._-]/g).filter(Boolean).map((part) => part.slice(0, 1).toUpperCase() + part.slice(1)).join(" ");
|
|
1729
|
+
}
|
|
1730
|
+
function toRecord(input) {
|
|
1731
|
+
if (input && typeof input === "object" && !Array.isArray(input)) return input;
|
|
1732
|
+
return {};
|
|
1733
|
+
}
|
|
1734
|
+
|
|
1735
|
+
// src/credentials.ts
|
|
1736
|
+
var InMemoryIntegrationSecretStore = class {
|
|
1737
|
+
secrets = /* @__PURE__ */ new Map();
|
|
1738
|
+
get(ref) {
|
|
1739
|
+
return this.secrets.get(secretKey(ref));
|
|
1740
|
+
}
|
|
1741
|
+
put(ref, credentials) {
|
|
1742
|
+
this.secrets.set(secretKey(ref), credentials);
|
|
1743
|
+
}
|
|
1744
|
+
delete(ref) {
|
|
1745
|
+
this.secrets.delete(secretKey(ref));
|
|
1746
|
+
}
|
|
1747
|
+
};
|
|
1748
|
+
function createConnectionCredentialResolver(options) {
|
|
1749
|
+
const now = options.now ?? (() => /* @__PURE__ */ new Date());
|
|
1750
|
+
return async function resolveDataSource(connection) {
|
|
1751
|
+
const credentials = await resolveConnectionCredentials(connection, {
|
|
1752
|
+
secrets: options.secrets,
|
|
1753
|
+
connections: options.connections,
|
|
1754
|
+
adapters: options.adapters,
|
|
1755
|
+
now,
|
|
1756
|
+
markConnectionError: options.markConnectionError
|
|
1757
|
+
});
|
|
1758
|
+
return {
|
|
1759
|
+
id: connection.id,
|
|
1760
|
+
projectId: String(connection.metadata?.projectId ?? connection.owner.id),
|
|
1761
|
+
publishedAgentId: typeof connection.metadata?.publishedAgentId === "string" ? connection.metadata.publishedAgentId : null,
|
|
1762
|
+
kind: connection.connectorId,
|
|
1763
|
+
label: connection.account?.displayName ?? connection.account?.email ?? connection.connectorId,
|
|
1764
|
+
consistencyModel: typeof connection.metadata?.consistencyModel === "string" ? connection.metadata.consistencyModel : "authoritative",
|
|
1765
|
+
scopes: connection.grantedScopes,
|
|
1766
|
+
metadata: connection.metadata ?? {},
|
|
1767
|
+
credentials,
|
|
1768
|
+
status: connection.status === "active" ? "active" : connection.status === "revoked" ? "revoked" : "error"
|
|
1769
|
+
};
|
|
1770
|
+
};
|
|
1771
|
+
}
|
|
1772
|
+
async function resolveConnectionCredentials(input, options) {
|
|
1773
|
+
if (input.status !== "active") throw new Error(`Connection ${input.id} is ${input.status}.`);
|
|
1774
|
+
if (!input.secretRef) return { kind: "none" };
|
|
1775
|
+
const current = await options.secrets.get(input.secretRef);
|
|
1776
|
+
if (!current) throw new Error(`Secret ${input.secretRef.provider}/${input.secretRef.id} not found.`);
|
|
1777
|
+
if (!isExpiredOauth(current, options.now ?? (() => /* @__PURE__ */ new Date()))) return current;
|
|
1778
|
+
const adapter = options.adapters?.find((candidate) => candidate.manifest.kind === input.connectorId);
|
|
1779
|
+
if (!adapter?.refreshToken) return current;
|
|
1780
|
+
try {
|
|
1781
|
+
const refreshed = await adapter.refreshToken(current);
|
|
1782
|
+
await options.secrets.put(input.secretRef, refreshed);
|
|
1783
|
+
if (options.connections) {
|
|
1784
|
+
await options.connections.put({
|
|
1785
|
+
...input,
|
|
1786
|
+
status: "active",
|
|
1787
|
+
updatedAt: (options.now?.() ?? /* @__PURE__ */ new Date()).toISOString(),
|
|
1788
|
+
expiresAt: refreshed.kind === "oauth2" && refreshed.expiresAt ? new Date(refreshed.expiresAt).toISOString() : input.expiresAt
|
|
1789
|
+
});
|
|
1790
|
+
}
|
|
1791
|
+
return refreshed;
|
|
1792
|
+
} catch (error) {
|
|
1793
|
+
const err = error instanceof Error ? error : new Error("Credential refresh failed.");
|
|
1794
|
+
await options.markConnectionError?.(input, err);
|
|
1795
|
+
if (options.connections) {
|
|
1796
|
+
await options.connections.put({
|
|
1797
|
+
...input,
|
|
1798
|
+
status: "expired",
|
|
1799
|
+
updatedAt: (options.now?.() ?? /* @__PURE__ */ new Date()).toISOString()
|
|
1800
|
+
});
|
|
1801
|
+
}
|
|
1802
|
+
throw err;
|
|
1803
|
+
}
|
|
1804
|
+
}
|
|
1805
|
+
function createCredentialBackedAdapterProvider(options) {
|
|
1806
|
+
return createConnectorAdapterProvider({
|
|
1807
|
+
...options,
|
|
1808
|
+
resolveDataSource: createConnectionCredentialResolver(options)
|
|
1809
|
+
});
|
|
1810
|
+
}
|
|
1811
|
+
async function revokeConnection(input) {
|
|
1812
|
+
if (input.connection.secretRef) await input.secrets?.delete?.(input.connection.secretRef);
|
|
1813
|
+
const revoked = {
|
|
1814
|
+
...input.connection,
|
|
1815
|
+
status: "revoked",
|
|
1816
|
+
updatedAt: (input.now?.() ?? /* @__PURE__ */ new Date()).toISOString()
|
|
1817
|
+
};
|
|
1818
|
+
await input.connections?.put(revoked);
|
|
1819
|
+
return revoked;
|
|
1820
|
+
}
|
|
1821
|
+
function isExpiredOauth(credentials, now) {
|
|
1822
|
+
return credentials.kind === "oauth2" && typeof credentials.expiresAt === "number" && credentials.expiresAt <= now().getTime() && Boolean(credentials.refreshToken);
|
|
1823
|
+
}
|
|
1824
|
+
function secretKey(ref) {
|
|
1825
|
+
return `${ref.provider}:${ref.id}`;
|
|
1826
|
+
}
|
|
1827
|
+
|
|
1828
|
+
// src/events.ts
|
|
1829
|
+
var InMemoryIntegrationEventStore = class {
|
|
1830
|
+
events = /* @__PURE__ */ new Map();
|
|
1831
|
+
providerIds = /* @__PURE__ */ new Set();
|
|
1832
|
+
put(event) {
|
|
1833
|
+
this.events.set(event.id, event);
|
|
1834
|
+
if (event.providerEventId) this.providerIds.add(providerKey(event.sourceId, event.providerEventId));
|
|
1835
|
+
}
|
|
1836
|
+
hasProviderEvent(sourceId, providerEventId) {
|
|
1837
|
+
return this.providerIds.has(providerKey(sourceId, providerEventId));
|
|
1838
|
+
}
|
|
1839
|
+
list() {
|
|
1840
|
+
return [...this.events.values()];
|
|
1841
|
+
}
|
|
1842
|
+
};
|
|
1843
|
+
async function receiveIntegrationWebhook(input) {
|
|
1844
|
+
if (!input.adapter.handleInboundEvent) {
|
|
1845
|
+
return { status: 405, body: { ok: false, error: "Connector does not support inbound webhooks." }, received: [], duplicates: [] };
|
|
1846
|
+
}
|
|
1847
|
+
const signature = input.adapter.verifySignature?.({
|
|
1848
|
+
rawBody: input.rawBody,
|
|
1849
|
+
headers: input.headers,
|
|
1850
|
+
source: input.source
|
|
1851
|
+
});
|
|
1852
|
+
if (signature && !signature.valid) {
|
|
1853
|
+
return { status: 401, body: { ok: false, error: signature.reason ?? "Invalid webhook signature." }, received: [], duplicates: [] };
|
|
1854
|
+
}
|
|
1855
|
+
const handled = await input.adapter.handleInboundEvent({
|
|
1856
|
+
source: input.source,
|
|
1857
|
+
rawBody: input.rawBody,
|
|
1858
|
+
headers: input.headers
|
|
1859
|
+
});
|
|
1860
|
+
const received = [];
|
|
1861
|
+
const duplicates = [];
|
|
1862
|
+
for (const inbound of handled.events) {
|
|
1863
|
+
const event = storedEvent(input.source, inbound, input.now ?? (() => /* @__PURE__ */ new Date()));
|
|
1864
|
+
if (event.providerEventId && await input.store.hasProviderEvent(event.sourceId, event.providerEventId)) {
|
|
1865
|
+
duplicates.push(event);
|
|
1866
|
+
continue;
|
|
1867
|
+
}
|
|
1868
|
+
await input.store.put(event);
|
|
1869
|
+
received.push(event);
|
|
1870
|
+
await dispatchStoredEvent(event, input.source, input.workflowRuntime);
|
|
1871
|
+
}
|
|
1872
|
+
return {
|
|
1873
|
+
status: handled.response?.status ?? 200,
|
|
1874
|
+
body: handled.response?.body ?? { received: true, count: received.length, duplicateCount: duplicates.length },
|
|
1875
|
+
headers: handled.response?.headers,
|
|
1876
|
+
received,
|
|
1877
|
+
duplicates
|
|
1878
|
+
};
|
|
1879
|
+
}
|
|
1880
|
+
function storedEventToTriggerEvent(event, source) {
|
|
1881
|
+
return {
|
|
1882
|
+
id: event.id,
|
|
1883
|
+
providerId: String(source.metadata.providerId ?? "first-party"),
|
|
1884
|
+
connectorId: event.connectorId,
|
|
1885
|
+
connectionId: source.id,
|
|
1886
|
+
trigger: event.eventType,
|
|
1887
|
+
occurredAt: event.receivedAt,
|
|
1888
|
+
payload: event.payload,
|
|
1889
|
+
metadata: {
|
|
1890
|
+
providerEventId: event.providerEventId,
|
|
1891
|
+
sourceId: event.sourceId,
|
|
1892
|
+
...event.metadata
|
|
1893
|
+
}
|
|
1894
|
+
};
|
|
1895
|
+
}
|
|
1896
|
+
async function dispatchStoredEvent(event, source, workflowRuntime) {
|
|
1897
|
+
if (!workflowRuntime) return;
|
|
1898
|
+
await workflowRuntime.dispatchEvent(storedEventToTriggerEvent(event, source), () => void 0);
|
|
1899
|
+
}
|
|
1900
|
+
function storedEvent(source, event, now) {
|
|
1901
|
+
return {
|
|
1902
|
+
id: `evt_${source.id}_${event.providerEventId ?? `${event.eventType}_${now().getTime()}`}`,
|
|
1903
|
+
sourceId: source.id,
|
|
1904
|
+
connectorId: source.kind,
|
|
1905
|
+
eventType: event.eventType,
|
|
1906
|
+
providerEventId: event.providerEventId,
|
|
1907
|
+
receivedAt: now().toISOString(),
|
|
1908
|
+
payload: event.payload
|
|
1909
|
+
};
|
|
1910
|
+
}
|
|
1911
|
+
function providerKey(sourceId, providerEventId) {
|
|
1912
|
+
return `${sourceId}:${providerEventId}`;
|
|
1913
|
+
}
|
|
1914
|
+
|
|
1915
|
+
// src/guard.ts
|
|
1916
|
+
import { createHash as createHash2 } from "crypto";
|
|
1917
|
+
var InMemoryIntegrationIdempotencyStore = class {
|
|
1918
|
+
records = /* @__PURE__ */ new Map();
|
|
1919
|
+
get(key) {
|
|
1920
|
+
return this.records.get(key);
|
|
1921
|
+
}
|
|
1922
|
+
put(record) {
|
|
1923
|
+
this.records.set(record.key, record);
|
|
1924
|
+
}
|
|
1925
|
+
};
|
|
1926
|
+
var DefaultIntegrationActionGuard = class {
|
|
1927
|
+
idempotency;
|
|
1928
|
+
audit;
|
|
1929
|
+
rateLimiter;
|
|
1930
|
+
now;
|
|
1931
|
+
constructor(options = {}) {
|
|
1932
|
+
this.idempotency = options.idempotency;
|
|
1933
|
+
this.audit = options.audit;
|
|
1934
|
+
this.rateLimiter = options.rateLimiter;
|
|
1935
|
+
this.now = options.now ?? (() => /* @__PURE__ */ new Date());
|
|
1936
|
+
}
|
|
1937
|
+
async invokeAction(ctx, proceed) {
|
|
1938
|
+
const idempotencyKey = ctx.request.idempotencyKey;
|
|
1939
|
+
const requestHash = hashRequest(ctx);
|
|
1940
|
+
if (idempotencyKey && this.idempotency) {
|
|
1941
|
+
const existing = await this.idempotency.get(idempotencyKey);
|
|
1942
|
+
if (existing) {
|
|
1943
|
+
if (existing.requestHash !== requestHash) {
|
|
1944
|
+
return {
|
|
1945
|
+
ok: false,
|
|
1946
|
+
action: ctx.request.action,
|
|
1947
|
+
output: { idempotencyConflict: true, message: "Idempotency key was reused with different integration input." }
|
|
1948
|
+
};
|
|
1949
|
+
}
|
|
1950
|
+
return {
|
|
1951
|
+
...existing.result,
|
|
1952
|
+
metadata: { ...existing.result.metadata ?? {}, idempotentReplay: true }
|
|
1953
|
+
};
|
|
1954
|
+
}
|
|
1955
|
+
}
|
|
1956
|
+
if (ctx.request.dryRun && ctx.action?.risk !== "read") {
|
|
1957
|
+
const result = {
|
|
1958
|
+
ok: true,
|
|
1959
|
+
action: ctx.request.action,
|
|
1960
|
+
output: { dryRun: true },
|
|
1961
|
+
metadata: { dryRun: true }
|
|
1962
|
+
};
|
|
1963
|
+
await this.writeIdempotency(idempotencyKey, requestHash, result);
|
|
1964
|
+
return result;
|
|
1965
|
+
}
|
|
1966
|
+
const rateLimit = await this.rateLimiter?.check(ctx);
|
|
1967
|
+
if (rateLimit && !rateLimit.allowed) {
|
|
1968
|
+
return {
|
|
1969
|
+
ok: false,
|
|
1970
|
+
action: ctx.request.action,
|
|
1971
|
+
output: { rateLimited: true, retryAfterMs: rateLimit.retryAfterMs, message: rateLimit.reason ?? "Integration rate limit exceeded." }
|
|
1972
|
+
};
|
|
1973
|
+
}
|
|
1974
|
+
try {
|
|
1975
|
+
const result = await proceed();
|
|
1976
|
+
await this.writeIdempotency(idempotencyKey, requestHash, result);
|
|
1977
|
+
await this.audit?.record(createIntegrationAuditEvent({
|
|
1978
|
+
type: result.ok ? "action.invoked" : "action.failed",
|
|
1979
|
+
actor: ctx.connection.owner,
|
|
1980
|
+
connectionId: ctx.connection.id,
|
|
1981
|
+
providerId: ctx.connection.providerId,
|
|
1982
|
+
connectorId: ctx.connection.connectorId,
|
|
1983
|
+
action: ctx.request.action,
|
|
1984
|
+
risk: ctx.action?.risk,
|
|
1985
|
+
dataClass: ctx.action?.dataClass,
|
|
1986
|
+
ok: result.ok,
|
|
1987
|
+
metadata: { idempotencyKey, externalId: result.externalId, warnings: result.warnings },
|
|
1988
|
+
now: this.now
|
|
1989
|
+
}));
|
|
1990
|
+
return result;
|
|
1991
|
+
} catch (error) {
|
|
1992
|
+
await this.audit?.record(createIntegrationAuditEvent({
|
|
1993
|
+
type: "action.failed",
|
|
1994
|
+
actor: ctx.connection.owner,
|
|
1995
|
+
connectionId: ctx.connection.id,
|
|
1996
|
+
providerId: ctx.connection.providerId,
|
|
1997
|
+
connectorId: ctx.connection.connectorId,
|
|
1998
|
+
action: ctx.request.action,
|
|
1999
|
+
risk: ctx.action?.risk,
|
|
2000
|
+
dataClass: ctx.action?.dataClass,
|
|
2001
|
+
ok: false,
|
|
2002
|
+
message: error instanceof Error ? error.message : "Integration action failed.",
|
|
2003
|
+
metadata: { idempotencyKey },
|
|
2004
|
+
now: this.now
|
|
2005
|
+
}));
|
|
2006
|
+
throw error;
|
|
2007
|
+
}
|
|
2008
|
+
}
|
|
2009
|
+
async writeIdempotency(key, requestHash, result) {
|
|
2010
|
+
if (!key || !this.idempotency) return;
|
|
2011
|
+
await this.idempotency.put({
|
|
2012
|
+
key,
|
|
2013
|
+
requestHash,
|
|
2014
|
+
result,
|
|
2015
|
+
createdAt: this.now().toISOString()
|
|
2016
|
+
});
|
|
2017
|
+
}
|
|
2018
|
+
};
|
|
2019
|
+
function createDefaultIntegrationActionGuard(options = {}) {
|
|
2020
|
+
return new DefaultIntegrationActionGuard(options);
|
|
2021
|
+
}
|
|
2022
|
+
function hashRequest(ctx) {
|
|
2023
|
+
return createHash2("sha256").update(JSON.stringify({
|
|
2024
|
+
connectionId: ctx.connection.id,
|
|
2025
|
+
action: ctx.request.action,
|
|
2026
|
+
input: ctx.request.input ?? null,
|
|
2027
|
+
dryRun: ctx.request.dryRun ?? false
|
|
2028
|
+
})).digest("base64url");
|
|
2029
|
+
}
|
|
2030
|
+
|
|
2031
|
+
// src/healthcheck.ts
|
|
2032
|
+
var InMemoryIntegrationHealthcheckStore = class {
|
|
2033
|
+
results = /* @__PURE__ */ new Map();
|
|
2034
|
+
put(result) {
|
|
2035
|
+
this.results.set(result.connectionId, result);
|
|
2036
|
+
}
|
|
2037
|
+
get(connectionId) {
|
|
2038
|
+
return this.results.get(connectionId);
|
|
2039
|
+
}
|
|
2040
|
+
list() {
|
|
2041
|
+
return [...this.results.values()];
|
|
2042
|
+
}
|
|
2043
|
+
};
|
|
2044
|
+
async function runIntegrationHealthcheck(input) {
|
|
2045
|
+
const now = input.now ?? (() => /* @__PURE__ */ new Date());
|
|
2046
|
+
const checkedAt = now().toISOString();
|
|
2047
|
+
const connector = input.connector ?? input.registry?.byId.get(input.connection.connectorId)?.connector;
|
|
2048
|
+
const checks = [];
|
|
2049
|
+
checks.push(connectionStatusCheck(input.connection, now));
|
|
2050
|
+
if (!connector) {
|
|
2051
|
+
checks.push({ id: "connector-known", status: "unknown", message: `Connector ${input.connection.connectorId} is not in the registry.` });
|
|
2052
|
+
} else {
|
|
2053
|
+
checks.push(connectorExecutableCheck(connector));
|
|
2054
|
+
checks.push(scopeShapeCheck(input.connection, connector));
|
|
2055
|
+
if (input.test && input.connection.status === "active") {
|
|
2056
|
+
checks.push(await liveHealthcheck(input.connection, connector, input.test));
|
|
2057
|
+
}
|
|
2058
|
+
}
|
|
2059
|
+
const result = {
|
|
2060
|
+
connectionId: input.connection.id,
|
|
2061
|
+
providerId: input.connection.providerId,
|
|
2062
|
+
connectorId: input.connection.connectorId,
|
|
2063
|
+
status: rollupHealthStatus(checks),
|
|
2064
|
+
checkedAt,
|
|
2065
|
+
checks
|
|
2066
|
+
};
|
|
2067
|
+
await input.audit?.record(createIntegrationAuditEvent({
|
|
2068
|
+
type: "healthcheck.completed",
|
|
2069
|
+
actor: input.connection.owner,
|
|
2070
|
+
connectionId: input.connection.id,
|
|
2071
|
+
providerId: input.connection.providerId,
|
|
2072
|
+
connectorId: input.connection.connectorId,
|
|
2073
|
+
ok: result.status === "healthy",
|
|
2074
|
+
message: result.status,
|
|
2075
|
+
metadata: { checks: checks.map((check) => ({ id: check.id, status: check.status, message: check.message })) },
|
|
2076
|
+
occurredAt: checkedAt
|
|
2077
|
+
}));
|
|
2078
|
+
return result;
|
|
2079
|
+
}
|
|
2080
|
+
async function runIntegrationHealthchecks(input) {
|
|
2081
|
+
const results = [];
|
|
2082
|
+
for (const connection of input.connections) {
|
|
2083
|
+
const result = await runIntegrationHealthcheck({
|
|
2084
|
+
connection,
|
|
2085
|
+
registry: input.registry,
|
|
2086
|
+
test: input.test,
|
|
2087
|
+
audit: input.audit,
|
|
2088
|
+
now: input.now
|
|
2089
|
+
});
|
|
2090
|
+
await input.store?.put(result);
|
|
2091
|
+
results.push(result);
|
|
2092
|
+
}
|
|
2093
|
+
return results;
|
|
2094
|
+
}
|
|
2095
|
+
function healthcheckRequest(action = "healthcheck") {
|
|
2096
|
+
return {
|
|
2097
|
+
connectionId: "__healthcheck__",
|
|
2098
|
+
action,
|
|
2099
|
+
input: {},
|
|
2100
|
+
dryRun: true,
|
|
2101
|
+
metadata: { healthcheck: true }
|
|
2102
|
+
};
|
|
2103
|
+
}
|
|
2104
|
+
function connectionStatusCheck(connection, now) {
|
|
2105
|
+
if (connection.status !== "active") {
|
|
2106
|
+
return { id: "connection-active", status: "unhealthy", message: `Connection is ${connection.status}.` };
|
|
2107
|
+
}
|
|
2108
|
+
if (connection.expiresAt && Date.parse(connection.expiresAt) <= now().getTime()) {
|
|
2109
|
+
return { id: "connection-active", status: "unhealthy", message: "Connection credentials are expired." };
|
|
2110
|
+
}
|
|
2111
|
+
return { id: "connection-active", status: "healthy", message: "Connection is active." };
|
|
2112
|
+
}
|
|
2113
|
+
function connectorExecutableCheck(connector) {
|
|
2114
|
+
const executable = connector.actions.length > 0 || (connector.triggers?.length ?? 0) > 0;
|
|
2115
|
+
if (!executable) {
|
|
2116
|
+
return { id: "connector-executable", status: "degraded", message: `${connector.title} is catalog-only.` };
|
|
2117
|
+
}
|
|
2118
|
+
return { id: "connector-executable", status: "healthy", message: `${connector.title} has executable actions or triggers.` };
|
|
2119
|
+
}
|
|
2120
|
+
function scopeShapeCheck(connection, connector) {
|
|
2121
|
+
const declaredScopes = new Set(connector.scopes);
|
|
2122
|
+
const undeclared = connection.grantedScopes.filter((scope) => !declaredScopes.has(scope));
|
|
2123
|
+
if (connector.scopes.length === 0 && connection.grantedScopes.length > 0) {
|
|
2124
|
+
return { id: "scope-shape", status: "unknown", message: "Connector does not declare a scope catalog.", metadata: { grantedScopes: connection.grantedScopes } };
|
|
2125
|
+
}
|
|
2126
|
+
if (undeclared.length > 0) {
|
|
2127
|
+
return { id: "scope-shape", status: "degraded", message: "Connection has scopes not declared by the connector.", metadata: { undeclared } };
|
|
2128
|
+
}
|
|
2129
|
+
return { id: "scope-shape", status: "healthy", message: "Granted scopes match the connector shape." };
|
|
2130
|
+
}
|
|
2131
|
+
async function liveHealthcheck(connection, connector, test) {
|
|
2132
|
+
try {
|
|
2133
|
+
const result = await test(connection, connector);
|
|
2134
|
+
const ok = typeof result === "boolean" ? result : result.ok;
|
|
2135
|
+
return {
|
|
2136
|
+
id: "provider-live-test",
|
|
2137
|
+
status: ok ? "healthy" : "unhealthy",
|
|
2138
|
+
message: ok ? "Provider live test passed." : "Provider live test failed.",
|
|
2139
|
+
metadata: typeof result === "boolean" ? void 0 : { action: result.action, warnings: result.warnings }
|
|
2140
|
+
};
|
|
2141
|
+
} catch (error) {
|
|
2142
|
+
return {
|
|
2143
|
+
id: "provider-live-test",
|
|
2144
|
+
status: "unhealthy",
|
|
2145
|
+
message: error instanceof Error ? error.message : "Provider live test failed."
|
|
2146
|
+
};
|
|
2147
|
+
}
|
|
2148
|
+
}
|
|
2149
|
+
function rollupHealthStatus(checks) {
|
|
2150
|
+
if (checks.some((check) => check.status === "unhealthy")) return "unhealthy";
|
|
2151
|
+
if (checks.some((check) => check.status === "degraded")) return "degraded";
|
|
2152
|
+
if (checks.some((check) => check.status === "unknown")) return "unknown";
|
|
2153
|
+
return "healthy";
|
|
2154
|
+
}
|
|
2155
|
+
|
|
1293
2156
|
// src/connectors/types.ts
|
|
1294
2157
|
var ResourceContention = class extends Error {
|
|
1295
2158
|
constructor(message, alternatives = [], currentState) {
|
|
@@ -1344,7 +2207,7 @@ function assertValidConnectorManifest(manifest) {
|
|
|
1344
2207
|
}
|
|
1345
2208
|
|
|
1346
2209
|
// src/connectors/oauth.ts
|
|
1347
|
-
import { createHash, randomBytes } from "crypto";
|
|
2210
|
+
import { createHash as createHash3, randomBytes } from "crypto";
|
|
1348
2211
|
var PENDING_TTL_MS = 10 * 60 * 1e3;
|
|
1349
2212
|
var InMemoryOAuthFlowStore = class {
|
|
1350
2213
|
pendingFlows = /* @__PURE__ */ new Map();
|
|
@@ -1372,7 +2235,7 @@ function startOAuthFlow(input) {
|
|
|
1372
2235
|
const now = input.now ?? Date.now();
|
|
1373
2236
|
store.sweep?.(now);
|
|
1374
2237
|
const codeVerifier = base64Url(randomBytes(48));
|
|
1375
|
-
const codeChallenge = base64Url(
|
|
2238
|
+
const codeChallenge = base64Url(createHash3("sha256").update(codeVerifier).digest());
|
|
1376
2239
|
const state = base64Url(randomBytes(24));
|
|
1377
2240
|
store.put(state, {
|
|
1378
2241
|
codeVerifier,
|
|
@@ -1837,7 +2700,7 @@ function readMetaString(meta, key) {
|
|
|
1837
2700
|
}
|
|
1838
2701
|
|
|
1839
2702
|
// src/connectors/adapters/google-sheets.ts
|
|
1840
|
-
import { createHash as
|
|
2703
|
+
import { createHash as createHash4 } from "crypto";
|
|
1841
2704
|
var SCOPES2 = ["https://www.googleapis.com/auth/spreadsheets"];
|
|
1842
2705
|
var AUTH_URL2 = "https://accounts.google.com/o/oauth2/v2/auth";
|
|
1843
2706
|
var TOKEN_URL2 = "https://oauth2.googleapis.com/token";
|
|
@@ -2111,7 +2974,7 @@ async function fetchAllRows(accessToken, meta) {
|
|
|
2111
2974
|
function rowFingerprint(values) {
|
|
2112
2975
|
const keys = Object.keys(values).sort();
|
|
2113
2976
|
const blob = keys.map((k) => `${k}=${values[k]}`).join("");
|
|
2114
|
-
return
|
|
2977
|
+
return createHash4("sha256").update(blob).digest("hex").slice(0, 16);
|
|
2115
2978
|
}
|
|
2116
2979
|
function matchesPredicate(row, predicate) {
|
|
2117
2980
|
for (const [k, v] of Object.entries(predicate)) {
|
|
@@ -4597,7 +5460,7 @@ function unique2(values) {
|
|
|
4597
5460
|
}
|
|
4598
5461
|
|
|
4599
5462
|
// src/policy.ts
|
|
4600
|
-
import { randomUUID } from "crypto";
|
|
5463
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
4601
5464
|
var StaticIntegrationPolicyEngine = class {
|
|
4602
5465
|
rules;
|
|
4603
5466
|
defaultReadEffect;
|
|
@@ -4640,7 +5503,7 @@ function buildApprovalRequest(ctx, reason, requestedAt) {
|
|
|
4640
5503
|
throw new Error("Cannot build approval request without an action descriptor.");
|
|
4641
5504
|
}
|
|
4642
5505
|
return {
|
|
4643
|
-
id: `approval_${
|
|
5506
|
+
id: `approval_${randomUUID2()}`,
|
|
4644
5507
|
connectionId: ctx.connection.id,
|
|
4645
5508
|
providerId: ctx.connection.providerId,
|
|
4646
5509
|
connectorId: ctx.connection.connectorId,
|
|
@@ -4656,7 +5519,7 @@ function buildApprovalRequest(ctx, reason, requestedAt) {
|
|
|
4656
5519
|
function redactApprovalRequest(request) {
|
|
4657
5520
|
return {
|
|
4658
5521
|
...request,
|
|
4659
|
-
inputPreview:
|
|
5522
|
+
inputPreview: redactUnknown2(request.inputPreview)
|
|
4660
5523
|
};
|
|
4661
5524
|
}
|
|
4662
5525
|
function ruleMatches(rule, ctx) {
|
|
@@ -4680,17 +5543,17 @@ function defaultReason(effect, risk) {
|
|
|
4680
5543
|
return `${risk} integration action requires approval by default policy.`;
|
|
4681
5544
|
}
|
|
4682
5545
|
function previewInput(input) {
|
|
4683
|
-
return
|
|
5546
|
+
return redactUnknown2(input);
|
|
4684
5547
|
}
|
|
4685
|
-
function
|
|
4686
|
-
if (Array.isArray(value)) return value.map(
|
|
5548
|
+
function redactUnknown2(value) {
|
|
5549
|
+
if (Array.isArray(value)) return value.map(redactUnknown2);
|
|
4687
5550
|
if (!value || typeof value !== "object") return value;
|
|
4688
5551
|
const out = {};
|
|
4689
5552
|
for (const [key, child] of Object.entries(value)) {
|
|
4690
5553
|
if (/token|secret|password|authorization|api[_-]?key|credential/i.test(key)) {
|
|
4691
5554
|
out[key] = "[REDACTED]";
|
|
4692
5555
|
} else {
|
|
4693
|
-
out[key] =
|
|
5556
|
+
out[key] = redactUnknown2(child);
|
|
4694
5557
|
}
|
|
4695
5558
|
}
|
|
4696
5559
|
return out;
|
|
@@ -4754,13 +5617,13 @@ function redactInvocationEnvelope(envelope) {
|
|
|
4754
5617
|
return {
|
|
4755
5618
|
...envelope,
|
|
4756
5619
|
capabilityToken: "[REDACTED]",
|
|
4757
|
-
input:
|
|
5620
|
+
input: redactUnknown3(envelope.input)
|
|
4758
5621
|
};
|
|
4759
5622
|
}
|
|
4760
5623
|
function redactCapability(capability) {
|
|
4761
5624
|
return {
|
|
4762
5625
|
...capability,
|
|
4763
|
-
metadata:
|
|
5626
|
+
metadata: redactUnknown3(capability.metadata)
|
|
4764
5627
|
};
|
|
4765
5628
|
}
|
|
4766
5629
|
function normalizeIntegrationResult(result) {
|
|
@@ -4788,15 +5651,40 @@ function normalizeIntegrationResult(result) {
|
|
|
4788
5651
|
metadata: result.metadata
|
|
4789
5652
|
};
|
|
4790
5653
|
}
|
|
4791
|
-
function
|
|
4792
|
-
|
|
5654
|
+
async function dispatchIntegrationInvocation(envelope, options) {
|
|
5655
|
+
try {
|
|
5656
|
+
validateIntegrationInvocationEnvelope(envelope, options);
|
|
5657
|
+
const result = await options.hub.invokeWithCapability(
|
|
5658
|
+
envelope.capabilityToken,
|
|
5659
|
+
invocationRequestFromEnvelope(envelope)
|
|
5660
|
+
);
|
|
5661
|
+
return normalizeIntegrationResult(result);
|
|
5662
|
+
} catch (error) {
|
|
5663
|
+
return {
|
|
5664
|
+
status: "failed",
|
|
5665
|
+
action: typeof envelope?.action === "string" ? envelope.action : "unknown",
|
|
5666
|
+
error: error instanceof Error ? error.message : "Integration invocation failed."
|
|
5667
|
+
};
|
|
5668
|
+
}
|
|
5669
|
+
}
|
|
5670
|
+
var IntegrationSandboxHost = class {
|
|
5671
|
+
options;
|
|
5672
|
+
constructor(options) {
|
|
5673
|
+
this.options = options;
|
|
5674
|
+
}
|
|
5675
|
+
dispatch(envelope) {
|
|
5676
|
+
return dispatchIntegrationInvocation(envelope, this.options);
|
|
5677
|
+
}
|
|
5678
|
+
};
|
|
5679
|
+
function redactUnknown3(value) {
|
|
5680
|
+
if (Array.isArray(value)) return value.map(redactUnknown3);
|
|
4793
5681
|
if (!value || typeof value !== "object") return value;
|
|
4794
5682
|
const out = {};
|
|
4795
5683
|
for (const [key, child] of Object.entries(value)) {
|
|
4796
5684
|
if (/token|secret|password|authorization|api[_-]?key|credential/i.test(key)) {
|
|
4797
5685
|
out[key] = "[REDACTED]";
|
|
4798
5686
|
} else {
|
|
4799
|
-
out[key] =
|
|
5687
|
+
out[key] = redactUnknown3(child);
|
|
4800
5688
|
}
|
|
4801
5689
|
}
|
|
4802
5690
|
return out;
|
|
@@ -4808,152 +5696,6 @@ function isPlainRecord(value) {
|
|
|
4808
5696
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
4809
5697
|
}
|
|
4810
5698
|
|
|
4811
|
-
// src/adapter-provider.ts
|
|
4812
|
-
function createConnectorAdapterProvider(options) {
|
|
4813
|
-
const providerId = options.id ?? "first-party";
|
|
4814
|
-
const now = options.now ?? (() => /* @__PURE__ */ new Date());
|
|
4815
|
-
const adapters = /* @__PURE__ */ new Map();
|
|
4816
|
-
for (const adapter of options.adapters) {
|
|
4817
|
-
adapters.set(adapter.manifest.kind, adapter);
|
|
4818
|
-
}
|
|
4819
|
-
return {
|
|
4820
|
-
id: providerId,
|
|
4821
|
-
kind: options.kind ?? "first_party",
|
|
4822
|
-
listConnectors: () => [...adapters.values()].map((adapter) => manifestToConnector(providerId, adapter)),
|
|
4823
|
-
async invokeAction(connection, request) {
|
|
4824
|
-
const adapter = adapters.get(connection.connectorId);
|
|
4825
|
-
if (!adapter) {
|
|
4826
|
-
throw new IntegrationError(`Connector adapter ${connection.connectorId} not found.`, "connector_not_found");
|
|
4827
|
-
}
|
|
4828
|
-
const capability = adapter.manifest.capabilities.find((candidate) => candidate.name === request.action);
|
|
4829
|
-
if (!capability) {
|
|
4830
|
-
throw new IntegrationError(`Capability ${request.action} is not defined by ${connection.connectorId}.`, "action_not_found");
|
|
4831
|
-
}
|
|
4832
|
-
const source = await options.resolveDataSource(connection);
|
|
4833
|
-
const invocation = {
|
|
4834
|
-
source,
|
|
4835
|
-
capabilityName: request.action,
|
|
4836
|
-
args: toRecord(request.input),
|
|
4837
|
-
idempotencyKey: request.idempotencyKey ?? `idem_${connection.id}_${request.action}_${now().getTime()}`,
|
|
4838
|
-
expectedEtag: typeof request.metadata?.expectedEtag === "string" ? request.metadata.expectedEtag : void 0,
|
|
4839
|
-
callSessionId: typeof request.metadata?.callSessionId === "string" ? request.metadata.callSessionId : void 0
|
|
4840
|
-
};
|
|
4841
|
-
if (capability.class === "read") {
|
|
4842
|
-
if (!adapter.executeRead) {
|
|
4843
|
-
throw new IntegrationError(`Connector ${connection.connectorId} does not implement reads.`, "action_not_found");
|
|
4844
|
-
}
|
|
4845
|
-
const result = await adapter.executeRead(invocation);
|
|
4846
|
-
return readResultToAction(request, result);
|
|
4847
|
-
}
|
|
4848
|
-
if (capability.class === "mutation") {
|
|
4849
|
-
if (!adapter.executeMutation) {
|
|
4850
|
-
throw new IntegrationError(`Connector ${connection.connectorId} does not implement mutations.`, "action_not_found");
|
|
4851
|
-
}
|
|
4852
|
-
const result = await adapter.executeMutation(invocation);
|
|
4853
|
-
return mutationResultToAction(request, result);
|
|
4854
|
-
}
|
|
4855
|
-
throw new IntegrationError(`Capability ${request.action} is not invokable as an action.`, "action_not_found");
|
|
4856
|
-
}
|
|
4857
|
-
};
|
|
4858
|
-
}
|
|
4859
|
-
function manifestToConnector(providerId, adapter) {
|
|
4860
|
-
const manifest = adapter.manifest;
|
|
4861
|
-
return {
|
|
4862
|
-
id: manifest.kind,
|
|
4863
|
-
providerId,
|
|
4864
|
-
title: manifest.displayName,
|
|
4865
|
-
category: mapCategory(manifest.category),
|
|
4866
|
-
auth: mapAuth(manifest.auth.kind),
|
|
4867
|
-
scopes: manifest.auth.kind === "oauth2" ? manifest.auth.scopes : [],
|
|
4868
|
-
actions: manifest.capabilities.filter((capability) => capability.class === "read" || capability.class === "mutation").map((capability) => ({
|
|
4869
|
-
id: capability.name,
|
|
4870
|
-
title: titleFromName(capability.name),
|
|
4871
|
-
risk: capability.class === "read" ? "read" : capability.externalEffect ? "destructive" : "write",
|
|
4872
|
-
requiredScopes: capability.requiredScopes ?? [],
|
|
4873
|
-
dataClass: inferDataClass(manifest.category),
|
|
4874
|
-
description: capability.description,
|
|
4875
|
-
approvalRequired: capability.class === "mutation",
|
|
4876
|
-
inputSchema: capability.parameters
|
|
4877
|
-
})),
|
|
4878
|
-
metadata: {
|
|
4879
|
-
source: "first-party-adapter",
|
|
4880
|
-
supportTier: "firstPartyExecutable",
|
|
4881
|
-
executable: true
|
|
4882
|
-
}
|
|
4883
|
-
};
|
|
4884
|
-
}
|
|
4885
|
-
function readResultToAction(request, result) {
|
|
4886
|
-
return {
|
|
4887
|
-
ok: true,
|
|
4888
|
-
action: request.action,
|
|
4889
|
-
output: result.data,
|
|
4890
|
-
metadata: {
|
|
4891
|
-
etag: result.etag,
|
|
4892
|
-
fetchedAt: result.fetchedAt
|
|
4893
|
-
}
|
|
4894
|
-
};
|
|
4895
|
-
}
|
|
4896
|
-
function mutationResultToAction(request, result) {
|
|
4897
|
-
if (result.status === "committed") {
|
|
4898
|
-
return {
|
|
4899
|
-
ok: true,
|
|
4900
|
-
action: request.action,
|
|
4901
|
-
output: result.data,
|
|
4902
|
-
metadata: {
|
|
4903
|
-
etagAfter: result.etagAfter,
|
|
4904
|
-
committedAt: result.committedAt,
|
|
4905
|
-
idempotentReplay: result.idempotentReplay
|
|
4906
|
-
}
|
|
4907
|
-
};
|
|
4908
|
-
}
|
|
4909
|
-
if (result.status === "conflict") {
|
|
4910
|
-
return {
|
|
4911
|
-
ok: false,
|
|
4912
|
-
action: request.action,
|
|
4913
|
-
output: {
|
|
4914
|
-
conflict: true,
|
|
4915
|
-
message: result.message,
|
|
4916
|
-
alternatives: result.alternatives,
|
|
4917
|
-
currentState: result.currentState
|
|
4918
|
-
}
|
|
4919
|
-
};
|
|
4920
|
-
}
|
|
4921
|
-
return {
|
|
4922
|
-
ok: false,
|
|
4923
|
-
action: request.action,
|
|
4924
|
-
output: {
|
|
4925
|
-
rateLimited: true,
|
|
4926
|
-
retryAfterMs: result.retryAfterMs,
|
|
4927
|
-
message: result.message
|
|
4928
|
-
}
|
|
4929
|
-
};
|
|
4930
|
-
}
|
|
4931
|
-
function mapAuth(kind) {
|
|
4932
|
-
if (kind === "oauth2") return "oauth2";
|
|
4933
|
-
if (kind === "api-key") return "api_key";
|
|
4934
|
-
if (kind === "none") return "none";
|
|
4935
|
-
return "custom";
|
|
4936
|
-
}
|
|
4937
|
-
function mapCategory(category) {
|
|
4938
|
-
if (category === "comms") return "chat";
|
|
4939
|
-
if (category === "spreadsheet") return "database";
|
|
4940
|
-
if (category === "doc") return "docs";
|
|
4941
|
-
if (category === "commerce") return "workflow";
|
|
4942
|
-
return category === "other" ? "other" : category;
|
|
4943
|
-
}
|
|
4944
|
-
function inferDataClass(category) {
|
|
4945
|
-
if (category === "commerce") return "sensitive";
|
|
4946
|
-
if (category === "webhook") return "internal";
|
|
4947
|
-
return "private";
|
|
4948
|
-
}
|
|
4949
|
-
function titleFromName(name) {
|
|
4950
|
-
return name.split(/[._-]/g).filter(Boolean).map((part) => part.slice(0, 1).toUpperCase() + part.slice(1)).join(" ");
|
|
4951
|
-
}
|
|
4952
|
-
function toRecord(input) {
|
|
4953
|
-
if (input && typeof input === "object" && !Array.isArray(input)) return input;
|
|
4954
|
-
return {};
|
|
4955
|
-
}
|
|
4956
|
-
|
|
4957
5699
|
// src/importers.ts
|
|
4958
5700
|
var HTTP_METHODS = /* @__PURE__ */ new Set(["get", "post", "put", "patch", "delete"]);
|
|
4959
5701
|
function importOpenApiConnector(document, options) {
|
|
@@ -5740,7 +6482,7 @@ var IntegrationHub = class {
|
|
|
5740
6482
|
assertScopes(connection, request.scopes);
|
|
5741
6483
|
const now = this.now();
|
|
5742
6484
|
const capability = {
|
|
5743
|
-
id: `cap_${
|
|
6485
|
+
id: `cap_${randomUUID3()}`,
|
|
5744
6486
|
subject: request.subject,
|
|
5745
6487
|
connectionId: request.connectionId,
|
|
5746
6488
|
scopes: unique6(request.scopes),
|
|
@@ -5995,16 +6737,26 @@ function unique6(values) {
|
|
|
5995
6737
|
}
|
|
5996
6738
|
export {
|
|
5997
6739
|
ACTIVEPIECES_OVERRIDES,
|
|
6740
|
+
ApprovalBackedPolicyEngine,
|
|
5998
6741
|
CredentialsExpired,
|
|
6742
|
+
DEFAULT_INTEGRATION_BRIDGE_ENV,
|
|
5999
6743
|
DEFAULT_SIGNATURE_TOLERANCE_SECONDS,
|
|
6744
|
+
DefaultIntegrationActionGuard,
|
|
6000
6745
|
INTEGRATION_FAMILIES,
|
|
6001
6746
|
InMemoryConnectionStore,
|
|
6747
|
+
InMemoryIntegrationApprovalStore,
|
|
6748
|
+
InMemoryIntegrationAuditStore,
|
|
6749
|
+
InMemoryIntegrationEventStore,
|
|
6002
6750
|
InMemoryIntegrationGrantStore,
|
|
6751
|
+
InMemoryIntegrationHealthcheckStore,
|
|
6752
|
+
InMemoryIntegrationIdempotencyStore,
|
|
6753
|
+
InMemoryIntegrationSecretStore,
|
|
6003
6754
|
InMemoryIntegrationWorkflowStore,
|
|
6004
6755
|
InMemoryOAuthFlowStore,
|
|
6005
6756
|
IntegrationError,
|
|
6006
6757
|
IntegrationHub,
|
|
6007
6758
|
IntegrationRuntime,
|
|
6759
|
+
IntegrationSandboxHost,
|
|
6008
6760
|
IntegrationWorkflowRuntime,
|
|
6009
6761
|
ResourceContention,
|
|
6010
6762
|
StaticIntegrationPolicyEngine,
|
|
@@ -6017,6 +6769,8 @@ export {
|
|
|
6017
6769
|
buildApprovalRequest,
|
|
6018
6770
|
buildDefaultIntegrationRegistry,
|
|
6019
6771
|
buildHealthcheckPlan,
|
|
6772
|
+
buildIntegrationBridgeEnvironment,
|
|
6773
|
+
buildIntegrationBridgePayload,
|
|
6020
6774
|
buildIntegrationCoverageConnectors,
|
|
6021
6775
|
buildIntegrationInvocationEnvelope,
|
|
6022
6776
|
buildIntegrationToolCatalog,
|
|
@@ -6024,14 +6778,23 @@ export {
|
|
|
6024
6778
|
composeIntegrationRegistry,
|
|
6025
6779
|
consoleStepsToText,
|
|
6026
6780
|
consumePendingFlow,
|
|
6781
|
+
createApprovalBackedPolicyEngine,
|
|
6782
|
+
createAuditingActionGuard,
|
|
6783
|
+
createConnectionCredentialResolver,
|
|
6027
6784
|
createConnectorAdapterProvider,
|
|
6785
|
+
createCredentialBackedAdapterProvider,
|
|
6786
|
+
createDefaultIntegrationActionGuard,
|
|
6028
6787
|
createDefaultIntegrationPolicyEngine,
|
|
6029
6788
|
createGatewayCatalogProvider,
|
|
6030
6789
|
createHttpIntegrationProvider,
|
|
6790
|
+
createIntegrationAuditEvent,
|
|
6031
6791
|
createIntegrationRuntime,
|
|
6032
6792
|
createIntegrationWorkflowRuntime,
|
|
6033
6793
|
createMockIntegrationProvider,
|
|
6034
6794
|
declarativeRestConnector,
|
|
6795
|
+
decodeIntegrationBridgePayload,
|
|
6796
|
+
dispatchIntegrationInvocation,
|
|
6797
|
+
encodeIntegrationBridgePayload,
|
|
6035
6798
|
exchangeAuthorizationCode,
|
|
6036
6799
|
firstHeader,
|
|
6037
6800
|
getActivepiecesOverride,
|
|
@@ -6041,6 +6804,7 @@ export {
|
|
|
6041
6804
|
gitlabConnector,
|
|
6042
6805
|
googleCalendar,
|
|
6043
6806
|
googleSheets,
|
|
6807
|
+
healthcheckRequest,
|
|
6044
6808
|
hubspot,
|
|
6045
6809
|
importGraphqlConnector,
|
|
6046
6810
|
importMcpConnector,
|
|
@@ -6059,16 +6823,25 @@ export {
|
|
|
6059
6823
|
normalizeGatewayCatalog,
|
|
6060
6824
|
normalizeIntegrationResult,
|
|
6061
6825
|
notionDatabase,
|
|
6826
|
+
parseIntegrationBridgeEnvironment,
|
|
6062
6827
|
parseIntegrationToolName,
|
|
6063
6828
|
parseStripeSignatureHeader,
|
|
6829
|
+
receiveIntegrationWebhook,
|
|
6064
6830
|
redactApprovalRequest,
|
|
6065
6831
|
redactCapability,
|
|
6832
|
+
redactIntegrationBridgePayload,
|
|
6066
6833
|
redactInvocationEnvelope,
|
|
6067
6834
|
refreshAccessToken,
|
|
6068
6835
|
renderAgentToolDescription,
|
|
6069
6836
|
renderConsoleSteps,
|
|
6070
6837
|
renderRunbookMarkdown,
|
|
6838
|
+
resolveConnectionCredentials,
|
|
6839
|
+
resolveIntegrationApproval,
|
|
6840
|
+
revokeConnection,
|
|
6841
|
+
runIntegrationHealthcheck,
|
|
6842
|
+
runIntegrationHealthchecks,
|
|
6071
6843
|
salesforceConnector,
|
|
6844
|
+
sanitizeAuditConnection,
|
|
6072
6845
|
sanitizeConnection,
|
|
6073
6846
|
searchIntegrationTools,
|
|
6074
6847
|
signCapability,
|
|
@@ -6076,6 +6849,7 @@ export {
|
|
|
6076
6849
|
slackEventsConnector,
|
|
6077
6850
|
specAuthToConnectorAuth,
|
|
6078
6851
|
startOAuthFlow,
|
|
6852
|
+
storedEventToTriggerEvent,
|
|
6079
6853
|
stripePackConnector,
|
|
6080
6854
|
stripeWebhookReceiverConnector,
|
|
6081
6855
|
summarizeIntegrationRegistry,
|