machinaos 0.0.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.
- package/.env.template +71 -0
- package/LICENSE +21 -0
- package/README.md +87 -0
- package/bin/cli.js +159 -0
- package/client/.dockerignore +45 -0
- package/client/Dockerfile +68 -0
- package/client/eslint.config.js +29 -0
- package/client/index.html +13 -0
- package/client/nginx.conf +66 -0
- package/client/package.json +48 -0
- package/client/src/App.tsx +27 -0
- package/client/src/Dashboard.tsx +1173 -0
- package/client/src/ParameterPanel.tsx +301 -0
- package/client/src/components/AIAgentNode.tsx +321 -0
- package/client/src/components/APIKeyValidator.tsx +118 -0
- package/client/src/components/ClaudeChatModelNode.tsx +18 -0
- package/client/src/components/ConditionalEdge.tsx +189 -0
- package/client/src/components/CredentialsModal.tsx +306 -0
- package/client/src/components/EdgeConditionEditor.tsx +443 -0
- package/client/src/components/GeminiChatModelNode.tsx +18 -0
- package/client/src/components/GenericNode.tsx +357 -0
- package/client/src/components/LocationParameterPanel.tsx +154 -0
- package/client/src/components/ModelNode.tsx +286 -0
- package/client/src/components/OpenAIChatModelNode.tsx +18 -0
- package/client/src/components/OutputPanel.tsx +471 -0
- package/client/src/components/ParameterRenderer.tsx +1874 -0
- package/client/src/components/SkillEditorModal.tsx +417 -0
- package/client/src/components/SquareNode.tsx +797 -0
- package/client/src/components/StartNode.tsx +250 -0
- package/client/src/components/ToolkitNode.tsx +365 -0
- package/client/src/components/TriggerNode.tsx +463 -0
- package/client/src/components/auth/LoginPage.tsx +247 -0
- package/client/src/components/auth/ProtectedRoute.tsx +59 -0
- package/client/src/components/base/BaseChatModelNode.tsx +271 -0
- package/client/src/components/icons/AIProviderIcons.tsx +50 -0
- package/client/src/components/maps/GoogleMapsPicker.tsx +137 -0
- package/client/src/components/maps/MapsPreviewPanel.tsx +110 -0
- package/client/src/components/maps/index.ts +26 -0
- package/client/src/components/parameterPanel/InputSection.tsx +1094 -0
- package/client/src/components/parameterPanel/LocationPanelLayout.tsx +65 -0
- package/client/src/components/parameterPanel/MapsSection.tsx +92 -0
- package/client/src/components/parameterPanel/MiddleSection.tsx +571 -0
- package/client/src/components/parameterPanel/OutputSection.tsx +81 -0
- package/client/src/components/parameterPanel/ParameterPanelLayout.tsx +82 -0
- package/client/src/components/parameterPanel/ToolSchemaEditor.tsx +436 -0
- package/client/src/components/parameterPanel/index.ts +42 -0
- package/client/src/components/shared/DataPanel.tsx +142 -0
- package/client/src/components/shared/JSONTreeRenderer.tsx +106 -0
- package/client/src/components/ui/AIResultModal.tsx +204 -0
- package/client/src/components/ui/AndroidSettingsPanel.tsx +401 -0
- package/client/src/components/ui/CodeEditor.tsx +81 -0
- package/client/src/components/ui/CollapsibleSection.tsx +88 -0
- package/client/src/components/ui/ComponentItem.tsx +154 -0
- package/client/src/components/ui/ComponentPalette.tsx +321 -0
- package/client/src/components/ui/ConsolePanel.tsx +1074 -0
- package/client/src/components/ui/ErrorBoundary.tsx +196 -0
- package/client/src/components/ui/InputNodesPanel.tsx +204 -0
- package/client/src/components/ui/MapSelector.tsx +314 -0
- package/client/src/components/ui/Modal.tsx +149 -0
- package/client/src/components/ui/NodeContextMenu.tsx +192 -0
- package/client/src/components/ui/NodeOutputPanel.tsx +1150 -0
- package/client/src/components/ui/OutputDisplayPanel.tsx +381 -0
- package/client/src/components/ui/SettingsPanel.tsx +243 -0
- package/client/src/components/ui/TopToolbar.tsx +736 -0
- package/client/src/components/ui/WhatsAppSettingsPanel.tsx +345 -0
- package/client/src/components/ui/WorkflowSidebar.tsx +294 -0
- package/client/src/config/antdTheme.ts +186 -0
- package/client/src/config/api.ts +54 -0
- package/client/src/contexts/AuthContext.tsx +221 -0
- package/client/src/contexts/ThemeContext.tsx +42 -0
- package/client/src/contexts/WebSocketContext.tsx +1971 -0
- package/client/src/factories/baseChatModelFactory.ts +256 -0
- package/client/src/hooks/useAndroidOperations.ts +164 -0
- package/client/src/hooks/useApiKeyValidation.ts +107 -0
- package/client/src/hooks/useApiKeys.ts +238 -0
- package/client/src/hooks/useAppTheme.ts +17 -0
- package/client/src/hooks/useComponentPalette.ts +51 -0
- package/client/src/hooks/useCopyPaste.ts +155 -0
- package/client/src/hooks/useDragAndDrop.ts +124 -0
- package/client/src/hooks/useDragVariable.ts +88 -0
- package/client/src/hooks/useExecution.ts +313 -0
- package/client/src/hooks/useParameterPanel.ts +176 -0
- package/client/src/hooks/useReactFlowNodes.ts +189 -0
- package/client/src/hooks/useToolSchema.ts +209 -0
- package/client/src/hooks/useWhatsApp.ts +196 -0
- package/client/src/hooks/useWorkflowManagement.ts +46 -0
- package/client/src/index.css +315 -0
- package/client/src/main.tsx +19 -0
- package/client/src/nodeDefinitions/aiAgentNodes.ts +336 -0
- package/client/src/nodeDefinitions/aiModelNodes.ts +340 -0
- package/client/src/nodeDefinitions/androidDeviceNodes.ts +140 -0
- package/client/src/nodeDefinitions/androidServiceNodes.ts +383 -0
- package/client/src/nodeDefinitions/chatNodes.ts +135 -0
- package/client/src/nodeDefinitions/codeNodes.ts +54 -0
- package/client/src/nodeDefinitions/documentNodes.ts +379 -0
- package/client/src/nodeDefinitions/index.ts +15 -0
- package/client/src/nodeDefinitions/locationNodes.ts +463 -0
- package/client/src/nodeDefinitions/schedulerNodes.ts +220 -0
- package/client/src/nodeDefinitions/skillNodes.ts +211 -0
- package/client/src/nodeDefinitions/toolNodes.ts +198 -0
- package/client/src/nodeDefinitions/utilityNodes.ts +284 -0
- package/client/src/nodeDefinitions/whatsappNodes.ts +865 -0
- package/client/src/nodeDefinitions/workflowNodes.ts +41 -0
- package/client/src/nodeDefinitions.ts +104 -0
- package/client/src/schemas/workflowSchema.ts +264 -0
- package/client/src/services/dynamicParameterService.ts +96 -0
- package/client/src/services/execution/aiAgentExecutionService.ts +35 -0
- package/client/src/services/executionService.ts +232 -0
- package/client/src/services/workflowApi.ts +91 -0
- package/client/src/store/useAppStore.ts +582 -0
- package/client/src/styles/theme.ts +508 -0
- package/client/src/styles/zIndex.ts +17 -0
- package/client/src/types/ComponentTypes.ts +39 -0
- package/client/src/types/EdgeCondition.ts +231 -0
- package/client/src/types/INodeProperties.ts +288 -0
- package/client/src/types/NodeTypes.ts +28 -0
- package/client/src/utils/formatters.ts +33 -0
- package/client/src/utils/googleMapsLoader.ts +140 -0
- package/client/src/utils/locationUtils.ts +85 -0
- package/client/src/utils/nodeUtils.ts +31 -0
- package/client/src/utils/workflow.ts +30 -0
- package/client/src/utils/workflowExport.ts +120 -0
- package/client/src/vite-env.d.ts +12 -0
- package/client/tailwind.config.js +60 -0
- package/client/tsconfig.json +25 -0
- package/client/tsconfig.node.json +11 -0
- package/client/vite.config.js +35 -0
- package/docker-compose.prod.yml +107 -0
- package/docker-compose.yml +104 -0
- package/docs-MachinaOs/README.md +85 -0
- package/docs-MachinaOs/deployment/docker.mdx +228 -0
- package/docs-MachinaOs/deployment/production.mdx +345 -0
- package/docs-MachinaOs/docs.json +75 -0
- package/docs-MachinaOs/faq.mdx +309 -0
- package/docs-MachinaOs/favicon.svg +5 -0
- package/docs-MachinaOs/installation.mdx +160 -0
- package/docs-MachinaOs/introduction.mdx +114 -0
- package/docs-MachinaOs/logo/dark.svg +6 -0
- package/docs-MachinaOs/logo/light.svg +6 -0
- package/docs-MachinaOs/nodes/ai-agent.mdx +216 -0
- package/docs-MachinaOs/nodes/ai-models.mdx +240 -0
- package/docs-MachinaOs/nodes/android.mdx +411 -0
- package/docs-MachinaOs/nodes/overview.mdx +181 -0
- package/docs-MachinaOs/nodes/schedulers.mdx +316 -0
- package/docs-MachinaOs/nodes/webhooks.mdx +330 -0
- package/docs-MachinaOs/nodes/whatsapp.mdx +305 -0
- package/docs-MachinaOs/quickstart.mdx +119 -0
- package/docs-MachinaOs/tutorials/ai-agent-workflow.mdx +177 -0
- package/docs-MachinaOs/tutorials/android-automation.mdx +242 -0
- package/docs-MachinaOs/tutorials/first-workflow.mdx +134 -0
- package/docs-MachinaOs/tutorials/whatsapp-automation.mdx +185 -0
- package/nul +0 -0
- package/package.json +70 -0
- package/scripts/build.js +158 -0
- package/scripts/check-ports.ps1 +33 -0
- package/scripts/clean.js +40 -0
- package/scripts/docker.js +93 -0
- package/scripts/kill-port.ps1 +154 -0
- package/scripts/start.js +210 -0
- package/scripts/stop.js +325 -0
- package/server/.dockerignore +44 -0
- package/server/Dockerfile +45 -0
- package/server/constants.py +249 -0
- package/server/core/__init__.py +1 -0
- package/server/core/cache.py +461 -0
- package/server/core/config.py +128 -0
- package/server/core/container.py +99 -0
- package/server/core/database.py +1211 -0
- package/server/core/logging.py +314 -0
- package/server/main.py +289 -0
- package/server/middleware/__init__.py +5 -0
- package/server/middleware/auth.py +89 -0
- package/server/models/__init__.py +1 -0
- package/server/models/auth.py +52 -0
- package/server/models/cache.py +24 -0
- package/server/models/database.py +211 -0
- package/server/models/nodes.py +455 -0
- package/server/package.json +9 -0
- package/server/pyproject.toml +72 -0
- package/server/requirements.txt +83 -0
- package/server/routers/__init__.py +1 -0
- package/server/routers/android.py +294 -0
- package/server/routers/auth.py +203 -0
- package/server/routers/database.py +151 -0
- package/server/routers/maps.py +142 -0
- package/server/routers/nodejs_compat.py +289 -0
- package/server/routers/webhook.py +90 -0
- package/server/routers/websocket.py +2127 -0
- package/server/routers/whatsapp.py +761 -0
- package/server/routers/workflow.py +200 -0
- package/server/services/__init__.py +1 -0
- package/server/services/ai.py +2415 -0
- package/server/services/android/__init__.py +27 -0
- package/server/services/android/broadcaster.py +114 -0
- package/server/services/android/client.py +608 -0
- package/server/services/android/manager.py +78 -0
- package/server/services/android/protocol.py +165 -0
- package/server/services/android_service.py +588 -0
- package/server/services/auth.py +131 -0
- package/server/services/chat_client.py +160 -0
- package/server/services/deployment/__init__.py +12 -0
- package/server/services/deployment/manager.py +706 -0
- package/server/services/deployment/state.py +47 -0
- package/server/services/deployment/triggers.py +275 -0
- package/server/services/event_waiter.py +785 -0
- package/server/services/execution/__init__.py +77 -0
- package/server/services/execution/cache.py +769 -0
- package/server/services/execution/conditions.py +373 -0
- package/server/services/execution/dlq.py +132 -0
- package/server/services/execution/executor.py +1351 -0
- package/server/services/execution/models.py +531 -0
- package/server/services/execution/recovery.py +235 -0
- package/server/services/handlers/__init__.py +126 -0
- package/server/services/handlers/ai.py +355 -0
- package/server/services/handlers/android.py +260 -0
- package/server/services/handlers/code.py +278 -0
- package/server/services/handlers/document.py +598 -0
- package/server/services/handlers/http.py +193 -0
- package/server/services/handlers/polyglot.py +105 -0
- package/server/services/handlers/tools.py +845 -0
- package/server/services/handlers/triggers.py +107 -0
- package/server/services/handlers/utility.py +822 -0
- package/server/services/handlers/whatsapp.py +476 -0
- package/server/services/maps.py +289 -0
- package/server/services/memory_store.py +103 -0
- package/server/services/node_executor.py +375 -0
- package/server/services/parameter_resolver.py +218 -0
- package/server/services/polyglot_client.py +169 -0
- package/server/services/scheduler.py +155 -0
- package/server/services/skill_loader.py +417 -0
- package/server/services/status_broadcaster.py +826 -0
- package/server/services/temporal/__init__.py +23 -0
- package/server/services/temporal/activities.py +344 -0
- package/server/services/temporal/client.py +76 -0
- package/server/services/temporal/executor.py +147 -0
- package/server/services/temporal/worker.py +251 -0
- package/server/services/temporal/workflow.py +355 -0
- package/server/services/temporal/ws_client.py +236 -0
- package/server/services/text.py +111 -0
- package/server/services/user_auth.py +172 -0
- package/server/services/websocket_client.py +29 -0
- package/server/services/workflow.py +597 -0
- package/server/skills/android-skill/SKILL.md +82 -0
- package/server/skills/assistant-personality/SKILL.md +45 -0
- package/server/skills/code-skill/SKILL.md +140 -0
- package/server/skills/http-skill/SKILL.md +161 -0
- package/server/skills/maps-skill/SKILL.md +170 -0
- package/server/skills/memory-skill/SKILL.md +154 -0
- package/server/skills/scheduler-skill/SKILL.md +84 -0
- package/server/skills/whatsapp-skill/SKILL.md +283 -0
- package/server/uv.lock +2916 -0
- package/server/whatsapp-rpc/.dockerignore +30 -0
- package/server/whatsapp-rpc/Dockerfile +44 -0
- package/server/whatsapp-rpc/Dockerfile.web +17 -0
- package/server/whatsapp-rpc/README.md +139 -0
- package/server/whatsapp-rpc/cli.js +95 -0
- package/server/whatsapp-rpc/configs/config.yaml +7 -0
- package/server/whatsapp-rpc/docker-compose.yml +35 -0
- package/server/whatsapp-rpc/docs/API.md +410 -0
- package/server/whatsapp-rpc/go.mod +67 -0
- package/server/whatsapp-rpc/go.sum +203 -0
- package/server/whatsapp-rpc/package.json +30 -0
- package/server/whatsapp-rpc/schema.json +1294 -0
- package/server/whatsapp-rpc/scripts/clean.cjs +66 -0
- package/server/whatsapp-rpc/scripts/cli.js +162 -0
- package/server/whatsapp-rpc/src/go/cmd/server/main.go +91 -0
- package/server/whatsapp-rpc/src/go/config/config.go +49 -0
- package/server/whatsapp-rpc/src/go/rpc/rpc.go +446 -0
- package/server/whatsapp-rpc/src/go/rpc/server.go +112 -0
- package/server/whatsapp-rpc/src/go/whatsapp/history.go +166 -0
- package/server/whatsapp-rpc/src/go/whatsapp/messages.go +390 -0
- package/server/whatsapp-rpc/src/go/whatsapp/service.go +2130 -0
- package/server/whatsapp-rpc/src/go/whatsapp/types.go +261 -0
- package/server/whatsapp-rpc/src/python/pyproject.toml +15 -0
- package/server/whatsapp-rpc/src/python/whatsapp_rpc/__init__.py +4 -0
- package/server/whatsapp-rpc/src/python/whatsapp_rpc/client.py +427 -0
- package/server/whatsapp-rpc/web/app.py +609 -0
- package/server/whatsapp-rpc/web/requirements.txt +6 -0
- package/server/whatsapp-rpc/web/rpc_client.py +427 -0
- package/server/whatsapp-rpc/web/static/openapi.yaml +59 -0
- package/server/whatsapp-rpc/web/templates/base.html +150 -0
- package/server/whatsapp-rpc/web/templates/contacts.html +240 -0
- package/server/whatsapp-rpc/web/templates/dashboard.html +320 -0
- package/server/whatsapp-rpc/web/templates/groups.html +328 -0
- package/server/whatsapp-rpc/web/templates/messages.html +465 -0
- package/server/whatsapp-rpc/web/templates/messaging.html +681 -0
- package/server/whatsapp-rpc/web/templates/send.html +259 -0
- package/server/whatsapp-rpc/web/templates/settings.html +459 -0
package/scripts/build.js
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Cross-platform build script for MachinaOS.
|
|
4
|
+
* Works on: Windows, macOS, Linux, WSL, Git Bash
|
|
5
|
+
*/
|
|
6
|
+
import { execSync } from 'child_process';
|
|
7
|
+
import { existsSync, copyFileSync } from 'fs';
|
|
8
|
+
import { resolve, dirname } from 'path';
|
|
9
|
+
import { fileURLToPath } from 'url';
|
|
10
|
+
|
|
11
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
12
|
+
const ROOT = resolve(__dirname, '..');
|
|
13
|
+
|
|
14
|
+
// Ensure Python UTF-8 encoding
|
|
15
|
+
process.env.PYTHONUTF8 = '1';
|
|
16
|
+
|
|
17
|
+
function run(cmd, cwd = ROOT) {
|
|
18
|
+
execSync(cmd, { cwd, stdio: 'inherit', shell: true });
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function check(cmd) {
|
|
22
|
+
try {
|
|
23
|
+
execSync(cmd, { stdio: 'pipe', shell: true });
|
|
24
|
+
return true;
|
|
25
|
+
} catch {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function getVersion(cmd) {
|
|
31
|
+
try {
|
|
32
|
+
return execSync(cmd, { encoding: 'utf-8', shell: true }).trim();
|
|
33
|
+
} catch {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function log(step, msg) {
|
|
39
|
+
console.log(`[${step}] ${msg}`);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function npmInstall(cwd = ROOT) {
|
|
43
|
+
// Try npm ci first (fast), fall back to npm install if lock file out of sync
|
|
44
|
+
try {
|
|
45
|
+
execSync('npm ci', { cwd, stdio: 'pipe', shell: true });
|
|
46
|
+
} catch {
|
|
47
|
+
execSync('npm install', { cwd, stdio: 'inherit', shell: true });
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// ============================================================================
|
|
52
|
+
// Check Dependencies
|
|
53
|
+
// ============================================================================
|
|
54
|
+
|
|
55
|
+
console.log('Checking dependencies...\n');
|
|
56
|
+
|
|
57
|
+
const missing = [];
|
|
58
|
+
|
|
59
|
+
// Node.js
|
|
60
|
+
const nodeVersion = getVersion('node --version');
|
|
61
|
+
if (nodeVersion) {
|
|
62
|
+
console.log(` Node.js: ${nodeVersion}`);
|
|
63
|
+
} else {
|
|
64
|
+
missing.push('Node.js - https://nodejs.org/');
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// npm
|
|
68
|
+
const npmVersion = getVersion('npm --version');
|
|
69
|
+
if (npmVersion) {
|
|
70
|
+
console.log(` npm: ${npmVersion}`);
|
|
71
|
+
} else {
|
|
72
|
+
missing.push('npm - comes with Node.js');
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Python
|
|
76
|
+
let pyCmd = null;
|
|
77
|
+
if (check('python --version')) {
|
|
78
|
+
pyCmd = 'python';
|
|
79
|
+
} else if (check('python3 --version')) {
|
|
80
|
+
pyCmd = 'python3';
|
|
81
|
+
}
|
|
82
|
+
if (pyCmd) {
|
|
83
|
+
const pyVersion = getVersion(`${pyCmd} --version`);
|
|
84
|
+
console.log(` ${pyVersion}`);
|
|
85
|
+
} else {
|
|
86
|
+
missing.push('Python 3.11+ - https://python.org/');
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// uv (Python package manager)
|
|
90
|
+
const uvVersion = getVersion('uv --version');
|
|
91
|
+
if (uvVersion) {
|
|
92
|
+
console.log(` uv: ${uvVersion}`);
|
|
93
|
+
} else {
|
|
94
|
+
missing.push('uv - https://docs.astral.sh/uv/getting-started/installation/');
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Go (required for WhatsApp server)
|
|
98
|
+
const goVersionFull = getVersion('go version');
|
|
99
|
+
if (goVersionFull) {
|
|
100
|
+
const goVersion = goVersionFull.match(/go\d+\.\d+(\.\d+)?/)?.[0] || 'go';
|
|
101
|
+
console.log(` Go: ${goVersion}`);
|
|
102
|
+
} else {
|
|
103
|
+
missing.push('Go - https://go.dev/dl/');
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (missing.length > 0) {
|
|
107
|
+
console.error('\nMissing required dependencies:\n');
|
|
108
|
+
for (const dep of missing) {
|
|
109
|
+
console.error(` - ${dep}`);
|
|
110
|
+
}
|
|
111
|
+
console.error('\nPlease install the missing dependencies and try again.');
|
|
112
|
+
process.exit(1);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
console.log('\nAll dependencies found.\n');
|
|
116
|
+
|
|
117
|
+
// ============================================================================
|
|
118
|
+
// Build
|
|
119
|
+
// ============================================================================
|
|
120
|
+
|
|
121
|
+
try {
|
|
122
|
+
// Step 0: Create .env if not exists
|
|
123
|
+
const envPath = resolve(ROOT, '.env');
|
|
124
|
+
const templatePath = resolve(ROOT, '.env.template');
|
|
125
|
+
if (!existsSync(envPath) && existsSync(templatePath)) {
|
|
126
|
+
copyFileSync(templatePath, envPath);
|
|
127
|
+
log('0/5', 'Created .env from template');
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Step 1: Install root dependencies
|
|
131
|
+
log('1/5', 'Installing root dependencies...');
|
|
132
|
+
npmInstall(ROOT);
|
|
133
|
+
|
|
134
|
+
// Step 2: Build client
|
|
135
|
+
log('2/5', 'Building client...');
|
|
136
|
+
run('npm run build', resolve(ROOT, 'client'));
|
|
137
|
+
|
|
138
|
+
// Step 3: Install Python dependencies
|
|
139
|
+
log('3/5', 'Installing Python dependencies...');
|
|
140
|
+
const serverDir = resolve(ROOT, 'server');
|
|
141
|
+
run('uv venv', serverDir);
|
|
142
|
+
run('uv sync', serverDir);
|
|
143
|
+
|
|
144
|
+
// Step 4: Install WhatsApp dependencies
|
|
145
|
+
log('4/5', 'Installing WhatsApp dependencies...');
|
|
146
|
+
const whatsappDir = resolve(ROOT, 'server/whatsapp-rpc');
|
|
147
|
+
npmInstall(whatsappDir);
|
|
148
|
+
|
|
149
|
+
// Step 5: Build WhatsApp server (Go binary)
|
|
150
|
+
log('5/5', 'Building WhatsApp server...');
|
|
151
|
+
run('npm run build', whatsappDir);
|
|
152
|
+
|
|
153
|
+
console.log('\nBuild complete.');
|
|
154
|
+
|
|
155
|
+
} catch (err) {
|
|
156
|
+
console.error('\nBuild failed:', err.message);
|
|
157
|
+
process.exit(1);
|
|
158
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Check port status - shows real vs orphaned TCP entries
|
|
2
|
+
param(
|
|
3
|
+
[int[]]$Ports = @(3000, 3010, 9400)
|
|
4
|
+
)
|
|
5
|
+
|
|
6
|
+
Write-Host "Checking port status...`n"
|
|
7
|
+
|
|
8
|
+
foreach ($port in $Ports) {
|
|
9
|
+
Write-Host "=== Port $port ===" -ForegroundColor Cyan
|
|
10
|
+
$connections = Get-NetTCPConnection -LocalPort $port -ErrorAction SilentlyContinue
|
|
11
|
+
|
|
12
|
+
if (-not $connections) {
|
|
13
|
+
Write-Host " No connections" -ForegroundColor Green
|
|
14
|
+
continue
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
$grouped = $connections | Group-Object State
|
|
18
|
+
foreach ($group in $grouped) {
|
|
19
|
+
Write-Host " $($group.Name): $($group.Count) connection(s)"
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
$processIds = $connections | Select-Object -ExpandProperty OwningProcess -Unique | Where-Object { $_ -ne 0 }
|
|
23
|
+
foreach ($procId in $processIds) {
|
|
24
|
+
$proc = Get-Process -Id $procId -ErrorAction SilentlyContinue
|
|
25
|
+
if ($proc) {
|
|
26
|
+
Write-Host " REAL: PID $procId ($($proc.ProcessName))" -ForegroundColor Yellow
|
|
27
|
+
} else {
|
|
28
|
+
Write-Host " ORPHAN: PID $procId (process dead, TCP entry will timeout)" -ForegroundColor DarkGray
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
Write-Host "`nNote: Orphaned entries auto-clear in 30-120 seconds. They don't block new processes."
|
package/scripts/clean.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Cross-platform clean script using Node.js native APIs.
|
|
4
|
+
* Works on: Windows, macOS, Linux, WSL, Git Bash
|
|
5
|
+
*/
|
|
6
|
+
import { rmSync, existsSync } from 'fs';
|
|
7
|
+
import { resolve, dirname } from 'path';
|
|
8
|
+
import { fileURLToPath } from 'url';
|
|
9
|
+
|
|
10
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
11
|
+
const ROOT = resolve(__dirname, '..');
|
|
12
|
+
|
|
13
|
+
const targets = [
|
|
14
|
+
'node_modules',
|
|
15
|
+
'client/node_modules',
|
|
16
|
+
'client/dist',
|
|
17
|
+
'client/.vite',
|
|
18
|
+
'server/whatsapp-rpc/node_modules',
|
|
19
|
+
'server/whatsapp-rpc/bin',
|
|
20
|
+
'server/whatsapp-rpc/data',
|
|
21
|
+
'server/data',
|
|
22
|
+
'server/.venv',
|
|
23
|
+
];
|
|
24
|
+
|
|
25
|
+
console.log('Cleaning...');
|
|
26
|
+
|
|
27
|
+
for (const target of targets) {
|
|
28
|
+
const fullPath = resolve(ROOT, target);
|
|
29
|
+
if (existsSync(fullPath)) {
|
|
30
|
+
console.log(` Removing: ${target}`);
|
|
31
|
+
try {
|
|
32
|
+
// Node.js native rmSync - works cross-platform
|
|
33
|
+
rmSync(fullPath, { recursive: true, force: true, maxRetries: 3, retryDelay: 100 });
|
|
34
|
+
} catch (err) {
|
|
35
|
+
console.log(` Warning: Could not remove ${target}: ${err.message}`);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
console.log('Done.');
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Docker compose wrapper that auto-enables Redis profile based on REDIS_ENABLED in .env
|
|
4
|
+
* Usage: node scripts/docker.js <command> [args...]
|
|
5
|
+
* Commands: up, down, build, logs, restart
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { spawn } from 'child_process';
|
|
9
|
+
import { readFileSync, existsSync, copyFileSync } from 'fs';
|
|
10
|
+
import { fileURLToPath } from 'url';
|
|
11
|
+
import { dirname, resolve } from 'path';
|
|
12
|
+
|
|
13
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
14
|
+
const ROOT = resolve(__dirname, '..');
|
|
15
|
+
|
|
16
|
+
// Create .env from template if it doesn't exist
|
|
17
|
+
function ensureEnvFile() {
|
|
18
|
+
const envPath = resolve(ROOT, '.env');
|
|
19
|
+
if (existsSync(envPath)) return true;
|
|
20
|
+
|
|
21
|
+
const templatePath = resolve(ROOT, '.env.template');
|
|
22
|
+
if (existsSync(templatePath)) {
|
|
23
|
+
console.log('[Docker] Creating .env from .env.template');
|
|
24
|
+
copyFileSync(templatePath, envPath);
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
console.warn('[Docker] Warning: No .env or .env.template found');
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Read .env file and check REDIS_ENABLED
|
|
33
|
+
function isRedisEnabled() {
|
|
34
|
+
const envPath = resolve(ROOT, '.env');
|
|
35
|
+
if (!existsSync(envPath)) return false;
|
|
36
|
+
|
|
37
|
+
const content = readFileSync(envPath, 'utf8');
|
|
38
|
+
const match = content.match(/^REDIS_ENABLED\s*=\s*(.+)$/m);
|
|
39
|
+
if (!match) return false;
|
|
40
|
+
|
|
41
|
+
const value = match[1].trim().toLowerCase();
|
|
42
|
+
return value === 'true' || value === '1' || value === 'yes';
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Get command and args
|
|
46
|
+
const [,, command, ...args] = process.argv;
|
|
47
|
+
|
|
48
|
+
if (!command) {
|
|
49
|
+
console.error('Usage: node scripts/docker.js <command> [args...]');
|
|
50
|
+
console.error('Commands: up, down, build, logs, restart');
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Ensure .env exists (create from template if needed)
|
|
55
|
+
ensureEnvFile();
|
|
56
|
+
|
|
57
|
+
// Build docker-compose command
|
|
58
|
+
const composeArgs = [];
|
|
59
|
+
|
|
60
|
+
// Add Redis profile if enabled in .env
|
|
61
|
+
if (isRedisEnabled()) {
|
|
62
|
+
composeArgs.push('--profile', 'redis');
|
|
63
|
+
console.log('[Docker] Redis profile enabled (REDIS_ENABLED=true in .env)');
|
|
64
|
+
} else {
|
|
65
|
+
console.log('[Docker] Redis profile disabled (REDIS_ENABLED=false in .env)');
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Add command
|
|
69
|
+
composeArgs.push(command);
|
|
70
|
+
|
|
71
|
+
// Add command-specific defaults
|
|
72
|
+
if (command === 'up') {
|
|
73
|
+
composeArgs.push('-d'); // detached by default
|
|
74
|
+
}
|
|
75
|
+
if (command === 'logs') {
|
|
76
|
+
composeArgs.push('-f'); // follow by default
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Add any additional args
|
|
80
|
+
composeArgs.push(...args);
|
|
81
|
+
|
|
82
|
+
console.log(`[Docker] Running: docker-compose ${composeArgs.join(' ')}`);
|
|
83
|
+
|
|
84
|
+
// Run docker-compose
|
|
85
|
+
const proc = spawn('docker-compose', composeArgs, {
|
|
86
|
+
cwd: ROOT,
|
|
87
|
+
stdio: 'inherit',
|
|
88
|
+
shell: true
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
proc.on('exit', (code) => {
|
|
92
|
+
process.exit(code || 0);
|
|
93
|
+
});
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
# Kill all processes using the specified ports (Windows)
|
|
2
|
+
# Usage: .\kill-port.ps1 [-Ports] 3000,3010,9400
|
|
3
|
+
# If no ports specified, uses default MachinaOs ports: 3000, 3010, 9400
|
|
4
|
+
|
|
5
|
+
param(
|
|
6
|
+
[int[]]$Ports = @(3000, 3010, 9400)
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
$ErrorActionPreference = 'SilentlyContinue'
|
|
10
|
+
|
|
11
|
+
function Get-ProcessTree {
|
|
12
|
+
param([int]$ParentId)
|
|
13
|
+
|
|
14
|
+
$children = @()
|
|
15
|
+
$childProcesses = Get-CimInstance Win32_Process -Filter "ParentProcessId=$ParentId" -ErrorAction SilentlyContinue
|
|
16
|
+
|
|
17
|
+
foreach ($child in $childProcesses) {
|
|
18
|
+
if ($child.ProcessId -and $child.ProcessId -ne 0) {
|
|
19
|
+
$children += $child.ProcessId
|
|
20
|
+
# Recursively get grandchildren
|
|
21
|
+
$children += Get-ProcessTree -ParentId $child.ProcessId
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return $children
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function Get-PortProcesses {
|
|
29
|
+
param([int]$Port)
|
|
30
|
+
|
|
31
|
+
$pids = @()
|
|
32
|
+
|
|
33
|
+
# Method 1: Get-NetTCPConnection (preferred)
|
|
34
|
+
$connections = Get-NetTCPConnection -LocalPort $Port -ErrorAction SilentlyContinue |
|
|
35
|
+
Where-Object { $_.State -eq 'Listen' -or $_.State -eq 'Established' }
|
|
36
|
+
|
|
37
|
+
if ($connections) {
|
|
38
|
+
$pids += $connections | Select-Object -ExpandProperty OwningProcess -Unique | Where-Object { $_ -ne 0 }
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
# Method 2: netstat fallback
|
|
42
|
+
if ($pids.Count -eq 0) {
|
|
43
|
+
$netstatOutput = netstat -ano 2>$null | Select-String ":$Port\s" | Select-String "LISTENING|ESTABLISHED"
|
|
44
|
+
foreach ($line in $netstatOutput) {
|
|
45
|
+
if ($line -match '\s+(\d+)\s*$') {
|
|
46
|
+
$pid = [int]$Matches[1]
|
|
47
|
+
if ($pid -ne 0 -and $pids -notcontains $pid) {
|
|
48
|
+
$pids += $pid
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return $pids | Select-Object -Unique
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function Stop-ProcessSafely {
|
|
58
|
+
param([int]$ProcessId)
|
|
59
|
+
|
|
60
|
+
$proc = Get-Process -Id $ProcessId -ErrorAction SilentlyContinue
|
|
61
|
+
if (-not $proc) {
|
|
62
|
+
return $false
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
$procName = $proc.ProcessName
|
|
66
|
+
|
|
67
|
+
# Try graceful stop first
|
|
68
|
+
Stop-Process -Id $ProcessId -ErrorAction SilentlyContinue
|
|
69
|
+
Start-Sleep -Milliseconds 200
|
|
70
|
+
|
|
71
|
+
# Check if still running
|
|
72
|
+
$proc = Get-Process -Id $ProcessId -ErrorAction SilentlyContinue
|
|
73
|
+
if ($proc) {
|
|
74
|
+
# Force kill
|
|
75
|
+
Stop-Process -Id $ProcessId -Force -ErrorAction SilentlyContinue
|
|
76
|
+
Start-Sleep -Milliseconds 200
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
# Verify killed
|
|
80
|
+
$proc = Get-Process -Id $ProcessId -ErrorAction SilentlyContinue
|
|
81
|
+
return (-not $proc)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
Write-Host "Stopping MachinaOs services..." -ForegroundColor Cyan
|
|
85
|
+
Write-Host "Platform: Windows"
|
|
86
|
+
Write-Host "Ports: $($Ports -join ', ')`n"
|
|
87
|
+
|
|
88
|
+
$allStopped = $true
|
|
89
|
+
|
|
90
|
+
foreach ($port in $Ports) {
|
|
91
|
+
$portPids = Get-PortProcesses -Port $port
|
|
92
|
+
|
|
93
|
+
if ($portPids.Count -eq 0) {
|
|
94
|
+
Write-Host "[OK] Port $port`: Free" -ForegroundColor Green
|
|
95
|
+
continue
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
# Collect all PIDs including child processes
|
|
99
|
+
$allPids = @()
|
|
100
|
+
foreach ($pid in $portPids) {
|
|
101
|
+
$allPids += $pid
|
|
102
|
+
$children = Get-ProcessTree -ParentId $pid
|
|
103
|
+
$allPids += $children
|
|
104
|
+
}
|
|
105
|
+
$allPids = $allPids | Select-Object -Unique | Where-Object { $_ -ne 0 }
|
|
106
|
+
|
|
107
|
+
$killedPids = @()
|
|
108
|
+
$failedPids = @()
|
|
109
|
+
|
|
110
|
+
# Kill all processes (children first, then parents)
|
|
111
|
+
$sortedPids = $allPids | Sort-Object -Descending
|
|
112
|
+
|
|
113
|
+
foreach ($pid in $sortedPids) {
|
|
114
|
+
$proc = Get-Process -Id $pid -ErrorAction SilentlyContinue
|
|
115
|
+
if ($proc) {
|
|
116
|
+
$procName = $proc.ProcessName
|
|
117
|
+
$killed = Stop-ProcessSafely -ProcessId $pid
|
|
118
|
+
if ($killed) {
|
|
119
|
+
$killedPids += "$pid ($procName)"
|
|
120
|
+
} else {
|
|
121
|
+
$failedPids += "$pid ($procName)"
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
# Verify port is free
|
|
127
|
+
Start-Sleep -Milliseconds 500
|
|
128
|
+
$remainingPids = Get-PortProcesses -Port $port
|
|
129
|
+
|
|
130
|
+
if ($remainingPids.Count -eq 0) {
|
|
131
|
+
if ($killedPids.Count -gt 0) {
|
|
132
|
+
Write-Host "[OK] Port $port`: Killed $($killedPids.Count) process(es)" -ForegroundColor Green
|
|
133
|
+
Write-Host " PIDs: $($killedPids -join ', ')" -ForegroundColor DarkGray
|
|
134
|
+
} else {
|
|
135
|
+
Write-Host "[OK] Port $port`: Free" -ForegroundColor Green
|
|
136
|
+
}
|
|
137
|
+
} else {
|
|
138
|
+
Write-Host "[!!] Port $port`: Warning - still in use" -ForegroundColor Yellow
|
|
139
|
+
if ($killedPids.Count -gt 0) {
|
|
140
|
+
Write-Host " Killed: $($killedPids -join ', ')" -ForegroundColor DarkGray
|
|
141
|
+
}
|
|
142
|
+
Write-Host " Still running: $($remainingPids -join ', ')" -ForegroundColor Red
|
|
143
|
+
$allStopped = $false
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
Write-Host ""
|
|
148
|
+
if ($allStopped) {
|
|
149
|
+
Write-Host "All services stopped successfully." -ForegroundColor Green
|
|
150
|
+
} else {
|
|
151
|
+
Write-Host "Warning: Some ports may still be in use." -ForegroundColor Yellow
|
|
152
|
+
Write-Host "Try running as Administrator or manually kill the processes." -ForegroundColor Yellow
|
|
153
|
+
exit 1
|
|
154
|
+
}
|
package/scripts/start.js
ADDED
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Cross-platform start script for MachinaOS services.
|
|
4
|
+
* Works on: Windows, macOS, Linux, WSL, Git Bash
|
|
5
|
+
*/
|
|
6
|
+
import { execSync, spawn } from 'child_process';
|
|
7
|
+
import { readFileSync, existsSync, copyFileSync } from 'fs';
|
|
8
|
+
import { resolve, dirname } from 'path';
|
|
9
|
+
import { fileURLToPath } from 'url';
|
|
10
|
+
|
|
11
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
12
|
+
const ROOT = resolve(__dirname, '..');
|
|
13
|
+
const START_TIME = Date.now();
|
|
14
|
+
|
|
15
|
+
// ============================================================================
|
|
16
|
+
// Platform Detection
|
|
17
|
+
// ============================================================================
|
|
18
|
+
const isWindows = process.platform === 'win32';
|
|
19
|
+
const isGitBash = isWindows && (process.env.MSYSTEM || process.env.SHELL?.includes('bash'));
|
|
20
|
+
const isMac = process.platform === 'darwin';
|
|
21
|
+
const useUnixCommands = !isWindows || isGitBash;
|
|
22
|
+
|
|
23
|
+
function getPlatformName() {
|
|
24
|
+
if (isGitBash) return 'Git Bash';
|
|
25
|
+
if (isWindows) return 'Windows';
|
|
26
|
+
if (isMac) return 'macOS';
|
|
27
|
+
return 'Linux';
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// ============================================================================
|
|
31
|
+
// Utilities
|
|
32
|
+
// ============================================================================
|
|
33
|
+
|
|
34
|
+
const elapsed = () => `${((Date.now() - START_TIME) / 1000).toFixed(2)}s`;
|
|
35
|
+
const log = (msg) => console.log(`[${elapsed()}] ${msg}`);
|
|
36
|
+
|
|
37
|
+
function exec(cmd, options = {}) {
|
|
38
|
+
try {
|
|
39
|
+
return execSync(cmd, {
|
|
40
|
+
encoding: 'utf-8',
|
|
41
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
42
|
+
timeout: options.timeout || 10000,
|
|
43
|
+
cwd: ROOT,
|
|
44
|
+
...options
|
|
45
|
+
}).trim();
|
|
46
|
+
} catch {
|
|
47
|
+
return '';
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function sleep(ms) {
|
|
52
|
+
try {
|
|
53
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
54
|
+
} catch {
|
|
55
|
+
const end = Date.now() + ms;
|
|
56
|
+
while (Date.now() < end) { /* spin */ }
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function loadConfig() {
|
|
61
|
+
const envPath = existsSync(resolve(ROOT, '.env'))
|
|
62
|
+
? resolve(ROOT, '.env')
|
|
63
|
+
: resolve(ROOT, '.env.template');
|
|
64
|
+
|
|
65
|
+
const env = {};
|
|
66
|
+
if (existsSync(envPath)) {
|
|
67
|
+
for (const line of readFileSync(envPath, 'utf-8').split('\n')) {
|
|
68
|
+
const match = line.match(/^([^#=]+)=(.*)$/);
|
|
69
|
+
if (match) {
|
|
70
|
+
env[match[1].trim()] = match[2].trim().replace(/^["']|["']$/g, '');
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return {
|
|
76
|
+
ports: [
|
|
77
|
+
parseInt(env.VITE_CLIENT_PORT) || 3000,
|
|
78
|
+
parseInt(env.PYTHON_BACKEND_PORT) || 3010,
|
|
79
|
+
parseInt(env.WHATSAPP_RPC_PORT) || 9400
|
|
80
|
+
],
|
|
81
|
+
temporalEnabled: env.TEMPORAL_ENABLED?.toLowerCase() === 'true'
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// ============================================================================
|
|
86
|
+
// Port Management
|
|
87
|
+
// ============================================================================
|
|
88
|
+
|
|
89
|
+
function getPidsOnPort(port) {
|
|
90
|
+
const pids = new Set();
|
|
91
|
+
|
|
92
|
+
if (useUnixCommands) {
|
|
93
|
+
const output = exec(`lsof -ti:${port} -sTCP:LISTEN 2>/dev/null`);
|
|
94
|
+
for (const pid of output.split('\n')) {
|
|
95
|
+
if (pid.trim() && /^\d+$/.test(pid.trim())) {
|
|
96
|
+
pids.add(pid.trim());
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if (pids.size === 0 && !isMac && !isGitBash) {
|
|
100
|
+
const ssOutput = exec(`ss -tlnp 2>/dev/null | grep :${port}`);
|
|
101
|
+
for (const match of ssOutput.matchAll(/pid=(\d+)/g)) {
|
|
102
|
+
pids.add(match[1]);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
} else {
|
|
106
|
+
const output = exec(`netstat -ano | findstr :${port} | findstr LISTENING`);
|
|
107
|
+
for (const line of output.split('\n')) {
|
|
108
|
+
const parts = line.trim().split(/\s+/);
|
|
109
|
+
const pid = parts[parts.length - 1];
|
|
110
|
+
if (pid && /^\d+$/.test(pid) && pid !== '0') {
|
|
111
|
+
pids.add(pid);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return Array.from(pids);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function killPid(pid) {
|
|
120
|
+
if (useUnixCommands) {
|
|
121
|
+
exec(`kill -15 ${pid} 2>/dev/null`);
|
|
122
|
+
sleep(100);
|
|
123
|
+
exec(`kill -9 ${pid} 2>/dev/null`);
|
|
124
|
+
} else {
|
|
125
|
+
exec(`taskkill /PID ${pid} /F 2>nul`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function freePort(port) {
|
|
130
|
+
const pids = getPidsOnPort(port);
|
|
131
|
+
if (pids.length === 0) return true;
|
|
132
|
+
|
|
133
|
+
for (const pid of pids) {
|
|
134
|
+
killPid(pid);
|
|
135
|
+
}
|
|
136
|
+
sleep(500);
|
|
137
|
+
|
|
138
|
+
return getPidsOnPort(port).length === 0;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// ============================================================================
|
|
142
|
+
// Main
|
|
143
|
+
// ============================================================================
|
|
144
|
+
|
|
145
|
+
const config = loadConfig();
|
|
146
|
+
|
|
147
|
+
// Ensure Python UTF-8 encoding
|
|
148
|
+
process.env.PYTHONUTF8 = '1';
|
|
149
|
+
|
|
150
|
+
console.log('\n=== MachinaOS Starting ===\n');
|
|
151
|
+
log(`Platform: ${getPlatformName()}`);
|
|
152
|
+
log(`Ports: ${config.ports.join(', ')}`);
|
|
153
|
+
log(`Temporal: ${config.temporalEnabled ? 'enabled' : 'disabled'}`);
|
|
154
|
+
|
|
155
|
+
// Create .env if not exists
|
|
156
|
+
const envPath = resolve(ROOT, '.env');
|
|
157
|
+
const templatePath = resolve(ROOT, '.env.template');
|
|
158
|
+
if (!existsSync(envPath) && existsSync(templatePath)) {
|
|
159
|
+
copyFileSync(templatePath, envPath);
|
|
160
|
+
log('Created .env from template');
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Free ports
|
|
164
|
+
log('Freeing ports...');
|
|
165
|
+
let allFree = true;
|
|
166
|
+
for (const port of config.ports) {
|
|
167
|
+
const pids = getPidsOnPort(port);
|
|
168
|
+
if (pids.length > 0) {
|
|
169
|
+
const freed = freePort(port);
|
|
170
|
+
if (freed) {
|
|
171
|
+
log(` Port ${port}: Freed (killed PIDs: ${pids.join(', ')})`);
|
|
172
|
+
} else {
|
|
173
|
+
log(` Port ${port}: Warning - could not free`);
|
|
174
|
+
allFree = false;
|
|
175
|
+
}
|
|
176
|
+
} else {
|
|
177
|
+
log(` Port ${port}: Already free`);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if (!allFree) {
|
|
182
|
+
log('Warning: Some ports could not be freed. Services may fail to start.');
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Start dev server
|
|
186
|
+
log('Starting services...');
|
|
187
|
+
log('Press Ctrl+C to stop (use npm run stop to kill all services)\n');
|
|
188
|
+
|
|
189
|
+
const devCommand = config.temporalEnabled ? 'dev:temporal' : 'dev';
|
|
190
|
+
|
|
191
|
+
// Use spawn with shell:true for cross-platform compatibility
|
|
192
|
+
const proc = spawn('npm', ['run', devCommand], {
|
|
193
|
+
cwd: ROOT,
|
|
194
|
+
stdio: 'inherit',
|
|
195
|
+
shell: true,
|
|
196
|
+
env: { ...process.env, FORCE_COLOR: '1' }
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
// Handle Ctrl+C gracefully
|
|
200
|
+
process.on('SIGINT', () => {
|
|
201
|
+
proc.kill('SIGINT');
|
|
202
|
+
});
|
|
203
|
+
process.on('SIGTERM', () => {
|
|
204
|
+
proc.kill('SIGTERM');
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
proc.on('close', (code) => {
|
|
208
|
+
console.log('\nDev server stopped.');
|
|
209
|
+
process.exit(code || 0);
|
|
210
|
+
});
|