@pipelex/sdk 0.40.0 → 0.42.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 +20 -1
- package/dist/cli/script.d.ts +1 -1
- package/dist/client.d.ts +68 -3
- package/dist/client.js +163 -82
- package/dist/client.js.map +1 -1
- package/dist/error-verdicts.js +14 -3
- package/dist/error-verdicts.js.map +1 -1
- package/dist/errors.d.ts +30 -8
- package/dist/errors.js +13 -8
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js.map +1 -1
- package/dist/product-models.d.ts +46 -0
- package/dist/runs.d.ts +11 -0
- package/dist/runs.js +35 -0
- package/dist/runs.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/docs/architecture.md +3 -2
- package/docs/errors.md +39 -4
- package/package.json +1 -1
package/dist/runs.d.ts
CHANGED
|
@@ -26,6 +26,17 @@ import type { DictPipeOutput, DictWorkingMemory, PipelexRunResultStart } from ".
|
|
|
26
26
|
* server-side but kept here for historical rows.
|
|
27
27
|
*/
|
|
28
28
|
export type RunStatus = "PENDING" | "STARTED" | "RUNNING" | "COMPLETED" | "FAILED" | "CANCELLED" | "TERMINATED" | "TIMED_OUT";
|
|
29
|
+
/** Every `RunStatus`, in the platform's declaration order. */
|
|
30
|
+
export declare const RUN_STATUSES: readonly RunStatus[];
|
|
31
|
+
/**
|
|
32
|
+
* The statuses a run-history filter sends, deduplicated into `RUN_STATUSES` order so the same
|
|
33
|
+
* filter always builds the same query, as the Python twin builds it. `undefined` is no filter,
|
|
34
|
+
* which lists every status. An empty list names no status, and a name that is no `RunStatus`
|
|
35
|
+
* names one the platform refuses with a `422`; each is a `RequestArgumentError`, `input` and not
|
|
36
|
+
* retryable, before any request, rather than a query that lists every status or fails late. A
|
|
37
|
+
* value that is not an array at all is a `TypeError`, a bug in the calling code.
|
|
38
|
+
*/
|
|
39
|
+
export declare function runStatusFilter(status: readonly RunStatus[] | undefined): readonly RunStatus[] | undefined;
|
|
29
40
|
/** A terminal status means the run is done and will not transition again. */
|
|
30
41
|
export declare function isTerminalRunStatus(status: RunStatus): boolean;
|
|
31
42
|
/** Only `COMPLETED` has a result; every other terminal status is a failure. */
|
package/dist/runs.js
CHANGED
|
@@ -1,6 +1,41 @@
|
|
|
1
1
|
import { RequestArgumentError, RunFailedError, RunTimeoutError } from "./errors.js";
|
|
2
2
|
import { MAX_TIMER_DELAY_MS } from "./timers.js";
|
|
3
3
|
import { dropUnfit, isPlainObject } from "./error-models.js";
|
|
4
|
+
/** Every `RunStatus`, in the platform's declaration order. */
|
|
5
|
+
export const RUN_STATUSES = [
|
|
6
|
+
"PENDING",
|
|
7
|
+
"STARTED",
|
|
8
|
+
"RUNNING",
|
|
9
|
+
"COMPLETED",
|
|
10
|
+
"FAILED",
|
|
11
|
+
"CANCELLED",
|
|
12
|
+
"TERMINATED",
|
|
13
|
+
"TIMED_OUT",
|
|
14
|
+
];
|
|
15
|
+
/**
|
|
16
|
+
* The statuses a run-history filter sends, deduplicated into `RUN_STATUSES` order so the same
|
|
17
|
+
* filter always builds the same query, as the Python twin builds it. `undefined` is no filter,
|
|
18
|
+
* which lists every status. An empty list names no status, and a name that is no `RunStatus`
|
|
19
|
+
* names one the platform refuses with a `422`; each is a `RequestArgumentError`, `input` and not
|
|
20
|
+
* retryable, before any request, rather than a query that lists every status or fails late. A
|
|
21
|
+
* value that is not an array at all is a `TypeError`, a bug in the calling code.
|
|
22
|
+
*/
|
|
23
|
+
export function runStatusFilter(status) {
|
|
24
|
+
if (status === undefined)
|
|
25
|
+
return undefined;
|
|
26
|
+
if (!Array.isArray(status)) {
|
|
27
|
+
throw new TypeError(`"status" must be an array naming one or more of ${RUN_STATUSES.join(", ")}.`);
|
|
28
|
+
}
|
|
29
|
+
if (status.length === 0) {
|
|
30
|
+
throw new RequestArgumentError(`"status" must name one or more of ${RUN_STATUSES.join(", ")}; omit it to list every status.`);
|
|
31
|
+
}
|
|
32
|
+
const known = RUN_STATUSES;
|
|
33
|
+
const unknown = status.filter((name) => !known.includes(name));
|
|
34
|
+
if (unknown.length > 0) {
|
|
35
|
+
throw new RequestArgumentError(`Unknown run status(es) ${unknown.join(", ")}; valid statuses are: ${RUN_STATUSES.join(", ")}.`);
|
|
36
|
+
}
|
|
37
|
+
return RUN_STATUSES.filter((name) => status.includes(name));
|
|
38
|
+
}
|
|
4
39
|
const TERMINAL_RUN_STATUSES = new Set([
|
|
5
40
|
"COMPLETED",
|
|
6
41
|
"FAILED",
|
package/dist/runs.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runs.js","sourceRoot":"","sources":["../src/runs.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"runs.js","sourceRoot":"","sources":["../src/runs.ts"],"names":[],"mappings":"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"}
|
package/dist/version.d.ts
CHANGED
package/dist/version.js
CHANGED
package/docs/architecture.md
CHANGED
|
@@ -115,6 +115,7 @@ Flat `src/` (mirrors `mthds-js`'s `runners/api` flatness — the SDK has one cli
|
|
|
115
115
|
- **Two independent opt-in lists on `validate`.** `render` asks the server to render a *view format* and adds a `rendered_<format>` field; `views` asks for a *structured view* and adds a same-named top-level field — today `input_form` and `output_form`, the per-pipe form descriptors. A caller that wants the forms without the dry-run verdict reads `pipeIo` instead, which derives them with no dry run. Each resolves its own tokens against its own supported set and unknown tokens are lenient-ignored on both, never a `422`. The client always asks for `render: ["markdown"]` but sends `views` only when a caller names a token, because an opt-in view exists precisely so the default response stays byte-identical for the consumers that discard it. `pipelex-api` gates `input_form` on the token as of 0.18.0; a 0.17.0 runner resolved no token and emitted the field on every valid verdict, which is why `PipelexValidationReport.input_form` is typed optional rather than required — the one typing that reads a response from either runner correctly.
|
|
116
116
|
- **Method-bundle transport (pipelex-api extension):** `execute` / `start` / the blocking fallback accept the whole method — the `.mthds` plus its `funcs/*.py`, `structures/*.py`, `requirements.txt` — instead of only the inline `mthds_contents` text, so custom PipeFunc Python travels with the method. Two equivalent, mutually exclusive encodings: `files` (a `{ relativePath: text }` map) and `bundle_b64` (the same bundle as a base64 zip). A bundle is self-contained, so it satisfies the "something to run" precondition on its own — neither `pipe_code` nor `mthds_contents` is required beside it. See [run-sources](#run-sources) above.
|
|
117
117
|
- **Method selectors on the run methods:** `execute` / `start` / `startAndWaitForResult` take the Pipelex API's `method_ref` (an address the runner resolves; provenance comes back typed as `method_provenance`) and the platform's `method_id` as **typed named options** (`PipelexRunOptions` / `PipelexStartOptions`), never `extra` entries. See [Pipelex API run extensions](#pipelex-api-run-extensions-method_ref) and [hosted run extensions](#hosted-run-extensions-method_id) above.
|
|
118
|
+
- **An idempotency key on a run start (hosted):** `start` and `startAndWaitForResult` take `idempotencyKey` (`PipelexStartRequestOptions`, intersected into `PipelexStartOptions`), sent as the `Idempotency-Key` header of `POST /v1/start` and never in the body; with no key there is no header. The hosted API binds the key to the run, answers a repeat of the same start with that run, and refuses the key with other options; a bare runner ignores the header. The client refuses a key that is not 1 to 255 visible ASCII characters with a `RequestArgumentError` before any request, the blocking `execute` fallback of `startAndWaitForResult` sends none, and the key leaves the error verdict as it is: a start that may have reached the server stays `runMayHaveStarted`, and the caller who sent a key recovers the run by sending the same start again under it. [errors.md](./errors.md#a-starts-idempotency-key--idempotencykey) holds the contract.
|
|
118
119
|
- **Method selectors on `validate`:** the first argument is inline contents (`string[]`) or a `ValidateMethodSelector` — `{ method_ref }` (runner-resolved by address) or `{ method_id }` (hosted, platform-resolved) — under the strict tooling XOR: exactly one selector, no linkage exception, `mthds_sources` legal only beside inline contents. A selector-resolution failure is a non-2xx `ApiResponseError`, never an `is_valid: false` verdict.
|
|
119
120
|
- **The files a bundle's steps read (`resources`, pipelex-api extension):** `validate` / `validateFiles` (in their last options argument), `execute` / `start` / `startAndWaitForResult` (both paths), the crate routes and `prepareInputs` (beside inline `files`) take `resources`, the files a `PipeDocGen` step's `template_file` names, keyed by the path the step names, each as its bytes (`Uint8Array`, `ArrayBuffer` or `Blob`), and send them as the wire's base64 map. `bundle-resources.ts` encodes them and refuses, before any request, a key the runner's path rule refuses and two keys for one file; the run routes and `validate` also mirror the runner's pairing refusals (beside a `method_ref`, or with no source on a run), while the crate routes leave theirs to the server, as they do the selector XOR. `resources` is reserved on `extra`, so it cannot bypass those checks. The valid verdict's `warnings` carry the template check's `template_check_warning` items, whose `template_file` and `template_location` `ValidationErrorItem` declares ahead of `mthds`, a difference the type-level pin states. See [bundle-resources.md](./bundle-resources.md).
|
|
120
121
|
- **Crate routes (`/v1/resolve`, `/v1/codegen`, `/v1/pipe-io`):** `resolve` emits the **normalized library crate** (the MTHDS standard's Library Crate Format, fingerprint riding inside the payload); `codegen` projects that crate through two explicit axes — `kind` (`types`) × `target` (`ts-zod` / `python-pydantic` / `python-structures`) — into stamped artifacts plus their `codegen.lock`. All three share one closure envelope (`files: [{content, source?}]` XOR a `method_ref`, the address form server-resolved and the registry form a `501`) and return a **discriminated 200 verdict** (`is_valid`, with `CrateInvalidReport` as the shared invalid arm) rather than a bare payload, the same discipline as `validate`. They also take the hosted `method_id` (`PipelexHostedToolingExtensions`) as a third closure form under the strict three-way XOR — a pass-through the platform resolves, meaningless against a bare runner. Writing the artifacts and lock verbatim reproduces a local `pipelex codegen types` run byte-for-byte, so the offline `pipelex codegen check` passes — the SDK stays transport-only and does not write files. Verifying such a tree afterwards is `runCodegenCheck`, a standalone pure helper rather than a client method, because it needs no server at all. `pipeIo` derives a method's three I/O artifacts — the standard's `PipeIOContracts`, `InputForm` and `OutputForm`, imported from `mthds/protocol` — for the pipe the route selects (the request's qualified `pipe_ref`, else the method's own entry pipe) or for every pipe under `all_pipes`, with the runnability facts beside them and the closure's files echoed on `include_files`, and it runs no dry run; a selection it refuses is a `422` typed `EntryPipeNotFoundError` or `EntryPipeAmbiguousError`. See [crate-routes.md](./crate-routes.md).
|
|
@@ -128,13 +129,13 @@ Flat `src/` (mirrors `mthds-js`'s `runners/api` flatness — the SDK has one cli
|
|
|
128
129
|
The hosted catalog/account routes the webapp drives. All ride the same `{base}/v1/*` + `Authorization: Bearer` + org-from-JWT contract as the protocol routes, go through the shared `requestProduct<T>` helper (30s management timeout, empty-body tolerant), and map a non-2xx RFC 9457 `problem+json` to a typed `ApiResponseError`. Every route parses a 2xx body through one private helper, so an answer that is not JSON (a gateway's HTML page, an empty body where one was due) is an `ApiResponseError` carrying its status and raw text, `runtime` and not retryable, rather than a bare `SyntaxError`. A product route whose answer the SDK or its caller reads before anything else (`listMethods`, `listRuns`, `getRunDetail`, `upload`, `requestUploadGrant`, `resolveStorageUrls`, and the methods routes answering one stored method) goes through `requestProductObject` or a helper built on the same read instead, which also refuses an empty body and JSON that is not an object; the two paged routes then refuse, the same way, through one page reader, a page whose `items` is not an array or whose `next_cursor` is neither a string nor `null`, `upload` an answer with no non-empty string `uri`, and the methods routes a stored `mthds` field that is neither a string nor absent and a stored `python` field that is not the serialized file list. Consumers branch on **`type` and `errorDomain`**, as the hosted-envelope spec (`conformance/specs/pipelex-hosted-envelope.md`, in the `conformance` repository) says, never on the HTTP status. The platform's `type` is `https://pipelex.com/errors/<code>`, one-to-one with its native `code` (`conflict`, `not_found`, `pipelex_api_key_limit_reached`, …), the finer surface-specific code, on which a client of the platform alone may branch as well: the two read the same answer, and a refusal the platform relays from the runner carries the runner's `type` and `errorType` and no `code`. The SDK reads the `error_domain` and `retryable` the server sent first, and where the platform sends none on a refusal it renders itself, as its Lambda-backed routes do not yet, `errorDomain` and `retryable` are the SDK's fallback, read from the status; every error carries both, decided. See [errors.md](./errors.md).
|
|
129
130
|
|
|
130
131
|
- **User profile:** `getMe` (`GET /v1/me`).
|
|
131
|
-
- **Methods catalog:** `listMethods`, `getMethod`, `createMethod`, `writeDraft`, `renameMethod`, `publishMethod`, `listMethodVersions`, `getMethodVersion`, `deleteMethod`. **A saved method has a draft and immutable published versions.** Its content fields (`mthds`, `python`, `input_data`) are its draft, which `writeDraft` (`PUT /v1/methods/{id}/draft`) replaces without validating it; `updated_at` is the draft's token, which moves on every draft write and nothing else, and `draft_digest` the SHA-256 of its two file sets. A draft write may carry `expected_updated_at`, the token the caller last saw, and is then a compare-and-swap: a draft that moved since is a `409` `method_update_conflict` and nothing is written. `renameMethod` (`PATCH /v1/methods/{id}`) changes the name alone and moves no token. `publishMethod` (`POST /v1/methods/{id}/publish`) takes the required `expected_draft_updated_at` and answers a `MethodPublishResult` discriminated on `outcome` — `published` with the new version, `unchanged` with the existing latest version when the draft's digest equals it, `refused` with a `reason` (`invalid` or `not_runnable`), a `message` and the runner's `validation` verdict — every arm carrying the `method`; the SDK reads the nested method as `getMethod` does, and an answer whose `outcome` is none of these is an answer it cannot read. A publish that produced no verdict is thrown rather than answered: only a draft that differs from the latest version reaches the runner, and a runner that cannot be reached, answers unusably or does not finish within the platform's deadline is a `502` or `503` with nothing written, so a retry is safe, and a retry of a publish that landed while its answer was lost answers `unchanged` with the version it wrote. A method is stored as one item that keeps room for the summary a publish adds, so a draft that saves can always be published: a creation or a draft write is a `413` `payload_too_large` when it would grow the method past that room, below the store's item limit, and a rename is the same `413` for a name so long it would leave the method too large to publish. A version is numbered from 1, never reused, and holds the draft's files as they stood, its `source_digest`, its `crate_fingerprint` (`null` when the runner could not resolve the bundle in memory), the runner's version, and when and by whom it was published; `listMethodVersions` pages the summaries newest first (`MethodVersionPage`, `{items, nextCursor}`, a `limit` from 1 to 100, 20 by default, and a `422` outside that range), and `getMethodVersion` reads one with its sources, its `python` parsed as a method's is. A method read carries `latest_version` and `latest_published`, both `null` until the first publish, so the publish state is derived: never published, draft unchanged (`draft_digest === latest_published.source_digest`), or draft ahead. The method routes take a bare id and address the method itself, never one of its versions; the platform does not parse a suffix there, so `mt_x@3` would answer a `404` that reads as a method that does not exist, and each route refuses a suffixed id with `RequestArgumentError` before anything is sent, naming `parseMethodSelector` and `getMethodVersion`. The whole-method `updateMethod` is gone, replaced by the draft write and the rename. **`deleteMethod` is asynchronous and its return type says so:** `DELETE /v1/methods/{id}` answers `202` with a `MethodDeletionAccepted` (`method_id`, `deletion_state`, `deletion_job_id`) the moment the platform has claimed the method and terminated its in-flight workflows — the rest of the cascade (runs, events, S3 objects) is enqueued. A resolved promise means "accepted", never "gone"; completion is the row disappearing from `listMethods`, not any field of that body. The claim is a conditional write, so a double-clicked delete is a `409 method_being_deleted` rather than a second cascade over the same runs, and an unknown or foreign-org id is a `404`. The erasure deletes the method's versions with the rest. (This doc previously stated the SDK had no `delete` because the platform had no such route — the route has always existed; the claim was simply false.) `listMethods` returns **one page**, not the whole catalog — a `MethodPage` (`{items, nextCursor}`) served from a narrow index projection, so its cost is the page size rather than the size of the catalog. It used to return every method with its full `.mthds` bundle attached, which hit DynamoDB's 1 MB page cap at a couple hundred methods and came back truncated with no error and no flag, so methods silently vanished from the UI. Rows are therefore `MethodSummary`, **not** `MethodData`: no `mthds`, no `python`, no `updated_at`, because none of them is in the projection and putting `mthds` back is what restored the truncation. Reach for `getMethod(id)` when you need a method's source. `query.q` is applied server-side across the whole catalog, not over the page — filtering one page client-side would be searching 50 rows of 10,000 and calling it a search. `iterateMethods` is the async iterator that follows the cursor for callers who genuinely want everything (`for await (const m of client.iterateMethods())`);
|
|
132
|
+
- **Methods catalog:** `listMethods`, `getMethod`, `createMethod`, `writeDraft`, `renameMethod`, `publishMethod`, `listMethodVersions`, `getMethodVersion`, `deleteMethod`. **A saved method has a draft and immutable published versions.** Its content fields (`mthds`, `python`, `input_data`) are its draft, which `writeDraft` (`PUT /v1/methods/{id}/draft`) replaces without validating it; `updated_at` is the draft's token, which moves on every draft write and nothing else, and `draft_digest` the SHA-256 of its two file sets. A draft write may carry `expected_updated_at`, the token the caller last saw, and is then a compare-and-swap: a draft that moved since is a `409` `method_update_conflict` and nothing is written. `renameMethod` (`PATCH /v1/methods/{id}`) changes the name alone and moves no token. `publishMethod` (`POST /v1/methods/{id}/publish`) takes the required `expected_draft_updated_at` and answers a `MethodPublishResult` discriminated on `outcome` — `published` with the new version, `unchanged` with the existing latest version when the draft's digest equals it, `refused` with a `reason` (`invalid` or `not_runnable`), a `message` and the runner's `validation` verdict — every arm carrying the `method`; the SDK reads the nested method as `getMethod` does, and an answer whose `outcome` is none of these is an answer it cannot read. A publish that produced no verdict is thrown rather than answered: only a draft that differs from the latest version reaches the runner, and a runner that cannot be reached, answers unusably or does not finish within the platform's deadline is a `502` or `503` with nothing written, so a retry is safe, and a retry of a publish that landed while its answer was lost answers `unchanged` with the version it wrote. A method is stored as one item that keeps room for the summary a publish adds, so a draft that saves can always be published: a creation or a draft write is a `413` `payload_too_large` when it would grow the method past that room, below the store's item limit, and a rename is the same `413` for a name so long it would leave the method too large to publish. A version is numbered from 1, never reused, and holds the draft's files as they stood, its `source_digest`, its `crate_fingerprint` (`null` when the runner could not resolve the bundle in memory), the runner's version, and when and by whom it was published; `listMethodVersions` pages the summaries newest first (`MethodVersionPage`, `{items, nextCursor}`, a `limit` from 1 to 100, 20 by default, and a `422` outside that range), and `getMethodVersion` reads one with its sources, its `python` parsed as a method's is. A method read carries `latest_version` and `latest_published`, both `null` until the first publish, so the publish state is derived: never published, draft unchanged (`draft_digest === latest_published.source_digest`), or draft ahead. The method routes take a bare id and address the method itself, never one of its versions; the platform does not parse a suffix there, so `mt_x@3` would answer a `404` that reads as a method that does not exist, and each route refuses a suffixed id with `RequestArgumentError` before anything is sent, naming `parseMethodSelector` and `getMethodVersion`. The whole-method `updateMethod` is gone, replaced by the draft write and the rename. **`deleteMethod` is asynchronous and its return type says so:** `DELETE /v1/methods/{id}` answers `202` with a `MethodDeletionAccepted` (`method_id`, `deletion_state`, `deletion_job_id`) the moment the platform has claimed the method and terminated its in-flight workflows — the rest of the cascade (runs, events, S3 objects) is enqueued. A resolved promise means "accepted", never "gone"; completion is the row disappearing from `listMethods`, not any field of that body. The claim is a conditional write, so a double-clicked delete is a `409 method_being_deleted` rather than a second cascade over the same runs, and an unknown or foreign-org id is a `404`. The erasure deletes the method's versions with the rest. (This doc previously stated the SDK had no `delete` because the platform had no such route — the route has always existed; the claim was simply false.) `listMethods` returns **one page**, not the whole catalog — a `MethodPage` (`{items, nextCursor}`) served from a narrow index projection, so its cost is the page size rather than the size of the catalog. It used to return every method with its full `.mthds` bundle attached, which hit DynamoDB's 1 MB page cap at a couple hundred methods and came back truncated with no error and no flag, so methods silently vanished from the UI. Rows are therefore `MethodSummary`, **not** `MethodData`: no `mthds`, no `python`, no `updated_at`, because none of them is in the projection and putting `mthds` back is what restored the truncation. Reach for `getMethod(id)` when you need a method's source. `query.q` is applied server-side across the whole catalog, not over the page — filtering one page client-side would be searching 50 rows of 10,000 and calling it a search. `query.published` filters the same way, sent as `published=true` (the methods with a published version) or `published=false` (those never published), and combines with `q`. Each row carries the method's publish state, so a list tells its methods apart without reading each one: `latest_version` and `latest_published` (a `MethodVersionSummary`), both `null` when the method was never published, and `draft_digest`, which is `null` on a row that never stored one until its next draft write, unlike on `MethodData`, because the list reads an index that holds no bundle to digest; all three are optional, absent from a platform that does not serve them. Never published is `latest_version === null`, the draft unchanged is `draft_digest === latest_published.source_digest`, and the draft ahead is two digests that differ. Both filters run after the index read, so **a filtered page can come back empty and still carry a `nextCursor`**: an empty page is not the end of the list, and only a `null` cursor is. `iterateMethods` is the async iterator that follows the cursor for callers who genuinely want everything (`for await (const m of client.iterateMethods())`); it keeps going **past empty pages**, since `{items: [], nextCursor: "…"}` means "nothing matched in the slice I just read, keep going". A server that keeps minting fresh cursors forever stops it at a page ceiling far past any real catalog with a `PagingNotTerminatingError` carrying `pageLimit`, never a silently truncated stream; `iterateRuns` shares the same loop. It gives up only when the server stops advancing its cursor, and **throws** rather than returning a partial answer; like `iterateRuns` it is deliberately not a `listAllMethods(): Promise<MethodSummary[]>`, because an all-at-once helper needs a page cap and a cap means silently returning a truncated list. `MethodSummary.deletion_state` rides the list rows: a method mid-erasure stays listed so the UI can render it as "Deleting…", while `getMethod` refuses it with a 409. Full read responses (`MethodData`, from `getMethod`) carry `org_id`, `created_by_user_id`, a server-derived `description` (recomputed from the draft's top-level `description` on every draft write), the draft token and digest and the latest version's summary on top of the write contract's `name` / `mthds` / `input_data`. `getMethodClosure` is a client-side semantic layer over `getMethod` (not a route) that parses the draft's polymorphic `mthds` source into a runnable, provenance-labelled closure — the draft, not what a bare id runs — see [input-preparation.md](./input-preparation.md#getmethodclosure--the-explicit-expansion-utility).
|
|
132
133
|
- **Organizations:** `listMemberships`, `createOrganization`, `renameOrganization` (`PATCH`). The org "switch" is a WorkOS-session op (`/api/active-org`), not a `/v1` route — out of SDK scope.
|
|
133
134
|
- **Billing:** `getSubscription`, `listPlans`, `listInvoices`, `createCheckout`, `changePlan` (409 `conflict` ⇒ no subscription), `getBillingPortal` (409 `conflict` ⇒ no subscription).
|
|
134
135
|
- **Pipelex API keys (`plx_sk_…`):** `listPipelexApiKeys`, `createPipelexApiKey` (409 `pipelex_api_key_limit_reached`), `revokePipelexApiKey`, `rotatePipelexApiKey`. The plaintext `api_key` is returned once, on create/rotate.
|
|
135
136
|
- **Onboarding:** `submitOnboarding`.
|
|
136
137
|
- **Storage:** `resolveStorageUrl`, `resolveStorageUrls` (`POST /v1/resolve-storage-url/bulk` — the single route applied to a list of at most 100 references, one verdict per reference in request order, a refused reference a value on its item; a deployment without the route answers a `404` `ApiResponseError`), `upload` (JSON base64 — the multipart hop is browser→BFF only), `requestUploadGrant` (`POST /v1/upload/grant` — a presigned, create-only `PUT` for one new object, which the holder of the bytes sends with the standalone `uploadWithGrant`, also exported from the browser-safe `@pipelex/sdk/upload` entry; a declared size over the cap is a `413` `ApiResponseError`). The higher-level `uploadFile` / `prepareInputs` preparation surface is built on top of `upload`; `prepareInputs` additionally composes one `pipeIo` call, which selects the pipe and answers with the input-form descriptor it walks, and takes the method as inline `files`, a `method_ref` or a `method_id` like every other method-taking operation — see [input-preparation.md](./input-preparation.md). The download twin — `locateArtifacts`, `collectArtifacts`, `resolveArtifacts`, `fetchArtifact`, `downloadArtifacts` — is built on top of `resolveStorageUrls` and `getRunResult`, never on the embedded `public_url`, and is documented on [artifact-download.md](./artifact-download.md); downloading is a separate explicit gesture, and no run call takes a `download` option.
|
|
137
|
-
- **Runs list/detail/update:** `listRuns` (by `method_id`) returns **one page**, not the whole history — a `RunPage` (`{items, nextCursor}`) served from a time-ordered index, so its cost is the page size rather than the size of the history. Its rows are `RunHistoryItem`, **not** `PipelineRun`: the id, `status`, `created_at`, `finished_at`, `pipe_code`, for a failed run `error`, and `method_version` and `source_digest`, which version of the method the run ran (a number, `"draft"`, or `null` for an inline source) and the digest of what it ran — what a history row shows. The history lists the runs of every version and of the draft together, by the bare id. `method_version` and `source_digest` are optional on every run record (`RunPublic`, `RunRead`, `PipelineRun`, `RunDetail`, `RunHistoryItem`), since a run recorded before the platform recorded them carries neither. The run's organization, creator, method, workflow id and result location are not on the list; `getRunDetail` returns them. `iterateRuns` is the async iterator that follows the cursor for callers who genuinely want everything (`for await (const run of client.iterateRuns(id))`); it is deliberately not a `listAllRuns(): Promise<RunHistoryItem[]>`, because an all-at-once helper needs a page cap and a cap means silently returning a truncated list. Both accept `createdFrom` / `createdTo`, applied server-side as index key conditions — these are **instants, not days** (ISO-8601 with a UTC offset; a naive timestamp is a 400), because only the caller knows which timezone's day it means. `getRunDetail` (`GET /v1/runs/{id}`) is the only call returning `mthds_contents` — what the run actually executed — and `inputs`; it is kept off the status read that pollers hit every few seconds and off the list, where the bundle would be multiplied by the page size. `updateRun` is the admin/manual status patch. The status/results/start lifecycle routes live in the run-lifecycle section above.
|
|
138
|
+
- **Runs list/detail/update:** `listRuns` (by `method_id`) returns **one page**, not the whole history — a `RunPage` (`{items, nextCursor}`) served from a time-ordered index, so its cost is the page size rather than the size of the history. Its rows are `RunHistoryItem`, **not** `PipelineRun`: the id, `status`, `created_at`, `finished_at`, `pipe_code`, for a failed run `error`, and `method_version` and `source_digest`, which version of the method the run ran (a number, `"draft"`, or `null` for an inline source) and the digest of what it ran — what a history row shows. The history lists the runs of every version and of the draft together, by the bare id. `method_version` and `source_digest` are optional on every run record (`RunPublic`, `RunRead`, `PipelineRun`, `RunDetail`, `RunHistoryItem`), since a run recorded before the platform recorded them carries neither. The run's organization, creator, method, workflow id and result location are not on the list; `getRunDetail` returns them. `iterateRuns` is the async iterator that follows the cursor for callers who genuinely want everything (`for await (const run of client.iterateRuns(id))`); it is deliberately not a `listAllRuns(): Promise<RunHistoryItem[]>`, because an all-at-once helper needs a page cap and a cap means silently returning a truncated list. Both accept `createdFrom` / `createdTo`, applied server-side as index key conditions — these are **instants, not days** (ISO-8601 with a UTC offset; a naive timestamp is a 400), because only the caller knows which timezone's day it means. Both also accept `createdBy: "me"`, sent as `created_by=me`, which keeps the runs the caller started (the platform resolves "me" from the authenticated identity and takes no other value), and `status`, a list of `RunStatus` sent as one repeated `status=` parameter per status, deduplicated into the platform's order, which keeps the runs in any of them: the caller's own latest unfinished run is `{ createdBy: "me", status: ["PENDING", "STARTED", "RUNNING"], limit: 1 }`. An empty `status` list would be sent as no parameter at all, which lists every status, so it is a `RequestArgumentError` before any request, as is a name that is no `RunStatus`. These two are filters applied after the index read, so **a filtered page can hold fewer runs than `limit`, or none, and still carry a `nextCursor`**: an empty page is not the end of the history, a `null` cursor is, and `iterateRuns` keeps following the cursor through one. It runs the same loop as `iterateMethods`, so it also stops on a cursor the server failed to advance and throws `PagingNotTerminatingError` past the same page ceiling. `getRunDetail` (`GET /v1/runs/{id}`) is the only call returning `mthds_contents` — what the run actually executed — and `inputs`; it is kept off the status read that pollers hit every few seconds and off the list, where the bundle would be multiplied by the page size. `updateRun` is the admin/manual status patch. The status/results/start lifecycle routes live in the run-lifecycle section above.
|
|
138
139
|
|
|
139
140
|
## Hook bundle (`dist-hooks/check.mjs`)
|
|
140
141
|
|
package/docs/errors.md
CHANGED
|
@@ -43,14 +43,14 @@ On an error from a request that may have started a run, `retryable` is `false` w
|
|
|
43
43
|
| Class | `errorDomain` | `retryable` | Why |
|
|
44
44
|
|---|---|---|---|
|
|
45
45
|
| `ApiResponseError` | the server's, else the fallback | the server's, else no for a sent `input` or `config` domain, else the fallback | See [below](#a-refused-requests-verdict). |
|
|
46
|
-
| `RequestArgumentError` | `input` | no | The client refused a call's arguments before sending any request: no run source given to `execute` or `start`, run sources or method selectors that exclude each other, a selector rule of `validate`, an empty `validateFiles`, a reserved key in `extra`, a `publishMethod` without its token, a `getMethodVersion` version that is not a positive integer, a selector `parseMethodSelector` refuses,
|
|
46
|
+
| `RequestArgumentError` | `input` | no | The client refused a call's arguments before sending any request: no run source given to `execute` or `start`, run sources or method selectors that exclude each other, a selector rule of `validate`, an empty `validateFiles`, a reserved key in `extra`, a `publishMethod` without its token, a `getMethodVersion` version that is not a positive integer, a selector `parseMethodSelector` refuses, an artifact selection that is empty or names an unknown artifact, or a start's `idempotencyKey` that is not 1 to 255 visible ASCII characters ([below](#a-starts-idempotency-key--idempotencykey)). Its options take an optional `verdict`, and the client declares `config` for a base URL that is not host-only, since it typically comes from `PIPELEX_BASE_URL`. That refusal shows the base URL's scheme and host alone and names the other parts it carried (credentials, a path, a query, a fragment) without their text, since those are where a secret travels in a URL; a value that is not an http or https URL is not shown at all. It is a `PipelineRequestError`, as the bare refusal it replaced was, and a refusal the standard's own run-source check raises keeps that original as `cause`. |
|
|
47
47
|
| `ApiUnreachableError` | `config` | yes | The address or the network must be checked, and a later attempt can get through. With the code `ABORT_TIMEOUT`, the SDK's own request timeout, it is `runtime`: the API took the request and did not answer in time. |
|
|
48
48
|
| `PipelineExecuteTimeoutError` | `input` | no | The blocking `execute` cannot outlast the gateway's limit; start the run and poll instead. The run goes on, so its `runMayHaveStarted` is always `true`. |
|
|
49
49
|
| `RunFailedError` | the report's | the report's | See [a failed run](#a-failed-run--runerrorreport). |
|
|
50
50
|
| `RunTimeoutError`, `RunStillRunningError` | `runtime` | yes | The run is still going; waiting again, by its id, can succeed. |
|
|
51
51
|
| `RunLifecycleUnavailableError` | `config` | no | The base URL points at a bare runner without the run lifecycle. |
|
|
52
52
|
| `MissingMainStuffError` | `runtime` | no | The API broke its own contract on a completed run. |
|
|
53
|
-
| `PagingNotTerminatingError` | `runtime` | no | A server minting cursors forever; `pageLimit` is the ceiling `iterateMethods` stopped at. |
|
|
53
|
+
| `PagingNotTerminatingError` | `runtime` | no | A server minting cursors forever; `pageLimit` is the ceiling `iterateMethods` or `iterateRuns` stopped at. |
|
|
54
54
|
| `InputPreparationError` and `EmptyMethodSourceError`, `InvalidLocalSourceError`, `InvalidInputValueError`, `UploadNotAllowedError`, `MethodLoadError`, `RejectedAssetError` | `input` | no | The asset, the inputs or the method selector must change. The family's base takes an optional `verdict` in its options, so a subclass of your own can declare another; `prepareInputs` declares `runtime` for a pipe I/O answer that breaks the route's contract: no boolean `is_valid`, an `input_form` that does not describe the pipe it selected, or a descriptor that is malformed or names a kind this SDK does not know. |
|
|
55
55
|
| `UnsupportedUploadCapabilityError`, `UploadAuthenticationError` | `config` | no | The deployment or the credential must change. |
|
|
56
56
|
| `UploadTransportError` | the cause's, else by `code` | the cause's, else by `code` | `uploadFile` wraps the `ApiResponseError` or `ApiUnreachableError` the client threw, and takes its verdict, so a wrapped `402` is `config` and not retryable. Without such a cause, which is always the case from `uploadWithGrant`: `timeout`, `storage_timeout` and `conflict` are `runtime` and retryable, `conflict` because storage documents its `409 ConditionalRequestConflict` as retryable and the grant is not spent by it (a spent grant is the `412` of a `RejectedAssetError` with code `grant_used`); `server_error` is `runtime`, and retryable when [the fallback table](#a-refused-requests-verdict) would call its `status` retryable, any `5xx` but the `501` storage answers for a request it does not implement, or when it carries no status; `unexpected` is `runtime`, and retryable when that table would call its `status` retryable, which is the `408` or `429` storage answers when it times out or throttles the request rather than refusing the file, and not retryable for any other status or none; `unreachable` is `config` and retryable; `redirected` is `config` and not retryable; `invalid_grant_url` is `runtime` and not retryable. |
|
|
@@ -75,6 +75,7 @@ The fallback reads the HTTP status, the platform's `code`, and whether the body
|
|
|
75
75
|
| `408`, `429` | `runtime` | yes | Refused for its timing, not its content. |
|
|
76
76
|
| `409` | `input` | no | The request conflicts with the stored state: a method being deleted, a stale draft token, a method never published addressed by its bare id, a taken label. |
|
|
77
77
|
| `409` with code `pipelex_api_key_limit_reached` | `config` | no | The organization holds as many keys as it may; someone removes one. |
|
|
78
|
+
| `409` with code `idempotency_key_in_use` | `runtime` | yes | An earlier request under the same idempotency key is still being handled, and the same request sent again under that key is answered with its outcome. |
|
|
78
79
|
| `501` | `config` | no | The deployment does not implement it, and asking again will not change that. |
|
|
79
80
|
| `500`, `502`, `503`, `504`, and any `5xx` not listed here | `runtime` | yes | A fault in the service, which may pass. |
|
|
80
81
|
| Anything else | `runtime` | no | A status no refusal carries, such as the `2xx` of an answer the SDK could not read; nothing says a retry helps. |
|
|
@@ -126,10 +127,44 @@ try {
|
|
|
126
127
|
}
|
|
127
128
|
```
|
|
128
129
|
|
|
129
|
-
**
|
|
130
|
+
**An idempotency key makes the retry safe, and leaves the verdict as it is.** A `start`, and so `startAndWaitForResult`, takes an optional `idempotencyKey`, which the SDK sends as the `Idempotency-Key` header of `POST /v1/start`; without one it sends no header. The hosted API binds the key to the run before it dispatches it, so the same start sent again under the same key is answered with that run and starts nothing, however the first attempt ended. A caller that sent a key therefore answers a failure whose `runMayHaveStarted` is `true` by sending the same start again, with the same key and the same options, and gets the run back instead of paying for a second one ([below](#a-starts-idempotency-key--idempotencykey)). The error does not say so itself: `runMayHaveStarted` stays `true` and `retryable` stays `false` with a key, since the SDK cannot know that the server honours the key (a bare runner ignores it), and a retry under a new key, or with other options, would start a second run. The platform reads its own answer the same way: the `502` with the code `bad_gateway` it answers for a start whose acknowledgement it lost is not retryable whether or not a key was sent, and it names the run that may be running in `problemDocument.pipeline_run_id`.
|
|
130
131
|
|
|
131
132
|
The Python SDK carries the same member as `run_may_have_started`, with the reader `run_may_have_started_of`, and reads every failure the same way; the `run_start` rows of the shared case file hold the two to the same answer for every status.
|
|
132
133
|
|
|
134
|
+
### A start's idempotency key — `idempotencyKey`
|
|
135
|
+
|
|
136
|
+
Generate one key per run you mean to start (a `crypto.randomUUID()` is a good one), and keep it with the options across that start's retries. A key is 1 to 255 visible ASCII characters, `!` to `~`; anything else, an empty string included, is a `RequestArgumentError` before anything is sent, since a start sent without the key its caller gave would lose the protection without a word.
|
|
137
|
+
|
|
138
|
+
```ts
|
|
139
|
+
import { errorVerdictOf, runMayHaveStartedOf } from "@pipelex/sdk";
|
|
140
|
+
|
|
141
|
+
// One key per run, kept with the options, unchanged, across the start's retries.
|
|
142
|
+
const options = { method_id: "mt_abc123", inputs, idempotencyKey: crypto.randomUUID() };
|
|
143
|
+
try {
|
|
144
|
+
return await client.start(options);
|
|
145
|
+
} catch (err) {
|
|
146
|
+
// Under the same key, a start that may have started a run is answered with that run.
|
|
147
|
+
if (runMayHaveStartedOf(err) || errorVerdictOf(err)?.retryable) return await client.start(options);
|
|
148
|
+
throw err;
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
What the hosted API answers a start sent under a key:
|
|
153
|
+
|
|
154
|
+
| The start | The answer |
|
|
155
|
+
|---|---|
|
|
156
|
+
| The first under the key | The run starts, the key bound to it before it is dispatched. |
|
|
157
|
+
| The same options under the same key, once the first was answered or bound to its run | The first start's acknowledgement, the run's state as it stands, and nothing starts. A first start refused for its content is refused again; one refused for its timing, such as a `429` in front of the runner, freed the key, and the retry is handled afresh. |
|
|
158
|
+
| The same options under the same key, while the first is still being handled | After a short wait, the first start's answer; failing that, a `409` with the code `idempotency_key_in_use`, `runtime` and retryable, with a `Retry-After` header that `ApiResponseError` does not expose. Nothing ran for it, so its `runMayHaveStarted` is `false`: send it again under the same key in a few seconds, never under a new one, which would start a second run. |
|
|
159
|
+
| Other options under the same key | A `409` with the generic code `conflict`, `input` and not retryable: a key reused for another request is a bug in the caller. Send the new request under a new key. |
|
|
160
|
+
| The same options under a key bound to a run since deleted | A `409` `conflict` too: send it under a new key to start a new run. |
|
|
161
|
+
|
|
162
|
+
`IdempotencyErrorCode` names the code a caller branches on, `idempotency_key_in_use`; the key's other refusals carry the generic `conflict`. The hosted API keeps a key for 24 hours, scoped to the caller and the organization, so a start sent again after that starts a new run.
|
|
163
|
+
|
|
164
|
+
**Keep the body identical.** The hosted API compares the request's bytes, so the retry must carry the same options in the same form. The SDK builds the same body from the same options, but inputs prepared twice are not the same options: `prepareInputs` uploads a local file again and returns a new storage reference each time, so prepare the inputs once and reuse what it returned for every attempt.
|
|
165
|
+
|
|
166
|
+
**A bare runner ignores the key.** It has no idempotency to honour, so a key sent to one protects nothing, and `startAndWaitForResult`'s blocking execute against one sends none.
|
|
167
|
+
|
|
133
168
|
## A failed run — `RunErrorReport`
|
|
134
169
|
|
|
135
170
|
A run that ends without completing (`FAILED`, `CANCELLED`, `TERMINATED` or `TIMED_OUT`) has no result, and the results read answers `409`. The body of that `409` says why: its `detail` names the status and then the report's message, its `run_status` member holds the status, and its `error` member holds the run's stored error report, or `null` when the run has none. The SDK reads the status from `run_status`, falling back to the status word of the `detail` sentence on a platform that does not send the member yet, and types the report as `RunErrorReport`.
|
|
@@ -236,7 +271,7 @@ Every non-2xx answer from a `/v1` route is a problem document, and the SDK throw
|
|
|
236
271
|
| `title` | `title` | The stable human label of the error class. |
|
|
237
272
|
| `serverMessage` | `detail` (or `message`) | The per-occurrence message. |
|
|
238
273
|
| `instance` | `instance` | The occurrence: a request URN (`urn:pipelex:request:<id>`) or a request path. |
|
|
239
|
-
| `code` | `code` | The platform's native code, a closed set (`conflict`, `not_found`, `validation_failed`, `pipelex_api_key_limit_reached`, …), one-to-one with `type`; `MethodErrorCode` names the method ones (see [The method codes](#the-method-codes)). **A branch field for a client of the platform**, absent on a refusal it relays from the runner. |
|
|
274
|
+
| `code` | `code` | The platform's native code, a closed set (`conflict`, `not_found`, `validation_failed`, `pipelex_api_key_limit_reached`, …), one-to-one with `type`; `MethodErrorCode` names the method ones (see [The method codes](#the-method-codes)) and `IdempotencyErrorCode` the idempotency key's ([above](#a-starts-idempotency-key--idempotencykey)). **A branch field for a client of the platform**, absent on a refusal it relays from the runner. |
|
|
240
275
|
| `errorType` | `error_type` | The runner's native code, its open exception class name. |
|
|
241
276
|
| `errorCategory`, `model`, `provider`, `providerMetadata` | `error_category`, `model`, `provider`, `provider_metadata` | An inference failure's class and origin, as on the run report. |
|
|
242
277
|
| `migration` | `migration` | A pending configuration migration that explains the failure. |
|