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
Expand Up @@ -6,6 +6,12 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and

## [Unreleased]

## [0.1.2] - 2026-05-03

### Fixed

- **Run steps now work in installed extensions.** The `node_modules/.bin/agent-device` shim that npm creates locally is not preserved in the packaged `.vsix`, so spawning the bundled CLI failed with `ENOENT` on every step. The extension now spawns the CLI's `.mjs` entrypoint directly via the host's Node runtime (`process.execPath` with `ELECTRON_RUN_AS_NODE=1`), which works in VS Code, Cursor, and other Electron-based forks.

## [0.1.1] - 2026-05-03

### Changed
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,7 +1,7 @@
{
"name": "agent-device",
"displayName": "Agent Device",
"version": "0.1.1",
"version": "0.1.2",
"description": "Author, run, and inspect agent-device .ad scripts inside your IDE.",
"categories": [
"Other",
Expand Down
24 changes: 17 additions & 7 deletions src/runners/cliRunner.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
import { spawn } from 'node:child_process';

export type BinPath = string | (() => string);
export interface ResolvedBin {
readonly command: string;
readonly prefixArgs: readonly string[];
readonly env?: NodeJS.ProcessEnv;
}

type BinSpec = string | ResolvedBin;
export type BinPath = BinSpec | (() => BinSpec);

export interface CliExecution {
readonly exitCode: number;
Expand All @@ -17,14 +24,16 @@ export interface CliRunOptions {
export class CliRunner {
constructor(private readonly binPath: BinPath) {}

private resolveBin(): string {
return typeof this.binPath === 'function' ? this.binPath() : this.binPath;
private resolveBin(): ResolvedBin {
const value = typeof this.binPath === 'function' ? this.binPath() : this.binPath;
return typeof value === 'string' ? { command: value, prefixArgs: [] } : value;
}

run(argv: readonly string[], options: CliRunOptions = {}): Promise<CliExecution> {
return new Promise((resolve, reject) => {
const proc = spawn(this.resolveBin(), [...argv], {
env: { ...process.env, ...options.env },
const bin = this.resolveBin();
const proc = spawn(bin.command, [...bin.prefixArgs, ...argv], {
env: { ...process.env, ...bin.env, ...options.env },
cwd: options.cwd,
signal: options.signal,
});
Expand All @@ -46,8 +55,9 @@ export class CliRunner {
}

spawnDetached(argv: readonly string[], options: CliRunOptions = {}): void {
const proc = spawn(this.resolveBin(), [...argv], {
env: { ...process.env, ...options.env },
const bin = this.resolveBin();
const proc = spawn(bin.command, [...bin.prefixArgs, ...argv], {
env: { ...process.env, ...bin.env, ...options.env },
cwd: options.cwd,
detached: true,
stdio: 'ignore',
Expand Down
6 changes: 3 additions & 3 deletions src/wiring/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as vscode from 'vscode';

import { SCRIPT_TEMPLATES } from '../data/templates';
import type { HtmlReportWriter } from '../reports/htmlReportWriter';
import { CliRunner } from '../runners/cliRunner';
import { CliRunner, type ResolvedBin } from '../runners/cliRunner';
import type { ReplayRunner } from '../runners/replayRunner';
import type { DeviceCatalog, DeviceEntry } from '../services/deviceCatalog';
import { parseSnapshotRefs, type SnapshotIndex } from '../services/snapshotIndex';
Expand All @@ -15,7 +15,7 @@ export interface CommandsDeps {
readonly deviceCatalog: DeviceCatalog;
readonly reportWriter: HtmlReportWriter;
readonly snapshotIndex: SnapshotIndex;
readonly resolveCliPath: () => string;
readonly resolveCliPath: () => ResolvedBin;
readonly sessionName: () => string;
}

Expand Down Expand Up @@ -149,7 +149,7 @@ function registerDeviceCommands(context: vscode.ExtensionContext, catalog: Devic
function registerSnapshotCommands(
context: vscode.ExtensionContext,
snapshotIndex: SnapshotIndex,
cliPath: () => string,
cliPath: () => ResolvedBin,
sessionName: () => string,
): void {
const cli = new CliRunner(cliPath);
Expand Down
30 changes: 25 additions & 5 deletions src/wiring/services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import * as path from 'node:path';
import * as vscode from 'vscode';

import { HtmlReportWriter } from '../reports/htmlReportWriter';
import { type ResolvedBin } from '../runners/cliRunner';
import { ReplayRunner } from '../runners/replayRunner';
import { AdFileIndex } from '../services/adFileIndex';
import { AgentDeviceConfig } from '../services/config';
Expand All @@ -10,7 +11,7 @@ import { SnapshotIndex } from '../services/snapshotIndex';

export interface ExtensionServices {
readonly config: AgentDeviceConfig;
readonly resolveCliPath: () => string;
readonly resolveCliPath: () => ResolvedBin;
readonly runner: ReplayRunner;
readonly fileIndex: AdFileIndex;
readonly deviceCatalog: DeviceCatalog;
Expand All @@ -24,7 +25,12 @@ export function createServices(context: vscode.ExtensionContext): ExtensionServi
context.subscriptions.push(output);

const config = new AgentDeviceConfig();
const resolveCliPath = (): string => config.cliPathOverride() ?? resolveBundledCliPath(context);
const resolveCliPath = (): ResolvedBin => {
const override = config.cliPathOverride();
return override !== undefined
? { command: override, prefixArgs: [] }
: resolveBundledCli(context);
};

const runner = new ReplayRunner({
cliPath: resolveCliPath,
Expand Down Expand Up @@ -56,7 +62,21 @@ export function createServices(context: vscode.ExtensionContext): ExtensionServi
};
}

function resolveBundledCliPath(context: vscode.ExtensionContext): string {
const binName = process.platform === 'win32' ? 'agent-device.cmd' : 'agent-device';
return path.join(context.extensionPath, 'node_modules', '.bin', binName);
function resolveBundledCli(context: vscode.ExtensionContext): ResolvedBin {
// The .vsix doesn't include npm's `.bin` shim directory, so spawn the CLI's
// .mjs entrypoint directly via the host's Node runtime. In the extension
// host, process.execPath is Electron — ELECTRON_RUN_AS_NODE makes it behave
// as a plain Node interpreter for the child process.
const scriptPath = path.join(
context.extensionPath,
'node_modules',
'agent-device',
'bin',
'agent-device.mjs',
);
return {
command: process.execPath,
prefixArgs: [scriptPath],
env: { ELECTRON_RUN_AS_NODE: '1' },
};
}
Loading