@pipelex/sdk 0.48.1 → 0.50.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 CHANGED
@@ -167,6 +167,23 @@ for await (const saved of client.iterateMethodExamples(created.method_id)) {
167
167
  // every example, oldest first
168
168
  }
169
169
 
170
+ // The organization's files, newest first: uploads, test files and the files runs made. Each row's
171
+ // `uri` goes into a run's inputs as any address does. A filtered page can be empty and still carry
172
+ // a nextCursor; iterateFiles follows it.
173
+ const files = await client.listFiles({ q: "invoice", kind: "uploaded", format: ["pdf"] });
174
+ await client.start({ method_id: created.method_id, inputs: { invoice: { url: files.items[0]!.uri } } });
175
+ // One file with its uses, a link that downloads it under its name, and a zip of a run's files.
176
+ const fileDetail = await client.getFile(files.items[0]!.file_id);
177
+ for (const link of (await client.linkFiles([{ file_id: fileDetail.file_id }])).items) {
178
+ // A fifteen-minute link, or why this file got none. Hand link.url to a browser, never a log.
179
+ if (link.error === null) console.log(`${link.name} downloadable until ${link.expires_at}`);
180
+ else console.error(link.error.code);
181
+ }
182
+ const archive = await client.requestFileArchive({ run_id: run.pipeline_run_id });
183
+ // then getFileArchive(archive.archive_id) until its state is ready or failed.
184
+ // An upload recorded as a test file of the method it was made to try.
185
+ await client.uploadFile("cases/receipt.pdf", { kind: "test", method_id: created.method_id });
186
+
170
187
  try {
171
188
  const { portal_url } = await client.getBillingPortal();
172
189
  // open portal_url ...
@@ -225,19 +242,18 @@ const client = new PipelexApiClient({ appInfo: { name: "acme-invoicer", version:
225
242
 
226
243
  ### Uploading from a browser
227
244
 
228
- A browser page that holds a file but not the API key can still store it: the server that holds the key asks for an upload grant, and the page sends the file straight to storage with it. The file never crosses your server or the API gateway, so it can be as large as the service's own limit. The page imports `@pipelex/sdk/upload`, which carries only the grant's sender, its types and its errors. Both entries bundle for the browser with nothing marked external, so a page may also import the main entry's error classes and types, while the client itself stays on the server that holds the key.
245
+ A browser page that holds a file but not the API key can still store it: the page describes the file, its SHA-256 included, the server that holds the key asks for an upload grant with that description, and the page sends the file straight to storage with it, unless the grant says your organization already holds those bytes, in which case nothing is sent at all. The file never crosses your server or the API gateway, so it can be as large as the service's own limit. The page imports `@pipelex/sdk/upload`, which carries only the description, the grant's sender, their types and their errors. Both entries bundle for the browser with nothing marked external, so a page may also import the main entry's error classes and types, while the client itself stays on the server that holds the key.
229
246
 
230
247
  ```ts
231
- // On the server, which holds the key:
232
- const grant = await client.requestUploadGrant({
233
- filename: "report.pdf",
234
- content_type: "application/pdf",
235
- size: 48213,
236
- });
237
-
238
248
  // In the page, which holds the file:
239
- import { uploadWithGrant } from "@pipelex/sdk/upload";
240
- const { uri } = await uploadWithGrant(grant, file); // pipelex-storage://…
249
+ import { describeUpload, uploadWithGrant } from "@pipelex/sdk/upload";
250
+ const description = await describeUpload(file); // { filename, content_type, size, sha256 }
251
+
252
+ // On the server, which holds the key, given the description:
253
+ const grant = await client.requestUploadGrant(description); // { stored, uri, file_id, url, … }
254
+
255
+ // Back in the page, with the grant:
256
+ const { uri, file_id } = await uploadWithGrant(grant, file); // sends nothing when grant.stored
241
257
  ```
242
258
 
243
259
  The full client surface is documented in [`docs/architecture.md`](./docs/architecture.md).
@@ -255,7 +271,9 @@ These pages ship inside the published package, so a reader who has only installe
255
271
  | [`docs/artifact-download.md`](./docs/artifact-download.md) | Turning the `pipelex-storage://` references a run produced back into bytes: `locateArtifacts`, `collectArtifacts`, `resolveArtifacts`, `fetchArtifact`, `downloadArtifacts`, and how a saved file is named after the field it fills |
256
272
  | [`docs/input-preparation.md`](./docs/input-preparation.md) | The other direction — `uploadFile` and `prepareInputs`, which turn local files into references a run can take, and the upload grant a browser page sends a file with |
257
273
  | [`docs/method-trees.md`](./docs/method-trees.md) | A method's tree: the four forms a run, a validation, a save and the crate routes take it in (a directory on disk among them), how it travels inline or by hash and what is uploaded, the files a step reads, the template check's warnings and which step reads which file of the tree, the deployment's limits, the read's listing with its links, `writeMethodTree`, and `MethodFileTransferError` |
258
- | [`docs/method-examples.md`](./docs/method-examples.md) | A method's saved examples: the create, the paged list and its iterator, the edit that raises the input revision, the latest-run write and its `recorded` or `stale` verdict, how an integer past `Number.MAX_SAFE_INTEGER` in the inputs stays exact, and what is refused before sending |
274
+ | [`docs/files.md`](./docs/files.md) | An organization's files: `listFiles` and `iterateFiles`, the row, the whole record and its provenance, the query and its repeated filters, paging through filtered pages, one file and its uses, the records of an address, the rename rule, links that name the file, a zip of files and its caps, a run's files, the copy into a method's draft, and an upload recorded as a method's test file with the refusals that come with it |
275
+ | [`docs/method-examples.md`](./docs/method-examples.md) | A method's saved examples: the create, the paged list and its iterator, the edit that raises the input revision, the latest-run write and its `recorded` or `stale` verdict, the build assistant's save and its `kept` or `created` verdict, how an integer past `Number.MAX_SAFE_INTEGER` in the inputs stays exact, and what is refused before sending |
276
+ | [`docs/organization-branding.md`](./docs/organization-branding.md) | An organization's logos and colours: `getBranding`, which every member and API key reads, `updateOrganizationBranding`, an admin's write that sets, clears or keeps each key, and how a refused logo or colour names its slot in `errors` so a settings screen shows the sentence under it |
259
277
  | [`docs/exact-integers.md`](./docs/exact-integers.md) | Which reads keep an integer past `Number.MAX_SAFE_INTEGER` exact, as a `bigint` (a run's inputs and outputs, an example's inputs), how a request body writes it back with its digits, and `parseExactJson` and `stringifyExactJson` for code showing such data as JSON text |
260
278
  | [`docs/crate-routes.md`](./docs/crate-routes.md) | `resolve`, `codegen` and `pipeIo` — the method's tree as the protocol's `files` map, with its dependencies, the normalized crate, the stamped types, and a method's I/O artifacts without a validation — and the offline `runCodegenCheck` that guards a committed tree |
261
279
  | [`docs/client-identification.md`](./docs/client-identification.md) | The `User-Agent` every API request carries, and `appInfo`, the option that puts your program's name in front of it |
@@ -15,7 +15,7 @@ import type { CommandIO } from "./io.js";
15
15
  * update notice and npm 12's run notices off the script's stderr, which carries the command's
16
16
  * own lines, while npm's warnings and errors still print.
17
17
  */
18
- export declare const SCRIPT_RUNNER = "npx --yes --no-update-notifier --loglevel=warn @pipelex/sdk@0.48.1";
18
+ export declare const SCRIPT_RUNNER = "npx --yes --no-update-notifier --loglevel=warn @pipelex/sdk@0.50.0";
19
19
  export declare const SCRIPT_WRITER = "@pipelex/sdk";
20
20
  export declare const SCRIPT_NEEDS = "Node 22.12 or later";
21
21
  /** Run `pipelex-sdk script` and return its exit code. */
package/dist/client.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import type { MTHDSProtocol, ModelCategory, ModelDeck, RunOptions, StartOptions, VersionInfo } from "mthds/protocol";
2
2
  import type { CodegenRequest, CodegenResponse, CrateRouteOptions, DictPipeOutput, FormatResponse, LintResponse, ModelCheckCategory, ModelReferenceVerdict, PipeIORequest, PipeIOResponse, PipelexRunResultStart, PipelexValidateSource, PipelexValidationResult, ResolveRequest, ResolveResponse, ValidateMethodSelector } from "./models.js";
3
3
  import { type GetRunResultOptions, type RunProgress, type RunRead, type RunResults, type RunResultState, type StartAndWaitForResultOptions, type WaitForResultOptions } from "./runs.js";
4
- import type { BillingPortalResponse, ChangePlanResponse, CheckoutResponse, InvoiceView, Membership, MembershipsResponse, ListMethodExamplesQuery, ListMethodsQuery, ListMethodVersionsQuery, MethodData, MethodDeletionAccepted, MethodDetail, MethodDraftInput, MethodExample, MethodExampleCreateInput, MethodExampleLatestRunInput, MethodExampleLatestRunResult, MethodExamplePage, MethodExampleUpdateInput, MethodPage, MethodPublishInput, MethodPublishResult, MethodRenameInput, MethodSummary, MethodVersion, MethodVersionPage, MethodWriteInput, OnboardingSubmission, PipelexApiKeyCreated, PipelexApiKeyList, ListRunsQuery, RunHistoryItem, RunDetail, RunPage, PlanView, ResolvedStorageUrl, SavedMethodFile, SubscriptionResponse, UpdateRunInput, UploadGrant, UploadGrantInput, UploadInput, UploadedFile, UserProfile } from "./product-models.js";
4
+ import type { BillingPortalResponse, ChangePlanResponse, CheckoutResponse, DraftFileFromFileInput, DraftFileFromFileResult, FileArchive, FileArchiveRequest, FileDetail, FileLinkTarget, FileLinksResult, FilePage, FileRecord, FileRecordList, FileRenameInput, FileSummary, GetFileQuery, GetFilesByUriOptions, InvoiceView, Membership, MembershipsResponse, OrganizationBranding, OrganizationBrandingUpdate, ListFilesQuery, ListMethodExamplesQuery, ListMethodsQuery, ListMethodVersionsQuery, ListRunFilesQuery, MethodData, MethodDeletionAccepted, MethodDetail, MethodDraftInput, MethodExample, MethodExampleAssistantSaveInput, MethodExampleAssistantSaveResult, MethodExampleCreateInput, MethodExampleLatestRunInput, MethodExampleLatestRunResult, MethodExamplePage, MethodExampleUpdateInput, MethodPage, MethodPublishInput, MethodPublishResult, MethodRenameInput, MethodSummary, MethodVersion, MethodVersionPage, MethodWriteInput, OnboardingSubmission, PipelexApiKeyCreated, PipelexApiKeyList, ListRunsQuery, RunHistoryItem, RunDetail, RunFilesPage, RunPage, PlanView, ResolvedStorageUrl, SavedMethodFile, SubscriptionResponse, UpdateRunInput, UploadGrant, UploadGrantInput, UploadInput, UploadedFile, UserProfile } from "./product-models.js";
5
5
  import type { MethodTreeForms, MethodTreeLimits } from "./method-trees.js";
6
6
  import type { AppInfo } from "./user-agent.js";
7
7
  import type { UploadableAsset, UploadFileOptions, UploadRecord } from "./upload.js";
@@ -279,7 +279,7 @@ export declare class PipelexApiClient implements HostedProtocolSurface {
279
279
  private readVerdict;
280
280
  /**
281
281
  * One page of a cursor-paged product route (`listMethods`, `listMethodVersions`,
282
- * `listMethodExamples`, `listRuns`), read as the platform always serializes it: `items` an
282
+ * `listMethodExamples`, `listRuns`, `listFiles`), read as the platform always serializes it: `items` an
283
283
  * array, `next_cursor` a string or `null`. A page breaking either is an answer the SDK cannot
284
284
  * read, so it throws the `ApiResponseError` `unreadableAnswer` builds, `runtime` and not
285
285
  * retryable, rather than handing the iterators a page they cannot walk: `items` that is not
@@ -1342,6 +1342,36 @@ export declare class PipelexApiClient implements HostedProtocolSurface {
1342
1342
  * `ApiResponseError` too, read as `publishMethod` reads its verdict.
1343
1343
  */
1344
1344
  recordMethodExampleLatestRun(methodId: string, exampleId: string, input: MethodExampleLatestRunInput): Promise<MethodExampleLatestRunResult>;
1345
+ /**
1346
+ * Save the build assistant's test inputs as its example of the method —
1347
+ * `POST /v1/methods/{id}/examples/assistant-save` — **never writing over a person's edit**.
1348
+ *
1349
+ * The assistant keeps one example of a method current with its test inputs, until a person
1350
+ * edits that example's inputs. Find its example as the method's newest with `kept_by_assistant`
1351
+ * true (`listMethodExamples` answers them oldest first), and send its `example_id` with the
1352
+ * `input_revision` you read it at. The platform writes the inputs into it only while it still
1353
+ * exists, still carries the flag and still holds that revision, in one conditional write that it
1354
+ * never retries. Otherwise, and when no example is named, which is how the first save on a
1355
+ * method creates the assistant's example, it saves the inputs as a new example flagged as the
1356
+ * assistant's, under `name`.
1357
+ *
1358
+ * The answer is a `MethodExampleAssistantSaveResult` discriminated on `outcome` — branch on it:
1359
+ * `kept`, the named example now holds the inputs, its name untouched; `created`, a new example
1360
+ * holds them. Both carry the `example`, which is the assistant's from then on. A retry of a save
1361
+ * that landed answers `kept` and writes nothing again.
1362
+ *
1363
+ * Sends `name`, `inputs` and, when given, `example_id` with `input_revision`, and nothing else:
1364
+ * the body is closed, and `null` reads as not given. Throws `RequestArgumentError` before any
1365
+ * request when `name` or `inputs` is not given, for an `example_id` that is not a non-empty
1366
+ * string or an `input_revision` that is not a positive integer, and when only one of the two is
1367
+ * given. Throws `ApiResponseError`: `403` `role_not_permitted` for a role that may not change
1368
+ * methods, such as a runner; `413` `payload_too_large` for an example larger than one stored item
1369
+ * holds; `422` for a name that is empty, blank or over 200 characters, or inputs that are not a
1370
+ * JSON object; and the codes every example route answers. An answer whose `outcome` is neither
1371
+ * arm's, or whose `example` is not an example `readMethodExample` can read, is an answer the SDK
1372
+ * cannot read, thrown as an `ApiResponseError` too.
1373
+ */
1374
+ saveAssistantMethodExample(methodId: string, input: MethodExampleAssistantSaveInput): Promise<MethodExampleAssistantSaveResult>;
1345
1375
  /**
1346
1376
  * Delete an example — `DELETE /v1/methods/{id}/examples/{example_id}`. The platform answers
1347
1377
  * `204`. Deleting the example leaves its latest run in the method's history.
@@ -1362,6 +1392,42 @@ export declare class PipelexApiClient implements HostedProtocolSurface {
1362
1392
  renameOrganization(orgId: string, input: {
1363
1393
  name: string;
1364
1394
  }): Promise<Membership>;
1395
+ /**
1396
+ * The branding of the caller's active organization — `GET /v1/branding`: its name, its light
1397
+ * and dark colour sets and its two logos. Any role reads it, `runner` included, by web session
1398
+ * or API key, so an app scaffolded from the method-app template reads its organization's
1399
+ * branding with its own key. A personal workspace, or a team that set nothing, answers every
1400
+ * colour and logo `null`. A logo's `uri` is a `pipelex-storage://` reference, which
1401
+ * `resolveStorageUrl` turns into a short-lived link to its bytes.
1402
+ *
1403
+ * Throws `ApiResponseError`: `400` when the caller has no active organization, `404` when its
1404
+ * row is missing, and an answer that is not a JSON object, an empty one included.
1405
+ */
1406
+ getBranding(): Promise<OrganizationBranding>;
1407
+ /**
1408
+ * Set or clear an organization's branding — `PATCH /v1/organizations/{org_id}/branding`, an
1409
+ * admin's write to the active organization. Every key of `input` is optional: a value sets it,
1410
+ * `null` clears it, and a key left out keeps its value (`OrganizationBrandingUpdate`), so `{}`
1411
+ * writes nothing. A new logo is the `pipelex-storage://` URI of a file the organization uploaded
1412
+ * on its own, which the platform reads and measures, then copies under `branding/`: the answer's
1413
+ * `uri` is that copy, and sending it back unchanged keeps the logo. The logo a write replaces or
1414
+ * clears is erased. Answers the branding as the write left it.
1415
+ *
1416
+ * Throws `RequestArgumentError` for an organization id that is empty, `.` or `..`, before any
1417
+ * request. Throws `ApiResponseError`, refusing every change of the request when it refuses one:
1418
+ * `403` `forbidden` when `orgId` is not the active organization, `403` `role_not_permitted` for
1419
+ * a caller who is not its admin, `404` for a missing organization, `409` `conflict` for a
1420
+ * personal workspace, which has no branding, and `422` `validation_failed` for a colour that is
1421
+ * not `#rrggbb` or a key the body does not know. A logo's refusal names its slot in the error's
1422
+ * `errors[]`, each entry's `field` being `square_logo` or `wide_logo` and its `detail` the
1423
+ * sentence to show under that slot: `400` `invalid_storage_uri` for a URI that is not a storage
1424
+ * URI, `403` `forbidden` for another organization's file, and `422` `validation_failed` with a
1425
+ * `LogoRefusalCode` per broken rule for a file the platform refused. A failure of the platform's
1426
+ * storage while reading or copying a logo is a `500` `storage_backend_failed`, retryable, with
1427
+ * nothing written. A `503` `service_unavailable` is a failure of its database, after which the
1428
+ * write may have landed or not: the same input sent again is safe.
1429
+ */
1430
+ updateOrganizationBranding(orgId: string, input: OrganizationBrandingUpdate): Promise<OrganizationBranding>;
1365
1431
  /** The active org's subscription state — `GET /v1/billing/subscription`. */
1366
1432
  getSubscription(): Promise<SubscriptionResponse>;
1367
1433
  /** Available plans (with `is_current`) — `GET /v1/billing/plans`. */
@@ -1448,7 +1514,8 @@ export declare class PipelexApiClient implements HostedProtocolSurface {
1448
1514
  */
1449
1515
  downloadArtifacts(request: DownloadArtifactsRequest): Promise<DownloadArtifactsResult>;
1450
1516
  /**
1451
- * Upload a base64 file — `POST /v1/upload`. An answer with no non-empty string `uri` names no
1517
+ * Upload a base64 file — `POST /v1/upload`, answering the file's `uri`, the `filename` as sent
1518
+ * and the `file_id` of its record. An answer with no non-empty string `uri` names no
1452
1519
  * stored file, so it is an answer the SDK cannot read: an `ApiResponseError`, `runtime` and
1453
1520
  * not retryable, which `uploadFile` wraps as an `UploadTransportError` with code `unexpected`.
1454
1521
  */
@@ -1456,18 +1523,24 @@ export declare class PipelexApiClient implements HostedProtocolSurface {
1456
1523
  /**
1457
1524
  * Request a grant to upload one file straight to storage — `POST /v1/upload/grant`.
1458
1525
  * `upload` for a caller that holds the bytes but not this client's credential, such
1459
- * as a browser page: the credential-holding side asks for the grant, hands it over,
1460
- * and the holder of the bytes sends them with `uploadWithGrant`, from the
1461
- * browser-safe `@pipelex/sdk/upload` entry. The bytes cross neither this client nor
1462
- * the API gateway, so the gateway's request quota does not cap the file below the
1463
- * service's own limit (`max_bytes`).
1464
- *
1465
- * The grant describes one new object: its `uri` exists once the `PUT` succeeds, and
1466
- * not before. It is create-only and short-lived, and it is a bearer capability, so
1467
- * keep it out of logs. The route never replays a grant: ask again for a new one
1468
- * rather than retrying. A declared `size` over the cap is a `413` `ApiResponseError`
1469
- * (`code` `payload_too_large`); a deployment without the route answers a `404`. See
1470
- * `docs/input-preparation.md`.
1526
+ * as a browser page: the holder of the bytes describes the file with `describeUpload`,
1527
+ * its SHA-256 included, the credential-holding side asks for the grant with that
1528
+ * description and hands the grant over, and the holder of the bytes sends them with
1529
+ * `uploadWithGrant`. Both are on the browser-safe `@pipelex/sdk/upload` entry. The
1530
+ * bytes cross neither this client nor the API gateway, so the gateway's request quota
1531
+ * does not cap the file below the service's own limit (`max_bytes`).
1532
+ *
1533
+ * **Branch on the answer's `stored`.** With the file's `sha256`, the object is named
1534
+ * after its bytes: when the organization already holds them, the grant answers
1535
+ * `stored: true` with the file's `uri` and `file_id` and no upload target, and nothing
1536
+ * is to be sent. Otherwise, and always without a `sha256`, it answers a presigned,
1537
+ * create-only `PUT` whose `uri` exists once the `PUT` succeeds, and not before. That
1538
+ * target is short-lived and a bearer capability, so keep it out of logs. The route
1539
+ * never replays a grant: ask again for a new one rather than retrying; with a
1540
+ * `sha256`, a new grant answers the same file record. A declared `size` over the cap
1541
+ * is a `413` `ApiResponseError` (`code` `payload_too_large`), a malformed `sha256` and
1542
+ * a `size` other than the one the organization holds those bytes at are a `422`, and a
1543
+ * deployment without the route answers a `404`. See `docs/input-preparation.md`.
1471
1544
  */
1472
1545
  requestUploadGrant(input: UploadGrantInput, options?: {
1473
1546
  signal?: AbortSignal;
@@ -1476,9 +1549,11 @@ export declare class PipelexApiClient implements HostedProtocolSurface {
1476
1549
  * Upload one local asset and return its {@link UploadRecord} — the single-asset
1477
1550
  * convenience over {@link upload}. Accepts `Blob`/`File`/`ArrayBuffer`/`Uint8Array`
1478
1551
  * in every runtime; a path string is Node-only (it fails instructively elsewhere).
1479
- * The record guarantees `uri`, `contentType`, `size`, and `filename`. Transport
1480
- * failures surface as the semantic input-preparation errors (rejected asset, auth,
1481
- * unsupported capability, transport). See `docs/input-preparation.md`.
1552
+ * The record guarantees `uri`, `contentType`, `size`, and `filename`. A `kind` and its
1553
+ * `method_id` in the options record the upload as a method's test file, checked before the
1554
+ * asset is read. Transport failures surface as the semantic input-preparation errors
1555
+ * (rejected asset, refused test file, auth, unsupported capability, transport). See
1556
+ * `docs/input-preparation.md`.
1482
1557
  */
1483
1558
  uploadFile(asset: UploadableAsset, options?: UploadFileOptions): Promise<UploadRecord>;
1484
1559
  /**
@@ -1511,6 +1586,231 @@ export declare class PipelexApiClient implements HostedProtocolSurface {
1511
1586
  * input too.
1512
1587
  */
1513
1588
  prepareInputs(request: PrepareInputsRequest): Promise<PreparedInputs>;
1589
+ /**
1590
+ * One page of the organization's files, newest first — `GET /v1/files`.
1591
+ *
1592
+ * Rows are `FileSummary`: the record's id, its `uri`, which a run's inputs take as they take
1593
+ * any address, the name a person sees, its type, size and kind, and where it came from. A list
1594
+ * never holds a file a run made along the way, nor an upload whose bytes have not been seen
1595
+ * stored. Every role of the organization reads every file of it.
1596
+ *
1597
+ * `methodId`, `runId` and `createdBy` choose which list serves the page: the method's files
1598
+ * (what its runs and examples took or made), the run's own files (its final outputs and inline
1599
+ * inputs, in the order of their addresses), one member's (`"me"` for the caller's own), or the
1600
+ * organization's. `q`, `kind`, `format`, `mediaType`, `createdFrom` and `createdTo` filter that
1601
+ * list server-side; `format` and `mediaType` are sent as one repeated parameter per value, and
1602
+ * an empty list of either is a `RequestArgumentError` before any request. The filters run after
1603
+ * the index read, so **a filtered page can hold fewer rows than `limit`, or none, and still
1604
+ * carry a `nextCursor`**: keep following it until it is `null`, with the same filters, since a
1605
+ * cursor is bound to the list and filters that issued it.
1606
+ *
1607
+ * Throws `ApiResponseError`: `400` for a date bound that is not an instant with an offset, a
1608
+ * range whose start is after its end, or a cursor of another list or other filters; `404`
1609
+ * `run_not_found` for a run that is not the organization's; `422` for a `limit` outside 1 to
1610
+ * 200, an unknown `kind`, more than 100 values of `format` or `mediaType`, or an id that cannot
1611
+ * be one. A page whose `items` is not an array or whose `next_cursor` is neither a string nor
1612
+ * `null` is an `ApiResponseError` of an answer the SDK cannot read. See `docs/files.md`.
1613
+ */
1614
+ listFiles(query?: ListFilesQuery): Promise<FilePage>;
1615
+ /**
1616
+ * Every file of the organization the query selects, streamed — follows the cursor for you.
1617
+ *
1618
+ * ```ts
1619
+ * for await (const file of client.iterateFiles({ kind: "uploaded", format: ["pdf"] })) {
1620
+ * if (file.name === "March invoice.pdf") break; // stop whenever you like
1621
+ * }
1622
+ * ```
1623
+ *
1624
+ * **Prefer `listFiles`** for anything user-facing: this makes as many round trips as the data
1625
+ * demands. It is an iterator rather than an all-at-once helper for the reason `iterateRuns` is
1626
+ * one: a helper returning an array needs a page cap, and a cap returns a truncated list with no
1627
+ * error.
1628
+ *
1629
+ * It keeps going **through an empty page that carries a cursor**, which under any filter means
1630
+ * "nothing matched in the slice just read", not "done", sending the same filters with every
1631
+ * page. It stops on a `null` cursor or on a cursor the server failed to advance, and throws
1632
+ * `PagingNotTerminatingError` past a page ceiling no real file list reaches.
1633
+ */
1634
+ iterateFiles(query?: Omit<ListFilesQuery, "cursor">): AsyncGenerator<FileSummary, void, undefined>;
1635
+ /**
1636
+ * One file with its provenance, the methods whose lists hold it, and one page of its uses —
1637
+ * `GET /v1/files/{file_id}`.
1638
+ *
1639
+ * The answer is a `FileDetail`: the file's whole record (`FileRecord`), its `method_ids`, and
1640
+ * `uses`, one page of the examples that hold its address, then the runs that took it. Page the
1641
+ * uses by passing `uses.next_cursor` back as `usesCursor`; `null` is the last page. A record whose
1642
+ * upload looked unfinished past the upload grant's lifespan is settled by the read, so it answers
1643
+ * `stored` when its bytes are there.
1644
+ *
1645
+ * Throws `RequestArgumentError` for a file id that is empty, `.` or `..`, or holds a lone
1646
+ * surrogate, before any request. Throws `ApiResponseError`: `404` `not_found` for a file that is
1647
+ * not the organization's; `422` for a `usesLimit` outside 1 to 200 or an id that cannot be one.
1648
+ * An answer that is not an object, or whose `uses` is not a page of `items` and `next_cursor`, is
1649
+ * an answer the SDK cannot read, thrown as an `ApiResponseError` too. See `docs/files.md`.
1650
+ */
1651
+ getFile(fileId: string, query?: GetFileQuery): Promise<FileDetail>;
1652
+ /**
1653
+ * The records holding one `pipelex-storage://` address — `GET /v1/files/by-uri` — in the order a
1654
+ * surface picks the one to show, the first being it.
1655
+ *
1656
+ * Several records hold one address when several members uploaded the same bytes. With `runId`,
1657
+ * the records of the member who started that run come first, so a run's inputs show the name its
1658
+ * starter gave the file; then the caller's; then the rest, each group oldest first. A record
1659
+ * whose upload has not finished is answered with its `state`, `pending`. An address no record
1660
+ * holds answers an empty `items`, which is how a client tells a file stored before the platform
1661
+ * kept records.
1662
+ *
1663
+ * Throws `RequestArgumentError` for a `uri` that is not a non-empty string, before any request.
1664
+ * Throws `ApiResponseError`: `400` `invalid_storage_uri` for a malformed address, `403`
1665
+ * `forbidden` for another organization's, `404` `run_not_found` for a run that is not the
1666
+ * organization's, and `422` for an address over 512 characters. An answer whose `items` is not an
1667
+ * array is an answer the SDK cannot read, thrown as an `ApiResponseError` too.
1668
+ */
1669
+ getFilesByUri(uri: string, options?: GetFilesByUriOptions): Promise<FileRecordList>;
1670
+ /**
1671
+ * Rename a file — `PATCH /v1/files/{file_id}` — and return its record as it now stands.
1672
+ *
1673
+ * **The platform holds the name to one rule, for every client**: it removes the characters no
1674
+ * list or header can show (control and format characters, a right-to-left override among
1675
+ * them), trims the ends, refuses a name with nothing left, and adds the file's own extension,
1676
+ * the one its `original_name` ends with, when the name does not end with it, case aside:
1677
+ * `March invoice` becomes `March invoice.pdf`, and `March.docx` becomes `March.docx.pdf`. A file
1678
+ * whose original name has no extension gains none. Read the saved name from the answer's
1679
+ * `name`. The rename changes `name` alone, never the file's bytes, its address or its
1680
+ * `original_name`.
1681
+ *
1682
+ * An `admin` or a `member` renames any file; any other role, a runner among them, only the files
1683
+ * whose record names it as `created_by`: its own uploads and the outputs of the runs it started.
1684
+ *
1685
+ * Sends `name` alone: the body is closed. Throws `RequestArgumentError` for a file id that is
1686
+ * empty, `.` or `..`, or holds a lone surrogate, and for a `name` that is not a string, before
1687
+ * any request. Throws `ApiResponseError`: `403` `role_not_permitted` past the rule above; `404`
1688
+ * `not_found` for a file that is not the organization's; `422` for a name over 512 characters or
1689
+ * with nothing left once cleaned. An answer that is not an object is an answer the SDK cannot
1690
+ * read, thrown as an `ApiResponseError` too.
1691
+ */
1692
+ renameFile(fileId: string, input: FileRenameInput): Promise<FileRecord>;
1693
+ /**
1694
+ * Fresh links to read files — `POST /v1/files/links` — one per requested file, in the request's
1695
+ * order, each named by exactly one of its record's id (`file_id`) and its address (`uri`).
1696
+ *
1697
+ * **Branch on each item's `error`, never on the status**: the request is a `200` whenever every
1698
+ * file got a verdict. A linked file carries its `url`, a presigned `GET` valid for fifteen
1699
+ * minutes, with its `expires_at`, the `name` the download saves under and its `media_type`, and
1700
+ * a `null` `error`. A file that got no link carries `error`, `{ code, detail }`, and `null` link
1701
+ * members: `not_found` for an unknown file id, `conflict` for a file whose upload has not
1702
+ * finished, `invalid_storage_uri` for a malformed address and `forbidden` for another
1703
+ * organization's.
1704
+ *
1705
+ * Each link is signed with a `Content-Disposition` naming the file, so a download saves under
1706
+ * the name a person sees rather than the bytes' hash. It opens in place (`inline`) for an image,
1707
+ * a PDF or plain text and downloads (`attachment`) for every other type, SVG, HTML and XML
1708
+ * included, so a file that could run scripts never opens from a link. A file named by its id is
1709
+ * linked under its record's name; an address under the name of its first stored record, the
1710
+ * caller's before another member's, whose id the item's `file_id` names. A link is a bearer
1711
+ * capability: keep it out of logs, and ask again for a fresh one rather than storing it.
1712
+ *
1713
+ * Sends each file as `{ file_id }` or `{ uri }` and nothing else: the body is closed. Throws
1714
+ * `RequestArgumentError` before any request when `files` is not a list, or one of its items does
1715
+ * not name exactly one of `file_id` and `uri` as a non-empty string. Throws `ApiResponseError`:
1716
+ * `422` for more than 100 files, or an id or address longer than one can be; `500` `config` when
1717
+ * the platform cannot sign at all. An answer that is not one item per requested file, each an
1718
+ * object with its `url` or its `error`, is an answer the SDK cannot read, thrown as an
1719
+ * `ApiResponseError` whose body hides every link.
1720
+ */
1721
+ linkFiles(files: readonly FileLinkTarget[]): Promise<FileLinksResult>;
1722
+ /**
1723
+ * Ask for one zip of chosen files, or of a run's files — `POST /v1/files/archives` — and return
1724
+ * the archive, `queued`.
1725
+ *
1726
+ * The platform cannot build a zip inside a request, so the zip is two calls: this one queues the
1727
+ * archive and answers at once (`202`), and `getFileArchive` reads it until its `state` is `ready`,
1728
+ * whose `url` downloads the zip, or `failed`, whose `failure` says why. Name the files by exactly
1729
+ * one of `file_ids`, in the order the zip holds them, and `run_id`, whose zip holds what went in
1730
+ * and what came out of the run, never a file it made along the way nor an input given as a web
1731
+ * link. `name` is the name the zip downloads under (see `FileArchiveRequest`). An archive is never
1732
+ * a file: no list shows it, and it is gone a day after it was asked for.
1733
+ *
1734
+ * Sends `file_ids` or `run_id`, and `name` when given, and nothing else: the body is closed, and a
1735
+ * `null` member reads as not given. Throws `RequestArgumentError` before any request when the
1736
+ * request names both or neither of `file_ids` and `run_id`, when `file_ids` is not a non-empty
1737
+ * list of non-empty strings, when `run_id` is not a non-empty string, and when `name` is not a
1738
+ * string. Throws `ApiResponseError`:
1739
+ *
1740
+ * - `422` `file_archive_too_large` past a cap, at most 500 files or 2,000,000,000 bytes, whose
1741
+ * `problemDocument.limit` names it (`FileArchiveLimit`), so the person can be told how far over
1742
+ * the selection is;
1743
+ * - `422` `validation_failed` for more than 1,000 ids listed, or an id longer than one can be;
1744
+ * - `404` `not_found` for a file that is not the organization's, the `detail` naming up to five
1745
+ * of them, or a run whose files hold nothing stored to zip; `404` `run_not_found` for a run
1746
+ * that is not the organization's;
1747
+ * - `409` `conflict`: an `input` one, not retryable, for a selected file whose upload has not
1748
+ * finished; a `runtime` one, retryable, for a run whose files are not all recorded yet, which
1749
+ * the same request clears a few seconds after the run ends;
1750
+ * - `503` `service_unavailable`, retryable, when the archive could not be queued: the same request
1751
+ * sent again asks for a new archive.
1752
+ *
1753
+ * An answer that is not an archive, an object with a string `archive_id` and a known `state`, is
1754
+ * an answer the SDK cannot read, thrown as an `ApiResponseError` too.
1755
+ */
1756
+ requestFileArchive(request: FileArchiveRequest): Promise<FileArchive>;
1757
+ /**
1758
+ * Where an archive stands — `GET /v1/files/archives/{archive_id}` — with a fresh link to its zip
1759
+ * once it is `ready`.
1760
+ *
1761
+ * **Branch on `state`.** `queued` and `building` mean read it again shortly: a zip of a few files
1762
+ * is ready within seconds, and the largest the caps allow within a few minutes. `ready` carries
1763
+ * `url`, a presigned `GET` of the zip valid for fifteen minutes and signed with a
1764
+ * `Content-Disposition` naming the archive's `name`, with `archive_bytes` and `ready_at`; each read
1765
+ * signs a fresh link, so a caller that let one lapse reads the archive again. `failed` carries
1766
+ * `failure`, `file_gone` or `build_failed`, and the remedy for both is a new request. An archive
1767
+ * still `queued` or `building` an hour after it was asked for reads as `failed`.
1768
+ *
1769
+ * Throws `RequestArgumentError` for an archive id that is empty, `.` or `..`, or holds a lone
1770
+ * surrogate, before any request. Throws `ApiResponseError`: `404` `not_found` for an archive that
1771
+ * is not the organization's, never existed, or has expired, a day after `created_at`; `500` when
1772
+ * the link cannot be signed. An answer that is not an archive is an answer the SDK cannot read,
1773
+ * thrown as an `ApiResponseError` whose body hides the link.
1774
+ */
1775
+ getFileArchive(archiveId: string): Promise<FileArchive>;
1776
+ /**
1777
+ * An archive as `res` carried it, checked to be one: an object with a string `archive_id` and a
1778
+ * `state` this SDK knows, the field a caller branches on. Anything else is an answer the SDK
1779
+ * cannot read, thrown as the `ApiResponseError` `unreadableAnswer` builds with every link hidden,
1780
+ * since a ready archive's answer carries one.
1781
+ */
1782
+ private readFileArchive;
1783
+ /**
1784
+ * Copy one of the organization's files into a method's draft —
1785
+ * `POST /v1/methods/{id}/draft/files/from-file`, "Add to method files".
1786
+ *
1787
+ * The file becomes a file of the draft at `path`, replacing a file already there. Its bytes are
1788
+ * copied into the organization's blob store by the storage service itself, so they never cross
1789
+ * the client or the API whatever their size, and are skipped when the store already holds them.
1790
+ * The method file then lives apart from its source: erasing the file's record, the run that made
1791
+ * it or the method it came from leaves it readable.
1792
+ *
1793
+ * The answer is a `DraftFileFromFileResult`, a `200` discriminated on `blob`: `copied`, or
1794
+ * `already_held` when the store held the bytes and nothing was copied. Both carry the `file` as
1795
+ * the draft's tree now names it and the `method` as the draft write stored it, whose
1796
+ * `updated_at` is the new draft token. With `expected_updated_at`, the write is a
1797
+ * compare-and-swap on the draft token; without it, the file is added to the draft as it stands,
1798
+ * never erasing someone else's save.
1799
+ *
1800
+ * Takes a bare catalog id, as `writeDraft` does. Sends `file_id`, `path` and, when given,
1801
+ * `expected_updated_at`, and nothing else: the body is closed, and a `null` token reads as not
1802
+ * given. Throws `RequestArgumentError` before any request for a `file_id` or a `path` that is not
1803
+ * a non-empty string, and an `expected_updated_at` that is not a string. Throws
1804
+ * `ApiResponseError`: `403` `role_not_permitted` for a role that may not change methods; `404`
1805
+ * for an unknown method or a file that is not the organization's; `409` `method_being_deleted`,
1806
+ * `409` `method_update_conflict` for a stale `expected_updated_at`, and `409` `conflict` for a
1807
+ * file whose upload has not finished or whose stored bytes do not match its record; `422`
1808
+ * `invalid_method_files` for a path the draft cannot hold, such as `.env` or `../logo.png`, and
1809
+ * `422` `method_tree_too_large` past the deployment's limits. An answer whose `blob` is neither
1810
+ * arm's, or whose `file` or `method` is not an object, is an answer the SDK cannot read, thrown
1811
+ * as an `ApiResponseError` too.
1812
+ */
1813
+ copyFileIntoDraft(methodId: string, input: DraftFileFromFileInput): Promise<DraftFileFromFileResult>;
1514
1814
  /**
1515
1815
  * One PAGE of a method's runs, newest first — `GET /v1/runs?method_id=`.
1516
1816
  *
@@ -1589,6 +1889,29 @@ export declare class PipelexApiClient implements HostedProtocolSurface {
1589
1889
  * example's go back as the run used them.
1590
1890
  */
1591
1891
  getRunDetail(runId: string): Promise<RunDetail>;
1892
+ /**
1893
+ * One page of a run's files, sorted by where they sit in the run — `GET /v1/runs/{id}/files`.
1894
+ *
1895
+ * Each entry carries its `mark`, `input`, `final` or `intermediate`, and the record a surface
1896
+ * shows for it (`RunFileEntry`): `inputs` are what went in, the inputs given by their address
1897
+ * with the record of the member who started the run first, then the inline inputs; `outputs` are
1898
+ * the run's final outputs; `intermediates` the files it made along the way. An input given as a
1899
+ * web link gets no record and is listed in `links`, with the warning that replaying the run needs
1900
+ * it to still serve the same file.
1901
+ *
1902
+ * **`state` is `listing` until the platform has recorded every file of the run**, a few seconds
1903
+ * after it ends: read it again until it is `listed` before taking the list as whole. The run's
1904
+ * own records are paged by address with `limit` and `cursor`; the inputs by address and the links
1905
+ * come on the first page only. Pass `nextCursor` back as `cursor`; `null` is the last page.
1906
+ *
1907
+ * Throws `RequestArgumentError` for a run id that is empty, `.` or `..`, or holds a lone
1908
+ * surrogate, before any request. Throws `ApiResponseError`: `404` for a run that is not the
1909
+ * organization's, exactly as for one that does not exist; `400` for a run id holding a character
1910
+ * outside letters, digits, `_` and `-`, or a cursor another listing issued; `422` for a `limit`
1911
+ * outside 1 to 200. A page whose `state` is unknown, whose lists are not arrays, or whose `next_cursor` is neither a
1912
+ * string nor `null`, is an answer the SDK cannot read, thrown as an `ApiResponseError` too.
1913
+ */
1914
+ listRunFiles(runId: string, query?: ListRunFilesQuery): Promise<RunFilesPage>;
1592
1915
  /** Patch a run's status (admin/manual) — `PUT /v1/runs/{id}`. */
1593
1916
  updateRun(runId: string, input: UpdateRunInput): Promise<void>;
1594
1917
  /**