DeepSeek Harness plugin

dsh-playwright-native

DeepSeek Harness host plugin that registers the native Playwright CLI as a single passthrough `playwright` tool (args forwarded verbatim).

Jump to install

Source facts

Repository
mitao-su/dsh-playwright-native
Latest update
Aug 14, 2026
Category
Tools & Capabilities
GitHub stars
1
Format
plugin
Catalog evidence
Upstream dsh.bundle evidence
Evidence path
package.json#dsh.bundle
Checked against
0.1.0-rc.8
Upstream check date
2026-08-20

This evidence comes from the upstream catalog. This site has not installed, run, or security-reviewed the plugin.

Install

Start with a prompt that asks an agent to review the GitHub repository and source. Switch to the command if you want to install it yourself.

Copy this prompt into DSH, Codex, or another agent and ask it to review the GitHub repository and source first.

Do not install or run any commands yet. Read this plugin's GitHub repository, README, and relevant source code. Then answer the questions below clearly and directly so I can decide whether it fits my needs:

1. What is this plugin, and what problem does it solve?
2. Who is it for, and what are its typical use cases?
3. How is it used after installation? Include one minimal example.
4. What known limitations or privacy, security, compatibility, or maintenance risks does it have?
5. Give a clear recommendation: recommend, conditionally recommend, or do not recommend, with reasons.

Distinguish statements documented by the repository, inferences from source code, and unknowns. If evidence is insufficient, say so explicitly. Do not guess or simply repeat the README.

GitHub: https://github.com/mitao-su/dsh-playwright-native
Plugin: dsh-playwright-native
Author: mitao-su

Check the source files

Read the README and other files from this plugin directory before installing.

File explorer3 files
README.mdSource · read only

把 Playwright 原生 CLI 注册成 DeepSeek Harness 工具

> 目标:让 Agent 直接调用本机安装的原生 playwright 命令——playwright testplaywright installplaywright show-reportplaywright --version……——而不是某个二次封装后的语义工具。

DSH 里"给 Agent 加一个工具"只有一条正路:在插件里用 ctx.tools.register(defineTool(...)) 注册。所以"把 Playwright CLI 注册成 DSH 插件"的正确做法是:注册一个名叫 playwright 的透传工具,把 Agent 给的参数原样、按序转发给本机 playwright 二进制。它不拆分 install/test/show-report 子工具、不做任何参数映射——Agent 敲什么,就跑什么。

本仓库就是这样一个插件(dsh-playwright-native),可直接安装:

dsh plugin --profile web add github:mitao-su/dsh-playwright-native

---

目录

