amos-fernandes commited on
Commit
3a65265
·
verified ·
1 Parent(s): 3e6243d

Upload 4501 files

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
.agent/workflows/update_clawdbot.md ADDED
@@ -0,0 +1,366 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ description: Update Clawdbot from upstream when branch has diverged (ahead/behind)
3
+ ---
4
+
5
+ # Clawdbot Upstream Sync Workflow
6
+
7
+ Use this workflow when your fork has diverged from upstream (e.g., "18 commits ahead, 29 commits behind").
8
+
9
+ ## Quick Reference
10
+
11
+ ```bash
12
+ # Check divergence status
13
+ git fetch upstream && git rev-list --left-right --count main...upstream/main
14
+
15
+ # Full sync (rebase preferred)
16
+ git fetch upstream && git rebase upstream/main && pnpm install && pnpm build && ./scripts/restart-mac.sh
17
+
18
+ # Check for Swift 6.2 issues after sync
19
+ grep -r "FileManager\.default\|Thread\.isMainThread" src/ apps/ --include="*.swift"
20
+ ```
21
+
22
+ ---
23
+
24
+ ## Step 1: Assess Divergence
25
+
26
+ ```bash
27
+ git fetch upstream
28
+ git log --oneline --left-right main...upstream/main | head -20
29
+ ```
30
+
31
+ This shows:
32
+ - `<` = your local commits (ahead)
33
+ - `>` = upstream commits you're missing (behind)
34
+
35
+ **Decision point:**
36
+ - Few local commits, many upstream → **Rebase** (cleaner history)
37
+ - Many local commits or shared branch → **Merge** (preserves history)
38
+
39
+ ---
40
+
41
+ ## Step 2A: Rebase Strategy (Preferred)
42
+
43
+ Replays your commits on top of upstream. Results in linear history.
44
+
45
+ ```bash
46
+ # Ensure working tree is clean
47
+ git status
48
+
49
+ # Rebase onto upstream
50
+ git rebase upstream/main
51
+ ```
52
+
53
+ ### Handling Rebase Conflicts
54
+
55
+ ```bash
56
+ # When conflicts occur:
57
+ # 1. Fix conflicts in the listed files
58
+ # 2. Stage resolved files
59
+ git add <resolved-files>
60
+
61
+ # 3. Continue rebase
62
+ git rebase --continue
63
+
64
+ # If a commit is no longer needed (already in upstream):
65
+ git rebase --skip
66
+
67
+ # To abort and return to original state:
68
+ git rebase --abort
69
+ ```
70
+
71
+ ### Common Conflict Patterns
72
+
73
+ | File | Resolution |
74
+ |------|------------|
75
+ | `package.json` | Take upstream deps, keep local scripts if needed |
76
+ | `pnpm-lock.yaml` | Accept upstream, regenerate with `pnpm install` |
77
+ | `*.patch` files | Usually take upstream version |
78
+ | Source files | Merge logic carefully, prefer upstream structure |
79
+
80
+ ---
81
+
82
+ ## Step 2B: Merge Strategy (Alternative)
83
+
84
+ Preserves all history with a merge commit.
85
+
86
+ ```bash
87
+ git merge upstream/main --no-edit
88
+ ```
89
+
90
+ Resolve conflicts same as rebase, then:
91
+ ```bash
92
+ git add <resolved-files>
93
+ git commit
94
+ ```
95
+
96
+ ---
97
+
98
+ ## Step 3: Rebuild Everything
99
+
100
+ After sync completes:
101
+
102
+ ```bash
103
+ # Install dependencies (regenerates lock if needed)
104
+ pnpm install
105
+
106
+ # Build TypeScript
107
+ pnpm build
108
+
109
+ # Build UI assets
110
+ pnpm ui:build
111
+
112
+ # Run diagnostics
113
+ pnpm clawdbot doctor
114
+ ```
115
+
116
+ ---
117
+
118
+ ## Step 4: Rebuild macOS App
119
+
120
+ ```bash
121
+ # Full rebuild, sign, and launch
122
+ ./scripts/restart-mac.sh
123
+
124
+ # Or just package without restart
125
+ pnpm mac:package
126
+ ```
127
+
128
+ ### Install to /Applications
129
+
130
+ ```bash
131
+ # Kill running app
132
+ pkill -x "Clawdbot" || true
133
+
134
+ # Move old version
135
+ mv /Applications/Clawdbot.app /tmp/Clawdbot-backup.app
136
+
137
+ # Install new build
138
+ cp -R dist/Clawdbot.app /Applications/
139
+
140
+ # Launch
141
+ open /Applications/Clawdbot.app
142
+ ```
143
+
144
+ ---
145
+
146
+ ## Step 4A: Verify macOS App & Agent
147
+
148
+ After rebuilding the macOS app, always verify it works correctly:
149
+
150
+ ```bash
151
+ # Check gateway health
152
+ pnpm clawdbot health
153
+
154
+ # Verify no zombie processes
155
+ ps aux | grep -E "(clawdbot|gateway)" | grep -v grep
156
+
157
+ # Test agent functionality by sending a verification message
158
+ pnpm clawdbot agent --message "Verification: macOS app rebuild successful - agent is responding." --session-id YOUR_TELEGRAM_SESSION_ID
159
+
160
+ # Confirm the message was received on Telegram
161
+ # (Check your Telegram chat with the bot)
162
+ ```
163
+
164
+ **Important:** Always wait for the Telegram verification message before proceeding. If the agent doesn't respond, troubleshoot the gateway or model configuration before pushing.
165
+
166
+ ---
167
+
168
+ ## Step 5: Handle Swift/macOS Build Issues (Common After Upstream Sync)
169
+
170
+ Upstream updates may introduce Swift 6.2 / macOS 26 SDK incompatibilities. Use analyze-mode for systematic debugging:
171
+
172
+ ### Analyze-Mode Investigation
173
+ ```bash
174
+ # Gather context with parallel agents
175
+ morph-mcp_warpgrep_codebase_search search_string="Find deprecated FileManager.default and Thread.isMainThread usages in Swift files" repo_path="/Volumes/Main SSD/Developer/clawdis"
176
+ morph-mcp_warpgrep_codebase_search search_string="Locate Peekaboo submodule and macOS app Swift files with concurrency issues" repo_path="/Volumes/Main SSD/Developer/clawdis"
177
+ ```
178
+
179
+ ### Common Swift 6.2 Fixes
180
+
181
+ **FileManager.default Deprecation:**
182
+ ```bash
183
+ # Search for deprecated usage
184
+ grep -r "FileManager\.default" src/ apps/ --include="*.swift"
185
+
186
+ # Replace with proper initialization
187
+ # OLD: FileManager.default
188
+ # NEW: FileManager()
189
+ ```
190
+
191
+ **Thread.isMainThread Deprecation:**
192
+ ```bash
193
+ # Search for deprecated usage
194
+ grep -r "Thread\.isMainThread" src/ apps/ --include="*.swift"
195
+
196
+ # Replace with modern concurrency check
197
+ # OLD: Thread.isMainThread
198
+ # NEW: await MainActor.run { ... } or DispatchQueue.main.sync { ... }
199
+ ```
200
+
201
+ ### Peekaboo Submodule Fixes
202
+ ```bash
203
+ # Check Peekaboo for concurrency issues
204
+ cd src/canvas-host/a2ui
205
+ grep -r "Thread\.isMainThread\|FileManager\.default" . --include="*.swift"
206
+
207
+ # Fix and rebuild submodule
208
+ cd /Volumes/Main SSD/Developer/clawdis
209
+ pnpm canvas:a2ui:bundle
210
+ ```
211
+
212
+ ### macOS App Concurrency Fixes
213
+ ```bash
214
+ # Check macOS app for issues
215
+ grep -r "Thread\.isMainThread\|FileManager\.default" apps/macos/ --include="*.swift"
216
+
217
+ # Clean and rebuild after fixes
218
+ cd apps/macos && rm -rf .build .swiftpm
219
+ ./scripts/restart-mac.sh
220
+ ```
221
+
222
+ ### Model Configuration Updates
223
+ If upstream introduced new model configurations:
224
+ ```bash
225
+ # Check for OpenRouter API key requirements
226
+ grep -r "openrouter\|OPENROUTER" src/ --include="*.ts" --include="*.js"
227
+
228
+ # Update clawdbot.json with fallback chains
229
+ # Add model fallback configurations as needed
230
+ ```
231
+
232
+ ---
233
+
234
+ ## Step 6: Verify & Push
235
+
236
+ ```bash
237
+ # Verify everything works
238
+ pnpm clawdbot health
239
+ pnpm test
240
+
241
+ # Push (force required after rebase)
242
+ git push origin main --force-with-lease
243
+
244
+ # Or regular push after merge
245
+ git push origin main
246
+ ```
247
+
248
+ ---
249
+
250
+ ## Troubleshooting
251
+
252
+ ### Build Fails After Sync
253
+
254
+ ```bash
255
+ # Clean and rebuild
256
+ rm -rf node_modules dist
257
+ pnpm install
258
+ pnpm build
259
+ ```
260
+
261
+ ### Type Errors (Bun/Node Incompatibility)
262
+
263
+ Common issue: `fetch.preconnect` type mismatch. Fix by using `FetchLike` type instead of `typeof fetch`.
264
+
265
+ ### macOS App Crashes on Launch
266
+
267
+ Usually resource bundle mismatch. Full rebuild required:
268
+ ```bash
269
+ cd apps/macos && rm -rf .build .swiftpm
270
+ ./scripts/restart-mac.sh
271
+ ```
272
+
273
+ ### Patch Failures
274
+
275
+ ```bash
276
+ # Check patch status
277
+ pnpm install 2>&1 | grep -i patch
278
+
279
+ # If patches fail, they may need updating for new dep versions
280
+ # Check patches/ directory against package.json patchedDependencies
281
+ ```
282
+
283
+ ### Swift 6.2 / macOS 26 SDK Build Failures
284
+
285
+ **Symptoms:** Build fails with deprecation warnings about `FileManager.default` or `Thread.isMainThread`
286
+
287
+ **Search-Mode Investigation:**
288
+ ```bash
289
+ # Exhaustive search for deprecated APIs
290
+ morph-mcp_warpgrep_codebase_search search_string="Find all Swift files using deprecated FileManager.default or Thread.isMainThread" repo_path="/Volumes/Main SSD/Developer/clawdis"
291
+ ```
292
+
293
+ **Quick Fix Commands:**
294
+ ```bash
295
+ # Find all affected files
296
+ find . -name "*.swift" -exec grep -l "FileManager\.default\|Thread\.isMainThread" {} \;
297
+
298
+ # Replace FileManager.default with FileManager()
299
+ find . -name "*.swift" -exec sed -i '' 's/FileManager\.default/FileManager()/g' {} \;
300
+
301
+ # For Thread.isMainThread, need manual review of each usage
302
+ grep -rn "Thread\.isMainThread" --include="*.swift" .
303
+ ```
304
+
305
+ **Rebuild After Fixes:**
306
+ ```bash
307
+ # Clean all build artifacts
308
+ rm -rf apps/macos/.build apps/macos/.swiftpm
309
+ rm -rf src/canvas-host/a2ui/.build
310
+
311
+ # Rebuild Peekaboo bundle
312
+ pnpm canvas:a2ui:bundle
313
+
314
+ # Full macOS rebuild
315
+ ./scripts/restart-mac.sh
316
+ ```
317
+
318
+ ---
319
+
320
+ ## Automation Script
321
+
322
+ Save as `scripts/sync-upstream.sh`:
323
+
324
+ ```bash
325
+ #!/usr/bin/env bash
326
+ set -euo pipefail
327
+
328
+ echo "==> Fetching upstream..."
329
+ git fetch upstream
330
+
331
+ echo "==> Current divergence:"
332
+ git rev-list --left-right --count main...upstream/main
333
+
334
+ echo "==> Rebasing onto upstream/main..."
335
+ git rebase upstream/main
336
+
337
+ echo "==> Installing dependencies..."
338
+ pnpm install
339
+
340
+ echo "==> Building..."
341
+ pnpm build
342
+ pnpm ui:build
343
+
344
+ echo "==> Running doctor..."
345
+ pnpm clawdbot doctor
346
+
347
+ echo "==> Rebuilding macOS app..."
348
+ ./scripts/restart-mac.sh
349
+
350
+ echo "==> Verifying gateway health..."
351
+ pnpm clawdbot health
352
+
353
+ echo "==> Checking for Swift 6.2 compatibility issues..."
354
+ if grep -r "FileManager\.default\|Thread\.isMainThread" src/ apps/ --include="*.swift" --quiet; then
355
+ echo "⚠️ Found potential Swift 6.2 deprecated API usage"
356
+ echo " Run manual fixes or use analyze-mode investigation"
357
+ else
358
+ echo "✅ No obvious Swift deprecation issues found"
359
+ fi
360
+
361
+ echo "==> Testing agent functionality..."
362
+ # Note: Update YOUR_TELEGRAM_SESSION_ID with actual session ID
363
+ pnpm clawdbot agent --message "Verification: Upstream sync and macOS rebuild completed successfully." --session-id YOUR_TELEGRAM_SESSION_ID || echo "Warning: Agent test failed - check Telegram for verification message"
364
+
365
+ echo "==> Done! Check Telegram for verification message, then run 'git push --force-with-lease' when ready."
366
+ ```
.detect-secrets.cfg ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # detect-secrets exclusion patterns (regex)
2
+ #
3
+ # Note: detect-secrets does not read this file by default. If you want these
4
+ # applied, wire them into your scan command (e.g. translate to --exclude-files
5
+ # / --exclude-lines) or into a baseline's filters_used.
6
+
7
+ [exclude-files]
8
+ # pnpm lockfiles contain lots of high-entropy package integrity blobs.
9
+ pattern = (^|/)pnpm-lock\.yaml$
10
+ # Generated output and vendored assets.
11
+ pattern = (^|/)(dist|vendor)/
12
+ # Local config file with allowlist patterns.
13
+ pattern = (^|/)\.detect-secrets\.cfg$
14
+
15
+ [exclude-lines]
16
+ # Fastlane checks for private key marker; not a real key.
17
+ pattern = key_content\.include\?\("BEGIN PRIVATE KEY"\)
18
+ # UI label string for Anthropic auth mode.
19
+ pattern = case \.apiKeyEnv: "API key \(env var\)"
20
+ # CodingKeys mapping uses apiKey literal.
21
+ pattern = case apikey = "apiKey"
22
+ # Schema labels referencing password fields (not actual secrets).
23
+ pattern = "gateway\.remote\.password"
24
+ pattern = "gateway\.auth\.password"
25
+ # Schema label for talk API key (label text only).
26
+ pattern = "talk\.apiKey"
27
+ # checking for typeof is not something we care about.
28
+ pattern = === "string"
29
+ # specific optional-chaining password check that didn't match the line above.
30
+ pattern = typeof remote\?\.password === "string"
.dockerignore ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .git
2
+ .worktrees
3
+ .bun-cache
4
+ .bun
5
+ .tmp
6
+ **/.tmp
7
+ .DS_Store
8
+ **/.DS_Store
9
+ *.png
10
+ *.jpg
11
+ *.jpeg
12
+ *.webp
13
+ *.gif
14
+ *.mp4
15
+ *.mov
16
+ *.wav
17
+ *.mp3
18
+ node_modules
19
+ **/node_modules
20
+ .pnpm-store
21
+ **/.pnpm-store
22
+ .turbo
23
+ **/.turbo
24
+ .cache
25
+ **/.cache
26
+ .next
27
+ **/.next
28
+ coverage
29
+ **/coverage
30
+ *.log
31
+ tmp
32
+ **/tmp
33
+
34
+ # build artifacts
35
+ dist
36
+ **/dist
37
+ apps/macos/.build
38
+ apps/ios/build
39
+ **/*.trace
40
+
41
+ # large app trees not needed for CLI build
42
+ apps/
43
+ assets/
44
+ Peekaboo/
45
+ Swabble/
46
+ Core/
47
+ Users/
48
+ vendor/
.env.example ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ # Copy to .env and fill with your Twilio credentials
2
+ TWILIO_ACCOUNT_SID=ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
3
+ TWILIO_AUTH_TOKEN=your_auth_token_here
4
+ # Must be a WhatsApp-enabled Twilio number, prefixed with whatsapp:
5
+ TWILIO_WHATSAPP_FROM=whatsapp:+17343367101
.gitattributes CHANGED
@@ -1,35 +1,21 @@
1
- *.7z filter=lfs diff=lfs merge=lfs -text
2
- *.arrow filter=lfs diff=lfs merge=lfs -text
3
- *.bin filter=lfs diff=lfs merge=lfs -text
4
- *.bz2 filter=lfs diff=lfs merge=lfs -text
5
- *.ckpt filter=lfs diff=lfs merge=lfs -text
6
- *.ftz filter=lfs diff=lfs merge=lfs -text
7
- *.gz filter=lfs diff=lfs merge=lfs -text
8
- *.h5 filter=lfs diff=lfs merge=lfs -text
9
- *.joblib filter=lfs diff=lfs merge=lfs -text
10
- *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
- *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
- *.model filter=lfs diff=lfs merge=lfs -text
13
- *.msgpack filter=lfs diff=lfs merge=lfs -text
14
- *.npy filter=lfs diff=lfs merge=lfs -text
15
- *.npz filter=lfs diff=lfs merge=lfs -text
16
- *.onnx filter=lfs diff=lfs merge=lfs -text
17
- *.ot filter=lfs diff=lfs merge=lfs -text
18
- *.parquet filter=lfs diff=lfs merge=lfs -text
19
- *.pb filter=lfs diff=lfs merge=lfs -text
20
- *.pickle filter=lfs diff=lfs merge=lfs -text
21
- *.pkl filter=lfs diff=lfs merge=lfs -text
22
- *.pt filter=lfs diff=lfs merge=lfs -text
23
- *.pth filter=lfs diff=lfs merge=lfs -text
24
- *.rar filter=lfs diff=lfs merge=lfs -text
25
- *.safetensors filter=lfs diff=lfs merge=lfs -text
26
- saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
- *.tar.* filter=lfs diff=lfs merge=lfs -text
28
- *.tar filter=lfs diff=lfs merge=lfs -text
29
- *.tflite filter=lfs diff=lfs merge=lfs -text
30
- *.tgz filter=lfs diff=lfs merge=lfs -text
31
- *.wasm filter=lfs diff=lfs merge=lfs -text
32
- *.xz filter=lfs diff=lfs merge=lfs -text
33
- *.zip filter=lfs diff=lfs merge=lfs -text
34
- *.zst filter=lfs diff=lfs merge=lfs -text
35
- *tfevents* filter=lfs diff=lfs merge=lfs -text
 
1
+ * text=auto eol=lf
2
+ apps/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png filter=lfs diff=lfs merge=lfs -text
3
+ apps/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png filter=lfs diff=lfs merge=lfs -text
4
+ apps/ios/Sources/Assets.xcassets/AppIcon.appiconset/icon-1024.png filter=lfs diff=lfs merge=lfs -text
5
+ apps/macos/Icon.icon/Assets/clawdbot-mac.png filter=lfs diff=lfs merge=lfs -text
6
+ apps/macos/Sources/Clawdbot/Resources/Clawdbot.icns filter=lfs diff=lfs merge=lfs -text
7
+ assets/dmg-background-small.png filter=lfs diff=lfs merge=lfs -text
8
+ assets/dmg-background.png filter=lfs diff=lfs merge=lfs -text
9
+ docs/assets/showcase/agents-ui.jpg filter=lfs diff=lfs merge=lfs -text
10
+ docs/assets/showcase/bambu-cli.png filter=lfs diff=lfs merge=lfs -text
11
+ docs/assets/showcase/codexmonitor.png filter=lfs diff=lfs merge=lfs -text
12
+ docs/assets/showcase/gohome-grafana.png filter=lfs diff=lfs merge=lfs -text
13
+ docs/assets/showcase/ios-testflight.jpg filter=lfs diff=lfs merge=lfs -text
14
+ docs/assets/showcase/oura-health.png filter=lfs diff=lfs merge=lfs -text
15
+ docs/assets/showcase/pr-review-telegram.jpg filter=lfs diff=lfs merge=lfs -text
16
+ docs/assets/showcase/roof-camera-sky.jpg filter=lfs diff=lfs merge=lfs -text
17
+ docs/assets/showcase/snag.png filter=lfs diff=lfs merge=lfs -text
18
+ docs/assets/showcase/wienerlinien.png filter=lfs diff=lfs merge=lfs -text
19
+ docs/assets/showcase/winix-air-purifier.jpg filter=lfs diff=lfs merge=lfs -text
20
+ docs/images/mobile-ui-screenshot.png filter=lfs diff=lfs merge=lfs -text
21
+ README-header.png filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
 
 
 
 
 
 
 
 
 
.github/FUNDING.yml ADDED
@@ -0,0 +1 @@
 
 
1
+ custom: ['https://github.com/sponsors/steipete']
.github/ISSUE_TEMPLATE/bug_report.md ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ name: Bug report
3
+ about: Report a problem or unexpected behavior in Clawdbot.
4
+ title: "[Bug]: "
5
+ labels: bug
6
+ ---
7
+
8
+ ## Summary
9
+ What went wrong?
10
+
11
+ ## Steps to reproduce
12
+ 1.
13
+ 2.
14
+ 3.
15
+
16
+ ## Expected behavior
17
+ What did you expect to happen?
18
+
19
+ ## Actual behavior
20
+ What actually happened?
21
+
22
+ ## Environment
23
+ - Clawdbot version:
24
+ - OS:
25
+ - Install method (pnpm/npx/docker/etc):
26
+
27
+ ## Logs or screenshots
28
+ Paste relevant logs or add screenshots (redact secrets).
.github/ISSUE_TEMPLATE/config.yml ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ blank_issues_enabled: true
2
+ contact_links:
3
+ - name: Onboarding
4
+ url: https://discord.gg/clawd
5
+ about: New to Clawdbot? Join Discord for setup guidance from Krill in #help.
6
+ - name: Support
7
+ url: https://discord.gg/clawd
8
+ about: Get help from Krill and the community on Discord in #help.
.github/ISSUE_TEMPLATE/feature_request.md ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ name: Feature request
3
+ about: Suggest an idea or improvement for Clawdbot.
4
+ title: "[Feature]: "
5
+ labels: enhancement
6
+ ---
7
+
8
+ ## Summary
9
+ Describe the problem you are trying to solve or the opportunity you see.
10
+
11
+ ## Proposed solution
12
+ What would you like Clawdbot to do?
13
+
14
+ ## Alternatives considered
15
+ Any other approaches you have considered?
16
+
17
+ ## Additional context
18
+ Links, screenshots, or related issues.
.github/actionlint.yaml ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # actionlint configuration
2
+ # https://github.com/rhysd/actionlint/blob/main/docs/config.md
3
+
4
+ self-hosted-runner:
5
+ labels:
6
+ # Blacksmith CI runners
7
+ - blacksmith-4vcpu-ubuntu-2404
8
+ - blacksmith-4vcpu-windows-2025
9
+
10
+ # Ignore patterns for known issues
11
+ paths:
12
+ .github/workflows/**/*.yml:
13
+ ignore:
14
+ # Ignore shellcheck warnings (we run shellcheck separately)
15
+ - 'shellcheck reported issue.+'
16
+ # Ignore intentional if: false for disabled jobs
17
+ - 'constant expression "false" in condition'
.github/dependabot.yml ADDED
@@ -0,0 +1,113 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Dependabot configuration
2
+ # https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
3
+
4
+ version: 2
5
+
6
+ registries:
7
+ npm-npmjs:
8
+ type: npm-registry
9
+ url: https://registry.npmjs.org
10
+ replaces-base: true
11
+
12
+ updates:
13
+ # npm dependencies (root)
14
+ - package-ecosystem: npm
15
+ directory: /
16
+ schedule:
17
+ interval: weekly
18
+ cooldown:
19
+ default-days: 7
20
+ groups:
21
+ production:
22
+ dependency-type: production
23
+ update-types:
24
+ - minor
25
+ - patch
26
+ development:
27
+ dependency-type: development
28
+ update-types:
29
+ - minor
30
+ - patch
31
+ open-pull-requests-limit: 10
32
+ registries:
33
+ - npm-npmjs
34
+
35
+ # GitHub Actions
36
+ - package-ecosystem: github-actions
37
+ directory: /
38
+ schedule:
39
+ interval: weekly
40
+ cooldown:
41
+ default-days: 7
42
+ groups:
43
+ actions:
44
+ patterns:
45
+ - "*"
46
+ update-types:
47
+ - minor
48
+ - patch
49
+ open-pull-requests-limit: 5
50
+
51
+ # Swift Package Manager - macOS app
52
+ - package-ecosystem: swift
53
+ directory: /apps/macos
54
+ schedule:
55
+ interval: weekly
56
+ cooldown:
57
+ default-days: 7
58
+ groups:
59
+ swift-deps:
60
+ patterns:
61
+ - "*"
62
+ update-types:
63
+ - minor
64
+ - patch
65
+ open-pull-requests-limit: 5
66
+
67
+ # Swift Package Manager - shared ClawdbotKit
68
+ - package-ecosystem: swift
69
+ directory: /apps/shared/ClawdbotKit
70
+ schedule:
71
+ interval: weekly
72
+ cooldown:
73
+ default-days: 7
74
+ groups:
75
+ swift-deps:
76
+ patterns:
77
+ - "*"
78
+ update-types:
79
+ - minor
80
+ - patch
81
+ open-pull-requests-limit: 5
82
+
83
+ # Swift Package Manager - Swabble
84
+ - package-ecosystem: swift
85
+ directory: /Swabble
86
+ schedule:
87
+ interval: weekly
88
+ cooldown:
89
+ default-days: 7
90
+ groups:
91
+ swift-deps:
92
+ patterns:
93
+ - "*"
94
+ update-types:
95
+ - minor
96
+ - patch
97
+ open-pull-requests-limit: 5
98
+
99
+ # Gradle - Android app
100
+ - package-ecosystem: gradle
101
+ directory: /apps/android
102
+ schedule:
103
+ interval: weekly
104
+ cooldown:
105
+ default-days: 7
106
+ groups:
107
+ android-deps:
108
+ patterns:
109
+ - "*"
110
+ update-types:
111
+ - minor
112
+ - patch
113
+ open-pull-requests-limit: 5
.github/labeler.yml ADDED
@@ -0,0 +1,222 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "channel: bluebubbles":
2
+ - changed-files:
3
+ - any-glob-to-any-file:
4
+ - "extensions/bluebubbles/**"
5
+ - "docs/channels/bluebubbles.md"
6
+ "channel: discord":
7
+ - changed-files:
8
+ - any-glob-to-any-file:
9
+ - "src/discord/**"
10
+ - "extensions/discord/**"
11
+ - "docs/channels/discord.md"
12
+ "channel: googlechat":
13
+ - changed-files:
14
+ - any-glob-to-any-file:
15
+ - "extensions/googlechat/**"
16
+ - "docs/channels/googlechat.md"
17
+ "channel: imessage":
18
+ - changed-files:
19
+ - any-glob-to-any-file:
20
+ - "src/imessage/**"
21
+ - "extensions/imessage/**"
22
+ - "docs/channels/imessage.md"
23
+ "channel: line":
24
+ - changed-files:
25
+ - any-glob-to-any-file:
26
+ - "extensions/line/**"
27
+ - "docs/channels/line.md"
28
+ "channel: matrix":
29
+ - changed-files:
30
+ - any-glob-to-any-file:
31
+ - "extensions/matrix/**"
32
+ - "docs/channels/matrix.md"
33
+ "channel: mattermost":
34
+ - changed-files:
35
+ - any-glob-to-any-file:
36
+ - "extensions/mattermost/**"
37
+ - "docs/channels/mattermost.md"
38
+ "channel: msteams":
39
+ - changed-files:
40
+ - any-glob-to-any-file:
41
+ - "extensions/msteams/**"
42
+ - "docs/channels/msteams.md"
43
+ "channel: nextcloud-talk":
44
+ - changed-files:
45
+ - any-glob-to-any-file:
46
+ - "extensions/nextcloud-talk/**"
47
+ - "docs/channels/nextcloud-talk.md"
48
+ "channel: nostr":
49
+ - changed-files:
50
+ - any-glob-to-any-file:
51
+ - "extensions/nostr/**"
52
+ - "docs/channels/nostr.md"
53
+ "channel: signal":
54
+ - changed-files:
55
+ - any-glob-to-any-file:
56
+ - "src/signal/**"
57
+ - "extensions/signal/**"
58
+ - "docs/channels/signal.md"
59
+ "channel: slack":
60
+ - changed-files:
61
+ - any-glob-to-any-file:
62
+ - "src/slack/**"
63
+ - "extensions/slack/**"
64
+ - "docs/channels/slack.md"
65
+ "channel: telegram":
66
+ - changed-files:
67
+ - any-glob-to-any-file:
68
+ - "src/telegram/**"
69
+ - "extensions/telegram/**"
70
+ - "docs/channels/telegram.md"
71
+ "channel: tlon":
72
+ - changed-files:
73
+ - any-glob-to-any-file:
74
+ - "extensions/tlon/**"
75
+ - "docs/channels/tlon.md"
76
+ "channel: voice-call":
77
+ - changed-files:
78
+ - any-glob-to-any-file:
79
+ - "extensions/voice-call/**"
80
+ "channel: whatsapp-web":
81
+ - changed-files:
82
+ - any-glob-to-any-file:
83
+ - "src/web/**"
84
+ - "extensions/whatsapp/**"
85
+ - "docs/channels/whatsapp.md"
86
+ "channel: zalo":
87
+ - changed-files:
88
+ - any-glob-to-any-file:
89
+ - "extensions/zalo/**"
90
+ - "docs/channels/zalo.md"
91
+ "channel: zalouser":
92
+ - changed-files:
93
+ - any-glob-to-any-file:
94
+ - "extensions/zalouser/**"
95
+ - "docs/channels/zalouser.md"
96
+
97
+ "app: android":
98
+ - changed-files:
99
+ - any-glob-to-any-file:
100
+ - "apps/android/**"
101
+ - "docs/platforms/android.md"
102
+ "app: ios":
103
+ - changed-files:
104
+ - any-glob-to-any-file:
105
+ - "apps/ios/**"
106
+ - "docs/platforms/ios.md"
107
+ "app: macos":
108
+ - changed-files:
109
+ - any-glob-to-any-file:
110
+ - "apps/macos/**"
111
+ - "docs/platforms/macos.md"
112
+ - "docs/platforms/mac/**"
113
+ "app: web-ui":
114
+ - changed-files:
115
+ - any-glob-to-any-file:
116
+ - "ui/**"
117
+ - "src/gateway/control-ui.ts"
118
+ - "src/gateway/control-ui-shared.ts"
119
+ - "src/gateway/protocol/**"
120
+ - "src/gateway/server-methods/chat.ts"
121
+ - "src/infra/control-ui-assets.ts"
122
+
123
+ "gateway":
124
+ - changed-files:
125
+ - any-glob-to-any-file:
126
+ - "src/gateway/**"
127
+ - "src/daemon/**"
128
+ - "docs/gateway/**"
129
+
130
+ "docs":
131
+ - changed-files:
132
+ - any-glob-to-any-file:
133
+ - "docs/**"
134
+ - "docs.acp.md"
135
+
136
+ "cli":
137
+ - changed-files:
138
+ - any-glob-to-any-file:
139
+ - "src/cli/**"
140
+
141
+ "commands":
142
+ - changed-files:
143
+ - any-glob-to-any-file:
144
+ - "src/commands/**"
145
+
146
+ "scripts":
147
+ - changed-files:
148
+ - any-glob-to-any-file:
149
+ - "scripts/**"
150
+
151
+ "docker":
152
+ - changed-files:
153
+ - any-glob-to-any-file:
154
+ - "Dockerfile"
155
+ - "Dockerfile.*"
156
+ - "docker-compose.yml"
157
+ - "docker-setup.sh"
158
+ - ".dockerignore"
159
+ - "scripts/**/*docker*"
160
+ - "scripts/**/Dockerfile*"
161
+ - "scripts/sandbox-*.sh"
162
+ - "src/agents/sandbox*.ts"
163
+ - "src/commands/sandbox*.ts"
164
+ - "src/cli/sandbox-cli.ts"
165
+ - "src/docker-setup.test.ts"
166
+ - "src/config/**/*sandbox*"
167
+ - "docs/cli/sandbox.md"
168
+ - "docs/gateway/sandbox*.md"
169
+ - "docs/install/docker.md"
170
+ - "docs/multi-agent-sandbox-tools.md"
171
+
172
+ "agents":
173
+ - changed-files:
174
+ - any-glob-to-any-file:
175
+ - "src/agents/**"
176
+
177
+ "security":
178
+ - changed-files:
179
+ - any-glob-to-any-file:
180
+ - "docs/cli/security.md"
181
+ - "docs/gateway/security.md"
182
+
183
+ "extensions: copilot-proxy":
184
+ - changed-files:
185
+ - any-glob-to-any-file:
186
+ - "extensions/copilot-proxy/**"
187
+ "extensions: diagnostics-otel":
188
+ - changed-files:
189
+ - any-glob-to-any-file:
190
+ - "extensions/diagnostics-otel/**"
191
+ "extensions: google-antigravity-auth":
192
+ - changed-files:
193
+ - any-glob-to-any-file:
194
+ - "extensions/google-antigravity-auth/**"
195
+ "extensions: google-gemini-cli-auth":
196
+ - changed-files:
197
+ - any-glob-to-any-file:
198
+ - "extensions/google-gemini-cli-auth/**"
199
+ "extensions: llm-task":
200
+ - changed-files:
201
+ - any-glob-to-any-file:
202
+ - "extensions/llm-task/**"
203
+ "extensions: lobster":
204
+ - changed-files:
205
+ - any-glob-to-any-file:
206
+ - "extensions/lobster/**"
207
+ "extensions: memory-core":
208
+ - changed-files:
209
+ - any-glob-to-any-file:
210
+ - "extensions/memory-core/**"
211
+ "extensions: memory-lancedb":
212
+ - changed-files:
213
+ - any-glob-to-any-file:
214
+ - "extensions/memory-lancedb/**"
215
+ "extensions: open-prose":
216
+ - changed-files:
217
+ - any-glob-to-any-file:
218
+ - "extensions/open-prose/**"
219
+ "extensions: qwen-portal-auth":
220
+ - changed-files:
221
+ - any-glob-to-any-file:
222
+ - "extensions/qwen-portal-auth/**"
.github/workflows/auto-response.yml ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: Auto response
2
+
3
+ on:
4
+ issues:
5
+ types: [labeled]
6
+ pull_request_target:
7
+ types: [labeled]
8
+
9
+ permissions:
10
+ issues: write
11
+ pull-requests: write
12
+
13
+ jobs:
14
+ auto-response:
15
+ runs-on: ubuntu-latest
16
+ steps:
17
+ - uses: actions/create-github-app-token@v1
18
+ id: app-token
19
+ with:
20
+ app-id: "2729701"
21
+ private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
22
+ - name: Handle labeled items
23
+ uses: actions/github-script@v7
24
+ with:
25
+ github-token: ${{ steps.app-token.outputs.token }}
26
+ script: |
27
+ const rules = [
28
+ {
29
+ label: "skill-clawdhub",
30
+ close: true,
31
+ message:
32
+ "Thanks for the contribution! New skills should be published to Clawdhub for everyone to use. We’re keeping the core lean on skills, so I’m closing this out.",
33
+ },
34
+ ];
35
+
36
+ const labelName = context.payload.label?.name;
37
+ if (!labelName) {
38
+ return;
39
+ }
40
+
41
+ const rule = rules.find((item) => item.label === labelName);
42
+ if (!rule) {
43
+ return;
44
+ }
45
+
46
+ const issueNumber = context.payload.issue?.number ?? context.payload.pull_request?.number;
47
+ if (!issueNumber) {
48
+ return;
49
+ }
50
+
51
+ await github.rest.issues.createComment({
52
+ owner: context.repo.owner,
53
+ repo: context.repo.repo,
54
+ issue_number: issueNumber,
55
+ body: rule.message,
56
+ });
57
+
58
+ if (rule.close) {
59
+ await github.rest.issues.update({
60
+ owner: context.repo.owner,
61
+ repo: context.repo.repo,
62
+ issue_number: issueNumber,
63
+ state: "closed",
64
+ });
65
+ }
.github/workflows/ci.yml ADDED
@@ -0,0 +1,646 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ pull_request:
6
+
7
+ jobs:
8
+ install-check:
9
+ runs-on: blacksmith-4vcpu-ubuntu-2404
10
+ steps:
11
+ - name: Checkout
12
+ uses: actions/checkout@v4
13
+ with:
14
+ submodules: false
15
+
16
+ - name: Checkout submodules (retry)
17
+ run: |
18
+ set -euo pipefail
19
+ git submodule sync --recursive
20
+ for attempt in 1 2 3 4 5; do
21
+ if git -c protocol.version=2 submodule update --init --force --depth=1 --recursive; then
22
+ exit 0
23
+ fi
24
+ echo "Submodule update failed (attempt $attempt/5). Retrying…"
25
+ sleep $((attempt * 10))
26
+ done
27
+ exit 1
28
+
29
+ - name: Setup Node.js
30
+ uses: actions/setup-node@v4
31
+ with:
32
+ node-version: 22.x
33
+ check-latest: true
34
+
35
+ - name: Setup pnpm (corepack retry)
36
+ run: |
37
+ set -euo pipefail
38
+ corepack enable
39
+ for attempt in 1 2 3; do
40
+ if corepack prepare pnpm@10.23.0 --activate; then
41
+ pnpm -v
42
+ exit 0
43
+ fi
44
+ echo "corepack prepare failed (attempt $attempt/3). Retrying..."
45
+ sleep $((attempt * 10))
46
+ done
47
+ exit 1
48
+
49
+ - name: Runtime versions
50
+ run: |
51
+ node -v
52
+ npm -v
53
+ pnpm -v
54
+
55
+ - name: Capture node path
56
+ run: echo "NODE_BIN=$(dirname \"$(node -p \"process.execPath\")\")" >> "$GITHUB_ENV"
57
+
58
+ - name: Install dependencies (frozen)
59
+ env:
60
+ CI: true
61
+ run: |
62
+ export PATH="$NODE_BIN:$PATH"
63
+ which node
64
+ node -v
65
+ pnpm -v
66
+ pnpm install --frozen-lockfile --ignore-scripts=false --config.engine-strict=false --config.enable-pre-post-scripts=true
67
+
68
+ checks:
69
+ runs-on: blacksmith-4vcpu-ubuntu-2404
70
+ strategy:
71
+ fail-fast: false
72
+ matrix:
73
+ include:
74
+ - runtime: node
75
+ task: lint
76
+ command: pnpm lint
77
+ - runtime: node
78
+ task: test
79
+ command: pnpm test
80
+ - runtime: node
81
+ task: build
82
+ command: pnpm build
83
+ - runtime: node
84
+ task: protocol
85
+ command: pnpm protocol:check
86
+ - runtime: node
87
+ task: format
88
+ command: pnpm format
89
+ - runtime: bun
90
+ task: test
91
+ command: bunx vitest run
92
+ - runtime: bun
93
+ task: build
94
+ command: bunx tsc -p tsconfig.json
95
+ steps:
96
+ - name: Checkout
97
+ uses: actions/checkout@v4
98
+ with:
99
+ submodules: false
100
+
101
+ - name: Checkout submodules (retry)
102
+ run: |
103
+ set -euo pipefail
104
+ git submodule sync --recursive
105
+ for attempt in 1 2 3 4 5; do
106
+ if git -c protocol.version=2 submodule update --init --force --depth=1 --recursive; then
107
+ exit 0
108
+ fi
109
+ echo "Submodule update failed (attempt $attempt/5). Retrying…"
110
+ sleep $((attempt * 10))
111
+ done
112
+ exit 1
113
+
114
+ - name: Setup Node.js
115
+ uses: actions/setup-node@v4
116
+ with:
117
+ node-version: 22.x
118
+ check-latest: true
119
+
120
+ - name: Setup pnpm (corepack retry)
121
+ run: |
122
+ set -euo pipefail
123
+ corepack enable
124
+ for attempt in 1 2 3; do
125
+ if corepack prepare pnpm@10.23.0 --activate; then
126
+ pnpm -v
127
+ exit 0
128
+ fi
129
+ echo "corepack prepare failed (attempt $attempt/3). Retrying..."
130
+ sleep $((attempt * 10))
131
+ done
132
+ exit 1
133
+
134
+ - name: Setup Bun
135
+ uses: oven-sh/setup-bun@v2
136
+ with:
137
+ bun-version: latest
138
+
139
+ - name: Runtime versions
140
+ run: |
141
+ node -v
142
+ npm -v
143
+ bun -v
144
+ pnpm -v
145
+
146
+ - name: Capture node path
147
+ run: echo "NODE_BIN=$(dirname \"$(node -p \"process.execPath\")\")" >> "$GITHUB_ENV"
148
+
149
+ - name: Install dependencies
150
+ env:
151
+ CI: true
152
+ run: |
153
+ export PATH="$NODE_BIN:$PATH"
154
+ which node
155
+ node -v
156
+ pnpm -v
157
+ pnpm install --frozen-lockfile --ignore-scripts=false --config.engine-strict=false --config.enable-pre-post-scripts=true || pnpm install --frozen-lockfile --ignore-scripts=false --config.engine-strict=false --config.enable-pre-post-scripts=true
158
+
159
+ - name: Run ${{ matrix.task }} (${{ matrix.runtime }})
160
+ run: ${{ matrix.command }}
161
+
162
+ secrets:
163
+ runs-on: blacksmith-4vcpu-ubuntu-2404
164
+ steps:
165
+ - name: Checkout
166
+ uses: actions/checkout@v4
167
+ with:
168
+ submodules: false
169
+
170
+ - name: Setup Python
171
+ uses: actions/setup-python@v5
172
+ with:
173
+ python-version: "3.12"
174
+
175
+ - name: Install detect-secrets
176
+ run: |
177
+ python -m pip install --upgrade pip
178
+ python -m pip install detect-secrets==1.5.0
179
+
180
+ - name: Detect secrets
181
+ run: |
182
+ if ! detect-secrets scan --baseline .secrets.baseline; then
183
+ echo "::error::Secret scanning failed. See docs/gateway/security.md#secret-scanning-detect-secrets"
184
+ exit 1
185
+ fi
186
+
187
+ checks-windows:
188
+ runs-on: blacksmith-4vcpu-windows-2025
189
+ env:
190
+ NODE_OPTIONS: --max-old-space-size=4096
191
+ defaults:
192
+ run:
193
+ shell: bash
194
+ strategy:
195
+ fail-fast: false
196
+ matrix:
197
+ include:
198
+ - runtime: node
199
+ task: lint
200
+ command: pnpm lint
201
+ - runtime: node
202
+ task: test
203
+ command: pnpm test
204
+ - runtime: node
205
+ task: build
206
+ command: pnpm build
207
+ - runtime: node
208
+ task: protocol
209
+ command: pnpm protocol:check
210
+ steps:
211
+ - name: Checkout
212
+ uses: actions/checkout@v4
213
+ with:
214
+ submodules: false
215
+
216
+ - name: Checkout submodules (retry)
217
+ run: |
218
+ set -euo pipefail
219
+ git submodule sync --recursive
220
+ for attempt in 1 2 3 4 5; do
221
+ if git -c protocol.version=2 submodule update --init --force --depth=1 --recursive; then
222
+ exit 0
223
+ fi
224
+ echo "Submodule update failed (attempt $attempt/5). Retrying…"
225
+ sleep $((attempt * 10))
226
+ done
227
+ exit 1
228
+
229
+ - name: Setup Node.js
230
+ uses: actions/setup-node@v4
231
+ with:
232
+ node-version: 22.x
233
+ check-latest: true
234
+
235
+ - name: Setup pnpm (corepack retry)
236
+ run: |
237
+ set -euo pipefail
238
+ corepack enable
239
+ for attempt in 1 2 3; do
240
+ if corepack prepare pnpm@10.23.0 --activate; then
241
+ pnpm -v
242
+ exit 0
243
+ fi
244
+ echo "corepack prepare failed (attempt $attempt/3). Retrying..."
245
+ sleep $((attempt * 10))
246
+ done
247
+ exit 1
248
+
249
+ - name: Setup Bun
250
+ uses: oven-sh/setup-bun@v2
251
+ with:
252
+ bun-version: latest
253
+
254
+ - name: Runtime versions
255
+ run: |
256
+ node -v
257
+ npm -v
258
+ bun -v
259
+ pnpm -v
260
+
261
+ - name: Capture node path
262
+ run: echo "NODE_BIN=$(dirname \"$(node -p \"process.execPath\")\")" >> "$GITHUB_ENV"
263
+
264
+ - name: Install dependencies
265
+ env:
266
+ CI: true
267
+ run: |
268
+ export PATH="$NODE_BIN:$PATH"
269
+ which node
270
+ node -v
271
+ pnpm -v
272
+ pnpm install --frozen-lockfile --ignore-scripts=false --config.engine-strict=false --config.enable-pre-post-scripts=true || pnpm install --frozen-lockfile --ignore-scripts=false --config.engine-strict=false --config.enable-pre-post-scripts=true
273
+
274
+ - name: Run ${{ matrix.task }} (${{ matrix.runtime }})
275
+ run: ${{ matrix.command }}
276
+
277
+ checks-macos:
278
+ if: github.event_name == 'pull_request'
279
+ runs-on: macos-latest
280
+ strategy:
281
+ fail-fast: false
282
+ matrix:
283
+ include:
284
+ - task: test
285
+ command: pnpm test
286
+ steps:
287
+ - name: Checkout
288
+ uses: actions/checkout@v4
289
+ with:
290
+ submodules: false
291
+
292
+ - name: Checkout submodules (retry)
293
+ run: |
294
+ set -euo pipefail
295
+ git submodule sync --recursive
296
+ for attempt in 1 2 3 4 5; do
297
+ if git -c protocol.version=2 submodule update --init --force --depth=1 --recursive; then
298
+ exit 0
299
+ fi
300
+ echo "Submodule update failed (attempt $attempt/5). Retrying…"
301
+ sleep $((attempt * 10))
302
+ done
303
+ exit 1
304
+
305
+ - name: Setup Node.js
306
+ uses: actions/setup-node@v4
307
+ with:
308
+ node-version: 22.x
309
+ check-latest: true
310
+
311
+ - name: Setup pnpm (corepack retry)
312
+ run: |
313
+ set -euo pipefail
314
+ corepack enable
315
+ for attempt in 1 2 3; do
316
+ if corepack prepare pnpm@10.23.0 --activate; then
317
+ pnpm -v
318
+ exit 0
319
+ fi
320
+ echo "corepack prepare failed (attempt $attempt/3). Retrying..."
321
+ sleep $((attempt * 10))
322
+ done
323
+ exit 1
324
+
325
+ - name: Runtime versions
326
+ run: |
327
+ node -v
328
+ npm -v
329
+ pnpm -v
330
+
331
+ - name: Capture node path
332
+ run: echo "NODE_BIN=$(dirname \"$(node -p \"process.execPath\")\")" >> "$GITHUB_ENV"
333
+
334
+ - name: Install dependencies
335
+ env:
336
+ CI: true
337
+ run: |
338
+ export PATH="$NODE_BIN:$PATH"
339
+ which node
340
+ node -v
341
+ pnpm -v
342
+ pnpm install --frozen-lockfile --ignore-scripts=false --config.engine-strict=false --config.enable-pre-post-scripts=true || pnpm install --frozen-lockfile --ignore-scripts=false --config.engine-strict=false --config.enable-pre-post-scripts=true
343
+
344
+ - name: Run ${{ matrix.task }}
345
+ env:
346
+ NODE_OPTIONS: --max-old-space-size=4096
347
+ run: ${{ matrix.command }}
348
+
349
+ macos-app:
350
+ if: github.event_name == 'pull_request'
351
+ runs-on: macos-latest
352
+ strategy:
353
+ fail-fast: false
354
+ matrix:
355
+ include:
356
+ - task: lint
357
+ command: |
358
+ swiftlint --config .swiftlint.yml
359
+ swiftformat --lint apps/macos/Sources --config .swiftformat
360
+ - task: build
361
+ command: |
362
+ set -euo pipefail
363
+ for attempt in 1 2 3; do
364
+ if swift build --package-path apps/macos --configuration release; then
365
+ exit 0
366
+ fi
367
+ echo "swift build failed (attempt $attempt/3). Retrying…"
368
+ sleep $((attempt * 20))
369
+ done
370
+ exit 1
371
+ - task: test
372
+ command: |
373
+ set -euo pipefail
374
+ for attempt in 1 2 3; do
375
+ if swift test --package-path apps/macos --parallel --enable-code-coverage --show-codecov-path; then
376
+ exit 0
377
+ fi
378
+ echo "swift test failed (attempt $attempt/3). Retrying…"
379
+ sleep $((attempt * 20))
380
+ done
381
+ exit 1
382
+ steps:
383
+ - name: Checkout
384
+ uses: actions/checkout@v4
385
+ with:
386
+ submodules: false
387
+
388
+ - name: Checkout submodules (retry)
389
+ run: |
390
+ set -euo pipefail
391
+ git submodule sync --recursive
392
+ for attempt in 1 2 3 4 5; do
393
+ if git -c protocol.version=2 submodule update --init --force --depth=1 --recursive; then
394
+ exit 0
395
+ fi
396
+ echo "Submodule update failed (attempt $attempt/5). Retrying…"
397
+ sleep $((attempt * 10))
398
+ done
399
+ exit 1
400
+
401
+ - name: Select Xcode 26.1
402
+ run: |
403
+ sudo xcode-select -s /Applications/Xcode_26.1.app
404
+ xcodebuild -version
405
+
406
+ - name: Install XcodeGen / SwiftLint / SwiftFormat
407
+ run: |
408
+ brew install xcodegen swiftlint swiftformat
409
+
410
+ - name: Show toolchain
411
+ run: |
412
+ sw_vers
413
+ xcodebuild -version
414
+ swift --version
415
+
416
+ - name: Run ${{ matrix.task }}
417
+ run: ${{ matrix.command }}
418
+ ios:
419
+ if: false # ignore iOS in CI for now
420
+ runs-on: macos-latest
421
+ steps:
422
+ - name: Checkout
423
+ uses: actions/checkout@v4
424
+ with:
425
+ submodules: false
426
+
427
+ - name: Checkout submodules (retry)
428
+ run: |
429
+ set -euo pipefail
430
+ git submodule sync --recursive
431
+ for attempt in 1 2 3 4 5; do
432
+ if git -c protocol.version=2 submodule update --init --force --depth=1 --recursive; then
433
+ exit 0
434
+ fi
435
+ echo "Submodule update failed (attempt $attempt/5). Retrying…"
436
+ sleep $((attempt * 10))
437
+ done
438
+ exit 1
439
+
440
+ - name: Select Xcode 26.1
441
+ run: |
442
+ sudo xcode-select -s /Applications/Xcode_26.1.app
443
+ xcodebuild -version
444
+
445
+ - name: Install XcodeGen
446
+ run: brew install xcodegen
447
+
448
+ - name: Install SwiftLint / SwiftFormat
449
+ run: brew install swiftlint swiftformat
450
+
451
+ - name: Show toolchain
452
+ run: |
453
+ sw_vers
454
+ xcodebuild -version
455
+ swift --version
456
+
457
+ - name: Generate iOS project
458
+ run: |
459
+ cd apps/ios
460
+ xcodegen generate
461
+
462
+ - name: iOS tests
463
+ run: |
464
+ set -euo pipefail
465
+ RESULT_BUNDLE_PATH="$RUNNER_TEMP/Clawdis-iOS.xcresult"
466
+ DEST_ID="$(
467
+ python3 - <<'PY'
468
+ import json
469
+ import subprocess
470
+ import sys
471
+ import uuid
472
+
473
+ def sh(args: list[str]) -> str:
474
+ return subprocess.check_output(args, text=True).strip()
475
+
476
+ # Prefer an already-created iPhone simulator if it exists.
477
+ devices = json.loads(sh(["xcrun", "simctl", "list", "devices", "-j"]))
478
+ candidates: list[tuple[str, str]] = []
479
+ for runtime, devs in (devices.get("devices") or {}).items():
480
+ for dev in devs or []:
481
+ if not dev.get("isAvailable"):
482
+ continue
483
+ name = str(dev.get("name") or "")
484
+ udid = str(dev.get("udid") or "")
485
+ if not udid or not name.startswith("iPhone"):
486
+ continue
487
+ candidates.append((name, udid))
488
+
489
+ candidates.sort(key=lambda it: (0 if "iPhone 16" in it[0] else 1, it[0]))
490
+ if candidates:
491
+ print(candidates[0][1])
492
+ sys.exit(0)
493
+
494
+ # Otherwise, create one from the newest available iOS runtime.
495
+ runtimes = json.loads(sh(["xcrun", "simctl", "list", "runtimes", "-j"])).get("runtimes") or []
496
+ ios = [rt for rt in runtimes if rt.get("platform") == "iOS" and rt.get("isAvailable")]
497
+ if not ios:
498
+ print("No available iOS runtimes found.", file=sys.stderr)
499
+ sys.exit(1)
500
+
501
+ def version_key(rt: dict) -> tuple[int, ...]:
502
+ parts: list[int] = []
503
+ for p in str(rt.get("version") or "0").split("."):
504
+ try:
505
+ parts.append(int(p))
506
+ except ValueError:
507
+ parts.append(0)
508
+ return tuple(parts)
509
+
510
+ ios.sort(key=version_key, reverse=True)
511
+ runtime = ios[0]
512
+ runtime_id = str(runtime.get("identifier") or "")
513
+ if not runtime_id:
514
+ print("Missing iOS runtime identifier.", file=sys.stderr)
515
+ sys.exit(1)
516
+
517
+ supported = runtime.get("supportedDeviceTypes") or []
518
+ iphones = [dt for dt in supported if dt.get("productFamily") == "iPhone"]
519
+ if not iphones:
520
+ print("No iPhone device types for iOS runtime.", file=sys.stderr)
521
+ sys.exit(1)
522
+
523
+ iphones.sort(
524
+ key=lambda dt: (
525
+ 0 if "iPhone 16" in str(dt.get("name") or "") else 1,
526
+ str(dt.get("name") or ""),
527
+ )
528
+ )
529
+ device_type_id = str(iphones[0].get("identifier") or "")
530
+ if not device_type_id:
531
+ print("Missing iPhone device type identifier.", file=sys.stderr)
532
+ sys.exit(1)
533
+
534
+ sim_name = f"CI iPhone {uuid.uuid4().hex[:8]}"
535
+ udid = sh(["xcrun", "simctl", "create", sim_name, device_type_id, runtime_id])
536
+ if not udid:
537
+ print("Failed to create iPhone simulator.", file=sys.stderr)
538
+ sys.exit(1)
539
+ print(udid)
540
+ PY
541
+ )"
542
+ echo "Using iOS Simulator id: $DEST_ID"
543
+ xcodebuild test \
544
+ -project apps/ios/Clawdis.xcodeproj \
545
+ -scheme Clawdis \
546
+ -destination "platform=iOS Simulator,id=$DEST_ID" \
547
+ -resultBundlePath "$RESULT_BUNDLE_PATH" \
548
+ -enableCodeCoverage YES
549
+
550
+ - name: iOS coverage summary
551
+ run: |
552
+ set -euo pipefail
553
+ RESULT_BUNDLE_PATH="$RUNNER_TEMP/Clawdis-iOS.xcresult"
554
+ xcrun xccov view --report --only-targets "$RESULT_BUNDLE_PATH"
555
+
556
+ - name: iOS coverage gate (43%)
557
+ run: |
558
+ set -euo pipefail
559
+ RESULT_BUNDLE_PATH="$RUNNER_TEMP/Clawdis-iOS.xcresult"
560
+ RESULT_BUNDLE_PATH="$RESULT_BUNDLE_PATH" python3 - <<'PY'
561
+ import json
562
+ import os
563
+ import subprocess
564
+ import sys
565
+
566
+ target_name = "Clawdis.app"
567
+ minimum = 0.43
568
+
569
+ report = json.loads(
570
+ subprocess.check_output(
571
+ ["xcrun", "xccov", "view", "--report", "--json", os.environ["RESULT_BUNDLE_PATH"]],
572
+ text=True,
573
+ )
574
+ )
575
+
576
+ target_coverage = None
577
+ for target in report.get("targets", []):
578
+ if target.get("name") == target_name:
579
+ target_coverage = float(target["lineCoverage"])
580
+ break
581
+
582
+ if target_coverage is None:
583
+ print(f"Could not find coverage for target: {target_name}")
584
+ sys.exit(1)
585
+
586
+ print(f"{target_name} line coverage: {target_coverage * 100:.2f}% (min {minimum * 100:.2f}%)")
587
+ if target_coverage + 1e-12 < minimum:
588
+ sys.exit(1)
589
+ PY
590
+
591
+ android:
592
+ runs-on: blacksmith-4vcpu-ubuntu-2404
593
+ strategy:
594
+ fail-fast: false
595
+ matrix:
596
+ include:
597
+ - task: test
598
+ command: ./gradlew --no-daemon :app:testDebugUnitTest
599
+ - task: build
600
+ command: ./gradlew --no-daemon :app:assembleDebug
601
+ steps:
602
+ - name: Checkout
603
+ uses: actions/checkout@v4
604
+ with:
605
+ submodules: false
606
+
607
+ - name: Checkout submodules (retry)
608
+ run: |
609
+ set -euo pipefail
610
+ git submodule sync --recursive
611
+ for attempt in 1 2 3 4 5; do
612
+ if git -c protocol.version=2 submodule update --init --force --depth=1 --recursive; then
613
+ exit 0
614
+ fi
615
+ echo "Submodule update failed (attempt $attempt/5). Retrying…"
616
+ sleep $((attempt * 10))
617
+ done
618
+ exit 1
619
+
620
+ - name: Setup Java
621
+ uses: actions/setup-java@v4
622
+ with:
623
+ distribution: temurin
624
+ java-version: 21
625
+
626
+ - name: Setup Android SDK
627
+ uses: android-actions/setup-android@v3
628
+ with:
629
+ accept-android-sdk-licenses: false
630
+
631
+ - name: Setup Gradle
632
+ uses: gradle/actions/setup-gradle@v4
633
+ with:
634
+ gradle-version: 8.11.1
635
+
636
+ - name: Install Android SDK packages
637
+ run: |
638
+ yes | sdkmanager --licenses >/dev/null
639
+ sdkmanager --install \
640
+ "platform-tools" \
641
+ "platforms;android-36" \
642
+ "build-tools;36.0.0"
643
+
644
+ - name: Run Android ${{ matrix.task }}
645
+ working-directory: apps/android
646
+ run: ${{ matrix.command }}
.github/workflows/docker-release.yml ADDED
@@ -0,0 +1,143 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: Docker Release
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+ tags:
8
+ - "v*"
9
+
10
+ env:
11
+ REGISTRY: ghcr.io
12
+ IMAGE_NAME: ${{ github.repository }}
13
+
14
+ jobs:
15
+ # Build amd64 image
16
+ build-amd64:
17
+ runs-on: ubuntu-latest
18
+ permissions:
19
+ packages: write
20
+ contents: read
21
+ outputs:
22
+ image-digest: ${{ steps.build.outputs.digest }}
23
+ image-metadata: ${{ steps.meta.outputs.json }}
24
+ steps:
25
+ - name: Checkout
26
+ uses: actions/checkout@v4
27
+
28
+ - name: Set up Docker Buildx
29
+ uses: docker/setup-buildx-action@v3
30
+
31
+ - name: Login to GitHub Container Registry
32
+ uses: docker/login-action@v3
33
+ with:
34
+ registry: ${{ env.REGISTRY }}
35
+ username: ${{ github.repository_owner }}
36
+ password: ${{ secrets.GITHUB_TOKEN }}
37
+
38
+ - name: Extract metadata
39
+ id: meta
40
+ uses: docker/metadata-action@v5
41
+ with:
42
+ images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
43
+ tags: |
44
+ type=ref,event=branch
45
+ type=semver,pattern={{version}}
46
+ type=semver,pattern={{version}},suffix=-amd64
47
+ type=semver,pattern={{version}},suffix=-arm64
48
+ type=ref,event=branch,suffix=-amd64
49
+ type=ref,event=branch,suffix=-arm64
50
+
51
+ - name: Build and push amd64 image
52
+ id: build
53
+ uses: docker/build-push-action@v6
54
+ with:
55
+ context: .
56
+ platforms: linux/amd64
57
+ labels: ${{ steps.meta.outputs.labels }}
58
+ tags: ${{ steps.meta.outputs.tags }}
59
+ cache-from: type=gha
60
+ cache-to: type=gha,mode=max
61
+ provenance: false
62
+ push: true
63
+
64
+ # Build arm64 image
65
+ build-arm64:
66
+ runs-on: ubuntu-24.04-arm
67
+ permissions:
68
+ packages: write
69
+ contents: read
70
+ outputs:
71
+ image-digest: ${{ steps.build.outputs.digest }}
72
+ image-metadata: ${{ steps.meta.outputs.json }}
73
+ steps:
74
+ - name: Checkout
75
+ uses: actions/checkout@v4
76
+
77
+ - name: Set up Docker Buildx
78
+ uses: docker/setup-buildx-action@v3
79
+
80
+ - name: Login to GitHub Container Registry
81
+ uses: docker/login-action@v3
82
+ with:
83
+ registry: ${{ env.REGISTRY }}
84
+ username: ${{ github.repository_owner }}
85
+ password: ${{ secrets.GITHUB_TOKEN }}
86
+
87
+ - name: Extract metadata
88
+ id: meta
89
+ uses: docker/metadata-action@v5
90
+ with:
91
+ images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
92
+ tags: |
93
+ type=ref,event=branch
94
+ type=semver,pattern={{version}}
95
+ type=semver,pattern={{version}},suffix=-amd64
96
+ type=semver,pattern={{version}},suffix=-arm64
97
+ type=ref,event=branch,suffix=-amd64
98
+ type=ref,event=branch,suffix=-arm64
99
+
100
+ - name: Build and push arm64 image
101
+ id: build
102
+ uses: docker/build-push-action@v6
103
+ with:
104
+ context: .
105
+ platforms: linux/arm64
106
+ labels: ${{ steps.meta.outputs.labels }}
107
+ tags: ${{ steps.meta.outputs.tags }}
108
+ cache-from: type=gha
109
+ cache-to: type=gha,mode=max
110
+ provenance: false
111
+ push: true
112
+
113
+ # Create multi-platform manifest
114
+ create-manifest:
115
+ runs-on: ubuntu-latest
116
+ permissions:
117
+ packages: write
118
+ contents: read
119
+ needs: [build-amd64, build-arm64]
120
+ steps:
121
+ - name: Login to GitHub Container Registry
122
+ uses: docker/login-action@v3
123
+ with:
124
+ registry: ${{ env.REGISTRY }}
125
+ username: ${{ github.repository_owner }}
126
+ password: ${{ secrets.GITHUB_TOKEN }}
127
+
128
+ - name: Extract metadata for manifest
129
+ id: meta
130
+ uses: docker/metadata-action@v5
131
+ with:
132
+ images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
133
+ tags: |
134
+ type=ref,event=branch
135
+ type=semver,pattern={{version}}
136
+
137
+ - name: Create and push manifest
138
+ run: |
139
+ docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
140
+ ${{ needs.build-amd64.outputs.image-digest }} \
141
+ ${{ needs.build-arm64.outputs.image-digest }}
142
+ env:
143
+ DOCKER_METADATA_OUTPUT_JSON: ${{ steps.meta.outputs.json }}
.github/workflows/install-smoke.yml ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: Install Smoke
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ workflow_dispatch:
8
+
9
+ jobs:
10
+ install-smoke:
11
+ runs-on: ubuntu-latest
12
+ steps:
13
+ - name: Checkout CLI
14
+ uses: actions/checkout@v4
15
+
16
+ - name: Setup pnpm (corepack retry)
17
+ run: |
18
+ set -euo pipefail
19
+ corepack enable
20
+ for attempt in 1 2 3; do
21
+ if corepack prepare pnpm@10.23.0 --activate; then
22
+ pnpm -v
23
+ exit 0
24
+ fi
25
+ echo "corepack prepare failed (attempt $attempt/3). Retrying..."
26
+ sleep $((attempt * 10))
27
+ done
28
+ exit 1
29
+
30
+ - name: Install pnpm deps (minimal)
31
+ run: pnpm install --ignore-scripts --frozen-lockfile
32
+
33
+ - name: Run installer docker tests
34
+ env:
35
+ CLAWDBOT_INSTALL_URL: https://clawd.bot/install.sh
36
+ CLAWDBOT_INSTALL_CLI_URL: https://clawd.bot/install-cli.sh
37
+ CLAWDBOT_NO_ONBOARD: "1"
38
+ CLAWDBOT_INSTALL_SMOKE_SKIP_CLI: "1"
39
+ CLAWDBOT_INSTALL_SMOKE_SKIP_NONROOT: ${{ github.event_name == 'pull_request' && '1' || '0' }}
40
+ CLAWDBOT_INSTALL_SMOKE_PREVIOUS: "2026.1.11-4"
41
+ run: pnpm test:install:smoke
.github/workflows/labeler.yml ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: Labeler
2
+
3
+ on:
4
+ pull_request_target:
5
+ types: [opened, synchronize, reopened]
6
+
7
+ permissions:
8
+ contents: read
9
+ pull-requests: write
10
+
11
+ jobs:
12
+ label:
13
+ runs-on: ubuntu-latest
14
+ steps:
15
+ - uses: actions/create-github-app-token@v1
16
+ id: app-token
17
+ with:
18
+ app-id: "2729701"
19
+ private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
20
+ - uses: actions/labeler@v5
21
+ with:
22
+ configuration-path: .github/labeler.yml
23
+ repo-token: ${{ steps.app-token.outputs.token }}
24
+ sync-labels: true
.github/workflows/workflow-sanity.yml ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: Workflow Sanity
2
+
3
+ on:
4
+ pull_request:
5
+ push:
6
+
7
+ jobs:
8
+ no-tabs:
9
+ runs-on: ubuntu-latest
10
+ steps:
11
+ - name: Checkout
12
+ uses: actions/checkout@v4
13
+
14
+ - name: Fail on tabs in workflow files
15
+ run: |
16
+ python - <<'PY'
17
+ from __future__ import annotations
18
+
19
+ import pathlib
20
+ import sys
21
+
22
+ root = pathlib.Path(".github/workflows")
23
+ bad: list[str] = []
24
+ for path in sorted(root.rglob("*.yml")):
25
+ if b"\t" in path.read_bytes():
26
+ bad.append(str(path))
27
+
28
+ for path in sorted(root.rglob("*.yaml")):
29
+ if b"\t" in path.read_bytes():
30
+ bad.append(str(path))
31
+
32
+ if bad:
33
+ print("Tabs found in workflow file(s):")
34
+ for path in bad:
35
+ print(f"- {path}")
36
+ sys.exit(1)
37
+ PY
.gitignore ADDED
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ node_modules
2
+ **/node_modules/
3
+ .env
4
+ docker-compose.extra.yml
5
+ dist
6
+ *.bun-build
7
+ pnpm-lock.yaml
8
+ bun.lock
9
+ bun.lockb
10
+ coverage
11
+ .pnpm-store
12
+ .worktrees/
13
+ .DS_Store
14
+ **/.DS_Store
15
+ ui/src/ui/__screenshots__/
16
+ ui/playwright-report/
17
+ ui/test-results/
18
+
19
+ # Bun build artifacts
20
+ *.bun-build
21
+ apps/macos/.build/
22
+ apps/shared/ClawdbotKit/.build/
23
+ **/ModuleCache/
24
+ bin/
25
+ bin/clawdbot-mac
26
+ bin/docs-list
27
+ apps/macos/.build-local/
28
+ apps/macos/.swiftpm/
29
+ apps/shared/ClawdbotKit/.swiftpm/
30
+ Core/
31
+ apps/ios/*.xcodeproj/
32
+ apps/ios/*.xcworkspace/
33
+ apps/ios/.swiftpm/
34
+ vendor/
35
+ apps/ios/Clawdbot.xcodeproj/
36
+ apps/ios/Clawdbot.xcodeproj/**
37
+ apps/macos/.build/**
38
+ **/*.bun-build
39
+ apps/ios/*.xcfilelist
40
+
41
+ # Vendor build artifacts
42
+ vendor/a2ui/renderers/lit/dist/
43
+ src/canvas-host/a2ui/*.bundle.js
44
+ src/canvas-host/a2ui/*.map
45
+ .bundle.hash
46
+
47
+ # fastlane (iOS)
48
+ apps/ios/fastlane/README.md
49
+ apps/ios/fastlane/report.xml
50
+ apps/ios/fastlane/Preview.html
51
+ apps/ios/fastlane/screenshots/
52
+ apps/ios/fastlane/test_output/
53
+ apps/ios/fastlane/logs/
54
+ apps/ios/fastlane/.env
55
+ apps/ios/fastlane/report.xml
56
+
57
+ # fastlane build artifacts (local)
58
+ apps/ios/*.ipa
59
+ apps/ios/*.dSYM.zip
60
+
61
+ # provisioning profiles (local)
62
+ apps/ios/*.mobileprovision
63
+ .env
64
+
65
+ # Local untracked files
66
+ .local/
67
+ .vscode/
68
+ IDENTITY.md
69
+ USER.md
70
+ .tgz
71
+
72
+ # local tooling
73
+ .serena/
.npmrc ADDED
@@ -0,0 +1 @@
 
 
1
+ allow-build-scripts=@whiskeysockets/baileys,sharp,esbuild,protobufjs,fs-ext,node-pty,@lydell/node-pty,@matrix-org/matrix-sdk-crypto-nodejs
.oxfmtrc.jsonc ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ {
2
+ "$schema": "./node_modules/oxfmt/configuration_schema.json",
3
+ "indentWidth": 2,
4
+ "printWidth": 100
5
+ }
.oxlintrc.json ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "$schema": "./node_modules/oxlint/configuration_schema.json",
3
+ "plugins": [
4
+ "unicorn",
5
+ "typescript",
6
+ "oxc"
7
+ ],
8
+ "categories": {
9
+ "correctness": "error"
10
+ },
11
+ "ignorePatterns": ["src/canvas-host/a2ui/a2ui.bundle.js"]
12
+ }
.pre-commit-config.yaml ADDED
@@ -0,0 +1,105 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Pre-commit hooks for clawdbot
2
+ # Install: prek install
3
+ # Run manually: prek run --all-files
4
+ #
5
+ # See https://pre-commit.com for more information
6
+
7
+ repos:
8
+ # Basic file hygiene
9
+ - repo: https://github.com/pre-commit/pre-commit-hooks
10
+ rev: v6.0.0
11
+ hooks:
12
+ - id: trailing-whitespace
13
+ exclude: '^(docs/|dist/|vendor/|.*\.snap$)'
14
+ - id: end-of-file-fixer
15
+ exclude: '^(docs/|dist/|vendor/|.*\.snap$)'
16
+ - id: check-yaml
17
+ args: [--allow-multiple-documents]
18
+ - id: check-added-large-files
19
+ args: [--maxkb=500]
20
+ - id: check-merge-conflict
21
+
22
+ # Secret detection (same as CI)
23
+ - repo: https://github.com/Yelp/detect-secrets
24
+ rev: v1.5.0
25
+ hooks:
26
+ - id: detect-secrets
27
+ args:
28
+ - --baseline
29
+ - .secrets.baseline
30
+ - --exclude-files
31
+ - '(^|/)(dist/|vendor/|pnpm-lock\.yaml$|\.detect-secrets\.cfg$)'
32
+ - --exclude-lines
33
+ - 'key_content\.include\?\("BEGIN PRIVATE KEY"\)'
34
+ - --exclude-lines
35
+ - 'case \.apiKeyEnv: "API key \(env var\)"'
36
+ - --exclude-lines
37
+ - 'case apikey = "apiKey"'
38
+ - --exclude-lines
39
+ - '"gateway\.remote\.password"'
40
+ - --exclude-lines
41
+ - '"gateway\.auth\.password"'
42
+ - --exclude-lines
43
+ - '"talk\.apiKey"'
44
+ - --exclude-lines
45
+ - '=== "string"'
46
+ - --exclude-lines
47
+ - 'typeof remote\?\.password === "string"'
48
+
49
+ # Shell script linting
50
+ - repo: https://github.com/koalaman/shellcheck-precommit
51
+ rev: v0.11.0
52
+ hooks:
53
+ - id: shellcheck
54
+ args: [--severity=error] # Only fail on errors, not warnings/info
55
+ # Exclude vendor and scripts with embedded code or known issues
56
+ exclude: '^(vendor/|scripts/e2e/)'
57
+
58
+ # GitHub Actions linting
59
+ - repo: https://github.com/rhysd/actionlint
60
+ rev: v1.7.10
61
+ hooks:
62
+ - id: actionlint
63
+
64
+ # GitHub Actions security audit
65
+ - repo: https://github.com/zizmorcore/zizmor-pre-commit
66
+ rev: v1.22.0
67
+ hooks:
68
+ - id: zizmor
69
+ args: [--persona=regular, --min-severity=medium, --min-confidence=medium]
70
+ exclude: '^(vendor/|Swabble/)'
71
+
72
+ # Project checks (same commands as CI)
73
+ - repo: local
74
+ hooks:
75
+ # oxlint --type-aware src test
76
+ - id: oxlint
77
+ name: oxlint
78
+ entry: scripts/pre-commit/run-node-tool.sh oxlint --type-aware src test
79
+ language: system
80
+ pass_filenames: false
81
+ types_or: [javascript, jsx, ts, tsx]
82
+
83
+ # oxfmt --check src test
84
+ - id: oxfmt
85
+ name: oxfmt
86
+ entry: scripts/pre-commit/run-node-tool.sh oxfmt --check src test
87
+ language: system
88
+ pass_filenames: false
89
+ types_or: [javascript, jsx, ts, tsx]
90
+
91
+ # swiftlint (same as CI)
92
+ - id: swiftlint
93
+ name: swiftlint
94
+ entry: swiftlint --config .swiftlint.yml
95
+ language: system
96
+ pass_filenames: false
97
+ types: [swift]
98
+
99
+ # swiftformat --lint (same as CI)
100
+ - id: swiftformat
101
+ name: swiftformat
102
+ entry: swiftformat --lint apps/macos/Sources --config .swiftformat
103
+ language: system
104
+ pass_filenames: false
105
+ types: [swift]
.prettierignore ADDED
@@ -0,0 +1 @@
 
 
1
+ src/canvas-host/a2ui/a2ui.bundle.js
.secrets.baseline ADDED
@@ -0,0 +1,2191 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "version": "1.5.0",
3
+ "plugins_used": [
4
+ {
5
+ "name": "ArtifactoryDetector"
6
+ },
7
+ {
8
+ "name": "AWSKeyDetector"
9
+ },
10
+ {
11
+ "name": "AzureStorageKeyDetector"
12
+ },
13
+ {
14
+ "name": "Base64HighEntropyString",
15
+ "limit": 4.5
16
+ },
17
+ {
18
+ "name": "BasicAuthDetector"
19
+ },
20
+ {
21
+ "name": "CloudantDetector"
22
+ },
23
+ {
24
+ "name": "DiscordBotTokenDetector"
25
+ },
26
+ {
27
+ "name": "GitHubTokenDetector"
28
+ },
29
+ {
30
+ "name": "GitLabTokenDetector"
31
+ },
32
+ {
33
+ "name": "HexHighEntropyString",
34
+ "limit": 3.0
35
+ },
36
+ {
37
+ "name": "IbmCloudIamDetector"
38
+ },
39
+ {
40
+ "name": "IbmCosHmacDetector"
41
+ },
42
+ {
43
+ "name": "IPPublicDetector"
44
+ },
45
+ {
46
+ "name": "JwtTokenDetector"
47
+ },
48
+ {
49
+ "name": "KeywordDetector",
50
+ "keyword_exclude": ""
51
+ },
52
+ {
53
+ "name": "MailchimpDetector"
54
+ },
55
+ {
56
+ "name": "NpmDetector"
57
+ },
58
+ {
59
+ "name": "OpenAIDetector"
60
+ },
61
+ {
62
+ "name": "PrivateKeyDetector"
63
+ },
64
+ {
65
+ "name": "PypiTokenDetector"
66
+ },
67
+ {
68
+ "name": "SendGridDetector"
69
+ },
70
+ {
71
+ "name": "SlackDetector"
72
+ },
73
+ {
74
+ "name": "SoftlayerDetector"
75
+ },
76
+ {
77
+ "name": "SquareOAuthDetector"
78
+ },
79
+ {
80
+ "name": "StripeDetector"
81
+ },
82
+ {
83
+ "name": "TelegramBotTokenDetector"
84
+ },
85
+ {
86
+ "name": "TwilioKeyDetector"
87
+ }
88
+ ],
89
+ "filters_used": [
90
+ {
91
+ "path": "detect_secrets.filters.allowlist.is_line_allowlisted"
92
+ },
93
+ {
94
+ "path": "detect_secrets.filters.common.is_baseline_file",
95
+ "filename": ".secrets.baseline"
96
+ },
97
+ {
98
+ "path": "detect_secrets.filters.common.is_ignored_due_to_verification_policies",
99
+ "min_level": 2
100
+ },
101
+ {
102
+ "path": "detect_secrets.filters.heuristic.is_indirect_reference"
103
+ },
104
+ {
105
+ "path": "detect_secrets.filters.heuristic.is_likely_id_string"
106
+ },
107
+ {
108
+ "path": "detect_secrets.filters.heuristic.is_lock_file"
109
+ },
110
+ {
111
+ "path": "detect_secrets.filters.heuristic.is_not_alphanumeric_string"
112
+ },
113
+ {
114
+ "path": "detect_secrets.filters.heuristic.is_potential_uuid"
115
+ },
116
+ {
117
+ "path": "detect_secrets.filters.heuristic.is_prefixed_with_dollar_sign"
118
+ },
119
+ {
120
+ "path": "detect_secrets.filters.heuristic.is_sequential_string"
121
+ },
122
+ {
123
+ "path": "detect_secrets.filters.heuristic.is_swagger_file"
124
+ },
125
+ {
126
+ "path": "detect_secrets.filters.heuristic.is_templated_secret"
127
+ },
128
+ {
129
+ "path": "detect_secrets.filters.regex.should_exclude_file",
130
+ "pattern": [
131
+ "(^|/)pnpm-lock\\.yaml$"
132
+ ]
133
+ },
134
+ {
135
+ "path": "detect_secrets.filters.regex.should_exclude_line",
136
+ "pattern": [
137
+ "key_content\\.include\\?\\(\"BEGIN PRIVATE KEY\"\\)",
138
+ "case \\.apiKeyEnv: \"API key \\(env var\\)\"",
139
+ "case apikey = \"apiKey\"",
140
+ "\"gateway\\.remote\\.password\"",
141
+ "\"gateway\\.auth\\.password\"",
142
+ "\"talk\\.apiKey\"",
143
+ "=== \"string\"",
144
+ "typeof remote\\?\\.password === \"string\""
145
+ ]
146
+ }
147
+ ],
148
+ "results": {
149
+ ".env.example": [
150
+ {
151
+ "type": "Twilio API Key",
152
+ "filename": ".env.example",
153
+ "hashed_secret": "3c7206eff845bc69cf12d904d0f95f9aec15535e",
154
+ "is_verified": false,
155
+ "line_number": 2
156
+ }
157
+ ],
158
+ "appcast.xml": [
159
+ {
160
+ "type": "Base64 High Entropy String",
161
+ "filename": "appcast.xml",
162
+ "hashed_secret": "4e5f0a148d9ef42afeb73b1c77643e2ef2dee0b9",
163
+ "is_verified": false,
164
+ "line_number": 90
165
+ },
166
+ {
167
+ "type": "Base64 High Entropy String",
168
+ "filename": "appcast.xml",
169
+ "hashed_secret": "f1ccdaf78c308ec2cf608818da13f5f1e4809ed1",
170
+ "is_verified": false,
171
+ "line_number": 138
172
+ },
173
+ {
174
+ "type": "Base64 High Entropy String",
175
+ "filename": "appcast.xml",
176
+ "hashed_secret": "2691dc9c9ded92ba62a2d8ee589e2d78e2aa0479",
177
+ "is_verified": false,
178
+ "line_number": 212
179
+ }
180
+ ],
181
+ "apps/macos/Tests/ClawdbotIPCTests/AnthropicAuthResolverTests.swift": [
182
+ {
183
+ "type": "Secret Keyword",
184
+ "filename": "apps/macos/Tests/ClawdbotIPCTests/AnthropicAuthResolverTests.swift",
185
+ "hashed_secret": "e761624445731fcb8b15da94343c6b92e507d190",
186
+ "is_verified": false,
187
+ "line_number": 26
188
+ },
189
+ {
190
+ "type": "Secret Keyword",
191
+ "filename": "apps/macos/Tests/ClawdbotIPCTests/AnthropicAuthResolverTests.swift",
192
+ "hashed_secret": "a23c8630c8a5fbaa21f095e0269c135c20d21689",
193
+ "is_verified": false,
194
+ "line_number": 42
195
+ }
196
+ ],
197
+ "apps/macos/Tests/ClawdbotIPCTests/GatewayEndpointStoreTests.swift": [
198
+ {
199
+ "type": "Secret Keyword",
200
+ "filename": "apps/macos/Tests/ClawdbotIPCTests/GatewayEndpointStoreTests.swift",
201
+ "hashed_secret": "19dad5cecb110281417d1db56b60e1b006d55bb4",
202
+ "is_verified": false,
203
+ "line_number": 61
204
+ }
205
+ ],
206
+ "apps/macos/Tests/ClawdbotIPCTests/GatewayLaunchAgentManagerTests.swift": [
207
+ {
208
+ "type": "Secret Keyword",
209
+ "filename": "apps/macos/Tests/ClawdbotIPCTests/GatewayLaunchAgentManagerTests.swift",
210
+ "hashed_secret": "1a91d62f7ca67399625a4368a6ab5d4a3baa6073",
211
+ "is_verified": false,
212
+ "line_number": 13
213
+ }
214
+ ],
215
+ "apps/macos/Tests/ClawdbotIPCTests/TailscaleIntegrationSectionTests.swift": [
216
+ {
217
+ "type": "Secret Keyword",
218
+ "filename": "apps/macos/Tests/ClawdbotIPCTests/TailscaleIntegrationSectionTests.swift",
219
+ "hashed_secret": "e5e9fa1ba31ecd1ae84f75caaa474f3a663f05f4",
220
+ "is_verified": false,
221
+ "line_number": 27
222
+ }
223
+ ],
224
+ "apps/shared/ClawdbotKit/Sources/ClawdbotKit/GatewayChannel.swift": [
225
+ {
226
+ "type": "Secret Keyword",
227
+ "filename": "apps/shared/ClawdbotKit/Sources/ClawdbotKit/GatewayChannel.swift",
228
+ "hashed_secret": "5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8",
229
+ "is_verified": false,
230
+ "line_number": 100
231
+ }
232
+ ],
233
+ "docs/brave-search.md": [
234
+ {
235
+ "type": "Secret Keyword",
236
+ "filename": "docs/brave-search.md",
237
+ "hashed_secret": "491d458f895b9213facb2ee9375b1b044eaea3ac",
238
+ "is_verified": false,
239
+ "line_number": 26
240
+ }
241
+ ],
242
+ "docs/channels/bluebubbles.md": [
243
+ {
244
+ "type": "Secret Keyword",
245
+ "filename": "docs/channels/bluebubbles.md",
246
+ "hashed_secret": "555da20df20d4172e00f1b73d7c3943802055270",
247
+ "is_verified": false,
248
+ "line_number": 32
249
+ }
250
+ ],
251
+ "docs/channels/matrix.md": [
252
+ {
253
+ "type": "Secret Keyword",
254
+ "filename": "docs/channels/matrix.md",
255
+ "hashed_secret": "45d676e7c6ab44cf4b8fa366ef2d8fccd3e6d6e6",
256
+ "is_verified": false,
257
+ "line_number": 58
258
+ }
259
+ ],
260
+ "docs/channels/nextcloud-talk.md": [
261
+ {
262
+ "type": "Secret Keyword",
263
+ "filename": "docs/channels/nextcloud-talk.md",
264
+ "hashed_secret": "76ed0a056aa77060de25754586440cff390791d0",
265
+ "is_verified": false,
266
+ "line_number": 47
267
+ }
268
+ ],
269
+ "docs/channels/nostr.md": [
270
+ {
271
+ "type": "Secret Keyword",
272
+ "filename": "docs/channels/nostr.md",
273
+ "hashed_secret": "edeb23e25a619c434d22bb7f1c3ca4841166b4e8",
274
+ "is_verified": false,
275
+ "line_number": 65
276
+ }
277
+ ],
278
+ "docs/channels/slack.md": [
279
+ {
280
+ "type": "Secret Keyword",
281
+ "filename": "docs/channels/slack.md",
282
+ "hashed_secret": "3f4800fb7c1fb79a9a48bfd562d90bc6b2e2b718",
283
+ "is_verified": false,
284
+ "line_number": 141
285
+ }
286
+ ],
287
+ "docs/concepts/memory.md": [
288
+ {
289
+ "type": "Secret Keyword",
290
+ "filename": "docs/concepts/memory.md",
291
+ "hashed_secret": "39d711243bfcee9fec8299b204e1aa9c3430fa12",
292
+ "is_verified": false,
293
+ "line_number": 108
294
+ },
295
+ {
296
+ "type": "Secret Keyword",
297
+ "filename": "docs/concepts/memory.md",
298
+ "hashed_secret": "1a8abbf465c52363ab4c9c6ad945b8e857cbea55",
299
+ "is_verified": false,
300
+ "line_number": 131
301
+ },
302
+ {
303
+ "type": "Secret Keyword",
304
+ "filename": "docs/concepts/memory.md",
305
+ "hashed_secret": "b9f640d6095b9f6b5a65983f7b76dbbb254e0044",
306
+ "is_verified": false,
307
+ "line_number": 373
308
+ }
309
+ ],
310
+ "docs/concepts/model-providers.md": [
311
+ {
312
+ "type": "Secret Keyword",
313
+ "filename": "docs/concepts/model-providers.md",
314
+ "hashed_secret": "ec3810e10fb78db55ce38b9c18d1c3eb1db739e0",
315
+ "is_verified": false,
316
+ "line_number": 168
317
+ },
318
+ {
319
+ "type": "Secret Keyword",
320
+ "filename": "docs/concepts/model-providers.md",
321
+ "hashed_secret": "ef83ad68b9b66e008727b7c417c6a8f618b5177e",
322
+ "is_verified": false,
323
+ "line_number": 255
324
+ }
325
+ ],
326
+ "docs/environment.md": [
327
+ {
328
+ "type": "Secret Keyword",
329
+ "filename": "docs/environment.md",
330
+ "hashed_secret": "a219d7693c25cd2d93313512e200ff3eb374d281",
331
+ "is_verified": false,
332
+ "line_number": 29
333
+ },
334
+ {
335
+ "type": "Secret Keyword",
336
+ "filename": "docs/environment.md",
337
+ "hashed_secret": "b6f56e5e92078ed7c078c46fbfeedcbe5719bc25",
338
+ "is_verified": false,
339
+ "line_number": 31
340
+ }
341
+ ],
342
+ "docs/gateway/configuration-examples.md": [
343
+ {
344
+ "type": "Secret Keyword",
345
+ "filename": "docs/gateway/configuration-examples.md",
346
+ "hashed_secret": "a219d7693c25cd2d93313512e200ff3eb374d281",
347
+ "is_verified": false,
348
+ "line_number": 53
349
+ },
350
+ {
351
+ "type": "Secret Keyword",
352
+ "filename": "docs/gateway/configuration-examples.md",
353
+ "hashed_secret": "b6f56e5e92078ed7c078c46fbfeedcbe5719bc25",
354
+ "is_verified": false,
355
+ "line_number": 55
356
+ },
357
+ {
358
+ "type": "Secret Keyword",
359
+ "filename": "docs/gateway/configuration-examples.md",
360
+ "hashed_secret": "22af290a1a3d5e941193a41a3d3a9e4ca8da5e27",
361
+ "is_verified": false,
362
+ "line_number": 319
363
+ },
364
+ {
365
+ "type": "Secret Keyword",
366
+ "filename": "docs/gateway/configuration-examples.md",
367
+ "hashed_secret": "c1e6ee547fd492df1441ac492e8bb294974712bd",
368
+ "is_verified": false,
369
+ "line_number": 414
370
+ },
371
+ {
372
+ "type": "Secret Keyword",
373
+ "filename": "docs/gateway/configuration-examples.md",
374
+ "hashed_secret": "16c249e04e2be318050cb883c40137361c0c7209",
375
+ "is_verified": false,
376
+ "line_number": 548
377
+ }
378
+ ],
379
+ "docs/gateway/configuration.md": [
380
+ {
381
+ "type": "Secret Keyword",
382
+ "filename": "docs/gateway/configuration.md",
383
+ "hashed_secret": "a219d7693c25cd2d93313512e200ff3eb374d281",
384
+ "is_verified": false,
385
+ "line_number": 272
386
+ },
387
+ {
388
+ "type": "Secret Keyword",
389
+ "filename": "docs/gateway/configuration.md",
390
+ "hashed_secret": "b6f56e5e92078ed7c078c46fbfeedcbe5719bc25",
391
+ "is_verified": false,
392
+ "line_number": 274
393
+ },
394
+ {
395
+ "type": "Secret Keyword",
396
+ "filename": "docs/gateway/configuration.md",
397
+ "hashed_secret": "e5e9fa1ba31ecd1ae84f75caaa474f3a663f05f4",
398
+ "is_verified": false,
399
+ "line_number": 1029
400
+ },
401
+ {
402
+ "type": "Secret Keyword",
403
+ "filename": "docs/gateway/configuration.md",
404
+ "hashed_secret": "1188d5a8ed7edcff5144a9472af960243eacf12e",
405
+ "is_verified": false,
406
+ "line_number": 1470
407
+ },
408
+ {
409
+ "type": "Secret Keyword",
410
+ "filename": "docs/gateway/configuration.md",
411
+ "hashed_secret": "bde4db9b4c3be4049adc3b9a69851d7c35119770",
412
+ "is_verified": false,
413
+ "line_number": 1486
414
+ },
415
+ {
416
+ "type": "Secret Keyword",
417
+ "filename": "docs/gateway/configuration.md",
418
+ "hashed_secret": "22af290a1a3d5e941193a41a3d3a9e4ca8da5e27",
419
+ "is_verified": false,
420
+ "line_number": 2268
421
+ },
422
+ {
423
+ "type": "Secret Keyword",
424
+ "filename": "docs/gateway/configuration.md",
425
+ "hashed_secret": "ec3810e10fb78db55ce38b9c18d1c3eb1db739e0",
426
+ "is_verified": false,
427
+ "line_number": 2344
428
+ },
429
+ {
430
+ "type": "Secret Keyword",
431
+ "filename": "docs/gateway/configuration.md",
432
+ "hashed_secret": "c1e6ee547fd492df1441ac492e8bb294974712bd",
433
+ "is_verified": false,
434
+ "line_number": 2658
435
+ },
436
+ {
437
+ "type": "Secret Keyword",
438
+ "filename": "docs/gateway/configuration.md",
439
+ "hashed_secret": "45d676e7c6ab44cf4b8fa366ef2d8fccd3e6d6e6",
440
+ "is_verified": false,
441
+ "line_number": 2844
442
+ }
443
+ ],
444
+ "docs/gateway/local-models.md": [
445
+ {
446
+ "type": "Secret Keyword",
447
+ "filename": "docs/gateway/local-models.md",
448
+ "hashed_secret": "16c249e04e2be318050cb883c40137361c0c7209",
449
+ "is_verified": false,
450
+ "line_number": 32
451
+ },
452
+ {
453
+ "type": "Secret Keyword",
454
+ "filename": "docs/gateway/local-models.md",
455
+ "hashed_secret": "49fd535e63175a827aab3eff9ac58a9e82460ac9",
456
+ "is_verified": false,
457
+ "line_number": 121
458
+ }
459
+ ],
460
+ "docs/gateway/tailscale.md": [
461
+ {
462
+ "type": "Secret Keyword",
463
+ "filename": "docs/gateway/tailscale.md",
464
+ "hashed_secret": "9cb0dc5383312aa15b9dc6745645bde18ff5ade9",
465
+ "is_verified": false,
466
+ "line_number": 75
467
+ }
468
+ ],
469
+ "docs/help/faq.md": [
470
+ {
471
+ "type": "Secret Keyword",
472
+ "filename": "docs/help/faq.md",
473
+ "hashed_secret": "491d458f895b9213facb2ee9375b1b044eaea3ac",
474
+ "is_verified": false,
475
+ "line_number": 925
476
+ },
477
+ {
478
+ "type": "Secret Keyword",
479
+ "filename": "docs/help/faq.md",
480
+ "hashed_secret": "a219d7693c25cd2d93313512e200ff3eb374d281",
481
+ "is_verified": false,
482
+ "line_number": 1113
483
+ },
484
+ {
485
+ "type": "Secret Keyword",
486
+ "filename": "docs/help/faq.md",
487
+ "hashed_secret": "b6f56e5e92078ed7c078c46fbfeedcbe5719bc25",
488
+ "is_verified": false,
489
+ "line_number": 1114
490
+ },
491
+ {
492
+ "type": "Secret Keyword",
493
+ "filename": "docs/help/faq.md",
494
+ "hashed_secret": "ec3810e10fb78db55ce38b9c18d1c3eb1db739e0",
495
+ "is_verified": false,
496
+ "line_number": 1439
497
+ },
498
+ {
499
+ "type": "Secret Keyword",
500
+ "filename": "docs/help/faq.md",
501
+ "hashed_secret": "45d676e7c6ab44cf4b8fa366ef2d8fccd3e6d6e6",
502
+ "is_verified": false,
503
+ "line_number": 1715
504
+ }
505
+ ],
506
+ "docs/nodes/talk.md": [
507
+ {
508
+ "type": "Secret Keyword",
509
+ "filename": "docs/nodes/talk.md",
510
+ "hashed_secret": "1188d5a8ed7edcff5144a9472af960243eacf12e",
511
+ "is_verified": false,
512
+ "line_number": 50
513
+ }
514
+ ],
515
+ "docs/perplexity.md": [
516
+ {
517
+ "type": "Secret Keyword",
518
+ "filename": "docs/perplexity.md",
519
+ "hashed_secret": "6b26c117c66a0c030e239eef595c1e18865132a8",
520
+ "is_verified": false,
521
+ "line_number": 35
522
+ }
523
+ ],
524
+ "docs/providers/anthropic.md": [
525
+ {
526
+ "type": "Secret Keyword",
527
+ "filename": "docs/providers/anthropic.md",
528
+ "hashed_secret": "c7a8c334eef5d1749fface7d42c66f9ae5e8cf36",
529
+ "is_verified": false,
530
+ "line_number": 32
531
+ }
532
+ ],
533
+ "docs/providers/glm.md": [
534
+ {
535
+ "type": "Secret Keyword",
536
+ "filename": "docs/providers/glm.md",
537
+ "hashed_secret": "ec3810e10fb78db55ce38b9c18d1c3eb1db739e0",
538
+ "is_verified": false,
539
+ "line_number": 22
540
+ }
541
+ ],
542
+ "docs/providers/minimax.md": [
543
+ {
544
+ "type": "Secret Keyword",
545
+ "filename": "docs/providers/minimax.md",
546
+ "hashed_secret": "ec3810e10fb78db55ce38b9c18d1c3eb1db739e0",
547
+ "is_verified": false,
548
+ "line_number": 49
549
+ },
550
+ {
551
+ "type": "Secret Keyword",
552
+ "filename": "docs/providers/minimax.md",
553
+ "hashed_secret": "16c249e04e2be318050cb883c40137361c0c7209",
554
+ "is_verified": false,
555
+ "line_number": 118
556
+ }
557
+ ],
558
+ "docs/providers/moonshot.md": [
559
+ {
560
+ "type": "Secret Keyword",
561
+ "filename": "docs/providers/moonshot.md",
562
+ "hashed_secret": "ec3810e10fb78db55ce38b9c18d1c3eb1db739e0",
563
+ "is_verified": false,
564
+ "line_number": 39
565
+ }
566
+ ],
567
+ "docs/providers/openai.md": [
568
+ {
569
+ "type": "Secret Keyword",
570
+ "filename": "docs/providers/openai.md",
571
+ "hashed_secret": "ec3810e10fb78db55ce38b9c18d1c3eb1db739e0",
572
+ "is_verified": false,
573
+ "line_number": 31
574
+ }
575
+ ],
576
+ "docs/providers/opencode.md": [
577
+ {
578
+ "type": "Secret Keyword",
579
+ "filename": "docs/providers/opencode.md",
580
+ "hashed_secret": "ec3810e10fb78db55ce38b9c18d1c3eb1db739e0",
581
+ "is_verified": false,
582
+ "line_number": 25
583
+ }
584
+ ],
585
+ "docs/providers/openrouter.md": [
586
+ {
587
+ "type": "Secret Keyword",
588
+ "filename": "docs/providers/openrouter.md",
589
+ "hashed_secret": "a219d7693c25cd2d93313512e200ff3eb374d281",
590
+ "is_verified": false,
591
+ "line_number": 22
592
+ }
593
+ ],
594
+ "docs/providers/synthetic.md": [
595
+ {
596
+ "type": "Secret Keyword",
597
+ "filename": "docs/providers/synthetic.md",
598
+ "hashed_secret": "ec3810e10fb78db55ce38b9c18d1c3eb1db739e0",
599
+ "is_verified": false,
600
+ "line_number": 31
601
+ }
602
+ ],
603
+ "docs/providers/zai.md": [
604
+ {
605
+ "type": "Secret Keyword",
606
+ "filename": "docs/providers/zai.md",
607
+ "hashed_secret": "ec3810e10fb78db55ce38b9c18d1c3eb1db739e0",
608
+ "is_verified": false,
609
+ "line_number": 25
610
+ }
611
+ ],
612
+ "docs/tools/browser.md": [
613
+ {
614
+ "type": "Basic Auth Credentials",
615
+ "filename": "docs/tools/browser.md",
616
+ "hashed_secret": "9d4e1e23bd5b727046a9e3b4b7db57bd8d6ee684",
617
+ "is_verified": false,
618
+ "line_number": 163
619
+ }
620
+ ],
621
+ "docs/tools/firecrawl.md": [
622
+ {
623
+ "type": "Secret Keyword",
624
+ "filename": "docs/tools/firecrawl.md",
625
+ "hashed_secret": "674397e2c0c2faaa85961c708d2a96a7cc7af217",
626
+ "is_verified": false,
627
+ "line_number": 28
628
+ }
629
+ ],
630
+ "docs/tools/skills-config.md": [
631
+ {
632
+ "type": "Secret Keyword",
633
+ "filename": "docs/tools/skills-config.md",
634
+ "hashed_secret": "c1e6ee547fd492df1441ac492e8bb294974712bd",
635
+ "is_verified": false,
636
+ "line_number": 30
637
+ }
638
+ ],
639
+ "docs/tools/skills.md": [
640
+ {
641
+ "type": "Secret Keyword",
642
+ "filename": "docs/tools/skills.md",
643
+ "hashed_secret": "c1e6ee547fd492df1441ac492e8bb294974712bd",
644
+ "is_verified": false,
645
+ "line_number": 160
646
+ }
647
+ ],
648
+ "docs/tools/web.md": [
649
+ {
650
+ "type": "Secret Keyword",
651
+ "filename": "docs/tools/web.md",
652
+ "hashed_secret": "6b26c117c66a0c030e239eef595c1e18865132a8",
653
+ "is_verified": false,
654
+ "line_number": 61
655
+ },
656
+ {
657
+ "type": "Secret Keyword",
658
+ "filename": "docs/tools/web.md",
659
+ "hashed_secret": "96c682c88ed551f22fe76d206c2dfb7df9221ad9",
660
+ "is_verified": false,
661
+ "line_number": 112
662
+ },
663
+ {
664
+ "type": "Secret Keyword",
665
+ "filename": "docs/tools/web.md",
666
+ "hashed_secret": "491d458f895b9213facb2ee9375b1b044eaea3ac",
667
+ "is_verified": false,
668
+ "line_number": 160
669
+ },
670
+ {
671
+ "type": "Secret Keyword",
672
+ "filename": "docs/tools/web.md",
673
+ "hashed_secret": "674397e2c0c2faaa85961c708d2a96a7cc7af217",
674
+ "is_verified": false,
675
+ "line_number": 223
676
+ }
677
+ ],
678
+ "docs/tts.md": [
679
+ {
680
+ "type": "Secret Keyword",
681
+ "filename": "docs/tts.md",
682
+ "hashed_secret": "bde4db9b4c3be4049adc3b9a69851d7c35119770",
683
+ "is_verified": false,
684
+ "line_number": 72
685
+ },
686
+ {
687
+ "type": "Secret Keyword",
688
+ "filename": "docs/tts.md",
689
+ "hashed_secret": "1188d5a8ed7edcff5144a9472af960243eacf12e",
690
+ "is_verified": false,
691
+ "line_number": 77
692
+ }
693
+ ],
694
+ "extensions/bluebubbles/src/actions.test.ts": [
695
+ {
696
+ "type": "Secret Keyword",
697
+ "filename": "extensions/bluebubbles/src/actions.test.ts",
698
+ "hashed_secret": "789cbe0407840b1c2041cb33452ff60f19bf58cc",
699
+ "is_verified": false,
700
+ "line_number": 73
701
+ }
702
+ ],
703
+ "extensions/bluebubbles/src/attachments.test.ts": [
704
+ {
705
+ "type": "Secret Keyword",
706
+ "filename": "extensions/bluebubbles/src/attachments.test.ts",
707
+ "hashed_secret": "789cbe0407840b1c2041cb33452ff60f19bf58cc",
708
+ "is_verified": false,
709
+ "line_number": 35
710
+ },
711
+ {
712
+ "type": "Secret Keyword",
713
+ "filename": "extensions/bluebubbles/src/attachments.test.ts",
714
+ "hashed_secret": "db1530e1ea43af094d3d75b8dbaf19a4a182a318",
715
+ "is_verified": false,
716
+ "line_number": 99
717
+ },
718
+ {
719
+ "type": "Secret Keyword",
720
+ "filename": "extensions/bluebubbles/src/attachments.test.ts",
721
+ "hashed_secret": "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3",
722
+ "is_verified": false,
723
+ "line_number": 117
724
+ },
725
+ {
726
+ "type": "Secret Keyword",
727
+ "filename": "extensions/bluebubbles/src/attachments.test.ts",
728
+ "hashed_secret": "052f076c732648ab32d2fcde9fe255319bfa0c7b",
729
+ "is_verified": false,
730
+ "line_number": 229
731
+ }
732
+ ],
733
+ "extensions/bluebubbles/src/chat.test.ts": [
734
+ {
735
+ "type": "Secret Keyword",
736
+ "filename": "extensions/bluebubbles/src/chat.test.ts",
737
+ "hashed_secret": "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3",
738
+ "is_verified": false,
739
+ "line_number": 33
740
+ },
741
+ {
742
+ "type": "Secret Keyword",
743
+ "filename": "extensions/bluebubbles/src/chat.test.ts",
744
+ "hashed_secret": "789cbe0407840b1c2041cb33452ff60f19bf58cc",
745
+ "is_verified": false,
746
+ "line_number": 68
747
+ },
748
+ {
749
+ "type": "Secret Keyword",
750
+ "filename": "extensions/bluebubbles/src/chat.test.ts",
751
+ "hashed_secret": "5c5a15a8b0b3e154d77746945e563ba40100681b",
752
+ "is_verified": false,
753
+ "line_number": 85
754
+ },
755
+ {
756
+ "type": "Secret Keyword",
757
+ "filename": "extensions/bluebubbles/src/chat.test.ts",
758
+ "hashed_secret": "faacad0ce4ea1c19b46e128fd79679d37d3d331d",
759
+ "is_verified": false,
760
+ "line_number": 134
761
+ },
762
+ {
763
+ "type": "Secret Keyword",
764
+ "filename": "extensions/bluebubbles/src/chat.test.ts",
765
+ "hashed_secret": "4dcc26a1d99532846fedf1265df4f40f4e0005b8",
766
+ "is_verified": false,
767
+ "line_number": 219
768
+ },
769
+ {
770
+ "type": "Secret Keyword",
771
+ "filename": "extensions/bluebubbles/src/chat.test.ts",
772
+ "hashed_secret": "fd2a721f7be1ee3d691a011affcdb11d0ca365a8",
773
+ "is_verified": false,
774
+ "line_number": 282
775
+ }
776
+ ],
777
+ "extensions/bluebubbles/src/monitor.test.ts": [
778
+ {
779
+ "type": "Secret Keyword",
780
+ "filename": "extensions/bluebubbles/src/monitor.test.ts",
781
+ "hashed_secret": "789cbe0407840b1c2041cb33452ff60f19bf58cc",
782
+ "is_verified": false,
783
+ "line_number": 187
784
+ },
785
+ {
786
+ "type": "Secret Keyword",
787
+ "filename": "extensions/bluebubbles/src/monitor.test.ts",
788
+ "hashed_secret": "1ae0af3fe72b3ba394f9fa95a6cffc090d726c23",
789
+ "is_verified": false,
790
+ "line_number": 394
791
+ }
792
+ ],
793
+ "extensions/bluebubbles/src/reactions.test.ts": [
794
+ {
795
+ "type": "Secret Keyword",
796
+ "filename": "extensions/bluebubbles/src/reactions.test.ts",
797
+ "hashed_secret": "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3",
798
+ "is_verified": false,
799
+ "line_number": 38
800
+ },
801
+ {
802
+ "type": "Secret Keyword",
803
+ "filename": "extensions/bluebubbles/src/reactions.test.ts",
804
+ "hashed_secret": "789cbe0407840b1c2041cb33452ff60f19bf58cc",
805
+ "is_verified": false,
806
+ "line_number": 179
807
+ },
808
+ {
809
+ "type": "Secret Keyword",
810
+ "filename": "extensions/bluebubbles/src/reactions.test.ts",
811
+ "hashed_secret": "a4a05c9a6449eb9d6cdac81dd7edc49230e327e6",
812
+ "is_verified": false,
813
+ "line_number": 210
814
+ },
815
+ {
816
+ "type": "Secret Keyword",
817
+ "filename": "extensions/bluebubbles/src/reactions.test.ts",
818
+ "hashed_secret": "a2833da9f0a16f09994754d0a31749cecf8c8c77",
819
+ "is_verified": false,
820
+ "line_number": 316
821
+ }
822
+ ],
823
+ "extensions/bluebubbles/src/send.test.ts": [
824
+ {
825
+ "type": "Secret Keyword",
826
+ "filename": "extensions/bluebubbles/src/send.test.ts",
827
+ "hashed_secret": "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3",
828
+ "is_verified": false,
829
+ "line_number": 38
830
+ },
831
+ {
832
+ "type": "Secret Keyword",
833
+ "filename": "extensions/bluebubbles/src/send.test.ts",
834
+ "hashed_secret": "faacad0ce4ea1c19b46e128fd79679d37d3d331d",
835
+ "is_verified": false,
836
+ "line_number": 675
837
+ }
838
+ ],
839
+ "extensions/bluebubbles/src/targets.test.ts": [
840
+ {
841
+ "type": "Hex High Entropy String",
842
+ "filename": "extensions/bluebubbles/src/targets.test.ts",
843
+ "hashed_secret": "a3af2fb0c1e2a30bb038049e1e4b401593af6225",
844
+ "is_verified": false,
845
+ "line_number": 62
846
+ }
847
+ ],
848
+ "extensions/bluebubbles/src/targets.ts": [
849
+ {
850
+ "type": "Hex High Entropy String",
851
+ "filename": "extensions/bluebubbles/src/targets.ts",
852
+ "hashed_secret": "a3af2fb0c1e2a30bb038049e1e4b401593af6225",
853
+ "is_verified": false,
854
+ "line_number": 214
855
+ }
856
+ ],
857
+ "extensions/copilot-proxy/index.ts": [
858
+ {
859
+ "type": "Secret Keyword",
860
+ "filename": "extensions/copilot-proxy/index.ts",
861
+ "hashed_secret": "50f013532a9770a2c2cfdc38b7581dd01df69b70",
862
+ "is_verified": false,
863
+ "line_number": 4
864
+ }
865
+ ],
866
+ "extensions/google-antigravity-auth/index.ts": [
867
+ {
868
+ "type": "Base64 High Entropy String",
869
+ "filename": "extensions/google-antigravity-auth/index.ts",
870
+ "hashed_secret": "709d0f232b6ac4f8d24dec3e4fabfdb14257174f",
871
+ "is_verified": false,
872
+ "line_number": 9
873
+ }
874
+ ],
875
+ "extensions/matrix/src/matrix/accounts.test.ts": [
876
+ {
877
+ "type": "Secret Keyword",
878
+ "filename": "extensions/matrix/src/matrix/accounts.test.ts",
879
+ "hashed_secret": "e5e9fa1ba31ecd1ae84f75caaa474f3a663f05f4",
880
+ "is_verified": false,
881
+ "line_number": 75
882
+ }
883
+ ],
884
+ "extensions/matrix/src/matrix/client.test.ts": [
885
+ {
886
+ "type": "Secret Keyword",
887
+ "filename": "extensions/matrix/src/matrix/client.test.ts",
888
+ "hashed_secret": "fe7fcdaea49ece14677acd32374d2f1225819d5c",
889
+ "is_verified": false,
890
+ "line_number": 14
891
+ },
892
+ {
893
+ "type": "Secret Keyword",
894
+ "filename": "extensions/matrix/src/matrix/client.test.ts",
895
+ "hashed_secret": "3dc927d80543dc0f643940b70d066bd4b4c4b78e",
896
+ "is_verified": false,
897
+ "line_number": 24
898
+ }
899
+ ],
900
+ "extensions/matrix/src/matrix/client/storage.ts": [
901
+ {
902
+ "type": "Secret Keyword",
903
+ "filename": "extensions/matrix/src/matrix/client/storage.ts",
904
+ "hashed_secret": "7505d64a54e061b7acd54ccd58b49dc43500b635",
905
+ "is_verified": false,
906
+ "line_number": 9
907
+ }
908
+ ],
909
+ "extensions/memory-lancedb/config.ts": [
910
+ {
911
+ "type": "Secret Keyword",
912
+ "filename": "extensions/memory-lancedb/config.ts",
913
+ "hashed_secret": "ecb252044b5ea0f679ee78ec1a12904739e2904d",
914
+ "is_verified": false,
915
+ "line_number": 70
916
+ }
917
+ ],
918
+ "extensions/memory-lancedb/index.test.ts": [
919
+ {
920
+ "type": "Secret Keyword",
921
+ "filename": "extensions/memory-lancedb/index.test.ts",
922
+ "hashed_secret": "ed65c049bb2f78ee4f703b2158ba9cc6ea31fb7e",
923
+ "is_verified": false,
924
+ "line_number": 70
925
+ }
926
+ ],
927
+ "extensions/msteams/src/probe.test.ts": [
928
+ {
929
+ "type": "Secret Keyword",
930
+ "filename": "extensions/msteams/src/probe.test.ts",
931
+ "hashed_secret": "1a91d62f7ca67399625a4368a6ab5d4a3baa6073",
932
+ "is_verified": false,
933
+ "line_number": 34
934
+ }
935
+ ],
936
+ "extensions/nextcloud-talk/src/accounts.ts": [
937
+ {
938
+ "type": "Secret Keyword",
939
+ "filename": "extensions/nextcloud-talk/src/accounts.ts",
940
+ "hashed_secret": "920f8f5815b381ea692e9e7c2f7119f2b1aa620a",
941
+ "is_verified": false,
942
+ "line_number": 26
943
+ },
944
+ {
945
+ "type": "Secret Keyword",
946
+ "filename": "extensions/nextcloud-talk/src/accounts.ts",
947
+ "hashed_secret": "71f8e7976e4cbc4561c9d62fb283e7f788202acb",
948
+ "is_verified": false,
949
+ "line_number": 139
950
+ }
951
+ ],
952
+ "extensions/nextcloud-talk/src/channel.ts": [
953
+ {
954
+ "type": "Secret Keyword",
955
+ "filename": "extensions/nextcloud-talk/src/channel.ts",
956
+ "hashed_secret": "71f8e7976e4cbc4561c9d62fb283e7f788202acb",
957
+ "is_verified": false,
958
+ "line_number": 390
959
+ }
960
+ ],
961
+ "extensions/nostr/README.md": [
962
+ {
963
+ "type": "Secret Keyword",
964
+ "filename": "extensions/nostr/README.md",
965
+ "hashed_secret": "edeb23e25a619c434d22bb7f1c3ca4841166b4e8",
966
+ "is_verified": false,
967
+ "line_number": 43
968
+ }
969
+ ],
970
+ "extensions/nostr/src/channel.test.ts": [
971
+ {
972
+ "type": "Hex High Entropy String",
973
+ "filename": "extensions/nostr/src/channel.test.ts",
974
+ "hashed_secret": "ce4303f6b22257d9c9cf314ef1dee4707c6e1c13",
975
+ "is_verified": false,
976
+ "line_number": 48
977
+ },
978
+ {
979
+ "type": "Secret Keyword",
980
+ "filename": "extensions/nostr/src/channel.test.ts",
981
+ "hashed_secret": "ce4303f6b22257d9c9cf314ef1dee4707c6e1c13",
982
+ "is_verified": false,
983
+ "line_number": 48
984
+ }
985
+ ],
986
+ "extensions/nostr/src/nostr-bus.fuzz.test.ts": [
987
+ {
988
+ "type": "Hex High Entropy String",
989
+ "filename": "extensions/nostr/src/nostr-bus.fuzz.test.ts",
990
+ "hashed_secret": "2b4489606a23fb31fcdc849fa7e577ba90f6d39a",
991
+ "is_verified": false,
992
+ "line_number": 202
993
+ },
994
+ {
995
+ "type": "Hex High Entropy String",
996
+ "filename": "extensions/nostr/src/nostr-bus.fuzz.test.ts",
997
+ "hashed_secret": "ce4303f6b22257d9c9cf314ef1dee4707c6e1c13",
998
+ "is_verified": false,
999
+ "line_number": 203
1000
+ },
1001
+ {
1002
+ "type": "Hex High Entropy String",
1003
+ "filename": "extensions/nostr/src/nostr-bus.fuzz.test.ts",
1004
+ "hashed_secret": "b84cb0c3925d34496e6c8b0e55b8c1664a438035",
1005
+ "is_verified": false,
1006
+ "line_number": 208
1007
+ }
1008
+ ],
1009
+ "extensions/nostr/src/nostr-bus.test.ts": [
1010
+ {
1011
+ "type": "Hex High Entropy String",
1012
+ "filename": "extensions/nostr/src/nostr-bus.test.ts",
1013
+ "hashed_secret": "ce4303f6b22257d9c9cf314ef1dee4707c6e1c13",
1014
+ "is_verified": false,
1015
+ "line_number": 11
1016
+ },
1017
+ {
1018
+ "type": "Hex High Entropy String",
1019
+ "filename": "extensions/nostr/src/nostr-bus.test.ts",
1020
+ "hashed_secret": "7258e28563f03fb4c5994e8402e6f610d1f0f110",
1021
+ "is_verified": false,
1022
+ "line_number": 33
1023
+ },
1024
+ {
1025
+ "type": "Hex High Entropy String",
1026
+ "filename": "extensions/nostr/src/nostr-bus.test.ts",
1027
+ "hashed_secret": "2b4489606a23fb31fcdc849fa7e577ba90f6d39a",
1028
+ "is_verified": false,
1029
+ "line_number": 101
1030
+ },
1031
+ {
1032
+ "type": "Hex High Entropy String",
1033
+ "filename": "extensions/nostr/src/nostr-bus.test.ts",
1034
+ "hashed_secret": "ef717286343f6da3f4e6f68c6de02a5148a801c4",
1035
+ "is_verified": false,
1036
+ "line_number": 106
1037
+ },
1038
+ {
1039
+ "type": "Hex High Entropy String",
1040
+ "filename": "extensions/nostr/src/nostr-bus.test.ts",
1041
+ "hashed_secret": "98b35fe4c45011220f509ebb5546d3889b55a891",
1042
+ "is_verified": false,
1043
+ "line_number": 111
1044
+ }
1045
+ ],
1046
+ "extensions/nostr/src/nostr-profile.fuzz.test.ts": [
1047
+ {
1048
+ "type": "Hex High Entropy String",
1049
+ "filename": "extensions/nostr/src/nostr-profile.fuzz.test.ts",
1050
+ "hashed_secret": "ce4303f6b22257d9c9cf314ef1dee4707c6e1c13",
1051
+ "is_verified": false,
1052
+ "line_number": 12
1053
+ }
1054
+ ],
1055
+ "extensions/nostr/src/nostr-profile.test.ts": [
1056
+ {
1057
+ "type": "Hex High Entropy String",
1058
+ "filename": "extensions/nostr/src/nostr-profile.test.ts",
1059
+ "hashed_secret": "ce4303f6b22257d9c9cf314ef1dee4707c6e1c13",
1060
+ "is_verified": false,
1061
+ "line_number": 14
1062
+ }
1063
+ ],
1064
+ "extensions/nostr/src/types.test.ts": [
1065
+ {
1066
+ "type": "Hex High Entropy String",
1067
+ "filename": "extensions/nostr/src/types.test.ts",
1068
+ "hashed_secret": "ce4303f6b22257d9c9cf314ef1dee4707c6e1c13",
1069
+ "is_verified": false,
1070
+ "line_number": 8
1071
+ },
1072
+ {
1073
+ "type": "Secret Keyword",
1074
+ "filename": "extensions/nostr/src/types.test.ts",
1075
+ "hashed_secret": "ce4303f6b22257d9c9cf314ef1dee4707c6e1c13",
1076
+ "is_verified": false,
1077
+ "line_number": 8
1078
+ },
1079
+ {
1080
+ "type": "Secret Keyword",
1081
+ "filename": "extensions/nostr/src/types.test.ts",
1082
+ "hashed_secret": "3bee216ebc256d692260fc3adc765050508fef5e",
1083
+ "is_verified": false,
1084
+ "line_number": 127
1085
+ }
1086
+ ],
1087
+ "extensions/open-prose/skills/prose/SKILL.md": [
1088
+ {
1089
+ "type": "Basic Auth Credentials",
1090
+ "filename": "extensions/open-prose/skills/prose/SKILL.md",
1091
+ "hashed_secret": "9d4e1e23bd5b727046a9e3b4b7db57bd8d6ee684",
1092
+ "is_verified": false,
1093
+ "line_number": 200
1094
+ }
1095
+ ],
1096
+ "extensions/open-prose/skills/prose/state/postgres.md": [
1097
+ {
1098
+ "type": "Secret Keyword",
1099
+ "filename": "extensions/open-prose/skills/prose/state/postgres.md",
1100
+ "hashed_secret": "fa9beb99e4029ad5a6615399e7bbae21356086b3",
1101
+ "is_verified": false,
1102
+ "line_number": 75
1103
+ },
1104
+ {
1105
+ "type": "Basic Auth Credentials",
1106
+ "filename": "extensions/open-prose/skills/prose/state/postgres.md",
1107
+ "hashed_secret": "9d4e1e23bd5b727046a9e3b4b7db57bd8d6ee684",
1108
+ "is_verified": false,
1109
+ "line_number": 198
1110
+ }
1111
+ ],
1112
+ "extensions/zalo/README.md": [
1113
+ {
1114
+ "type": "Secret Keyword",
1115
+ "filename": "extensions/zalo/README.md",
1116
+ "hashed_secret": "f51aaee16a4a756d287f126b99c081b73cba7f15",
1117
+ "is_verified": false,
1118
+ "line_number": 41
1119
+ }
1120
+ ],
1121
+ "extensions/zalo/src/monitor.webhook.test.ts": [
1122
+ {
1123
+ "type": "Secret Keyword",
1124
+ "filename": "extensions/zalo/src/monitor.webhook.test.ts",
1125
+ "hashed_secret": "e5e9fa1ba31ecd1ae84f75caaa474f3a663f05f4",
1126
+ "is_verified": false,
1127
+ "line_number": 43
1128
+ }
1129
+ ],
1130
+ "skills/1password/references/cli-examples.md": [
1131
+ {
1132
+ "type": "Secret Keyword",
1133
+ "filename": "skills/1password/references/cli-examples.md",
1134
+ "hashed_secret": "9dda0987cc3054773a2df97e352d4f64d233ef10",
1135
+ "is_verified": false,
1136
+ "line_number": 17
1137
+ }
1138
+ ],
1139
+ "skills/local-places/SERVER_README.md": [
1140
+ {
1141
+ "type": "Secret Keyword",
1142
+ "filename": "skills/local-places/SERVER_README.md",
1143
+ "hashed_secret": "6d9c68c603e465077bdd49c62347fe54717f83a3",
1144
+ "is_verified": false,
1145
+ "line_number": 28
1146
+ }
1147
+ ],
1148
+ "skills/openai-whisper-api/SKILL.md": [
1149
+ {
1150
+ "type": "Secret Keyword",
1151
+ "filename": "skills/openai-whisper-api/SKILL.md",
1152
+ "hashed_secret": "1077361f94d70e1ddcc7c6dc581a489532a81d03",
1153
+ "is_verified": false,
1154
+ "line_number": 39
1155
+ }
1156
+ ],
1157
+ "skills/trello/SKILL.md": [
1158
+ {
1159
+ "type": "Secret Keyword",
1160
+ "filename": "skills/trello/SKILL.md",
1161
+ "hashed_secret": "11fa7c37d697f30e6aee828b4426a10f83ab2380",
1162
+ "is_verified": false,
1163
+ "line_number": 18
1164
+ }
1165
+ ],
1166
+ "src/agents/memory-search.test.ts": [
1167
+ {
1168
+ "type": "Secret Keyword",
1169
+ "filename": "src/agents/memory-search.test.ts",
1170
+ "hashed_secret": "a1b49d68a91fdf9c9217773f3fac988d77fa0f50",
1171
+ "is_verified": false,
1172
+ "line_number": 164
1173
+ }
1174
+ ],
1175
+ "src/agents/model-auth.test.ts": [
1176
+ {
1177
+ "type": "Secret Keyword",
1178
+ "filename": "src/agents/model-auth.test.ts",
1179
+ "hashed_secret": "07a6b9cec637c806195e8aa7e5c0851ab03dc35e",
1180
+ "is_verified": false,
1181
+ "line_number": 211
1182
+ },
1183
+ {
1184
+ "type": "Secret Keyword",
1185
+ "filename": "src/agents/model-auth.test.ts",
1186
+ "hashed_secret": "21f296583ccd80c5ab9b3330a8b0d47e4a409fb9",
1187
+ "is_verified": false,
1188
+ "line_number": 240
1189
+ },
1190
+ {
1191
+ "type": "Secret Keyword",
1192
+ "filename": "src/agents/model-auth.test.ts",
1193
+ "hashed_secret": "77e991e9f56e6fa4ed1a908208048421f1214c07",
1194
+ "is_verified": false,
1195
+ "line_number": 264
1196
+ },
1197
+ {
1198
+ "type": "Secret Keyword",
1199
+ "filename": "src/agents/model-auth.test.ts",
1200
+ "hashed_secret": "dff6d4ff5dc357cf451d1855ab9cbda562645c9f",
1201
+ "is_verified": false,
1202
+ "line_number": 295
1203
+ }
1204
+ ],
1205
+ "src/agents/model-auth.ts": [
1206
+ {
1207
+ "type": "Secret Keyword",
1208
+ "filename": "src/agents/model-auth.ts",
1209
+ "hashed_secret": "8956265d216d474a080edaa97880d37fc1386f33",
1210
+ "is_verified": false,
1211
+ "line_number": 22
1212
+ }
1213
+ ],
1214
+ "src/agents/models-config.auto-injects-github-copilot-provider-token-is.test.ts": [
1215
+ {
1216
+ "type": "Secret Keyword",
1217
+ "filename": "src/agents/models-config.auto-injects-github-copilot-provider-token-is.test.ts",
1218
+ "hashed_secret": "7cf31e8b6cda49f70c31f1f25af05d46f924142d",
1219
+ "is_verified": false,
1220
+ "line_number": 16
1221
+ }
1222
+ ],
1223
+ "src/agents/models-config.falls-back-default-baseurl-token-exchange-fails.test.ts": [
1224
+ {
1225
+ "type": "Secret Keyword",
1226
+ "filename": "src/agents/models-config.falls-back-default-baseurl-token-exchange-fails.test.ts",
1227
+ "hashed_secret": "7cf31e8b6cda49f70c31f1f25af05d46f924142d",
1228
+ "is_verified": false,
1229
+ "line_number": 16
1230
+ }
1231
+ ],
1232
+ "src/agents/models-config.fills-missing-provider-apikey-from-env-var.test.ts": [
1233
+ {
1234
+ "type": "Secret Keyword",
1235
+ "filename": "src/agents/models-config.fills-missing-provider-apikey-from-env-var.test.ts",
1236
+ "hashed_secret": "7cf31e8b6cda49f70c31f1f25af05d46f924142d",
1237
+ "is_verified": false,
1238
+ "line_number": 16
1239
+ },
1240
+ {
1241
+ "type": "Secret Keyword",
1242
+ "filename": "src/agents/models-config.fills-missing-provider-apikey-from-env-var.test.ts",
1243
+ "hashed_secret": "fcdd655b11f33ba4327695084a347b2ba192976c",
1244
+ "is_verified": false,
1245
+ "line_number": 50
1246
+ },
1247
+ {
1248
+ "type": "Secret Keyword",
1249
+ "filename": "src/agents/models-config.fills-missing-provider-apikey-from-env-var.test.ts",
1250
+ "hashed_secret": "3a81eb091f80c845232225be5663d270e90dacb7",
1251
+ "is_verified": false,
1252
+ "line_number": 108
1253
+ }
1254
+ ],
1255
+ "src/agents/models-config.normalizes-gemini-3-ids-preview-google-providers.test.ts": [
1256
+ {
1257
+ "type": "Secret Keyword",
1258
+ "filename": "src/agents/models-config.normalizes-gemini-3-ids-preview-google-providers.test.ts",
1259
+ "hashed_secret": "7cf31e8b6cda49f70c31f1f25af05d46f924142d",
1260
+ "is_verified": false,
1261
+ "line_number": 16
1262
+ },
1263
+ {
1264
+ "type": "Secret Keyword",
1265
+ "filename": "src/agents/models-config.normalizes-gemini-3-ids-preview-google-providers.test.ts",
1266
+ "hashed_secret": "980d02eb9335ae7c9e9984f6c8ad432352a0d2ac",
1267
+ "is_verified": false,
1268
+ "line_number": 57
1269
+ }
1270
+ ],
1271
+ "src/agents/models-config.skips-writing-models-json-no-env-token.test.ts": [
1272
+ {
1273
+ "type": "Secret Keyword",
1274
+ "filename": "src/agents/models-config.skips-writing-models-json-no-env-token.test.ts",
1275
+ "hashed_secret": "7cf31e8b6cda49f70c31f1f25af05d46f924142d",
1276
+ "is_verified": false,
1277
+ "line_number": 16
1278
+ },
1279
+ {
1280
+ "type": "Secret Keyword",
1281
+ "filename": "src/agents/models-config.skips-writing-models-json-no-env-token.test.ts",
1282
+ "hashed_secret": "fcdd655b11f33ba4327695084a347b2ba192976c",
1283
+ "is_verified": false,
1284
+ "line_number": 112
1285
+ },
1286
+ {
1287
+ "type": "Secret Keyword",
1288
+ "filename": "src/agents/models-config.skips-writing-models-json-no-env-token.test.ts",
1289
+ "hashed_secret": "94c4be5a1976115e8152960c21e04400a4fccdf6",
1290
+ "is_verified": false,
1291
+ "line_number": 146
1292
+ }
1293
+ ],
1294
+ "src/agents/models-config.uses-first-github-copilot-profile-env-tokens.test.ts": [
1295
+ {
1296
+ "type": "Secret Keyword",
1297
+ "filename": "src/agents/models-config.uses-first-github-copilot-profile-env-tokens.test.ts",
1298
+ "hashed_secret": "7cf31e8b6cda49f70c31f1f25af05d46f924142d",
1299
+ "is_verified": false,
1300
+ "line_number": 16
1301
+ }
1302
+ ],
1303
+ "src/agents/openai-responses.reasoning-replay.test.ts": [
1304
+ {
1305
+ "type": "Secret Keyword",
1306
+ "filename": "src/agents/openai-responses.reasoning-replay.test.ts",
1307
+ "hashed_secret": "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3",
1308
+ "is_verified": false,
1309
+ "line_number": 124
1310
+ }
1311
+ ],
1312
+ "src/agents/pi-embedded-runner.applygoogleturnorderingfix.test.ts": [
1313
+ {
1314
+ "type": "Secret Keyword",
1315
+ "filename": "src/agents/pi-embedded-runner.applygoogleturnorderingfix.test.ts",
1316
+ "hashed_secret": "e9a5f12a8ecbb3eb46eca5096b5c52aa5e7c9fdd",
1317
+ "is_verified": false,
1318
+ "line_number": 58
1319
+ }
1320
+ ],
1321
+ "src/agents/pi-embedded-runner.buildembeddedsandboxinfo.test.ts": [
1322
+ {
1323
+ "type": "Secret Keyword",
1324
+ "filename": "src/agents/pi-embedded-runner.buildembeddedsandboxinfo.test.ts",
1325
+ "hashed_secret": "e9a5f12a8ecbb3eb46eca5096b5c52aa5e7c9fdd",
1326
+ "is_verified": false,
1327
+ "line_number": 57
1328
+ }
1329
+ ],
1330
+ "src/agents/pi-embedded-runner.createsystempromptoverride.test.ts": [
1331
+ {
1332
+ "type": "Secret Keyword",
1333
+ "filename": "src/agents/pi-embedded-runner.createsystempromptoverride.test.ts",
1334
+ "hashed_secret": "e9a5f12a8ecbb3eb46eca5096b5c52aa5e7c9fdd",
1335
+ "is_verified": false,
1336
+ "line_number": 56
1337
+ }
1338
+ ],
1339
+ "src/agents/pi-embedded-runner.get-dm-history-limit-from-session-key.falls-back-provider-default-per-dm-not.test.ts": [
1340
+ {
1341
+ "type": "Secret Keyword",
1342
+ "filename": "src/agents/pi-embedded-runner.get-dm-history-limit-from-session-key.falls-back-provider-default-per-dm-not.test.ts",
1343
+ "hashed_secret": "e9a5f12a8ecbb3eb46eca5096b5c52aa5e7c9fdd",
1344
+ "is_verified": false,
1345
+ "line_number": 56
1346
+ }
1347
+ ],
1348
+ "src/agents/pi-embedded-runner.get-dm-history-limit-from-session-key.returns-undefined-sessionkey-is-undefined.test.ts": [
1349
+ {
1350
+ "type": "Secret Keyword",
1351
+ "filename": "src/agents/pi-embedded-runner.get-dm-history-limit-from-session-key.returns-undefined-sessionkey-is-undefined.test.ts",
1352
+ "hashed_secret": "e9a5f12a8ecbb3eb46eca5096b5c52aa5e7c9fdd",
1353
+ "is_verified": false,
1354
+ "line_number": 56
1355
+ }
1356
+ ],
1357
+ "src/agents/pi-embedded-runner.limithistoryturns.test.ts": [
1358
+ {
1359
+ "type": "Secret Keyword",
1360
+ "filename": "src/agents/pi-embedded-runner.limithistoryturns.test.ts",
1361
+ "hashed_secret": "e9a5f12a8ecbb3eb46eca5096b5c52aa5e7c9fdd",
1362
+ "is_verified": false,
1363
+ "line_number": 57
1364
+ }
1365
+ ],
1366
+ "src/agents/pi-embedded-runner.resolvesessionagentids.test.ts": [
1367
+ {
1368
+ "type": "Secret Keyword",
1369
+ "filename": "src/agents/pi-embedded-runner.resolvesessionagentids.test.ts",
1370
+ "hashed_secret": "e9a5f12a8ecbb3eb46eca5096b5c52aa5e7c9fdd",
1371
+ "is_verified": false,
1372
+ "line_number": 56
1373
+ }
1374
+ ],
1375
+ "src/agents/pi-embedded-runner.splitsdktools.test.ts": [
1376
+ {
1377
+ "type": "Secret Keyword",
1378
+ "filename": "src/agents/pi-embedded-runner.splitsdktools.test.ts",
1379
+ "hashed_secret": "e9a5f12a8ecbb3eb46eca5096b5c52aa5e7c9fdd",
1380
+ "is_verified": false,
1381
+ "line_number": 57
1382
+ }
1383
+ ],
1384
+ "src/agents/pi-embedded-runner.test.ts": [
1385
+ {
1386
+ "type": "Secret Keyword",
1387
+ "filename": "src/agents/pi-embedded-runner.test.ts",
1388
+ "hashed_secret": "e9a5f12a8ecbb3eb46eca5096b5c52aa5e7c9fdd",
1389
+ "is_verified": false,
1390
+ "line_number": 117
1391
+ },
1392
+ {
1393
+ "type": "Secret Keyword",
1394
+ "filename": "src/agents/pi-embedded-runner.test.ts",
1395
+ "hashed_secret": "fcdd655b11f33ba4327695084a347b2ba192976c",
1396
+ "is_verified": false,
1397
+ "line_number": 178
1398
+ }
1399
+ ],
1400
+ "src/agents/skills.applyskillenvoverrides.test.ts": [
1401
+ {
1402
+ "type": "Secret Keyword",
1403
+ "filename": "src/agents/skills.applyskillenvoverrides.test.ts",
1404
+ "hashed_secret": "5df3a673d724e8a1eb673a8baf623e183940804d",
1405
+ "is_verified": false,
1406
+ "line_number": 54
1407
+ },
1408
+ {
1409
+ "type": "Secret Keyword",
1410
+ "filename": "src/agents/skills.applyskillenvoverrides.test.ts",
1411
+ "hashed_secret": "8921daaa546693e52bc1f9c40bdcf15e816e0448",
1412
+ "is_verified": false,
1413
+ "line_number": 80
1414
+ }
1415
+ ],
1416
+ "src/agents/skills.build-workspace-skills-prompt.prefers-workspace-skills-managed-skills.test.ts": [
1417
+ {
1418
+ "type": "Secret Keyword",
1419
+ "filename": "src/agents/skills.build-workspace-skills-prompt.prefers-workspace-skills-managed-skills.test.ts",
1420
+ "hashed_secret": "7a85f4764bbd6daf1c3545efbbf0f279a6dc0beb",
1421
+ "is_verified": false,
1422
+ "line_number": 124
1423
+ }
1424
+ ],
1425
+ "src/agents/skills.build-workspace-skills-prompt.syncs-merged-skills-into-target-workspace.test.ts": [
1426
+ {
1427
+ "type": "Secret Keyword",
1428
+ "filename": "src/agents/skills.build-workspace-skills-prompt.syncs-merged-skills-into-target-workspace.test.ts",
1429
+ "hashed_secret": "3acfb2c2b433c0ea7ff107e33df91b18e52f960f",
1430
+ "is_verified": false,
1431
+ "line_number": 102
1432
+ }
1433
+ ],
1434
+ "src/agents/tools/web-fetch.ssrf.test.ts": [
1435
+ {
1436
+ "type": "Secret Keyword",
1437
+ "filename": "src/agents/tools/web-fetch.ssrf.test.ts",
1438
+ "hashed_secret": "5ce8e9d54c77266fff990194d2219a708c59b76c",
1439
+ "is_verified": false,
1440
+ "line_number": 55
1441
+ }
1442
+ ],
1443
+ "src/agents/tools/web-search.ts": [
1444
+ {
1445
+ "type": "Secret Keyword",
1446
+ "filename": "src/agents/tools/web-search.ts",
1447
+ "hashed_secret": "dfba7aade0868074c2861c98e2a9a92f3178a51b",
1448
+ "is_verified": false,
1449
+ "line_number": 85
1450
+ },
1451
+ {
1452
+ "type": "Secret Keyword",
1453
+ "filename": "src/agents/tools/web-search.ts",
1454
+ "hashed_secret": "71f8e7976e4cbc4561c9d62fb283e7f788202acb",
1455
+ "is_verified": false,
1456
+ "line_number": 190
1457
+ },
1458
+ {
1459
+ "type": "Secret Keyword",
1460
+ "filename": "src/agents/tools/web-search.ts",
1461
+ "hashed_secret": "c4865ff9250aca23b0d98eb079dad70ebec1cced",
1462
+ "is_verified": false,
1463
+ "line_number": 198
1464
+ },
1465
+ {
1466
+ "type": "Secret Keyword",
1467
+ "filename": "src/agents/tools/web-search.ts",
1468
+ "hashed_secret": "527ee41f36386e85fa932ef09471ca017f3c95c8",
1469
+ "is_verified": false,
1470
+ "line_number": 199
1471
+ }
1472
+ ],
1473
+ "src/agents/tools/web-tools.enabled-defaults.test.ts": [
1474
+ {
1475
+ "type": "Secret Keyword",
1476
+ "filename": "src/agents/tools/web-tools.enabled-defaults.test.ts",
1477
+ "hashed_secret": "47b249a75ca78fdb578d0f28c33685e27ea82684",
1478
+ "is_verified": false,
1479
+ "line_number": 213
1480
+ },
1481
+ {
1482
+ "type": "Secret Keyword",
1483
+ "filename": "src/agents/tools/web-tools.enabled-defaults.test.ts",
1484
+ "hashed_secret": "d0ffd81d6d7ad1bc3c365660fe8882480c9a986e",
1485
+ "is_verified": false,
1486
+ "line_number": 242
1487
+ }
1488
+ ],
1489
+ "src/agents/tools/web-tools.fetch.test.ts": [
1490
+ {
1491
+ "type": "Secret Keyword",
1492
+ "filename": "src/agents/tools/web-tools.fetch.test.ts",
1493
+ "hashed_secret": "5ce8e9d54c77266fff990194d2219a708c59b76c",
1494
+ "is_verified": false,
1495
+ "line_number": 101
1496
+ }
1497
+ ],
1498
+ "src/auto-reply/reply.directive.directive-behavior.prefers-alias-matches-fuzzy-selection-is-ambiguous.e2e.test.ts": [
1499
+ {
1500
+ "type": "Secret Keyword",
1501
+ "filename": "src/auto-reply/reply.directive.directive-behavior.prefers-alias-matches-fuzzy-selection-is-ambiguous.e2e.test.ts",
1502
+ "hashed_secret": "e9a5f12a8ecbb3eb46eca5096b5c52aa5e7c9fdd",
1503
+ "is_verified": false,
1504
+ "line_number": 90
1505
+ },
1506
+ {
1507
+ "type": "Secret Keyword",
1508
+ "filename": "src/auto-reply/reply.directive.directive-behavior.prefers-alias-matches-fuzzy-selection-is-ambiguous.e2e.test.ts",
1509
+ "hashed_secret": "16c249e04e2be318050cb883c40137361c0c7209",
1510
+ "is_verified": false,
1511
+ "line_number": 96
1512
+ }
1513
+ ],
1514
+ "src/auto-reply/reply.directive.directive-behavior.supports-fuzzy-model-matches-model-directive.e2e.test.ts": [
1515
+ {
1516
+ "type": "Secret Keyword",
1517
+ "filename": "src/auto-reply/reply.directive.directive-behavior.supports-fuzzy-model-matches-model-directive.e2e.test.ts",
1518
+ "hashed_secret": "e9a5f12a8ecbb3eb46eca5096b5c52aa5e7c9fdd",
1519
+ "is_verified": false,
1520
+ "line_number": 87
1521
+ },
1522
+ {
1523
+ "type": "Secret Keyword",
1524
+ "filename": "src/auto-reply/reply.directive.directive-behavior.supports-fuzzy-model-matches-model-directive.e2e.test.ts",
1525
+ "hashed_secret": "16c249e04e2be318050cb883c40137361c0c7209",
1526
+ "is_verified": false,
1527
+ "line_number": 228
1528
+ }
1529
+ ],
1530
+ "src/auto-reply/status.test.ts": [
1531
+ {
1532
+ "type": "Secret Keyword",
1533
+ "filename": "src/auto-reply/status.test.ts",
1534
+ "hashed_secret": "3acfb2c2b433c0ea7ff107e33df91b18e52f960f",
1535
+ "is_verified": false,
1536
+ "line_number": 20
1537
+ }
1538
+ ],
1539
+ "src/browser/cdp.helpers.test.ts": [
1540
+ {
1541
+ "type": "Basic Auth Credentials",
1542
+ "filename": "src/browser/cdp.helpers.test.ts",
1543
+ "hashed_secret": "9d4e1e23bd5b727046a9e3b4b7db57bd8d6ee684",
1544
+ "is_verified": false,
1545
+ "line_number": 22
1546
+ }
1547
+ ],
1548
+ "src/browser/cdp.test.ts": [
1549
+ {
1550
+ "type": "Basic Auth Credentials",
1551
+ "filename": "src/browser/cdp.test.ts",
1552
+ "hashed_secret": "9d4e1e23bd5b727046a9e3b4b7db57bd8d6ee684",
1553
+ "is_verified": false,
1554
+ "line_number": 172
1555
+ }
1556
+ ],
1557
+ "src/browser/target-id.test.ts": [
1558
+ {
1559
+ "type": "Hex High Entropy String",
1560
+ "filename": "src/browser/target-id.test.ts",
1561
+ "hashed_secret": "4e126c049580d66ca1549fa534d95a7263f27f46",
1562
+ "is_verified": false,
1563
+ "line_number": 13
1564
+ }
1565
+ ],
1566
+ "src/cli/update-cli.test.ts": [
1567
+ {
1568
+ "type": "Hex High Entropy String",
1569
+ "filename": "src/cli/update-cli.test.ts",
1570
+ "hashed_secret": "e4f91dd323bac5bfc4f60a6e433787671dc2421d",
1571
+ "is_verified": false,
1572
+ "line_number": 112
1573
+ }
1574
+ ],
1575
+ "src/commands/auth-choice.preferred-provider.ts": [
1576
+ {
1577
+ "type": "Secret Keyword",
1578
+ "filename": "src/commands/auth-choice.preferred-provider.ts",
1579
+ "hashed_secret": "c03a8d10174dd7eb2b3288b570a5a74fdd9ae05d",
1580
+ "is_verified": false,
1581
+ "line_number": 8
1582
+ }
1583
+ ],
1584
+ "src/commands/auth-choice.test.ts": [
1585
+ {
1586
+ "type": "Secret Keyword",
1587
+ "filename": "src/commands/auth-choice.test.ts",
1588
+ "hashed_secret": "2480500ff391183070fe22ba8665a8be19350833",
1589
+ "is_verified": false,
1590
+ "line_number": 289
1591
+ },
1592
+ {
1593
+ "type": "Secret Keyword",
1594
+ "filename": "src/commands/auth-choice.test.ts",
1595
+ "hashed_secret": "77e991e9f56e6fa4ed1a908208048421f1214c07",
1596
+ "is_verified": false,
1597
+ "line_number": 350
1598
+ },
1599
+ {
1600
+ "type": "Secret Keyword",
1601
+ "filename": "src/commands/auth-choice.test.ts",
1602
+ "hashed_secret": "1b4d8423b11d32dd0c466428ac81de84a4a9442b",
1603
+ "is_verified": false,
1604
+ "line_number": 528
1605
+ }
1606
+ ],
1607
+ "src/commands/configure.gateway-auth.test.ts": [
1608
+ {
1609
+ "type": "Secret Keyword",
1610
+ "filename": "src/commands/configure.gateway-auth.test.ts",
1611
+ "hashed_secret": "e5e9fa1ba31ecd1ae84f75caaa474f3a663f05f4",
1612
+ "is_verified": false,
1613
+ "line_number": 8
1614
+ }
1615
+ ],
1616
+ "src/commands/models/list.status.test.ts": [
1617
+ {
1618
+ "type": "Base64 High Entropy String",
1619
+ "filename": "src/commands/models/list.status.test.ts",
1620
+ "hashed_secret": "d6ae2508a78a232d5378ef24b85ce40cbb4d7ff0",
1621
+ "is_verified": false,
1622
+ "line_number": 11
1623
+ },
1624
+ {
1625
+ "type": "Base64 High Entropy String",
1626
+ "filename": "src/commands/models/list.status.test.ts",
1627
+ "hashed_secret": "2d8012102440ea97852b3152239218f00579bafa",
1628
+ "is_verified": false,
1629
+ "line_number": 18
1630
+ },
1631
+ {
1632
+ "type": "Base64 High Entropy String",
1633
+ "filename": "src/commands/models/list.status.test.ts",
1634
+ "hashed_secret": "51848e2be4b461a549218d3167f19c01be6b98b8",
1635
+ "is_verified": false,
1636
+ "line_number": 46
1637
+ },
1638
+ {
1639
+ "type": "Secret Keyword",
1640
+ "filename": "src/commands/models/list.status.test.ts",
1641
+ "hashed_secret": "51848e2be4b461a549218d3167f19c01be6b98b8",
1642
+ "is_verified": false,
1643
+ "line_number": 46
1644
+ },
1645
+ {
1646
+ "type": "Secret Keyword",
1647
+ "filename": "src/commands/models/list.status.test.ts",
1648
+ "hashed_secret": "1c1e381bfb72d3b7bfca9437053d9875356680f0",
1649
+ "is_verified": false,
1650
+ "line_number": 52
1651
+ }
1652
+ ],
1653
+ "src/commands/onboard-auth.config-minimax.ts": [
1654
+ {
1655
+ "type": "Secret Keyword",
1656
+ "filename": "src/commands/onboard-auth.config-minimax.ts",
1657
+ "hashed_secret": "16c249e04e2be318050cb883c40137361c0c7209",
1658
+ "is_verified": false,
1659
+ "line_number": 30
1660
+ },
1661
+ {
1662
+ "type": "Secret Keyword",
1663
+ "filename": "src/commands/onboard-auth.config-minimax.ts",
1664
+ "hashed_secret": "ddcb713196b974770575a9bea5a4e7d46361f8e9",
1665
+ "is_verified": false,
1666
+ "line_number": 85
1667
+ }
1668
+ ],
1669
+ "src/commands/onboard-auth.test.ts": [
1670
+ {
1671
+ "type": "Secret Keyword",
1672
+ "filename": "src/commands/onboard-auth.test.ts",
1673
+ "hashed_secret": "666c100dab549a6f56da7da546bd848ed5086541",
1674
+ "is_verified": false,
1675
+ "line_number": 230
1676
+ },
1677
+ {
1678
+ "type": "Secret Keyword",
1679
+ "filename": "src/commands/onboard-auth.test.ts",
1680
+ "hashed_secret": "e184b402822abc549b37689c84e8e0e33c39a1f1",
1681
+ "is_verified": false,
1682
+ "line_number": 262
1683
+ }
1684
+ ],
1685
+ "src/commands/onboard-non-interactive.ai-gateway.test.ts": [
1686
+ {
1687
+ "type": "Secret Keyword",
1688
+ "filename": "src/commands/onboard-non-interactive.ai-gateway.test.ts",
1689
+ "hashed_secret": "77e991e9f56e6fa4ed1a908208048421f1214c07",
1690
+ "is_verified": false,
1691
+ "line_number": 50
1692
+ }
1693
+ ],
1694
+ "src/commands/onboard-non-interactive/api-keys.ts": [
1695
+ {
1696
+ "type": "Secret Keyword",
1697
+ "filename": "src/commands/onboard-non-interactive/api-keys.ts",
1698
+ "hashed_secret": "112f3a99b283a4e1788dedd8e0e5d35375c33747",
1699
+ "is_verified": false,
1700
+ "line_number": 10
1701
+ }
1702
+ ],
1703
+ "src/config/config.env-vars.test.ts": [
1704
+ {
1705
+ "type": "Secret Keyword",
1706
+ "filename": "src/config/config.env-vars.test.ts",
1707
+ "hashed_secret": "a24ef9c1a27cac44823571ceef2e8262718eee36",
1708
+ "is_verified": false,
1709
+ "line_number": 15
1710
+ },
1711
+ {
1712
+ "type": "Secret Keyword",
1713
+ "filename": "src/config/config.env-vars.test.ts",
1714
+ "hashed_secret": "29d5f92e9ee44d4854d6dfaeefc3dc27d779fdf3",
1715
+ "is_verified": false,
1716
+ "line_number": 47
1717
+ },
1718
+ {
1719
+ "type": "Secret Keyword",
1720
+ "filename": "src/config/config.env-vars.test.ts",
1721
+ "hashed_secret": "1672b6a1e7956c6a70f45d699aa42a351b1f8b80",
1722
+ "is_verified": false,
1723
+ "line_number": 63
1724
+ }
1725
+ ],
1726
+ "src/config/config.talk-api-key-fallback.test.ts": [
1727
+ {
1728
+ "type": "Secret Keyword",
1729
+ "filename": "src/config/config.talk-api-key-fallback.test.ts",
1730
+ "hashed_secret": "bea2f7b64fab8d1d414d0449530b1e088d36d5b1",
1731
+ "is_verified": false,
1732
+ "line_number": 42
1733
+ }
1734
+ ],
1735
+ "src/config/config.web-search-provider.test.ts": [
1736
+ {
1737
+ "type": "Secret Keyword",
1738
+ "filename": "src/config/config.web-search-provider.test.ts",
1739
+ "hashed_secret": "3acfb2c2b433c0ea7ff107e33df91b18e52f960f",
1740
+ "is_verified": false,
1741
+ "line_number": 14
1742
+ }
1743
+ ],
1744
+ "src/config/env-substitution.test.ts": [
1745
+ {
1746
+ "type": "Secret Keyword",
1747
+ "filename": "src/config/env-substitution.test.ts",
1748
+ "hashed_secret": "f2b14f68eb995facb3a1c35287b778d5bd785511",
1749
+ "is_verified": false,
1750
+ "line_number": 38
1751
+ },
1752
+ {
1753
+ "type": "Secret Keyword",
1754
+ "filename": "src/config/env-substitution.test.ts",
1755
+ "hashed_secret": "ec417f567082612f8fd6afafe1abcab831fca840",
1756
+ "is_verified": false,
1757
+ "line_number": 69
1758
+ },
1759
+ {
1760
+ "type": "Secret Keyword",
1761
+ "filename": "src/config/env-substitution.test.ts",
1762
+ "hashed_secret": "520bd69c3eb1646d9a78181ecb4c90c51fdf428d",
1763
+ "is_verified": false,
1764
+ "line_number": 70
1765
+ },
1766
+ {
1767
+ "type": "Secret Keyword",
1768
+ "filename": "src/config/env-substitution.test.ts",
1769
+ "hashed_secret": "f136444bf9b3d01a9f9b772b80ac6bf7b6a43ef0",
1770
+ "is_verified": false,
1771
+ "line_number": 228
1772
+ }
1773
+ ],
1774
+ "src/config/schema.ts": [
1775
+ {
1776
+ "type": "Secret Keyword",
1777
+ "filename": "src/config/schema.ts",
1778
+ "hashed_secret": "e73c9fcad85cd4eecc74181ec4bdb31064d68439",
1779
+ "is_verified": false,
1780
+ "line_number": 184
1781
+ },
1782
+ {
1783
+ "type": "Secret Keyword",
1784
+ "filename": "src/config/schema.ts",
1785
+ "hashed_secret": "2eda7cd978f39eebec3bf03e4410a40e14167fff",
1786
+ "is_verified": false,
1787
+ "line_number": 220
1788
+ },
1789
+ {
1790
+ "type": "Secret Keyword",
1791
+ "filename": "src/config/schema.ts",
1792
+ "hashed_secret": "9f4cda226d3868676ac7f86f59e4190eb94bd208",
1793
+ "is_verified": false,
1794
+ "line_number": 418
1795
+ },
1796
+ {
1797
+ "type": "Secret Keyword",
1798
+ "filename": "src/config/schema.ts",
1799
+ "hashed_secret": "01822c8bbf6a8b136944b14182cb885100ec2eae",
1800
+ "is_verified": false,
1801
+ "line_number": 437
1802
+ },
1803
+ {
1804
+ "type": "Secret Keyword",
1805
+ "filename": "src/config/schema.ts",
1806
+ "hashed_secret": "bb7dfd9746e660e4a4374951ec5938ef0e343255",
1807
+ "is_verified": false,
1808
+ "line_number": 487
1809
+ }
1810
+ ],
1811
+ "src/config/slack-http-config.test.ts": [
1812
+ {
1813
+ "type": "Secret Keyword",
1814
+ "filename": "src/config/slack-http-config.test.ts",
1815
+ "hashed_secret": "e5e9fa1ba31ecd1ae84f75caaa474f3a663f05f4",
1816
+ "is_verified": false,
1817
+ "line_number": 11
1818
+ }
1819
+ ],
1820
+ "src/gateway/auth.test.ts": [
1821
+ {
1822
+ "type": "Secret Keyword",
1823
+ "filename": "src/gateway/auth.test.ts",
1824
+ "hashed_secret": "e5e9fa1ba31ecd1ae84f75caaa474f3a663f05f4",
1825
+ "is_verified": false,
1826
+ "line_number": 43
1827
+ },
1828
+ {
1829
+ "type": "Secret Keyword",
1830
+ "filename": "src/gateway/auth.test.ts",
1831
+ "hashed_secret": "a4b48a81cdab1e1a5dd37907d6c85ca1c61ddc7c",
1832
+ "is_verified": false,
1833
+ "line_number": 51
1834
+ }
1835
+ ],
1836
+ "src/gateway/call.test.ts": [
1837
+ {
1838
+ "type": "Secret Keyword",
1839
+ "filename": "src/gateway/call.test.ts",
1840
+ "hashed_secret": "e5e9fa1ba31ecd1ae84f75caaa474f3a663f05f4",
1841
+ "is_verified": false,
1842
+ "line_number": 285
1843
+ },
1844
+ {
1845
+ "type": "Secret Keyword",
1846
+ "filename": "src/gateway/call.test.ts",
1847
+ "hashed_secret": "e493f561d90c6638c1f51c5a8a069c3b129b79ed",
1848
+ "is_verified": false,
1849
+ "line_number": 295
1850
+ },
1851
+ {
1852
+ "type": "Secret Keyword",
1853
+ "filename": "src/gateway/call.test.ts",
1854
+ "hashed_secret": "2e07956ffc9bc4fd624064c40b7495c85d5f1467",
1855
+ "is_verified": false,
1856
+ "line_number": 300
1857
+ },
1858
+ {
1859
+ "type": "Secret Keyword",
1860
+ "filename": "src/gateway/call.test.ts",
1861
+ "hashed_secret": "bddc29032de580fb53b3a9a0357dd409086db800",
1862
+ "is_verified": false,
1863
+ "line_number": 313
1864
+ }
1865
+ ],
1866
+ "src/gateway/client.test.ts": [
1867
+ {
1868
+ "type": "Private Key",
1869
+ "filename": "src/gateway/client.test.ts",
1870
+ "hashed_secret": "1348b145fa1a555461c1b790a2f66614781091e9",
1871
+ "is_verified": false,
1872
+ "line_number": 83
1873
+ }
1874
+ ],
1875
+ "src/gateway/gateway-cli-backend.live.test.ts": [
1876
+ {
1877
+ "type": "Hex High Entropy String",
1878
+ "filename": "src/gateway/gateway-cli-backend.live.test.ts",
1879
+ "hashed_secret": "3e2fd4a90d5afbd27974730c4d6a9592fe300825",
1880
+ "is_verified": false,
1881
+ "line_number": 38
1882
+ }
1883
+ ],
1884
+ "src/gateway/gateway-models.profiles.live.test.ts": [
1885
+ {
1886
+ "type": "Hex High Entropy String",
1887
+ "filename": "src/gateway/gateway-models.profiles.live.test.ts",
1888
+ "hashed_secret": "3e2fd4a90d5afbd27974730c4d6a9592fe300825",
1889
+ "is_verified": false,
1890
+ "line_number": 219
1891
+ }
1892
+ ],
1893
+ "src/gateway/gateway.e2e.test.ts": [
1894
+ {
1895
+ "type": "Secret Keyword",
1896
+ "filename": "src/gateway/gateway.e2e.test.ts",
1897
+ "hashed_secret": "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3",
1898
+ "is_verified": false,
1899
+ "line_number": 73
1900
+ }
1901
+ ],
1902
+ "src/gateway/server.auth.e2e.test.ts": [
1903
+ {
1904
+ "type": "Secret Keyword",
1905
+ "filename": "src/gateway/server.auth.e2e.test.ts",
1906
+ "hashed_secret": "e5e9fa1ba31ecd1ae84f75caaa474f3a663f05f4",
1907
+ "is_verified": false,
1908
+ "line_number": 179
1909
+ },
1910
+ {
1911
+ "type": "Secret Keyword",
1912
+ "filename": "src/gateway/server.auth.e2e.test.ts",
1913
+ "hashed_secret": "a4b48a81cdab1e1a5dd37907d6c85ca1c61ddc7c",
1914
+ "is_verified": false,
1915
+ "line_number": 197
1916
+ }
1917
+ ],
1918
+ "src/gateway/session-utils.test.ts": [
1919
+ {
1920
+ "type": "Base64 High Entropy String",
1921
+ "filename": "src/gateway/session-utils.test.ts",
1922
+ "hashed_secret": "bb9a5d9483409d2c60b28268a0efcb93324d4cda",
1923
+ "is_verified": false,
1924
+ "line_number": 156
1925
+ }
1926
+ ],
1927
+ "src/gateway/tools-invoke-http.test.ts": [
1928
+ {
1929
+ "type": "Secret Keyword",
1930
+ "filename": "src/gateway/tools-invoke-http.test.ts",
1931
+ "hashed_secret": "e5e9fa1ba31ecd1ae84f75caaa474f3a663f05f4",
1932
+ "is_verified": false,
1933
+ "line_number": 56
1934
+ }
1935
+ ],
1936
+ "src/gateway/ws-log.test.ts": [
1937
+ {
1938
+ "type": "Base64 High Entropy String",
1939
+ "filename": "src/gateway/ws-log.test.ts",
1940
+ "hashed_secret": "edd2e7ac4f61d0c606e80a0919d727540842a307",
1941
+ "is_verified": false,
1942
+ "line_number": 22
1943
+ }
1944
+ ],
1945
+ "src/infra/env.test.ts": [
1946
+ {
1947
+ "type": "Secret Keyword",
1948
+ "filename": "src/infra/env.test.ts",
1949
+ "hashed_secret": "df98a117ddabf85991b9fe0e268214dc0e1254dc",
1950
+ "is_verified": false,
1951
+ "line_number": 10
1952
+ },
1953
+ {
1954
+ "type": "Secret Keyword",
1955
+ "filename": "src/infra/env.test.ts",
1956
+ "hashed_secret": "6d811dc1f59a55ca1a3d38b5042a062b9f79e8ec",
1957
+ "is_verified": false,
1958
+ "line_number": 25
1959
+ }
1960
+ ],
1961
+ "src/infra/outbound/message-action-runner.test.ts": [
1962
+ {
1963
+ "type": "Hex High Entropy String",
1964
+ "filename": "src/infra/outbound/message-action-runner.test.ts",
1965
+ "hashed_secret": "804ec071803318791b835cffd6e509c8d32239db",
1966
+ "is_verified": false,
1967
+ "line_number": 88
1968
+ },
1969
+ {
1970
+ "type": "Secret Keyword",
1971
+ "filename": "src/infra/outbound/message-action-runner.test.ts",
1972
+ "hashed_secret": "789cbe0407840b1c2041cb33452ff60f19bf58cc",
1973
+ "is_verified": false,
1974
+ "line_number": 385
1975
+ }
1976
+ ],
1977
+ "src/infra/outbound/outbound-policy.test.ts": [
1978
+ {
1979
+ "type": "Hex High Entropy String",
1980
+ "filename": "src/infra/outbound/outbound-policy.test.ts",
1981
+ "hashed_secret": "804ec071803318791b835cffd6e509c8d32239db",
1982
+ "is_verified": false,
1983
+ "line_number": 33
1984
+ }
1985
+ ],
1986
+ "src/infra/shell-env.test.ts": [
1987
+ {
1988
+ "type": "Secret Keyword",
1989
+ "filename": "src/infra/shell-env.test.ts",
1990
+ "hashed_secret": "65c10dc3549fe07424148a8a4790a3341ecbc253",
1991
+ "is_verified": false,
1992
+ "line_number": 27
1993
+ },
1994
+ {
1995
+ "type": "Secret Keyword",
1996
+ "filename": "src/infra/shell-env.test.ts",
1997
+ "hashed_secret": "e013ffda590d2178607c16d11b1ea42f75ceb0e7",
1998
+ "is_verified": false,
1999
+ "line_number": 59
2000
+ },
2001
+ {
2002
+ "type": "Base64 High Entropy String",
2003
+ "filename": "src/infra/shell-env.test.ts",
2004
+ "hashed_secret": "be6ee9a6bf9f2dad84a5a67d6c0576a5bacc391e",
2005
+ "is_verified": false,
2006
+ "line_number": 61
2007
+ }
2008
+ ],
2009
+ "src/logging/redact.test.ts": [
2010
+ {
2011
+ "type": "Base64 High Entropy String",
2012
+ "filename": "src/logging/redact.test.ts",
2013
+ "hashed_secret": "dd7754662b89333191ff45e8257a3e6d3fcd3990",
2014
+ "is_verified": false,
2015
+ "line_number": 9
2016
+ },
2017
+ {
2018
+ "type": "Private Key",
2019
+ "filename": "src/logging/redact.test.ts",
2020
+ "hashed_secret": "1348b145fa1a555461c1b790a2f66614781091e9",
2021
+ "is_verified": false,
2022
+ "line_number": 64
2023
+ },
2024
+ {
2025
+ "type": "Hex High Entropy String",
2026
+ "filename": "src/logging/redact.test.ts",
2027
+ "hashed_secret": "7992945213f7d76889fa83ff0f2be352409c837e",
2028
+ "is_verified": false,
2029
+ "line_number": 65
2030
+ },
2031
+ {
2032
+ "type": "Base64 High Entropy String",
2033
+ "filename": "src/logging/redact.test.ts",
2034
+ "hashed_secret": "063995ecb4fa5afe2460397d322925cd867b7d74",
2035
+ "is_verified": false,
2036
+ "line_number": 79
2037
+ }
2038
+ ],
2039
+ "src/media-understanding/apply.test.ts": [
2040
+ {
2041
+ "type": "Secret Keyword",
2042
+ "filename": "src/media-understanding/apply.test.ts",
2043
+ "hashed_secret": "3acfb2c2b433c0ea7ff107e33df91b18e52f960f",
2044
+ "is_verified": false,
2045
+ "line_number": 14
2046
+ }
2047
+ ],
2048
+ "src/media-understanding/providers/deepgram/audio.test.ts": [
2049
+ {
2050
+ "type": "Secret Keyword",
2051
+ "filename": "src/media-understanding/providers/deepgram/audio.test.ts",
2052
+ "hashed_secret": "3acfb2c2b433c0ea7ff107e33df91b18e52f960f",
2053
+ "is_verified": false,
2054
+ "line_number": 31
2055
+ }
2056
+ ],
2057
+ "src/media-understanding/providers/google/video.test.ts": [
2058
+ {
2059
+ "type": "Secret Keyword",
2060
+ "filename": "src/media-understanding/providers/google/video.test.ts",
2061
+ "hashed_secret": "3acfb2c2b433c0ea7ff107e33df91b18e52f960f",
2062
+ "is_verified": false,
2063
+ "line_number": 28
2064
+ }
2065
+ ],
2066
+ "src/media-understanding/providers/openai/audio.test.ts": [
2067
+ {
2068
+ "type": "Secret Keyword",
2069
+ "filename": "src/media-understanding/providers/openai/audio.test.ts",
2070
+ "hashed_secret": "3acfb2c2b433c0ea7ff107e33df91b18e52f960f",
2071
+ "is_verified": false,
2072
+ "line_number": 26
2073
+ }
2074
+ ],
2075
+ "src/media-understanding/runner.auto-audio.test.ts": [
2076
+ {
2077
+ "type": "Secret Keyword",
2078
+ "filename": "src/media-understanding/runner.auto-audio.test.ts",
2079
+ "hashed_secret": "3acfb2c2b433c0ea7ff107e33df91b18e52f960f",
2080
+ "is_verified": false,
2081
+ "line_number": 42
2082
+ }
2083
+ ],
2084
+ "src/media-understanding/runner.deepgram.test.ts": [
2085
+ {
2086
+ "type": "Secret Keyword",
2087
+ "filename": "src/media-understanding/runner.deepgram.test.ts",
2088
+ "hashed_secret": "3acfb2c2b433c0ea7ff107e33df91b18e52f960f",
2089
+ "is_verified": false,
2090
+ "line_number": 46
2091
+ }
2092
+ ],
2093
+ "src/memory/embeddings.test.ts": [
2094
+ {
2095
+ "type": "Secret Keyword",
2096
+ "filename": "src/memory/embeddings.test.ts",
2097
+ "hashed_secret": "a47110e348a3063541fb1f1f640d635d457181a0",
2098
+ "is_verified": false,
2099
+ "line_number": 32
2100
+ },
2101
+ {
2102
+ "type": "Secret Keyword",
2103
+ "filename": "src/memory/embeddings.test.ts",
2104
+ "hashed_secret": "c734e47630dda71619c696d88381f06f7511bd78",
2105
+ "is_verified": false,
2106
+ "line_number": 149
2107
+ },
2108
+ {
2109
+ "type": "Secret Keyword",
2110
+ "filename": "src/memory/embeddings.test.ts",
2111
+ "hashed_secret": "56e1d57b8db262b08bc73c60ed08d8c92e59503f",
2112
+ "is_verified": false,
2113
+ "line_number": 179
2114
+ }
2115
+ ],
2116
+ "src/pairing/pairing-store.ts": [
2117
+ {
2118
+ "type": "Base64 High Entropy String",
2119
+ "filename": "src/pairing/pairing-store.ts",
2120
+ "hashed_secret": "f8c6f1ff98c5ee78c27d34a3ca68f35ad79847af",
2121
+ "is_verified": false,
2122
+ "line_number": 12
2123
+ }
2124
+ ],
2125
+ "src/security/audit.test.ts": [
2126
+ {
2127
+ "type": "Hex High Entropy String",
2128
+ "filename": "src/security/audit.test.ts",
2129
+ "hashed_secret": "b1775a785f09a6ebaf2dc33d6eaeb98974d9cdb8",
2130
+ "is_verified": false,
2131
+ "line_number": 180
2132
+ },
2133
+ {
2134
+ "type": "Hex High Entropy String",
2135
+ "filename": "src/security/audit.test.ts",
2136
+ "hashed_secret": "fa8da98a5bdb77b4902cbb4338e6e94ea825300e",
2137
+ "is_verified": false,
2138
+ "line_number": 209
2139
+ },
2140
+ {
2141
+ "type": "Secret Keyword",
2142
+ "filename": "src/security/audit.test.ts",
2143
+ "hashed_secret": "21f688ab56f76a99e5c6ed342291422f4e57e47f",
2144
+ "is_verified": false,
2145
+ "line_number": 1046
2146
+ },
2147
+ {
2148
+ "type": "Secret Keyword",
2149
+ "filename": "src/security/audit.test.ts",
2150
+ "hashed_secret": "3dc927d80543dc0f643940b70d066bd4b4c4b78e",
2151
+ "is_verified": false,
2152
+ "line_number": 1077
2153
+ }
2154
+ ],
2155
+ "src/tts/tts.test.ts": [
2156
+ {
2157
+ "type": "Secret Keyword",
2158
+ "filename": "src/tts/tts.test.ts",
2159
+ "hashed_secret": "2e7a7ee14caebf378fc32d6cf6f557f347c96773",
2160
+ "is_verified": false,
2161
+ "line_number": 33
2162
+ },
2163
+ {
2164
+ "type": "Hex High Entropy String",
2165
+ "filename": "src/tts/tts.test.ts",
2166
+ "hashed_secret": "b214f706bb602c1cc2adc5c6165e73622305f4bb",
2167
+ "is_verified": false,
2168
+ "line_number": 68
2169
+ }
2170
+ ],
2171
+ "src/web/qr-image.test.ts": [
2172
+ {
2173
+ "type": "Hex High Entropy String",
2174
+ "filename": "src/web/qr-image.test.ts",
2175
+ "hashed_secret": "564666dc1ca6e7318b2d5feeb1ce7b5bf717411e",
2176
+ "is_verified": false,
2177
+ "line_number": 12
2178
+ }
2179
+ ],
2180
+ "test/provider-timeout.e2e.test.ts": [
2181
+ {
2182
+ "type": "Secret Keyword",
2183
+ "filename": "test/provider-timeout.e2e.test.ts",
2184
+ "hashed_secret": "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3",
2185
+ "is_verified": false,
2186
+ "line_number": 182
2187
+ }
2188
+ ]
2189
+ },
2190
+ "generated_at": "2026-01-25T10:55:04Z"
2191
+ }
.shellcheckrc ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ShellCheck configuration
2
+ # https://www.shellcheck.net/wiki/
3
+
4
+ # Disable common false positives and style suggestions
5
+
6
+ # SC2034: Variable appears unused (often exported or used indirectly)
7
+ disable=SC2034
8
+
9
+ # SC2155: Declare and assign separately (common idiom, rarely causes issues)
10
+ disable=SC2155
11
+
12
+ # SC2295: Expansions inside ${..} need quoting (info-level, rarely causes issues)
13
+ disable=SC2295
14
+
15
+ # SC1012: \r is literal (tr -d '\r' works as intended on most systems)
16
+ disable=SC1012
17
+
18
+ # SC2026: Word outside quotes (info-level, often intentional)
19
+ disable=SC2026
20
+
21
+ # SC2016: Expressions don't expand in single quotes (often intentional in sed/awk)
22
+ disable=SC2016
23
+
24
+ # SC2129: Consider using { cmd1; cmd2; } >> file (style preference)
25
+ disable=SC2129
.swiftformat ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # SwiftFormat configuration adapted from Peekaboo defaults (Swift 6 friendly)
2
+
3
+ --swiftversion 6.2
4
+
5
+ # Self handling
6
+ --self insert
7
+ --selfrequired
8
+
9
+ # Imports / extensions
10
+ --importgrouping testable-bottom
11
+ --extensionacl on-declarations
12
+
13
+ # Indentation
14
+ --indent 4
15
+ --indentcase false
16
+ --ifdef no-indent
17
+ --xcodeindentation enabled
18
+
19
+ # Line breaks
20
+ --linebreaks lf
21
+ --maxwidth 120
22
+
23
+ # Whitespace
24
+ --trimwhitespace always
25
+ --emptybraces no-space
26
+ --nospaceoperators ...,..<
27
+ --ranges no-space
28
+ --someAny true
29
+ --voidtype void
30
+
31
+ # Wrapping
32
+ --wraparguments before-first
33
+ --wrapparameters before-first
34
+ --wrapcollections before-first
35
+ --closingparen same-line
36
+
37
+ # Organization
38
+ --organizetypes class,struct,enum,extension
39
+ --extensionmark "MARK: - %t + %p"
40
+ --marktypes always
41
+ --markextensions always
42
+ --structthreshold 0
43
+ --enumthreshold 0
44
+
45
+ # Other
46
+ --stripunusedargs closure-only
47
+ --header ignore
48
+ --allman false
49
+
50
+ # Exclusions
51
+ --exclude .build,.swiftpm,DerivedData,node_modules,dist,coverage,xcuserdata,Peekaboo,Swabble,apps/android,apps/ios,apps/shared,apps/macos/Sources/ClawdisProtocol,apps/macos/Sources/ClawdbotProtocol
.swiftlint.yml ADDED
@@ -0,0 +1,148 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # SwiftLint configuration adapted from Peekaboo defaults (Swift 6 friendly)
2
+
3
+ included:
4
+ - apps/macos/Sources
5
+
6
+ excluded:
7
+ - .build
8
+ - DerivedData
9
+ - "**/.build"
10
+ - "**/.swiftpm"
11
+ - "**/DerivedData"
12
+ - "**/Generated"
13
+ - "**/Resources"
14
+ - "**/Package.swift"
15
+ - "**/Tests/Resources"
16
+ - node_modules
17
+ - dist
18
+ - coverage
19
+ - "*.playground"
20
+ # Generated (protocol-gen-swift.ts)
21
+ - apps/macos/Sources/ClawdbotProtocol/GatewayModels.swift
22
+
23
+ analyzer_rules:
24
+ - unused_declaration
25
+ - unused_import
26
+
27
+ opt_in_rules:
28
+ - array_init
29
+ - closure_spacing
30
+ - contains_over_first_not_nil
31
+ - empty_count
32
+ - empty_string
33
+ - explicit_init
34
+ - fallthrough
35
+ - fatal_error_message
36
+ - first_where
37
+ - joined_default_parameter
38
+ - last_where
39
+ - literal_expression_end_indentation
40
+ - multiline_arguments
41
+ - multiline_parameters
42
+ - operator_usage_whitespace
43
+ - overridden_super_call
44
+ - pattern_matching_keywords
45
+ - private_outlet
46
+ - prohibited_super_call
47
+ - redundant_nil_coalescing
48
+ - sorted_first_last
49
+ - switch_case_alignment
50
+ - unneeded_parentheses_in_closure_argument
51
+ - vertical_parameter_alignment_on_call
52
+
53
+ disabled_rules:
54
+ # SwiftFormat handles these
55
+ - trailing_whitespace
56
+ - trailing_newline
57
+ - trailing_comma
58
+ - vertical_whitespace
59
+ - indentation_width
60
+
61
+ # Style exclusions
62
+ - explicit_self
63
+ - identifier_name
64
+ - file_header
65
+ - explicit_top_level_acl
66
+ - explicit_acl
67
+ - explicit_type_interface
68
+ - missing_docs
69
+ - required_deinit
70
+ - prefer_nimble
71
+ - quick_discouraged_call
72
+ - quick_discouraged_focused_test
73
+ - quick_discouraged_pending_test
74
+ - anonymous_argument_in_multiline_closure
75
+ - no_extension_access_modifier
76
+ - no_grouping_extension
77
+ - switch_case_on_newline
78
+ - strict_fileprivate
79
+ - extension_access_modifier
80
+ - convenience_type
81
+ - no_magic_numbers
82
+ - one_declaration_per_file
83
+ - vertical_whitespace_between_cases
84
+ - vertical_whitespace_closing_braces
85
+ - superfluous_else
86
+ - number_separator
87
+ - prefixed_toplevel_constant
88
+ - opening_brace
89
+ - trailing_closure
90
+ - contrasted_opening_brace
91
+ - sorted_imports
92
+ - redundant_type_annotation
93
+ - shorthand_optional_binding
94
+ - untyped_error_in_catch
95
+ - file_name
96
+ - todo
97
+
98
+ force_cast: warning
99
+ force_try: warning
100
+
101
+ type_name:
102
+ min_length:
103
+ warning: 2
104
+ error: 1
105
+ max_length:
106
+ warning: 60
107
+ error: 80
108
+
109
+ function_body_length:
110
+ warning: 150
111
+ error: 300
112
+
113
+ function_parameter_count:
114
+ warning: 7
115
+ error: 10
116
+
117
+ file_length:
118
+ warning: 1500
119
+ error: 2500
120
+ ignore_comment_only_lines: true
121
+
122
+ type_body_length:
123
+ warning: 800
124
+ error: 1200
125
+
126
+ cyclomatic_complexity:
127
+ warning: 20
128
+ error: 120
129
+
130
+ large_tuple:
131
+ warning: 4
132
+ error: 5
133
+
134
+ nesting:
135
+ type_level:
136
+ warning: 4
137
+ error: 6
138
+ function_level:
139
+ warning: 5
140
+ error: 7
141
+
142
+ line_length:
143
+ warning: 120
144
+ error: 250
145
+ ignores_comments: true
146
+ ignores_urls: true
147
+
148
+ reporter: "xcode"
AGENTS.md ADDED
@@ -0,0 +1,163 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Repository Guidelines
2
+ - Repo: https://github.com/moltbot/moltbot
3
+ - GitHub issues/comments/PR comments: use literal multiline strings or `-F - <<'EOF'` (or $'...') for real newlines; never embed "\\n".
4
+
5
+ ## Project Structure & Module Organization
6
+ - Source code: `src/` (CLI wiring in `src/cli`, commands in `src/commands`, web provider in `src/provider-web.ts`, infra in `src/infra`, media pipeline in `src/media`).
7
+ - Tests: colocated `*.test.ts`.
8
+ - Docs: `docs/` (images, queue, Pi config). Built output lives in `dist/`.
9
+ - Plugins/extensions: live under `extensions/*` (workspace packages). Keep plugin-only deps in the extension `package.json`; do not add them to the root `package.json` unless core uses them.
10
+ - Plugins: install runs `npm install --omit=dev` in plugin dir; runtime deps must live in `dependencies`. Avoid `workspace:*` in `dependencies` (npm install breaks); put `moltbot` in `devDependencies` or `peerDependencies` instead (runtime resolves `clawdbot/plugin-sdk` via jiti alias).
11
+ - Installers served from `https://molt.bot/*`: live in the sibling repo `../molt.bot` (`public/install.sh`, `public/install-cli.sh`, `public/install.ps1`).
12
+ - Messaging channels: always consider **all** built-in + extension channels when refactoring shared logic (routing, allowlists, pairing, command gating, onboarding, docs).
13
+ - Core channel docs: `docs/channels/`
14
+ - Core channel code: `src/telegram`, `src/discord`, `src/slack`, `src/signal`, `src/imessage`, `src/web` (WhatsApp web), `src/channels`, `src/routing`
15
+ - Extensions (channel plugins): `extensions/*` (e.g. `extensions/msteams`, `extensions/matrix`, `extensions/zalo`, `extensions/zalouser`, `extensions/voice-call`)
16
+ - When adding channels/extensions/apps/docs, review `.github/labeler.yml` for label coverage.
17
+
18
+ ## Docs Linking (Mintlify)
19
+ - Docs are hosted on Mintlify (docs.molt.bot).
20
+ - Internal doc links in `docs/**/*.md`: root-relative, no `.md`/`.mdx` (example: `[Config](/configuration)`).
21
+ - Section cross-references: use anchors on root-relative paths (example: `[Hooks](/configuration#hooks)`).
22
+ - Doc headings and anchors: avoid em dashes and apostrophes in headings because they break Mintlify anchor links.
23
+ - When Peter asks for links, reply with full `https://docs.molt.bot/...` URLs (not root-relative).
24
+ - When you touch docs, end the reply with the `https://docs.molt.bot/...` URLs you referenced.
25
+ - README (GitHub): keep absolute docs URLs (`https://docs.molt.bot/...`) so links work on GitHub.
26
+ - Docs content must be generic: no personal device names/hostnames/paths; use placeholders like `user@gateway-host` and “gateway host”.
27
+
28
+ ## exe.dev VM ops (general)
29
+ - Access: stable path is `ssh exe.dev` then `ssh vm-name` (assume SSH key already set).
30
+ - SSH flaky: use exe.dev web terminal or Shelley (web agent); keep a tmux session for long ops.
31
+ - Update: `sudo npm i -g moltbot@latest` (global install needs root on `/usr/lib/node_modules`).
32
+ - Config: use `moltbot config set ...`; ensure `gateway.mode=local` is set.
33
+ - Discord: store raw token only (no `DISCORD_BOT_TOKEN=` prefix).
34
+ - Restart: stop old gateway and run:
35
+ `pkill -9 -f moltbot-gateway || true; nohup moltbot gateway run --bind loopback --port 18789 --force > /tmp/moltbot-gateway.log 2>&1 &`
36
+ - Verify: `moltbot channels status --probe`, `ss -ltnp | rg 18789`, `tail -n 120 /tmp/moltbot-gateway.log`.
37
+
38
+ ## Build, Test, and Development Commands
39
+ - Runtime baseline: Node **22+** (keep Node + Bun paths working).
40
+ - Install deps: `pnpm install`
41
+ - Pre-commit hooks: `prek install` (runs same checks as CI)
42
+ - Also supported: `bun install` (keep `pnpm-lock.yaml` + Bun patching in sync when touching deps/patches).
43
+ - Prefer Bun for TypeScript execution (scripts, dev, tests): `bun <file.ts>` / `bunx <tool>`.
44
+ - Run CLI in dev: `pnpm moltbot ...` (bun) or `pnpm dev`.
45
+ - Node remains supported for running built output (`dist/*`) and production installs.
46
+ - Mac packaging (dev): `scripts/package-mac-app.sh` defaults to current arch. Release checklist: `docs/platforms/mac/release.md`.
47
+ - Type-check/build: `pnpm build` (tsc)
48
+ - Lint/format: `pnpm lint` (oxlint), `pnpm format` (oxfmt)
49
+ - Tests: `pnpm test` (vitest); coverage: `pnpm test:coverage`
50
+
51
+ ## Coding Style & Naming Conventions
52
+ - Language: TypeScript (ESM). Prefer strict typing; avoid `any`.
53
+ - Formatting/linting via Oxlint and Oxfmt; run `pnpm lint` before commits.
54
+ - Add brief code comments for tricky or non-obvious logic.
55
+ - Keep files concise; extract helpers instead of “V2” copies. Use existing patterns for CLI options and dependency injection via `createDefaultDeps`.
56
+ - Aim to keep files under ~700 LOC; guideline only (not a hard guardrail). Split/refactor when it improves clarity or testability.
57
+ - Naming: use **Moltbot** for product/app/docs headings; use `moltbot` for CLI command, package/binary, paths, and config keys.
58
+
59
+ ## Release Channels (Naming)
60
+ - stable: tagged releases only (e.g. `vYYYY.M.D`), npm dist-tag `latest`.
61
+ - beta: prerelease tags `vYYYY.M.D-beta.N`, npm dist-tag `beta` (may ship without macOS app).
62
+ - dev: moving head on `main` (no tag; git checkout main).
63
+
64
+ ## Testing Guidelines
65
+ - Framework: Vitest with V8 coverage thresholds (70% lines/branches/functions/statements).
66
+ - Naming: match source names with `*.test.ts`; e2e in `*.e2e.test.ts`.
67
+ - Run `pnpm test` (or `pnpm test:coverage`) before pushing when you touch logic.
68
+ - Do not set test workers above 16; tried already.
69
+ - Live tests (real keys): `CLAWDBOT_LIVE_TEST=1 pnpm test:live` (Moltbot-only) or `LIVE=1 pnpm test:live` (includes provider live tests). Docker: `pnpm test:docker:live-models`, `pnpm test:docker:live-gateway`. Onboarding Docker E2E: `pnpm test:docker:onboard`.
70
+ - Full kit + what’s covered: `docs/testing.md`.
71
+ - Pure test additions/fixes generally do **not** need a changelog entry unless they alter user-facing behavior or the user asks for one.
72
+ - Mobile: before using a simulator, check for connected real devices (iOS + Android) and prefer them when available.
73
+
74
+ ## Commit & Pull Request Guidelines
75
+ - Create commits with `scripts/committer "<msg>" <file...>`; avoid manual `git add`/`git commit` so staging stays scoped.
76
+ - Follow concise, action-oriented commit messages (e.g., `CLI: add verbose flag to send`).
77
+ - Group related changes; avoid bundling unrelated refactors.
78
+ - Changelog workflow: keep latest released version at top (no `Unreleased`); after publishing, bump version and start a new top section.
79
+ - PRs should summarize scope, note testing performed, and mention any user-facing changes or new flags.
80
+ - PR review flow: when given a PR link, review via `gh pr view`/`gh pr diff` and do **not** change branches.
81
+ - PR review calls: prefer a single `gh pr view --json ...` to batch metadata/comments; run `gh pr diff` only when needed.
82
+ - Before starting a review when a GH Issue/PR is pasted: run `git pull`; if there are local changes or unpushed commits, stop and alert the user before reviewing.
83
+ - Goal: merge PRs. Prefer **rebase** when commits are clean; **squash** when history is messy.
84
+ - PR merge flow: create a temp branch from `main`, merge the PR branch into it (prefer squash unless commit history is important; use rebase/merge when it is). Always try to merge the PR unless it’s truly difficult, then use another approach. If we squash, add the PR author as a co-contributor. Apply fixes, add changelog entry (include PR # + thanks), run full gate before the final commit, commit, merge back to `main`, delete the temp branch, and end on `main`.
85
+ - If you review a PR and later do work on it, land via merge/squash (no direct-main commits) and always add the PR author as a co-contributor.
86
+ - When working on a PR: add a changelog entry with the PR number and thank the contributor.
87
+ - When working on an issue: reference the issue in the changelog entry.
88
+ - When merging a PR: leave a PR comment that explains exactly what we did and include the SHA hashes.
89
+ - When merging a PR from a new contributor: add their avatar to the README “Thanks to all clawtributors” thumbnail list.
90
+ - After merging a PR: run `bun scripts/update-clawtributors.ts` if the contributor is missing, then commit the regenerated README.
91
+
92
+ ## Shorthand Commands
93
+ - `sync`: if working tree is dirty, commit all changes (pick a sensible Conventional Commit message), then `git pull --rebase`; if rebase conflicts and cannot resolve, stop; otherwise `git push`.
94
+
95
+ ### PR Workflow (Review vs Land)
96
+ - **Review mode (PR link only):** read `gh pr view/diff`; **do not** switch branches; **do not** change code.
97
+ - **Landing mode:** create an integration branch from `main`, bring in PR commits (**prefer rebase** for linear history; **merge allowed** when complexity/conflicts make it safer), apply fixes, add changelog (+ thanks + PR #), run full gate **locally before committing** (`pnpm lint && pnpm build && pnpm test`), commit, merge back to `main`, then `git switch main` (never stay on a topic branch after landing). Important: contributor needs to be in git graph after this!
98
+
99
+ ## Security & Configuration Tips
100
+ - Web provider stores creds at `~/.clawdbot/credentials/`; rerun `moltbot login` if logged out.
101
+ - Pi sessions live under `~/.clawdbot/sessions/` by default; the base directory is not configurable.
102
+ - Environment variables: see `~/.profile`.
103
+ - Never commit or publish real phone numbers, videos, or live configuration values. Use obviously fake placeholders in docs, tests, and examples.
104
+ - Release flow: always read `docs/reference/RELEASING.md` and `docs/platforms/mac/release.md` before any release work; do not ask routine questions once those docs answer them.
105
+
106
+ ## Troubleshooting
107
+ - Rebrand/migration issues or legacy config/service warnings: run `moltbot doctor` (see `docs/gateway/doctor.md`).
108
+
109
+ ## Agent-Specific Notes
110
+ - Vocabulary: "makeup" = "mac app".
111
+ - Never edit `node_modules` (global/Homebrew/npm/git installs too). Updates overwrite. Skill notes go in `tools.md` or `AGENTS.md`.
112
+ - Signal: "update fly" => `fly ssh console -a flawd-bot -C "bash -lc 'cd /data/clawd/moltbot && git pull --rebase origin main'"` then `fly machines restart e825232f34d058 -a flawd-bot`.
113
+ - When working on a GitHub Issue or PR, print the full URL at the end of the task.
114
+ - When answering questions, respond with high-confidence answers only: verify in code; do not guess.
115
+ - Never update the Carbon dependency.
116
+ - Any dependency with `pnpm.patchedDependencies` must use an exact version (no `^`/`~`).
117
+ - Patching dependencies (pnpm patches, overrides, or vendored changes) requires explicit approval; do not do this by default.
118
+ - CLI progress: use `src/cli/progress.ts` (`osc-progress` + `@clack/prompts` spinner); don’t hand-roll spinners/bars.
119
+ - Status output: keep tables + ANSI-safe wrapping (`src/terminal/table.ts`); `status --all` = read-only/pasteable, `status --deep` = probes.
120
+ - Gateway currently runs only as the menubar app; there is no separate LaunchAgent/helper label installed. Restart via the Moltbot Mac app or `scripts/restart-mac.sh`; to verify/kill use `launchctl print gui/$UID | grep moltbot` rather than assuming a fixed label. **When debugging on macOS, start/stop the gateway via the app, not ad-hoc tmux sessions; kill any temporary tunnels before handoff.**
121
+ - macOS logs: use `./scripts/clawlog.sh` to query unified logs for the Moltbot subsystem; it supports follow/tail/category filters and expects passwordless sudo for `/usr/bin/log`.
122
+ - If shared guardrails are available locally, review them; otherwise follow this repo's guidance.
123
+ - SwiftUI state management (iOS/macOS): prefer the `Observation` framework (`@Observable`, `@Bindable`) over `ObservableObject`/`@StateObject`; don’t introduce new `ObservableObject` unless required for compatibility, and migrate existing usages when touching related code.
124
+ - Connection providers: when adding a new connection, update every UI surface and docs (macOS app, web UI, mobile if applicable, onboarding/overview docs) and add matching status + configuration forms so provider lists and settings stay in sync.
125
+ - Version locations: `package.json` (CLI), `apps/android/app/build.gradle.kts` (versionName/versionCode), `apps/ios/Sources/Info.plist` + `apps/ios/Tests/Info.plist` (CFBundleShortVersionString/CFBundleVersion), `apps/macos/Sources/Moltbot/Resources/Info.plist` (CFBundleShortVersionString/CFBundleVersion), `docs/install/updating.md` (pinned npm version), `docs/platforms/mac/release.md` (APP_VERSION/APP_BUILD examples), Peekaboo Xcode projects/Info.plists (MARKETING_VERSION/CURRENT_PROJECT_VERSION).
126
+ - **Restart apps:** “restart iOS/Android apps” means rebuild (recompile/install) and relaunch, not just kill/launch.
127
+ - **Device checks:** before testing, verify connected real devices (iOS/Android) before reaching for simulators/emulators.
128
+ - iOS Team ID lookup: `security find-identity -p codesigning -v` → use Apple Development (…) TEAMID. Fallback: `defaults read com.apple.dt.Xcode IDEProvisioningTeamIdentifiers`.
129
+ - A2UI bundle hash: `src/canvas-host/a2ui/.bundle.hash` is auto-generated; ignore unexpected changes, and only regenerate via `pnpm canvas:a2ui:bundle` (or `scripts/bundle-a2ui.sh`) when needed. Commit the hash as a separate commit.
130
+ - Release signing/notary keys are managed outside the repo; follow internal release docs.
131
+ - Notary auth env vars (`APP_STORE_CONNECT_ISSUER_ID`, `APP_STORE_CONNECT_KEY_ID`, `APP_STORE_CONNECT_API_KEY_P8`) are expected in your environment (per internal release docs).
132
+ - **Multi-agent safety:** do **not** create/apply/drop `git stash` entries unless explicitly requested (this includes `git pull --rebase --autostash`). Assume other agents may be working; keep unrelated WIP untouched and avoid cross-cutting state changes.
133
+ - **Multi-agent safety:** when the user says "push", you may `git pull --rebase` to integrate latest changes (never discard other agents' work). When the user says "commit", scope to your changes only. When the user says "commit all", commit everything in grouped chunks.
134
+ - **Multi-agent safety:** do **not** create/remove/modify `git worktree` checkouts (or edit `.worktrees/*`) unless explicitly requested.
135
+ - **Multi-agent safety:** do **not** switch branches / check out a different branch unless explicitly requested.
136
+ - **Multi-agent safety:** running multiple agents is OK as long as each agent has its own session.
137
+ - **Multi-agent safety:** when you see unrecognized files, keep going; focus on your changes and commit only those.
138
+ - Lint/format churn:
139
+ - If staged+unstaged diffs are formatting-only, auto-resolve without asking.
140
+ - If commit/push already requested, auto-stage and include formatting-only follow-ups in the same commit (or a tiny follow-up commit if needed), no extra confirmation.
141
+ - Only ask when changes are semantic (logic/data/behavior).
142
+ - Lobster seam: use the shared CLI palette in `src/terminal/palette.ts` (no hardcoded colors); apply palette to onboarding/config prompts and other TTY UI output as needed.
143
+ - **Multi-agent safety:** focus reports on your edits; avoid guard-rail disclaimers unless truly blocked; when multiple agents touch the same file, continue if safe; end with a brief “other files present” note only if relevant.
144
+ - Bug investigations: read source code of relevant npm dependencies and all related local code before concluding; aim for high-confidence root cause.
145
+ - Code style: add brief comments for tricky logic; keep files under ~500 LOC when feasible (split/refactor as needed).
146
+ - Tool schema guardrails (google-antigravity): avoid `Type.Union` in tool input schemas; no `anyOf`/`oneOf`/`allOf`. Use `stringEnum`/`optionalStringEnum` (Type.Unsafe enum) for string lists, and `Type.Optional(...)` instead of `... | null`. Keep top-level tool schema as `type: "object"` with `properties`.
147
+ - Tool schema guardrails: avoid raw `format` property names in tool schemas; some validators treat `format` as a reserved keyword and reject the schema.
148
+ - When asked to open a “session” file, open the Pi session logs under `~/.clawdbot/agents/<agentId>/sessions/*.jsonl` (use the `agent=<id>` value in the Runtime line of the system prompt; newest unless a specific ID is given), not the default `sessions.json`. If logs are needed from another machine, SSH via Tailscale and read the same path there.
149
+ - Do not rebuild the macOS app over SSH; rebuilds must be run directly on the Mac.
150
+ - Never send streaming/partial replies to external messaging surfaces (WhatsApp, Telegram); only final replies should be delivered there. Streaming/tool events may still go to internal UIs/control channel.
151
+ - Voice wake forwarding tips:
152
+ - Command template should stay `moltbot-mac agent --message "${text}" --thinking low`; `VoiceWakeForwarder` already shell-escapes `${text}`. Don’t add extra quotes.
153
+ - launchd PATH is minimal; ensure the app’s launch agent PATH includes standard system paths plus your pnpm bin (typically `$HOME/Library/pnpm`) so `pnpm`/`moltbot` binaries resolve when invoked via `moltbot-mac`.
154
+ - For manual `moltbot message send` messages that include `!`, use the heredoc pattern noted below to avoid the Bash tool’s escaping.
155
+ - Release guardrails: do not change version numbers without operator’s explicit consent; always ask permission before running any npm publish/release step.
156
+
157
+ ## NPM + 1Password (publish/verify)
158
+ - Use the 1password skill; all `op` commands must run inside a fresh tmux session.
159
+ - Sign in: `eval "$(op signin --account my.1password.com)"` (app unlocked + integration on).
160
+ - OTP: `op read 'op://Private/Npmjs/one-time password?attribute=otp'`.
161
+ - Publish: `npm publish --access public --otp="<otp>"` (run from the package dir).
162
+ - Verify without local npmrc side effects: `npm view <pkg> version --userconfig "$(mktemp)"`.
163
+ - Kill the tmux session after publish.
CHANGELOG.md ADDED
The diff for this file is too large to render. See raw diff
 
CLAUDE.md ADDED
@@ -0,0 +1 @@
 
 
1
+ AGENTS.md
CONTRIBUTING.md ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Contributing to Moltbot
2
+
3
+ Welcome to the lobster tank! 🦞
4
+
5
+ ## Quick Links
6
+ - **GitHub:** https://github.com/moltbot/moltbot
7
+ - **Discord:** https://discord.gg/qkhbAGHRBT
8
+ - **X/Twitter:** [@steipete](https://x.com/steipete) / [@moltbot](https://x.com/moltbot)
9
+
10
+ ## Maintainers
11
+
12
+ - **Peter Steinberger** - Benevolent Dictator
13
+ - GitHub: [@steipete](https://github.com/steipete) · X: [@steipete](https://x.com/steipete)
14
+
15
+ - **Shadow** - Discord + Slack subsystem
16
+ - GitHub: [@thewilloftheshadow](https://github.com/thewilloftheshadow) · X: [@4shad0wed](https://x.com/4shad0wed)
17
+
18
+ - **Jos** - Telegram, API, Nix mode
19
+ - GitHub: [@joshp123](https://github.com/joshp123) · X: [@jjpcodes](https://x.com/jjpcodes)
20
+
21
+ ## How to Contribute
22
+ 1. **Bugs & small fixes** → Open a PR!
23
+ 2. **New features / architecture** → Start a [GitHub Discussion](https://github.com/moltbot/moltbot/discussions) or ask in Discord first
24
+ 3. **Questions** → Discord #setup-help
25
+
26
+ ## Before You PR
27
+ - Test locally with your Moltbot instance
28
+ - Run linter: `npm run lint`
29
+ - Keep PRs focused (one thing per PR)
30
+ - Describe what & why
31
+
32
+ ## AI/Vibe-Coded PRs Welcome! 🤖
33
+
34
+ Built with Codex, Claude, or other AI tools? **Awesome - just mark it!**
35
+
36
+ Please include in your PR:
37
+ - [ ] Mark as AI-assisted in the PR title or description
38
+ - [ ] Note the degree of testing (untested / lightly tested / fully tested)
39
+ - [ ] Include prompts or session logs if possible (super helpful!)
40
+ - [ ] Confirm you understand what the code does
41
+
42
+ AI PRs are first-class citizens here. We just want transparency so reviewers know what to look for.
43
+
44
+ ## Current Focus & Roadmap 🗺
45
+
46
+ We are currently prioritizing:
47
+ - **Stability**: Fixing edge cases in channel connections (WhatsApp/Telegram).
48
+ - **UX**: Improving the onboarding wizard and error messages.
49
+ - **Skills**: Expanding the library of bundled skills and improving the Skill Creation developer experience.
50
+ - **Performance**: Optimizing token usage and compaction logic.
51
+
52
+ Check the [GitHub Issues](https://github.com/moltbot/moltbot/issues) for "good first issue" labels!
Dockerfile ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM node:22-bookworm
2
+
3
+ # Install Bun (required for build scripts)
4
+ RUN curl -fsSL https://bun.sh/install | bash
5
+ ENV PATH="/root/.bun/bin:${PATH}"
6
+
7
+ RUN corepack enable
8
+
9
+ WORKDIR /app
10
+
11
+ ARG CLAWDBOT_DOCKER_APT_PACKAGES=""
12
+ RUN if [ -n "$CLAWDBOT_DOCKER_APT_PACKAGES" ]; then \
13
+ apt-get update && \
14
+ DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends $CLAWDBOT_DOCKER_APT_PACKAGES && \
15
+ apt-get clean && \
16
+ rm -rf /var/lib/apt/lists/* /var/cache/apt/archives/*; \
17
+ fi
18
+
19
+ COPY package.json pnpm-lock.yaml pnpm-workspace.yaml .npmrc ./
20
+ COPY ui/package.json ./ui/package.json
21
+ COPY patches ./patches
22
+ COPY scripts ./scripts
23
+
24
+ RUN pnpm install --frozen-lockfile
25
+
26
+ COPY . .
27
+ RUN pnpm build
28
+ # Force pnpm for UI build (Bun may fail on ARM/Synology architectures)
29
+ ENV CLAWDBOT_PREFER_PNPM=1
30
+ RUN pnpm ui:install
31
+ RUN pnpm ui:build
32
+
33
+ ENV NODE_ENV=production
34
+
35
+ # Security hardening: Run as non-root user
36
+ # The node:22-bookworm image includes a 'node' user (uid 1000)
37
+ # This reduces the attack surface by preventing container escape via root privileges
38
+ USER node
39
+
40
+ CMD ["node", "dist/index.js"]
Dockerfile.sandbox ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM debian:bookworm-slim
2
+
3
+ ENV DEBIAN_FRONTEND=noninteractive
4
+
5
+ RUN apt-get update \
6
+ && apt-get install -y --no-install-recommends \
7
+ bash \
8
+ ca-certificates \
9
+ curl \
10
+ git \
11
+ jq \
12
+ python3 \
13
+ ripgrep \
14
+ && rm -rf /var/lib/apt/lists/*
15
+
16
+ CMD ["sleep", "infinity"]
Dockerfile.sandbox-browser ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM debian:bookworm-slim
2
+
3
+ ENV DEBIAN_FRONTEND=noninteractive
4
+
5
+ RUN apt-get update \
6
+ && apt-get install -y --no-install-recommends \
7
+ bash \
8
+ ca-certificates \
9
+ chromium \
10
+ curl \
11
+ fonts-liberation \
12
+ fonts-noto-color-emoji \
13
+ git \
14
+ jq \
15
+ novnc \
16
+ python3 \
17
+ socat \
18
+ websockify \
19
+ x11vnc \
20
+ xvfb \
21
+ && rm -rf /var/lib/apt/lists/*
22
+
23
+ COPY scripts/sandbox-browser-entrypoint.sh /usr/local/bin/moltbot-sandbox-browser
24
+ RUN chmod +x /usr/local/bin/moltbot-sandbox-browser
25
+
26
+ EXPOSE 9222 5900 6080
27
+
28
+ CMD ["moltbot-sandbox-browser"]
LICENSE ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Peter Steinberger
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
README-header.png ADDED

Git LFS Details

  • SHA256: 31a53674902787b6665ee3cb9a01e50f66352004db4f023e4e6506b6c07fb80a
  • Pointer size: 132 Bytes
  • Size of remote file: 1.41 MB
README.md CHANGED
@@ -1,12 +1,512 @@
1
- ---
2
- title: Melbot
3
- emoji: 😻
4
- colorFrom: green
5
- colorTo: pink
6
- sdk: docker
7
- pinned: false
8
- license: mit
9
- short_description: melbot
10
- ---
11
-
12
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 🦞 Clawdbot — Personal AI Assistant
2
+
3
+ <p align="center">
4
+ <img src="https://raw.githubusercontent.com/clawdbot/clawdbot/main/docs/whatsapp-clawd.jpg" alt="Clawdbot" width="400">
5
+ </p>
6
+
7
+ <p align="center">
8
+ <strong>EXFOLIATE! EXFOLIATE!</strong>
9
+ </p>
10
+
11
+ <p align="center">
12
+ <a href="https://github.com/clawdbot/clawdbot/actions/workflows/ci.yml?branch=main"><img src="https://img.shields.io/github/actions/workflow/status/clawdbot/clawdbot/ci.yml?branch=main&style=for-the-badge" alt="CI status"></a>
13
+ <a href="https://github.com/clawdbot/clawdbot/releases"><img src="https://img.shields.io/github/v/release/clawdbot/clawdbot?include_prereleases&style=for-the-badge" alt="GitHub release"></a>
14
+ <a href="https://deepwiki.com/clawdbot/clawdbot"><img src="https://img.shields.io/badge/DeepWiki-clawdbot-111111?style=for-the-badge" alt="DeepWiki"></a>
15
+ <a href="https://discord.gg/clawd"><img src="https://img.shields.io/discord/1456350064065904867?label=Discord&logo=discord&logoColor=white&color=5865F2&style=for-the-badge" alt="Discord"></a>
16
+ <a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-blue.svg?style=for-the-badge" alt="MIT License"></a>
17
+ </p>
18
+
19
+ **Clawdbot** is a *personal AI assistant* you run on your own devices.
20
+ It answers you on the channels you already use (WhatsApp, Telegram, Slack, Discord, Google Chat, Signal, iMessage, Microsoft Teams, WebChat), plus extension channels like BlueBubbles, Matrix, Zalo, and Zalo Personal. It can speak and listen on macOS/iOS/Android, and can render a live Canvas you control. The Gateway is just the control plane — the product is the assistant.
21
+
22
+ If you want a personal, single-user assistant that feels local, fast, and always-on, this is it.
23
+
24
+ [Website](https://molt.bot) · [Docs](https://docs.molt.bot) · [Getting Started](https://docs.molt.bot/start/getting-started) · [Updating](https://docs.molt.bot/install/updating) · [Showcase](https://docs.molt.bot/start/showcase) · [FAQ](https://docs.molt.bot/start/faq) · [Wizard](https://docs.molt.bot/start/wizard) · [Nix](https://github.com/clawdbot/nix-clawdbot) · [Docker](https://docs.molt.bot/install/docker) · [Discord](https://discord.gg/clawd)
25
+
26
+ Preferred setup: run the onboarding wizard (`clawdbot onboard`). It walks through gateway, workspace, channels, and skills. The CLI wizard is the recommended path and works on **macOS, Linux, and Windows (via WSL2; strongly recommended)**.
27
+ Works with npm, pnpm, or bun.
28
+ New install? Start here: [Getting started](https://docs.molt.bot/start/getting-started)
29
+
30
+ **Subscriptions (OAuth):**
31
+ - **[Anthropic](https://www.anthropic.com/)** (Claude Pro/Max)
32
+ - **[OpenAI](https://openai.com/)** (ChatGPT/Codex)
33
+
34
+ Model note: while any model is supported, I strongly recommend **Anthropic Pro/Max (100/200) + Opus 4.5** for long‑context strength and better prompt‑injection resistance. See [Onboarding](https://docs.molt.bot/start/onboarding).
35
+
36
+ ## Models (selection + auth)
37
+
38
+ - Models config + CLI: [Models](https://docs.molt.bot/concepts/models)
39
+ - Auth profile rotation (OAuth vs API keys) + fallbacks: [Model failover](https://docs.molt.bot/concepts/model-failover)
40
+
41
+ ## Install (recommended)
42
+
43
+ Runtime: **Node ≥22**.
44
+
45
+ ```bash
46
+ npm install -g moltbot@latest
47
+ # or: pnpm add -g moltbot@latest
48
+
49
+ moltbot onboard --install-daemon
50
+ ```
51
+
52
+ The wizard installs the Gateway daemon (launchd/systemd user service) so it stays running.
53
+ Legacy note: `clawdbot` remains available as a compatibility shim.
54
+
55
+ ## Quick start (TL;DR)
56
+
57
+ Runtime: **Node ≥22**.
58
+
59
+ Full beginner guide (auth, pairing, channels): [Getting started](https://docs.molt.bot/start/getting-started)
60
+
61
+ ```bash
62
+ moltbot onboard --install-daemon
63
+
64
+ moltbot gateway --port 18789 --verbose
65
+
66
+ # Send a message
67
+ moltbot message send --to +1234567890 --message "Hello from Moltbot"
68
+
69
+ # Talk to the assistant (optionally deliver back to any connected channel: WhatsApp/Telegram/Slack/Discord/Google Chat/Signal/iMessage/BlueBubbles/Microsoft Teams/Matrix/Zalo/Zalo Personal/WebChat)
70
+ moltbot agent --message "Ship checklist" --thinking high
71
+ ```
72
+
73
+ Upgrading? [Updating guide](https://docs.molt.bot/install/updating) (and run `moltbot doctor`).
74
+
75
+ ## Development channels
76
+
77
+ - **stable**: tagged releases (`vYYYY.M.D` or `vYYYY.M.D-<patch>`), npm dist-tag `latest`.
78
+ - **beta**: prerelease tags (`vYYYY.M.D-beta.N`), npm dist-tag `beta` (macOS app may be missing).
79
+ - **dev**: moving head of `main`, npm dist-tag `dev` (when published).
80
+
81
+ Switch channels (git + npm): `clawdbot update --channel stable|beta|dev`.
82
+ Details: [Development channels](https://docs.molt.bot/install/development-channels).
83
+
84
+ ## From source (development)
85
+
86
+ Prefer `pnpm` for builds from source. Bun is optional for running TypeScript directly.
87
+
88
+ ```bash
89
+ git clone https://github.com/clawdbot/clawdbot.git
90
+ cd clawdbot
91
+
92
+ pnpm install
93
+ pnpm ui:build # auto-installs UI deps on first run
94
+ pnpm build
95
+
96
+ pnpm moltbot onboard --install-daemon
97
+
98
+ # Dev loop (auto-reload on TS changes)
99
+ pnpm gateway:watch
100
+ ```
101
+
102
+ Note: `pnpm moltbot ...` runs TypeScript directly (via `tsx`). `pnpm build` produces `dist/` for running via Node / the packaged `moltbot` binary.
103
+
104
+ ## Security defaults (DM access)
105
+
106
+ Clawdbot connects to real messaging surfaces. Treat inbound DMs as **untrusted input**.
107
+
108
+ Full security guide: [Security](https://docs.molt.bot/gateway/security)
109
+
110
+ Default behavior on Telegram/WhatsApp/Signal/iMessage/Microsoft Teams/Discord/Google Chat/Slack:
111
+ - **DM pairing** (`dmPolicy="pairing"` / `channels.discord.dm.policy="pairing"` / `channels.slack.dm.policy="pairing"`): unknown senders receive a short pairing code and the bot does not process their message.
112
+ - Approve with: `clawdbot pairing approve <channel> <code>` (then the sender is added to a local allowlist store).
113
+ - Public inbound DMs require an explicit opt-in: set `dmPolicy="open"` and include `"*"` in the channel allowlist (`allowFrom` / `channels.discord.dm.allowFrom` / `channels.slack.dm.allowFrom`).
114
+
115
+ Run `clawdbot doctor` to surface risky/misconfigured DM policies.
116
+
117
+ ## Highlights
118
+
119
+ - **[Local-first Gateway](https://docs.molt.bot/gateway)** — single control plane for sessions, channels, tools, and events.
120
+ - **[Multi-channel inbox](https://docs.molt.bot/channels)** — WhatsApp, Telegram, Slack, Discord, Google Chat, Signal, iMessage, BlueBubbles, Microsoft Teams, Matrix, Zalo, Zalo Personal, WebChat, macOS, iOS/Android.
121
+ - **[Multi-agent routing](https://docs.molt.bot/gateway/configuration)** — route inbound channels/accounts/peers to isolated agents (workspaces + per-agent sessions).
122
+ - **[Voice Wake](https://docs.molt.bot/nodes/voicewake) + [Talk Mode](https://docs.molt.bot/nodes/talk)** — always-on speech for macOS/iOS/Android with ElevenLabs.
123
+ - **[Live Canvas](https://docs.molt.bot/platforms/mac/canvas)** — agent-driven visual workspace with [A2UI](https://docs.molt.bot/platforms/mac/canvas#canvas-a2ui).
124
+ - **[First-class tools](https://docs.molt.bot/tools)** — browser, canvas, nodes, cron, sessions, and Discord/Slack actions.
125
+ - **[Companion apps](https://docs.molt.bot/platforms/macos)** — macOS menu bar app + iOS/Android [nodes](https://docs.molt.bot/nodes).
126
+ - **[Onboarding](https://docs.molt.bot/start/wizard) + [skills](https://docs.molt.bot/tools/skills)** — wizard-driven setup with bundled/managed/workspace skills.
127
+
128
+ ## Star History
129
+
130
+ [![Star History Chart](https://api.star-history.com/svg?repos=clawdbot/clawdbot&type=date&legend=top-left)](https://www.star-history.com/#clawdbot/clawdbot&type=date&legend=top-left)
131
+
132
+ ## Everything we built so far
133
+
134
+ ### Core platform
135
+ - [Gateway WS control plane](https://docs.molt.bot/gateway) with sessions, presence, config, cron, webhooks, [Control UI](https://docs.molt.bot/web), and [Canvas host](https://docs.molt.bot/platforms/mac/canvas#canvas-a2ui).
136
+ - [CLI surface](https://docs.molt.bot/tools/agent-send): gateway, agent, send, [wizard](https://docs.molt.bot/start/wizard), and [doctor](https://docs.molt.bot/gateway/doctor).
137
+ - [Pi agent runtime](https://docs.molt.bot/concepts/agent) in RPC mode with tool streaming and block streaming.
138
+ - [Session model](https://docs.molt.bot/concepts/session): `main` for direct chats, group isolation, activation modes, queue modes, reply-back. Group rules: [Groups](https://docs.molt.bot/concepts/groups).
139
+ - [Media pipeline](https://docs.molt.bot/nodes/images): images/audio/video, transcription hooks, size caps, temp file lifecycle. Audio details: [Audio](https://docs.molt.bot/nodes/audio).
140
+
141
+ ### Channels
142
+ - [Channels](https://docs.molt.bot/channels): [WhatsApp](https://docs.molt.bot/channels/whatsapp) (Baileys), [Telegram](https://docs.molt.bot/channels/telegram) (grammY), [Slack](https://docs.molt.bot/channels/slack) (Bolt), [Discord](https://docs.molt.bot/channels/discord) (discord.js), [Google Chat](https://docs.molt.bot/channels/googlechat) (Chat API), [Signal](https://docs.molt.bot/channels/signal) (signal-cli), [iMessage](https://docs.molt.bot/channels/imessage) (imsg), [BlueBubbles](https://docs.molt.bot/channels/bluebubbles) (extension), [Microsoft Teams](https://docs.molt.bot/channels/msteams) (extension), [Matrix](https://docs.molt.bot/channels/matrix) (extension), [Zalo](https://docs.molt.bot/channels/zalo) (extension), [Zalo Personal](https://docs.molt.bot/channels/zalouser) (extension), [WebChat](https://docs.molt.bot/web/webchat).
143
+ - [Group routing](https://docs.molt.bot/concepts/group-messages): mention gating, reply tags, per-channel chunking and routing. Channel rules: [Channels](https://docs.molt.bot/channels).
144
+
145
+ ### Apps + nodes
146
+ - [macOS app](https://docs.molt.bot/platforms/macos): menu bar control plane, [Voice Wake](https://docs.molt.bot/nodes/voicewake)/PTT, [Talk Mode](https://docs.molt.bot/nodes/talk) overlay, [WebChat](https://docs.molt.bot/web/webchat), debug tools, [remote gateway](https://docs.molt.bot/gateway/remote) control.
147
+ - [iOS node](https://docs.molt.bot/platforms/ios): [Canvas](https://docs.molt.bot/platforms/mac/canvas), [Voice Wake](https://docs.molt.bot/nodes/voicewake), [Talk Mode](https://docs.molt.bot/nodes/talk), camera, screen recording, Bonjour pairing.
148
+ - [Android node](https://docs.molt.bot/platforms/android): [Canvas](https://docs.molt.bot/platforms/mac/canvas), [Talk Mode](https://docs.molt.bot/nodes/talk), camera, screen recording, optional SMS.
149
+ - [macOS node mode](https://docs.molt.bot/nodes): system.run/notify + canvas/camera exposure.
150
+
151
+ ### Tools + automation
152
+ - [Browser control](https://docs.molt.bot/tools/browser): dedicated clawd Chrome/Chromium, snapshots, actions, uploads, profiles.
153
+ - [Canvas](https://docs.molt.bot/platforms/mac/canvas): [A2UI](https://docs.molt.bot/platforms/mac/canvas#canvas-a2ui) push/reset, eval, snapshot.
154
+ - [Nodes](https://docs.molt.bot/nodes): camera snap/clip, screen record, [location.get](https://docs.molt.bot/nodes/location-command), notifications.
155
+ - [Cron + wakeups](https://docs.molt.bot/automation/cron-jobs); [webhooks](https://docs.molt.bot/automation/webhook); [Gmail Pub/Sub](https://docs.molt.bot/automation/gmail-pubsub).
156
+ - [Skills platform](https://docs.molt.bot/tools/skills): bundled, managed, and workspace skills with install gating + UI.
157
+
158
+ ### Runtime + safety
159
+ - [Channel routing](https://docs.molt.bot/concepts/channel-routing), [retry policy](https://docs.molt.bot/concepts/retry), and [streaming/chunking](https://docs.molt.bot/concepts/streaming).
160
+ - [Presence](https://docs.molt.bot/concepts/presence), [typing indicators](https://docs.molt.bot/concepts/typing-indicators), and [usage tracking](https://docs.molt.bot/concepts/usage-tracking).
161
+ - [Models](https://docs.molt.bot/concepts/models), [model failover](https://docs.molt.bot/concepts/model-failover), and [session pruning](https://docs.molt.bot/concepts/session-pruning).
162
+ - [Security](https://docs.molt.bot/gateway/security) and [troubleshooting](https://docs.molt.bot/channels/troubleshooting).
163
+
164
+ ### Ops + packaging
165
+ - [Control UI](https://docs.molt.bot/web) + [WebChat](https://docs.molt.bot/web/webchat) served directly from the Gateway.
166
+ - [Tailscale Serve/Funnel](https://docs.molt.bot/gateway/tailscale) or [SSH tunnels](https://docs.molt.bot/gateway/remote) with token/password auth.
167
+ - [Nix mode](https://docs.molt.bot/install/nix) for declarative config; [Docker](https://docs.molt.bot/install/docker)-based installs.
168
+ - [Doctor](https://docs.molt.bot/gateway/doctor) migrations, [logging](https://docs.molt.bot/logging).
169
+
170
+ ## How it works (short)
171
+
172
+ ```
173
+ WhatsApp / Telegram / Slack / Discord / Google Chat / Signal / iMessage / BlueBubbles / Microsoft Teams / Matrix / Zalo / Zalo Personal / WebChat
174
+
175
+
176
+ ┌───────────────────────────────┐
177
+ │ Gateway │
178
+ │ (control plane) │
179
+ │ ws://127.0.0.1:18789 │
180
+ └──────────────┬────────────────┘
181
+
182
+ ├─ Pi agent (RPC)
183
+ ├─ CLI (clawdbot …)
184
+ ├─ WebChat UI
185
+ ├─ macOS app
186
+ └─ iOS / Android nodes
187
+ ```
188
+
189
+ ## Key subsystems
190
+
191
+ - **[Gateway WebSocket network](https://docs.molt.bot/concepts/architecture)** — single WS control plane for clients, tools, and events (plus ops: [Gateway runbook](https://docs.molt.bot/gateway)).
192
+ - **[Tailscale exposure](https://docs.molt.bot/gateway/tailscale)** — Serve/Funnel for the Gateway dashboard + WS (remote access: [Remote](https://docs.molt.bot/gateway/remote)).
193
+ - **[Browser control](https://docs.molt.bot/tools/browser)** — clawd‑managed Chrome/Chromium with CDP control.
194
+ - **[Canvas + A2UI](https://docs.molt.bot/platforms/mac/canvas)** — agent‑driven visual workspace (A2UI host: [Canvas/A2UI](https://docs.molt.bot/platforms/mac/canvas#canvas-a2ui)).
195
+ - **[Voice Wake](https://docs.molt.bot/nodes/voicewake) + [Talk Mode](https://docs.molt.bot/nodes/talk)** — always‑on speech and continuous conversation.
196
+ - **[Nodes](https://docs.molt.bot/nodes)** — Canvas, camera snap/clip, screen record, `location.get`, notifications, plus macOS‑only `system.run`/`system.notify`.
197
+
198
+ ## Tailscale access (Gateway dashboard)
199
+
200
+ Clawdbot can auto-configure Tailscale **Serve** (tailnet-only) or **Funnel** (public) while the Gateway stays bound to loopback. Configure `gateway.tailscale.mode`:
201
+
202
+ - `off`: no Tailscale automation (default).
203
+ - `serve`: tailnet-only HTTPS via `tailscale serve` (uses Tailscale identity headers by default).
204
+ - `funnel`: public HTTPS via `tailscale funnel` (requires shared password auth).
205
+
206
+ Notes:
207
+ - `gateway.bind` must stay `loopback` when Serve/Funnel is enabled (Clawdbot enforces this).
208
+ - Serve can be forced to require a password by setting `gateway.auth.mode: "password"` or `gateway.auth.allowTailscale: false`.
209
+ - Funnel refuses to start unless `gateway.auth.mode: "password"` is set.
210
+ - Optional: `gateway.tailscale.resetOnExit` to undo Serve/Funnel on shutdown.
211
+
212
+ Details: [Tailscale guide](https://docs.molt.bot/gateway/tailscale) · [Web surfaces](https://docs.molt.bot/web)
213
+
214
+ ## Remote Gateway (Linux is great)
215
+
216
+ It’s perfectly fine to run the Gateway on a small Linux instance. Clients (macOS app, CLI, WebChat) can connect over **Tailscale Serve/Funnel** or **SSH tunnels**, and you can still pair device nodes (macOS/iOS/Android) to execute device‑local actions when needed.
217
+
218
+ - **Gateway host** runs the exec tool and channel connections by default.
219
+ - **Device nodes** run device‑local actions (`system.run`, camera, screen recording, notifications) via `node.invoke`.
220
+ In short: exec runs where the Gateway lives; device actions run where the device lives.
221
+
222
+ Details: [Remote access](https://docs.molt.bot/gateway/remote) · [Nodes](https://docs.molt.bot/nodes) · [Security](https://docs.molt.bot/gateway/security)
223
+
224
+ ## macOS permissions via the Gateway protocol
225
+
226
+ The macOS app can run in **node mode** and advertises its capabilities + permission map over the Gateway WebSocket (`node.list` / `node.describe`). Clients can then execute local actions via `node.invoke`:
227
+
228
+ - `system.run` runs a local command and returns stdout/stderr/exit code; set `needsScreenRecording: true` to require screen-recording permission (otherwise you’ll get `PERMISSION_MISSING`).
229
+ - `system.notify` posts a user notification and fails if notifications are denied.
230
+ - `canvas.*`, `camera.*`, `screen.record`, and `location.get` are also routed via `node.invoke` and follow TCC permission status.
231
+
232
+ Elevated bash (host permissions) is separate from macOS TCC:
233
+
234
+ - Use `/elevated on|off` to toggle per‑session elevated access when enabled + allowlisted.
235
+ - Gateway persists the per‑session toggle via `sessions.patch` (WS method) alongside `thinkingLevel`, `verboseLevel`, `model`, `sendPolicy`, and `groupActivation`.
236
+
237
+ Details: [Nodes](https://docs.molt.bot/nodes) · [macOS app](https://docs.molt.bot/platforms/macos) · [Gateway protocol](https://docs.molt.bot/concepts/architecture)
238
+
239
+ ## Agent to Agent (sessions_* tools)
240
+
241
+ - Use these to coordinate work across sessions without jumping between chat surfaces.
242
+ - `sessions_list` — discover active sessions (agents) and their metadata.
243
+ - `sessions_history` — fetch transcript logs for a session.
244
+ - `sessions_send` — message another session; optional reply‑back ping‑pong + announce step (`REPLY_SKIP`, `ANNOUNCE_SKIP`).
245
+
246
+ Details: [Session tools](https://docs.molt.bot/concepts/session-tool)
247
+
248
+ ## Skills registry (ClawdHub)
249
+
250
+ ClawdHub is a minimal skill registry. With ClawdHub enabled, the agent can search for skills automatically and pull in new ones as needed.
251
+
252
+ [ClawdHub](https://ClawdHub.com)
253
+
254
+ ## Chat commands
255
+
256
+ Send these in WhatsApp/Telegram/Slack/Google Chat/Microsoft Teams/WebChat (group commands are owner-only):
257
+
258
+ - `/status` — compact session status (model + tokens, cost when available)
259
+ - `/new` or `/reset` — reset the session
260
+ - `/compact` — compact session context (summary)
261
+ - `/think <level>` — off|minimal|low|medium|high|xhigh (GPT-5.2 + Codex models only)
262
+ - `/verbose on|off`
263
+ - `/usage off|tokens|full` — per-response usage footer
264
+ - `/restart` — restart the gateway (owner-only in groups)
265
+ - `/activation mention|always` — group activation toggle (groups only)
266
+
267
+ ## Apps (optional)
268
+
269
+ The Gateway alone delivers a great experience. All apps are optional and add extra features.
270
+
271
+ If you plan to build/run companion apps, follow the platform runbooks below.
272
+
273
+ ### macOS (Clawdbot.app) (optional)
274
+
275
+ - Menu bar control for the Gateway and health.
276
+ - Voice Wake + push-to-talk overlay.
277
+ - WebChat + debug tools.
278
+ - Remote gateway control over SSH.
279
+
280
+ Note: signed builds required for macOS permissions to stick across rebuilds (see `docs/mac/permissions.md`).
281
+
282
+ ### iOS node (optional)
283
+
284
+ - Pairs as a node via the Bridge.
285
+ - Voice trigger forwarding + Canvas surface.
286
+ - Controlled via `clawdbot nodes …`.
287
+
288
+ Runbook: [iOS connect](https://docs.molt.bot/platforms/ios).
289
+
290
+ ### Android node (optional)
291
+
292
+ - Pairs via the same Bridge + pairing flow as iOS.
293
+ - Exposes Canvas, Camera, and Screen capture commands.
294
+ - Runbook: [Android connect](https://docs.molt.bot/platforms/android).
295
+
296
+ ## Agent workspace + skills
297
+
298
+ - Workspace root: `~/clawd` (configurable via `agents.defaults.workspace`).
299
+ - Injected prompt files: `AGENTS.md`, `SOUL.md`, `TOOLS.md`.
300
+ - Skills: `~/clawd/skills/<skill>/SKILL.md`.
301
+
302
+ ## Configuration
303
+
304
+ Minimal `~/.clawdbot/clawdbot.json` (model + defaults):
305
+
306
+ ```json5
307
+ {
308
+ agent: {
309
+ model: "anthropic/claude-opus-4-5"
310
+ }
311
+ }
312
+ ```
313
+
314
+ [Full configuration reference (all keys + examples).](https://docs.molt.bot/gateway/configuration)
315
+
316
+ ## Security model (important)
317
+
318
+ - **Default:** tools run on the host for the **main** session, so the agent has full access when it’s just you.
319
+ - **Group/channel safety:** set `agents.defaults.sandbox.mode: "non-main"` to run **non‑main sessions** (groups/channels) inside per‑session Docker sandboxes; bash then runs in Docker for those sessions.
320
+ - **Sandbox defaults:** allowlist `bash`, `process`, `read`, `write`, `edit`, `sessions_list`, `sessions_history`, `sessions_send`, `sessions_spawn`; denylist `browser`, `canvas`, `nodes`, `cron`, `discord`, `gateway`.
321
+
322
+ Details: [Security guide](https://docs.molt.bot/gateway/security) · [Docker + sandboxing](https://docs.molt.bot/install/docker) · [Sandbox config](https://docs.molt.bot/gateway/configuration)
323
+
324
+ ### [WhatsApp](https://docs.molt.bot/channels/whatsapp)
325
+
326
+ - Link the device: `pnpm clawdbot channels login` (stores creds in `~/.clawdbot/credentials`).
327
+ - Allowlist who can talk to the assistant via `channels.whatsapp.allowFrom`.
328
+ - If `channels.whatsapp.groups` is set, it becomes a group allowlist; include `"*"` to allow all.
329
+
330
+ ### [Telegram](https://docs.molt.bot/channels/telegram)
331
+
332
+ - Set `TELEGRAM_BOT_TOKEN` or `channels.telegram.botToken` (env wins).
333
+ - Optional: set `channels.telegram.groups` (with `channels.telegram.groups."*".requireMention`); when set, it is a group allowlist (include `"*"` to allow all). Also `channels.telegram.allowFrom` or `channels.telegram.webhookUrl` as needed.
334
+
335
+ ```json5
336
+ {
337
+ channels: {
338
+ telegram: {
339
+ botToken: "123456:ABCDEF"
340
+ }
341
+ }
342
+ }
343
+ ```
344
+
345
+ ### [Slack](https://docs.molt.bot/channels/slack)
346
+
347
+ - Set `SLACK_BOT_TOKEN` + `SLACK_APP_TOKEN` (or `channels.slack.botToken` + `channels.slack.appToken`).
348
+
349
+ ### [Discord](https://docs.molt.bot/channels/discord)
350
+
351
+ - Set `DISCORD_BOT_TOKEN` or `channels.discord.token` (env wins).
352
+ - Optional: set `commands.native`, `commands.text`, or `commands.useAccessGroups`, plus `channels.discord.dm.allowFrom`, `channels.discord.guilds`, or `channels.discord.mediaMaxMb` as needed.
353
+
354
+ ```json5
355
+ {
356
+ channels: {
357
+ discord: {
358
+ token: "1234abcd"
359
+ }
360
+ }
361
+ }
362
+ ```
363
+
364
+ ### [Signal](https://docs.molt.bot/channels/signal)
365
+
366
+ - Requires `signal-cli` and a `channels.signal` config section.
367
+
368
+ ### [iMessage](https://docs.molt.bot/channels/imessage)
369
+
370
+ - macOS only; Messages must be signed in.
371
+ - If `channels.imessage.groups` is set, it becomes a group allowlist; include `"*"` to allow all.
372
+
373
+ ### [Microsoft Teams](https://docs.molt.bot/channels/msteams)
374
+
375
+ - Configure a Teams app + Bot Framework, then add a `msteams` config section.
376
+ - Allowlist who can talk via `msteams.allowFrom`; group access via `msteams.groupAllowFrom` or `msteams.groupPolicy: "open"`.
377
+
378
+ ### [WebChat](https://docs.molt.bot/web/webchat)
379
+
380
+ - Uses the Gateway WebSocket; no separate WebChat port/config.
381
+
382
+ Browser control (optional):
383
+
384
+ ```json5
385
+ {
386
+ browser: {
387
+ enabled: true,
388
+ color: "#FF4500"
389
+ }
390
+ }
391
+ ```
392
+
393
+ ## Docs
394
+
395
+ Use these when you’re past the onboarding flow and want the deeper reference.
396
+ - [Start with the docs index for navigation and “what’s where.”](https://docs.molt.bot)
397
+ - [Read the architecture overview for the gateway + protocol model.](https://docs.molt.bot/concepts/architecture)
398
+ - [Use the full configuration reference when you need every key and example.](https://docs.molt.bot/gateway/configuration)
399
+ - [Run the Gateway by the book with the operational runbook.](https://docs.molt.bot/gateway)
400
+ - [Learn how the Control UI/Web surfaces work and how to expose them safely.](https://docs.molt.bot/web)
401
+ - [Understand remote access over SSH tunnels or tailnets.](https://docs.molt.bot/gateway/remote)
402
+ - [Follow the onboarding wizard flow for a guided setup.](https://docs.molt.bot/start/wizard)
403
+ - [Wire external triggers via the webhook surface.](https://docs.molt.bot/automation/webhook)
404
+ - [Set up Gmail Pub/Sub triggers.](https://docs.molt.bot/automation/gmail-pubsub)
405
+ - [Learn the macOS menu bar companion details.](https://docs.molt.bot/platforms/mac/menu-bar)
406
+ - [Platform guides: Windows (WSL2)](https://docs.molt.bot/platforms/windows), [Linux](https://docs.molt.bot/platforms/linux), [macOS](https://docs.molt.bot/platforms/macos), [iOS](https://docs.molt.bot/platforms/ios), [Android](https://docs.molt.bot/platforms/android)
407
+ - [Debug common failures with the troubleshooting guide.](https://docs.molt.bot/channels/troubleshooting)
408
+ - [Review security guidance before exposing anything.](https://docs.molt.bot/gateway/security)
409
+
410
+ ## Advanced docs (discovery + control)
411
+
412
+ - [Discovery + transports](https://docs.molt.bot/gateway/discovery)
413
+ - [Bonjour/mDNS](https://docs.molt.bot/gateway/bonjour)
414
+ - [Gateway pairing](https://docs.molt.bot/gateway/pairing)
415
+ - [Remote gateway README](https://docs.molt.bot/gateway/remote-gateway-readme)
416
+ - [Control UI](https://docs.molt.bot/web/control-ui)
417
+ - [Dashboard](https://docs.molt.bot/web/dashboard)
418
+
419
+ ## Operations & troubleshooting
420
+
421
+ - [Health checks](https://docs.molt.bot/gateway/health)
422
+ - [Gateway lock](https://docs.molt.bot/gateway/gateway-lock)
423
+ - [Background process](https://docs.molt.bot/gateway/background-process)
424
+ - [Browser troubleshooting (Linux)](https://docs.molt.bot/tools/browser-linux-troubleshooting)
425
+ - [Logging](https://docs.molt.bot/logging)
426
+
427
+ ## Deep dives
428
+
429
+ - [Agent loop](https://docs.molt.bot/concepts/agent-loop)
430
+ - [Presence](https://docs.molt.bot/concepts/presence)
431
+ - [TypeBox schemas](https://docs.molt.bot/concepts/typebox)
432
+ - [RPC adapters](https://docs.molt.bot/reference/rpc)
433
+ - [Queue](https://docs.molt.bot/concepts/queue)
434
+
435
+ ## Workspace & skills
436
+
437
+ - [Skills config](https://docs.molt.bot/tools/skills-config)
438
+ - [Default AGENTS](https://docs.molt.bot/reference/AGENTS.default)
439
+ - [Templates: AGENTS](https://docs.molt.bot/reference/templates/AGENTS)
440
+ - [Templates: BOOTSTRAP](https://docs.molt.bot/reference/templates/BOOTSTRAP)
441
+ - [Templates: IDENTITY](https://docs.molt.bot/reference/templates/IDENTITY)
442
+ - [Templates: SOUL](https://docs.molt.bot/reference/templates/SOUL)
443
+ - [Templates: TOOLS](https://docs.molt.bot/reference/templates/TOOLS)
444
+ - [Templates: USER](https://docs.molt.bot/reference/templates/USER)
445
+
446
+ ## Platform internals
447
+
448
+ - [macOS dev setup](https://docs.molt.bot/platforms/mac/dev-setup)
449
+ - [macOS menu bar](https://docs.molt.bot/platforms/mac/menu-bar)
450
+ - [macOS voice wake](https://docs.molt.bot/platforms/mac/voicewake)
451
+ - [iOS node](https://docs.molt.bot/platforms/ios)
452
+ - [Android node](https://docs.molt.bot/platforms/android)
453
+ - [Windows (WSL2)](https://docs.molt.bot/platforms/windows)
454
+ - [Linux app](https://docs.molt.bot/platforms/linux)
455
+
456
+ ## Email hooks (Gmail)
457
+
458
+ - [docs.molt.bot/gmail-pubsub](https://docs.molt.bot/automation/gmail-pubsub)
459
+
460
+ ## Clawd
461
+
462
+ Clawdbot was built for **Clawd**, a space lobster AI assistant. 🦞
463
+ by Peter Steinberger and the community.
464
+
465
+ - [clawd.me](https://clawd.me)
466
+ - [soul.md](https://soul.md)
467
+ - [steipete.me](https://steipete.me)
468
+
469
+ ## Community
470
+
471
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines, maintainers, and how to submit PRs.
472
+ AI/vibe-coded PRs welcome! 🤖
473
+
474
+ Special thanks to [Mario Zechner](https://mariozechner.at/) for his support and for
475
+ [pi-mono](https://github.com/badlogic/pi-mono).
476
+
477
+ Thanks to all clawtributors:
478
+
479
+ <p align="left">
480
+ <a href="https://github.com/steipete"><img src="https://avatars.githubusercontent.com/u/58493?v=4&s=48" width="48" height="48" alt="steipete" title="steipete"/></a> <a href="https://github.com/plum-dawg"><img src="https://avatars.githubusercontent.com/u/5909950?v=4&s=48" width="48" height="48" alt="plum-dawg" title="plum-dawg"/></a> <a href="https://github.com/bohdanpodvirnyi"><img src="https://avatars.githubusercontent.com/u/31819391?v=4&s=48" width="48" height="48" alt="bohdanpodvirnyi" title="bohdanpodvirnyi"/></a> <a href="https://github.com/iHildy"><img src="https://avatars.githubusercontent.com/u/25069719?v=4&s=48" width="48" height="48" alt="iHildy" title="iHildy"/></a> <a href="https://github.com/jaydenfyi"><img src="https://avatars.githubusercontent.com/u/213395523?v=4&s=48" width="48" height="48" alt="jaydenfyi" title="jaydenfyi"/></a> <a href="https://github.com/joaohlisboa"><img src="https://avatars.githubusercontent.com/u/8200873?v=4&s=48" width="48" height="48" alt="joaohlisboa" title="joaohlisboa"/></a> <a href="https://github.com/mneves75"><img src="https://avatars.githubusercontent.com/u/2423436?v=4&s=48" width="48" height="48" alt="mneves75" title="mneves75"/></a> <a href="https://github.com/MatthieuBizien"><img src="https://avatars.githubusercontent.com/u/173090?v=4&s=48" width="48" height="48" alt="MatthieuBizien" title="MatthieuBizien"/></a> <a href="https://github.com/MaudeBot"><img src="https://avatars.githubusercontent.com/u/255777700?v=4&s=48" width="48" height="48" alt="MaudeBot" title="MaudeBot"/></a> <a href="https://github.com/Glucksberg"><img src="https://avatars.githubusercontent.com/u/80581902?v=4&s=48" width="48" height="48" alt="Glucksberg" title="Glucksberg"/></a>
481
+ <a href="https://github.com/rahthakor"><img src="https://avatars.githubusercontent.com/u/8470553?v=4&s=48" width="48" height="48" alt="rahthakor" title="rahthakor"/></a> <a href="https://github.com/vrknetha"><img src="https://avatars.githubusercontent.com/u/20596261?v=4&s=48" width="48" height="48" alt="vrknetha" title="vrknetha"/></a> <a href="https://github.com/radek-paclt"><img src="https://avatars.githubusercontent.com/u/50451445?v=4&s=48" width="48" height="48" alt="radek-paclt" title="radek-paclt"/></a> <a href="https://github.com/tobiasbischoff"><img src="https://avatars.githubusercontent.com/u/711564?v=4&s=48" width="48" height="48" alt="Tobias Bischoff" title="Tobias Bischoff"/></a> <a href="https://github.com/joshp123"><img src="https://avatars.githubusercontent.com/u/1497361?v=4&s=48" width="48" height="48" alt="joshp123" title="joshp123"/></a> <a href="https://github.com/czekaj"><img src="https://avatars.githubusercontent.com/u/1464539?v=4&s=48" width="48" height="48" alt="czekaj" title="czekaj"/></a> <a href="https://github.com/mukhtharcm"><img src="https://avatars.githubusercontent.com/u/56378562?v=4&s=48" width="48" height="48" alt="mukhtharcm" title="mukhtharcm"/></a> <a href="https://github.com/sebslight"><img src="https://avatars.githubusercontent.com/u/19554889?v=4&s=48" width="48" height="48" alt="sebslight" title="sebslight"/></a> <a href="https://github.com/maxsumrall"><img src="https://avatars.githubusercontent.com/u/628843?v=4&s=48" width="48" height="48" alt="maxsumrall" title="maxsumrall"/></a> <a href="https://github.com/xadenryan"><img src="https://avatars.githubusercontent.com/u/165437834?v=4&s=48" width="48" height="48" alt="xadenryan" title="xadenryan"/></a>
482
+ <a href="https://github.com/rodrigouroz"><img src="https://avatars.githubusercontent.com/u/384037?v=4&s=48" width="48" height="48" alt="rodrigouroz" title="rodrigouroz"/></a> <a href="https://github.com/juanpablodlc"><img src="https://avatars.githubusercontent.com/u/92012363?v=4&s=48" width="48" height="48" alt="juanpablodlc" title="juanpablodlc"/></a> <a href="https://github.com/hsrvc"><img src="https://avatars.githubusercontent.com/u/129702169?v=4&s=48" width="48" height="48" alt="hsrvc" title="hsrvc"/></a> <a href="https://github.com/magimetal"><img src="https://avatars.githubusercontent.com/u/36491250?v=4&s=48" width="48" height="48" alt="magimetal" title="magimetal"/></a> <a href="https://github.com/zerone0x"><img src="https://avatars.githubusercontent.com/u/39543393?v=4&s=48" width="48" height="48" alt="zerone0x" title="zerone0x"/></a> <a href="https://github.com/meaningfool"><img src="https://avatars.githubusercontent.com/u/2862331?v=4&s=48" width="48" height="48" alt="meaningfool" title="meaningfool"/></a> <a href="https://github.com/tyler6204"><img src="https://avatars.githubusercontent.com/u/64381258?v=4&s=48" width="48" height="48" alt="tyler6204" title="tyler6204"/></a> <a href="https://github.com/vignesh07"><img src="https://avatars.githubusercontent.com/u/1436853?v=4&s=48" width="48" height="48" alt="vignesh07" title="vignesh07"/></a> <a href="https://github.com/patelhiren"><img src="https://avatars.githubusercontent.com/u/172098?v=4&s=48" width="48" height="48" alt="patelhiren" title="patelhiren"/></a> <a href="https://github.com/NicholasSpisak"><img src="https://avatars.githubusercontent.com/u/129075147?v=4&s=48" width="48" height="48" alt="NicholasSpisak" title="NicholasSpisak"/></a>
483
+ <a href="https://github.com/jonisjongithub"><img src="https://avatars.githubusercontent.com/u/86072337?v=4&s=48" width="48" height="48" alt="jonisjongithub" title="jonisjongithub"/></a> <a href="https://github.com/AbhisekBasu1"><img src="https://avatars.githubusercontent.com/u/40645221?v=4&s=48" width="48" height="48" alt="abhisekbasu1" title="abhisekbasu1"/></a> <a href="https://github.com/jamesgroat"><img src="https://avatars.githubusercontent.com/u/2634024?v=4&s=48" width="48" height="48" alt="jamesgroat" title="jamesgroat"/></a> <a href="https://github.com/claude"><img src="https://avatars.githubusercontent.com/u/81847?v=4&s=48" width="48" height="48" alt="claude" title="claude"/></a> <a href="https://github.com/JustYannicc"><img src="https://avatars.githubusercontent.com/u/52761674?v=4&s=48" width="48" height="48" alt="JustYannicc" title="JustYannicc"/></a> <a href="https://github.com/Hyaxia"><img src="https://avatars.githubusercontent.com/u/36747317?v=4&s=48" width="48" height="48" alt="Hyaxia" title="Hyaxia"/></a> <a href="https://github.com/dantelex"><img src="https://avatars.githubusercontent.com/u/631543?v=4&s=48" width="48" height="48" alt="dantelex" title="dantelex"/></a> <a href="https://github.com/SocialNerd42069"><img src="https://avatars.githubusercontent.com/u/118244303?v=4&s=48" width="48" height="48" alt="SocialNerd42069" title="SocialNerd42069"/></a> <a href="https://github.com/daveonkels"><img src="https://avatars.githubusercontent.com/u/533642?v=4&s=48" width="48" height="48" alt="daveonkels" title="daveonkels"/></a> <a href="https://github.com/apps/google-labs-jules"><img src="https://avatars.githubusercontent.com/in/842251?v=4&s=48" width="48" height="48" alt="google-labs-jules[bot]" title="google-labs-jules[bot]"/></a>
484
+ <a href="https://github.com/lc0rp"><img src="https://avatars.githubusercontent.com/u/2609441?v=4&s=48" width="48" height="48" alt="lc0rp" title="lc0rp"/></a> <a href="https://github.com/mousberg"><img src="https://avatars.githubusercontent.com/u/57605064?v=4&s=48" width="48" height="48" alt="mousberg" title="mousberg"/></a> <a href="https://github.com/mteam88"><img src="https://avatars.githubusercontent.com/u/84196639?v=4&s=48" width="48" height="48" alt="mteam88" title="mteam88"/></a> <a href="https://github.com/hirefrank"><img src="https://avatars.githubusercontent.com/u/183158?v=4&s=48" width="48" height="48" alt="hirefrank" title="hirefrank"/></a> <a href="https://github.com/joeynyc"><img src="https://avatars.githubusercontent.com/u/17919866?v=4&s=48" width="48" height="48" alt="joeynyc" title="joeynyc"/></a> <a href="https://github.com/orlyjamie"><img src="https://avatars.githubusercontent.com/u/6668807?v=4&s=48" width="48" height="48" alt="orlyjamie" title="orlyjamie"/></a> <a href="https://github.com/dbhurley"><img src="https://avatars.githubusercontent.com/u/5251425?v=4&s=48" width="48" height="48" alt="dbhurley" title="dbhurley"/></a> <a href="https://github.com/mbelinky"><img src="https://avatars.githubusercontent.com/u/132747814?v=4&s=48" width="48" height="48" alt="Mariano Belinky" title="Mariano Belinky"/></a> <a href="https://github.com/omniwired"><img src="https://avatars.githubusercontent.com/u/322761?v=4&s=48" width="48" height="48" alt="Eng. Juan Combetto" title="Eng. Juan Combetto"/></a> <a href="https://github.com/TSavo"><img src="https://avatars.githubusercontent.com/u/877990?v=4&s=48" width="48" height="48" alt="TSavo" title="TSavo"/></a>
485
+ <a href="https://github.com/julianengel"><img src="https://avatars.githubusercontent.com/u/10634231?v=4&s=48" width="48" height="48" alt="julianengel" title="julianengel"/></a> <a href="https://github.com/bradleypriest"><img src="https://avatars.githubusercontent.com/u/167215?v=4&s=48" width="48" height="48" alt="bradleypriest" title="bradleypriest"/></a> <a href="https://github.com/benithors"><img src="https://avatars.githubusercontent.com/u/20652882?v=4&s=48" width="48" height="48" alt="benithors" title="benithors"/></a> <a href="https://github.com/rohannagpal"><img src="https://avatars.githubusercontent.com/u/4009239?v=4&s=48" width="48" height="48" alt="rohannagpal" title="rohannagpal"/></a> <a href="https://github.com/timolins"><img src="https://avatars.githubusercontent.com/u/1440854?v=4&s=48" width="48" height="48" alt="timolins" title="timolins"/></a> <a href="https://github.com/f-trycua"><img src="https://avatars.githubusercontent.com/u/195596869?v=4&s=48" width="48" height="48" alt="f-trycua" title="f-trycua"/></a> <a href="https://github.com/benostein"><img src="https://avatars.githubusercontent.com/u/31802821?v=4&s=48" width="48" height="48" alt="benostein" title="benostein"/></a> <a href="https://github.com/Nachx639"><img src="https://avatars.githubusercontent.com/u/71144023?v=4&s=48" width="48" height="48" alt="nachx639" title="nachx639"/></a> <a href="https://github.com/pvoo"><img src="https://avatars.githubusercontent.com/u/20116814?v=4&s=48" width="48" height="48" alt="pvoo" title="pvoo"/></a> <a href="https://github.com/sreekaransrinath"><img src="https://avatars.githubusercontent.com/u/50989977?v=4&s=48" width="48" height="48" alt="sreekaransrinath" title="sreekaransrinath"/></a>
486
+ <a href="https://github.com/gupsammy"><img src="https://avatars.githubusercontent.com/u/20296019?v=4&s=48" width="48" height="48" alt="gupsammy" title="gupsammy"/></a> <a href="https://github.com/cristip73"><img src="https://avatars.githubusercontent.com/u/24499421?v=4&s=48" width="48" height="48" alt="cristip73" title="cristip73"/></a> <a href="https://github.com/stefangalescu"><img src="https://avatars.githubusercontent.com/u/52995748?v=4&s=48" width="48" height="48" alt="stefangalescu" title="stefangalescu"/></a> <a href="https://github.com/nachoiacovino"><img src="https://avatars.githubusercontent.com/u/50103937?v=4&s=48" width="48" height="48" alt="nachoiacovino" title="nachoiacovino"/></a> <a href="https://github.com/vsabavat"><img src="https://avatars.githubusercontent.com/u/50385532?v=4&s=48" width="48" height="48" alt="Vasanth Rao Naik Sabavat" title="Vasanth Rao Naik Sabavat"/></a> <a href="https://github.com/petter-b"><img src="https://avatars.githubusercontent.com/u/62076402?v=4&s=48" width="48" height="48" alt="petter-b" title="petter-b"/></a> <a href="https://github.com/cpojer"><img src="https://avatars.githubusercontent.com/u/13352?v=4&s=48" width="48" height="48" alt="cpojer" title="cpojer"/></a> <a href="https://github.com/scald"><img src="https://avatars.githubusercontent.com/u/1215913?v=4&s=48" width="48" height="48" alt="scald" title="scald"/></a> <a href="https://github.com/gumadeiras"><img src="https://avatars.githubusercontent.com/u/5599352?v=4&s=48" width="48" height="48" alt="gumadeiras" title="gumadeiras"/></a> <a href="https://github.com/andranik-sahakyan"><img src="https://avatars.githubusercontent.com/u/8908029?v=4&s=48" width="48" height="48" alt="andranik-sahakyan" title="andranik-sahakyan"/></a>
487
+ <a href="https://github.com/davidguttman"><img src="https://avatars.githubusercontent.com/u/431696?v=4&s=48" width="48" height="48" alt="davidguttman" title="davidguttman"/></a> <a href="https://github.com/thewilloftheshadow"><img src="https://avatars.githubusercontent.com/u/35580099?v=4&s=48" width="48" height="48" alt="thewilloftheshadow" title="thewilloftheshadow"/></a> <a href="https://github.com/sleontenko"><img src="https://avatars.githubusercontent.com/u/7135949?v=4&s=48" width="48" height="48" alt="sleontenko" title="sleontenko"/></a> <a href="https://github.com/denysvitali"><img src="https://avatars.githubusercontent.com/u/4939519?v=4&s=48" width="48" height="48" alt="denysvitali" title="denysvitali"/></a> <a href="https://github.com/shakkernerd"><img src="https://avatars.githubusercontent.com/u/165377636?v=4&s=48" width="48" height="48" alt="shakkernerd" title="shakkernerd"/></a> <a href="https://github.com/sircrumpet"><img src="https://avatars.githubusercontent.com/u/4436535?v=4&s=48" width="48" height="48" alt="sircrumpet" title="sircrumpet"/></a> <a href="https://github.com/peschee"><img src="https://avatars.githubusercontent.com/u/63866?v=4&s=48" width="48" height="48" alt="peschee" title="peschee"/></a> <a href="https://github.com/rafaelreis-r"><img src="https://avatars.githubusercontent.com/u/57492577?v=4&s=48" width="48" height="48" alt="rafaelreis-r" title="rafaelreis-r"/></a> <a href="https://github.com/dominicnunez"><img src="https://avatars.githubusercontent.com/u/43616264?v=4&s=48" width="48" height="48" alt="dominicnunez" title="dominicnunez"/></a> <a href="https://github.com/ratulsarna"><img src="https://avatars.githubusercontent.com/u/105903728?v=4&s=48" width="48" height="48" alt="ratulsarna" title="ratulsarna"/></a>
488
+ <a href="https://github.com/lutr0"><img src="https://avatars.githubusercontent.com/u/76906369?v=4&s=48" width="48" height="48" alt="lutr0" title="lutr0"/></a> <a href="https://github.com/danielz1z"><img src="https://avatars.githubusercontent.com/u/235270390?v=4&s=48" width="48" height="48" alt="danielz1z" title="danielz1z"/></a> <a href="https://github.com/AdeboyeDN"><img src="https://avatars.githubusercontent.com/u/65312338?v=4&s=48" width="48" height="48" alt="AdeboyeDN" title="AdeboyeDN"/></a> <a href="https://github.com/Alg0rix"><img src="https://avatars.githubusercontent.com/u/53804949?v=4&s=48" width="48" height="48" alt="Alg0rix" title="Alg0rix"/></a> <a href="https://github.com/emanuelst"><img src="https://avatars.githubusercontent.com/u/9994339?v=4&s=48" width="48" height="48" alt="emanuelst" title="emanuelst"/></a> <a href="https://github.com/KristijanJovanovski"><img src="https://avatars.githubusercontent.com/u/8942284?v=4&s=48" width="48" height="48" alt="KristijanJovanovski" title="KristijanJovanovski"/></a> <a href="https://github.com/rdev"><img src="https://avatars.githubusercontent.com/u/8418866?v=4&s=48" width="48" height="48" alt="rdev" title="rdev"/></a> <a href="https://github.com/rhuanssauro"><img src="https://avatars.githubusercontent.com/u/164682191?v=4&s=48" width="48" height="48" alt="rhuanssauro" title="rhuanssauro"/></a> <a href="https://github.com/joshrad-dev"><img src="https://avatars.githubusercontent.com/u/62785552?v=4&s=48" width="48" height="48" alt="joshrad-dev" title="joshrad-dev"/></a> <a href="https://github.com/kiranjd"><img src="https://avatars.githubusercontent.com/u/25822851?v=4&s=48" width="48" height="48" alt="kiranjd" title="kiranjd"/></a>
489
+ <a href="https://github.com/osolmaz"><img src="https://avatars.githubusercontent.com/u/2453968?v=4&s=48" width="48" height="48" alt="osolmaz" title="osolmaz"/></a> <a href="https://github.com/adityashaw2"><img src="https://avatars.githubusercontent.com/u/41204444?v=4&s=48" width="48" height="48" alt="adityashaw2" title="adityashaw2"/></a> <a href="https://github.com/CashWilliams"><img src="https://avatars.githubusercontent.com/u/613573?v=4&s=48" width="48" height="48" alt="CashWilliams" title="CashWilliams"/></a> <a href="https://github.com/search?q=sheeek"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="sheeek" title="sheeek"/></a> <a href="https://github.com/artuskg"><img src="https://avatars.githubusercontent.com/u/11966157?v=4&s=48" width="48" height="48" alt="artuskg" title="artuskg"/></a> <a href="https://github.com/Takhoffman"><img src="https://avatars.githubusercontent.com/u/781889?v=4&s=48" width="48" height="48" alt="Takhoffman" title="Takhoffman"/></a> <a href="https://github.com/onutc"><img src="https://avatars.githubusercontent.com/u/152018508?v=4&s=48" width="48" height="48" alt="onutc" title="onutc"/></a> <a href="https://github.com/pauloportella"><img src="https://avatars.githubusercontent.com/u/22947229?v=4&s=48" width="48" height="48" alt="pauloportella" title="pauloportella"/></a> <a href="https://github.com/neooriginal"><img src="https://avatars.githubusercontent.com/u/54811660?v=4&s=48" width="48" height="48" alt="neooriginal" title="neooriginal"/></a> <a href="https://github.com/ManuelHettich"><img src="https://avatars.githubusercontent.com/u/17690367?v=4&s=48" width="48" height="48" alt="manuelhettich" title="manuelhettich"/></a>
490
+ <a href="https://github.com/minghinmatthewlam"><img src="https://avatars.githubusercontent.com/u/14224566?v=4&s=48" width="48" height="48" alt="minghinmatthewlam" title="minghinmatthewlam"/></a> <a href="https://github.com/myfunc"><img src="https://avatars.githubusercontent.com/u/19294627?v=4&s=48" width="48" height="48" alt="myfunc" title="myfunc"/></a> <a href="https://github.com/travisirby"><img src="https://avatars.githubusercontent.com/u/5958376?v=4&s=48" width="48" height="48" alt="travisirby" title="travisirby"/></a> <a href="https://github.com/buddyh"><img src="https://avatars.githubusercontent.com/u/31752869?v=4&s=48" width="48" height="48" alt="buddyh" title="buddyh"/></a> <a href="https://github.com/connorshea"><img src="https://avatars.githubusercontent.com/u/2977353?v=4&s=48" width="48" height="48" alt="connorshea" title="connorshea"/></a> <a href="https://github.com/kyleok"><img src="https://avatars.githubusercontent.com/u/58307870?v=4&s=48" width="48" height="48" alt="kyleok" title="kyleok"/></a> <a href="https://github.com/mcinteerj"><img src="https://avatars.githubusercontent.com/u/3613653?v=4&s=48" width="48" height="48" alt="mcinteerj" title="mcinteerj"/></a> <a href="https://github.com/apps/dependabot"><img src="https://avatars.githubusercontent.com/in/29110?v=4&s=48" width="48" height="48" alt="dependabot[bot]" title="dependabot[bot]"/></a> <a href="https://github.com/John-Rood"><img src="https://avatars.githubusercontent.com/u/62669593?v=4&s=48" width="48" height="48" alt="John-Rood" title="John-Rood"/></a> <a href="https://github.com/obviyus"><img src="https://avatars.githubusercontent.com/u/22031114?v=4&s=48" width="48" height="48" alt="obviyus" title="obviyus"/></a>
491
+ <a href="https://github.com/timkrase"><img src="https://avatars.githubusercontent.com/u/38947626?v=4&s=48" width="48" height="48" alt="timkrase" title="timkrase"/></a> <a href="https://github.com/uos-status"><img src="https://avatars.githubusercontent.com/u/255712580?v=4&s=48" width="48" height="48" alt="uos-status" title="uos-status"/></a> <a href="https://github.com/gerardward2007"><img src="https://avatars.githubusercontent.com/u/3002155?v=4&s=48" width="48" height="48" alt="gerardward2007" title="gerardward2007"/></a> <a href="https://github.com/roshanasingh4"><img src="https://avatars.githubusercontent.com/u/88576930?v=4&s=48" width="48" height="48" alt="roshanasingh4" title="roshanasingh4"/></a> <a href="https://github.com/tosh-hamburg"><img src="https://avatars.githubusercontent.com/u/58424326?v=4&s=48" width="48" height="48" alt="tosh-hamburg" title="tosh-hamburg"/></a> <a href="https://github.com/azade-c"><img src="https://avatars.githubusercontent.com/u/252790079?v=4&s=48" width="48" height="48" alt="azade-c" title="azade-c"/></a> <a href="https://github.com/JonUleis"><img src="https://avatars.githubusercontent.com/u/7644941?v=4&s=48" width="48" height="48" alt="JonUleis" title="JonUleis"/></a> <a href="https://github.com/bjesuiter"><img src="https://avatars.githubusercontent.com/u/2365676?v=4&s=48" width="48" height="48" alt="bjesuiter" title="bjesuiter"/></a> <a href="https://github.com/cheeeee"><img src="https://avatars.githubusercontent.com/u/21245729?v=4&s=48" width="48" height="48" alt="cheeeee" title="cheeeee"/></a> <a href="https://github.com/robbyczgw-cla"><img src="https://avatars.githubusercontent.com/u/239660374?v=4&s=48" width="48" height="48" alt="robbyczgw-cla" title="robbyczgw-cla"/></a>
492
+ <a href="https://github.com/dlauer"><img src="https://avatars.githubusercontent.com/u/757041?v=4&s=48" width="48" height="48" alt="dlauer" title="dlauer"/></a> <a href="https://github.com/j1philli"><img src="https://avatars.githubusercontent.com/u/3744255?v=4&s=48" width="48" height="48" alt="Josh Phillips" title="Josh Phillips"/></a> <a href="https://github.com/YuriNachos"><img src="https://avatars.githubusercontent.com/u/19365375?v=4&s=48" width="48" height="48" alt="YuriNachos" title="YuriNachos"/></a> <a href="https://github.com/pookNast"><img src="https://avatars.githubusercontent.com/u/14242552?v=4&s=48" width="48" height="48" alt="pookNast" title="pookNast"/></a> <a href="https://github.com/Whoaa512"><img src="https://avatars.githubusercontent.com/u/1581943?v=4&s=48" width="48" height="48" alt="Whoaa512" title="Whoaa512"/></a> <a href="https://github.com/chriseidhof"><img src="https://avatars.githubusercontent.com/u/5382?v=4&s=48" width="48" height="48" alt="chriseidhof" title="chriseidhof"/></a> <a href="https://github.com/ngutman"><img src="https://avatars.githubusercontent.com/u/1540134?v=4&s=48" width="48" height="48" alt="ngutman" title="ngutman"/></a> <a href="https://github.com/ysqander"><img src="https://avatars.githubusercontent.com/u/80843820?v=4&s=48" width="48" height="48" alt="ysqander" title="ysqander"/></a> <a href="https://github.com/aj47"><img src="https://avatars.githubusercontent.com/u/8023513?v=4&s=48" width="48" height="48" alt="aj47" title="aj47"/></a> <a href="https://github.com/superman32432432"><img src="https://avatars.githubusercontent.com/u/7228420?v=4&s=48" width="48" height="48" alt="superman32432432" title="superman32432432"/></a>
493
+ <a href="https://github.com/search?q=Yurii%20Chukhlib"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Yurii Chukhlib" title="Yurii Chukhlib"/></a> <a href="https://github.com/grp06"><img src="https://avatars.githubusercontent.com/u/1573959?v=4&s=48" width="48" height="48" alt="grp06" title="grp06"/></a> <a href="https://github.com/antons"><img src="https://avatars.githubusercontent.com/u/129705?v=4&s=48" width="48" height="48" alt="antons" title="antons"/></a> <a href="https://github.com/austinm911"><img src="https://avatars.githubusercontent.com/u/31991302?v=4&s=48" width="48" height="48" alt="austinm911" title="austinm911"/></a> <a href="https://github.com/apps/blacksmith-sh"><img src="https://avatars.githubusercontent.com/in/807020?v=4&s=48" width="48" height="48" alt="blacksmith-sh[bot]" title="blacksmith-sh[bot]"/></a> <a href="https://github.com/damoahdominic"><img src="https://avatars.githubusercontent.com/u/4623434?v=4&s=48" width="48" height="48" alt="damoahdominic" title="damoahdominic"/></a> <a href="https://github.com/dan-dr"><img src="https://avatars.githubusercontent.com/u/6669808?v=4&s=48" width="48" height="48" alt="dan-dr" title="dan-dr"/></a> <a href="https://github.com/HeimdallStrategy"><img src="https://avatars.githubusercontent.com/u/223014405?v=4&s=48" width="48" height="48" alt="HeimdallStrategy" title="HeimdallStrategy"/></a> <a href="https://github.com/imfing"><img src="https://avatars.githubusercontent.com/u/5097752?v=4&s=48" width="48" height="48" alt="imfing" title="imfing"/></a> <a href="https://github.com/jalehman"><img src="https://avatars.githubusercontent.com/u/550978?v=4&s=48" width="48" height="48" alt="jalehman" title="jalehman"/></a>
494
+ <a href="https://github.com/jarvis-medmatic"><img src="https://avatars.githubusercontent.com/u/252428873?v=4&s=48" width="48" height="48" alt="jarvis-medmatic" title="jarvis-medmatic"/></a> <a href="https://github.com/kkarimi"><img src="https://avatars.githubusercontent.com/u/875218?v=4&s=48" width="48" height="48" alt="kkarimi" title="kkarimi"/></a> <a href="https://github.com/mahmoudashraf93"><img src="https://avatars.githubusercontent.com/u/9130129?v=4&s=48" width="48" height="48" alt="mahmoudashraf93" title="mahmoudashraf93"/></a> <a href="https://github.com/pkrmf"><img src="https://avatars.githubusercontent.com/u/1714267?v=4&s=48" width="48" height="48" alt="pkrmf" title="pkrmf"/></a> <a href="https://github.com/RandyVentures"><img src="https://avatars.githubusercontent.com/u/149904821?v=4&s=48" width="48" height="48" alt="RandyVentures" title="RandyVentures"/></a> <a href="https://github.com/search?q=Ryan%20Lisse"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Ryan Lisse" title="Ryan Lisse"/></a> <a href="https://github.com/dougvk"><img src="https://avatars.githubusercontent.com/u/401660?v=4&s=48" width="48" height="48" alt="dougvk" title="dougvk"/></a> <a href="https://github.com/erikpr1994"><img src="https://avatars.githubusercontent.com/u/6299331?v=4&s=48" width="48" height="48" alt="erikpr1994" title="erikpr1994"/></a> <a href="https://github.com/search?q=Ghost"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Ghost" title="Ghost"/></a> <a href="https://github.com/jonasjancarik"><img src="https://avatars.githubusercontent.com/u/2459191?v=4&s=48" width="48" height="48" alt="jonasjancarik" title="jonasjancarik"/></a>
495
+ <a href="https://github.com/search?q=Keith%20the%20Silly%20Goose"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Keith the Silly Goose" title="Keith the Silly Goose"/></a> <a href="https://github.com/search?q=L36%20Server"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="L36 Server" title="L36 Server"/></a> <a href="https://github.com/search?q=Marc"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Marc" title="Marc"/></a> <a href="https://github.com/mitschabaude-bot"><img src="https://avatars.githubusercontent.com/u/247582884?v=4&s=48" width="48" height="48" alt="mitschabaude-bot" title="mitschabaude-bot"/></a> <a href="https://github.com/mkbehr"><img src="https://avatars.githubusercontent.com/u/1285?v=4&s=48" width="48" height="48" alt="mkbehr" title="mkbehr"/></a> <a href="https://github.com/neist"><img src="https://avatars.githubusercontent.com/u/1029724?v=4&s=48" width="48" height="48" alt="neist" title="neist"/></a> <a href="https://github.com/sibbl"><img src="https://avatars.githubusercontent.com/u/866535?v=4&s=48" width="48" height="48" alt="sibbl" title="sibbl"/></a> <a href="https://github.com/chrisrodz"><img src="https://avatars.githubusercontent.com/u/2967620?v=4&s=48" width="48" height="48" alt="chrisrodz" title="chrisrodz"/></a> <a href="https://github.com/search?q=Friederike%20Seiler"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Friederike Seiler" title="Friederike Seiler"/></a> <a href="https://github.com/gabriel-trigo"><img src="https://avatars.githubusercontent.com/u/38991125?v=4&s=48" width="48" height="48" alt="gabriel-trigo" title="gabriel-trigo"/></a>
496
+ <a href="https://github.com/Iamadig"><img src="https://avatars.githubusercontent.com/u/102129234?v=4&s=48" width="48" height="48" alt="iamadig" title="iamadig"/></a> <a href="https://github.com/jdrhyne"><img src="https://avatars.githubusercontent.com/u/7828464?v=4&s=48" width="48" height="48" alt="Jonathan D. Rhyne (DJ-D)" title="Jonathan D. Rhyne (DJ-D)"/></a> <a href="https://github.com/search?q=Joshua%20Mitchell"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Joshua Mitchell" title="Joshua Mitchell"/></a> <a href="https://github.com/search?q=Kit"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Kit" title="Kit"/></a> <a href="https://github.com/koala73"><img src="https://avatars.githubusercontent.com/u/996596?v=4&s=48" width="48" height="48" alt="koala73" title="koala73"/></a> <a href="https://github.com/manmal"><img src="https://avatars.githubusercontent.com/u/142797?v=4&s=48" width="48" height="48" alt="manmal" title="manmal"/></a> <a href="https://github.com/ogulcancelik"><img src="https://avatars.githubusercontent.com/u/7064011?v=4&s=48" width="48" height="48" alt="ogulcancelik" title="ogulcancelik"/></a> <a href="https://github.com/pasogott"><img src="https://avatars.githubusercontent.com/u/23458152?v=4&s=48" width="48" height="48" alt="pasogott" title="pasogott"/></a> <a href="https://github.com/petradonka"><img src="https://avatars.githubusercontent.com/u/7353770?v=4&s=48" width="48" height="48" alt="petradonka" title="petradonka"/></a> <a href="https://github.com/rubyrunsstuff"><img src="https://avatars.githubusercontent.com/u/246602379?v=4&s=48" width="48" height="48" alt="rubyrunsstuff" title="rubyrunsstuff"/></a>
497
+ <a href="https://github.com/siddhantjain"><img src="https://avatars.githubusercontent.com/u/4835232?v=4&s=48" width="48" height="48" alt="siddhantjain" title="siddhantjain"/></a> <a href="https://github.com/suminhthanh"><img src="https://avatars.githubusercontent.com/u/2907636?v=4&s=48" width="48" height="48" alt="suminhthanh" title="suminhthanh"/></a> <a href="https://github.com/svkozak"><img src="https://avatars.githubusercontent.com/u/31941359?v=4&s=48" width="48" height="48" alt="svkozak" title="svkozak"/></a> <a href="https://github.com/VACInc"><img src="https://avatars.githubusercontent.com/u/3279061?v=4&s=48" width="48" height="48" alt="VACInc" title="VACInc"/></a> <a href="https://github.com/wes-davis"><img src="https://avatars.githubusercontent.com/u/16506720?v=4&s=48" width="48" height="48" alt="wes-davis" title="wes-davis"/></a> <a href="https://github.com/zats"><img src="https://avatars.githubusercontent.com/u/2688806?v=4&s=48" width="48" height="48" alt="zats" title="zats"/></a> <a href="https://github.com/24601"><img src="https://avatars.githubusercontent.com/u/1157207?v=4&s=48" width="48" height="48" alt="24601" title="24601"/></a> <a href="https://github.com/adam91holt"><img src="https://avatars.githubusercontent.com/u/9592417?v=4&s=48" width="48" height="48" alt="adam91holt" title="adam91holt"/></a> <a href="https://github.com/ameno-"><img src="https://avatars.githubusercontent.com/u/2416135?v=4&s=48" width="48" height="48" alt="ameno-" title="ameno-"/></a> <a href="https://github.com/search?q=Chris%20Taylor"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Chris Taylor" title="Chris Taylor"/></a>
498
+ <a href="https://github.com/dguido"><img src="https://avatars.githubusercontent.com/u/294844?v=4&s=48" width="48" height="48" alt="dguido" title="dguido"/></a> <a href="https://github.com/djangonavarro220"><img src="https://avatars.githubusercontent.com/u/251162586?v=4&s=48" width="48" height="48" alt="Django Navarro" title="Django Navarro"/></a> <a href="https://github.com/evalexpr"><img src="https://avatars.githubusercontent.com/u/23485511?v=4&s=48" width="48" height="48" alt="evalexpr" title="evalexpr"/></a> <a href="https://github.com/henrino3"><img src="https://avatars.githubusercontent.com/u/4260288?v=4&s=48" width="48" height="48" alt="henrino3" title="henrino3"/></a> <a href="https://github.com/humanwritten"><img src="https://avatars.githubusercontent.com/u/206531610?v=4&s=48" width="48" height="48" alt="humanwritten" title="humanwritten"/></a> <a href="https://github.com/larlyssa"><img src="https://avatars.githubusercontent.com/u/13128869?v=4&s=48" width="48" height="48" alt="larlyssa" title="larlyssa"/></a> <a href="https://github.com/odysseus0"><img src="https://avatars.githubusercontent.com/u/8635094?v=4&s=48" width="48" height="48" alt="odysseus0" title="odysseus0"/></a> <a href="https://github.com/oswalpalash"><img src="https://avatars.githubusercontent.com/u/6431196?v=4&s=48" width="48" height="48" alt="oswalpalash" title="oswalpalash"/></a> <a href="https://github.com/pcty-nextgen-service-account"><img src="https://avatars.githubusercontent.com/u/112553441?v=4&s=48" width="48" height="48" alt="pcty-nextgen-service-account" title="pcty-nextgen-service-account"/></a> <a href="https://github.com/rmorse"><img src="https://avatars.githubusercontent.com/u/853547?v=4&s=48" width="48" height="48" alt="rmorse" title="rmorse"/></a>
499
+ <a href="https://github.com/Syhids"><img src="https://avatars.githubusercontent.com/u/671202?v=4&s=48" width="48" height="48" alt="Syhids" title="Syhids"/></a> <a href="https://github.com/search?q=Aaron%20Konyer"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Aaron Konyer" title="Aaron Konyer"/></a> <a href="https://github.com/aaronveklabs"><img src="https://avatars.githubusercontent.com/u/225997828?v=4&s=48" width="48" height="48" alt="aaronveklabs" title="aaronveklabs"/></a> <a href="https://github.com/andreabadesso"><img src="https://avatars.githubusercontent.com/u/3586068?v=4&s=48" width="48" height="48" alt="andreabadesso" title="andreabadesso"/></a> <a href="https://github.com/search?q=Andrii"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Andrii" title="Andrii"/></a> <a href="https://github.com/cash-echo-bot"><img src="https://avatars.githubusercontent.com/u/252747386?v=4&s=48" width="48" height="48" alt="cash-echo-bot" title="cash-echo-bot"/></a> <a href="https://github.com/search?q=Clawd"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Clawd" title="Clawd"/></a> <a href="https://github.com/search?q=ClawdFx"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="ClawdFx" title="ClawdFx"/></a> <a href="https://github.com/EnzeD"><img src="https://avatars.githubusercontent.com/u/9866900?v=4&s=48" width="48" height="48" alt="EnzeD" title="EnzeD"/></a> <a href="https://github.com/erik-agens"><img src="https://avatars.githubusercontent.com/u/80908960?v=4&s=48" width="48" height="48" alt="erik-agens" title="erik-agens"/></a>
500
+ <a href="https://github.com/Evizero"><img src="https://avatars.githubusercontent.com/u/10854026?v=4&s=48" width="48" height="48" alt="Evizero" title="Evizero"/></a> <a href="https://github.com/fcatuhe"><img src="https://avatars.githubusercontent.com/u/17382215?v=4&s=48" width="48" height="48" alt="fcatuhe" title="fcatuhe"/></a> <a href="https://github.com/itsjaydesu"><img src="https://avatars.githubusercontent.com/u/220390?v=4&s=48" width="48" height="48" alt="itsjaydesu" title="itsjaydesu"/></a> <a href="https://github.com/ivancasco"><img src="https://avatars.githubusercontent.com/u/2452858?v=4&s=48" width="48" height="48" alt="ivancasco" title="ivancasco"/></a> <a href="https://github.com/ivanrvpereira"><img src="https://avatars.githubusercontent.com/u/183991?v=4&s=48" width="48" height="48" alt="ivanrvpereira" title="ivanrvpereira"/></a> <a href="https://github.com/jayhickey"><img src="https://avatars.githubusercontent.com/u/1676460?v=4&s=48" width="48" height="48" alt="jayhickey" title="jayhickey"/></a> <a href="https://github.com/jeffersonwarrior"><img src="https://avatars.githubusercontent.com/u/89030989?v=4&s=48" width="48" height="48" alt="jeffersonwarrior" title="jeffersonwarrior"/></a> <a href="https://github.com/search?q=jeffersonwarrior"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="jeffersonwarrior" title="jeffersonwarrior"/></a> <a href="https://github.com/jverdi"><img src="https://avatars.githubusercontent.com/u/345050?v=4&s=48" width="48" height="48" alt="jverdi" title="jverdi"/></a> <a href="https://github.com/longmaba"><img src="https://avatars.githubusercontent.com/u/9361500?v=4&s=48" width="48" height="48" alt="longmaba" title="longmaba"/></a>
501
+ <a href="https://github.com/mickahouan"><img src="https://avatars.githubusercontent.com/u/31423109?v=4&s=48" width="48" height="48" alt="mickahouan" title="mickahouan"/></a> <a href="https://github.com/mjrussell"><img src="https://avatars.githubusercontent.com/u/1641895?v=4&s=48" width="48" height="48" alt="mjrussell" title="mjrussell"/></a> <a href="https://github.com/odnxe"><img src="https://avatars.githubusercontent.com/u/403141?v=4&s=48" width="48" height="48" alt="odnxe" title="odnxe"/></a> <a href="https://github.com/p6l-richard"><img src="https://avatars.githubusercontent.com/u/18185649?v=4&s=48" width="48" height="48" alt="p6l-richard" title="p6l-richard"/></a> <a href="https://github.com/philipp-spiess"><img src="https://avatars.githubusercontent.com/u/458591?v=4&s=48" width="48" height="48" alt="philipp-spiess" title="philipp-spiess"/></a> <a href="https://github.com/search?q=Pocket%20Clawd"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Pocket Clawd" title="Pocket Clawd"/></a> <a href="https://github.com/robaxelsen"><img src="https://avatars.githubusercontent.com/u/13132899?v=4&s=48" width="48" height="48" alt="robaxelsen" title="robaxelsen"/></a> <a href="https://github.com/search?q=Sash%20Catanzarite"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Sash Catanzarite" title="Sash Catanzarite"/></a> <a href="https://github.com/T5-AndyML"><img src="https://avatars.githubusercontent.com/u/22801233?v=4&s=48" width="48" height="48" alt="T5-AndyML" title="T5-AndyML"/></a> <a href="https://github.com/travisp"><img src="https://avatars.githubusercontent.com/u/165698?v=4&s=48" width="48" height="48" alt="travisp" title="travisp"/></a>
502
+ <a href="https://github.com/search?q=VAC"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="VAC" title="VAC"/></a> <a href="https://github.com/search?q=william%20arzt"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="william arzt" title="william arzt"/></a> <a href="https://github.com/zknicker"><img src="https://avatars.githubusercontent.com/u/1164085?v=4&s=48" width="48" height="48" alt="zknicker" title="zknicker"/></a> <a href="https://github.com/abhaymundhara"><img src="https://avatars.githubusercontent.com/u/62872231?v=4&s=48" width="48" height="48" alt="abhaymundhara" title="abhaymundhara"/></a> <a href="https://github.com/search?q=alejandro%20maza"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="alejandro maza" title="alejandro maza"/></a> <a href="https://github.com/Alex-Alaniz"><img src="https://avatars.githubusercontent.com/u/88956822?v=4&s=48" width="48" height="48" alt="Alex-Alaniz" title="Alex-Alaniz"/></a> <a href="https://github.com/alexstyl"><img src="https://avatars.githubusercontent.com/u/1665273?v=4&s=48" width="48" height="48" alt="alexstyl" title="alexstyl"/></a> <a href="https://github.com/andrewting19"><img src="https://avatars.githubusercontent.com/u/10536704?v=4&s=48" width="48" height="48" alt="andrewting19" title="andrewting19"/></a> <a href="https://github.com/anpoirier"><img src="https://avatars.githubusercontent.com/u/1245729?v=4&s=48" width="48" height="48" alt="anpoirier" title="anpoirier"/></a> <a href="https://github.com/arthyn"><img src="https://avatars.githubusercontent.com/u/5466421?v=4&s=48" width="48" height="48" alt="arthyn" title="arthyn"/></a>
503
+ <a href="https://github.com/Asleep123"><img src="https://avatars.githubusercontent.com/u/122379135?v=4&s=48" width="48" height="48" alt="Asleep123" title="Asleep123"/></a> <a href="https://github.com/bolismauro"><img src="https://avatars.githubusercontent.com/u/771999?v=4&s=48" width="48" height="48" alt="bolismauro" title="bolismauro"/></a> <a href="https://github.com/chenyuan99"><img src="https://avatars.githubusercontent.com/u/25518100?v=4&s=48" width="48" height="48" alt="chenyuan99" title="chenyuan99"/></a> <a href="https://github.com/search?q=Clawdbot%20Maintainers"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Clawdbot Maintainers" title="Clawdbot Maintainers"/></a> <a href="https://github.com/conhecendoia"><img src="https://avatars.githubusercontent.com/u/82890727?v=4&s=48" width="48" height="48" alt="conhecendoia" title="conhecendoia"/></a> <a href="https://github.com/dasilva333"><img src="https://avatars.githubusercontent.com/u/947827?v=4&s=48" width="48" height="48" alt="dasilva333" title="dasilva333"/></a> <a href="https://github.com/David-Marsh-Photo"><img src="https://avatars.githubusercontent.com/u/228404527?v=4&s=48" width="48" height="48" alt="David-Marsh-Photo" title="David-Marsh-Photo"/></a> <a href="https://github.com/search?q=Developer"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Developer" title="Developer"/></a> <a href="https://github.com/search?q=Dimitrios%20Ploutarchos"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Dimitrios Ploutarchos" title="Dimitrios Ploutarchos"/></a> <a href="https://github.com/search?q=Drake%20Thomsen"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Drake Thomsen" title="Drake Thomsen"/></a>
504
+ <a href="https://github.com/fal3"><img src="https://avatars.githubusercontent.com/u/6484295?v=4&s=48" width="48" height="48" alt="fal3" title="fal3"/></a> <a href="https://github.com/search?q=Felix%20Krause"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Felix Krause" title="Felix Krause"/></a> <a href="https://github.com/foeken"><img src="https://avatars.githubusercontent.com/u/13864?v=4&s=48" width="48" height="48" alt="foeken" title="foeken"/></a> <a href="https://github.com/search?q=ganghyun%20kim"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="ganghyun kim" title="ganghyun kim"/></a> <a href="https://github.com/grrowl"><img src="https://avatars.githubusercontent.com/u/907140?v=4&s=48" width="48" height="48" alt="grrowl" title="grrowl"/></a> <a href="https://github.com/gtsifrikas"><img src="https://avatars.githubusercontent.com/u/8904378?v=4&s=48" width="48" height="48" alt="gtsifrikas" title="gtsifrikas"/></a> <a href="https://github.com/HazAT"><img src="https://avatars.githubusercontent.com/u/363802?v=4&s=48" width="48" height="48" alt="HazAT" title="HazAT"/></a> <a href="https://github.com/hrdwdmrbl"><img src="https://avatars.githubusercontent.com/u/554881?v=4&s=48" width="48" height="48" alt="hrdwdmrbl" title="hrdwdmrbl"/></a> <a href="https://github.com/hugobarauna"><img src="https://avatars.githubusercontent.com/u/2719?v=4&s=48" width="48" height="48" alt="hugobarauna" title="hugobarauna"/></a> <a href="https://github.com/search?q=Jamie%20Openshaw"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Jamie Openshaw" title="Jamie Openshaw"/></a>
505
+ <a href="https://github.com/search?q=Jane"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Jane" title="Jane"/></a> <a href="https://github.com/search?q=Jarvis"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Jarvis" title="Jarvis"/></a> <a href="https://github.com/search?q=Jefferson%20Nunn"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Jefferson Nunn" title="Jefferson Nunn"/></a> <a href="https://github.com/kentaro"><img src="https://avatars.githubusercontent.com/u/3458?v=4&s=48" width="48" height="48" alt="kentaro" title="kentaro"/></a> <a href="https://github.com/search?q=Kevin%20Lin"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Kevin Lin" title="Kevin Lin"/></a> <a href="https://github.com/kitze"><img src="https://avatars.githubusercontent.com/u/1160594?v=4&s=48" width="48" height="48" alt="kitze" title="kitze"/></a> <a href="https://github.com/Kiwitwitter"><img src="https://avatars.githubusercontent.com/u/25277769?v=4&s=48" width="48" height="48" alt="Kiwitwitter" title="Kiwitwitter"/></a> <a href="https://github.com/levifig"><img src="https://avatars.githubusercontent.com/u/1605?v=4&s=48" width="48" height="48" alt="levifig" title="levifig"/></a> <a href="https://github.com/search?q=Lloyd"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Lloyd" title="Lloyd"/></a> <a href="https://github.com/loukotal"><img src="https://avatars.githubusercontent.com/u/18210858?v=4&s=48" width="48" height="48" alt="loukotal" title="loukotal"/></a>
506
+ <a href="https://github.com/louzhixian"><img src="https://avatars.githubusercontent.com/u/7994361?v=4&s=48" width="48" height="48" alt="louzhixian" title="louzhixian"/></a> <a href="https://github.com/martinpucik"><img src="https://avatars.githubusercontent.com/u/5503097?v=4&s=48" width="48" height="48" alt="martinpucik" title="martinpucik"/></a> <a href="https://github.com/search?q=Matt%20mini"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Matt mini" title="Matt mini"/></a> <a href="https://github.com/mertcicekci0"><img src="https://avatars.githubusercontent.com/u/179321902?v=4&s=48" width="48" height="48" alt="mertcicekci0" title="mertcicekci0"/></a> <a href="https://github.com/search?q=Miles"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Miles" title="Miles"/></a> <a href="https://github.com/mrdbstn"><img src="https://avatars.githubusercontent.com/u/58957632?v=4&s=48" width="48" height="48" alt="mrdbstn" title="mrdbstn"/></a> <a href="https://github.com/MSch"><img src="https://avatars.githubusercontent.com/u/7475?v=4&s=48" width="48" height="48" alt="MSch" title="MSch"/></a> <a href="https://github.com/search?q=Mustafa%20Tag%20Eldeen"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Mustafa Tag Eldeen" title="Mustafa Tag Eldeen"/></a> <a href="https://github.com/ndraiman"><img src="https://avatars.githubusercontent.com/u/12609607?v=4&s=48" width="48" height="48" alt="ndraiman" title="ndraiman"/></a> <a href="https://github.com/nexty5870"><img src="https://avatars.githubusercontent.com/u/3869659?v=4&s=48" width="48" height="48" alt="nexty5870" title="nexty5870"/></a>
507
+ <a href="https://github.com/Noctivoro"><img src="https://avatars.githubusercontent.com/u/183974570?v=4&s=48" width="48" height="48" alt="Noctivoro" title="Noctivoro"/></a> <a href="https://github.com/ppamment"><img src="https://avatars.githubusercontent.com/u/2122919?v=4&s=48" width="48" height="48" alt="ppamment" title="ppamment"/></a> <a href="https://github.com/prathamdby"><img src="https://avatars.githubusercontent.com/u/134331217?v=4&s=48" width="48" height="48" alt="prathamdby" title="prathamdby"/></a> <a href="https://github.com/ptn1411"><img src="https://avatars.githubusercontent.com/u/57529765?v=4&s=48" width="48" height="48" alt="ptn1411" title="ptn1411"/></a> <a href="https://github.com/reeltimeapps"><img src="https://avatars.githubusercontent.com/u/637338?v=4&s=48" width="48" height="48" alt="reeltimeapps" title="reeltimeapps"/></a> <a href="https://github.com/RLTCmpe"><img src="https://avatars.githubusercontent.com/u/10762242?v=4&s=48" width="48" height="48" alt="RLTCmpe" title="RLTCmpe"/></a> <a href="https://github.com/search?q=Rolf%20Fredheim"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Rolf Fredheim" title="Rolf Fredheim"/></a> <a href="https://github.com/search?q=Rony%20Kelner"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Rony Kelner" title="Rony Kelner"/></a> <a href="https://github.com/search?q=Samrat%20Jha"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Samrat Jha" title="Samrat Jha"/></a> <a href="https://github.com/senoldogann"><img src="https://avatars.githubusercontent.com/u/45736551?v=4&s=48" width="48" height="48" alt="senoldogann" title="senoldogann"/></a>
508
+ <a href="https://github.com/Seredeep"><img src="https://avatars.githubusercontent.com/u/22802816?v=4&s=48" width="48" height="48" alt="Seredeep" title="Seredeep"/></a> <a href="https://github.com/sergical"><img src="https://avatars.githubusercontent.com/u/3760543?v=4&s=48" width="48" height="48" alt="sergical" title="sergical"/></a> <a href="https://github.com/shiv19"><img src="https://avatars.githubusercontent.com/u/9407019?v=4&s=48" width="48" height="48" alt="shiv19" title="shiv19"/></a> <a href="https://github.com/shiyuanhai"><img src="https://avatars.githubusercontent.com/u/1187370?v=4&s=48" width="48" height="48" alt="shiyuanhai" title="shiyuanhai"/></a> <a href="https://github.com/siraht"><img src="https://avatars.githubusercontent.com/u/73152895?v=4&s=48" width="48" height="48" alt="siraht" title="siraht"/></a> <a href="https://github.com/snopoke"><img src="https://avatars.githubusercontent.com/u/249606?v=4&s=48" width="48" height="48" alt="snopoke" title="snopoke"/></a> <a href="https://github.com/Suksham-sharma"><img src="https://avatars.githubusercontent.com/u/94667656?v=4&s=48" width="48" height="48" alt="Suksham-sharma" title="Suksham-sharma"/></a> <a href="https://github.com/search?q=techboss"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="techboss" title="techboss"/></a> <a href="https://github.com/testingabc321"><img src="https://avatars.githubusercontent.com/u/8577388?v=4&s=48" width="48" height="48" alt="testingabc321" title="testingabc321"/></a> <a href="https://github.com/search?q=The%20Admiral"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="The Admiral" title="The Admiral"/></a>
509
+ <a href="https://github.com/thesash"><img src="https://avatars.githubusercontent.com/u/1166151?v=4&s=48" width="48" height="48" alt="thesash" title="thesash"/></a> <a href="https://github.com/search?q=Ubuntu"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Ubuntu" title="Ubuntu"/></a> <a href="https://github.com/voidserf"><img src="https://avatars.githubusercontent.com/u/477673?v=4&s=48" width="48" height="48" alt="voidserf" title="voidserf"/></a> <a href="https://github.com/search?q=Vultr-Clawd%20Admin"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Vultr-Clawd Admin" title="Vultr-Clawd Admin"/></a> <a href="https://github.com/search?q=Wimmie"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Wimmie" title="Wimmie"/></a> <a href="https://github.com/search?q=wolfred"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="wolfred" title="wolfred"/></a> <a href="https://github.com/wstock"><img src="https://avatars.githubusercontent.com/u/1394687?v=4&s=48" width="48" height="48" alt="wstock" title="wstock"/></a> <a href="https://github.com/yazinsai"><img src="https://avatars.githubusercontent.com/u/1846034?v=4&s=48" width="48" height="48" alt="yazinsai" title="yazinsai"/></a> <a href="https://github.com/search?q=ymat19"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="ymat19" title="ymat19"/></a> <a href="https://github.com/search?q=Zach%20Knickerbocker"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Zach Knickerbocker" title="Zach Knickerbocker"/></a>
510
+ <a href="https://github.com/0xJonHoldsCrypto"><img src="https://avatars.githubusercontent.com/u/81202085?v=4&s=48" width="48" height="48" alt="0xJonHoldsCrypto" title="0xJonHoldsCrypto"/></a> <a href="https://github.com/aaronn"><img src="https://avatars.githubusercontent.com/u/1653630?v=4&s=48" width="48" height="48" alt="aaronn" title="aaronn"/></a> <a href="https://github.com/Alphonse-arianee"><img src="https://avatars.githubusercontent.com/u/254457365?v=4&s=48" width="48" height="48" alt="Alphonse-arianee" title="Alphonse-arianee"/></a> <a href="https://github.com/atalovesyou"><img src="https://avatars.githubusercontent.com/u/3534502?v=4&s=48" width="48" height="48" alt="atalovesyou" title="atalovesyou"/></a> <a href="https://github.com/search?q=Azade"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Azade" title="Azade"/></a> <a href="https://github.com/carlulsoe"><img src="https://avatars.githubusercontent.com/u/34673973?v=4&s=48" width="48" height="48" alt="carlulsoe" title="carlulsoe"/></a> <a href="https://github.com/search?q=ddyo"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="ddyo" title="ddyo"/></a> <a href="https://github.com/search?q=Erik"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Erik" title="Erik"/></a> <a href="https://github.com/hougangdev"><img src="https://avatars.githubusercontent.com/u/105773686?v=4&s=48" width="48" height="48" alt="hougangdev" title="hougangdev"/></a> <a href="https://github.com/latitudeki5223"><img src="https://avatars.githubusercontent.com/u/119656367?v=4&s=48" width="48" height="48" alt="latitudeki5223" title="latitudeki5223"/></a>
511
+ <a href="https://github.com/search?q=Manuel%20Maly"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Manuel Maly" title="Manuel Maly"/></a> <a href="https://github.com/search?q=Mourad%20Boustani"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Mourad Boustani" title="Mourad Boustani"/></a> <a href="https://github.com/odrobnik"><img src="https://avatars.githubusercontent.com/u/333270?v=4&s=48" width="48" height="48" alt="odrobnik" title="odrobnik"/></a> <a href="https://github.com/pcty-nextgen-ios-builder"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="pcty-nextgen-ios-builder" title="pcty-nextgen-ios-builder"/></a> <a href="https://github.com/search?q=Quentin"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Quentin" title="Quentin"/></a> <a href="https://github.com/search?q=Randy%20Torres"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Randy Torres" title="Randy Torres"/></a> <a href="https://github.com/rhjoh"><img src="https://avatars.githubusercontent.com/u/105699450?v=4&s=48" width="48" height="48" alt="rhjoh" title="rhjoh"/></a> <a href="https://github.com/ronak-guliani"><img src="https://avatars.githubusercontent.com/u/23518228?v=4&s=48" width="48" height="48" alt="ronak-guliani" title="ronak-guliani"/></a> <a href="https://github.com/search?q=William%20Stock"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="William Stock" title="William Stock"/></a>
512
+ </p>
SECURITY.md ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Security Policy
2
+
3
+ If you believe you've found a security issue in Moltbot, please report it privately.
4
+
5
+ ## Reporting
6
+
7
+ - Email: `steipete@gmail.com`
8
+ - What to include: reproduction steps, impact assessment, and (if possible) a minimal PoC.
9
+
10
+ ## Operational Guidance
11
+
12
+ For threat model + hardening guidance (including `moltbot security audit --deep` and `--fix`), see:
13
+
14
+ - `https://docs.molt.bot/gateway/security`
15
+
16
+ ### Web Interface Safety
17
+
18
+ Moltbot's web interface is intended for local use only. Do **not** bind it to the public internet; it is not hardened for public exposure.
19
+
20
+ ## Runtime Requirements
21
+
22
+ ### Node.js Version
23
+
24
+ Moltbot requires **Node.js 22.12.0 or later** (LTS). This version includes important security patches:
25
+
26
+ - CVE-2025-59466: async_hooks DoS vulnerability
27
+ - CVE-2026-21636: Permission model bypass vulnerability
28
+
29
+ Verify your Node.js version:
30
+
31
+ ```bash
32
+ node --version # Should be v22.12.0 or later
33
+ ```
34
+
35
+ ### Docker Security
36
+
37
+ When running Moltbot in Docker:
38
+
39
+ 1. The official image runs as a non-root user (`node`) for reduced attack surface
40
+ 2. Use `--read-only` flag when possible for additional filesystem protection
41
+ 3. Limit container capabilities with `--cap-drop=ALL`
42
+
43
+ Example secure Docker run:
44
+
45
+ ```bash
46
+ docker run --read-only --cap-drop=ALL \
47
+ -v moltbot-data:/app/data \
48
+ moltbot/moltbot:latest
49
+ ```
50
+
51
+ ## Security Scanning
52
+
53
+ This project uses `detect-secrets` for automated secret detection in CI/CD.
54
+ See `.detect-secrets.cfg` for configuration and `.secrets.baseline` for the baseline.
55
+
56
+ Run locally:
57
+
58
+ ```bash
59
+ pip install detect-secrets==1.5.0
60
+ detect-secrets scan --baseline .secrets.baseline
61
+ ```
Swabble/.github/workflows/ci.yml ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ jobs:
9
+ build-and-test:
10
+ runs-on: macos-latest
11
+ defaults:
12
+ run:
13
+ shell: bash
14
+ working-directory: swabble
15
+ steps:
16
+ - name: Checkout swabble
17
+ uses: actions/checkout@v4
18
+ with:
19
+ path: swabble
20
+
21
+ - name: Select Xcode 26.1 (prefer 26.1.1)
22
+ run: |
23
+ set -euo pipefail
24
+ # pick the newest installed 26.1.x, fallback to newest 26.x
25
+ CANDIDATE="$(ls -d /Applications/Xcode_26.1*.app 2>/dev/null | sort -V | tail -1 || true)"
26
+ if [[ -z "$CANDIDATE" ]]; then
27
+ CANDIDATE="$(ls -d /Applications/Xcode_26*.app 2>/dev/null | sort -V | tail -1 || true)"
28
+ fi
29
+ if [[ -z "$CANDIDATE" ]]; then
30
+ echo "No Xcode 26.x found on runner" >&2
31
+ exit 1
32
+ fi
33
+ echo "Selecting $CANDIDATE"
34
+ sudo xcode-select -s "$CANDIDATE"
35
+ xcodebuild -version
36
+
37
+ - name: Show Swift version
38
+ run: swift --version
39
+
40
+ - name: Install tooling
41
+ run: |
42
+ brew update
43
+ brew install swiftlint swiftformat
44
+
45
+ - name: Format check
46
+ run: |
47
+ ./scripts/format.sh
48
+ git diff --exit-code
49
+
50
+ - name: Lint
51
+ run: ./scripts/lint.sh
52
+
53
+ - name: Test
54
+ run: swift test --parallel
Swabble/.gitignore ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # macOS
2
+ .DS_Store
3
+
4
+ # SwiftPM / Build
5
+ /.build
6
+ /.swiftpm
7
+ /DerivedData
8
+ xcuserdata/
9
+ *.xcuserstate
10
+
11
+ # Editors
12
+ /.vscode
13
+ .idea/
14
+
15
+ # Xcode artifacts
16
+ *.hmap
17
+ *.ipa
18
+ *.dSYM.zip
19
+ *.dSYM
20
+
21
+ # Playgrounds
22
+ *.xcplayground
23
+ playground.xcworkspace
24
+ timeline.xctimeline
25
+
26
+ # Carthage
27
+ Carthage/Build/
28
+
29
+ # fastlane
30
+ fastlane/report.xml
31
+ fastlane/Preview.html
32
+ fastlane/screenshots/**/*.png
33
+ fastlane/test_output
Swabble/.swiftformat ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ --swiftversion 6.2
2
+ --indent 4
3
+ --maxwidth 120
4
+ --wraparguments before-first
5
+ --wrapcollections before-first
6
+ --stripunusedargs closure-only
7
+ --self remove
8
+ --header ""
Swabble/.swiftlint.yml ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # SwiftLint for swabble
2
+ included:
3
+ - Sources
4
+ excluded:
5
+ - .build
6
+ - DerivedData
7
+ - "**/.swiftpm"
8
+ - "**/.build"
9
+ - "**/DerivedData"
10
+ - "**/.DS_Store"
11
+ opt_in_rules:
12
+ - array_init
13
+ - closure_spacing
14
+ - explicit_init
15
+ - fatal_error_message
16
+ - first_where
17
+ - joined_default_parameter
18
+ - last_where
19
+ - literal_expression_end_indentation
20
+ - multiline_arguments
21
+ - multiline_parameters
22
+ - operator_usage_whitespace
23
+ - redundant_nil_coalescing
24
+ - sorted_first_last
25
+ - switch_case_alignment
26
+ - vertical_parameter_alignment_on_call
27
+ - vertical_whitespace_opening_braces
28
+ - vertical_whitespace_closing_braces
29
+
30
+ disabled_rules:
31
+ - trailing_whitespace
32
+ - trailing_newline
33
+ - indentation_width
34
+ - identifier_name
35
+ - explicit_self
36
+ - file_header
37
+ - todo
38
+
39
+ line_length:
40
+ warning: 140
41
+ error: 180
42
+
43
+ reporter: "xcode"
Swabble/CHANGELOG.md ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Changelog
2
+
3
+ ## 0.2.0 — 2025-12-23
4
+
5
+ ### Highlights
6
+ - Added `SwabbleKit` (multi-platform wake-word gate utilities with segment-aware gap detection).
7
+ - Swabble package now supports iOS + macOS consumers; CLI remains macOS 26-only.
8
+
9
+ ### Changes
10
+ - CLI wake-word matching/stripping routed through `SwabbleKit` helpers.
11
+ - Speech pipeline types now explicitly gated to macOS 26 / iOS 26 availability.
Swabble/LICENSE ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Peter Steinberger
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
Swabble/Package.resolved ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "originHash" : "c0677e232394b5f6b0191b6dbb5bae553d55264f65ae725cd03a8ffdfda9cdd3",
3
+ "pins" : [
4
+ {
5
+ "identity" : "commander",
6
+ "kind" : "remoteSourceControl",
7
+ "location" : "https://github.com/steipete/Commander.git",
8
+ "state" : {
9
+ "revision" : "9e349575c8e3c6745e81fe19e5bb5efa01b078ce",
10
+ "version" : "0.2.1"
11
+ }
12
+ },
13
+ {
14
+ "identity" : "swift-syntax",
15
+ "kind" : "remoteSourceControl",
16
+ "location" : "https://github.com/swiftlang/swift-syntax.git",
17
+ "state" : {
18
+ "revision" : "0687f71944021d616d34d922343dcef086855920",
19
+ "version" : "600.0.1"
20
+ }
21
+ },
22
+ {
23
+ "identity" : "swift-testing",
24
+ "kind" : "remoteSourceControl",
25
+ "location" : "https://github.com/apple/swift-testing",
26
+ "state" : {
27
+ "revision" : "399f76dcd91e4c688ca2301fa24a8cc6d9927211",
28
+ "version" : "0.99.0"
29
+ }
30
+ }
31
+ ],
32
+ "version" : 3
33
+ }
Swabble/Package.swift ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // swift-tools-version: 6.2
2
+ import PackageDescription
3
+
4
+ let package = Package(
5
+ name: "swabble",
6
+ platforms: [
7
+ .macOS(.v15),
8
+ .iOS(.v17),
9
+ ],
10
+ products: [
11
+ .library(name: "Swabble", targets: ["Swabble"]),
12
+ .library(name: "SwabbleKit", targets: ["SwabbleKit"]),
13
+ .executable(name: "swabble", targets: ["SwabbleCLI"]),
14
+ ],
15
+ dependencies: [
16
+ .package(url: "https://github.com/steipete/Commander.git", exact: "0.2.1"),
17
+ .package(url: "https://github.com/apple/swift-testing", from: "0.99.0"),
18
+ ],
19
+ targets: [
20
+ .target(
21
+ name: "Swabble",
22
+ path: "Sources/SwabbleCore",
23
+ swiftSettings: []),
24
+ .target(
25
+ name: "SwabbleKit",
26
+ path: "Sources/SwabbleKit",
27
+ swiftSettings: [
28
+ .enableUpcomingFeature("StrictConcurrency"),
29
+ ]),
30
+ .executableTarget(
31
+ name: "SwabbleCLI",
32
+ dependencies: [
33
+ "Swabble",
34
+ "SwabbleKit",
35
+ .product(name: "Commander", package: "Commander"),
36
+ ],
37
+ path: "Sources/swabble"),
38
+ .testTarget(
39
+ name: "SwabbleKitTests",
40
+ dependencies: [
41
+ "SwabbleKit",
42
+ .product(name: "Testing", package: "swift-testing"),
43
+ ],
44
+ swiftSettings: [
45
+ .enableUpcomingFeature("StrictConcurrency"),
46
+ .enableExperimentalFeature("SwiftTesting"),
47
+ ]),
48
+ .testTarget(
49
+ name: "swabbleTests",
50
+ dependencies: [
51
+ "Swabble",
52
+ .product(name: "Testing", package: "swift-testing"),
53
+ ]),
54
+ ],
55
+ swiftLanguageModes: [.v6])
Swabble/README.md ADDED
@@ -0,0 +1,111 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 🎙️ swabble — Speech.framework wake-word hook daemon (macOS 26)
2
+
3
+ swabble is a Swift 6.2 wake-word hook daemon. The CLI targets macOS 26 (SpeechAnalyzer + SpeechTranscriber). The shared `SwabbleKit` target is multi-platform and exposes wake-word gating utilities for iOS/macOS apps.
4
+
5
+ - **Local-only**: Speech.framework on-device models; zero network usage.
6
+ - **Wake word**: Default `clawd` (aliases `claude`), optional `--no-wake` bypass.
7
+ - **SwabbleKit**: Shared wake gate utilities (gap-based gating when you provide speech segments).
8
+ - **Hooks**: Run any command with prefix/env, cooldown, min_chars, timeout.
9
+ - **Services**: launchd helper stubs for start/stop/install.
10
+ - **File transcribe**: TXT or SRT with time ranges (using AttributedString splits).
11
+
12
+ ## Quick start
13
+ ```bash
14
+ # Install deps
15
+ brew install swiftformat swiftlint
16
+
17
+ # Build
18
+ swift build
19
+
20
+ # Write default config (~/.config/swabble/config.json)
21
+ swift run swabble setup
22
+
23
+ # Run foreground daemon
24
+ swift run swabble serve
25
+
26
+ # Test your hook
27
+ swift run swabble test-hook "hello world"
28
+
29
+ # Transcribe a file to SRT
30
+ swift run swabble transcribe /path/to/audio.m4a --format srt --output out.srt
31
+ ```
32
+
33
+ ## Use as a library
34
+ Add swabble as a SwiftPM dependency and import the `Swabble` or `SwabbleKit` product:
35
+
36
+ ```swift
37
+ // Package.swift
38
+ dependencies: [
39
+ .package(url: "https://github.com/steipete/swabble.git", branch: "main"),
40
+ ],
41
+ targets: [
42
+ .target(name: "MyApp", dependencies: [
43
+ .product(name: "Swabble", package: "swabble"), // Speech pipeline (macOS 26+ / iOS 26+)
44
+ .product(name: "SwabbleKit", package: "swabble"), // Wake-word gate utilities (iOS 17+ / macOS 15+)
45
+ ]),
46
+ ]
47
+ ```
48
+
49
+ ## CLI
50
+ - `serve` — foreground loop (mic → wake → hook)
51
+ - `transcribe <file>` — offline transcription (txt|srt)
52
+ - `test-hook "text"` — invoke configured hook
53
+ - `mic list|set <index>` — enumerate/select input device
54
+ - `setup` — write default config JSON
55
+ - `doctor` — check Speech auth & device availability
56
+ - `health` — prints `ok`
57
+ - `tail-log` — last 10 transcripts
58
+ - `status` — show wake state + recent transcripts
59
+ - `service install|uninstall|status` — user launchd plist (stub: prints launchctl commands)
60
+ - `start|stop|restart` — placeholders until full launchd wiring
61
+
62
+ All commands accept Commander runtime flags (`-v/--verbose`, `--json-output`, `--log-level`), plus `--config` where applicable.
63
+
64
+ ## Config
65
+ `~/.config/swabble/config.json` (auto-created by `setup`):
66
+ ```json
67
+ {
68
+ "audio": {"deviceName": "", "deviceIndex": -1, "sampleRate": 16000, "channels": 1},
69
+ "wake": {"enabled": true, "word": "clawd", "aliases": ["claude"]},
70
+ "hook": {
71
+ "command": "",
72
+ "args": [],
73
+ "prefix": "Voice swabble from ${hostname}: ",
74
+ "cooldownSeconds": 1,
75
+ "minCharacters": 24,
76
+ "timeoutSeconds": 5,
77
+ "env": {}
78
+ },
79
+ "logging": {"level": "info", "format": "text"},
80
+ "transcripts": {"enabled": true, "maxEntries": 50},
81
+ "speech": {"localeIdentifier": "en_US", "etiquetteReplacements": false}
82
+ }
83
+ ```
84
+
85
+ - Config path override: `--config /path/to/config.json` on relevant commands.
86
+ - Transcripts persist to `~/Library/Application Support/swabble/transcripts.log`.
87
+
88
+ ## Hook protocol
89
+ When a wake-gated transcript passes min_chars & cooldown, swabble runs:
90
+ ```
91
+ <command> <args...> "<prefix><text>"
92
+ ```
93
+ Environment variables:
94
+ - `SWABBLE_TEXT` — stripped transcript (wake word removed)
95
+ - `SWABBLE_PREFIX` — rendered prefix (hostname substituted)
96
+ - plus any `hook.env` key/values
97
+
98
+ ## Speech pipeline
99
+ - `AVAudioEngine` tap → `BufferConverter` → `AnalyzerInput` → `SpeechAnalyzer` with a `SpeechTranscriber` module.
100
+ - Requests volatile + final results; the CLI uses text-only wake gating today.
101
+ - Authorization requested at first start; requires macOS 26 + new Speech.framework APIs.
102
+
103
+ ## Development
104
+ - Format: `./scripts/format.sh` (uses local `.swiftformat`)
105
+ - Lint: `./scripts/lint.sh` (uses local `.swiftlint.yml`)
106
+ - Tests: `swift test` (uses swift-testing package)
107
+
108
+ ## Roadmap
109
+ - launchd control (load/bootout, PID + status socket)
110
+ - JSON logging + PII redaction toggle
111
+ - Stronger wake-word detection and control socket status/health
Swabble/Sources/SwabbleCore/Config/Config.swift ADDED
@@ -0,0 +1,77 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import Foundation
2
+
3
+ public struct SwabbleConfig: Codable, Sendable {
4
+ public struct Audio: Codable, Sendable {
5
+ public var deviceName: String = ""
6
+ public var deviceIndex: Int = -1
7
+ public var sampleRate: Double = 16000
8
+ public var channels: Int = 1
9
+ }
10
+
11
+ public struct Wake: Codable, Sendable {
12
+ public var enabled: Bool = true
13
+ public var word: String = "clawd"
14
+ public var aliases: [String] = ["claude"]
15
+ }
16
+
17
+ public struct Hook: Codable, Sendable {
18
+ public var command: String = ""
19
+ public var args: [String] = []
20
+ public var prefix: String = "Voice swabble from ${hostname}: "
21
+ public var cooldownSeconds: Double = 1
22
+ public var minCharacters: Int = 24
23
+ public var timeoutSeconds: Double = 5
24
+ public var env: [String: String] = [:]
25
+ }
26
+
27
+ public struct Logging: Codable, Sendable {
28
+ public var level: String = "info"
29
+ public var format: String = "text" // text|json placeholder
30
+ }
31
+
32
+ public struct Transcripts: Codable, Sendable {
33
+ public var enabled: Bool = true
34
+ public var maxEntries: Int = 50
35
+ }
36
+
37
+ public struct Speech: Codable, Sendable {
38
+ public var localeIdentifier: String = Locale.current.identifier
39
+ public var etiquetteReplacements: Bool = false
40
+ }
41
+
42
+ public var audio = Audio()
43
+ public var wake = Wake()
44
+ public var hook = Hook()
45
+ public var logging = Logging()
46
+ public var transcripts = Transcripts()
47
+ public var speech = Speech()
48
+
49
+ public static let defaultPath = FileManager.default
50
+ .homeDirectoryForCurrentUser
51
+ .appendingPathComponent(".config/swabble/config.json")
52
+
53
+ public init() {}
54
+ }
55
+
56
+ public enum ConfigError: Error {
57
+ case missingConfig
58
+ }
59
+
60
+ public enum ConfigLoader {
61
+ public static func load(at path: URL?) throws -> SwabbleConfig {
62
+ let url = path ?? SwabbleConfig.defaultPath
63
+ if !FileManager.default.fileExists(atPath: url.path) {
64
+ throw ConfigError.missingConfig
65
+ }
66
+ let data = try Data(contentsOf: url)
67
+ return try JSONDecoder().decode(SwabbleConfig.self, from: data)
68
+ }
69
+
70
+ public static func save(_ config: SwabbleConfig, at path: URL?) throws {
71
+ let url = path ?? SwabbleConfig.defaultPath
72
+ let dir = url.deletingLastPathComponent()
73
+ try FileManager.default.createDirectory(at: dir, withIntermediateDirectories: true)
74
+ let data = try JSONEncoder().encode(config)
75
+ try data.write(to: url)
76
+ }
77
+ }
Swabble/Sources/SwabbleCore/Hooks/HookExecutor.swift ADDED
@@ -0,0 +1,75 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import Foundation
2
+
3
+ public struct HookJob: Sendable {
4
+ public let text: String
5
+ public let timestamp: Date
6
+
7
+ public init(text: String, timestamp: Date) {
8
+ self.text = text
9
+ self.timestamp = timestamp
10
+ }
11
+ }
12
+
13
+ public actor HookExecutor {
14
+ private let config: SwabbleConfig
15
+ private var lastRun: Date?
16
+ private let hostname: String
17
+
18
+ public init(config: SwabbleConfig) {
19
+ self.config = config
20
+ hostname = Host.current().localizedName ?? "host"
21
+ }
22
+
23
+ public func shouldRun() -> Bool {
24
+ guard config.hook.cooldownSeconds > 0 else { return true }
25
+ if let lastRun, Date().timeIntervalSince(lastRun) < config.hook.cooldownSeconds {
26
+ return false
27
+ }
28
+ return true
29
+ }
30
+
31
+ public func run(job: HookJob) async throws {
32
+ guard shouldRun() else { return }
33
+ guard !config.hook.command.isEmpty else { throw NSError(
34
+ domain: "Hook",
35
+ code: 1,
36
+ userInfo: [NSLocalizedDescriptionKey: "hook command not set"]) }
37
+
38
+ let prefix = config.hook.prefix.replacingOccurrences(of: "${hostname}", with: hostname)
39
+ let payload = prefix + job.text
40
+
41
+ let process = Process()
42
+ process.executableURL = URL(fileURLWithPath: config.hook.command)
43
+ process.arguments = config.hook.args + [payload]
44
+
45
+ var env = ProcessInfo.processInfo.environment
46
+ env["SWABBLE_TEXT"] = job.text
47
+ env["SWABBLE_PREFIX"] = prefix
48
+ for (k, v) in config.hook.env {
49
+ env[k] = v
50
+ }
51
+ process.environment = env
52
+
53
+ let pipe = Pipe()
54
+ process.standardOutput = pipe
55
+ process.standardError = pipe
56
+
57
+ try process.run()
58
+
59
+ let timeoutNanos = UInt64(max(config.hook.timeoutSeconds, 0.1) * 1_000_000_000)
60
+ try await withThrowingTaskGroup(of: Void.self) { group in
61
+ group.addTask {
62
+ process.waitUntilExit()
63
+ }
64
+ group.addTask {
65
+ try await Task.sleep(nanoseconds: timeoutNanos)
66
+ if process.isRunning {
67
+ process.terminate()
68
+ }
69
+ }
70
+ try await group.next()
71
+ group.cancelAll()
72
+ }
73
+ lastRun = Date()
74
+ }
75
+ }