@oxygen-agent/cli 1.50.37 → 1.64.5

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 CHANGED
@@ -34,4 +34,4 @@ oxygen update
34
34
 
35
35
  For product documentation and support, visit https://oxygen-agent.com.
36
36
 
37
- Version: 1.50.37
37
+ Version: 1.64.5
@@ -1,6 +1,19 @@
1
+ export type StoredProfileIdentity = {
2
+ organization: {
3
+ id: string;
4
+ slug: string | null;
5
+ name: string;
6
+ };
7
+ user: {
8
+ id: string;
9
+ email: string | null;
10
+ };
11
+ capturedAt: string;
12
+ };
1
13
  export type StoredCredentials = {
2
14
  token: string;
3
15
  apiUrl: string;
16
+ identity?: StoredProfileIdentity;
4
17
  };
5
18
  export type CredentialProfile = StoredCredentials & {
6
19
  name: string;
@@ -23,5 +36,17 @@ export declare function clearCredentials(env?: NodeJS.ProcessEnv, options?: {
23
36
  remainingProfiles: number;
24
37
  }>;
25
38
  export declare function listCredentialProfiles(env?: NodeJS.ProcessEnv): Promise<CredentialProfilesState>;
39
+ export type ProfileResolution = {
40
+ name: string;
41
+ source: "env" | "file" | "default";
42
+ credentials: StoredCredentials | null;
43
+ exists: boolean;
44
+ };
45
+ export declare function resolveActiveProfile(env?: NodeJS.ProcessEnv): Promise<ProfileResolution>;
46
+ export declare function findProfileNameByOrganizationId(organizationId: string, env?: NodeJS.ProcessEnv): Promise<string | null>;
47
+ export declare function pickProfileNameForIdentity(organizationId: string, candidateSeed: string, env?: NodeJS.ProcessEnv): Promise<{
48
+ name: string;
49
+ renamed: boolean;
50
+ }>;
26
51
  export declare function switchCredentialProfile(profile: string, env?: NodeJS.ProcessEnv): Promise<CredentialProfile>;
27
52
  export declare function normalizeApiUrl(value: string): string;
@@ -43,9 +43,17 @@ export async function saveCredentials(credentials, env = process.env, options =
43
43
  token: credentials.token,
44
44
  apiUrl: normalizeApiUrl(credentials.apiUrl),
45
45
  };
46
+ if (credentials.identity)
47
+ normalized.identity = credentials.identity;
46
48
  const existing = await readSystemCredentialDocument(env);
47
49
  const profile = normalizeProfileName(options.profile ?? readEnvProfile(env) ?? existing?.activeProfile ?? DEFAULT_PROFILE_NAME);
48
50
  const profiles = existing ? readProfiles(existing) : {};
51
+ if (!normalized.identity) {
52
+ const prior = profiles[profile];
53
+ if (prior?.identity && prior.token === normalized.token) {
54
+ normalized.identity = prior.identity;
55
+ }
56
+ }
49
57
  profiles[profile] = normalized;
50
58
  const activeProfile = options.activate === false
51
59
  ? readProfileName(existing?.activeProfile) ?? profile
@@ -116,6 +124,50 @@ export async function listCredentialProfiles(env = process.env) {
116
124
  }),
117
125
  };
118
126
  }
