Files
orca/src/main/cli/appimage-cli-wrapper.ts
T
5124b0d0ad fix(serve): install orca CLI in headless serve so orca claude-teams resolves (takeover of #5766) (#7267)
* fix(serve): install the orca CLI in headless serve mode

In headless serve mode the orca CLI command (~/.local/bin/orca on Linux,
/usr/local/bin/orca on macOS) is never created, so the in-app Claude Team
launcher (orca claude-teams) opened by 'Start new agent' fails with
command-not-found. CliInstaller.install() is only invoked from renderer/desktop
flows (onboarding, Settings 'Install CLI', feature tips) via the cli:install IPC;
headless serve has no renderer. Run the existing idempotent installer on the
serve startup path, best-effort so a failure never blocks serve startup.

Fixes #5763.

* fix(serve): install a bare `orca` dispatcher on Linux for Claude Team

On Linux CliInstaller installs the CLI as `orca-ide` (to avoid shadowing GNOME
Orca's /usr/bin/orca), not bare `orca`. But the Claude Team launcher (`orca
claude-teams`), composed client-side by both the desktop renderer and the
mobile app and written verbatim to the host PTY, invokes bare `orca` — which
is unresolved in the initial managed terminal (no agent-teams shim on PATH
yet). The serve-mode CLI install therefore fixed macOS but not Linux.

Add a bare-`orca` dispatcher in the serve branch (host-side, Linux only) that
execs the bundled CLI wrapper. ~/.local/bin is hardcoded ahead of /usr/bin on
the managed-terminal PATH, so it resolves. Plain file (not a managed symlink)
so CliInstaller.removeLegacyLinuxCommandIfManaged leaves it untouched.

* Restore ai-vault files to origin/main (drop stale pre-0.52 oxfmt merge artifact)

Co-authored-by: Orca <help@stably.ai>

* Harden serve CLI install: AppImage-safe dispatcher, no-prompt macOS, skip Windows, conflict guard

- Gate CliInstaller.install() to macOS+Linux; Windows would only mutate the
  persistent user-registry PATH without helping the current serve's terminals.
- Inject a non-prompting privilegedRunner so headless macOS never pops an
  osascript admin dialog when /usr/local/bin isn't writable (skips instead).
- Linux dispatcher: exec the stable $APPIMAGE via buildAppImageCliWrapper when
  running from an AppImage (resourcesPath is an ephemeral FUSE mount); skip when
  the bundled orca-ide launcher is absent; never clobber a user-owned ~/.local/bin/orca.
- Single-quote the exec target; add coverage for all new branches.

Co-authored-by: Orca <help@stably.ai>

---------

Co-authored-by: Daniel Rudaev <daniel@d1dx.com>
Co-authored-by: Orca <help@stably.ai>
2026-07-03 14:12:33 -07:00

38 lines
1.4 KiB
TypeScript

const APPIMAGE_CLI_SCRIPT = [
'(async()=>{',
'try{',
'const path=require("path");',
'const appDir=process.env.APPDIR;',
'if(!appDir){console.error("Orca AppImage runtime did not set APPDIR.");process.exit(1);}',
'const cli=path.join(appDir,"resources","app.asar.unpacked","out","cli","index.js");',
'await Promise.resolve(require(cli).main(process.argv.slice(1)));',
'}catch(error){',
'console.error(error&&error.stack?error.stack:String(error));process.exit(1);',
'}',
'})();'
].join('')
export function buildAppImageCliWrapper(appImagePath: string): string {
// Why: AppImage mounts resources under a fresh FUSE path per launch, so the
// installed command must call the stable outer AppImage and resolve APPDIR.
return `#!/usr/bin/env bash
set -euo pipefail
APPIMAGE=${quoteShell(appImagePath)}
if [ ! -f "$APPIMAGE" ]; then
echo "Orca AppImage not found at $APPIMAGE" >&2
echo "If you moved the AppImage, re-run CLI registration from Orca Settings." >&2
exit 1
fi
export ORCA_NODE_OPTIONS="\${NODE_OPTIONS-}"
export ORCA_NODE_REPL_EXTERNAL_MODULE="\${NODE_REPL_EXTERNAL_MODULE-}"
unset NODE_OPTIONS
unset NODE_REPL_EXTERNAL_MODULE
# Why: AppImage mount paths change on each launch; $APPDIR is the runtime mount.
ELECTRON_RUN_AS_NODE=1 exec "$APPIMAGE" -e ${quoteShell(APPIMAGE_CLI_SCRIPT)} -- "$@"
`
}
export function quoteShell(value: string): string {
return `'${value.replaceAll("'", `'"'"'`)}'`
}