Initial rrweb SGrobot skill package

This commit is contained in:
2026-06-24 09:45:48 +08:00
commit 27a0ebe1d7
17 changed files with 1554 additions and 0 deletions

View File

@@ -0,0 +1,28 @@
import { test } from "node:test";
import assert from "node:assert/strict";
import { readFile } from "node:fs/promises";
import { generateSgRobotArtifacts } from "../scripts/generate_sgrobot_flow.mjs";
test("recorded customer callback flow generates SGrobot-compliant artifacts", async () => {
const trace = JSON.parse(await readFile("examples/recordings/customer-callback-trace.json", "utf8"));
const result = generateSgRobotArtifacts(trace, { defaultMode: "execute" });
const plan = result.files.get("plan_templates/rrweb-customer-callback-draft/PlanTemplate.toml");
const skill = result.files.get(".sgclaw-workspace/skills/rrweb-customer-callback-draft-execute/SKILL.toml");
const script = result.files.get(".sgclaw-workspace/skills/rrweb-customer-callback-draft-execute/scripts/execute_trace.js");
const evidence = result.files.get(".sgclaw-workspace/skills/rrweb-customer-callback-draft-execute/trace.semantic.json");
assert.equal(result.summary.planId, "rrweb-customer-callback-draft");
assert.equal(result.summary.skillName, "rrweb-customer-callback-draft-execute");
assert.equal(result.summary.requiresApproval, true);
assert.match(plan, /skills = \["browser\.open_url"\]/);
assert.match(plan, /skills = \["rrweb-customer-callback-draft-execute"\]/);
assert.match(plan, /requires_approval = true/);
assert.match(skill, /kind = "sgbrowser_action"/);
assert.match(skill, /command = "sgBrowserExcuteJsCodeByDomain"/);
assert.match(skill, /script_path = "scripts\/execute_trace.js"/);
assert.match(script, /"type": "select"/);
assert.match(script, /"type": "fill"/);
assert.match(script, /"risk": "write"/);
assert.equal(JSON.parse(evidence).steps.length, 4);
});

View File

@@ -0,0 +1,56 @@
import { test } from "node:test";
import assert from "node:assert/strict";
import { mkdtemp, readFile, rm } from "node:fs/promises";
import { tmpdir } from "node:os";
import path from "node:path";
import { generateSgRobotArtifacts, writeArtifacts } from "../scripts/generate_sgrobot_flow.mjs";
const trace = {
id: "expense-approval",
name: "费用审批",
description: "打开审批页,填写意见并点击同意。",
startUrl: "https://oa.example.test/expense/123",
steps: [
{ type: "assertText", text: "费用审批", label: "确认在审批页" },
{ type: "fill", selector: "textarea[name='comment']", value: "同意", label: "填写审批意见" },
{ type: "click", selector: "button[data-action='approve']", label: "点击同意", risk: "write" },
],
};
test("generates SGrobot plan and runtime skill artifacts", () => {
const result = generateSgRobotArtifacts(trace, { defaultMode: "execute" });
assert.equal(result.summary.planId, "rrweb-expense-approval");
assert.equal(result.summary.skillName, "rrweb-expense-approval-execute");
assert.equal(result.summary.requiresApproval, true);
const plan = result.files.get("plan_templates/rrweb-expense-approval/PlanTemplate.toml");
const skill = result.files.get(".sgclaw-workspace/skills/rrweb-expense-approval-execute/SKILL.toml");
const script = result.files.get(".sgclaw-workspace/skills/rrweb-expense-approval-execute/scripts/execute_trace.js");
assert.equal(plan.includes('skills = ["rrweb-expense-approval-execute"]'), true);
assert.equal(plan.includes("requires_approval = true"), true);
assert.equal(skill.includes('command = "sgBrowserExcuteJsCodeByDomain"'), true);
assert.equal(skill.includes('expected_domain = "oa.example.test"'), true);
assert.equal(script.includes('"default_mode": "execute"'), true);
assert.equal(script.includes('replace(/[\\s_-]+/g, "")'), true);
assert.equal(script.includes('replace(/[\\\\s_-]+/g, "")'), false);
});
test("writes generated artifacts under a SGrobot repository root", async () => {
const root = await mkdtemp(path.join(tmpdir(), "rrweb-sgrobot-"));
try {
const result = generateSgRobotArtifacts(trace);
await writeArtifacts(root, result.files);
const plan = await readFile(path.join(root, "plan_templates/rrweb-expense-approval/PlanTemplate.toml"), "utf8");
const skill = await readFile(path.join(root, ".sgclaw-workspace/skills/rrweb-expense-approval-execute/SKILL.toml"), "utf8");
const evidence = await readFile(path.join(root, ".sgclaw-workspace/skills/rrweb-expense-approval-execute/trace.semantic.json"), "utf8");
assert.match(plan, /skills = \["browser\.open_url"\]/);
assert.match(skill, /script_path = "scripts\/execute_trace.js"/);
assert.equal(JSON.parse(evidence).id, "expense-approval");
} finally {
await rm(root, { recursive: true, force: true });
}
});