alvin-bot 4.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (136) hide show
  1. package/.env.example +43 -0
  2. package/BACKLOG.md +223 -0
  3. package/CHANGELOG.md +63 -0
  4. package/CLAUDE.example.md +152 -0
  5. package/CODE_OF_CONDUCT.md +52 -0
  6. package/CONTRIBUTING.md +72 -0
  7. package/LICENSE +21 -0
  8. package/README.md +529 -0
  9. package/SECURITY.md +38 -0
  10. package/SOUL.example.md +60 -0
  11. package/TOOLS.example.md +42 -0
  12. package/alvin-bot.config.example.json +24 -0
  13. package/bin/cli.js +1088 -0
  14. package/dist/.metadata_never_index +0 -0
  15. package/dist/claude.js +102 -0
  16. package/dist/config.js +65 -0
  17. package/dist/engine.js +90 -0
  18. package/dist/find-claude-binary.js +98 -0
  19. package/dist/handlers/commands.js +1489 -0
  20. package/dist/handlers/document.js +187 -0
  21. package/dist/handlers/message.js +200 -0
  22. package/dist/handlers/photo.js +154 -0
  23. package/dist/handlers/platform-message.js +275 -0
  24. package/dist/handlers/video.js +237 -0
  25. package/dist/handlers/voice.js +148 -0
  26. package/dist/i18n.js +299 -0
  27. package/dist/index.js +442 -0
  28. package/dist/init-data-dir.js +81 -0
  29. package/dist/middleware/auth.js +215 -0
  30. package/dist/migrate.js +139 -0
  31. package/dist/paths.js +87 -0
  32. package/dist/platforms/discord.js +161 -0
  33. package/dist/platforms/index.js +130 -0
  34. package/dist/platforms/signal.js +205 -0
  35. package/dist/platforms/slack.js +318 -0
  36. package/dist/platforms/telegram.js +111 -0
  37. package/dist/platforms/types.js +8 -0
  38. package/dist/platforms/whatsapp.js +648 -0
  39. package/dist/providers/claude-sdk-provider.js +173 -0
  40. package/dist/providers/codex-cli-provider.js +121 -0
  41. package/dist/providers/index.js +7 -0
  42. package/dist/providers/openai-compatible.js +388 -0
  43. package/dist/providers/registry.js +209 -0
  44. package/dist/providers/tool-executor.js +450 -0
  45. package/dist/providers/types.js +205 -0
  46. package/dist/services/access.js +144 -0
  47. package/dist/services/asset-index.js +230 -0
  48. package/dist/services/browser-manager.js +161 -0
  49. package/dist/services/browser.js +121 -0
  50. package/dist/services/compaction.js +129 -0
  51. package/dist/services/cron.js +462 -0
  52. package/dist/services/custom-tools.js +317 -0
  53. package/dist/services/delivery-queue.js +154 -0
  54. package/dist/services/elevenlabs.js +58 -0
  55. package/dist/services/embeddings.js +386 -0
  56. package/dist/services/exec-guard.js +46 -0
  57. package/dist/services/fallback-order.js +151 -0
  58. package/dist/services/heartbeat.js +192 -0
  59. package/dist/services/hooks.js +44 -0
  60. package/dist/services/imagegen.js +72 -0
  61. package/dist/services/language-detect.js +144 -0
  62. package/dist/services/markdown.js +63 -0
  63. package/dist/services/mcp.js +252 -0
  64. package/dist/services/memory.js +133 -0
  65. package/dist/services/personality.js +227 -0
  66. package/dist/services/plugins.js +171 -0
  67. package/dist/services/reminders.js +97 -0
  68. package/dist/services/restart.js +48 -0
  69. package/dist/services/security-audit.js +66 -0
  70. package/dist/services/self-search.js +129 -0
  71. package/dist/services/session.js +93 -0
  72. package/dist/services/skills.js +287 -0
  73. package/dist/services/standing-orders.js +29 -0
  74. package/dist/services/subagents.js +142 -0
  75. package/dist/services/sudo.js +243 -0
  76. package/dist/services/telegram.js +113 -0
  77. package/dist/services/tool-discovery.js +214 -0
  78. package/dist/services/usage-tracker.js +137 -0
  79. package/dist/services/users.js +199 -0
  80. package/dist/services/voice.js +95 -0
  81. package/dist/tui/index.js +507 -0
  82. package/dist/web/canvas.js +30 -0
  83. package/dist/web/doctor-api.js +606 -0
  84. package/dist/web/openai-compat.js +252 -0
  85. package/dist/web/server.js +1351 -0
  86. package/dist/web/setup-api.js +1078 -0
  87. package/docs/mcp.example.json +16 -0
  88. package/docs/screenshots/00-Login.png +0 -0
  89. package/docs/screenshots/01-Chat-Dark-Conversation.png +0 -0
  90. package/docs/screenshots/02-Chat.png +0 -0
  91. package/docs/screenshots/03-Dashboard-Overview.png +0 -0
  92. package/docs/screenshots/04-AI-Models-and-Providers.png +0 -0
  93. package/docs/screenshots/05-Personality-Editor.png +0 -0
  94. package/docs/screenshots/06-Memory-Manager.png +0 -0
  95. package/docs/screenshots/07-Active-Sessions.png +0 -0
  96. package/docs/screenshots/08-File-Browser.png +0 -0
  97. package/docs/screenshots/09-Scheduled-Jobs.png +0 -0
  98. package/docs/screenshots/10-Custom-Tools.png +0 -0
  99. package/docs/screenshots/11-Plugins-and-MCP.png +0 -0
  100. package/docs/screenshots/12-Messaging-Platforms.png +0 -0
  101. package/docs/screenshots/12.1-Messaging-Platforms-WhatsApp-Groups-List.png +0 -0
  102. package/docs/screenshots/12.2-Messaging-Platforms-WA-Group-Details.png +0 -0
  103. package/docs/screenshots/13-User-Management.png +0 -0
  104. package/docs/screenshots/14-Web-Terminal.png +0 -0
  105. package/docs/screenshots/15-Maintenance-and-Health.png +0 -0
  106. package/docs/screenshots/16-Settings-and-Env.png +0 -0
  107. package/docs/screenshots/TG-commands.png +0 -0
  108. package/docs/screenshots/TG.png +0 -0
  109. package/docs/screenshots/_Mac-Installer.png +0 -0
  110. package/docs/tools.example.json +33 -0
  111. package/install.sh +165 -0
  112. package/package.json +190 -0
  113. package/plugins/calendar/index.js +270 -0
  114. package/plugins/email/index.js +231 -0
  115. package/plugins/finance/index.js +254 -0
  116. package/plugins/notes/index.js +227 -0
  117. package/plugins/smarthome/index.js +230 -0
  118. package/plugins/weather/index.js +122 -0
  119. package/skills/apple-notes/SKILL.md +31 -0
  120. package/skills/browse/SKILL.md +136 -0
  121. package/skills/code-project/SKILL.md +43 -0
  122. package/skills/data-analysis/SKILL.md +39 -0
  123. package/skills/document-creation/SKILL.md +48 -0
  124. package/skills/email-summary/SKILL.md +46 -0
  125. package/skills/github/SKILL.md +42 -0
  126. package/skills/summarize/SKILL.md +28 -0
  127. package/skills/system-admin/SKILL.md +39 -0
  128. package/skills/weather/SKILL.md +34 -0
  129. package/skills/web-research/SKILL.md +35 -0
  130. package/web/public/canvas.html +52 -0
  131. package/web/public/css/style.css +555 -0
  132. package/web/public/index.html +189 -0
  133. package/web/public/js/app.js +3102 -0
  134. package/web/public/js/i18n.js +1048 -0
  135. package/web/public/js/icons.js +104 -0
  136. package/web/public/login.html +48 -0
