@muxi-ai/muxi-typescript 0.20260107.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,67 @@
1
+ # MUXI TypeScript SDK
2
+
3
+ Official TypeScript/Node SDK for [MUXI](https://muxi.org) — infrastructure for AI agents.
4
+
5
+ **Highlights**
6
+ - Fetch-based HTTP transport (Node 18+), automatic idempotency, SDK/client headers
7
+ - Formation client/admin key auth; server HMAC auth
8
+ - SSE helpers for chat/audio streams and deploy/log tails
9
+ - Retries on 429/5xx/connection errors with exponential backoff
10
+
11
+ ## Installation
12
+
13
+ ```bash
14
+ npm install @muxi-ai/muxi-typescript
15
+ ```
16
+
17
+ ## Quick Start (server)
18
+
19
+ ```ts
20
+ import { ServerClient } from "@muxi-ai/muxi-typescript";
21
+
22
+ const server = new ServerClient({
23
+ url: "https://server.example.com",
24
+ keyId: process.env.MUXI_KEY_ID!,
25
+ secretKey: process.env.MUXI_SECRET_KEY!,
26
+ });
27
+
28
+ console.log(await server.status());
29
+ ```
30
+
31
+ ## Quick Start (formation)
32
+
33
+ ```ts
34
+ import { FormationClient } from "@muxi-ai/muxi-typescript";
35
+
36
+ const formation = new FormationClient({
37
+ serverUrl: "https://server.example.com",
38
+ formationId: "your-formation",
39
+ clientKey: process.env.MUXI_CLIENT_KEY!,
40
+ adminKey: process.env.MUXI_ADMIN_KEY!,
41
+ });
42
+
43
+ console.log(await formation.health());
44
+
45
+ // Streaming chat (SSE)
46
+ for await (const evt of formation.chatStream({ message: "hello" })) {
47
+ console.log(evt);
48
+ break;
49
+ }
50
+ ```
51
+
52
+ ## Formation base URL override
53
+
54
+ - Default: `serverUrl + /api/{formationId}/v1`
55
+ - Override: set `baseUrl` (e.g., `http://localhost:9012/v1` for direct formation)
56
+
57
+ ## Auth & headers
58
+
59
+ - Server: HMAC with `keyId`/`secretKey` on `/rpc/*` calls.
60
+ - Formation: `X-MUXI-CLIENT-KEY` or `X-MUXI-ADMIN-KEY`; optional `X-Muxi-User-ID` via `userId` param.
61
+ - Idempotency: `X-Muxi-Idempotency-Key` auto-generated on every request.
62
+ - SDK/Client headers set automatically (`X-Muxi-SDK`, `X-Muxi-Client`).
63
+
64
+ ## Streaming
65
+
66
+ - Chat/audio: `chatStream` / `audioChatStream` return async generators of SSE events.
67
+ - Deploy/log streams: `deployFormationStream`, `updateFormationStream`, `streamFormationLogs`, etc.
package/dist/auth.d.ts ADDED
@@ -0,0 +1 @@
1
+ export declare function buildAuthHeader(keyId: string, secretKey: string, method: string, path: string): string;
package/dist/auth.js ADDED
@@ -0,0 +1,8 @@
1
+ import { createHmac } from "crypto";
2
+ export function buildAuthHeader(keyId, secretKey, method, path) {
3
+ const timestamp = Math.floor(Date.now() / 1000);
4
+ const signPath = path.split("?", 1)[0];
5
+ const message = `${timestamp};${method};${signPath}`;
6
+ const mac = createHmac("sha256", secretKey).update(message).digest("base64");
7
+ return `MUXI-HMAC key=${keyId}, timestamp=${timestamp}, signature=${mac}`;
8
+ }
@@ -0,0 +1 @@
1
+ export declare function unwrapEnvelope<T = unknown>(obj: any): T;
@@ -0,0 +1,19 @@
1
+ export function unwrapEnvelope(obj) {
2
+ if (obj === null || typeof obj !== "object")
3
+ return obj;
4
+ if (!Object.prototype.hasOwnProperty.call(obj, "data"))
5
+ return obj;
6
+ const data = obj.data;
7
+ const request = obj.request ?? {};
8
+ const requestId = request.id ?? obj.request_id;
9
+ const timestamp = obj.timestamp;
10
+ if (data && typeof data === "object" && !Array.isArray(data)) {
11
+ const out = { ...data };
12
+ if (requestId && out.request_id === undefined)
13
+ out.request_id = requestId;
14
+ if (timestamp !== undefined && out.timestamp === undefined)
15
+ out.timestamp = timestamp;
16
+ return out;
17
+ }
18
+ return (data ?? obj);
19
+ }
@@ -0,0 +1,26 @@
1
+ export declare class MuxiError extends Error {
2
+ readonly code: string;
3
+ readonly status: number;
4
+ readonly data?: unknown;
5
+ constructor(code: string, message: string, status: number, data?: unknown);
6
+ }
7
+ export declare class AuthenticationError extends MuxiError {
8
+ }
9
+ export declare class AuthorizationError extends MuxiError {
10
+ }
11
+ export declare class NotFoundError extends MuxiError {
12
+ }
13
+ export declare class ConflictError extends MuxiError {
14
+ }
15
+ export declare class ValidationError extends MuxiError {
16
+ }
17
+ export declare class RateLimitError extends MuxiError {
18
+ readonly retryAfter?: number;
19
+ constructor(message: string, status: number, retryAfter?: number, data?: unknown);
20
+ }
21
+ export declare class ServerError extends MuxiError {
22
+ }
23
+ export declare class ConnectionError extends MuxiError {
24
+ constructor(message: string);
25
+ }
26
+ export declare function mapError(status: number, code: string, message: string, details?: any, retryAfter?: number): MuxiError;
package/dist/errors.js ADDED
@@ -0,0 +1,48 @@
1
+ export class MuxiError extends Error {
2
+ constructor(code, message, status, data) {
3
+ super(message);
4
+ this.code = code;
5
+ this.status = status;
6
+ this.data = data;
7
+ }
8
+ }
9
+ export class AuthenticationError extends MuxiError {
10
+ }
11
+ export class AuthorizationError extends MuxiError {
12
+ }
13
+ export class NotFoundError extends MuxiError {
14
+ }
15
+ export class ConflictError extends MuxiError {
16
+ }
17
+ export class ValidationError extends MuxiError {
18
+ }
19
+ export class RateLimitError extends MuxiError {
20
+ constructor(message, status, retryAfter, data) {
21
+ super("RATE_LIMITED", message, status, data);
22
+ this.retryAfter = retryAfter;
23
+ }
24
+ }
25
+ export class ServerError extends MuxiError {
26
+ }
27
+ export class ConnectionError extends MuxiError {
28
+ constructor(message) {
29
+ super("CONNECTION_ERROR", message, 0);
30
+ }
31
+ }
32
+ export function mapError(status, code, message, details, retryAfter) {
33
+ if (status === 401)
34
+ return new AuthenticationError(code || "UNAUTHORIZED", message, status, details);
35
+ if (status === 403)
36
+ return new AuthorizationError(code || "FORBIDDEN", message, status, details);
37
+ if (status === 404)
38
+ return new NotFoundError(code || "NOT_FOUND", message, status, details);
39
+ if (status === 409)
40
+ return new ConflictError(code || "CONFLICT", message, status, details);
41
+ if (status === 422)
42
+ return new ValidationError(code || "VALIDATION_ERROR", message, status, details);
43
+ if (status === 429)
44
+ return new RateLimitError(message || "Too Many Requests", status, retryAfter, details);
45
+ if (status >= 500)
46
+ return new ServerError(code || "SERVER_ERROR", message, status, details);
47
+ return new MuxiError(code || "ERROR", message, status, details);
48
+ }
@@ -0,0 +1,89 @@
1
+ export interface FormationClientOptions {
2
+ formationId: string;
3
+ clientKey?: string;
4
+ adminKey?: string;
5
+ serverUrl?: string;
6
+ baseUrl?: string;
7
+ timeoutMs?: number;
8
+ maxRetries?: number;
9
+ debug?: boolean;
10
+ }
11
+ export interface RequestOptions {
12
+ params?: Record<string, any>;
13
+ body?: any;
14
+ userId?: string;
15
+ headers?: Record<string, string>;
16
+ }
17
+ export declare class FormationClient {
18
+ private readonly transport;
19
+ constructor(opts: FormationClientOptions);
20
+ health(): Promise<any>;
21
+ getStatus(): Promise<any>;
22
+ getConfig(): Promise<any>;
23
+ getFormationInfo(): Promise<any>;
24
+ getAgents(): Promise<any>;
25
+ getAgent(agentId: string): Promise<any>;
26
+ getMcpServers(): Promise<any>;
27
+ getMcpServer(id: string): Promise<any>;
28
+ getMcpTools(): Promise<any>;
29
+ getSecrets(): Promise<any>;
30
+ getSecret(key: string): Promise<any>;
31
+ setSecret(key: string, value: string): Promise<any>;
32
+ deleteSecret(key: string): Promise<any>;
33
+ chat(payload: Record<string, any>, userId?: string): Promise<any>;
34
+ chatStream(payload: Record<string, any>, userId?: string): AsyncGenerator<any, void, void>;
35
+ audioChat(payload: Record<string, any>, userId?: string): Promise<any>;
36
+ audioChatStream(payload: Record<string, any>, userId?: string): AsyncGenerator<any, void, void>;
37
+ getSessions(userId: string, limit?: number): Promise<any>;
38
+ getSession(sessionId: string, userId: string): Promise<any>;
39
+ getSessionMessages(sessionId: string, userId: string): Promise<any>;
40
+ restoreSession(sessionId: string, userId: string, messages: any[]): Promise<any>;
41
+ getRequests(userId: string): Promise<any>;
42
+ getRequestStatus(requestId: string, userId: string): Promise<any>;
43
+ cancelRequest(requestId: string, userId: string): Promise<any>;
44
+ getMemoryConfig(): Promise<any>;
45
+ getMemories(userId: string, limit?: number): Promise<any>;
46
+ addMemory(userId: string, type: string, detail: string): Promise<any>;
47
+ deleteMemory(userId: string, memoryId: string): Promise<any>;
48
+ getUserBuffer(userId: string): Promise<any>;
49
+ clearUserBuffer(userId: string): Promise<any>;
50
+ clearSessionBuffer(userId: string, sessionId: string): Promise<any>;
51
+ clearAllBuffers(): Promise<any>;
52
+ getMemoryBuffers(): Promise<any>;
53
+ getBufferStats(): Promise<any>;
54
+ getSchedulerConfig(): Promise<any>;
55
+ getSchedulerJobs(userId: string): Promise<any>;
56
+ getSchedulerJob(jobId: string): Promise<any>;
57
+ createSchedulerJob(jobType: string, schedule: string, message: string, userId: string): Promise<any>;
58
+ deleteSchedulerJob(jobId: string): Promise<any>;
59
+ getAsyncConfig(): Promise<any>;
60
+ getAsyncJobs(): Promise<any>;
61
+ getAsyncJob(jobId: string): Promise<any>;
62
+ cancelAsyncJob(jobId: string): Promise<any>;
63
+ getA2AConfig(): Promise<any>;
64
+ getLoggingConfig(): Promise<any>;
65
+ getLoggingDestinations(): Promise<any>;
66
+ listCredentialServices(): Promise<any>;
67
+ listCredentials(userId: string): Promise<any>;
68
+ getCredential(credentialId: string, userId: string): Promise<any>;
69
+ createCredential(userId: string, payload: Record<string, any>): Promise<any>;
70
+ deleteCredential(credentialId: string, userId: string): Promise<any>;
71
+ getUserIdentifiers(): Promise<any>;
72
+ getUserIdentifiersForUser(userId: string): Promise<any>;
73
+ linkUserIdentifier(muxiUserId: string, identifiers: any[]): Promise<any>;
74
+ unlinkUserIdentifier(identifier: string): Promise<any>;
75
+ getOverlordConfig(): Promise<any>;
76
+ getOverlordPersona(): Promise<any>;
77
+ getLlmSettings(): Promise<any>;
78
+ getTriggers(): Promise<any>;
79
+ getTrigger(name: string): Promise<any>;
80
+ fireTrigger(name: string, data: any, asyncMode?: boolean, userId?: string): Promise<any>;
81
+ getSops(): Promise<any>;
82
+ getSop(name: string): Promise<any>;
83
+ getAuditLog(): Promise<any>;
84
+ clearAuditLog(): Promise<any>;
85
+ streamEvents(userId: string): AsyncGenerator<any, void, void>;
86
+ streamRequest(userId: string, sessionId: string, requestId: string): AsyncGenerator<any, void, void>;
87
+ streamLogs(filters?: Record<string, any>): AsyncGenerator<any, void, void>;
88
+ resolveUser(identifier: string, createUser?: boolean): Promise<any>;
89
+ }
@@ -0,0 +1,262 @@
1
+ import { unwrapEnvelope } from "./envelope.js";
2
+ import { ConnectionError, MuxiError, mapError } from "./errors.js";
3
+ import { version } from "./version.js";
4
+ import { randomUUID } from "crypto";
5
+ const RETRY_STATUS = new Set([429, 500, 502, 503, 504]);
6
+ function computeBaseUrl(opts) {
7
+ if (opts.baseUrl)
8
+ return opts.baseUrl.replace(/\/$/, "");
9
+ if (!opts.serverUrl)
10
+ throw new Error("serverUrl or baseUrl is required");
11
+ return `${opts.serverUrl.replace(/\/$/, "")}/api/${opts.formationId}/v1`;
12
+ }
13
+ function buildUrl(baseUrl, path, params) {
14
+ const rel = path.startsWith("/") ? path : `/${path}`;
15
+ const search = new URLSearchParams();
16
+ Object.entries(params || {}).forEach(([k, v]) => {
17
+ if (v === undefined || v === null)
18
+ return;
19
+ search.set(k, String(v));
20
+ });
21
+ const query = search.toString();
22
+ const fullPath = query ? `${rel}?${query}` : rel;
23
+ return { url: `${baseUrl}${fullPath}`, fullPath };
24
+ }
25
+ async function parseJson(resp) {
26
+ const text = await resp.text();
27
+ if (!text)
28
+ return undefined;
29
+ try {
30
+ return JSON.parse(text);
31
+ }
32
+ catch {
33
+ return text;
34
+ }
35
+ }
36
+ class FormationTransport {
37
+ constructor(baseUrl, adminKey, clientKey, timeoutMs, maxRetries, debug) {
38
+ this.baseUrl = baseUrl;
39
+ this.adminKey = adminKey?.trim();
40
+ this.clientKey = clientKey?.trim();
41
+ this.timeoutMs = timeoutMs ?? 30_000;
42
+ this.maxRetries = maxRetries ?? 0;
43
+ this.debug = !!debug;
44
+ }
45
+ headers(useAdmin, userId, extra) {
46
+ const headers = {
47
+ "X-Muxi-SDK": `typescript/${version}`,
48
+ "X-Muxi-Client": `node-${process.version}`,
49
+ "X-Muxi-Idempotency-Key": randomUUID(),
50
+ };
51
+ if (useAdmin) {
52
+ if (!this.adminKey)
53
+ throw new Error("admin key required");
54
+ headers["X-MUXI-ADMIN-KEY"] = this.adminKey;
55
+ }
56
+ else {
57
+ if (!this.clientKey)
58
+ throw new Error("client key required");
59
+ headers["X-MUXI-CLIENT-KEY"] = this.clientKey;
60
+ }
61
+ if (userId)
62
+ headers["X-Muxi-User-ID"] = userId;
63
+ if (extra)
64
+ Object.assign(headers, extra);
65
+ return headers;
66
+ }
67
+ async requestJson(method, path, opts) {
68
+ const { url, fullPath } = buildUrl(this.baseUrl, path, opts.params);
69
+ const headers = this.headers(opts.useAdmin, opts.userId, {
70
+ ...(opts.body !== undefined ? { "Content-Type": "application/json" } : {}),
71
+ ...(opts.headers || {}),
72
+ });
73
+ const body = opts.body === undefined ? undefined : JSON.stringify(opts.body);
74
+ let attempt = 0;
75
+ let backoff = 500;
76
+ while (true) {
77
+ const controller = new AbortController();
78
+ const timeout = setTimeout(() => controller.abort(), this.timeoutMs);
79
+ try {
80
+ const resp = await fetch(url, { method, headers, body, signal: controller.signal });
81
+ clearTimeout(timeout);
82
+ if (this.debug)
83
+ console.debug(`${method} ${fullPath} -> ${resp.status}`);
84
+ if (resp.status >= 400) {
85
+ const payload = await parseJson(resp);
86
+ const code = payload?.code || payload?.error || "ERROR";
87
+ const message = payload?.message || resp.statusText;
88
+ const retryAfter = Number(resp.headers.get("Retry-After") || 0);
89
+ if (RETRY_STATUS.has(resp.status) && attempt < this.maxRetries) {
90
+ const sleepFor = Math.min(backoff, 30_000);
91
+ await new Promise((r) => setTimeout(r, sleepFor));
92
+ backoff *= 2;
93
+ attempt += 1;
94
+ continue;
95
+ }
96
+ throw mapError(resp.status, code, message, payload, retryAfter);
97
+ }
98
+ const data = await parseJson(resp);
99
+ return unwrapEnvelope(data);
100
+ }
101
+ catch (err) {
102
+ clearTimeout(timeout);
103
+ if (err instanceof MuxiError)
104
+ throw err;
105
+ if (attempt < this.maxRetries) {
106
+ const sleepFor = Math.min(backoff, 30_000);
107
+ await new Promise((r) => setTimeout(r, sleepFor));
108
+ backoff *= 2;
109
+ attempt += 1;
110
+ continue;
111
+ }
112
+ throw new ConnectionError(err?.message || String(err));
113
+ }
114
+ }
115
+ }
116
+ async *streamSse(method, path, opts) {
117
+ const { url, fullPath } = buildUrl(this.baseUrl, path, opts.params);
118
+ const headers = this.headers(opts.useAdmin, opts.userId, {
119
+ Accept: "text/event-stream",
120
+ ...(opts.body !== undefined ? { "Content-Type": "application/json" } : {}),
121
+ ...(opts.headers || {}),
122
+ });
123
+ const body = opts.body === undefined ? undefined : JSON.stringify(opts.body);
124
+ const resp = await fetch(url, { method, headers, body });
125
+ if (!resp.ok || !resp.body) {
126
+ const payload = await parseJson(resp);
127
+ throw mapError(resp.status, payload?.code || "STREAM_ERROR", payload?.message || resp.statusText, payload);
128
+ }
129
+ if (this.debug)
130
+ console.debug(`${method} ${fullPath} -> stream ${resp.status}`);
131
+ const reader = resp.body.getReader();
132
+ const decoder = new TextDecoder();
133
+ let buffer = "";
134
+ while (true) {
135
+ const { done, value } = await reader.read();
136
+ if (done)
137
+ break;
138
+ buffer += decoder.decode(value, { stream: true });
139
+ let idx;
140
+ while ((idx = buffer.indexOf("\n")) >= 0) {
141
+ const line = buffer.slice(0, idx).trimEnd();
142
+ buffer = buffer.slice(idx + 1);
143
+ if (!line)
144
+ continue;
145
+ if (line.startsWith("data:")) {
146
+ const payload = line.slice(5).trim();
147
+ if (!payload)
148
+ continue;
149
+ try {
150
+ yield JSON.parse(payload);
151
+ }
152
+ catch {
153
+ yield payload;
154
+ }
155
+ }
156
+ }
157
+ }
158
+ if (buffer.trim()) {
159
+ const payload = buffer.trim();
160
+ try {
161
+ yield JSON.parse(payload);
162
+ }
163
+ catch {
164
+ yield payload;
165
+ }
166
+ }
167
+ }
168
+ }
169
+ export class FormationClient {
170
+ constructor(opts) {
171
+ const baseUrl = computeBaseUrl(opts);
172
+ this.transport = new FormationTransport(baseUrl, opts.adminKey, opts.clientKey, opts.timeoutMs, opts.maxRetries, opts.debug);
173
+ }
174
+ // Health / status / config
175
+ health() { return this.transport.requestJson("GET", "/health", { useAdmin: false }); }
176
+ getStatus() { return this.transport.requestJson("GET", "/status", { useAdmin: true }); }
177
+ getConfig() { return this.transport.requestJson("GET", "/config", { useAdmin: true }); }
178
+ getFormationInfo() { return this.transport.requestJson("GET", "/formation", { useAdmin: true }); }
179
+ // Agents / MCP
180
+ getAgents() { return this.transport.requestJson("GET", "/agents", { useAdmin: true }); }
181
+ getAgent(agentId) { return this.transport.requestJson("GET", `/agents/${agentId}`, { useAdmin: true }); }
182
+ getMcpServers() { return this.transport.requestJson("GET", "/mcp/servers", { useAdmin: true }); }
183
+ getMcpServer(id) { return this.transport.requestJson("GET", `/mcp/servers/${id}`, { useAdmin: true }); }
184
+ getMcpTools() { return this.transport.requestJson("GET", "/mcp/tools", { useAdmin: true }); }
185
+ // Secrets
186
+ getSecrets() { return this.transport.requestJson("GET", "/secrets", { useAdmin: true }); }
187
+ getSecret(key) { return this.transport.requestJson("GET", `/secrets/${key}`, { useAdmin: true }); }
188
+ setSecret(key, value) { return this.transport.requestJson("POST", `/secrets/${key}`, { useAdmin: true, body: { value } }); }
189
+ deleteSecret(key) { return this.transport.requestJson("DELETE", `/secrets/${key}`, { useAdmin: true }); }
190
+ // Chat
191
+ chat(payload, userId = "") { return this.transport.requestJson("POST", "/chat", { useAdmin: false, body: payload, userId }); }
192
+ chatStream(payload, userId = "") {
193
+ return this.transport.streamSse("POST", "/chat", { useAdmin: false, body: { ...payload, stream: true }, userId });
194
+ }
195
+ audioChat(payload, userId = "") { return this.transport.requestJson("POST", "/audiochat", { useAdmin: false, body: payload, userId }); }
196
+ audioChatStream(payload, userId = "") {
197
+ return this.transport.streamSse("POST", "/audiochat", { useAdmin: false, body: { ...payload, stream: true }, userId });
198
+ }
199
+ // Sessions / requests
200
+ getSessions(userId, limit) { return this.transport.requestJson("GET", "/sessions", { useAdmin: false, params: { user_id: userId, limit }, userId }); }
201
+ getSession(sessionId, userId) { return this.transport.requestJson("GET", `/sessions/${sessionId}`, { useAdmin: false, userId }); }
202
+ getSessionMessages(sessionId, userId) { return this.transport.requestJson("GET", `/sessions/${sessionId}/messages`, { useAdmin: false, userId }); }
203
+ restoreSession(sessionId, userId, messages) { return this.transport.requestJson("POST", `/sessions/${sessionId}/restore`, { useAdmin: false, userId, body: { messages } }); }
204
+ getRequests(userId) { return this.transport.requestJson("GET", "/requests", { useAdmin: false, userId }); }
205
+ getRequestStatus(requestId, userId) { return this.transport.requestJson("GET", `/requests/${requestId}`, { useAdmin: false, userId }); }
206
+ cancelRequest(requestId, userId) { return this.transport.requestJson("DELETE", `/requests/${requestId}`, { useAdmin: false, userId }); }
207
+ // Memory
208
+ getMemoryConfig() { return this.transport.requestJson("GET", "/memory", { useAdmin: true }); }
209
+ getMemories(userId, limit) { return this.transport.requestJson("GET", "/memory/user", { useAdmin: false, params: { user_id: userId, limit } }); }
210
+ addMemory(userId, type, detail) { return this.transport.requestJson("POST", "/memory", { useAdmin: false, body: { user_id: userId, type, detail } }); }
211
+ deleteMemory(userId, memoryId) { return this.transport.requestJson("DELETE", `/memory/${memoryId}`, { useAdmin: false, params: { user_id: userId } }); }
212
+ getUserBuffer(userId) { return this.transport.requestJson("GET", `/memory/buffer/${userId}`, { useAdmin: false }); }
213
+ clearUserBuffer(userId) { return this.transport.requestJson("DELETE", `/memory/buffer/${userId}`, { useAdmin: false }); }
214
+ clearSessionBuffer(userId, sessionId) { return this.transport.requestJson("DELETE", `/memory/buffer/${userId}/${sessionId}`, { useAdmin: false }); }
215
+ clearAllBuffers() { return this.transport.requestJson("DELETE", "/memory/buffer", { useAdmin: true }); }
216
+ getMemoryBuffers() { return this.transport.requestJson("GET", "/memory/buffers", { useAdmin: true }); }
217
+ getBufferStats() { return this.transport.requestJson("GET", "/memory/stats", { useAdmin: true }); }
218
+ // Scheduler
219
+ getSchedulerConfig() { return this.transport.requestJson("GET", "/scheduler/config", { useAdmin: true }); }
220
+ getSchedulerJobs(userId) { return this.transport.requestJson("GET", "/scheduler/jobs", { useAdmin: true, params: { user_id: userId } }); }
221
+ getSchedulerJob(jobId) { return this.transport.requestJson("GET", `/scheduler/jobs/${jobId}`, { useAdmin: true }); }
222
+ createSchedulerJob(jobType, schedule, message, userId) {
223
+ return this.transport.requestJson("POST", "/scheduler/jobs", { useAdmin: true, body: { type: jobType, schedule, message, user_id: userId } });
224
+ }
225
+ deleteSchedulerJob(jobId) { return this.transport.requestJson("DELETE", `/scheduler/jobs/${jobId}`, { useAdmin: true }); }
226
+ // Async / logging / a2a
227
+ getAsyncConfig() { return this.transport.requestJson("GET", "/async", { useAdmin: true }); }
228
+ getAsyncJobs() { return this.transport.requestJson("GET", "/async/jobs", { useAdmin: true }); }
229
+ getAsyncJob(jobId) { return this.transport.requestJson("GET", `/async/jobs/${jobId}`, { useAdmin: true }); }
230
+ cancelAsyncJob(jobId) { return this.transport.requestJson("DELETE", `/async/jobs/${jobId}`, { useAdmin: true }); }
231
+ getA2AConfig() { return this.transport.requestJson("GET", "/a2a", { useAdmin: true }); }
232
+ getLoggingConfig() { return this.transport.requestJson("GET", "/logging", { useAdmin: true }); }
233
+ getLoggingDestinations() { return this.transport.requestJson("GET", "/logging/destinations", { useAdmin: true }); }
234
+ // Credentials / identifiers
235
+ listCredentialServices() { return this.transport.requestJson("GET", "/credentials/services", { useAdmin: true }); }
236
+ listCredentials(userId) { return this.transport.requestJson("GET", "/credentials", { useAdmin: false, userId }); }
237
+ getCredential(credentialId, userId) { return this.transport.requestJson("GET", `/credentials/${credentialId}`, { useAdmin: false, userId }); }
238
+ createCredential(userId, payload) { return this.transport.requestJson("POST", "/credentials", { useAdmin: false, userId, body: payload }); }
239
+ deleteCredential(credentialId, userId) { return this.transport.requestJson("DELETE", `/credentials/${credentialId}`, { useAdmin: false, userId }); }
240
+ getUserIdentifiers() { return this.transport.requestJson("GET", "/users/identifiers", { useAdmin: true }); }
241
+ getUserIdentifiersForUser(userId) { return this.transport.requestJson("GET", `/users/${userId}/identifiers`, { useAdmin: true }); }
242
+ linkUserIdentifier(muxiUserId, identifiers) { return this.transport.requestJson("POST", "/users/identifiers", { useAdmin: true, body: { muxi_user_id: muxiUserId, identifiers } }); }
243
+ unlinkUserIdentifier(identifier) { return this.transport.requestJson("DELETE", `/users/identifiers/${identifier}`, { useAdmin: true }); }
244
+ // Overlord / LLM
245
+ getOverlordConfig() { return this.transport.requestJson("GET", "/overlord", { useAdmin: true }); }
246
+ getOverlordPersona() { return this.transport.requestJson("GET", "/overlord/persona", { useAdmin: true }); }
247
+ getLlmSettings() { return this.transport.requestJson("GET", "/llm/settings", { useAdmin: true }); }
248
+ // Triggers / SOP / Audit
249
+ getTriggers() { return this.transport.requestJson("GET", "/triggers", { useAdmin: false }); }
250
+ getTrigger(name) { return this.transport.requestJson("GET", `/triggers/${name}`, { useAdmin: false }); }
251
+ fireTrigger(name, data, asyncMode = false, userId = "") { return this.transport.requestJson("POST", `/triggers/${name}`, { useAdmin: false, userId, params: { async: String(asyncMode).toLowerCase() }, body: data }); }
252
+ getSops() { return this.transport.requestJson("GET", "/sops", { useAdmin: false }); }
253
+ getSop(name) { return this.transport.requestJson("GET", `/sops/${name}`, { useAdmin: false }); }
254
+ getAuditLog() { return this.transport.requestJson("GET", "/audit", { useAdmin: true }); }
255
+ clearAuditLog() { return this.transport.requestJson("DELETE", "/audit", { useAdmin: true, params: { confirm: "clear-audit-log" } }); }
256
+ // Events / logs streaming
257
+ streamEvents(userId) { return this.transport.streamSse("GET", `/events/${userId}`, { useAdmin: false }); }
258
+ streamRequest(userId, sessionId, requestId) { return this.transport.streamSse("GET", `/requests/${requestId}/stream`, { useAdmin: false, params: { user_id: userId, session_id: sessionId } }); }
259
+ streamLogs(filters) { return this.transport.streamSse("POST", "/logs/stream", { useAdmin: true, body: filters || {} }); }
260
+ // Resolve user
261
+ resolveUser(identifier, createUser = false) { return this.transport.requestJson("GET", "/users/resolve", { useAdmin: false, params: { identifier, create_user: String(createUser).toLowerCase() } }); }
262
+ }
@@ -0,0 +1,4 @@
1
+ export { ServerClient, type ServerClientOptions } from "./server.js";
2
+ export { FormationClient, type FormationClientOptions } from "./formation.js";
3
+ export { MuxiError, ConnectionError } from "./errors.js";
4
+ export { version } from "./version.js";
package/dist/index.js ADDED
@@ -0,0 +1,4 @@
1
+ export { ServerClient } from "./server.js";
2
+ export { FormationClient } from "./formation.js";
3
+ export { MuxiError, ConnectionError } from "./errors.js";
4
+ export { version } from "./version.js";
@@ -0,0 +1,37 @@
1
+ import type { RequestOptions } from "./transport.js";
2
+ export interface ServerClientOptions {
3
+ url: string;
4
+ keyId: string;
5
+ secretKey: string;
6
+ timeoutMs?: number;
7
+ maxRetries?: number;
8
+ debug?: boolean;
9
+ }
10
+ export declare class ServerClient {
11
+ private readonly transport;
12
+ constructor(opts: ServerClientOptions);
13
+ ping(): Promise<any>;
14
+ health(): Promise<any>;
15
+ status(): Promise<any>;
16
+ listFormations(): Promise<any>;
17
+ getFormation(id: string): Promise<any>;
18
+ stopFormation(id: string): Promise<any>;
19
+ startFormation(id: string): Promise<any>;
20
+ restartFormation(id: string): Promise<any>;
21
+ rollbackFormation(id: string): Promise<any>;
22
+ deleteFormation(id: string): Promise<any>;
23
+ cancelUpdate(id: string): Promise<any>;
24
+ deployFormation(id: string, payload: Record<string, any>): Promise<any>;
25
+ updateFormation(id: string, payload: Record<string, any>): Promise<any>;
26
+ getFormationLogs(id: string, limit?: number): Promise<any>;
27
+ getServerLogs(limit?: number): Promise<any>;
28
+ deployFormationStream(id: string, payload: Record<string, any>): AsyncGenerator<any, void, unknown>;
29
+ updateFormationStream(id: string, payload: Record<string, any>): AsyncGenerator<any, void, unknown>;
30
+ startFormationStream(id: string): AsyncGenerator<any, void, unknown>;
31
+ restartFormationStream(id: string): AsyncGenerator<any, void, unknown>;
32
+ rollbackFormationStream(id: string): AsyncGenerator<any, void, unknown>;
33
+ streamFormationLogs(id: string): AsyncGenerator<any, void, unknown>;
34
+ _rpcGet<T = any>(path: string, options?: RequestOptions): Promise<T>;
35
+ _rpcPost<T = any>(path: string, body: Record<string, any>): Promise<T>;
36
+ _rpcDelete<T = any>(path: string): Promise<T>;
37
+ }
package/dist/server.js ADDED
@@ -0,0 +1,60 @@
1
+ import { Transport } from "./transport.js";
2
+ async function* parseSseLines(lines) {
3
+ for await (const line of lines) {
4
+ const trimmed = line.trim();
5
+ if (!trimmed)
6
+ continue;
7
+ if (!trimmed.startsWith("data:"))
8
+ continue;
9
+ const payload = trimmed.slice(5).trim();
10
+ if (!payload)
11
+ continue;
12
+ try {
13
+ yield JSON.parse(payload);
14
+ }
15
+ catch {
16
+ yield payload;
17
+ }
18
+ }
19
+ }
20
+ export class ServerClient {
21
+ constructor(opts) {
22
+ const topts = {
23
+ baseUrl: opts.url.replace(/\/$/, ""),
24
+ keyId: opts.keyId,
25
+ secretKey: opts.secretKey,
26
+ timeoutMs: opts.timeoutMs,
27
+ maxRetries: opts.maxRetries,
28
+ debug: opts.debug,
29
+ };
30
+ this.transport = new Transport(topts);
31
+ }
32
+ // Unauthenticated
33
+ ping() { return this.transport.requestJson("GET", "/ping"); }
34
+ health() { return this.transport.requestJson("GET", "/health"); }
35
+ // Authenticated
36
+ status() { return this._rpcGet("/rpc/server/status"); }
37
+ listFormations() { return this._rpcGet("/rpc/formations"); }
38
+ getFormation(id) { return this._rpcGet(`/rpc/formations/${id}`); }
39
+ stopFormation(id) { return this._rpcPost(`/rpc/formations/${id}/stop`, {}); }
40
+ startFormation(id) { return this._rpcPost(`/rpc/formations/${id}/start`, {}); }
41
+ restartFormation(id) { return this._rpcPost(`/rpc/formations/${id}/restart`, {}); }
42
+ rollbackFormation(id) { return this._rpcPost(`/rpc/formations/${id}/rollback`, {}); }
43
+ deleteFormation(id) { return this._rpcDelete(`/rpc/formations/${id}`); }
44
+ cancelUpdate(id) { return this._rpcPost(`/rpc/formations/${id}/cancel-update`, {}); }
45
+ deployFormation(id, payload) { return this._rpcPost(`/rpc/formations/${id}/deploy`, payload); }
46
+ updateFormation(id, payload) { return this._rpcPost(`/rpc/formations/${id}/update`, payload); }
47
+ getFormationLogs(id, limit) { return this._rpcGet(`/rpc/formations/${id}/logs`, { params: limit !== undefined ? { limit } : undefined }); }
48
+ getServerLogs(limit) { return this._rpcGet(`/rpc/server/logs`, { params: limit !== undefined ? { limit } : undefined }); }
49
+ // Streaming
50
+ deployFormationStream(id, payload) { return parseSseLines(this.transport.streamLines("POST", `/rpc/formations/${id}/deploy/stream`, { body: payload })); }
51
+ updateFormationStream(id, payload) { return parseSseLines(this.transport.streamLines("POST", `/rpc/formations/${id}/update/stream`, { body: payload })); }
52
+ startFormationStream(id) { return parseSseLines(this.transport.streamLines("POST", `/rpc/formations/${id}/start/stream`, { body: {} })); }
53
+ restartFormationStream(id) { return parseSseLines(this.transport.streamLines("POST", `/rpc/formations/${id}/restart/stream`, { body: {} })); }
54
+ rollbackFormationStream(id) { return parseSseLines(this.transport.streamLines("POST", `/rpc/formations/${id}/rollback/stream`, { body: {} })); }
55
+ streamFormationLogs(id) { return parseSseLines(this.transport.streamLines("GET", `/rpc/formations/${id}/logs/stream`)); }
56
+ // Generic RPC helpers
57
+ _rpcGet(path, options = {}) { return this.transport.requestJson("GET", path, options); }
58
+ _rpcPost(path, body) { return this.transport.requestJson("POST", path, { body }); }
59
+ _rpcDelete(path) { return this.transport.requestJson("DELETE", path); }
60
+ }
@@ -0,0 +1,21 @@
1
+ export interface TransportOptions {
2
+ baseUrl: string;
3
+ keyId?: string;
4
+ secretKey?: string;
5
+ timeoutMs?: number;
6
+ maxRetries?: number;
7
+ debug?: boolean;
8
+ }
9
+ export interface RequestOptions {
10
+ params?: Record<string, any>;
11
+ body?: any;
12
+ headers?: Record<string, string>;
13
+ authPath?: string;
14
+ stream?: boolean;
15
+ }
16
+ export declare class Transport {
17
+ private readonly opts;
18
+ constructor(opts: TransportOptions);
19
+ requestJson<T = any>(method: string, path: string, options?: RequestOptions): Promise<T>;
20
+ streamLines(method: string, path: string, options?: RequestOptions): AsyncGenerator<string, void, void>;
21
+ }
@@ -0,0 +1,138 @@
1
+ import { buildAuthHeader } from "./auth.js";
2
+ import { unwrapEnvelope } from "./envelope.js";
3
+ import { ConnectionError, MuxiError, mapError } from "./errors.js";
4
+ import { version } from "./version.js";
5
+ import { randomUUID } from "crypto";
6
+ const RETRY_STATUS = new Set([429, 500, 502, 503, 504]);
7
+ function buildUrl(baseUrl, path, params) {
8
+ const rel = path.startsWith("/") ? path : `/${path}`;
9
+ const search = new URLSearchParams();
10
+ Object.entries(params || {}).forEach(([k, v]) => {
11
+ if (v === undefined || v === null)
12
+ return;
13
+ search.set(k, String(v));
14
+ });
15
+ const query = search.toString();
16
+ const fullPath = query ? `${rel}?${query}` : rel;
17
+ return { url: `${baseUrl}${fullPath}`, fullPath };
18
+ }
19
+ function baseHeaders(opts, method, pathForAuth, extra) {
20
+ const headers = {
21
+ "Content-Type": "application/json",
22
+ "X-Muxi-SDK": `typescript/${version}`,
23
+ "X-Muxi-Client": `node-${process.version}`,
24
+ "X-Muxi-Idempotency-Key": randomUUID(),
25
+ };
26
+ if (opts.keyId && opts.secretKey) {
27
+ headers.Authorization = buildAuthHeader(opts.keyId.trim(), opts.secretKey.trim(), method, pathForAuth);
28
+ }
29
+ if (extra)
30
+ Object.assign(headers, extra);
31
+ return headers;
32
+ }
33
+ async function parseJson(resp) {
34
+ const text = await resp.text();
35
+ if (!text)
36
+ return undefined;
37
+ try {
38
+ return JSON.parse(text);
39
+ }
40
+ catch {
41
+ return text;
42
+ }
43
+ }
44
+ export class Transport {
45
+ constructor(opts) {
46
+ this.opts = { ...opts, timeoutMs: opts.timeoutMs ?? 30_000, maxRetries: opts.maxRetries ?? 0 };
47
+ }
48
+ async requestJson(method, path, options = {}) {
49
+ const { url, fullPath } = buildUrl(this.opts.baseUrl, path, options.params);
50
+ const headers = baseHeaders(this.opts, method, options.authPath ?? fullPath, options.headers);
51
+ const body = options.body === undefined ? undefined : JSON.stringify(options.body);
52
+ let attempt = 0;
53
+ let backoff = 500;
54
+ while (true) {
55
+ const controller = new AbortController();
56
+ const timeout = setTimeout(() => controller.abort(), this.opts.timeoutMs);
57
+ try {
58
+ const resp = await fetch(url, {
59
+ method,
60
+ headers,
61
+ body,
62
+ signal: controller.signal,
63
+ });
64
+ clearTimeout(timeout);
65
+ if (this.opts.debug) {
66
+ console.debug(`${method} ${url} -> ${resp.status}`);
67
+ }
68
+ if (resp.status >= 400) {
69
+ const payload = await parseJson(resp);
70
+ const code = payload?.code || payload?.error || "ERROR";
71
+ const message = payload?.message || resp.statusText;
72
+ const retryAfter = Number(resp.headers.get("Retry-After") || 0);
73
+ if (RETRY_STATUS.has(resp.status) && attempt < (this.opts.maxRetries ?? 0)) {
74
+ const sleepFor = Math.min(backoff, 30_000);
75
+ await new Promise((r) => setTimeout(r, sleepFor));
76
+ backoff *= 2;
77
+ attempt += 1;
78
+ continue;
79
+ }
80
+ throw mapError(resp.status, code, message, payload, retryAfter);
81
+ }
82
+ const data = await parseJson(resp);
83
+ return unwrapEnvelope(data);
84
+ }
85
+ catch (err) {
86
+ clearTimeout(timeout);
87
+ if (err instanceof MuxiError)
88
+ throw err;
89
+ if (attempt < (this.opts.maxRetries ?? 0)) {
90
+ const sleepFor = Math.min(backoff, 30_000);
91
+ await new Promise((r) => setTimeout(r, sleepFor));
92
+ backoff *= 2;
93
+ attempt += 1;
94
+ continue;
95
+ }
96
+ throw new ConnectionError(err?.message || String(err));
97
+ }
98
+ }
99
+ }
100
+ async *streamLines(method, path, options = {}) {
101
+ const { url, fullPath } = buildUrl(this.opts.baseUrl, path, options.params);
102
+ const headers = baseHeaders(this.opts, method, options.authPath ?? fullPath, {
103
+ Accept: "text/event-stream",
104
+ ...(options.headers || {}),
105
+ });
106
+ const body = options.body === undefined ? undefined : JSON.stringify(options.body);
107
+ const resp = await fetch(url, {
108
+ method,
109
+ headers,
110
+ body,
111
+ signal: undefined,
112
+ });
113
+ if (!resp.ok || !resp.body) {
114
+ const payload = await parseJson(resp);
115
+ throw new MuxiError(payload?.code || "STREAM_ERROR", payload?.message || resp.statusText, resp.status, payload);
116
+ }
117
+ const reader = resp.body.getReader();
118
+ const decoder = new TextDecoder();
119
+ let buffer = "";
120
+ while (true) {
121
+ const { done, value } = await reader.read();
122
+ if (done)
123
+ break;
124
+ buffer += decoder.decode(value, { stream: true });
125
+ let idx;
126
+ while ((idx = buffer.indexOf("\n")) >= 0) {
127
+ const line = buffer.slice(0, idx).trimEnd();
128
+ buffer = buffer.slice(idx + 1);
129
+ if (!line)
130
+ continue;
131
+ yield line;
132
+ }
133
+ }
134
+ if (buffer.trim()) {
135
+ yield buffer.trim();
136
+ }
137
+ }
138
+ }
@@ -0,0 +1 @@
1
+ export declare const version = "0.0.0";
@@ -0,0 +1 @@
1
+ export const version = "0.0.0";
package/package.json ADDED
@@ -0,0 +1,46 @@
1
+ {
2
+ "name": "@muxi-ai/muxi-typescript",
3
+ "version": "0.20260107.0",
4
+ "description": "Official MUXI SDK for TypeScript",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "main": "dist/index.js",
8
+ "types": "dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js",
13
+ "default": "./dist/index.js"
14
+ }
15
+ },
16
+ "files": [
17
+ "dist"
18
+ ],
19
+ "scripts": {
20
+ "clean": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\"",
21
+ "build": "npm run clean && tsc -p tsconfig.json",
22
+ "test": "npm run build && npm run test:unit && npm run test:e2e",
23
+ "test:unit": "node --test --loader ts-node/esm tests/unit/*.test.ts",
24
+ "test:e2e": "node --test --loader ts-node/esm tests/e2e/*.test.ts"
25
+ },
26
+ "engines": {
27
+ "node": ">=18"
28
+ },
29
+ "publishConfig": {
30
+ "access": "public",
31
+ "provenance": true
32
+ },
33
+ "repository": {
34
+ "type": "git",
35
+ "url": "git+https://github.com/muxi-ai/muxi-typescript.git"
36
+ },
37
+ "bugs": {
38
+ "url": "https://github.com/muxi-ai/muxi-typescript/issues"
39
+ },
40
+ "homepage": "https://github.com/muxi-ai/muxi-typescript#readme",
41
+ "devDependencies": {
42
+ "@types/node": "^20.11.24",
43
+ "ts-node": "^10.9.2",
44
+ "typescript": "^5.3.3"
45
+ }
46
+ }