@pipelex/sdk 0.58.0 → 0.60.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 +1 -2
- package/dist/cli/script.d.ts +1 -1
- package/dist/client.d.ts +121 -33
- package/dist/client.js +179 -55
- package/dist/client.js.map +1 -1
- package/dist/errors.d.ts +5 -4
- package/dist/errors.js +5 -4
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/models.d.ts +15 -3
- package/dist/product-models.d.ts +7 -36
- package/dist/runs.d.ts +7 -0
- package/dist/runs.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/webhooks.d.ts +304 -0
- package/dist/webhooks.js +318 -0
- package/dist/webhooks.js.map +1 -0
- package/docs/architecture.md +10 -4
- package/docs/crate-routes.md +5 -5
- package/docs/errors.md +2 -0
- package/docs/input-preparation.md +1 -1
- package/docs/method-copy.md +5 -14
- package/docs/webhooks.md +130 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -177,7 +177,6 @@ const copy = await client.copyMethod(
|
|
|
177
177
|
{ idempotencyKey: crypto.randomUUID() },
|
|
178
178
|
);
|
|
179
179
|
if (copy.outcome === "copied") console.log(copy.method.copied_from); // the original it names
|
|
180
|
-
const copies = await client.listMethodCopies(created.method_id); // where the copies went
|
|
181
180
|
|
|
182
181
|
// The organization's files, newest first: uploads, test files and the files runs made. Each row's
|
|
183
182
|
// `uri` goes into a run's inputs as any address does. A filtered page can be empty and still carry
|
|
@@ -285,7 +284,7 @@ These pages ship inside the published package, so a reader who has only installe
|
|
|
285
284
|
| [`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` |
|
|
286
285
|
| [`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 |
|
|
287
286
|
| [`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 |
|
|
288
|
-
| [`docs/method-copy.md`](./docs/method-copy.md) | Copying a method into another organization or duplicating it: what a copy holds and the stored files it copies, the `copied` or `refused` verdict, every refusal, the copy's idempotency key,
|
|
287
|
+
| [`docs/method-copy.md`](./docs/method-copy.md) | Copying a method into another organization or duplicating it: what a copy holds and the stored files it copies, the `copied` or `refused` verdict, every refusal, the copy's idempotency key, and `copied_from` |
|
|
289
288
|
| [`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 |
|
|
290
289
|
| [`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 |
|
|
291
290
|
| [`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 |
|
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.60.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,9 +1,10 @@
|
|
|
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, DraftFileFromFileInput, DraftFileFromFileResult, FileArchive, FileArchiveRequest, FileDetail, FileLinkTarget, FileLinksResult, FilePage, FileRecord, FileRecordList, FileRenameInput, FileSummary, GetFileQuery, GetFilesByUriOptions, InvoiceView, Membership, MembershipsResponse, OrganizationBranding, OrganizationBrandingUpdate, ListFilesQuery,
|
|
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, MethodCopyInput, MethodCopyResult, 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
|
+
import type { WebhookDeliveryDetail, WebhookDeliveryList, WebhookReplayAccepted, WebhookReplayOptions, WebhookSecret, WebhookSecretRotateOptions } from "./webhooks.js";
|
|
7
8
|
import type { UploadableAsset, UploadFileOptions, UploadRecord } from "./upload.js";
|
|
8
9
|
import type { PrepareInputsRequest, PreparedInputs } from "./prepare-inputs.js";
|
|
9
10
|
import type { BulkResolveStorageUrlsInput, BulkResolvedStorageUrls, DownloadArtifactsRequest, DownloadArtifactsResult, FetchArtifactOptions, ResolvedArtifact } from "./artifacts.js";
|
|
@@ -37,6 +38,27 @@ export interface PipelexApiRunExtensions {
|
|
|
37
38
|
* An empty string is treated as absent and is not sent.
|
|
38
39
|
*/
|
|
39
40
|
method_ref?: string | null;
|
|
41
|
+
/**
|
|
42
|
+
* Webhook URLs to call once the run has ended, each with a signed `run.finished` event whose
|
|
43
|
+
* `data` is the run's status read, so a caller learns of the end without polling. Read the call
|
|
44
|
+
* with `verifyWebhook`, then fetch the results with the event's `pipeline_run_id`; see
|
|
45
|
+
* `docs/webhooks.md`.
|
|
46
|
+
*
|
|
47
|
+
* A layer-2 extension: the hosted API and a self-hosted runner both take it on `start` and
|
|
48
|
+
* `execute`, and each holds the URLs to its own rules, which this client does not restate. The
|
|
49
|
+
* hosted API takes at most 5 HTTPS URLs of at most 2,048 characters, none repeated, and refuses a
|
|
50
|
+
* URL whose host resolves to a private, loopback, link-local or metadata address with a `422`
|
|
51
|
+
* `request.invalid` naming it as `callback_urls.<index>`; a deployment that does not deliver
|
|
52
|
+
* caller webhooks refuses the list with a `422` `request.unsupported`. A self-hosted runner
|
|
53
|
+
* whose operator configured no signing secret refuses it with a `422`
|
|
54
|
+
* `pipelex.webhook.secret_not_configured`. The start's acknowledgement and every read of the run
|
|
55
|
+
* list the URLs redacted, as `callback_urls`.
|
|
56
|
+
*
|
|
57
|
+
* Not a run source: a run still needs something to run. A value that is not an array of strings
|
|
58
|
+
* is a `RequestArgumentError` before anything is sent, and an empty array is treated as absent
|
|
59
|
+
* and is not sent.
|
|
60
|
+
*/
|
|
61
|
+
callback_urls?: readonly string[] | null;
|
|
40
62
|
}
|
|
41
63
|
/**
|
|
42
64
|
* The hosted API's own run arguments — the layer-3 extensions this client adds
|
|
@@ -262,7 +284,8 @@ export declare class PipelexApiClient implements HostedProtocolSurface {
|
|
|
262
284
|
* `url` is a fully resolved absolute URL. `mayStartRun` marks a request that may create a run,
|
|
263
285
|
* whose errors then say whether it may have (see `RunCreation`).
|
|
264
286
|
* `idempotencyKey`, already checked by `idempotencyKeyOf`, is sent as the
|
|
265
|
-
* `Idempotency-Key` header; only `start
|
|
287
|
+
* `Idempotency-Key` header; only `start`, `copyMethod`, `rotateWebhookSecret` and
|
|
288
|
+
* `replayRunWebhookDelivery` pass one.
|
|
266
289
|
*/
|
|
267
290
|
private requestRaw;
|
|
268
291
|
/**
|
|
@@ -592,10 +615,10 @@ export declare class PipelexApiClient implements HostedProtocolSurface {
|
|
|
592
615
|
*
|
|
593
616
|
* The mapping is what makes their no-verdict arms usable: a crate route answers `422`
|
|
594
617
|
* for a request it cannot act on (a pipe selection `pipe-io` refuses; an unknown
|
|
595
|
-
* `kind`/`target`, or a `pipe_ref` on the concept-set-wide `types` kind, on `codegen
|
|
596
|
-
*
|
|
597
|
-
* server-side as of pipelex-api 0.21.0). A caller branches on
|
|
598
|
-
* never on a message.
|
|
618
|
+
* `kind`/`target`, or a `pipe_ref` on the concept-set-wide `types` kind, on `codegen`;
|
|
619
|
+
* the reserved registry-form `method_ref`, coded `request.unsupported`, on all three, the
|
|
620
|
+
* address form being resolved server-side as of pipelex-api 0.21.0). A caller branches on
|
|
621
|
+
* the problem's `code`, never on a message.
|
|
599
622
|
*
|
|
600
623
|
* All of these are static and inference-free, so they default to the management-call
|
|
601
624
|
* timeout. The internal raise is `crateRequestTimeoutMs`'s: the fetch-sized budget a
|
|
@@ -682,7 +705,7 @@ export declare class PipelexApiClient implements HostedProtocolSurface {
|
|
|
682
705
|
* protocol's `files` map or as a `directory`, `tree` or `contents` the client sends
|
|
683
706
|
* inline when small and by hash otherwise (see {@link CrateRequestBase}); an
|
|
684
707
|
* address-form `method_ref` (server-resolved, pipelex-api >= 0.21.0; the registry form
|
|
685
|
-
*
|
|
708
|
+
* is a `422` `request.unsupported`); or a hosted `method_id` (platform-resolved — see
|
|
686
709
|
* {@link PipelexHostedToolingExtensions}). An empty map or string is absent.
|
|
687
710
|
*
|
|
688
711
|
* `options.signal` stops the call between the steps a `directory`, `tree` or `contents`
|
|
@@ -707,7 +730,8 @@ export declare class PipelexApiClient implements HostedProtocolSurface {
|
|
|
707
730
|
* Same 200-verdict discipline and same three-form closure selector as
|
|
708
731
|
* {@link resolve}. Only a no-verdict condition throws `ApiResponseError`: an
|
|
709
732
|
* unknown `kind`/`target`, a `pipe_ref` on the concept-set-wide `types` kind, or
|
|
710
|
-
* a malformed selector is a 422
|
|
733
|
+
* a malformed selector is a 422, and so is a registry-form `method_ref`, coded
|
|
734
|
+
* `request.unsupported`.
|
|
711
735
|
*/
|
|
712
736
|
codegen(request: CodegenRequest, options?: CrateRouteOptions): Promise<CodegenResponse>;
|
|
713
737
|
/**
|
|
@@ -738,9 +762,9 @@ export declare class PipelexApiClient implements HostedProtocolSurface {
|
|
|
738
762
|
* Only a no-verdict condition throws `ApiResponseError`: a malformed selector and an
|
|
739
763
|
* over-limit file are `422` problems; a selection the route refuses is a `422` whose
|
|
740
764
|
* `code` is `request.pipe_not_found` (an unknown `pipe_ref`), `request.pipe_unspecified`
|
|
741
|
-
* (no entry pipe) or `request.pipe_ambiguous`
|
|
742
|
-
* candidates in its `params`; a registry-form `method_ref` is a
|
|
743
|
-
* artifacts cannot be derived is a `500`.
|
|
765
|
+
* (no entry pipe, or several bundles' `main_pipe` declarations) or `request.pipe_ambiguous`
|
|
766
|
+
* (an ambiguous bare code), the candidates in its `params`; a registry-form `method_ref` is a
|
|
767
|
+
* `422` `request.unsupported`; a pipe whose artifacts cannot be derived is a `500`.
|
|
744
768
|
*
|
|
745
769
|
* A `method_ref` gets the fetch-sized budget, as on the other crate routes. On the
|
|
746
770
|
* hosted API the gateway caps a request at about 30 seconds whatever the client allows,
|
|
@@ -1324,26 +1348,6 @@ export declare class PipelexApiClient implements HostedProtocolSurface {
|
|
|
1324
1348
|
* an `ApiResponseError` too.
|
|
1325
1349
|
*/
|
|
1326
1350
|
copyMethod(methodId: string, input: MethodCopyInput, options?: MethodCopyRequestOptions): Promise<MethodCopyResult>;
|
|
1327
|
-
/**
|
|
1328
|
-
* One page of the copies made of a method, in the order of the copies' ids, which says nothing
|
|
1329
|
-
* of when each was made — `GET /v1/methods/{id}/copies`.
|
|
1330
|
-
*
|
|
1331
|
-
* Each row is the `MethodCopyMarker` the original keeps of a copy: the copy's id, the
|
|
1332
|
-
* destination's id, the member who made it and when. The destination is named by its id alone,
|
|
1333
|
-
* never its name, so a surface shows a destination's name only from the reader's own
|
|
1334
|
-
* memberships, and a copy in an organization the reader does not belong to stays a bare id. A
|
|
1335
|
-
* marker stays when its copy is erased, and the copy then reads as not found. Pass the page's
|
|
1336
|
-
* `nextCursor` back as `cursor` to continue; `null` is the last page. A method never copied,
|
|
1337
|
-
* and a copy itself, answer an empty page. The listing is open to every member of the method's
|
|
1338
|
-
* organization, a runner included, as every read of a method is.
|
|
1339
|
-
*
|
|
1340
|
-
* Takes a bare catalog id, as `getMethod` does. Throws `ApiResponseError`: `404`
|
|
1341
|
-
* `pipelex.catalog.not_found` for an unknown method, `409` `pipelex.catalog.being_deleted`, and
|
|
1342
|
-
* `422` `request.invalid` for a cursor the listing did not issue for this method or a `limit`
|
|
1343
|
-
* outside 1 to 100. A page whose `items` is not an array or whose `next_cursor` is neither a
|
|
1344
|
-
* string nor `null` is an answer the SDK cannot read, thrown as an `ApiResponseError` too.
|
|
1345
|
-
*/
|
|
1346
|
-
listMethodCopies(methodId: string, query?: ListMethodCopiesQuery): Promise<MethodCopyPage>;
|
|
1347
1351
|
/**
|
|
1348
1352
|
* Erase a method and everything it produced — `DELETE /v1/methods/{id}`.
|
|
1349
1353
|
*
|
|
@@ -1557,6 +1561,31 @@ export declare class PipelexApiClient implements HostedProtocolSurface {
|
|
|
1557
1561
|
* the write may have landed or not: the same input sent again is safe.
|
|
1558
1562
|
*/
|
|
1559
1563
|
updateOrganizationBranding(orgId: string, input: OrganizationBrandingUpdate): Promise<OrganizationBranding>;
|
|
1564
|
+
/**
|
|
1565
|
+
* The organization's webhook signing secret — `GET /v1/organizations/{org_id}/webhook-secret`:
|
|
1566
|
+
* the `whsec_` secret every caller webhook of the organization is signed with, which a receiver
|
|
1567
|
+
* hands to `verifyWebhook`. The platform generates it on this first read when the organization
|
|
1568
|
+
* has none. Treat the answer as a credential: never log or cache it.
|
|
1569
|
+
*
|
|
1570
|
+
* An admin's route, from a web session only: the hosted gateway refuses an API key on it. Throws
|
|
1571
|
+
* `ApiResponseError`: `403` `auth.forbidden` whose `params.requirement` names `admin` for any
|
|
1572
|
+
* other role, `403` `pipelex.org.mismatch` for an organization other than the caller's active
|
|
1573
|
+
* one, and `404` `pipelex.org.not_found` for one with no record.
|
|
1574
|
+
*/
|
|
1575
|
+
getWebhookSecret(orgId: string): Promise<WebhookSecret>;
|
|
1576
|
+
/**
|
|
1577
|
+
* Rotate the organization's webhook signing secret — `POST
|
|
1578
|
+
* /v1/organizations/{org_id}/webhook-secret/rotate`: a new secret is current at once, and the
|
|
1579
|
+
* one it replaces keeps signing beside it for 24 hours, then is dropped, so a receiver has a day
|
|
1580
|
+
* to take the new one. An organization with no secret yet gets its first. Answers the new secret,
|
|
1581
|
+
* as `getWebhookSecret` does, with its guards and refusals.
|
|
1582
|
+
*
|
|
1583
|
+
* `options.idempotencyKey` is sent as the `Idempotency-Key` header. The answer carries a secret,
|
|
1584
|
+
* so the platform never replays it: the same key sent again is refused `409`
|
|
1585
|
+
* `request.already_executed` and rotates nothing more, and the secret is then read with
|
|
1586
|
+
* `getWebhookSecret`. A malformed key is a `RequestArgumentError` before anything is sent.
|
|
1587
|
+
*/
|
|
1588
|
+
rotateWebhookSecret(orgId: string, options?: WebhookSecretRotateOptions): Promise<WebhookSecret>;
|
|
1560
1589
|
/** The active org's subscription state — `GET /v1/billing/subscription`. */
|
|
1561
1590
|
getSubscription(): Promise<SubscriptionResponse>;
|
|
1562
1591
|
/** Available plans (with `is_current`) — `GET /v1/billing/plans`. */
|
|
@@ -2066,6 +2095,65 @@ export declare class PipelexApiClient implements HostedProtocolSurface {
|
|
|
2066
2095
|
* string nor `null`, is an answer the SDK cannot read, thrown as an `ApiResponseError` too.
|
|
2067
2096
|
*/
|
|
2068
2097
|
listRunFiles(runId: string, query?: ListRunFilesQuery): Promise<RunFilesPage>;
|
|
2098
|
+
/**
|
|
2099
|
+
* A run's caller-webhook deliveries — `GET /v1/runs/{id}/webhook-deliveries`: one per URL the
|
|
2100
|
+
* run registered in `callback_urls`, in their order, once the run has ended, each with its
|
|
2101
|
+
* state and every attempt, its URL redacted. A run that registered none, or has not ended, lists
|
|
2102
|
+
* none. Any member of the organization reads them, by web session or API key.
|
|
2103
|
+
*
|
|
2104
|
+
* Throws `RequestArgumentError` for a run id that is empty, `.` or `..`, or holds a lone
|
|
2105
|
+
* surrogate, before any request. Throws `ApiResponseError`: `404` `run.not_found` for a run that
|
|
2106
|
+
* is not the organization's, exactly as for one that does not exist. An answer whose
|
|
2107
|
+
* `deliveries` is not an array is an answer the SDK cannot read, thrown as an `ApiResponseError`
|
|
2108
|
+
* too.
|
|
2109
|
+
*/
|
|
2110
|
+
listRunWebhookDeliveries(runId: string): Promise<WebhookDeliveryList>;
|
|
2111
|
+
/**
|
|
2112
|
+
* One delivery of a run's webhooks, with its event —
|
|
2113
|
+
* `GET /v1/runs/{id}/webhook-deliveries/{delivery_id}`: the body its latest attempt sent, or,
|
|
2114
|
+
* before any attempt, the body its first attempt will send. Every attempt and replay of the
|
|
2115
|
+
* delivery sends that body, only its `attempt` changing, so the event matches what a receiver
|
|
2116
|
+
* logged only once an attempt was made: it then equals, as JSON, the body that attempt brought
|
|
2117
|
+
* the receiver. Compare the two as parsed JSON values, never as text: the read promises the same
|
|
2118
|
+
* value, not the same bytes.
|
|
2119
|
+
* `deliveryId` is the delivery's `delivery_id`, the receiver's `webhook-id`.
|
|
2120
|
+
*
|
|
2121
|
+
* Throws `RequestArgumentError` for an id that is empty, `.` or `..`, or holds a lone surrogate,
|
|
2122
|
+
* before any request. Throws `ApiResponseError`: `404` `run.not_found` for a run the caller
|
|
2123
|
+
* cannot read, and `422` `request.invalid` naming `delivery_id` for a delivery the run does not
|
|
2124
|
+
* have.
|
|
2125
|
+
*/
|
|
2126
|
+
getRunWebhookDelivery(runId: string, deliveryId: string): Promise<WebhookDeliveryDetail>;
|
|
2127
|
+
/**
|
|
2128
|
+
* Send a settled delivery again — `POST /v1/runs/{id}/webhook-deliveries/{delivery_id}/replay`:
|
|
2129
|
+
* one new attempt under the same `webhook-id`, with the same `data`, the next `attempt`, a fresh
|
|
2130
|
+
* timestamp, and the signatures of the secrets live when it is sent. A replay the receiver
|
|
2131
|
+
* accepts marks the delivery `delivered`; one it does not leaves its state as it was, and the
|
|
2132
|
+
* attempt is recorded either way. A replay never returns a delivery to `pending`. Anyone who
|
|
2133
|
+
* can read the run asks for one, an API key included, under the organization's replay rate.
|
|
2134
|
+
*
|
|
2135
|
+
* Answers the attempt number the replay is sent under; a replay asked again before it is sent is
|
|
2136
|
+
* that same attempt.
|
|
2137
|
+
*
|
|
2138
|
+
* `options.idempotencyKey` is sent as the `Idempotency-Key` header, and nothing is sent when it
|
|
2139
|
+
* is absent or `null`. The platform stores the replay's answer under the key, so the same replay
|
|
2140
|
+
* sent again under the same key, as after an answer lost to a time limit, gets the stored answer,
|
|
2141
|
+
* naming the same attempt, and the delivery is not sent twice. A key reused for another request
|
|
2142
|
+
* is refused `409` `request.idempotency_conflict`, and one whose earlier request is still being
|
|
2143
|
+
* handled `409` `request.idempotency_key_in_use`, whose `retry` is `yes`: send it again under the
|
|
2144
|
+
* same key after its `retry_after`. See `WebhookReplayOptions`.
|
|
2145
|
+
*
|
|
2146
|
+
* Throws `RequestArgumentError` for an id that is empty, `.` or `..`, or holds a lone surrogate,
|
|
2147
|
+
* or for an idempotency key that is not 1 to 255 visible ASCII characters, before any request.
|
|
2148
|
+
* Throws `ApiResponseError`: `404` `run.not_found` for a run the caller cannot read; `422`
|
|
2149
|
+
* `request.invalid` naming `delivery_id` for a delivery the run does not have; `409`
|
|
2150
|
+
* `pipelex.org.not_manageable` with the reason `webhook_delivery_pending` for a delivery still on
|
|
2151
|
+
* its retry schedule, or `webhook_delivery_attempts_exhausted` for one holding the most attempts
|
|
2152
|
+
* a delivery holds; `429` `account.rate_limited`, with its `retry_after`, past the
|
|
2153
|
+
* organization's replay rate; and a `500` or `503` when the replay could not be queued, which
|
|
2154
|
+
* then counts nothing against the rate.
|
|
2155
|
+
*/
|
|
2156
|
+
replayRunWebhookDelivery(runId: string, deliveryId: string, options?: WebhookReplayOptions): Promise<WebhookReplayAccepted>;
|
|
2069
2157
|
/** Patch a run's status (admin/manual) — `PUT /v1/runs/{id}`. */
|
|
2070
2158
|
updateRun(runId: string, input: UpdateRunInput): Promise<void>;
|
|
2071
2159
|
/**
|
|
@@ -2079,8 +2167,8 @@ export declare class PipelexApiClient implements HostedProtocolSurface {
|
|
|
2079
2167
|
/**
|
|
2080
2168
|
* Blocking `POST /v1/execute` adapted onto `RunResults` — the bare-runner
|
|
2081
2169
|
* path. Forwards every protocol field PLUS every extension surface: the
|
|
2082
|
-
* runner-resolved `method_ref`, the
|
|
2083
|
-
* `extra` passthrough. An extension-only call (`{ extra }` with no
|
|
2170
|
+
* runner-resolved `method_ref`, the run's `callback_urls`, the hosted
|
|
2171
|
+
* `method_id`, and the generic `extra` passthrough. An extension-only call (`{ extra }` with no
|
|
2084
2172
|
* pipe_code/bundle) or a vendor selector riding `extra` must survive this
|
|
2085
2173
|
* path, not just the durable one — a `method_ref` run must run the same
|
|
2086
2174
|
* fetched package here, and a hosted `method_id` must reach the server too,
|
package/dist/client.js
CHANGED
|
@@ -240,6 +240,10 @@ const ARCHIVE_ID_ARGUMENT = {
|
|
|
240
240
|
name: "archive_id",
|
|
241
241
|
what: "an archive id, the archive's non-empty `archive_id`",
|
|
242
242
|
};
|
|
243
|
+
const DELIVERY_ID_ARGUMENT = {
|
|
244
|
+
name: "delivery_id",
|
|
245
|
+
what: "a delivery id, the delivery's non-empty `delivery_id`",
|
|
246
|
+
};
|
|
243
247
|
/** What the run id of each example write is, as a refusal of it names it. */
|
|
244
248
|
const CREATE_RUN_ID_ARGUMENT = {
|
|
245
249
|
name: "run_id",
|
|
@@ -578,7 +582,8 @@ export class PipelexApiClient {
|
|
|
578
582
|
* `url` is a fully resolved absolute URL. `mayStartRun` marks a request that may create a run,
|
|
579
583
|
* whose errors then say whether it may have (see `RunCreation`).
|
|
580
584
|
* `idempotencyKey`, already checked by `idempotencyKeyOf`, is sent as the
|
|
581
|
-
* `Idempotency-Key` header; only `start
|
|
585
|
+
* `Idempotency-Key` header; only `start`, `copyMethod`, `rotateWebhookSecret` and
|
|
586
|
+
* `replayRunWebhookDelivery` pass one.
|
|
582
587
|
*/
|
|
583
588
|
async requestRaw(method, url, options = {}) {
|
|
584
589
|
const hasBody = options.body !== undefined;
|
|
@@ -1300,10 +1305,10 @@ export class PipelexApiClient {
|
|
|
1300
1305
|
*
|
|
1301
1306
|
* The mapping is what makes their no-verdict arms usable: a crate route answers `422`
|
|
1302
1307
|
* for a request it cannot act on (a pipe selection `pipe-io` refuses; an unknown
|
|
1303
|
-
* `kind`/`target`, or a `pipe_ref` on the concept-set-wide `types` kind, on `codegen
|
|
1304
|
-
*
|
|
1305
|
-
* server-side as of pipelex-api 0.21.0). A caller branches on
|
|
1306
|
-
* never on a message.
|
|
1308
|
+
* `kind`/`target`, or a `pipe_ref` on the concept-set-wide `types` kind, on `codegen`;
|
|
1309
|
+
* the reserved registry-form `method_ref`, coded `request.unsupported`, on all three, the
|
|
1310
|
+
* address form being resolved server-side as of pipelex-api 0.21.0). A caller branches on
|
|
1311
|
+
* the problem's `code`, never on a message.
|
|
1307
1312
|
*
|
|
1308
1313
|
* All of these are static and inference-free, so they default to the management-call
|
|
1309
1314
|
* timeout. The internal raise is `crateRequestTimeoutMs`'s: the fetch-sized budget a
|
|
@@ -1425,9 +1430,8 @@ export class PipelexApiClient {
|
|
|
1425
1430
|
// unexposed — see their section above for why that blocks nothing. `pipe-io` is
|
|
1426
1431
|
// newer: a runner serves it from `pipelex-api` v0.33.0, and codes its selection
|
|
1427
1432
|
// refusals `request.pipe_not_found`, `request.pipe_unspecified` and
|
|
1428
|
-
// `request.pipe_ambiguous
|
|
1429
|
-
//
|
|
1430
|
-
// `docs/crate-routes.md`).
|
|
1433
|
+
// `request.pipe_ambiguous` from v0.90.0; the dev plane serves it too, measured on
|
|
1434
|
+
// api-dev.pipelex.com on 2026-10-31 (see `docs/crate-routes.md`).
|
|
1431
1435
|
//
|
|
1432
1436
|
// All three are STATIC routes (no dry-run sweep), so like every static sibling they
|
|
1433
1437
|
// take no `timeoutMs` — see the policy note on `requestExtension` before adding one
|
|
@@ -1456,7 +1460,7 @@ export class PipelexApiClient {
|
|
|
1456
1460
|
* protocol's `files` map or as a `directory`, `tree` or `contents` the client sends
|
|
1457
1461
|
* inline when small and by hash otherwise (see {@link CrateRequestBase}); an
|
|
1458
1462
|
* address-form `method_ref` (server-resolved, pipelex-api >= 0.21.0; the registry form
|
|
1459
|
-
*
|
|
1463
|
+
* is a `422` `request.unsupported`); or a hosted `method_id` (platform-resolved — see
|
|
1460
1464
|
* {@link PipelexHostedToolingExtensions}). An empty map or string is absent.
|
|
1461
1465
|
*
|
|
1462
1466
|
* `options.signal` stops the call between the steps a `directory`, `tree` or `contents`
|
|
@@ -1483,7 +1487,8 @@ export class PipelexApiClient {
|
|
|
1483
1487
|
* Same 200-verdict discipline and same three-form closure selector as
|
|
1484
1488
|
* {@link resolve}. Only a no-verdict condition throws `ApiResponseError`: an
|
|
1485
1489
|
* unknown `kind`/`target`, a `pipe_ref` on the concept-set-wide `types` kind, or
|
|
1486
|
-
* a malformed selector is a 422
|
|
1490
|
+
* a malformed selector is a 422, and so is a registry-form `method_ref`, coded
|
|
1491
|
+
* `request.unsupported`.
|
|
1487
1492
|
*/
|
|
1488
1493
|
async codegen(request, options = {}) {
|
|
1489
1494
|
return this.crateRequest("codegen", "codegen", request, options);
|
|
@@ -1516,9 +1521,9 @@ export class PipelexApiClient {
|
|
|
1516
1521
|
* Only a no-verdict condition throws `ApiResponseError`: a malformed selector and an
|
|
1517
1522
|
* over-limit file are `422` problems; a selection the route refuses is a `422` whose
|
|
1518
1523
|
* `code` is `request.pipe_not_found` (an unknown `pipe_ref`), `request.pipe_unspecified`
|
|
1519
|
-
* (no entry pipe) or `request.pipe_ambiguous`
|
|
1520
|
-
* candidates in its `params`; a registry-form `method_ref` is a
|
|
1521
|
-
* artifacts cannot be derived is a `500`.
|
|
1524
|
+
* (no entry pipe, or several bundles' `main_pipe` declarations) or `request.pipe_ambiguous`
|
|
1525
|
+
* (an ambiguous bare code), the candidates in its `params`; a registry-form `method_ref` is a
|
|
1526
|
+
* `422` `request.unsupported`; a pipe whose artifacts cannot be derived is a `500`.
|
|
1522
1527
|
*
|
|
1523
1528
|
* A `method_ref` gets the fetch-sized budget, as on the other crate routes. On the
|
|
1524
1529
|
* hosted API the gateway caps a request at about 30 seconds whatever the client allows,
|
|
@@ -2587,30 +2592,6 @@ export class PipelexApiClient {
|
|
|
2587
2592
|
}
|
|
2588
2593
|
return answer;
|
|
2589
2594
|
}
|
|
2590
|
-
/**
|
|
2591
|
-
* One page of the copies made of a method, in the order of the copies' ids, which says nothing
|
|
2592
|
-
* of when each was made — `GET /v1/methods/{id}/copies`.
|
|
2593
|
-
*
|
|
2594
|
-
* Each row is the `MethodCopyMarker` the original keeps of a copy: the copy's id, the
|
|
2595
|
-
* destination's id, the member who made it and when. The destination is named by its id alone,
|
|
2596
|
-
* never its name, so a surface shows a destination's name only from the reader's own
|
|
2597
|
-
* memberships, and a copy in an organization the reader does not belong to stays a bare id. A
|
|
2598
|
-
* marker stays when its copy is erased, and the copy then reads as not found. Pass the page's
|
|
2599
|
-
* `nextCursor` back as `cursor` to continue; `null` is the last page. A method never copied,
|
|
2600
|
-
* and a copy itself, answer an empty page. The listing is open to every member of the method's
|
|
2601
|
-
* organization, a runner included, as every read of a method is.
|
|
2602
|
-
*
|
|
2603
|
-
* Takes a bare catalog id, as `getMethod` does. Throws `ApiResponseError`: `404`
|
|
2604
|
-
* `pipelex.catalog.not_found` for an unknown method, `409` `pipelex.catalog.being_deleted`, and
|
|
2605
|
-
* `422` `request.invalid` for a cursor the listing did not issue for this method or a `limit`
|
|
2606
|
-
* outside 1 to 100. A page whose `items` is not an array or whose `next_cursor` is neither a
|
|
2607
|
-
* string nor `null` is an answer the SDK cannot read, thrown as an `ApiResponseError` too.
|
|
2608
|
-
*/
|
|
2609
|
-
async listMethodCopies(methodId, query = {}) {
|
|
2610
|
-
const search = productQuery({ limit: query.limit, cursor: query.cursor });
|
|
2611
|
-
const endpoint = `${methodPath(methodId, "listMethodCopies")}/copies`;
|
|
2612
|
-
return this.requestPage(`${endpoint}${search}`);
|
|
2613
|
-
}
|
|
2614
2595
|
/**
|
|
2615
2596
|
* Erase a method and everything it produced — `DELETE /v1/methods/{id}`.
|
|
2616
2597
|
*
|
|
@@ -2939,6 +2920,39 @@ export class PipelexApiClient {
|
|
|
2939
2920
|
const segment = idPathSegment(orgId, "updateOrganizationBranding", ORG_ID_ARGUMENT);
|
|
2940
2921
|
return this.requestProductObject("PATCH", `organizations/${segment}/branding`, input);
|
|
2941
2922
|
}
|
|
2923
|
+
/**
|
|
2924
|
+
* The organization's webhook signing secret — `GET /v1/organizations/{org_id}/webhook-secret`:
|
|
2925
|
+
* the `whsec_` secret every caller webhook of the organization is signed with, which a receiver
|
|
2926
|
+
* hands to `verifyWebhook`. The platform generates it on this first read when the organization
|
|
2927
|
+
* has none. Treat the answer as a credential: never log or cache it.
|
|
2928
|
+
*
|
|
2929
|
+
* An admin's route, from a web session only: the hosted gateway refuses an API key on it. Throws
|
|
2930
|
+
* `ApiResponseError`: `403` `auth.forbidden` whose `params.requirement` names `admin` for any
|
|
2931
|
+
* other role, `403` `pipelex.org.mismatch` for an organization other than the caller's active
|
|
2932
|
+
* one, and `404` `pipelex.org.not_found` for one with no record.
|
|
2933
|
+
*/
|
|
2934
|
+
async getWebhookSecret(orgId) {
|
|
2935
|
+
const segment = idPathSegment(orgId, "getWebhookSecret", ORG_ID_ARGUMENT);
|
|
2936
|
+
return this.requestProductObject("GET", `organizations/${segment}/webhook-secret`);
|
|
2937
|
+
}
|
|
2938
|
+
/**
|
|
2939
|
+
* Rotate the organization's webhook signing secret — `POST
|
|
2940
|
+
* /v1/organizations/{org_id}/webhook-secret/rotate`: a new secret is current at once, and the
|
|
2941
|
+
* one it replaces keeps signing beside it for 24 hours, then is dropped, so a receiver has a day
|
|
2942
|
+
* to take the new one. An organization with no secret yet gets its first. Answers the new secret,
|
|
2943
|
+
* as `getWebhookSecret` does, with its guards and refusals.
|
|
2944
|
+
*
|
|
2945
|
+
* `options.idempotencyKey` is sent as the `Idempotency-Key` header. The answer carries a secret,
|
|
2946
|
+
* so the platform never replays it: the same key sent again is refused `409`
|
|
2947
|
+
* `request.already_executed` and rotates nothing more, and the secret is then read with
|
|
2948
|
+
* `getWebhookSecret`. A malformed key is a `RequestArgumentError` before anything is sent.
|
|
2949
|
+
*/
|
|
2950
|
+
async rotateWebhookSecret(orgId, options = {}) {
|
|
2951
|
+
const endpoint = `organizations/${idPathSegment(orgId, "rotateWebhookSecret", ORG_ID_ARGUMENT)}/webhook-secret/rotate`;
|
|
2952
|
+
const idempotencyKey = idempotencyKeyOf(options, "rotate the secret");
|
|
2953
|
+
const res = await this.requestProductAnswer("POST", endpoint, undefined, { idempotencyKey });
|
|
2954
|
+
return this.readObjectAnswer("POST", endpoint, res);
|
|
2955
|
+
}
|
|
2942
2956
|
/** The active org's subscription state — `GET /v1/billing/subscription`. */
|
|
2943
2957
|
async getSubscription() {
|
|
2944
2958
|
return this.requestProduct("GET", "billing/subscription");
|
|
@@ -3669,6 +3683,87 @@ export class PipelexApiClient {
|
|
|
3669
3683
|
}
|
|
3670
3684
|
return { ...rest, nextCursor };
|
|
3671
3685
|
}
|
|
3686
|
+
/**
|
|
3687
|
+
* A run's caller-webhook deliveries — `GET /v1/runs/{id}/webhook-deliveries`: one per URL the
|
|
3688
|
+
* run registered in `callback_urls`, in their order, once the run has ended, each with its
|
|
3689
|
+
* state and every attempt, its URL redacted. A run that registered none, or has not ended, lists
|
|
3690
|
+
* none. Any member of the organization reads them, by web session or API key.
|
|
3691
|
+
*
|
|
3692
|
+
* Throws `RequestArgumentError` for a run id that is empty, `.` or `..`, or holds a lone
|
|
3693
|
+
* surrogate, before any request. Throws `ApiResponseError`: `404` `run.not_found` for a run that
|
|
3694
|
+
* is not the organization's, exactly as for one that does not exist. An answer whose
|
|
3695
|
+
* `deliveries` is not an array is an answer the SDK cannot read, thrown as an `ApiResponseError`
|
|
3696
|
+
* too.
|
|
3697
|
+
*/
|
|
3698
|
+
async listRunWebhookDeliveries(runId) {
|
|
3699
|
+
const segment = idPathSegment(runId, "listRunWebhookDeliveries", RUN_ID_ARGUMENT);
|
|
3700
|
+
const endpoint = `runs/${segment}/webhook-deliveries`;
|
|
3701
|
+
const res = await this.requestProductAnswer("GET", endpoint, undefined);
|
|
3702
|
+
const list = this.readObjectAnswer("GET", endpoint, res);
|
|
3703
|
+
if (!Array.isArray(list.deliveries)) {
|
|
3704
|
+
throw this.unreadableAnswer("GET", `/${API_PREFIX}/${endpoint}`, res, "a run's deliveries whose `deliveries` is not an array");
|
|
3705
|
+
}
|
|
3706
|
+
return list;
|
|
3707
|
+
}
|
|
3708
|
+
/**
|
|
3709
|
+
* One delivery of a run's webhooks, with its event —
|
|
3710
|
+
* `GET /v1/runs/{id}/webhook-deliveries/{delivery_id}`: the body its latest attempt sent, or,
|
|
3711
|
+
* before any attempt, the body its first attempt will send. Every attempt and replay of the
|
|
3712
|
+
* delivery sends that body, only its `attempt` changing, so the event matches what a receiver
|
|
3713
|
+
* logged only once an attempt was made: it then equals, as JSON, the body that attempt brought
|
|
3714
|
+
* the receiver. Compare the two as parsed JSON values, never as text: the read promises the same
|
|
3715
|
+
* value, not the same bytes.
|
|
3716
|
+
* `deliveryId` is the delivery's `delivery_id`, the receiver's `webhook-id`.
|
|
3717
|
+
*
|
|
3718
|
+
* Throws `RequestArgumentError` for an id that is empty, `.` or `..`, or holds a lone surrogate,
|
|
3719
|
+
* before any request. Throws `ApiResponseError`: `404` `run.not_found` for a run the caller
|
|
3720
|
+
* cannot read, and `422` `request.invalid` naming `delivery_id` for a delivery the run does not
|
|
3721
|
+
* have.
|
|
3722
|
+
*/
|
|
3723
|
+
async getRunWebhookDelivery(runId, deliveryId) {
|
|
3724
|
+
const call = "getRunWebhookDelivery";
|
|
3725
|
+
const run = idPathSegment(runId, call, RUN_ID_ARGUMENT);
|
|
3726
|
+
const delivery = idPathSegment(deliveryId, call, DELIVERY_ID_ARGUMENT);
|
|
3727
|
+
return this.requestProductObject("GET", `runs/${run}/webhook-deliveries/${delivery}`);
|
|
3728
|
+
}
|
|
3729
|
+
/**
|
|
3730
|
+
* Send a settled delivery again — `POST /v1/runs/{id}/webhook-deliveries/{delivery_id}/replay`:
|
|
3731
|
+
* one new attempt under the same `webhook-id`, with the same `data`, the next `attempt`, a fresh
|
|
3732
|
+
* timestamp, and the signatures of the secrets live when it is sent. A replay the receiver
|
|
3733
|
+
* accepts marks the delivery `delivered`; one it does not leaves its state as it was, and the
|
|
3734
|
+
* attempt is recorded either way. A replay never returns a delivery to `pending`. Anyone who
|
|
3735
|
+
* can read the run asks for one, an API key included, under the organization's replay rate.
|
|
3736
|
+
*
|
|
3737
|
+
* Answers the attempt number the replay is sent under; a replay asked again before it is sent is
|
|
3738
|
+
* that same attempt.
|
|
3739
|
+
*
|
|
3740
|
+
* `options.idempotencyKey` is sent as the `Idempotency-Key` header, and nothing is sent when it
|
|
3741
|
+
* is absent or `null`. The platform stores the replay's answer under the key, so the same replay
|
|
3742
|
+
* sent again under the same key, as after an answer lost to a time limit, gets the stored answer,
|
|
3743
|
+
* naming the same attempt, and the delivery is not sent twice. A key reused for another request
|
|
3744
|
+
* is refused `409` `request.idempotency_conflict`, and one whose earlier request is still being
|
|
3745
|
+
* handled `409` `request.idempotency_key_in_use`, whose `retry` is `yes`: send it again under the
|
|
3746
|
+
* same key after its `retry_after`. See `WebhookReplayOptions`.
|
|
3747
|
+
*
|
|
3748
|
+
* Throws `RequestArgumentError` for an id that is empty, `.` or `..`, or holds a lone surrogate,
|
|
3749
|
+
* or for an idempotency key that is not 1 to 255 visible ASCII characters, before any request.
|
|
3750
|
+
* Throws `ApiResponseError`: `404` `run.not_found` for a run the caller cannot read; `422`
|
|
3751
|
+
* `request.invalid` naming `delivery_id` for a delivery the run does not have; `409`
|
|
3752
|
+
* `pipelex.org.not_manageable` with the reason `webhook_delivery_pending` for a delivery still on
|
|
3753
|
+
* its retry schedule, or `webhook_delivery_attempts_exhausted` for one holding the most attempts
|
|
3754
|
+
* a delivery holds; `429` `account.rate_limited`, with its `retry_after`, past the
|
|
3755
|
+
* organization's replay rate; and a `500` or `503` when the replay could not be queued, which
|
|
3756
|
+
* then counts nothing against the rate.
|
|
3757
|
+
*/
|
|
3758
|
+
async replayRunWebhookDelivery(runId, deliveryId, options = {}) {
|
|
3759
|
+
const call = "replayRunWebhookDelivery";
|
|
3760
|
+
const run = idPathSegment(runId, call, RUN_ID_ARGUMENT);
|
|
3761
|
+
const delivery = idPathSegment(deliveryId, call, DELIVERY_ID_ARGUMENT);
|
|
3762
|
+
const endpoint = `runs/${run}/webhook-deliveries/${delivery}/replay`;
|
|
3763
|
+
const idempotencyKey = idempotencyKeyOf(options, "replay the delivery");
|
|
3764
|
+
const res = await this.requestProductAnswer("POST", endpoint, undefined, { idempotencyKey });
|
|
3765
|
+
return this.readObjectAnswer("POST", endpoint, res);
|
|
3766
|
+
}
|
|
3672
3767
|
/** Patch a run's status (admin/manual) — `PUT /v1/runs/{id}`. */
|
|
3673
3768
|
async updateRun(runId, input) {
|
|
3674
3769
|
const segment = idPathSegment(runId, "updateRun", RUN_ID_ARGUMENT);
|
|
@@ -3685,6 +3780,8 @@ export class PipelexApiClient {
|
|
|
3685
3780
|
const extensions = buildExtensions(options.extra);
|
|
3686
3781
|
const api = buildApiRunExtensions(options);
|
|
3687
3782
|
const hosted = buildHostedRunExtensions(options);
|
|
3783
|
+
// Not a run source, so outside `api`, whose size the precondition below reads.
|
|
3784
|
+
const webhooks = buildCallbackUrls(call, options);
|
|
3688
3785
|
const forms = protocolTreeForms(call, options);
|
|
3689
3786
|
const treeForms = treeFormsGiven(forms);
|
|
3690
3787
|
if (!options.pipe_code &&
|
|
@@ -3696,13 +3793,13 @@ export class PipelexApiClient {
|
|
|
3696
3793
|
}
|
|
3697
3794
|
assertMethodRefPairsWithNothing(options, treeForms);
|
|
3698
3795
|
assertLinkageMethodIdIsBare(options, treeForms);
|
|
3699
|
-
return { forms, extensions, api, hosted };
|
|
3796
|
+
return { forms, extensions, api, hosted, webhooks };
|
|
3700
3797
|
}
|
|
3701
3798
|
/**
|
|
3702
3799
|
* Blocking `POST /v1/execute` adapted onto `RunResults` — the bare-runner
|
|
3703
3800
|
* path. Forwards every protocol field PLUS every extension surface: the
|
|
3704
|
-
* runner-resolved `method_ref`, the
|
|
3705
|
-
* `extra` passthrough. An extension-only call (`{ extra }` with no
|
|
3801
|
+
* runner-resolved `method_ref`, the run's `callback_urls`, the hosted
|
|
3802
|
+
* `method_id`, and the generic `extra` passthrough. An extension-only call (`{ extra }` with no
|
|
3706
3803
|
* pipe_code/bundle) or a vendor selector riding `extra` must survive this
|
|
3707
3804
|
* path, not just the durable one — a `method_ref` run must run the same
|
|
3708
3805
|
* fetched package here, and a hosted `method_id` must reach the server too,
|
|
@@ -3724,6 +3821,8 @@ export class PipelexApiClient {
|
|
|
3724
3821
|
tree: options.tree ?? undefined,
|
|
3725
3822
|
files: options.files ?? undefined,
|
|
3726
3823
|
method_ref: options.method_ref ?? undefined,
|
|
3824
|
+
// A runner calls the run's webhooks once the blocking run has ended, as the platform does.
|
|
3825
|
+
callback_urls: options.callback_urls ?? undefined,
|
|
3727
3826
|
method_id: options.method_id ?? undefined,
|
|
3728
3827
|
extra: options.extra ?? undefined,
|
|
3729
3828
|
}, signal, sending);
|
|
@@ -3798,12 +3897,12 @@ const PROTOCOL_REQUEST_KEYS = [
|
|
|
3798
3897
|
"dynamic_output_concept_ref",
|
|
3799
3898
|
"files",
|
|
3800
3899
|
];
|
|
3801
|
-
// The PIPELEX API's own request
|
|
3802
|
-
// itself
|
|
3803
|
-
//
|
|
3804
|
-
//
|
|
3805
|
-
//
|
|
3806
|
-
const PIPELEX_API_REQUEST_KEYS = ["method_ref"];
|
|
3900
|
+
// The PIPELEX API's own request fields — the layer-2 extensions the runner reads
|
|
3901
|
+
// itself: `method_ref`, a run source in its own right, and `callback_urls`, the
|
|
3902
|
+
// webhooks to call once the run has ended. Reserved on `extra` for the same
|
|
3903
|
+
// reason the protocol fields are: `extra` merges last, so a smuggled copy would
|
|
3904
|
+
// overwrite the validated named option and bypass its checks.
|
|
3905
|
+
const PIPELEX_API_REQUEST_KEYS = ["method_ref", "callback_urls"];
|
|
3807
3906
|
// The HOSTED API's own request fields — the layer-3 extensions this client
|
|
3808
3907
|
// names itself. Reserved on `extra` for the same reason the protocol fields
|
|
3809
3908
|
// are: `extra` merges last, so a smuggled copy would overwrite the validated
|
|
@@ -3906,6 +4005,7 @@ function runRequestBody(call, options, files, parts) {
|
|
|
3906
4005
|
: options.dynamic_output_concept_ref,
|
|
3907
4006
|
files: files ?? undefined,
|
|
3908
4007
|
...parts.api,
|
|
4008
|
+
...parts.webhooks,
|
|
3909
4009
|
...parts.hosted,
|
|
3910
4010
|
...parts.extensions,
|
|
3911
4011
|
};
|
|
@@ -3986,6 +4086,29 @@ function buildApiRunExtensions(options) {
|
|
|
3986
4086
|
const methodRef = nonEmptyString(options.method_ref);
|
|
3987
4087
|
return methodRef === undefined ? {} : { method_ref: methodRef };
|
|
3988
4088
|
}
|
|
4089
|
+
/**
|
|
4090
|
+
* Copy a run's `callback_urls` onto the wire body, `call` naming the caller's method in a refusal.
|
|
4091
|
+
* An absent, `null` or empty list yields an empty object, so the key is not sent, as the platform
|
|
4092
|
+
* treats an empty list as absent. A value that is not an array, or an array holding anything but
|
|
4093
|
+
* strings, is refused before anything is sent; the URLs themselves are the server's to judge,
|
|
4094
|
+
* since the hosted API and a self-hosted runner each hold them to their own rules. The Python SDK
|
|
4095
|
+
* reads the argument the same way.
|
|
4096
|
+
*/
|
|
4097
|
+
function buildCallbackUrls(call, options) {
|
|
4098
|
+
const urls = options.callback_urls;
|
|
4099
|
+
if (urls == null)
|
|
4100
|
+
return {};
|
|
4101
|
+
if (!Array.isArray(urls)) {
|
|
4102
|
+
throw new RequestArgumentError(`${call}() takes callback_urls as an array of URLs; got ${typeof urls}.`, { problem: argumentInvalidProblem("callback_urls", "not_a_list") });
|
|
4103
|
+
}
|
|
4104
|
+
const copy = [...urls];
|
|
4105
|
+
if (copy.some((url) => typeof url !== "string")) {
|
|
4106
|
+
throw new RequestArgumentError(`${call}() takes each of callback_urls as a string URL.`, {
|
|
4107
|
+
problem: argumentInvalidProblem("callback_urls", "not_a_string"),
|
|
4108
|
+
});
|
|
4109
|
+
}
|
|
4110
|
+
return copy.length === 0 ? {} : { callback_urls: copy };
|
|
4111
|
+
}
|
|
3989
4112
|
/**
|
|
3990
4113
|
* Copy the hosted API's own run arguments onto the wire body. Named options,
|
|
3991
4114
|
* not `extra` entries — see `PipelexHostedRunExtensions`.
|
|
@@ -4108,9 +4231,9 @@ function nonEmptyString(value) {
|
|
|
4108
4231
|
/** The request header the hosted API reads a request's idempotency key from. */
|
|
4109
4232
|
const IDEMPOTENCY_KEY_HEADER = "Idempotency-Key";
|
|
4110
4233
|
/**
|
|
4111
|
-
* The longest idempotency key a
|
|
4234
|
+
* The longest idempotency key a request sends. It is room for any generated key, and keeps the
|
|
4112
4235
|
* hosted API's stored record of the key far inside its store's limit, which a longer key could
|
|
4113
|
-
* exceed, so that the
|
|
4236
|
+
* exceed, so that the request would run without the protection the key was sent for.
|
|
4114
4237
|
*/
|
|
4115
4238
|
const MAX_IDEMPOTENCY_KEY_LENGTH = 255;
|
|
4116
4239
|
/**
|
|
@@ -4120,13 +4243,14 @@ const MAX_IDEMPOTENCY_KEY_LENGTH = 255;
|
|
|
4120
4243
|
*/
|
|
4121
4244
|
const VISIBLE_ASCII = /^[\x21-\x7E]+$/;
|
|
4122
4245
|
/**
|
|
4123
|
-
* The idempotency key a start
|
|
4124
|
-
* (absent or `null`). A key that is not a string of 1 to
|
|
4125
|
-
* with a `RequestArgumentError` rather than dropped, an
|
|
4126
|
-
* without the key its caller gave would lose the
|
|
4127
|
-
* `request` says what the request does, as the
|
|
4128
|
-
*
|
|
4129
|
-
*
|
|
4246
|
+
* The idempotency key a start, a method copy, a secret rotation or a delivery replay sends, or
|
|
4247
|
+
* `undefined` when the options carry none (absent or `null`). A key that is not a string of 1 to
|
|
4248
|
+
* 255 visible ASCII characters is refused with a `RequestArgumentError` rather than dropped, an
|
|
4249
|
+
* empty one included, since a request sent without the key its caller gave would lose the
|
|
4250
|
+
* protection the caller asked for without a word. `request` says what the request does, as the
|
|
4251
|
+
* refusal's last sentence names it ("start the run", "copy the method", "rotate the secret",
|
|
4252
|
+
* "replay the delivery"). The message never quotes the key, which a caller may derive from
|
|
4253
|
+
* something private. The Python SDK holds a key to the same rule, in the same words.
|
|
4130
4254
|
*/
|
|
4131
4255
|
function idempotencyKeyOf(options, request = "start the run") {
|
|
4132
4256
|
const key = options.idempotencyKey;
|