iosm-cli 0.2.0 → 0.2.2

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 (111) hide show
  1. package/CHANGELOG.md +53 -0
  2. package/README.md +64 -52
  3. package/dist/core/agent-teams.d.ts.map +1 -1
  4. package/dist/core/agent-teams.js +38 -11
  5. package/dist/core/agent-teams.js.map +1 -1
  6. package/dist/core/failure-retrospective.d.ts +12 -0
  7. package/dist/core/failure-retrospective.d.ts.map +1 -0
  8. package/dist/core/failure-retrospective.js +115 -0
  9. package/dist/core/failure-retrospective.js.map +1 -0
  10. package/dist/core/model-registry.d.ts.map +1 -1
  11. package/dist/core/model-registry.js +2 -3
  12. package/dist/core/model-registry.js.map +1 -1
  13. package/dist/core/models-dev-provider-catalog.d.ts +30 -0
  14. package/dist/core/models-dev-provider-catalog.d.ts.map +1 -0
  15. package/dist/core/models-dev-provider-catalog.js +118 -0
  16. package/dist/core/models-dev-provider-catalog.js.map +1 -0
  17. package/dist/core/models-dev-providers.d.ts +12 -0
  18. package/dist/core/models-dev-providers.d.ts.map +1 -0
  19. package/dist/core/models-dev-providers.js +736 -0
  20. package/dist/core/models-dev-providers.js.map +1 -0
  21. package/dist/core/project-index/index.d.ts +17 -0
  22. package/dist/core/project-index/index.d.ts.map +1 -0
  23. package/dist/core/project-index/index.js +323 -0
  24. package/dist/core/project-index/index.js.map +1 -0
  25. package/dist/core/project-index/types.d.ts +34 -0
  26. package/dist/core/project-index/types.d.ts.map +1 -0
  27. package/dist/core/project-index/types.js +2 -0
  28. package/dist/core/project-index/types.js.map +1 -0
  29. package/dist/core/sdk.d.ts.map +1 -1
  30. package/dist/core/sdk.js +8 -0
  31. package/dist/core/sdk.js.map +1 -1
  32. package/dist/core/shared-memory.d.ts +46 -0
  33. package/dist/core/shared-memory.d.ts.map +1 -0
  34. package/dist/core/shared-memory.js +253 -0
  35. package/dist/core/shared-memory.js.map +1 -0
  36. package/dist/core/slash-commands.d.ts.map +1 -1
  37. package/dist/core/slash-commands.js +5 -1
  38. package/dist/core/slash-commands.js.map +1 -1
  39. package/dist/core/subagents.js +1 -1
  40. package/dist/core/subagents.js.map +1 -1
  41. package/dist/core/swarm/gates.d.ts +9 -0
  42. package/dist/core/swarm/gates.d.ts.map +1 -0
  43. package/dist/core/swarm/gates.js +65 -0
  44. package/dist/core/swarm/gates.js.map +1 -0
  45. package/dist/core/swarm/index.d.ts +9 -0
  46. package/dist/core/swarm/index.d.ts.map +1 -0
  47. package/dist/core/swarm/index.js +9 -0
  48. package/dist/core/swarm/index.js.map +1 -0
  49. package/dist/core/swarm/locks.d.ts +21 -0
  50. package/dist/core/swarm/locks.d.ts.map +1 -0
  51. package/dist/core/swarm/locks.js +93 -0
  52. package/dist/core/swarm/locks.js.map +1 -0
  53. package/dist/core/swarm/planner.d.ts +16 -0
  54. package/dist/core/swarm/planner.d.ts.map +1 -0
  55. package/dist/core/swarm/planner.js +137 -0
  56. package/dist/core/swarm/planner.js.map +1 -0
  57. package/dist/core/swarm/retry.d.ts +16 -0
  58. package/dist/core/swarm/retry.d.ts.map +1 -0
  59. package/dist/core/swarm/retry.js +32 -0
  60. package/dist/core/swarm/retry.js.map +1 -0
  61. package/dist/core/swarm/scheduler.d.ts +48 -0
  62. package/dist/core/swarm/scheduler.d.ts.map +1 -0
  63. package/dist/core/swarm/scheduler.js +554 -0
  64. package/dist/core/swarm/scheduler.js.map +1 -0
  65. package/dist/core/swarm/spawn.d.ts +16 -0
  66. package/dist/core/swarm/spawn.d.ts.map +1 -0
  67. package/dist/core/swarm/spawn.js +42 -0
  68. package/dist/core/swarm/spawn.js.map +1 -0
  69. package/dist/core/swarm/state-store.d.ts +35 -0
  70. package/dist/core/swarm/state-store.d.ts.map +1 -0
  71. package/dist/core/swarm/state-store.js +106 -0
  72. package/dist/core/swarm/state-store.js.map +1 -0
  73. package/dist/core/swarm/types.d.ts +116 -0
  74. package/dist/core/swarm/types.d.ts.map +1 -0
  75. package/dist/core/swarm/types.js +2 -0
  76. package/dist/core/swarm/types.js.map +1 -0
  77. package/dist/core/system-prompt.d.ts.map +1 -1
  78. package/dist/core/system-prompt.js +3 -2
  79. package/dist/core/system-prompt.js.map +1 -1
  80. package/dist/core/tools/shared-memory.d.ts +23 -0
  81. package/dist/core/tools/shared-memory.d.ts.map +1 -0
  82. package/dist/core/tools/shared-memory.js +134 -0
  83. package/dist/core/tools/shared-memory.js.map +1 -0
  84. package/dist/core/tools/task.d.ts +8 -1
  85. package/dist/core/tools/task.d.ts.map +1 -1
  86. package/dist/core/tools/task.js +664 -123
  87. package/dist/core/tools/task.js.map +1 -1
  88. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  89. package/dist/modes/interactive/components/footer.js +3 -11
  90. package/dist/modes/interactive/components/footer.js.map +1 -1
  91. package/dist/modes/interactive/components/login-dialog.d.ts +1 -0
  92. package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
  93. package/dist/modes/interactive/components/login-dialog.js +27 -4
  94. package/dist/modes/interactive/components/login-dialog.js.map +1 -1
  95. package/dist/modes/interactive/components/oauth-selector.d.ts +13 -1
  96. package/dist/modes/interactive/components/oauth-selector.d.ts.map +1 -1
  97. package/dist/modes/interactive/components/oauth-selector.js +89 -27
  98. package/dist/modes/interactive/components/oauth-selector.js.map +1 -1
  99. package/dist/modes/interactive/components/subagent-message.d.ts.map +1 -1
  100. package/dist/modes/interactive/components/subagent-message.js +14 -0
  101. package/dist/modes/interactive/components/subagent-message.js.map +1 -1
  102. package/dist/modes/interactive/interactive-mode.d.ts +50 -0
  103. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  104. package/dist/modes/interactive/interactive-mode.js +1594 -51
  105. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  106. package/docs/cli-reference.md +11 -1
  107. package/docs/configuration.md +4 -1
  108. package/docs/getting-started.md +2 -2
  109. package/docs/interactive-mode.md +43 -4
  110. package/docs/orchestration-and-subagents.md +96 -169
  111. package/package.json +5 -4
@@ -203,8 +203,8 @@ export class FooterComponent {
203
203
  }
204
204
  usageParts.push(contextPercentStr);
205
205
  let statsLeft = [...statusParts, ...usageParts].join(separator);
206
- // Add model name on the right side, plus thinking level if model supports it
207
- const modelName = state.model?.id || "no-model";
206
+ // Add provider/model on the right side, plus thinking level if model supports it
207
+ const modelName = state.model ? `${state.model.provider}/${state.model.id}` : "no-model";
208
208
  let statsLeftWidth = visibleWidth(statsLeft);
209
209
  // If statsLeft is too wide, truncate it
