feat(packages): add dsh-plugin-browserskill — DeepSeek Harness tools for bsk

Add a new monorepo package that registers BrowserSkill (bsk) browser
automation as model-visible tools in DeepSeek Harness (dsh):

- 11 tools mapping to bsk CLI --json commands: session start/stop/list,
  navigate, snapshot, observe, click, fill, press, screenshot, emulate
- multi-session support: optional session arg with a current-session
  pointer, session id echoed in every result, configurable concurrency
  cap (default 5), and dispose-time cleanup of plugin-started sessions
- cancellation: exec.signal aborts kill the underlying bsk child process
- terminal-style UI cards (command line as the call card, output as the
  result card); screenshots inline the PNG through the host attachment
  store when the model route accepts image input, else return the path
- config: bskPath / defaultTimeoutMs / maxSessions via Schemastery schema
- install guidance when the bsk binary is missing; bsk JSON error
  envelopes (code/message/hint) surfaced to the model
- unit tests mock the bsk runner (no real browser required); the root
  lint script now also runs the package's typecheck + tests so the
  existing CI frontend job covers the new package (a dedicated CI job
  diff is provided in the PR; the pushing token lacks workflow scope)
This commit is contained in:
BB-fat
2026-08-13 15:04:52 +00:00
parent 4549ed0892
commit 703fc64399
17 changed files with 3135 additions and 11 deletions
@@ -0,0 +1,80 @@
/**
* dsh-plugin-browserskill — a DeepSeek Harness bundle that registers
* BrowserSkill (`bsk`) browser automation as model-visible tools.
*
* Each tool spawns `bsk <cmd> --json`, parses the structured output, and
* returns a canonical JSON value. The plugin tracks the sessions it starts so
* one agent conversation can drive several browsers at once; unloading the
* plugin stops every tracked session and kills any in-flight bsk children.
*
* @module dsh-plugin-browserskill
*/
import type { Context } from "@deepseek-ai/cordis";
import Schema from "@deepseek-ai/schemastery";
import { createBskRunner } from "./runner";
import { SessionRegistry } from "./sessions";
import { type PluginConfig, registerTools } from "./tools";
export const name = "dsh-plugin-browserskill";
export const inject = ["tools"];
/** Runtime configuration schema (validated and defaulted by Cordis). */
export const Config = Schema.object({
bskPath: Schema.string()
.default("bsk")
.description("Path to the bsk CLI binary (defaults to resolving `bsk` from PATH)."),
defaultTimeoutMs: Schema.number()
.default(120_000)
.description("Default per-command timeout in milliseconds."),
maxSessions: Schema.number()
.default(5)
.description("Maximum number of concurrent browser sessions started through this plugin."),
});
export type Config = PluginConfig;
export function apply(ctx: Context, config: Partial<PluginConfig> = {}): void {
const resolved: PluginConfig = {
bskPath: config.bskPath ?? "bsk",
defaultTimeoutMs: config.defaultTimeoutMs ?? 120_000,
maxSessions: config.maxSessions ?? 5,
};
const runner = createBskRunner(resolved.bskPath);
const registry = new SessionRegistry(resolved.maxSessions);
registerTools({ ctx, runner, registry, config: resolved });
// Non-blocking install probe: warn early when bsk is missing instead of
// failing the first tool call with a bare spawn error.
runner.run(["status"], { timeoutMs: 10_000 }).then(
() => {},
(error: unknown) => {
const detail = error instanceof Error ? error.message : String(error);
console.warn(
`[${name}] bsk probe failed (${detail}); browser tools will report install guidance until the bsk CLI is available`,
);
},
);
// Unload cleanup: kill in-flight children, then stop every session this
// plugin started (best effort; sessions started outside the plugin are
// left running).
ctx.effect(() => {
return () => {
runner.killAll();
const stops = registry
.list()
.map((session) =>
runner.run(["session", "stop", session.sessionId], { timeoutMs: 15_000 }).catch(() => {}),
);
return Promise.all(stops).then(() => {});
};
});
}
export type { BskRunner, BskRunResult, SpawnImpl } from "./runner";
export { BskError, createBskRunner } from "./runner";
export { SessionRegistry } from "./sessions";
export type { PluginConfig, ToolDeps } from "./tools";
export { registerTools } from "./tools";