@covro/covro 0.6.1-dev.1 → 0.6.1-dev.3

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.
Files changed (108) hide show
  1. package/README.md +2 -2
  2. package/README.zh-CN.md +2 -2
  3. package/dist/codex/account-usage.js +132 -0
  4. package/dist/codex/account-usage.js.map +7 -0
  5. package/dist/codex/adapter.js +31 -2
  6. package/dist/codex/adapter.js.map +2 -2
  7. package/dist/server/events.js +20 -3
  8. package/dist/server/events.js.map +2 -2
  9. package/dist/server/http.js +8 -0
  10. package/dist/server/http.js.map +2 -2
  11. package/dist/server/service.js +32 -11
  12. package/dist/server/service.js.map +2 -2
  13. package/dist/server/thread-patch.js +58 -0
  14. package/dist/server/thread-patch.js.map +7 -0
  15. package/dist/server/websocket.js +14 -1
  16. package/dist/server/websocket.js.map +2 -2
  17. package/dist/shared/account-usage.js +1 -0
  18. package/dist/shared/account-usage.js.map +7 -0
  19. package/dist/shared/thread-patch.js +38 -0
  20. package/dist/shared/thread-patch.js.map +7 -0
  21. package/dist/shared/validation.js +1 -0
  22. package/dist/shared/validation.js.map +2 -2
  23. package/dist/web/assets/{abnfDiagram-WEYOIA3C-12kbP4fE.js → abnfDiagram-WEYOIA3C-BzVXOBcA.js} +1 -1
  24. package/dist/web/assets/{arc-3Cwtj2dL.js → arc-CyQWAnQd.js} +1 -1
  25. package/dist/web/assets/{architecture-WOLXFQ4H-DLpj2b3w.js → architecture-WOLXFQ4H-BEoyxwG0.js} +1 -1
  26. package/dist/web/assets/{architectureDiagram-ZHVYKT53-RlF4-Ioc.js → architectureDiagram-ZHVYKT53-CCXl4OZW.js} +1 -1
  27. package/dist/web/assets/{blockDiagram-RJAE7QA3-C4qOX-bt.js → blockDiagram-RJAE7QA3-D7dGEImG.js} +1 -1
  28. package/dist/web/assets/{c4Diagram-XDPPPE54-D7JMbo-C.js → c4Diagram-XDPPPE54-DV8znYp0.js} +1 -1
  29. package/dist/web/assets/channel-kBOj6wNa.js +1 -0
  30. package/dist/web/assets/{chunk-2E7WHVMD-IDKX-Tea.js → chunk-2E7WHVMD-0QrTjfW_.js} +1 -1
  31. package/dist/web/assets/{chunk-4EMNKR2T-Dc-yhxET.js → chunk-4EMNKR2T-D-zlzBAy.js} +1 -1
  32. package/dist/web/assets/{chunk-55XCU76K-CGd2UwoZ.js → chunk-55XCU76K-CVHEMROA.js} +1 -1
  33. package/dist/web/assets/{chunk-7S6XSTFO-BvKa88G4.js → chunk-7S6XSTFO-C2rIevGp.js} +1 -1
  34. package/dist/web/assets/{chunk-AQDTSKZB-C7nMX4W-.js → chunk-AQDTSKZB-DYAv-Riy.js} +1 -1
  35. package/dist/web/assets/{chunk-DSNLSHJZ-BzJy_vEu.js → chunk-DSNLSHJZ-DWI7CHhD.js} +1 -1
  36. package/dist/web/assets/{chunk-HAGFOTED-r9b5k0Ex.js → chunk-HAGFOTED-CO_q1459.js} +1 -1
  37. package/dist/web/assets/{chunk-HNE4Q36P-DQoXCaGC.js → chunk-HNE4Q36P-CgksCWEg.js} +1 -1
  38. package/dist/web/assets/{chunk-NQOKRVIF-1bL6YAHF.js → chunk-NQOKRVIF-BkpxlXF9.js} +1 -1
  39. package/dist/web/assets/{chunk-UHW7PFBP-psrTYP5z.js → chunk-UHW7PFBP-BMZQKGkg.js} +1 -1
  40. package/dist/web/assets/{chunk-VQ5W62QI-E2m7QCpn.js → chunk-VQ5W62QI-DxCsqxIr.js} +1 -1
  41. package/dist/web/assets/{chunk-WQQL4CDK-Y328jV78.js → chunk-WQQL4CDK-Drs4qCbY.js} +1 -1
  42. package/dist/web/assets/{chunk-XXDRQBXY-D4uat8z8.js → chunk-XXDRQBXY--mv0FkSH.js} +1 -1
  43. package/dist/web/assets/{chunk-YZO67HFG-C5FacS0u.js → chunk-YZO67HFG-BrXN1Efx.js} +1 -1
  44. package/dist/web/assets/{chunk-ZAUX2LTX-lmxyuzUz.js → chunk-ZAUX2LTX-DWiVb0QN.js} +1 -1
  45. package/dist/web/assets/{chunk-ZCHMQSOR-B-eLKLDi.js → chunk-ZCHMQSOR-p8m3-BJN.js} +1 -1
  46. package/dist/web/assets/{classDiagram-v2-4MYWR3UA-ClhXABmk.js → classDiagram-v2-4MYWR3UA-lnHHmraY.js} +1 -1
  47. package/dist/web/assets/{cose-bilkent-JH36ORCC-BwPkaA_U.js → cose-bilkent-JH36ORCC-B1BVV8Wv.js} +1 -1
  48. package/dist/web/assets/{cynefin-EF2NZ3EQ-Bsl3oI8o.js → cynefin-EF2NZ3EQ-C4TULYfr.js} +1 -1
  49. package/dist/web/assets/{cynefinDiagram-WF24UDRP-ap8wP3TK.js → cynefinDiagram-WF24UDRP-rExRJUWB.js} +1 -1
  50. package/dist/web/assets/{dagre-NK4GIZVX-D93ljWDv.js → dagre-NK4GIZVX-DI6KC0Pk.js} +1 -1
  51. package/dist/web/assets/{diagram-CBK3MWTC-CgXKZEzJ.js → diagram-CBK3MWTC-Don_Jr-E.js} +1 -1
  52. package/dist/web/assets/{diagram-I5XIIV2D-Ce32mo7i.js → diagram-I5XIIV2D-BW1FwPhX.js} +1 -1
  53. package/dist/web/assets/{diagram-ODIL2UCW-WM-FVzky.js → diagram-ODIL2UCW-DxqQXqCa.js} +1 -1
  54. package/dist/web/assets/{diagram-PFBEJUXE-4gCFo57A.js → diagram-PFBEJUXE-DeFKndZw.js} +1 -1
  55. package/dist/web/assets/{diagram-RJA3I4VV-BsUVCONT.js → diagram-RJA3I4VV-CM31q1uK.js} +1 -1
  56. package/dist/web/assets/{diagram-TYUNFSNR-CuIasd0o.js → diagram-TYUNFSNR-DTq2AOnu.js} +1 -1
  57. package/dist/web/assets/{dist-DE2aWlVX.js → dist-V4sbzI-h.js} +1 -1
  58. package/dist/web/assets/{ebnfDiagram-V4P3TTPT-BMFDt5P9.js → ebnfDiagram-V4P3TTPT-DF58HT_y.js} +1 -1
  59. package/dist/web/assets/{elk-QONTBCTQ-CbWBnkf9.js → elk-QONTBCTQ-BwrVYRCb.js} +1 -1
  60. package/dist/web/assets/{erDiagram-KYQIGQAA-BfujTOMs.js → erDiagram-KYQIGQAA-P1WxHJun.js} +1 -1
  61. package/dist/web/assets/{eventmodeling-K75KTNOO-D9yPZPJr.js → eventmodeling-K75KTNOO-BtccOmRK.js} +1 -1
  62. package/dist/web/assets/flowDiagram-IUGLLWBU-DnTlbOxw.js +1 -0
  63. package/dist/web/assets/{ganttDiagram-R7GPNTJY-BQailqpt.js → ganttDiagram-R7GPNTJY-D3qJC4vO.js} +1 -1
  64. package/dist/web/assets/{gitGraph-VSP46ZUC-Cn0-LfrH.js → gitGraph-VSP46ZUC-BVY9Gtih.js} +1 -1
  65. package/dist/web/assets/{gitGraphDiagram-PDYL7XHY-D_Bv68A5.js → gitGraphDiagram-PDYL7XHY-CZvvvj3Q.js} +1 -1
  66. package/dist/web/assets/{index-B3yair14.js → index-B9qAZAx0.js} +47 -47
  67. package/dist/web/assets/{index-qYBdGqKM.css → index-_yn3Nt0h.css} +1 -1
  68. package/dist/web/assets/{info-OHQRW6UA-C-rFhEgh.js → info-OHQRW6UA-BGqiJ9Yx.js} +1 -1
  69. package/dist/web/assets/{infoDiagram-MVDRKAX5-yWVzvl2L.js → infoDiagram-MVDRKAX5-DxdTuj3n.js} +1 -1
  70. package/dist/web/assets/{ishikawaDiagram-HPEHV2GE-DFYQOW9-.js → ishikawaDiagram-HPEHV2GE-DbhjLHsb.js} +1 -1
  71. package/dist/web/assets/{journeyDiagram-BOSHW7O7-B1zy7CA-.js → journeyDiagram-BOSHW7O7-DJROkZUW.js} +1 -1
  72. package/dist/web/assets/{kanban-definition-FNSA4HC5-CxyXZtX-.js → kanban-definition-FNSA4HC5-D4HijqrG.js} +1 -1
  73. package/dist/web/assets/{line-DU2ihvUj.js → line-d2EHEr40.js} +1 -1
  74. package/dist/web/assets/{linear-CFRZhmcT.js → linear-CnT_m8Aq.js} +1 -1
  75. package/dist/web/assets/{mermaid-parser.core-DKeYv3x5.js → mermaid-parser.core-BqD0_cdI.js} +3 -3
  76. package/dist/web/assets/{mermaid.core-D8iJrzMj.js → mermaid.core-Dd1sHHm2.js} +4 -4
  77. package/dist/web/assets/{mindmap-definition-6OY57UG3-CKbrIT8N.js → mindmap-definition-6OY57UG3-KPpq2dAV.js} +1 -1
  78. package/dist/web/assets/{packet-JDAUHWVQ-B9qYbkxZ.js → packet-JDAUHWVQ-CXnrw9bC.js} +1 -1
  79. package/dist/web/assets/{pegDiagram-ECIUJNBX-QnJNmnXC.js → pegDiagram-ECIUJNBX-BIXIhs7W.js} +1 -1
  80. package/dist/web/assets/{pie-XZMESJXO-BWBbsmjp.js → pie-XZMESJXO-TrDzTbhs.js} +1 -1
  81. package/dist/web/assets/{pieDiagram-GNMPY74T-Bubnnbvj.js → pieDiagram-GNMPY74T-D8a4Sfn1.js} +1 -1
  82. package/dist/web/assets/{quadrantDiagram-D57K4MC3-Cs_Ch2cN.js → quadrantDiagram-D57K4MC3-Bt2I_ygO.js} +1 -1
  83. package/dist/web/assets/{radar-ABXABTNO-DR6emy7T.js → radar-ABXABTNO-BZDG4zL1.js} +1 -1
  84. package/dist/web/assets/{railroad-I3PHUGI6-B0D7AskV.js → railroad-I3PHUGI6-CVZI3RsT.js} +1 -1
  85. package/dist/web/assets/{railroad-abnf-LNEFI6M7-Bz6aNqE_.js → railroad-abnf-LNEFI6M7-BLFBfBsa.js} +1 -1
  86. package/dist/web/assets/{railroad-ebnf-DWYD2UWJ-D0OpFEED.js → railroad-ebnf-DWYD2UWJ-CKhl-KNN.js} +1 -1
  87. package/dist/web/assets/{railroad-peg-JR7BVNR7-C_NZ01lj.js → railroad-peg-JR7BVNR7-CaxuMu1-.js} +1 -1
  88. package/dist/web/assets/{railroadDiagram-Y72NRBL7-Dj39CKJS.js → railroadDiagram-Y72NRBL7-BmN9o9yG.js} +1 -1
  89. package/dist/web/assets/{requirementDiagram-EQYSW5RA-B3LvvxeY.js → requirementDiagram-EQYSW5RA-CytiXIk0.js} +1 -1
  90. package/dist/web/assets/{sankeyDiagram-SY24JHO5-DZRJovJs.js → sankeyDiagram-SY24JHO5-2oasnbjI.js} +1 -1
  91. package/dist/web/assets/{sequenceDiagram-BLEHNHQU-Cjtr9DEf.js → sequenceDiagram-BLEHNHQU-CgKYeJhF.js} +1 -1
  92. package/dist/web/assets/{src-CiORhf3I.js → src-DeJ6DlL6.js} +1 -1
  93. package/dist/web/assets/{stateDiagram-v2-A6IESKWZ-CRWe83GV.js → stateDiagram-v2-A6IESKWZ-BcBo9Tt9.js} +1 -1
  94. package/dist/web/assets/{swimlanes-B3ENRKFQ-Dl64HZAr.js → swimlanes-B3ENRKFQ-DHvDY449.js} +1 -1
  95. package/dist/web/assets/swimlanesDiagram-AYQRV4AK-2cPOFNNL.js +8 -0
  96. package/dist/web/assets/{timeline-definition-JXYKGLGB-CNMLxrA1.js → timeline-definition-JXYKGLGB-DHOYx58-.js} +1 -1
  97. package/dist/web/assets/{treeView-D4JQ5SDB-DJKJS2TK.js → treeView-D4JQ5SDB-BKzsH1H3.js} +1 -1
  98. package/dist/web/assets/{treemap-SAJKECNS-Bys6-NSp.js → treemap-SAJKECNS-Dznq3JYy.js} +1 -1
  99. package/dist/web/assets/{usecaseDiagram-WPOLBNNX-tYc1Q8xT.js → usecaseDiagram-WPOLBNNX-B_Qb6GIb.js} +1 -1
  100. package/dist/web/assets/{vennDiagram-Q3Y3IDCW-yr790RR-.js → vennDiagram-Q3Y3IDCW-DDRYcnZj.js} +1 -1
  101. package/dist/web/assets/{wardley-7MLQ67FV-d-Le2twL.js → wardley-7MLQ67FV-CwCK5TZf.js} +1 -1
  102. package/dist/web/assets/{wardleyDiagram-IVAIDKWA-1Q-unpfn.js → wardleyDiagram-IVAIDKWA-DW68fX4l.js} +1 -1
  103. package/dist/web/assets/{xychartDiagram-4URNHDU4-zofaLTye.js → xychartDiagram-4URNHDU4-Cdk5RxaR.js} +1 -1
  104. package/dist/web/index.html +2 -2
  105. package/package.json +3 -3
  106. package/dist/web/assets/channel-AXdPKUi0.js +0 -1
  107. package/dist/web/assets/flowDiagram-IUGLLWBU-DosDBlJM.js +0 -1
  108. package/dist/web/assets/swimlanesDiagram-AYQRV4AK-Cmp6k7s_.js +0 -8