210
210
  if (statsLeftWidth > width) {
@@ -222,15 +222,7 @@ export class FooterComponent {
222
222
  ? `${theme.fg("accent", modelName)}${theme.fg("muted", " • thinking off")}`
223
223
  : `${theme.fg("accent", modelName)}${theme.fg("muted", ` • ${thinkingLevel}`)}`;
224
224
  }
225
- // Prepend the provider in parentheses if there are multiple providers and there's enough room
226
- let rightSide = rightSideWithoutProvider;
227
- if (this.footerData.getAvailableProviderCount() > 1 && state.model) {
228
- rightSide = `${theme.fg("muted", `(${state.model.provider}) `)}${rightSideWithoutProvider}`;
229
- if (statsLeftWidth + minPadding + visibleWidth(rightSide) > width) {
230
- // Too wide, fall back
231
- rightSide = rightSideWithoutProvider;
232
- }
233
- }
225
+ const rightSide = rightSideWithoutProvider;
234
226
  const rightSideWidth = visibleWidth(rightSide);
235
227
  const totalNeeded = statsLeftWidth + minPadding + rightSideWidth;
236
228
  let statsLine;
@@ -1 +1 @@
1
- {"version":3,"file":"footer.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/footer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAClD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAkB,eAAe,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAGrF,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAE1C;;;GAGG;AACH,SAAS,kBAAkB,CAAC,IAAY;IACvC,qFAAqF;IACrF,OAAO,IAAI;SACT,OAAO,CAAC,WAAW,EAAE,GAAG,CAAC;SACzB,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC;SACnB,IAAI,EAAE,CAAC;AACV,CAAC;AAED;;GAEG;AACH,SAAS,YAAY,CAAC,KAAa;IAClC,IAAI,KAAK,GAAG,IAAI;QAAE,OAAO,KAAK,CAAC,QAAQ,EAAE,CAAC;IAC1C,IAAI,KAAK,GAAG,KAAK;QAAE,OAAO,GAAG,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC;IAC1D,IAAI,KAAK,GAAG,OAAO;QAAE,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC;IAC3D,IAAI,KAAK,GAAG,QAAQ;QAAE,OAAO,GAAG,CAAC,KAAK,GAAG,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC;IAChE,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC;AAC1C,CAAC;AAED,SAAS,KAAK,CAAC,IAAY,EAAE,KAAiD;IAC7E,OAAO,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,CAAC,GAAG,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,IAAI,CAAC,GAAG,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;AAC5E,CAAC;AAED;;;GAGG;AACH,SAAS,aAAa,CAAC,GAAW;IACjC,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IACnC,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC;QAAE,OAAO,EAAE,CAAC;IAEpC,uCAAuC;IACvC,IAAI,OAA2B,CAAC;IAChC,IAAI,CAAC;QACJ,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;QAC1C,IAAI,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;YAC3B,MAAM,OAAO,GAAG,WAAW,CAAC,SAAS,CAAC,CAAC,IAAI,EAAE,CAAC;YAC9C,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACxB,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;YACvC,CAAC;QACF,CAAC;IACF,CAAC;IAAC,MAAM,CAAC;QACR,qBAAqB;IACtB,CAAC;IAED,MAAM,KAAK,GAAG,KAAK,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IACzC,MAAM,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,KAAK,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAChE,OAAO,KAAK,GAAG,MAAM,CAAC;AACvB,CAAC;AAED;;;GAGG;AACH,MAAM,OAAO,eAAe;IAK3B,YACS,OAAqB,EACrB,UAAsC;QADtC,YAAO,GAAP,OAAO,CAAc;QACrB,eAAU,GAAV,UAAU,CAA4B;QANvC,uBAAkB,GAAG,IAAI,CAAC;QAC1B,aAAQ,GAAG,KAAK,CAAC;QACjB,kBAAa,GAAG,EAAE,CAAC;IAKxB,CAAC;IAEJ,qBAAqB,CAAC,OAAgB;QACrC,IAAI,CAAC,kBAAkB,GAAG,OAAO,CAAC;IACnC,CAAC;IAED;;;OAGG;IACH,WAAW,CAAC,OAAgB;QAC3B,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC;IACzB,CAAC;IAED;;;;OAIG;IACH,gBAAgB,CAAC,OAAe;QAC/B,IAAI,CAAC,aAAa,GAAG,OAAO,CAAC;IAC9B,CAAC;IAED;;;OAGG;IACH,UAAU;QACT,sDAAsD;IACvD,CAAC;IAED;;;OAGG;IACH,OAAO;QACN,0CAA0C;IAC3C,CAAC;IAED,MAAM,CAAC,KAAa;QACnB,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC;QACjC,MAAM,SAAS,GAAG,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;QAEzC,0FAA0F;QAC1F,IAAI,UAAU,GAAG,CAAC,CAAC;QACnB,IAAI,WAAW,GAAG,CAAC,CAAC;QACpB,IAAI,cAAc,GAAG,CAAC,CAAC;QACvB,IAAI,eAAe,GAAG,CAAC,CAAC;QACxB,IAAI,SAAS,GAAG,CAAC,CAAC;QAElB,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,OAAO,CAAC,cAAc,CAAC,UAAU,EAAE,EAAE,CAAC;YAC9D,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;gBACpE,UAAU,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC;gBACxC,WAAW,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC;gBAC1C,cAAc,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC;gBAChD,eAAe,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC;gBAClD,SAAS,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC;YAC7C,CAAC;QACF,CAAC;QAED,uEAAuE;QACvE,oEAAoE;QACpE,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,eAAe,EAAE,CAAC;QACpD,MAAM,aAAa,GAAG,YAAY,EAAE,aAAa,IAAI,KAAK,CAAC,KAAK,EAAE,aAAa,IAAI,CAAC,CAAC;QACrF,MAAM,mBAAmB,GAAG,YAAY,EAAE,OAAO,IAAI,CAAC,CAAC;QACvD,MAAM,cAAc,GAAG,YAAY,EAAE,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,mBAAmB,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;QAE7F,MAAM,cAAc,GAAG,IAAI,CAAC,OAAO,CAAC,cAA2C,CAAC;QAChF,MAAM,UAAU,GAAG,cAAc,CAAC,MAAM,EAAE,EAAE,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;QAE9D,gCAAgC;QAChC,IAAI,GAAG,GAAG,UAAU,CAAC;QACrB,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC;QACzD,IAAI,IAAI,IAAI,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YAClC,GAAG,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;QACpC,CAAC;QAED,8BAA8B;QAC9B,MAAM,MAAM,GAAG,IAAI,CAAC,UAAU,CAAC,YAAY,EAAE,CAAC;QAC9C,IAAI,MAAM,EAAE,CAAC;YACZ,GAAG,GAAG,GAAG,GAAG,KAAK,MAAM,GAAG,CAAC;QAC5B,CAAC;QAED,0BAA0B;QAC1B,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,CAAC,cAAc,CAAC,cAAc,EAAE,CAAC;QACjE,IAAI,WAAW,EAAE,CAAC;YACjB,GAAG,GAAG,GAAG,GAAG,MAAM,WAAW,EAAE,CAAC;QACjC,CAAC;QAED,MAAM,WAAW,GAAG,EAAE,CAAC;QAEvB,kFAAkF;QAClF,mFAAmF;QACnF,kFAAkF;QAClF,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YACnB,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC;QAC3C,CAAC;QAED,yEAAyE;QACzE,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;YACxB,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC,CAAC;QACtD,CAAC;QAED,IAAI,IAAI,CAAC,OAAO,CAAC,YAAY,EAAE,CAAC;YAC/B,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,YAAY,EAAE,SAAS,CAAC,CAAC,CAAC;QAClD,CAAC;aAAM,IAAI,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC;YACrC,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC,CAAC;QAC9C,CAAC;aAAM,CAAC;YACP,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC,CAAC;QAC7C,CAAC;QAED,MAAM,eAAe,GAAG,IAAI,CAAC,OAAO,CAAC,mBAAmB,IAAI,CAAC,CAAC;QAC9D,IAAI,eAAe,GAAG,CAAC,EAAE,CAAC;YACzB,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,eAAe,EAAE,EAAE,eAAe,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC;QACjG,CAAC;QAED,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,YAAY,IAAI,CAAC,CAAC;QACpD,IAAI,YAAY,GAAG,CAAC,EAAE,CAAC;YACtB,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,YAAY,EAAE,EAAE,SAAS,CAAC,CAAC,CAAC;QAC7D,CAAC;QAED,mBAAmB;QACnB,MAAM,UAAU,GAAG,EAAE,CAAC;QACtB,IAAI,UAAU;YAAE,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,IAAI,YAAY,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC;QACnF,IAAI,WAAW;YAAE,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,IAAI,YAAY,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,CAAC;QACrF,IAAI,cAAc;YAAE,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,IAAI,YAAY,CAAC,cAAc,CAAC,EAAE,CAAC,CAAC,CAAC;QAC3F,IAAI,eAAe;YAAE,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,IAAI,YAAY,CAAC,eAAe,CAAC,EAAE,CAAC,CAAC,CAAC;QAE7F,+DAA+D;QAC/D,MAAM,iBAAiB,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,YAAY,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;QACrG,IAAI,SAAS,IAAI,iBAAiB,EAAE,CAAC;YACpC,MAAM,OAAO,GAAG,IAAI,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,iBAAiB,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;YAC/E,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC;QAC7C,CAAC;QAED,6CAA6C;QAC7C,IAAI,iBAAyB,CAAC;QAC9B,MAAM,aAAa,GAAG,IAAI,CAAC,kBAAkB,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC;QAC/D,MAAM,qBAAqB,GAC1B,cAAc,KAAK,GAAG;YACrB,CAAC,CAAC,SAAS,YAAY,CAAC,aAAa,CAAC,GAAG,aAAa,EAAE;YACxD,CAAC,CAAC,OAAO,cAAc,KAAK,YAAY,CAAC,aAAa,CAAC,GAAG,aAAa,EAAE,CAAC;QAC5E,IAAI,mBAAmB,GAAG,EAAE,EAAE,CAAC;YAC9B,iBAAiB,GAAG,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,qBAAqB,CAAC,CAAC;QAC9D,CAAC;aAAM,IAAI,mBAAmB,GAAG,EAAE,EAAE,CAAC;YACrC,iBAAiB,GAAG,KAAK,CAAC,EAAE,CAAC,SAAS,EAAE,qBAAqB,CAAC,CAAC;QAChE,CAAC;aAAM,CAAC;YACP,iBAAiB,GAAG,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,qBAAqB,CAAC,CAAC;QAC9D,CAAC;QACD,UAAU,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;QAEnC,IAAI,SAAS,GAAG,CAAC,GAAG,WAAW,EAAE,GAAG,UAAU,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAEhE,6EAA6E;QAC7E,MAAM,SAAS,GAAG,KAAK,CAAC,KAAK,EAAE,EAAE,IAAI,UAAU,CAAC;QAEhD,IAAI,cAAc,GAAG,YAAY,CAAC,SAAS,CAAC,CAAC;QAE7C,wCAAwC;QACxC,IAAI,cAAc,GAAG,KAAK,EAAE,CAAC;YAC5B,SAAS,GAAG,eAAe,CAAC,SAAS,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;YACrD,cAAc,GAAG,YAAY,CAAC,SAAS,CAAC,CAAC;QAC1C,CAAC;QAED,mFAAmF;QACnF,MAAM,UAAU,GAAG,CAAC,CAAC;QAErB,2DAA2D;QAC3D,IAAI,wBAAwB,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;QAC5G,IAAI,KAAK,CAAC,KAAK,EAAE,SAAS,EAAE,CAAC;YAC5B,MAAM,aAAa,GAAG,KAAK,CAAC,aAAa,IAAI,KAAK,CAAC;YACnD,wBAAwB;gBACvB,aAAa,KAAK,KAAK;oBACtB,CAAC,CAAC,GAAG,KAAK,CAAC,EAAE,CAAC,QAAQ,EAAE,SAAS,CAAC,GAAG,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,iBAAiB,CAAC,EAAE;oBAC3E,CAAC,CAAC,GAAG,KAAK,CAAC,EAAE,CAAC,QAAQ,EAAE,SAAS,CAAC,GAAG,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,aAAa,EAAE,CAAC,EAAE,CAAC;QACnF,CAAC;QAED,8FAA8F;QAC9F,IAAI,SAAS,GAAG,wBAAwB,CAAC;QACzC,IAAI,IAAI,CAAC,UAAU,CAAC,yBAAyB,EAAE,GAAG,CAAC,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;YACpE,SAAS,GAAG,GAAG,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,IAAI,KAAK,CAAC,KAAK,CAAC,QAAQ,IAAI,CAAC,GAAG,wBAAwB,EAAE,CAAC;YAC5F,IAAI,cAAc,GAAG,UAAU,GAAG,YAAY,CAAC,SAAS,CAAC,GAAG,KAAK,EAAE,CAAC;gBACnE,sBAAsB;gBACtB,SAAS,GAAG,wBAAwB,CAAC;YACtC,CAAC;QACF,CAAC;QAED,MAAM,cAAc,GAAG,YAAY,CAAC,SAAS,CAAC,CAAC;QAC/C,MAAM,WAAW,GAAG,cAAc,GAAG,UAAU,GAAG,cAAc,CAAC;QAEjE,IAAI,SAAiB,CAAC;QACtB,IAAI,WAAW,IAAI,KAAK,EAAE,CAAC;YAC1B,8CAA8C;YAC9C,MAAM,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,KAAK,GAAG,cAAc,GAAG,cAAc,CAAC,CAAC;YACpE,SAAS,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,CAAC;QAC7C,CAAC;aAAM,CAAC;YACP,8BAA8B;YAC9B,MAAM,iBAAiB,GAAG,KAAK,GAAG,cAAc,GAAG,UAAU,CAAC;YAC9D,IAAI,iBAAiB,GAAG,CAAC,EAAE,CAAC;gBAC3B,MAAM,cAAc,GAAG,eAAe,CAAC,SAAS,EAAE,iBAAiB,EAAE,EAAE,CAAC,CAAC;gBACzE,MAAM,mBAAmB,GAAG,YAAY,CAAC,cAAc,CAAC,CAAC;gBACzD,MAAM,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,GAAG,cAAc,GAAG,mBAAmB,CAAC,CAAC,CAAC;gBACtF,SAAS,GAAG,SAAS,GAAG,OAAO,GAAG,cAAc,CAAC;YAClD,CAAC;iBAAM,CAAC;gBACP,yCAAyC;gBACzC,SAAS,GAAG,SAAS,CAAC;YACvB,CAAC;QACF,CAAC;QAED,+DAA+D;QAC/D,MAAM,UAAU,GAAG,aAAa,CAAC,UAAU,CAAC,CAAC;QAC7C,IAAI,OAAe,CAAC;QACpB,IAAI,UAAU,EAAE,CAAC;YAChB,+DAA+D;YAC/D,MAAM,WAAW,GAAG,IAAI,GAAG,UAAU,GAAG,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;YAC7D,MAAM,gBAAgB,GAAG,YAAY,CAAC,WAAW,CAAC,CAAC;YACnD,0DAA0D;YAC1D,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,GAAG,gBAAgB,CAAC,CAAC;YAC1D,MAAM,eAAe,GAAG,eAAe,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,CAAC,EAAE,WAAW,EAAE,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC;YACnG,OAAO,GAAG,eAAe,GAAG,WAAW,CAAC;QACzC,CAAC;aAAM,CAAC;YACP,OAAO,GAAG,eAAe,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC;QAChF,CAAC;QACD,MAAM,KAAK,GAAG,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;QAEnC,wEAAwE;QACxE,MAAM,iBAAiB,GAAG,IAAI,CAAC,UAAU,CAAC,oBAAoB,EAAE,CAAC;QACjE,IAAI,iBAAiB,CAAC,IAAI,GAAG,CAAC,EAAE,CAAC;YAChC,MAAM,cAAc,GAAG,KAAK,CAAC,IAAI,CAAC,iBAAiB,CAAC,OAAO,EAAE,CAAC;iBAC5D,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;iBACtC,GAAG,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,kBAAkB,CAAC,IAAI,CAAC,CAAC,CAAC;YAC9C,MAAM,UAAU,GAAG,cAAc,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAC5C,iFAAiF;YACjF,KAAK,CAAC,IAAI,CAAC,eAAe,CAAC,UAAU,EAAE,KAAK,EAAE,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC;QACxE,CAAC;QAED,OAAO,KAAK,CAAC;IACd,CAAC;CACD","sourcesContent":["import { existsSync, readdirSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { type Component, truncateToWidth, visibleWidth } from \"@mariozechner/pi-tui\";\nimport type { AgentSession } from \"../../../core/agent-session.js\";\nimport type { ReadonlyFooterDataProvider } from \"../../../core/footer-data-provider.js\";\nimport { theme } from \"../theme/theme.js\";\n\n/**\n * Sanitize text for display in a single-line status.\n * Removes newlines, tabs, carriage returns, and other control characters.\n */\nfunction sanitizeStatusText(text: string): string {\n\t// Replace newlines, tabs, carriage returns with space, then collapse multiple spaces\n\treturn text\n\t\t.replace(/[\\r\\n\\t]/g, \" \")\n\t\t.replace(/ +/g, \" \")\n\t\t.trim();\n}\n\n/**\n * Format token counts (similar to web-ui)\n */\nfunction formatTokens(count: number): string {\n\tif (count < 1000) return count.toString();\n\tif (count < 10000) return `${(count / 1000).toFixed(1)}k`;\n\tif (count < 1000000) return `${Math.round(count / 1000)}k`;\n\tif (count < 10000000) return `${(count / 1000000).toFixed(1)}M`;\n\treturn `${Math.round(count / 1000000)}M`;\n}\n\nfunction badge(text: string, color: \"accent\" | \"success\" | \"warning\" | \"muted\"): string {\n\treturn theme.fg(\"dim\", \"[\") + theme.fg(color, text) + theme.fg(\"dim\", \"]\");\n}\n\n/**\n * Detect IOSM workspace and return a compact status segment.\n * Returns empty string when no IOSM workspace is found.\n */\nfunction getIosmStatus(cwd: string): string {\n\tconst iosmDir = join(cwd, \".iosm\");\n\tif (!existsSync(iosmDir)) return \"\";\n\n\t// Try to find the most recent cycle ID\n\tlet cycleId: string | undefined;\n\ttry {\n\t\tconst cyclesDir = join(iosmDir, \"cycles\");\n\t\tif (existsSync(cyclesDir)) {\n\t\t\tconst entries = readdirSync(cyclesDir).sort();\n\t\t\tif (entries.length > 0) {\n\t\t\t\tcycleId = entries[entries.length - 1];\n\t\t\t}\n\t\t}\n\t} catch {\n\t\t// Ignore read errors\n\t}\n\n\tconst label = theme.fg(\"accent\", \"iosm\");\n\tconst suffix = cycleId ? theme.fg(\"muted\", ` #${cycleId}`) : \"\";\n\treturn label + suffix;\n}\n\n/**\n * Footer component that shows pwd, token stats, and context usage.\n * Computes token/context stats from session, gets git branch and extension statuses from provider.\n */\nexport class FooterComponent implements Component {\n\tprivate autoCompactEnabled = true;\n\tprivate planMode = false;\n\tprivate activeProfile = \"\";\n\n\tconstructor(\n\t\tprivate session: AgentSession,\n\t\tprivate footerData: ReadonlyFooterDataProvider,\n\t) {}\n\n\tsetAutoCompactEnabled(enabled: boolean): void {\n\t\tthis.autoCompactEnabled = enabled;\n\t}\n\n\t/**\n\t * Toggle plan-mode badge in the status line.\n\t * When enabled, a [PLAN] badge is prepended before the session state badge.\n\t */\n\tsetPlanMode(enabled: boolean): void {\n\t\tthis.planMode = enabled;\n\t}\n\n\t/**\n\t * Set the active profile name.\n\t * When non-empty, a [profile] badge is shown in the status line so the\n\t * operator can always see the current profile, including \"full\".\n\t */\n\tsetActiveProfile(profile: string): void {\n\t\tthis.activeProfile = profile;\n\t}\n\n\t/**\n\t * No-op: git branch caching now handled by provider.\n\t * Kept for compatibility with existing call sites in interactive-mode.\n\t */\n\tinvalidate(): void {\n\t\t// No-op: git branch is cached/invalidated by provider\n\t}\n\n\t/**\n\t * Clean up resources.\n\t * Git watcher cleanup now handled by provider.\n\t */\n\tdispose(): void {\n\t\t// Git watcher cleanup handled by provider\n\t}\n\n\trender(width: number): string[] {\n\t\tconst state = this.session.state;\n\t\tconst separator = theme.fg(\"dim\", \" • \");\n\n\t\t// Calculate cumulative usage from ALL session entries (not just post-compaction messages)\n\t\tlet totalInput = 0;\n\t\tlet totalOutput = 0;\n\t\tlet totalCacheRead = 0;\n\t\tlet totalCacheWrite = 0;\n\t\tlet totalCost = 0;\n\n\t\tfor (const entry of this.session.sessionManager.getEntries()) {\n\t\t\tif (entry.type === \"message\" && entry.message.role === \"assistant\") {\n\t\t\t\ttotalInput += entry.message.usage.input;\n\t\t\t\ttotalOutput += entry.message.usage.output;\n\t\t\t\ttotalCacheRead += entry.message.usage.cacheRead;\n\t\t\t\ttotalCacheWrite += entry.message.usage.cacheWrite;\n\t\t\t\ttotalCost += entry.message.usage.cost.total;\n\t\t\t}\n\t\t}\n\n\t\t// Calculate context usage from session (handles compaction correctly).\n\t\t// After compaction, tokens are unknown until the next LLM response.\n\t\tconst contextUsage = this.session.getContextUsage();\n\t\tconst contextWindow = contextUsage?.contextWindow ?? state.model?.contextWindow ?? 0;\n\t\tconst contextPercentValue = contextUsage?.percent ?? 0;\n\t\tconst contextPercent = contextUsage?.percent !== null ? contextPercentValue.toFixed(1) : \"?\";\n\n\t\tconst sessionManager = this.session.sessionManager as { getCwd?: () => string };\n\t\tconst sessionCwd = sessionManager.getCwd?.() ?? process.cwd();\n\n\t\t// Replace home directory with ~\n\t\tlet pwd = sessionCwd;\n\t\tconst home = process.env.HOME || process.env.USERPROFILE;\n\t\tif (home && pwd.startsWith(home)) {\n\t\t\tpwd = `~${pwd.slice(home.length)}`;\n\t\t}\n\n\t\t// Add git branch if available\n\t\tconst branch = this.footerData.getGitBranch();\n\t\tif (branch) {\n\t\t\tpwd = `${pwd} (${branch})`;\n\t\t}\n\n\t\t// Add session name if set\n\t\tconst sessionName = this.session.sessionManager.getSessionName();\n\t\tif (sessionName) {\n\t\t\tpwd = `${pwd} • ${sessionName}`;\n\t\t}\n\n\t\tconst statusParts = [];\n\n\t\t// Plan-mode badge: prepended first so it is always the leftmost status indicator.\n\t\t// This placement mirrors how editors surface mode indicators (e.g. INSERT, VISUAL)\n\t\t// before the file-status indicator, giving the operator immediate mode awareness.\n\t\tif (this.planMode) {\n\t\t\tstatusParts.push(badge(\"PLAN\", \"accent\"));\n\t\t}\n\n\t\t// Profile badge is always shown when a profile is set, including \"full\".\n\t\tif (this.activeProfile) {\n\t\t\tstatusParts.push(badge(this.activeProfile, \"muted\"));\n\t\t}\n\n\t\tif (this.session.isCompacting) {\n\t\t\tstatusParts.push(badge(\"compacting\", \"warning\"));\n\t\t} else if (this.session.isStreaming) {\n\t\t\tstatusParts.push(badge(\"working\", \"accent\"));\n\t\t} else {\n\t\t\tstatusParts.push(badge(\"ready\", \"success\"));\n\t\t}\n\n\t\tconst pendingMessages = this.session.pendingMessageCount ?? 0;\n\t\tif (pendingMessages > 0) {\n\t\t\tstatusParts.push(badge(`queue ${pendingMessages}`, pendingMessages > 1 ? \"warning\" : \"accent\"));\n\t\t}\n\n\t\tconst retryAttempt = this.session.retryAttempt ?? 0;\n\t\tif (retryAttempt > 0) {\n\t\t\tstatusParts.push(badge(`retry ${retryAttempt}`, \"warning\"));\n\t\t}\n\n\t\t// Build stats line\n\t\tconst usageParts = [];\n\t\tif (totalInput) usageParts.push(theme.fg(\"muted\", `↑${formatTokens(totalInput)}`));\n\t\tif (totalOutput) usageParts.push(theme.fg(\"muted\", `↓${formatTokens(totalOutput)}`));\n\t\tif (totalCacheRead) usageParts.push(theme.fg(\"muted\", `R${formatTokens(totalCacheRead)}`));\n\t\tif (totalCacheWrite) usageParts.push(theme.fg(\"muted\", `W${formatTokens(totalCacheWrite)}`));\n\n\t\t// Show cost with \"(sub)\" indicator if using OAuth subscription\n\t\tconst usingSubscription = state.model ? this.session.modelRegistry.isUsingOAuth(state.model) : false;\n\t\tif (totalCost || usingSubscription) {\n\t\t\tconst costStr = `$${totalCost.toFixed(3)}${usingSubscription ? \" (sub)\" : \"\"}`;\n\t\t\tusageParts.push(theme.fg(\"muted\", costStr));\n\t\t}\n\n\t\t// Colorize context percentage based on usage\n\t\tlet contextPercentStr: string;\n\t\tconst autoIndicator = this.autoCompactEnabled ? \" (auto)\" : \"\";\n\t\tconst contextPercentDisplay =\n\t\t\tcontextPercent === \"?\"\n\t\t\t\t? `ctx ?/${formatTokens(contextWindow)}${autoIndicator}`\n\t\t\t\t: `ctx ${contextPercent}%/${formatTokens(contextWindow)}${autoIndicator}`;\n\t\tif (contextPercentValue > 90) {\n\t\t\tcontextPercentStr = theme.fg(\"error\", contextPercentDisplay);\n\t\t} else if (contextPercentValue > 70) {\n\t\t\tcontextPercentStr = theme.fg(\"warning\", contextPercentDisplay);\n\t\t} else {\n\t\t\tcontextPercentStr = theme.fg(\"muted\", contextPercentDisplay);\n\t\t}\n\t\tusageParts.push(contextPercentStr);\n\n\t\tlet statsLeft = [...statusParts, ...usageParts].join(separator);\n\n\t\t// Add model name on the right side, plus thinking level if model supports it\n\t\tconst modelName = state.model?.id || \"no-model\";\n\n\t\tlet statsLeftWidth = visibleWidth(statsLeft);\n\n\t\t// If statsLeft is too wide, truncate it\n\t\tif (statsLeftWidth > width) {\n\t\t\tstatsLeft = truncateToWidth(statsLeft, width, \"...\");\n\t\t\tstatsLeftWidth = visibleWidth(statsLeft);\n\t\t}\n\n\t\t// Calculate available space for padding (minimum 2 spaces between stats and model)\n\t\tconst minPadding = 2;\n\n\t\t// Add thinking level indicator if model supports reasoning\n\t\tlet rightSideWithoutProvider = state.model ? theme.fg(\"accent\", modelName) : theme.fg(\"warning\", modelName);\n\t\tif (state.model?.reasoning) {\n\t\t\tconst thinkingLevel = state.thinkingLevel || \"off\";\n\t\t\trightSideWithoutProvider =\n\t\t\t\tthinkingLevel === \"off\"\n\t\t\t\t\t? `${theme.fg(\"accent\", modelName)}${theme.fg(\"muted\", \" • thinking off\")}`\n\t\t\t\t\t: `${theme.fg(\"accent\", modelName)}${theme.fg(\"muted\", ` • ${thinkingLevel}`)}`;\n\t\t}\n\n\t\t// Prepend the provider in parentheses if there are multiple providers and there's enough room\n\t\tlet rightSide = rightSideWithoutProvider;\n\t\tif (this.footerData.getAvailableProviderCount() > 1 && state.model) {\n\t\t\trightSide = `${theme.fg(\"muted\", `(${state.model.provider}) `)}${rightSideWithoutProvider}`;\n\t\t\tif (statsLeftWidth + minPadding + visibleWidth(rightSide) > width) {\n\t\t\t\t// Too wide, fall back\n\t\t\t\trightSide = rightSideWithoutProvider;\n\t\t\t}\n\t\t}\n\n\t\tconst rightSideWidth = visibleWidth(rightSide);\n\t\tconst totalNeeded = statsLeftWidth + minPadding + rightSideWidth;\n\n\t\tlet statsLine: string;\n\t\tif (totalNeeded <= width) {\n\t\t\t// Both fit - add padding to right-align model\n\t\t\tconst padding = \" \".repeat(width - statsLeftWidth - rightSideWidth);\n\t\t\tstatsLine = statsLeft + padding + rightSide;\n\t\t} else {\n\t\t\t// Need to truncate right side\n\t\t\tconst availableForRight = width - statsLeftWidth - minPadding;\n\t\t\tif (availableForRight > 0) {\n\t\t\t\tconst truncatedRight = truncateToWidth(rightSide, availableForRight, \"\");\n\t\t\t\tconst truncatedRightWidth = visibleWidth(truncatedRight);\n\t\t\t\tconst padding = \" \".repeat(Math.max(0, width - statsLeftWidth - truncatedRightWidth));\n\t\t\t\tstatsLine = statsLeft + padding + truncatedRight;\n\t\t\t} else {\n\t\t\t\t// Not enough space for right side at all\n\t\t\t\tstatsLine = statsLeft;\n\t\t\t}\n\t\t}\n\n\t\t// Build pwd line, optionally with IOSM status segment appended\n\t\tconst iosmStatus = getIosmStatus(sessionCwd);\n\t\tlet pwdLine: string;\n\t\tif (iosmStatus) {\n\t\t\t// Assemble the IOSM segment (carries its own ANSI color codes)\n\t\t\tconst iosmSegment = \" [\" + iosmStatus + theme.fg(\"dim\", \"]\");\n\t\t\tconst iosmSegmentWidth = visibleWidth(iosmSegment);\n\t\t\t// Truncate pwd portion to leave room for the iosm segment\n\t\t\tconst maxPwdWidth = Math.max(0, width - iosmSegmentWidth);\n\t\t\tconst dimPwdTruncated = truncateToWidth(theme.fg(\"dim\", pwd), maxPwdWidth, theme.fg(\"dim\", \"...\"));\n\t\t\tpwdLine = dimPwdTruncated + iosmSegment;\n\t\t} else {\n\t\t\tpwdLine = truncateToWidth(theme.fg(\"dim\", pwd), width, theme.fg(\"dim\", \"...\"));\n\t\t}\n\t\tconst lines = [pwdLine, statsLine];\n\n\t\t// Add extension statuses on a single line, sorted by key alphabetically\n\t\tconst extensionStatuses = this.footerData.getExtensionStatuses();\n\t\tif (extensionStatuses.size > 0) {\n\t\t\tconst sortedStatuses = Array.from(extensionStatuses.entries())\n\t\t\t\t.sort(([a], [b]) => a.localeCompare(b))\n\t\t\t\t.map(([, text]) => sanitizeStatusText(text));\n\t\t\tconst statusLine = sortedStatuses.join(\" \");\n\t\t\t// Truncate to terminal width with dim ellipsis for consistency with footer style\n\t\t\tlines.push(truncateToWidth(statusLine, width, theme.fg(\"dim\", \"...\")));\n\t\t}\n\n\t\treturn lines;\n\t}\n}\n"]}
1
+ {"version":3,"file":"footer.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/footer.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync, readdirSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { type Component, truncateToWidth, visibleWidth } from \"@mariozechner/pi-tui\";\nimport type { AgentSession } from \"../../../core/agent-session.js\";\nimport type { ReadonlyFooterDataProvider } from \"../../../core/footer-data-provider.js\";\nimport { theme } from \"../theme/theme.js\";\n\n/**\n * Sanitize text for display in a single-line status.\n * Removes newlines, tabs, carriage returns, and other control characters.\n */\nfunction sanitizeStatusText(text: string): string {\n\t// Replace newlines, tabs, carriage returns with space, then collapse multiple spaces\n\treturn text\n\t\t.replace(/[\\r\\n\\t]/g, \" \")\n\t\t.replace(/ +/g, \" \")\n\t\t.trim();\n}\n\n/**\n * Format token counts (similar to web-ui)\n */\nfunction formatTokens(count: number): string {\n\tif (count < 1000) return count.toString();\n\tif (count < 10000) return `${(count / 1000).toFixed(1)}k`;\n\tif (count < 1000000) return `${Math.round(count / 1000)}k`;\n\tif (count < 10000000) return `${(count / 1000000).toFixed(1)}M`;\n\treturn `${Math.round(count / 1000000)}M`;\n}\n\nfunction badge(text: string, color: \"accent\" | \"success\" | \"warning\" | \"muted\"): string {\n\treturn theme.fg(\"dim\", \"[\") + theme.fg(color, text) + theme.fg(\"dim\", \"]\");\n}\n\n/**\n * Detect IOSM workspace and return a compact status segment.\n * Returns empty string when no IOSM workspace is found.\n */\nfunction getIosmStatus(cwd: string): string {\n\tconst iosmDir = join(cwd, \".iosm\");\n\tif (!existsSync(iosmDir)) return \"\";\n\n\t// Try to find the most recent cycle ID\n\tlet cycleId: string | undefined;\n\ttry {\n\t\tconst cyclesDir = join(iosmDir, \"cycles\");\n\t\tif (existsSync(cyclesDir)) {\n\t\t\tconst entries = readdirSync(cyclesDir).sort();\n\t\t\tif (entries.length > 0) {\n\t\t\t\tcycleId = entries[entries.length - 1];\n\t\t\t}\n\t\t}\n\t} catch {\n\t\t// Ignore read errors\n\t}\n\n\tconst label = theme.fg(\"accent\", \"iosm\");\n\tconst suffix = cycleId ? theme.fg(\"muted\", ` #${cycleId}`) : \"\";\n\treturn label + suffix;\n}\n\n/**\n * Footer component that shows pwd, token stats, and context usage.\n * Computes token/context stats from session, gets git branch and extension statuses from provider.\n */\nexport class FooterComponent implements Component {\n\tprivate autoCompactEnabled = true;\n\tprivate planMode = false;\n\tprivate activeProfile = \"\";\n\n\tconstructor(\n\t\tprivate session: AgentSession,\n\t\tprivate footerData: ReadonlyFooterDataProvider,\n\t) {}\n\n\tsetAutoCompactEnabled(enabled: boolean): void {\n\t\tthis.autoCompactEnabled = enabled;\n\t}\n\n\t/**\n\t * Toggle plan-mode badge in the status line.\n\t * When enabled, a [PLAN] badge is prepended before the session state badge.\n\t */\n\tsetPlanMode(enabled: boolean): void {\n\t\tthis.planMode = enabled;\n\t}\n\n\t/**\n\t * Set the active profile name.\n\t * When non-empty, a [profile] badge is shown in the status line so the\n\t * operator can always see the current profile, including \"full\".\n\t */\n\tsetActiveProfile(profile: string): void {\n\t\tthis.activeProfile = profile;\n\t}\n\n\t/**\n\t * No-op: git branch caching now handled by provider.\n\t * Kept for compatibility with existing call sites in interactive-mode.\n\t */\n\tinvalidate(): void {\n\t\t// No-op: git branch is cached/invalidated by provider\n\t}\n\n\t/**\n\t * Clean up resources.\n\t * Git watcher cleanup now handled by provider.\n\t */\n\tdispose(): void {\n\t\t// Git watcher cleanup handled by provider\n\t}\n\n\trender(width: number): string[] {\n\t\tconst state = this.session.state;\n\t\tconst separator = theme.fg(\"dim\", \" • \");\n\n\t\t// Calculate cumulative usage from ALL session entries (not just post-compaction messages)\n\t\tlet totalInput = 0;\n\t\tlet totalOutput = 0;\n\t\tlet totalCacheRead = 0;\n\t\tlet totalCacheWrite = 0;\n\t\tlet totalCost = 0;\n\n\t\tfor (const entry of this.session.sessionManager.getEntries()) {\n\t\t\tif (entry.type === \"message\" && entry.message.role === \"assistant\") {\n\t\t\t\ttotalInput += entry.message.usage.input;\n\t\t\t\ttotalOutput += entry.message.usage.output;\n\t\t\t\ttotalCacheRead += entry.message.usage.cacheRead;\n\t\t\t\ttotalCacheWrite += entry.message.usage.cacheWrite;\n\t\t\t\ttotalCost += entry.message.usage.cost.total;\n\t\t\t}\n\t\t}\n\n\t\t// Calculate context usage from session (handles compaction correctly).\n\t\t// After compaction, tokens are unknown until the next LLM response.\n\t\tconst contextUsage = this.session.getContextUsage();\n\t\tconst contextWindow = contextUsage?.contextWindow ?? state.model?.contextWindow ?? 0;\n\t\tconst contextPercentValue = contextUsage?.percent ?? 0;\n\t\tconst contextPercent = contextUsage?.percent !== null ? contextPercentValue.toFixed(1) : \"?\";\n\n\t\tconst sessionManager = this.session.sessionManager as { getCwd?: () => string };\n\t\tconst sessionCwd = sessionManager.getCwd?.() ?? process.cwd();\n\n\t\t// Replace home directory with ~\n\t\tlet pwd = sessionCwd;\n\t\tconst home = process.env.HOME || process.env.USERPROFILE;\n\t\tif (home && pwd.startsWith(home)) {\n\t\t\tpwd = `~${pwd.slice(home.length)}`;\n\t\t}\n\n\t\t// Add git branch if available\n\t\tconst branch = this.footerData.getGitBranch();\n\t\tif (branch) {\n\t\t\tpwd = `${pwd} (${branch})`;\n\t\t}\n\n\t\t// Add session name if set\n\t\tconst sessionName = this.session.sessionManager.getSessionName();\n\t\tif (sessionName) {\n\t\t\tpwd = `${pwd} • ${sessionName}`;\n\t\t}\n\n\t\tconst statusParts = [];\n\n\t\t// Plan-mode badge: prepended first so it is always the leftmost status indicator.\n\t\t// This placement mirrors how editors surface mode indicators (e.g. INSERT, VISUAL)\n\t\t// before the file-status indicator, giving the operator immediate mode awareness.\n\t\tif (this.planMode) {\n\t\t\tstatusParts.push(badge(\"PLAN\", \"accent\"));\n\t\t}\n\n\t\t// Profile badge is always shown when a profile is set, including \"full\".\n\t\tif (this.activeProfile) {\n\t\t\tstatusParts.push(badge(this.activeProfile, \"muted\"));\n\t\t}\n\n\t\tif (this.session.isCompacting) {\n\t\t\tstatusParts.push(badge(\"compacting\", \"warning\"));\n\t\t} else if (this.session.isStreaming) {\n\t\t\tstatusParts.push(badge(\"working\", \"accent\"));\n\t\t} else {\n\t\t\tstatusParts.push(badge(\"ready\", \"success\"));\n\t\t}\n\n\t\tconst pendingMessages = this.session.pendingMessageCount ?? 0;\n\t\tif (pendingMessages > 0) {\n\t\t\tstatusParts.push(badge(`queue ${pendingMessages}`, pendingMessages > 1 ? \"warning\" : \"accent\"));\n\t\t}\n\n\t\tconst retryAttempt = this.session.retryAttempt ?? 0;\n\t\tif (retryAttempt > 0) {\n\t\t\tstatusParts.push(badge(`retry ${retryAttempt}`, \"warning\"));\n\t\t}\n\n\t\t// Build stats line\n\t\tconst usageParts = [];\n\t\tif (totalInput) usageParts.push(theme.fg(\"muted\", `↑${formatTokens(totalInput)}`));\n\t\tif (totalOutput) usageParts.push(theme.fg(\"muted\", `↓${formatTokens(totalOutput)}`));\n\t\tif (totalCacheRead) usageParts.push(theme.fg(\"muted\", `R${formatTokens(totalCacheRead)}`));\n\t\tif (totalCacheWrite) usageParts.push(theme.fg(\"muted\", `W${formatTokens(totalCacheWrite)}`));\n\n\t\t// Show cost with \"(sub)\" indicator if using OAuth subscription\n\t\tconst usingSubscription = state.model ? this.session.modelRegistry.isUsingOAuth(state.model) : false;\n\t\tif (totalCost || usingSubscription) {\n\t\t\tconst costStr = `$${totalCost.toFixed(3)}${usingSubscription ? \" (sub)\" : \"\"}`;\n\t\t\tusageParts.push(theme.fg(\"muted\", costStr));\n\t\t}\n\n\t\t// Colorize context percentage based on usage\n\t\tlet contextPercentStr: string;\n\t\tconst autoIndicator = this.autoCompactEnabled ? \" (auto)\" : \"\";\n\t\tconst contextPercentDisplay =\n\t\t\tcontextPercent === \"?\"\n\t\t\t\t? `ctx ?/${formatTokens(contextWindow)}${autoIndicator}`\n\t\t\t\t: `ctx ${contextPercent}%/${formatTokens(contextWindow)}${autoIndicator}`;\n\t\tif (contextPercentValue > 90) {\n\t\t\tcontextPercentStr = theme.fg(\"error\", contextPercentDisplay);\n\t\t} else if (contextPercentValue > 70) {\n\t\t\tcontextPercentStr = theme.fg(\"warning\", contextPercentDisplay);\n\t\t} else {\n\t\t\tcontextPercentStr = theme.fg(\"muted\", contextPercentDisplay);\n\t\t}\n\t\tusageParts.push(contextPercentStr);\n\n\t\tlet statsLeft = [...statusParts, ...usageParts].join(separator);\n\n\t\t// Add provider/model on the right side, plus thinking level if model supports it\n\t\tconst modelName = state.model ? `${state.model.provider}/${state.model.id}` : \"no-model\";\n\n\t\tlet statsLeftWidth = visibleWidth(statsLeft);\n\n\t\t// If statsLeft is too wide, truncate it\n\t\tif (statsLeftWidth > width) {\n\t\t\tstatsLeft = truncateToWidth(statsLeft, width, \"...\");\n\t\t\tstatsLeftWidth = visibleWidth(statsLeft);\n\t\t}\n\n\t\t// Calculate available space for padding (minimum 2 spaces between stats and model)\n\t\tconst minPadding = 2;\n\n\t\t// Add thinking level indicator if model supports reasoning\n\t\tlet rightSideWithoutProvider = state.model ? theme.fg(\"accent\", modelName) : theme.fg(\"warning\", modelName);\n\t\tif (state.model?.reasoning) {\n\t\t\tconst thinkingLevel = state.thinkingLevel || \"off\";\n\t\t\trightSideWithoutProvider =\n\t\t\t\tthinkingLevel === \"off\"\n\t\t\t\t\t? `${theme.fg(\"accent\", modelName)}${theme.fg(\"muted\", \" • thinking off\")}`\n\t\t\t\t\t: `${theme.fg(\"accent\", modelName)}${theme.fg(\"muted\", ` • ${thinkingLevel}`)}`;\n\t\t}\n\n\t\tconst rightSide = rightSideWithoutProvider;\n\n\t\tconst rightSideWidth = visibleWidth(rightSide);\n\t\tconst totalNeeded = statsLeftWidth + minPadding + rightSideWidth;\n\n\t\tlet statsLine: string;\n\t\tif (totalNeeded <= width) {\n\t\t\t// Both fit - add padding to right-align model\n\t\t\tconst padding = \" \".repeat(width - statsLeftWidth - rightSideWidth);\n\t\t\tstatsLine = statsLeft + padding + rightSide;\n\t\t} else {\n\t\t\t// Need to truncate right side\n\t\t\tconst availableForRight = width - statsLeftWidth - minPadding;\n\t\t\tif (availableForRight > 0) {\n\t\t\t\tconst truncatedRight = truncateToWidth(rightSide, availableForRight, \"\");\n\t\t\t\tconst truncatedRightWidth = visibleWidth(truncatedRight);\n\t\t\t\tconst padding = \" \".repeat(Math.max(0, width - statsLeftWidth - truncatedRightWidth));\n\t\t\t\tstatsLine = statsLeft + padding + truncatedRight;\n\t\t\t} else {\n\t\t\t\t// Not enough space for right side at all\n\t\t\t\tstatsLine = statsLeft;\n\t\t\t}\n\t\t}\n\n\t\t// Build pwd line, optionally with IOSM status segment appended\n\t\tconst iosmStatus = getIosmStatus(sessionCwd);\n\t\tlet pwdLine: string;\n\t\tif (iosmStatus) {\n\t\t\t// Assemble the IOSM segment (carries its own ANSI color codes)\n\t\t\tconst iosmSegment = \" [\" + iosmStatus + theme.fg(\"dim\", \"]\");\n\t\t\tconst iosmSegmentWidth = visibleWidth(iosmSegment);\n\t\t\t// Truncate pwd portion to leave room for the iosm segment\n\t\t\tconst maxPwdWidth = Math.max(0, width - iosmSegmentWidth);\n\t\t\tconst dimPwdTruncated = truncateToWidth(theme.fg(\"dim\", pwd), maxPwdWidth, theme.fg(\"dim\", \"...\"));\n\t\t\tpwdLine = dimPwdTruncated + iosmSegment;\n\t\t} else {\n\t\t\tpwdLine = truncateToWidth(theme.fg(\"dim\", pwd), width, theme.fg(\"dim\", \"...\"));\n\t\t}\n\t\tconst lines = [pwdLine, statsLine];\n\n\t\t// Add extension statuses on a single line, sorted by key alphabetically\n\t\tconst extensionStatuses = this.footerData.getExtensionStatuses();\n\t\tif (extensionStatuses.size > 0) {\n\t\t\tconst sortedStatuses = Array.from(extensionStatuses.entries())\n\t\t\t\t.sort(([a], [b]) => a.localeCompare(b))\n\t\t\t\t.map(([, text]) => sanitizeStatusText(text));\n\t\t\tconst statusLine = sortedStatuses.join(\" \");\n\t\t\t// Truncate to terminal width with dim ellipsis for consistency with footer style\n\t\t\tlines.push(truncateToWidth(statusLine, width, theme.fg(\"dim\", \"...\")));\n\t\t}\n\n\t\treturn lines;\n\t}\n}\n"]}
@@ -16,6 +16,7 @@ export declare class LoginDialogComponent extends Container implements Focusable
16
16
  constructor(tui: TUI, providerId: string, onComplete: (success: boolean, message?: string) => void);
