@oxygen-agent/cli 1.50.37 → 1.98.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/browser-login.js +1 -1
- package/dist/credentials.d.ts +33 -0
- package/dist/credentials.js +200 -7
- package/dist/http-client.d.ts +5 -1
- package/dist/http-client.js +23 -2
- package/dist/index.js +2123 -366
- package/dist/skills.d.ts +47 -0
- package/dist/skills.js +375 -0
- package/dist/update.d.ts +3 -0
- package/dist/update.js +9 -5
- package/dist/windows-shim.d.ts +7 -0
- package/dist/windows-shim.js +21 -0
- package/node_modules/@oxygen/shared/dist/cell-format.js +2 -3
- package/node_modules/@oxygen/shared/dist/file-import.d.ts +5 -0
- package/node_modules/@oxygen/shared/dist/file-import.js +156 -6
- package/node_modules/@oxygen/shared/dist/index.d.ts +1 -0
- package/node_modules/@oxygen/shared/dist/index.js +1 -0
- package/node_modules/@oxygen/shared/dist/object-storage.d.ts +26 -0
- package/node_modules/@oxygen/shared/dist/object-storage.js +115 -0
- package/node_modules/@oxygen/shared/dist/provider-request-outcomes.d.ts +3 -0
- package/node_modules/@oxygen/shared/dist/provider-request-outcomes.js +5 -0
- package/node_modules/@oxygen/shared/dist/version.d.ts +1 -1
- package/node_modules/@oxygen/shared/dist/version.js +1 -1
- package/node_modules/@oxygen/workflows/dist/index.d.ts +91 -0
- package/node_modules/@oxygen/workflows/dist/index.js +268 -4
- package/package.json +1 -1
package/README.md
CHANGED
package/dist/browser-login.js
CHANGED
|
@@ -44,7 +44,7 @@ export async function createBrowserLoginSession(apiUrl) {
|
|
|
44
44
|
cli_state: state,
|
|
45
45
|
source: "oxygen_cli",
|
|
46
46
|
});
|
|
47
|
-
const redirectPath = `/settings/
|
|
47
|
+
const redirectPath = `/settings/cli?${redirectParams.toString()}`;
|
|
48
48
|
const verificationUrl = new URL("/auth/sign-in", apiUrl);
|
|
49
49
|
verificationUrl.searchParams.set("redirect_url", redirectPath);
|
|
50
50
|
verificationUrl.searchParams.set("source", "oxygen_cli");
|
package/dist/credentials.d.ts
CHANGED
|
@@ -1,6 +1,22 @@
|
|
|
1
|
+
export type StoredProfileIdentity = {
|
|
2
|
+
organization: StoredOrganization | null;
|
|
3
|
+
user: {
|
|
4
|
+
id: string;
|
|
5
|
+
email: string | null;
|
|
6
|
+
};
|
|
7
|
+
capturedAt: string;
|
|
8
|
+
};
|
|
9
|
+
export type StoredOrganization = {
|
|
10
|
+
id: string;
|
|
11
|
+
slug: string | null;
|
|
12
|
+
name: string;
|
|
13
|
+
};
|
|
1
14
|
export type StoredCredentials = {
|
|
2
15
|
token: string;
|
|
3
16
|
apiUrl: string;
|
|
17
|
+
authKind?: "org_api_key" | "user_session";
|
|
18
|
+
activeOrganization?: StoredOrganization | null;
|
|
19
|
+
identity?: StoredProfileIdentity;
|
|
4
20
|
};
|
|
5
21
|
export type CredentialProfile = StoredCredentials & {
|
|
6
22
|
name: string;
|
|
@@ -23,5 +39,22 @@ export declare function clearCredentials(env?: NodeJS.ProcessEnv, options?: {
|
|
|
23
39
|
remainingProfiles: number;
|
|
24
40
|
}>;
|
|
25
41
|
export declare function listCredentialProfiles(env?: NodeJS.ProcessEnv): Promise<CredentialProfilesState>;
|
|
42
|
+
export type ProfileResolution = {
|
|
43
|
+
name: string;
|
|
44
|
+
source: "env" | "file" | "default";
|
|
45
|
+
credentials: StoredCredentials | null;
|
|
46
|
+
exists: boolean;
|
|
47
|
+
};
|
|
48
|
+
export declare function resolveActiveProfile(env?: NodeJS.ProcessEnv): Promise<ProfileResolution>;
|
|
49
|
+
export declare function findProfileNameByOrganizationId(organizationId: string, env?: NodeJS.ProcessEnv): Promise<string | null>;
|
|
50
|
+
export declare function pickProfileNameForIdentity(organizationId: string, candidateSeed: string, env?: NodeJS.ProcessEnv): Promise<{
|
|
51
|
+
name: string;
|
|
52
|
+
renamed: boolean;
|
|
53
|
+
}>;
|
|
54
|
+
export declare function pickProfileNameForUserSession(userId: string, candidateSeed: string, env?: NodeJS.ProcessEnv): Promise<{
|
|
55
|
+
name: string;
|
|
56
|
+
renamed: boolean;
|
|
57
|
+
}>;
|
|
26
58
|
export declare function switchCredentialProfile(profile: string, env?: NodeJS.ProcessEnv): Promise<CredentialProfile>;
|
|
59
|
+
export declare function updateActiveOrganizationForProfile(profile: string, organization: StoredOrganization, env?: NodeJS.ProcessEnv): Promise<CredentialProfile>;
|
|
27
60
|
export declare function normalizeApiUrl(value: string): string;
|
package/dist/credentials.js
CHANGED
|
@@ -16,10 +16,14 @@ export function defaultApiUrl(env = process.env) {
|
|
|
16
16
|
export async function loadCredentials(env = process.env) {
|
|
17
17
|
const envToken = readEnvToken(env);
|
|
18
18
|
if (envToken) {
|
|
19
|
-
|
|
19
|
+
const credentials = {
|
|
20
20
|
token: envToken,
|
|
21
21
|
apiUrl: defaultApiUrl(env),
|
|
22
22
|
};
|
|
23
|
+
const authKind = inferAuthKind(envToken);
|
|
24
|
+
if (authKind)
|
|
25
|
+
credentials.authKind = authKind;
|
|
26
|
+
return credentials;
|
|
23
27
|
}
|
|
24
28
|
const document = await readSystemCredentialDocument(env);
|
|
25
29
|
if (!document)
|
|
@@ -43,9 +47,22 @@ export async function saveCredentials(credentials, env = process.env, options =
|
|
|
43
47
|
token: credentials.token,
|
|
44
48
|
apiUrl: normalizeApiUrl(credentials.apiUrl),
|
|
45
49
|
};
|
|
50
|
+
if (credentials.authKind)
|
|
51
|
+
normalized.authKind = credentials.authKind;
|
|
52
|
+
if (credentials.activeOrganization !== undefined) {
|
|
53
|
+
normalized.activeOrganization = credentials.activeOrganization;
|
|
54
|
+
}
|
|
55
|
+
if (credentials.identity)
|
|
56
|
+
normalized.identity = credentials.identity;
|
|
46
57
|
const existing = await readSystemCredentialDocument(env);
|
|
47
58
|
const profile = normalizeProfileName(options.profile ?? readEnvProfile(env) ?? existing?.activeProfile ?? DEFAULT_PROFILE_NAME);
|
|
48
59
|
const profiles = existing ? readProfiles(existing) : {};
|
|
60
|
+
if (!normalized.identity) {
|
|
61
|
+
const prior = profiles[profile];
|
|
62
|
+
if (prior?.identity && prior.token === normalized.token) {
|
|
63
|
+
normalized.identity = prior.identity;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
49
66
|
profiles[profile] = normalized;
|
|
50
67
|
const activeProfile = options.activate === false
|
|
51
68
|
? readProfileName(existing?.activeProfile) ?? profile
|
|
@@ -88,12 +105,18 @@ export async function clearCredentials(env = process.env, options = {}) {
|
|
|
88
105
|
export async function listCredentialProfiles(env = process.env) {
|
|
89
106
|
const envToken = readEnvToken(env);
|
|
90
107
|
if (envToken) {
|
|
108
|
+
const credentials = {
|
|
109
|
+
token: envToken,
|
|
110
|
+
apiUrl: defaultApiUrl(env),
|
|
111
|
+
};
|
|
112
|
+
const authKind = inferAuthKind(envToken);
|
|
113
|
+
if (authKind)
|
|
114
|
+
credentials.authKind = authKind;
|
|
91
115
|
return {
|
|
92
116
|
activeProfile: readEnvProfile(env) ?? "env",
|
|
93
117
|
profiles: [{
|
|
94
118
|
name: readEnvProfile(env) ?? "env",
|
|
95
|
-
|
|
96
|
-
apiUrl: defaultApiUrl(env),
|
|
119
|
+
...credentials,
|
|
97
120
|
}],
|
|
98
121
|
};
|
|
99
122
|
}
|
|
@@ -116,6 +139,73 @@ export async function listCredentialProfiles(env = process.env) {
|
|
|
116
139
|
}),
|
|
117
140
|
};
|
|
118
141
|
}
|
|
142
|
+
export async function resolveActiveProfile(env = process.env) {
|
|
143
|
+
const envProfile = readEnvProfile(env);
|
|
144
|
+
const document = await readSystemCredentialDocument(env);
|
|
145
|
+
const profiles = document ? readProfiles(document) : {};
|
|
146
|
+
const fileActive = readProfileName(document?.activeProfile);
|
|
147
|
+
const candidate = envProfile ?? fileActive ?? DEFAULT_PROFILE_NAME;
|
|
148
|
+
const credentials = profiles[candidate] ?? null;
|
|
149
|
+
const source = envProfile
|
|
150
|
+
? "env"
|
|
151
|
+
: fileActive
|
|
152
|
+
? "file"
|
|
153
|
+
: "default";
|
|
154
|
+
return { name: candidate, source, credentials, exists: Boolean(credentials) };
|
|
155
|
+
}
|
|
156
|
+
export async function findProfileNameByOrganizationId(organizationId, env = process.env) {
|
|
157
|
+
const document = await readSystemCredentialDocument(env);
|
|
158
|
+
if (!document)
|
|
159
|
+
return null;
|
|
160
|
+
const profiles = readProfiles(document);
|
|
161
|
+
for (const [name, credentials] of Object.entries(profiles)) {
|
|
162
|
+
if (credentials.identity?.organization?.id === organizationId)
|
|
163
|
+
return name;
|
|
164
|
+
if (credentials.activeOrganization?.id === organizationId)
|
|
165
|
+
return name;
|
|
166
|
+
}
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
export async function pickProfileNameForIdentity(organizationId, candidateSeed, env = process.env) {
|
|
170
|
+
const document = await readSystemCredentialDocument(env);
|
|
171
|
+
const profiles = document ? readProfiles(document) : {};
|
|
172
|
+
const existingForOrg = Object.entries(profiles).find(([, credentials]) => credentials.identity?.organization?.id === organizationId ||
|
|
173
|
+
credentials.activeOrganization?.id === organizationId);
|
|
174
|
+
if (existingForOrg)
|
|
175
|
+
return { name: existingForOrg[0], renamed: false };
|
|
176
|
+
const base = normalizeProfileName(candidateSeed);
|
|
177
|
+
if (!profiles[base])
|
|
178
|
+
return { name: base, renamed: false };
|
|
179
|
+
for (let suffix = 2; suffix < 1000; suffix++) {
|
|
180
|
+
const next = `${base}-${suffix}`;
|
|
181
|
+
if (!profiles[next])
|
|
182
|
+
return { name: next, renamed: true };
|
|
183
|
+
}
|
|
184
|
+
throw new OxygenError("profile_collision", "Unable to derive a unique CLI profile name.", {
|
|
185
|
+
details: { base },
|
|
186
|
+
exitCode: 1,
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
export async function pickProfileNameForUserSession(userId, candidateSeed, env = process.env) {
|
|
190
|
+
const document = await readSystemCredentialDocument(env);
|
|
191
|
+
const profiles = document ? readProfiles(document) : {};
|
|
192
|
+
const existingForUser = Object.entries(profiles).find(([, credentials]) => credentials.authKind === "user_session" &&
|
|
193
|
+
credentials.identity?.user.id === userId);
|
|
194
|
+
if (existingForUser)
|
|
195
|
+
return { name: existingForUser[0], renamed: false };
|
|
196
|
+
const base = normalizeProfileName(candidateSeed);
|
|
197
|
+
if (!profiles[base])
|
|
198
|
+
return { name: base, renamed: false };
|
|
199
|
+
for (let suffix = 2; suffix < 1000; suffix++) {
|
|
200
|
+
const next = `${base}-${suffix}`;
|
|
201
|
+
if (!profiles[next])
|
|
202
|
+
return { name: next, renamed: true };
|
|
203
|
+
}
|
|
204
|
+
throw new OxygenError("profile_collision", "Unable to derive a unique CLI profile name.", {
|
|
205
|
+
details: { base },
|
|
206
|
+
exitCode: 1,
|
|
207
|
+
});
|
|
208
|
+
}
|
|
119
209
|
export async function switchCredentialProfile(profile, env = process.env) {
|
|
120
210
|
const document = await readSystemCredentialDocument(env);
|
|
121
211
|
if (!document) {
|
|
@@ -135,6 +225,35 @@ export async function switchCredentialProfile(profile, env = process.env) {
|
|
|
135
225
|
await writeCredentialDocument({ profiles, activeProfile: profileName }, env);
|
|
136
226
|
return { name: profileName, ...credentials };
|
|
137
227
|
}
|
|
228
|
+
export async function updateActiveOrganizationForProfile(profile, organization, env = process.env) {
|
|
229
|
+
const document = await readSystemCredentialDocument(env);
|
|
230
|
+
if (!document) {
|
|
231
|
+
throw new OxygenError("not_logged_in", "Run `oxygen login` before selecting an organization.", {
|
|
232
|
+
exitCode: 1,
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
const profileName = normalizeProfileName(profile);
|
|
236
|
+
const profiles = readProfiles(document);
|
|
237
|
+
const credentials = profiles[profileName];
|
|
238
|
+
if (!credentials) {
|
|
239
|
+
throw new OxygenError("profile_not_found", `Oxygen CLI profile "${profileName}" is not stored.`, {
|
|
240
|
+
details: { profile: profileName },
|
|
241
|
+
exitCode: 1,
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
const updated = {
|
|
245
|
+
...credentials,
|
|
246
|
+
activeOrganization: organization,
|
|
247
|
+
};
|
|
248
|
+
if (credentials.identity)
|
|
249
|
+
updated.identity = { ...credentials.identity, organization };
|
|
250
|
+
profiles[profileName] = updated;
|
|
251
|
+
await writeCredentialDocument({
|
|
252
|
+
profiles,
|
|
253
|
+
activeProfile: readProfileName(document.activeProfile) ?? profileName,
|
|
254
|
+
}, env);
|
|
255
|
+
return { name: profileName, ...updated };
|
|
256
|
+
}
|
|
138
257
|
export function normalizeApiUrl(value) {
|
|
139
258
|
const trimmed = value.trim();
|
|
140
259
|
if (!trimmed)
|
|
@@ -231,6 +350,8 @@ async function writeCredentialDocument(input, env) {
|
|
|
231
350
|
activeProfile: input.activeProfile,
|
|
232
351
|
profiles: input.profiles,
|
|
233
352
|
};
|
|
353
|
+
if (activeCredentials.identity)
|
|
354
|
+
document.identity = activeCredentials.identity;
|
|
234
355
|
await writeSystemCredentialPayload(serializeCredentialDocument(document), env);
|
|
235
356
|
}
|
|
236
357
|
function credentialStore(env) {
|
|
@@ -392,7 +513,7 @@ async function deleteWindowsCredentialManager(env) {
|
|
|
392
513
|
[void][OxygenCredMan]::CredDelete("${SERVICE_NAME}", 1, 0)
|
|
393
514
|
`, env).catch(() => undefined);
|
|
394
515
|
}
|
|
395
|
-
|
|
516
|
+
function runPowerShell(script, env) {
|
|
396
517
|
const encodedCommand = Buffer.from(script, "utf16le").toString("base64");
|
|
397
518
|
return execFileAsync(windowsPowerShellCommand(env), [
|
|
398
519
|
"-NoProfile",
|
|
@@ -423,7 +544,7 @@ function credentialStoreError(operation, store, error) {
|
|
|
423
544
|
return new OxygenError("credential_store_failed", `Unable to ${operation} Oxygen CLI credentials in ${store}.`, { details, exitCode: 1 });
|
|
424
545
|
}
|
|
425
546
|
function sanitizeCredentialErrorText(value) {
|
|
426
|
-
return value.replace(/\
|
|
547
|
+
return value.replace(/\boxy_(?:live|user)_[A-Za-z0-9_-]+\b/g, "[redacted_token]");
|
|
427
548
|
}
|
|
428
549
|
function macOSSecurityCommand(env) {
|
|
429
550
|
return env.OXYGEN_SECURITY_COMMAND?.trim() || "security";
|
|
@@ -516,16 +637,81 @@ function parseStoredCredentialsObject(value) {
|
|
|
516
637
|
return null;
|
|
517
638
|
if (typeof apiUrl !== "string" || !apiUrl.trim())
|
|
518
639
|
return null;
|
|
519
|
-
|
|
640
|
+
const credentials = {
|
|
641
|
+
token: token.trim(),
|
|
642
|
+
apiUrl: normalizeApiUrl(apiUrl),
|
|
643
|
+
};
|
|
644
|
+
const authKind = value.authKind;
|
|
645
|
+
if (authKind === "org_api_key" || authKind === "user_session") {
|
|
646
|
+
credentials.authKind = authKind;
|
|
647
|
+
}
|
|
648
|
+
else {
|
|
649
|
+
const inferredAuthKind = inferAuthKind(credentials.token);
|
|
650
|
+
if (inferredAuthKind)
|
|
651
|
+
credentials.authKind = inferredAuthKind;
|
|
652
|
+
}
|
|
653
|
+
const activeOrganization = parseStoredOrganization(value.activeOrganization);
|
|
654
|
+
if (activeOrganization)
|
|
655
|
+
credentials.activeOrganization = activeOrganization;
|
|
656
|
+
const identity = parseStoredProfileIdentity(value.identity);
|
|
657
|
+
if (identity)
|
|
658
|
+
credentials.identity = identity;
|
|
659
|
+
return credentials;
|
|
660
|
+
}
|
|
661
|
+
function parseStoredProfileIdentity(value) {
|
|
662
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
663
|
+
return null;
|
|
664
|
+
const record = value;
|
|
665
|
+
const org = record.organization;
|
|
666
|
+
const user = record.user;
|
|
667
|
+
if (!user || typeof user !== "object" || Array.isArray(user))
|
|
668
|
+
return null;
|
|
669
|
+
const userRecord = user;
|
|
670
|
+
const organization = parseStoredOrganization(org);
|
|
671
|
+
const userId = typeof userRecord.id === "string" ? userRecord.id.trim() : "";
|
|
672
|
+
if (!userId)
|
|
673
|
+
return null;
|
|
674
|
+
const userEmail = typeof userRecord.email === "string" && userRecord.email.trim()
|
|
675
|
+
? userRecord.email.trim()
|
|
676
|
+
: null;
|
|
677
|
+
const capturedAt = typeof record.capturedAt === "string" && record.capturedAt.trim()
|
|
678
|
+
? record.capturedAt.trim()
|
|
679
|
+
: new Date(0).toISOString();
|
|
680
|
+
return {
|
|
681
|
+
organization,
|
|
682
|
+
user: { id: userId, email: userEmail },
|
|
683
|
+
capturedAt,
|
|
684
|
+
};
|
|
685
|
+
}
|
|
686
|
+
function parseStoredOrganization(value) {
|
|
687
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
688
|
+
return null;
|
|
689
|
+
const record = value;
|
|
690
|
+
const id = typeof record.id === "string" ? record.id.trim() : "";
|
|
691
|
+
const name = typeof record.name === "string" ? record.name : "";
|
|
692
|
+
if (!id || !name)
|
|
693
|
+
return null;
|
|
694
|
+
const slug = typeof record.slug === "string" && record.slug.trim()
|
|
695
|
+
? record.slug.trim()
|
|
696
|
+
: null;
|
|
697
|
+
return { id, slug, name };
|
|
520
698
|
}
|
|
521
699
|
function readProfiles(document) {
|
|
522
700
|
const profiles = {};
|
|
523
701
|
const rawProfiles = document.profiles ?? {};
|
|
524
702
|
for (const [name, credentials] of Object.entries(rawProfiles)) {
|
|
525
|
-
|
|
703
|
+
const normalized = {
|
|
526
704
|
token: credentials.token,
|
|
527
705
|
apiUrl: normalizeApiUrl(credentials.apiUrl),
|
|
528
706
|
};
|
|
707
|
+
if (credentials.authKind)
|
|
708
|
+
normalized.authKind = credentials.authKind;
|
|
709
|
+
if (credentials.activeOrganization !== undefined) {
|
|
710
|
+
normalized.activeOrganization = credentials.activeOrganization;
|
|
711
|
+
}
|
|
712
|
+
if (credentials.identity)
|
|
713
|
+
normalized.identity = credentials.identity;
|
|
714
|
+
profiles[normalizeProfileName(name)] = normalized;
|
|
529
715
|
}
|
|
530
716
|
if (Object.keys(profiles).length === 0) {
|
|
531
717
|
const defaultCredentials = parseStoredCredentialsObject(document);
|
|
@@ -538,3 +724,10 @@ function readEnvToken(env) {
|
|
|
538
724
|
const token = env.OXYGEN_API_KEY ?? env.OXYGEN_TOKEN;
|
|
539
725
|
return token?.trim() || null;
|
|
540
726
|
}
|
|
727
|
+
function inferAuthKind(token) {
|
|
728
|
+
if (token.startsWith("oxy_user_"))
|
|
729
|
+
return "user_session";
|
|
730
|
+
if (token.startsWith("oxy_live_"))
|
|
731
|
+
return "org_api_key";
|
|
732
|
+
return undefined;
|
|
733
|
+
}
|
package/dist/http-client.d.ts
CHANGED
|
@@ -2,10 +2,14 @@ import { type StoredCredentials } from "./credentials.js";
|
|
|
2
2
|
type RequestOptions = {
|
|
3
3
|
method?: "GET" | "POST" | "DELETE";
|
|
4
4
|
body?: Record<string, unknown>;
|
|
5
|
+
formData?: FormData;
|
|
5
6
|
credentials?: StoredCredentials;
|
|
6
7
|
requireAuth?: boolean;
|
|
7
8
|
timeoutMs?: number;
|
|
9
|
+
traceId?: string;
|
|
8
10
|
fetch?: typeof fetch;
|
|
11
|
+
selectedOrganization?: string;
|
|
9
12
|
};
|
|
10
|
-
export declare function requestOxygen<T>(
|
|
13
|
+
export declare function requestOxygen<T>(// skipcq: JS-R1005
|
|
14
|
+
path: string, options?: RequestOptions): Promise<T>;
|
|
11
15
|
export {};
|
package/dist/http-client.js
CHANGED
|
@@ -2,7 +2,8 @@ import { OXYGEN_VERSION, OxygenError } from "@oxygen/shared";
|
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
3
|
import { defaultApiUrl, loadCredentials } from "./credentials.js";
|
|
4
4
|
const DEFAULT_REQUEST_TIMEOUT_MS = 120_000;
|
|
5
|
-
export async function requestOxygen(
|
|
5
|
+
export async function requestOxygen(// skipcq: JS-R1005
|
|
6
|
+
path, options = {}) {
|
|
6
7
|
const credentials = options.credentials
|
|
7
8
|
?? (options.requireAuth === false ? null : await loadCredentials());
|
|
8
9
|
if (!credentials && options.requireAuth !== false) {
|
|
@@ -11,7 +12,7 @@ export async function requestOxygen(path, options = {}) {
|
|
|
11
12
|
});
|
|
12
13
|
}
|
|
13
14
|
const apiUrl = credentials?.apiUrl ?? defaultApiUrl();
|
|
14
|
-
const traceId = randomUUID();
|
|
15
|
+
const traceId = options.traceId ?? randomUUID();
|
|
15
16
|
const headers = {
|
|
16
17
|
Accept: "application/json",
|
|
17
18
|
"X-Oxygen-Trace-Id": traceId,
|
|
@@ -20,6 +21,13 @@ export async function requestOxygen(path, options = {}) {
|
|
|
20
21
|
if (credentials?.token) {
|
|
21
22
|
headers.Authorization = `Bearer ${credentials.token}`;
|
|
22
23
|
}
|
|
24
|
+
const selectedOrganization = resolveSelectedOrganization(credentials, options.selectedOrganization);
|
|
25
|
+
if (selectedOrganization) {
|
|
26
|
+
headers["X-Oxygen-Organization"] = selectedOrganization;
|
|
27
|
+
}
|
|
28
|
+
if (options.body && options.formData) {
|
|
29
|
+
throw new OxygenError("invalid_request", "Pass body or formData, not both.", { exitCode: 1 });
|
|
30
|
+
}
|
|
23
31
|
if (options.body) {
|
|
24
32
|
headers["Content-Type"] = "application/json";
|
|
25
33
|
}
|
|
@@ -31,6 +39,7 @@ export async function requestOxygen(path, options = {}) {
|
|
|
31
39
|
method: options.method ?? "GET",
|
|
32
40
|
headers,
|
|
33
41
|
...(options.body ? { body: JSON.stringify(options.body) } : {}),
|
|
42
|
+
...(options.formData ? { body: options.formData } : {}),
|
|
34
43
|
};
|
|
35
44
|
if (timeout.signal)
|
|
36
45
|
requestInit.signal = timeout.signal;
|
|
@@ -44,6 +53,7 @@ export async function requestOxygen(path, options = {}) {
|
|
|
44
53
|
api_url: apiUrl,
|
|
45
54
|
path,
|
|
46
55
|
timeout_ms: timeoutMs,
|
|
56
|
+
trace_id: traceId,
|
|
47
57
|
},
|
|
48
58
|
exitCode: 1,
|
|
49
59
|
});
|
|
@@ -72,6 +82,17 @@ export async function requestOxygen(path, options = {}) {
|
|
|
72
82
|
}
|
|
73
83
|
return envelope.data;
|
|
74
84
|
}
|
|
85
|
+
function resolveSelectedOrganization(credentials, explicit) {
|
|
86
|
+
const fromOption = explicit?.trim();
|
|
87
|
+
if (fromOption)
|
|
88
|
+
return fromOption;
|
|
89
|
+
const fromEnv = process.env.OXYGEN_ORG?.trim();
|
|
90
|
+
if (fromEnv)
|
|
91
|
+
return fromEnv;
|
|
92
|
+
return credentials?.activeOrganization?.slug
|
|
93
|
+
?? credentials?.activeOrganization?.id
|
|
94
|
+
?? null;
|
|
95
|
+
}
|
|
75
96
|
const staleCliWarningVersions = new Set();
|
|
76
97
|
function warnIfCliIsOlderThanApi(serverVersion) {
|
|
77
98
|
if (!serverVersion || staleCliWarningVersions.has(serverVersion))
|