@oxygen-agent/cli 1.64.5 → 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 +13 -5
- package/dist/credentials.js +112 -17
- package/dist/http-client.d.ts +5 -1
- package/dist/http-client.js +23 -2
- package/dist/index.js +1417 -306
- package/dist/skills.d.ts +8 -2
- package/dist/skills.js +67 -17
- package/dist/update.js +2 -5
- package/dist/windows-shim.d.ts +7 -0
- package/dist/windows-shim.js +21 -0
- 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/object-storage.d.ts +26 -0
- package/node_modules/@oxygen/shared/dist/object-storage.js +115 -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 +232 -1
- 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,18 +1,21 @@
|
|
|
1
1
|
export type StoredProfileIdentity = {
|
|
2
|
-
organization:
|
|
3
|
-
id: string;
|
|
4
|
-
slug: string | null;
|
|
5
|
-
name: string;
|
|
6
|
-
};
|
|
2
|
+
organization: StoredOrganization | null;
|
|
7
3
|
user: {
|
|
8
4
|
id: string;
|
|
9
5
|
email: string | null;
|
|
10
6
|
};
|
|
11
7
|
capturedAt: string;
|
|
12
8
|
};
|
|
9
|
+
export type StoredOrganization = {
|
|
10
|
+
id: string;
|
|
11
|
+
slug: string | null;
|
|
12
|
+
name: string;
|
|
13
|
+
};
|
|
13
14
|
export type StoredCredentials = {
|
|
14
15
|
token: string;
|
|
15
16
|
apiUrl: string;
|
|
17
|
+
authKind?: "org_api_key" | "user_session";
|
|
18
|
+
activeOrganization?: StoredOrganization | null;
|
|
16
19
|
identity?: StoredProfileIdentity;
|
|
17
20
|
};
|
|
18
21
|
export type CredentialProfile = StoredCredentials & {
|
|
@@ -48,5 +51,10 @@ export declare function pickProfileNameForIdentity(organizationId: string, candi
|
|
|
48
51
|
name: string;
|
|
49
52
|
renamed: boolean;
|
|
50
53
|
}>;
|
|
54
|
+
export declare function pickProfileNameForUserSession(userId: string, candidateSeed: string, env?: NodeJS.ProcessEnv): Promise<{
|
|
55
|
+
name: string;
|
|
56
|
+
renamed: boolean;
|
|
57
|
+
}>;
|
|
51
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>;
|
|
52
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,6 +47,11 @@ 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
|
+
}
|
|
46
55
|
if (credentials.identity)
|
|
47
56
|
normalized.identity = credentials.identity;
|
|
48
57
|
const existing = await readSystemCredentialDocument(env);
|
|
@@ -96,12 +105,18 @@ export async function clearCredentials(env = process.env, options = {}) {
|
|
|
96
105
|
export async function listCredentialProfiles(env = process.env) {
|
|
97
106
|
const envToken = readEnvToken(env);
|
|
98
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;
|
|
99
115
|
return {
|
|
100
116
|
activeProfile: readEnvProfile(env) ?? "env",
|
|
101
117
|
profiles: [{
|
|
102
118
|
name: readEnvProfile(env) ?? "env",
|
|
103
|
-
|
|
104
|
-
apiUrl: defaultApiUrl(env),
|
|
119
|
+
...credentials,
|
|
105
120
|
}],
|
|
106
121
|
};
|
|
107
122
|
}
|
|
@@ -144,7 +159,9 @@ export async function findProfileNameByOrganizationId(organizationId, env = proc
|
|
|
144
159
|
return null;
|
|
145
160
|
const profiles = readProfiles(document);
|
|
146
161
|
for (const [name, credentials] of Object.entries(profiles)) {
|
|
147
|
-
if (credentials.identity?.organization
|
|
162
|
+
if (credentials.identity?.organization?.id === organizationId)
|
|
163
|
+
return name;
|
|
164
|
+
if (credentials.activeOrganization?.id === organizationId)
|
|
148
165
|
return name;
|
|
149
166
|
}
|
|
150
167
|
return null;
|
|
@@ -152,7 +169,8 @@ export async function findProfileNameByOrganizationId(organizationId, env = proc
|
|
|
152
169
|
export async function pickProfileNameForIdentity(organizationId, candidateSeed, env = process.env) {
|
|
153
170
|
const document = await readSystemCredentialDocument(env);
|
|
154
171
|
const profiles = document ? readProfiles(document) : {};
|
|
155
|
-
const existingForOrg = Object.entries(profiles).find(([, credentials]) => credentials.identity?.organization
|
|
172
|
+
const existingForOrg = Object.entries(profiles).find(([, credentials]) => credentials.identity?.organization?.id === organizationId ||
|
|
173
|
+
credentials.activeOrganization?.id === organizationId);
|
|
156
174
|
if (existingForOrg)
|
|
157
175
|
return { name: existingForOrg[0], renamed: false };
|
|
158
176
|
const base = normalizeProfileName(candidateSeed);
|
|
@@ -168,6 +186,26 @@ export async function pickProfileNameForIdentity(organizationId, candidateSeed,
|
|
|
168
186
|
exitCode: 1,
|
|
169
187
|
});
|
|
170
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
|
+
}
|
|
171
209
|
export async function switchCredentialProfile(profile, env = process.env) {
|
|
172
210
|
const document = await readSystemCredentialDocument(env);
|
|
173
211
|
if (!document) {
|
|
@@ -187,6 +225,35 @@ export async function switchCredentialProfile(profile, env = process.env) {
|
|
|
187
225
|
await writeCredentialDocument({ profiles, activeProfile: profileName }, env);
|
|
188
226
|
return { name: profileName, ...credentials };
|
|
189
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
|
+
}
|
|
190
257
|
export function normalizeApiUrl(value) {
|
|
191
258
|
const trimmed = value.trim();
|
|
192
259
|
if (!trimmed)
|
|
@@ -477,7 +544,7 @@ function credentialStoreError(operation, store, error) {
|
|
|
477
544
|
return new OxygenError("credential_store_failed", `Unable to ${operation} Oxygen CLI credentials in ${store}.`, { details, exitCode: 1 });
|
|
478
545
|
}
|
|
479
546
|
function sanitizeCredentialErrorText(value) {
|
|
480
|
-
return value.replace(/\
|
|
547
|
+
return value.replace(/\boxy_(?:live|user)_[A-Za-z0-9_-]+\b/g, "[redacted_token]");
|
|
481
548
|
}
|
|
482
549
|
function macOSSecurityCommand(env) {
|
|
483
550
|
return env.OXYGEN_SECURITY_COMMAND?.trim() || "security";
|
|
@@ -574,6 +641,18 @@ function parseStoredCredentialsObject(value) {
|
|
|
574
641
|
token: token.trim(),
|
|
575
642
|
apiUrl: normalizeApiUrl(apiUrl),
|
|
576
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;
|
|
577
656
|
const identity = parseStoredProfileIdentity(value.identity);
|
|
578
657
|
if (identity)
|
|
579
658
|
credentials.identity = identity;
|
|
@@ -585,19 +664,10 @@ function parseStoredProfileIdentity(value) {
|
|
|
585
664
|
const record = value;
|
|
586
665
|
const org = record.organization;
|
|
587
666
|
const user = record.user;
|
|
588
|
-
if (!org || typeof org !== "object" || Array.isArray(org))
|
|
589
|
-
return null;
|
|
590
667
|
if (!user || typeof user !== "object" || Array.isArray(user))
|
|
591
668
|
return null;
|
|
592
|
-
const orgRecord = org;
|
|
593
669
|
const userRecord = user;
|
|
594
|
-
const
|
|
595
|
-
const orgName = typeof orgRecord.name === "string" ? orgRecord.name : "";
|
|
596
|
-
if (!orgId || !orgName)
|
|
597
|
-
return null;
|
|
598
|
-
const orgSlug = typeof orgRecord.slug === "string" && orgRecord.slug.trim()
|
|
599
|
-
? orgRecord.slug.trim()
|
|
600
|
-
: null;
|
|
670
|
+
const organization = parseStoredOrganization(org);
|
|
601
671
|
const userId = typeof userRecord.id === "string" ? userRecord.id.trim() : "";
|
|
602
672
|
if (!userId)
|
|
603
673
|
return null;
|
|
@@ -608,11 +678,24 @@ function parseStoredProfileIdentity(value) {
|
|
|
608
678
|
? record.capturedAt.trim()
|
|
609
679
|
: new Date(0).toISOString();
|
|
610
680
|
return {
|
|
611
|
-
organization
|
|
681
|
+
organization,
|
|
612
682
|
user: { id: userId, email: userEmail },
|
|
613
683
|
capturedAt,
|
|
614
684
|
};
|
|
615
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 };
|
|
698
|
+
}
|
|
616
699
|
function readProfiles(document) {
|
|
617
700
|
const profiles = {};
|
|
618
701
|
const rawProfiles = document.profiles ?? {};
|
|
@@ -621,6 +704,11 @@ function readProfiles(document) {
|
|
|
621
704
|
token: credentials.token,
|
|
622
705
|
apiUrl: normalizeApiUrl(credentials.apiUrl),
|
|
623
706
|
};
|
|
707
|
+
if (credentials.authKind)
|
|
708
|
+
normalized.authKind = credentials.authKind;
|
|
709
|
+
if (credentials.activeOrganization !== undefined) {
|
|
710
|
+
normalized.activeOrganization = credentials.activeOrganization;
|
|
711
|
+
}
|
|
624
712
|
if (credentials.identity)
|
|
625
713
|
normalized.identity = credentials.identity;
|
|
626
714
|
profiles[normalizeProfileName(name)] = normalized;
|
|
@@ -636,3 +724,10 @@ function readEnvToken(env) {
|
|
|
636
724
|
const token = env.OXYGEN_API_KEY ?? env.OXYGEN_TOKEN;
|
|
637
725
|
return token?.trim() || null;
|
|
638
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))
|