machinaos 0.0.10 → 0.0.13

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 (72) hide show
  1. package/.env.template +16 -0
  2. package/client/package.json +1 -1
  3. package/client/src/Dashboard.tsx +3 -3
  4. package/client/src/components/AIAgentNode.tsx +24 -12
  5. package/client/src/components/OutputPanel.tsx +3 -2
  6. package/client/src/components/parameterPanel/InputSection.tsx +16 -3
  7. package/client/src/nodeDefinitions/aiAgentNodes.ts +12 -0
  8. package/client/src/nodeDefinitions/specializedAgentNodes.ts +68 -320
  9. package/client/src/nodeDefinitions/toolNodes.ts +87 -1
  10. package/client/src/nodeDefinitions/workflowNodes.ts +55 -1
  11. package/package.json +12 -3
  12. package/scripts/daemon.js +427 -0
  13. package/scripts/start.js +7 -1
  14. package/scripts/sync-version.js +108 -0
  15. package/server/Dockerfile +6 -7
  16. package/server/constants.py +2 -0
  17. package/server/core/cleanup.py +123 -0
  18. package/server/core/config.py +16 -0
  19. package/server/core/database.py +92 -1
  20. package/server/core/health.py +121 -0
  21. package/server/examples/__init__.py +1 -0
  22. package/server/gunicorn.conf.py +46 -0
  23. package/server/main.py +38 -3
  24. package/server/models/database.py +1 -0
  25. package/server/models/nodes.py +18 -2
  26. package/server/requirements-docker.txt +86 -0
  27. package/server/routers/database.py +16 -0
  28. package/server/routers/websocket.py +6 -5
  29. package/server/services/ai.py +115 -14
  30. package/server/services/auth.py +6 -1
  31. package/server/services/deployment/manager.py +14 -0
  32. package/server/services/event_waiter.py +55 -0
  33. package/server/services/example_loader.py +60 -0
  34. package/server/services/execution/executor.py +2 -0
  35. package/server/services/execution/models.py +8 -0
  36. package/server/services/handlers/__init__.py +2 -0
  37. package/server/services/handlers/ai.py +164 -11
  38. package/server/services/handlers/document.py +13 -4
  39. package/server/services/handlers/tools.py +445 -14
  40. package/server/services/node_executor.py +3 -0
  41. package/server/services/temporal/activities.py +3 -0
  42. package/server/services/workflow.py +2 -0
  43. package/server/skills/android_agent/app-launcher-skill/SKILL.md +137 -0
  44. package/server/skills/android_agent/app-list-skill/SKILL.md +148 -0
  45. package/server/skills/android_agent/audio-skill/SKILL.md +169 -0
  46. package/server/skills/android_agent/battery-skill/SKILL.md +114 -0
  47. package/server/skills/android_agent/bluetooth-skill/SKILL.md +151 -0
  48. package/server/skills/android_agent/camera-skill/SKILL.md +148 -0
  49. package/server/skills/android_agent/environmental-skill/SKILL.md +140 -0
  50. package/server/skills/android_agent/location-skill/SKILL.md +163 -0
  51. package/server/skills/android_agent/motion-skill/SKILL.md +141 -0
  52. package/server/skills/android_agent/screen-control-skill/SKILL.md +164 -0
  53. package/server/skills/android_agent/wifi-skill/SKILL.md +182 -0
  54. package/server/skills/assistant/subagent-skill/SKILL.md +205 -0
  55. package/server/skills/coding_agent/javascript-skill/SKILL.md +196 -0
  56. package/server/skills/coding_agent/python-skill/SKILL.md +165 -0
  57. package/server/skills/social_agent/whatsapp-db-skill/SKILL.md +284 -0
  58. package/server/skills/social_agent/whatsapp-send-skill/SKILL.md +180 -0
  59. package/server/skills/task_agent/cron-scheduler-skill/SKILL.md +215 -0
  60. package/server/skills/task_agent/task-manager-skill/SKILL.md +251 -0
  61. package/server/skills/task_agent/timer-skill/SKILL.md +168 -0
  62. package/server/skills/travel_agent/geocoding-skill/SKILL.md +186 -0
  63. package/server/skills/travel_agent/nearby-places-skill/SKILL.md +234 -0
  64. package/server/skills/web_agent/http-request-skill/SKILL.md +211 -0
  65. package/server/skills/android/skill/SKILL.md +0 -84
  66. package/server/skills/assistant/code-skill/SKILL.md +0 -176
  67. package/server/skills/assistant/http-skill/SKILL.md +0 -163
  68. package/server/skills/assistant/maps-skill/SKILL.md +0 -172
  69. package/server/skills/assistant/scheduler-skill/SKILL.md +0 -86
  70. package/server/skills/assistant/whatsapp-skill/SKILL.md +0 -285
  71. /package/server/skills/{android → android_agent}/personality/SKILL.md +0 -0
  72. /package/server/skills/{assistant → web_agent}/web-search-skill/SKILL.md +0 -0
