explorbot 0.1.32-beta.1 → 0.2.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.
- package/README.md +35 -19
- package/bin/explorbot-cli.ts +33 -10
- package/boat/api-tester/src/config.ts +45 -3
- package/boat/doc-collector/src/ai/documentarian.ts +26 -28
- package/boat/doc-collector/src/ai/tools.ts +74 -16
- package/boat/doc-collector/src/cli.ts +1 -6
- package/boat/doc-collector/src/docbot.ts +30 -9
- package/boat/doc-collector/src/docs-renderer.ts +19 -24
- package/boat/doc-collector/src/screenshots.ts +34 -2
- package/boat/doc-collector/src/state-diagram.ts +281 -0
- package/dist/bin/explorbot-cli.js +29 -9
- package/dist/boat/api-tester/src/config.js +43 -4
- package/dist/boat/doc-collector/src/ai/documentarian.js +19 -7
- package/dist/boat/doc-collector/src/ai/tools.js +53 -16
- package/dist/boat/doc-collector/src/cli.js +1 -5
- package/dist/boat/doc-collector/src/docbot.js +27 -8
- package/dist/boat/doc-collector/src/docs-renderer.js +18 -3
- package/dist/boat/doc-collector/src/screenshots.js +30 -1
- package/dist/boat/doc-collector/src/state-diagram.js +200 -0
- package/dist/models.json +30 -0
- package/dist/package.json +23 -3
- package/dist/src/action-result.d.ts +8 -10
- package/dist/src/action-result.js +72 -164
- package/dist/src/action.d.ts +13 -12
- package/dist/src/action.js +37 -113
- package/dist/src/ai/agent.d.ts +17 -0
- package/dist/src/ai/captain/idle-mode.js +1 -1
- package/dist/src/ai/captain/test-mode.js +1 -1
- package/dist/src/ai/captain/web-mode.js +18 -29
- package/dist/src/ai/captain.d.ts +1 -3
- package/dist/src/ai/captain.js +17 -45
- package/dist/src/ai/conversation.d.ts +2 -5
- package/dist/src/ai/conversation.js +9 -28
- package/dist/src/ai/driller.d.ts +2 -17
- package/dist/src/ai/driller.js +26 -50
- package/dist/src/ai/experience-compactor.d.ts +2 -2
- package/dist/src/ai/experience-compactor.js +16 -33
- package/dist/src/ai/fisherman-tools.js +1 -1
- package/dist/src/ai/fisherman.d.ts +0 -1
- package/dist/src/ai/fisherman.js +0 -3
- package/dist/src/ai/historian/codeceptjs.d.ts +2 -0
- package/dist/src/ai/historian/codeceptjs.js +4 -3
- package/dist/src/ai/historian/playwright.d.ts +4 -1
- package/dist/src/ai/historian/playwright.js +4 -3
- package/dist/src/ai/historian/screencast.d.ts +2 -1
- package/dist/src/ai/historian/screencast.js +2 -2
- package/dist/src/ai/historian/utils.d.ts +0 -1
- package/dist/src/ai/historian/utils.js +0 -1
- package/dist/src/ai/historian.d.ts +10 -8
- package/dist/src/ai/historian.js +1 -5
- package/dist/src/ai/navigator.d.ts +8 -6
- package/dist/src/ai/navigator.js +34 -65
- package/dist/src/ai/pilot.d.ts +13 -8
- package/dist/src/ai/pilot.js +53 -59
- package/dist/src/ai/planner.d.ts +5 -4
- package/dist/src/ai/planner.js +66 -52
- package/dist/src/ai/provider.d.ts +4 -1
- package/dist/src/ai/provider.js +77 -137
- package/dist/src/ai/quartermaster.d.ts +22 -22
- package/dist/src/ai/quartermaster.js +8 -14
- package/dist/src/ai/rerunner.d.ts +3 -13
- package/dist/src/ai/rerunner.js +23 -44
- package/dist/src/ai/researcher/cache.js +11 -18
- package/dist/src/ai/researcher/coordinates.js +4 -3
- package/dist/src/ai/researcher/deep-analysis.d.ts +2 -0
- package/dist/src/ai/researcher/deep-analysis.js +8 -11
- package/dist/src/ai/researcher/locators.d.ts +9 -2
- package/dist/src/ai/researcher/locators.js +65 -8
- package/dist/src/ai/researcher/parser.js +1 -1
- package/dist/src/ai/researcher/research-result.d.ts +0 -1
- package/dist/src/ai/researcher/research-result.js +0 -19
- package/dist/src/ai/researcher/sections.d.ts +2 -0
- package/dist/src/ai/researcher/sections.js +7 -3
- package/dist/src/ai/researcher.js +40 -106
- package/dist/src/ai/rules.d.ts +1 -0
- package/dist/src/ai/rules.js +22 -15
- package/dist/src/ai/session-analyst.js +8 -5
- package/dist/src/ai/task-agent.d.ts +19 -6
- package/dist/src/ai/task-agent.js +37 -33
- package/dist/src/ai/tester.d.ts +6 -14
- package/dist/src/ai/tester.js +46 -111
- package/dist/src/ai/tools.d.ts +26 -14
- package/dist/src/ai/tools.js +138 -132
- package/dist/src/command-handler.js +1 -1
- package/dist/src/commands/compact-command.d.ts +1 -1
- package/dist/src/commands/compact-command.js +6 -6
- package/dist/src/commands/context-aria-command.js +1 -1
- package/dist/src/commands/context-command.js +8 -6
- package/dist/src/commands/context-data-command.js +2 -2
- package/dist/src/commands/context-experience-command.js +2 -2
- package/dist/src/commands/context-html-command.js +2 -2
- package/dist/src/commands/context-knowledge-command.js +2 -2
- package/dist/src/commands/drill-command.js +1 -1
- package/dist/src/commands/experience-command.js +1 -1
- package/dist/src/commands/explore-command.js +4 -3
- package/dist/src/commands/freesail-command.js +2 -2
- package/dist/src/commands/knows-command.js +1 -1
- package/dist/src/commands/learn-command.js +5 -3
- package/dist/src/commands/path-command.js +1 -1
- package/dist/src/commands/research-command.js +1 -1
- package/dist/src/commands/test-command.js +1 -1
- package/dist/src/components/AddKnowledge.d.ts +2 -0
- package/dist/src/components/AddKnowledge.js +3 -7
- package/dist/src/components/App.js +3 -3
- package/dist/src/config.d.ts +19 -0
- package/dist/src/config.js +156 -10
- package/dist/src/experience-tracker.d.ts +11 -6
- package/dist/src/experience-tracker.js +82 -155
- package/dist/src/explorbot.d.ts +21 -13
- package/dist/src/explorbot.js +84 -80
- package/dist/src/explorer.d.ts +66 -102
- package/dist/src/explorer.js +332 -623
- package/dist/src/knowledge-tracker.d.ts +2 -2
- package/dist/src/knowledge-tracker.js +38 -45
- package/dist/src/state-manager.d.ts +10 -30
- package/dist/src/state-manager.js +11 -129
- package/dist/src/utils/aria.d.ts +6 -1
- package/dist/src/utils/aria.js +4 -1
- package/dist/src/utils/cache.d.ts +15 -0
- package/dist/src/utils/cache.js +34 -0
- package/dist/src/utils/context-formatter.d.ts +6 -0
- package/dist/src/utils/context-formatter.js +15 -27
- package/dist/src/utils/hooks-runner.js +2 -4
- package/dist/src/utils/html.d.ts +5 -0
- package/dist/src/utils/html.js +71 -0
- package/dist/src/utils/markdown-files.d.ts +10 -0
- package/dist/src/utils/markdown-files.js +21 -0
- package/dist/src/utils/markdown-query.d.ts +6 -0
- package/dist/src/utils/markdown-query.js +14 -1
- package/dist/src/utils/page-readiness.d.ts +1 -0
- package/dist/src/utils/page-readiness.js +1 -1
- package/dist/src/utils/secrets.js +2 -2
- package/dist/src/utils/strings.d.ts +2 -0
- package/dist/src/utils/strings.js +10 -0
- package/dist/src/utils/test-files.js +1 -1
- package/dist/src/utils/web-annotate.d.ts +5 -0
- package/dist/src/utils/web-annotate.js +58 -0
- package/dist/src/utils/web-eidx.d.ts +2 -0
- package/dist/src/utils/web-eidx.js +20 -0
- package/dist/src/utils/web-element.d.ts +3 -1
- package/dist/src/utils/web-element.js +32 -0
- package/dist/src/utils/web-sandbox.d.ts +4 -0
- package/dist/src/utils/web-sandbox.js +37 -0
- package/models.json +30 -0
- package/package.json +23 -3
- package/src/action-result.ts +70 -173
- package/src/action.ts +38 -132
- package/src/ai/agent.ts +20 -0
- package/src/ai/captain/idle-mode.ts +1 -1
- package/src/ai/captain/test-mode.ts +1 -1
- package/src/ai/captain/web-mode.ts +18 -30
- package/src/ai/captain.ts +17 -42
- package/src/ai/conversation.ts +9 -32
- package/src/ai/driller.ts +53 -83
- package/src/ai/experience-compactor.ts +16 -35
- package/src/ai/fisherman-tools.ts +1 -1
- package/src/ai/fisherman.ts +0 -4
- package/src/ai/historian/codeceptjs.ts +5 -3
- package/src/ai/historian/playwright.ts +7 -4
- package/src/ai/historian/screencast.ts +4 -3
- package/src/ai/historian/utils.ts +0 -1
- package/src/ai/historian.ts +6 -8
- package/src/ai/navigator.ts +39 -70
- package/src/ai/pilot.ts +59 -61
- package/src/ai/planner.ts +76 -62
- package/src/ai/provider.ts +85 -146
- package/src/ai/quartermaster.ts +39 -45
- package/src/ai/rerunner.ts +27 -50
- package/src/ai/researcher/cache.ts +10 -16
- package/src/ai/researcher/coordinates.ts +4 -3
- package/src/ai/researcher/deep-analysis.ts +10 -11
- package/src/ai/researcher/locators.ts +66 -9
- package/src/ai/researcher/parser.ts +1 -1
- package/src/ai/researcher/research-result.ts +0 -18
- package/src/ai/researcher/sections.ts +9 -3
- package/src/ai/researcher.ts +42 -119
- package/src/ai/rules.ts +24 -13
- package/src/ai/session-analyst.ts +8 -5
- package/src/ai/task-agent.ts +45 -38
- package/src/ai/tester.ts +51 -129
- package/src/ai/tools.ts +145 -153
- package/src/command-handler.ts +1 -1
- package/src/commands/compact-command.ts +7 -8
- package/src/commands/context-aria-command.ts +1 -1
- package/src/commands/context-command.ts +8 -6
- package/src/commands/context-data-command.ts +2 -2
- package/src/commands/context-experience-command.ts +2 -2
- package/src/commands/context-html-command.ts +2 -2
- package/src/commands/context-knowledge-command.ts +2 -2
- package/src/commands/drill-command.ts +1 -1
- package/src/commands/experience-command.ts +1 -1
- package/src/commands/explore-command.ts +4 -3
- package/src/commands/freesail-command.ts +2 -2
- package/src/commands/knows-command.ts +1 -1
- package/src/commands/learn-command.ts +5 -3
- package/src/commands/path-command.ts +1 -1
- package/src/commands/research-command.ts +1 -1
- package/src/commands/test-command.ts +1 -1
- package/src/components/AddKnowledge.tsx +5 -7
- package/src/components/App.tsx +3 -3
- package/src/config.ts +185 -11
- package/src/experience-tracker.ts +89 -152
- package/src/explorbot.ts +88 -83
- package/src/explorer.ts +402 -693
- package/src/knowledge-tracker.ts +42 -49
- package/src/state-manager.ts +20 -155
- package/src/utils/aria.ts +11 -2
- package/src/utils/cache.ts +40 -0
- package/src/utils/context-formatter.ts +12 -15
- package/src/utils/hooks-runner.ts +2 -4
- package/src/utils/html.ts +79 -0
- package/src/utils/markdown-files.ts +30 -0
- package/src/utils/markdown-query.ts +15 -1
- package/src/utils/page-readiness.ts +1 -1
- package/src/utils/secrets.ts +2 -3
- package/src/utils/strings.ts +12 -0
- package/src/utils/test-files.ts +1 -1
- package/src/utils/web-annotate.ts +64 -0
- package/src/utils/web-eidx.ts +21 -0
- package/src/utils/web-element.ts +34 -0
- package/src/utils/web-sandbox.ts +43 -0
- package/dist/boat/api-tester/example/apibot.config.js +0 -30
|
@@ -6,19 +6,21 @@ class Documentarian {
|
|
|
6
6
|
provider;
|
|
7
7
|
config;
|
|
8
8
|
explorer;
|
|
9
|
-
|
|
9
|
+
stateManager;
|
|
10
|
+
constructor(provider, config = {}, explorer, stateManager) {
|
|
10
11
|
this.provider = provider;
|
|
11
12
|
this.config = config;
|
|
12
13
|
this.explorer = explorer;
|
|
14
|
+
this.stateManager = stateManager;
|
|
13
15
|
}
|
|
14
|
-
async document(state, research) {
|
|
16
|
+
async document(state, research, captureState) {
|
|
15
17
|
const interactiveEnabled = this.config.docs?.interactive === true && this.explorer;
|
|
16
18
|
if (!interactiveEnabled) {
|
|
17
19
|
tag('info').log('Documentarian: Using static mode (interactive disabled or no explorer)');
|
|
18
20
|
return this.documentStatic(state, research);
|
|
19
21
|
}
|
|
20
22
|
tag('info').log('Documentarian: Using interactive mode with tools');
|
|
21
|
-
return this.documentWithInteraction(state, research);
|
|
23
|
+
return this.documentWithInteraction(state, research, captureState);
|
|
22
24
|
}
|
|
23
25
|
async documentStatic(state, research) {
|
|
24
26
|
try {
|
|
@@ -31,11 +33,12 @@ class Documentarian {
|
|
|
31
33
|
return this.generateDocumentation(state, this.sanitizeResearch(research), true);
|
|
32
34
|
}
|
|
33
35
|
}
|
|
34
|
-
async documentWithInteraction(state, research) {
|
|
36
|
+
async documentWithInteraction(state, research, captureState) {
|
|
37
|
+
let meaningfulInteractions = [];
|
|
35
38
|
try {
|
|
36
39
|
tag('info').log('Starting interactive exploration...');
|
|
37
|
-
const deterministicInteractions = await collectDocInteractions(this.explorer, state, research, this.config);
|
|
38
|
-
|
|
40
|
+
const deterministicInteractions = await collectDocInteractions(this.explorer, this.stateManager, state, research, this.config, captureState);
|
|
41
|
+
meaningfulInteractions = this.getMeaningfulInteractions(deterministicInteractions);
|
|
39
42
|
if (meaningfulInteractions.length > 0) {
|
|
40
43
|
tag('success').log(`Collected ${meaningfulInteractions.length} deterministic interactions`);
|
|
41
44
|
return await this.generateDocumentationWithInteractions(state, research, meaningfulInteractions);
|
|
@@ -50,7 +53,16 @@ class Documentarian {
|
|
|
50
53
|
}
|
|
51
54
|
catch (error) {
|
|
52
55
|
const message = error instanceof Error ? error.message : String(error);
|
|
53
|
-
tag('warning').log(`Interactive documentation failed: ${message}
|
|
56
|
+
tag('warning').log(`Interactive documentation failed: ${message}.`);
|
|
57
|
+
if (meaningfulInteractions.length > 0) {
|
|
58
|
+
tag('info').log(`Preserving ${meaningfulInteractions.length} observed interaction(s) without AI summary.`);
|
|
59
|
+
return this.normalizeDocumentation({
|
|
60
|
+
summary: `Observed ${meaningfulInteractions.length} interaction(s); AI-generated summary was unavailable.`,
|
|
61
|
+
can: [],
|
|
62
|
+
might: [],
|
|
63
|
+
interactions: meaningfulInteractions,
|
|
64
|
+
}, state, research);
|
|
65
|
+
}
|
|
54
66
|
return this.documentStatic(state, research);
|
|
55
67
|
}
|
|
56
68
|
}
|
|
@@ -1,23 +1,24 @@
|
|
|
1
1
|
import { parseResearchSections } from "../../../../src/ai/researcher/parser.js";
|
|
2
|
+
import { detectFocusArea } from "../../../../src/utils/aria.js";
|
|
2
3
|
const DEFAULT_MAX_PRIMARY_CANDIDATES = 3;
|
|
3
4
|
const DEFAULT_MAX_INTERACTIONS = 5;
|
|
4
5
|
const MAX_LINKS = 15;
|
|
5
6
|
const DEFAULT_WAIT_MS = 700;
|
|
6
7
|
const TAB_WAIT_MS = 500;
|
|
7
8
|
const DEFAULT_DENIED_ACTION_LABELS = ['delete', 'remove', 'destroy', 'archive', 'discard', 'logout', 'sign out', 'signout', 'sign_out', 'erase', 'drop'];
|
|
8
|
-
export async function collectDocInteractions(explorer, state, research, config = {}) {
|
|
9
|
+
export async function collectDocInteractions(explorer, stateManager, state, research, config = {}, captureState) {
|
|
9
10
|
const sections = parseResearchSections(research);
|
|
10
11
|
const transitions = [];
|
|
11
12
|
const maxInteractions = getPositiveConfigNumber(config.docs?.maxInteractions, DEFAULT_MAX_INTERACTIONS);
|
|
12
13
|
const tabGroup = findTabGroup(sections);
|
|
13
14
|
if (tabGroup) {
|
|
14
|
-
transitions.push(...(await exploreTabGroup(explorer, tabGroup, state.url, maxInteractions)));
|
|
15
|
+
transitions.push(...(await exploreTabGroup(explorer, stateManager, tabGroup, state.url, maxInteractions, captureState)));
|
|
15
16
|
}
|
|
16
17
|
for (const candidate of findActionCandidates(sections, config)) {
|
|
17
18
|
if (transitions.length >= maxInteractions) {
|
|
18
19
|
break;
|
|
19
20
|
}
|
|
20
|
-
const transition = await executeInteraction(explorer, candidate, state.url, DEFAULT_WAIT_MS);
|
|
21
|
+
const transition = await executeInteraction(explorer, stateManager, candidate, state.url, DEFAULT_WAIT_MS, captureState);
|
|
21
22
|
if (!transition) {
|
|
22
23
|
continue;
|
|
23
24
|
}
|
|
@@ -32,18 +33,18 @@ export function pickDocActionCandidates(research, config = {}) {
|
|
|
32
33
|
section: candidate.sectionName,
|
|
33
34
|
}));
|
|
34
35
|
}
|
|
35
|
-
async function exploreTabGroup(explorer, tabGroup, restoreUrl, maxInteractions) {
|
|
36
|
+
async function exploreTabGroup(explorer, stateManager, tabGroup, restoreUrl, maxInteractions, captureState) {
|
|
36
37
|
const transitions = [];
|
|
37
38
|
for (const element of tabGroup.elements) {
|
|
38
39
|
if (transitions.length >= maxInteractions) {
|
|
39
40
|
break;
|
|
40
41
|
}
|
|
41
|
-
const transition = await executeInteraction(explorer, {
|
|
42
|
+
const transition = await executeInteraction(explorer, stateManager, {
|
|
42
43
|
element,
|
|
43
44
|
container: tabGroup.container,
|
|
44
45
|
role: 'tab',
|
|
45
46
|
sectionName: tabGroup.sectionName,
|
|
46
|
-
}, restoreUrl, TAB_WAIT_MS);
|
|
47
|
+
}, restoreUrl, TAB_WAIT_MS, captureState);
|
|
47
48
|
if (!transition) {
|
|
48
49
|
continue;
|
|
49
50
|
}
|
|
@@ -52,8 +53,8 @@ async function exploreTabGroup(explorer, tabGroup, restoreUrl, maxInteractions)
|
|
|
52
53
|
await restoreInteractionState(explorer, restoreUrl, buildPrimaryCommand(tabGroup.elements[0], tabGroup.container));
|
|
53
54
|
return transitions;
|
|
54
55
|
}
|
|
55
|
-
async function executeInteraction(explorer, candidate, restoreUrl, waitMs) {
|
|
56
|
-
const beforeState =
|
|
56
|
+
async function executeInteraction(explorer, stateManager, candidate, restoreUrl, waitMs, captureState) {
|
|
57
|
+
const beforeState = stateManager.getCurrentState();
|
|
57
58
|
if (!beforeState) {
|
|
58
59
|
return null;
|
|
59
60
|
}
|
|
@@ -62,7 +63,7 @@ async function executeInteraction(explorer, candidate, restoreUrl, waitMs) {
|
|
|
62
63
|
return null;
|
|
63
64
|
}
|
|
64
65
|
await wait(waitMs);
|
|
65
|
-
const afterState =
|
|
66
|
+
const afterState = stateManager.getCurrentState();
|
|
66
67
|
if (!afterState) {
|
|
67
68
|
return null;
|
|
68
69
|
}
|
|
@@ -73,15 +74,21 @@ async function executeInteraction(explorer, candidate, restoreUrl, waitMs) {
|
|
|
73
74
|
newElements: ariaChanges.newCount,
|
|
74
75
|
removedElements: ariaChanges.removedCount,
|
|
75
76
|
});
|
|
76
|
-
if (
|
|
77
|
+
if (captureState && isMeaningfulStateTransition(transition)) {
|
|
78
|
+
const screenshot = await captureState(afterState, transition);
|
|
79
|
+
if (screenshot) {
|
|
80
|
+
transition.screenshot = screenshot;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
if (urlChanged || ariaChanges.newCount > 0) {
|
|
77
84
|
await restoreInteractionState(explorer, restoreUrl);
|
|
78
85
|
}
|
|
79
86
|
return transition;
|
|
80
87
|
}
|
|
81
88
|
async function attemptInteraction(explorer, candidate) {
|
|
82
|
-
const action = explorer.
|
|
89
|
+
const action = explorer.action();
|
|
83
90
|
for (const command of buildClickCommands(candidate.element, candidate.container)) {
|
|
84
|
-
const success = await action.attempt(command, buildPurpose(candidate)
|
|
91
|
+
const success = await action.attempt(command, buildPurpose(candidate));
|
|
85
92
|
if (success) {
|
|
86
93
|
return true;
|
|
87
94
|
}
|
|
@@ -90,15 +97,15 @@ async function attemptInteraction(explorer, candidate) {
|
|
|
90
97
|
}
|
|
91
98
|
async function restoreInteractionState(explorer, restoreUrl, primaryCommand) {
|
|
92
99
|
if (primaryCommand) {
|
|
93
|
-
const action = explorer.
|
|
94
|
-
const restored = await action.attempt(primaryCommand, `Restore initial state on ${restoreUrl}
|
|
100
|
+
const action = explorer.action();
|
|
101
|
+
const restored = await action.attempt(primaryCommand, `Restore initial state on ${restoreUrl}`);
|
|
95
102
|
if (restored) {
|
|
96
103
|
await wait(TAB_WAIT_MS);
|
|
97
104
|
return;
|
|
98
105
|
}
|
|
99
106
|
}
|
|
100
|
-
const action = explorer.
|
|
101
|
-
await action.attempt(`I.amOnPage(${JSON.stringify(restoreUrl)})`, `Restore page ${restoreUrl}
|
|
107
|
+
const action = explorer.action();
|
|
108
|
+
await action.attempt(`I.amOnPage(${JSON.stringify(restoreUrl)})`, `Restore page ${restoreUrl}`);
|
|
102
109
|
}
|
|
103
110
|
function buildTransition(candidate, beforeState, afterState, changes) {
|
|
104
111
|
const transition = {
|
|
@@ -109,12 +116,42 @@ function buildTransition(candidate, beforeState, afterState, changes) {
|
|
|
109
116
|
newCapabilities: collectDiscoveryNotes(afterState, changes),
|
|
110
117
|
element: buildInteractionElement(candidate),
|
|
111
118
|
changes,
|
|
119
|
+
targetState: describeTargetState(beforeState, afterState, candidate),
|
|
112
120
|
};
|
|
113
121
|
if (changes.urlChanged) {
|
|
114
122
|
transition.targetUrl = afterState.url;
|
|
115
123
|
}
|
|
116
124
|
return transition;
|
|
117
125
|
}
|
|
126
|
+
function describeTargetState(beforeState, afterState, candidate) {
|
|
127
|
+
const beforeFocus = detectFocusArea(beforeState.ariaSnapshot || null);
|
|
128
|
+
const afterFocus = detectFocusArea(afterState.ariaSnapshot || null);
|
|
129
|
+
if (afterFocus.detected && (!beforeFocus.detected || beforeFocus.name !== afterFocus.name)) {
|
|
130
|
+
return {
|
|
131
|
+
kind: afterFocus.type || 'dialog',
|
|
132
|
+
label: afterFocus.name || candidate.element.name.trim(),
|
|
133
|
+
url: afterState.url,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
const beforePath = beforeState.url.split('?')[0].split('#')[0];
|
|
137
|
+
const afterPath = afterState.url.split('?')[0].split('#')[0];
|
|
138
|
+
const headings = collectHeadings(afterState);
|
|
139
|
+
let kind = 'page';
|
|
140
|
+
if (beforePath === afterPath) {
|
|
141
|
+
kind = 'section';
|
|
142
|
+
}
|
|
143
|
+
return {
|
|
144
|
+
kind,
|
|
145
|
+
label: headings[0] || afterState.title || candidate.element.name.trim(),
|
|
146
|
+
url: afterState.url,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
function isMeaningfulStateTransition(transition) {
|
|
150
|
+
if (transition.targetUrl || transition.changes?.urlChanged) {
|
|
151
|
+
return true;
|
|
152
|
+
}
|
|
153
|
+
return (transition.changes?.newElements || 0) > 0;
|
|
154
|
+
}
|
|
118
155
|
function buildInteractionElement(candidate) {
|
|
119
156
|
const element = {
|
|
120
157
|
role: candidate.role,
|
|
@@ -4,10 +4,6 @@ import { Command } from 'commander';
|
|
|
4
4
|
import { setPreserveConsoleLogs } from "../../../src/utils/logger.js";
|
|
5
5
|
import { DocBot } from "./docbot.js";
|
|
6
6
|
function buildOptions(options) {
|
|
7
|
-
let session = options.session;
|
|
8
|
-
if (options.session === true) {
|
|
9
|
-
session = 'output/session.json';
|
|
10
|
-
}
|
|
11
7
|
return {
|
|
12
8
|
verbose: options.verbose || options.debug,
|
|
13
9
|
config: options.config,
|
|
@@ -15,7 +11,7 @@ function buildOptions(options) {
|
|
|
15
11
|
show: options.show,
|
|
16
12
|
headless: options.headless,
|
|
17
13
|
incognito: options.incognito,
|
|
18
|
-
session,
|
|
14
|
+
session: options.session,
|
|
19
15
|
docsConfig: options.docsConfig,
|
|
20
16
|
};
|
|
21
17
|
}
|
|
@@ -9,7 +9,8 @@ import { DocbotConfigParser } from "./config.js";
|
|
|
9
9
|
import { renderPageDocumentation, renderSpecIndex } from "./docs-renderer.js";
|
|
10
10
|
import { getDocPageKey, shouldCrawlDocPath } from "./path-filter.js";
|
|
11
11
|
import { extractResearchNavigationTargets } from "./research-navigation.js";
|
|
12
|
-
import { captureDocumentationScreenshots } from "./screenshots.js";
|
|
12
|
+
import { captureDocumentationScreenshots, captureInteractionScreenshot } from "./screenshots.js";
|
|
13
|
+
import { renderMermaidBody } from "./state-diagram.js";
|
|
13
14
|
class DocBot {
|
|
14
15
|
explorBot;
|
|
15
16
|
configParser;
|
|
@@ -38,7 +39,7 @@ class DocBot {
|
|
|
38
39
|
config: this.options.docsConfig,
|
|
39
40
|
path: this.options.path,
|
|
40
41
|
});
|
|
41
|
-
this.documentarian = new Documentarian(this.explorBot.getProvider(), this.config, this.explorBot.getExplorer());
|
|
42
|
+
this.documentarian = new Documentarian(this.explorBot.getProvider(), this.config, this.explorBot.getExplorer(), this.explorBot.stateManager());
|
|
42
43
|
this.ensureDirectory(this.configParser.getOutputDir());
|
|
43
44
|
this.ensureDirectory(this.getPagesDir());
|
|
44
45
|
}
|
|
@@ -65,7 +66,7 @@ class DocBot {
|
|
|
65
66
|
if (documented.has(targetKey)) {
|
|
66
67
|
continue;
|
|
67
68
|
}
|
|
68
|
-
const stateManager = this.explorBot.
|
|
69
|
+
const stateManager = this.explorBot.stateManager();
|
|
69
70
|
if (stateManager.hasVisitedState(target)) {
|
|
70
71
|
continue;
|
|
71
72
|
}
|
|
@@ -96,7 +97,17 @@ class DocBot {
|
|
|
96
97
|
screenshot: this.shouldUseScreenshots(),
|
|
97
98
|
force: true,
|
|
98
99
|
});
|
|
99
|
-
const
|
|
100
|
+
const pagePath = this.getPageFilePath(state.url);
|
|
101
|
+
const documentation = await this.documentarian.document(state, research, async (interactionState, transition) => {
|
|
102
|
+
if (!this.shouldUseScreenshots()) {
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
return captureInteractionScreenshot(this.explorBot.getExplorer(), interactionState, transition, {
|
|
106
|
+
pageFilePath: pagePath,
|
|
107
|
+
screenshotsDir: this.getScreenshotsDir(),
|
|
108
|
+
config: this.config,
|
|
109
|
+
});
|
|
110
|
+
});
|
|
100
111
|
const lowSignalReason = this.getLowSignalReason(documentation, research);
|
|
101
112
|
if (lowSignalReason) {
|
|
102
113
|
skipped.push({
|
|
@@ -118,6 +129,7 @@ class DocBot {
|
|
|
118
129
|
mightActions: documentation.might.map((item) => item.action),
|
|
119
130
|
interactionActions: (documentation.interactions || []).map((item) => item.action),
|
|
120
131
|
qualityNotes: documentation.qualityNotes || [],
|
|
132
|
+
interactions: documentation.interactions || [],
|
|
121
133
|
filePath,
|
|
122
134
|
});
|
|
123
135
|
documented.add(pageKey);
|
|
@@ -142,11 +154,12 @@ class DocBot {
|
|
|
142
154
|
});
|
|
143
155
|
}
|
|
144
156
|
}
|
|
145
|
-
const indexPath = this.saveIndex(effectiveStartPath, pages, skipped, effectiveMaxPages);
|
|
157
|
+
const { indexPath, diagramPath } = this.saveIndex(effectiveStartPath, pages, skipped, effectiveMaxPages);
|
|
146
158
|
return {
|
|
147
159
|
pages,
|
|
148
160
|
skipped,
|
|
149
161
|
indexPath,
|
|
162
|
+
diagramPath,
|
|
150
163
|
outputDir: this.configParser.getOutputDir(),
|
|
151
164
|
};
|
|
152
165
|
}
|
|
@@ -338,6 +351,9 @@ class DocBot {
|
|
|
338
351
|
if (documentation.can.length >= minCanActions) {
|
|
339
352
|
return null;
|
|
340
353
|
}
|
|
354
|
+
if ((documentation.interactions || []).length > 0) {
|
|
355
|
+
return null;
|
|
356
|
+
}
|
|
341
357
|
const interactiveCount = this.countInteractiveElements(research);
|
|
342
358
|
if (interactiveCount >= minInteractiveElements) {
|
|
343
359
|
return null;
|
|
@@ -365,9 +381,12 @@ class DocBot {
|
|
|
365
381
|
});
|
|
366
382
|
}
|
|
367
383
|
saveIndex(startPath, pages, skipped, maxPages) {
|
|
368
|
-
const
|
|
369
|
-
|
|
370
|
-
|
|
384
|
+
const outputDir = this.configParser.getOutputDir();
|
|
385
|
+
const indexPath = path.join(outputDir, 'index.md');
|
|
386
|
+
writeFileSync(indexPath, renderSpecIndex(outputDir, startPath, pages, skipped, maxPages), 'utf8');
|
|
387
|
+
const diagramPath = path.join(outputDir, 'state-diagram.mmd');
|
|
388
|
+
writeFileSync(diagramPath, renderMermaidBody(outputDir, pages), 'utf8');
|
|
389
|
+
return { indexPath, diagramPath };
|
|
371
390
|
}
|
|
372
391
|
getPagesDir() {
|
|
373
392
|
return path.join(this.configParser.getOutputDir(), 'pages');
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
|
+
import { buildStateGraph, renderMermaidFromGraph, renderStateMapFromGraph } from "./state-diagram.js";
|
|
3
|
+
import { normalizeInlineText } from "../../../src/utils/strings.js";
|
|
2
4
|
function renderPageDocumentation(state, documentation, screenshots = []) {
|
|
3
5
|
const lines = [];
|
|
4
6
|
lines.push(`# ${state.url}`);
|
|
@@ -40,6 +42,10 @@ function renderPageDocumentation(state, documentation, screenshots = []) {
|
|
|
40
42
|
}
|
|
41
43
|
lines.push('');
|
|
42
44
|
}
|
|
45
|
+
if (transition.screenshot) {
|
|
46
|
+
lines.push(``);
|
|
47
|
+
lines.push('');
|
|
48
|
+
}
|
|
43
49
|
}
|
|
44
50
|
}
|
|
45
51
|
lines.push('## User Can');
|
|
@@ -90,6 +96,18 @@ function renderSpecIndex(outputDir, startPath, pages, skipped, maxPages) {
|
|
|
90
96
|
lines.push(`Pages skipped: ${skipped.length}`);
|
|
91
97
|
lines.push(`Max pages: ${maxPages}`);
|
|
92
98
|
lines.push('');
|
|
99
|
+
const graph = buildStateGraph(outputDir, pages);
|
|
100
|
+
lines.push('## State Transitions');
|
|
101
|
+
lines.push('');
|
|
102
|
+
lines.push(`\`\`\`mermaid\n${renderMermaidFromGraph(graph)}\n\`\`\``);
|
|
103
|
+
lines.push('');
|
|
104
|
+
const stateMap = renderStateMapFromGraph(graph);
|
|
105
|
+
if (stateMap) {
|
|
106
|
+
lines.push('### State Index');
|
|
107
|
+
lines.push('');
|
|
108
|
+
lines.push(stateMap);
|
|
109
|
+
lines.push('');
|
|
110
|
+
}
|
|
93
111
|
lines.push('## Pages');
|
|
94
112
|
lines.push('');
|
|
95
113
|
if (pages.length === 0) {
|
|
@@ -192,7 +210,4 @@ function ensureSentence(text) {
|
|
|
192
210
|
}
|
|
193
211
|
return `${trimmed}.`;
|
|
194
212
|
}
|
|
195
|
-
function normalizeInlineText(text) {
|
|
196
|
-
return text.normalize('NFKC').replace(/\s+/g, ' ').trim();
|
|
197
|
-
}
|
|
198
213
|
export { renderPageDocumentation, renderSpecIndex, ensureSentence, normalizeAction };
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { mkdirSync } from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { parseResearchSections } from "../../../src/ai/researcher/parser.js";
|
|
4
|
+
import { detectFocusArea } from "../../../src/utils/aria.js";
|
|
4
5
|
import { safeFilename, sanitizeFilename } from "../../../src/utils/strings.js";
|
|
5
6
|
const DEFAULT_MAX_SECTION_SCREENSHOTS = 8;
|
|
6
7
|
export async function captureDocumentationScreenshots(explorer, state, research, options) {
|
|
7
|
-
const page = explorer.
|
|
8
|
+
const page = explorer.page;
|
|
8
9
|
if (!page) {
|
|
9
10
|
return [];
|
|
10
11
|
}
|
|
@@ -46,6 +47,34 @@ export function getScreenshotSections(research) {
|
|
|
46
47
|
}
|
|
47
48
|
return sections;
|
|
48
49
|
}
|
|
50
|
+
export async function captureInteractionScreenshot(explorer, state, transition, options) {
|
|
51
|
+
const page = explorer.page;
|
|
52
|
+
if (!page) {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
mkdirSync(options.screenshotsDir, { recursive: true });
|
|
56
|
+
const pageName = sanitizeFilename(state.url || 'page') || 'page';
|
|
57
|
+
const stateName = sanitizeFilename(transition.targetState?.label || transition.action) || 'state';
|
|
58
|
+
const filePath = path.join(options.screenshotsDir, safeFilename(`${pageName}_${stateName}`, '.png'));
|
|
59
|
+
const focus = detectFocusArea(state.ariaSnapshot || null);
|
|
60
|
+
try {
|
|
61
|
+
if (focus.detected) {
|
|
62
|
+
await page.locator('[role="dialog"], [role="alertdialog"], [aria-modal="true"]').last().screenshot({ path: filePath });
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
await page.screenshot({ path: filePath });
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
return {
|
|
72
|
+
title: transition.targetState?.label || transition.action,
|
|
73
|
+
path: filePath,
|
|
74
|
+
relativePath: toMarkdownPath(options.pageFilePath, filePath),
|
|
75
|
+
kind: 'state',
|
|
76
|
+
};
|
|
77
|
+
}
|
|
49
78
|
async function captureFullPageScreenshot(page, pageName, options) {
|
|
50
79
|
const filePath = path.join(options.screenshotsDir, safeFilename(`${pageName}_page`, '.png'));
|
|
51
80
|
try {
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { normalizeUrl } from "../../../src/state-manager.js";
|
|
3
|
+
import { normalizeInlineText } from "../../../src/utils/strings.js";
|
|
4
|
+
function buildStateGraph(outputDir, pages) {
|
|
5
|
+
const pageIds = new Map();
|
|
6
|
+
const pageNodes = [];
|
|
7
|
+
const adjacency = new Map();
|
|
8
|
+
const classAssignment = new Map([
|
|
9
|
+
['page', []],
|
|
10
|
+
['dialog', []],
|
|
11
|
+
['section', []],
|
|
12
|
+
]);
|
|
13
|
+
for (const [index, page] of pages.entries()) {
|
|
14
|
+
const id = `page${index}`;
|
|
15
|
+
pageIds.set(normalizeUrl(page.url), id);
|
|
16
|
+
adjacency.set(id, new Set());
|
|
17
|
+
classAssignment.get('page')?.push(id);
|
|
18
|
+
pageNodes.push({ id, kind: 'page', label: page.title || page.url, subLabel: page.url, filePath: page.filePath });
|
|
19
|
+
}
|
|
20
|
+
const stateKeys = new Map();
|
|
21
|
+
const transientByPage = new Map();
|
|
22
|
+
const clicks = [];
|
|
23
|
+
const edges = [];
|
|
24
|
+
const drawnBack = new Set();
|
|
25
|
+
let stateIndex = 0;
|
|
26
|
+
for (const [pageIndex, page] of pages.entries()) {
|
|
27
|
+
const sourceId = `page${pageIndex}`;
|
|
28
|
+
for (const transition of page.interactions || []) {
|
|
29
|
+
const targetState = transition.targetState;
|
|
30
|
+
if (!targetState) {
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
const normalizedTarget = normalizeUrl(targetState.url);
|
|
34
|
+
const isPageTarget = targetState.kind === 'page';
|
|
35
|
+
let pageTargetId;
|
|
36
|
+
if (isPageTarget) {
|
|
37
|
+
pageTargetId = pageIds.get(normalizedTarget);
|
|
38
|
+
}
|
|
39
|
+
let targetId;
|
|
40
|
+
if (pageTargetId && pageTargetId !== sourceId) {
|
|
41
|
+
targetId = pageTargetId;
|
|
42
|
+
}
|
|
43
|
+
if (!targetId) {
|
|
44
|
+
const stateKey = `${sourceId}:${targetState.kind}:${normalizeInlineText(targetState.label)}:${normalizedTarget}`;
|
|
45
|
+
targetId = stateKeys.get(stateKey);
|
|
46
|
+
if (!targetId) {
|
|
47
|
+
targetId = `state${stateIndex++}`;
|
|
48
|
+
stateKeys.set(stateKey, targetId);
|
|
49
|
+
adjacency.set(targetId, new Set());
|
|
50
|
+
classAssignment.get(classForKind(targetState.kind))?.push(targetId);
|
|
51
|
+
const list = transientByPage.get(sourceId) ?? [];
|
|
52
|
+
list.push({ id: targetId, kind: targetState.kind, label: targetState.label, subLabel: targetState.kind, parentPageId: sourceId });
|
|
53
|
+
transientByPage.set(sourceId, list);
|
|
54
|
+
if (transition.screenshot) {
|
|
55
|
+
const screenshotPath = path.resolve(path.dirname(page.filePath), transition.screenshot.relativePath);
|
|
56
|
+
clicks.push({ node: targetId, target: path.relative(outputDir, screenshotPath).replaceAll('\\', '/'), tooltip: 'Open state screenshot' });
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if (!targetId) {
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
const pairKey = `${sourceId}>${targetId}`;
|
|
64
|
+
if (adjacency.get(targetId)?.has(sourceId)) {
|
|
65
|
+
if (drawnBack.has(pairKey)) {
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
drawnBack.add(pairKey);
|
|
69
|
+
edges.push({ source: sourceId, target: targetId, action: transition.action, isBack: true });
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
if (adjacency.get(sourceId)?.has(targetId) || createsCycle(sourceId, targetId, adjacency)) {
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
adjacency.get(sourceId)?.add(targetId);
|
|
76
|
+
edges.push({ source: sourceId, target: targetId, action: transition.action, isBack: false });
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
const pageClicks = [];
|
|
80
|
+
for (const pageNode of pageNodes) {
|
|
81
|
+
const filePath = pageNode.filePath;
|
|
82
|
+
if (!filePath) {
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
const relativeFile = path.relative(outputDir, filePath).replaceAll('\\', '/');
|
|
86
|
+
pageClicks.push({ node: pageNode.id, target: relativeFile, tooltip: `Open ${pageNode.label}` });
|
|
87
|
+
}
|
|
88
|
+
return { pages: pageNodes, transientByPage, edges, clicks: [...pageClicks, ...clicks], classAssignment };
|
|
89
|
+
}
|
|
90
|
+
function renderMermaidBody(outputDir, pages) {
|
|
91
|
+
return renderMermaidFromGraph(buildStateGraph(outputDir, pages));
|
|
92
|
+
}
|
|
93
|
+
function renderMermaidFromGraph(graph) {
|
|
94
|
+
const lines = ['flowchart TD'];
|
|
95
|
+
if (graph.pages.length === 0) {
|
|
96
|
+
lines.push(' empty["No documented states"]');
|
|
97
|
+
return lines.join('\n');
|
|
98
|
+
}
|
|
99
|
+
for (const page of graph.pages) {
|
|
100
|
+
lines.push(` ${renderNodeLine(page)}`);
|
|
101
|
+
const children = graph.transientByPage.get(page.id);
|
|
102
|
+
if (!children || children.length === 0) {
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
lines.push(` subgraph sg_${page.id} ["${escapeMermaidLabel(page.label)} — transient states"]`);
|
|
106
|
+
for (const child of children) {
|
|
107
|
+
lines.push(` ${renderNodeLine(child)}`);
|
|
108
|
+
}
|
|
109
|
+
lines.push(' end');
|
|
110
|
+
}
|
|
111
|
+
for (const edge of graph.edges) {
|
|
112
|
+
let arrow = '-->';
|
|
113
|
+
if (edge.isBack) {
|
|
114
|
+
arrow = '-.->';
|
|
115
|
+
}
|
|
116
|
+
lines.push(` ${edge.source} ${arrow}|"${escapeMermaidLabel(edge.action)}"| ${edge.target}`);
|
|
117
|
+
}
|
|
118
|
+
lines.push(' classDef page fill:#dbeafe,stroke:#2563eb,color:#0f172a;');
|
|
119
|
+
lines.push(' classDef dialog fill:#ffedd5,stroke:#ea580c,color:#0f172a;');
|
|
120
|
+
lines.push(' classDef section fill:#f3e8ff,stroke:#9333ea,color:#0f172a;');
|
|
121
|
+
for (const [className, ids] of graph.classAssignment) {
|
|
122
|
+
if (ids.length === 0) {
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
lines.push(` class ${ids.join(',')} ${className};`);
|
|
126
|
+
}
|
|
127
|
+
for (const click of graph.clicks) {
|
|
128
|
+
lines.push(` click ${click.node} "${click.target}" "${escapeMermaidLabel(click.tooltip)}"`);
|
|
129
|
+
}
|
|
130
|
+
return lines.join('\n');
|
|
131
|
+
}
|
|
132
|
+
function renderStateMapFromGraph(graph) {
|
|
133
|
+
if (graph.pages.length === 0) {
|
|
134
|
+
return '';
|
|
135
|
+
}
|
|
136
|
+
const clickByNode = new Map(graph.clicks.map((click) => [click.node, click]));
|
|
137
|
+
const rows = ['| State | Type | Open |', '| --- | --- | --- |'];
|
|
138
|
+
for (const pageNode of graph.pages) {
|
|
139
|
+
const pageClick = clickByNode.get(pageNode.id);
|
|
140
|
+
let openCell = '—';
|
|
141
|
+
if (pageClick) {
|
|
142
|
+
openCell = `[open page](${pageClick.target})`;
|
|
143
|
+
}
|
|
144
|
+
rows.push(`| ${escapeTable(pageNode.label)} | page | ${openCell} |`);
|
|
145
|
+
for (const child of graph.transientByPage.get(pageNode.id) ?? []) {
|
|
146
|
+
const childClick = clickByNode.get(child.id);
|
|
147
|
+
let childCell = '—';
|
|
148
|
+
if (childClick) {
|
|
149
|
+
childCell = `[view screenshot](${childClick.target})`;
|
|
150
|
+
}
|
|
151
|
+
rows.push(`| ${escapeTable(child.label)} | ${child.kind} | ${childCell} |`);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return rows.join('\n');
|
|
155
|
+
}
|
|
156
|
+
function renderNodeLine(node) {
|
|
157
|
+
const label = `${escapeMermaidLabel(node.label)}<br/>${escapeMermaidLabel(node.subLabel)}`;
|
|
158
|
+
if (node.kind === 'dialog' || node.kind === 'modal') {
|
|
159
|
+
return `${node.id}{{"${label}"}}`;
|
|
160
|
+
}
|
|
161
|
+
if (node.kind === 'section') {
|
|
162
|
+
return `${node.id}("${label}")`;
|
|
163
|
+
}
|
|
164
|
+
return `${node.id}["${label}"]`;
|
|
165
|
+
}
|
|
166
|
+
function createsCycle(sourceId, targetId, adjacency) {
|
|
167
|
+
if (sourceId === targetId) {
|
|
168
|
+
return true;
|
|
169
|
+
}
|
|
170
|
+
const pending = [targetId];
|
|
171
|
+
const visited = new Set();
|
|
172
|
+
while (pending.length > 0) {
|
|
173
|
+
const nodeId = pending.pop();
|
|
174
|
+
if (!nodeId || visited.has(nodeId)) {
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
if (nodeId === sourceId) {
|
|
178
|
+
return true;
|
|
179
|
+
}
|
|
180
|
+
visited.add(nodeId);
|
|
181
|
+
pending.push(...(adjacency.get(nodeId) || []));
|
|
182
|
+
}
|
|
183
|
+
return false;
|
|
184
|
+
}
|
|
185
|
+
function escapeMermaidLabel(value) {
|
|
186
|
+
return normalizeInlineText(value).replaceAll('&', '&').replaceAll('"', '"').replaceAll('|', '|');
|
|
187
|
+
}
|
|
188
|
+
function escapeTable(value) {
|
|
189
|
+
return normalizeInlineText(value).replaceAll('|', '\\|');
|
|
190
|
+
}
|
|
191
|
+
function classForKind(kind) {
|
|
192
|
+
if (kind === 'section') {
|
|
193
|
+
return 'section';
|
|
194
|
+
}
|
|
195
|
+
if (kind === 'page') {
|
|
196
|
+
return 'page';
|
|
197
|
+
}
|
|
198
|
+
return 'dialog';
|
|
199
|
+
}
|
|
200
|
+
export { buildStateGraph, renderMermaidBody, renderMermaidFromGraph, renderStateMapFromGraph };
|
package/dist/models.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"openrouter": {
|
|
3
|
+
"model": "openai/gpt-oss-20b:nitro",
|
|
4
|
+
"visionModel": "google/gemma-4-31b-it:nitro",
|
|
5
|
+
"agenticModel": "google/gemma-4-31b-it:nitro"
|
|
6
|
+
},
|
|
7
|
+
"groq": {
|
|
8
|
+
"model": "openai/gpt-oss-20b",
|
|
9
|
+
"visionModel": "qwen/qwen3.6-27b",
|
|
10
|
+
"agenticModel": "qwen/qwen3.6-27b"
|
|
11
|
+
},
|
|
12
|
+
"openai": {
|
|
13
|
+
"model": "gpt-5.4-nano",
|
|
14
|
+
"visionModel": "gpt-5.4-nano",
|
|
15
|
+
"agenticModel": "gpt-5.6-luna"
|
|
16
|
+
},
|
|
17
|
+
"anthropic": {
|
|
18
|
+
"agenticModel": "claude-haiku-4-5-20251001"
|
|
19
|
+
},
|
|
20
|
+
"mistral": {
|
|
21
|
+
"model": "mistral-small-latest",
|
|
22
|
+
"visionModel": "mistral-small-latest",
|
|
23
|
+
"agenticModel": "mistral-large-latest"
|
|
24
|
+
},
|
|
25
|
+
"google": {
|
|
26
|
+
"model": "gemini-3.1-flash-lite",
|
|
27
|
+
"visionModel": "gemini-3.1-flash-lite",
|
|
28
|
+
"agenticModel": "gemini-3.5-flash"
|
|
29
|
+
}
|
|
30
|
+
}
|