@tangle-network/agent-integrations 0.26.0 → 0.28.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.
Files changed (46) hide show
  1. package/dist/bin/tangle-catalog-runtime.js +6 -2
  2. package/dist/bin/tangle-catalog-runtime.js.map +1 -1
  3. package/dist/catalog.d.ts +5 -1
  4. package/dist/catalog.js +6 -2
  5. package/dist/chunk-ATYHZXLL.js +457 -0
  6. package/dist/chunk-ATYHZXLL.js.map +1 -0
  7. package/dist/chunk-H4XYLS7T.js +75 -0
  8. package/dist/chunk-H4XYLS7T.js.map +1 -0
  9. package/dist/{chunk-GA4VTE3U.js → chunk-JU25UDN2.js} +5 -58
  10. package/dist/chunk-JU25UDN2.js.map +1 -0
  11. package/dist/chunk-P24T3MLM.js +106 -0
  12. package/dist/chunk-P24T3MLM.js.map +1 -0
  13. package/dist/chunk-SVQ4PHDZ.js +129 -0
  14. package/dist/chunk-SVQ4PHDZ.js.map +1 -0
  15. package/dist/{chunk-ALCIWTIR.js → chunk-UWRYFPJW.js} +41 -83
  16. package/dist/chunk-UWRYFPJW.js.map +1 -0
  17. package/dist/connect/index.d.ts +112 -0
  18. package/dist/connect/index.js +14 -0
  19. package/dist/connect/index.js.map +1 -0
  20. package/dist/connectors/adapters/index.d.ts +593 -1
  21. package/dist/connectors/adapters/index.js +15 -1
  22. package/dist/connectors/index.d.ts +2 -1
  23. package/dist/connectors/index.js +19 -5
  24. package/dist/errors-Bg3_rxnQ.d.ts +32 -0
  25. package/dist/index.d.ts +6 -2
  26. package/dist/index.js +47 -9
  27. package/dist/middleware/index.d.ts +137 -0
  28. package/dist/middleware/index.js +14 -0
  29. package/dist/middleware/index.js.map +1 -0
  30. package/dist/registry.d.ts +29 -33
  31. package/dist/registry.js +6 -2
  32. package/dist/router-BncoovUh.d.ts +149 -0
  33. package/dist/runtime.d.ts +5 -1
  34. package/dist/runtime.js +6 -2
  35. package/dist/specs.d.ts +5 -1
  36. package/dist/stripe/index.d.ts +812 -0
  37. package/dist/stripe/index.js +866 -0
  38. package/dist/stripe/index.js.map +1 -0
  39. package/dist/tangle-catalog-runtime.d.ts +5 -1
  40. package/dist/tangle-catalog-runtime.js +6 -2
  41. package/dist/tangle-id-CTU4kGId.d.ts +553 -0
  42. package/dist/webhooks/index.d.ts +3 -148
  43. package/package.json +16 -1
  44. package/dist/chunk-ALCIWTIR.js.map +0 -1
  45. package/dist/chunk-GA4VTE3U.js.map +0 -1
  46. package/dist/index-D4D4CEKX.d.ts +0 -976
