daemora 1.0.1 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (134) hide show
  1. package/README.md +106 -76
  2. package/SOUL.md +100 -28
  3. package/config/mcp.json +9 -9
  4. package/package.json +15 -8
  5. package/skills/apple-notes.md +0 -52
  6. package/skills/apple-reminders.md +1 -87
  7. package/skills/camsnap.md +20 -144
  8. package/skills/coding.md +7 -7
  9. package/skills/documents.md +6 -6
  10. package/skills/email.md +6 -6
  11. package/skills/gif-search.md +28 -171
  12. package/skills/healthcheck.md +21 -203
  13. package/skills/image-gen.md +24 -123
  14. package/skills/model-usage.md +18 -165
  15. package/skills/obsidian.md +28 -174
  16. package/skills/pdf.md +30 -181
  17. package/skills/research.md +6 -6
  18. package/skills/skill-creator.md +35 -111
  19. package/skills/spotify.md +2 -17
  20. package/skills/summarize.md +36 -193
  21. package/skills/things.md +23 -175
  22. package/skills/tmux.md +1 -91
  23. package/skills/trello.md +32 -157
  24. package/skills/video-frames.md +26 -166
  25. package/skills/weather.md +6 -6
  26. package/src/a2a/A2AClient.js +2 -2
  27. package/src/a2a/A2AServer.js +6 -6
  28. package/src/a2a/AgentCard.js +2 -2
  29. package/src/agents/SubAgentManager.js +61 -19
  30. package/src/agents/Supervisor.js +4 -4
  31. package/src/channels/BaseChannel.js +6 -6
  32. package/src/channels/BlueBubblesChannel.js +112 -0
  33. package/src/channels/DiscordChannel.js +8 -8
  34. package/src/channels/EmailChannel.js +54 -26
  35. package/src/channels/FeishuChannel.js +140 -0
  36. package/src/channels/GoogleChatChannel.js +8 -8
  37. package/src/channels/HttpChannel.js +2 -2
  38. package/src/channels/IRCChannel.js +144 -0
  39. package/src/channels/LineChannel.js +13 -13
  40. package/src/channels/MatrixChannel.js +97 -0
  41. package/src/channels/MattermostChannel.js +119 -0
  42. package/src/channels/NextcloudChannel.js +133 -0
  43. package/src/channels/NostrChannel.js +175 -0
  44. package/src/channels/SignalChannel.js +9 -9
  45. package/src/channels/SlackChannel.js +10 -10
  46. package/src/channels/TeamsChannel.js +10 -10
  47. package/src/channels/TelegramChannel.js +8 -8
  48. package/src/channels/TwitchChannel.js +128 -0
  49. package/src/channels/WhatsAppChannel.js +10 -10
  50. package/src/channels/ZaloChannel.js +119 -0
  51. package/src/channels/iMessageChannel.js +150 -0
  52. package/src/channels/index.js +241 -11
  53. package/src/cli.js +835 -38
  54. package/src/config/agentProfiles.js +19 -19
  55. package/src/config/channels.js +1 -1
  56. package/src/config/default.js +12 -7
  57. package/src/config/models.js +3 -3
  58. package/src/config/permissions.js +2 -2
  59. package/src/core/AgentLoop.js +13 -13
  60. package/src/core/Compaction.js +3 -3
  61. package/src/core/CostTracker.js +2 -2
  62. package/src/core/EventBus.js +15 -15
  63. package/src/core/TaskQueue.js +24 -7
  64. package/src/core/TaskRunner.js +19 -6
  65. package/src/daemon/DaemonManager.js +4 -4
  66. package/src/hooks/HookRunner.js +4 -4
  67. package/src/index.js +6 -2
  68. package/src/mcp/MCPAgentRunner.js +3 -3
  69. package/src/mcp/MCPClient.js +9 -9
  70. package/src/mcp/MCPManager.js +14 -14
  71. package/src/models/ModelRouter.js +2 -2
  72. package/src/safety/AuditLog.js +3 -3
  73. package/src/safety/CircuitBreaker.js +2 -2
  74. package/src/safety/CommandGuard.js +132 -0
  75. package/src/safety/FilesystemGuard.js +23 -3
  76. package/src/safety/GitRollback.js +5 -5
  77. package/src/safety/HumanApproval.js +9 -9
  78. package/src/safety/InputSanitizer.js +81 -8
  79. package/src/safety/PermissionGuard.js +2 -2
  80. package/src/safety/Sandbox.js +1 -1
  81. package/src/safety/SecretScanner.js +90 -28
  82. package/src/safety/SecretVault.js +2 -2
  83. package/src/scheduler/Heartbeat.js +3 -3
  84. package/src/scheduler/Scheduler.js +6 -6
  85. package/src/setup/theme.js +171 -66
  86. package/src/setup/wizard.js +432 -57
  87. package/src/skills/SkillLoader.js +145 -8
  88. package/src/storage/TaskStore.js +39 -15
  89. package/src/systemPrompt.js +45 -43
  90. package/src/tenants/TenantManager.js +79 -22
  91. package/src/tools/ToolRegistry.js +3 -3
  92. package/src/tools/applyPatch.js +2 -2
  93. package/src/tools/browserAutomation.js +4 -4
  94. package/src/tools/calendar.js +155 -0
  95. package/src/tools/clipboard.js +71 -0
  96. package/src/tools/contacts.js +138 -0
  97. package/src/tools/createDocument.js +2 -2
  98. package/src/tools/cronTool.js +14 -14
  99. package/src/tools/database.js +165 -0
  100. package/src/tools/editFile.js +10 -10
  101. package/src/tools/executeCommand.js +11 -3
  102. package/src/tools/generateImage.js +79 -0
  103. package/src/tools/gitTool.js +141 -0
  104. package/src/tools/glob.js +1 -1
  105. package/src/tools/googlePlaces.js +136 -0
  106. package/src/tools/grep.js +2 -2
  107. package/src/tools/iMessageTool.js +86 -0
  108. package/src/tools/imageAnalysis.js +3 -3
  109. package/src/tools/index.js +56 -2
  110. package/src/tools/makeVoiceCall.js +283 -0
  111. package/src/tools/manageAgents.js +2 -2
  112. package/src/tools/manageMCP.js +38 -20
  113. package/src/tools/memory.js +25 -32
  114. package/src/tools/messageChannel.js +1 -1
  115. package/src/tools/notification.js +90 -0
  116. package/src/tools/philipsHue.js +147 -0
  117. package/src/tools/projectTracker.js +8 -8
  118. package/src/tools/readFile.js +1 -1
  119. package/src/tools/readPDF.js +73 -0
  120. package/src/tools/screenCapture.js +6 -6
  121. package/src/tools/searchContent.js +2 -2
  122. package/src/tools/searchFiles.js +1 -1
  123. package/src/tools/sendEmail.js +79 -24
  124. package/src/tools/sendFile.js +4 -4
  125. package/src/tools/sonos.js +137 -0
  126. package/src/tools/sshTool.js +130 -0
  127. package/src/tools/textToSpeech.js +5 -5
  128. package/src/tools/transcribeAudio.js +4 -4
  129. package/src/tools/useMCP.js +4 -4
  130. package/src/tools/webFetch.js +2 -2
  131. package/src/tools/webSearch.js +1 -1
  132. package/src/utils/Embeddings.js +79 -0
  133. package/src/voice/VoiceSessionManager.js +170 -0
  134. package/src/voice/VoiceWebhook.js +188 -0