package/README.md CHANGED
@@ -29,9 +29,9 @@
29
29
 
30
30
  ## ✨ What you can do
31
31
 
32
- - 📱 **Follow tasks from any device.** Review changes, handle approvals, and answer questions on desktop or mobile. Tasks keep running when you close the page.
32
+ - 📱 **Follow tasks from any device.** Review changes, handle approvals, answer questions, and check [account quotas](docs/account-usage.md) on desktop or mobile. Tasks keep running when you close the page.
33
33
  - 🗂️ **Keep projects and conversations together.** Start without a project, track parallel tasks, and resume or fork your Codex CLI conversations.
34
- - 📖 **Read results while you chat.** Preview documents, diagrams, and code changes beside the conversation on desktop, or full-screen on mobile.
34
+ - 📖 **Read results while you chat.** Preview documents, diagrams, and code changes beside the conversation on desktop, or full-screen on mobile. Loaded history stays complete as new responses stream in.
35
35
  - 🔔 **Know when your attention is needed.** Enable browser notifications for completed work, questions, and approvals; keep the page open in a supported desktop browser.
36
36
 
37
37
  Find controls, shortcuts, and supported commands in the [usage guide · 中文](docs/usage.zh-CN.md) and [command guide](docs/slash-commands.md).
package/README.zh-CN.md CHANGED
@@ -29,9 +29,9 @@
29
29
 
