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
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Standalone clean script - no npm dependencies required
|
|
3
|
+
const { existsSync, unlinkSync, readdirSync, rmSync } = require('fs');
|
|
4
|
+
const { join } = require('path');
|
|
5
|
+
const { execSync } = require('child_process');
|
|
6
|
+
|
|
7
|
+
const ROOT = join(__dirname, '..');
|
|
8
|
+
const BIN = process.platform === 'win32' ? 'whatsapp-rpc-server.exe' : 'whatsapp-rpc-server';
|
|
9
|
+
const BIN_DIR = join(ROOT, 'bin');
|
|
10
|
+
|
|
11
|
+
const log = (msg, color) => {
|
|
12
|
+
const colors = { green: '\x1b[32m', blue: '\x1b[34m', yellow: '\x1b[33m', reset: '\x1b[0m' };
|
|
13
|
+
console.log(`${colors[color] || ''}${msg}${colors.reset}`);
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
// Kill process on port
|
|
17
|
+
const killPort = (port, name) => {
|
|
18
|
+
try {
|
|
19
|
+
if (process.platform === 'win32') {
|
|
20
|
+
execSync(`for /f "tokens=5" %a in ('netstat -aon ^| findstr :${port} ^| findstr LISTENING') do taskkill /F /PID %a`, { stdio: 'ignore', shell: 'cmd.exe' });
|
|
21
|
+
} else {
|
|
22
|
+
execSync(`lsof -ti:${port} | xargs kill -9 2>/dev/null || true`, { stdio: 'ignore' });
|
|
23
|
+
}
|
|
24
|
+
log(`${name} stopped`, 'green');
|
|
25
|
+
} catch { log(`${name} not running`, 'yellow'); }
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
log('Stopping all processes...', 'blue');
|
|
29
|
+
killPort(9400, 'API');
|
|
30
|
+
killPort(5000, 'Web');
|
|
31
|
+
|
|
32
|
+
// Remove binary
|
|
33
|
+
const bin = join(BIN_DIR, BIN);
|
|
34
|
+
if (existsSync(bin)) { unlinkSync(bin); log(`Removed ${BIN}`, 'green'); }
|
|
35
|
+
|
|
36
|
+
// Remove bin directory if empty
|
|
37
|
+
if (existsSync(BIN_DIR) && readdirSync(BIN_DIR).length === 0) {
|
|
38
|
+
rmSync(BIN_DIR, { recursive: true }); log('Removed bin/', 'green');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Remove entire data directory (database, QR codes, etc.)
|
|
42
|
+
const dataDir = join(ROOT, 'data');
|
|
43
|
+
if (existsSync(dataDir)) {
|
|
44
|
+
rmSync(dataDir, { recursive: true });
|
|
45
|
+
log('Removed data/', 'green');
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Remove any .db files in project root (legacy locations)
|
|
49
|
+
readdirSync(ROOT).filter(f => f.endsWith('.db') || f.endsWith('.db-wal') || f.endsWith('.db-shm')).forEach(f => {
|
|
50
|
+
unlinkSync(join(ROOT, f));
|
|
51
|
+
log(`Removed ${f}`, 'green');
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
// Remove node_modules
|
|
55
|
+
const nodeModules = join(ROOT, 'node_modules');
|
|
56
|
+
if (existsSync(nodeModules)) { rmSync(nodeModules, { recursive: true }); log('Removed node_modules/', 'green'); }
|
|
57
|
+
|
|
58
|
+
// Remove Python cache
|
|
59
|
+
const pycache = join(ROOT, 'web', '__pycache__');
|
|
60
|
+
if (existsSync(pycache)) { rmSync(pycache, { recursive: true }); log('Removed web/__pycache__/', 'green'); }
|
|
61
|
+
|
|
62
|
+
// Remove package-lock.json
|
|
63
|
+
const lockFile = join(ROOT, 'package-lock.json');
|
|
64
|
+
if (existsSync(lockFile)) { unlinkSync(lockFile); log('Removed package-lock.json', 'green'); }
|
|
65
|
+
|
|
66
|
+
log('Clean complete', 'green');
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { program } from 'commander';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
import { execa } from 'execa';
|
|
5
|
+
import killPort from 'kill-port';
|
|
6
|
+
import { Socket } from 'net';
|
|
7
|
+
import { execSync, spawn } from 'child_process';
|
|
8
|
+
import { existsSync, statSync, unlinkSync, readdirSync, rmSync } from 'fs';
|
|
9
|
+
import { dirname, join } from 'path';
|
|
10
|
+
import { fileURLToPath } from 'url';
|
|
11
|
+
|
|
12
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
13
|
+
const ROOT = join(__dirname, '..');
|
|
14
|
+
const API_PORT = 9400, WEB_PORT = 5000;
|
|
15
|
+
const BIN = process.platform === 'win32' ? 'whatsapp-rpc-server.exe' : 'whatsapp-rpc-server';
|
|
16
|
+
const BIN_DIR = join(ROOT, 'bin');
|
|
17
|
+
|
|
18
|
+
const log = (m, c = 'blue') => console.log(chalk[c](m));
|
|
19
|
+
const sleep = ms => new Promise(r => setTimeout(r, ms));
|
|
20
|
+
|
|
21
|
+
const portUp = port => new Promise(r => {
|
|
22
|
+
const s = new Socket();
|
|
23
|
+
s.setTimeout(2000);
|
|
24
|
+
s.on('connect', () => { s.destroy(); r(true); });
|
|
25
|
+
s.on('timeout', () => { s.destroy(); r(false); });
|
|
26
|
+
s.on('error', () => r(false));
|
|
27
|
+
s.connect(port, '127.0.0.1');
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
const wait = async (port, ms = 10000) => {
|
|
31
|
+
const t = Date.now();
|
|
32
|
+
while (Date.now() - t < ms) { if (await portUp(port)) return true; await sleep(500); }
|
|
33
|
+
return false;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const kill = async (port, name) => {
|
|
37
|
+
try { await killPort(port); log(`${name} stopped`, 'green'); }
|
|
38
|
+
catch { log(`${name} not running`, 'yellow'); }
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const py = () => {
|
|
42
|
+
try { execSync('python --version', { stdio: 'ignore' }); return 'python'; }
|
|
43
|
+
catch { try { execSync('python3 --version', { stdio: 'ignore' }); return 'python3'; } catch { return null; } }
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const hasGo = () => {
|
|
47
|
+
try { execSync('go version', { stdio: 'ignore' }); return true; }
|
|
48
|
+
catch { return false; }
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
async function api(foreground = false) {
|
|
52
|
+
if (await portUp(API_PORT)) { log(`API already on ${API_PORT}`, 'yellow'); return; }
|
|
53
|
+
const bin = join(BIN_DIR, BIN);
|
|
54
|
+
if (!existsSync(bin)) {
|
|
55
|
+
if (!hasGo()) {
|
|
56
|
+
log('Go is not installed. Please either:', 'red');
|
|
57
|
+
log(' 1. Install Go: https://go.dev/dl/', 'yellow');
|
|
58
|
+
log(' 2. Or run "npm run build" on a machine with Go installed', 'yellow');
|
|
59
|
+
log(' 3. Or copy the pre-built binary to: ' + bin, 'yellow');
|
|
60
|
+
process.exit(1);
|
|
61
|
+
}
|
|
62
|
+
log('Building...', 'yellow');
|
|
63
|
+
await build();
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (foreground) {
|
|
67
|
+
// Run in foreground - will receive Ctrl+C signals
|
|
68
|
+
const proc = spawn(bin, [], { cwd: ROOT, stdio: 'inherit' });
|
|
69
|
+
proc.on('close', (code) => process.exit(code || 0));
|
|
70
|
+
process.on('SIGINT', () => { proc.kill('SIGINT'); });
|
|
71
|
+
process.on('SIGTERM', () => { proc.kill('SIGTERM'); });
|
|
72
|
+
log(`API: ws://localhost:${API_PORT}/ws/rpc`, 'green');
|
|
73
|
+
} else {
|
|
74
|
+
// Run detached in background
|
|
75
|
+
spawn(bin, [], { cwd: ROOT, detached: true, stdio: 'ignore' }).unref();
|
|
76
|
+
if (await wait(API_PORT)) log(`API: ws://localhost:${API_PORT}/ws/rpc`, 'green');
|
|
77
|
+
else log('API failed to start', 'red');
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async function web() {
|
|
82
|
+
if (await portUp(WEB_PORT)) { log(`Web already on ${WEB_PORT}`, 'yellow'); return; }
|
|
83
|
+
const p = py(); if (!p) { log('Python not found', 'red'); return; }
|
|
84
|
+
if (!await portUp(API_PORT)) log('Warning: API not running', 'yellow');
|
|
85
|
+
spawn(p, ['app.py'], { cwd: join(ROOT, 'web'), detached: true, stdio: 'ignore' }).unref();
|
|
86
|
+
if (await wait(WEB_PORT)) log(`Web: http://localhost:${WEB_PORT}`, 'green');
|
|
87
|
+
else log('Web failed to start', 'red');
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
async function start() { await api(); await sleep(1000); await web(); }
|
|
91
|
+
async function stop() { await kill(API_PORT, 'API'); await kill(WEB_PORT, 'Web'); }
|
|
92
|
+
|
|
93
|
+
async function status() {
|
|
94
|
+
const a = await portUp(API_PORT), w = await portUp(WEB_PORT);
|
|
95
|
+
log(`API (${API_PORT}): ${a ? 'UP' : 'DOWN'}`, a ? 'green' : 'red');
|
|
96
|
+
log(`Web (${WEB_PORT}): ${w ? 'UP' : 'DOWN'}`, w ? 'green' : 'red');
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
async function build() {
|
|
100
|
+
if (!hasGo()) {
|
|
101
|
+
log('Go is not installed. Install from: https://go.dev/dl/', 'red');
|
|
102
|
+
process.exit(1);
|
|
103
|
+
}
|
|
104
|
+
const bin = join(BIN_DIR, BIN);
|
|
105
|
+
if (!existsSync(BIN_DIR)) { await execa('mkdir', ['-p', BIN_DIR]); }
|
|
106
|
+
if (existsSync(bin)) unlinkSync(bin);
|
|
107
|
+
await execa('go', ['build', '-o', bin, './src/go/cmd/server'], { cwd: ROOT, stdio: 'inherit' });
|
|
108
|
+
log(`Built: ${BIN} (${(statSync(bin).size / 1024 / 1024).toFixed(1)}MB)`, 'green');
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
async function clean() {
|
|
112
|
+
log('Stopping all processes...', 'blue');
|
|
113
|
+
await stop();
|
|
114
|
+
await sleep(1000);
|
|
115
|
+
|
|
116
|
+
// Remove binary
|
|
117
|
+
const bin = join(BIN_DIR, BIN);
|
|
118
|
+
if (existsSync(bin)) { unlinkSync(bin); log(`Removed ${BIN}`, 'green'); }
|
|
119
|
+
|
|
120
|
+
// Remove bin directory if empty
|
|
121
|
+
if (existsSync(BIN_DIR) && readdirSync(BIN_DIR).length === 0) {
|
|
122
|
+
rmSync(BIN_DIR, { recursive: true }); log('Removed bin/', 'green');
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Remove entire data directory (database, QR codes, etc.)
|
|
126
|
+
const dataDir = join(ROOT, 'data');
|
|
127
|
+
if (existsSync(dataDir)) {
|
|
128
|
+
rmSync(dataDir, { recursive: true });
|
|
129
|
+
log('Removed data/', 'green');
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Remove any .db files in project root (legacy locations)
|
|
133
|
+
readdirSync(ROOT).filter(f => f.endsWith('.db') || f.endsWith('.db-wal') || f.endsWith('.db-shm')).forEach(f => {
|
|
134
|
+
unlinkSync(join(ROOT, f));
|
|
135
|
+
log(`Removed ${f}`, 'green');
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
// Remove node_modules
|
|
139
|
+
const nodeModules = join(ROOT, 'node_modules');
|
|
140
|
+
if (existsSync(nodeModules)) { rmSync(nodeModules, { recursive: true }); log('Removed node_modules/', 'green'); }
|
|
141
|
+
|
|
142
|
+
// Remove Python cache
|
|
143
|
+
const pycache = join(ROOT, 'web', '__pycache__');
|
|
144
|
+
if (existsSync(pycache)) { rmSync(pycache, { recursive: true }); log('Removed web/__pycache__/', 'green'); }
|
|
145
|
+
|
|
146
|
+
// Remove package-lock.json
|
|
147
|
+
const lockFile = join(ROOT, 'package-lock.json');
|
|
148
|
+
if (existsSync(lockFile)) { unlinkSync(lockFile); log('Removed package-lock.json', 'green'); }
|
|
149
|
+
|
|
150
|
+
log('Clean complete', 'green');
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
program.name('wa').version('1.0.0');
|
|
154
|
+
program.command('start').description('Start all').action(start);
|
|
155
|
+
program.command('stop').description('Stop all').action(stop);
|
|
156
|
+
program.command('restart').description('Restart all').action(async () => { await stop(); await sleep(1000); await start(); });
|
|
157
|
+
program.command('status').description('Status').action(status);
|
|
158
|
+
program.command('api').description('Start API only').option('-f, --foreground', 'Run in foreground (receive signals)').action((opts) => api(opts.foreground));
|
|
159
|
+
program.command('web').description('Start Web only').action(web);
|
|
160
|
+
program.command('build').description('Build binary').action(build);
|
|
161
|
+
program.command('clean').description('Clean artifacts').action(clean);
|
|
162
|
+
program.parse();
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"context"
|
|
5
|
+
"fmt"
|
|
6
|
+
"net/http"
|
|
7
|
+
"os"
|
|
8
|
+
"os/signal"
|
|
9
|
+
"syscall"
|
|
10
|
+
"time"
|
|
11
|
+
|
|
12
|
+
"github.com/sirupsen/logrus"
|
|
13
|
+
|
|
14
|
+
"whatsapp-rpc/src/go/config"
|
|
15
|
+
server "whatsapp-rpc/src/go/rpc"
|
|
16
|
+
"whatsapp-rpc/src/go/whatsapp"
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
func main() {
|
|
20
|
+
// Initialize logger
|
|
21
|
+
logger := logrus.New()
|
|
22
|
+
logger.SetFormatter(&logrus.TextFormatter{
|
|
23
|
+
FullTimestamp: true,
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
// Load configuration
|
|
27
|
+
cfg, err := config.Load()
|
|
28
|
+
if err != nil {
|
|
29
|
+
logger.Fatalf("Failed to load config: %v", err)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Set log level (5 = Debug, 4 = Info)
|
|
33
|
+
if cfg.LogLevel >= 5 {
|
|
34
|
+
logger.SetLevel(logrus.DebugLevel)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
logger.Info("Starting WhatsApp WebSocket RPC Server")
|
|
38
|
+
|
|
39
|
+
// Initialize WhatsApp service
|
|
40
|
+
whatsappService, err := whatsapp.NewService(cfg.Database, logger)
|
|
41
|
+
if err != nil {
|
|
42
|
+
logger.Fatalf("Failed to initialize WhatsApp service: %v", err)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Auto-start if there's an existing session (no need to press Start button)
|
|
46
|
+
if whatsappService.HasExistingSession() {
|
|
47
|
+
logger.Info("Existing session found, auto-connecting...")
|
|
48
|
+
if err := whatsappService.Start(); err != nil {
|
|
49
|
+
logger.Warnf("Auto-start failed: %v (user can manually start)", err)
|
|
50
|
+
}
|
|
51
|
+
} else {
|
|
52
|
+
logger.Info("No existing session, waiting for user to start pairing")
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Create and start server (WebSocket RPC only)
|
|
56
|
+
srv := server.New(whatsappService, logger)
|
|
57
|
+
router := srv.SetupRoutes()
|
|
58
|
+
|
|
59
|
+
httpServer := &http.Server{
|
|
60
|
+
Addr: fmt.Sprintf(":%d", cfg.Server.Port),
|
|
61
|
+
Handler: router,
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Start server in goroutine
|
|
65
|
+
go func() {
|
|
66
|
+
logger.Infof("WebSocket RPC server listening on port %d", cfg.Server.Port)
|
|
67
|
+
logger.Infof("Connect via: ws://localhost:%d/ws/rpc", cfg.Server.Port)
|
|
68
|
+
if err := httpServer.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
|
69
|
+
logger.Fatalf("Server error: %v", err)
|
|
70
|
+
}
|
|
71
|
+
}()
|
|
72
|
+
|
|
73
|
+
// Wait for interrupt signal
|
|
74
|
+
quit := make(chan os.Signal, 1)
|
|
75
|
+
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
|
|
76
|
+
<-quit
|
|
77
|
+
|
|
78
|
+
logger.Info("Shutting down server...")
|
|
79
|
+
|
|
80
|
+
// Graceful shutdown
|
|
81
|
+
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
|
82
|
+
defer cancel()
|
|
83
|
+
|
|
84
|
+
whatsappService.Shutdown()
|
|
85
|
+
|
|
86
|
+
if err := httpServer.Shutdown(ctx); err != nil {
|
|
87
|
+
logger.Errorf("Server shutdown error: %v", err)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
logger.Info("Server stopped")
|
|
91
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
package config
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"github.com/spf13/viper"
|
|
5
|
+
)
|
|
6
|
+
|
|
7
|
+
type Config struct {
|
|
8
|
+
Environment string `mapstructure:"environment"`
|
|
9
|
+
LogLevel int `mapstructure:"log_level"`
|
|
10
|
+
Server ServerConfig `mapstructure:"server"`
|
|
11
|
+
Database DatabaseConfig `mapstructure:"database"`
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
type ServerConfig struct {
|
|
15
|
+
Port int `mapstructure:"port"`
|
|
16
|
+
Host string `mapstructure:"host"`
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
type DatabaseConfig struct {
|
|
20
|
+
Path string `mapstructure:"path"`
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
func Load() (*Config, error) {
|
|
24
|
+
viper.SetDefault("environment", "development")
|
|
25
|
+
viper.SetDefault("log_level", 4) // Info level
|
|
26
|
+
viper.SetDefault("server.port", 9400)
|
|
27
|
+
viper.SetDefault("server.host", "0.0.0.0")
|
|
28
|
+
viper.SetDefault("database.path", "data/whatsapp.db")
|
|
29
|
+
|
|
30
|
+
// Environment variables
|
|
31
|
+
viper.SetEnvPrefix("WA")
|
|
32
|
+
viper.AutomaticEnv()
|
|
33
|
+
|
|
34
|
+
// Config file
|
|
35
|
+
viper.SetConfigName("config")
|
|
36
|
+
viper.SetConfigType("yaml")
|
|
37
|
+
viper.AddConfigPath(".")
|
|
38
|
+
viper.AddConfigPath("./configs")
|
|
39
|
+
|
|
40
|
+
// Read config file (optional)
|
|
41
|
+
viper.ReadInConfig()
|
|
42
|
+
|
|
43
|
+
var config Config
|
|
44
|
+
if err := viper.Unmarshal(&config); err != nil {
|
|
45
|
+
return nil, err
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return &config, nil
|
|
49
|
+
}
|