diff --git a/apps/macos/Sources/Clawdis/GatewayEnvironment.swift b/apps/macos/Sources/Clawdis/GatewayEnvironment.swift index 60343ea31..d552c3cdb 100644 --- a/apps/macos/Sources/Clawdis/GatewayEnvironment.swift +++ b/apps/macos/Sources/Clawdis/GatewayEnvironment.swift @@ -66,7 +66,7 @@ enum GatewayEnvironment { static func bundledGatewayExecutable() -> String? { 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 } @@ -198,7 +198,7 @@ enum GatewayEnvironment { let port = self.gatewayPort() 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) } if let gatewayBin { diff --git a/apps/macos/Sources/Clawdis/GatewayLaunchAgentManager.swift b/apps/macos/Sources/Clawdis/GatewayLaunchAgentManager.swift index 634d25806..050ea42da 100644 --- a/apps/macos/Sources/Clawdis/GatewayLaunchAgentManager.swift +++ b/apps/macos/Sources/Clawdis/GatewayLaunchAgentManager.swift @@ -7,7 +7,7 @@ enum GatewayLaunchAgentManager { } 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 { @@ -62,6 +62,7 @@ enum GatewayLaunchAgentManager { ProgramArguments \(gatewayBin) + gateway-daemon --port \(port) --bind diff --git a/scripts/codesign-mac-app.sh b/scripts/codesign-mac-app.sh index 98800055a..1d82d2798 100755 --- a/scripts/codesign-mac-app.sh +++ b/scripts/codesign-mac-app.sh @@ -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 echo "Signing gateway payload: $f"; sign_item "$f" "$ENT_TMP_BASE" 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 - 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 diff --git a/scripts/package-mac-app.sh b/scripts/package-mac-app.sh index da4f75777..6088d6775 100755 --- a/scripts/package-mac-app.sh +++ b/scripts/package-mac-app.sh @@ -131,28 +131,16 @@ if [[ "${SKIP_GATEWAY_PACKAGE:-0}" != "1" ]]; then exit 1 fi - echo "🧰 Building bundled gateway (bun --compile)" + echo "🧰 Building bundled relay (bun --compile)" mkdir -p "$RELAY_DIR" - BUN_OUT="$RELAY_DIR/clawdis-gateway" - bun build "$ROOT_DIR/dist/macos/gateway-daemon.js" \ + RELAY_OUT="$RELAY_DIR/clawdis" + bun build "$ROOT_DIR/dist/macos/relay.js" \ --compile \ --bytecode \ - --outfile "$BUN_OUT" \ + --outfile "$RELAY_OUT" \ -e electron \ --define "__CLAWDIS_VERSION__=\\\"$PKG_VERSION\\\"" - chmod +x "$BUN_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" + chmod +x "$RELAY_OUT" echo "🎨 Copying gateway A2UI host assets" rm -rf "$RELAY_DIR/a2ui" diff --git a/src/infra/path-env.test.ts b/src/infra/path-env.test.ts index b3dbf969f..686e3d641 100644 --- a/src/infra/path-env.test.ts +++ b/src/infra/path-env.test.ts @@ -12,11 +12,8 @@ describe("ensureClawdisCliOnPath", () => { try { const relayDir = path.join(tmp, "Relay"); await fs.mkdir(relayDir, { recursive: true }); - const gatewayPath = path.join(relayDir, "clawdis-gateway"); 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.chmod(gatewayPath, 0o755); await fs.chmod(cliPath, 0o755); const originalPath = process.env.PATH; @@ -25,7 +22,7 @@ describe("ensureClawdisCliOnPath", () => { delete process.env.CLAWDIS_PATH_BOOTSTRAPPED; try { ensureClawdisCliOnPath({ - execPath: gatewayPath, + execPath: cliPath, cwd: tmp, homeDir: tmp, platform: "darwin", diff --git a/src/infra/path-env.ts b/src/infra/path-env.ts index f8703eba0..484b0ce9d 100644 --- a/src/infra/path-env.ts +++ b/src/infra/path-env.ts @@ -55,7 +55,7 @@ function candidateBinDirs(opts: EnsureClawdisPathOpts): 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 { const execDir = path.dirname(execPath); const siblingClawdis = path.join(execDir, "clawdis"); diff --git a/src/macos/relay.ts b/src/macos/relay.ts new file mode 100644 index 000000000..54416f8f1 --- /dev/null +++ b/src/macos/relay.ts @@ -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 { + // 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(); +