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,52 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: twitter
|
|
3
|
+
description: Fetch tweets and monitor AI influencers via FXTwitter API + web search. No API keys needed for single tweets.
|
|
4
|
+
user-invocable: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Twitter / X
|
|
8
|
+
|
|
9
|
+
Fetch Twitter/X content without API keys using the free FXTwitter API. No timeline/search endpoint — use web search for discovery, FXTwitter for structured data.
|
|
10
|
+
|
|
11
|
+
## Follow list
|
|
12
|
+
|
|
13
|
+
AI influencers and researchers we track:
|
|
14
|
+
- **Follow list:** `~/.abtars/workspace/twitterX/follows.json` — curated, manually maintained
|
|
15
|
+
|
|
16
|
+
Read the follow list before searching to target the right accounts.
|
|
17
|
+
|
|
18
|
+
## Pipeline
|
|
19
|
+
|
|
20
|
+
1. Read follow list: `cat ~/.abtars/workspace/twitterX/base.follows.json`
|
|
21
|
+
2. Search for recent tweets: `web_search("site:x.com from:handle 2026")`
|
|
22
|
+
3. Extract tweet ID from URL: `https://x.com/{user}/status/{id}`
|
|
23
|
+
4. Fetch structured data: `curl -s "https://api.fxtwitter.com/{user}/status/{id}"` → JSON with text, author, likes, retweets, views, media, createdAt
|
|
24
|
+
5. Compile results
|
|
25
|
+
|
|
26
|
+
## Endpoints
|
|
27
|
+
|
|
28
|
+
- **Tweet:** `GET https://api.fxtwitter.com/{screen_name}/status/{tweet_id}`
|
|
29
|
+
- **Tweet + translation:** `GET https://api.fxtwitter.com/{screen_name}/status/{tweet_id}/{lang_code}`
|
|
30
|
+
- **User profile:** `GET https://api.fxtwitter.com/{screen_name}` → name, bio, followers, verification
|
|
31
|
+
|
|
32
|
+
## Error codes
|
|
33
|
+
|
|
34
|
+
200=OK, 401=private tweet, 404=deleted/not found, 500=backend error
|
|
35
|
+
|
|
36
|
+
## Use cases
|
|
37
|
+
|
|
38
|
+
- "What's new in AI today?" → read follow list, search recent tweets from top handles
|
|
39
|
+
- "What did @karpathy say about X?" → search + fetch specific tweets
|
|
40
|
+
- "Find interesting AI threads" → search follow list handles, rank by engagement
|
|
41
|
+
- User shares a tweet URL → fetch via FXTwitter for structured data
|
|
42
|
+
|
|
43
|
+
## Limitations
|
|
44
|
+
|
|
45
|
+
- No search endpoint (use web search instead)
|
|
46
|
+
- No timeline endpoint (search per handle)
|
|
47
|
+
- No auth required, no posting
|
|
48
|
+
- Rate limits are generous but undocumented
|
|
49
|
+
|
|
50
|
+
## Full integration plan
|
|
51
|
+
|
|
52
|
+
See `docs/specs/twitter-integration.plan.md` for the `abtars-tweet` CLI roadmap (rettiwt-api, daily newsletter, discovery).
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: gmail
|
|
3
|
+
description: Read and manage Gmail via gws-cli
|
|
4
|
+
user-invocable: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Gmail Access
|
|
8
|
+
|
|
9
|
+
Read, search, and manage emails using `gws-cli gmail`. Auth is pre-configured.
|
|
10
|
+
|
|
11
|
+
## Commands
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
# List unread emails
|
|
15
|
+
gws-cli gmail list -q "is:unread" -n 20
|
|
16
|
+
|
|
17
|
+
# Read a message by ID
|
|
18
|
+
gws-cli gmail read MSG_ID
|
|
19
|
+
|
|
20
|
+
# Search
|
|
21
|
+
gws-cli gmail search -q "from:someone subject:invoice"
|
|
22
|
+
|
|
23
|
+
# Mark as read
|
|
24
|
+
gws-cli gmail mark-read MSG_ID
|
|
25
|
+
|
|
26
|
+
# Mark as unread
|
|
27
|
+
gws-cli gmail mark-unread MSG_ID
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Search query syntax
|
|
31
|
+
|
|
32
|
+
Standard Gmail operators: `from:`, `to:`, `subject:`, `is:unread`, `newer_than:1d`, `has:attachment`, `label:`, `after:2026/03/01`.
|
|
33
|
+
|
|
34
|
+
## When checking emails
|
|
35
|
+
|
|
36
|
+
1. `gws-cli gmail list -q "is:unread" -n 20`
|
|
37
|
+
2. For each unread email: `gws-cli gmail read <id>`, then send a SHORT summary to the user as a separate message (one message per email)
|
|
38
|
+
3. Summary format: **From:** / **Subject:** / 1-2 sentence gist
|
|
39
|
+
4. Do NOT create files, reports, or .md documents — deliver summaries directly in chat
|
|
40
|
+
5. After summarizing, mark as read: `gws-cli gmail mark-read <id>`
|
|
41
|
+
|
|
42
|
+
## Rules
|
|
43
|
+
- Never delete or trash emails without explicit user request
|
|
44
|
+
- If no unread emails, do not send any message — stay silent
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: irc-chat
|
|
3
|
+
description: Participate in IRC channels when the bridge has irc.json configured. Post on user request, respond to @mentions and the user, coordinate with other bots on shared channels.
|
|
4
|
+
requires: abtars
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# IRC Chat Participation
|
|
8
|
+
|
|
9
|
+
You can participate in IRC channels. The bridge's IRC connections are defined in `~/.abtars/config/irc.json`. Each server entry lists channels you're configured for, with nick and mode per channel.
|
|
10
|
+
|
|
11
|
+
## How to find your IRC context
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
cat ~/.abtars/config/irc.json
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
The file has `servers[]`, each with `id`, `host`, `nick`, and `channels{}`. For each channel:
|
|
18
|
+
- `mode`: `"plain"` or `"secure"` (secure = Ed25519-signed, plain = unauthenticated)
|
|
19
|
+
- `requireMention`: `true` = only respond when your nick is mentioned
|
|
20
|
+
- `allowFrom`: nicks whose messages you will react to (others are ignored even if addressed to you)
|
|
21
|
+
- `trustedKeys`: (secure mode only) map of nick → Ed25519 public key
|
|
22
|
+
|
|
23
|
+
If no `irc.json` or no servers, IRC is inactive for this bridge.
|
|
24
|
+
|
|
25
|
+
## When to post
|
|
26
|
+
|
|
27
|
+
**Post only when the user explicitly asks:**
|
|
28
|
+
- "post to IRC that the deploy is done"
|
|
29
|
+
- "tell the #bridges channel I'm restarting"
|
|
30
|
+
- "announce on IRC: ..."
|
|
31
|
+
|
|
32
|
+
The platform adapter handles tool invocation — you don't call raw IRC commands. Use the same outbound message channel you'd use for any reply; the adapter routes to IRC when the user's prompt came from IRC.
|
|
33
|
+
|
|
34
|
+
**Don't post proactively** unless the user configured you to (e.g. a cron'd status broadcast). Spontaneous IRC posts from an LLM are noise.
|
|
35
|
+
|
|
36
|
+
## When to respond
|
|
37
|
+
|
|
38
|
+
- An inbound IRC message mentioned your nick (adapter filtered it already — if it reached you, it's addressed to you)
|
|
39
|
+
- The sender is in `allowFrom` for that channel (adapter filtered)
|
|
40
|
+
- Keep replies short — one or two lines. IRC is a ticker, not a chat window
|
|
41
|
+
|
|
42
|
+
If you're uncertain whether to respond, don't. A missed reply is recoverable; a noisy loop between bots is not.
|
|
43
|
+
|
|
44
|
+
## Channel awareness
|
|
45
|
+
|
|
46
|
+
You may be on multiple channels. The inbound message carries its channel — respond on the same channel unless the user says otherwise. Don't cross-post.
|
|
47
|
+
|
|
48
|
+
If the user says "join channel #X", you can't actually join from the LLM side — joining is a config change (`irc.json`). Tell the user: "To join #X, add it under the appropriate server in `~/.abtars/config/irc.json` and restart. I can describe the config shape if you want."
|
|
49
|
+
|
|
50
|
+
## Message format
|
|
51
|
+
|
|
52
|
+
- Plain text. No markdown (IRC doesn't render it).
|
|
53
|
+
- One line per message. The adapter splits long outputs automatically, but keep replies short on purpose — IRC lines above ~340 chars on secure channels get truncated.
|
|
54
|
+
- No emojis unless you know the channel supports UTF-8 (most modern servers do, but older clients break).
|
|
55
|
+
|
|
56
|
+
## Signatures (secure channels)
|
|
57
|
+
|
|
58
|
+
Secure channels require Ed25519 signatures. **You do not sign anything manually** — the adapter layer signs every outbound message on secure channels automatically using the bot's private key from `.env`.
|
|
59
|
+
|
|
60
|
+
- Secure channels are bot-to-bot (humans can't sign in their IRC client).
|
|
61
|
+
- If you receive a message on a secure channel that fails verification, the adapter drops it before you see it — you'll never be tricked into replying to a spoofed sender.
|
|
62
|
+
- Don't try to embed signature-like tags in your replies (`[sig:...]`). The adapter does that.
|
|
63
|
+
|
|
64
|
+
## What NOT to do
|
|
65
|
+
|
|
66
|
+
- Don't relay private conversations (user-to-you) to IRC without explicit instruction
|
|
67
|
+
- Don't quote secrets, tokens, file paths, or PII — IRC messages may be logged or world-readable depending on server
|
|
68
|
+
- Don't chat with another bot unprompted. A brief acknowledgement to coordinate is fine; small talk is not
|
|
69
|
+
- Don't respond with long code blocks — commit to git and reference the SHA
|
|
70
|
+
- Don't ignore `allowFrom` or `requireMention` — the adapter respects these, and if a message reached you, trust that it should be answered
|
|
71
|
+
|
|
72
|
+
## Interaction with peer_ask (a2a-communication)
|
|
73
|
+
|
|
74
|
+
These are separate tools for different purposes:
|
|
75
|
+
|
|
76
|
+
| | peer_ask (a2a-communication) | IRC |
|
|
77
|
+
|---|---|---|
|
|
78
|
+
| Model | Sync RPC | Async broadcast |
|
|
79
|
+
| Audience | One peer | Whole channel |
|
|
80
|
+
| Visibility | Log-only | Human-watchable (via IRC client) |
|
|
81
|
+
| Use when | You need an answer from a specific peer | User asks you to announce / converse on a channel |
|
|
82
|
+
|
|
83
|
+
If the user says "ask <peer> what time it is" → use `peer_ask`.
|
|
84
|
+
If the user says "tell IRC you're restarting" → reply through the adapter, it goes to IRC.
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: linear
|
|
3
|
+
description: Manage Linear projects, issues, and tasks via the bundled Node CLI and the official Linear API. Use when you need to read, create, update, or organize Linear issues, projects, teams, milestones, comments, cycles, labels, and documents.
|
|
4
|
+
metadata: {"gracebot":{"always":false,"emoji":"📐","homepage":"https://github.com/MaTriXy/linear-skill","requires":{"bins":["node","npm"],"env":["LINEAR_API_KEY"]},"primaryEnv":"LINEAR_API_KEY","install":[{"id":"node-brew","kind":"brew","formula":"node","bins":["node","npm"],"label":"Install Node.js (brew)"}]},"clawdbot":{"always":false,"emoji":"📐","homepage":"https://github.com/MaTriXy/linear-skill","requires":{"bins":["node","npm"],"env":["LINEAR_API_KEY"]},"primaryEnv":"LINEAR_API_KEY","install":[{"id":"node-brew","kind":"brew","formula":"node","bins":["node","npm"],"label":"Install Node.js (brew)"}]}}
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Linear Workflow Management
|
|
8
|
+
|
|
9
|
+
Manage Linear issues and projects through the bundled CLI at `{baseDir}/scripts/linear-cli.js`.
|
|
10
|
+
|
|
11
|
+
## Scope and Runtime Model
|
|
12
|
+
|
|
13
|
+
- This skill runs `node {baseDir}/scripts/linear-cli.js ...`.
|
|
14
|
+
- The CLI uses the official `@linear/sdk`.
|
|
15
|
+
- Authentication is `LINEAR_API_KEY` from the local environment.
|
|
16
|
+
- Expected API destination is Linear GraphQL (`https://api.linear.app/graphql`) through the official SDK.
|
|
17
|
+
|
|
18
|
+
## Prerequisites
|
|
19
|
+
|
|
20
|
+
1. Node.js and npm are installed.
|
|
21
|
+
2. Install script dependencies once:
|
|
22
|
+
- `cd {baseDir}/scripts && npm install`
|
|
23
|
+
3. Set your API key:
|
|
24
|
+
- `export LINEAR_API_KEY="lin_api_..."`
|
|
25
|
+
|
|
26
|
+
If dependencies or `LINEAR_API_KEY` are missing, stop and complete setup before issue/project operations.
|
|
27
|
+
|
|
28
|
+
## Authentication and Credentials
|
|
29
|
+
|
|
30
|
+
- Required credential: `LINEAR_API_KEY`.
|
|
31
|
+
- Get it from `https://linear.app/settings/api`.
|
|
32
|
+
- Use least-privilege access and a dedicated token for automation.
|
|
33
|
+
|
|
34
|
+
## Required Workflow
|
|
35
|
+
|
|
36
|
+
1. Clarify intent and scope:
|
|
37
|
+
- Team/project, labels, cycle, assignee, due date, priority.
|
|
38
|
+
2. Read current state first:
|
|
39
|
+
- List/get issues, projects, statuses, labels, users, cycles.
|
|
40
|
+
3. Apply mutations second:
|
|
41
|
+
- Create/update issues, comments, projects, milestones, labels.
|
|
42
|
+
4. Summarize exactly what changed:
|
|
43
|
+
- Mention IDs, states, assignees, blockers, and follow-up actions.
|
|
44
|
+
|
|
45
|
+
## Command Coverage
|
|
46
|
+
|
|
47
|
+
- Teams and projects:
|
|
48
|
+
`teams`, `projects`, `createProject`
|
|
49
|
+
- Issues:
|
|
50
|
+
`issues`, `issue`, `createIssue`, `updateIssue`
|
|
51
|
+
- Comments:
|
|
52
|
+
`createComment`
|
|
53
|
+
- States and labels:
|
|
54
|
+
`states`, `labels`
|
|
55
|
+
- User:
|
|
56
|
+
`user`
|
|
57
|
+
|
|
58
|
+
## Quick Examples
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
node {baseDir}/scripts/linear-cli.js teams
|
|
62
|
+
node {baseDir}/scripts/linear-cli.js projects
|
|
63
|
+
node {baseDir}/scripts/linear-cli.js issues
|
|
64
|
+
node {baseDir}/scripts/linear-cli.js issue ENG-123
|
|
65
|
+
node {baseDir}/scripts/linear-cli.js createIssue "Title" "Description" "team-id" '{"priority":2}'
|
|
66
|
+
node {baseDir}/scripts/linear-cli.js updateIssue "issue-id" '{"stateId":"state-id"}'
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Practical Workflows
|
|
70
|
+
|
|
71
|
+
- Triage urgent bugs:
|
|
72
|
+
list high-priority open issues, assign owners, move state to `In Progress`, add triage comments.
|
|
73
|
+
- Sprint planning:
|
|
74
|
+
review cycle scope, create missing issues, set priorities and estimates, align assignees.
|
|
75
|
+
- Release prep:
|
|
76
|
+
verify blockers, update project status, create milestone tasks, add rollout comments.
|
|
77
|
+
- Documentation cleanup:
|
|
78
|
+
find stale docs/issues, open follow-up tasks, link related records.
|
|
79
|
+
|
|
80
|
+
## Safety and Operational Rules
|
|
81
|
+
|
|
82
|
+
- Never invent IDs; fetch and confirm before updates.
|
|
83
|
+
- Prefer narrow updates over broad bulk edits.
|
|
84
|
+
- For bulk edits, explain grouping logic before applying changes.
|
|
85
|
+
- Do not include secrets in issue comments or descriptions.
|
|
86
|
+
- Do not send data to endpoints outside Linear API scope for this skill.
|
|
87
|
+
|
|
88
|
+
## References
|
|
89
|
+
|
|
90
|
+
- `references/API.md` for priority values and workflow patterns.
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: mcporter
|
|
3
|
+
description: Call external MCP servers via the mcporter CLI. Use for accessing tools from services like Context7, Linear, GitHub, Notion, etc.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# mcporter — MCP Tool Access
|
|
7
|
+
|
|
8
|
+
Use `mcporter` to discover and call tools on external MCP servers.
|
|
9
|
+
|
|
10
|
+
## List available servers
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
mcporter list
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## List tools on a specific server
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
mcporter list <server> --schema
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Call a tool
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
mcporter call <server>.<tool> key=value key2=value2
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Examples
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
# Resolve a library ID on Context7
|
|
32
|
+
mcporter call context7.resolve-library-id libraryName=react
|
|
33
|
+
|
|
34
|
+
# Get library docs
|
|
35
|
+
mcporter call context7.get-library-docs context7CompatibleLibraryID=/websites/react_dev topic=hooks
|
|
36
|
+
|
|
37
|
+
# Search Linear
|
|
38
|
+
mcporter call linear.search_documentation query="automations"
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Notes
|
|
42
|
+
|
|
43
|
+
- Config: `~/workspace/mcporter/config/mcporter.json`
|
|
44
|
+
- Auto-imports configs from Cursor, Claude, Codex, VS Code
|
|
45
|
+
- For OAuth servers, run `mcporter auth <server>` first
|
|
46
|
+
- Use `--output json` for machine-readable results
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# Model Scout
|
|
2
|
+
|
|
3
|
+
Find the best free cloud models and propose the top 3 candidates.
|
|
4
|
+
|
|
5
|
+
## When to use
|
|
6
|
+
User asks about model recommendations, best deals, or you need to evaluate if a better model is available.
|
|
7
|
+
|
|
8
|
+
## Config files
|
|
9
|
+
|
|
10
|
+
- **models.json** — `~/.abtars/config/models.json` (hot-reloaded)
|
|
11
|
+
- **transport.json** — `~/.abtars/config/transport.json` (lists providers, defaults, fallbackChain)
|
|
12
|
+
|
|
13
|
+
## transport.json provider fields (relevant to scouting)
|
|
14
|
+
|
|
15
|
+
Each provider in `transport.json` may have:
|
|
16
|
+
- **`defaults`** — `Record<agent, { model, fallbacks? }>` — preset models loaded on `/model change → provider`. Professor is required; missing subagents inherit professor's model.
|
|
17
|
+
- **`fallbackChain`** — `string[]` — ordered list of always-available models tried when the configured model fails. Used by subagent runtime (not professor).
|
|
18
|
+
|
|
19
|
+
When proposing new models, consider whether they should be added to a provider's `defaults` or `fallbackChain`.
|
|
20
|
+
|
|
21
|
+
## models.json schema
|
|
22
|
+
|
|
23
|
+
```json
|
|
24
|
+
{
|
|
25
|
+
"model-id": {
|
|
26
|
+
"contextWindow": 262144,
|
|
27
|
+
"maxOutput": 16384,
|
|
28
|
+
"rank": 2,
|
|
29
|
+
"cost": { "input": 0.0, "output": 0.0 },
|
|
30
|
+
"transports": ["ollama", "openrouter"],
|
|
31
|
+
"description": "High IQ free model, top Intelligence Index score",
|
|
32
|
+
"validatedAt": "2026-04-13"
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
- **rank**: 1 = frontier, 2 = strong, 3 = good, 4 = basic, 5 = minimal
|
|
38
|
+
- **cost**: per million tokens in USD. `0.0` = free
|
|
39
|
+
- **transports**: provider names from transport.json that can serve this model
|
|
40
|
+
- **description**: why this model was added (scout writes this)
|
|
41
|
+
- **validatedAt**: date when last verified alive (auto-set by script)
|
|
42
|
+
|
|
43
|
+
## Scouting workflow
|
|
44
|
+
|
|
45
|
+
### 1. Scan available free models
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
# OpenRouter free tier
|
|
49
|
+
python3 {baseDir}/scout-openrouter.py
|
|
50
|
+
|
|
51
|
+
# Ollama cloud models (no local, cloud only)
|
|
52
|
+
python3 {baseDir}/scout-ollama.py
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### 2. Research quality
|
|
56
|
+
|
|
57
|
+
Browse leaderboards to get Intelligence Index scores:
|
|
58
|
+
```bash
|
|
59
|
+
abtars-browser --action navigate --url "https://artificialanalysis.ai/leaderboards/models"
|
|
60
|
+
abtars-browser --action extract_text --max-chars 5000
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Search for new cloud models on Ollama:
|
|
64
|
+
```bash
|
|
65
|
+
abtars-browser --action navigate --url "https://ollama.com/search?q=cloud"
|
|
66
|
+
abtars-browser --action extract_text --max-chars 5000
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### 3. Propose top 3
|
|
70
|
+
|
|
71
|
+
After scanning and researching, propose exactly 3 candidates ranked by:
|
|
72
|
+
|
|
73
|
+
1. **Intelligence Index** (higher = better) → determines rank
|
|
74
|
+
2. **Context window** (bigger = fewer compactions)
|
|
75
|
+
3. **Tool calling** (required — model must support function calling)
|
|
76
|
+
4. **Throughput** (tokens/sec, matters for interactive use)
|
|
77
|
+
|
|
78
|
+
Format:
|
|
79
|
+
```
|
|
80
|
+
🏆 Top 3 free cloud models:
|
|
81
|
+
|
|
82
|
+
1. model-name (provider) — Intelligence: XX, Context: XXK
|
|
83
|
+
Why: [one sentence reason]
|
|
84
|
+
|
|
85
|
+
2. model-name (provider) — Intelligence: XX, Context: XXK
|
|
86
|
+
Why: [one sentence reason]
|
|
87
|
+
|
|
88
|
+
3. model-name (provider) — Intelligence: XX, Context: XXK
|
|
89
|
+
Why: [one sentence reason]
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### 4. Add approved models
|
|
93
|
+
|
|
94
|
+
After user approves, add with description explaining why:
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
python3 {baseDir}/scout-add-model.py \
|
|
98
|
+
"model-id" contextWindow maxOutput rank input_cost output_cost \
|
|
99
|
+
"Why this model: Intelligence XX, free, large context" \
|
|
100
|
+
ollama openrouter
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
The script automatically:
|
|
104
|
+
- Backs up models.json to `.old`
|
|
105
|
+
- Sets `validatedAt` to today's date
|
|
106
|
+
- Validates all entries after write
|
|
107
|
+
- Restores from backup if validation fails
|
|
108
|
+
|
|
109
|
+
### 5. Test
|
|
110
|
+
|
|
111
|
+
```
|
|
112
|
+
/model change → pick provider → verify defaults loaded
|
|
113
|
+
/model (check fallback chain displayed)
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## Liveness test
|
|
117
|
+
|
|
118
|
+
Before proposing, verify the model actually responds:
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
# Ollama cloud
|
|
122
|
+
curl -s http://localhost:11434/v1/chat/completions -d '{
|
|
123
|
+
"model": "model-name",
|
|
124
|
+
"messages": [{"role":"user","content":"Say hi"}],
|
|
125
|
+
"max_tokens": 5
|
|
126
|
+
}'
|
|
127
|
+
|
|
128
|
+
# OpenRouter
|
|
129
|
+
curl -s https://openrouter.ai/api/v1/chat/completions \
|
|
130
|
+
-H "Authorization: Bearer $OPENROUTER_API_KEY" \
|
|
131
|
+
-d '{"model":"model-id","messages":[{"role":"user","content":"Say hi"}],"max_tokens":5}'
|
|
132
|
+
```
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""scout-add-model.py — Add or update a model in models.json.
|
|
3
|
+
|
|
4
|
+
Usage:
|
|
5
|
+
scout-add-model.py MODEL_ID CTX_WINDOW MAX_OUTPUT RANK INPUT_COST OUTPUT_COST "DESCRIPTION" TRANSPORT [TRANSPORT...]
|
|
6
|
+
|
|
7
|
+
Example:
|
|
8
|
+
scout-add-model.py "nemotron-3-super:cloud" 262144 16384 2 0.0 0.0 "Nvidia 120B MoE, free" ollama openrouter
|
|
9
|
+
"""
|
|
10
|
+
import json, sys, shutil
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
from datetime import date
|
|
13
|
+
|
|
14
|
+
MODELS_PATH = Path.home() / ".abtars" / "config" / "models.json"
|
|
15
|
+
REQUIRED = {"contextWindow", "maxOutput", "rank", "cost", "transports"}
|
|
16
|
+
|
|
17
|
+
def main():
|
|
18
|
+
if len(sys.argv) < 8:
|
|
19
|
+
print(__doc__.strip(), file=sys.stderr)
|
|
20
|
+
sys.exit(2)
|
|
21
|
+
|
|
22
|
+
model_id = sys.argv[1]
|
|
23
|
+
ctx = int(sys.argv[2])
|
|
24
|
+
max_out = int(sys.argv[3])
|
|
25
|
+
rank = int(sys.argv[4])
|
|
26
|
+
in_cost = float(sys.argv[5])
|
|
27
|
+
out_cost = float(sys.argv[6])
|
|
28
|
+
desc = sys.argv[7]
|
|
29
|
+
transports = sys.argv[8:]
|
|
30
|
+
|
|
31
|
+
if not transports:
|
|
32
|
+
print("Error: at least one transport required", file=sys.stderr)
|
|
33
|
+
sys.exit(2)
|
|
34
|
+
|
|
35
|
+
backup = MODELS_PATH.with_suffix(".json.old")
|
|
36
|
+
shutil.copy2(MODELS_PATH, backup)
|
|
37
|
+
|
|
38
|
+
with open(MODELS_PATH) as f:
|
|
39
|
+
models = json.load(f)
|
|
40
|
+
|
|
41
|
+
models[model_id] = {
|
|
42
|
+
"contextWindow": ctx,
|
|
43
|
+
"maxOutput": max_out,
|
|
44
|
+
"rank": rank,
|
|
45
|
+
"cost": {"input": in_cost, "output": out_cost},
|
|
46
|
+
"transports": transports,
|
|
47
|
+
"description": desc,
|
|
48
|
+
"validatedAt": str(date.today()),
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
# Validate all entries
|
|
52
|
+
for mid, m in models.items():
|
|
53
|
+
missing = REQUIRED - set(m.keys())
|
|
54
|
+
if missing:
|
|
55
|
+
print(f"Validation failed: {mid} missing {missing}. Restoring backup.", file=sys.stderr)
|
|
56
|
+
shutil.copy2(backup, MODELS_PATH)
|
|
57
|
+
sys.exit(1)
|
|
58
|
+
|
|
59
|
+
with open(MODELS_PATH, "w") as f:
|
|
60
|
+
json.dump(models, f, indent=2)
|
|
61
|
+
f.write("\n")
|
|
62
|
+
|
|
63
|
+
free = "FREE" if in_cost == 0 and out_cost == 0 else f"${in_cost}/{out_cost}"
|
|
64
|
+
print(f"✓ {model_id} (rank {rank}, ctx {ctx//1024}K, {free}, via {transports})")
|
|
65
|
+
|
|
66
|
+
if __name__ == "__main__":
|
|
67
|
+
main()
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""List Ollama cloud models (free, no local GPU needed) + local installed.
|
|
3
|
+
Fetches from ollama.com library API for cloud models.
|
|
4
|
+
Usage:
|
|
5
|
+
scout-ollama.py # list only
|
|
6
|
+
scout-ollama.py --test # list + liveness test cloud models + write status
|
|
7
|
+
"""
|
|
8
|
+
import json, os, sys, urllib.request
|
|
9
|
+
|
|
10
|
+
config_dir = os.path.expanduser("~/.abtars/config")
|
|
11
|
+
models_path = os.path.join(config_dir, "models.json")
|
|
12
|
+
do_test = "--test" in sys.argv
|
|
13
|
+
|
|
14
|
+
# Load current catalog
|
|
15
|
+
try:
|
|
16
|
+
catalog = json.load(open(models_path))
|
|
17
|
+
except Exception:
|
|
18
|
+
catalog = {}
|
|
19
|
+
cataloged = {k for k in catalog if "ollama" in catalog[k].get("transports", [])}
|
|
20
|
+
|
|
21
|
+
# --- Cloud models from ollama.com ---
|
|
22
|
+
print("=== Ollama Cloud Models (free) ===")
|
|
23
|
+
print(f"{'Model':<45} {'Status'}")
|
|
24
|
+
print("-" * 60)
|
|
25
|
+
|
|
26
|
+
try:
|
|
27
|
+
req = urllib.request.Request(
|
|
28
|
+
"https://ollama.com/search?c=cloud",
|
|
29
|
+
headers={"User-Agent": "abtars-scout/1.0"}
|
|
30
|
+
)
|
|
31
|
+
import re
|
|
32
|
+
html = urllib.request.urlopen(req, timeout=10).read().decode()
|
|
33
|
+
cloud_names = list(dict.fromkeys(re.findall(r'href="/library/([^"]+)"', html)))
|
|
34
|
+
cloud_models = [{"name": f"{n}:cloud"} for n in cloud_names]
|
|
35
|
+
except Exception as e:
|
|
36
|
+
# Fallback: check local tags for :cloud suffix
|
|
37
|
+
cloud_models = []
|
|
38
|
+
print(f" (ollama.com scrape failed: {e}, falling back to local tags)")
|
|
39
|
+
|
|
40
|
+
# If API didn't work, check local tags for :cloud suffix
|
|
41
|
+
if not cloud_models:
|
|
42
|
+
try:
|
|
43
|
+
local_data = json.loads(urllib.request.urlopen("http://localhost:11434/api/tags", timeout=5).read())
|
|
44
|
+
cloud_models = [{"name": m["name"]} for m in local_data.get("models", []) if ":cloud" in m["name"]]
|
|
45
|
+
except Exception:
|
|
46
|
+
pass
|
|
47
|
+
|
|
48
|
+
from datetime import date
|
|
49
|
+
today = date.today().isoformat()
|
|
50
|
+
|
|
51
|
+
for m in cloud_models:
|
|
52
|
+
name = m["name"] if isinstance(m, dict) else m
|
|
53
|
+
if not name.endswith(":cloud") and ":cloud" not in name:
|
|
54
|
+
name = f"{name}:cloud"
|
|
55
|
+
status = "✓ cataloged" if name in cataloged else "NEW"
|
|
56
|
+
|
|
57
|
+
# Liveness test
|
|
58
|
+
if do_test and status == "NEW":
|
|
59
|
+
try:
|
|
60
|
+
test_data = json.dumps({"model": name, "messages": [{"role": "user", "content": "hi"}], "max_tokens": 5}).encode()
|
|
61
|
+
test_req = urllib.request.Request(
|
|
62
|
+
"http://localhost:11434/v1/chat/completions",
|
|
63
|
+
headers={"Content-Type": "application/json"},
|
|
64
|
+
data=test_data
|
|
65
|
+
)
|
|
66
|
+
resp = json.loads(urllib.request.urlopen(test_req, timeout=15).read())
|
|
67
|
+
content = resp.get("choices", [{}])[0].get("message", {}).get("content", "")
|
|
68
|
+
alive = bool(content.strip())
|
|
69
|
+
except Exception:
|
|
70
|
+
alive = False
|
|
71
|
+
status = f"{'✓ alive' if alive else '✗ dead'}"
|
|
72
|
+
|
|
73
|
+
if name not in catalog:
|
|
74
|
+
catalog[name] = {
|
|
75
|
+
"contextWindow": 262144, "maxOutput": 16384, "rank": 3,
|
|
76
|
+
"cost": {"input": 0.0, "output": 0.0},
|
|
77
|
+
"transports": ["ollama"],
|
|
78
|
+
"description": f"Ollama cloud model, scouted {today}",
|
|
79
|
+
"validatedAt": today, "status": "alive" if alive else "dead"
|
|
80
|
+
}
|
|
81
|
+
else:
|
|
82
|
+
catalog[name]["validatedAt"] = today
|
|
83
|
+
catalog[name]["status"] = "alive" if alive else "dead"
|
|
84
|
+
|
|
85
|
+
print(f" {name:<43} {status}")
|
|
86
|
+
|
|
87
|
+
# --- Local installed models ---
|
|
88
|
+
print(f"\n=== Local Installed ===")
|
|
89
|
+
print(f"{'Model':<45} {'Size':>8} {'Status'}")
|
|
90
|
+
print("-" * 60)
|
|
91
|
+
|
|
92
|
+
try:
|
|
93
|
+
local_data = json.loads(urllib.request.urlopen("http://localhost:11434/api/tags", timeout=5).read())
|
|
94
|
+
installed = local_data.get("models", [])
|
|
95
|
+
except Exception:
|
|
96
|
+
installed = []
|
|
97
|
+
print(" (Ollama not reachable at localhost:11434)")
|
|
98
|
+
|
|
99
|
+
try:
|
|
100
|
+
ps = json.loads(urllib.request.urlopen("http://localhost:11434/api/ps", timeout=5).read())
|
|
101
|
+
running = {m["name"] for m in ps.get("models", [])}
|
|
102
|
+
except Exception:
|
|
103
|
+
running = set()
|
|
104
|
+
|
|
105
|
+
for m in installed:
|
|
106
|
+
name = m["name"]
|
|
107
|
+
size_gb = m.get("size", 0) / 1e9
|
|
108
|
+
run_mark = " ▶" if name in running else ""
|
|
109
|
+
status = "✓ cataloged" if name in cataloged else "NEW"
|
|
110
|
+
print(f" {name:<43} {size_gb:>6.1f}G{run_mark} {status}")
|
|
111
|
+
|
|
112
|
+
if do_test:
|
|
113
|
+
with open(models_path, "w") as f:
|
|
114
|
+
json.dump(catalog, f, indent=2)
|
|
115
|
+
f.write("\n")
|
|
116
|
+
print(f"\n✓ models.json updated ({len(catalog)} entries)")
|