@mutagent/cli 0.2.52 → 0.2.53

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/bin/cli.js CHANGED
@@ -901,11 +901,6 @@ function tenancyHeaders(t) {
901
901
  const headers = {};
902
902
  if (!t.workspaceId)
903
903
  return headers;
904
- if (t.keyScope === "workspace") {
905
- if (t.workspaceOrigin === "override")
906
- headers["x-workspace-id"] = t.workspaceId;
907
- return headers;
908
- }
909
904
  headers["x-workspace-id"] = t.workspaceId;
910
905
  if (t.keyScope === "user" && t.organizationId)
911
906
  headers["x-organization-id"] = t.organizationId;
@@ -915,7 +910,6 @@ function configTenancyHeaders(config) {
915
910
  return tenancyHeaders({
916
911
  keyScope: config.keyScope,
917
912
  workspaceId: config.defaultWorkspace,
918
- workspaceOrigin: config.workspaceOrigin,
919
913
  organizationId: config.defaultOrganization
920
914
  });
921
915
  }
@@ -1052,6 +1046,17 @@ ${apiKey}`;
1052
1046
  probe.catch(() => keySelfProbes.delete(cacheKey));
1053
1047
  return probe;
1054
1048
  }
1049
+ function confirmKeyScope(apiKey, endpoint) {
1050
+ const cacheKey = `${endpoint}
1051
+ ${apiKey}`;
1052
+ const done = keyScopeConfirmations.get(cacheKey);
1053
+ if (done)
1054
+ return done;
1055
+ const read = readKeyScope(apiKey, endpoint, !envKeyReplacesFile());
1056
+ keyScopeConfirmations.set(cacheKey, read);
1057
+ keySelfProbes.set(cacheKey, read);
1058
+ return read;
1059
+ }
1055
1060
  async function readKeyScope(apiKey, endpoint, fromFile) {
1056
1061
  try {
1057
1062
  const self = await fetchKeySelf(apiKey, endpoint);
@@ -1119,7 +1124,14 @@ async function findWorkspace(wanted, t) {
1119
1124
  if (t.workspaceId && (wanted === t.workspaceId || wanted === t.workspaceName)) {
1120
1125
  return { id: t.workspaceId, name: t.workspaceName ?? t.workspaceId };
1121
1126
  }
1122
- 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)");
1127
+ const self = await confirmKeyScope(t.apiKey, t.endpoint);
1128
+ if (self && self.scope !== "workspace") {
1129
+ return findWorkspace(wanted, { ...t, keyScope: self.scope, organization: self.organization ?? t.organization });
1130
+ }
1131
+ const own = self?.workspace ?? (t.workspaceId ? { id: t.workspaceId, name: t.workspaceName ?? t.workspaceId } : undefined);
1132
+ if (own && (wanted === own.id || wanted === own.name))
1133
+ return { id: own.id, name: own.name };
1134
+ throw new TenancyError("WORKSPACE_KEY_MISMATCH", `This key only works in ${own?.name ?? "its own workspace"}.`, "Run: mutagent login (for a key that can switch workspaces)");
1123
1135
  }
1124
1136
  const rows = await listKeyWorkspaces(t.apiKey, t.endpoint, t);
1125
1137
  const byId = rows.find((w) => w.id === wanted);
@@ -1136,7 +1148,7 @@ async function findWorkspace(wanted, t) {
1136
1148
  const orgs = await fetchOrganizations(t.apiKey, t.endpoint);
1137
1149
  throw otherOrganizationHint(wanted, t.organization, orgs);
1138
1150
  }
1139
- var TENANCY_DENIED = "TENANCY_DENIED", UUID_PATTERN, TenancyError, ENV_KEY_WORKSPACE_FIX = "Pass --workspace <name|id> or set MUTAGENT_WORKSPACE_ID", orgAssertion, workspaceFlag, keySelfProbes;
1151
+ var TENANCY_DENIED = "TENANCY_DENIED", UUID_PATTERN, TenancyError, ENV_KEY_WORKSPACE_FIX = "Pass --workspace <name|id> or set MUTAGENT_WORKSPACE_ID", orgAssertion, workspaceFlag, keySelfProbes, keyScopeConfirmations;
1140
1152
  var init_organization_scope = __esm(() => {
1141
1153
  init_errors();
1142
1154
  init_config();
@@ -1148,6 +1160,7 @@ var init_organization_scope = __esm(() => {
1148
1160
  }
1149
1161
  };
1150
1162
  keySelfProbes = new Map;
1163
+ keyScopeConfirmations = new Map;
1151
1164
  });
1152
1165
 
1153
1166
  // src/lib/sdk-debug.ts
@@ -1464,6 +1477,46 @@ function buildCreateProviderBody(data) {
1464
1477
  return body;
1465
1478
  }
1466
1479
 
1480
+ // src/lib/key-scope-heal.ts
1481
+ function isOrgKeyWithoutWorkspace(status, body) {
1482
+ return status === 400 && body.toLowerCase().includes(ORG_KEY_NEEDS_WORKSPACE);
1483
+ }
1484
+ function sameHeaders(a, b) {
1485
+ const keys = Object.keys(a);
1486
+ return keys.length === Object.keys(b).length && keys.every((k) => a[k] === b[k]);
1487
+ }
1488
+ async function fetchWithScopeHeal(input, init, target) {
1489
+ const spare = input instanceof Request ? input.clone() : undefined;
1490
+ const response = await (init == null ? fetch(input) : fetch(input, init));
1491
+ if (response.status !== 400)
1492
+ return response;
1493
+ const body = await response.clone().text().catch(() => "");
1494
+ if (!isOrgKeyWithoutWorkspace(response.status, body))
1495
+ return response;
1496
+ const before = target.headers();
1497
+ const self = await confirmKeyScope(target.apiKey, target.endpoint).catch(() => {
1498
+ return;
1499
+ });
1500
+ if (!self)
1501
+ return response;
1502
+ target.adopt(self);
1503
+ const after = target.headers();
1504
+ if (sameHeaders(before, after))
1505
+ return response;
1506
+ if (spare) {
1507
+ const headers = new Headers(spare.headers);
1508
+ for (const [name, value] of Object.entries(after))
1509
+ headers.set(name, value);
1510
+ return fetch(new Request(spare, { headers }));
1511
+ }
1512
+ const headers = { ...init?.headers, ...after };
1513
+ return fetch(input, { ...init, headers });
1514
+ }
1515
+ var ORG_KEY_NEEDS_WORKSPACE = "x-workspace-id header required";
1516
+ var init_key_scope_heal = __esm(() => {
1517
+ init_organization_scope();
1518
+ });
1519
+
1467
1520
  // src/lib/sdk-mappers.ts
1468
1521
  import { LlmProviderType as SDKProviderEnum } from "@mutagent/sdk/models";
1469
1522
  function isProviderType(value) {
@@ -1574,7 +1627,7 @@ class SDKClientWrapper {
1574
1627
  workspaceOrigin: opts.workspaceOrigin ?? (opts.workspaceId ? "selection" : undefined),
1575
1628
  organizationName: opts.organizationName
1576
1629
  };
1577
- const httpClient = new HTTPClient;
1630
+ const httpClient = new HTTPClient({ fetcher: (input, init) => fetchWithScopeHeal(input, init, this.scopeHealTarget()) });
1578
1631
  httpClient.addHook("beforeRequest", (req) => {
1579
1632
  for (const [name, value] of Object.entries(this.tenancyHeaders()))
1580
1633
  req.headers.set(name, value);
@@ -1644,7 +1697,7 @@ class SDKClientWrapper {
1644
1697
  }
1645
1698
  let response;
1646
1699
  try {
1647
- response = await fetch(`${this.endpoint}${path}`, {
1700
+ response = await fetchWithScopeHeal(`${this.endpoint}${path}`, {
1648
1701
  ...options,
1649
1702
  headers: {
1650
1703
  "x-api-key": this.apiKey,
@@ -1652,7 +1705,7 @@ class SDKClientWrapper {
1652
1705
  ...this.tenancyHeaders(),
1653
1706
  ...extraHeaders
1654
1707
  }
1655
- });
1708
+ }, this.scopeHealTarget());
1656
1709
  } catch {
1657
1710
  throw unreachableServerError(this.endpoint);
1658
1711
  }
@@ -1826,10 +1879,23 @@ class SDKClientWrapper {
1826
1879
  return tenancyHeaders({
1827
1880
  keyScope: this.tenancy.keyScope,
1828
1881
  workspaceId: this.workspaceId,
1829
- workspaceOrigin: this.tenancy.workspaceOrigin,
1830
1882
  organizationId: this.organizationId
1831
1883
  });
1832
1884
  }
1885
+ scopeHealTarget() {
1886
+ return {
1887
+ apiKey: this.apiKey,
1888
+ endpoint: this.endpoint,
1889
+ headers: () => this.tenancyHeaders(),
1890
+ adopt: (self) => {
1891
+ this.tenancy.keyScope = self.scope;
1892
+ if (!this.workspaceId && self.scope === "workspace" && self.workspace)
1893
+ this.workspaceId = self.workspace.id;
1894
+ if (self.scope === "user" && self.organization)
1895
+ this.organizationId ??= self.organization.id;
1896
+ }
1897
+ };
1898
+ }
1833
1899
  requireSelectedWorkspace() {
1834
1900
  if (!this.workspaceId)
1835
1901
  throw noWorkspaceSelected();
@@ -1944,6 +2010,7 @@ var init_sdk_client = __esm(() => {
1944
2010
  init_key_check();
1945
2011
  init_organization_scope();
1946
2012
  init_sdk_debug();
2013
+ init_key_scope_heal();
1947
2014
  init_known_secrets();
1948
2015
  init_sdk_mappers();
1949
2016
  init_organization_scope();
@@ -2140,7 +2207,8 @@ async function createAgentDeploymentAPI() {
2140
2207
  const workspaceId = context.requireSelectedWorkspace();
2141
2208
  const httpClient = new HTTPClient2;
2142
2209
  httpClient.addHook("beforeRequest", (request) => {
2143
- request.headers.set("x-workspace-id", workspaceId);
2210
+ for (const [name, value] of Object.entries(tenancyHeaders({ workspaceId })))
2211
+ request.headers.set(name, value);
2144
2212
  return request;
2145
2213
  });
2146
2214
  const sdk = new Mutagent2({ security: { apiKey }, serverURL: endpoint, httpClient, timeoutMs: Math.max(config.timeout, 120000), retryConfig: { strategy: "none" } });
@@ -18011,7 +18079,6 @@ async function connectAgentSpec() {
18011
18079
  const headers = tenancyHeaders({
18012
18080
  keyScope: resolved.keyScope,
18013
18081
  workspaceId: resolved.workspaceId,
18014
- workspaceOrigin: resolved.workspaceOrigin,
18015
18082
  organizationId: resolved.organization?.id
18016
18083
  });
18017
18084
  const httpClient = new HTTPClient3;
@@ -21219,7 +21286,6 @@ the answer is verified, not read from local config.
21219
21286
  const headers = tenancyHeaders({
21220
21287
  keyScope: tenancy.keyScope,
21221
21288
  workspaceId: tenancy.workspaceId,
21222
- workspaceOrigin: tenancy.workspaceOrigin,
21223
21289
  organizationId: tenancy.organization?.id
21224
21290
  });
21225
21291
  const self = await fetchKeySelf(apiKey, tenancy.endpoint, { headers, ctx: tenancy });
@@ -27422,7 +27488,6 @@ async function resolveConnection() {
27422
27488
  const headers = tenancyHeaders({
27423
27489
  keyScope: tenancy.keyScope,
27424
27490
  workspaceId,
27425
- workspaceOrigin: tenancy.workspaceOrigin,
27426
27491
  organizationId: tenancy.organization?.id
27427
27492
  });
27428
27493
  return {
@@ -31702,7 +31767,7 @@ async function resolveGatewayConnection() {
31702
31767
  const headers = {
31703
31768
  "x-api-key": apiKey,
31704
31769
  "Content-Type": "application/json",
31705
- "x-workspace-id": workspaceId
31770
+ ...tenancyHeaders({ workspaceId })
31706
31771
  };
31707
31772
  return {
31708
31773
  endpoint: tenancy.endpoint,
@@ -34782,7 +34847,6 @@ async function connectReports(opts) {
34782
34847
  const headers = tenancyHeaders({
34783
34848
  keyScope: tenancy.keyScope,
34784
34849
  workspaceId: tenancy.workspaceId,
34785
- workspaceOrigin: tenancy.workspaceOrigin,
34786
34850
  organizationId: tenancy.organization?.id
34787
34851
  });
34788
34852
  const httpClient = new HTTPClient5;
@@ -36555,7 +36619,6 @@ async function connectIntegrations() {
36555
36619
  const headers = tenancyHeaders({
36556
36620
  keyScope: tenancy.keyScope,
36557
36621
  workspaceId: tenancy.workspaceId,
36558
- workspaceOrigin: tenancy.workspaceOrigin,
36559
36622
  organizationId: tenancy.organization?.id
36560
36623
  });
36561
36624
  const tenancyCtx = {
@@ -38100,4 +38163,4 @@ if (process.env.CLI_VERSION) {
38100
38163
  }
38101
38164
  runCli(process.argv.slice(2), cliVersion);
38102
38165
 
38103
- //# debugId=7181C3F925DEA1DA64756E2164756E21
38166
+ //# debugId=F12EBD8D0A6ACFED64756E2164756E21
package/dist/index.js CHANGED
@@ -1051,11 +1051,6 @@ function tenancyHeaders(t) {
1051
1051
  const headers = {};
1052
1052
  if (!t.workspaceId)
1053
1053
  return headers;
1054
- if (t.keyScope === "workspace") {
1055
- if (t.workspaceOrigin === "override")
1056
- headers["x-workspace-id"] = t.workspaceId;
1057
- return headers;
1058
- }
1059
1054
  headers["x-workspace-id"] = t.workspaceId;
1060
1055
  if (t.keyScope === "user" && t.organizationId)
1061
1056
  headers["x-organization-id"] = t.organizationId;
@@ -1065,7 +1060,6 @@ function configTenancyHeaders(config) {
1065
1060
  return tenancyHeaders({
1066
1061
  keyScope: config.keyScope,
1067
1062
  workspaceId: config.defaultWorkspace,
1068
- workspaceOrigin: config.workspaceOrigin,
1069
1063
  organizationId: config.defaultOrganization
1070
1064
  });
1071
1065
  }
@@ -1202,6 +1196,17 @@ ${apiKey}`;
1202
1196
  probe.catch(() => keySelfProbes.delete(cacheKey));
1203
1197
  return probe;
1204
1198
  }
