oc-codex-multi-auth 6.13.0 → 6.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +305 -47
- package/dist/index.js.map +1 -1
- package/dist/lib/accounts/state.d.ts.map +1 -1
- package/dist/lib/accounts/state.js +2 -2
- package/dist/lib/accounts/state.js.map +1 -1
- package/dist/lib/codex-usage.d.ts.map +1 -1
- package/dist/lib/codex-usage.js +14 -79
- package/dist/lib/codex-usage.js.map +1 -1
- package/dist/lib/errors.d.ts +7 -0
- package/dist/lib/errors.d.ts.map +1 -1
- package/dist/lib/errors.js +14 -0
- package/dist/lib/errors.js.map +1 -1
- package/dist/lib/proactive-refresh.d.ts.map +1 -1
- package/dist/lib/proactive-refresh.js +14 -3
- package/dist/lib/proactive-refresh.js.map +1 -1
- package/dist/lib/request/fetch-helpers.d.ts +2 -1
- package/dist/lib/request/fetch-helpers.d.ts.map +1 -1
- package/dist/lib/request/fetch-helpers.js +17 -4
- package/dist/lib/request/fetch-helpers.js.map +1 -1
- package/dist/lib/storage/coordinated-refresh.d.ts +15 -0
- package/dist/lib/storage/coordinated-refresh.d.ts.map +1 -0
- package/dist/lib/storage/coordinated-refresh.js +246 -0
- package/dist/lib/storage/coordinated-refresh.js.map +1 -0
- package/dist/lib/storage/flagged.d.ts.map +1 -1
- package/dist/lib/storage/flagged.js +13 -3
- package/dist/lib/storage/flagged.js.map +1 -1
- package/dist/lib/storage/load-save.d.ts.map +1 -1
- package/dist/lib/storage/load-save.js +6 -3
- package/dist/lib/storage/load-save.js.map +1 -1
- package/dist/lib/storage/transaction-lock.d.ts +21 -0
- package/dist/lib/storage/transaction-lock.d.ts.map +1 -0
- package/dist/lib/storage/transaction-lock.js +194 -0
- package/dist/lib/storage/transaction-lock.js.map +1 -0
- package/dist/lib/tools/codex-note.d.ts.map +1 -1
- package/dist/lib/tools/codex-note.js +19 -9
- package/dist/lib/tools/codex-note.js.map +1 -1
- package/dist/lib/tools/codex-tag.d.ts.map +1 -1
- package/dist/lib/tools/codex-tag.js +23 -12
- package/dist/lib/tools/codex-tag.js.map +1 -1
- package/dist/lib/tools/refresh-account.d.ts +5 -15
- package/dist/lib/tools/refresh-account.d.ts.map +1 -1
- package/dist/lib/tools/refresh-account.js +17 -110
- package/dist/lib/tools/refresh-account.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,118 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Shared token refresh persistence for the account-management tools.
|
|
3
3
|
*
|
|
4
|
-
* OpenAI refresh tokens are single-use and rotate on exchange.
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* reports `refresh_token_reused` for every account.
|
|
4
|
+
* OpenAI refresh tokens are single-use and rotate on exchange. Production
|
|
5
|
+
* refreshes flow through `coordinatePersistedRefresh()`, which owns the
|
|
6
|
+
* authoritative reload, exchange, and durable commit under one lease.
|
|
8
7
|
*/
|
|
9
|
-
import { queuedRefresh } from "../refresh-queue.js";
|
|
10
8
|
import { extractAccountUserId } from "../auth/token-utils.js";
|
|
11
|
-
import { withAccountStorageTransaction, } from "../storage.js";
|
|
12
9
|
import { findAccountIndexByIdentityKeys, toAccountIdentityKeys, } from "../storage/identity.js";
|
|
10
|
+
import { coordinatePersistedRefresh } from "../storage/coordinated-refresh.js";
|
|
13
11
|
export function findAccountIndexByIdentity(accounts, identity) {
|
|
14
12
|
return findAccountIndexByIdentityKeys(accounts, toAccountIdentityKeys(identity));
|
|
15
13
|
}
|
|
16
|
-
/**
|
|
17
|
-
* Persists one successful refresh in a short storage transaction. The account
|
|
18
|
-
* is re-located from a freshly re-read snapshot, and sibling accounts that
|
|
19
|
-
* share the consumed single-use refresh token are rotated atomically with it.
|
|
20
|
-
*/
|
|
21
|
-
export async function persistRefreshResult(index, identity, refreshResult) {
|
|
22
|
-
const rotated = refreshResult.refresh !== identity.refreshToken;
|
|
23
|
-
const rotatedAt = rotated ? Date.now() : undefined;
|
|
24
|
-
try {
|
|
25
|
-
return await withAccountStorageTransaction(async (current, persist) => {
|
|
26
|
-
if (!current) {
|
|
27
|
-
throw new Error("Account storage is unavailable");
|
|
28
|
-
}
|
|
29
|
-
const idx = findAccountIndexByIdentity(current.accounts, identity);
|
|
30
|
-
if (idx < 0) {
|
|
31
|
-
throw new Error("Account was removed while its token was refreshing");
|
|
32
|
-
}
|
|
33
|
-
const target = current.accounts[idx];
|
|
34
|
-
if (!target) {
|
|
35
|
-
throw new Error("Account was removed while its token was refreshing");
|
|
36
|
-
}
|
|
37
|
-
if (target.refreshToken === identity.refreshToken) {
|
|
38
|
-
if (rotated) {
|
|
39
|
-
// A login can produce sibling org/workspace records sharing one
|
|
40
|
-
// single-use refresh token. Mirror the account-state propagation used
|
|
41
|
-
// by request-time refresh: update those records with the new token and
|
|
42
|
-
// expire their workspace-specific access tokens so they refresh cleanly.
|
|
43
|
-
// Do this before assigning the target, since siblings are matched by
|
|
44
|
-
// the consumed token and the target starts with that token too.
|
|
45
|
-
for (const sibling of current.accounts) {
|
|
46
|
-
if (sibling === target)
|
|
47
|
-
continue;
|
|
48
|
-
if (sibling.refreshToken !== identity.refreshToken)
|
|
49
|
-
continue;
|
|
50
|
-
sibling.refreshToken = refreshResult.refresh;
|
|
51
|
-
sibling.expiresAt = 0;
|
|
52
|
-
if (rotatedAt !== undefined) {
|
|
53
|
-
sibling.tokenRotatedAt = rotatedAt;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
target.refreshToken = refreshResult.refresh;
|
|
58
|
-
target.accountUserId =
|
|
59
|
-
extractAccountUserId(refreshResult.access) ?? target.accountUserId;
|
|
60
|
-
target.accessToken = refreshResult.access;
|
|
61
|
-
target.expiresAt = refreshResult.expires;
|
|
62
|
-
if (rotatedAt !== undefined) {
|
|
63
|
-
target.tokenRotatedAt = rotatedAt;
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
else if (target.refreshToken === refreshResult.refresh) {
|
|
67
|
-
// A sibling account in this process just persisted the same rotation
|
|
68
|
-
// and propagated the new token to this workspace record. Apply only
|
|
69
|
-
// this workspace's freshly returned access token; do not overwrite
|
|
70
|
-
// non-credential state in the fresh snapshot.
|
|
71
|
-
target.accessToken = refreshResult.access;
|
|
72
|
-
target.accountUserId =
|
|
73
|
-
extractAccountUserId(refreshResult.access) ?? target.accountUserId;
|
|
74
|
-
target.expiresAt = refreshResult.expires;
|
|
75
|
-
}
|
|
76
|
-
else {
|
|
77
|
-
// The refresh was keyed off `identity.refreshToken` (the consumed,
|
|
78
|
-
// pre-rotation value). If storage carries neither that token nor the
|
|
79
|
-
// exact rotation returned by this refresh, another process rotated
|
|
80
|
-
// this credential while this network-bound refresh was in flight. We
|
|
81
|
-
// cannot know which resulting chain is live, so fail this
|
|
82
|
-
// verification rather than clobber whatever the other process wrote.
|
|
83
|
-
throw new Error("Refresh token changed concurrently while verification was in progress");
|
|
84
|
-
}
|
|
85
|
-
await persist(current);
|
|
86
|
-
return {
|
|
87
|
-
index,
|
|
88
|
-
identity,
|
|
89
|
-
refreshToken: refreshResult.refresh,
|
|
90
|
-
accessToken: refreshResult.access,
|
|
91
|
-
expiresAt: refreshResult.expires,
|
|
92
|
-
rotatedAt,
|
|
93
|
-
persisted: true,
|
|
94
|
-
};
|
|
95
|
-
});
|
|
96
|
-
}
|
|
97
|
-
catch (error) {
|
|
98
|
-
return {
|
|
99
|
-
index,
|
|
100
|
-
identity,
|
|
101
|
-
refreshToken: refreshResult.refresh,
|
|
102
|
-
accessToken: refreshResult.access,
|
|
103
|
-
expiresAt: refreshResult.expires,
|
|
104
|
-
rotatedAt,
|
|
105
|
-
persisted: false,
|
|
106
|
-
persistError: error instanceof Error ? error.message : String(error),
|
|
107
|
-
};
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
14
|
/**
|
|
111
15
|
* Refreshes one account and persists the rotated credential before reporting
|
|
112
16
|
* success. Disabled standalone accounts are skipped: refreshing them is wrong
|
|
113
17
|
* (they may intentionally retain a dead duplicate credential), while disabled
|
|
114
18
|
* siblings sharing an enabled account's consumed token are still updated by
|
|
115
|
-
* `
|
|
19
|
+
* `coordinatePersistedRefresh()` so the shared credential remains consistent.
|
|
116
20
|
*/
|
|
117
21
|
export async function refreshAndPersistAccount(account) {
|
|
118
22
|
const { index, identity } = account;
|
|
@@ -121,7 +25,7 @@ export async function refreshAndPersistAccount(account) {
|
|
|
121
25
|
}
|
|
122
26
|
let refreshResult;
|
|
123
27
|
try {
|
|
124
|
-
refreshResult = await
|
|
28
|
+
refreshResult = await coordinatePersistedRefresh(identity);
|
|
125
29
|
}
|
|
126
30
|
catch (error) {
|
|
127
31
|
return {
|
|
@@ -139,16 +43,19 @@ export async function refreshAndPersistAccount(account) {
|
|
|
139
43
|
error: refreshResult.message ?? refreshResult.reason ?? "token refresh failed",
|
|
140
44
|
};
|
|
141
45
|
}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
46
|
+
return {
|
|
47
|
+
status: "refreshed",
|
|
48
|
+
index,
|
|
49
|
+
result: {
|
|
146
50
|
index,
|
|
147
51
|
identity,
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
52
|
+
refreshToken: refreshResult.refresh,
|
|
53
|
+
accessToken: refreshResult.access,
|
|
54
|
+
expiresAt: refreshResult.expires,
|
|
55
|
+
rotatedAt: refreshResult.rotatedAt,
|
|
56
|
+
persisted: true,
|
|
57
|
+
},
|
|
58
|
+
};
|
|
152
59
|
}
|
|
153
60
|
export function buildRefreshInputs(accounts) {
|
|
154
61
|
return accounts.map((account, index) => ({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"refresh-account.js","sourceRoot":"","sources":["../../../lib/tools/refresh-account.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"refresh-account.js","sourceRoot":"","sources":["../../../lib/tools/refresh-account.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAE9D,OAAO,EACN,8BAA8B,EAC9B,qBAAqB,GACrB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,0BAA0B,EAAE,MAAM,mCAAmC,CAAC;AA+B/E,MAAM,UAAU,0BAA0B,CACzC,QAAkC,EAClC,QAAgC;IAEhC,OAAO,8BAA8B,CAAC,QAAQ,EAAE,qBAAqB,CAAC,QAAQ,CAAC,CAAC,CAAC;AAClF,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAC7C,OAA4B;IAE5B,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC;IACpC,IAAI,OAAO,CAAC,OAAO,KAAK,KAAK,EAAE,CAAC;QAC/B,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC;IAC/C,CAAC;IAED,IAAI,aAAqE,CAAC;IAC1E,IAAI,CAAC;QACJ,aAAa,GAAG,MAAM,0BAA0B,CAAC,QAAQ,CAAC,CAAC;IAC5D,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,OAAO;YACN,MAAM,EAAE,QAAQ;YAChB,KAAK;YACL,QAAQ;YACR,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;SAC7D,CAAC;IACH,CAAC;IAED,IAAI,aAAa,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QACtC,OAAO;YACN,MAAM,EAAE,QAAQ;YAChB,KAAK;YACL,QAAQ;YACR,KAAK,EACJ,aAAa,CAAC,OAAO,IAAI,aAAa,CAAC,MAAM,IAAI,sBAAsB;SACxE,CAAC;IACH,CAAC;IAED,OAAO;QACN,MAAM,EAAE,WAAW;QACnB,KAAK;QACL,MAAM,EAAE;YACP,KAAK;YACL,QAAQ;YACR,YAAY,EAAE,aAAa,CAAC,OAAO;YACnC,WAAW,EAAE,aAAa,CAAC,MAAM;YACjC,SAAS,EAAE,aAAa,CAAC,OAAO;YAChC,SAAS,EAAE,aAAa,CAAC,SAAS;YAClC,SAAS,EAAE,IAAI;SACf;KACD,CAAC;AACH,CAAC;AAED,MAAM,UAAU,kBAAkB,CACjC,QAA6B;IAE7B,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;QACxC,KAAK;QACL,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,QAAQ,EAAE;YACT,cAAc,EAAE,OAAO,CAAC,cAAc;YACtC,SAAS,EAAE,OAAO,CAAC,SAAS;YAC5B,aAAa,EACZ,OAAO,CAAC,aAAa,EAAE,IAAI,EAAE,IAAI,oBAAoB,CAAC,OAAO,CAAC,WAAW,CAAC;YAC3E,YAAY,EAAE,OAAO,CAAC,YAAY;SAClC;KACD,CAAC,CAAC,CAAC;AACL,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "oc-codex-multi-auth",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.14.0",
|
|
4
4
|
"description": "OpenCode plugin for ChatGPT Plus/Pro OAuth with Codex/GPT-5 routing, multi-account rotation, account switching, health checks, diagnostics, and recovery tools",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|