robotrock 0.2.0 → 0.3.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.
@@ -1,6 +1,6 @@
1
- import { R as RobotRockToolCallInfo, F as FormatToolApprovalTaskOptions, H as HumanToolResult } from '../workflow-CHWiRPU9.js';
2
- export { A as APPROVE_BY_HUMAN_ACTIONS, d as ApproveByHumanToolDurableOptions, c as ApproveByHumanToolOptions, r as CreateRobotRockAiToolsOptions, f as CreateSendToHumanToolDurableOptions, C as CreateSendToHumanToolOptions, B as ResolveToolApprovalsOptions, i as RobotRockAiContext, j as RobotRockAiMode, k as RobotRockAiPollingContext, l as RobotRockAiTriggerContext, m as RobotRockAiWorkflowContext, D as RobotRockToolApprovalConfig, z as RobotRockToolApprovalDecision, E as RunWithRobotRockApprovalsOptions, T as ToolApprovalRequestPart, t as applyRobotRockToolApprovalToTools, g as approveByHumanForAi, b as approveByHumanInputSchema, a as approveByHumanTool, u as collectApprovalRequests, o as createRobotRockAiTools, p as createRobotRockAiTriggerContext, q as createRobotRockAiWorkflowContext, v as createRobotRockNeedsApproval, w as createRobotRockToolApproval, e as createSendToHumanTool, n as normalizeRobotRockAiContext, x as resolveToolApprovalsViaRobotRock, y as runWithRobotRockApprovals, h as sendToHumanForAi, s as sendToHumanToolInputSchema } from '../workflow-CHWiRPU9.js';
3
- import { a as SendToHumanInput } from '../client-agOgTJob.js';
1
+ import { R as RobotRockToolCallInfo, F as FormatToolApprovalTaskOptions, H as HumanToolResult } from '../workflow-BYeIZgD0.js';
2
+ export { A as APPROVE_BY_HUMAN_ACTIONS, d as ApproveByHumanToolDurableOptions, c as ApproveByHumanToolOptions, r as CreateRobotRockAiToolsOptions, f as CreateSendToHumanToolDurableOptions, C as CreateSendToHumanToolOptions, B as ResolveToolApprovalsOptions, i as RobotRockAiContext, j as RobotRockAiMode, k as RobotRockAiPollingContext, l as RobotRockAiTriggerContext, m as RobotRockAiWorkflowContext, D as RobotRockToolApprovalConfig, z as RobotRockToolApprovalDecision, E as RunWithRobotRockApprovalsOptions, T as ToolApprovalRequestPart, t as applyRobotRockToolApprovalToTools, g as approveByHumanForAi, b as approveByHumanInputSchema, a as approveByHumanTool, u as collectApprovalRequests, o as createRobotRockAiTools, p as createRobotRockAiTriggerContext, q as createRobotRockAiWorkflowContext, v as createRobotRockNeedsApproval, w as createRobotRockToolApproval, e as createSendToHumanTool, n as normalizeRobotRockAiContext, x as resolveToolApprovalsViaRobotRock, y as runWithRobotRockApprovals, h as sendToHumanForAi, s as sendToHumanToolInputSchema } from '../workflow-BYeIZgD0.js';
3
+ import { a as SendToHumanInput } from '../client-Dhk9qxhL.js';
4
4
  import { DiscriminatedApprovalResult } from '../schemas/index.js';
5
5
  import 'ai';
6
6
  import 'zod';
package/dist/ai/index.js CHANGED
@@ -19,8 +19,8 @@ import {
19
19
  sendToHumanForAi,
20
20
  sendToHumanToolInputSchema,
21
21
  toHumanToolResult
22
- } from "../chunk-OJ57M3RW.js";
23
- import "../chunk-7FVE6OYZ.js";
22
+ } from "../chunk-DSZ3GMT4.js";
23
+ import "../chunk-LXM7VS4Q.js";
24
24
  export {
25
25
  APPROVE_BY_HUMAN_ACTIONS,
26
26
  DEFAULT_APPROVE_ACTIONS,
@@ -1,5 +1,5 @@
1
- export { i as RobotRockAiContext, j as RobotRockAiMode, k as RobotRockAiPollingContext, l as RobotRockAiTriggerContext, t as applyRobotRockToolApprovalToTools, a as approveByHumanTool, o as createRobotRockAiTools, p as createRobotRockAiTriggerContext, v as createRobotRockNeedsApproval, w as createRobotRockToolApproval, e as createSendToHumanTool, x as resolveToolApprovalsViaRobotRock, y as runWithRobotRockApprovals } from '../workflow-CHWiRPU9.js';
1
+ export { i as RobotRockAiContext, j as RobotRockAiMode, k as RobotRockAiPollingContext, l as RobotRockAiTriggerContext, t as applyRobotRockToolApprovalToTools, a as approveByHumanTool, o as createRobotRockAiTools, p as createRobotRockAiTriggerContext, v as createRobotRockNeedsApproval, w as createRobotRockToolApproval, e as createSendToHumanTool, x as resolveToolApprovalsViaRobotRock, y as runWithRobotRockApprovals } from '../workflow-BYeIZgD0.js';
2
2
  import 'ai';
3
3
  import 'zod';
4
- import '../client-agOgTJob.js';
4
+ import '../client-Dhk9qxhL.js';
5
5
  import '../schemas/index.js';
@@ -8,8 +8,8 @@ import {
8
8
  createSendToHumanTool,
9
9
  resolveToolApprovalsViaRobotRock,
10
10
  runWithRobotRockApprovals
11
- } from "../chunk-OJ57M3RW.js";
12
- import "../chunk-7FVE6OYZ.js";
11
+ } from "../chunk-DSZ3GMT4.js";
12
+ import "../chunk-LXM7VS4Q.js";
13
13
  export {
14
14
  applyRobotRockToolApprovalToTools,
15
15
  approveByHumanTool,
@@ -1,5 +1,5 @@
1
- export { i as RobotRockAiContext, j as RobotRockAiMode, k as RobotRockAiPollingContext, l as RobotRockAiTriggerContext, m as RobotRockAiWorkflowContext, t as applyRobotRockToolApprovalToTools, a as approveByHumanTool, o as createRobotRockAiTools, q as createRobotRockAiWorkflowContext, v as createRobotRockNeedsApproval, w as createRobotRockToolApproval, e as createSendToHumanTool, x as resolveToolApprovalsViaRobotRock, y as runWithRobotRockApprovals } from '../workflow-CHWiRPU9.js';
1
+ export { i as RobotRockAiContext, j as RobotRockAiMode, k as RobotRockAiPollingContext, l as RobotRockAiTriggerContext, m as RobotRockAiWorkflowContext, t as applyRobotRockToolApprovalToTools, a as approveByHumanTool, o as createRobotRockAiTools, q as createRobotRockAiWorkflowContext, v as createRobotRockNeedsApproval, w as createRobotRockToolApproval, e as createSendToHumanTool, x as resolveToolApprovalsViaRobotRock, y as runWithRobotRockApprovals } from '../workflow-BYeIZgD0.js';
2
2
  import 'ai';
3
3
  import 'zod';
4
- import '../client-agOgTJob.js';
4
+ import '../client-Dhk9qxhL.js';
5
5
  import '../schemas/index.js';
@@ -8,8 +8,8 @@ import {
8
8
  createSendToHumanTool,
9
9
  resolveToolApprovalsViaRobotRock,
10
10
  runWithRobotRockApprovals
11
- } from "../chunk-OJ57M3RW.js";
12
- import "../chunk-7FVE6OYZ.js";
11
+ } from "../chunk-DSZ3GMT4.js";
12
+ import "../chunk-LXM7VS4Q.js";
13
13
  export {
14
14
  applyRobotRockToolApprovalToTools,
15
15
  approveByHumanTool,
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  assignToSchema
3
- } from "./chunk-7FVE6OYZ.js";
3
+ } from "./chunk-LXM7VS4Q.js";
4
4
 
5
5
  // src/ai/context.ts
6
6
  var APPROVE_BY_HUMAN_ACTIONS = [
@@ -515,4 +515,4 @@ export {
515
515
  resolveToolApprovalsViaRobotRock,
516
516
  runWithRobotRockApprovals
517
517
  };
518
- //# sourceMappingURL=chunk-OJ57M3RW.js.map
518
+ //# sourceMappingURL=chunk-DSZ3GMT4.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  createTaskBodySchema
3
- } from "./chunk-7FVE6OYZ.js";
3
+ } from "./chunk-LXM7VS4Q.js";
4
4
 
5
5
  // src/approval-result.ts
6
6
  var TaskTimeoutError = class extends Error {
@@ -90,7 +90,7 @@ var RobotRock = class {
90
90
  );
91
91
  }
92
92
  this.apiKey = apiKey;
93
- const rawBase = config.baseUrl ?? "https://api.robotrock.com/v1";
93
+ const rawBase = config.baseUrl ?? "https://api.robotrock.io/v1";
94
94
  this.baseUrl = rawBase.replace(/\/+$/, "");
95
95
  this.app = config.app;
96
96
  this.version = config.version ?? 2;
@@ -108,7 +108,8 @@ var RobotRock = class {
108
108
  });
109
109
  const bodyPayload = {
110
110
  ...normalizedTask,
111
- ...task.assignTo !== void 0 ? { assignTo: task.assignTo } : {}
111
+ ...task.assignTo !== void 0 ? { assignTo: task.assignTo } : {},
112
+ ...task.threadId !== void 0 ? { threadId: task.threadId } : {}
112
113
  };
113
114
  const validation = createTaskBodySchema.safeParse(bodyPayload);