1199
+ function confirmKeyScope(apiKey, endpoint) {
1200
+ const cacheKey = `${endpoint}
1201
+ ${apiKey}`;
1202
+ const done = keyScopeConfirmations.get(cacheKey);
1203
+ if (done)
1204
+ return done;
1205
+ const read = readKeyScope(apiKey, endpoint, !envKeyReplacesFile());
1206
+ keyScopeConfirmations.set(cacheKey, read);
1207
+ keySelfProbes.set(cacheKey, read);
1208
+ return read;
1209
+ }
1205
1210
  async function readKeyScope(apiKey, endpoint, fromFile) {
1206
1211
  try {
1207
1212
  const self = await fetchKeySelf(apiKey, endpoint);
@@ -1269,7 +1274,14 @@ async function findWorkspace(wanted, t) {
1269
1274
  if (t.workspaceId && (wanted === t.workspaceId || wanted === t.workspaceName)) {
1270
1275
  return { id: t.workspaceId, name: t.workspaceName ?? t.workspaceId };
1271
1276
  }
1272
- 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)");
1277
+ const self = await confirmKeyScope(t.apiKey, t.endpoint);
1278
+ if (self && self.scope !== "workspace") {
1279
+ return findWorkspace(wanted, { ...t, keyScope: self.scope, organization: self.organization ?? t.organization });
1280
+ }
1281
+ const own = self?.workspace ?? (t.workspaceId ? { id: t.workspaceId, name: t.workspaceName ?? t.workspaceId } : undefined);
1282
+ if (own && (wanted === own.id || wanted === own.name))
1283
+ return { id: own.id, name: own.name };
1284
+ throw new TenancyError("WORKSPACE_KEY_MISMATCH", `This key only works in ${own?.name ?? "its own workspace"}.`, "Run: mutagent login (for a key that can switch workspaces)");
1273
1285
  }
1274
1286
  const rows = await listKeyWorkspaces(t.apiKey, t.endpoint, t);
1275
1287
  const byId = rows.find((w) => w.id === wanted);
@@ -1286,7 +1298,7 @@ async function findWorkspace(wanted, t) {
1286
1298
  const orgs = await fetchOrganizations(t.apiKey, t.endpoint);
1287
1299
  throw otherOrganizationHint(wanted, t.organization, orgs);
1288
1300
  }
1289
- var TENANCY_DENIED = "TENANCY_DENIED", UUID_PATTERN, TenancyError, ENV_KEY_WORKSPACE_FIX = "Pass --workspace <name|id> or set MUTAGENT_WORKSPACE_ID", orgAssertion, workspaceFlag, keySelfProbes;
1301
+ var TENANCY_DENIED = "TENANCY_DENIED", UUID_PATTERN, TenancyError, ENV_KEY_WORKSPACE_FIX = "Pass --workspace <name|id> or set MUTAGENT_WORKSPACE_ID", orgAssertion, workspaceFlag, keySelfProbes, keyScopeConfirmations;
1290
1302
  var init_organization_scope = __esm(() => {
1291
1303
  init_errors();
1292
1304
  init_config();
@@ -1298,6 +1310,7 @@ var init_organization_scope = __esm(() => {
1298
1310
  }
1299
1311
  };
1300
1312
  keySelfProbes = new Map;
1313
+ keyScopeConfirmations = new Map;
1301
1314
  });
1302
1315
 
1303
1316
  // src/lib/provider-request.ts
@@ -1417,6 +1430,46 @@ var init_sdk_debug = __esm(() => {
1417
1430
  };
1418
1431
  });
