@m8t-stack/platform-sdk 0.1.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 ADDED
@@ -0,0 +1,26 @@
1
+ # `@m8t-stack/platform-sdk`
2
+
3
+ > 📦 The one import for integrating with an m8t-stack gateway: `npm i @m8t-stack/platform-sdk`
4
+
5
+ A thin, typed client for the m8t gateway HTTP API. Acquires a service-principal token from a pluggable Azure credential (default `DefaultAzureCredential`, secretless via Managed Identity), invokes workers, streams replies, and **re-exports all of [`@m8t-stack/api-contract`](../api-contract)** so you import types from one place.
6
+
7
+ ## Quick start
8
+
9
+ ```ts
10
+ import { PlatformClient } from "@m8t-stack/platform-sdk";
11
+
12
+ const client = new PlatformClient({
13
+ gatewayUrl: "https://your-gateway.example.com",
14
+ audience: "api://<gateway-app-client-id>",
15
+ });
16
+
17
+ const { id } = await client.createConversation({ agentName: "stacey" });
18
+ const stream = client.invoke({ agentName: "stacey", conversationId: id, message: "Hello!" });
19
+ for await (const ev of stream) {
20
+ if (ev.type === "text") process.stdout.write(ev.delta);
21
+ }
22
+ ```
23
+
24
+ The default credential resolves a token for `${audience}/.default`. The token's app role must include `Agents.Invoke` (see the gateway operator setup). Pass any `@azure/core-auth` `TokenCredential` as `credential` to override.
25
+
26
+ See [versioning & deprecation policy](../../docs/versioning-and-deprecation.md). Full runnable example: [`examples/stream-reply.ts`](./examples/stream-reply.ts).
@@ -0,0 +1,107 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PlatformClient = void 0;
4
+ const identity_1 = require("@azure/identity");
5
+ const api_contract_1 = require("@m8t-stack/api-contract");
6
+ const credential_js_1 = require("./credential.js");
7
+ const errors_js_1 = require("./errors.js");
8
+ const stream_js_1 = require("./stream.js");
9
+ /**
10
+ * Typed client for an m8t-stack gateway. Authenticates as a service principal
11
+ * (or any TokenCredential) and calls the gateway HTTP API — never Foundry directly.
12
+ */
13
+ class PlatformClient {
14
+ #base;
15
+ #audience;
16
+ #credential;
17
+ #fetch;
18
+ constructor(options) {
19
+ this.#base = options.gatewayUrl.replace(/\/+$/, "");
20
+ this.#audience = options.audience;
21
+ this.#credential = options.credential ?? new identity_1.DefaultAzureCredential();
22
+ this.#fetch = options.fetch ?? globalThis.fetch.bind(globalThis);
23
+ }
24
+ async #token() {
25
+ return (0, credential_js_1.acquireGatewayToken)(this.#credential, this.#audience);
26
+ }
27
+ async #authHeaders(extra) {
28
+ return { authorization: `Bearer ${await this.#token()}`, ...extra };
29
+ }
30
+ /** Build a PlatformApiError from a gateway error envelope + the HTTP status. */
31
+ #envelopeError(error, status) {
32
+ return new errors_js_1.PlatformApiError(error.code, error.message, error.details, status);
33
+ }
34
+ /** Call a JSON gateway route and unwrap its ApiResponse envelope, throwing on error. */
35
+ async #json(path, init) {
36
+ const res = await this.#fetch(`${this.#base}${path}`, init);
37
+ let parsed;
38
+ try {
39
+ parsed = await res.json();
40
+ }
41
+ catch {
42
+ throw new errors_js_1.PlatformApiError("invalid_response", `Gateway returned a non-JSON ${String(res.status)} response for ${path}.`, undefined, res.status);
43
+ }
44
+ if (!(0, api_contract_1.isApiResponse)(parsed)) {
45
+ throw new errors_js_1.PlatformApiError("invalid_response", `Gateway returned an unrecognised body for ${path}.`, undefined, res.status);
46
+ }
47
+ if (!parsed.ok)
48
+ throw this.#envelopeError(parsed.error, res.status);
49
+ if (!res.ok) {
50
+ throw new errors_js_1.PlatformApiError("http_error", `HTTP ${String(res.status)} for ${path}.`, undefined, res.status);
51
+ }
52
+ return parsed.data;
53
+ }
54
+ /** GET /api/me — the validated caller identity (incl. the F1 `principalId`). */
55
+ async me() {
56
+ return this.#json("/api/me", { method: "GET", headers: await this.#authHeaders() });
57
+ }
58
+ /** GET /api/agents — the worker roster. */
59
+ async listAgents() {
60
+ return this.#json("/api/agents", { method: "GET", headers: await this.#authHeaders() });
61
+ }
62
+ /** POST /api/conversations — create a conversation for an agent. */
63
+ async createConversation(opts) {
64
+ return this.#json("/api/conversations", {
65
+ method: "POST",
66
+ headers: await this.#authHeaders({ "content-type": "application/json" }),
67
+ body: JSON.stringify({ agentName: opts.agentName }),
68
+ });
69
+ }
70
+ /**
71
+ * POST /api/chat — invoke a worker and stream its reply. Returns an
72
+ * InvokeStream (async-iterable of text/artifacts events, with `.text()`).
73
+ * The request is sent lazily on first iteration.
74
+ */
75
+ invoke(opts) {
76
+ return (0, stream_js_1.makeInvokeStream)(async () => {
77
+ const res = await this.#fetch(`${this.#base}/api/chat`, {
78
+ method: "POST",
79
+ headers: await this.#authHeaders({ "content-type": "application/json" }),
80
+ body: JSON.stringify({
81
+ agentName: opts.agentName,
82
+ conversationId: opts.conversationId,
83
+ messages: [{ role: "user", content: opts.message }],
84
+ }),
85
+ });
86
+ if (!res.ok) {
87
+ let parsed;
88
+ try {
89
+ parsed = await res.json();
90
+ }
91
+ catch {
92
+ throw new errors_js_1.PlatformApiError("invalid_response", `Chat failed with a non-JSON HTTP ${String(res.status)} response.`, undefined, res.status);
93
+ }
94
+ if ((0, api_contract_1.isApiResponse)(parsed) && !parsed.ok) {
95
+ throw this.#envelopeError(parsed.error, res.status);
96
+ }
97
+ throw new errors_js_1.PlatformApiError("invalid_response", `Chat failed with HTTP ${String(res.status)} and an unrecognised body.`, undefined, res.status);
98
+ }
99
+ if (!res.body) {
100
+ throw new errors_js_1.PlatformApiError("no_stream_body", "Chat response had no body to stream.", undefined, res.status);
101
+ }
102
+ return res.body;
103
+ });
104
+ }
105
+ }
106
+ exports.PlatformClient = PlatformClient;
107
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.acquireGatewayToken = acquireGatewayToken;
4
+ const errors_js_1 = require("./errors.js");
5
+ /**
6
+ * Acquire a bearer token for the gateway's API audience. Scope is
7
+ * `${audience}/.default` (the Entra app-only convention). The token must carry
8
+ * the `Agents.Invoke` app role — granted to the caller's service identity by the
9
+ * gateway operator. Any `@azure/core-auth` TokenCredential works
10
+ * (DefaultAzureCredential / ManagedIdentityCredential / ClientSecretCredential …).
11
+ */
12
+ async function acquireGatewayToken(credential, audience) {
13
+ const scope = `${audience}/.default`;
14
+ const accessToken = await credential.getToken(scope);
15
+ if (!accessToken?.token) {
16
+ throw new errors_js_1.PlatformApiError("token_unavailable", `Could not acquire a gateway token for scope ${scope}.`);
17
+ }
18
+ return accessToken.token;
19
+ }
20
+ //# sourceMappingURL=credential.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credential.js","sourceRoot":"","sources":["../../src/credential.ts"],"names":[],"mappings":";;AAUA,kDAUC;AAnBD,2CAA+C;AAE/C;;;;;;GAMG;AACI,KAAK,UAAU,mBAAmB,CAAC,UAA2B,EAAE,QAAgB;IACrF,MAAM,KAAK,GAAG,GAAG,QAAQ,WAAW,CAAC;IACrC,MAAM,WAAW,GAAG,MAAM,UAAU,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IACrD,IAAI,CAAC,WAAW,EAAE,KAAK,EAAE,CAAC;QACxB,MAAM,IAAI,4BAAgB,CACxB,mBAAmB,EACnB,+CAA+C,KAAK,GAAG,CACxD,CAAC;IACJ,CAAC;IACD,OAAO,WAAW,CAAC,KAAK,CAAC;AAC3B,CAAC"}
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PlatformApiError = void 0;
4
+ /**
5
+ * Thrown for any non-success from the gateway — an `ApiResponse` error envelope
6
+ * (`{ ok: false, error: { code, message, details } }`), a mid-stream
7
+ * `{ type: "error", errorText }` SSE frame, or a transport-level failure.
8
+ */
9
+ class PlatformApiError extends Error {
10
+ code;
11
+ details;
12
+ /** The HTTP status, when the error came from a response (undefined for stream/transport errors). */
13
+ httpStatus;
14
+ constructor(code, message, details, httpStatus) {
15
+ super(message);
16
+ this.name = "PlatformApiError";
17
+ this.code = code;
18
+ this.details = details;
19
+ this.httpStatus = httpStatus;
20
+ }
21
+ }
22
+ exports.PlatformApiError = PlatformApiError;
23
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/errors.ts"],"names":[],"mappings":";;;AAAA;;;;GAIG;AACH,MAAa,gBAAiB,SAAQ,KAAK;IAChC,IAAI,CAAS;IACb,OAAO,CAAW;IAC3B,oGAAoG;IAC3F,UAAU,CAAU;IAE7B,YAAY,IAAY,EAAE,OAAe,EAAE,OAAiB,EAAE,UAAmB;QAC/E,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,kBAAkB,CAAC;QAC/B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;IAC/B,CAAC;CACF;AAbD,4CAaC"}
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.acquireGatewayToken = exports.makeInvokeStream = exports.PlatformApiError = exports.PlatformClient = void 0;
18
+ // The one consumer-facing import: the typed gateway client + every api-contract type.
19
+ __exportStar(require("@m8t-stack/api-contract"), exports);
20
+ var client_js_1 = require("./client.js");
21
+ Object.defineProperty(exports, "PlatformClient", { enumerable: true, get: function () { return client_js_1.PlatformClient; } });
22
+ var errors_js_1 = require("./errors.js");
23
+ Object.defineProperty(exports, "PlatformApiError", { enumerable: true, get: function () { return errors_js_1.PlatformApiError; } });
24
+ var stream_js_1 = require("./stream.js");
25
+ Object.defineProperty(exports, "makeInvokeStream", { enumerable: true, get: function () { return stream_js_1.makeInvokeStream; } });
26
+ var credential_js_1 = require("./credential.js");
27
+ Object.defineProperty(exports, "acquireGatewayToken", { enumerable: true, get: function () { return credential_js_1.acquireGatewayToken; } });
28
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAAA,sFAAsF;AACtF,0DAAwC;AAExC,yCAA6C;AAApC,2GAAA,cAAc,OAAA;AAOvB,yCAA+C;AAAtC,6GAAA,gBAAgB,OAAA;AACzB,yCAA+C;AAAtC,6GAAA,gBAAgB,OAAA;AAEzB,iDAAsD;AAA7C,oHAAA,mBAAmB,OAAA"}
@@ -0,0 +1 @@
1
+ {"type":"commonjs"}
@@ -0,0 +1,91 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.makeInvokeStream = makeInvokeStream;
4
+ const errors_js_1 = require("./errors.js");
5
+ /**
6
+ * Parse a gateway `POST /api/chat` response body — the AI-SDK UI-message-stream
7
+ * v1 SSE protocol: `data: <json>\n\n` frames, terminated by `data: [DONE]`.
8
+ * Yields text/artifacts events; ignores control frames; throws on an `error` frame.
9
+ */
10
+ async function* parseSse(body) {
11
+ const reader = body.getReader();
12
+ const decoder = new TextDecoder();
13
+ let buffer = "";
14
+ try {
15
+ for (;;) {
16
+ const { done, value } = await reader.read();
17
+ // A clean stream ends via the `[DONE]` sentinel (an explicit `return`
18
+ // below). Reaching `done` here means the body closed without it — a
19
+ // truncated stream is treated as a silent end (any partial trailing
20
+ // frame in `buffer` is dropped), mirroring standard SSE clients.
21
+ if (done)
22
+ break;
23
+ buffer += decoder.decode(value, { stream: true });
24
+ let sep;
25
+ while ((sep = buffer.indexOf("\n\n")) !== -1) {
26
+ const frame = buffer.slice(0, sep);
27
+ buffer = buffer.slice(sep + 2);
28
+ // `.trim()` (not just trimStart) so a normalized `\r\n` line ending
29
+ // can't leave a trailing `\r` that would break JSON.parse.
30
+ const data = frame.startsWith("data:") ? frame.slice(5).trim() : frame.trim();
31
+ if (data.length === 0)
32
+ continue;
33
+ if (data === "[DONE]")
34
+ return;
35
+ let part;
36
+ try {
37
+ part = JSON.parse(data);
38
+ }
39
+ catch {
40
+ continue; // tolerate a non-JSON keep-alive/comment line
41
+ }
42
+ if (part.type === "text-delta" && typeof part.delta === "string") {
43
+ yield { type: "text", delta: part.delta };
44
+ }
45
+ else if (part.type === "data-artifacts" && Array.isArray(part.data)) {
46
+ yield { type: "artifacts", artifacts: part.data };
47
+ }
48
+ else if (part.type === "error") {
49
+ const text = typeof part.errorText === "string" ? part.errorText : "stream error";
50
+ throw new errors_js_1.PlatformApiError("stream_error", text);
51
+ }
52
+ // else: control frame (start/start-step/text-start/text-end/finish-step/finish) — ignore
53
+ }
54
+ }
55
+ }
56
+ finally {
57
+ reader.releaseLock();
58
+ // If the consumer broke out early (or an error frame threw), abandon the
59
+ // underlying HTTP body so the server stops pushing into a buffer no one
60
+ // reads. Best-effort: a no-op once the stream already ended.
61
+ void body.cancel().catch(() => { });
62
+ }
63
+ }
64
+ /**
65
+ * Build an `InvokeStream` from a lazy body provider. The body is fetched on first
66
+ * iteration (or first `.text()` call) so `invoke()` can return synchronously.
67
+ */
68
+ function makeInvokeStream(getBody) {
69
+ let iterator;
70
+ const ensure = () => {
71
+ iterator ??= (async function* () {
72
+ const body = await getBody();
73
+ yield* parseSse(body);
74
+ })();
75
+ return iterator;
76
+ };
77
+ return {
78
+ [Symbol.asyncIterator]() {
79
+ return ensure();
80
+ },
81
+ async text() {
82
+ let out = "";
83
+ for await (const ev of ensure()) {
84
+ if (ev.type === "text")
85
+ out += ev.delta;
86
+ }
87
+ return out;
88
+ },
89
+ };
90
+ }
91
+ //# sourceMappingURL=stream.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stream.js","sourceRoot":"","sources":["../../src/stream.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,61 @@
1
+ import type { TokenCredential } from "@azure/core-auth";
2
+ import type { MeResponse } from "@m8t-stack/api-contract";
3
+ import { type InvokeStream } from "./stream.js";
4
+ /** One agent in the gateway's `GET /api/agents` roster (mirrors the gateway wire shape). */
5
+ export interface AgentSummary {
6
+ name: string;
7
+ id?: string;
8
+ model: string;
9
+ description?: string;
10
+ /** "prompt" | "hosted" today; typed as string to tolerate new worker kinds. */
11
+ kind: string;
12
+ harness?: string;
13
+ }
14
+ export interface AgentsResponse {
15
+ projectName: string;
16
+ agents: AgentSummary[];
17
+ }
18
+ /** A gateway conversation (mirrors `ConversationSummary`). */
19
+ export interface Conversation {
20
+ id: string;
21
+ agent: string;
22
+ title: string | null;
23
+ createdAt: string;
24
+ }
25
+ export interface PlatformClientOptions {
26
+ /** Gateway base URL, e.g. "https://gw.example.com" (no trailing /api). */
27
+ gatewayUrl: string;
28
+ /** Gateway app-reg audience, e.g. "api://<clientId>". Token scope is `${audience}/.default`. */
29
+ audience: string;
30
+ /** Any @azure/core-auth credential. Defaults to a secretless DefaultAzureCredential. */
31
+ credential?: TokenCredential;
32
+ /** Injectable fetch (tests / custom transport). Defaults to global fetch. */
33
+ fetch?: typeof fetch;
34
+ }
35
+ /**
36
+ * Typed client for an m8t-stack gateway. Authenticates as a service principal
37
+ * (or any TokenCredential) and calls the gateway HTTP API — never Foundry directly.
38
+ */
39
+ export declare class PlatformClient {
40
+ #private;
41
+ constructor(options: PlatformClientOptions);
42
+ /** GET /api/me — the validated caller identity (incl. the F1 `principalId`). */
43
+ me(): Promise<MeResponse>;
44
+ /** GET /api/agents — the worker roster. */
45
+ listAgents(): Promise<AgentsResponse>;
46
+ /** POST /api/conversations — create a conversation for an agent. */
47
+ createConversation(opts: {
48
+ agentName: string;
49
+ }): Promise<Conversation>;
50
+ /**
51
+ * POST /api/chat — invoke a worker and stream its reply. Returns an
52
+ * InvokeStream (async-iterable of text/artifacts events, with `.text()`).
53
+ * The request is sent lazily on first iteration.
54
+ */
55
+ invoke(opts: {
56
+ agentName: string;
57
+ conversationId: string;
58
+ message: string;
59
+ }): InvokeStream;
60
+ }
61
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/client.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAExD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAG1D,OAAO,EAAoB,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC;AAElE,4FAA4F;AAC5F,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,+EAA+E;IAC/E,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,cAAc;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,YAAY,EAAE,CAAC;CACxB;AAED,8DAA8D;AAC9D,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,qBAAqB;IACpC,0EAA0E;IAC1E,UAAU,EAAE,MAAM,CAAC;IACnB,gGAAgG;IAChG,QAAQ,EAAE,MAAM,CAAC;IACjB,wFAAwF;IACxF,UAAU,CAAC,EAAE,eAAe,CAAC;IAC7B,6EAA6E;IAC7E,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;CACtB;AAED;;;GAGG;AACH,qBAAa,cAAc;;gBAMb,OAAO,EAAE,qBAAqB;IAoD1C,gFAAgF;IAC1E,EAAE,IAAI,OAAO,CAAC,UAAU,CAAC;IAI/B,2CAA2C;IACrC,UAAU,IAAI,OAAO,CAAC,cAAc,CAAC;IAI3C,oEAAoE;IAC9D,kBAAkB,CAAC,IAAI,EAAE;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,YAAY,CAAC;IAQ5E;;;;OAIG;IACH,MAAM,CAAC,IAAI,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,cAAc,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,GAAG,YAAY;CAuC3F"}
@@ -0,0 +1,103 @@
1
+ import { DefaultAzureCredential } from "@azure/identity";
2
+ import { isApiResponse } from "@m8t-stack/api-contract";
3
+ import { acquireGatewayToken } from "./credential.js";
4
+ import { PlatformApiError } from "./errors.js";
5
+ import { makeInvokeStream } from "./stream.js";
6
+ /**
7
+ * Typed client for an m8t-stack gateway. Authenticates as a service principal
8
+ * (or any TokenCredential) and calls the gateway HTTP API — never Foundry directly.
9
+ */
10
+ export class PlatformClient {
11
+ #base;
12
+ #audience;
13
+ #credential;
14
+ #fetch;
15
+ constructor(options) {
16
+ this.#base = options.gatewayUrl.replace(/\/+$/, "");
17
+ this.#audience = options.audience;
18
+ this.#credential = options.credential ?? new DefaultAzureCredential();
19
+ this.#fetch = options.fetch ?? globalThis.fetch.bind(globalThis);
20
+ }
21
+ async #token() {
22
+ return acquireGatewayToken(this.#credential, this.#audience);
23
+ }
24
+ async #authHeaders(extra) {
25
+ return { authorization: `Bearer ${await this.#token()}`, ...extra };
26
+ }
27
+ /** Build a PlatformApiError from a gateway error envelope + the HTTP status. */
28
+ #envelopeError(error, status) {
29
+ return new PlatformApiError(error.code, error.message, error.details, status);
30
+ }
31
+ /** Call a JSON gateway route and unwrap its ApiResponse envelope, throwing on error. */
32
+ async #json(path, init) {
33
+ const res = await this.#fetch(`${this.#base}${path}`, init);
34
+ let parsed;
35
+ try {
36
+ parsed = await res.json();
37
+ }
38
+ catch {
39
+ throw new PlatformApiError("invalid_response", `Gateway returned a non-JSON ${String(res.status)} response for ${path}.`, undefined, res.status);
40
+ }
41
+ if (!isApiResponse(parsed)) {
42
+ throw new PlatformApiError("invalid_response", `Gateway returned an unrecognised body for ${path}.`, undefined, res.status);
43
+ }
44
+ if (!parsed.ok)
45
+ throw this.#envelopeError(parsed.error, res.status);
46
+ if (!res.ok) {
47
+ throw new PlatformApiError("http_error", `HTTP ${String(res.status)} for ${path}.`, undefined, res.status);
48
+ }
49
+ return parsed.data;
50
+ }
51
+ /** GET /api/me — the validated caller identity (incl. the F1 `principalId`). */
52
+ async me() {
53
+ return this.#json("/api/me", { method: "GET", headers: await this.#authHeaders() });
54
+ }
55
+ /** GET /api/agents — the worker roster. */
56
+ async listAgents() {
57
+ return this.#json("/api/agents", { method: "GET", headers: await this.#authHeaders() });
58
+ }
59
+ /** POST /api/conversations — create a conversation for an agent. */
60
+ async createConversation(opts) {
61
+ return this.#json("/api/conversations", {
62
+ method: "POST",
63
+ headers: await this.#authHeaders({ "content-type": "application/json" }),
64
+ body: JSON.stringify({ agentName: opts.agentName }),
65
+ });
66
+ }
67
+ /**
68
+ * POST /api/chat — invoke a worker and stream its reply. Returns an
69
+ * InvokeStream (async-iterable of text/artifacts events, with `.text()`).
70
+ * The request is sent lazily on first iteration.
71
+ */
72
+ invoke(opts) {
73
+ return makeInvokeStream(async () => {
74
+ const res = await this.#fetch(`${this.#base}/api/chat`, {
75
+ method: "POST",
76
+ headers: await this.#authHeaders({ "content-type": "application/json" }),
77
+ body: JSON.stringify({
78
+ agentName: opts.agentName,
79
+ conversationId: opts.conversationId,
80
+ messages: [{ role: "user", content: opts.message }],
81
+ }),
82
+ });
83
+ if (!res.ok) {
84
+ let parsed;
85
+ try {
86
+ parsed = await res.json();
87
+ }
88
+ catch {
89
+ throw new PlatformApiError("invalid_response", `Chat failed with a non-JSON HTTP ${String(res.status)} response.`, undefined, res.status);
90
+ }
91
+ if (isApiResponse(parsed) && !parsed.ok) {
92
+ throw this.#envelopeError(parsed.error, res.status);
93
+ }
94
+ throw new PlatformApiError("invalid_response", `Chat failed with HTTP ${String(res.status)} and an unrecognised body.`, undefined, res.status);
95
+ }
96
+ if (!res.body) {
97
+ throw new PlatformApiError("no_stream_body", "Chat response had no body to stream.", undefined, res.status);
98
+ }
99
+ return res.body;
100
+ });
101
+ }
102
+ }
103
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AAEzD,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAExD,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,gBAAgB,EAAqB,MAAM,aAAa,CAAC;AAqClE;;;GAGG;AACH,MAAM,OAAO,cAAc;IAChB,KAAK,CAAS;IACd,SAAS,CAAS;IAClB,WAAW,CAAkB;IAC7B,MAAM,CAAe;IAE9B,YAAY,OAA8B;QACxC,IAAI,CAAC,KAAK,GAAG,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;QACpD,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,QAAQ,CAAC;QAClC,IAAI,CAAC,WAAW,GAAG,OAAO,CAAC,UAAU,IAAI,IAAI,sBAAsB,EAAE,CAAC;QACtE,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,KAAK,IAAI,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IACnE,CAAC;IAED,KAAK,CAAC,MAAM;QACV,OAAO,mBAAmB,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;IAC/D,CAAC;IAED,KAAK,CAAC,YAAY,CAAC,KAA8B;QAC/C,OAAO,EAAE,aAAa,EAAE,UAAU,MAAM,IAAI,CAAC,MAAM,EAAE,EAAE,EAAE,GAAG,KAAK,EAAE,CAAC;IACtE,CAAC;IAED,gFAAgF;IAChF,cAAc,CACZ,KAA2D,EAC3D,MAAc;QAEd,OAAO,IAAI,gBAAgB,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAChF,CAAC;IAED,wFAAwF;IACxF,KAAK,CAAC,KAAK,CAAI,IAAY,EAAE,IAAiB;QAC5C,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,GAAG,IAAI,EAAE,EAAE,IAAI,CAAC,CAAC;QAC5D,IAAI,MAAe,CAAC;QACpB,IAAI,CAAC;YACH,MAAM,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;QAC5B,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,IAAI,gBAAgB,CACxB,kBAAkB,EAClB,+BAA+B,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,iBAAiB,IAAI,GAAG,EACzE,SAAS,EACT,GAAG,CAAC,MAAM,CACX,CAAC;QACJ,CAAC;QACD,IAAI,CAAC,aAAa,CAAI,MAAM,CAAC,EAAE,CAAC;YAC9B,MAAM,IAAI,gBAAgB,CACxB,kBAAkB,EAClB,6CAA6C,IAAI,GAAG,EACpD,SAAS,EACT,GAAG,CAAC,MAAM,CACX,CAAC;QACJ,CAAC;QACD,IAAI,CAAC,MAAM,CAAC,EAAE;YAAE,MAAM,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;QACpE,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACZ,MAAM,IAAI,gBAAgB,CAAC,YAAY,EAAE,QAAQ,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,IAAI,GAAG,EAAE,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;QAC7G,CAAC;QACD,OAAO,MAAM,CAAC,IAAI,CAAC;IACrB,CAAC;IAED,gFAAgF;IAChF,KAAK,CAAC,EAAE;QACN,OAAO,IAAI,CAAC,KAAK,CAAa,SAAS,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC;IAClG,CAAC;IAED,2CAA2C;IAC3C,KAAK,CAAC,UAAU;QACd,OAAO,IAAI,CAAC,KAAK,CAAiB,aAAa,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC;IAC1G,CAAC;IAED,oEAAoE;IACpE,KAAK,CAAC,kBAAkB,CAAC,IAA2B;QAClD,OAAO,IAAI,CAAC,KAAK,CAAe,oBAAoB,EAAE;YACpD,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,MAAM,IAAI,CAAC,YAAY,CAAC,EAAE,cAAc,EAAE,kBAAkB,EAAE,CAAC;YACxE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC;SACpD,CAAC,CAAC;IACL,CAAC;IAED;;;;OAIG;IACH,MAAM,CAAC,IAAoE;QACzE,OAAO,gBAAgB,CAAC,KAAK,IAAI,EAAE;YACjC,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,WAAW,EAAE;gBACtD,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE,MAAM,IAAI,CAAC,YAAY,CAAC,EAAE,cAAc,EAAE,kBAAkB,EAAE,CAAC;gBACxE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;oBACnB,SAAS,EAAE,IAAI,CAAC,SAAS;oBACzB,cAAc,EAAE,IAAI,CAAC,cAAc;oBACnC,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC;iBACpD,CAAC;aACH,CAAC,CAAC;YACH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;gBACZ,IAAI,MAAe,CAAC;gBACpB,IAAI,CAAC;oBACH,MAAM,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;gBAC5B,CAAC;gBAAC,MAAM,CAAC;oBACP,MAAM,IAAI,gBAAgB,CACxB,kBAAkB,EAClB,oCAAoC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,YAAY,EAClE,SAAS,EACT,GAAG,CAAC,MAAM,CACX,CAAC;gBACJ,CAAC;gBACD,IAAI,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;oBACxC,MAAM,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;gBACtD,CAAC;gBACD,MAAM,IAAI,gBAAgB,CACxB,kBAAkB,EAClB,yBAAyB,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,4BAA4B,EACvE,SAAS,EACT,GAAG,CAAC,MAAM,CACX,CAAC;YACJ,CAAC;YACD,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;gBACd,MAAM,IAAI,gBAAgB,CAAC,gBAAgB,EAAE,sCAAsC,EAAE,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;YAC9G,CAAC;YACD,OAAO,GAAG,CAAC,IAAI,CAAC;QAClB,CAAC,CAAC,CAAC;IACL,CAAC;CACF"}
@@ -0,0 +1,10 @@
1
+ import type { TokenCredential } from "@azure/core-auth";
2
+ /**
3
+ * Acquire a bearer token for the gateway's API audience. Scope is
4
+ * `${audience}/.default` (the Entra app-only convention). The token must carry
5
+ * the `Agents.Invoke` app role — granted to the caller's service identity by the
6
+ * gateway operator. Any `@azure/core-auth` TokenCredential works
7
+ * (DefaultAzureCredential / ManagedIdentityCredential / ClientSecretCredential …).
8
+ */
9
+ export declare function acquireGatewayToken(credential: TokenCredential, audience: string): Promise<string>;
10
+ //# sourceMappingURL=credential.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credential.d.ts","sourceRoot":"","sources":["../../src/credential.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAGxD;;;;;;GAMG;AACH,wBAAsB,mBAAmB,CAAC,UAAU,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAUxG"}
@@ -0,0 +1,17 @@
1
+ import { PlatformApiError } from "./errors.js";
2
+ /**
3
+ * Acquire a bearer token for the gateway's API audience. Scope is
4
+ * `${audience}/.default` (the Entra app-only convention). The token must carry
5
+ * the `Agents.Invoke` app role — granted to the caller's service identity by the
6
+ * gateway operator. Any `@azure/core-auth` TokenCredential works
7
+ * (DefaultAzureCredential / ManagedIdentityCredential / ClientSecretCredential …).
8
+ */
9
+ export async function acquireGatewayToken(credential, audience) {
10
+ const scope = `${audience}/.default`;
11
+ const accessToken = await credential.getToken(scope);
12
+ if (!accessToken?.token) {
13
+ throw new PlatformApiError("token_unavailable", `Could not acquire a gateway token for scope ${scope}.`);
14
+ }
15
+ return accessToken.token;
16
+ }
17
+ //# sourceMappingURL=credential.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credential.js","sourceRoot":"","sources":["../../src/credential.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAE/C;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,UAA2B,EAAE,QAAgB;IACrF,MAAM,KAAK,GAAG,GAAG,QAAQ,WAAW,CAAC;IACrC,MAAM,WAAW,GAAG,MAAM,UAAU,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IACrD,IAAI,CAAC,WAAW,EAAE,KAAK,EAAE,CAAC;QACxB,MAAM,IAAI,gBAAgB,CACxB,mBAAmB,EACnB,+CAA+C,KAAK,GAAG,CACxD,CAAC;IACJ,CAAC;IACD,OAAO,WAAW,CAAC,KAAK,CAAC;AAC3B,CAAC"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Thrown for any non-success from the gateway — an `ApiResponse` error envelope
3
+ * (`{ ok: false, error: { code, message, details } }`), a mid-stream
4
+ * `{ type: "error", errorText }` SSE frame, or a transport-level failure.
5
+ */
6
+ export declare class PlatformApiError extends Error {
7
+ readonly code: string;
8
+ readonly details?: unknown;
9
+ /** The HTTP status, when the error came from a response (undefined for stream/transport errors). */
10
+ readonly httpStatus?: number;
11
+ constructor(code: string, message: string, details?: unknown, httpStatus?: number);
12
+ }
13
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/errors.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,qBAAa,gBAAiB,SAAQ,KAAK;IACzC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAC3B,oGAAoG;IACpG,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;gBAEjB,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,EAAE,UAAU,CAAC,EAAE,MAAM;CAOlF"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Thrown for any non-success from the gateway — an `ApiResponse` error envelope
3
+ * (`{ ok: false, error: { code, message, details } }`), a mid-stream
4
+ * `{ type: "error", errorText }` SSE frame, or a transport-level failure.
5
+ */
6
+ export class PlatformApiError extends Error {
7
+ code;
8
+ details;
9
+ /** The HTTP status, when the error came from a response (undefined for stream/transport errors). */
10
+ httpStatus;
11
+ constructor(code, message, details, httpStatus) {
12
+ super(message);
13
+ this.name = "PlatformApiError";
14
+ this.code = code;
15
+ this.details = details;
16
+ this.httpStatus = httpStatus;
17
+ }
18
+ }
19
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/errors.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,OAAO,gBAAiB,SAAQ,KAAK;IAChC,IAAI,CAAS;IACb,OAAO,CAAW;IAC3B,oGAAoG;IAC3F,UAAU,CAAU;IAE7B,YAAY,IAAY,EAAE,OAAe,EAAE,OAAiB,EAAE,UAAmB;QAC/E,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,kBAAkB,CAAC;QAC/B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;IAC/B,CAAC;CACF"}
@@ -0,0 +1,8 @@
1
+ export * from "@m8t-stack/api-contract";
2
+ export { PlatformClient } from "./client.js";
3
+ export type { PlatformClientOptions, AgentSummary, AgentsResponse, Conversation, } from "./client.js";
4
+ export { PlatformApiError } from "./errors.js";
5
+ export { makeInvokeStream } from "./stream.js";
6
+ export type { InvokeStream, InvokeEvent, InvokeTextEvent, InvokeArtifactsEvent } from "./stream.js";
7
+ export { acquireGatewayToken } from "./credential.js";
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AACA,cAAc,yBAAyB,CAAC;AAExC,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,YAAY,EACV,qBAAqB,EACrB,YAAY,EACZ,cAAc,EACd,YAAY,GACb,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACpG,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC"}
@@ -0,0 +1,7 @@
1
+ // The one consumer-facing import: the typed gateway client + every api-contract type.
2
+ export * from "@m8t-stack/api-contract";
3
+ export { PlatformClient } from "./client.js";
4
+ export { PlatformApiError } from "./errors.js";
5
+ export { makeInvokeStream } from "./stream.js";
6
+ export { acquireGatewayToken } from "./credential.js";
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,sFAAsF;AACtF,cAAc,yBAAyB,CAAC;AAExC,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAO7C,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAE/C,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC"}
@@ -0,0 +1,29 @@
1
+ /** A text token from the worker's reply. */
2
+ export interface InvokeTextEvent {
3
+ type: "text";
4
+ delta: string;
5
+ }
6
+ /** Artifacts (e.g. files) emitted by a hosted worker, as a `data-artifacts` part. */
7
+ export interface InvokeArtifactsEvent {
8
+ type: "artifacts";
9
+ artifacts: unknown[];
10
+ }
11
+ export type InvokeEvent = InvokeTextEvent | InvokeArtifactsEvent;
12
+ /**
13
+ * An async-iterable reply stream with a `.text()` convenience that drains to a string.
14
+ *
15
+ * The async iterator and `.text()` share ONE underlying generator over the
16
+ * response body — consume the stream **one way**: either `for await (…)` OR
17
+ * `.text()`, not both, and not two concurrent iterations. A second consumer
18
+ * sees only what the first left unread.
19
+ */
20
+ export interface InvokeStream extends AsyncIterable<InvokeEvent> {
21
+ /** Drain the stream and return the concatenated text deltas. */
22
+ text(): Promise<string>;
23
+ }
24
+ /**
25
+ * Build an `InvokeStream` from a lazy body provider. The body is fetched on first
26
+ * iteration (or first `.text()` call) so `invoke()` can return synchronously.
27
+ */
28
+ export declare function makeInvokeStream(getBody: () => Promise<ReadableStream<Uint8Array>>): InvokeStream;
29
+ //# sourceMappingURL=stream.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stream.d.ts","sourceRoot":"","sources":["../../src/stream.ts"],"names":[],"mappings":"AAEA,4CAA4C;AAC5C,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf;AAED,qFAAqF;AACrF,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,WAAW,CAAC;IAClB,SAAS,EAAE,OAAO,EAAE,CAAC;CACtB;AAED,MAAM,MAAM,WAAW,GAAG,eAAe,GAAG,oBAAoB,CAAC;AAEjE;;;;;;;GAOG;AACH,MAAM,WAAW,YAAa,SAAQ,aAAa,CAAC,WAAW,CAAC;IAC9D,gEAAgE;IAChE,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;CACzB;AAuDD;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,OAAO,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC,GAAG,YAAY,CAqBjG"}
@@ -0,0 +1,88 @@
1
+ import { PlatformApiError } from "./errors.js";
2
+ /**
3
+ * Parse a gateway `POST /api/chat` response body — the AI-SDK UI-message-stream
4
+ * v1 SSE protocol: `data: <json>\n\n` frames, terminated by `data: [DONE]`.
5
+ * Yields text/artifacts events; ignores control frames; throws on an `error` frame.
6
+ */
7
+ async function* parseSse(body) {
8
+ const reader = body.getReader();
9
+ const decoder = new TextDecoder();
10
+ let buffer = "";
11
+ try {
12
+ for (;;) {
13
+ const { done, value } = await reader.read();
14
+ // A clean stream ends via the `[DONE]` sentinel (an explicit `return`
15
+ // below). Reaching `done` here means the body closed without it — a
16
+ // truncated stream is treated as a silent end (any partial trailing
17
+ // frame in `buffer` is dropped), mirroring standard SSE clients.
18
+ if (done)
19
+ break;
20
+ buffer += decoder.decode(value, { stream: true });
21
+ let sep;
22
+ while ((sep = buffer.indexOf("\n\n")) !== -1) {
23
+ const frame = buffer.slice(0, sep);
24
+ buffer = buffer.slice(sep + 2);
25
+ // `.trim()` (not just trimStart) so a normalized `\r\n` line ending
26
+ // can't leave a trailing `\r` that would break JSON.parse.
27
+ const data = frame.startsWith("data:") ? frame.slice(5).trim() : frame.trim();
28
+ if (data.length === 0)
29
+ continue;
30
+ if (data === "[DONE]")
31
+ return;
32
+ let part;
33
+ try {
34
+ part = JSON.parse(data);
35
+ }
36
+ catch {
37
+ continue; // tolerate a non-JSON keep-alive/comment line
38
+ }
39
+ if (part.type === "text-delta" && typeof part.delta === "string") {
40
+ yield { type: "text", delta: part.delta };
41
+ }
42
+ else if (part.type === "data-artifacts" && Array.isArray(part.data)) {
43
+ yield { type: "artifacts", artifacts: part.data };
44
+ }
45
+ else if (part.type === "error") {
46
+ const text = typeof part.errorText === "string" ? part.errorText : "stream error";
47
+ throw new PlatformApiError("stream_error", text);
48
+ }
49
+ // else: control frame (start/start-step/text-start/text-end/finish-step/finish) — ignore
50
+ }
51
+ }
52
+ }
53
+ finally {
54
+ reader.releaseLock();
55
+ // If the consumer broke out early (or an error frame threw), abandon the
56
+ // underlying HTTP body so the server stops pushing into a buffer no one
57
+ // reads. Best-effort: a no-op once the stream already ended.
58
+ void body.cancel().catch(() => { });
59
+ }
60
+ }
61
+ /**
62
+ * Build an `InvokeStream` from a lazy body provider. The body is fetched on first
63
+ * iteration (or first `.text()` call) so `invoke()` can return synchronously.
64
+ */
65
+ export function makeInvokeStream(getBody) {
66
+ let iterator;
67
+ const ensure = () => {
68
+ iterator ??= (async function* () {
69
+ const body = await getBody();
70
+ yield* parseSse(body);
71
+ })();
72
+ return iterator;
73
+ };
74
+ return {
75
+ [Symbol.asyncIterator]() {
76
+ return ensure();
77
+ },
78
+ async text() {
79
+ let out = "";
80
+ for await (const ev of ensure()) {
81
+ if (ev.type === "text")
82
+ out += ev.delta;
83
+ }
84
+ return out;
85
+ },
86
+ };
87
+ }
88
+ //# sourceMappingURL=stream.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stream.js","sourceRoot":"","sources":["../../src/stream.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "@m8t-stack/platform-sdk",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "main": "./dist/cjs/index.js",
6
+ "types": "./dist/esm/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./dist/esm/index.d.ts",
10
+ "import": "./dist/esm/index.js",
11
+ "require": "./dist/cjs/index.js",
12
+ "default": "./dist/cjs/index.js"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist",
17
+ "README.md"
18
+ ],
19
+ "license": "MIT",
20
+ "repository": {
21
+ "type": "git",
22
+ "url": "git+https://github.com/m8t-run/m8t.git",
23
+ "directory": "packages/platform-sdk"
24
+ },
25
+ "homepage": "https://github.com/m8t-run/m8t/tree/main/packages/platform-sdk#readme",
26
+ "bugs": {
27
+ "url": "https://github.com/m8t-run/m8t/issues"
28
+ },
29
+ "description": "Typed client + service-principal auth for the m8t-stack gateway; re-exports @m8t-stack/api-contract.",
30
+ "publishConfig": {
31
+ "access": "public"
32
+ },
33
+ "packageManager": "pnpm@10.33.0",
34
+ "scripts": {
35
+ "build": "tsc && tsc -p tsconfig.cjs.json && echo '{\"type\":\"commonjs\"}' > dist/cjs/package.json",
36
+ "prepare": "pnpm run build",
37
+ "test": "vitest run",
38
+ "test:watch": "vitest",
39
+ "typecheck": "tsc --noEmit -p tsconfig.eslint.json",
40
+ "lint": "eslint ."
41
+ },
42
+ "dependencies": {
43
+ "@azure/core-auth": "^1.10.1",
44
+ "@azure/identity": "^4.13.1",
45
+ "@m8t-stack/api-contract": "^0.1.0"
46
+ }
47
+ }