abtars 0.1.0-alpha.1

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 (312) hide show
  1. package/LICENSE +190 -0
  2. package/README.md +84 -0
  3. package/bundle/_registry.generated-M4WY2MMI.js +35 -0
  4. package/bundle/_registry.generated-M4WY2MMI.js.map +7 -0
  5. package/bundle/abtars-browser.js +162 -0
  6. package/bundle/abtars-browser.js.map +7 -0
  7. package/bundle/abtars-cli.js +1438 -0
  8. package/bundle/abtars-cli.js.map +7 -0
  9. package/bundle/abtars-restart.js +12 -0
  10. package/bundle/abtars-restart.js.map +7 -0
  11. package/bundle/abtars-rss.js +165 -0
  12. package/bundle/abtars-rss.js.map +7 -0
  13. package/bundle/abtars-task.js +258 -0
  14. package/bundle/abtars-task.js.map +7 -0
  15. package/bundle/abtars.js +4072 -0
  16. package/bundle/abtars.js.map +7 -0
  17. package/bundle/agent-api-rate-limit-OQNFMXTZ.js +38 -0
  18. package/bundle/agent-api-rate-limit-OQNFMXTZ.js.map +7 -0
  19. package/bundle/agent-registry-LT4JNQH6.js +18 -0
  20. package/bundle/agent-registry-LT4JNQH6.js.map +7 -0
  21. package/bundle/agents/default.md +29 -0
  22. package/bundle/anthropic-adapter-2APTH3LA.js +40 -0
  23. package/bundle/anthropic-adapter-2APTH3LA.js.map +7 -0
  24. package/bundle/bridge-lock-transport-4AC2G5G6.js +39 -0
  25. package/bundle/bridge-lock-transport-4AC2G5G6.js.map +7 -0
  26. package/bundle/browse-delivery-JXBY36GK.js +17 -0
  27. package/bundle/browse-delivery-JXBY36GK.js.map +7 -0
  28. package/bundle/browser-ELNDVPLC.js +18 -0
  29. package/bundle/browser-ELNDVPLC.js.map +7 -0
  30. package/bundle/capability-CIL3G4FI.js +17 -0
  31. package/bundle/capability-CIL3G4FI.js.map +7 -0
  32. package/bundle/chunk-265TPOPC.js +289 -0
  33. package/bundle/chunk-265TPOPC.js.map +7 -0
  34. package/bundle/chunk-2UENBO6M.js +223 -0
  35. package/bundle/chunk-2UENBO6M.js.map +7 -0
  36. package/bundle/chunk-2UPU3OW6.js +67 -0
  37. package/bundle/chunk-2UPU3OW6.js.map +7 -0
  38. package/bundle/chunk-2XU2X4OI.js +125 -0
  39. package/bundle/chunk-2XU2X4OI.js.map +7 -0
  40. package/bundle/chunk-3B7BBE4F.js +758 -0
  41. package/bundle/chunk-3B7BBE4F.js.map +7 -0
  42. package/bundle/chunk-3E545J66.js +69 -0
  43. package/bundle/chunk-3E545J66.js.map +7 -0
  44. package/bundle/chunk-5R2ANXQ7.js +510 -0
  45. package/bundle/chunk-5R2ANXQ7.js.map +7 -0
  46. package/bundle/chunk-6CPN4IGS.js +507 -0
  47. package/bundle/chunk-6CPN4IGS.js.map +7 -0
  48. package/bundle/chunk-6NR3OHEW.js +88 -0
  49. package/bundle/chunk-6NR3OHEW.js.map +7 -0
  50. package/bundle/chunk-6SETMHNN.js +206 -0
  51. package/bundle/chunk-6SETMHNN.js.map +7 -0
  52. package/bundle/chunk-6UCRKRWR.js +644 -0
  53. package/bundle/chunk-6UCRKRWR.js.map +7 -0
  54. package/bundle/chunk-AR6GO6YC.js +83 -0
  55. package/bundle/chunk-AR6GO6YC.js.map +7 -0
  56. package/bundle/chunk-AZJIODTQ.js +54 -0
  57. package/bundle/chunk-AZJIODTQ.js.map +7 -0
  58. package/bundle/chunk-BHMZ4RCC.js +3706 -0
  59. package/bundle/chunk-BHMZ4RCC.js.map +7 -0
  60. package/bundle/chunk-BQ2L4GMG.js +9175 -0
  61. package/bundle/chunk-BQ2L4GMG.js.map +7 -0
  62. package/bundle/chunk-BSSBCSCL.js +159 -0
  63. package/bundle/chunk-BSSBCSCL.js.map +7 -0
  64. package/bundle/chunk-BUUVFUPO.js +157 -0
  65. package/bundle/chunk-BUUVFUPO.js.map +7 -0
  66. package/bundle/chunk-CEVRHKJY.js +131 -0
  67. package/bundle/chunk-CEVRHKJY.js.map +7 -0
  68. package/bundle/chunk-CWOHNFUV.js +39 -0
  69. package/bundle/chunk-CWOHNFUV.js.map +7 -0
  70. package/bundle/chunk-D2DCBO6M.js +228 -0
  71. package/bundle/chunk-D2DCBO6M.js.map +7 -0
  72. package/bundle/chunk-FMWKEPM7.js +31 -0
  73. package/bundle/chunk-FMWKEPM7.js.map +7 -0
  74. package/bundle/chunk-GRNENTPA.js +145 -0
  75. package/bundle/chunk-GRNENTPA.js.map +7 -0
  76. package/bundle/chunk-GST5T3WZ.js +93 -0
  77. package/bundle/chunk-GST5T3WZ.js.map +7 -0
  78. package/bundle/chunk-GUQVJC3U.js +299 -0
  79. package/bundle/chunk-GUQVJC3U.js.map +7 -0
  80. package/bundle/chunk-HX7Y7EYP.js +3659 -0
  81. package/bundle/chunk-HX7Y7EYP.js.map +7 -0
  82. package/bundle/chunk-JCJS4ZIB.js +296 -0
  83. package/bundle/chunk-JCJS4ZIB.js.map +7 -0
  84. package/bundle/chunk-JW6RU47G.js +184 -0
  85. package/bundle/chunk-JW6RU47G.js.map +7 -0
  86. package/bundle/chunk-LSPKJQCI.js +24 -0
  87. package/bundle/chunk-LSPKJQCI.js.map +7 -0
  88. package/bundle/chunk-M6VBAPNT.js +16 -0
  89. package/bundle/chunk-M6VBAPNT.js.map +7 -0
  90. package/bundle/chunk-MPX525QO.js +129 -0
  91. package/bundle/chunk-MPX525QO.js.map +7 -0
  92. package/bundle/chunk-MW6WDLU7.js +130 -0
  93. package/bundle/chunk-MW6WDLU7.js.map +7 -0
  94. package/bundle/chunk-NT3OBORC.js +215 -0
  95. package/bundle/chunk-NT3OBORC.js.map +7 -0
  96. package/bundle/chunk-NWDBD4PA.js +50 -0
  97. package/bundle/chunk-NWDBD4PA.js.map +7 -0
  98. package/bundle/chunk-OP7BTAWY.js +29 -0
  99. package/bundle/chunk-OP7BTAWY.js.map +7 -0
  100. package/bundle/chunk-PLCY3GFH.js +77 -0
  101. package/bundle/chunk-PLCY3GFH.js.map +7 -0
  102. package/bundle/chunk-PNEDC45Y.js +97 -0
  103. package/bundle/chunk-PNEDC45Y.js.map +7 -0
  104. package/bundle/chunk-QBGBT5QS.js +81 -0
  105. package/bundle/chunk-QBGBT5QS.js.map +7 -0
  106. package/bundle/chunk-RVE2N7FA.js +70 -0
  107. package/bundle/chunk-RVE2N7FA.js.map +7 -0
  108. package/bundle/chunk-TZHIDLDS.js +71910 -0
  109. package/bundle/chunk-TZHIDLDS.js.map +7 -0
  110. package/bundle/chunk-UCQ2WC3B.js +126 -0
  111. package/bundle/chunk-UCQ2WC3B.js.map +7 -0
  112. package/bundle/chunk-UHRP745J.js +214 -0
  113. package/bundle/chunk-UHRP745J.js.map +7 -0
  114. package/bundle/chunk-V76TVMCM.js +58 -0
  115. package/bundle/chunk-V76TVMCM.js.map +7 -0
  116. package/bundle/chunk-VVEDVGCR.js +981 -0
  117. package/bundle/chunk-VVEDVGCR.js.map +7 -0
  118. package/bundle/chunk-W6FAL35D.js +102 -0
  119. package/bundle/chunk-W6FAL35D.js.map +7 -0
  120. package/bundle/chunk-X6TERNVJ.js +15902 -0
  121. package/bundle/chunk-X6TERNVJ.js.map +7 -0
  122. package/bundle/chunk-X76UX47U.js +47 -0
  123. package/bundle/chunk-X76UX47U.js.map +7 -0
  124. package/bundle/chunk-XREWVCUO.js +518 -0
  125. package/bundle/chunk-XREWVCUO.js.map +7 -0
  126. package/bundle/chunk-Y6XAEX2Q.js +408 -0
  127. package/bundle/chunk-Y6XAEX2Q.js.map +7 -0
  128. package/bundle/chunk-YOCTDKKL.js +28 -0
  129. package/bundle/chunk-YOCTDKKL.js.map +7 -0
  130. package/bundle/chunk-ZXPXCDA6.js +160 -0
  131. package/bundle/chunk-ZXPXCDA6.js.map +7 -0
  132. package/bundle/commands-BHVUOU3V.js +31 -0
  133. package/bundle/commands-BHVUOU3V.js.map +7 -0
  134. package/bundle/completion-buffer-P253ONKF.js +13 -0
  135. package/bundle/completion-buffer-P253ONKF.js.map +7 -0
  136. package/bundle/config-RGSDAPZN.js +19 -0
  137. package/bundle/config-RGSDAPZN.js.map +7 -0
  138. package/bundle/config-show-ERTATR6E.js +40 -0
  139. package/bundle/config-show-ERTATR6E.js.map +7 -0
  140. package/bundle/context-HCEGZNDC.js +72 -0
  141. package/bundle/context-HCEGZNDC.js.map +7 -0
  142. package/bundle/delegation-tools-GYTS2D6A.js +27 -0
  143. package/bundle/delegation-tools-GYTS2D6A.js.map +7 -0
  144. package/bundle/deploy-lib-import-32ZFKHWP.js +49 -0
  145. package/bundle/deploy-lib-import-32ZFKHWP.js.map +7 -0
  146. package/bundle/digital-signature-OFCGSHWO.js +13 -0
  147. package/bundle/digital-signature-OFCGSHWO.js.map +7 -0
  148. package/bundle/direct-api-transport-YR7SXXNN.js +860 -0
  149. package/bundle/direct-api-transport-YR7SXXNN.js.map +7 -0
  150. package/bundle/discord-adapter-YYWVMPPU.js +584 -0
  151. package/bundle/discord-adapter-YYWVMPPU.js.map +7 -0
  152. package/bundle/dist-MTMKARCP.js +1969 -0
  153. package/bundle/dist-MTMKARCP.js.map +7 -0
  154. package/bundle/dns-wakeup-27M7D2MR.js +107 -0
  155. package/bundle/dns-wakeup-27M7D2MR.js.map +7 -0
  156. package/bundle/doctor-QNUSDY73.js +248 -0
  157. package/bundle/doctor-QNUSDY73.js.map +7 -0
  158. package/bundle/ensure-invariants-NMXNS476.js +49 -0
  159. package/bundle/ensure-invariants-NMXNS476.js.map +7 -0
  160. package/bundle/env-schema-2KBHBDGN.js +19 -0
  161. package/bundle/env-schema-2KBHBDGN.js.map +7 -0
  162. package/bundle/esm-DDP6NCZG.js +100663 -0
  163. package/bundle/esm-DDP6NCZG.js.map +7 -0
  164. package/bundle/fallback-policy-L4QV2PEJ.js +46 -0
  165. package/bundle/fallback-policy-L4QV2PEJ.js.map +7 -0
  166. package/bundle/health-check-SPA7NT6N.js +56 -0
  167. package/bundle/health-check-SPA7NT6N.js.map +7 -0
  168. package/bundle/hook-system-6Q5YTR53.js +17 -0
  169. package/bundle/hook-system-6Q5YTR53.js.map +7 -0
  170. package/bundle/hotskills-K7BM4YLB.js +12 -0
  171. package/bundle/hotskills-K7BM4YLB.js.map +7 -0
  172. package/bundle/install-6HRZVKUM.js +15 -0
  173. package/bundle/install-6HRZVKUM.js.map +7 -0
  174. package/bundle/install-log-IAPHYKD4.js +28 -0
  175. package/bundle/install-log-IAPHYKD4.js.map +7 -0
  176. package/bundle/install-manifest-SPQRUNXL.js +102 -0
  177. package/bundle/install-manifest-SPQRUNXL.js.map +7 -0
  178. package/bundle/install-validate-PVLZXYLQ.js +53 -0
  179. package/bundle/install-validate-PVLZXYLQ.js.map +7 -0
  180. package/bundle/irc-adapter-OI5UZSQF.js +293 -0
  181. package/bundle/irc-adapter-OI5UZSQF.js.map +7 -0
  182. package/bundle/irc-config-55YO6EGB.js +88 -0
  183. package/bundle/irc-config-55YO6EGB.js.map +7 -0
  184. package/bundle/logs-ZNYXX5PA.js +19 -0
  185. package/bundle/logs-ZNYXX5PA.js.map +7 -0
  186. package/bundle/media-utils-XNNDTYFI.js +4662 -0
  187. package/bundle/media-utils-XNNDTYFI.js.map +7 -0
  188. package/bundle/message-pipeline-LLH5SYMO.js +33 -0
  189. package/bundle/message-pipeline-LLH5SYMO.js.map +7 -0
  190. package/bundle/meta.json +41304 -0
  191. package/bundle/model-health-registry-35LQNVQR.js +11 -0
  192. package/bundle/model-health-registry-35LQNVQR.js.map +7 -0
  193. package/bundle/notification-Y5S5MMLV.js +13 -0
  194. package/bundle/notification-Y5S5MMLV.js.map +7 -0
  195. package/bundle/openrouter-credits-EDY7ETAU.js +32 -0
  196. package/bundle/openrouter-credits-EDY7ETAU.js.map +7 -0
  197. package/bundle/passwd-RRFV4CC5.js +133 -0
  198. package/bundle/passwd-RRFV4CC5.js.map +7 -0
  199. package/bundle/paths-G33RZWZ7.js +17 -0
  200. package/bundle/paths-G33RZWZ7.js.map +7 -0
  201. package/bundle/peer-client-52XYMNI7.js +156 -0
  202. package/bundle/peer-client-52XYMNI7.js.map +7 -0
  203. package/bundle/peer-config-VK6EDLN5.js +16 -0
  204. package/bundle/peer-config-VK6EDLN5.js.map +7 -0
  205. package/bundle/peer-sessions-EAXTNQ36.js +49 -0
  206. package/bundle/peer-sessions-EAXTNQ36.js.map +7 -0
  207. package/bundle/pending-callback-RIMQZ7FJ.js +40 -0
  208. package/bundle/pending-callback-RIMQZ7FJ.js.map +7 -0
  209. package/bundle/phase-transport-KYERDL2O.js +22 -0
  210. package/bundle/phase-transport-KYERDL2O.js.map +7 -0
  211. package/bundle/public/css/dashboard.css +542 -0
  212. package/bundle/public/index.html +180 -0
  213. package/bundle/public/js/app.js +437 -0
  214. package/bundle/public/memory-universe.js +384 -0
  215. package/bundle/responses-adapter-AAQTY3K4.js +30 -0
  216. package/bundle/responses-adapter-AAQTY3K4.js.map +7 -0
  217. package/bundle/restore-ZE3SEPSS.js +46 -0
  218. package/bundle/restore-ZE3SEPSS.js.map +7 -0
  219. package/bundle/self-healer-utils-DMUUXC47.js +43 -0
  220. package/bundle/self-healer-utils-DMUUXC47.js.map +7 -0
  221. package/bundle/skill-stats-LLEXEXLR.js +22 -0
  222. package/bundle/skill-stats-LLEXEXLR.js.map +7 -0
  223. package/bundle/sleep-OYIUOVQD.js +19 -0
  224. package/bundle/sleep-OYIUOVQD.js.map +7 -0
  225. package/bundle/soul-loader-54WCVNLJ.js +16 -0
  226. package/bundle/soul-loader-54WCVNLJ.js.map +7 -0
  227. package/bundle/src-JL4PVO23.js +8 -0
  228. package/bundle/src-JL4PVO23.js.map +7 -0
  229. package/bundle/sse-parser-anthropic-P7CE2MH2.js +72 -0
  230. package/bundle/sse-parser-anthropic-P7CE2MH2.js.map +7 -0
  231. package/bundle/sse-parser-responses-EQQA5FWN.js +63 -0
  232. package/bundle/sse-parser-responses-EQQA5FWN.js.map +7 -0
  233. package/bundle/ssrf-guard-FZCBYIVW.js +64 -0
  234. package/bundle/ssrf-guard-FZCBYIVW.js.map +7 -0
  235. package/bundle/start-FH3GRMJ4.js +35 -0
  236. package/bundle/start-FH3GRMJ4.js.map +7 -0
  237. package/bundle/stream-single-WSG4D53C.js +33 -0
  238. package/bundle/stream-single-WSG4D53C.js.map +7 -0
  239. package/bundle/stt-2UH3RITX.js +14 -0
  240. package/bundle/stt-2UH3RITX.js.map +7 -0
  241. package/bundle/subagent-runtime-LE2ZXH3G.js +12 -0
  242. package/bundle/subagent-runtime-LE2ZXH3G.js.map +7 -0
  243. package/bundle/system-message-T5R3EYYN.js +30 -0
  244. package/bundle/system-message-T5R3EYYN.js.map +7 -0
  245. package/bundle/system-status-KQ6KHFJ6.js +189 -0
  246. package/bundle/system-status-KQ6KHFJ6.js.map +7 -0
  247. package/bundle/task-store-K7CQDEPI.js +22 -0
  248. package/bundle/task-store-K7CQDEPI.js.map +7 -0
  249. package/bundle/telegram-adapter-2V3XUMT5.js +1060 -0
  250. package/bundle/telegram-adapter-2V3XUMT5.js.map +7 -0
  251. package/bundle/tool-registry-MU3OX4UI.js +38 -0
  252. package/bundle/tool-registry-MU3OX4UI.js.map +7 -0
  253. package/bundle/tool-sandbox-VYOK4ZOA.js +20 -0
  254. package/bundle/tool-sandbox-VYOK4ZOA.js.map +7 -0
  255. package/bundle/transport-config-YLXU33RO.js +57 -0
  256. package/bundle/transport-config-YLXU33RO.js.map +7 -0
  257. package/bundle/update-QCW5LXRN.js +13 -0
  258. package/bundle/update-QCW5LXRN.js.map +7 -0
  259. package/bundle/update-check-27KZSAP6.js +12 -0
  260. package/bundle/update-check-27KZSAP6.js.map +7 -0
  261. package/bundle/usage-tracker-OVVEVMOY.js +17 -0
  262. package/bundle/usage-tracker-OVVEVMOY.js.map +7 -0
  263. package/bundle/user-registry-D4SD73UV.js +16 -0
  264. package/bundle/user-registry-D4SD73UV.js.map +7 -0
  265. package/core/professor.json +14 -0
  266. package/core/prompts/browsing_prompt.md +39 -0
  267. package/core/prompts/compaction.md +32 -0
  268. package/core/skills/memory/classification/SKILL.md +37 -0
  269. package/core/skills/memory/memory-anomalies/SKILL.md +39 -0
  270. package/core/skills/memory/memory-search/SKILL.md +48 -0
  271. package/core/skills/memory/topic-save/SKILL.md +44 -0
  272. package/core/skills/ops/cron/SKILL.md +51 -0
  273. package/core/skills/ops/gdrive-backup/SKILL.md +15 -0
  274. package/core/skills/ops/session-start/SKILL.md +11 -0
  275. package/core/skills/ops/skill-authoring/SKILL.md +54 -0
  276. package/core/skills/ops/system-health/SKILL.md +104 -0
  277. package/core/skills/ops/troubleshooting/SKILL.md +48 -0
  278. package/core/skills/ops/trust-gating/SKILL.md +30 -0
  279. package/core/skills/tools/a2a-communication/SKILL.md +68 -0
  280. package/core/skills/tools/browse-delegate/SKILL.md +27 -0
  281. package/core/skills/tools/browser/SKILL.md +36 -0
  282. package/core/skills/tools/clawhub/SKILL.md +44 -0
  283. package/core/skills/tools/delegation/SKILL.md +48 -0
  284. package/core/skills/tools/fxtwitter/SKILL.md +52 -0
  285. package/core/skills/tools/gmail/SKILL.md +44 -0
  286. package/core/skills/tools/irc-chat/SKILL.md +84 -0
  287. package/core/skills/tools/linear/SKILL.md +90 -0
  288. package/core/skills/tools/mcporter/SKILL.md +46 -0
  289. package/core/skills/tools/model-scout/SKILL.md +132 -0
  290. package/core/skills/tools/model-scout/scout-add-model.py +67 -0
  291. package/core/skills/tools/model-scout/scout-ollama.py +116 -0
  292. package/core/skills/tools/model-scout/scout-openrouter.py +85 -0
  293. package/core/skills/tools/nlm/SKILL.md +40 -0
  294. package/core/skills/tools/todo/SKILL.md +30 -0
  295. package/core/skills/tools/twitterX/SKILL.md +52 -0
  296. package/core/skills/tools/twitterX/scripts/abtars-tweet.js +532 -0
  297. package/core/skills/tools/twitterX/scripts/package.json +1 -0
  298. package/core/skills/tools/web-fetch/SKILL.md +29 -0
  299. package/package.json +59 -0
  300. package/scripts/abtars-daemon.service +23 -0
  301. package/scripts/abtars-fetch.sh +42 -0
  302. package/scripts/abtars-watchdog.service +13 -0
  303. package/scripts/abtars.sh +14 -0
  304. package/scripts/abtars@.service +21 -0
  305. package/scripts/browser-patchright.sh +79 -0
  306. package/scripts/com.abtars.daemon.plist +24 -0
  307. package/scripts/com.abtars.watchdog.plist +27 -0
  308. package/scripts/daily-backup.sh +62 -0
  309. package/scripts/doctor.sh +553 -0
  310. package/scripts/hooks/audit-logger.sh +22 -0
  311. package/scripts/upgrade-deps.sh +64 -0
  312. package/scripts/watchdog.sh +309 -0
