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.
- package/README.md +106 -76
- package/SOUL.md +100 -28
- package/config/mcp.json +9 -9
- package/package.json +15 -8
- package/skills/apple-notes.md +0 -52
- package/skills/apple-reminders.md +1 -87
- package/skills/camsnap.md +20 -144
- package/skills/coding.md +7 -7
- package/skills/documents.md +6 -6
- package/skills/email.md +6 -6
- package/skills/gif-search.md +28 -171
- package/skills/healthcheck.md +21 -203
- package/skills/image-gen.md +24 -123
- package/skills/model-usage.md +18 -165
- package/skills/obsidian.md +28 -174
- package/skills/pdf.md +30 -181
- package/skills/research.md +6 -6
- package/skills/skill-creator.md +35 -111
- package/skills/spotify.md +2 -17
- package/skills/summarize.md +36 -193
- package/skills/things.md +23 -175
- package/skills/tmux.md +1 -91
- package/skills/trello.md +32 -157
- package/skills/video-frames.md +26 -166
- package/skills/weather.md +6 -6
- package/src/a2a/A2AClient.js +2 -2
- package/src/a2a/A2AServer.js +6 -6
- package/src/a2a/AgentCard.js +2 -2
- package/src/agents/SubAgentManager.js +61 -19
- package/src/agents/Supervisor.js +4 -4
- package/src/channels/BaseChannel.js +6 -6
- package/src/channels/BlueBubblesChannel.js +112 -0
- package/src/channels/DiscordChannel.js +8 -8
- package/src/channels/EmailChannel.js +54 -26
- package/src/channels/FeishuChannel.js +140 -0
- package/src/channels/GoogleChatChannel.js +8 -8
- package/src/channels/HttpChannel.js +2 -2
- package/src/channels/IRCChannel.js +144 -0
- package/src/channels/LineChannel.js +13 -13
- package/src/channels/MatrixChannel.js +97 -0
- package/src/channels/MattermostChannel.js +119 -0
- package/src/channels/NextcloudChannel.js +133 -0
- package/src/channels/NostrChannel.js +175 -0
- package/src/channels/SignalChannel.js +9 -9
- package/src/channels/SlackChannel.js +10 -10
- package/src/channels/TeamsChannel.js +10 -10
- package/src/channels/TelegramChannel.js +8 -8
- package/src/channels/TwitchChannel.js +128 -0
- package/src/channels/WhatsAppChannel.js +10 -10
- package/src/channels/ZaloChannel.js +119 -0
- package/src/channels/iMessageChannel.js +150 -0
- package/src/channels/index.js +241 -11
- package/src/cli.js +835 -38
- package/src/config/agentProfiles.js +19 -19
- package/src/config/channels.js +1 -1
- package/src/config/default.js +12 -7
- package/src/config/models.js +3 -3
- package/src/config/permissions.js +2 -2
- package/src/core/AgentLoop.js +13 -13
- package/src/core/Compaction.js +3 -3
- package/src/core/CostTracker.js +2 -2
- package/src/core/EventBus.js +15 -15
- package/src/core/TaskQueue.js +24 -7
- package/src/core/TaskRunner.js +19 -6
- package/src/daemon/DaemonManager.js +4 -4
- package/src/hooks/HookRunner.js +4 -4
- package/src/index.js +6 -2
- package/src/mcp/MCPAgentRunner.js +3 -3
- package/src/mcp/MCPClient.js +9 -9
- package/src/mcp/MCPManager.js +14 -14
- package/src/models/ModelRouter.js +2 -2
- package/src/safety/AuditLog.js +3 -3
- package/src/safety/CircuitBreaker.js +2 -2
- package/src/safety/CommandGuard.js +132 -0
- package/src/safety/FilesystemGuard.js +23 -3
- package/src/safety/GitRollback.js +5 -5
- package/src/safety/HumanApproval.js +9 -9
- package/src/safety/InputSanitizer.js +81 -8
- package/src/safety/PermissionGuard.js +2 -2
- package/src/safety/Sandbox.js +1 -1
- package/src/safety/SecretScanner.js +90 -28
- package/src/safety/SecretVault.js +2 -2
- package/src/scheduler/Heartbeat.js +3 -3
- package/src/scheduler/Scheduler.js +6 -6
- package/src/setup/theme.js +171 -66
- package/src/setup/wizard.js +432 -57
- package/src/skills/SkillLoader.js +145 -8
- package/src/storage/TaskStore.js +39 -15
- package/src/systemPrompt.js +45 -43
- package/src/tenants/TenantManager.js +79 -22
- package/src/tools/ToolRegistry.js +3 -3
- package/src/tools/applyPatch.js +2 -2
- package/src/tools/browserAutomation.js +4 -4
- package/src/tools/calendar.js +155 -0
- package/src/tools/clipboard.js +71 -0
- package/src/tools/contacts.js +138 -0
- package/src/tools/createDocument.js +2 -2
- package/src/tools/cronTool.js +14 -14
- package/src/tools/database.js +165 -0
- package/src/tools/editFile.js +10 -10
- package/src/tools/executeCommand.js +11 -3
- package/src/tools/generateImage.js +79 -0
- package/src/tools/gitTool.js +141 -0
- package/src/tools/glob.js +1 -1
- package/src/tools/googlePlaces.js +136 -0
- package/src/tools/grep.js +2 -2
- package/src/tools/iMessageTool.js +86 -0
- package/src/tools/imageAnalysis.js +3 -3
- package/src/tools/index.js +56 -2
- package/src/tools/makeVoiceCall.js +283 -0
- package/src/tools/manageAgents.js +2 -2
- package/src/tools/manageMCP.js +38 -20
- package/src/tools/memory.js +25 -32
- package/src/tools/messageChannel.js +1 -1
- package/src/tools/notification.js +90 -0
- package/src/tools/philipsHue.js +147 -0
- package/src/tools/projectTracker.js +8 -8
- package/src/tools/readFile.js +1 -1
- package/src/tools/readPDF.js +73 -0
- package/src/tools/screenCapture.js +6 -6
- package/src/tools/searchContent.js +2 -2
- package/src/tools/searchFiles.js +1 -1
- package/src/tools/sendEmail.js +79 -24
- package/src/tools/sendFile.js +4 -4
- package/src/tools/sonos.js +137 -0
- package/src/tools/sshTool.js +130 -0
- package/src/tools/textToSpeech.js +5 -5
- package/src/tools/transcribeAudio.js +4 -4
- package/src/tools/useMCP.js +4 -4
- package/src/tools/webFetch.js +2 -2
- package/src/tools/webSearch.js +1 -1
- package/src/utils/Embeddings.js +79 -0
- package/src/voice/VoiceSessionManager.js +170 -0
- package/src/voice/VoiceWebhook.js +188 -0
package/skills/gif-search.md
CHANGED
|
@@ -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
|
|
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
|
-
##
|
|
8
|
+
## Search Tenor (no API key needed)
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
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
|
-
##
|
|
15
|
+
## Search Giphy (free key at developers.giphy.com)
|
|
103
16
|
|
|
104
17
|
```bash
|
|
105
|
-
|
|
106
|
-
|
|
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
|
-
##
|
|
22
|
+
## Download GIF
|
|
121
23
|
|
|
122
24
|
```bash
|
|
123
|
-
|
|
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
|
-
##
|
|
28
|
+
## Create GIF from video clip
|
|
140
29
|
|
|
141
30
|
```bash
|
|
142
|
-
#
|
|
143
|
-
|
|
144
|
-
|
|
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
|
-
##
|
|
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
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
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
|
-
##
|
|
46
|
+
## Workflow
|
|
189
47
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
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
|
|
53
|
+
If the channel doesn't support GIFs, share the URL instead.
|
package/skills/healthcheck.md
CHANGED
|
@@ -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.
|
|
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
|
-
##
|
|
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
|
-
#
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
#
|
|
140
|
-
lsof -
|
|
141
|
-
|
|
142
|
-
#
|
|
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
|
-
##
|
|
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
|
-
|
|
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
|
-
|
|
29
|
+
## Output format
|
|
211
30
|
|
|
212
31
|
```
|
|
213
|
-
🖥 System Health
|
|
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
|
|
218
|
-
⚠️ Disk /:
|
|
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)
|
package/skills/image-gen.md
CHANGED
|
@@ -1,147 +1,48 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: image-gen
|
|
3
|
-
description: Generate images using OpenAI
|
|
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
|
|
16
|
-
|
|
17
|
-
| `gpt-image-1` | Highest quality,
|
|
18
|
-
| `dall-e-3` | Artistic, stylized | 1
|
|
19
|
-
| `dall-e-2` | Fast, cheap drafts | 10
|
|
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
|
-
##
|
|
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
|
|
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
|
|
26
|
+
## Prompt tips
|
|
125
27
|
|
|
126
|
-
-
|
|
127
|
-
-
|
|
128
|
-
-
|
|
129
|
-
-
|
|
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
|
-
##
|
|
33
|
+
## Workflow
|
|
133
34
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
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
|
-
##
|
|
41
|
+
## Errors
|
|
141
42
|
|
|
142
43
|
| Error | Fix |
|
|
143
44
|
|-------|-----|
|
|
144
|
-
| 400 content policy | Rephrase prompt, remove
|
|
145
|
-
| 401 unauthorized | Check `OPENAI_API_KEY` is
|
|
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` |
|