mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-09 22:24:31 +00:00
chore: remove dead macos relay and daemon code
This commit is contained in:
@@ -1,279 +0,0 @@
|
|||||||
#!/usr/bin/env node
|
|
||||||
import process from "node:process";
|
|
||||||
import type { GatewayLockHandle } from "../infra/gateway-lock.js";
|
|
||||||
import { restartGatewayProcessWithFreshPid } from "../infra/process-respawn.js";
|
|
||||||
|
|
||||||
declare const __OPENCLAW_VERSION__: string | undefined;
|
|
||||||
|
|
||||||
const BUNDLED_VERSION =
|
|
||||||
(typeof __OPENCLAW_VERSION__ === "string" && __OPENCLAW_VERSION__) ||
|
|
||||||
process.env.OPENCLAW_BUNDLED_VERSION ||
|
|
||||||
"0.0.0";
|
|
||||||
|
|
||||||
function argValue(args: string[], flag: string): string | undefined {
|
|
||||||
const idx = args.indexOf(flag);
|
|
||||||
if (idx < 0) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
const value = args[idx + 1];
|
|
||||||
return value && !value.startsWith("-") ? value : undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
function hasFlag(args: string[], flag: string): boolean {
|
|
||||||
return args.includes(flag);
|
|
||||||
}
|
|
||||||
|
|
||||||
const args = process.argv.slice(2);
|
|
||||||
|
|
||||||
type GatewayWsLogStyle = "auto" | "full" | "compact";
|
|
||||||
|
|
||||||
async function main() {
|
|
||||||
if (hasFlag(args, "--version") || hasFlag(args, "-v")) {
|
|
||||||
// Match `openclaw --version` behavior for Swift env/version checks.
|
|
||||||
// Keep output a single line.
|
|
||||||
console.log(BUNDLED_VERSION);
|
|
||||||
process.exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bun runtime ships a global `Long` that protobufjs detects, but it does not
|
|
||||||
// implement the long.js API that Baileys/WAProto expects (fromBits, ...).
|
|
||||||
// Ensure we use long.js so the embedded gateway doesn't crash at startup.
|
|
||||||
if (typeof process.versions.bun === "string") {
|
|
||||||
const mod = await import("long");
|
|
||||||
const Long = (mod as unknown as { default?: unknown }).default ?? mod;
|
|
||||||
(globalThis as unknown as { Long?: unknown }).Long = Long;
|
|
||||||
}
|
|
||||||
|
|
||||||
const [
|
|
||||||
{ loadConfig },
|
|
||||||
{ startGatewayServer },
|
|
||||||
{ setGatewayWsLogStyle },
|
|
||||||
{ setVerbose },
|
|
||||||
{ acquireGatewayLock, GatewayLockError },
|
|
||||||
{
|
|
||||||
consumeGatewaySigusr1RestartAuthorization,
|
|
||||||
isGatewaySigusr1RestartExternallyAllowed,
|
|
||||||
markGatewaySigusr1RestartHandled,
|
|
||||||
},
|
|
||||||
{ defaultRuntime },
|
|
||||||
{ enableConsoleCapture, setConsoleTimestampPrefix },
|
|
||||||
commandQueueMod,
|
|
||||||
{ createRestartIterationHook },
|
|
||||||
] = await Promise.all([
|
|
||||||
import("../config/config.js"),
|
|
||||||
import("../gateway/server.js"),
|
|
||||||
import("../gateway/ws-logging.js"),
|
|
||||||
import("../globals.js"),
|
|
||||||
import("../infra/gateway-lock.js"),
|
|
||||||
import("../infra/restart.js"),
|
|
||||||
import("../runtime.js"),
|
|
||||||
import("../logging.js"),
|
|
||||||
import("../process/command-queue.js"),
|
|
||||||
import("../process/restart-recovery.js"),
|
|
||||||
] as const);
|
|
||||||
|
|
||||||
enableConsoleCapture();
|
|
||||||
setConsoleTimestampPrefix(true);
|
|
||||||
setVerbose(hasFlag(args, "--verbose"));
|
|
||||||
|
|
||||||
const wsLogRaw = hasFlag(args, "--compact") ? "compact" : argValue(args, "--ws-log");
|
|
||||||
const wsLogStyle: GatewayWsLogStyle =
|
|
||||||
wsLogRaw === "compact" ? "compact" : wsLogRaw === "full" ? "full" : "auto";
|
|
||||||
setGatewayWsLogStyle(wsLogStyle);
|
|
||||||
|
|
||||||
const cfg = loadConfig();
|
|
||||||
const portRaw =
|
|
||||||
argValue(args, "--port") ??
|
|
||||||
process.env.OPENCLAW_GATEWAY_PORT ??
|
|
||||||
process.env.CLAWDBOT_GATEWAY_PORT ??
|
|
||||||
(typeof cfg.gateway?.port === "number" ? String(cfg.gateway.port) : "") ??
|
|
||||||
"18789";
|
|
||||||
const port = Number.parseInt(portRaw, 10);
|
|
||||||
if (Number.isNaN(port) || port <= 0) {
|
|
||||||
defaultRuntime.error(`Invalid --port (${portRaw})`);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
const bindRaw =
|
|
||||||
argValue(args, "--bind") ??
|
|
||||||
process.env.OPENCLAW_GATEWAY_BIND ??
|
|
||||||
process.env.CLAWDBOT_GATEWAY_BIND ??
|
|
||||||
cfg.gateway?.bind ??
|
|
||||||
"loopback";
|
|
||||||
const bind =
|
|
||||||
bindRaw === "loopback" ||
|
|
||||||
bindRaw === "lan" ||
|
|
||||||
bindRaw === "auto" ||
|
|
||||||
bindRaw === "custom" ||
|
|
||||||
bindRaw === "tailnet"
|
|
||||||
? bindRaw
|
|
||||||
: null;
|
|
||||||
if (!bind) {
|
|
||||||
defaultRuntime.error('Invalid --bind (use "loopback", "lan", "tailnet", "auto", or "custom")');
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
const token = argValue(args, "--token");
|
|
||||||
if (token) {
|
|
||||||
process.env.OPENCLAW_GATEWAY_TOKEN = token;
|
|
||||||
}
|
|
||||||
|
|
||||||
let server: Awaited<ReturnType<typeof startGatewayServer>> | null = null;
|
|
||||||
let lock: GatewayLockHandle | null = null;
|
|
||||||
let shuttingDown = false;
|
|
||||||
let forceExitTimer: ReturnType<typeof setTimeout> | null = null;
|
|
||||||
let restartResolver: (() => void) | null = null;
|
|
||||||
|
|
||||||
const cleanupSignals = () => {
|
|
||||||
process.removeListener("SIGTERM", onSigterm);
|
|
||||||
process.removeListener("SIGINT", onSigint);
|
|
||||||
process.removeListener("SIGUSR1", onSigusr1);
|
|
||||||
};
|
|
||||||
|
|
||||||
const request = (action: "stop" | "restart", signal: string) => {
|
|
||||||
if (shuttingDown) {
|
|
||||||
defaultRuntime.log(`gateway: received ${signal} during shutdown; ignoring`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
shuttingDown = true;
|
|
||||||
const isRestart = action === "restart";
|
|
||||||
defaultRuntime.log(
|
|
||||||
`gateway: received ${signal}; ${isRestart ? "restarting" : "shutting down"}`,
|
|
||||||
);
|
|
||||||
|
|
||||||
const DRAIN_TIMEOUT_MS = 30_000;
|
|
||||||
const SHUTDOWN_TIMEOUT_MS = 5_000;
|
|
||||||
const forceExitMs = isRestart ? DRAIN_TIMEOUT_MS + SHUTDOWN_TIMEOUT_MS : SHUTDOWN_TIMEOUT_MS;
|
|
||||||
forceExitTimer = setTimeout(() => {
|
|
||||||
defaultRuntime.error("gateway: shutdown timed out; exiting without full cleanup");
|
|
||||||
cleanupSignals();
|
|
||||||
process.exit(0);
|
|
||||||
}, forceExitMs);
|
|
||||||
|
|
||||||
void (async () => {
|
|
||||||
try {
|
|
||||||
if (isRestart) {
|
|
||||||
const activeTasks = commandQueueMod.getActiveTaskCount();
|
|
||||||
if (activeTasks > 0) {
|
|
||||||
defaultRuntime.log(
|
|
||||||
`gateway: draining ${activeTasks} active task(s) before restart (timeout ${DRAIN_TIMEOUT_MS}ms)`,
|
|
||||||
);
|
|
||||||
const { drained } = await commandQueueMod.waitForActiveTasks(DRAIN_TIMEOUT_MS);
|
|
||||||
if (drained) {
|
|
||||||
defaultRuntime.log("gateway: all active tasks drained");
|
|
||||||
} else {
|
|
||||||
defaultRuntime.log("gateway: drain timeout reached; proceeding with restart");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await server?.close({
|
|
||||||
reason: isRestart ? "gateway restarting" : "gateway stopping",
|
|
||||||
restartExpectedMs: isRestart ? 1500 : null,
|
|
||||||
});
|
|
||||||
} catch (err) {
|
|
||||||
defaultRuntime.error(`gateway: shutdown error: ${String(err)}`);
|
|
||||||
} finally {
|
|
||||||
if (forceExitTimer) {
|
|
||||||
clearTimeout(forceExitTimer);
|
|
||||||
}
|
|
||||||
server = null;
|
|
||||||
if (isRestart) {
|
|
||||||
const respawn = restartGatewayProcessWithFreshPid();
|
|
||||||
if (respawn.mode === "spawned" || respawn.mode === "supervised") {
|
|
||||||
const modeLabel =
|
|
||||||
respawn.mode === "spawned"
|
|
||||||
? `spawned pid ${respawn.pid ?? "unknown"}`
|
|
||||||
: "supervisor restart";
|
|
||||||
defaultRuntime.log(`gateway: restart mode full process restart (${modeLabel})`);
|
|
||||||
cleanupSignals();
|
|
||||||
process.exit(0);
|
|
||||||
} else {
|
|
||||||
if (respawn.mode === "failed") {
|
|
||||||
defaultRuntime.log(
|
|
||||||
`gateway: full process restart failed (${respawn.detail ?? "unknown error"}); falling back to in-process restart`,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
defaultRuntime.log("gateway: restart mode in-process restart (OPENCLAW_NO_RESPAWN)");
|
|
||||||
}
|
|
||||||
shuttingDown = false;
|
|
||||||
restartResolver?.();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
cleanupSignals();
|
|
||||||
process.exit(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
};
|
|
||||||
|
|
||||||
const onSigterm = () => {
|
|
||||||
defaultRuntime.log("gateway: signal SIGTERM received");
|
|
||||||
request("stop", "SIGTERM");
|
|
||||||
};
|
|
||||||
const onSigint = () => {
|
|
||||||
defaultRuntime.log("gateway: signal SIGINT received");
|
|
||||||
request("stop", "SIGINT");
|
|
||||||
};
|
|
||||||
const onSigusr1 = () => {
|
|
||||||
defaultRuntime.log("gateway: signal SIGUSR1 received");
|
|
||||||
const authorized = consumeGatewaySigusr1RestartAuthorization();
|
|
||||||
if (!authorized && !isGatewaySigusr1RestartExternallyAllowed()) {
|
|
||||||
defaultRuntime.log(
|
|
||||||
"gateway: SIGUSR1 restart ignored (not authorized; commands.restart=false or use gateway tool).",
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
markGatewaySigusr1RestartHandled();
|
|
||||||
request("restart", "SIGUSR1");
|
|
||||||
};
|
|
||||||
|
|
||||||
process.on("SIGTERM", onSigterm);
|
|
||||||
process.on("SIGINT", onSigint);
|
|
||||||
process.on("SIGUSR1", onSigusr1);
|
|
||||||
|
|
||||||
try {
|
|
||||||
try {
|
|
||||||
lock = await acquireGatewayLock();
|
|
||||||
} catch (err) {
|
|
||||||
if (err instanceof GatewayLockError) {
|
|
||||||
defaultRuntime.error(`Gateway start blocked: ${err.message}`);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
const onIteration = createRestartIterationHook(() => {
|
|
||||||
// After an in-process restart (SIGUSR1), reset command-queue lane state.
|
|
||||||
// Interrupted tasks from the previous lifecycle may have left `active`
|
|
||||||
// counts elevated (their finally blocks never ran), permanently blocking
|
|
||||||
// new work from draining.
|
|
||||||
commandQueueMod.resetAllLanes();
|
|
||||||
});
|
|
||||||
|
|
||||||
// eslint-disable-next-line no-constant-condition
|
|
||||||
while (true) {
|
|
||||||
onIteration();
|
|
||||||
try {
|
|
||||||
server = await startGatewayServer(port, { bind });
|
|
||||||
} catch (err) {
|
|
||||||
cleanupSignals();
|
|
||||||
defaultRuntime.error(`Gateway failed to start: ${String(err)}`);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
await new Promise<void>((resolve) => {
|
|
||||||
restartResolver = resolve;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
await lock?.release();
|
|
||||||
cleanupSignals();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void main().catch((err) => {
|
|
||||||
console.error(
|
|
||||||
"[openclaw] Gateway daemon failed:",
|
|
||||||
err instanceof Error ? (err.stack ?? err.message) : err,
|
|
||||||
);
|
|
||||||
process.exit(1);
|
|
||||||
});
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
import { describe, expect, it, vi } from "vitest";
|
|
||||||
import { parseRelaySmokeTest, runRelaySmokeTest } from "./relay-smoke.js";
|
|
||||||
|
|
||||||
vi.mock("../web/qr-image.js", () => ({
|
|
||||||
renderQrPngBase64: vi.fn(async () => "base64"),
|
|
||||||
}));
|
|
||||||
|
|
||||||
describe("parseRelaySmokeTest", () => {
|
|
||||||
it("parses --smoke qr", () => {
|
|
||||||
expect(parseRelaySmokeTest(["--smoke", "qr"], {})).toBe("qr");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("rejects --smoke without a value", () => {
|
|
||||||
expect(() => parseRelaySmokeTest(["--smoke"], {})).toThrow(
|
|
||||||
"Missing value for --smoke (expected: qr)",
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("rejects --smoke when the next arg is another flag", () => {
|
|
||||||
expect(() => parseRelaySmokeTest(["--smoke", "--smoke-qr"], {})).toThrow(
|
|
||||||
"Missing value for --smoke (expected: qr)",
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("parses --smoke-qr", () => {
|
|
||||||
expect(parseRelaySmokeTest(["--smoke-qr"], {})).toBe("qr");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("parses env var smoke mode only when no args", () => {
|
|
||||||
expect(parseRelaySmokeTest([], { OPENCLAW_SMOKE_QR: "1" })).toBe("qr");
|
|
||||||
expect(parseRelaySmokeTest(["send"], { OPENCLAW_SMOKE_QR: "1" })).toBe(null);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("supports OPENCLAW_SMOKE=qr only when no args", () => {
|
|
||||||
expect(parseRelaySmokeTest([], { OPENCLAW_SMOKE: "qr" })).toBe("qr");
|
|
||||||
expect(parseRelaySmokeTest(["send"], { OPENCLAW_SMOKE: "qr" })).toBe(null);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("rejects unknown smoke values", () => {
|
|
||||||
expect(() => parseRelaySmokeTest(["--smoke", "nope"], {})).toThrow("Unknown smoke test");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("prefers explicit --smoke over env vars", () => {
|
|
||||||
expect(parseRelaySmokeTest(["--smoke", "qr"], { OPENCLAW_SMOKE: "nope" })).toBe("qr");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("runRelaySmokeTest", () => {
|
|
||||||
it("runs qr smoke test", async () => {
|
|
||||||
await runRelaySmokeTest("qr");
|
|
||||||
const mod = await import("../web/qr-image.js");
|
|
||||||
expect(mod.renderQrPngBase64).toHaveBeenCalledWith("smoke-test");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
export type RelaySmokeTest = "qr";
|
|
||||||
|
|
||||||
export function parseRelaySmokeTest(args: string[], env: NodeJS.ProcessEnv): RelaySmokeTest | null {
|
|
||||||
const smokeIdx = args.indexOf("--smoke");
|
|
||||||
if (smokeIdx !== -1) {
|
|
||||||
const value = args[smokeIdx + 1];
|
|
||||||
if (!value || value.startsWith("-")) {
|
|
||||||
throw new Error("Missing value for --smoke (expected: qr)");
|
|
||||||
}
|
|
||||||
if (value === "qr") {
|
|
||||||
return "qr";
|
|
||||||
}
|
|
||||||
throw new Error(`Unknown smoke test: ${value}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (args.includes("--smoke-qr")) {
|
|
||||||
return "qr";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Back-compat: only run env-based smoke mode when no CLI args are present,
|
|
||||||
// to avoid surprising early-exit when users set env vars globally.
|
|
||||||
if (args.length === 0 && (env.OPENCLAW_SMOKE_QR === "1" || env.OPENCLAW_SMOKE === "qr")) {
|
|
||||||
return "qr";
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function runRelaySmokeTest(test: RelaySmokeTest): Promise<void> {
|
|
||||||
switch (test) {
|
|
||||||
case "qr": {
|
|
||||||
const { renderQrPngBase64 } = await import("../web/qr-image.js");
|
|
||||||
await renderQrPngBase64("smoke-test");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,82 +0,0 @@
|
|||||||
#!/usr/bin/env node
|
|
||||||
import process from "node:process";
|
|
||||||
|
|
||||||
declare const __OPENCLAW_VERSION__: string | undefined;
|
|
||||||
|
|
||||||
const BUNDLED_VERSION =
|
|
||||||
(typeof __OPENCLAW_VERSION__ === "string" && __OPENCLAW_VERSION__) ||
|
|
||||||
process.env.OPENCLAW_BUNDLED_VERSION ||
|
|
||||||
"0.0.0";
|
|
||||||
|
|
||||||
function hasFlag(args: string[], flag: string): boolean {
|
|
||||||
return args.includes(flag);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function patchBunLongForProtobuf(): Promise<void> {
|
|
||||||
// Bun ships a global `Long` that protobufjs detects, but it is not long.js and
|
|
||||||
// misses critical APIs (fromBits, ...). Baileys WAProto expects long.js.
|
|
||||||
if (typeof process.versions.bun !== "string") {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const mod = await import("long");
|
|
||||||
const Long = (mod as unknown as { default?: unknown }).default ?? mod;
|
|
||||||
(globalThis as unknown as { Long?: unknown }).Long = Long;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function main() {
|
|
||||||
const args = process.argv.slice(2);
|
|
||||||
|
|
||||||
// Swift side expects `--version` to return a plain semver string.
|
|
||||||
if (hasFlag(args, "--version") || hasFlag(args, "-V") || hasFlag(args, "-v")) {
|
|
||||||
console.log(BUNDLED_VERSION);
|
|
||||||
process.exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
const { parseRelaySmokeTest, runRelaySmokeTest } = await import("./relay-smoke.js");
|
|
||||||
const smokeTest = parseRelaySmokeTest(args, process.env);
|
|
||||||
if (smokeTest) {
|
|
||||||
try {
|
|
||||||
await runRelaySmokeTest(smokeTest);
|
|
||||||
process.exit(0);
|
|
||||||
} catch (err) {
|
|
||||||
console.error(`Relay smoke test failed (${smokeTest}):`, err);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await patchBunLongForProtobuf();
|
|
||||||
|
|
||||||
const { loadDotEnv } = await import("../infra/dotenv.js");
|
|
||||||
loadDotEnv({ quiet: true });
|
|
||||||
|
|
||||||
const { ensureOpenClawCliOnPath } = await import("../infra/path-env.js");
|
|
||||||
ensureOpenClawCliOnPath();
|
|
||||||
|
|
||||||
const { enableConsoleCapture } = await import("../logging.js");
|
|
||||||
enableConsoleCapture();
|
|
||||||
|
|
||||||
const { assertSupportedRuntime } = await import("../infra/runtime-guard.js");
|
|
||||||
assertSupportedRuntime();
|
|
||||||
const { formatUncaughtError } = await import("../infra/errors.js");
|
|
||||||
const { installUnhandledRejectionHandler } = await import("../infra/unhandled-rejections.js");
|
|
||||||
|
|
||||||
const { buildProgram } = await import("../cli/program.js");
|
|
||||||
const program = buildProgram();
|
|
||||||
|
|
||||||
installUnhandledRejectionHandler();
|
|
||||||
|
|
||||||
process.on("uncaughtException", (error) => {
|
|
||||||
console.error("[openclaw] Uncaught exception:", formatUncaughtError(error));
|
|
||||||
process.exit(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
await program.parseAsync(process.argv);
|
|
||||||
}
|
|
||||||
|
|
||||||
void main().catch((err) => {
|
|
||||||
console.error(
|
|
||||||
"[openclaw] Relay failed:",
|
|
||||||
err instanceof Error ? (err.stack ?? err.message) : err,
|
|
||||||
);
|
|
||||||
process.exit(1);
|
|
||||||
});
|
|
||||||
Reference in New Issue
Block a user