17
17
  get signal(): AbortSignal;
18
18
  private cancel;
19
+ private tryOpenBrowser;
19
20
  /**
20
21
  * Called by onAuth callback - show URL and optional instructions
21
22
  */
@@ -1 +1 @@
1
- {"version":3,"file":"login-dialog.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/login-dialog.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,KAAK,SAAS,EAA6C,KAAK,GAAG,EAAE,MAAM,sBAAsB,CAAC;AAMtH;;GAEG;AACH,qBAAa,oBAAqB,SAAQ,SAAU,YAAW,SAAS;IAqBtE,OAAO,CAAC,UAAU;IApBnB,OAAO,CAAC,gBAAgB,CAAY;IACpC,OAAO,CAAC,KAAK,CAAQ;IACrB,OAAO,CAAC,GAAG,CAAM;IACjB,OAAO,CAAC,eAAe,CAAyB;IAChD,OAAO,CAAC,aAAa,CAAC,CAA0B;IAChD,OAAO,CAAC,aAAa,CAAC,CAAyB;IAG/C,OAAO,CAAC,QAAQ,CAAS;IACzB,IAAI,OAAO,IAAI,OAAO,CAErB;IACD,IAAI,OAAO,CAAC,KAAK,EAAE,OAAO,EAGzB;gBAGA,GAAG,EAAE,GAAG,EACR,UAAU,EAAE,MAAM,EACV,UAAU,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,MAAM,KAAK,IAAI;IAmCjE,IAAI,MAAM,IAAI,WAAW,CAExB;IAED,OAAO,CAAC,MAAM;IAUd;;OAEG;IACH,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI;IAqBlD;;OAEG;IACH,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAahD;;;OAGG;IACH,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAoBlE;;OAEG;IACH,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAOlC;;OAEG;IACH,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAKnC,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;CAW/B"}
1
+ {"version":3,"file":"login-dialog.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/login-dialog.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,KAAK,SAAS,EAA6C,KAAK,GAAG,EAAE,MAAM,sBAAsB,CAAC;AAMtH;;GAEG;AACH,qBAAa,oBAAqB,SAAQ,SAAU,YAAW,SAAS;IAqBtE,OAAO,CAAC,UAAU;IApBnB,OAAO,CAAC,gBAAgB,CAAY;IACpC,OAAO,CAAC,KAAK,CAAQ;IACrB,OAAO,CAAC,GAAG,CAAM;IACjB,OAAO,CAAC,eAAe,CAAyB;IAChD,OAAO,CAAC,aAAa,CAAC,CAA0B;IAChD,OAAO,CAAC,aAAa,CAAC,CAAyB;IAG/C,OAAO,CAAC,QAAQ,CAAS;IACzB,IAAI,OAAO,IAAI,OAAO,CAErB;IACD,IAAI,OAAO,CAAC,KAAK,EAAE,OAAO,EAGzB;gBAGA,GAAG,EAAE,GAAG,EACR,UAAU,EAAE,MAAM,EACV,UAAU,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,MAAM,KAAK,IAAI;IAmCjE,IAAI,MAAM,IAAI,WAAW,CAExB;IAED,OAAO,CAAC,MAAM;IAUd,OAAO,CAAC,cAAc;IAyBtB;;OAEG;IACH,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI;IAmBlD;;OAEG;IACH,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAahD;;;OAGG;IACH,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAoBlE;;OAEG;IACH,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAOlC;;OAEG;IACH,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAKnC,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;CAW/B"}
@@ -1,6 +1,6 @@
1
1
  import { getOAuthProviders } from "@mariozechner/pi-ai/oauth";
