@tangle-network/hub-sdk 1.0.5 → 1.0.7
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 +17 -6
- package/dist/{client-BH3gTLjN.d.ts → client-C-JbtG50.d.ts} +2 -2
- package/dist/{client-BH3gTLjN.d.ts.map → client-C-JbtG50.d.ts.map} +1 -1
- package/dist/{client-CsdLLECZ.js → client-gF4iRjd3.js} +10 -5
- package/dist/client-gF4iRjd3.js.map +1 -0
- package/dist/index.d.ts +2 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -24
- package/dist/index.js.map +1 -1
- package/dist/mcp/index.d.ts +1 -1
- package/dist/mcp/index.js +1 -1
- package/package.json +3 -3
- package/dist/client-CsdLLECZ.js.map +0 -1
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/broker.ts","../src/event-delivery.ts","../src/knowledge-approval.ts","../src/line-grants.ts","../src/workflow-definition-types.ts"],"sourcesContent":["import { HubClient, HubSdkError } from \"./client.js\";\n\nexport interface HubAppBrokerOptions {\n baseUrl: string;\n clientId: string;\n clientSecret: string;\n fetch?: typeof fetch;\n}\nexport interface HubAppBrokerToken {\n accessToken: string;\n expiresIn: number;\n scopes: string[];\n}\nexport interface HubAppBrokerExchange extends HubAppBrokerToken {\n grantId: string;\n connectionId: string;\n}\n/** Server-only client. App secrets never become a user's general API key.\n * Every invocation mints and consumes one short-lived, exact-grant token. */\nexport class HubAppBrokerClient {\n private readonly baseUrl: string;\n private readonly fetcher: typeof fetch;\n constructor(private readonly options: HubAppBrokerOptions) {\n this.baseUrl = options.baseUrl.replace(/\\/+$/, \"\");\n this.fetcher = options.fetch ?? fetch.bind(globalThis);\n }\n private async token(\n path: string,\n body: Record<string, unknown>,\n ): Promise<Record<string, unknown>> {\n const response = await this.fetcher(`${this.baseUrl}${path}`, {\n method: \"POST\",\n // Workers support manual redirects; the non-ok check below rejects every 3xx.\n redirect: \"manual\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n ...body,\n client_id: this.options.clientId,\n client_secret: this.options.clientSecret,\n }),\n });\n const result: unknown = await response.json().catch(() => null);\n if (\n !response.ok ||\n !result ||\n typeof result !== \"object\" ||\n Array.isArray(result)\n ) {\n throw new HubSdkError(\n {\n code: \"HUB_CONFIG_MISSING\",\n message: \"Application authorization is unavailable\",\n },\n { status: response.status },\n );\n }\n return result as Record<string, unknown>;\n }\n private readToken(body: Record<string, unknown>): HubAppBrokerToken {\n if (\n typeof body.access_token !== \"string\" ||\n !body.access_token.startsWith(\"sk-tan-broker-\") ||\n body.token_type !== \"Bearer\" ||\n typeof body.expires_in !== \"number\" ||\n !Number.isSafeInteger(body.expires_in) ||\n body.expires_in <= 0 ||\n typeof body.scope !== \"string\"\n )\n throw new HubSdkError({\n code: \"HUB_CONFIG_MISSING\",\n message: \"Invalid application authorization response\",\n });\n return {\n accessToken: body.access_token,\n expiresIn: body.expires_in,\n scopes: body.scope.split(\" \").filter(Boolean),\n };\n }\n async exchange(input: {\n code: string;\n redirectUri: string;\n connectionId?: string;\n }): Promise<HubAppBrokerExchange> {\n const body = await this.token(\"/v1/apps/oauth/token\", {\n grant_type: \"authorization_code\",\n code: input.code,\n redirect_uri: input.redirectUri,\n ...(input.connectionId === undefined\n ? {}\n : { connection_id: input.connectionId }),\n ttl_seconds: 60,\n });\n const token = this.readToken(body);\n if (\n typeof body.grant_id !== \"string\" ||\n !body.grant_id ||\n typeof body.connection_id !== \"string\" ||\n !body.connection_id.trim() ||\n (input.connectionId !== undefined &&\n body.connection_id !== input.connectionId)\n )\n throw new HubSdkError({\n code: \"HUB_CONFIG_MISSING\",\n message: \"Application connection did not match consent\",\n });\n return {\n ...token,\n grantId: body.grant_id,\n connectionId: body.connection_id,\n };\n }\n async mint(grantId: string): Promise<HubAppBrokerToken> {\n if (!/^[A-Za-z0-9_-]{1,256}$/.test(grantId))\n throw new Error(\"Invalid application grant\");\n return this.readToken(\n await this.token(\n `/v1/apps/grants/${encodeURIComponent(grantId)}/mint-broker-token`,\n { grant_id: grantId, ttl_seconds: 60 },\n ),\n );\n }\n async invoke<T = unknown>(\n grantId: string,\n connectionId: string,\n actionPath: string,\n input: Record<string, unknown>,\n ) {\n const token = await this.mint(grantId);\n if (!token.scopes.includes(actionPath))\n throw new HubSdkError({\n code: \"HUB_POLICY_DENIED\",\n message: \"This action was not granted to the application\",\n });\n return new HubClient({\n baseUrl: this.baseUrl,\n apiKey: token.accessToken,\n fetch: this.fetcher,\n }).tools.invoke<T>(actionPath, input, { connectionId });\n }\n}\n","import type { HubEventDelivery } from \"./types.js\";\n\nconst DEFAULT_TOLERANCE_SECONDS = 300;\nconst MAX_TOLERANCE_SECONDS = 3_600;\nconst MAX_SIGNATURE_HEADER_LENGTH = 1_024;\nconst MAX_SIGNATURES = 5;\nconst SHA256_HEX_LENGTH = 64;\nconst MIN_CALLBACK_SECRET_BYTES = 32;\nconst MAX_SECRET_SCOPE_COMPONENT_BYTES = 512;\nconst DEFAULT_MAX_CALLBACK_BODY_BYTES = 5 * 1024 * 1024 + 64 * 1024;\nconst MAX_CALLBACK_BODY_BYTES = 10 * 1024 * 1024;\n\nexport type HubEventSignatureFailure =\n | \"missing\"\n | \"malformed\"\n | \"stale\"\n | \"mismatch\";\n\nexport type HubEventSignatureResult =\n | { valid: true; timestamp: number }\n | { valid: false; reason: HubEventSignatureFailure };\n\nexport interface VerifyHubEventSignatureInput {\n body: string;\n signature: string | null | undefined;\n secret: string;\n toleranceSeconds?: number;\n now?: number | Date;\n}\n\nexport interface DeriveHubEventCallbackSecretInput {\n /** App-level secret stored only in the product's server environment. */\n rootSecret: string;\n /** Stable product slug, such as `relationships`. */\n productId: string;\n /** Stable Tangle owner id; user and team ids are both valid. */\n ownerId: string;\n /** Stable product-local binding id used in the callback route. */\n bindingId: string;\n}\n\nexport type HubEventRequestFailureCode =\n | \"METHOD_NOT_ALLOWED\"\n | \"UNEXPECTED_EVENT\"\n | \"UNSUPPORTED_MEDIA_TYPE\"\n | \"PAYLOAD_TOO_LARGE\"\n | \"INVALID_SIGNATURE\"\n | \"INVALID_DELIVERY\"\n | \"DELIVERY_ID_MISMATCH\";\n\nexport type HubEventRequestResult =\n | { ok: true; delivery: HubEventDelivery }\n | {\n ok: false;\n code: HubEventRequestFailureCode;\n response: Response;\n };\n\nexport interface AuthenticateHubEventRequestInput {\n request: Request;\n /** The per-binding secret supplied when the subscription was created. */\n secret: string;\n /** Defaults to 5 MiB plus envelope overhead; hard-capped at 10 MiB. */\n maxBodyBytes?: number;\n toleranceSeconds?: number;\n now?: number | Date;\n}\n\nexport class HubEventDeliveryError extends Error {\n readonly code:\n | \"INVALID_JSON\"\n | \"INVALID_PAYLOAD\"\n | \"CRYPTO_UNAVAILABLE\"\n | \"INVALID_TOLERANCE\"\n | \"INVALID_CALLBACK_SECRET\"\n | \"INVALID_SECRET_SCOPE\"\n | \"INVALID_BODY_LIMIT\";\n\n constructor(code: HubEventDeliveryError[\"code\"], message: string) {\n super(message);\n this.name = \"HubEventDeliveryError\";\n this.code = code;\n }\n}\n\n/**\n * Derive one callback secret per product binding from a single server-held root\n * secret. Products persist only the binding id; the derived secret can be\n * reproduced for callback authentication without another secret table.\n */\nexport async function deriveHubEventCallbackSecret(\n input: DeriveHubEventCallbackSecretInput,\n): Promise<string> {\n const encoder = new TextEncoder();\n if (encoder.encode(input.rootSecret).byteLength < MIN_CALLBACK_SECRET_BYTES) {\n throw new HubEventDeliveryError(\n \"INVALID_CALLBACK_SECRET\",\n `Hub event root secret must contain at least ${MIN_CALLBACK_SECRET_BYTES} UTF-8 bytes`,\n );\n }\n for (const [name, value] of [\n [\"productId\", input.productId],\n [\"ownerId\", input.ownerId],\n [\"bindingId\", input.bindingId],\n ] as const) {\n const bytes = encoder.encode(value).byteLength;\n if (bytes === 0 || bytes > MAX_SECRET_SCOPE_COMPONENT_BYTES) {\n throw new HubEventDeliveryError(\n \"INVALID_SECRET_SCOPE\",\n `${name} must contain 1-${MAX_SECRET_SCOPE_COMPONENT_BYTES} UTF-8 bytes`,\n );\n }\n }\n const subtle = requireSubtleCrypto();\n const key = await subtle.importKey(\n \"raw\",\n encoder.encode(input.rootSecret),\n { name: \"HMAC\", hash: \"SHA-256\" },\n false,\n [\"sign\"],\n );\n const scope = JSON.stringify([\n \"tangle-hub-event-callback\",\n 1,\n input.productId,\n input.ownerId,\n input.bindingId,\n ]);\n const signature = new Uint8Array(\n await subtle.sign(\"HMAC\", key, encoder.encode(scope)),\n );\n return bytesToBase64Url(signature);\n}\n\n/**\n * Authenticate and parse the platform's callback as one operation. The exact\n * raw body is read once with a size limit before its signature is checked.\n *\n * A successful callback can be retried. Use `delivery.runId` as the durable\n * idempotency key before starting product work.\n */\nexport async function authenticateHubEventRequest(\n input: AuthenticateHubEventRequestInput,\n): Promise<HubEventRequestResult> {\n if (input.request.method !== \"POST\") {\n return requestFailure(\"METHOD_NOT_ALLOWED\", 405, \"method_not_allowed\", {\n Allow: \"POST\",\n });\n }\n if (input.request.headers.get(\"x-tangle-event\") !== \"hub.event\") {\n return requestFailure(\"UNEXPECTED_EVENT\", 400, \"invalid_hub_event\");\n }\n const contentType = input.request.headers.get(\"content-type\") ?? \"\";\n if (!/^application\\/json(?:\\s*;|$)/i.test(contentType)) {\n return requestFailure(\n \"UNSUPPORTED_MEDIA_TYPE\",\n 415,\n \"unsupported_media_type\",\n );\n }\n const maxBodyBytes = input.maxBodyBytes ?? DEFAULT_MAX_CALLBACK_BODY_BYTES;\n if (\n !Number.isSafeInteger(maxBodyBytes) ||\n maxBodyBytes < 1 ||\n maxBodyBytes > MAX_CALLBACK_BODY_BYTES\n ) {\n throw new HubEventDeliveryError(\n \"INVALID_BODY_LIMIT\",\n `Hub event body limit must be an integer from 1 to ${MAX_CALLBACK_BODY_BYTES} bytes`,\n );\n }\n if (\n new TextEncoder().encode(input.secret).byteLength <\n MIN_CALLBACK_SECRET_BYTES\n ) {\n throw new HubEventDeliveryError(\n \"INVALID_CALLBACK_SECRET\",\n `Hub event callback secret must contain at least ${MIN_CALLBACK_SECRET_BYTES} UTF-8 bytes`,\n );\n }\n const declaredLength = input.request.headers.get(\"content-length\");\n if (\n declaredLength !== null &&\n /^\\d+$/.test(declaredLength) &&\n Number(declaredLength) > maxBodyBytes\n ) {\n return requestFailure(\"PAYLOAD_TOO_LARGE\", 413, \"payload_too_large\");\n }\n const body = await readRequestBody(input.request, maxBodyBytes);\n if (body === null) {\n return requestFailure(\"PAYLOAD_TOO_LARGE\", 413, \"payload_too_large\");\n }\n const signature = await verifyHubEventSignature({\n body,\n signature: input.request.headers.get(\"x-tangle-signature\"),\n secret: input.secret,\n ...(input.toleranceSeconds !== undefined\n ? { toleranceSeconds: input.toleranceSeconds }\n : {}),\n ...(input.now !== undefined ? { now: input.now } : {}),\n });\n if (!signature.valid) {\n return requestFailure(\"INVALID_SIGNATURE\", 401, \"invalid_signature\");\n }\n let delivery: HubEventDelivery;\n try {\n delivery = parseHubEventDelivery(body);\n } catch (error) {\n if (error instanceof HubEventDeliveryError) {\n return requestFailure(\"INVALID_DELIVERY\", 400, \"invalid_delivery\");\n }\n throw error;\n }\n if (input.request.headers.get(\"x-tangle-delivery-id\") !== delivery.runId) {\n return requestFailure(\"DELIVERY_ID_MISMATCH\", 400, \"invalid_delivery\");\n }\n return { ok: true, delivery };\n}\n\n/**\n * Authenticate a Hub event callback against its exact raw request body.\n * The timestamp window rejects captured-request replay, and byte-wise\n * comparison avoids secret-dependent string comparison behavior.\n */\nexport async function verifyHubEventSignature(\n input: VerifyHubEventSignatureInput,\n): Promise<HubEventSignatureResult> {\n if (!input.signature) return { valid: false, reason: \"missing\" };\n if (input.signature.length > MAX_SIGNATURE_HEADER_LENGTH) {\n return { valid: false, reason: \"malformed\" };\n }\n const parsed = parseSignatureHeader(input.signature);\n if (!parsed) return { valid: false, reason: \"malformed\" };\n\n const tolerance = input.toleranceSeconds ?? DEFAULT_TOLERANCE_SECONDS;\n if (\n !Number.isFinite(tolerance) ||\n !Number.isInteger(tolerance) ||\n tolerance < 0 ||\n tolerance > MAX_TOLERANCE_SECONDS\n ) {\n throw new HubEventDeliveryError(\n \"INVALID_TOLERANCE\",\n `Signature tolerance must be an integer from 0 to ${MAX_TOLERANCE_SECONDS} seconds`,\n );\n }\n const nowMs =\n input.now instanceof Date\n ? input.now.getTime()\n : typeof input.now === \"number\"\n ? input.now\n : Date.now();\n if (\n !Number.isFinite(nowMs) ||\n Math.abs(Math.floor(nowMs / 1_000) - parsed.timestamp) > tolerance\n ) {\n return { valid: false, reason: \"stale\" };\n }\n\n const subtle = requireSubtleCrypto();\n const encoder = new TextEncoder();\n const key = await subtle.importKey(\n \"raw\",\n encoder.encode(input.secret),\n { name: \"HMAC\", hash: \"SHA-256\" },\n false,\n [\"sign\"],\n );\n const expected = new Uint8Array(\n await subtle.sign(\n \"HMAC\",\n key,\n encoder.encode(`${parsed.timestamp}.${input.body}`),\n ),\n );\n let matched = 0;\n for (const signature of parsed.signatures) {\n matched |= constantTimeEqual(expected, signature);\n }\n return matched === 1\n ? { valid: true, timestamp: parsed.timestamp }\n : { valid: false, reason: \"mismatch\" };\n}\n\nexport function parseHubEventDelivery(\n input: string | unknown,\n): HubEventDelivery {\n let value: unknown = input;\n if (typeof input === \"string\") {\n try {\n value = JSON.parse(input);\n } catch {\n throw new HubEventDeliveryError(\n \"INVALID_JSON\",\n \"Hub event callback body is not valid JSON\",\n );\n }\n }\n if (!isRecord(value)) return invalidDelivery();\n const source = value.source;\n const providerEvent = value.providerEvent;\n if (\n !nonEmptyString(value.subscriptionId) ||\n !nonEmptyString(value.workflowId) ||\n !nonEmptyString(value.runId) ||\n !isRecord(source) ||\n (source.kind !== \"channel\" && source.kind !== \"connection\") ||\n !nonEmptyString(source.id) ||\n !nonEmptyString(source.provider) ||\n !nonEmptyString(source.event) ||\n !isRecord(providerEvent) ||\n !nonEmptyString(providerEvent.provider) ||\n !nonEmptyString(providerEvent.connectionId) ||\n !nonEmptyString(providerEvent.type) ||\n providerEvent.provider !== source.provider ||\n providerEvent.type !== source.event ||\n !Object.hasOwn(providerEvent, \"payload\") ||\n !optionalString(providerEvent.action) ||\n !optionalString(providerEvent.repo) ||\n !optionalString(providerEvent.deliveryId) ||\n !nonEmptyString(value.firedAt) ||\n !Number.isFinite(Date.parse(value.firedAt))\n ) {\n return invalidDelivery();\n }\n return {\n subscriptionId: value.subscriptionId,\n workflowId: value.workflowId,\n runId: value.runId,\n source: {\n kind: source.kind,\n id: source.id,\n provider: source.provider,\n event: source.event,\n },\n providerEvent: {\n provider: providerEvent.provider,\n connectionId: providerEvent.connectionId,\n type: providerEvent.type,\n ...(typeof providerEvent.action === \"string\"\n ? { action: providerEvent.action }\n : {}),\n ...(typeof providerEvent.repo === \"string\"\n ? { repo: providerEvent.repo }\n : {}),\n ...(typeof providerEvent.deliveryId === \"string\"\n ? { deliveryId: providerEvent.deliveryId }\n : {}),\n payload: providerEvent.payload,\n },\n firedAt: value.firedAt,\n };\n}\n\nfunction parseSignatureHeader(\n header: string,\n): { timestamp: number; signatures: Uint8Array[] } | null {\n let timestamp: number | undefined;\n const signatures: Uint8Array[] = [];\n for (const segment of header.split(\",\")) {\n const separator = segment.indexOf(\"=\");\n if (separator <= 0) return null;\n const key = segment.slice(0, separator).trim();\n const value = segment.slice(separator + 1).trim();\n if (key === \"t\") {\n if (timestamp !== undefined || !/^\\d+$/.test(value)) return null;\n const candidate = Number(value);\n if (!Number.isSafeInteger(candidate) || candidate <= 0) return null;\n timestamp = candidate;\n } else if (key === \"v1\") {\n if (\n signatures.length >= MAX_SIGNATURES ||\n value.length !== SHA256_HEX_LENGTH ||\n !/^[0-9a-f]+$/i.test(value)\n ) {\n return null;\n }\n signatures.push(hexToBytes(value));\n }\n }\n return timestamp !== undefined && signatures.length > 0\n ? { timestamp, signatures }\n : null;\n}\n\nfunction hexToBytes(hex: string): Uint8Array {\n const bytes = new Uint8Array(hex.length / 2);\n for (let index = 0; index < bytes.length; index += 1) {\n bytes[index] = Number.parseInt(hex.slice(index * 2, index * 2 + 2), 16);\n }\n return bytes;\n}\n\nfunction constantTimeEqual(expected: Uint8Array, actual: Uint8Array): number {\n let difference = expected.length ^ actual.length;\n const length = Math.max(expected.length, actual.length);\n for (let index = 0; index < length; index += 1) {\n difference |=\n (expected[index % expected.length] ?? 0) ^\n (actual[index % actual.length] ?? 0);\n }\n return difference === 0 ? 1 : 0;\n}\n\nfunction nonEmptyString(value: unknown): value is string {\n return typeof value === \"string\" && value.length > 0;\n}\n\nfunction optionalString(value: unknown): boolean {\n return value === undefined || nonEmptyString(value);\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction invalidDelivery(): never {\n throw new HubEventDeliveryError(\n \"INVALID_PAYLOAD\",\n \"Hub event callback body does not match the delivery contract\",\n );\n}\n\nfunction requireSubtleCrypto(): SubtleCrypto {\n const subtle = globalThis.crypto?.subtle;\n if (!subtle) {\n throw new HubEventDeliveryError(\n \"CRYPTO_UNAVAILABLE\",\n \"Web Crypto is required for Hub event callback authentication\",\n );\n }\n return subtle;\n}\n\nfunction bytesToBase64Url(bytes: Uint8Array): string {\n let binary = \"\";\n for (const byte of bytes) binary += String.fromCharCode(byte);\n return btoa(binary)\n .replace(/\\+/g, \"-\")\n .replace(/\\//g, \"_\")\n .replace(/=+$/g, \"\");\n}\n\nasync function readRequestBody(\n request: Request,\n maxBytes: number,\n): Promise<string | null> {\n const reader = request.body?.getReader();\n if (!reader) return \"\";\n const chunks: Uint8Array[] = [];\n let total = 0;\n try {\n while (true) {\n const next = await reader.read();\n if (next.done) break;\n total += next.value.byteLength;\n if (total > maxBytes) return null;\n chunks.push(next.value);\n }\n } finally {\n await reader.cancel().catch(() => {});\n }\n const body = new Uint8Array(total);\n let offset = 0;\n for (const chunk of chunks) {\n body.set(chunk, offset);\n offset += chunk.byteLength;\n }\n return new TextDecoder().decode(body);\n}\n\nfunction requestFailure(\n code: HubEventRequestFailureCode,\n status: number,\n error: string,\n headers?: HeadersInit,\n): Extract<HubEventRequestResult, { ok: false }> {\n return {\n ok: false,\n code,\n response: Response.json(\n { error },\n {\n status,\n headers: {\n \"cache-control\": \"no-store\",\n ...(headers ? Object.fromEntries(new Headers(headers)) : {}),\n },\n },\n ),\n };\n}\n","import type {\n HubKnowledgeManagementClient,\n KnowledgeSourceSummary,\n} from \"./knowledge-management.js\";\n\n/** Called only after the owner reviews this exact pending effect and clicks\n * Approve. Re-read first: a stale tab cannot approve a substituted operation. */\nexport async function approveKnowledgeSource(input: {\n port: Pick<HubKnowledgeManagementClient, \"get\" | \"refresh\">;\n approvals: Pick<import(\"./client.js\").HubApprovalsClient, \"approve\">;\n source: KnowledgeSourceSummary;\n}): Promise<KnowledgeSourceSummary> {\n const { source, port, approvals } = input;\n if (\n source.status !== \"awaiting-approval\" ||\n !source.approval ||\n !source.refreshRequestId ||\n source.approval.preview.sourceId !== source.id ||\n source.approval.preview.requestId !== source.refreshRequestId\n ) {\n throw new Error(\n \"This source has no reviewable pending action. Reload knowledge.\",\n );\n }\n const current = await port.get(source.id);\n if (\n current.id !== source.id ||\n current.status !== \"awaiting-approval\" ||\n current.refreshRequestId !== source.refreshRequestId ||\n JSON.stringify(current.approval) !== JSON.stringify(source.approval)\n ) {\n throw new Error(\n \"The requested action changed. Reload knowledge and review it again.\",\n );\n }\n let approvalToken: string | undefined;\n try {\n const decision = await approvals.approve(source.approval.id);\n if (\n decision.approval.id !== source.approval.id ||\n decision.approval.actionPath !== source.approval.path ||\n decision.approval.connectionId !== source.approval.preview.connectionId ||\n decision.approval.providerId !== source.approval.preview.provider ||\n // Hub consumes the approval when it issues the one-time capability.\n decision.approval.status !== \"consumed\" ||\n !decision.capabilityToken?.token\n ) {\n throw new Error(\n \"The approval did not return a matching one-time capability. Reload knowledge.\",\n );\n }\n approvalToken = decision.capabilityToken.token;\n return await port.refresh(source.id, {\n requestId: source.refreshRequestId,\n approvalToken,\n });\n } finally {\n // Never put capability tokens in component state, storage, URLs, or logs.\n approvalToken = undefined;\n }\n}\n","import { HubTransport } from \"./client.js\";\nimport { jsonInit } from \"./json-init.js\";\nimport type { HubLineOwnerSendInput, HubLineSendReceipt } from \"./lines.js\";\n\nexport type HubOwnerAskSendInput = Pick<\n HubLineOwnerSendInput,\n \"text\" | \"image\" | \"reply\" | \"idempotencyKey\"\n>;\n\nexport interface HubLineGrantBinding {\n workspaceId: string;\n workspaceAgentId: string;\n}\n\nexport interface HubLineGrant {\n id: string;\n /** Canonical Hub identity that consented this grant; distinct from the app-local owner. */\n ownerUserId?: string;\n purpose?: \"agent\" | \"owner_asks\";\n lineId: string;\n connectionId: string;\n workspaceId: string;\n workspaceAgentId: string;\n agentProfileVersion: string;\n backendSha256: string;\n attachmentId: string | null;\n intakeMode: \"guests\" | \"owner_only\";\n /** \"application\" grants select the app's callback for the owner member instead of attaching. */\n execution: \"backend\" | \"application\" | \"none\";\n applicationOrigin: string | null;\n priorAttachmentId: string | null;\n priorAttachmentActive: boolean;\n monthlyBudgetUsd: number;\n monthlySpentUsd: number;\n}\n\nexport interface HubLineGrantLine {\n id: string;\n connectionId: string;\n transport: \"sms\" | \"imessage\" | \"whatsapp\";\n status: string;\n address: string | null;\n attachment: { id: string; sandboxId: string; status: string } | null;\n}\n\nexport interface HubLineGrantAttachInput {\n workspaceAgentId: string;\n agentProfileVersion: string;\n renewalCode?: string;\n respond: { kind: \"agent\"; backend: Record<string, unknown> };\n}\n\nexport interface HubLineGrantAttachment {\n id: string;\n sandboxId: string;\n status: string;\n members?: Array<{ id: string; role: string }>;\n}\n\nexport interface HubLineGrantThread {\n id: string;\n lineId: string;\n attachmentId: string;\n}\n\nexport interface HubLineGrantMessage {\n id: string;\n threadId: string;\n direction: string;\n text: string | null;\n}\n\n/** The consented owner member on the owner's current attachment. */\nexport interface HubLineGrantMemberApplication {\n lineId: string;\n attachmentId: string;\n memberId: string;\n subjectId: string;\n /** Pass back unchanged as `expectedMemberRevision`. */\n memberRevision: string;\n /** False until the owner has texted the line and Hub recorded consent. */\n eligible: boolean;\n /** Hub-derived; every callback for this grant carries it. */\n binding: string;\n selection: {\n id: string;\n /** True when this grant's callback is the selected application. */\n own: boolean;\n /**\n * Hub's name for the app that answers the owner's texts, shown to the\n * owner in every app they consented on this line: a registered app's\n * name, or \"Another app\" when Hub has none. Absent before Platform named\n * selection holders.\n */\n app?: { name: string };\n /** When the current app started answering. */\n selectedAt?: string;\n /** This app's own callback; never returned for another app. */\n url?: string;\n createdAt?: string;\n } | null;\n}\n\nexport interface HubLineGrantSelectApplicationInput {\n /** The current selection exactly as read. */\n expectedSelectionId: string | null;\n /**\n * Set only when the owner chose to move their texts here from the app shown\n * in `selection.app`: that selection's id, also passed as\n * `expectedSelectionId`. Without it Hub never replaces another app.\n */\n replaceSelectionId?: string;\n expectedMemberRevision: string;\n /** HTTPS callback on the owner-approved application origin. */\n url: string;\n /** Bearer Hub sends to the callback; 32-512 printable ASCII characters. */\n secret: string;\n}\n\nexport interface HubLineGrantApplicationSelection {\n id: string;\n lineId: string;\n attachmentId: string;\n memberId: string;\n subjectId: string;\n status: string;\n application: { url: string; binding: string };\n createdAt: string;\n revokedAt: string | null;\n}\n\nfunction path(binding: HubLineGrantBinding, suffix = \"\"): string {\n if (!binding.workspaceId || !binding.workspaceAgentId)\n throw new Error(\"A workspace and agent are required for a line grant\");\n const query = new URLSearchParams({\n workspaceId: binding.workspaceId,\n workspaceAgentId: binding.workspaceAgentId,\n });\n return `/v1/hub/line-grants/current${suffix}?${query}`;\n}\n\n/** Every call uses HubTransport's authHeaders, so one-use app broker tokens can be minted per request. */\nexport class HubLineGrantsClient extends HubTransport {\n current(binding: HubLineGrantBinding): Promise<HubLineGrant> {\n return this.request(path(binding), { method: \"GET\" });\n }\n\n async line(binding: HubLineGrantBinding): Promise<HubLineGrantLine> {\n return (\n await this.request<{ line: HubLineGrantLine }>(path(binding, \"/line\"), {\n method: \"GET\",\n })\n ).line;\n }\n\n async attach(\n binding: HubLineGrantBinding,\n input: HubLineGrantAttachInput,\n ): Promise<HubLineGrantAttachment> {\n return (\n await this.request<{ attachment: HubLineGrantAttachment }>(\n path(binding, \"/attachment\"),\n jsonInit(\"PUT\", input),\n )\n ).attachment;\n }\n\n detach(binding: HubLineGrantBinding): Promise<{ detached: boolean }> {\n return this.request(path(binding, \"/attachment\"), { method: \"DELETE\" });\n }\n\n async threads(binding: HubLineGrantBinding): Promise<HubLineGrantThread[]> {\n return (\n await this.request<{ threads: HubLineGrantThread[] }>(\n path(binding, \"/threads\"),\n { method: \"GET\" },\n )\n ).threads;\n }\n\n async messages(\n binding: HubLineGrantBinding,\n threadId: string,\n ): Promise<HubLineGrantMessage[]> {\n if (!threadId || threadId.length > 256)\n throw new Error(\"A bounded line thread id is required\");\n return (\n await this.request<{ messages: HubLineGrantMessage[] }>(\n path(binding, `/threads/${encodeURIComponent(threadId)}/messages`),\n { method: \"GET\" },\n )\n ).messages;\n }\n\n memberApplication(\n binding: HubLineGrantBinding,\n ): Promise<HubLineGrantMemberApplication> {\n return this.request(path(binding, \"/member-application\"), {\n method: \"GET\",\n });\n }\n\n /**\n * Route the owner's texts to this app's callback; the incumbent attachment\n * stays. `replaceSelectionId` moves them from another app on the owner's\n * explicit choice.\n */\n async selectApplication(\n binding: HubLineGrantBinding,\n input: HubLineGrantSelectApplicationInput,\n ): Promise<HubLineGrantApplicationSelection> {\n return (\n await this.request<{ selection: HubLineGrantApplicationSelection }>(\n path(binding, \"/member-application\"),\n jsonInit(\"PUT\", input),\n )\n ).selection;\n }\n\n /** Return the owner's texts to the incumbent attachment. */\n async clearApplication(\n binding: HubLineGrantBinding,\n input: { expectedSelectionId: string; expectedMemberRevision: string },\n ): Promise<void> {\n await this.request(\n path(binding, \"/member-application\"),\n jsonInit(\"DELETE\", input),\n );\n }\n\n /** Ask only the verified owner member named by this owner_asks grant. */\n send(\n binding: HubLineGrantBinding,\n input: HubOwnerAskSendInput,\n ): Promise<HubLineSendReceipt> {\n return this.request(path(binding, \"/send\"), jsonInit(\"POST\", input));\n }\n\n /** Only a send created by this same grant is visible. */\n outbox(\n binding: HubLineGrantBinding,\n messageId: string,\n ): Promise<HubLineSendReceipt> {\n if (!/^lmsg_[A-Za-z0-9_-]+$/.test(messageId))\n throw new Error(\"A line message id is required\");\n return this.request(\n path(binding, `/outbox/${encodeURIComponent(messageId)}`),\n { method: \"GET\" },\n );\n }\n\n revoke(\n binding: HubLineGrantBinding,\n expectedGrantId?: string,\n ): Promise<{ revoked: boolean }> {\n const target = path(binding);\n return this.request(\n expectedGrantId === undefined\n ? target\n : `${target}&expectedGrantId=${encodeURIComponent(expectedGrantId)}`,\n { method: \"DELETE\" },\n );\n }\n}\n","import type { HubWorkflowGraphSpec } from \"./workflow-graph-types.js\";\nimport type { HubWorkflowRunSummary } from \"./workflow-run-types.js\";\n\nexport type {\n HubWorkflowCondition,\n HubWorkflowConditionOp,\n HubWorkflowConditionScalar,\n HubWorkflowGraphEdge,\n HubWorkflowGraphJoinRule,\n HubWorkflowGraphNode,\n HubWorkflowGraphSpec,\n} from \"./workflow-graph-types.js\";\n\n// ---------- Workflows ----------\n//\n// The workflow resource on the platform (`/v1/workflows`). YAML is the\n// durable representation; the platform compiles it to the engine's run model.\n// Reachable from inside a sandbox via the same client (TANGLE_API_KEY auth).\n\nexport interface HubWorkflowAction {\n kind: string;\n config: Record<string, unknown>;\n}\n\n/** Inbound-event matcher for a `provider_event` trigger. */\nexport interface HubWorkflowEventFilter {\n event: string;\n action?: string;\n repo?: string;\n}\n\nexport interface HubWorkflowTrigger {\n id: string;\n kind: \"provider_event\" | \"schedule\" | \"webhook\";\n enabled: boolean;\n provider: string | null;\n connectionId: string | null;\n eventFilter: HubWorkflowEventFilter | null;\n cron: string | null;\n timezone: string | null;\n nextFireAt: string | null;\n lastFiredAt: string | null;\n /** Schedule only: the ISO-8601 instant it stops firing; null when unbounded. */\n until: string | null;\n}\n\nexport interface HubWorkflowValidationError {\n path: string;\n message: string;\n /** Machine-readable reason where one is defined, e.g. `schedule_outlives_grant`. */\n code?: string;\n}\n\nexport interface HubWorkflow {\n id: string;\n name: string;\n description: string | null;\n enabled: boolean;\n /** Compiled engine actions (connection refs resolved to ids). */\n actions: HubWorkflowAction[];\n /** The durable YAML this workflow compiled from. */\n definitionYaml: string | null;\n validationErrors: HubWorkflowValidationError[];\n /** Current head revision; null on pre-versioning rows until their next\n * update (they have no revision history to point at yet). */\n headRev?: number | null;\n /** Present on get/create/update detail responses; omitted on list. */\n triggers?: HubWorkflowTrigger[];\n /** Present on the detail response: the trigger fields a manual run must supply,\n * derived from the definition's `${trigger.*}` references (empty when none). */\n manualRunInputs?: HubManualRunInput[];\n /** Present on the get-detail response: the compiled graph topology (null\n * when the definition has no graph topology, or is chain-equivalent —\n * the legacy linear semantics). Omitted on the list projection. */\n graph?: HubWorkflowGraphSpec | null;\n /** Create response only, when the definition has a webhook trigger: the\n * hook delivery URL, plus the plaintext bearer token exactly once at mint\n * (absent when an active token already exists — rotate to re-reveal). */\n hook?: HubWorkflowHookEndpoint;\n /** Present on the list response (latest run, or null if never run); omitted on detail. */\n lastRun?: HubWorkflowRunSummary | null;\n createdAt: string;\n updatedAt: string;\n}\n\n/** A trigger field a manual \"Run now\" must supply (derived from the\n * definition's `${trigger.*}` references). `required` is false only when every\n * read is wrapped in a `default(...)`. */\nexport interface HubManualRunInput {\n path: string;\n required: boolean;\n}\n\nexport type HubWorkflowValidateResponse =\n | { valid: true; name: string; actionCount: number; triggerCount: number }\n | { valid: false; errors: HubWorkflowValidationError[] };\n\n/** JSON Schema for the YAML workflow definition. */\nexport type HubWorkflowSchemaResponse = Record<string, unknown>;\n\n// ---------- Workflow definition revisions ----------\n//\n// The append-only history of a workflow's YAML (`GET :id/revisions`,\n// `POST :id/rollback`). Every create/update/rollback appends one rev; rollback\n// restores a prior rev's YAML as a NEW head — history is never rewritten.\n// `GET :id/revisions` lists APPLIED revs only (the history); proposed and\n// rejected revs live on the proposals surface below.\n\nexport type HubWorkflowRevisionSource = \"user\" | \"copilot\" | \"intelligence\";\n\n/** Lifecycle state of a revision. The head rev only ever points at an\n * `applied` revision; `proposed` is a service-token-authored suggestion\n * awaiting an owner decision; `rejected` is a declined proposal (kept for\n * audit). */\nexport type HubWorkflowRevisionStatus = \"applied\" | \"proposed\" | \"rejected\";\n\n/** Revision list projection — attribution + note + status, WITHOUT the YAML body. */\nexport interface HubWorkflowRevisionMeta {\n rev: number;\n createdAt: string;\n createdBy: string;\n source: HubWorkflowRevisionSource;\n note: string | null;\n status: HubWorkflowRevisionStatus;\n}\n\n/** One revision WITH its YAML (and the actions compiled alongside it). */\nexport interface HubWorkflowRevision extends HubWorkflowRevisionMeta {\n yaml: string;\n actions: HubWorkflowAction[];\n}\n\n/** A proposal (proposed or rejected revision) WITH its evidence — what the\n * proposer derived the suggestion from (e.g. health stats), plus\n * `rejectionReason` once declined. */\nexport interface HubWorkflowProposal extends HubWorkflowRevisionMeta {\n evidence: unknown;\n}\n\n// ---------- Workflow templates ----------\n//\n// The curated starter gallery (`GET /v1/workflows/templates`) and one-click\n// instantiate. Static catalog data — no per-user fields.\n\nexport type HubWorkflowTemplateCategory =\n | \"Code Review\"\n | \"Triage\"\n | \"Reports\"\n | \"Multi-agent\";\n\n/** One author-time literal a gallery starter needs the user to supply. Every\n * declared parameter is required; `example` is a valid value that seeds the\n * form placeholder. */\nexport interface HubWorkflowTemplateParameter {\n key: string;\n label: string;\n type: \"string\" | \"integer\";\n help?: string;\n example: string;\n default?: string;\n defaultFrom?: \"native_evaluation\";\n required?: boolean;\n advanced?: boolean;\n}\n\nexport interface HubWorkflowTemplate {\n id: string;\n name: string;\n summary: string;\n category: HubWorkflowTemplateCategory;\n icon: string;\n requiresSandbox: boolean;\n /** Providers the template references (a \"uses GitHub\" hint), sorted. */\n providers: string[];\n parameters: HubWorkflowTemplateParameter[];\n /** The template with parameter tokens rendered to their example values. */\n previewYaml: string;\n setupNotes?: string[];\n}\n\n/** A connection a definition needs, with where to connect it. */\nexport interface HubWorkflowConnectionRequirement {\n provider: string;\n connected: boolean;\n kind: \"integration\" | \"github_app\";\n /** Where to satisfy the requirement (App install flow for `github_app`,\n * integrations page otherwise); null when no target exists. */\n connectUrl: string | null;\n /** Another requirement in the same list that must be satisfied first — a\n * `github_app` install is claimed through the `github` connection, so it is\n * refused while that connection is missing. Null when nothing blocks this\n * requirement. The list is ordered prerequisite-first. */\n prerequisite: { provider: string; kind: \"integration\" | \"github_app\" } | null;\n}\n\n/** Stable identity shared by workflow connection requirements and prerequisite references. */\nexport function workflowConnectionRequirementKey(\n ref: Readonly<Pick<HubWorkflowConnectionRequirement, \"provider\" | \"kind\">>,\n): string {\n return `${ref.kind}:${ref.provider}`;\n}\n\n/**\n * Result of instantiating a template. `created: false` is NOT an error — an\n * actionable continuation listing the connections to satisfy before retrying\n * the same call. Invalid parameters or a compile failure throw `HubSdkError`\n * (400) instead, matching the create route's contract.\n */\nexport type HubWorkflowTemplateInstantiateResult =\n | { created: true; workflow: HubWorkflow }\n | {\n created: false;\n connectionRequirements: HubWorkflowConnectionRequirement[];\n };\n\n// ---------- Webhook hook tokens ----------\n\n/** A workflow's webhook delivery endpoint. `token` is the plaintext bearer —\n * revealed only at mint/rotate time (only the hash is stored). */\nexport interface HubWorkflowHookEndpoint {\n url: string;\n token?: string;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/broker.ts","../src/event-delivery.ts","../src/line-grants.ts","../src/workflow-definition-types.ts"],"sourcesContent":["import { HubClient, HubSdkError } from \"./client.js\";\n\nexport interface HubAppBrokerOptions {\n baseUrl: string;\n clientId: string;\n clientSecret: string;\n fetch?: typeof fetch;\n}\nexport interface HubAppBrokerToken {\n accessToken: string;\n expiresIn: number;\n scopes: string[];\n}\nexport interface HubAppBrokerExchange extends HubAppBrokerToken {\n grantId: string;\n connectionId: string;\n}\n/** Server-only client. App secrets never become a user's general API key.\n * Every invocation mints and consumes one short-lived, exact-grant token. */\nexport class HubAppBrokerClient {\n private readonly baseUrl: string;\n private readonly fetcher: typeof fetch;\n constructor(private readonly options: HubAppBrokerOptions) {\n this.baseUrl = options.baseUrl.replace(/\\/+$/, \"\");\n this.fetcher = options.fetch ?? fetch.bind(globalThis);\n }\n private async token(\n path: string,\n body: Record<string, unknown>,\n ): Promise<Record<string, unknown>> {\n const response = await this.fetcher(`${this.baseUrl}${path}`, {\n method: \"POST\",\n // Workers support manual redirects; the non-ok check below rejects every 3xx.\n redirect: \"manual\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n ...body,\n client_id: this.options.clientId,\n client_secret: this.options.clientSecret,\n }),\n });\n const result: unknown = await response.json().catch(() => null);\n if (\n !response.ok ||\n !result ||\n typeof result !== \"object\" ||\n Array.isArray(result)\n ) {\n throw new HubSdkError(\n {\n code: \"HUB_CONFIG_MISSING\",\n message: \"Application authorization is unavailable\",\n },\n { status: response.status },\n );\n }\n return result as Record<string, unknown>;\n }\n private readToken(body: Record<string, unknown>): HubAppBrokerToken {\n if (\n typeof body.access_token !== \"string\" ||\n !body.access_token.startsWith(\"sk-tan-broker-\") ||\n body.token_type !== \"Bearer\" ||\n typeof body.expires_in !== \"number\" ||\n !Number.isSafeInteger(body.expires_in) ||\n body.expires_in <= 0 ||\n typeof body.scope !== \"string\"\n )\n throw new HubSdkError({\n code: \"HUB_CONFIG_MISSING\",\n message: \"Invalid application authorization response\",\n });\n return {\n accessToken: body.access_token,\n expiresIn: body.expires_in,\n scopes: body.scope.split(\" \").filter(Boolean),\n };\n }\n async exchange(input: {\n code: string;\n redirectUri: string;\n connectionId?: string;\n }): Promise<HubAppBrokerExchange> {\n const body = await this.token(\"/v1/apps/oauth/token\", {\n grant_type: \"authorization_code\",\n code: input.code,\n redirect_uri: input.redirectUri,\n ...(input.connectionId === undefined\n ? {}\n : { connection_id: input.connectionId }),\n ttl_seconds: 60,\n });\n const token = this.readToken(body);\n if (\n typeof body.grant_id !== \"string\" ||\n !body.grant_id ||\n typeof body.connection_id !== \"string\" ||\n !body.connection_id.trim() ||\n (input.connectionId !== undefined &&\n body.connection_id !== input.connectionId)\n )\n throw new HubSdkError({\n code: \"HUB_CONFIG_MISSING\",\n message: \"Application connection did not match consent\",\n });\n return {\n ...token,\n grantId: body.grant_id,\n connectionId: body.connection_id,\n };\n }\n async mint(grantId: string): Promise<HubAppBrokerToken> {\n if (!/^[A-Za-z0-9_-]{1,256}$/.test(grantId))\n throw new Error(\"Invalid application grant\");\n return this.readToken(\n await this.token(\n `/v1/apps/grants/${encodeURIComponent(grantId)}/mint-broker-token`,\n { grant_id: grantId, ttl_seconds: 60 },\n ),\n );\n }\n async invoke<T = unknown>(\n grantId: string,\n connectionId: string,\n actionPath: string,\n input: Record<string, unknown>,\n ) {\n const token = await this.mint(grantId);\n if (!token.scopes.includes(actionPath))\n throw new HubSdkError({\n code: \"HUB_POLICY_DENIED\",\n message: \"This action was not granted to the application\",\n });\n return new HubClient({\n baseUrl: this.baseUrl,\n apiKey: token.accessToken,\n fetch: this.fetcher,\n }).tools.invoke<T>(actionPath, input, { connectionId });\n }\n}\n","import type { HubEventDelivery } from \"./types.js\";\n\nconst DEFAULT_TOLERANCE_SECONDS = 300;\nconst MAX_TOLERANCE_SECONDS = 3_600;\nconst MAX_SIGNATURE_HEADER_LENGTH = 1_024;\nconst MAX_SIGNATURES = 5;\nconst SHA256_HEX_LENGTH = 64;\nconst MIN_CALLBACK_SECRET_BYTES = 32;\nconst MAX_SECRET_SCOPE_COMPONENT_BYTES = 512;\nconst DEFAULT_MAX_CALLBACK_BODY_BYTES = 5 * 1024 * 1024 + 64 * 1024;\nconst MAX_CALLBACK_BODY_BYTES = 10 * 1024 * 1024;\n\nexport type HubEventSignatureFailure =\n | \"missing\"\n | \"malformed\"\n | \"stale\"\n | \"mismatch\";\n\nexport type HubEventSignatureResult =\n | { valid: true; timestamp: number }\n | { valid: false; reason: HubEventSignatureFailure };\n\nexport interface VerifyHubEventSignatureInput {\n body: string;\n signature: string | null | undefined;\n secret: string;\n toleranceSeconds?: number;\n now?: number | Date;\n}\n\nexport interface DeriveHubEventCallbackSecretInput {\n /** App-level secret stored only in the product's server environment. */\n rootSecret: string;\n /** Stable product slug, such as `relationships`. */\n productId: string;\n /** Stable Tangle owner id; user and team ids are both valid. */\n ownerId: string;\n /** Stable product-local binding id used in the callback route. */\n bindingId: string;\n}\n\nexport type HubEventRequestFailureCode =\n | \"METHOD_NOT_ALLOWED\"\n | \"UNEXPECTED_EVENT\"\n | \"UNSUPPORTED_MEDIA_TYPE\"\n | \"PAYLOAD_TOO_LARGE\"\n | \"INVALID_SIGNATURE\"\n | \"INVALID_DELIVERY\"\n | \"DELIVERY_ID_MISMATCH\";\n\nexport type HubEventRequestResult =\n | { ok: true; delivery: HubEventDelivery }\n | {\n ok: false;\n code: HubEventRequestFailureCode;\n response: Response;\n };\n\nexport interface AuthenticateHubEventRequestInput {\n request: Request;\n /** The per-binding secret supplied when the subscription was created. */\n secret: string;\n /** Defaults to 5 MiB plus envelope overhead; hard-capped at 10 MiB. */\n maxBodyBytes?: number;\n toleranceSeconds?: number;\n now?: number | Date;\n}\n\nexport class HubEventDeliveryError extends Error {\n readonly code:\n | \"INVALID_JSON\"\n | \"INVALID_PAYLOAD\"\n | \"CRYPTO_UNAVAILABLE\"\n | \"INVALID_TOLERANCE\"\n | \"INVALID_CALLBACK_SECRET\"\n | \"INVALID_SECRET_SCOPE\"\n | \"INVALID_BODY_LIMIT\";\n\n constructor(code: HubEventDeliveryError[\"code\"], message: string) {\n super(message);\n this.name = \"HubEventDeliveryError\";\n this.code = code;\n }\n}\n\n/**\n * Derive one callback secret per product binding from a single server-held root\n * secret. Products persist only the binding id; the derived secret can be\n * reproduced for callback authentication without another secret table.\n */\nexport async function deriveHubEventCallbackSecret(\n input: DeriveHubEventCallbackSecretInput,\n): Promise<string> {\n const encoder = new TextEncoder();\n if (encoder.encode(input.rootSecret).byteLength < MIN_CALLBACK_SECRET_BYTES) {\n throw new HubEventDeliveryError(\n \"INVALID_CALLBACK_SECRET\",\n `Hub event root secret must contain at least ${MIN_CALLBACK_SECRET_BYTES} UTF-8 bytes`,\n );\n }\n for (const [name, value] of [\n [\"productId\", input.productId],\n [\"ownerId\", input.ownerId],\n [\"bindingId\", input.bindingId],\n ] as const) {\n const bytes = encoder.encode(value).byteLength;\n if (bytes === 0 || bytes > MAX_SECRET_SCOPE_COMPONENT_BYTES) {\n throw new HubEventDeliveryError(\n \"INVALID_SECRET_SCOPE\",\n `${name} must contain 1-${MAX_SECRET_SCOPE_COMPONENT_BYTES} UTF-8 bytes`,\n );\n }\n }\n const subtle = requireSubtleCrypto();\n const key = await subtle.importKey(\n \"raw\",\n encoder.encode(input.rootSecret),\n { name: \"HMAC\", hash: \"SHA-256\" },\n false,\n [\"sign\"],\n );\n const scope = JSON.stringify([\n \"tangle-hub-event-callback\",\n 1,\n input.productId,\n input.ownerId,\n input.bindingId,\n ]);\n const signature = new Uint8Array(\n await subtle.sign(\"HMAC\", key, encoder.encode(scope)),\n );\n return bytesToBase64Url(signature);\n}\n\n/**\n * Authenticate and parse the platform's callback as one operation. The exact\n * raw body is read once with a size limit before its signature is checked.\n *\n * A successful callback can be retried. Use `delivery.runId` as the durable\n * idempotency key before starting product work.\n */\nexport async function authenticateHubEventRequest(\n input: AuthenticateHubEventRequestInput,\n): Promise<HubEventRequestResult> {\n if (input.request.method !== \"POST\") {\n return requestFailure(\"METHOD_NOT_ALLOWED\", 405, \"method_not_allowed\", {\n Allow: \"POST\",\n });\n }\n if (input.request.headers.get(\"x-tangle-event\") !== \"hub.event\") {\n return requestFailure(\"UNEXPECTED_EVENT\", 400, \"invalid_hub_event\");\n }\n const contentType = input.request.headers.get(\"content-type\") ?? \"\";\n if (!/^application\\/json(?:\\s*;|$)/i.test(contentType)) {\n return requestFailure(\n \"UNSUPPORTED_MEDIA_TYPE\",\n 415,\n \"unsupported_media_type\",\n );\n }\n const maxBodyBytes = input.maxBodyBytes ?? DEFAULT_MAX_CALLBACK_BODY_BYTES;\n if (\n !Number.isSafeInteger(maxBodyBytes) ||\n maxBodyBytes < 1 ||\n maxBodyBytes > MAX_CALLBACK_BODY_BYTES\n ) {\n throw new HubEventDeliveryError(\n \"INVALID_BODY_LIMIT\",\n `Hub event body limit must be an integer from 1 to ${MAX_CALLBACK_BODY_BYTES} bytes`,\n );\n }\n if (\n new TextEncoder().encode(input.secret).byteLength <\n MIN_CALLBACK_SECRET_BYTES\n ) {\n throw new HubEventDeliveryError(\n \"INVALID_CALLBACK_SECRET\",\n `Hub event callback secret must contain at least ${MIN_CALLBACK_SECRET_BYTES} UTF-8 bytes`,\n );\n }\n const declaredLength = input.request.headers.get(\"content-length\");\n if (\n declaredLength !== null &&\n /^\\d+$/.test(declaredLength) &&\n Number(declaredLength) > maxBodyBytes\n ) {\n return requestFailure(\"PAYLOAD_TOO_LARGE\", 413, \"payload_too_large\");\n }\n const body = await readRequestBody(input.request, maxBodyBytes);\n if (body === null) {\n return requestFailure(\"PAYLOAD_TOO_LARGE\", 413, \"payload_too_large\");\n }\n const signature = await verifyHubEventSignature({\n body,\n signature: input.request.headers.get(\"x-tangle-signature\"),\n secret: input.secret,\n ...(input.toleranceSeconds !== undefined\n ? { toleranceSeconds: input.toleranceSeconds }\n : {}),\n ...(input.now !== undefined ? { now: input.now } : {}),\n });\n if (!signature.valid) {\n return requestFailure(\"INVALID_SIGNATURE\", 401, \"invalid_signature\");\n }\n let delivery: HubEventDelivery;\n try {\n delivery = parseHubEventDelivery(body);\n } catch (error) {\n if (error instanceof HubEventDeliveryError) {\n return requestFailure(\"INVALID_DELIVERY\", 400, \"invalid_delivery\");\n }\n throw error;\n }\n if (input.request.headers.get(\"x-tangle-delivery-id\") !== delivery.runId) {\n return requestFailure(\"DELIVERY_ID_MISMATCH\", 400, \"invalid_delivery\");\n }\n return { ok: true, delivery };\n}\n\n/**\n * Authenticate a Hub event callback against its exact raw request body.\n * The timestamp window rejects captured-request replay, and byte-wise\n * comparison avoids secret-dependent string comparison behavior.\n */\nexport async function verifyHubEventSignature(\n input: VerifyHubEventSignatureInput,\n): Promise<HubEventSignatureResult> {\n if (!input.signature) return { valid: false, reason: \"missing\" };\n if (input.signature.length > MAX_SIGNATURE_HEADER_LENGTH) {\n return { valid: false, reason: \"malformed\" };\n }\n const parsed = parseSignatureHeader(input.signature);\n if (!parsed) return { valid: false, reason: \"malformed\" };\n\n const tolerance = input.toleranceSeconds ?? DEFAULT_TOLERANCE_SECONDS;\n if (\n !Number.isFinite(tolerance) ||\n !Number.isInteger(tolerance) ||\n tolerance < 0 ||\n tolerance > MAX_TOLERANCE_SECONDS\n ) {\n throw new HubEventDeliveryError(\n \"INVALID_TOLERANCE\",\n `Signature tolerance must be an integer from 0 to ${MAX_TOLERANCE_SECONDS} seconds`,\n );\n }\n const nowMs =\n input.now instanceof Date\n ? input.now.getTime()\n : typeof input.now === \"number\"\n ? input.now\n : Date.now();\n if (\n !Number.isFinite(nowMs) ||\n Math.abs(Math.floor(nowMs / 1_000) - parsed.timestamp) > tolerance\n ) {\n return { valid: false, reason: \"stale\" };\n }\n\n const subtle = requireSubtleCrypto();\n const encoder = new TextEncoder();\n const key = await subtle.importKey(\n \"raw\",\n encoder.encode(input.secret),\n { name: \"HMAC\", hash: \"SHA-256\" },\n false,\n [\"sign\"],\n );\n const expected = new Uint8Array(\n await subtle.sign(\n \"HMAC\",\n key,\n encoder.encode(`${parsed.timestamp}.${input.body}`),\n ),\n );\n let matched = 0;\n for (const signature of parsed.signatures) {\n matched |= constantTimeEqual(expected, signature);\n }\n return matched === 1\n ? { valid: true, timestamp: parsed.timestamp }\n : { valid: false, reason: \"mismatch\" };\n}\n\nexport function parseHubEventDelivery(\n input: string | unknown,\n): HubEventDelivery {\n let value: unknown = input;\n if (typeof input === \"string\") {\n try {\n value = JSON.parse(input);\n } catch {\n throw new HubEventDeliveryError(\n \"INVALID_JSON\",\n \"Hub event callback body is not valid JSON\",\n );\n }\n }\n if (!isRecord(value)) return invalidDelivery();\n const source = value.source;\n const providerEvent = value.providerEvent;\n if (\n !nonEmptyString(value.subscriptionId) ||\n !nonEmptyString(value.workflowId) ||\n !nonEmptyString(value.runId) ||\n !isRecord(source) ||\n (source.kind !== \"channel\" && source.kind !== \"connection\") ||\n !nonEmptyString(source.id) ||\n !nonEmptyString(source.provider) ||\n !nonEmptyString(source.event) ||\n !isRecord(providerEvent) ||\n !nonEmptyString(providerEvent.provider) ||\n !nonEmptyString(providerEvent.connectionId) ||\n !nonEmptyString(providerEvent.type) ||\n providerEvent.provider !== source.provider ||\n providerEvent.type !== source.event ||\n !Object.hasOwn(providerEvent, \"payload\") ||\n !optionalString(providerEvent.action) ||\n !optionalString(providerEvent.repo) ||\n !optionalString(providerEvent.deliveryId) ||\n !nonEmptyString(value.firedAt) ||\n !Number.isFinite(Date.parse(value.firedAt))\n ) {\n return invalidDelivery();\n }\n return {\n subscriptionId: value.subscriptionId,\n workflowId: value.workflowId,\n runId: value.runId,\n source: {\n kind: source.kind,\n id: source.id,\n provider: source.provider,\n event: source.event,\n },\n providerEvent: {\n provider: providerEvent.provider,\n connectionId: providerEvent.connectionId,\n type: providerEvent.type,\n ...(typeof providerEvent.action === \"string\"\n ? { action: providerEvent.action }\n : {}),\n ...(typeof providerEvent.repo === \"string\"\n ? { repo: providerEvent.repo }\n : {}),\n ...(typeof providerEvent.deliveryId === \"string\"\n ? { deliveryId: providerEvent.deliveryId }\n : {}),\n payload: providerEvent.payload,\n },\n firedAt: value.firedAt,\n };\n}\n\nfunction parseSignatureHeader(\n header: string,\n): { timestamp: number; signatures: Uint8Array[] } | null {\n let timestamp: number | undefined;\n const signatures: Uint8Array[] = [];\n for (const segment of header.split(\",\")) {\n const separator = segment.indexOf(\"=\");\n if (separator <= 0) return null;\n const key = segment.slice(0, separator).trim();\n const value = segment.slice(separator + 1).trim();\n if (key === \"t\") {\n if (timestamp !== undefined || !/^\\d+$/.test(value)) return null;\n const candidate = Number(value);\n if (!Number.isSafeInteger(candidate) || candidate <= 0) return null;\n timestamp = candidate;\n } else if (key === \"v1\") {\n if (\n signatures.length >= MAX_SIGNATURES ||\n value.length !== SHA256_HEX_LENGTH ||\n !/^[0-9a-f]+$/i.test(value)\n ) {\n return null;\n }\n signatures.push(hexToBytes(value));\n }\n }\n return timestamp !== undefined && signatures.length > 0\n ? { timestamp, signatures }\n : null;\n}\n\nfunction hexToBytes(hex: string): Uint8Array {\n const bytes = new Uint8Array(hex.length / 2);\n for (let index = 0; index < bytes.length; index += 1) {\n bytes[index] = Number.parseInt(hex.slice(index * 2, index * 2 + 2), 16);\n }\n return bytes;\n}\n\nfunction constantTimeEqual(expected: Uint8Array, actual: Uint8Array): number {\n let difference = expected.length ^ actual.length;\n const length = Math.max(expected.length, actual.length);\n for (let index = 0; index < length; index += 1) {\n difference |=\n (expected[index % expected.length] ?? 0) ^\n (actual[index % actual.length] ?? 0);\n }\n return difference === 0 ? 1 : 0;\n}\n\nfunction nonEmptyString(value: unknown): value is string {\n return typeof value === \"string\" && value.length > 0;\n}\n\nfunction optionalString(value: unknown): boolean {\n return value === undefined || nonEmptyString(value);\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction invalidDelivery(): never {\n throw new HubEventDeliveryError(\n \"INVALID_PAYLOAD\",\n \"Hub event callback body does not match the delivery contract\",\n );\n}\n\nfunction requireSubtleCrypto(): SubtleCrypto {\n const subtle = globalThis.crypto?.subtle;\n if (!subtle) {\n throw new HubEventDeliveryError(\n \"CRYPTO_UNAVAILABLE\",\n \"Web Crypto is required for Hub event callback authentication\",\n );\n }\n return subtle;\n}\n\nfunction bytesToBase64Url(bytes: Uint8Array): string {\n let binary = \"\";\n for (const byte of bytes) binary += String.fromCharCode(byte);\n return btoa(binary)\n .replace(/\\+/g, \"-\")\n .replace(/\\//g, \"_\")\n .replace(/=+$/g, \"\");\n}\n\nasync function readRequestBody(\n request: Request,\n maxBytes: number,\n): Promise<string | null> {\n const reader = request.body?.getReader();\n if (!reader) return \"\";\n const chunks: Uint8Array[] = [];\n let total = 0;\n try {\n while (true) {\n const next = await reader.read();\n if (next.done) break;\n total += next.value.byteLength;\n if (total > maxBytes) return null;\n chunks.push(next.value);\n }\n } finally {\n await reader.cancel().catch(() => {});\n }\n const body = new Uint8Array(total);\n let offset = 0;\n for (const chunk of chunks) {\n body.set(chunk, offset);\n offset += chunk.byteLength;\n }\n return new TextDecoder().decode(body);\n}\n\nfunction requestFailure(\n code: HubEventRequestFailureCode,\n status: number,\n error: string,\n headers?: HeadersInit,\n): Extract<HubEventRequestResult, { ok: false }> {\n return {\n ok: false,\n code,\n response: Response.json(\n { error },\n {\n status,\n headers: {\n \"cache-control\": \"no-store\",\n ...(headers ? Object.fromEntries(new Headers(headers)) : {}),\n },\n },\n ),\n };\n}\n","import { HubTransport } from \"./client.js\";\nimport { jsonInit } from \"./json-init.js\";\nimport type { HubLineOwnerSendInput, HubLineSendReceipt } from \"./lines.js\";\n\nexport type HubOwnerAskSendInput = Pick<\n HubLineOwnerSendInput,\n \"text\" | \"image\" | \"reply\" | \"idempotencyKey\"\n>;\n\nexport interface HubLineGrantBinding {\n workspaceId: string;\n workspaceAgentId: string;\n}\n\nexport interface HubLineGrant {\n id: string;\n /** Canonical Hub identity that consented this grant; distinct from the app-local owner. */\n ownerUserId?: string;\n purpose?: \"agent\" | \"owner_asks\";\n lineId: string;\n connectionId: string;\n workspaceId: string;\n workspaceAgentId: string;\n agentProfileVersion: string;\n backendSha256: string;\n attachmentId: string | null;\n intakeMode: \"guests\" | \"owner_only\";\n /** \"application\" grants select the app's callback for the owner member instead of attaching. */\n execution: \"backend\" | \"application\" | \"none\";\n applicationOrigin: string | null;\n priorAttachmentId: string | null;\n priorAttachmentActive: boolean;\n monthlyBudgetUsd: number;\n monthlySpentUsd: number;\n}\n\nexport interface HubLineGrantLine {\n id: string;\n connectionId: string;\n transport: \"sms\" | \"imessage\" | \"whatsapp\";\n status: string;\n address: string | null;\n attachment: { id: string; sandboxId: string; status: string } | null;\n}\n\nexport interface HubLineGrantAttachInput {\n workspaceAgentId: string;\n agentProfileVersion: string;\n renewalCode?: string;\n respond: { kind: \"agent\"; backend: Record<string, unknown> };\n}\n\nexport interface HubLineGrantAttachment {\n id: string;\n sandboxId: string;\n status: string;\n members?: Array<{ id: string; role: string }>;\n}\n\nexport interface HubLineGrantThread {\n id: string;\n lineId: string;\n attachmentId: string;\n}\n\nexport interface HubLineGrantMessage {\n id: string;\n threadId: string;\n direction: string;\n text: string | null;\n}\n\n/** The consented owner member on the owner's current attachment. */\nexport interface HubLineGrantMemberApplication {\n lineId: string;\n attachmentId: string;\n memberId: string;\n subjectId: string;\n /** Pass back unchanged as `expectedMemberRevision`. */\n memberRevision: string;\n /** False until the owner has texted the line and Hub recorded consent. */\n eligible: boolean;\n /** Hub-derived; every callback for this grant carries it. */\n binding: string;\n selection: {\n id: string;\n /** True when this grant's callback is the selected application. */\n own: boolean;\n /**\n * Hub's name for the app that answers the owner's texts, shown to the\n * owner in every app they consented on this line: a registered app's\n * name, or \"Another app\" when Hub has none. Absent before Platform named\n * selection holders.\n */\n app?: { name: string };\n /** When the current app started answering. */\n selectedAt?: string;\n /** This app's own callback; never returned for another app. */\n url?: string;\n createdAt?: string;\n } | null;\n}\n\nexport interface HubLineGrantSelectApplicationInput {\n /** The current selection exactly as read. */\n expectedSelectionId: string | null;\n /**\n * Set only when the owner chose to move their texts here from the app shown\n * in `selection.app`: that selection's id, also passed as\n * `expectedSelectionId`. Without it Hub never replaces another app.\n */\n replaceSelectionId?: string;\n expectedMemberRevision: string;\n /** HTTPS callback on the owner-approved application origin. */\n url: string;\n /** Bearer Hub sends to the callback; 32-512 printable ASCII characters. */\n secret: string;\n}\n\nexport interface HubLineGrantApplicationSelection {\n id: string;\n lineId: string;\n attachmentId: string;\n memberId: string;\n subjectId: string;\n status: string;\n application: { url: string; binding: string };\n createdAt: string;\n revokedAt: string | null;\n}\n\nfunction path(binding: HubLineGrantBinding, suffix = \"\"): string {\n if (!binding.workspaceId || !binding.workspaceAgentId)\n throw new Error(\"A workspace and agent are required for a line grant\");\n const query = new URLSearchParams({\n workspaceId: binding.workspaceId,\n workspaceAgentId: binding.workspaceAgentId,\n });\n return `/v1/hub/line-grants/current${suffix}?${query}`;\n}\n\n/** Every call uses HubTransport's authHeaders, so one-use app broker tokens can be minted per request. */\nexport class HubLineGrantsClient extends HubTransport {\n current(binding: HubLineGrantBinding): Promise<HubLineGrant> {\n return this.request(path(binding), { method: \"GET\" });\n }\n\n async line(binding: HubLineGrantBinding): Promise<HubLineGrantLine> {\n return (\n await this.request<{ line: HubLineGrantLine }>(path(binding, \"/line\"), {\n method: \"GET\",\n })\n ).line;\n }\n\n async attach(\n binding: HubLineGrantBinding,\n input: HubLineGrantAttachInput,\n ): Promise<HubLineGrantAttachment> {\n return (\n await this.request<{ attachment: HubLineGrantAttachment }>(\n path(binding, \"/attachment\"),\n jsonInit(\"PUT\", input),\n )\n ).attachment;\n }\n\n detach(binding: HubLineGrantBinding): Promise<{ detached: boolean }> {\n return this.request(path(binding, \"/attachment\"), { method: \"DELETE\" });\n }\n\n async threads(binding: HubLineGrantBinding): Promise<HubLineGrantThread[]> {\n return (\n await this.request<{ threads: HubLineGrantThread[] }>(\n path(binding, \"/threads\"),\n { method: \"GET\" },\n )\n ).threads;\n }\n\n async messages(\n binding: HubLineGrantBinding,\n threadId: string,\n ): Promise<HubLineGrantMessage[]> {\n if (!threadId || threadId.length > 256)\n throw new Error(\"A bounded line thread id is required\");\n return (\n await this.request<{ messages: HubLineGrantMessage[] }>(\n path(binding, `/threads/${encodeURIComponent(threadId)}/messages`),\n { method: \"GET\" },\n )\n ).messages;\n }\n\n memberApplication(\n binding: HubLineGrantBinding,\n ): Promise<HubLineGrantMemberApplication> {\n return this.request(path(binding, \"/member-application\"), {\n method: \"GET\",\n });\n }\n\n /**\n * Route the owner's texts to this app's callback; the incumbent attachment\n * stays. `replaceSelectionId` moves them from another app on the owner's\n * explicit choice.\n */\n async selectApplication(\n binding: HubLineGrantBinding,\n input: HubLineGrantSelectApplicationInput,\n ): Promise<HubLineGrantApplicationSelection> {\n return (\n await this.request<{ selection: HubLineGrantApplicationSelection }>(\n path(binding, \"/member-application\"),\n jsonInit(\"PUT\", input),\n )\n ).selection;\n }\n\n /** Return the owner's texts to the incumbent attachment. */\n async clearApplication(\n binding: HubLineGrantBinding,\n input: { expectedSelectionId: string; expectedMemberRevision: string },\n ): Promise<void> {\n await this.request(\n path(binding, \"/member-application\"),\n jsonInit(\"DELETE\", input),\n );\n }\n\n /** Ask only the verified owner member named by this owner_asks grant. */\n send(\n binding: HubLineGrantBinding,\n input: HubOwnerAskSendInput,\n ): Promise<HubLineSendReceipt> {\n return this.request(path(binding, \"/send\"), jsonInit(\"POST\", input));\n }\n\n /** Only a send created by this same grant is visible. */\n outbox(\n binding: HubLineGrantBinding,\n messageId: string,\n ): Promise<HubLineSendReceipt> {\n if (!/^lmsg_[A-Za-z0-9_-]+$/.test(messageId))\n throw new Error(\"A line message id is required\");\n return this.request(\n path(binding, `/outbox/${encodeURIComponent(messageId)}`),\n { method: \"GET\" },\n );\n }\n\n revoke(\n binding: HubLineGrantBinding,\n expectedGrantId?: string,\n ): Promise<{ revoked: boolean }> {\n const target = path(binding);\n return this.request(\n expectedGrantId === undefined\n ? target\n : `${target}&expectedGrantId=${encodeURIComponent(expectedGrantId)}`,\n { method: \"DELETE\" },\n );\n }\n}\n","import type { HubWorkflowGraphSpec } from \"./workflow-graph-types.js\";\nimport type { HubWorkflowRunSummary } from \"./workflow-run-types.js\";\n\nexport type {\n HubWorkflowCondition,\n HubWorkflowConditionOp,\n HubWorkflowConditionScalar,\n HubWorkflowGraphEdge,\n HubWorkflowGraphJoinRule,\n HubWorkflowGraphNode,\n HubWorkflowGraphSpec,\n} from \"./workflow-graph-types.js\";\n\n// ---------- Workflows ----------\n//\n// The workflow resource on the platform (`/v1/workflows`). YAML is the\n// durable representation; the platform compiles it to the engine's run model.\n// Reachable from inside a sandbox via the same client (TANGLE_API_KEY auth).\n\nexport interface HubWorkflowAction {\n kind: string;\n config: Record<string, unknown>;\n}\n\n/** Inbound-event matcher for a `provider_event` trigger. */\nexport interface HubWorkflowEventFilter {\n event: string;\n action?: string;\n repo?: string;\n}\n\nexport interface HubWorkflowTrigger {\n id: string;\n kind: \"provider_event\" | \"schedule\" | \"webhook\";\n enabled: boolean;\n provider: string | null;\n connectionId: string | null;\n eventFilter: HubWorkflowEventFilter | null;\n cron: string | null;\n timezone: string | null;\n nextFireAt: string | null;\n lastFiredAt: string | null;\n /** Schedule only: the ISO-8601 instant it stops firing; null when unbounded. */\n until: string | null;\n}\n\nexport interface HubWorkflowValidationError {\n path: string;\n message: string;\n /** Machine-readable reason where one is defined, e.g. `schedule_outlives_grant`. */\n code?: string;\n}\n\nexport interface HubWorkflow {\n id: string;\n name: string;\n description: string | null;\n enabled: boolean;\n /** Compiled engine actions (connection refs resolved to ids). */\n actions: HubWorkflowAction[];\n /** The durable YAML this workflow compiled from. */\n definitionYaml: string | null;\n validationErrors: HubWorkflowValidationError[];\n /** Current head revision; null on pre-versioning rows until their next\n * update (they have no revision history to point at yet). */\n headRev?: number | null;\n /** Present on get/create/update detail responses; omitted on list. */\n triggers?: HubWorkflowTrigger[];\n /** Present on the detail response: the trigger fields a manual run must supply,\n * derived from the definition's `${trigger.*}` references (empty when none). */\n manualRunInputs?: HubManualRunInput[];\n /** Present on the get-detail response: the compiled graph topology (null\n * when the definition has no graph topology, or is chain-equivalent —\n * the legacy linear semantics). Omitted on the list projection. */\n graph?: HubWorkflowGraphSpec | null;\n /** Create response only, when the definition has a webhook trigger: the\n * hook delivery URL, plus the plaintext bearer token exactly once at mint\n * (absent when an active token already exists — rotate to re-reveal). */\n hook?: HubWorkflowHookEndpoint;\n /** Present on the list response (latest run, or null if never run); omitted on detail. */\n lastRun?: HubWorkflowRunSummary | null;\n createdAt: string;\n updatedAt: string;\n}\n\n/** A trigger field a manual \"Run now\" must supply (derived from the\n * definition's `${trigger.*}` references). `required` is false only when every\n * read is wrapped in a `default(...)`. */\nexport interface HubManualRunInput {\n path: string;\n required: boolean;\n}\n\nexport type HubWorkflowValidateResponse =\n | { valid: true; name: string; actionCount: number; triggerCount: number }\n | { valid: false; errors: HubWorkflowValidationError[] };\n\n/** JSON Schema for the YAML workflow definition. */\nexport type HubWorkflowSchemaResponse = Record<string, unknown>;\n\n// ---------- Workflow definition revisions ----------\n//\n// The append-only history of a workflow's YAML (`GET :id/revisions`,\n// `POST :id/rollback`). Every create/update/rollback appends one rev; rollback\n// restores a prior rev's YAML as a NEW head — history is never rewritten.\n// `GET :id/revisions` lists APPLIED revs only (the history); proposed and\n// rejected revs live on the proposals surface below.\n\nexport type HubWorkflowRevisionSource = \"user\" | \"copilot\" | \"intelligence\";\n\n/** Lifecycle state of a revision. The head rev only ever points at an\n * `applied` revision; `proposed` is a service-token-authored suggestion\n * awaiting an owner decision; `rejected` is a declined proposal (kept for\n * audit). */\nexport type HubWorkflowRevisionStatus = \"applied\" | \"proposed\" | \"rejected\";\n\n/** Revision list projection — attribution + note + status, WITHOUT the YAML body. */\nexport interface HubWorkflowRevisionMeta {\n rev: number;\n createdAt: string;\n createdBy: string;\n source: HubWorkflowRevisionSource;\n note: string | null;\n status: HubWorkflowRevisionStatus;\n}\n\n/** One revision WITH its YAML (and the actions compiled alongside it). */\nexport interface HubWorkflowRevision extends HubWorkflowRevisionMeta {\n yaml: string;\n actions: HubWorkflowAction[];\n}\n\n/** A proposal (proposed or rejected revision) WITH its evidence — what the\n * proposer derived the suggestion from (e.g. health stats), plus\n * `rejectionReason` once declined. */\nexport interface HubWorkflowProposal extends HubWorkflowRevisionMeta {\n evidence: unknown;\n}\n\n// ---------- Workflow templates ----------\n//\n// The curated starter gallery (`GET /v1/workflows/templates`) and one-click\n// instantiate. Static catalog data — no per-user fields.\n\nexport type HubWorkflowTemplateCategory =\n | \"Code Review\"\n | \"Triage\"\n | \"Reports\"\n | \"Multi-agent\";\n\n/** One author-time literal a gallery starter needs the user to supply. Every\n * declared parameter is required; `example` is a valid value that seeds the\n * form placeholder. */\nexport interface HubWorkflowTemplateParameter {\n key: string;\n label: string;\n type: \"string\" | \"integer\";\n help?: string;\n example: string;\n default?: string;\n defaultFrom?: \"native_evaluation\";\n required?: boolean;\n advanced?: boolean;\n}\n\nexport interface HubWorkflowTemplate {\n id: string;\n name: string;\n summary: string;\n category: HubWorkflowTemplateCategory;\n icon: string;\n requiresSandbox: boolean;\n /** Providers the template references (a \"uses GitHub\" hint), sorted. */\n providers: string[];\n parameters: HubWorkflowTemplateParameter[];\n /** The template with parameter tokens rendered to their example values. */\n previewYaml: string;\n setupNotes?: string[];\n}\n\n/** A connection a definition needs, with where to connect it. */\nexport interface HubWorkflowConnectionRequirement {\n provider: string;\n connected: boolean;\n kind: \"integration\" | \"github_app\";\n /** Where to satisfy the requirement (App install flow for `github_app`,\n * integrations page otherwise); null when no target exists. */\n connectUrl: string | null;\n /** Another requirement in the same list that must be satisfied first — a\n * `github_app` install is claimed through the `github` connection, so it is\n * refused while that connection is missing. Null when nothing blocks this\n * requirement. The list is ordered prerequisite-first. */\n prerequisite: { provider: string; kind: \"integration\" | \"github_app\" } | null;\n}\n\n/** Stable identity shared by workflow connection requirements and prerequisite references. */\nexport function workflowConnectionRequirementKey(\n ref: Readonly<Pick<HubWorkflowConnectionRequirement, \"provider\" | \"kind\">>,\n): string {\n return `${ref.kind}:${ref.provider}`;\n}\n\n/**\n * Result of instantiating a template. `created: false` is NOT an error — an\n * actionable continuation listing the connections to satisfy before retrying\n * the same call. Invalid parameters or a compile failure throw `HubSdkError`\n * (400) instead, matching the create route's contract.\n */\nexport type HubWorkflowTemplateInstantiateResult =\n | { created: true; workflow: HubWorkflow }\n | {\n created: false;\n connectionRequirements: HubWorkflowConnectionRequirement[];\n };\n\n// ---------- Webhook hook tokens ----------\n\n/** A workflow's webhook delivery endpoint. `token` is the plaintext bearer —\n * revealed only at mint/rotate time (only the hash is stored). */\nexport interface HubWorkflowHookEndpoint {\n url: string;\n token?: string;\n}\n"],"mappings":"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"}
|
package/dist/mcp/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as HubClient } from "../client-
|
|
1
|
+
import { t as HubClient } from "../client-C-JbtG50.js";
|
|
2
2
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
3
3
|
import { Hono } from "hono";
|
|
4
4
|
import { AgentProfile, AgentProfileConnection, AgentProfileMcpServer } from "@tangle-network/agent-interface";
|
package/dist/mcp/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { b as isHubUnauthenticated, v as HubSdkError } from "../client-
|
|
1
|
+
import { b as isHubUnauthenticated, v as HubSdkError } from "../client-gF4iRjd3.js";
|
|
2
2
|
import { n as createHubMcpBridgeServer, t as HttpHubMcpBridgeClient } from "../bridge-D8_lAKaW.js";
|
|
3
3
|
import { Hono } from "hono";
|
|
4
4
|
import { z } from "zod";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tangle-network/hub-sdk",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.7",
|
|
4
4
|
"description": "Typed SDK for Tangle Hub tool discovery and execution",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -42,8 +42,8 @@
|
|
|
42
42
|
"vitest": "^4.1.12"
|
|
43
43
|
},
|
|
44
44
|
"tangleRelease": {
|
|
45
|
-
"sourceSha": "
|
|
46
|
-
"mergedAt": "2026-10-
|
|
45
|
+
"sourceSha": "3efc4a354c78aa60a607cf9e12c60319beaa7ec5",
|
|
46
|
+
"mergedAt": "2026-10-28T21:57:33-06:00"
|
|
47
47
|
},
|
|
48
48
|
"scripts": {
|
|
49
49
|
"build": "tsdown",
|