screenhand 0.1.1 → 0.3.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.
- package/README.md +193 -109
- package/bin/darwin-arm64/macos-bridge +0 -0
- package/dist/mcp-desktop.js +5876 -0
- package/dist/scripts/codex-monitor-daemon.js +335 -0
- package/dist/scripts/export-help-center.js +112 -0
- package/dist/scripts/marketing-loop.js +117 -0
- package/dist/scripts/observer-daemon.js +288 -0
- package/dist/scripts/orchestrator-daemon.js +399 -0
- package/dist/scripts/supervisor-daemon.js +272 -0
- package/dist/scripts/threads-campaign.js +208 -0
- package/dist/scripts/worker-daemon.js +228 -0
- package/dist/src/agent/cli.js +82 -0
- package/dist/src/agent/loop.js +274 -0
- package/dist/src/community/fetcher.js +109 -0
- package/dist/src/community/index.js +6 -0
- package/dist/src/community/publisher.js +191 -0
- package/dist/src/community/remote-api.js +121 -0
- package/dist/src/community/types.js +3 -0
- package/dist/src/community/validator.js +95 -0
- package/{src/config.ts → dist/src/config.js} +5 -10
- package/dist/src/context-tracker.js +489 -0
- package/{src/index.ts → dist/src/index.js} +32 -52
- package/dist/src/ingestion/coverage-auditor.js +233 -0
- package/dist/src/ingestion/doc-parser.js +164 -0
- package/dist/src/ingestion/index.js +8 -0
- package/dist/src/ingestion/menu-scanner.js +152 -0
- package/dist/src/ingestion/reference-merger.js +186 -0
- package/dist/src/ingestion/shortcut-extractor.js +180 -0
- package/dist/src/ingestion/tutorial-extractor.js +170 -0
- package/dist/src/ingestion/types.js +3 -0
- package/dist/src/jobs/manager.js +305 -0
- package/dist/src/jobs/runner.js +806 -0
- package/dist/src/jobs/store.js +102 -0
- package/dist/src/jobs/types.js +30 -0
- package/dist/src/jobs/worker.js +97 -0
- package/dist/src/learning/engine.js +356 -0
- package/dist/src/learning/index.js +9 -0
- package/dist/src/learning/locator-policy.js +120 -0
- package/dist/src/learning/pattern-policy.js +89 -0
- package/dist/src/learning/recovery-policy.js +116 -0
- package/dist/src/learning/sensor-policy.js +115 -0
- package/dist/src/learning/timing-model.js +204 -0
- package/dist/src/learning/topology-policy.js +90 -0
- package/dist/src/learning/types.js +9 -0
- package/dist/src/logging/timeline-logger.js +48 -0
- package/dist/src/mcp/mcp-stdio-server.js +464 -0
- package/dist/src/mcp/server.js +363 -0
- package/dist/src/mcp-entry.js +60 -0
- package/dist/src/memory/playbook-seeds.js +200 -0
- package/dist/src/memory/recall.js +222 -0
- package/dist/src/memory/research.js +104 -0
- package/dist/src/memory/seeds.js +101 -0
- package/dist/src/memory/service.js +446 -0
- package/dist/src/memory/session.js +169 -0
- package/dist/src/memory/store.js +451 -0
- package/{src/runtime/locator-cache.ts → dist/src/memory/types.js} +1 -17
- package/dist/src/monitor/codex-monitor.js +382 -0
- package/dist/src/monitor/task-queue.js +97 -0
- package/dist/src/monitor/types.js +62 -0
- package/dist/src/native/bridge-client.js +412 -0
- package/{src/native/macos-bridge-client.ts → dist/src/native/macos-bridge-client.js} +0 -1
- package/dist/src/observer/state.js +199 -0
- package/dist/src/observer/types.js +43 -0
- package/dist/src/orchestrator/state.js +68 -0
- package/dist/src/orchestrator/types.js +22 -0
- package/dist/src/perception/ax-source.js +162 -0
- package/dist/src/perception/cdp-source.js +162 -0
- package/dist/src/perception/coordinator.js +771 -0
- package/dist/src/perception/frame-differ.js +287 -0
- package/dist/src/perception/index.js +22 -0
- package/dist/src/perception/manager.js +199 -0
- package/dist/src/perception/types.js +47 -0
- package/dist/src/perception/vision-source.js +399 -0
- package/dist/src/planner/deterministic.js +298 -0
- package/dist/src/planner/executor.js +870 -0
- package/dist/src/planner/goal-store.js +92 -0
- package/dist/src/planner/index.js +21 -0
- package/dist/src/planner/planner.js +520 -0
- package/dist/src/planner/tool-registry.js +71 -0
- package/dist/src/planner/types.js +22 -0
- package/dist/src/platform/explorer.js +213 -0
- package/dist/src/platform/help-center-markdown.js +527 -0
- package/dist/src/platform/learner.js +257 -0
- package/dist/src/playbook/engine.js +486 -0
- package/dist/src/playbook/index.js +20 -0
- package/dist/src/playbook/mcp-recorder.js +204 -0
- package/dist/src/playbook/recorder.js +536 -0
- package/dist/src/playbook/runner.js +408 -0
- package/dist/src/playbook/store.js +312 -0
- package/dist/src/playbook/types.js +17 -0
- package/dist/src/recovery/detectors.js +156 -0
- package/dist/src/recovery/engine.js +327 -0
- package/dist/src/recovery/index.js +20 -0
- package/dist/src/recovery/strategies.js +274 -0
- package/dist/src/recovery/types.js +20 -0
- package/dist/src/runtime/accessibility-adapter.js +430 -0
- package/dist/src/runtime/app-adapter.js +64 -0
- package/dist/src/runtime/applescript-adapter.js +305 -0
- package/dist/src/runtime/ax-role-map.js +96 -0
- package/dist/src/runtime/browser-adapter.js +52 -0
- package/dist/src/runtime/cdp-chrome-adapter.js +521 -0
- package/dist/src/runtime/composite-adapter.js +221 -0
- package/dist/src/runtime/execution-contract.js +159 -0
- package/dist/src/runtime/executor.js +286 -0
- package/dist/src/runtime/locator-cache.js +50 -0
- package/dist/src/runtime/planning-loop.js +63 -0
- package/dist/src/runtime/service.js +432 -0
- package/dist/src/runtime/session-manager.js +63 -0
- package/dist/src/runtime/state-observer.js +121 -0
- package/dist/src/runtime/vision-adapter.js +225 -0
- package/dist/src/state/app-map-types.js +72 -0
- package/dist/src/state/app-map.js +1974 -0
- package/dist/src/state/entity-tracker.js +108 -0
- package/dist/src/state/fusion.js +96 -0
- package/dist/src/state/index.js +21 -0
- package/dist/src/state/ladder-generator.js +236 -0
- package/dist/src/state/persistence.js +156 -0
- package/dist/src/state/types.js +17 -0
- package/dist/src/state/world-model.js +1456 -0
- package/dist/src/supervisor/locks.js +186 -0
- package/dist/src/supervisor/supervisor.js +403 -0
- package/dist/src/supervisor/types.js +30 -0
- package/dist/src/test-mcp-protocol.js +154 -0
- package/dist/src/types.js +17 -0
- package/dist/src/util/atomic-write.js +133 -0
- package/dist/src/util/sanitize.js +146 -0
- package/dist-app-maps/com.figma.Desktop.json +959 -0
- package/dist-app-maps/com.hnc.Discord.json +1146 -0
- package/dist-app-maps/notion.id.json +2831 -0
- package/dist-playbooks/canva-screenhand-carousel.json +445 -0
- package/dist-playbooks/codex-desktop.json +76 -0
- package/dist-playbooks/competitor-research-stack.json +122 -0
- package/dist-playbooks/davinci-color-grade.json +153 -0
- package/dist-playbooks/davinci-edit-timeline.json +162 -0
- package/dist-playbooks/davinci-render.json +114 -0
- package/dist-playbooks/devto.json +52 -0
- package/dist-playbooks/discord.json +41 -0
- package/dist-playbooks/google-flow-create-project.json +59 -0
- package/dist-playbooks/google-flow-edit-image.json +90 -0
- package/dist-playbooks/google-flow-edit-video.json +90 -0
- package/dist-playbooks/google-flow-generate-image.json +68 -0
- package/dist-playbooks/google-flow-generate-video.json +191 -0
- package/dist-playbooks/google-flow-open-project.json +48 -0
- package/dist-playbooks/google-flow-open-scenebuilder.json +64 -0
- package/dist-playbooks/google-flow-search-assets.json +64 -0
- package/dist-playbooks/instagram.json +57 -0
- package/dist-playbooks/linkedin.json +52 -0
- package/dist-playbooks/n8n.json +43 -0
- package/dist-playbooks/reddit.json +52 -0
- package/dist-playbooks/threads.json +59 -0
- package/dist-playbooks/x-twitter.json +59 -0
- package/dist-playbooks/youtube.json +59 -0
- package/dist-references/canva.json +646 -0
- package/dist-references/codex-desktop.json +305 -0
- package/dist-references/davinci-resolve-keyboard.json +594 -0
- package/dist-references/davinci-resolve-menu-map.json +1139 -0
- package/dist-references/davinci-resolve-menus-batch1.json +116 -0
- package/dist-references/davinci-resolve-menus-batch2.json +372 -0
- package/dist-references/davinci-resolve-menus-batch3.json +330 -0
- package/dist-references/davinci-resolve-menus-batch4.json +297 -0
- package/dist-references/davinci-resolve-shortcuts.json +333 -0
- package/dist-references/devto.json +317 -0
- package/dist-references/discord.json +549 -0
- package/dist-references/figma.json +1186 -0
- package/dist-references/finder.json +146 -0
- package/dist-references/google-ads-transparency.json +95 -0
- package/dist-references/google-flow.json +649 -0
- package/dist-references/instagram.json +341 -0
- package/dist-references/linkedin.json +324 -0
- package/dist-references/meta-ad-library.json +86 -0
- package/dist-references/n8n.json +387 -0
- package/dist-references/notes.json +27 -0
- package/dist-references/notion.json +163 -0
- package/dist-references/reddit.json +341 -0
- package/dist-references/threads.json +337 -0
- package/dist-references/x-twitter.json +403 -0
- package/dist-references/youtube.json +373 -0
- package/native/macos-bridge/Package.swift +1 -0
- package/native/macos-bridge/Sources/AccessibilityBridge.swift +257 -36
- package/native/macos-bridge/Sources/AppManagement.swift +212 -2
- package/native/macos-bridge/Sources/CoreGraphicsBridge.swift +348 -53
- package/native/macos-bridge/Sources/StreamCapture.swift +136 -0
- package/native/macos-bridge/Sources/VisionBridge.swift +165 -7
- package/native/macos-bridge/Sources/main.swift +169 -16
- package/native/windows-bridge/Program.cs +5 -0
- package/native/windows-bridge/ScreenCapture.cs +124 -0
- package/package.json +29 -4
- package/scripts/postinstall.cjs +127 -0
- package/.claude/commands/automate.md +0 -28
- package/.claude/commands/debug-ui.md +0 -19
- package/.claude/commands/screenshot.md +0 -15
- package/.github/FUNDING.yml +0 -1
- package/.github/ISSUE_TEMPLATE/bug_report.md +0 -27
- package/.github/ISSUE_TEMPLATE/feature_request.md +0 -20
- package/.mcp.json +0 -8
- package/DESKTOP_MCP_GUIDE.md +0 -92
- package/SECURITY.md +0 -44
- package/docs/architecture.md +0 -47
- package/install-skills.sh +0 -19
- package/mcp-bridge.ts +0 -271
- package/mcp-desktop.ts +0 -1221
- package/playbooks/instagram.json +0 -41
- package/playbooks/instagram_v2.json +0 -201
- package/playbooks/x_v1.json +0 -211
- package/scripts/devpost-live-loop.mjs +0 -421
- package/src/logging/timeline-logger.ts +0 -55
- package/src/mcp/server.ts +0 -449
- package/src/memory/recall.ts +0 -191
- package/src/memory/research.ts +0 -146
- package/src/memory/seeds.ts +0 -123
- package/src/memory/session.ts +0 -201
- package/src/memory/store.ts +0 -434
- package/src/memory/types.ts +0 -69
- package/src/native/bridge-client.ts +0 -239
- package/src/runtime/accessibility-adapter.ts +0 -487
- package/src/runtime/app-adapter.ts +0 -169
- package/src/runtime/applescript-adapter.ts +0 -376
- package/src/runtime/ax-role-map.ts +0 -102
- package/src/runtime/browser-adapter.ts +0 -129
- package/src/runtime/cdp-chrome-adapter.ts +0 -676
- package/src/runtime/composite-adapter.ts +0 -274
- package/src/runtime/executor.ts +0 -396
- package/src/runtime/planning-loop.ts +0 -81
- package/src/runtime/service.ts +0 -448
- package/src/runtime/session-manager.ts +0 -50
- package/src/runtime/state-observer.ts +0 -136
- package/src/runtime/vision-adapter.ts +0 -297
- package/src/types.ts +0 -297
- package/tests/bridge-client.test.ts +0 -176
- package/tests/browser-stealth.test.ts +0 -210
- package/tests/composite-adapter.test.ts +0 -64
- package/tests/mcp-server.test.ts +0 -151
- package/tests/memory-recall.test.ts +0 -339
- package/tests/memory-research.test.ts +0 -159
- package/tests/memory-seeds.test.ts +0 -120
- package/tests/memory-store.test.ts +0 -392
- package/tests/types.test.ts +0 -92
- package/tsconfig.check.json +0 -17
- package/tsconfig.json +0 -19
- package/vitest.config.ts +0 -8
- /package/{playbooks → dist-references}/devpost.json +0 -0
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
// Copyright (C) 2025 Clazro Technology Private Limited
|
|
2
|
+
// SPDX-License-Identifier: AGPL-3.0-only
|
|
3
|
+
/**
|
|
4
|
+
* PlatformLearner — scrape official docs, help center, shortcuts for a platform.
|
|
5
|
+
*
|
|
6
|
+
* Crawls documentation pages via CDP, extracts structured data,
|
|
7
|
+
* and saves as a reference JSON.
|
|
8
|
+
*/
|
|
9
|
+
import fs from "node:fs";
|
|
10
|
+
import path from "node:path";
|
|
11
|
+
import { writeFileAtomicSync } from "../util/atomic-write.js";
|
|
12
|
+
/** Common URL patterns for platform documentation */
|
|
13
|
+
export function buildDocUrls(platform, rootUrl) {
|
|
14
|
+
const base = rootUrl ?? `https://${platform}.com`;
|
|
15
|
+
const origin = base.replace(/\/$/, "");
|
|
16
|
+
return [
|
|
17
|
+
origin,
|
|
18
|
+
`${origin}/help`,
|
|
19
|
+
`${origin}/support`,
|
|
20
|
+
`${origin}/docs`,
|
|
21
|
+
`${origin}/keyboard-shortcuts`,
|
|
22
|
+
`${origin}/shortcuts`,
|
|
23
|
+
`https://help.${platform}.com`,
|
|
24
|
+
`https://support.${platform}.com`,
|
|
25
|
+
`https://docs.${platform}.com`,
|
|
26
|
+
`${origin}/developers`,
|
|
27
|
+
`${origin}/api`,
|
|
28
|
+
`${origin}/changelog`,
|
|
29
|
+
`${origin}/whats-new`,
|
|
30
|
+
];
|
|
31
|
+
}
|
|
32
|
+
/** Extract keyboard shortcuts from a page */
|
|
33
|
+
export async function extractShortcuts(cdpEvaluate) {
|
|
34
|
+
const result = await cdpEvaluate(`(() => {
|
|
35
|
+
const shortcuts = {};
|
|
36
|
+
// Look for common shortcut table patterns
|
|
37
|
+
const tables = document.querySelectorAll('table');
|
|
38
|
+
for (const table of tables) {
|
|
39
|
+
const rows = table.querySelectorAll('tr');
|
|
40
|
+
for (const row of rows) {
|
|
41
|
+
const cells = row.querySelectorAll('td, th');
|
|
42
|
+
if (cells.length >= 2) {
|
|
43
|
+
const text0 = (cells[0].textContent || '').trim();
|
|
44
|
+
const text1 = (cells[1].textContent || '').trim();
|
|
45
|
+
// Check if either cell contains key combos
|
|
46
|
+
if (text0.match(/[⌘⌥⇧⌃]|ctrl|cmd|alt|shift/i) || text1.match(/[⌘⌥⇧⌃]|ctrl|cmd|alt|shift/i)) {
|
|
47
|
+
shortcuts[text0] = text1;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
// Also check kbd elements
|
|
53
|
+
const kbds = document.querySelectorAll('kbd');
|
|
54
|
+
for (const kbd of kbds) {
|
|
55
|
+
const parent = kbd.closest('li, tr, p, div');
|
|
56
|
+
if (parent) {
|
|
57
|
+
const keyText = kbd.textContent.trim();
|
|
58
|
+
const descText = parent.textContent.replace(keyText, '').trim().substring(0, 80);
|
|
59
|
+
if (keyText && descText) shortcuts[keyText] = descText;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return shortcuts;
|
|
63
|
+
})()`);
|
|
64
|
+
return result.result?.value ?? {};
|
|
65
|
+
}
|
|
66
|
+
/** Extract page content as structured text */
|
|
67
|
+
export async function extractPageContent(cdpEvaluate) {
|
|
68
|
+
const result = await cdpEvaluate(`(() => {
|
|
69
|
+
const headings = Array.from(document.querySelectorAll('h1, h2, h3')).map(h => h.textContent.trim()).filter(Boolean);
|
|
70
|
+
const links = Array.from(document.querySelectorAll('a[href]')).slice(0, 100).map(a => ({
|
|
71
|
+
text: (a.textContent || '').trim().substring(0, 80),
|
|
72
|
+
href: a.href,
|
|
73
|
+
})).filter(l => l.text && l.href);
|
|
74
|
+
return {
|
|
75
|
+
title: document.title,
|
|
76
|
+
headings,
|
|
77
|
+
links,
|
|
78
|
+
text: document.body.innerText.substring(0, 8000),
|
|
79
|
+
};
|
|
80
|
+
})()`);
|
|
81
|
+
return result.result?.value ?? { title: "", headings: [], links: [], text: "" };
|
|
82
|
+
}
|
|
83
|
+
/** Extract interactive element selectors from a page */
|
|
84
|
+
export async function extractSelectors(cdpEvaluate) {
|
|
85
|
+
const result = await cdpEvaluate(`(() => {
|
|
86
|
+
const selectors = {};
|
|
87
|
+
const elements = document.querySelectorAll('[data-testid], [aria-label], [role="button"], [role="tab"], [role="menuitem"]');
|
|
88
|
+
for (const el of Array.from(elements).slice(0, 50)) {
|
|
89
|
+
const testId = el.getAttribute('data-testid');
|
|
90
|
+
const label = el.getAttribute('aria-label');
|
|
91
|
+
const key = testId || label || el.textContent?.trim().substring(0, 30) || '';
|
|
92
|
+
if (!key) continue;
|
|
93
|
+
|
|
94
|
+
let selector = '';
|
|
95
|
+
if (testId) selector = '[data-testid="' + testId + '"]';
|
|
96
|
+
else if (el.id) selector = '#' + el.id;
|
|
97
|
+
else if (label) selector = '[aria-label="' + label + '"]';
|
|
98
|
+
|
|
99
|
+
if (selector) selectors[key] = selector;
|
|
100
|
+
}
|
|
101
|
+
return selectors;
|
|
102
|
+
})()`);
|
|
103
|
+
return result.result?.value ?? {};
|
|
104
|
+
}
|
|
105
|
+
/** Crawl a page via CDP: navigate, wait, extract */
|
|
106
|
+
export async function crawlPage(cdpClient, url, timeoutMs = 10000) {
|
|
107
|
+
try {
|
|
108
|
+
// Navigate
|
|
109
|
+
await cdpClient.Page.navigate({ url });
|
|
110
|
+
// Wait for load
|
|
111
|
+
await new Promise((resolve) => {
|
|
112
|
+
const timer = setTimeout(resolve, timeoutMs);
|
|
113
|
+
cdpClient.Page.loadEventFired().then(() => { clearTimeout(timer); resolve(); }).catch(() => { clearTimeout(timer); resolve(); });
|
|
114
|
+
});
|
|
115
|
+
// Extra wait for SPA content
|
|
116
|
+
await new Promise(r => setTimeout(r, 2000));
|
|
117
|
+
const evaluate = async (expr) => {
|
|
118
|
+
return cdpClient.Runtime.evaluate({ expression: expr, returnByValue: true, awaitPromise: true });
|
|
119
|
+
};
|
|
120
|
+
const content = await extractPageContent(evaluate);
|
|
121
|
+
const shortcuts = await extractShortcuts(evaluate);
|
|
122
|
+
const selectors = await extractSelectors(evaluate);
|
|
123
|
+
return { success: true, content, shortcuts, selectors };
|
|
124
|
+
}
|
|
125
|
+
catch (err) {
|
|
126
|
+
return { success: false, error: err instanceof Error ? err.message : String(err) };
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
/** Compile crawl results into a learn result */
|
|
130
|
+
export function compileLearnResult(platform, crawledPages) {
|
|
131
|
+
const allShortcuts = {};
|
|
132
|
+
const allSelectors = {};
|
|
133
|
+
const features = [];
|
|
134
|
+
const tips = [];
|
|
135
|
+
const sourceUrls = [];
|
|
136
|
+
const flows = {};
|
|
137
|
+
const apiEndpoints = [];
|
|
138
|
+
const knownLimitations = [];
|
|
139
|
+
for (const page of crawledPages) {
|
|
140
|
+
sourceUrls.push(page.url);
|
|
141
|
+
if (page.shortcuts) {
|
|
142
|
+
Object.assign(allShortcuts, page.shortcuts);
|
|
143
|
+
}
|
|
144
|
+
if (page.selectors && Object.keys(page.selectors).length > 0) {
|
|
145
|
+
const pageName = page.content?.title?.replace(/[^a-zA-Z0-9]/g, "_").substring(0, 30) ?? "page";
|
|
146
|
+
allSelectors[pageName] = page.selectors;
|
|
147
|
+
}
|
|
148
|
+
if (page.content) {
|
|
149
|
+
// Extract features from headings
|
|
150
|
+
for (const h of page.content.headings) {
|
|
151
|
+
if (h.length > 3 && h.length < 80)
|
|
152
|
+
features.push(h);
|
|
153
|
+
}
|
|
154
|
+
// Look for API-related links
|
|
155
|
+
for (const link of page.content.links) {
|
|
156
|
+
if (/api|developer|endpoint|sdk|integration/i.test(link.text)) {
|
|
157
|
+
apiEndpoints.push(`${link.text}: ${link.href}`);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
// Extract flows from numbered step sequences (e.g. "1. Click..." "2. Enter..." "3. Submit...")
|
|
161
|
+
const contentLines = page.content.text.split("\n");
|
|
162
|
+
let currentFlow = null;
|
|
163
|
+
for (let i = 0; i < contentLines.length; i++) {
|
|
164
|
+
const line = contentLines[i].trim();
|
|
165
|
+
const stepMatch = line.match(/^(\d+)[.)]\s+(.+)/);
|
|
166
|
+
if (stepMatch) {
|
|
167
|
+
const stepNum = parseInt(stepMatch[1], 10);
|
|
168
|
+
const stepText = stepMatch[2].trim();
|
|
169
|
+
if (stepNum === 1 && stepText.length > 5) {
|
|
170
|
+
// Start a new flow — use the preceding heading as the name
|
|
171
|
+
const heading = i > 0 ? contentLines.slice(Math.max(0, i - 3), i).find(l => l.trim().length > 3 && !l.trim().match(/^\d/)) : null;
|
|
172
|
+
const flowName = (heading?.trim() ?? `flow_${Object.keys(flows).length + 1}`).replace(/[^a-zA-Z0-9_ ]/g, "").substring(0, 50).trim();
|
|
173
|
+
currentFlow = { name: flowName, steps: [stepText] };
|
|
174
|
+
}
|
|
175
|
+
else if (currentFlow && stepNum > 1) {
|
|
176
|
+
currentFlow.steps.push(stepText);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
else if (currentFlow && currentFlow.steps.length >= 2) {
|
|
180
|
+
// End of step sequence — save the flow
|
|
181
|
+
const key = currentFlow.name.toLowerCase().replace(/\s+/g, "_");
|
|
182
|
+
if (!flows[key]) {
|
|
183
|
+
flows[key] = { description: currentFlow.name, steps: currentFlow.steps };
|
|
184
|
+
}
|
|
185
|
+
currentFlow = null;
|
|
186
|
+
}
|
|
187
|
+
else if (line.length > 0 && !line.match(/^\d/)) {
|
|
188
|
+
currentFlow = null;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
// Save any trailing flow
|
|
192
|
+
if (currentFlow && currentFlow.steps.length >= 2) {
|
|
193
|
+
const key = currentFlow.name.toLowerCase().replace(/\s+/g, "_");
|
|
194
|
+
if (!flows[key]) {
|
|
195
|
+
flows[key] = { description: currentFlow.name, steps: currentFlow.steps };
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
// Look for limitation/known-issue mentions
|
|
199
|
+
const text = page.content.text.toLowerCase();
|
|
200
|
+
if (text.includes("limitation") || text.includes("known issue") || text.includes("not supported")) {
|
|
201
|
+
const lines = page.content.text.split("\n");
|
|
202
|
+
for (const line of lines) {
|
|
203
|
+
if (/limitation|known issue|not supported|doesn't support|won't work/i.test(line)) {
|
|
204
|
+
knownLimitations.push(line.trim().substring(0, 200));
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
return {
|
|
211
|
+
platform,
|
|
212
|
+
learnedAt: new Date().toISOString(),
|
|
213
|
+
sourceUrls,
|
|
214
|
+
shortcuts: allShortcuts,
|
|
215
|
+
features: [...new Set(features)].slice(0, 50),
|
|
216
|
+
selectors: allSelectors,
|
|
217
|
+
flows,
|
|
218
|
+
apiEndpoints: [...new Set(apiEndpoints)].slice(0, 20),
|
|
219
|
+
knownLimitations: [...new Set(knownLimitations)].slice(0, 20),
|
|
220
|
+
tips,
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
/** Save learn result as a reference JSON */
|
|
224
|
+
export function saveLearnResult(referencesDir, result) {
|
|
225
|
+
if (!fs.existsSync(referencesDir)) {
|
|
226
|
+
fs.mkdirSync(referencesDir, { recursive: true });
|
|
227
|
+
}
|
|
228
|
+
const filePath = path.join(referencesDir, `${result.platform}-learned.json`);
|
|
229
|
+
const reference = {
|
|
230
|
+
id: `${result.platform}-learned`,
|
|
231
|
+
name: `${result.platform} — Auto-Learned from Docs`,
|
|
232
|
+
description: `Scraped ${result.sourceUrls.length} documentation pages. Found ${Object.keys(result.shortcuts).length} shortcuts, ${result.features.length} features.`,
|
|
233
|
+
platform: result.platform,
|
|
234
|
+
bundleId: result.bundleId ?? null,
|
|
235
|
+
version: "1.0.0",
|
|
236
|
+
tags: [result.platform, "auto-learned"],
|
|
237
|
+
successCount: 0,
|
|
238
|
+
failCount: 0,
|
|
239
|
+
urls: Object.fromEntries(result.sourceUrls.map((u, i) => [`doc_${i}`, u])),
|
|
240
|
+
selectors: result.selectors,
|
|
241
|
+
shortcuts: result.shortcuts,
|
|
242
|
+
flows: result.flows,
|
|
243
|
+
detection: {},
|
|
244
|
+
errors: [],
|
|
245
|
+
policyNotes: {},
|
|
246
|
+
_meta: {
|
|
247
|
+
learnedAt: result.learnedAt,
|
|
248
|
+
sourceUrls: result.sourceUrls,
|
|
249
|
+
features: result.features,
|
|
250
|
+
apiEndpoints: result.apiEndpoints,
|
|
251
|
+
knownLimitations: result.knownLimitations,
|
|
252
|
+
tips: result.tips,
|
|
253
|
+
},
|
|
254
|
+
};
|
|
255
|
+
writeFileAtomicSync(filePath, JSON.stringify(reference, null, 2));
|
|
256
|
+
return filePath;
|
|
257
|
+
}
|