@tokensrc/codex 1.14.4 → 1.14.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/dist/client.js CHANGED
@@ -198,7 +198,7 @@ export class CodexServiceClient {
198
198
  async listV3CredentialAssignments() {
199
199
  const parsed = CodexV3CredentialAssignmentListResponseSchema.safeParse(await this.request(CODEX_V3_CREDENTIAL_ASSIGNMENTS_PATH));
200
200
  if (!parsed.success) {
201
- throw new CodexServiceError("Invalid Codex v3 credential assignments response.", "invalid_codex_service_response");
201
+ throw new CodexServiceError("Invalid Codex v4 token-pool assignments response.", "invalid_codex_service_response");
202
202
  }
203
203
  return parsed.data;
204
204
  }
@@ -216,7 +216,7 @@ export class CodexServiceClient {
216
216
  requireV3Delivery(parsed, assignmentId, clientRequestId) {
217
217
  if (!parsed.success || parsed.data.delivery.assignmentId !== assignmentId
218
218
  || parsed.data.delivery.clientRequestId !== clientRequestId) {
219
- throw new CodexServiceError("Invalid or mismatched Codex v3 full delivery response.", "invalid_codex_service_response");
219
+ throw new CodexServiceError("Invalid or mismatched Codex v4 full delivery response.", "invalid_codex_service_response");
220
220
  }
221
221
  return parsed.data;
222
222
  }
@@ -226,7 +226,7 @@ export class CodexServiceClient {
226
226
  const parsed = CodexV3FullCredentialDeliveryAckResponseSchema.safeParse(await this.request(codexV3FullCredentialDeliveryAckPath(deliveryId), { method: "POST", body, headers: { "Idempotency-Key": key } }));
227
227
  if (!parsed.success || parsed.data.delivery.id !== deliveryId
228
228
  || parsed.data.delivery.generationVersion !== body.generationVersion) {
229
- throw new CodexServiceError("Invalid or mismatched Codex v3 delivery ACK response.", "invalid_codex_service_response");
229
+ throw new CodexServiceError("Invalid or mismatched Codex v4 delivery ACK response.", "invalid_codex_service_response");
230
230
  }
231
231
  return parsed.data;
232
232
  }
@@ -249,7 +249,7 @@ export class CodexServiceClient {
249
249
  || parsed.data.report.accessTokenHealth !== expected.accessTokenHealth
250
250
  || parsed.data.report.refreshTokenHealth !== expected.refreshTokenHealth
251
251
  || parsed.data.report.observedAt !== expected.observedAt) {
252
- throw new CodexServiceError("Invalid or mismatched Codex v3 generation report response.", "invalid_codex_service_response");
252
+ throw new CodexServiceError("Invalid or mismatched Codex v4 generation report response.", "invalid_codex_service_response");
253
253
  }
254
254
  return parsed.data;
255
255
  }
@@ -3,7 +3,7 @@ const definitions = [
3
3
  { name: "login", description: "Enroll an unbound or explicitly assigned official Codex credential", command: "codex login", summary: "默认加入未绑定凭据池;可按用户名/邮箱或经确认的 Keycloak subject 直接分配,上传者仅作为审计身份", access: "authenticated", examples: ["numa codex login", "numa codex login --assign-to user@example.com", "numa codex login --assign-to-subject 01234567-89ab-cdef-0123-456789abcdef --yes"], nestedOnly: true },
4
4
  { name: "logout", description: "Remove the standalone OIDC token cache", command: "logout", summary: "退出独立 Codex CLI", access: "public", examples: ["tokensrc-codex logout"], standaloneOnly: true },
5
5
  { name: "list", description: "List available Codex accounts", command: "codex list [-u|--server <url>]", summary: "列出服务端 Codex 账号元数据,不传输 refresh token", access: "authenticated", examples: ["numa codex list --server https://platform.example/api"] },
6
- { name: "upload", description: "Enroll the official Codex login or upload the legacy local pool", command: "codex upload [-u|--server <url>]", summary: "v3 读取官方 CODEX_HOME/auth.json 完整凭据并完成托管安装;旧 profile 保持批量池上传", access: "authenticated", examples: ["numa codex upload --server https://platform.example/api", "numa codex upload --storage ~/.codex/multi-auth/openai-codex-accounts.json"] },
6
+ { name: "upload", description: "Upload the legacy v1 local account pool", command: "codex upload [-u|--server <url>]", summary: "仅用于 v1 兼容账号批量导入;新账号统一运行 numa codex login 进入 v4 RT 池", access: "authenticated", examples: ["numa codex upload --storage ~/.codex/multi-auth/openai-codex-accounts.json"] },
7
7
  { name: "use", description: "Select an account and install official Codex credentials", command: "codex use [account]", summary: "选择服务端账号并写入官方 ChatGPT auth.json", access: "authenticated", examples: ["numa codex use acct_xxx"] },
8
8
  { name: "run", description: "Launch the official Codex CLI", command: "codex run [-s|--select-account|-a|--account <id>] [-u|--server <url>] [--] [codexArgs...]", summary: "选择或复用 ChatGPT 账号后启动官方 Codex;模型流量不经过宿主", access: "authenticated", examples: ["numa codex run -s", "numa codex run --account acct_xxx", "numa codex run -- exec \"完成长任务\""] },
9
9
  { name: "app", description: "Launch the official Codex desktop app", command: "codex app [workspace] [-s|--select-account|-a|--account <id>] [-u|--server <url>]", summary: "检查批准版本并使用官方 ChatGPT 登录启动 macOS/Windows Codex 桌面 App", access: "authenticated", examples: ["numa codex app . -s", "numa codex app . --account acct_xxx"] },
@@ -1 +1 @@
1
- {"version":3,"file":"command-directory.js","sourceRoot":"","sources":["../src/command-directory.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"command-directory.js","sourceRoot":"","sources":["../src/command-directory.ts"],"names":[],"mappings":"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"}
package/dist/commands.js CHANGED
@@ -12,11 +12,11 @@ import { cleanupLegacyCodexProvider } from "./legacy-cleanup.js";
12
12
  import { executeCodexEnrollmentLogin } from "./enrollment-flow.js";
13
13
  import { executeCodexMigrationLoginBatch } from "./migration-login-batch.js";
14
14
  import { executeCodexShortLivedCredentialDelivery } from "./short-lived-delivery-flow.js";
15
- import { executeManagedCodexV3Upload, reportManagedCodexV3Credentials } from "./managed-credential-v3-flow.js";
15
+ import { executeCodexTokenPoolV4Delivery, reportManagedCodexV3Credentials } from "./managed-credential-v3-flow.js";
16
16
  import { resolveManagedCodexV3AssignmentTarget } from "./managed-credential-v3-assignment.js";
17
17
  import { executeCodexTokenPoolV4Login } from "./token-pool-v4-upload-flow.js";
18
18
  import { readManagedCodexV3State } from "./managed-credential-v3.js";
19
- import { profileManagedCredentialsV3Enabled } from "./profile.js";
19
+ import { profileAccountTokenPoolV4Enabled } from "./profile.js";
20
20
  import { executeCodexAssignmentRetirementLocalDelete } from "./retirement-flow.js";
21
21
  import { clearCodexSelection, loadCodexSelection, rememberCodexSelectionProviderAccount, saveCodexSelection } from "./selection.js";
22
22
  import { installCodexShortcuts, removeCodexShortcuts } from "./shortcuts.js";
@@ -35,43 +35,6 @@ function findV3Assignment(input) {
35
35
  && item.providerAccountId === input.providerAccountId
36
36
  && item.lineageId === input.lineageId);
37
37
  }
38
- async function saveManagedV3Selection(input) {
39
- await saveCodexSelection(input.deps.config.persistence.configDirectory, {
40
- serviceUrl: input.options.server ?? input.deps.config.connection.serverUrl,
41
- allowInsecurePoc: Boolean(input.options.insecurePoc || input.options.insecureHttp
42
- || input.deps.config.connection.allowInsecureHttp),
43
- account: {
44
- id: input.managedAccountId,
45
- accountId: input.providerAccountId,
46
- label: input.label,
47
- distributionMode: "CLIENT_MANAGED_V3",
48
- assignmentId: input.assignmentId,
49
- managedAccountId: input.managedAccountId,
50
- providerAccountId: input.providerAccountId
51
- }
52
- });
53
- }
54
- async function recoverManagedV3Selection(deps, options) {
55
- const state = await readManagedCodexV3State(deps.env);
56
- if (!state || state.phase !== "INSTALLED" || state.authorityMode !== "CLIENT_MANAGED")
57
- return undefined;
58
- const current = await loadCodexSelection(deps.config.persistence.configDirectory);
59
- if (current?.account.distributionMode === "CLIENT_MANAGED_V3"
60
- && current.account.assignmentId === state.assignmentId
61
- && current.account.managedAccountId === state.managedAccountId
62
- && current.account.providerAccountId === state.providerAccountId)
63
- return undefined;
64
- const assignments = await deps.clientFor(serviceConnection(options)).listV3CredentialAssignments();
65
- const assignment = findV3Assignment({ assignments, ...state });
66
- if (!assignment || assignment.authorityMode !== "CLIENT_MANAGED") {
67
- throw new CodexServiceError("Installed v3 credentials have no matching server assignment.", "lineage_version_conflict", 409);
68
- }
69
- await saveManagedV3Selection({
70
- deps, options, assignmentId: state.assignmentId, managedAccountId: state.managedAccountId,
71
- providerAccountId: state.providerAccountId, label: assignment.label
72
- });
73
- return state;
74
- }
75
38
  export function effectiveCodexCredentialRefreshBefore(requestedMs, serverManagedShortLived) {
76
39
  return serverManagedShortLived
77
40
  ? Math.min(requestedMs, SERVER_MANAGED_SHORT_LIVED_REFRESH_WINDOW_MS)
@@ -99,8 +62,8 @@ function schemaV2LifecycleEnabled(config) {
99
62
  const capabilities = config.profile?.userBoundRefreshTokens;
100
63
  return capabilities?.delivery === true || capabilities?.migrationPlans === true;
101
64
  }
102
- function managedCredentialsV3Enabled(config) {
103
- return profileManagedCredentialsV3Enabled(config.profile);
65
+ function accountTokenPoolV4Enabled(config) {
66
+ return profileAccountTokenPoolV4Enabled(config.profile);
104
67
  }
105
68
  function codexConfigPathForEnv(env) {
106
69
  const codexHome = env.CODEX_HOME?.trim();
@@ -151,7 +114,7 @@ function formatAdminAccount(account) {
151
114
  }
152
115
  async function loadAccountData(deps, options = {}) {
153
116
  const previous = await loadCodexSelection(deps.config.persistence.configDirectory);
154
- if (managedCredentialsV3Enabled(deps.config)) {
117
+ if (accountTokenPoolV4Enabled(deps.config)) {
155
118
  const v3Assignments = await deps.clientFor(serviceConnection(options)).listV3CredentialAssignments();
156
119
  const accounts = v3Assignments.items.map((assignment) => ({
157
120
  id: assignment.managedAccountId,
@@ -234,7 +197,7 @@ async function chooseAccount(deps, requested, options = {}) {
234
197
  id: v3Assignment.managedAccountId,
235
198
  accountId: v3Assignment.providerAccountId,
236
199
  label: accountLabel,
237
- distributionMode: "CLIENT_MANAGED_V3",
200
+ distributionMode: "CLIENT_MANAGED_V4",
238
201
  assignmentId: v3Assignment.assignmentId,
239
202
  managedAccountId: v3Assignment.managedAccountId,
240
203
  providerAccountId: v3Assignment.providerAccountId
@@ -260,22 +223,36 @@ async function chooseAccount(deps, requested, options = {}) {
260
223
  async function installCredentials(deps, selection, service) {
261
224
  const client = deps.clientFor(service);
262
225
  await cleanupLegacyCodexProvider(codexConfigPathForEnv(deps.env));
263
- if (managedCredentialsV3Enabled(deps.config)) {
264
- const state = await readManagedCodexV3State(deps.env);
265
- if (!state || state.phase !== "INSTALLED" || state.authorityMode !== "CLIENT_MANAGED"
266
- || selection.account.distributionMode !== "CLIENT_MANAGED_V3"
267
- || selection.account.assignmentId !== state.assignmentId
268
- || selection.account.managedAccountId !== state.managedAccountId
269
- || selection.account.providerAccountId !== state.providerAccountId) {
270
- throw new CodexServiceError("The selected v3 assignment does not match acknowledged local credentials; run `numa codex login` or `numa codex upload`.", "lineage_version_conflict", 409);
226
+ if (accountTokenPoolV4Enabled(deps.config)) {
227
+ if (selection.account.distributionMode !== "CLIENT_MANAGED_V4"
228
+ || !selection.account.assignmentId || !selection.account.managedAccountId
229
+ || !selection.account.providerAccountId) {
230
+ throw new CodexServiceError("The selected account is not a v4 token-pool assignment.", "codex_assignment_not_found", 404);
271
231
  }
272
232
  const assignments = await client.listV3CredentialAssignments();
273
- const current = findV3Assignment({ assignments, ...state });
274
- if (!current || current.assignmentId !== state.assignmentId || current.lineageId !== state.lineageId
275
- || current.authorityMode !== "CLIENT_MANAGED") {
276
- throw new CodexServiceError("The v3 credential assignment is no longer active.", "codex_assignment_not_found", 404);
233
+ const assignment = assignments.items.find((item) => item.assignmentId === selection.account.assignmentId
234
+ && item.managedAccountId === selection.account.managedAccountId
235
+ && item.providerAccountId === selection.account.providerAccountId);
236
+ if (!assignment) {
237
+ throw new CodexServiceError("The v4 token-pool assignment is no longer active.", "codex_assignment_not_found", 404);
238
+ }
239
+ const state = await readManagedCodexV3State(deps.env);
240
+ if (state?.phase === "INSTALLED" && state.authorityMode === "CLIENT_MANAGED"
241
+ && selection.account.assignmentId === state.assignmentId
242
+ && selection.account.managedAccountId === state.managedAccountId
243
+ && selection.account.providerAccountId === state.providerAccountId
244
+ && assignment.lineageId === state.lineageId
245
+ && assignment.currentGenerationVersion === state.generationVersion
246
+ && assignment.authorityMode === "CLIENT_MANAGED") {
247
+ return { expiresAt: Date.parse(state.credentialExpiresAt), selection };
277
248
  }
278
- return { expiresAt: Date.parse(state.credentialExpiresAt), selection };
249
+ const delivered = await executeCodexTokenPoolV4Delivery({
250
+ client,
251
+ tenantId: deps.config.connection.tenantId,
252
+ assignment,
253
+ env: deps.env
254
+ });
255
+ return { expiresAt: Date.parse(delivered.credentialExpiresAt), selection };
279
256
  }
280
257
  if (deps.config.profile?.userBoundRefreshTokens?.delivery === true) {
281
258
  await discardCodexFileCredentialBackup(deps.env);
@@ -320,10 +297,11 @@ async function installCredentials(deps, selection, service) {
320
297
  return { expiresAt: response.expiresAt, selection };
321
298
  }
322
299
  async function selectedCredentials(deps, requested, options = {}) {
323
- if (managedCredentialsV3Enabled(deps.config)) {
300
+ if (accountTokenPoolV4Enabled(deps.config)) {
324
301
  const state = await readManagedCodexV3State(deps.env);
325
302
  if (!state || state.phase !== "INSTALLED" || state.authorityMode !== "CLIENT_MANAGED") {
326
- throw new CodexServiceError("No acknowledged v3 Codex credential is installed; run `numa codex login` or `numa codex upload`.", "managed_codex_v3_login_required", 409);
303
+ const selection = (await chooseAccount(deps, requested, options)).selection;
304
+ return installCredentials(deps, selection, serviceConnection(options));
327
305
  }
328
306
  if (requested && ![
329
307
  state.assignmentId,
@@ -331,7 +309,8 @@ async function selectedCredentials(deps, requested, options = {}) {
331
309
  state.providerAccountId,
332
310
  state.lineageId
333
311
  ].includes(requested)) {
334
- throw new CodexServiceError("The requested account is not the active v3 lineage.", "codex_account_not_found", 404);
312
+ const selection = (await chooseAccount(deps, requested, options)).selection;
313
+ return installCredentials(deps, selection, serviceConnection(options));
335
314
  }
336
315
  const assignments = await deps.clientFor(serviceConnection(options)).listV3CredentialAssignments();
337
316
  const assignment = findV3Assignment({ assignments, ...state });
@@ -340,7 +319,7 @@ async function selectedCredentials(deps, requested, options = {}) {
340
319
  || assignment.providerAccountId !== state.providerAccountId
341
320
  || assignment.lineageId !== state.lineageId
342
321
  || assignment.authorityMode !== "CLIENT_MANAGED") {
343
- throw new CodexServiceError("The local v3 lineage no longer matches the server assignment.", "lineage_version_conflict", 409);
322
+ throw new CodexServiceError("The local v4 lineage no longer matches the server assignment.", "lineage_version_conflict", 409);
344
323
  }
345
324
  const selection = await saveCodexSelection(deps.config.persistence.configDirectory, {
346
325
  serviceUrl: options.server ?? deps.config.connection.serverUrl,
@@ -349,7 +328,7 @@ async function selectedCredentials(deps, requested, options = {}) {
349
328
  id: state.managedAccountId,
350
329
  accountId: state.providerAccountId,
351
330
  label: assignment.label,
352
- distributionMode: "CLIENT_MANAGED_V3",
331
+ distributionMode: "CLIENT_MANAGED_V4",
353
332
  assignmentId: state.assignmentId,
354
333
  managedAccountId: state.managedAccountId,
355
334
  providerAccountId: state.providerAccountId
@@ -422,7 +401,7 @@ async function selectedCredentials(deps, requested, options = {}) {
422
401
  return { selection, expiresAt: local.expiresAt };
423
402
  }
424
403
  async function launchWithSelectedCredentials(deps, selected, launch) {
425
- if (managedCredentialsV3Enabled(deps.config)) {
404
+ if (accountTokenPoolV4Enabled(deps.config)) {
426
405
  try {
427
406
  return await launch();
428
407
  }
@@ -481,7 +460,7 @@ export function registerCodexCommands(program, deps, options = {}) {
481
460
  else
482
461
  deps.output.write("Logged out.\n");
483
462
  });
484
- if (!includeAuthCommands && managedCredentialsV3Enabled(deps.config))
463
+ if (!includeAuthCommands && accountTokenPoolV4Enabled(deps.config))
485
464
  serviceOptions(codex.command("login")
486
465
  .description("Run an isolated official Codex login and enroll an unbound or explicitly assigned credential"))
487
466
  .option("--device-auth", "use the official Codex device authorization flow")
@@ -489,8 +468,8 @@ export function registerCodexCommands(program, deps, options = {}) {
489
468
  .option("--assign-to-subject <uuid>", "assign directly to a canonical Keycloak subject UUID")
490
469
  .option("--yes", "confirm direct subject assignment")
491
470
  .action(async (opts, command) => {
492
- if (!managedCredentialsV3Enabled(deps.config)) {
493
- throw new CodexServiceError("Managed Codex credential v3 is not enabled by the verified platform profile.", "managed_codex_v3_not_enabled", 409);
471
+ if (!accountTokenPoolV4Enabled(deps.config)) {
472
+ throw new CodexServiceError("The Codex v4 account token pool is not enabled by the verified platform profile.", "codex_token_pool_v4_not_enabled", 409);
494
473
  }
495
474
  const target = resolveManagedCodexV3AssignmentTarget(opts);
496
475
  const result = await executeCodexTokenPoolV4Login({
@@ -652,7 +631,7 @@ export function registerCodexCommands(program, deps, options = {}) {
652
631
  throw new Error("auth-agent replaces the current login; add --replace-login to confirm.");
653
632
  if (opts.credentialStore !== "file")
654
633
  throw new Error("ChatGPT auth.json 同步目前仅支持 --credential-store file。");
655
- if (managedCredentialsV3Enabled(deps.config)) {
634
+ if (accountTokenPoolV4Enabled(deps.config)) {
656
635
  const interval = Number(opts.interval) * 1_000;
657
636
  if (!Number.isFinite(interval) || interval <= 0)
658
637
  throw new Error("--interval must be positive.");
@@ -725,55 +704,12 @@ export function registerCodexCommands(program, deps, options = {}) {
725
704
  await new Promise((resolve) => setTimeout(resolve, interval));
726
705
  } while (true);
727
706
  });
728
- serviceOptions(codex.command("upload").description("Enroll official Codex credentials (v3) or upload the legacy local pool"))
707
+ serviceOptions(codex.command("upload").description("Upload the legacy v1 local account pool"))
729
708
  .option("--storage <path>", "account storage path")
730
709
  .option("--source <name>", "upload source")
731
710
  .action(async (opts, command) => {
732
- if (managedCredentialsV3Enabled(deps.config)) {
733
- if (opts.storage || opts.source) {
734
- throw new CodexServiceError("Managed credential v3 upload reads only the official CODEX_HOME/auth.json.", "invalid_configuration");
735
- }
736
- const recovered = await recoverManagedV3Selection(deps, opts);
737
- if (recovered) {
738
- if (await json(command, deps))
739
- print(deps.output, success({
740
- assignmentId: recovered.assignmentId,
741
- managedAccountId: recovered.managedAccountId,
742
- providerAccountId: recovered.providerAccountId,
743
- lineageId: recovered.lineageId,
744
- generationVersion: recovered.generationVersion,
745
- authorityMode: recovered.authorityMode,
746
- credentialExpiresAt: recovered.credentialExpiresAt,
747
- selectionRecovered: true
748
- }));
749
- else
750
- deps.output.write(`Official Codex credentials were already installed; recovered their local selection; generation=${recovered.generationVersion}.\n`);
751
- return;
752
- }
753
- const result = await executeManagedCodexV3Upload({
754
- client: deps.clientFor(serviceConnection(opts)),
755
- tenantId: deps.config.connection.tenantId,
756
- env: deps.env
757
- });
758
- await saveManagedV3Selection({
759
- deps, options: opts, assignmentId: result.assignmentId, managedAccountId: result.managedAccountId,
760
- providerAccountId: result.providerAccountId, label: "Codex account"
761
- });
762
- const output = {
763
- enrollmentId: result.enrollmentId,
764
- assignmentId: result.assignmentId,
765
- managedAccountId: result.managedAccountId,
766
- providerAccountId: result.providerAccountId,
767
- lineageId: result.lineageId,
768
- generationVersion: result.generationVersion,
769
- authorityMode: result.authorityMode,
770
- credentialExpiresAt: result.credentialExpiresAt
771
- };
772
- if (await json(command, deps))
773
- print(deps.output, success(output));
774
- else
775
- deps.output.write(`Official Codex credentials enrolled, installed, and acknowledged; generation=${result.generationVersion}.\n`);
776
- return;
711
+ if (accountTokenPoolV4Enabled(deps.config)) {
712
+ throw new CodexServiceError("This tenant uses the v4 RT pool. Administrators must run `numa codex login`; v1 pool upload is compatibility-only.", "codex_token_pool_v4_login_required", 409);
777
713
  }
778
714
  assertLegacyManagementEnabled(deps.config);
779
715
  if (opts.storage)
@@ -833,124 +769,126 @@ export function registerCodexCommands(program, deps, options = {}) {
833
769
  if (failures.length > 0)
834
770
  process.exitCode = 1;
835
771
  });
836
- const enrollment = codex.command("enrollment")
837
- .description("Execute administrator-created one-shot Codex login intents");
838
- serviceOptions(enrollment.command("status <intent>").description("Show redacted enrollment intent status"))
839
- .action(async (intent, opts, command) => {
840
- const result = await deps.clientFor(serviceConnection(opts)).getEnrollmentIntent(intent);
841
- if (await json(command, deps)) {
842
- print(deps.output, { ok: true, ...result });
843
- return;
844
- }
845
- deps.output.write(`${result.intent.id} ${result.intent.type} ${result.intent.status}`
846
- + ` revision=${result.intent.revision}\n`);
847
- deps.output.write(`expires=${result.intent.expiresAt}`
848
- + `${result.intent.assignmentId ? ` assignment=${result.intent.assignmentId}` : ""}`
849
- + `${result.intent.managedAccountId ? ` account=${result.intent.managedAccountId}` : ""}\n`);
850
- if (!result.capabilities.candidateUpload) {
851
- deps.output.write("安全候选上传尚未启用;当前任务只能查询或取消,不会启动 OpenAI 登录。\n");
852
- }
853
- });
854
- serviceOptions(enrollment.command("login <intent>")
855
- .description("Run one isolated official Codex login for an enrollment intent"))
856
- .option("--device-auth", "use the official Codex device authorization flow")
857
- .action(async (intent, _opts, command) => {
858
- const result = await executeCodexEnrollmentLogin({
859
- client: deps.clientFor(serviceConnection(_opts)),
860
- tenantId: deps.config.connection.tenantId,
861
- intentId: intent,
862
- deviceAuth: _opts.deviceAuth
772
+ if (schemaV2LifecycleEnabled(deps.config)) {
773
+ const enrollment = codex.command("enrollment")
774
+ .description("Execute administrator-created one-shot Codex login intents");
775
+ serviceOptions(enrollment.command("status <intent>").description("Show redacted enrollment intent status"))
776
+ .action(async (intent, opts, command) => {
777
+ const result = await deps.clientFor(serviceConnection(opts)).getEnrollmentIntent(intent);
778
+ if (await json(command, deps)) {
779
+ print(deps.output, { ok: true, ...result });
780
+ return;
781
+ }
782
+ deps.output.write(`${result.intent.id} ${result.intent.type} ${result.intent.status}`
783
+ + ` revision=${result.intent.revision}\n`);
784
+ deps.output.write(`expires=${result.intent.expiresAt}`
785
+ + `${result.intent.assignmentId ? ` assignment=${result.intent.assignmentId}` : ""}`
786
+ + `${result.intent.managedAccountId ? ` account=${result.intent.managedAccountId}` : ""}\n`);
787
+ if (!result.capabilities.candidateUpload) {
788
+ deps.output.write("安全候选上传尚未启用;当前任务只能查询或取消,不会启动 OpenAI 登录。\n");
789
+ }
863
790
  });
864
- if (await json(command, deps))
865
- print(deps.output, { ok: true, ...result });
866
- else {
867
- deps.output.write(`${result.intent.id} 登录候选已处理;状态=${result.intent.status}`
868
- + `${result.result.managedAccountId ? `,账号=${result.result.managedAccountId}` : ""}`
869
- + `${result.result.lineageId ? `,lineage=${result.result.lineageId}` : ""}`
870
- + "。\n");
871
- if (result.result.outcome === "TEMPORARILY_UNAVAILABLE") {
872
- deps.output.write("同一进程内的安全重试已耗尽;请取消并新建任务,不能用另一次登录覆盖已保留的候选。\n");
791
+ serviceOptions(enrollment.command("login <intent>")
792
+ .description("Run one isolated official Codex login for an enrollment intent"))
793
+ .option("--device-auth", "use the official Codex device authorization flow")
794
+ .action(async (intent, _opts, command) => {
795
+ const result = await executeCodexEnrollmentLogin({
796
+ client: deps.clientFor(serviceConnection(_opts)),
797
+ tenantId: deps.config.connection.tenantId,
798
+ intentId: intent,
799
+ deviceAuth: _opts.deviceAuth
800
+ });
801
+ if (await json(command, deps))
802
+ print(deps.output, { ok: true, ...result });
803
+ else {
804
+ deps.output.write(`${result.intent.id} 登录候选已处理;状态=${result.intent.status}`
805
+ + `${result.result.managedAccountId ? `,账号=${result.result.managedAccountId}` : ""}`
806
+ + `${result.result.lineageId ? `,lineage=${result.result.lineageId}` : ""}`
807
+ + "。\n");
808
+ if (result.result.outcome === "TEMPORARILY_UNAVAILABLE") {
809
+ deps.output.write("同一进程内的安全重试已耗尽;请取消并新建任务,不能用另一次登录覆盖已保留的候选。\n");
810
+ }
873
811
  }
874
- }
875
- });
876
- serviceOptions(enrollment.command("login-batch <planId>")
877
- .description("Sequentially execute isolated logins from a migration plan queue")
878
- .option("--device-auth", "use the official Codex device authorization flow")
879
- .option("--dry-run", "show the redacted sequential plan without starting login")
880
- .option("--page-size <count>", "migration queue page size", "100"))
881
- .action(async (planId, opts, command) => {
882
- if (deps.config.profile?.userBoundRefreshTokens?.migrationPlans !== true) {
883
- throw new CodexServiceError("The selected tenant has not enabled schema-v2 migration login plans.", "capability_not_enabled", 409);
884
- }
885
- const pageSize = Number(opts.pageSize);
886
- if (!Number.isInteger(pageSize) || pageSize < 1 || pageSize > 100) {
887
- throw new CodexServiceError("--page-size must be an integer from 1 to 100.", "invalid_configuration");
888
- }
889
- const batch = await executeCodexMigrationLoginBatch({
890
- client: deps.clientFor(serviceConnection(opts)),
891
- tenantId: deps.config.connection.tenantId,
892
- planId,
893
- deviceAuth: opts.deviceAuth,
894
- dryRun: opts.dryRun,
895
- pageSize
896
812
  });
897
- const failed = batch.counts.failed > 0
898
- || ["TARGET_UNCERTAIN", "AUTHENTICATION_ERROR", "SERVICE_ERROR", "LOGIN_ERROR"]
899
- .includes(batch.stopReason);
900
- if (await json(command, deps)) {
901
- print(deps.output, { ok: !failed, ...batch });
902
- }
903
- else {
904
- deps.output.write(`migration=${batch.plan.id} status=${batch.plan.status} revision=${batch.plan.revision}`
905
- + ` stop=${batch.stopReason}${batch.dryRun ? " dry-run" : ""}\n`);
906
- deps.output.write(`capacity: verifiedMinimum=${batch.capacity.verifiedMinimum}, verifiedLimit=unknown,`
907
- + ` sequentialRequired=${batch.capacity.sequentialRequired}; verifiedMinimum 不是上限。\n`);
908
- for (const item of batch.items) {
909
- deps.output.write(`${item.assignmentId} ${item.action}/${item.status}`
910
- + `${item.intentId ? ` intent=${item.intentId}` : ""}`
911
- + `${item.outcome ? ` outcome=${item.outcome}` : ""}`
912
- + `${item.skipReason ? ` skip=${item.skipReason}` : ""}`
913
- + `${item.safeErrorCode ? ` error=${item.safeErrorCode}` : ""}\n`);
813
+ serviceOptions(enrollment.command("login-batch <planId>")
814
+ .description("Sequentially execute isolated logins from a migration plan queue")
815
+ .option("--device-auth", "use the official Codex device authorization flow")
816
+ .option("--dry-run", "show the redacted sequential plan without starting login")
817
+ .option("--page-size <count>", "migration queue page size", "100"))
818
+ .action(async (planId, opts, command) => {
819
+ if (deps.config.profile?.userBoundRefreshTokens?.migrationPlans !== true) {
820
+ throw new CodexServiceError("The selected tenant has not enabled schema-v2 migration login plans.", "capability_not_enabled", 409);
914
821
  }
915
- deps.output.write(`summary: attempted=${batch.counts.attempted}, completed=${batch.counts.completed},`
916
- + ` failed=${batch.counts.failed}, skipped=${batch.counts.skipped}.\n`);
917
- }
918
- if (failed)
919
- process.exitCode = 1;
920
- });
921
- serviceOptions(enrollment.command("cancel <intent>").description("Cancel an enrollment intent"))
922
- .option("--idempotency-key <key>", "reuse a prior cancellation idempotency key")
923
- .action(async (intent, opts, command) => {
924
- const client = deps.clientFor(serviceConnection(opts));
925
- const current = await client.getEnrollmentIntent(intent);
926
- const result = await client.cancelEnrollmentIntent(intent, {
927
- idempotencyKey: opts.idempotencyKey?.trim() || randomUUID(),
928
- revision: current.intent.revision
822
+ const pageSize = Number(opts.pageSize);
823
+ if (!Number.isInteger(pageSize) || pageSize < 1 || pageSize > 100) {
824
+ throw new CodexServiceError("--page-size must be an integer from 1 to 100.", "invalid_configuration");
825
+ }
826
+ const batch = await executeCodexMigrationLoginBatch({
827
+ client: deps.clientFor(serviceConnection(opts)),
828
+ tenantId: deps.config.connection.tenantId,
829
+ planId,
830
+ deviceAuth: opts.deviceAuth,
831
+ dryRun: opts.dryRun,
832
+ pageSize
833
+ });
834
+ const failed = batch.counts.failed > 0
835
+ || ["TARGET_UNCERTAIN", "AUTHENTICATION_ERROR", "SERVICE_ERROR", "LOGIN_ERROR"]
836
+ .includes(batch.stopReason);
837
+ if (await json(command, deps)) {
838
+ print(deps.output, { ok: !failed, ...batch });
839
+ }
840
+ else {
841
+ deps.output.write(`migration=${batch.plan.id} status=${batch.plan.status} revision=${batch.plan.revision}`
842
+ + ` stop=${batch.stopReason}${batch.dryRun ? " dry-run" : ""}\n`);
843
+ deps.output.write(`capacity: verifiedMinimum=${batch.capacity.verifiedMinimum}, verifiedLimit=unknown,`
844
+ + ` sequentialRequired=${batch.capacity.sequentialRequired}; verifiedMinimum 不是上限。\n`);
845
+ for (const item of batch.items) {
846
+ deps.output.write(`${item.assignmentId} ${item.action}/${item.status}`
847
+ + `${item.intentId ? ` intent=${item.intentId}` : ""}`
848
+ + `${item.outcome ? ` outcome=${item.outcome}` : ""}`
849
+ + `${item.skipReason ? ` skip=${item.skipReason}` : ""}`
850
+ + `${item.safeErrorCode ? ` error=${item.safeErrorCode}` : ""}\n`);
851
+ }
852
+ deps.output.write(`summary: attempted=${batch.counts.attempted}, completed=${batch.counts.completed},`
853
+ + ` failed=${batch.counts.failed}, skipped=${batch.counts.skipped}.\n`);
854
+ }
855
+ if (failed)
856
+ process.exitCode = 1;
929
857
  });
930
- if (await json(command, deps))
931
- print(deps.output, { ok: true, ...result });
932
- else
933
- deps.output.write(`${result.intent.id} 已取消;状态=${result.intent.status}。\n`);
934
- });
935
- const retirement = codex.command("retirement")
936
- .description("Remove an exact locally installed lineage for a Server retirement request");
937
- serviceOptions(retirement.command("acknowledge <retirement>")
938
- .description("Delete the matching managed short-lived credentials and append local evidence")
939
- .option("--idempotency-key <key>", "reuse a prior local deletion acknowledgement key"))
940
- .action(async (retirementId, opts, command) => {
941
- const result = await executeCodexAssignmentRetirementLocalDelete({
942
- client: deps.clientFor(serviceConnection(opts)),
943
- retirementId,
944
- env: deps.env,
945
- ...(opts.idempotencyKey?.trim() ? { idempotencyKey: opts.idempotencyKey.trim() } : {})
858
+ serviceOptions(enrollment.command("cancel <intent>").description("Cancel an enrollment intent"))
859
+ .option("--idempotency-key <key>", "reuse a prior cancellation idempotency key")
860
+ .action(async (intent, opts, command) => {
861
+ const client = deps.clientFor(serviceConnection(opts));
862
+ const current = await client.getEnrollmentIntent(intent);
863
+ const result = await client.cancelEnrollmentIntent(intent, {
864
+ idempotencyKey: opts.idempotencyKey?.trim() || randomUUID(),
865
+ revision: current.intent.revision
866
+ });
867
+ if (await json(command, deps))
868
+ print(deps.output, { ok: true, ...result });
869
+ else
870
+ deps.output.write(`${result.intent.id} 已取消;状态=${result.intent.status}。\n`);
946
871
  });
947
- if (await json(command, deps))
948
- print(deps.output, { ok: true, ...result });
949
- else
950
- deps.output.write(`本机匹配的短效凭据已删除并记录证据;retirement=${result.retirement.id}`
951
- + `,revision=${result.retirement.revision},状态=${result.retirement.status}。\n`
952
- + "该证据不代表其他终端已清理;最终退役仍等待全部已披露短效凭据自然过期。\n");
953
- });
872
+ const retirement = codex.command("retirement")
873
+ .description("Remove an exact locally installed lineage for a Server retirement request");
874
+ serviceOptions(retirement.command("acknowledge <retirement>")
875
+ .description("Delete the matching managed short-lived credentials and append local evidence")
876
+ .option("--idempotency-key <key>", "reuse a prior local deletion acknowledgement key"))
877
+ .action(async (retirementId, opts, command) => {
878
+ const result = await executeCodexAssignmentRetirementLocalDelete({
879
+ client: deps.clientFor(serviceConnection(opts)),
880
+ retirementId,
881
+ env: deps.env,
882
+ ...(opts.idempotencyKey?.trim() ? { idempotencyKey: opts.idempotencyKey.trim() } : {})
883
+ });
884
+ if (await json(command, deps))
885
+ print(deps.output, { ok: true, ...result });
886
+ else
887
+ deps.output.write(`本机匹配的短效凭据已删除并记录证据;retirement=${result.retirement.id}`
888
+ + `,revision=${result.retirement.revision},状态=${result.retirement.status}。\n`
889
+ + "该证据不代表其他终端已清理;最终退役仍等待全部已披露短效凭据自然过期。\n");
890
+ });
891
+ }
954
892
  const admin = codex.command("admin")
955
893
  .description("Administer server-managed Codex accounts and assignments");
956
894
  const adminAccounts = admin.command("accounts")