@pipelex/sdk 0.34.1 → 0.35.1
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 +23 -3
- package/dist/cli/help.d.ts +3 -3
- package/dist/cli/help.js +6 -3
- package/dist/cli/help.js.map +1 -1
- package/dist/cli/method.d.ts +9 -3
- package/dist/cli/method.js +30 -7
- package/dist/cli/method.js.map +1 -1
- package/dist/cli/script.d.ts +6 -2
- package/dist/cli/script.js +13 -6
- package/dist/cli/script.js.map +1 -1
- package/dist/client.d.ts +178 -40
- package/dist/client.js +267 -42
- package/dist/client.js.map +1 -1
- package/dist/errors.d.ts +34 -9
- package/dist/errors.js +16 -9
- package/dist/errors.js.map +1 -1
- package/dist/execute-result.d.ts +6 -0
- package/dist/execute-result.js.map +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/method-selector.d.ts +42 -0
- package/dist/method-selector.js +63 -0
- package/dist/method-selector.js.map +1 -0
- package/dist/models.d.ts +22 -9
- package/dist/product-models.d.ts +221 -11
- package/dist/runs.d.ts +13 -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 +6 -5
- package/docs/cli.md +6 -6
- package/docs/crate-routes.md +4 -2
- package/docs/errors.md +18 -5
- package/docs/input-preparation.md +5 -3
- package/docs/run-results.md +1 -1
- package/package.json +1 -1
package/dist/models.d.ts
CHANGED
|
@@ -58,28 +58,41 @@ export interface MethodProvenance {
|
|
|
58
58
|
/**
|
|
59
59
|
* The `POST /v1/start` 202 ack as the Pipelex API returns it — the protocol's
|
|
60
60
|
* `RunResultStart` plus the server's `method_provenance` extension, populated
|
|
61
|
-
* for `method_ref` runs and absent or `null` otherwise
|
|
61
|
+
* for `method_ref` runs and absent or `null` otherwise, and the hosted
|
|
62
|
+
* `method_version`, populated for `method_id` runs.
|
|
62
63
|
*/
|
|
63
64
|
export interface PipelexRunResultStart extends RunResultStart {
|
|
64
65
|
method_provenance?: MethodProvenance | null;
|
|
66
|
+
/**
|
|
67
|
+
* Which version of the stored method a `method_id` run runs: the latest published version's
|
|
68
|
+
* number for a bare id, the pinned number for `mt_…@<n>`, `"draft"` for `mt_…@draft`. Absent
|
|
69
|
+
* or `null` for any other run, and on a server that does not resolve version selectors.
|
|
70
|
+
*/
|
|
71
|
+
method_version?: number | "draft" | null;
|
|
65
72
|
}
|
|
66
73
|
/**
|
|
67
74
|
* The hosted tooling routes' own selector — the layer-3 extension the platform
|
|
68
75
|
* adds on `POST /v1/validate`, `/v1/resolve`, `/v1/codegen`, and `/v1/pipe-io`: a stored
|
|
69
|
-
* method's catalog id (`mt_…`), resolved server-side against
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
76
|
+
* method's catalog id (`mt_…`), optionally naming a version, resolved server-side against
|
|
77
|
+
* the org's catalog, the `.mthds` files of the version it names injected as inline source
|
|
78
|
+
* before the runner sees the request. A bare `mt_…` names the latest published version,
|
|
79
|
+
* `mt_…@<n>` version `n` and `mt_…@draft` the draft. A **pass-through to the hosted API**:
|
|
80
|
+
* nothing is expanded client-side, and it is meaningless off-platform (a bare runner
|
|
81
|
+
* rejects the request as carrying no source it understands).
|
|
74
82
|
*
|
|
75
83
|
* The tooling routes are stateless, so there is no linkage exception: exactly
|
|
76
84
|
* one of inline source / `method_ref` / `method_id` per request — any second
|
|
77
85
|
* selector is a request-shape `422`. An unknown or foreign-org id is a `404`
|
|
78
|
-
* (indistinguishable by design); a
|
|
79
|
-
* `
|
|
86
|
+
* (indistinguishable by design); a bare id of a method never published is a `409`
|
|
87
|
+
* `method_not_published`; a version never published is a `404`
|
|
88
|
+
* `method_version_not_found`; a malformed suffix, or a stored method with no MTHDS
|
|
89
|
+
* source, is a `422`.
|
|
80
90
|
*/
|
|
81
91
|
export interface PipelexHostedToolingExtensions {
|
|
82
|
-
/**
|
|
92
|
+
/**
|
|
93
|
+
* A stored method's catalog id (`mt_…`, `mt_…@<n>` or `mt_…@draft`) — hosted-only,
|
|
94
|
+
* resolved server-side.
|
|
95
|
+
*/
|
|
83
96
|
method_id?: string;
|
|
84
97
|
}
|
|
85
98
|
/**
|
package/dist/product-models.d.ts
CHANGED
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
*/
|
|
12
12
|
import type { MethodFile } from "mthds/protocol";
|
|
13
13
|
import type { RunErrorReport } from "./error-models.js";
|
|
14
|
+
import type { PipelexValidationResult } from "./models.js";
|
|
14
15
|
import type { RunStatus } from "./runs.js";
|
|
15
16
|
export interface UserProfile {
|
|
16
17
|
email: string;
|
|
@@ -19,33 +20,113 @@ export interface UserProfile {
|
|
|
19
20
|
/** ISO timestamp the user completed onboarding; absent/null until they do. */
|
|
20
21
|
onboarding_completed_at?: string | null;
|
|
21
22
|
}
|
|
23
|
+
/**
|
|
24
|
+
* One published version without its sources — what `listMethodVersions` lists, what a
|
|
25
|
+
* method read carries as `latest_published`, and what a publish answers as `version`.
|
|
26
|
+
*/
|
|
27
|
+
export interface MethodVersionSummary {
|
|
28
|
+
/** The version number, from 1, never reused and never renumbered. */
|
|
29
|
+
version: number;
|
|
30
|
+
/**
|
|
31
|
+
* SHA-256 of the canonical form of the version's two file sets (`.mthds` and Python), as 64
|
|
32
|
+
* lowercase hex characters — the same function as the method's `draft_digest`, so equal
|
|
33
|
+
* digests mean the same sources would run.
|
|
34
|
+
*/
|
|
35
|
+
source_digest: string;
|
|
36
|
+
/**
|
|
37
|
+
* The runner's `crate.fingerprint` for the bundle (`POST /v1/resolve`), which covers its
|
|
38
|
+
* meaning rather than its bytes and is what generated client code is checked against.
|
|
39
|
+
* `null` when the runner validated the bundle but could not resolve it in memory, as for a
|
|
40
|
+
* bundle that depends on another method by address.
|
|
41
|
+
*/
|
|
42
|
+
crate_fingerprint: string | null;
|
|
43
|
+
/** The version of the runner that validated the bundle and computed the fingerprint. */
|
|
44
|
+
runner_version: string | null;
|
|
45
|
+
/** The bundle's top-level `description` at publish time. */
|
|
46
|
+
description: string | null;
|
|
47
|
+
/** ISO-8601 UTC instant of the publish. */
|
|
48
|
+
published_at: string;
|
|
49
|
+
/**
|
|
50
|
+
* The publisher's canonical user id, or `system:publish-initial` for a version the one-time
|
|
51
|
+
* migration wrote for a method saved before versions existed.
|
|
52
|
+
*/
|
|
53
|
+
published_by: string;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* One published version with its sources — `getMethodVersion`. It carries no `name`, which
|
|
57
|
+
* belongs to the method and changes without a publish, and no `input_data`, which is the
|
|
58
|
+
* editor's form state and not part of what a caller runs.
|
|
59
|
+
*/
|
|
60
|
+
export interface MethodVersion extends MethodVersionSummary {
|
|
61
|
+
method_id: string;
|
|
62
|
+
/** The version's `.mthds` source, in the same stored form as `MethodData.mthds`. */
|
|
63
|
+
mthds: string;
|
|
64
|
+
/**
|
|
65
|
+
* The version's custom-PipeFunc Python, as `MethodFile[]`, parsed from the wire string
|
|
66
|
+
* exactly as `MethodData.python` is. Empty array when the version has no custom Python.
|
|
67
|
+
*/
|
|
68
|
+
python?: MethodFile[];
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* A saved method — `getMethod`, `createMethod`, `writeDraft`, `renameMethod`, and the
|
|
72
|
+
* `method` of every publish outcome. Its content fields ARE the draft; the latest
|
|
73
|
+
* published version is summarized in `latest_published`.
|
|
74
|
+
*
|
|
75
|
+
* The publish state a client shows is derived, never stored: never published
|
|
76
|
+
* (`latest_version` is `null`), published with the draft unchanged (`draft_digest ===
|
|
77
|
+
* latest_published.source_digest`), or published with the draft ahead (they differ).
|
|
78
|
+
*/
|
|
22
79
|
export interface MethodData {
|
|
23
80
|
method_id: string;
|
|
24
81
|
org_id: string;
|
|
25
82
|
created_by_user_id: string;
|
|
26
83
|
name: string;
|
|
27
|
-
/** The `.mthds` bundle source. */
|
|
84
|
+
/** The draft's `.mthds` bundle source. */
|
|
28
85
|
mthds: string;
|
|
29
86
|
/**
|
|
30
|
-
*
|
|
87
|
+
* The draft's custom-PipeFunc Python sources, as `MethodFile[]` (`{ name, content }`). On
|
|
31
88
|
* the wire this is the serialized `[{ name, content }]` catalog string (empty
|
|
32
89
|
* `""` when the method has no custom Python); the client (de)serializes it via
|
|
33
90
|
* `mthds/protocol`'s `parseMethodFiles`/`serializeMethodFiles`, so callers work
|
|
34
91
|
* with the typed array. Empty array when the method has no custom Python.
|
|
35
|
-
* Returned on both get and list.
|
|
36
92
|
*/
|
|
37
93
|
python?: MethodFile[];
|
|
94
|
+
/** The editor's form inputs, saved with the draft and outside both digests. */
|
|
38
95
|
input_data?: Record<string, unknown> | null;
|
|
39
96
|
/** Legacy persisted output spec; optional. */
|
|
40
97
|
pipe_output?: Record<string, unknown> | null;
|
|
41
98
|
/**
|
|
42
|
-
* Derived from the
|
|
43
|
-
* server recomputes it from the
|
|
99
|
+
* Derived from the draft's top-level `description`. Read-side only: the
|
|
100
|
+
* server recomputes it from the draft on every draft write, so it never appears on
|
|
44
101
|
* the write contract.
|
|
45
102
|
*/
|
|
46
103
|
description?: string | null;
|
|
47
104
|
created_at: string;
|
|
105
|
+
/**
|
|
106
|
+
* **The draft's token.** It moves on every draft write and on nothing else: creation sets
|
|
107
|
+
* it, and neither a rename nor a publish moves it. Echo it verbatim — the platform compares
|
|
108
|
+
* the strings — as `expected_updated_at` on the next `writeDraft`, or as
|
|
109
|
+
* `expected_draft_updated_at` on `publishMethod`, so neither ever overwrites or publishes a
|
|
110
|
+
* draft the caller has not seen.
|
|
111
|
+
*/
|
|
48
112
|
updated_at: string;
|
|
113
|
+
/**
|
|
114
|
+
* The draft's digest: SHA-256 of the canonical form of its two file sets, 64 lowercase hex
|
|
115
|
+
* characters. `input_data` is outside it, so saving only the form inputs moves the token and
|
|
116
|
+
* leaves the digest as it was.
|
|
117
|
+
*/
|
|
118
|
+
draft_digest: string;
|
|
119
|
+
/** The number of the latest published version; `null` when the method was never published. */
|
|
120
|
+
latest_version: number | null;
|
|
121
|
+
/** The latest published version's summary; `null` when the method was never published. */
|
|
122
|
+
latest_published: MethodVersionSummary | null;
|
|
123
|
+
/**
|
|
124
|
+
* Where the method is in the erasure cascade; absent or `null` on a normal method. Every
|
|
125
|
+
* method route refuses a method whose erasure has started with a `409`
|
|
126
|
+
* `method_being_deleted`, so a method read seldom carries it; `MethodSummary` carries the
|
|
127
|
+
* same field on the list, where a method mid-erasure stays listed.
|
|
128
|
+
*/
|
|
129
|
+
deletion_state?: MethodDeletionState | null;
|
|
49
130
|
}
|
|
50
131
|
/** Where a method is in the erasure cascade; absent on a normal method. */
|
|
51
132
|
export type MethodDeletionState = "pending" | "in_progress" | "failed";
|
|
@@ -113,19 +194,126 @@ export interface MethodPage {
|
|
|
113
194
|
items: MethodSummary[];
|
|
114
195
|
nextCursor: string | null;
|
|
115
196
|
}
|
|
116
|
-
/**
|
|
197
|
+
/**
|
|
198
|
+
* The create payload — `createMethod`. A new method holds this as its draft and has no
|
|
199
|
+
* version yet. Its draft is then written with `writeDraft` and its name changed with
|
|
200
|
+
* `renameMethod`.
|
|
201
|
+
*/
|
|
117
202
|
export interface MethodWriteInput {
|
|
118
203
|
name: string;
|
|
119
204
|
mthds: string;
|
|
120
205
|
/**
|
|
121
|
-
* Custom-PipeFunc Python as `MethodFile[]
|
|
122
|
-
*
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
206
|
+
* Custom-PipeFunc Python as `MethodFile[]`, serialized to the catalog string on the wire;
|
|
207
|
+
* omitted or an empty array, the method has none.
|
|
208
|
+
*/
|
|
209
|
+
python?: MethodFile[];
|
|
210
|
+
input_data?: Record<string, unknown> | null;
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* The draft write — `writeDraft`, `PUT /v1/methods/{id}/draft`. It never validates: a draft
|
|
214
|
+
* may be invalid, and an autosave of work in progress often is.
|
|
215
|
+
*/
|
|
216
|
+
export interface MethodDraftInput {
|
|
217
|
+
/** The draft's `.mthds` files, in the stored serialized form. Required. */
|
|
218
|
+
mthds: string;
|
|
219
|
+
/**
|
|
220
|
+
* Custom-PipeFunc Python as `MethodFile[]`. Three-way: **omit** (`undefined`) keeps the
|
|
221
|
+
* stored Python (the client sends nothing); an **empty array** `[]` clears it (the client
|
|
222
|
+
* serializes it to `""`); a **non-empty array** replaces it. It is a replace, not a merge —
|
|
223
|
+
* send the full set on a write that intends to change it.
|
|
126
224
|
*/
|
|
127
225
|
python?: MethodFile[];
|
|
226
|
+
/**
|
|
227
|
+
* The editor's form inputs. Three-way too: **omit** keeps the stored inputs, an explicit
|
|
228
|
+
* `null` clears them, and any other value replaces them.
|
|
229
|
+
*/
|
|
128
230
|
input_data?: Record<string, unknown> | null;
|
|
231
|
+
/**
|
|
232
|
+
* The draft token the caller last saw — the `updated_at` of the method it last read or
|
|
233
|
+
* wrote, echoed verbatim. With it the write is a compare-and-swap: a draft that moved since
|
|
234
|
+
* is refused with a `409` whose `code` is `method_update_conflict`, and nothing is written.
|
|
235
|
+
* Omitted, the write is last-writer-wins.
|
|
236
|
+
*/
|
|
237
|
+
expected_updated_at?: string;
|
|
238
|
+
}
|
|
239
|
+
/** The rename — `renameMethod`, `PATCH /v1/methods/{id}`. It moves no token. */
|
|
240
|
+
export interface MethodRenameInput {
|
|
241
|
+
/** The method's new name, non-empty. */
|
|
242
|
+
name: string;
|
|
243
|
+
}
|
|
244
|
+
/** The publish — `publishMethod`, `POST /v1/methods/{id}/publish`. */
|
|
245
|
+
export interface MethodPublishInput {
|
|
246
|
+
/**
|
|
247
|
+
* The draft token the caller last saw (`MethodData.updated_at`), echoed verbatim. Required: a
|
|
248
|
+
* publish never takes a draft its caller has not seen, so a draft that moved since is
|
|
249
|
+
* refused with a `409` whose `code` is `method_update_conflict`, and nothing is published.
|
|
250
|
+
*/
|
|
251
|
+
expected_draft_updated_at: string;
|
|
252
|
+
}
|
|
253
|
+
/** What a publish did with the draft — the discriminant of `MethodPublishResult`. */
|
|
254
|
+
export type MethodPublishOutcome = "published" | "unchanged" | "refused";
|
|
255
|
+
/**
|
|
256
|
+
* Why a draft was not published: `invalid`, it does not validate; `not_runnable`, it validates
|
|
257
|
+
* with pending signatures, so it is valid but does not run yet.
|
|
258
|
+
*/
|
|
259
|
+
export type MethodPublishRefusalReason = "invalid" | "not_runnable";
|
|
260
|
+
/** A publish that wrote a new version. */
|
|
261
|
+
export interface MethodPublished {
|
|
262
|
+
outcome: "published";
|
|
263
|
+
/** The new version's summary. */
|
|
264
|
+
version: MethodVersionSummary;
|
|
265
|
+
method: MethodData;
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* A publish with nothing to publish: the draft's digest equals the latest version's, so no
|
|
269
|
+
* runner was asked. Also the answer to the loser of two concurrent publishes of the same draft.
|
|
270
|
+
*/
|
|
271
|
+
export interface MethodPublishUnchanged {
|
|
272
|
+
outcome: "unchanged";
|
|
273
|
+
/** The existing latest version's summary. */
|
|
274
|
+
version: MethodVersionSummary;
|
|
275
|
+
method: MethodData;
|
|
276
|
+
}
|
|
277
|
+
/** A publish the draft's content refused: nothing was published. */
|
|
278
|
+
export interface MethodPublishRefused {
|
|
279
|
+
outcome: "refused";
|
|
280
|
+
reason: MethodPublishRefusalReason;
|
|
281
|
+
/** Says why; for `not_runnable`, that the draft "is valid but does not run yet". */
|
|
282
|
+
message: string;
|
|
283
|
+
/**
|
|
284
|
+
* The runner's `POST /v1/validate` answer for the draft, verbatim: an invalid arm whose
|
|
285
|
+
* `validation_errors` say what to fix, or, for `not_runnable`, the valid arm.
|
|
286
|
+
*/
|
|
287
|
+
validation: PipelexValidationResult;
|
|
288
|
+
method: MethodData;
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* The answer of `publishMethod`, a `200` discriminated on `outcome` because each arm is a
|
|
292
|
+
* verdict about the draft's content. Branch on `outcome`, never on the status: a stale token,
|
|
293
|
+
* a method being deleted, a draft with no `.mthds` file or an unreachable runner are thrown as
|
|
294
|
+
* `ApiResponseError` instead, since they produce no verdict about the content.
|
|
295
|
+
*/
|
|
296
|
+
export type MethodPublishResult = MethodPublished | MethodPublishUnchanged | MethodPublishRefused;
|
|
297
|
+
/** Query for one page of a method's versions. */
|
|
298
|
+
export interface ListMethodVersionsQuery {
|
|
299
|
+
/**
|
|
300
|
+
* Page size, from 1 to 100; the API defaults to 20 and refuses a `limit` outside that range
|
|
301
|
+
* with a `422`.
|
|
302
|
+
*/
|
|
303
|
+
limit?: number;
|
|
304
|
+
/** Opaque `nextCursor` from the previous page. */
|
|
305
|
+
cursor?: string;
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* One page of a method's published versions, newest first, without their sources.
|
|
309
|
+
*
|
|
310
|
+
* `nextCursor` is opaque — pass it straight back to `listMethodVersions` to continue; `null`
|
|
311
|
+
* means this was the last page. Versions are read whole on the server, so a page may be short
|
|
312
|
+
* while `nextCursor` is set. A method never published answers an empty page.
|
|
313
|
+
*/
|
|
314
|
+
export interface MethodVersionPage {
|
|
315
|
+
items: MethodVersionSummary[];
|
|
316
|
+
nextCursor: string | null;
|
|
129
317
|
}
|
|
130
318
|
export interface Membership {
|
|
131
319
|
org_id: string;
|
|
@@ -297,6 +485,20 @@ export interface PipelineRun {
|
|
|
297
485
|
* whose completion callback carried one. This is how a consumer tells the user
|
|
298
486
|
* WHY a run failed rather than showing a generic message. */
|
|
299
487
|
error?: RunErrorReport | null;
|
|
488
|
+
/**
|
|
489
|
+
* Which version of its method the run ran: the version number for a run addressed by a
|
|
490
|
+
* bare id (the latest published version) or by `mt_…@<n>`, `"draft"` for one addressed by
|
|
491
|
+
* `mt_…@draft`, and `null` for a run of an inline source. Absent on a run recorded by a
|
|
492
|
+
* platform that did not record it yet.
|
|
493
|
+
*/
|
|
494
|
+
method_version?: number | "draft" | null;
|
|
495
|
+
/**
|
|
496
|
+
* The digest of the files the run ran, in the canonical form of `MethodData.draft_digest`, so
|
|
497
|
+
* an inline run identical to a version records that version's `source_digest`. `null` when
|
|
498
|
+
* the platform never held the files, as on a `method_ref` run; absent on a run recorded by a
|
|
499
|
+
* platform that did not record it yet.
|
|
500
|
+
*/
|
|
501
|
+
source_digest?: string | null;
|
|
300
502
|
}
|
|
301
503
|
/**
|
|
302
504
|
* One row of a method's run history — an item of `listRuns`' page and of `iterateRuns`.
|
|
@@ -319,6 +521,14 @@ export interface RunHistoryItem {
|
|
|
319
521
|
/** The runner's stored error report — present only on a failed run that recorded one,
|
|
320
522
|
* so opening it from history shows why without another read. */
|
|
321
523
|
error?: RunErrorReport | null;
|
|
524
|
+
/**
|
|
525
|
+
* Which version of the method the run ran — a number, `"draft"`, or `null` for an inline
|
|
526
|
+
* source (see `PipelineRun.method_version`). The history lists the runs of every version
|
|
527
|
+
* and of the draft together, so this is how a row says which one it was.
|
|
528
|
+
*/
|
|
529
|
+
method_version?: number | "draft" | null;
|
|
530
|
+
/** The digest of the files the run ran (see `PipelineRun.source_digest`). */
|
|
531
|
+
source_digest?: string | null;
|
|
322
532
|
}
|
|
323
533
|
/**
|
|
324
534
|
* A single run, whole — `GET /v1/runs/{id}`.
|
package/dist/runs.d.ts
CHANGED
|
@@ -53,6 +53,19 @@ export interface RunPublic {
|
|
|
53
53
|
* which is where `getRunResult`'s failed arm and `RunFailedError` take it from.
|
|
54
54
|
*/
|
|
55
55
|
error?: RunErrorReport | null;
|
|
56
|
+
/**
|
|
57
|
+
* Which version of its stored method the run ran: the version number for a run addressed by
|
|
58
|
+
* a bare `method_id` (the latest published version) or by `mt_…@<n>`, `"draft"` for one
|
|
59
|
+
* addressed by `mt_…@draft`, and `null` for a run of an inline source. A hosted extension,
|
|
60
|
+
* absent on a server that does not record it.
|
|
61
|
+
*/
|
|
62
|
+
method_version?: number | "draft" | null;
|
|
63
|
+
/**
|
|
64
|
+
* The digest of the files the run ran, in the canonical form of `MethodData.draft_digest`;
|
|
65
|
+
* `null` when the platform never held the files, as on a `method_ref` run. A hosted
|
|
66
|
+
* extension, absent on a server that does not record it.
|
|
67
|
+
*/
|
|
68
|
+
source_digest?: string | null;
|
|
56
69
|
/** Server-specific response fields (defined by the server you call). */
|
|
57
70
|
[extension: string]: unknown;
|
|
58
71
|
}
|
package/dist/runs.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runs.js","sourceRoot":"","sources":["../src/runs.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9D,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAyCjD,MAAM,qBAAqB,GAA2B,IAAI,GAAG,CAAY;IACvE,WAAW;IACX,QAAQ;IACR,WAAW;IACX,YAAY;IACZ,WAAW;CACZ,CAAC,CAAC;AAEH,6EAA6E;AAC7E,MAAM,UAAU,mBAAmB,CAAC,MAAiB;IACnD,OAAO,qBAAqB,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;AAC3C,CAAC;AAED,+EAA+E;AAC/E,MAAM,UAAU,kBAAkB,CAAC,MAAiB;IAClD,OAAO,MAAM,KAAK,WAAW,CAAC;AAChC,CAAC;
|
|
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
|
@@ -58,7 +58,7 @@ The ruling is about the artifacts, not about one envelope: `RunResults` carries
|
|
|
58
58
|
|
|
59
59
|
A run request carries its method inline exactly one of three ways: `mthds_contents` text, a `files` bundle map, or a `bundle_b64` zip. Those three are *encodings* of a bundle the caller ships, and they are mutually exclusive. Two selectors sit beside them on separate axes: the Pipelex-API `method_ref` (an address the RUNNER resolves by git fetch — a complete run source that pairs with nothing; see [Pipelex API run extensions](#pipelex-api-run-extensions-method_ref) below) and the hosted `method_id` (a catalog id the PLATFORM resolves — legal on its own, and legal alongside one inline encoding, where the inline source runs and the id rides along as run-history linkage; see [Hosted run extensions](#hosted-run-extensions-method_id) below). The rules — and crucially, _where they live_ — are:
|
|
60
60
|
|
|
61
|
-
- **Exclusivity is enforced from the standard, not restated here.** `assertExclusiveRunSources` and `hasBundlePayload` are imported from `mthds/protocol`; they are pure predicates over `RunRequest`, so this client and the MTHDS runners cannot drift on which combinations they reject. A second local copy would be free to disagree about what is legal. The `method_ref` pairings
|
|
61
|
+
- **Exclusivity is enforced from the standard, not restated here.** `assertExclusiveRunSources` and `hasBundlePayload` are imported from `mthds/protocol`; they are pure predicates over `RunRequest`, so this client and the MTHDS runners cannot drift on which combinations they reject. A second local copy would be free to disagree about what is legal. The `method_ref` pairings (`assertMethodRefPairsWithNothing` in `client.ts`) and the linkage form's bare `method_id` (`assertLinkageMethodIdIsBare`) are the local additions, because the protocol knows neither field — the wording of the `method_ref` errors mirrors the server's own validator.
|
|
62
62
|
- **Exclusivity keys off presence, the run-source precondition keys off runnability.** Supplying `files: {}` _and_ `bundle_b64` is rejected — the caller expressed two encodings — while an empty encoding on its own carries no method and so is neither shipped on the wire nor counted as "something to run".
|
|
63
63
|
- **`files` / `bundle_b64` / `bundleMain` / `method_ref` / `method_id` are reserved keys on `extra`.** `extra` merges last into the body, so smuggling a run source through it would overwrite the validated fields and bypass the exclusivity check; `buildExtensions` rejects any reserved key — the named run sources, the two method selectors, _and_ the client-only `bundleMain` hint. `buildExtensions` also strips prototype-pollution keys (`__proto__` / `constructor` / `prototype`), which `JSON.parse`-sourced `extra` can carry, so the client never emits a pollution gadget on the wire.
|
|
64
64
|
- **`bundleMain` is client-only and never serialized.** It is an entrypoint hint for a _local_ runner; the request builders name wire fields explicitly, and it is reserved on `extra`, so it cannot leak onto the wire by either path.
|
|
@@ -79,9 +79,10 @@ A run request carries its method inline exactly one of three ways: `mthds_conten
|
|
|
79
79
|
- **It is a named option, not an `extra` entry.** Typing its own platform's arguments is the one job a layer-3 client exists to do; `extra` stays the escape hatch for extensions this client does not know about (a third-party server, or a newer version of ours). That split, and the reserved-keys guard that enforces it, are normative for every client in the layered stack: a client types the arguments of the platform it is built for, and only the layer that owns an argument may validate or reject it. It was previously passed as `extra: { method_id }`, which worked but documented nothing and validated nothing.
|
|
80
80
|
- **The guard is per layer.** `method_id` is reserved on `extra` _here_ and must never become reserved in `mthds` / `mthds-python`: a protocol client talking to another vendor's server has no business rejecting that vendor's arguments.
|
|
81
81
|
- **Pure pass-through — nothing is expanded client-side.** The platform resolves the id against the org's catalog. The same posture holds across the tooling routes: `validate` / `resolve` / `codegen` / `pipeIo` take a typed `method_id` the platform resolves server-side (see the surface list below), and so does `prepareInputs`, which composes a `pipeIo` of its own. `getMethodClosure` stays public as the local expansion utility, for a caller that wants a stored method's files in hand or talks to a bare runner, which has no catalog to resolve an id against.
|
|
82
|
-
- **Alone it is a run source; alongside an inline source it is linkage.** With no inline source the platform resolves and runs the
|
|
82
|
+
- **Alone it is a run source; alongside an inline source it is linkage.** With no inline source the platform resolves and runs the version the id names. With one, the inline source is what RUNS (precedence) and the id is recorded as run-history linkage on the Run row — the index key `GET /v1/runs?method_id=` queries. Rejecting the combination is not an option: it once orphaned every unsaved-bundle run from its method. The client therefore counts `method_id` towards its "something to run" precondition, and never treats it as an exclusivity violation.
|
|
83
83
|
- **It rides the blocking fallback too.** `startAndWaitForResult` degrades to `POST /v1/execute` against a runner with no run store, and the selector goes with it — so a bare `pipelex-api` answers a `422` that names the key (its Rule 4 diagnosis: "a hosted-only selector must be sent to the hosted API") instead of the client silently dropping it and running something else.
|
|
84
84
|
- **An empty string is treated as absent.** `method_id: ""` selects no method and links nothing, so it is not sent and does not satisfy the precondition.
|
|
85
|
+
- **It may name a version.** A bare `mt_…` runs the method's latest published version, `mt_…@<n>` the fixed version `n` and `mt_…@draft` its draft; the suffix is part of the string and passes through untouched, on the run routes and the tooling routes alike, since the platform parses it at its edge. On the linkage form, beside an inline source, the id must be bare: the inline source is what runs, so a suffix there would claim a version that did not run. The platform refuses it with a `422`, and `execute` and `start` refuse it first with a `RequestArgumentError`, before anything is sent, counting an inline source only when it reaches the wire: an empty `mthds_contents`, `files: {}` or `bundle_b64: ""` is no source, so a suffixed id beside one runs the version it names. The start ack and the execute response carry `method_version`, the version a `method_id` run runs, so a caller learns which version a bare id ran from the answer that started it. `parseMethodSelector` splits a selector into its bare id and version locally, for a caller that reads the method by its bare id.
|
|
85
86
|
|
|
86
87
|
## Module layout
|
|
87
88
|
|
|
@@ -121,16 +122,16 @@ Flat `src/` (mirrors `mthds-js`'s `runners/api` flatness — the SDK has one cli
|
|
|
121
122
|
|
|
122
123
|
### Product routes (hosted management surface)
|
|
123
124
|
|
|
124
|
-
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`, …),
|
|
125
|
+
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 platform does not emit `error_domain` or `retryable` on its own refusals yet, so their `errorDomain` and `retryable` are the SDK's fallback, read from the status; every error carries both, decided. See [errors.md](./errors.md).
|
|
125
126
|
|
|
126
127
|
- **User profile:** `getMe` (`GET /v1/me`).
|
|
127
|
-
- **Methods catalog:** `listMethods`, `getMethod`, `createMethod`, `
|
|
128
|
+
- **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())`); unlike `iterateRuns` it must keep going **past empty pages**, because `q` is a post-read filter over a bounded index slice per request, so `{items: [], nextCursor: "…"}` means "nothing matched in the slice I just read, keep going" — the difference is the server, not the client. 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. 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).
|
|
128
129
|
- **Organizations:** `listMemberships`, `createOrganization`, `renameOrganization` (`PATCH`). The org "switch" is a WorkOS-session op (`/api/active-org`), not a `/v1` route — out of SDK scope.
|
|
129
130
|
- **Billing:** `getSubscription`, `listPlans`, `listInvoices`, `createCheckout`, `changePlan` (409 `conflict` ⇒ no subscription), `getBillingPortal` (409 `conflict` ⇒ no subscription).
|
|
130
131
|
- **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.
|
|
131
132
|
- **Onboarding:** `submitOnboarding`.
|
|
132
133
|
- **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.
|
|
133
|
-
- **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
|
|
134
|
+
- **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.
|
|
134
135
|
|
|
135
136
|
## Hook bundle (`dist-hooks/check.mjs`)
|
|
136
137
|
|
package/docs/cli.md
CHANGED
|
@@ -19,7 +19,7 @@ The command lives in `src/cli.ts`, the executable, and `src/cli/`. It sits at th
|
|
|
19
19
|
pipelex-sdk run --method <method> [--pipe <domain.pipe_code>] [--inputs <file> | --inputs-template]
|
|
20
20
|
```
|
|
21
21
|
|
|
22
|
-
- **`--method`**, required, names the method in one of the forms below, told apart by shape alone. A value starting with `mt_` is a catalog id, sent as `method_id`. A value starting with `github.com/` is a published method's address, `github.com/<owner>/<repo>[/<package>][@<tag>]`, sent as `method_ref`. Anything else is a path to a `.mthds` file or a bundle directory, which must exist and is read from disk and sent inline (see "Reading a bundle" below). A file or directory that happens to be named `mt_…` is reached as `./mt_…`, so what a value names never depends on what the current directory holds, and an address is written without its scheme. An `mt_` value holding a character no catalog id holds, anything but letters, digits, `_` and `-` (the ids are minted as `mt_` and a UUID, and the run route takes no other character), is a usage error made before any request, which says to write `./` to name a file: `mt_review.mthds` is refused, `./mt_review.mthds` is a path.
|
|
22
|
+
- **`--method`**, required, names the method in one of the forms below, told apart by shape alone. A value starting with `mt_` is a catalog id, sent as `method_id`. A value starting with `github.com/` is a published method's address, `github.com/<owner>/<repo>[/<package>][@<tag>]`, sent as `method_ref`. Anything else is a path to a `.mthds` file or a bundle directory, which must exist and is read from disk and sent inline (see "Reading a bundle" below). A file or directory that happens to be named `mt_…` is reached as `./mt_…`, so what a value names never depends on what the current directory holds, and an address is written without its scheme. An `mt_` value holding a character no catalog id holds, anything but letters, digits, `_` and `-` (the ids are minted as `mt_` and a UUID, and the run route takes no other character), is a usage error made before any request, which says to write `./` to name a file: `mt_review.mthds` is refused, `./mt_review.mthds` is a path. A catalog id may end in a version suffix, which is sent whole: a bare `mt_…` runs the method's latest published version, `mt_…@<n>` the fixed version `n` and `mt_…@draft` its draft. A suffix that is neither a positive number without a leading zero nor `draft` (`@0`, `@03`, `@Draft`, two suffixes) is a usage error made before any request, as `parseMethodSelector` reads it.
|
|
23
23
|
- **`--pipe`** names the pipe by its qualified ref, `domain.pipe_code`. It is sent as `pipe_ref` to the input preparation and as `pipe_code` to the run. A bare code, and an `alias->domain.pipe_code` ref naming a dependency's pipe, are usage errors. Without it, the run takes the method's main pipe, as the API resolves it.
|
|
24
24
|
- **`--inputs`** names a JSON file holding one object, one entry per input, the shape `mthds run --inputs` and `cli-python`'s `--inputs` take; `-` reads it from stdin. The text is UTF-8, and a byte-order mark at its start is dropped, as RFC 8259 lets a JSON reader do, since Windows PowerShell 5.1 writes one (`Out-File -Encoding utf8`); a bundle's mark, by contrast, is sent as it is on disk. A relative path resolves against the current directory. A file or stdin that cannot be read is a usage error, `cannot read stdin.` with the system's reason under it (`Reason: EIO`), and a stdin closed when the command starts reads as empty, so it is not valid JSON: Node opens the null device on a standard stream a process starts without, and the Python command reads the missing stream the same way. A `process.stdin` destroyed before the command reads it is unreadable too, with the reason `ERR_STREAM_PREMATURE_CLOSE`. Without it the run gets no inputs. When the object has entries, they first go through the SDK's input preparation (`prepareInputs`, one `POST /v1/pipe-io` call): a local path or a `data:` URL at a file input is uploaded and replaced by its `pipelex-storage://` reference, each upload is reported on stderr, and a URL passes through. An empty object or no `--inputs` makes no preparation request. A value at a file input that is no file, a `data:` URL that does not decode or a value of another type such as a number, is a usage error, as is a local path that cannot be read; a method that does not load, met during the preparation, is reported as `--inputs-template` reports it ("Exit codes, errors and Ctrl-C" below). The command tells the two apart by the SDK's error classes, `InvalidLocalSourceError` and `InvalidInputValueError` for the inputs' mistakes and `MethodLoadError` for the method's ([`input-preparation.md`](input-preparation.md)).
|
|
25
25
|
- **`--inputs-template`** prints the pipe's inputs template, the object `--inputs` takes with a placeholder for every input, and runs nothing. The template is rendered by `mthds`' `renderInputsTemplate`, in its compact JSON shape, from the input-form descriptor one `POST /v1/pipe-io` call returns, which spends no inference. It takes no `--inputs`.
|
|
@@ -41,13 +41,13 @@ pipelex-sdk script --method <address | catalog id> [--pipe <domain.pipe_code>] [
|
|
|
41
41
|
# receipt-review: runs github.com/acme/methods/receipt-review@v1.0.0 on the Pipelex API.
|
|
42
42
|
# Written by @pipelex/sdk X.Y.Z. Needs Node 22.12 or later, and PIPELEX_API_KEY in the environment.
|
|
43
43
|
# Usage: ./receipt-review --inputs inputs.json, or ./receipt-review --inputs-template to see what to fill in.
|
|
44
|
-
exec npx --yes @pipelex/sdk@X.Y.Z run --method 'github.com/acme/methods/receipt-review@v1.0.0' --pipe 'receipts.review_receipt' "$@"
|
|
44
|
+
exec npx --yes --no-update-notifier --loglevel=warn @pipelex/sdk@X.Y.Z run --method 'github.com/acme/methods/receipt-review@v1.0.0' --pipe 'receipts.review_receipt' "$@"
|
|
45
45
|
```
|
|
46
46
|
|
|
47
47
|
- **The method is an address or a catalog id.** A local bundle is refused, since a script naming a path breaks as soon as the script or the bundle moves; the refusal names both ways to get one, publishing the method or saving it to the catalog. An address without a tag is accepted, with a warning that the script follows the repository's default branch.
|
|
48
|
-
- **The name** defaults to the address's last segment without its tag, or, for a catalog id, to the method's name read from its catalog entry (`GET /v1/methods/{id}
|
|
48
|
+
- **The name** defaults to the address's last segment without its tag, or, for a catalog id, to the method's name read from its catalog entry (`GET /v1/methods/{id}`, by the bare id, since the name belongs to the method and not to a version; the script keeps the suffix) and kebab-cased: decomposed (NFKD), stripped of combining marks, lowercased, every run of characters outside `a-z0-9` turned into one `-`, trimmed of `-`. A name that comes out empty asks for `--name`, and so does a catalog entry whose name is missing or not a string. A name is a file name: it may not be empty, `.` or `..`, or hold a `/`, a `\` or a control character. The `\` is refused on every platform, since Windows reads it as a `/` and `..\outside` would leave `--dir`.
|
|
49
49
|
- **The file** is written in `--dir`, by default the current directory, which must exist. It is created with the permissions of an executable (`0755`, less the umask) and never over an existing file, a dangling link included. A write that fails once the file exists removes it, so a script is whole or absent, never a truncated one that would run half a command and make the next attempt say it already exists; a Ctrl-C waits for the file to be whole ("Exit codes, errors and Ctrl-C" below). The path printed is `--dir` as given without its trailing `/`, or `.`, then `/` and the name.
|
|
50
|
-
- **The script's one line** is `exec npx --yes @pipelex/sdk@<version> run --method '<method>' [--pipe '<pipe>'] "$@"`: every value single-quoted, an inner `'` spelled `'\''`, and no value may hold a control character. The `"$@"` passes everything else on, `--inputs`, `--inputs-template` and stdin included, and since `run` refuses a flag given twice, a second `--method` cannot quietly run another method. The version is the one of the SDK that wrote the script, so the script keeps running the command it was written with; `npx` caches the package, so after the first run only the API needs the network. There is no option to write the Python SDK's line, since this SDK cannot know which version of the other package exists and was tested.
|
|
50
|
+
- **The script's one line** is `exec npx --yes --no-update-notifier --loglevel=warn @pipelex/sdk@<version> run --method '<method>' [--pipe '<pipe>'] "$@"`: every value single-quoted, an inner `'` spelled `'\''`, and no value may hold a control character. The `"$@"` passes everything else on, `--inputs`, `--inputs-template` and stdin included, and since `run` refuses a flag given twice, a second `--method` cannot quietly run another method. The version is the one of the SDK that wrote the script, so the script keeps running the command it was written with; `npx` caches the package, so after the first run only the API needs the network. The two flags keep npm's own notices off the script's stderr, which carries the command's lines: `--no-update-notifier` removes the update notice npm 10 and 11 print wherever their cache starts empty, as in a fresh container, and `--loglevel=warn` removes the `npm notice run …` lines npm 12 prints at every run, while npm's warnings and errors, such as a version that cannot be fetched or a Node older than the package needs, still print. They act on `npx` alone, which hands everything after the package to the command. There is no option to write the Python SDK's line, since this SDK cannot know which version of the other package exists and was tested.
|
|
51
51
|
- **On Windows** the script needs a POSIX shell, such as Git Bash or WSL; otherwise type the `npx` line directly.
|
|
52
52
|
|
|
53
53
|
## The environment
|
|
@@ -66,7 +66,7 @@ The exit code is presentation, following the workspace's surface-output rule: a
|
|
|
66
66
|
| --- | --- |
|
|
67
67
|
| `0` | The result was printed, or the script written, or the help or version shown |
|
|
68
68
|
| `1` | The run failed, the API refused a request, could not be reached, or answered what the command cannot read |
|
|
69
|
-
| `2` | A usage error: a bad flag, an unreadable or invalid inputs file, a selector of no known form, a path that names no bundle, a missing key, a refused base URL, an `mt_` value that cannot be a catalog id, a local file the inputs name that cannot be read, a value at a file input that is no file, a script that would overwrite a file |
|
|
69
|
+
| `2` | A usage error: a bad flag, an unreadable or invalid inputs file, a selector of no known form, a path that names no bundle, a missing key, a refused base URL, an `mt_` value that cannot be a catalog id or names no version, a local file the inputs name that cannot be read, a value at a file input that is no file, a script that would overwrite a file |
|
|
70
70
|
| `130` | Interrupted: on Linux and macOS the process ends by the interrupt signal, which a shell reports as `130` |
|
|
71
71
|
|
|
72
72
|
Every error goes to stderr, its first line `Error: <sentence>` and the lines under it giving the details. The command words a failure from the error's typed fields rather than from an SDK message, wherever the fields carry what a person needs, so that the JavaScript and Python commands print the same lines for the same answer:
|
|
@@ -108,7 +108,7 @@ The table holds:
|
|
|
108
108
|
|
|
109
109
|
- **`base_url`**, the origin every recorded route is served on, and **`env`**, the environment every case starts from: a key, that base URL and the poll interval `0`.
|
|
110
110
|
- **`answers`**, named answers a route's exchange can refer to rather than repeat.
|
|
111
|
-
- **`placeholders`**, each with its JavaScript and Python value: `WRITER` (`@pipelex/sdk`), `NEEDS` (`Node 22.12 or later`), `RUNNER` (`npx --yes @pipelex/sdk@{{SDK_VERSION}}`), and `SDK_VERSION`, which each suite fills with its own SDK's version. They appear as `{{NAME}}` in a case's expected outputs only, and each suite substitutes the language values first, then `{{SDK_VERSION}}`.
|
|
111
|
+
- **`placeholders`**, each with its JavaScript and Python value: `WRITER` (`@pipelex/sdk`), `NEEDS` (`Node 22.12 or later`), `RUNNER` (`npx --yes --no-update-notifier --loglevel=warn @pipelex/sdk@{{SDK_VERSION}}`), and `SDK_VERSION`, which each suite fills with its own SDK's version. They appear as `{{NAME}}` in a case's expected outputs only, and each suite substitutes the language values first, then `{{SDK_VERSION}}`.
|
|
112
112
|
- **`cases`**, each with these fields:
|
|
113
113
|
- `name`, unique, and `summary`, one sentence saying what the case holds.
|
|
114
114
|
- `argv`, the arguments after the command's name.
|
package/docs/crate-routes.md
CHANGED
|
@@ -33,7 +33,7 @@ Treat that attribution as best-effort, not a guarantee — which is why `Validat
|
|
|
33
33
|
|
|
34
34
|
An **address-form** `method_ref` (`github.com/<owner>/<repo>[/<selector>][@<tag>]`) is resolved by the server through the same fetch path as a `method_ref` run: the repository fetched at the tag, the package located by manifest identity, the package's real relative paths feeding the per-file `source` labels. Any non-address reference stays reserved and answers `501`.
|
|
35
35
|
|
|
36
|
-
`method_id` is the hosted platform's selector, and it is a **pass-through**: nothing is expanded client-side — the platform resolves the id against the org's catalog and injects the stored source before the runner sees the request. It is meaningless against a bare runner (no catalog), and on `api.pipelex.com` its availability follows the platform deploy that adds the tooling-route transform. An unknown or foreign-org id is a `404` (indistinguishable by design); a stored method with no MTHDS source is a `422`.
|
|
36
|
+
`method_id` is the hosted platform's selector, and it is a **pass-through**: nothing is expanded client-side — the platform resolves the id against the org's catalog and injects the stored source before the runner sees the request. A bare `mt_…` names the method's latest published version, `mt_…@<n>` the fixed version `n` and `mt_…@draft` its draft, and the suffix rides the string untouched. It is meaningless against a bare runner (no catalog), and on `api.pipelex.com` its availability follows the platform deploy that adds the tooling-route transform. An unknown or foreign-org id is a `404` (indistinguishable by design); a bare id of a method never published is a `409` `method_not_published`, whose detail names `mt_…@draft`; a version the method never published is a `404` `method_version_not_found`; a malformed suffix is a `422`; a stored method with no MTHDS source is a `422`.
|
|
37
37
|
|
|
38
38
|
Supplying **no** selector or **more than one** is a request-shape `422` — the tooling routes are stateless, so there is no linkage exception; a second selector could only be ignored, which is the worst contract of the three. The SDK does not model the XOR in the type system — the union would force the overwhelmingly common `{ files }` call site to pick a branch for no gain, and the server's answer is a typed `ApiResponseError` either way.
|
|
39
39
|
|
|
@@ -134,9 +134,11 @@ Only a **no-verdict** condition, as the typed `ApiResponseError` — branch on i
|
|
|
134
134
|
|
|
135
135
|
| Status | Cause |
|
|
136
136
|
| ------------- | --------------------------------------------------------------------------------------------------------- |
|
|
137
|
-
| `422` | Request shape: no closure selector or more than one, an over-limit file, an unknown `kind`/`target`, a `pipe_ref` on `kind: "types"`, a fetched package with no `.mthds` file, a stored method with no MTHDS source. |
|
|
137
|
+
| `422` | Request shape: no closure selector or more than one, an over-limit file, an unknown `kind`/`target`, a `pipe_ref` on `kind: "types"`, a fetched package with no `.mthds` file, a stored method with no MTHDS source, a `method_id` whose version suffix is malformed. |
|
|
138
138
|
| `422` | On `pipeIo`, a pipe selection the route refuses: `errorType` `EntryPipeNotFoundError` for a `pipe_ref` that names no pipe or a method with no entry pipe, `EntryPipeAmbiguousError` for a code that matches pipes in several domains or several `main_pipe` declarations. The `serverMessage` names the candidates where there are some. |
|
|
139
139
|
| `404` | Unknown or foreign-org `method_id` (indistinguishable by design); no package at a `method_ref` address. |
|
|
140
|
+
| `404` | `code` `method_version_not_found`: a `mt_…@<n>` naming a version the method never published. |
|
|
141
|
+
| `409` | `code` `method_not_published`: a bare `method_id` of a method never published; `mt_…@draft` reaches its draft. |
|
|
140
142
|
| `501` | Registry-form `method_ref` — reserved, not implemented (the address form resolves). |
|
|
141
143
|
| `401` / `403` | Auth, or on a hosted origin a route the gateway does not list yet (`403 {"message":"Forbidden"}`). |
|
|
142
144
|
| `502` | On a hosted origin, a cold `method_ref` clone that outran the gateway's 30-second cap; a retry clears it. |
|