@tokensrc/codex 1.14.17 → 1.14.18
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 +4 -74
- package/dist/client.d.ts +1 -16
- package/dist/client.js +1 -114
- package/dist/client.js.map +1 -1
- package/dist/commands.d.ts +0 -1
- package/dist/commands.js +6 -266
- package/dist/commands.js.map +1 -1
- package/dist/credentials.d.ts +3 -75
- package/dist/credentials.js +2 -338
- package/dist/credentials.js.map +1 -1
- package/dist/index.d.ts +0 -8
- package/dist/index.js +0 -8
- package/dist/index.js.map +1 -1
- package/dist/managed-credential-v3-envelope.d.ts +0 -15
- package/dist/managed-credential-v3-envelope.js +0 -26
- package/dist/managed-credential-v3-envelope.js.map +1 -1
- package/dist/managed-credential-v3-flow.d.ts +0 -17
- package/dist/managed-credential-v3-flow.js +3 -90
- package/dist/managed-credential-v3-flow.js.map +1 -1
- package/dist/managed-credential-v3.d.ts +0 -8
- package/dist/managed-credential-v3.js +0 -23
- package/dist/managed-credential-v3.js.map +1 -1
- package/dist/profile.d.ts +0 -6
- package/dist/profile.js +0 -22
- package/dist/profile.js.map +1 -1
- package/dist/protocol.d.ts +0 -622
- package/dist/protocol.js +0 -277
- package/dist/protocol.js.map +1 -1
- package/package.json +2 -2
- package/dist/credential-supervisor.d.ts +0 -17
- package/dist/credential-supervisor.js +0 -84
- package/dist/credential-supervisor.js.map +0 -1
- package/dist/enrollment-envelope.d.ts +0 -18
- package/dist/enrollment-envelope.js +0 -112
- package/dist/enrollment-envelope.js.map +0 -1
- package/dist/enrollment-flow.d.ts +0 -27
- package/dist/enrollment-flow.js +0 -224
- package/dist/enrollment-flow.js.map +0 -1
- package/dist/migration-login-batch.d.ts +0 -66
- package/dist/migration-login-batch.js +0 -202
- package/dist/migration-login-batch.js.map +0 -1
- package/dist/retirement-flow.d.ts +0 -38
- package/dist/retirement-flow.js +0 -78
- package/dist/retirement-flow.js.map +0 -1
- package/dist/short-lived-delivery-flow.d.ts +0 -41
- package/dist/short-lived-delivery-flow.js +0 -141
- package/dist/short-lived-delivery-flow.js.map +0 -1
- package/dist/short-lived-delivery-journal.d.ts +0 -34
- package/dist/short-lived-delivery-journal.js +0 -137
- package/dist/short-lived-delivery-journal.js.map +0 -1
- package/dist/short-lived-delivery.d.ts +0 -26
- package/dist/short-lived-delivery.js +0 -139
- package/dist/short-lived-delivery.js.map +0 -1
package/dist/commands.js
CHANGED
|
@@ -4,34 +4,24 @@ import { join } from "node:path";
|
|
|
4
4
|
import { getStoragePath, loadAccounts, setStoragePath, setStoragePathDirect } from "codex-multi-auth/storage";
|
|
5
5
|
import { normalizeCodexServiceUrl } from "./client.js";
|
|
6
6
|
import { CodexServiceError } from "./errors.js";
|
|
7
|
-
import { backupCodexFileCredentials, codexFileCredentialsMatchAccount,
|
|
7
|
+
import { backupCodexFileCredentials, codexFileCredentialsMatchAccount, inspectLocalCodexCredentialStatus, resetManagedCodexFileCredentials, restoreCodexFileCredentials, writeCodexChatgptFileCredentials } from "./credentials.js";
|
|
8
8
|
import { CODEX_DISTRIBUTION_SCHEMA_VERSION } from "./protocol.js";
|
|
9
9
|
import { formatCodexAccountDisplayIdentity, resolveCodexAdminAccount, resolveCodexAccountDisplayName } from "./account-display.js";
|
|
10
10
|
import { loadCodexQuotaUploads } from "./quota-cache.js";
|
|
11
11
|
import { cleanupLegacyCodexProvider } from "./legacy-cleanup.js";
|
|
12
|
-
import { executeCodexEnrollmentLogin } from "./enrollment-flow.js";
|
|
13
|
-
import { executeCodexMigrationLoginBatch } from "./migration-login-batch.js";
|
|
14
|
-
import { executeCodexShortLivedCredentialDelivery } from "./short-lived-delivery-flow.js";
|
|
15
12
|
import { executeCodexTokenPoolV4Delivery, reconcilePendingManagedCodexV3Operation, reportManagedCodexV3Credentials } from "./managed-credential-v3-flow.js";
|
|
16
13
|
import { resolveManagedCodexV3AssignmentTarget } from "./managed-credential-v3-assignment.js";
|
|
17
14
|
import { executeCodexTokenPoolV4Login } from "./token-pool-v4-upload-flow.js";
|
|
18
15
|
import { readManagedCodexV3State } from "./managed-credential-v3.js";
|
|
19
16
|
import { profileAccountTokenPoolV4Enabled, profileMultiDeviceCredentialSyncEnabled, profileRequestBoundDeviceCredentialsEnabled } from "./profile.js";
|
|
20
17
|
import { superviseCodexV5Credentials, synchronizeCodexV5SelectionOnce } from "./multi-device-v5-flow.js";
|
|
21
|
-
import {
|
|
22
|
-
import { clearCodexSelection, loadCodexSelection, rememberCodexSelectionProviderAccount, saveCodexSelection } from "./selection.js";
|
|
18
|
+
import { clearCodexSelection, loadCodexSelection, saveCodexSelection } from "./selection.js";
|
|
23
19
|
import { installCodexShortcuts, removeCodexShortcuts } from "./shortcuts.js";
|
|
24
20
|
import { isCodexAccountAvailable, isCodexSubscriptionExpired, renderCodexAccountDashboard, selectCodexAccount } from "./tui.js";
|
|
25
|
-
import { readCodexShortLivedDeliveryJournal } from "./short-lived-delivery-journal.js";
|
|
26
|
-
import { superviseCodexShortLivedCredentials } from "./credential-supervisor.js";
|
|
27
21
|
import { ensureCodexV6DeviceCredentials, reconcileCodexV6DeviceAccess, reportCodexV6DeviceCredentials, requireAvailableCodexV6DeviceAccess } from "./request-bound-device-v6-flow.js";
|
|
28
22
|
import { getOrCreateCodexInstallationId } from "./multi-device-v5-state.js";
|
|
29
23
|
import { resetCodexV6LocalState } from "./request-bound-device-v6-state.js";
|
|
30
24
|
export const NO_AVAILABLE_CHATGPT_ACCOUNT_MESSAGE = "当前用户没有绑定可用的 ChatGPT 账号。请前往 OA 系统提交 IT11 工单,申请独享或共享的 ChatGPT 个人账号。";
|
|
31
|
-
// The Server refreshes a user-bound lineage only inside its final 60-second
|
|
32
|
-
// credential safety window. Asking for a new delivery earlier would merely
|
|
33
|
-
// re-encrypt the same access/ID tokens and must not be treated as a renewal.
|
|
34
|
-
const SERVER_MANAGED_SHORT_LIVED_REFRESH_WINDOW_MS = 60_000;
|
|
35
25
|
const SCHEMA_V2_LEGACY_MANAGEMENT_DISABLED = "schema_v2_legacy_management_disabled";
|
|
36
26
|
function findV3Assignment(input) {
|
|
37
27
|
return input.assignments.items.find((item) => item.assignmentId === input.assignmentId
|
|
@@ -39,11 +29,6 @@ function findV3Assignment(input) {
|
|
|
39
29
|
&& item.providerAccountId === input.providerAccountId
|
|
40
30
|
&& item.lineageId === input.lineageId);
|
|
41
31
|
}
|
|
42
|
-
export function effectiveCodexCredentialRefreshBefore(requestedMs, serverManagedShortLived) {
|
|
43
|
-
return serverManagedShortLived
|
|
44
|
-
? Math.min(requestedMs, SERVER_MANAGED_SHORT_LIVED_REFRESH_WINDOW_MS)
|
|
45
|
-
: requestedMs;
|
|
46
|
-
}
|
|
47
32
|
async function json(command, deps) {
|
|
48
33
|
if (command.optsWithGlobals().json)
|
|
49
34
|
return true;
|
|
@@ -62,10 +47,6 @@ function success(value) {
|
|
|
62
47
|
function importFailures(credentials) {
|
|
63
48
|
return credentials.filter(({ status }) => status === "REJECTED" || status === "UNKNOWN");
|
|
64
49
|
}
|
|
65
|
-
function schemaV2LifecycleEnabled(config) {
|
|
66
|
-
const capabilities = config.profile?.userBoundRefreshTokens;
|
|
67
|
-
return capabilities?.delivery === true || capabilities?.migrationPlans === true;
|
|
68
|
-
}
|
|
69
50
|
function accountTokenPoolV4Enabled(config) {
|
|
70
51
|
return profileAccountTokenPoolV4Enabled(config.profile);
|
|
71
52
|
}
|
|
@@ -85,19 +66,9 @@ function codexConfigPathForEnv(env) {
|
|
|
85
66
|
return codexHome ? join(codexHome, "config.toml") : undefined;
|
|
86
67
|
}
|
|
87
68
|
function assertLegacyManagementEnabled(config) {
|
|
88
|
-
|
|
89
|
-
if (!schemaV2LifecycleEnabled(config) && !requestBound)
|
|
69
|
+
if (!requestBoundDeviceCredentialsEnabled(config))
|
|
90
70
|
return;
|
|
91
|
-
throw new CodexServiceError(
|
|
92
|
-
? "Request-bound per-device credentials are enabled; legacy direct assignment management is disabled."
|
|
93
|
-
: "Schema-v2 user-bound credentials are enabled; legacy refresh-token pool management is disabled.", SCHEMA_V2_LEGACY_MANAGEMENT_DISABLED, 409);
|
|
94
|
-
}
|
|
95
|
-
function assignmentCredentialStatus(assignment) {
|
|
96
|
-
if (isCodexSubscriptionExpired(assignment))
|
|
97
|
-
return "EXPIRED";
|
|
98
|
-
if (assignment.deliveryEligible)
|
|
99
|
-
return "AVAILABLE";
|
|
100
|
-
return ["NEEDS_REAUTH", "LOST", "DIVERGED", "COMPROMISED"].includes(assignment.lineageStatus ?? "") ? "NEEDS_REAUTH" : "TEMPORARILY_UNAVAILABLE";
|
|
71
|
+
throw new CodexServiceError("Request-bound per-device credentials are enabled; legacy direct assignment management is disabled.", SCHEMA_V2_LEGACY_MANAGEMENT_DISABLED, 409);
|
|
101
72
|
}
|
|
102
73
|
function assertCodexSubscriptionActive(subscription, label) {
|
|
103
74
|
if (!isCodexSubscriptionExpired(subscription))
|
|
@@ -290,31 +261,6 @@ async function loadAccountData(deps, options = {}, allowV4Replacement = false) {
|
|
|
290
261
|
preferredV3Assignments
|
|
291
262
|
};
|
|
292
263
|
}
|
|
293
|
-
if (deps.config.profile?.userBoundRefreshTokens?.delivery === true) {
|
|
294
|
-
const assignments = await deps.clientFor(serviceConnection(options)).listCredentialAssignments();
|
|
295
|
-
const accounts = assignments.items.map((assignment) => ({
|
|
296
|
-
id: assignment.managedAccountId,
|
|
297
|
-
accountId: assignment.assignmentId,
|
|
298
|
-
label: assignment.label,
|
|
299
|
-
isDefault: previous?.account.assignmentId === assignment.assignmentId,
|
|
300
|
-
...(assignment.credentialExpiresAt
|
|
301
|
-
? { accessExpiresAt: Date.parse(assignment.credentialExpiresAt) }
|
|
302
|
-
: {}),
|
|
303
|
-
...subscriptionSummary(assignment),
|
|
304
|
-
credentialStatus: assignmentCredentialStatus(assignment),
|
|
305
|
-
lineageStatus: assignment.lineageStatus ?? undefined,
|
|
306
|
-
quota: null
|
|
307
|
-
}));
|
|
308
|
-
return {
|
|
309
|
-
response: {
|
|
310
|
-
schemaVersion: CODEX_DISTRIBUTION_SCHEMA_VERSION,
|
|
311
|
-
accounts,
|
|
312
|
-
defaultAccountId: previous?.account.managedAccountId ?? null
|
|
313
|
-
},
|
|
314
|
-
selectedAccountId: previous?.account.managedAccountId,
|
|
315
|
-
assignments
|
|
316
|
-
};
|
|
317
|
-
}
|
|
318
264
|
const response = await deps.clientFor(serviceConnection(options)).listAccounts();
|
|
319
265
|
return {
|
|
320
266
|
response,
|
|
@@ -443,39 +389,6 @@ async function installCredentials(deps, selection, service, allowV4Replacement =
|
|
|
443
389
|
});
|
|
444
390
|
return { expiresAt: Date.parse(delivered.credentialExpiresAt), selection };
|
|
445
391
|
}
|
|
446
|
-
if (deps.config.profile?.userBoundRefreshTokens?.delivery === true) {
|
|
447
|
-
await discardCodexFileCredentialBackup(deps.env);
|
|
448
|
-
if (selection.account.distributionMode !== "SERVER_MANAGED_SHORT_LIVED"
|
|
449
|
-
|| !selection.account.assignmentId || !selection.account.managedAccountId) {
|
|
450
|
-
throw new CodexServiceError("The saved Codex selection is not a schema-v2 credential assignment.", "codex_assignment_not_found", 404);
|
|
451
|
-
}
|
|
452
|
-
const assignments = await client.listCredentialAssignments();
|
|
453
|
-
const assignment = assignments.items.find((item) => item.assignmentId === selection.account.assignmentId
|
|
454
|
-
&& item.managedAccountId === selection.account.managedAccountId);
|
|
455
|
-
if (!assignment) {
|
|
456
|
-
throw new CodexServiceError("The selected schema-v2 credential assignment is no longer active.", "codex_assignment_not_found", 404);
|
|
457
|
-
}
|
|
458
|
-
const state = await readManagedCodexLineageState(deps.env);
|
|
459
|
-
const journal = await readCodexShortLivedDeliveryJournal(deps.env);
|
|
460
|
-
const recoverable = journal?.tenantId === deps.config.connection.tenantId
|
|
461
|
-
&& journal.assignmentId === assignment.assignmentId
|
|
462
|
-
|| state?.assignmentId === assignment.assignmentId
|
|
463
|
-
&& (state.tenantId === deps.config.connection.tenantId || state.tenantId === null)
|
|
464
|
-
&& (state.phase === "INSTALLING"
|
|
465
|
-
|| state.phase === "INSTALLED_PENDING_ACK" && Date.parse(state.ackExpiresAt) > Date.now());
|
|
466
|
-
if (!assignment.deliveryEligible && !recoverable) {
|
|
467
|
-
throw new CodexServiceError("The selected schema-v2 credential assignment is not ready for delivery.", "short_lived_credential_lineage_not_ready", 409);
|
|
468
|
-
}
|
|
469
|
-
const delivered = await executeCodexShortLivedCredentialDelivery({
|
|
470
|
-
client,
|
|
471
|
-
tenantId: deps.config.connection.tenantId,
|
|
472
|
-
assignmentId: assignment.assignmentId,
|
|
473
|
-
allowNewDelivery: assignment.deliveryEligible,
|
|
474
|
-
env: deps.env
|
|
475
|
-
});
|
|
476
|
-
const updated = await rememberCodexSelectionProviderAccount(deps.config.persistence.configDirectory, assignment.assignmentId, delivered.providerAccountId);
|
|
477
|
-
return { expiresAt: Date.parse(delivered.credentialExpiresAt), selection: updated };
|
|
478
|
-
}
|
|
479
392
|
await backupCodexFileCredentials(deps.env);
|
|
480
393
|
const response = await client.getLoginCredentials(selection.account.id);
|
|
481
394
|
await writeCodexChatgptFileCredentials({
|
|
@@ -633,43 +546,6 @@ async function selectedCredentials(deps, requested, options = {}) {
|
|
|
633
546
|
if (!selection || serviceChanged || requested && !requestedMatchesSelection) {
|
|
634
547
|
selection = (await chooseAccount(deps, requested, options)).selection;
|
|
635
548
|
}
|
|
636
|
-
if (deps.config.profile?.userBoundRefreshTokens?.delivery === true) {
|
|
637
|
-
if (selection.account.distributionMode !== "SERVER_MANAGED_SHORT_LIVED") {
|
|
638
|
-
selection = (await chooseAccount(deps, requested, options)).selection;
|
|
639
|
-
}
|
|
640
|
-
const assignments = await deps.clientFor({
|
|
641
|
-
serverUrl: selection.serviceUrl,
|
|
642
|
-
allowInsecureHttp: selection.allowInsecurePoc
|
|
643
|
-
}).listCredentialAssignments();
|
|
644
|
-
const assignment = assignments.items.find((item) => item.assignmentId === selection.account.assignmentId
|
|
645
|
-
&& item.managedAccountId === selection.account.managedAccountId);
|
|
646
|
-
if (!assignment) {
|
|
647
|
-
throw new CodexServiceError("The selected schema-v2 credential assignment is no longer active.", "codex_assignment_not_found", 404);
|
|
648
|
-
}
|
|
649
|
-
assertCodexSubscriptionActive(assignment, assignment.label);
|
|
650
|
-
const state = await readManagedCodexLineageState(deps.env);
|
|
651
|
-
const journal = await readCodexShortLivedDeliveryJournal(deps.env);
|
|
652
|
-
const stateMatches = state !== undefined
|
|
653
|
-
&& (state.tenantId === deps.config.connection.tenantId || state.tenantId === null)
|
|
654
|
-
&& state.assignmentId === assignment.assignmentId
|
|
655
|
-
&& state.managedAccountId === assignment.managedAccountId;
|
|
656
|
-
const local = await inspectLocalCodexCredentialStatus(stateMatches ? state.providerAccountId : selection.account.providerAccountId, deps.env);
|
|
657
|
-
const pending = journal?.tenantId === deps.config.connection.tenantId
|
|
658
|
-
&& journal.assignmentId === assignment.assignmentId
|
|
659
|
-
|| stateMatches && state.phase !== "INSTALLED";
|
|
660
|
-
if (!assignment.deliveryEligible || !stateMatches || !local.usable
|
|
661
|
-
|| local.matchesExpectedAccount !== true || pending) {
|
|
662
|
-
return installCredentials(deps, selection, {
|
|
663
|
-
serverUrl: selection.serviceUrl,
|
|
664
|
-
allowInsecureHttp: selection.allowInsecurePoc
|
|
665
|
-
});
|
|
666
|
-
}
|
|
667
|
-
const matchingState = state;
|
|
668
|
-
if (selection.account.providerAccountId !== matchingState.providerAccountId) {
|
|
669
|
-
selection = await rememberCodexSelectionProviderAccount(deps.config.persistence.configDirectory, assignment.assignmentId, matchingState.providerAccountId);
|
|
670
|
-
}
|
|
671
|
-
return { selection, expiresAt: local.expiresAt ?? Date.parse(matchingState.credentialExpiresAt) };
|
|
672
|
-
}
|
|
673
549
|
if (!selection.account.accountId) {
|
|
674
550
|
throw new CodexServiceError("The saved Codex selection has no provider account.", "invalid_configuration");
|
|
675
551
|
}
|
|
@@ -762,21 +638,7 @@ async function launchWithSelectedCredentials(deps, selected, launch) {
|
|
|
762
638
|
}
|
|
763
639
|
}
|
|
764
640
|
}
|
|
765
|
-
|
|
766
|
-
return launch();
|
|
767
|
-
let current = selected;
|
|
768
|
-
return superviseCodexShortLivedCredentials({
|
|
769
|
-
initialExpiresAt: current.expiresAt,
|
|
770
|
-
launch: (signal) => launch(signal),
|
|
771
|
-
renew: async () => {
|
|
772
|
-
const installed = await installCredentials(deps, current.selection, {
|
|
773
|
-
serverUrl: current.selection.serviceUrl,
|
|
774
|
-
allowInsecureHttp: current.selection.allowInsecurePoc
|
|
775
|
-
});
|
|
776
|
-
current = installed;
|
|
777
|
-
return { expiresAt: installed.expiresAt };
|
|
778
|
-
}
|
|
779
|
-
});
|
|
641
|
+
return launch();
|
|
780
642
|
}
|
|
781
643
|
export function registerCodexCommands(program, deps, options = {}) {
|
|
782
644
|
const codex = options.nested === false
|
|
@@ -1093,8 +955,6 @@ export function registerCodexCommands(program, deps, options = {}) {
|
|
|
1093
955
|
.action(async (_options, command) => {
|
|
1094
956
|
const selection = await loadCodexSelection(deps.config.persistence.configDirectory);
|
|
1095
957
|
const cleanup = await cleanupLegacyCodexProvider(codexConfigPathForEnv(deps.env));
|
|
1096
|
-
if (schemaV2LifecycleEnabled(deps.config))
|
|
1097
|
-
await discardCodexFileCredentialBackup(deps.env);
|
|
1098
958
|
const auth = await resetManagedCodexFileCredentials(selection?.account.providerAccountId ?? selection?.account.accountId, deps.env);
|
|
1099
959
|
await resetCodexV6LocalState(deps.env);
|
|
1100
960
|
await clearCodexSelection(deps.config.persistence.configDirectory);
|
|
@@ -1193,7 +1053,7 @@ export function registerCodexCommands(program, deps, options = {}) {
|
|
|
1193
1053
|
if (!Number.isFinite(requestedRefreshBefore) || requestedRefreshBefore <= 0) {
|
|
1194
1054
|
throw new Error("--refresh-before must be positive.");
|
|
1195
1055
|
}
|
|
1196
|
-
const refreshBefore =
|
|
1056
|
+
const refreshBefore = requestedRefreshBefore;
|
|
1197
1057
|
let expiresAt = selected.expiresAt;
|
|
1198
1058
|
do {
|
|
1199
1059
|
const timestamp = Date.now();
|
|
@@ -1296,126 +1156,6 @@ export function registerCodexCommands(program, deps, options = {}) {
|
|
|
1296
1156
|
if (failures.length > 0)
|
|
1297
1157
|
process.exitCode = 1;
|
|
1298
1158
|
});
|
|
1299
|
-
if (schemaV2LifecycleEnabled(deps.config)) {
|
|
1300
|
-
const enrollment = codex.command("enrollment")
|
|
1301
|
-
.description("Execute administrator-created one-shot Codex login intents");
|
|
1302
|
-
serviceOptions(enrollment.command("status <intent>").description("Show redacted enrollment intent status"))
|
|
1303
|
-
.action(async (intent, opts, command) => {
|
|
1304
|
-
const result = await deps.clientFor(serviceConnection(opts)).getEnrollmentIntent(intent);
|
|
1305
|
-
if (await json(command, deps)) {
|
|
1306
|
-
print(deps.output, { ok: true, ...result });
|
|
1307
|
-
return;
|
|
1308
|
-
}
|
|
1309
|
-
deps.output.write(`${result.intent.id} ${result.intent.type} ${result.intent.status}`
|
|
1310
|
-
+ ` revision=${result.intent.revision}\n`);
|
|
1311
|
-
deps.output.write(`expires=${result.intent.expiresAt}`
|
|
1312
|
-
+ `${result.intent.assignmentId ? ` assignment=${result.intent.assignmentId}` : ""}`
|
|
1313
|
-
+ `${result.intent.managedAccountId ? ` account=${result.intent.managedAccountId}` : ""}\n`);
|
|
1314
|
-
if (!result.capabilities.candidateUpload) {
|
|
1315
|
-
deps.output.write("安全候选上传尚未启用;当前任务只能查询或取消,不会启动 OpenAI 登录。\n");
|
|
1316
|
-
}
|
|
1317
|
-
});
|
|
1318
|
-
serviceOptions(enrollment.command("login <intent>")
|
|
1319
|
-
.description("Run one isolated official Codex login for an enrollment intent"))
|
|
1320
|
-
.option("--device-auth", "use the official Codex device authorization flow")
|
|
1321
|
-
.action(async (intent, _opts, command) => {
|
|
1322
|
-
const result = await executeCodexEnrollmentLogin({
|
|
1323
|
-
client: deps.clientFor(serviceConnection(_opts)),
|
|
1324
|
-
tenantId: deps.config.connection.tenantId,
|
|
1325
|
-
intentId: intent,
|
|
1326
|
-
deviceAuth: _opts.deviceAuth
|
|
1327
|
-
});
|
|
1328
|
-
if (await json(command, deps))
|
|
1329
|
-
print(deps.output, { ok: true, ...result });
|
|
1330
|
-
else {
|
|
1331
|
-
deps.output.write(`${result.intent.id} 登录候选已处理;状态=${result.intent.status}`
|
|
1332
|
-
+ `${result.result.managedAccountId ? `,账号=${result.result.managedAccountId}` : ""}`
|
|
1333
|
-
+ `${result.result.lineageId ? `,lineage=${result.result.lineageId}` : ""}`
|
|
1334
|
-
+ "。\n");
|
|
1335
|
-
if (result.result.outcome === "TEMPORARILY_UNAVAILABLE") {
|
|
1336
|
-
deps.output.write("同一进程内的安全重试已耗尽;请取消并新建任务,不能用另一次登录覆盖已保留的候选。\n");
|
|
1337
|
-
}
|
|
1338
|
-
}
|
|
1339
|
-
});
|
|
1340
|
-
serviceOptions(enrollment.command("login-batch <planId>")
|
|
1341
|
-
.description("Sequentially execute isolated logins from a migration plan queue")
|
|
1342
|
-
.option("--device-auth", "use the official Codex device authorization flow")
|
|
1343
|
-
.option("--dry-run", "show the redacted sequential plan without starting login")
|
|
1344
|
-
.option("--page-size <count>", "migration queue page size", "100"))
|
|
1345
|
-
.action(async (planId, opts, command) => {
|
|
1346
|
-
if (deps.config.profile?.userBoundRefreshTokens?.migrationPlans !== true) {
|
|
1347
|
-
throw new CodexServiceError("The selected tenant has not enabled schema-v2 migration login plans.", "capability_not_enabled", 409);
|
|
1348
|
-
}
|
|
1349
|
-
const pageSize = Number(opts.pageSize);
|
|
1350
|
-
if (!Number.isInteger(pageSize) || pageSize < 1 || pageSize > 100) {
|
|
1351
|
-
throw new CodexServiceError("--page-size must be an integer from 1 to 100.", "invalid_configuration");
|
|
1352
|
-
}
|
|
1353
|
-
const batch = await executeCodexMigrationLoginBatch({
|
|
1354
|
-
client: deps.clientFor(serviceConnection(opts)),
|
|
1355
|
-
tenantId: deps.config.connection.tenantId,
|
|
1356
|
-
planId,
|
|
1357
|
-
deviceAuth: opts.deviceAuth,
|
|
1358
|
-
dryRun: opts.dryRun,
|
|
1359
|
-
pageSize
|
|
1360
|
-
});
|
|
1361
|
-
const failed = batch.counts.failed > 0
|
|
1362
|
-
|| ["TARGET_UNCERTAIN", "AUTHENTICATION_ERROR", "SERVICE_ERROR", "LOGIN_ERROR"]
|
|
1363
|
-
.includes(batch.stopReason);
|
|
1364
|
-
if (await json(command, deps)) {
|
|
1365
|
-
print(deps.output, { ok: !failed, ...batch });
|
|
1366
|
-
}
|
|
1367
|
-
else {
|
|
1368
|
-
deps.output.write(`migration=${batch.plan.id} status=${batch.plan.status} revision=${batch.plan.revision}`
|
|
1369
|
-
+ ` stop=${batch.stopReason}${batch.dryRun ? " dry-run" : ""}\n`);
|
|
1370
|
-
deps.output.write(`capacity: verifiedMinimum=${batch.capacity.verifiedMinimum}, verifiedLimit=unknown,`
|
|
1371
|
-
+ ` sequentialRequired=${batch.capacity.sequentialRequired}; verifiedMinimum 不是上限。\n`);
|
|
1372
|
-
for (const item of batch.items) {
|
|
1373
|
-
deps.output.write(`${item.assignmentId} ${item.action}/${item.status}`
|
|
1374
|
-
+ `${item.intentId ? ` intent=${item.intentId}` : ""}`
|
|
1375
|
-
+ `${item.outcome ? ` outcome=${item.outcome}` : ""}`
|
|
1376
|
-
+ `${item.skipReason ? ` skip=${item.skipReason}` : ""}`
|
|
1377
|
-
+ `${item.safeErrorCode ? ` error=${item.safeErrorCode}` : ""}\n`);
|
|
1378
|
-
}
|
|
1379
|
-
deps.output.write(`summary: attempted=${batch.counts.attempted}, completed=${batch.counts.completed},`
|
|
1380
|
-
+ ` failed=${batch.counts.failed}, skipped=${batch.counts.skipped}.\n`);
|
|
1381
|
-
}
|
|
1382
|
-
if (failed)
|
|
1383
|
-
process.exitCode = 1;
|
|
1384
|
-
});
|
|
1385
|
-
serviceOptions(enrollment.command("cancel <intent>").description("Cancel an enrollment intent"))
|
|
1386
|
-
.option("--idempotency-key <key>", "reuse a prior cancellation idempotency key")
|
|
1387
|
-
.action(async (intent, opts, command) => {
|
|
1388
|
-
const client = deps.clientFor(serviceConnection(opts));
|
|
1389
|
-
const current = await client.getEnrollmentIntent(intent);
|
|
1390
|
-
const result = await client.cancelEnrollmentIntent(intent, {
|
|
1391
|
-
idempotencyKey: opts.idempotencyKey?.trim() || randomUUID(),
|
|
1392
|
-
revision: current.intent.revision
|
|
1393
|
-
});
|
|
1394
|
-
if (await json(command, deps))
|
|
1395
|
-
print(deps.output, { ok: true, ...result });
|
|
1396
|
-
else
|
|
1397
|
-
deps.output.write(`${result.intent.id} 已取消;状态=${result.intent.status}。\n`);
|
|
1398
|
-
});
|
|
1399
|
-
const retirement = codex.command("retirement")
|
|
1400
|
-
.description("Remove an exact locally installed lineage for a Server retirement request");
|
|
1401
|
-
serviceOptions(retirement.command("acknowledge <retirement>")
|
|
1402
|
-
.description("Delete the matching managed short-lived credentials and append local evidence")
|
|
1403
|
-
.option("--idempotency-key <key>", "reuse a prior local deletion acknowledgement key"))
|
|
1404
|
-
.action(async (retirementId, opts, command) => {
|
|
1405
|
-
const result = await executeCodexAssignmentRetirementLocalDelete({
|
|
1406
|
-
client: deps.clientFor(serviceConnection(opts)),
|
|
1407
|
-
retirementId,
|
|
1408
|
-
env: deps.env,
|
|
1409
|
-
...(opts.idempotencyKey?.trim() ? { idempotencyKey: opts.idempotencyKey.trim() } : {})
|
|
1410
|
-
});
|
|
1411
|
-
if (await json(command, deps))
|
|
1412
|
-
print(deps.output, { ok: true, ...result });
|
|
1413
|
-
else
|
|
1414
|
-
deps.output.write(`本机匹配的短效凭据已删除并记录证据;retirement=${result.retirement.id}`
|
|
1415
|
-
+ `,revision=${result.retirement.revision},状态=${result.retirement.status}。\n`
|
|
1416
|
-
+ "该证据不代表其他终端已清理;最终退役仍等待全部已披露短效凭据自然过期。\n");
|
|
1417
|
-
});
|
|
1418
|
-
}
|
|
1419
1159
|
const admin = codex.command("admin")
|
|
1420
1160
|
.description("Administer server-managed Codex accounts and assignments");
|
|
1421
1161
|
const adminRequests = admin.command("requests")
|