Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
3 changes: 1 addition & 2 deletions apps/server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,7 @@
"@opencode-ai/sdk": "^1.3.15",
"@pierre/diffs": "catalog:",
"effect": "catalog:",
"node-pty": "^1.1.0",
"open": "^10.1.0"
"node-pty": "^1.1.0"
},
"devDependencies": {
"@effect/language-service": "catalog:",
Expand Down
166 changes: 162 additions & 4 deletions apps/server/src/open.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,53 @@ import * as NodeServices from "@effect/platform-node/NodeServices";
import { assert, it } from "@effect/vitest";
import { assertSuccess } from "@effect/vitest/utils";
import * as Effect from "effect/Effect";
import * as Encoding from "effect/Encoding";
import * as FileSystem from "effect/FileSystem";
import * as Layer from "effect/Layer";
import * as Path from "effect/Path";
import * as Random from "effect/Random";
import * as Sink from "effect/Sink";
import * as Stream from "effect/Stream";
import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process";

import {
launchBrowser,
isCommandAvailable,
launchDetached,
resolveAvailableEditors,
resolveBrowserLaunch,
resolveEditorLaunch,
} from "./open.ts";

function encodeUtf16LeBase64(input: string): string {
const bytes = new Uint8Array(input.length * 2);
for (let index = 0; index < input.length; index += 1) {
const code = input.charCodeAt(index);
bytes[index * 2] = code & 0xff;
bytes[index * 2 + 1] = code >>> 8;
}
return Encoding.encodeBase64(bytes);
}

function makeMockDetachedHandle(onUnref: () => void = () => undefined) {
return ChildProcessSpawner.makeHandle({
pid: ChildProcessSpawner.ProcessId(1),
exitCode: Effect.succeed(ChildProcessSpawner.ExitCode(0)),
isRunning: Effect.succeed(true),
kill: () => Effect.void,
unref: Effect.sync(() => {
onUnref();
return Effect.void;
}),
stdin: Sink.drain,
stdout: Stream.empty,
stderr: Stream.empty,
all: Stream.empty,
getInputFd: () => Sink.drain,
getOutputFd: () => Stream.empty,
});
}

it.layer(NodeServices.layer)("resolveEditorLaunch", (it) => {
it.effect("returns commands for command-based editors", () =>
Effect.gen(function* () {
Expand Down Expand Up @@ -477,23 +513,145 @@ it.layer(NodeServices.layer)("resolveEditorLaunch", (it) => {
);
});

it("resolveBrowserLaunch maps default browser launchers by platform", () => {
const target = "https://example.com/some path?name=o'hara";

assert.deepEqual(resolveBrowserLaunch(target, "darwin").command, "open");
assert.deepEqual(resolveBrowserLaunch(target, "darwin").args, [target]);
assert.deepEqual(resolveBrowserLaunch(target, "darwin").options, {
detached: true,
stdin: "ignore",
stdout: "ignore",
stderr: "ignore",
});

assert.deepEqual(resolveBrowserLaunch(target, "linux", {}).command, "xdg-open");
assert.deepEqual(resolveBrowserLaunch(target, "linux", {}).args, [target]);

const windows = resolveBrowserLaunch(target, "win32", {
SYSTEMROOT: "C:\\Windows",
});
assert.equal(windows.command, "C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe");
assert.deepEqual(windows.args, [
"-NoProfile",
"-NonInteractive",
"-ExecutionPolicy",
"Bypass",
"-EncodedCommand",
encodeUtf16LeBase64(
"$ProgressPreference = 'SilentlyContinue'; Start 'https://example.com/some path?name=o''hara'",
),
]);
assert.deepEqual(windows.options, {
shell: false,
stdin: "ignore",
stdout: "ignore",
stderr: "ignore",
});
});

it("resolveBrowserLaunch opens through Windows from WSL when not remote", () => {
const launch = resolveBrowserLaunch("https://example.com", "linux", {
WSL_DISTRO_NAME: "Ubuntu",
});
assert.equal(launch.command, "/mnt/c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe");
});

it("resolveBrowserLaunch keeps xdg-open for WSL over SSH", () => {
const launch = resolveBrowserLaunch("https://example.com", "linux", {
WSL_DISTRO_NAME: "Ubuntu",
SSH_CONNECTION: "client server",
});
assert.equal(launch.command, "xdg-open");
});

it.layer(NodeServices.layer)("launchBrowser", (it) => {
it.effect("spawns through the ChildProcessSpawner service and unrefs the handle", () =>
Effect.gen(function* () {
let spawnedCommand: ChildProcess.StandardCommand | undefined;
let didUnref = false;

const spawnerLayer = Layer.mock(ChildProcessSpawner.ChildProcessSpawner, {
spawn: (command) =>
Effect.sync(() => {
assert.equal(ChildProcess.isStandardCommand(command), true);
if (!ChildProcess.isStandardCommand(command)) {
throw new Error("Expected a standard command");
}
spawnedCommand = command;
return makeMockDetachedHandle(() => {
didUnref = true;
});
}),
});

const result = yield* launchBrowser("https://example.com").pipe(
Effect.provide(spawnerLayer),
Effect.result,
);

assertSuccess(result, undefined);
assert.ok(spawnedCommand);
const expectedLaunch = resolveBrowserLaunch("https://example.com");
assert.equal(spawnedCommand.command, expectedLaunch.command);
assert.deepEqual(spawnedCommand.args, expectedLaunch.args);
assert.deepEqual(spawnedCommand.options, expectedLaunch.options);
assert.equal(didUnref, true);
}),
);
});

it.layer(NodeServices.layer)("launchDetached", (it) => {
it.effect("resolves when command can be spawned", () =>
it.effect("spawns through the ChildProcessSpawner service and unrefs the handle", () =>
Effect.gen(function* () {
let spawnedCommand: ChildProcess.StandardCommand | undefined;
let didUnref = false;
const expectedArgs = ["-e", "process.exit(0)"];

const spawnerLayer = Layer.mock(ChildProcessSpawner.ChildProcessSpawner, {
spawn: (command) =>
Effect.sync(() => {
assert.equal(ChildProcess.isStandardCommand(command), true);
if (!ChildProcess.isStandardCommand(command)) {
throw new Error("Expected a standard command");
}
spawnedCommand = command;
return makeMockDetachedHandle(() => {
didUnref = true;
});
}),
});

const result = yield* launchDetached({
command: process.execPath,
args: ["-e", "process.exit(0)"],
}).pipe(Effect.result);
args: expectedArgs,
}).pipe(Effect.provide(spawnerLayer), Effect.result);

assertSuccess(result, undefined);
assert.ok(spawnedCommand);
assert.equal(spawnedCommand.command, process.execPath);
assert.deepEqual(
spawnedCommand.args,
process.platform === "win32" ? expectedArgs.map((arg) => `"${arg}"`) : expectedArgs,
);
assert.deepEqual(spawnedCommand.options, {
detached: true,
shell: process.platform === "win32",
stdin: "ignore",
stdout: "ignore",
stderr: "ignore",
});
assert.equal(didUnref, true);
}),
);

it.effect("rejects when command does not exist", () =>
Effect.gen(function* () {
const spawnerLayer = Layer.mock(ChildProcessSpawner.ChildProcessSpawner, {});
const result = yield* launchDetached({
command: `t3code-no-such-command-${yield* Random.nextUUIDv4}`,
args: [],
}).pipe(Effect.result);
}).pipe(Effect.provide(spawnerLayer), Effect.result);
assert.equal(result._tag, "Failure");
}),
);
Expand Down
Loading
Loading