@@ -0,0 +1,230 @@
1
+ /**
2
+ * Smart Home Plugin — Control smart home devices.
3
+ *
4
+ * Supports:
5
+ * - Philips Hue (via local bridge API)
6
+ * - Generic HTTP devices (webhooks, IFTTT, Home Assistant)
7
+ *
8
+ * Configuration via docs/smarthome.json:
9
+ * {
10
+ * "hue": { "bridge": "192.168.1.x", "username": "api-key" },
11
+ * "devices": [
12
+ * { "name": "Desk Lamp", "type": "hue", "id": "1" },
13
+ * { "name": "Fan", "type": "webhook", "on": "http://...", "off": "http://..." }
14
+ * ]
15
+ * }
16
+ */
17
+
18
+ import fs from "fs";
19
+ import path from "path";
20
+ import { fileURLToPath } from "url";
21
+
22
+ const PLUGIN_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
23
+ const CONFIG_FILE = path.resolve(PLUGIN_ROOT, "docs", "smarthome.json");
24
+
25
+ function loadConfig() {
26
+ try {
27
+ return JSON.parse(fs.readFileSync(CONFIG_FILE, "utf-8"));
28
+ } catch {
29
+ return { hue: null, devices: [] };
30
+ }
31
+ }
32
+
33
+ async function hueRequest(method, endpoint, body = null) {
34
+ const config = loadConfig();
35
+ if (!config.hue?.bridge || !config.hue?.username) {
36
+ throw new Error("Hue Bridge not configured. Create docs/smarthome.json");
37
+ }
38
+ const url = `http://${config.hue.bridge}/api/${config.hue.username}${endpoint}`;
39
+ const options = { method, headers: { "Content-Type": "application/json" } };
40
+ if (body) options.body = JSON.stringify(body);
41
+ const res = await fetch(url, options);
42
+ return res.json();
43
+ }
44
+
45
+ export default {
46
+ name: "smarthome",
47
+ description: "Smart home control (Hue, Webhooks, Home Assistant)",
48
+ version: "1.0.0",
49
+ author: "Alvin Bot",
50
+
51
+ commands: [
52
+ {
53
+ command: "home",
54
+ description: "Control smart home",
55
+ handler: async (ctx, args) => {
56
+ const config = loadConfig();
57
+
58
+ if (!args) {
59
+ if (config.devices.length === 0 && !config.hue) {
60
+ await ctx.reply(
61
+ "🏠 *Smart Home*\n\n" +
62
+ "Not configured.\nCreate `docs/smarthome.json` with:\n" +
63
+ "```json\n" +
64
+ '{\n "hue": { "bridge": "IP", "username": "KEY" },\n "devices": []\n}\n' +
65
+ "```",
66
+ { parse_mode: "Markdown" }
67
+ );
68
+ return;
69
+ }
70
+
71
+ // List devices
72
+ const lines = config.devices.map((d, i) => `${i + 1}. ${d.type === "hue" ? "💡" : "🔌"} *${d.name}* (${d.type})`);
73
+
74
+ // If Hue configured, show lights
75
+ if (config.hue) {
76
+ try {
77
+ const lights = await hueRequest("GET", "/lights");
78
+ for (const [id, light] of Object.entries(lights)) {
79
+ const state = light.state?.on ? "🟢" : "⚫";
80
+ const brightness = light.state?.bri ? ` (${Math.round(light.state.bri / 254 * 100)}%)` : "";
81
+ lines.push(`💡 ${state} *${light.name}*${brightness} [Hue #${id}]`);
82
+ }
83
+ } catch (err) {
84
+ lines.push(`⚠️ Hue Bridge unreachable: ${err.message}`);
85
+ }
86
+ }
87
+
88
+ await ctx.reply(
89
+ `🏠 *Smart Home:*\n\n${lines.join("\n")}\n\n` +
90
+ "_Commands: `/home on <name>`, `/home off <name>`, `/home brightness <name> 50`_",
91
+ { parse_mode: "Markdown" }
92
+ );
93
+ return;
94
+ }
95
+
96
+ // /home on <device>
97
+ if (args.startsWith("on ") || args.startsWith("off ")) {
98
+ const on = args.startsWith("on ");
99
+ const deviceName = args.slice(on ? 3 : 4).trim().toLowerCase();
100
+
101
+ // Check configured devices first
102
+ const device = config.devices.find(d => d.name.toLowerCase().includes(deviceName));
103
+ if (device) {
104
+ if (device.type === "webhook") {
105
+ const url = on ? device.on : device.off;
106
+ if (!url) { await ctx.reply("❌ No webhook for this state."); return; }
107
+ await fetch(url, { method: "POST" });
108
+ await ctx.reply(`${on ? "🟢" : "⚫"} *${device.name}* turned ${on ? "on" : "off"}.`, { parse_mode: "Markdown" });
109
+ return;
110
+ }
111
+ if (device.type === "hue" && device.id) {
112
+ await hueRequest("PUT", `/lights/${device.id}/state`, { on });
113
+ await ctx.reply(`💡 *${device.name}* ${on ? "on" : "off"}.`, { parse_mode: "Markdown" });
114
+ return;
115
+ }
116
+ }
117
+
118
+ // Try Hue lights by name
119
+ if (config.hue) {
120
+ try {
121
+ const lights = await hueRequest("GET", "/lights");
122
+ for (const [id, light] of Object.entries(lights)) {
123
+ if (light.name.toLowerCase().includes(deviceName)) {
124
+ await hueRequest("PUT", `/lights/${id}/state`, { on });
125
+ await ctx.reply(`💡 *${light.name}* ${on ? "on" : "off"}.`, { parse_mode: "Markdown" });
126
+ return;
127
+ }
128
+ }
129
+ } catch { /* bridge not reachable */ }
130
+ }
131
+
132
+ await ctx.reply(`❌ Device "${deviceName}" not found.`);
133
+ return;
134
+ }
135
+
136
+ // /home brightness <device> <0-100>
137
+ if (args.startsWith("brightness ") || args.startsWith("bri ")) {
138
+ const parts = args.split(" ").slice(1);
139
+ const level = parseInt(parts[parts.length - 1]);
140
+ const deviceName = parts.slice(0, -1).join(" ").toLowerCase();
141
+
142
+ if (isNaN(level) || level < 0 || level > 100) {
143
+ await ctx.reply("Brightness: 0-100. Example: `/home brightness Lamp 50`", { parse_mode: "Markdown" });
144
+ return;
145
+ }
146
+
147
+ const bri = Math.round(level / 100 * 254);
148
+
149
+ if (config.hue) {
150
+ try {
151
+ const lights = await hueRequest("GET", "/lights");
152
+ for (const [id, light] of Object.entries(lights)) {
153
+ if (light.name.toLowerCase().includes(deviceName)) {
154
+ await hueRequest("PUT", `/lights/${id}/state`, { on: true, bri });
155
+ await ctx.reply(`💡 *${light.name}* brightness: ${level}%`, { parse_mode: "Markdown" });
156
+ return;
157
+ }
158
+ }
159
+ } catch { /* bridge not reachable */ }
160
+ }
161
+
162
+ await ctx.reply(`❌ Device "${deviceName}" not found.`);
163
+ return;
164
+ }
165
+
166
+ // /home scene <scene-name>
167
+ if (args.startsWith("scene ")) {
168
+ const sceneName = args.slice(6).trim().toLowerCase();
169
+ if (!config.hue) { await ctx.reply("❌ Hue not configured."); return; }
170
+
171
+ try {
172
+ const scenes = await hueRequest("GET", "/scenes");
173
+ for (const [id, scene] of Object.entries(scenes)) {
174
+ if (scene.name.toLowerCase().includes(sceneName)) {
175
+ await hueRequest("PUT", "/groups/0/action", { scene: id });
176
+ await ctx.reply(`🎨 Scene activated: *${scene.name}*`, { parse_mode: "Markdown" });
177
+ return;
178
+ }
179
+ }
180
+ await ctx.reply(`❌ Scene "${sceneName}" not found.`);
181
+ } catch (err) {
182
+ await ctx.reply(`❌ Hue error: ${err.message}`);
183
+ }
184
+ return;
185
+ }
186
+
187
+ await ctx.reply(
188
+ "🏠 *Smart Home commands:*\n\n" +
189
+ "`/home` — List devices\n" +
190
+ "`/home on Lamp` — Turn on\n" +
191
+ "`/home off Lamp` — Turn off\n" +
192
+ "`/home brightness Lamp 50` — Brightness\n" +
193
+ "`/home scene Relax` — Activate Hue scene",
194
+ { parse_mode: "Markdown" }
195
+ );
196
+ },
197
+ },
198
+ ],
199
+
200
+ tools: [
201
+ {
202
+ name: "control_device",
203
+ description: "Turn a smart home device on or off",
204
+ parameters: {
205
+ type: "object",
206
+ properties: {
207
+ device: { type: "string", description: "Device name" },
208
+ action: { type: "string", enum: ["on", "off"], description: "Action" },
209
+ brightness: { type: "number", description: "Brightness 0-100 (optional)" },
210
+ },
211
+ required: ["device", "action"],
212
+ },
213
+ execute: async (params) => {
214
+ const config = loadConfig();
215
+ if (!config.hue) return "Smart Home not configured";
216
+
217
+ const lights = await hueRequest("GET", "/lights");
218
+ for (const [id, light] of Object.entries(lights)) {
219
+ if (light.name.toLowerCase().includes(params.device.toLowerCase())) {
220
+ const state = { on: params.action === "on" };
221
+ if (params.brightness !== undefined) state.bri = Math.round(params.brightness / 100 * 254);
222
+ await hueRequest("PUT", `/lights/${id}/state`, state);
223
+ return `${light.name}: ${params.action}${params.brightness ? ` (${params.brightness}%)` : ""}`;
224
+ }
225
+ }
226
+ return `Device "${params.device}" not found`;
227
+ },
228
+ },
229
+ ],
230
+ };
@@ -0,0 +1,122 @@
1
+ /**
2
+ * Weather Plugin — Get current weather and forecasts.
3
+ *
4
+ * Uses wttr.in (no API key needed).
5
+ * Example plugin for Alvin Bot's plugin system.
6
+ */
7
+
8
+ export default {
9
+ name: "weather",
10
+ description: "Weather queries via wttr.in (no API key needed)",
11
+ version: "1.0.0",
12
+ author: "Alvin Bot",
13
+
14
+ commands: [
15
+ {
16
+ command: "weather",
17
+ description: "Get weather (e.g. /weather Berlin)",
18
+ handler: async (ctx, args) => {
19
+ const location = args || "Berlin";
20
+
21
+ try {
22
+ await ctx.api.sendChatAction(ctx.chat.id, "typing");
23
+
24
+ const response = await fetch(
25
+ `https://wttr.in/${encodeURIComponent(location)}?format=j1`,
26
+ { headers: { "User-Agent": "AlvinBot/1.0" } }
27
+ );
28
+
29
+ if (!response.ok) {
30
+ await ctx.reply(`❌ Weather for "${location}" not found.`);
31
+ return;
32
+ }
33
+
34
+ const data = await response.json();
35
+ const current = data.current_condition?.[0];
36
+ const area = data.nearest_area?.[0];
37
+
38
+ if (!current) {
39
+ await ctx.reply(`❌ No weather data for "${location}".`);
40
+ return;
41
+ }
42
+
43
+ const areaName = area?.areaName?.[0]?.value || location;
44
+ const country = area?.country?.[0]?.value || "";
45
+ const temp = current.temp_C;
46
+ const feelsLike = current.FeelsLikeC;
47
+ const desc = current.lang_de?.[0]?.value || current.weatherDesc?.[0]?.value || "";
48
+ const humidity = current.humidity;
49
+ const wind = current.windspeedKmph;
50
+ const windDir = current.winddir16Point;
51
+
52
+ // Weather emoji based on description
53
+ let emoji = "🌤️";
54
+ const descLower = desc.toLowerCase();
55
+ if (descLower.includes("regen") || descLower.includes("rain")) emoji = "🌧️";
56
+ else if (descLower.includes("schnee") || descLower.includes("snow")) emoji = "🌨️";
57
+ else if (descLower.includes("gewitter") || descLower.includes("thunder")) emoji = "⛈️";
58
+ else if (descLower.includes("wolkig") || descLower.includes("cloud") || descLower.includes("bewölkt")) emoji = "☁️";
59
+ else if (descLower.includes("sonnig") || descLower.includes("sunny") || descLower.includes("klar") || descLower.includes("clear")) emoji = "☀️";
60
+ else if (descLower.includes("nebel") || descLower.includes("fog")) emoji = "🌫️";
61
+
62
+ // 3-day forecast
63
+ const forecast = data.weather?.slice(0, 3).map(day => {
64
+ const date = day.date;
65
+ const maxT = day.maxtempC;
66
+ const minT = day.mintempC;
67
+ const dayDesc = day.hourly?.[4]?.lang_de?.[0]?.value || day.hourly?.[4]?.weatherDesc?.[0]?.value || "";
68
+ return `📅 ${date}: ${minT}°–${maxT}°C, ${dayDesc}`;
69
+ }).join("\n") || "";
70
+
71
+ await ctx.reply(
72
+ `${emoji} *Weather in ${areaName}*${country ? ` (${country})` : ""}\n\n` +
73
+ `🌡️ ${temp}°C (feels like ${feelsLike}°C)\n` +
74
+ `${desc}\n` +
75
+ `💧 Humidity: ${humidity}%\n` +
76
+ `💨 Wind: ${wind} km/h ${windDir}\n` +
77
+ (forecast ? `\n*3-Day Forecast:*\n${forecast}` : ""),
78
+ { parse_mode: "Markdown" }
79
+ );
80
+ } catch (err) {
81
+ await ctx.reply(`❌ Error: ${err.message || err}`);
82
+ }
83
+ },
84
+ },
85
+ ],
86
+
87
+ tools: [
88
+ {
89
+ name: "get_weather",
90
+ description: "Get current weather for a location",
91
+ parameters: {
92
+ type: "object",
93
+ properties: {
94
+ location: { type: "string", description: "City name (e.g. Berlin, London)" },
95
+ },
96
+ required: ["location"],
97
+ },
98
+ execute: async (params) => {
99
+ const location = params.location || "Berlin";
100
+ const response = await fetch(
101
+ `https://wttr.in/${encodeURIComponent(location)}?format=j1`,
102
+ { headers: { "User-Agent": "AlvinBot/1.0" } }
103
+ );
104
+
105
+ if (!response.ok) return `Weather not found for "${location}"`;
106
+
107
+ const data = await response.json();
108
+ const current = data.current_condition?.[0];
109
+ if (!current) return `No weather data for "${location}"`;
110
+
111
+ return JSON.stringify({
112
+ location,
113
+ temperature: `${current.temp_C}°C`,
114
+ feelsLike: `${current.FeelsLikeC}°C`,
115
+ description: current.lang_de?.[0]?.value || current.weatherDesc?.[0]?.value,
116
+ humidity: `${current.humidity}%`,
117
+ wind: `${current.windspeedKmph} km/h ${current.winddir16Point}`,
118
+ });
119
+ },
120
+ },
121
+ ],
122
+ };
@@ -0,0 +1,31 @@
1
+ ---
2
+ name: Apple Notes
3
+ description: Read, create, and search Apple Notes via AppleScript
4
+ triggers: apple notes, notes app, notizen, note, notes, apple notizen
5
+ priority: 5
6
+ category: productivity
7
+ ---
8
+
9
+ # Apple Notes
10
+
11
+ Access Apple Notes.app via AppleScript (macOS only).
12
+
13
+ ## Read Notes
14
+ ```bash
15
+ osascript -e 'tell application "Notes" to get name of every note in default account'
16
+ ```
17
+
18
+ ## Search Notes
19
+ ```bash
20
+ osascript -e 'tell application "Notes" to get name of every note of default account whose name contains "search term"'
21
+ ```
22
+
23
+ ## Create Note
24
+ ```bash
25
+ osascript -e 'tell application "Notes" to make new note at folder "Notes" of default account with properties {name:"Title", body:"Content"}'
26
+ ```
27
+
28
+ ## Read Note Content
29
+ ```bash
30
+ osascript -e 'tell application "Notes" to get body of note "Note Title" of default account'
31
+ ```
@@ -0,0 +1,136 @@
1
+ ---
2
+ name: Browser Automation
3
+ description: Interactive browser control — navigate, click, fill forms, screenshot, test web apps
4
+ triggers: browse, browser, test webapp, test app, test website, screenshot page, interact with, click on, fill form, visual test, qa test, check page, open page, test my app, browse to, open url, puppeteer, playwright, browser automation, test die seite, teste die app, schau dir an, öffne die seite, teste mal, visual check, check the ui, check the page
5
+ priority: 8
6
+ category: automation
7
+ ---
8
+
9
+ # Browser Automation — Playwright Interactive
10
+
11
+ ## Browser Strategies
12
+
13
+ Alvin Bot auto-selects the best browser approach:
14
+
15
+ | Strategy | When | How |
16
+ |----------|------|-----|
17
+ | **CLI** (default) | Simple screenshots, text extraction, PDF | Headless Playwright, one-shot |
18
+ | **HTTP Gateway** | Interactive browsing, form-filling, QA testing | Persistent browser server on port 3800 |
19
+ | **CDP** | Attach to user's Chrome (with login state) | Chrome DevTools Protocol via CDP_URL |
20
+
21
+ The gateway starts automatically when needed and shuts down after 5 min idle.
22
+ For CDP: Launch Chrome with `--remote-debugging-port=9222` and set `CDP_URL=http://localhost:9222`.
23
+
24
+ ---
25
+
26
+ You have a persistent Playwright browser server that gives you **eyes** and **hands** to interact with web pages. You can navigate, see screenshots, read the accessibility tree, click buttons, fill forms, and test running web apps.
27
+
28
+ ## Quick Start
29
+
30
+ ```bash
31
+ # 1. Ensure server is running (auto-shuts down after 5 min idle)
32
+ curl -s http://127.0.0.1:3800/health 2>/dev/null | grep -q '"ok":true' || \
33
+ (BOT_DIR=$(node -e "console.log(require('path').resolve(require.resolve('alvin-bot/package.json'), '..'))" 2>/dev/null || echo ".") && cd "$BOT_DIR" && node scripts/browse-server.cjs &) && sleep 3
34
+
35
+ # 2. Navigate to a page
36
+ curl -s "http://127.0.0.1:3800/navigate?url=https://example.com" | jq
37
+
38
+ # 3. Take a screenshot (view it with Read tool)
39
+ SHOT=$(curl -s "http://127.0.0.1:3800/screenshot" | jq -r '.path')
40
+ # Then use Read tool on $SHOT to see the image
41
+
42
+ # 4. Get interactive elements
43
+ curl -s "http://127.0.0.1:3800/tree" | jq '.tree[]' -r
44
+
45
+ # 5. Click something
46
+ curl -s "http://127.0.0.1:3800/click?ref=e5" | jq
47
+ ```
48
+
49
+ ## All Routes
50
+
51
+ | Route | Params | What it does |
52
+ |-------|--------|-------------|
53
+ | `/navigate` | `url` | Open a URL, returns title + accessibility tree |
54
+ | `/screenshot` | `full=true` (optional) | Take screenshot, returns file path |
55
+ | `/tree` | `limit=N` (optional) | Get all interactive elements with @eN refs |
56
+ | `/click` | `ref=eN` | Click element by ref |
57
+ | `/fill` | `ref=eN`, `value=text` | Fill input field |
58
+ | `/type` | `ref=eN`, `text=chars` | Type character by character (for special inputs) |
59
+ | `/press` | `key=Enter`, `ref=eN` (opt) | Press keyboard key |
60
+ | `/select` | `ref=eN`, `value=opt` | Select dropdown option |
61
+ | `/hover` | `ref=eN` | Hover over element |
62
+ | `/scroll` | `direction=down/up/top/bottom`, `amount=600` | Scroll page |
63
+ | `/eval` | `js=expression` | Run JavaScript on page |
64
+ | `/wait` | `ms=2000` or `selector=.class` | Wait for time or element |
65
+ | `/viewport` | `device=mobile/tablet` or `width=W&height=H` | Change viewport |
66
+ | `/cookies` | `set=[{...}]` (optional) | Get or set cookies |
67
+ | `/back` | — | Browser back |
68
+ | `/forward` | — | Browser forward |
69
+ | `/reload` | — | Reload page |
70
+ | `/network` | `limit=20` | Recent network requests |
71
+ | `/info` | — | Current page info |
72
+ | `/close` | — | Close browser + shutdown server |
73
+ | `/health` | — | Server status check |
74
+
75
+ ## Element Refs (@eN)
76
+
77
+ The accessibility tree assigns **refs** like `@e1`, `@e2`, `@e3` to every interactive element (links, buttons, inputs, etc.). Use these refs for all interactions — they're more robust than CSS selectors.
78
+
79
+ Example tree:
80
+ ```
81
+ @e1 <a href="/"> "Home"
82
+ @e2 <a href="/dashboard"> "Dashboard"
83
+ @e3 <input type="email" name="email" placeholder="Enter email">
84
+ @e4 <input type="password" name="password" placeholder="Password">
85
+ @e5 <button> "Sign In"
86
+ @e6 <a href="/forgot"> "Forgot password?"
87
+ ```
88
+
89
+ To login:
90
+ ```bash
91
+ curl -s "http://127.0.0.1:3800/fill?ref=e3&value=user@example.com"
92
+ curl -s "http://127.0.0.1:3800/fill?ref=e4&value=mypassword"
93
+ curl -s "http://127.0.0.1:3800/click?ref=e5"
94
+ ```
95
+
96
+ ## Standard Workflow: Test a Web App
97
+
98
+ 1. **Start** the browse server if not running
99
+ 2. **Navigate** to the app URL
100
+ 3. **Screenshot** → view with Read tool to see current state
101
+ 4. **Tree** → see all interactive elements
102
+ 5. **Interact** (click, fill, press) using @eN refs
103
+ 6. **Screenshot** again to verify the result
104
+ 7. **Repeat** for each test step
105
+ 8. **Report** findings to the user
106
+ 9. **Close** when done
107
+
108
+ ## Mobile Testing
109
+
110
+ ```bash
111
+ # Switch to mobile viewport
112
+ curl -s "http://127.0.0.1:3800/viewport?device=mobile"
113
+ curl -s "http://127.0.0.1:3800/screenshot" | jq -r '.path'
114
+ # Switch back to desktop
115
+ curl -s "http://127.0.0.1:3800/viewport?width=1280&height=720"
116
+ ```
117
+
118
+ ## Auth / Cookie Injection
119
+
120
+ For pages that need authentication:
121
+ ```bash
122
+ # Set cookies manually
123
+ curl -s 'http://127.0.0.1:3800/cookies?set=[{"name":"session","value":"abc123","domain":"example.com","path":"/"}]'
124
+ # Then navigate to the authenticated page
125
+ curl -s "http://127.0.0.1:3800/navigate?url=https://example.com/dashboard"
126
+ ```
127
+
128
+ ## Important Notes
129
+
130
+ - **Server auto-shuts down** after 5 min idle — restart if needed
131
+ - **One page at a time** — navigation replaces the current page
132
+ - **Screenshots** are saved to `/tmp/alvin-bot/browse/` — view with Read tool
133
+ - **127.0.0.1 only** — not accessible from outside
134
+ - **URL-encode** values with special chars: `value=hello%20world`
135
+ - **Refs reset** on every navigation/click — always get fresh /tree after page changes
136
+ - For **local dev servers**: use `http://localhost:PORT` as the URL
@@ -0,0 +1,43 @@
1
+ ---
2
+ name: Code Project
3
+ description: Build, debug, and manage code projects
4
+ triggers: code, programming, build project, create app, debug, fix bug, refactor, deploy, git, npm, python project, node project
5
+ priority: 4
6
+ category: development
7
+ ---
8
+
9
+ # Code Project Skill
10
+
11
+ When working on code projects:
12
+
13
+ ## Workflow
14
+ 1. **Understand** — read existing files, README, package.json before changing anything
15
+ 2. **Plan** — explain what you'll do before doing it
16
+ 3. **Implement** — write clean, well-structured code
17
+ 4. **Test** — run the code, check for errors, verify it works
18
+ 5. **Document** — update README if you made significant changes
19
+
20
+ ## Guidelines
21
+ - **Read before write** — always understand existing code structure first
22
+ - **Small commits** — one logical change per step
23
+ - **Error handling** — always handle errors, never ignore them
24
+ - **Types** — use TypeScript types, avoid `any` where possible
25
+ - **Test after changes** — run `node --check`, build, or test suite
26
+
27
+ ## Common Patterns
28
+ ```bash
29
+ # Node.js project
30
+ cat package.json | head -20 # understand the project
31
+ npm run build # verify it builds
32
+ npm test # run tests
33
+
34
+ # Python project
35
+ cat requirements.txt # dependencies
36
+ python3 -m py_compile file.py # syntax check
37
+ python3 -m pytest # run tests
38
+
39
+ # Git
40
+ git status # check state
41
+ git diff --stat # see changes
42
+ git log --oneline -5 # recent history
43
+ ```
@@ -0,0 +1,39 @@
1
+ ---
2
+ name: Data Analysis
3
+ description: Analyze data files (CSV, JSON, Excel) with Python
4
+ triggers: analyze data, data analysis, datenanalyse, csv, excel analysis, chart, graph, visualize, statistik, statistics
5
+ priority: 4
6
+ category: analysis
7
+ ---
8
+
9
+ # Data Analysis Skill
10
+
11
+ When analyzing data files or creating visualizations:
12
+
13
+ ## Workflow
14
+ 1. **Read the file** — use Python (pandas) for CSV/Excel, or parse JSON directly
15
+ 2. **Analyze YOURSELF** — describe patterns, trends, outliers in natural language
16
+ 3. **Visualize** with matplotlib/seaborn if charts are requested
17
+ 4. **Save outputs** to /tmp/ and deliver to the user
18
+
19
+ ## Python Pattern
20
+ ```python
21
+ import pandas as pd
22
+ import matplotlib.pyplot as plt
23
+
24
+ df = pd.read_csv("/path/to/data.csv") # or pd.read_excel()
25
+ print(df.describe())
26
+ print(df.head(10))
27
+
28
+ # Visualization
29
+ fig, ax = plt.subplots(figsize=(10, 6))
30
+ df.plot(kind='bar', ax=ax)
31
+ plt.tight_layout()
32
+ plt.savefig('/tmp/chart.png', dpi=150)
33
+ ```
34
+
35
+ ## Guidelines
36
+ - Always show a quick summary first (rows, columns, types, missing values)
37
+ - Interpret results — don't just dump numbers, explain what they mean
38
+ - For large datasets: show head + describe, not the full data
39
+ - Charts: clean labels, readable fonts, meaningful titles
@@ -0,0 +1,48 @@
1
+ ---
2
+ name: Document Creation
3
+ description: Create professional documents (PDF, reports, letters)
4
+ triggers: pdf, document, report, brief, letter, create document, dokument erstellen, bericht, schreiben, anschreiben, vorlage
5
+ priority: 4
6
+ category: productivity
7
+ ---
8
+
9
+ # Document Creation Skill
10
+
11
+ When creating professional documents:
12
+
13
+ ## Workflow
14
+ 1. **Clarify** format and content requirements
15
+ 2. **Write** the content YOURSELF — you are the language model
16
+ 3. **Generate** HTML with professional styling
17
+ 4. **Convert** to PDF using available tools
18
+ 5. **Deliver** to the user
19
+
20
+ ## PDF via HTML + Chrome/Playwright
21
+ ```bash
22
+ # Write HTML
23
+ cat > /tmp/document.html << 'EOF'
24
+ <!DOCTYPE html>
25
+ <html><head><meta charset="utf-8">
26
+ <style>
27
+ body { font-family: 'Segoe UI', system-ui, sans-serif; max-width: 210mm; margin: 0 auto; padding: 20mm; line-height: 1.6; color: #333; }
28
+ h1 { color: #1a1a2e; border-bottom: 2px solid #e2b04a; padding-bottom: 8px; }
29
+ h2 { color: #2d2a26; margin-top: 1.5em; }
30
+ .header { text-align: right; font-size: 0.9em; color: #666; margin-bottom: 2em; }
31
+ table { width: 100%; border-collapse: collapse; margin: 1em 0; }
32
+ th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
33
+ th { background: #f5f5f5; }
34
+ </style></head><body>
35
+ <!-- CONTENT HERE -->
36
+ </body></html>
37
+ EOF
38
+
39
+ # Convert to PDF
40
+ npx playwright-core pdf /tmp/document.html /tmp/document.pdf --format=A4 2>/dev/null || \
41
+ wkhtmltopdf --page-size A4 /tmp/document.html /tmp/document.pdf 2>/dev/null
42
+ ```
43
+
44
+ ## Guidelines
45
+ - **A4 format** for European documents, Letter for US
46
+ - **Professional tone** unless told otherwise
47
+ - **Page breaks** — use `break-inside: avoid` on logical blocks
48
+ - **Date format** — match user's locale (DD.MM.YYYY for DE, MM/DD/YYYY for EN)