2
2
  import { Container, getEditorKeybindings, Input, Spacer, Text } from "@mariozechner/pi-tui";
3
- import { exec } from "child_process";
3
+ import { spawn } from "node:child_process";
4
4
  import { theme } from "../theme/theme.js";
5
5
  import { DynamicBorder } from "./dynamic-border.js";
6
6
  import { keyHint } from "./keybinding-hints.js";
@@ -58,6 +58,31 @@ export class LoginDialogComponent extends Container {
58
58
  }
59
59
  this.onComplete(false, "Login cancelled");
60
60
  }
61
+ tryOpenBrowser(url) {
62
+ const safeUrl = url.trim();
63
+ if (!safeUrl)
64
+ return;
65
+ const closeChild = (command, args) => {
66
+ const child = spawn(command, args, {
67
+ stdio: "ignore",
68
+ detached: true,
69
+ });
70
+ child.on("error", () => {
71
+ // best effort: hyperlink in TUI is still available for manual open
72
+ });
73
+ child.unref();
74
+ };
75
+ if (process.platform === "darwin") {
76
+ closeChild("open", [safeUrl]);
77
+ return;
78
+ }
79
+ if (process.platform === "win32") {
80
+ // Use cmd built-in `start` without interpolating user input into a shell string.
81
+ closeChild("cmd.exe", ["/d", "/s", "/c", "start", "", safeUrl]);
82
+ return;
83
+ }
84
+ closeChild("xdg-open", [safeUrl]);
85
+ }
61
86
  /**
62
87
  * Called by onAuth callback - show URL and optional instructions
63
88
  */