@@ -0,0 +1,427 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Cross-platform service installer for MachinaOS.
4
+ * Works on: Windows (NSSM), Linux (systemd), macOS (launchd)
5
+ *
6
+ * Usage:
7
+ * node scripts/service.js install [OPTIONS]
8
+ * node scripts/service.js uninstall
9
+ * node scripts/service.js status
10
+ * node scripts/service.js start
11
+ * node scripts/service.js stop
12
+ * node scripts/service.js restart
13
+ *
14
+ * Options:
15
+ * --dir=PATH Installation directory (default: current directory)
16
+ * --user=USER Service user (default: current user)
17
+ * --memory=LIMIT Memory limit (default: 2G, Linux only)
18
+ */
19
+ import { execSync, spawnSync } from 'child_process';
20
+ import { existsSync, writeFileSync, mkdirSync, unlinkSync } from 'fs';
21
+ import { resolve, dirname } from 'path';
22
+ import { fileURLToPath } from 'url';
23
+ import { homedir } from 'os';
24
+
25
+ const __dirname = dirname(fileURLToPath(import.meta.url));
26
+ const ROOT = resolve(__dirname, '..');
27
+
28
+ // Platform detection
29
+ const isWindows = process.platform === 'win32';
30
+ const isMac = process.platform === 'darwin';
31
+ const isLinux = process.platform === 'linux';
32
+
33
+ // Service name
34
+ const SERVICE_NAME = 'MachinaOs';
35
+
36
+ // Parse command line arguments
37
+ const args = process.argv.slice(2);
38
+ const command = args[0] || 'help';
39
+ const options = {};
40
+ args.slice(1).forEach(arg => {
41
+ const match = arg.match(/^--(\w+)=(.+)$/);
42
+ if (match) {
43
+ options[match[1]] = match[2];
44
+ }
45
+ });
46
+
47
+ // Default options
48
+ const installDir = options.dir || ROOT;
49
+ const serviceUser = options.user || (isWindows ? process.env.USERNAME : process.env.USER);
50
+ const memoryLimit = options.memory || '2G';
51
+
52
+ function run(cmd, opts = {}) {
53
+ try {
54
+ return execSync(cmd, {
55
+ encoding: 'utf-8',
56
+ stdio: opts.silent ? 'pipe' : 'inherit',
57
+ shell: true,
58
+ ...opts
59
+ });
60
+ } catch (e) {
61
+ if (!opts.ignoreError) {
62
+ throw e;
63
+ }
64
+ return '';
65
+ }
66
+ }
67
+
68
+ function runSilent(cmd) {
69
+ try {
70
+ execSync(cmd, { encoding: 'utf-8', stdio: 'pipe', shell: true });
71
+ return true;
72
+ } catch {
73
+ return false;
74
+ }
75
+ }
76
+
77
+ // ============================================================================
78
+ // Windows (NSSM)
79
+ // ============================================================================
80
+
81
+ function windowsCheckNssm() {
82
+ if (!runSilent('where nssm')) {
83
+ console.log('NSSM not found. Install from https://nssm.cc/');
84
+ console.log('Or use: winget install nssm');
85
+ process.exit(1);
86
+ }
87
+ }
88
+
89
+ function windowsInstall() {
90
+ windowsCheckNssm();
91
+
92
+ const pythonExe = resolve(installDir, 'server', '.venv', 'Scripts', 'python.exe');
93
+ const serverDir = resolve(installDir, 'server');
94
+ const logsDir = resolve(installDir, 'logs');
95
+
96
+ if (!existsSync(pythonExe)) {
97
+ console.log(`Python venv not found at ${pythonExe}`);
98
+ console.log('Run "npm run build" first.');
99
+ process.exit(1);
100
+ }
101
+
102
+ // Create logs directory
103
+ if (!existsSync(logsDir)) {
104
+ mkdirSync(logsDir, { recursive: true });
105
+ }
106
+
107
+ console.log(`Installing ${SERVICE_NAME} service...`);
108
+ console.log(` Directory: ${installDir}`);
109
+ console.log(` Python: ${pythonExe}`);
110
+
111
+ // Remove existing service if present
112
+ run(`nssm stop ${SERVICE_NAME}`, { ignoreError: true, silent: true });
113
+ run(`nssm remove ${SERVICE_NAME} confirm`, { ignoreError: true, silent: true });
114
+
115
+ // Install service
116
+ run(`nssm install ${SERVICE_NAME} "${pythonExe}" -m gunicorn main:app -c gunicorn.conf.py`);
117
+ run(`nssm set ${SERVICE_NAME} AppDirectory "${serverDir}"`);
118
+ run(`nssm set ${SERVICE_NAME} AppEnvironmentExtra "PYTHONPATH=${serverDir}"`);
119
+ run(`nssm set ${SERVICE_NAME} Start SERVICE_AUTO_START`);
120
+ run(`nssm set ${SERVICE_NAME} AppStdout "${logsDir}\\service.log"`);
121
+ run(`nssm set ${SERVICE_NAME} AppStderr "${logsDir}\\service.log"`);
122
+ run(`nssm set ${SERVICE_NAME} AppRotateFiles 1`);
123
+ run(`nssm set ${SERVICE_NAME} AppRotateBytes 10485760`); // 10MB
124
+
125
+ console.log('Service installed successfully.');
126
+ console.log(`Logs: ${logsDir}\\service.log`);
127
+ }
128
+
129
+ function windowsUninstall() {
130
+ windowsCheckNssm();
131
+ run(`nssm stop ${SERVICE_NAME}`, { ignoreError: true });
132
+ run(`nssm remove ${SERVICE_NAME} confirm`, { ignoreError: true });
133
+ console.log('Service uninstalled.');
134
+ }
135
+
136
+ function windowsStatus() {
137
+ windowsCheckNssm();
138
+ run(`nssm status ${SERVICE_NAME}`, { ignoreError: true });
139
+ }
140
+
141
+ function windowsStart() {
142
+ windowsCheckNssm();
143
+ run(`nssm start ${SERVICE_NAME}`);
144
+ }
145
+
146
+ function windowsStop() {
147
+ windowsCheckNssm();
148
+ run(`nssm stop ${SERVICE_NAME}`);
149
+ }
150
+
151
+ function windowsRestart() {
152
+ windowsCheckNssm();
153
+ run(`nssm restart ${SERVICE_NAME}`);
154
+ }
155
+
156
+ // ============================================================================
157
+ // Linux (systemd)
158
+ // ============================================================================
159
+
160
+ function linuxGenerateServiceFile() {
161
+ const venvPath = resolve(installDir, 'server', '.venv');
162
+ const serverDir = resolve(installDir, 'server');
163
+
164
+ return `[Unit]
165
+ Description=MachinaOs Backend
166
+ After=network.target
167
+
168
+ [Service]
169
+ Type=simple
170
+ User=${serviceUser}
171
+ WorkingDirectory=${serverDir}
172
+ EnvironmentFile=${installDir}/.env
173
+ ExecStart=${venvPath}/bin/gunicorn main:app -c gunicorn.conf.py
174
+ Restart=always
175
+ RestartSec=5
176
+ StartLimitIntervalSec=60
177
+ StartLimitBurst=5
178
+
179
+ # Resource limits
180
+ MemoryMax=${memoryLimit}
181
+
182
+ [Install]
183
+ WantedBy=multi-user.target
184
+ `;
185
+ }
186
+
187
+ function linuxInstall() {
188
+ const venvPath = resolve(installDir, 'server', '.venv', 'bin', 'python');
189
+
190
+ if (!existsSync(venvPath)) {
191
+ console.log(`Python venv not found at ${venvPath}`);
192
+ console.log('Run "npm run build" first.');
193
+ process.exit(1);
194
+ }
195
+
196
+ console.log(`Installing ${SERVICE_NAME} service...`);
197
+ console.log(` Directory: ${installDir}`);
198
+ console.log(` User: ${serviceUser}`);
199
+ console.log(` Memory limit: ${memoryLimit}`);
200
+
201
+ const serviceContent = linuxGenerateServiceFile();
202
+ const tempFile = '/tmp/machina.service';
203
+ writeFileSync(tempFile, serviceContent);
204
+
205
+ run(`sudo cp ${tempFile} /etc/systemd/system/machina.service`);
206
+ run('sudo systemctl daemon-reload');
207
+ run('sudo systemctl enable machina');
208
+
209
+ console.log('Service installed successfully.');
210
+ console.log('Start with: sudo systemctl start machina');
211
+ console.log('View logs: journalctl -u machina -f');
212
+ }
213
+
214
+ function linuxUninstall() {
215
+ run('sudo systemctl stop machina', { ignoreError: true });
216
+ run('sudo systemctl disable machina', { ignoreError: true });
217
+ if (existsSync('/etc/systemd/system/machina.service')) {
218
+ run('sudo rm /etc/systemd/system/machina.service');
219
+ }
220
+ run('sudo systemctl daemon-reload');
221
+ console.log('Service uninstalled.');
222
+ }
223
+
224
+ function linuxStatus() {
225
+ run('systemctl status machina', { ignoreError: true });
226
+ }
227
+
228
+ function linuxStart() {
229
+ run('sudo systemctl start machina');
230
+ }
231
+
232
+ function linuxStop() {
233
+ run('sudo systemctl stop machina');
234
+ }
235
+
236
+ function linuxRestart() {
237
+ run('sudo systemctl restart machina');
238
+ }
239
+
240
+ // ============================================================================
241
+ // macOS (launchd)
242
+ // ============================================================================
243
+
244
+ function macGeneratePlist() {
245
+ const venvPath = resolve(installDir, 'server', '.venv');
246
+ const serverDir = resolve(installDir, 'server');
247
+ const logsDir = resolve(installDir, 'logs');
248
+
249
+ return `<?xml version="1.0" encoding="UTF-8"?>
250
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
251
+ <plist version="1.0">
252
+ <dict>
253
+ <key>Label</key>
254
+ <string>com.machinaos.backend</string>
255
+ <key>ProgramArguments</key>
256
+ <array>
257
+ <string>${venvPath}/bin/gunicorn</string>
258
+ <string>main:app</string>
259
+ <string>-c</string>
260
+ <string>gunicorn.conf.py</string>
261
+ </array>
262
+ <key>WorkingDirectory</key>
263
+ <string>${serverDir}</string>
264
+ <key>EnvironmentVariables</key>
265
+ <dict>
266
+ <key>PATH</key>
267
+ <string>${venvPath}/bin:/usr/local/bin:/usr/bin:/bin</string>
268
+ </dict>
269
+ <key>RunAtLoad</key>
270
+ <true/>
271
+ <key>KeepAlive</key>
272
+ <true/>
273
+ <key>StandardOutPath</key>
274
+ <string>${logsDir}/service.log</string>
275
+ <key>StandardErrorPath</key>
276
+ <string>${logsDir}/service.log</string>
277
+ </dict>
278
+ </plist>
279
+ `;
280
+ }
281
+
282
+ function macGetPlistPath() {
283
+ return resolve(homedir(), 'Library', 'LaunchAgents', 'com.machinaos.backend.plist');
284
+ }
285
+
286
+ function macInstall() {
287
+ const venvPath = resolve(installDir, 'server', '.venv', 'bin', 'python');
288
+ const logsDir = resolve(installDir, 'logs');
289
+
290
+ if (!existsSync(venvPath)) {
291
+ console.log(`Python venv not found at ${venvPath}`);
292
+ console.log('Run "npm run build" first.');
293
+ process.exit(1);
294
+ }
295
+
296
+ // Create logs directory
297
+ if (!existsSync(logsDir)) {
298
+ mkdirSync(logsDir, { recursive: true });
299
+ }
300
+
301
+ console.log(`Installing ${SERVICE_NAME} service...`);
302
+ console.log(` Directory: ${installDir}`);
303
+
304
+ const plistContent = macGeneratePlist();
305
+ const plistPath = macGetPlistPath();
306
+
307
+ // Ensure LaunchAgents directory exists
308
+ const launchAgentsDir = dirname(plistPath);
309
+ if (!existsSync(launchAgentsDir)) {
310
+ mkdirSync(launchAgentsDir, { recursive: true });
311
+ }
312
+
313
+ // Unload existing if present
314
+ run(`launchctl unload "${plistPath}"`, { ignoreError: true, silent: true });
315
+
316
+ writeFileSync(plistPath, plistContent);
317
+
318
+ console.log('Service installed successfully.');
319
+ console.log(`Plist: ${plistPath}`);
320
+ console.log(`Logs: ${logsDir}/service.log`);
321
+ console.log('Start with: node scripts/service.js start');
322
+ }
323
+
324
+ function macUninstall() {
325
+ const plistPath = macGetPlistPath();
326
+ run(`launchctl unload "${plistPath}"`, { ignoreError: true });
327
+ if (existsSync(plistPath)) {
328
+ unlinkSync(plistPath);
329
+ }
330
+ console.log('Service uninstalled.');
331
+ }
332
+
333
+ function macStatus() {
334
+ run('launchctl list | grep machinaos', { ignoreError: true });
335
+ }
336
+
337
+ function macStart() {
338
+ const plistPath = macGetPlistPath();
339
+ run(`launchctl load "${plistPath}"`);
340
+ }
341
+
342
+ function macStop() {
343
+ const plistPath = macGetPlistPath();
344
+ run(`launchctl unload "${plistPath}"`);
345
+ }
346
+
347
+ function macRestart() {
348
+ macStop();
349
+ macStart();
350
+ }
351
+
352
+ // ============================================================================
353
+ // Main
354
+ // ============================================================================
355
+
356
+ function showHelp() {
357
+ console.log(`
358
+ MachinaOS Service Manager
359
+
360
+ Usage: node scripts/service.js <command> [options]
361
+
362
+ Commands:
363
+ install Install as system service
364
+ uninstall Remove system service
365
+ status Show service status
366
+ start Start the service
367
+ stop Stop the service
368
+ restart Restart the service
369
+
370
+ Options:
371
+ --dir=PATH Installation directory (default: current)
372
+ --user=USER Service user (Linux only, default: current user)
373
+ --memory=LIMIT Memory limit (Linux only, default: 2G)
374
+
375
+ Platform: ${isWindows ? 'Windows (NSSM)' : isMac ? 'macOS (launchd)' : 'Linux (systemd)'}
376
+
377
+ Examples:
378
+ node scripts/service.js install
379
+ node scripts/service.js install --dir=/opt/machinaos --user=machina
380
+ node scripts/service.js status
381
+ node scripts/service.js restart
382
+ `);
383
+ }
384
+
385
+ // Dispatch to platform-specific functions
386
+ const handlers = {
387
+ windows: {
388
+ install: windowsInstall,
389
+ uninstall: windowsUninstall,
390
+ status: windowsStatus,
391
+ start: windowsStart,
392
+ stop: windowsStop,
393
+ restart: windowsRestart,
394
+ },
395
+ linux: {
396
+ install: linuxInstall,
397
+ uninstall: linuxUninstall,
398
+ status: linuxStatus,
399
+ start: linuxStart,
400
+ stop: linuxStop,
401
+ restart: linuxRestart,
402
+ },
403
+ mac: {
404
+ install: macInstall,
405
+ uninstall: macUninstall,
406
+ status: macStatus,
407
+ start: macStart,
408
+ stop: macStop,
409
+ restart: macRestart,
410
+ },
411
+ };
412
+
413
+ const platform = isWindows ? 'windows' : isMac ? 'mac' : 'linux';
414
+
415
+ if (command === 'help' || command === '--help' || command === '-h') {
416
+ showHelp();
417
+ process.exit(0);
418
+ }
419
+
420
+ const handler = handlers[platform][command];
421
+ if (!handler) {
422
+ console.log(`Unknown command: ${command}`);
423
+ showHelp();
424
+ process.exit(1);
425
+ }
426
+
427
+ handler();
package/scripts/start.js CHANGED
@@ -12,6 +12,10 @@ const __dirname = dirname(fileURLToPath(import.meta.url));
12
12
  const ROOT = resolve(__dirname, '..');
