@tuvl/client 0.0.1 → 2026.2.1-beta.2
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 +384 -3
- package/dist/index.d.mts +569 -0
- package/dist/index.d.ts +569 -0
- package/dist/index.js +849 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +840 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +59 -12
- package/index.js +0 -2
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,569 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Low-level HTTP transport for @tuvl/client.
|
|
3
|
+
*
|
|
4
|
+
* Handles auth header injection, JSON serialisation, and returns raw
|
|
5
|
+
* Response objects so higher-level transports (SSE, gRPC) can consume them.
|
|
6
|
+
*/
|
|
7
|
+
interface TransportOptions {
|
|
8
|
+
baseUrl: string;
|
|
9
|
+
defaultToken?: string;
|
|
10
|
+
}
|
|
11
|
+
declare class Transport {
|
|
12
|
+
/** Public so TuvlClient can forward it to the gRPC subtransport. */
|
|
13
|
+
readonly baseUrl: string;
|
|
14
|
+
private defaultToken;
|
|
15
|
+
constructor(options: TransportOptions);
|
|
16
|
+
/** Update the default token (e.g. after refresh). */
|
|
17
|
+
setToken(token: string): void;
|
|
18
|
+
/** Build the Authorization header value, or undefined if no token. */
|
|
19
|
+
private authHeader;
|
|
20
|
+
/** Perform a plain JSON POST and return the parsed response body. */
|
|
21
|
+
post<TBody = unknown, TResponse = unknown>(path: string, body: TBody, options?: {
|
|
22
|
+
token?: string;
|
|
23
|
+
signal?: AbortSignal;
|
|
24
|
+
}): Promise<TResponse>;
|
|
25
|
+
/**
|
|
26
|
+
* Open an SSE-capable stream and return the raw Response.
|
|
27
|
+
* Callers are responsible for reading `response.body`.
|
|
28
|
+
*/
|
|
29
|
+
postStream(path: string, body: unknown, options?: {
|
|
30
|
+
token?: string;
|
|
31
|
+
signal?: AbortSignal;
|
|
32
|
+
}): Promise<Response>;
|
|
33
|
+
/** Simple GET returning parsed JSON. */
|
|
34
|
+
get<TResponse = unknown>(path: string, options?: {
|
|
35
|
+
token?: string;
|
|
36
|
+
signal?: AbortSignal;
|
|
37
|
+
}): Promise<TResponse>;
|
|
38
|
+
/** Perform a JSON PATCH and return the parsed response body. */
|
|
39
|
+
patch<TBody = unknown, TResponse = unknown>(path: string, body: TBody, options?: {
|
|
40
|
+
token?: string;
|
|
41
|
+
signal?: AbortSignal;
|
|
42
|
+
}): Promise<TResponse>;
|
|
43
|
+
/** Perform a DELETE request. Expects a 204 No Content response. */
|
|
44
|
+
delete(path: string, options?: {
|
|
45
|
+
token?: string;
|
|
46
|
+
signal?: AbortSignal;
|
|
47
|
+
}): Promise<void>;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Shared types for @tuvl/client.
|
|
52
|
+
*
|
|
53
|
+
* Mirrors the Python-side StepEvent dataclass, the SSE envelope produced by
|
|
54
|
+
* tuvl.core.engine.streaming, and the system_router manifest shapes.
|
|
55
|
+
*/
|
|
56
|
+
/** Returned by all /auth/* login and refresh endpoints. */
|
|
57
|
+
interface TokenResponse {
|
|
58
|
+
access_token: string;
|
|
59
|
+
token_type: string;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Decoded identity and permissions for the currently authenticated user.
|
|
63
|
+
* Returned by GET /auth/me.
|
|
64
|
+
*/
|
|
65
|
+
interface MeResponse {
|
|
66
|
+
/** User UUID extracted from the `user()` Datalog fact in the token. */
|
|
67
|
+
user_id: string;
|
|
68
|
+
/** Role names from all `group()` facts — e.g. ["hr_manager", "member"]. */
|
|
69
|
+
groups: string[];
|
|
70
|
+
/** Permission scopes from all `scope()` facts — e.g. ["candidate:read"]. */
|
|
71
|
+
scopes: string[];
|
|
72
|
+
}
|
|
73
|
+
/** A single execution event streamed by the tuvl server (SSE or gRPC). */
|
|
74
|
+
interface StepEvent {
|
|
75
|
+
event_type: "step";
|
|
76
|
+
step_id: string;
|
|
77
|
+
kind: string;
|
|
78
|
+
signal: string;
|
|
79
|
+
snapshot: Record<string, unknown>;
|
|
80
|
+
duration_ms: number;
|
|
81
|
+
error_detail?: string | null;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Terminal event emitted when the workflow finishes (success OR business failure).
|
|
85
|
+
* Wire shape from tuvl.core.api.manager._sse_generator:
|
|
86
|
+
* {success: true, data: <output>, error: null}
|
|
87
|
+
* {success: false, data: <partial>, error: {...workflow error...}}
|
|
88
|
+
*/
|
|
89
|
+
interface DoneEvent {
|
|
90
|
+
event_type: "done";
|
|
91
|
+
success: boolean;
|
|
92
|
+
data: unknown;
|
|
93
|
+
error: WorkflowErrorPayload | null;
|
|
94
|
+
}
|
|
95
|
+
/** Terminal event emitted when the workflow engine itself raised an exception. */
|
|
96
|
+
interface ErrorEvent {
|
|
97
|
+
event_type: "error";
|
|
98
|
+
message: string;
|
|
99
|
+
details?: string | null;
|
|
100
|
+
}
|
|
101
|
+
/** Terminal event emitted when a workflow hits a Human-in-the-Loop step. */
|
|
102
|
+
interface SuspendedEvent {
|
|
103
|
+
event_type: "suspended";
|
|
104
|
+
instance_id?: string;
|
|
105
|
+
paused_step_id?: string;
|
|
106
|
+
ui?: unknown;
|
|
107
|
+
schema?: unknown;
|
|
108
|
+
context_data?: Record<string, unknown>;
|
|
109
|
+
output_key?: string;
|
|
110
|
+
[key: string]: unknown;
|
|
111
|
+
}
|
|
112
|
+
/** Shape of the `error` field on a non-success DoneEvent / REST envelope. */
|
|
113
|
+
interface WorkflowErrorPayload {
|
|
114
|
+
code?: string;
|
|
115
|
+
message?: string;
|
|
116
|
+
details?: unknown;
|
|
117
|
+
[key: string]: unknown;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Shape of the plain REST response body returned by
|
|
121
|
+
* `POST <workflow_trigger_path>` (and the versioned variant).
|
|
122
|
+
*
|
|
123
|
+
* `execute()` unwraps this and returns just `data` on success, throwing
|
|
124
|
+
* `TuvlWorkflowError` on `success === false`.
|
|
125
|
+
*/
|
|
126
|
+
interface RestEnvelope<TData = unknown> {
|
|
127
|
+
success: boolean;
|
|
128
|
+
status_code: number;
|
|
129
|
+
data: TData | null;
|
|
130
|
+
error: WorkflowErrorPayload | null;
|
|
131
|
+
}
|
|
132
|
+
/** Manifest for a single workflow (from GET /api/_system/workflows/{name}). */
|
|
133
|
+
interface WorkflowManifest {
|
|
134
|
+
name: string;
|
|
135
|
+
trigger_path: string;
|
|
136
|
+
trigger_method: string;
|
|
137
|
+
has_slow_steps: boolean;
|
|
138
|
+
slow_kinds_present: string[];
|
|
139
|
+
required_scope: string | null;
|
|
140
|
+
required_group: string | null;
|
|
141
|
+
steps: Array<{
|
|
142
|
+
id: string;
|
|
143
|
+
kind: string;
|
|
144
|
+
}>;
|
|
145
|
+
}
|
|
146
|
+
/** Map of workflow manifests (from GET /api/_system/workflows). */
|
|
147
|
+
type WorkflowManifestMap = Record<string, Omit<WorkflowManifest, "name" | "steps">>;
|
|
148
|
+
/** Options passed to TuvlClient.execute(). */
|
|
149
|
+
interface ExecuteOptions<_TOutput = unknown> {
|
|
150
|
+
payload?: Record<string, unknown>;
|
|
151
|
+
onProgress?: (event: StepEvent) => void;
|
|
152
|
+
onSuspended?: (event: SuspendedEvent) => void;
|
|
153
|
+
mode?: "sse" | "grpc" | "rest";
|
|
154
|
+
token?: string;
|
|
155
|
+
signal?: AbortSignal;
|
|
156
|
+
}
|
|
157
|
+
/** Options passed to TuvlClient.executeVersioned(). */
|
|
158
|
+
interface ExecuteVersionedOptions<_TOutput = unknown> extends ExecuteOptions<_TOutput> {
|
|
159
|
+
/** The workflow's schema_version, e.g. "v2". Calls /{version}/run/{name}. */
|
|
160
|
+
version: string;
|
|
161
|
+
}
|
|
162
|
+
/** Body sent to POST /api/workflows/resume. */
|
|
163
|
+
interface ResumeRequest {
|
|
164
|
+
instance_id: string;
|
|
165
|
+
human_input?: Record<string, unknown>;
|
|
166
|
+
}
|
|
167
|
+
/** Options passed to TuvlClient.resumeWorkflow(). */
|
|
168
|
+
interface ResumeOptions<_TOutput = unknown> {
|
|
169
|
+
instanceId: string;
|
|
170
|
+
humanInput?: Record<string, unknown>;
|
|
171
|
+
onProgress?: (event: StepEvent) => void;
|
|
172
|
+
onSuspended?: (event: SuspendedEvent) => void;
|
|
173
|
+
mode?: "sse" | "rest";
|
|
174
|
+
token?: string;
|
|
175
|
+
signal?: AbortSignal;
|
|
176
|
+
}
|
|
177
|
+
/** Options passed to TuvlClient constructor. */
|
|
178
|
+
interface TuvlClientOptions {
|
|
179
|
+
baseUrl: string;
|
|
180
|
+
token?: string;
|
|
181
|
+
manifestCacheTtl?: number;
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Options for listing records from a CRUD model endpoint.
|
|
185
|
+
*
|
|
186
|
+
* `filters` maps to bracket-notation query params: `filter[key]=value`.
|
|
187
|
+
* `include` maps to `?include=relation1,relation2` for eager-loading relations.
|
|
188
|
+
*/
|
|
189
|
+
interface CrudListOptions {
|
|
190
|
+
limit?: number;
|
|
191
|
+
offset?: number;
|
|
192
|
+
/** Server-side field filters: { stage: "screening" } → `?filter[stage]=screening` */
|
|
193
|
+
filters?: Record<string, string>;
|
|
194
|
+
/** Relation names to embed: ["posting"] → `?include=posting` */
|
|
195
|
+
include?: string[];
|
|
196
|
+
token?: string;
|
|
197
|
+
signal?: AbortSignal;
|
|
198
|
+
}
|
|
199
|
+
/** Options for retrieving a single CRUD record. */
|
|
200
|
+
interface CrudGetOptions {
|
|
201
|
+
/** Relation names to embed: ["candidate"] → `?include=candidate` */
|
|
202
|
+
include?: string[];
|
|
203
|
+
token?: string;
|
|
204
|
+
signal?: AbortSignal;
|
|
205
|
+
}
|
|
206
|
+
/** Options for create / update / delete calls. */
|
|
207
|
+
interface CrudMutateOptions {
|
|
208
|
+
token?: string;
|
|
209
|
+
signal?: AbortSignal;
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Thrown by `execute()` when a workflow finishes with `success: false`.
|
|
213
|
+
*/
|
|
214
|
+
declare class TuvlWorkflowError extends Error {
|
|
215
|
+
readonly data: unknown;
|
|
216
|
+
readonly error: WorkflowErrorPayload | null;
|
|
217
|
+
constructor(message: string, data: unknown, error: WorkflowErrorPayload | null);
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Thrown by `execute()` when a workflow suspends at a Human-in-the-Loop step.
|
|
221
|
+
*/
|
|
222
|
+
declare class TuvlWorkflowSuspendedError extends Error {
|
|
223
|
+
readonly suspended: SuspendedEvent;
|
|
224
|
+
constructor(suspended: SuspendedEvent);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* CrudClient — typed CRUD access for tuvl model endpoints.
|
|
229
|
+
*
|
|
230
|
+
* The tuvl server mounts auto-generated CRUD routers at:
|
|
231
|
+
* /models/{modelname}/ (list, create)
|
|
232
|
+
* /models/{modelname}/{id} (get, update, delete)
|
|
233
|
+
*
|
|
234
|
+
* Usage via TuvlClient.crud():
|
|
235
|
+
*
|
|
236
|
+
* ```ts
|
|
237
|
+
* const client = new TuvlClient({ baseUrl: "http://localhost:8000", token });
|
|
238
|
+
*
|
|
239
|
+
* // List all candidates
|
|
240
|
+
* const all = await client.crud("candidate").list();
|
|
241
|
+
*
|
|
242
|
+
* // Filter + embed relations
|
|
243
|
+
* const results = await client.crud("candidate").list({
|
|
244
|
+
* filters: { stage: "screening" },
|
|
245
|
+
* include: ["posting"],
|
|
246
|
+
* limit: 50,
|
|
247
|
+
* });
|
|
248
|
+
*
|
|
249
|
+
* // Get one
|
|
250
|
+
* const c = await client.crud("candidate").get("uuid-here");
|
|
251
|
+
*
|
|
252
|
+
* // Create
|
|
253
|
+
* const created = await client.crud<CandidateRead, CandidateCreate>("candidate")
|
|
254
|
+
* .create({ name: "Alice", email: "alice@example.com" });
|
|
255
|
+
*
|
|
256
|
+
* // Update
|
|
257
|
+
* const updated = await client.crud("candidate").update("uuid", { stage: "interview" });
|
|
258
|
+
*
|
|
259
|
+
* // Delete
|
|
260
|
+
* await client.crud("candidate").delete("uuid");
|
|
261
|
+
* ```
|
|
262
|
+
*
|
|
263
|
+
* Type parameters:
|
|
264
|
+
* TRead — shape returned by GET / POST / PATCH (defaults to `unknown`)
|
|
265
|
+
* TCreate — body for POST (defaults to `Partial<TRead>`)
|
|
266
|
+
* TUpdate — body for PATCH (defaults to `Partial<TRead>`)
|
|
267
|
+
*/
|
|
268
|
+
|
|
269
|
+
declare class CrudClient<TRead = unknown, TCreate = Partial<TRead>, TUpdate = Partial<TRead>> {
|
|
270
|
+
private readonly transport;
|
|
271
|
+
private readonly basePath;
|
|
272
|
+
constructor(transport: Transport, modelName: string);
|
|
273
|
+
/** Build query string from list options. */
|
|
274
|
+
private _buildQuery;
|
|
275
|
+
/** Build ?include= query string for get-by-id calls. */
|
|
276
|
+
private _buildGetQuery;
|
|
277
|
+
/**
|
|
278
|
+
* GET /models/{model}/
|
|
279
|
+
* Returns all records matching the given filters (server default: up to 100).
|
|
280
|
+
*/
|
|
281
|
+
list(options?: CrudListOptions): Promise<TRead[]>;
|
|
282
|
+
/**
|
|
283
|
+
* GET /models/{model}/{id}
|
|
284
|
+
* Returns a single record by UUID, optionally with embedded relations.
|
|
285
|
+
*/
|
|
286
|
+
get(id: string, options?: CrudGetOptions): Promise<TRead>;
|
|
287
|
+
/**
|
|
288
|
+
* POST /models/{model}/
|
|
289
|
+
* Creates a new record and returns the created resource (HTTP 201).
|
|
290
|
+
*/
|
|
291
|
+
create(body: TCreate, options?: CrudMutateOptions): Promise<TRead>;
|
|
292
|
+
/**
|
|
293
|
+
* PATCH /models/{model}/{id}
|
|
294
|
+
* Partially updates a record (only fields present in `body` are changed).
|
|
295
|
+
*/
|
|
296
|
+
update(id: string, body: TUpdate, options?: CrudMutateOptions): Promise<TRead>;
|
|
297
|
+
/**
|
|
298
|
+
* DELETE /models/{model}/{id}
|
|
299
|
+
* Deletes a record (HTTP 204). Throws if the record is not found.
|
|
300
|
+
*/
|
|
301
|
+
delete(id: string, options?: CrudMutateOptions): Promise<void>;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* TuvlClient — the main entry point for @tuvl/client.
|
|
306
|
+
*
|
|
307
|
+
* Auto-detects the best transport for each workflow call:
|
|
308
|
+
*
|
|
309
|
+
* mode="rest" (default) → plain POST, unwraps the REST envelope
|
|
310
|
+
* mode="sse" (auto) → SSE streaming when onProgress is provided
|
|
311
|
+
* AND the workflow has_slow_steps === true
|
|
312
|
+
* mode="grpc" → gRPC-Web via sonora (requires peer deps)
|
|
313
|
+
*
|
|
314
|
+
* Return-value contract — IDENTICAL across all three transports:
|
|
315
|
+
*
|
|
316
|
+
* • on success → resolves with the `data` value
|
|
317
|
+
* • on workflow-business error → rejects with `TuvlWorkflowError`
|
|
318
|
+
* • on HITL suspension → rejects with `TuvlWorkflowSuspendedError`
|
|
319
|
+
* • on transport / engine error → rejects with a plain `Error`
|
|
320
|
+
*
|
|
321
|
+
* Manifest caching avoids extra round-trips on repeated calls to the same
|
|
322
|
+
* workflow. TTL defaults to 60 s and is configurable.
|
|
323
|
+
*/
|
|
324
|
+
|
|
325
|
+
declare class TuvlClient {
|
|
326
|
+
private readonly transport;
|
|
327
|
+
private readonly manifestCache;
|
|
328
|
+
private readonly manifestCacheTtl;
|
|
329
|
+
constructor(options: TuvlClientOptions);
|
|
330
|
+
/** Update the default auth token (e.g. after token refresh). */
|
|
331
|
+
setToken(token: string): void;
|
|
332
|
+
/** Expose the base URL for callers that need raw access (e.g. gRPC). */
|
|
333
|
+
get baseUrl(): string;
|
|
334
|
+
/** Fetch (and cache) the manifest for a single workflow. */
|
|
335
|
+
getManifest(workflowName: string, options?: {
|
|
336
|
+
token?: string;
|
|
337
|
+
signal?: AbortSignal;
|
|
338
|
+
}): Promise<WorkflowManifest>;
|
|
339
|
+
/** Fetch manifests for all registered workflows. */
|
|
340
|
+
listWorkflows(options?: {
|
|
341
|
+
token?: string;
|
|
342
|
+
signal?: AbortSignal;
|
|
343
|
+
}): Promise<WorkflowManifestMap>;
|
|
344
|
+
/** Invalidate the cached manifest for a workflow (or all if no name given). */
|
|
345
|
+
invalidateManifest(workflowName?: string): void;
|
|
346
|
+
/**
|
|
347
|
+
* Execute a workflow and return its `data` payload.
|
|
348
|
+
*
|
|
349
|
+
* Transport selection:
|
|
350
|
+
* 1. mode="grpc" → gRPC-Web (always, regardless of onProgress)
|
|
351
|
+
* 2. mode="sse" → SSE stream
|
|
352
|
+
* 3. onProgress provided → SSE if workflow has_slow_steps, else REST
|
|
353
|
+
* 4. default → REST
|
|
354
|
+
*
|
|
355
|
+
* @throws {TuvlWorkflowError} when the workflow returns success=false
|
|
356
|
+
* @throws {TuvlWorkflowSuspendedError} when the workflow suspends at HITL
|
|
357
|
+
* @throws {Error} on transport / engine failures
|
|
358
|
+
*/
|
|
359
|
+
execute<TOutput = unknown>(workflowName: string, options?: ExecuteOptions<TOutput>): Promise<TOutput>;
|
|
360
|
+
/**
|
|
361
|
+
* Execute a specific version of a workflow via `/{version}/run/{name}`.
|
|
362
|
+
*
|
|
363
|
+
* Useful when you want to pin to a particular schema_version without
|
|
364
|
+
* relying on the currently-active default trigger path.
|
|
365
|
+
*
|
|
366
|
+
* @throws {TuvlWorkflowError} when the workflow returns success=false
|
|
367
|
+
* @throws {TuvlWorkflowSuspendedError} when the workflow suspends at HITL
|
|
368
|
+
* @throws {Error} on transport / engine failures
|
|
369
|
+
*/
|
|
370
|
+
executeVersioned<TOutput = unknown>(workflowName: string, version: string, options?: ExecuteVersionedOptions<TOutput> | ExecuteOptions<TOutput>): Promise<TOutput>;
|
|
371
|
+
/**
|
|
372
|
+
* Resume a HITL-suspended workflow instance.
|
|
373
|
+
*
|
|
374
|
+
* After the workflow throws `TuvlWorkflowSuspendedError`, capture the
|
|
375
|
+
* `event.instance_id` from the suspended event and pass it here along with
|
|
376
|
+
* the human-provided data to continue execution.
|
|
377
|
+
*
|
|
378
|
+
* @throws {TuvlWorkflowError} when the resumed workflow returns success=false
|
|
379
|
+
* @throws {TuvlWorkflowSuspendedError} when the workflow suspends again at another HITL step
|
|
380
|
+
* @throws {Error} on transport / engine failures
|
|
381
|
+
*/
|
|
382
|
+
resumeWorkflow<TOutput = unknown>(options: ResumeOptions<TOutput>): Promise<TOutput>;
|
|
383
|
+
private _executeSse;
|
|
384
|
+
private _executeGrpc;
|
|
385
|
+
private _unwrapRest;
|
|
386
|
+
/**
|
|
387
|
+
* Return a typed CRUD client for a tuvl model endpoint.
|
|
388
|
+
*
|
|
389
|
+
* The returned client targets `/models/{modelName}/` and shares the
|
|
390
|
+
* same transport (base URL + auth token) as this TuvlClient instance.
|
|
391
|
+
*
|
|
392
|
+
* @example
|
|
393
|
+
* ```ts
|
|
394
|
+
* // List all candidates
|
|
395
|
+
* const all = await client.crud("candidate").list();
|
|
396
|
+
*
|
|
397
|
+
* // With filters + embedded relations
|
|
398
|
+
* const results = await client.crud("candidate").list({
|
|
399
|
+
* filters: { stage: "screening" },
|
|
400
|
+
* include: ["posting"],
|
|
401
|
+
* limit: 50,
|
|
402
|
+
* });
|
|
403
|
+
*
|
|
404
|
+
* // Get one record
|
|
405
|
+
* const c = await client.crud("candidate").get("uuid-here");
|
|
406
|
+
*
|
|
407
|
+
* // Strongly-typed variant
|
|
408
|
+
* const c2 = await client
|
|
409
|
+
* .crud<CandidateRead, CandidateCreate>("candidate")
|
|
410
|
+
* .create({ name: "Alice", email: "alice@example.com" });
|
|
411
|
+
* ```
|
|
412
|
+
*/
|
|
413
|
+
crud<TRead = unknown, TCreate = Partial<TRead>, TUpdate = Partial<TRead>>(modelName: string): CrudClient<TRead, TCreate, TUpdate>;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
/**
|
|
417
|
+
* TuvlAuth — authentication helpers for @tuvl/client.
|
|
418
|
+
*
|
|
419
|
+
* Wraps the most common /auth/* endpoints (login, refresh, logout, who-am-I,
|
|
420
|
+
* OAuth start, bootstrap) so consumers never have to manually construct
|
|
421
|
+
* form-encoded bodies or manage Bearer headers for auth operations.
|
|
422
|
+
*
|
|
423
|
+
* Admin endpoints (user/role/scope CRUD, federation config) are NOT wrapped
|
|
424
|
+
* — call them directly via `Transport` or `fetch` with the token.
|
|
425
|
+
*/
|
|
426
|
+
|
|
427
|
+
interface TuvlAuthOptions {
|
|
428
|
+
/** Base URL of the tuvl server. Trailing slash is stripped automatically. */
|
|
429
|
+
baseUrl: string;
|
|
430
|
+
}
|
|
431
|
+
interface BootstrapRequest {
|
|
432
|
+
email: string;
|
|
433
|
+
password: string;
|
|
434
|
+
/** Optional extra scope to grant the superadmin role on creation. */
|
|
435
|
+
admin_scope?: string;
|
|
436
|
+
}
|
|
437
|
+
declare class TuvlAuth {
|
|
438
|
+
private readonly baseUrl;
|
|
439
|
+
constructor(options: TuvlAuthOptions);
|
|
440
|
+
/**
|
|
441
|
+
* Decode the current token server-side and return the user's identity,
|
|
442
|
+
* role memberships (`groups`), and permission scopes.
|
|
443
|
+
*
|
|
444
|
+
* Biscuit tokens are protobuf-encoded and cannot be decoded in pure JS,
|
|
445
|
+
* so this is the correct way to read "who is logged in" and "what can
|
|
446
|
+
* they do" from the TypeScript SDK.
|
|
447
|
+
*
|
|
448
|
+
* @throws if the token is invalid, expired, or revoked.
|
|
449
|
+
*/
|
|
450
|
+
getMe(token: string): Promise<MeResponse>;
|
|
451
|
+
/**
|
|
452
|
+
* Exchange an email + password for a Biscuit bearer token.
|
|
453
|
+
*
|
|
454
|
+
* Calls `POST /auth/token` with an `application/x-www-form-urlencoded`
|
|
455
|
+
* body (OAuth2 password-grant). The `username` field must be the user's
|
|
456
|
+
* email address.
|
|
457
|
+
*/
|
|
458
|
+
loginWithPassword(email: string, password: string): Promise<TokenResponse>;
|
|
459
|
+
/**
|
|
460
|
+
* Create the first superadmin user (one-time IAM bootstrap).
|
|
461
|
+
*
|
|
462
|
+
* Calls `POST /auth/bootstrap`. Returns 409 on every subsequent call
|
|
463
|
+
* once any user exists.
|
|
464
|
+
*/
|
|
465
|
+
bootstrap(req: BootstrapRequest): Promise<TokenResponse>;
|
|
466
|
+
/**
|
|
467
|
+
* Return the URL the browser should navigate to in order to start an
|
|
468
|
+
* OAuth2 login flow for the given provider.
|
|
469
|
+
*
|
|
470
|
+
* Supported built-ins: `"google"`, `"github"`, `"microsoft"`. Any
|
|
471
|
+
* provider configured in the project's `federation/` directory also
|
|
472
|
+
* works.
|
|
473
|
+
*
|
|
474
|
+
* After the OAuth dance the server either:
|
|
475
|
+
* - redirects to `TUVL_OAUTH_UI_REDIRECT_URL?token=<biscuit_b64>`, OR
|
|
476
|
+
* - returns a JSON `{access_token, token_type}` body (CLI / server flows).
|
|
477
|
+
*/
|
|
478
|
+
getOAuthLoginUrl(provider: string): string;
|
|
479
|
+
/**
|
|
480
|
+
* Exchange a valid (non-expired, non-revoked) token for a fresh one.
|
|
481
|
+
* The old token is immediately blacklisted — discard it after this call.
|
|
482
|
+
*/
|
|
483
|
+
refresh(token: string): Promise<TokenResponse>;
|
|
484
|
+
/**
|
|
485
|
+
* Revoke the given token (logout). After this the token is blacklisted
|
|
486
|
+
* across all workers that share a Redis instance. Resolves silently on
|
|
487
|
+
* success (HTTP 204).
|
|
488
|
+
*/
|
|
489
|
+
logout(token: string): Promise<void>;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
/**
|
|
493
|
+
* SSE transport for @tuvl/client.
|
|
494
|
+
*
|
|
495
|
+
* Uses the Fetch API ReadableStream to parse SSE frames from a POST response
|
|
496
|
+
* opened by Transport.postStream(). This avoids the browser's native
|
|
497
|
+
* EventSource (which only supports GET) and works in Node 18+.
|
|
498
|
+
*
|
|
499
|
+
* Wire format produced by tuvl.core.engine.streaming:
|
|
500
|
+
*
|
|
501
|
+
* event: step data: {step_id, kind, signal, snapshot, duration_ms, error_detail}
|
|
502
|
+
* event: suspended data: {<hitl_request payload>}
|
|
503
|
+
* event: done data: {success, data, error}
|
|
504
|
+
* event: error data: {message, details}
|
|
505
|
+
*
|
|
506
|
+
* The JSON body has NO `event_type` field — it lives only on the `event:`
|
|
507
|
+
* line. This parser injects the event name as `event_type` into the yielded
|
|
508
|
+
* object so downstream consumers can switch on a single discriminator.
|
|
509
|
+
*/
|
|
510
|
+
|
|
511
|
+
type SseFrame = StepEvent | DoneEvent | ErrorEvent | SuspendedEvent;
|
|
512
|
+
/**
|
|
513
|
+
* Parse an SSE stream from a Fetch Response body.
|
|
514
|
+
*
|
|
515
|
+
* Yields typed frames as they arrive. The generator terminates when it
|
|
516
|
+
* receives an event with event_type "done", "error", or "suspended", or
|
|
517
|
+
* when the stream closes.
|
|
518
|
+
*/
|
|
519
|
+
declare function parseSseStream(response: Response, signal?: AbortSignal): AsyncGenerator<SseFrame>;
|
|
520
|
+
|
|
521
|
+
/**
|
|
522
|
+
* gRPC-Web transport for @tuvl/client.
|
|
523
|
+
*
|
|
524
|
+
* This module is an OPTIONAL transport that requires the peer dependencies:
|
|
525
|
+
* @protobuf-ts/grpcweb-transport
|
|
526
|
+
* @protobuf-ts/runtime-rpc
|
|
527
|
+
*
|
|
528
|
+
* It uses dynamic import() so the main bundle has zero overhead if you never
|
|
529
|
+
* use mode="grpc". The protobuf definitions are created inline rather than
|
|
530
|
+
* from generated stubs, keeping the SDK self-contained (no make proto step
|
|
531
|
+
* required on the client side).
|
|
532
|
+
*
|
|
533
|
+
* The wire format matches execution.proto:
|
|
534
|
+
* service ExecutionService { rpc RunWorkflow(RunRequest) → stream StepEvent }
|
|
535
|
+
*/
|
|
536
|
+
/**
|
|
537
|
+
* gRPC StepEvent on the wire. Unlike the typed SSE `StepEvent` (which is
|
|
538
|
+
* narrowed to `event_type: "step"`), this can carry "step" | "done" |
|
|
539
|
+
* "error" | "suspended" — the discriminator the servicer chose.
|
|
540
|
+
*/
|
|
541
|
+
interface GrpcStepEvent {
|
|
542
|
+
event_type: "step" | "done" | "error" | "suspended";
|
|
543
|
+
step_id: string;
|
|
544
|
+
kind: string;
|
|
545
|
+
signal: string;
|
|
546
|
+
/** Decoded from `snapshot_json` on the wire. */
|
|
547
|
+
snapshot: Record<string, unknown>;
|
|
548
|
+
duration_ms: number;
|
|
549
|
+
error_detail?: string;
|
|
550
|
+
}
|
|
551
|
+
interface GrpcRunOptions {
|
|
552
|
+
baseUrl: string;
|
|
553
|
+
workflowName: string;
|
|
554
|
+
payloadJson: string;
|
|
555
|
+
tokenFallback?: string;
|
|
556
|
+
token?: string;
|
|
557
|
+
signal?: AbortSignal;
|
|
558
|
+
}
|
|
559
|
+
/**
|
|
560
|
+
* Open a gRPC-Web server-streaming call to ExecutionService.RunWorkflow.
|
|
561
|
+
*
|
|
562
|
+
* Yields `GrpcStepEvent` objects (with `snapshot_json` already parsed into
|
|
563
|
+
* `snapshot`). The caller decides what each `event_type` means.
|
|
564
|
+
*
|
|
565
|
+
* Throws if the peer deps are not installed.
|
|
566
|
+
*/
|
|
567
|
+
declare function openGrpcStream(options: GrpcRunOptions): AsyncGenerator<GrpcStepEvent>;
|
|
568
|
+
|
|
569
|
+
export { type BootstrapRequest, CrudClient, type CrudGetOptions, type CrudListOptions, type CrudMutateOptions, type DoneEvent, type ErrorEvent, type ExecuteOptions, type ExecuteVersionedOptions, type MeResponse, type RestEnvelope, type ResumeOptions, type ResumeRequest, type StepEvent, type SuspendedEvent, type TokenResponse, Transport, TuvlAuth, type TuvlAuthOptions, TuvlClient, type TuvlClientOptions, TuvlWorkflowError, TuvlWorkflowSuspendedError, type WorkflowErrorPayload, type WorkflowManifest, type WorkflowManifestMap, openGrpcStream, parseSseStream };
|