chainlesschain 0.162.49 → 0.162.61

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 (174) hide show
  1. package/bin/chainlesschain.js +1 -1
  2. package/package.json +2 -2
  3. package/src/assets/web-panel/assets/{AIOps-DmdtNmWd.js → AIOps-BiB8WfIz.js} +1 -1
  4. package/src/assets/web-panel/assets/{ActionButton-DkuYVafg.js → ActionButton-NLBhC6jG.js} +1 -1
  5. package/src/assets/web-panel/assets/{Analytics-Ba_h8Tub.js → Analytics-k62j-xiL.js} +3 -3
  6. package/src/assets/web-panel/assets/{AppLayout-yb8Zm9MX.js → AppLayout-spr0Sm6J.js} +3 -3
  7. package/src/assets/web-panel/assets/{Audit-BGjex2fm.js → Audit-C3NHJos3.js} +1 -1
  8. package/src/assets/web-panel/assets/{Backup-IFQ2hOF2.js → Backup-C2V9tGqF.js} +1 -1
  9. package/src/assets/web-panel/assets/{BaseInput-W8AkPkrV.js → BaseInput-DeKm11mH.js} +1 -1
  10. package/src/assets/web-panel/assets/{Chat-BgI7t-iW.js → Chat-CHZ2CU7x.js} +6 -6
  11. package/src/assets/web-panel/assets/ChatBubbleRenderer-DEXSa7tC.js +1 -0
  12. package/src/assets/web-panel/assets/{Checkbox-DuWsZP4g.js → Checkbox-q6E9VeLr.js} +1 -1
  13. package/src/assets/web-panel/assets/{Codegen-DyoTNmYg.js → Codegen--4w4QpUI.js} +1 -1
  14. package/src/assets/web-panel/assets/{Col-DttmlDRk.js → Col-DLOkwTsj.js} +1 -1
  15. package/src/assets/web-panel/assets/{Community-D9nnIdKn.js → Community-B1LxJGfE.js} +1 -1
  16. package/src/assets/web-panel/assets/{Compact-C8KVQaHb.js → Compact-C_769oQZ.js} +1 -1
  17. package/src/assets/web-panel/assets/{Compliance-R2owqgjj.js → Compliance-zsI0s7vB.js} +1 -1
  18. package/src/assets/web-panel/assets/{Cowork-DwGMMjRn.js → Cowork-A1WA6whF.js} +2 -2
  19. package/src/assets/web-panel/assets/{Cron-BSTcN_lK.js → Cron-C3JDTyyd.js} +2 -2
  20. package/src/assets/web-panel/assets/{Crosschain-CTNuIbFD.js → Crosschain-D8O6uB86.js} +1 -1
  21. package/src/assets/web-panel/assets/{DID-CgApGsFP.js → DID-BpOebm5d.js} +2 -2
  22. package/src/assets/web-panel/assets/{Dashboard-D_OJ3UN5.js → Dashboard-Defso6kA.js} +2 -2
  23. package/src/assets/web-panel/assets/{Dropdown-B84Jwra_.js → Dropdown-Cv9BrwT_.js} +1 -1
  24. package/src/assets/web-panel/assets/{EmailListRenderer-Bv-YO-6y.js → EmailListRenderer-BWKHbh4C.js} +1 -1
  25. package/src/assets/web-panel/assets/{FamilyGuardDashboard-drgZ408Y.js → FamilyGuardDashboard--m_Ru7Ci.js} +1 -1
  26. package/src/assets/web-panel/assets/{Federation-CtzFkdW2.js → Federation-Bs6ZcAP0.js} +1 -1
  27. package/src/assets/web-panel/assets/{FormItemContext-BFAvNhl9.js → FormItemContext-CcAs3Acx.js} +1 -1
  28. package/src/assets/web-panel/assets/{GenericCardRenderer-DnuEyz_l.js → GenericCardRenderer-DI1oL4pK.js} +1 -1
  29. package/src/assets/web-panel/assets/{Git-jlHajmRJ.js → Git-C27t3-fW.js} +2 -2
  30. package/src/assets/web-panel/assets/{Governance-DmJC7PGL.js → Governance-Dr_syXc_.js} +1 -1
  31. package/src/assets/web-panel/assets/{Inference-B-u7xD2n.js → Inference-CWM8dIbA.js} +1 -1
  32. package/src/assets/web-panel/assets/{KnowledgeGraph-BaYCA2Cd.js → KnowledgeGraph--cFDUZv3.js} +1 -1
  33. package/src/assets/web-panel/assets/{Logs-DTNYQWfp.js → Logs-Cnn2_Onf.js} +2 -2
  34. package/src/assets/web-panel/assets/{Marketplace-CUu1xYvo.js → Marketplace-4T9ok3Gz.js} +1 -1
  35. package/src/assets/web-panel/assets/{McpTools-BmoeTyrC.js → McpTools-BQvZwqcN.js} +5 -5
  36. package/src/assets/web-panel/assets/{Memory-DxTU3QU7.js → Memory-BE9rPkM_.js} +2 -2
  37. package/src/assets/web-panel/assets/{MobileBridge-CpcOlKAD.js → MobileBridge-DJ3j5lXC.js} +2 -2
  38. package/src/assets/web-panel/assets/{MobileProjects-Bjh_z16l.js → MobileProjects-qasLvYdb.js} +1 -1
  39. package/src/assets/web-panel/assets/{Mtc-LfxwOm0x.js → Mtc-D3CSPTD9.js} +2 -2
  40. package/src/assets/web-panel/assets/{MtcAudit-D6A9Gjkh.js → MtcAudit-DaYVGCN5.js} +2 -2
  41. package/src/assets/web-panel/assets/{Multisig-Ch_jofPV.js → Multisig-Dz1c5r5w.js} +3 -3
  42. package/src/assets/web-panel/assets/{NLProgramming-Bkvogg0I.js → NLProgramming-BIKV_K-a.js} +1 -1
  43. package/src/assets/web-panel/assets/{Notes-C5t5Xihm.js → Notes-f6t-rmOa.js} +3 -3
  44. package/src/assets/web-panel/assets/{NotificationSettings-CTpDUNCb.js → NotificationSettings-DHLQh8Fy.js} +1 -1
  45. package/src/assets/web-panel/assets/OrderTableRenderer-CDMZ3o6i.js +1 -0
  46. package/src/assets/web-panel/assets/{Organization-Dr37BaXa.js → Organization-DIsL758p.js} +4 -4
  47. package/src/assets/web-panel/assets/{Overflow-ZGjsdP7N.js → Overflow-BMM7apnZ.js} +1 -1
  48. package/src/assets/web-panel/assets/{P2P-bWJU5Vxd.js → P2P-CTGMmTvi.js} +2 -2
  49. package/src/assets/web-panel/assets/{PdhVaultBrowser-BRVoW-ye.js → PdhVaultBrowser-DWwmm0k1.js} +3 -3
  50. package/src/assets/web-panel/assets/{Permissions-BOSnFZaC.js → Permissions-Bed5JxMx.js} +4 -4
  51. package/src/assets/web-panel/assets/{PersonalDataHub-X4SgjP6P.js → PersonalDataHub-CIiZhSM5.js} +4 -4
  52. package/src/assets/web-panel/assets/{Pipeline-DoJhB9bj.js → Pipeline-CtirPodz.js} +1 -1
  53. package/src/assets/web-panel/assets/{Privacy-OM9lDj-R.js → Privacy-DuXhXhE7.js} +1 -1
  54. package/src/assets/web-panel/assets/{ProjectInit-BXQEOmLn.js → ProjectInit-DZrnguBl.js} +2 -2
  55. package/src/assets/web-panel/assets/{ProjectSettings-DBXo3K5u.js → ProjectSettings-HIltqsJ1.js} +2 -2
  56. package/src/assets/web-panel/assets/{Projects-CJ4DBJlS.js → Projects-BWFkePg4.js} +1 -1
  57. package/src/assets/web-panel/assets/{Providers-Tk9SawmO.js → Providers-DEP0Jdvl.js} +1 -1
  58. package/src/assets/web-panel/assets/{QuickAsk-DRI1-nTC.js → QuickAsk-T2THoHNx.js} +1 -1
  59. package/src/assets/web-panel/assets/{Recommend-DtrIVTu9.js → Recommend-CvbxaSwm.js} +1 -1
  60. package/src/assets/web-panel/assets/{Reputation-DkH8ImwZ.js → Reputation-6Afy6tfp.js} +1 -1
  61. package/src/assets/web-panel/assets/{Row-DpA9dlvi.js → Row-DY8OPWaO.js} +1 -1
  62. package/src/assets/web-panel/assets/{RssFeed-DV3OhxWd.js → RssFeed-wDGWb9pZ.js} +3 -3
  63. package/src/assets/web-panel/assets/{Search-QxdntiQx.js → Search-D_zHAwZY.js} +1 -1
  64. package/src/assets/web-panel/assets/{Security-CGuEnrD2.js → Security-Czq7AlGG.js} +4 -4
  65. package/src/assets/web-panel/assets/{Services-BvwSSD8b.js → Services-Ac1g0ZcG.js} +2 -2
  66. package/src/assets/web-panel/assets/{Skeleton-sx_8L3-5.js → Skeleton-DXQ3eeSW.js} +1 -1
  67. package/src/assets/web-panel/assets/{Skills-dWOwxRsu.js → Skills-CWRioX4u.js} +1 -1
  68. package/src/assets/web-panel/assets/{Sla-zxXnfKrT.js → Sla-BlHthzfs.js} +1 -1
  69. package/src/assets/web-panel/assets/{SpeechSettings-CmFlcNjr.js → SpeechSettings-Ct240JmL.js} +1 -1
  70. package/src/assets/web-panel/assets/{SyncSettings-BeXeqURL.js → SyncSettings-BgDIt8Q-.js} +2 -2
  71. package/src/assets/web-panel/assets/Tasks-3PTmatJP.js +1 -0
  72. package/src/assets/web-panel/assets/{Templates-DlgR3XFH.js → Templates-Dp9QhyIw.js} +1 -1
  73. package/src/assets/web-panel/assets/{Tenant-0P8HgQaM.js → Tenant-CHTYMxzY.js} +1 -1
  74. package/src/assets/web-panel/assets/Terminal-X-NGwLpv.js +3 -0
  75. package/src/assets/web-panel/assets/{TimelineRenderer-hbO7agZs.js → TimelineRenderer-Bh8jA18j.js} +1 -1
  76. package/src/assets/web-panel/assets/{Tokens-CsmhgTBO.js → Tokens-DWkTd5dv.js} +1 -1
  77. package/src/assets/web-panel/assets/{Trigger-DnaF_2PP.js → Trigger-CRgVg6sd.js} +1 -1
  78. package/src/assets/web-panel/assets/{Trust-C1oafGj1.js → Trust-BgWOXd0W.js} +1 -1
  79. package/src/assets/web-panel/assets/{UkeySign-eLL4DOmC.js → UkeySign-BlTUB9Y-.js} +1 -1
  80. package/src/assets/web-panel/assets/{VideoEditing-CX45sVq7.js → VideoEditing-DI64XgNb.js} +1 -1
  81. package/src/assets/web-panel/assets/{Wallet-aWPqpHdQ.js → Wallet-CJ3TNGiG.js} +4 -4
  82. package/src/assets/web-panel/assets/{WebAuthn-DMYV1MAo.js → WebAuthn-B2-rWWoV.js} +4 -4
  83. package/src/assets/web-panel/assets/{WorkflowEditor-D9uRIJvH.js → WorkflowEditor-VI9otbaH.js} +1 -1
  84. package/src/assets/web-panel/assets/{chat-BmWYfCxG.js → chat-CgYfiaVh.js} +1 -1
  85. package/src/assets/web-panel/assets/{colors-DqvTCkBe.js → colors-B9EhRTky.js} +1 -1
  86. package/src/assets/web-panel/assets/{compact-item-Bh0L0ejI.js → compact-item-Cb7bjraa.js} +1 -1
  87. package/src/assets/web-panel/assets/{createContext-r2qgp1mn.js → createContext-DlXPeXuj.js} +1 -1
  88. package/src/assets/web-panel/assets/devWarning-D-Hp8s_8.js +1 -0
  89. package/src/assets/web-panel/assets/{hasIn-BcffXa-S.js → hasIn-BbgRfrdf.js} +1 -1
  90. package/src/assets/web-panel/assets/{index-DxajFkK2.js → index-1iUK_kAw.js} +1 -1
  91. package/src/assets/web-panel/assets/{index-BCBqTRWH.js → index-2ts5iOIB.js} +1 -1
  92. package/src/assets/web-panel/assets/{index-CDR3GmaO.js → index-5CrFMQjt.js} +1 -1
  93. package/src/assets/web-panel/assets/{index-B7z0qK1W.js → index-AR-QpAkP.js} +1 -1
  94. package/src/assets/web-panel/assets/{index-BLN-neIf.js → index-BD2W-qsS.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-De36_UgR.js → index-BU8hEUyq.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-Bma_yHcC.js → index-BfSS-U5o.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-Dcjol7ot.js → index-Bk7r1a9x.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-BbMox24t.js → index-BoEFFKn3.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-C2-02rrp.js → index-Bt-lPYpq.js} +1 -1
  100. package/src/assets/web-panel/assets/index-BvnHuxVM.js +1 -0
  101. package/src/assets/web-panel/assets/{index-DPFT7J7I.js → index-C73WgOc2.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-BXXxkeij.js → index-C8DB27uJ.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-XwbSqOB2.js → index-C95qWAh4.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-CAlxkpnv.js → index-CGx8aO_Y.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-B3mmDuOv.js → index-CHR47Q5B.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-CJt0iuep.js → index-CMyzmvtJ.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-S4E77Aer.js → index-Caiu2avX.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-BtyXyl3t.js → index-Cf9zwbk-.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-CCyB-RK5.js → index-Cmr31VCO.js} +3 -3
  110. package/src/assets/web-panel/assets/{index-CKnEtlZD.js → index-D-Zz9PvD.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-DWlDSE0F.js → index-D6t-Shqr.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-h4O0AcBt.js → index-D8kB0k3E.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-Cbj6C3pA.js → index-DDzNdZcX.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-DutDlDUF.js → index-DPaffcT8.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-BXae4ZyX.js → index-DUU9DY4J.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-U86pxDyR.js → index-DXxa7PR8.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-DRXcGa5y.js → index-DbLJShJB.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-BMn_luHQ.js → index-De59Xat-.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-Ct8qhPZe.js → index-Dh6qWb1v.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-CY8RXaZR.js → index-DkQIyK-V.js} +1 -1
  121. package/src/assets/web-panel/assets/index-Dkm5IGwX.js +1 -0
  122. package/src/assets/web-panel/assets/{index-585fuGAN.js → index-Dx4sm6dm.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-COrfHebA.js → index-Ira0HLPr.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-BeV-KoQl.js → index-OVrh8wTN.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-DW1y18GR.js → index-c7-Jd6WB.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-C9nh3ANl.js → index-fG-1gXy0.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-BQlAPNSU.js → index-ojRAd7Nq.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-B6pAm1iJ.js → index-p03wNqiP.js} +1 -1
  129. package/src/assets/web-panel/assets/{initDefaultProps-C1d8I-BX.js → initDefaultProps-JT674ACa.js} +1 -1
  130. package/src/assets/web-panel/assets/{motion-Dq7fiy4Y.js → motion-CokflrA9.js} +1 -1
  131. package/src/assets/web-panel/assets/{move-Bqb2dySM.js → move-CfMhRpyC.js} +1 -1
  132. package/src/assets/web-panel/assets/{omit-BUYqb4My.js → omit-ClYc5II5.js} +1 -1
  133. package/src/assets/web-panel/assets/{pickAttrs-DeytiKlZ.js → pickAttrs-CTwEb_8h.js} +1 -1
  134. package/src/assets/web-panel/assets/{placementArrow-xrXZWCqG.js → placementArrow-Cb3StU_t.js} +1 -1
  135. package/src/assets/web-panel/assets/{responsiveObserve-CcL2K-YY.js → responsiveObserve-uIxkx5M1.js} +1 -1
  136. package/src/assets/web-panel/assets/{slide-DmCWaic7.js → slide-B9HZBQ7i.js} +1 -1
  137. package/src/assets/web-panel/assets/{statusUtils-CqNrFif7.js → statusUtils-C72bwYl0.js} +1 -1
  138. package/src/assets/web-panel/assets/{styleChecker-C436m5Xy.js → styleChecker-BFTtaQb8.js} +1 -1
  139. package/src/assets/web-panel/assets/{useFlexGapSupport-CVhutCN8.js → useFlexGapSupport-DKl5j41_.js} +1 -1
  140. package/src/assets/web-panel/assets/{useFs-DUd49Bui.js → useFs-BUHS6bo3.js} +1 -1
  141. package/src/assets/web-panel/assets/{usePersonalDataHub-fuS9raic.js → usePersonalDataHub-D8KrYSq4.js} +1 -1
  142. package/src/assets/web-panel/assets/{vnode-C3kmDmk-.js → vnode-JYP-aZDj.js} +1 -1
  143. package/src/assets/web-panel/assets/{zoom-hX-F1dT-.js → zoom-BFusdxdH.js} +1 -1
  144. package/src/assets/web-panel/index.html +1 -1
  145. package/src/commands/agent.js +67 -29
  146. package/src/commands/terminal-setup.js +127 -0
  147. package/src/harness/mcp-client.js +48 -2
  148. package/src/index.js +2 -0
  149. package/src/lib/image-input.js +8 -2
  150. package/src/lib/llm-pricing.js +15 -0
  151. package/src/lib/permission-rules.cjs +11 -1
  152. package/src/lib/personal-data-hub-wiring.js +24 -0
  153. package/src/lib/repl-multiline.js +64 -0
  154. package/src/lib/repl-rewind.js +65 -2
  155. package/src/lib/repl-vim.js +445 -0
  156. package/src/lib/safe-mode.js +17 -3
  157. package/src/lib/skill-loader.js +45 -1
  158. package/src/lib/slash-commands.js +13 -3
  159. package/src/lib/status-line.cjs +33 -3
  160. package/src/lib/terminal-setup.js +209 -0
  161. package/src/repl/agent-repl.js +274 -27
  162. package/src/repl/session-cost.js +98 -1
  163. package/src/runtime/agent-core.js +23 -8
  164. package/src/runtime/fallback-model.js +125 -30
  165. package/src/runtime/headless-runner.js +2 -0
  166. package/src/runtime/headless-stream.js +2 -0
  167. package/src/runtime/mcp-config.js +14 -3
  168. package/src/assets/web-panel/assets/ChatBubbleRenderer-CfpKEQUF.js +0 -1
  169. package/src/assets/web-panel/assets/OrderTableRenderer-ST2lr-Bi.js +0 -1
  170. package/src/assets/web-panel/assets/Tasks-iImd8xSO.js +0 -1
  171. package/src/assets/web-panel/assets/Terminal-B5VDEEHD.js +0 -3
  172. package/src/assets/web-panel/assets/devWarning-CusWDjWW.js +0 -1
  173. package/src/assets/web-panel/assets/index-BhYltBvN.js +0 -1
  174. package/src/assets/web-panel/assets/index-CZiIHw4e.js +0 -1