114
115
  if (!validation.success) {
@@ -248,6 +249,7 @@ function normalizeSendToHumanInput(task, clientDefaults) {
248
249
  actions,
249
250
  idempotencyKey: _idempotencyKey,
250
251
  assignTo: _assignTo,
252
+ threadId: _threadId,
251
253
  validUntil,
252
254
  app: taskApp,
253
255
  ...rest
@@ -297,7 +299,7 @@ function getErrorMessage(data, fallback) {
297
299
  }
298
300
 
299
301
  // src/env.ts
300
- var DEFAULT_BASE_URL = "https://api.robotrock.com/v1";
302
+ var DEFAULT_BASE_URL = "https://api.robotrock.io/v1";
301
303
  function resolveRobotRockConfig(overrides) {
302
304
  const apiKey = overrides?.apiKey ?? process.env.ROBOTROCK_API_KEY;
303
305
  if (!apiKey) {
@@ -327,4 +329,4 @@ export {
327
329
  resolveRobotRockConfig,
328
330
  resolveRobotRockClient
329
331
  };
330
- //# sourceMappingURL=chunk-THVGHUTX.js.map
332
+ //# sourceMappingURL=chunk-KOXJCIST.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/approval-result.ts","../src/client.ts","../src/env.ts"],"sourcesContent":["import type { DiscriminatedApprovalResult, Task } from \"./schemas/index.js\";\n\nexport class TaskTimeoutError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"TaskTimeoutError\";\n }\n}\n\nexport class TaskExpiredError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"TaskExpiredError\";\n }\n}\n\n/**\n * Map a handled API task to a discriminated approval result.\n * Runtime validation is minimal; TypeScript narrows via `task.actions` at the call site.\n */\nexport function toDiscriminatedApprovalResult<A extends readonly { id: string; schema?: unknown }[]>(\n actions: A,\n task: Task\n): DiscriminatedApprovalResult<A> {\n void actions;\n\n if (!task.handled) {\n throw new Error(\"Task has no handled result\");\n }\n\n return {\n actionId: task.handled.action.id,\n data: task.handled.action.data,\n handledBy: task.handled.handledBy,\n handledAt: new Date(task.handledAt ?? Date.now()),\n taskId: task.id,\n } as DiscriminatedApprovalResult<A>;\n}\n","import {\n type AssignToInput,\n type TaskContextInput,\n createTaskBodySchema,\n} from \"./schemas/index.js\";\nimport {\n TaskExpiredError,\n TaskTimeoutError,\n toDiscriminatedApprovalResult,\n} from \"./approval-result.js\";\nimport type { DiscriminatedApprovalResult, Task, TaskResponse } from \"./schemas/index.js\";\n\nexport type RobotRockWebhookConfig = {\n url: string;\n headers?: Record<string, string>;\n};\n\nexport interface RobotRockPollingOptions {\n /** Poll interval when no webhook is configured (ms). @default 2000 */\n intervalMs?: number;\n /**\n * Max time to poll when no webhook is configured (ms).\n * Polling also stops when the task's `validUntil` passes, whichever is sooner.\n * @default 86400000 (24h)\n */\n timeoutMs?: number;\n}\n\ntype RobotRockClientBaseConfig = {\n /** Optional override for API key. Falls back to ROBOTROCK_API_KEY. */\n apiKey?: string;\n /**\n * Base URL for the RobotRock API\n * @default \"https://api.robotrock.io/v1\"\n */\n baseUrl?: string;\n /**\n * Default inbox app bucket for every task from this client.\n * When omitted, the API uses your API key name.\n */\n app?: string;\n /**\n * Task context format version sent on every `sendToHuman` request.\n * @default 2\n */\n version?: 2;\n};\n\n/** Client config with a webhook (mutually exclusive with `polling`). */\nexport type RobotRockWebhookClientConfig = RobotRockClientBaseConfig & {\n webhook: RobotRockWebhookConfig;\n polling?: never;\n};\n\n/** Client config without a webhook; optional `polling` controls the wait loop. */\nexport type RobotRockPollingClientConfig = RobotRockClientBaseConfig & {\n webhook?: never;\n polling?: RobotRockPollingOptions;\n};\n\nexport type RobotRockConfig = RobotRockWebhookClientConfig | RobotRockPollingClientConfig;\n\nexport type SendToHumanActionInput = Omit<TaskContextInput[\"actions\"][number], \"handlers\">;\n\nexport type SendToHumanValidUntil = Date | string;\n\nexport type SendToHumanInput<\n A extends readonly SendToHumanActionInput[] = readonly SendToHumanActionInput[],\n> = Omit<TaskContextInput, \"app\" | \"actions\" | \"version\" | \"validUntil\"> & {\n actions: A;\n /** Task deadline; serialized to an ISO 8601 string on the wire. */\n validUntil?: SendToHumanValidUntil;\n /** Optional idempotency key to prevent duplicate tasks */\n idempotencyKey?: string;\n /** Assign to tenant users (email) and/or groups (slug). Narrows inbox visibility. */\n assignTo?: AssignToInput;\n /**\n * Groups related tasks together in the inbox. Omit to let the server generate\n * one (returned as `task.threadId`) and reuse it on later tasks in the thread.\n */\n threadId?: string;\n};\n\ntype SendToHumanWithAppInput<\n A extends readonly SendToHumanActionInput[] = readonly SendToHumanActionInput[],\n> = (SendToHumanInput<A> | Readonly<SendToHumanInput<A>>) & {\n /** Inbox app bucket. Overrides the client `app` when set. */\n app?: string;\n};\n\nexport type SendToHumanResult<\n A extends readonly SendToHumanActionInput[] = readonly SendToHumanActionInput[],\n> =\n | {\n mode: \"created\";\n task: TaskResponse[\"task\"];\n }\n | ({\n mode: \"handled\";\n task: TaskResponse[\"task\"];\n } & DiscriminatedApprovalResult<A>);\n\nconst DEFAULT_POLL_INTERVAL_MS = 2_000;\nconst DEFAULT_TIMEOUT_MS = 24 * 60 * 60 * 1_000;\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nfunction parseValidUntilMs(value: string | number | Date | undefined): number | undefined {\n if (value === undefined) {\n return undefined;\n }\n\n if (value instanceof Date) {\n const ms = value.getTime();\n return Number.isNaN(ms) ? undefined : ms;\n }\n\n if (typeof value === \"number\") {\n return Number.isFinite(value) ? value : undefined;\n }\n\n const parsed = Date.parse(value);\n return Number.isNaN(parsed) ? undefined : parsed;\n}\n\nfunction serializeValidUntil(value: SendToHumanValidUntil): string {\n if (value instanceof Date) {\n const ms = value.getTime();\n if (Number.isNaN(ms)) {\n throw new RobotRockError(\"Invalid validUntil: Date is invalid\", 400);\n }\n return value.toISOString();\n }\n\n if (typeof value === \"string\" && !Number.isNaN(Date.parse(value))) {\n return new Date(value).toISOString();\n }\n\n throw new RobotRockError(\"Invalid validUntil: expected a Date or parseable date string\", 400);\n}\n\nexport class RobotRockError extends Error {\n constructor(\n message: string,\n public readonly statusCode: number,\n public readonly response?: unknown\n ) {\n super(message);\n this.name = \"RobotRockError\";\n }\n}\n\n/**\n * RobotRock API client for creating and querying human-in-the-loop tasks.\n */\nexport class RobotRock {\n private readonly apiKey: string;\n private readonly baseUrl: string;\n private readonly app?: string;\n private readonly version: 2;\n private readonly webhook?: RobotRockWebhookConfig;\n private readonly polling: RobotRockPollingOptions;\n\n constructor(config: RobotRockConfig) {\n if (config.webhook && config.polling) {\n throw new Error(\n \"RobotRock client cannot configure both webhook and polling. Use webhook for callbacks or polling to block until handled.\"\n );\n }\n\n const apiKey = config.apiKey ?? process.env.ROBOTROCK_API_KEY;\n if (!apiKey) {\n throw new Error(\n \"RobotRock API key is required. Set ROBOTROCK_API_KEY or pass apiKey when creating the client.\"\n );\n }\n this.apiKey = apiKey;\n const rawBase = config.baseUrl ?? \"https://api.robotrock.io/v1\";\n this.baseUrl = rawBase.replace(/\\/+$/, \"\");\n this.app = config.app;\n this.version = config.version ?? 2;\n this.webhook = config.webhook;\n this.polling = config.polling ?? {};\n }\n\n /**\n * Create a task via POST /v1 without waiting for a human response.\n */\n async createTask<const A extends readonly SendToHumanActionInput[]>(\n task: SendToHumanWithAppInput<A>\n ): Promise<TaskResponse[\"task\"]> {\n const normalizedTask = normalizeSendToHumanInput(task, {\n webhook: this.webhook,\n app: this.app,\n version: this.version,\n });\n const bodyPayload = {\n ...normalizedTask,\n ...(task.assignTo !== undefined ? { assignTo: task.assignTo } : {}),\n ...(task.threadId !== undefined ? { threadId: task.threadId } : {}),\n };\n const validation = createTaskBodySchema.safeParse(bodyPayload);\n if (!validation.success) {\n throw new RobotRockError(\n `Invalid task: ${validation.error.errors[0]?.message}`,\n 400,\n validation.error.errors\n );\n }\n\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n \"X-Api-Key\": this.apiKey,\n };\n\n if (task.idempotencyKey) {\n headers[\"Idempotency-Key\"] = task.idempotencyKey;\n }\n\n const response = await fetch(`${this.baseUrl}/`, {\n method: \"POST\",\n headers,\n body: JSON.stringify(validation.data),\n });\n\n const data = await parseResponseBody(response);\n\n if (!response.ok) {\n throw new RobotRockError(\n getErrorMessage(data, \"Failed to create task\"),\n response.status,\n data\n );\n }\n\n return (data as unknown as TaskResponse).task;\n }\n\n async sendToHuman<const A extends readonly SendToHumanActionInput[]>(\n task: SendToHumanWithAppInput<A>\n ): Promise<SendToHumanResult<A>> {\n const normalizedTask = normalizeSendToHumanInput(task, {\n webhook: this.webhook,\n app: this.app,\n version: this.version,\n });\n const createdTaskTask = await this.createTask(task);\n const hasHandlers = normalizedTask.actions.some(\n (action) => Array.isArray(action.handlers) && action.handlers.length > 0\n );\n\n if (hasHandlers) {\n return {\n mode: \"created\",\n task: createdTaskTask,\n };\n }\n\n const timeoutMs = this.polling.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n const pollIntervalMs = this.polling.intervalMs ?? DEFAULT_POLL_INTERVAL_MS;\n const pollingDeadline = Date.now() + timeoutMs;\n const validUntilMs = parseValidUntilMs(createdTaskTask.validUntil);\n const deadline =\n validUntilMs !== undefined ? Math.min(pollingDeadline, validUntilMs) : pollingDeadline;\n const taskId = createdTaskTask.taskId;\n\n while (Date.now() < deadline) {\n const existing = await this.getTask(taskId);\n\n if (existing?.status === \"handled\" && existing.handled) {\n return {\n mode: \"handled\",\n task: createdTaskTask,\n ...(toDiscriminatedApprovalResult(\n normalizedTask.actions as unknown as A,\n existing\n ) as DiscriminatedApprovalResult<A>),\n };\n }\n\n if (existing?.status === \"expired\" || (existing && Date.now() >= existing.validUntil)) {\n throw new TaskExpiredError(\"Task reached validUntil before a human completed it\");\n }\n\n const remainingMs = deadline - Date.now();\n await sleep(Math.min(pollIntervalMs, Math.max(0, remainingMs)));\n }\n\n if (validUntilMs !== undefined && Date.now() >= validUntilMs) {\n throw new TaskExpiredError(\"Task reached validUntil before a human completed it\");\n }\n\n throw new TaskTimeoutError(`No human response within ${timeoutMs}ms`);\n }\n\n /**\n * Get a task by public task id (returned as `task.taskId` from {@link sendToHuman}).\n */\n async getTask(taskId: string): Promise<Task | null> {\n const response = await fetch(`${this.baseUrl}/tasks/${taskId}`, {\n method: \"GET\",\n headers: {\n \"X-Api-Key\": this.apiKey,\n },\n });\n\n if (response.status === 404) {\n return null;\n }\n\n const data = await parseResponseBody(response);\n\n if (!response.ok) {\n throw new RobotRockError(\n getErrorMessage(data, \"Failed to get task\"),\n response.status,\n data\n );\n }\n\n return data as unknown as Task;\n }\n\n async cancelTask(taskId: string): Promise<void> {\n const response = await fetch(`${this.baseUrl}/tasks/${taskId}/cancel`, {\n method: \"POST\",\n headers: {\n \"X-Api-Key\": this.apiKey,\n },\n });\n\n if (!response.ok) {\n const data = await parseResponseBody(response);\n throw new RobotRockError(\n getErrorMessage(data, \"Failed to cancel task\"),\n response.status,\n data\n );\n }\n }\n}\n\nexport function createClient(config: RobotRockConfig): RobotRock {\n return new RobotRock(config);\n}\n\nexport function attachWebhookToActions(\n actions: readonly SendToHumanActionInput[],\n webhook: RobotRockWebhookConfig\n): TaskContextInput[\"actions\"] {\n return actions.map((action) => ({\n ...action,\n handlers: webhookToHandlers(webhook),\n }));\n}\n\nfunction webhookToHandlers(\n webhook: RobotRockWebhookConfig\n): TaskContextInput[\"actions\"][number][\"handlers\"] {\n return [\n {\n type: \"webhook\" as const,\n url: webhook.url,\n headers: webhook.headers ?? {},\n },\n ];\n}\n\nfunction normalizeSendToHumanInput<\n A extends readonly SendToHumanActionInput[] = readonly SendToHumanActionInput[],\n>(\n task: SendToHumanWithAppInput<A>,\n clientDefaults: { webhook?: RobotRockWebhookConfig; app?: string; version: 2 }\n): TaskContextInput {\n const {\n actions,\n idempotencyKey: _idempotencyKey,\n assignTo: _assignTo,\n threadId: _threadId,\n validUntil,\n app: taskApp,\n ...rest\n } = task;\n\n const webhook = clientDefaults.webhook;\n const normalizedActions: TaskContextInput[\"actions\"] = webhook\n ? attachWebhookToActions(actions, webhook)\n : (actions as unknown as TaskContextInput[\"actions\"]);\n\n const app = taskApp ?? clientDefaults.app;\n\n return {\n ...rest,\n version: clientDefaults.version,\n ...(app ? { app } : {}),\n ...(validUntil !== undefined ? { validUntil: serializeValidUntil(validUntil) } : {}),\n actions: normalizedActions,\n };\n}\n\ntype ParsedResponseBody = Record<string, unknown> | unknown[] | string | null;\n\nasync function parseResponseBody(response: Response): Promise<ParsedResponseBody> {\n const contentType = response.headers.get(\"content-type\") ?? \"\";\n const bodyText = await response.text();\n\n if (!bodyText) {\n return null;\n }\n\n if (contentType.toLowerCase().includes(\"application/json\")) {\n try {\n return JSON.parse(bodyText) as ParsedResponseBody;\n } catch {\n // Fall through and return text body below so error messages stay useful.\n }\n }\n\n try {\n return JSON.parse(bodyText) as ParsedResponseBody;\n } catch {\n return bodyText;\n }\n}\n\nfunction getErrorMessage(data: ParsedResponseBody, fallback: string): string {\n if (data && typeof data === \"object\" && !Array.isArray(data)) {\n const maybeMessage = (data as Record<string, unknown>).message;\n if (typeof maybeMessage === \"string\" && maybeMessage.trim()) {\n return maybeMessage;\n }\n }\n\n if (typeof data === \"string\" && data.trim()) {\n const compact = data.replace(/\\s+/g, \" \").trim();\n const snippet = compact.length > 180 ? `${compact.slice(0, 180)}...` : compact;\n return `${fallback}. Server returned non-JSON response: ${snippet}`;\n }\n\n return fallback;\n}\n","import { createClient, type RobotRock, type RobotRockConfig } from \"./client.js\";\n\nconst DEFAULT_BASE_URL = \"https://api.robotrock.io/v1\";\n\n/**\n * Read RobotRock client config from environment variables.\n *\n * - `ROBOTROCK_API_KEY` (required when not passed explicitly)\n * - `ROBOTROCK_BASE_URL` or `ROBOTROCK_API_URL` (optional)\n * - `ROBOTROCK_APP` (optional inbox app bucket)\n */\nexport function resolveRobotRockConfig(\n overrides?: Partial<RobotRockConfig>\n): RobotRockConfig {\n const apiKey = overrides?.apiKey ?? process.env.ROBOTROCK_API_KEY;\n if (!apiKey) {\n throw new Error(\n \"RobotRock API key is required. Set ROBOTROCK_API_KEY or pass apiKey when creating the client.\"\n );\n }\n\n const baseUrl =\n overrides?.baseUrl ??\n process.env.ROBOTROCK_BASE_URL ??\n process.env.ROBOTROCK_API_URL ??\n DEFAULT_BASE_URL;\n\n const app = overrides?.app ?? process.env.ROBOTROCK_APP;\n\n return app ? { apiKey, baseUrl, app } : { apiKey, baseUrl };\n}\n\n/** Use an explicit client or create one from env / optional config overrides. */\nexport function resolveRobotRockClient(\n client?: RobotRock,\n configOverrides?: Partial<RobotRockConfig>\n): RobotRock {\n if (client) {\n return client;\n }\n return createClient(resolveRobotRockConfig(configOverrides));\n}\n"],"mappings":"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","names":[]}
@@ -111,7 +111,12 @@ var assignToSchema = z.object({
111
111
  { message: 'Cannot combine "all" with other group slugs' }
112
112
  );
113
113
  var createTaskBodySchema = taskContextSchema.extend({
114
- assignTo: assignToSchema.optional()
114
+ assignTo: assignToSchema.optional(),
115
+ /**
116
+ * Groups related tasks together. When omitted, the server generates one and
117
+ * returns it so the caller can reuse it on later tasks in the same thread.
118
+ */
119
+ threadId: z.string().min(1).optional()
115
120
  });
116
121
 
117
122
  export {
@@ -121,4 +126,4 @@ export {
121
126
  assignToSchema,
122
127
  createTaskBodySchema
123
128
  };
124
- //# sourceMappingURL=chunk-7FVE6OYZ.js.map
129
+ //# sourceMappingURL=chunk-LXM7VS4Q.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/schemas/index.ts"],"sourcesContent":["import { z } from \"zod\";\n\nexport interface JSONSchema7 {\n $id?: string;\n $ref?: string;\n $schema?: string;\n $comment?: string;\n type?: JSONSchema7TypeName | JSONSchema7TypeName[];\n enum?: unknown[];\n const?: unknown;\n multipleOf?: number;\n maximum?: number;\n exclusiveMaximum?: number;\n minimum?: number;\n exclusiveMinimum?: number;\n maxLength?: number;\n minLength?: number;\n pattern?: string;\n format?: string;\n items?: JSONSchema7 | JSONSchema7[];\n additionalItems?: JSONSchema7 | boolean;\n maxItems?: number;\n minItems?: number;\n uniqueItems?: boolean;\n contains?: JSONSchema7;\n maxProperties?: number;\n minProperties?: number;\n required?: string[];\n properties?: Record<string, JSONSchema7>;\n patternProperties?: Record<string, JSONSchema7>;\n additionalProperties?: JSONSchema7 | boolean;\n dependencies?: Record<string, JSONSchema7 | string[]>;\n propertyNames?: JSONSchema7;\n if?: JSONSchema7;\n then?: JSONSchema7;\n else?: JSONSchema7;\n allOf?: JSONSchema7[];\n anyOf?: JSONSchema7[];\n oneOf?: JSONSchema7[];\n not?: JSONSchema7;\n title?: string;\n description?: string;\n default?: unknown;\n readOnly?: boolean;\n writeOnly?: boolean;\n examples?: unknown[];\n}\n\nexport type JSONSchema7TypeName =\n | \"string\"\n | \"number\"\n | \"integer\"\n | \"boolean\"\n | \"object\"\n | \"array\"\n | \"null\";\n\nexport type ExtendedJSONSchema7 = JSONSchema7 & {\n enumNames?: string[];\n [key: string]: unknown;\n};\n\nexport type UiSchema = {\n \"ui:widget\"?: string;\n \"ui:title\"?: string;\n \"ui:description\"?: string;\n \"ui:placeholder\"?: string;\n \"ui:options\"?: Record<string, unknown>;\n [key: string]: unknown;\n};\n\nconst safeUrlSchema = z\n .string()\n .refine((url) => url.startsWith(\"http://\") || url.startsWith(\"https://\"), {\n message: \"URL must start with http:// or https://\",\n });\n\nconst jsonSchema7Schema = z.custom<ExtendedJSONSchema7>(\n (val) => typeof val === \"object\" && val !== null,\n { message: \"Must be a valid JSON Schema object\" }\n);\n\nconst uiSchemaSchema = z.custom<UiSchema>((val) => typeof val === \"object\" && val !== null, {\n message: \"Must be a valid UiSchema object\",\n});\n\nconst webhookHandlerSchema = z.object({\n type: z.literal(\"webhook\"),\n url: safeUrlSchema,\n headers: z.record(z.string()),\n});\n\nconst triggerHandlerSchema = webhookHandlerSchema.extend({\n type: z.literal(\"trigger\"),\n tokenId: z.string().min(1),\n});\n\nconst handlerSchema = z.discriminatedUnion(\"type\", [webhookHandlerSchema, triggerHandlerSchema]);\n\nconst taskActionSchema = z.object({\n id: z.string().min(1),\n title: z.string().min(1),\n description: z.string().optional(),\n schema: jsonSchema7Schema.optional(),\n ui: uiSchemaSchema.optional(),\n data: z.record(z.unknown()).optional(),\n handlers: z.array(handlerSchema).min(1).optional(),\n});\n\nconst uiFieldSchemaSchema: z.ZodType<Record<string, unknown>> = z\n .object({\n \"ui:widget\": z.string().optional(),\n \"ui:title\": z.string().optional(),\n \"ui:description\": z.string().optional(),\n \"ui:options\": z.record(z.unknown()).optional(),\n items: z.lazy(() => z.record(uiFieldSchemaSchema)).optional(),\n })\n .passthrough();\n\nconst contextUiSchema = z.record(uiFieldSchemaSchema).optional();\n\nconst contextDataSchema = z\n .object({\n data: z.record(z.unknown()),\n ui: contextUiSchema,\n })\n .optional();\n\nexport const taskContextSchema = z.object({\n app: z.string().min(1).optional(),\n type: z.string().min(1),\n name: z.string().min(1),\n description: z.string().optional(),\n validUntil: z.string().optional(),\n context: contextDataSchema,\n version: z.literal(2).optional(),\n actions: z.array(taskActionSchema).min(1, \"At least one action is required\"),\n});\n\n/**\n * Assignment targets at task create (not stored in task context JSON).\n * Unknown user emails are auto-provisioned as assignee memberships (not full team seats).\n */\nexport const assignToSchema = z\n .object({\n users: z.array(z.string().email()).optional(),\n groups: z.array(z.string().min(1)).optional(),\n })\n .refine(\n (data) => {\n const groups = data.groups ?? [];\n if (groups.includes(\"all\") && groups.length > 1) {\n return false;\n }\n return true;\n },\n { message: 'Cannot combine \"all\" with other group slugs' }\n );\n\nexport const createTaskBodySchema = taskContextSchema.extend({\n assignTo: assignToSchema.optional(),\n /**\n * Groups related tasks together. When omitted, the server generates one and\n * returns it so the caller can reuse it on later tasks in the same thread.\n */\n threadId: z.string().min(1).optional(),\n});\n\nexport type AssignToInput = z.infer<typeof assignToSchema>;\nexport type CreateTaskBodyInput = z.input<typeof createTaskBodySchema>;\nexport type CreateTaskBody = z.output<typeof createTaskBodySchema>;\nexport type TaskContextInput = z.input<typeof taskContextSchema>;\nexport type TaskContextOutput = z.output<typeof taskContextSchema>;\nexport type TaskContext = TaskContextOutput;\nexport type TaskAction = z.infer<typeof taskActionSchema>;\nexport type WebhookHandler = z.infer<typeof webhookHandlerSchema>;\nexport type TriggerHandler = z.infer<typeof triggerHandlerSchema>;\nexport type Handler = z.infer<typeof handlerSchema>;\n\ntype InferObjectProperties<\n Props,\n Req extends PropertyKey,\n> = Props extends Record<string, unknown>\n ? ({\n [K in keyof Props as K extends Req ? K : never]-?: InferJsonSchema7<Props[K]>;\n } & {\n [K in keyof Props as K extends Req ? never : K]?: InferJsonSchema7<Props[K]>;\n } extends infer O\n ? { [K in keyof O]: O[K] }\n : never)\n : Record<string, unknown>;\n\ntype RequiredKeys<S> =\n S extends { readonly required: readonly string[] } ? S[\"required\"][number] : never;\n\nexport type InferJsonSchema7<S> = [S] extends [undefined]\n ? Record<string, never>\n : S extends { readonly const: infer C }\n ? C\n : S extends {\n readonly enum: readonly (infer E)[];\n }\n ? E\n : S extends {\n readonly type: \"object\";\n readonly properties?: infer Props;\n }\n ? InferObjectProperties<Props, RequiredKeys<S>>\n : S extends {\n readonly type: \"object\";\n readonly properties?: undefined;\n }\n ? Record<string, unknown>\n : S extends {\n readonly type: \"array\";\n readonly items?: infer Items;\n }\n ? Items extends readonly unknown[]\n ? InferJsonSchema7<Items[number]>[]\n : Items extends object\n ? InferJsonSchema7<Items>[]\n : unknown[]\n : S extends { readonly type: \"string\" }\n ? string\n : S extends { readonly type: \"number\" } | { readonly type: \"integer\" }\n ? number\n : S extends { readonly type: \"boolean\" }\n ? boolean\n : unknown;\n\nexport type TaskStatus = \"pending\" | \"open\" | \"handled\" | \"expired\";\n\nexport interface TaskResponse {\n success: boolean;\n task: {\n taskId: string;\n threadId: string;\n status: \"pending\" | \"open\";\n context: TaskContext;\n validUntil: string;\n submittedAt: string;\n };\n message: string;\n}\n\nexport interface Task {\n id: string;\n threadId?: string;\n createdAt: Date;\n status: TaskStatus;\n context: TaskContext;\n validUntil: number;\n handledAt?: number;\n handled?: {\n action: {\n id: string;\n data: unknown;\n };\n handledBy?: string;\n userId?: string;\n token?: string;\n };\n}\n\nexport type InferActionData<T extends { schema?: unknown }> = [\n Exclude<T[\"schema\"], undefined>,\n] extends [\n never,\n]\n ? Record<string, never>\n : Exclude<T[\"schema\"], undefined> extends infer S\n ? InferJsonSchema7<S>\n : Record<string, never>;\n\nexport type TupleElementIndices<T extends readonly unknown[]> = T extends readonly [\n unknown,\n ...unknown[],\n]\n ? Exclude<keyof T, keyof unknown[]>\n : number;\n\nexport interface TaskResult<T = Record<string, unknown>> {\n actionId: string;\n data: T;\n handledBy?: string;\n handledAt: Date;\n}\n\nexport interface ApprovalResult<T = Record<string, unknown>> extends TaskResult<T> {\n taskId: string;\n}\n\nexport type DiscriminatedApprovalResult<\n TActions extends readonly { id: string; schema?: unknown }[],\n> = {\n [I in TupleElementIndices<TActions>]: TActions[I] extends { id: string; schema?: unknown }\n ? Omit<ApprovalResult<InferActionData<TActions[I]>>, \"actionId\" | \"data\"> & {\n actionId: TActions[I][\"id\"];\n data: InferActionData<TActions[I]>;\n }\n : never\n}[TupleElementIndices<TActions>];\n"],"mappings":"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","names":[]}
@@ -19,7 +19,7 @@ type RobotRockClientBaseConfig = {
19
19
  apiKey?: string;
20
20
  /**
21
21
  * Base URL for the RobotRock API
22
- * @default "https://api.robotrock.com/v1"
22
+ * @default "https://api.robotrock.io/v1"
23
23
  */
24
24
  baseUrl?: string;
25
25
  /**
@@ -54,6 +54,11 @@ type SendToHumanInput<A extends readonly SendToHumanActionInput[] = readonly Sen
54
54
  idempotencyKey?: string;
55
55
  /** Assign to tenant users (email) and/or groups (slug). Narrows inbox visibility. */
56
56
  assignTo?: AssignToInput;
57
+ /**
58
+ * Groups related tasks together in the inbox. Omit to let the server generate
59
+ * one (returned as `task.threadId`) and reuse it on later tasks in the thread.
60
+ */
61
+ threadId?: string;
57
62
  };
58
63
  type SendToHumanWithAppInput<A extends readonly SendToHumanActionInput[] = readonly SendToHumanActionInput[]> = (SendToHumanInput<A> | Readonly<SendToHumanInput<A>>) & {
59
64
  /** Inbox app bucket. Overrides the client `app` when set. */
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { R as RobotRockConfig, b as RobotRock } from './client-agOgTJob.js';
2
- export { c as RobotRockError, g as RobotRockPollingClientConfig, i as RobotRockPollingOptions, f as RobotRockWebhookClientConfig, h as RobotRockWebhookConfig, S as SendToHumanActionInput, a as SendToHumanInput, k as SendToHumanResult, j as SendToHumanValidUntil, d as attachWebhookToActions, e as createClient } from './client-agOgTJob.js';
1
+ import { R as RobotRockConfig, b as RobotRock } from './client-Dhk9qxhL.js';
2
+ export { c as RobotRockError, g as RobotRockPollingClientConfig, i as RobotRockPollingOptions, f as RobotRockWebhookClientConfig, h as RobotRockWebhookConfig, S as SendToHumanActionInput, a as SendToHumanInput, k as SendToHumanResult, j as SendToHumanValidUntil, d as attachWebhookToActions, e as createClient } from './client-Dhk9qxhL.js';
3
3
  import { Task, DiscriminatedApprovalResult } from './schemas/index.js';
4
4
  export { ApprovalResult, AssignToInput, CreateTaskBody, CreateTaskBodyInput, Handler, InferActionData, TaskAction, TaskContext, TaskContextInput, TaskResponse, TaskResult, TaskStatus, TriggerHandler, TupleElementIndices, WebhookHandler, assignToSchema, createTaskBodySchema, taskContextSchema } from './schemas/index.js';
5
5
  import { z } from 'zod';
@@ -87,6 +87,11 @@ interface VerifyRobotRockWebhookOptions {
87
87
  * Keep undefined in production to enforce the canonical env var.
88
88
  */
89
89
  secret?: string;
90
+ /**
91
+ * Resolve the tenant signing secret by public task id (hosted MCP uses Convex).
92
+ * Used when secret is not passed explicitly.
93
+ */
94
+ resolveSecret?: (taskId: string) => Promise<string | undefined>;
90
95
  /** Signature header to read. @default "x-robotrock-signature" */
91
96
  signatureHeader?: string;
92
97
  }
package/dist/index.js CHANGED
@@ -8,12 +8,12 @@ import {
8
8
  resolveRobotRockClient,
9
9
  resolveRobotRockConfig,
10
10
  toDiscriminatedApprovalResult
11
- } from "./chunk-THVGHUTX.js";
11
+ } from "./chunk-KOXJCIST.js";
12
12
  import {
13
13
  assignToSchema,
14
14
  createTaskBodySchema,
15
15
  taskContextSchema
16
- } from "./chunk-7FVE6OYZ.js";
16
+ } from "./chunk-LXM7VS4Q.js";
17
17
 
18
18
  // src/webhook.ts
19
19
  import { createHmac, timingSafeEqual } from "crypto";
@@ -44,13 +44,6 @@ var RobotRockWebhookError = class extends Error {
44
44
  async function verifyRobotRockWebhook(request, options = {}) {
45
45
  const signatureHeaderName = options.signatureHeader ?? ROBOTROCK_SIGNATURE_HEADER;
46
46
  const signature = request.headers.get(signatureHeaderName);
47
- const secret = options.secret ?? process.env.ROBOTROCK_WEBHOOK_SECRET;
48
- if (!secret) {
49
- throw new RobotRockWebhookError(
50
- "Missing ROBOTROCK_WEBHOOK_SECRET for webhook verification",
51
- "MISSING_WEBHOOK_SECRET"
52
- );
53
- }
54
47
  if (!signature) {
55
48
  throw new RobotRockWebhookError(
56
49
  `Missing webhook signature header: ${signatureHeaderName}`,
@@ -58,6 +51,13 @@ async function verifyRobotRockWebhook(request, options = {}) {
58
51
  );
59
52
  }
60
53
  const rawBody = await request.text();
54
+ const secret = await resolveWebhookSigningSecret(rawBody, options);
55
+ if (!secret) {
56
+ throw new RobotRockWebhookError(
57
+ "Missing webhook signing secret for verification",
58
+ "MISSING_WEBHOOK_SECRET"
59
+ );
60
+ }
61
61
  assertValidSignature(rawBody, signature, secret);
62
62
  let parsedBody;
63
63
  try {
@@ -80,6 +80,31 @@ async function verifyRobotRockWebhook(request, options = {}) {
80
80
  headers: normalizeHeaders(request.headers)
81
81
  };
82
82
  }
83
+ async function resolveWebhookSigningSecret(rawBody, options) {
84
+ if (options.secret) {
85
+ return options.secret;
86
+ }
87
+ if (options.resolveSecret) {
88
+ const taskId = peekWebhookTaskId(rawBody);
89
+ if (taskId) {
90
+ const resolved = await options.resolveSecret(taskId);
91
+ if (resolved) {
92
+ return resolved;
93
+ }
94
+ }
95
+ }
96
+ return process.env.ROBOTROCK_WEBHOOK_SECRET;
97
+ }
98
+ function peekWebhookTaskId(rawBody) {
99
+ try {
100
+ const parsed = JSON.parse(rawBody);
101
+ if (typeof parsed === "object" && parsed !== null && "taskId" in parsed && typeof parsed.taskId === "string") {
102
+ return parsed.taskId;
103
+ }
104
+ } catch {
105
+ }
106
+ return void 0;
107
+ }
83
108
  function assertValidSignature(rawBody, signature, secret) {
84
109
  const expected = `sha256=${createHmac("sha256", secret).update(rawBody).digest("hex")}`;
85
110
  const expectedBuffer = Buffer.from(expected);
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/webhook.ts"],"sourcesContent":["import { createHmac, timingSafeEqual } from \"node:crypto\";\nimport { z } from \"zod\";\n\nconst ROBOTROCK_SIGNATURE_HEADER = \"x-robotrock-signature\";\n\nconst robotRockWebhookPayloadBodySchema = z.object({\n taskId: z.string().min(1),\n action: z.object({\n id: z.string().min(1),\n title: z.string().min(1),\n data: z.unknown(),\n }),\n handledBy: z.string().min(1).optional(),\n handledAt: z.string().min(1),\n handlerType: z.string().min(1),\n});\n\nconst robotRockWebhookPayloadSchema = robotRockWebhookPayloadBodySchema.extend({\n headers: z.record(z.string()),\n});\n\nexport type RobotRockWebhookErrorCode =\n | \"MISSING_WEBHOOK_SECRET\"\n | \"MISSING_SIGNATURE\"\n | \"INVALID_SIGNATURE\"\n | \"INVALID_JSON\"\n | \"INVALID_PAYLOAD\";\n\nexport class RobotRockWebhookError extends Error {\n constructor(\n message: string,\n public readonly code: RobotRockWebhookErrorCode,\n public readonly details?: unknown\n ) {\n super(message);\n this.name = \"RobotRockWebhookError\";\n }\n}\n\nexport type RobotRockWebhookPayload = z.infer<typeof robotRockWebhookPayloadSchema>;\n\nexport interface VerifyRobotRockWebhookOptions {\n /**\n * Override shared secret (defaults to ROBOTROCK_WEBHOOK_SECRET).\n * Keep undefined in production to enforce the canonical env var.\n */\n secret?: string;\n /** Signature header to read. @default \"x-robotrock-signature\" */\n signatureHeader?: string;\n}\n\n/**\n * Verify a RobotRock webhook request and return a validated payload.\n * Throws RobotRockWebhookError with machine-readable `code` for audit logging.\n */\nexport async function verifyRobotRockWebhook(\n request: Request,\n options: VerifyRobotRockWebhookOptions = {}\n): Promise<RobotRockWebhookPayload> {\n const signatureHeaderName = options.signatureHeader ?? ROBOTROCK_SIGNATURE_HEADER;\n const signature = request.headers.get(signatureHeaderName);\n const secret = options.secret ?? process.env.ROBOTROCK_WEBHOOK_SECRET;\n\n if (!secret) {\n throw new RobotRockWebhookError(\n \"Missing ROBOTROCK_WEBHOOK_SECRET for webhook verification\",\n \"MISSING_WEBHOOK_SECRET\"\n );\n }\n\n if (!signature) {\n throw new RobotRockWebhookError(\n `Missing webhook signature header: ${signatureHeaderName}`,\n \"MISSING_SIGNATURE\"\n );\n }\n\n const rawBody = await request.text();\n assertValidSignature(rawBody, signature, secret);\n\n let parsedBody: unknown;\n try {\n parsedBody = JSON.parse(rawBody);\n } catch (error) {\n throw new RobotRockWebhookError(\"Webhook body is not valid JSON\", \"INVALID_JSON\", {\n cause: error instanceof Error ? error.message : String(error),\n });\n }\n\n const payloadResult = robotRockWebhookPayloadBodySchema.safeParse(parsedBody);\n if (!payloadResult.success) {\n throw new RobotRockWebhookError(\n \"Webhook payload schema validation failed\",\n \"INVALID_PAYLOAD\",\n payloadResult.error.flatten()\n );\n }\n\n return {\n ...payloadResult.data,\n headers: normalizeHeaders(request.headers),\n };\n}\n\nfunction assertValidSignature(rawBody: string, signature: string, secret: string): void {\n const expected = `sha256=${createHmac(\"sha256\", secret).update(rawBody).digest(\"hex\")}`;\n const expectedBuffer = Buffer.from(expected);\n const receivedBuffer = Buffer.from(signature);\n\n if (\n expectedBuffer.length !== receivedBuffer.length ||\n !timingSafeEqual(expectedBuffer, receivedBuffer)\n ) {\n throw new RobotRockWebhookError(\"Webhook signature verification failed\", \"INVALID_SIGNATURE\");\n }\n}\n\nfunction normalizeHeaders(headers: Headers): Record<string, string> {\n const result: Record<string, string> = {};\n headers.forEach((value, key) => {\n result[key] = value;\n });\n return result;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA,SAAS,YAAY,uBAAuB;AAC5C,SAAS,SAAS;AAElB,IAAM,6BAA6B;AAEnC,IAAM,oCAAoC,EAAE,OAAO;AAAA,EACjD,QAAQ,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EACxB,QAAQ,EAAE,OAAO;AAAA,IACf,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,IACpB,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,IACvB,MAAM,EAAE,QAAQ;AAAA,EAClB,CAAC;AAAA,EACD,WAAW,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS;AAAA,EACtC,WAAW,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EAC3B,aAAa,EAAE,OAAO,EAAE,IAAI,CAAC;AAC/B,CAAC;AAED,IAAM,gCAAgC,kCAAkC,OAAO;AAAA,EAC7E,SAAS,EAAE,OAAO,EAAE,OAAO,CAAC;AAC9B,CAAC;AASM,IAAM,wBAAN,cAAoC,MAAM;AAAA,EAC/C,YACE,SACgB,MACA,SAChB;AACA,UAAM,OAAO;AAHG;AACA;AAGhB,SAAK,OAAO;AAAA,EACd;AACF;AAkBA,eAAsB,uBACpB,SACA,UAAyC,CAAC,GACR;AAClC,QAAM,sBAAsB,QAAQ,mBAAmB;AACvD,QAAM,YAAY,QAAQ,QAAQ,IAAI,mBAAmB;AACzD,QAAM,SAAS,QAAQ,UAAU,QAAQ,IAAI;AAE7C,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAEA,MAAI,CAAC,WAAW;AACd,UAAM,IAAI;AAAA,MACR,qCAAqC,mBAAmB;AAAA,MACxD;AAAA,IACF;AAAA,EACF;AAEA,QAAM,UAAU,MAAM,QAAQ,KAAK;AACnC,uBAAqB,SAAS,WAAW,MAAM;AAE/C,MAAI;AACJ,MAAI;AACF,iBAAa,KAAK,MAAM,OAAO;AAAA,EACjC,SAAS,OAAO;AACd,UAAM,IAAI,sBAAsB,kCAAkC,gBAAgB;AAAA,MAChF,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAAA,IAC9D,CAAC;AAAA,EACH;AAEA,QAAM,gBAAgB,kCAAkC,UAAU,UAAU;AAC5E,MAAI,CAAC,cAAc,SAAS;AAC1B,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,MACA,cAAc,MAAM,QAAQ;AAAA,IAC9B;AAAA,EACF;AAEA,SAAO;AAAA,IACL,GAAG,cAAc;AAAA,IACjB,SAAS,iBAAiB,QAAQ,OAAO;AAAA,EAC3C;AACF;AAEA,SAAS,qBAAqB,SAAiB,WAAmB,QAAsB;AACtF,QAAM,WAAW,UAAU,WAAW,UAAU,MAAM,EAAE,OAAO,OAAO,EAAE,OAAO,KAAK,CAAC;AACrF,QAAM,iBAAiB,OAAO,KAAK,QAAQ;AAC3C,QAAM,iBAAiB,OAAO,KAAK,SAAS;AAE5C,MACE,eAAe,WAAW,eAAe,UACzC,CAAC,gBAAgB,gBAAgB,cAAc,GAC/C;AACA,UAAM,IAAI,sBAAsB,yCAAyC,mBAAmB;AAAA,EAC9F;AACF;AAEA,SAAS,iBAAiB,SAA0C;AAClE,QAAM,SAAiC,CAAC;AACxC,UAAQ,QAAQ,CAAC,OAAO,QAAQ;AAC9B,WAAO,GAAG,IAAI;AAAA,EAChB,CAAC;AACD,SAAO;AACT;","names":[]}
1
+ {"version":3,"sources":["../src/webhook.ts"],"sourcesContent":["import { createHmac, timingSafeEqual } from \"node:crypto\";\nimport { z } from \"zod\";\n\nconst ROBOTROCK_SIGNATURE_HEADER = \"x-robotrock-signature\";\n\nconst robotRockWebhookPayloadBodySchema = z.object({\n taskId: z.string().min(1),\n action: z.object({\n id: z.string().min(1),\n title: z.string().min(1),\n data: z.unknown(),\n }),\n handledBy: z.string().min(1).optional(),\n handledAt: z.string().min(1),\n handlerType: z.string().min(1),\n});\n\nconst robotRockWebhookPayloadSchema = robotRockWebhookPayloadBodySchema.extend({\n headers: z.record(z.string()),\n});\n\nexport type RobotRockWebhookErrorCode =\n | \"MISSING_WEBHOOK_SECRET\"\n | \"MISSING_SIGNATURE\"\n | \"INVALID_SIGNATURE\"\n | \"INVALID_JSON\"\n | \"INVALID_PAYLOAD\";\n\nexport class RobotRockWebhookError extends Error {\n constructor(\n message: string,\n public readonly code: RobotRockWebhookErrorCode,\n public readonly details?: unknown\n ) {\n super(message);\n this.name = \"RobotRockWebhookError\";\n }\n}\n\nexport type RobotRockWebhookPayload = z.infer<typeof robotRockWebhookPayloadSchema>;\n\nexport interface VerifyRobotRockWebhookOptions {\n /**\n * Override shared secret (defaults to ROBOTROCK_WEBHOOK_SECRET).\n * Keep undefined in production to enforce the canonical env var.\n */\n secret?: string;\n /**\n * Resolve the tenant signing secret by public task id (hosted MCP uses Convex).\n * Used when secret is not passed explicitly.\n */\n resolveSecret?: (taskId: string) => Promise<string | undefined>;\n /** Signature header to read. @default \"x-robotrock-signature\" */\n signatureHeader?: string;\n}\n\n/**\n * Verify a RobotRock webhook request and return a validated payload.\n * Throws RobotRockWebhookError with machine-readable `code` for audit logging.\n */\nexport async function verifyRobotRockWebhook(\n request: Request,\n options: VerifyRobotRockWebhookOptions = {}\n): Promise<RobotRockWebhookPayload> {\n const signatureHeaderName = options.signatureHeader ?? ROBOTROCK_SIGNATURE_HEADER;\n const signature = request.headers.get(signatureHeaderName);\n\n if (!signature) {\n throw new RobotRockWebhookError(\n `Missing webhook signature header: ${signatureHeaderName}`,\n \"MISSING_SIGNATURE\"\n );\n }\n\n const rawBody = await request.text();\n const secret = await resolveWebhookSigningSecret(rawBody, options);\n\n if (!secret) {\n throw new RobotRockWebhookError(\n \"Missing webhook signing secret for verification\",\n \"MISSING_WEBHOOK_SECRET\"\n );\n }\n\n assertValidSignature(rawBody, signature, secret);\n\n let parsedBody: unknown;\n try {\n parsedBody = JSON.parse(rawBody);\n } catch (error) {\n throw new RobotRockWebhookError(\"Webhook body is not valid JSON\", \"INVALID_JSON\", {\n cause: error instanceof Error ? error.message : String(error),\n });\n }\n\n const payloadResult = robotRockWebhookPayloadBodySchema.safeParse(parsedBody);\n if (!payloadResult.success) {\n throw new RobotRockWebhookError(\n \"Webhook payload schema validation failed\",\n \"INVALID_PAYLOAD\",\n payloadResult.error.flatten()\n );\n }\n\n return {\n ...payloadResult.data,\n headers: normalizeHeaders(request.headers),\n };\n}\n\nasync function resolveWebhookSigningSecret(\n rawBody: string,\n options: VerifyRobotRockWebhookOptions\n): Promise<string | undefined> {\n if (options.secret) {\n return options.secret;\n }\n\n if (options.resolveSecret) {\n const taskId = peekWebhookTaskId(rawBody);\n if (taskId) {\n const resolved = await options.resolveSecret(taskId);\n if (resolved) {\n return resolved;\n }\n }\n }\n\n return process.env.ROBOTROCK_WEBHOOK_SECRET;\n}\n\nfunction peekWebhookTaskId(rawBody: string): string | undefined {\n try {\n const parsed: unknown = JSON.parse(rawBody);\n if (\n typeof parsed === \"object\" &&\n parsed !== null &&\n \"taskId\" in parsed &&\n typeof (parsed as { taskId: unknown }).taskId === \"string\"\n ) {\n return (parsed as { taskId: string }).taskId;\n }\n } catch {\n // fall through\n }\n return undefined;\n}\n\nfunction assertValidSignature(rawBody: string, signature: string, secret: string): void {\n const expected = `sha256=${createHmac(\"sha256\", secret).update(rawBody).digest(\"hex\")}`;\n const expectedBuffer = Buffer.from(expected);\n const receivedBuffer = Buffer.from(signature);\n\n if (\n expectedBuffer.length !== receivedBuffer.length ||\n !timingSafeEqual(expectedBuffer, receivedBuffer)\n ) {\n throw new RobotRockWebhookError(\"Webhook signature verification failed\", \"INVALID_SIGNATURE\");\n }\n}\n\nfunction normalizeHeaders(headers: Headers): Record<string, string> {\n const result: Record<string, string> = {};\n headers.forEach((value, key) => {\n result[key] = value;\n });\n return result;\n}\n"],"mappings":"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","names":[]}
@@ -335,7 +335,10 @@ declare const taskContextSchema: z.ZodObject<{
335
335
  } | undefined;
336
336
  version?: 2 | undefined;
337
337
  }>;
338
- /** Assignment targets at task create (not stored in task context JSON). */
338
+ /**
339
+ * Assignment targets at task create (not stored in task context JSON).
340
+ * Unknown user emails are auto-provisioned as assignee memberships (not full team seats).
341
+ */
339
342
  declare const assignToSchema: z.ZodEffects<z.ZodObject<{
340
343
  users: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
341
344
  groups: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
@@ -457,6 +460,11 @@ declare const createTaskBodySchema: z.ZodObject<{
457
460
  users?: string[] | undefined;
458
461
  groups?: string[] | undefined;
459
462
  }>>;
463
+ /**
464
+ * Groups related tasks together. When omitted, the server generates one and
465
+ * returns it so the caller can reuse it on later tasks in the same thread.
466
+ */
467
+ threadId: z.ZodOptional<z.ZodString>;
460
468
  }, "strip", z.ZodTypeAny, {
461
469
  type: string;
462
470
  name: string;
@@ -490,6 +498,7 @@ declare const createTaskBodySchema: z.ZodObject<{
490
498
  users?: string[] | undefined;
491
499
  groups?: string[] | undefined;
492
500
  } | undefined;
501
+ threadId?: string | undefined;
493
502
  }, {
494
503
  type: string;
495
504
  name: string;
@@ -523,6 +532,7 @@ declare const createTaskBodySchema: z.ZodObject<{
523
532
  users?: string[] | undefined;
524
533
  groups?: string[] | undefined;
525
534
  } | undefined;
535
+ threadId?: string | undefined;
526
536
  }>;
527
537
  type AssignToInput = z.infer<typeof assignToSchema>;
528
538
  type CreateTaskBodyInput = z.input<typeof createTaskBodySchema>;
@@ -571,6 +581,7 @@ interface TaskResponse {
571
581
  success: boolean;
572
582
  task: {
573
583
  taskId: string;
584
+ threadId: string;
574
585
  status: "pending" | "open";
575
586
  context: TaskContext;
576
587
  validUntil: string;
@@ -580,6 +591,7 @@ interface TaskResponse {
580
591
  }
581
592
  interface Task {
582
593
  id: string;
594
+ threadId?: string;
583
595
  createdAt: Date;
584
596
  status: TaskStatus;
585
597
  context: TaskContext;
@@ -2,7 +2,7 @@ import {
2
2
  assignToSchema,
3
3
  createTaskBodySchema,
4
4
  taskContextSchema
5
- } from "../chunk-7FVE6OYZ.js";
5
+ } from "../chunk-LXM7VS4Q.js";
6
6
  export {
7
7
  assignToSchema,
8
8
  createTaskBodySchema,
@@ -1,5 +1,5 @@
1
1
  import * as _trigger_dev_sdk from '@trigger.dev/sdk';
2
- import { S as SendToHumanActionInput, a as SendToHumanInput } from '../client-agOgTJob.js';
2
+ import { S as SendToHumanActionInput, a as SendToHumanInput } from '../client-Dhk9qxhL.js';
3
3
  export { R as RobotRockHandlerWebhookPayload } from '../handler-webhook-BqEi6Bk-.js';
4
4
  export { ApprovalResult, DiscriminatedApprovalResult, TaskContextInput, TaskResult } from '../schemas/index.js';
5
5
  import 'zod';
@@ -6,8 +6,8 @@ import {
6
6
  createClient,
7
7
  resolveRobotRockConfig,
8
8
  toDiscriminatedApprovalResult
9
- } from "../chunk-THVGHUTX.js";
10
- import "../chunk-7FVE6OYZ.js";
9
+ } from "../chunk-KOXJCIST.js";
10
+ import "../chunk-LXM7VS4Q.js";
11
11
 
12
12
  // src/trigger/index.ts
13
13
  import { task, wait } from "@trigger.dev/sdk";
@@ -1,6 +1,6 @@
1
1
  import { DiscriminatedApprovalResult } from '../schemas/index.js';
2
2
  export { ApprovalResult, TaskContextInput, TaskResult } from '../schemas/index.js';
3
- import { S as SendToHumanActionInput, a as SendToHumanInput } from '../client-agOgTJob.js';
3
+ import { S as SendToHumanActionInput, a as SendToHumanInput } from '../client-Dhk9qxhL.js';
4
4
  export { R as RobotRockHandlerWebhookPayload } from '../handler-webhook-BqEi6Bk-.js';
5
5
  import 'zod';
6
6
 
@@ -7,11 +7,11 @@ import {
7
7
  createClient,
8
8
  resolveRobotRockConfig,
9
9
  toDiscriminatedApprovalResult
10
- } from "../chunk-THVGHUTX.js";
10
+ } from "../chunk-KOXJCIST.js";
11
11
  import {
12
12
  __callDispose,
13
13
  __using
14
- } from "../chunk-7FVE6OYZ.js";
14
+ } from "../chunk-LXM7VS4Q.js";
15
15
 
16
16
  // src/workflow/index.ts
17
17
  import { createWebhook, sleep } from "workflow";
@@ -1,7 +1,7 @@
1
1
  import * as ai from 'ai';
2
2
  import { ToolApprovalResponse } from 'ai';
3
3
  import { z } from 'zod';
4
- import { b as RobotRock, a as SendToHumanInput, S as SendToHumanActionInput } from './client-agOgTJob.js';
4
+ import { b as RobotRock, a as SendToHumanInput, S as SendToHumanActionInput } from './client-Dhk9qxhL.js';
5
5
  import { DiscriminatedApprovalResult } from './schemas/index.js';
6
6
 
7
7
  declare const APPROVE_BY_HUMAN_ACTIONS$1: readonly [{
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "robotrock",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Human-in-the-loop approval workflows for AI agents",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -41,7 +41,7 @@
41
41
  "dev": "tsup --watch",
42
42
  "typecheck": "tsc --noEmit",
43
43
  "test": "vitest run",
44
- "lint": "eslint src/",
44
+ "lint": "eslint .",
45
45
  "clean": "rm -rf dist"
46
46
  },
47
47
  "keywords": [
@@ -67,6 +67,7 @@
67
67
  "@trigger.dev/sdk": "4.4.6",
68
68
  "@types/node": "^25.0.3",
69
69
  "ai": "^6.0.196",
70
+ "eslint": "^9.39.2",
70
71
  "tsup": "^8.0.0",
71
72
  "typescript": "^5.9.3",
72
73
  "workflow": "^4.3.1"
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/schemas/index.ts"],"sourcesContent":["import { z } from \"zod\";\n\nexport interface JSONSchema7 {\n $id?: string;\n $ref?: string;\n $schema?: string;\n $comment?: string;\n type?: JSONSchema7TypeName | JSONSchema7TypeName[];\n enum?: unknown[];\n const?: unknown;\n multipleOf?: number;\n maximum?: number;\n exclusiveMaximum?: number;\n minimum?: number;\n exclusiveMinimum?: number;\n maxLength?: number;\n minLength?: number;\n pattern?: string;\n format?: string;\n items?: JSONSchema7 | JSONSchema7[];\n additionalItems?: JSONSchema7 | boolean;\n maxItems?: number;\n minItems?: number;\n uniqueItems?: boolean;\n contains?: JSONSchema7;\n maxProperties?: number;\n minProperties?: number;\n required?: string[];\n properties?: Record<string, JSONSchema7>;\n patternProperties?: Record<string, JSONSchema7>;\n additionalProperties?: JSONSchema7 | boolean;\n dependencies?: Record<string, JSONSchema7 | string[]>;\n propertyNames?: JSONSchema7;\n if?: JSONSchema7;\n then?: JSONSchema7;\n else?: JSONSchema7;\n allOf?: JSONSchema7[];\n anyOf?: JSONSchema7[];\n oneOf?: JSONSchema7[];\n not?: JSONSchema7;\n title?: string;\n description?: string;\n default?: unknown;\n readOnly?: boolean;\n writeOnly?: boolean;\n examples?: unknown[];\n}\n\nexport type JSONSchema7TypeName =\n | \"string\"\n | \"number\"\n | \"integer\"\n | \"boolean\"\n | \"object\"\n | \"array\"\n | \"null\";\n\nexport type ExtendedJSONSchema7 = JSONSchema7 & {\n enumNames?: string[];\n [key: string]: unknown;\n};\n\nexport type UiSchema = {\n \"ui:widget\"?: string;\n \"ui:title\"?: string;\n \"ui:description\"?: string;\n \"ui:placeholder\"?: string;\n \"ui:options\"?: Record<string, unknown>;\n [key: string]: unknown;\n};\n\nconst safeUrlSchema = z\n .string()\n .refine((url) => url.startsWith(\"http://\") || url.startsWith(\"https://\"), {\n message: \"URL must start with http:// or https://\",\n });\n\nconst jsonSchema7Schema = z.custom<ExtendedJSONSchema7>(\n (val) => typeof val === \"object\" && val !== null,\n { message: \"Must be a valid JSON Schema object\" }\n);\n\nconst uiSchemaSchema = z.custom<UiSchema>((val) => typeof val === \"object\" && val !== null, {\n message: \"Must be a valid UiSchema object\",\n});\n\nconst webhookHandlerSchema = z.object({\n type: z.literal(\"webhook\"),\n url: safeUrlSchema,\n headers: z.record(z.string()),\n});\n\nconst triggerHandlerSchema = webhookHandlerSchema.extend({\n type: z.literal(\"trigger\"),\n tokenId: z.string().min(1),\n});\n\nconst handlerSchema = z.discriminatedUnion(\"type\", [webhookHandlerSchema, triggerHandlerSchema]);\n\nconst taskActionSchema = z.object({\n id: z.string().min(1),\n title: z.string().min(1),\n description: z.string().optional(),\n schema: jsonSchema7Schema.optional(),\n ui: uiSchemaSchema.optional(),\n data: z.record(z.unknown()).optional(),\n handlers: z.array(handlerSchema).min(1).optional(),\n});\n\nconst uiFieldSchemaSchema: z.ZodType<Record<string, unknown>> = z\n .object({\n \"ui:widget\": z.string().optional(),\n \"ui:title\": z.string().optional(),\n \"ui:description\": z.string().optional(),\n \"ui:options\": z.record(z.unknown()).optional(),\n items: z.lazy(() => z.record(uiFieldSchemaSchema)).optional(),\n })\n .passthrough();\n\nconst contextUiSchema = z.record(uiFieldSchemaSchema).optional();\n\nconst contextDataSchema = z\n .object({\n data: z.record(z.unknown()),\n ui: contextUiSchema,\n })\n .optional();\n\nexport const taskContextSchema = z.object({\n app: z.string().min(1).optional(),\n type: z.string().min(1),\n name: z.string().min(1),\n description: z.string().optional(),\n validUntil: z.string().optional(),\n context: contextDataSchema,\n version: z.literal(2).optional(),\n actions: z.array(taskActionSchema).min(1, \"At least one action is required\"),\n});\n\n/** Assignment targets at task create (not stored in task context JSON). */\nexport const assignToSchema = z\n .object({\n users: z.array(z.string().email()).optional(),\n groups: z.array(z.string().min(1)).optional(),\n })\n .refine(\n (data) => {\n const groups = data.groups ?? [];\n if (groups.includes(\"all\") && groups.length > 1) {\n return false;\n }\n return true;\n },\n { message: 'Cannot combine \"all\" with other group slugs' }\n );\n\nexport const createTaskBodySchema = taskContextSchema.extend({\n assignTo: assignToSchema.optional(),\n});\n\nexport type AssignToInput = z.infer<typeof assignToSchema>;\nexport type CreateTaskBodyInput = z.input<typeof createTaskBodySchema>;\nexport type CreateTaskBody = z.output<typeof createTaskBodySchema>;\nexport type TaskContextInput = z.input<typeof taskContextSchema>;\nexport type TaskContextOutput = z.output<typeof taskContextSchema>;\nexport type TaskContext = TaskContextOutput;\nexport type TaskAction = z.infer<typeof taskActionSchema>;\nexport type WebhookHandler = z.infer<typeof webhookHandlerSchema>;\nexport type TriggerHandler = z.infer<typeof triggerHandlerSchema>;\nexport type Handler = z.infer<typeof handlerSchema>;\n\ntype InferObjectProperties<\n Props,\n Req extends PropertyKey,\n> = Props extends Record<string, unknown>\n ? ({\n [K in keyof Props as K extends Req ? K : never]-?: InferJsonSchema7<Props[K]>;\n } & {\n [K in keyof Props as K extends Req ? never : K]?: InferJsonSchema7<Props[K]>;\n } extends infer O\n ? { [K in keyof O]: O[K] }\n : never)\n : Record<string, unknown>;\n\ntype RequiredKeys<S> =\n S extends { readonly required: readonly string[] } ? S[\"required\"][number] : never;\n\nexport type InferJsonSchema7<S> = [S] extends [undefined]\n ? Record<string, never>\n : S extends { readonly const: infer C }\n ? C\n : S extends {\n readonly enum: readonly (infer E)[];\n }\n ? E\n : S extends {\n readonly type: \"object\";\n readonly properties?: infer Props;\n }\n ? InferObjectProperties<Props, RequiredKeys<S>>\n : S extends {\n readonly type: \"object\";\n readonly properties?: undefined;\n }\n ? Record<string, unknown>\n : S extends {\n readonly type: \"array\";\n readonly items?: infer Items;\n }\n ? Items extends readonly unknown[]\n ? InferJsonSchema7<Items[number]>[]\n : Items extends object\n ? InferJsonSchema7<Items>[]\n : unknown[]\n : S extends { readonly type: \"string\" }\n ? string\n : S extends { readonly type: \"number\" } | { readonly type: \"integer\" }\n ? number\n : S extends { readonly type: \"boolean\" }\n ? boolean\n : unknown;\n\nexport type TaskStatus = \"pending\" | \"open\" | \"handled\" | \"expired\";\n\nexport interface TaskResponse {\n success: boolean;\n task: {\n taskId: string;\n status: \"pending\" | \"open\";\n context: TaskContext;\n validUntil: string;\n submittedAt: string;\n };\n message: string;\n}\n\nexport interface Task {\n id: string;\n createdAt: Date;\n status: TaskStatus;\n context: TaskContext;\n validUntil: number;\n handledAt?: number;\n handled?: {\n action: {\n id: string;\n data: unknown;\n };\n handledBy?: string;\n userId?: string;\n token?: string;\n };\n}\n\nexport type InferActionData<T extends { schema?: unknown }> = [\n Exclude<T[\"schema\"], undefined>,\n] extends [\n never,\n]\n ? Record<string, never>\n : Exclude<T[\"schema\"], undefined> extends infer S\n ? InferJsonSchema7<S>\n : Record<string, never>;\n\nexport type TupleElementIndices<T extends readonly unknown[]> = T extends readonly [\n unknown,\n ...unknown[],\n]\n ? Exclude<keyof T, keyof unknown[]>\n : number;\n\nexport interface TaskResult<T = Record<string, unknown>> {\n actionId: string;\n data: T;\n handledBy?: string;\n handledAt: Date;\n}\n\nexport interface ApprovalResult<T = Record<string, unknown>> extends TaskResult<T> {\n taskId: string;\n}\n\nexport type DiscriminatedApprovalResult<\n TActions extends readonly { id: string; schema?: unknown }[],\n> = {\n [I in TupleElementIndices<TActions>]: TActions[I] extends { id: string; schema?: unknown }\n ? Omit<ApprovalResult<InferActionData<TActions[I]>>, \"actionId\" | \"data\"> & {\n actionId: TActions[I][\"id\"];\n data: InferActionData<TActions[I]>;\n }\n : never\n}[TupleElementIndices<TActions>];\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/approval-result.ts","../src/client.ts","../src/env.ts"],"sourcesContent":["import type { DiscriminatedApprovalResult, Task } from \"./schemas/index.js\";\n\nexport class TaskTimeoutError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"TaskTimeoutError\";\n }\n}\n\nexport class TaskExpiredError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"TaskExpiredError\";\n }\n}\n\n/**\n * Map a handled API task to a discriminated approval result.\n * Runtime validation is minimal; TypeScript narrows via `task.actions` at the call site.\n */\nexport function toDiscriminatedApprovalResult<A extends readonly { id: string; schema?: unknown }[]>(\n actions: A,\n task: Task\n): DiscriminatedApprovalResult<A> {\n void actions;\n\n if (!task.handled) {\n throw new Error(\"Task has no handled result\");\n }\n\n return {\n actionId: task.handled.action.id,\n data: task.handled.action.data,\n handledBy: task.handled.handledBy,\n handledAt: new Date(task.handledAt ?? Date.now()),\n taskId: task.id,\n } as DiscriminatedApprovalResult<A>;\n}\n","import {\n type AssignToInput,\n type TaskContextInput,\n createTaskBodySchema,\n} from \"./schemas/index.js\";\nimport {\n TaskExpiredError,\n TaskTimeoutError,\n toDiscriminatedApprovalResult,\n} from \"./approval-result.js\";\nimport type { DiscriminatedApprovalResult, Task, TaskResponse } from \"./schemas/index.js\";\n\nexport type RobotRockWebhookConfig = {\n url: string;\n headers?: Record<string, string>;\n};\n\nexport interface RobotRockPollingOptions {\n /** Poll interval when no webhook is configured (ms). @default 2000 */\n intervalMs?: number;\n /**\n * Max time to poll when no webhook is configured (ms).\n * Polling also stops when the task's `validUntil` passes, whichever is sooner.\n * @default 86400000 (24h)\n */\n timeoutMs?: number;\n}\n\ntype RobotRockClientBaseConfig = {\n /** Optional override for API key. Falls back to ROBOTROCK_API_KEY. */\n apiKey?: string;\n /**\n * Base URL for the RobotRock API\n * @default \"https://api.robotrock.com/v1\"\n */\n baseUrl?: string;\n /**\n * Default inbox app bucket for every task from this client.\n * When omitted, the API uses your API key name.\n */\n app?: string;\n /**\n * Task context format version sent on every `sendToHuman` request.\n * @default 2\n */\n version?: 2;\n};\n\n/** Client config with a webhook (mutually exclusive with `polling`). */\nexport type RobotRockWebhookClientConfig = RobotRockClientBaseConfig & {\n webhook: RobotRockWebhookConfig;\n polling?: never;\n};\n\n/** Client config without a webhook; optional `polling` controls the wait loop. */\nexport type RobotRockPollingClientConfig = RobotRockClientBaseConfig & {\n webhook?: never;\n polling?: RobotRockPollingOptions;\n};\n\nexport type RobotRockConfig = RobotRockWebhookClientConfig | RobotRockPollingClientConfig;\n\nexport type SendToHumanActionInput = Omit<TaskContextInput[\"actions\"][number], \"handlers\">;\n\nexport type SendToHumanValidUntil = Date | string;\n\nexport type SendToHumanInput<\n A extends readonly SendToHumanActionInput[] = readonly SendToHumanActionInput[],\n> = Omit<TaskContextInput, \"app\" | \"actions\" | \"version\" | \"validUntil\"> & {\n actions: A;\n /** Task deadline; serialized to an ISO 8601 string on the wire. */\n validUntil?: SendToHumanValidUntil;\n /** Optional idempotency key to prevent duplicate tasks */\n idempotencyKey?: string;\n /** Assign to tenant users (email) and/or groups (slug). Narrows inbox visibility. */\n assignTo?: AssignToInput;\n};\n\ntype SendToHumanWithAppInput<\n A extends readonly SendToHumanActionInput[] = readonly SendToHumanActionInput[],\n> = (SendToHumanInput<A> | Readonly<SendToHumanInput<A>>) & {\n /** Inbox app bucket. Overrides the client `app` when set. */\n app?: string;\n};\n\nexport type SendToHumanResult<\n A extends readonly SendToHumanActionInput[] = readonly SendToHumanActionInput[],\n> =\n | {\n mode: \"created\";\n task: TaskResponse[\"task\"];\n }\n | ({\n mode: \"handled\";\n task: TaskResponse[\"task\"];\n } & DiscriminatedApprovalResult<A>);\n\nconst DEFAULT_POLL_INTERVAL_MS = 2_000;\nconst DEFAULT_TIMEOUT_MS = 24 * 60 * 60 * 1_000;\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nfunction parseValidUntilMs(value: string | number | Date | undefined): number | undefined {\n if (value === undefined) {\n return undefined;\n }\n\n if (value instanceof Date) {\n const ms = value.getTime();\n return Number.isNaN(ms) ? undefined : ms;\n }\n\n if (typeof value === \"number\") {\n return Number.isFinite(value) ? value : undefined;\n }\n\n const parsed = Date.parse(value);\n return Number.isNaN(parsed) ? undefined : parsed;\n}\n\nfunction serializeValidUntil(value: SendToHumanValidUntil): string {\n if (value instanceof Date) {\n const ms = value.getTime();\n if (Number.isNaN(ms)) {\n throw new RobotRockError(\"Invalid validUntil: Date is invalid\", 400);\n }\n return value.toISOString();\n }\n\n if (typeof value === \"string\" && !Number.isNaN(Date.parse(value))) {\n return new Date(value).toISOString();\n }\n\n throw new RobotRockError(\"Invalid validUntil: expected a Date or parseable date string\", 400);\n}\n\nexport class RobotRockError extends Error {\n constructor(\n message: string,\n public readonly statusCode: number,\n public readonly response?: unknown\n ) {\n super(message);\n this.name = \"RobotRockError\";\n }\n}\n\n/**\n * RobotRock API client for creating and querying human-in-the-loop tasks.\n */\nexport class RobotRock {\n private readonly apiKey: string;\n private readonly baseUrl: string;\n private readonly app?: string;\n private readonly version: 2;\n private readonly webhook?: RobotRockWebhookConfig;\n private readonly polling: RobotRockPollingOptions;\n\n constructor(config: RobotRockConfig) {\n if (config.webhook && config.polling) {\n throw new Error(\n \"RobotRock client cannot configure both webhook and polling. Use webhook for callbacks or polling to block until handled.\"\n );\n }\n\n const apiKey = config.apiKey ?? process.env.ROBOTROCK_API_KEY;\n if (!apiKey) {\n throw new Error(\n \"RobotRock API key is required. Set ROBOTROCK_API_KEY or pass apiKey when creating the client.\"\n );\n }\n this.apiKey = apiKey;\n const rawBase = config.baseUrl ?? \"https://api.robotrock.com/v1\";\n this.baseUrl = rawBase.replace(/\\/+$/, \"\");\n this.app = config.app;\n this.version = config.version ?? 2;\n this.webhook = config.webhook;\n this.polling = config.polling ?? {};\n }\n\n /**\n * Create a task via POST /v1 without waiting for a human response.\n */\n async createTask<const A extends readonly SendToHumanActionInput[]>(\n task: SendToHumanWithAppInput<A>\n ): Promise<TaskResponse[\"task\"]> {\n const normalizedTask = normalizeSendToHumanInput(task, {\n webhook: this.webhook,\n app: this.app,\n version: this.version,\n });\n const bodyPayload = {\n ...normalizedTask,\n ...(task.assignTo !== undefined ? { assignTo: task.assignTo } : {}),\n };\n const validation = createTaskBodySchema.safeParse(bodyPayload);\n if (!validation.success) {\n throw new RobotRockError(\n `Invalid task: ${validation.error.errors[0]?.message}`,\n 400,\n validation.error.errors\n );\n }\n\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n \"X-Api-Key\": this.apiKey,\n };\n\n if (task.idempotencyKey) {\n headers[\"Idempotency-Key\"] = task.idempotencyKey;\n }\n\n const response = await fetch(`${this.baseUrl}/`, {\n method: \"POST\",\n headers,\n body: JSON.stringify(validation.data),\n });\n\n const data = await parseResponseBody(response);\n\n if (!response.ok) {\n throw new RobotRockError(\n getErrorMessage(data, \"Failed to create task\"),\n response.status,\n data\n );\n }\n\n return (data as unknown as TaskResponse).task;\n }\n\n async sendToHuman<const A extends readonly SendToHumanActionInput[]>(\n task: SendToHumanWithAppInput<A>\n ): Promise<SendToHumanResult<A>> {\n const normalizedTask = normalizeSendToHumanInput(task, {\n webhook: this.webhook,\n app: this.app,\n version: this.version,\n });\n const createdTaskTask = await this.createTask(task);\n const hasHandlers = normalizedTask.actions.some(\n (action) => Array.isArray(action.handlers) && action.handlers.length > 0\n );\n\n if (hasHandlers) {\n return {\n mode: \"created\",\n task: createdTaskTask,\n };\n }\n\n const timeoutMs = this.polling.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n const pollIntervalMs = this.polling.intervalMs ?? DEFAULT_POLL_INTERVAL_MS;\n const pollingDeadline = Date.now() + timeoutMs;\n const validUntilMs = parseValidUntilMs(createdTaskTask.validUntil);\n const deadline =\n validUntilMs !== undefined ? Math.min(pollingDeadline, validUntilMs) : pollingDeadline;\n const taskId = createdTaskTask.taskId;\n\n while (Date.now() < deadline) {\n const existing = await this.getTask(taskId);\n\n if (existing?.status === \"handled\" && existing.handled) {\n return {\n mode: \"handled\",\n task: createdTaskTask,\n ...(toDiscriminatedApprovalResult(\n normalizedTask.actions as unknown as A,\n existing\n ) as DiscriminatedApprovalResult<A>),\n };\n }\n\n if (existing?.status === \"expired\" || (existing && Date.now() >= existing.validUntil)) {\n throw new TaskExpiredError(\"Task reached validUntil before a human completed it\");\n }\n\n const remainingMs = deadline - Date.now();\n await sleep(Math.min(pollIntervalMs, Math.max(0, remainingMs)));\n }\n\n if (validUntilMs !== undefined && Date.now() >= validUntilMs) {\n throw new TaskExpiredError(\"Task reached validUntil before a human completed it\");\n }\n\n throw new TaskTimeoutError(`No human response within ${timeoutMs}ms`);\n }\n\n /**\n * Get a task by public task id (returned as `task.taskId` from {@link sendToHuman}).\n */\n async getTask(taskId: string): Promise<Task | null> {\n const response = await fetch(`${this.baseUrl}/tasks/${taskId}`, {\n method: \"GET\",\n headers: {\n \"X-Api-Key\": this.apiKey,\n },\n });\n\n if (response.status === 404) {\n return null;\n }\n\n const data = await parseResponseBody(response);\n\n if (!response.ok) {\n throw new RobotRockError(\n getErrorMessage(data, \"Failed to get task\"),\n response.status,\n data\n );\n }\n\n return data as unknown as Task;\n }\n\n async cancelTask(taskId: string): Promise<void> {\n const response = await fetch(`${this.baseUrl}/tasks/${taskId}/cancel`, {\n method: \"POST\",\n headers: {\n \"X-Api-Key\": this.apiKey,\n },\n });\n\n if (!response.ok) {\n const data = await parseResponseBody(response);\n throw new RobotRockError(\n getErrorMessage(data, \"Failed to cancel task\"),\n response.status,\n data\n );\n }\n }\n}\n\nexport function createClient(config: RobotRockConfig): RobotRock {\n return new RobotRock(config);\n}\n\nexport function attachWebhookToActions(\n actions: readonly SendToHumanActionInput[],\n webhook: RobotRockWebhookConfig\n): TaskContextInput[\"actions\"] {\n return actions.map((action) => ({\n ...action,\n handlers: webhookToHandlers(webhook),\n }));\n}\n\nfunction webhookToHandlers(\n webhook: RobotRockWebhookConfig\n): TaskContextInput[\"actions\"][number][\"handlers\"] {\n return [\n {\n type: \"webhook\" as const,\n url: webhook.url,\n headers: webhook.headers ?? {},\n },\n ];\n}\n\nfunction normalizeSendToHumanInput<\n A extends readonly SendToHumanActionInput[] = readonly SendToHumanActionInput[],\n>(\n task: SendToHumanWithAppInput<A>,\n clientDefaults: { webhook?: RobotRockWebhookConfig; app?: string; version: 2 }\n): TaskContextInput {\n const {\n actions,\n idempotencyKey: _idempotencyKey,\n assignTo: _assignTo,\n validUntil,\n app: taskApp,\n ...rest\n } = task;\n\n const webhook = clientDefaults.webhook;\n const normalizedActions: TaskContextInput[\"actions\"] = webhook\n ? attachWebhookToActions(actions, webhook)\n : (actions as unknown as TaskContextInput[\"actions\"]);\n\n const app = taskApp ?? clientDefaults.app;\n\n return {\n ...rest,\n version: clientDefaults.version,\n ...(app ? { app } : {}),\n ...(validUntil !== undefined ? { validUntil: serializeValidUntil(validUntil) } : {}),\n actions: normalizedActions,\n };\n}\n\ntype ParsedResponseBody = Record<string, unknown> | unknown[] | string | null;\n\nasync function parseResponseBody(response: Response): Promise<ParsedResponseBody> {\n const contentType = response.headers.get(\"content-type\") ?? \"\";\n const bodyText = await response.text();\n\n if (!bodyText) {\n return null;\n }\n\n if (contentType.toLowerCase().includes(\"application/json\")) {\n try {\n return JSON.parse(bodyText) as ParsedResponseBody;\n } catch {\n // Fall through and return text body below so error messages stay useful.\n }\n }\n\n try {\n return JSON.parse(bodyText) as ParsedResponseBody;\n } catch {\n return bodyText;\n }\n}\n\nfunction getErrorMessage(data: ParsedResponseBody, fallback: string): string {\n if (data && typeof data === \"object\" && !Array.isArray(data)) {\n const maybeMessage = (data as Record<string, unknown>).message;\n if (typeof maybeMessage === \"string\" && maybeMessage.trim()) {\n return maybeMessage;\n }\n }\n\n if (typeof data === \"string\" && data.trim()) {\n const compact = data.replace(/\\s+/g, \" \").trim();\n const snippet = compact.length > 180 ? `${compact.slice(0, 180)}...` : compact;\n return `${fallback}. Server returned non-JSON response: ${snippet}`;\n }\n\n return fallback;\n}\n","import { createClient, type RobotRock, type RobotRockConfig } from \"./client.js\";\n\nconst DEFAULT_BASE_URL = \"https://api.robotrock.com/v1\";\n\n/**\n * Read RobotRock client config from environment variables.\n *\n * - `ROBOTROCK_API_KEY` (required when not passed explicitly)\n * - `ROBOTROCK_BASE_URL` or `ROBOTROCK_API_URL` (optional)\n * - `ROBOTROCK_APP` (optional inbox app bucket)\n */\nexport function resolveRobotRockConfig(\n overrides?: Partial<RobotRockConfig>\n): RobotRockConfig {\n const apiKey = overrides?.apiKey ?? process.env.ROBOTROCK_API_KEY;\n if (!apiKey) {\n throw new Error(\n \"RobotRock API key is required. Set ROBOTROCK_API_KEY or pass apiKey when creating the client.\"\n );\n }\n\n const baseUrl =\n overrides?.baseUrl ??\n process.env.ROBOTROCK_BASE_URL ??\n process.env.ROBOTROCK_API_URL ??\n DEFAULT_BASE_URL;\n\n const app = overrides?.app ?? process.env.ROBOTROCK_APP;\n\n return app ? { apiKey, baseUrl, app } : { apiKey, baseUrl };\n}\n\n/** Use an explicit client or create one from env / optional config overrides. */\nexport function resolveRobotRockClient(\n client?: RobotRock,\n configOverrides?: Partial<RobotRockConfig>\n): RobotRock {\n if (client) {\n return client;\n }\n return createClient(resolveRobotRockConfig(configOverrides));\n}\n"],"mappings":";;;;;AAEO,IAAM,mBAAN,cAA+B,MAAM;AAAA,EAC1C,YAAY,SAAiB;AAC3B,UAAM,OAAO;AACb,SAAK,OAAO;AAAA,EACd;AACF;AAEO,IAAM,mBAAN,cAA+B,MAAM;AAAA,EAC1C,YAAY,SAAiB;AAC3B,UAAM,OAAO;AACb,SAAK,OAAO;AAAA,EACd;AACF;AAMO,SAAS,8BACd,SACA,MACgC;AAChC,OAAK;AAEL,MAAI,CAAC,KAAK,SAAS;AACjB,UAAM,IAAI,MAAM,4BAA4B;AAAA,EAC9C;AAEA,SAAO;AAAA,IACL,UAAU,KAAK,QAAQ,OAAO;AAAA,IAC9B,MAAM,KAAK,QAAQ,OAAO;AAAA,IAC1B,WAAW,KAAK,QAAQ;AAAA,IACxB,WAAW,IAAI,KAAK,KAAK,aAAa,KAAK,IAAI,CAAC;AAAA,IAChD,QAAQ,KAAK;AAAA,EACf;AACF;;;AC4DA,IAAM,2BAA2B;AACjC,IAAM,qBAAqB,KAAK,KAAK,KAAK;AAE1C,SAAS,MAAM,IAA2B;AACxC,SAAO,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,EAAE,CAAC;AACzD;AAEA,SAAS,kBAAkB,OAA+D;AACxF,MAAI,UAAU,QAAW;AACvB,WAAO;AAAA,EACT;AAEA,MAAI,iBAAiB,MAAM;AACzB,UAAM,KAAK,MAAM,QAAQ;AACzB,WAAO,OAAO,MAAM,EAAE,IAAI,SAAY;AAAA,EACxC;AAEA,MAAI,OAAO,UAAU,UAAU;AAC7B,WAAO,OAAO,SAAS,KAAK,IAAI,QAAQ;AAAA,EAC1C;AAEA,QAAM,SAAS,KAAK,MAAM,KAAK;AAC/B,SAAO,OAAO,MAAM,MAAM,IAAI,SAAY;AAC5C;AAEA,SAAS,oBAAoB,OAAsC;AACjE,MAAI,iBAAiB,MAAM;AACzB,UAAM,KAAK,MAAM,QAAQ;AACzB,QAAI,OAAO,MAAM,EAAE,GAAG;AACpB,YAAM,IAAI,eAAe,uCAAuC,GAAG;AAAA,IACrE;AACA,WAAO,MAAM,YAAY;AAAA,EAC3B;AAEA,MAAI,OAAO,UAAU,YAAY,CAAC,OAAO,MAAM,KAAK,MAAM,KAAK,CAAC,GAAG;AACjE,WAAO,IAAI,KAAK,KAAK,EAAE,YAAY;AAAA,EACrC;AAEA,QAAM,IAAI,eAAe,gEAAgE,GAAG;AAC9F;AAEO,IAAM,iBAAN,cAA6B,MAAM;AAAA,EACxC,YACE,SACgB,YACA,UAChB;AACA,UAAM,OAAO;AAHG;AACA;AAGhB,SAAK,OAAO;AAAA,EACd;AACF;AAKO,IAAM,YAAN,MAAgB;AAAA,EACJ;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEjB,YAAY,QAAyB;AACnC,QAAI,OAAO,WAAW,OAAO,SAAS;AACpC,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAEA,UAAM,SAAS,OAAO,UAAU,QAAQ,IAAI;AAC5C,QAAI,CAAC,QAAQ;AACX,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AACA,SAAK,SAAS;AACd,UAAM,UAAU,OAAO,WAAW;AAClC,SAAK,UAAU,QAAQ,QAAQ,QAAQ,EAAE;AACzC,SAAK,MAAM,OAAO;AAClB,SAAK,UAAU,OAAO,WAAW;AACjC,SAAK,UAAU,OAAO;AACtB,SAAK,UAAU,OAAO,WAAW,CAAC;AAAA,EACpC;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,WACJ,MAC+B;AAC/B,UAAM,iBAAiB,0BAA0B,MAAM;AAAA,MACrD,SAAS,KAAK;AAAA,MACd,KAAK,KAAK;AAAA,MACV,SAAS,KAAK;AAAA,IAChB,CAAC;AACD,UAAM,cAAc;AAAA,MAClB,GAAG;AAAA,MACH,GAAI,KAAK,aAAa,SAAY,EAAE,UAAU,KAAK,SAAS,IAAI,CAAC;AAAA,IACnE;AACA,UAAM,aAAa,qBAAqB,UAAU,WAAW;AAC7D,QAAI,CAAC,WAAW,SAAS;AACvB,YAAM,IAAI;AAAA,QACR,iBAAiB,WAAW,MAAM,OAAO,CAAC,GAAG,OAAO;AAAA,QACpD;AAAA,QACA,WAAW,MAAM;AAAA,MACnB;AAAA,IACF;AAEA,UAAM,UAAkC;AAAA,MACtC,gBAAgB;AAAA,MAChB,aAAa,KAAK;AAAA,IACpB;AAEA,QAAI,KAAK,gBAAgB;AACvB,cAAQ,iBAAiB,IAAI,KAAK;AAAA,IACpC;AAEA,UAAM,WAAW,MAAM,MAAM,GAAG,KAAK,OAAO,KAAK;AAAA,MAC/C,QAAQ;AAAA,MACR;AAAA,MACA,MAAM,KAAK,UAAU,WAAW,IAAI;AAAA,IACtC,CAAC;AAED,UAAM,OAAO,MAAM,kBAAkB,QAAQ;AAE7C,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,IAAI;AAAA,QACR,gBAAgB,MAAM,uBAAuB;AAAA,QAC7C,SAAS;AAAA,QACT;AAAA,MACF;AAAA,IACF;AAEA,WAAQ,KAAiC;AAAA,EAC3C;AAAA,EAEA,MAAM,YACJ,MAC+B;AAC/B,UAAM,iBAAiB,0BAA0B,MAAM;AAAA,MACrD,SAAS,KAAK;AAAA,MACd,KAAK,KAAK;AAAA,MACV,SAAS,KAAK;AAAA,IAChB,CAAC;AACD,UAAM,kBAAkB,MAAM,KAAK,WAAW,IAAI;AAClD,UAAM,cAAc,eAAe,QAAQ;AAAA,MACzC,CAAC,WAAW,MAAM,QAAQ,OAAO,QAAQ,KAAK,OAAO,SAAS,SAAS;AAAA,IACzE;AAEA,QAAI,aAAa;AACf,aAAO;AAAA,QACL,MAAM;AAAA,QACN,MAAM;AAAA,MACR;AAAA,IACF;AAEA,UAAM,YAAY,KAAK,QAAQ,aAAa;AAC5C,UAAM,iBAAiB,KAAK,QAAQ,cAAc;AAClD,UAAM,kBAAkB,KAAK,IAAI,IAAI;AACrC,UAAM,eAAe,kBAAkB,gBAAgB,UAAU;AACjE,UAAM,WACJ,iBAAiB,SAAY,KAAK,IAAI,iBAAiB,YAAY,IAAI;AACzE,UAAM,SAAS,gBAAgB;AAE/B,WAAO,KAAK,IAAI,IAAI,UAAU;AAC5B,YAAM,WAAW,MAAM,KAAK,QAAQ,MAAM;AAE1C,UAAI,UAAU,WAAW,aAAa,SAAS,SAAS;AACtD,eAAO;AAAA,UACL,MAAM;AAAA,UACN,MAAM;AAAA,UACN,GAAI;AAAA,YACF,eAAe;AAAA,YACf;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAEA,UAAI,UAAU,WAAW,aAAc,YAAY,KAAK,IAAI,KAAK,SAAS,YAAa;AACrF,cAAM,IAAI,iBAAiB,qDAAqD;AAAA,MAClF;AAEA,YAAM,cAAc,WAAW,KAAK,IAAI;AACxC,YAAM,MAAM,KAAK,IAAI,gBAAgB,KAAK,IAAI,GAAG,WAAW,CAAC,CAAC;AAAA,IAChE;AAEA,QAAI,iBAAiB,UAAa,KAAK,IAAI,KAAK,cAAc;AAC5D,YAAM,IAAI,iBAAiB,qDAAqD;AAAA,IAClF;AAEA,UAAM,IAAI,iBAAiB,4BAA4B,SAAS,IAAI;AAAA,EACtE;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,QAAQ,QAAsC;AAClD,UAAM,WAAW,MAAM,MAAM,GAAG,KAAK,OAAO,UAAU,MAAM,IAAI;AAAA,MAC9D,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,aAAa,KAAK;AAAA,MACpB;AAAA,IACF,CAAC;AAED,QAAI,SAAS,WAAW,KAAK;AAC3B,aAAO;AAAA,IACT;AAEA,UAAM,OAAO,MAAM,kBAAkB,QAAQ;AAE7C,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,IAAI;AAAA,QACR,gBAAgB,MAAM,oBAAoB;AAAA,QAC1C,SAAS;AAAA,QACT;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,WAAW,QAA+B;AAC9C,UAAM,WAAW,MAAM,MAAM,GAAG,KAAK,OAAO,UAAU,MAAM,WAAW;AAAA,MACrE,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,aAAa,KAAK;AAAA,MACpB;AAAA,IACF,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,OAAO,MAAM,kBAAkB,QAAQ;AAC7C,YAAM,IAAI;AAAA,QACR,gBAAgB,MAAM,uBAAuB;AAAA,QAC7C,SAAS;AAAA,QACT;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAEO,SAAS,aAAa,QAAoC;AAC/D,SAAO,IAAI,UAAU,MAAM;AAC7B;AAEO,SAAS,uBACd,SACA,SAC6B;AAC7B,SAAO,QAAQ,IAAI,CAAC,YAAY;AAAA,IAC9B,GAAG;AAAA,IACH,UAAU,kBAAkB,OAAO;AAAA,EACrC,EAAE;AACJ;AAEA,SAAS,kBACP,SACiD;AACjD,SAAO;AAAA,IACL;AAAA,MACE,MAAM;AAAA,MACN,KAAK,QAAQ;AAAA,MACb,SAAS,QAAQ,WAAW,CAAC;AAAA,IAC/B;AAAA,EACF;AACF;AAEA,SAAS,0BAGP,MACA,gBACkB;AAClB,QAAM;AAAA,IACJ;AAAA,IACA,gBAAgB;AAAA,IAChB,UAAU;AAAA,IACV;AAAA,IACA,KAAK;AAAA,IACL,GAAG;AAAA,EACL,IAAI;AAEJ,QAAM,UAAU,eAAe;AAC/B,QAAM,oBAAiD,UACnD,uBAAuB,SAAS,OAAO,IACtC;AAEL,QAAM,MAAM,WAAW,eAAe;AAEtC,SAAO;AAAA,IACL,GAAG;AAAA,IACH,SAAS,eAAe;AAAA,IACxB,GAAI,MAAM,EAAE,IAAI,IAAI,CAAC;AAAA,IACrB,GAAI,eAAe,SAAY,EAAE,YAAY,oBAAoB,UAAU,EAAE,IAAI,CAAC;AAAA,IAClF,SAAS;AAAA,EACX;AACF;AAIA,eAAe,kBAAkB,UAAiD;AAChF,QAAM,cAAc,SAAS,QAAQ,IAAI,cAAc,KAAK;AAC5D,QAAM,WAAW,MAAM,SAAS,KAAK;AAErC,MAAI,CAAC,UAAU;AACb,WAAO;AAAA,EACT;AAEA,MAAI,YAAY,YAAY,EAAE,SAAS,kBAAkB,GAAG;AAC1D,QAAI;AACF,aAAO,KAAK,MAAM,QAAQ;AAAA,IAC5B,QAAQ;AAAA,IAER;AAAA,EACF;AAEA,MAAI;AACF,WAAO,KAAK,MAAM,QAAQ;AAAA,EAC5B,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,gBAAgB,MAA0B,UAA0B;AAC3E,MAAI,QAAQ,OAAO,SAAS,YAAY,CAAC,MAAM,QAAQ,IAAI,GAAG;AAC5D,UAAM,eAAgB,KAAiC;AACvD,QAAI,OAAO,iBAAiB,YAAY,aAAa,KAAK,GAAG;AAC3D,aAAO;AAAA,IACT;AAAA,EACF;AAEA,MAAI,OAAO,SAAS,YAAY,KAAK,KAAK,GAAG;AAC3C,UAAM,UAAU,KAAK,QAAQ,QAAQ,GAAG,EAAE,KAAK;AAC/C,UAAM,UAAU,QAAQ,SAAS,MAAM,GAAG,QAAQ,MAAM,GAAG,GAAG,CAAC,QAAQ;AACvE,WAAO,GAAG,QAAQ,wCAAwC,OAAO;AAAA,EACnE;AAEA,SAAO;AACT;;;ACjbA,IAAM,mBAAmB;AASlB,SAAS,uBACd,WACiB;AACjB,QAAM,SAAS,WAAW,UAAU,QAAQ,IAAI;AAChD,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAEA,QAAM,UACJ,WAAW,WACX,QAAQ,IAAI,sBACZ,QAAQ,IAAI,qBACZ;AAEF,QAAM,MAAM,WAAW,OAAO,QAAQ,IAAI;AAE1C,SAAO,MAAM,EAAE,QAAQ,SAAS,IAAI,IAAI,EAAE,QAAQ,QAAQ;AAC5D;AAGO,SAAS,uBACd,QACA,iBACW;AACX,MAAI,QAAQ;AACV,WAAO;AAAA,EACT;AACA,SAAO,aAAa,uBAAuB,eAAe,CAAC;AAC7D;","names":[]}