@tokensrc/codex 1.14.4 → 1.14.6
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 +4 -4
- package/dist/command-directory.js +1 -1
- package/dist/command-directory.js.map +1 -1
- package/dist/commands.js +169 -228
- package/dist/commands.js.map +1 -1
- package/dist/managed-credential-v3-envelope.js +5 -5
- package/dist/managed-credential-v3-envelope.js.map +1 -1
- package/dist/managed-credential-v3-flow.d.ts +9 -0
- package/dist/managed-credential-v3-flow.js +56 -4
- package/dist/managed-credential-v3-flow.js.map +1 -1
- package/dist/managed-credential-v3-journal.d.ts +17 -0
- package/dist/managed-credential-v3-journal.js +43 -0
- package/dist/managed-credential-v3-journal.js.map +1 -1
- package/dist/profile.d.ts +2 -0
- package/dist/profile.js +31 -5
- package/dist/profile.js.map +1 -1
- package/dist/protocol.d.ts +83 -13
- package/dist/protocol.js +20 -15
- package/dist/protocol.js.map +1 -1
- package/dist/selection.d.ts +1 -1
- package/dist/selection.js +18 -6
- package/dist/selection.js.map +1 -1
- package/dist/token-pool-v4-upload-flow.js +6 -5
- package/dist/token-pool-v4-upload-flow.js.map +1 -1
- package/dist/tui.js +3 -3
- package/dist/tui.js.map +1 -1
- package/package.json +2 -2
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
|
|
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
|
|
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
|
|
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
|
|
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: "
|
|
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":"AAWA,MAAM,WAAW,GAAsC;IACrD,EAAE,IAAI,EAAE,OAAO,EAAE,WAAW,EAAE,wCAAwC,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,gBAAgB,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,sBAAsB,CAAC,EAAE,cAAc,EAAE,IAAI,EAAE;IACjM,EAAE,IAAI,EAAE,OAAO,EAAE,WAAW,EAAE,oEAAoE,EAAE,OAAO,EAAE,aAAa,EAAE,OAAO,EAAE,2DAA2D,EAAE,MAAM,EAAE,eAAe,EAAE,QAAQ,EAAE,CAAC,kBAAkB,EAAE,+CAA+C,EAAE,iFAAiF,CAAC,EAAE,UAAU,EAAE,IAAI,EAAE;IACjZ,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,wCAAwC,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,gBAAgB,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,uBAAuB,CAAC,EAAE,cAAc,EAAE,IAAI,EAAE;IACpM,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,+BAA+B,EAAE,OAAO,EAAE,gCAAgC,EAAE,OAAO,EAAE,qCAAqC,EAAE,MAAM,EAAE,eAAe,EAAE,QAAQ,EAAE,CAAC,uDAAuD,CAAC,EAAE;IACvP,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,
|
|
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 {
|
|
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 {
|
|
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
|
|
103
|
-
return
|
|
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,23 +114,26 @@ function formatAdminAccount(account) {
|
|
|
151
114
|
}
|
|
152
115
|
async function loadAccountData(deps, options = {}) {
|
|
153
116
|
const previous = await loadCodexSelection(deps.config.persistence.configDirectory);
|
|
154
|
-
if (
|
|
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,
|
|
158
121
|
accountId: assignment.assignmentId,
|
|
159
122
|
label: assignment.label,
|
|
160
|
-
isDefault:
|
|
123
|
+
isDefault: previous?.account.distributionMode === "CLIENT_MANAGED_V4"
|
|
124
|
+
&& previous.account.assignmentId === assignment.assignmentId,
|
|
161
125
|
accessExpiresAt: Date.parse(assignment.credentialExpiresAt),
|
|
162
126
|
credentialStatus: assignment.lineageStatus === "NEEDS_REAUTH"
|
|
127
|
+
|| assignment.refreshTokenHealth === "REJECTED"
|
|
163
128
|
? "NEEDS_REAUTH"
|
|
164
|
-
: assignment.authorityMode === "CLIENT_MANAGED"
|
|
129
|
+
: assignment.deliveryEligible || assignment.authorityMode === "CLIENT_MANAGED"
|
|
130
|
+
? "AVAILABLE" : "TEMPORARILY_UNAVAILABLE",
|
|
165
131
|
lineageStatus: assignment.lineageStatus === "STALE" ? "SYNC_LAGGING" : assignment.lineageStatus,
|
|
166
|
-
quota: null
|
|
132
|
+
quota: assignment.quota ?? null
|
|
167
133
|
}));
|
|
168
134
|
return {
|
|
169
135
|
response: { schemaVersion: CODEX_DISTRIBUTION_SCHEMA_VERSION, accounts,
|
|
170
|
-
defaultAccountId:
|
|
136
|
+
defaultAccountId: previous?.account.managedAccountId ?? null },
|
|
171
137
|
selectedAccountId: previous?.account.managedAccountId,
|
|
172
138
|
v3Assignments
|
|
173
139
|
};
|
|
@@ -234,7 +200,7 @@ async function chooseAccount(deps, requested, options = {}) {
|
|
|
234
200
|
id: v3Assignment.managedAccountId,
|
|
235
201
|
accountId: v3Assignment.providerAccountId,
|
|
236
202
|
label: accountLabel,
|
|
237
|
-
distributionMode: "
|
|
203
|
+
distributionMode: "CLIENT_MANAGED_V4",
|
|
238
204
|
assignmentId: v3Assignment.assignmentId,
|
|
239
205
|
managedAccountId: v3Assignment.managedAccountId,
|
|
240
206
|
providerAccountId: v3Assignment.providerAccountId
|
|
@@ -260,22 +226,36 @@ async function chooseAccount(deps, requested, options = {}) {
|
|
|
260
226
|
async function installCredentials(deps, selection, service) {
|
|
261
227
|
const client = deps.clientFor(service);
|
|
262
228
|
await cleanupLegacyCodexProvider(codexConfigPathForEnv(deps.env));
|
|
263
|
-
if (
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|| selection.account.
|
|
267
|
-
|
|
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);
|
|
229
|
+
if (accountTokenPoolV4Enabled(deps.config)) {
|
|
230
|
+
if (selection.account.distributionMode !== "CLIENT_MANAGED_V4"
|
|
231
|
+
|| !selection.account.assignmentId || !selection.account.managedAccountId
|
|
232
|
+
|| !selection.account.providerAccountId) {
|
|
233
|
+
throw new CodexServiceError("The selected account is not a v4 token-pool assignment.", "codex_assignment_not_found", 404);
|
|
271
234
|
}
|
|
272
235
|
const assignments = await client.listV3CredentialAssignments();
|
|
273
|
-
const
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
236
|
+
const assignment = assignments.items.find((item) => item.assignmentId === selection.account.assignmentId
|
|
237
|
+
&& item.managedAccountId === selection.account.managedAccountId
|
|
238
|
+
&& item.providerAccountId === selection.account.providerAccountId);
|
|
239
|
+
if (!assignment) {
|
|
240
|
+
throw new CodexServiceError("The v4 token-pool assignment is no longer active.", "codex_assignment_not_found", 404);
|
|
241
|
+
}
|
|
242
|
+
const state = await readManagedCodexV3State(deps.env);
|
|
243
|
+
if (state?.phase === "INSTALLED" && state.authorityMode === "CLIENT_MANAGED"
|
|
244
|
+
&& selection.account.assignmentId === state.assignmentId
|
|
245
|
+
&& selection.account.managedAccountId === state.managedAccountId
|
|
246
|
+
&& selection.account.providerAccountId === state.providerAccountId
|
|
247
|
+
&& assignment.lineageId === state.lineageId
|
|
248
|
+
&& assignment.currentGenerationVersion === state.generationVersion
|
|
249
|
+
&& assignment.authorityMode === "CLIENT_MANAGED") {
|
|
250
|
+
return { expiresAt: Date.parse(state.credentialExpiresAt), selection };
|
|
277
251
|
}
|
|
278
|
-
|
|
252
|
+
const delivered = await executeCodexTokenPoolV4Delivery({
|
|
253
|
+
client,
|
|
254
|
+
tenantId: deps.config.connection.tenantId,
|
|
255
|
+
assignment,
|
|
256
|
+
env: deps.env
|
|
257
|
+
});
|
|
258
|
+
return { expiresAt: Date.parse(delivered.credentialExpiresAt), selection };
|
|
279
259
|
}
|
|
280
260
|
if (deps.config.profile?.userBoundRefreshTokens?.delivery === true) {
|
|
281
261
|
await discardCodexFileCredentialBackup(deps.env);
|
|
@@ -320,10 +300,11 @@ async function installCredentials(deps, selection, service) {
|
|
|
320
300
|
return { expiresAt: response.expiresAt, selection };
|
|
321
301
|
}
|
|
322
302
|
async function selectedCredentials(deps, requested, options = {}) {
|
|
323
|
-
if (
|
|
303
|
+
if (accountTokenPoolV4Enabled(deps.config)) {
|
|
324
304
|
const state = await readManagedCodexV3State(deps.env);
|
|
325
305
|
if (!state || state.phase !== "INSTALLED" || state.authorityMode !== "CLIENT_MANAGED") {
|
|
326
|
-
|
|
306
|
+
const selection = (await chooseAccount(deps, requested, options)).selection;
|
|
307
|
+
return installCredentials(deps, selection, serviceConnection(options));
|
|
327
308
|
}
|
|
328
309
|
if (requested && ![
|
|
329
310
|
state.assignmentId,
|
|
@@ -331,7 +312,8 @@ async function selectedCredentials(deps, requested, options = {}) {
|
|
|
331
312
|
state.providerAccountId,
|
|
332
313
|
state.lineageId
|
|
333
314
|
].includes(requested)) {
|
|
334
|
-
|
|
315
|
+
const selection = (await chooseAccount(deps, requested, options)).selection;
|
|
316
|
+
return installCredentials(deps, selection, serviceConnection(options));
|
|
335
317
|
}
|
|
336
318
|
const assignments = await deps.clientFor(serviceConnection(options)).listV3CredentialAssignments();
|
|
337
319
|
const assignment = findV3Assignment({ assignments, ...state });
|
|
@@ -340,7 +322,7 @@ async function selectedCredentials(deps, requested, options = {}) {
|
|
|
340
322
|
|| assignment.providerAccountId !== state.providerAccountId
|
|
341
323
|
|| assignment.lineageId !== state.lineageId
|
|
342
324
|
|| assignment.authorityMode !== "CLIENT_MANAGED") {
|
|
343
|
-
throw new CodexServiceError("The local
|
|
325
|
+
throw new CodexServiceError("The local v4 lineage no longer matches the server assignment.", "lineage_version_conflict", 409);
|
|
344
326
|
}
|
|
345
327
|
const selection = await saveCodexSelection(deps.config.persistence.configDirectory, {
|
|
346
328
|
serviceUrl: options.server ?? deps.config.connection.serverUrl,
|
|
@@ -349,7 +331,7 @@ async function selectedCredentials(deps, requested, options = {}) {
|
|
|
349
331
|
id: state.managedAccountId,
|
|
350
332
|
accountId: state.providerAccountId,
|
|
351
333
|
label: assignment.label,
|
|
352
|
-
distributionMode: "
|
|
334
|
+
distributionMode: "CLIENT_MANAGED_V4",
|
|
353
335
|
assignmentId: state.assignmentId,
|
|
354
336
|
managedAccountId: state.managedAccountId,
|
|
355
337
|
providerAccountId: state.providerAccountId
|
|
@@ -422,7 +404,7 @@ async function selectedCredentials(deps, requested, options = {}) {
|
|
|
422
404
|
return { selection, expiresAt: local.expiresAt };
|
|
423
405
|
}
|
|
424
406
|
async function launchWithSelectedCredentials(deps, selected, launch) {
|
|
425
|
-
if (
|
|
407
|
+
if (accountTokenPoolV4Enabled(deps.config)) {
|
|
426
408
|
try {
|
|
427
409
|
return await launch();
|
|
428
410
|
}
|
|
@@ -481,7 +463,7 @@ export function registerCodexCommands(program, deps, options = {}) {
|
|
|
481
463
|
else
|
|
482
464
|
deps.output.write("Logged out.\n");
|
|
483
465
|
});
|
|
484
|
-
if (!includeAuthCommands &&
|
|
466
|
+
if (!includeAuthCommands && accountTokenPoolV4Enabled(deps.config))
|
|
485
467
|
serviceOptions(codex.command("login")
|
|
486
468
|
.description("Run an isolated official Codex login and enroll an unbound or explicitly assigned credential"))
|
|
487
469
|
.option("--device-auth", "use the official Codex device authorization flow")
|
|
@@ -489,8 +471,8 @@ export function registerCodexCommands(program, deps, options = {}) {
|
|
|
489
471
|
.option("--assign-to-subject <uuid>", "assign directly to a canonical Keycloak subject UUID")
|
|
490
472
|
.option("--yes", "confirm direct subject assignment")
|
|
491
473
|
.action(async (opts, command) => {
|
|
492
|
-
if (!
|
|
493
|
-
throw new CodexServiceError("
|
|
474
|
+
if (!accountTokenPoolV4Enabled(deps.config)) {
|
|
475
|
+
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
476
|
}
|
|
495
477
|
const target = resolveManagedCodexV3AssignmentTarget(opts);
|
|
496
478
|
const result = await executeCodexTokenPoolV4Login({
|
|
@@ -652,7 +634,7 @@ export function registerCodexCommands(program, deps, options = {}) {
|
|
|
652
634
|
throw new Error("auth-agent replaces the current login; add --replace-login to confirm.");
|
|
653
635
|
if (opts.credentialStore !== "file")
|
|
654
636
|
throw new Error("ChatGPT auth.json 同步目前仅支持 --credential-store file。");
|
|
655
|
-
if (
|
|
637
|
+
if (accountTokenPoolV4Enabled(deps.config)) {
|
|
656
638
|
const interval = Number(opts.interval) * 1_000;
|
|
657
639
|
if (!Number.isFinite(interval) || interval <= 0)
|
|
658
640
|
throw new Error("--interval must be positive.");
|
|
@@ -725,55 +707,12 @@ export function registerCodexCommands(program, deps, options = {}) {
|
|
|
725
707
|
await new Promise((resolve) => setTimeout(resolve, interval));
|
|
726
708
|
} while (true);
|
|
727
709
|
});
|
|
728
|
-
serviceOptions(codex.command("upload").description("
|
|
710
|
+
serviceOptions(codex.command("upload").description("Upload the legacy v1 local account pool"))
|
|
729
711
|
.option("--storage <path>", "account storage path")
|
|
730
712
|
.option("--source <name>", "upload source")
|
|
731
713
|
.action(async (opts, command) => {
|
|
732
|
-
if (
|
|
733
|
-
|
|
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;
|
|
714
|
+
if (accountTokenPoolV4Enabled(deps.config)) {
|
|
715
|
+
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
716
|
}
|
|
778
717
|
assertLegacyManagementEnabled(deps.config);
|
|
779
718
|
if (opts.storage)
|
|
@@ -833,124 +772,126 @@ export function registerCodexCommands(program, deps, options = {}) {
|
|
|
833
772
|
if (failures.length > 0)
|
|
834
773
|
process.exitCode = 1;
|
|
835
774
|
});
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
.
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
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
|
|
775
|
+
if (schemaV2LifecycleEnabled(deps.config)) {
|
|
776
|
+
const enrollment = codex.command("enrollment")
|
|
777
|
+
.description("Execute administrator-created one-shot Codex login intents");
|
|
778
|
+
serviceOptions(enrollment.command("status <intent>").description("Show redacted enrollment intent status"))
|
|
779
|
+
.action(async (intent, opts, command) => {
|
|
780
|
+
const result = await deps.clientFor(serviceConnection(opts)).getEnrollmentIntent(intent);
|
|
781
|
+
if (await json(command, deps)) {
|
|
782
|
+
print(deps.output, { ok: true, ...result });
|
|
783
|
+
return;
|
|
784
|
+
}
|
|
785
|
+
deps.output.write(`${result.intent.id} ${result.intent.type} ${result.intent.status}`
|
|
786
|
+
+ ` revision=${result.intent.revision}\n`);
|
|
787
|
+
deps.output.write(`expires=${result.intent.expiresAt}`
|
|
788
|
+
+ `${result.intent.assignmentId ? ` assignment=${result.intent.assignmentId}` : ""}`
|
|
789
|
+
+ `${result.intent.managedAccountId ? ` account=${result.intent.managedAccountId}` : ""}\n`);
|
|
790
|
+
if (!result.capabilities.candidateUpload) {
|
|
791
|
+
deps.output.write("安全候选上传尚未启用;当前任务只能查询或取消,不会启动 OpenAI 登录。\n");
|
|
792
|
+
}
|
|
863
793
|
});
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
794
|
+
serviceOptions(enrollment.command("login <intent>")
|
|
795
|
+
.description("Run one isolated official Codex login for an enrollment intent"))
|
|
796
|
+
.option("--device-auth", "use the official Codex device authorization flow")
|
|
797
|
+
.action(async (intent, _opts, command) => {
|
|
798
|
+
const result = await executeCodexEnrollmentLogin({
|
|
799
|
+
client: deps.clientFor(serviceConnection(_opts)),
|
|
800
|
+
tenantId: deps.config.connection.tenantId,
|
|
801
|
+
intentId: intent,
|
|
802
|
+
deviceAuth: _opts.deviceAuth
|
|
803
|
+
});
|
|
804
|
+
if (await json(command, deps))
|
|
805
|
+
print(deps.output, { ok: true, ...result });
|
|
806
|
+
else {
|
|
807
|
+
deps.output.write(`${result.intent.id} 登录候选已处理;状态=${result.intent.status}`
|
|
808
|
+
+ `${result.result.managedAccountId ? `,账号=${result.result.managedAccountId}` : ""}`
|
|
809
|
+
+ `${result.result.lineageId ? `,lineage=${result.result.lineageId}` : ""}`
|
|
810
|
+
+ "。\n");
|
|
811
|
+
if (result.result.outcome === "TEMPORARILY_UNAVAILABLE") {
|
|
812
|
+
deps.output.write("同一进程内的安全重试已耗尽;请取消并新建任务,不能用另一次登录覆盖已保留的候选。\n");
|
|
813
|
+
}
|
|
873
814
|
}
|
|
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
815
|
});
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
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`);
|
|
816
|
+
serviceOptions(enrollment.command("login-batch <planId>")
|
|
817
|
+
.description("Sequentially execute isolated logins from a migration plan queue")
|
|
818
|
+
.option("--device-auth", "use the official Codex device authorization flow")
|
|
819
|
+
.option("--dry-run", "show the redacted sequential plan without starting login")
|
|
820
|
+
.option("--page-size <count>", "migration queue page size", "100"))
|
|
821
|
+
.action(async (planId, opts, command) => {
|
|
822
|
+
if (deps.config.profile?.userBoundRefreshTokens?.migrationPlans !== true) {
|
|
823
|
+
throw new CodexServiceError("The selected tenant has not enabled schema-v2 migration login plans.", "capability_not_enabled", 409);
|
|
914
824
|
}
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
825
|
+
const pageSize = Number(opts.pageSize);
|
|
826
|
+
if (!Number.isInteger(pageSize) || pageSize < 1 || pageSize > 100) {
|
|
827
|
+
throw new CodexServiceError("--page-size must be an integer from 1 to 100.", "invalid_configuration");
|
|
828
|
+
}
|
|
829
|
+
const batch = await executeCodexMigrationLoginBatch({
|
|
830
|
+
client: deps.clientFor(serviceConnection(opts)),
|
|
831
|
+
tenantId: deps.config.connection.tenantId,
|
|
832
|
+
planId,
|
|
833
|
+
deviceAuth: opts.deviceAuth,
|
|
834
|
+
dryRun: opts.dryRun,
|
|
835
|
+
pageSize
|
|
836
|
+
});
|
|
837
|
+
const failed = batch.counts.failed > 0
|
|
838
|
+
|| ["TARGET_UNCERTAIN", "AUTHENTICATION_ERROR", "SERVICE_ERROR", "LOGIN_ERROR"]
|
|
839
|
+
.includes(batch.stopReason);
|
|
840
|
+
if (await json(command, deps)) {
|
|
841
|
+
print(deps.output, { ok: !failed, ...batch });
|
|
842
|
+
}
|
|
843
|
+
else {
|
|
844
|
+
deps.output.write(`migration=${batch.plan.id} status=${batch.plan.status} revision=${batch.plan.revision}`
|
|
845
|
+
+ ` stop=${batch.stopReason}${batch.dryRun ? " dry-run" : ""}\n`);
|
|
846
|
+
deps.output.write(`capacity: verifiedMinimum=${batch.capacity.verifiedMinimum}, verifiedLimit=unknown,`
|
|
847
|
+
+ ` sequentialRequired=${batch.capacity.sequentialRequired}; verifiedMinimum 不是上限。\n`);
|
|
848
|
+
for (const item of batch.items) {
|
|
849
|
+
deps.output.write(`${item.assignmentId} ${item.action}/${item.status}`
|
|
850
|
+
+ `${item.intentId ? ` intent=${item.intentId}` : ""}`
|
|
851
|
+
+ `${item.outcome ? ` outcome=${item.outcome}` : ""}`
|
|
852
|
+
+ `${item.skipReason ? ` skip=${item.skipReason}` : ""}`
|
|
853
|
+
+ `${item.safeErrorCode ? ` error=${item.safeErrorCode}` : ""}\n`);
|
|
854
|
+
}
|
|
855
|
+
deps.output.write(`summary: attempted=${batch.counts.attempted}, completed=${batch.counts.completed},`
|
|
856
|
+
+ ` failed=${batch.counts.failed}, skipped=${batch.counts.skipped}.\n`);
|
|
857
|
+
}
|
|
858
|
+
if (failed)
|
|
859
|
+
process.exitCode = 1;
|
|
929
860
|
});
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
deps.
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
retirementId,
|
|
944
|
-
env: deps.env,
|
|
945
|
-
...(opts.idempotencyKey?.trim() ? { idempotencyKey: opts.idempotencyKey.trim() } : {})
|
|
861
|
+
serviceOptions(enrollment.command("cancel <intent>").description("Cancel an enrollment intent"))
|
|
862
|
+
.option("--idempotency-key <key>", "reuse a prior cancellation idempotency key")
|
|
863
|
+
.action(async (intent, opts, command) => {
|
|
864
|
+
const client = deps.clientFor(serviceConnection(opts));
|
|
865
|
+
const current = await client.getEnrollmentIntent(intent);
|
|
866
|
+
const result = await client.cancelEnrollmentIntent(intent, {
|
|
867
|
+
idempotencyKey: opts.idempotencyKey?.trim() || randomUUID(),
|
|
868
|
+
revision: current.intent.revision
|
|
869
|
+
});
|
|
870
|
+
if (await json(command, deps))
|
|
871
|
+
print(deps.output, { ok: true, ...result });
|
|
872
|
+
else
|
|
873
|
+
deps.output.write(`${result.intent.id} 已取消;状态=${result.intent.status}。\n`);
|
|
946
874
|
});
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
875
|
+
const retirement = codex.command("retirement")
|
|
876
|
+
.description("Remove an exact locally installed lineage for a Server retirement request");
|
|
877
|
+
serviceOptions(retirement.command("acknowledge <retirement>")
|
|
878
|
+
.description("Delete the matching managed short-lived credentials and append local evidence")
|
|
879
|
+
.option("--idempotency-key <key>", "reuse a prior local deletion acknowledgement key"))
|
|
880
|
+
.action(async (retirementId, opts, command) => {
|
|
881
|
+
const result = await executeCodexAssignmentRetirementLocalDelete({
|
|
882
|
+
client: deps.clientFor(serviceConnection(opts)),
|
|
883
|
+
retirementId,
|
|
884
|
+
env: deps.env,
|
|
885
|
+
...(opts.idempotencyKey?.trim() ? { idempotencyKey: opts.idempotencyKey.trim() } : {})
|
|
886
|
+
});
|
|
887
|
+
if (await json(command, deps))
|
|
888
|
+
print(deps.output, { ok: true, ...result });
|
|
889
|
+
else
|
|
890
|
+
deps.output.write(`本机匹配的短效凭据已删除并记录证据;retirement=${result.retirement.id}`
|
|
891
|
+
+ `,revision=${result.retirement.revision},状态=${result.retirement.status}。\n`
|
|
892
|
+
+ "该证据不代表其他终端已清理;最终退役仍等待全部已披露短效凭据自然过期。\n");
|
|
893
|
+
});
|
|
894
|
+
}
|
|
954
895
|
const admin = codex.command("admin")
|
|
955
896
|
.description("Administer server-managed Codex accounts and assignments");
|
|
956
897
|
const adminAccounts = admin.command("accounts")
|