1419
1432
 
1433
+ // src/lib/key-scope-heal.ts
1434
+ function isOrgKeyWithoutWorkspace(status, body) {
1435
+ return status === 400 && body.toLowerCase().includes(ORG_KEY_NEEDS_WORKSPACE);
1436
+ }
1437
+ function sameHeaders(a, b) {
1438
+ const keys = Object.keys(a);
1439
+ return keys.length === Object.keys(b).length && keys.every((k) => a[k] === b[k]);
1440
+ }
1441
+ async function fetchWithScopeHeal(input, init, target) {
1442
+ const spare = input instanceof Request ? input.clone() : undefined;
1443
+ const response = await (init == null ? fetch(input) : fetch(input, init));
1444
+ if (response.status !== 400)
1445
+ return response;
1446
+ const body = await response.clone().text().catch(() => "");
1447
+ if (!isOrgKeyWithoutWorkspace(response.status, body))
1448
+ return response;
1449
+ const before = target.headers();
1450
+ const self = await confirmKeyScope(target.apiKey, target.endpoint).catch(() => {
1451
+ return;
1452
+ });
1453
+ if (!self)
1454
+ return response;
1455
+ target.adopt(self);
1456
+ const after = target.headers();
1457
+ if (sameHeaders(before, after))
1458
+ return response;
1459
+ if (spare) {
1460
+ const headers = new Headers(spare.headers);
1461
+ for (const [name, value] of Object.entries(after))
1462
+ headers.set(name, value);
1463
+ return fetch(new Request(spare, { headers }));
1464
+ }
1465
+ const headers = { ...init?.headers, ...after };
1466
+ return fetch(input, { ...init, headers });
1467
+ }
1468
+ var ORG_KEY_NEEDS_WORKSPACE = "x-workspace-id header required";
1469
+ var init_key_scope_heal = __esm(() => {
1470
+ init_organization_scope();
1471
+ });
1472
+
1420
1473
  // src/lib/sdk-mappers.ts