@@ -72,9 +97,7 @@ export class LoginDialogComponent extends Container {
72
97
  this.contentContainer.addChild(new Spacer(1));
73
98
  this.contentContainer.addChild(new Text(theme.fg("warning", instructions), 1, 0));
74
99
  }
75
- // Try to open browser
76
- const openCmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
77
- exec(`${openCmd} "${url}"`);
100
+ this.tryOpenBrowser(url);
78
101
  this.tui.requestRender();
79
102
  }
80
103
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"login-dialog.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/login-dialog.ts"],"names":[],"mappings":"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","sourcesContent":["import { getOAuthProviders } from \"@mariozechner/pi-ai/oauth\";\nimport { Container, type Focusable, getEditorKeybindings, Input, Spacer, Text, type TUI } from \"@mariozechner/pi-tui\";\nimport { exec } from \"child_process\";\nimport { theme } from \"../theme/theme.js\";\nimport { DynamicBorder } from \"./dynamic-border.js\";\nimport { keyHint } from \"./keybinding-hints.js\";\n\n/**\n * Login dialog component - replaces editor during OAuth login flow\n */\nexport class LoginDialogComponent extends Container implements Focusable {\n\tprivate contentContainer: Container;\n\tprivate input: Input;\n\tprivate tui: TUI;\n\tprivate abortController = new AbortController();\n\tprivate inputResolver?: (value: string) => void;\n\tprivate inputRejecter?: (error: Error) => void;\n\n\t// Focusable implementation - propagate to input for IME cursor positioning\n\tprivate _focused = false;\n\tget focused(): boolean {\n\t\treturn this._focused;\n\t}\n\tset focused(value: boolean) {\n\t\tthis._focused = value;\n\t\tthis.input.focused = value;\n\t}\n\n\tconstructor(\n\t\ttui: TUI,\n\t\tproviderId: string,\n\t\tprivate onComplete: (success: boolean, message?: string) => void,\n\t) {\n\t\tsuper();\n\t\tthis.tui = tui;\n\n\t\tconst providerInfo = getOAuthProviders().find((p) => p.id === providerId);\n\t\tconst providerName = providerInfo?.name || providerId;\n\n\t\t// Top border\n\t\tthis.addChild(new DynamicBorder());\n\n\t\t// Title\n\t\tthis.addChild(new Text(theme.fg(\"warning\", `Login to ${providerName}`), 1, 0));\n\n\t\t// Dynamic content area\n\t\tthis.contentContainer = new Container();\n\t\tthis.addChild(this.contentContainer);\n\n\t\t// Input (always present, used when needed)\n\t\tthis.input = new Input();\n\t\tthis.input.onSubmit = () => {\n\t\t\tif (this.inputResolver) {\n\t\t\t\tthis.inputResolver(this.input.getValue());\n\t\t\t\tthis.inputResolver = undefined;\n\t\t\t\tthis.inputRejecter = undefined;\n\t\t\t}\n\t\t};\n\t\tthis.input.onEscape = () => {\n\t\t\tthis.cancel();\n\t\t};\n\n\t\t// Bottom border\n\t\tthis.addChild(new DynamicBorder());\n\t}\n\n\tget signal(): AbortSignal {\n\t\treturn this.abortController.signal;\n\t}\n\n\tprivate cancel(): void {\n\t\tthis.abortController.abort();\n\t\tif (this.inputRejecter) {\n\t\t\tthis.inputRejecter(new Error(\"Login cancelled\"));\n\t\t\tthis.inputResolver = undefined;\n\t\t\tthis.inputRejecter = undefined;\n\t\t}\n\t\tthis.onComplete(false, \"Login cancelled\");\n\t}\n\n\t/**\n\t * Called by onAuth callback - show URL and optional instructions\n\t */\n\tshowAuth(url: string, instructions?: string): void {\n\t\tthis.contentContainer.clear();\n\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"accent\", url), 1, 0));\n\n\t\tconst clickHint = process.platform === \"darwin\" ? \"Cmd+click to open\" : \"Ctrl+click to open\";\n\t\tconst hyperlink = `\\x1b]8;;${url}\\x07${clickHint}\\x1b]8;;\\x07`;\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"dim\", hyperlink), 1, 0));\n\n\t\tif (instructions) {\n\t\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\t\tthis.contentContainer.addChild(new Text(theme.fg(\"warning\", instructions), 1, 0));\n\t\t}\n\n\t\t// Try to open browser\n\t\tconst openCmd = process.platform === \"darwin\" ? \"open\" : process.platform === \"win32\" ? \"start\" : \"xdg-open\";\n\t\texec(`${openCmd} \"${url}\"`);\n\n\t\tthis.tui.requestRender();\n\t}\n\n\t/**\n\t * Show input for manual code/URL entry (for callback server providers)\n\t */\n\tshowManualInput(prompt: string): Promise<string> {\n\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"dim\", prompt), 1, 0));\n\t\tthis.contentContainer.addChild(this.input);\n\t\tthis.contentContainer.addChild(new Text(`(${keyHint(\"selectCancel\", \"to cancel\")})`, 1, 0));\n\t\tthis.tui.requestRender();\n\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tthis.inputResolver = resolve;\n\t\t\tthis.inputRejecter = reject;\n\t\t});\n\t}\n\n\t/**\n\t * Called by onPrompt callback - show prompt and wait for input\n\t * Note: Does NOT clear content, appends to existing (preserves URL from showAuth)\n\t */\n\tshowPrompt(message: string, placeholder?: string): Promise<string> {\n\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"text\", message), 1, 0));\n\t\tif (placeholder) {\n\t\t\tthis.contentContainer.addChild(new Text(theme.fg(\"dim\", `e.g., ${placeholder}`), 1, 0));\n\t\t}\n\t\tthis.contentContainer.addChild(this.input);\n\t\tthis.contentContainer.addChild(\n\t\t\tnew Text(`(${keyHint(\"selectCancel\", \"to cancel,\")} ${keyHint(\"selectConfirm\", \"to submit\")})`, 1, 0),\n\t\t);\n\n\t\tthis.input.setValue(\"\");\n\t\tthis.tui.requestRender();\n\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tthis.inputResolver = resolve;\n\t\t\tthis.inputRejecter = reject;\n\t\t});\n\t}\n\n\t/**\n\t * Show waiting message (for polling flows like GitHub Copilot)\n\t */\n\tshowWaiting(message: string): void {\n\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"dim\", message), 1, 0));\n\t\tthis.contentContainer.addChild(new Text(`(${keyHint(\"selectCancel\", \"to cancel\")})`, 1, 0));\n\t\tthis.tui.requestRender();\n\t}\n\n\t/**\n\t * Called by onProgress callback\n\t */\n\tshowProgress(message: string): void {\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"dim\", message), 1, 0));\n\t\tthis.tui.requestRender();\n\t}\n\n\thandleInput(data: string): void {\n\t\tconst kb = getEditorKeybindings();\n\n\t\tif (kb.matches(data, \"selectCancel\")) {\n\t\t\tthis.cancel();\n\t\t\treturn;\n\t\t}\n\n\t\t// Pass to input\n\t\tthis.input.handleInput(data);\n\t}\n}\n"]}
1
+ {"version":3,"file":"login-dialog.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/login-dialog.ts"],"names":[],"mappings":"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","sourcesContent":["import { getOAuthProviders } from \"@mariozechner/pi-ai/oauth\";\nimport { Container, type Focusable, getEditorKeybindings, Input, Spacer, Text, type TUI } from \"@mariozechner/pi-tui\";\nimport { spawn } from \"node:child_process\";\nimport { theme } from \"../theme/theme.js\";\nimport { DynamicBorder } from \"./dynamic-border.js\";\nimport { keyHint } from \"./keybinding-hints.js\";\n\n/**\n * Login dialog component - replaces editor during OAuth login flow\n */\nexport class LoginDialogComponent extends Container implements Focusable {\n\tprivate contentContainer: Container;\n\tprivate input: Input;\n\tprivate tui: TUI;\n\tprivate abortController = new AbortController();\n\tprivate inputResolver?: (value: string) => void;\n\tprivate inputRejecter?: (error: Error) => void;\n\n\t// Focusable implementation - propagate to input for IME cursor positioning\n\tprivate _focused = false;\n\tget focused(): boolean {\n\t\treturn this._focused;\n\t}\n\tset focused(value: boolean) {\n\t\tthis._focused = value;\n\t\tthis.input.focused = value;\n\t}\n\n\tconstructor(\n\t\ttui: TUI,\n\t\tproviderId: string,\n\t\tprivate onComplete: (success: boolean, message?: string) => void,\n\t) {\n\t\tsuper();\n\t\tthis.tui = tui;\n\n\t\tconst providerInfo = getOAuthProviders().find((p) => p.id === providerId);\n\t\tconst providerName = providerInfo?.name || providerId;\n\n\t\t// Top border\n\t\tthis.addChild(new DynamicBorder());\n\n\t\t// Title\n\t\tthis.addChild(new Text(theme.fg(\"warning\", `Login to ${providerName}`), 1, 0));\n\n\t\t// Dynamic content area\n\t\tthis.contentContainer = new Container();\n\t\tthis.addChild(this.contentContainer);\n\n\t\t// Input (always present, used when needed)\n\t\tthis.input = new Input();\n\t\tthis.input.onSubmit = () => {\n\t\t\tif (this.inputResolver) {\n\t\t\t\tthis.inputResolver(this.input.getValue());\n\t\t\t\tthis.inputResolver = undefined;\n\t\t\t\tthis.inputRejecter = undefined;\n\t\t\t}\n\t\t};\n\t\tthis.input.onEscape = () => {\n\t\t\tthis.cancel();\n\t\t};\n\n\t\t// Bottom border\n\t\tthis.addChild(new DynamicBorder());\n\t}\n\n\tget signal(): AbortSignal {\n\t\treturn this.abortController.signal;\n\t}\n\n\tprivate cancel(): void {\n\t\tthis.abortController.abort();\n\t\tif (this.inputRejecter) {\n\t\t\tthis.inputRejecter(new Error(\"Login cancelled\"));\n\t\t\tthis.inputResolver = undefined;\n\t\t\tthis.inputRejecter = undefined;\n\t\t}\n\t\tthis.onComplete(false, \"Login cancelled\");\n\t}\n\n\tprivate tryOpenBrowser(url: string): void {\n\t\tconst safeUrl = url.trim();\n\t\tif (!safeUrl) return;\n\t\tconst closeChild = (command: string, args: string[]): void => {\n\t\t\tconst child = spawn(command, args, {\n\t\t\t\tstdio: \"ignore\",\n\t\t\t\tdetached: true,\n\t\t\t});\n\t\t\tchild.on(\"error\", () => {\n\t\t\t\t// best effort: hyperlink in TUI is still available for manual open\n\t\t\t});\n\t\t\tchild.unref();\n\t\t};\n\t\tif (process.platform === \"darwin\") {\n\t\t\tcloseChild(\"open\", [safeUrl]);\n\t\t\treturn;\n\t\t}\n\t\tif (process.platform === \"win32\") {\n\t\t\t// Use cmd built-in `start` without interpolating user input into a shell string.\n\t\t\tcloseChild(\"cmd.exe\", [\"/d\", \"/s\", \"/c\", \"start\", \"\", safeUrl]);\n\t\t\treturn;\n\t\t}\n\t\tcloseChild(\"xdg-open\", [safeUrl]);\n\t}\n\n\t/**\n\t * Called by onAuth callback - show URL and optional instructions\n\t */\n\tshowAuth(url: string, instructions?: string): void {\n\t\tthis.contentContainer.clear();\n\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"accent\", url), 1, 0));\n\n\t\tconst clickHint = process.platform === \"darwin\" ? \"Cmd+click to open\" : \"Ctrl+click to open\";\n\t\tconst hyperlink = `\\x1b]8;;${url}\\x07${clickHint}\\x1b]8;;\\x07`;\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"dim\", hyperlink), 1, 0));\n\n\t\tif (instructions) {\n\t\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\t\tthis.contentContainer.addChild(new Text(theme.fg(\"warning\", instructions), 1, 0));\n\t\t}\n\n\t\tthis.tryOpenBrowser(url);\n\n\t\tthis.tui.requestRender();\n\t}\n\n\t/**\n\t * Show input for manual code/URL entry (for callback server providers)\n\t */\n\tshowManualInput(prompt: string): Promise<string> {\n\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"dim\", prompt), 1, 0));\n\t\tthis.contentContainer.addChild(this.input);\n\t\tthis.contentContainer.addChild(new Text(`(${keyHint(\"selectCancel\", \"to cancel\")})`, 1, 0));\n\t\tthis.tui.requestRender();\n\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tthis.inputResolver = resolve;\n\t\t\tthis.inputRejecter = reject;\n\t\t});\n\t}\n\n\t/**\n\t * Called by onPrompt callback - show prompt and wait for input\n\t * Note: Does NOT clear content, appends to existing (preserves URL from showAuth)\n\t */\n\tshowPrompt(message: string, placeholder?: string): Promise<string> {\n\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"text\", message), 1, 0));\n\t\tif (placeholder) {\n\t\t\tthis.contentContainer.addChild(new Text(theme.fg(\"dim\", `e.g., ${placeholder}`), 1, 0));\n\t\t}\n\t\tthis.contentContainer.addChild(this.input);\n\t\tthis.contentContainer.addChild(\n\t\t\tnew Text(`(${keyHint(\"selectCancel\", \"to cancel,\")} ${keyHint(\"selectConfirm\", \"to submit\")})`, 1, 0),\n\t\t);\n\n\t\tthis.input.setValue(\"\");\n\t\tthis.tui.requestRender();\n\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tthis.inputResolver = resolve;\n\t\t\tthis.inputRejecter = reject;\n\t\t});\n\t}\n\n\t/**\n\t * Show waiting message (for polling flows like GitHub Copilot)\n\t */\n\tshowWaiting(message: string): void {\n\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"dim\", message), 1, 0));\n\t\tthis.contentContainer.addChild(new Text(`(${keyHint(\"selectCancel\", \"to cancel\")})`, 1, 0));\n\t\tthis.tui.requestRender();\n\t}\n\n\t/**\n\t * Called by onProgress callback\n\t */\n\tshowProgress(message: string): void {\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"dim\", message), 1, 0));\n\t\tthis.tui.requestRender();\n\t}\n\n\thandleInput(data: string): void {\n\t\tconst kb = getEditorKeybindings();\n\n\t\tif (kb.matches(data, \"selectCancel\")) {\n\t\t\tthis.cancel();\n\t\t\treturn;\n\t\t}\n\n\t\t// Pass to input\n\t\tthis.input.handleInput(data);\n\t}\n}\n"]}
@@ -1,18 +1,30 @@
1
1
  import { Container } from "@mariozechner/pi-tui";
