Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

## 0.2.4 - 2026-05-17

### Fixed
- Fixed Windows Chrome CDP startup by spawning the Chrome/Edge/Brave executable directly without a shell.
- Added a regression test to keep Chrome CDP spawn options shell-free on every platform.

## 0.2.3 - 2026-05-17

### Fixed
Expand Down
9 changes: 5 additions & 4 deletions chrome-cdp-backend.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,10 @@ export function buildChromeLaunchArgs({ debugPort, userDataDir, profileName = nu
return args;
}

export function chromeSpawnOptions() {
return { stdio: 'ignore' };
}

async function readJson(url) {
const response = await fetch(url, { headers: { accept: 'application/json' } });
if (!response.ok) {
Expand Down Expand Up @@ -550,10 +554,7 @@ export class ChromeCdpBrowserBackend {
profileName: this.profileName,
startUrl: 'about:blank'
});
this.chromeProcess = spawn(executable, args, {
stdio: 'ignore',
shell: process.platform === 'win32'
});
this.chromeProcess = spawn(executable, args, chromeSpawnOptions());
this.chromeProcess.unref?.();

let version;
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@agentify/desktop",
"version": "0.2.3",
"version": "0.2.4",
"description": "Agentify Desktop control center and MCP server for local AI web sessions",
"license": "MPL-2.0",
"type": "module",
Expand Down
17 changes: 13 additions & 4 deletions tests/chrome-cdp-backend.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import os from 'node:os';
import path from 'node:path';
import fs from 'node:fs/promises';

import { ChromeCdpBrowserBackend, ChromeCdpConnection } from '../chrome-cdp-backend.mjs';
import { ChromeCdpBrowserBackend, ChromeCdpConnection, chromeSpawnOptions } from '../chrome-cdp-backend.mjs';

class MockWebSocket {
constructor() {
Expand Down Expand Up @@ -84,6 +84,12 @@ test('chrome-cdp-backend: pending commands reject when websocket closes', async
await assert.rejects(async () => await pending, /chrome_cdp_disconnected/);
});

test('chrome-cdp-backend: Chrome spawn does not use shell on any platform', () => {
const opts = chromeSpawnOptions();
assert.equal(opts.stdio, 'ignore');
assert.equal(Object.hasOwn(opts, 'shell'), false);
});

test('chrome-cdp-backend: connect rejects if websocket closes before open', async () => {
const conn = new ChromeCdpConnection('ws://example.test/devtools/browser/1', {
wsFactory: () => ({
Expand Down Expand Up @@ -266,12 +272,15 @@ test('chrome-cdp-backend: session close is best-effort when closeTarget fails',

test('chrome-cdp-backend: start cleans up spawned chrome process when CDP connect fails', async () => {
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'agentify-chrome-start-fail-'));
const scriptPath = path.join(tmpDir, 'fake-chrome.sh');
await fs.writeFile(scriptPath, '#!/bin/sh\nsleep 30\n', { encoding: 'utf8', mode: 0o755 });
let executablePath = process.execPath;
if (process.platform !== 'win32') {
executablePath = path.join(tmpDir, 'fake-chrome.sh');
await fs.writeFile(executablePath, '#!/bin/sh\nsleep 30\n', { encoding: 'utf8', mode: 0o755 });
}

const backend = new ChromeCdpBrowserBackend({
stateDir: tmpDir,
executablePath: scriptPath,
executablePath,
debugPort: 45999
});

Expand Down
Loading