@pipelex/sdk 0.48.1 → 0.49.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 +10 -0
- package/dist/cli/script.d.ts +1 -1
- package/dist/client.d.ts +89 -5
- package/dist/client.js +131 -7
- package/dist/client.js.map +1 -1
- package/dist/errors.d.ts +91 -6
- package/dist/errors.js +48 -6
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/prepare-inputs.d.ts +12 -1
- package/dist/prepare-inputs.js +26 -3
- package/dist/prepare-inputs.js.map +1 -1
- package/dist/product-models.d.ts +219 -2
- package/dist/product-models.js +2 -2
- package/dist/upload.d.ts +39 -11
- package/dist/upload.js +115 -8
- package/dist/upload.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/docs/architecture.md +3 -1
- package/docs/errors.md +6 -1
- package/docs/files.md +88 -0
- package/docs/input-preparation.md +14 -4
- package/docs/organization-branding.md +102 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -167,6 +167,14 @@ 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
|
+
// An upload recorded as a test file of the method it was made to try.
|
|
176
|
+
await client.uploadFile("cases/receipt.pdf", { kind: "test", method_id: created.method_id });
|
|
177
|
+
|
|
170
178
|
try {
|
|
171
179
|
const { portal_url } = await client.getBillingPortal();
|
|
172
180
|
// open portal_url ...
|
|
@@ -255,7 +263,9 @@ These pages ship inside the published package, so a reader who has only installe
|
|
|
255
263
|
| [`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
264
|
| [`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
265
|
| [`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` |
|
|
266
|
+
| [`docs/files.md`](./docs/files.md) | An organization's files: `listFiles` and `iterateFiles`, the row and its provenance, the query and its repeated filters, paging through filtered pages, and an upload recorded as a method's test file with the refusals that come with it |
|
|
258
267
|
| [`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 |
|
|
268
|
+
| [`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
269
|
| [`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
270
|
| [`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
271
|
| [`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 |
|
package/dist/cli/script.d.ts
CHANGED
|
@@ -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.
|
|
18
|
+
export declare const SCRIPT_RUNNER = "npx --yes --no-update-notifier --loglevel=warn @pipelex/sdk@0.49.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, FilePage, FileSummary, InvoiceView, Membership, MembershipsResponse, OrganizationBranding, OrganizationBrandingUpdate, ListFilesQuery, 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";
|
|
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
|
|
@@ -1362,6 +1362,42 @@ export declare class PipelexApiClient implements HostedProtocolSurface {
|
|
|
1362
1362
|
renameOrganization(orgId: string, input: {
|
|
1363
1363
|
name: string;
|
|
1364
1364
|
}): Promise<Membership>;
|
|
1365
|
+
/**
|
|
1366
|
+
* The branding of the caller's active organization — `GET /v1/branding`: its name, its light
|
|
1367
|
+
* and dark colour sets and its two logos. Any role reads it, `runner` included, by web session
|
|
1368
|
+
* or API key, so an app scaffolded from the method-app template reads its organization's
|
|
1369
|
+
* branding with its own key. A personal workspace, or a team that set nothing, answers every
|
|
1370
|
+
* colour and logo `null`. A logo's `uri` is a `pipelex-storage://` reference, which
|
|
1371
|
+
* `resolveStorageUrl` turns into a short-lived link to its bytes.
|
|
1372
|
+
*
|
|
1373
|
+
* Throws `ApiResponseError`: `400` when the caller has no active organization, `404` when its
|
|
1374
|
+
* row is missing, and an answer that is not a JSON object, an empty one included.
|
|
1375
|
+
*/
|
|
1376
|
+
getBranding(): Promise<OrganizationBranding>;
|
|
1377
|
+
/**
|
|
1378
|
+
* Set or clear an organization's branding — `PATCH /v1/organizations/{org_id}/branding`, an
|
|
1379
|
+
* admin's write to the active organization. Every key of `input` is optional: a value sets it,
|
|
1380
|
+
* `null` clears it, and a key left out keeps its value (`OrganizationBrandingUpdate`), so `{}`
|
|
1381
|
+
* writes nothing. A new logo is the `pipelex-storage://` URI of a file the organization uploaded
|
|
1382
|
+
* on its own, which the platform reads and measures, then copies under `branding/`: the answer's
|
|
1383
|
+
* `uri` is that copy, and sending it back unchanged keeps the logo. The logo a write replaces or
|
|
1384
|
+
* clears is erased. Answers the branding as the write left it.
|
|
1385
|
+
*
|
|
1386
|
+
* Throws `RequestArgumentError` for an organization id that is empty, `.` or `..`, before any
|
|
1387
|
+
* request. Throws `ApiResponseError`, refusing every change of the request when it refuses one:
|
|
1388
|
+
* `403` `forbidden` when `orgId` is not the active organization, `403` `role_not_permitted` for
|
|
1389
|
+
* a caller who is not its admin, `404` for a missing organization, `409` `conflict` for a
|
|
1390
|
+
* personal workspace, which has no branding, and `422` `validation_failed` for a colour that is
|
|
1391
|
+
* not `#rrggbb` or a key the body does not know. A logo's refusal names its slot in the error's
|
|
1392
|
+
* `errors[]`, each entry's `field` being `square_logo` or `wide_logo` and its `detail` the
|
|
1393
|
+
* sentence to show under that slot: `400` `invalid_storage_uri` for a URI that is not a storage
|
|
1394
|
+
* URI, `403` `forbidden` for another organization's file, and `422` `validation_failed` with a
|
|
1395
|
+
* `LogoRefusalCode` per broken rule for a file the platform refused. A failure of the platform's
|
|
1396
|
+
* storage while reading or copying a logo is a `500` `storage_backend_failed`, retryable, with
|
|
1397
|
+
* nothing written. A `503` `service_unavailable` is a failure of its database, after which the
|
|
1398
|
+
* write may have landed or not: the same input sent again is safe.
|
|
1399
|
+
*/
|
|
1400
|
+
updateOrganizationBranding(orgId: string, input: OrganizationBrandingUpdate): Promise<OrganizationBranding>;
|
|
1365
1401
|
/** The active org's subscription state — `GET /v1/billing/subscription`. */
|
|
1366
1402
|
getSubscription(): Promise<SubscriptionResponse>;
|
|
1367
1403
|
/** Available plans (with `is_current`) — `GET /v1/billing/plans`. */
|
|
@@ -1476,9 +1512,11 @@ export declare class PipelexApiClient implements HostedProtocolSurface {
|
|
|
1476
1512
|
* Upload one local asset and return its {@link UploadRecord} — the single-asset
|
|
1477
1513
|
* convenience over {@link upload}. Accepts `Blob`/`File`/`ArrayBuffer`/`Uint8Array`
|
|
1478
1514
|
* in every runtime; a path string is Node-only (it fails instructively elsewhere).
|
|
1479
|
-
* The record guarantees `uri`, `contentType`, `size`, and `filename`.
|
|
1480
|
-
*
|
|
1481
|
-
*
|
|
1515
|
+
* The record guarantees `uri`, `contentType`, `size`, and `filename`. A `kind` and its
|
|
1516
|
+
* `method_id` in the options record the upload as a method's test file, checked before the
|
|
1517
|
+
* asset is read. Transport failures surface as the semantic input-preparation errors
|
|
1518
|
+
* (rejected asset, refused test file, auth, unsupported capability, transport). See
|
|
1519
|
+
* `docs/input-preparation.md`.
|
|
1482
1520
|
*/
|
|
1483
1521
|
uploadFile(asset: UploadableAsset, options?: UploadFileOptions): Promise<UploadRecord>;
|
|
1484
1522
|
/**
|
|
@@ -1511,6 +1549,52 @@ export declare class PipelexApiClient implements HostedProtocolSurface {
|
|
|
1511
1549
|
* input too.
|
|
1512
1550
|
*/
|
|
1513
1551
|
prepareInputs(request: PrepareInputsRequest): Promise<PreparedInputs>;
|
|
1552
|
+
/**
|
|
1553
|
+
* One page of the organization's files, newest first — `GET /v1/files`.
|
|
1554
|
+
*
|
|
1555
|
+
* Rows are `FileSummary`: the record's id, its `uri`, which a run's inputs take as they take
|
|
1556
|
+
* any address, the name a person sees, its type, size and kind, and where it came from. A list
|
|
1557
|
+
* never holds a file a run made along the way, nor an upload whose bytes have not been seen
|
|
1558
|
+
* stored. Every role of the organization reads every file of it.
|
|
1559
|
+
*
|
|
1560
|
+
* `methodId`, `runId` and `createdBy` choose which list serves the page: the method's files
|
|
1561
|
+
* (what its runs and examples took or made), the run's own files (its final outputs and inline
|
|
1562
|
+
* inputs, in the order of their addresses), one member's (`"me"` for the caller's own), or the
|
|
1563
|
+
* organization's. `q`, `kind`, `format`, `mediaType`, `createdFrom` and `createdTo` filter that
|
|
1564
|
+
* list server-side; `format` and `mediaType` are sent as one repeated parameter per value, and
|
|
1565
|
+
* an empty list of either is a `RequestArgumentError` before any request. The filters run after
|
|
1566
|
+
* the index read, so **a filtered page can hold fewer rows than `limit`, or none, and still
|
|
1567
|
+
* carry a `nextCursor`**: keep following it until it is `null`, with the same filters, since a
|
|
1568
|
+
* cursor is bound to the list and filters that issued it.
|
|
1569
|
+
*
|
|
1570
|
+
* Throws `ApiResponseError`: `400` for a date bound that is not an instant with an offset, a
|
|
1571
|
+
* range whose start is after its end, or a cursor of another list or other filters; `404`
|
|
1572
|
+
* `run_not_found` for a run that is not the organization's; `422` for a `limit` outside 1 to
|
|
1573
|
+
* 200, an unknown `kind`, more than 100 values of `format` or `mediaType`, or an id that cannot
|
|
1574
|
+
* be one. A page whose `items` is not an array or whose `next_cursor` is neither a string nor
|
|
1575
|
+
* `null` is an `ApiResponseError` of an answer the SDK cannot read. See `docs/files.md`.
|
|
1576
|
+
*/
|
|
1577
|
+
listFiles(query?: ListFilesQuery): Promise<FilePage>;
|
|
1578
|
+
/**
|
|
1579
|
+
* Every file of the organization the query selects, streamed — follows the cursor for you.
|
|
1580
|
+
*
|
|
1581
|
+
* ```ts
|
|
1582
|
+
* for await (const file of client.iterateFiles({ kind: "uploaded", format: ["pdf"] })) {
|
|
1583
|
+
* if (file.name === "March invoice.pdf") break; // stop whenever you like
|
|
1584
|
+
* }
|
|
1585
|
+
* ```
|
|
1586
|
+
*
|
|
1587
|
+
* **Prefer `listFiles`** for anything user-facing: this makes as many round trips as the data
|
|
1588
|
+
* demands. It is an iterator rather than an all-at-once helper for the reason `iterateRuns` is
|
|
1589
|
+
* one: a helper returning an array needs a page cap, and a cap returns a truncated list with no
|
|
1590
|
+
* error.
|
|
1591
|
+
*
|
|
1592
|
+
* It keeps going **through an empty page that carries a cursor**, which under any filter means
|
|
1593
|
+
* "nothing matched in the slice just read", not "done", sending the same filters with every
|
|
1594
|
+
* page. It stops on a `null` cursor or on a cursor the server failed to advance, and throws
|
|
1595
|
+
* `PagingNotTerminatingError` past a page ceiling no real file list reaches.
|
|
1596
|
+
*/
|
|
1597
|
+
iterateFiles(query?: Omit<ListFilesQuery, "cursor">): AsyncGenerator<FileSummary, void, undefined>;
|
|
1514
1598
|
/**
|
|
1515
1599
|
* One PAGE of a method's runs, newest first — `GET /v1/runs?method_id=`.
|
|
1516
1600
|
*
|
package/dist/client.js
CHANGED
|
@@ -14,7 +14,7 @@ import { PipelexExecuteResult, resultsFromExecute } from "./execute-result.js";
|
|
|
14
14
|
import { ExactJsonUnsupportedError, parseExactJson, stringifyExactJson } from "./exact-json.js";
|
|
15
15
|
import { MAX_TIMER_DELAY_MS, isTimerDelay } from "./timers.js";
|
|
16
16
|
// A pure RUNAWAY guard on the paged-list iterators (`iterateMethods`,
|
|
17
|
-
// `iterateRuns`, `iterateMethodExamples`), deliberately not a coverage limit.
|
|
17
|
+
// `iterateRuns`, `iterateMethodExamples`, `iterateFiles`), deliberately not a coverage limit.
|
|
18
18
|
//
|
|
19
19
|
// It counts TOTAL pages, not empty ones. An earlier version capped consecutive
|
|
20
20
|
// empty pages, which is the wrong axis: an empty page whose cursor ADVANCED is
|
|
@@ -31,8 +31,8 @@ import { MAX_TIMER_DELAY_MS, isTimerDelay } from "./timers.js";
|
|
|
31
31
|
const MAX_PAGES = 10_000;
|
|
32
32
|
/**
|
|
33
33
|
* Follow a paged list's cursor until the server says there is no more, yielding every row —
|
|
34
|
-
* the one loop behind `iterateMethods`, `iterateRuns
|
|
35
|
-
* drift apart.
|
|
34
|
+
* the one loop behind `iterateMethods`, `iterateRuns`, `iterateMethodExamples` and
|
|
35
|
+
* `iterateFiles`, so they cannot drift apart.
|
|
36
36
|
*
|
|
37
37
|
* It yields lazily: a caller that breaks early stops the requests. It stops when `nextCursor`
|
|
38
38
|
* is `null`, the normal end, and when the server hands back the cursor it was just sent, which
|
|
@@ -186,6 +186,24 @@ function productQuery(params) {
|
|
|
186
186
|
const text = search.toString();
|
|
187
187
|
return text ? `?${text}` : "";
|
|
188
188
|
}
|
|
189
|
+
/**
|
|
190
|
+
* A list filter sent as one repeated query parameter per value, such as `listFiles`' `format`:
|
|
191
|
+
* `undefined` when omitted, the values as given otherwise. An empty list names no value, and sent
|
|
192
|
+
* as no parameter at all it would read as no filter and list everything, so it is a
|
|
193
|
+
* `RequestArgumentError` before any request; a value that is not a list of strings is a
|
|
194
|
+
* `TypeError`, a bug in the calling code.
|
|
195
|
+
*/
|
|
196
|
+
function repeatedFilter(call, name, values) {
|
|
197
|
+
if (values === undefined)
|
|
198
|
+
return undefined;
|
|
199
|
+
if (!Array.isArray(values) || values.some((value) => typeof value !== "string")) {
|
|
200
|
+
throw new TypeError(`${call}() takes "${name}" as a list of strings.`);
|
|
201
|
+
}
|
|
202
|
+
if (values.length === 0) {
|
|
203
|
+
throw new RequestArgumentError(`${call}() takes "${name}" as one or more values; omit it to list every ${name}.`);
|
|
204
|
+
}
|
|
205
|
+
return values;
|
|
206
|
+
}
|
|
189
207
|
/** `items` joined as a sentence names them, the last after `conjunction`: `a`, `a or b`, `a, b or c`. */
|
|
190
208
|
function inWords(items, conjunction) {
|
|
191
209
|
if (items.length < 2)
|
|
@@ -552,7 +570,7 @@ export class PipelexApiClient {
|
|
|
552
570
|
}
|
|
553
571
|
/**
|
|
554
572
|
* One page of a cursor-paged product route (`listMethods`, `listMethodVersions`,
|
|
555
|
-
* `listMethodExamples`, `listRuns`), read as the platform always serializes it: `items` an
|
|
573
|
+
* `listMethodExamples`, `listRuns`, `listFiles`), read as the platform always serializes it: `items` an
|
|
556
574
|
* array, `next_cursor` a string or `null`. A page breaking either is an answer the SDK cannot
|
|
557
575
|
* read, so it throws the `ApiResponseError` `unreadableAnswer` builds, `runtime` and not
|
|
558
576
|
* retryable, rather than handing the iterators a page they cannot walk: `items` that is not
|
|
@@ -2499,6 +2517,47 @@ export class PipelexApiClient {
|
|
|
2499
2517
|
const segment = idPathSegment(orgId, "renameOrganization", ORG_ID_ARGUMENT);
|
|
2500
2518
|
return this.requestProduct("PATCH", `organizations/${segment}`, input);
|
|
2501
2519
|
}
|
|
2520
|
+
/**
|
|
2521
|
+
* The branding of the caller's active organization — `GET /v1/branding`: its name, its light
|
|
2522
|
+
* and dark colour sets and its two logos. Any role reads it, `runner` included, by web session
|
|
2523
|
+
* or API key, so an app scaffolded from the method-app template reads its organization's
|
|
2524
|
+
* branding with its own key. A personal workspace, or a team that set nothing, answers every
|
|
2525
|
+
* colour and logo `null`. A logo's `uri` is a `pipelex-storage://` reference, which
|
|
2526
|
+
* `resolveStorageUrl` turns into a short-lived link to its bytes.
|
|
2527
|
+
*
|
|
2528
|
+
* Throws `ApiResponseError`: `400` when the caller has no active organization, `404` when its
|
|
2529
|
+
* row is missing, and an answer that is not a JSON object, an empty one included.
|
|
2530
|
+
*/
|
|
2531
|
+
async getBranding() {
|
|
2532
|
+
return this.requestProductObject("GET", "branding");
|
|
2533
|
+
}
|
|
2534
|
+
/**
|
|
2535
|
+
* Set or clear an organization's branding — `PATCH /v1/organizations/{org_id}/branding`, an
|
|
2536
|
+
* admin's write to the active organization. Every key of `input` is optional: a value sets it,
|
|
2537
|
+
* `null` clears it, and a key left out keeps its value (`OrganizationBrandingUpdate`), so `{}`
|
|
2538
|
+
* writes nothing. A new logo is the `pipelex-storage://` URI of a file the organization uploaded
|
|
2539
|
+
* on its own, which the platform reads and measures, then copies under `branding/`: the answer's
|
|
2540
|
+
* `uri` is that copy, and sending it back unchanged keeps the logo. The logo a write replaces or
|
|
2541
|
+
* clears is erased. Answers the branding as the write left it.
|
|
2542
|
+
*
|
|
2543
|
+
* Throws `RequestArgumentError` for an organization id that is empty, `.` or `..`, before any
|
|
2544
|
+
* request. Throws `ApiResponseError`, refusing every change of the request when it refuses one:
|
|
2545
|
+
* `403` `forbidden` when `orgId` is not the active organization, `403` `role_not_permitted` for
|
|
2546
|
+
* a caller who is not its admin, `404` for a missing organization, `409` `conflict` for a
|
|
2547
|
+
* personal workspace, which has no branding, and `422` `validation_failed` for a colour that is
|
|
2548
|
+
* not `#rrggbb` or a key the body does not know. A logo's refusal names its slot in the error's
|
|
2549
|
+
* `errors[]`, each entry's `field` being `square_logo` or `wide_logo` and its `detail` the
|
|
2550
|
+
* sentence to show under that slot: `400` `invalid_storage_uri` for a URI that is not a storage
|
|
2551
|
+
* URI, `403` `forbidden` for another organization's file, and `422` `validation_failed` with a
|
|
2552
|
+
* `LogoRefusalCode` per broken rule for a file the platform refused. A failure of the platform's
|
|
2553
|
+
* storage while reading or copying a logo is a `500` `storage_backend_failed`, retryable, with
|
|
2554
|
+
* nothing written. A `503` `service_unavailable` is a failure of its database, after which the
|
|
2555
|
+
* write may have landed or not: the same input sent again is safe.
|
|
2556
|
+
*/
|
|
2557
|
+
async updateOrganizationBranding(orgId, input) {
|
|
2558
|
+
const segment = idPathSegment(orgId, "updateOrganizationBranding", ORG_ID_ARGUMENT);
|
|
2559
|
+
return this.requestProductObject("PATCH", `organizations/${segment}/branding`, input);
|
|
2560
|
+
}
|
|
2502
2561
|
/** The active org's subscription state — `GET /v1/billing/subscription`. */
|
|
2503
2562
|
async getSubscription() {
|
|
2504
2563
|
return this.requestProduct("GET", "billing/subscription");
|
|
@@ -2642,9 +2701,11 @@ export class PipelexApiClient {
|
|
|
2642
2701
|
* Upload one local asset and return its {@link UploadRecord} — the single-asset
|
|
2643
2702
|
* convenience over {@link upload}. Accepts `Blob`/`File`/`ArrayBuffer`/`Uint8Array`
|
|
2644
2703
|
* in every runtime; a path string is Node-only (it fails instructively elsewhere).
|
|
2645
|
-
* The record guarantees `uri`, `contentType`, `size`, and `filename`.
|
|
2646
|
-
*
|
|
2647
|
-
*
|
|
2704
|
+
* The record guarantees `uri`, `contentType`, `size`, and `filename`. A `kind` and its
|
|
2705
|
+
* `method_id` in the options record the upload as a method's test file, checked before the
|
|
2706
|
+
* asset is read. Transport failures surface as the semantic input-preparation errors
|
|
2707
|
+
* (rejected asset, refused test file, auth, unsupported capability, transport). See
|
|
2708
|
+
* `docs/input-preparation.md`.
|
|
2648
2709
|
*/
|
|
2649
2710
|
async uploadFile(asset, options) {
|
|
2650
2711
|
return uploadFileImpl(this, asset, options);
|
|
@@ -2681,6 +2742,69 @@ export class PipelexApiClient {
|
|
|
2681
2742
|
async prepareInputs(request) {
|
|
2682
2743
|
return prepareInputsImpl(this, request);
|
|
2683
2744
|
}
|
|
2745
|
+
/**
|
|
2746
|
+
* One page of the organization's files, newest first — `GET /v1/files`.
|
|
2747
|
+
*
|
|
2748
|
+
* Rows are `FileSummary`: the record's id, its `uri`, which a run's inputs take as they take
|
|
2749
|
+
* any address, the name a person sees, its type, size and kind, and where it came from. A list
|
|
2750
|
+
* never holds a file a run made along the way, nor an upload whose bytes have not been seen
|
|
2751
|
+
* stored. Every role of the organization reads every file of it.
|
|
2752
|
+
*
|
|
2753
|
+
* `methodId`, `runId` and `createdBy` choose which list serves the page: the method's files
|
|
2754
|
+
* (what its runs and examples took or made), the run's own files (its final outputs and inline
|
|
2755
|
+
* inputs, in the order of their addresses), one member's (`"me"` for the caller's own), or the
|
|
2756
|
+
* organization's. `q`, `kind`, `format`, `mediaType`, `createdFrom` and `createdTo` filter that
|
|
2757
|
+
* list server-side; `format` and `mediaType` are sent as one repeated parameter per value, and
|
|
2758
|
+
* an empty list of either is a `RequestArgumentError` before any request. The filters run after
|
|
2759
|
+
* the index read, so **a filtered page can hold fewer rows than `limit`, or none, and still
|
|
2760
|
+
* carry a `nextCursor`**: keep following it until it is `null`, with the same filters, since a
|
|
2761
|
+
* cursor is bound to the list and filters that issued it.
|
|
2762
|
+
*
|
|
2763
|
+
* Throws `ApiResponseError`: `400` for a date bound that is not an instant with an offset, a
|
|
2764
|
+
* range whose start is after its end, or a cursor of another list or other filters; `404`
|
|
2765
|
+
* `run_not_found` for a run that is not the organization's; `422` for a `limit` outside 1 to
|
|
2766
|
+
* 200, an unknown `kind`, more than 100 values of `format` or `mediaType`, or an id that cannot
|
|
2767
|
+
* be one. A page whose `items` is not an array or whose `next_cursor` is neither a string nor
|
|
2768
|
+
* `null` is an `ApiResponseError` of an answer the SDK cannot read. See `docs/files.md`.
|
|
2769
|
+
*/
|
|
2770
|
+
async listFiles(query = {}) {
|
|
2771
|
+
const search = productQuery({
|
|
2772
|
+
method_id: query.methodId,
|
|
2773
|
+
run_id: query.runId,
|
|
2774
|
+
created_by: query.createdBy,
|
|
2775
|
+
kind: query.kind,
|
|
2776
|
+
format: repeatedFilter("listFiles", "format", query.format),
|
|
2777
|
+
media_type: repeatedFilter("listFiles", "mediaType", query.mediaType),
|
|
2778
|
+
created_from: query.createdFrom,
|
|
2779
|
+
created_to: query.createdTo,
|
|
2780
|
+
q: query.q,
|
|
2781
|
+
limit: query.limit,
|
|
2782
|
+
cursor: query.cursor,
|
|
2783
|
+
});
|
|
2784
|
+
return this.requestPage(`files${search}`);
|
|
2785
|
+
}
|
|
2786
|
+
/**
|
|
2787
|
+
* Every file of the organization the query selects, streamed — follows the cursor for you.
|
|
2788
|
+
*
|
|
2789
|
+
* ```ts
|
|
2790
|
+
* for await (const file of client.iterateFiles({ kind: "uploaded", format: ["pdf"] })) {
|
|
2791
|
+
* if (file.name === "March invoice.pdf") break; // stop whenever you like
|
|
2792
|
+
* }
|
|
2793
|
+
* ```
|
|
2794
|
+
*
|
|
2795
|
+
* **Prefer `listFiles`** for anything user-facing: this makes as many round trips as the data
|
|
2796
|
+
* demands. It is an iterator rather than an all-at-once helper for the reason `iterateRuns` is
|
|
2797
|
+
* one: a helper returning an array needs a page cap, and a cap returns a truncated list with no
|
|
2798
|
+
* error.
|
|
2799
|
+
*
|
|
2800
|
+
* It keeps going **through an empty page that carries a cursor**, which under any filter means
|
|
2801
|
+
* "nothing matched in the slice just read", not "done", sending the same filters with every
|
|
2802
|
+
* page. It stops on a `null` cursor or on a cursor the server failed to advance, and throws
|
|
2803
|
+
* `PagingNotTerminatingError` past a page ceiling no real file list reaches.
|
|
2804
|
+
*/
|
|
2805
|
+
async *iterateFiles(query = {}) {
|
|
2806
|
+
yield* followCursor("listFiles", (cursor) => this.listFiles({ ...query, cursor }));
|
|
2807
|
+
}
|
|
2684
2808
|
/**
|
|
2685
2809
|
* One PAGE of a method's runs, newest first — `GET /v1/runs?method_id=`.
|
|
2686
2810
|
*
|