@@ -69,6 +69,81 @@ export function addUsage(store, events) {
69
69
  return s;
70
70
  }
71
71
 
72
+ /**
73
+ * Classify a priced model row into a cost CATEGORY by its role in the session
74
+ * (Claude-Code `/cost` breakdown parity). Roles are derived from config + the
75
+ * live session: the active model is "main", the configured vision model is
76
+ * "vision", any model in the fallback chain is "fallback", anything else (e.g.
77
+ * a model switched to mid-session) is "other".
78
+ *
79
+ * The active model wins over vision/fallback when names collide, so a session
80
+ * that never used vision shows everything as "main".
81
+ *
82
+ * @param {string} provider
83
+ * @param {string} model
84
+ * @param {{mainProvider?:string, mainModel?:string, visionModel?:string, fallbackModels?:string[]}} roles
85
+ * @returns {"main"|"vision"|"fallback"|"other"}
86
+ */
87
+ export function classifyModelRole(provider, model, roles = {}) {
88
+ const lc = (x) => String(x || "").toLowerCase();
89
+ const p = lc(provider);
90
+ const m = lc(model);
91
+ if (
92
+ roles.mainModel &&
93
+ m === lc(roles.mainModel) &&
94
+ (!roles.mainProvider || p === lc(roles.mainProvider))
95
+ ) {
96
+ return "main";
97
+ }
98
+ if (roles.visionModel && m === lc(roles.visionModel)) return "vision";
99
+ if (
100
+ Array.isArray(roles.fallbackModels) &&
101
+ roles.fallbackModels.some((fm) => lc(fm) === m)
102
+ ) {
103
+ return "fallback";
104
+ }
105
+ return "other";
106
+ }
107
+
108
+ /**
109
+ * Group a priced rollup's per-model rows into categories. Operates on the output
110
+ * of priceRollup (rows carry cost/matched/free), so dollar sums are accurate and
111
+ * unpriced models are flagged rather than silently counted as $0.
112
+ *
113
+ * @returns {Array<{category,inputTokens,outputTokens,totalTokens,calls,cost,models,anyUnpriced}>}
114
+ * sorted by cost (then tokens) descending.
115
+ */
116
+ export function categorizeByRole(pricedResult, roles = {}) {
117
+ const cats = new Map();
118
+ for (const row of pricedResult?.byModel || []) {
119
+ const category = classifyModelRole(row.provider, row.model, roles);
120
+ let c = cats.get(category);
121
+ if (!c) {
122
+ c = {
123
+ category,
124
+ inputTokens: 0,
125
+ outputTokens: 0,
126
+ totalTokens: 0,
127
+ calls: 0,
128
+ cost: 0,
129
+ models: [],
130
+ anyUnpriced: false,
131
+ };
132
+ cats.set(category, c);
133
+ }
134
+ c.inputTokens += num(row.inputTokens);
135
+ c.outputTokens += num(row.outputTokens);
136
+ c.totalTokens += num(row.totalTokens);
137
+ c.calls += num(row.calls);
138
+ if (row.matched && !row.free) c.cost += num(row.cost);
139
+ if (!row.matched && !row.free) c.anyUnpriced = true;
140
+ if (row.model && !c.models.includes(row.model)) c.models.push(row.model);
141
+ }
142
+ return Array.from(cats.values()).sort(
143
+ (a, b) => b.cost - a.cost || b.totalTokens - a.totalTokens,
144
+ );
145
+ }
146
+
72
147
  /** Snapshot the store as the `{ total, byModel[] }` aggregate priceRollup wants. */