1421
1474
  import { LlmProviderType as SDKProviderEnum } from "@mutagent/sdk/models";
1422
1475
  function isProviderType(value) {
@@ -1527,7 +1580,7 @@ class SDKClientWrapper {
1527
1580
  workspaceOrigin: opts.workspaceOrigin ?? (opts.workspaceId ? "selection" : undefined),
1528
1581
  organizationName: opts.organizationName
1529
1582
  };
1530
- const httpClient = new HTTPClient;
1583
+ const httpClient = new HTTPClient({ fetcher: (input, init) => fetchWithScopeHeal(input, init, this.scopeHealTarget()) });
1531
1584
  httpClient.addHook("beforeRequest", (req) => {
1532
1585
  for (const [name, value] of Object.entries(this.tenancyHeaders()))
1533
1586
  req.headers.set(name, value);
@@ -1597,7 +1650,7 @@ class SDKClientWrapper {
1597
1650
  }
1598
1651
  let response;
1599
1652
  try {
1600
- response = await fetch(`${this.endpoint}${path}`, {
1653
+ response = await fetchWithScopeHeal(`${this.endpoint}${path}`, {
1601
1654
  ...options,
1602
1655
  headers: {
1603
1656
  "x-api-key": this.apiKey,
@@ -1605,7 +1658,7 @@ class SDKClientWrapper {
1605
1658
  ...this.tenancyHeaders(),
1606
1659
  ...extraHeaders
1607
1660
  }
1608
- });
1661
+ }, this.scopeHealTarget());
1609
1662
  } catch {
1610
1663
  throw unreachableServerError(this.endpoint);
1611
1664
  }
@@ -1779,10 +1832,23 @@ class SDKClientWrapper {
1779
1832
  return tenancyHeaders({
1780
1833
  keyScope: this.tenancy.keyScope,
1781
1834
  workspaceId: this.workspaceId,
1782
- workspaceOrigin: this.tenancy.workspaceOrigin,
1783
1835
  organizationId: this.organizationId
1784
1836
  });
1785
1837
  }
1838
+ scopeHealTarget() {
1839
+ return {
1840
+ apiKey: this.apiKey,
1841
+ endpoint: this.endpoint,
1842
+ headers: () => this.tenancyHeaders(),
1843
+ adopt: (self) => {
1844
+ this.tenancy.keyScope = self.scope;
1845
+ if (!this.workspaceId && self.scope === "workspace" && self.workspace)
1846
+ this.workspaceId = self.workspace.id;
1847
+ if (self.scope === "user" && self.organization)
1848
+ this.organizationId ??= self.organization.id;
1849
+ }
1850
+ };
1851
+ }
1786
1852
  requireSelectedWorkspace() {
1787
1853
  if (!this.workspaceId)
1788
1854
  throw noWorkspaceSelected();
@@ -1897,6 +1963,7 @@ var init_sdk_client = __esm(() => {
1897
1963
  init_key_check();
1898
1964
  init_organization_scope();
1899
1965
  init_sdk_debug();
1966
+ init_key_scope_heal();
1900
1967
  init_known_secrets();
1901
1968
  init_sdk_mappers();
1902
1969
  init_organization_scope();
@@ -1922,4 +1989,4 @@ export {
1922
1989
  version
1923
1990
  };
1924
1991
 
1925
- //# debugId=4CBE15031C5FFF4664756E2164756E21
1992
+ //# debugId=CB6D96307760100964756E2164756E21
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mutagent/cli",
3
- "version": "0.2.52",
3
+ "version": "0.2.53",
4
4
  "description": "Bun-native CLI for the MutagenT AI platform - auth, provider config, and lifecycle-tool installer for AI-native workflows",
5
5
  "type": "module",
6
6
  "bin": {