codexuse-cli 3.5.6 → 3.6.0

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/dist/index.js CHANGED
@@ -2102,6 +2102,7 @@ var APP_STORAGE_DB_NAME = "state.sqlite";
2102
2102
  var SQLITE_BUSY_TIMEOUT_MS = 5e3;
2103
2103
  var SQLITE_BUSY_MAX_ATTEMPTS = 3;
2104
2104
  var SQLITE_BUSY_RETRY_DELAY_MS = 100;
2105
+ var writeQueueByDbPath = /* @__PURE__ */ new Map();
2105
2106
  var initializedDbPaths = /* @__PURE__ */ new Set();
2106
2107
  function isRecord(value) {
2107
2108
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
@@ -2142,6 +2143,23 @@ function rollback(db) {
2142
2143
  } catch {
2143
2144
  }
2144
2145
  }
2146
+ async function withWriteQueue(dbPath, task) {
2147
+ const previous = writeQueueByDbPath.get(dbPath) ?? Promise.resolve();
2148
+ let release;
2149
+ const current = new Promise((resolve) => {
2150
+ release = resolve;
2151
+ });
2152
+ writeQueueByDbPath.set(dbPath, current);
2153
+ await previous;
2154
+ try {
2155
+ return await task();
2156
+ } finally {
2157
+ release?.();
2158
+ if (writeQueueByDbPath.get(dbPath) === current) {
2159
+ writeQueueByDbPath.delete(dbPath);
2160
+ }
2161
+ }
2162
+ }
2145
2163
  function applyConnectionPragmas(db) {
2146
2164
  db.exec(`PRAGMA busy_timeout = ${SQLITE_BUSY_TIMEOUT_MS};`);
2147
2165
  db.exec("PRAGMA journal_mode = WAL;");
@@ -2213,56 +2231,62 @@ async function readDocument(dbPath, namespace, normalize) {
2213
2231
  });
2214
2232
  }
2215
2233
  async function writeDocument(dbPath, namespace, value) {
2216
- return withDatabase(dbPath, (db) => {
2217
- const normalizedValue = clone(value);
2218
- beginImmediate(db);
2219
- try {
2220
- db.prepare(
2221
- `
2222
- INSERT INTO ${APP_STORAGE_TABLE} (namespace, value_json, updated_at)
2223
- VALUES (?, ?, ?)
2224
- ON CONFLICT(namespace)
2225
- DO UPDATE SET
2226
- value_json = excluded.value_json,
2227
- updated_at = excluded.updated_at
2228
- `
2229
- ).run(namespace, JSON.stringify(normalizedValue), (/* @__PURE__ */ new Date()).toISOString());
2230
- commit(db);
2231
- return normalizedValue;
2232
- } catch (error) {
2233
- rollback(db);
2234
- throw error;
2235
- }
2236
- });
2234
+ return withWriteQueue(
2235
+ dbPath,
2236
+ async () => withDatabase(dbPath, (db) => {
2237
+ const normalizedValue = clone(value);
2238
+ beginImmediate(db);
2239
+ try {
2240
+ db.prepare(
2241
+ `
2242
+ INSERT INTO ${APP_STORAGE_TABLE} (namespace, value_json, updated_at)
2243
+ VALUES (?, ?, ?)
2244
+ ON CONFLICT(namespace)
2245
+ DO UPDATE SET
2246
+ value_json = excluded.value_json,
2247
+ updated_at = excluded.updated_at
2248
+ `
2249
+ ).run(namespace, JSON.stringify(normalizedValue), (/* @__PURE__ */ new Date()).toISOString());
2250
+ commit(db);
2251
+ return normalizedValue;
2252
+ } catch (error) {
2253
+ rollback(db);
2254
+ throw error;
2255
+ }
2256
+ })
2257
+ );
2237
2258
  }
