@dexto/tui 1.6.12 → 1.6.13

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 (66) hide show
  1. package/dist/InkCLIRefactored.cjs +39 -29
  2. package/dist/InkCLIRefactored.d.ts.map +1 -1
  3. package/dist/InkCLIRefactored.js +39 -29
  4. package/dist/components/Footer.cjs +9 -2
  5. package/dist/components/Footer.d.ts +3 -2
  6. package/dist/components/Footer.d.ts.map +1 -1
  7. package/dist/components/Footer.js +17 -3
  8. package/dist/components/modes/AlternateBufferCLI.cjs +2 -1
  9. package/dist/components/modes/AlternateBufferCLI.d.ts.map +1 -1
  10. package/dist/components/modes/AlternateBufferCLI.js +2 -1
  11. package/dist/components/modes/StaticCLI.cjs +2 -1
  12. package/dist/components/modes/StaticCLI.d.ts.map +1 -1
  13. package/dist/components/modes/StaticCLI.js +2 -1
  14. package/dist/components/overlays/ChatGPTUsageCapOverlay.cjs +90 -0
  15. package/dist/components/overlays/ChatGPTUsageCapOverlay.d.ts +19 -0
  16. package/dist/components/overlays/ChatGPTUsageCapOverlay.d.ts.map +1 -0
  17. package/dist/components/overlays/ChatGPTUsageCapOverlay.js +70 -0
  18. package/dist/components/overlays/ModelSelectorRefactored.cjs +263 -38
  19. package/dist/components/overlays/ModelSelectorRefactored.d.ts.map +1 -1
  20. package/dist/components/overlays/ModelSelectorRefactored.js +267 -38
  21. package/dist/components/overlays/ReasoningOverlay.cjs +1 -1
  22. package/dist/components/overlays/ReasoningOverlay.js +1 -1
  23. package/dist/containers/OverlayContainer.cjs +104 -13
  24. package/dist/containers/OverlayContainer.d.ts.map +1 -1
  25. package/dist/containers/OverlayContainer.js +104 -13
  26. package/dist/hooks/useAgentEvents.cjs +33 -2
  27. package/dist/hooks/useAgentEvents.d.ts.map +1 -1
  28. package/dist/hooks/useAgentEvents.js +35 -3
  29. package/dist/hooks/useCLIState.cjs +1 -0
  30. package/dist/hooks/useCLIState.d.ts.map +1 -1
  31. package/dist/hooks/useCLIState.js +1 -0
  32. package/dist/interactive-commands/exit-stats.cjs +16 -0
  33. package/dist/interactive-commands/exit-stats.d.ts +4 -0
  34. package/dist/interactive-commands/exit-stats.d.ts.map +1 -1
  35. package/dist/interactive-commands/exit-stats.js +15 -0
  36. package/dist/interactive-commands/general-commands.cjs +13 -2
  37. package/dist/interactive-commands/general-commands.d.ts.map +1 -1
  38. package/dist/interactive-commands/general-commands.js +14 -3
  39. package/dist/interactive-commands/general-commands.test.cjs +152 -0
  40. package/dist/interactive-commands/general-commands.test.d.ts +2 -0
  41. package/dist/interactive-commands/general-commands.test.d.ts.map +1 -0
  42. package/dist/interactive-commands/general-commands.test.js +151 -0
  43. package/dist/services/processStream.test.cjs +1 -0
  44. package/dist/services/processStream.test.js +1 -0
  45. package/dist/state/initialState.cjs +1 -0
  46. package/dist/state/initialState.d.ts.map +1 -1
  47. package/dist/state/initialState.js +1 -0
  48. package/dist/state/types.d.ts +4 -2
  49. package/dist/state/types.d.ts.map +1 -1
  50. package/dist/utils/chatgpt-rate-limit.cjs +72 -0
  51. package/dist/utils/chatgpt-rate-limit.d.ts +11 -0
  52. package/dist/utils/chatgpt-rate-limit.d.ts.map +1 -0
  53. package/dist/utils/chatgpt-rate-limit.js +49 -0
  54. package/dist/utils/chatgpt-rate-limit.test.cjs +46 -0
  55. package/dist/utils/chatgpt-rate-limit.test.d.ts +2 -0
  56. package/dist/utils/chatgpt-rate-limit.test.d.ts.map +1 -0
  57. package/dist/utils/chatgpt-rate-limit.test.js +49 -0
  58. package/dist/utils/llm-provider-display.cjs +11 -1
  59. package/dist/utils/llm-provider-display.d.ts +2 -2
  60. package/dist/utils/llm-provider-display.d.ts.map +1 -1
  61. package/dist/utils/llm-provider-display.js +11 -1
  62. package/dist/utils/llm-provider-display.test.cjs +15 -0
  63. package/dist/utils/llm-provider-display.test.d.ts +2 -0
  64. package/dist/utils/llm-provider-display.test.d.ts.map +1 -0
  65. package/dist/utils/llm-provider-display.test.js +14 -0
  66. package/package.json +4 -4