2
2
  import type { AuthStorage } from "../../../core/auth-storage.js";
3
+ export type LoginProviderKind = "oauth" | "api_key";
4
+ export type LoginProviderOption = {
5
+ id: string;
6
+ name: string;
7
+ kind: LoginProviderKind;
8
+ };
3
9
  /**
4
10
  * Component that renders an OAuth provider selector
5
11
  */
6
12
  export declare class OAuthSelectorComponent extends Container {
13
+ private searchInput;
14
+ private summaryText;
7
15
  private listContainer;
8
16
  private allProviders;
17
+ private filteredProviders;
9
18
  private selectedIndex;
19
+ private maxVisible;
10
20
  private mode;
11
21
  private authStorage;
12
22
  private onSelectCallback;
13
23
  private onCancelCallback;
14
- constructor(mode: "login" | "logout", authStorage: AuthStorage, onSelect: (providerId: string) => void, onCancel: () => void);
24
+ private apiKeyProviders;
25
+ constructor(mode: "login" | "logout", authStorage: AuthStorage, onSelect: (provider: LoginProviderOption) => void, onCancel: () => void, apiKeyProviders?: LoginProviderOption[]);
15
26
  private loadProviders;
27
+ private applyFilter;
16
28
  private updateList;
17
29
  handleInput(keyData: string): void;
18
30
  }
@@ -1 +1 @@
1
- {"version":3,"file":"oauth-selector.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/oauth-selector.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAA+C,MAAM,sBAAsB,CAAC;AAC9F,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC;AAcjE;;GAEG;AACH,qBAAa,sBAAuB,SAAQ,SAAS;IACpD,OAAO,CAAC,aAAa,CAAY;IACjC,OAAO,CAAC,YAAY,CAA6B;IACjD,OAAO,CAAC,aAAa,CAAa;IAClC,OAAO,CAAC,IAAI,CAAqB;IACjC,OAAO,CAAC,WAAW,CAAc;IACjC,OAAO,CAAC,gBAAgB,CAA+B;IACvD,OAAO,CAAC,gBAAgB,CAAa;gBAGpC,IAAI,EAAE,OAAO,GAAG,QAAQ,EACxB,WAAW,EAAE,WAAW,EACxB,QAAQ,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,EACtC,QAAQ,EAAE,MAAM,IAAI;IAqCrB,OAAO,CAAC,aAAa;IAkCrB,OAAO,CAAC,UAAU;IA6ClB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;CAwBlC"}
1
+ {"version":3,"file":"oauth-selector.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/oauth-selector.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAyE,MAAM,sBAAsB,CAAC;AACxH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC;AAIjE,MAAM,MAAM,iBAAiB,GAAG,OAAO,GAAG,SAAS,CAAC;AAEpD,MAAM,MAAM,mBAAmB,GAAG;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,iBAAiB,CAAC;CACxB,CAAC;AAMF;;GAEG;AACH,qBAAa,sBAAuB,SAAQ,SAAS;IACpD,OAAO,CAAC,WAAW,CAAQ;IAC3B,OAAO,CAAC,WAAW,CAAO;IAC1B,OAAO,CAAC,aAAa,CAAY;IACjC,OAAO,CAAC,YAAY,CAA6B;IACjD,OAAO,CAAC,iBAAiB,CAA6B;IACtD,OAAO,CAAC,aAAa,CAAa;IAClC,OAAO,CAAC,UAAU,CAAc;IAChC,OAAO,CAAC,IAAI,CAAqB;IACjC,OAAO,CAAC,WAAW,CAAc;IACjC,OAAO,CAAC,gBAAgB,CAA0C;IAClE,OAAO,CAAC,gBAAgB,CAAa;IACrC,OAAO,CAAC,eAAe,CAAwB;gBAG9C,IAAI,EAAE,OAAO,GAAG,QAAQ,EACxB,WAAW,EAAE,WAAW,EACxB,QAAQ,EAAE,CAAC,QAAQ,EAAE,mBAAmB,KAAK,IAAI,EACjD,QAAQ,EAAE,MAAM,IAAI,EACpB,eAAe,GAAE,mBAAmB,EAAoC;IA0CzE,OAAO,CAAC,aAAa;IAgDrB,OAAO,CAAC,WAAW;IAYnB,OAAO,CAAC,UAAU;IA6DlB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;CA0ClC"}
@@ -1,22 +1,25 @@
1
1
  import { getOAuthProviders } from "@mariozechner/pi-ai/oauth";
2
- import { Container, getEditorKeybindings, Spacer, TruncatedText } from "@mariozechner/pi-tui";
2
+ import { Container, fuzzyFilter, getEditorKeybindings, Input, Spacer, Text, TruncatedText } from "@mariozechner/pi-tui";
3
3
  import { theme } from "../theme/theme.js";
4
4
  import { DynamicBorder } from "./dynamic-border.js";
5
- const API_KEY_LOGIN_PROVIDERS = [{ id: "openrouter", name: "OpenRouter", kind: "api_key" }];
5
+ const DEFAULT_API_KEY_LOGIN_PROVIDERS = [
6
+ { id: "openrouter", name: "OpenRouter", kind: "api_key" },
7
+ ];
6
8
  /**
7
9
  * Component that renders an OAuth provider selector
8
10
  */