30
30
  ## ✨ 你可以做什么
31
31
 
32
- - 📱 **随时随地跟进任务。** 用电脑或手机查看改动、处理审批、回答问题。关闭页面,任务仍会继续。
32
+ - 📱 **随时随地跟进任务。** 用电脑或手机查看改动、处理审批、回答问题和[账户额度](docs/account-usage.md)。关闭页面,任务仍会继续。
33
33
  - 🗂️ **集中管理项目与会话。** 无需项目即可开始任务,在一个工作台查看并行进展,接续或 Fork Codex CLI 会话。
34
- - 📖 **一边看产出,一边讨论。** 电脑上将文档、图表和代码变更与会话并排阅读,手机上使用全屏阅读器。
34
+ - 📖 **一边看产出,一边讨论。** 电脑上将文档、图表和代码变更与会话并排阅读,手机上使用全屏阅读器。新回复实时到达时,已加载的历史正文保持完整。
35
35
  - 🔔 **及时收到待办提醒。** 开启浏览器通知,在工作结束、需要回答或审批时收到提醒;需在支持通知的桌面浏览器中保持页面运行。
36
36
 
37
37
  操作、快捷键和命令用法见[使用指南](docs/usage.zh-CN.md)与[Slash 命令说明](docs/slash-commands.md)。