13
13
  const START_TIME = Date.now();
14
14
 
15
+ // Parse command line arguments
16
+ const args = process.argv.slice(2);
17
+ const isDaemonMode = args.includes('--daemon');
18
+
15
19
  // ============================================================================
16
20
  // Platform Detection
17
21
  // ============================================================================
@@ -158,6 +162,7 @@ process.env.PYTHONUTF8 = '1';
158
162
 
159
163
  console.log('\n=== MachinaOS Starting ===\n');
160
164
  log(`Platform: ${getPlatformName()}`);
165
+ log(`Mode: ${isDaemonMode ? 'Daemon (Gunicorn)' : 'Development (uvicorn)'}`);
161
166
  log(`Ports: ${config.ports.join(', ')}`);
162
167
  log(`Temporal: ${config.temporalEnabled ? 'enabled' : 'disabled'}`);
163
168
 
@@ -219,7 +224,8 @@ if (isProduction) {
219
224
  services.push('npm:client:start');
220
225
  }
221
226
 
222
- services.push('npm:python:start');
227
+ // Python backend: use gunicorn in daemon mode, uvicorn in development
228
+ services.push(isDaemonMode ? 'npm:python:daemon' : 'npm:python:start');
223
229
 
224
230
  // WhatsApp RPC: only start if the pre-built binary exists