127
+ export async function resolveActiveProfile(env = process.env) {
128
+ const envProfile = readEnvProfile(env);
129
+ const document = await readSystemCredentialDocument(env);
130
+ const profiles = document ? readProfiles(document) : {};
131
+ const fileActive = readProfileName(document?.activeProfile);
132
+ const candidate = envProfile ?? fileActive ?? DEFAULT_PROFILE_NAME;
133
+ const credentials = profiles[candidate] ?? null;
134
+ const source = envProfile
135
+ ? "env"
136
+ : fileActive
137
+ ? "file"
138
+ : "default";
139
+ return { name: candidate, source, credentials, exists: Boolean(credentials) };
140
+ }
141
+ export async function findProfileNameByOrganizationId(organizationId, env = process.env) {
142
+ const document = await readSystemCredentialDocument(env);
143
+ if (!document)
144
+ return null;
145
+ const profiles = readProfiles(document);
146
+ for (const [name, credentials] of Object.entries(profiles)) {
147
+ if (credentials.identity?.organization.id === organizationId)
148
+ return name;
149
+ }
150
+ return null;
151
+ }
152
+ export async function pickProfileNameForIdentity(organizationId, candidateSeed, env = process.env) {
153
+ const document = await readSystemCredentialDocument(env);
154
+ const profiles = document ? readProfiles(document) : {};
155
+ const existingForOrg = Object.entries(profiles).find(([, credentials]) => credentials.identity?.organization.id === organizationId);
156
+ if (existingForOrg)
157
+ return { name: existingForOrg[0], renamed: false };
158
+ const base = normalizeProfileName(candidateSeed);
159
+ if (!profiles[base])
160
+ return { name: base, renamed: false };
161
+ for (let suffix = 2; suffix < 1000; suffix++) {
162
+ const next = `${base}-${suffix}`;
163
+ if (!profiles[next])
164
+ return { name: next, renamed: true };
165
+ }
166
+ throw new OxygenError("profile_collision", "Unable to derive a unique CLI profile name.", {
167
+ details: { base },
168
+ exitCode: 1,
169
+ });
170
+ }
119
171
  export async function switchCredentialProfile(profile, env = process.env) {
120
172
  const document = await readSystemCredentialDocument(env);
121
173
  if (!document) {
@@ -231,6 +283,8 @@ async function writeCredentialDocument(input, env) {
231
283
  activeProfile: input.activeProfile,
232
284
  profiles: input.profiles,
233
285
  };
286
+ if (activeCredentials.identity)
287
+ document.identity = activeCredentials.identity;
234
288
  await writeSystemCredentialPayload(serializeCredentialDocument(document), env);
235
289
  }
236
290
  function credentialStore(env) {
@@ -392,7 +446,7 @@ async function deleteWindowsCredentialManager(env) {
392
446
  [void][OxygenCredMan]::CredDelete("${SERVICE_NAME}", 1, 0)
393
447
  `, env).catch(() => undefined);
394
448
  }
395
- async function runPowerShell(script, env) {
449
+ function runPowerShell(script, env) {
396
450
  const encodedCommand = Buffer.from(script, "utf16le").toString("base64");
397
451
  return execFileAsync(windowsPowerShellCommand(env), [
398
452
  "-NoProfile",
@@ -516,16 +570,60 @@ function parseStoredCredentialsObject(value) {
516
570
  return null;
517
571
  if (typeof apiUrl !== "string" || !apiUrl.trim())
518
572
  return null;
519
- return { token: token.trim(), apiUrl: normalizeApiUrl(apiUrl) };
573
+ const credentials = {
574
+ token: token.trim(),
575
+ apiUrl: normalizeApiUrl(apiUrl),
576
+ };
577
+ const identity = parseStoredProfileIdentity(value.identity);
578
+ if (identity)
579
+ credentials.identity = identity;
580
+ return credentials;
581
+ }
582
+ function parseStoredProfileIdentity(value) {
583
+ if (!value || typeof value !== "object" || Array.isArray(value))
584
+ return null;
585
+ const record = value;
586
+ const org = record.organization;
587
+ const user = record.user;
588
+ if (!org || typeof org !== "object" || Array.isArray(org))
589
+ return null;
590
+ if (!user || typeof user !== "object" || Array.isArray(user))
591
+ return null;
592
+ const orgRecord = org;
593
+ const userRecord = user;
594
+ const orgId = typeof orgRecord.id === "string" ? orgRecord.id.trim() : "";
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;
601
+ const userId = typeof userRecord.id === "string" ? userRecord.id.trim() : "";
602
+ if (!userId)
603
+ return null;
604
+ const userEmail = typeof userRecord.email === "string" && userRecord.email.trim()
605
+ ? userRecord.email.trim()
606
+ : null;
607
+ const capturedAt = typeof record.capturedAt === "string" && record.capturedAt.trim()
608
+ ? record.capturedAt.trim()
609
+ : new Date(0).toISOString();
610
+ return {
611
+ organization: { id: orgId, slug: orgSlug, name: orgName },
612
+ user: { id: userId, email: userEmail },
613
+ capturedAt,
614
+ };
520
615
  }
521
616
  function readProfiles(document) {
522
617
  const profiles = {};
523
618
  const rawProfiles = document.profiles ?? {};
524
619
  for (const [name, credentials] of Object.entries(rawProfiles)) {
525
- profiles[normalizeProfileName(name)] = {
620
+ const normalized = {
526
621
  token: credentials.token,
527
622
  apiUrl: normalizeApiUrl(credentials.apiUrl),
528
623
  };
624
+ if (credentials.identity)
625
+ normalized.identity = credentials.identity;
626
+ profiles[normalizeProfileName(name)] = normalized;
529
627
  }
530
628
  if (Object.keys(profiles).length === 0) {
531
629
  const defaultCredentials = parseStoredCredentialsObject(document);