free-computer-use 0.1.0

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 (135) hide show
  1. package/.env.example +31 -0
  2. package/CHANGELOG.md +19 -0
  3. package/CONTRIBUTING.md +66 -0
  4. package/LICENSE +21 -0
  5. package/README.md +140 -0
  6. package/SECURITY.md +133 -0
  7. package/artifacts/benchmark-public.json +797 -0
  8. package/assets/readme/hero.svg +53 -0
  9. package/assets/readme/incident-demo.gif +0 -0
  10. package/assets/readme/incident-evidence.json +52 -0
  11. package/dist/actions/compiler.d.ts +5 -0
  12. package/dist/actions/compiler.js +16 -0
  13. package/dist/actions/compiler.js.map +1 -0
  14. package/dist/actions/executor.d.ts +38 -0
  15. package/dist/actions/executor.js +305 -0
  16. package/dist/actions/executor.js.map +1 -0
  17. package/dist/actions/policy.d.ts +4 -0
  18. package/dist/actions/policy.js +18 -0
  19. package/dist/actions/policy.js.map +1 -0
  20. package/dist/actions/schema.d.ts +4149 -0
  21. package/dist/actions/schema.js +59 -0
  22. package/dist/actions/schema.js.map +1 -0
  23. package/dist/adapters/generic.d.ts +14 -0
  24. package/dist/adapters/generic.js +33 -0
  25. package/dist/adapters/generic.js.map +1 -0
  26. package/dist/agent/Agent.d.ts +69 -0
  27. package/dist/agent/Agent.js +375 -0
  28. package/dist/agent/Agent.js.map +1 -0
  29. package/dist/agent/Control.d.ts +23 -0
  30. package/dist/agent/Control.js +51 -0
  31. package/dist/agent/Control.js.map +1 -0
  32. package/dist/agent/TokenBudget.d.ts +52 -0
  33. package/dist/agent/TokenBudget.js +47 -0
  34. package/dist/agent/TokenBudget.js.map +1 -0
  35. package/dist/agent/goalCriteria.d.ts +2 -0
  36. package/dist/agent/goalCriteria.js +13 -0
  37. package/dist/agent/goalCriteria.js.map +1 -0
  38. package/dist/browser/Browser.d.ts +72 -0
  39. package/dist/browser/Browser.js +378 -0
  40. package/dist/browser/Browser.js.map +1 -0
  41. package/dist/browser/DomExtractor.d.ts +6 -0
  42. package/dist/browser/DomExtractor.js +134 -0
  43. package/dist/browser/DomExtractor.js.map +1 -0
  44. package/dist/browser/Interaction.d.ts +43 -0
  45. package/dist/browser/Interaction.js +225 -0
  46. package/dist/browser/Interaction.js.map +1 -0
  47. package/dist/browser/NetworkGuardProxy.d.ts +31 -0
  48. package/dist/browser/NetworkGuardProxy.js +331 -0
  49. package/dist/browser/NetworkGuardProxy.js.map +1 -0
  50. package/dist/browser/Observer.d.ts +12 -0
  51. package/dist/browser/Observer.js +31 -0
  52. package/dist/browser/Observer.js.map +1 -0
  53. package/dist/browser/PageCompressor.d.ts +47 -0
  54. package/dist/browser/PageCompressor.js +62 -0
  55. package/dist/browser/PageCompressor.js.map +1 -0
  56. package/dist/browser/SecurityBoundaryError.d.ts +3 -0
  57. package/dist/browser/SecurityBoundaryError.js +4 -0
  58. package/dist/browser/SecurityBoundaryError.js.map +1 -0
  59. package/dist/browser/SelectorEngine.d.ts +18 -0
  60. package/dist/browser/SelectorEngine.js +72 -0
  61. package/dist/browser/SelectorEngine.js.map +1 -0
  62. package/dist/browser/types.d.ts +38 -0
  63. package/dist/browser/types.js +2 -0
  64. package/dist/browser/types.js.map +1 -0
  65. package/dist/cli/index.d.ts +2 -0
  66. package/dist/cli/index.js +284 -0
  67. package/dist/cli/index.js.map +1 -0
  68. package/dist/config.d.ts +11 -0
  69. package/dist/config.js +77 -0
  70. package/dist/config.js.map +1 -0
  71. package/dist/history/TraceStore.d.ts +29 -0
  72. package/dist/history/TraceStore.js +53 -0
  73. package/dist/history/TraceStore.js.map +1 -0
  74. package/dist/index.d.ts +18 -0
  75. package/dist/index.js +16 -0
  76. package/dist/index.js.map +1 -0
  77. package/dist/llm/ClaudeSubscriptionProvider.d.ts +26 -0
  78. package/dist/llm/ClaudeSubscriptionProvider.js +182 -0
  79. package/dist/llm/ClaudeSubscriptionProvider.js.map +1 -0
  80. package/dist/llm/CodexSubscriptionProvider.d.ts +26 -0
  81. package/dist/llm/CodexSubscriptionProvider.js +168 -0
  82. package/dist/llm/CodexSubscriptionProvider.js.map +1 -0
  83. package/dist/llm/FlashProvider.d.ts +27 -0
  84. package/dist/llm/FlashProvider.js +103 -0
  85. package/dist/llm/FlashProvider.js.map +1 -0
  86. package/dist/llm/LLMProvider.d.ts +37 -0
  87. package/dist/llm/LLMProvider.js +2 -0
  88. package/dist/llm/LLMProvider.js.map +1 -0
  89. package/dist/llm/cliEnvironment.d.ts +5 -0
  90. package/dist/llm/cliEnvironment.js +21 -0
  91. package/dist/llm/cliEnvironment.js.map +1 -0
  92. package/dist/llm/prompts.d.ts +4 -0
  93. package/dist/llm/prompts.js +12 -0
  94. package/dist/llm/prompts.js.map +1 -0
  95. package/dist/llm/structuredOutput.d.ts +3 -0
  96. package/dist/llm/structuredOutput.js +58 -0
  97. package/dist/llm/structuredOutput.js.map +1 -0
  98. package/dist/mcp/index.d.ts +13 -0
  99. package/dist/mcp/index.js +243 -0
  100. package/dist/mcp/index.js.map +1 -0
  101. package/dist/profile/ProfileStore.d.ts +7 -0
  102. package/dist/profile/ProfileStore.js +52 -0
  103. package/dist/profile/ProfileStore.js.map +1 -0
  104. package/dist/profile/VariableResolver.d.ts +15 -0
  105. package/dist/profile/VariableResolver.js +50 -0
  106. package/dist/profile/VariableResolver.js.map +1 -0
  107. package/dist/server/index.d.ts +14 -0
  108. package/dist/server/index.js +239 -0
  109. package/dist/server/index.js.map +1 -0
  110. package/dist/ui/app.js +132 -0
  111. package/dist/ui/index.html +29 -0
  112. package/dist/ui/logo.svg +5 -0
  113. package/dist/ui/results.js +59 -0
  114. package/dist/ui/style.css +33 -0
  115. package/dist/verification/Verifier.d.ts +72 -0
  116. package/dist/verification/Verifier.js +87 -0
  117. package/dist/verification/Verifier.js.map +1 -0
  118. package/dist/workflows/WorkflowEngine.d.ts +23 -0
  119. package/dist/workflows/WorkflowEngine.js +52 -0
  120. package/dist/workflows/WorkflowEngine.js.map +1 -0
  121. package/docs/ADOPTION.md +61 -0
  122. package/docs/ARCHITECTURE.md +65 -0
  123. package/docs/BENCHMARKS.md +64 -0
  124. package/docs/IMPLEMENTATION.md +93 -0
  125. package/docs/LOCAL_DATA.md +98 -0
  126. package/docs/LOCAL_VALIDATION.md +87 -0
  127. package/docs/MCP.md +81 -0
  128. package/docs/PROVIDERS.md +150 -0
  129. package/docs/RELEASE_CHECKLIST.md +37 -0
  130. package/docs/REQUIREMENTS.md +39 -0
  131. package/docs/SUPPORT_MATRIX.md +193 -0
  132. package/docs/THREAT_MODEL.md +108 -0
  133. package/docs/USEFUL_EXAMPLES.md +151 -0
  134. package/docs/brand-mark.svg +12 -0
  135. package/package.json +90 -0