225
231
  const whatsappBin = resolve(ROOT, 'node_modules', 'whatsapp-rpc', 'bin',
@@ -0,0 +1,108 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Syncs package.json version from the latest git tag.
4
+ *
5
+ * Usage:
6
+ * node scripts/sync-version.js # Sync from latest tag
7
+ * node scripts/sync-version.js v0.0.11 # Sync from specific tag
8
+ *
9
+ * This script:
10
+ * 1. Gets the latest git tag (or uses provided tag)
11
+ * 2. Strips the 'v' prefix to get semver version
12
+ * 3. Updates root package.json and client/package.json
13
+ */
14
+ import { execSync } from 'child_process';
15
+ import { readFileSync, writeFileSync } from 'fs';
16
+ import { resolve, dirname } from 'path';
17
+ import { fileURLToPath } from 'url';
18
+
19
+ const __dirname = dirname(fileURLToPath(import.meta.url));
20
+ const ROOT = resolve(__dirname, '..');
21
+
22
+ function getLatestTag() {
23
+ try {
24
+ // Get the latest tag sorted by version
25
+ const tag = execSync('git describe --tags --abbrev=0', {
26
+ encoding: 'utf-8',
27
+ stdio: ['pipe', 'pipe', 'pipe'],
28
+ cwd: ROOT
29
+ }).trim();
30
+ return tag;
31
+ } catch {
32
+ // Fallback: list tags and get the latest by version sort
33
+ try {
34
+ const tags = execSync('git tag -l --sort=-version:refname', {
35
+ encoding: 'utf-8',
36
+ stdio: ['pipe', 'pipe', 'pipe'],
37
+ cwd: ROOT
38
+ }).trim().split('\n');
39
+ return tags[0] || null;
40
+ } catch {
41
+ return null;
42
+ }
43
+ }
44
+ }
45
+
46
+ function tagToVersion(tag) {
47
+ // Strip 'v' prefix if present: v0.0.11 -> 0.0.11
48
+ return tag.replace(/^v/, '');
49
+ }
50
+
51
+ function updatePackageJson(filePath, newVersion) {
52
+ const content = readFileSync(filePath, 'utf-8');
53
+ const pkg = JSON.parse(content);
54
+ const oldVersion = pkg.version;
55
+
56
+ if (oldVersion === newVersion) {
57
+ console.log(` ${filePath}: already at ${newVersion}`);
58
+ return false;
59
+ }
60
+
61
+ pkg.version = newVersion;
62
+ // Preserve formatting (2-space indent)
63
+ writeFileSync(filePath, JSON.stringify(pkg, null, 2) + '\n');
64
+ console.log(` ${filePath}: ${oldVersion} -> ${newVersion}`);
65
+ return true;
66
+ }
67
+
68
+ // Main
69
+ const providedTag = process.argv[2];
70
+ const tag = providedTag || getLatestTag();
71
+
72
+ if (!tag) {
73
+ console.error('Error: No git tags found and no tag provided.');
74
+ console.error('Usage: node scripts/sync-version.js [tag]');
75
+ process.exit(1);
76
+ }
77
+
78
+ const version = tagToVersion(tag);
79
+
80
+ if (!/^\d+\.\d+\.\d+/.test(version)) {
81
+ console.error(`Error: Invalid version format from tag "${tag}": "${version}"`);
82
+ console.error('Expected semver format like v0.0.11 or 0.0.11');
83
+ process.exit(1);
84
+ }
85
+
86
+ console.log(`Syncing version from tag: ${tag} -> ${version}\n`);
87
+
88
+ const packageFiles = [
89
+ resolve(ROOT, 'package.json'),
90
+ resolve(ROOT, 'client', 'package.json'),
91
+ ];
92
+
93
+ let updated = 0;
94
+ for (const file of packageFiles) {
95
+ try {
96
+ if (updatePackageJson(file, version)) {
97
+ updated++;
98
+ }
99
+ } catch (err) {
100
+ console.error(` Error updating ${file}: ${err.message}`);
101
+ }
102
+ }
103
+
104
+ if (updated > 0) {
105
+ console.log(`\nUpdated ${updated} file(s). Don't forget to commit the changes.`);
106
+ } else {
107
+ console.log('\nAll package.json files already at correct version.');
108
+ }
package/server/Dockerfile CHANGED
@@ -1,4 +1,4 @@
1
- # Python FastAPI Backend
1
+ # Python FastAPI Backend (CPU-only, no CUDA)
2
2
  FROM python:3.12-slim
3
3
 
4
4
  WORKDIR /app
@@ -10,13 +10,12 @@ RUN apt-get update && apt-get install -y \
10
10
  && rm -rf /var/lib/apt/lists/*
11
11
 
12
12
  # Copy requirements first for better caching
13
- COPY requirements.txt .
13
+ # Use requirements-docker.txt which excludes CUDA-heavy dependencies
14
+ COPY requirements-docker.txt .
14
15
 
15
- # Install Python dependencies
16
- RUN pip install --no-cache-dir -r requirements.txt
17
-
18
- # Install aiohttp for WebSocket client (Android relay)
19
- RUN pip install --no-cache-dir aiohttp>=3.9.0
16
+ # Install Python dependencies (CPU-only)
17
+ # This avoids sentence-transformers/PyTorch CUDA which adds ~5GB to image
18
+ RUN pip install --no-cache-dir -r requirements-docker.txt
20
19
 
21
20
  # Copy application code
22
21
  COPY . .
@@ -214,6 +214,7 @@ EVENT_TRIGGER_TYPES: FrozenSet[str] = frozenset([
214
214
  'whatsappReceive',
215
215
  'workflowTrigger',
216
216
  'chatTrigger',
217
+ 'taskTrigger',
217
218
  ])
218
219
 
219
220
  # Legacy alias for backwards compatibility
@@ -235,6 +236,7 @@ WORKFLOW_TRIGGER_TYPES: FrozenSet[str] = frozenset([
235
236
  'whatsappReceive',
236
237
  'workflowTrigger',
237
238
  'chatTrigger',
239
+ 'taskTrigger',
238
240
  ])
239
241
 
240
242
  # =============================================================================