@optimuslabs/harness-map-staging 1.5.28 → 1.5.29

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.
@@ -185,6 +185,11 @@ export function cursorTeamViolation(approved, signedIn) {
185
185
  * the ChatGPT account is the person's own, so the login is keyed by email domain instead.
186
186
  */
187
187
  const CODEX_PERSONAL_PLANS = new Set(['free', 'go', 'plus']);
188
+ /**
189
+ * Codex work plans, mirroring the backend's `CodexPlans.WORK`: only these are a shared workspace.
190
+ * Any other plan with a ChatGPT login (Pro, or one not recognised) is a personal account too.
191
+ */
192
+ const CODEX_WORK_PLANS = new Set(['business', 'hc', 'enterprise', 'edu', 'education']);
188
193
  /** Codex's login from ~/.codex/auth.json (claims only, never tokens), or null when there is none. */
189
194
  export function readCodexSignedIn(home = homedir()) {
190
195
  const identity = readCodexIdentity(home);
@@ -192,19 +197,24 @@ export function readCodexSignedIn(home = homedir()) {
192
197
  }
193
198
  /**
194
199
  * The tenant a Codex login is in, mirroring the backend's `codex_tenants`: its ChatGPT workspace
195
- * (`chatgpt_account_id`, the claim Codex's own `forced_chatgpt_workspace_id` checks), or
196
- * `@<email domain>` on a personal plan. Null when there is no ChatGPT login (an API key).
200
+ * (`chatgpt_account_id`, the claim Codex's own `forced_chatgpt_workspace_id` checks) on a work plan,
201
+ * else `@<email domain>`, a personal account. Null when there is no ChatGPT login (an API key).
197
202
  */
198
203
  export function codexTenants(signedIn) {
199
204
  const plan = (signedIn.plan ?? '').trim().toLowerCase();
200
- if (CODEX_PERSONAL_PLANS.has(plan)) {
201
- const email = (signedIn.email ?? '').trim();
202
- const at = email.lastIndexOf('@');
203
- return at >= 0 && at < email.length - 1 ? [`@${email.slice(at + 1).toLowerCase()}`] : [];
204
- }
205
+ if (CODEX_PERSONAL_PLANS.has(plan))
206
+ return codexEmailDomainTenant(signedIn);
205
207
  const account = (signedIn.account_id ?? '').trim();
206
208
  // The collector falls back to the API organization ("org-...") with no ChatGPT account.
207
- return account && !account.startsWith('org-') ? [account] : null;
209
+ if (!account || account.startsWith('org-'))
210
+ return null;
211
+ return CODEX_WORK_PLANS.has(plan) ? [account] : codexEmailDomainTenant(signedIn);
212
+ }
213
+ /** `@<email domain>` for a Codex login, or [] when it has no email: how a personal account is keyed. */
214
+ function codexEmailDomainTenant(signedIn) {
215
+ const email = (signedIn.email ?? '').trim();
216
+ const at = email.lastIndexOf('@');
217
+ return at >= 0 && at < email.length - 1 ? [`@${email.slice(at + 1).toLowerCase()}`] : [];
208
218
  }
209
219
  /** The block message when Codex's login may not be used, else null. */
210
220
  export function codexTenantViolation(approved, signedIn) {
@@ -126,11 +126,17 @@ function claudeJsonDetails(home, account, orgUuids) {
126
126
  const name = text(oauth.organizationName);
127
127
  if (name)
128
128
  details.orgNames[org] = name;
129
- const plan = readClaudeLoginPlan(home) || claudeCodePlan(claudeJson, oauth);
129
+ // The org's own type first: the login token's plan describes whichever org the token is for now,
130
+ // which is not this one while ~/.claude.json still caches the org from before a move to a team.
131
+ const plan = claudeOrgPlan(oauth) || readClaudeLoginPlan(home) || claudeCodePlan(claudeJson, oauth);
130
132
  if (plan)
131
133
  details.orgPlans[org] = plan;
132
134
  return details;
133
135
  }
136
+ /** The plan of the org `oauthAccount` names, fetched with it: its type, else its seat tier. */
137
+ function claudeOrgPlan(oauth) {
138
+ return text(oauth.organizationType) || text(oauth.seatTier);
139
+ }
134
140
  /** Raw, in the order the backend reads Claude Code's plan, so both normalise alike. */
135
141
  function claudeCodePlan(claudeJson, oauth) {
136
142
  return (text(claudeJson?.subscriptionType) ||
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@optimuslabs/harness-map-staging",
3
- "version": "1.5.28",
3
+ "version": "1.5.29",
4
4
  "description": "CLI helpers for logging hardware UUIDs and posting startup payloads to Optimus Security.",
5
5
  "type": "module",
6
6
  "bin": {