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.
- package/.env.template +16 -0
- package/client/package.json +1 -1
- package/client/src/Dashboard.tsx +3 -3
- package/client/src/components/AIAgentNode.tsx +24 -12
- package/client/src/components/OutputPanel.tsx +3 -2
- package/client/src/components/parameterPanel/InputSection.tsx +16 -3
- package/client/src/nodeDefinitions/aiAgentNodes.ts +12 -0
- package/client/src/nodeDefinitions/specializedAgentNodes.ts +68 -320
- package/client/src/nodeDefinitions/toolNodes.ts +87 -1
- package/client/src/nodeDefinitions/workflowNodes.ts +55 -1
- package/package.json +12 -3
- package/scripts/daemon.js +427 -0
- package/scripts/start.js +7 -1
- package/scripts/sync-version.js +108 -0
- package/server/Dockerfile +6 -7
- package/server/constants.py +2 -0
- package/server/core/cleanup.py +123 -0
- package/server/core/config.py +16 -0
- package/server/core/database.py +92 -1
- package/server/core/health.py +121 -0
- package/server/examples/__init__.py +1 -0
- package/server/gunicorn.conf.py +46 -0
- package/server/main.py +38 -3
- package/server/models/database.py +1 -0
- package/server/models/nodes.py +18 -2
- package/server/requirements-docker.txt +86 -0
- package/server/routers/database.py +16 -0
- package/server/routers/websocket.py +6 -5
- package/server/services/ai.py +115 -14
- package/server/services/auth.py +6 -1
- package/server/services/deployment/manager.py +14 -0
- package/server/services/event_waiter.py +55 -0
- package/server/services/example_loader.py +60 -0
- package/server/services/execution/executor.py +2 -0
- package/server/services/execution/models.py +8 -0
- package/server/services/handlers/__init__.py +2 -0
- package/server/services/handlers/ai.py +164 -11
- package/server/services/handlers/document.py +13 -4
- package/server/services/handlers/tools.py +445 -14
- package/server/services/node_executor.py +3 -0
- package/server/services/temporal/activities.py +3 -0
- package/server/services/workflow.py +2 -0
- package/server/skills/android_agent/app-launcher-skill/SKILL.md +137 -0
- package/server/skills/android_agent/app-list-skill/SKILL.md +148 -0
- package/server/skills/android_agent/audio-skill/SKILL.md +169 -0
- package/server/skills/android_agent/battery-skill/SKILL.md +114 -0
- package/server/skills/android_agent/bluetooth-skill/SKILL.md +151 -0
- package/server/skills/android_agent/camera-skill/SKILL.md +148 -0
- package/server/skills/android_agent/environmental-skill/SKILL.md +140 -0
- package/server/skills/android_agent/location-skill/SKILL.md +163 -0
- package/server/skills/android_agent/motion-skill/SKILL.md +141 -0
- package/server/skills/android_agent/screen-control-skill/SKILL.md +164 -0
- package/server/skills/android_agent/wifi-skill/SKILL.md +182 -0
- package/server/skills/assistant/subagent-skill/SKILL.md +205 -0
- package/server/skills/coding_agent/javascript-skill/SKILL.md +196 -0
- package/server/skills/coding_agent/python-skill/SKILL.md +165 -0
- package/server/skills/social_agent/whatsapp-db-skill/SKILL.md +284 -0
- package/server/skills/social_agent/whatsapp-send-skill/SKILL.md +180 -0
- package/server/skills/task_agent/cron-scheduler-skill/SKILL.md +215 -0
- package/server/skills/task_agent/task-manager-skill/SKILL.md +251 -0
- package/server/skills/task_agent/timer-skill/SKILL.md +168 -0
- package/server/skills/travel_agent/geocoding-skill/SKILL.md +186 -0
- package/server/skills/travel_agent/nearby-places-skill/SKILL.md +234 -0
- package/server/skills/web_agent/http-request-skill/SKILL.md +211 -0
- package/server/skills/android/skill/SKILL.md +0 -84
- package/server/skills/assistant/code-skill/SKILL.md +0 -176
- package/server/skills/assistant/http-skill/SKILL.md +0 -163
- package/server/skills/assistant/maps-skill/SKILL.md +0 -172
- package/server/skills/assistant/scheduler-skill/SKILL.md +0 -86
- package/server/skills/assistant/whatsapp-skill/SKILL.md +0 -285
- /package/server/skills/{android → android_agent}/personality/SKILL.md +0 -0
- /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
|
-
|
|
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
|
-
|
|
13
|
+
# Use requirements-docker.txt which excludes CUDA-heavy dependencies
|
|
14
|
+
COPY requirements-docker.txt .
|
|
14
15
|
|
|
15
|
-
# Install Python dependencies
|
|
16
|
-
|
|
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 . .
|
package/server/constants.py
CHANGED
|
@@ -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
|
# =============================================================================
|