Compare commits

..
4 Commits
Author SHA1 Message Date
StanPonomarev 7708df699b Merge pull request 'fix: playwritgt timing out' (#3) from fix/playwright-timeout into main
Release / release (push) Successful in 8s
Reviewed-on: #3
2026-09-12 19:18:21 +02:00
StanPonomarev 4532af33fb fix: playwritgt timing out
CI / verify (pull_request) Successful in 8s
2026-09-12 19:17:30 +02:00
StanPonomarev 5b6d5fc301 Merge pull request 'fix tmp dirs' (#2) from fix/wrong_tmp_dir into main
Release / release (push) Successful in 9s
Reviewed-on: #2
2026-09-12 19:10:43 +02:00
StanPonomarev 62b56b6a8a fix tmp dirs
CI / verify (pull_request) Successful in 8s
2026-09-12 19:09:49 +02:00
5 changed files with 53 additions and 23 deletions
+29
View File
@@ -0,0 +1,29 @@
name: CI
on:
pull_request:
permissions:
code: read
jobs:
verify:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: https://github.com/actions/checkout@v4
with:
github-server-url: ${{ gitea.server_url }}
persist-credentials: false
- name: Install Bun
uses: https://github.com/oven-sh/setup-bun@v2
with:
bun-version: "1.3.14"
- name: Verify and build
run: |
bun install --frozen-lockfile
bun run check
bun run lint
bun run format:check
bun run test
bun run build
+3 -1
View File
@@ -36,7 +36,9 @@ For an installed plugin, use `./plugins/stanp-opencode` as the plugin package pa
## Playwright ## Playwright
The plugin prefers a local Playwright MCP installation. If no local installation is available, Docker is required for the automatic fallback. Existing `playwright` configuration is preserved. The plugin prefers a local Playwright MCP installation. If ordinary Playwright is installed, it can download and run the MCP package with `npx`; otherwise Docker is required for the automatic fallback. Automatically configured servers allow five minutes for startup because the first launch may need to download a package or container image. Docker reuses its cached image on later launches. Existing `playwright` configuration, including its timeout, is preserved.
If `opencode mcp list` reports `MCP error -32001: Request timed out`, prefetch the Docker fallback with `docker pull mcr.microsoft.com/playwright/mcp`, or install `@playwright/mcp` in the project so the plugin can launch it directly. Then reconnect the server from `/mcps` or restart OpenCode. A custom `playwright` entry under `mcp.servers` can also set its own `timeout.startup` in milliseconds.
## Uninstall ## Uninstall
+5 -1
View File
@@ -118,7 +118,11 @@ export default Plugin.define({
await context.mcp.transform((editor) => { await context.mcp.transform((editor) => {
if (!editor.get("playwright")) if (!editor.get("playwright"))
editor.set("playwright", { type: "local", command: playwrightCommand }); editor.set("playwright", {
type: "local",
command: playwrightCommand,
timeout: { startup: 300_000 },
});
hasCodegraph = editor.get("codegraph") !== undefined; hasCodegraph = editor.get("codegraph") !== undefined;
if (hasCodegraph) if (hasCodegraph)
editor.update("codegraph", (server) => { editor.update("codegraph", (server) => {
+1 -9
View File
@@ -3,15 +3,7 @@ import { constants } from "node:fs";
import { dirname, join, parse, resolve } from "node:path"; import { dirname, join, parse, resolve } from "node:path";
import { z } from "zod"; import { z } from "zod";
const dockerCommand = [ const dockerCommand = ["docker", "run", "-i", "--rm", "--init", "mcr.microsoft.com/playwright/mcp"];
"docker",
"run",
"-i",
"--rm",
"--init",
"--pull=always",
"mcr.microsoft.com/playwright/mcp",
];
const directPackageManifest = z.object({ const directPackageManifest = z.object({
name: z.literal("@playwright/mcp"), name: z.literal("@playwright/mcp"),
bin: z.string(), bin: z.string(),
+15 -12
View File
@@ -1,5 +1,6 @@
import { afterEach, expect, test } from "bun:test"; import { afterEach, expect, test } from "bun:test";
import { chmod, mkdir, mkdtemp, rm, symlink, writeFile } from "node:fs/promises"; import { chmod, mkdir, mkdtemp, rm, symlink, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path"; import { join } from "node:path";
import { Agent, Model, Plugin, Provider } from "@opencode/plugin"; import { Agent, Model, Plugin, Provider } from "@opencode/plugin";
import type { AgentEditor } from "@opencode/plugin/promise/agent"; import type { AgentEditor } from "@opencode/plugin/promise/agent";
@@ -32,7 +33,13 @@ async function host(
options: Plugin.Context["options"] = { provider: "test" }, options: Plugin.Context["options"] = { provider: "test" },
configuredMcp: Record< configuredMcp: Record<
string, string,
{ type: "local"; command: string[]; codemode?: boolean; disabled?: boolean } {
type: "local";
command: string[];
codemode?: boolean;
disabled?: boolean;
timeout?: { startup?: number; catalog?: number; execution?: number };
}
> = {}, > = {},
configuredMcpTools: string[] = [], configuredMcpTools: string[] = [],
directory = "/test", directory = "/test",
@@ -418,9 +425,9 @@ test("coordinator inherits only codegraph MCP permissions when that server exist
test("playwright MCP prefers direct MCP, uses npx for Playwright, and preserves fallback behavior", async () => { test("playwright MCP prefers direct MCP, uses npx for Playwright, and preserves fallback behavior", async () => {
const originalPath = process.env.PATH; const originalPath = process.env.PATH;
process.env.PATH = ""; const root = await mkdtemp(join(tmpdir(), "stanp-playwright-"));
const root = await mkdtemp("/tmp/opencode/stanp-playwright-");
try { try {
process.env.PATH = "";
const packageRoot = join(root, "local", "node_modules", "@playwright", "mcp"); const packageRoot = join(root, "local", "node_modules", "@playwright", "mcp");
const executable = join(packageRoot, "cli.js"); const executable = join(packageRoot, "cli.js");
const localProject = join(root, "local", "project"); const localProject = join(root, "local", "project");
@@ -439,6 +446,7 @@ test("playwright MCP prefers direct MCP, uses npx for Playwright, and preserves
expect(local.mcpServers.get("playwright")).toEqual({ expect(local.mcpServers.get("playwright")).toEqual({
type: "local", type: "local",
command: [executable], command: [executable],
timeout: { startup: 300_000 },
}); });
const globalPackage = join(root, "global", "@playwright", "mcp"); const globalPackage = join(root, "global", "@playwright", "mcp");
@@ -529,15 +537,8 @@ test("playwright MCP prefers direct MCP, uses npx for Playwright, and preserves
const fallback = await host(); const fallback = await host();
expect(fallback.mcpServers.get("playwright")).toEqual({ expect(fallback.mcpServers.get("playwright")).toEqual({
type: "local", type: "local",
command: [ command: ["docker", "run", "-i", "--rm", "--init", "mcr.microsoft.com/playwright/mcp"],
"docker", timeout: { startup: 300_000 },
"run",
"-i",
"--rm",
"--init",
"--pull=always",
"mcr.microsoft.com/playwright/mcp",
],
}); });
const existing = await host( const existing = await host(
@@ -548,6 +549,7 @@ test("playwright MCP prefers direct MCP, uses npx for Playwright, and preserves
type: "local", type: "local",
command: ["custom-playwright-mcp"], command: ["custom-playwright-mcp"],
disabled: true, disabled: true,
timeout: { startup: 12_345 },
}, },
docs: { type: "local", command: ["docs"], codemode: true }, docs: { type: "local", command: ["docs"], codemode: true },
}, },
@@ -556,6 +558,7 @@ test("playwright MCP prefers direct MCP, uses npx for Playwright, and preserves
type: "local", type: "local",
command: ["custom-playwright-mcp"], command: ["custom-playwright-mcp"],
disabled: true, disabled: true,
timeout: { startup: 12_345 },
}); });
expect(existing.mcpServers.get("docs")?.codemode).toBe(true); expect(existing.mcpServers.get("docs")?.codemode).toBe(true);
} finally { } finally {