@pipelex/sdk 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Evotis S.A.S.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,75 @@
1
+ # @pipelex/sdk
2
+
3
+ TypeScript SDK for the **Pipelex hosted API** — execute MTHDS methods, manage runs, and call the product surface (methods catalog, organizations, billing, API keys, storage) from Node.
4
+
5
+ > Pipelex is the runtime/product. [MTHDS](https://mthds.ai) is the open standard it implements. This SDK speaks to the hosted Pipelex API; the pure protocol wire types it builds on come from the [`mthds`](https://www.npmjs.com/package/mthds) package via its `mthds/protocol` subpath.
6
+
7
+ ## Status
8
+
9
+ Early. `PipelexApiClient` implements the MTHDS protocol-execution routes (`execute` / `start` / `validate` / `models` / `version`), the build helpers (`/v1/build/*`), the durable run lifecycle (`start` → poll → result), and the Pipelex product routes (user profile, methods catalog, organizations, billing, API keys, gateway key, onboarding, storage, runs list/update).
10
+
11
+ ## Install
12
+
13
+ ```bash
14
+ npm install @pipelex/sdk
15
+ ```
16
+
17
+ ## Usage
18
+
19
+ ```ts
20
+ import { PipelexApiClient } from "@pipelex/sdk";
21
+
22
+ // Base URL + token from PIPELEX_API_URL / PIPELEX_API_KEY, or pass them explicitly.
23
+ const client = new PipelexApiClient({
24
+ baseUrl: "https://api.pipelex.com",
25
+ apiToken: process.env.PIPELEX_API_KEY,
26
+ });
27
+
28
+ // Validate an MTHDS bundle (a 200-diagnostic verdict, discriminated on `is_valid`).
29
+ const report = await client.validate(["domain = 'demo'"]);
30
+ if (report.is_valid) {
31
+ // Run it and wait for the result (durable start + poll on the hosted API).
32
+ const result = await client.startAndWaitForResult({ pipe_code: "demo.greet" });
33
+ console.log(result.main_stuff ?? result.pipe_output);
34
+ }
35
+ ```
36
+
37
+ ### Product routes
38
+
39
+ The hosted management surface (catalog, account, billing) hangs off the same client. Every product route maps a non-2xx `problem+json` to a typed `ApiResponseError` — branch on the structured `code`, not the HTTP status:
40
+
41
+ ```ts
42
+ import { PipelexApiClient, ApiResponseError } from "@pipelex/sdk";
43
+
44
+ const client = new PipelexApiClient({ apiToken: process.env.PIPELEX_API_KEY });
45
+
46
+ const me = await client.getMe(); // GET /v1/me
47
+ const methods = await client.listMethods(); // GET /v1/methods
48
+ const created = await client.createMethod({ name: "Greeter", mthds: "domain = 'demo'" });
49
+
50
+ try {
51
+ const { portal_url } = await client.getBillingPortal();
52
+ // open portal_url ...
53
+ } catch (err) {
54
+ if (err instanceof ApiResponseError && err.code === "conflict") {
55
+ // no subscription yet — start one via createCheckout(...)
56
+ }
57
+ }
58
+ ```
59
+
60
+ The full client surface is documented in [`docs/architecture.md`](./docs/architecture.md).
61
+
62
+ ## Develop
63
+
64
+ ```bash
65
+ make install # Install dependencies
66
+ make check # Lint + format check + typecheck + build + depcruise (alias: make c)
67
+ make test # Run the test suite (alias: make t)
68
+ make all # Clean, check, and test
69
+ ```
70
+
71
+ Always run `make check` before committing.
72
+
73
+ ## License
74
+
75
+ [MIT](./LICENSE)
@@ -0,0 +1,297 @@
1
+ import type { MTHDSProtocol, ModelCategory, ModelDeck, RunOptions, RunResultStart, StartOptions, VersionInfo } from "mthds/protocol";
2
+ import type { BuildInputsRequest, BuildOutputRequest, BuildRunnerRequest, BuildRunnerResponse, ConceptRequest, ConceptResponse, DictPipeOutput, DictRunResultExecute, PipeSpecRequest, PipeSpecResponse, PipelexValidationResult } from "./models.js";
3
+ import { type RunRead, type RunResults, type RunResultState, type WaitForResultOptions } from "./runs.js";
4
+ import type { BillingPortalResponse, ChangePlanResponse, CheckoutResponse, GatewayApiKey, GatewayApiKeyStatus, InvoiceView, Membership, MembershipsResponse, MethodData, MethodWriteInput, OnboardingSubmission, PipelexApiKeyCreated, PipelexApiKeyList, PipelineRun, PlanView, ResolvedStorageUrl, SubscriptionResponse, UpdateRunInput, UploadInput, UploadedFile, UserProfile } from "./product-models.js";
5
+ export interface MthdsFile {
6
+ /** File contents to validate. */
7
+ content: string;
8
+ /** Optional provenance URI threaded into validation diagnostics. */
9
+ uri?: string;
10
+ }
11
+ export interface ValidateFilesOptions {
12
+ /** Whether unresolved pipe signatures are accepted as pending instead of invalid. */
13
+ allowSignatures?: boolean;
14
+ /** Optional validate presentation hints, e.g. ["markdown"]. */
15
+ render?: string[];
16
+ }
17
+ export interface PipelexApiClientOptions {
18
+ /** API token (Bearer). Falls back to `PIPELEX_API_KEY`. Optional for anonymous bare runners. */
19
+ apiToken?: string;
20
+ /**
21
+ * API base URL — host only, NO version prefix (e.g. `https://api.pipelex.com`
22
+ * or `http://localhost:8081`). Every endpoint composes as
23
+ * `{baseUrl}/v1/{endpoint}`. Falls back to `PIPELEX_API_URL`, then the hosted
24
+ * default.
25
+ */
26
+ baseUrl?: string;
27
+ }
28
+ /** Hosted default — the client composes every endpoint as `{base}/v1/{endpoint}`. */
29
+ export declare const DEFAULT_API_BASE_URL = "https://api.pipelex.com";
30
+ /**
31
+ * Client for the Pipelex hosted API — and any MTHDS-compliant runner.
32
+ *
33
+ * One base URL (`PIPELEX_API_URL`); every endpoint is `<base>/v1/<endpoint>`:
34
+ * - **protocol** (`execute` / `start` / `validate` / `models` / `version`) — works
35
+ * against any MTHDS-compliant runner, hosted or bare.
36
+ * - **build extensions** (`/v1/build/*`) — the Pipelex API's authoring helpers.
37
+ * - **run lifecycle** (`getRunStatus` / `getRunResult` / `waitForResult`) — the
38
+ * durable polling extension that survives long runs and lets a caller resume by
39
+ * id. Served only by a deployment that includes the platform block (the Pipelex
40
+ * Hosted API); a bare `pipelex-api` runner 404s those routes, which the lifecycle
41
+ * methods translate into a clear `RunLifecycleUnavailableError`.
42
+ *
43
+ * Implements `MTHDSProtocol<DictPipeOutput>` so the protocol-execution methods
44
+ * stay shaped like the standard's wire surface (`mthds/protocol`). The Pipelex
45
+ * extensions (build, run lifecycle) ride on top.
46
+ */
47
+ export declare class PipelexApiClient implements MTHDSProtocol<DictPipeOutput> {
48
+ private readonly apiToken;
49
+ private readonly baseUrl;
50
+ /** Origin root derived from the base URL — `/health` lives here, not under `/v1`. */
51
+ private readonly originUrl;
52
+ /** Cached `/v1/version` handshake outcome — whether the durable lifecycle is served. */
53
+ private lifecycleAvailable;
54
+ constructor(options?: PipelexApiClientOptions);
55
+ /** Build an API URL: `<base>/v1/<endpoint>`. */
56
+ private url;
57
+ /**
58
+ * Issue one HTTP request and return the raw status/headers/body. Wraps
59
+ * DNS/connect/TLS/timeout failures as `ApiUnreachableError`; a caller-driven
60
+ * abort (Ctrl-C / agent walk-away) propagates as-is so the poll loop can stop
61
+ * cleanly. Non-2xx interpretation is left to the caller. `url` is a fully
62
+ * resolved absolute URL.
63
+ */
64
+ private requestRaw;
65
+ /**
66
+ * Issue a request and parse the JSON body, throwing a plain `Error` on a
67
+ * non-2xx response. Used by the build extensions and `health` — surfaces
68
+ * that don't need the protocol's structured error taxonomy.
69
+ */
70
+ private requestJson;
71
+ private postApi;
72
+ /**
73
+ * Issue a Pipelex-product request (`/v1/me`, `/v1/methods`, `/v1/billing/*`,
74
+ * …) and parse its JSON body, mapping a non-2xx response to the typed
75
+ * `ApiResponseError` so callers branch on the structured `code` discriminant,
76
+ * not the HTTP status. Empty-body tolerant — DELETE / onboarding / updateRun
77
+ * answer 2xx with no body, returned as `undefined`. Uses the management-call
78
+ * timeout, not the blocking-execute ceiling.
79
+ */
80
+ private requestProduct;
81
+ private throwApiResponseError;
82
+ /**
83
+ * Translate a "route absent" 404 (a bare pipelex-api with no platform block)
84
+ * into a clear `RunLifecycleUnavailableError`. The platform's own 404s (run
85
+ * not found / cross-org) carry a structured error envelope (a `code` field)
86
+ * and are left for normal handling.
87
+ */
88
+ private throwIfLifecycleUnavailable;
89
+ /**
90
+ * Map the protocol's optional 202 execute degrade to a typed
91
+ * error. Hosted does not emit 202 today, but the protocol permits it;
92
+ * raising a typed error (with the `pipeline_run_id` + `Location` + `Retry-After`
93
+ * hints) beats a generic parse failure on an unexpected body shape.
94
+ */
95
+ private throwIfExecuteDegraded;
96
+ health(): Promise<Record<string, unknown>>;
97
+ /**
98
+ * Execute a method synchronously and wait for its completion —
99
+ * `POST /v1/execute`.
100
+ *
101
+ * Behind the hosted gateway, synchronous requests terminate at ~30s; a run
102
+ * that exceeds that surfaces as `PipelineExecuteTimeoutError` pointing at the
103
+ * durable start+poll path. Throws `RunStillRunningError` on the protocol's
104
+ * optional 202 degrade.
105
+ */
106
+ execute(options: RunOptions): Promise<DictRunResultExecute>;
107
+ /**
108
+ * Start a method asynchronously — `POST /v1/start` (202, no output yet).
109
+ *
110
+ * Server-specific extension args ride `options.extra` and merge into the
111
+ * request body — the server you call defines and handles them (including a
112
+ * client-supplied run id where a server supports one). The returned
113
+ * `pipeline_run_id` is always authoritative; on a hosted deployment it is
114
+ * durable — poll `getRunStatus` / `getRunResult`.
115
+ */
116
+ start(options: StartOptions): Promise<RunResultStart>;
117
+ /**
118
+ * Parse, validate, and dry-run an MTHDS bundle — `POST /v1/validate`.
119
+ *
120
+ * `/validate` is a diagnostic endpoint: every produced verdict rides a **200**,
121
+ * discriminated on `is_valid`. This returns the `PipelexValidationResult` union
122
+ * verbatim — `is_valid: true` ⇒ the typed `PipelexValidationReport` (structural
123
+ * artifacts), `is_valid: false` ⇒ a `PipelexInvalidReport` (`validation_errors[]`).
124
+ * An invalid bundle is NOT thrown — the caller pattern-matches `is_valid`. Only a
125
+ * *no-verdict* condition (a malformed request, an `mthds_sources` length mismatch,
126
+ * auth, a server fault) is non-2xx and surfaces as `ApiResponseError`.
127
+ *
128
+ * `mthdsSources` (optional, parallel to `mthdsContents`) names each submitted
129
+ * content — a Pipelex-API extension threaded onto `blueprint.source`, so
130
+ * cross-file diagnostics name the owning file (an unnamed content yields
131
+ * `source: null`). The server 422s a length mismatch; this client sends the
132
+ * arrays verbatim and surfaces that as an `ApiResponseError`.
133
+ *
134
+ * `render` is the Pipelex-API presentation hint — a list of view-format tokens.
135
+ * This client always asks for Markdown so both valid results and produced
136
+ * validation-error verdicts carry `rendered_markdown`; callers may add more
137
+ * tokens. Unknown tokens are server-side lenient-ignored (never a 422).
138
+ */
139
+ validate(mthdsContents: string[], allowSignatures?: boolean, mthdsSources?: string[], render?: string[]): Promise<PipelexValidationResult>;
140
+ /**
141
+ * Validate paired MTHDS files while preserving URI attribution for diagnostics.
142
+ *
143
+ * This adapter intentionally keeps the low-level `validate(...)` payload shape
144
+ * intact for existing consumers. When any file has a URI, every content gets a
145
+ * parallel source label; inline labels are deterministic so the server never
146
+ * sees a length-mismatched `mthds_sources` array.
147
+ */
148
+ validateFiles(files: MthdsFile[], options?: ValidateFilesOptions): Promise<PipelexValidationResult>;
149
+ /** The model deck the runner can route to — `GET /v1/models[?type=]`. */
150
+ models(category?: ModelCategory): Promise<ModelDeck>;
151
+ /**
152
+ * Protocol + implementation versions — `GET /v1/version` (always public).
153
+ * The handshake for feature detection (hosted extensions or not).
154
+ */
155
+ version(): Promise<VersionInfo>;
156
+ buildInputs(request: BuildInputsRequest): Promise<unknown>;
157
+ buildOutput(request: BuildOutputRequest): Promise<unknown>;
158
+ buildRunner(request: BuildRunnerRequest): Promise<BuildRunnerResponse>;
159
+ concept(request: ConceptRequest): Promise<ConceptResponse>;
160
+ pipeSpec(request: PipeSpecRequest): Promise<PipeSpecResponse>;
161
+ /**
162
+ * Fetch a run's status by bare id — `GET /v1/runs/{pipeline_run_id}/status`.
163
+ *
164
+ * Self-healing: a finished-but-unrecorded run resolves to its true terminal
165
+ * status on read. `degraded: true` means Temporal was unreachable and
166
+ * `status` is the last-known value; `retry_after_seconds` carries the
167
+ * server's backoff hint when present. Throws `RunLifecycleUnavailableError`
168
+ * when the lifecycle routes are absent (a bare runner).
169
+ */
170
+ getRunStatus(runId: string, options?: {
171
+ signal?: AbortSignal;
172
+ }): Promise<RunRead>;
173
+ /**
174
+ * Single-shot result lookup — `GET /v1/runs/{pipeline_run_id}/results`.
175
+ * Maps the server's poll semantics to a discriminated union:
176
+ * - HTTP 202 → `running` (with the `Retry-After` hint)
177
+ * - HTTP 200 → `completed` (with the result artifacts)
178
+ * - HTTP 409 → `failed` (terminal non-`COMPLETED`)
179
+ * - HTTP 503 → `running` (Temporal degraded — retry, never fail a poller)
180
+ *
181
+ * Throws `RunLifecycleUnavailableError` when the lifecycle routes are absent
182
+ * (a bare runner).
183
+ */
184
+ getRunResult(runId: string, options?: {
185
+ signal?: AbortSignal;
186
+ }): Promise<RunResultState>;
187
+ /** Poll an already-started run (by id) until it reaches a terminal state. */
188
+ waitForResult(runId: string, options?: WaitForResultOptions): Promise<RunResults>;
189
+ /**
190
+ * Whether the configured server serves the durable run lifecycle, decided
191
+ * via the `GET /v1/version` handshake and cached for the client's lifetime. A
192
+ * bare `pipelex-api` runner has no run store; anything else is assumed hosted.
193
+ * When the handshake itself fails, assume hosted (the SDK default) and let the
194
+ * start call surface the real error.
195
+ */
196
+ private supportsRunLifecycle;
197
+ /**
198
+ * Start a run and wait for its result.
199
+ *
200
+ * - **Hosted** (per the `/v1/version` handshake): durable start + poll, the
201
+ * path that survives the gateway's ~30s synchronous ceiling.
202
+ * - **Bare runner** (no run store): the blocking `POST /v1/execute`, which
203
+ * has no gateway cap off-platform and returns the native `pipe_output`.
204
+ */
205
+ startAndWaitForResult(options: StartOptions, pollOptions?: WaitForResultOptions): Promise<RunResults>;
206
+ /** The authenticated user's profile — `GET /v1/me`. */
207
+ getMe(): Promise<UserProfile>;
208
+ /** List the caller's saved methods — `GET /v1/methods`. */
209
+ listMethods(): Promise<MethodData[]>;
210
+ /** Fetch one method by id — `GET /v1/methods/{id}`. */
211
+ getMethod(methodId: string): Promise<MethodData>;
212
+ /** Create a method — `POST /v1/methods`. */
213
+ createMethod(input: MethodWriteInput): Promise<MethodData>;
214
+ /** Replace a method (rename = changed `name`) — `PUT /v1/methods/{id}`. */
215
+ updateMethod(methodId: string, input: MethodWriteInput): Promise<MethodData>;
216
+ /** Delete a method — `DELETE /v1/methods/{id}`. */
217
+ deleteMethod(methodId: string): Promise<void>;
218
+ /** The caller's org memberships + active-org feature flags — `GET /v1/organizations/memberships`. */
219
+ listMemberships(): Promise<MembershipsResponse>;
220
+ /** Create an organization — `POST /v1/organizations`. */
221
+ createOrganization(input: {
222
+ name: string;
223
+ }): Promise<Membership>;
224
+ /** Rename an organization — `PATCH /v1/organizations/{org_id}`. */
225
+ renameOrganization(orgId: string, input: {
226
+ name: string;
227
+ }): Promise<Membership>;
228
+ /** The active org's subscription state — `GET /v1/billing/subscription`. */
229
+ getSubscription(): Promise<SubscriptionResponse>;
230
+ /** Available plans (with `is_current`) — `GET /v1/billing/plans`. */
231
+ listPlans(): Promise<PlanView[]>;
232
+ /** Past invoices — `GET /v1/billing/invoices`. */
233
+ listInvoices(): Promise<InvoiceView[]>;
234
+ /** Open a Stripe checkout for a plan — `POST /v1/billing/checkout`. */
235
+ createCheckout(input: {
236
+ plan: string;
237
+ }): Promise<CheckoutResponse>;
238
+ /**
239
+ * Switch the existing subscription's plan — `POST /v1/billing/change-plan`.
240
+ * A 409 `conflict` (`ApiResponseError.code`) means there is no subscription
241
+ * to change — start one via `createCheckout` first.
242
+ */
243
+ changePlan(input: {
244
+ plan: string;
245
+ }): Promise<ChangePlanResponse>;
246
+ /**
247
+ * A Stripe billing-portal session URL — `GET /v1/billing/portal`. A 409
248
+ * `conflict` (`ApiResponseError.code`) means there is no subscription yet.
249
+ */
250
+ getBillingPortal(): Promise<BillingPortalResponse>;
251
+ /** List the caller's Pipelex API keys — `GET /v1/pipelex-api-keys`. */
252
+ listPipelexApiKeys(): Promise<PipelexApiKeyList>;
253
+ /**
254
+ * Mint a Pipelex API key — `POST /v1/pipelex-api-keys`. The plaintext
255
+ * `api_key` is returned ONCE. A 409 `pipelex_api_key_limit_reached`
256
+ * (`ApiResponseError.code`) means the per-account key limit is hit.
257
+ */
258
+ createPipelexApiKey(input: {
259
+ label: string;
260
+ }): Promise<PipelexApiKeyCreated>;
261
+ /** Revoke a Pipelex API key — `DELETE /v1/pipelex-api-keys/{id}`. */
262
+ revokePipelexApiKey(id: string): Promise<void>;
263
+ /**
264
+ * Rotate a Pipelex API key — `POST /v1/pipelex-api-keys/{id}/rotate` (no
265
+ * body). Returns the new plaintext `api_key` once; the old key stops working.
266
+ */
267
+ rotatePipelexApiKey(id: string): Promise<PipelexApiKeyCreated>;
268
+ /**
269
+ * Provision the gateway (LLM inference) API key — `POST /v1/gateway-api-key`.
270
+ * The JSON body is ALWAYS sent (even with `promo_code: null`) — the server
271
+ * 422s an empty body.
272
+ */
273
+ createGatewayApiKey(input: {
274
+ promo_code: string | null;
275
+ }): Promise<GatewayApiKey>;
276
+ /** The gateway key status (`null` until provisioned) — `GET /v1/gateway-api-key`. */
277
+ getGatewayApiKey(): Promise<GatewayApiKeyStatus>;
278
+ /** Submit the onboarding questionnaire — `POST /v1/onboarding/submit`. */
279
+ submitOnboarding(input: OnboardingSubmission): Promise<void>;
280
+ /** Resolve a storage URI to a presigned URL — `POST /v1/resolve-storage-url`. */
281
+ resolveStorageUrl(input: {
282
+ uri: string;
283
+ }): Promise<ResolvedStorageUrl>;
284
+ /** Upload a base64 file — `POST /v1/upload`. */
285
+ upload(input: UploadInput): Promise<UploadedFile>;
286
+ /** List a method's runs — `GET /v1/runs?method_id={methodId}`. */
287
+ listRuns(methodId: string): Promise<PipelineRun[]>;
288
+ /** Patch a run's status (admin/manual) — `PUT /v1/runs/{id}`. */
289
+ updateRun(runId: string, input: UpdateRunInput): Promise<void>;
290
+ /**
291
+ * Blocking `POST /v1/execute` adapted onto `RunResults` — the bare-runner
292
+ * path. Forwards every protocol field PLUS the `extra` extension passthrough:
293
+ * an extension-only call (`{ extra }` with no pipe_code/bundle) or a vendor
294
+ * selector riding `extra` must survive this path, not just the durable one.
295
+ */
296
+ private executeBlocking;
297
+ }