73
148
  export function costAggregate(store) {
74
149
  const s = store || newCostStore();
@@ -84,7 +159,7 @@ export function costAggregate(store) {
84
159
  * Render the live session cost. `pricingOverrides` is typically
85
160
  * `config.llm.pricing`. Returns plain text (the REPL does the I/O).
86
161
  */
87
- export function renderSessionCost(store, { pricingOverrides } = {}) {
162
+ export function renderSessionCost(store, { pricingOverrides, roles } = {}) {
88
163
  const agg = costAggregate(store);
89
164
  if (agg.total.calls === 0) {
90
165
  return "Session cost: no LLM calls yet this session.";
@@ -108,6 +183,28 @@ export function renderSessionCost(store, { pricingOverrides } = {}) {
108
183
  : "unpriced";
109
184
  lines.push(` ${provider} ${model} ${price} ${tokens}`);
110
185
  }
186
+
187
+ // Category breakdown (main / vision / fallback / other) — only worth showing
188
+ // when more than one category was actually used; a single-model session is
189
+ // already fully described by the per-model rows above.
190
+ if (roles) {
191
+ const cats = categorizeByRole(result, roles);
192
+ if (cats.length >= 2) {
193
+ const totalCost = num(result.cost.totalCost);
194
+ lines.push(" by category:");
195
+ for (const c of cats) {
196
+ const label = c.category.padEnd(9);
197
+ const price =
198
+ c.anyUnpriced && c.cost === 0 ? "unpriced" : fmtUsd(c.cost);
199
+ const pct =
200
+ totalCost > 0 ? ` (${Math.round((c.cost / totalCost) * 100)}%)` : "";
201
+ lines.push(
202
+ ` ${label} ${price}${pct} in=${c.inputTokens} out=${c.outputTokens} ${c.calls} calls`,
203
+ );
204
+ }
205
+ }
206
+ }
207
+
111
208
  if (result.unpriced.length > 0) {
112
209
  lines.push(
113
210
  ` note: ${result.unpriced.length} model(s) have no rate — ` +
@@ -921,9 +921,8 @@ export async function executeTool(name, args, context = {}) {
921
921
  settingsVerdict.decision !== "allow" &&
922
922
  args?.path
923
923
  ) {
924
- const { sensitiveFileReason } = await import(
925
- "../lib/sensitive-file-guard.js"
926
- );
924
+ const { sensitiveFileReason } =
925
+ await import("../lib/sensitive-file-guard.js");
927
926
  const sensReason = sensitiveFileReason(args.path);
928
927
  if (sensReason) {
929
928
  const confirm = context.permissionConfirm || context.shellConfirm || null;
@@ -1409,10 +1408,18 @@ async function executeToolInner(
1409
1408
  cwd: task.cwd,
1410
1409
  shell: true,
1411
1410
  windowsHide: true,
1412
- // Same CC_SESSION_ID correlation as the foreground path.
1411
+ // Same agent-identity env as the foreground path: CLAUDECODE marks
1412
+ // "running under the agent"; the session id correlates work to the
1413
+ // run (CC_SESSION_ID + CLAUDE_CODE_SESSION_ID for Claude-Code parity).
1413
1414
  env: {
1414
1415
  ...process.env,
1415
- ...(sessionId ? { CC_SESSION_ID: String(sessionId) } : {}),
1416
+ CLAUDECODE: "1",
1417
+ ...(sessionId
1418
+ ? {
1419
+ CC_SESSION_ID: String(sessionId),
1420
+ CLAUDE_CODE_SESSION_ID: String(sessionId),
1421
+ }
1422
+ : {}),
1416
1423
  },
1417
1424
  // POSIX: own process group so check_shell{kill}/teardown can signal
1418
1425
  // the whole tree (shell + its grandchild command). No-op on Windows
@@ -1471,11 +1478,19 @@ async function executeToolInner(
1471
1478
  encoding: "utf8",
1472
1479
  timeout: _resolveShellTimeout(args.timeout),
1473
1480
  maxBuffer: 1024 * 1024,
1474
- // CC_SESSION_ID (Claude-Code CLAUDE_CODE_SESSION_ID parity,
1475
- // 2.1.132): lets scripts/hooks correlate work to the agent session.
1481
+ // Agent-identity env for shell subprocesses (Claude-Code 2.1.132
1482
+ // parity): CLAUDECODE=1 marks "running under the agent"; CC_SESSION_ID
1483
+ // + its CLAUDE_CODE_SESSION_ID mirror let scripts/hooks correlate work
1484
+ // to the agent session (the mirror is what CC-targeting tools expect).
1476
1485
  env: {
1477
1486
  ...process.env,
1478
- ...(sessionId ? { CC_SESSION_ID: String(sessionId) } : {}),
1487
+ CLAUDECODE: "1",
1488
+ ...(sessionId
1489
+ ? {
1490
+ CC_SESSION_ID: String(sessionId),
1491
+ CLAUDE_CODE_SESSION_ID: String(sessionId),
1492
+ }
1493
+ : {}),
1479
1494
  },
1480
1495
  });
1481
1496
  return attachDescriptor(
@@ -1,18 +1,28 @@
1
1
  /**
2
2
  * `--fallback-model` support — Claude-Code parity for unattended runs.
3
3
  *
4
- * Wraps the agent loop's LLM call so a single request that fails with a
5
- * *retryable* error (overload / rate-limit / transient network) is transparently
6
- * re-issued once with a backup model. Because it sits at the chatFn seam
7
- * (`agentLoop` uses `options.chatFn || chatWithTools`), fallback needs no changes
8
- * to the runners — the wrapped fn is passed in via `options.chatFn`.
4
+ * Wraps the agent loop's LLM call so a request that fails with a *retryable*
5
+ * error (overload / rate-limit / transient network) or because the primary
6
+ * model is *not found* (decommissioned / mistyped id) is transparently
7
+ * re-issued against a chain of up to a few backup models, in order. Because it
8
+ * sits at the chatFn seam (`agentLoop` uses `options.chatFn || chatWithTools`),
9
+ * fallback needs no changes to the runners — the wrapped fn is passed in via
10
+ * `options.chatFn`.
9
11
  *
10
- * Same-provider only: the fallback swaps `options.model`, keeping the configured
12
+ * Parity reference: Claude Code 2.1.166 added an ordered fallback chain (up to
13
+ * 3 models) and 2.1.152 switches to the configured fallback when the primary
14
+ * model is "not found".
15
+ *
16
+ * Same-provider only: each hop swaps `options.model`, keeping the configured
11
17
  * provider / baseUrl / apiKey. Cross-provider fallback is a larger feature.
12
18
  */
13
19
 
14
20
  import { chatWithTools } from "./agent-core.js";
15
21
 
22
+ // Claude Code caps the fallback chain at 3 backups; mirror that here so a
23
+ // mis-configured comma list can't fan out into an unbounded retry storm.
24
+ const MAX_FALLBACK_MODELS = 3;
25
+
16
26
  // Heuristics for "try again on a different model" — overloaded backends,
17
27
  // rate limits, and transient connectivity. Deliberately conservative: a 4xx
18
28
  // that is not 429 (bad request / auth) is NOT retried.
@@ -36,6 +46,22 @@ const RETRYABLE_PATTERNS = [
36
46
  /network error/i,
37
47
  ];
38
48
 
49
+ // A primary model that no longer exists (decommissioned / typo / not enabled
50
+ // for this key) is NOT transient, but it IS exactly the case the fallback
51
+ // chain exists for. Detected separately from RETRYABLE so an auth/quota 4xx
52
+ // (which a different model would also hit) is still surfaced, not masked.
53
+ const MODEL_NOT_FOUND_PATTERNS = [
54
+ /model[^a-z]*not[^a-z]*found/i,
55
+ /model_not_found/i,
56
+ /no such model/i,
57
+ /unknown model/i,
58
+ /model .* does not exist/i,
59
+ /does not exist.*model/i,
60
+ /invalid model/i,
61
+ /model .* is not (?:available|supported)/i,
62
+ /unsupported model/i,
63
+ ];
64
+
39
65
  /**
40
66
  * Decide whether an error from an LLM call warrants a fallback retry.
41
67
  * @param {any} err
@@ -64,46 +90,115 @@ export function isRetryableModelError(err) {
64
90
  }
65
91
 
66
92
  /**
67
- * Build a chatFn that retries once on the fallback model.
93
+ * Decide whether an error means the *primary model is unavailable* (not found /
94
+ * decommissioned / not enabled), which a different model in the chain might
95
+ * resolve. Kept separate from {@link isRetryableModelError} so generic 4xx
96
+ * (auth / quota / bad request) is never silently retried.
97
+ * @param {any} err
98
+ * @returns {boolean}
99
+ */
100
+ export function isModelNotFoundError(err) {
101
+ if (!err) return false;
102
+ const status =
103
+ typeof err.status === "number"
104
+ ? err.status
105
+ : typeof err.statusCode === "number"
106
+ ? err.statusCode
107
+ : null;
108
+ // A bare 404 from a model endpoint almost always means "this model id".
109
+ if (status === 404) return true;
110
+
111
+ const parts = [
112
+ err.message,
113
+ typeof err.code === "string" ? err.code : "",
114
+ err.cause?.message,
115
+ err.cause?.code,
116
+ ]
117
+ .filter(Boolean)
118
+ .join(" ");
119
+ return MODEL_NOT_FOUND_PATTERNS.some((re) => re.test(parts));
120
+ }
121
+
122
+ /**
123
+ * Normalize a fallback-model spec into an ordered, de-duplicated list.
124
+ * Accepts a single string, a comma-separated string, an array (whose entries
125
+ * may themselves be comma-separated), or nullish. Trims, drops empties, removes
126
+ * duplicates (first wins), and caps at {@link MAX_FALLBACK_MODELS}.
127
+ * @param {string|string[]|null|undefined} input
128
+ * @returns {string[]}
129
+ */
130
+ export function normalizeFallbackModels(input) {
131
+ if (input == null) return [];
132
+ const raw = Array.isArray(input) ? input : [input];
133
+ const out = [];
134
+ for (const entry of raw) {
135
+ if (typeof entry !== "string") continue;
136
+ for (const piece of entry.split(",")) {
137
+ const m = piece.trim();
138
+ if (m && !out.includes(m)) out.push(m);
139
+ if (out.length >= MAX_FALLBACK_MODELS) return out;
140
+ }
141
+ }
142
+ return out;
143
+ }
144
+
145
+ /**
146
+ * Build a chatFn that walks an ordered fallback chain when the primary fails.
147
+ *
148
+ * On a retryable error (or a model-not-found error) the call is re-issued
149
+ * against the next distinct model in the chain; this repeats down the chain
150
+ * until one succeeds or the chain is exhausted (then the last error is thrown).
151
+ * A fallback equal to the model just tried is skipped (no wasted round-trip).
68
152
  *
69
153
  * @param {object} opts
70
- * @param {string} opts.fallbackModel backup model name (required)
154
+ * @param {string|string[]} [opts.fallbackModels] ordered backup model(s)
155
+ * @param {string} [opts.fallbackModel] single backup (back-compat)
71
156
  * @param {Function} [opts.baseChatFn=chatWithTools] underlying LLM call
72
- * @param {Function} [opts.isRetryable] error predicate (testing seam)
73
- * @param {Function} [opts.onFallback] notified ({from,to,error}) on retry
157
+ * @param {Function} [opts.isRetryable] transient-error predicate (seam)
158
+ * @param {Function} [opts.isModelNotFound] missing-model predicate (seam)
159
+ * @param {Function} [opts.onFallback] notified ({from,to,error}) per hop
74
160
  * @returns {Function} a (messages, options) => Promise<result> chatFn
75
161
  */
76
162
  export function makeFallbackChatFn(opts = {}) {
77
- const fallbackModel = opts.fallbackModel;
163
+ const models = normalizeFallbackModels(
164
+ opts.fallbackModels != null ? opts.fallbackModels : opts.fallbackModel,
165
+ );
78
166
  const baseChatFn = opts.baseChatFn || chatWithTools;
79
167
  const isRetryable = opts.isRetryable || isRetryableModelError;
168
+ const isModelNotFound = opts.isModelNotFound || isModelNotFoundError;
80
169
  const onFallback = opts.onFallback;
81
170
 
82
171
  return async function chatWithFallback(messages, options = {}) {
83
172
  try {
84
173
  return await baseChatFn(messages, options);
85
- } catch (err) {
86
- const primaryModel = options.model;
87
- // Skip a no-op retry when the fallback is the same model as the primary.
88
- if (
89
- !fallbackModel ||
90
- fallbackModel === primaryModel ||
91
- !isRetryable(err)
92
- ) {
93
- throw err;
94
- }
95
- if (typeof onFallback === "function") {
174
+ } catch (firstErr) {
175
+ let err = firstErr;
176
+ let lastTried = options.model;
177
+ for (const candidate of models) {
178
+ // Skip a no-op hop (fallback identical to the model just attempted).
179
+ if (!candidate || candidate === lastTried) continue;
180
+ // Only advance the chain for transient failures or a missing primary;
181
+ // a real bad-request / auth error is surfaced immediately.
182
+ if (!isRetryable(err) && !isModelNotFound(err)) throw err;
183
+ if (typeof onFallback === "function") {
184
+ try {
185
+ onFallback({
186
+ from: lastTried,
187
+ to: candidate,
188
+ error: err?.message || String(err),
189
+ });
190
+ } catch {
191
+ // Notification is best-effort — never mask the retry.
192
+ }
193
+ }
194
+ lastTried = candidate;
96
195
  try {
97
- onFallback({
98
- from: primaryModel,
99
- to: fallbackModel,
100
- error: err?.message || String(err),
101
- });
102
- } catch {
103
- // Notification is best-effort — never mask the retry.
196
+ return await baseChatFn(messages, { ...options, model: candidate });
197
+ } catch (nextErr) {
198
+ err = nextErr;
104
199
  }
105
200
  }
106
- return await baseChatFn(messages, { ...options, model: fallbackModel });
201
+ throw err;
107
202
  }
108
203
  };
109
204
  }
@@ -527,6 +527,8 @@ export async function runAgentHeadless(options = {}, deps = {}) {
527
527
  includeRegistered: options.useRegisteredMcp !== false,
528
528
  ide: options.ide,
529
529
  cwd: options.cwd || process.cwd(),
530
+ // advertise the session id to spawned stdio MCP servers
531
+ sessionId,
530
532
  },
531
533
  {
532
534
  writeErr,
@@ -460,6 +460,8 @@ export async function runAgentHeadlessStream(options = {}, deps = {}) {
460
460
  includeRegistered: options.useRegisteredMcp !== false,
461
461
  ide: options.ide,
462
462
  cwd: options.cwd || process.cwd(),
463
+ // advertise the session id to spawned stdio MCP servers
464
+ sessionId,
463
465
  },
464
466
  {
465
467
  writeErr,
@@ -91,6 +91,13 @@ export async function setupMcpFromConfig(servers, deps = {}) {
91
91
  connected,
92
92
  } = result;
93
93
 
94
+ // Advertise the agent session id to stdio MCP servers spawned below
95
+ // (CC_SESSION_ID / CLAUDE_CODE_SESSION_ID + CLAUDECODE marker). Idempotent —
96
+ // safe when an accumulating `into` client was already given one.
97
+ if (deps.sessionId != null && typeof mcpClient.setSessionId === "function") {
98
+ mcpClient.setSessionId(deps.sessionId);
99
+ }
100
+
94
101
  for (const [name, cfg] of Object.entries(servers)) {
95
102
  // Skip a name already connected — an earlier batch (ad-hoc --mcp-config)
96
103
  // wins over a later one (registered) on a clash.
@@ -385,13 +392,17 @@ export async function resolveAgentMcp(args = {}, deps = {}) {
385
392
  const doFile = deps.loadMcpConfig || loadMcpConfig;
386
393
  const doReg = deps.loadRegisteredMcp || loadRegisteredMcp;
387
394
  const doIde = deps.loadIdeMcp || loadIdeMcp;
395
+ // Thread the agent session id down to setupMcpFromConfig so spawned stdio MCP
396
+ // servers get CC_SESSION_ID / CLAUDE_CODE_SESSION_ID (Claude-Code parity).
397
+ const fwd =
398
+ args.sessionId != null ? { ...deps, sessionId: args.sessionId } : deps;
388
399
  let result = null;
389
400
  if (args.mcpConfigPath) {
390
- result = await doFile(args.mcpConfigPath, deps); // fail-fast on bad file
401
+ result = await doFile(args.mcpConfigPath, fwd); // fail-fast on bad file
391
402
  }
392
403
  if (args.includeRegistered !== false && args.db) {
393
404
  result = await doReg(args.db, {
394
- ...deps,
405
+ ...fwd,
395
406
  all: args.allRegistered === true,
396
407
  into: result || undefined,
397
408
  });
@@ -402,7 +413,7 @@ export async function resolveAgentMcp(args = {}, deps = {}) {
402
413
  if (args.ide === true || inIde) {
403
414
  result = await doIde(
404
415
  { cwd: args.cwd, env, force: args.ide === true },
405
- { ...deps, into: result || undefined },
416
+ { ...fwd, into: result || undefined },
406
417
  );
407
418
  }
408
419
  }
@@ -1 +0,0 @@
1
- import{I as h,P as u,J as y,U as n,R as a,S,K as C,Q as x,V as w,_ as B,b as s}from"./vendor-BvqAck49.js";import{_ as k}from"./index-CCyB-RK5.js";import"./icons-DP3uiYxy.js";const N={__name:"ChatBubbleRenderer",props:{event:{type:Object,required:!0}},setup(c,{expose:d}){d();const t=c,r=s(()=>{const e=t.event.content||{};return e.text||e.body||e.message||e.title||JSON.stringify(e).slice(0,200)}),i=s(()=>{const e=t.event.content||{};return e.from||e.sender||e.senderName||t.event.actor||"(unknown)"}),l=s(()=>{const e=(t.event.actor||"").toLowerCase();return e.includes("self")||e==="me"||e.endsWith("_self")}),o=s(()=>{const e=t.event.source.adapter||"";return e.startsWith("messaging-qq")?"magenta":e==="wechat"?"green":"blue"}),m=s(()=>{if(!t.event.occurredAt)return"";try{const e=new Date(t.event.occurredAt),p=e.getFullYear(),f=String(e.getMonth()+1).padStart(2,"0"),g=String(e.getDate()).padStart(2,"0"),b=String(e.getHours()).padStart(2,"0"),v=String(e.getMinutes()).padStart(2,"0");return`${p}-${f}-${g} ${b}:${v}`}catch{return""}}),_={props:t,messageText:r,actorLabel:i,isMine:l,adapterColor:o,formattedTime:m,computed:s};return Object.defineProperty(_,"__isScriptSetup",{enumerable:!1,value:!0}),_}},T={class:"bubble"},M={class:"meta"},R={class:"actor"},V={class:"time"},q={class:"body"};function A(c,d,t,r,i,l){const o=h("a-tag");return u(),y("div",{class:B(["chat-row",{mine:r.isMine}])},[n("div",T,[n("div",M,[n("span",R,a(r.actorLabel),1),n("span",V,a(r.formattedTime),1)]),n("div",q,a(r.messageText),1),t.event.source.adapter?(u(),S(o,{key:0,class:"src",color:r.adapterColor},{default:C(()=>[x(a(t.event.source.adapter),1)]),_:1},8,["color"])):w("v-if",!0)])],2)}const j=k(N,[["render",A],["__scopeId","data-v-49238629"],["__file","/tmp/cc-web-panel-yoZvdA/repo/packages/web-panel/src/components/pdh/renderers/ChatBubbleRenderer.vue"]]);export{j as default};
@@ -1 +0,0 @@
1
- import{I as w,P as l,J as u,U as s,R as n,c as i,K as v,Q as _,V as y,b as o}from"./vendor-BvqAck49.js";import{_ as S}from"./index-CCyB-RK5.js";import"./icons-DP3uiYxy.js";const k={__name:"OrderTableRenderer",props:{event:{type:Object,required:!0}},setup(p,{expose:r}){r();const a=p,e=o(()=>a.event.content||{}),d=o(()=>a.event.extra||{}),m=o(()=>d.value.merchant||e.value.merchant||e.value.counterparty||"—"),c=o(()=>e.value.title||e.value.name||e.value.itemName||e.value.text||"—"),g=o(()=>{const t=e.value.amount??e.value.price??e.value.total;return t==null?"—":`${e.value.currency||"¥"} ${typeof t=="number"?t.toFixed(2):t}`}),T=o(()=>d.value.orderNo||e.value.orderNo||e.value.orderId),f=o(()=>e.value.status||e.value.state),b=o(()=>{const t=(f.value||"").toLowerCase();return t.includes("成功")||t.includes("succe")||t.includes("paid")?"green":t.includes("退")||t.includes("refund")?"orange":t.includes("失败")||t.includes("fail")?"red":"default"}),h=o(()=>{if(!a.event.occurredAt)return"";const t=new Date(a.event.occurredAt);return`${t.getFullYear()}-${String(t.getMonth()+1).padStart(2,"0")}-${String(t.getDate()).padStart(2,"0")} ${String(t.getHours()).padStart(2,"0")}:${String(t.getMinutes()).padStart(2,"0")}`}),x={props:a,c:e,e:d,merchantText:m,itemText:c,amountText:g,orderNo:T,statusText:f,statusColor:b,formattedTime:h,computed:o};return Object.defineProperty(x,"__isScriptSetup",{enumerable:!1,value:!0}),x}},N={class:"order-card"},C={class:"head"},O={class:"time"},V={class:"row"},R={class:"val"},A={class:"row"},B={class:"val"},D={class:"row amount-row"},I={class:"val amount"},j={key:0,class:"row"},F={class:"val mono"},M={key:1,class:"row"};function P(p,r,a,e,d,m){const c=w("a-tag");return l(),u("div",N,[s("div",C,[s("span",O,n(e.formattedTime),1),i(c,{color:"gold"},{default:v(()=>[_(n(a.event.source.adapter),1)]),_:1}),i(c,null,{default:v(()=>[_(n(a.event.subtype),1)]),_:1})]),s("div",V,[r[0]||(r[0]=s("span",{class:"key"},"商户",-1)),s("span",R,n(e.merchantText),1)]),s("div",A,[r[1]||(r[1]=s("span",{class:"key"},"商品/项目",-1)),s("span",B,n(e.itemText),1)]),s("div",D,[r[2]||(r[2]=s("span",{class:"key"},"金额",-1)),s("span",I,n(e.amountText),1)]),e.orderNo?(l(),u("div",j,[r[3]||(r[3]=s("span",{class:"key"},"单号",-1)),s("span",F,n(e.orderNo),1)])):y("v-if",!0),e.statusText?(l(),u("div",M,[r[4]||(r[4]=s("span",{class:"key"},"状态",-1)),i(c,{color:e.statusColor},{default:v(()=>[_(n(e.statusText),1)]),_:1},8,["color"])])):y("v-if",!0)])}const K=S(k,[["render",P],["__scopeId","data-v-5ed5524d"],["__file","/tmp/cc-web-panel-yoZvdA/repo/packages/web-panel/src/components/pdh/renderers/OrderTableRenderer.vue"]]);export{K as default};
@@ -1 +0,0 @@
1
- import{E as B,b as z,r as D,I as p,J as c,U as _,c as n,K as a,S as T,V as w,o as P,x as I,P as i,Q as g,R as r,F as h,Z as F}from"./vendor-BvqAck49.js";import{u as R,_ as O}from"./index-CCyB-RK5.js";import{R as E}from"./icons-DP3uiYxy.js";const V=B("tasks",()=>{const k=D([]),l=D(!1);let u=null,e=null;const x=z(()=>k.value.filter(t=>t.status==="running")),b=z(()=>k.value.filter(t=>t.status==="pending")),y=z(()=>k.value.filter(t=>t.status==="completed"||t.status==="failed"||t.status==="timeout"));async function f(){const t=R();l.value=!0;try{const s=await t.sendRaw({type:"tasks-list"});s&&Array.isArray(s.tasks)&&(k.value=s.tasks)}catch{}finally{l.value=!1}}async function o(t){const s=R();try{await s.sendRaw({type:"tasks-stop",taskId:t})}catch{}finally{await f()}}const d=D(null);function m(t=5e3){v(),f(),u=setInterval(f,t),C()}function v(){u&&(clearInterval(u),u=null),e&&(e(),e=null)}function C(){const t=R();if(e)return;const s=S=>{S.type==="task:notification"&&S.payload?.task&&(d.value=S.payload.task,f(),setTimeout(()=>{d.value=null},8e3))};e=t.onRuntimeEvent(s)}function N(t){return!t||t<0?"-":t<1e3?`${t}ms`:t<6e4?`${(t/1e3).toFixed(1)}s`:`${(t/6e4).toFixed(1)}m`}function A(t){switch(t){case"running":return"processing";case"pending":return"default";case"completed":return"success";case"failed":return"error";case"timeout":return"warning";default:return"default"}}return{tasks:k,loading:l,running:x,pending:b,completed:y,lastNotification:d,fetchTasks:f,stopTask:o,startPolling:m,stopPolling:v,formatDuration:N,getStatusColor:A}}),U={__name:"Tasks",setup(k,{expose:l}){l();const u=V(),e=[{title:"状态",key:"status",width:90},{title:"描述",key:"description",ellipsis:!0},{title:"类型",dataIndex:"type",width:100},{title:"耗时",key:"duration",width:90},{title:"创建时间",key:"createdAt",width:180},{title:"结果",key:"result",ellipsis:!0},{title:"操作",key:"action",width:80}];function x(o){return o.status==="running"&&o.startedAt?u.formatDuration(Date.now()-o.startedAt):o.completedAt&&o.startedAt?u.formatDuration(o.completedAt-o.startedAt):"-"}function b(o){return o?new Date(o).toLocaleString():"-"}function y(o,d){return o?o.length>d?`${o.slice(0,d)}...`:o:""}P(()=>u.startPolling(5e3)),I(()=>u.stopPolling());const f={store:u,columns:e,getDuration:x,formatTime:b,truncate:y,onMounted:P,onUnmounted:I,get ReloadOutlined(){return E},get useTasksStore(){return V}};return Object.defineProperty(f,"__isScriptSetup",{enumerable:!1,value:!0}),f}},L={class:"page-header"},Z={key:0,class:"error-text"},j={key:1},J={class:"task-header"},K={class:"task-desc"},M={class:"task-id"},Q={class:"task-meta"},W=["title"],q={key:0,class:"error-text"},G={key:1,class:"success-text"},H={key:2,class:"muted-text"};function X(k,l,u,e,x,b){const y=p("a-button"),f=p("a-space"),o=p("a-alert"),d=p("a-statistic"),m=p("a-card"),v=p("a-col"),C=p("a-row"),N=p("a-tag"),A=p("a-table");return i(),c("div",null,[_("div",L,[l[3]||(l[3]=_("div",null,[_("h2",{class:"page-title"},"后台任务"),_("p",{class:"page-sub"},"查看后台任务队列、实时通知和任务执行结果。")],-1)),n(f,null,{default:a(()=>[n(y,{ghost:"",loading:e.store.loading,onClick:l[0]||(l[0]=t=>e.store.fetchTasks())},{icon:a(()=>[n(e.ReloadOutlined)]),default:a(()=>[l[2]||(l[2]=g(" 刷新 ",-1))]),_:1},8,["loading"])]),_:1})]),e.store.lastNotification?(i(),T(o,{key:0,type:e.store.lastNotification.status==="completed"?"success":"error","show-icon":"",closable:"",class:"banner",onClose:l[1]||(l[1]=t=>e.store.lastNotification=null)},{message:a(()=>[g(" 任务"+r(e.store.lastNotification.status==="completed"?"完成":"结束")+": "+r(e.store.lastNotification.description||e.store.lastNotification.id.slice(0,16)),1)]),description:a(()=>[e.store.lastNotification.error?(i(),c("span",Z,r(e.store.lastNotification.error),1)):e.store.lastNotification.result?(i(),c("span",j,r(e.truncate(String(e.store.lastNotification.result),120)),1)):w("v-if",!0)]),_:1},8,["type"])):w("v-if",!0),n(C,{gutter:[16,16],class:"stats-row"},{default:a(()=>[n(v,{xs:12,sm:6},{default:a(()=>[n(m,{class:"stat-card",size:"small"},{default:a(()=>[n(d,{title:"全部任务",value:e.store.tasks.length},null,8,["value"])]),_:1})]),_:1}),n(v,{xs:12,sm:6},{default:a(()=>[n(m,{class:"stat-card",size:"small"},{default:a(()=>[n(d,{title:"运行中",value:e.store.running.length},null,8,["value"])]),_:1})]),_:1}),n(v,{xs:12,sm:6},{default:a(()=>[n(m,{class:"stat-card",size:"small"},{default:a(()=>[n(d,{title:"等待中",value:e.store.pending.length},null,8,["value"])]),_:1})]),_:1}),n(v,{xs:12,sm:6},{default:a(()=>[n(m,{class:"stat-card",size:"small"},{default:a(()=>[n(d,{title:"已完成",value:e.store.completed.length},null,8,["value"])]),_:1})]),_:1})]),_:1}),e.store.running.length>0?(i(),T(m,{key:1,title:"运行中的任务",class:"panel-card",size:"small"},{default:a(()=>[(i(!0),c(h,null,F(e.store.running,t=>(i(),c("div",{key:t.id,class:"task-item running"},[_("div",J,[n(N,{color:e.store.getStatusColor(t.status)},{default:a(()=>[g(r(t.status.toUpperCase()),1)]),_:2},1032,["color"]),_("span",K,r(t.description),1),_("span",M,r(t.id.slice(0,16)),1)]),_("div",Q,[_("span",null,"类型: "+r(t.type||"-"),1),_("span",null,"已运行: "+r(e.store.formatDuration(Date.now()-t.startedAt)),1),n(y,{size:"small",danger:"",onClick:s=>e.store.stopTask(t.id)},{default:a(()=>[...l[4]||(l[4]=[g("停止",-1)])]),_:1},8,["onClick"])])]))),128))]),_:1})):w("v-if",!0),n(m,{class:"panel-card"},{default:a(()=>[n(A,{columns:e.columns,"data-source":e.store.tasks,pagination:{pageSize:15,size:"small"},loading:e.store.loading,"row-key":"id",size:"small"},{bodyCell:a(({column:t,record:s})=>[t.key==="status"?(i(),T(N,{key:0,color:e.store.getStatusColor(s.status)},{default:a(()=>[g(r(s.status),1)]),_:2},1032,["color"])):t.key==="description"?(i(),c("span",{key:1,title:s.command},r(s.description),9,W)):t.key==="duration"?(i(),c(h,{key:2},[g(r(e.getDuration(s)),1)],64)):t.key==="createdAt"?(i(),c(h,{key:3},[g(r(e.formatTime(s.createdAt)),1)],64)):t.key==="result"?(i(),c(h,{key:4},[s.error?(i(),c("span",q,r(e.truncate(s.error,60)),1)):s.result?(i(),c("span",G,r(e.truncate(String(s.result),60)),1)):(i(),c("span",H,"-"))],64)):t.key==="action"?(i(),c(h,{key:5},[s.status==="running"?(i(),T(y,{key:0,size:"small",danger:"",onClick:S=>e.store.stopTask(s.id)},{default:a(()=>[...l[5]||(l[5]=[g(" 停止 ",-1)])]),_:1},8,["onClick"])):w("v-if",!0)],64)):w("v-if",!0)]),_:1},8,["data-source","loading"])]),_:1})])}const et=O(U,[["render",X],["__scopeId","data-v-da5ff6a2"],["__file","/tmp/cc-web-panel-yoZvdA/repo/packages/web-panel/src/views/Tasks.vue"]]);export{et as default};
@@ -1,3 +0,0 @@
1
- const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./xterm-BZcWGsqw.js","./markdown-CsiA8-E5.js","./markdown-Dfs9RUU9.css","./addon-fit-CK6X9sAG.js","./xterm-DFuMZ0ql.css"])))=>i.map(i=>d[i]);
2
- import{u as fe,_ as me,d as V,e as U}from"./index-CCyB-RK5.js";import{I as z,J as x,U as y,Q as D,S as K,K as T,V as R,c as A,F as Q,Z as Y,R as E,o as G,f as H,w as $,n as q,b as ee,r as S,P as _,_ as ve,a2 as we,a3 as he,a4 as ye}from"./vendor-BvqAck49.js";import{R as _e,b as pe,as as ge}from"./icons-DP3uiYxy.js";const k=new Map,C=new Map,N=new Set,F=new Set;let te=!1;function xe(c){te||(te=!0,c.onMessage(o=>{if(!(!o||typeof o.type!="string")){if(o.type==="terminal.stdout"){const{sessionId:s,data:e,seq:i}=o.payload||{};if(!s)return;let w;try{const d=atob(e||""),f=new Uint8Array(d.length);for(let n=0;n<d.length;n++)f[n]=d.charCodeAt(n);w=new TextDecoder("utf-8").decode(f)}catch{w=""}const u={sessionId:s,data:w,seq:i};k.get(s)?.forEach(d=>d(u)),N.forEach(d=>d(u))}else if(o.type==="terminal.exit"){const{sessionId:s,exitCode:e,signal:i}=o.payload||{};if(!s)return;const w={sessionId:s,exitCode:e,signal:i};C.get(s)?.forEach(u=>u(w)),F.forEach(u=>u(w))}}}))}function ne(c){const o=new TextEncoder().encode(c);let s="";for(let e=0;e<o.length;e++)s+=String.fromCharCode(o[e]);return btoa(s)}function ae(c){const o=atob(c||""),s=new Uint8Array(o.length);for(let e=0;e<o.length;e++)s[e]=o.charCodeAt(e);return new TextDecoder("utf-8").decode(s)}function oe(){const c=fe();xe(c);async function o(n={}){const a=await c.sendRaw({type:"terminal.create",payload:{shell:n.shell,cwd:n.cwd,env:n.env,cols:n.cols,rows:n.rows}});if(a.ok===!1)throw new Error(a.error||"terminal_create_failed");return a.result??a}async function s(){const n=await c.sendRaw({type:"terminal.list",payload:{}});if(n.ok===!1)throw new Error(n.error||"terminal_list_failed");const a=n.result??n;return Array.isArray(a.sessions)?a.sessions:[]}async function e(n,a){const r=await c.sendRaw({type:"terminal.stdin",payload:{sessionId:n,data:ne(String(a))}});if(r.ok===!1)throw new Error(r.error||"terminal_stdin_failed");return r.result??r}async function i(n,a,r){const h=await c.sendRaw({type:"terminal.resize",payload:{sessionId:n,cols:a,rows:r}});if(h.ok===!1)throw new Error(h.error||"terminal_resize_failed");return h.result??h}async function w(n){const a=await c.sendRaw({type:"terminal.close",payload:{sessionId:n}});if(a.ok===!1)throw new Error(a.error||"terminal_close_failed");return a.result??a}async function u(n,a=0){const r=await c.sendRaw({type:"terminal.history",payload:{sessionId:n,fromSeq:a}});if(r.ok===!1)throw new Error(r.error||"terminal_history_failed");const h=r.result??r;return{truncated:!!h.truncated,chunks:(h.chunks||[]).map(L=>({seq:L.seq,data:ae(L.data)}))}}function d(n,a){return n?(k.has(n)||k.set(n,new Set),k.get(n).add(a),()=>{k.get(n)?.delete(a),k.get(n)?.size===0&&k.delete(n)}):(N.add(a),()=>N.delete(a))}function f(n,a){return n?(C.has(n)||C.set(n,new Set),C.get(n).add(a),()=>{C.get(n)?.delete(a),C.get(n)?.size===0&&C.delete(n)}):(F.add(a),()=>F.delete(a))}return{create:o,list:s,stdin:e,resize:i,close:w,history:u,onStdout:d,onExit:f,_internal:{stdoutSubs:k,exitSubs:C,toBase64Utf8:ne,fromBase64Utf8:ae}}}const Se={__name:"Terminal",setup(c,{expose:o}){o();const s=oe(),e=S([]),i=S(null),w=S("pwsh"),u=S(!1),d=S(!1),f=S(""),n=S(""),a=S([]),r=[{value:"pwsh",label:"PowerShell"},{value:"cmd",label:"CMD"},{value:"bash",label:"Bash"},{value:"wsl",label:"WSL"}],h=ee(()=>e.value.find(t=>t.id===i.value));function L(t){return t?t.slice(0,8):""}let O=null,M=null;async function I(){if(O)return{xtermMod:O,fitAddonMod:M};try{O=await U(()=>import("./xterm-BZcWGsqw.js").then(t=>t.x),__vite__mapDeps([0,1,2]),import.meta.url),M=await U(()=>import("./addon-fit-CK6X9sAG.js").then(t=>t.a),__vite__mapDeps([3,1,2]),import.meta.url),await U(()=>Promise.resolve({}),__vite__mapDeps([4]),import.meta.url)}catch(t){throw n.value="xterm 资源加载失败:"+(t?.message||"未知错误"),t}return{xtermMod:O,fitAddonMod:M}}async function B(t){await q();const{xtermMod:l,fitAddonMod:p}=await I(),b=a.value.find(v=>v?.dataset?.sessionId===t.id);if(!b)return;const m=new l.Terminal({cursorBlink:!0,fontFamily:'Consolas, "Courier New", monospace',fontSize:13,theme:{background:"#1e1e1e",foreground:"#d4d4d4"},convertEol:!1}),P=new p.FitAddon;m.loadAddon(P),m.open(b);try{P.fit()}catch{}t.xterm=m,t.fitAddon=P;const ie=m.onData(v=>{s.stdin(t.id,v).catch(g=>{String(g?.message||"").includes("dangerous_keyword_blocked")?V.warning("该命令被桌面端拦截(高危关键字)"):V.error("stdin 失败: "+(g?.message||g))})}),ce=s.onStdout(t.id,({data:v,seq:g})=>{t.lastSeq=g,m.write(v)}),de=s.onExit(t.id,({exitCode:v,signal:g})=>{t.alive=!1,t.exitCode=v,t.signal=g,m.writeln(`\r
3
- \x1B[33m[session exited, code=${v}, signal=${g??"-"}]\x1B[0m`)});t.offs=()=>{try{ie.dispose?.()}catch{}ce(),de()};try{const{chunks:v,truncated:g}=await s.history(t.id,0);g&&m.writeln("\x1B[2m[history truncated — earlier output was evicted]\x1B[0m");for(const J of v)m.write(J.data),t.lastSeq=J.seq}catch(v){m.writeln(`\x1B[31m[history fetch failed: ${v?.message||v}]\x1B[0m`)}const j=new ResizeObserver(()=>{try{P.fit(),s.resize(t.id,m.cols,m.rows).catch(()=>{})}catch{}});j.observe(b);const ue=t.offs;t.offs=()=>{try{j.disconnect()}catch{}ue()}}async function re(){u.value=!0,f.value="";try{const t=await s.create({shell:w.value,cols:80,rows:24}),l={id:t.sessionId,shell:t.shell,cwd:"",alive:!0,lastSeq:0,exitCode:null,xterm:null,fitAddon:null,offs:()=>{}};e.value.push(l),i.value=l.id,await B(l)}catch(t){f.value=t?.message||String(t)}finally{u.value=!1}}async function se(t){try{await s.close(t)}catch(l){f.value=l?.message||String(l)}setTimeout(()=>W(t),500)}function W(t){const l=e.value.findIndex(b=>b.id===t);if(l===-1)return;const p=e.value[l];try{p.offs?.()}catch{}try{p.xterm?.dispose?.()}catch{}e.value.splice(l,1),i.value===t&&(i.value=e.value[0]?.id||null)}function le(t){i.value=t,q(()=>{const l=e.value.find(p=>p.id===t);try{l?.fitAddon?.fit()}catch{}})}async function X(){d.value=!0,f.value="";try{const t=await s.list();for(const l of t){const p=e.value.find(m=>m.id===l.id);if(p){p.alive=l.alive,p.lastSeq=l.lastSeq;continue}const b={id:l.id,shell:l.shell,cwd:l.cwd,alive:l.alive,lastSeq:l.lastSeq,exitCode:null,xterm:null,fitAddon:null,offs:()=>{}};e.value.push(b),await B(b)}!i.value&&e.value.length>0&&(i.value=e.value[0].id)}catch(t){f.value=t?.message||String(t)}finally{d.value=!1}}G(async()=>{await X()}),H(()=>{for(const t of e.value){try{t.offs?.()}catch{}try{t.xterm?.dispose?.()}catch{}}}),$(i,()=>{q(()=>{const t=h.value;try{t?.fitAddon?.fit()}catch{}})});const Z={term:s,sessions:e,activeId:i,newShell:w,creating:u,loadingList:d,error:f,warning:n,xtermContainers:a,shellOptions:r,active:h,shortId:L,get xtermMod(){return O},set xtermMod(t){O=t},get fitAddonMod(){return M},set fitAddonMod(t){M=t},loadXterm:I,mountXterm:B,onCreate:re,onClose:se,removeSession:W,activate:le,refreshList:X,ref:S,computed:ee,onMounted:G,onBeforeUnmount:H,nextTick:q,watch:$,get message(){return V},get PlusOutlined(){return ge},get CloseOutlined(){return pe},get ReloadOutlined(){return _e},get useTerminal(){return oe}};return Object.defineProperty(Z,"__isScriptSetup",{enumerable:!1,value:!0}),Z}},be={class:"terminal-page"},ke={class:"terminal-header"},Ce={class:"page-sub"},Ae={class:"terminal-body"},Ee={class:"session-tabs"},Oe=["onClick"],Te={class:"session-shell"},Re={class:"session-id"},Me={key:0,class:"session-empty"},ze={class:"xterm-host"},Le=["data-session-id"],Pe={key:0,class:"xterm-placeholder"},De={key:1,class:"terminal-footer"},qe={key:0,class:"footer-exit"};function Be(c,o,s,e,i,w){const u=z("a-tag"),d=z("a-select"),f=z("a-button"),n=z("a-space"),a=z("a-alert");return _(),x("div",be,[y("div",ke,[y("div",null,[o[3]||(o[3]=y("h2",{class:"page-title"},"远程终端",-1)),y("p",Ce,[o[2]||(o[2]=D(" 桌面端托管的 PTY 会话;Android 端可远程操控同一通道 ",-1)),e.warning?(_(),K(u,{key:0,color:"orange",style:{"margin-left":"8px"}},{default:T(()=>[D(E(e.warning),1)]),_:1})):R("v-if",!0)])]),A(n,null,{default:T(()=>[A(d,{value:e.newShell,"onUpdate:value":o[0]||(o[0]=r=>e.newShell=r),style:{width:"130px"},size:"small",options:e.shellOptions},null,8,["value"]),A(f,{type:"primary",size:"small",loading:e.creating,onClick:e.onCreate},{icon:T(()=>[A(e.PlusOutlined)]),default:T(()=>[o[4]||(o[4]=D(" 新会话 ",-1))]),_:1},8,["loading"]),A(f,{size:"small",loading:e.loadingList,onClick:e.refreshList},{icon:T(()=>[A(e.ReloadOutlined)]),default:T(()=>[o[5]||(o[5]=D(" 刷新 ",-1))]),_:1},8,["loading"])]),_:1})]),e.error?(_(),K(a,{key:0,message:e.error,type:"error","show-icon":"",closable:"",style:{"margin-bottom":"12px"},onClose:o[1]||(o[1]=r=>e.error="")},null,8,["message"])):R("v-if",!0),y("div",Ae,[y("div",Ee,[(_(!0),x(Q,null,Y(e.sessions,r=>(_(),x("div",{key:r.id,class:ve(["session-tab",{active:r.id===e.activeId,dead:!r.alive}]),onClick:h=>e.activate(r.id)},[y("span",Te,E(r.shell),1),y("span",Re,E(e.shortId(r.id)),1),A(e.CloseOutlined,{class:"session-close",onClick:we(h=>e.onClose(r.id),["stop"])},null,8,["onClick"])],10,Oe))),128)),e.sessions.length===0?(_(),x("div",Me,' 点击 "新会话" 创建第一个终端 ')):R("v-if",!0)]),y("div",ze,[(_(!0),x(Q,null,Y(e.sessions,r=>he((_(),x("div",{key:r.id,ref_for:!0,ref:"xtermContainers","data-session-id":r.id,class:"xterm-container"},null,8,Le)),[[ye,r.id===e.activeId]])),128)),e.sessions.length===0?(_(),x("div",Pe,[...o[6]||(o[6]=[y("span",null,"无活跃会话",-1)])])):R("v-if",!0)])]),e.active?(_(),x("div",De,[y("span",null,E(e.active.shell)+" · "+E(e.active.cwd||"(默认 cwd)")+" · seq "+E(e.active.lastSeq),1),e.active.alive?R("v-if",!0):(_(),x("span",qe,"已退出 (code="+E(e.active.exitCode??"-")+")",1))])):R("v-if",!0)])}const Fe=me(Se,[["render",Be],["__scopeId","data-v-65366a29"],["__file","/tmp/cc-web-panel-yoZvdA/repo/packages/web-panel/src/views/Terminal.vue"]]);export{Fe as default};
@@ -1 +0,0 @@
1
- import{O as r}from"./index-CCyB-RK5.js";const o=((n,a,e)=>{r(n,`[ant-design-vue: ${a}] ${e}`)});export{o as d};
@@ -1 +0,0 @@
1
- import{C as o}from"./Col-DttmlDRk.js";import{U as t}from"./index-CCyB-RK5.js";import"./vendor-BvqAck49.js";import"./index-Ct8qhPZe.js";import"./icons-DP3uiYxy.js";const s=t(o);export{s as default};
@@ -1 +0,0 @@
1
- import{A as o}from"./Row-DpA9dlvi.js";import{U as t}from"./index-CCyB-RK5.js";import"./vendor-BvqAck49.js";import"./responsiveObserve-CcL2K-YY.js";import"./useFlexGapSupport-CVhutCN8.js";import"./styleChecker-C436m5Xy.js";import"./index-Ct8qhPZe.js";import"./icons-DP3uiYxy.js";const l=t(o);export{l as default};