@@ -0,0 +1,132 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { z } from "zod";
3
+ import { CodexError, isRecord } from "./transport.js";
4
+ const nullable = (value) => value.nullish().transform((v) => v ?? null);
5
+ const text = nullable(z.string());
6
+ const number = nullable(z.number().finite());
7
+ const boolean = nullable(z.boolean());
8
+ const timestamp = nullable(z.number().finite().min(0).max(864e10));
9
+ const window = nullable(
10
+ z.object({
11
+ usedPercent: number,
12
+ windowDurationMins: nullable(z.number().finite().nonnegative()),
13
+ resetsAt: timestamp
14
+ })
15
+ );
16
+ const group = z.object({
17
+ limitId: text,
18
+ limitName: text,
19
+ normalModelSlug: text,
20
+ planType: text,
21
+ primary: window,
22
+ secondary: window,
23
+ credits: nullable(z.object({ hasCredits: boolean, unlimited: boolean, balance: text })),
24
+ individualLimit: nullable(
25
+ z.object({
26
+ limit: text,
27
+ used: text,
28
+ remainingPercent: number,
29
+ resetsAt: timestamp
30
+ })
31
+ ),
32
+ spendControlReached: boolean,
33
+ rateLimitReachedType: text
34
+ });
35
+ const response = z.object({
36
+ rateLimits: nullable(group),
37
+ rateLimitsByLimitId: nullable(z.record(z.string(), group)),
38
+ ordinaryUsageAllowed: boolean,
39
+ rateLimitResetCredits: nullable(
40
+ z.object({ availableCount: nullable(z.number().int().nonnegative()) })
41
+ )
42
+ });
43
+ function accountUsageValues(input) {
44
+ const value = response.parse(input);
45
+ const groups = Object.entries(value.rateLimitsByLimitId ?? {}).map(
46
+ ([id, entry]) => ({ ...entry, limitId: entry.limitId ?? id })
47
+ );
48
+ if (value.rateLimits && (!groups.length || value.rateLimits.limitId !== null && !groups.some((entry) => entry.limitId === value.rateLimits?.limitId))) {
49
+ groups.unshift(value.rateLimits);
50
+ }
51
+ return {
52
+ groups,
53
+ ordinaryUsageAllowed: value.ordinaryUsageAllowed,
54
+ resetCreditsAvailable: value.rateLimitResetCredits?.availableCount ?? null
55
+ };
56
+ }
57
+ class AccountUsageReader {
58
+ constructor(source) {
59
+ this.source = source;
60
+ }
61
+ source;
62
+ sequence = 0;
63
+ identity;
64
+ value = this.empty("offline");
65
+ snapshot() {
66
+ return structuredClone(this.value);
67
+ }
68
+ empty(status) {
69
+ return {
70
+ scope: randomUUID(),
71
+ status,
72
+ refreshing: false,
73
+ updatedAt: null,
74
+ groups: [],
75
+ ordinaryUsageAllowed: null,
76
+ resetCreditsAvailable: null
77
+ };
78
+ }
79
+ invalidate(status) {
80
+ this.sequence++;
81
+ this.identity = void 0;
82
+ this.value = this.empty(status);
83
+ this.source.changed();
84
+ }
85
+ async refresh() {
86
+ const sequence = ++this.sequence;
87
+ this.value = { ...this.value, refreshing: true };
88
+ this.source.changed();
89
+ try {
90
+ const result = await this.source.account();
91
+ if (sequence !== this.sequence) return this.snapshot();
92
+ if (!isRecord(result) || !Object.hasOwn(result, "account") || typeof result.requiresOpenaiAuth !== "boolean")
93
+ throw new Error("Invalid account response");
94
+ const identity = JSON.stringify(result.account);
95
+ if (this.identity !== void 0 && identity !== this.identity) {
96
+ this.value = this.empty("loading");
97
+ this.source.changed();
98
+ }
99
+ this.identity = identity;
100
+ this.source.authenticated(result.account !== null || !result.requiresOpenaiAuth);
101
+ if (!result.account) {
102
+ this.value = {
103
+ ...this.empty(result.requiresOpenaiAuth ? "signedOut" : "unsupported"),
104
+ scope: this.value.scope
105
+ };
106
+ } else if (result.account.type !== "chatgpt") {
107
+ this.value = { ...this.empty("unsupported"), scope: this.value.scope };
108
+ } else {
109
+ const values = accountUsageValues(await this.source.limits());
110
+ if (sequence !== this.sequence) return this.snapshot();
111
+ this.value = {
112
+ ...this.value,
113
+ ...values,
114
+ status: "ready",
115
+ refreshing: false,
116
+ updatedAt: Date.now()
117
+ };
118
+ }
119
+ } catch (error) {
120
+ if (sequence !== this.sequence) return this.snapshot();
121
+ const unsupported = error instanceof CodexError && (error.rpcCode === -32601 || error.code === "PROTOCOL_INCOMPATIBLE");
122
+ this.value = unsupported ? { ...this.empty("unsupported"), scope: this.value.scope } : { ...this.value, status: "error", refreshing: false };
123
+ }
124
+ if (sequence === this.sequence) this.source.changed();
125
+ return this.snapshot();
126
+ }
127
+ }
128
+ export {
129
+ AccountUsageReader,
130
+ accountUsageValues
131
+ };
132
+ //# sourceMappingURL=account-usage.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/codex/account-usage.ts"],
4
+ "sourcesContent": ["import { randomUUID } from 'node:crypto';\nimport { z } from 'zod';\nimport type { GetAccountResponse } from './generated/v2/GetAccountResponse.js';\nimport type { AccountUsage, UsageGroup } from '../shared/account-usage.js';\nimport { CodexError, isRecord } from './transport.js';\n\nconst nullable = <T extends z.ZodType>(value: T) => value.nullish().transform((v) => v ?? null);\nconst text = nullable(z.string());\nconst number = nullable(z.number().finite());\nconst boolean = nullable(z.boolean());\nconst timestamp = nullable(z.number().finite().min(0).max(8.64e12));\nconst window = nullable(\n z.object({\n usedPercent: number,\n windowDurationMins: nullable(z.number().finite().nonnegative()),\n resetsAt: timestamp,\n }),\n);\nconst group = z.object({\n limitId: text,\n limitName: text,\n normalModelSlug: text,\n planType: text,\n primary: window,\n secondary: window,\n credits: nullable(z.object({ hasCredits: boolean, unlimited: boolean, balance: text })),\n individualLimit: nullable(\n z.object({\n limit: text,\n used: text,\n remainingPercent: number,\n resetsAt: timestamp,\n }),\n ),\n spendControlReached: boolean,\n rateLimitReachedType: text,\n});\nconst response = z.object({\n rateLimits: nullable(group),\n rateLimitsByLimitId: nullable(z.record(z.string(), group)),\n ordinaryUsageAllowed: boolean,\n rateLimitResetCredits: nullable(\n z.object({ availableCount: nullable(z.number().int().nonnegative()) }),\n ),\n});\n\nexport function accountUsageValues(input: unknown) {\n const value = response.parse(input);\n const groups: UsageGroup[] = Object.entries(value.rateLimitsByLimitId ?? {}).map(\n ([id, entry]) => ({ ...entry, limitId: entry.limitId ?? id }),\n );\n // The legacy bucket mirrors a map entry. Keep it only when it adds a distinct bucket.\n if (\n value.rateLimits &&\n (!groups.length ||\n (value.rateLimits.limitId !== null &&\n !groups.some((entry) => entry.limitId === value.rateLimits?.limitId)))\n ) {\n groups.unshift(value.rateLimits);\n }\n return {\n groups,\n ordinaryUsageAllowed: value.ordinaryUsageAllowed,\n resetCreditsAvailable: value.rateLimitResetCredits?.availableCount ?? null,\n };\n}\n\n/** Owns freshness and identity boundaries. Sparse notifications trigger a full read. */\nexport class AccountUsageReader {\n private sequence = 0;\n private identity?: string;\n private value: AccountUsage = this.empty('offline');\n constructor(\n private readonly source: {\n account: () => Promise<GetAccountResponse>;\n limits: () => Promise<unknown>;\n changed: () => void;\n authenticated: (value: boolean) => void;\n },\n ) {}\n\n snapshot(): AccountUsage {\n return structuredClone(this.value);\n }\n\n private empty(status: AccountUsage['status']): AccountUsage {\n return {\n scope: randomUUID(),\n status,\n refreshing: false,\n updatedAt: null,\n groups: [],\n ordinaryUsageAllowed: null,\n resetCreditsAvailable: null,\n };\n }\n\n invalidate(status: 'offline' | 'loading'): void {\n this.sequence++;\n this.identity = undefined;\n this.value = this.empty(status);\n this.source.changed();\n }\n\n async refresh(): Promise<AccountUsage> {\n const sequence = ++this.sequence;\n this.value = { ...this.value, refreshing: true };\n this.source.changed();\n try {\n const result = await this.source.account();\n if (sequence !== this.sequence) return this.snapshot();\n if (\n !isRecord(result) ||\n !Object.hasOwn(result, 'account') ||\n typeof result.requiresOpenaiAuth !== 'boolean'\n )\n throw new Error('Invalid account response');\n const identity = JSON.stringify(result.account);\n if (this.identity !== undefined && identity !== this.identity) {\n this.value = this.empty('loading');\n this.source.changed();\n }\n this.identity = identity;\n this.source.authenticated(result.account !== null || !result.requiresOpenaiAuth);\n if (!result.account) {\n this.value = {\n ...this.empty(result.requiresOpenaiAuth ? 'signedOut' : 'unsupported'),\n scope: this.value.scope,\n };\n } else if (result.account.type !== 'chatgpt') {\n this.value = { ...this.empty('unsupported'), scope: this.value.scope };\n } else {\n const values = accountUsageValues(await this.source.limits());\n if (sequence !== this.sequence) return this.snapshot();\n this.value = {\n ...this.value,\n ...values,\n status: 'ready',\n refreshing: false,\n updatedAt: Date.now(),\n };\n }\n } catch (error) {\n if (sequence !== this.sequence) return this.snapshot();\n const unsupported =\n error instanceof CodexError &&\n (error.rpcCode === -32601 || error.code === 'PROTOCOL_INCOMPATIBLE');\n this.value = unsupported\n ? { ...this.empty('unsupported'), scope: this.value.scope }\n : { ...this.value, status: 'error', refreshing: false };\n }\n if (sequence === this.sequence) this.source.changed();\n return this.snapshot();\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -1,4 +1,5 @@
1
1
  import { CODEX_COMPATIBILITY } from "./compatibility.js";
2
+ import { AccountUsageReader } from "./account-usage.js";
2
3
  import { SHELL_TIMEOUT_MS, SHELL_OUTPUT_BYTES } from "../shared/shell.js";
3
4
  import { spawn, execFile } from "node:child_process";
4
5
  import { randomUUID } from "node:crypto";
@@ -71,7 +72,8 @@ const CAPABILITIES = [
71
72
  "config/batchWrite",
72
73
  "configRequirements/read",
73
74
  "experimentalFeature/list",
74
- "account/read"
75
+ "account/read",
76
+ "account/rateLimits/read"
75
77
  ];
76
78
  function permissionPathLabel(path) {
77
79
  if (path.type === "path") return path.path;
@@ -98,6 +100,21 @@ class CodexAdapter {
98
100
  this.options = options;
99
101
  }
100
102
  options;
103
+ accountUsage = new AccountUsageReader({
104
+ account: () => this.call("account/read", { refreshToken: false }),
105
+ limits: () => this.call("account/rateLimits/read", { excludeResetCreditDetails: true }),
106
+ changed: () => this.publishRuntime(),
107
+ authenticated: (value) => {
108
+ this.state.authenticated = value;
109
+ }
110
+ });
111
+ async refreshAccountUsage() {
112
+ if (!this.ready) {
113
+ this.accountUsage.invalidate("offline");
114
+ return this.accountUsage.snapshot();
115
+ }
116
+ return this.accountUsage.refresh();
117
+ }
101
118
  commandOutputs = /* @__PURE__ */ new Map();
102
119
  epoch = randomUUID();
103
120
  child;
@@ -137,7 +154,7 @@ class CodexAdapter {
137
154
  return this.state.ready;
138
155
  }
139
156
  diagnostics() {
140
- return structuredClone(this.state);
157
+ return { ...structuredClone(this.state), accountUsage: this.accountUsage.snapshot() };
141
158
  }
142
159
  onEvent(listener) {
143
160
  this.listeners.add(listener);
@@ -172,6 +189,7 @@ class CodexAdapter {
172
189
  error: void 0,
173
190
  capabilities: []
174
191
  };
192
+ this.accountUsage.invalidate("loading");
175
193
  this.publishRuntime();
176
194
  try {
177
195
  const env = {
@@ -235,6 +253,7 @@ class CodexAdapter {
235
253
  this.tokenUsage.clear();
236
254
  this.denials.clear();
237
255
  this.state.connection = "unavailable";
256
+ this.accountUsage.invalidate("offline");
238
257
  this.state.error = error.message;
239
258
  this.expireActions();
240
259
  this.publishRuntime();
@@ -319,6 +338,7 @@ class CodexAdapter {
319
338
  this.state.connection = "ready";
320
339
  this.state.capabilities = CAPABILITIES.filter((method) => !this.disabled.has(method));
321
340
  this.publishRuntime();
341
+ void this.refreshAccountUsage();
322
342
  } catch (error) {
323
343
  const failure = asCodexError(error);
324
344
  this.state.ready = false;
@@ -332,6 +352,7 @@ class CodexAdapter {
332
352
  }
333
353
  async stop() {
334
354
  this.stopped = true;
355
+ this.accountUsage.invalidate("offline");
335
356
  clearTimeout(this.restartTimer);
336
357
  this.restartTimer = void 0;
337
358
  await this.starting?.catch(() => {
@@ -959,6 +980,14 @@ class CodexAdapter {
959
980
  const event = message;
960
981
  const p = event.params;
961
982
  switch (event.method) {
983
+ case "account/updated":
984
+ this.state.authenticated = null;
985
+ this.accountUsage.invalidate("loading");
986
+ if (this.ready) void this.refreshAccountUsage();
987
+ break;
988
+ case "account/rateLimits/updated":
989
+ if (this.ready) void this.refreshAccountUsage();
990
+ break;
962
991
  case "command/exec/outputDelta": {
963
992
  const { processId, stream, deltaBase64, capReached } = event.params;
964
993
  this.commandOutputs.get(processId)?.(