robotrock 0.1.0 → 0.2.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.
@@ -0,0 +1,330 @@
1
+ import {
2
+ createTaskBodySchema
3
+ } from "./chunk-7FVE6OYZ.js";
4
+
5
+ // src/approval-result.ts
6
+ var TaskTimeoutError = class extends Error {
7
+ constructor(message) {
8
+ super(message);
9
+ this.name = "TaskTimeoutError";
10
+ }
11
+ };
12
+ var TaskExpiredError = class extends Error {
13
+ constructor(message) {
14
+ super(message);
15
+ this.name = "TaskExpiredError";
16
+ }
17
+ };
18
+ function toDiscriminatedApprovalResult(actions, task) {
19
+ void actions;
20
+ if (!task.handled) {
21
+ throw new Error("Task has no handled result");
22
+ }
23
+ return {
24
+ actionId: task.handled.action.id,
25
+ data: task.handled.action.data,
26
+ handledBy: task.handled.handledBy,
27
+ handledAt: new Date(task.handledAt ?? Date.now()),
28
+ taskId: task.id
29
+ };
30
+ }
31
+
32
+ // src/client.ts
33
+ var DEFAULT_POLL_INTERVAL_MS = 2e3;
34
+ var DEFAULT_TIMEOUT_MS = 24 * 60 * 60 * 1e3;
35
+ function sleep(ms) {
36
+ return new Promise((resolve) => setTimeout(resolve, ms));
37
+ }
38
+ function parseValidUntilMs(value) {
39
+ if (value === void 0) {
40
+ return void 0;
41
+ }
42
+ if (value instanceof Date) {
43
+ const ms = value.getTime();
44
+ return Number.isNaN(ms) ? void 0 : ms;
45
+ }
46
+ if (typeof value === "number") {
47
+ return Number.isFinite(value) ? value : void 0;
48
+ }
49
+ const parsed = Date.parse(value);
50
+ return Number.isNaN(parsed) ? void 0 : parsed;
51
+ }
52
+ function serializeValidUntil(value) {
53
+ if (value instanceof Date) {
54
+ const ms = value.getTime();
55
+ if (Number.isNaN(ms)) {
56
+ throw new RobotRockError("Invalid validUntil: Date is invalid", 400);
57
+ }
58
+ return value.toISOString();
59
+ }
60
+ if (typeof value === "string" && !Number.isNaN(Date.parse(value))) {
61
+ return new Date(value).toISOString();
62
+ }
63
+ throw new RobotRockError("Invalid validUntil: expected a Date or parseable date string", 400);
64
+ }
65
+ var RobotRockError = class extends Error {
66
+ constructor(message, statusCode, response) {
67
+ super(message);
68
+ this.statusCode = statusCode;
69
+ this.response = response;
70
+ this.name = "RobotRockError";
71
+ }
72
+ };
73
+ var RobotRock = class {
74
+ apiKey;
75
+ baseUrl;
76
+ app;
77
+ version;
78
+ webhook;
79
+ polling;
80
+ constructor(config) {
81
+ if (config.webhook && config.polling) {
82
+ throw new Error(
83
+ "RobotRock client cannot configure both webhook and polling. Use webhook for callbacks or polling to block until handled."
84
+ );
85
+ }
86
+ const apiKey = config.apiKey ?? process.env.ROBOTROCK_API_KEY;
87
+ if (!apiKey) {
88
+ throw new Error(
89
+ "RobotRock API key is required. Set ROBOTROCK_API_KEY or pass apiKey when creating the client."
90
+ );
91
+ }
92
+ this.apiKey = apiKey;
93
+ const rawBase = config.baseUrl ?? "https://api.robotrock.com/v1";
94
+ this.baseUrl = rawBase.replace(/\/+$/, "");
95
+ this.app = config.app;
96
+ this.version = config.version ?? 2;
97
+ this.webhook = config.webhook;
98
+ this.polling = config.polling ?? {};
99
+ }
100
+ /**
101
+ * Create a task via POST /v1 without waiting for a human response.
102
+ */
103
+ async createTask(task) {
104
+ const normalizedTask = normalizeSendToHumanInput(task, {
105
+ webhook: this.webhook,
106
+ app: this.app,
107
+ version: this.version
108
+ });
109
+ const bodyPayload = {
110
+ ...normalizedTask,
111
+ ...task.assignTo !== void 0 ? { assignTo: task.assignTo } : {}
112
+ };
113
+ const validation = createTaskBodySchema.safeParse(bodyPayload);
114
+ if (!validation.success) {
115
+ throw new RobotRockError(
116
+ `Invalid task: ${validation.error.errors[0]?.message}`,
117
+ 400,
118
+ validation.error.errors
119
+ );
120
+ }
121
+ const headers = {
122
+ "Content-Type": "application/json",
123
+ "X-Api-Key": this.apiKey
124
+ };
125
+ if (task.idempotencyKey) {
126
+ headers["Idempotency-Key"] = task.idempotencyKey;
127
+ }
128
+ const response = await fetch(`${this.baseUrl}/`, {
129
+ method: "POST",
130
+ headers,
131
+ body: JSON.stringify(validation.data)
132
+ });
133
+ const data = await parseResponseBody(response);
134
+ if (!response.ok) {
135
+ throw new RobotRockError(
136
+ getErrorMessage(data, "Failed to create task"),
137
+ response.status,
138
+ data
139
+ );
140
+ }
141
+ return data.task;
142
+ }
143
+ async sendToHuman(task) {
144
+ const normalizedTask = normalizeSendToHumanInput(task, {
145
+ webhook: this.webhook,
146
+ app: this.app,
147
+ version: this.version
148
+ });
149
+ const createdTaskTask = await this.createTask(task);
150
+ const hasHandlers = normalizedTask.actions.some(
151
+ (action) => Array.isArray(action.handlers) && action.handlers.length > 0
152
+ );
153
+ if (hasHandlers) {
154
+ return {
155
+ mode: "created",
156
+ task: createdTaskTask
157
+ };
158
+ }
159
+ const timeoutMs = this.polling.timeoutMs ?? DEFAULT_TIMEOUT_MS;
160
+ const pollIntervalMs = this.polling.intervalMs ?? DEFAULT_POLL_INTERVAL_MS;
161
+ const pollingDeadline = Date.now() + timeoutMs;
162
+ const validUntilMs = parseValidUntilMs(createdTaskTask.validUntil);
163
+ const deadline = validUntilMs !== void 0 ? Math.min(pollingDeadline, validUntilMs) : pollingDeadline;
164
+ const taskId = createdTaskTask.taskId;
165
+ while (Date.now() < deadline) {
166
+ const existing = await this.getTask(taskId);
167
+ if (existing?.status === "handled" && existing.handled) {
168
+ return {
169
+ mode: "handled",
170
+ task: createdTaskTask,
171
+ ...toDiscriminatedApprovalResult(
172
+ normalizedTask.actions,
173
+ existing
174
+ )
175
+ };
176
+ }
177
+ if (existing?.status === "expired" || existing && Date.now() >= existing.validUntil) {
178
+ throw new TaskExpiredError("Task reached validUntil before a human completed it");
179
+ }
180
+ const remainingMs = deadline - Date.now();
181
+ await sleep(Math.min(pollIntervalMs, Math.max(0, remainingMs)));
182
+ }
183
+ if (validUntilMs !== void 0 && Date.now() >= validUntilMs) {
184
+ throw new TaskExpiredError("Task reached validUntil before a human completed it");
185
+ }
186
+ throw new TaskTimeoutError(`No human response within ${timeoutMs}ms`);
187
+ }
188
+ /**
189
+ * Get a task by public task id (returned as `task.taskId` from {@link sendToHuman}).
190
+ */
191
+ async getTask(taskId) {
192
+ const response = await fetch(`${this.baseUrl}/tasks/${taskId}`, {
193
+ method: "GET",
194
+ headers: {
195
+ "X-Api-Key": this.apiKey
196
+ }
197
+ });
198
+ if (response.status === 404) {
199
+ return null;
200
+ }
201
+ const data = await parseResponseBody(response);
202
+ if (!response.ok) {
203
+ throw new RobotRockError(
204
+ getErrorMessage(data, "Failed to get task"),
205
+ response.status,
206
+ data
207
+ );
208
+ }
209
+ return data;
210
+ }
211
+ async cancelTask(taskId) {
212
+ const response = await fetch(`${this.baseUrl}/tasks/${taskId}/cancel`, {
213
+ method: "POST",
214
+ headers: {
215
+ "X-Api-Key": this.apiKey
216
+ }
217
+ });
218
+ if (!response.ok) {
219
+ const data = await parseResponseBody(response);
220
+ throw new RobotRockError(
221
+ getErrorMessage(data, "Failed to cancel task"),
222
+ response.status,
223
+ data
224
+ );
225
+ }
226
+ }
227
+ };
228
+ function createClient(config) {
229
+ return new RobotRock(config);
230
+ }
231
+ function attachWebhookToActions(actions, webhook) {
232
+ return actions.map((action) => ({
233
+ ...action,
234
+ handlers: webhookToHandlers(webhook)
235
+ }));
236
+ }
237
+ function webhookToHandlers(webhook) {
238
+ return [
239
+ {
240
+ type: "webhook",
241
+ url: webhook.url,
242
+ headers: webhook.headers ?? {}
243
+ }
244
+ ];
245
+ }
246
+ function normalizeSendToHumanInput(task, clientDefaults) {
247
+ const {
248
+ actions,
249
+ idempotencyKey: _idempotencyKey,
250
+ assignTo: _assignTo,
251
+ validUntil,
252
+ app: taskApp,
253
+ ...rest
254
+ } = task;
255
+ const webhook = clientDefaults.webhook;
256
+ const normalizedActions = webhook ? attachWebhookToActions(actions, webhook) : actions;
257
+ const app = taskApp ?? clientDefaults.app;
258
+ return {
259
+ ...rest,
260
+ version: clientDefaults.version,
261
+ ...app ? { app } : {},
262
+ ...validUntil !== void 0 ? { validUntil: serializeValidUntil(validUntil) } : {},
263
+ actions: normalizedActions
264
+ };
265
+ }
266
+ async function parseResponseBody(response) {
267
+ const contentType = response.headers.get("content-type") ?? "";
268
+ const bodyText = await response.text();
269
+ if (!bodyText) {
270
+ return null;
271
+ }
272
+ if (contentType.toLowerCase().includes("application/json")) {
273
+ try {
274
+ return JSON.parse(bodyText);
275
+ } catch {
276
+ }
277
+ }
278
+ try {
279
+ return JSON.parse(bodyText);
280
+ } catch {
281
+ return bodyText;
282
+ }
283
+ }
284
+ function getErrorMessage(data, fallback) {
285
+ if (data && typeof data === "object" && !Array.isArray(data)) {
286
+ const maybeMessage = data.message;
287
+ if (typeof maybeMessage === "string" && maybeMessage.trim()) {
288
+ return maybeMessage;
289
+ }
290
+ }
291
+ if (typeof data === "string" && data.trim()) {
292
+ const compact = data.replace(/\s+/g, " ").trim();
293
+ const snippet = compact.length > 180 ? `${compact.slice(0, 180)}...` : compact;
294
+ return `${fallback}. Server returned non-JSON response: ${snippet}`;
295
+ }
296
+ return fallback;
297
+ }
298
+
299
+ // src/env.ts
300
+ var DEFAULT_BASE_URL = "https://api.robotrock.com/v1";
301
+ function resolveRobotRockConfig(overrides) {
302
+ const apiKey = overrides?.apiKey ?? process.env.ROBOTROCK_API_KEY;
303
+ if (!apiKey) {
304
+ throw new Error(
305
+ "RobotRock API key is required. Set ROBOTROCK_API_KEY or pass apiKey when creating the client."
306
+ );
307
+ }
308
+ const baseUrl = overrides?.baseUrl ?? process.env.ROBOTROCK_BASE_URL ?? process.env.ROBOTROCK_API_URL ?? DEFAULT_BASE_URL;
309
+ const app = overrides?.app ?? process.env.ROBOTROCK_APP;
310
+ return app ? { apiKey, baseUrl, app } : { apiKey, baseUrl };
311
+ }
312
+ function resolveRobotRockClient(client, configOverrides) {
313
+ if (client) {
314
+ return client;
315
+ }
316
+ return createClient(resolveRobotRockConfig(configOverrides));
317
+ }
318
+
319
+ export {
320
+ TaskTimeoutError,
321
+ TaskExpiredError,
322
+ toDiscriminatedApprovalResult,
323
+ RobotRockError,
324
+ RobotRock,
325
+ createClient,
326
+ attachWebhookToActions,
327
+ resolveRobotRockConfig,
328
+ resolveRobotRockClient
329
+ };
330
+ //# sourceMappingURL=chunk-THVGHUTX.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.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":"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","names":[]}
@@ -0,0 +1,99 @@
1
+ import { TaskContextInput, AssignToInput, TaskResponse, DiscriminatedApprovalResult, Task } from './schemas/index.js';
2
+
3
+ type RobotRockWebhookConfig = {
4
+ url: string;
5
+ headers?: Record<string, string>;
6
+ };
7
+ interface RobotRockPollingOptions {
8
+ /** Poll interval when no webhook is configured (ms). @default 2000 */
9
+ intervalMs?: number;
10
+ /**
11
+ * Max time to poll when no webhook is configured (ms).
12
+ * Polling also stops when the task's `validUntil` passes, whichever is sooner.
13
+ * @default 86400000 (24h)
14
+ */
15
+ timeoutMs?: number;
16
+ }
17
+ type RobotRockClientBaseConfig = {
18
+ /** Optional override for API key. Falls back to ROBOTROCK_API_KEY. */
19
+ apiKey?: string;
20
+ /**
21
+ * Base URL for the RobotRock API
22
+ * @default "https://api.robotrock.com/v1"
23
+ */
24
+ baseUrl?: string;
25
+ /**
26
+ * Default inbox app bucket for every task from this client.
27
+ * When omitted, the API uses your API key name.
28
+ */
29
+ app?: string;
30
+ /**
31
+ * Task context format version sent on every `sendToHuman` request.
32
+ * @default 2
33
+ */
34
+ version?: 2;
35
+ };
36
+ /** Client config with a webhook (mutually exclusive with `polling`). */
37
+ type RobotRockWebhookClientConfig = RobotRockClientBaseConfig & {
38
+ webhook: RobotRockWebhookConfig;
39
+ polling?: never;
40
+ };
41
+ /** Client config without a webhook; optional `polling` controls the wait loop. */
42
+ type RobotRockPollingClientConfig = RobotRockClientBaseConfig & {
43
+ webhook?: never;
44
+ polling?: RobotRockPollingOptions;
45
+ };
46
+ type RobotRockConfig = RobotRockWebhookClientConfig | RobotRockPollingClientConfig;
47
+ type SendToHumanActionInput = Omit<TaskContextInput["actions"][number], "handlers">;
48
+ type SendToHumanValidUntil = Date | string;
49
+ type SendToHumanInput<A extends readonly SendToHumanActionInput[] = readonly SendToHumanActionInput[]> = Omit<TaskContextInput, "app" | "actions" | "version" | "validUntil"> & {
50
+ actions: A;
51
+ /** Task deadline; serialized to an ISO 8601 string on the wire. */
52
+ validUntil?: SendToHumanValidUntil;
53
+ /** Optional idempotency key to prevent duplicate tasks */
54
+ idempotencyKey?: string;
55
+ /** Assign to tenant users (email) and/or groups (slug). Narrows inbox visibility. */
56
+ assignTo?: AssignToInput;
57
+ };
58
+ type SendToHumanWithAppInput<A extends readonly SendToHumanActionInput[] = readonly SendToHumanActionInput[]> = (SendToHumanInput<A> | Readonly<SendToHumanInput<A>>) & {
59
+ /** Inbox app bucket. Overrides the client `app` when set. */
60
+ app?: string;
61
+ };
62
+ type SendToHumanResult<A extends readonly SendToHumanActionInput[] = readonly SendToHumanActionInput[]> = {
63
+ mode: "created";
64
+ task: TaskResponse["task"];
65
+ } | ({
66
+ mode: "handled";
67
+ task: TaskResponse["task"];
68
+ } & DiscriminatedApprovalResult<A>);
69
+ declare class RobotRockError extends Error {
70
+ readonly statusCode: number;
71
+ readonly response?: unknown | undefined;
72
+ constructor(message: string, statusCode: number, response?: unknown | undefined);
73
+ }
74
+ /**
75
+ * RobotRock API client for creating and querying human-in-the-loop tasks.
76
+ */
77
+ declare class RobotRock {
78
+ private readonly apiKey;
79
+ private readonly baseUrl;
80
+ private readonly app?;
81
+ private readonly version;
82
+ private readonly webhook?;
83
+ private readonly polling;
84
+ constructor(config: RobotRockConfig);
85
+ /**
86
+ * Create a task via POST /v1 without waiting for a human response.
87
+ */
88
+ createTask<const A extends readonly SendToHumanActionInput[]>(task: SendToHumanWithAppInput<A>): Promise<TaskResponse["task"]>;
89
+ sendToHuman<const A extends readonly SendToHumanActionInput[]>(task: SendToHumanWithAppInput<A>): Promise<SendToHumanResult<A>>;
90
+ /**
91
+ * Get a task by public task id (returned as `task.taskId` from {@link sendToHuman}).
92
+ */
93
+ getTask(taskId: string): Promise<Task | null>;
94
+ cancelTask(taskId: string): Promise<void>;
95
+ }
96
+ declare function createClient(config: RobotRockConfig): RobotRock;
97
+ declare function attachWebhookToActions(actions: readonly SendToHumanActionInput[], webhook: RobotRockWebhookConfig): TaskContextInput["actions"];
98
+
99
+ export { type RobotRockConfig as R, type SendToHumanActionInput as S, type SendToHumanInput as a, RobotRock as b, RobotRockError as c, attachWebhookToActions as d, createClient as e, type RobotRockWebhookClientConfig as f, type RobotRockPollingClientConfig as g, type RobotRockWebhookConfig as h, type RobotRockPollingOptions as i, type SendToHumanValidUntil as j, type SendToHumanResult as k };
@@ -0,0 +1,16 @@
1
+ /**
2
+ * JSON body posted when a RobotRock action handler runs (webhook or Trigger wait token).
3
+ */
4
+ interface RobotRockHandlerWebhookPayload {
5
+ taskId: string;
6
+ action: {
7
+ id: string;
8
+ title: string;
9
+ data: unknown;
10
+ };
11
+ handledBy?: string;
12
+ handledAt: string;
13
+ handlerType: string;
14
+ }
15
+
16
+ export type { RobotRockHandlerWebhookPayload as R };
package/dist/index.d.ts CHANGED
@@ -1,53 +1,99 @@
1
- import { R as RobotRockConfig, a as RobotRock, C as CreateTaskOptions } from './client-BQ-j7q68.js';
2
- export { b as RobotRockError, c as createClient } from './client-BQ-j7q68.js';
3
- import { TaskAction, TaskContextInput, DiscriminatedApprovalResult, Task } from '@robotrock/core';
4
- export { ApprovalResult, DiscriminatedApprovalResult, Handler, InferActionData, Task, TaskAction, TaskContext, TaskContextInput, TaskResponse, TaskResult, TaskStatus, TriggerHandler, TupleElementIndices, WebhookHandler, taskContextSchema } from '@robotrock/core';
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';
3
+ import { Task, DiscriminatedApprovalResult } from './schemas/index.js';
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
+ import { z } from 'zod';
6
+ export { R as RobotRockHandlerWebhookPayload } from './handler-webhook-BqEi6Bk-.js';
5
7
 
