From 7623117ad67ee1405bd9211f183fa01502911f59 Mon Sep 17 00:00:00 2001 From: siddseethepalli Date: Thu, 16 Apr 2026 07:02:09 +0000 Subject: [PATCH] build(macos): bundle credential-executor binary into Velissa.app Without this, CES is non-functional in the packaged macOS app: the daemon searches for credential-executor at ~/.vellum/workspace/bin/ (not installed) and falls back to source at /credential-executor/src/main.ts, which doesn't resolve because import.meta.dir in a Bun-compiled binary is virtualized. Every credential read then fails over to the encrypted file store and provider-credential lookups (e.g. Deepgram for STT) return undefined. - build.sh: compile credential-executor alongside vellum-daemon / vellum-cli / vellum-gateway and copy to Contents/MacOS/, with matching clean, staleness, signing, and notarize-exclusion entries. - release.yml: add CES to the inline x64 prebuild + cleanup paths. - executable-discovery.ts: search dirname(process.execPath) first so the packaged app locates its own CES; dev runs via `bun run` fall through since process.execPath points at the bun binary. --- .github/workflows/release.yml | 16 ++++-- .../executable-discovery.ts | 14 ++++- clients/macos/build.sh | 56 ++++++++++++++++++- 3 files changed, 78 insertions(+), 8 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9f9875078a3..a082f69117d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1542,8 +1542,8 @@ jobs: - name: Free disk space run: | - rm -rf .build daemon-bin assistant-bin cli-bin gateway-bin - rm -rf ../../assistant/node_modules ../../cli/node_modules ../../gateway/node_modules + rm -rf .build daemon-bin assistant-bin cli-bin gateway-bin ces-bin + rm -rf ../../assistant/node_modules ../../cli/node_modules ../../gateway/node_modules ../../credential-executor/node_modules rm -rf ~/Library/Developer/Xcode/DerivedData 2>/dev/null || true rm -rf ~/Library/Caches/org.swift.swiftpm 2>/dev/null || true df -h . @@ -1877,6 +1877,7 @@ jobs: ASSISTANT_SRC_DIR="../../assistant" CLI_SRC_DIR="../../cli" GATEWAY_SRC_DIR="../../gateway" + CES_SRC_DIR="../../credential-executor" DAEMON_EXTERNAL_FLAGS=(--external electron --external "chromium-bidi/*") DAEMON_FLAGS=("${DAEMON_EXTERNAL_FLAGS[@]}") @@ -1925,6 +1926,13 @@ jobs: "$GATEWAY_SRC_DIR/src/index.ts" --outfile gateway-bin/vellum-gateway chmod +x gateway-bin/vellum-gateway + # Credential-executor (CES) + (cd "$CES_SRC_DIR" && bun install --frozen-lockfile 2>/dev/null || bun install) + mkdir -p ces-bin + bun build --compile --target=bun-darwin-x64 \ + "$CES_SRC_DIR/src/main.ts" --outfile ces-bin/credential-executor + chmod +x ces-bin/credential-executor + - name: Build release .app run: | IS_STAGING="${{ needs.extract-version.outputs.is_staging }}" @@ -1968,8 +1976,8 @@ jobs: - name: Free disk space run: | - rm -rf .build daemon-bin assistant-bin cli-bin gateway-bin - rm -rf ../../assistant/node_modules ../../cli/node_modules ../../gateway/node_modules + rm -rf .build daemon-bin assistant-bin cli-bin gateway-bin ces-bin + rm -rf ../../assistant/node_modules ../../cli/node_modules ../../gateway/node_modules ../../credential-executor/node_modules rm -rf ~/Library/Developer/Xcode/DerivedData 2>/dev/null || true df -h . diff --git a/assistant/src/credential-execution/executable-discovery.ts b/assistant/src/credential-execution/executable-discovery.ts index e6da44e7272..57124ad83fd 100644 --- a/assistant/src/credential-execution/executable-discovery.ts +++ b/assistant/src/credential-execution/executable-discovery.ts @@ -17,7 +17,7 @@ */ import { existsSync } from "node:fs"; -import { join } from "node:path"; +import { dirname, join } from "node:path"; import { getIsContainerized } from "../config/env-registry.js"; import { getLogger } from "../util/logger.js"; @@ -65,9 +65,19 @@ function getManagedBootstrapSocketPath(): string { * `getDataDir()` (under `~/.vellum/workspace/data`) was previously included * but is inside the sandbox write boundary, so a sandboxed tool could plant * a malicious binary there. Removed to close the sandbox-escape vector. + * + * Search order: + * 1. Alongside the running executable (packaged macOS app: + * `.app/Contents/MacOS/credential-executor`). When running from + * source via `bun run`, `process.execPath` points at the bun binary + * itself, so this path won't exist and the search falls through. + * 2. `/credential-executor` — user-installed override (dev flow). */ function getLocalBinarySearchPaths(): string[] { - return [join(getBinDir(), "credential-executor")]; + return [ + join(dirname(process.execPath), "credential-executor"), + join(getBinDir(), "credential-executor"), + ]; } // --------------------------------------------------------------------------- diff --git a/clients/macos/build.sh b/clients/macos/build.sh index 0c084b8421d..3fc08d7e25c 100755 --- a/clients/macos/build.sh +++ b/clients/macos/build.sh @@ -245,6 +245,7 @@ ASSISTANT_SRC_DIR="$SCRIPT_DIR/../../assistant" CLI_SRC_DIR="$SCRIPT_DIR/../../cli" GATEWAY_SRC_DIR="$SCRIPT_DIR/../../gateway" NATIVE_HOST_SRC_DIR="$SCRIPT_DIR/../chrome-extension/native-host" +CES_SRC_DIR="$SCRIPT_DIR/../../credential-executor" # Chrome extension allowlist IDs injected into compiled binaries as a fallback # for packaged runs where repo-relative `meta/browser-extension/...` paths are @@ -355,6 +356,7 @@ build_binaries() { (cd "$ASSISTANT_SRC_DIR" && bun install --frozen-lockfile 2>/dev/null || bun install) (cd "$CLI_SRC_DIR" && bun install --frozen-lockfile 2>/dev/null || bun install) (cd "$GATEWAY_SRC_DIR" && bun install --frozen-lockfile 2>/dev/null || bun install) + (cd "$CES_SRC_DIR" && bun install --frozen-lockfile 2>/dev/null || bun install) if [ -d "$NATIVE_HOST_SRC_DIR/src" ]; then (cd "$NATIVE_HOST_SRC_DIR" && bun install --frozen-lockfile 2>/dev/null || bun install) fi @@ -409,6 +411,10 @@ build_binaries() { "$SCRIPT_DIR/gateway-bin" "vellum-gateway" & pids+=($!) + SKIP_BUN_INSTALL=1 build_bun_binary "$CES_SRC_DIR" "$CES_SRC_DIR/src/main.ts" \ + "$SCRIPT_DIR/ces-bin" "credential-executor" & + pids+=($!) + if [ -d "$NATIVE_HOST_SRC_DIR/src" ]; then SKIP_BUN_INSTALL=1 build_bun_binary "$NATIVE_HOST_SRC_DIR" "$NATIVE_HOST_SRC_DIR/src/index.ts" \ "$SCRIPT_DIR/native-host-bin" "vellum-chrome-native-host" "${native_host_flags[@]}" & @@ -552,7 +558,7 @@ case "$CMD" in clean) echo "Cleaning..." rm -rf "$SCRIPT_DIR/dist" "$SCRIPT_DIR/../.build" - rm -rf "$SCRIPT_DIR/daemon-bin" "$SCRIPT_DIR/assistant-bin" "$SCRIPT_DIR/cli-bin" "$SCRIPT_DIR/gateway-bin" "$SCRIPT_DIR/native-host-bin" + rm -rf "$SCRIPT_DIR/daemon-bin" "$SCRIPT_DIR/assistant-bin" "$SCRIPT_DIR/cli-bin" "$SCRIPT_DIR/gateway-bin" "$SCRIPT_DIR/native-host-bin" "$SCRIPT_DIR/ces-bin" rm -rf "$SPM_MODULE_CACHE" echo "Done." exit 0 @@ -597,7 +603,7 @@ if [ "$CMD" = "release" ] || [ "$CMD" = "release-application" ]; then # (e.g. arm64 binaries from a previous build being bundled into an x86_64 release). # Skip when SKIP_BUN_REBUILD=1, since pre-built binaries are intentionally provided. if [ "${SKIP_BUN_REBUILD:-}" != "1" ]; then - rm -rf "$SCRIPT_DIR/daemon-bin" "$SCRIPT_DIR/assistant-bin" "$SCRIPT_DIR/cli-bin" "$SCRIPT_DIR/gateway-bin" "$SCRIPT_DIR/native-host-bin" + rm -rf "$SCRIPT_DIR/daemon-bin" "$SCRIPT_DIR/assistant-bin" "$SCRIPT_DIR/cli-bin" "$SCRIPT_DIR/gateway-bin" "$SCRIPT_DIR/native-host-bin" "$SCRIPT_DIR/ces-bin" fi fi fi @@ -803,6 +809,32 @@ if [ -f "$SCRIPT_DIR/gateway-bin/vellum-gateway" ]; then fi fi +# Auto-build credential-executor (CES) binary if missing or stale. +# The compiled binary is bundled alongside the daemon in Contents/MacOS/ so +# the packaged app can locate it without requiring a separate install. +CES_BIN_NEEDS_BUILD=false +if [ "${SKIP_BUN_REBUILD:-}" != "1" ] && [ -d "$CES_SRC_DIR/src" ] && command -v bun &>/dev/null; then + if [ ! -f "$SCRIPT_DIR/ces-bin/credential-executor" ]; then + CES_BIN_NEEDS_BUILD=true + elif [ -n "$(find "$CES_SRC_DIR/src" -name '*.ts' -newer "$SCRIPT_DIR/ces-bin/credential-executor" -print -quit 2>/dev/null)" ]; then + CES_BIN_NEEDS_BUILD=true + elif [ "$CES_SRC_DIR/package.json" -nt "$SCRIPT_DIR/ces-bin/credential-executor" ] || \ + [ "$CES_SRC_DIR/bun.lock" -nt "$SCRIPT_DIR/ces-bin/credential-executor" ]; then + CES_BIN_NEEDS_BUILD=true + fi +fi +if [ "$CES_BIN_NEEDS_BUILD" = true ]; then + build_bun_binary "$CES_SRC_DIR" "$CES_SRC_DIR/src/main.ts" \ + "$SCRIPT_DIR/ces-bin" "credential-executor" +fi + +# Also rebuild if CES binary changed or newly added +if [ -f "$SCRIPT_DIR/ces-bin/credential-executor" ]; then + if [ ! -f "$MACOS_DIR/credential-executor" ] || [ "$SCRIPT_DIR/ces-bin/credential-executor" -nt "$MACOS_DIR/credential-executor" ]; then + NEEDS_REBUILD=true + fi +fi + # Auto-build Chrome native messaging helper binary if missing or stale # and bun is available. This is the binary Chrome spawns via # chrome.runtime.connectNative("com.vellum.daemon") — see @@ -892,6 +924,18 @@ if [ "$NEEDS_REBUILD" = true ]; then echo "No gateway binary at $GATEWAY_BIN — skipping (dev mode)" fi + # Copy bundled credential-executor (CES) binary (if available). + # The daemon locates this via `dirname(process.execPath)` — see + # getLocalBinarySearchPaths() in assistant/src/credential-execution/executable-discovery.ts. + CES_BIN="$SCRIPT_DIR/ces-bin/credential-executor" + if [ -f "$CES_BIN" ]; then + echo "Bundling credential-executor binary..." + cp "$CES_BIN" "$MACOS_DIR/credential-executor" + chmod +x "$MACOS_DIR/credential-executor" + else + echo "No credential-executor binary at $CES_BIN — skipping (dev mode)" + fi + # Copy bundled Chrome native messaging helper binary (if available). # This is an auxiliary executable under Contents/MacOS/ that Chrome # spawns via the com.vellum.daemon.json manifest written by @@ -1388,6 +1432,13 @@ if [ -f "$MACOS_DIR/vellum-chrome-native-host" ]; then echo "Chrome native messaging helper binary signed" fi +# Sign credential-executor (CES) binary +if [ -f "$MACOS_DIR/credential-executor" ]; then + CES_SIGN_FLAGS=(--force --sign "$SIGN_IDENTITY" "${CODESIGN_TS_FLAGS[@]}") + codesign "${CES_SIGN_FLAGS[@]}" "$MACOS_DIR/credential-executor" + echo "credential-executor binary signed" +fi + # Embedding runtime node_modules are no longer bundled (downloaded post-hatch). # Sign any additional regular files directly under Contents/MacOS. @@ -1400,6 +1451,7 @@ if [ -d "$MACOS_DIR" ]; then ! -name "vellum-cli" \ ! -name "vellum-gateway" \ ! -name "vellum-chrome-native-host" \ + ! -name "credential-executor" \ -exec codesign "${EXTRA_FILE_SIGN_FLAGS[@]}" {} \; fi