refactor(macos): bundle single relay binary
This commit is contained in:
@@ -66,7 +66,7 @@ enum GatewayEnvironment {
|
|||||||
|
|
||||||
static func bundledGatewayExecutable() -> String? {
|
static func bundledGatewayExecutable() -> String? {
|
||||||
guard let res = Bundle.main.resourceURL else { return nil }
|
guard let res = Bundle.main.resourceURL else { return nil }
|
||||||
let path = res.appendingPathComponent("Relay/clawdis-gateway").path
|
let path = res.appendingPathComponent("Relay/clawdis").path
|
||||||
return FileManager.default.isExecutableFile(atPath: path) ? path : nil
|
return FileManager.default.isExecutableFile(atPath: path) ? path : nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -198,7 +198,7 @@ enum GatewayEnvironment {
|
|||||||
|
|
||||||
let port = self.gatewayPort()
|
let port = self.gatewayPort()
|
||||||
if let bundled {
|
if let bundled {
|
||||||
let cmd = [bundled, "--port", "\(port)", "--bind", "loopback"]
|
let cmd = [bundled, "gateway-daemon", "--port", "\(port)", "--bind", "loopback"]
|
||||||
return GatewayCommandResolution(status: status, command: cmd)
|
return GatewayCommandResolution(status: status, command: cmd)
|
||||||
}
|
}
|
||||||
if let gatewayBin {
|
if let gatewayBin {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ enum GatewayLaunchAgentManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static func gatewayExecutablePath(bundlePath: String) -> String {
|
private static func gatewayExecutablePath(bundlePath: String) -> String {
|
||||||
"\(bundlePath)/Contents/Resources/Relay/clawdis-gateway"
|
"\(bundlePath)/Contents/Resources/Relay/clawdis"
|
||||||
}
|
}
|
||||||
|
|
||||||
private static func relayDir(bundlePath: String) -> String {
|
private static func relayDir(bundlePath: String) -> String {
|
||||||
@@ -62,6 +62,7 @@ enum GatewayLaunchAgentManager {
|
|||||||
<key>ProgramArguments</key>
|
<key>ProgramArguments</key>
|
||||||
<array>
|
<array>
|
||||||
<string>\(gatewayBin)</string>
|
<string>\(gatewayBin)</string>
|
||||||
|
<string>gateway-daemon</string>
|
||||||
<string>--port</string>
|
<string>--port</string>
|
||||||
<string>\(port)</string>
|
<string>\(port)</string>
|
||||||
<string>--bind</string>
|
<string>--bind</string>
|
||||||
|
|||||||
@@ -136,11 +136,8 @@ if [ -d "$APP_BUNDLE/Contents/Resources/Relay" ]; then
|
|||||||
find "$APP_BUNDLE/Contents/Resources/Relay" -type f \( -name "*.node" -o -name "*.dylib" \) -print0 | while IFS= read -r -d '' f; do
|
find "$APP_BUNDLE/Contents/Resources/Relay" -type f \( -name "*.node" -o -name "*.dylib" \) -print0 | while IFS= read -r -d '' f; do
|
||||||
echo "Signing gateway payload: $f"; sign_item "$f" "$ENT_TMP_BASE"
|
echo "Signing gateway payload: $f"; sign_item "$f" "$ENT_TMP_BASE"
|
||||||
done
|
done
|
||||||
if [ -f "$APP_BUNDLE/Contents/Resources/Relay/clawdis-gateway" ]; then
|
|
||||||
echo "Signing embedded gateway"; sign_item "$APP_BUNDLE/Contents/Resources/Relay/clawdis-gateway" "$ENT_TMP_BUN"
|
|
||||||
fi
|
|
||||||
if [ -f "$APP_BUNDLE/Contents/Resources/Relay/clawdis" ]; then
|
if [ -f "$APP_BUNDLE/Contents/Resources/Relay/clawdis" ]; then
|
||||||
echo "Signing embedded CLI"; sign_item "$APP_BUNDLE/Contents/Resources/Relay/clawdis" "$ENT_TMP_BUN"
|
echo "Signing embedded relay"; sign_item "$APP_BUNDLE/Contents/Resources/Relay/clawdis" "$ENT_TMP_BUN"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
@@ -131,28 +131,16 @@ if [[ "${SKIP_GATEWAY_PACKAGE:-0}" != "1" ]]; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "🧰 Building bundled gateway (bun --compile)"
|
echo "🧰 Building bundled relay (bun --compile)"
|
||||||
mkdir -p "$RELAY_DIR"
|
mkdir -p "$RELAY_DIR"
|
||||||
BUN_OUT="$RELAY_DIR/clawdis-gateway"
|
RELAY_OUT="$RELAY_DIR/clawdis"
|
||||||
bun build "$ROOT_DIR/dist/macos/gateway-daemon.js" \
|
bun build "$ROOT_DIR/dist/macos/relay.js" \
|
||||||
--compile \
|
--compile \
|
||||||
--bytecode \
|
--bytecode \
|
||||||
--outfile "$BUN_OUT" \
|
--outfile "$RELAY_OUT" \
|
||||||
-e electron \
|
-e electron \
|
||||||
--define "__CLAWDIS_VERSION__=\\\"$PKG_VERSION\\\""
|
--define "__CLAWDIS_VERSION__=\\\"$PKG_VERSION\\\""
|
||||||
chmod +x "$BUN_OUT"
|
chmod +x "$RELAY_OUT"
|
||||||
|
|
||||||
echo "🧰 Building bundled CLI (bun --compile)"
|
|
||||||
CLI_OUT="$RELAY_DIR/clawdis"
|
|
||||||
bun build "$ROOT_DIR/dist/index.js" \
|
|
||||||
--compile \
|
|
||||||
--bytecode \
|
|
||||||
--outfile "$CLI_OUT" \
|
|
||||||
-e playwright-core \
|
|
||||||
-e electron \
|
|
||||||
-e "chromium-bidi*" \
|
|
||||||
--define "__CLAWDIS_VERSION__=\\\"$PKG_VERSION\\\""
|
|
||||||
chmod +x "$CLI_OUT"
|
|
||||||
|
|
||||||
echo "🎨 Copying gateway A2UI host assets"
|
echo "🎨 Copying gateway A2UI host assets"
|
||||||
rm -rf "$RELAY_DIR/a2ui"
|
rm -rf "$RELAY_DIR/a2ui"
|
||||||
|
|||||||
@@ -12,11 +12,8 @@ describe("ensureClawdisCliOnPath", () => {
|
|||||||
try {
|
try {
|
||||||
const relayDir = path.join(tmp, "Relay");
|
const relayDir = path.join(tmp, "Relay");
|
||||||
await fs.mkdir(relayDir, { recursive: true });
|
await fs.mkdir(relayDir, { recursive: true });
|
||||||
const gatewayPath = path.join(relayDir, "clawdis-gateway");
|
|
||||||
const cliPath = path.join(relayDir, "clawdis");
|
const cliPath = path.join(relayDir, "clawdis");
|
||||||
await fs.writeFile(gatewayPath, "#!/bin/sh\nexit 0\n", "utf-8");
|
|
||||||
await fs.writeFile(cliPath, "#!/bin/sh\necho ok\n", "utf-8");
|
await fs.writeFile(cliPath, "#!/bin/sh\necho ok\n", "utf-8");
|
||||||
await fs.chmod(gatewayPath, 0o755);
|
|
||||||
await fs.chmod(cliPath, 0o755);
|
await fs.chmod(cliPath, 0o755);
|
||||||
|
|
||||||
const originalPath = process.env.PATH;
|
const originalPath = process.env.PATH;
|
||||||
@@ -25,7 +22,7 @@ describe("ensureClawdisCliOnPath", () => {
|
|||||||
delete process.env.CLAWDIS_PATH_BOOTSTRAPPED;
|
delete process.env.CLAWDIS_PATH_BOOTSTRAPPED;
|
||||||
try {
|
try {
|
||||||
ensureClawdisCliOnPath({
|
ensureClawdisCliOnPath({
|
||||||
execPath: gatewayPath,
|
execPath: cliPath,
|
||||||
cwd: tmp,
|
cwd: tmp,
|
||||||
homeDir: tmp,
|
homeDir: tmp,
|
||||||
platform: "darwin",
|
platform: "darwin",
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ function candidateBinDirs(opts: EnsureClawdisPathOpts): string[] {
|
|||||||
|
|
||||||
const candidates: string[] = [];
|
const candidates: string[] = [];
|
||||||
|
|
||||||
// Bun bundled (macOS app): `clawdis` is shipped next to `clawdis-gateway`.
|
// Bun bundled (macOS app): `clawdis` lives in the Relay dir (process.execPath).
|
||||||
try {
|
try {
|
||||||
const execDir = path.dirname(execPath);
|
const execDir = path.dirname(execPath);
|
||||||
const siblingClawdis = path.join(execDir, "clawdis");
|
const siblingClawdis = path.join(execDir, "clawdis");
|
||||||
|
|||||||
69
src/macos/relay.ts
Normal file
69
src/macos/relay.ts
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
import process from "node:process";
|
||||||
|
|
||||||
|
declare const __CLAWDIS_VERSION__: string | undefined;
|
||||||
|
|
||||||
|
const BUNDLED_VERSION =
|
||||||
|
typeof __CLAWDIS_VERSION__ === "string" ? __CLAWDIS_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);
|
||||||
|
}
|
||||||
|
|
||||||
|
await patchBunLongForProtobuf();
|
||||||
|
|
||||||
|
const { default: dotenv } = await import("dotenv");
|
||||||
|
dotenv.config({ quiet: true });
|
||||||
|
|
||||||
|
const { ensureClawdisCliOnPath } = await import("../infra/path-env.js");
|
||||||
|
ensureClawdisCliOnPath();
|
||||||
|
|
||||||
|
const { enableConsoleCapture } = await import("../logging.js");
|
||||||
|
enableConsoleCapture();
|
||||||
|
|
||||||
|
const { assertSupportedRuntime } = await import("../infra/runtime-guard.js");
|
||||||
|
assertSupportedRuntime();
|
||||||
|
|
||||||
|
const { buildProgram } = await import("../cli/program.js");
|
||||||
|
const program = buildProgram();
|
||||||
|
|
||||||
|
process.on("unhandledRejection", (reason, _promise) => {
|
||||||
|
console.error(
|
||||||
|
"[clawdis] Unhandled promise rejection:",
|
||||||
|
reason instanceof Error ? (reason.stack ?? reason.message) : reason,
|
||||||
|
);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
process.on("uncaughtException", (error) => {
|
||||||
|
console.error("[clawdis] Uncaught exception:", error.stack ?? error.message);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
await program.parseAsync(process.argv);
|
||||||
|
}
|
||||||
|
|
||||||
|
void main();
|
||||||
|
|
||||||
Reference in New Issue
Block a user