oc-codex-multi-auth 6.21.0 → 6.23.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 +168 -10
- package/config/README.md +53 -33
- package/config/opencode-legacy.json +129 -279
- package/config/opencode-modern.json +43 -132
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +293 -57
- package/dist/index.js.map +1 -1
- package/dist/lib/account-display.d.ts +46 -0
- package/dist/lib/account-display.d.ts.map +1 -1
- package/dist/lib/account-display.js +260 -0
- package/dist/lib/account-display.js.map +1 -1
- package/dist/lib/accounts/warm-request.d.ts.map +1 -1
- package/dist/lib/accounts/warm-request.js +9 -11
- package/dist/lib/accounts/warm-request.js.map +1 -1
- package/dist/lib/accounts.d.ts +4 -0
- package/dist/lib/accounts.d.ts.map +1 -1
- package/dist/lib/accounts.js +20 -15
- package/dist/lib/accounts.js.map +1 -1
- package/dist/lib/auth/login-runner.d.ts.map +1 -1
- package/dist/lib/auth/login-runner.js +79 -2
- package/dist/lib/auth/login-runner.js.map +1 -1
- package/dist/lib/auto-update-checker.d.ts +17 -1
- package/dist/lib/auto-update-checker.d.ts.map +1 -1
- package/dist/lib/auto-update-checker.js +49 -3
- package/dist/lib/auto-update-checker.js.map +1 -1
- package/dist/lib/cli.d.ts +1 -0
- package/dist/lib/cli.d.ts.map +1 -1
- package/dist/lib/cli.js +9 -3
- package/dist/lib/cli.js.map +1 -1
- package/dist/lib/codex-usage.d.ts +4 -3
- package/dist/lib/codex-usage.d.ts.map +1 -1
- package/dist/lib/codex-usage.js +13 -11
- package/dist/lib/codex-usage.js.map +1 -1
- package/dist/lib/config.d.ts +75 -0
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +152 -1
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/constants.d.ts +13 -0
- package/dist/lib/constants.d.ts.map +1 -1
- package/dist/lib/constants.js +13 -0
- package/dist/lib/constants.js.map +1 -1
- package/dist/lib/plan-allotment.d.ts +72 -0
- package/dist/lib/plan-allotment.d.ts.map +1 -0
- package/dist/lib/plan-allotment.js +142 -0
- package/dist/lib/plan-allotment.js.map +1 -0
- package/dist/lib/plugin-origin.d.ts +77 -0
- package/dist/lib/plugin-origin.d.ts.map +1 -0
- package/dist/lib/plugin-origin.js +301 -0
- package/dist/lib/plugin-origin.js.map +1 -0
- package/dist/lib/prompts/codex.d.ts +12 -2
- package/dist/lib/prompts/codex.d.ts.map +1 -1
- package/dist/lib/prompts/codex.js +47 -16
- package/dist/lib/prompts/codex.js.map +1 -1
- package/dist/lib/quota-display.d.ts +40 -0
- package/dist/lib/quota-display.d.ts.map +1 -0
- package/dist/lib/quota-display.js +45 -0
- package/dist/lib/quota-display.js.map +1 -0
- package/dist/lib/quota-notifications.d.ts +2 -1
- package/dist/lib/quota-notifications.d.ts.map +1 -1
- package/dist/lib/quota-notifications.js +34 -14
- package/dist/lib/quota-notifications.js.map +1 -1
- package/dist/lib/quota-overview.d.ts +253 -0
- package/dist/lib/quota-overview.d.ts.map +1 -0
- package/dist/lib/quota-overview.js +707 -0
- package/dist/lib/quota-overview.js.map +1 -0
- package/dist/lib/request/fetch-helpers.d.ts.map +1 -1
- package/dist/lib/request/fetch-helpers.js +84 -50
- package/dist/lib/request/fetch-helpers.js.map +1 -1
- package/dist/lib/request/helpers/model-map.d.ts +10 -0
- package/dist/lib/request/helpers/model-map.d.ts.map +1 -1
- package/dist/lib/request/helpers/model-map.js +39 -11
- package/dist/lib/request/helpers/model-map.js.map +1 -1
- package/dist/lib/request/helpers/responses-lite.d.ts.map +1 -1
- package/dist/lib/request/helpers/responses-lite.js +4 -1
- package/dist/lib/request/helpers/responses-lite.js.map +1 -1
- package/dist/lib/request/request-transformer.d.ts.map +1 -1
- package/dist/lib/request/request-transformer.js +37 -15
- package/dist/lib/request/request-transformer.js.map +1 -1
- package/dist/lib/request/retry-budget.d.ts +26 -0
- package/dist/lib/request/retry-budget.d.ts.map +1 -1
- package/dist/lib/request/retry-budget.js +43 -0
- package/dist/lib/request/retry-budget.js.map +1 -1
- package/dist/lib/schemas.d.ts +73 -0
- package/dist/lib/schemas.d.ts.map +1 -1
- package/dist/lib/schemas.js +53 -0
- package/dist/lib/schemas.js.map +1 -1
- package/dist/lib/storage/backup.d.ts +33 -0
- package/dist/lib/storage/backup.d.ts.map +1 -1
- package/dist/lib/storage/backup.js +43 -7
- package/dist/lib/storage/backup.js.map +1 -1
- package/dist/lib/storage/credential-snapshots.d.ts +78 -0
- package/dist/lib/storage/credential-snapshots.d.ts.map +1 -0
- package/dist/lib/storage/credential-snapshots.js +302 -0
- package/dist/lib/storage/credential-snapshots.js.map +1 -0
- package/dist/lib/storage/export-import.d.ts.map +1 -1
- package/dist/lib/storage/export-import.js +7 -0
- package/dist/lib/storage/export-import.js.map +1 -1
- package/dist/lib/storage/flagged.d.ts.map +1 -1
- package/dist/lib/storage/flagged.js +23 -1
- package/dist/lib/storage/flagged.js.map +1 -1
- package/dist/lib/storage/load-save.d.ts +4 -0
- package/dist/lib/storage/load-save.d.ts.map +1 -1
- package/dist/lib/storage/load-save.js +50 -0
- package/dist/lib/storage/load-save.js.map +1 -1
- package/dist/lib/storage/paths.d.ts +1 -0
- package/dist/lib/storage/paths.d.ts.map +1 -1
- package/dist/lib/storage/paths.js +1 -1
- package/dist/lib/storage/paths.js.map +1 -1
- package/dist/lib/storage/test-home-guard.d.ts +35 -0
- package/dist/lib/storage/test-home-guard.d.ts.map +1 -0
- package/dist/lib/storage/test-home-guard.js +59 -0
- package/dist/lib/storage/test-home-guard.js.map +1 -0
- package/dist/lib/tools/codex-dashboard.d.ts.map +1 -1
- package/dist/lib/tools/codex-dashboard.js +3 -2
- package/dist/lib/tools/codex-dashboard.js.map +1 -1
- package/dist/lib/tools/codex-doctor.d.ts.map +1 -1
- package/dist/lib/tools/codex-doctor.js +16 -0
- package/dist/lib/tools/codex-doctor.js.map +1 -1
- package/dist/lib/tools/codex-health.d.ts.map +1 -1
- package/dist/lib/tools/codex-health.js +10 -2
- package/dist/lib/tools/codex-health.js.map +1 -1
- package/dist/lib/tools/codex-label.d.ts.map +1 -1
- package/dist/lib/tools/codex-label.js +1 -0
- package/dist/lib/tools/codex-label.js.map +1 -1
- package/dist/lib/tools/codex-limits.d.ts.map +1 -1
- package/dist/lib/tools/codex-limits.js +13 -9
- package/dist/lib/tools/codex-limits.js.map +1 -1
- package/dist/lib/tools/codex-list.d.ts.map +1 -1
- package/dist/lib/tools/codex-list.js +28 -3
- package/dist/lib/tools/codex-list.js.map +1 -1
- package/dist/lib/tools/codex-note.d.ts.map +1 -1
- package/dist/lib/tools/codex-note.js +4 -1
- package/dist/lib/tools/codex-note.js.map +1 -1
- package/dist/lib/tools/codex-pool.d.ts.map +1 -1
- package/dist/lib/tools/codex-pool.js +6 -2
- package/dist/lib/tools/codex-pool.js.map +1 -1
- package/dist/lib/tools/codex-refresh.d.ts.map +1 -1
- package/dist/lib/tools/codex-refresh.js +4 -1
- package/dist/lib/tools/codex-refresh.js.map +1 -1
- package/dist/lib/tools/codex-remove.d.ts.map +1 -1
- package/dist/lib/tools/codex-remove.js +1 -0
- package/dist/lib/tools/codex-remove.js.map +1 -1
- package/dist/lib/tools/codex-reset.d.ts.map +1 -1
- package/dist/lib/tools/codex-reset.js +13 -7
- package/dist/lib/tools/codex-reset.js.map +1 -1
- package/dist/lib/tools/codex-status.d.ts.map +1 -1
- package/dist/lib/tools/codex-status.js +25 -2
- package/dist/lib/tools/codex-status.js.map +1 -1
- package/dist/lib/tools/codex-switch.d.ts.map +1 -1
- package/dist/lib/tools/codex-switch.js +1 -0
- package/dist/lib/tools/codex-switch.js.map +1 -1
- package/dist/lib/tools/codex-tag.d.ts.map +1 -1
- package/dist/lib/tools/codex-tag.js +4 -1
- package/dist/lib/tools/codex-tag.js.map +1 -1
- package/dist/lib/tools/codex-warm.d.ts.map +1 -1
- package/dist/lib/tools/codex-warm.js +1 -0
- package/dist/lib/tools/codex-warm.js.map +1 -1
- package/dist/lib/tools/index.d.ts +9 -0
- package/dist/lib/tools/index.d.ts.map +1 -1
- package/dist/lib/tools/index.js.map +1 -1
- package/dist/lib/tui-quota-cache.d.ts +35 -0
- package/dist/lib/tui-quota-cache.d.ts.map +1 -1
- package/dist/lib/tui-quota-cache.js +76 -11
- package/dist/lib/tui-quota-cache.js.map +1 -1
- package/dist/lib/tui-quota-overview.d.ts +55 -0
- package/dist/lib/tui-quota-overview.d.ts.map +1 -0
- package/dist/lib/tui-quota-overview.js +209 -0
- package/dist/lib/tui-quota-overview.js.map +1 -0
- package/dist/lib/tui-status.d.ts +55 -0
- package/dist/lib/tui-status.d.ts.map +1 -1
- package/dist/lib/tui-status.js +155 -14
- package/dist/lib/tui-status.js.map +1 -1
- package/dist/lib/ui/auth-menu.d.ts +2 -0
- package/dist/lib/ui/auth-menu.d.ts.map +1 -1
- package/dist/lib/ui/auth-menu.js +10 -6
- package/dist/lib/ui/auth-menu.js.map +1 -1
- package/dist/tui.d.ts +55 -0
- package/dist/tui.d.ts.map +1 -1
- package/dist/tui.js +511 -29
- package/dist/tui.js.map +1 -1
- package/package.json +1 -1
- package/scripts/install-oc-codex-multi-auth-core.js +583 -66
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../lib/constants.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,kFAAkF;AAClF,eAAO,MAAM,YAAY,wBAAwB,CAAC;AAElD,qFAAqF;AACrF,eAAO,MAAM,mBAAmB,0BAA0B,CAAC;AAE3D,uDAAuD;AACvD,eAAO,MAAM,WAAW,wBAAe,CAAC;AAExC,4DAA4D;AAC5D,eAAO,MAAM,kBAAkB,sCAAsC,CAAC;AACtE,eAAO,MAAM,yBAAyB,+BAA+B,CAAC;AACtE,eAAO,MAAM,0BAA0B,8CAA8C,CAAC;AACtF,eAAO,MAAM,iCAAiC,uCAAuC,CAAC;AACtF,eAAO,MAAM,iCAAiC,uCAAuC,CAAC;AAEtF,uCAAuC;AACvC,eAAO,MAAM,cAAc,oCAAoC,CAAC;AAEhE,gEAAgE;AAChE,eAAO,MAAM,aAAa,kBAAkB,CAAC;AAE7C,yEAAyE;AACzE,eAAO,MAAM,WAAW,WAAW,CAAC;AAEpC,wBAAwB;AACxB,eAAO,MAAM,WAAW;;;;;;;CAOd,CAAC;AAEX,8BAA8B;AAC9B,eAAO,MAAM,cAAc;;;;;;;CAOjB,CAAC;AAEX,oCAAoC;AACpC,eAAO,MAAM,oBAAoB;;;CAGvB,CAAC;AAEX,wBAAwB;AACxB,eAAO,MAAM,SAAS;;;CAGZ,CAAC;AAEX,4CAA4C;AAC5C,eAAO,MAAM,cAAc,EAAG,6BAAsC,CAAC;AAErE,oFAAoF;AACpF,eAAO,MAAM,sBAAsB,EAAG,gCAAyC,CAAC;AAEhF,qBAAqB;AACrB,eAAO,MAAM,cAAc;;;;CAIjB,CAAC;AAEX,qCAAqC;AACrC,eAAO,MAAM,UAAU;;;;;CAKb,CAAC;AAEX,gDAAgD;AAChD,eAAO,MAAM,gBAAgB;;;;CAInB,CAAC;AAEX,iCAAiC;AACjC,eAAO,MAAM,WAAW;;;;;;;;CAUd,CAAC;AAEX,kCAAkC;AAClC,eAAO,MAAM,cAAc;IAC1B,8DAA8D;;IAE9D,sEAAsE;;IAEtE,uEAAuE;;CAE9D,CAAC"}
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../lib/constants.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,kFAAkF;AAClF,eAAO,MAAM,YAAY,wBAAwB,CAAC;AAElD,qFAAqF;AACrF,eAAO,MAAM,mBAAmB,0BAA0B,CAAC;AAE3D,uDAAuD;AACvD,eAAO,MAAM,WAAW,wBAAe,CAAC;AAExC,4DAA4D;AAC5D,eAAO,MAAM,kBAAkB,sCAAsC,CAAC;AACtE,eAAO,MAAM,yBAAyB,+BAA+B,CAAC;AACtE,eAAO,MAAM,0BAA0B,8CAA8C,CAAC;AACtF,eAAO,MAAM,iCAAiC,uCAAuC,CAAC;AACtF,eAAO,MAAM,iCAAiC,uCAAuC,CAAC;AAEtF,uCAAuC;AACvC,eAAO,MAAM,cAAc,oCAAoC,CAAC;AAEhE,gEAAgE;AAChE,eAAO,MAAM,aAAa,kBAAkB,CAAC;AAE7C,yEAAyE;AACzE,eAAO,MAAM,WAAW,WAAW,CAAC;AAEpC,wBAAwB;AACxB,eAAO,MAAM,WAAW;;;;;;;CAOd,CAAC;AAEX,8BAA8B;AAC9B,eAAO,MAAM,cAAc;;;;;;;CAOjB,CAAC;AAEX,oCAAoC;AACpC,eAAO,MAAM,oBAAoB;;;CAGvB,CAAC;AAEX,wBAAwB;AACxB,eAAO,MAAM,SAAS;;;CAGZ,CAAC;AAEX,4CAA4C;AAC5C,eAAO,MAAM,cAAc,EAAG,6BAAsC,CAAC;AAErE,oFAAoF;AACpF,eAAO,MAAM,sBAAsB,EAAG,gCAAyC,CAAC;AAEhF,qBAAqB;AACrB,eAAO,MAAM,cAAc;;;;CAIjB,CAAC;AAEX,qCAAqC;AACrC,eAAO,MAAM,UAAU;;;;;CAKb,CAAC;AAEX,gDAAgD;AAChD,eAAO,MAAM,gBAAgB;;;;CAInB,CAAC;AAEX,iCAAiC;AACjC,eAAO,MAAM,WAAW;;;;;;;;CAUd,CAAC;AAEX,kCAAkC;AAClC,eAAO,MAAM,cAAc;IAC1B,8DAA8D;;IAE9D,sEAAsE;;IAEtE,uEAAuE;;CAE9D,CAAC;AAEX;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,2BAA2B,IAAI,CAAC"}
|
package/dist/lib/constants.js
CHANGED
|
@@ -90,4 +90,17 @@ export const ACCOUNT_LIMITS = {
|
|
|
90
90
|
/** Number of consecutive auth failures before auto-removing account */
|
|
91
91
|
MAX_AUTH_FAILURES_BEFORE_REMOVAL: 3,
|
|
92
92
|
};
|
|
93
|
+
/**
|
|
94
|
+
* Cap on quota-exhausted model hops within one request (index.ts).
|
|
95
|
+
*
|
|
96
|
+
* The attempted set already stops a request revisiting a model; this bounds
|
|
97
|
+
* total hops even under a cyclic custom chain. It must be at least the longest
|
|
98
|
+
* DEFAULT_UNSUPPORTED_CODEX_FALLBACK_CHAIN row (gpt-6-astra's, 6 targets), or a
|
|
99
|
+
* request whose models are blocked one after another stops before reaching the
|
|
100
|
+
* tail of its own default chain. A hop only happens onto a model some account
|
|
101
|
+
* can serve right now, so a larger cap costs nothing when nothing is usable.
|
|
102
|
+
* Kept here rather than in fetch-helpers because test/index.test.ts replaces
|
|
103
|
+
* that module with a factory mock, which would read a new export as undefined.
|
|
104
|
+
*/
|
|
105
|
+
export const MAX_QUOTA_FALLBACK_SWITCHES = 6;
|
|
93
106
|
//# sourceMappingURL=constants.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../../lib/constants.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,kFAAkF;AAClF,MAAM,CAAC,MAAM,YAAY,GAAG,qBAAqB,CAAC;AAElD,qFAAqF;AACrF,MAAM,CAAC,MAAM,mBAAmB,GAAG,uBAAuB,CAAC;AAE3D,uDAAuD;AACvD,MAAM,CAAC,MAAM,WAAW,GAAG,YAAY,CAAC;AAExC,4DAA4D;AAC5D,MAAM,CAAC,MAAM,kBAAkB,GAAG,mCAAmC,CAAC;AACtE,MAAM,CAAC,MAAM,yBAAyB,GAAG,4BAA4B,CAAC;AACtE,MAAM,CAAC,MAAM,0BAA0B,GAAG,2CAA2C,CAAC;AACtF,MAAM,CAAC,MAAM,iCAAiC,GAAG,oCAAoC,CAAC;AACtF,MAAM,CAAC,MAAM,iCAAiC,GAAG,oCAAoC,CAAC;AAEtF,uCAAuC;AACvC,MAAM,CAAC,MAAM,cAAc,GAAG,iCAAiC,CAAC;AAEhE,gEAAgE;AAChE,MAAM,CAAC,MAAM,aAAa,GAAG,eAAe,CAAC;AAE7C,yEAAyE;AACzE,MAAM,CAAC,MAAM,WAAW,GAAG,QAAQ,CAAC;AAEpC,wBAAwB;AACxB,MAAM,CAAC,MAAM,WAAW,GAAG;IAC1B,WAAW,EAAE,GAAG;IAChB,EAAE,EAAE,GAAG;IACP,SAAS,EAAE,GAAG;IACd,YAAY,EAAE,GAAG;IACjB,SAAS,EAAE,GAAG;IACd,iBAAiB,EAAE,GAAG;CACb,CAAC;AAEX,8BAA8B;AAC9B,MAAM,CAAC,MAAM,cAAc,GAAG;IAC7B,IAAI,EAAE,aAAa;IACnB,UAAU,EAAE,oBAAoB;IAChC,eAAe,EAAE,qBAAqB;IACtC,UAAU,EAAE,YAAY;IACxB,UAAU,EAAE,YAAY;IACxB,eAAe,EAAE,iBAAiB;CACzB,CAAC;AAEX,oCAAoC;AACpC,MAAM,CAAC,MAAM,oBAAoB,GAAG;IACnC,cAAc,EAAE,wBAAwB;IACxC,gBAAgB,EAAE,cAAc;CACvB,CAAC;AAEX,wBAAwB;AACxB,MAAM,CAAC,MAAM,SAAS,GAAG;IACxB,SAAS,EAAE,YAAY;IACvB,eAAe,EAAE,kBAAkB;CAC1B,CAAC;AAEX,4CAA4C;AAC5C,MAAM,CAAC,MAAM,cAAc,GAAG,6BAAsC,CAAC;AAErE,oFAAoF;AACpF,MAAM,CAAC,MAAM,sBAAsB,GAAG,gCAAyC,CAAC;AAEhF,qBAAqB;AACrB,MAAM,CAAC,MAAM,cAAc,GAAG;IAC7B,aAAa,EAAE,wCAAwC;IACvD,oBAAoB,EAAE,kDAAkD;IACxE,mBAAmB,EAAE,uBAAuB;CACnC,CAAC;AAEX,qCAAqC;AACrC,MAAM,CAAC,MAAM,UAAU,GAAG;IACzB,gBAAgB,EAAE,kBAAkB;IACpC,eAAe,EAAE,iBAAiB;IAClC,QAAQ,EAAE,UAAU;IACpB,cAAc,EAAE,gBAAgB;CACvB,CAAC;AAEX,gDAAgD;AAChD,MAAM,CAAC,MAAM,gBAAgB,GAAG;IAC/B,MAAM,EAAE,MAAM;IACd,KAAK,EAAE,OAAO;IACd,KAAK,EAAE,UAAU;CACR,CAAC;AAEX,iCAAiC;AACjC,MAAM,CAAC,MAAM,WAAW,GAAG;IAC1B,KAAK,EAAE,gCAAgC;IACvC,oBAAoB,EAAE,iCAAiC;IACvD,iBAAiB,EAAE,2BAA2B;IAC9C,YAAY,EAAE,gCAAgC;IAC9C,OAAO,EAAE,2BAA2B;IACpC,2BAA2B,EAC1B,uHAAuH;IACxH,mBAAmB,EAClB,0IAA0I;CAClI,CAAC;AAEX,kCAAkC;AAClC,MAAM,CAAC,MAAM,cAAc,GAAG;IAC7B,8DAA8D;IAC9D,YAAY,EAAE,EAAE;IAChB,sEAAsE;IACtE,wBAAwB,EAAE,MAAM;IAChC,uEAAuE;IACvE,gCAAgC,EAAE,CAAC;CAC1B,CAAC"}
|
|
1
|
+
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../../lib/constants.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How much Codex capacity one ChatGPT plan carries relative to another.
|
|
3
|
+
*
|
|
4
|
+
* A pool of accounts on different plans has no single "percent used": one
|
|
5
|
+
* Pro seat spent to 50% has given up far more capacity than a Business
|
|
6
|
+
* Standard seat spent to 50%, so averaging the two percentages unweighted
|
|
7
|
+
* reports a pool that does not exist. Every plan here therefore carries a
|
|
8
|
+
* {@link PlanAllotment.weight} - its allotment relative to a 1x seat - and the
|
|
9
|
+
* pool total is the weighted mean.
|
|
10
|
+
*
|
|
11
|
+
* The weights are OpenAI's own published per-seat ratios, taken from the
|
|
12
|
+
* plan's monthly price against the 1x Plus/Business Standard seat: Pro is
|
|
13
|
+
* $200 against $20, and is marketed as 20x. They describe the *subscription*,
|
|
14
|
+
* not a measured token allowance, which is the only ratio OpenAI states and
|
|
15
|
+
* the same one the seat is sold on.
|
|
16
|
+
*
|
|
17
|
+
* `plan_type` is what the `/wham/usage` endpoint and the `chatgpt_plan_type`
|
|
18
|
+
* access-token claim report. Two of its slugs are not derivable from their
|
|
19
|
+
* text - `team` is the slug still emitted for what OpenAI now calls Business,
|
|
20
|
+
* and `self_serve_business_prolite` is the premium Business seat rather than
|
|
21
|
+
* the personal Pro Lite tier that shares the `prolite` token - so both are
|
|
22
|
+
* matched explicitly below.
|
|
23
|
+
*
|
|
24
|
+
* This module is deliberately a leaf (no imports). The prompt status line
|
|
25
|
+
* depends on it, and naming a plan is a separate concern that already has an
|
|
26
|
+
* owner in `lib/auth/plan-tier.ts`; pulling that in here would drag JWT
|
|
27
|
+
* decoding into the TUI's render path for a number that needs none of it.
|
|
28
|
+
*/
|
|
29
|
+
export type PlanAllotment = {
|
|
30
|
+
/**
|
|
31
|
+
* Allotment relative to a 1x seat, or `undefined` when the plan states no
|
|
32
|
+
* ratio. `undefined` is not 1: a plan we cannot place must not be
|
|
33
|
+
* silently averaged as though it were the baseline.
|
|
34
|
+
*/
|
|
35
|
+
weight?: number;
|
|
36
|
+
/** Marketing badge for the same ratio, e.g. `5x`. */
|
|
37
|
+
multiplier?: string;
|
|
38
|
+
/** Per-seat monthly price in USD, as listed by OpenAI. */
|
|
39
|
+
monthlyUsd?: number;
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* Weight used for a plan that states no ratio, so one unplaceable account
|
|
43
|
+
* cannot remove every other account from the pool total. It is the baseline
|
|
44
|
+
* seat rather than a guess at something larger: under-weighting an unknown
|
|
45
|
+
* plan understates one account, while over-weighting it would let a plan we
|
|
46
|
+
* failed to recognize dominate the number the whole pool is judged by.
|
|
47
|
+
*/
|
|
48
|
+
export declare const DEFAULT_PLAN_WEIGHT = 1;
|
|
49
|
+
/**
|
|
50
|
+
* Reduce a `plan_type` to the form the matchers below expect.
|
|
51
|
+
*
|
|
52
|
+
* The admin roster spells a tier as one token (`chatgptteamplan`) while the
|
|
53
|
+
* usage endpoint reports the bare word (`team`), and the premium Business
|
|
54
|
+
* seat arrives underscored (`self_serve_business_prolite`). All three have to
|
|
55
|
+
* land on the same normalized text or one seat is weighted differently
|
|
56
|
+
* depending on which surface named it.
|
|
57
|
+
*/
|
|
58
|
+
export declare function normalizePlanSlug(value: string | null | undefined): string | undefined;
|
|
59
|
+
/**
|
|
60
|
+
* Resolve a `plan_type` to its allotment. An unrecognized plan returns an
|
|
61
|
+
* empty allotment rather than a guess, so callers can tell "1x" apart from
|
|
62
|
+
* "we do not know".
|
|
63
|
+
*/
|
|
64
|
+
export declare function describePlanAllotment(planType: string | null | undefined): PlanAllotment;
|
|
65
|
+
/**
|
|
66
|
+
* The weight to average an account by, falling back to
|
|
67
|
+
* {@link DEFAULT_PLAN_WEIGHT} for a plan that states no ratio.
|
|
68
|
+
*/
|
|
69
|
+
export declare function getPlanWeight(planType: string | null | undefined): number;
|
|
70
|
+
/** `5x`, or `undefined` when the plan states no ratio. */
|
|
71
|
+
export declare function formatPlanMultiplier(planType: string | null | undefined): string | undefined;
|
|
72
|
+
//# sourceMappingURL=plan-allotment.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plan-allotment.d.ts","sourceRoot":"","sources":["../../lib/plan-allotment.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,MAAM,MAAM,aAAa,GAAG;IAC3B;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,qDAAqD;IACrD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,0DAA0D;IAC1D,UAAU,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAIF;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB,IAAI,CAAC;AAErC;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAetF;AA6CD;;;;GAIG;AACH,wBAAgB,qBAAqB,CACpC,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GACjC,aAAa,CAkBf;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAEzE;AAED,0DAA0D;AAC1D,wBAAgB,oBAAoB,CACnC,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GACjC,MAAM,GAAG,SAAS,CAEpB"}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How much Codex capacity one ChatGPT plan carries relative to another.
|
|
3
|
+
*
|
|
4
|
+
* A pool of accounts on different plans has no single "percent used": one
|
|
5
|
+
* Pro seat spent to 50% has given up far more capacity than a Business
|
|
6
|
+
* Standard seat spent to 50%, so averaging the two percentages unweighted
|
|
7
|
+
* reports a pool that does not exist. Every plan here therefore carries a
|
|
8
|
+
* {@link PlanAllotment.weight} - its allotment relative to a 1x seat - and the
|
|
9
|
+
* pool total is the weighted mean.
|
|
10
|
+
*
|
|
11
|
+
* The weights are OpenAI's own published per-seat ratios, taken from the
|
|
12
|
+
* plan's monthly price against the 1x Plus/Business Standard seat: Pro is
|
|
13
|
+
* $200 against $20, and is marketed as 20x. They describe the *subscription*,
|
|
14
|
+
* not a measured token allowance, which is the only ratio OpenAI states and
|
|
15
|
+
* the same one the seat is sold on.
|
|
16
|
+
*
|
|
17
|
+
* `plan_type` is what the `/wham/usage` endpoint and the `chatgpt_plan_type`
|
|
18
|
+
* access-token claim report. Two of its slugs are not derivable from their
|
|
19
|
+
* text - `team` is the slug still emitted for what OpenAI now calls Business,
|
|
20
|
+
* and `self_serve_business_prolite` is the premium Business seat rather than
|
|
21
|
+
* the personal Pro Lite tier that shares the `prolite` token - so both are
|
|
22
|
+
* matched explicitly below.
|
|
23
|
+
*
|
|
24
|
+
* This module is deliberately a leaf (no imports). The prompt status line
|
|
25
|
+
* depends on it, and naming a plan is a separate concern that already has an
|
|
26
|
+
* owner in `lib/auth/plan-tier.ts`; pulling that in here would drag JWT
|
|
27
|
+
* decoding into the TUI's render path for a number that needs none of it.
|
|
28
|
+
*/
|
|
29
|
+
const UNKNOWN_ALLOTMENT = {};
|
|
30
|
+
/**
|
|
31
|
+
* Weight used for a plan that states no ratio, so one unplaceable account
|
|
32
|
+
* cannot remove every other account from the pool total. It is the baseline
|
|
33
|
+
* seat rather than a guess at something larger: under-weighting an unknown
|
|
34
|
+
* plan understates one account, while over-weighting it would let a plan we
|
|
35
|
+
* failed to recognize dominate the number the whole pool is judged by.
|
|
36
|
+
*/
|
|
37
|
+
export const DEFAULT_PLAN_WEIGHT = 1;
|
|
38
|
+
/**
|
|
39
|
+
* Reduce a `plan_type` to the form the matchers below expect.
|
|
40
|
+
*
|
|
41
|
+
* The admin roster spells a tier as one token (`chatgptteamplan`) while the
|
|
42
|
+
* usage endpoint reports the bare word (`team`), and the premium Business
|
|
43
|
+
* seat arrives underscored (`self_serve_business_prolite`). All three have to
|
|
44
|
+
* land on the same normalized text or one seat is weighted differently
|
|
45
|
+
* depending on which surface named it.
|
|
46
|
+
*/
|
|
47
|
+
export function normalizePlanSlug(value) {
|
|
48
|
+
if (typeof value !== "string")
|
|
49
|
+
return undefined;
|
|
50
|
+
// Case is folded before anything else is stripped, so every pattern below
|
|
51
|
+
// can be written once in lower case. Stripping first would leave the
|
|
52
|
+
// trailing-`plan` rule matching `teamplan` but not `Team_Plan`, and the
|
|
53
|
+
// same seat would then weigh differently depending on which surface spelled
|
|
54
|
+
// it.
|
|
55
|
+
const normalized = value
|
|
56
|
+
.trim()
|
|
57
|
+
.toLowerCase()
|
|
58
|
+
.replace(/^chatgpt[\s_-]*/, "")
|
|
59
|
+
.replace(/[\s_-]+/g, " ")
|
|
60
|
+
.replace(/\s?plan$/, "")
|
|
61
|
+
.trim();
|
|
62
|
+
return normalized || undefined;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* A `business` seat's ratio, which depends on the qualifier beside it.
|
|
66
|
+
*
|
|
67
|
+
* `self_serve_business_prolite` normalizes to `self serve business prolite`,
|
|
68
|
+
* so `business` is matched as a whole word anywhere in the text rather than
|
|
69
|
+
* as a prefix. A bare `business` names the workspace rather than the seat,
|
|
70
|
+
* and the two seats inside it are 5x apart, so it states no ratio at all.
|
|
71
|
+
*/
|
|
72
|
+
function describeBusinessSeat(plan) {
|
|
73
|
+
if (!/(^| )business( |$)/.test(plan))
|
|
74
|
+
return undefined;
|
|
75
|
+
if (plan.includes("prolite") ||
|
|
76
|
+
plan.includes("pro lite") ||
|
|
77
|
+
plan.includes("premium")) {
|
|
78
|
+
return { weight: 5, multiplier: "5x", monthlyUsd: 125 };
|
|
79
|
+
}
|
|
80
|
+
if (plan.includes("standard")) {
|
|
81
|
+
return { weight: 1, multiplier: "1x", monthlyUsd: 25 };
|
|
82
|
+
}
|
|
83
|
+
return UNKNOWN_ALLOTMENT;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* The $100 Pro that predates the $200 one. Both report a `pro` family slug,
|
|
87
|
+
* and they are 4x apart, so the legacy spellings are matched before the
|
|
88
|
+
* current tier claims the bare word.
|
|
89
|
+
*/
|
|
90
|
+
function isLegacyProPlan(plan) {
|
|
91
|
+
return (plan === "pro 5x" ||
|
|
92
|
+
plan === "pro 100" ||
|
|
93
|
+
plan === "pro legacy" ||
|
|
94
|
+
plan === "legacy pro" ||
|
|
95
|
+
plan === "legacy pro 5x" ||
|
|
96
|
+
plan === "pro legacy 5x");
|
|
97
|
+
}
|
|
98
|
+
function isCurrentProPlan(plan) {
|
|
99
|
+
return plan === "pro" || plan === "pro 20x" || plan === "pro 200";
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Resolve a `plan_type` to its allotment. An unrecognized plan returns an
|
|
103
|
+
* empty allotment rather than a guess, so callers can tell "1x" apart from
|
|
104
|
+
* "we do not know".
|
|
105
|
+
*/
|
|
106
|
+
export function describePlanAllotment(planType) {
|
|
107
|
+
const plan = normalizePlanSlug(planType);
|
|
108
|
+
if (!plan)
|
|
109
|
+
return UNKNOWN_ALLOTMENT;
|
|
110
|
+
if (plan === "plus")
|
|
111
|
+
return { weight: 1, multiplier: "1x", monthlyUsd: 20 };
|
|
112
|
+
// `team` is the slug OpenAI still emits for Business; a qualified
|
|
113
|
+
// `team premium` / `team standard` is handled by the business matcher.
|
|
114
|
+
if (plan === "team")
|
|
115
|
+
return { weight: 1, multiplier: "1x", monthlyUsd: 25 };
|
|
116
|
+
const business = describeBusinessSeat(plan);
|
|
117
|
+
if (business)
|
|
118
|
+
return business;
|
|
119
|
+
if (isLegacyProPlan(plan))
|
|
120
|
+
return { weight: 5, multiplier: "5x", monthlyUsd: 100 };
|
|
121
|
+
if (isCurrentProPlan(plan))
|
|
122
|
+
return { weight: 20, multiplier: "20x", monthlyUsd: 200 };
|
|
123
|
+
if (plan === "prolite" || plan === "pro lite") {
|
|
124
|
+
return { weight: 5, multiplier: "5x", monthlyUsd: 100 };
|
|
125
|
+
}
|
|
126
|
+
// Go, Free and Enterprise all reach here. The first two carry no Codex
|
|
127
|
+
// allotment worth weighting, and Enterprise is negotiated per contract, so
|
|
128
|
+
// none of them states a ratio this code could apply.
|
|
129
|
+
return UNKNOWN_ALLOTMENT;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* The weight to average an account by, falling back to
|
|
133
|
+
* {@link DEFAULT_PLAN_WEIGHT} for a plan that states no ratio.
|
|
134
|
+
*/
|
|
135
|
+
export function getPlanWeight(planType) {
|
|
136
|
+
return describePlanAllotment(planType).weight ?? DEFAULT_PLAN_WEIGHT;
|
|
137
|
+
}
|
|
138
|
+
/** `5x`, or `undefined` when the plan states no ratio. */
|
|
139
|
+
export function formatPlanMultiplier(planType) {
|
|
140
|
+
return describePlanAllotment(planType).multiplier;
|
|
141
|
+
}
|
|
142
|
+
//# sourceMappingURL=plan-allotment.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plan-allotment.js","sourceRoot":"","sources":["../../lib/plan-allotment.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where this plugin is running from, and where it has run from before.
|
|
3
|
+
*
|
|
4
|
+
* OpenCode can load the plugin from the published package or from a checkout
|
|
5
|
+
* a developer points it at, and the two are indistinguishable at runtime
|
|
6
|
+
* unless the plugin asks. Knowing which one is live decides whether offering
|
|
7
|
+
* an update makes sense, and a record of previous origins is what turns "my
|
|
8
|
+
* edits stopped taking effect" into a diagnosable event.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* The origin history file name, shared with the installer.
|
|
12
|
+
*
|
|
13
|
+
* The installer is plain JS that must run before anything is built, so it
|
|
14
|
+
* cannot import this module and declares the same name itself. A test pins the
|
|
15
|
+
* two together, because a silent disagreement would leave each side reporting
|
|
16
|
+
* confidently about a file the other never writes.
|
|
17
|
+
*/
|
|
18
|
+
export declare const HISTORY_FILE_NAME = "oc-codex-multi-auth-origin.json";
|
|
19
|
+
export interface PluginOrigin {
|
|
20
|
+
name: string;
|
|
21
|
+
version: string;
|
|
22
|
+
root: string;
|
|
23
|
+
isLocalCheckout: boolean;
|
|
24
|
+
}
|
|
25
|
+
export interface PluginOriginSighting extends PluginOrigin {
|
|
26
|
+
firstSeen: string;
|
|
27
|
+
lastSeen: string;
|
|
28
|
+
}
|
|
29
|
+
export interface PluginOriginHistory {
|
|
30
|
+
version: number;
|
|
31
|
+
sightings: PluginOriginSighting[];
|
|
32
|
+
}
|
|
33
|
+
/** Built output lives inside the package, so the root is at or above it. */
|
|
34
|
+
export declare function findPackageRoot(startDirectory: string): string | null;
|
|
35
|
+
/**
|
|
36
|
+
* A root a package manager chose, as opposed to one a human did. The version
|
|
37
|
+
* suffix is what separates OpenCode's plugin cache from a monorepo that merely
|
|
38
|
+
* keeps its packages in a `packages/` directory.
|
|
39
|
+
*/
|
|
40
|
+
export declare function isPackageManagerRoot(root: string, platform?: NodeJS.Platform, cacheDirectory?: string): boolean;
|
|
41
|
+
export declare function resolvePluginOrigin(moduleUrl: string): PluginOrigin | null;
|
|
42
|
+
/** The origin of this running build. Fixed for the life of the process. */
|
|
43
|
+
export declare function getPluginOrigin(): PluginOrigin | null;
|
|
44
|
+
export declare function getPluginOriginHistoryPath(home?: string): string;
|
|
45
|
+
export declare function readPluginOriginHistory(historyPath?: string): PluginOriginHistory;
|
|
46
|
+
/**
|
|
47
|
+
* History is appended to rather than overwritten, so one origin replacing
|
|
48
|
+
* another leaves both on record. A last-writer-wins marker would instead
|
|
49
|
+
* confirm whatever state a clobber left behind.
|
|
50
|
+
*/
|
|
51
|
+
export declare function withSighting(history: PluginOriginHistory, origin: PluginOrigin, seenAt: string, platform?: NodeJS.Platform): PluginOriginHistory;
|
|
52
|
+
/**
|
|
53
|
+
* Records this origin without dropping anybody else's.
|
|
54
|
+
*
|
|
55
|
+
* Every OpenCode process records at startup, and a machine running many of
|
|
56
|
+
* them starts several at once, so read-modify-write on a shared file is a real
|
|
57
|
+
* race rather than a theoretical one. The reader that matters here asks which
|
|
58
|
+
* origins have been seen, so a lost sighting is a lost answer: the very
|
|
59
|
+
* handover this file exists to report - a checkout replaced by the installed
|
|
60
|
+
* package - is two different origins written at close to the same time.
|
|
61
|
+
*
|
|
62
|
+
* The history is therefore re-read INSIDE the lease, so each writer merges
|
|
63
|
+
* into what is actually on disk. A writer that cannot take the lease records
|
|
64
|
+
* nothing rather than overwriting blind; it is about to be started again, and
|
|
65
|
+
* a missing sighting costs a later startup while a clobbered one costs the
|
|
66
|
+
* only evidence there was. A lease reclaimed mid-write is the same situation
|
|
67
|
+
* arriving later, and is answered the same way.
|
|
68
|
+
*/
|
|
69
|
+
export declare function recordPluginOrigin(origin: PluginOrigin, historyPath?: string, now?: () => Date): Promise<PluginOriginHistory>;
|
|
70
|
+
/**
|
|
71
|
+
* The checkout this plugin used to run from and no longer does. Reported only
|
|
72
|
+
* when the current origin is package-manager output, since a move between two
|
|
73
|
+
* checkouts is an ordinary thing to do deliberately.
|
|
74
|
+
*/
|
|
75
|
+
export declare function findReplacedLocalCheckout(origin: PluginOrigin, history: PluginOriginHistory, platform?: NodeJS.Platform): PluginOriginSighting | null;
|
|
76
|
+
export declare function describePluginOrigin(origin: PluginOrigin | null): string;
|
|
77
|
+
//# sourceMappingURL=plugin-origin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin-origin.d.ts","sourceRoot":"","sources":["../../lib/plugin-origin.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAaH;;;;;;;GAOG;AACH,eAAO,MAAM,iBAAiB,oCAAoC,CAAC;AA0BnE,MAAM,WAAW,YAAY;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,eAAe,EAAE,OAAO,CAAC;CACzB;AAED,MAAM,WAAW,oBAAqB,SAAQ,YAAY;IACzD,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,mBAAmB;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,oBAAoB,EAAE,CAAC;CAClC;AA0BD,4EAA4E;AAC5E,wBAAgB,eAAe,CAAC,cAAc,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CASrE;AAkCD;;;;GAIG;AACH,wBAAgB,oBAAoB,CACnC,IAAI,EAAE,MAAM,EACZ,QAAQ,GAAE,MAAM,CAAC,QAA2B,EAC5C,cAAc,GAAE,MAAgC,GAC9C,OAAO,CAYT;AAED,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CAiB1E;AAID,2EAA2E;AAC3E,wBAAgB,eAAe,IAAI,YAAY,GAAG,IAAI,CAGrD;AAED,wBAAgB,0BAA0B,CAAC,IAAI,GAAE,MAAkB,GAAG,MAAM,CAE3E;AAeD,wBAAgB,uBAAuB,CACtC,WAAW,GAAE,MAAqC,GAChD,mBAAmB,CAUrB;AAMD;;;;GAIG;AACH,wBAAgB,YAAY,CAC3B,OAAO,EAAE,mBAAmB,EAC5B,MAAM,EAAE,YAAY,EACpB,MAAM,EAAE,MAAM,EACd,QAAQ,GAAE,MAAM,CAAC,QAA2B,GAC1C,mBAAmB,CAiBrB;AA6BD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,kBAAkB,CACvC,MAAM,EAAE,YAAY,EACpB,WAAW,GAAE,MAAqC,EAClD,GAAG,GAAE,MAAM,IAAuB,GAChC,OAAO,CAAC,mBAAmB,CAAC,CA4C9B;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,CACxC,MAAM,EAAE,YAAY,EACpB,OAAO,EAAE,mBAAmB,EAC5B,QAAQ,GAAE,MAAM,CAAC,QAA2B,GAC1C,oBAAoB,GAAG,IAAI,CAc7B;AAED,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI,GAAG,MAAM,CAKxE"}
|
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where this plugin is running from, and where it has run from before.
|
|
3
|
+
*
|
|
4
|
+
* OpenCode can load the plugin from the published package or from a checkout
|
|
5
|
+
* a developer points it at, and the two are indistinguishable at runtime
|
|
6
|
+
* unless the plugin asks. Knowing which one is live decides whether offering
|
|
7
|
+
* an update makes sense, and a record of previous origins is what turns "my
|
|
8
|
+
* edits stopped taking effect" into a diagnosable event.
|
|
9
|
+
*/
|
|
10
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
11
|
+
import { mkdir, rm, writeFile } from "node:fs/promises";
|
|
12
|
+
import { homedir } from "node:os";
|
|
13
|
+
import { dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
14
|
+
import { fileURLToPath } from "node:url";
|
|
15
|
+
import { lock } from "proper-lockfile";
|
|
16
|
+
import { createLogger } from "./logger.js";
|
|
17
|
+
import { renameWithWindowsRetry } from "./storage/atomic-write.js";
|
|
18
|
+
const log = createLogger("plugin-origin");
|
|
19
|
+
/**
|
|
20
|
+
* The origin history file name, shared with the installer.
|
|
21
|
+
*
|
|
22
|
+
* The installer is plain JS that must run before anything is built, so it
|
|
23
|
+
* cannot import this module and declares the same name itself. A test pins the
|
|
24
|
+
* two together, because a silent disagreement would leave each side reporting
|
|
25
|
+
* confidently about a file the other never writes.
|
|
26
|
+
*/
|
|
27
|
+
export const HISTORY_FILE_NAME = "oc-codex-multi-auth-origin.json";
|
|
28
|
+
const HISTORY_VERSION = 1;
|
|
29
|
+
const MAX_SIGHTINGS = 10;
|
|
30
|
+
const PACKAGE_ROOT_LOOKUP_DEPTH = 3;
|
|
31
|
+
/** The lease covers one small read and one small write, so it is short. */
|
|
32
|
+
const HISTORY_LOCK_STALE_MS = 10_000;
|
|
33
|
+
const HISTORY_LOCK_UPDATE_MS = 2_000;
|
|
34
|
+
const HISTORY_LOCK_RETRIES = {
|
|
35
|
+
retries: 10,
|
|
36
|
+
factor: 1.8,
|
|
37
|
+
minTimeout: 50,
|
|
38
|
+
maxTimeout: 500,
|
|
39
|
+
randomize: true,
|
|
40
|
+
};
|
|
41
|
+
const MANAGED_PACKAGE_NAMES = new Set(["oc-codex-multi-auth", "oc-chatgpt-multi-auth"]);
|
|
42
|
+
function isManagedPackageName(name) {
|
|
43
|
+
return MANAGED_PACKAGE_NAMES.has(name.toLowerCase());
|
|
44
|
+
}
|
|
45
|
+
function defaultCacheDirectory(home = homedir()) {
|
|
46
|
+
return join(home, ".cache", "opencode");
|
|
47
|
+
}
|
|
48
|
+
function emptyHistory() {
|
|
49
|
+
return { version: HISTORY_VERSION, sightings: [] };
|
|
50
|
+
}
|
|
51
|
+
function readPackageManifest(directory) {
|
|
52
|
+
try {
|
|
53
|
+
const parsed = JSON.parse(readFileSync(join(directory, "package.json"), "utf8"));
|
|
54
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
55
|
+
return null;
|
|
56
|
+
return parsed;
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
function manifestName(manifest) {
|
|
63
|
+
const name = manifest?.name;
|
|
64
|
+
return typeof name === "string" && name.trim() ? name.trim() : null;
|
|
65
|
+
}
|
|
66
|
+
/** Built output lives inside the package, so the root is at or above it. */
|
|
67
|
+
export function findPackageRoot(startDirectory) {
|
|
68
|
+
let current = resolve(startDirectory);
|
|
69
|
+
for (let depth = 0; depth <= PACKAGE_ROOT_LOOKUP_DEPTH; depth += 1) {
|
|
70
|
+
if (manifestName(readPackageManifest(current)))
|
|
71
|
+
return current;
|
|
72
|
+
const parent = dirname(current);
|
|
73
|
+
if (parent === current)
|
|
74
|
+
return null;
|
|
75
|
+
current = parent;
|
|
76
|
+
}
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
function pathSegments(path) {
|
|
80
|
+
return path.replaceAll("\\", "/").replace(/\/+$/, "").split("/").filter(Boolean);
|
|
81
|
+
}
|
|
82
|
+
function foldPathCase(value, platform) {
|
|
83
|
+
return platform === "win32" || platform === "darwin" ? value.toLowerCase() : value;
|
|
84
|
+
}
|
|
85
|
+
function isInsideDirectory(candidate, directory, platform) {
|
|
86
|
+
const relativePath = relative(foldPathCase(resolve(directory), platform), foldPathCase(resolve(candidate), platform));
|
|
87
|
+
return relativePath !== "" && !relativePath.startsWith("..") && !isAbsolute(relativePath);
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* How two spellings of a root are told apart. Windows reaches one directory
|
|
91
|
+
* under several of them, and so does a default-case-insensitive APFS volume,
|
|
92
|
+
* so comparing verbatim there would let `C:\Repo` and `c:\repo` - or `/Repo`
|
|
93
|
+
* and `/repo` - occupy two slots in a bounded history and evict a genuinely
|
|
94
|
+
* different origin between them.
|
|
95
|
+
*/
|
|
96
|
+
function rootComparisonKey(root, platform = process.platform) {
|
|
97
|
+
const normalized = root.replaceAll("\\", "/").replace(/\/+$/, "");
|
|
98
|
+
return foldPathCase(normalized, platform);
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* A root a package manager chose, as opposed to one a human did. The version
|
|
102
|
+
* suffix is what separates OpenCode's plugin cache from a monorepo that merely
|
|
103
|
+
* keeps its packages in a `packages/` directory.
|
|
104
|
+
*/
|
|
105
|
+
export function isPackageManagerRoot(root, platform = process.platform, cacheDirectory = defaultCacheDirectory()) {
|
|
106
|
+
const segments = pathSegments(root).map((segment) => foldPathCase(segment, platform));
|
|
107
|
+
if (segments.some((segment, index) => segment === "node_modules" ||
|
|
108
|
+
(segments[index - 1] === "packages" && segment.includes("@")))) {
|
|
109
|
+
return true;
|
|
110
|
+
}
|
|
111
|
+
return isInsideDirectory(root, cacheDirectory, platform);
|
|
112
|
+
}
|
|
113
|
+
export function resolvePluginOrigin(moduleUrl) {
|
|
114
|
+
let moduleDirectory;
|
|
115
|
+
try {
|
|
116
|
+
moduleDirectory = dirname(fileURLToPath(moduleUrl));
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
const root = findPackageRoot(moduleDirectory);
|
|
122
|
+
if (!root)
|
|
123
|
+
return null;
|
|
124
|
+
const manifest = readPackageManifest(root);
|
|
125
|
+
const name = manifestName(manifest);
|
|
126
|
+
if (!name || !isManagedPackageName(name))
|
|
127
|
+
return null;
|
|
128
|
+
const version = typeof manifest?.version === "string" ? manifest.version : "0.0.0";
|
|
129
|
+
return { name, version, root, isLocalCheckout: !isPackageManagerRoot(root) };
|
|
130
|
+
}
|
|
131
|
+
let cachedOrigin;
|
|
132
|
+
/** The origin of this running build. Fixed for the life of the process. */
|
|
133
|
+
export function getPluginOrigin() {
|
|
134
|
+
if (cachedOrigin === undefined)
|
|
135
|
+
cachedOrigin = resolvePluginOrigin(import.meta.url);
|
|
136
|
+
return cachedOrigin;
|
|
137
|
+
}
|
|
138
|
+
export function getPluginOriginHistoryPath(home = homedir()) {
|
|
139
|
+
return join(home, ".opencode", HISTORY_FILE_NAME);
|
|
140
|
+
}
|
|
141
|
+
function isSighting(value) {
|
|
142
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
143
|
+
return false;
|
|
144
|
+
const candidate = value;
|
|
145
|
+
return (typeof candidate.name === "string" &&
|
|
146
|
+
typeof candidate.version === "string" &&
|
|
147
|
+
typeof candidate.root === "string" &&
|
|
148
|
+
typeof candidate.isLocalCheckout === "boolean" &&
|
|
149
|
+
typeof candidate.firstSeen === "string" &&
|
|
150
|
+
typeof candidate.lastSeen === "string");
|
|
151
|
+
}
|
|
152
|
+
export function readPluginOriginHistory(historyPath = getPluginOriginHistoryPath()) {
|
|
153
|
+
try {
|
|
154
|
+
const parsed = JSON.parse(readFileSync(historyPath, "utf8"));
|
|
155
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
156
|
+
return emptyHistory();
|
|
157
|
+
const sightings = parsed.sightings;
|
|
158
|
+
if (!Array.isArray(sightings))
|
|
159
|
+
return emptyHistory();
|
|
160
|
+
return { version: HISTORY_VERSION, sightings: sightings.filter(isSighting) };
|
|
161
|
+
}
|
|
162
|
+
catch {
|
|
163
|
+
return emptyHistory();
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
function byLastSeen(left, right) {
|
|
167
|
+
return (Date.parse(left.lastSeen) || 0) - (Date.parse(right.lastSeen) || 0);
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* History is appended to rather than overwritten, so one origin replacing
|
|
171
|
+
* another leaves both on record. A last-writer-wins marker would instead
|
|
172
|
+
* confirm whatever state a clobber left behind.
|
|
173
|
+
*/
|
|
174
|
+
export function withSighting(history, origin, seenAt, platform = process.platform) {
|
|
175
|
+
// Matched on the comparison key, recorded as the origin spells it: a reader
|
|
176
|
+
// is shown the path they configured, not a folded version of it.
|
|
177
|
+
const key = rootComparisonKey(origin.root, platform);
|
|
178
|
+
const sameRoot = (sighting) => rootComparisonKey(sighting.root, platform) === key;
|
|
179
|
+
const previous = history.sightings.find(sameRoot);
|
|
180
|
+
const others = history.sightings.filter((sighting) => !sameRoot(sighting));
|
|
181
|
+
const current = {
|
|
182
|
+
...origin,
|
|
183
|
+
firstSeen: previous?.firstSeen ?? seenAt,
|
|
184
|
+
lastSeen: seenAt,
|
|
185
|
+
};
|
|
186
|
+
return {
|
|
187
|
+
version: HISTORY_VERSION,
|
|
188
|
+
sightings: [...others, current].slice(-MAX_SIGHTINGS),
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Replaces the history, unless ownership was lost while the new copy was being
|
|
193
|
+
* written. Only the rename is visible to anybody else, so that is where the
|
|
194
|
+
* question has to be asked: checking earlier leaves the whole of `writeFile`
|
|
195
|
+
* as a window in which the lease can be reclaimed and newer history written,
|
|
196
|
+
* which this rename would then replace with an older snapshot.
|
|
197
|
+
*
|
|
198
|
+
* Returns whether the replacement happened.
|
|
199
|
+
*/
|
|
200
|
+
async function writeHistory(historyPath, history, stillOwned) {
|
|
201
|
+
const temporaryPath = `${historyPath}.${process.pid}.tmp`;
|
|
202
|
+
await writeFile(temporaryPath, `${JSON.stringify(history, null, 2)}\n`, "utf-8");
|
|
203
|
+
try {
|
|
204
|
+
if (!stillOwned())
|
|
205
|
+
return false;
|
|
206
|
+
await renameWithWindowsRetry(temporaryPath, historyPath);
|
|
207
|
+
return true;
|
|
208
|
+
}
|
|
209
|
+
finally {
|
|
210
|
+
if (existsSync(temporaryPath)) {
|
|
211
|
+
await rm(temporaryPath, { force: true }).catch(() => undefined);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Records this origin without dropping anybody else's.
|
|
217
|
+
*
|
|
218
|
+
* Every OpenCode process records at startup, and a machine running many of
|
|
219
|
+
* them starts several at once, so read-modify-write on a shared file is a real
|
|
220
|
+
* race rather than a theoretical one. The reader that matters here asks which
|
|
221
|
+
* origins have been seen, so a lost sighting is a lost answer: the very
|
|
222
|
+
* handover this file exists to report - a checkout replaced by the installed
|
|
223
|
+
* package - is two different origins written at close to the same time.
|
|
224
|
+
*
|
|
225
|
+
* The history is therefore re-read INSIDE the lease, so each writer merges
|
|
226
|
+
* into what is actually on disk. A writer that cannot take the lease records
|
|
227
|
+
* nothing rather than overwriting blind; it is about to be started again, and
|
|
228
|
+
* a missing sighting costs a later startup while a clobbered one costs the
|
|
229
|
+
* only evidence there was. A lease reclaimed mid-write is the same situation
|
|
230
|
+
* arriving later, and is answered the same way.
|
|
231
|
+
*/
|
|
232
|
+
export async function recordPluginOrigin(origin, historyPath = getPluginOriginHistoryPath(), now = () => new Date()) {
|
|
233
|
+
await mkdir(dirname(historyPath), { recursive: true });
|
|
234
|
+
let compromised;
|
|
235
|
+
let release = null;
|
|
236
|
+
try {
|
|
237
|
+
release = await lock(historyPath, {
|
|
238
|
+
realpath: false,
|
|
239
|
+
lockfilePath: `${historyPath}.lock`,
|
|
240
|
+
stale: HISTORY_LOCK_STALE_MS,
|
|
241
|
+
update: HISTORY_LOCK_UPDATE_MS,
|
|
242
|
+
retries: HISTORY_LOCK_RETRIES,
|
|
243
|
+
// proper-lockfile's default rethrows from the timer that refreshes the
|
|
244
|
+
// lease, so it lands outside every promise chain and ends the process
|
|
245
|
+
// hosting this plugin. A stalled event loop is enough to trigger it.
|
|
246
|
+
// Nothing here is worth an editor closing, so the loss is recorded and
|
|
247
|
+
// the write is abandoned instead.
|
|
248
|
+
onCompromised: (error) => {
|
|
249
|
+
compromised = error;
|
|
250
|
+
log.warn("The plugin origin history lease was reclaimed", {
|
|
251
|
+
path: `${historyPath}.lock`,
|
|
252
|
+
error: error.message,
|
|
253
|
+
});
|
|
254
|
+
},
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
catch (error) {
|
|
258
|
+
log.debug("Skipped recording the plugin origin; another process holds the history", {
|
|
259
|
+
error: error instanceof Error ? error.message : String(error),
|
|
260
|
+
});
|
|
261
|
+
return readPluginOriginHistory(historyPath);
|
|
262
|
+
}
|
|
263
|
+
try {
|
|
264
|
+
const next = withSighting(readPluginOriginHistory(historyPath), origin, now().toISOString());
|
|
265
|
+
// Whoever reclaimed the lease owns the file now, so writing what we read
|
|
266
|
+
// before they did would drop their sighting - the clobber the lease
|
|
267
|
+
// exists to prevent. Asked again at the rename, since the lease can be
|
|
268
|
+
// lost at any point up to it.
|
|
269
|
+
if (compromised)
|
|
270
|
+
return readPluginOriginHistory(historyPath);
|
|
271
|
+
const written = await writeHistory(historyPath, next, () => !compromised);
|
|
272
|
+
return written ? next : readPluginOriginHistory(historyPath);
|
|
273
|
+
}
|
|
274
|
+
finally {
|
|
275
|
+
await release().catch(() => undefined);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* The checkout this plugin used to run from and no longer does. Reported only
|
|
280
|
+
* when the current origin is package-manager output, since a move between two
|
|
281
|
+
* checkouts is an ordinary thing to do deliberately.
|
|
282
|
+
*/
|
|
283
|
+
export function findReplacedLocalCheckout(origin, history, platform = process.platform) {
|
|
284
|
+
if (origin.isLocalCheckout)
|
|
285
|
+
return null;
|
|
286
|
+
const currentKey = rootComparisonKey(origin.root, platform);
|
|
287
|
+
return (history.sightings
|
|
288
|
+
.filter((sighting) => isManagedPackageName(sighting.name) &&
|
|
289
|
+
sighting.isLocalCheckout &&
|
|
290
|
+
rootComparisonKey(sighting.root, platform) !== currentKey)
|
|
291
|
+
.sort(byLastSeen)
|
|
292
|
+
.at(-1) ?? null);
|
|
293
|
+
}
|
|
294
|
+
export function describePluginOrigin(origin) {
|
|
295
|
+
if (!origin)
|
|
296
|
+
return "unknown";
|
|
297
|
+
return origin.isLocalCheckout
|
|
298
|
+
? `local checkout at ${origin.root} (v${origin.version})`
|
|
299
|
+
: `installed package v${origin.version}`;
|
|
300
|
+
}
|
|
301
|
+
//# sourceMappingURL=plugin-origin.js.map
|