create-openclaw-bot 5.7.10 → 5.8.0
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/README.md +159 -315
- package/README.vi.md +164 -315
- package/dist/cli.js +111 -2809
- package/dist/legacy-cli.js +2812 -0
- package/dist/server/local-server.js +2372 -0
- package/dist/setup/data/header.js +80 -80
- package/dist/setup/shared/bot-config-gen.js +469 -462
- package/dist/setup/shared/common-gen.js +313 -315
- package/dist/setup/shared/docker-gen.js +574 -500
- package/dist/setup/shared/install-gen.js +566 -566
- package/dist/setup/shared/workspace-gen.js +2 -1
- package/dist/setup.js +396 -204
- package/dist/web/app.js +1106 -0
- package/dist/web/bvvbank.jpg +0 -0
- package/dist/web/index.html +14 -0
- package/dist/web/momo.jpg +0 -0
- package/dist/web/openclaw-logo.png +0 -0
- package/dist/web/openclaw-logo.svg +1 -0
- package/dist/web/styles.css +607 -0
- package/package.json +3 -2
package/dist/setup.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
/* ============================================
|
|
2
|
-
OpenClaw Setup Wizard
|
|
2
|
+
OpenClaw Setup Wizard  Logic v2
|
|
3
3
|
Multi-model, Multi-plugin, Multi-channel
|
|
4
4
|
============================================ */
|
|
5
|
-
// AUTO-GENERATED by build.mjs
|
|
5
|
+
// AUTO-GENERATED by build.mjs  edit files in src/setup/ instead
|
|
6
6
|
|
|
7
7
|
(function () {
|
|
8
8
|
'use strict';
|
|
9
9
|
|
|
10
|
-
//
|
|
10
|
+
// ââ€â‚¬Ã¢â€â‚¬ Globals: CDN logos, state, shared utils (setup/data/header.js) ââ€â‚¬
|
|
11
11
|
// @ts-nocheck
|
|
12
12
|
/* eslint-disable no-undef, no-unused-vars */
|
|
13
13
|
/**
|
|
@@ -76,7 +76,7 @@
|
|
|
76
76
|
|| 'grammy @grammyjs/runner @grammyjs/transformer-throttler @buape/carbon @larksuiteoapi/node-sdk @slack/web-api';
|
|
77
77
|
|
|
78
78
|
function getGatewayAllowedOrigins(port) {
|
|
79
|
-
const normalizedPort = Number(port) ||
|
|
79
|
+
const normalizedPort = Number(port) || 18789;
|
|
80
80
|
const origins = new Set([
|
|
81
81
|
`http://localhost:${normalizedPort}`,
|
|
82
82
|
`http://127.0.0.1:${normalizedPort}`,
|
|
@@ -89,7 +89,7 @@
|
|
|
89
89
|
return Array.from(origins);
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
-
//
|
|
92
|
+
// ââ€â‚¬Ã¢â€â‚¬ PROVIDERS object (setup/data/providers.js) ââ€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬
|
|
93
93
|
// @ts-nocheck
|
|
94
94
|
/* eslint-disable no-undef, no-unused-vars */
|
|
95
95
|
/**
|
|
@@ -212,7 +212,7 @@
|
|
|
212
212
|
};
|
|
213
213
|
|
|
214
214
|
|
|
215
|
-
//
|
|
215
|
+
// ââ€â‚¬Ã¢â€â‚¬ CHANNELS, system prompts, security rules (setup/data/channels.js)
|
|
216
216
|
// @ts-nocheck
|
|
217
217
|
/* eslint-disable no-undef, no-unused-vars */
|
|
218
218
|
/**
|
|
@@ -383,7 +383,7 @@
|
|
|
383
383
|
- ✅ Limit exposed ports (only 38789)`,
|
|
384
384
|
};
|
|
385
385
|
|
|
386
|
-
//
|
|
386
|
+
// ââ€â‚¬Ã¢â€â‚¬ PLUGINS list (setup/data/plugins.js) ââ€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬
|
|
387
387
|
// @ts-nocheck
|
|
388
388
|
/* eslint-disable no-undef, no-unused-vars */
|
|
389
389
|
/**
|
|
@@ -445,7 +445,7 @@
|
|
|
445
445
|
];
|
|
446
446
|
|
|
447
447
|
|
|
448
|
-
//
|
|
448
|
+
// ââ€â‚¬Ã¢â€â‚¬ SKILLS list (setup/data/skills.js) ââ€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬
|
|
449
449
|
// @ts-nocheck
|
|
450
450
|
/* eslint-disable no-undef, no-unused-vars */
|
|
451
451
|
/**
|
|
@@ -616,10 +616,10 @@
|
|
|
616
616
|
}
|
|
617
617
|
|
|
618
618
|
|
|
619
|
-
//
|
|
619
|
+
// ââ€â‚¬Ã¢â€â‚¬ Shared runtime constants, relay helpers, auth profile builders (setup/shared/common-gen.js)
|
|
620
620
|
// @ts-nocheck
|
|
621
621
|
(function (root) {
|
|
622
|
-
const OPENCLAW_NPM_SPEC = 'openclaw@
|
|
622
|
+
const OPENCLAW_NPM_SPEC = 'openclaw@latest';
|
|
623
623
|
const OPENCLAW_RUNTIME_PACKAGES = 'grammy @grammyjs/runner @grammyjs/transformer-throttler @buape/carbon @larksuiteoapi/node-sdk @slack/web-api';
|
|
624
624
|
const NINE_ROUTER_NPM_SPEC = '9router@latest';
|
|
625
625
|
const NINE_ROUTER_PORT = 20128;
|
|
@@ -861,8 +861,9 @@ If setup reported a plugin install error, run this after the bot is running:
|
|
|
861
861
|
return JSON.stringify(buildAuthProfilesJson(options), null, 2);
|
|
862
862
|
}
|
|
863
863
|
|
|
864
|
-
function get9RouterBaseUrl(deployMode = 'native') {
|
|
865
|
-
|
|
864
|
+
function get9RouterBaseUrl(deployMode = 'native', routerPort) {
|
|
865
|
+
const port = routerPort || NINE_ROUTER_PORT;
|
|
866
|
+
return deployMode === 'docker' ? `http://9router:${port}/v1` : `http://localhost:${port}/v1`;
|
|
866
867
|
}
|
|
867
868
|
|
|
868
869
|
function build9RouterProviderConfig(baseUrl = `${NINE_ROUTER_API_BASE_URL}/v1`) {
|
|
@@ -870,6 +871,9 @@ If setup reported a plugin install error, run this after the bot is running:
|
|
|
870
871
|
baseUrl,
|
|
871
872
|
apiKey: NINE_ROUTER_PROXY_API_KEY,
|
|
872
873
|
api: 'openai-completions',
|
|
874
|
+
request: {
|
|
875
|
+
allowPrivateNetwork: true,
|
|
876
|
+
},
|
|
873
877
|
models: [
|
|
874
878
|
{
|
|
875
879
|
id: 'smart-route',
|
|
@@ -877,18 +881,12 @@ If setup reported a plugin install error, run this after the bot is running:
|
|
|
877
881
|
contextWindow: 200000,
|
|
878
882
|
maxTokens: 8192,
|
|
879
883
|
},
|
|
880
|
-
...SUPPORTED_CODEX_MODELS.map((id) => ({
|
|
881
|
-
id,
|
|
882
|
-
name: `Codex ${id.slice(3).replace(/-/g, ' ').replace(/\b\w/g, (char) => char.toUpperCase())}`,
|
|
883
|
-
contextWindow: 200000,
|
|
884
|
-
maxTokens: 8192,
|
|
885
|
-
})),
|
|
886
884
|
],
|
|
887
885
|
};
|
|
888
886
|
}
|
|
889
887
|
|
|
890
|
-
function buildGatewayConfig(port =
|
|
891
|
-
const normalizedPort = Number(port) ||
|
|
888
|
+
function buildGatewayConfig(port = 18789, deployMode = 'native', allowedOrigins = [], osChoice = '') {
|
|
889
|
+
const normalizedPort = Number(port) || 18789;
|
|
892
890
|
const cfg = {
|
|
893
891
|
port: normalizedPort,
|
|
894
892
|
mode: 'local',
|
|
@@ -933,7 +931,7 @@ if (typeof exports !== 'undefined' && typeof globalThis !== 'undefined' && globa
|
|
|
933
931
|
Object.assign(exports, globalThis.__openclawCommon);
|
|
934
932
|
}
|
|
935
933
|
|
|
936
|
-
//
|
|
934
|
+
// ââ€â‚¬Ã¢â€â‚¬ Shared workspace file builders (IDENTITY, SOUL, AGENTS, TOOLS, TEAMS...) (setup/shared/workspace-gen.js)
|
|
937
935
|
/** @typedef {typeof globalThis & { __openclawWorkspace?: Record<string, Function> }} OpenClawWorkspaceRoot */
|
|
938
936
|
|
|
939
937
|
const workspaceRoot = /** @type {OpenClawWorkspaceRoot} */ (
|
|
@@ -1187,8 +1185,9 @@ const CDP_URL = 'http://127.0.0.1:9222';
|
|
|
1187
1185
|
const { isVi = true, variant = 'wizard', workspaceRoot = '' } = options;
|
|
1188
1186
|
const wsRoot = workspaceRoot.replace(/\/+$/, '');
|
|
1189
1187
|
const btPath = wsRoot ? `${wsRoot}/browser-tool.js` : 'browser-tool.js';
|
|
1188
|
+
const modeHeading = variant === 'cli-server' ? '# Headless Server Mode\n\n' : '';
|
|
1190
1189
|
|
|
1191
|
-
return
|
|
1190
|
+
return `${modeHeading}# Navigation
|
|
1192
1191
|
node ${btPath} status
|
|
1193
1192
|
node ${btPath} open "https://google.com"
|
|
1194
1193
|
node ${btPath} get_url
|
|
@@ -1460,7 +1459,7 @@ if (typeof exports !== 'undefined' && workspaceRoot.__openclawWorkspace) {
|
|
|
1460
1459
|
Object.assign(exports, workspaceRoot.__openclawWorkspace);
|
|
1461
1460
|
}
|
|
1462
1461
|
|
|
1463
|
-
//
|
|
1462
|
+
// ââ€â‚¬Ã¢â€â‚¬ Centralized bot config builders (openclaw.json, exec-approvals, .env) (setup/shared/bot-config-gen.js)
|
|
1464
1463
|
// @ts-nocheck
|
|
1465
1464
|
/**
|
|
1466
1465
|
* @fileoverview Centralized bot configuration builders — single source of truth.
|
|
@@ -1514,7 +1513,7 @@ if (typeof exports !== 'undefined' && workspaceRoot.__openclawWorkspace) {
|
|
|
1514
1513
|
* @param {Array} opts.skills - Full SKILLS registry array
|
|
1515
1514
|
* @param {boolean} opts.hasBrowserDesktop - Browser desktop mode
|
|
1516
1515
|
* @param {boolean} opts.hasBrowserServer - Browser server mode
|
|
1517
|
-
* @param {number} [opts.gatewayPort=
|
|
1516
|
+
* @param {number} [opts.gatewayPort=18789]
|
|
1518
1517
|
* @param {Array} [opts.gatewayAllowedOrigins]
|
|
1519
1518
|
* @param {string} [opts.osChoice] - 'windows' | 'macos' | 'vps' | 'ubuntu'
|
|
1520
1519
|
* @param {string} [opts.selectedModel] - For Ollama: specific model selected
|
|
@@ -1533,10 +1532,11 @@ if (typeof exports !== 'undefined' && workspaceRoot.__openclawWorkspace) {
|
|
|
1533
1532
|
skills = [],
|
|
1534
1533
|
hasBrowserDesktop = false,
|
|
1535
1534
|
hasBrowserServer = false,
|
|
1536
|
-
gatewayPort =
|
|
1535
|
+
gatewayPort = 18789,
|
|
1537
1536
|
gatewayAllowedOrigins = [],
|
|
1538
1537
|
osChoice = '',
|
|
1539
1538
|
selectedModel = '',
|
|
1539
|
+
routerPort,
|
|
1540
1540
|
} = opts;
|
|
1541
1541
|
|
|
1542
1542
|
const common = _common;
|
|
@@ -1547,7 +1547,7 @@ if (typeof exports !== 'undefined' && workspaceRoot.__openclawWorkspace) {
|
|
|
1547
1547
|
const agentsList = agentMetas.map((meta) => ({
|
|
1548
1548
|
id: meta.agentId,
|
|
1549
1549
|
...(meta.name ? { name: meta.name } : {}),
|
|
1550
|
-
workspace:
|
|
1550
|
+
workspace: `/root/project/.openclaw/${meta.workspaceDir || 'workspace-' + meta.agentId}`,
|
|
1551
1551
|
agentDir: `agents/${meta.agentId}/agent`,
|
|
1552
1552
|
model: { primary: model, fallbacks: [] },
|
|
1553
1553
|
}));
|
|
@@ -1571,7 +1571,7 @@ if (typeof exports !== 'undefined' && workspaceRoot.__openclawWorkspace) {
|
|
|
1571
1571
|
mode: 'merge',
|
|
1572
1572
|
providers: {
|
|
1573
1573
|
'9router': common.build9RouterProviderConfig(
|
|
1574
|
-
common.get9RouterBaseUrl ? common.get9RouterBaseUrl(deployMode) :
|
|
1574
|
+
common.get9RouterBaseUrl ? common.get9RouterBaseUrl(deployMode, routerPort) : `http://9router:${routerPort || 20128}/v1`
|
|
1575
1575
|
),
|
|
1576
1576
|
},
|
|
1577
1577
|
};
|
|
@@ -1597,6 +1597,9 @@ if (typeof exports !== 'undefined' && workspaceRoot.__openclawWorkspace) {
|
|
|
1597
1597
|
|
|
1598
1598
|
// ── commands ──────────────────────────────────────────────────────────────
|
|
1599
1599
|
cfg.commands = { native: 'auto', nativeSkills: 'auto', restart: true, ownerDisplay: 'raw' };
|
|
1600
|
+
if (selectedSkills.includes('scheduler')) {
|
|
1601
|
+
cfg.commands.ownerAllowFrom = ['*'];
|
|
1602
|
+
}
|
|
1600
1603
|
|
|
1601
1604
|
// ── bindings (multi-bot or Zalo) ─────────────────────────────────────────
|
|
1602
1605
|
if (isMultiBot && channelKey === 'telegram') {
|
|
@@ -1614,6 +1617,9 @@ if (typeof exports !== 'undefined' && workspaceRoot.__openclawWorkspace) {
|
|
|
1614
1617
|
|
|
1615
1618
|
// ── tools ────────────────────────────────────────────────────────────────
|
|
1616
1619
|
cfg.tools = { profile: 'full', exec: { host: 'gateway', security: 'full', ask: 'off' } };
|
|
1620
|
+
if (selectedSkills.includes('scheduler')) {
|
|
1621
|
+
cfg.tools.alsoAllow = ['group:automation'];
|
|
1622
|
+
}
|
|
1617
1623
|
if (isMultiBot) {
|
|
1618
1624
|
cfg.tools.agentToAgent = {
|
|
1619
1625
|
enabled: true,
|
|
@@ -1924,7 +1930,7 @@ if (typeof exports !== 'undefined' && typeof globalThis !== 'undefined' && globa
|
|
|
1924
1930
|
Object.assign(exports, globalThis.__openclawBotConfig);
|
|
1925
1931
|
}
|
|
1926
1932
|
|
|
1927
|
-
//
|
|
1933
|
+
// ââ€â‚¬Ã¢â€â‚¬ Shared install artifacts: Chrome debug, uninstall, skill catalog (setup/shared/install-gen.js)
|
|
1928
1934
|
// @ts-nocheck
|
|
1929
1935
|
// install-gen.js — Build install/runtime artifacts (Chrome debug, uninstall, skill catalog)
|
|
1930
1936
|
// Workspace .md files are in workspace-gen.js (single source of truth).
|
|
@@ -2043,12 +2049,12 @@ fi
|
|
|
2043
2049
|
}
|
|
2044
2050
|
|
|
2045
2051
|
if (os === 'vps') {
|
|
2046
|
-
return { name: 'uninstall-openclaw-vps.sh', content: `#!/usr/bin/env bash\nset -e\nPROJECT_DIR="${absUnix}"\nAPP_NAME="${appName}"\necho ""\necho "============================================================"\necho " OpenClaw Uninstaller - VPS / Ubuntu Server"\necho " Project: $PROJECT_DIR"\necho " PM2 app: $APP_NAME"\necho "============================================================"\necho ""\nread -rp "Type YES to confirm full removal: " CONFIRM\nif [ "$CONFIRM" != "YES" ]; then echo "Cancelled."; exit 0; fi\necho "[1/5] Stopping PM2 processes..."\nif command -v pm2 &>/dev/null; then\n pm2 delete "$APP_NAME" "$APP_NAME-9router" "$APP_NAME-9router-sync" openclaw openclaw-multibot 2>/dev/null || true\n pm2 save --force 2>/dev/null || true\nfi\necho "[2/5] Killing leftover processes on ports
|
|
2052
|
+
return { name: 'uninstall-openclaw-vps.sh', content: `#!/usr/bin/env bash\nset -e\nPROJECT_DIR="${absUnix}"\nAPP_NAME="${appName}"\necho ""\necho "============================================================"\necho " OpenClaw Uninstaller - VPS / Ubuntu Server"\necho " Project: $PROJECT_DIR"\necho " PM2 app: $APP_NAME"\necho "============================================================"\necho ""\nread -rp "Type YES to confirm full removal: " CONFIRM\nif [ "$CONFIRM" != "YES" ]; then echo "Cancelled."; exit 0; fi\necho "[1/5] Stopping PM2 processes..."\nif command -v pm2 &>/dev/null; then\n pm2 delete "$APP_NAME" "$APP_NAME-9router" "$APP_NAME-9router-sync" openclaw openclaw-multibot 2>/dev/null || true\n pm2 save --force 2>/dev/null || true\nfi\necho "[2/5] Killing leftover processes on ports 18789 / 20128..."\nfor port in 18789 20128; do\n pid=$(lsof -ti tcp:$port 2>/dev/null || true)\n [ -n "$pid" ] && kill -9 $pid 2>/dev/null || true\ndone\necho "[3/5] Uninstalling npm packages..."\nnpm uninstall -g openclaw 9router pm2 grammy @grammyjs/runner @grammyjs/transformer-throttler @buape/carbon @larksuiteoapi/node-sdk @slack/web-api 2>/dev/null || true\necho "[4/5] Removing project directory..."\n[ -d "$PROJECT_DIR" ] && rm -rf "$PROJECT_DIR" && echo " OK: Deleted $PROJECT_DIR" || echo " INFO: Not found."\necho "[5/5] Checking home-level .9router / .openclaw..."\nfor dir in "$HOME/.9router" "$HOME/.openclaw"; do\n if [ -d "$dir" ]; then\n read -rp "Delete $dir ? [YES/no]: " CLEAN\n [ "$CLEAN" = "YES" ] && rm -rf "$dir" && echo " OK: Deleted $dir" || echo " Kept: $dir"\n fi\ndone\n` };
|
|
2047
2053
|
}
|
|
2048
2054
|
|
|
2049
2055
|
if (os === 'linux' || os === 'linux-desktop' || os === 'macos') {
|
|
2050
2056
|
const label = os === 'macos' ? 'macOS' : 'Linux Desktop';
|
|
2051
|
-
return { name: 'uninstall-openclaw.sh', content: `#!/usr/bin/env bash\nset -e\nPROJECT_DIR="${absUnix}"\necho ""\necho "============================================================"\necho " OpenClaw Uninstaller - ${label} Native"\necho " Project: $PROJECT_DIR"\necho "============================================================"\necho ""\nread -rp "Type YES to confirm full removal: " CONFIRM\nif [ "$CONFIRM" != "YES" ]; then echo "Cancelled."; exit 0; fi\necho "[1/4] Stopping openclaw and 9router processes..."\npkill -f "openclaw gateway run" 2>/dev/null || true\npkill -f "9router.*20128" 2>/dev/null || true\npkill -f "9router-smart-route" 2>/dev/null || true\npkill -f "$PROJECT_DIR" 2>/dev/null || true\nfor port in
|
|
2057
|
+
return { name: 'uninstall-openclaw.sh', content: `#!/usr/bin/env bash\nset -e\nPROJECT_DIR="${absUnix}"\necho ""\necho "============================================================"\necho " OpenClaw Uninstaller - ${label} Native"\necho " Project: $PROJECT_DIR"\necho "============================================================"\necho ""\nread -rp "Type YES to confirm full removal: " CONFIRM\nif [ "$CONFIRM" != "YES" ]; then echo "Cancelled."; exit 0; fi\necho "[1/4] Stopping openclaw and 9router processes..."\npkill -f "openclaw gateway run" 2>/dev/null || true\npkill -f "9router.*20128" 2>/dev/null || true\npkill -f "9router-smart-route" 2>/dev/null || true\npkill -f "$PROJECT_DIR" 2>/dev/null || true\nfor port in 18789 20128; do\n pid=$(lsof -ti tcp:$port 2>/dev/null || true)\n [ -n "$pid" ] && kill -9 $pid 2>/dev/null || true\ndone\necho "[2/4] Uninstalling npm packages..."\nnpm uninstall -g openclaw 9router grammy @grammyjs/runner @grammyjs/transformer-throttler @buape/carbon @larksuiteoapi/node-sdk @slack/web-api 2>/dev/null || true\nsudo npm uninstall -g openclaw 9router 2>/dev/null || true\necho "[3/4] Removing project directory..."\n[ -d "$PROJECT_DIR" ] && rm -rf "$PROJECT_DIR" && echo " OK: Deleted $PROJECT_DIR" || echo " INFO: Not found."\necho "[4/4] Checking home-level .9router / .openclaw..."\nfor dir in "$HOME/.9router" "$HOME/.openclaw"; do\n if [ -d "$dir" ]; then\n read -rp "Delete $dir ? [YES/no]: " CLEAN\n [ "$CLEAN" = "YES" ] && rm -rf "$dir" && echo " OK: Deleted $dir" || echo " Kept: $dir"\n fi\ndone\n` };
|
|
2052
2058
|
}
|
|
2053
2059
|
|
|
2054
2060
|
return null;
|
|
@@ -2144,7 +2150,7 @@ fi
|
|
|
2144
2150
|
L.push('echo.');
|
|
2145
2151
|
L.push(isVi ? 'echo [OK] OpenClaw Gateway da khoi dong trong cua so moi!' : 'echo [OK] OpenClaw Gateway started in a new window!');
|
|
2146
2152
|
L.push('echo.');
|
|
2147
|
-
L.push('echo OpenClaw Dashboard: http://127.0.0.1:
|
|
2153
|
+
L.push('echo OpenClaw Dashboard: http://127.0.0.1:18789');
|
|
2148
2154
|
if (is9Router) L.push('echo 9Router Dashboard: http://127.0.0.1:20128/dashboard');
|
|
2149
2155
|
L.push('echo.');
|
|
2150
2156
|
L.push(isVi ? 'echo Ban co the dong cua so nay.' : 'echo You may close this window.');
|
|
@@ -2218,7 +2224,7 @@ fi
|
|
|
2218
2224
|
}
|
|
2219
2225
|
L.push('pm2 save >/dev/null 2>&1 || true');
|
|
2220
2226
|
L.push('echo ""');
|
|
2221
|
-
L.push('echo "OpenClaw Dashboard: http://127.0.0.1:
|
|
2227
|
+
L.push('echo "OpenClaw Dashboard: http://127.0.0.1:18789"');
|
|
2222
2228
|
if (is9Router) L.push('echo "9Router Dashboard: http://127.0.0.1:20128/dashboard"');
|
|
2223
2229
|
L.push('echo ""');
|
|
2224
2230
|
L.push(isVi ? 'echo "Log gateway: pm2 logs $APP_NAME"' : 'echo "Gateway logs: pm2 logs $APP_NAME"');
|
|
@@ -2269,7 +2275,7 @@ fi
|
|
|
2269
2275
|
L.push(isVi ? `echo "[OK] Gateway khoi dong (PID $GW_PID). Log: ${logFileGw}"` : `echo "[OK] Gateway started (PID $GW_PID). Log: ${logFileGw}"`);
|
|
2270
2276
|
L.push('');
|
|
2271
2277
|
L.push('echo ""');
|
|
2272
|
-
L.push('echo "OpenClaw Dashboard: http://127.0.0.1:
|
|
2278
|
+
L.push('echo "OpenClaw Dashboard: http://127.0.0.1:18789"');
|
|
2273
2279
|
if (is9Router) L.push('echo "9Router Dashboard: http://127.0.0.1:20128/dashboard"');
|
|
2274
2280
|
L.push('echo ""');
|
|
2275
2281
|
L.push(isVi ? 'echo "Bot dang chay background. Dung: openclaw gateway stop"' : 'echo "Bot running in background. Stop: openclaw gateway stop"');
|
|
@@ -2367,7 +2373,7 @@ fi
|
|
|
2367
2373
|
"Write-Host \"\"",
|
|
2368
2374
|
"if ($exitCode -eq 0) {",
|
|
2369
2375
|
" Write-Host \" 🎉 Upgrade hoan tat!\" -ForegroundColor Green",
|
|
2370
|
-
" Write-Host \" Dashboard: http://localhost:
|
|
2376
|
+
" Write-Host \" Dashboard: http://localhost:18789\" -ForegroundColor Cyan",
|
|
2371
2377
|
"} else {",
|
|
2372
2378
|
" Write-Host \" ⚠️ Ma loi: $exitCode — xem log o tren.\" -ForegroundColor Yellow",
|
|
2373
2379
|
"}",
|
|
@@ -2456,7 +2462,7 @@ fi
|
|
|
2456
2462
|
"echo \"\"",
|
|
2457
2463
|
"if [ $EXIT_CODE -eq 0 ]; then",
|
|
2458
2464
|
" echo -e \"${GREEN} 🎉 Upgrade hoan tat!${NC}\"",
|
|
2459
|
-
" echo -e \"${CYAN} Dashboard: http://localhost:
|
|
2465
|
+
" echo -e \"${CYAN} Dashboard: http://localhost:18789${NC}\"",
|
|
2460
2466
|
"else",
|
|
2461
2467
|
" echo -e \"${YELLOW} ⚠️ Ma loi: $EXIT_CODE — xem log o tren.${NC}\"",
|
|
2462
2468
|
"fi",
|
|
@@ -2492,7 +2498,7 @@ if (typeof exports !== 'undefined' && typeof globalThis !== 'undefined' && globa
|
|
|
2492
2498
|
Object.assign(exports, globalThis.__openclawInstall);
|
|
2493
2499
|
}
|
|
2494
2500
|
|
|
2495
|
-
//
|
|
2501
|
+
// ââ€â‚¬Ã¢â€â‚¬ Shared Docker artifact helpers for wizard + CLI (setup/shared/docker-gen.js)
|
|
2496
2502
|
// @ts-nocheck
|
|
2497
2503
|
(function (root) {
|
|
2498
2504
|
const common = (typeof globalThis !== 'undefined' && globalThis.__openclawCommon) || {};
|
|
@@ -2512,65 +2518,106 @@ if (typeof exports !== 'undefined' && typeof globalThis !== 'undefined' && globa
|
|
|
2512
2518
|
return String(text).split('\n').map((line) => `${prefix}${line}`).join('\n');
|
|
2513
2519
|
}
|
|
2514
2520
|
|
|
2515
|
-
function build9RouterSmartRouteSyncScript(
|
|
2516
|
-
|
|
2517
|
-
const
|
|
2518
|
-
const
|
|
2519
|
-
|
|
2520
|
-
const
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
const
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
if (
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
if (
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2521
|
+
function build9RouterSmartRouteSyncScript() {
|
|
2522
|
+
const lines = [
|
|
2523
|
+
"const fs = require('fs');",
|
|
2524
|
+
"const INTERVAL = 30000;",
|
|
2525
|
+
"const DB_PATH = '/root/.9router/db/data.sqlite';",
|
|
2526
|
+
"const PORT = process.env.PORT || 20128;",
|
|
2527
|
+
"const COMBO_NAME = 'smart-route';",
|
|
2528
|
+
"const API_BASE = `http://localhost:${PORT}`;",
|
|
2529
|
+
"",
|
|
2530
|
+
"function ensureSettings() {",
|
|
2531
|
+
" try {",
|
|
2532
|
+
" let db = null;",
|
|
2533
|
+
" try {",
|
|
2534
|
+
" const { DatabaseSync } = require('node:sqlite');",
|
|
2535
|
+
" db = new DatabaseSync(DB_PATH);",
|
|
2536
|
+
" } catch {",
|
|
2537
|
+
" let Database;",
|
|
2538
|
+
" try { Database = require('/usr/local/lib/node_modules/better-sqlite3'); } catch {",
|
|
2539
|
+
" try { Database = require('better-sqlite3'); } catch { return; }",
|
|
2540
|
+
" }",
|
|
2541
|
+
" db = Database(DB_PATH);",
|
|
2542
|
+
" }",
|
|
2543
|
+
' const existing = db.prepare("SELECT * FROM settings WHERE id = 1").get();',
|
|
2544
|
+
" if (!existing) {",
|
|
2545
|
+
' db.prepare("INSERT INTO settings (id, data) VALUES (1, ?)").run(JSON.stringify({ requireLogin: false }));',
|
|
2546
|
+
" } else {",
|
|
2547
|
+
" try {",
|
|
2548
|
+
" const data = JSON.parse(existing.data || '{}');",
|
|
2549
|
+
" if (data.requireLogin !== false) {",
|
|
2550
|
+
" data.requireLogin = false;",
|
|
2551
|
+
' db.prepare("UPDATE settings SET data = ? WHERE id = 1").run(JSON.stringify(data));',
|
|
2552
|
+
" }",
|
|
2553
|
+
" } catch {}",
|
|
2554
|
+
" }",
|
|
2555
|
+
" db.close();",
|
|
2556
|
+
" } catch (e) {}",
|
|
2557
|
+
"}",
|
|
2558
|
+
"",
|
|
2559
|
+
"const sync = async () => {",
|
|
2560
|
+
" try {",
|
|
2561
|
+
" if (!fs.existsSync(DB_PATH)) return;",
|
|
2562
|
+
"",
|
|
2563
|
+
" let existingCombo = null;",
|
|
2564
|
+
" try {",
|
|
2565
|
+
" const resp = await fetch(`${API_BASE}/api/combos`);",
|
|
2566
|
+
" if (resp.status === 401) {",
|
|
2567
|
+
" ensureSettings();",
|
|
2568
|
+
" return;",
|
|
2569
|
+
" }",
|
|
2570
|
+
" const data = await resp.json();",
|
|
2571
|
+
" if (data.combos) {",
|
|
2572
|
+
" existingCombo = data.combos.find(c => c.name === COMBO_NAME);",
|
|
2573
|
+
" }",
|
|
2574
|
+
" } catch (e) { return; }",
|
|
2575
|
+
"",
|
|
2576
|
+
" if (existingCombo) return;",
|
|
2577
|
+
"",
|
|
2578
|
+
" let activeProviders = [];",
|
|
2579
|
+
" try {",
|
|
2580
|
+
" const resp = await fetch(`${API_BASE}/api/providers`);",
|
|
2581
|
+
" const data = await resp.json();",
|
|
2582
|
+
" const conns = data.connections || data.providerConnections || [];",
|
|
2583
|
+
" activeProviders = [...new Set(",
|
|
2584
|
+
" conns.filter(c => c && c.provider && c.isActive !== false && !c.disabled).map(c => c.provider)",
|
|
2585
|
+
" )];",
|
|
2586
|
+
" } catch (e) { return; }",
|
|
2587
|
+
"",
|
|
2588
|
+
" if (!activeProviders.length) return;",
|
|
2589
|
+
"",
|
|
2590
|
+
" let models = [];",
|
|
2591
|
+
" try {",
|
|
2592
|
+
" const resp = await fetch(`${API_BASE}/api/models`);",
|
|
2593
|
+
" const data = await resp.json();",
|
|
2594
|
+
" if (data.models && Array.isArray(data.models)) {",
|
|
2595
|
+
" models = data.models",
|
|
2596
|
+
" .filter(m => activeProviders.includes(m.provider))",
|
|
2597
|
+
" .filter(m => !/(embedding|image|tts|stt|audio|vision)/i.test(m.model))",
|
|
2598
|
+
" .map(m => m.fullModel);",
|
|
2599
|
+
" }",
|
|
2600
|
+
" models = [...new Set(models)];",
|
|
2601
|
+
" } catch (e) { return; }",
|
|
2602
|
+
"",
|
|
2603
|
+
" if (!models.length) return;",
|
|
2604
|
+
"",
|
|
2605
|
+
" try {",
|
|
2606
|
+
" await fetch(`${API_BASE}/api/combos`, {",
|
|
2607
|
+
" method: 'POST',",
|
|
2608
|
+
" headers: { 'Content-Type': 'application/json' },",
|
|
2609
|
+
" body: JSON.stringify({ name: COMBO_NAME, models })",
|
|
2610
|
+
" });",
|
|
2611
|
+
" console.log('[sync-combo] Created smart-route with ' + models.length + ' models');",
|
|
2612
|
+
" } catch (e) {}",
|
|
2613
|
+
" } catch (e) {}",
|
|
2614
|
+
"};",
|
|
2615
|
+
"",
|
|
2616
|
+
"if (fs.existsSync(DB_PATH)) ensureSettings();",
|
|
2617
|
+
"setTimeout(sync, 10000);",
|
|
2618
|
+
"setInterval(sync, INTERVAL);",
|
|
2619
|
+
];
|
|
2620
|
+
return lines.join('\n');
|
|
2574
2621
|
}
|
|
2575
2622
|
|
|
2576
2623
|
function build9RouterPatchScript() {
|
|
@@ -2635,20 +2682,20 @@ for(const root of roots){if(!root||!fs.existsSync(root))continue;touched+=patchP
|
|
|
2635
2682
|
if(touched){console.log('[patch-9router] Applied Codex compatibility patch.');}else{console.log('[patch-9router] No compatible 9router source files found to patch.');}`;
|
|
2636
2683
|
}
|
|
2637
2684
|
|
|
2638
|
-
function build9RouterComposeEntrypointScript(
|
|
2685
|
+
function build9RouterComposeEntrypointScript(routerPort) {
|
|
2686
|
+
const port = routerPort || 20128;
|
|
2639
2687
|
const nineRouterSpec = (typeof globalThis !== 'undefined' && globalThis.__openclawCommon && globalThis.__openclawCommon.NINE_ROUTER_NPM_SPEC) || '9router@latest';
|
|
2640
2688
|
return [
|
|
2641
|
-
`npm install -g
|
|
2642
|
-
`node -e "require('fs').writeFileSync('/tmp/patch-9router.js',Buffer.from('${patchScriptBase64}','base64').toString())"`,
|
|
2643
|
-
`node -e "require('fs').writeFileSync('/tmp/sync.js',Buffer.from('${syncScriptBase64}','base64').toString())"`,
|
|
2689
|
+
`npm install -g ` + nineRouterSpec + ` better-sqlite3`,
|
|
2644
2690
|
'node /tmp/patch-9router.js || true',
|
|
2691
|
+
'node -e "const fs=require(\'fs\'),path=require(\'path\'); const DB_PATH=\'/root/.9router/db/data.sqlite\'; const dir=path.dirname(DB_PATH); if(!fs.existsSync(dir))fs.mkdirSync(dir,{recursive:true}); try{ const {DatabaseSync}=require(\'node:sqlite\'); const db=new DatabaseSync(DB_PATH); db.prepare(\'CREATE TABLE IF NOT EXISTS settings (id INTEGER PRIMARY KEY CHECK (id = 1), data TEXT NOT NULL)\').run(); const existing=db.prepare(\'SELECT * FROM settings WHERE id = 1\').get(); if(!existing){ db.prepare(\'INSERT INTO settings (id, data) VALUES (1, ?)\').run(JSON.stringify({requireLogin:false})); } db.close(); }catch(e){}" || true',
|
|
2645
2692
|
'node /tmp/sync.js > /tmp/sync.log 2>&1 &',
|
|
2646
|
-
|
|
2693
|
+
`exec 9router -n -l -H 0.0.0.0 -p ${port} --skip-update`
|
|
2647
2694
|
].join('\n');
|
|
2648
2695
|
}
|
|
2649
2696
|
|
|
2650
2697
|
function buildGatewayPatchCmd() {
|
|
2651
|
-
return `node -e \\"const fs=require('fs'),os=require('os'),path=require('path'),p=path.join(process.cwd(),'.openclaw','openclaw.json');if(fs.existsSync(p)){const c=JSON.parse(fs.readFileSync(p,'utf8'));const a=new Set(['http://localhost:
|
|
2698
|
+
return `node -e \\"const fs=require('fs'),os=require('os'),path=require('path'),p=path.join(process.cwd(),'.openclaw','openclaw.json');if(fs.existsSync(p)){const c=JSON.parse(fs.readFileSync(p,'utf8'));const gp=Number(process.env.OPENCLAW_GATEWAY_PORT||process.env.OPENCLAW_PORT)||c.gateway?.port||18789;const a=new Set(['http://localhost:'+gp,'http://127.0.0.1:'+gp,'http://0.0.0.0:'+gp]);for(const entries of Object.values(os.networkInterfaces()||{})){for(const entry of entries||[]){if(!entry||entry.internal||entry.family!=='IPv4'||!entry.address)continue;a.add('http://' + entry.address + ':'+gp);}}const p9=c.models&&c.models.providers&&c.models.providers['9router'];if(p9){p9.request=Object.assign({},p9.request,{allowPrivateNetwork:true});}c.tools=Object.assign({},c.tools,{profile:'full',exec:{host:'gateway',security:'full',ask:'off'}});c.gateway=Object.assign({},c.gateway,{port:gp,bind:'custom',customBindHost:'0.0.0.0',controlUi:Object.assign({},c.gateway?.controlUi,{allowedOrigins:Array.from(a).filter(Boolean)})});fs.writeFileSync(p,JSON.stringify(c,null,2));}\\"`;
|
|
2652
2699
|
}
|
|
2653
2700
|
|
|
2654
2701
|
function buildDockerArtifacts(options) {
|
|
@@ -2665,7 +2712,7 @@ if(touched){console.log('[patch-9router] Applied Codex compatibility patch.');}e
|
|
|
2665
2712
|
dockerfilePlugins = [],
|
|
2666
2713
|
dockerfileSkillInstallMode = 'none',
|
|
2667
2714
|
runtimeCommandParts = [],
|
|
2668
|
-
volumeMount = '../../.openclaw:/root/project/.openclaw
|
|
2715
|
+
volumeMount = '../../.openclaw:/root/project/.openclaw\n - ../../:/mnt/project',
|
|
2669
2716
|
singleComposeName = 'oc-bot',
|
|
2670
2717
|
multiComposeName = 'oc-multibot',
|
|
2671
2718
|
singleAppContainerName = 'openclaw-bot',
|
|
@@ -2678,7 +2725,8 @@ if(touched){console.log('[patch-9router] Applied Codex compatibility patch.');}e
|
|
|
2678
2725
|
multiOllamaNumParallel = 1,
|
|
2679
2726
|
singleOllamaNumParallel = 1,
|
|
2680
2727
|
emitBrowserInstall = true,
|
|
2681
|
-
|
|
2728
|
+
gatewayPort = 18789,
|
|
2729
|
+
routerPort = 20128,
|
|
2682
2730
|
} = options;
|
|
2683
2731
|
|
|
2684
2732
|
const browserAptExtra = hasBrowser ? ' xvfb socat' : '';
|
|
@@ -2704,10 +2752,9 @@ if(touched){console.log('[patch-9router] Applied Codex compatibility patch.');}e
|
|
|
2704
2752
|
// Dynamic runtime configuration: backup config before any first-run install, restore after.
|
|
2705
2753
|
// Missing plugin install may touch openclaw.json, so preserve critical fields.
|
|
2706
2754
|
const backupConfigScript = `const fs=require('fs'),path=require('path'),p=path.join(process.cwd(),'.openclaw','openclaw.json'),b=p.replace('openclaw.json','.openclaw-config-backup.json');if(fs.existsSync(p)){fs.copyFileSync(p,b);}`;
|
|
2707
|
-
const backupConfigB64 = encodeBase64Utf8(backupConfigScript);
|
|
2708
2755
|
|
|
2709
|
-
const restoreConfigScript = `const fs=require('fs'),os=require('os'),path=require('path'),p=path.join(process.cwd(),'.openclaw','openclaw.json'),b=p.replace('openclaw.json','.openclaw-config-backup.json');if(fs.existsSync(p)&&fs.existsSync(b)){const c=JSON.parse(fs.readFileSync(p,'utf8'));const bk=JSON.parse(fs.readFileSync(b,'utf8'));const keep=['agents','channels','bindings','commands','models','browser','skills'];for(const k of keep){if(bk[k]&&!c[k])c[k]=bk[k];}const a=new Set(['http://localhost:
|
|
2710
|
-
const
|
|
2756
|
+
const restoreConfigScript = `const fs=require('fs'),os=require('os'),path=require('path'),p=path.join(process.cwd(),'.openclaw','openclaw.json'),b=p.replace('openclaw.json','.openclaw-config-backup.json');if(fs.existsSync(p)&&fs.existsSync(b)){const c=JSON.parse(fs.readFileSync(p,'utf8'));const bk=JSON.parse(fs.readFileSync(b,'utf8'));const keep=['agents','channels','bindings','commands','models','browser','skills','plugins','tools'];for(const k of keep){if(bk[k]&&!c[k])c[k]=bk[k];}const gp=Number(process.env.OPENCLAW_GATEWAY_PORT||process.env.OPENCLAW_PORT)||c.gateway?.port||bk.gateway?.port||18789;const a=new Set(['http://localhost:'+gp,'http://127.0.0.1:'+gp,'http://0.0.0.0:'+gp]);for(const entries of Object.values(os.networkInterfaces()||{})){for(const entry of entries||[]){if(!entry||entry.internal||entry.family!=='IPv4'||!entry.address)continue;a.add('http://'+entry.address+':'+gp);}}c.tools=Object.assign({},c.tools,{profile:'full',exec:{host:'gateway',security:'full',ask:'off'}});c.gateway=Object.assign({},c.gateway,{port:gp,bind:'custom',customBindHost:'0.0.0.0',mode:c.gateway?.mode||bk.gateway?.mode||'local',controlUi:Object.assign({},c.gateway?.controlUi,{allowedOrigins:Array.from(a).filter(Boolean)})});fs.writeFileSync(p,JSON.stringify(c,null,2));fs.unlinkSync(b);}`;
|
|
2757
|
+
const securityCompatScript = `const fs=require('fs'),path=require('path');const scopes=['operator.admin','operator.pairing','operator.approvals'];function uniq(a){return Array.from(new Set([...(Array.isArray(a)?a:[]),...scopes]));}function walk(v){if(!v||typeof v!=='object')return;if(Array.isArray(v)){v.forEach(walk);return;}if(Array.isArray(v.scopes)||Array.isArray(v.approvedScopes)){v.scopes=uniq(v.scopes);v.approvedScopes=uniq(v.approvedScopes);}Object.values(v).forEach(walk);}const home=process.env.OPENCLAW_HOME||path.join(process.cwd(),'.openclaw');const state=process.env.OPENCLAW_STATE_DIR||home;const cfgPath=path.join(process.cwd(),'.openclaw','openclaw.json');if(fs.existsSync(cfgPath)){const c=JSON.parse(fs.readFileSync(cfgPath,'utf8'));const p=c.models&&c.models.providers&&c.models.providers['9router'];if(p){p.request=Object.assign({},p.request,{allowPrivateNetwork:true});}fs.writeFileSync(cfgPath,JSON.stringify(c,null,2));}for(const root of Array.from(new Set([home,state]))){const f=path.join(root,'devices','paired.json');if(fs.existsSync(f)){const d=JSON.parse(fs.readFileSync(f,'utf8'));walk(d);fs.writeFileSync(f,JSON.stringify(d,null,2));}}`;
|
|
2711
2758
|
|
|
2712
2759
|
const runtimeParts = runtimeCommandParts.filter(Boolean);
|
|
2713
2760
|
const runtimePrelude = [
|
|
@@ -2746,9 +2793,34 @@ if(touched){console.log('[patch-9router] Applied Codex compatibility patch.');}e
|
|
|
2746
2793
|
];
|
|
2747
2794
|
runtimeParts.unshift(...runtimePrelude);
|
|
2748
2795
|
// Backup config BEFORE plugin installs (runtimeCommandParts may contain plugin install commands)
|
|
2749
|
-
runtimeParts.unshift(`node -
|
|
2796
|
+
runtimeParts.unshift(`node - <<'NODE'\n${backupConfigScript}\nNODE`);
|
|
2750
2797
|
// Restore config AFTER plugin installs (which may clobber openclaw.json)
|
|
2751
|
-
runtimeParts.push(`node -
|
|
2798
|
+
runtimeParts.push(`node - <<'NODE'\n${restoreConfigScript}\nNODE`);
|
|
2799
|
+
runtimeParts.push(`node - <<'NODE'\n${securityCompatScript}\nNODE`);
|
|
2800
|
+
// Zalouser stability: patch watchdog tolerance and add auto-restart monitor
|
|
2801
|
+
runtimeParts.push([
|
|
2802
|
+
'# Patch zalouser watchdog tolerance (35s -> 90s) to survive provider auth pre-warming',
|
|
2803
|
+
'ZALO_JS=$(find "$OPENCLAW_HOME" -path "*/zalouser/dist/zalo-js-*.js" -type f 2>/dev/null | head -1)',
|
|
2804
|
+
'if [ -n "$ZALO_JS" ] && grep -q "35e3" "$ZALO_JS" 2>/dev/null; then',
|
|
2805
|
+
' sed -i "s/LISTENER_WATCHDOG_MAX_GAP_MS\\\\s*=\\\\s*35e3/LISTENER_WATCHDOG_MAX_GAP_MS = 90e3/" "$ZALO_JS"',
|
|
2806
|
+
' echo "[entrypoint] patched zalouser watchdog gap: 35s -> 90s"',
|
|
2807
|
+
'fi',
|
|
2808
|
+
].join('\\n'));
|
|
2809
|
+
runtimeParts.push([
|
|
2810
|
+
'# Zalo channel auto-restart monitor (background)',
|
|
2811
|
+
'(',
|
|
2812
|
+
' sleep 180',
|
|
2813
|
+
' while true; do',
|
|
2814
|
+
' sleep 60',
|
|
2815
|
+
' STATUS=$(openclaw channels status 2>/dev/null | grep -i "Zalo Personal" || true)',
|
|
2816
|
+
' if echo "$STATUS" | grep -qi "stopped"; then',
|
|
2817
|
+
' echo "[zalo-monitor] Zalo channel stopped - restarting container in 5s"',
|
|
2818
|
+
' sleep 5',
|
|
2819
|
+
' kill 1 2>/dev/null || true',
|
|
2820
|
+
' fi',
|
|
2821
|
+
' done',
|
|
2822
|
+
') &',
|
|
2823
|
+
].join('\\n'));
|
|
2752
2824
|
if (hasBrowser) {
|
|
2753
2825
|
runtimeParts.push('socat TCP-LISTEN:9222,fork,reuseaddr TCP:host.docker.internal:9222 &');
|
|
2754
2826
|
runtimeParts.push('Xvfb :99 -screen 0 1280x720x24 > /dev/null 2>&1 & DISPLAY=:99 openclaw gateway run');
|
|
@@ -2756,7 +2828,6 @@ if(touched){console.log('[patch-9router] Applied Codex compatibility patch.');}e
|
|
|
2756
2828
|
runtimeParts.push('openclaw gateway run');
|
|
2757
2829
|
}
|
|
2758
2830
|
const runtimeScript = ['#!/bin/sh', 'set -e', ...runtimeParts].join('\n');
|
|
2759
|
-
const runtimeScriptB64 = encodeBase64Utf8(runtimeScript);
|
|
2760
2831
|
const dockerfile = `FROM node:22-slim
|
|
2761
2832
|
|
|
2762
2833
|
RUN apt-get update && apt-get install -y git curl python3${browserAptExtra} && rm -rf /var/lib/apt/lists/*
|
|
@@ -2765,21 +2836,20 @@ ARG OPENCLAW_VER="${openClawNpmSpec}"
|
|
|
2765
2836
|
ARG CACHE_BUST=""
|
|
2766
2837
|
RUN echo "CACHE_BUST=$CACHE_BUST" && npm install -g $OPENCLAW_VER ${openClawRuntimePackages}${skillLines}${pluginLines}
|
|
2767
2838
|
${patchLine}
|
|
2768
|
-
|
|
2839
|
+
COPY entrypoint.sh /usr/local/bin/openclaw-entrypoint.sh
|
|
2840
|
+
RUN chmod +x /usr/local/bin/openclaw-entrypoint.sh
|
|
2769
2841
|
WORKDIR /root/project
|
|
2770
2842
|
|
|
2771
|
-
EXPOSE
|
|
2843
|
+
EXPOSE ${gatewayPort}
|
|
2772
2844
|
|
|
2773
2845
|
CMD ["/bin/sh", "/usr/local/bin/openclaw-entrypoint.sh"]`;
|
|
2774
2846
|
|
|
2775
|
-
const syncScript = build9RouterSmartRouteSyncScript(
|
|
2776
|
-
const
|
|
2777
|
-
const
|
|
2778
|
-
const patchScriptBase64 = encodeBase64Utf8(patchScript);
|
|
2779
|
-
const docker9RouterEntrypointScript = build9RouterComposeEntrypointScript(syncScriptBase64, patchScriptBase64);
|
|
2847
|
+
const syncScript = build9RouterSmartRouteSyncScript();
|
|
2848
|
+
const patchScript = build9RouterPatchScript();
|
|
2849
|
+
const docker9RouterEntrypointScript = build9RouterComposeEntrypointScript(routerPort);
|
|
2780
2850
|
const extraHostsBlock = ` extra_hosts:\n - "host.docker.internal:host-gateway"`;
|
|
2781
2851
|
|
|
2782
|
-
const appEnvironmentBlock =
|
|
2852
|
+
const appEnvironmentBlock = ` environment:\n - OPENCLAW_HOME=/root/project/.openclaw\n - OPENCLAW_STATE_DIR=/root/project/.openclaw\n - OPENCLAW_ALLOW_INSECURE_PRIVATE_WS=1\n - OPENCLAW_GATEWAY_PORT=${gatewayPort}\n - OPENCLAW_PORT=${gatewayPort}\n tmpfs:\n - /root/project/.openclaw/plugin-runtime-deps\n`;
|
|
2783
2853
|
|
|
2784
2854
|
let compose;
|
|
2785
2855
|
if (isMultiBot) {
|
|
@@ -2797,11 +2867,11 @@ services:
|
|
|
2797
2867
|
container_name: ${multiAppContainerName}
|
|
2798
2868
|
restart: always
|
|
2799
2869
|
env_file:
|
|
2800
|
-
-
|
|
2870
|
+
- ../../.env
|
|
2801
2871
|
${appEnvironmentBlock}${dependsOn}${extraHosts} volumes:
|
|
2802
2872
|
- ${volumeMount}
|
|
2803
2873
|
ports:
|
|
2804
|
-
- "
|
|
2874
|
+
- "${gatewayPort}:${gatewayPort}"
|
|
2805
2875
|
|
|
2806
2876
|
9router:
|
|
2807
2877
|
image: node:22-slim
|
|
@@ -2813,13 +2883,15 @@ ${appEnvironmentBlock}${dependsOn}${extraHosts} volumes:
|
|
|
2813
2883
|
- |
|
|
2814
2884
|
${indentBlock(docker9RouterEntrypointScript, 8)}
|
|
2815
2885
|
environment:
|
|
2816
|
-
- PORT
|
|
2886
|
+
- PORT=${routerPort}
|
|
2817
2887
|
- HOSTNAME=0.0.0.0
|
|
2818
2888
|
- CI=true
|
|
2819
2889
|
volumes:
|
|
2820
2890
|
- 9router-data:/root/.9router
|
|
2891
|
+
- ./sync.js:/tmp/sync.js:ro
|
|
2892
|
+
- ./patch-9router.js:/tmp/patch-9router.js:ro
|
|
2821
2893
|
ports:
|
|
2822
|
-
- "
|
|
2894
|
+
- "${routerPort}:${routerPort}"
|
|
2823
2895
|
|
|
2824
2896
|
volumes:
|
|
2825
2897
|
9router-data:`;
|
|
@@ -2832,11 +2904,11 @@ services:
|
|
|
2832
2904
|
container_name: ${multiAppContainerName}
|
|
2833
2905
|
restart: always
|
|
2834
2906
|
env_file:
|
|
2835
|
-
-
|
|
2907
|
+
- ../../.env
|
|
2836
2908
|
${appEnvironmentBlock}${dependsOn}${extraHosts} volumes:
|
|
2837
2909
|
- ${volumeMount}
|
|
2838
2910
|
ports:
|
|
2839
|
-
- "
|
|
2911
|
+
- "${gatewayPort}:${gatewayPort}"
|
|
2840
2912
|
|
|
2841
2913
|
ollama:
|
|
2842
2914
|
image: ollama/ollama:latest
|
|
@@ -2872,11 +2944,11 @@ services:
|
|
|
2872
2944
|
container_name: ${multiAppContainerName}
|
|
2873
2945
|
restart: always
|
|
2874
2946
|
env_file:
|
|
2875
|
-
-
|
|
2947
|
+
- ../../.env
|
|
2876
2948
|
${appEnvironmentBlock}${extraHosts} volumes:
|
|
2877
2949
|
- ${volumeMount}
|
|
2878
2950
|
ports:
|
|
2879
|
-
- "
|
|
2951
|
+
- "${gatewayPort}:${gatewayPort}"`;
|
|
2880
2952
|
}
|
|
2881
2953
|
} else if (is9Router) {
|
|
2882
2954
|
compose = `name: ${singleComposeName}
|
|
@@ -2886,13 +2958,14 @@ services:
|
|
|
2886
2958
|
container_name: ${singleAppContainerName}
|
|
2887
2959
|
restart: always
|
|
2888
2960
|
env_file:
|
|
2889
|
-
-
|
|
2961
|
+
- ../../.env
|
|
2890
2962
|
depends_on:
|
|
2891
2963
|
- 9router
|
|
2892
2964
|
${appEnvironmentBlock}${hasBrowser ? `${extraHostsBlock}\n` : ''} volumes:
|
|
2893
2965
|
- ${volumeMount}
|
|
2966
|
+
- openclaw-plugins:/root/project/.openclaw/npm
|
|
2894
2967
|
ports:
|
|
2895
|
-
- "
|
|
2968
|
+
- "${gatewayPort}:${gatewayPort}"
|
|
2896
2969
|
|
|
2897
2970
|
9router:
|
|
2898
2971
|
image: node:22-slim
|
|
@@ -2904,16 +2977,19 @@ ${appEnvironmentBlock}${hasBrowser ? `${extraHostsBlock}\n` : ''} volumes:
|
|
|
2904
2977
|
- |
|
|
2905
2978
|
${indentBlock(docker9RouterEntrypointScript, 8)}
|
|
2906
2979
|
environment:
|
|
2907
|
-
- PORT
|
|
2980
|
+
- PORT=${routerPort}
|
|
2908
2981
|
- HOSTNAME=0.0.0.0
|
|
2909
2982
|
- CI=true
|
|
2910
2983
|
volumes:
|
|
2911
2984
|
- 9router-data:/root/.9router
|
|
2985
|
+
- ./sync.js:/tmp/sync.js:ro
|
|
2986
|
+
- ./patch-9router.js:/tmp/patch-9router.js:ro
|
|
2912
2987
|
ports:
|
|
2913
|
-
- "
|
|
2988
|
+
- "${routerPort}:${routerPort}"
|
|
2914
2989
|
|
|
2915
2990
|
volumes:
|
|
2916
|
-
9router-data
|
|
2991
|
+
9router-data:
|
|
2992
|
+
openclaw-plugins:`;
|
|
2917
2993
|
} else if (isLocal) {
|
|
2918
2994
|
const ollamaModelTag = String(selectedModel || 'ollama/gemma4:e2b').replace('ollama/', '');
|
|
2919
2995
|
compose = `name: ${singleComposeName}
|
|
@@ -2922,12 +2998,12 @@ services:
|
|
|
2922
2998
|
build: .
|
|
2923
2999
|
container_name: ${singleAppContainerName}
|
|
2924
3000
|
restart: always
|
|
2925
|
-
env_file:
|
|
3001
|
+
env_file: ../../.env
|
|
2926
3002
|
${appEnvironmentBlock} depends_on:
|
|
2927
3003
|
ollama:
|
|
2928
3004
|
condition: service_healthy
|
|
2929
3005
|
${hasBrowser ? `${extraHostsBlock}\n` : ''} ports:
|
|
2930
|
-
- "
|
|
3006
|
+
- "${gatewayPort}:${gatewayPort}"
|
|
2931
3007
|
volumes:
|
|
2932
3008
|
- ${volumeMount}
|
|
2933
3009
|
|
|
@@ -2965,17 +3041,19 @@ services:
|
|
|
2965
3041
|
container_name: ${singleAppContainerName}
|
|
2966
3042
|
restart: always
|
|
2967
3043
|
env_file:
|
|
2968
|
-
-
|
|
3044
|
+
- ../../.env
|
|
2969
3045
|
${appEnvironmentBlock}${plainSingleExtraHosts ? `${extraHostsBlock}\n` : ''} volumes:
|
|
2970
3046
|
- ${volumeMount}
|
|
2971
3047
|
ports:
|
|
2972
|
-
- "
|
|
3048
|
+
- "${gatewayPort}:${gatewayPort}"`;
|
|
2973
3049
|
}
|
|
2974
3050
|
|
|
2975
3051
|
return {
|
|
2976
3052
|
dockerfile,
|
|
2977
3053
|
compose,
|
|
3054
|
+
entrypointScript: runtimeScript,
|
|
2978
3055
|
syncScript,
|
|
3056
|
+
patchScript,
|
|
2979
3057
|
docker9RouterEntrypointScript,
|
|
2980
3058
|
gatewayPatchCmd: buildGatewayPatchCmd(),
|
|
2981
3059
|
};
|
|
@@ -2996,7 +3074,9 @@ if (typeof exports !== 'undefined' && typeof globalThis !== 'undefined' && globa
|
|
|
2996
3074
|
Object.assign(exports, globalThis.__openclawDockerGen);
|
|
2997
3075
|
}
|
|
2998
3076
|
|
|
2999
|
-
|
|
3077
|
+
|
|
3078
|
+
|
|
3079
|
+
// ââ€â‚¬Ã¢â€â‚¬ buildNativeScriptCtx and shared native runtime helpers (setup/generators/native-helpers-gen.js)
|
|
3000
3080
|
// @ts-nocheck
|
|
3001
3081
|
/* eslint-disable no-undef, no-unused-vars */
|
|
3002
3082
|
/**
|
|
@@ -3048,15 +3128,101 @@ function buildNativeScriptCtx(options) {
|
|
|
3048
3128
|
});
|
|
3049
3129
|
|
|
3050
3130
|
function native9RouterSyncScriptContent() {
|
|
3051
|
-
return `const fs=require('fs');
|
|
3052
|
-
const path=require('path');
|
|
3053
|
-
const INTERVAL=30000;
|
|
3054
|
-
const
|
|
3055
|
-
const
|
|
3056
|
-
const
|
|
3057
|
-
const
|
|
3058
|
-
|
|
3059
|
-
|
|
3131
|
+
return `const fs = require('fs');
|
|
3132
|
+
const path = require('path');
|
|
3133
|
+
const INTERVAL = 30000;
|
|
3134
|
+
const DB_PATH = path.join(process.env.DATA_DIR || '.9router', 'db', 'data.sqlite');
|
|
3135
|
+
const PORT = process.env.PORT || 20128;
|
|
3136
|
+
const COMBO_NAME = 'smart-route';
|
|
3137
|
+
const API_BASE = \\\`http://localhost:\\\${PORT}\\\`;
|
|
3138
|
+
|
|
3139
|
+
function ensureSettings() {
|
|
3140
|
+
try {
|
|
3141
|
+
let Database;
|
|
3142
|
+
try {
|
|
3143
|
+
const cp = require('child_process');
|
|
3144
|
+
const npmRoot = cp.execSync('npm root -g').toString().trim();
|
|
3145
|
+
Database = require(path.join(npmRoot, '9router', 'node_modules', 'better-sqlite3'));
|
|
3146
|
+
} catch {
|
|
3147
|
+
try { Database = require('better-sqlite3'); } catch { return; }
|
|
3148
|
+
}
|
|
3149
|
+
const db = Database(DB_PATH);
|
|
3150
|
+
const existing = db.prepare("SELECT * FROM settings WHERE id = 1").get();
|
|
3151
|
+
if (!existing) {
|
|
3152
|
+
db.prepare("INSERT INTO settings (id, data) VALUES (1, ?)").run(JSON.stringify({ requireLogin: false }));
|
|
3153
|
+
} else {
|
|
3154
|
+
try {
|
|
3155
|
+
const data = JSON.parse(existing.data || '{}');
|
|
3156
|
+
if (data.requireLogin !== false) {
|
|
3157
|
+
data.requireLogin = false;
|
|
3158
|
+
db.prepare("UPDATE settings SET data = ? WHERE id = 1").run(JSON.stringify(data));
|
|
3159
|
+
}
|
|
3160
|
+
} catch {}
|
|
3161
|
+
}
|
|
3162
|
+
db.close();
|
|
3163
|
+
} catch (e) {}
|
|
3164
|
+
}
|
|
3165
|
+
|
|
3166
|
+
const sync = async () => {
|
|
3167
|
+
try {
|
|
3168
|
+
if (!fs.existsSync(DB_PATH)) return;
|
|
3169
|
+
|
|
3170
|
+
let existingCombo = null;
|
|
3171
|
+
try {
|
|
3172
|
+
const resp = await fetch(\\\`\\\${API_BASE}/api/combos\\\`);
|
|
3173
|
+
if (resp.status === 401) {
|
|
3174
|
+
ensureSettings();
|
|
3175
|
+
return;
|
|
3176
|
+
}
|
|
3177
|
+
const data = await resp.json();
|
|
3178
|
+
if (data.combos) {
|
|
3179
|
+
existingCombo = data.combos.find(c => c.name === COMBO_NAME);
|
|
3180
|
+
}
|
|
3181
|
+
} catch (e) { return; }
|
|
3182
|
+
|
|
3183
|
+
if (existingCombo) return;
|
|
3184
|
+
|
|
3185
|
+
let activeProviders = [];
|
|
3186
|
+
try {
|
|
3187
|
+
const resp = await fetch(\\\`\\\${API_BASE}/api/providers\\\`);
|
|
3188
|
+
const data = await resp.json();
|
|
3189
|
+
const conns = data.connections || data.providerConnections || [];
|
|
3190
|
+
activeProviders = [...new Set(
|
|
3191
|
+
conns.filter(c => c && c.provider && c.isActive !== false && !c.disabled).map(c => c.provider)
|
|
3192
|
+
)];
|
|
3193
|
+
} catch (e) { return; }
|
|
3194
|
+
|
|
3195
|
+
if (!activeProviders.length) return;
|
|
3196
|
+
|
|
3197
|
+
let models = [];
|
|
3198
|
+
try {
|
|
3199
|
+
const resp = await fetch(\\\`\\\${API_BASE}/api/models\\\`);
|
|
3200
|
+
const data = await resp.json();
|
|
3201
|
+
if (data.models && Array.isArray(data.models)) {
|
|
3202
|
+
models = data.models
|
|
3203
|
+
.filter(m => activeProviders.includes(m.provider))
|
|
3204
|
+
.filter(m => !/(embedding|image|tts|stt|audio|vision)/i.test(m.model))
|
|
3205
|
+
.map(m => m.fullModel);
|
|
3206
|
+
}
|
|
3207
|
+
models = [...new Set(models)];
|
|
3208
|
+
} catch (e) { return; }
|
|
3209
|
+
|
|
3210
|
+
if (!models.length) return;
|
|
3211
|
+
|
|
3212
|
+
try {
|
|
3213
|
+
await fetch(\\\`\\\${API_BASE}/api/combos\\\`, {
|
|
3214
|
+
method: 'POST',
|
|
3215
|
+
headers: { 'Content-Type': 'application/json' },
|
|
3216
|
+
body: JSON.stringify({ name: COMBO_NAME, models })
|
|
3217
|
+
});
|
|
3218
|
+
console.log('[sync-combo] Created smart-route with ' + models.length + ' models');
|
|
3219
|
+
} catch (e) {}
|
|
3220
|
+
} catch (e) {}
|
|
3221
|
+
};
|
|
3222
|
+
|
|
3223
|
+
if (fs.existsSync(DB_PATH)) ensureSettings();
|
|
3224
|
+
setTimeout(sync, 10000);
|
|
3225
|
+
setInterval(sync, INTERVAL);`;
|
|
3060
3226
|
}
|
|
3061
3227
|
|
|
3062
3228
|
function native9RouterServerEntryLookup() {
|
|
@@ -3202,12 +3368,16 @@ const sync=async()=>{try{const res=await fetch(ROUTER+'/api/providers');if(!res.
|
|
|
3202
3368
|
list: multiBotAgentMetas.map((meta) => ({
|
|
3203
3369
|
id: meta.agentId,
|
|
3204
3370
|
name: meta.name,
|
|
3205
|
-
workspace: '.openclaw/' + meta.workspaceDir,
|
|
3206
|
-
agentDir: `agents/${meta.agentId}/agent`,
|
|
3207
3371
|
model: { primary: state.config.model, fallbacks: [] },
|
|
3208
3372
|
})),
|
|
3209
3373
|
},
|
|
3210
|
-
commands: {
|
|
3374
|
+
commands: {
|
|
3375
|
+
native: 'auto',
|
|
3376
|
+
nativeSkills: 'auto',
|
|
3377
|
+
restart: true,
|
|
3378
|
+
ownerDisplay: 'raw',
|
|
3379
|
+
...(state.config.skills.includes('scheduler') ? { ownerAllowFrom: ['*'] } : {}),
|
|
3380
|
+
},
|
|
3211
3381
|
bindings: multiBotAgentMetas.map((meta) => ({
|
|
3212
3382
|
agentId: meta.agentId,
|
|
3213
3383
|
match: { channel: 'telegram', accountId: meta.accountId },
|
|
@@ -3234,6 +3404,7 @@ const sync=async()=>{try{const res=await fetch(ROUTER+'/api/providers');if(!res.
|
|
|
3234
3404
|
},
|
|
3235
3405
|
tools: {
|
|
3236
3406
|
profile: 'full',
|
|
3407
|
+
...(state.config.skills.includes('scheduler') ? { alsoAllow: ['group:automation'] } : {}),
|
|
3237
3408
|
exec: { host: 'gateway', security: 'full', ask: 'off' },
|
|
3238
3409
|
agentToAgent: {
|
|
3239
3410
|
enabled: true,
|
|
@@ -3254,12 +3425,11 @@ const sync=async()=>{try{const res=await fetch(ROUTER+'/api/providers');if(!res.
|
|
|
3254
3425
|
}
|
|
3255
3426
|
} : {}),
|
|
3256
3427
|
gateway: {
|
|
3257
|
-
port:
|
|
3428
|
+
port: 18789,
|
|
3258
3429
|
mode: 'local',
|
|
3259
|
-
bind: state.nativeOs === 'vps' ? '
|
|
3260
|
-
...(state.nativeOs === 'vps' ? { customBindHost: '0.0.0.0' } : {}),
|
|
3430
|
+
bind: state.nativeOs === 'vps' ? 'lan' : 'loopback',
|
|
3261
3431
|
controlUi: {
|
|
3262
|
-
allowedOrigins: getGatewayAllowedOrigins(
|
|
3432
|
+
allowedOrigins: getGatewayAllowedOrigins(18789),
|
|
3263
3433
|
},
|
|
3264
3434
|
auth: { mode: 'token', token: crypto.randomUUID().replace(/-/g, '') },
|
|
3265
3435
|
},
|
|
@@ -3324,7 +3494,7 @@ const sync=async()=>{try{const res=await fetch(ROUTER+'/api/providers');if(!res.
|
|
|
3324
3494
|
};
|
|
3325
3495
|
}
|
|
3326
3496
|
|
|
3327
|
-
//
|
|
3497
|
+
// ââ€â‚¬Ã¢â€â‚¬ botEnvContent, botConfigContent, botWorkspaceFiles, botFiles, helpers (setup/generators/config-gen.js)
|
|
3328
3498
|
// @ts-nocheck
|
|
3329
3499
|
/* eslint-disable no-undef, no-unused-vars */
|
|
3330
3500
|
/**
|
|
@@ -3381,12 +3551,12 @@ const sync=async()=>{try{const res=await fetch(ROUTER+'/api/providers');if(!res.
|
|
|
3381
3551
|
const bot = state.bots[botIndex] || {};
|
|
3382
3552
|
const botName = bot.name || `Bot ${botIndex + 1}`;
|
|
3383
3553
|
const agentId = botName.toLowerCase().replace(/[^a-z0-9]+/g, '-');
|
|
3384
|
-
const basePort =
|
|
3554
|
+
const basePort = 18789 + botIndex;
|
|
3385
3555
|
const groupId = state.groupId || '';
|
|
3386
3556
|
|
|
3387
3557
|
// Force use global provider if proxy mode is chosen globally, else use bot specific provider
|
|
3388
3558
|
const botProvider = (provider && provider.isProxy) ? provider : (PROVIDERS[bot.provider] || provider);
|
|
3389
|
-
const actualModel = botProvider.isProxy ?
|
|
3559
|
+
const actualModel = botProvider.isProxy ? 'smart-route' : (bot.model || state.config.model);
|
|
3390
3560
|
const bcfg = globalThis.__openclawBotConfig;
|
|
3391
3561
|
|
|
3392
3562
|
const cfg = bcfg.buildOpenclawJson({
|
|
@@ -3554,7 +3724,7 @@ const sync=async()=>{try{const res=await fetch(ROUTER+'/api/providers');if(!res.
|
|
|
3554
3724
|
}));
|
|
3555
3725
|
}
|
|
3556
3726
|
|
|
3557
|
-
//
|
|
3727
|
+
// ââ€â‚¬Ã¢â€â‚¬ generateZaloLoginBat, generateZaloLoginSh (unified) (setup/generators/zalo-login-gen.js)
|
|
3558
3728
|
// @ts-nocheck
|
|
3559
3729
|
/* eslint-disable no-undef, no-unused-vars */
|
|
3560
3730
|
/**
|
|
@@ -3591,15 +3761,13 @@ const sync=async()=>{try{const res=await fetch(ROUTER+'/api/providers');if(!res.
|
|
|
3591
3761
|
* @param {string} opts.projectDirVar - BAT var for project dir e.g. '%PROJECT_DIR%'
|
|
3592
3762
|
* @param {string} opts.label - Unique BAT label suffix (avoid duplicate labels)
|
|
3593
3763
|
* e.g. 'win', 'multi', 'combo'
|
|
3594
|
-
* @param {boolean} [opts.useInstance] -
|
|
3764
|
+
* @param {boolean} [opts.useInstance] - Reserved for QR file suffix only.
|
|
3595
3765
|
* @returns {string[]} Lines to push into the bat script
|
|
3596
3766
|
*/
|
|
3597
3767
|
function generateZaloLoginBat(opts) {
|
|
3598
3768
|
const { homeVar, projectDirVar, label = 'default', useInstance = false } = opts;
|
|
3599
3769
|
const credPath = `${homeVar}\\credentials\\zalouser\\credentials.json`;
|
|
3600
|
-
const loginCmd =
|
|
3601
|
-
? 'openclaw channels login --channel zalouser --instance default --verbose'
|
|
3602
|
-
: 'openclaw channels login --channel zalouser --verbose';
|
|
3770
|
+
const loginCmd = 'openclaw channels login --channel zalouser --verbose';
|
|
3603
3771
|
const contLabel = `:zalo_continue_${label}`;
|
|
3604
3772
|
const retryLabel = `:retry_zalo_${label}`;
|
|
3605
3773
|
|
|
@@ -3639,15 +3807,13 @@ function generateZaloLoginBat(opts) {
|
|
|
3639
3807
|
* @param {object} opts
|
|
3640
3808
|
* @param {string} opts.homeVar - Shell var for OPENCLAW_HOME e.g. '$OPENCLAW_HOME'
|
|
3641
3809
|
* @param {string} opts.projectDirVar - Shell var for project dir e.g. '$PROJECT_DIR'
|
|
3642
|
-
* @param {boolean} [opts.useInstance] -
|
|
3810
|
+
* @param {boolean} [opts.useInstance] - Reserved for QR file suffix only.
|
|
3643
3811
|
* @returns {string[]} Lines to push into the sh script
|
|
3644
3812
|
*/
|
|
3645
3813
|
function generateZaloLoginSh(opts) {
|
|
3646
3814
|
const { homeVar, projectDirVar, useInstance = false } = opts;
|
|
3647
3815
|
const credPath = `${homeVar}/credentials/zalouser/credentials.json`;
|
|
3648
|
-
const loginCmd =
|
|
3649
|
-
? 'openclaw channels login --channel zalouser --instance default --verbose'
|
|
3650
|
-
: 'openclaw channels login --channel zalouser --verbose';
|
|
3816
|
+
const loginCmd = 'openclaw channels login --channel zalouser --verbose';
|
|
3651
3817
|
|
|
3652
3818
|
return [
|
|
3653
3819
|
`# ── Zalo Personal Login (idempotent) ─────────────────────────────────`,
|
|
@@ -3671,7 +3837,7 @@ function generateZaloLoginSh(opts) {
|
|
|
3671
3837
|
];
|
|
3672
3838
|
}
|
|
3673
3839
|
|
|
3674
|
-
//
|
|
3840
|
+
// ââ€â‚¬Ã¢â€â‚¬ generateStartScript wizard wrapper (delegates to install-gen) (setup/generators/gateway-start-gen.js)
|
|
3675
3841
|
// @ts-nocheck
|
|
3676
3842
|
/* eslint-disable no-undef, no-unused-vars */
|
|
3677
3843
|
/**
|
|
@@ -3718,7 +3884,7 @@ function generateStartScript() {
|
|
|
3718
3884
|
return null;
|
|
3719
3885
|
}
|
|
3720
3886
|
|
|
3721
|
-
//
|
|
3887
|
+
// ââ€â‚¬Ã¢â€â‚¬ generateUninstallScript, setup script download helpers (setup/generators/download-gen.js)
|
|
3722
3888
|
// @ts-nocheck
|
|
3723
3889
|
/* eslint-disable no-undef, no-unused-vars */
|
|
3724
3890
|
/**
|
|
@@ -3975,7 +4141,7 @@ window.__downloadGen = {
|
|
|
3975
4141
|
updateDockerDlLabel,
|
|
3976
4142
|
};
|
|
3977
4143
|
|
|
3978
|
-
//
|
|
4144
|
+
// ââ€â‚¬Ã¢â€â‚¬ Windows .bat  if (state.nativeOs === "win") block (setup/os/win-bat.js)
|
|
3979
4145
|
// @ts-nocheck
|
|
3980
4146
|
/* eslint-disable no-undef, no-unused-vars */
|
|
3981
4147
|
/**
|
|
@@ -4027,8 +4193,8 @@ function generateWinBat(ctx) {
|
|
|
4027
4193
|
|
|
4028
4194
|
function appendDashboardInfo(arr) {
|
|
4029
4195
|
arr.push('echo.');
|
|
4030
|
-
arr.push('echo OpenClaw Dashboard: http://127.0.0.1:
|
|
4031
|
-
arr.push('echo Other reachable URLs: http://localhost:
|
|
4196
|
+
arr.push('echo OpenClaw Dashboard: http://127.0.0.1:18789');
|
|
4197
|
+
arr.push('echo Other reachable URLs: http://localhost:18789');
|
|
4032
4198
|
arr.push('echo If the dashboard asks for a Gateway Token, run: openclaw dashboard');
|
|
4033
4199
|
if (is9Router) {
|
|
4034
4200
|
arr.push('echo.');
|
|
@@ -4120,7 +4286,7 @@ function generateWinBat(ctx) {
|
|
|
4120
4286
|
return { scriptName, scriptContent };
|
|
4121
4287
|
}
|
|
4122
4288
|
|
|
4123
|
-
//
|
|
4289
|
+
// ââ€â‚¬Ã¢â€â‚¬ macOS .sh  if (state.nativeOs === "macos") block (setup/os/macos-sh.js)
|
|
4124
4290
|
// @ts-nocheck
|
|
4125
4291
|
/* eslint-disable no-undef, no-unused-vars */
|
|
4126
4292
|
/**
|
|
@@ -4238,7 +4404,7 @@ function generateMacOsSh(ctx) {
|
|
|
4238
4404
|
return { scriptName, scriptContent };
|
|
4239
4405
|
}
|
|
4240
4406
|
|
|
4241
|
-
//
|
|
4407
|
+
// ââ€â‚¬Ã¢â€â‚¬ VPS/PM2 .sh  if (state.nativeOs === "vps") block (setup/os/vps-sh.js)
|
|
4242
4408
|
// @ts-nocheck
|
|
4243
4409
|
/* eslint-disable no-undef, no-unused-vars */
|
|
4244
4410
|
/**
|
|
@@ -4352,7 +4518,7 @@ GWEOF`);
|
|
|
4352
4518
|
}
|
|
4353
4519
|
|
|
4354
4520
|
vps.push('echo ""');
|
|
4355
|
-
vps.push('echo "Dashboard: http://127.0.0.1:
|
|
4521
|
+
vps.push('echo "Dashboard: http://127.0.0.1:18789"');
|
|
4356
4522
|
if (is9Router) vps.push('echo "9Router: http://127.0.0.1:20128/dashboard"');
|
|
4357
4523
|
vps.push('echo ""');
|
|
4358
4524
|
vps.push(`echo "Restart: bash start-bot.sh"`);
|
|
@@ -4363,7 +4529,7 @@ GWEOF`);
|
|
|
4363
4529
|
return { scriptName, scriptContent };
|
|
4364
4530
|
}
|
|
4365
4531
|
|
|
4366
|
-
//
|
|
4532
|
+
// ââ€â‚¬Ã¢â€â‚¬ Linux Desktop .sh  if (state.nativeOs === "linux-desktop") block (setup/os/linux-sh.js)
|
|
4367
4533
|
// @ts-nocheck
|
|
4368
4534
|
/* eslint-disable no-undef, no-unused-vars */
|
|
4369
4535
|
/**
|
|
@@ -4426,7 +4592,7 @@ function generateLinuxSh(ctx) {
|
|
|
4426
4592
|
return { scriptName, scriptContent };
|
|
4427
4593
|
}
|
|
4428
4594
|
|
|
4429
|
-
//
|
|
4595
|
+
// ââ€â‚¬Ã¢â€â‚¬ UI init, language/channel/deploy controllers, form rendering (setup/ui/controller.js)
|
|
4430
4596
|
// @ts-nocheck
|
|
4431
4597
|
/* eslint-disable no-undef, no-unused-vars */
|
|
4432
4598
|
/**
|
|
@@ -5075,7 +5241,7 @@ function generateLinuxSh(ctx) {
|
|
|
5075
5241
|
envContent.textContent = lines.join('\n');
|
|
5076
5242
|
}
|
|
5077
5243
|
|
|
5078
|
-
//
|
|
5244
|
+
// ââ€â‚¬Ã¢â€â‚¬ Multi-bot state + UI (setup/ui/multi-bot.js) ââ€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬
|
|
5079
5245
|
// @ts-nocheck
|
|
5080
5246
|
/* eslint-disable no-undef, no-unused-vars */
|
|
5081
5247
|
/**
|
|
@@ -5424,7 +5590,7 @@ function generateLinuxSh(ctx) {
|
|
|
5424
5590
|
|
|
5425
5591
|
// ========== Step 1: Deploy Mode + OS ==========
|
|
5426
5592
|
|
|
5427
|
-
//
|
|
5593
|
+
// ââ€â‚¬Ã¢â€â‚¬ Step navigation, validation (setup/ui/steps.js) ââ€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬
|
|
5428
5594
|
// @ts-nocheck
|
|
5429
5595
|
/* eslint-disable no-undef, no-unused-vars */
|
|
5430
5596
|
/**
|
|
@@ -5584,7 +5750,7 @@ function generateLinuxSh(ctx) {
|
|
|
5584
5750
|
|
|
5585
5751
|
// ========== Step 2: Bot Config ==========
|
|
5586
5752
|
|
|
5587
|
-
//
|
|
5753
|
+
// ââ€â‚¬Ã¢â€â‚¬ generateOutput + generateNativeScript + clipboard (setup/ui/output.js)
|
|
5588
5754
|
// @ts-nocheck
|
|
5589
5755
|
/* eslint-disable no-undef, no-unused-vars */
|
|
5590
5756
|
/**
|
|
@@ -5751,15 +5917,26 @@ Write-Host "Chrome se tu dong bat Debug Mode moi khi ban dang nhap Windows (dela
|
|
|
5751
5917
|
},
|
|
5752
5918
|
list: [{
|
|
5753
5919
|
id: agentId,
|
|
5754
|
-
|
|
5920
|
+
name: botName,
|
|
5921
|
+
workspace: `/root/project/.openclaw/workspace-${agentId}`,
|
|
5755
5922
|
agentDir: `agents/${agentId}/agent`,
|
|
5756
5923
|
model: { primary: state.config.model, fallbacks: [] },
|
|
5757
5924
|
}],
|
|
5758
5925
|
},
|
|
5759
|
-
commands: {
|
|
5926
|
+
commands: {
|
|
5927
|
+
native: 'auto',
|
|
5928
|
+
nativeSkills: 'auto',
|
|
5929
|
+
restart: true,
|
|
5930
|
+
ownerDisplay: 'raw',
|
|
5931
|
+
...(state.config.skills.includes('scheduler') ? { ownerAllowFrom: ['*'] } : {}),
|
|
5932
|
+
},
|
|
5760
5933
|
channels: ch.channelConfig,
|
|
5761
|
-
tools: {
|
|
5762
|
-
|
|
5934
|
+
tools: {
|
|
5935
|
+
profile: 'full',
|
|
5936
|
+
...(state.config.skills.includes('scheduler') ? { alsoAllow: ['group:automation'] } : {}),
|
|
5937
|
+
exec: { host: 'gateway', security: 'full', ask: 'off' },
|
|
5938
|
+
},
|
|
5939
|
+
gateway: common.buildGatewayConfig(18789, state.deployMode, getGatewayAllowedOrigins(18789), state.nativeOs || ''),
|
|
5763
5940
|
};
|
|
5764
5941
|
|
|
5765
5942
|
// 9Router: add proxy endpoint config under models.providers
|
|
@@ -5849,7 +6026,7 @@ Write-Host "Chrome se tu dong bat Debug Mode moi khi ban dang nhap Windows (dela
|
|
|
5849
6026
|
clawConfig.agents.list = multiBotAgentMetas.map((meta) => ({
|
|
5850
6027
|
id: meta.agentId,
|
|
5851
6028
|
name: meta.name,
|
|
5852
|
-
workspace:
|
|
6029
|
+
workspace: `/root/project/.openclaw/${meta.workspaceDir}`,
|
|
5853
6030
|
agentDir: `agents/${meta.agentId}/agent`,
|
|
5854
6031
|
model: { primary: state.config.model, fallbacks: [] },
|
|
5855
6032
|
}));
|
|
@@ -5880,6 +6057,7 @@ Write-Host "Chrome se tu dong bat Debug Mode moi khi ban dang nhap Windows (dela
|
|
|
5880
6057
|
};
|
|
5881
6058
|
clawConfig.tools = {
|
|
5882
6059
|
...(clawConfig.tools || {}),
|
|
6060
|
+
...(state.config.skills.includes('scheduler') ? { alsoAllow: ['group:automation'] } : {}),
|
|
5883
6061
|
agentToAgent: {
|
|
5884
6062
|
enabled: true,
|
|
5885
6063
|
allow: multiBotAgentMetas.map((meta) => meta.agentId),
|
|
@@ -5974,6 +6152,9 @@ model:
|
|
|
5974
6152
|
dockerfileSkillInstallMode: 'build',
|
|
5975
6153
|
runtimeCommandParts: [
|
|
5976
6154
|
pluginInstallCmd,
|
|
6155
|
+
// zalouser: use npm install (not openclaw plugins install) to avoid openclaw.json writes
|
|
6156
|
+
// ClawHub build gives error:not configured; npm version works correctly
|
|
6157
|
+
state.channel === 'zalo-personal' ? 'ensure_zalouser' : '',
|
|
5977
6158
|
'while true; do sleep 5; openclaw devices approve --latest 2>/dev/null || true; done >/dev/null 2>&1 &'
|
|
5978
6159
|
].filter(Boolean),
|
|
5979
6160
|
plainSingleExtraHosts: true,
|
|
@@ -5983,6 +6164,7 @@ model:
|
|
|
5983
6164
|
});
|
|
5984
6165
|
const dockerfile = dockerArtifacts.dockerfile;
|
|
5985
6166
|
const compose = dockerArtifacts.compose;
|
|
6167
|
+
const entrypointScript = dockerArtifacts.entrypointScript;
|
|
5986
6168
|
// isMultiBot => unified into isMultiBot above
|
|
5987
6169
|
setOutput('out-dockerfile', dockerfile);
|
|
5988
6170
|
setOutput('out-compose', compose);
|
|
@@ -6396,6 +6578,7 @@ fi
|
|
|
6396
6578
|
if (!isNativeMode) {
|
|
6397
6579
|
sharedFiles['docker/openclaw/Dockerfile'] = dockerfile;
|
|
6398
6580
|
sharedFiles['docker/openclaw/docker-compose.yml'] = compose;
|
|
6581
|
+
sharedFiles['docker/openclaw/entrypoint.sh'] = entrypointScript;
|
|
6399
6582
|
sharedFiles['docker/openclaw/.env'] = rootEnvContent;
|
|
6400
6583
|
}
|
|
6401
6584
|
sharedFiles[globalThis.__openclawCommon.TELEGRAM_SETUP_GUIDE_FILENAME] = buildTelegramPostInstallChecklist();
|
|
@@ -6467,6 +6650,7 @@ fi
|
|
|
6467
6650
|
if (!isNativeMode) {
|
|
6468
6651
|
singleFiles['docker/openclaw/Dockerfile'] = dockerfile;
|
|
6469
6652
|
singleFiles['docker/openclaw/docker-compose.yml'] = compose;
|
|
6653
|
+
singleFiles['docker/openclaw/entrypoint.sh'] = entrypointScript;
|
|
6470
6654
|
singleFiles['docker/openclaw/.env'] = rootEnvContent;
|
|
6471
6655
|
}
|
|
6472
6656
|
state._generatedFiles = singleFiles;
|
|
@@ -6635,38 +6819,46 @@ fi
|
|
|
6635
6819
|
// ========== Zalo Personal Login Guide (post-setup) ==========
|
|
6636
6820
|
function generateZaloOnboardGuide() {
|
|
6637
6821
|
const lang = document.getElementById('cfg-language')?.value || 'vi';
|
|
6638
|
-
|
|
6639
|
-
|
|
6640
|
-
docker compose up -d --force-recreate ai-bot
|
|
6641
|
-
docker compose exec ai-bot openclaw channels status --probe`);
|
|
6822
|
+
const isVi = lang === 'vi';
|
|
6823
|
+
const containerName = state.botCount > 1 ? 'openclaw-multibot' : 'openclaw-bot';
|
|
6642
6824
|
|
|
6643
|
-
|
|
6825
|
+
setOutput('out-zalo-onboard-cmd', `# ${isVi ? 'Bước 1: Dọn dẹp session cũ' : 'Step 1: Clean up old session'}
|
|
6826
|
+
docker exec ${containerName} rm -f /root/project/.openclaw/credentials/zalouser/credentials.json
|
|
6827
|
+
|
|
6828
|
+
# ${isVi ? 'Bước 2: Kích hoạt màn hình login QR (Quét mã trên terminal)' : 'Step 2: Start login QR (Scan on terminal)'}
|
|
6829
|
+
docker exec -it ${containerName} openclaw channels login --channel zalouser --verbose
|
|
6830
|
+
|
|
6831
|
+
# ${isVi ? 'Bước 3: Khởi động lại container sau khi login thành công' : 'Step 3: Restart container after successful login'}
|
|
6832
|
+
docker restart ${containerName}`);
|
|
6833
|
+
|
|
6834
|
+
if (isVi) {
|
|
6644
6835
|
setOutput('out-zalo-onboard-guide', `┌─────────────────────────────────────────────────────┐
|
|
6645
|
-
│ Chạy lệnh bên trái để
|
|
6836
|
+
│ Chạy các lệnh bên trái để đăng nhập Zalo Personal │
|
|
6837
|
+
│ theo quy trình chuẩn 4 bước. │
|
|
6646
6838
|
├─────────────────────────────────────────────────────┤
|
|
6647
|
-
│ 1.
|
|
6648
|
-
│
|
|
6649
|
-
│
|
|
6650
|
-
│
|
|
6651
|
-
│ docker cp
|
|
6839
|
+
│ 1. Lệnh 1 xoá file credentials.json cũ để tránh │
|
|
6840
|
+
│ lỗi xung đột "Already linked". │
|
|
6841
|
+
│ 2. Lệnh 2 mở màn hình login. Quét mã QR hiện trên │
|
|
6842
|
+
│ terminal hoặc lấy file từ container: │
|
|
6843
|
+
│ docker cp ${containerName}:/tmp/openclaw/ │
|
|
6652
6844
|
│ openclaw-zalouser-qr-default.png . │
|
|
6653
|
-
│
|
|
6654
|
-
│
|
|
6655
|
-
│
|
|
6845
|
+
│ 3. Sau khi quét xong và terminal báo thành công, │
|
|
6846
|
+
│ nhấn Ctrl+C để thoát. │
|
|
6847
|
+
│ 4. Chạy Lệnh 3 để restart container giúp nhận tin. │
|
|
6656
6848
|
└─────────────────────────────────────────────────────┘`);
|
|
6657
6849
|
} else {
|
|
6658
6850
|
setOutput('out-zalo-onboard-guide', `┌─────────────────────────────────────────────────────┐
|
|
6659
|
-
│ Run the
|
|
6851
|
+
│ Run the commands on the left to login Zalo Personal│
|
|
6852
|
+
│ following the standard 4-step workflow. │
|
|
6660
6853
|
├─────────────────────────────────────────────────────┤
|
|
6661
|
-
│ 1.
|
|
6662
|
-
│
|
|
6663
|
-
│
|
|
6664
|
-
│
|
|
6665
|
-
│ docker cp
|
|
6854
|
+
│ 1. Command 1 deletes the old credentials.json file │
|
|
6855
|
+
│ to avoid "Already linked" conflicts. │
|
|
6856
|
+
│ 2. Command 2 opens the login interface. Scan the │
|
|
6857
|
+
│ QR on your terminal or copy the image file: │
|
|
6858
|
+
│ docker cp ${containerName}:/tmp/openclaw/ │
|
|
6666
6859
|
│ openclaw-zalouser-qr-default.png . │
|
|
6667
|
-
│
|
|
6668
|
-
│
|
|
6669
|
-
│ 7. Run channels status --probe; it should run. │
|
|
6860
|
+
│ 3. Once scanned and successful, press Ctrl+C. │
|
|
6861
|
+
│ 4. Run Command 3 to restart the container. │
|
|
6670
6862
|
└─────────────────────────────────────────────────────┘`);
|
|
6671
6863
|
}
|
|
6672
6864
|
}
|