1. [它和"封装"的区别(先看清要什么)](#1-它和封装的区别先看清要什么) 2. [前置要求](#2-前置要求) 3. [原理:工具注册 = ctx.tools.register](#3-原理工具注册--ctxtoolsregister) 4. [工程结构](#4-工程结构) 5. [Step 1 package.json](#5-step-1-packagejson) 6. [Step 2 cordis.patch.yml](#6-step-2-cordispatchyml) 7. [Step 3 src/index.ts 实现透传工具](#7-step-3-srcindexts-实现透传工具) 8. [Step 4 构建](#8-step-4-构建) 9. [Step 5 注册进 profile](#9-step-5-注册进-profile) 10. [Step 6 使用](#10-step-6-使用) 11. [Step 7 发布到 GitHub(带 dsh-plugin topic)](#11-step-7-发布到-github带-dsh-plugin-topic) 12. [常见问题](#12-常见问题)

---

1. 它和"封装"的区别(先看清要什么)

语义封装(不要)原生注册(本仓库)
工具名playwright_version / playwright_test / …playwright
参数每个子命令一套自订字段一个 args: string[],原样透传
行为把 CLI 选项映射成自定义 schemaAgent 敲什么就 playwright 什么
Agent 视角调一个"别人打包过的工具"原生命令

一句话:CLI 是能力,ctx.shell 是执行缝,defineTool + cordis.patch.yml 是注册。 我们要的是最后两样,不重写第一样。

---

2. 前置要求

要求
Playwright本机已安装 playwright 命令(playwright --version 可跑)。默认用它;也可在配置里改成 npx --no-install playwright
DSHdsh 可用(本教程验证于 0.1.0-rc.6);目标 profile 已挂载 shell 执行器(标准 web/headless 自带)
pnpm用于 dsh plugin add(pnpm ≥ 10 对 Git 依赖默认拦截构建脚本,见 §11)

验证环境:

playwright --version     # 例如 Version 1.62.1
dsh --version            # 例如 0.1.0-rc.6

---

3. 原理:工具注册 = ctx.tools.register

DSH 的模型可见工具全部来自插件注册(或 MCP server)。核心契约:

export const name = 'playwright-native'
export const inject = ['tools', 'shell', 'systemPrompt']   // 必需 service

export function apply(ctx: Context, config: Config): void {
  ctx.tools.register(defineTool({
    name: 'playwright',            // ← Agent 看到的工具名,就是原生命令名
    description: '...',
    parameters: { args: { type: 'array', items: { type: 'string' }, required: true } },
    output: { schema, render },
    execute: async (args, exec) => run(exec, ['playwright', ...args.args]),  // 原样转发
  }))
}
  • 执行缝走 ctx.shell(与官方 bash/pwsh 工具同源),自动获得 sandbox 策略、DSH_* 环境、输出截断、超时/中止分类。
  • cordis.patch.yml + dsh.bundle.patch 让这个包成为 profile 配置树里的一层,dsh plugin add 安装后自动把它写进 dsh.profile.bundles

---

4. 工程结构

dsh-playwright-native/
├── package.json          # dsh.bundle.patch + exports + peerDeps
├── tsconfig.json         # src/ → lib/
├── cordis.patch.yml      # 插件配置层(注册的核心)
├── LICENSE
└── src/
    └── index.ts          # apply() + defineTool('playwright', 透传)

---

5. Step 1 package.json

{
  "name": "dsh-playwright-native",
  "version": "0.1.0",
  "type": "module",
  "main": "lib/index.js",
  "types": "lib/index.d.ts",
  "exports": {
    ".": { "types": "./lib/index.d.ts", "default": "./lib/index.js" },
    "./cordis.patch.yml": "./cordis.patch.yml",
    "./package.json": "./package.json"
  },
  "files": ["lib", "cordis.patch.yml", "README.md", "LICENSE"],
  "dsh": { "bundle": { "patch": "./cordis.patch.yml" } },
  "scripts": {
    "build": "tsc -p tsconfig.json",
    "typecheck": "tsc -p tsconfig.json --noEmit"
  },
  "keywords": ["dsh-plugin", "deepseek-harness", "playwright", "e2e", "testing"],
  "license": "MIT",
  "peerDependencies": {
    "@deepseek-ai/cordis": "^4.0.1",
    "@deepseek-ai/dsh-agent": "^0.1.0-rc.6",
    "@deepseek-ai/dsh-shell": "0.1.0-rc.6",
    "@deepseek-ai/dsh-tools": "0.1.0-rc.6",
    "@deepseek-ai/schemastery": "3.18.1"
  }
}

要点:

  • dsh.bundle.patch 是注册的关键:没有它,包只是普通依赖,不会成为 profile 层。
  • exports 必须暴露 ./cordis.patch.yml./package.json,供 loader 读取。
  • DSH/Cordis 运行时声明为 peerDependencies,避免复制运行时身份。
  • keywords 里的 dsh-plugin 服务 npm 搜索;GitHub 仓库的 topic 是另一处(§11)。

tsconfig.json

{
  "compilerOptions": {
    "target": "ES2022",
    "module": "NodeNext",
    "moduleResolution": "NodeNext",
    "strict": true,
    "declaration": true,
    "outDir": "lib",
    "rootDir": "src",
    "skipLibCheck": true,
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,
    "types": ["node"]
  },
  "include": ["src"]
}

---

6. Step 2 cordis.patch.yml

顶层数组;id 是配置树里稳定的行身份,name 是 Node 可解析的包名:

# dsh-playwright-native bundle layer.
- insert:
    - id: playwright-native
      name: dsh-playwright-native
      config:
        command: playwright        # 可改成 "npx --no-install playwright" 强制本地副本
        timeoutMs: 600000          # 每次调用的默认超时(毫秒)

注意:后层按 id 覆盖前层,且 config整段替换(不是深合并),覆盖时要重述所需键。

---

7. Step 3 src/index.ts 实现透传工具

完整实现见 [src/index.ts](src/index.ts)。核心只有两点——args 原样拼到命令上ctx.shell

export const name = 'playwright-native'
export const inject = ['tools', 'shell', 'systemPrompt']

export interface Config { command: string; timeoutMs: number }
export const Config = z.object({
  command: z.string().default('playwright'),      // 默认直呼本机原生命令
  timeoutMs: z.number().default(600_000),
})

export function apply(ctx: Context, config: Config): void {
  const run = async (exec: ToolRunContext, argv: string[]) => {
    const command = argv.map(quote).join(' ')
    const spec = ctx.shell.resolve({              // 走 shell 执行器,不做 child_process 手搓
      command,
      workdir: resolveWorkdir(args.workdir, exec),  // 默认会话工作区
      timeoutMs: config.timeoutMs,
      signal: exec.signal,                          // 转发中止
    })
    const result = await ctx.shell.run(spec)
    return canonical(result)                        // { exitCode, signal, stdout, stderr, ... }
  }

  ctx.systemPrompt.section({
    name: 'tool:playwright',
    order: 106,
    text: 'The `playwright` tool runs the native Playwright CLI ... check the [exit code: N] and [sandbox: ...] markers.',
  })

  ctx.tools.register(defineTool({
    name: 'playwright',
    description:
      'Run the native Playwright CLI with the exact arguments you would type on the command line. ' +
      'Arguments are forwarded verbatim and in order — do not prepend `playwright` yourself. ...',
    parameters: {
      args: {                        // ← 唯一核心参数:原生命令参数数组
        type: 'array',
        items: { type: 'string' },
        required: true,
        description: 'Playwright CLI arguments in order, e.g. ["test", "tests/", "--reporter", "html"].',
      },
      workdir: { type: 'string', description: 'Working directory (default: session workspace).' },
      timeoutMs: { type: 'integer', description: 'Timeout override in milliseconds.' },
    },
    output: { schema: runSchema, render: (_, v) => textBlock(renderRun(v)) },
    isConcurrencySafe: () => false,  // playwright test 共享 test-results/report 目录,不并发
    execute: async (args, exec) =>
      run(exec, [...config.command.split(/\s+/).filter(Boolean), ...args.args]),  // 原样透传
    presentCall: (args) => ({
      card: 'terminal',
      title: `playwright ${(args.args ?? []).join(' ')}`,
      description: 'Run the native Playwright CLI',
    }),
  }))
}

几个"原生"的关键点:

  • 只有一个 args 数组,没有把 install/test/show-report 拆成不同工具,也没有把 CLI 选项映射成自定义 schema。
  • 非零退出 = 报告,不是报错:结果带 [exit code: N] 标记,由模型自行决定下一步。
  • command 可配置:默认 playwright(直呼本机原生命令);要强制走项目本地副本时改成 npx --no-install playwright
  • 沙箱升级playwright test 会 fork worker 子进程、playwright install 会下载浏览器,都可能被文件沙箱拒绝。完整实现里保留了与官方 shell 工具一致的 sandbox_permissions + justification 升级出口(拒绝后带 [sandbox: file access denied ...] 标记,用最窄更宽模式重试、执行前弹审批)。

---

8. Step 4 构建

pnpm install
pnpm build        # tsc → lib/index.js + lib/index.d.ts

lib/ 提交进 Git(本仓库即如此),Git 分发时 pnpm 不执行任何 prepare 脚本、零交互、无需 allowBuilds

---

9. Step 5 注册进 profile

dsh plugin 是 profile 目录里的 pnpm 转发层:首次使用时初始化 profile → 转发 pnpm add → 按安装状态把声明了 dsh.bundle 的包对账进 dsh.profile.bundles

# 本地路径(先这样验证)
dsh plugin --profile web add ./dsh-playwright-native

# GitHub(本机 git 可访问 GitHub)
dsh plugin --profile web add github:mitao-su/dsh-playwright-native

# 受限网络备选:GitHub tarball
dsh plugin --profile web add https://github.com/mitao-su/dsh-playwright-native/archive/refs/heads/main.tar.gz

安装后重启目标 profile。验证注册生效:

dsh --profile web --dump-config

应能看到这一层(本教程实测):

# == dsh-playwright-native
- id: playwright-native
  name: dsh-playwright-native
  config:
    command: playwright
    timeoutMs: 600000

> 实测过程:全新 profile 先被初始化为 @deepseek-ai/dsh-baseadddsh.profile.bundles 变成 ["@deepseek-ai/dsh-base", "dsh-playwright-native"]--dump-config 里出现上面这层。

配置覆盖(profile 的 cordis.patch.ymlid 整段替换):

- insert:
    - id: playwright-native
      name: dsh-playwright-native
      config:
        command: npx --no-install playwright
        timeoutMs: 600000

---

10. Step 6 使用

注册后,Agent 的工具列表里多了一个 playwright,直接传原生命令参数:

Agent 想做的事调用 playwrightargs
看版本["--version"]
装浏览器["install", "chromium"]
跑测试["test", "tests/login.spec.ts", "--reporter", "html"]
打开报告["show-report"]
录脚本["codegen", "https://example.com"]
截图["screenshot", "https://example.com", "shot.png"]

无 GUI 的一次真实任务验证:

dsh --profile headless "用 playwright 跑 tests/ 下的用例并打开报告"

---

11. Step 7 发布到 GitHub(带 dsh-plugin topic)

keywords 里的 dsh-plugin 只服务 npm 搜索;GitHub 仓库的 topic 是另一处

cd dsh-playwright-native
git init -b main && git add . && git commit -m "register the native Playwright CLI as a DSH tool"

# 1) 建公开仓库并从本地推上去(owner 省略时默认当前登录账号)
gh repo create dsh-playwright-native --public --source . --remote origin --push \
  --description "把原生 Playwright CLI 注册为 DeepSeek Harness 透传工具"

# 2) 打 topic
gh repo edit mitao-su/dsh-playwright-native --add-topic dsh-plugin

# 3) 验证
gh repo view mitao-su/dsh-playwright-native --json name,url,repositoryTopics

Git 分发说明:Git 获取的是源码,不是构建产物。本仓库走"把 lib/ 提交进 Git"的无交互安装路径(无 prepare 脚本),所以无需 allowBuilds;若你的插件要跑构建脚本,pnpm ≥ 10 会默认拦截,需在 profile 的 pnpm-workspace.yaml 显式 allowBuilds 后重跑 add

---

12. 常见问题

  • 沙箱拒绝playwright test fork worker / playwright install 下载浏览器可能被文件沙箱拒绝,结果带 [sandbox: file access denied ...] 标记。用 sandbox_permissions + justification 以最窄更宽模式重试同一命令(执行前弹审批,仅本次生效)。
  • 双副本:若把 command 配成 npx --no-install playwright,它只认 workdir 里的本地 @playwright/test;spec 文件与 CLI 解析到不同副本会出现 test.describe() not expected here。解决:workdir 指向装有本地 @playwright/test 的项目目录,或直接保持默认 command: playwright
  • 引号:参数按挂载 shell 方言(bash/pwsh)自动安全引用;含单引号的路径请先重命名。
  • 并发playwright test 声明 isConcurrencySafe: () => false(共享 test-results/report 目录)。

License

MIT(见 [LICENSE](LICENSE))。