@@ -0,0 +1,12 @@
1
+ import type { Page } from 'playwright';
2
+ import { DomExtractor } from './DomExtractor.js';
3
+ import { SelectorEngine } from './SelectorEngine.js';
4
+ import { PageCompressor } from './PageCompressor.js';
5
+ export declare class Observer {
6
+ readonly extractor: DomExtractor;
7
+ readonly selectors: SelectorEngine;
8
+ readonly compressor: PageCompressor;
9
+ inspect(page: Page, region?: string): Promise<import("./types.js").PageState>;
10
+ accessibility(page: Page, region?: string): Promise<string>;
11
+ fragment(page: Page, selector: string): Promise<string>;
12
+ }
@@ -0,0 +1,31 @@
1
+ import { DomExtractor } from './DomExtractor.js';
2
+ import { SelectorEngine } from './SelectorEngine.js';
3
+ import { PageCompressor } from './PageCompressor.js';
4
+ export class Observer {
5
+ extractor = new DomExtractor();
6
+ selectors = new SelectorEngine();
7
+ compressor = new PageCompressor();
8
+ async inspect(page, region) {
9
+ const state = await this.extractor.extract(page, region);
10
+ this.selectors.remember(state.elements);
11
+ return state;
12
+ }
13
+ async accessibility(page, region) {
14
+ return (await (region ? page.locator(region) : page.locator('body')).ariaSnapshot()).slice(0, 10000);
15
+ }
16
+ async fragment(page, selector) {
17
+ return page.locator(selector).evaluate(el => {
18
+ const clone = el.cloneNode(true);
19
+ clone.querySelectorAll('script,style,svg').forEach(node => node.remove());
20
+ for (const node of [clone, ...clone.querySelectorAll('*')]) {
21
+ for (const a of [...node.attributes])
22
+ if (!['id', 'role', 'name', 'type', 'aria-label', 'aria-labelledby', 'placeholder', 'required', 'disabled', 'checked', 'data-testid', 'data-fcu-ref'].includes(a.name))
23
+ node.removeAttribute(a.name);
24
+ if (node.matches('textarea'))
25
+ node.textContent = '[local value omitted]';
26
+ }
27
+ return clone.outerHTML.slice(0, 8000);
28
+ });
29
+ }
30
+ }
31
+ //# sourceMappingURL=Observer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Observer.js","sourceRoot":"","sources":["../../src/browser/Observer.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,MAAM,OAAO,QAAQ;IACV,SAAS,GAAC,IAAI,YAAY,EAAE,CAAC;IAC7B,SAAS,GAAC,IAAI,cAAc,EAAE,CAAC;IAC/B,UAAU,GAAC,IAAI,cAAc,EAAE,CAAC;IACzC,KAAK,CAAC,OAAO,CAAC,IAAS,EAAC,MAAc;QACpC,MAAM,KAAK,GAAC,MAAM,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,EAAC,MAAM,CAAC,CAAC;QACtD,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QACxC,OAAO,KAAK,CAAC;IACf,CAAC;IACD,KAAK,CAAC,aAAa,CAAC,IAAS,EAAC,MAAc;QAC1C,OAAO,CAAC,MAAM,CAAC,MAAM,CAAA,CAAC,CAAA,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAA,CAAC,CAAA,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,YAAY,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAC,KAAK,CAAC,CAAC;IAClG,CAAC;IACD,KAAK,CAAC,QAAQ,CAAC,IAAS,EAAC,QAAe;QACtC,OAAO,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAA,EAAE;YACzC,MAAM,KAAK,GAAC,EAAE,CAAC,SAAS,CAAC,IAAI,CAAY,CAAC;YAC1C,KAAK,CAAC,gBAAgB,CAAC,kBAAkB,CAAC,CAAC,OAAO,CAAC,IAAI,CAAA,EAAE,CAAA,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;YACxE,KAAI,MAAM,IAAI,IAAI,CAAC,KAAK,EAAC,GAAG,KAAK,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;gBACzD,KAAI,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,UAAU,CAAC;oBAAE,IAAG,CAAC,CAAC,IAAI,EAAC,MAAM,EAAC,MAAM,EAAC,MAAM,EAAC,YAAY,EAAC,iBAAiB,EAAC,aAAa,EAAC,UAAU,EAAC,UAAU,EAAC,SAAS,EAAC,aAAa,EAAC,cAAc,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC;wBAAE,IAAI,CAAC,eAAe,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;gBAC9N,IAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC;oBAAC,IAAI,CAAC,WAAW,GAAC,uBAAuB,CAAC;YACvE,CAAC;YACD,OAAO,KAAK,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,EAAC,IAAI,CAAC,CAAC;QACvC,CAAC,CAAC,CAAC;IACL,CAAC;CACF"}
@@ -0,0 +1,47 @@
1
+ import type { PageState } from './types.js';
2
+ export declare const similarity: (a: string, b: string) => number;
3
+ export interface Compression {
4
+ text: string;
5
+ bytes: number;
6
+ rawBytes: number;
7
+ reduction: number;
8
+ truncated: boolean;
9
+ }
10
+ export declare class PageCompressor {
11
+ compress(state: PageState, options?: {
12
+ goal?: string;
13
+ maxChars?: number;
14
+ level?: 1 | 2 | 3;
15
+ region?: string;
16
+ }): Compression;
17
+ }
18
+ export declare function diffPages(previous: PageState, current: PageState): {
19
+ url: string;
20
+ title: string;
21
+ hash: string;
22
+ removed: string[];
23
+ added: {
24
+ ref: string;
25
+ role: string;
26
+ name: string;
27
+ type: string | undefined;
28
+ required: boolean | undefined;
29
+ options: string[] | undefined;
30
+ }[];
31
+ changed: {
32
+ ref: string;
33
+ role: string;
34
+ name: string;
35
+ type: string | undefined;
36
+ required: boolean | undefined;
37
+ options: string[] | undefined;
38
+ hasValue: boolean | undefined;
39
+ checked: boolean | undefined;
40
+ error: string | undefined;
41
+ }[];
42
+ headings: string[];
43
+ text: string | undefined;
44
+ tables: string[][][] | undefined;
45
+ dialogs: string[] | undefined;
46
+ warnings: string[];
47
+ };
@@ -0,0 +1,62 @@
1
+ export const similarity = (a, b) => {
2
+ const tokens = (s) => new Set(s.toLowerCase().replace(/[^\p{L}\p{N}]+/gu, ' ').split(' ').filter(word => word && !['the', 'this', 'a', 'and', 'of', 'to', 'in', 'on', 'for', 'with', 'my', 'using', 'from', 'at', 'an', 'then', 'is', 'it', 'me'].includes(word)));
3
+ const aa = tokens(a), bb = tokens(b);
4
+ if (!aa.size || !bb.size)
5
+ return 0;
6
+ return [...aa].filter(x => bb.has(x)).length / new Set([...aa, ...bb]).size;
7
+ };
8
+ export class PageCompressor {
9
+ compress(state, options = {}) {
10
+ const max = options.maxChars ?? 12000;
11
+ const brief = (e) => `[${e.ref}] ${e.role} ${JSON.stringify(e.name)}${e.href ? ` href=${JSON.stringify(e.href)}` : ''}${e.selectors.id ? ` id=${JSON.stringify(e.selectors.id)}` : ''}${e.type ? ` type=${e.type}` : ''}${e.required ? ' required' : ''}${e.disabled ? ' disabled' : ''}${e.form ? ` form=${JSON.stringify(e.form)}` : ''}${e.hasValue ? ' populated' : ''}${e.checked ? ' checked' : ''}${e.error ? ` error=${JSON.stringify(e.error)}` : ''}${e.options ? ` options=${JSON.stringify(e.options)}` : ''}`;
12
+ const elements = state.elements.filter(e => !options.region || e.region === options.region || e.form === options.region);
13
+ const ranked = options.goal ? [...elements].sort((a, b) => similarity(options.goal, [b.name, b.form, b.role].join(' ')) - similarity(options.goal, [a.name, a.form, a.role].join(' '))) : elements;
14
+ const lines = [`PAGE ${state.title}`, `URL ${state.url}`, `STATE ${state.hash}`, `HEADINGS ${state.headings.join(' | ')}`, `WARNINGS ${state.warnings.join(' | ')}`];
15
+ if (options.level !== 1) {
16
+ // Reserve space for document data even when navigation contains hundreds
17
+ // of links. Reading tasks must not lose all facts behind the nav list.
18
+ const controlBudget = Math.max(300, Math.floor(max * .45));
19
+ let used = 0, retained = 0;
20
+ lines.push('INTERACTIVE');
21
+ for (const element of ranked) {
22
+ const line = brief(element);
23
+ if (used + line.length + 1 > controlBudget)
24
+ continue;
25
+ lines.push(line);
26
+ used += line.length + 1;
27
+ retained++;
28
+ }
29
+ if (retained < ranked.length)
30
+ lines.push(`[${ranked.length - retained} controls omitted]`);
31
+ if (state.dialogs.length)
32
+ lines.push('DIALOGS', ...state.dialogs);
33
+ if (state.tables.length)
34
+ lines.push('TABLES', JSON.stringify(state.tables).slice(0, Math.floor(max * .2)));
35
+ }
36
+ else
37
+ lines.push(`INTERACTIVE COUNT ${elements.length}`, `REGIONS ${[...new Set(elements.map(e => e.region).filter(Boolean))].join(' | ')}`);
38
+ lines.push('VISIBLE TEXT', state.text);
39
+ const full = lines.join('\n');
40
+ const truncated = full.length > max || state.truncated;
41
+ const text = full.length > max ? full.slice(0, Math.max(0, max - 55)) + '\n[TRUNCATED: request a region or more context]' : full;
42
+ const bytes = Buffer.byteLength(text);
43
+ return { text, bytes, rawBytes: state.htmlBytes, reduction: state.htmlBytes ? 1 - bytes / state.htmlBytes : 0, truncated };
44
+ }
45
+ }
46
+ export function diffPages(previous, current) {
47
+ const signature = (e) => JSON.stringify([e.role, e.name, e.required, e.disabled, e.hasValue, e.checked, e.error, e.options]);
48
+ const old = new Map(previous.elements.map(e => [e.ref, e]));
49
+ const fresh = new Map(current.elements.map(e => [e.ref, e]));
50
+ return {
51
+ url: current.url, title: current.title, hash: current.hash,
52
+ removed: previous.elements.filter(e => !fresh.has(e.ref)).map(e => e.ref),
53
+ added: current.elements.filter(e => !old.has(e.ref)).map(e => ({ ref: e.ref, role: e.role, name: e.name, type: e.type, required: e.required, options: e.options })),
54
+ changed: current.elements.filter(e => old.has(e.ref) && signature(old.get(e.ref)) !== signature(e)).map(e => ({ ref: e.ref, role: e.role, name: e.name, type: e.type, required: e.required, options: e.options, hasValue: e.hasValue, checked: e.checked, error: e.error })),
55
+ headings: current.headings.filter(h => !previous.headings.includes(h)),
56
+ text: current.text === previous.text ? undefined : current.text,
57
+ tables: JSON.stringify(current.tables) === JSON.stringify(previous.tables) ? undefined : current.tables,
58
+ dialogs: JSON.stringify(current.dialogs) === JSON.stringify(previous.dialogs) ? undefined : current.dialogs,
59
+ warnings: current.warnings,
60
+ };
61
+ }
62
+ //# sourceMappingURL=PageCompressor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PageCompressor.js","sourceRoot":"","sources":["../../src/browser/PageCompressor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ export declare class SecurityBoundaryError extends Error {
2
+ constructor(message: string);
3
+ }
@@ -0,0 +1,4 @@
1
+ export class SecurityBoundaryError extends Error {
2
+ constructor(message) { super(message); this.name = 'SecurityBoundaryError'; }
3
+ }
4
+ //# sourceMappingURL=SecurityBoundaryError.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SecurityBoundaryError.js","sourceRoot":"","sources":["../../src/browser/SecurityBoundaryError.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,qBAAsB,SAAQ,KAAK;IAC9C,YAAY,OAAc,IAAE,KAAK,CAAC,OAAO,CAAC,CAAC,CAAA,IAAI,CAAC,IAAI,GAAC,uBAAuB,CAAC,CAAA,CAAC;CAC/E"}
@@ -0,0 +1,18 @@
1
+ import type { Locator, Page, Frame } from 'playwright';
2
+ import type { Target, SemanticTarget } from '../actions/schema.js';
3
+ import type { PageElement } from './types.js';
4
+ export declare const selectorRanks: readonly ['role', 'label', 'placeholder', 'testId', 'id', 'attributeName', 'text', 'css', 'path'];
5
+ export declare class SelectorEngine {
6
+ private registry;
7
+ remember(elements: PageElement[]): void;
8
+ descriptor(target: Target): SemanticTarget;
9
+ element(target: Target): PageElement | undefined;
10
+ candidates(root: Page | Frame, t: SemanticTarget): {
11
+ strategy: string;
12
+ locator: Locator;
13
+ }[];
14
+ resolve(page: Page, target: Target, timeoutMs?: number, allowMany?: boolean): Promise<{
15
+ locator: Locator;
16
+ strategy: string;
17
+ }>;
18
+ }
@@ -0,0 +1,72 @@
1
+ const attr = (value) => JSON.stringify(value);
2
+ export const selectorRanks = ['role', 'label', 'placeholder', 'testId', 'id', 'attributeName', 'text', 'css', 'path'];
3
+ export class SelectorEngine {
4
+ registry = new Map();
5
+ remember(elements) { for (const element of elements)
6
+ this.registry.set(element.ref, element); while (this.registry.size > 5000)
7
+ this.registry.delete(this.registry.keys().next().value); }
8
+ descriptor(target) {
9
+ if (typeof target !== 'string')
10
+ return target;
11
+ if (target.startsWith('css:'))
12
+ return { css: target.slice(4) };
13
+ const el = this.registry.get(target);
14
+ if (!el)
15
+ throw new Error(`Unknown semantic reference ${target}`);
16
+ return { ...el.selectors };
17
+ }
18
+ element(target) { return typeof target === 'string' ? this.registry.get(target) : undefined; }
19
+ candidates(root, t) {
20
+ const list = [];
21
+ if (t.role)
22
+ list.push({ strategy: 'role', locator: root.getByRole(t.role, t.name ? { name: t.name, exact: true } : {}) });
23
+ if (t.label)
24
+ list.push({ strategy: 'label', locator: root.getByLabel(t.label, { exact: true }) });
25
+ else if (t.name && ['textbox', 'combobox', 'spinbutton', 'upload'].includes(t.role ?? ''))
26
+ list.push({ strategy: 'label', locator: root.getByLabel(t.name, { exact: true }) });
27
+ if (t.placeholder)
28
+ list.push({ strategy: 'placeholder', locator: root.getByPlaceholder(t.placeholder, { exact: true }) });
29
+ if (t.testId)
30
+ list.push({ strategy: 'testId', locator: root.getByTestId(t.testId) });
31
+ if (t.id)
32
+ list.push({ strategy: 'id', locator: root.locator(`[id=${attr(t.id)}]`) });
33
+ if (t.attributeName)
34
+ list.push({ strategy: 'attributeName', locator: root.locator(`[name=${attr(t.attributeName)}]`) });
35
+ if (t.text)
36
+ list.push({ strategy: 'text', locator: root.getByText(t.text, { exact: true }) });
37
+ if (t.css)
38
+ list.push({ strategy: 'css', locator: root.locator(t.css) });
39
+ // Native disclosure summaries are clickable controls, but Chromium does not
40
+ // expose them through Playwright's button role. Preserve exact-name matching.
41
+ if (t.role === 'button' && t.name)
42
+ list.push({ strategy: 'disclosure', locator: root.locator('summary').and(root.getByText(t.name, { exact: true })) });
43
+ if (t.role === 'button' && /^(next( step)?|continue|proceed|advance)$/i.test(t.name ?? ''))
44
+ list.push({ strategy: 'local-synonym', locator: root.getByRole('button', { name: /^(next( step)?|continue|proceed|advance)$/i }) });
45
+ return list;
46
+ }
47
+ async resolve(page, target, timeoutMs = 4000, allowMany = false) {
48
+ const t = this.descriptor(target), observed = this.element(target);
49
+ const root = page.frames()[t.frame ?? 0];
50
+ if (!root)
51
+ throw new Error('Target frame no longer exists');
52
+ const deadline = Date.now() + timeoutMs;
53
+ do {
54
+ for (const candidate of this.candidates(root, t)) {
55
+ const count = await candidate.locator.count();
56
+ if (count === 1 || allowMany && count > 1)
57
+ return candidate;
58
+ }
59
+ if (observed) {
60
+ const ref = root.locator(`[data-fcu-ref=${attr(observed.ref)}]`);
61
+ if (await ref.count() === 1)
62
+ return { locator: ref, strategy: 'reference' };
63
+ const path = root.locator(observed.path);
64
+ if (await path.count() === 1 && await path.evaluate((el, name) => (el.getAttribute('aria-label') || el.textContent || '').replace(/\s+/g, ' ').trim() === name, observed.name))
65
+ return { locator: path, strategy: 'path' };
66
+ }
67
+ await page.waitForTimeout(70);
68
+ } while (Date.now() < deadline);
69
+ throw new Error(`Target missing or ambiguous: ${typeof target === 'string' ? target : JSON.stringify(target)}`);
70
+ }
71
+ }
72
+ //# sourceMappingURL=SelectorEngine.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SelectorEngine.js","sourceRoot":"","sources":["../../src/browser/SelectorEngine.ts"],"names":[],"mappings":"AAGA,MAAM,IAAI,GAAG,CAAC,KAAY,EAAC,EAAE,CAAA,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;AACnD,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,MAAM,EAAC,OAAO,EAAC,aAAa,EAAC,QAAQ,EAAC,IAAI,EAAC,eAAe,EAAC,MAAM,EAAC,KAAK,EAAC,MAAM,CAAU,CAAC;AACvH,MAAM,OAAO,cAAc;IACjB,QAAQ,GAAC,IAAI,GAAG,EAAsB,CAAC;IAC/C,QAAQ,CAAC,QAAsB,IAAI,KAAI,MAAM,OAAO,IAAI,QAAQ;QAAE,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,EAAC,OAAO,CAAC,CAAC,CAAA,OAAM,IAAI,CAAC,QAAQ,CAAC,IAAI,GAAC,IAAI;QAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC,KAAM,CAAC,CAAC,CAAC,CAAC;IAClM,UAAU,CAAC,MAAa;QACtB,IAAG,OAAO,MAAM,KAAG,QAAQ;YAAC,OAAO,MAAM,CAAC;QAC1C,IAAG,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC;YAAC,OAAO,EAAC,GAAG,EAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,EAAC,CAAC;QAC1D,MAAM,EAAE,GAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACnC,IAAG,CAAC,EAAE;YAAC,MAAM,IAAI,KAAK,CAAC,8BAA8B,MAAM,EAAE,CAAC,CAAC;QAC/D,OAAO,EAAC,GAAG,EAAE,CAAC,SAAS,EAAC,CAAC;IAC3B,CAAC;IACD,OAAO,CAAC,MAAa,IAAI,OAAO,OAAO,MAAM,KAAG,QAAQ,CAAA,CAAC,CAAA,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA,CAAC,CAAA,SAAS,CAAC,CAAC,CAAC;IAC/F,UAAU,CAAC,IAAe,EAAC,CAAgB;QACzC,MAAM,IAAI,GAAqC,EAAE,CAAC;QAClD,IAAG,CAAC,CAAC,IAAI;YAAC,IAAI,CAAC,IAAI,CAAC,EAAC,QAAQ,EAAC,MAAM,EAAC,OAAO,EAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,IAAwC,EAAC,CAAC,CAAC,IAAI,CAAA,CAAC,CAAA,EAAC,IAAI,EAAC,CAAC,CAAC,IAAI,EAAC,KAAK,EAAC,IAAI,EAAC,CAAA,CAAC,CAAA,EAAE,CAAC,EAAC,CAAC,CAAC;QAC7I,IAAG,CAAC,CAAC,KAAK;YAAC,IAAI,CAAC,IAAI,CAAC,EAAC,QAAQ,EAAC,OAAO,EAAC,OAAO,EAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,KAAK,EAAC,EAAC,KAAK,EAAC,IAAI,EAAC,CAAC,EAAC,CAAC,CAAC;aAClF,IAAG,CAAC,CAAC,IAAI,IAAE,CAAC,SAAS,EAAC,UAAU,EAAC,YAAY,EAAC,QAAQ,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,IAAE,EAAE,CAAC;YAAC,IAAI,CAAC,IAAI,CAAC,EAAC,QAAQ,EAAC,OAAO,EAAC,OAAO,EAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,EAAC,EAAC,KAAK,EAAC,IAAI,EAAC,CAAC,EAAC,CAAC,CAAC;QAC7J,IAAG,CAAC,CAAC,WAAW;YAAC,IAAI,CAAC,IAAI,CAAC,EAAC,QAAQ,EAAC,aAAa,EAAC,OAAO,EAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,CAAC,WAAW,EAAC,EAAC,KAAK,EAAC,IAAI,EAAC,CAAC,EAAC,CAAC,CAAC;QAC/G,IAAG,CAAC,CAAC,MAAM;YAAC,IAAI,CAAC,IAAI,CAAC,EAAC,QAAQ,EAAC,QAAQ,EAAC,OAAO,EAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC,EAAC,CAAC,CAAC;QAC9E,IAAG,CAAC,CAAC,EAAE;YAAC,IAAI,CAAC,IAAI,CAAC,EAAC,QAAQ,EAAC,IAAI,EAAC,OAAO,EAAC,IAAI,CAAC,OAAO,CAAC,OAAO,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,EAAC,CAAC,CAAC;QAC9E,IAAG,CAAC,CAAC,aAAa;YAAC,IAAI,CAAC,IAAI,CAAC,EAAC,QAAQ,EAAC,eAAe,EAAC,OAAO,EAAC,IAAI,CAAC,OAAO,CAAC,SAAS,IAAI,CAAC,CAAC,CAAC,aAAa,CAAC,GAAG,CAAC,EAAC,CAAC,CAAC;QACjH,IAAG,CAAC,CAAC,IAAI;YAAC,IAAI,CAAC,IAAI,CAAC,EAAC,QAAQ,EAAC,MAAM,EAAC,OAAO,EAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,EAAC,EAAC,KAAK,EAAC,IAAI,EAAC,CAAC,EAAC,CAAC,CAAC;QACnF,IAAG,CAAC,CAAC,GAAG;YAAC,IAAI,CAAC,IAAI,CAAC,EAAC,QAAQ,EAAC,KAAK,EAAC,OAAO,EAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,EAAC,CAAC,CAAC;QACjE,4EAA4E;QAC5E,8EAA8E;QAC9E,IAAG,CAAC,CAAC,IAAI,KAAG,QAAQ,IAAE,CAAC,CAAC,IAAI;YAAC,IAAI,CAAC,IAAI,CAAC,EAAC,QAAQ,EAAC,YAAY,EAAC,OAAO,EAAC,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,EAAC,EAAC,KAAK,EAAC,IAAI,EAAC,CAAC,CAAC,EAAC,CAAC,CAAC;QACzI,IAAG,CAAC,CAAC,IAAI,KAAG,QAAQ,IAAE,4CAA4C,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,IAAE,EAAE,CAAC;YAAC,IAAI,CAAC,IAAI,CAAC,EAAC,QAAQ,EAAC,eAAe,EAAC,OAAO,EAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAC,EAAC,IAAI,EAAC,4CAA4C,EAAC,CAAC,EAAC,CAAC,CAAC;QAC/M,OAAO,IAAI,CAAC;IACd,CAAC;IACD,KAAK,CAAC,OAAO,CAAC,IAAS,EAAC,MAAa,EAAC,SAAS,GAAC,IAAI,EAAC,SAAS,GAAC,KAAK;QAClE,MAAM,CAAC,GAAC,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,QAAQ,GAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QAC/D,MAAM,IAAI,GAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,KAAK,IAAE,CAAC,CAAC,CAAC;QACrC,IAAG,CAAC,IAAI;YAAC,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAC;QAC1D,MAAM,QAAQ,GAAC,IAAI,CAAC,GAAG,EAAE,GAAC,SAAS,CAAC;QACpC,GAAG,CAAC;YACF,KAAI,MAAM,SAAS,IAAI,IAAI,CAAC,UAAU,CAAC,IAAI,EAAC,CAAC,CAAC,EAAE,CAAC;gBAC/C,MAAM,KAAK,GAAC,MAAM,SAAS,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;gBAC5C,IAAG,KAAK,KAAG,CAAC,IAAE,SAAS,IAAE,KAAK,GAAC,CAAC;oBAAC,OAAO,SAAS,CAAC;YACpD,CAAC;YACD,IAAG,QAAQ,EAAE,CAAC;gBACZ,MAAM,GAAG,GAAC,IAAI,CAAC,OAAO,CAAC,iBAAiB,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;gBAC/D,IAAG,MAAM,GAAG,CAAC,KAAK,EAAE,KAAG,CAAC;oBAAC,OAAO,EAAC,OAAO,EAAC,GAAG,EAAC,QAAQ,EAAC,WAAW,EAAC,CAAC;gBACnE,MAAM,IAAI,GAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;gBACvC,IAAG,MAAM,IAAI,CAAC,KAAK,EAAE,KAAG,CAAC,IAAI,MAAM,IAAI,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAC,IAAI,EAAC,EAAE,CAAA,CAAC,EAAE,CAAC,YAAY,CAAC,YAAY,CAAC,IAAE,EAAE,CAAC,WAAW,IAAE,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,EAAC,GAAG,CAAC,CAAC,IAAI,EAAE,KAAG,IAAI,EAAC,QAAQ,CAAC,IAAI,CAAC;oBAAC,OAAM,EAAC,OAAO,EAAC,IAAI,EAAC,QAAQ,EAAC,MAAM,EAAC,CAAC;YACxM,CAAC;YACD,MAAM,IAAI,CAAC,cAAc,CAAC,EAAE,CAAC,CAAC;QAChC,CAAC,QAAM,IAAI,CAAC,GAAG,EAAE,GAAC,QAAQ,EAAE;QAC5B,MAAM,IAAI,KAAK,CAAC,gCAAgC,OAAO,MAAM,KAAG,QAAQ,CAAA,CAAC,CAAA,MAAM,CAAA,CAAC,CAAA,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IAC5G,CAAC;CACF"}
@@ -0,0 +1,38 @@
1
+ import type { SemanticTarget } from '../actions/schema.js';
2
+ export interface PageElement {
3
+ ref: string;
4
+ tag: string;
5
+ role: string;
6
+ name: string;
7
+ label?: string;
8
+ type?: string;
9
+ required?: boolean;
10
+ disabled?: boolean;
11
+ hasValue?: boolean;
12
+ checked?: boolean;
13
+ options?: string[];
14
+ error?: string;
15
+ form?: string;
16
+ frame: number;
17
+ selectors: SemanticTarget;
18
+ path: string;
19
+ region?: string;
20
+ href?: string;
21
+ }
22
+ export interface PageState {
23
+ url: string;
24
+ title: string;
25
+ headings: string[];
26
+ text: string;
27
+ elements: PageElement[];
28
+ tables: string[][][];
29
+ dialogs: string[];
30
+ htmlBytes: number;
31
+ hash: string;
32
+ warnings: string[];
33
+ frames: {
34
+ index: number;
35
+ url: string;
36
+ }[];
37
+ truncated: boolean;
38
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/browser/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,284 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from 'commander';
3
+ import { createInterface } from 'node:readline/promises';
4
+ import { stdin, stdout } from 'node:process';
5
+ import { join, resolve } from 'node:path';
6
+ import { readFile, mkdir } from 'node:fs/promises';
7
+ import { Agent } from '../agent/Agent.js';
8
+ import { TraceStore } from '../history/TraceStore.js';
9
+ import { ProfileStore } from '../profile/ProfileStore.js';
10
+ import { VariableResolver } from '../profile/VariableResolver.js';
11
+ import { WorkflowEngine } from '../workflows/WorkflowEngine.js';
12
+ import { Browser } from '../browser/Browser.js';
13
+ import { FlashProvider } from '../llm/FlashProvider.js';
14
+ import { CodexSubscriptionProvider } from '../llm/CodexSubscriptionProvider.js';
15
+ import { ClaudeSubscriptionProvider } from '../llm/ClaudeSubscriptionProvider.js';
16
+ import { ensureDataDirectory, loadEnvironment, runtimeConfig } from '../config.js';
17
+ import { z } from 'zod';
18
+ const packageManifest = JSON.parse(await readFile(new URL('../../package.json', import.meta.url), 'utf8'));
19
+ loadEnvironment();
20
+ const program = new Command().name('agent').description('DOM-first browser automation: plan once, execute locally.').version(packageManifest.version);
21
+ const policySchema = z.enum(['sensitive', 'always', 'never']);
22
+ function runFlags(command) { return command.option('--headed', 'Show Chromium').option('--debug', 'Log all observe/plan/execute/verify/repair events').option('--profile <file>', 'Use a local profile JSON file').option('--allow-origin <url>', 'Allow another exact origin', (value, old) => [...old, new URL(value).origin], []).option('--allow-external', 'Allow browser requests outside the origin allowlist').option('--confirmation <policy>', 'sensitive | always | never', 'sensitive').option('--no-workflows', 'Skip learned workflow lookup').option('--max-steps <n>', 'Maximum browser actions', '120').option('--max-repairs <n>', 'Maximum model repairs', '3').option('--ephemeral', 'Do not reuse saved browser cookies/session').option('--expect-text <text>', 'Trusted final text criterion the model cannot weaken').option('--expect-url <part>', 'Trusted final URL substring the model cannot weaken').option('--ultra', 'Explicit Ultra mode: skip website and sensitive-action approvals'); }
23
+ async function makeAgent(url, options) {
24
+ const config = runtimeConfig();
25
+ ensureDataDirectory(config.dataDir);
26
+ const store = new TraceStore(join(config.dataDir, 'history.sqlite'));
27
+ try {
28
+ const vault = await new ProfileStore(options.profile ? resolve(options.profile) : join(config.dataDir, 'profile.json')).load();
29
+ const agent = new Agent({ store, provider: config.provider, budget: config.budget, vault, useWorkflows: options.workflows,
30
+ mode: options.ultra ? 'ultra' : 'normal',
31
+ confirmation: policySchema.parse(options.confirmation ?? 'sensitive'), downloadDir: join(config.dataDir, 'downloads'),
32
+ completionCriteria: [...(options.expectText ? [{ type: 'text_exists', value: options.expectText }] : []), ...(options.expectUrl ? [{ type: 'url_contains', value: options.expectUrl }] : [])],
33
+ maxSteps: z.coerce.number().int().positive().parse(options.maxSteps ?? 120), maxRepairs: z.coerce.number().int().nonnegative().parse(options.maxRepairs ?? 8),
34
+ browser: { headless: !options.headed, profileDir: options.ephemeral ? undefined : join(config.dataDir, 'browser'), allowedOrigins: [new URL(url).origin, ...options.allowOrigin ?? []], allowExternal: options.allowExternal } });
35
+ return { agent, store };
36
+ }
37
+ catch (error) {
38
+ store.close();
39
+ throw error;
40
+ }
41
+ }
42
+ function showTrace(trace) {
43
+ console.log(`\n${trace.status.toUpperCase()} ${trace.id}`);
44
+ for (const result of trace.actions)
45
+ if (result.success && result.data)
46
+ console.log(JSON.stringify(result.data, null, 2));
47
+ console.log(JSON.stringify(trace.metrics, null, 2));
48
+ if (trace.error)
49
+ console.error(trace.error);
50
+ if (trace.status !== 'completed')
51
+ process.exitCode = 1;
52
+ }
53
+ function connectEvents(agent, debug, rl) {
54
+ agent.on('event', event => { if (debug || ['PLAN', 'REPAIR', 'HUMAN', 'ERROR', 'DONE', 'CACHE', 'LOCAL'].includes(event.phase))
55
+ console.log(`[${event.phase}] ${event.message}`); });
56
+ agent.control.on('approval', async (pending) => {
57
+ if (!stdin.isTTY) {
58
+ console.error('Human approval needs a terminal or the local UI; action rejected.');
59
+ agent.control.reject();
60
+ return;
61
+ }
62
+ const reader = rl ?? createInterface({ input: stdin, output: stdout }), cancellation = new AbortController();
63
+ const onChange = () => { if (agent.control.stopped || agent.control.pending !== pending)
64
+ cancellation.abort(); };
65
+ agent.control.on('change', onChange);
66
+ try {
67
+ while (agent.control.pending === pending && !agent.control.stopped) {
68
+ const answer = (await reader.question(`${pending.reason}\n${JSON.stringify(pending.action)}\nApprove? [y/N] `, { signal: cancellation.signal })).trim();
69
+ if (answer === ':stop') {
70
+ agent.control.stop();
71
+ break;
72
+ }
73
+ if (answer === ':pause') {
74
+ agent.control.pause();
75
+ continue;
76
+ }
77
+ if (answer === ':resume') {
78
+ agent.control.resume();
79
+ continue;
80
+ }
81
+ if (agent.control.pending !== pending || agent.control.stopped)
82
+ break;
83
+ if (/^(y|yes|:approve)$/i.test(answer))
84
+ agent.control.approve();
85
+ else
86
+ agent.control.reject();
87
+ break;
88
+ }
89
+ }
90
+ catch {
91
+ if (agent.control.pending === pending && !agent.control.stopped)
92
+ agent.control.reject();
93
+ }
94
+ finally {
95
+ agent.control.off('change', onChange);
96
+ if (!rl)
97
+ reader.close();
98
+ }
99
+ });
100
+ }
101
+ async function interactive(url, options = {}) {
102
+ if (!stdin.isTTY)
103
+ throw new Error('Interactive mode requires a terminal. Use agent run or agent ui.');
104
+ const reader = createInterface({ input: stdin, output: stdout });
105
+ const start = url ?? await reader.question('Starting URL: ');
106
+ const { agent, store } = await makeAgent(start, { ...options, headed: true }).catch(error => { reader.close(); throw error; });
107
+ connectEvents(agent, !!options.debug, reader);
108
+ try {
109
+ await agent.open(start);
110
+ console.log(`FreeComputerUse · ${agent.options.provider?.name ?? 'local workflows only'}\nCommands: :pause :resume :approve :reject :stop :inspect :quit`);
111
+ while (true) {
112
+ const goal = (await reader.question('> ')).trim();
113
+ if (!goal)
114
+ continue;
115
+ if (goal === ':quit')
116
+ break;
117
+ if (goal === ':inspect') {
118
+ console.log(agent.variables.redact(agent.observer.compressor.compress(await agent.observe()).text));
119
+ continue;
120
+ }
121
+ if (goal.startsWith(':')) {
122
+ console.log('Control commands are available while a task runs, or in the local UI.');
123
+ continue;
124
+ }
125
+ // Read input while the run is active so humans can take/return control.
126
+ const handler = (line) => { if (agent.control.pending)
127
+ return; const control = line.trim(); if (control === ':pause')
128
+ agent.control.pause(); if (control === ':resume')
129
+ agent.control.resume(); if (control === ':stop')
130
+ agent.control.stop(); };
131
+ reader.on('line', handler);
132
+ try {
133
+ showTrace(await agent.run(goal));
134
+ }
135
+ finally {
136
+ reader.off('line', handler);
137
+ }
138
+ if (agent.control.stopped)
139
+ break;
140
+ }
141
+ }
142
+ finally {
143
+ reader.close();
144
+ await agent.close();
145
+ store.close();
146
+ }
147
+ }
148
+ runFlags(program.command('run <goal> <url>').description('Run a natural language browser task')).action(async (goal, url, options) => {
149
+ const { agent, store } = await makeAgent(url, options);
150
+ connectEvents(agent, !!options.debug);
151
+ const interrupt = () => agent.control.stop();
152
+ process.once('SIGINT', interrupt);
153
+ try {
154
+ showTrace(await agent.run(goal, url));
155
+ }
156
+ finally {
157
+ process.off('SIGINT', interrupt);
158
+ await agent.close();
159
+ store.close();
160
+ }
161
+ });
162
+ runFlags(program.command('open <url>').description('Open a persistent browser and enter tasks')).action(interactive);
163
+ program.command('inspect <url>').option('--region <region>', 'Form id, tag or accessible region name').option('--level <n>', '1 overview, 2 controls, 3 component', '2').option('--screenshot <file>', 'Explicit local screenshot fallback').option('--accessibility', 'Show accessibility snapshot').option('--ultra', 'Skip the website access prompt').action(async (url, options) => {
164
+ const store = new TraceStore(':memory:'), agent = new Agent({ store, mode: options.ultra ? 'ultra' : 'normal', browser: { allowedOrigins: [new URL(url).origin] } });
165
+ connectEvents(agent, false);
166
+ try {
167
+ const state = await agent.open(url);
168
+ const scoped = options.region ? await agent.observer.inspect(agent.browser.page, options.region) : state;
169
+ console.log(agent.variables.redact(options.accessibility ? await agent.observer.accessibility(agent.browser.page) : agent.observer.compressor.compress(scoped, { level: z.coerce.number().int().min(1).max(3).parse(options.level) }).text));
170
+ if (options.screenshot) {
171
+ const file = resolve(options.screenshot);
172
+ await mkdir(join(file, '..'), { recursive: true });
173
+ await agent.browser.page.screenshot({ path: file });
174
+ console.log(`Saved local screenshot: ${file}`);
175
+ }
176
+ }
177
+ finally {
178
+ await agent.close();
179
+ store.close();
180
+ }
181
+ });
182
+ runFlags(program.command('replay <id>').option('--url <url>', 'Override starting URL')).action(async (id, options) => {
183
+ const config = runtimeConfig();
184
+ ensureDataDirectory(config.dataDir);
185
+ const lookup = new TraceStore(join(config.dataDir, 'history.sqlite'));
186
+ const trace = lookup.get(id);
187
+ lookup.close();
188
+ if (!trace)
189
+ throw new Error('Run not found');
190
+ const { agent, store } = await makeAgent(options.url ?? trace.url, options);
191
+ connectEvents(agent, !!options.debug);
192
+ try {
193
+ showTrace(await agent.replay(trace, options.url));
194
+ }
195
+ finally {
196
+ await agent.close();
197
+ store.close();
198
+ }
199
+ });
200
+ program.command('history').option('--limit <n>', 'Maximum rows', '30').action(options => { const config = runtimeConfig(); ensureDataDirectory(config.dataDir); const store = new TraceStore(join(config.dataDir, 'history.sqlite')); try {
201
+ console.log(new VariableResolver().redact(JSON.stringify(store.history(z.coerce.number().int().min(1).max(1000).parse(options.limit)), null, 2)));
202
+ }
203
+ finally {
204
+ store.close();
205
+ } });
206
+ program.command('workflows').option('--show <id>', 'Show a learned semantic workflow').action(options => { const config = runtimeConfig(); ensureDataDirectory(config.dataDir); const store = new TraceStore(join(config.dataDir, 'history.sqlite')); try {
207
+ const workflows = new WorkflowEngine(store);
208
+ console.log(JSON.stringify(options.show ? workflows.get(options.show) ?? 'Not found' : workflows.list(), null, 2));
209
+ }
210
+ finally {
211
+ store.close();
212
+ } });
213
+ program.command('config').option('--profile <file>', 'Import a profile/files JSON into the local vault').action(async (options) => {
214
+ const config = runtimeConfig();
215
+ ensureDataDirectory(config.dataDir);
216
+ const profile = new ProfileStore(join(config.dataDir, 'profile.json'));
217
+ if (options.profile) {
218
+ await profile.save(JSON.parse(await readFile(resolve(options.profile), 'utf8')));
219
+ console.log('Local profile imported (mode 0600).');
220
+ }
221
+ const vault = await profile.load();
222
+ console.log(JSON.stringify({ dataDir: config.dataDir, model: config.provider?.name ?? 'not configured', apiKeyConfigured: !!process.env.LLM_API_KEY, budget: config.budget.limits, profileAliases: Object.keys(vault.profile), fileAliases: Object.keys(vault.files) }, null, 2));
223
+ });
224
+ program.command('doctor').option('--api', 'Validate provider credentials or subscription login').action(async (options) => {
225
+ const config = runtimeConfig();
226
+ console.log(`Node ${process.version}; model ${config.provider?.name ?? 'not configured'}; data ${config.dataDir}`);
227
+ const browser = new Browser();
228
+ try {
229
+ await browser.launch();
230
+ console.log('Browser launch: passed');
231
+ }
232
+ finally {
233
+ await browser.close();
234
+ }
235
+ if (options.api) {
236
+ if (!config.provider)
237
+ throw new Error('No model provider configured; set LLM_API_KEY or sign in with a supported CLI subscription');
238
+ if (config.provider instanceof CodexSubscriptionProvider) {
239
+ const version = await config.provider.checkLogin();
240
+ console.log(`Codex ChatGPT login: passed (CLI ${version})`);
241
+ return;
242
+ }
243
+ if (config.provider instanceof ClaudeSubscriptionProvider) {
244
+ const version = await config.provider.checkLogin();
245
+ console.log(`Claude subscription login: passed (CLI ${version})`);
246
+ return;
247
+ }
248
+ if (config.provider instanceof FlashProvider) {
249
+ const anthropic = config.provider.config.protocol === 'anthropic';
250
+ const response = await fetch(config.provider.config.baseURL.replace(/\/$/, '') + '/models', { headers: anthropic ? { 'x-api-key': config.provider.config.key, 'anthropic-version': '2023-06-01' } : { Authorization: `Bearer ${config.provider.config.key}` }, signal: AbortSignal.timeout(15000), redirect: 'error' });
251
+ if (!response.ok)
252
+ throw new Error(`Provider models HTTP ${response.status}`);
253
+ const data = await response.json();
254
+ console.log('Provider models:', data.data?.map(m => m.id).join(', '));
255
+ if (!data.data?.some(m => m.id === config.provider.name))
256
+ throw new Error('Configured model is not advertised by this provider');
257
+ }
258
+ }
259
+ });
260
+ program.command('ui').option('--port <n>', 'Loopback web UI port', '4318').option('--headed', 'Show Chromium alongside preview').action(async (options) => {
261
+ const { startServer } = await import('../server/index.js');
262
+ await startServer({ port: z.coerce.number().int().min(0).max(65535).parse(options.port), headed: !!options.headed });
263
+ });
264
+ program.command('mcp').description('Start the local MCP server over stdio').action(async () => {
265
+ const { serveStdio } = await import('@modelcontextprotocol/server/stdio');
266
+ const { createMcpRuntime } = await import('../mcp/index.js');
267
+ const runtime = createMcpRuntime(), handle = serveStdio(runtime.createServer);
268
+ let closing = false;
269
+ const shutdown = () => { if (closing)
270
+ return; closing = true; void handle.close().finally(() => runtime.close()); };
271
+ process.once('SIGINT', shutdown);
272
+ process.once('SIGTERM', shutdown);
273
+ stdin.once('end', shutdown);
274
+ });
275
+ program.action(() => interactive());
276
+ try {
277
+ await program.parseAsync();
278
+ }
279
+ catch (error) {
280
+ const message = error instanceof Error ? error.message : 'Command failed';
281
+ console.error(message.replace(/sk-[a-zA-Z0-9_-]{16,}/g, '[redacted key]'));
282
+ process.exitCode = 1;
283
+ }
284
+ //# sourceMappingURL=index.js.map