@openwop/openwop 1.0.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,171 @@
1
+ /**
2
+ * Request/response types for the openwop REST surface.
3
+ *
4
+ * Mirrors `api/openapi.yaml` and the JSON Schemas in
5
+ * `schemas/`. Hand-authored rather than codegen'd — see
6
+ * SDK README §rationale.
7
+ *
8
+ * Forward-compat: types use `string` (not narrow unions) for fields whose
9
+ * spec'd values may grow over time (status enums, event types, error codes).
10
+ * Consumers wanting exhaustive narrowing should `as const` their checks
11
+ * rather than relying on the SDK to refuse unknown values.
12
+ */
13
+ /** Run statuses per `RunSnapshot.status` in OpenAPI. */
14
+ export type RunStatus = 'pending' | 'running' | 'paused' | 'waiting-approval' | 'waiting-input' | 'completed' | 'failed' | 'cancelled';
15
+ export interface Capabilities {
16
+ protocolVersion: string;
17
+ supportedEnvelopes: readonly string[];
18
+ schemaVersions: Record<string, number>;
19
+ limits: {
20
+ clarificationRounds: number;
21
+ schemaRounds: number;
22
+ envelopesPerTurn: number;
23
+ maxNodeExecutions?: number;
24
+ };
25
+ extensions?: Record<string, unknown>;
26
+ implementation?: {
27
+ name?: string;
28
+ version?: string;
29
+ vendor?: string;
30
+ };
31
+ engineVersion?: number;
32
+ eventLogSchemaVersion?: number;
33
+ supportedTransports?: readonly ('rest' | 'mcp' | 'a2a' | 'grpc')[];
34
+ configurable?: Record<string, unknown>;
35
+ observability?: Record<string, unknown>;
36
+ minClientVersion?: string;
37
+ }
38
+ export interface RunSnapshot {
39
+ runId: string;
40
+ workflowId: string;
41
+ status: RunStatus;
42
+ currentNodeId?: string;
43
+ startedAt?: string;
44
+ completedAt?: string;
45
+ nodeStates?: Record<string, unknown>;
46
+ variables?: Record<string, unknown>;
47
+ channels?: Record<string, unknown>;
48
+ error?: {
49
+ code?: string;
50
+ message?: string;
51
+ };
52
+ }
53
+ /**
54
+ * Per-run parameter overlay carried in `RunOptions.configurable`. Reserved
55
+ * keys are typed; unknown keys are passed through verbatim. See
56
+ * `run-options.md`.
57
+ */
58
+ export interface RunConfigurable {
59
+ /** Override the per-run node-execution ceiling. Clamped server-side. */
60
+ recursionLimit?: number;
61
+ /** Override AI model for nodes that consume `ctx.config.configurable.model`. */
62
+ model?: string;
63
+ /** Override AI temperature (server SHOULD enforce 0..2). */
64
+ temperature?: number;
65
+ /** Override AI max-tokens cap. */
66
+ maxTokens?: number;
67
+ /** Per-prompt-ID variant override map. */
68
+ promptOverrides?: Record<string, string>;
69
+ /** Implementation-specific extensions; passed through verbatim. */
70
+ [key: string]: unknown;
71
+ }
72
+ export interface CreateRunRequest {
73
+ workflowId: string;
74
+ inputs?: Record<string, unknown>;
75
+ tenantId?: string;
76
+ scopeId?: string;
77
+ callbackUrl?: string;
78
+ configurable?: RunConfigurable;
79
+ tags?: readonly string[];
80
+ metadata?: Record<string, unknown>;
81
+ }
82
+ export interface CreateRunResponse {
83
+ runId: string;
84
+ status: RunStatus;
85
+ eventsUrl: string;
86
+ statusUrl?: string;
87
+ }
88
+ export interface CancelRunRequest {
89
+ reason?: string;
90
+ }
91
+ export interface CancelRunResponse {
92
+ runId: string;
93
+ status: 'cancelled' | 'cancelling';
94
+ }
95
+ export interface ForkRunRequest {
96
+ fromSeq: number;
97
+ mode: 'replay' | 'branch';
98
+ runOptionsOverlay?: Record<string, unknown>;
99
+ }
100
+ export interface ForkRunResponse {
101
+ runId: string;
102
+ sourceRunId: string;
103
+ fromSeq?: number;
104
+ mode: 'replay' | 'branch';
105
+ status: RunStatus;
106
+ eventsUrl: string;
107
+ }
108
+ export interface ResolveInterruptRequest {
109
+ resumeValue: unknown;
110
+ }
111
+ export interface ResolveInterruptResponse {
112
+ runId: string;
113
+ nodeId: string;
114
+ status: RunStatus;
115
+ }
116
+ /**
117
+ * Token-scoped interrupt inspection response — mirrors `suspend-request.schema.json`
118
+ * (the `InterruptPayload` shape).
119
+ */
120
+ export interface InterruptByTokenInspection {
121
+ kind: 'approval' | 'clarification' | 'external-event' | 'custom';
122
+ key: string;
123
+ resumeSchema?: Record<string, unknown>;
124
+ timeoutMs?: number;
125
+ data: unknown;
126
+ }
127
+ export interface ResolveInterruptByTokenResponse {
128
+ [key: string]: unknown;
129
+ }
130
+ export interface PollEventsResponse {
131
+ events: readonly RunEventDoc[];
132
+ isComplete: boolean;
133
+ }
134
+ /** Mirror of `run-event.schema.json` — top-level shape only. */
135
+ export interface RunEventDoc {
136
+ eventId: string;
137
+ runId: string;
138
+ nodeId?: string;
139
+ type: string;
140
+ payload: unknown;
141
+ timestamp: string;
142
+ sequence: number;
143
+ schemaVersion?: number;
144
+ engineVersion?: string;
145
+ causationId?: string;
146
+ }
147
+ export interface ErrorEnvelope {
148
+ error: string;
149
+ message: string;
150
+ details?: Record<string, unknown>;
151
+ }
152
+ export type StreamMode = 'values' | 'updates' | 'messages' | 'debug';
153
+ /**
154
+ * Thrown when the server returns a non-2xx response. Carries the original
155
+ * status, parsed error envelope (if available), the raw response text,
156
+ * and any `traceparent` the server returned (per
157
+ * `observability.md` §Trace context propagation —
158
+ * "Clients SHOULD display the trace ID in error messages so operators
159
+ * can search backend traces").
160
+ */
161
+ export declare class WopError extends Error {
162
+ readonly status: number;
163
+ readonly envelope: ErrorEnvelope | undefined;
164
+ readonly rawText: string;
165
+ /** W3C `traceparent` from the response headers, when present. */
166
+ readonly traceparent: string | undefined;
167
+ /** 32-hex-char trace ID extracted from `traceparent`, when parseable. */
168
+ readonly traceId: string | undefined;
169
+ constructor(status: number, rawText: string, envelope: ErrorEnvelope | undefined, traceparent: string | undefined);
170
+ }
171
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/dist/types.js ADDED
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Request/response types for the openwop REST surface.
3
+ *
4
+ * Mirrors `api/openapi.yaml` and the JSON Schemas in
5
+ * `schemas/`. Hand-authored rather than codegen'd — see
6
+ * SDK README §rationale.
7
+ *
8
+ * Forward-compat: types use `string` (not narrow unions) for fields whose
9
+ * spec'd values may grow over time (status enums, event types, error codes).
10
+ * Consumers wanting exhaustive narrowing should `as const` their checks
11
+ * rather than relying on the SDK to refuse unknown values.
12
+ */
13
+ /**
14
+ * Thrown when the server returns a non-2xx response. Carries the original
15
+ * status, parsed error envelope (if available), the raw response text,
16
+ * and any `traceparent` the server returned (per
17
+ * `observability.md` §Trace context propagation —
18
+ * "Clients SHOULD display the trace ID in error messages so operators
19
+ * can search backend traces").
20
+ */
21
+ export class WopError extends Error {
22
+ status;
23
+ envelope;
24
+ rawText;
25
+ /** W3C `traceparent` from the response headers, when present. */
26
+ traceparent;
27
+ /** 32-hex-char trace ID extracted from `traceparent`, when parseable. */
28
+ traceId;
29
+ constructor(status, rawText, envelope, traceparent) {
30
+ const traceId = traceparent ? extractTraceId(traceparent) : undefined;
31
+ const baseMessage = envelope?.message ?? `openwop request failed: HTTP ${status}`;
32
+ const messageWithTrace = traceId ? `${baseMessage} (trace=${traceId})` : baseMessage;
33
+ super(messageWithTrace);
34
+ this.name = 'WopError';
35
+ this.status = status;
36
+ this.rawText = rawText;
37
+ this.envelope = envelope;
38
+ this.traceparent = traceparent;
39
+ this.traceId = traceId;
40
+ }
41
+ }
42
+ /**
43
+ * Extract the 32-hex trace ID from a W3C traceparent header. Format:
44
+ * `00-<32-hex>-<16-hex>-<2-hex>`. Returns undefined for malformed
45
+ * input — never throws (errors during error construction would be
46
+ * truly miserable).
47
+ */
48
+ function extractTraceId(traceparent) {
49
+ const parts = traceparent.split('-');
50
+ if (parts.length < 3)
51
+ return undefined;
52
+ const traceId = parts[1];
53
+ if (!traceId || !/^[0-9a-f]{32}$/i.test(traceId))
54
+ return undefined;
55
+ return traceId;
56
+ }
57
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAoKH;;;;;;;GAOG;AACH,MAAM,OAAO,QAAS,SAAQ,KAAK;IACxB,MAAM,CAAS;IACf,QAAQ,CAA4B;IACpC,OAAO,CAAS;IACzB,iEAAiE;IACxD,WAAW,CAAqB;IACzC,yEAAyE;IAChE,OAAO,CAAqB;IAErC,YACE,MAAc,EACd,OAAe,EACf,QAAmC,EACnC,WAA+B;QAE/B,MAAM,OAAO,GAAG,WAAW,CAAC,CAAC,CAAC,cAAc,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QACtE,MAAM,WAAW,GAAG,QAAQ,EAAE,OAAO,IAAI,gCAAgC,MAAM,EAAE,CAAC;QAClF,MAAM,gBAAgB,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,WAAW,WAAW,OAAO,GAAG,CAAC,CAAC,CAAC,WAAW,CAAC;QACrF,KAAK,CAAC,gBAAgB,CAAC,CAAC;QACxB,IAAI,CAAC,IAAI,GAAG,UAAU,CAAC;QACvB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;QAC/B,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACzB,CAAC;CACF;AAED;;;;;GAKG;AACH,SAAS,cAAc,CAAC,WAAmB;IACzC,MAAM,KAAK,GAAG,WAAW,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACrC,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,SAAS,CAAC;IACvC,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;IACzB,IAAI,CAAC,OAAO,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,OAAO,CAAC;QAAE,OAAO,SAAS,CAAC;IACnE,OAAO,OAAO,CAAC;AACjB,CAAC"}
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "@openwop/openwop",
3
+ "version": "1.0.0",
4
+ "description": "Production-ready TypeScript reference SDK for OpenWOP v1.0 compliant servers.",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git+https://github.com/openwop/openwop.git",
8
+ "directory": "sdk/typescript"
9
+ },
10
+ "license": "Apache-2.0",
11
+ "type": "module",
12
+ "main": "./dist/index.js",
13
+ "types": "./dist/index.d.ts",
14
+ "exports": {
15
+ ".": {
16
+ "types": "./dist/index.d.ts",
17
+ "import": "./dist/index.js"
18
+ }
19
+ },
20
+ "files": [
21
+ "dist",
22
+ "src",
23
+ "!src/__tests__",
24
+ "README.md",
25
+ "LICENSE"
26
+ ],
27
+ "scripts": {
28
+ "build": "rm -rf dist && tsc -p tsconfig.build.json",
29
+ "typecheck": "tsc --noEmit",
30
+ "test": "vitest run",
31
+ "prepublishOnly": "npm run build"
32
+ },
33
+ "engines": {
34
+ "node": ">=20"
35
+ },
36
+ "devDependencies": {
37
+ "typescript": "^5.6.0",
38
+ "@types/node": "^22.0.0",
39
+ "vitest": "^2.1"
40
+ }
41
+ }
package/src/client.ts ADDED
@@ -0,0 +1,277 @@
1
+ /**
2
+ * OpenwopClient — typed HTTP client for the openwop REST surface.
3
+ *
4
+ * Hand-authored. Each method maps 1:1 to a documented endpoint in
5
+ * ../../api/openapi.yaml. Request/response types live in ./types.ts.
6
+ *
7
+ * Auth: a single bearer-style API key, supplied at construction. See
8
+ * ../../auth.md for credential format.
9
+ */
10
+
11
+ import { streamEvents, type EventsStreamOptions } from './sse.js';
12
+ import {
13
+ WopError,
14
+ type Capabilities,
15
+ type CancelRunRequest,
16
+ type CancelRunResponse,
17
+ type CreateRunRequest,
18
+ type CreateRunResponse,
19
+ type ErrorEnvelope,
20
+ type ForkRunRequest,
21
+ type ForkRunResponse,
22
+ type InterruptByTokenInspection,
23
+ type PollEventsResponse,
24
+ type ResolveInterruptByTokenResponse,
25
+ type ResolveInterruptRequest,
26
+ type ResolveInterruptResponse,
27
+ type RunEventDoc,
28
+ type RunSnapshot,
29
+ } from './types.js';
30
+
31
+ export interface OpenwopClientOptions {
32
+ /** Base URL of the openwop server, e.g., `https://api.example.com`. Trailing slash optional. */
33
+ readonly baseUrl: string;
34
+ /** API key (bearer-style). See auth.md. */
35
+ readonly apiKey: string;
36
+ /** Optional fetch implementation override (test injection). Defaults to global `fetch`. */
37
+ readonly fetch?: typeof fetch;
38
+ /** Default `Accept-Language` to send. Optional. */
39
+ readonly acceptLanguage?: string;
40
+ }
41
+
42
+ export interface MutationOptions {
43
+ /** RFC-spec'd Idempotency-Key for at-most-once mutation semantics. */
44
+ readonly idempotencyKey?: string;
45
+ /** Optional X-Dedup hint for cross-host claim coordination on POST /v1/runs. */
46
+ readonly dedup?: 'enforce';
47
+ }
48
+
49
+ interface RawRequestOptions {
50
+ readonly method: string;
51
+ readonly path: string;
52
+ readonly body?: unknown;
53
+ readonly headers?: Record<string, string>;
54
+ readonly signal?: AbortSignal;
55
+ }
56
+
57
+ export class OpenwopClient {
58
+ readonly #baseUrl: string;
59
+ readonly #apiKey: string;
60
+ readonly #fetch: typeof fetch;
61
+ readonly #acceptLanguage: string | undefined;
62
+
63
+ constructor(opts: OpenwopClientOptions) {
64
+ if (!opts.baseUrl) throw new TypeError('OpenwopClient: baseUrl is required');
65
+ if (!opts.apiKey) throw new TypeError('OpenwopClient: apiKey is required');
66
+ this.#baseUrl = opts.baseUrl.replace(/\/$/, '');
67
+ this.#apiKey = opts.apiKey;
68
+ this.#fetch = opts.fetch ?? fetch;
69
+ this.#acceptLanguage = opts.acceptLanguage;
70
+ }
71
+
72
+ // ── Discovery ────────────────────────────────────────────────────────
73
+ readonly discovery = {
74
+ capabilities: (): Promise<Capabilities> =>
75
+ this.#request<Capabilities>({ method: 'GET', path: '/.well-known/openwop' }, false),
76
+
77
+ openapi: (): Promise<unknown> =>
78
+ this.#request<unknown>({ method: 'GET', path: '/v1/openapi.json' }, false),
79
+ };
80
+
81
+ // ── Workflows ────────────────────────────────────────────────────────
82
+ readonly workflows = {
83
+ get: (workflowId: string): Promise<unknown> =>
84
+ this.#request<unknown>({
85
+ method: 'GET',
86
+ path: `/v1/workflows/${encodeURIComponent(workflowId)}`,
87
+ }),
88
+ };
89
+
90
+ // ── Runs ─────────────────────────────────────────────────────────────
91
+ readonly runs = {
92
+ create: (body: CreateRunRequest, opts: MutationOptions = {}): Promise<CreateRunResponse> =>
93
+ this.#request<CreateRunResponse>({
94
+ method: 'POST',
95
+ path: '/v1/runs',
96
+ body,
97
+ headers: this.#mutationHeaders(opts),
98
+ }),
99
+
100
+ get: (runId: string): Promise<RunSnapshot> =>
101
+ this.#request<RunSnapshot>({
102
+ method: 'GET',
103
+ path: `/v1/runs/${encodeURIComponent(runId)}`,
104
+ }),
105
+
106
+ cancel: (
107
+ runId: string,
108
+ body: CancelRunRequest = {},
109
+ opts: MutationOptions = {},
110
+ ): Promise<CancelRunResponse> =>
111
+ this.#request<CancelRunResponse>({
112
+ method: 'POST',
113
+ path: `/v1/runs/${encodeURIComponent(runId)}/cancel`,
114
+ body,
115
+ headers: this.#mutationHeaders(opts),
116
+ }),
117
+
118
+ fork: (
119
+ runId: string,
120
+ body: ForkRunRequest,
121
+ opts: MutationOptions = {},
122
+ ): Promise<ForkRunResponse> =>
123
+ this.#request<ForkRunResponse>({
124
+ method: 'POST',
125
+ path: `/v1/runs/${encodeURIComponent(runId)}:fork`,
126
+ body,
127
+ headers: this.#mutationHeaders(opts),
128
+ }),
129
+
130
+ pollEvents: (
131
+ runId: string,
132
+ params: { lastSequence?: number; timeoutSeconds?: number } = {},
133
+ ): Promise<PollEventsResponse> => {
134
+ const search = new URLSearchParams();
135
+ if (params.lastSequence !== undefined) {
136
+ search.set('lastSequence', String(params.lastSequence));
137
+ }
138
+ if (params.timeoutSeconds !== undefined) {
139
+ search.set('timeout', String(params.timeoutSeconds));
140
+ }
141
+ const qs = search.toString();
142
+ return this.#request<PollEventsResponse>({
143
+ method: 'GET',
144
+ path: `/v1/runs/${encodeURIComponent(runId)}/events/poll${qs ? `?${qs}` : ''}`,
145
+ });
146
+ },
147
+
148
+ /**
149
+ * Async-iterable SSE consumer. The connection auto-closes when the
150
+ * server closes the stream (terminal run event); break out of the
151
+ * loop or call `signal.abort()` to terminate early.
152
+ */
153
+ events: (runId: string, opts: EventsStreamOptions = {}): AsyncGenerator<RunEventDoc, void, void> =>
154
+ streamEvents({ baseUrl: this.#baseUrl, apiKey: this.#apiKey }, runId, opts),
155
+ };
156
+
157
+ // ── HITL interrupts (run-scoped + signed-token) ──────────────────────
158
+ readonly interrupts = {
159
+ resolveByRun: (
160
+ runId: string,
161
+ nodeId: string,
162
+ body: ResolveInterruptRequest,
163
+ opts: MutationOptions = {},
164
+ ): Promise<ResolveInterruptResponse> =>
165
+ this.#request<ResolveInterruptResponse>({
166
+ method: 'POST',
167
+ path: `/v1/runs/${encodeURIComponent(runId)}/interrupts/${encodeURIComponent(nodeId)}`,
168
+ body,
169
+ headers: this.#mutationHeaders(opts),
170
+ }),
171
+
172
+ /**
173
+ * Inspect an interrupt via signed token — useful for showing the
174
+ * interrupt's `kind`, `data`, and `resumeSchema` to a downstream
175
+ * UI before the user resolves. Token is the auth, no API key
176
+ * required (signed-token endpoints intentionally bypass bearer
177
+ * auth so external systems can resolve without openwop credentials).
178
+ */
179
+ inspectByToken: (token: string): Promise<InterruptByTokenInspection> =>
180
+ this.#request<InterruptByTokenInspection>(
181
+ {
182
+ method: 'GET',
183
+ path: `/v1/interrupts/${encodeURIComponent(token)}`,
184
+ },
185
+ false, // unauthenticated (token IS the auth)
186
+ ),
187
+
188
+ /**
189
+ * Resolve an interrupt via signed token — used by external
190
+ * systems (calendar webhooks, payment confirmations) that the
191
+ * engine handed a callback URL at suspension time.
192
+ */
193
+ resolveByToken: (
194
+ token: string,
195
+ body: ResolveInterruptRequest,
196
+ opts: MutationOptions = {},
197
+ ): Promise<ResolveInterruptByTokenResponse> =>
198
+ this.#request<ResolveInterruptByTokenResponse>(
199
+ {
200
+ method: 'POST',
201
+ path: `/v1/interrupts/${encodeURIComponent(token)}`,
202
+ body,
203
+ headers: this.#mutationHeaders(opts),
204
+ },
205
+ false, // unauthenticated (token IS the auth)
206
+ ),
207
+ };
208
+
209
+ // ── Internals ────────────────────────────────────────────────────────
210
+ #mutationHeaders(opts: MutationOptions): Record<string, string> {
211
+ const h: Record<string, string> = {};
212
+ if (opts.idempotencyKey) h['Idempotency-Key'] = opts.idempotencyKey;
213
+ if (opts.dedup) h['X-Dedup'] = opts.dedup;
214
+ return h;
215
+ }
216
+
217
+ async #request<T>(opts: RawRequestOptions, authenticated = true): Promise<T> {
218
+ const url = `${this.#baseUrl}${opts.path}`;
219
+ const headers: Record<string, string> = {
220
+ Accept: 'application/json',
221
+ ...(opts.headers ?? {}),
222
+ };
223
+ if (opts.body !== undefined && headers['Content-Type'] === undefined) {
224
+ headers['Content-Type'] = 'application/json';
225
+ }
226
+ if (authenticated) {
227
+ headers.Authorization = `Bearer ${this.#apiKey}`;
228
+ }
229
+ if (this.#acceptLanguage) {
230
+ headers['Accept-Language'] = this.#acceptLanguage;
231
+ }
232
+
233
+ const init: RequestInit = { method: opts.method, headers };
234
+ if (opts.body !== undefined) {
235
+ init.body = JSON.stringify(opts.body);
236
+ }
237
+ if (opts.signal) {
238
+ init.signal = opts.signal;
239
+ }
240
+
241
+ const res = await this.#fetch(url, init);
242
+ const text = await res.text();
243
+ // Capture traceparent for error reporting per observability.md
244
+ // §Trace context propagation. Header name is case-insensitive per
245
+ // RFC 9110; fetch normalizes to lowercase but be defensive.
246
+ const traceparent =
247
+ res.headers.get('traceparent') ?? res.headers.get('Traceparent') ?? undefined;
248
+
249
+ if (!res.ok) {
250
+ let env: ErrorEnvelope | undefined;
251
+ try {
252
+ const parsed = text.length > 0 ? JSON.parse(text) : undefined;
253
+ if (parsed && typeof parsed === 'object' && 'error' in parsed && 'message' in parsed) {
254
+ env = parsed as ErrorEnvelope;
255
+ }
256
+ } catch {
257
+ // not JSON; leave envelope undefined
258
+ }
259
+ throw new WopError(res.status, text, env, traceparent);
260
+ }
261
+
262
+ if (text.length === 0) return undefined as T;
263
+ try {
264
+ return JSON.parse(text) as T;
265
+ } catch {
266
+ throw new WopError(
267
+ res.status,
268
+ text,
269
+ {
270
+ error: 'invalid_json',
271
+ message: 'Server returned non-JSON body for a 2xx response',
272
+ },
273
+ traceparent,
274
+ );
275
+ }
276
+ }
277
+ }
package/src/index.ts ADDED
@@ -0,0 +1,57 @@
1
+ /**
2
+ * @openwop/openwop — TypeScript reference SDK for OpenWOP-compliant servers.
3
+ *
4
+ * Public surface:
5
+ * - OpenwopClient (auth + endpoint methods)
6
+ * - WopError (typed error wrapping ErrorEnvelope)
7
+ * - All request/response types mirroring the OpenAPI spec
8
+ * - streamEvents helper for advanced SSE use cases
9
+ *
10
+ * See README.md for usage examples.
11
+ */
12
+
13
+ export { OpenwopClient } from './client.js';
14
+ export type { OpenwopClientOptions, MutationOptions } from './client.js';
15
+ export { WopError } from './types.js';
16
+ export type {
17
+ Capabilities,
18
+ CancelRunRequest,
19
+ CancelRunResponse,
20
+ CreateRunRequest,
21
+ CreateRunResponse,
22
+ ErrorEnvelope,
23
+ ForkRunRequest,
24
+ ForkRunResponse,
25
+ InterruptByTokenInspection,
26
+ PollEventsResponse,
27
+ ResolveInterruptByTokenResponse,
28
+ ResolveInterruptRequest,
29
+ ResolveInterruptResponse,
30
+ RunConfigurable,
31
+ RunEventDoc,
32
+ RunSnapshot,
33
+ RunStatus,
34
+ StreamMode,
35
+ } from './types.js';
36
+ export { streamEvents } from './sse.js';
37
+ export type { EventsStreamContext, EventsStreamOptions } from './sse.js';
38
+
39
+ // Run-status + run-error helpers (added in 1). Forward-compat predicates
40
+ // over the wire-format unions declared above; full design + rationale in
41
+ // `run-helpers.ts`.
42
+ export {
43
+ ACTIVE_RUN_STATUSES,
44
+ TERMINAL_RUN_STATUSES,
45
+ isTerminalRunStatus,
46
+ HTTP_ERROR_CODES,
47
+ isHttpErrorCode,
48
+ RUN_ERROR_CODES,
49
+ isRunErrorCode,
50
+ } from './run-helpers.js';
51
+ export type {
52
+ ActiveRunStatus,
53
+ TerminalRunStatus,
54
+ HttpErrorCode,
55
+ RunErrorCode,
56
+ RunError,
57
+ } from './run-helpers.js';