6
8
  /**
7
9
  * Read RobotRock client config from environment variables.
8
10
  *
9
11
  * - `ROBOTROCK_API_KEY` (required when not passed explicitly)
10
12
  * - `ROBOTROCK_BASE_URL` or `ROBOTROCK_API_URL` (optional)
13
+ * - `ROBOTROCK_APP` (optional inbox app bucket)
11
14
  */
12
15
  declare function resolveRobotRockConfig(overrides?: Partial<RobotRockConfig>): RobotRockConfig;
13
16
  /** Use an explicit client or create one from env / optional config overrides. */
14
17
  declare function resolveRobotRockClient(client?: RobotRock, configOverrides?: Partial<RobotRockConfig>): RobotRock;
15
18
 
16
- /** Task payload for {@link askHuman}; `actions` must stay a literal tuple for inference. */
17
- type AskHumanTask<A extends readonly TaskAction[]> = Omit<TaskContextInput, "actions"> & {
18
- readonly actions: A;
19
- };
20
- type AskHumanParams<A extends readonly TaskAction[]> = {
21
- task: AskHumanTask<A>;
22
- /** Pre-configured client; when omitted, one is created from env / `apiKey` / `baseUrl`. */
23
- client?: RobotRock;
24
- apiKey?: string;
25
- baseUrl?: string;
26
- /** Poll interval while waiting for a human (ms). @default 2000 */
27
- pollInterval?: number;
28
- /** Max wait time (ms). @default 86400000 (24h) */
29
- timeout?: number;
30
- } & Pick<CreateTaskOptions, "idempotencyKey">;
31
- /**
32
- * Create a human-in-the-loop task and block until someone completes it.
33
- *
34
- * Uses polling against the RobotRock API. For durable waits inside Trigger.dev,
35
- * use `robotrock/trigger` instead.
36
- *
37
- * The return type is inferred from `task.actions`: `actionId` narrows `data`.
38
- */
39
- declare function askHuman<const A extends readonly TaskAction[]>(params: AskHumanParams<A>): Promise<DiscriminatedApprovalResult<A>>;
40
-
41
- declare class AskHumanTimeoutError extends Error {
19
+ declare class TaskTimeoutError extends Error {
42
20
  constructor(message: string);
43
21
  }
44
- declare class AskHumanExpiredError extends Error {
22
+ declare class TaskExpiredError extends Error {
45
23
  constructor(message: string);
46
24
  }
47
25
  /**
48
26
  * Map a handled API task to a discriminated approval result.
49
27
  * Runtime validation is minimal; TypeScript narrows via `task.actions` at the call site.
50
28
  */
51
- declare function toDiscriminatedApprovalResult<A extends readonly TaskAction[]>(actions: A, task: Task, streamEntryId: string): DiscriminatedApprovalResult<A>;
29
+ declare function toDiscriminatedApprovalResult<A extends readonly {
30
+ id: string;
31
+ schema?: unknown;
32
+ }[]>(actions: A, task: Task): DiscriminatedApprovalResult<A>;
33
+
34
+ declare const robotRockWebhookPayloadSchema: z.ZodObject<{
35
+ taskId: z.ZodString;
36
+ action: z.ZodObject<{
37
+ id: z.ZodString;
38
+ title: z.ZodString;
39
+ data: z.ZodUnknown;
40
+ }, "strip", z.ZodTypeAny, {
41
+ id: string;
42
+ title: string;
43
+ data?: unknown;
44
+ }, {
45
+ id: string;
46
+ title: string;
47
+ data?: unknown;
48
+ }>;
49
+ handledBy: z.ZodOptional<z.ZodString>;
50
+ handledAt: z.ZodString;
51
+ handlerType: z.ZodString;
52
+ } & {
53
+ headers: z.ZodRecord<z.ZodString, z.ZodString>;
54
+ }, "strip", z.ZodTypeAny, {
55
+ headers: Record<string, string>;
56
+ taskId: string;
57
+ handledAt: string;
58
+ action: {
59
+ id: string;
60
+ title: string;
61
+ data?: unknown;
62
+ };
63
+ handlerType: string;
64
+ handledBy?: string | undefined;
65
+ }, {
66
+ headers: Record<string, string>;
67
+ taskId: string;
68
+ handledAt: string;
69
+ action: {
70
+ id: string;
71
+ title: string;
72
+ data?: unknown;
73
+ };
74
+ handlerType: string;
75
+ handledBy?: string | undefined;
76
+ }>;
77
+ type RobotRockWebhookErrorCode = "MISSING_WEBHOOK_SECRET" | "MISSING_SIGNATURE" | "INVALID_SIGNATURE" | "INVALID_JSON" | "INVALID_PAYLOAD";
78
+ declare class RobotRockWebhookError extends Error {
79
+ readonly code: RobotRockWebhookErrorCode;
80
+ readonly details?: unknown | undefined;
81
+ constructor(message: string, code: RobotRockWebhookErrorCode, details?: unknown | undefined);
82
+ }
83
+ type RobotRockWebhookPayload = z.infer<typeof robotRockWebhookPayloadSchema>;
84
+ interface VerifyRobotRockWebhookOptions {
85
+ /**
86
+ * Override shared secret (defaults to ROBOTROCK_WEBHOOK_SECRET).
87
+ * Keep undefined in production to enforce the canonical env var.
88
+ */
89
+ secret?: string;
90
+ /** Signature header to read. @default "x-robotrock-signature" */
91
+ signatureHeader?: string;
92
+ }
93
+ /**
94
+ * Verify a RobotRock webhook request and return a validated payload.
95
+ * Throws RobotRockWebhookError with machine-readable `code` for audit logging.
96
+ */
97
+ declare function verifyRobotRockWebhook(request: Request, options?: VerifyRobotRockWebhookOptions): Promise<RobotRockWebhookPayload>;
52
98
 
53
- export { AskHumanExpiredError, type AskHumanParams, type AskHumanTask, AskHumanTimeoutError, CreateTaskOptions, RobotRock, RobotRockConfig, askHuman, resolveRobotRockClient, resolveRobotRockConfig, toDiscriminatedApprovalResult };
99
+ export { DiscriminatedApprovalResult, RobotRock, RobotRockConfig, RobotRockWebhookError, type RobotRockWebhookErrorCode, type RobotRockWebhookPayload, Task, TaskExpiredError, TaskTimeoutError, type VerifyRobotRockWebhookOptions, resolveRobotRockClient, resolveRobotRockConfig, toDiscriminatedApprovalResult, verifyRobotRockWebhook };