mirror of
https://github.com/Tencent/BrowserSkill.git
synced 2026-09-10 13:01:23 +08:00
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)
56 lines
2.2 KiB
TypeScript
56 lines
2.2 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import { SessionRegistry } from "../src/sessions";
|
|
|
|
describe("SessionRegistry", () => {
|
|
it("tracks the current session across add and remove", () => {
|
|
const registry = new SessionRegistry(5);
|
|
expect(registry.current()).toBeUndefined();
|
|
registry.add({ sessionId: "s1", startedAtMs: 1 });
|
|
expect(registry.current()).toBe("s1");
|
|
registry.add({ sessionId: "s2", startedAtMs: 2 });
|
|
expect(registry.current()).toBe("s2");
|
|
registry.remove("s2");
|
|
expect(registry.current()).toBe("s1");
|
|
registry.remove("s1");
|
|
expect(registry.current()).toBeUndefined();
|
|
});
|
|
|
|
it("enforces the concurrency cap", () => {
|
|
const registry = new SessionRegistry(2);
|
|
registry.add({ sessionId: "s1", startedAtMs: 1 });
|
|
registry.add({ sessionId: "s2", startedAtMs: 2 });
|
|
expect(() => registry.add({ sessionId: "s3", startedAtMs: 3 })).toThrow(/session limit/);
|
|
registry.remove("s1");
|
|
registry.add({ sessionId: "s3", startedAtMs: 3 });
|
|
expect(registry.size()).toBe(2);
|
|
});
|
|
|
|
it("resolve prefers the explicit session and adopts unknown ids", () => {
|
|
const registry = new SessionRegistry(5);
|
|
registry.add({ sessionId: "s1", startedAtMs: 1 });
|
|
expect(registry.resolve("s9", "tool")).toBe("s9");
|
|
expect(registry.current()).toBe("s9");
|
|
expect(registry.list().map((s) => s.sessionId)).toContain("s9");
|
|
});
|
|
|
|
it("resolve falls back to the current session", () => {
|
|
const registry = new SessionRegistry(5);
|
|
registry.add({ sessionId: "s1", startedAtMs: 1 });
|
|
expect(registry.resolve(undefined, "tool")).toBe("s1");
|
|
});
|
|
|
|
it("resolve throws actionable guidance when no session exists", () => {
|
|
const registry = new SessionRegistry(5);
|
|
expect(() => registry.resolve(undefined, "browser_click")).toThrow(/browser_session_start/);
|
|
});
|
|
|
|
it("touch refreshes recency so fallback picks the most recent survivor", () => {
|
|
const registry = new SessionRegistry(5);
|
|
registry.add({ sessionId: "s1", startedAtMs: 1 });
|
|
registry.add({ sessionId: "s2", startedAtMs: 2 });
|
|
registry.touch("s1");
|
|
registry.remove("s1");
|
|
expect(registry.current()).toBe("s2");
|
|
});
|
|
});
|