2238
2259
  async function updateDocument(input) {
2239
- return withDatabase(input.dbPath, (db) => {
2240
- beginImmediate(db);
2241
- try {
2242
- const row = db.prepare(
2243
- `SELECT value_json AS valueJson FROM ${APP_STORAGE_TABLE} WHERE namespace = ?`
2244
- ).get(input.namespace);
2245
- const current = isRecord(row) ? input.normalize(
2246
- safeParseJson(row.valueJson) ?? input.fallback()
2247
- ) : input.fallback();
2248
- const next = input.normalize(input.transform(clone(current)));
2249
- db.prepare(
2250
- `
2251
- INSERT INTO ${APP_STORAGE_TABLE} (namespace, value_json, updated_at)
2252
- VALUES (?, ?, ?)
2253
- ON CONFLICT(namespace)
2254
- DO UPDATE SET
2255
- value_json = excluded.value_json,
2256
- updated_at = excluded.updated_at
2257
- `
2258
- ).run(input.namespace, JSON.stringify(next), (/* @__PURE__ */ new Date()).toISOString());
2259
- commit(db);
2260
- return clone(next);
2261
- } catch (error) {
2262
- rollback(db);
2263
- throw error;
2264
- }
2265
- });
2260
+ return withWriteQueue(
2261
+ input.dbPath,
2262
+ async () => withDatabase(input.dbPath, (db) => {
2263
+ beginImmediate(db);
2264
+ try {
2265
+ const row = db.prepare(
2266
+ `SELECT value_json AS valueJson FROM ${APP_STORAGE_TABLE} WHERE namespace = ?`
2267
+ ).get(input.namespace);
2268
+ const current = isRecord(row) ? input.normalize(
2269
+ safeParseJson(row.valueJson) ?? input.fallback()
2270
+ ) : input.fallback();
2271
+ const next = input.normalize(input.transform(clone(current)));
2272
+ db.prepare(
2273
+ `
2274
+ INSERT INTO ${APP_STORAGE_TABLE} (namespace, value_json, updated_at)
2275
+ VALUES (?, ?, ?)
2276
+ ON CONFLICT(namespace)
2277
+ DO UPDATE SET
2278
+ value_json = excluded.value_json,
2279
+ updated_at = excluded.updated_at
2280
+ `
2281
+ ).run(input.namespace, JSON.stringify(next), (/* @__PURE__ */ new Date()).toISOString());
2282
+ commit(db);
2283
+ return clone(next);
2284
+ } catch (error) {
2285
+ rollback(db);
2286
+ throw error;
2287
+ }
2288
+ })
2289
+ );
2266
2290
  }
2267
2291
 
2268
2292
  // ../../packages/runtime-app-state/src/app/state.ts
@@ -3511,6 +3535,63 @@ var import_path = __toESM(require("path"), 1);
3511
3535
  var import_path2 = require("path");
3512
3536
  var import_toml = __toESM(require_toml(), 1);
3513
3537
 
