@aion0/bastion 0.1.7 → 0.1.10
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/dist/api.d.ts +72 -0
- package/dist/api.d.ts.map +1 -0
- package/dist/api.js +121 -0
- package/dist/api.js.map +1 -0
- package/dist/cli/commands/openclaw.d.ts.map +1 -1
- package/dist/cli/commands/openclaw.js +268 -63
- package/dist/cli/commands/openclaw.js.map +1 -1
- package/dist/cli/daemon.d.ts.map +1 -1
- package/dist/cli/daemon.js +6 -1
- package/dist/cli/daemon.js.map +1 -1
- package/dist/core/bootstrap.d.ts +30 -0
- package/dist/core/bootstrap.d.ts.map +1 -0
- package/dist/core/bootstrap.js +221 -0
- package/dist/core/bootstrap.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +14 -175
- package/dist/index.js.map +1 -1
- package/dist/plugins/builtin/dlp-scanner.d.ts +1 -1
- package/dist/plugins/builtin/dlp-scanner.d.ts.map +1 -1
- package/dist/plugins/builtin/dlp-scanner.js +25 -1
- package/dist/plugins/builtin/dlp-scanner.js.map +1 -1
- package/dist/plugins/builtin/tool-guard.d.ts +1 -1
- package/dist/plugins/builtin/tool-guard.d.ts.map +1 -1
- package/dist/plugins/builtin/tool-guard.js +11 -1
- package/dist/plugins/builtin/tool-guard.js.map +1 -1
- package/dist/plugins/index.d.ts +3 -1
- package/dist/plugins/index.d.ts.map +1 -1
- package/dist/plugins/index.js +17 -1
- package/dist/plugins/index.js.map +1 -1
- package/integration/openclaw-docker/fix-issues/not-install-brew/Dockerfile +155 -0
- package/integration/openclaw-docker/fix-issues/not-install-brew/Dockerfile.upstream +133 -0
- package/integration/openclaw-docker/fix-issues/not-install-brew/docker-setup.sh +578 -0
- package/integration/openclaw-docker/fix-issues/not-install-brew/docker-setup.sh.upstream +570 -0
- package/integration/openclaw-docker/fix-issues/not-install-brew/release_notes.md +53 -0
- package/package.json +14 -4
package/dist/api.d.ts
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import type { BastionConfig } from './config/schema.js';
|
|
2
|
+
import type { ConfigManager } from './config/manager.js';
|
|
3
|
+
import type { PluginManager } from './plugins/index.js';
|
|
4
|
+
export interface BastionServerOptions {
|
|
5
|
+
configPath?: string;
|
|
6
|
+
config?: Partial<BastionConfig>;
|
|
7
|
+
port?: number;
|
|
8
|
+
host?: string;
|
|
9
|
+
silent?: boolean;
|
|
10
|
+
logLevel?: 'debug' | 'info' | 'warn' | 'error';
|
|
11
|
+
dbPath?: string;
|
|
12
|
+
skipPidFile?: boolean;
|
|
13
|
+
skipRetention?: boolean;
|
|
14
|
+
}
|
|
15
|
+
export interface DlpFindingEvent {
|
|
16
|
+
requestId: string;
|
|
17
|
+
patternName: string;
|
|
18
|
+
patternCategory: string;
|
|
19
|
+
action: string;
|
|
20
|
+
matchCount: number;
|
|
21
|
+
direction: string;
|
|
22
|
+
}
|
|
23
|
+
export interface ToolGuardAlertEvent {
|
|
24
|
+
requestId: string;
|
|
25
|
+
toolName: string;
|
|
26
|
+
ruleId: string;
|
|
27
|
+
ruleName: string;
|
|
28
|
+
severity: string;
|
|
29
|
+
category: string;
|
|
30
|
+
action: string;
|
|
31
|
+
matchedText: string;
|
|
32
|
+
}
|
|
33
|
+
export interface RequestCompleteEvent {
|
|
34
|
+
requestId: string;
|
|
35
|
+
statusCode: number;
|
|
36
|
+
latencyMs: number;
|
|
37
|
+
isStreaming: boolean;
|
|
38
|
+
provider: string;
|
|
39
|
+
model: string;
|
|
40
|
+
usage: {
|
|
41
|
+
inputTokens: number;
|
|
42
|
+
outputTokens: number;
|
|
43
|
+
cacheCreationTokens: number;
|
|
44
|
+
cacheReadTokens: number;
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
export interface CertificateInfo {
|
|
48
|
+
certPath: string;
|
|
49
|
+
exists: boolean;
|
|
50
|
+
}
|
|
51
|
+
export interface BastionServer {
|
|
52
|
+
readonly port: number;
|
|
53
|
+
readonly host: string;
|
|
54
|
+
readonly url: string;
|
|
55
|
+
readonly caCertPath: string;
|
|
56
|
+
readonly dashboardUrl: string;
|
|
57
|
+
readonly authToken: string | undefined;
|
|
58
|
+
readonly config: BastionConfig;
|
|
59
|
+
readonly plugins: PluginManager;
|
|
60
|
+
readonly configManager: ConfigManager;
|
|
61
|
+
on(event: 'dlp:finding', handler: (data: DlpFindingEvent) => void): BastionServer;
|
|
62
|
+
on(event: 'toolguard:alert', handler: (data: ToolGuardAlertEvent) => void): BastionServer;
|
|
63
|
+
on(event: 'request:complete', handler: (data: RequestCompleteEvent) => void): BastionServer;
|
|
64
|
+
on(event: 'error', handler: (error: Error) => void): BastionServer;
|
|
65
|
+
on(event: 'close', handler: () => void): BastionServer;
|
|
66
|
+
off(event: string, handler: (...args: unknown[]) => void): BastionServer;
|
|
67
|
+
removeAllListeners(event?: string): BastionServer;
|
|
68
|
+
close(): Promise<void>;
|
|
69
|
+
}
|
|
70
|
+
export declare function createServer(options?: BastionServerOptions): Promise<BastionServer>;
|
|
71
|
+
export declare function ensureCertificate(): Promise<CertificateInfo>;
|
|
72
|
+
//# sourceMappingURL=api.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"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"}
|
package/dist/api.js
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createServer = createServer;
|
|
4
|
+
exports.ensureCertificate = ensureCertificate;
|
|
5
|
+
const node_events_1 = require("node:events");
|
|
6
|
+
const bootstrap_js_1 = require("./core/bootstrap.js");
|
|
7
|
+
const certs_js_1 = require("./proxy/certs.js");
|
|
8
|
+
const database_js_1 = require("./storage/database.js");
|
|
9
|
+
const logger_js_1 = require("./utils/logger.js");
|
|
10
|
+
const log = (0, logger_js_1.createLogger)('api');
|
|
11
|
+
// ── Implementation ──
|
|
12
|
+
class BastionServerImpl extends node_events_1.EventEmitter {
|
|
13
|
+
result;
|
|
14
|
+
closed = false;
|
|
15
|
+
constructor(result) {
|
|
16
|
+
super();
|
|
17
|
+
this.result = result;
|
|
18
|
+
this.bridgeEvents(result.eventBus);
|
|
19
|
+
}
|
|
20
|
+
get port() {
|
|
21
|
+
const addr = this.result.server.address();
|
|
22
|
+
return addr?.port ?? this.result.config.server.port;
|
|
23
|
+
}
|
|
24
|
+
get host() {
|
|
25
|
+
return this.result.config.server.host;
|
|
26
|
+
}
|
|
27
|
+
get url() {
|
|
28
|
+
return `http://${this.host}:${this.port}`;
|
|
29
|
+
}
|
|
30
|
+
get caCertPath() {
|
|
31
|
+
return (0, certs_js_1.getCACertPath)();
|
|
32
|
+
}
|
|
33
|
+
get dashboardUrl() {
|
|
34
|
+
return `${this.url}/dashboard`;
|
|
35
|
+
}
|
|
36
|
+
get authToken() {
|
|
37
|
+
return this.result.authToken;
|
|
38
|
+
}
|
|
39
|
+
get config() {
|
|
40
|
+
return this.result.configManager.get();
|
|
41
|
+
}
|
|
42
|
+
get plugins() {
|
|
43
|
+
return this.result.pluginManager;
|
|
44
|
+
}
|
|
45
|
+
get configManager() {
|
|
46
|
+
return this.result.configManager;
|
|
47
|
+
}
|
|
48
|
+
async close() {
|
|
49
|
+
if (this.closed)
|
|
50
|
+
return;
|
|
51
|
+
this.closed = true;
|
|
52
|
+
log.info('Closing BastionServer');
|
|
53
|
+
// Stop purge interval
|
|
54
|
+
if (this.result.purgeInterval) {
|
|
55
|
+
clearInterval(this.result.purgeInterval);
|
|
56
|
+
}
|
|
57
|
+
// Close HTTP server
|
|
58
|
+
await new Promise((resolve) => {
|
|
59
|
+
this.result.server.close(() => resolve());
|
|
60
|
+
});
|
|
61
|
+
// Run destroy callbacks (external plugins)
|
|
62
|
+
for (const cb of this.result.destroyCallbacks) {
|
|
63
|
+
try {
|
|
64
|
+
await cb();
|
|
65
|
+
}
|
|
66
|
+
catch (err) {
|
|
67
|
+
log.warn('Destroy callback failed', { error: err.message });
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
// Clean up event bus
|
|
71
|
+
this.result.eventBus.removeAllListeners();
|
|
72
|
+
// Close database
|
|
73
|
+
(0, database_js_1.closeDatabase)();
|
|
74
|
+
this.emit('close');
|
|
75
|
+
this.removeAllListeners();
|
|
76
|
+
log.info('BastionServer closed');
|
|
77
|
+
}
|
|
78
|
+
bridgeEvents(eventBus) {
|
|
79
|
+
eventBus.on('dlp:finding', (data) => {
|
|
80
|
+
this.emit('dlp:finding', data);
|
|
81
|
+
});
|
|
82
|
+
eventBus.on('toolguard:alert', (data) => {
|
|
83
|
+
this.emit('toolguard:alert', data);
|
|
84
|
+
});
|
|
85
|
+
eventBus.on('request:complete', (data) => {
|
|
86
|
+
this.emit('request:complete', data);
|
|
87
|
+
});
|
|
88
|
+
eventBus.on('request:blocked', (data) => {
|
|
89
|
+
this.emit('request:blocked', data);
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
// ── Public API ──
|
|
94
|
+
async function createServer(options) {
|
|
95
|
+
const result = await (0, bootstrap_js_1.bootstrap)({
|
|
96
|
+
configPath: options?.configPath,
|
|
97
|
+
configOverrides: options?.config,
|
|
98
|
+
silent: options?.silent ?? true,
|
|
99
|
+
logLevel: options?.logLevel,
|
|
100
|
+
port: options?.port,
|
|
101
|
+
host: options?.host,
|
|
102
|
+
dbPath: options?.dbPath,
|
|
103
|
+
skipPidFile: options?.skipPidFile ?? true,
|
|
104
|
+
skipRetention: options?.skipRetention,
|
|
105
|
+
});
|
|
106
|
+
const server = new BastionServerImpl(result);
|
|
107
|
+
log.info('BastionServer created', {
|
|
108
|
+
port: server.port,
|
|
109
|
+
host: server.host,
|
|
110
|
+
url: server.url,
|
|
111
|
+
});
|
|
112
|
+
return server;
|
|
113
|
+
}
|
|
114
|
+
async function ensureCertificate() {
|
|
115
|
+
(0, certs_js_1.ensureCA)();
|
|
116
|
+
return {
|
|
117
|
+
certPath: (0, certs_js_1.getCACertPath)(),
|
|
118
|
+
exists: true,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
//# sourceMappingURL=api.js.map
|
package/dist/api.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.js","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openclaw.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/openclaw.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"openclaw.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/openclaw.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA6hBzC,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA41B9D"}
|
|
@@ -135,6 +135,57 @@ if (proxyUrl) {
|
|
|
135
135
|
} else { L('no proxy URL found, skipping'); }
|
|
136
136
|
`;
|
|
137
137
|
// ── shared helpers ───────────────────────────────────────────────────────────
|
|
138
|
+
/**
|
|
139
|
+
* 3-way merge a fix file into the upstream source using `git merge-file`.
|
|
140
|
+
* - srcFile: current upstream file (in the OpenClaw source directory)
|
|
141
|
+
* - baseFile: the upstream version our fixes were originally based on (.upstream)
|
|
142
|
+
* - oursFile: our modified version with fixes
|
|
143
|
+
* Falls back to overwrite if git is unavailable or conflicts are unresolvable.
|
|
144
|
+
*/
|
|
145
|
+
function mergeFixFile(srcFile, baseFile, oursFile, label) {
|
|
146
|
+
if (!(0, node_fs_1.existsSync)(baseFile)) {
|
|
147
|
+
// No merge base — just overwrite
|
|
148
|
+
(0, node_fs_1.copyFileSync)(oursFile, srcFile);
|
|
149
|
+
console.log(` Patched ${label} (no merge base, overwrite)`);
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
// Work on a temp copy so merge failures don't corrupt the original
|
|
153
|
+
const tmpFile = srcFile + '.bastion-merge';
|
|
154
|
+
(0, node_fs_1.copyFileSync)(srcFile, tmpFile);
|
|
155
|
+
try {
|
|
156
|
+
(0, node_child_process_1.execSync)(`git merge-file "${tmpFile}" "${baseFile}" "${oursFile}"`, {
|
|
157
|
+
stdio: 'pipe',
|
|
158
|
+
});
|
|
159
|
+
// Exit 0 — clean merge
|
|
160
|
+
(0, node_fs_1.copyFileSync)(tmpFile, srcFile);
|
|
161
|
+
console.log(` Merged ${label}`);
|
|
162
|
+
}
|
|
163
|
+
catch (err) {
|
|
164
|
+
const exitCode = err.status;
|
|
165
|
+
if (exitCode !== undefined && exitCode > 0) {
|
|
166
|
+
// Conflicts exist — check if they contain real conflict markers
|
|
167
|
+
const merged = (0, node_fs_1.readFileSync)(tmpFile, 'utf-8');
|
|
168
|
+
if (merged.includes('<<<<<<<')) {
|
|
169
|
+
console.log(` WARNING: ${label} merge has conflicts, using our version.`);
|
|
170
|
+
(0, node_fs_1.copyFileSync)(oursFile, srcFile);
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
// Some changes but no conflict markers — safe to use
|
|
174
|
+
(0, node_fs_1.copyFileSync)(tmpFile, srcFile);
|
|
175
|
+
console.log(` Merged ${label}`);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
else {
|
|
179
|
+
// git merge-file not available or other error
|
|
180
|
+
console.log(` WARNING: git merge-file failed for ${label}, using our version.`);
|
|
181
|
+
(0, node_fs_1.copyFileSync)(oursFile, srcFile);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
finally {
|
|
185
|
+
if ((0, node_fs_1.existsSync)(tmpFile))
|
|
186
|
+
(0, node_fs_1.unlinkSync)(tmpFile);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
138
189
|
function generateToken() {
|
|
139
190
|
return (0, node_crypto_1.randomBytes)(32).toString('hex');
|
|
140
191
|
}
|
|
@@ -185,6 +236,16 @@ function envVal(name, key) {
|
|
|
185
236
|
const match = content.match(new RegExp(`^${key}=(.*)$`, 'm'));
|
|
186
237
|
return match?.[1] ?? '';
|
|
187
238
|
}
|
|
239
|
+
/** Check if a Docker image has real Homebrew installed (not the brew-shim). */
|
|
240
|
+
function imageHasRealBrew(image) {
|
|
241
|
+
try {
|
|
242
|
+
const output = (0, node_child_process_1.execSync)(`docker run --rm --entrypoint sh ${image} -c "test -x /home/linuxbrew/.linuxbrew/bin/brew && echo yes || echo no"`, { stdio: 'pipe', timeout: 15_000 }).toString().trim();
|
|
243
|
+
return output === 'yes';
|
|
244
|
+
}
|
|
245
|
+
catch {
|
|
246
|
+
return false;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
188
249
|
function writeEnvFile(dir, vars) {
|
|
189
250
|
const content = Object.entries(vars)
|
|
190
251
|
.map(([k, v]) => `${k}=${v}`)
|
|
@@ -241,10 +302,13 @@ function generateComposeFile(image) {
|
|
|
241
302
|
`;
|
|
242
303
|
}
|
|
243
304
|
/** Bastion proxy overlay — merged via docker compose -f ... -f ... */
|
|
244
|
-
function generateBastionOverride(caPath) {
|
|
305
|
+
function generateBastionOverride(caPath, options) {
|
|
306
|
+
const brewMount = options?.skipBrewShim ? '' : '\n - ./brew:/usr/local/bin/brew:ro';
|
|
307
|
+
// Only run as root when brew-shim is needed (apt-get requires root).
|
|
308
|
+
// Real Homebrew refuses to run as root, so we must keep the default user (node).
|
|
309
|
+
const userLine = options?.skipBrewShim ? '' : '\n user: root';
|
|
245
310
|
return `services:
|
|
246
|
-
openclaw-gateway
|
|
247
|
-
user: root
|
|
311
|
+
openclaw-gateway:${userLine}
|
|
248
312
|
environment:
|
|
249
313
|
HOME: /home/node
|
|
250
314
|
HTTPS_PROXY: "http://openclaw-gw@host.docker.internal:\${BASTION_PORT:-8420}"
|
|
@@ -253,11 +317,9 @@ function generateBastionOverride(caPath) {
|
|
|
253
317
|
NODE_OPTIONS: "--import /opt/bastion/proxy-bootstrap.mjs"
|
|
254
318
|
volumes:
|
|
255
319
|
- ${caPath}:/etc/ssl/certs/bastion-ca.crt:ro
|
|
256
|
-
- ./proxy-bootstrap.mjs:/opt/bastion/proxy-bootstrap.mjs:ro
|
|
257
|
-
- ./brew:/usr/local/bin/brew:ro
|
|
320
|
+
- ./proxy-bootstrap.mjs:/opt/bastion/proxy-bootstrap.mjs:ro${brewMount}
|
|
258
321
|
|
|
259
|
-
openclaw-cli
|
|
260
|
-
user: root
|
|
322
|
+
openclaw-cli:${userLine}
|
|
261
323
|
environment:
|
|
262
324
|
HOME: /home/node
|
|
263
325
|
HTTPS_PROXY: "http://openclaw-cli@host.docker.internal:\${BASTION_PORT:-8420}"
|
|
@@ -266,8 +328,7 @@ function generateBastionOverride(caPath) {
|
|
|
266
328
|
NODE_OPTIONS: "--import /opt/bastion/proxy-bootstrap.mjs"
|
|
267
329
|
volumes:
|
|
268
330
|
- ${caPath}:/etc/ssl/certs/bastion-ca.crt:ro
|
|
269
|
-
- ./proxy-bootstrap.mjs:/opt/bastion/proxy-bootstrap.mjs:ro
|
|
270
|
-
- ./brew:/usr/local/bin/brew:ro
|
|
331
|
+
- ./proxy-bootstrap.mjs:/opt/bastion/proxy-bootstrap.mjs:ro${brewMount}
|
|
271
332
|
`;
|
|
272
333
|
}
|
|
273
334
|
/** Build the list of compose file args for an instance. */
|
|
@@ -539,10 +600,17 @@ function registerOpenclawCommand(program) {
|
|
|
539
600
|
// If instance already exists, update overlay + bootstrap and start
|
|
540
601
|
if ((0, node_fs_1.existsSync)(dir)) {
|
|
541
602
|
console.log(`Instance '${name}' exists, starting...`);
|
|
542
|
-
|
|
603
|
+
const instanceImage = envVal(name, 'OPENCLAW_IMAGE') || options.image;
|
|
604
|
+
const hasRealBrew = imageHasRealBrew(instanceImage);
|
|
605
|
+
if (hasRealBrew) {
|
|
606
|
+
console.log(' Image has real Homebrew installed — skipping brew-shim.');
|
|
607
|
+
}
|
|
608
|
+
// Always update proxy bootstrap + Bastion override (idempotent)
|
|
543
609
|
(0, node_fs_1.writeFileSync)((0, node_path_1.join)(dir, 'proxy-bootstrap.mjs'), PROXY_BOOTSTRAP_CONTENT, 'utf-8');
|
|
544
|
-
|
|
545
|
-
|
|
610
|
+
if (!hasRealBrew) {
|
|
611
|
+
(0, node_fs_1.writeFileSync)((0, node_path_1.join)(dir, 'brew'), BREW_SHIM_CONTENT, { mode: 0o755 });
|
|
612
|
+
}
|
|
613
|
+
(0, node_fs_1.writeFileSync)((0, node_path_1.join)(dir, 'docker-compose.bastion.yml'), generateBastionOverride(caPath, { skipBrewShim: hasRealBrew }), 'utf-8');
|
|
546
614
|
// Migrate old-style compose that had Bastion proxy baked in
|
|
547
615
|
const existingCompose = (0, node_fs_1.readFileSync)((0, node_path_1.join)(dir, 'docker-compose.yml'), 'utf-8');
|
|
548
616
|
if (existingCompose.includes('NODE_EXTRA_CA_CERTS')) {
|
|
@@ -563,73 +631,80 @@ function registerOpenclawCommand(program) {
|
|
|
563
631
|
console.log(`Dashboard: http://127.0.0.1:${finalPort}/?token=${finalToken}`);
|
|
564
632
|
return;
|
|
565
633
|
}
|
|
566
|
-
// Create new instance
|
|
634
|
+
// Create new instance — delegate to docker-setup.sh for onboarding,
|
|
635
|
+
// permissions, gateway config, sandbox setup, etc.
|
|
567
636
|
const configDir = options.configDir ?? (0, node_path_1.join)((0, node_os_1.homedir)(), `.openclaw-${name}`);
|
|
568
637
|
const workspaceDir = options.workspace ?? (0, node_path_1.join)((0, node_os_1.homedir)(), `openclaw-${name}`, 'workspace');
|
|
569
638
|
(0, node_fs_1.mkdirSync)(dir, { recursive: true });
|
|
570
|
-
|
|
571
|
-
(0, node_fs_1.mkdirSync)(workspaceDir, { recursive: true });
|
|
572
|
-
(0, node_fs_1.mkdirSync)((0, node_path_1.join)(configDir, 'devices'), { recursive: true });
|
|
573
|
-
const token = generateToken();
|
|
574
|
-
writeEnvFile(dir, {
|
|
575
|
-
OPENCLAW_IMAGE: options.image,
|
|
576
|
-
OPENCLAW_CONFIG_DIR: configDir,
|
|
577
|
-
OPENCLAW_WORKSPACE_DIR: workspaceDir,
|
|
578
|
-
OPENCLAW_GATEWAY_TOKEN: token,
|
|
579
|
-
OPENCLAW_GATEWAY_PORT: String(port),
|
|
580
|
-
OPENCLAW_BRIDGE_PORT: String(bridgePort),
|
|
581
|
-
OPENCLAW_GATEWAY_BIND: 'lan',
|
|
582
|
-
BASTION_PORT: String(bastionPort),
|
|
583
|
-
CLAUDE_AI_SESSION_KEY: '',
|
|
584
|
-
CLAUDE_WEB_SESSION_KEY: '',
|
|
585
|
-
CLAUDE_WEB_COOKIE: '',
|
|
586
|
-
});
|
|
639
|
+
// Generate docker-compose.yml (docker-setup.sh expects it in ROOT_DIR)
|
|
587
640
|
(0, node_fs_1.writeFileSync)((0, node_path_1.join)(dir, 'docker-compose.yml'), generateComposeFile(options.image), 'utf-8');
|
|
588
|
-
|
|
641
|
+
// Copy docker-setup.sh from fix-issues
|
|
642
|
+
const fixesDir = (0, node_path_1.resolve)((0, node_path_1.join)(__dirname, '..', '..', '..', 'integration', 'openclaw-docker', 'fix-issues', 'not-install-brew'));
|
|
643
|
+
const setupScript = (0, node_path_1.join)(fixesDir, 'docker-setup.sh');
|
|
644
|
+
if (!(0, node_fs_1.existsSync)(setupScript)) {
|
|
645
|
+
console.error('docker-setup.sh not found. Bastion installation may be incomplete.');
|
|
646
|
+
process.exit(1);
|
|
647
|
+
}
|
|
648
|
+
(0, node_fs_1.copyFileSync)(setupScript, (0, node_path_1.join)(dir, 'docker-setup.sh'));
|
|
649
|
+
// Run docker-setup.sh — it handles: .env, permissions, onboarding,
|
|
650
|
+
// gateway config, controlUi origin, sandbox setup, etc.
|
|
651
|
+
console.log(`==> Running OpenClaw Docker setup (gateway: ${port}, bridge: ${bridgePort})...`);
|
|
652
|
+
const setupCode = await new Promise((resolve) => {
|
|
653
|
+
const child = (0, node_child_process_1.spawn)('bash', [(0, node_path_1.join)(dir, 'docker-setup.sh')], {
|
|
654
|
+
stdio: 'inherit',
|
|
655
|
+
env: {
|
|
656
|
+
...process.env,
|
|
657
|
+
OPENCLAW_SKIP_BUILD: '1',
|
|
658
|
+
OPENCLAW_IMAGE: options.image,
|
|
659
|
+
OPENCLAW_CONFIG_DIR: configDir,
|
|
660
|
+
OPENCLAW_WORKSPACE_DIR: workspaceDir,
|
|
661
|
+
OPENCLAW_GATEWAY_PORT: String(port),
|
|
662
|
+
OPENCLAW_BRIDGE_PORT: String(bridgePort),
|
|
663
|
+
OPENCLAW_GATEWAY_BIND: 'lan',
|
|
664
|
+
// Match project name used by dc() so later commands find the containers
|
|
665
|
+
COMPOSE_PROJECT_NAME: `openclaw-${name}`,
|
|
666
|
+
},
|
|
667
|
+
});
|
|
668
|
+
child.on('close', (c) => resolve(c ?? 1));
|
|
669
|
+
child.on('error', (err) => {
|
|
670
|
+
console.error(`docker-setup.sh error: ${err.message}`);
|
|
671
|
+
resolve(1);
|
|
672
|
+
});
|
|
673
|
+
});
|
|
674
|
+
if (setupCode !== 0) {
|
|
675
|
+
console.error('Docker setup failed.');
|
|
676
|
+
process.exit(setupCode);
|
|
677
|
+
}
|
|
678
|
+
// Append Bastion-specific env vars to .env created by docker-setup.sh
|
|
679
|
+
const envFile = (0, node_path_1.join)(dir, '.env');
|
|
680
|
+
let envContent = (0, node_fs_1.existsSync)(envFile) ? (0, node_fs_1.readFileSync)(envFile, 'utf-8') : '';
|
|
681
|
+
if (!envContent.includes('BASTION_PORT=')) {
|
|
682
|
+
envContent += `BASTION_PORT=${bastionPort}\n`;
|
|
683
|
+
(0, node_fs_1.writeFileSync)(envFile, envContent, 'utf-8');
|
|
684
|
+
}
|
|
685
|
+
// Add Bastion proxy overlay on top
|
|
686
|
+
const hasRealBrew = imageHasRealBrew(options.image);
|
|
687
|
+
if (hasRealBrew) {
|
|
688
|
+
console.log(' Image has real Homebrew installed — skipping brew-shim.');
|
|
689
|
+
}
|
|
589
690
|
(0, node_fs_1.writeFileSync)((0, node_path_1.join)(dir, 'proxy-bootstrap.mjs'), PROXY_BOOTSTRAP_CONTENT, 'utf-8');
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
console.log('==> Initializing gateway config...');
|
|
593
|
-
let code = await dc(name, ['run', '--rm', 'openclaw-cli', 'config', 'set', 'gateway.mode', 'local']);
|
|
594
|
-
if (code !== 0) {
|
|
595
|
-
console.error('Failed to initialize gateway config');
|
|
596
|
-
process.exit(code);
|
|
691
|
+
if (!hasRealBrew) {
|
|
692
|
+
(0, node_fs_1.writeFileSync)((0, node_path_1.join)(dir, 'brew'), BREW_SHIM_CONTENT, { mode: 0o755 });
|
|
597
693
|
}
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
console.log('==>
|
|
601
|
-
code = await dc(name, ['up', '-d', 'openclaw-gateway']);
|
|
694
|
+
(0, node_fs_1.writeFileSync)((0, node_path_1.join)(dir, 'docker-compose.bastion.yml'), generateBastionOverride(caPath, { skipBrewShim: hasRealBrew }), 'utf-8');
|
|
695
|
+
// Restart gateway with Bastion proxy overlay
|
|
696
|
+
console.log('==> Restarting gateway with Bastion proxy overlay...');
|
|
697
|
+
const code = await dc(name, ['up', '-d', '--force-recreate', 'openclaw-gateway']);
|
|
602
698
|
if (code !== 0)
|
|
603
699
|
process.exit(code);
|
|
604
700
|
await sleep(3000);
|
|
605
|
-
console.log('');
|
|
606
|
-
console.log('==> Running interactive onboarding...');
|
|
607
|
-
console.log(` When prompted for gateway token, use: ${token}`);
|
|
608
|
-
console.log('');
|
|
609
|
-
code = await dc(name, ['exec', 'openclaw-gateway', 'node', 'dist/index.js', 'onboard', '--no-install-daemon']);
|
|
610
|
-
if (code !== 0) {
|
|
611
|
-
console.error('Onboarding failed');
|
|
612
|
-
process.exit(code);
|
|
613
|
-
}
|
|
614
|
-
console.log('');
|
|
615
|
-
console.log('==> Applying post-onboard fixes...');
|
|
616
|
-
syncToken(name);
|
|
617
|
-
fixBind(name);
|
|
618
|
-
console.log('==> Restarting gateway...');
|
|
619
|
-
await dc(name, ['restart', 'openclaw-gateway']);
|
|
620
|
-
await sleep(3000);
|
|
621
701
|
approveDevices(name);
|
|
622
|
-
await dc(name, ['restart', 'openclaw-gateway']);
|
|
623
|
-
await sleep(2000);
|
|
624
702
|
const finalToken = envVal(name, 'OPENCLAW_GATEWAY_TOKEN');
|
|
625
703
|
console.log('');
|
|
626
704
|
console.log(`==> Instance '${name}' is ready!`);
|
|
627
705
|
console.log('');
|
|
628
706
|
console.log(` Dashboard: http://127.0.0.1:${port}/?token=${finalToken}`);
|
|
629
707
|
console.log('');
|
|
630
|
-
console.log(' Open the URL above in your browser.');
|
|
631
|
-
console.log(' If prompted to pair, refresh the page — devices are auto-approved.');
|
|
632
|
-
console.log('');
|
|
633
708
|
});
|
|
634
709
|
// bastion openclaw docker run
|
|
635
710
|
docker
|
|
@@ -1058,5 +1133,135 @@ function registerOpenclawCommand(program) {
|
|
|
1058
1133
|
process.exitCode = code ?? 0;
|
|
1059
1134
|
});
|
|
1060
1135
|
});
|
|
1136
|
+
// ════════════════════════════════════════════════════════════════════════════
|
|
1137
|
+
// bastion openclaw build ...
|
|
1138
|
+
// ════════════════════════════════════════════════════════════════════════════
|
|
1139
|
+
openclaw
|
|
1140
|
+
.command('build')
|
|
1141
|
+
.description('Build OpenClaw Docker image from source')
|
|
1142
|
+
.option('--tag <tag>', 'Git branch/tag to build', 'main')
|
|
1143
|
+
.option('--brew', 'Install Homebrew (~500MB) for brew-based skills')
|
|
1144
|
+
.option('--browser', 'Install Chromium + Xvfb (~300MB) for browser automation')
|
|
1145
|
+
.option('--docker-cli', 'Install Docker CLI (~50MB) for sandbox container management')
|
|
1146
|
+
.option('--apt-packages <packages>', 'Extra apt packages to install (comma-separated)')
|
|
1147
|
+
.option('--image <name>', 'Docker image name', DEFAULT_IMAGE)
|
|
1148
|
+
.option('--src <path>', 'Path to existing OpenClaw source (skip clone)')
|
|
1149
|
+
.action(async (options) => {
|
|
1150
|
+
checkDocker();
|
|
1151
|
+
const srcDir = options.src ? (0, node_path_1.resolve)(options.src) : (0, node_path_1.join)(OPENCLAW_DIR, 'src');
|
|
1152
|
+
// Clone or update source
|
|
1153
|
+
if (options.src) {
|
|
1154
|
+
if (!(0, node_fs_1.existsSync)(srcDir)) {
|
|
1155
|
+
console.error(`Source directory not found: ${srcDir}`);
|
|
1156
|
+
process.exit(1);
|
|
1157
|
+
}
|
|
1158
|
+
console.log(`==> Using existing source: ${srcDir}`);
|
|
1159
|
+
}
|
|
1160
|
+
else {
|
|
1161
|
+
console.log(`==> Preparing source (branch/tag: ${options.tag})...`);
|
|
1162
|
+
if ((0, node_fs_1.existsSync)(srcDir)) {
|
|
1163
|
+
console.log(' Source directory exists, fetching latest...');
|
|
1164
|
+
(0, node_child_process_1.execSync)(`git -C "${srcDir}" fetch --all --tags`, { stdio: 'inherit' });
|
|
1165
|
+
(0, node_child_process_1.execSync)(`git -C "${srcDir}" checkout "${options.tag}"`, { stdio: 'inherit' });
|
|
1166
|
+
try {
|
|
1167
|
+
(0, node_child_process_1.execSync)(`git -C "${srcDir}" pull --ff-only`, { stdio: 'pipe' });
|
|
1168
|
+
}
|
|
1169
|
+
catch { /* ignore */ }
|
|
1170
|
+
}
|
|
1171
|
+
else {
|
|
1172
|
+
console.log(' Cloning openclaw repository...');
|
|
1173
|
+
(0, node_fs_1.mkdirSync)((0, node_path_1.join)(OPENCLAW_DIR), { recursive: true });
|
|
1174
|
+
(0, node_child_process_1.execSync)(`git clone --branch "${options.tag}" https://github.com/openclaw/openclaw.git "${srcDir}"`, { stdio: 'inherit' });
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
// Apply Bastion fixes — merge into upstream source via 3-way merge
|
|
1178
|
+
const fixesDir = (0, node_path_1.resolve)((0, node_path_1.join)(__dirname, '..', '..', '..', 'integration', 'openclaw-docker', 'fix-issues', 'not-install-brew'));
|
|
1179
|
+
if ((0, node_fs_1.existsSync)(fixesDir)) {
|
|
1180
|
+
console.log('==> Applying Bastion integration fixes...');
|
|
1181
|
+
// Dockerfile: 3-way merge (preserves upstream changes)
|
|
1182
|
+
const upstreamDockerfile = (0, node_path_1.join)(srcDir, 'Dockerfile');
|
|
1183
|
+
const baseDockerfile = (0, node_path_1.join)(fixesDir, 'Dockerfile.upstream');
|
|
1184
|
+
const oursDockerfile = (0, node_path_1.join)(fixesDir, 'Dockerfile');
|
|
1185
|
+
if ((0, node_fs_1.existsSync)(upstreamDockerfile) && (0, node_fs_1.existsSync)(oursDockerfile)) {
|
|
1186
|
+
mergeFixFile(upstreamDockerfile, baseDockerfile, oursDockerfile, 'Dockerfile');
|
|
1187
|
+
}
|
|
1188
|
+
// docker-setup.sh: 3-way merge (preserves upstream changes)
|
|
1189
|
+
const upstreamSetup = (0, node_path_1.join)(srcDir, 'docker-setup.sh');
|
|
1190
|
+
const baseSetup = (0, node_path_1.join)(fixesDir, 'docker-setup.sh.upstream');
|
|
1191
|
+
const oursSetup = (0, node_path_1.join)(fixesDir, 'docker-setup.sh');
|
|
1192
|
+
if ((0, node_fs_1.existsSync)(oursSetup)) {
|
|
1193
|
+
if ((0, node_fs_1.existsSync)(upstreamSetup)) {
|
|
1194
|
+
mergeFixFile(upstreamSetup, baseSetup, oursSetup, 'docker-setup.sh');
|
|
1195
|
+
}
|
|
1196
|
+
else {
|
|
1197
|
+
(0, node_fs_1.copyFileSync)(oursSetup, upstreamSetup);
|
|
1198
|
+
console.log(' Copied docker-setup.sh (upstream has none)');
|
|
1199
|
+
}
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
1202
|
+
else {
|
|
1203
|
+
console.log(' WARNING: Fix files not found, building with original Dockerfile.');
|
|
1204
|
+
}
|
|
1205
|
+
// Build Docker image
|
|
1206
|
+
console.log(`==> Building Docker image '${options.image}'...`);
|
|
1207
|
+
const buildArgs = ['build'];
|
|
1208
|
+
const extras = [];
|
|
1209
|
+
if (options.brew) {
|
|
1210
|
+
buildArgs.push('--build-arg', 'OPENCLAW_INSTALL_BREW=1');
|
|
1211
|
+
extras.push('Homebrew (~500MB)');
|
|
1212
|
+
}
|
|
1213
|
+
if (options.browser) {
|
|
1214
|
+
buildArgs.push('--build-arg', 'OPENCLAW_INSTALL_BROWSER=1');
|
|
1215
|
+
extras.push('Chromium + Xvfb (~300MB)');
|
|
1216
|
+
}
|
|
1217
|
+
if (options.dockerCli) {
|
|
1218
|
+
buildArgs.push('--build-arg', 'OPENCLAW_INSTALL_DOCKER_CLI=1');
|
|
1219
|
+
extras.push('Docker CLI (~50MB)');
|
|
1220
|
+
}
|
|
1221
|
+
if (options.aptPackages) {
|
|
1222
|
+
buildArgs.push('--build-arg', `OPENCLAW_DOCKER_APT_PACKAGES=${options.aptPackages}`);
|
|
1223
|
+
extras.push(`apt: ${options.aptPackages}`);
|
|
1224
|
+
}
|
|
1225
|
+
if (extras.length > 0) {
|
|
1226
|
+
console.log(` Optional components: ${extras.join(', ')}`);
|
|
1227
|
+
}
|
|
1228
|
+
// Show hints for unused optional components
|
|
1229
|
+
const hints = [];
|
|
1230
|
+
if (!options.brew) {
|
|
1231
|
+
hints.push('--brew : Homebrew for brew-based skills (1password-cli, signal-cli, etc.)');
|
|
1232
|
+
}
|
|
1233
|
+
if (!options.browser) {
|
|
1234
|
+
hints.push('--browser : Chromium for browser automation');
|
|
1235
|
+
}
|
|
1236
|
+
if (!options.dockerCli) {
|
|
1237
|
+
hints.push('--docker-cli : Docker CLI for sandbox container management');
|
|
1238
|
+
}
|
|
1239
|
+
if (hints.length > 0) {
|
|
1240
|
+
console.log('');
|
|
1241
|
+
console.log(' TIP: Optional build flags available:');
|
|
1242
|
+
for (const hint of hints) {
|
|
1243
|
+
console.log(` ${hint}`);
|
|
1244
|
+
}
|
|
1245
|
+
console.log(` Example: bastion openclaw build --brew --browser --docker-cli`);
|
|
1246
|
+
console.log('');
|
|
1247
|
+
}
|
|
1248
|
+
buildArgs.push('-t', options.image, '-f', (0, node_path_1.join)(srcDir, 'Dockerfile'), srcDir);
|
|
1249
|
+
const code = await new Promise((resolve) => {
|
|
1250
|
+
const child = (0, node_child_process_1.spawn)('docker', buildArgs, { stdio: 'inherit' });
|
|
1251
|
+
child.on('close', (c) => resolve(c ?? 1));
|
|
1252
|
+
child.on('error', (err) => {
|
|
1253
|
+
console.error(`docker build error: ${err.message}`);
|
|
1254
|
+
resolve(1);
|
|
1255
|
+
});
|
|
1256
|
+
});
|
|
1257
|
+
if (code !== 0) {
|
|
1258
|
+
console.error('Docker build failed.');
|
|
1259
|
+
process.exit(code);
|
|
1260
|
+
}
|
|
1261
|
+
console.log(`==> Build complete: ${options.image}`);
|
|
1262
|
+
if (options.brew) {
|
|
1263
|
+
console.log(' Homebrew installed — brew-shim will NOT be mounted for instances using this image.');
|
|
1264
|
+
}
|
|
1265
|
+
});
|
|
1061
1266
|
}
|
|
1062
1267
|
//# sourceMappingURL=openclaw.js.map
|