create-openclaw-bot 5.4.2 → 5.6.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 +19 -16
- package/README.vi.md +20 -17
- package/{cli.js → dist/cli.js} +295 -224
- package/dist/setup/data/channels.js +164 -0
- package/dist/setup/data/header.js +80 -0
- package/dist/setup/data/index.js +73 -0
- package/dist/setup/data/plugins.js +60 -0
- package/dist/setup/data/providers.js +121 -0
- package/dist/setup/data/skills.js +169 -0
- package/dist/setup/shared/common-gen.js +223 -0
- package/dist/setup/shared/docker-gen.js +359 -0
- package/dist/setup/shared/install-gen.js +485 -0
- package/dist/setup/shared/workspace-gen.js +434 -0
- package/{setup.js → dist/setup.js} +833 -1153
- package/package.json +10 -7
- package/.github/workflows/check-openclaw-update.yml +0 -106
- package/CHANGELOG.md +0 -591
- package/CHANGELOG.vi.md +0 -577
- package/docs/SETUP.md +0 -532
- package/docs/SETUP.vi.md +0 -439
- package/docs/ai-providers.md +0 -144
- package/docs/ai-providers.vi.md +0 -144
- package/docs/browser-automation-guide.md +0 -207
- package/docs/faq.md +0 -63
- package/docs/faq.vi.md +0 -63
- package/docs/hardware-guide.md +0 -55
- package/docs/hardware-guide.vi.md +0 -55
- package/docs/install-docker.md +0 -161
- package/docs/install-docker.vi.md +0 -161
- package/docs/install-native.md +0 -96
- package/docs/install-native.vi.md +0 -96
- package/docs/preview.png +0 -0
- package/docs/skills-plugins-guide.md +0 -126
- package/index.html +0 -589
- package/patch-tray.js +0 -7
- package/style.css +0 -1653
- package/upgrade.ps1 +0 -90
- package/upgrade.sh +0 -93
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
(function (root) {
|
|
3
|
+
function encodeBase64Utf8(value) {
|
|
4
|
+
if (typeof Buffer !== 'undefined') {
|
|
5
|
+
return Buffer.from(String(value), 'utf8').toString('base64');
|
|
6
|
+
}
|
|
7
|
+
return btoa(String.fromCharCode(...new TextEncoder().encode(String(value))));
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function indentBlock(text, spaces) {
|
|
11
|
+
const prefix = ' '.repeat(spaces);
|
|
12
|
+
return String(text).split('\n').map((line) => `${prefix}${line}`).join('\n');
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function build9RouterSmartRouteSyncScript(dbPath) {
|
|
16
|
+
return `const fs=require('fs');const INTERVAL=30000;const p='${dbPath}';
|
|
17
|
+
const PM={codex:['cx/gpt-5.4','cx/gpt-5.3-codex','cx/gpt-5.3-codex-high','cx/gpt-5.2-codex','cx/gpt-5.2','cx/gpt-5.1-codex-max','cx/gpt-5.1-codex','cx/gpt-5.1','cx/gpt-5-codex'],'claude-code':['cc/claude-opus-4-6','cc/claude-sonnet-4-6','cc/claude-opus-4-5-20251101','cc/claude-sonnet-4-5-20250929','cc/claude-haiku-4-5-20251001'],github:['gh/gpt-5.4','gh/gpt-5.3-codex','gh/gpt-5.2-codex','gh/gpt-5.2','gh/gpt-5.1-codex-max','gh/gpt-5.1-codex','gh/gpt-5.1','gh/gpt-5','gh/gpt-4.1','gh/gpt-4o','gh/claude-opus-4.6','gh/claude-sonnet-4.6','gh/claude-sonnet-4.5','gh/claude-opus-4.5','gh/claude-haiku-4.5','gh/gemini-3-pro-preview','gh/gemini-3-flash-preview','gh/gemini-2.5-pro'],cursor:['cu/default','cu/claude-4.6-opus-max','cu/claude-4.5-opus-high-thinking','cu/claude-4.5-sonnet-thinking','cu/claude-4.5-sonnet','cu/gpt-5.3-codex','cu/gpt-5.2-codex','cu/gemini-3-flash-preview'],'kilo:['kc/anthropic/claude-sonnet-4-20250514','kc/anthropic/claude-opus-4-20250514','kc/google/gemini-2.5-pro','kc/google/gemini-2.5-flash','kc/openai/gpt-4.1','kc/deepseek/deepseek-chat'],'cline':['cl/anthropic/claude-sonnet-4.6','cl/anthropic/claude-opus-4.6','cl/openai/gpt-5.3-codex','cl/openai/gpt-5.4','cl/google/gemini-3.1-pro-preview'],'gemini-cli':['gc/gemini-3-flash-preview','gc/gemini-3-pro-preview'],'iflow':['if/qwen3-coder-plus','if/kimi-k2','if/kimi-k2-thinking','if/glm-4.7','if/deepseek-r1','if/deepseek-v3.2','if/deepseek-v3','if/qwen3-max','if/qwen3-235b','if/iflow-rome-30ba3b'],'qwen':['qw/qwen3-coder-plus','qw/qwen3-coder-flash','qw/vision-model','qw/coder-model'],'kiro':['kr/claude-sonnet-4.5','kr/claude-haiku-4.5','kr/deepseek-3.2','kr/deepseek-3.1','kr/qwen3-coder-next'],'ollama':['ollama/gemma4:e2b','ollama/gemma4:e4b','ollama/gemma4:26b','ollama/gemma4:31b','ollama/qwen3.5','ollama/kimi-k2.5','ollama/glm-5','ollama/glm-4.7-flash','ollama/minimax-m2.5','ollama/gpt-oss:120b'],'kimi-coding':['kmc/kimi-k2.5','kmc/kimi-k2.5-thinking','kmc/kimi-latest'],'glm':['glm/glm-5.1','glm/glm-5','glm/glm-4.7'],'glm-cn':['glm/glm-5.1','glm/glm-5','glm/glm-4.7'],'minimax':['minimax/MiniMax-M2.7','minimax/MiniMax-M2.5','minimax/MiniMax-M2.1'],'kimi':['kimi/kimi-k2.5','kimi/kimi-k2.5-thinking','kimi/kimi-latest'],'deepseek':['deepseek/deepseek-chat','deepseek/deepseek-reasoner'],'xai':['xai/grok-4','xai/grok-4-fast-reasoning','xai/grok-code-fast-1'],'mistral':['mistral/mistral-large-latest','mistral/codestral-latest'],'groq':['groq/llama-3.3-70b-versatile','groq/openai/gpt-oss-120b'],'cerebras':['cerebras/gpt-oss-120b'],'alicode':['alicode/qwen3.5-plus','alicode/qwen3-coder-plus'],'openai':['openai/gpt-4o','openai/gpt-4.1'],'anthropic':['anthropic/claude-sonnet-4','anthropic/claude-haiku-3.5'],'gemini':['gemini/gemini-2.5-flash','gemini/gemini-2.5-pro']};
|
|
18
|
+
console.log('[sync-combo] 9Router sync loop started...');
|
|
19
|
+
const sync = async () => {
|
|
20
|
+
try {
|
|
21
|
+
let db = {};
|
|
22
|
+
try { db = JSON.parse(fs.readFileSync(p, 'utf8')); } catch(e){}
|
|
23
|
+
if (!db.combos) db.combos = [];
|
|
24
|
+
const removeSmartRoute = () => {
|
|
25
|
+
const next = db.combos.filter(x => x.id !== 'smart-route');
|
|
26
|
+
if (next.length !== db.combos.length) {
|
|
27
|
+
db.combos = next;
|
|
28
|
+
fs.writeFileSync(p, JSON.stringify(db, null, 2));
|
|
29
|
+
console.log('[sync-combo] Removed smart-route (no active providers)');
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
const res = await fetch('http://localhost:20128/api/providers');
|
|
33
|
+
if (!res.ok) { console.log('[sync-combo] API not ready, retrying...'); return; }
|
|
34
|
+
const d = await res.json();
|
|
35
|
+
const a = (d.connections || []).filter(c => c && c.provider && c.isActive !== false && !c.disabled).map(c => c.provider);
|
|
36
|
+
if (!a.length) { removeSmartRoute(); return; }
|
|
37
|
+
const PREF = ['openai','anthropic','claude-code','codex','cursor','github','cline','kimi','minimax','deepseek','glm','alicode','xai','mistral','kilo','kiro','iflow','qwen','gemini-cli','ollama'];
|
|
38
|
+
a.sort((x, y) => (PREF.indexOf(x) === -1 ? 99 : PREF.indexOf(x)) - (PREF.indexOf(y) === -1 ? 99 : PREF.indexOf(y)));
|
|
39
|
+
const m = a.flatMap(pv => PM[pv] || []);
|
|
40
|
+
if (!m.length) { removeSmartRoute(); return; }
|
|
41
|
+
const c = { id: 'smart-route', name: 'smart-route', alias: 'smart-route', models: m };
|
|
42
|
+
const i = db.combos.findIndex(x => x.id === 'smart-route');
|
|
43
|
+
if (i >= 0) {
|
|
44
|
+
if (JSON.stringify(db.combos[i].models) !== JSON.stringify(c.models)) {
|
|
45
|
+
db.combos[i] = c;
|
|
46
|
+
fs.writeFileSync(p, JSON.stringify(db, null, 2));
|
|
47
|
+
console.log('[sync-combo] Updated smart-route: ' + c.models.length + ' models');
|
|
48
|
+
}
|
|
49
|
+
} else {
|
|
50
|
+
db.combos.push(c);
|
|
51
|
+
fs.writeFileSync(p, JSON.stringify(db, null, 2));
|
|
52
|
+
console.log('[sync-combo] Created smart-route: ' + c.models.length + ' models');
|
|
53
|
+
}
|
|
54
|
+
} catch (e) {}
|
|
55
|
+
};
|
|
56
|
+
setTimeout(sync, 5000);
|
|
57
|
+
setInterval(sync, INTERVAL);`;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function build9RouterComposeEntrypointScript(syncScriptBase64) {
|
|
61
|
+
return [
|
|
62
|
+
'npm install -g 9router',
|
|
63
|
+
`node -e "require('fs').writeFileSync('/tmp/sync.js',Buffer.from('${syncScriptBase64}','base64').toString())"`,
|
|
64
|
+
'node /tmp/sync.js > /tmp/sync.log 2>&1 &',
|
|
65
|
+
'exec 9router -n -l -H 0.0.0.0 -p 20128 --skip-update'
|
|
66
|
+
].join('\n');
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function buildGatewayPatchCmd() {
|
|
70
|
+
return `node -e \\"const fs=require('fs'),os=require('os'),p='/root/.openclaw/openclaw.json';if(fs.existsSync(p)){const c=JSON.parse(fs.readFileSync(p,'utf8'));const a=new Set(['http://localhost:18791','http://127.0.0.1:18791','http://0.0.0.0:18791']);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 + ':18791');}}c.tools=Object.assign({},c.tools,{profile:'full',exec:{host:'gateway',security:'full',ask:'off'}});c.gateway=Object.assign({},c.gateway,{port:18791,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));}\\"`;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function buildDockerArtifacts(options) {
|
|
74
|
+
const {
|
|
75
|
+
openClawNpmSpec,
|
|
76
|
+
openClawRuntimePackages,
|
|
77
|
+
is9Router,
|
|
78
|
+
isLocal,
|
|
79
|
+
isMultiBot,
|
|
80
|
+
hasBrowser,
|
|
81
|
+
selectedModel,
|
|
82
|
+
agentId,
|
|
83
|
+
allSkills = [],
|
|
84
|
+
dockerfileSkillInstallMode = 'none',
|
|
85
|
+
runtimeCommandParts = [],
|
|
86
|
+
volumeMount = '../../.openclaw:/root/.openclaw',
|
|
87
|
+
singleComposeName = 'oc-bot',
|
|
88
|
+
multiComposeName = 'oc-multibot',
|
|
89
|
+
singleAppContainerName = 'openclaw-bot',
|
|
90
|
+
multiAppContainerName = 'openclaw-multibot',
|
|
91
|
+
singleRouterContainerName = '9router',
|
|
92
|
+
multiRouterContainerName = '9router-multibot',
|
|
93
|
+
singleOllamaContainerName = 'ollama',
|
|
94
|
+
multiOllamaContainerName = 'ollama-multibot',
|
|
95
|
+
plainSingleExtraHosts = false,
|
|
96
|
+
multiOllamaNumParallel = 1,
|
|
97
|
+
singleOllamaNumParallel = 1,
|
|
98
|
+
emitBrowserInstall = true,
|
|
99
|
+
} = options;
|
|
100
|
+
|
|
101
|
+
const browserAptExtra = hasBrowser ? ' xvfb' : '';
|
|
102
|
+
const browserInstallLines = hasBrowser && emitBrowserInstall
|
|
103
|
+
? [
|
|
104
|
+
'',
|
|
105
|
+
'# Browser Automation: Playwright engine (needed for native CDP)',
|
|
106
|
+
'RUN npm install -g agent-browser playwright \\',
|
|
107
|
+
' && npx playwright install chromium --with-deps \\',
|
|
108
|
+
' && ln -f -s /root/.cache/ms-playwright/chromium-*/chrome-linux*/chrome /usr/bin/google-chrome',
|
|
109
|
+
'',
|
|
110
|
+
''
|
|
111
|
+
].join('\n')
|
|
112
|
+
: '';
|
|
113
|
+
const skillLines = dockerfileSkillInstallMode === 'build' && allSkills.length > 0
|
|
114
|
+
? `\n# Install skills (ClawHub)\n${allSkills.map((skill) => `RUN openclaw skills install ${skill} || echo "Warning: Failed to install ${skill} due to rate limits."`).join('\n')}\n`
|
|
115
|
+
: '';
|
|
116
|
+
const patchLine = `RUN node -e "const fs=require('fs');const path=require('path');const dir='/usr/local/lib/node_modules/openclaw/dist';const from='\\t\\t\\t\\t\\tonAgentRunStart: (runId) => {';const to='\\t\\t\\t\\t\\ttimeoutOverrideSeconds: Math.max(1, Math.ceil(timeoutMs / 1e3)),\\n\\t\\t\\t\\t\\tonAgentRunStart: (runId) => {';const files=fs.readdirSync(dir).filter(n=>/\\.js$/.test(n));let patched=0;for(const file of files){const p=path.join(dir,file);let s='';try{s=fs.readFileSync(p,'utf8');}catch{continue;}if(s.includes(to)||!s.includes(from))continue;s=s.replace(from,to);fs.writeFileSync(p,s);patched++;}if(!patched){process.exit(0);}"`;
|
|
117
|
+
|
|
118
|
+
// Dynamic runtime configuration injection for container internal IPs
|
|
119
|
+
const setupInternalIpScript = `const fs=require('fs'),os=require('os'),p='/root/.openclaw/openclaw.json';if(fs.existsSync(p)){const c=JSON.parse(fs.readFileSync(p,'utf8'));const a=new Set(['http://localhost:18791','http://127.0.0.1:18791','http://0.0.0.0:18791']);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 + ':18791');}}c.tools=Object.assign({},c.tools,{profile:'full',exec:{host:'gateway',security:'full',ask:'off'}});c.gateway=Object.assign({},c.gateway,{port:18791,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));}`;
|
|
120
|
+
const setupInternalIpB64 = encodeBase64Utf8(setupInternalIpScript);
|
|
121
|
+
|
|
122
|
+
const runtimeParts = runtimeCommandParts.filter(Boolean);
|
|
123
|
+
runtimeParts.unshift(`node -e "eval(Buffer.from('${setupInternalIpB64}','base64').toString())" &&`);
|
|
124
|
+
if (hasBrowser) {
|
|
125
|
+
runtimeParts.push('(Xvfb :99 -screen 0 1280x720x24 > /dev/null 2>&1 &) && export DISPLAY=:99 && openclaw gateway run');
|
|
126
|
+
} else {
|
|
127
|
+
runtimeParts.push('openclaw gateway run');
|
|
128
|
+
}
|
|
129
|
+
const dockerfile = `FROM node:22-slim
|
|
130
|
+
|
|
131
|
+
RUN apt-get update && apt-get install -y git curl${browserAptExtra} && rm -rf /var/lib/apt/lists/*
|
|
132
|
+
${browserInstallLines}
|
|
133
|
+
ARG OPENCLAW_VER="${openClawNpmSpec}"
|
|
134
|
+
RUN npm install -g ${openClawNpmSpec} ${openClawRuntimePackages}${skillLines}
|
|
135
|
+
${patchLine}
|
|
136
|
+
WORKDIR /root/.openclaw
|
|
137
|
+
|
|
138
|
+
EXPOSE 18791
|
|
139
|
+
|
|
140
|
+
CMD sh -c "${runtimeParts.join(' ')}"`;
|
|
141
|
+
|
|
142
|
+
const syncScript = build9RouterSmartRouteSyncScript('/root/.9router/db.json');
|
|
143
|
+
const syncScriptBase64 = encodeBase64Utf8(syncScript);
|
|
144
|
+
const docker9RouterEntrypointScript = build9RouterComposeEntrypointScript(syncScriptBase64);
|
|
145
|
+
const extraHostsBlock = ` extra_hosts:\n - "host.docker.internal:host-gateway"`;
|
|
146
|
+
|
|
147
|
+
let compose;
|
|
148
|
+
if (isMultiBot) {
|
|
149
|
+
const dependsOn = is9Router
|
|
150
|
+
? ' depends_on:\n - 9router\n'
|
|
151
|
+
: isLocal
|
|
152
|
+
? ' depends_on:\n ollama:\n condition: service_healthy\n'
|
|
153
|
+
: '';
|
|
154
|
+
const extraHosts = hasBrowser ? `${extraHostsBlock}\n` : '';
|
|
155
|
+
if (is9Router) {
|
|
156
|
+
compose = `name: ${multiComposeName}
|
|
157
|
+
services:
|
|
158
|
+
ai-bot:
|
|
159
|
+
build: .
|
|
160
|
+
container_name: ${multiAppContainerName}
|
|
161
|
+
restart: always
|
|
162
|
+
env_file:
|
|
163
|
+
- .env
|
|
164
|
+
${dependsOn}${extraHosts} volumes:
|
|
165
|
+
- ${volumeMount}
|
|
166
|
+
ports:
|
|
167
|
+
- "18791:18791"
|
|
168
|
+
|
|
169
|
+
9router:
|
|
170
|
+
image: node:22-slim
|
|
171
|
+
container_name: ${multiRouterContainerName}
|
|
172
|
+
restart: always
|
|
173
|
+
entrypoint:
|
|
174
|
+
- /bin/sh
|
|
175
|
+
- -c
|
|
176
|
+
- |
|
|
177
|
+
${indentBlock(docker9RouterEntrypointScript, 8)}
|
|
178
|
+
environment:
|
|
179
|
+
- PORT=20128
|
|
180
|
+
- HOSTNAME=0.0.0.0
|
|
181
|
+
- CI=true
|
|
182
|
+
volumes:
|
|
183
|
+
- 9router-data:/root/.9router
|
|
184
|
+
ports:
|
|
185
|
+
- "20128:20128"
|
|
186
|
+
|
|
187
|
+
volumes:
|
|
188
|
+
9router-data:`;
|
|
189
|
+
} else if (isLocal) {
|
|
190
|
+
const ollamaModelTag = String(selectedModel || 'ollama/gemma4:e2b').replace('ollama/', '');
|
|
191
|
+
compose = `name: ${multiComposeName}
|
|
192
|
+
services:
|
|
193
|
+
ai-bot:
|
|
194
|
+
build: .
|
|
195
|
+
container_name: ${multiAppContainerName}
|
|
196
|
+
restart: always
|
|
197
|
+
env_file:
|
|
198
|
+
- .env
|
|
199
|
+
${dependsOn}${extraHosts} volumes:
|
|
200
|
+
- ${volumeMount}
|
|
201
|
+
ports:
|
|
202
|
+
- "18791:18791"
|
|
203
|
+
|
|
204
|
+
ollama:
|
|
205
|
+
image: ollama/ollama:latest
|
|
206
|
+
container_name: ${multiOllamaContainerName}
|
|
207
|
+
restart: always
|
|
208
|
+
environment:
|
|
209
|
+
- OLLAMA_KEEP_ALIVE=24h
|
|
210
|
+
- OLLAMA_NUM_PARALLEL=${multiOllamaNumParallel}
|
|
211
|
+
volumes:
|
|
212
|
+
- ollama-data:/root/.ollama
|
|
213
|
+
entrypoint:
|
|
214
|
+
- /bin/sh
|
|
215
|
+
- -c
|
|
216
|
+
- |
|
|
217
|
+
ollama serve &
|
|
218
|
+
until ollama list > /dev/null 2>&1; do sleep 1; done
|
|
219
|
+
ollama pull ${ollamaModelTag}
|
|
220
|
+
wait
|
|
221
|
+
healthcheck:
|
|
222
|
+
test: ["CMD-SHELL", "ollama list > /dev/null 2>&1"]
|
|
223
|
+
interval: 10s
|
|
224
|
+
timeout: 5s
|
|
225
|
+
retries: 10
|
|
226
|
+
start_period: 30s
|
|
227
|
+
|
|
228
|
+
volumes:
|
|
229
|
+
ollama-data:`;
|
|
230
|
+
} else {
|
|
231
|
+
compose = `name: ${multiComposeName}
|
|
232
|
+
services:
|
|
233
|
+
ai-bot:
|
|
234
|
+
build: .
|
|
235
|
+
container_name: ${multiAppContainerName}
|
|
236
|
+
restart: always
|
|
237
|
+
env_file:
|
|
238
|
+
- .env
|
|
239
|
+
${extraHosts} volumes:
|
|
240
|
+
- ${volumeMount}
|
|
241
|
+
ports:
|
|
242
|
+
- "18791:18791"`;
|
|
243
|
+
}
|
|
244
|
+
} else if (is9Router) {
|
|
245
|
+
compose = `name: ${singleComposeName}
|
|
246
|
+
services:
|
|
247
|
+
ai-bot:
|
|
248
|
+
build: .
|
|
249
|
+
container_name: ${singleAppContainerName}
|
|
250
|
+
restart: always
|
|
251
|
+
env_file:
|
|
252
|
+
- .env
|
|
253
|
+
depends_on:
|
|
254
|
+
- 9router
|
|
255
|
+
${hasBrowser ? `${extraHostsBlock}\n` : ''} volumes:
|
|
256
|
+
- ${volumeMount}
|
|
257
|
+
ports:
|
|
258
|
+
- "18791:18791"
|
|
259
|
+
|
|
260
|
+
9router:
|
|
261
|
+
image: node:22-slim
|
|
262
|
+
container_name: ${singleRouterContainerName}
|
|
263
|
+
restart: always
|
|
264
|
+
entrypoint:
|
|
265
|
+
- /bin/sh
|
|
266
|
+
- -c
|
|
267
|
+
- |
|
|
268
|
+
${indentBlock(docker9RouterEntrypointScript, 8)}
|
|
269
|
+
environment:
|
|
270
|
+
- PORT=20128
|
|
271
|
+
- HOSTNAME=0.0.0.0
|
|
272
|
+
- CI=true
|
|
273
|
+
volumes:
|
|
274
|
+
- 9router-data:/root/.9router
|
|
275
|
+
ports:
|
|
276
|
+
- "20128:20128"
|
|
277
|
+
|
|
278
|
+
volumes:
|
|
279
|
+
9router-data:`;
|
|
280
|
+
} else if (isLocal) {
|
|
281
|
+
const ollamaModelTag = String(selectedModel || 'ollama/gemma4:e2b').replace('ollama/', '');
|
|
282
|
+
compose = `name: ${singleComposeName}
|
|
283
|
+
services:
|
|
284
|
+
ai-bot:
|
|
285
|
+
build: .
|
|
286
|
+
container_name: ${singleAppContainerName}
|
|
287
|
+
restart: always
|
|
288
|
+
env_file: .env
|
|
289
|
+
depends_on:
|
|
290
|
+
ollama:
|
|
291
|
+
condition: service_healthy
|
|
292
|
+
${hasBrowser ? `${extraHostsBlock}\n` : ''} ports:
|
|
293
|
+
- "18791:18791"
|
|
294
|
+
volumes:
|
|
295
|
+
- ${volumeMount}
|
|
296
|
+
|
|
297
|
+
ollama:
|
|
298
|
+
image: ollama/ollama:latest
|
|
299
|
+
container_name: ${singleOllamaContainerName}
|
|
300
|
+
restart: always
|
|
301
|
+
environment:
|
|
302
|
+
- OLLAMA_KEEP_ALIVE=24h
|
|
303
|
+
- OLLAMA_NUM_PARALLEL=${singleOllamaNumParallel}
|
|
304
|
+
volumes:
|
|
305
|
+
- ollama-data:/root/.ollama
|
|
306
|
+
entrypoint:
|
|
307
|
+
- /bin/sh
|
|
308
|
+
- -c
|
|
309
|
+
- |
|
|
310
|
+
ollama serve &
|
|
311
|
+
until ollama list > /dev/null 2>&1; do sleep 1; done
|
|
312
|
+
ollama pull ${ollamaModelTag}
|
|
313
|
+
wait
|
|
314
|
+
healthcheck:
|
|
315
|
+
test: ["CMD-SHELL", "ollama list > /dev/null 2>&1"]
|
|
316
|
+
interval: 10s
|
|
317
|
+
timeout: 5s
|
|
318
|
+
retries: 10
|
|
319
|
+
start_period: 30s
|
|
320
|
+
|
|
321
|
+
volumes:
|
|
322
|
+
ollama-data:`;
|
|
323
|
+
} else {
|
|
324
|
+
compose = `name: ${singleComposeName}
|
|
325
|
+
services:
|
|
326
|
+
ai-bot:
|
|
327
|
+
build: .
|
|
328
|
+
container_name: ${singleAppContainerName}
|
|
329
|
+
restart: always
|
|
330
|
+
env_file:
|
|
331
|
+
- .env
|
|
332
|
+
${plainSingleExtraHosts ? `${extraHostsBlock}\n` : ''} volumes:
|
|
333
|
+
- ${volumeMount}
|
|
334
|
+
ports:
|
|
335
|
+
- "18791:18791"`;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
return {
|
|
339
|
+
dockerfile,
|
|
340
|
+
compose,
|
|
341
|
+
syncScript,
|
|
342
|
+
docker9RouterEntrypointScript,
|
|
343
|
+
gatewayPatchCmd: buildGatewayPatchCmd(),
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
root.__openclawDockerGen = {
|
|
348
|
+
encodeBase64Utf8,
|
|
349
|
+
indentBlock,
|
|
350
|
+
build9RouterSmartRouteSyncScript,
|
|
351
|
+
build9RouterComposeEntrypointScript,
|
|
352
|
+
buildGatewayPatchCmd,
|
|
353
|
+
buildDockerArtifacts,
|
|
354
|
+
};
|
|
355
|
+
|
|
356
|
+
})(typeof globalThis !== 'undefined' ? globalThis : {});
|
|
357
|
+
if (typeof exports !== 'undefined' && typeof globalThis !== 'undefined' && globalThis.__openclawDockerGen) {
|
|
358
|
+
Object.assign(exports, globalThis.__openclawDockerGen);
|
|
359
|
+
}
|