oc-codex-multi-auth 6.0.0 → 6.1.1
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/LICENSE +1 -1
- package/README.md +56 -18
- package/assets/readme-hero.svg +10 -10
- package/config/README.md +1 -1
- package/config/minimal-opencode.json +1 -1
- package/config/opencode-legacy.json +1 -1
- package/config/opencode-modern.json +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +112 -2525
- package/dist/index.js.map +1 -1
- package/dist/lib/accounts/persistence.d.ts +34 -0
- package/dist/lib/accounts/persistence.d.ts.map +1 -0
- package/dist/lib/accounts/persistence.js +153 -0
- package/dist/lib/accounts/persistence.js.map +1 -0
- package/dist/lib/accounts/recovery.d.ts +59 -0
- package/dist/lib/accounts/recovery.d.ts.map +1 -0
- package/dist/lib/accounts/recovery.js +229 -0
- package/dist/lib/accounts/recovery.js.map +1 -0
- package/dist/lib/accounts/rotation.d.ts +40 -0
- package/dist/lib/accounts/rotation.d.ts.map +1 -0
- package/dist/lib/accounts/rotation.js +204 -0
- package/dist/lib/accounts/rotation.js.map +1 -0
- package/dist/lib/accounts/state.d.ts +92 -0
- package/dist/lib/accounts/state.d.ts.map +1 -0
- package/dist/lib/accounts/state.js +366 -0
- package/dist/lib/accounts/state.js.map +1 -0
- package/dist/lib/accounts.d.ts +48 -91
- package/dist/lib/accounts.d.ts.map +1 -1
- package/dist/lib/accounts.js +121 -699
- package/dist/lib/accounts.js.map +1 -1
- package/dist/lib/auth/auth.d.ts +1 -1
- package/dist/lib/auth/auth.d.ts.map +1 -1
- package/dist/lib/auth/auth.js +5 -2
- package/dist/lib/auth/auth.js.map +1 -1
- package/dist/lib/auth/login-runner.d.ts +32 -0
- package/dist/lib/auth/login-runner.d.ts.map +1 -1
- package/dist/lib/auth/login-runner.js +79 -64
- package/dist/lib/auth/login-runner.js.map +1 -1
- package/dist/lib/auth/server.d.ts.map +1 -1
- package/dist/lib/auth/server.js +3 -8
- package/dist/lib/auth/server.js.map +1 -1
- package/dist/lib/circuit-breaker.d.ts +53 -2
- package/dist/lib/circuit-breaker.d.ts.map +1 -1
- package/dist/lib/circuit-breaker.js +65 -6
- package/dist/lib/circuit-breaker.js.map +1 -1
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +77 -32
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/error-sentinels.d.ts +20 -0
- package/dist/lib/error-sentinels.d.ts.map +1 -0
- package/dist/lib/error-sentinels.js +28 -0
- package/dist/lib/error-sentinels.js.map +1 -0
- package/dist/lib/errors.d.ts +100 -1
- package/dist/lib/errors.d.ts.map +1 -1
- package/dist/lib/errors.js +116 -1
- package/dist/lib/errors.js.map +1 -1
- package/dist/lib/logger.d.ts +14 -1
- package/dist/lib/logger.d.ts.map +1 -1
- package/dist/lib/logger.js +65 -5
- package/dist/lib/logger.js.map +1 -1
- package/dist/lib/oauth-constants.d.ts +18 -0
- package/dist/lib/oauth-constants.d.ts.map +1 -0
- package/dist/lib/oauth-constants.js +18 -0
- package/dist/lib/oauth-constants.js.map +1 -0
- package/dist/lib/oauth-success.d.ts +2 -0
- package/dist/lib/oauth-success.d.ts.map +1 -0
- package/dist/lib/oauth-success.html +711 -711
- package/dist/lib/oauth-success.js +714 -0
- package/dist/lib/oauth-success.js.map +1 -0
- package/dist/lib/prompts/codex.d.ts.map +1 -1
- package/dist/lib/prompts/codex.js +12 -3
- package/dist/lib/prompts/codex.js.map +1 -1
- package/dist/lib/prompts/opencode-codex.d.ts.map +1 -1
- package/dist/lib/prompts/opencode-codex.js +3 -2
- package/dist/lib/prompts/opencode-codex.js.map +1 -1
- package/dist/lib/recovery/hook.d.ts +43 -0
- package/dist/lib/recovery/hook.d.ts.map +1 -0
- package/dist/lib/recovery/hook.js +325 -0
- package/dist/lib/recovery/hook.js.map +1 -0
- package/dist/lib/recovery/index.d.ts +1 -0
- package/dist/lib/recovery/index.d.ts.map +1 -1
- package/dist/lib/recovery/index.js +1 -0
- package/dist/lib/recovery/index.js.map +1 -1
- package/dist/lib/recovery/storage.d.ts.map +1 -1
- package/dist/lib/recovery/storage.js +5 -1
- package/dist/lib/recovery/storage.js.map +1 -1
- package/dist/lib/recovery.d.ts +15 -30
- package/dist/lib/recovery.d.ts.map +1 -1
- package/dist/lib/recovery.js +14 -312
- package/dist/lib/recovery.js.map +1 -1
- package/dist/lib/request/fetch-helpers.d.ts +1 -0
- package/dist/lib/request/fetch-helpers.d.ts.map +1 -1
- package/dist/lib/request/fetch-helpers.js +58 -10
- package/dist/lib/request/fetch-helpers.js.map +1 -1
- package/dist/lib/request/response-handler.d.ts.map +1 -1
- package/dist/lib/request/response-handler.js +8 -2
- package/dist/lib/request/response-handler.js.map +1 -1
- package/dist/lib/runtime.d.ts +101 -0
- package/dist/lib/runtime.d.ts.map +1 -0
- package/dist/lib/runtime.js +77 -0
- package/dist/lib/runtime.js.map +1 -0
- package/dist/lib/schemas.d.ts +164 -1
- package/dist/lib/schemas.d.ts.map +1 -1
- package/dist/lib/schemas.js +251 -0
- package/dist/lib/schemas.js.map +1 -1
- package/dist/lib/storage/atomic-write.d.ts +31 -0
- package/dist/lib/storage/atomic-write.d.ts.map +1 -0
- package/dist/lib/storage/atomic-write.js +75 -0
- package/dist/lib/storage/atomic-write.js.map +1 -0
- package/dist/lib/storage/backup.d.ts +13 -0
- package/dist/lib/storage/backup.d.ts.map +1 -0
- package/dist/lib/storage/backup.js +59 -0
- package/dist/lib/storage/backup.js.map +1 -0
- package/dist/lib/storage/errors.d.ts +17 -0
- package/dist/lib/storage/errors.d.ts.map +1 -0
- package/dist/lib/storage/errors.js +38 -0
- package/dist/lib/storage/errors.js.map +1 -0
- package/dist/lib/storage/export-import.d.ts +84 -0
- package/dist/lib/storage/export-import.d.ts.map +1 -0
- package/dist/lib/storage/export-import.js +222 -0
- package/dist/lib/storage/export-import.js.map +1 -0
- package/dist/lib/storage/flagged.d.ts +29 -0
- package/dist/lib/storage/flagged.d.ts.map +1 -0
- package/dist/lib/storage/flagged.js +207 -0
- package/dist/lib/storage/flagged.js.map +1 -0
- package/dist/lib/storage/identity.d.ts +72 -0
- package/dist/lib/storage/identity.d.ts.map +1 -0
- package/dist/lib/storage/identity.js +230 -0
- package/dist/lib/storage/identity.js.map +1 -0
- package/dist/lib/storage/keychain.d.ts +146 -0
- package/dist/lib/storage/keychain.d.ts.map +1 -0
- package/dist/lib/storage/keychain.js +278 -0
- package/dist/lib/storage/keychain.js.map +1 -0
- package/dist/lib/storage/load-save.d.ts +61 -0
- package/dist/lib/storage/load-save.d.ts.map +1 -0
- package/dist/lib/storage/load-save.js +606 -0
- package/dist/lib/storage/load-save.js.map +1 -0
- package/dist/lib/storage/migrations.d.ts +35 -0
- package/dist/lib/storage/migrations.d.ts.map +1 -1
- package/dist/lib/storage/migrations.js +37 -0
- package/dist/lib/storage/migrations.js.map +1 -1
- package/dist/lib/storage/normalize.d.ts +22 -0
- package/dist/lib/storage/normalize.d.ts.map +1 -0
- package/dist/lib/storage/normalize.js +114 -0
- package/dist/lib/storage/normalize.js.map +1 -0
- package/dist/lib/storage/paths.d.ts.map +1 -1
- package/dist/lib/storage/paths.js +2 -1
- package/dist/lib/storage/paths.js.map +1 -1
- package/dist/lib/storage/state.d.ts +46 -0
- package/dist/lib/storage/state.d.ts.map +1 -0
- package/dist/lib/storage/state.js +100 -0
- package/dist/lib/storage/state.js.map +1 -0
- package/dist/lib/storage/worktree-lock.d.ts +79 -0
- package/dist/lib/storage/worktree-lock.d.ts.map +1 -0
- package/dist/lib/storage/worktree-lock.js +265 -0
- package/dist/lib/storage/worktree-lock.js.map +1 -0
- package/dist/lib/storage.d.ts +21 -155
- package/dist/lib/storage.d.ts.map +1 -1
- package/dist/lib/storage.js +24 -1167
- package/dist/lib/storage.js.map +1 -1
- package/dist/lib/tools/_shared.d.ts +16 -0
- package/dist/lib/tools/_shared.d.ts.map +1 -0
- package/dist/lib/tools/_shared.js +21 -0
- package/dist/lib/tools/_shared.js.map +1 -0
- package/dist/lib/tools/codex-dashboard.d.ts +8 -0
- package/dist/lib/tools/codex-dashboard.d.ts.map +1 -0
- package/dist/lib/tools/codex-dashboard.js +176 -0
- package/dist/lib/tools/codex-dashboard.js.map +1 -0
- package/dist/lib/tools/codex-diag.d.ts +19 -0
- package/dist/lib/tools/codex-diag.d.ts.map +1 -0
- package/dist/lib/tools/codex-diag.js +139 -0
- package/dist/lib/tools/codex-diag.js.map +1 -0
- package/dist/lib/tools/codex-diff.d.ts +44 -0
- package/dist/lib/tools/codex-diff.d.ts.map +1 -0
- package/dist/lib/tools/codex-diff.js +284 -0
- package/dist/lib/tools/codex-diff.js.map +1 -0
- package/dist/lib/tools/codex-doctor.d.ts +8 -0
- package/dist/lib/tools/codex-doctor.d.ts.map +1 -0
- package/dist/lib/tools/codex-doctor.js +284 -0
- package/dist/lib/tools/codex-doctor.js.map +1 -0
- package/dist/lib/tools/codex-export.d.ts +8 -0
- package/dist/lib/tools/codex-export.d.ts.map +1 -0
- package/dist/lib/tools/codex-export.js +63 -0
- package/dist/lib/tools/codex-export.js.map +1 -0
- package/dist/lib/tools/codex-health.d.ts +8 -0
- package/dist/lib/tools/codex-health.d.ts.map +1 -0
- package/dist/lib/tools/codex-health.js +124 -0
- package/dist/lib/tools/codex-health.js.map +1 -0
- package/dist/lib/tools/codex-help.d.ts +8 -0
- package/dist/lib/tools/codex-help.d.ts.map +1 -0
- package/dist/lib/tools/codex-help.js +127 -0
- package/dist/lib/tools/codex-help.js.map +1 -0
- package/dist/lib/tools/codex-import.d.ts +8 -0
- package/dist/lib/tools/codex-import.d.ts.map +1 -0
- package/dist/lib/tools/codex-import.js +97 -0
- package/dist/lib/tools/codex-import.js.map +1 -0
- package/dist/lib/tools/codex-keychain.d.ts +36 -0
- package/dist/lib/tools/codex-keychain.d.ts.map +1 -0
- package/dist/lib/tools/codex-keychain.js +289 -0
- package/dist/lib/tools/codex-keychain.js.map +1 -0
- package/dist/lib/tools/codex-label.d.ts +8 -0
- package/dist/lib/tools/codex-label.d.ts.map +1 -0
- package/dist/lib/tools/codex-label.js +143 -0
- package/dist/lib/tools/codex-label.js.map +1 -0
- package/dist/lib/tools/codex-limits.d.ts +8 -0
- package/dist/lib/tools/codex-limits.d.ts.map +1 -0
- package/dist/lib/tools/codex-limits.js +497 -0
- package/dist/lib/tools/codex-limits.js.map +1 -0
- package/dist/lib/tools/codex-list.d.ts +8 -0
- package/dist/lib/tools/codex-list.d.ts.map +1 -0
- package/dist/lib/tools/codex-list.js +260 -0
- package/dist/lib/tools/codex-list.js.map +1 -0
- package/dist/lib/tools/codex-metrics.d.ts +8 -0
- package/dist/lib/tools/codex-metrics.d.ts.map +1 -0
- package/dist/lib/tools/codex-metrics.js +180 -0
- package/dist/lib/tools/codex-metrics.js.map +1 -0
- package/dist/lib/tools/codex-next.d.ts +8 -0
- package/dist/lib/tools/codex-next.d.ts.map +1 -0
- package/dist/lib/tools/codex-next.js +54 -0
- package/dist/lib/tools/codex-next.js.map +1 -0
- package/dist/lib/tools/codex-note.d.ts +8 -0
- package/dist/lib/tools/codex-note.d.ts.map +1 -0
- package/dist/lib/tools/codex-note.js +79 -0
- package/dist/lib/tools/codex-note.js.map +1 -0
- package/dist/lib/tools/codex-refresh.d.ts +8 -0
- package/dist/lib/tools/codex-refresh.d.ts.map +1 -0
- package/dist/lib/tools/codex-refresh.js +78 -0
- package/dist/lib/tools/codex-refresh.js.map +1 -0
- package/dist/lib/tools/codex-remove.d.ts +8 -0
- package/dist/lib/tools/codex-remove.d.ts.map +1 -0
- package/dist/lib/tools/codex-remove.js +184 -0
- package/dist/lib/tools/codex-remove.js.map +1 -0
- package/dist/lib/tools/codex-setup.d.ts +8 -0
- package/dist/lib/tools/codex-setup.d.ts.map +1 -0
- package/dist/lib/tools/codex-setup.js +26 -0
- package/dist/lib/tools/codex-setup.js.map +1 -0
- package/dist/lib/tools/codex-status.d.ts +8 -0
- package/dist/lib/tools/codex-status.d.ts.map +1 -0
- package/dist/lib/tools/codex-status.js +263 -0
- package/dist/lib/tools/codex-status.js.map +1 -0
- package/dist/lib/tools/codex-switch.d.ts +8 -0
- package/dist/lib/tools/codex-switch.d.ts.map +1 -0
- package/dist/lib/tools/codex-switch.js +122 -0
- package/dist/lib/tools/codex-switch.js.map +1 -0
- package/dist/lib/tools/codex-tag.d.ts +8 -0
- package/dist/lib/tools/codex-tag.d.ts.map +1 -0
- package/dist/lib/tools/codex-tag.js +101 -0
- package/dist/lib/tools/codex-tag.js.map +1 -0
- package/dist/lib/tools/index.d.ts +143 -0
- package/dist/lib/tools/index.d.ts.map +1 -0
- package/dist/lib/tools/index.js +67 -0
- package/dist/lib/tools/index.js.map +1 -0
- package/dist/lib/ui/format.d.ts.map +1 -1
- package/dist/lib/ui/format.js +5 -0
- package/dist/lib/ui/format.js.map +1 -1
- package/dist/lib/ui/runtime.d.ts +10 -0
- package/dist/lib/ui/runtime.d.ts.map +1 -1
- package/dist/lib/ui/runtime.js +16 -11
- package/dist/lib/ui/runtime.js.map +1 -1
- package/dist/lib/ui/select.d.ts.map +1 -1
- package/dist/lib/ui/select.js +12 -3
- package/dist/lib/ui/select.js.map +1 -1
- package/dist/lib/ui/theme.d.ts +19 -0
- package/dist/lib/ui/theme.d.ts.map +1 -1
- package/dist/lib/ui/theme.js +27 -0
- package/dist/lib/ui/theme.js.map +1 -1
- package/package.json +24 -16
- package/scripts/copy-oauth-success.js +16 -6
- package/scripts/install-oc-codex-multi-auth-core.js +462 -381
- package/scripts/install-oc-codex-multi-auth.js +15 -15
- package/scripts/test-all-models.sh +1 -1
- package/dist/lib/audit.d.ts +0 -45
- package/dist/lib/audit.d.ts.map +0 -1
- package/dist/lib/audit.js +0 -154
- package/dist/lib/audit.js.map +0 -1
- package/dist/lib/auth-rate-limit.d.ts +0 -20
- package/dist/lib/auth-rate-limit.d.ts.map +0 -1
- package/dist/lib/auth-rate-limit.js +0 -91
- package/dist/lib/auth-rate-limit.js.map +0 -1
- package/dist/lib/runtime-contracts.d.ts +0 -16
- package/dist/lib/runtime-contracts.d.ts.map +0 -1
- package/dist/lib/runtime-contracts.js +0 -24
- package/dist/lib/runtime-contracts.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"identity.d.ts","sourceRoot":"","sources":["../../../lib/storage/identity.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAEzD,MAAM,MAAM,WAAW,GAAG;IACxB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,iBAAiB,CAAC,iBAAiB,CAAC,CAAC;IACvD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAKF,wBAAgB,uBAAuB,CAAC,OAAO,EAAE;IAC/C,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;CACtB,GAAG,MAAM,CAaT;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAEzE;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAGhE;AAED,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,IAAI,CAAC,iBAAiB,EAAE,gBAAgB,GAAG,WAAW,GAAG,cAAc,CAAC,GAChF,MAAM,EAAE,CAkBV;AAED,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,IAAI,CAAC,iBAAiB,EAAE,gBAAgB,GAAG,WAAW,GAAG,cAAc,CAAC,GAChF,MAAM,GAAG,SAAS,CAEpB;AAED,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,EAAE,CASpF;AAED,wBAAgB,8BAA8B,CAC5C,QAAQ,EAAE,IAAI,CAAC,iBAAiB,EAAE,gBAAgB,GAAG,WAAW,GAAG,cAAc,CAAC,EAAE,EACpF,YAAY,EAAE,MAAM,EAAE,GACrB,MAAM,CASR;AAgFD;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,SAAS;IAAE,cAAc,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,EACtJ,QAAQ,EAAE,CAAC,EAAE,GACZ,CAAC,EAAE,CAEL;AAED;;;;;;GAMG;AACH,wBAAgB,0BAA0B,CAAC,CAAC,SAAS;IAAE,cAAc,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,EACvJ,QAAQ,EAAE,CAAC,EAAE,GACZ,CAAC,EAAE,CAiEL;AAED;;;;;GAKG;AACH,wBAAgB,6BAA6B,CAAC,CAAC,SAAS,WAAW,GAAG;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,EAAE,QAAQ,EAAE,CAAC,EAAE,GAAG,CAAC,EAAE,CAE5G"}
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Account identity + dedup helpers.
|
|
3
|
+
*
|
|
4
|
+
* Split out of `lib/storage.ts` in RC-2. This module is pure and synchronous:
|
|
5
|
+
* it owns the identity-key hierarchy (`organizationId` -> `accountId` ->
|
|
6
|
+
* `refreshToken`) and the dedup + merge rules used by `normalizeAccountStorage`
|
|
7
|
+
* and the import transaction.
|
|
8
|
+
*
|
|
9
|
+
* The two public exports that were already re-exported from `lib/storage.ts`
|
|
10
|
+
* (`deduplicateAccounts`, `deduplicateAccountsByEmail`) keep their original
|
|
11
|
+
* shape so external callers can import them via either the barrel or this
|
|
12
|
+
* submodule.
|
|
13
|
+
*/
|
|
14
|
+
const normalizeWorkspaceIdentityPart = (value) => typeof value === "string" && value.trim().length > 0 ? value.trim() : undefined;
|
|
15
|
+
export function getWorkspaceIdentityKey(account) {
|
|
16
|
+
const organizationId = normalizeWorkspaceIdentityPart(account.organizationId);
|
|
17
|
+
const accountId = normalizeWorkspaceIdentityPart(account.accountId);
|
|
18
|
+
const refreshToken = normalizeWorkspaceIdentityPart(account.refreshToken) ?? "";
|
|
19
|
+
if (organizationId) {
|
|
20
|
+
return accountId
|
|
21
|
+
? `organizationId:${organizationId}|accountId:${accountId}`
|
|
22
|
+
: `organizationId:${organizationId}`;
|
|
23
|
+
}
|
|
24
|
+
if (accountId) {
|
|
25
|
+
return `accountId:${accountId}`;
|
|
26
|
+
}
|
|
27
|
+
return `refreshToken:${refreshToken}`;
|
|
28
|
+
}
|
|
29
|
+
export function isRecord(value) {
|
|
30
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
31
|
+
}
|
|
32
|
+
export function clampIndex(index, length) {
|
|
33
|
+
if (length <= 0)
|
|
34
|
+
return 0;
|
|
35
|
+
return Math.max(0, Math.min(index, length - 1));
|
|
36
|
+
}
|
|
37
|
+
export function toAccountIdentityKeys(account) {
|
|
38
|
+
const keys = [];
|
|
39
|
+
const organizationId = typeof account.organizationId === "string" ? account.organizationId.trim() : "";
|
|
40
|
+
if (organizationId) {
|
|
41
|
+
keys.push(`organizationId:${organizationId}`);
|
|
42
|
+
}
|
|
43
|
+
const accountId = typeof account.accountId === "string" ? account.accountId.trim() : "";
|
|
44
|
+
if (accountId) {
|
|
45
|
+
keys.push(`accountId:${accountId}`);
|
|
46
|
+
}
|
|
47
|
+
const refreshToken = typeof account.refreshToken === "string" ? account.refreshToken.trim() : "";
|
|
48
|
+
if (refreshToken) {
|
|
49
|
+
keys.push(`refreshToken:${refreshToken}`);
|
|
50
|
+
}
|
|
51
|
+
return keys;
|
|
52
|
+
}
|
|
53
|
+
export function toAccountIdentityKey(account) {
|
|
54
|
+
return toAccountIdentityKeys(account)[0];
|
|
55
|
+
}
|
|
56
|
+
export function extractActiveKeys(accounts, activeIndex) {
|
|
57
|
+
const candidate = accounts[activeIndex];
|
|
58
|
+
if (!isRecord(candidate))
|
|
59
|
+
return [];
|
|
60
|
+
return toAccountIdentityKeys({
|
|
61
|
+
organizationId: typeof candidate.organizationId === "string" ? candidate.organizationId : undefined,
|
|
62
|
+
accountId: typeof candidate.accountId === "string" ? candidate.accountId : undefined,
|
|
63
|
+
refreshToken: typeof candidate.refreshToken === "string" ? candidate.refreshToken : "",
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
export function findAccountIndexByIdentityKeys(accounts, identityKeys) {
|
|
67
|
+
if (identityKeys.length === 0)
|
|
68
|
+
return -1;
|
|
69
|
+
for (const identityKey of identityKeys) {
|
|
70
|
+
const idx = accounts.findIndex((account) => toAccountIdentityKeys(account).includes(identityKey));
|
|
71
|
+
if (idx >= 0) {
|
|
72
|
+
return idx;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return -1;
|
|
76
|
+
}
|
|
77
|
+
function selectNewestAccount(current, candidate) {
|
|
78
|
+
if (!current)
|
|
79
|
+
return candidate;
|
|
80
|
+
const currentLastUsed = current.lastUsed || 0;
|
|
81
|
+
const candidateLastUsed = candidate.lastUsed || 0;
|
|
82
|
+
if (candidateLastUsed > currentLastUsed)
|
|
83
|
+
return candidate;
|
|
84
|
+
if (candidateLastUsed < currentLastUsed)
|
|
85
|
+
return current;
|
|
86
|
+
const currentAddedAt = current.addedAt || 0;
|
|
87
|
+
const candidateAddedAt = candidate.addedAt || 0;
|
|
88
|
+
return candidateAddedAt >= currentAddedAt ? candidate : current;
|
|
89
|
+
}
|
|
90
|
+
function pickNewestAccountIndex(accounts, existingIndex, candidateIndex) {
|
|
91
|
+
const existing = accounts[existingIndex];
|
|
92
|
+
const candidate = accounts[candidateIndex];
|
|
93
|
+
if (!existing)
|
|
94
|
+
return candidateIndex;
|
|
95
|
+
if (!candidate)
|
|
96
|
+
return existingIndex;
|
|
97
|
+
const newest = selectNewestAccount(existing, candidate);
|
|
98
|
+
return newest === candidate ? candidateIndex : existingIndex;
|
|
99
|
+
}
|
|
100
|
+
function mergeAccountRecords(target, source) {
|
|
101
|
+
const newest = selectNewestAccount(target, source);
|
|
102
|
+
const older = newest === target ? source : target;
|
|
103
|
+
return {
|
|
104
|
+
...older,
|
|
105
|
+
...newest,
|
|
106
|
+
organizationId: target.organizationId ?? source.organizationId,
|
|
107
|
+
accountId: target.accountId ?? source.accountId,
|
|
108
|
+
accountIdSource: target.accountIdSource ?? source.accountIdSource,
|
|
109
|
+
accountLabel: target.accountLabel ?? source.accountLabel,
|
|
110
|
+
email: target.email ?? source.email,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
function deduplicateAccountsByKey(accounts) {
|
|
114
|
+
const working = [...accounts];
|
|
115
|
+
const keyToIndex = new Map();
|
|
116
|
+
const indicesToRemove = new Set();
|
|
117
|
+
for (let i = 0; i < working.length; i += 1) {
|
|
118
|
+
const account = working[i];
|
|
119
|
+
if (!account)
|
|
120
|
+
continue;
|
|
121
|
+
const key = toAccountIdentityKey(account);
|
|
122
|
+
if (!key)
|
|
123
|
+
continue;
|
|
124
|
+
const existingIndex = keyToIndex.get(key);
|
|
125
|
+
if (existingIndex === undefined) {
|
|
126
|
+
keyToIndex.set(key, i);
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
const newestIndex = pickNewestAccountIndex(working, existingIndex, i);
|
|
130
|
+
const obsoleteIndex = newestIndex === existingIndex ? i : existingIndex;
|
|
131
|
+
const target = working[newestIndex];
|
|
132
|
+
const source = working[obsoleteIndex];
|
|
133
|
+
if (target && source) {
|
|
134
|
+
working[newestIndex] = mergeAccountRecords(target, source);
|
|
135
|
+
}
|
|
136
|
+
indicesToRemove.add(obsoleteIndex);
|
|
137
|
+
keyToIndex.set(key, newestIndex);
|
|
138
|
+
}
|
|
139
|
+
const result = [];
|
|
140
|
+
for (let i = 0; i < working.length; i += 1) {
|
|
141
|
+
if (indicesToRemove.has(i))
|
|
142
|
+
continue;
|
|
143
|
+
const account = working[i];
|
|
144
|
+
if (account)
|
|
145
|
+
result.push(account);
|
|
146
|
+
}
|
|
147
|
+
return result;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Removes duplicate accounts, keeping the most recently used entry for each unique key.
|
|
151
|
+
* Deduplication identity hierarchy: organizationId -> accountId -> refreshToken.
|
|
152
|
+
* @param accounts - Array of accounts to deduplicate
|
|
153
|
+
* @returns New array with duplicates removed
|
|
154
|
+
*/
|
|
155
|
+
export function deduplicateAccounts(accounts) {
|
|
156
|
+
return deduplicateAccountsByKey(accounts);
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Removes duplicate legacy accounts by email, keeping the most recently used entry.
|
|
160
|
+
* Accounts with organizationId/accountId are never merged by email to avoid collapsing workspace variants.
|
|
161
|
+
* Accounts without email are always preserved.
|
|
162
|
+
* @param accounts - Array of accounts to deduplicate
|
|
163
|
+
* @returns New array with email duplicates removed
|
|
164
|
+
*/
|
|
165
|
+
export function deduplicateAccountsByEmail(accounts) {
|
|
166
|
+
const emailToNewestIndex = new Map();
|
|
167
|
+
const indicesToKeep = new Set();
|
|
168
|
+
for (let i = 0; i < accounts.length; i += 1) {
|
|
169
|
+
const account = accounts[i];
|
|
170
|
+
if (!account)
|
|
171
|
+
continue;
|
|
172
|
+
const organizationId = account.organizationId?.trim();
|
|
173
|
+
if (organizationId) {
|
|
174
|
+
indicesToKeep.add(i);
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
const accountId = account.accountId?.trim();
|
|
178
|
+
if (accountId) {
|
|
179
|
+
indicesToKeep.add(i);
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
const email = account.email?.trim();
|
|
183
|
+
if (!email) {
|
|
184
|
+
indicesToKeep.add(i);
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
187
|
+
const existingIndex = emailToNewestIndex.get(email);
|
|
188
|
+
if (existingIndex === undefined) {
|
|
189
|
+
emailToNewestIndex.set(email, i);
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
const existing = accounts[existingIndex];
|
|
193
|
+
// istanbul ignore next -- defensive code: existingIndex always refers to valid account
|
|
194
|
+
if (!existing) {
|
|
195
|
+
emailToNewestIndex.set(email, i);
|
|
196
|
+
continue;
|
|
197
|
+
}
|
|
198
|
+
const existingLastUsed = existing.lastUsed || 0;
|
|
199
|
+
const candidateLastUsed = account.lastUsed || 0;
|
|
200
|
+
const existingAddedAt = existing.addedAt || 0;
|
|
201
|
+
const candidateAddedAt = account.addedAt || 0;
|
|
202
|
+
const isNewer = candidateLastUsed > existingLastUsed ||
|
|
203
|
+
(candidateLastUsed === existingLastUsed && candidateAddedAt > existingAddedAt);
|
|
204
|
+
if (isNewer) {
|
|
205
|
+
emailToNewestIndex.set(email, i);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
for (const idx of emailToNewestIndex.values()) {
|
|
209
|
+
indicesToKeep.add(idx);
|
|
210
|
+
}
|
|
211
|
+
const result = [];
|
|
212
|
+
for (let i = 0; i < accounts.length; i += 1) {
|
|
213
|
+
if (indicesToKeep.has(i)) {
|
|
214
|
+
const account = accounts[i];
|
|
215
|
+
if (account)
|
|
216
|
+
result.push(account);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
return result;
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Applies storage deduplication semantics used by normalize/import paths.
|
|
223
|
+
* 1) Dedupe only exact identity duplicates (organizationId -> accountId -> refreshToken),
|
|
224
|
+
* preserving distinct workspace variants that share a refresh token.
|
|
225
|
+
* 2) Then apply legacy email dedupe only for entries that still do not have organizationId/accountId.
|
|
226
|
+
*/
|
|
227
|
+
export function deduplicateAccountsForStorage(accounts) {
|
|
228
|
+
return deduplicateAccountsByEmail(deduplicateAccountsByKey(accounts));
|
|
229
|
+
}
|
|
230
|
+
//# sourceMappingURL=identity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"identity.js","sourceRoot":"","sources":["../../../lib/storage/identity.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Opt-in OS-keychain credential backend.
|
|
3
|
+
*
|
|
4
|
+
* Phase 4 F1. Addresses the audit finding (docs/audits/09-security-trust.md)
|
|
5
|
+
* that per-project accounts are stored as plaintext V3 JSON on disk. This
|
|
6
|
+
* module introduces an alternate storage backend that persists the SAME
|
|
7
|
+
* V3 JSON blob as a secret value in the OS keychain:
|
|
8
|
+
*
|
|
9
|
+
* - macOS: Keychain
|
|
10
|
+
* - Windows: Credential Manager
|
|
11
|
+
* - Linux: Secret Service / libsecret
|
|
12
|
+
*
|
|
13
|
+
* **Default behavior is unchanged.** The backend only activates when
|
|
14
|
+
* `CODEX_KEYCHAIN=1` is set in the environment. Any other value (unset,
|
|
15
|
+
* `"0"`, `"false"`, `""`, `"yes"`, ...) leaves the existing JSON path in
|
|
16
|
+
* full control. This is deliberate: credential storage is the highest-trust
|
|
17
|
+
* surface in the plugin and every new failure mode is a login regression.
|
|
18
|
+
*
|
|
19
|
+
* Data model:
|
|
20
|
+
* - Service name: `oc-codex-multi-auth` (fixed)
|
|
21
|
+
* - Account key: `accounts:<project-storage-key>` where the project key
|
|
22
|
+
* is the `projectName-sha256hash12` string already produced by
|
|
23
|
+
* `lib/storage/paths.ts#getProjectStorageKey`. For the global file we
|
|
24
|
+
* use the literal key `accounts:global`.
|
|
25
|
+
* - Secret payload: the exact JSON string that would otherwise be written
|
|
26
|
+
* to disk (UTF-8, pretty-printed to match the file contents; the OS
|
|
27
|
+
* keychain APIs used here store arbitrary UTF-8 so no base64 encoding
|
|
28
|
+
* is required — documented here for reviewers).
|
|
29
|
+
*
|
|
30
|
+
* Fallback contract:
|
|
31
|
+
* - Every entry point returns a soft failure (`null` on read,
|
|
32
|
+
* `{ok: false}` on write) instead of throwing when the native module
|
|
33
|
+
* cannot load or a keychain call fails. Callers MUST fall back to the
|
|
34
|
+
* JSON backend on failure; they must never silently lose credentials.
|
|
35
|
+
* - All errors are logged with `log.warn` / `log.error`. Secret values
|
|
36
|
+
* never appear in log text — only the service name, account key, and
|
|
37
|
+
* the native error message.
|
|
38
|
+
*
|
|
39
|
+
* Testing:
|
|
40
|
+
* - The native module is loaded lazily so unit tests can substitute
|
|
41
|
+
* `_setBackendForTests` to avoid hitting the real OS keychain.
|
|
42
|
+
* Integration tests that DO want to hit the real keychain can set
|
|
43
|
+
* `CODEX_KEYCHAIN=1` and let `loadBackend` resolve the module.
|
|
44
|
+
*/
|
|
45
|
+
/**
|
|
46
|
+
* The keychain service identifier this plugin owns. Chosen to match the npm
|
|
47
|
+
* package name so a human inspecting Keychain Access / Credential Manager
|
|
48
|
+
* can easily identify which program owns a stored credential.
|
|
49
|
+
*/
|
|
50
|
+
export declare const KEYCHAIN_SERVICE_NAME = "oc-codex-multi-auth";
|
|
51
|
+
/**
|
|
52
|
+
* Account key used when the plugin is operating without a per-project
|
|
53
|
+
* storage path (i.e. the global accounts file). Kept distinct from any
|
|
54
|
+
* per-project key so the two storage scopes never collide in the OS
|
|
55
|
+
* keychain's (service, account) index.
|
|
56
|
+
*/
|
|
57
|
+
export declare const GLOBAL_KEYCHAIN_ACCOUNT_KEY = "accounts:global";
|
|
58
|
+
/**
|
|
59
|
+
* Reserved account key used exclusively by the availability probe. Kept
|
|
60
|
+
* double-underscored and service-suffixed so a future refactor that drops
|
|
61
|
+
* the `accounts:` prefix on real entries (or adds a different prefix) still
|
|
62
|
+
* cannot collide with this probe (F1 post-merge LOW finding).
|
|
63
|
+
*/
|
|
64
|
+
export declare const KEYCHAIN_PROBE_ACCOUNT_KEY = "__probe__@oc-codex-multi-auth";
|
|
65
|
+
/**
|
|
66
|
+
* Minimal abstraction over the native `@napi-rs/keyring` `Entry` API.
|
|
67
|
+
* Declared as an interface so tests can inject a deterministic in-memory
|
|
68
|
+
* backend without touching the real OS keychain.
|
|
69
|
+
*/
|
|
70
|
+
export interface KeychainBackend {
|
|
71
|
+
get(service: string, account: string): Promise<string | null>;
|
|
72
|
+
set(service: string, account: string, secret: string): Promise<void>;
|
|
73
|
+
delete(service: string, account: string): Promise<boolean>;
|
|
74
|
+
isAvailable(): Promise<boolean>;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Build the keychain account key for a given project storage key. When the
|
|
78
|
+
* plugin is running against the global accounts file, pass `null` to use
|
|
79
|
+
* the reserved global account key.
|
|
80
|
+
*/
|
|
81
|
+
export declare function buildKeychainAccountKey(projectStorageKey: string | null): string;
|
|
82
|
+
/**
|
|
83
|
+
* Reads the V3 JSON blob from the OS keychain for the given project storage
|
|
84
|
+
* key. Returns `null` when:
|
|
85
|
+
* - the native module is unavailable (not installed, missing prebuilt)
|
|
86
|
+
* - no entry exists yet
|
|
87
|
+
* - any keychain error occurs
|
|
88
|
+
*
|
|
89
|
+
* The caller MUST treat `null` as "fall back to JSON". Callers must never
|
|
90
|
+
* interpret `null` as "no credentials" unconditionally — it may mean
|
|
91
|
+
* "keychain locked / permission denied" and the JSON file still holds the
|
|
92
|
+
* authoritative copy.
|
|
93
|
+
*/
|
|
94
|
+
export declare function readFromKeychain(projectStorageKey: string | null): Promise<string | null>;
|
|
95
|
+
export interface KeychainWriteResult {
|
|
96
|
+
ok: boolean;
|
|
97
|
+
/** Populated on failure. Never contains secret material. */
|
|
98
|
+
error?: string;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Persist the V3 JSON blob to the OS keychain. Returns `{ok:false}` on
|
|
102
|
+
* failure so callers can fall back to JSON. Never throws.
|
|
103
|
+
*/
|
|
104
|
+
export declare function writeToKeychain(projectStorageKey: string | null, jsonBlob: string): Promise<KeychainWriteResult>;
|
|
105
|
+
/**
|
|
106
|
+
* Remove the plugin's keychain entry for the given project key. Returns
|
|
107
|
+
* true when the entry existed and was deleted, false otherwise (including
|
|
108
|
+
* "not present"). Never throws.
|
|
109
|
+
*/
|
|
110
|
+
export declare function deleteFromKeychain(projectStorageKey: string | null): Promise<boolean>;
|
|
111
|
+
/**
|
|
112
|
+
* Probe the backend end-to-end (write + read + delete a throwaway entry)
|
|
113
|
+
* to confirm the OS keychain is reachable and unlocked. Used by the
|
|
114
|
+
* `codex-keychain status` tool to give the operator a clear yes/no signal
|
|
115
|
+
* instead of waiting until the next real save.
|
|
116
|
+
*
|
|
117
|
+
* Gated on the opt-in flag (F1 post-merge LOW finding): with
|
|
118
|
+
* `CODEX_KEYCHAIN` unset the probe is a no-op that returns `false` without
|
|
119
|
+
* touching the OS keychain. This preserves the "unset -> zero keychain code
|
|
120
|
+
* path" invariant the feature advertises and avoids the first-run macOS
|
|
121
|
+
* "allow/always allow" prompt that `entry.setPassword` can otherwise
|
|
122
|
+
* trigger for users who run `codex-keychain status` without opting in.
|
|
123
|
+
*
|
|
124
|
+
* Pass `env` to override the opt-in lookup in tests.
|
|
125
|
+
*/
|
|
126
|
+
export declare function keychainIsAvailable(env?: NodeJS.ProcessEnv): Promise<boolean>;
|
|
127
|
+
/**
|
|
128
|
+
* Parse the `CODEX_KEYCHAIN` environment variable. Only the literal string
|
|
129
|
+
* `"1"` enables the opt-in. Anything else (unset, `"0"`, `"false"`, `""`,
|
|
130
|
+
* `"yes"`, ...) leaves the JSON backend in full control. This mirrors
|
|
131
|
+
* `EnvBooleanSchema`'s contract and is documented in README.md.
|
|
132
|
+
*/
|
|
133
|
+
export declare function isKeychainOptInEnabled(env?: NodeJS.ProcessEnv): boolean;
|
|
134
|
+
/**
|
|
135
|
+
* Test-only hook: inject a deterministic backend (typically an in-memory
|
|
136
|
+
* Map-backed stub) and mark the cache as resolved so `getBackend` returns
|
|
137
|
+
* the injected backend without attempting to load the native module.
|
|
138
|
+
*
|
|
139
|
+
* Passing `null` clears the cache so a subsequent call re-runs the lazy
|
|
140
|
+
* loader — useful when a test needs to verify the unavailable-backend
|
|
141
|
+
* branch.
|
|
142
|
+
*/
|
|
143
|
+
export declare function _setBackendForTests(backend: KeychainBackend | null): void;
|
|
144
|
+
/** Test-only reset to the initial "not yet loaded" state. */
|
|
145
|
+
export declare function _resetBackendForTests(): void;
|
|
146
|
+
//# sourceMappingURL=keychain.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keychain.d.ts","sourceRoot":"","sources":["../../../lib/storage/keychain.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AAMH;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,wBAAwB,CAAC;AAE3D;;;;;GAKG;AACH,eAAO,MAAM,2BAA2B,oBAAoB,CAAC;AAE7D;;;;;GAKG;AACH,eAAO,MAAM,0BAA0B,kCAAuC,CAAC;AAE/E;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC/B,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC9D,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACrE,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC3D,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;CAChC;AAiHD;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,iBAAiB,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAGhF;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,gBAAgB,CACrC,iBAAiB,EAAE,MAAM,GAAG,IAAI,GAC9B,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAKxB;AAED,MAAM,WAAW,mBAAmB;IACnC,EAAE,EAAE,OAAO,CAAC;IACZ,4DAA4D;IAC5D,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;GAGG;AACH,wBAAsB,eAAe,CACpC,iBAAiB,EAAE,MAAM,GAAG,IAAI,EAChC,QAAQ,EAAE,MAAM,GACd,OAAO,CAAC,mBAAmB,CAAC,CAkB9B;AAED;;;;GAIG;AACH,wBAAsB,kBAAkB,CACvC,iBAAiB,EAAE,MAAM,GAAG,IAAI,GAC9B,OAAO,CAAC,OAAO,CAAC,CAKlB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,mBAAmB,CACxC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAClC,OAAO,CAAC,OAAO,CAAC,CAKlB;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,OAAO,CAEpF;AAED;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,eAAe,GAAG,IAAI,GAAG,IAAI,CAGzE;AAED,6DAA6D;AAC7D,wBAAgB,qBAAqB,IAAI,IAAI,CAG5C"}
|