@@ -234,42 +234,52 @@ async function startInkCliRefactored(agent, initialSessionId, options = {}) {
234
234
  for (const modelStat of exitStats.modelStats) {
235
235
  const modelLabel = `${modelStat.model} (${modelStat.messageCount} msgs)`;
236
236
  process.stdout.write(chalk.gray(` \u2022 ${modelLabel}`) + "\n");
237
- const tokens = modelStat.tokenUsage;
238
- process.stdout.write(
239
- chalk.gray(` Input tokens: ${tokens.inputTokens.toLocaleString()}`) + "\n"
240
- );
241
- process.stdout.write(
242
- chalk.gray(
243
- ` Output tokens: ${tokens.outputTokens.toLocaleString()}`
244
- ) + "\n"
245
- );
246
- process.stdout.write(
247
- chalk.gray(
248
- ` Reasoning tokens: ${tokens.reasoningTokens.toLocaleString()}`
249
- ) + "\n"
250
- );
251
- process.stdout.write(
252
- chalk.gray(
253
- ` Cache read tokens: ${tokens.cacheReadTokens.toLocaleString()}`
254
- ) + "\n"
255
- );
256
- process.stdout.write(
257
- chalk.gray(
258
- ` Cache write tokens: ${tokens.cacheWriteTokens.toLocaleString()}`
259
- ) + "\n"
260
- );
261
- process.stdout.write(
262
- chalk.gray(` Total tokens: ${tokens.totalTokens.toLocaleString()}`) + "\n"
263
- );
264
- if (modelStat.estimatedCost !== void 0) {
237
+ if (exitStats.tokenUsage) {
238
+ const tokens = modelStat.tokenUsage;
239
+ process.stdout.write(
240
+ chalk.gray(
241
+ ` Input tokens: ${tokens.inputTokens.toLocaleString()}`
242
+ ) + "\n"
243
+ );
244
+ process.stdout.write(
245
+ chalk.gray(
246
+ ` Output tokens: ${tokens.outputTokens.toLocaleString()}`
247
+ ) + "\n"
248
+ );
265
249
  process.stdout.write(
266
250
  chalk.gray(
267
- ` Cost: ${formatCost(modelStat.estimatedCost)}`
251
+ ` Reasoning tokens: ${tokens.reasoningTokens.toLocaleString()}`
268
252
  ) + "\n"
269
253
  );
254
+ process.stdout.write(
255
+ chalk.gray(
256
+ ` Cache read tokens: ${tokens.cacheReadTokens.toLocaleString()}`
257
+ ) + "\n"
258
+ );
259
+ process.stdout.write(
260
+ chalk.gray(
261
+ ` Cache write tokens: ${tokens.cacheWriteTokens.toLocaleString()}`
262
+ ) + "\n"
263
+ );
264
+ process.stdout.write(
265
+ chalk.gray(
266
+ ` Total tokens: ${tokens.totalTokens.toLocaleString()}`
267
+ ) + "\n"
268
+ );
269
+ if (modelStat.estimatedCost !== void 0) {
270
+ process.stdout.write(
271
+ chalk.gray(
272
+ ` Cost: ${formatCost(modelStat.estimatedCost)}`
273
+ ) + "\n"
274
+ );
275
+ }
270
276
  }
271
277
  }
272
278
  }
279
+ if (exitStats.usageNote) {
280
+ process.stdout.write(chalk.gray("\n Usage:") + "\n");
281
+ process.stdout.write(chalk.gray(` ${exitStats.usageNote}`) + "\n");
282
+ }
273
283
  if (exitStats.tokenUsage) {
274
284
  const {
275
285
  inputTokens,
@@ -1 +1 @@
1
- {"version":3,"file":"InkCLIRefactored.d.ts","sourceRoot":"","sources":["../src/InkCLIRefactored.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAK9C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAWpD,OAAO,KAAK,EAAe,wBAAwB,EAAE,MAAM,8BAA8B,CAAC;AAuB1F,UAAU,WAAW;IACjB,KAAK,EAAE,UAAU,CAAC;IAClB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,WAAW,EAAE,WAAW,CAAC;IACzB,YAAY,EAAE,wBAAwB,GAAG,IAAI,CAAC;IAC9C,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,wBAAwB,CAAC,EAAE,OAAO,CAAC;CACtC;AA2DD;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,EAC7B,KAAK,EACL,gBAAgB,EAChB,aAAa,EACb,WAAW,EACX,YAAY,EACZ,cAAc,EACd,wBAAgC,GACnC,EAAE,WAAW,2CAmBb;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC1B,kDAAkD;IAClD,UAAU,CAAC,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS,CAAC;IACpF,wEAAwE;IACxE,cAAc,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACrC,mDAAmD;IACnD,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IAC3C,iEAAiE;IACjE,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,wDAAwD;IACxD,iBAAiB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CAC3C;AAED;;GAEG;AACH,wBAAsB,qBAAqB,CACvC,KAAK,EAAE,UAAU,EACjB,gBAAgB,EAAE,MAAM,GAAG,IAAI,EAC/B,OAAO,GAAE,aAAkB,GAC5B,OAAO,CAAC,IAAI,CAAC,CA4Qf"}
1
+ {"version":3,"file":"InkCLIRefactored.d.ts","sourceRoot":"","sources":["../src/InkCLIRefactored.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAK9C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAWpD,OAAO,KAAK,EAAe,wBAAwB,EAAE,MAAM,8BAA8B,CAAC;AAuB1F,UAAU,WAAW;IACjB,KAAK,EAAE,UAAU,CAAC;IAClB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,WAAW,EAAE,WAAW,CAAC;IACzB,YAAY,EAAE,wBAAwB,GAAG,IAAI,CAAC;IAC9C,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,wBAAwB,CAAC,EAAE,OAAO,CAAC;CACtC;AA2DD;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,EAC7B,KAAK,EACL,gBAAgB,EAChB,aAAa,EACb,WAAW,EACX,YAAY,EACZ,cAAc,EACd,wBAAgC,GACnC,EAAE,WAAW,2CAmBb;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC1B,kDAAkD;IAClD,UAAU,CAAC,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS,CAAC;IACpF,wEAAwE;IACxE,cAAc,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACrC,mDAAmD;IACnD,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IAC3C,iEAAiE;IACjE,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,wDAAwD;IACxD,iBAAiB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CAC3C;AAED;;GAEG;AACH,wBAAsB,qBAAqB,CACvC,KAAK,EAAE,UAAU,EACjB,gBAAgB,EAAE,MAAM,GAAG,IAAI,EAC/B,OAAO,GAAE,aAAkB,GAC5B,OAAO,CAAC,IAAI,CAAC,CAqRf"}
@@ -205,42 +205,52 @@ async function startInkCliRefactored(agent, initialSessionId, options = {}) {
205
205
  for (const modelStat of exitStats.modelStats) {
206
206
  const modelLabel = `${modelStat.model} (${modelStat.messageCount} msgs)`;
207
207
  process.stdout.write(chalk.gray(` \u2022 ${modelLabel}`) + "\n");
208
- const tokens = modelStat.tokenUsage;
209
- process.stdout.write(
210
- chalk.gray(` Input tokens: ${tokens.inputTokens.toLocaleString()}`) + "\n"
211
- );
212
- process.stdout.write(
213
- chalk.gray(
214
- ` Output tokens: ${tokens.outputTokens.toLocaleString()}`
215
- ) + "\n"
216
- );
217
- process.stdout.write(
218
- chalk.gray(
219
- ` Reasoning tokens: ${tokens.reasoningTokens.toLocaleString()}`
220
- ) + "\n"
221
- );
222
- process.stdout.write(
223
- chalk.gray(
224
- ` Cache read tokens: ${tokens.cacheReadTokens.toLocaleString()}`
225
- ) + "\n"
226
- );
227
- process.stdout.write(
228
- chalk.gray(
229
- ` Cache write tokens: ${tokens.cacheWriteTokens.toLocaleString()}`
230
- ) + "\n"
231
- );
232
- process.stdout.write(
233
- chalk.gray(` Total tokens: ${tokens.totalTokens.toLocaleString()}`) + "\n"
234
- );
235
- if (modelStat.estimatedCost !== void 0) {
208
+ if (exitStats.tokenUsage) {
209
+ const tokens = modelStat.tokenUsage;
210
+ process.stdout.write(
211
+ chalk.gray(
212
+ ` Input tokens: ${tokens.inputTokens.toLocaleString()}`
213
+ ) + "\n"
214
+ );
215
+ process.stdout.write(
216
+ chalk.gray(
217
+ ` Output tokens: ${tokens.outputTokens.toLocaleString()}`
218
+ ) + "\n"
219
+ );
236
220
  process.stdout.write(
237
221
  chalk.gray(
238
- ` Cost: ${formatCost(modelStat.estimatedCost)}`
222
+ ` Reasoning tokens: ${tokens.reasoningTokens.toLocaleString()}`
239
223
  ) + "\n"
240
224
  );
225
+ process.stdout.write(
226
+ chalk.gray(
227
+ ` Cache read tokens: ${tokens.cacheReadTokens.toLocaleString()}`
228
+ ) + "\n"
229
+ );
230
+ process.stdout.write(
231
+ chalk.gray(
232
+ ` Cache write tokens: ${tokens.cacheWriteTokens.toLocaleString()}`
233
+ ) + "\n"
234
+ );
235
+ process.stdout.write(
236
+ chalk.gray(
237
+ ` Total tokens: ${tokens.totalTokens.toLocaleString()}`
238
+ ) + "\n"
239
+ );
240
+ if (modelStat.estimatedCost !== void 0) {
241
+ process.stdout.write(
242
+ chalk.gray(
243
+ ` Cost: ${formatCost(modelStat.estimatedCost)}`
244
+ ) + "\n"
245
+ );
246
+ }
241
247
  }
242
248
  }
243
249
  }
250
+ if (exitStats.usageNote) {
251
+ process.stdout.write(chalk.gray("\n Usage:") + "\n");
252
+ process.stdout.write(chalk.gray(` ${exitStats.usageNote}`) + "\n");
253
+ }
244
254
  if (exitStats.tokenUsage) {
245
255
  const {
246
256
  inputTokens,
@@ -37,6 +37,7 @@ var import_node_path = __toESM(require("node:path"), 1);
37
37
  var import_ink = require("ink");
38
38
  var import_core = require("@dexto/core");
39
39
  var import_llm_provider_display = require("../utils/llm-provider-display.js");
40
+ var import_chatgpt_rate_limit = require("../utils/chatgpt-rate-limit.js");
40
41
  function Footer({
41
42
  agent,
42
43
  sessionId,
@@ -46,7 +47,8 @@ function Footer({
46
47
  autoApproveEdits,
47
48
  bypassPermissions,
48
49
  planModeActive,
49
- isShellMode
50
+ isShellMode,
51
+ chatgptRateLimitStatus
50
52
  }) {
51
53
  const displayPath = cwd ? import_node_path.default.basename(cwd) || cwd : "";
52
54
  const displayModelName = (0, import_core.getModelDisplayName)(modelName);
@@ -54,10 +56,14 @@ function Footer({
54
56
  const [, setLlmTick] = (0, import_react.useState)(0);
55
57
  const llmConfig = sessionId ? agent.getCurrentLLMConfig(sessionId) : null;
56
58
  const provider = llmConfig?.provider ?? null;
57
- const providerLabel = provider ? (0, import_llm_provider_display.getLLMProviderDisplayName)(provider) : null;
59
+ const providerLabel = provider ? (0, import_llm_provider_display.getLLMProviderDisplayName)(provider, llmConfig?.baseURL) : null;
58
60
  const reasoningProfile = provider && llmConfig ? (0, import_core.getReasoningProfile)(provider, llmConfig.model) : null;
59
61
  const reasoningVariant = llmConfig?.reasoning?.variant ?? reasoningProfile?.defaultVariant ?? void 0;
60
62
  const showReasoningVariant = reasoningProfile?.capable === true && typeof reasoningVariant === "string";
63
+ const isChatGPTLogin = provider === "openai-compatible" && (0, import_core.parseCodexBaseURL)(llmConfig?.baseURL)?.authMode === "chatgpt";
64
+ const showChatGPTRateLimitHint = isChatGPTLogin && (0, import_chatgpt_rate_limit.shouldShowChatGPTRateLimitHint)(chatgptRateLimitStatus);
65
+ const chatGPTRateLimitHint = showChatGPTRateLimitHint && chatgptRateLimitStatus ? (0, import_chatgpt_rate_limit.getChatGPTRateLimitHint)(chatgptRateLimitStatus) : null;
66
+ const chatGPTRateLimitColor = chatgptRateLimitStatus?.exceeded ? "redBright" : (chatgptRateLimitStatus?.usedPercent ?? 0) >= 90 ? "yellowBright" : "yellow";
61
67
  (0, import_react.useEffect)(() => {
62
68
  if (!sessionId) {
63
69
  setContextLeft(null);
@@ -141,6 +147,7 @@ function Footer({
141
147
  contextLeft.percentLeft,
142
148
  "% context left"
143
149
  ] }) }),
150
+ chatGPTRateLimitHint && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ink.Box, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ink.Text, { color: chatGPTRateLimitColor, children: chatGPTRateLimitHint }) }),
144
151
  isShellMode && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_ink.Box, { children: [
145
152
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ink.Text, { color: "yellow", bold: true, children: "!" }),
146
153
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ink.Text, { color: "gray", children: " for shell mode" })
@@ -2,7 +2,7 @@
2
2
  * Footer Component
3
3
  * Status line at the bottom showing CWD, branch, and model info.
4
4
  */
5
- import { type DextoAgent } from '@dexto/core';
5
+ import { type CodexRateLimitSnapshot, type DextoAgent } from '@dexto/core';
6
6
  interface FooterProps {
7
7
  agent: DextoAgent;
8
8
  sessionId: string | null;
@@ -14,10 +14,11 @@ interface FooterProps {
14
14
  planModeActive?: boolean;
15
15
  /** Whether user is in shell command mode (input starts with !) */
16
16
  isShellMode?: boolean;
17
+ chatgptRateLimitStatus?: CodexRateLimitSnapshot | null;
17
18
  }
18
19
  /**
19
20
  * Pure presentational component for footer status line
20
21
  */
21
- export declare function Footer({ agent, sessionId, modelName, cwd, branchName, autoApproveEdits, bypassPermissions, planModeActive, isShellMode, }: FooterProps): import("react/jsx-runtime").JSX.Element;
22
+ export declare function Footer({ agent, sessionId, modelName, cwd, branchName, autoApproveEdits, bypassPermissions, planModeActive, isShellMode, chatgptRateLimitStatus, }: FooterProps): import("react/jsx-runtime").JSX.Element;
22
23
  export {};
23
24
  //# sourceMappingURL=Footer.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Footer.d.ts","sourceRoot":"","sources":["../../src/components/Footer.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,EAA4C,KAAK,UAAU,EAAE,MAAM,aAAa,CAAC;AAGxF,UAAU,WAAW;IACjB,KAAK,EAAE,UAAU,CAAC;IAClB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,kEAAkE;IAClE,WAAW,CAAC,EAAE,OAAO,CAAC;CACzB;AAED;;GAEG;AACH,wBAAgB,MAAM,CAAC,EACnB,KAAK,EACL,SAAS,EACT,SAAS,EACT,GAAG,EACH,UAAU,EACV,gBAAgB,EAChB,iBAAiB,EACjB,cAAc,EACd,WAAW,GACd,EAAE,WAAW,2CAqJb"}
1
+ {"version":3,"file":"Footer.d.ts","sourceRoot":"","sources":["../../src/components/Footer.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,EAIH,KAAK,sBAAsB,EAC3B,KAAK,UAAU,EAClB,MAAM,aAAa,CAAC;AAOrB,UAAU,WAAW;IACjB,KAAK,EAAE,UAAU,CAAC;IAClB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,kEAAkE;IAClE,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,sBAAsB,CAAC,EAAE,sBAAsB,GAAG,IAAI,CAAC;CAC1D;AAED;;GAEG;AACH,wBAAgB,MAAM,CAAC,EACnB,KAAK,EACL,SAAS,EACT,SAAS,EACT,GAAG,EACH,UAAU,EACV,gBAAgB,EAChB,iBAAiB,EACjB,cAAc,EACd,WAAW,EACX,sBAAsB,GACzB,EAAE,WAAW,2CAyKb"}
@@ -2,8 +2,16 @@ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
2
2
  import { useEffect, useState } from "react";
3
3
  import path from "node:path";
4
4
  import { Box, Text } from "ink";
5
- import { getModelDisplayName, getReasoningProfile } from "@dexto/core";
5
+ import {
6
+ getModelDisplayName,
7
+ getReasoningProfile,
8
+ parseCodexBaseURL
9
+ } from "@dexto/core";
6
10
  import { getLLMProviderDisplayName } from "../utils/llm-provider-display.js";
11
+ import {
12
+ getChatGPTRateLimitHint,
13
+ shouldShowChatGPTRateLimitHint
14
+ } from "../utils/chatgpt-rate-limit.js";
7
15
  function Footer({
8
16
  agent,
9
17
  sessionId,
@@ -13,7 +21,8 @@ function Footer({
13
21
  autoApproveEdits,
14
22
  bypassPermissions,
15
23
  planModeActive,
16
- isShellMode
24
+ isShellMode,
25
+ chatgptRateLimitStatus
17
26
  }) {
18
27
  const displayPath = cwd ? path.basename(cwd) || cwd : "";
19
28
  const displayModelName = getModelDisplayName(modelName);
@@ -21,10 +30,14 @@ function Footer({
21
30
  const [, setLlmTick] = useState(0);
22
31
  const llmConfig = sessionId ? agent.getCurrentLLMConfig(sessionId) : null;
23
32
  const provider = llmConfig?.provider ?? null;
24
- const providerLabel = provider ? getLLMProviderDisplayName(provider) : null;
33
+ const providerLabel = provider ? getLLMProviderDisplayName(provider, llmConfig?.baseURL) : null;
25
34
  const reasoningProfile = provider && llmConfig ? getReasoningProfile(provider, llmConfig.model) : null;
26
35
  const reasoningVariant = llmConfig?.reasoning?.variant ?? reasoningProfile?.defaultVariant ?? void 0;
27
36
  const showReasoningVariant = reasoningProfile?.capable === true && typeof reasoningVariant === "string";
37
+ const isChatGPTLogin = provider === "openai-compatible" && parseCodexBaseURL(llmConfig?.baseURL)?.authMode === "chatgpt";
38
+ const showChatGPTRateLimitHint = isChatGPTLogin && shouldShowChatGPTRateLimitHint(chatgptRateLimitStatus);
39
+ const chatGPTRateLimitHint = showChatGPTRateLimitHint && chatgptRateLimitStatus ? getChatGPTRateLimitHint(chatgptRateLimitStatus) : null;
40
+ const chatGPTRateLimitColor = chatgptRateLimitStatus?.exceeded ? "redBright" : (chatgptRateLimitStatus?.usedPercent ?? 0) >= 90 ? "yellowBright" : "yellow";
28
41
  useEffect(() => {
29
42
  if (!sessionId) {
30
43
  setContextLeft(null);
@@ -108,6 +121,7 @@ function Footer({
108
121
  contextLeft.percentLeft,
109
122
  "% context left"
110
123
  ] }) }),
124
+ chatGPTRateLimitHint && /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsx(Text, { color: chatGPTRateLimitColor, children: chatGPTRateLimitHint }) }),
111
125
  isShellMode && /* @__PURE__ */ jsxs(Box, { children: [
112
126
  /* @__PURE__ */ jsx(Text, { color: "yellow", bold: true, children: "!" }),
113
127
  /* @__PURE__ */ jsx(Text, { color: "gray", children: " for shell mode" })
@@ -341,7 +341,8 @@ function AlternateBufferCLI({
341
341
  autoApproveEdits: ui.autoApproveEdits,
342
342
  bypassPermissions: ui.bypassPermissions,
343
343
  planModeActive: ui.planModeActive,
344
- isShellMode: buffer.text.startsWith("!")
344
+ isShellMode: buffer.text.startsWith("!"),
345
+ chatgptRateLimitStatus: ui.chatgptRateLimitStatus
345
346
  }
346
347
  ),
347
348
  !hideChrome && ui.historySearch.isActive && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
@@ -1 +1 @@
1
- {"version":3,"file":"AlternateBufferCLI.d.ts","sourceRoot":"","sources":["../../../src/components/modes/AlternateBufferCLI.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAG9C,OAAO,KAAK,EAAW,WAAW,EAAE,MAAM,sBAAsB,CAAC;AA+BjE,UAAU,uBAAuB;IAC7B,KAAK,EAAE,UAAU,CAAC;IAClB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,WAAW,EAAE,WAAW,CAAC;IACzB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,wBAAwB,CAAC,EAAE,OAAO,CAAC;IACnC,2EAA2E;IAC3E,kBAAkB,CAAC,EAAE,MAAM,IAAI,CAAC;IAChC,6DAA6D;IAC7D,YAAY,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,wBAAgB,kBAAkB,CAAC,EAC/B,KAAK,EACL,gBAAgB,EAChB,aAAa,EACb,WAAW,EACX,cAAc,EACd,wBAAgC,EAChC,kBAAkB,EAClB,YAAmB,GACtB,EAAE,uBAAuB,2CAiXzB"}
1
+ {"version":3,"file":"AlternateBufferCLI.d.ts","sourceRoot":"","sources":["../../../src/components/modes/AlternateBufferCLI.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAG9C,OAAO,KAAK,EAAW,WAAW,EAAE,MAAM,sBAAsB,CAAC;AA+BjE,UAAU,uBAAuB;IAC7B,KAAK,EAAE,UAAU,CAAC;IAClB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,WAAW,EAAE,WAAW,CAAC;IACzB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,wBAAwB,CAAC,EAAE,OAAO,CAAC;IACnC,2EAA2E;IAC3E,kBAAkB,CAAC,EAAE,MAAM,IAAI,CAAC;IAChC,6DAA6D;IAC7D,YAAY,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,wBAAgB,kBAAkB,CAAC,EAC/B,KAAK,EACL,gBAAgB,EAChB,aAAa,EACb,WAAW,EACX,cAAc,EACd,wBAAgC,EAChC,kBAAkB,EAClB,YAAmB,GACtB,EAAE,uBAAuB,2CAkXzB"}
@@ -321,7 +321,8 @@ function AlternateBufferCLI({
321
321
  autoApproveEdits: ui.autoApproveEdits,
322
322
  bypassPermissions: ui.bypassPermissions,
323
323
  planModeActive: ui.planModeActive,
324
- isShellMode: buffer.text.startsWith("!")
324
+ isShellMode: buffer.text.startsWith("!"),
325
+ chatgptRateLimitStatus: ui.chatgptRateLimitStatus
325
326
  }
326
327
  ),
327
328
  !hideChrome && ui.historySearch.isActive && /* @__PURE__ */ jsx(
@@ -284,7 +284,8 @@ function StaticCLI({
284
284
  autoApproveEdits: ui.autoApproveEdits,
285
285
  bypassPermissions: ui.bypassPermissions,
286
286
  planModeActive: ui.planModeActive,
287
- isShellMode: buffer.text.startsWith("!")
287
+ isShellMode: buffer.text.startsWith("!"),
288
+ chatgptRateLimitStatus: ui.chatgptRateLimitStatus
288
289
  }
289
290
  ),
290
291
  !(0, import_overlayPresentation.shouldHideStatusChrome)(ui.activeOverlay, approval) && ui.historySearch.isActive && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
@@ -1 +1 @@
1
- {"version":3,"file":"StaticCLI.d.ts","sourceRoot":"","sources":["../../../src/components/modes/StaticCLI.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAIH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAM9C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAsBxD,UAAU,cAAc;IACpB,KAAK,EAAE,UAAU,CAAC;IAClB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,WAAW,EAAE,WAAW,CAAC;IACzB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,wBAAwB,CAAC,EAAE,OAAO,CAAC;IACnC,6DAA6D;IAC7D,YAAY,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,wBAAgB,SAAS,CAAC,EACtB,KAAK,EACL,gBAAgB,EAChB,aAAa,EACb,WAAW,EACX,cAAc,EACd,wBAAgC,EAChC,YAAmB,GACtB,EAAE,cAAc,2CA2RhB"}
1
+ {"version":3,"file":"StaticCLI.d.ts","sourceRoot":"","sources":["../../../src/components/modes/StaticCLI.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAIH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAM9C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAsBxD,UAAU,cAAc;IACpB,KAAK,EAAE,UAAU,CAAC;IAClB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,WAAW,EAAE,WAAW,CAAC;IACzB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,wBAAwB,CAAC,EAAE,OAAO,CAAC;IACnC,6DAA6D;IAC7D,YAAY,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,wBAAgB,SAAS,CAAC,EACtB,KAAK,EACL,gBAAgB,EAChB,aAAa,EACb,WAAW,EACX,cAAc,EACd,wBAAgC,EAChC,YAAmB,GACtB,EAAE,cAAc,2CA4RhB"}
@@ -261,7 +261,8 @@ function StaticCLI({
261
261
  autoApproveEdits: ui.autoApproveEdits,
262
262
  bypassPermissions: ui.bypassPermissions,
263
263
  planModeActive: ui.planModeActive,
264
- isShellMode: buffer.text.startsWith("!")
264
+ isShellMode: buffer.text.startsWith("!"),
265
+ chatgptRateLimitStatus: ui.chatgptRateLimitStatus
265
266
  }
266
267
  ),
267
268
  !shouldHideStatusChrome(ui.activeOverlay, approval) && ui.historySearch.isActive && /* @__PURE__ */ jsx(
@@ -0,0 +1,90 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var ChatGPTUsageCapOverlay_exports = {};
20
+ __export(ChatGPTUsageCapOverlay_exports, {
21
+ default: () => ChatGPTUsageCapOverlay_default
22
+ });
23
+ module.exports = __toCommonJS(ChatGPTUsageCapOverlay_exports);
24
+ var import_jsx_runtime = require("react/jsx-runtime");
25
+ var import_react = require("react");
26
+ var import_ink = require("ink");
27
+ var import_chatgpt_rate_limit = require("../../utils/chatgpt-rate-limit.js");
28
+ const ChatGPTUsageCapOverlay = (0, import_react.forwardRef)(function ChatGPTUsageCapOverlay2({
29
+ isVisible,
30
+ currentModelDisplayName,
31
+ fallbackModelDisplayName,
32
+ usedDefaultFallback,
33
+ apiKeyConfigured,
34
+ status,
35
+ onConfirm,
36
+ onClose
37
+ }, ref) {
38
+ (0, import_react.useImperativeHandle)(
39
+ ref,
40
+ () => ({
41
+ handleInput: (_input, key) => {
42
+ if (!isVisible) {
43
+ return false;
44
+ }
45
+ if (key.escape) {
46
+ onClose();
47
+ return true;
48
+ }
49
+ if (key.return) {
50
+ onConfirm();
51
+ return true;
52
+ }
53
+ return true;
54
+ }
55
+ }),
56
+ [isVisible, onClose, onConfirm]
57
+ );
58
+ if (!isVisible) {
59
+ return null;
60
+ }
61
+ const actionLabel = apiKeyConfigured ? `Switch this session to ${fallbackModelDisplayName} via OpenAI API key` : `Add an OpenAI API key and switch this session to ${fallbackModelDisplayName}`;
62
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
63
+ import_ink.Box,
64
+ {
65
+ flexDirection: "column",
66
+ borderStyle: "round",
67
+ borderColor: "yellow",
68
+ paddingX: 1,
69
+ marginTop: 1,
70
+ children: [
71
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ink.Box, { marginBottom: 1, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ink.Text, { bold: true, color: "yellowBright", children: "ChatGPT usage cap reached" }) }),
72
+ status && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ink.Box, { marginBottom: 1, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ink.Text, { color: "gray", children: (0, import_chatgpt_rate_limit.getChatGPTRateLimitHint)(status) }) }),
73
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_ink.Box, { flexDirection: "column", marginBottom: 1, children: [
74
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_ink.Text, { color: "gray", children: [
75
+ currentModelDisplayName,
76
+ " is currently running through ChatGPT Login."
77
+ ] }),
78
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ink.Text, { children: actionLabel })
79
+ ] }),
80
+ usedDefaultFallback && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ink.Box, { marginBottom: 1, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_ink.Text, { color: "gray", children: [
81
+ "The current ChatGPT model is not available through the OpenAI API path, so Dexto will use ",
82
+ fallbackModelDisplayName,
83
+ " for this session."
84
+ ] }) }),
85
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ink.Box, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ink.Text, { color: "gray", children: "Enter to continue \u2022 Esc to dismiss" }) })
86
+ ]
87
+ }
88
+ );
89
+ });
90
+ var ChatGPTUsageCapOverlay_default = ChatGPTUsageCapOverlay;
@@ -0,0 +1,19 @@
1
+ import React from 'react';
2
+ import type { CodexRateLimitSnapshot } from '@dexto/core';
3
+ import type { Key } from '../../hooks/useInputOrchestrator.js';
4
+ export interface ChatGPTUsageCapOverlayProps {
5
+ isVisible: boolean;
6
+ currentModelDisplayName: string;
7
+ fallbackModelDisplayName: string;
8
+ usedDefaultFallback: boolean;
9
+ apiKeyConfigured: boolean;
10
+ status: CodexRateLimitSnapshot | null;
11
+ onConfirm: () => void;
12
+ onClose: () => void;
13
+ }
14
+ export interface ChatGPTUsageCapOverlayHandle {
15
+ handleInput: (input: string, key: Key) => boolean;
16
+ }
17
+ declare const ChatGPTUsageCapOverlay: React.ForwardRefExoticComponent<ChatGPTUsageCapOverlayProps & React.RefAttributes<ChatGPTUsageCapOverlayHandle>>;
18
+ export default ChatGPTUsageCapOverlay;
19
+ //# sourceMappingURL=ChatGPTUsageCapOverlay.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ChatGPTUsageCapOverlay.d.ts","sourceRoot":"","sources":["../../../src/components/overlays/ChatGPTUsageCapOverlay.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA0C,MAAM,OAAO,CAAC;AAE/D,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAC1D,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,qCAAqC,CAAC;AAG/D,MAAM,WAAW,2BAA2B;IACxC,SAAS,EAAE,OAAO,CAAC;IACnB,uBAAuB,EAAE,MAAM,CAAC;IAChC,wBAAwB,EAAE,MAAM,CAAC;IACjC,mBAAmB,EAAE,OAAO,CAAC;IAC7B,gBAAgB,EAAE,OAAO,CAAC;IAC1B,MAAM,EAAE,sBAAsB,GAAG,IAAI,CAAC;IACtC,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB,OAAO,EAAE,MAAM,IAAI,CAAC;CACvB;AAED,MAAM,WAAW,4BAA4B;IACzC,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC;CACrD;AAED,QAAA,MAAM,sBAAsB,kHAyF1B,CAAC;AAEH,eAAe,sBAAsB,CAAC"}
@@ -0,0 +1,70 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { forwardRef, useImperativeHandle } from "react";
3
+ import { Box, Text } from "ink";
4
+ import { getChatGPTRateLimitHint } from "../../utils/chatgpt-rate-limit.js";
5
+ const ChatGPTUsageCapOverlay = forwardRef(function ChatGPTUsageCapOverlay2({
6
+ isVisible,
7
+ currentModelDisplayName,
8
+ fallbackModelDisplayName,
9
+ usedDefaultFallback,
10
+ apiKeyConfigured,
11
+ status,
12
+ onConfirm,
13
+ onClose
14
+ }, ref) {
15
+ useImperativeHandle(
16
+ ref,
17
+ () => ({
18
+ handleInput: (_input, key) => {
19
+ if (!isVisible) {
20
+ return false;
21
+ }
22
+ if (key.escape) {
23
+ onClose();
24
+ return true;
25
+ }
26
+ if (key.return) {
27
+ onConfirm();
28
+ return true;
29
+ }
30
+ return true;
31
+ }
32
+ }),
33
+ [isVisible, onClose, onConfirm]
34
+ );
35
+ if (!isVisible) {
36
+ return null;
37
+ }
38
+ const actionLabel = apiKeyConfigured ? `Switch this session to ${fallbackModelDisplayName} via OpenAI API key` : `Add an OpenAI API key and switch this session to ${fallbackModelDisplayName}`;
39
+ return /* @__PURE__ */ jsxs(
40
+ Box,
41
+ {
42
+ flexDirection: "column",
43
+ borderStyle: "round",
44
+ borderColor: "yellow",
45
+ paddingX: 1,
46
+ marginTop: 1,
47
+ children: [
48
+ /* @__PURE__ */ jsx(Box, { marginBottom: 1, children: /* @__PURE__ */ jsx(Text, { bold: true, color: "yellowBright", children: "ChatGPT usage cap reached" }) }),
49
+ status && /* @__PURE__ */ jsx(Box, { marginBottom: 1, children: /* @__PURE__ */ jsx(Text, { color: "gray", children: getChatGPTRateLimitHint(status) }) }),
50
+ /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [
51
+ /* @__PURE__ */ jsxs(Text, { color: "gray", children: [
52
+ currentModelDisplayName,
53
+ " is currently running through ChatGPT Login."
54
+ ] }),
55
+ /* @__PURE__ */ jsx(Text, { children: actionLabel })
56
+ ] }),
57
+ usedDefaultFallback && /* @__PURE__ */ jsx(Box, { marginBottom: 1, children: /* @__PURE__ */ jsxs(Text, { color: "gray", children: [
58
+ "The current ChatGPT model is not available through the OpenAI API path, so Dexto will use ",
59
+ fallbackModelDisplayName,
60
+ " for this session."
61
+ ] }) }),
62
+ /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsx(Text, { color: "gray", children: "Enter to continue \u2022 Esc to dismiss" }) })
63
+ ]
64
+ }
65
+ );
66
+ });
67
+ var ChatGPTUsageCapOverlay_default = ChatGPTUsageCapOverlay;
68
+ export {
69
+ ChatGPTUsageCapOverlay_default as default
70
+ };