@@ -1,196 +1,53 @@
1
1
  ---
2
2
  name: gif-search
3
- description: Search for GIFs, download and send animated GIFs, create GIFs from images or video clips. Use when the user asks for a GIF, wants to find a funny/reaction GIF, send a GIF, or convert something to a GIF. Searches Giphy/Tenor by default; can create GIFs from local video too.
3
+ description: Search for GIFs, download and send animated GIFs, create GIFs from images or video. Use when the user asks for a GIF, wants to find a reaction GIF, send a GIF, or create a GIF from video.
4
4
  triggers: gif, find gif, send gif, reaction gif, funny gif, animated gif, search gif, giphy, tenor, make gif, create gif
5
+ metadata: {"daemora": {"emoji": "🎭"}}
5
6
  ---
6
7
 
7
- ## When to Use
8
+ ## Search Tenor (no API key needed)
8
9
 
9
- ✅ Find and send reaction GIFs, search Giphy/Tenor by keyword, create GIFs from video clips or image sequences, convert media to GIF
10
-
11
- Large video files → use video-frames skill instead; static images just share the image directly
12
-
13
- ## Search Giphy (requires free API key)
14
-
15
- ```python
16
- #!/usr/bin/env python3
17
- """Search Giphy for GIFs by keyword."""
18
- import os, json, urllib.request, urllib.parse
19
- from pathlib import Path
20
-
21
- GIPHY_KEY = os.environ.get("GIPHY_API_KEY", "") # free key at developers.giphy.com
22
-
23
- def search_gif(query: str, limit: int = 5, rating: str = "g") -> list[dict]:
24
- """Search Giphy. Returns list of GIF info dicts."""
25
- if not GIPHY_KEY:
26
- # Fall back to Giphy public beta key (rate-limited)
27
- key = "dc6zaTOxFJmzC"
28
- else:
29
- key = GIPHY_KEY
30
-
31
- params = urllib.parse.urlencode({
32
- "q": query, "api_key": key,
33
- "limit": limit, "rating": rating,
34
- "lang": "en"
35
- })
36
- url = f"https://api.giphy.com/v1/gifs/search?{params}"
37
- data = json.loads(urllib.request.urlopen(url).read())
38
- return [
39
- {
40
- "title": g["title"],
41
- "url": g["images"]["original"]["url"],
42
- "mp4": g["images"]["original"]["mp4"],
43
- "preview": g["images"]["fixed_height_small"]["url"],
44
- "width": int(g["images"]["original"]["width"]),
45
- "height": int(g["images"]["original"]["height"]),
46
- }
47
- for g in data.get("data", [])
48
- ]
49
-
50
- def download_gif(url: str, output: str = None) -> str:
51
- """Download a GIF to a local path."""
52
- if not output:
53
- from datetime import datetime
54
- output = f"/tmp/gif_{datetime.now().strftime('%Y%m%d%H%M%S')}.gif"
55
- urllib.request.urlretrieve(url, output)
56
- size = Path(output).stat().st_size
57
- print(f"✅ GIF downloaded: {output} ({size//1024}KB)")
58
- return output
59
-
60
- # Search and get first result
61
- results = search_gif("excited celebration")
62
- if results:
63
- g = results[0]
64
- print(f"Found: {g['title']}")
65
- path = download_gif(g["url"])
66
- # Then: sendFile(path, channel, sessionId) to send via Telegram/Slack/Discord
67
- ```
68
-
69
- ## Search Tenor (no API key needed for basic use)
70
-
71
- ```python
72
- #!/usr/bin/env python3
73
- import json, urllib.request, urllib.parse
74
-
75
- def search_tenor(query: str, limit: int = 5) -> list[dict]:
76
- """Search Tenor GIFs. Uses public key."""
77
- params = urllib.parse.urlencode({
78
- "q": query,
79
- "key": "LIVDSRZULELA", # Tenor public demo key
80
- "limit": limit,
81
- "media_filter": "minimal",
82
- "contentfilter": "low"
83
- })
84
- url = f"https://tenor.googleapis.com/v2/search?{params}"
85
- data = json.loads(urllib.request.urlopen(url).read())
86
- return [
87
- {
88
- "title": r.get("content_description", ""),
89
- "url": r["media_formats"]["gif"]["url"],
90
- "mp4": r["media_formats"].get("mp4", {}).get("url", ""),
91
- "size": r["media_formats"]["gif"].get("size", 0),
92
- }
93
- for r in data.get("results", [])
94
- ]
95
-
96
- results = search_tenor("happy dance")
97
- for r in results[:3]:
98
- print(f" • {r['title'][:60]} ({r['size']//1024}KB)")
99
- print(f" {r['url']}")
10
+ ```bash
11
+ curl -s "https://tenor.googleapis.com/v2/search?q=happy+dance&key=LIVDSRZULELA&limit=5&media_filter=minimal" \
12
+ | python3 -c "import sys,json; [print(r['media_formats']['gif']['url']) for r in json.load(sys.stdin)['results']]"
100
13
  ```
101
14
 
102
- ## Create GIF from Video Clip
15
+ ## Search Giphy (free key at developers.giphy.com)
103
16
 
104
17
  ```bash