9
11
  export class OAuthSelectorComponent extends Container {
10
- constructor(mode, authStorage, onSelect, onCancel) {
12
+ constructor(mode, authStorage, onSelect, onCancel, apiKeyProviders = DEFAULT_API_KEY_LOGIN_PROVIDERS) {
11
13
  super();
12
14
  this.allProviders = [];
15
+ this.filteredProviders = [];
13
16
  this.selectedIndex = 0;
17
+ this.maxVisible = 10;
14
18
  this.mode = mode;
15
19
  this.authStorage = authStorage;
16
20
  this.onSelectCallback = onSelect;
17
21
  this.onCancelCallback = onCancel;
18
- // Load all OAuth providers
19
- this.loadProviders();
22
+ this.apiKeyProviders = apiKeyProviders.filter((provider) => provider.kind === "api_key");
20
23
  // Add top border
21
24
  this.addChild(new DynamicBorder());
22
25
  this.addChild(new Spacer(1));
@@ -26,6 +29,11 @@ export class OAuthSelectorComponent extends Container {
26
29
  if (mode === "login") {
27
30
  this.addChild(new TruncatedText(theme.fg("muted", "OAuth + API key providers")));
28
31
  }
32
+ this.summaryText = new Text(theme.fg("muted", "Loading providers..."), 0, 0);
33
+ this.addChild(this.summaryText);
34
+ this.addChild(new Spacer(1));
35
+ this.searchInput = new Input();
36
+ this.addChild(this.searchInput);
29
37
  this.addChild(new Spacer(1));
30
38
  // Create list container
31
39
  this.listContainer = new Container();
@@ -34,6 +42,7 @@ export class OAuthSelectorComponent extends Container {
34
42
  // Add bottom border
35
43
  this.addChild(new DynamicBorder());
36
44
  // Initial render
45
+ this.loadProviders();
37
46
  this.updateList();
38
47
  }
39
48
  loadProviders() {
@@ -42,23 +51,32 @@ export class OAuthSelectorComponent extends Container {
42
51
  name: provider.name,
43
52
  kind: "oauth",
44
53
  }));
54
+ const oauthProviderIds = new Set(oauthProviders.map((provider) => provider.id));
45
55
  if (this.mode === "login") {
46
- const merged = [...oauthProviders];
47
- for (const provider of API_KEY_LOGIN_PROVIDERS) {
48
- if (!merged.some((candidate) => candidate.id === provider.id)) {
49
- merged.push(provider);
50
- }
51
- }
52
- this.allProviders = merged;
56
+ const apiKeyProviders = this.apiKeyProviders
57
+ .filter((provider) => !oauthProviderIds.has(provider.id))
58
+ .sort((a, b) => a.name.localeCompare(b.name));
59
+ this.allProviders = [...oauthProviders, ...apiKeyProviders];
60
+ this.applyFilter(this.searchInput.getValue());
53
61
  return;
54
62
  }
55
63
  // Logout mode: show only providers with saved credentials in auth.json.
56
64
  const savedCredentialsProviders = [...oauthProviders];
57
- for (const provider of API_KEY_LOGIN_PROVIDERS) {
58
- if (this.authStorage.has(provider.id) && !savedCredentialsProviders.some((candidate) => candidate.id === provider.id)) {
59
- savedCredentialsProviders.push(provider);
65
+ const apiProvidersById = new Map(this.apiKeyProviders
66
+ .filter((provider) => !oauthProviderIds.has(provider.id))
67
+ .map((provider) => [provider.id, provider]));
68
+ // Include ad-hoc API-key providers from auth.json so users can always logout.
69
+ for (const providerId of this.authStorage.list()) {
70
+ const credential = this.authStorage.get(providerId);
71
+ if (credential?.type === "api_key" && !oauthProviderIds.has(providerId) && !apiProvidersById.has(providerId)) {
72
+ apiProvidersById.set(providerId, {
73
+ id: providerId,
74
+ name: providerId,
75
+ kind: "api_key",
76
+ });
60
77
  }
61
78
  }
79
+ savedCredentialsProviders.push(...apiProvidersById.values());
62
80
  this.allProviders = savedCredentialsProviders.filter((provider) => {
63
81
  const credentials = this.authStorage.get(provider.id);
64
82
  if (!credentials)
@@ -67,11 +85,36 @@ export class OAuthSelectorComponent extends Container {
67
85
  return credentials.type === "oauth";
68
86
  return credentials.type === "api_key";
69
87
  });
88
+ this.allProviders.sort((a, b) => a.name.localeCompare(b.name));
89
+ this.applyFilter(this.searchInput.getValue());
90
+ }
91
+ applyFilter(query) {
92
+ const trimmed = query.trim();
93
+ this.filteredProviders = trimmed
94
+ ? fuzzyFilter(this.allProviders, trimmed, (provider) => `${provider.name} ${provider.id}`)
95
+ : this.allProviders;
96
+ if (this.filteredProviders.length === 0) {
97
+ this.selectedIndex = 0;
98
+ }
99
+ else {
100
+ this.selectedIndex = Math.min(this.selectedIndex, this.filteredProviders.length - 1);
101
+ }
70
102
  }
71
103
  updateList() {
72
104
  this.listContainer.clear();
73
- for (let i = 0; i < this.allProviders.length; i++) {
74
- const provider = this.allProviders[i];
105
+ const shown = this.filteredProviders.length;
106
+ const total = this.allProviders.length;
107
+ const summary = `${theme.fg("muted", "Showing")} ${theme.fg("accent", String(shown))}${theme.fg("muted", "/")}${theme.fg("muted", String(total))}`;
108
+ this.summaryText.setText(summary);
109
+ if (this.filteredProviders.length === 0) {
110
+ const message = this.allProviders.length === 0 ? "No providers available" : "No matching providers.";
111
+ this.listContainer.addChild(new TruncatedText(theme.fg("muted", ` ${message}`), 0, 0));
112
+ return;
113
+ }
114
+ const startIndex = Math.max(0, Math.min(this.selectedIndex - Math.floor(this.maxVisible / 2), Math.max(0, this.filteredProviders.length - this.maxVisible)));
115
+ const endIndex = Math.min(startIndex + this.maxVisible, this.filteredProviders.length);
116
+ for (let i = startIndex; i < endIndex; i++) {
117
+ const provider = this.filteredProviders[i];
75
118
  if (!provider)
76
119
  continue;
77
120
  const isSelected = i === this.selectedIndex;
@@ -98,37 +141,56 @@ export class OAuthSelectorComponent extends Container {
98
141
  }
99
142
  this.listContainer.addChild(new TruncatedText(line, 0, 0));
100
143
  }
101
- // Show "no providers" if empty
102
- if (this.allProviders.length === 0) {
103
- const message = this.mode === "login"
104
- ? "No providers available"
105
- : "No saved provider credentials. Use /login first.";
106
- this.listContainer.addChild(new TruncatedText(theme.fg("muted", ` ${message}`), 0, 0));
144
+ if (startIndex > 0 || endIndex < this.filteredProviders.length) {
145
+ this.listContainer.addChild(new Text(theme.fg("muted", ` (${this.selectedIndex + 1}/${this.filteredProviders.length})`), 0, 0));
107
146
  }
108
147
  }
109
148
  handleInput(keyData) {
110
149
  const kb = getEditorKeybindings();
111
150
  // Up arrow
112
151
  if (kb.matches(keyData, "selectUp")) {
113
- this.selectedIndex = Math.max(0, this.selectedIndex - 1);
152
+ if (this.filteredProviders.length === 0)
153
+ return;
154
+ this.selectedIndex = this.selectedIndex === 0 ? this.filteredProviders.length - 1 : this.selectedIndex - 1;
114
155
  this.updateList();
115
156
  }
116
157
  // Down arrow
117
158
  else if (kb.matches(keyData, "selectDown")) {
118
- this.selectedIndex = Math.min(this.allProviders.length - 1, this.selectedIndex + 1);
159
+ if (this.filteredProviders.length === 0)
160
+ return;
161
+ this.selectedIndex = this.selectedIndex === this.filteredProviders.length - 1 ? 0 : this.selectedIndex + 1;
162
+ this.updateList();
163
+ }
164
+ // Page up/down
165
+ else if (kb.matches(keyData, "selectPageUp")) {
166
+ if (this.filteredProviders.length === 0)
167
+ return;
168
+ this.selectedIndex = Math.max(0, this.selectedIndex - this.maxVisible);
169
+ this.updateList();
170
+ }
171
+ else if (kb.matches(keyData, "selectPageDown")) {
172
+ if (this.filteredProviders.length === 0)
173
+ return;
174
+ this.selectedIndex = Math.min(this.filteredProviders.length - 1, this.selectedIndex + this.maxVisible);
119
175
  this.updateList();
120
176
  }
121
177
  // Enter
122
178
  else if (kb.matches(keyData, "selectConfirm")) {
123
- const selectedProvider = this.allProviders[this.selectedIndex];
179
+ const selectedProvider = this.filteredProviders[this.selectedIndex];
124
180
  if (selectedProvider) {
125
- this.onSelectCallback(selectedProvider.id);
181
+ this.onSelectCallback(selectedProvider);
126
182
  }
127
183
  }
128
184
  // Escape or Ctrl+C
129
185
  else if (kb.matches(keyData, "selectCancel")) {
130
186
  this.onCancelCallback();
131
187
  }
188
+ // Pass everything else to search input
189
+ else {
190
+ this.searchInput.handleInput(keyData);
191
+ this.applyFilter(this.searchInput.getValue());
192
+ this.updateList();
193
+ }
132
194
  }
133
195
  }
134
196
  //# sourceMappingURL=oauth-selector.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"oauth-selector.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/oauth-selector.ts"],"names":[],"mappings":"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","sourcesContent":["import { getOAuthProviders } from \"@mariozechner/pi-ai/oauth\";\nimport { Container, getEditorKeybindings, Spacer, TruncatedText } from \"@mariozechner/pi-tui\";\nimport type { AuthStorage } from \"../../../core/auth-storage.js\";\nimport { theme } from \"../theme/theme.js\";\nimport { DynamicBorder } from \"./dynamic-border.js\";\n\ntype LoginProviderKind = \"oauth\" | \"api_key\";\n\ntype LoginProviderOption = {\n\tid: string;\n\tname: string;\n\tkind: LoginProviderKind;\n};\n\nconst API_KEY_LOGIN_PROVIDERS: LoginProviderOption[] = [{ id: \"openrouter\", name: \"OpenRouter\", kind: \"api_key\" }];\n\n/**\n * Component that renders an OAuth provider selector\n */\nexport class OAuthSelectorComponent extends Container {\n\tprivate listContainer: Container;\n\tprivate allProviders: LoginProviderOption[] = [];\n\tprivate selectedIndex: number = 0;\n\tprivate mode: \"login\" | \"logout\";\n\tprivate authStorage: AuthStorage;\n\tprivate onSelectCallback: (providerId: string) => void;\n\tprivate onCancelCallback: () => void;\n\n\tconstructor(\n\t\tmode: \"login\" | \"logout\",\n\t\tauthStorage: AuthStorage,\n\t\tonSelect: (providerId: string) => void,\n\t\tonCancel: () => void,\n\t) {\n\t\tsuper();\n\n\t\tthis.mode = mode;\n\t\tthis.authStorage = authStorage;\n\t\tthis.onSelectCallback = onSelect;\n\t\tthis.onCancelCallback = onCancel;\n\n\t\t// Load all OAuth providers\n\t\tthis.loadProviders();\n\n\t\t// Add top border\n\t\tthis.addChild(new DynamicBorder());\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Add title\n\t\tconst title = mode === \"login\" ? \"Select provider to authenticate:\" : \"Select provider to logout:\";\n\t\tthis.addChild(new TruncatedText(theme.bold(title)));\n\t\tif (mode === \"login\") {\n\t\t\tthis.addChild(new TruncatedText(theme.fg(\"muted\", \"OAuth + API key providers\")));\n\t\t}\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Create list container\n\t\tthis.listContainer = new Container();\n\t\tthis.addChild(this.listContainer);\n\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Add bottom border\n\t\tthis.addChild(new DynamicBorder());\n\n\t\t// Initial render\n\t\tthis.updateList();\n\t}\n\n\tprivate loadProviders(): void {\n\t\tconst oauthProviders: LoginProviderOption[] = getOAuthProviders().map((provider) => ({\n\t\t\tid: provider.id,\n\t\t\tname: provider.name,\n\t\t\tkind: \"oauth\",\n\t\t}));\n\n\t\tif (this.mode === \"login\") {\n\t\t\tconst merged = [...oauthProviders];\n\t\t\tfor (const provider of API_KEY_LOGIN_PROVIDERS) {\n\t\t\t\tif (!merged.some((candidate) => candidate.id === provider.id)) {\n\t\t\t\t\tmerged.push(provider);\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.allProviders = merged;\n\t\t\treturn;\n\t\t}\n\n\t\t// Logout mode: show only providers with saved credentials in auth.json.\n\t\tconst savedCredentialsProviders = [...oauthProviders];\n\t\tfor (const provider of API_KEY_LOGIN_PROVIDERS) {\n\t\t\tif (this.authStorage.has(provider.id) && !savedCredentialsProviders.some((candidate) => candidate.id === provider.id)) {\n\t\t\t\tsavedCredentialsProviders.push(provider);\n\t\t\t}\n\t\t}\n\n\t\tthis.allProviders = savedCredentialsProviders.filter((provider) => {\n\t\t\tconst credentials = this.authStorage.get(provider.id);\n\t\t\tif (!credentials) return false;\n\t\t\tif (provider.kind === \"oauth\") return credentials.type === \"oauth\";\n\t\t\treturn credentials.type === \"api_key\";\n\t\t});\n\t}\n\n\tprivate updateList(): void {\n\t\tthis.listContainer.clear();\n\n\t\tfor (let i = 0; i < this.allProviders.length; i++) {\n\t\t\tconst provider = this.allProviders[i];\n\t\t\tif (!provider) continue;\n\n\t\t\tconst isSelected = i === this.selectedIndex;\n\n\t\t\t// Check if user is authenticated for this provider\n\t\t\tconst credentials = this.authStorage.get(provider.id);\n\t\t\tconst isConfigured =\n\t\t\t\tprovider.kind === \"oauth\"\n\t\t\t\t\t? credentials?.type === \"oauth\"\n\t\t\t\t\t: credentials?.type === \"api_key\" || this.authStorage.hasAuth(provider.id);\n\t\t\tconst statusIndicator = isConfigured\n\t\t\t\t? provider.kind === \"oauth\"\n\t\t\t\t\t? theme.fg(\"success\", \" ✓ logged in\")\n\t\t\t\t\t: theme.fg(\"success\", \" ✓ key configured\")\n\t\t\t\t: \"\";\n\t\t\tconst providerKindBadge = provider.kind === \"api_key\" ? theme.fg(\"warning\", \" [API key]\") : \"\";\n\n\t\t\tlet line = \"\";\n\t\t\tif (isSelected) {\n\t\t\t\tconst prefix = theme.fg(\"accent\", \"→ \");\n\t\t\t\tconst text = theme.fg(\"accent\", provider.name);\n\t\t\t\tline = prefix + text + providerKindBadge + statusIndicator;\n\t\t\t} else {\n\t\t\t\tconst text = ` ${provider.name}`;\n\t\t\t\tline = text + providerKindBadge + statusIndicator;\n\t\t\t}\n\n\t\t\tthis.listContainer.addChild(new TruncatedText(line, 0, 0));\n\t\t}\n\n\t\t// Show \"no providers\" if empty\n\t\tif (this.allProviders.length === 0) {\n\t\t\tconst message =\n\t\t\t\tthis.mode === \"login\"\n\t\t\t\t\t? \"No providers available\"\n\t\t\t\t\t: \"No saved provider credentials. Use /login first.\";\n\t\t\tthis.listContainer.addChild(new TruncatedText(theme.fg(\"muted\", ` ${message}`), 0, 0));\n\t\t}\n\t}\n\n\thandleInput(keyData: string): void {\n\t\tconst kb = getEditorKeybindings();\n\t\t// Up arrow\n\t\tif (kb.matches(keyData, \"selectUp\")) {\n\t\t\tthis.selectedIndex = Math.max(0, this.selectedIndex - 1);\n\t\t\tthis.updateList();\n\t\t}\n\t\t// Down arrow\n\t\telse if (kb.matches(keyData, \"selectDown\")) {\n\t\t\tthis.selectedIndex = Math.min(this.allProviders.length - 1, this.selectedIndex + 1);\n\t\t\tthis.updateList();\n\t\t}\n\t\t// Enter\n\t\telse if (kb.matches(keyData, \"selectConfirm\")) {\n\t\t\tconst selectedProvider = this.allProviders[this.selectedIndex];\n\t\t\tif (selectedProvider) {\n\t\t\t\tthis.onSelectCallback(selectedProvider.id);\n\t\t\t}\n\t\t}\n\t\t// Escape or Ctrl+C\n\t\telse if (kb.matches(keyData, \"selectCancel\")) {\n\t\t\tthis.onCancelCallback();\n\t\t}\n\t}\n}\n"]}
1
+ {"version":3,"file":"oauth-selector.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/oauth-selector.ts"],"names":[],"mappings":"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","sourcesContent":["import { getOAuthProviders } from \"@mariozechner/pi-ai/oauth\";\nimport { Container, fuzzyFilter, getEditorKeybindings, Input, Spacer, Text, TruncatedText } from \"@mariozechner/pi-tui\";\nimport type { AuthStorage } from \"../../../core/auth-storage.js\";\nimport { theme } from \"../theme/theme.js\";\nimport { DynamicBorder } from \"./dynamic-border.js\";\n\nexport type LoginProviderKind = \"oauth\" | \"api_key\";\n\nexport type LoginProviderOption = {\n\tid: string;\n\tname: string;\n\tkind: LoginProviderKind;\n};\n\nconst DEFAULT_API_KEY_LOGIN_PROVIDERS: LoginProviderOption[] = [\n\t{ id: \"openrouter\", name: \"OpenRouter\", kind: \"api_key\" },\n];\n\n/**\n * Component that renders an OAuth provider selector\n */\nexport class OAuthSelectorComponent extends Container {\n\tprivate searchInput: Input;\n\tprivate summaryText: Text;\n\tprivate listContainer: Container;\n\tprivate allProviders: LoginProviderOption[] = [];\n\tprivate filteredProviders: LoginProviderOption[] = [];\n\tprivate selectedIndex: number = 0;\n\tprivate maxVisible: number = 10;\n\tprivate mode: \"login\" | \"logout\";\n\tprivate authStorage: AuthStorage;\n\tprivate onSelectCallback: (provider: LoginProviderOption) => void;\n\tprivate onCancelCallback: () => void;\n\tprivate apiKeyProviders: LoginProviderOption[];\n\n\tconstructor(\n\t\tmode: \"login\" | \"logout\",\n\t\tauthStorage: AuthStorage,\n\t\tonSelect: (provider: LoginProviderOption) => void,\n\t\tonCancel: () => void,\n\t\tapiKeyProviders: LoginProviderOption[] = DEFAULT_API_KEY_LOGIN_PROVIDERS,\n\t) {\n\t\tsuper();\n\n\t\tthis.mode = mode;\n\t\tthis.authStorage = authStorage;\n\t\tthis.onSelectCallback = onSelect;\n\t\tthis.onCancelCallback = onCancel;\n\t\tthis.apiKeyProviders = apiKeyProviders.filter((provider) => provider.kind === \"api_key\");\n\n\t\t// Add top border\n\t\tthis.addChild(new DynamicBorder());\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Add title\n\t\tconst title = mode === \"login\" ? \"Select provider to authenticate:\" : \"Select provider to logout:\";\n\t\tthis.addChild(new TruncatedText(theme.bold(title)));\n\t\tif (mode === \"login\") {\n\t\t\tthis.addChild(new TruncatedText(theme.fg(\"muted\", \"OAuth + API key providers\")));\n\t\t}\n\t\tthis.summaryText = new Text(theme.fg(\"muted\", \"Loading providers...\"), 0, 0);\n\t\tthis.addChild(this.summaryText);\n\t\tthis.addChild(new Spacer(1));\n\n\t\tthis.searchInput = new Input();\n\t\tthis.addChild(this.searchInput);\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Create list container\n\t\tthis.listContainer = new Container();\n\t\tthis.addChild(this.listContainer);\n\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Add bottom border\n\t\tthis.addChild(new DynamicBorder());\n\n\t\t// Initial render\n\t\tthis.loadProviders();\n\t\tthis.updateList();\n\t}\n\n\tprivate loadProviders(): void {\n\t\tconst oauthProviders: LoginProviderOption[] = getOAuthProviders().map((provider) => ({\n\t\t\tid: provider.id,\n\t\t\tname: provider.name,\n\t\t\tkind: \"oauth\",\n\t\t}));\n\t\tconst oauthProviderIds = new Set(oauthProviders.map((provider) => provider.id));\n\n\t\tif (this.mode === \"login\") {\n\t\t\tconst apiKeyProviders = this.apiKeyProviders\n\t\t\t\t.filter((provider) => !oauthProviderIds.has(provider.id))\n\t\t\t\t.sort((a, b) => a.name.localeCompare(b.name));\n\t\t\tthis.allProviders = [...oauthProviders, ...apiKeyProviders];\n\t\t\tthis.applyFilter(this.searchInput.getValue());\n\t\t\treturn;\n\t\t}\n\n\t\t// Logout mode: show only providers with saved credentials in auth.json.\n\t\tconst savedCredentialsProviders = [...oauthProviders];\n\t\tconst apiProvidersById = new Map(\n\t\t\tthis.apiKeyProviders\n\t\t\t\t.filter((provider) => !oauthProviderIds.has(provider.id))\n\t\t\t\t.map((provider) => [provider.id, provider] as const),\n\t\t);\n\n\t\t// Include ad-hoc API-key providers from auth.json so users can always logout.\n\t\tfor (const providerId of this.authStorage.list()) {\n\t\t\tconst credential = this.authStorage.get(providerId);\n\t\t\tif (credential?.type === \"api_key\" && !oauthProviderIds.has(providerId) && !apiProvidersById.has(providerId)) {\n\t\t\t\tapiProvidersById.set(providerId, {\n\t\t\t\t\tid: providerId,\n\t\t\t\t\tname: providerId,\n\t\t\t\t\tkind: \"api_key\",\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t\tsavedCredentialsProviders.push(...apiProvidersById.values());\n\n\t\tthis.allProviders = savedCredentialsProviders.filter((provider) => {\n\t\t\tconst credentials = this.authStorage.get(provider.id);\n\t\t\tif (!credentials) return false;\n\t\t\tif (provider.kind === \"oauth\") return credentials.type === \"oauth\";\n\t\t\treturn credentials.type === \"api_key\";\n\t\t});\n\t\tthis.allProviders.sort((a, b) => a.name.localeCompare(b.name));\n\t\tthis.applyFilter(this.searchInput.getValue());\n\t}\n\n\tprivate applyFilter(query: string): void {\n\t\tconst trimmed = query.trim();\n\t\tthis.filteredProviders = trimmed\n\t\t\t? fuzzyFilter(this.allProviders, trimmed, (provider) => `${provider.name} ${provider.id}`)\n\t\t\t: this.allProviders;\n\t\tif (this.filteredProviders.length === 0) {\n\t\t\tthis.selectedIndex = 0;\n\t\t} else {\n\t\t\tthis.selectedIndex = Math.min(this.selectedIndex, this.filteredProviders.length - 1);\n\t\t}\n\t}\n\n\tprivate updateList(): void {\n\t\tthis.listContainer.clear();\n\t\tconst shown = this.filteredProviders.length;\n\t\tconst total = this.allProviders.length;\n\t\tconst summary = `${theme.fg(\"muted\", \"Showing\")} ${theme.fg(\"accent\", String(shown))}${theme.fg(\"muted\", \"/\")}${theme.fg(\"muted\", String(total))}`;\n\t\tthis.summaryText.setText(summary);\n\n\t\tif (this.filteredProviders.length === 0) {\n\t\t\tconst message = this.allProviders.length === 0 ? \"No providers available\" : \"No matching providers.\";\n\t\t\tthis.listContainer.addChild(new TruncatedText(theme.fg(\"muted\", ` ${message}`), 0, 0));\n\t\t\treturn;\n\t\t}\n\n\t\tconst startIndex = Math.max(\n\t\t\t0,\n\t\t\tMath.min(\n\t\t\t\tthis.selectedIndex - Math.floor(this.maxVisible / 2),\n\t\t\t\tMath.max(0, this.filteredProviders.length - this.maxVisible),\n\t\t\t),\n\t\t);\n\t\tconst endIndex = Math.min(startIndex + this.maxVisible, this.filteredProviders.length);\n\n\t\tfor (let i = startIndex; i < endIndex; i++) {\n\t\t\tconst provider = this.filteredProviders[i];\n\t\t\tif (!provider) continue;\n\n\t\t\tconst isSelected = i === this.selectedIndex;\n\n\t\t\t// Check if user is authenticated for this provider\n\t\t\tconst credentials = this.authStorage.get(provider.id);\n\t\t\tconst isConfigured =\n\t\t\t\tprovider.kind === \"oauth\"\n\t\t\t\t\t? credentials?.type === \"oauth\"\n\t\t\t\t\t: credentials?.type === \"api_key\" || this.authStorage.hasAuth(provider.id);\n\t\t\tconst statusIndicator = isConfigured\n\t\t\t\t? provider.kind === \"oauth\"\n\t\t\t\t\t? theme.fg(\"success\", \" ✓ logged in\")\n\t\t\t\t\t: theme.fg(\"success\", \" ✓ key configured\")\n\t\t\t\t: \"\";\n\t\t\tconst providerKindBadge = provider.kind === \"api_key\" ? theme.fg(\"warning\", \" [API key]\") : \"\";\n\n\t\t\tlet line = \"\";\n\t\t\tif (isSelected) {\n\t\t\t\tconst prefix = theme.fg(\"accent\", \"→ \");\n\t\t\t\tconst text = theme.fg(\"accent\", provider.name);\n\t\t\t\tline = prefix + text + providerKindBadge + statusIndicator;\n\t\t\t} else {\n\t\t\t\tconst text = ` ${provider.name}`;\n\t\t\t\tline = text + providerKindBadge + statusIndicator;\n\t\t\t}\n\n\t\t\tthis.listContainer.addChild(new TruncatedText(line, 0, 0));\n\t\t}\n\n\t\tif (startIndex > 0 || endIndex < this.filteredProviders.length) {\n\t\t\tthis.listContainer.addChild(\n\t\t\t\tnew Text(theme.fg(\"muted\", ` (${this.selectedIndex + 1}/${this.filteredProviders.length})`), 0, 0),\n\t\t\t);\n\t\t}\n\t}\n\n\thandleInput(keyData: string): void {\n\t\tconst kb = getEditorKeybindings();\n\t\t// Up arrow\n\t\tif (kb.matches(keyData, \"selectUp\")) {\n\t\t\tif (this.filteredProviders.length === 0) return;\n\t\t\tthis.selectedIndex = this.selectedIndex === 0 ? this.filteredProviders.length - 1 : this.selectedIndex - 1;\n\t\t\tthis.updateList();\n\t\t}\n\t\t// Down arrow\n\t\telse if (kb.matches(keyData, \"selectDown\")) {\n\t\t\tif (this.filteredProviders.length === 0) return;\n\t\t\tthis.selectedIndex = this.selectedIndex === this.filteredProviders.length - 1 ? 0 : this.selectedIndex + 1;\n\t\t\tthis.updateList();\n\t\t}\n\t\t// Page up/down\n\t\telse if (kb.matches(keyData, \"selectPageUp\")) {\n\t\t\tif (this.filteredProviders.length === 0) return;\n\t\t\tthis.selectedIndex = Math.max(0, this.selectedIndex - this.maxVisible);\n\t\t\tthis.updateList();\n\t\t} else if (kb.matches(keyData, \"selectPageDown\")) {\n\t\t\tif (this.filteredProviders.length === 0) return;\n\t\t\tthis.selectedIndex = Math.min(this.filteredProviders.length - 1, this.selectedIndex + this.maxVisible);\n\t\t\tthis.updateList();\n\t\t}\n\t\t// Enter\n\t\telse if (kb.matches(keyData, \"selectConfirm\")) {\n\t\t\tconst selectedProvider = this.filteredProviders[this.selectedIndex];\n\t\t\tif (selectedProvider) {\n\t\t\t\tthis.onSelectCallback(selectedProvider);\n\t\t\t}\n\t\t}\n\t\t// Escape or Ctrl+C\n\t\telse if (kb.matches(keyData, \"selectCancel\")) {\n\t\t\tthis.onCancelCallback();\n\t\t}\n\t\t// Pass everything else to search input\n\t\telse {\n\t\t\tthis.searchInput.handleInput(keyData);\n\t\t\tthis.applyFilter(this.searchInput.getValue());\n\t\t\tthis.updateList();\n\t\t}\n\t}\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"subagent-message.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/subagent-message.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,GAAG,EAAgB,MAAM,sBAAsB,CAAC;AAGzD,MAAM,MAAM,kBAAkB,GAAG,QAAQ,GAAG,UAAU,GAAG,SAAS,GAAG,YAAY,CAAC;AAClF,MAAM,MAAM,sBAAsB,GAAG,SAAS,GAAG,SAAS,GAAG,MAAM,GAAG,QAAQ,CAAC;AAE/E,MAAM,WAAW,oBAAoB;IACpC,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,sBAAsB,CAAC;CAC/B;AAED,MAAM,WAAW,YAAY;IAC5B,2FAA2F;IAC3F,WAAW,EAAE,MAAM,CAAC;IACpB,oDAAoD;IACpD,OAAO,EAAE,MAAM,CAAC;IAChB,8CAA8C;IAC9C,MAAM,EAAE,SAAS,GAAG,MAAM,GAAG,OAAO,CAAC;IACrC,4EAA4E;IAC5E,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,+EAA+E;IAC/E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,sDAAsD;IACtD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iDAAiD;IACjD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,oDAAoD;IACpD,UAAU,CAAC,EAAE,kBAAkB,CAAC;IAChC,mDAAmD;IACnD,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,kCAAkC;IAClC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,+BAA+B;IAC/B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,kCAAkC;IAClC,SAAS,CAAC,EAAE,MAAM,GAAG,UAAU,CAAC;IAChC,4DAA4D;IAC5D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,4CAA4C;IAC5C,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,8CAA8C;IAC9C,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,4DAA4D;IAC5D,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,gDAAgD;IAChD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,mEAAmE;IACnE,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,+DAA+D;IAC/D,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,qDAAqD;IACrD,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,yDAAyD;IACzD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,oDAAoD;IACpD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,qDAAqD;IACrD,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,iDAAiD;IACjD,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,mDAAmD;IACnD,aAAa,CAAC,EAAE,oBAAoB,EAAE,CAAC;CACvC;AA4ID;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBAAa,wBAAyB,SAAQ,GAAG;gBACpC,IAAI,EAAE,YAAY;IAK9B;;;OAGG;IACH,MAAM,CAAC,IAAI,EAAE,YAAY,GAAG,IAAI;IAKvB,UAAU,IAAI,IAAI;IAI3B,OAAO,CAAC,aAAa;CAoJrB"}
1
+ {"version":3,"file":"subagent-message.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/subagent-message.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,GAAG,EAAgB,MAAM,sBAAsB,CAAC;AAGzD,MAAM,MAAM,kBAAkB,GAAG,QAAQ,GAAG,UAAU,GAAG,SAAS,GAAG,YAAY,CAAC;AAClF,MAAM,MAAM,sBAAsB,GAAG,SAAS,GAAG,SAAS,GAAG,MAAM,GAAG,QAAQ,CAAC;AAE/E,MAAM,WAAW,oBAAoB;IACpC,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,sBAAsB,CAAC;CAC/B;AAED,MAAM,WAAW,YAAY;IAC5B,2FAA2F;IAC3F,WAAW,EAAE,MAAM,CAAC;IACpB,oDAAoD;IACpD,OAAO,EAAE,MAAM,CAAC;IAChB,8CAA8C;IAC9C,MAAM,EAAE,SAAS,GAAG,MAAM,GAAG,OAAO,CAAC;IACrC,4EAA4E;IAC5E,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,+EAA+E;IAC/E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,sDAAsD;IACtD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iDAAiD;IACjD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,oDAAoD;IACpD,UAAU,CAAC,EAAE,kBAAkB,CAAC;IAChC,mDAAmD;IACnD,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,kCAAkC;IAClC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,+BAA+B;IAC/B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,kCAAkC;IAClC,SAAS,CAAC,EAAE,MAAM,GAAG,UAAU,CAAC;IAChC,4DAA4D;IAC5D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,4CAA4C;IAC5C,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,8CAA8C;IAC9C,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,4DAA4D;IAC5D,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,gDAAgD;IAChD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,mEAAmE;IACnE,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,+DAA+D;IAC/D,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,qDAAqD;IACrD,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,yDAAyD;IACzD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,oDAAoD;IACpD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,qDAAqD;IACrD,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,iDAAiD;IACjD,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,mDAAmD;IACnD,aAAa,CAAC,EAAE,oBAAoB,EAAE,CAAC;CACvC;AAwJD;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBAAa,wBAAyB,SAAQ,GAAG;gBACpC,IAAI,EAAE,YAAY;IAK9B;;;OAGG;IACH,MAAM,CAAC,IAAI,EAAE,YAAY,GAAG,IAAI;IAKvB,UAAU,IAAI,IAAI;IAI3B,OAAO,CAAC,aAAa;CAuJrB"}