3538
+ // ../../packages/contracts/src/profiles/identity.ts
3539
+ function normalizeValue(value) {
3540
+ if (typeof value !== "string") {
3541
+ return null;
3542
+ }
3543
+ const trimmed = value.trim();
3544
+ return trimmed.length > 0 ? trimmed : null;
3545
+ }
3546
+ function normalizeEmail(value) {
3547
+ const normalized = normalizeValue(value);
3548
+ return normalized ? normalized.toLowerCase() : null;
3549
+ }
3550
+ function encodeIdentityPart(value) {
3551
+ return encodeURIComponent(value);
3552
+ }
3553
+ function resolveLegacyProfileAccountKey(profile) {
3554
+ const accountId = normalizeValue(profile.accountId);
3555
+ if (accountId) {
3556
+ return accountId;
3557
+ }
3558
+ const email = normalizeValue(profile.email ?? profile.metadata?.email);
3559
+ if (email) {
3560
+ return email;
3561
+ }
3562
+ const profileName = normalizeValue(profile.name);
3563
+ return profileName ? `profile:${profileName}` : null;
3564
+ }
3565
+ function resolveProfileIdentityKey(profile) {
3566
+ const workspaceId = normalizeValue(profile.workspaceId);
3567
+ const accountUserId = normalizeValue(profile.metadata?.chatgptAccountUserId);
3568
+ if (accountUserId) {
3569
+ return `seat:${encodeIdentityPart(accountUserId)}`;
3570
+ }
3571
+ const userId = normalizeValue(profile.metadata?.userId) ?? normalizeValue(profile.metadata?.chatgptUserId);
3572
+ if (workspaceId && userId) {
3573
+ return `workspace-user:${encodeIdentityPart(workspaceId)}|${encodeIdentityPart(userId)}`;
3574
+ }
3575
+ const accountId = normalizeValue(profile.accountId);
3576
+ if (accountId) {
3577
+ return accountId;
3578
+ }
3579
+ const email = normalizeEmail(profile.email ?? profile.metadata?.email);
3580
+ if (workspaceId && email) {
3581
+ return `workspace-email:${encodeIdentityPart(workspaceId)}|${encodeIdentityPart(email)}`;
3582
+ }
3583
+ if (userId) {
3584
+ return `user:${encodeIdentityPart(userId)}`;
3585
+ }
3586
+ if (email) {
3587
+ return `email:${encodeIdentityPart(email)}`;
3588
+ }
3589
+ return resolveLegacyProfileAccountKey(profile);
3590
+ }
3591
+ function resolveProfileIdentityKeyFromProfile(profile) {
3592
+ return resolveProfileIdentityKey(profile) ?? `profile:${profile.name}`;
3593
+ }
3594
+
3514
3595
  // ../../packages/runtime-codex/src/codex/rpc.ts
3515
3596
  var import_node_child_process2 = require("child_process");
3516
3597
  var import_node_readline = __toESM(require("readline"), 1);
@@ -4516,6 +4597,7 @@ var ProfileManager = class {
4516
4597
  groups,
4517
4598
  userId,
4518
4599
  chatgptUserId,
4600
+ chatgptAccountUserId,
4519
4601
  emailVerified,
4520
4602
  tokenExpiresAt: this.toIsoStringFromSeconds(exp),
4521
4603
  tokenIssuedAt: this.toIsoStringFromSeconds(iat),
@@ -4523,7 +4605,7 @@ var ProfileManager = class {
4523
4605
  };
4524
4606
  const hasMeaningfulData = Boolean(metadata.planType) || Boolean(
4525
4607
  metadata.subscription && (metadata.subscription.activeStart || metadata.subscription.activeUntil || metadata.subscription.lastChecked)
4526
- ) || Boolean(metadata.organizations && metadata.organizations.length > 0) || Boolean(metadata.groups && metadata.groups.length > 0) || Boolean(metadata.userId) || Boolean(metadata.chatgptUserId) || Boolean(metadata.email) || typeof metadata.emailVerified === "boolean" || Boolean(metadata.tokenExpiresAt) || Boolean(metadata.tokenIssuedAt) || Boolean(metadata.tokenAuthTime);
4608
+ ) || Boolean(metadata.organizations && metadata.organizations.length > 0) || Boolean(metadata.groups && metadata.groups.length > 0) || Boolean(metadata.userId) || Boolean(metadata.chatgptUserId) || Boolean(metadata.chatgptAccountUserId) || Boolean(metadata.email) || typeof metadata.emailVerified === "boolean" || Boolean(metadata.tokenExpiresAt) || Boolean(metadata.tokenIssuedAt) || Boolean(metadata.tokenAuthTime);
4527
4609
  return hasMeaningfulData ? metadata : void 0;
4528
4610
  }
