create-openclaw-bot 5.6.14 → 5.7.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 +11 -12
- package/README.vi.md +11 -12
- package/dist/cli.js +85 -263
- package/dist/setup/data/channels.js +1 -0
- package/dist/setup/shared/bot-config-gen.js +467 -0
- package/dist/setup/shared/common-gen.js +1 -1
- package/dist/setup/shared/docker-gen.js +443 -442
- package/dist/setup/shared/workspace-gen.js +129 -10
- package/dist/setup.js +5929 -5472
- package/package.json +39 -39
|
@@ -1,442 +1,443 @@
|
|
|
1
|
-
// @ts-nocheck
|
|
2
|
-
(function (root) {
|
|
3
|
-
const common = (typeof globalThis !== 'undefined' && globalThis.__openclawCommon) || {};
|
|
4
|
-
const SUPPORTED_CODEX_MODELS = common.SUPPORTED_CODEX_MODELS || ['cx/gpt-5.4', 'cx/gpt-5.3-codex', 'cx/gpt-5.2', 'cx/gpt-5.4-mini'];
|
|
5
|
-
const SMART_ROUTE_PROVIDER_MODELS = common.SMART_ROUTE_PROVIDER_MODELS || { codex: SUPPORTED_CODEX_MODELS };
|
|
6
|
-
const SMART_ROUTE_PROVIDER_ORDER = common.SMART_ROUTE_PROVIDER_ORDER || ['codex'];
|
|
7
|
-
|
|
8
|
-
function encodeBase64Utf8(value) {
|
|
9
|
-
if (typeof Buffer !== 'undefined') {
|
|
10
|
-
return Buffer.from(String(value), 'utf8').toString('base64');
|
|
11
|
-
}
|
|
12
|
-
return btoa(String.fromCharCode(...new TextEncoder().encode(String(value))));
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
function indentBlock(text, spaces) {
|
|
16
|
-
const prefix = ' '.repeat(spaces);
|
|
17
|
-
return String(text).split('\n').map((line) => `${prefix}${line}`).join('\n');
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
function build9RouterSmartRouteSyncScript(dbPath) {
|
|
21
|
-
return `const fs=require('fs');const INTERVAL=30000;const p='${dbPath}';
|
|
22
|
-
const PM=${JSON.stringify(SMART_ROUTE_PROVIDER_MODELS)};
|
|
23
|
-
const PREF=${JSON.stringify(SMART_ROUTE_PROVIDER_ORDER)};
|
|
24
|
-
console.log('[sync-combo] 9Router sync loop started...');
|
|
25
|
-
const sync = async () => {
|
|
26
|
-
try {
|
|
27
|
-
let db = {};
|
|
28
|
-
try { db = JSON.parse(fs.readFileSync(p, 'utf8')); } catch(e){}
|
|
29
|
-
if (!db.combos) db.combos = [];
|
|
30
|
-
const removeSmartRoute = () => {
|
|
31
|
-
const next = db.combos.filter(x => x.id !== 'smart-route');
|
|
32
|
-
if (next.length !== db.combos.length) {
|
|
33
|
-
db.combos = next;
|
|
34
|
-
fs.writeFileSync(p, JSON.stringify(db, null, 2));
|
|
35
|
-
console.log('[sync-combo] Removed smart-route (no active providers)');
|
|
36
|
-
}
|
|
37
|
-
};
|
|
38
|
-
const res = await fetch('http://localhost:20128/api/providers');
|
|
39
|
-
if (!res.ok) { console.log('[sync-combo] API not ready, retrying...'); return; }
|
|
40
|
-
const d = await res.json();
|
|
41
|
-
const rawConnections = Array.isArray(d.connections) ? d.connections : Array.isArray(d.providerConnections) ? d.providerConnections : [];
|
|
42
|
-
const a = [...new Set(rawConnections.filter(c => c && c.provider && c.isActive !== false && !c.disabled).map(c => c.provider))];
|
|
43
|
-
if (!a.length) { removeSmartRoute(); return; }
|
|
44
|
-
a.sort((x, y) => (PREF.indexOf(x) === -1 ? 99 : PREF.indexOf(x)) - (PREF.indexOf(y) === -1 ? 99 : PREF.indexOf(y)));
|
|
45
|
-
const m = a.flatMap(pv => PM[pv] || []);
|
|
46
|
-
if (!m.length) { removeSmartRoute(); return; }
|
|
47
|
-
const c = { id: 'smart-route', name: 'smart-route', alias: 'smart-route', models: m };
|
|
48
|
-
const i = db.combos.findIndex(x => x.id === 'smart-route');
|
|
49
|
-
if (i >= 0) {
|
|
50
|
-
if (JSON.stringify(db.combos[i].models) !== JSON.stringify(c.models)) {
|
|
51
|
-
db.combos[i] = c;
|
|
52
|
-
fs.writeFileSync(p, JSON.stringify(db, null, 2));
|
|
53
|
-
console.log('[sync-combo] Updated smart-route: ' + c.models.length + ' models');
|
|
54
|
-
}
|
|
55
|
-
} else {
|
|
56
|
-
db.combos.push(c);
|
|
57
|
-
fs.writeFileSync(p, JSON.stringify(db, null, 2));
|
|
58
|
-
console.log('[sync-combo] Created smart-route: ' + c.models.length + ' models');
|
|
59
|
-
}
|
|
60
|
-
} catch (e) {}
|
|
61
|
-
};
|
|
62
|
-
setTimeout(sync, 5000);
|
|
63
|
-
setInterval(sync, INTERVAL);`;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
function build9RouterPatchScript() {
|
|
67
|
-
return `const fs=require('fs');const path=require('path');const cp=require('child_process');
|
|
68
|
-
const MODELS=${JSON.stringify(SUPPORTED_CODEX_MODELS.map((model) => model.replace('cx/', '')))};
|
|
69
|
-
const MODEL_NAMES={"gpt-5.4":"GPT 5.4","gpt-5.4-mini":"GPT 5.4 Mini","gpt-5.3-codex":"GPT 5.3 Codex","gpt-5.2":"GPT 5.2"};
|
|
70
|
-
const SELF_TEST_BLOCK=[
|
|
71
|
-
'codex: {',
|
|
72
|
-
' url: "https://chatgpt.com/backend-api/codex/responses",',
|
|
73
|
-
' method: "POST",',
|
|
74
|
-
' authHeader: "Authorization",',
|
|
75
|
-
' authPrefix: "Bearer ",',
|
|
76
|
-
' extraHeaders: { "Content-Type": "application/json", "originator": "codex-cli", "User-Agent": "codex-cli/1.0.18 (macOS; arm64)" },',
|
|
77
|
-
' body: JSON.stringify({',
|
|
78
|
-
' model: "gpt-5.2",',
|
|
79
|
-
' instructions: "You are a coding assistant.",',
|
|
80
|
-
' input: [{ role: "user", content: [{ type: "input_text", text: "Reply with exactly: ok" }] }],',
|
|
81
|
-
' stream: true,',
|
|
82
|
-
' store: false,',
|
|
83
|
-
' }),',
|
|
84
|
-
' acceptStatuses: [200, 400],',
|
|
85
|
-
' refreshable: true,',
|
|
86
|
-
' },'
|
|
87
|
-
].join('\\n');
|
|
88
|
-
const roots=new Set();
|
|
89
|
-
function add(p){if(p)roots.add(p);}
|
|
90
|
-
try{const npmRoot=cp.execSync('npm root -g',{stdio:['ignore','pipe','ignore'],encoding:'utf8'}).trim();if(npmRoot)add(path.join(npmRoot,'9router'));}catch{}
|
|
91
|
-
add(path.join(process.env.APPDATA||'','npm','node_modules','9router'));
|
|
92
|
-
add('/usr/local/lib/node_modules/9router');
|
|
93
|
-
add('/usr/lib/node_modules/9router');
|
|
94
|
-
add(path.join(process.cwd(),'node_modules','9router'));
|
|
95
|
-
function patchFile(filePath, transform){if(!fs.existsSync(filePath))return false;const before=fs.readFileSync(filePath,'utf8');const after=transform(before);if(!after||after===before)return false;fs.writeFileSync(filePath,after);return true;}
|
|
96
|
-
function patchText(text,replacers){let next=text;for(const replacer of replacers){next=replacer(next);}return next===text?null:next;}
|
|
97
|
-
function patchProviderModels(root){return patchFile(path.join(root,'open-sse','config','providerModels.js'),(text)=>text.replace(/cx:\\s*\\[[\\s\\S]*?\\],/,()=>{const lines=MODELS.map((id)=>' { id: "'+id+'", name: "'+(MODEL_NAMES[id]||id)+'" },');return 'cx: [ // OpenAI Codex\\n'+lines.join('\\n')+'\\n ],';}));}
|
|
98
|
-
function patchCodexLikeFile(filePath){return patchFile(filePath,(text)=>{if(text.includes('max_output_tokens'))return text;return patchText(text,[
|
|
99
|
-
(value)=>value.replace(/delete (\\w+)\\.max_tokens,delete \\1\\.user/g,'delete $1.max_tokens,delete $1.max_output_tokens,delete $1.user'),
|
|
100
|
-
(value)=>value.replace(/delete (\\w+)\\.max_tokens;(\\s*)delete \\1\\.user/g,'delete $1.max_tokens;$2delete $1.max_output_tokens;$2delete $1.user'),
|
|
101
|
-
(value)=>value.replace(' delete body.max_tokens;\\n',' delete body.max_tokens;\\n delete body.max_output_tokens;\\n')
|
|
102
|
-
]);});}
|
|
103
|
-
function patchCodexExecutor(root){let touched=0;touched+=patchCodexLikeFile(path.join(root,'open-sse','executors','codex.js'))?1:0;const chunksDir=path.join(root,'app','.next','server','chunks');if(fs.existsSync(chunksDir)){for(const entry of fs.readdirSync(chunksDir)){if(!entry.endsWith('.js'))continue;touched+=patchCodexLikeFile(path.join(chunksDir,entry))?1:0;}}return touched;}
|
|
104
|
-
function patchResponsesNullGuard(root){let touched=0;const chunksDir=path.join(root,'app','.next','server','chunks');if(!fs.existsSync(chunksDir))return touched;for(const entry of fs.readdirSync(chunksDir)){if(!entry.endsWith('.js'))continue;touched+=patchFile(path.join(chunksDir,entry),(text)=>patchText(text,[
|
|
105
|
-
(value)=>value.replace('let b=a.content.find(a=>"output_text"===a.type);','let b=a.content.find(a=>a&&"output_text"===a.type);'),
|
|
106
|
-
(value)=>value.replace('let c=a.content.find(a=>"string"==typeof a.text);','let c=a.content.find(a=>a&&"string"==typeof a.text);'),
|
|
107
|
-
(value)=>value.replace('let b=a.filter(a=>a?.type==="message");','let b=a.filter(a=>a&&a?.type==="message");'),
|
|
108
|
-
(value)=>value.replace('for(let a of j){let b=a.type||(a.role?"message":null);','for(let a of j){let b=a&&(a.type||(a.role?"message":null));'),
|
|
109
|
-
(value)=>value.replace('for(let a of b.messages||[]){if("system"===a.role){','for(let a of b.messages||[])if(a){if("system"===a.role){'),
|
|
110
|
-
(value)=>value.replace('let b=Array.isArray(a.content)?a.content.map(a=>"input_text"===a.type||"output_text"===a.type?{type:"text",text:a.text}:"input_image"===a.type?{type:"image_url",image_url:{url:a.image_url||a.file_id||"",detail:a.detail||"auto"}}:a):a.content;','let b=Array.isArray(a.content)?a.content.map(a=>a&&("input_text"===a.type||"output_text"===a.type)?{type:"text",text:a.text}:a&&"input_image"===a.type?{type:"image_url",image_url:{url:a.image_url||a.file_id||"",detail:a.detail||"auto"}}:a).filter(Boolean):a.content;'),
|
|
111
|
-
(value)=>value.replace('c="string"==typeof a.content?[{type:b,text:a.content}]:Array.isArray(a.content)?a.content.map(a=>{if("text"===a.type)return{type:b,text:a.text};if("image_url"===a.type)return{type:"input_image",image_url:"string"==typeof a.image_url?a.image_url:a.image_url?.url,detail:a.image_url?.detail||"auto"};if("input_image"===a.type)return a;let c=a.text||a.content||JSON.stringify(a);return{type:b,text:"string"==typeof c?c:JSON.stringify(c)}}):[];','c="string"==typeof a.content?[{type:b,text:a.content}]:Array.isArray(a.content)?a.content.map(a=>{if(!a)return null;if("text"===a.type)return{type:b,text:a.text};if("image_url"===a.type)return{type:"input_image",image_url:"string"==typeof a.image_url?a.image_url:a.image_url?.url,detail:a.image_url?.detail||"auto"};if("input_image"===a.type)return a;let c=a.text||a.content||JSON.stringify(a);return{type:b,text:"string"==typeof c?c:JSON.stringify(c)}}).filter(Boolean):[];'),
|
|
112
|
-
(value)=>value.replace('b.tools&&Array.isArray(b.tools)&&(e.tools=b.tools.map(a=>{if(a.function)return a;let b=a.name;return b&&"string"==typeof b&&""!==b.trim()?{type:"function",function:{name:b,description:String(a.description||""),parameters:i(a.parameters),strict:a.strict}}:null}).filter(Boolean))','b.tools&&Array.isArray(b.tools)&&(e.tools=b.tools.map(a=>{if(!a)return null;if(a.function)return a;let b=a.name;return b&&"string"==typeof b&&""!==b.trim()?{type:"function",function:{name:b,description:String(a.description||""),parameters:i(a.parameters),strict:a.strict}}:null}).filter(Boolean))'),
|
|
113
|
-
(value)=>value.replace('b.tools&&Array.isArray(b.tools)&&(e.tools=b.tools.map(a=>"function"===a.type?{type:"function",name:a.function.name,description:String(a.function.description||""),parameters:i(a.function.parameters),strict:a.function.strict}:a)),','b.tools&&Array.isArray(b.tools)&&(e.tools=b.tools.map(a=>a&&"function"===a.type?{type:"function",name:a.function.name,description:String(a.function.description||""),parameters:i(a.function.parameters),strict:a.function.strict}:a).filter(Boolean)),'),
|
|
114
|
-
(value)=>value.replace('filter(a=>"function_call"===a.type)','filter(a=>a&&"function_call"===a.type)'),
|
|
115
|
-
(value)=>value.replace(/filter\\(a=>"text"===a\\.type\\)/g,'filter(a=>a&&"text"===a.type)'),
|
|
116
|
-
(value)=>value.replace(/find\\(a=>"message_stop"===a\\.type\\)/g,'find(a=>a&&"message_stop"===a.type)'),
|
|
117
|
-
(value)=>value.replace(/find\\(a=>"content_block_delta"===a\\.type\\)/g,'find(a=>a&&"content_block_delta"===a.type)'),
|
|
118
|
-
(value)=>value.replace(/find\\(a=>"message_delta"===a\\.type\\)/g,'find(a=>a&&"message_delta"===a.type)'),
|
|
119
|
-
(value)=>value.replace(/find\\(a=>"message_start"===a\\.type\\)/g,'find(a=>a&&"message_start"===a.type)'),
|
|
120
|
-
(value)=>value.replace(/for\\(let e of a\\.content\\)(?!if\\(e\\))/g,'for(let e of a.content)if(e)')
|
|
121
|
-
] ))?1:0;}return touched;}
|
|
122
|
-
function patchSelfTest(root){return patchFile(path.join(root,'src','app','api','providers','[id]','test','testUtils.js'),(text)=>{if(text.includes('model: "gpt-5.2"')&&text.includes('store: false')&&text.includes('acceptStatuses: [200, 400]'))return text;return text.replace(/codex:\\s*\\{[\\s\\S]*?refreshable:\\s*true,\\s*\\},/,SELF_TEST_BLOCK);});}
|
|
123
|
-
let touched=0;
|
|
124
|
-
for(const root of roots){if(!root||!fs.existsSync(root))continue;touched+=patchProviderModels(root)?1:0;touched+=patchCodexExecutor(root)?1:0;touched+=patchResponsesNullGuard(root)?1:0;touched+=patchSelfTest(root)?1:0;}
|
|
125
|
-
if(touched){console.log('[patch-9router] Applied Codex compatibility patch.');}else{console.log('[patch-9router] No compatible 9router source files found to patch.');}`;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
function build9RouterComposeEntrypointScript(syncScriptBase64, patchScriptBase64) {
|
|
129
|
-
const nineRouterSpec = (typeof globalThis !== 'undefined' && globalThis.__openclawCommon && globalThis.__openclawCommon.NINE_ROUTER_NPM_SPEC) || '9router@latest';
|
|
130
|
-
return [
|
|
131
|
-
`npm install -g ${nineRouterSpec}`,
|
|
132
|
-
`node -e "require('fs').writeFileSync('/tmp/patch-9router.js',Buffer.from('${patchScriptBase64}','base64').toString())"`,
|
|
133
|
-
`node -e "require('fs').writeFileSync('/tmp/sync.js',Buffer.from('${syncScriptBase64}','base64').toString())"`,
|
|
134
|
-
'node /tmp/patch-9router.js || true',
|
|
135
|
-
'node /tmp/sync.js > /tmp/sync.log 2>&1 &',
|
|
136
|
-
'exec 9router -n -l -H 0.0.0.0 -p 20128 --skip-update'
|
|
137
|
-
].join('\n');
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
function buildGatewayPatchCmd() {
|
|
141
|
-
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: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));}\\"`;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
function buildDockerArtifacts(options) {
|
|
145
|
-
const {
|
|
146
|
-
openClawNpmSpec,
|
|
147
|
-
openClawRuntimePackages,
|
|
148
|
-
is9Router,
|
|
149
|
-
isLocal,
|
|
150
|
-
isMultiBot,
|
|
151
|
-
hasBrowser,
|
|
152
|
-
selectedModel,
|
|
153
|
-
agentId,
|
|
154
|
-
allSkills = [],
|
|
155
|
-
dockerfileSkillInstallMode = 'none',
|
|
156
|
-
runtimeCommandParts = [],
|
|
157
|
-
volumeMount = '../..:/root/project',
|
|
158
|
-
singleComposeName = 'oc-bot',
|
|
159
|
-
multiComposeName = 'oc-multibot',
|
|
160
|
-
singleAppContainerName = 'openclaw-bot',
|
|
161
|
-
multiAppContainerName = 'openclaw-multibot',
|
|
162
|
-
singleRouterContainerName = '9router',
|
|
163
|
-
multiRouterContainerName = '9router-multibot',
|
|
164
|
-
singleOllamaContainerName = 'ollama',
|
|
165
|
-
multiOllamaContainerName = 'ollama-multibot',
|
|
166
|
-
plainSingleExtraHosts = false,
|
|
167
|
-
multiOllamaNumParallel = 1,
|
|
168
|
-
singleOllamaNumParallel = 1,
|
|
169
|
-
emitBrowserInstall = true,
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
const
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
'
|
|
177
|
-
'
|
|
178
|
-
'
|
|
179
|
-
' &&
|
|
180
|
-
'',
|
|
181
|
-
''
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
: ''
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
const
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
runtimeParts.
|
|
195
|
-
runtimeParts.unshift('export
|
|
196
|
-
runtimeParts.unshift(
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
runtimeParts.push('
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
const
|
|
205
|
-
const
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
${
|
|
209
|
-
|
|
210
|
-
ARG
|
|
211
|
-
|
|
212
|
-
${
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
const
|
|
222
|
-
const
|
|
223
|
-
const
|
|
224
|
-
const
|
|
225
|
-
const
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
: '
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
-
|
|
258
|
-
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
-
|
|
263
|
-
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
-
|
|
298
|
-
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
ollama
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
-
|
|
349
|
-
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
-
|
|
354
|
-
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
-
|
|
391
|
-
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
ollama
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
(function (root) {
|
|
3
|
+
const common = (typeof globalThis !== 'undefined' && globalThis.__openclawCommon) || {};
|
|
4
|
+
const SUPPORTED_CODEX_MODELS = common.SUPPORTED_CODEX_MODELS || ['cx/gpt-5.4', 'cx/gpt-5.3-codex', 'cx/gpt-5.2', 'cx/gpt-5.4-mini'];
|
|
5
|
+
const SMART_ROUTE_PROVIDER_MODELS = common.SMART_ROUTE_PROVIDER_MODELS || { codex: SUPPORTED_CODEX_MODELS };
|
|
6
|
+
const SMART_ROUTE_PROVIDER_ORDER = common.SMART_ROUTE_PROVIDER_ORDER || ['codex'];
|
|
7
|
+
|
|
8
|
+
function encodeBase64Utf8(value) {
|
|
9
|
+
if (typeof Buffer !== 'undefined') {
|
|
10
|
+
return Buffer.from(String(value), 'utf8').toString('base64');
|
|
11
|
+
}
|
|
12
|
+
return btoa(String.fromCharCode(...new TextEncoder().encode(String(value))));
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function indentBlock(text, spaces) {
|
|
16
|
+
const prefix = ' '.repeat(spaces);
|
|
17
|
+
return String(text).split('\n').map((line) => `${prefix}${line}`).join('\n');
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function build9RouterSmartRouteSyncScript(dbPath) {
|
|
21
|
+
return `const fs=require('fs');const INTERVAL=30000;const p='${dbPath}';
|
|
22
|
+
const PM=${JSON.stringify(SMART_ROUTE_PROVIDER_MODELS)};
|
|
23
|
+
const PREF=${JSON.stringify(SMART_ROUTE_PROVIDER_ORDER)};
|
|
24
|
+
console.log('[sync-combo] 9Router sync loop started...');
|
|
25
|
+
const sync = async () => {
|
|
26
|
+
try {
|
|
27
|
+
let db = {};
|
|
28
|
+
try { db = JSON.parse(fs.readFileSync(p, 'utf8')); } catch(e){}
|
|
29
|
+
if (!db.combos) db.combos = [];
|
|
30
|
+
const removeSmartRoute = () => {
|
|
31
|
+
const next = db.combos.filter(x => x.id !== 'smart-route');
|
|
32
|
+
if (next.length !== db.combos.length) {
|
|
33
|
+
db.combos = next;
|
|
34
|
+
fs.writeFileSync(p, JSON.stringify(db, null, 2));
|
|
35
|
+
console.log('[sync-combo] Removed smart-route (no active providers)');
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
const res = await fetch('http://localhost:20128/api/providers');
|
|
39
|
+
if (!res.ok) { console.log('[sync-combo] API not ready, retrying...'); return; }
|
|
40
|
+
const d = await res.json();
|
|
41
|
+
const rawConnections = Array.isArray(d.connections) ? d.connections : Array.isArray(d.providerConnections) ? d.providerConnections : [];
|
|
42
|
+
const a = [...new Set(rawConnections.filter(c => c && c.provider && c.isActive !== false && !c.disabled).map(c => c.provider))];
|
|
43
|
+
if (!a.length) { removeSmartRoute(); return; }
|
|
44
|
+
a.sort((x, y) => (PREF.indexOf(x) === -1 ? 99 : PREF.indexOf(x)) - (PREF.indexOf(y) === -1 ? 99 : PREF.indexOf(y)));
|
|
45
|
+
const m = a.flatMap(pv => PM[pv] || []);
|
|
46
|
+
if (!m.length) { removeSmartRoute(); return; }
|
|
47
|
+
const c = { id: 'smart-route', name: 'smart-route', alias: 'smart-route', models: m };
|
|
48
|
+
const i = db.combos.findIndex(x => x.id === 'smart-route');
|
|
49
|
+
if (i >= 0) {
|
|
50
|
+
if (JSON.stringify(db.combos[i].models) !== JSON.stringify(c.models)) {
|
|
51
|
+
db.combos[i] = c;
|
|
52
|
+
fs.writeFileSync(p, JSON.stringify(db, null, 2));
|
|
53
|
+
console.log('[sync-combo] Updated smart-route: ' + c.models.length + ' models');
|
|
54
|
+
}
|
|
55
|
+
} else {
|
|
56
|
+
db.combos.push(c);
|
|
57
|
+
fs.writeFileSync(p, JSON.stringify(db, null, 2));
|
|
58
|
+
console.log('[sync-combo] Created smart-route: ' + c.models.length + ' models');
|
|
59
|
+
}
|
|
60
|
+
} catch (e) {}
|
|
61
|
+
};
|
|
62
|
+
setTimeout(sync, 5000);
|
|
63
|
+
setInterval(sync, INTERVAL);`;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function build9RouterPatchScript() {
|
|
67
|
+
return `const fs=require('fs');const path=require('path');const cp=require('child_process');
|
|
68
|
+
const MODELS=${JSON.stringify(SUPPORTED_CODEX_MODELS.map((model) => model.replace('cx/', '')))};
|
|
69
|
+
const MODEL_NAMES={"gpt-5.4":"GPT 5.4","gpt-5.4-mini":"GPT 5.4 Mini","gpt-5.3-codex":"GPT 5.3 Codex","gpt-5.2":"GPT 5.2"};
|
|
70
|
+
const SELF_TEST_BLOCK=[
|
|
71
|
+
'codex: {',
|
|
72
|
+
' url: "https://chatgpt.com/backend-api/codex/responses",',
|
|
73
|
+
' method: "POST",',
|
|
74
|
+
' authHeader: "Authorization",',
|
|
75
|
+
' authPrefix: "Bearer ",',
|
|
76
|
+
' extraHeaders: { "Content-Type": "application/json", "originator": "codex-cli", "User-Agent": "codex-cli/1.0.18 (macOS; arm64)" },',
|
|
77
|
+
' body: JSON.stringify({',
|
|
78
|
+
' model: "gpt-5.2",',
|
|
79
|
+
' instructions: "You are a coding assistant.",',
|
|
80
|
+
' input: [{ role: "user", content: [{ type: "input_text", text: "Reply with exactly: ok" }] }],',
|
|
81
|
+
' stream: true,',
|
|
82
|
+
' store: false,',
|
|
83
|
+
' }),',
|
|
84
|
+
' acceptStatuses: [200, 400],',
|
|
85
|
+
' refreshable: true,',
|
|
86
|
+
' },'
|
|
87
|
+
].join('\\n');
|
|
88
|
+
const roots=new Set();
|
|
89
|
+
function add(p){if(p)roots.add(p);}
|
|
90
|
+
try{const npmRoot=cp.execSync('npm root -g',{stdio:['ignore','pipe','ignore'],encoding:'utf8'}).trim();if(npmRoot)add(path.join(npmRoot,'9router'));}catch{}
|
|
91
|
+
add(path.join(process.env.APPDATA||'','npm','node_modules','9router'));
|
|
92
|
+
add('/usr/local/lib/node_modules/9router');
|
|
93
|
+
add('/usr/lib/node_modules/9router');
|
|
94
|
+
add(path.join(process.cwd(),'node_modules','9router'));
|
|
95
|
+
function patchFile(filePath, transform){if(!fs.existsSync(filePath))return false;const before=fs.readFileSync(filePath,'utf8');const after=transform(before);if(!after||after===before)return false;fs.writeFileSync(filePath,after);return true;}
|
|
96
|
+
function patchText(text,replacers){let next=text;for(const replacer of replacers){next=replacer(next);}return next===text?null:next;}
|
|
97
|
+
function patchProviderModels(root){return patchFile(path.join(root,'open-sse','config','providerModels.js'),(text)=>text.replace(/cx:\\s*\\[[\\s\\S]*?\\],/,()=>{const lines=MODELS.map((id)=>' { id: "'+id+'", name: "'+(MODEL_NAMES[id]||id)+'" },');return 'cx: [ // OpenAI Codex\\n'+lines.join('\\n')+'\\n ],';}));}
|
|
98
|
+
function patchCodexLikeFile(filePath){return patchFile(filePath,(text)=>{if(text.includes('max_output_tokens'))return text;return patchText(text,[
|
|
99
|
+
(value)=>value.replace(/delete (\\w+)\\.max_tokens,delete \\1\\.user/g,'delete $1.max_tokens,delete $1.max_output_tokens,delete $1.user'),
|
|
100
|
+
(value)=>value.replace(/delete (\\w+)\\.max_tokens;(\\s*)delete \\1\\.user/g,'delete $1.max_tokens;$2delete $1.max_output_tokens;$2delete $1.user'),
|
|
101
|
+
(value)=>value.replace(' delete body.max_tokens;\\n',' delete body.max_tokens;\\n delete body.max_output_tokens;\\n')
|
|
102
|
+
]);});}
|
|
103
|
+
function patchCodexExecutor(root){let touched=0;touched+=patchCodexLikeFile(path.join(root,'open-sse','executors','codex.js'))?1:0;const chunksDir=path.join(root,'app','.next','server','chunks');if(fs.existsSync(chunksDir)){for(const entry of fs.readdirSync(chunksDir)){if(!entry.endsWith('.js'))continue;touched+=patchCodexLikeFile(path.join(chunksDir,entry))?1:0;}}return touched;}
|
|
104
|
+
function patchResponsesNullGuard(root){let touched=0;const chunksDir=path.join(root,'app','.next','server','chunks');if(!fs.existsSync(chunksDir))return touched;for(const entry of fs.readdirSync(chunksDir)){if(!entry.endsWith('.js'))continue;touched+=patchFile(path.join(chunksDir,entry),(text)=>patchText(text,[
|
|
105
|
+
(value)=>value.replace('let b=a.content.find(a=>"output_text"===a.type);','let b=a.content.find(a=>a&&"output_text"===a.type);'),
|
|
106
|
+
(value)=>value.replace('let c=a.content.find(a=>"string"==typeof a.text);','let c=a.content.find(a=>a&&"string"==typeof a.text);'),
|
|
107
|
+
(value)=>value.replace('let b=a.filter(a=>a?.type==="message");','let b=a.filter(a=>a&&a?.type==="message");'),
|
|
108
|
+
(value)=>value.replace('for(let a of j){let b=a.type||(a.role?"message":null);','for(let a of j){let b=a&&(a.type||(a.role?"message":null));'),
|
|
109
|
+
(value)=>value.replace('for(let a of b.messages||[]){if("system"===a.role){','for(let a of b.messages||[])if(a){if("system"===a.role){'),
|
|
110
|
+
(value)=>value.replace('let b=Array.isArray(a.content)?a.content.map(a=>"input_text"===a.type||"output_text"===a.type?{type:"text",text:a.text}:"input_image"===a.type?{type:"image_url",image_url:{url:a.image_url||a.file_id||"",detail:a.detail||"auto"}}:a):a.content;','let b=Array.isArray(a.content)?a.content.map(a=>a&&("input_text"===a.type||"output_text"===a.type)?{type:"text",text:a.text}:a&&"input_image"===a.type?{type:"image_url",image_url:{url:a.image_url||a.file_id||"",detail:a.detail||"auto"}}:a).filter(Boolean):a.content;'),
|
|
111
|
+
(value)=>value.replace('c="string"==typeof a.content?[{type:b,text:a.content}]:Array.isArray(a.content)?a.content.map(a=>{if("text"===a.type)return{type:b,text:a.text};if("image_url"===a.type)return{type:"input_image",image_url:"string"==typeof a.image_url?a.image_url:a.image_url?.url,detail:a.image_url?.detail||"auto"};if("input_image"===a.type)return a;let c=a.text||a.content||JSON.stringify(a);return{type:b,text:"string"==typeof c?c:JSON.stringify(c)}}):[];','c="string"==typeof a.content?[{type:b,text:a.content}]:Array.isArray(a.content)?a.content.map(a=>{if(!a)return null;if("text"===a.type)return{type:b,text:a.text};if("image_url"===a.type)return{type:"input_image",image_url:"string"==typeof a.image_url?a.image_url:a.image_url?.url,detail:a.image_url?.detail||"auto"};if("input_image"===a.type)return a;let c=a.text||a.content||JSON.stringify(a);return{type:b,text:"string"==typeof c?c:JSON.stringify(c)}}).filter(Boolean):[];'),
|
|
112
|
+
(value)=>value.replace('b.tools&&Array.isArray(b.tools)&&(e.tools=b.tools.map(a=>{if(a.function)return a;let b=a.name;return b&&"string"==typeof b&&""!==b.trim()?{type:"function",function:{name:b,description:String(a.description||""),parameters:i(a.parameters),strict:a.strict}}:null}).filter(Boolean))','b.tools&&Array.isArray(b.tools)&&(e.tools=b.tools.map(a=>{if(!a)return null;if(a.function)return a;let b=a.name;return b&&"string"==typeof b&&""!==b.trim()?{type:"function",function:{name:b,description:String(a.description||""),parameters:i(a.parameters),strict:a.strict}}:null}).filter(Boolean))'),
|
|
113
|
+
(value)=>value.replace('b.tools&&Array.isArray(b.tools)&&(e.tools=b.tools.map(a=>"function"===a.type?{type:"function",name:a.function.name,description:String(a.function.description||""),parameters:i(a.function.parameters),strict:a.function.strict}:a)),','b.tools&&Array.isArray(b.tools)&&(e.tools=b.tools.map(a=>a&&"function"===a.type?{type:"function",name:a.function.name,description:String(a.function.description||""),parameters:i(a.function.parameters),strict:a.function.strict}:a).filter(Boolean)),'),
|
|
114
|
+
(value)=>value.replace('filter(a=>"function_call"===a.type)','filter(a=>a&&"function_call"===a.type)'),
|
|
115
|
+
(value)=>value.replace(/filter\\(a=>"text"===a\\.type\\)/g,'filter(a=>a&&"text"===a.type)'),
|
|
116
|
+
(value)=>value.replace(/find\\(a=>"message_stop"===a\\.type\\)/g,'find(a=>a&&"message_stop"===a.type)'),
|
|
117
|
+
(value)=>value.replace(/find\\(a=>"content_block_delta"===a\\.type\\)/g,'find(a=>a&&"content_block_delta"===a.type)'),
|
|
118
|
+
(value)=>value.replace(/find\\(a=>"message_delta"===a\\.type\\)/g,'find(a=>a&&"message_delta"===a.type)'),
|
|
119
|
+
(value)=>value.replace(/find\\(a=>"message_start"===a\\.type\\)/g,'find(a=>a&&"message_start"===a.type)'),
|
|
120
|
+
(value)=>value.replace(/for\\(let e of a\\.content\\)(?!if\\(e\\))/g,'for(let e of a.content)if(e)')
|
|
121
|
+
] ))?1:0;}return touched;}
|
|
122
|
+
function patchSelfTest(root){return patchFile(path.join(root,'src','app','api','providers','[id]','test','testUtils.js'),(text)=>{if(text.includes('model: "gpt-5.2"')&&text.includes('store: false')&&text.includes('acceptStatuses: [200, 400]'))return text;return text.replace(/codex:\\s*\\{[\\s\\S]*?refreshable:\\s*true,\\s*\\},/,SELF_TEST_BLOCK);});}
|
|
123
|
+
let touched=0;
|
|
124
|
+
for(const root of roots){if(!root||!fs.existsSync(root))continue;touched+=patchProviderModels(root)?1:0;touched+=patchCodexExecutor(root)?1:0;touched+=patchResponsesNullGuard(root)?1:0;touched+=patchSelfTest(root)?1:0;}
|
|
125
|
+
if(touched){console.log('[patch-9router] Applied Codex compatibility patch.');}else{console.log('[patch-9router] No compatible 9router source files found to patch.');}`;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function build9RouterComposeEntrypointScript(syncScriptBase64, patchScriptBase64) {
|
|
129
|
+
const nineRouterSpec = (typeof globalThis !== 'undefined' && globalThis.__openclawCommon && globalThis.__openclawCommon.NINE_ROUTER_NPM_SPEC) || '9router@latest';
|
|
130
|
+
return [
|
|
131
|
+
`npm install -g ${nineRouterSpec}`,
|
|
132
|
+
`node -e "require('fs').writeFileSync('/tmp/patch-9router.js',Buffer.from('${patchScriptBase64}','base64').toString())"`,
|
|
133
|
+
`node -e "require('fs').writeFileSync('/tmp/sync.js',Buffer.from('${syncScriptBase64}','base64').toString())"`,
|
|
134
|
+
'node /tmp/patch-9router.js || true',
|
|
135
|
+
'node /tmp/sync.js > /tmp/sync.log 2>&1 &',
|
|
136
|
+
'exec 9router -n -l -H 0.0.0.0 -p 20128 --skip-update'
|
|
137
|
+
].join('\n');
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function buildGatewayPatchCmd() {
|
|
141
|
+
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: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));}\\"`;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function buildDockerArtifacts(options) {
|
|
145
|
+
const {
|
|
146
|
+
openClawNpmSpec,
|
|
147
|
+
openClawRuntimePackages,
|
|
148
|
+
is9Router,
|
|
149
|
+
isLocal,
|
|
150
|
+
isMultiBot,
|
|
151
|
+
hasBrowser,
|
|
152
|
+
selectedModel,
|
|
153
|
+
agentId,
|
|
154
|
+
allSkills = [],
|
|
155
|
+
dockerfileSkillInstallMode = 'none',
|
|
156
|
+
runtimeCommandParts = [],
|
|
157
|
+
volumeMount = '../..:/root/project',
|
|
158
|
+
singleComposeName = 'oc-bot',
|
|
159
|
+
multiComposeName = 'oc-multibot',
|
|
160
|
+
singleAppContainerName = 'openclaw-bot',
|
|
161
|
+
multiAppContainerName = 'openclaw-multibot',
|
|
162
|
+
singleRouterContainerName = '9router',
|
|
163
|
+
multiRouterContainerName = '9router-multibot',
|
|
164
|
+
singleOllamaContainerName = 'ollama',
|
|
165
|
+
multiOllamaContainerName = 'ollama-multibot',
|
|
166
|
+
plainSingleExtraHosts = false,
|
|
167
|
+
multiOllamaNumParallel = 1,
|
|
168
|
+
singleOllamaNumParallel = 1,
|
|
169
|
+
emitBrowserInstall = true,
|
|
170
|
+
|
|
171
|
+
} = options;
|
|
172
|
+
|
|
173
|
+
const browserAptExtra = hasBrowser ? ' xvfb socat' : '';
|
|
174
|
+
const browserInstallLines = hasBrowser && emitBrowserInstall
|
|
175
|
+
? [
|
|
176
|
+
'',
|
|
177
|
+
'# Browser Automation: Playwright engine (needed for native CDP)',
|
|
178
|
+
'RUN npm install -g agent-browser playwright \\',
|
|
179
|
+
' && npx playwright install chromium --with-deps \\',
|
|
180
|
+
' && ln -f -s /root/.cache/ms-playwright/chromium-*/chrome-linux*/chrome /usr/bin/google-chrome',
|
|
181
|
+
'',
|
|
182
|
+
''
|
|
183
|
+
].join('\n')
|
|
184
|
+
: '';
|
|
185
|
+
const skillLines = dockerfileSkillInstallMode === 'build' && allSkills.length > 0
|
|
186
|
+
? `\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`
|
|
187
|
+
: '';
|
|
188
|
+
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);}"`;
|
|
189
|
+
|
|
190
|
+
// Dynamic runtime configuration injection for container internal IPs
|
|
191
|
+
const setupInternalIpScript = `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: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));}`;
|
|
192
|
+
const setupInternalIpB64 = encodeBase64Utf8(setupInternalIpScript);
|
|
193
|
+
|
|
194
|
+
const runtimeParts = runtimeCommandParts.filter(Boolean);
|
|
195
|
+
runtimeParts.unshift('export OPENCLAW_HOME="$PWD/.openclaw"');
|
|
196
|
+
runtimeParts.unshift('export OPENCLAW_STATE_DIR="$PWD/.openclaw"');
|
|
197
|
+
runtimeParts.unshift(`node -e 'eval(Buffer.from("${setupInternalIpB64}","base64").toString())'`);
|
|
198
|
+
if (hasBrowser) {
|
|
199
|
+
runtimeParts.push('socat TCP-LISTEN:9222,fork,reuseaddr TCP:host.docker.internal:9222 &');
|
|
200
|
+
runtimeParts.push('Xvfb :99 -screen 0 1280x720x24 > /dev/null 2>&1 & DISPLAY=:99 openclaw gateway run');
|
|
201
|
+
} else {
|
|
202
|
+
runtimeParts.push('openclaw gateway run');
|
|
203
|
+
}
|
|
204
|
+
const runtimeScript = ['#!/bin/sh', 'set -e', ...runtimeParts].join('\n');
|
|
205
|
+
const runtimeScriptB64 = encodeBase64Utf8(runtimeScript);
|
|
206
|
+
const dockerfile = `FROM node:22-slim
|
|
207
|
+
|
|
208
|
+
RUN apt-get update && apt-get install -y git curl${browserAptExtra} && rm -rf /var/lib/apt/lists/*
|
|
209
|
+
${browserInstallLines}
|
|
210
|
+
ARG OPENCLAW_VER="${openClawNpmSpec}"
|
|
211
|
+
ARG CACHE_BUST=""
|
|
212
|
+
RUN npm install -g ${openClawNpmSpec} ${openClawRuntimePackages}${skillLines}
|
|
213
|
+
${patchLine}
|
|
214
|
+
RUN node -e "require('fs').writeFileSync('/usr/local/bin/openclaw-entrypoint.sh', Buffer.from('${runtimeScriptB64}','base64').toString())" && chmod +x /usr/local/bin/openclaw-entrypoint.sh
|
|
215
|
+
WORKDIR /root/project
|
|
216
|
+
|
|
217
|
+
EXPOSE 18791
|
|
218
|
+
|
|
219
|
+
CMD ["/bin/sh", "/usr/local/bin/openclaw-entrypoint.sh"]`;
|
|
220
|
+
|
|
221
|
+
const syncScript = build9RouterSmartRouteSyncScript('/root/.9router/db.json');
|
|
222
|
+
const syncScriptBase64 = encodeBase64Utf8(syncScript);
|
|
223
|
+
const patchScript = build9RouterPatchScript();
|
|
224
|
+
const patchScriptBase64 = encodeBase64Utf8(patchScript);
|
|
225
|
+
const docker9RouterEntrypointScript = build9RouterComposeEntrypointScript(syncScriptBase64, patchScriptBase64);
|
|
226
|
+
const extraHostsBlock = ` extra_hosts:\n - "host.docker.internal:host-gateway"`;
|
|
227
|
+
|
|
228
|
+
const appEnvironmentBlock = ' environment:\n - OPENCLAW_HOME=/root/project/.openclaw\n - OPENCLAW_STATE_DIR=/root/project/.openclaw\n';
|
|
229
|
+
|
|
230
|
+
let compose;
|
|
231
|
+
if (isMultiBot) {
|
|
232
|
+
const dependsOn = is9Router
|
|
233
|
+
? ' depends_on:\n - 9router\n'
|
|
234
|
+
: isLocal
|
|
235
|
+
? ' depends_on:\n ollama:\n condition: service_healthy\n'
|
|
236
|
+
: '';
|
|
237
|
+
const extraHosts = hasBrowser ? `${extraHostsBlock}\n` : '';
|
|
238
|
+
if (is9Router) {
|
|
239
|
+
compose = `name: ${multiComposeName}
|
|
240
|
+
services:
|
|
241
|
+
ai-bot:
|
|
242
|
+
build: .
|
|
243
|
+
container_name: ${multiAppContainerName}
|
|
244
|
+
restart: always
|
|
245
|
+
env_file:
|
|
246
|
+
- .env
|
|
247
|
+
${appEnvironmentBlock}${dependsOn}${extraHosts} volumes:
|
|
248
|
+
- ${volumeMount}
|
|
249
|
+
ports:
|
|
250
|
+
- "18791:18791"
|
|
251
|
+
|
|
252
|
+
9router:
|
|
253
|
+
image: node:22-slim
|
|
254
|
+
container_name: ${multiRouterContainerName}
|
|
255
|
+
restart: always
|
|
256
|
+
entrypoint:
|
|
257
|
+
- /bin/sh
|
|
258
|
+
- -c
|
|
259
|
+
- |
|
|
260
|
+
${indentBlock(docker9RouterEntrypointScript, 8)}
|
|
261
|
+
environment:
|
|
262
|
+
- PORT=20128
|
|
263
|
+
- HOSTNAME=0.0.0.0
|
|
264
|
+
- CI=true
|
|
265
|
+
volumes:
|
|
266
|
+
- 9router-data:/root/.9router
|
|
267
|
+
ports:
|
|
268
|
+
- "20128:20128"
|
|
269
|
+
|
|
270
|
+
volumes:
|
|
271
|
+
9router-data:`;
|
|
272
|
+
} else if (isLocal) {
|
|
273
|
+
const ollamaModelTag = String(selectedModel || 'ollama/gemma4:e2b').replace('ollama/', '');
|
|
274
|
+
compose = `name: ${multiComposeName}
|
|
275
|
+
services:
|
|
276
|
+
ai-bot:
|
|
277
|
+
build: .
|
|
278
|
+
container_name: ${multiAppContainerName}
|
|
279
|
+
restart: always
|
|
280
|
+
env_file:
|
|
281
|
+
- .env
|
|
282
|
+
${appEnvironmentBlock}${dependsOn}${extraHosts} volumes:
|
|
283
|
+
- ${volumeMount}
|
|
284
|
+
ports:
|
|
285
|
+
- "18791:18791"
|
|
286
|
+
|
|
287
|
+
ollama:
|
|
288
|
+
image: ollama/ollama:latest
|
|
289
|
+
container_name: ${multiOllamaContainerName}
|
|
290
|
+
restart: always
|
|
291
|
+
environment:
|
|
292
|
+
- OLLAMA_KEEP_ALIVE=24h
|
|
293
|
+
- OLLAMA_NUM_PARALLEL=${multiOllamaNumParallel}
|
|
294
|
+
volumes:
|
|
295
|
+
- ollama-data:/root/.ollama
|
|
296
|
+
entrypoint:
|
|
297
|
+
- /bin/sh
|
|
298
|
+
- -c
|
|
299
|
+
- |
|
|
300
|
+
ollama serve &
|
|
301
|
+
until ollama list > /dev/null 2>&1; do sleep 1; done
|
|
302
|
+
ollama pull ${ollamaModelTag}
|
|
303
|
+
wait
|
|
304
|
+
healthcheck:
|
|
305
|
+
test: ["CMD-SHELL", "ollama list > /dev/null 2>&1"]
|
|
306
|
+
interval: 10s
|
|
307
|
+
timeout: 5s
|
|
308
|
+
retries: 10
|
|
309
|
+
start_period: 30s
|
|
310
|
+
|
|
311
|
+
volumes:
|
|
312
|
+
ollama-data:`;
|
|
313
|
+
} else {
|
|
314
|
+
compose = `name: ${multiComposeName}
|
|
315
|
+
services:
|
|
316
|
+
ai-bot:
|
|
317
|
+
build: .
|
|
318
|
+
container_name: ${multiAppContainerName}
|
|
319
|
+
restart: always
|
|
320
|
+
env_file:
|
|
321
|
+
- .env
|
|
322
|
+
${appEnvironmentBlock}${extraHosts} volumes:
|
|
323
|
+
- ${volumeMount}
|
|
324
|
+
ports:
|
|
325
|
+
- "18791:18791"`;
|
|
326
|
+
}
|
|
327
|
+
} else if (is9Router) {
|
|
328
|
+
compose = `name: ${singleComposeName}
|
|
329
|
+
services:
|
|
330
|
+
ai-bot:
|
|
331
|
+
build: .
|
|
332
|
+
container_name: ${singleAppContainerName}
|
|
333
|
+
restart: always
|
|
334
|
+
env_file:
|
|
335
|
+
- .env
|
|
336
|
+
depends_on:
|
|
337
|
+
- 9router
|
|
338
|
+
${appEnvironmentBlock}${hasBrowser ? `${extraHostsBlock}\n` : ''} volumes:
|
|
339
|
+
- ${volumeMount}
|
|
340
|
+
ports:
|
|
341
|
+
- "18791:18791"
|
|
342
|
+
|
|
343
|
+
9router:
|
|
344
|
+
image: node:22-slim
|
|
345
|
+
container_name: ${singleRouterContainerName}
|
|
346
|
+
restart: always
|
|
347
|
+
entrypoint:
|
|
348
|
+
- /bin/sh
|
|
349
|
+
- -c
|
|
350
|
+
- |
|
|
351
|
+
${indentBlock(docker9RouterEntrypointScript, 8)}
|
|
352
|
+
environment:
|
|
353
|
+
- PORT=20128
|
|
354
|
+
- HOSTNAME=0.0.0.0
|
|
355
|
+
- CI=true
|
|
356
|
+
volumes:
|
|
357
|
+
- 9router-data:/root/.9router
|
|
358
|
+
ports:
|
|
359
|
+
- "20128:20128"
|
|
360
|
+
|
|
361
|
+
volumes:
|
|
362
|
+
9router-data:`;
|
|
363
|
+
} else if (isLocal) {
|
|
364
|
+
const ollamaModelTag = String(selectedModel || 'ollama/gemma4:e2b').replace('ollama/', '');
|
|
365
|
+
compose = `name: ${singleComposeName}
|
|
366
|
+
services:
|
|
367
|
+
ai-bot:
|
|
368
|
+
build: .
|
|
369
|
+
container_name: ${singleAppContainerName}
|
|
370
|
+
restart: always
|
|
371
|
+
env_file: .env
|
|
372
|
+
${appEnvironmentBlock} depends_on:
|
|
373
|
+
ollama:
|
|
374
|
+
condition: service_healthy
|
|
375
|
+
${hasBrowser ? `${extraHostsBlock}\n` : ''} ports:
|
|
376
|
+
- "18791:18791"
|
|
377
|
+
volumes:
|
|
378
|
+
- ${volumeMount}
|
|
379
|
+
|
|
380
|
+
ollama:
|
|
381
|
+
image: ollama/ollama:latest
|
|
382
|
+
container_name: ${singleOllamaContainerName}
|
|
383
|
+
restart: always
|
|
384
|
+
environment:
|
|
385
|
+
- OLLAMA_KEEP_ALIVE=24h
|
|
386
|
+
- OLLAMA_NUM_PARALLEL=${singleOllamaNumParallel}
|
|
387
|
+
volumes:
|
|
388
|
+
- ollama-data:/root/.ollama
|
|
389
|
+
entrypoint:
|
|
390
|
+
- /bin/sh
|
|
391
|
+
- -c
|
|
392
|
+
- |
|
|
393
|
+
ollama serve &
|
|
394
|
+
until ollama list > /dev/null 2>&1; do sleep 1; done
|
|
395
|
+
ollama pull ${ollamaModelTag}
|
|
396
|
+
wait
|
|
397
|
+
healthcheck:
|
|
398
|
+
test: ["CMD-SHELL", "ollama list > /dev/null 2>&1"]
|
|
399
|
+
interval: 10s
|
|
400
|
+
timeout: 5s
|
|
401
|
+
retries: 10
|
|
402
|
+
start_period: 30s
|
|
403
|
+
|
|
404
|
+
volumes:
|
|
405
|
+
ollama-data:`;
|
|
406
|
+
} else {
|
|
407
|
+
compose = `name: ${singleComposeName}
|
|
408
|
+
services:
|
|
409
|
+
ai-bot:
|
|
410
|
+
build: .
|
|
411
|
+
container_name: ${singleAppContainerName}
|
|
412
|
+
restart: always
|
|
413
|
+
env_file:
|
|
414
|
+
- .env
|
|
415
|
+
${appEnvironmentBlock}${plainSingleExtraHosts ? `${extraHostsBlock}\n` : ''} volumes:
|
|
416
|
+
- ${volumeMount}
|
|
417
|
+
ports:
|
|
418
|
+
- "18791:18791"`;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
return {
|
|
422
|
+
dockerfile,
|
|
423
|
+
compose,
|
|
424
|
+
syncScript,
|
|
425
|
+
docker9RouterEntrypointScript,
|
|
426
|
+
gatewayPatchCmd: buildGatewayPatchCmd(),
|
|
427
|
+
};
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
root.__openclawDockerGen = {
|
|
431
|
+
encodeBase64Utf8,
|
|
432
|
+
indentBlock,
|
|
433
|
+
build9RouterSmartRouteSyncScript,
|
|
434
|
+
build9RouterPatchScript,
|
|
435
|
+
build9RouterComposeEntrypointScript,
|
|
436
|
+
buildGatewayPatchCmd,
|
|
437
|
+
buildDockerArtifacts,
|
|
438
|
+
};
|
|
439
|
+
|
|
440
|
+
})(typeof globalThis !== 'undefined' ? globalThis : {});
|
|
441
|
+
if (typeof exports !== 'undefined' && typeof globalThis !== 'undefined' && globalThis.__openclawDockerGen) {
|
|
442
|
+
Object.assign(exports, globalThis.__openclawDockerGen);
|
|
443
|
+
}
|