@llmsafespaces/sdk 0.22.0 → 0.24.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/index.cjs CHANGED
@@ -56,6 +56,8 @@ var NotFoundError = class extends LLMSafeSpacesError {
56
56
  }
57
57
  };
58
58
  var ConflictError = class extends LLMSafeSpacesError {
59
+ /** Current workspace phase, when the 409 body carries one (upload phase gate, Epic 67 D5). */
60
+ phase;
59
61
  constructor(message) {
60
62
  super(message, 409, "CONFLICT");
61
63
  this.name = "ConflictError";
@@ -120,6 +122,9 @@ var LLMSafeSpaces = class {
120
122
  agentRoles;
121
123
  workflows;
122
124
  triggers;
125
+ mcpServers;
126
+ adminMcpServers;
127
+ orgMcpServers;
123
128
  constructor(options) {
124
129
  this.baseUrl = options.baseUrl.replace(/\/$/, "");
125
130
  this.timeout = options.timeout ?? DEFAULT_TIMEOUT;
@@ -142,11 +147,15 @@ var LLMSafeSpaces = class {
142
147
  this.agentRoles = new AgentRolesAPI(this);
143
148
  this.workflows = new WorkflowsAPI(this);
144
149
  this.triggers = new TriggersAPI(this);
150
+ this.mcpServers = new McpServersAPI(this);
151
+ this.adminMcpServers = new AdminMcpServersAPI(this);
152
+ this.orgMcpServers = new OrgMcpServersAPI(this);
145
153
  }
146
154
  /** Internal: make an authenticated request. */
147
155
  async request(method, path, body, timeout) {
148
156
  const url = `${this.baseUrl}/api/v1${path}`;
149
- const headers = { "Content-Type": "application/json" };
157
+ const isForm = typeof FormData !== "undefined" && body instanceof FormData;
158
+ const headers = isForm ? {} : { "Content-Type": "application/json" };
150
159
  if (this.apiKey) {
151
160
  headers["Authorization"] = `Bearer ${this.apiKey}`;
152
161
  } else if (this.token) {
@@ -162,7 +171,7 @@ var LLMSafeSpaces = class {
162
171
  res = await this.fetchFn(url, {
163
172
  method,
164
173
  headers,
165
- body: body ? JSON.stringify(body) : void 0,
174
+ body: body ? isForm ? body : JSON.stringify(body) : void 0,
166
175
  signal: controller.signal
167
176
  });
168
177
  } catch (e) {
@@ -186,8 +195,12 @@ var LLMSafeSpaces = class {
186
195
  throw new AuthError(msg, res.status);
187
196
  case 404:
188
197
  throw new NotFoundError(msg);
189
- case 409:
190
- throw new ConflictError(msg);
198
+ case 409: {
199
+ const phase = errBody.phase;
200
+ const conflict = new ConflictError(msg);
201
+ if (phase) conflict.phase = phase;
202
+ throw conflict;
203
+ }
191
204
  case 429:
192
205
  throw new RateLimitError(msg);
193
206
  case 503: {
@@ -205,6 +218,52 @@ var LLMSafeSpaces = class {
205
218
  if (text === "") return void 0;
206
219
  return JSON.parse(text);
207
220
  }
221
+ /**
222
+ * Internal: like {@link request}, but also returns the response headers
223
+ * (e.g. pagination cursors). Body decoding follows the same contract.
224
+ */
225
+ async requestWithHeaders(method, path, body, timeout) {
226
+ const url = `${this.baseUrl}/api/v1${path}`;
227
+ const headers = { "Content-Type": "application/json" };
228
+ if (this.apiKey) {
229
+ headers["Authorization"] = `Bearer ${this.apiKey}`;
230
+ } else if (this.token) {
231
+ headers["Authorization"] = `Bearer ${this.token}`;
232
+ }
233
+ const controller = new AbortController();
234
+ const timer = setTimeout(() => controller.abort(), timeout ?? this.timeout);
235
+ let res;
236
+ try {
237
+ res = await this.fetchFn(url, {
238
+ method,
239
+ headers,
240
+ body: body ? JSON.stringify(body) : void 0,
241
+ signal: controller.signal
242
+ });
243
+ } catch (e) {
244
+ clearTimeout(timer);
245
+ if (e instanceof Error && e.name === "AbortError") {
246
+ throw new TimeoutError();
247
+ }
248
+ throw e;
249
+ }
250
+ clearTimeout(timer);
251
+ if (!res.ok) {
252
+ const errBody = await res.json().catch(() => ({ error: res.statusText }));
253
+ const msg = errBody.error ?? res.statusText;
254
+ if (res.status === 401 || res.status === 403) throw new AuthError(msg, res.status);
255
+ if (res.status === 404) throw new NotFoundError(msg);
256
+ throw new LLMSafeSpacesError(msg, res.status);
257
+ }
258
+ let data;
259
+ if (res.status === 204) {
260
+ data = void 0;
261
+ } else {
262
+ const text = await res.text();
263
+ data = text === "" ? void 0 : JSON.parse(text);
264
+ }
265
+ return { data, headers: res.headers };
266
+ }
208
267
  async login() {
209
268
  if (!this.credentials) throw new AuthError("No credentials configured");
210
269
  this.loggingIn = true;
@@ -246,6 +305,18 @@ var WorkspacesAPI = class {
246
305
  getStatus(id) {
247
306
  return this.client.request("GET", `/workspaces/${id}/status`);
248
307
  }
308
+ /**
309
+ * Uploads a file into the workspace (Epic 67): multipart POST with a
310
+ * single part named `file`; the file lands on the workspace PVC under
311
+ * /workspace/uploads/. The returned path feeds the `files` parameter of
312
+ * sessions.sendPromptAsync / sessions.enqueue. The workspace must be
313
+ * Active; a 409 rejects with ConflictError carrying `phase`.
314
+ */
315
+ upload(id, filename, content) {
316
+ const form = new FormData();
317
+ form.append("file", typeof content === "string" ? new Blob([content]) : content, filename);
318
+ return this.client.request("POST", `/workspaces/${id}/uploads`, form);
319
+ }
249
320
  activate(id) {
250
321
  return this.client.request("POST", `/workspaces/${id}/activate`);
251
322
  }
@@ -327,20 +398,44 @@ var SessionsAPI = class {
327
398
  getHistory(workspaceId, sessionId) {
328
399
  return this.client.request("GET", `/workspaces/${workspaceId}/sessions/${sessionId}/message`);
329
400
  }
401
+ /**
402
+ * Returns one page of session history with cursor pagination.
403
+ * nextCursor is "" when the beginning of the session was reached
404
+ * (no X-Next-Cursor response header).
405
+ */
406
+ async getHistoryPage(workspaceId, sessionId, opts) {
407
+ const q = new URLSearchParams();
408
+ if (opts?.limit && opts.limit > 0) q.set("limit", String(opts.limit));
409
+ if (opts?.before) q.set("before", opts.before);
410
+ const qs = q.toString();
411
+ const path = `/workspaces/${workspaceId}/sessions/${sessionId}/message${qs ? `?${qs}` : ""}`;
412
+ const { data, headers } = await this.client.requestWithHeaders("GET", path);
413
+ return { messages: data ?? [], nextCursor: headers.get("X-Next-Cursor") ?? "" };
414
+ }
330
415
  abort(workspaceId, sessionId) {
331
416
  return this.client.request("POST", `/workspaces/${workspaceId}/sessions/${sessionId}/abort`);
332
417
  }
333
418
  get(workspaceId, sessionId) {
334
419
  return this.client.request("GET", `/workspaces/${workspaceId}/sessions/${sessionId}`);
335
420
  }
336
- sendPromptAsync(workspaceId, sessionId, message) {
337
- return this.client.request("POST", `/workspaces/${workspaceId}/sessions/${sessionId}/prompt`, { message });
421
+ /**
422
+ * Sends a prompt asynchronously (202; the reply arrives on the workspace
423
+ * SSE stream). Optional `files` (Epic 67) are upload-namespace paths —
424
+ * the API composes the v1 attachment manifest into the dispatched text.
425
+ */
426
+ sendPromptAsync(workspaceId, sessionId, message, files) {
427
+ const body = { parts: [{ type: "text", text: message }] };
428
+ if (files && files.length > 0) body.files = files;
429
+ return this.client.request("POST", `/workspaces/${workspaceId}/sessions/${sessionId}/prompt`, body);
338
430
  }
339
431
  delete(workspaceId, sessionId) {
340
432
  return this.client.request("DELETE", `/workspaces/${workspaceId}/sessions/${sessionId}`);
341
433
  }
342
- enqueue(workspaceId, sessionId, text) {
343
- return this.client.request("POST", `/workspaces/${workspaceId}/sessions/${sessionId}/queue`, { text });
434
+ /** Enqueues a message for a busy session; optional `files` as in sendPromptAsync. */
435
+ enqueue(workspaceId, sessionId, text, files) {
436
+ const body = { text };
437
+ if (files && files.length > 0) body.files = files;
438
+ return this.client.request("POST", `/workspaces/${workspaceId}/sessions/${sessionId}/queue`, body);
344
439
  }
345
440
  /**
346
441
  * @deprecated Under the V2 session-queue model (Epic 63), the queue is
@@ -675,6 +770,110 @@ var TriggersAPI = class {
675
770
  return this.client.request("DELETE", `/me/triggers/${id}`);
676
771
  }
677
772
  };
773
+ var McpServersAPI = class {
774
+ constructor(client) {
775
+ this.client = client;
776
+ }
777
+ client;
778
+ list() {
779
+ return this.client.request("GET", "/me/mcp-servers").then((r) => Array.isArray(r) ? r : r.servers ?? []);
780
+ }
781
+ get(id) {
782
+ return this.client.request("GET", `/me/mcp-servers/${id}`);
783
+ }
784
+ create(req) {
785
+ return this.client.request("POST", "/me/mcp-servers", req);
786
+ }
787
+ update(id, req) {
788
+ return this.client.request("PUT", `/me/mcp-servers/${id}`, req);
789
+ }
790
+ delete(id) {
791
+ return this.client.request("DELETE", `/me/mcp-servers/${id}`);
792
+ }
793
+ bind(id, workspaceId) {
794
+ return this.client.request("POST", `/me/mcp-servers/${id}/bindings`, { workspaceId });
795
+ }
796
+ unbind(id, workspaceId) {
797
+ return this.client.request("DELETE", `/me/mcp-servers/${id}/bindings/${workspaceId}`);
798
+ }
799
+ createAutoApply(id, targetType, targetId) {
800
+ return this.client.request("POST", `/me/mcp-servers/${id}/auto-apply`, { targetType, targetId });
801
+ }
802
+ listAutoApply(id) {
803
+ return this.client.request("GET", `/me/mcp-servers/${id}/auto-apply`).then((r) => Array.isArray(r) ? r : r.rules ?? []);
804
+ }
805
+ };
806
+ var AdminMcpServersAPI = class {
807
+ constructor(client) {
808
+ this.client = client;
809
+ }
810
+ client;
811
+ list() {
812
+ return this.client.request("GET", "/admin/mcp-servers").then((r) => Array.isArray(r) ? r : r.servers ?? []);
813
+ }
814
+ get(id) {
815
+ return this.client.request("GET", `/admin/mcp-servers/${id}`);
816
+ }
817
+ create(req) {
818
+ return this.client.request("POST", "/admin/mcp-servers", req);
819
+ }
820
+ update(id, req) {
821
+ return this.client.request("PUT", `/admin/mcp-servers/${id}`, req);
822
+ }
823
+ delete(id) {
824
+ return this.client.request("DELETE", `/admin/mcp-servers/${id}`);
825
+ }
826
+ bind(id, workspaceId) {
827
+ return this.client.request("POST", `/admin/mcp-servers/${id}/bindings`, { workspaceId });
828
+ }
829
+ unbind(id, workspaceId) {
830
+ return this.client.request("DELETE", `/admin/mcp-servers/${id}/bindings/${workspaceId}`);
831
+ }
832
+ createAutoApply(id, targetType, targetId) {
833
+ return this.client.request("POST", `/admin/mcp-servers/${id}/auto-apply`, { targetType, targetId });
834
+ }
835
+ listAutoApply(id) {
836
+ return this.client.request("GET", `/admin/mcp-servers/${id}/auto-apply`).then((r) => Array.isArray(r) ? r : r.rules ?? []);
837
+ }
838
+ /** targetId omitted → removes every rule of the targetType. */
839
+ deleteAutoApply(id, targetType, targetId) {
840
+ const suffix = targetId ? `/${targetId}` : "";
841
+ return this.client.request("DELETE", `/admin/mcp-servers/${id}/auto-apply/${targetType}${suffix}`);
842
+ }
843
+ };
844
+ var OrgMcpServersAPI = class {
845
+ constructor(client) {
846
+ this.client = client;
847
+ }
848
+ client;
849
+ list(orgId) {
850
+ return this.client.request("GET", `/orgs/${orgId}/mcp-servers`).then((r) => Array.isArray(r) ? r : r.servers ?? []);
851
+ }
852
+ get(orgId, id) {
853
+ return this.client.request("GET", `/orgs/${orgId}/mcp-servers/${id}`);
854
+ }
855
+ create(orgId, req) {
856
+ return this.client.request("POST", `/orgs/${orgId}/mcp-servers`, req);
857
+ }
858
+ update(orgId, id, req) {
859
+ return this.client.request("PUT", `/orgs/${orgId}/mcp-servers/${id}`, req);
860
+ }
861
+ delete(orgId, id) {
862
+ return this.client.request("DELETE", `/orgs/${orgId}/mcp-servers/${id}`);
863
+ }
864
+ bind(orgId, id, workspaceId) {
865
+ return this.client.request("POST", `/orgs/${orgId}/mcp-servers/${id}/bindings`, { workspaceId });
866
+ }
867
+ unbind(orgId, id, workspaceId) {
868
+ return this.client.request("DELETE", `/orgs/${orgId}/mcp-servers/${id}/bindings/${workspaceId}`);
869
+ }
870
+ createAutoApply(orgId, id, targetType, targetId) {
871
+ return this.client.request("POST", `/orgs/${orgId}/mcp-servers/${id}/auto-apply`, { targetType, targetId });
872
+ }
873
+ listAutoApply(orgId, id) {
874
+ return this.client.request("GET", `/orgs/${orgId}/mcp-servers/${id}/auto-apply`).then((r) => Array.isArray(r) ? r : r.rules ?? []);
875
+ }
876
+ };
678
877
 
679
878
  // src/types.ts
680
879
  var SECRET_NAME_PATTERN = /^[a-z0-9._-]+$/;
package/dist/index.d.cts CHANGED
@@ -294,6 +294,58 @@ interface QueuedMessage {
294
294
  enqueued_at: string;
295
295
  retry_count: number;
296
296
  }
297
+ /**
298
+ * Result of a workspace file upload (Epic 67): the absolute path of the
299
+ * stored file on the workspace PVC (/workspace/uploads/<uuid>-<name>),
300
+ * its sanitized name, and the stored byte count.
301
+ */
302
+ interface FileUpload {
303
+ path: string;
304
+ name: string;
305
+ size: number;
306
+ }
307
+ interface McpServer {
308
+ id: string;
309
+ name: string;
310
+ transport: "http" | "sse" | "stdio";
311
+ url?: string;
312
+ command?: string;
313
+ args?: string[];
314
+ timeoutMs?: number;
315
+ hasSecret: boolean;
316
+ enabled: boolean;
317
+ createdAt?: string;
318
+ updatedAt?: string;
319
+ }
320
+ interface CreateMcpServerRequest {
321
+ name: string;
322
+ transport: "http" | "sse" | "stdio";
323
+ url?: string;
324
+ command?: string;
325
+ args?: string[];
326
+ timeoutMs?: number;
327
+ enabled?: boolean;
328
+ env?: Record<string, string>;
329
+ headers?: Record<string, string>;
330
+ autoApply?: {
331
+ targetType: string;
332
+ targetId?: string;
333
+ };
334
+ }
335
+ interface UpdateMcpServerRequest {
336
+ name?: string;
337
+ url?: string;
338
+ command?: string;
339
+ args?: string[];
340
+ timeoutMs?: number;
341
+ enabled?: boolean;
342
+ env?: Record<string, string>;
343
+ headers?: Record<string, string>;
344
+ }
345
+ interface McpAutoApplyRule {
346
+ targetType: string;
347
+ targetId?: string;
348
+ }
297
349
 
298
350
  declare class LLMSafeSpaces {
299
351
  readonly baseUrl: string;
@@ -319,9 +371,20 @@ declare class LLMSafeSpaces {
319
371
  readonly agentRoles: AgentRolesAPI;
320
372
  readonly workflows: WorkflowsAPI;
321
373
  readonly triggers: TriggersAPI;
374
+ readonly mcpServers: McpServersAPI;
375
+ readonly adminMcpServers: AdminMcpServersAPI;
376
+ readonly orgMcpServers: OrgMcpServersAPI;
322
377
  constructor(options: ClientOptions);
323
378
  /** Internal: make an authenticated request. */
324
379
  request<T>(method: string, path: string, body?: unknown, timeout?: number): Promise<T>;
380
+ /**
381
+ * Internal: like {@link request}, but also returns the response headers
382
+ * (e.g. pagination cursors). Body decoding follows the same contract.
383
+ */
384
+ requestWithHeaders<T>(method: string, path: string, body?: unknown, timeout?: number): Promise<{
385
+ data: T;
386
+ headers: Headers;
387
+ }>;
325
388
  private login;
326
389
  }
327
390
  declare class WorkspacesAPI {
@@ -333,6 +396,14 @@ declare class WorkspacesAPI {
333
396
  rename(id: string, name: string): Promise<void>;
334
397
  delete(id: string): Promise<void>;
335
398
  getStatus(id: string): Promise<WorkspaceStatusResult>;
399
+ /**
400
+ * Uploads a file into the workspace (Epic 67): multipart POST with a
401
+ * single part named `file`; the file lands on the workspace PVC under
402
+ * /workspace/uploads/. The returned path feeds the `files` parameter of
403
+ * sessions.sendPromptAsync / sessions.enqueue. The workspace must be
404
+ * Active; a 409 rejects with ConflictError carrying `phase`.
405
+ */
406
+ upload(id: string, filename: string, content: Blob | string): Promise<FileUpload>;
336
407
  activate(id: string): Promise<ActivateWorkspaceResponse>;
337
408
  suspend(id: string): Promise<void>;
338
409
  restart(id: string): Promise<void>;
@@ -378,11 +449,29 @@ declare class SessionsAPI {
378
449
  sendMessage(workspaceId: string, sessionId: string, content: string): Promise<Message>;
379
450
  /** Returns the session transcript in contract shape. */
380
451
  getHistory(workspaceId: string, sessionId: string): Promise<Message[]>;
452
+ /**
453
+ * Returns one page of session history with cursor pagination.
454
+ * nextCursor is "" when the beginning of the session was reached
455
+ * (no X-Next-Cursor response header).
456
+ */
457
+ getHistoryPage(workspaceId: string, sessionId: string, opts?: {
458
+ limit?: number;
459
+ before?: string;
460
+ }): Promise<{
461
+ messages: Message[];
462
+ nextCursor: string;
463
+ }>;
381
464
  abort(workspaceId: string, sessionId: string): Promise<void>;
382
465
  get(workspaceId: string, sessionId: string): Promise<Record<string, unknown>>;
383
- sendPromptAsync(workspaceId: string, sessionId: string, message: string): Promise<void>;
466
+ /**
467
+ * Sends a prompt asynchronously (202; the reply arrives on the workspace
468
+ * SSE stream). Optional `files` (Epic 67) are upload-namespace paths —
469
+ * the API composes the v1 attachment manifest into the dispatched text.
470
+ */
471
+ sendPromptAsync(workspaceId: string, sessionId: string, message: string, files?: string[]): Promise<void>;
384
472
  delete(workspaceId: string, sessionId: string): Promise<void>;
385
- enqueue(workspaceId: string, sessionId: string, text: string): Promise<{
473
+ /** Enqueues a message for a busy session; optional `files` as in sendPromptAsync. */
474
+ enqueue(workspaceId: string, sessionId: string, text: string, files?: string[]): Promise<{
386
475
  messageID: string;
387
476
  }>;
388
477
  /**
@@ -640,6 +729,50 @@ declare class TriggersAPI {
640
729
  }): Promise<TriggerResponse>;
641
730
  delete(id: string): Promise<void>;
642
731
  }
732
+ /** MCP servers owned by the caller (/me/mcp-servers, Epic 53). */
733
+ declare class McpServersAPI {
734
+ private readonly client;
735
+ constructor(client: LLMSafeSpaces);
736
+ list(): Promise<McpServer[]>;
737
+ get(id: string): Promise<McpServer>;
738
+ create(req: CreateMcpServerRequest): Promise<McpServer>;
739
+ update(id: string, req: UpdateMcpServerRequest): Promise<McpServer>;
740
+ delete(id: string): Promise<void>;
741
+ bind(id: string, workspaceId: string): Promise<void>;
742
+ unbind(id: string, workspaceId: string): Promise<void>;
743
+ createAutoApply(id: string, targetType: string, targetId?: string): Promise<void>;
744
+ listAutoApply(id: string): Promise<McpAutoApplyRule[]>;
745
+ }
746
+ /** Platform MCP servers (/admin/mcp-servers; admin scope). */
747
+ declare class AdminMcpServersAPI {
748
+ private readonly client;
749
+ constructor(client: LLMSafeSpaces);
750
+ list(): Promise<McpServer[]>;
751
+ get(id: string): Promise<McpServer>;
752
+ create(req: CreateMcpServerRequest): Promise<McpServer>;
753
+ update(id: string, req: UpdateMcpServerRequest): Promise<McpServer>;
754
+ delete(id: string): Promise<void>;
755
+ bind(id: string, workspaceId: string): Promise<void>;
756
+ unbind(id: string, workspaceId: string): Promise<void>;
757
+ createAutoApply(id: string, targetType: string, targetId?: string): Promise<void>;
758
+ listAutoApply(id: string): Promise<McpAutoApplyRule[]>;
759
+ /** targetId omitted → removes every rule of the targetType. */
760
+ deleteAutoApply(id: string, targetType: string, targetId?: string): Promise<void>;
761
+ }
762
+ /** Organization MCP servers (/orgs/{orgId}/mcp-servers; org-admin scope). */
763
+ declare class OrgMcpServersAPI {
764
+ private readonly client;
765
+ constructor(client: LLMSafeSpaces);
766
+ list(orgId: string): Promise<McpServer[]>;
767
+ get(orgId: string, id: string): Promise<McpServer>;
768
+ create(orgId: string, req: CreateMcpServerRequest): Promise<McpServer>;
769
+ update(orgId: string, id: string, req: UpdateMcpServerRequest): Promise<McpServer>;
770
+ delete(orgId: string, id: string): Promise<void>;
771
+ bind(orgId: string, id: string, workspaceId: string): Promise<void>;
772
+ unbind(orgId: string, id: string, workspaceId: string): Promise<void>;
773
+ createAutoApply(orgId: string, id: string, targetType: string, targetId?: string): Promise<void>;
774
+ listAutoApply(orgId: string, id: string): Promise<McpAutoApplyRule[]>;
775
+ }
643
776
 
644
777
  /** Base error for all LLMSafeSpaces API errors. */
645
778
  declare class LLMSafeSpacesError extends Error {
@@ -654,6 +787,8 @@ declare class NotFoundError extends LLMSafeSpacesError {
654
787
  constructor(message: string);
655
788
  }
656
789
  declare class ConflictError extends LLMSafeSpacesError {
790
+ /** Current workspace phase, when the 409 body carries one (upload phase gate, Epic 67 D5). */
791
+ phase?: string;
657
792
  constructor(message: string);
658
793
  }
659
794
  declare class TimeoutError extends LLMSafeSpacesError {
@@ -678,4 +813,4 @@ declare class ServiceUnavailableError extends LLMSafeSpacesError {
678
813
  constructor(message?: string, reason?: string, retryAfter?: number);
679
814
  }
680
815
 
681
- export { type APIKey, type ActivateWorkspaceResponse, type ActiveSessionsResponse, AuthError, type AuthResponse, type ClientOptions, ConflictError, type Cost, type CreateProviderCredentialRequest, type CreateSecretRequest, type CreateWorkspaceRequest, type EnsureSessionResponse, type FetchFn, type FileDiff, type InputOption, type InputRequest, LLMSafeSpaces, LLMSafeSpacesError, type Message, type ModelRef, NotFoundError, type PaginationMetadata, type Part, type ProviderCredential, type QueuedMessage, RateLimitError, type RefreshWorkspaceResult, SECRET_NAME_PATTERN, type SecretResponse, ServiceUnavailableError, type SessionListItem, type TerminalTicket, TimeoutError, type ToolPart, type ToolRef, type UpdateProviderCredentialRequest, type User, type Workspace, type WorkspaceCondition, type WorkspaceListItem, type WorkspaceListResult, type WorkspaceStatusResult };
816
+ export { type APIKey, type ActivateWorkspaceResponse, type ActiveSessionsResponse, AuthError, type AuthResponse, type ClientOptions, ConflictError, type Cost, type CreateMcpServerRequest, type CreateProviderCredentialRequest, type CreateSecretRequest, type CreateWorkspaceRequest, type EnsureSessionResponse, type FetchFn, type FileDiff, type FileUpload, type InputOption, type InputRequest, LLMSafeSpaces, LLMSafeSpacesError, type McpAutoApplyRule, type McpServer, type Message, type ModelRef, NotFoundError, type PaginationMetadata, type Part, type ProviderCredential, type QueuedMessage, RateLimitError, type RefreshWorkspaceResult, SECRET_NAME_PATTERN, type SecretResponse, ServiceUnavailableError, type SessionListItem, type TerminalTicket, TimeoutError, type ToolPart, type ToolRef, type UpdateMcpServerRequest, type UpdateProviderCredentialRequest, type User, type Workspace, type WorkspaceCondition, type WorkspaceListItem, type WorkspaceListResult, type WorkspaceStatusResult };
package/dist/index.d.ts CHANGED
@@ -294,6 +294,58 @@ interface QueuedMessage {
294
294
  enqueued_at: string;
295
295
  retry_count: number;
296
296
  }
297
+ /**
298
+ * Result of a workspace file upload (Epic 67): the absolute path of the
299
+ * stored file on the workspace PVC (/workspace/uploads/&lt;uuid&gt;-&lt;name&gt;),
300
+ * its sanitized name, and the stored byte count.
301
+ */
302
+ interface FileUpload {
303
+ path: string;
304
+ name: string;
305
+ size: number;
306
+ }
307
+ interface McpServer {
308
+ id: string;
309
+ name: string;
310
+ transport: "http" | "sse" | "stdio";
311
+ url?: string;
312
+ command?: string;
313
+ args?: string[];
314
+ timeoutMs?: number;
315
+ hasSecret: boolean;
316
+ enabled: boolean;
317
+ createdAt?: string;
318
+ updatedAt?: string;
319
+ }
320
+ interface CreateMcpServerRequest {
321
+ name: string;
322
+ transport: "http" | "sse" | "stdio";
323
+ url?: string;
324
+ command?: string;
325
+ args?: string[];
326
+ timeoutMs?: number;
327
+ enabled?: boolean;
328
+ env?: Record<string, string>;
329
+ headers?: Record<string, string>;
330
+ autoApply?: {
331
+ targetType: string;
332
+ targetId?: string;
333
+ };
334
+ }
335
+ interface UpdateMcpServerRequest {
336
+ name?: string;
337
+ url?: string;
338
+ command?: string;
339
+ args?: string[];
340
+ timeoutMs?: number;
341
+ enabled?: boolean;
342
+ env?: Record<string, string>;
343
+ headers?: Record<string, string>;
344
+ }
345
+ interface McpAutoApplyRule {
346
+ targetType: string;
347
+ targetId?: string;
348
+ }
297
349
 
298
350
  declare class LLMSafeSpaces {
299
351
  readonly baseUrl: string;
@@ -319,9 +371,20 @@ declare class LLMSafeSpaces {
319
371
  readonly agentRoles: AgentRolesAPI;
320
372
  readonly workflows: WorkflowsAPI;
321
373
  readonly triggers: TriggersAPI;
374
+ readonly mcpServers: McpServersAPI;
375
+ readonly adminMcpServers: AdminMcpServersAPI;
376
+ readonly orgMcpServers: OrgMcpServersAPI;
322
377
  constructor(options: ClientOptions);
323
378
  /** Internal: make an authenticated request. */
324
379
  request<T>(method: string, path: string, body?: unknown, timeout?: number): Promise<T>;
380
+ /**
381
+ * Internal: like {@link request}, but also returns the response headers
382
+ * (e.g. pagination cursors). Body decoding follows the same contract.
383
+ */
384
+ requestWithHeaders<T>(method: string, path: string, body?: unknown, timeout?: number): Promise<{
385
+ data: T;
386
+ headers: Headers;
387
+ }>;
325
388
  private login;
326
389
  }
327
390
  declare class WorkspacesAPI {
@@ -333,6 +396,14 @@ declare class WorkspacesAPI {
333
396
  rename(id: string, name: string): Promise<void>;
334
397
  delete(id: string): Promise<void>;
335
398
  getStatus(id: string): Promise<WorkspaceStatusResult>;
399
+ /**
400
+ * Uploads a file into the workspace (Epic 67): multipart POST with a
401
+ * single part named `file`; the file lands on the workspace PVC under
402
+ * /workspace/uploads/. The returned path feeds the `files` parameter of
403
+ * sessions.sendPromptAsync / sessions.enqueue. The workspace must be
404
+ * Active; a 409 rejects with ConflictError carrying `phase`.
405
+ */
406
+ upload(id: string, filename: string, content: Blob | string): Promise<FileUpload>;
336
407
  activate(id: string): Promise<ActivateWorkspaceResponse>;
337
408
  suspend(id: string): Promise<void>;
338
409
  restart(id: string): Promise<void>;
@@ -378,11 +449,29 @@ declare class SessionsAPI {
378
449
  sendMessage(workspaceId: string, sessionId: string, content: string): Promise<Message>;
379
450
  /** Returns the session transcript in contract shape. */
380
451
  getHistory(workspaceId: string, sessionId: string): Promise<Message[]>;
452
+ /**
453
+ * Returns one page of session history with cursor pagination.
454
+ * nextCursor is "" when the beginning of the session was reached
455
+ * (no X-Next-Cursor response header).
456
+ */
457
+ getHistoryPage(workspaceId: string, sessionId: string, opts?: {
458
+ limit?: number;
459
+ before?: string;
460
+ }): Promise<{
461
+ messages: Message[];
462
+ nextCursor: string;
463
+ }>;
381
464
  abort(workspaceId: string, sessionId: string): Promise<void>;
382
465
  get(workspaceId: string, sessionId: string): Promise<Record<string, unknown>>;
383
- sendPromptAsync(workspaceId: string, sessionId: string, message: string): Promise<void>;
466
+ /**
467
+ * Sends a prompt asynchronously (202; the reply arrives on the workspace
468
+ * SSE stream). Optional `files` (Epic 67) are upload-namespace paths —
469
+ * the API composes the v1 attachment manifest into the dispatched text.
470
+ */
471
+ sendPromptAsync(workspaceId: string, sessionId: string, message: string, files?: string[]): Promise<void>;
384
472
  delete(workspaceId: string, sessionId: string): Promise<void>;
385
- enqueue(workspaceId: string, sessionId: string, text: string): Promise<{
473
+ /** Enqueues a message for a busy session; optional `files` as in sendPromptAsync. */
474
+ enqueue(workspaceId: string, sessionId: string, text: string, files?: string[]): Promise<{
386
475
  messageID: string;
387
476
  }>;
388
477
  /**
@@ -640,6 +729,50 @@ declare class TriggersAPI {
640
729
  }): Promise<TriggerResponse>;
641
730
  delete(id: string): Promise<void>;
642
731
  }
732
+ /** MCP servers owned by the caller (/me/mcp-servers, Epic 53). */
733
+ declare class McpServersAPI {
734
+ private readonly client;
735
+ constructor(client: LLMSafeSpaces);
736
+ list(): Promise<McpServer[]>;
737
+ get(id: string): Promise<McpServer>;
738
+ create(req: CreateMcpServerRequest): Promise<McpServer>;
739
+ update(id: string, req: UpdateMcpServerRequest): Promise<McpServer>;
740
+ delete(id: string): Promise<void>;
741
+ bind(id: string, workspaceId: string): Promise<void>;
742
+ unbind(id: string, workspaceId: string): Promise<void>;
743
+ createAutoApply(id: string, targetType: string, targetId?: string): Promise<void>;
744
+ listAutoApply(id: string): Promise<McpAutoApplyRule[]>;
745
+ }
746
+ /** Platform MCP servers (/admin/mcp-servers; admin scope). */
747
+ declare class AdminMcpServersAPI {
748
+ private readonly client;
749
+ constructor(client: LLMSafeSpaces);
750
+ list(): Promise<McpServer[]>;
751
+ get(id: string): Promise<McpServer>;
752
+ create(req: CreateMcpServerRequest): Promise<McpServer>;
753
+ update(id: string, req: UpdateMcpServerRequest): Promise<McpServer>;
754
+ delete(id: string): Promise<void>;
755
+ bind(id: string, workspaceId: string): Promise<void>;
756
+ unbind(id: string, workspaceId: string): Promise<void>;
757
+ createAutoApply(id: string, targetType: string, targetId?: string): Promise<void>;
758
+ listAutoApply(id: string): Promise<McpAutoApplyRule[]>;
759
+ /** targetId omitted → removes every rule of the targetType. */
760
+ deleteAutoApply(id: string, targetType: string, targetId?: string): Promise<void>;
761
+ }
762
+ /** Organization MCP servers (/orgs/{orgId}/mcp-servers; org-admin scope). */
763
+ declare class OrgMcpServersAPI {
764
+ private readonly client;
765
+ constructor(client: LLMSafeSpaces);
766
+ list(orgId: string): Promise<McpServer[]>;
767
+ get(orgId: string, id: string): Promise<McpServer>;
768
+ create(orgId: string, req: CreateMcpServerRequest): Promise<McpServer>;
769
+ update(orgId: string, id: string, req: UpdateMcpServerRequest): Promise<McpServer>;
770
+ delete(orgId: string, id: string): Promise<void>;
771
+ bind(orgId: string, id: string, workspaceId: string): Promise<void>;
772
+ unbind(orgId: string, id: string, workspaceId: string): Promise<void>;
773
+ createAutoApply(orgId: string, id: string, targetType: string, targetId?: string): Promise<void>;
774
+ listAutoApply(orgId: string, id: string): Promise<McpAutoApplyRule[]>;
775
+ }
643
776
 
644
777
  /** Base error for all LLMSafeSpaces API errors. */
645
778
  declare class LLMSafeSpacesError extends Error {
@@ -654,6 +787,8 @@ declare class NotFoundError extends LLMSafeSpacesError {
654
787
  constructor(message: string);
655
788
  }
656
789
  declare class ConflictError extends LLMSafeSpacesError {
790
+ /** Current workspace phase, when the 409 body carries one (upload phase gate, Epic 67 D5). */
791
+ phase?: string;
657
792
  constructor(message: string);
658
793
  }
659
794
  declare class TimeoutError extends LLMSafeSpacesError {
@@ -678,4 +813,4 @@ declare class ServiceUnavailableError extends LLMSafeSpacesError {
678
813
  constructor(message?: string, reason?: string, retryAfter?: number);
679
814
  }
680
815
 
681
- export { type APIKey, type ActivateWorkspaceResponse, type ActiveSessionsResponse, AuthError, type AuthResponse, type ClientOptions, ConflictError, type Cost, type CreateProviderCredentialRequest, type CreateSecretRequest, type CreateWorkspaceRequest, type EnsureSessionResponse, type FetchFn, type FileDiff, type InputOption, type InputRequest, LLMSafeSpaces, LLMSafeSpacesError, type Message, type ModelRef, NotFoundError, type PaginationMetadata, type Part, type ProviderCredential, type QueuedMessage, RateLimitError, type RefreshWorkspaceResult, SECRET_NAME_PATTERN, type SecretResponse, ServiceUnavailableError, type SessionListItem, type TerminalTicket, TimeoutError, type ToolPart, type ToolRef, type UpdateProviderCredentialRequest, type User, type Workspace, type WorkspaceCondition, type WorkspaceListItem, type WorkspaceListResult, type WorkspaceStatusResult };
816
+ export { type APIKey, type ActivateWorkspaceResponse, type ActiveSessionsResponse, AuthError, type AuthResponse, type ClientOptions, ConflictError, type Cost, type CreateMcpServerRequest, type CreateProviderCredentialRequest, type CreateSecretRequest, type CreateWorkspaceRequest, type EnsureSessionResponse, type FetchFn, type FileDiff, type FileUpload, type InputOption, type InputRequest, LLMSafeSpaces, LLMSafeSpacesError, type McpAutoApplyRule, type McpServer, type Message, type ModelRef, NotFoundError, type PaginationMetadata, type Part, type ProviderCredential, type QueuedMessage, RateLimitError, type RefreshWorkspaceResult, SECRET_NAME_PATTERN, type SecretResponse, ServiceUnavailableError, type SessionListItem, type TerminalTicket, TimeoutError, type ToolPart, type ToolRef, type UpdateMcpServerRequest, type UpdateProviderCredentialRequest, type User, type Workspace, type WorkspaceCondition, type WorkspaceListItem, type WorkspaceListResult, type WorkspaceStatusResult };
package/dist/index.js CHANGED
@@ -22,6 +22,8 @@ var NotFoundError = class extends LLMSafeSpacesError {
22
22
  }
23
23
  };
24
24
  var ConflictError = class extends LLMSafeSpacesError {
25
+ /** Current workspace phase, when the 409 body carries one (upload phase gate, Epic 67 D5). */
26
+ phase;
25
27
  constructor(message) {
26
28
  super(message, 409, "CONFLICT");
27
29
  this.name = "ConflictError";
@@ -86,6 +88,9 @@ var LLMSafeSpaces = class {
86
88
  agentRoles;
87
89
  workflows;
88
90
  triggers;
91
+ mcpServers;
92
+ adminMcpServers;
93
+ orgMcpServers;
89
94
  constructor(options) {
90
95
  this.baseUrl = options.baseUrl.replace(/\/$/, "");
91
96
  this.timeout = options.timeout ?? DEFAULT_TIMEOUT;
@@ -108,11 +113,15 @@ var LLMSafeSpaces = class {
108
113
  this.agentRoles = new AgentRolesAPI(this);
109
114
  this.workflows = new WorkflowsAPI(this);
110
115
  this.triggers = new TriggersAPI(this);
116
+ this.mcpServers = new McpServersAPI(this);
117
+ this.adminMcpServers = new AdminMcpServersAPI(this);
118
+ this.orgMcpServers = new OrgMcpServersAPI(this);
111
119
  }
112
120
  /** Internal: make an authenticated request. */
113
121
  async request(method, path, body, timeout) {
114
122
  const url = `${this.baseUrl}/api/v1${path}`;
115
- const headers = { "Content-Type": "application/json" };
123
+ const isForm = typeof FormData !== "undefined" && body instanceof FormData;
124
+ const headers = isForm ? {} : { "Content-Type": "application/json" };
116
125
  if (this.apiKey) {
117
126
  headers["Authorization"] = `Bearer ${this.apiKey}`;
118
127
  } else if (this.token) {
@@ -128,7 +137,7 @@ var LLMSafeSpaces = class {
128
137
  res = await this.fetchFn(url, {
129
138
  method,
130
139
  headers,
131
- body: body ? JSON.stringify(body) : void 0,
140
+ body: body ? isForm ? body : JSON.stringify(body) : void 0,
132
141
  signal: controller.signal
133
142
  });
134
143
  } catch (e) {
@@ -152,8 +161,12 @@ var LLMSafeSpaces = class {
152
161
  throw new AuthError(msg, res.status);
153
162
  case 404:
154
163
  throw new NotFoundError(msg);
155
- case 409:
156
- throw new ConflictError(msg);
164
+ case 409: {
165
+ const phase = errBody.phase;
166
+ const conflict = new ConflictError(msg);
167
+ if (phase) conflict.phase = phase;
168
+ throw conflict;
169
+ }
157
170
  case 429:
158
171
  throw new RateLimitError(msg);
159
172
  case 503: {
@@ -171,6 +184,52 @@ var LLMSafeSpaces = class {
171
184
  if (text === "") return void 0;
172
185
  return JSON.parse(text);
173
186
  }
187
+ /**
188
+ * Internal: like {@link request}, but also returns the response headers
189
+ * (e.g. pagination cursors). Body decoding follows the same contract.
190
+ */
191
+ async requestWithHeaders(method, path, body, timeout) {
192
+ const url = `${this.baseUrl}/api/v1${path}`;
193
+ const headers = { "Content-Type": "application/json" };
194
+ if (this.apiKey) {
195
+ headers["Authorization"] = `Bearer ${this.apiKey}`;
196
+ } else if (this.token) {
197
+ headers["Authorization"] = `Bearer ${this.token}`;
198
+ }
199
+ const controller = new AbortController();
200
+ const timer = setTimeout(() => controller.abort(), timeout ?? this.timeout);
201
+ let res;
202
+ try {
203
+ res = await this.fetchFn(url, {
204
+ method,
205
+ headers,
206
+ body: body ? JSON.stringify(body) : void 0,
207
+ signal: controller.signal
208
+ });
209
+ } catch (e) {
210
+ clearTimeout(timer);
211
+ if (e instanceof Error && e.name === "AbortError") {
212
+ throw new TimeoutError();
213
+ }
214
+ throw e;
215
+ }
216
+ clearTimeout(timer);
217
+ if (!res.ok) {
218
+ const errBody = await res.json().catch(() => ({ error: res.statusText }));
219
+ const msg = errBody.error ?? res.statusText;
220
+ if (res.status === 401 || res.status === 403) throw new AuthError(msg, res.status);
221
+ if (res.status === 404) throw new NotFoundError(msg);
222
+ throw new LLMSafeSpacesError(msg, res.status);
223
+ }
224
+ let data;
225
+ if (res.status === 204) {
226
+ data = void 0;
227
+ } else {
228
+ const text = await res.text();
229
+ data = text === "" ? void 0 : JSON.parse(text);
230
+ }
231
+ return { data, headers: res.headers };
232
+ }
174
233
  async login() {
175
234
  if (!this.credentials) throw new AuthError("No credentials configured");
176
235
  this.loggingIn = true;
@@ -212,6 +271,18 @@ var WorkspacesAPI = class {
212
271
  getStatus(id) {
213
272
  return this.client.request("GET", `/workspaces/${id}/status`);
214
273
  }
274
+ /**
275
+ * Uploads a file into the workspace (Epic 67): multipart POST with a
276
+ * single part named `file`; the file lands on the workspace PVC under
277
+ * /workspace/uploads/. The returned path feeds the `files` parameter of
278
+ * sessions.sendPromptAsync / sessions.enqueue. The workspace must be
279
+ * Active; a 409 rejects with ConflictError carrying `phase`.
280
+ */
281
+ upload(id, filename, content) {
282
+ const form = new FormData();
283
+ form.append("file", typeof content === "string" ? new Blob([content]) : content, filename);
284
+ return this.client.request("POST", `/workspaces/${id}/uploads`, form);
285
+ }
215
286
  activate(id) {
216
287
  return this.client.request("POST", `/workspaces/${id}/activate`);
217
288
  }
@@ -293,20 +364,44 @@ var SessionsAPI = class {
293
364
  getHistory(workspaceId, sessionId) {
294
365
  return this.client.request("GET", `/workspaces/${workspaceId}/sessions/${sessionId}/message`);
295
366
  }
367
+ /**
368
+ * Returns one page of session history with cursor pagination.
369
+ * nextCursor is "" when the beginning of the session was reached
370
+ * (no X-Next-Cursor response header).
371
+ */
372
+ async getHistoryPage(workspaceId, sessionId, opts) {
373
+ const q = new URLSearchParams();
374
+ if (opts?.limit && opts.limit > 0) q.set("limit", String(opts.limit));
375
+ if (opts?.before) q.set("before", opts.before);
376
+ const qs = q.toString();
377
+ const path = `/workspaces/${workspaceId}/sessions/${sessionId}/message${qs ? `?${qs}` : ""}`;
378
+ const { data, headers } = await this.client.requestWithHeaders("GET", path);
379
+ return { messages: data ?? [], nextCursor: headers.get("X-Next-Cursor") ?? "" };
380
+ }
296
381
  abort(workspaceId, sessionId) {
297
382
  return this.client.request("POST", `/workspaces/${workspaceId}/sessions/${sessionId}/abort`);
298
383
  }
299
384
  get(workspaceId, sessionId) {
300
385
  return this.client.request("GET", `/workspaces/${workspaceId}/sessions/${sessionId}`);
301
386
  }
302
- sendPromptAsync(workspaceId, sessionId, message) {
303
- return this.client.request("POST", `/workspaces/${workspaceId}/sessions/${sessionId}/prompt`, { message });
387
+ /**
388
+ * Sends a prompt asynchronously (202; the reply arrives on the workspace
389
+ * SSE stream). Optional `files` (Epic 67) are upload-namespace paths —
390
+ * the API composes the v1 attachment manifest into the dispatched text.
391
+ */
392
+ sendPromptAsync(workspaceId, sessionId, message, files) {
393
+ const body = { parts: [{ type: "text", text: message }] };
394
+ if (files && files.length > 0) body.files = files;
395
+ return this.client.request("POST", `/workspaces/${workspaceId}/sessions/${sessionId}/prompt`, body);
304
396
  }
305
397
  delete(workspaceId, sessionId) {
306
398
  return this.client.request("DELETE", `/workspaces/${workspaceId}/sessions/${sessionId}`);
307
399
  }
308
- enqueue(workspaceId, sessionId, text) {
309
- return this.client.request("POST", `/workspaces/${workspaceId}/sessions/${sessionId}/queue`, { text });
400
+ /** Enqueues a message for a busy session; optional `files` as in sendPromptAsync. */
401
+ enqueue(workspaceId, sessionId, text, files) {
402
+ const body = { text };
403
+ if (files && files.length > 0) body.files = files;
404
+ return this.client.request("POST", `/workspaces/${workspaceId}/sessions/${sessionId}/queue`, body);
310
405
  }
311
406
  /**
312
407
  * @deprecated Under the V2 session-queue model (Epic 63), the queue is
@@ -641,6 +736,110 @@ var TriggersAPI = class {
641
736
  return this.client.request("DELETE", `/me/triggers/${id}`);
642
737
  }
643
738
  };
739
+ var McpServersAPI = class {
740
+ constructor(client) {
741
+ this.client = client;
742
+ }
743
+ client;
744
+ list() {
745
+ return this.client.request("GET", "/me/mcp-servers").then((r) => Array.isArray(r) ? r : r.servers ?? []);
746
+ }
747
+ get(id) {
748
+ return this.client.request("GET", `/me/mcp-servers/${id}`);
749
+ }
750
+ create(req) {
751
+ return this.client.request("POST", "/me/mcp-servers", req);
752
+ }
753
+ update(id, req) {
754
+ return this.client.request("PUT", `/me/mcp-servers/${id}`, req);
755
+ }
756
+ delete(id) {
757
+ return this.client.request("DELETE", `/me/mcp-servers/${id}`);
758
+ }
759
+ bind(id, workspaceId) {
760
+ return this.client.request("POST", `/me/mcp-servers/${id}/bindings`, { workspaceId });
761
+ }
762
+ unbind(id, workspaceId) {
763
+ return this.client.request("DELETE", `/me/mcp-servers/${id}/bindings/${workspaceId}`);
764
+ }
765
+ createAutoApply(id, targetType, targetId) {
766
+ return this.client.request("POST", `/me/mcp-servers/${id}/auto-apply`, { targetType, targetId });
767
+ }
768
+ listAutoApply(id) {
769
+ return this.client.request("GET", `/me/mcp-servers/${id}/auto-apply`).then((r) => Array.isArray(r) ? r : r.rules ?? []);
770
+ }
771
+ };
772
+ var AdminMcpServersAPI = class {
773
+ constructor(client) {
774
+ this.client = client;
775
+ }
776
+ client;
777
+ list() {
778
+ return this.client.request("GET", "/admin/mcp-servers").then((r) => Array.isArray(r) ? r : r.servers ?? []);
779
+ }
780
+ get(id) {
781
+ return this.client.request("GET", `/admin/mcp-servers/${id}`);
782
+ }
783
+ create(req) {
784
+ return this.client.request("POST", "/admin/mcp-servers", req);
785
+ }
786
+ update(id, req) {
787
+ return this.client.request("PUT", `/admin/mcp-servers/${id}`, req);
788
+ }
789
+ delete(id) {
790
+ return this.client.request("DELETE", `/admin/mcp-servers/${id}`);
791
+ }
792
+ bind(id, workspaceId) {
793
+ return this.client.request("POST", `/admin/mcp-servers/${id}/bindings`, { workspaceId });
794
+ }
795
+ unbind(id, workspaceId) {
796
+ return this.client.request("DELETE", `/admin/mcp-servers/${id}/bindings/${workspaceId}`);
797
+ }
798
+ createAutoApply(id, targetType, targetId) {
799
+ return this.client.request("POST", `/admin/mcp-servers/${id}/auto-apply`, { targetType, targetId });
800
+ }
801
+ listAutoApply(id) {
802
+ return this.client.request("GET", `/admin/mcp-servers/${id}/auto-apply`).then((r) => Array.isArray(r) ? r : r.rules ?? []);
803
+ }
804
+ /** targetId omitted → removes every rule of the targetType. */
805
+ deleteAutoApply(id, targetType, targetId) {
806
+ const suffix = targetId ? `/${targetId}` : "";
807
+ return this.client.request("DELETE", `/admin/mcp-servers/${id}/auto-apply/${targetType}${suffix}`);
808
+ }
809
+ };
810
+ var OrgMcpServersAPI = class {
811
+ constructor(client) {
812
+ this.client = client;
813
+ }
814
+ client;
815
+ list(orgId) {
816
+ return this.client.request("GET", `/orgs/${orgId}/mcp-servers`).then((r) => Array.isArray(r) ? r : r.servers ?? []);
817
+ }
818
+ get(orgId, id) {
819
+ return this.client.request("GET", `/orgs/${orgId}/mcp-servers/${id}`);
820
+ }
821
+ create(orgId, req) {
822
+ return this.client.request("POST", `/orgs/${orgId}/mcp-servers`, req);
823
+ }
824
+ update(orgId, id, req) {
825
+ return this.client.request("PUT", `/orgs/${orgId}/mcp-servers/${id}`, req);
826
+ }
827
+ delete(orgId, id) {
828
+ return this.client.request("DELETE", `/orgs/${orgId}/mcp-servers/${id}`);
829
+ }
830
+ bind(orgId, id, workspaceId) {
831
+ return this.client.request("POST", `/orgs/${orgId}/mcp-servers/${id}/bindings`, { workspaceId });
832
+ }
833
+ unbind(orgId, id, workspaceId) {
834
+ return this.client.request("DELETE", `/orgs/${orgId}/mcp-servers/${id}/bindings/${workspaceId}`);
835
+ }
836
+ createAutoApply(orgId, id, targetType, targetId) {
837
+ return this.client.request("POST", `/orgs/${orgId}/mcp-servers/${id}/auto-apply`, { targetType, targetId });
838
+ }
839
+ listAutoApply(orgId, id) {
840
+ return this.client.request("GET", `/orgs/${orgId}/mcp-servers/${id}/auto-apply`).then((r) => Array.isArray(r) ? r : r.rules ?? []);
841
+ }
842
+ };
644
843
 
645
844
  // src/types.ts
646
845
  var SECRET_NAME_PATTERN = /^[a-z0-9._-]+$/;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@llmsafespaces/sdk",
3
- "version": "0.22.0",
3
+ "version": "0.24.0",
4
4
  "description": "TypeScript SDK for LLMSafeSpaces API",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",