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,553 @@
1
+ #!/usr/bin/env bash
2
+ # doctor.sh -- health check and repair for ~/.abtars
3
+ #
4
+ # Usage:
5
+ # doctor.sh # diagnose only -- prints warnings, changes nothing
6
+ # doctor.sh --fix # safe fixes (chmod, mkdir, stale locks, stale sleep locks)
7
+ # doctor.sh --fix-full # all safe fixes + FTS rebuild, WAL checkpoint, git push check
8
+ set -uo pipefail
9
+
10
+ AB="$HOME/.abtars"
11
+ ABMIND="${ABMIND_HOME:-$HOME/.abmind}"
12
+ DB="$ABMIND/memory/memory.db"
13
+ FIX=false
14
+ FIX_FULL=false
15
+ WARNS=0
16
+ FIXES=0
17
+
18
+ case "${1:-}" in
19
+ --fix-full) FIX=true; FIX_FULL=true ;;
20
+ --fix) FIX=true ;;
21
+ esac
22
+
23
+ warn() { echo "[doctor] WARN: $1"; WARNS=$((WARNS + 1)); }
24
+ fix() { echo "[doctor] FIX: $1"; FIXES=$((FIXES + 1)); }
25
+
26
+ # Helper: read JSON field via python3
27
+ json_field() { python3 -c "import json,sys; print(json.load(open(sys.argv[1])).get(sys.argv[2],sys.argv[3]))" "$1" "$2" "${3:-0}" 2>/dev/null || echo "${3:-0}"; }
28
+
29
+ # Read install mode from manifest — MANDATORY
30
+ INSTALL_MODE=$(json_field "$AB/manifest.json" installMode "")
31
+ if [[ -z "$INSTALL_MODE" ]]; then
32
+ echo "[doctor] FATAL: installMode not set in manifest.json. Run 'abtars install' first." >&2
33
+ exit 1
34
+ fi
35
+ if [[ "$INSTALL_MODE" != "simple" && "$INSTALL_MODE" != "supervised" && "$INSTALL_MODE" != "supervised-daemon" ]]; then
36
+ echo "[doctor] FATAL: invalid installMode '$INSTALL_MODE' in manifest.json." >&2
37
+ exit 1
38
+ fi
39
+
40
+ # Helper: cross-platform file mode (replaces stat -c %a which fails on macOS)
41
+ file_mode() { python3 -c "import os; print(oct(os.stat('$1').st_mode & 0o777)[2:])" 2>/dev/null; }
42
+
43
+ # Helper: process age in seconds from ps -o etime= (POSIX, both macOS + Linux)
44
+ ps_age_seconds() {
45
+ ps -o etime= -p "$1" 2>/dev/null | python3 -c "
46
+ import sys
47
+ t = sys.stdin.read().strip()
48
+ if not t: sys.exit(1)
49
+ parts = t.replace('-', ':').split(':')
50
+ mul = [1, 60, 3600, 86400]
51
+ print(sum(int(p) * m for p, m in zip(reversed(parts), mul)))
52
+ " 2>/dev/null
53
+ }
54
+
55
+ # ── Manifest reconciliation (install-time state) ────────────────────────────
56
+ MANIFEST="$AB/current/install-manifest.json"
57
+ if [ -f "$MANIFEST" ] && command -v python3 &>/dev/null; then
58
+ MANIFEST_FIX_FLAG=""
59
+ if $FIX; then MANIFEST_FIX_FLAG="--fix"; fi
60
+ python3 -c "
61
+ import json, os, sys, shutil, stat
62
+
63
+ manifest = json.load(open('$MANIFEST'))
64
+ home = '$AB'
65
+ fix_mode = '--fix' in sys.argv
66
+
67
+ for d in manifest.get('directories', []):
68
+ p = os.path.join(home, d['path'])
69
+ mode = d.get('mode')
70
+ if os.path.isdir(p):
71
+ if mode:
72
+ actual = oct(os.stat(p).st_mode & 0o777)
73
+ expected = oct(int(mode, 8))
74
+ if actual != expected:
75
+ if fix_mode:
76
+ os.chmod(p, int(mode, 8))
77
+ print(f'[manifest] FIX: {d[\"path\"]}/ permissions {actual} -> {expected}')
78
+ else:
79
+ print(f'[manifest] WARN: {d[\"path\"]}/ permissions {actual}, expected {expected}')
80
+ else:
81
+ print(f'[manifest] OK: {d[\"path\"]}/')
82
+ elif fix_mode:
83
+ os.makedirs(p, mode=int(mode, 8) if mode else 0o755, exist_ok=True)
84
+ print(f'[manifest] FIX: created {d[\"path\"]}/')
85
+ else:
86
+ print(f'[manifest] WARN: {d[\"path\"]}/ MISSING')
87
+
88
+ for req in manifest.get('requiredConfigs', []):
89
+ p = os.path.join(home, req['path'])
90
+ if os.path.exists(p):
91
+ print(f'[manifest] OK: {req[\"path\"]}')
92
+ else:
93
+ print(f'[manifest] WARN: {req[\"path\"]} MISSING -- {req[\"remediation\"]}')
94
+ " $MANIFEST_FIX_FLAG 2>/dev/null || echo "[manifest] check skipped (python3 error)"
95
+ else
96
+ echo "[manifest] check skipped (manifest not found or python3 missing)"
97
+ fi
98
+
99
+ # ── Watchdog health (supervised mode only) ───────────────────────────────────
100
+
101
+ WD_ALIVE=false
102
+ WD_PID=""
103
+
104
+ if [[ "$INSTALL_MODE" == "supervised" ]]; then
105
+
106
+ WD_LOCK="$AB/watchdog.lock"
107
+ WD_PID=""
108
+ WD_ALIVE=false
109
+ if [ -f "$WD_LOCK" ]; then
110
+ WD_PID=$(json_field "$WD_LOCK" pid 0)
111
+ WD_LAST=$(json_field "$WD_LOCK" lastCheck 0)
112
+ if [ "$WD_PID" -gt 0 ] 2>/dev/null && kill -0 "$WD_PID" 2>/dev/null; then
113
+ WD_ALIVE=true
114
+ # Check lastCheck freshness (should be < 2 min old)
115
+ if [ "$WD_LAST" -gt 0 ]; then
116
+ WD_AGE=$(( ($(date +%s) - WD_LAST / 1000) ))
117
+ if [ "$WD_AGE" -gt 120 ]; then
118
+ warn "watchdog stale -- last check ${WD_AGE}s ago"
119
+ fi
120
+ fi
121
+ else
122
+ # Watchdog PID dead — check if bridge also dead (circuit breaker?)
123
+ BRIDGE_PID=$(json_field "$AB/bridge.lock" pid 0 2>/dev/null)
124
+ if [ "$BRIDGE_PID" -gt 0 ] 2>/dev/null && ! kill -0 "$BRIDGE_PID" 2>/dev/null; then
125
+ warn "watchdog and bridge both dead -- circuit breaker may have tripped. Check $AB/logs/watchdog.log"
126
+ else
127
+ warn "watchdog not running (PID $WD_PID dead)"
128
+ fi
129
+ if $FIX; then
130
+ if [[ "$(uname)" == "Darwin" ]] && launchctl list 2>/dev/null | grep -q abtars.watchdog; then
131
+ launchctl kickstart -k "gui/$(id -u)/com.abtars.watchdog" 2>/dev/null && fix "restarted watchdog via LaunchAgent"
132
+ elif command -v systemctl &>/dev/null && systemctl --user is-enabled abtars-watchdog.service &>/dev/null; then
133
+ systemctl --user restart abtars-watchdog.service 2>/dev/null && fix "restarted watchdog via systemd"
134
+ else
135
+ warn "watchdog not running -- start manually: ~/.abtars/watchdog.sh --all --web --agent &"
136
+ fi
137
+ fi
138
+ fi
139
+ else
140
+ warn "watchdog.lock missing -- watchdog not running"
141
+ if $FIX; then
142
+ if [[ "$(uname)" == "Darwin" ]] && launchctl list 2>/dev/null | grep -q abtars.watchdog; then
143
+ launchctl kickstart -k "gui/$(id -u)/com.abtars.watchdog" 2>/dev/null && fix "started watchdog via LaunchAgent"
144
+ elif command -v systemctl &>/dev/null && systemctl --user is-enabled abtars-watchdog.service &>/dev/null; then
145
+ systemctl --user start abtars-watchdog.service 2>/dev/null && fix "started watchdog via systemd"
146
+ else
147
+ warn "start manually: ~/.abtars/watchdog.sh --all --web --agent &"
148
+ fi
149
+ fi
150
+ fi
151
+
152
+ # LaunchAgent / systemd check (supervised mode only)
153
+ if [[ "$INSTALL_MODE" == "supervised" || "$INSTALL_MODE" == "supervised-daemon" ]]; then
154
+ if [[ "$(uname)" == "Darwin" ]]; then
155
+ if ! launchctl list 2>/dev/null | grep -q abtars.watchdog; then
156
+ if $FIX || $FIX_FULL; then
157
+ PLIST_SRC="$(dirname "$0")/com.abtars.watchdog.plist"
158
+ PLIST_DST="$HOME/Library/LaunchAgents/com.abtars.watchdog.plist"
159
+ if [ -f "$PLIST_SRC" ]; then
160
+ sed "s|{{HOME}}|$HOME|g" "$PLIST_SRC" > "$PLIST_DST"
161
+ launchctl load "$PLIST_DST" 2>/dev/null && fix "installed and loaded watchdog LaunchAgent"
162
+ else
163
+ warn "watchdog LaunchAgent not loaded -- plist not found at $PLIST_SRC"
164
+ fi
165
+ else
166
+ warn "watchdog LaunchAgent not loaded -- run with --fix-full to install"
167
+ fi
168
+ fi
169
+ elif command -v systemctl &>/dev/null; then
170
+ if ! systemctl --user is-enabled abtars-watchdog.service &>/dev/null 2>&1; then
171
+ if $FIX_FULL; then
172
+ SVC_SRC="$(dirname "$0")/abtars-watchdog.service"
173
+ SVC_DST="$HOME/.config/systemd/user/abtars-watchdog.service"
174
+ if [ -f "$SVC_SRC" ]; then
175
+ mkdir -p "$(dirname "$SVC_DST")"
176
+ cp "$SVC_SRC" "$SVC_DST"
177
+ systemctl --user daemon-reload
178
+ systemctl --user enable --now abtars-watchdog.service 2>/dev/null && fix "installed and enabled watchdog systemd unit"
179
+ else
180
+ warn "watchdog systemd unit not enabled -- service file not found at $SVC_SRC"
181
+ fi
182
+ else
183
+ warn "watchdog systemd unit not enabled -- run with --fix-full to install"
184
+ fi
185
+ fi
186
+ fi
187
+ fi # end supervised-only watchdog block
188
+ fi # end supervised-only supervisor check
189
+
190
+ # 1. Directory permissions (sensitive dirs should be 700)
191
+ for d in "$AB/secret" "$AB/secret/cookies" "$ABMIND/memory"; do
192
+ if [ -d "$d" ] && [ "$(file_mode "$d")" != "700" ]; then
193
+ if $FIX || $FIX_FULL; then
194
+ chmod 700 "$d"; fix "$d permissions → 700"
195
+ else
196
+ warn "$d permissions not 700"
197
+ fi
198
+ fi
199
+ done
200
+
201
+ # 2. Stale lock files (older than 1 hour) — skip bridge.lock if watchdog is alive
202
+ while IFS= read -r f; do
203
+ if $WD_ALIVE && [[ "$f" == *"bridge.lock"* ]]; then
204
+ continue # watchdog owns bridge.lock lifecycle
205
+ fi
206
+ if $FIX; then
207
+ rm -f "$f"; fix "removed stale lock $f"
208
+ else
209
+ warn "stale lock: $f"
210
+ fi
211
+ done < <(find "$AB" -name "*.lock" -not -path "*/sleep/*" -not -path "*/node_modules/*" -not -name "watchdog.lock" -mmin +60 2>/dev/null)
212
+
213
+ # 3. Stale sleep lock (older than 2 hours, no matching audit .md)
214
+ for lockfile in "$ABMIND/memory/sleep"/sleep_*.lock; do
215
+ [ -f "$lockfile" ] || continue
216
+ lockage=$(( ($(date +%s) - $(stat -c %Y "$lockfile" 2>/dev/null || echo 0)) / 60 ))
217
+ if [ "$lockage" -gt 120 ]; then
218
+ base=$(basename "$lockfile" .lock)
219
+ if ! ls "$ABMIND/memory/sleep/${base}"_*.md &>/dev/null; then
220
+ if $FIX; then
221
+ rm -f "$lockfile"; fix "removed stale sleep lock $lockfile ${lockage}min old, no audit"
222
+ else
223
+ warn "stale sleep lock: $lockfile ${lockage}min old, no audit -- sleep may have hung"
224
+ fi
225
+ fi
226
+ fi
227
+ done
228
+
229
+ # 4. Cookie file exists and is valid JSON (only if cookies dir exists — optional feature)
230
+ COOKIE="$AB/secret/cookies/x-cookies.json"
231
+ if [ -f "$COOKIE" ]; then
232
+ if head -c4 "$COOKIE" | grep -q "^ENC:"; then
233
+ : # encrypted at rest — skip JSON validation
234
+ elif ! python3 -c "import json; json.load(open('$COOKIE'))" 2>/dev/null; then
235
+ warn "$COOKIE is not valid JSON -- cookie auth will fail"
236
+ fi
237
+ fi
238
+
239
+ # 5. Required dirs exist
240
+ for d in "$AB/skills" "$AB/logs"; do
241
+ if [ ! -d "$d" ]; then
242
+ if $FIX; then
243
+ mkdir -p "$d"; fix "created missing dir $d"
244
+ else
245
+ warn "missing dir: $d"
246
+ fi
247
+ fi
248
+ done
249
+
250
+ # 7. Recent backup check (skip on fresh installs)
251
+ BACKUP_DIR="$HOME/.backup-abtars"
252
+ if [ -d "$BACKUP_DIR" ]; then
253
+ LATEST=$(find "$BACKUP_DIR" -name "abtars-*.zip" -mtime -2 2>/dev/null | head -1)
254
+ if [ -z "$LATEST" ]; then
255
+ warn "no backup in last 2 days -- check daily-backup.sh cron"
256
+ fi
257
+ fi
258
+
259
+ # 8. Memory DB health (skip if DB doesn't exist yet — fresh install)
260
+ if [ -f "$DB" ]; then
261
+ INTEGRITY=$(sqlite3 "$DB" "PRAGMA integrity_check;" 2>/dev/null | head -1)
262
+ if [ "$INTEGRITY" != "ok" ]; then
263
+ warn "memory.db integrity check failed: $INTEGRITY"
264
+ fi
265
+
266
+ DB_SIZE=$(stat -c %s "$DB" 2>/dev/null || echo 0)
267
+ if [ "$DB_SIZE" -gt 419430400 ]; then
268
+ DB_MB=$((DB_SIZE / 1048576))
269
+ warn "memory.db is ${DB_MB}MB -- approaching 500MB disk budget"
270
+ fi
271
+
272
+ LATEST_SLEEP=$(find "$ABMIND/memory/sleep" -name "sleep_*.md" -mtime -3 2>/dev/null | head -1)
273
+ if [ -z "$LATEST_SLEEP" ]; then
274
+ warn "no sleep audit in last 3 days -- GC/consolidation not running"
275
+ fi
276
+ fi
277
+
278
+ # 9. Embedding health (only if EMBEDDING_ENABLED=true)
279
+ if grep -q "^EMBEDDING_ENABLED=true" "$AB/config/.env" "$AB/.env" 2>/dev/null; then
280
+ if ! command -v ollama &>/dev/null; then
281
+ warn "EMBEDDING_ENABLED but ollama not installed"
282
+ elif ! curl -sf http://localhost:11434/api/tags &>/dev/null; then
283
+ warn "EMBEDDING_ENABLED but ollama not running — start with: systemctl start ollama"
284
+ elif ! ollama list 2>/dev/null | grep -q "nomic-embed-text"; then
285
+ if $FIX; then
286
+ ollama pull nomic-embed-text &>/dev/null && fix "pulled nomic-embed-text model"
287
+ else
288
+ warn "EMBEDDING_ENABLED but nomic-embed-text not pulled"
289
+ fi
290
+ fi
291
+
292
+ if [ -f "$DB" ]; then
293
+ NULL_EMBEDS=$(sqlite3 "$DB" "SELECT COUNT(*) FROM extracted_memories WHERE embedding IS NULL;" 2>/dev/null || echo 0)
294
+ if [ "$NULL_EMBEDS" -gt 0 ]; then
295
+ if $FIX; then
296
+ EMBEDDING_ENABLED=true node "$(dirname "$0")/../dist/cli/abmind.js" embed 2>/dev/null && fix "batch-embedded $NULL_EMBEDS memories"
297
+ else
298
+ warn "$NULL_EMBEDS extracted memories missing embeddings -- run: abmind embed"
299
+ fi
300
+ fi
301
+ fi
302
+ fi
303
+
304
+ # 10. Heartbeat liveness (startup check -- was previous session's heartbeat healthy?)
305
+ LOCK_FILE="$AB/bridge.lock"
306
+ if [ -f "$LOCK_FILE" ]; then
307
+ HB_TS=$(json_field "$LOCK_FILE" lastHeartbeat 0)
308
+ if [ "$HB_TS" -gt 0 ]; then
309
+ HB_AGE=$(( ($(date +%s) - HB_TS / 1000) / 60 ))
310
+ if [ "$HB_AGE" -gt 15 ]; then
311
+ warn "heartbeat was stale before restart last tick ${HB_AGE}min ago -- heartbeat may have stopped"
312
+ fi
313
+ fi
314
+ # sleepStatus daytime check (macOS only)
315
+ if [[ "$(uname)" == "Darwin" ]]; then
316
+ SLEEP_STATUS=$(json_field "$LOCK_FILE" sleepStatus awake)
317
+ HOUR=$(date +%H)
318
+ if [ "$SLEEP_STATUS" = "hw_sleep" ] && [ "$HOUR" -ge 8 ] && [ "$HOUR" -le 23 ]; then
319
+ warn "sleepStatus is hw_sleep but it is daytime (${HOUR}:00) -- Mac should be awake"
320
+ fi
321
+ fi
322
+ fi
323
+
324
+ # 11. Core files size check (should be ≤15 non-empty lines each)
325
+ for f in "$AB/core/user_profile.md" "$AB/core/agent_notes.md" "$AB/core/core_facts.md"; do
326
+ if [ -f "$f" ]; then
327
+ LINES=$(grep -c '[^[:space:]]' "$f")
328
+ if [ "$LINES" -gt 15 ]; then
329
+ FNAME=$(basename "$f"); warn "$FNAME has $LINES non-empty lines limit: 10 -- Dreamy may have overgrown it"
330
+ fi
331
+ fi
332
+ done
333
+
334
+ # 12. Schema version check (removed — schema managed by MemoryManager, no migration table)
335
+
336
+ # 14. Orphaned kiro-cli processes
337
+ KIRO_PROCS=$(pgrep -f 'kiro-cli acp' 2>/dev/null | wc -l)
338
+ if [ "$KIRO_PROCS" -gt 1 ]; then
339
+ if $FIX; then
340
+ # Keep the newest, kill the rest
341
+ PIDS=$(pgrep -f 'kiro-cli acp' 2>/dev/null | sort -n)
342
+ NEWEST=$(echo "$PIDS" | tail -1)
343
+ for pid in $PIDS; do
344
+ if [ "$pid" != "$NEWEST" ]; then
345
+ kill "$pid" 2>/dev/null && fix "killed orphaned kiro-cli acp pid $pid"
346
+ fi
347
+ done
348
+ else
349
+ warn "$KIRO_PROCS kiro-cli acp processes running -- likely orphans from previous bridge"
350
+ fi
351
+ fi
352
+
353
+ # 12b. Orphaned abtars-sleep processes
354
+ SLEEP_PROCS=$(pgrep -f 'abtars-sleep' 2>/dev/null | wc -l)
355
+ if [ "$SLEEP_PROCS" -gt 1 ]; then
356
+ if $FIX; then
357
+ PIDS=$(pgrep -f 'abtars-sleep' 2>/dev/null | sort -n)
358
+ NEWEST=$(echo "$PIDS" | tail -1)
359
+ for pid in $PIDS; do
360
+ if [ "$pid" != "$NEWEST" ]; then
361
+ kill "$pid" 2>/dev/null && fix "killed orphaned abtars-sleep pid $pid"
362
+ fi
363
+ done
364
+ else
365
+ warn "$SLEEP_PROCS abtars-sleep processes running -- likely orphans"
366
+ fi
367
+ fi
368
+
369
+ # 15. Orphaned abtars.sh wrappers (not parented by watchdog)
370
+ if $WD_ALIVE && [ -n "$WD_PID" ]; then
371
+ WRAPPER_ORPHANS=0
372
+ while IFS= read -r pid; do
373
+ PPID_OF=$(ps -o ppid= -p "$pid" 2>/dev/null | tr -d ' ')
374
+ if [ "$PPID_OF" != "$WD_PID" ]; then
375
+ WRAPPER_ORPHANS=$((WRAPPER_ORPHANS + 1))
376
+ if $FIX; then
377
+ kill "$pid" 2>/dev/null && fix "killed orphaned abtars.sh wrapper pid $pid (parent $PPID_OF, not watchdog $WD_PID)"
378
+ fi
379
+ fi
380
+ done < <(pgrep -f 'abtars.sh.*--all' 2>/dev/null)
381
+ if [ "$WRAPPER_ORPHANS" -gt 0 ] && ! $FIX; then
382
+ warn "$WRAPPER_ORPHANS orphaned abtars.sh wrapper(s) not parented by watchdog"
383
+ fi
384
+ fi
385
+
386
+ # 13. Full fixes (--fix-full only)
387
+ if $FIX_FULL && [ -f "$DB" ]; then
388
+ sqlite3 "$DB" 'INSERT INTO messages_fts(messages_fts) VALUES('"'"'rebuild'"'"');' 2>/dev/null && fix "rebuilt messages_fts index"
389
+ sqlite3 "$DB" 'INSERT INTO extracted_memories_fts(extracted_memories_fts) VALUES('"'"'rebuild'"'"');' 2>/dev/null && fix "rebuilt extracted_memories_fts index"
390
+ sqlite3 "$DB" 'PRAGMA wal_checkpoint(TRUNCATE);' 2>/dev/null && fix "WAL checkpoint truncate"
391
+ fi
392
+
393
+ if $FIX_FULL; then
394
+ cd "$AB"
395
+ if [ -d .git ]; then
396
+ if ! git remote get-url origin &>/dev/null; then
397
+ warn "git remote 'origin' missing -- backup push will fail"
398
+ elif ! timeout 5 git push --dry-run &>/dev/null; then
399
+ warn "git push would fail -- check upstream/auth"
400
+ fi
401
+ fi
402
+ fi
403
+
404
+ # 16. Hooks health
405
+ HOOKS_CONFIG="$AB/config/hooks.json"
406
+
407
+ # 16a. hooks.json validity
408
+ if [ -f "$HOOKS_CONFIG" ]; then
409
+ if ! python3 -c "import json; json.load(open('$HOOKS_CONFIG'))" 2>/dev/null; then
410
+ warn "hooks.json is not valid JSON — hooks silently disabled"
411
+ fi
412
+ fi
413
+
414
+ # 16b. Hooks dir permissions
415
+ if [ -d "$AB/hooks" ]; then
416
+ HMODE=$(file_mode "$AB/hooks")
417
+ if [ -n "$HMODE" ] && [ "$HMODE" != "700" ]; then
418
+ if $FIX; then chmod 700 "$AB/hooks" && fix "hooks dir → 700"
419
+ else warn "hooks dir mode $HMODE, expected 700 — hooks disabled"; fi
420
+ fi
421
+ fi
422
+
423
+ # 16c. Referenced scripts exist + executable
424
+ if [ -f "$HOOKS_CONFIG" ]; then
425
+ while IFS= read -r cmd; do
426
+ [ -z "$cmd" ] && continue
427
+ resolved="${cmd/#\~\/.abtars/$AB}"
428
+ resolved="${resolved/#\~/$HOME}"
429
+ if [ ! -f "$resolved" ]; then
430
+ warn "hooks.json references missing script: $resolved"
431
+ elif [ ! -x "$resolved" ]; then
432
+ if $FIX; then chmod +x "$resolved" && fix "chmod +x $resolved"
433
+ else warn "hook script not executable: $resolved"; fi
434
+ fi
435
+ done < <(python3 -c "
436
+ import json
437
+ try:
438
+ c = json.load(open('$HOOKS_CONFIG'))
439
+ for hooks in c.get('hooks', {}).values():
440
+ for h in hooks or []:
441
+ print(h.get('command', ''))
442
+ except Exception: pass
443
+ " 2>/dev/null)
444
+ fi
445
+
446
+ # 16d. Stuck hook processes (>60s)
447
+ if [ -d "$AB/hooks" ]; then
448
+ while IFS= read -r pid; do
449
+ AGE=$(ps_age_seconds "$pid")
450
+ [ -z "$AGE" ] && continue
451
+ if [ "$AGE" -gt 60 ]; then
452
+ if $FIX; then
453
+ kill -TERM "$pid" 2>/dev/null
454
+ sleep 2
455
+ kill -0 "$pid" 2>/dev/null && kill -KILL "$pid" 2>/dev/null
456
+ fix "killed stuck hook $pid (${AGE}s)"
457
+ else
458
+ warn "stuck hook $pid (${AGE}s) — run with --fix"
459
+ fi
460
+ fi
461
+ done < <(pgrep -f "$AB/hooks/" 2>/dev/null)
462
+ fi
463
+
464
+ # 16e. Hook log file size
465
+ if [ -d "$AB/logs" ]; then
466
+ while IFS= read -r f; do
467
+ warn "hook log large: $f ($(du -h "$f" | cut -f1)) — consider rotation"
468
+ done < <(find "$AB/logs" -name '*.jsonl' -size +100M 2>/dev/null)
469
+ fi
470
+
471
+ # 17. Filesystem permissions (#441)
472
+ check_perm() {
473
+ local path="$1" expected="$2" label="$3"
474
+ if [ ! -e "$path" ]; then return; fi
475
+ actual=$(stat -c "%a" "$path" 2>/dev/null || stat -f "%Lp" "$path" 2>/dev/null)
476
+ if [ "$actual" != "$expected" ]; then
477
+ warn "$label is $actual — should be $expected"
478
+ if $FIX || $FIX_FULL; then
479
+ chmod "$expected" "$path" && fix "$label → $expected"
480
+ fi
481
+ fi
482
+ }
483
+
484
+ check_perm "$AB" "700" "~/.abtars/"
485
+ check_perm "$AB/config" "700" "config/"
486
+ check_perm "$AB/secret" "700" "secret/"
487
+
488
+ # Check all files in config/ and secret/ are 600
489
+ for dir in "$AB/config" "$AB/secret"; do
490
+ [ -d "$dir" ] || continue
491
+ for f in "$dir"/*; do
492
+ [ -f "$f" ] || continue
493
+ actual=$(stat -c "%a" "$f" 2>/dev/null || stat -f "%Lp" "$f" 2>/dev/null)
494
+ if [ "$actual" != "600" ]; then
495
+ warn "$(basename "$f") in $(basename "$dir")/ is $actual — should be 600"
496
+ if $FIX || $FIX_FULL; then
497
+ chmod 600 "$f" && fix "$(basename "$f") → 600"
498
+ fi
499
+ fi
500
+ done
501
+ done
502
+
503
+ # ── Retention policy (#297) ──────────────────────────────────────────────────
504
+ LOGS_KEEP_DAYS=7
505
+ DATA_KEEP_DAYS=30
506
+ AUDIT_MAX_BYTES=5242880 # 5MB
507
+
508
+ stale_logs=$(find "$AB/logs" -type f -name "*.log" -mtime +"$LOGS_KEEP_DAYS" 2>/dev/null | wc -l)
509
+ stale_overflow=$(find "$AB/overflow" -type f -mtime +"$DATA_KEEP_DAYS" 2>/dev/null | wc -l)
510
+ stale_reports=$(find "$AB/reports" -type f -mtime +"$DATA_KEEP_DAYS" 2>/dev/null | wc -l)
511
+ stale_media=$(find "$AB/received/media" -type f -mtime +"$LOGS_KEEP_DAYS" 2>/dev/null | wc -l)
512
+
513
+ audit_size=0
514
+ if [ -f "$AB/logs/audit.jsonl" ]; then
515
+ audit_size=$(stat -c%s "$AB/logs/audit.jsonl" 2>/dev/null || stat -f%z "$AB/logs/audit.jsonl" 2>/dev/null || echo 0)
516
+ fi
517
+
518
+ total_stale=$((stale_logs + stale_overflow + stale_reports + stale_media))
519
+ if [ "$total_stale" -gt 0 ] || [ "$audit_size" -gt "$AUDIT_MAX_BYTES" ]; then
520
+ if $FIX || $FIX_FULL; then
521
+ [ "$stale_logs" -gt 0 ] && find "$AB/logs" -type f -name "*.log" -mtime +"$LOGS_KEEP_DAYS" -delete && fix "deleted $stale_logs log file(s) older than ${LOGS_KEEP_DAYS}d"
522
+ [ "$stale_overflow" -gt 0 ] && find "$AB/overflow" -type f -mtime +"$DATA_KEEP_DAYS" -delete && fix "deleted $stale_overflow overflow file(s) older than ${DATA_KEEP_DAYS}d"
523
+ [ "$stale_reports" -gt 0 ] && find "$AB/reports" -type f -mtime +"$DATA_KEEP_DAYS" -delete && fix "deleted $stale_reports report file(s) older than ${DATA_KEEP_DAYS}d"
524
+ [ "$stale_media" -gt 0 ] && find "$AB/received/media" -type f -mtime +"$LOGS_KEEP_DAYS" -delete && fix "deleted $stale_media media file(s) older than ${LOGS_KEEP_DAYS}d"
525
+ if [ "$audit_size" -gt "$AUDIT_MAX_BYTES" ]; then
526
+ tail -5000 "$AB/logs/audit.jsonl" > "$AB/logs/audit.jsonl.tmp" && mv "$AB/logs/audit.jsonl.tmp" "$AB/logs/audit.jsonl"
527
+ fix "audit.jsonl truncated (was $audit_size bytes)"
528
+ fi
529
+ else
530
+ [ "$total_stale" -gt 0 ] && warn "$total_stale stale file(s) reclaimable (logs>${LOGS_KEEP_DAYS}d, overflow/reports>${DATA_KEEP_DAYS}d, media>${LOGS_KEEP_DAYS}d)"
531
+ [ "$audit_size" -gt "$AUDIT_MAX_BYTES" ] && warn "audit.jsonl is $audit_size bytes (>${AUDIT_MAX_BYTES})"
532
+ fi
533
+ fi
534
+
535
+ # Summary
536
+ if $FIX_FULL && [ -f "$AB/logs/watchdog.log" ]; then
537
+ echo ""
538
+ echo "[doctor] Last 10 lines of watchdog.log:"
539
+ tail -10 "$AB/logs/watchdog.log" | sed 's/^/ /'
540
+ fi
541
+
542
+ if $FIX || $FIX_FULL; then
543
+ echo "[doctor] Done. $FIXES fixes applied, $WARNS warnings."
544
+ else
545
+ if [ "$WARNS" -eq 0 ]; then
546
+ echo "[doctor] All clear."
547
+ else
548
+ echo "[doctor] $WARNS warnings. Run with --fix or --fix-full to repair."
549
+ fi
550
+ fi
551
+
552
+ if $FIX; then exit 0; fi
553
+ exit $(( WARNS > 0 ? 1 : 0 ))
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env bash
2
+ # audit-logger.sh — Canonical hook example for abtars.
3
+ # Appends each hook event as a JSON line to ~/.abtars/logs/audit.jsonl.
4
+ #
5
+ # Install:
6
+ # mkdir -p ~/.abtars/hooks && chmod 700 ~/.abtars/hooks
7
+ # cp scripts/hooks/audit-logger.sh ~/.abtars/hooks/
8
+ # chmod +x ~/.abtars/hooks/audit-logger.sh
9
+ #
10
+ # Then add to ~/.abtars/config/hooks.json:
11
+ # { "enabled": true, "hooks": {
12
+ # "BeforeMessage": [{ "name": "audit-in", "command": "~/.abtars/hooks/audit-logger.sh" }],
13
+ # "AfterMessage": [{ "name": "audit-out", "command": "~/.abtars/hooks/audit-logger.sh" }],
14
+ # "AfterPrompt": [{ "name": "audit-prompt", "command": "~/.abtars/hooks/audit-logger.sh" }]
15
+ # }}
16
+
17
+ LOG="${ABTARS_HOME:-$HOME/.abtars}/logs/audit.jsonl"
18
+ mkdir -p "$(dirname "$LOG")"
19
+
20
+ # Read JSON from stdin, append to log
21
+ cat >> "$LOG"
22
+ echo >> "$LOG"
@@ -0,0 +1,64 @@
1
+ #!/usr/bin/env bash
2
+ # upgrade-deps.sh — Upgrade all external dependencies (abtars + abmind).
3
+ # Portable: macOS and Linux. Runs weekly via cron; safe to run manually.
4
+ # Does not restart the bridge. Deploy with `abtars update` to activate.
5
+
6
+ # Intentionally no `set -e`: one failing step must not skip later steps.
7
+ set -uo pipefail
8
+
9
+ echo "🔄 Dependency upgrade ($(uname -s))"
10
+ echo ""
11
+
12
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
13
+ BRIDGE_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
14
+
15
+ # Locate an abmind checkout (sibling of bridge source, or common HOME layouts).
16
+ ABMIND_ROOT=""
17
+ for candidate in "$BRIDGE_ROOT/../abmind" "$HOME/abmind" "$HOME/workspace/ab/abmind"; do
18
+ if [[ -f "$candidate/package.json" ]]; then
19
+ ABMIND_ROOT="$(cd "$candidate" && pwd)"
20
+ break
21
+ fi
22
+ done
23
+
24
+ npm_update() {
25
+ local label="$1" dir="$2"
26
+ echo "📦 $label npm ($dir)..."
27
+ ( cd "$dir" && npm update --save 2>&1 | tail -3 ) || echo " ⚠️ $label npm update failed"
28
+ ( cd "$dir" && npm audit fix --force 2>&1 | tail -2 ) || echo " ⚠️ $label npm audit fix failed"
29
+ echo ""
30
+ }
31
+
32
+ npm_update "abtars" "$BRIDGE_ROOT"
33
+ if [[ -n "$ABMIND_ROOT" ]]; then
34
+ npm_update "abmind" "$ABMIND_ROOT"
35
+ else
36
+ echo "📦 abmind npm: checkout not found — skipped"
37
+ echo ""
38
+ fi
39
+
40
+ # pipx tools — use `pipx upgrade` (no grep -P, portable).
41
+ if command -v pipx >/dev/null 2>&1; then
42
+ echo "🐍 pipx tools..."
43
+ for name in notebooklm-mcp-cli; do
44
+ echo -n " $name: "
45
+ if pipx list --short 2>/dev/null | grep -q "^$name "; then
46
+ pipx upgrade "$name" 2>&1 | tail -1
47
+ else
48
+ echo "not installed (skip)"
49
+ fi
50
+ done
51
+ echo ""
52
+ fi
53
+
54
+ # Homebrew — macOS only.
55
+ if command -v brew >/dev/null 2>&1; then
56
+ echo "🍺 Homebrew..."
57
+ brew update 2>&1 | tail -2
58
+ brew upgrade 2>&1 | tail -5
59
+ echo ""
60
+ fi
61
+
62
+ echo "🤖 kiro-cli: $(kiro-cli --version 2>/dev/null || echo 'not found') — update via kiro.dev"
63
+ echo ""
64
+ echo "✅ Done. Activate with: (cd abmind && abmind update) && (cd abtars && abtars update)"