Repository navigation
Expand file tree
/
Copy pathdocker-entrypoint.mjs
More file actions
138 lines (133 loc) 路 4.21 KB
/
Copy pathdocker-entrypoint.mjs
File metadata and controls
138 lines (133 loc) 路 4.21 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
// Image activation owns unattended retained-volume repair; ordinary CLI startup only admits state.
import { spawn } from "node:child_process";
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { getCommandOptionsWithRootOptions } from "./cli-root-options.mjs";
import {
GATEWAY_RUN_BOOLEAN_FLAGS,
GATEWAY_RUN_VALUE_FLAGS,
isForegroundGatewayRunArgv,
} from "./gateway-run-argv.mjs";
const root = path.dirname(fileURLToPath(import.meta.url));
const command = process.argv.slice(2);
function executable(name) {
const candidates = name.includes(path.sep)
? [path.resolve(name)]
: (process.env.PATH ?? "").split(path.delimiter).map((dir) => path.resolve(dir, name));
for (const candidate of candidates) {
try {
fs.accessSync(candidate, fs.constants.X_OK);
if (fs.statSync(candidate).isFile()) {
return fs.realpathSync(candidate);
}
} catch {
// Match normal PATH lookup; exec reports an unavailable command below.
}
}
return undefined;
}
function gatewayInvocation(program) {
if (!command.length) {
throw new Error("Docker entrypoint requires a command");
}
let argv;
if (program === fs.realpathSync(process.execPath) && command[1]) {
let entry;
try {
entry = fs.realpathSync(command[1]);
} catch {
return undefined;
}
if (
!["openclaw.mjs", "dist/index.js", "dist/entry.js", "dist/entry.mjs"].some(
(file) => entry === path.join(root, file),
)
) {
return undefined;
}
argv = [process.execPath, entry, ...command.slice(2)];
} else if (program === path.join(root, "openclaw.mjs")) {
argv = [process.execPath, program, ...command.slice(1)];
} else {
return undefined;
}
if (!isForegroundGatewayRunArgv(argv)) {
return undefined;
}
const options = getCommandOptionsWithRootOptions(argv, {
commandPath: ["gateway"],
booleanFlags: [...GATEWAY_RUN_BOOLEAN_FLAGS],
valueFlags: [...GATEWAY_RUN_VALUE_FLAGS],
mode: "command-path",
});
// Reset owns deletion before initialization; do not migrate state it will discard.
return options && !options.commandOptions.includes("--reset") ? options : undefined;
}
async function activate() {
const program = command[0] && executable(command[0]);
if (!program) {
console.error("OpenClaw container activation command was not found");
process.exitCode = 127;
return;
}
const invocation = gatewayInvocation(program);
if (invocation) {
let interrupted;
let child;
const listeners = new Map();
for (const signal of ["SIGTERM", "SIGINT", "SIGHUP", "SIGQUIT"]) {
const listener = () => {
interrupted ??= signal;
// Doctor's maintenance barriers own INT/TERM; HUP/QUIT must use that drain too.
child?.kill(signal === "SIGHUP" || signal === "SIGQUIT" ? "SIGTERM" : signal);
};
process.on(signal, listener);
listeners.set(signal, listener);
}
try {
child = spawn(
process.execPath,
[
path.join(root, "openclaw.mjs"),
...invocation.rootOptions,
"doctor",
"--fix",
"--non-interactive",
],
{ stdio: "inherit", env: process.env },
);
/** @type {Error | undefined} */
let failure;
child.on("error", (error) => {
failure = error;
});
const outcome = await new Promise((resolve) => {
child.once("close", (code, signal) => resolve({ code, signal }));
});
if (failure) {
throw failure;
}
const signal = interrupted ?? outcome.signal;
if (signal || outcome.code !== 0) {
process.exitCode = signal ? 128 + os.constants.signals[signal] : (outcome.code ?? 1);
return;
}
} finally {
for (const [signal, listener] of listeners) {
process.off(signal, listener);
}
}
}
// Replace the adapter so tini continues to supervise the original command directly.
process.execve(program, command, process.env);
}
try {
await activate();
} catch (error) {
console.error(
`OpenClaw container activation failed: ${error instanceof Error ? error.message : String(error)}`,
);
process.exitCode = 1;
}