@mutagent/cli 0.1.379 → 0.1.380

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.
Files changed (3) hide show
  1. package/dist/bin/cli.js +908 -750
  2. package/dist/index.js +479 -402
  3. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -255,6 +255,164 @@ var init_known_secrets = __esm(() => {
255
255
  known = new Set;
256
256
  });
257
257
 
258
+ // src/lib/config.ts
259
+ import { cosmiconfigSync } from "cosmiconfig";
260
+ import { z } from "zod";
261
+ import { homedir } from "os";
262
+ import { join } from "path";
263
+ import { existsSync, readFileSync, mkdirSync } from "fs";
264
+ function parseJsonSafe(content, schema) {
265
+ try {
266
+ const parsed = JSON.parse(content);
267
+ return schema.parse(parsed);
268
+ } catch {
269
+ return null;
270
+ }
271
+ }
272
+ function loadConfig() {
273
+ const explorer = cosmiconfigSync("mutagent");
274
+ const result = explorer.search();
275
+ const rcConfig = result?.config;
276
+ let credentials = null;
277
+ if (existsSync(CREDENTIALS_FILE)) {
278
+ credentials = parseJsonSafe(readFileSync(CREDENTIALS_FILE, "utf-8"), credentialsSchema);
279
+ }
280
+ const envKey = process.env.MUTAGENT_API_KEY;
281
+ const fileDescribesKey = envKey === undefined || envKey === credentials?.apiKey;
282
+ const envWorkspace = process.env.MUTAGENT_WORKSPACE_ID?.trim() ?? "";
283
+ const fileSelection = fileDescribesKey ? credentials?.defaultWorkspace : undefined;
284
+ const selected = fileSelection ?? rcConfig?.defaultWorkspace;
285
+ const defaultWorkspace = envWorkspace !== "" ? envWorkspace : selected;
286
+ const merged = {
287
+ providers: {},
288
+ environment: "production",
289
+ ...rcConfig,
290
+ ...credentials,
291
+ apiKey: envKey ?? credentials?.apiKey ?? rcConfig?.apiKey,
292
+ endpoint: process.env.MUTAGENT_ENDPOINT ?? credentials?.endpoint ?? rcConfig?.endpoint,
293
+ defaultWorkspace,
294
+ workspaceOrigin: envWorkspace !== "" ? "override" : selected ? "selection" : undefined,
295
+ workspaceName: envWorkspace === "" && fileSelection !== undefined && fileSelection === selected ? credentials?.workspaceName : undefined,
296
+ defaultOrganization: fileDescribesKey ? credentials?.defaultOrganization ?? rcConfig?.defaultOrganization : undefined,
297
+ organizationName: fileDescribesKey ? credentials?.organizationName : undefined,
298
+ organizationSlug: fileDescribesKey ? credentials?.organizationSlug : undefined,
299
+ keyScope: fileDescribesKey ? credentials?.keyScope : undefined
300
+ };
301
+ return configSchema.parse(merged);
302
+ }
303
+ function readCredentialsFile() {
304
+ if (!existsSync(CREDENTIALS_FILE))
305
+ return {};
306
+ try {
307
+ return JSON.parse(readFileSync(CREDENTIALS_FILE, "utf-8"));
308
+ } catch {
309
+ return {};
310
+ }
311
+ }
312
+ function updateCredentials(patch) {
313
+ if (!existsSync(CREDENTIALS_DIR)) {
314
+ mkdirSync(CREDENTIALS_DIR, { recursive: true });
315
+ }
316
+ const updated = Object.fromEntries(Object.entries({ ...readCredentialsFile(), ...patch }).filter(([key, value]) => value !== undefined && key !== "defaultWorkspaceSource"));
317
+ writeSecureFile(CREDENTIALS_FILE, JSON.stringify(updated, null, 2));
318
+ }
319
+ function saveCredentials(apiKey, endpoint) {
320
+ if (!existsSync(CREDENTIALS_DIR)) {
321
+ mkdirSync(CREDENTIALS_DIR, { recursive: true });
322
+ }
323
+ const config = loadConfig();
324
+ const credentials = {
325
+ apiKey,
326
+ endpoint: endpoint ?? config.endpoint
327
+ };
328
+ writeSecureFile(CREDENTIALS_FILE, JSON.stringify(credentials, null, 2));
329
+ }
330
+ function saveFullCredentials(creds) {
331
+ const existing = readCredentialsFile();
332
+ updateCredentials({
333
+ apiKey: creds.apiKey,
334
+ endpoint: creds.endpoint ?? existing.endpoint ?? "https://api.mutagent.io",
335
+ defaultWorkspace: creds.workspaceId,
336
+ workspaceName: creds.workspaceName,
337
+ defaultOrganization: creds.organizationId,
338
+ organizationName: creds.organizationName,
339
+ organizationSlug: creds.organizationSlug,
340
+ keyScope: creds.keyScope,
341
+ expiresAt: creds.expiresAt
342
+ });
343
+ }
344
+ function clearCredentials() {
345
+ if (existsSync(CREDENTIALS_FILE)) {
346
+ writeSecureFile(CREDENTIALS_FILE, JSON.stringify({}));
347
+ }
348
+ }
349
+ function hasCredentials() {
350
+ if (process.env.MUTAGENT_API_KEY)
351
+ return true;
352
+ if (!existsSync(CREDENTIALS_FILE))
353
+ return false;
354
+ const credentials = parseJsonSafe(readFileSync(CREDENTIALS_FILE, "utf-8"), credentialsSchema);
355
+ return credentials?.apiKey !== undefined && credentials.apiKey !== "";
356
+ }
357
+ function envKeyReplacesFile() {
358
+ const envKey = process.env.MUTAGENT_API_KEY;
359
+ if (envKey === undefined)
360
+ return false;
361
+ if (!existsSync(CREDENTIALS_FILE))
362
+ return true;
363
+ const credentials = parseJsonSafe(readFileSync(CREDENTIALS_FILE, "utf-8"), credentialsSchema);
364
+ return envKey !== credentials?.apiKey;
365
+ }
366
+ function getApiKey() {
367
+ const apiKey = process.env.MUTAGENT_API_KEY ?? loadConfig().apiKey;
368
+ registerSecret(apiKey);
369
+ return apiKey;
370
+ }
371
+ function setWorkspaceSelection(workspaceId, workspaceName) {
372
+ updateCredentials({ defaultWorkspace: workspaceId, workspaceName });
373
+ }
374
+ function recordKeyScope(self) {
375
+ updateCredentials({
376
+ keyScope: self.scope,
377
+ defaultOrganization: self.organization?.id,
378
+ organizationName: self.organization?.name,
379
+ organizationSlug: self.organization?.slug,
380
+ ...self.scope === "workspace" && self.workspace ? { defaultWorkspace: self.workspace.id, workspaceName: self.workspace.name } : {}
381
+ });
382
+ }
383
+ var keyScopeSchema, configSchema, credentialsSchema, CREDENTIALS_DIR, CREDENTIALS_FILE;
384
+ var init_config = __esm(() => {
385
+ init_secure_file();
386
+ init_known_secrets();
387
+ keyScopeSchema = z.enum(["organization", "workspace", "user"]);
388
+ configSchema = z.object({
389
+ apiKey: z.string().optional(),
390
+ endpoint: z.string().default("https://api.mutagent.io"),
391
+ format: z.enum(["table", "json"]).default("table"),
392
+ timeout: z.number().default(30000),
393
+ defaultWorkspace: z.string().optional(),
394
+ workspaceOrigin: z.enum(["override", "selection"]).optional(),
395
+ workspaceName: z.string().optional(),
396
+ defaultOrganization: z.string().optional(),
397
+ organizationName: z.string().optional(),
398
+ organizationSlug: z.string().optional(),
399
+ keyScope: keyScopeSchema.optional()
400
+ });
401
+ credentialsSchema = z.object({
402
+ apiKey: z.string().optional(),
403
+ endpoint: z.string().optional(),
404
+ defaultWorkspace: z.string().optional(),
405
+ workspaceName: z.string().optional(),
406
+ defaultOrganization: z.string().optional(),
407
+ organizationName: z.string().optional(),
408
+ organizationSlug: z.string().optional(),
409
+ keyScope: keyScopeSchema.optional(),
410
+ expiresAt: z.string().optional()
411
+ }).loose();
412
+ CREDENTIALS_DIR = join(homedir(), ".config", "mutagent");
413
+ CREDENTIALS_FILE = join(CREDENTIALS_DIR, "credentials.json");
414
+ });
415
+
258
416
  // src/lib/errors.ts
