@k9o/core 1.1.1 → 1.3.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/dist/types.d.ts CHANGED
@@ -27,7 +27,7 @@
27
27
  * canonical org subdomain `https://<orgSlug>.api.k9o.io`. Pass an explicit host to override — an explicit `<orgSlug>` subdomain,
28
28
  * or a self-hosted / Sovereign-Control host, or a dev host — used as-is.
29
29
  * - `fetch` — custom fetch implementation (defaults to global)
30
- * - `retryConfig` — retry/backoff config (reserved; currently ships single-attempt)
30
+ * - `retryConfig` — retry/backoff config for idempotent requests
31
31
  * - `defaultHeaders` — headers merged into every request
32
32
  */
33
33
  type CreateClientOptions = {
@@ -40,8 +40,8 @@ type CreateClientOptions = {
40
40
  defaultHeaders?: Record<string, string> | undefined;
41
41
  };
42
42
  /**
43
- * Retry config. The type is reserved; the HTTP client currently makes a
44
- * single attempt per request and does not yet honour this config.
43
+ * Retry config. Only GET, HEAD, OPTIONS and DELETE are retried, on network failure and on
44
+ * HTTP 429, 502, 503 and 504.
45
45
  */
46
46
  type RetryConfig = {
47
47
  maxRetries: number;
@@ -2,12 +2,15 @@
2
2
  * URL grammar.
3
3
  *
4
4
  * Canonical URL shapes:
5
- * - Member-plane (project-scoped): `<orgSlug>.api.k9o.io/@<projectSlug>/<sub-resource>/...`
6
- * - Org-plane (org-scoped): `<orgSlug>.api.k9o.io/<resource>/...`
7
- * - Platform-plane (cross-org): `api.k9o.io/discovery/...` + `api.k9o.io/auth/...` etc.
5
+ * - Member-plane (project-scoped): `<orgSlug>.api.<apex>/@<projectSlug>/<sub-resource>/...`
6
+ * - Org-plane (org-scoped): `<orgSlug>.api.<apex>/<resource>/...`
7
+ * - Platform-plane (cross-org): `api.<apex>/discovery/...` + `api.<apex>/auth/...` etc.
8
+ *
9
+ * `<apex>` is whatever the deployment uses (public cloud, internal, air-gapped, localhost);
10
+ * the SDK takes the host from the baseUrl it is given and never matches it against a list.
8
11
  *
9
12
  * The `baseUrl` passed to `createClient` is the gateway host root —
10
- * scheme + host (+ optional port). Path-bearing baseUrls
13
+ * scheme + host (+ optional port) Path-bearing baseUrls
11
14
  * are rejected with `K9O_INVALID_BASE_URL`.
12
15
  *
13
16
  * The SDK constructs URLs by composing `baseUrl + path-from-builder`.
@@ -32,18 +35,31 @@
32
35
  * - query / fragment (`https://api.k9o.io?x=1`)
33
36
  */
34
37
  declare function validateBaseUrl(baseUrl: string): string;
38
+ /** The convenience default when the caller passes no baseUrl. A default value only: never used to parse a host. */
39
+ declare const DEFAULT_BASE_URL = "https://api.k9o.io";
40
+ /** The structure of a gateway API host, read from the host itself. */
41
+ type ApiHost = {
42
+ /** First label of an org host (`acme` in `acme.api.corp.internal`); null for a platform host (`api.corp.internal`). */
43
+ orgSlug: string | null;
44
+ /** `api` or `api-staging`: the label that marks a gateway host. */
45
+ family: "api" | "api-staging";
46
+ /** 4-character region code when the host is regional (`api-de01`), else null (region-free). */
47
+ region: string | null;
48
+ /** Everything after the gateway label (`corp.internal`, `k9o.io`): the apex, never matched against a list. */
49
+ apex: string;
50
+ };
51
+ /**
52
+ * Read the org slug and region out of a gateway host without knowing its apex. The gateway label is `api` or
53
+ * `api-staging`, optionally followed by `-<rgn>`; the label before it is the org slug and whatever follows it is the
54
+ * apex. Returns null for any other shape (localhost, an IP, a single-label host, a host with no gateway label), which
55
+ * callers use exactly as given.
56
+ */
57
+ declare function parseApiHost(host: string): ApiHost | null;
35
58
  /**
36
- * Resolve the effective baseUrl. `baseUrl` is
37
- * OPTIONAL — it defaults to the platform apex `https://api.k9o.io`. When the
38
- * (validated) host is a bare apex in `K9O_ORG_DERIVABLE_APEXES` and `orgSlug`
39
- * is supplied, the SDK composes the canonical org-subdomain wire root
40
- * `https://<orgSlug>.<apex>`. An explicit `<orgSlug>` subdomain, a
41
- * self-hosted / Sovereign host, or a dev host (anything not in that set)
42
- * passes through unchanged — `orgSlug` is intentionally NOT validated or
43
- * composed onto a non-apex host (fixed 2026-08-30: this used to silently
44
- * ignore `orgSlug` for `api-staging.k9o.io` too, not just genuine
45
- * self-hosted/custom hosts, since only the literal production apex string
46
- * was checked).
59
+ * Resolve the effective baseUrl. `baseUrl` is OPTIONAL: omitted, it defaults to {@link DEFAULT_BASE_URL} (the public
60
+ * cloud, a convenience only). When the (validated) host is a platform host (`api.<apex>` or `api-staging.<apex>`, any
61
+ * apex) and `orgSlug` is supplied, the canonical org host `<orgSlug>.<host>` is composed. Any other host (an org host
62
+ * already, an internal or air-gapped host, localhost) passes through unchanged; `orgSlug` is then only sent as a header.
47
63
  */
48
64
  declare function resolveBaseUrl(baseUrl: string | undefined, orgSlug?: string): string;
49
65
  declare function validateSlug(slug: string, kind: "org" | "project"): void;
@@ -72,4 +88,4 @@ declare function orgPath(resource: string): string;
72
88
  */
73
89
  declare function platformPath(resource: string): string;
74
90
 
75
- export { orgPath, platformPath, projectPath, resolveBaseUrl, validateBaseUrl, validateSlug };
91
+ export { type ApiHost, DEFAULT_BASE_URL, orgPath, parseApiHost, platformPath, projectPath, resolveBaseUrl, validateBaseUrl, validateSlug };
@@ -1,14 +1,18 @@
1
1
  import {
2
+ DEFAULT_BASE_URL,
2
3
  orgPath,
4
+ parseApiHost,
3
5
  platformPath,
4
6
  projectPath,
5
7
  resolveBaseUrl,
6
8
  validateBaseUrl,
7
9
  validateSlug
8
- } from "./chunk-2NDFTHUB.js";
10
+ } from "./chunk-UL53GNYM.js";
9
11
  import "./chunk-LJZ45UA2.js";
10
12
  export {
13
+ DEFAULT_BASE_URL,
11
14
  orgPath,
15
+ parseApiHost,
12
16
  platformPath,
13
17
  projectPath,
14
18
  resolveBaseUrl,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@k9o/core",
3
- "version": "1.1.1",
3
+ "version": "1.3.0",
4
4
  "description": "Kilimanjaro platform SDK — createClient + data-plane + projects + migrations + functions + studio (workflows/dashboards) + compute.llms + realtime subscriptions + KError + meta accessor sugar. Composes with @k9o/gate (auth) and @k9o/extensions (extension consumption).",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -60,9 +60,9 @@
60
60
  "url": "https://github.com/k9o-dev/sdk-typescript"
61
61
  },
62
62
  "dependencies": {
63
- "@k9o/gate": "1.1.1",
64
- "@k9o/extensions": "1.1.1",
65
- "@k9o/kibo": "1.1.1"
63
+ "@k9o/gate": "1.3.0",
64
+ "@k9o/extensions": "1.3.0",
65
+ "@k9o/kibo": "1.3.0"
66
66
  },
67
67
  "devDependencies": {
68
68
  "bun-types": "^1.1.0",
package/src/audit.ts CHANGED
@@ -4,25 +4,21 @@
4
4
  * Backed by the gateway audit-log routes.
5
5
  *
6
6
  * Wire mapping:
7
- * - `query({ since, until, type, actorId, targetKind, targetId, limit, cursor })`
7
+ * - `query({ eventType, actorId, after, before, limit, cursor })`
8
8
  * → GET /@<slug>/audit-log?...
9
9
  *
10
- * The endpoint accepts cursor-paginated reverse-chronological queries.
11
- * `type` is the audit-event namespace (e.g. `data.write.access.insert`,
12
- * `gate.signin.success`) per the event-name taxonomy +
13
- * `feedback_error_code_namespace_boundary_and_prefix.md` cement.
10
+ * Option names are the gateway's query parameter names. The endpoint
11
+ * accepts cursor-paginated reverse-chronological queries. `eventType` is
12
+ * the audit-event name (e.g. `data.write.access.insert`).
14
13
  *
15
- * Returns audit events with full context payload — actorId / targetKind /
16
- * targetId / payload / occurredAt / requestId / traceId.
14
+ * Returns audit events with full context payload.
17
15
  */
18
16
 
19
17
  import { request, type HttpClientConfig } from "./http.ts";
20
18
  import { projectPath } from "./url-grammar.ts";
21
19
  import type { Envelope } from "./envelope.ts";
22
20
 
23
- /**
24
- * Audit event record `_sys.audit_log` shape.
25
- */
21
+ /** Audit event record. */
26
22
  export type AuditEvent = {
27
23
  id: string;
28
24
  /** Dotted namespace (e.g. `data.write.access.insert`). */
@@ -37,18 +33,14 @@ export type AuditEvent = {
37
33
  };
38
34
 
39
35
  export type AuditQueryOpts = {
40
- /** ISO 8601 lower bound (inclusive). */
41
- since?: string | undefined;
42
- /** ISO 8601 upper bound (exclusive). */
43
- until?: string | undefined;
44
- /** Filter by event type (exact match; future cycles may add prefix-match). */
45
- type?: string | undefined;
36
+ /** Filter by event type. */
37
+ eventType?: string | undefined;
46
38
  /** Filter by actor. */
47
39
  actorId?: string | undefined;
48
- /** Filter by target resource kind. */
49
- targetKind?: string | undefined;
50
- /** Filter by target resource id. */
51
- targetId?: string | undefined;
40
+ /** ISO 8601 lower bound: only events after this instant. */
41
+ after?: string | undefined;
42
+ /** ISO 8601 upper bound: only events before this instant. */
43
+ before?: string | undefined;
52
44
  /** Page size (per-tier max bounded server-side). */
53
45
  limit?: number | undefined;
54
46
  /** Cursor from prior response. */
@@ -68,9 +60,8 @@ export function makeAuditNamespace(config: HttpClientConfig) {
68
60
  * Query the audit log. Returns reverse-chronological events matching
69
61
  * all supplied filters (AND composition).
70
62
  *
71
- * Per RBAC: requires `AUDIT_READER` role minimum; Custodian-
72
- * class roles inherit. Project-level audit is project-scoped (this
73
- * endpoint); platform-level audit is admin-only (separate surface).
63
+ * Requires the `AUDIT_READER` role at minimum. This endpoint is
64
+ * project-scoped.
74
65
  */
75
66
  async query(opts?: AuditQueryOpts): Promise<Envelope<ReadonlyArray<AuditEvent>>> {
76
67
  const slug = requireProjectSlug(config);
@@ -83,8 +74,7 @@ export function makeAuditNamespace(config: HttpClientConfig) {
83
74
 
84
75
  /**
85
76
  * Convenience: live-tail recent events. Currently single-shot read
86
- * with `since` defaulted to "1 minute ago"; subscription-mode tail
87
- * (SSE) lands when the streaming layer ships pattern.
77
+ * with `after` defaulted to "1 minute ago".
88
78
  */
89
79
  async tail(opts?: { sinceMinutes?: number; limit?: number }): Promise<Envelope<ReadonlyArray<AuditEvent>>> {
90
80
  const slug = requireProjectSlug(config);
@@ -93,7 +83,7 @@ export function makeAuditNamespace(config: HttpClientConfig) {
93
83
  return request(config, {
94
84
  method: "GET",
95
85
  path: projectPath(slug, "audit-log"),
96
- query: { since, limit: opts?.limit ?? 100 },
86
+ query: { after: since, limit: opts?.limit ?? 100 },
97
87
  });
98
88
  },
99
89
  };
@@ -1,20 +1,20 @@
1
1
  /**
2
2
  * Compute LLMs SDK — `client.compute.llms.*`.
3
3
  *
4
- * Compute LLMs registry. Curated 9-model library
5
- * + 4-endpoint family + per-model tier-gating.
4
+ * Compute LLMs registry: a curated model library, four endpoints and
5
+ * per-model tier gating.
6
6
  *
7
- * Wire mapping (project-plane;):
7
+ * Wire mapping (project-plane):
8
8
  * - list() → GET /@<slug>/compute/llms
9
9
  * - read(modelId) → GET /@<slug>/compute/llms/<model-id>
10
10
  * - invoke(modelId, ...) → POST /@<slug>/compute/llms/<model-id>/invoke
11
11
  * - invokeStream(modelId,...) → POST /@<slug>/compute/llms/<model-id>/invoke-stream (SSE)
12
12
  *
13
- * Per RBAC: list/read against all cohort roles; invoke +
14
- * invokeStream require PROJECT_AUTHOR or PROJECT_ADMIN (LLM cost gate).
15
- * Per, surfaces K9O_LLM_PROVIDER_UNAVAILABLE (503) +
16
- * reuses K9O_MODEL_NOT_DECLARED / K9O_TIER_FEATURE_GATED /
17
- * K9O_AGENT_INFERENCE_TIMEOUT.
13
+ * RBAC: list/read against all cohort roles; invoke + invokeStream require
14
+ * PROJECT_AUTHOR or PROJECT_ADMIN (LLM cost gate). External-provider models
15
+ * answer 501 `K9O_NOT_IMPLEMENTED` until the provider client is built.
16
+ * Surfaces K9O_LLM_PROVIDER_UNAVAILABLE (503), K9O_MODEL_NOT_DECLARED and
17
+ * K9O_TIER_FEATURE_GATED.
18
18
  */
19
19
 
20
20
  import { request, requestStream, type HttpClientConfig } from "../http.ts";
@@ -22,7 +22,7 @@ import { projectPath } from "../url-grammar.ts";
22
22
  import type { Envelope } from "../envelope.ts";
23
23
  import { parseSseStream } from "../internal/sse.ts";
24
24
 
25
- /** Curated 9-model closed enum at v2 launch (extensible v2.x). */
25
+ /** Curated model ids. */
26
26
  export type LLMModelId =
27
27
  | "mistral-7b"
28
28
  | "mistral-8x7b"
@@ -37,7 +37,7 @@ export type LLMModelId =
37
37
  export type LLMProviderClass = "self-hosted" | "external";
38
38
  export type LLMModality = "text-completion" | "text-completion+tool-use" | "embeddings";
39
39
 
40
- /** Model metadata + (cost transparency). */
40
+ /** Model metadata with per-model cost. */
41
41
  export type LLMModel = {
42
42
  id: LLMModelId;
43
43
  providerClass: LLMProviderClass;
@@ -46,35 +46,54 @@ export type LLMModel = {
46
46
  tierGate: string;
47
47
  contextWindow: number;
48
48
  capabilities: ReadonlyArray<string>;
49
- /** Per-model token cost for cost-transparency. */
49
+ /** Per-model token cost. */
50
50
  costPerMillionInputTokens: number;
51
51
  costPerMillionOutputTokens: number;
52
52
  };
53
53
 
54
- /** Inference parameters (passed through to provider). */
54
+ /** Inference parameters the route reads; sent flat in the request body. */
55
55
  export type LLMInvokeParams = {
56
+ /** 1 to 32000. */
56
57
  maxTokens?: number;
58
+ /** 0 to 2. */
57
59
  temperature?: number;
58
- topP?: number;
59
- stop?: ReadonlyArray<string>;
60
- [key: string]: unknown;
60
+ /** Free-form usage label recorded with the call. */
61
+ usageType?: string;
62
+ };
63
+
64
+ /** Token counts the model server reported for a call. */
65
+ export type LLMUsage = {
66
+ promptTokens: number;
67
+ completionTokens: number;
68
+ totalTokens: number;
61
69
  };
62
70
 
63
- /** Synchronous inference response `/invoke`. */
71
+ /** Synchronous inference response from `/invoke`. */
64
72
  export type LLMResponse = {
65
- completion: string;
66
- tokensUsed: number;
67
- latencyMs: number;
73
+ modelId: string;
74
+ /** The generated text. */
75
+ content: string;
76
+ finishReason: string | null;
77
+ usage: LLMUsage;
78
+ usageType: string;
68
79
  };
69
80
 
70
- /** Streaming chunk `/invoke-stream` (SSE per-token). */
81
+ /**
82
+ * One chunk of the `/invoke-stream` response: the model server's OpenAI-style
83
+ * chat-completion chunk, passed through unchanged. Read incremental text from
84
+ * `choices[0].delta.content`; the final chunk may carry `usage`.
85
+ */
71
86
  export type LLMStreamChunk = {
72
- /** Incremental completion delta. */
73
- delta: string;
74
- /** True on the terminal chunk. */
75
- done?: boolean;
76
- /** Populated on terminal chunk. */
77
- tokensUsed?: number;
87
+ id?: string;
88
+ object?: string;
89
+ model?: string;
90
+ choices?: ReadonlyArray<{
91
+ index?: number;
92
+ delta?: { role?: string; content?: string | null };
93
+ finish_reason?: string | null;
94
+ }>;
95
+ usage?: { prompt_tokens?: number; completion_tokens?: number; total_tokens?: number } | null;
96
+ [key: string]: unknown;
78
97
  };
79
98
 
80
99
  export type LLMInvokeInput = {
@@ -82,7 +101,11 @@ export type LLMInvokeInput = {
82
101
  params?: LLMInvokeParams;
83
102
  };
84
103
 
85
- /** Error code coined in (others reused). */
104
+ function invokeBody(input: LLMInvokeInput): Record<string, unknown> {
105
+ return { prompt: input.prompt, ...input.params };
106
+ }
107
+
108
+ /** Error codes specific to LLM calls. */
86
109
  export const LLM_ERROR_CODES = {
87
110
  PROVIDER_UNAVAILABLE: "K9O_LLM_PROVIDER_UNAVAILABLE",
88
111
  } as const;
@@ -120,24 +143,23 @@ export function makeLlmsNamespace(config: HttpClientConfig) {
120
143
  return request(config, {
121
144
  method: "POST",
122
145
  path: projectPath(slug, `compute/llms/${encodeURIComponent(modelId)}/invoke`),
123
- body: { prompt: input.prompt, params: input.params },
146
+ body: invokeBody(input),
124
147
  });
125
148
  },
126
149
 
127
150
  /**
128
151
  * POST /@<slug>/compute/llms/<model-id>/invoke-stream — streaming
129
- * inference (SSE; per-token delta for low-latency UX).
152
+ * inference (SSE; the model server's OpenAI-style chunks).
130
153
  * Returns an async iterable of `LLMStreamChunk` — caller writes
131
154
  * `for await (const chunk of client.compute.llms.invokeStream(...))`.
132
- * The SDK handles SSE framing internally per
133
- * `feedback_sdk_abstracted_primitive_default_to_richer_interaction.md`.
155
+ * The SDK handles SSE framing internally.
134
156
  */
135
157
  async *invokeStream(modelId: LLMModelId | string, input: LLMInvokeInput): AsyncGenerator<LLMStreamChunk, void, void> {
136
158
  const slug = requireProjectSlug(config);
137
159
  const response = await requestStream(config, {
138
160
  method: "POST",
139
161
  path: projectPath(slug, `compute/llms/${encodeURIComponent(modelId)}/invoke-stream`),
140
- body: { prompt: input.prompt, params: input.params },
162
+ body: invokeBody(input),
141
163
  accept: "text/event-stream",
142
164
  });
143
165
  // requestStream guarantees a non-null body on 2xx.
@@ -148,7 +170,7 @@ export function makeLlmsNamespace(config: HttpClientConfig) {
148
170
 
149
171
  export function makeComputeNamespace(config: HttpClientConfig) {
150
172
  return {
151
- /** LLM registry + inference — `client.compute.llms.*`. */
173
+ /** LLM registry + inference — `client.compute.llms.*` . */
152
174
  llms: makeLlmsNamespace(config),
153
175
  };
154
176
  }
package/src/functions.ts CHANGED
@@ -4,15 +4,14 @@
4
4
  * Backed by the gateway functions and invoke routes.
5
5
  *
6
6
  * Wire mapping:
7
- * - `deploy({ name, runtime, code, ... })` → POST /@<slug>/functions
7
+ * - `deploy({ name, runtime, tier, artifactKey, version })` → POST /@<slug>/functions
8
8
  * - `list()` → GET /@<slug>/functions
9
9
  * - `get(name)` → GET /@<slug>/functions/<name>
10
10
  * - `delete(name)` → DELETE /@<slug>/functions/<name>
11
11
  * - `invoke(name, payload)` → POST /@<slug>/functions/<name>/invoke
12
12
  *
13
- * Per: deploy requires PROJECT_ADMIN; invoke authority is per
14
- * function manifest. Per: deploy returns 202 with status
15
- * `deploying`; the function is ready when status flips to `active`.
13
+ * Deploy requires PROJECT_ADMIN and returns with status `deploying`; the
14
+ * function is ready when status flips to `active`.
16
15
  */
17
16
 
18
17
  import { request, type HttpClientConfig } from "./http.ts";
@@ -26,23 +25,45 @@ export type FunctionRecord = {
26
25
  /** `fn_<base62>`. */
27
26
  id: string;
28
27
  name: string;
29
- runtime: "wasm" | "bun" | "node";
30
- status: "deploying" | "active" | "retired" | "failed";
31
- tier: "low" | "medium" | "high";
28
+ runtime: DeployFunctionInput["runtime"];
29
+ tier: "gateway" | "sandbox";
30
+ entryPoint: string;
31
+ version: string;
32
+ status: "deploying" | "active" | "error" | "retired";
33
+ /** Who manages the function: a developer deploy, or an extension (`extension:<id>`). */
34
+ source: string;
35
+ description: string | null;
32
36
  createdAt: string;
33
- updatedAt: string | null;
37
+ deployedAt: string | null;
38
+ retiredAt: string | null;
39
+ errorDetail: string | null;
34
40
  };
35
41
 
36
42
  /**
37
- * Deploy input. The `code` field is the runtime bundle (base64-encoded
38
- * WASM or JS source).
43
+ * Deploy input. The function's bytes are uploaded first (the gateway's
44
+ * function-artifact upload returns the `artifactKey`); deploy references
45
+ * that artifact.
39
46
  */
40
47
  export type DeployFunctionInput = {
48
+ /** Function name (1 to 128 characters). */
41
49
  name: string;
42
- runtime: "wasm" | "bun" | "node";
43
- code: string;
44
- tier?: "low" | "medium" | "high";
45
- config?: Record<string, unknown>;
50
+ runtime: "bun" | "python" | "go" | "java" | "kotlin" | "dotnet" | "swift" | "php" | "ruby" | "rust" | "tinygo" | "zig" | "assemblyscript";
51
+ /** `gateway` runs WebAssembly in the gateway; `sandbox` runs an OCI image archive. */
52
+ tier: "gateway" | "sandbox";
53
+ /** Content-addressed key of the uploaded artifact. */
54
+ artifactKey: string;
55
+ /** Version label for this deploy (1 to 64 characters). */
56
+ version: string;
57
+ description?: string | undefined;
58
+ };
59
+
60
+ /** Optional invoke controls; property names are the gateway's. */
61
+ export type InvokeFunctionOptions = {
62
+ /** Run asynchronously instead of waiting for the result. */
63
+ background?: boolean | undefined;
64
+ delegation?: { token: string; invokeSessionId?: string | undefined } | undefined;
65
+ onBehalfOf?: string | undefined;
66
+ confinedTo?: string | undefined;
46
67
  };
47
68
 
48
69
  function requireProjectSlug(config: HttpClientConfig): string {
@@ -54,7 +75,22 @@ function requireProjectSlug(config: HttpClientConfig): string {
54
75
 
55
76
  export function makeFunctionsNamespace(config: HttpClientConfig) {
56
77
  return {
57
- /** Deploy or redeploy a function. Returns the function record with status. */
78
+ /**
79
+ * Upload a function artifact and get back its content-addressed `artifactKey`, to pass to `deploy`.
80
+ * `POST /@<projectSlug>/functions/artifacts` with the raw bytes: a WebAssembly module for the gateway tier (up to
81
+ * 16 MiB) or an OCI image archive for the sandbox tier (up to 256 MiB). Uploading the same bytes again returns the same key.
82
+ */
83
+ async uploadArtifact(bytes: Uint8Array | ArrayBuffer | Blob, kind: "wasm" | "tar"): Promise<Envelope<{ artifactKey: string }>> {
84
+ const slug = requireProjectSlug(config);
85
+ return request(config, {
86
+ method: "POST",
87
+ path: projectPath(slug, "functions/artifacts"),
88
+ rawBody: bytes,
89
+ headers: { "content-type": kind === "wasm" ? "application/wasm" : "application/x-tar" },
90
+ });
91
+ },
92
+
93
+ /** Deploy or redeploy a function from an uploaded artifact. Returns the function record with status. */
58
94
  async deploy(input: DeployFunctionInput): Promise<Envelope<FunctionRecord>> {
59
95
  const slug = requireProjectSlug(config);
60
96
  return request(config, {
@@ -65,7 +101,7 @@ export function makeFunctionsNamespace(config: HttpClientConfig) {
65
101
  },
66
102
 
67
103
  /** List deployed functions (cursor pagination). */
68
- async list(opts?: { limit?: number; cursor?: string; status?: FunctionRecord["status"] }): Promise<Envelope<ReadonlyArray<FunctionRecord>>> {
104
+ async list(opts?: { limit?: number; cursor?: string; status?: FunctionRecord["status"]; tier?: FunctionRecord["tier"]; source?: string }): Promise<Envelope<ReadonlyArray<FunctionRecord>>> {
69
105
  const slug = requireProjectSlug(config);
70
106
  return request(config, {
71
107
  method: "GET",
@@ -83,8 +119,8 @@ export function makeFunctionsNamespace(config: HttpClientConfig) {
83
119
  });
84
120
  },
85
121
 
86
- /** Retire a function. Returns confirmation; the function name enters a 24h cooldown. */
87
- async delete(name: string): Promise<Envelope<{ retired: boolean; cooldownUntil: string }>> {
122
+ /** Retire a function. Returns the retired record; the function name enters a 24h cooldown. */
123
+ async delete(name: string): Promise<Envelope<FunctionRecord>> {
88
124
  const slug = requireProjectSlug(config);
89
125
  return request(config, {
90
126
  method: "DELETE",
@@ -93,21 +129,18 @@ export function makeFunctionsNamespace(config: HttpClientConfig) {
93
129
  },
94
130
 
95
131
  /**
96
- * Invoke a function. The `payload` is passed to the function runtime
97
- * verbatim; the gateway returns whatever the function returns wrapped
98
- * in the standard envelope.
99
- *
100
- * Per: invoke authority is per-function (declared in the
101
- * function manifest). Per: streaming responses use the
102
- * subscription/async-iterator pattern; this method covers the
132
+ * Invoke a function. The `payload` is sent as the `payload` field of
133
+ * the request body and passed to the function runtime verbatim; the
134
+ * gateway returns whatever the function returns wrapped in the standard
135
+ * envelope. Invoke authority is per function. This method covers the
103
136
  * request-response variant only.
104
137
  */
105
- async invoke<TResult = unknown, TPayload = unknown>(name: string, payload: TPayload): Promise<Envelope<TResult>> {
138
+ async invoke<TResult = unknown, TPayload = unknown>(name: string, payload?: TPayload, options?: InvokeFunctionOptions): Promise<Envelope<TResult>> {
106
139
  const slug = requireProjectSlug(config);
107
140
  return request(config, {
108
141
  method: "POST",
109
142
  path: projectPath(slug, `functions/${encodeURIComponent(name)}/invoke`),
110
- body: payload,
143
+ body: { ...(payload !== undefined ? { payload } : {}), ...(options ?? {}) },
111
144
  });
112
145
  },
113
146
  };
package/src/http.ts CHANGED
@@ -53,6 +53,8 @@ export type RequestInit = {
53
53
  method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
54
54
  path: string;
55
55
  body?: unknown;
56
+ /** Send these bytes as the body, unchanged, with the `content-type` given in `headers` (for binary routes). Wins over `body`. */
57
+ rawBody?: Uint8Array | ArrayBuffer | Blob | undefined;
56
58
  /** Per-call bearer override (wins over config.apiKey). */
57
59
  bearerToken?: string | undefined;
58
60
  /** Per-call additional headers (merged with config.defaultHeaders + auth). */
@@ -109,7 +111,9 @@ export async function requestOnce<T>(
109
111
  method: init.method,
110
112
  headers,
111
113
  };
112
- if (init.body !== undefined) {
114
+ if (init.rawBody !== undefined) {
115
+ requestInit["body"] = init.rawBody;
116
+ } else if (init.body !== undefined) {
113
117
  if (typeof FormData !== "undefined" && init.body instanceof FormData) {
114
118
  // Multipart: the runtime sets the content type (with its boundary); never JSON-encode it.
115
119
  requestInit["body"] = init.body;