@optimuslabs/harness-map-staging 1.5.35 → 1.5.36

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.
@@ -87,7 +87,7 @@ export function readClaudeSignedInTenant(env = process.env) {
87
87
  /**
88
88
  * Block messages, one shape for every agent (Optimus Labs' prompt-block wording):
89
89
  * "This device only allows <agent> <under/on/in> the <approved> <kind>. You're signed in to
90
- * <what>. <how to switch>." With nothing approved yet: "This device doesn't allow <agent> signed in
90
+ * <what>. <how to switch>." With nothing approved yet: "This device doesn't allow <agent> to be signed in
91
91
  * to <what>." and the same last sentence.
92
92
  */
93
93
  const TENANT_WORDS = {
@@ -96,6 +96,8 @@ const TENANT_WORDS = {
96
96
  cursor: { name: 'Cursor', kind: 'team', prep: 'on' },
97
97
  codex: { name: 'Codex', kind: 'workspace', prep: 'in' },
98
98
  };
99
+ /** How a block that names the organization's own rule begins; such a block needs no title above it. */
100
+ export const ORGANIZATION_LEAD = 'Your organization';
99
101
  /** A tenant's name as the admin approved it, or its id when it has none. */
100
102
  function nameOf(t) {
101
103
  return (t.tenant_name || '').trim() || t.tenant_id.trim();
@@ -108,35 +110,54 @@ function emailDomain(email) {
108
110
  function capitalised(word) {
109
111
  return word ? word[0].toUpperCase() + word.slice(1) : word;
110
112
  }
111
- /** What to pick to get back in: the one approved tenant, one of several, or the company's own. */
112
- function choice(approved, kind) {
113
+ /**
114
+ * What to pick to get back in: the one approved tenant, one of several, or the company's own,
115
+ * named after `company` when it is known ("your Optimus Labs organization").
116
+ */
117
+ function choice(approved, kind, company) {
113
118
  if (approved.length === 1)
114
119
  return approved[0];
115
- return approved.length > 1 ? 'one of them' : `your company's ${kind}`;
120
+ return approved.length > 1 ? 'one of them' : `your ${company || "company's"} ${kind}`;
116
121
  }
117
- function switchStep(agent, approved) {
122
+ function switchStep(agent, approved, company) {
118
123
  const kind = TENANT_WORDS[agent].kind;
119
124
  if (agent === 'claude_desktop')
120
- return `Switch Claude Desktop to ${choice(approved, kind)}.`;
121
- if (agent === 'codex')
122
- return `Run codex logout, then codex login and choose ${choice(approved, kind)}.`;
125
+ return `Switch Claude Desktop to ${choice(approved, kind, company)}.`;
126
+ if (agent === 'codex') {
127
+ return `Run codex logout, then codex login and choose ${choice(approved, kind, company)}.`;
128
+ }
123
129
  if (agent === 'cursor') {
124
- if (approved.length === 1)
125
- return `Sign out of Cursor and sign in with your ${approved[0]} team account.`;
130
+ // Reached only without one account to name (see cursorTeamViolation).
126
131
  return approved.length > 1
127
132
  ? 'Sign out of Cursor and sign in with an account on one of those teams.'
128
133
  : "Sign out of Cursor and sign in with your company's team account.";
129
134
  }
130
- return `Run /login and choose ${choice(approved, kind)}.`;
135
+ return `Run /login and choose ${choice(approved, kind, company)}.`;
131
136
  }
132
- /** The block message for `agent` signed in to `signedIn` (already worded) with these approved. */
133
- export function tenantBlockMessage(agent, approved, signedIn) {
137
+ /**
138
+ * The block message for `agent` signed in to `signedIn` (already worded) with these approved.
139
+ * `company` names the company when the agent has nothing approved of its own; '' says "company's".
140
+ */
141
+ export function tenantBlockMessage(agent, approved, signedIn, company = '') {
134
142
  const w = TENANT_WORDS[agent];
135
143
  const lead = approved.length
136
144
  ? `This device only allows ${w.name} ${w.prep} the ${approved.join(' or ')} ${w.kind}. ` +
137
145
  `You're signed in to ${signedIn}.`
138
- : `This device doesn't allow ${w.name} signed in to ${signedIn}.`;
139
- return `${lead} ${switchStep(agent, approved)}`;
146
+ : `This device doesn't allow ${w.name} to be signed in to ${signedIn}.`;
147
+ return `${lead} ${switchStep(agent, approved, company)}`;
148
+ }
149
+ /**
150
+ * The company's name for "your <company> team account": the one name its approved tenants go by,
151
+ * across every agent (a Cursor login on no approved team is pointed at the company's Claude
152
+ * organization's name). '' when there is none, or several. Personal accounts (`@<domain>`) and a
153
+ * person's own org ("ada@gmail.com's Organization") are not the company's.
154
+ */
155
+ export function companyName(approved) {
156
+ const names = new Set(Object.values(approved ?? {})
157
+ .flatMap((policy) => tenantIds(policy?.tenants))
158
+ .filter((t) => !t.tenant_id.trim().startsWith('@') && !/'s organization$/i.test(nameOf(t)))
159
+ .map(nameOf));
160
+ return names.size === 1 ? [...names][0] : '';
140
161
  }
141
162
  const CLAUDE_PERSONAL_PLANS = new Set(['free', 'pro', 'max']);
142
163
  /** "a personal Max account (gmail.com)" or "the Beta Corp organization". */
@@ -159,23 +180,18 @@ function tenantIds(tenants) {
159
180
  /**
160
181
  * The block message when `signedIn` may not be used for `agent`, else null.
161
182
  *
162
- * Mirrors the backend's `tenant_approval`: a tenant the organization marked unapproved is always
163
- * blocked; a shadow tenant (never decided) only once the agent has at least one approved tenant.
164
- * Ids compare exactly, as the backend does -- vendor ids are not ours to case-fold.
183
+ * Only a tenant the organization blocked (marked unapproved) is refused. One never decided is
184
+ * awaiting review and gets through, as an approved one does. Ids compare exactly, as the backend
185
+ * does -- vendor ids are not ours to case-fold.
165
186
  */
166
187
  export function approvedTenantViolation(agent, approved, signedIn) {
167
188
  if (!signedIn)
168
189
  return null;
169
190
  const policy = approved?.[agent];
170
- const approvedTenants = tenantIds(policy?.tenants);
171
- const unapprovedTenants = tenantIds(policy?.unapproved_tenants);
172
191
  const signedInId = signedIn.tenant_id.trim();
173
- const isIn = (list) => list.some((t) => t.tenant_id.trim() === signedInId);
174
- const blocked = isIn(unapprovedTenants) ||
175
- ((policy?.enforced || approvedTenants.length > 0) && !isIn(approvedTenants));
176
- if (!blocked)
192
+ if (!tenantIds(policy?.unapproved_tenants).some((t) => t.tenant_id.trim() === signedInId))
177
193
  return null;
178
- return tenantBlockMessage(agent, approvedTenants.map(nameOf), claudeSignedInText(signedIn));
194
+ return tenantBlockMessage(agent, tenantIds(policy?.tenants).map(nameOf), claudeSignedInText(signedIn), companyName(approved));
179
195
  }
180
196
  /**
181
197
  * The tenants a Cursor login is in: its teams, or, on no team, `@` and its email domain. Mirrors the
@@ -203,42 +219,30 @@ export function readCursorSignedInTeams(home = homedir()) {
203
219
  /**
204
220
  * Why a login in these tenants may not be used, or null when it may. Shared by every agent whose
205
221
  * login is a list of tenants (Cursor's teams) or one tenant that may be a personal account's
206
- * `@<domain>` (Codex), and mirrors the backend's `team_tenant_approval`: any approved tenant admits
207
- * the login; failing that, one marked unapproved is always blocked, and any other -- an unknown
208
- * tenant, or a personal account -- only once at least one tenant is approved for this agent.
222
+ * `@<domain>` (Codex). Blocked only when every tenant the login is in was blocked (marked
223
+ * unapproved): Cursor admits a login when any of its teams is allowed, and a tenant awaiting review
224
+ * is allowed, as an approved one is.
209
225
  */
210
226
  function listedTenantViolation(policy, tenants) {
211
- const approvedList = tenantIds(policy?.tenants);
212
- if (tenants.some((id) => isListed(approvedList, id)))
227
+ const unapprovedList = tenantIds(policy?.unapproved_tenants);
228
+ if (!tenants.length || !tenants.every((id) => isListed(unapprovedList, id)))
213
229
  return null;
214
- const enforced = approvedList.length > 0 || Boolean(policy?.enforced);
215
- if (!isRefused(tenants, enforced, tenantIds(policy?.unapproved_tenants)))
216
- return null;
217
- return { approved: approvedList.map(nameOf) };
230
+ return { approved: tenantIds(policy?.tenants).map(nameOf) };
218
231
  }
219
232
  function isListed(list, id) {
220
233
  return list.some((t) => t.tenant_id.trim() === id);
221
234
  }
222
- /** Whether a login in these tenants is refused: one marked unapproved always; any other only once
223
- * the agent is enforced (an older backend that sends no flag, with nothing approved, lets it pass). */
224
- function isRefused(tenants, enforced, unapprovedList) {
225
- if (tenants.some((id) => isListed(unapprovedList, id)))
226
- return true;
227
- return enforced;
228
- }
229
235
  /** A tenant's name from the organization's lists, or '' when it has none there. */
230
236
  function knownName(policy, id) {
231
237
  const all = [...tenantIds(policy?.tenants), ...tenantIds(policy?.unapproved_tenants)];
232
238
  const hit = all.find((t) => t.tenant_id.trim() === id);
233
239
  return (hit?.tenant_name || '').trim();
234
240
  }
235
- /** "a personal account on no team (gmail.com)", "the Beta team" or "team 9876". */
241
+ /** "a personal account", "the Beta team" or "team 9876". */
236
242
  function cursorSignedInText(signedIn, policy) {
237
243
  const tenants = cursorTenants(signedIn);
238
- if (!tenants.length || tenants[0].startsWith('@')) {
239
- const domain = emailDomain(signedIn.email) || (tenants[0] ?? '').slice(1);
240
- return `a personal account on no team${domain ? ` (${domain})` : ''}`;
241
- }
244
+ if (!tenants.length || tenants[0].startsWith('@'))
245
+ return 'a personal account';
242
246
  return tenants
243
247
  .map((id) => (knownName(policy, id) ? `the ${knownName(policy, id)} team` : `team ${id}`))
244
248
  .join(' and ');
@@ -255,13 +259,22 @@ function codexSignedInText(signedIn, tenants, policy) {
255
259
  return name ? `the ${name} workspace` : `workspace ${tenant}`;
256
260
  }
257
261
  /** The block message when Cursor's login may not be used, else null. */
258
- export function cursorTeamViolation(approved, signedIn) {
262
+ export function cursorTeamViolation(approved, signedIn, teamDomains = []) {
259
263
  if (!signedIn)
260
264
  return null;
261
265
  const v = listedTenantViolation(approved?.cursor, cursorTenants(signedIn));
262
266
  if (!v)
263
267
  return null;
264
- return tenantBlockMessage('cursor', v.approved, cursorSignedInText(signedIn, approved?.cursor));
268
+ const signedInText = cursorSignedInText(signedIn, approved?.cursor);
269
+ // The one account to point at: the approved team, else the company's name. Several approved
270
+ // teams, or no name known, keep the generic message.
271
+ const account = v.approved.length === 1 ? v.approved[0] : v.approved.length ? '' : companyName(approved);
272
+ if (!account)
273
+ return tenantBlockMessage('cursor', v.approved, signedInText);
274
+ const domains = teamDomains.length ? ` (${teamDomains.join(', ')})` : '';
275
+ return (`${ORGANIZATION_LEAD} does not allow Cursor to be signed in to ${signedInText}. ` +
276
+ `The approved organization's team account for Cursor is ${account}${domains}. ` +
277
+ 'Please sign out and sign in with the approved account to use Cursor.');
265
278
  }
266
279
  /**
267
280
  * Codex plans that are personal, mirroring the backend's `CodexPlans.PERSONAL`. On one of these
@@ -307,34 +320,7 @@ export function codexTenantViolation(approved, signedIn) {
307
320
  const v = listedTenantViolation(approved?.codex, tenants);
308
321
  if (!v)
309
322
  return null;
310
- return tenantBlockMessage('codex', v.approved, codexSignedInText(signedIn, tenants, approved?.codex));
311
- }
312
- /**
313
- * Whether a personal account's `@<domain>` tenant is on a team domain: an allowlisted domain that
314
- * someone on an approved team or enterprise tenant signs in with (any vendor's). A subdomain counts.
315
- */
316
- export function onTeamDomain(tenant, teamDomains) {
317
- if (!tenant.startsWith('@') || !teamDomains?.length)
318
- return false;
319
- const domain = tenant.slice(1);
320
- return teamDomains.some((d) => domain === d || domain.endsWith(`.${d}`));
321
- }
322
- /**
323
- * The block message for Cursor's login, else null. A login on a team is checked against the
324
- * approved teams. A personal account is only checked once its email is on a team domain; then it
325
- * is blocked unless an admin approved that personal tenant (`@<domain>`).
326
- */
327
- export function cursorLoginViolation(approved, signedIn, teamDomains) {
328
- if (!signedIn)
329
- return null;
330
- const checked = signedIn.team_ids.length > 0 || cursorTenants(signedIn).some((t) => onTeamDomain(t, teamDomains));
331
- return checked ? cursorTeamViolation(approved, signedIn) : null;
332
- }
333
- /** The block message for Codex's login, else null; a personal plan is checked as Cursor's is. */
334
- export function codexLoginViolation(approved, signedIn, teamDomains) {
335
- const tenants = signedIn ? codexTenants(signedIn) : null;
336
- const checked = tenants?.some((t) => !t.startsWith('@') || onTeamDomain(t, teamDomains));
337
- return checked ? codexTenantViolation(approved, signedIn) : null;
323
+ return tenantBlockMessage('codex', v.approved, codexSignedInText(signedIn, tenants, approved?.codex), companyName(approved));
338
324
  }
339
325
  /**
340
326
  * The org Claude Desktop is signed into now, for its Code tab's prompts, or null when unknown.
@@ -378,26 +364,6 @@ export function unapprovedEmailDomainViolation(email, unapproved) {
378
364
  return (`This device doesn't allow accounts on ${hit}. You're signed in as ${address}. ` +
379
365
  'Sign in with your work account.');
380
366
  }
381
- /**
382
- * The block message when a login's email is not on the organization's email allowlist, else null.
383
- *
384
- * The allowlist is the organization's approved email domains; a domain covers its subdomains
385
- * (`mail.acme.com` under `acme.com`). Null, so the login is allowed, when no allowlist has been
386
- * synced to this machine yet or the login has no email we can read.
387
- */
388
- export function emailAllowlistViolation(email, allowed) {
389
- if (!Array.isArray(allowed))
390
- return null;
391
- const address = (email ?? '').trim().toLowerCase();
392
- const at = address.lastIndexOf('@');
393
- if (at < 0 || at === address.length - 1)
394
- return null;
395
- const domain = address.slice(at + 1);
396
- if (allowed.some((d) => domain === d || domain.endsWith(`.${d}`)))
397
- return null;
398
- return (`This device only allows accounts on your organization's email domains. You're signed in as ${address}. ` +
399
- 'Sign in with your work account.');
400
- }
401
367
  /** The id an agent is approved under on the Agents page: Desktop's Code tab runs Claude Code. */
402
368
  const APPROVAL_ID = { claude_desktop: 'claude' };
403
369
  const AGENT_NAME = {
@@ -24,7 +24,7 @@ import { tryResolveHardwareUuid } from './log_config_files/runtime/hardware_uuid
24
24
  import { cursorAutofixDefersInlineVerify, syncRemediations, } from './log_config_files/runtime/remediation_sync.js';
25
25
  import { loadEndpointBase } from './log_config_files/sender/endpoint_config.js';
26
26
  import { formatRemediationChangePreviewForApplied } from './remediation_change_preview.js';
27
- import { agentApprovalViolation, approvedTenantViolation, codexLoginViolation, emailAllowlistViolation, cursorLoginViolation, readClaudeDesktopSignedInTenant, readClaudeSignedInTenant, readCodexSignedIn, readCursorSignedInTeams, } from './approved_tenant_gate.js';
27
+ import { agentApprovalViolation, approvedTenantViolation, codexTenantViolation, cursorTeamViolation, ORGANIZATION_LEAD, readClaudeDesktopSignedInTenant, readClaudeSignedInTenant, readCodexSignedIn, readCursorSignedInTeams, unapprovedEmailDomainViolation, } from './approved_tenant_gate.js';
28
28
  const MANIFEST_STALE_MS = 7 * 24 * 60 * 60 * 1000;
29
29
  /** After verified (or failed verify), push enforcement + session log immediately — do not wait for background runner. */
30
30
  async function flushRemediationOutcomeToServer(complianceLogPath) {
@@ -89,8 +89,12 @@ function printAllowWithAdvisory(ide, advisoryMessage) {
89
89
  }
90
90
  /** Every block reads as an Optimus Labs notice: a title line, then the message. */
91
91
  const BLOCK_TITLE = 'Optimus Labs\n\n';
92
+ /** The block as shown: titled, unless it already says it is the organization's rule. */
93
+ function titled(violationMessage) {
94
+ return violationMessage.startsWith(ORGANIZATION_LEAD) ? violationMessage : BLOCK_TITLE + violationMessage;
95
+ }
92
96
  function blockPayload(ide, violationMessage) {
93
- const text = BLOCK_TITLE + violationMessage;
97
+ const text = titled(violationMessage);
94
98
  if (ide === 'claude' || ide === 'copilot' || ide === 'opencode') {
95
99
  return JSON.stringify({ decision: 'block', reason: text, systemMessage: text });
96
100
  }
@@ -101,7 +105,7 @@ function blockPayload(ide, violationMessage) {
101
105
  * warning, so the Claude-shaped payload (which carries both) prints the same sentence twice.
102
106
  */
103
107
  function codexBlockPayload(violationMessage) {
104
- return JSON.stringify({ decision: 'block', reason: BLOCK_TITLE + violationMessage });
108
+ return JSON.stringify({ decision: 'block', reason: titled(violationMessage) });
105
109
  }
106
110
  function getManifestStalenessMs() {
107
111
  try {
@@ -264,7 +268,7 @@ function loginEmail(agent) {
264
268
  return readCursorSignedInTeams()?.email ?? '';
265
269
  return null;
266
270
  }
267
- /** Why this agent's signed-in tenant is not approved, or null when it is (or cannot be read). */
271
+ /** Why this agent's signed-in tenant is blocked, or null when it is not (or cannot be read). */
268
272
  function tenantViolation(agent) {
269
273
  const { approved_tenants: approved, team_email_domains: teamDomains } = readRemediationInstructionsFile();
270
274
  if (agent === 'claude')
@@ -272,12 +276,10 @@ function tenantViolation(agent) {
272
276
  if (agent === 'claude_desktop') {
273
277
  return approvedTenantViolation('claude_desktop', approved, readClaudeDesktopSignedInTenant());
274
278
  }
275
- // A personal account (Cursor on no team, Codex on a personal plan) is left to the email allowlist
276
- // until its domain is a team domain; then it needs its personal tenant approved.
277
279
  if (agent === 'codex')
278
- return codexLoginViolation(approved, readCodexSignedIn(), teamDomains);
280
+ return codexTenantViolation(approved, readCodexSignedIn());
279
281
  if (agent === 'cursor')
280
- return cursorLoginViolation(approved, readCursorSignedInTeams(), teamDomains);
282
+ return cursorTeamViolation(approved, readCursorSignedInTeams(), teamDomains);
281
283
  return null;
282
284
  }
283
285
  /** The block for a login: which check refused it and why, or null when every check passes. */
@@ -288,16 +290,16 @@ function loginBlock(agent, email) {
288
290
  return { event: 'prompt_gate_block_unapproved_agent', reason: agentMsg };
289
291
  if (email === null)
290
292
  return null;
291
- const emailMsg = emailAllowlistViolation(email, synced.allowed_email_domains);
293
+ const emailMsg = unapprovedEmailDomainViolation(email, synced.unapproved_email_domains);
292
294
  if (emailMsg)
293
- return { event: 'prompt_gate_block_email_not_allowlisted', reason: emailMsg };
295
+ return { event: 'prompt_gate_block_unapproved_email_domain', reason: emailMsg };
294
296
  const tenantMsg = tenantViolation(agent);
295
297
  return tenantMsg ? { event: 'prompt_gate_block_unapproved_tenant', reason: tenantMsg } : null;
296
298
  }
297
299
  /**
298
- * Block the prompt unless the agent is approved, the login's email is on the organization's email
299
- * allowlist and its tenant is approved (all as remediation sync stored them), printing the block
300
- * payload; true when it did. A login whose email or tenant cannot be read passes that check, and a
300
+ * Block the prompt when the agent is not approved, or the login's email domain or its tenant was
301
+ * blocked (all as remediation sync stored them), printing the block payload; true when it did. A
302
+ * domain or tenant awaiting review gets through. A login whose email or tenant cannot be read passes that check, and a
301
303
  * machine that has synced nothing yet is not blocked.
302
304
  */
303
305
  function blockedByLogin(agent, ide) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@optimuslabs/harness-map-staging",
3
- "version": "1.5.35",
3
+ "version": "1.5.36",
4
4
  "description": "CLI helpers for logging hardware UUIDs and posting startup payloads to Optimus Security.",
5
5
  "type": "module",
6
6
  "bin": {