browser-opt 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. package/dist/browser-opt/index.d.ts +9 -0
  2. package/dist/browser-opt/index.d.ts.map +1 -0
  3. package/dist/browser-opt/index.js +7 -0
  4. package/dist/browser-opt/index.js.map +1 -0
  5. package/dist/browser-opt/runner/actions/index.d.ts +13 -0
  6. package/dist/browser-opt/runner/actions/index.d.ts.map +1 -0
  7. package/dist/browser-opt/runner/actions/index.js +44 -0
  8. package/dist/browser-opt/runner/actions/index.js.map +1 -0
  9. package/dist/browser-opt/runner/actions/utils/click-action.d.ts +10 -0
  10. package/dist/browser-opt/runner/actions/utils/click-action.d.ts.map +1 -0
  11. package/dist/browser-opt/runner/actions/utils/click-action.js +17 -0
  12. package/dist/browser-opt/runner/actions/utils/click-action.js.map +1 -0
  13. package/dist/browser-opt/runner/actions/utils/date-action.d.ts +21 -0
  14. package/dist/browser-opt/runner/actions/utils/date-action.d.ts.map +1 -0
  15. package/dist/browser-opt/runner/actions/utils/date-action.js +464 -0
  16. package/dist/browser-opt/runner/actions/utils/date-action.js.map +1 -0
  17. package/dist/browser-opt/runner/actions/utils/dom-action.d.ts +8 -0
  18. package/dist/browser-opt/runner/actions/utils/dom-action.d.ts.map +1 -0
  19. package/dist/browser-opt/runner/actions/utils/dom-action.js +167 -0
  20. package/dist/browser-opt/runner/actions/utils/dom-action.js.map +1 -0
  21. package/dist/browser-opt/runner/actions/utils/fill-action.d.ts +10 -0
  22. package/dist/browser-opt/runner/actions/utils/fill-action.d.ts.map +1 -0
  23. package/dist/browser-opt/runner/actions/utils/fill-action.js +20 -0
  24. package/dist/browser-opt/runner/actions/utils/fill-action.js.map +1 -0
  25. package/dist/browser-opt/runner/actions/utils/handoff-action.d.ts +10 -0
  26. package/dist/browser-opt/runner/actions/utils/handoff-action.d.ts.map +1 -0
  27. package/dist/browser-opt/runner/actions/utils/handoff-action.js +6 -0
  28. package/dist/browser-opt/runner/actions/utils/handoff-action.js.map +1 -0
  29. package/dist/browser-opt/runner/actions/utils/open-action.d.ts +10 -0
  30. package/dist/browser-opt/runner/actions/utils/open-action.d.ts.map +1 -0
  31. package/dist/browser-opt/runner/actions/utils/open-action.js +9 -0
  32. package/dist/browser-opt/runner/actions/utils/open-action.js.map +1 -0
  33. package/dist/browser-opt/runner/actions/utils/select-option-action.d.ts +17 -0
  34. package/dist/browser-opt/runner/actions/utils/select-option-action.d.ts.map +1 -0
  35. package/dist/browser-opt/runner/actions/utils/select-option-action.js +532 -0
  36. package/dist/browser-opt/runner/actions/utils/select-option-action.js.map +1 -0
  37. package/dist/browser-opt/runner/actions/utils/upload-action.d.ts +7 -0
  38. package/dist/browser-opt/runner/actions/utils/upload-action.d.ts.map +1 -0
  39. package/dist/browser-opt/runner/actions/utils/upload-action.js +173 -0
  40. package/dist/browser-opt/runner/actions/utils/upload-action.js.map +1 -0
  41. package/dist/browser-opt/runner/evidence.d.ts +18 -0
  42. package/dist/browser-opt/runner/evidence.d.ts.map +1 -0
  43. package/dist/browser-opt/runner/evidence.js +116 -0
  44. package/dist/browser-opt/runner/evidence.js.map +1 -0
  45. package/dist/browser-opt/runner/handoff.d.ts +28 -0
  46. package/dist/browser-opt/runner/handoff.d.ts.map +1 -0
  47. package/dist/browser-opt/runner/handoff.js +132 -0
  48. package/dist/browser-opt/runner/handoff.js.map +1 -0
  49. package/dist/browser-opt/runner/index.d.ts +11 -0
  50. package/dist/browser-opt/runner/index.d.ts.map +1 -0
  51. package/dist/browser-opt/runner/index.js +244 -0
  52. package/dist/browser-opt/runner/index.js.map +1 -0
  53. package/dist/browser-opt/runner/step-executor.d.ts +5 -0
  54. package/dist/browser-opt/runner/step-executor.d.ts.map +1 -0
  55. package/dist/browser-opt/runner/step-executor.js +302 -0
  56. package/dist/browser-opt/runner/step-executor.js.map +1 -0
  57. package/dist/browser-opt/type.d.ts +120 -0
  58. package/dist/browser-opt/type.d.ts.map +1 -0
  59. package/dist/browser-opt/type.js +2 -0
  60. package/dist/browser-opt/type.js.map +1 -0
  61. package/dist/browser-opt/utils.d.ts +48 -0
  62. package/dist/browser-opt/utils.d.ts.map +1 -0
  63. package/dist/browser-opt/utils.js +885 -0
  64. package/dist/browser-opt/utils.js.map +1 -0
  65. package/dist/browser-opt/workflow/index.d.ts +7 -0
  66. package/dist/browser-opt/workflow/index.d.ts.map +1 -0
  67. package/dist/browser-opt/workflow/index.js +6 -0
  68. package/dist/browser-opt/workflow/index.js.map +1 -0
  69. package/dist/browser-opt/workflow/matcher.d.ts +12 -0
  70. package/dist/browser-opt/workflow/matcher.d.ts.map +1 -0
  71. package/dist/browser-opt/workflow/matcher.js +102 -0
  72. package/dist/browser-opt/workflow/matcher.js.map +1 -0
  73. package/dist/browser-opt/workflow/store.d.ts +15 -0
  74. package/dist/browser-opt/workflow/store.d.ts.map +1 -0
  75. package/dist/browser-opt/workflow/store.js +175 -0
  76. package/dist/browser-opt/workflow/store.js.map +1 -0
  77. package/dist/browser-opt/workflow/type.d.ts +43 -0
  78. package/dist/browser-opt/workflow/type.d.ts.map +1 -0
  79. package/dist/browser-opt/workflow/type.js +6 -0
  80. package/dist/browser-opt/workflow/type.js.map +1 -0
  81. package/dist/cli/browser-opt.d.ts +3 -0
  82. package/dist/cli/browser-opt.d.ts.map +1 -0
  83. package/dist/cli/browser-opt.js +11 -0
  84. package/dist/cli/browser-opt.js.map +1 -0
  85. package/dist/cli/commands/browser-opt.d.ts +2 -0
  86. package/dist/cli/commands/browser-opt.d.ts.map +1 -0
  87. package/dist/cli/commands/browser-opt.js +514 -0
  88. package/dist/cli/commands/browser-opt.js.map +1 -0
  89. package/dist/cli/commands/setup.d.ts +10 -0
  90. package/dist/cli/commands/setup.d.ts.map +1 -0
  91. package/dist/cli/commands/setup.js +35 -0
  92. package/dist/cli/commands/setup.js.map +1 -0
  93. package/dist/cli/utils/args.d.ts +17 -0
  94. package/dist/cli/utils/args.d.ts.map +1 -0
  95. package/dist/cli/utils/args.js +74 -0
  96. package/dist/cli/utils/args.js.map +1 -0
  97. package/dist/cli/utils/constants.d.ts +8 -0
  98. package/dist/cli/utils/constants.d.ts.map +1 -0
  99. package/dist/cli/utils/constants.js +18 -0
  100. package/dist/cli/utils/constants.js.map +1 -0
  101. package/dist/cli/utils/output.d.ts +7 -0
  102. package/dist/cli/utils/output.d.ts.map +1 -0
  103. package/dist/cli/utils/output.js +45 -0
  104. package/dist/cli/utils/output.js.map +1 -0
  105. package/package.json +35 -0
  106. package/skills/browser-opt/SKILL.md +249 -0
  107. package/skills/browser-opt/manifest.json +75 -0
