agent-desktop/npm/bin/agent-desktop.js
Lahfir 3f322728b4
feat!: implement Playwright-grade foundation contract
Settle the Playwright-grade reliability contract in agent-desktop-core
before the Windows/Linux adapters are built, so they inherit it instead
of redesigning it. Every command now observes, waits, verifies, and
reports honestly instead of firing blindly.

Highlights: capability-supertrait split of PlatformAdapter with
not_supported() defaults; canonical role/state vocabulary with live
`is --property visible`; display enumeration (`list-displays`) and honest
`--screen` with scale factor; truthful Automation permission; `native_id`
identity spine; window-id-first resolution; serializable `LocatorQuery`
with live `find`; default-on auto-wait before every ref action; three-way
`hit_test` occlusion gate; `scroll_into_view` in core; core accessible-name
precedence; typed `ActionStep` delivery tier; `ProcessState` and
`APP_UNRESPONSIVE`; `LaunchOptions`; baseline-diff desktop signals
(`wait --event`); typed clipboard (`Text`/`Image`/`FileUrls`); mouse
modifier chords and `mouse-wheel`. Hardened through a 35-reviewer pass with
independent validation and a green live e2e gate (109/0), plus a
head-vs-main performance comparison harness.

BREAKING CHANGE: default-on auto-wait changes the timing of every
previously-untouched ref-action call (bounded 5000 ms default; `--timeout-ms 0`
restores single-shot). `ENVELOPE_VERSION` is now `2.1` (adds the
`APP_UNRESPONSIVE` code and process state in error details). FFI ABI major
is `3` (append-only struct evolution; `wait --event` is intentionally not
exposed over FFI). The legacy string clipboard API is removed in favor of
typed content. `key-down`/`key-up` fail closed until daemon-owned held input
exists. `close-app` verifies termination and the osascript fallback path is
removed. `--text` matching is subtree containment: `find --text X --first`
returns the outermost matching container.
2026-07-20 00:21:38 -07:00

112 lines
3.3 KiB
JavaScript
Executable file

#!/usr/bin/env node
const { spawn } = require('child_process');
const { existsSync, accessSync, chmodSync, constants } = require('fs');
const { isAbsolute, join } = require('path');
const { platform, arch } = require('os');
const binDir = __dirname;
const MACOS_HELPER_NAME = 'agent-desktop-macos-helper';
const MACOS_HELPER_PATH_ENV = 'AGENT_DESKTOP_MACOS_HELPER_PATH';
function getBinaryName() {
const os = platform();
const cpuArch = arch();
const platformMap = {
'darwin-arm64': 'agent-desktop-darwin-arm64',
'darwin-x64': 'agent-desktop-darwin-x64',
'linux-x64': 'agent-desktop-linux-x64',
'linux-arm64': 'agent-desktop-linux-arm64',
'win32-x64': 'agent-desktop-win32-x64.exe',
};
return platformMap[`${os}-${cpuArch}`] || null;
}
function main() {
const binaryName = getBinaryName();
if (!binaryName) {
console.error(`Error: Unsupported platform: ${platform()}-${arch()}`);
console.error('agent-desktop currently supports: macOS (ARM64, x64)');
console.error('Windows and Linux support is coming in Phase 2.');
console.error('See: https://github.com/lahfir/agent-desktop');
process.exit(1);
}
const binaryPath = join(binDir, binaryName);
if (!binaryPath || !existsSync(binaryPath)) {
console.error(`Error: Native binary not found for ${platform()}-${arch()}`);
console.error(`Expected: ${binaryPath}`);
console.error('');
console.error('Try reinstalling:');
console.error(' npm install -g agent-desktop');
console.error('');
console.error('Or download directly from:');
console.error(' https://github.com/lahfir/agent-desktop/releases');
if (typeof process.versions.bun !== 'undefined') {
console.error('');
console.error('Bun detected — postinstall scripts require --trust:');
console.error(' bun install -g --trust agent-desktop');
}
process.exit(1);
}
if (platform() !== 'win32') {
try {
accessSync(binaryPath, constants.X_OK);
} catch {
try {
chmodSync(binaryPath, 0o755);
} catch (err) {
console.error(`Error: Cannot make binary executable: ${err.message}`);
console.error('Try running: chmod +x ' + binaryPath);
process.exit(1);
}
}
}
if (platform() === 'darwin') {
const override = process.env[MACOS_HELPER_PATH_ENV];
if (override && !isAbsolute(override)) {
console.error(`Error: ${MACOS_HELPER_PATH_ENV} must be an absolute path`);
process.exit(1);
}
const helperPath = override || join(binDir, MACOS_HELPER_NAME);
if (!existsSync(helperPath)) {
console.error(`Error: macOS helper not found: ${helperPath}`);
console.error('Reinstall agent-desktop so the CLI and helper come from the same release.');
process.exit(1);
}
try {
accessSync(helperPath, constants.X_OK);
} catch {
try {
chmodSync(helperPath, 0o755);
} catch (err) {
console.error(`Error: Cannot make macOS helper executable: ${err.message}`);
process.exit(1);
}
}
}
const child = spawn(binaryPath, process.argv.slice(2), {
stdio: 'inherit',
windowsHide: false,
});
child.on('error', (err) => {
console.error(`Error executing binary: ${err.message}`);
process.exit(1);
});
child.on('close', (code) => {
process.exit(code ?? 0);
});
}
main();