@@ -0,0 +1,408 @@
1
+ import { createRequire as __bundleCreateRequire } from 'node:module'; import { fileURLToPath as __bundleFileURLToPath } from 'node:url'; import { dirname as __bundleDirname } from 'node:path'; const require = __bundleCreateRequire(import.meta.url); const __chunk_filename = __bundleFileURLToPath(import.meta.url); const __chunk_dirname = __bundleDirname(__chunk_filename);
2
+ import {
3
+ readEnvWithDefault
4
+ } from "./chunk-YOCTDKKL.js";
5
+ import {
6
+ TRANSPORT_SCHEMA,
7
+ init_config_validator,
8
+ validateShape
9
+ } from "./chunk-2UPU3OW6.js";
10
+ import {
11
+ init_log_and_swallow,
12
+ logAndSwallow
13
+ } from "./chunk-FMWKEPM7.js";
14
+ import {
15
+ getEnv,
16
+ init_env_schema
17
+ } from "./chunk-JCJS4ZIB.js";
18
+ import {
19
+ init_logger,
20
+ logError,
21
+ logInfo,
22
+ logWarn
23
+ } from "./chunk-BUUVFUPO.js";
24
+ import {
25
+ abtarsHome,
26
+ init_paths
27
+ } from "./chunk-X76UX47U.js";
28
+ import {
29
+ __require
30
+ } from "./chunk-NWDBD4PA.js";
31
+
32
+ // src/components/transport-config.ts
33
+ init_log_and_swallow();
34
+ init_env_schema();
35
+ init_config_validator();
36
+ init_paths();
37
+ import { readFileSync, writeFileSync, existsSync, statSync } from "node:fs";
38
+ import { join } from "node:path";
39
+ init_logger();
40
+ var TAG = "transport-config";
41
+ var cachedTransport = null;
42
+ function configDir() {
43
+ return join(abtarsHome(), "config");
44
+ }
45
+ function loadModels() {
46
+ const p = join(configDir(), getEnv().modelsConfig);
47
+ try {
48
+ return JSON.parse(readFileSync(p, "utf-8"));
49
+ } catch (err) {
50
+ logWarn(TAG, `Failed to load models.json: ${err instanceof Error ? err.message : String(err)}`);
51
+ return {};
52
+ }
53
+ }
54
+ function loadTransport() {
55
+ if (cachedTransport) return cachedTransport;
56
+ const dir = configDir();
57
+ const p = join(dir, getEnv().transportConfig);
58
+ try {
59
+ cachedTransport = JSON.parse(readFileSync(p, "utf-8"));
60
+ validateShape(cachedTransport, TRANSPORT_SCHEMA, "transport.json");
61
+ logInfo(TAG, `Loaded transport config (${Object.keys(cachedTransport.agents).length} agents, ${Object.keys(cachedTransport.providers).length} providers)`);
62
+ const repairs = validateAndRepair(cachedTransport);
63
+ if (repairs.length > 0) {
64
+ for (const r of repairs) logWarn(TAG, `Auto-repaired: ${r.agent} was on ${r.oldProvider} \u2014 ${r.reason}`);
65
+ writeTransportConfig(cachedTransport, `invariant auto-repair (${repairs.length} agents)`);
66
+ pendingRepairs = repairs;
67
+ }
68
+ return cachedTransport;
69
+ } catch (err) {
70
+ logAndSwallow(TAG, "loadTransport parse", err);
71
+ try {
72
+ cachedTransport = JSON.parse(readFileSync(join(dir, "transport.default.json"), "utf-8"));
73
+ logWarn(TAG, `transport.json missing/corrupt \u2014 loaded transport.default.json`);
74
+ return cachedTransport;
75
+ } catch (err2) {
76
+ logError(TAG, `No transport config available: ${err2 instanceof Error ? err2.message : String(err2)}`);
77
+ return null;
78
+ }
79
+ }
80
+ }
81
+ function resolveHailMary(transport) {
82
+ const tc = transport ?? loadTransport();
83
+ if (!tc?.hailMary) return null;
84
+ const provider = tc.providers[tc.hailMary.provider];
85
+ if (!provider?.endpoint) return null;
86
+ return { model: tc.hailMary.model, endpoint: provider.endpoint, apiKeyEnv: provider.apiKeyEnv };
87
+ }
88
+ function clearTransportCache() {
89
+ cachedTransport = null;
90
+ }
91
+ var pendingRepairs = [];
92
+ function consumeRepairs() {
93
+ const r = pendingRepairs;
94
+ pendingRepairs = [];
95
+ return r;
96
+ }
97
+ function validateAndRepair(tc) {
98
+ const profAssignment = tc.agents["professor"];
99
+ if (!profAssignment) return [];
100
+ const profProvider = tc.providers[profAssignment.provider];
101
+ if (!profProvider) return [];
102
+ const profType = profProvider.transport;
103
+ const repairs = [];
104
+ for (const [agent, assignment] of Object.entries(tc.agents)) {
105
+ if (agent === "professor") continue;
106
+ const provider = tc.providers[assignment.provider];
107
+ if (!provider) continue;
108
+ const agentType = provider.transport;
109
+ let violation = false;
110
+ if (agentType !== profType) {
111
+ violation = true;
112
+ } else if (profType !== "api" && assignment.provider !== profAssignment.provider) {
113
+ violation = true;
114
+ }
115
+ if (violation) {
116
+ repairs.push({ agent, oldProvider: assignment.provider, reason: `${provider.transport} incompatible with professor (${profType}/${profAssignment.provider})` });
117
+ tc.agents[agent] = { model: profAssignment.model, provider: profAssignment.provider };
118
+ }
119
+ }
120
+ const fallbacks = profAssignment.fallbacks;
121
+ if (fallbacks) {
122
+ for (let i = fallbacks.length - 1; i >= 0; i--) {
123
+ const fb = fallbacks[i];
124
+ const fbProvider = tc.providers[fb.provider];
125
+ if (!fbProvider) continue;
126
+ const fbType = fbProvider.transport;
127
+ if (fbType !== profType || profType !== "api" && fb.provider !== profAssignment.provider) {
128
+ repairs.push({ agent: `professor_fb${i + 1}`, oldProvider: fb.provider, reason: `fallback ${fbProvider.transport} incompatible with professor (${profType}/${profAssignment.provider})` });
129
+ fallbacks.splice(i, 1);
130
+ }
131
+ }
132
+ }
133
+ return repairs;
134
+ }
135
+ function resolveAgent(role, transport, models) {
136
+ const tc = transport ?? loadTransport();
137
+ if (!tc) return null;
138
+ const effectiveRole = role === "task" ? "professor" : role;
139
+ const assignment = tc.agents[effectiveRole];
140
+ if (!assignment) {
141
+ logWarn(TAG, `No agent assignment for role "${role}"`);
142
+ return null;
143
+ }
144
+ const providers = tc.providers;
145
+ let effectiveModel = assignment.model;
146
+ let effectiveProvider = assignment.provider;
147
+ const demotedModel = assignment.demotedModel ?? assignment.model;
148
+ if (assignment.demoted && demotedModel === assignment.model) {
149
+ const firstHealthy = (assignment.fallbacks ?? []).find((fb) => !fb.demoted);
150
+ if (firstHealthy) {
151
+ effectiveModel = firstHealthy.model;
152
+ effectiveProvider = firstHealthy.provider;
153
+ } else {
154
+ logWarn(TAG, `All models demoted for role "${role}" \u2014 using primary anyway`);
155
+ }
156
+ }
157
+ const resolvedProvider = providers[effectiveProvider];
158
+ if (!resolvedProvider) {
159
+ logWarn(TAG, `Provider "${effectiveProvider}" not found for role "${role}"`);
160
+ return null;
161
+ }
162
+ const mc = models ?? loadModels();
163
+ const modelEntry = mc[effectiveModel];
164
+ if (!modelEntry && effectiveModel) {
165
+ logWarn(TAG, `Model "${effectiveModel}" not in models.json \u2014 using defaults`);
166
+ }
167
+ return {
168
+ model: effectiveModel,
169
+ provider: resolvedProvider,
170
+ providerName: effectiveProvider,
171
+ contextWindow: modelEntry?.contextWindow ?? 128e3,
172
+ maxOutput: modelEntry?.maxOutput ?? 8192,
173
+ fallbacks: (assignment.fallbacks ?? []).filter((fb) => !fb.demoted && fb.model !== effectiveModel)
174
+ };
175
+ }
176
+ function getEnvFallback() {
177
+ const providerName = readEnvWithDefault("DEFAULT_PROVIDER", "openrouter", "default LLM provider");
178
+ const transport = getEnv().defaultTransport;
179
+ const model = readEnvWithDefault("DEFAULT_MODEL", "minimax-m2.5:cloud", "default LLM model");
180
+ const provider = { transport };
181
+ if (transport === "api") {
182
+ provider.endpoint = providerName === "openrouter" ? "https://openrouter.ai/api/v1" : "http://localhost:11434/v1";
183
+ if (providerName === "openrouter") provider.apiKeyEnv = "OPENROUTER_API_KEY";
184
+ }
185
+ return { provider, providerName, model, contextWindow: 128e3, maxOutput: 8192 };
186
+ }
187
+ function validateAtStartup() {
188
+ const tc = loadTransport();
189
+ if (!tc) return;
190
+ const mc = loadModels();
191
+ for (const [role, assignment] of Object.entries(tc.agents)) {
192
+ if (!tc.providers[assignment.provider]) {
193
+ logWarn(TAG, `Agent "${role}": provider "${assignment.provider}" not defined in providers`);
194
+ }
195
+ const modelEntry = mc[assignment.model];
196
+ if (!modelEntry) {
197
+ logWarn(TAG, `Agent "${role}": model "${assignment.model}" not in models.json`);
198
+ } else if (!modelEntry.transports.includes(assignment.provider)) {
199
+ logWarn(TAG, `Agent "${role}": model "${assignment.model}" not listed for provider "${assignment.provider}" in models.json`);
200
+ }
201
+ }
202
+ }
203
+ function writeTransportConfig(tc, reason) {
204
+ for (const [role, agent] of Object.entries(tc.agents)) {
205
+ if (!agent.model?.trim()) {
206
+ logWarn(TAG, `Refusing to write transport.json \u2014 agent "${role}" has empty model`);
207
+ return;
208
+ }
209
+ }
210
+ const p = join(configDir(), getEnv().transportConfig);
211
+ const oldPath = p.replace(".json", ".old.json");
212
+ try {
213
+ const oldAge = Date.now() - statSync(oldPath).mtimeMs;
214
+ if (oldAge > 15 * 6e4) writeFileSync(oldPath, readFileSync(p, "utf-8"), "utf-8");
215
+ } catch {
216
+ try {
217
+ writeFileSync(oldPath, readFileSync(p, "utf-8"), "utf-8");
218
+ } catch (err) {
219
+ logAndSwallow(TAG, "backup transport.old.json", err);
220
+ }
221
+ }
222
+ writeFileSync(p, JSON.stringify(tc, null, 2), "utf-8");
223
+ cachedTransport = tc;
224
+ logInfo(TAG, reason ? `transport.json updated \u2014 ${reason}` : "transport.json updated");
225
+ }
226
+ function cleanDemotedModels(tc, chosenModel) {
227
+ for (const agent of Object.values(tc.agents)) {
228
+ if (agent.demoted) {
229
+ if (agent.model === chosenModel) {
230
+ delete agent.demoted;
231
+ delete agent.demotedReason;
232
+ delete agent.demotedModel;
233
+ }
234
+ }
235
+ if (agent.fallbacks) {
236
+ for (const fb of agent.fallbacks) {
237
+ if (fb.demoted && fb.model === chosenModel) {
238
+ delete fb.demoted;
239
+ delete fb.demotedReason;
240
+ delete fb.demotedModel;
241
+ }
242
+ }
243
+ }
244
+ }
245
+ }
246
+ function demoteModel(model, reason) {
247
+ const tc = loadTransport();
248
+ if (!tc) return;
249
+ let found = false;
250
+ for (const agent of Object.values(tc.agents)) {
251
+ if (agent.model === model) {
252
+ agent.demoted = (/* @__PURE__ */ new Date()).toISOString();
253
+ agent.demotedReason = reason;
254
+ agent.demotedModel = model;
255
+ found = true;
256
+ }
257
+ for (const fb of agent.fallbacks ?? []) {
258
+ if (fb.model === model) {
259
+ fb.demoted = (/* @__PURE__ */ new Date()).toISOString();
260
+ fb.demotedReason = reason;
261
+ fb.demotedModel = model;
262
+ found = true;
263
+ }
264
+ }
265
+ }
266
+ if (found) writeTransportConfig(tc, `auto-demote ${model} (${reason})`);
267
+ }
268
+ function restorePrevious() {
269
+ const dir = configDir();
270
+ const activePath = join(dir, getEnv().transportConfig);
271
+ const oldPath = activePath.replace(".json", ".old.json");
272
+ if (!existsSync(oldPath)) return { ok: false, error: "Nothing to restore \u2014 no previous config saved." };
273
+ try {
274
+ const current = readFileSync(activePath, "utf-8");
275
+ const old = readFileSync(oldPath, "utf-8");
276
+ writeFileSync(activePath, old, "utf-8");
277
+ writeFileSync(oldPath, current, "utf-8");
278
+ cachedTransport = null;
279
+ logInfo(TAG, "transport.json swapped with .old (restore)");
280
+ return { ok: true };
281
+ } catch (err) {
282
+ return { ok: false, error: `Restore failed: ${err instanceof Error ? err.message : String(err)}` };
283
+ }
284
+ }
285
+ function resetToDefaults() {
286
+ const dir = configDir();
287
+ const defaultPath = join(dir, "transport.default.json");
288
+ const activePath = join(dir, getEnv().transportConfig);
289
+ try {
290
+ try {
291
+ writeFileSync(activePath.replace(".json", ".old.json"), readFileSync(activePath, "utf-8"), "utf-8");
292
+ } catch (err) {
293
+ logAndSwallow("transport_config", "op", err);
294
+ }
295
+ const defaults = readFileSync(defaultPath, "utf-8");
296
+ writeFileSync(activePath, defaults, "utf-8");
297
+ cachedTransport = null;
298
+ logInfo(TAG, "transport.json reset to defaults (old saved as .old.json)");
299
+ return true;
300
+ } catch (err) {
301
+ logWarn(TAG, `No transport.default.json \u2014 keeping current config: ${err instanceof Error ? err.message : String(err)}`);
302
+ return false;
303
+ }
304
+ }
305
+ function getAvailableProviders(tc) {
306
+ return Object.entries(tc.providers).map(([name, config]) => ({ name, config }));
307
+ }
308
+ function loadProviderDefaults(providerName, tc) {
309
+ const config = tc ?? loadTransport();
310
+ if (!config) return null;
311
+ const provider = config.providers[providerName];
312
+ if (!provider?.defaults) return null;
313
+ const defaults = provider.defaults;
314
+ if (!defaults["professor"]) return null;
315
+ const profModel = defaults["professor"].model;
316
+ const result = { ...defaults };
317
+ for (const role of ["dreamy", "browsie", "coding"]) {
318
+ if (!result[role]) result[role] = { model: profModel };
319
+ }
320
+ return result;
321
+ }
322
+ function getModelsForProvider(providerName, models) {
323
+ const mc = models ?? loadModels();
324
+ return Object.entries(mc).filter(([, entry]) => entry.transports.includes(providerName)).map(([id, entry]) => ({ id, entry })).sort((a, b) => a.entry.rank - b.entry.rank || a.entry.cost.input - b.entry.cost.input);
325
+ }
326
+ function formatRank(rank) {
327
+ const stars = Math.max(1, Math.min(5, 6 - rank));
328
+ return "\u2605".repeat(stars) + "\u2606".repeat(5 - stars);
329
+ }
330
+ function formatCost(cost) {
331
+ if (cost.input === 0 && cost.output === 0) return "free";
332
+ const inp = `$${cost.input}`;
333
+ const out = cost.output != null ? `$${cost.output}` : "$???";
334
+ return `${inp}/${out}`;
335
+ }
336
+ function validateProviderReady(providerName, provider, env) {
337
+ if (provider.transport === "tmux") return { ok: true };
338
+ if (provider.transport === "api") {
339
+ if (!provider.apiKeyEnv) return { ok: true };
340
+ const key = env.getApiKey(provider.apiKeyEnv);
341
+ if (!key) {
342
+ return {
343
+ ok: false,
344
+ reason: `${providerName} requires API key from env var '${provider.apiKeyEnv}' but it's not set`,
345
+ fix: `Add ${provider.apiKeyEnv}=... to .env and restart`
346
+ };
347
+ }
348
+ return { ok: true };
349
+ }
350
+ if (provider.transport === "acp") {
351
+ const cli = provider.cli;
352
+ if (!cli) {
353
+ return {
354
+ ok: false,
355
+ reason: `ACP provider ${providerName} has no 'cli' field set in transport.json`,
356
+ fix: `Add "cli": "<path-to-cli>" to provider ${providerName} in transport.json`
357
+ };
358
+ }
359
+ try {
360
+ const { execSync } = __require("node:child_process");
361
+ execSync(`${cli} --version`, { timeout: 3e3, stdio: "pipe" });
362
+ return { ok: true };
363
+ } catch (err) {
364
+ const errMsg = err instanceof Error ? err.message.split("\n")[0] : String(err);
365
+ return {
366
+ ok: false,
367
+ reason: `ACP provider ${providerName} CLI '${cli}' is not runnable (${errMsg})`,
368
+ fix: `Install ${cli} or update its path in transport.json`
369
+ };
370
+ }
371
+ }
372
+ return {
373
+ ok: false,
374
+ reason: `Unknown transport type '${provider.transport}' for provider ${providerName}`,
375
+ fix: `Use 'api', 'acp', or 'tmux' for provider.transport`
376
+ };
377
+ }
378
+ function formatValidationError(providerName, result) {
379
+ if (result.ok) return "";
380
+ return `\u274C Cannot switch to ${providerName}: ${result.reason}
381
+ Fix: ${result.fix}`;
382
+ }
383
+
384
+ export {
385
+ configDir,
386
+ loadModels,
387
+ loadTransport,
388
+ resolveHailMary,
389
+ clearTransportCache,
390
+ consumeRepairs,
391
+ validateAndRepair,
392
+ resolveAgent,
393
+ getEnvFallback,
394
+ validateAtStartup,
395
+ writeTransportConfig,
396
+ cleanDemotedModels,
397
+ demoteModel,
398
+ restorePrevious,
399
+ resetToDefaults,
400
+ getAvailableProviders,
401
+ loadProviderDefaults,
402
+ getModelsForProvider,
403
+ formatRank,
404
+ formatCost,
405
+ validateProviderReady,
406
+ formatValidationError
407
+ };
408
+ //# sourceMappingURL=chunk-Y6XAEX2Q.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/components/transport-config.ts"],
4
+ "sourcesContent": ["import { logAndSwallow } from \"./log-and-swallow.js\";\nimport { getEnv } from \"./env-schema.js\";\nimport { validateShape, TRANSPORT_SCHEMA } from \"./config-validator.js\";\n/**\n * transport-config.ts \u2014 Load and validate transport.json + models.json.\n * Falls back to .env defaults if JSON is broken.\n */\n\nimport { readFileSync, writeFileSync, existsSync, statSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { abtarsHome } from \"../paths.js\";\nimport { readEnvWithDefault } from \"./env.js\";\nimport { logInfo, logWarn, logError } from \"./logger.js\";\n\nconst TAG = \"transport-config\";\n\n// \u2500\u2500 Types \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport type ModelCost = { input: number; output: number };\n\nexport type ModelEntry = {\n contextWindow: number;\n maxOutput: number;\n rank: number;\n cost: ModelCost;\n transports: string[];\n description?: string;\n addedAt?: string;\n validatedAt?: string;\n status?: \"alive\" | \"dead\" | \"untested\";\n};\n\nexport type ModelCatalog = Record<string, ModelEntry>;\n\nexport type AgentAssignment = {\n model: string;\n provider: string;\n fallbacks?: Array<{ model: string; provider: string }>;\n};\n\nexport type ProviderConfig = {\n transport: \"acp\" | \"tmux\" | \"api\";\n cli?: string;\n endpoint?: string;\n apiKeyEnv?: string;\n apiFormat?: \"chat\" | \"responses\" | \"anthropic\";\n thinking?: { style: \"effort\"; default: string } | { style: \"extended\"; default: number };\n defaults?: Record<string, { model: string; fallbacks?: string[] }>;\n fallbackChain?: string[];\n};\n\nexport type TransportDefaults = {\n tmux?: { session?: string; captureDelaySec?: number; maxWaitSec?: number };\n acp?: { permissionTimeoutMs?: number };\n};\n\nimport type { HealthPolicyConfig } from \"./transport/model-health-registry.js\";\n\nexport type TransportConfig = {\n agents: Record<string, AgentAssignment>;\n providers: Record<string, ProviderConfig>;\n transportDefaults?: TransportDefaults;\n maxTurns?: number;\n hailMary?: { model: string; provider: string };\n healthPolicy?: HealthPolicyConfig;\n};\n\nexport type ResolvedAgent = {\n model: string;\n provider: ProviderConfig;\n providerName: string;\n contextWindow: number;\n maxOutput: number;\n fallbacks: Array<{ model: string; provider: string }>;\n};\n\n// \u2500\u2500 Loaders \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nlet cachedTransport: TransportConfig | null = null;\n\nexport function configDir(): string {\n return join(abtarsHome(), \"config\");\n}\n\nexport function loadModels(): ModelCatalog {\n const p = join(configDir(), getEnv().modelsConfig);\n try {\n return JSON.parse(readFileSync(p, \"utf-8\")) as ModelCatalog;\n } catch (err) {\n logWarn(TAG, `Failed to load models.json: ${err instanceof Error ? err.message : String(err)}`);\n return {};\n }\n}\n\nexport function loadTransport(): TransportConfig | null {\n if (cachedTransport) return cachedTransport;\n const dir = configDir();\n const p = join(dir, getEnv().transportConfig);\n try {\n cachedTransport = JSON.parse(readFileSync(p, \"utf-8\")) as TransportConfig;\n validateShape(cachedTransport, TRANSPORT_SCHEMA, \"transport.json\");\n logInfo(TAG, `Loaded transport config (${Object.keys(cachedTransport.agents).length} agents, ${Object.keys(cachedTransport.providers).length} providers)`);\n const repairs = validateAndRepair(cachedTransport);\n if (repairs.length > 0) {\n for (const r of repairs) logWarn(TAG, `Auto-repaired: ${r.agent} was on ${r.oldProvider} \u2014 ${r.reason}`);\n writeTransportConfig(cachedTransport, `invariant auto-repair (${repairs.length} agents)`);\n pendingRepairs = repairs;\n }\n return cachedTransport;\n } catch (err) {\n logAndSwallow(TAG, \"loadTransport parse\", err);\n // Fallback to transport.default.json\n try {\n cachedTransport = JSON.parse(readFileSync(join(dir, \"transport.default.json\"), \"utf-8\")) as TransportConfig;\n logWarn(TAG, `transport.json missing/corrupt \u2014 loaded transport.default.json`);\n return cachedTransport;\n } catch (err) {\n logError(TAG, `No transport config available: ${err instanceof Error ? err.message : String(err)}`);\n return null;\n }\n }\n}\n\n/** Resolve hailMary from transport.json. Returns null if not configured. */\nexport function resolveHailMary(transport?: TransportConfig | null): { model: string; endpoint: string; apiKeyEnv?: string } | null {\n const tc = transport ?? loadTransport();\n if (!tc?.hailMary) return null;\n const provider = tc.providers[tc.hailMary.provider];\n if (!provider?.endpoint) return null;\n return { model: tc.hailMary.model, endpoint: provider.endpoint, apiKeyEnv: provider.apiKeyEnv };\n}\n\n/** Force re-read on next call (for tests). */\nexport function clearTransportCache(): void {\n cachedTransport = null;\n}\n\n// \u2500\u2500 Invariant validation \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport type RepairEntry = { agent: string; oldProvider: string; reason: string };\n\n/** Stashed repairs from last loadTransport() \u2014 consumed by model-health task. */\nlet pendingRepairs: RepairEntry[] = [];\nexport function consumeRepairs(): RepairEntry[] {\n const r = pendingRepairs;\n pendingRepairs = [];\n return r;\n}\n\n/**\n * Validate transport invariant: all agents must share professor's transport type.\n * For acp/tmux, provider name must also match (single child process).\n * Violations are auto-repaired (subagent reset to professor's assignment).\n */\nexport function validateAndRepair(tc: TransportConfig): RepairEntry[] {\n const profAssignment = tc.agents[\"professor\"];\n if (!profAssignment) return [];\n const profProvider = tc.providers[profAssignment.provider];\n if (!profProvider) return [];\n\n const profType = profProvider.transport;\n const repairs: RepairEntry[] = [];\n\n for (const [agent, assignment] of Object.entries(tc.agents)) {\n if (agent === \"professor\") continue;\n const provider = tc.providers[assignment.provider];\n if (!provider) continue;\n\n const agentType = provider.transport;\n let violation = false;\n\n if (agentType !== profType) {\n // Cross-transport-type violation\n violation = true;\n } else if (profType !== \"api\" && assignment.provider !== profAssignment.provider) {\n // ACP/tmux: must share exact provider (single child process)\n violation = true;\n }\n\n if (violation) {\n repairs.push({ agent, oldProvider: assignment.provider, reason: `${provider.transport} incompatible with professor (${profType}/${profAssignment.provider})` });\n tc.agents[agent] = { model: profAssignment.model, provider: profAssignment.provider };\n }\n }\n\n // Validate professor fallbacks \u2014 must also match professor's transport type\n const fallbacks = profAssignment.fallbacks;\n if (fallbacks) {\n for (let i = fallbacks.length - 1; i >= 0; i--) {\n const fb = fallbacks[i]!;\n const fbProvider = tc.providers[fb.provider];\n if (!fbProvider) continue;\n const fbType = fbProvider.transport;\n if (fbType !== profType || (profType !== \"api\" && fb.provider !== profAssignment.provider)) {\n repairs.push({ agent: `professor_fb${i + 1}`, oldProvider: fb.provider, reason: `fallback ${fbProvider.transport} incompatible with professor (${profType}/${profAssignment.provider})` });\n fallbacks.splice(i, 1);\n }\n }\n }\n\n // hailMary is exempt \u2014 manual emergency override that rebuilds transport\n\n return repairs;\n}\n\n// \u2500\u2500 Resolution \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport function resolveAgent(role: string, transport?: TransportConfig | null, models?: ModelCatalog): ResolvedAgent | null {\n const tc = transport ?? loadTransport();\n if (!tc) return null;\n\n // cron inherits professor\n const effectiveRole = role === \"task\" ? \"professor\" : role;\n const assignment = tc.agents[effectiveRole];\n if (!assignment) {\n logWarn(TAG, `No agent assignment for role \"${role}\"`);\n return null;\n }\n\n const providers = tc.providers;\n // If primary is demoted AND the demoted model matches current, promote first non-demoted fallback\n let effectiveModel = assignment.model;\n let effectiveProvider = assignment.provider;\n const demotedModel = (assignment as any).demotedModel ?? assignment.model;\n if ((assignment as any).demoted && demotedModel === assignment.model) {\n const firstHealthy = (assignment.fallbacks ?? []).find((fb: any) => !fb.demoted);\n if (firstHealthy) {\n effectiveModel = firstHealthy.model;\n effectiveProvider = firstHealthy.provider;\n } else {\n logWarn(TAG, `All models demoted for role \"${role}\" \u2014 using primary anyway`);\n }\n }\n\n const resolvedProvider = providers[effectiveProvider];\n if (!resolvedProvider) {\n logWarn(TAG, `Provider \"${effectiveProvider}\" not found for role \"${role}\"`);\n return null;\n }\n\n const mc = models ?? loadModels();\n const modelEntry = mc[effectiveModel];\n if (!modelEntry && effectiveModel) {\n logWarn(TAG, `Model \"${effectiveModel}\" not in models.json \u2014 using defaults`);\n }\n\n return {\n model: effectiveModel,\n provider: resolvedProvider,\n providerName: effectiveProvider,\n contextWindow: modelEntry?.contextWindow ?? 128000,\n maxOutput: modelEntry?.maxOutput ?? 8192,\n fallbacks: (assignment.fallbacks ?? []).filter((fb: any) => !fb.demoted && fb.model !== effectiveModel),\n };\n}\n\n// \u2500\u2500 Fallback from .env \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport type EnvFallback = {\n provider: ProviderConfig;\n providerName: string;\n model: string;\n contextWindow: number;\n maxOutput: number;\n};\n\nexport function getEnvFallback(): EnvFallback {\n const providerName = readEnvWithDefault(\"DEFAULT_PROVIDER\", \"openrouter\", \"default LLM provider\");\n const transport = getEnv().defaultTransport as \"api\" | \"acp\" | \"tmux\";\n const model = readEnvWithDefault(\"DEFAULT_MODEL\", \"minimax-m2.5:cloud\", \"default LLM model\");\n\n const provider: ProviderConfig = { transport };\n if (transport === \"api\") {\n provider.endpoint = providerName === \"openrouter\"\n ? \"https://openrouter.ai/api/v1\"\n : \"http://localhost:11434/v1\";\n if (providerName === \"openrouter\") provider.apiKeyEnv = \"OPENROUTER_API_KEY\";\n }\n\n return { provider, providerName, model, contextWindow: 128000, maxOutput: 8192 };\n}\n\n// \u2500\u2500 Validation (startup) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport function validateAtStartup(): void {\n const tc = loadTransport();\n if (!tc) return;\n const mc = loadModels();\n\n for (const [role, assignment] of Object.entries(tc.agents)) {\n if (!tc.providers[assignment.provider]) {\n logWarn(TAG, `Agent \"${role}\": provider \"${assignment.provider}\" not defined in providers`);\n }\n const modelEntry = mc[assignment.model];\n if (!modelEntry) {\n logWarn(TAG, `Agent \"${role}\": model \"${assignment.model}\" not in models.json`);\n } else if (!modelEntry.transports.includes(assignment.provider)) {\n logWarn(TAG, `Agent \"${role}\": model \"${assignment.model}\" not listed for provider \"${assignment.provider}\" in models.json`);\n }\n }\n}\n\n// \u2500\u2500 Write \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport function writeTransportConfig(tc: TransportConfig, reason?: string): void {\n // Guard: reject empty model strings before persisting\n for (const [role, agent] of Object.entries(tc.agents)) {\n if (!agent.model?.trim()) {\n logWarn(TAG, `Refusing to write transport.json \u2014 agent \"${role}\" has empty model`);\n return;\n }\n }\n const p = join(configDir(), getEnv().transportConfig);\n // Save current as .old before overwriting (enables /model restore)\n // Only overwrite .old if it's >15min old \u2014 preserves last-known-good during rapid changes\n const oldPath = p.replace(\".json\", \".old.json\");\n try {\n const oldAge = Date.now() - statSync(oldPath).mtimeMs;\n if (oldAge > 15 * 60_000) writeFileSync(oldPath, readFileSync(p, \"utf-8\"), \"utf-8\");\n } catch { try { writeFileSync(oldPath, readFileSync(p, \"utf-8\"), \"utf-8\"); } catch (err) { logAndSwallow(TAG, \"backup transport.old.json\", err); } }\n writeFileSync(p, JSON.stringify(tc, null, 2), \"utf-8\");\n cachedTransport = tc;\n logInfo(TAG, reason ? `transport.json updated \u2014 ${reason}` : \"transport.json updated\");\n}\n\n/** Remove demoted models from config. Called on user-initiated model switch.\n * Models the user just chose are resurrected (demotion cleared). All other demoted entries are deleted. */\nexport function cleanDemotedModels(tc: TransportConfig, chosenModel?: string): void {\n for (const agent of Object.values(tc.agents)) {\n if ((agent as any).demoted) {\n if (agent.model === chosenModel) { delete (agent as any).demoted; delete (agent as any).demotedReason; delete (agent as any).demotedModel; }\n }\n if (agent.fallbacks) {\n for (const fb of agent.fallbacks) {\n if ((fb as any).demoted && fb.model === chosenModel) { delete (fb as any).demoted; delete (fb as any).demotedReason; delete (fb as any).demotedModel; }\n }\n }\n }\n}\n\n/** Mark a model as demoted in transport.json. Skipped by candidate loading. */\nexport function demoteModel(model: string, reason: \"auth\" | \"timeout\"): void {\n const tc = loadTransport();\n if (!tc) return;\n let found = false;\n for (const agent of Object.values(tc.agents)) {\n if (agent.model === model) { (agent as any).demoted = new Date().toISOString(); (agent as any).demotedReason = reason; (agent as any).demotedModel = model; found = true; }\n for (const fb of agent.fallbacks ?? []) {\n if (fb.model === model) { (fb as any).demoted = new Date().toISOString(); (fb as any).demotedReason = reason; (fb as any).demotedModel = model; found = true; }\n }\n }\n if (found) writeTransportConfig(tc, `auto-demote ${model} (${reason})`);\n}\n\n/** Swap transport.json \u2194 transport.json.old (undo last switch). */\nexport function restorePrevious(): { ok: boolean; error?: string } {\n const dir = configDir();\n const activePath = join(dir, getEnv().transportConfig);\n const oldPath = activePath.replace(\".json\", \".old.json\");\n if (!existsSync(oldPath)) return { ok: false, error: \"Nothing to restore \u2014 no previous config saved.\" };\n try {\n const current = readFileSync(activePath, \"utf-8\");\n const old = readFileSync(oldPath, \"utf-8\");\n writeFileSync(activePath, old, \"utf-8\");\n writeFileSync(oldPath, current, \"utf-8\");\n cachedTransport = null;\n logInfo(TAG, \"transport.json swapped with .old (restore)\");\n return { ok: true };\n } catch (err) {\n return { ok: false, error: `Restore failed: ${err instanceof Error ? err.message : String(err)}` };\n }\n}\n\n/** Copy transport.default.json \u2192 transport.json, clear cache. Returns true if successful. */\nexport function resetToDefaults(): boolean {\n const dir = configDir();\n const defaultPath = join(dir, \"transport.default.json\");\n const activePath = join(dir, getEnv().transportConfig);\n try {\n // Backup current before overwriting\n try { writeFileSync(activePath.replace(\".json\", \".old.json\"), readFileSync(activePath, \"utf-8\"), \"utf-8\"); } catch (err) { logAndSwallow(\"transport_config\", \"op\", err); }\n const defaults = readFileSync(defaultPath, \"utf-8\");\n writeFileSync(activePath, defaults, \"utf-8\");\n cachedTransport = null;\n logInfo(TAG, \"transport.json reset to defaults (old saved as .old.json)\");\n return true;\n } catch (err) {\n logWarn(TAG, `No transport.default.json \u2014 keeping current config: ${err instanceof Error ? err.message : String(err)}`);\n return false;\n }\n}\n\n// \u2500\u2500 Provider availability \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport function getAvailableProviders(tc: TransportConfig): Array<{ name: string; config: ProviderConfig }> {\n return Object.entries(tc.providers).map(([name, config]) => ({ name, config }));\n}\n\n/** Load a provider's defaults block. Missing subagents inherit professor's model. */\nexport function loadProviderDefaults(providerName: string, tc?: TransportConfig | null): Record<string, { model: string; fallbacks?: string[] }> | null {\n const config = tc ?? loadTransport();\n if (!config) return null;\n const provider = config.providers[providerName];\n if (!provider?.defaults) return null;\n const defaults = provider.defaults;\n if (!defaults[\"professor\"]) return null;\n const profModel = defaults[\"professor\"].model;\n const result: Record<string, { model: string; fallbacks?: string[] }> = { ...defaults };\n for (const role of [\"dreamy\", \"browsie\", \"coding\"]) {\n if (!result[role]) result[role] = { model: profModel };\n }\n return result;\n}\n\n// \u2500\u2500 Model helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport function getModelsForProvider(providerName: string, models?: ModelCatalog): Array<{ id: string; entry: ModelEntry }> {\n const mc = models ?? loadModels();\n return Object.entries(mc)\n .filter(([, entry]) => entry.transports.includes(providerName))\n .map(([id, entry]) => ({ id, entry }))\n .sort((a, b) => a.entry.rank - b.entry.rank || a.entry.cost.input - b.entry.cost.input);\n}\n\nexport function formatRank(rank: number): string {\n const stars = Math.max(1, Math.min(5, 6 - rank));\n return \"\u2605\".repeat(stars) + \"\u2606\".repeat(5 - stars);\n}\n\nexport function formatCost(cost: ModelCost): string {\n if (cost.input === 0 && cost.output === 0) return \"free\";\n const inp = `$${cost.input}`;\n const out = cost.output != null ? `$${cost.output}` : \"$???\";\n return `${inp}/${out}`;\n}\n\n// \u2500\u2500 Provider readiness validation (#367) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport type ProviderValidationResult =\n | { ok: true }\n | { ok: false; reason: string; fix: string };\n\n/**\n * Minimal env accessor \u2014 just the slice validateProviderReady needs.\n * Matches the shape exposed by getEnv().\n */\nexport type EnvAccessor = {\n getApiKey(envName: string): string | undefined;\n};\n\n/**\n * Validate that a transport provider's prerequisites are in place BEFORE the\n * bridge attempts to switch to it (#367).\n *\n * Contract:\n * - `api` + `apiKeyEnv` declared \u2192 env var must be non-empty\n * - `api` + no `apiKeyEnv` \u2192 always ok (local ollama-style)\n * - `acp` \u2192 `provider.cli` must be runnable (`<cli> --version` within 3s)\n * - `tmux` \u2192 always ok (out of scope)\n *\n * Pure aside from the ACP `execSync` probe. execSync is imported lazily so\n * unit tests can stub it via dependency injection if needed.\n */\nexport function validateProviderReady(\n providerName: string,\n provider: ProviderConfig,\n env: EnvAccessor,\n): ProviderValidationResult {\n if (provider.transport === \"tmux\") return { ok: true };\n\n if (provider.transport === \"api\") {\n if (!provider.apiKeyEnv) return { ok: true };\n const key = env.getApiKey(provider.apiKeyEnv);\n if (!key) {\n return {\n ok: false,\n reason: `${providerName} requires API key from env var '${provider.apiKeyEnv}' but it's not set`,\n fix: `Add ${provider.apiKeyEnv}=... to .env and restart`,\n };\n }\n return { ok: true };\n }\n\n if (provider.transport === \"acp\") {\n const cli = provider.cli;\n if (!cli) {\n return {\n ok: false,\n reason: `ACP provider ${providerName} has no 'cli' field set in transport.json`,\n fix: `Add \\\"cli\\\": \\\"<path-to-cli>\\\" to provider ${providerName} in transport.json`,\n };\n }\n try {\n // Inline require so mocks work in tests and production stays synchronous.\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const { execSync } = require(\"node:child_process\") as typeof import(\"node:child_process\");\n execSync(`${cli} --version`, { timeout: 3000, stdio: \"pipe\" });\n return { ok: true };\n } catch (err) {\n const errMsg = err instanceof Error ? err.message.split(\"\\n\")[0] : String(err);\n return {\n ok: false,\n reason: `ACP provider ${providerName} CLI '${cli}' is not runnable (${errMsg})`,\n fix: `Install ${cli} or update its path in transport.json`,\n };\n }\n }\n\n // Unknown transport \u2014 fail closed with a clear message.\n return {\n ok: false,\n reason: `Unknown transport type '${(provider as ProviderConfig).transport}' for provider ${providerName}`,\n fix: `Use 'api', 'acp', or 'tmux' for provider.transport`,\n };\n}\n\n/** Format a validation failure for user-visible error messages. */\nexport function formatValidationError(providerName: string, result: ProviderValidationResult): string {\n if (result.ok) return \"\";\n return `\u274C Cannot switch to ${providerName}: ${result.reason}\\n Fix: ${result.fix}`;\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["err"]
7
+ }
@@ -0,0 +1,28 @@
1
+ import { createRequire as __bundleCreateRequire } from 'node:module'; import { fileURLToPath as __bundleFileURLToPath } from 'node:url'; import { dirname as __bundleDirname } from 'node:path'; const require = __bundleCreateRequire(import.meta.url); const __chunk_filename = __bundleFileURLToPath(import.meta.url); const __chunk_dirname = __bundleDirname(__chunk_filename);
2
+ import {
3
+ init_logger,
4
+ logTrace
5
+ } from "./chunk-BUUVFUPO.js";
6
+
7
+ // src/components/env.ts
8
+ init_logger();
9
+ var warned = /* @__PURE__ */ new Set();
10
+ function readEnv(key, impact) {
11
+ const v = process.env[key];
12
+ if (v !== void 0 && v.trim() !== "") return v;
13
+ if (!warned.has(key)) {
14
+ warned.add(key);
15
+ logTrace("env", `${key} not set \u2014 ${impact}`);
16
+ }
17
+ return void 0;
18
+ }
19
+ function readEnvWithDefault(key, fallback, impact) {
20
+ const v = readEnv(key, `${impact} (falling back to "${fallback}")`);
21
+ return v ?? fallback;
22
+ }
23
+
24
+ export {
25
+ readEnv,
26
+ readEnvWithDefault
27
+ };
28
+ //# sourceMappingURL=chunk-YOCTDKKL.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/components/env.ts"],
4
+ "sourcesContent": ["/**\n * Env var helpers.\n *\n * `readEnv(key, impact)` reads process.env[key] and warns once per process\n * per missing key. Replaces raw `process.env.X` reads \u2014 authors get\n * visibility into misconfig without polluting hot paths with per-read\n * warnings.\n *\n * The helper intentionally warns AT the first read site (lazy), not at\n * boot. Reasons:\n * - Boot-time lists go stale; consumer-site reads can't.\n * - New env vars auto-covered when authors reach for this helper.\n * - Warn-once guarantees quiet hot paths (Set<string> dedup).\n *\n * For vars that MUST be set for the bridge to function (e.g. TELEGRAM_BOT_TOKEN),\n * use a boot-time fatal check instead \u2014 missing them should exit(1), not log.\n */\n\nimport { logTrace } from \"./logger.js\";\n\nconst warned = new Set<string>();\n\nexport function readEnv(key: string, impact: string): string | undefined {\n const v = process.env[key];\n if (v !== undefined && v.trim() !== \"\") return v;\n if (!warned.has(key)) {\n warned.add(key);\n logTrace(\"env\", `${key} not set \u2014 ${impact}`);\n }\n return undefined;\n}\n\n/** Same as readEnv but returns a fallback when the var is unset. Still warns once. */\nexport function readEnvWithDefault(key: string, fallback: string, impact: string): string {\n const v = readEnv(key, `${impact} (falling back to \"${fallback}\")`);\n return v ?? fallback;\n}\n\n/** Test-only: clear the warn-once cache. */\nexport function _resetEnvWarnedForTests(): void {\n warned.clear();\n}\n"],
5
+ "mappings": ";;;;;;;AAkBA;AAEA,IAAM,SAAS,oBAAI,IAAY;AAExB,SAAS,QAAQ,KAAa,QAAoC;AACvE,QAAM,IAAI,QAAQ,IAAI,GAAG;AACzB,MAAI,MAAM,UAAa,EAAE,KAAK,MAAM,GAAI,QAAO;AAC/C,MAAI,CAAC,OAAO,IAAI,GAAG,GAAG;AACpB,WAAO,IAAI,GAAG;AACd,aAAS,OAAO,GAAG,GAAG,mBAAc,MAAM,EAAE;AAAA,EAC9C;AACA,SAAO;AACT;AAGO,SAAS,mBAAmB,KAAa,UAAkB,QAAwB;AACxF,QAAM,IAAI,QAAQ,KAAK,GAAG,MAAM,sBAAsB,QAAQ,IAAI;AAClE,SAAO,KAAK;AACd;",
6
+ "names": []
7
+ }