@@ -0,0 +1,106 @@
1
+ import {
2
+ DEFAULT_TANGLE_PLATFORM_URL,
3
+ TangleIdentityUnreachableError,
4
+ createTangleIdentityClient
5
+ } from "./chunk-ATYHZXLL.js";
6
+
7
+ // src/connect/index.ts
8
+ function startConnectFlow(opts, input) {
9
+ if (!input.appId) {
10
+ throw new TangleIdentityUnreachableError("connect/start: appId is required");
11
+ }
12
+ if (!input.state) {
13
+ throw new TangleIdentityUnreachableError(
14
+ "connect/start: state is required for CSRF protection (matches the platform contract)"
15
+ );
16
+ }
17
+ const baseUrl = (opts.baseUrl ?? DEFAULT_TANGLE_PLATFORM_URL).replace(/\/+$/, "");
18
+ const url = new URL(`${baseUrl}/cross-site/authorize`);
19
+ url.searchParams.set("app", input.appId);
20
+ url.searchParams.set("state", input.state);
21
+ if (input.redirectUri) url.searchParams.set("redirect", input.redirectUri);
22
+ return { authorizeUrl: url.toString() };
23
+ }
24
+ async function finishConnectFlow(opts, input) {
25
+ if (!input.code) {
26
+ throw new TangleIdentityUnreachableError("connect/finish: code is required");
27
+ }
28
+ if (!input.appId) {
29
+ throw new TangleIdentityUnreachableError("connect/finish: appId is required");
30
+ }
31
+ const baseUrl = (opts.baseUrl ?? DEFAULT_TANGLE_PLATFORM_URL).replace(/\/+$/, "");
32
+ const fetchImpl = opts.fetchImpl ?? fetch;
33
+ const timeoutMs = opts.timeoutMs ?? 5e3;
34
+ let res;
35
+ try {
36
+ res = await fetchImpl(`${baseUrl}/cross-site/exchange`, {
37
+ method: "POST",
38
+ headers: { "content-type": "application/json" },
39
+ body: JSON.stringify({ code: input.code, app: input.appId }),
40
+ signal: AbortSignal.timeout(timeoutMs)
41
+ });
42
+ } catch (err) {
43
+ throw new TangleIdentityUnreachableError("connect/finish: exchange request failed", { cause: err });
44
+ }
45
+ if (res.status === 401) {
46
+ throw new TangleIdentityUnreachableError(
47
+ "connect/finish: exchange code rejected \u2014 replay, expired, or wrong app",
48
+ { status: 401 }
49
+ );
50
+ }
51
+ if (!res.ok) {
52
+ const detail = await res.text().catch(() => "");
53
+ throw new TangleIdentityUnreachableError(
54
+ `connect/finish: /cross-site/exchange returned ${res.status}: ${detail.slice(0, 200)}`,
55
+ { status: res.status }
56
+ );
57
+ }
58
+ const body = await res.json().catch(() => null);
59
+ if (!body || typeof body.apiKey !== "string" || !body.user || typeof body.user.id !== "string") {
60
+ throw new TangleIdentityUnreachableError("connect/finish: exchange response had an invalid shape");
61
+ }
62
+ return {
63
+ apiKey: body.apiKey,
64
+ user: {
65
+ id: body.user.id,
66
+ ...typeof body.user.email === "string" ? { email: body.user.email } : {},
67
+ ...body.user.name !== void 0 ? { name: body.user.name } : {},
68
+ ...body.user.image !== void 0 ? { image: body.user.image } : {}
69
+ },
70
+ balance: typeof body.balance === "number" && Number.isFinite(body.balance) ? body.balance : 0
71
+ };
72
+ }
73
+ async function revokeConnectFlow(opts, input) {
74
+ if (!input.apiKey) {
75
+ throw new TangleIdentityUnreachableError("connect/revoke: apiKey is required");
76
+ }
77
+ const client = createTangleIdentityClient(opts);
78
+ await client.revokeSession(input.apiKey);
79
+ }
80
+ var InMemoryConnectStateStore = class {
81
+ entries = /* @__PURE__ */ new Map();
82
+ put(state, value) {
83
+ this.entries.set(state, {
84
+ appId: value.appId,
85
+ expiresAt: Date.now() + (value.ttlMs ?? 10 * 6e4)
86
+ });
87
+ }
88
+ consume(state) {
89
+ const entry = this.entries.get(state);
90
+ this.entries.delete(state);
91
+ if (!entry || entry.expiresAt <= Date.now()) return void 0;
92
+ return { appId: entry.appId };
93
+ }
94
+ /** Test-only — drop pending state between unit-test runs. */
95
+ clear() {
96
+ this.entries.clear();
97
+ }
98
+ };
99
+
100
+ export {
101
+ startConnectFlow,
102
+ finishConnectFlow,
103
+ revokeConnectFlow,
104
+ InMemoryConnectStateStore
105
+ };
106
+ //# sourceMappingURL=chunk-P24T3MLM.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/connect/index.ts"],"sourcesContent":["/**\n * @stable Cross-product connect flow.\n *\n * A product app (legal, tax, gtm, creative, agent-builder, sandbox, …) that\n * is already part of the Tangle trusted-app registry on id.tangle.tools\n * routes its users through this flow to obtain an `sk-tan-*` API key bound\n * to the calling user. The shape mirrors the platform's `/cross-site/*`\n * routes one-for-one so consumers can swap a bespoke fetch loop for these\n * helpers without changing the wire protocol.\n *\n * Three stages:\n *\n * 1. start({ appId, returnUrl, state }) → { authorizeUrl }\n * The product redirects the user to `authorizeUrl`. id.tangle.tools\n * checks the session cookie; if absent it punts to the login page\n * with a callback back to /cross-site/authorize.\n *\n * 2. callback({ code, app, state }) → { apiKey, user, workspaceId }\n * id.tangle.tools redirects back to the product's `returnUrl` with\n * `?code=…&app=…&state=…`. The product calls `finish()` with the\n * code; the helper POSTs /cross-site/exchange and returns the minted\n * key + identity. `state` is verified by the caller against its own\n * session (we never see it twice; CSRF is the caller's responsibility\n * per the platform contract — see `cross-site.ts` line 148).\n *\n * 3. revoke({ apiKey }) → void\n * Revoke the credential. Wraps `tangleIdentity().revokeSession`.\n *\n * Storage: this module is stateless. Persistence of the minted key (per\n * user, per workspace) is the caller's job — it goes in whatever\n * encrypted-credentials store the product already runs (sandbox uses Redis,\n * gtm uses Postgres, blueprints uses CF KV). The recipe is identical to\n * sandbox/api/src/lib/platform-client.ts — caller supplies a store, this\n * module hands back the raw key once and never persists it.\n *\n * Why not invent a new wire protocol: tcloud + sandbox already speak this\n * one against the live platform deployment. Diverging breaks the boundary\n * we maintain at the directive level (\"DO NOT invent the wire protocol —\n * use what tcloud already does\"). Every byte on the wire here matches a\n * test in `products/platform/api/tests/cross-site.test.ts`.\n */\n\nimport {\n createTangleIdentityClient,\n DEFAULT_TANGLE_PLATFORM_URL,\n TangleIdentityUnreachableError,\n type TangleIdentityOptions,\n type TangleUserSummary,\n} from '../connectors/adapters/tangle-id.js'\n\nexport interface ConnectFlowOptions extends TangleIdentityOptions {\n /** Base URL of id.tangle.tools (defaults to {@link DEFAULT_TANGLE_PLATFORM_URL}). */\n baseUrl?: string\n}\n\nexport interface StartConnectInput {\n /** Trusted app id (registered on id.tangle.tools — `evals`, `sandbox`,\n * `agent-builder`, `tax-agent`, `legal-agent`, …). */\n appId: string\n /** Caller-generated CSRF nonce. The caller stashes it in its own\n * session/cookie store; on the callback it MUST be compared against\n * the `state` returned in the redirect. */\n state: string\n /** Optional exact-match override of the registered callback URI. When\n * omitted, the platform falls back to the app's first registered\n * redirectUri. When provided, MUST equal one of the registered entries\n * (origin + pathname) — otherwise the platform refuses the flow. */\n redirectUri?: string\n}\n\nexport interface StartConnectOutput {\n /** The URL to redirect the user's browser to. */\n authorizeUrl: string\n}\n\nexport interface FinishConnectInput {\n /** Auth code returned by id.tangle.tools on the callback redirect. */\n code: string\n /** Same `appId` passed to `start()`. */\n appId: string\n}\n\nexport interface FinishConnectOutput {\n /** Newly-minted `sk-tan-*` API key bound to the calling user. Returned\n * ONCE — caller is responsible for stashing it in the product's\n * encrypted credentials store. */\n apiKey: string\n /** Identity hydrated from the exchange response. */\n user: TangleUserSummary\n /** Initial balance the platform returns alongside the key. */\n balance: number\n}\n\n/** Initiate a cross-product connect flow. Returns the URL the product\n * app should redirect the user's browser to. */\nexport function startConnectFlow(\n opts: ConnectFlowOptions,\n input: StartConnectInput,\n): StartConnectOutput {\n if (!input.appId) {\n throw new TangleIdentityUnreachableError('connect/start: appId is required')\n }\n if (!input.state) {\n throw new TangleIdentityUnreachableError(\n 'connect/start: state is required for CSRF protection (matches the platform contract)',\n )\n }\n const baseUrl = (opts.baseUrl ?? DEFAULT_TANGLE_PLATFORM_URL).replace(/\\/+$/, '')\n const url = new URL(`${baseUrl}/cross-site/authorize`)\n url.searchParams.set('app', input.appId)\n url.searchParams.set('state', input.state)\n if (input.redirectUri) url.searchParams.set('redirect', input.redirectUri)\n return { authorizeUrl: url.toString() }\n}\n\n/** Finish a cross-product connect flow. Calls /cross-site/exchange and\n * returns the minted API key + hydrated user identity. */\nexport async function finishConnectFlow(\n opts: ConnectFlowOptions,\n input: FinishConnectInput,\n): Promise<FinishConnectOutput> {\n if (!input.code) {\n throw new TangleIdentityUnreachableError('connect/finish: code is required')\n }\n if (!input.appId) {\n throw new TangleIdentityUnreachableError('connect/finish: appId is required')\n }\n const baseUrl = (opts.baseUrl ?? DEFAULT_TANGLE_PLATFORM_URL).replace(/\\/+$/, '')\n const fetchImpl = opts.fetchImpl ?? fetch\n const timeoutMs = opts.timeoutMs ?? 5_000\n let res: Response\n try {\n res = await fetchImpl(`${baseUrl}/cross-site/exchange`, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ code: input.code, app: input.appId }),\n signal: AbortSignal.timeout(timeoutMs),\n })\n } catch (err) {\n throw new TangleIdentityUnreachableError('connect/finish: exchange request failed', { cause: err })\n }\n if (res.status === 401) {\n throw new TangleIdentityUnreachableError(\n 'connect/finish: exchange code rejected — replay, expired, or wrong app',\n { status: 401 },\n )\n }\n if (!res.ok) {\n const detail = await res.text().catch(() => '')\n throw new TangleIdentityUnreachableError(\n `connect/finish: /cross-site/exchange returned ${res.status}: ${detail.slice(0, 200)}`,\n { status: res.status },\n )\n }\n const body = (await res.json().catch(() => null)) as\n | {\n apiKey?: string\n user?: { id?: string; email?: string; name?: string | null; image?: string | null }\n balance?: number\n }\n | null\n if (!body || typeof body.apiKey !== 'string' || !body.user || typeof body.user.id !== 'string') {\n throw new TangleIdentityUnreachableError('connect/finish: exchange response had an invalid shape')\n }\n return {\n apiKey: body.apiKey,\n user: {\n id: body.user.id,\n ...(typeof body.user.email === 'string' ? { email: body.user.email } : {}),\n ...(body.user.name !== undefined ? { name: body.user.name } : {}),\n ...(body.user.image !== undefined ? { image: body.user.image } : {}),\n },\n balance: typeof body.balance === 'number' && Number.isFinite(body.balance) ? body.balance : 0,\n }\n}\n\n/** Revoke a minted API key. Idempotent — re-revoking a stale key is a no-op. */\nexport async function revokeConnectFlow(\n opts: ConnectFlowOptions,\n input: { apiKey: string },\n): Promise<void> {\n if (!input.apiKey) {\n throw new TangleIdentityUnreachableError('connect/revoke: apiKey is required')\n }\n const client = createTangleIdentityClient(opts)\n await client.revokeSession(input.apiKey)\n}\n\n/**\n * Convenience: build a tiny session manager keyed by `state` for products\n * that don't already have a CSRF store. NOT recommended for production —\n * use your existing session cookie / signed-state mechanism. Exposed for\n * tests and for quick prototyping. In-memory; not shared across workers.\n */\nexport class InMemoryConnectStateStore {\n private readonly entries = new Map<string, { appId: string; expiresAt: number }>()\n\n put(state: string, value: { appId: string; ttlMs?: number }): void {\n this.entries.set(state, {\n appId: value.appId,\n expiresAt: Date.now() + (value.ttlMs ?? 10 * 60_000),\n })\n }\n\n consume(state: string): { appId: string } | undefined {\n const entry = this.entries.get(state)\n this.entries.delete(state)\n if (!entry || entry.expiresAt <= Date.now()) return undefined\n return { appId: entry.appId }\n }\n\n /** Test-only — drop pending state between unit-test runs. */\n clear(): void {\n this.entries.clear()\n }\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,129 @@
1
+ import {
2
+ TangleIdentityUnreachableError,
3
+ createTangleIdentityClient
4
+ } from "./chunk-ATYHZXLL.js";
5
+
6
+ // src/middleware/index.ts
7
+ async function requireTangleAuth(request, opts = {}) {
8
+ const client = opts.client ?? createTangleIdentityClient(opts);
9
+ const requireCredential = opts.requireCredential !== false;
10
+ const token = extractToken(request, opts.sessionCookieName);
11
+ if (!token) {
12
+ if (!requireCredential) {
13
+ return {
14
+ ok: true,
15
+ auth: {
16
+ userId: "",
17
+ workspaceId: "",
18
+ scopes: [],
19
+ kind: "session",
20
+ ownerType: "user"
21
+ }
22
+ };
23
+ }
24
+ return { ok: false, status: 401, reason: "missing_credential" };
25
+ }
26
+ let result;
27
+ try {
28
+ result = await client.verifyToken(token);
29
+ } catch (err) {
30
+ if (err instanceof TangleIdentityUnreachableError) {
31
+ return { ok: false, status: 503, reason: "platform_unreachable" };
32
+ }
33
+ throw err;
34
+ }
35
+ if (!result.valid) {
36
+ const status = result.reason === "service_token_refused" ? 403 : 401;
37
+ return { ok: false, status, reason: result.reason };
38
+ }
39
+ return {
40
+ ok: true,
41
+ auth: {
42
+ userId: result.userId,
43
+ workspaceId: result.workspaceId,
44
+ scopes: result.scopes,
45
+ kind: result.kind,
46
+ ownerType: result.ownerType,
47
+ ...result.expiresAt !== void 0 ? { expiresAt: result.expiresAt } : {},
48
+ ...result.credentialId ? { credentialId: result.credentialId } : {},
49
+ ...result.product ? { product: result.product } : {}
50
+ }
51
+ };
52
+ }
53
+ function extractToken(request, sessionCookieName = "better-auth.session_token") {
54
+ const headers = request.headers;
55
+ const authHeader = headerValue(headers, "authorization");
56
+ if (authHeader && authHeader.startsWith("Bearer ")) {
57
+ const candidate = authHeader.slice(7).trim();
58
+ if (!candidate) return void 0;
59
+ if (candidate.startsWith("svc_")) return void 0;
60
+ return candidate;
61
+ }
62
+ const cookieHeader = headerValue(headers, "cookie");
63
+ if (cookieHeader) {
64
+ const token = readCookie(cookieHeader, sessionCookieName);
65
+ if (token) return token;
66
+ }
67
+ return void 0;
68
+ }
69
+ function headerValue(headers, name) {
70
+ if (typeof headers.get === "function") {
71
+ return headers.get(name) ?? void 0;
72
+ }
73
+ const lookup = headers[name] ?? headers[name.toLowerCase()];
74
+ if (Array.isArray(lookup)) return typeof lookup[0] === "string" ? lookup[0] : void 0;
75
+ return typeof lookup === "string" ? lookup : void 0;
76
+ }
77
+ function readCookie(cookieHeader, name) {
78
+ const target = `${name}=`;
79
+ for (const piece of cookieHeader.split(";")) {
80
+ const trimmed = piece.trim();
81
+ if (!trimmed.startsWith(target)) continue;
82
+ return trimmed.slice(target.length);
83
+ }
84
+ return void 0;
85
+ }
86
+ function honoTangleAuthMiddleware(opts = {}) {
87
+ return async function tangleAuthHandler(c, next) {
88
+ const outcome = await requireTangleAuth(c.req.raw, opts);
89
+ if (!outcome.ok) {
90
+ return new Response(
91
+ JSON.stringify({
92
+ success: false,
93
+ error: { code: outcome.reason.toUpperCase(), message: outcome.reason }
94
+ }),
95
+ {
96
+ status: outcome.status,
97
+ headers: { "content-type": "application/json" }
98
+ }
99
+ );
100
+ }
101
+ c.set("tangleAuth", outcome.auth);
102
+ await next();
103
+ };
104
+ }
105
+ function expressTangleAuthMiddleware(opts = {}) {
106
+ return async function tangleAuthHandler(req, res, next) {
107
+ const headers = req.headers ?? {};
108
+ const outcome = await requireTangleAuth({ headers }, opts);
109
+ if (!outcome.ok) {
110
+ res.status(outcome.status);
111
+ res.setHeader?.("content-type", "application/json");
112
+ res.end(JSON.stringify({
113
+ success: false,
114
+ error: { code: outcome.reason.toUpperCase(), message: outcome.reason }
115
+ }));
116
+ return;
117
+ }
118
+ req.tangleAuth = outcome.auth;
119
+ next();
120
+ };
121
+ }
122
+
123
+ export {
124
+ requireTangleAuth,
125
+ extractToken,
126
+ honoTangleAuthMiddleware,
127
+ expressTangleAuthMiddleware
128
+ };
129
+ //# sourceMappingURL=chunk-SVQ4PHDZ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/middleware/index.ts"],"sourcesContent":["/**\n * @stable Drop-in request middleware that verifies id.tangle.tools\n * credentials and attaches `{ userId, workspaceId, scopes, kind }` to the\n * request.\n *\n * The middleware is framework-agnostic. Instead of binding to express /\n * hono / itty-router specifically (each has its own request typings and\n * lifecycle), the helper accepts either a `Request` (web standard) or a\n * `{ headers }`-shaped object and returns a typed result the caller wires\n * into its own context. Concrete adapters for hono / express / fetch live\n * one call below in the same module so a product can pick the shape it\n * uses without dragging in framework types from the rest.\n *\n * Why this matters: legal-agent runs on Bun + Hono. tax-agent runs on\n * CF Workers + itty-router. gtm-agent runs on Node + Express. Wiring an\n * identical \"is this caller authed\" check across all three is what\n * unblocks shipping product apps in parallel.\n *\n * Token sources (checked in order):\n *\n * 1. `Authorization: Bearer <token>` — handles both sk-tan-* API keys\n * and Better Auth-issued session bearers.\n * 2. `Cookie: better-auth.session_token=<jwt>` — the canonical browser\n * flow. We forward the cookie value as a Bearer to the platform's\n * `/api/auth/get-session` endpoint.\n *\n * On success the middleware returns:\n *\n * { ok: true, auth: { userId, workspaceId, scopes, kind, expiresAt? } }\n *\n * On failure:\n *\n * { ok: false, status: 401|403, reason: '<stable-code>' }\n *\n * The caller decides whether to short-circuit the request (production) or\n * downgrade to anonymous (read-only public endpoints). The middleware\n * NEVER throws on bad-token; only true platform unreachability bubbles up\n * as a `TangleIdentityUnreachableError`.\n */\n\nimport {\n createTangleIdentityClient,\n TangleIdentityUnreachableError,\n type TangleIdentityClient,\n type TangleIdentityOptions,\n type TangleTokenVerifyFailure,\n type TangleTokenVerifyResult,\n} from '../connectors/adapters/tangle-id.js'\n\n/** Auth context the middleware attaches to the request on success. */\nexport interface TangleAuthContext {\n userId: string\n workspaceId: string\n scopes: string[]\n kind: 'api_key' | 'session'\n /** Wall-clock ms epoch when the credential expires, when known. */\n expiresAt?: number\n /** Stable credential id (key id for API keys, session id for sessions). */\n credentialId?: string\n /** Owner-shape on the platform side. */\n ownerType: 'user' | 'team'\n /** Product the credential is scoped to, when known. */\n product?: string\n}\n\nexport type TangleAuthOutcome =\n | { ok: true; auth: TangleAuthContext }\n | { ok: false; status: 401 | 403 | 503; reason: TangleAuthReason }\n\n/** Stable failure reasons surfaced to the caller. */\nexport type TangleAuthReason =\n | 'missing_credential'\n | 'malformed_credential'\n | 'service_token_refused'\n | TangleTokenVerifyFailure\n | 'platform_unreachable'\n\nexport interface RequireTangleAuthOptions extends TangleIdentityOptions {\n /** Pre-built client. When supplied, all `TangleIdentityOptions` fields\n * are ignored. Tests pass a stub here; production code typically\n * constructs the client once at boot and passes it in. */\n client?: TangleIdentityClient\n /** Override the cookie name where the session bearer lives. Defaults\n * to `better-auth.session_token` — matches the platform's Better Auth\n * configuration. */\n sessionCookieName?: string\n /** If true, missing-credential returns `ok: false, status: 401`\n * (default). If false, the middleware returns `ok: true` with a\n * synthetic anonymous context — useful for public endpoints that want\n * to opportunistically hydrate identity. */\n requireCredential?: boolean\n}\n\n/**\n * Verify the credential on `request` against id.tangle.tools and resolve\n * to a typed {@link TangleAuthContext}. Request type is the web-standard\n * `Request` shape — works in Bun, Workers, Deno, Node 20+, Hono context's\n * `c.req.raw`, and Express adapters that surface `req` via `webRequest()`.\n */\nexport async function requireTangleAuth(\n request: Pick<Request, 'headers'>,\n opts: RequireTangleAuthOptions = {},\n): Promise<TangleAuthOutcome> {\n const client = opts.client ?? createTangleIdentityClient(opts)\n const requireCredential = opts.requireCredential !== false\n\n const token = extractToken(request, opts.sessionCookieName)\n if (!token) {\n if (!requireCredential) {\n return {\n ok: true,\n auth: {\n userId: '',\n workspaceId: '',\n scopes: [],\n kind: 'session',\n ownerType: 'user',\n },\n }\n }\n return { ok: false, status: 401, reason: 'missing_credential' }\n }\n\n let result: TangleTokenVerifyResult\n try {\n result = await client.verifyToken(token)\n } catch (err) {\n if (err instanceof TangleIdentityUnreachableError) {\n return { ok: false, status: 503, reason: 'platform_unreachable' }\n }\n throw err\n }\n\n if (!result.valid) {\n const status = result.reason === 'service_token_refused' ? 403 : 401\n return { ok: false, status, reason: result.reason }\n }\n\n return {\n ok: true,\n auth: {\n userId: result.userId,\n workspaceId: result.workspaceId,\n scopes: result.scopes,\n kind: result.kind,\n ownerType: result.ownerType,\n ...(result.expiresAt !== undefined ? { expiresAt: result.expiresAt } : {}),\n ...(result.credentialId ? { credentialId: result.credentialId } : {}),\n ...(result.product ? { product: result.product } : {}),\n },\n }\n}\n\n/**\n * Extract the bearer credential from a request. Public so callers that\n * want to reuse the same token-discovery logic outside the middleware\n * (e.g. to attribute audit log entries) don't have to re-implement it.\n *\n * Order: Authorization header first (canonical), session cookie second.\n * Service tokens (`svc_*`) are explicitly dropped — the platform's\n * middleware refuses to map them to a user, so accepting them here\n * would invite the exact \"service-as-user\" privilege escalation the\n * platform's `resolveServiceIdentity` already guards against.\n */\nexport function extractToken(\n request: Pick<Request, 'headers'>,\n sessionCookieName = 'better-auth.session_token',\n): string | undefined {\n const headers = request.headers\n const authHeader = headerValue(headers, 'authorization')\n if (authHeader && authHeader.startsWith('Bearer ')) {\n const candidate = authHeader.slice(7).trim()\n if (!candidate) return undefined\n if (candidate.startsWith('svc_')) return undefined\n return candidate\n }\n const cookieHeader = headerValue(headers, 'cookie')\n if (cookieHeader) {\n const token = readCookie(cookieHeader, sessionCookieName)\n if (token) return token\n }\n return undefined\n}\n\nfunction headerValue(headers: Headers | Record<string, string | string[] | undefined>, name: string): string | undefined {\n if (typeof (headers as Headers).get === 'function') {\n return (headers as Headers).get(name) ?? undefined\n }\n const lookup = (headers as Record<string, unknown>)[name] ?? (headers as Record<string, unknown>)[name.toLowerCase()]\n if (Array.isArray(lookup)) return typeof lookup[0] === 'string' ? lookup[0] : undefined\n return typeof lookup === 'string' ? lookup : undefined\n}\n\nfunction readCookie(cookieHeader: string, name: string): string | undefined {\n // Cookies are semicolon-delimited; we DON'T URL-decode the value because\n // Better Auth's signed-cookie format includes `.` and `=` that survive\n // unencoded through the standard cookie parser. Matching by exact name=\n // prefix keeps us protocol-correct.\n const target = `${name}=`\n for (const piece of cookieHeader.split(';')) {\n const trimmed = piece.trim()\n if (!trimmed.startsWith(target)) continue\n return trimmed.slice(target.length)\n }\n return undefined\n}\n\n/**\n * Hono-flavored convenience wrapper. Returns a hono middleware factory\n * that calls {@link requireTangleAuth} and stashes the result on the\n * Hono context under `c.set('tangleAuth', auth)`. On failure short-\n * circuits with the canonical {success:false} envelope the platform uses.\n *\n * Kept typed against a structural `Context`-like shape so this module\n * does NOT take a hono peerDep. Consumers pass `c` directly.\n */\nexport function honoTangleAuthMiddleware(opts: RequireTangleAuthOptions = {}) {\n return async function tangleAuthHandler(\n c: HonoLikeContext,\n next: () => Promise<void>,\n ): Promise<Response | void> {\n const outcome = await requireTangleAuth(c.req.raw, opts)\n if (!outcome.ok) {\n return new Response(\n JSON.stringify({\n success: false,\n error: { code: outcome.reason.toUpperCase(), message: outcome.reason },\n }),\n {\n status: outcome.status,\n headers: { 'content-type': 'application/json' },\n },\n )\n }\n c.set('tangleAuth', outcome.auth)\n await next()\n }\n}\n\n/** Minimal Hono Context-shaped surface. Avoids the hono peerDep. */\nexport interface HonoLikeContext {\n req: { raw: Request }\n set(key: 'tangleAuth', value: TangleAuthContext): void\n}\n\n/**\n * Express-flavored convenience wrapper. Same outcome shape as the Hono\n * helper, expressed via the Node `req` / `res` / `next` triple. Consumers\n * pass the triple as positional args. Returns a function compatible with\n * any express-like `app.use(fn)`.\n */\nexport function expressTangleAuthMiddleware(opts: RequireTangleAuthOptions = {}) {\n return async function tangleAuthHandler(\n req: ExpressLikeRequest,\n res: ExpressLikeResponse,\n next: (err?: unknown) => void,\n ): Promise<void> {\n // Build a `Request`-compatible header view from express's\n // `IncomingMessage.headers` map — string | string[] | undefined.\n const headers: Record<string, string | string[] | undefined> = req.headers ?? {}\n const outcome = await requireTangleAuth({ headers: headers as never }, opts)\n if (!outcome.ok) {\n res.status(outcome.status)\n res.setHeader?.('content-type', 'application/json')\n res.end(JSON.stringify({\n success: false,\n error: { code: outcome.reason.toUpperCase(), message: outcome.reason },\n }))\n return\n }\n req.tangleAuth = outcome.auth\n next()\n }\n}\n\n/** Minimal Express-shaped surfaces. Avoids the express peerDep. */\nexport interface ExpressLikeRequest {\n headers: Record<string, string | string[] | undefined>\n tangleAuth?: TangleAuthContext\n}\nexport interface ExpressLikeResponse {\n status(code: number): unknown\n setHeader?(name: string, value: string): unknown\n end(body: string): unknown\n}\n"],"mappings":"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","names":[]}
@@ -1,3 +1,7 @@
1
+ import {
2
+ IntegrationRuntimeError,
3
+ normalizeIntegrationError
4
+ } from "./chunk-H4XYLS7T.js";
1
5
  import {
2
6
  integrationSpecToConnector,
3
7
  listIntegrationSpecs
@@ -736,75 +740,6 @@ function assertBridgePayload(value) {
736
740
  if (!Array.isArray(payload.tools)) throw new Error("Invalid integration bridge tools.");
737
741
  }
738
742
 
739
- // src/errors.ts
740
- var IntegrationRuntimeError = class extends Error {
741
- code;
742
- status;
743
- userAction;
744
- metadata;
745
- constructor(input) {
746
- super(input.message);
747
- this.name = "IntegrationRuntimeError";
748
- this.code = input.code;
749
- this.status = input.status ?? statusForCode(input.code);
750
- this.userAction = input.userAction;
751
- this.metadata = input.metadata;
752
- }
753
- };
754
- function normalizeIntegrationError(error) {
755
- if (error instanceof IntegrationRuntimeError) {
756
- return {
757
- ok: false,
758
- code: error.code,
759
- message: error.message,
760
- status: error.status,
761
- userAction: error.userAction,
762
- metadata: redactUnknown2(error.metadata)
763
- };
764
- }
765
- const message = error instanceof Error ? error.message : String(error ?? "Unknown integration error.");
766
- return {
767
- ok: false,
768
- code: inferCode(message),
769
- message,
770
- status: 500
771
- };
772
- }
773
- function statusForCode(code) {
774
- if (code === "missing_connection" || code === "missing_grant") return 409;
775
- if (code === "approval_required") return 202;
776
- if (code === "approval_denied") return 403;
777
- if (code === "connection_revoked" || code === "connection_expired" || code === "provider_auth_failed") return 401;
778
- if (code === "scope_missing" || code === "action_denied" || code === "passthrough_disabled") return 403;
779
- if (code === "action_not_found" || code === "trigger_not_found" || code === "manifest_invalid" || code === "input_invalid") return 400;
780
- if (code === "provider_rate_limited") return 429;
781
- if (code === "provider_unavailable") return 503;
782
- if (code === "capability_expired" || code === "capability_invalid") return 401;
783
- return 500;
784
- }
785
- function inferCode(message) {
786
- if (/approval/i.test(message)) return "approval_required";
787
- if (/scope/i.test(message)) return "scope_missing";
788
- if (/expired/i.test(message)) return "connection_expired";
789
- if (/revoked/i.test(message)) return "connection_revoked";
790
- if (/rate.?limit|429/i.test(message)) return "provider_rate_limited";
791
- if (/unauth|forbidden|401|403/i.test(message)) return "provider_auth_failed";
792
- return "unknown";
793
- }
794
- function redactUnknown2(value) {
795
- if (Array.isArray(value)) return value.map(redactUnknown2);
796
- if (!value || typeof value !== "object") return value;
797
- const out = {};
798
- for (const [key, child] of Object.entries(value)) {
799
- if (/token|secret|password|authorization|api[_-]?key|credential|refresh/i.test(key)) {
800
- out[key] = "[REDACTED]";
801
- } else {
802
- out[key] = redactUnknown2(child);
803
- }
804
- }
805
- return out;
806
- }
807
-
808
743
  // src/client.ts
809
744
  var TangleIntegrationsClient = class {
810
745
  endpoint;
@@ -1292,6 +1227,31 @@ function toTrigger2(connector, trigger2, connection) {
1292
1227
  connectionId: connection?.id
1293
1228
  };
1294
1229
  }
1230
+ function filterDiscoveryByWorkspaceScopes(discovery, workspaceScopes, opts = {}) {
1231
+ if (workspaceScopes.length === 0 && !opts.denyByDefault) return discovery;
1232
+ const granted = new Set(workspaceScopes);
1233
+ const hasWildcard = granted.has("tangle:*");
1234
+ function allowed(connectorId, scopes) {
1235
+ if (hasWildcard) return true;
1236
+ if (granted.has(`${connectorId}:*`)) return true;
1237
+ for (const scope of scopes) {
1238
+ if (!granted.has(scope)) return false;
1239
+ }
1240
+ return true;
1241
+ }
1242
+ const capabilities = discovery.capabilities.filter((cap) => allowed(cap.connectorId, cap.scopes));
1243
+ const triggers = discovery.triggers.filter((t) => allowed(t.connectorId, t.scopes));
1244
+ const countsByConnector = {};
1245
+ for (const cap of capabilities) {
1246
+ countsByConnector[cap.connectorId] = (countsByConnector[cap.connectorId] ?? 0) + 1;
1247
+ }
1248
+ return {
1249
+ capabilities,
1250
+ triggers,
1251
+ countsByConnector,
1252
+ unreachableConnectors: discovery.unreachableConnectors
1253
+ };
1254
+ }
1295
1255
 
1296
1256
  // src/events.ts
1297
1257
  var InMemoryIntegrationEventStore = class {
@@ -1829,7 +1789,7 @@ function buildApprovalRequest(ctx, reason, requestedAt) {
1829
1789
  function redactApprovalRequest(request) {
1830
1790
  return {
1831
1791
  ...request,
1832
- inputPreview: redactUnknown3(request.inputPreview)
1792
+ inputPreview: redactUnknown2(request.inputPreview)
1833
1793
  };
1834
1794
  }
1835
1795
  function ruleMatches(rule, ctx) {
@@ -1853,17 +1813,17 @@ function defaultReason2(effect, risk) {
1853
1813
  return `${risk} integration action requires approval by default policy.`;
1854
1814
  }
1855
1815
  function previewInput(input) {
1856
- return redactUnknown3(input);
1816
+ return redactUnknown2(input);
1857
1817
  }
1858
- function redactUnknown3(value) {
1859
- if (Array.isArray(value)) return value.map(redactUnknown3);
1818
+ function redactUnknown2(value) {
1819
+ if (Array.isArray(value)) return value.map(redactUnknown2);
1860
1820
  if (!value || typeof value !== "object") return value;
1861
1821
  const out = {};
1862
1822
  for (const [key, child] of Object.entries(value)) {
1863
1823
  if (/token|secret|password|authorization|api[_-]?key|credential/i.test(key)) {
1864
1824
  out[key] = "[REDACTED]";
1865
1825
  } else {
1866
- out[key] = redactUnknown3(child);
1826
+ out[key] = redactUnknown2(child);
1867
1827
  }
1868
1828
  }
1869
1829
  return out;
@@ -1946,13 +1906,13 @@ function redactInvocationEnvelope(envelope) {
1946
1906
  return {
1947
1907
  ...envelope,
1948
1908
  capabilityToken: "[REDACTED]",
1949
- input: redactUnknown4(envelope.input)
1909
+ input: redactUnknown3(envelope.input)
1950
1910
  };
1951
1911
  }
1952
1912
  function redactCapability(capability) {
1953
1913
  return {
1954
1914
  ...capability,
1955
- metadata: redactUnknown4(capability.metadata)
1915
+ metadata: redactUnknown3(capability.metadata)
1956
1916
  };
1957
1917
  }
1958
1918
  function normalizeIntegrationResult(result) {
@@ -2005,15 +1965,15 @@ var IntegrationSandboxHost = class {
2005
1965
  return dispatchIntegrationInvocation(envelope, this.options);
2006
1966
  }
2007
1967
  };
2008
- function redactUnknown4(value) {
2009
- if (Array.isArray(value)) return value.map(redactUnknown4);
1968
+ function redactUnknown3(value) {
1969
+ if (Array.isArray(value)) return value.map(redactUnknown3);
2010
1970
  if (!value || typeof value !== "object") return value;
2011
1971
  const out = {};
2012
1972
  for (const [key, child] of Object.entries(value)) {
2013
1973
  if (/token|secret|password|authorization|api[_-]?key|credential/i.test(key)) {
2014
1974
  out[key] = "[REDACTED]";
2015
1975
  } else {
2016
- out[key] = redactUnknown4(child);
1976
+ out[key] = redactUnknown3(child);
2017
1977
  }
2018
1978
  }
2019
1979
  return out;
@@ -4451,9 +4411,6 @@ export {
4451
4411
  buildIntegrationBridgeEnvironment,
4452
4412
  parseIntegrationBridgeEnvironment,
4453
4413
  redactIntegrationBridgePayload,
4454
- IntegrationRuntimeError,
4455
- normalizeIntegrationError,
4456
- statusForCode,
4457
4414
  TangleIntegrationsClient,
4458
4415
  createTangleIntegrationsClient,
4459
4416
  renderConsentSummary,
@@ -4468,6 +4425,7 @@ export {
4468
4425
  createCredentialBackedAdapterProvider,
4469
4426
  revokeConnection,
4470
4427
  discoverWorkspaceCapabilities,
4428
+ filterDiscoveryByWorkspaceScopes,
4471
4429
  InMemoryIntegrationEventStore,
4472
4430
  receiveIntegrationWebhook,
4473
4431
  storedEventToTriggerEvent,
@@ -4526,4 +4484,4 @@ export {
4526
4484
  signCapability,
4527
4485
  verifyCapabilityToken
4528
4486
  };
4529
- //# sourceMappingURL=chunk-ALCIWTIR.js.map
4487
+ //# sourceMappingURL=chunk-UWRYFPJW.js.map