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.
- package/LICENSE +190 -0
- package/README.md +84 -0
- package/bundle/_registry.generated-M4WY2MMI.js +35 -0
- package/bundle/_registry.generated-M4WY2MMI.js.map +7 -0
- package/bundle/abtars-browser.js +162 -0
- package/bundle/abtars-browser.js.map +7 -0
- package/bundle/abtars-cli.js +1438 -0
- package/bundle/abtars-cli.js.map +7 -0
- package/bundle/abtars-restart.js +12 -0
- package/bundle/abtars-restart.js.map +7 -0
- package/bundle/abtars-rss.js +165 -0
- package/bundle/abtars-rss.js.map +7 -0
- package/bundle/abtars-task.js +258 -0
- package/bundle/abtars-task.js.map +7 -0
- package/bundle/abtars.js +4072 -0
- package/bundle/abtars.js.map +7 -0
- package/bundle/agent-api-rate-limit-OQNFMXTZ.js +38 -0
- package/bundle/agent-api-rate-limit-OQNFMXTZ.js.map +7 -0
- package/bundle/agent-registry-LT4JNQH6.js +18 -0
- package/bundle/agent-registry-LT4JNQH6.js.map +7 -0
- package/bundle/agents/default.md +29 -0
- package/bundle/anthropic-adapter-2APTH3LA.js +40 -0
- package/bundle/anthropic-adapter-2APTH3LA.js.map +7 -0
- package/bundle/bridge-lock-transport-4AC2G5G6.js +39 -0
- package/bundle/bridge-lock-transport-4AC2G5G6.js.map +7 -0
- package/bundle/browse-delivery-JXBY36GK.js +17 -0
- package/bundle/browse-delivery-JXBY36GK.js.map +7 -0
- package/bundle/browser-ELNDVPLC.js +18 -0
- package/bundle/browser-ELNDVPLC.js.map +7 -0
- package/bundle/capability-CIL3G4FI.js +17 -0
- package/bundle/capability-CIL3G4FI.js.map +7 -0
- package/bundle/chunk-265TPOPC.js +289 -0
- package/bundle/chunk-265TPOPC.js.map +7 -0
- package/bundle/chunk-2UENBO6M.js +223 -0
- package/bundle/chunk-2UENBO6M.js.map +7 -0
- package/bundle/chunk-2UPU3OW6.js +67 -0
- package/bundle/chunk-2UPU3OW6.js.map +7 -0
- package/bundle/chunk-2XU2X4OI.js +125 -0
- package/bundle/chunk-2XU2X4OI.js.map +7 -0
- package/bundle/chunk-3B7BBE4F.js +758 -0
- package/bundle/chunk-3B7BBE4F.js.map +7 -0
- package/bundle/chunk-3E545J66.js +69 -0
- package/bundle/chunk-3E545J66.js.map +7 -0
- package/bundle/chunk-5R2ANXQ7.js +510 -0
- package/bundle/chunk-5R2ANXQ7.js.map +7 -0
- package/bundle/chunk-6CPN4IGS.js +507 -0
- package/bundle/chunk-6CPN4IGS.js.map +7 -0
- package/bundle/chunk-6NR3OHEW.js +88 -0
- package/bundle/chunk-6NR3OHEW.js.map +7 -0
- package/bundle/chunk-6SETMHNN.js +206 -0
- package/bundle/chunk-6SETMHNN.js.map +7 -0
- package/bundle/chunk-6UCRKRWR.js +644 -0
- package/bundle/chunk-6UCRKRWR.js.map +7 -0
- package/bundle/chunk-AR6GO6YC.js +83 -0
- package/bundle/chunk-AR6GO6YC.js.map +7 -0
- package/bundle/chunk-AZJIODTQ.js +54 -0
- package/bundle/chunk-AZJIODTQ.js.map +7 -0
- package/bundle/chunk-BHMZ4RCC.js +3706 -0
- package/bundle/chunk-BHMZ4RCC.js.map +7 -0
- package/bundle/chunk-BQ2L4GMG.js +9175 -0
- package/bundle/chunk-BQ2L4GMG.js.map +7 -0
- package/bundle/chunk-BSSBCSCL.js +159 -0
- package/bundle/chunk-BSSBCSCL.js.map +7 -0
- package/bundle/chunk-BUUVFUPO.js +157 -0
- package/bundle/chunk-BUUVFUPO.js.map +7 -0
- package/bundle/chunk-CEVRHKJY.js +131 -0
- package/bundle/chunk-CEVRHKJY.js.map +7 -0
- package/bundle/chunk-CWOHNFUV.js +39 -0
- package/bundle/chunk-CWOHNFUV.js.map +7 -0
- package/bundle/chunk-D2DCBO6M.js +228 -0
- package/bundle/chunk-D2DCBO6M.js.map +7 -0
- package/bundle/chunk-FMWKEPM7.js +31 -0
- package/bundle/chunk-FMWKEPM7.js.map +7 -0
- package/bundle/chunk-GRNENTPA.js +145 -0
- package/bundle/chunk-GRNENTPA.js.map +7 -0
- package/bundle/chunk-GST5T3WZ.js +93 -0
- package/bundle/chunk-GST5T3WZ.js.map +7 -0
- package/bundle/chunk-GUQVJC3U.js +299 -0
- package/bundle/chunk-GUQVJC3U.js.map +7 -0
- package/bundle/chunk-HX7Y7EYP.js +3659 -0
- package/bundle/chunk-HX7Y7EYP.js.map +7 -0
- package/bundle/chunk-JCJS4ZIB.js +296 -0
- package/bundle/chunk-JCJS4ZIB.js.map +7 -0
- package/bundle/chunk-JW6RU47G.js +184 -0
- package/bundle/chunk-JW6RU47G.js.map +7 -0
- package/bundle/chunk-LSPKJQCI.js +24 -0
- package/bundle/chunk-LSPKJQCI.js.map +7 -0
- package/bundle/chunk-M6VBAPNT.js +16 -0
- package/bundle/chunk-M6VBAPNT.js.map +7 -0
- package/bundle/chunk-MPX525QO.js +129 -0
- package/bundle/chunk-MPX525QO.js.map +7 -0
- package/bundle/chunk-MW6WDLU7.js +130 -0
- package/bundle/chunk-MW6WDLU7.js.map +7 -0
- package/bundle/chunk-NT3OBORC.js +215 -0
- package/bundle/chunk-NT3OBORC.js.map +7 -0
- package/bundle/chunk-NWDBD4PA.js +50 -0
- package/bundle/chunk-NWDBD4PA.js.map +7 -0
- package/bundle/chunk-OP7BTAWY.js +29 -0
- package/bundle/chunk-OP7BTAWY.js.map +7 -0
- package/bundle/chunk-PLCY3GFH.js +77 -0
- package/bundle/chunk-PLCY3GFH.js.map +7 -0
- package/bundle/chunk-PNEDC45Y.js +97 -0
- package/bundle/chunk-PNEDC45Y.js.map +7 -0
- package/bundle/chunk-QBGBT5QS.js +81 -0
- package/bundle/chunk-QBGBT5QS.js.map +7 -0
- package/bundle/chunk-RVE2N7FA.js +70 -0
- package/bundle/chunk-RVE2N7FA.js.map +7 -0
- package/bundle/chunk-TZHIDLDS.js +71910 -0
- package/bundle/chunk-TZHIDLDS.js.map +7 -0
- package/bundle/chunk-UCQ2WC3B.js +126 -0
- package/bundle/chunk-UCQ2WC3B.js.map +7 -0
- package/bundle/chunk-UHRP745J.js +214 -0
- package/bundle/chunk-UHRP745J.js.map +7 -0
- package/bundle/chunk-V76TVMCM.js +58 -0
- package/bundle/chunk-V76TVMCM.js.map +7 -0
- package/bundle/chunk-VVEDVGCR.js +981 -0
- package/bundle/chunk-VVEDVGCR.js.map +7 -0
- package/bundle/chunk-W6FAL35D.js +102 -0
- package/bundle/chunk-W6FAL35D.js.map +7 -0
- package/bundle/chunk-X6TERNVJ.js +15902 -0
- package/bundle/chunk-X6TERNVJ.js.map +7 -0
- package/bundle/chunk-X76UX47U.js +47 -0
- package/bundle/chunk-X76UX47U.js.map +7 -0
- package/bundle/chunk-XREWVCUO.js +518 -0
- package/bundle/chunk-XREWVCUO.js.map +7 -0
- package/bundle/chunk-Y6XAEX2Q.js +408 -0
- package/bundle/chunk-Y6XAEX2Q.js.map +7 -0
- package/bundle/chunk-YOCTDKKL.js +28 -0
- package/bundle/chunk-YOCTDKKL.js.map +7 -0
- package/bundle/chunk-ZXPXCDA6.js +160 -0
- package/bundle/chunk-ZXPXCDA6.js.map +7 -0
- package/bundle/commands-BHVUOU3V.js +31 -0
- package/bundle/commands-BHVUOU3V.js.map +7 -0
- package/bundle/completion-buffer-P253ONKF.js +13 -0
- package/bundle/completion-buffer-P253ONKF.js.map +7 -0
- package/bundle/config-RGSDAPZN.js +19 -0
- package/bundle/config-RGSDAPZN.js.map +7 -0
- package/bundle/config-show-ERTATR6E.js +40 -0
- package/bundle/config-show-ERTATR6E.js.map +7 -0
- package/bundle/context-HCEGZNDC.js +72 -0
- package/bundle/context-HCEGZNDC.js.map +7 -0
- package/bundle/delegation-tools-GYTS2D6A.js +27 -0
- package/bundle/delegation-tools-GYTS2D6A.js.map +7 -0
- package/bundle/deploy-lib-import-32ZFKHWP.js +49 -0
- package/bundle/deploy-lib-import-32ZFKHWP.js.map +7 -0
- package/bundle/digital-signature-OFCGSHWO.js +13 -0
- package/bundle/digital-signature-OFCGSHWO.js.map +7 -0
- package/bundle/direct-api-transport-YR7SXXNN.js +860 -0
- package/bundle/direct-api-transport-YR7SXXNN.js.map +7 -0
- package/bundle/discord-adapter-YYWVMPPU.js +584 -0
- package/bundle/discord-adapter-YYWVMPPU.js.map +7 -0
- package/bundle/dist-MTMKARCP.js +1969 -0
- package/bundle/dist-MTMKARCP.js.map +7 -0
- package/bundle/dns-wakeup-27M7D2MR.js +107 -0
- package/bundle/dns-wakeup-27M7D2MR.js.map +7 -0
- package/bundle/doctor-QNUSDY73.js +248 -0
- package/bundle/doctor-QNUSDY73.js.map +7 -0
- package/bundle/ensure-invariants-NMXNS476.js +49 -0
- package/bundle/ensure-invariants-NMXNS476.js.map +7 -0
- package/bundle/env-schema-2KBHBDGN.js +19 -0
- package/bundle/env-schema-2KBHBDGN.js.map +7 -0
- package/bundle/esm-DDP6NCZG.js +100663 -0
- package/bundle/esm-DDP6NCZG.js.map +7 -0
- package/bundle/fallback-policy-L4QV2PEJ.js +46 -0
- package/bundle/fallback-policy-L4QV2PEJ.js.map +7 -0
- package/bundle/health-check-SPA7NT6N.js +56 -0
- package/bundle/health-check-SPA7NT6N.js.map +7 -0
- package/bundle/hook-system-6Q5YTR53.js +17 -0
- package/bundle/hook-system-6Q5YTR53.js.map +7 -0
- package/bundle/hotskills-K7BM4YLB.js +12 -0
- package/bundle/hotskills-K7BM4YLB.js.map +7 -0
- package/bundle/install-6HRZVKUM.js +15 -0
- package/bundle/install-6HRZVKUM.js.map +7 -0
- package/bundle/install-log-IAPHYKD4.js +28 -0
- package/bundle/install-log-IAPHYKD4.js.map +7 -0
- package/bundle/install-manifest-SPQRUNXL.js +102 -0
- package/bundle/install-manifest-SPQRUNXL.js.map +7 -0
- package/bundle/install-validate-PVLZXYLQ.js +53 -0
- package/bundle/install-validate-PVLZXYLQ.js.map +7 -0
- package/bundle/irc-adapter-OI5UZSQF.js +293 -0
- package/bundle/irc-adapter-OI5UZSQF.js.map +7 -0
- package/bundle/irc-config-55YO6EGB.js +88 -0
- package/bundle/irc-config-55YO6EGB.js.map +7 -0
- package/bundle/logs-ZNYXX5PA.js +19 -0
- package/bundle/logs-ZNYXX5PA.js.map +7 -0
- package/bundle/media-utils-XNNDTYFI.js +4662 -0
- package/bundle/media-utils-XNNDTYFI.js.map +7 -0
- package/bundle/message-pipeline-LLH5SYMO.js +33 -0
- package/bundle/message-pipeline-LLH5SYMO.js.map +7 -0
- package/bundle/meta.json +41304 -0
- package/bundle/model-health-registry-35LQNVQR.js +11 -0
- package/bundle/model-health-registry-35LQNVQR.js.map +7 -0
- package/bundle/notification-Y5S5MMLV.js +13 -0
- package/bundle/notification-Y5S5MMLV.js.map +7 -0
- package/bundle/openrouter-credits-EDY7ETAU.js +32 -0
- package/bundle/openrouter-credits-EDY7ETAU.js.map +7 -0
- package/bundle/passwd-RRFV4CC5.js +133 -0
- package/bundle/passwd-RRFV4CC5.js.map +7 -0
- package/bundle/paths-G33RZWZ7.js +17 -0
- package/bundle/paths-G33RZWZ7.js.map +7 -0
- package/bundle/peer-client-52XYMNI7.js +156 -0
- package/bundle/peer-client-52XYMNI7.js.map +7 -0
- package/bundle/peer-config-VK6EDLN5.js +16 -0
- package/bundle/peer-config-VK6EDLN5.js.map +7 -0
- package/bundle/peer-sessions-EAXTNQ36.js +49 -0
- package/bundle/peer-sessions-EAXTNQ36.js.map +7 -0
- package/bundle/pending-callback-RIMQZ7FJ.js +40 -0
- package/bundle/pending-callback-RIMQZ7FJ.js.map +7 -0
- package/bundle/phase-transport-KYERDL2O.js +22 -0
- package/bundle/phase-transport-KYERDL2O.js.map +7 -0
- package/bundle/public/css/dashboard.css +542 -0
- package/bundle/public/index.html +180 -0
- package/bundle/public/js/app.js +437 -0
- package/bundle/public/memory-universe.js +384 -0
- package/bundle/responses-adapter-AAQTY3K4.js +30 -0
- package/bundle/responses-adapter-AAQTY3K4.js.map +7 -0
- package/bundle/restore-ZE3SEPSS.js +46 -0
- package/bundle/restore-ZE3SEPSS.js.map +7 -0
- package/bundle/self-healer-utils-DMUUXC47.js +43 -0
- package/bundle/self-healer-utils-DMUUXC47.js.map +7 -0
- package/bundle/skill-stats-LLEXEXLR.js +22 -0
- package/bundle/skill-stats-LLEXEXLR.js.map +7 -0
- package/bundle/sleep-OYIUOVQD.js +19 -0
- package/bundle/sleep-OYIUOVQD.js.map +7 -0
- package/bundle/soul-loader-54WCVNLJ.js +16 -0
- package/bundle/soul-loader-54WCVNLJ.js.map +7 -0
- package/bundle/src-JL4PVO23.js +8 -0
- package/bundle/src-JL4PVO23.js.map +7 -0
- package/bundle/sse-parser-anthropic-P7CE2MH2.js +72 -0
- package/bundle/sse-parser-anthropic-P7CE2MH2.js.map +7 -0
- package/bundle/sse-parser-responses-EQQA5FWN.js +63 -0
- package/bundle/sse-parser-responses-EQQA5FWN.js.map +7 -0
- package/bundle/ssrf-guard-FZCBYIVW.js +64 -0
- package/bundle/ssrf-guard-FZCBYIVW.js.map +7 -0
- package/bundle/start-FH3GRMJ4.js +35 -0
- package/bundle/start-FH3GRMJ4.js.map +7 -0
- package/bundle/stream-single-WSG4D53C.js +33 -0
- package/bundle/stream-single-WSG4D53C.js.map +7 -0
- package/bundle/stt-2UH3RITX.js +14 -0
- package/bundle/stt-2UH3RITX.js.map +7 -0
- package/bundle/subagent-runtime-LE2ZXH3G.js +12 -0
- package/bundle/subagent-runtime-LE2ZXH3G.js.map +7 -0
- package/bundle/system-message-T5R3EYYN.js +30 -0
- package/bundle/system-message-T5R3EYYN.js.map +7 -0
- package/bundle/system-status-KQ6KHFJ6.js +189 -0
- package/bundle/system-status-KQ6KHFJ6.js.map +7 -0
- package/bundle/task-store-K7CQDEPI.js +22 -0
- package/bundle/task-store-K7CQDEPI.js.map +7 -0
- package/bundle/telegram-adapter-2V3XUMT5.js +1060 -0
- package/bundle/telegram-adapter-2V3XUMT5.js.map +7 -0
- package/bundle/tool-registry-MU3OX4UI.js +38 -0
- package/bundle/tool-registry-MU3OX4UI.js.map +7 -0
- package/bundle/tool-sandbox-VYOK4ZOA.js +20 -0
- package/bundle/tool-sandbox-VYOK4ZOA.js.map +7 -0
- package/bundle/transport-config-YLXU33RO.js +57 -0
- package/bundle/transport-config-YLXU33RO.js.map +7 -0
- package/bundle/update-QCW5LXRN.js +13 -0
- package/bundle/update-QCW5LXRN.js.map +7 -0
- package/bundle/update-check-27KZSAP6.js +12 -0
- package/bundle/update-check-27KZSAP6.js.map +7 -0
- package/bundle/usage-tracker-OVVEVMOY.js +17 -0
- package/bundle/usage-tracker-OVVEVMOY.js.map +7 -0
- package/bundle/user-registry-D4SD73UV.js +16 -0
- package/bundle/user-registry-D4SD73UV.js.map +7 -0
- package/core/professor.json +14 -0
- package/core/prompts/browsing_prompt.md +39 -0
- package/core/prompts/compaction.md +32 -0
- package/core/skills/memory/classification/SKILL.md +37 -0
- package/core/skills/memory/memory-anomalies/SKILL.md +39 -0
- package/core/skills/memory/memory-search/SKILL.md +48 -0
- package/core/skills/memory/topic-save/SKILL.md +44 -0
- package/core/skills/ops/cron/SKILL.md +51 -0
- package/core/skills/ops/gdrive-backup/SKILL.md +15 -0
- package/core/skills/ops/session-start/SKILL.md +11 -0
- package/core/skills/ops/skill-authoring/SKILL.md +54 -0
- package/core/skills/ops/system-health/SKILL.md +104 -0
- package/core/skills/ops/troubleshooting/SKILL.md +48 -0
- package/core/skills/ops/trust-gating/SKILL.md +30 -0
- package/core/skills/tools/a2a-communication/SKILL.md +68 -0
- package/core/skills/tools/browse-delegate/SKILL.md +27 -0
- package/core/skills/tools/browser/SKILL.md +36 -0
- package/core/skills/tools/clawhub/SKILL.md +44 -0
- package/core/skills/tools/delegation/SKILL.md +48 -0
- package/core/skills/tools/fxtwitter/SKILL.md +52 -0
- package/core/skills/tools/gmail/SKILL.md +44 -0
- package/core/skills/tools/irc-chat/SKILL.md +84 -0
- package/core/skills/tools/linear/SKILL.md +90 -0
- package/core/skills/tools/mcporter/SKILL.md +46 -0
- package/core/skills/tools/model-scout/SKILL.md +132 -0
- package/core/skills/tools/model-scout/scout-add-model.py +67 -0
- package/core/skills/tools/model-scout/scout-ollama.py +116 -0
- package/core/skills/tools/model-scout/scout-openrouter.py +85 -0
- package/core/skills/tools/nlm/SKILL.md +40 -0
- package/core/skills/tools/todo/SKILL.md +30 -0
- package/core/skills/tools/twitterX/SKILL.md +52 -0
- package/core/skills/tools/twitterX/scripts/abtars-tweet.js +532 -0
- package/core/skills/tools/twitterX/scripts/package.json +1 -0
- package/core/skills/tools/web-fetch/SKILL.md +29 -0
- package/package.json +59 -0
- package/scripts/abtars-daemon.service +23 -0
- package/scripts/abtars-fetch.sh +42 -0
- package/scripts/abtars-watchdog.service +13 -0
- package/scripts/abtars.sh +14 -0
- package/scripts/abtars@.service +21 -0
- package/scripts/browser-patchright.sh +79 -0
- package/scripts/com.abtars.daemon.plist +24 -0
- package/scripts/com.abtars.watchdog.plist +27 -0
- package/scripts/daily-backup.sh +62 -0
- package/scripts/doctor.sh +553 -0
- package/scripts/hooks/audit-logger.sh +22 -0
- package/scripts/upgrade-deps.sh +64 -0
- 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)"
|