4529
4611
  /**
@@ -4639,13 +4721,35 @@ var ProfileManager = class {
4639
4721
  return accountId.trim();
4640
4722
  }
4641
4723
  }
4642
- const projectId = "project_id" in data && typeof data.project_id === "string" ? data.project_id?.trim() : void 0;
4643
- if (projectId) {
4644
- return projectId;
4645
- }
4646
4724
  const email = "email" in data && typeof data.email === "string" ? data.email?.trim() : void 0;
4647
4725
  return email || void 0;
4648
4726
  }
4727
+ resolveProfileIdentityKey(args) {
4728
+ return resolveProfileIdentityKey(args);
4729
+ }
4730
+ resolveLegacyAccountKey(args) {
4731
+ return resolveLegacyProfileAccountKey(args);
4732
+ }
4733
+ resolveProfileIdentityFromData(name, data, metadata) {
4734
+ const resolvedMetadata = metadata ?? this.extractProfileMetadata(data);
4735
+ const workspace = this.resolveWorkspaceIdentity(data, resolvedMetadata);
4736
+ const email = this.resolveProfileEmail(data, resolvedMetadata) ?? null;
4737
+ return this.resolveProfileIdentityKey({
4738
+ name,
4739
+ accountId: this.getAccountIdFromData(data) ?? null,
4740
+ workspaceId: workspace.id ?? null,
4741
+ email,
4742
+ metadata: resolvedMetadata
4743
+ });
4744
+ }
4745
+ resolveProfileIdentityFromRecord(record) {
4746
+ return this.resolveProfileIdentityFromData(record.name, record.data, record.metadata) ?? this.resolveLegacyAccountKey({
4747
+ name: record.name,
4748
+ accountId: record.accountId,
4749
+ email: record.email,
4750
+ metadata: record.metadata
4751
+ }) ?? resolveFallbackAccountKey(record.name);
4752
+ }
4649
4753
  resolveWorkspaceIdentity(data, metadata) {
4650
4754
  const directId = "workspace_id" in data && typeof data.workspace_id === "string" ? data.workspace_id.trim() : void 0;
4651
4755
  const directName = "workspace_name" in data && typeof data.workspace_name === "string" ? data.workspace_name.trim() : void 0;
@@ -4666,12 +4770,9 @@ var ProfileManager = class {
4666
4770
  const record = await this.readProfileRecord(normalized);
4667
4771
  return record ?? void 0;
4668
4772
  }
4669
- async getProfileRowByAccountId(accountId, options) {
4773
+ async getProfileRowByIdentityKey(identityKey, options) {
4670
4774
  const records = await this.listProfileRecords();
4671
- const matches = records.filter((record) => {
4672
- const storedAccountId = record.accountId ?? this.getAccountIdFromData(record.data);
4673
- return storedAccountId === accountId;
4674
- });
4775
+ const matches = records.filter((record) => this.resolveProfileIdentityFromRecord(record) === identityKey);
4675
4776
  if (matches.length === 0) {
4676
4777
  return void 0;
4677
4778
  }
@@ -4685,12 +4786,11 @@ var ProfileManager = class {
4685
4786
  }
4686
4787
  return matches[0];
4687
4788
  }
4688
- async getProfileRowByAccountAndWorkspace(accountId, workspaceId, options) {
4789
+ async getProfileRowByIdentityAndWorkspace(identityKey, workspaceId, options) {
4689
4790
  const workspaceKey = workspaceId && workspaceId.trim().length > 0 ? workspaceId.trim() : DEFAULT_WORKSPACE_ID;
4690
4791
  const records = await this.listProfileRecords();
4691
4792
  const matches = records.filter((record) => {
4692
- const storedAccountId = record.accountId ?? this.getAccountIdFromData(record.data);
4693
- if (storedAccountId !== accountId) {
4793
+ if (this.resolveProfileIdentityFromRecord(record) !== identityKey) {
4694
4794
  return false;
4695
4795
  }
4696
4796
  const storedWorkspace = record.workspaceId ?? record.data.workspace_id ?? DEFAULT_WORKSPACE_ID;
@@ -4968,11 +5068,17 @@ var ProfileManager = class {
4968
5068
  if (latestRow) {
4969
5069
  existing = latestRow.data;
4970
5070
  }
4971
- const existingAccountId = this.getAccountIdFromData(existing);
4972
- const newAccountId = this.getAccountIdFromData(normalized);
4973
- if (existingAccountId && newAccountId && existingAccountId !== newAccountId) {
5071
+ const existingMetadata = this.extractProfileMetadata(existing);
5072
+ const existingWorkspace = this.resolveWorkspaceIdentity(existing, existingMetadata);
5073
+ existing.workspace_id = existing.workspace_id ?? existingWorkspace.id ?? DEFAULT_WORKSPACE_ID;
5074
+ if (existingWorkspace.name) {
5075
+ existing.workspace_name = existing.workspace_name ?? existingWorkspace.name;
5076
+ }
5077
+ const existingIdentity = this.resolveProfileIdentityFromData(profileName, existing, existingMetadata);
5078
+ const nextIdentity = this.resolveProfileIdentityFromData(profileName, normalized, metadata);
5079
+ if (existingIdentity && nextIdentity && existingIdentity !== nextIdentity) {
4974
5080
  logWarn(
4975
- `Skipped syncing tokens for profile '${profileName}' because Codex auth switched to account '${newAccountId}'.`
5081
+ `Skipped syncing tokens for profile '${profileName}' because Codex auth switched to a different OpenAI identity.`
4976
5082
  );
4977
5083
  return;
4978
5084
  }
@@ -5208,15 +5314,6 @@ var ProfileManager = class {
5208
5314
  if (workspace.name) {
5209
5315
  normalizedProfile.workspace_name = normalizedProfile.workspace_name ?? workspace.name;
5210
5316
  }
5211
- const accountId = this.getAccountIdFromData(normalizedProfile);
5212
- if (accountId) {
5213
- const requestedWorkspace = normalizedProfile.workspace_id ?? DEFAULT_WORKSPACE_ID;
5214
- const duplicate = await this.getProfileRowByAccountAndWorkspace(accountId, requestedWorkspace, { authMethod: "codex-cli" });
5215
- if (duplicate) {
5216
- normalizedProfile.workspace_id = `${requestedWorkspace}:${profileName}`;
5217
- normalizedProfile.workspace_name = normalizedProfile.workspace_name ?? profileName;
5218
- }
5219
- }
5220
5317
  const resolvedEmail = this.resolveProfileEmail(normalizedProfile, metadata);
5221
5318
  if (resolvedEmail) {
5222
5319
  normalizedProfile.email = resolvedEmail;
@@ -5271,25 +5368,30 @@ var ProfileManager = class {
5271
5368
  throw new Error("Failed to read Codex auth file. Complete the login and try again.");
5272
5369
  }
5273
5370
  const normalized = this.normalizeProfileData(activeAuth);
5274
- const existingAccountId = this.getAccountIdFromData(existing);
5275
- const newAccountId = this.getAccountIdFromData(normalized);
5371
+ const existingMetadata = record.metadata ?? this.extractProfileMetadata(existing);
5372
+ const currentWorkspace = this.resolveWorkspaceIdentity(existing, existingMetadata);
5373
+ existing.workspace_id = existing.workspace_id ?? currentWorkspace.id ?? DEFAULT_WORKSPACE_ID;
5374
+ if (currentWorkspace.name) {
5375
+ existing.workspace_name = existing.workspace_name ?? currentWorkspace.name;
5376
+ }
5276
5377
  const normalizedMetadata = this.extractProfileMetadata(normalized);
5277
5378
  const normalizedWorkspace = this.resolveWorkspaceIdentity(normalized, normalizedMetadata);
5278
5379
  normalized.workspace_id = normalized.workspace_id ?? normalizedWorkspace.id ?? DEFAULT_WORKSPACE_ID;
5279
5380
  if (normalizedWorkspace.name) {
5280
5381
  normalized.workspace_name = normalized.workspace_name ?? normalizedWorkspace.name;
5281
5382
  }
5282
- if (existingAccountId && newAccountId && existingAccountId !== newAccountId) {
5383
+ const existingIdentity = this.resolveProfileIdentityFromData(profileName, existing, existingMetadata);
5384
+ const newIdentity = this.resolveProfileIdentityFromData(profileName, normalized, normalizedMetadata);
5385
+ if (existingIdentity && newIdentity && existingIdentity !== newIdentity) {
5283
5386
  throw new Error(
5284
- `Active Codex login is for account '${newAccountId}', but profile '${profileName}' is tied to '${existingAccountId}'. Create a new profile for the new account instead.`
5387
+ `Active Codex login is for a different OpenAI identity than profile '${profileName}'. Create a new profile for that login instead.`
5285
5388
  );
5286
5389
  }
5287
- if (existingAccountId && normalized.workspace_id) {
5288
- const currentWorkspace = this.resolveWorkspaceIdentity(existing, this.extractProfileMetadata(existing));
5390
+ if (existingIdentity && normalized.workspace_id) {
5289
5391
  const targetWorkspaceId = normalized.workspace_id;
5290
5392
  const currentWorkspaceId = currentWorkspace.id ?? DEFAULT_WORKSPACE_ID;
5291
5393
  if (targetWorkspaceId !== currentWorkspaceId) {
5292
- const duplicate = await this.getProfileRowByAccountAndWorkspace(existingAccountId, targetWorkspaceId, {
5394
+ const duplicate = await this.getProfileRowByIdentityAndWorkspace(existingIdentity, targetWorkspaceId, {
5293
5395
  authMethod: this.resolveAuthMethod(existing)
5294
5396
  });
5295
5397
  if (duplicate && duplicate.name !== profileName) {
@@ -5506,9 +5608,9 @@ var ProfileManager = class {
5506
5608
  const normalizedAuth = this.normalizeProfileData(activeAuth);
5507
5609
  const authMetadata = this.extractProfileMetadata(normalizedAuth);
5508
5610
  const workspace = this.resolveWorkspaceIdentity(normalizedAuth, authMetadata);
5509
- const activeAccountId = this.getAccountIdFromData(normalizedAuth);
5510
- if (activeAccountId) {
5511
- const scoped = await this.getProfileRowByAccountAndWorkspace(activeAccountId, workspace.id ?? DEFAULT_WORKSPACE_ID, {
5611
+ const activeIdentityKey = this.resolveProfileIdentityFromData(null, normalizedAuth, authMetadata);
5612
+ if (activeIdentityKey) {
5613
+ const scoped = await this.getProfileRowByIdentityAndWorkspace(activeIdentityKey, workspace.id ?? DEFAULT_WORKSPACE_ID, {
5512
5614
  preferAuthMethod: "codex-cli"
5513
5615
  });
5514
5616
  if (scoped) {
@@ -5516,7 +5618,7 @@ var ProfileManager = class {
5516
5618
  await this.persistPreferredProfileName(normalized);
5517
5619
  return { name: normalized, trusted: true };
5518
5620
  }
5519
- const matching = await this.getProfileRowByAccountId(activeAccountId, { preferAuthMethod: "codex-cli" });
5621
+ const matching = await this.getProfileRowByIdentityKey(activeIdentityKey, { preferAuthMethod: "codex-cli" });
5520
5622
  if (matching) {
5521
5623
  const normalized = this.normalizeProfileName(matching.name);
5522
5624
  await this.persistPreferredProfileName(normalized);
@@ -7218,7 +7320,7 @@ function formatProfileLabel(name, displayName) {
7218
7320
  return name;
7219
7321
  }
7220
7322
  function profileRateLimitKey(profile) {
7221
- return profile.accountId ?? `profile:${profile.name}`;
7323
+ return resolveProfileIdentityKeyFromProfile(profile);
7222
7324
  }
7223
7325
  function toTitleCase(value) {
7224
7326
  return value.replace(/\w\S*/g, (word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase());
@@ -10124,7 +10226,7 @@ async function ensureCliStorageReady() {
10124
10226
  }
10125
10227
 
10126
10228
  // src/app/main.ts
10127
- var VERSION = true ? "3.5.6" : "0.0.0";
10229
+ var VERSION = true ? "3.6.0" : "0.0.0";
10128
10230
  async function runCli() {
10129
10231
  const args = process.argv.slice(2);
10130
10232
  if (args.length === 0) {