259
417
  function createErrorFromUnknown(error) {
260
418
  if (error instanceof MutagentError) {
@@ -278,7 +436,7 @@ function handleError(error, isJson) {
278
436
  }
279
437
  if (error instanceof WorkspaceContextError) {
280
438
  jsonOutput.remediation = {
281
- setWorkspace: "mutagent config set workspace <workspace-id>",
439
+ setWorkspace: "mutagent workspaces use <name>",
282
440
  listWorkspaces: "mutagent workspaces list"
283
441
  };
284
442
  }
@@ -300,7 +458,7 @@ function handleError(error, isJson) {
300
458
  console.log(JSON.stringify(scrubKnownDeep(jsonOutput), null, 2));
301
459
  } else {
302
460
  console.error(`Error: ${scrubKnown(error.message)}`);
303
- if (error instanceof AuthenticationError) {
461
+ if (error.name === "TenancyError") {} else if (error instanceof AuthenticationError) {
304
462
  console.error("");
305
463
  console.error(AUTH_REMEDIATION_MESSAGE);
306
464
  } else if (error instanceof WorkspaceContextError) {
@@ -403,14 +561,14 @@ var init_errors = __esm(() => {
403
561
  }
404
562
  };
405
563
  WORKSPACE_REMEDIATION_MESSAGE = [
406
- "Workspace context missing. To fix:",
407
- " mutagent config set workspace <workspace-id> # Set default workspace",
408
- " mutagent workspaces list # List available workspaces"
564
+ "No workspace selected. To fix:",
565
+ " mutagent workspaces use <name> # Select the workspace every later command works in",
566
+ " mutagent workspaces list # Your workspaces"
409
567
  ].join(`
410
568
  `);
411
569
  WorkspaceContextError = class WorkspaceContextError extends MutagentError {
412
570
  constructor(message) {
413
- super("WORKSPACE_REQUIRED", message ?? "Workspace context is required but not configured", "Run: mutagent config set workspace <workspace-id>", 3);
571
+ super("WORKSPACE_REQUIRED", message ?? "Workspace context is required but not configured", "Run: mutagent workspaces use <name>", 3);
414
572
  }
415
573
  };
416
574
  ValidationError = class ValidationError extends MutagentError {
@@ -444,6 +602,272 @@ var init_errors = __esm(() => {
444
602
  };
445
603
  });
446
604
 
605
+ // src/lib/organization-scope.ts
606
+ var exports_organization_scope = {};
607
+ __export(exports_organization_scope, {
608
+ tenancyHeaders: () => tenancyHeaders,
609
+ tenancyFailure: () => tenancyFailure,
610
+ setWorkspaceFlag: () => setWorkspaceFlag,
611
+ setOrgAssertion: () => setOrgAssertion,
612
+ resolveKeyTenancy: () => resolveKeyTenancy,
613
+ otherOrganizationHint: () => otherOrganizationHint,
614
+ noWorkspaceSelected: () => noWorkspaceSelected,
615
+ matchesOrganization: () => matchesOrganization,
616
+ looksLikeWorkspaceId: () => looksLikeWorkspaceId,
617
+ listKeyWorkspaces: () => listKeyWorkspaces,
618
+ findWorkspace: () => findWorkspace,
619
+ fetchOrganizations: () => fetchOrganizations,
620
+ fetchKeySelf: () => fetchKeySelf,
621
+ envKeyHasNoSelection: () => envKeyHasNoSelection,
622
+ configTenancyHeaders: () => configTenancyHeaders,
623
+ TenancyError: () => TenancyError,
624
+ TENANCY_DENIED: () => TENANCY_DENIED
625
+ });
626
+ function looksLikeWorkspaceId(value) {
627
+ return UUID_PATTERN.test(value);
628
+ }
629
+ function tenancyHeaders(t) {
630
+ const headers = {};
631
+ if (!t.workspaceId)
632
+ return headers;
633
+ if (t.keyScope === "workspace") {
634
+ if (t.workspaceOrigin === "override")
635
+ headers["x-workspace-id"] = t.workspaceId;
636
+ return headers;
637
+ }
638
+ headers["x-workspace-id"] = t.workspaceId;
639
+ if (t.keyScope === "user" && t.organizationId)
640
+ headers["x-organization-id"] = t.organizationId;
641
+ return headers;
642
+ }
643
+ function configTenancyHeaders(config) {
644
+ return tenancyHeaders({
645
+ keyScope: config.keyScope,
646
+ workspaceId: config.defaultWorkspace,
647
+ workspaceOrigin: config.workspaceOrigin,
648
+ organizationId: config.defaultOrganization
649
+ });
650
+ }
651
+ function parseBody(body) {
652
+ try {
653
+ const parsed = JSON.parse(body);
654
+ if (parsed && typeof parsed === "object")
655
+ return parsed;
656
+ } catch {}
657
+ return {};
658
+ }
659
+ function tenancyFailure(status, body, ctx) {
660
+ const parsed = parseBody(body);
661
+ if (status === 401) {
662
+ return new TenancyError("AUTH_EXPIRED", "Your CLI key expired or was revoked.", "Run: mutagent login", 2);
663
+ }
664
+ if (status === 403 && parsed.code === TENANCY_DENIED) {
665
+ const shown = ctx.workspaceName ?? ctx.workspaceId ?? "workspace";
666
+ if (ctx.workspaceOrigin === "selection") {
667
+ return new TenancyError(TENANCY_DENIED, `Your saved workspace ${shown} is no longer available to you.`, "Run: mutagent workspaces use <name>");
668
+ }
669
+ const where = ctx.organizationName ? ` in ${ctx.organizationName}` : "";
670
+ return new TenancyError(TENANCY_DENIED, `No workspace "${shown}" among your workspaces${where}.`, "See: mutagent workspaces list");
671
+ }
672
+ if (status === 403 && ctx.keyScope === "workspace" && ctx.workspaceOrigin === "override") {
673
+ return new TenancyError("WORKSPACE_KEY_MISMATCH", "This API key only works in its own workspace.", "Run: mutagent login (for a key that can switch workspaces)");
674
+ }
675
+ if (status === 400 && (parsed.message ?? body).toLowerCase().includes("x-workspace-id")) {
676
+ return noWorkspaceSelected();
677
+ }
678
+ return;
679
+ }
680
+ function noWorkspaceSelected() {
681
+ const fix = envKeyReplacesFile() ? ENV_KEY_WORKSPACE_FIX : "Run: mutagent workspaces use <name>";
682
+ return new TenancyError("WORKSPACE_REQUIRED", "No workspace selected.", fix, 3);
683
+ }
684
+ function envKeyHasNoSelection() {
685
+ return new TenancyError("WORKSPACE_FROM_ENV", "With MUTAGENT_API_KEY set, the workspace comes from --workspace or MUTAGENT_WORKSPACE_ID.", ENV_KEY_WORKSPACE_FIX);
686
+ }
687
+ async function tenancyFetch(url, init) {
688
+ try {
689
+ return await fetch(url, init);
690
+ } catch {
691
+ throw new MutagentError("REQUEST_FAILED", `Unable to connect to the Mutagent server at ${new URL(url).origin}.`, "Check your connection and the endpoint: mutagent config get endpoint");
692
+ }
693
+ }
694
+ async function fetchKeySelf(apiKey, endpoint, opts = {}) {
695
+ const response = await tenancyFetch(`${endpoint}/api/api-keys/self`, {
696
+ headers: { "x-api-key": apiKey, ...opts.headers }
697
+ });
698
+ if (!response.ok) {
699
+ const body = await response.text().catch(() => "");
700
+ throw tenancyFailure(response.status, body, opts.ctx ?? {}) ?? new MutagentError("KEY_SCOPE_UNAVAILABLE", `Could not read this API key's scope (${String(response.status)}).`, "Check the endpoint with: mutagent config get endpoint");
701
+ }
702
+ return await response.json();
703
+ }
704
+ async function fetchOrganizations(apiKey, endpoint) {
705
+ try {
706
+ const response = await fetch(`${endpoint}/api/organizations`, {
707
+ headers: { "x-api-key": apiKey }
708
+ });
709
+ if (!response.ok)
710
+ return [];
711
+ const data = await response.json();
712
+ return data.data ?? [];
713
+ } catch {
714
+ return [];
715
+ }
716
+ }
717
+ function matchesOrganization(value, org) {
718
+ if (!org)
719
+ return false;
720
+ return value === org.id || org.slug !== undefined && value === org.slug;
721
+ }
722
+ function otherOrganizationHint(wanted, keyOrg, orgs) {
723
+ const others = orgs.filter((o) => o.id !== keyOrg?.id);
724
+ const where = keyOrg ? ` in ${keyOrg.name}` : "";
725
+ const what = `No workspace "${wanted}"${where}.`;
726
+ const named = others.map((o) => `${o.name} (${o.slug ?? o.id})`).join(", ");
727
+ if (others.length === 1 && others[0]) {
728
+ const only = others[0];
729
+ return new TenancyError("WORKSPACE_NOT_FOUND", `${what} Your other organization: ${named}.`, `To switch: mutagent login --org ${only.slug ?? only.id}`);
730
+ }
731
+ if (others.length > 1) {
732
+ return new TenancyError("WORKSPACE_NOT_FOUND", `${what} Your other organizations: ${named}.`, "To switch: mutagent login --org <slug>");
733
+ }
734
+ return new TenancyError("WORKSPACE_NOT_FOUND", what, "See: mutagent workspaces list");
735
+ }
736
+ function setOrgAssertion(value) {
737
+ const trimmed = value?.trim();
738
+ orgAssertion = trimmed === "" ? undefined : trimmed;
739
+ }
740
+ function setWorkspaceFlag(value) {
741
+ const trimmed = value?.trim();
742
+ workspaceFlag = trimmed === "" ? undefined : trimmed;
743
+ }
744
+ function endpointOf(configured) {
745
+ const trimmed = configured?.trim();
746
+ return (trimmed === undefined || trimmed === "" ? "https://api.mutagent.io" : trimmed).replace(/\/+$/, "");
747
+ }
748
+ async function probeKeyScope(apiKey, endpoint, workspaceNamed) {
749
+ const fromFile = !envKeyReplacesFile();
750
+ if (!fromFile && workspaceNamed && orgAssertion === undefined)
751
+ return;
752
+ try {
753
+ const self = await fetchKeySelf(apiKey, endpoint);
754
+ if (fromFile)
755
+ recordKeyScope(self);
756
+ return self;
757
+ } catch (error) {
758
+ if (error instanceof MutagentError && (error.code === "AUTH_EXPIRED" || error.code === "REQUEST_FAILED"))
759
+ throw error;
760
+ return;
761
+ }
762
+ }
763
+ function enforceOrgAssertion(org) {
764
+ if (orgAssertion === undefined)
765
+ return;
766
+ if (org === undefined) {
767
+ throw new TenancyError("ORG_UNKNOWN", "This key's organization is unknown, so --org cannot be checked.", "Run: mutagent workspaces current");
768
+ }
769
+ if (!matchesOrganization(orgAssertion, org)) {
770
+ throw new TenancyError("ORG_MISMATCH", `Your key is for ${org.name}.`, `Run: mutagent login --org ${orgAssertion}`);
771
+ }
772
+ }
773
+ async function resolveKeyTenancy(apiKey, opts = {}) {
774
+ const config = loadConfig();
775
+ const endpoint = endpointOf(config.endpoint);
776
+ let keyScope = config.keyScope;
777
+ let organization = config.defaultOrganization ? { id: config.defaultOrganization, name: config.organizationName ?? config.defaultOrganization, slug: config.organizationSlug } : undefined;
778
+ let workspaceId = config.defaultWorkspace;
779
+ let workspaceName = config.workspaceName;
780
+ const workspaceOrigin = config.workspaceOrigin;
781
+ if (keyScope === undefined && (opts.probe !== false || orgAssertion !== undefined)) {
782
+ const self = await probeKeyScope(apiKey, endpoint, workspaceId !== undefined);
783
+ if (self) {
784
+ keyScope = self.scope;
785
+ organization = self.organization ?? undefined;
786
+ if (self.scope === "workspace" && self.workspace && workspaceOrigin !== "override") {
787
+ workspaceId = self.workspace.id;
788
+ workspaceName = self.workspace.name;
789
+ }
790
+ }
791
+ }
792
+ enforceOrgAssertion(organization);
793
+ if (opts.resolveOverride !== false && workspaceFlag !== undefined && workspaceId === workspaceFlag && !looksLikeWorkspaceId(workspaceId)) {
794
+ const found = await findWorkspace(workspaceId, { apiKey, endpoint, keyScope, organization, workspaceId: config.defaultWorkspace, workspaceName });
795
+ workspaceId = found.id;
796
+ workspaceName = found.name;
797
+ process.env.MUTAGENT_WORKSPACE_ID = found.id;
798
+ }
799
+ return { endpoint, keyScope, organization, workspaceId, workspaceName, workspaceOrigin };
800
+ }
801
+ async function listKeyWorkspaces(apiKey, endpoint, t) {
802
+ const headers = { "x-api-key": apiKey };
803
+ if (t.keyScope === "user" && t.organization)
804
+ headers["x-organization-id"] = t.organization.id;
805
+ const response = await tenancyFetch(`${endpoint}/api/workspaces`, { headers });
806
+ if (!response.ok) {
807
+ const body = await response.text().catch(() => "");
808
+ throw tenancyFailure(response.status, body, { keyScope: t.keyScope, organizationName: t.organization?.name }) ?? new MutagentError("REQUEST_FAILED", `Could not list workspaces (${String(response.status)}).`, "Run: mutagent workspaces current");
809
+ }
810
+ const data = await response.json();
811
+ return data.workspaces ?? [];
812
+ }
813
+ async function findWorkspace(wanted, t) {
814
+ if (t.keyScope === "workspace") {
815
+ if (t.workspaceId && (wanted === t.workspaceId || wanted === t.workspaceName)) {
816
+ return { id: t.workspaceId, name: t.workspaceName ?? t.workspaceId };
817
+ }
818
+ throw new TenancyError("WORKSPACE_KEY_MISMATCH", `This key only works in ${t.workspaceName ?? t.workspaceId ?? "its own workspace"}.`, "Run: mutagent login (for a key that can switch workspaces)");
819
+ }
820
+ const rows = await listKeyWorkspaces(t.apiKey, t.endpoint, t);
821
+ const byId = rows.find((w) => w.id === wanted);
822
+ if (byId)
823
+ return { id: byId.id, name: byId.name };
824
+ let matches = rows.filter((w) => w.name === wanted || w.slug === wanted);
825
+ if (matches.length === 0)
826
+ matches = rows.filter((w) => w.name.toLowerCase() === wanted.toLowerCase());
827
+ if (matches.length === 1 && matches[0])
828
+ return { id: matches[0].id, name: matches[0].name };
829
+ if (matches.length > 1) {
830
+ throw new TenancyError("WORKSPACE_AMBIGUOUS", `Several workspaces are named "${wanted}": ${matches.map((w) => `${w.name} (${w.id})`).join(", ")}.`, "Run: mutagent workspaces use <id>");
831
+ }
832
+ const orgs = await fetchOrganizations(t.apiKey, t.endpoint);
833
+ throw otherOrganizationHint(wanted, t.organization, orgs);
834
+ }
835
+ var TENANCY_DENIED = "TENANCY_DENIED", UUID_PATTERN, TenancyError, ENV_KEY_WORKSPACE_FIX = "Pass --workspace <name|id> or set MUTAGENT_WORKSPACE_ID", orgAssertion, workspaceFlag;
836
+ var init_organization_scope = __esm(() => {
837
+ init_errors();
838
+ init_config();
839
+ UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
840
+ TenancyError = class TenancyError extends MutagentError {
841
+ constructor(code, what, fix, exitCode = 1) {
842
+ super(code, `${what} ${fix}`, fix.replace(/^(Run|See|To switch): /, ""), exitCode);
843
+ this.name = "TenancyError";
844
+ }
845
+ };
846
+ });
847
+
848
+ // src/lib/provider-request.ts
849
+ function buildCreateProviderBody(data) {
850
+ const body = {
851
+ name: data.name,
852
+ provider: data.provider,
853
+ isDefault: data.isDefault ?? false
854
+ };
855
+ if (data.apiKey)
856
+ body.apiKey = data.apiKey;
857
+ if (data.baseUrl)
858
+ body.baseUrl = data.baseUrl;
859
+ if (data.hostedFamily)
860
+ body.hostedFamily = data.hostedFamily;
861
+ if (data.credentialFields && Object.keys(data.credentialFields).length > 0) {
862
+ body.credentialFields = data.credentialFields;
863
+ }
864
+ if (data.metadata && Object.keys(data.metadata).length > 0)
865
+ body.metadata = data.metadata;
866
+ if (data.supportedModels !== undefined)
867
+ body.supportedModels = data.supportedModels;
868
+ return body;
869
+ }
870
+
447
871
  // src/lib/sdk-debug.ts
448
872
  function isDebugEnabled(env = process.env) {
449
873
  return TRUTHY.has((env.MUTAGENT_DEBUG ?? "").trim().toLowerCase());
@@ -538,282 +962,6 @@ var init_sdk_debug = __esm(() => {
538
962
  };
539
963
  });
540
964
 
541
- // src/lib/organization-scope.ts
542
- function isWellFormedOrganizationId(id) {
543
- return ORGANIZATION_ID_PATTERN.test(id);
544
- }
545
- function isOrganizationScopeMismatch(status, body) {
546
- if (status !== 403)
547
- return false;
548
- return body.toLowerCase().includes("organization scope mismatch");
549
- }
550
- async function lookupOrganizations(apiKey, endpoint) {
551
- try {
552
- const response = await fetch(`${endpoint}/api/organizations`, {
553
- headers: { "x-api-key": apiKey }
554
- });
555
- if (!response.ok)
556
- return [];
557
- const data = await response.json();
558
- return data.data ?? [];
559
- } catch {
560
- return [];
561
- }
562
- }
563
- async function fetchOrganizations(apiKey, endpoint) {
564
- return lookupOrganizations(apiKey, endpoint);
565
- }
566
- async function assertUsableOrganizationId(id, lookup) {
567
- const orgs = lookup.apiKey ? await lookup.fetchOrganizations(lookup.apiKey, lookup.endpoint).catch(() => []) : [];
568
- if (orgs.length === 0) {
569
- if (isWellFormedOrganizationId(id))
570
- return;
571
- throw new MutagentError("INVALID_ORGANIZATION_ID", `"${id}" is not an organization id.`, `Organization ids look like org_xxxxxxxxxxxxxxxxxxxxx.
572
- ` + "Log in so the id can be checked against your account: mutagent auth login --browser");
573
- }
574
- if (orgs.some((org) => org.id === id))
575
- return;
576
- throw new MutagentError("UNKNOWN_ORGANIZATION", `"${id}" is not an organization this API key can access.`, `Available: ${orgs.map((org) => org.id).join(", ")}
577
- ` + "A stored organization that does not match the key makes every request fail with 403.");
578
- }
579
- function createOrgScopeRecoveringFetcher(hooks) {
580
- let recoveryUsed = false;
581
- async function recover() {
582
- if (recoveryUsed)
583
- return;
584
- recoveryUsed = true;
585
- const stale = hooks.currentOrganization();
586
- if (!stale)
587
- return;
588
- hooks.adoptOrganization(undefined);
589
- hooks.forgetStoredOrganization?.();
590
- const orgs = await lookupOrganizations(hooks.apiKey, hooks.endpoint);
591
- const resolved = orgs[0]?.id;
592
- if (!resolved || resolved === stale)
593
- return;
594
- hooks.adoptOrganization(resolved);
595
- if (isDebugEnabled()) {
596
- process.stderr.write(`[mutagent] Stored organization was rejected by the API; re-resolved and retried once.
597
- `);
598
- }
599
- return resolved;
600
- }
601
- return async function fetchWithOrgRecovery(input, init) {
602
- const retryTemplate = input instanceof Request ? input.clone() : undefined;
603
- const response = init == null ? await fetch(input) : await fetch(input, init);
604
- if (response.status !== 403)
605
- return response;
606
- const body = await response.clone().text().catch(() => "");
607
- if (!isOrganizationScopeMismatch(response.status, body))
608
- return response;
609
- const resolved = await recover();
610
- if (!resolved)
611
- return response;
612
- if (retryTemplate) {
613
- retryTemplate.headers.set("x-organization-id", resolved);
614
- return fetch(retryTemplate);
615
- }
616
- const headers = new Headers(init?.headers);
617
- headers.set("x-organization-id", resolved);
618
- return fetch(input, { ...init, headers });
619
- };
620
- }
621
- var ORGANIZATION_ID_PATTERN;
622
- var init_organization_scope = __esm(() => {
623
- init_errors();
624
- init_sdk_debug();
625
- ORGANIZATION_ID_PATTERN = /^org_[A-Za-z0-9_-]{8,}$/;
626
- });
627
-
628
- // src/lib/config.ts
629
- import { cosmiconfigSync } from "cosmiconfig";
630
- import { z } from "zod";
631
- import { homedir } from "os";
632
- import { join } from "path";
633
- import { existsSync, readFileSync, mkdirSync } from "fs";
634
- function parseJsonSafe(content, schema) {
635
- try {
636
- const parsed = JSON.parse(content);
637
- return schema.parse(parsed);
638
- } catch {
639
- return null;
640
- }
641
- }
642
- function loadConfig() {
643
- const explorer = cosmiconfigSync("mutagent");
644
- const result = explorer.search();
645
- const rcConfig = result?.config;
646
- let credentials = null;
647
- if (existsSync(CREDENTIALS_FILE)) {
648
- credentials = parseJsonSafe(readFileSync(CREDENTIALS_FILE, "utf-8"), credentialsSchema);
649
- }
650
- const merged = {
651
- providers: {},
652
- environment: "production",
653
- ...rcConfig,
654
- ...credentials,
655
- apiKey: process.env.MUTAGENT_API_KEY ?? credentials?.apiKey ?? rcConfig?.apiKey,
656
- endpoint: process.env.MUTAGENT_ENDPOINT ?? credentials?.endpoint ?? rcConfig?.endpoint,
657
- defaultWorkspace: process.env.MUTAGENT_WORKSPACE_ID ?? credentials?.defaultWorkspace ?? rcConfig?.defaultWorkspace,
658
- defaultWorkspaceSource: process.env.MUTAGENT_WORKSPACE_ID ? "user" : credentials?.defaultWorkspace ? credentials.defaultWorkspaceSource ?? "login-inferred" : rcConfig?.defaultWorkspace ? "login-inferred" : undefined,
659
- defaultOrganization: credentials?.defaultOrganization ?? rcConfig?.defaultOrganization
660
- };
661
- return configSchema.parse(merged);
662
- }
663
- function saveCredentials(apiKey, endpoint) {
664
- if (!existsSync(CREDENTIALS_DIR)) {
665
- mkdirSync(CREDENTIALS_DIR, { recursive: true });
666
- }
667
- const config = loadConfig();
668
- const credentials = {
669
- apiKey,
670
- endpoint: endpoint ?? config.endpoint
671
- };
672
- writeSecureFile(CREDENTIALS_FILE, JSON.stringify(credentials, null, 2));
673
- }
674
- function saveFullCredentials(creds) {
675
- if (!existsSync(CREDENTIALS_DIR)) {
676
- mkdirSync(CREDENTIALS_DIR, { recursive: true });
677
- }
678
- let existingCredentials = {};
679
- if (existsSync(CREDENTIALS_FILE)) {
680
- try {
681
- existingCredentials = JSON.parse(readFileSync(CREDENTIALS_FILE, "utf-8"));
682
- } catch {}
683
- }
684
- const existingSource = existingCredentials.defaultWorkspaceSource;
685
- const workspaceChanged = creds.workspaceId !== undefined && creds.workspaceId !== existingCredentials.defaultWorkspace;
686
- const source = creds.workspaceId === undefined ? existingSource : workspaceChanged ? "login-inferred" : existingSource ?? "login-inferred";
687
- const credentials = {
688
- ...existingCredentials,
689
- apiKey: creds.apiKey,
690
- endpoint: creds.endpoint ?? existingCredentials.endpoint ?? "https://api.mutagent.io",
691
- defaultWorkspace: creds.workspaceId ?? existingCredentials.defaultWorkspace,
692
- defaultWorkspaceSource: source,
693
- defaultOrganization: creds.organizationId ?? existingCredentials.defaultOrganization,
694
- expiresAt: creds.expiresAt
695
- };
696
- writeSecureFile(CREDENTIALS_FILE, JSON.stringify(credentials, null, 2));
697
- }
698
- function clearCredentials() {
699
- if (existsSync(CREDENTIALS_FILE)) {
700
- writeSecureFile(CREDENTIALS_FILE, JSON.stringify({}));
701
- }
702
- }
703
- function hasCredentials() {
704
- if (process.env.MUTAGENT_API_KEY)
705
- return true;
706
- if (!existsSync(CREDENTIALS_FILE))
707
- return false;
708
- const credentials = parseJsonSafe(readFileSync(CREDENTIALS_FILE, "utf-8"), credentialsSchema);
709
- return credentials?.apiKey !== undefined && credentials.apiKey !== "";
710
- }
711
- function getApiKey() {
712
- const apiKey = process.env.MUTAGENT_API_KEY ?? loadConfig().apiKey;
713
- registerSecret(apiKey);
714
- return apiKey;
715
- }
716
- function setDefaultWorkspace(workspaceId, source = "user") {
717
- if (!existsSync(CREDENTIALS_DIR)) {
718
- mkdirSync(CREDENTIALS_DIR, { recursive: true });
719
- }
720
- let existingCredentials = {};
721
- if (existsSync(CREDENTIALS_FILE)) {
722
- try {
723
- existingCredentials = JSON.parse(readFileSync(CREDENTIALS_FILE, "utf-8"));
724
- } catch {}
725
- }
726
- const updated = {
727
- ...existingCredentials,
728
- defaultWorkspace: workspaceId,
729
- defaultWorkspaceSource: source
730
- };
731
- writeSecureFile(CREDENTIALS_FILE, JSON.stringify(updated, null, 2));
732
- }
733
- function setDefaultOrganization(organizationId) {
734
- if (!isWellFormedOrganizationId(organizationId)) {
735
- throw new MutagentError("INVALID_ORGANIZATION_ID", `"${organizationId}" is not an organization id.`, `Organization ids look like org_xxxxxxxxxxxxxxxxxxxxx.
736
- ` + "List the ones you can use with: mutagent auth status");
737
- }
738
- if (!existsSync(CREDENTIALS_DIR)) {
739
- mkdirSync(CREDENTIALS_DIR, { recursive: true });
740
- }
741
- let existingCredentials = {};
742
- if (existsSync(CREDENTIALS_FILE)) {
743
- try {
744
- existingCredentials = JSON.parse(readFileSync(CREDENTIALS_FILE, "utf-8"));
745
- } catch {}
746
- }
747
- const updated = {
748
- ...existingCredentials,
749
- defaultOrganization: organizationId
750
- };
751
- writeSecureFile(CREDENTIALS_FILE, JSON.stringify(updated, null, 2));
752
- }
753
- function clearDefaultOrganization() {
754
- if (!existsSync(CREDENTIALS_FILE))
755
- return;
756
- let existingCredentials;
757
- try {
758
- existingCredentials = JSON.parse(readFileSync(CREDENTIALS_FILE, "utf-8"));
759
- } catch {
760
- return;
761
- }
762
- if (!("defaultOrganization" in existingCredentials))
763
- return;
764
- delete existingCredentials.defaultOrganization;
765
- writeSecureFile(CREDENTIALS_FILE, JSON.stringify(existingCredentials, null, 2));
766
- }
767
- var configSchema, credentialsSchema, CREDENTIALS_DIR, CREDENTIALS_FILE;
768
- var init_config = __esm(() => {
769
- init_secure_file();
770
- init_known_secrets();
771
- init_organization_scope();
772
- init_errors();
773
- configSchema = z.object({
774
- apiKey: z.string().optional(),
775
- endpoint: z.string().default("https://api.mutagent.io"),
776
- format: z.enum(["table", "json"]).default("table"),
777
- timeout: z.number().default(30000),
778
- defaultWorkspace: z.string().optional(),
779
- defaultWorkspaceSource: z.enum(["user", "login-inferred"]).optional(),
780
- defaultOrganization: z.string().optional()
781
- });
782
- credentialsSchema = z.object({
783
- apiKey: z.string().optional(),
784
- endpoint: z.string().optional(),
785
- defaultWorkspace: z.string().optional(),
786
- defaultWorkspaceSource: z.enum(["user", "login-inferred"]).optional(),
787
- defaultOrganization: z.string().optional(),
788
- expiresAt: z.string().optional()
789
- }).loose();
790
- CREDENTIALS_DIR = join(homedir(), ".config", "mutagent");
791
- CREDENTIALS_FILE = join(CREDENTIALS_DIR, "credentials.json");
792
- });
793
-
794
- // src/lib/provider-request.ts
795
- function buildCreateProviderBody(data) {
796
- const body = {
797
- name: data.name,
798
- provider: data.provider,
799
- isDefault: data.isDefault ?? false
800
- };
801
- if (data.apiKey)
802
- body.apiKey = data.apiKey;
803
- if (data.baseUrl)
804
- body.baseUrl = data.baseUrl;
805
- if (data.hostedFamily)
806
- body.hostedFamily = data.hostedFamily;
807
- if (data.credentialFields && Object.keys(data.credentialFields).length > 0) {
808
- body.credentialFields = data.credentialFields;
809
- }
810
- if (data.metadata && Object.keys(data.metadata).length > 0)
811
- body.metadata = data.metadata;
812
- if (data.supportedModels !== undefined)
813
- body.supportedModels = data.supportedModels;
814
- return body;
815
- }
816
-
817
965
  // src/lib/sdk-mappers.ts
818
966
  import { LlmProviderType as SDKProviderEnum } from "@mutagent/sdk/models";
819
967
  function isProviderType(value) {
@@ -902,15 +1050,6 @@ var init_sdk_mappers = __esm(() => {
902
1050
  });
903
1051
 
904
1052
  // src/lib/sdk-client.ts
905
- var exports_sdk_client = {};
906
- __export(exports_sdk_client, {
907
- validateApiKey: () => validateApiKey,
908
- resetSDKClient: () => resetSDKClient,
909
- getSDKClient: () => getSDKClient,
910
- fetchWorkspaces: () => fetchWorkspaces,
911
- fetchOrganizations: () => fetchOrganizations,
912
- MutagentSDK: () => SDKClientWrapper
913
- });
914
1053
  import { Mutagent, HTTPClient } from "@mutagent/sdk";
915
1054
 
916
1055
  class SDKClientWrapper {
@@ -919,32 +1058,24 @@ class SDKClientWrapper {
919
1058
  endpoint;
920
1059
  workspaceId;
921
1060
  organizationId;
922
- workspaceResolution = "none";
1061
+ tenancy;
923
1062
  debugSecrets = new Set;
924
- recoveringFetch;
925
1063
  constructor(opts) {
926
1064
  this.apiKey = opts.apiKey;
927
1065
  this.endpoint = opts.serverURL ?? "http://localhost:3003";
928
1066
  this.workspaceId = opts.workspaceId;
929
1067
  this.organizationId = opts.organizationId;
930
- if (opts.workspaceId && opts.workspaceSource === "user") {
931
- this.workspaceResolution = "configured";
932
- }
933
- this.recoveringFetch = createOrgScopeRecoveringFetcher({
934
- apiKey: this.apiKey,
935
- endpoint: this.endpoint,
936
- currentOrganization: () => this.organizationId,
937
- adoptOrganization: (id) => {
938
- this.organizationId = id;
939
- },
940
- forgetStoredOrganization: opts.clearStoredOrganization
941
- });
942
- const httpClient = new HTTPClient({ fetcher: this.recoveringFetch });
1068
+ this.tenancy = {
1069
+ keyScope: opts.keyScope,
1070
+ workspaceId: opts.workspaceId,
1071
+ workspaceName: opts.workspaceName,
1072
+ workspaceOrigin: opts.workspaceOrigin ?? (opts.workspaceId ? "selection" : undefined),
1073
+ organizationName: opts.organizationName
1074
+ };
1075
+ const httpClient = new HTTPClient;
943
1076
  httpClient.addHook("beforeRequest", (req) => {
944
- if (this.workspaceId)
945
- req.headers.set("x-workspace-id", this.workspaceId);
946
- if (this.organizationId)
947
- req.headers.set("x-organization-id", this.organizationId);
1077
+ for (const [name, value] of Object.entries(this.tenancyHeaders()))
1078
+ req.headers.set(name, value);
948
1079
  return req;
949
1080
  });
950
1081
  this.debugSecrets.add(this.apiKey);
@@ -961,6 +1092,11 @@ class SDKClientWrapper {
961
1092
  handleError(error) {
962
1093
  if (error && typeof error === "object") {
963
1094
  const err = error;
1095
+ if (err.statusCode) {
1096
+ const tenancy = tenancyFailure(err.statusCode, err.body ?? "", this.tenancy);
1097
+ if (tenancy)
1098
+ throw tenancy;
1099
+ }
964
1100
  if (err.statusCode === 401) {
965
1101
  throw new AuthenticationError;
966
1102
  }
@@ -999,28 +1135,26 @@ class SDKClientWrapper {
999
1135
  } else if (optHeaders) {
1000
1136
  extraHeaders = optHeaders;
1001
1137
  }
1002
- const tenancyHeaders = {};
1003
- if (this.workspaceId)
1004
- tenancyHeaders["x-workspace-id"] = this.workspaceId;
1005
- if (this.organizationId)
1006
- tenancyHeaders["x-organization-id"] = this.organizationId;
1007
- const response = await this.recoveringFetch(`${this.endpoint}${path}`, {
1138
+ const response = await fetch(`${this.endpoint}${path}`, {
1008
1139
  ...options,
1009
1140
  headers: {
1010
1141
  "x-api-key": this.apiKey,
1011
1142
  "Content-Type": "application/json",
1012
- ...tenancyHeaders,
1143
+ ...this.tenancyHeaders(),
1013
1144
  ...extraHeaders
1014
1145
  }
1015
1146
  });
1016
1147
  if (!response.ok) {
1148
+ const error = await response.text();
1149
+ const tenancy = tenancyFailure(response.status, error, this.tenancy);
1150
+ if (tenancy)
1151
+ throw tenancy;
1017
1152
  if (response.status === 401) {
1018
1153
  throw new AuthenticationError;
1019
1154
  }
1020
1155
  if (response.status === 403) {
1021
- throw new ApiError(403, await response.text() || "Access denied");
1156
+ throw new ApiError(403, error || "Access denied");
1022
1157
  }
1023
- const error = await response.text();
1024
1158
  const errorLower = error.toLowerCase();
1025
1159
  if (errorLower.includes("workspace") && (errorLower.includes("missing") || errorLower.includes("required")) || errorLower.includes("x-workspace-id")) {
1026
1160
  throw new WorkspaceContextError(error);
@@ -1166,11 +1300,9 @@ class SDKClientWrapper {
1166
1300
  }
1167
1301
  async getModelsCatalog() {
1168
1302
  try {
1169
- const wsId = this.workspaceId;
1170
- if (!wsId)
1303
+ if (!this.workspaceId)
1171
1304
  return { models: [] };
1172
- const qs = `?workspaceId=${encodeURIComponent(wsId)}`;
1173
- return await this.request(`/api/providers/catalog${qs}`);
1305
+ return await this.request("/api/providers/catalog");
1174
1306
  } catch {
1175
1307
  return { models: [] };
1176
1308
  }
@@ -1178,56 +1310,17 @@ class SDKClientWrapper {
1178
1310
  getCurrentWorkspaceId() {
1179
1311
  return this.workspaceId;
1180
1312
  }
1181
- getCurrentOrgId() {
1182
- return this.organizationId;
1183
- }
1184
- async ensureContext() {
1185
- const needsWorkspaceClassification = !this.workspaceId || this.workspaceResolution === "none";
1186
- if (this.organizationId && !needsWorkspaceClassification)
1187
- return;
1188
- if (!this.organizationId) {
1189
- let orgs = await fetchOrganizations(this.apiKey, this.endpoint).catch(() => []);
1190
- if (orgs.length === 0) {
1191
- await new Promise((r) => setTimeout(r, 1000));
1192
- orgs = await fetchOrganizations(this.apiKey, this.endpoint).catch(() => []);
1193
- }
1194
- if (orgs.length > 0 && orgs[0]) {
1195
- this.organizationId = orgs[0].id;
1196
- }
1197
- }
1198
- if (this.workspaceResolution === "none" && this.organizationId) {
1199
- try {
1200
- const response = await this.sdk.workspaces.listWorkspaces({ includeInactive: false });
1201
- const workspaces = toWorkspaceList(response.workspaces);
1202
- if (workspaces.length === 1 && workspaces[0]) {
1203
- this.workspaceId = workspaces[0].id;
1204
- this.workspaceResolution = "sole";
1205
- } else if (workspaces.length > 1) {
1206
- const defaultWs = workspaces.find((ws) => ws.isDefault);
1207
- if (defaultWs) {
1208
- this.workspaceId = defaultWs.id;
1209
- this.workspaceResolution = "configured";
1210
- } else {
1211
- const first = workspaces[0] ?? undefined;
1212
- if (first) {
1213
- this.workspaceId = this.workspaceId ?? first.id;
1214
- this.workspaceResolution = "ambiguous";
1215
- }
1216
- }
1217
- }
1218
- } catch {}
1219
- }
1220
- }
1221
- getWorkspaceResolution() {
1222
- return this.workspaceResolution;
1313
+ tenancyHeaders() {
1314
+ return tenancyHeaders({
1315
+ keyScope: this.tenancy.keyScope,
1316
+ workspaceId: this.workspaceId,
1317
+ workspaceOrigin: this.tenancy.workspaceOrigin,
1318
+ organizationId: this.organizationId
1319
+ });
1223
1320
  }
1224
- requireUnambiguousWorkspace() {
1225
- if (!this.workspaceId || this.workspaceResolution === "none") {
1226
- throw new MutagentError("WORKSPACE_REQUIRED", "No workspace configured.", "Set one with: mutagent config set workspace <workspace-id> (list them with: mutagent workspaces list)");
1227
- }
1228
- if (this.workspaceResolution === "ambiguous") {
1229
- throw new MutagentError("WORKSPACE_AMBIGUOUS", "Several workspaces are available and none is marked default, so the target for this write is ambiguous.", "Choose one explicitly: mutagent config set workspace <workspace-id> (list them with: mutagent workspaces list)");
1230
- }
1321
+ requireSelectedWorkspace() {
1322
+ if (!this.workspaceId)
1323
+ throw noWorkspaceSelected();
1231
1324
  return this.workspaceId;
1232
1325
  }
1233
1326
  async testProvider(id) {
@@ -1241,7 +1334,7 @@ class SDKClientWrapper {
1241
1334
  }
1242
1335
  }
1243
1336
  async createProvider(data) {
1244
- this.requireUnambiguousWorkspace();
1337
+ this.requireSelectedWorkspace();
1245
1338
  registerSecret(data.apiKey);
1246
1339
  registerSecretFields(data.credentialFields);
1247
1340
  const body = buildCreateProviderBody(data);
@@ -1256,7 +1349,7 @@ class SDKClientWrapper {
1256
1349
  }
1257
1350
  }
1258
1351
  async updateProvider(id, data) {
1259
- const workspaceId = this.requireUnambiguousWorkspace();
1352
+ const workspaceId = this.requireSelectedWorkspace();
1260
1353
  if (data.apiKey)
1261
1354
  this.debugSecrets.add(data.apiKey);
1262
1355
  registerSecret(data.apiKey);
@@ -1295,7 +1388,7 @@ class SDKClientWrapper {
1295
1388
  }
1296
1389
  }
1297
1390
  async deleteProvider(id) {
1298
- const workspaceId = this.requireUnambiguousWorkspace();
1391
+ const workspaceId = this.requireSelectedWorkspace();
1299
1392
  try {
1300
1393
  await this.sdk.llmProviders.deleteProviderConfig({ id });
1301
1394
  return { workspaceId };
@@ -1310,21 +1403,18 @@ async function getSDKClient() {
1310
1403
  if (!apiKey) {
1311
1404
  throw new AuthenticationError;
1312
1405
  }
1313
- const config = loadConfig();
1406
+ const tenancy = await resolveKeyTenancy(apiKey);
1407
+ if (tenancy.keyScope === "organization" && !tenancy.workspaceId)
1408
+ throw noWorkspaceSelected();
1314
1409
  sdkClient = new SDKClientWrapper({
1315
1410
  apiKey,
1316
- serverURL: config.endpoint,
1317
- workspaceId: config.defaultWorkspace,
1318
- organizationId: config.defaultOrganization,
1319
- workspaceSource: config.defaultWorkspaceSource,
1320
- clearStoredOrganization: clearDefaultOrganization
1321
- });
1322
- await sdkClient.ensureContext().catch((err) => {
1323
- if (isDebugEnabled()) {
1324
- const message = err instanceof Error ? err.message : String(err);
1325
- process.stderr.write(`[mutagent] Context resolution failed: ${message}
1326
- `);
1327
- }
1411
+ serverURL: tenancy.endpoint,
1412
+ workspaceId: tenancy.workspaceId,
1413
+ workspaceName: tenancy.workspaceName,
1414
+ workspaceOrigin: tenancy.workspaceOrigin,
1415
+ organizationId: tenancy.organization?.id,
1416
+ organizationName: tenancy.organization?.name,
1417
+ keyScope: tenancy.keyScope
1328
1418
  });
1329
1419
  }
1330
1420
  return sdkClient;
@@ -1342,19 +1432,6 @@ async function validateApiKey(apiKey, endpoint) {
1342
1432
  return false;
1343
1433
  }
1344
1434
  }
1345
- async function fetchWorkspaces(apiKey, endpoint, orgId) {
1346
- try {
1347
- const response = await fetch(`${endpoint}/api/workspaces`, {
1348
- headers: { "x-api-key": apiKey, "x-organization-id": orgId }
1349
- });
1350
- if (!response.ok)
1351
- return [];
1352
- const data = await response.json();
1353
- return data.workspaces ?? [];
1354
- } catch {
1355
- return [];
1356
- }
1357
- }
1358
1435
  var sdkClient = null;
1359
1436
  var init_sdk_client = __esm(() => {
1360
1437
  init_errors();
@@ -1587,4 +1664,4 @@ export {
1587
1664
  ApiError
1588
1665
  };
1589
1666
 
1590
- //# debugId=437AE9F8B6FD0C9364756E2164756E21
1667
+ //# debugId=E203EEB55C5F59D464756E2164756E21