@weavetab/mcp 2.5.0-beta.2 → 2.5.0-beta.5
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/AGENTS.md +1 -1
- package/ARCHITECTURE.md +6 -6
- package/LICENSE +216 -631
- package/README.md +153 -1074
- package/SECURITY.md +33 -22
- package/TROUBLESHOOTING.md +2 -2
- package/dist/cdp/bridge.js +2 -1
- package/dist/cdp/connector.d.ts +2 -0
- package/dist/cdp/connector.js +10 -6
- package/dist/cdp/helpers.d.ts +4 -0
- package/dist/cdp/helpers.js +181 -0
- package/dist/cdp/omni_broker.d.ts +1 -1
- package/dist/cdp/omni_broker.js +1 -1
- package/dist/cdp/walker.d.ts +25 -1
- package/dist/cdp/walker.js +304 -7
- package/dist/cli/plugin.d.ts +1 -0
- package/dist/cli/plugin.js +81 -18
- package/dist/cli/updater.d.ts +1 -1
- package/dist/cli/updater.js +27 -7
- package/dist/config/loader.d.ts +9 -1
- package/dist/config/loader.js +42 -3
- package/dist/desktop/discovery.d.ts +44 -0
- package/dist/desktop/discovery.js +364 -0
- package/dist/index.js +137 -21
- package/dist/intelligence/marks.d.ts +36 -0
- package/dist/intelligence/marks.js +30 -0
- package/dist/intelligence/patternCompiler.d.ts +118 -0
- package/dist/intelligence/patternCompiler.js +259 -0
- package/dist/intelligence/sessionMemory.d.ts +2 -0
- package/dist/intelligence/sessionMemory.js +18 -8
- package/dist/overlay/injector.js +2 -2
- package/dist/plugin/interface.d.ts +88 -5
- package/dist/plugin/loader.d.ts +12 -1
- package/dist/plugin/loader.js +280 -70
- package/dist/plugin/pipeline.d.ts +75 -0
- package/dist/plugin/pipeline.js +183 -0
- package/dist/plugin/registry.d.ts +2 -1
- package/dist/plugin/registry.js +24 -14
- package/dist/security/secrets.d.ts +7 -0
- package/dist/security/secrets.js +37 -0
- package/dist/sensors/actionability.js +1 -2
- package/dist/sensors/guard.js +4 -10
- package/dist/sensors/mutations.js +12 -0
- package/dist/sensors/operation_monitor.js +1 -0
- package/dist/server.d.ts +1 -1
- package/dist/server.js +453 -178
- package/dist/state/agent.d.ts +1 -0
- package/dist/state/agent.js +3 -0
- package/dist/state/delta.d.ts +12 -4
- package/dist/state/delta.js +67 -14
- package/dist/state/session.d.ts +6 -0
- package/dist/state/session.js +27 -0
- package/dist/tools/browser_detect.d.ts +16 -3
- package/dist/tools/browser_detect.js +52 -7
- package/dist/tools/burst.d.ts +17 -8
- package/dist/tools/burst.js +236 -77
- package/dist/tools/captcha.js +1 -1
- package/dist/tools/checkpoint.d.ts +38 -0
- package/dist/tools/checkpoint.js +271 -0
- package/dist/tools/click.d.ts +1 -0
- package/dist/tools/click.js +74 -70
- package/dist/tools/design_extract.js +1 -1
- package/dist/tools/eval.js +1 -1
- package/dist/tools/fill.d.ts +20 -1
- package/dist/tools/fill.js +199 -40
- package/dist/tools/find.d.ts +16 -0
- package/dist/tools/find.js +67 -44
- package/dist/tools/github/index.d.ts +19 -4
- package/dist/tools/github/index.js +40 -4
- package/dist/tools/highlight.d.ts +6 -2
- package/dist/tools/highlight.js +158 -72
- package/dist/tools/inspect.js +1 -1
- package/dist/tools/key.js +65 -56
- package/dist/tools/navigate.d.ts +1 -1
- package/dist/tools/navigate.js +34 -39
- package/dist/tools/pdf.js +21 -13
- package/dist/tools/pointer.d.ts +18 -3
- package/dist/tools/pointer.js +89 -1
- package/dist/tools/read.d.ts +28 -8
- package/dist/tools/read.js +230 -56
- package/dist/tools/scrape.js +67 -13
- package/dist/tools/screenshot.js +21 -16
- package/dist/tools/scroll.js +6 -11
- package/dist/tools/select.js +8 -2
- package/dist/tools/snapshot.js +3 -34
- package/dist/tools/tabs.js +15 -7
- package/dist/tools/thought.d.ts +2 -0
- package/dist/tools/thought.js +13 -14
- package/dist/tools/type.d.ts +2 -0
- package/dist/tools/type.js +107 -58
- package/dist/tools/type_secret.js +42 -76
- package/dist/tools/upload.js +1 -1
- package/dist/tools/video_compiler.d.ts +1 -1
- package/dist/tools/video_compiler.js +11 -12
- package/dist/version.d.ts +5 -0
- package/dist/version.js +5 -0
- package/docs/DOCUMENTATION.md +37 -41
- package/docs/PLUGINS.md +121 -0
- package/docs/TOOLS.md +54 -0
- package/docs/architecture/blind-injection.md +83 -20
- package/docs/architecture/overview.md +49 -49
- package/docs/architecture/security.md +8 -7
- package/docs/architecture/transport.md +2 -2
- package/docs/cli/cli-commands.md +7 -1
- package/docs/cli/cli-flags.md +15 -7
- package/docs/cli/cli-scripting.md +6 -3
- package/docs/configuration/config-locations.md +3 -4
- package/docs/configuration/env-variables.md +4 -4
- package/docs/configuration/folders.md +55 -54
- package/docs/desktop-automation/electron-apps.md +214 -162
- package/docs/get-started/introduction.md +60 -60
- package/docs/get-started/setup-wizard.md +7 -6
- package/docs/intelligence/site-profiles.md +151 -0
- package/docs/tools/tools.md +631 -293
- package/docs/troubleshooting/trouble-browser.md +1 -1
- package/docs/troubleshooting/trouble-loop.md +31 -31
- package/ext-dist/background.js +10 -62
- package/ext-dist/content.js +333 -920
- package/ext-dist/hud/hud.js +9 -9
- package/ext-dist/manifest.json +2 -6
- package/ext-dist/styles.css +6 -1
- package/icon.png +0 -0
- package/mcp.json +87 -71
- package/package.json +118 -119
- package/server.json +5 -5
- package/ext-dist/sidepanel/sidepanel.js +0 -25
- package/ext-dist/sidepanel.html +0 -547
package/dist/cdp/walker.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { logDebug } from "../audit/logger.js";
|
|
2
2
|
import { SessionSecretTracker } from "../security/secrets.js";
|
|
3
|
+
import { getStoredRef, getStoredProps } from "../state/delta.js";
|
|
4
|
+
import { getRawCachedActionMap } from "../state/cache.js";
|
|
3
5
|
// ─── Public types ─────────────────────────────────────────────────────────────
|
|
4
6
|
export const WT_REGISTRY_INIT_EXPR = `
|
|
5
7
|
window.__weavetab = window.__weavetab || {};
|
|
@@ -162,7 +164,7 @@ export async function buildActionMap(session, options = {}, targetId = "default"
|
|
|
162
164
|
try {
|
|
163
165
|
const parsed = JSON.parse(liveIndexResult.value);
|
|
164
166
|
if (parsed.source === 'live-index' && parsed.elements && Array.isArray(parsed.elements)) {
|
|
165
|
-
logDebug(`[Walker]
|
|
167
|
+
logDebug(`[Walker] read ${parsed.elements.length} elements from extension live index`);
|
|
166
168
|
extensionMap = parsed.elements;
|
|
167
169
|
}
|
|
168
170
|
}
|
|
@@ -309,8 +311,16 @@ export async function buildActionMap(session, options = {}, targetId = "default"
|
|
|
309
311
|
else if (role === 'input' && (el.type === 'text' || el.type === 'search')) role = 'textbox';
|
|
310
312
|
var frameUrl = '';
|
|
311
313
|
try { if (el.ownerDocument !== document) frameUrl = el.ownerDocument.defaultView.location.href; } catch(e) { frameUrl = 'cross-origin-' + (_coIdx++); }
|
|
312
|
-
var
|
|
313
|
-
var
|
|
314
|
+
var secretRegex = /password|passcode|pin|passphrase|secret|token|auth.?key|access.?code|mot.?de.?passe|contrase[nñ]a|kennwort|senha/i;
|
|
315
|
+
var isSecret = el.hasAttribute('data-wt-secret') ||
|
|
316
|
+
(el.tagName === 'INPUT' && (
|
|
317
|
+
el.type === 'password' ||
|
|
318
|
+
secretRegex.test(el.getAttribute('autocomplete') || '') ||
|
|
319
|
+
secretRegex.test(el.getAttribute('name') || '') ||
|
|
320
|
+
secretRegex.test(el.getAttribute('id') || '') ||
|
|
321
|
+
secretRegex.test(el.getAttribute('aria-label') || '')
|
|
322
|
+
));
|
|
323
|
+
var labelStr = el.getAttribute('aria-label') || el.innerText || (isSecret ? '' : el.value) || el.placeholder || '';
|
|
314
324
|
// For radio/checkbox, serialize checked state as 'true'/'false' so the agent
|
|
315
325
|
// can see which option is selected without having to scrape the DOM separately.
|
|
316
326
|
var elType = (el.type || '').toLowerCase();
|
|
@@ -345,10 +355,59 @@ export async function buildActionMap(session, options = {}, targetId = "default"
|
|
|
345
355
|
break;
|
|
346
356
|
}
|
|
347
357
|
}
|
|
358
|
+
|
|
359
|
+
// ── Container context binding ─────────────────────────────────────
|
|
360
|
+
// Walk up the DOM to find a meaningful card/row/list-item heading that
|
|
361
|
+
// describes this element's entity context, e.g. "Pro Plan ($29/mo)".
|
|
362
|
+
var containerCtx = '';
|
|
363
|
+
try {
|
|
364
|
+
var CARD_SEL = '[class*="card"],[class*="row"],[class*="item"],[class*="product"],[class*="plan"],[class*="entry"],[class*="result"],[class*="tile"],[class*="option"],[data-entity]';
|
|
365
|
+
var ancestor = el.parentElement;
|
|
366
|
+
var depth3 = 0;
|
|
367
|
+
while (ancestor && depth3 < 8) {
|
|
368
|
+
if (ancestor.matches && ancestor.matches(CARD_SEL)) {
|
|
369
|
+
var heading = ancestor.querySelector('h1,h2,h3,h4,h5,h6,[class*="title"],[class*="name"],[class*="heading"],[class*="label"]');
|
|
370
|
+
if (heading) {
|
|
371
|
+
var hText = (heading.innerText || heading.textContent || '').trim().slice(0, 60);
|
|
372
|
+
if (hText && hText !== labelStr) { containerCtx = hText; break; }
|
|
373
|
+
}
|
|
374
|
+
var allText = (ancestor.innerText || ancestor.textContent || '').replace(/\s+/g, ' ').trim().slice(0, 60);
|
|
375
|
+
if (allText && allText !== labelStr) { containerCtx = allText; break; }
|
|
376
|
+
}
|
|
377
|
+
ancestor = ancestor.parentElement;
|
|
378
|
+
depth3++;
|
|
379
|
+
}
|
|
380
|
+
} catch(e) {}
|
|
381
|
+
|
|
382
|
+
// ── Modal detection ───────────────────────────────────────────────
|
|
383
|
+
var inModal = false;
|
|
384
|
+
var modalTitleStr = '';
|
|
385
|
+
try {
|
|
386
|
+
var MODAL_SEL = 'dialog[open],[role="dialog"],[aria-modal="true"],[class*="modal"],[class*="overlay"],[class*="drawer"]';
|
|
387
|
+
var modalAnc = el.parentElement;
|
|
388
|
+
var md = 0;
|
|
389
|
+
while (modalAnc && md < 15) {
|
|
390
|
+
if (modalAnc.matches && modalAnc.matches(MODAL_SEL)) {
|
|
391
|
+
inModal = true;
|
|
392
|
+
var mh = modalAnc.querySelector('h1,h2,h3,h4,[class*="title"]');
|
|
393
|
+
if (mh) {
|
|
394
|
+
var lbId = modalAnc.getAttribute('aria-labelledby');
|
|
395
|
+
var lbEl = lbId ? document.getElementById(lbId) : null;
|
|
396
|
+
modalTitleStr = ((lbEl || mh).innerText || (lbEl || mh).textContent || '').trim().slice(0, 60);
|
|
397
|
+
}
|
|
398
|
+
break;
|
|
399
|
+
}
|
|
400
|
+
modalAnc = modalAnc.parentElement;
|
|
401
|
+
md++;
|
|
402
|
+
}
|
|
403
|
+
} catch(e) {}
|
|
404
|
+
|
|
348
405
|
results.push({
|
|
349
406
|
id: String(id),
|
|
350
407
|
role: el.getAttribute('role') || role,
|
|
408
|
+
type: elType || undefined,
|
|
351
409
|
label: labelStr,
|
|
410
|
+
placeholder: el.placeholder || undefined,
|
|
352
411
|
value: valStr,
|
|
353
412
|
x: Math.round(rect.left + rect.width / 2),
|
|
354
413
|
y: Math.round(rect.top + rect.height / 2),
|
|
@@ -357,7 +416,12 @@ export async function buildActionMap(session, options = {}, targetId = "default"
|
|
|
357
416
|
textContent: txtContent,
|
|
358
417
|
className: el.className || '',
|
|
359
418
|
frameUrl: frameUrl,
|
|
360
|
-
structuralIdiom: idiom || undefined
|
|
419
|
+
structuralIdiom: idiom || undefined,
|
|
420
|
+
containerContext: containerCtx || undefined,
|
|
421
|
+
isModal: inModal || undefined,
|
|
422
|
+
modalTitle: modalTitleStr || undefined,
|
|
423
|
+
disabled: (el.disabled || el.getAttribute('aria-disabled') === 'true') || undefined,
|
|
424
|
+
checked: isCheckable ? el.checked : undefined,
|
|
361
425
|
});
|
|
362
426
|
} catch (e) {
|
|
363
427
|
// Silently skip elements that cause errors (cross-origin SVG, detached nodes, etc.)
|
|
@@ -446,6 +510,8 @@ export async function buildActionMap(session, options = {}, targetId = "default"
|
|
|
446
510
|
role: el.role || el.tag || 'generic',
|
|
447
511
|
label: el.label || el.text || el.placeholder || '',
|
|
448
512
|
value: el.value,
|
|
513
|
+
placeholder: el.placeholder || undefined,
|
|
514
|
+
type: el.type || undefined,
|
|
449
515
|
group: 'main_content',
|
|
450
516
|
fingerprint: `${el.role || el.tag || 'generic'}:${el.label || el.text || ''}:${el.id}`,
|
|
451
517
|
depth: 0,
|
|
@@ -459,6 +525,10 @@ export async function buildActionMap(session, options = {}, targetId = "default"
|
|
|
459
525
|
className: el.className || el.attributes?.class || '',
|
|
460
526
|
structuralIdiom: el.structuralIdiom || undefined,
|
|
461
527
|
frameUrl: el.frameUrl || undefined,
|
|
528
|
+
containerContext: el.containerContext || undefined,
|
|
529
|
+
isModal: el.isModal || undefined,
|
|
530
|
+
modalTitle: el.modalTitle || undefined,
|
|
531
|
+
disabled: el.disabled || undefined,
|
|
462
532
|
}));
|
|
463
533
|
// Post-mapping sanitizer — catch any w:undefined/w:null and reassign.
|
|
464
534
|
// Uses a stable hash derived from the element's fingerprint, coordinates, and
|
|
@@ -590,7 +660,15 @@ export async function buildActionMap(session, options = {}, targetId = "default"
|
|
|
590
660
|
if (options.limit && filtered.length > options.limit) {
|
|
591
661
|
filtered = filtered.slice(0, options.limit);
|
|
592
662
|
}
|
|
593
|
-
|
|
663
|
+
// Apply session secret redaction over all element labels, values, placeholders
|
|
664
|
+
const redactedElements = filtered.map(el => ({
|
|
665
|
+
...el,
|
|
666
|
+
label: targetId ? SessionSecretTracker.redact(targetId, el.label) : el.label,
|
|
667
|
+
value: el.value !== undefined ? (targetId ? SessionSecretTracker.redact(targetId, el.value) : el.value) : undefined,
|
|
668
|
+
placeholder: el.placeholder ? (targetId ? SessionSecretTracker.redact(targetId, el.placeholder) : el.placeholder) : undefined,
|
|
669
|
+
textContent: el.textContent ? (targetId ? SessionSecretTracker.redact(targetId, el.textContent) : el.textContent) : undefined,
|
|
670
|
+
}));
|
|
671
|
+
return { url, title, elements: redactedElements, targetId };
|
|
594
672
|
}
|
|
595
673
|
// ─── toRefEntries — converts ActionElements to RefEntry for delta tracking ───
|
|
596
674
|
export function toRefEntries(elements) {
|
|
@@ -600,7 +678,7 @@ export function toRefEntries(elements) {
|
|
|
600
678
|
label: el.label,
|
|
601
679
|
type: el.type,
|
|
602
680
|
href: el.href,
|
|
603
|
-
disabled: el.disabled,
|
|
681
|
+
disabled: el.disabled ?? false,
|
|
604
682
|
value: el.value,
|
|
605
683
|
fingerprint: el.fingerprint,
|
|
606
684
|
}));
|
|
@@ -888,11 +966,17 @@ export async function buildAXTreeMap(session, _options = {}, targetId = "default
|
|
|
888
966
|
else {
|
|
889
967
|
value = node.value?.value !== undefined ? String(node.value.value) : undefined;
|
|
890
968
|
}
|
|
891
|
-
const
|
|
969
|
+
const SECRET_TERMS = /password|passcode|pin|passphrase|secret|token|auth.?key|access.?code|mot.?de.?passe|contrase[nñ]a|kennwort|senha|пароль|密碼|密码/i;
|
|
970
|
+
const isSecretRole = role === 'password' || SECRET_TERMS.test(node.name?.value || '') || SECRET_TERMS.test(node.description?.value || '');
|
|
892
971
|
if (isSecretRole || (node.backendDOMNodeId !== undefined && SessionSecretTracker.isNodeSecret(targetId, node.backendDOMNodeId))) {
|
|
893
972
|
label = '[REDACTED]';
|
|
894
973
|
value = '[REDACTED]';
|
|
895
974
|
}
|
|
975
|
+
else {
|
|
976
|
+
label = targetId ? SessionSecretTracker.redact(targetId, label) : label;
|
|
977
|
+
if (value !== undefined)
|
|
978
|
+
value = targetId ? SessionSecretTracker.redact(targetId, value) : value;
|
|
979
|
+
}
|
|
896
980
|
// Use globally-unique sequential ID (Bug #5 fix: was local idCounter = 1).
|
|
897
981
|
const id = dedupeRefId(`w:${axIdCursor++}`);
|
|
898
982
|
elements.push({
|
|
@@ -944,3 +1028,216 @@ export async function buildAXTreeMap(session, _options = {}, targetId = "default
|
|
|
944
1028
|
axTreeCacheSet(url, map);
|
|
945
1029
|
return map;
|
|
946
1030
|
}
|
|
1031
|
+
/**
|
|
1032
|
+
* Sanitizes element IDs in an ActionElement list, replacing empty or malformed
|
|
1033
|
+
* IDs with sequential fallback IDs.
|
|
1034
|
+
*/
|
|
1035
|
+
export function sanitizeElementIds(els, counter) {
|
|
1036
|
+
let fixed = 0;
|
|
1037
|
+
for (const el of els) {
|
|
1038
|
+
if (!el.id || el.id === "w:undefined" || el.id === "w:null" || el.id === "w:") {
|
|
1039
|
+
el.id = `w:${counter.value++}`;
|
|
1040
|
+
fixed++;
|
|
1041
|
+
}
|
|
1042
|
+
}
|
|
1043
|
+
return fixed;
|
|
1044
|
+
}
|
|
1045
|
+
/**
|
|
1046
|
+
* Recovers a detached or invalidated w:NN ref on reactive SPAs.
|
|
1047
|
+
* When hydration or DOM re-renders detach an element, this helper executes a sub-5ms
|
|
1048
|
+
* multi-tier recovery heuristic (connected node verification, stored CSS selector,
|
|
1049
|
+
* exact label/aria, placeholder/name match, and fuzzy substring) and re-stamps
|
|
1050
|
+
* data-wt-id="NN" onto the live DOM node so subsequent interaction succeeds.
|
|
1051
|
+
*/
|
|
1052
|
+
export async function recoverDetachedRef(session, targetId, ref, preferredCandidate) {
|
|
1053
|
+
const refNumericId = ref.replace(/^w:/, '');
|
|
1054
|
+
// Extract known metadata for this ref
|
|
1055
|
+
const storedRef = getStoredRef(targetId, ref);
|
|
1056
|
+
const storedProps = getStoredProps(targetId, ref);
|
|
1057
|
+
const rawMap = getRawCachedActionMap(targetId);
|
|
1058
|
+
const cachedEl = rawMap?.elements?.find(e => e.id === ref || e.id === `w:${refNumericId}`);
|
|
1059
|
+
const candidateLabel = preferredCandidate?.label || storedProps?.label || storedRef?.label || cachedEl?.label || '';
|
|
1060
|
+
const candidatePlaceholder = preferredCandidate?.placeholder || storedProps?.placeholder || cachedEl?.placeholder || '';
|
|
1061
|
+
const candidateRole = preferredCandidate?.role || storedProps?.role || storedRef?.role || cachedEl?.role || '';
|
|
1062
|
+
const candidateSelector = preferredCandidate?.selector || cachedEl?.selector || (storedProps?.className ? `.${storedProps.className.split(' ')[0]}` : undefined);
|
|
1063
|
+
const candidateText = preferredCandidate?.textContent || storedProps?.textContent || cachedEl?.textContent || '';
|
|
1064
|
+
const meta = {
|
|
1065
|
+
label: candidateLabel === '[REDACTED]' ? '' : candidateLabel,
|
|
1066
|
+
placeholder: candidatePlaceholder === '[REDACTED]' ? '' : candidatePlaceholder,
|
|
1067
|
+
role: candidateRole,
|
|
1068
|
+
selector: candidateSelector,
|
|
1069
|
+
textContent: candidateText === '[REDACTED]' ? '' : candidateText,
|
|
1070
|
+
};
|
|
1071
|
+
try {
|
|
1072
|
+
const { result } = await session.Runtime.evaluate({
|
|
1073
|
+
expression: `(function(refId, meta) {
|
|
1074
|
+
// 1. Check if node with data-wt-id=refId is already in DOM, connected, and visible
|
|
1075
|
+
const existing = document.querySelector('[data-wt-id="' + refId + '"]');
|
|
1076
|
+
if (existing && existing.isConnected) {
|
|
1077
|
+
const r = existing.getBoundingClientRect();
|
|
1078
|
+
if (r.width > 0 && r.height > 0) {
|
|
1079
|
+
return {
|
|
1080
|
+
success: true,
|
|
1081
|
+
strategy: 'connected_existing',
|
|
1082
|
+
x: Math.round(r.left + r.width / 2),
|
|
1083
|
+
y: Math.round(r.top + r.height / 2),
|
|
1084
|
+
label: existing.getAttribute('aria-label') || (existing.innerText || existing.textContent || '').trim() || existing.placeholder || '',
|
|
1085
|
+
role: existing.getAttribute('role') || existing.tagName.toLowerCase()
|
|
1086
|
+
};
|
|
1087
|
+
}
|
|
1088
|
+
}
|
|
1089
|
+
|
|
1090
|
+
function isValidCandidate(el) {
|
|
1091
|
+
if (!el || !el.isConnected) return false;
|
|
1092
|
+
const r = el.getBoundingClientRect();
|
|
1093
|
+
if (r.width <= 0 || r.height <= 0) return false;
|
|
1094
|
+
try {
|
|
1095
|
+
const style = window.getComputedStyle(el);
|
|
1096
|
+
if (style.visibility === 'hidden' || style.display === 'none') return false;
|
|
1097
|
+
} catch(e) {}
|
|
1098
|
+
return true;
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1101
|
+
// 2. Strategy: Stored Selector (if stable CSS selector recorded)
|
|
1102
|
+
if (meta.selector && !meta.selector.startsWith('[data-wt-id')) {
|
|
1103
|
+
try {
|
|
1104
|
+
const el = document.querySelector(meta.selector);
|
|
1105
|
+
if (isValidCandidate(el)) {
|
|
1106
|
+
el.setAttribute('data-wt-id', refId);
|
|
1107
|
+
const r = el.getBoundingClientRect();
|
|
1108
|
+
return {
|
|
1109
|
+
success: true,
|
|
1110
|
+
strategy: 'stored_selector',
|
|
1111
|
+
x: Math.round(r.left + r.width / 2),
|
|
1112
|
+
y: Math.round(r.top + r.height / 2),
|
|
1113
|
+
label: el.getAttribute('aria-label') || (el.innerText || el.textContent || '').trim() || el.placeholder || '',
|
|
1114
|
+
role: el.getAttribute('role') || el.tagName.toLowerCase()
|
|
1115
|
+
};
|
|
1116
|
+
}
|
|
1117
|
+
} catch(e) {}
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
const normLabel = (meta.label || '').toLowerCase().trim();
|
|
1121
|
+
const normPlaceholder = (meta.placeholder || '').toLowerCase().trim();
|
|
1122
|
+
|
|
1123
|
+
const candidates = Array.from(document.querySelectorAll(
|
|
1124
|
+
'button, a, input, select, textarea, [role="button"], [role="link"], [role="textbox"], [role="checkbox"], [role="tab"], [tabindex="0"]'
|
|
1125
|
+
));
|
|
1126
|
+
|
|
1127
|
+
// 3. Strategy: Exact Label / Text / Aria match
|
|
1128
|
+
if (normLabel) {
|
|
1129
|
+
for (const c of candidates) {
|
|
1130
|
+
if (!isValidCandidate(c)) continue;
|
|
1131
|
+
const aria = (c.getAttribute('aria-label') || '').toLowerCase().trim();
|
|
1132
|
+
const text = ((c.innerText || c.textContent || '')).toLowerCase().trim();
|
|
1133
|
+
const val = (c.value || '').toLowerCase().trim();
|
|
1134
|
+
const placeholder = (c.placeholder || '').toLowerCase().trim();
|
|
1135
|
+
const name = (c.getAttribute('name') || '').toLowerCase().trim();
|
|
1136
|
+
|
|
1137
|
+
if (aria === normLabel || text === normLabel || placeholder === normLabel || name === normLabel || (val && val === normLabel)) {
|
|
1138
|
+
c.setAttribute('data-wt-id', refId);
|
|
1139
|
+
const r = c.getBoundingClientRect();
|
|
1140
|
+
return {
|
|
1141
|
+
success: true,
|
|
1142
|
+
strategy: 'exact_label',
|
|
1143
|
+
x: Math.round(r.left + r.width / 2),
|
|
1144
|
+
y: Math.round(r.top + r.height / 2),
|
|
1145
|
+
label: c.getAttribute('aria-label') || (c.innerText || c.textContent || '').trim() || c.placeholder || '',
|
|
1146
|
+
role: c.getAttribute('role') || c.tagName.toLowerCase()
|
|
1147
|
+
};
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
// 4. Strategy: Placeholder or Name match
|
|
1153
|
+
if (normPlaceholder) {
|
|
1154
|
+
for (const c of candidates) {
|
|
1155
|
+
if (!isValidCandidate(c)) continue;
|
|
1156
|
+
const placeholder = (c.placeholder || '').toLowerCase().trim();
|
|
1157
|
+
const name = (c.getAttribute('name') || '').toLowerCase().trim();
|
|
1158
|
+
if (placeholder === normPlaceholder || name === normPlaceholder) {
|
|
1159
|
+
c.setAttribute('data-wt-id', refId);
|
|
1160
|
+
const r = c.getBoundingClientRect();
|
|
1161
|
+
return {
|
|
1162
|
+
success: true,
|
|
1163
|
+
strategy: 'placeholder_or_name',
|
|
1164
|
+
x: Math.round(r.left + r.width / 2),
|
|
1165
|
+
y: Math.round(r.top + r.height / 2),
|
|
1166
|
+
label: c.placeholder || c.getAttribute('name') || '',
|
|
1167
|
+
role: c.getAttribute('role') || c.tagName.toLowerCase()
|
|
1168
|
+
};
|
|
1169
|
+
}
|
|
1170
|
+
}
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1173
|
+
// 5. Strategy: Fuzzy Substring Match (if label length >= 3)
|
|
1174
|
+
if (normLabel && normLabel.length >= 3) {
|
|
1175
|
+
let bestMatch = null;
|
|
1176
|
+
let minDiff = Infinity;
|
|
1177
|
+
for (const c of candidates) {
|
|
1178
|
+
if (!isValidCandidate(c)) continue;
|
|
1179
|
+
const text = ((c.innerText || c.textContent || '')).toLowerCase().trim();
|
|
1180
|
+
const aria = (c.getAttribute('aria-label') || '').toLowerCase().trim();
|
|
1181
|
+
const candStr = aria || text;
|
|
1182
|
+
if (candStr && candStr.includes(normLabel)) {
|
|
1183
|
+
const diff = candStr.length - normLabel.length;
|
|
1184
|
+
if (diff >= 0 && diff < minDiff) {
|
|
1185
|
+
minDiff = diff;
|
|
1186
|
+
bestMatch = c;
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
if (bestMatch) {
|
|
1191
|
+
bestMatch.setAttribute('data-wt-id', refId);
|
|
1192
|
+
const r = bestMatch.getBoundingClientRect();
|
|
1193
|
+
return {
|
|
1194
|
+
success: true,
|
|
1195
|
+
strategy: 'fuzzy_label',
|
|
1196
|
+
x: Math.round(r.left + r.width / 2),
|
|
1197
|
+
y: Math.round(r.top + r.height / 2),
|
|
1198
|
+
label: bestMatch.getAttribute('aria-label') || (bestMatch.innerText || bestMatch.textContent || '').trim() || bestMatch.placeholder || '',
|
|
1199
|
+
role: bestMatch.getAttribute('role') || bestMatch.tagName.toLowerCase()
|
|
1200
|
+
};
|
|
1201
|
+
}
|
|
1202
|
+
}
|
|
1203
|
+
|
|
1204
|
+
return { success: false };
|
|
1205
|
+
})(${JSON.stringify(refNumericId)}, ${JSON.stringify(meta)})`,
|
|
1206
|
+
returnByValue: true,
|
|
1207
|
+
});
|
|
1208
|
+
const res = result?.value;
|
|
1209
|
+
if (res && res.success) {
|
|
1210
|
+
const recoveredElement = {
|
|
1211
|
+
id: ref.startsWith('w:') ? ref : `w:${ref}`,
|
|
1212
|
+
role: res.role || candidateRole || 'button',
|
|
1213
|
+
label: res.label || candidateLabel || 'recovered-element',
|
|
1214
|
+
x: res.x,
|
|
1215
|
+
y: res.y,
|
|
1216
|
+
selector: `[data-wt-id="${refNumericId}"]`,
|
|
1217
|
+
group: "utility",
|
|
1218
|
+
depth: 0,
|
|
1219
|
+
fingerprint: `${res.role || candidateRole}:${res.label || candidateLabel}`,
|
|
1220
|
+
};
|
|
1221
|
+
// Patch the raw cache if present so subsequent lookups hit
|
|
1222
|
+
if (rawMap && rawMap.elements) {
|
|
1223
|
+
const existingIdx = rawMap.elements.findIndex(e => e.id === recoveredElement.id);
|
|
1224
|
+
if (existingIdx !== -1) {
|
|
1225
|
+
rawMap.elements[existingIdx] = recoveredElement;
|
|
1226
|
+
}
|
|
1227
|
+
else {
|
|
1228
|
+
rawMap.elements.push(recoveredElement);
|
|
1229
|
+
}
|
|
1230
|
+
}
|
|
1231
|
+
return {
|
|
1232
|
+
recovered: true,
|
|
1233
|
+
element: recoveredElement,
|
|
1234
|
+
strategy: res.strategy,
|
|
1235
|
+
warning: `(Auto-recovered detached ref '${ref}' via ${res.strategy}: '${recoveredElement.label}')`,
|
|
1236
|
+
};
|
|
1237
|
+
}
|
|
1238
|
+
}
|
|
1239
|
+
catch (err) {
|
|
1240
|
+
logDebug(`[Walker] Detached ref recovery failed for ${ref}: ${err}`);
|
|
1241
|
+
}
|
|
1242
|
+
return { recovered: false };
|
|
1243
|
+
}
|
package/dist/cli/plugin.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export declare function runPluginAdd(inputName: string, args?: string[]): Promise<void>;
|
|
2
|
+
export declare function runPluginAddLocal(inputPath: string): Promise<void>;
|
|
2
3
|
export declare function runPluginList(): Promise<void>;
|
|
3
4
|
export declare function runPluginSearch(query?: string): Promise<void>;
|
|
4
5
|
export declare function runPluginBrowse(): Promise<void>;
|
package/dist/cli/plugin.js
CHANGED
|
@@ -2,20 +2,22 @@ import * as p from "@clack/prompts";
|
|
|
2
2
|
import pc from "picocolors";
|
|
3
3
|
import boxen from "boxen";
|
|
4
4
|
import ora from "ora";
|
|
5
|
+
import fs from "node:fs";
|
|
5
6
|
import path from "node:path";
|
|
6
7
|
import { spawn } from "node:child_process";
|
|
7
8
|
import { createRequire } from "node:module";
|
|
9
|
+
import { WEAVETAB_VERSION } from "../version.js";
|
|
8
10
|
const _require = createRequire(import.meta.url);
|
|
9
11
|
function getVersion() {
|
|
10
12
|
try {
|
|
11
|
-
return _require("../../package.json").version ||
|
|
13
|
+
return _require("../../package.json").version || WEAVETAB_VERSION;
|
|
12
14
|
}
|
|
13
15
|
catch {
|
|
14
|
-
return
|
|
16
|
+
return WEAVETAB_VERSION;
|
|
15
17
|
}
|
|
16
18
|
}
|
|
17
|
-
import { getVerifiedPackages, resolvePluginTrust, resolvePluginCandidates, REGISTRY_URL, } from "../plugin/registry.js";
|
|
18
|
-
import { listInstalledPlugins, inspectInstalledPackage } from "../plugin/loader.js";
|
|
19
|
+
import { getVerifiedPackages, resolvePluginTrust, resolvePluginCandidates, invalidateRegistryCache, REGISTRY_URL, } from "../plugin/registry.js";
|
|
20
|
+
import { listInstalledPlugins, inspectInstalledPackage, normalizePermissions } from "../plugin/loader.js";
|
|
19
21
|
// Helper to run child process safely
|
|
20
22
|
function runCommand(cmd, cmdArgs, cwd = process.cwd()) {
|
|
21
23
|
return new Promise((resolve) => {
|
|
@@ -46,8 +48,20 @@ function runNpmCommand(cmdArgs, cwd = process.cwd()) {
|
|
|
46
48
|
// ---------------------------------------------------------------------------
|
|
47
49
|
export async function runPluginAdd(inputName, args = []) {
|
|
48
50
|
if (!inputName || inputName.startsWith("-")) {
|
|
49
|
-
console.log(pc.red("[ERROR] Please provide a package name to install."));
|
|
50
|
-
console.log(pc.dim("Usage: weavetab plugin add <packageName> [--official | --community] [--yes]"));
|
|
51
|
+
console.log(pc.red("[ERROR] Please provide a package name or local path to install."));
|
|
52
|
+
console.log(pc.dim("Usage: weavetab plugin add <packageName|./local-path> [--official | --community] [--yes]"));
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
// Local path install: `weavetab plugin add ./my-plugin` (npm installs the directory).
|
|
56
|
+
if (inputName === "." ||
|
|
57
|
+
inputName.startsWith("./") ||
|
|
58
|
+
inputName.startsWith("../") ||
|
|
59
|
+
inputName.startsWith("file:") ||
|
|
60
|
+
inputName.startsWith("/") ||
|
|
61
|
+
inputName.startsWith("\\") ||
|
|
62
|
+
/^[a-zA-Z]:[\\/]/.test(inputName) ||
|
|
63
|
+
inputName.startsWith("~/")) {
|
|
64
|
+
await runPluginAddLocal(inputName);
|
|
51
65
|
return;
|
|
52
66
|
}
|
|
53
67
|
const isForce = args.includes("--yes") || args.includes("-y") || args.includes("--force");
|
|
@@ -167,14 +181,14 @@ export async function runPluginAdd(inputName, args = []) {
|
|
|
167
181
|
}
|
|
168
182
|
return;
|
|
169
183
|
}
|
|
170
|
-
spinner.succeed(pc.green(`
|
|
184
|
+
spinner.succeed(pc.green(`installed ${pc.bold(selectedPkg)}`));
|
|
171
185
|
// Inspect installed package for weavetab.json
|
|
172
186
|
const targetDir = path.join(process.cwd(), "node_modules", selectedPkg);
|
|
173
187
|
const installedInfo = inspectInstalledPackage(targetDir);
|
|
174
188
|
if (installedInfo && installedInfo.manifest) {
|
|
175
189
|
const manifest = installedInfo.manifest;
|
|
176
190
|
const tools = manifest.tools || [];
|
|
177
|
-
const permissions = manifest.permissions
|
|
191
|
+
const permissions = normalizePermissions(manifest.permissions);
|
|
178
192
|
const infoLines = [
|
|
179
193
|
`${pc.bold("Plugin Details:")}`,
|
|
180
194
|
` ${pc.dim("Name:")} ${manifest.name || selectedPkg}`,
|
|
@@ -222,6 +236,49 @@ export async function runPluginAdd(inputName, args = []) {
|
|
|
222
236
|
}
|
|
223
237
|
}
|
|
224
238
|
// ---------------------------------------------------------------------------
|
|
239
|
+
// 1b. ADD FROM LOCAL PATH (`weavetab plugin add ./my-plugin`)
|
|
240
|
+
// ---------------------------------------------------------------------------
|
|
241
|
+
export async function runPluginAddLocal(inputPath) {
|
|
242
|
+
let dir = inputPath;
|
|
243
|
+
if (dir.startsWith("file:"))
|
|
244
|
+
dir = dir.slice("file:".length);
|
|
245
|
+
if (dir.startsWith("~/"))
|
|
246
|
+
dir = path.join(process.env.HOME || process.env.USERPROFILE || "", dir.slice(2));
|
|
247
|
+
const absDir = path.resolve(process.cwd(), dir);
|
|
248
|
+
if (!fs.existsSync(absDir) || !fs.statSync(absDir).isDirectory()) {
|
|
249
|
+
console.log(pc.red(`[ERROR] Local path not found: ${inputPath}`));
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
const local = inspectInstalledPackage(absDir);
|
|
253
|
+
if (!local) {
|
|
254
|
+
console.log(pc.red(`[ERROR] No valid weavetab.json manifest in ${absDir}`));
|
|
255
|
+
console.log(pc.dim("Scaffold one with: npx @weavetab/sdk init <name>"));
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
const spinner = ora(`Installing local plugin ${pc.cyan(local.packageName)} from ${pc.dim(absDir)} via npm...`).start();
|
|
259
|
+
const installResult = await runNpmCommand(["install", absDir]);
|
|
260
|
+
if (installResult.code !== 0) {
|
|
261
|
+
spinner.fail(pc.red(`Failed to install ${local.packageName}`));
|
|
262
|
+
if (installResult.stderr)
|
|
263
|
+
console.error(pc.dim(installResult.stderr));
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
spinner.succeed(pc.green(`installed ${pc.bold(local.packageName)} (local path)`));
|
|
267
|
+
const targetDir = path.join(process.cwd(), "node_modules", local.packageName);
|
|
268
|
+
const installedInfo = fs.existsSync(targetDir) ? inspectInstalledPackage(targetDir) : local;
|
|
269
|
+
const manifest = (installedInfo?.manifest || local.manifest);
|
|
270
|
+
const tools = manifest.tools || [];
|
|
271
|
+
console.log();
|
|
272
|
+
console.log(boxen(`${pc.bold("Plugin Details:")}\n` +
|
|
273
|
+
` ${pc.dim("Name:")} ${manifest.name || local.packageName}\n` +
|
|
274
|
+
` ${pc.dim("Version:")} ${manifest.version || "1.0.0"}\n` +
|
|
275
|
+
` ${pc.dim("Source:")} ${absDir} (local path)\n` +
|
|
276
|
+
` ${pc.dim("Description:")} ${manifest.description || "No description provided"}` +
|
|
277
|
+
(tools.length > 0 ? `\n\n${pc.bold(`Exposed Tools (${tools.length}):`)}` + tools.map((t) => `\n • ${pc.cyan(t.name)}: ${pc.dim(t.description || "")}`).join("") : ""), { padding: 1, borderStyle: "round", borderColor: "green" }));
|
|
278
|
+
console.log();
|
|
279
|
+
console.log(pc.green("✔ Plugin is ready to use! Restart Weavetab MCP server to activate changes.\n"));
|
|
280
|
+
}
|
|
281
|
+
// ---------------------------------------------------------------------------
|
|
225
282
|
// 2. LIST INSTALLED PLUGINS
|
|
226
283
|
// ---------------------------------------------------------------------------
|
|
227
284
|
export async function runPluginList() {
|
|
@@ -387,7 +444,7 @@ export async function runPluginRemove(pkgName, args = []) {
|
|
|
387
444
|
console.error(pc.dim(result.stderr));
|
|
388
445
|
return;
|
|
389
446
|
}
|
|
390
|
-
spinner.succeed(pc.green(`
|
|
447
|
+
spinner.succeed(pc.green(`removed ${pc.bold(pkgName)}`));
|
|
391
448
|
console.log(pc.dim("The plugin and its tools have been uninstalled.\n"));
|
|
392
449
|
}
|
|
393
450
|
// ---------------------------------------------------------------------------
|
|
@@ -419,7 +476,12 @@ export async function runPluginVerify(pkgName) {
|
|
|
419
476
|
// 7. MAIN CLI ROUTER FOR PLUGINS (User / Operations Mode)
|
|
420
477
|
// ---------------------------------------------------------------------------
|
|
421
478
|
export async function runPluginCli(args) {
|
|
422
|
-
|
|
479
|
+
if (args.includes("--refresh")) {
|
|
480
|
+
invalidateRegistryCache();
|
|
481
|
+
console.log(pc.green("[OK] Plugin registry cache refreshed.\n"));
|
|
482
|
+
}
|
|
483
|
+
const cleanArgs = args.filter((a) => a !== "--refresh");
|
|
484
|
+
const subcmd = cleanArgs[0]?.toLowerCase();
|
|
423
485
|
if (!subcmd || subcmd === "--help" || subcmd === "-h" || subcmd === "help") {
|
|
424
486
|
console.log();
|
|
425
487
|
console.log(boxen(pc.bold(pc.cyan(" WEAVETAB PLUGIN MANAGER ")), { padding: 0, borderStyle: "round", borderColor: "cyan" }));
|
|
@@ -428,7 +490,7 @@ export async function runPluginCli(args) {
|
|
|
428
490
|
console.log(` weavetab plugin <command> [options]`);
|
|
429
491
|
console.log();
|
|
430
492
|
console.log(pc.bold("COMMANDS"));
|
|
431
|
-
console.log(` ${pc.cyan("add <pkg>")}
|
|
493
|
+
console.log(` ${pc.cyan("add <pkg|path>")} Install a plugin by npm name or local path (e.g. @fy2ne/wt-youtube, ./my-plugin)`);
|
|
432
494
|
console.log(` ${pc.cyan("browse")} Interactive terminal explorer to browse & 1-click install`);
|
|
433
495
|
console.log(` ${pc.cyan("search [query]")} Search plugins across official registry & npm`);
|
|
434
496
|
console.log(` ${pc.cyan("list")} List all installed plugins and their registered tools`);
|
|
@@ -436,10 +498,11 @@ export async function runPluginCli(args) {
|
|
|
436
498
|
console.log(` ${pc.cyan("verify <pkg>")} Check trust tier and registry verification status`);
|
|
437
499
|
console.log();
|
|
438
500
|
console.log(pc.bold("DEVELOPER SDK (@weavetab/sdk)"));
|
|
439
|
-
console.log(` ${pc.dim("Scaffold a plugin:")} ${pc.cyan("npx
|
|
501
|
+
console.log(` ${pc.dim("Scaffold a plugin:")} ${pc.cyan("npx @weavetab/sdk init <name>")}`);
|
|
440
502
|
console.log(` ${pc.dim("Build & validate:")} ${pc.cyan("npx @weavetab/sdk build")}`);
|
|
441
503
|
console.log();
|
|
442
|
-
console.log(pc.bold("OPTIONS (for 'add')"));
|
|
504
|
+
console.log(pc.bold("OPTIONS (for 'add' / general)"));
|
|
505
|
+
console.log(` ${pc.cyan("--refresh")} Invalidate and refresh the verified plugin cache`);
|
|
443
506
|
console.log(` ${pc.cyan("--official, -O")} Prefer official @weavetab/plugin-<name>`);
|
|
444
507
|
console.log(` ${pc.cyan("--community, -C")} Prefer community package`);
|
|
445
508
|
console.log(` ${pc.cyan("--yes, -y")} Bypass interactive warning prompt for unverified plugins`);
|
|
@@ -448,13 +511,13 @@ export async function runPluginCli(args) {
|
|
|
448
511
|
}
|
|
449
512
|
if (subcmd === "init" || subcmd === "create" || subcmd === "new" || subcmd === "build" || subcmd === "manifest") {
|
|
450
513
|
console.log(pc.yellow(`\n[Weavetab] Plugin authoring commands belong to the developer SDK (@weavetab/sdk).`));
|
|
451
|
-
console.log(pc.dim("To scaffold a new plugin: ") + pc.cyan("npx
|
|
514
|
+
console.log(pc.dim("To scaffold a new plugin: ") + pc.cyan("npx @weavetab/sdk init <name>"));
|
|
452
515
|
console.log(pc.dim("To build/validate a plugin: ") + pc.cyan("npx @weavetab/sdk build"));
|
|
453
516
|
console.log(pc.dim("SDK documentation: ") + pc.cyan("https://github.com/weavetab/sdk\n"));
|
|
454
517
|
return;
|
|
455
518
|
}
|
|
456
519
|
if (subcmd === "add" || subcmd === "install" || subcmd === "i") {
|
|
457
|
-
await runPluginAdd(
|
|
520
|
+
await runPluginAdd(cleanArgs[1], cleanArgs.slice(2));
|
|
458
521
|
return;
|
|
459
522
|
}
|
|
460
523
|
if (subcmd === "browse" || subcmd === "explore") {
|
|
@@ -462,7 +525,7 @@ export async function runPluginCli(args) {
|
|
|
462
525
|
return;
|
|
463
526
|
}
|
|
464
527
|
if (subcmd === "search" || subcmd === "find") {
|
|
465
|
-
await runPluginSearch(
|
|
528
|
+
await runPluginSearch(cleanArgs[1]);
|
|
466
529
|
return;
|
|
467
530
|
}
|
|
468
531
|
if (subcmd === "list" || subcmd === "ls") {
|
|
@@ -470,11 +533,11 @@ export async function runPluginCli(args) {
|
|
|
470
533
|
return;
|
|
471
534
|
}
|
|
472
535
|
if (subcmd === "remove" || subcmd === "rm" || subcmd === "uninstall") {
|
|
473
|
-
await runPluginRemove(
|
|
536
|
+
await runPluginRemove(cleanArgs[1], cleanArgs.slice(2));
|
|
474
537
|
return;
|
|
475
538
|
}
|
|
476
539
|
if (subcmd === "verify" || subcmd === "check") {
|
|
477
|
-
await runPluginVerify(
|
|
540
|
+
await runPluginVerify(cleanArgs[1]);
|
|
478
541
|
return;
|
|
479
542
|
}
|
|
480
543
|
console.log(pc.red(`[ERROR] Unknown plugin subcommand: "${subcmd}"`));
|
package/dist/cli/updater.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare function checkForUpdates(): Promise<void>;
|
|
1
|
+
export declare function checkForUpdates(force?: boolean): Promise<void>;
|