robotrock 0.1.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.
- package/README.md +190 -33
- package/dist/ai/index.d.ts +24 -0
- package/dist/ai/index.js +46 -0
- package/dist/ai/index.js.map +1 -0
- package/dist/ai/trigger.d.ts +5 -0
- package/dist/ai/trigger.js +24 -0
- package/dist/ai/trigger.js.map +1 -0
- package/dist/ai/workflow.d.ts +5 -0
- package/dist/ai/workflow.js +24 -0
- package/dist/ai/workflow.js.map +1 -0
- package/dist/chunk-D2FBSEZK.js +67 -0
- package/dist/chunk-D2FBSEZK.js.map +1 -0
- package/dist/chunk-DSZ3GMT4.js +518 -0
- package/dist/chunk-DSZ3GMT4.js.map +1 -0
- package/dist/chunk-KOXJCIST.js +332 -0
- package/dist/chunk-KOXJCIST.js.map +1 -0
- package/dist/chunk-LXM7VS4Q.js +129 -0
- package/dist/chunk-LXM7VS4Q.js.map +1 -0
- package/dist/client-Dhk9qxhL.d.ts +104 -0
- package/dist/handler-webhook-BqEi6Bk-.d.ts +16 -0
- package/dist/index.d.ts +84 -33
- package/dist/index.js +117 -38
- package/dist/index.js.map +1 -1
- package/dist/schemas/index.d.ts +643 -0
- package/dist/schemas/index.js +11 -0
- package/dist/schemas/index.js.map +1 -0
- package/dist/trigger/index.d.ts +34 -37
- package/dist/trigger/index.js +44 -72
- package/dist/trigger/index.js.map +1 -1
- package/dist/workflow/index.d.ts +35 -0
- package/dist/workflow/index.js +99 -0
- package/dist/workflow/index.js.map +1 -0
- package/dist/workflow-BYeIZgD0.d.ts +309 -0
- package/package.json +38 -8
- package/dist/chunk-TUQXDKV6.js +0 -209
- package/dist/chunk-TUQXDKV6.js.map +0 -1
- package/dist/client-BQ-j7q68.d.ts +0 -37
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createTaskBodySchema
|
|
3
|
+
} from "./chunk-LXM7VS4Q.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.io/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
|
+
...task.threadId !== void 0 ? { threadId: task.threadId } : {}
|
|
113
|
+
};
|
|
114
|
+
const validation = createTaskBodySchema.safeParse(bodyPayload);
|
|
115
|
+
if (!validation.success) {
|
|
116
|
+
throw new RobotRockError(
|
|
117
|
+
`Invalid task: ${validation.error.errors[0]?.message}`,
|
|
118
|
+
400,
|
|
119
|
+
validation.error.errors
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
const headers = {
|
|
123
|
+
"Content-Type": "application/json",
|
|
124
|
+
"X-Api-Key": this.apiKey
|
|
125
|
+
};
|
|
126
|
+
if (task.idempotencyKey) {
|
|
127
|
+
headers["Idempotency-Key"] = task.idempotencyKey;
|
|
128
|
+
}
|
|
129
|
+
const response = await fetch(`${this.baseUrl}/`, {
|
|
130
|
+
method: "POST",
|
|
131
|
+
headers,
|
|
132
|
+
body: JSON.stringify(validation.data)
|
|
133
|
+
});
|
|
134
|
+
const data = await parseResponseBody(response);
|
|
135
|
+
if (!response.ok) {
|
|
136
|
+
throw new RobotRockError(
|
|
137
|
+
getErrorMessage(data, "Failed to create task"),
|
|
138
|
+
response.status,
|
|
139
|
+
data
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
return data.task;
|
|
143
|
+
}
|
|
144
|
+
async sendToHuman(task) {
|
|
145
|
+
const normalizedTask = normalizeSendToHumanInput(task, {
|
|
146
|
+
webhook: this.webhook,
|
|
147
|
+
app: this.app,
|
|
148
|
+
version: this.version
|
|
149
|
+
});
|
|
150
|
+
const createdTaskTask = await this.createTask(task);
|
|
151
|
+
const hasHandlers = normalizedTask.actions.some(
|
|
152
|
+
(action) => Array.isArray(action.handlers) && action.handlers.length > 0
|
|
153
|
+
);
|
|
154
|
+
if (hasHandlers) {
|
|
155
|
+
return {
|
|
156
|
+
mode: "created",
|
|
157
|
+
task: createdTaskTask
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
const timeoutMs = this.polling.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
161
|
+
const pollIntervalMs = this.polling.intervalMs ?? DEFAULT_POLL_INTERVAL_MS;
|
|
162
|
+
const pollingDeadline = Date.now() + timeoutMs;
|
|
163
|
+
const validUntilMs = parseValidUntilMs(createdTaskTask.validUntil);
|
|
164
|
+
const deadline = validUntilMs !== void 0 ? Math.min(pollingDeadline, validUntilMs) : pollingDeadline;
|
|
165
|
+
const taskId = createdTaskTask.taskId;
|
|
166
|
+
while (Date.now() < deadline) {
|
|
167
|
+
const existing = await this.getTask(taskId);
|
|
168
|
+
if (existing?.status === "handled" && existing.handled) {
|
|
169
|
+
return {
|
|
170
|
+
mode: "handled",
|
|
171
|
+
task: createdTaskTask,
|
|
172
|
+
...toDiscriminatedApprovalResult(
|
|
173
|
+
normalizedTask.actions,
|
|
174
|
+
existing
|
|
175
|
+
)
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
if (existing?.status === "expired" || existing && Date.now() >= existing.validUntil) {
|
|
179
|
+
throw new TaskExpiredError("Task reached validUntil before a human completed it");
|
|
180
|
+
}
|
|
181
|
+
const remainingMs = deadline - Date.now();
|
|
182
|
+
await sleep(Math.min(pollIntervalMs, Math.max(0, remainingMs)));
|
|
183
|
+
}
|
|
184
|
+
if (validUntilMs !== void 0 && Date.now() >= validUntilMs) {
|
|
185
|
+
throw new TaskExpiredError("Task reached validUntil before a human completed it");
|
|
186
|
+
}
|
|
187
|
+
throw new TaskTimeoutError(`No human response within ${timeoutMs}ms`);
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Get a task by public task id (returned as `task.taskId` from {@link sendToHuman}).
|
|
191
|
+
*/
|
|
192
|
+
async getTask(taskId) {
|
|
193
|
+
const response = await fetch(`${this.baseUrl}/tasks/${taskId}`, {
|
|
194
|
+
method: "GET",
|
|
195
|
+
headers: {
|
|
196
|
+
"X-Api-Key": this.apiKey
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
if (response.status === 404) {
|
|
200
|
+
return null;
|
|
201
|
+
}
|
|
202
|
+
const data = await parseResponseBody(response);
|
|
203
|
+
if (!response.ok) {
|
|
204
|
+
throw new RobotRockError(
|
|
205
|
+
getErrorMessage(data, "Failed to get task"),
|
|
206
|
+
response.status,
|
|
207
|
+
data
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
return data;
|
|
211
|
+
}
|
|
212
|
+
async cancelTask(taskId) {
|
|
213
|
+
const response = await fetch(`${this.baseUrl}/tasks/${taskId}/cancel`, {
|
|
214
|
+
method: "POST",
|
|
215
|
+
headers: {
|
|
216
|
+
"X-Api-Key": this.apiKey
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
if (!response.ok) {
|
|
220
|
+
const data = await parseResponseBody(response);
|
|
221
|
+
throw new RobotRockError(
|
|
222
|
+
getErrorMessage(data, "Failed to cancel task"),
|
|
223
|
+
response.status,
|
|
224
|
+
data
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
};
|
|
229
|
+
function createClient(config) {
|
|
230
|
+
return new RobotRock(config);
|
|
231
|
+
}
|
|
232
|
+
function attachWebhookToActions(actions, webhook) {
|
|
233
|
+
return actions.map((action) => ({
|
|
234
|
+
...action,
|
|
235
|
+
handlers: webhookToHandlers(webhook)
|
|
236
|
+
}));
|
|
237
|
+
}
|
|
238
|
+
function webhookToHandlers(webhook) {
|
|
239
|
+
return [
|
|
240
|
+
{
|
|
241
|
+
type: "webhook",
|
|
242
|
+
url: webhook.url,
|
|
243
|
+
headers: webhook.headers ?? {}
|
|
244
|
+
}
|
|
245
|
+
];
|
|
246
|
+
}
|
|
247
|
+
function normalizeSendToHumanInput(task, clientDefaults) {
|
|
248
|
+
const {
|
|
249
|
+
actions,
|
|
250
|
+
idempotencyKey: _idempotencyKey,
|
|
251
|
+
assignTo: _assignTo,
|
|
252
|
+
threadId: _threadId,
|
|
253
|
+
validUntil,
|
|
254
|
+
app: taskApp,
|
|
255
|
+
...rest
|
|
256
|
+
} = task;
|
|
257
|
+
const webhook = clientDefaults.webhook;
|
|
258
|
+
const normalizedActions = webhook ? attachWebhookToActions(actions, webhook) : actions;
|
|
259
|
+
const app = taskApp ?? clientDefaults.app;
|
|
260
|
+
return {
|
|
261
|
+
...rest,
|
|
262
|
+
version: clientDefaults.version,
|
|
263
|
+
...app ? { app } : {},
|
|
264
|
+
...validUntil !== void 0 ? { validUntil: serializeValidUntil(validUntil) } : {},
|
|
265
|
+
actions: normalizedActions
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
async function parseResponseBody(response) {
|
|
269
|
+
const contentType = response.headers.get("content-type") ?? "";
|
|
270
|
+
const bodyText = await response.text();
|
|
271
|
+
if (!bodyText) {
|
|
272
|
+
return null;
|
|
273
|
+
}
|
|
274
|
+
if (contentType.toLowerCase().includes("application/json")) {
|
|
275
|
+
try {
|
|
276
|
+
return JSON.parse(bodyText);
|
|
277
|
+
} catch {
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
try {
|
|
281
|
+
return JSON.parse(bodyText);
|
|
282
|
+
} catch {
|
|
283
|
+
return bodyText;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
function getErrorMessage(data, fallback) {
|
|
287
|
+
if (data && typeof data === "object" && !Array.isArray(data)) {
|
|
288
|
+
const maybeMessage = data.message;
|
|
289
|
+
if (typeof maybeMessage === "string" && maybeMessage.trim()) {
|
|
290
|
+
return maybeMessage;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
if (typeof data === "string" && data.trim()) {
|
|
294
|
+
const compact = data.replace(/\s+/g, " ").trim();
|
|
295
|
+
const snippet = compact.length > 180 ? `${compact.slice(0, 180)}...` : compact;
|
|
296
|
+
return `${fallback}. Server returned non-JSON response: ${snippet}`;
|
|
297
|
+
}
|
|
298
|
+
return fallback;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// src/env.ts
|
|
302
|
+
var DEFAULT_BASE_URL = "https://api.robotrock.io/v1";
|
|
303
|
+
function resolveRobotRockConfig(overrides) {
|
|
304
|
+
const apiKey = overrides?.apiKey ?? process.env.ROBOTROCK_API_KEY;
|
|
305
|
+
if (!apiKey) {
|
|
306
|
+
throw new Error(
|
|
307
|
+
"RobotRock API key is required. Set ROBOTROCK_API_KEY or pass apiKey when creating the client."
|
|
308
|
+
);
|
|
309
|
+
}
|
|
310
|
+
const baseUrl = overrides?.baseUrl ?? process.env.ROBOTROCK_BASE_URL ?? process.env.ROBOTROCK_API_URL ?? DEFAULT_BASE_URL;
|
|
311
|
+
const app = overrides?.app ?? process.env.ROBOTROCK_APP;
|
|
312
|
+
return app ? { apiKey, baseUrl, app } : { apiKey, baseUrl };
|
|
313
|
+
}
|
|
314
|
+
function resolveRobotRockClient(client, configOverrides) {
|
|
315
|
+
if (client) {
|
|
316
|
+
return client;
|
|
317
|
+
}
|
|
318
|
+
return createClient(resolveRobotRockConfig(configOverrides));
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
export {
|
|
322
|
+
TaskTimeoutError,
|
|
323
|
+
TaskExpiredError,
|
|
324
|
+
toDiscriminatedApprovalResult,
|
|
325
|
+
RobotRockError,
|
|
326
|
+
RobotRock,
|
|
327
|
+
createClient,
|
|
328
|
+
attachWebhookToActions,
|
|
329
|
+
resolveRobotRockConfig,
|
|
330
|
+
resolveRobotRockClient
|
|
331
|
+
};
|
|
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":[]}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
var __knownSymbol = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name);
|
|
2
|
+
var __typeError = (msg) => {
|
|
3
|
+
throw TypeError(msg);
|
|
4
|
+
};
|
|
5
|
+
var __using = (stack, value, async) => {
|
|
6
|
+
if (value != null) {
|
|
7
|
+
if (typeof value !== "object" && typeof value !== "function") __typeError("Object expected");
|
|
8
|
+
var dispose, inner;
|
|
9
|
+
if (async) dispose = value[__knownSymbol("asyncDispose")];
|
|
10
|
+
if (dispose === void 0) {
|
|
11
|
+
dispose = value[__knownSymbol("dispose")];
|
|
12
|
+
if (async) inner = dispose;
|
|
13
|
+
}
|
|
14
|
+
if (typeof dispose !== "function") __typeError("Object not disposable");
|
|
15
|
+
if (inner) dispose = function() {
|
|
16
|
+
try {
|
|
17
|
+
inner.call(this);
|
|
18
|
+
} catch (e) {
|
|
19
|
+
return Promise.reject(e);
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
stack.push([async, dispose, value]);
|
|
23
|
+
} else if (async) {
|
|
24
|
+
stack.push([async]);
|
|
25
|
+
}
|
|
26
|
+
return value;
|
|
27
|
+
};
|
|
28
|
+
var __callDispose = (stack, error, hasError) => {
|
|
29
|
+
var E = typeof SuppressedError === "function" ? SuppressedError : function(e, s, m, _) {
|
|
30
|
+
return _ = Error(m), _.name = "SuppressedError", _.error = e, _.suppressed = s, _;
|
|
31
|
+
};
|
|
32
|
+
var fail = (e) => error = hasError ? new E(e, error, "An error was suppressed during disposal") : (hasError = true, e);
|
|
33
|
+
var next = (it) => {
|
|
34
|
+
while (it = stack.pop()) {
|
|
35
|
+
try {
|
|
36
|
+
var result = it[1] && it[1].call(it[2]);
|
|
37
|
+
if (it[0]) return Promise.resolve(result).then(next, (e) => (fail(e), next()));
|
|
38
|
+
} catch (e) {
|
|
39
|
+
fail(e);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
if (hasError) throw error;
|
|
43
|
+
};
|
|
44
|
+
return next();
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
// src/schemas/index.ts
|
|
48
|
+
import { z } from "zod";
|
|
49
|
+
var safeUrlSchema = z.string().refine((url) => url.startsWith("http://") || url.startsWith("https://"), {
|
|
50
|
+
message: "URL must start with http:// or https://"
|
|
51
|
+
});
|
|
52
|
+
var jsonSchema7Schema = z.custom(
|
|
53
|
+
(val) => typeof val === "object" && val !== null,
|
|
54
|
+
{ message: "Must be a valid JSON Schema object" }
|
|
55
|
+
);
|
|
56
|
+
var uiSchemaSchema = z.custom((val) => typeof val === "object" && val !== null, {
|
|
57
|
+
message: "Must be a valid UiSchema object"
|
|
58
|
+
});
|
|
59
|
+
var webhookHandlerSchema = z.object({
|
|
60
|
+
type: z.literal("webhook"),
|
|
61
|
+
url: safeUrlSchema,
|
|
62
|
+
headers: z.record(z.string())
|
|
63
|
+
});
|
|
64
|
+
var triggerHandlerSchema = webhookHandlerSchema.extend({
|
|
65
|
+
type: z.literal("trigger"),
|
|
66
|
+
tokenId: z.string().min(1)
|
|
67
|
+
});
|
|
68
|
+
var handlerSchema = z.discriminatedUnion("type", [webhookHandlerSchema, triggerHandlerSchema]);
|
|
69
|
+
var taskActionSchema = z.object({
|
|
70
|
+
id: z.string().min(1),
|
|
71
|
+
title: z.string().min(1),
|
|
72
|
+
description: z.string().optional(),
|
|
73
|
+
schema: jsonSchema7Schema.optional(),
|
|
74
|
+
ui: uiSchemaSchema.optional(),
|
|
75
|
+
data: z.record(z.unknown()).optional(),
|
|
76
|
+
handlers: z.array(handlerSchema).min(1).optional()
|
|
77
|
+
});
|
|
78
|
+
var uiFieldSchemaSchema = z.object({
|
|
79
|
+
"ui:widget": z.string().optional(),
|
|
80
|
+
"ui:title": z.string().optional(),
|
|
81
|
+
"ui:description": z.string().optional(),
|
|
82
|
+
"ui:options": z.record(z.unknown()).optional(),
|
|
83
|
+
items: z.lazy(() => z.record(uiFieldSchemaSchema)).optional()
|
|
84
|
+
}).passthrough();
|
|
85
|
+
var contextUiSchema = z.record(uiFieldSchemaSchema).optional();
|
|
86
|
+
var contextDataSchema = z.object({
|
|
87
|
+
data: z.record(z.unknown()),
|
|
88
|
+
ui: contextUiSchema
|
|
89
|
+
}).optional();
|
|
90
|
+
var taskContextSchema = z.object({
|
|
91
|
+
app: z.string().min(1).optional(),
|
|
92
|
+
type: z.string().min(1),
|
|
93
|
+
name: z.string().min(1),
|
|
94
|
+
description: z.string().optional(),
|
|
95
|
+
validUntil: z.string().optional(),
|
|
96
|
+
context: contextDataSchema,
|
|
97
|
+
version: z.literal(2).optional(),
|
|
98
|
+
actions: z.array(taskActionSchema).min(1, "At least one action is required")
|
|
99
|
+
});
|
|
100
|
+
var assignToSchema = z.object({
|
|
101
|
+
users: z.array(z.string().email()).optional(),
|
|
102
|
+
groups: z.array(z.string().min(1)).optional()
|
|
103
|
+
}).refine(
|
|
104
|
+
(data) => {
|
|
105
|
+
const groups = data.groups ?? [];
|
|
106
|
+
if (groups.includes("all") && groups.length > 1) {
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
return true;
|
|
110
|
+
},
|
|
111
|
+
{ message: 'Cannot combine "all" with other group slugs' }
|
|
112
|
+
);
|
|
113
|
+
var createTaskBodySchema = taskContextSchema.extend({
|
|
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()
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
export {
|
|
123
|
+
__using,
|
|
124
|
+
__callDispose,
|
|
125
|
+
taskContextSchema,
|
|
126
|
+
assignToSchema,
|
|
127
|
+
createTaskBodySchema
|
|
128
|
+
};
|
|
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":[]}
|
|
@@ -0,0 +1,104 @@
|
|
|
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.io/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
|
+
* 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;
|
|
62
|
+
};
|
|
63
|
+
type SendToHumanWithAppInput<A extends readonly SendToHumanActionInput[] = readonly SendToHumanActionInput[]> = (SendToHumanInput<A> | Readonly<SendToHumanInput<A>>) & {
|
|
64
|
+
/** Inbox app bucket. Overrides the client `app` when set. */
|
|
65
|
+
app?: string;
|
|
66
|
+
};
|
|
67
|
+
type SendToHumanResult<A extends readonly SendToHumanActionInput[] = readonly SendToHumanActionInput[]> = {
|
|
68
|
+
mode: "created";
|
|
69
|
+
task: TaskResponse["task"];
|
|
70
|
+
} | ({
|
|
71
|
+
mode: "handled";
|
|
72
|
+
task: TaskResponse["task"];
|
|
73
|
+
} & DiscriminatedApprovalResult<A>);
|
|
74
|
+
declare class RobotRockError extends Error {
|
|
75
|
+
readonly statusCode: number;
|
|
76
|
+
readonly response?: unknown | undefined;
|
|
77
|
+
constructor(message: string, statusCode: number, response?: unknown | undefined);
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* RobotRock API client for creating and querying human-in-the-loop tasks.
|
|
81
|
+
*/
|
|
82
|
+
declare class RobotRock {
|
|
83
|
+
private readonly apiKey;
|
|
84
|
+
private readonly baseUrl;
|
|
85
|
+
private readonly app?;
|
|
86
|
+
private readonly version;
|
|
87
|
+
private readonly webhook?;
|
|
88
|
+
private readonly polling;
|
|
89
|
+
constructor(config: RobotRockConfig);
|
|
90
|
+
/**
|
|
91
|
+
* Create a task via POST /v1 without waiting for a human response.
|
|
92
|
+
*/
|
|
93
|
+
createTask<const A extends readonly SendToHumanActionInput[]>(task: SendToHumanWithAppInput<A>): Promise<TaskResponse["task"]>;
|
|
94
|
+
sendToHuman<const A extends readonly SendToHumanActionInput[]>(task: SendToHumanWithAppInput<A>): Promise<SendToHumanResult<A>>;
|
|
95
|
+
/**
|
|
96
|
+
* Get a task by public task id (returned as `task.taskId` from {@link sendToHuman}).
|
|
97
|
+
*/
|
|
98
|
+
getTask(taskId: string): Promise<Task | null>;
|
|
99
|
+
cancelTask(taskId: string): Promise<void>;
|
|
100
|
+
}
|
|
101
|
+
declare function createClient(config: RobotRockConfig): RobotRock;
|
|
102
|
+
declare function attachWebhookToActions(actions: readonly SendToHumanActionInput[], webhook: RobotRockWebhookConfig): TaskContextInput["actions"];
|
|
103
|
+
|
|
104
|
+
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 };
|