@@ -0,0 +1,74 @@
1
+ /**
2
+ * 统一封装 CLI 参数解析与常用 flag 读取逻辑,避免各命令重复处理细节。
3
+ * 这里仅负责轻量解析,不引入命令语义,让命令文件保持聚焦在业务流程上。
4
+ */
5
+ export function parseCliArgs(args) {
6
+ const positionals = [];
7
+ const flags = {};
8
+ for (let i = 0; i < args.length; i++) {
9
+ const token = args[i];
10
+ if (!token.startsWith('--')) {
11
+ positionals.push(token);
12
+ continue;
13
+ }
14
+ const key = token.slice(2);
15
+ const next = args[i + 1];
16
+ if (next && !next.startsWith('--')) {
17
+ flags[key] = next;
18
+ i += 1;
19
+ }
20
+ else {
21
+ flags[key] = true;
22
+ }
23
+ }
24
+ return { positionals, flags };
25
+ }
26
+ export function getStringFlag(flags, key) {
27
+ const value = flags[key];
28
+ return typeof value === 'string' ? value : undefined;
29
+ }
30
+ export function getBooleanFlag(flags, key) {
31
+ return flags[key] === true;
32
+ }
33
+ export function resolveProfile(flags) {
34
+ return getStringFlag(flags, 'profile');
35
+ }
36
+ export function resolveStatePath(flags) {
37
+ const flagState = getStringFlag(flags, 'state')?.trim();
38
+ if (flagState) {
39
+ return flagState;
40
+ }
41
+ const envState = process.env.AGENT_BROWSER_STATE?.trim();
42
+ return envState || undefined;
43
+ }
44
+ export function resolveReuseRunningBrowser(flags, statePath, defaultValue = false) {
45
+ if (statePath) {
46
+ return false;
47
+ }
48
+ if (getBooleanFlag(flags, 'clean-browser')) {
49
+ return false;
50
+ }
51
+ if (getBooleanFlag(flags, 'reuse-focused-browser')) {
52
+ return true;
53
+ }
54
+ return defaultValue;
55
+ }
56
+ export function resolveLiveViewport(flags) {
57
+ if (getBooleanFlag(flags, 'no-live-viewport')) {
58
+ return false;
59
+ }
60
+ if (getBooleanFlag(flags, 'live-viewport')) {
61
+ return true;
62
+ }
63
+ return true;
64
+ }
65
+ export function parseCsv(value) {
66
+ if (!value) {
67
+ return [];
68
+ }
69
+ return value
70
+ .split(',')
71
+ .map((entry) => entry.trim())
72
+ .filter((entry) => entry.length > 0);
73
+ }
74
+ //# sourceMappingURL=args.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"args.js","sourceRoot":"","sources":["../../../src/cli/utils/args.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * 存放 browser-opt CLI 复用的提示文案与常量,避免命令编排文件混入长文本。
3
+ * 当前只保留 browser-opt 自身会消费的内容,防止把 browser-e2e 边界带进来。
4
+ */
5
+ export declare const LIVE_VIEWPORT_DASHBOARD_URL = "http://localhost:4848";
6
+ export declare const HANDOFF_DONE_ANSWERS: readonly ["done", "ok", "继续", "完成"];
7
+ export declare const BROWSER_OPT_USAGE = "\u4F7F\u7528\u65B9\u5F0F\uFF1A\n npx browser-opt <\u81EA\u7136\u8BED\u8A00\u6D41\u7A0B> [--profile <name>] [--state <path>] [--session <id>] [--no-live-viewport] [--output-dir <dir>] [--agent-chat]\n npx browser-opt setup [--with-deps] [--skip-skill] [--agent agents|codex] [--skills-dir <\u76EE\u5F55>]\n npx browser-opt save \"<\u540D\u79F0>\" --flow \"<\u5B8C\u6574\u6D41\u7A0B>\" [--workflow-dir <\u76EE\u5F55>] [--force]\n npx browser-opt run \"<\u67E5\u8BE2\u8BED\u53E5>\" [--workflow-dir <\u76EE\u5F55>]\n npx browser-opt run --workflow-id \"<ID>\" [--workflow-dir <\u76EE\u5F55>]\n npx browser-opt start --workflow-id \"<ID>\" [--workflow-dir <\u76EE\u5F55>] [--json]\n npx browser-opt status --run-id \"<ID>\" [--json]\n npx browser-opt resume --run-id \"<ID>\" [--json]\n npx browser-opt list [--workflow-dir <\u76EE\u5F55>] [--json]\n npx browser-opt match \"<\u67E5\u8BE2\u8BED\u53E5>\" [--workflow-dir <\u76EE\u5F55>] [--json]";
8
+ //# sourceMappingURL=constants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../src/cli/utils/constants.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,eAAO,MAAM,2BAA2B,0BAA0B,CAAC;AAEnE,eAAO,MAAM,oBAAoB,qCAAsC,CAAC;AAExE,eAAO,MAAM,iBAAiB,07BAUkC,CAAC"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * 存放 browser-opt CLI 复用的提示文案与常量,避免命令编排文件混入长文本。
3
+ * 当前只保留 browser-opt 自身会消费的内容,防止把 browser-e2e 边界带进来。
4
+ */
5
+ export const LIVE_VIEWPORT_DASHBOARD_URL = 'http://localhost:4848';
6
+ export const HANDOFF_DONE_ANSWERS = ['done', 'ok', '继续', '完成'];
7
+ export const BROWSER_OPT_USAGE = `使用方式:
8
+ npx browser-opt <自然语言流程> [--profile <name>] [--state <path>] [--session <id>] [--no-live-viewport] [--output-dir <dir>] [--agent-chat]
9
+ npx browser-opt setup [--with-deps] [--skip-skill] [--agent agents|codex] [--skills-dir <目录>]
10
+ npx browser-opt save "<名称>" --flow "<完整流程>" [--workflow-dir <目录>] [--force]
11
+ npx browser-opt run "<查询语句>" [--workflow-dir <目录>]
12
+ npx browser-opt run --workflow-id "<ID>" [--workflow-dir <目录>]
13
+ npx browser-opt start --workflow-id "<ID>" [--workflow-dir <目录>] [--json]
14
+ npx browser-opt status --run-id "<ID>" [--json]
15
+ npx browser-opt resume --run-id "<ID>" [--json]
16
+ npx browser-opt list [--workflow-dir <目录>] [--json]
17
+ npx browser-opt match "<查询语句>" [--workflow-dir <目录>] [--json]`;
18
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../../../src/cli/utils/constants.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,CAAC,MAAM,2BAA2B,GAAG,uBAAuB,CAAC;AAEnE,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAU,CAAC;AAExE,MAAM,CAAC,MAAM,iBAAiB,GAAG;;;;;;;;;;gEAU+B,CAAC"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * 统一管理 browser-opt CLI 的结果输出,避免命令编排代码夹杂大量控制台细节。
3
+ * 该文件只保留 browser-opt 自己会用到的展示逻辑,不再耦合 browser-e2e 文案。
4
+ */
5
+ import type { BrowserOptRunResult } from '../../browser-opt/type.js';
6
+ export declare function printBrowserOptResult(result: BrowserOptRunResult): void;
7
+ //# sourceMappingURL=output.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"output.d.ts","sourceRoot":"","sources":["../../../src/cli/utils/output.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAGrE,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,mBAAmB,GAAG,IAAI,CA8CvE"}
@@ -0,0 +1,45 @@
1
+ import { collectFailedBrowserOptSteps, formatBrowserOptStepStatus } from '../../browser-opt/utils.js';
2
+ export function printBrowserOptResult(result) {
3
+ if (result.passed) {
4
+ console.log('执行成功');
5
+ return;
6
+ }
7
+ const { report } = result;
8
+ console.log(`Status: ${report.status}`);
9
+ if (report.handoffTriggered) {
10
+ console.log('Handoff: 已触发,请在浏览器中完成登录后输入 done 继续。');
11
+ }
12
+ console.log(`Report JSON: ${report.reportJsonPath}`);
13
+ console.log(`Report Markdown: ${report.reportMarkdownPath}`);
14
+ console.log(`Log: ${report.logPath}`);
15
+ const failedSteps = collectFailedBrowserOptSteps(report);
16
+ console.log('Failed steps:');
17
+ if (failedSteps.length === 0) {
18
+ console.log(' - n/a');
19
+ }
20
+ else {
21
+ for (const step of failedSteps) {
22
+ console.log(` - ${step.index}. ${step.instruction}`);
23
+ console.log(` Reason: ${step.error ?? step.verification ?? '未知原因'}`);
24
+ }
25
+ }
26
+ console.log('Evidence screenshots:');
27
+ for (const screenshot of report.screenshots) {
28
+ console.log(` - ${screenshot}`);
29
+ }
30
+ console.log('Detailed log:');
31
+ for (const log of report.logs) {
32
+ console.log(` ${log}`);
33
+ }
34
+ for (const step of report.steps) {
35
+ const mark = formatBrowserOptStepStatus(step);
36
+ console.log(`${mark} ${step.index}. ${step.instruction}`);
37
+ if (step.handoffTriggered && step.verification) {
38
+ console.log(` Handoff: ${step.verification}`);
39
+ }
40
+ if (step.error) {
41
+ console.log(` Error: ${step.error}`);
42
+ }
43
+ }
44
+ }
45
+ //# sourceMappingURL=output.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"output.js","sourceRoot":"","sources":["../../../src/cli/utils/output.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,4BAA4B,EAAE,0BAA0B,EAAE,MAAM,4BAA4B,CAAC;AAEtG,MAAM,UAAU,qBAAqB,CAAC,MAA2B;IAC/D,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;QAClB,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACpB,OAAO;IACT,CAAC;IAED,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC;IAC1B,OAAO,CAAC,GAAG,CAAC,WAAW,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;IACxC,IAAI,MAAM,CAAC,gBAAgB,EAAE,CAAC;QAC5B,OAAO,CAAC,GAAG,CAAC,qCAAqC,CAAC,CAAC;IACrD,CAAC;IACD,OAAO,CAAC,GAAG,CAAC,gBAAgB,MAAM,CAAC,cAAc,EAAE,CAAC,CAAC;IACrD,OAAO,CAAC,GAAG,CAAC,oBAAoB,MAAM,CAAC,kBAAkB,EAAE,CAAC,CAAC;IAC7D,OAAO,CAAC,GAAG,CAAC,QAAQ,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC;IAEtC,MAAM,WAAW,GAAG,4BAA4B,CAAC,MAAM,CAAC,CAAC;IACzD,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;IAC7B,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC7B,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IACzB,CAAC;SAAM,CAAC;QACN,KAAK,MAAM,IAAI,IAAI,WAAW,EAAE,CAAC;YAC/B,OAAO,CAAC,GAAG,CAAC,OAAO,IAAI,CAAC,KAAK,KAAK,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC;YACtD,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,YAAY,IAAI,MAAM,EAAE,CAAC,CAAC;QAC1E,CAAC;IACH,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,uBAAuB,CAAC,CAAC;IACrC,KAAK,MAAM,UAAU,IAAI,MAAM,CAAC,WAAW,EAAE,CAAC;QAC5C,OAAO,CAAC,GAAG,CAAC,OAAO,UAAU,EAAE,CAAC,CAAC;IACnC,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;IAC7B,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;QAC9B,OAAO,CAAC,GAAG,CAAC,KAAK,GAAG,EAAE,CAAC,CAAC;IAC1B,CAAC;IAED,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;QAChC,MAAM,IAAI,GAAG,0BAA0B,CAAC,IAAI,CAAC,CAAC;QAC9C,OAAO,CAAC,GAAG,CAAC,GAAG,IAAI,IAAI,IAAI,CAAC,KAAK,KAAK,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC;QAC1D,IAAI,IAAI,CAAC,gBAAgB,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;YAC/C,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,CAAC,YAAY,EAAE,CAAC,CAAC;QACjD,CAAC;QACD,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;QACxC,CAAC;IACH,CAAC;AACH,CAAC"}
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "browser-opt",
3
+ "version": "1.0.1",
4
+ "description": "带证据报告的自然语言浏览器工作流 CLI",
5
+ "type": "module",
6
+ "main": "dist/browser-opt/index.js",
7
+ "types": "dist/browser-opt/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/browser-opt/index.js",
11
+ "types": "./dist/browser-opt/index.d.ts"
12
+ }
13
+ },
14
+ "bin": {
15
+ "browser-opt": "dist/cli/browser-opt.js"
16
+ },
17
+ "files": [
18
+ "dist",
19
+ "skills/browser-opt"
20
+ ],
21
+ "scripts": {
22
+ "build": "tsc -p tsconfig.json",
23
+ "typecheck": "tsc -p tsconfig.json --noEmit"
24
+ },
25
+ "engines": {
26
+ "node": ">=24.0.0"
27
+ },
28
+ "publishConfig": {
29
+ "access": "public"
30
+ },
31
+ "dependencies": {
32
+ "@browser-automated/browser-core": "1.0.0",
33
+ "agent-browser": "^0.33.1"
34
+ }
35
+ }
@@ -0,0 +1,249 @@
1
+ ---
2
+ name: browser-opt
3
+ description: Execute natural-language browser flows with agent-browser and produce simplified PASS/FAIL results.
4
+ summary: Run deterministic browser execution loops with screenshots, JSON snapshots, retries, and concise success output.
5
+ ---
6
+
7
+ # browser-opt skill
8
+
9
+ This skill is the deterministic browser execution entrypoint. It is intentionally separate from `browser-e2e`: `browser-opt` runs the flow now and reports evidence; it does not match generated tests or create Playwright code.
10
+
11
+ By default, the calling AI is responsible for understanding the page from `snapshot --json`; `agent-browser` only executes deterministic commands such as `open`, `fill @ref`, `click @ref`, screenshots, and waits. Do not use `agent-browser chat` unless the user explicitly asks for the legacy chat mode or passes `--agent-chat`.
12
+
13
+ Natural-language steps that explicitly ask to open developer tools are mapped to
14
+ CDP's experimental `Target.openDevTools` command. This opens native DevTools for
15
+ the active target in the same Chrome window; do not use `agent-browser inspect`,
16
+ which opens a separate DevTools frontend URL. If the current Chrome version does
17
+ not support `Target.openDevTools`, report the failure instead of falling back to
18
+ the separate frontend. Supported expressions include `打开开发者工具`,
19
+ `启动 DevTools`, `调起 Chrome DevTools`, and `inspect current page`:
20
+
21
+ ```text
22
+ /browser-opt 测试 https://example.com 的页面调试流程。
23
+
24
+ 目标:
25
+ 1. 打开开发者工具。
26
+ ```
27
+
28
+ ## Image upload examples
29
+
30
+ For test environments, describe upload steps as automatic URL uploads. `browser-opt` downloads the remote image into the run evidence directory and calls `agent-browser upload` with the local file path:
31
+
32
+ ```text
33
+ /browser-opt 测试 https://example.com/live/create 的直播间创建流程。
34
+
35
+ 目标:
36
+ 1. 打开页面。
37
+ 2. 在“直播间名称”输入“自动化测试直播间”。
38
+ 3. 自动上传“直播间分享封面”,图片来源 URL 为“https://stantic.ifengqun.com/front/fq-ecmiddle-sys/upload/82243689cae75e27b3867a5cbdd4292b.png”。
39
+ 4. 自动上传“直播间封面”,图片来源 URL 为“https://stantic.ifengqun.com/front/fq-ecmiddle-sys/upload/82243689cae75e27b3867a5cbdd4292b.png”。
40
+ 5. 验证页面显示封面预览或上传成功状态。
41
+ ```
42
+
43
+ For production environments, describe upload steps as manual handoff so the operator can choose real local images through the system file picker:
44
+
45
+ ```text
46
+ /browser-opt 操作 https://example.com/live/create 的直播间创建流程。
47
+
48
+ 目标:
49
+ 1. 打开页面。
50
+ 2. 在“直播间名称”输入“正式直播间名称”。
51
+ 3. handoff 给操作人员:请手动选择“直播间分享封面”的本地真实图片,并在裁剪/确认完成后恢复自动化。
52
+ 4. handoff 给操作人员:请手动选择“直播间封面”的本地真实图片,并在裁剪/确认完成后恢复自动化。
53
+ 5. 验证页面显示封面预览或上传成功状态。
54
+ ```
55
+
56
+ ## Required agent-browser practice
57
+
58
+ Every execution must follow these rules:
59
+
60
+ - Always open the actual system Chrome browser for the run. Do not open or operate inside the agent tool's built-in browser, including the Copilot/Codex in-app browser or any agent-browser dashboard/preview window.
61
+ - Strictly run an `open -> snapshot --json -> deterministic act -> re-snapshot` loop.
62
+ - Take a screenshot for every step.
63
+ - Use text matching or element existence checks for verification points.
64
+ - Retry with a fresh snapshot when an action fails or an element reference is stale.
65
+ - Use `--json` output when parsing elements.
66
+ - After every step, reason about the current page state and the next action.
67
+ - If a reference becomes invalid, take a fresh snapshot before retrying.
68
+ - Prefer deterministic commands and element refs over natural-language `chat`.
69
+ - Final report must include `PASS` or `FAIL`, evidence screenshot paths, and detailed logs.
70
+
71
+ ## Interactive handoff execution
72
+
73
+ For saved workflows, start the browser run as a detached task and keep the returned stable `runId`. Do not keep an `exec_command` or PTY session id as the recovery handle; Codex may discard that process handle when the handoff response ends the current turn.
74
+
75
+ ```bash
76
+ npx --yes browser-opt start --workflow-id "<matched.id>" --json
77
+ npx --yes browser-opt status --run-id "<runId>" --json
78
+ ```
79
+
80
+ Poll `status` until it returns `PASS`, `FAIL`, or `HANDOFF`. When it returns `HANDOFF`, ask the user to finish the manual action and end the current turn normally. After the user replies `done`, restore the original runner and browser with:
81
+
82
+ ```bash
83
+ npx --yes browser-opt resume --run-id "<runId>" --json
84
+ npx --yes browser-opt status --run-id "<runId>" --json
85
+ ```
86
+
87
+ The detached task still executes `browser-opt run` exactly once. `status` is read-only and `resume` only sends a one-time signal to that original process. Never start a second `run` or `start` command to simulate resume.
88
+
89
+ ## Saved workflows
90
+
91
+ Reusable flows are stored as JSON files under the calling project's
92
+ `.browser-opt/workflows/` directory by default. Resolve relative paths from the
93
+ calling project's current working directory, not from this skill or package directory.
94
+
95
+ Install the published CLI and this Skill with `npx --yes browser-opt setup`.
96
+ By default it installs to the shared Agent Skills directory, similar to
97
+ `npx skills add`. Use `--agent codex` for the Codex-specific skills directory,
98
+ or `--skills-dir <dir>` for another agent root.
99
+ Use this command prefix for every `browser-opt` invocation:
100
+
101
+ ```bash
102
+ npx --yes browser-opt
103
+ ```
104
+
105
+ Only use `browser-opt ...` when `command -v browser-opt` succeeds in the same
106
+ execution environment; otherwise use `npx --yes browser-opt ...`.
107
+
108
+ Save a complete flow without executing it:
109
+
110
+ ```bash
111
+ npx --yes browser-opt save "创建安选公开直播流程" --flow "<full natural language flow>"
112
+ npx --yes browser-opt save "创建安选公开直播流程" --flow "<full natural language flow>" --workflow-dir ./custom/workflows
113
+ ```
114
+
115
+ Saving an existing name fails by default. Only pass `--force` when the user
116
+ explicitly wants to replace it.
117
+
118
+ Saved workflow files are structured JSON, not a single `flow` string. The
119
+ persisted schema uses `target.url` as the page entrypoint plus a `steps` string
120
+ array for business actions after the page is opened. When `target.url` exists,
121
+ do not include a first step such as "打开页面":
122
+
123
+ ```json
124
+ {
125
+ "id": "创建安选公开直播流程",
126
+ "name": "创建安选公开直播流程",
127
+ "target": {
128
+ "url": "https://example.com/live/create"
129
+ },
130
+ "steps": [
131
+ "在“直播间名称”输入“自动化测试直播间”。",
132
+ "验证页面包含“创建成功”。"
133
+ ],
134
+ "createdAt": "2026-07-28T08:00:00.000Z",
135
+ "updatedAt": "2026-07-28T08:00:00.000Z"
136
+ }
137
+ ```
138
+
139
+ When `/browser-opt` is followed by a short request without a URL, such as:
140
+
141
+ ```text
142
+ /browser-opt 执行创建安选公开直播流程
143
+ ```
144
+
145
+ Do not treat it as a new one-shot flow. First run:
146
+
147
+ ```bash
148
+ npx --yes browser-opt match "<short request>" --json
149
+ ```
150
+
151
+ Handle the JSON result as follows:
152
+
153
+ - `matched`: run `npx --yes browser-opt start --workflow-id "<matched.id>" --json`, retain its `runId`, and follow the interactive handoff execution protocol above.
154
+ - `ambiguous`: do not rely on the CLI's human-readable stdout as the user-facing
155
+ choice list, and do not ask through a modal/input tool that may render Markdown
156
+ as plain text. Parse `match --json`, then ask in a normal assistant message.
157
+ Show the returned candidates, at most three, numbered from 1. Render each
158
+ candidate name itself as a Markdown file link using `filePath`, such as
159
+ `[创建安选公开直播流程](</absolute/path/创建安选公开直播流程.json>)`. Do not show a
160
+ bare `file://` URL. Ask the user to reply with the number (for example `1`,
161
+ `2`, or `3`), even when there is only one weakly similar candidate. Do not
162
+ open a browser before the choice. Then map the selected number back to the
163
+ candidate ID and run it. In GitHub Copilot Chat, local Markdown file links may
164
+ render as plain text; include `displayPath` in backticks after the link so the
165
+ path remains visible and can be opened through VS Code's file detection.
166
+ - `not-found`: tell the user no saved workflow matched and show the returned
167
+ available workflow names as Markdown file links using `filePath` when present.
168
+ Ask for a more specific request or a full flow with URL.
169
+ - Warnings describe invalid workflow files that were skipped. Report them without
170
+ blocking valid candidates.
171
+
172
+ Use `--workflow-dir` consistently on both `match` and `run` when the user selects
173
+ a custom directory. Use `npx --yes browser-opt list --json` when
174
+ the user asks to see all saved workflows.
175
+
176
+ Example ambiguous response format:
177
+
178
+ ```markdown
179
+ 匹配结果是 `ambiguous`,请回复要执行的流程编号:
180
+
181
+ 1. [创建安选公开直播(测试环境)](</Users/lee/project/.browser-opt/workflows/创建安选公开直播(测试环境).json>)
182
+ `.browser-opt/workflows/创建安选公开直播(测试环境).json`
183
+ 2. [创建安选私域直播](</Users/lee/project/.browser-opt/workflows/创建安选私域直播.json>)
184
+ `.browser-opt/workflows/创建安选私域直播.json`
185
+
186
+ 请回复数字,例如 `1`。
187
+ ```
188
+
189
+ ## Trigger
190
+
191
+ Use `/browser-opt` followed by either a full natural-language flow or a saved
192
+ workflow request. A full flow includes its target URL:
193
+
194
+ ```text
195
+ /browser-opt 测试 https://example.com 的搜索功能。
196
+
197
+ 目标:
198
+ 1. 打开首页。
199
+ 2. 在搜索框输入 "agent-browser"。
200
+ 3. 点击搜索按钮。
201
+ 4. 验证搜索结果页面是否包含至少 3 个结果项。
202
+ 5. 点击第一个结果,验证跳转正确。
203
+ ```
204
+
205
+ Translate that into:
206
+
207
+ ```bash
208
+ npx --yes browser-opt "<full natural language flow>"
209
+ ```
210
+
211
+ Optional runtime flags:
212
+
213
+ ```bash
214
+ npx --yes browser-opt "<flow>" --profile Default
215
+ npx --yes browser-opt "<flow>" --state ./.browser-opt/states/browser-opt-default.json
216
+ npx --yes browser-opt "<flow>" --no-live-viewport
217
+ npx --yes browser-opt "<flow>" --output-dir ./.browser-opt/artifacts
218
+ npx --yes browser-opt "<flow>" --agent-chat
219
+ ```
220
+
221
+ Auth state reuse policy:
222
+
223
+ - `browser-opt` first checks its saved auth state under `.browser-opt/states/`.
224
+ - If a default state file exists, it loads that state first, so only cookies/storage are reused and prior Chrome tabs are not restored.
225
+ - If the default state opens on a login screen or later redirects there, close the state window and replace it once with the selected Chrome profile before entering handoff. Keep that profile window and the original `browser-opt` runner alive for resume so the operator can use Chrome's password manager.
226
+ - Saved workflows keep the original runner alive as a detached task and use `runId` for handoff recovery across Codex turns; an `exec_command` session id is never a durable recovery handle.
227
+ - Programmatic runs use the same one-time profile fallback when the default state is invalid.
228
+ - If no default state file exists, the single main agent opens the target directly with `--profile Default` and saves state from that same window. Do not create a separate profile importer.
229
+ - Pass `--profile <name>` to choose a different Chrome profile for first import and default-state fallback.
230
+ - Pass `--state <path>` to use a custom state file without automatic profile fallback.
231
+ - Do not rely on focused-browser reuse for login import: ordinary Chrome is usually not CDP-accessible, and auto-connect can attach to the wrong temporary browser.
232
+
233
+ It shows and keeps the actual system Chrome browser by default so the user can watch the operation and inspect the final page state. This must be a real Chrome window, not the agent tool's built-in browser such as the Copilot/Codex in-app browser, and it must not open the agent-browser dashboard at `http://localhost:4848`. Use `--no-live-viewport` only when the user explicitly wants headless execution. `--agent-chat` is a legacy compatibility mode. It may require `AI_GATEWAY_API_KEY`; avoid it when the caller can inspect snapshots and produce deterministic actions.
234
+
235
+ ## Output
236
+
237
+ The CLI writes an evidence directory under `.browser-opt/artifacts/` unless `--output-dir` is provided.
238
+
239
+ Expected artifacts:
240
+
241
+ - `report.json` for machine parsing.
242
+ - `report.md` for human review.
243
+ - `00-open.png`, `01-before.png`, `01-after.png`, and later step screenshots.
244
+ - `*.snapshot.json` files captured through `agent-browser snapshot -i --json`.
245
+
246
+ The assistant response must be concise:
247
+
248
+ - If the run succeeds, reply only with `执行成功`.
249
+ - If the run fails, summarize the failure status, report paths, screenshot paths, and failing step.
@@ -0,0 +1,75 @@
1
+ {
2
+ "name": "browser-opt",
3
+ "version": "0.2.0",
4
+ "description": "Run immediate or project-saved natural-language browser workflows with simplified PASS/FAIL results",
5
+ "triggers": [
6
+ "browser-opt",
7
+ "natural language browser flow",
8
+ "execute browser flow",
9
+ "自然语言执行浏览器流程",
10
+ "执行已保存的浏览器流程",
11
+ "运行 workflow"
12
+ ],
13
+ "inputs": {
14
+ "type": "object",
15
+ "properties": {
16
+ "flow": {
17
+ "type": "string",
18
+ "description": "Full natural-language browser flow including a URL and expected steps"
19
+ },
20
+ "query": {
21
+ "type": "string",
22
+ "description": "Short request used to match a saved workflow in the calling project"
23
+ },
24
+ "workflowDir": {
25
+ "type": "string",
26
+ "description": "Optional workflow directory, default .browser-opt/workflows under the calling project"
27
+ },
28
+ "profile": {
29
+ "type": "string",
30
+ "description": "Chrome profile name or path. Use state for login-only reuse; profile keeps full Chrome profile behavior."
31
+ },
32
+ "state": {
33
+ "type": "string",
34
+ "description": "Path to an agent-browser state file. Preferred for reusing login state without restoring tabs or Chrome recovery UI."
35
+ },
36
+ "liveViewport": {
37
+ "type": "boolean",
38
+ "description": "Whether to show the actual system Chrome browser, default true; never use the agent tool's built-in browser or the agent-browser dashboard"
39
+ },
40
+ "outputDir": {
41
+ "type": "string",
42
+ "description": "Optional directory for evidence reports"
43
+ }
44
+ }
45
+ },
46
+ "outputs": {
47
+ "type": "object",
48
+ "properties": {
49
+ "status": {
50
+ "enum": [
51
+ "PASS",
52
+ "FAIL"
53
+ ]
54
+ },
55
+ "reportJsonPath": {
56
+ "type": "string"
57
+ },
58
+ "reportMarkdownPath": {
59
+ "type": "string"
60
+ },
61
+ "screenshotPaths": {
62
+ "type": "array",
63
+ "items": {
64
+ "type": "string"
65
+ }
66
+ },
67
+ "logPaths": {
68
+ "type": "array",
69
+ "items": {
70
+ "type": "string"
71
+ }
72
+ }
73
+ }
74
+ }
75
+ }