@mutagent/cli 0.1.359 → 0.1.361
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 +237 -118
- package/dist/index.js +293 -186
- package/package.json +2 -2
package/dist/bin/cli.js
CHANGED
|
@@ -16519,6 +16519,187 @@ function writeSecureFile(path, contents) {
|
|
|
16519
16519
|
var SECURE_FILE_MODE = 384;
|
|
16520
16520
|
var init_secure_file = () => {};
|
|
16521
16521
|
|
|
16522
|
+
// src/lib/sdk-debug.ts
|
|
16523
|
+
function isDebugEnabled(env = process.env) {
|
|
16524
|
+
return TRUTHY.has((env.MUTAGENT_DEBUG ?? "").trim().toLowerCase());
|
|
16525
|
+
}
|
|
16526
|
+
function redactValue(value2, depth = 0) {
|
|
16527
|
+
if (depth > 32)
|
|
16528
|
+
return REDACTED;
|
|
16529
|
+
if (Array.isArray(value2))
|
|
16530
|
+
return value2.map((item) => redactValue(item, depth + 1));
|
|
16531
|
+
if (value2 === null || typeof value2 !== "object")
|
|
16532
|
+
return value2;
|
|
16533
|
+
const out = {};
|
|
16534
|
+
for (const [name, field] of Object.entries(value2)) {
|
|
16535
|
+
if (/^credentialfields$/i.test(name) && field !== null && typeof field === "object") {
|
|
16536
|
+
out[name] = Object.fromEntries(Object.keys(field).map((key) => [key, REDACTED]));
|
|
16537
|
+
} else if (isSensitiveFieldName(name) && field !== null && field !== undefined) {
|
|
16538
|
+
out[name] = REDACTED;
|
|
16539
|
+
} else {
|
|
16540
|
+
out[name] = redactValue(field, depth + 1);
|
|
16541
|
+
}
|
|
16542
|
+
}
|
|
16543
|
+
return out;
|
|
16544
|
+
}
|
|
16545
|
+
function redactText(text, secrets) {
|
|
16546
|
+
const header = HEADER_LINE.exec(text);
|
|
16547
|
+
let out = header?.[1] && SENSITIVE_HEADER.test(header[1]) ? `${header[1]}: ${REDACTED}` : text;
|
|
16548
|
+
out = out.replace(JSON_STRING_FIELD, (match, name, sep5) => isSensitiveFieldName(name) ? `"${name}"${sep5}"${REDACTED}"` : match);
|
|
16549
|
+
out = out.replace(AUTH_SCHEME, `$1 ${REDACTED}`);
|
|
16550
|
+
return scrubSecrets(out, secrets);
|
|
16551
|
+
}
|
|
16552
|
+
function format(arg, secrets) {
|
|
16553
|
+
if (typeof arg === "string")
|
|
16554
|
+
return redactText(arg, secrets);
|
|
16555
|
+
if (arg !== null && typeof arg === "object") {
|
|
16556
|
+
let json;
|
|
16557
|
+
try {
|
|
16558
|
+
json = JSON.stringify(redactValue(arg), null, 2);
|
|
16559
|
+
} catch {
|
|
16560
|
+
json = undefined;
|
|
16561
|
+
}
|
|
16562
|
+
return redactText(json ?? "<unserialisable>", secrets);
|
|
16563
|
+
}
|
|
16564
|
+
return redactText(String(arg), secrets);
|
|
16565
|
+
}
|
|
16566
|
+
function createSdkDebugLogger(opts = {}) {
|
|
16567
|
+
if (!isDebugEnabled(opts.env ?? process.env))
|
|
16568
|
+
return SILENT;
|
|
16569
|
+
const write = opts.write ?? ((chunk) => {
|
|
16570
|
+
process.stderr.write(chunk);
|
|
16571
|
+
});
|
|
16572
|
+
const secrets = opts.secrets ?? (() => []);
|
|
16573
|
+
let depth = 0;
|
|
16574
|
+
const emit = (text) => {
|
|
16575
|
+
const pad = " ".repeat(depth);
|
|
16576
|
+
write(`${text.split(`
|
|
16577
|
+
`).map((line) => pad + line).join(`
|
|
16578
|
+
`)}
|
|
16579
|
+
`);
|
|
16580
|
+
};
|
|
16581
|
+
return {
|
|
16582
|
+
group(label) {
|
|
16583
|
+
if (label !== undefined)
|
|
16584
|
+
emit(redactText(label, secrets()));
|
|
16585
|
+
depth += 1;
|
|
16586
|
+
},
|
|
16587
|
+
groupEnd() {
|
|
16588
|
+
depth = Math.max(0, depth - 1);
|
|
16589
|
+
},
|
|
16590
|
+
log(message, ...args) {
|
|
16591
|
+
emit([message, ...args].map((arg) => format(arg, secrets())).join(" "));
|
|
16592
|
+
}
|
|
16593
|
+
};
|
|
16594
|
+
}
|
|
16595
|
+
var TRUTHY, SENSITIVE_HEADER, HEADER_LINE, JSON_STRING_FIELD, AUTH_SCHEME, SILENT;
|
|
16596
|
+
var init_sdk_debug = __esm(() => {
|
|
16597
|
+
init_secret_scrub();
|
|
16598
|
+
TRUTHY = new Set(["1", "true", "yes"]);
|
|
16599
|
+
SENSITIVE_HEADER = /^(x-api-key|authorization|proxy-authorization|cookie|set-cookie|x-[\w-]*(?:key|token|secret|auth)[\w-]*)$/i;
|
|
16600
|
+
HEADER_LINE = /^([A-Za-z0-9-]+):\s(.*)$/s;
|
|
16601
|
+
JSON_STRING_FIELD = /"((?:[^"\\]|\\.)*)"(\s*:\s*)"(?:[^"\\]|\\.)*"/g;
|
|
16602
|
+
AUTH_SCHEME = /\b(Bearer|Basic)\s+[A-Za-z0-9._~+/=-]+/gi;
|
|
16603
|
+
SILENT = {
|
|
16604
|
+
group: () => {
|
|
16605
|
+
return;
|
|
16606
|
+
},
|
|
16607
|
+
groupEnd: () => {
|
|
16608
|
+
return;
|
|
16609
|
+
},
|
|
16610
|
+
log: () => {
|
|
16611
|
+
return;
|
|
16612
|
+
}
|
|
16613
|
+
};
|
|
16614
|
+
});
|
|
16615
|
+
|
|
16616
|
+
// src/lib/organization-scope.ts
|
|
16617
|
+
function isWellFormedOrganizationId(id) {
|
|
16618
|
+
return ORGANIZATION_ID_PATTERN.test(id);
|
|
16619
|
+
}
|
|
16620
|
+
function isOrganizationScopeMismatch(status, body) {
|
|
16621
|
+
if (status !== 403)
|
|
16622
|
+
return false;
|
|
16623
|
+
return body.toLowerCase().includes("organization scope mismatch");
|
|
16624
|
+
}
|
|
16625
|
+
async function lookupOrganizations(apiKey, endpoint) {
|
|
16626
|
+
try {
|
|
16627
|
+
const response = await fetch(`${endpoint}/api/organizations`, {
|
|
16628
|
+
headers: { "x-api-key": apiKey }
|
|
16629
|
+
});
|
|
16630
|
+
if (!response.ok)
|
|
16631
|
+
return [];
|
|
16632
|
+
const data = await response.json();
|
|
16633
|
+
return data.data ?? [];
|
|
16634
|
+
} catch {
|
|
16635
|
+
return [];
|
|
16636
|
+
}
|
|
16637
|
+
}
|
|
16638
|
+
async function fetchOrganizations(apiKey, endpoint) {
|
|
16639
|
+
return lookupOrganizations(apiKey, endpoint);
|
|
16640
|
+
}
|
|
16641
|
+
async function assertUsableOrganizationId(id, lookup) {
|
|
16642
|
+
const orgs = lookup.apiKey ? await lookup.fetchOrganizations(lookup.apiKey, lookup.endpoint).catch(() => []) : [];
|
|
16643
|
+
if (orgs.length === 0) {
|
|
16644
|
+
if (isWellFormedOrganizationId(id))
|
|
16645
|
+
return;
|
|
16646
|
+
throw new MutagentError("INVALID_ORGANIZATION_ID", `"${id}" is not an organization id.`, `Organization ids look like org_xxxxxxxxxxxxxxxxxxxxx.
|
|
16647
|
+
` + "Log in so the id can be checked against your account: mutagent auth login --browser");
|
|
16648
|
+
}
|
|
16649
|
+
if (orgs.some((org) => org.id === id))
|
|
16650
|
+
return;
|
|
16651
|
+
throw new MutagentError("UNKNOWN_ORGANIZATION", `"${id}" is not an organization this API key can access.`, `Available: ${orgs.map((org) => org.id).join(", ")}
|
|
16652
|
+
` + "A stored organization that does not match the key makes every request fail with 403.");
|
|
16653
|
+
}
|
|
16654
|
+
function createOrgScopeRecoveringFetcher(hooks) {
|
|
16655
|
+
let recoveryUsed = false;
|
|
16656
|
+
async function recover() {
|
|
16657
|
+
if (recoveryUsed)
|
|
16658
|
+
return;
|
|
16659
|
+
recoveryUsed = true;
|
|
16660
|
+
const stale = hooks.currentOrganization();
|
|
16661
|
+
if (!stale)
|
|
16662
|
+
return;
|
|
16663
|
+
hooks.adoptOrganization(undefined);
|
|
16664
|
+
hooks.forgetStoredOrganization?.();
|
|
16665
|
+
const orgs = await lookupOrganizations(hooks.apiKey, hooks.endpoint);
|
|
16666
|
+
const resolved = orgs[0]?.id;
|
|
16667
|
+
if (!resolved || resolved === stale)
|
|
16668
|
+
return;
|
|
16669
|
+
hooks.adoptOrganization(resolved);
|
|
16670
|
+
if (isDebugEnabled()) {
|
|
16671
|
+
process.stderr.write(`[mutagent] Stored organization was rejected by the API; re-resolved and retried once.
|
|
16672
|
+
`);
|
|
16673
|
+
}
|
|
16674
|
+
return resolved;
|
|
16675
|
+
}
|
|
16676
|
+
return async function fetchWithOrgRecovery(input, init) {
|
|
16677
|
+
const retryTemplate = input instanceof Request ? input.clone() : undefined;
|
|
16678
|
+
const response = init == null ? await fetch(input) : await fetch(input, init);
|
|
16679
|
+
if (response.status !== 403)
|
|
16680
|
+
return response;
|
|
16681
|
+
const body = await response.clone().text().catch(() => "");
|
|
16682
|
+
if (!isOrganizationScopeMismatch(response.status, body))
|
|
16683
|
+
return response;
|
|
16684
|
+
const resolved = await recover();
|
|
16685
|
+
if (!resolved)
|
|
16686
|
+
return response;
|
|
16687
|
+
if (retryTemplate) {
|
|
16688
|
+
retryTemplate.headers.set("x-organization-id", resolved);
|
|
16689
|
+
return fetch(retryTemplate);
|
|
16690
|
+
}
|
|
16691
|
+
const headers = new Headers(init?.headers);
|
|
16692
|
+
headers.set("x-organization-id", resolved);
|
|
16693
|
+
return fetch(input, { ...init, headers });
|
|
16694
|
+
};
|
|
16695
|
+
}
|
|
16696
|
+
var ORGANIZATION_ID_PATTERN;
|
|
16697
|
+
var init_organization_scope = __esm(() => {
|
|
16698
|
+
init_errors();
|
|
16699
|
+
init_sdk_debug();
|
|
16700
|
+
ORGANIZATION_ID_PATTERN = /^org_[A-Za-z0-9_-]{8,}$/;
|
|
16701
|
+
});
|
|
16702
|
+
|
|
16522
16703
|
// src/lib/config.ts
|
|
16523
16704
|
import { cosmiconfigSync } from "cosmiconfig";
|
|
16524
16705
|
import { z } from "zod";
|
|
@@ -16625,6 +16806,10 @@ function setDefaultWorkspace(workspaceId, source2 = "user") {
|
|
|
16625
16806
|
writeSecureFile(CREDENTIALS_FILE, JSON.stringify(updated, null, 2));
|
|
16626
16807
|
}
|
|
16627
16808
|
function setDefaultOrganization(organizationId) {
|
|
16809
|
+
if (!isWellFormedOrganizationId(organizationId)) {
|
|
16810
|
+
throw new MutagentError("INVALID_ORGANIZATION_ID", `"${organizationId}" is not an organization id.`, `Organization ids look like org_xxxxxxxxxxxxxxxxxxxxx.
|
|
16811
|
+
` + "List the ones you can use with: mutagent auth status");
|
|
16812
|
+
}
|
|
16628
16813
|
if (!existsSync2(CREDENTIALS_DIR)) {
|
|
16629
16814
|
mkdirSync(CREDENTIALS_DIR, { recursive: true });
|
|
16630
16815
|
}
|
|
@@ -16640,10 +16825,26 @@ function setDefaultOrganization(organizationId) {
|
|
|
16640
16825
|
};
|
|
16641
16826
|
writeSecureFile(CREDENTIALS_FILE, JSON.stringify(updated, null, 2));
|
|
16642
16827
|
}
|
|
16828
|
+
function clearDefaultOrganization() {
|
|
16829
|
+
if (!existsSync2(CREDENTIALS_FILE))
|
|
16830
|
+
return;
|
|
16831
|
+
let existingCredentials;
|
|
16832
|
+
try {
|
|
16833
|
+
existingCredentials = JSON.parse(readFileSync2(CREDENTIALS_FILE, "utf-8"));
|
|
16834
|
+
} catch {
|
|
16835
|
+
return;
|
|
16836
|
+
}
|
|
16837
|
+
if (!("defaultOrganization" in existingCredentials))
|
|
16838
|
+
return;
|
|
16839
|
+
delete existingCredentials.defaultOrganization;
|
|
16840
|
+
writeSecureFile(CREDENTIALS_FILE, JSON.stringify(existingCredentials, null, 2));
|
|
16841
|
+
}
|
|
16643
16842
|
var configSchema, credentialsSchema, CREDENTIALS_DIR, CREDENTIALS_FILE;
|
|
16644
16843
|
var init_config = __esm(() => {
|
|
16645
16844
|
init_secure_file();
|
|
16646
16845
|
init_known_secrets();
|
|
16846
|
+
init_organization_scope();
|
|
16847
|
+
init_errors();
|
|
16647
16848
|
configSchema = z.object({
|
|
16648
16849
|
apiKey: z.string().optional(),
|
|
16649
16850
|
endpoint: z.string().default("https://api.mutagent.io"),
|
|
@@ -16688,100 +16889,6 @@ function buildCreateProviderBody(data) {
|
|
|
16688
16889
|
return body;
|
|
16689
16890
|
}
|
|
16690
16891
|
|
|
16691
|
-
// src/lib/sdk-debug.ts
|
|
16692
|
-
function isDebugEnabled(env = process.env) {
|
|
16693
|
-
return TRUTHY.has((env.MUTAGENT_DEBUG ?? "").trim().toLowerCase());
|
|
16694
|
-
}
|
|
16695
|
-
function redactValue(value2, depth = 0) {
|
|
16696
|
-
if (depth > 32)
|
|
16697
|
-
return REDACTED;
|
|
16698
|
-
if (Array.isArray(value2))
|
|
16699
|
-
return value2.map((item) => redactValue(item, depth + 1));
|
|
16700
|
-
if (value2 === null || typeof value2 !== "object")
|
|
16701
|
-
return value2;
|
|
16702
|
-
const out = {};
|
|
16703
|
-
for (const [name, field] of Object.entries(value2)) {
|
|
16704
|
-
if (/^credentialfields$/i.test(name) && field !== null && typeof field === "object") {
|
|
16705
|
-
out[name] = Object.fromEntries(Object.keys(field).map((key) => [key, REDACTED]));
|
|
16706
|
-
} else if (isSensitiveFieldName(name) && field !== null && field !== undefined) {
|
|
16707
|
-
out[name] = REDACTED;
|
|
16708
|
-
} else {
|
|
16709
|
-
out[name] = redactValue(field, depth + 1);
|
|
16710
|
-
}
|
|
16711
|
-
}
|
|
16712
|
-
return out;
|
|
16713
|
-
}
|
|
16714
|
-
function redactText(text, secrets) {
|
|
16715
|
-
const header = HEADER_LINE.exec(text);
|
|
16716
|
-
let out = header?.[1] && SENSITIVE_HEADER.test(header[1]) ? `${header[1]}: ${REDACTED}` : text;
|
|
16717
|
-
out = out.replace(JSON_STRING_FIELD, (match, name, sep5) => isSensitiveFieldName(name) ? `"${name}"${sep5}"${REDACTED}"` : match);
|
|
16718
|
-
out = out.replace(AUTH_SCHEME, `$1 ${REDACTED}`);
|
|
16719
|
-
return scrubSecrets(out, secrets);
|
|
16720
|
-
}
|
|
16721
|
-
function format(arg, secrets) {
|
|
16722
|
-
if (typeof arg === "string")
|
|
16723
|
-
return redactText(arg, secrets);
|
|
16724
|
-
if (arg !== null && typeof arg === "object") {
|
|
16725
|
-
let json;
|
|
16726
|
-
try {
|
|
16727
|
-
json = JSON.stringify(redactValue(arg), null, 2);
|
|
16728
|
-
} catch {
|
|
16729
|
-
json = undefined;
|
|
16730
|
-
}
|
|
16731
|
-
return redactText(json ?? "<unserialisable>", secrets);
|
|
16732
|
-
}
|
|
16733
|
-
return redactText(String(arg), secrets);
|
|
16734
|
-
}
|
|
16735
|
-
function createSdkDebugLogger(opts = {}) {
|
|
16736
|
-
if (!isDebugEnabled(opts.env ?? process.env))
|
|
16737
|
-
return SILENT;
|
|
16738
|
-
const write = opts.write ?? ((chunk) => {
|
|
16739
|
-
process.stderr.write(chunk);
|
|
16740
|
-
});
|
|
16741
|
-
const secrets = opts.secrets ?? (() => []);
|
|
16742
|
-
let depth = 0;
|
|
16743
|
-
const emit = (text) => {
|
|
16744
|
-
const pad = " ".repeat(depth);
|
|
16745
|
-
write(`${text.split(`
|
|
16746
|
-
`).map((line) => pad + line).join(`
|
|
16747
|
-
`)}
|
|
16748
|
-
`);
|
|
16749
|
-
};
|
|
16750
|
-
return {
|
|
16751
|
-
group(label) {
|
|
16752
|
-
if (label !== undefined)
|
|
16753
|
-
emit(redactText(label, secrets()));
|
|
16754
|
-
depth += 1;
|
|
16755
|
-
},
|
|
16756
|
-
groupEnd() {
|
|
16757
|
-
depth = Math.max(0, depth - 1);
|
|
16758
|
-
},
|
|
16759
|
-
log(message, ...args) {
|
|
16760
|
-
emit([message, ...args].map((arg) => format(arg, secrets())).join(" "));
|
|
16761
|
-
}
|
|
16762
|
-
};
|
|
16763
|
-
}
|
|
16764
|
-
var TRUTHY, SENSITIVE_HEADER, HEADER_LINE, JSON_STRING_FIELD, AUTH_SCHEME, SILENT;
|
|
16765
|
-
var init_sdk_debug = __esm(() => {
|
|
16766
|
-
init_secret_scrub();
|
|
16767
|
-
TRUTHY = new Set(["1", "true", "yes"]);
|
|
16768
|
-
SENSITIVE_HEADER = /^(x-api-key|authorization|proxy-authorization|cookie|set-cookie|x-[\w-]*(?:key|token|secret|auth)[\w-]*)$/i;
|
|
16769
|
-
HEADER_LINE = /^([A-Za-z0-9-]+):\s(.*)$/s;
|
|
16770
|
-
JSON_STRING_FIELD = /"((?:[^"\\]|\\.)*)"(\s*:\s*)"(?:[^"\\]|\\.)*"/g;
|
|
16771
|
-
AUTH_SCHEME = /\b(Bearer|Basic)\s+[A-Za-z0-9._~+/=-]+/gi;
|
|
16772
|
-
SILENT = {
|
|
16773
|
-
group: () => {
|
|
16774
|
-
return;
|
|
16775
|
-
},
|
|
16776
|
-
groupEnd: () => {
|
|
16777
|
-
return;
|
|
16778
|
-
},
|
|
16779
|
-
log: () => {
|
|
16780
|
-
return;
|
|
16781
|
-
}
|
|
16782
|
-
};
|
|
16783
|
-
});
|
|
16784
|
-
|
|
16785
16892
|
// src/lib/sdk-mappers.ts
|
|
16786
16893
|
import { LlmProviderType as SDKProviderEnum } from "@mutagent/sdk/models";
|
|
16787
16894
|
function isProviderType(value2) {
|
|
@@ -16889,6 +16996,7 @@ class SDKClientWrapper {
|
|
|
16889
16996
|
organizationId;
|
|
16890
16997
|
workspaceResolution = "none";
|
|
16891
16998
|
debugSecrets = new Set;
|
|
16999
|
+
recoveringFetch;
|
|
16892
17000
|
constructor(opts) {
|
|
16893
17001
|
this.apiKey = opts.apiKey;
|
|
16894
17002
|
this.endpoint = opts.serverURL ?? "http://localhost:3003";
|
|
@@ -16897,7 +17005,16 @@ class SDKClientWrapper {
|
|
|
16897
17005
|
if (opts.workspaceId && opts.workspaceSource === "user") {
|
|
16898
17006
|
this.workspaceResolution = "configured";
|
|
16899
17007
|
}
|
|
16900
|
-
|
|
17008
|
+
this.recoveringFetch = createOrgScopeRecoveringFetcher({
|
|
17009
|
+
apiKey: this.apiKey,
|
|
17010
|
+
endpoint: this.endpoint,
|
|
17011
|
+
currentOrganization: () => this.organizationId,
|
|
17012
|
+
adoptOrganization: (id) => {
|
|
17013
|
+
this.organizationId = id;
|
|
17014
|
+
},
|
|
17015
|
+
forgetStoredOrganization: opts.clearStoredOrganization
|
|
17016
|
+
});
|
|
17017
|
+
const httpClient = new HTTPClient({ fetcher: this.recoveringFetch });
|
|
16901
17018
|
httpClient.addHook("beforeRequest", (req) => {
|
|
16902
17019
|
if (this.workspaceId)
|
|
16903
17020
|
req.headers.set("x-workspace-id", this.workspaceId);
|
|
@@ -16962,7 +17079,7 @@ class SDKClientWrapper {
|
|
|
16962
17079
|
tenancyHeaders["x-workspace-id"] = this.workspaceId;
|
|
16963
17080
|
if (this.organizationId)
|
|
16964
17081
|
tenancyHeaders["x-organization-id"] = this.organizationId;
|
|
16965
|
-
const response = await
|
|
17082
|
+
const response = await this.recoveringFetch(`${this.endpoint}${path}`, {
|
|
16966
17083
|
...options,
|
|
16967
17084
|
headers: {
|
|
16968
17085
|
"x-api-key": this.apiKey,
|
|
@@ -17274,7 +17391,8 @@ async function getSDKClient() {
|
|
|
17274
17391
|
serverURL: config.endpoint,
|
|
17275
17392
|
workspaceId: config.defaultWorkspace,
|
|
17276
17393
|
organizationId: config.defaultOrganization,
|
|
17277
|
-
workspaceSource: config.defaultWorkspaceSource
|
|
17394
|
+
workspaceSource: config.defaultWorkspaceSource,
|
|
17395
|
+
clearStoredOrganization: clearDefaultOrganization
|
|
17278
17396
|
});
|
|
17279
17397
|
await sdkClient.ensureContext().catch((err) => {
|
|
17280
17398
|
if (isDebugEnabled()) {
|
|
@@ -17299,19 +17417,6 @@ async function validateApiKey(apiKey, endpoint) {
|
|
|
17299
17417
|
return false;
|
|
17300
17418
|
}
|
|
17301
17419
|
}
|
|
17302
|
-
async function fetchOrganizations(apiKey, endpoint) {
|
|
17303
|
-
try {
|
|
17304
|
-
const response = await fetch(`${endpoint}/api/organizations`, {
|
|
17305
|
-
headers: { "x-api-key": apiKey }
|
|
17306
|
-
});
|
|
17307
|
-
if (!response.ok)
|
|
17308
|
-
return [];
|
|
17309
|
-
const data = await response.json();
|
|
17310
|
-
return data.data ?? [];
|
|
17311
|
-
} catch {
|
|
17312
|
-
return [];
|
|
17313
|
-
}
|
|
17314
|
-
}
|
|
17315
17420
|
async function fetchWorkspaces(apiKey, endpoint, orgId) {
|
|
17316
17421
|
try {
|
|
17317
17422
|
const response = await fetch(`${endpoint}/api/workspaces`, {
|
|
@@ -17329,9 +17434,11 @@ var sdkClient = null;
|
|
|
17329
17434
|
var init_sdk_client = __esm(() => {
|
|
17330
17435
|
init_errors();
|
|
17331
17436
|
init_config();
|
|
17437
|
+
init_organization_scope();
|
|
17332
17438
|
init_sdk_debug();
|
|
17333
17439
|
init_known_secrets();
|
|
17334
17440
|
init_sdk_mappers();
|
|
17441
|
+
init_organization_scope();
|
|
17335
17442
|
});
|
|
17336
17443
|
|
|
17337
17444
|
// src/lib/agent/sdk.ts
|
|
@@ -19047,6 +19154,7 @@ import { Command as Command4 } from "commander";
|
|
|
19047
19154
|
import chalk6 from "chalk";
|
|
19048
19155
|
init_errors();
|
|
19049
19156
|
init_sdk_client();
|
|
19157
|
+
init_organization_scope();
|
|
19050
19158
|
var VALID_CONFIG_KEYS = ["apiKey", "endpoint", "format", "timeout", "defaultWorkspace", "defaultOrganization"];
|
|
19051
19159
|
function createConfigCommand() {
|
|
19052
19160
|
const config = new Command4("config").description("Manage CLI configuration").addHelpText("after", `
|
|
@@ -19123,15 +19231,26 @@ Examples:
|
|
|
19123
19231
|
${chalk6.dim("$")} mutagent config set org <org-id>
|
|
19124
19232
|
|
|
19125
19233
|
${chalk6.dim("Persists organization ID for org-scoped API keys.")}
|
|
19126
|
-
|
|
19234
|
+
${chalk6.dim("The ID is checked against your account first — a stored organization the")}
|
|
19235
|
+
${chalk6.dim("API key does not own makes every later command fail with 403.")}
|
|
19236
|
+
`).action(async (id) => {
|
|
19127
19237
|
const isJson = getJsonFlag(config);
|
|
19128
19238
|
const output = new OutputFormatter(isJson ? "json" : "table");
|
|
19129
|
-
|
|
19130
|
-
|
|
19131
|
-
|
|
19132
|
-
|
|
19133
|
-
|
|
19134
|
-
|
|
19239
|
+
try {
|
|
19240
|
+
await assertUsableOrganizationId(id, {
|
|
19241
|
+
apiKey: getApiKey(),
|
|
19242
|
+
endpoint: loadConfig().endpoint ?? "https://api.mutagent.io",
|
|
19243
|
+
fetchOrganizations
|
|
19244
|
+
});
|
|
19245
|
+
setDefaultOrganization(id);
|
|
19246
|
+
resetSDKClient();
|
|
19247
|
+
if (isJson) {
|
|
19248
|
+
output.output({ success: true, defaultOrganization: id });
|
|
19249
|
+
} else {
|
|
19250
|
+
output.success(`Default organization set to ${id}`);
|
|
19251
|
+
}
|
|
19252
|
+
} catch (error3) {
|
|
19253
|
+
handleError(error3, isJson);
|
|
19135
19254
|
}
|
|
19136
19255
|
});
|
|
19137
19256
|
config.addCommand(set2);
|
|
@@ -29237,4 +29356,4 @@ if (isAgentInvocation(rawArgs)) {
|
|
|
29237
29356
|
program.parse(argvForCommander, { from: "user" });
|
|
29238
29357
|
}
|
|
29239
29358
|
|
|
29240
|
-
//# debugId=
|
|
29359
|
+
//# debugId=76A898BDAFE9975F64756E2164756E21
|
package/dist/index.js
CHANGED
|
@@ -255,152 +255,6 @@ 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 merged = {
|
|
281
|
-
providers: {},
|
|
282
|
-
environment: "production",
|
|
283
|
-
...rcConfig,
|
|
284
|
-
...credentials,
|
|
285
|
-
apiKey: process.env.MUTAGENT_API_KEY ?? credentials?.apiKey ?? rcConfig?.apiKey,
|
|
286
|
-
endpoint: process.env.MUTAGENT_ENDPOINT ?? credentials?.endpoint ?? rcConfig?.endpoint,
|
|
287
|
-
defaultWorkspace: process.env.MUTAGENT_WORKSPACE_ID ?? credentials?.defaultWorkspace ?? rcConfig?.defaultWorkspace,
|
|
288
|
-
defaultWorkspaceSource: process.env.MUTAGENT_WORKSPACE_ID ? "user" : credentials?.defaultWorkspace ? credentials.defaultWorkspaceSource ?? "login-inferred" : rcConfig?.defaultWorkspace ? "login-inferred" : undefined,
|
|
289
|
-
defaultOrganization: credentials?.defaultOrganization ?? rcConfig?.defaultOrganization
|
|
290
|
-
};
|
|
291
|
-
return configSchema.parse(merged);
|
|
292
|
-
}
|
|
293
|
-
function saveCredentials(apiKey, endpoint) {
|
|
294
|
-
if (!existsSync(CREDENTIALS_DIR)) {
|
|
295
|
-
mkdirSync(CREDENTIALS_DIR, { recursive: true });
|
|
296
|
-
}
|
|
297
|
-
const config = loadConfig();
|
|
298
|
-
const credentials = {
|
|
299
|
-
apiKey,
|
|
300
|
-
endpoint: endpoint ?? config.endpoint
|
|
301
|
-
};
|
|
302
|
-
writeSecureFile(CREDENTIALS_FILE, JSON.stringify(credentials, null, 2));
|
|
303
|
-
}
|
|
304
|
-
function saveFullCredentials(creds) {
|
|
305
|
-
if (!existsSync(CREDENTIALS_DIR)) {
|
|
306
|
-
mkdirSync(CREDENTIALS_DIR, { recursive: true });
|
|
307
|
-
}
|
|
308
|
-
let existingCredentials = {};
|
|
309
|
-
if (existsSync(CREDENTIALS_FILE)) {
|
|
310
|
-
try {
|
|
311
|
-
existingCredentials = JSON.parse(readFileSync(CREDENTIALS_FILE, "utf-8"));
|
|
312
|
-
} catch {}
|
|
313
|
-
}
|
|
314
|
-
const existingSource = existingCredentials.defaultWorkspaceSource;
|
|
315
|
-
const workspaceChanged = creds.workspaceId !== undefined && creds.workspaceId !== existingCredentials.defaultWorkspace;
|
|
316
|
-
const source = creds.workspaceId === undefined ? existingSource : workspaceChanged ? "login-inferred" : existingSource ?? "login-inferred";
|
|
317
|
-
const credentials = {
|
|
318
|
-
...existingCredentials,
|
|
319
|
-
apiKey: creds.apiKey,
|
|
320
|
-
endpoint: creds.endpoint ?? existingCredentials.endpoint ?? "https://api.mutagent.io",
|
|
321
|
-
defaultWorkspace: creds.workspaceId ?? existingCredentials.defaultWorkspace,
|
|
322
|
-
defaultWorkspaceSource: source,
|
|
323
|
-
defaultOrganization: creds.organizationId ?? existingCredentials.defaultOrganization,
|
|
324
|
-
expiresAt: creds.expiresAt
|
|
325
|
-
};
|
|
326
|
-
writeSecureFile(CREDENTIALS_FILE, JSON.stringify(credentials, null, 2));
|
|
327
|
-
}
|
|
328
|
-
function clearCredentials() {
|
|
329
|
-
if (existsSync(CREDENTIALS_FILE)) {
|
|
330
|
-
writeSecureFile(CREDENTIALS_FILE, JSON.stringify({}));
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
function hasCredentials() {
|
|
334
|
-
if (process.env.MUTAGENT_API_KEY)
|
|
335
|
-
return true;
|
|
336
|
-
if (!existsSync(CREDENTIALS_FILE))
|
|
337
|
-
return false;
|
|
338
|
-
const credentials = parseJsonSafe(readFileSync(CREDENTIALS_FILE, "utf-8"), credentialsSchema);
|
|
339
|
-
return credentials?.apiKey !== undefined && credentials.apiKey !== "";
|
|
340
|
-
}
|
|
341
|
-
function getApiKey() {
|
|
342
|
-
const apiKey = process.env.MUTAGENT_API_KEY ?? loadConfig().apiKey;
|
|
343
|
-
registerSecret(apiKey);
|
|
344
|
-
return apiKey;
|
|
345
|
-
}
|
|
346
|
-
function setDefaultWorkspace(workspaceId, source = "user") {
|
|
347
|
-
if (!existsSync(CREDENTIALS_DIR)) {
|
|
348
|
-
mkdirSync(CREDENTIALS_DIR, { recursive: true });
|
|
349
|
-
}
|
|
350
|
-
let existingCredentials = {};
|
|
351
|
-
if (existsSync(CREDENTIALS_FILE)) {
|
|
352
|
-
try {
|
|
353
|
-
existingCredentials = JSON.parse(readFileSync(CREDENTIALS_FILE, "utf-8"));
|
|
354
|
-
} catch {}
|
|
355
|
-
}
|
|
356
|
-
const updated = {
|
|
357
|
-
...existingCredentials,
|
|
358
|
-
defaultWorkspace: workspaceId,
|
|
359
|
-
defaultWorkspaceSource: source
|
|
360
|
-
};
|
|
361
|
-
writeSecureFile(CREDENTIALS_FILE, JSON.stringify(updated, null, 2));
|
|
362
|
-
}
|
|
363
|
-
function setDefaultOrganization(organizationId) {
|
|
364
|
-
if (!existsSync(CREDENTIALS_DIR)) {
|
|
365
|
-
mkdirSync(CREDENTIALS_DIR, { recursive: true });
|
|
366
|
-
}
|
|
367
|
-
let existingCredentials = {};
|
|
368
|
-
if (existsSync(CREDENTIALS_FILE)) {
|
|
369
|
-
try {
|
|
370
|
-
existingCredentials = JSON.parse(readFileSync(CREDENTIALS_FILE, "utf-8"));
|
|
371
|
-
} catch {}
|
|
372
|
-
}
|
|
373
|
-
const updated = {
|
|
374
|
-
...existingCredentials,
|
|
375
|
-
defaultOrganization: organizationId
|
|
376
|
-
};
|
|
377
|
-
writeSecureFile(CREDENTIALS_FILE, JSON.stringify(updated, null, 2));
|
|
378
|
-
}
|
|
379
|
-
var configSchema, credentialsSchema, CREDENTIALS_DIR, CREDENTIALS_FILE;
|
|
380
|
-
var init_config = __esm(() => {
|
|
381
|
-
init_secure_file();
|
|
382
|
-
init_known_secrets();
|
|
383
|
-
configSchema = z.object({
|
|
384
|
-
apiKey: z.string().optional(),
|
|
385
|
-
endpoint: z.string().default("https://api.mutagent.io"),
|
|
386
|
-
format: z.enum(["table", "json"]).default("table"),
|
|
387
|
-
timeout: z.number().default(30000),
|
|
388
|
-
defaultWorkspace: z.string().optional(),
|
|
389
|
-
defaultWorkspaceSource: z.enum(["user", "login-inferred"]).optional(),
|
|
390
|
-
defaultOrganization: z.string().optional()
|
|
391
|
-
});
|
|
392
|
-
credentialsSchema = z.object({
|
|
393
|
-
apiKey: z.string().optional(),
|
|
394
|
-
endpoint: z.string().optional(),
|
|
395
|
-
defaultWorkspace: z.string().optional(),
|
|
396
|
-
defaultWorkspaceSource: z.enum(["user", "login-inferred"]).optional(),
|
|
397
|
-
defaultOrganization: z.string().optional(),
|
|
398
|
-
expiresAt: z.string().optional()
|
|
399
|
-
}).loose();
|
|
400
|
-
CREDENTIALS_DIR = join(homedir(), ".config", "mutagent");
|
|
401
|
-
CREDENTIALS_FILE = join(CREDENTIALS_DIR, "credentials.json");
|
|
402
|
-
});
|
|
403
|
-
|
|
404
258
|
// src/lib/errors.ts
|
|
405
259
|
function createErrorFromUnknown(error) {
|
|
406
260
|
if (error instanceof MutagentError) {
|
|
@@ -590,29 +444,6 @@ var init_errors = __esm(() => {
|
|
|
590
444
|
};
|
|
591
445
|
});
|
|
592
446
|
|
|
593
|
-
// src/lib/provider-request.ts
|
|
594
|
-
function buildCreateProviderBody(data) {
|
|
595
|
-
const body = {
|
|
596
|
-
name: data.name,
|
|
597
|
-
provider: data.provider,
|
|
598
|
-
isDefault: data.isDefault ?? false
|
|
599
|
-
};
|
|
600
|
-
if (data.apiKey)
|
|
601
|
-
body.apiKey = data.apiKey;
|
|
602
|
-
if (data.baseUrl)
|
|
603
|
-
body.baseUrl = data.baseUrl;
|
|
604
|
-
if (data.hostedFamily)
|
|
605
|
-
body.hostedFamily = data.hostedFamily;
|
|
606
|
-
if (data.credentialFields && Object.keys(data.credentialFields).length > 0) {
|
|
607
|
-
body.credentialFields = data.credentialFields;
|
|
608
|
-
}
|
|
609
|
-
if (data.metadata && Object.keys(data.metadata).length > 0)
|
|
610
|
-
body.metadata = data.metadata;
|
|
611
|
-
if (data.supportedModels !== undefined)
|
|
612
|
-
body.supportedModels = data.supportedModels;
|
|
613
|
-
return body;
|
|
614
|
-
}
|
|
615
|
-
|
|
616
447
|
// src/lib/sdk-debug.ts
|
|
617
448
|
function isDebugEnabled(env = process.env) {
|
|
618
449
|
return TRUTHY.has((env.MUTAGENT_DEBUG ?? "").trim().toLowerCase());
|
|
@@ -707,6 +538,282 @@ var init_sdk_debug = __esm(() => {
|
|
|
707
538
|
};
|
|
708
539
|
});
|
|
709
540
|
|
|
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
|
+
|
|
710
817
|
// src/lib/sdk-mappers.ts
|
|
711
818
|
import { LlmProviderType as SDKProviderEnum } from "@mutagent/sdk/models";
|
|
712
819
|
function isProviderType(value) {
|
|
@@ -814,6 +921,7 @@ class SDKClientWrapper {
|
|
|
814
921
|
organizationId;
|
|
815
922
|
workspaceResolution = "none";
|
|
816
923
|
debugSecrets = new Set;
|
|
924
|
+
recoveringFetch;
|
|
817
925
|
constructor(opts) {
|
|
818
926
|
this.apiKey = opts.apiKey;
|
|
819
927
|
this.endpoint = opts.serverURL ?? "http://localhost:3003";
|
|
@@ -822,7 +930,16 @@ class SDKClientWrapper {
|
|
|
822
930
|
if (opts.workspaceId && opts.workspaceSource === "user") {
|
|
823
931
|
this.workspaceResolution = "configured";
|
|
824
932
|
}
|
|
825
|
-
|
|
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 });
|
|
826
943
|
httpClient.addHook("beforeRequest", (req) => {
|
|
827
944
|
if (this.workspaceId)
|
|
828
945
|
req.headers.set("x-workspace-id", this.workspaceId);
|
|
@@ -887,7 +1004,7 @@ class SDKClientWrapper {
|
|
|
887
1004
|
tenancyHeaders["x-workspace-id"] = this.workspaceId;
|
|
888
1005
|
if (this.organizationId)
|
|
889
1006
|
tenancyHeaders["x-organization-id"] = this.organizationId;
|
|
890
|
-
const response = await
|
|
1007
|
+
const response = await this.recoveringFetch(`${this.endpoint}${path}`, {
|
|
891
1008
|
...options,
|
|
892
1009
|
headers: {
|
|
893
1010
|
"x-api-key": this.apiKey,
|
|
@@ -1199,7 +1316,8 @@ async function getSDKClient() {
|
|
|
1199
1316
|
serverURL: config.endpoint,
|
|
1200
1317
|
workspaceId: config.defaultWorkspace,
|
|
1201
1318
|
organizationId: config.defaultOrganization,
|
|
1202
|
-
workspaceSource: config.defaultWorkspaceSource
|
|
1319
|
+
workspaceSource: config.defaultWorkspaceSource,
|
|
1320
|
+
clearStoredOrganization: clearDefaultOrganization
|
|
1203
1321
|
});
|
|
1204
1322
|
await sdkClient.ensureContext().catch((err) => {
|
|
1205
1323
|
if (isDebugEnabled()) {
|
|
@@ -1224,19 +1342,6 @@ async function validateApiKey(apiKey, endpoint) {
|
|
|
1224
1342
|
return false;
|
|
1225
1343
|
}
|
|
1226
1344
|
}
|
|
1227
|
-
async function fetchOrganizations(apiKey, endpoint) {
|
|
1228
|
-
try {
|
|
1229
|
-
const response = await fetch(`${endpoint}/api/organizations`, {
|
|
1230
|
-
headers: { "x-api-key": apiKey }
|
|
1231
|
-
});
|
|
1232
|
-
if (!response.ok)
|
|
1233
|
-
return [];
|
|
1234
|
-
const data = await response.json();
|
|
1235
|
-
return data.data ?? [];
|
|
1236
|
-
} catch {
|
|
1237
|
-
return [];
|
|
1238
|
-
}
|
|
1239
|
-
}
|
|
1240
1345
|
async function fetchWorkspaces(apiKey, endpoint, orgId) {
|
|
1241
1346
|
try {
|
|
1242
1347
|
const response = await fetch(`${endpoint}/api/workspaces`, {
|
|
@@ -1254,9 +1359,11 @@ var sdkClient = null;
|
|
|
1254
1359
|
var init_sdk_client = __esm(() => {
|
|
1255
1360
|
init_errors();
|
|
1256
1361
|
init_config();
|
|
1362
|
+
init_organization_scope();
|
|
1257
1363
|
init_sdk_debug();
|
|
1258
1364
|
init_known_secrets();
|
|
1259
1365
|
init_sdk_mappers();
|
|
1366
|
+
init_organization_scope();
|
|
1260
1367
|
});
|
|
1261
1368
|
|
|
1262
1369
|
// src/index.ts
|
|
@@ -1480,4 +1587,4 @@ export {
|
|
|
1480
1587
|
ApiError
|
|
1481
1588
|
};
|
|
1482
1589
|
|
|
1483
|
-
//# debugId=
|
|
1590
|
+
//# debugId=437AE9F8B6FD0C9364756E2164756E21
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mutagent/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.361",
|
|
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": {
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
44
|
"@inquirer/prompts": "^8.7.2",
|
|
45
|
-
"@mutagent/sdk": "0.6.
|
|
45
|
+
"@mutagent/sdk": "0.6.4",
|
|
46
46
|
"chalk": "^5.3.0",
|
|
47
47
|
"commander": "^12.1.0",
|
|
48
48
|
"cosmiconfig": "^9.0.0",
|