105
- # Requires ffmpeg (brew install ffmpeg)
106
- VIDEO="/path/to/video.mp4"
107
- START="00:00:05"
108
- DURATION=3 # seconds
109
- OUTPUT="/tmp/clip.gif"
110
- WIDTH=480
111
-
112
- ffmpeg -ss "$START" -t "$DURATION" -i "$VIDEO" \
113
- -vf "fps=10,scale=${WIDTH}:-1:flags=lanczos,split[s0][s1];[s0]palettegen=max_colors=128[p];[s1][p]paletteuse=dither=bayer" \
114
- -loop 0 "$OUTPUT" -y -loglevel quiet
115
-
116
- SIZE=$(du -h "$OUTPUT" | cut -f1)
117
- echo "✅ GIF: $OUTPUT ($SIZE)"
18
+ curl -s "https://api.giphy.com/v1/gifs/search?q=celebration&api_key=$GIPHY_API_KEY&limit=5&rating=g" \
19
+ | python3 -c "import sys,json; [print(g['images']['original']['url']) for g in json.load(sys.stdin)['data']]"
118
20
  ```
119
21
 
120
- ## Create GIF from Image Sequence
22
+ ## Download GIF
121
23
 
122
24
  ```bash
123
- # Convert a series of images to GIF
124
- INPUT_DIR="/tmp/frames"
125
- OUTPUT="/tmp/animation.gif"
126
- DELAY=10 # delay between frames (1/100 sec) — 10 = 100ms = 10fps
127
-
128
- # Using ImageMagick (brew install imagemagick)
129
- convert -delay $DELAY -loop 0 "${INPUT_DIR}"/*.png "$OUTPUT"
130
-
131
- # Or with ffmpeg (better quality)
132
- ffmpeg -framerate 10 -pattern_type glob -i "${INPUT_DIR}/*.png" \
133
- -vf "scale=480:-1:flags=lanczos,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse" \
134
- -loop 0 "$OUTPUT" -y -loglevel quiet
135
-
136
- echo "✅ GIF: $OUTPUT"
25
+ curl -L -o /tmp/reaction.gif "GIF_URL_HERE"
137
26
  ```
138
27
 
139
- ## Optimize GIF Size
28
+ ## Create GIF from video clip
140
29
 
141
30
  ```bash
142
- # GIFs can be huge — optimize before sending
143
- INPUT="/tmp/original.gif"
144
- OUTPUT="/tmp/optimized.gif"
145
-
146
- # Using gifsicle (brew install gifsicle)
147
- gifsicle --optimize=3 --lossy=80 "$INPUT" -o "$OUTPUT"
148
-
149
- BEFORE=$(du -h "$INPUT" | cut -f1)
150
- AFTER=$(du -h "$OUTPUT" | cut -f1)
151
- echo "Before: $BEFORE → After: $AFTER"
152
-
153
- # Or reduce size by lowering resolution
154
- ffmpeg -i "$INPUT" -vf "scale=320:-1:flags=lanczos" "$OUTPUT" -y -loglevel quiet
31
+ # Requires ffmpeg (brew install ffmpeg)
32
+ ffmpeg -ss 00:00:05 -t 3 -i video.mp4 \
33
+ -vf "fps=10,scale=480:-1:flags=lanczos,split[s0][s1];[s0]palettegen=max_colors=128[p];[s1][p]paletteuse=dither=bayer" \
34
+ -loop 0 /tmp/clip.gif -y -loglevel quiet
155
35
  ```
156
36
 
157
- ## Full Workflow: Search Download → Send
158
-
159
- ```python
160
- #!/usr/bin/env python3
161
- """Search for a GIF and prepare it to send via a channel."""
162
-
163
- def get_gif_for_message(query: str, channel: str = "telegram", session_id: str = "") -> str:
164
- """
165
- Search for a GIF matching the query, download it, return the local path.
166
- Then caller uses sendFile(path, channel, session_id) to deliver it.
167
- """
168
- # Try Tenor first (no key needed), fall back to Giphy
169
- results = search_tenor(query, limit=3)
170
- if not results:
171
- results = search_gif(query, limit=3)
172
-
173
- if not results:
174
- return None
37
+ ## Create GIF from image sequence
175
38
 
176
- # Pick first result
177
- gif_url = results[0]["url"]
178
- path = download_gif(gif_url)
179
- return path
180
-
181
- # Example:
182
- path = get_gif_for_message("thumbs up success")
183
- if path:
184
- print(f"Ready to send: {path}")
185
- # sendFile(path, "telegram", session_id)
39
+ ```bash
40
+ ffmpeg -framerate 10 -pattern_type glob -i "/tmp/frames/*.png" \
41
+ -vf "scale=480:-1:flags=lanczos,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse" \
42
+ -loop 0 /tmp/animation.gif -y -loglevel quiet
43
+ # Or: brew install imagemagick && convert -delay 10 -loop 0 /tmp/frames/*.png /tmp/animation.gif
186
44
  ```
187
45
 
188
- ## Response Format
46
+ ## Workflow
189
47
 
190
- When the user asks to send a GIF:
191
- 1. Search with relevant keywords
192
- 2. Download the best match
193
- 3. Send via `sendFile(path, channel, sessionId)`
194
- 4. Confirm: "Sent 🎉 GIF: [title]"
48
+ 1. Search Tenor first (no key needed), fall back to Giphy if no results
49
+ 2. Download the best match to `/tmp/reaction.gif`
50
+ 3. Send: `sendFile("/tmp/reaction.gif", channel, sessionId)`
51
+ 4. Confirm: "Sent 🎉 [GIF title]"
195
52
 
196
- If the channel doesn't support GIFs (plain text email), share the URL instead.
53
+ If the channel doesn't support GIFs, share the URL instead.
@@ -1,222 +1,40 @@
1
1
  ---
2
2
  name: healthcheck
3
- description: Monitor system health, check disk usage, CPU load, memory, running processes, network connectivity, and service status. Use when the user asks about system health, disk space, memory usage, CPU usage, running processes, server status, or whether a service is running. Works on macOS and Linux.
3
+ description: Monitor system health, check disk usage, CPU load, memory, running processes, network connectivity, and service status. Use when the user asks about system health, disk space, memory usage, CPU usage, running processes, server status, or whether a service is running.
4
4
  triggers: health check, disk space, memory usage, CPU usage, system status, process running, server health, is running, disk full, system monitor, uptime, load average
5
+ metadata: {"daemora": {"emoji": "🩺"}}
5
6
  ---
6
7
 
7
- ## When to Use
8
-
9
- ✅ Check disk space, memory, CPU, running processes, network, service uptime, port availability, web endpoint health, log tails
10
-
11
- ❌ Deep profiling or performance tracing — use dedicated profilers (Instruments, perf, etc.)
12
-
13
- ## Full System Health Report
14
-
15
- ```bash
16
- python3 << 'EOF'
17
- import subprocess, platform, os
18
- from datetime import datetime
19
-
20
- def run(cmd):
21
- r = subprocess.run(cmd, shell=True, capture_output=True, text=True)
22
- return r.stdout.strip()
23
-
24
- print(f"🖥 System Health Report — {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
25
- print(f"{'─'*50}")
26
-
27
- # OS + Uptime
28
- print(f"OS: {platform.system()} {platform.mac_ver()[0] or platform.release()}")
29
- print(f"Uptime: {run('uptime | sed s/.*up/up/')}")
30
-
31
- # CPU
32
- if platform.system() == "Darwin":
33
- load = os.getloadavg()
34
- cpu_count = int(run("sysctl -n hw.ncpu"))
35
- print(f"CPU: load {load[0]:.2f} / {load[1]:.2f} / {load[2]:.2f} ({cpu_count} cores)")
36
- else:
37
- print(f"CPU: {run('top -bn1 | grep Cpu | head -1')}")
38
-
39
- # Memory
40
- if platform.system() == "Darwin":
41
- mem = run("vm_stat")
42
- page_size = 4096
43
- lines = {l.split(':')[0].strip(): int(l.split(':')[1].strip().rstrip('.'))
44
- for l in mem.split('\n') if ':' in l}
45
- free = (lines.get('Pages free', 0) + lines.get('Pages speculative', 0)) * page_size / 1024**3
46
- active = lines.get('Pages active', 0) * page_size / 1024**3
47
- wired = lines.get('Pages wired down', 0) * page_size / 1024**3
48
- total = float(run("sysctl -n hw.memsize")) / 1024**3
49
- used = total - free
50
- print(f"Memory: {used:.1f}GB used / {total:.1f}GB total ({free:.1f}GB free)")
51
- else:
52
- print(f"Memory: {run('free -h | grep Mem')}")
53
-
54
- # Disk
55
- import shutil
56
- disks = ['/'] + (['/Volumes/' + d for d in os.listdir('/Volumes')] if os.path.exists('/Volumes') else [])
57
- print("\nDisk Usage:")
58
- for disk in disks:
59
- try:
60
- total, used, free = shutil.disk_usage(disk)
61
- pct = used / total * 100
62
- bar = '█' * int(pct/5) + '░' * (20 - int(pct/5))
63
- status = "⚠️" if pct > 85 else "✅"
64
- print(f" {status} {disk:<20} {bar} {pct:.0f}% ({free/1024**3:.0f}GB free)")
65
- except:
66
- pass
67
- EOF
68
- ```
69
-
70
- ## Disk Space Check
71
-
72
- ```bash
73
- # Quick disk check
74
- df -h | grep -E "^/dev|Filesystem"
75
-
76
- # Find largest directories (top 10)
77
- du -sh /* 2>/dev/null | sort -rh | head -10
78
-
79
- # Find large files (>100MB) in a directory
80
- find ~/Downloads -size +100M -exec ls -lh {} \; 2>/dev/null | sort -k5 -rh | head -20
81
-
82
- # Check inode usage (different from disk space — can be "full" with space remaining)
83
- df -i /
84
- ```
85
-
86
- ## Memory Usage
87
-
88
- ```bash
89
- # macOS
90
- vm_stat && top -l 1 -n 0 | grep -E "PhysMem|CPU"
91
-
92
- # Top memory-consuming processes
93
- ps aux --sort=-%mem 2>/dev/null | head -10 || \
94
- ps aux | sort -k4 -rn | head -10
95
- ```
96
-
97
- ## CPU Usage
98
-
99
- ```bash
100
- # Top CPU processes
101
- ps aux --sort=-%cpu 2>/dev/null | head -10 || \
102
- ps aux | sort -k3 -rn | head -10
103
-
104
- # macOS: CPU usage over 5 seconds
105
- top -l 2 -n 0 -stats cpu | tail -5
106
- ```
107
-
108
- ## Check if a Process is Running
109
-
110
- ```python
111
- #!/usr/bin/env python3
112
- import subprocess, sys
113
-
114
- def is_running(process_name: str) -> tuple[bool, list[str]]:
115
- """Check if a process is running. Returns (running, [pid lines])."""
116
- result = subprocess.run(
117
- ["pgrep", "-fl", process_name],
118
- capture_output=True, text=True
119
- )
120
- lines = [l for l in result.stdout.strip().split('\n') if l and process_name.lower() in l.lower()]
121
- return len(lines) > 0, lines
122
-
123
- # Check common services
124
- services = ["nginx", "postgres", "redis-server", "node", "python3", "docker"]
125
- for svc in services:
126
- running, pids = is_running(svc)
127
- icon = "✅" if running else "❌"
128
- info = f" PIDs: {', '.join(p.split()[0] for p in pids[:3])}" if running else ""
129
- print(f"{icon} {svc:<20}{info}")
130
- ```
131
-
132
- ## Check Port Availability
8
+ ## Quick checks
133
9
 
134
10
  ```bash
135
- # Check if a port is in use
136
- PORT=3000
137
- lsof -i :$PORT 2>/dev/null | grep LISTEN || echo "Port $PORT is free"
138
-
139
- # List all listening ports
140
- lsof -nP -iTCP -sTCP:LISTEN 2>/dev/null | awk 'NR>1 {print $1, $9}' | sort -u
141
-
142
- # Check if a service is actually responding
143
- curl -s -o /dev/null -w "%{http_code}" http://localhost:3000/health
144
- ```
145
-
146
- ## HTTP Endpoint Health Check
147
-
148
- ```python
149
- #!/usr/bin/env python3
150
- import urllib.request, urllib.error, time, json
151
-
152
- def check_endpoint(url: str, timeout: int = 5) -> dict:
153
- """Check if an HTTP endpoint is healthy."""
154
- start = time.time()
155
- try:
156
- req = urllib.request.Request(url)
157
- with urllib.request.urlopen(req, timeout=timeout) as resp:
158
- latency = (time.time() - start) * 1000
159
- return {
160
- "url": url,
161
- "status": resp.status,
162
- "latency_ms": round(latency, 1),
163
- "healthy": 200 <= resp.status < 300
164
- }
165
- except urllib.error.HTTPError as e:
166
- return {"url": url, "status": e.code, "healthy": False, "error": str(e)}
167
- except Exception as e:
168
- return {"url": url, "status": 0, "healthy": False, "error": str(e)}
169
-
170
- # Check multiple endpoints
171
- endpoints = [
172
- "http://localhost:3000/health",
173
- "http://localhost:8081/health",
174
- "https://api.example.com/ping",
175
- ]
176
-
177
- print("🔍 Endpoint Health Check")
178
- print(f"{'─'*60}")
179
- for ep in endpoints:
180
- result = check_endpoint(ep)
181
- icon = "✅" if result["healthy"] else "❌"
182
- latency = f"{result['latency_ms']}ms" if result["healthy"] else result.get("error", "unreachable")
183
- print(f"{icon} {result['status']} {latency:<15} {ep}")
11
+ df -h # disk usage
12
+ vm_stat && uptime # memory + CPU (macOS)
13
+ free -h && uptime # memory + CPU (Linux)
14
+ ps aux | sort -k3 -rn | head -10 # top CPU processes
15
+ ps aux | sort -k4 -rn | head -10 # top memory processes
16
+ lsof -i :3000 | grep LISTEN # check if port in use
17
+ lsof -nP -iTCP -sTCP:LISTEN | awk 'NR>1 {print $1, $9}' | sort -u # all listening ports
18
+ pgrep -fl nginx # check if process is running
19
+ curl -s -o /dev/null -w "%{http_code}" http://localhost:3000/health # HTTP health check
184
20
  ```
185
21
 
186
- ## Check Recent Errors in Logs
187
-
188
- ```bash
189
- # System log (macOS)
190
- log show --last 1h --predicate 'messageType == 16 or messageType == 17' \
191
- --style syslog 2>/dev/null | tail -20
192
-
193
- # Application log tail
194
- tail -100 ~/Library/Logs/your-app/app.log 2>/dev/null | grep -iE "error|warn|fatal" | tail -20
195
-
196
- # Daemora agent logs
197
- tail -50 data/audit/*.jsonl 2>/dev/null | python3 -c "
198
- import sys, json
199
- for line in sys.stdin:
200
- try:
201
- e = json.loads(line)
202
- if e.get('type') in ['error', 'warn']:
203
- print(f\"{e['timestamp']} [{e['type']}] {e.get('message', '')}\")
204
- except: pass
205
- "
206
- ```
22
+ ## Workflow
207
23
 
208
- ## Response Format
24
+ 1. Run the relevant checks based on what was asked
25
+ 2. Present results as a clean status dashboard
26
+ 3. Flag anything wrong - high disk %, processes not running, unreachable endpoints
27
+ 4. Suggest fixes for common issues
209
28
 
210
- Present health check results as a clean status dashboard:
29
+ ## Output format
211
30
 
212
31
  ```
213
- 🖥 System Health MacBook Pro
32
+ 🖥 System Health - [hostname]
214
33
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
215
34
  ✅ Uptime: up 3 days, 4:22
216
35
  ✅ CPU: load 0.8 / 1.2 / 1.5 (8 cores)
217
- ✅ Memory: 6.2GB used / 16GB total (9.8GB free)
218
- ⚠️ Disk /: ████████████████░░░░ 82% (38GB free) getting full
219
- ✅ Disk /Volumes/Data: ████░░░░░░░░░░░░░░░░ 22% (780GB free)
36
+ ✅ Memory: 6.2GB used / 16GB total
37
+ ⚠️ Disk /: 82% (38GB free) - getting full
220
38
 
221
39
  Services:
222
40
  ✅ node (PID 4821)
@@ -1,147 +1,48 @@
1
1
  ---
2
2
  name: image-gen
3
- description: Generate images using OpenAI's image models (gpt-image-1, DALL-E 3, DALL-E 2). Use when the user asks to create, generate, draw, or illustrate an image, photo, logo, icon, illustration, or visual. Requires OPENAI_API_KEY.
3
+ description: Generate images using OpenAI image models (gpt-image-1, DALL-E 3, DALL-E 2). Use when the user asks to create, generate, draw, or illustrate an image, photo, logo, icon, or visual. Requires OPENAI_API_KEY.
4
4
  triggers: generate image, create image, draw, illustrate, make a picture, logo, icon, artwork, visual, dall-e, image generation, render
5
+ metadata: {"daemora": {"emoji": "🎨"}}
5
6
  ---
6
7
 
7
- ## When to Use
8
-
9
- ✅ Creating images from text descriptions, generating logos/icons, batch image sets, product mockups, illustration requests, visual content
10
-
11
- ❌ Editing existing images (use gpt-image-1 with input image), video generation, real-time/live images
12
-
13
8
  ## Models
14
9
 
15
- | Model | Best For | Max Count | Quality Options |
16
- |-------|---------|-----------|----------------|
17
- | `gpt-image-1` | Highest quality, follows prompts best | 10/call | `high`, `medium`, `low` |
18
- | `dall-e-3` | Artistic, stylized | 1/call | `hd`, `standard` |
19
- | `dall-e-2` | Fast, cheap drafts | 10/call | `standard` |
10
+ | Model | Best for | Max/call | Quality |
11
+ |-------|---------|----------|---------|
12
+ | `gpt-image-1` | Highest quality, instruction-following | 10 | `high` / `medium` / `low` |
13
+ | `dall-e-3` | Artistic, stylized | 1 | `hd` / `standard` |
14
+ | `dall-e-2` | Fast, cheap drafts | 10 | `standard` |
20
15
 
21
16
  Default: `gpt-image-1` at `high` quality.
22
17
 
23
- ## Single Image (Python)
24
-
25
- ```python
26
- #!/usr/bin/env python3
27
- import os, base64, json, urllib.request, urllib.parse
28
- from datetime import datetime
29
- from pathlib import Path
30
-
31
- api_key = os.environ["OPENAI_API_KEY"]
32
- prompt = "A sleek futuristic dashboard UI in dark mode, neon blue accents, clean typography"
33
- model = "gpt-image-1" # or "dall-e-3", "dall-e-2"
34
- size = "1024x1024" # gpt-image-1: 1024x1024, 1536x1024 (landscape), 1024x1536 (portrait)
35
- quality = "high" # gpt-image-1: high/medium/low | dall-e-3: hd/standard
36
-
37
- payload = json.dumps({
38
- "model": model, "prompt": prompt,
39
- "n": 1, "size": size, "quality": quality,
40
- "response_format": "b64_json"
41
- }).encode()
42
-
43
- req = urllib.request.Request(
44
- "https://api.openai.com/v1/images/generations",
45
- data=payload,
46
- headers={"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"}
47
- )
48
- resp = json.loads(urllib.request.urlopen(req).read())
49
-
50
- out_dir = Path("/tmp/daemora-images")
51
- out_dir.mkdir(exist_ok=True)
52
- ts = datetime.now().strftime("%Y%m%d_%H%M%S")
53
- out_file = out_dir / f"image_{ts}.png"
54
- out_file.write_bytes(base64.b64decode(resp["data"][0]["b64_json"]))
55
- print(f"Saved: {out_file}")
56
- ```
57
-
58
- ## Batch Generation
59
-
60
- ```python
61
- #!/usr/bin/env python3
62
- """Generate multiple images with varied prompts and save to a gallery."""
63
- import os, base64, json, urllib.request, urllib.parse
64
- from pathlib import Path
65
- from datetime import datetime
66
-
67
- API_KEY = os.environ["OPENAI_API_KEY"]
68
- OUT_DIR = Path("/tmp/daemora-images") / datetime.now().strftime("%Y%m%d_%H%M%S")
69
- OUT_DIR.mkdir(parents=True, exist_ok=True)
70
-
71
- PROMPTS = [
72
- "prompt 1 here",
73
- "prompt 2 here",
74
- ]
75
-
76
- def generate(prompt, model="gpt-image-1", size="1024x1024", quality="high"):
77
- payload = json.dumps({
78
- "model": model, "prompt": prompt,
79
- "n": 1, "size": size, "quality": quality,
80
- "response_format": "b64_json"
81
- }).encode()
82
- req = urllib.request.Request(
83
- "https://api.openai.com/v1/images/generations",
84
- data=payload,
85
- headers={"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json"}
86
- )
87
- return json.loads(urllib.request.urlopen(req).read())["data"][0]["b64_json"]
88
-
89
- saved = []
90
- for i, prompt in enumerate(PROMPTS):
91
- print(f"Generating {i+1}/{len(PROMPTS)}: {prompt[:50]}...")
92
- try:
93
- img_b64 = generate(prompt)
94
- path = OUT_DIR / f"image_{i+1:02d}.png"
95
- path.write_bytes(base64.b64decode(img_b64))
96
- saved.append((path, prompt))
97
- print(f" ✅ Saved: {path}")
98
- except Exception as e:
99
- print(f" ❌ Failed: {e}")
100
-
101
- # Write HTML gallery
102
- html = f"""<!DOCTYPE html><html><head><title>Generated Images</title>
103
- <style>body{{background:#111;color:#eee;font-family:sans-serif;padding:20px}}
104
- .grid{{display:grid;grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:16px}}
105
- img{{width:100%;border-radius:8px}} p{{font-size:12px;opacity:.7;margin:4px 0 12px}}</style></head>
106
- <body><h2>Daemora Image Generation — {len(saved)} images</h2><div class="grid">
107
- """
108
- for path, prompt in saved:
109
- html += f'<div><img src="{path.name}" alt=""><p>{prompt}</p></div>\n'
110
- html += "</div></body></html>"
111
- (OUT_DIR / "index.html").write_text(html)
112
- print(f"\nGallery: {OUT_DIR / 'index.html'}")
113
- print(f"Open: open '{OUT_DIR / 'index.html'}'")
114
- ```
115
-
116
- ## Size Options by Model
18
+ ## Sizes
117
19
 
118
20
  ```
119
- gpt-image-1: 1024x1024 (square) | 1536x1024 (landscape) | 1024x1536 (portrait) | auto
21
+ gpt-image-1: 1024x1024 | 1536x1024 (landscape) | 1024x1536 (portrait)
120
22
  dall-e-3: 1024x1024 | 1792x1024 (wide) | 1024x1792 (tall)
121
23
  dall-e-2: 256x256 | 512x512 | 1024x1024
122
24
  ```
123
25
 
124
- ## Prompt Writing Tips
26
+ ## Prompt tips
125
27
 
126
- - **Be specific**: "A minimalist logo for a fintech startup, dark blue on white, geometric sans-serif font" beats "a logo"
127
- - **Specify style**: photorealistic / watercolor / flat illustration / 3D render / oil painting
128
- - **Specify lighting**: soft studio lighting / dramatic shadows / golden hour / neon
129
- - **Specify composition**: bird's eye view / close-up / wide angle / portrait
130
- - **Negative guidance**: add "no text, no watermarks" if needed
28
+ - Be specific: "minimalist fintech logo, dark blue, geometric sans-serif" beats "a logo"
29
+ - Specify style: photorealistic / watercolor / flat illustration / 3D render
30
+ - Specify lighting: soft studio / dramatic shadows / golden hour / neon
31
+ - Add "no text, no watermarks" if needed
131
32
 
132
- ## After Generation
33
+ ## Workflow
133
34
 
134
- Always:
135
- 1. Save images to `/tmp/daemora-images/` or a user-specified path
136
- 2. Report the file path(s) in the response
137
- 3. For multiple images, open the HTML gallery: `executeCommand("open /tmp/daemora-images/*/index.html")`
138
- 4. If the user wants to send the image, use `sendFile(path, channel, sessionId)`
35
+ 1. Use the `generateImage` tool with the user's prompt
36
+ 2. Save to `/tmp/daemora-images/image_TIMESTAMP.png`
37
+ 3. Report the path
38
+ 4. On macOS: `executeCommand("open /tmp/daemora-images/")` if multiple images
39
+ 5. To send: `sendFile(path, channel, sessionId)`
139
40
 
140
- ## Error Handling
41
+ ## Errors
141
42
 
142
43
  | Error | Fix |
143
44
  |-------|-----|
144
- | 400 content policy | Rephrase prompt, remove potentially flagged terms |
145
- | 401 unauthorized | Check `OPENAI_API_KEY` is set and valid |
45
+ | 400 content policy | Rephrase prompt, remove flagged terms |
46
+ | 401 unauthorized | Check `OPENAI_API_KEY` is valid |
146
47
  | 429 rate limit | Wait 10s, retry with `n=1` |
147
- | 503 unavailable | Retry after 30s; fall back to dall-e-3 |
48
+ | 503 unavailable | Retry after 30s; fall back to `dall-e-3` |