@aexhq/sdk 1.0.3-canary → 1.0.5-canary
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 +5 -3
- package/dist/_contracts/account-operations.d.ts +101 -0
- package/dist/_contracts/account-operations.js +242 -0
- package/dist/_contracts/account-types.d.ts +461 -0
- package/dist/_contracts/account-types.js +1 -0
- package/dist/_contracts/api-key.d.ts +25 -19
- package/dist/_contracts/api-key.js +35 -38
- package/dist/_contracts/api-routes.d.ts +20 -0
- package/dist/_contracts/api-routes.js +109 -0
- package/dist/_contracts/archive-limits.js +6 -2
- package/dist/_contracts/asset-bundle.js +12 -40
- package/dist/_contracts/asset-upload-helper.d.ts +14 -21
- package/dist/_contracts/asset-upload-helper.js +27 -54
- package/dist/_contracts/billing-admission.d.ts +29 -0
- package/dist/_contracts/billing-admission.js +28 -0
- package/dist/_contracts/bundle-manifest.js +15 -38
- package/dist/_contracts/error-codes.d.ts +1 -1
- package/dist/_contracts/error-codes.js +23 -3
- package/dist/_contracts/error-factory.js +10 -1
- package/dist/_contracts/event-envelope.d.ts +8 -2
- package/dist/_contracts/event-envelope.js +12 -13
- package/dist/_contracts/failure-class.d.ts +29 -0
- package/dist/_contracts/failure-class.js +73 -0
- package/dist/_contracts/http.d.ts +98 -10
- package/dist/_contracts/http.js +275 -127
- package/dist/_contracts/ids.d.ts +66 -0
- package/dist/_contracts/ids.js +119 -0
- package/dist/_contracts/index.d.ts +6 -0
- package/dist/_contracts/index.js +17 -0
- package/dist/_contracts/internal.d.ts +2 -2
- package/dist/_contracts/internal.js +3 -5
- package/dist/_contracts/models.d.ts +2 -1
- package/dist/_contracts/models.js +4 -8
- package/dist/_contracts/operation-core.d.ts +36 -0
- package/dist/_contracts/operation-core.js +70 -0
- package/dist/_contracts/operations.d.ts +25 -94
- package/dist/_contracts/operations.js +143 -302
- package/dist/_contracts/post-hook.d.ts +3 -0
- package/dist/_contracts/post-hook.js +34 -40
- package/dist/_contracts/runtime-kind.d.ts +25 -8
- package/dist/_contracts/runtime-kind.js +28 -14
- package/dist/_contracts/runtime-manifest.d.ts +10 -6
- package/dist/_contracts/runtime-security-profile.d.ts +2 -1
- package/dist/_contracts/runtime-security-profile.js +4 -5
- package/dist/_contracts/runtime-sizes.d.ts +14 -13
- package/dist/_contracts/runtime-sizes.js +19 -40
- package/dist/_contracts/runtime-types.d.ts +183 -267
- package/dist/_contracts/runtime-types.js +17 -0
- package/dist/_contracts/schemas/asset-bundle.d.ts +70 -0
- package/dist/_contracts/schemas/asset-bundle.js +107 -0
- package/dist/_contracts/schemas/asset-ref.d.ts +61 -0
- package/dist/_contracts/schemas/asset-ref.js +118 -0
- package/dist/_contracts/schemas/bundle-manifest.d.ts +66 -0
- package/dist/_contracts/schemas/bundle-manifest.js +77 -0
- package/dist/_contracts/schemas/index.d.ts +32 -0
- package/dist/_contracts/schemas/index.js +30 -0
- package/dist/_contracts/schemas/mcp-server.d.ts +99 -0
- package/dist/_contracts/schemas/mcp-server.js +209 -0
- package/dist/_contracts/schemas/models.d.ts +29 -0
- package/dist/_contracts/schemas/models.js +51 -0
- package/dist/_contracts/schemas/numeric.d.ts +18 -0
- package/dist/_contracts/schemas/numeric.js +28 -0
- package/dist/_contracts/schemas/post-hook.d.ts +45 -0
- package/dist/_contracts/schemas/post-hook.js +68 -0
- package/dist/_contracts/schemas/response-assets.d.ts +75 -0
- package/dist/_contracts/schemas/response-assets.js +81 -0
- package/dist/_contracts/schemas/response-billing.d.ts +208 -0
- package/dist/_contracts/schemas/response-billing.js +139 -0
- package/dist/_contracts/schemas/response-common.d.ts +132 -0
- package/dist/_contracts/schemas/response-common.js +162 -0
- package/dist/_contracts/schemas/response-identity.d.ts +194 -0
- package/dist/_contracts/schemas/response-identity.js +96 -0
- package/dist/_contracts/schemas/response-mcp-servers.d.ts +51 -0
- package/dist/_contracts/schemas/response-mcp-servers.js +32 -0
- package/dist/_contracts/schemas/response-secrets.d.ts +50 -0
- package/dist/_contracts/schemas/response-secrets.js +32 -0
- package/dist/_contracts/schemas/response-sessions-internal.d.ts +200 -0
- package/dist/_contracts/schemas/response-sessions-internal.js +142 -0
- package/dist/_contracts/schemas/response-sessions.d.ts +1598 -0
- package/dist/_contracts/schemas/response-sessions.js +377 -0
- package/dist/_contracts/schemas/response-webhooks.d.ts +76 -0
- package/dist/_contracts/schemas/response-webhooks.js +42 -0
- package/dist/_contracts/schemas/response-workspace.d.ts +225 -0
- package/dist/_contracts/schemas/response-workspace.js +99 -0
- package/dist/_contracts/schemas/runtime-kind.d.ts +31 -0
- package/dist/_contracts/schemas/runtime-kind.js +29 -0
- package/dist/_contracts/schemas/runtime-security-profile.d.ts +28 -0
- package/dist/_contracts/schemas/runtime-security-profile.js +26 -0
- package/dist/_contracts/schemas/runtime-sizes.d.ts +70 -0
- package/dist/_contracts/schemas/runtime-sizes.js +127 -0
- package/dist/_contracts/schemas/session-limits.d.ts +34 -0
- package/dist/_contracts/schemas/session-limits.js +39 -0
- package/dist/_contracts/schemas/session-machine.d.ts +23 -0
- package/dist/_contracts/schemas/session-machine.js +24 -0
- package/dist/_contracts/schemas/session-request-config.d.ts +58 -0
- package/dist/_contracts/schemas/session-request-config.js +134 -0
- package/dist/_contracts/schemas/session-webhook.d.ts +11 -0
- package/dist/_contracts/schemas/session-webhook.js +38 -0
- package/dist/_contracts/schemas/side-effect-audit.d.ts +98 -0
- package/dist/_contracts/schemas/side-effect-audit.js +102 -0
- package/dist/_contracts/schemas/submission-assets.d.ts +117 -0
- package/dist/_contracts/schemas/submission-assets.js +147 -0
- package/dist/_contracts/schemas/submission-body.d.ts +251 -0
- package/dist/_contracts/schemas/submission-body.js +378 -0
- package/dist/_contracts/schemas/submission-environment.d.ts +79 -0
- package/dist/_contracts/schemas/submission-environment.js +179 -0
- package/dist/_contracts/schemas/submission-request.d.ts +158 -0
- package/dist/_contracts/schemas/submission-request.js +49 -0
- package/dist/_contracts/schemas/submission-secrets.d.ts +47 -0
- package/dist/_contracts/schemas/submission-secrets.js +108 -0
- package/dist/_contracts/schemas/wire.d.ts +118 -0
- package/dist/_contracts/schemas/wire.js +159 -0
- package/dist/_contracts/schemas/workspace-resources.d.ts +50 -0
- package/dist/_contracts/schemas/workspace-resources.js +87 -0
- package/dist/_contracts/sdk-errors.d.ts +33 -4
- package/dist/_contracts/sdk-errors.js +32 -2
- package/dist/_contracts/sdk-secrets.js +15 -0
- package/dist/_contracts/session-artifacts.d.ts +26 -4
- package/dist/_contracts/session-artifacts.js +36 -6
- package/dist/_contracts/session-config.d.ts +38 -29
- package/dist/_contracts/session-config.js +138 -299
- package/dist/_contracts/session-cost-types.d.ts +211 -0
- package/dist/_contracts/session-cost-types.js +69 -0
- package/dist/_contracts/session-cost.d.ts +3 -138
- package/dist/_contracts/session-cost.js +66 -70
- package/dist/_contracts/side-effect-audit.js +35 -20
- package/dist/_contracts/submission-limits.d.ts +61 -0
- package/dist/_contracts/submission-limits.js +60 -0
- package/dist/_contracts/submission.d.ts +23 -44
- package/dist/_contracts/submission.js +266 -721
- package/dist/_contracts/wire-observer.d.ts +49 -0
- package/dist/_contracts/wire-observer.js +34 -0
- package/dist/_contracts/workspace-resources.d.ts +8 -1
- package/dist/_contracts/workspace-resources.js +12 -23
- package/dist/asset-upload.js +25 -28
- package/dist/asset-upload.js.map +1 -1
- package/dist/cli.mjs +3885 -1069
- package/dist/cli.mjs.sha256 +1 -1
- package/dist/client-types.d.ts +37 -6
- package/dist/client.d.ts +8 -24
- package/dist/client.js +18 -31
- package/dist/client.js.map +1 -1
- package/dist/event-projection.d.ts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js.map +1 -1
- package/dist/mcp-server.js +7 -4
- package/dist/mcp-server.js.map +1 -1
- package/dist/retry.d.ts +29 -96
- package/dist/retry.js +33 -265
- package/dist/retry.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/docs/billing.md +56 -18
- package/docs/concepts/providers-and-runtimes.md +60 -21
- package/docs/defaults.md +6 -4
- package/docs/errors.md +34 -14
- package/docs/events.md +1 -1
- package/docs/files.md +2 -2
- package/docs/limits-and-quotas.md +16 -5
- package/docs/limits.md +3 -2
- package/docs/public-surface.json +1 -1
- package/docs/retries.md +8 -4
- package/docs/secrets.md +29 -13
- package/docs/session-record.md +1 -1
- package/docs/testing.md +2 -2
- package/package.json +5 -3
- package/dist/_contracts/allowed-keys.d.ts +0 -13
- package/dist/_contracts/allowed-keys.js +0 -18
- package/dist/_contracts/testing/fake-clock.d.ts +0 -60
- package/dist/_contracts/testing/fake-clock.js +0 -203
- package/dist/_contracts/testing/fake-object-stores.d.ts +0 -23
- package/dist/_contracts/testing/fake-object-stores.js +0 -46
- package/dist/_contracts/testing/fake-web-socket.d.ts +0 -36
- package/dist/_contracts/testing/fake-web-socket.js +0 -45
- package/dist/_contracts/testing/stub-env.d.ts +0 -15
- package/dist/_contracts/testing/stub-env.js +0 -52
- package/dist/_contracts/testing/test-platform.d.ts +0 -21
- package/dist/_contracts/testing/test-platform.js +0 -87
- package/dist/_contracts/testing/wait-for-condition.d.ts +0 -25
- package/dist/_contracts/testing/wait-for-condition.js +0 -42
- package/dist/_contracts/testing.d.ts +0 -14
- package/dist/_contracts/testing.js +0 -14
package/README.md
CHANGED
|
@@ -6,13 +6,15 @@ The TypeScript SDK and bundled CLI for durable aex agent sessions.
|
|
|
6
6
|
npm i @aexhq/sdk
|
|
7
7
|
```
|
|
8
8
|
|
|
9
|
+
Model access is managed: name a model by its `creator/model` gateway slug and
|
|
10
|
+
the platform's key routes it. You supply no provider API key.
|
|
11
|
+
|
|
9
12
|
```ts
|
|
10
|
-
import { Aex
|
|
13
|
+
import { Aex } from "@aexhq/sdk";
|
|
11
14
|
|
|
12
15
|
const aex = new Aex(process.env.AEX_API_KEY!);
|
|
13
16
|
const session = await aex.sessions.create({
|
|
14
|
-
model:
|
|
15
|
-
apiKeys: { anthropic: process.env.ANTHROPIC_API_KEY! }
|
|
17
|
+
model: "anthropic/claude-haiku-4-5"
|
|
16
18
|
});
|
|
17
19
|
|
|
18
20
|
const result = await session.messages.send("Summarize this repository.").finished();
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client operations for the ACCOUNT and WORKSPACE-MANAGEMENT surfaces: billing,
|
|
3
|
+
* the workspace secret store, and the control-plane orgs / workspaces / API
|
|
4
|
+
* keys / members.
|
|
5
|
+
*
|
|
6
|
+
* Split out of `operations.ts`, which keeps the session-lifecycle transport
|
|
7
|
+
* (create / message / stream / files / archive / workspace resources). The seam
|
|
8
|
+
* is the subject: nothing here touches a session, and every function below is
|
|
9
|
+
* re-exported from `operations.ts`, so the `operations` namespace on
|
|
10
|
+
* `@aexhq/contracts/internal` is exactly what it was.
|
|
11
|
+
*
|
|
12
|
+
* The same rule as its parent applies: every function takes an `HttpClient` so
|
|
13
|
+
* callers own auth + fetch injection, and workspace identity is derived
|
|
14
|
+
* server-side from the API key on the data plane. Control-plane calls pass org /
|
|
15
|
+
* workspace / key ids EXPLICITLY, because a control-plane principal spans many.
|
|
16
|
+
*/
|
|
17
|
+
import type { HttpClient } from "./http.js";
|
|
18
|
+
import { type IdempotencyOptions } from "./operation-core.js";
|
|
19
|
+
import type { ApiKeyRecord, BillingAutoTopupRequest, BillingAutoTopupUpdate, BillingHostedSession, BillingLedgerPage, BillingLedgerQuery, BillingPortalRequest, BillingSummary, BillingTopupCheckoutRequest, CreateApiKeyRequest, CreateOrgInviteRequest, CreateOrgRequest, CreateWorkspaceRequest, NewApiKey, NewWorkspace, OrgInvite, OrgMemberRecord, OrgRecord, SecretRecord, WebhookSigningSecret, WorkspaceRecord } from "./account-types.js";
|
|
20
|
+
/**
|
|
21
|
+
* Read the workspace billing summary (`GET /api/billing`, scope `billing:read`):
|
|
22
|
+
* prepaid balance, current-month spend, spend cap, the free monthly allowances,
|
|
23
|
+
* auto-recharge settings and the saved card.
|
|
24
|
+
*/
|
|
25
|
+
export declare function getBilling(http: HttpClient): Promise<BillingSummary>;
|
|
26
|
+
/**
|
|
27
|
+
* Buy prepaid credit (`POST /api/billing/topup/checkout`). Returns only the
|
|
28
|
+
* hosted URL; the balance moves after the charge settles, not when this
|
|
29
|
+
* resolves. The same flow captures the card on first use.
|
|
30
|
+
*/
|
|
31
|
+
export declare function createBillingTopupCheckout(http: HttpClient, request: BillingTopupCheckoutRequest, options?: IdempotencyOptions): Promise<BillingHostedSession>;
|
|
32
|
+
/**
|
|
33
|
+
* Update auto-recharge (`PATCH /api/billing/autotopup`). Omitted fields keep
|
|
34
|
+
* their stored value; the response echoes the stored settings.
|
|
35
|
+
*
|
|
36
|
+
* No idempotency key: this is a whole-state PATCH, so a replay writes the same
|
|
37
|
+
* row. The body guard stays — an `idempotencyKey` in the body was never a
|
|
38
|
+
* request field and silently ignoring it would look like it worked.
|
|
39
|
+
*/
|
|
40
|
+
export declare function updateBillingAutoTopup(http: HttpClient, request: BillingAutoTopupRequest): Promise<BillingAutoTopupUpdate>;
|
|
41
|
+
/**
|
|
42
|
+
* Create a hosted billing-portal session for the workspace customer.
|
|
43
|
+
* Returns only the hosted URL.
|
|
44
|
+
*/
|
|
45
|
+
export declare function createBillingPortal(http: HttpClient, request?: BillingPortalRequest, options?: IdempotencyOptions): Promise<BillingHostedSession>;
|
|
46
|
+
/**
|
|
47
|
+
* Read recent workspace credit-ledger rows (`GET /api/billing/ledger`, scope
|
|
48
|
+
* `billing:read`), newest first. `limit` is clamped server-side to [1, 100]
|
|
49
|
+
* (default 25); the read is not cursor-paged.
|
|
50
|
+
*/
|
|
51
|
+
export declare function getBillingLedger(http: HttpClient, query?: BillingLedgerQuery): Promise<BillingLedgerPage>;
|
|
52
|
+
/**
|
|
53
|
+
* Reveal the workspace webhook signing secret (`POST /api/webhook/signing-secret`),
|
|
54
|
+
* CREATING one on first use. Repeat calls return the same `whsec_<base64>` value —
|
|
55
|
+
* the hosted API does not rotate it. POST (not GET) so a reveal is a logged action.
|
|
56
|
+
* Pass the returned `whsec` to `verifyAexWebhook` as `secret`.
|
|
57
|
+
*/
|
|
58
|
+
export declare function getWebhookSigningSecret(http: HttpClient): Promise<WebhookSigningSecret>;
|
|
59
|
+
/** Create a named workspace secret. The value travels in the body. */
|
|
60
|
+
export declare function createSecret(http: HttpClient, args: {
|
|
61
|
+
readonly name: string;
|
|
62
|
+
readonly value: string;
|
|
63
|
+
}): Promise<SecretRecord>;
|
|
64
|
+
export declare function listSecrets(http: HttpClient): Promise<readonly SecretRecord[]>;
|
|
65
|
+
/** Metadata for one workspace secret by name. Never returns the value. */
|
|
66
|
+
export declare function getSecret(http: HttpClient, name: string): Promise<SecretRecord>;
|
|
67
|
+
/** Replace the value of an existing workspace secret; bumps its version. */
|
|
68
|
+
export declare function rotateSecret(http: HttpClient, args: {
|
|
69
|
+
readonly name: string;
|
|
70
|
+
readonly value: string;
|
|
71
|
+
}): Promise<SecretRecord>;
|
|
72
|
+
export declare function deleteSecret(http: HttpClient, name: string): Promise<void>;
|
|
73
|
+
/** Create an org (the caller becomes its admin). `POST /api/orgs`. */
|
|
74
|
+
export declare function createOrg(http: HttpClient, request: CreateOrgRequest): Promise<OrgRecord>;
|
|
75
|
+
/** List the orgs the caller belongs to. `GET /api/orgs`. */
|
|
76
|
+
export declare function listOrgs(http: HttpClient): Promise<readonly OrgRecord[]>;
|
|
77
|
+
/** List an org's members (and pending invites, as `status: "pending"`). `GET /api/orgs/:orgId/members`. */
|
|
78
|
+
export declare function listOrgMembers(http: HttpClient, orgId: string): Promise<readonly OrgMemberRecord[]>;
|
|
79
|
+
/** Invite an email to an org at a role. `POST /api/orgs/:orgId/invites`. */
|
|
80
|
+
export declare function createOrgInvite(http: HttpClient, orgId: string, request: CreateOrgInviteRequest): Promise<OrgInvite>;
|
|
81
|
+
/**
|
|
82
|
+
* Create a workspace under an org and mint its FIRST workspace-scoped API key,
|
|
83
|
+
* returned once as {@link NewWorkspace}. `POST /api/workspaces`. The free tier
|
|
84
|
+
* caps at 3 workspaces per org (the server surfaces a 409 when exceeded).
|
|
85
|
+
*/
|
|
86
|
+
export declare function createWorkspace(http: HttpClient, request: CreateWorkspaceRequest): Promise<NewWorkspace>;
|
|
87
|
+
/** List the workspaces the caller can manage across their orgs. `GET /api/workspaces`. */
|
|
88
|
+
export declare function listWorkspaces(http: HttpClient): Promise<readonly WorkspaceRecord[]>;
|
|
89
|
+
/** Delete a workspace by id. `DELETE /api/workspaces/:id`. Idempotent. */
|
|
90
|
+
export declare function deleteWorkspace(http: HttpClient, workspaceId: string): Promise<void>;
|
|
91
|
+
/**
|
|
92
|
+
* Mint an API key, returned once as {@link NewApiKey}. `POST /api/keys`. Pass
|
|
93
|
+
* `workspaceId` for a data-plane workspace key, or `account: true` for an
|
|
94
|
+
* account PAT (control-plane). A PAT cannot mint another PAT (anti-escalation,
|
|
95
|
+
* enforced server-side).
|
|
96
|
+
*/
|
|
97
|
+
export declare function createApiKey(http: HttpClient, request?: CreateApiKeyRequest): Promise<NewApiKey>;
|
|
98
|
+
/** List API keys (metadata only; never values). `GET /api/keys`. */
|
|
99
|
+
export declare function listApiKeys(http: HttpClient): Promise<readonly ApiKeyRecord[]>;
|
|
100
|
+
/** Revoke/delete an API key by id. `DELETE /api/keys/:id`. Idempotent. */
|
|
101
|
+
export declare function deleteApiKey(http: HttpClient, keyId: string): Promise<void>;
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
import { SessionStateError } from "./sdk-errors.js";
|
|
2
|
+
import { isRecord } from "./value-guards.js";
|
|
3
|
+
import { configError, resolveIdempotencyKey } from "./operation-core.js";
|
|
4
|
+
/**
|
|
5
|
+
* Read the workspace billing summary (`GET /api/billing`, scope `billing:read`):
|
|
6
|
+
* prepaid balance, current-month spend, spend cap, the free monthly allowances,
|
|
7
|
+
* auto-recharge settings and the saved card.
|
|
8
|
+
*/
|
|
9
|
+
export async function getBilling(http) {
|
|
10
|
+
return http.request("/api/billing");
|
|
11
|
+
}
|
|
12
|
+
function rejectBodyIdempotencyKey(request) {
|
|
13
|
+
if (isRecord(request) && Object.prototype.hasOwnProperty.call(request, "idempotencyKey")) {
|
|
14
|
+
throw configError("idempotencyKey", "billing idempotencyKey belongs in the second options argument, not the request body");
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
function resolveBillingIdempotencyKey(request, options) {
|
|
18
|
+
rejectBodyIdempotencyKey(request);
|
|
19
|
+
const idempotencyKey = resolveIdempotencyKey(options?.idempotencyKey);
|
|
20
|
+
return idempotencyKey;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Buy prepaid credit (`POST /api/billing/topup/checkout`). Returns only the
|
|
24
|
+
* hosted URL; the balance moves after the charge settles, not when this
|
|
25
|
+
* resolves. The same flow captures the card on first use.
|
|
26
|
+
*/
|
|
27
|
+
export async function createBillingTopupCheckout(http, request, options) {
|
|
28
|
+
const idempotencyKey = resolveBillingIdempotencyKey(request, options);
|
|
29
|
+
return http.request("/api/billing/topup/checkout", {
|
|
30
|
+
method: "POST",
|
|
31
|
+
headers: { "Idempotency-Key": idempotencyKey },
|
|
32
|
+
body: JSON.stringify(request)
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Update auto-recharge (`PATCH /api/billing/autotopup`). Omitted fields keep
|
|
37
|
+
* their stored value; the response echoes the stored settings.
|
|
38
|
+
*
|
|
39
|
+
* No idempotency key: this is a whole-state PATCH, so a replay writes the same
|
|
40
|
+
* row. The body guard stays — an `idempotencyKey` in the body was never a
|
|
41
|
+
* request field and silently ignoring it would look like it worked.
|
|
42
|
+
*/
|
|
43
|
+
export async function updateBillingAutoTopup(http, request) {
|
|
44
|
+
rejectBodyIdempotencyKey(request);
|
|
45
|
+
return http.request("/api/billing/autotopup", {
|
|
46
|
+
method: "PATCH",
|
|
47
|
+
body: JSON.stringify(request)
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Create a hosted billing-portal session for the workspace customer.
|
|
52
|
+
* Returns only the hosted URL.
|
|
53
|
+
*/
|
|
54
|
+
export async function createBillingPortal(http, request = {}, options) {
|
|
55
|
+
const idempotencyKey = resolveBillingIdempotencyKey(request, options);
|
|
56
|
+
return http.request("/api/billing/portal", {
|
|
57
|
+
method: "POST",
|
|
58
|
+
headers: { "Idempotency-Key": idempotencyKey },
|
|
59
|
+
body: JSON.stringify(request)
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Read recent workspace credit-ledger rows (`GET /api/billing/ledger`, scope
|
|
64
|
+
* `billing:read`), newest first. `limit` is clamped server-side to [1, 100]
|
|
65
|
+
* (default 25); the read is not cursor-paged.
|
|
66
|
+
*/
|
|
67
|
+
export async function getBillingLedger(http, query) {
|
|
68
|
+
const params = {};
|
|
69
|
+
if (query?.limit !== undefined)
|
|
70
|
+
params.limit = String(query.limit);
|
|
71
|
+
return http.request("/api/billing/ledger", {}, params);
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Reveal the workspace webhook signing secret (`POST /api/webhook/signing-secret`),
|
|
75
|
+
* CREATING one on first use. Repeat calls return the same `whsec_<base64>` value —
|
|
76
|
+
* the hosted API does not rotate it. POST (not GET) so a reveal is a logged action.
|
|
77
|
+
* Pass the returned `whsec` to `verifyAexWebhook` as `secret`.
|
|
78
|
+
*/
|
|
79
|
+
export async function getWebhookSigningSecret(http) {
|
|
80
|
+
return http.request("/api/webhook/signing-secret", { method: "POST" });
|
|
81
|
+
}
|
|
82
|
+
// ===========================================================================
|
|
83
|
+
// Workspace secret operations
|
|
84
|
+
//
|
|
85
|
+
// Value-bearing requests (create/rotate) carry the value in the JSON BODY,
|
|
86
|
+
// never the URL/query, so it never lands in logs or the request line. Reads
|
|
87
|
+
// return metadata only; persisted secret values are write-only through this API.
|
|
88
|
+
// ===========================================================================
|
|
89
|
+
/** Create a named workspace secret. The value travels in the body. */
|
|
90
|
+
export async function createSecret(http, args) {
|
|
91
|
+
const result = await http.request("/api/secrets", {
|
|
92
|
+
method: "POST",
|
|
93
|
+
body: JSON.stringify({ name: args.name, value: args.value })
|
|
94
|
+
});
|
|
95
|
+
return unwrapSecret(result);
|
|
96
|
+
}
|
|
97
|
+
export async function listSecrets(http) {
|
|
98
|
+
const result = await http.request("/api/secrets");
|
|
99
|
+
if (!isRecord(result) || !Array.isArray(result.secrets)) {
|
|
100
|
+
throw new SessionStateError("workspace secrets response must contain a secrets array");
|
|
101
|
+
}
|
|
102
|
+
return result.secrets;
|
|
103
|
+
}
|
|
104
|
+
/** Metadata for one workspace secret by name. Never returns the value. */
|
|
105
|
+
export async function getSecret(http, name) {
|
|
106
|
+
const result = await http.request(`/api/secrets/${encodeURIComponent(name)}`);
|
|
107
|
+
return unwrapSecret(result);
|
|
108
|
+
}
|
|
109
|
+
/** Replace the value of an existing workspace secret; bumps its version. */
|
|
110
|
+
export async function rotateSecret(http, args) {
|
|
111
|
+
const result = await http.request(`/api/secrets/${encodeURIComponent(args.name)}/rotate`, { method: "POST", body: JSON.stringify({ value: args.value }) });
|
|
112
|
+
return unwrapSecret(result);
|
|
113
|
+
}
|
|
114
|
+
export async function deleteSecret(http, name) {
|
|
115
|
+
await http.request(`/api/secrets/${encodeURIComponent(name)}`, {
|
|
116
|
+
method: "DELETE"
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
function unwrapSecret(result) {
|
|
120
|
+
if (!isRecord(result) || !isRecord(result.secret)) {
|
|
121
|
+
throw new SessionStateError("workspace secret response must contain a secret object");
|
|
122
|
+
}
|
|
123
|
+
return result.secret;
|
|
124
|
+
}
|
|
125
|
+
// ===========================================================================
|
|
126
|
+
// Control-plane operations (orgs / workspaces / API keys / members)
|
|
127
|
+
//
|
|
128
|
+
// These target the ACCOUNT/control-plane surface on the dashboard BFF — reached
|
|
129
|
+
// with an account PAT / device session, NOT a data-plane workspace key. They
|
|
130
|
+
// mirror the publish/list/get/delete generic and the value-bearing one-time
|
|
131
|
+
// reveal shapes above (create returns the key exactly once). Endpoints:
|
|
132
|
+
// orgs: POST/GET /api/orgs, GET /api/orgs/:orgId/members,
|
|
133
|
+
// POST /api/orgs/:orgId/invites
|
|
134
|
+
// workspaces: POST/GET /api/workspaces, DELETE /api/workspaces/:id
|
|
135
|
+
// keys: POST/GET /api/keys, DELETE /api/keys/:id
|
|
136
|
+
// Workspace/org identity is passed EXPLICITLY here (unlike the data plane, which
|
|
137
|
+
// derives the workspace from the key) because a control-plane principal spans
|
|
138
|
+
// multiple orgs and workspaces.
|
|
139
|
+
// ===========================================================================
|
|
140
|
+
/** Create an org (the caller becomes its admin). `POST /api/orgs`. */
|
|
141
|
+
export async function createOrg(http, request) {
|
|
142
|
+
const result = await http.request("/api/orgs", {
|
|
143
|
+
method: "POST",
|
|
144
|
+
body: JSON.stringify(request)
|
|
145
|
+
});
|
|
146
|
+
return unwrapControlRecord(result, "org", "org");
|
|
147
|
+
}
|
|
148
|
+
/** List the orgs the caller belongs to. `GET /api/orgs`. */
|
|
149
|
+
export async function listOrgs(http) {
|
|
150
|
+
return listControlRecords(http, "/api/orgs", "orgs");
|
|
151
|
+
}
|
|
152
|
+
/** List an org's members (and pending invites, as `status: "pending"`). `GET /api/orgs/:orgId/members`. */
|
|
153
|
+
export async function listOrgMembers(http, orgId) {
|
|
154
|
+
requireControlId(orgId, "orgId", "listOrgMembers");
|
|
155
|
+
return listControlRecords(http, `/api/orgs/${encodeURIComponent(orgId)}/members`, "members");
|
|
156
|
+
}
|
|
157
|
+
/** Invite an email to an org at a role. `POST /api/orgs/:orgId/invites`. */
|
|
158
|
+
export async function createOrgInvite(http, orgId, request) {
|
|
159
|
+
requireControlId(orgId, "orgId", "createOrgInvite");
|
|
160
|
+
const result = await http.request(`/api/orgs/${encodeURIComponent(orgId)}/invites`, { method: "POST", body: JSON.stringify(request) });
|
|
161
|
+
return unwrapControlRecord(result, "invite", "org invite");
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Create a workspace under an org and mint its FIRST workspace-scoped API key,
|
|
165
|
+
* returned once as {@link NewWorkspace}. `POST /api/workspaces`. The free tier
|
|
166
|
+
* caps at 3 workspaces per org (the server surfaces a 409 when exceeded).
|
|
167
|
+
*/
|
|
168
|
+
export async function createWorkspace(http, request) {
|
|
169
|
+
const result = await http.request("/api/workspaces", {
|
|
170
|
+
method: "POST",
|
|
171
|
+
body: JSON.stringify(request)
|
|
172
|
+
});
|
|
173
|
+
const workspace = unwrapControlRecord(result, "workspace", "new workspace");
|
|
174
|
+
if (typeof workspace.workspaceId !== "string" || workspace.workspaceId.length === 0) {
|
|
175
|
+
throw new SessionStateError("createWorkspace response is missing workspaceId");
|
|
176
|
+
}
|
|
177
|
+
if (typeof workspace.apiKey !== "string" || workspace.apiKey.length === 0) {
|
|
178
|
+
throw new SessionStateError("createWorkspace response is missing the one-time apiKey");
|
|
179
|
+
}
|
|
180
|
+
return workspace;
|
|
181
|
+
}
|
|
182
|
+
/** List the workspaces the caller can manage across their orgs. `GET /api/workspaces`. */
|
|
183
|
+
export async function listWorkspaces(http) {
|
|
184
|
+
return listControlRecords(http, "/api/workspaces", "workspaces");
|
|
185
|
+
}
|
|
186
|
+
/** Delete a workspace by id. `DELETE /api/workspaces/:id`. Idempotent. */
|
|
187
|
+
export async function deleteWorkspace(http, workspaceId) {
|
|
188
|
+
requireControlId(workspaceId, "workspaceId", "deleteWorkspace");
|
|
189
|
+
await http.request(`/api/workspaces/${encodeURIComponent(workspaceId)}`, { method: "DELETE" });
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Mint an API key, returned once as {@link NewApiKey}. `POST /api/keys`. Pass
|
|
193
|
+
* `workspaceId` for a data-plane workspace key, or `account: true` for an
|
|
194
|
+
* account PAT (control-plane). A PAT cannot mint another PAT (anti-escalation,
|
|
195
|
+
* enforced server-side).
|
|
196
|
+
*/
|
|
197
|
+
export async function createApiKey(http, request = {}) {
|
|
198
|
+
if (request.account === true && request.workspaceId !== undefined) {
|
|
199
|
+
throw configError("account", "createApiKey: pass either workspaceId or account:true, not both");
|
|
200
|
+
}
|
|
201
|
+
const result = await http.request("/api/keys", {
|
|
202
|
+
method: "POST",
|
|
203
|
+
body: JSON.stringify(request)
|
|
204
|
+
});
|
|
205
|
+
const key = unwrapControlRecord(result, "key", "new api key");
|
|
206
|
+
if (typeof key.id !== "string" || key.id.length === 0) {
|
|
207
|
+
throw new SessionStateError("createApiKey response is missing the key id");
|
|
208
|
+
}
|
|
209
|
+
if (typeof key.apiKey !== "string" || key.apiKey.length === 0) {
|
|
210
|
+
throw new SessionStateError("createApiKey response is missing the one-time apiKey");
|
|
211
|
+
}
|
|
212
|
+
return key;
|
|
213
|
+
}
|
|
214
|
+
/** List API keys (metadata only; never values). `GET /api/keys`. */
|
|
215
|
+
export async function listApiKeys(http) {
|
|
216
|
+
return listControlRecords(http, "/api/keys", "keys");
|
|
217
|
+
}
|
|
218
|
+
/** Revoke/delete an API key by id. `DELETE /api/keys/:id`. Idempotent. */
|
|
219
|
+
export async function deleteApiKey(http, keyId) {
|
|
220
|
+
requireControlId(keyId, "keyId", "deleteApiKey");
|
|
221
|
+
await http.request(`/api/keys/${encodeURIComponent(keyId)}`, { method: "DELETE" });
|
|
222
|
+
}
|
|
223
|
+
function requireControlId(value, field, context) {
|
|
224
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
225
|
+
throw configError(field, `${context}: ${field} must be a non-empty string`);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
/** Unwrap a `{ <key>: T }` single-record control-plane envelope, validating shape. */
|
|
229
|
+
function unwrapControlRecord(result, key, label) {
|
|
230
|
+
if (!isRecord(result) || !isRecord(result[key])) {
|
|
231
|
+
throw new SessionStateError(`${label} response must contain a ${key} object`);
|
|
232
|
+
}
|
|
233
|
+
return result[key];
|
|
234
|
+
}
|
|
235
|
+
/** Unwrap a `{ <key>: T[] }` list control-plane envelope, validating shape. */
|
|
236
|
+
async function listControlRecords(http, path, key) {
|
|
237
|
+
const result = await http.request(path);
|
|
238
|
+
if (!isRecord(result) || !Array.isArray(result[key])) {
|
|
239
|
+
throw new SessionStateError(`${path} response must contain a ${key} array`);
|
|
240
|
+
}
|
|
241
|
+
return result[key];
|
|
242
|
+
}
|