@weavetab/plugin-realtime 0.0.0-stage → 0.2.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.
@@ -0,0 +1,59 @@
1
+ import { run, arm } from "./page.js";
2
+ import { canvasSnap } from "./canvas.js";
3
+ import { statusTool } from "./reflex.js";
4
+ const RECENT = `((win) => {
5
+ const rt = window.__wt_rt;
6
+ if (!rt) return { click: null, last_mut_ms_ago: null, fresh: [] };
7
+ const now = performance.now();
8
+ const clk = rt.click && now - rt.click.t < 15000
9
+ ? { x: Math.round(rt.click.x), y: Math.round(rt.click.y), age_ms: Math.round(now - rt.click.t) }
10
+ : null;
11
+ const sel = 'button, a[href], input, select, textarea, summary, dialog[open], [role=button], [role=link], [role=menuitem], [role=tab], [role=dialog], [role=alertdialog], [onclick]';
12
+ const fresh = [];
13
+ for (const el of document.querySelectorAll(sel)) {
14
+ let b;
15
+ for (let n = el; n && b === undefined; n = n.parentElement) b = rt.born.get(n);
16
+ if (b === undefined || now - b > win) continue;
17
+ const r = el.getBoundingClientRect();
18
+ if (r.width < 2 || r.height < 2) continue;
19
+ const s = getComputedStyle(el);
20
+ if (s.visibility === 'hidden' || s.display === 'none') continue;
21
+ const label = (el.getAttribute('aria-label') || el.innerText || el.value || el.title || '').trim().replace(/\\s+/g, ' ').slice(0, 60);
22
+ const dist = clk ? Math.round(Math.hypot(r.left + r.width / 2 - clk.x, r.top + r.height / 2 - clk.y)) : null;
23
+ fresh.push({
24
+ tag: el.getAttribute('role') || el.tagName.toLowerCase(),
25
+ id: el.id || null,
26
+ wt_id: el.dataset && el.dataset.wtId || null,
27
+ label,
28
+ age_ms: Math.round(now - b),
29
+ dist_from_click_px: dist
30
+ });
31
+ if (fresh.length >= 25) break;
32
+ }
33
+ fresh.sort((a, b) => a.age_ms - b.age_ms);
34
+ return {
35
+ url: location.href,
36
+ click: clk,
37
+ last_mut_ms_ago: rt.lastMut ? Math.round(now - rt.lastMut) : null,
38
+ fresh
39
+ };
40
+ })`;
41
+ export async function readFeed(sess, winMs) {
42
+ if (!sess?.Runtime)
43
+ return { active_tab: false };
44
+ await arm(sess);
45
+ const [st, dom, cv] = await Promise.all([
46
+ statusTool(sess, 0),
47
+ run(sess, `${RECENT}(${winMs})`),
48
+ canvasSnap(sess),
49
+ ]);
50
+ return {
51
+ url: dom.url,
52
+ probe: st.probe,
53
+ last_click: dom.click,
54
+ last_mut_ms_ago: dom.last_mut_ms_ago,
55
+ recent_elements: dom.fresh,
56
+ reflexes: st.reflexes,
57
+ canvas: cv || st.canvas,
58
+ };
59
+ }
package/package.json CHANGED
@@ -1,6 +1,53 @@
1
1
  {
2
2
  "name": "@weavetab/plugin-realtime",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.2.0",
4
+ "description": "Causal age/proximity tags for browser_map, 60Hz in-page reflex rules, canvas entity sampling and a bounded click loop for Weavetab.",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/index.js",
11
+ "types": "./dist/index.d.ts"
12
+ }
13
+ },
14
+ "files": [
15
+ "dist",
16
+ "weavetab.json",
17
+ "README.md",
18
+ "LICENSE"
19
+ ],
20
+ "scripts": {
21
+ "build": "tsc",
22
+ "typecheck": "tsc --noEmit",
23
+ "preflight": "weavetab preflight",
24
+ "test": "node --test test/*.test.mjs",
25
+ "prepublishOnly": "npm run build"
26
+ },
27
+ "dependencies": {
28
+ "@weavetab/sdk": "^2.6.1"
29
+ },
30
+ "devDependencies": {
31
+ "@types/node": "^22.10.0",
32
+ "chrome-remote-interface": "^0.33.2",
33
+ "typescript": "^5.5.0"
34
+ },
35
+ "keywords": [
36
+ "weavetab",
37
+ "weavetab-plugin",
38
+ "cdp",
39
+ "canvas",
40
+ "browser-automation"
41
+ ],
42
+ "author": {
43
+ "name": "fy2ne",
44
+ "url": "https://fy2ne.me"
45
+ },
46
+ "license": "Apache-2.0",
47
+ "repository": { "type": "git", "url": "git+https://github.com/Weavetab/Realtime.git" },
48
+ "bugs": { "url": "https://github.com/Weavetab/Realtime/issues" },
49
+ "homepage": "https://github.com/Weavetab/Realtime#readme",
50
+ "engines": {
51
+ "node": ">=18.0.0"
52
+ }
53
+ }
package/weavetab.json ADDED
@@ -0,0 +1,158 @@
1
+ {
2
+ "$schema": "https://weavetab.dev/schema/plugin.json",
3
+ "name": "@weavetab/plugin-realtime",
4
+ "displayName": "Realtime",
5
+ "version": "0.2.0",
6
+ "description": "Causal tags for browser_map, in-page reflex rules, canvas entity sampling and a bounded click loop for Weavetab.",
7
+ "entry": "dist/index.js",
8
+ "engines": {
9
+ "weavetab": ">=2.6.1",
10
+ "node": ">=18.0.0"
11
+ },
12
+ "environments": ["mcp"],
13
+ "authors": [{ "name": "fy2ne", "role": "Author", "url": "https://fy2ne.me" }],
14
+ "license": "Apache-2.0",
15
+ "tags": ["Canvas", "Games", "CDP", "Automation", "Community"],
16
+ "defaultConfig": { "causalWindowMs": 4000 },
17
+ "links": { "github": "https://github.com/Weavetab/Realtime", "npm": "https://www.npmjs.com/package/@weavetab/plugin-realtime" },
18
+ "permissions": {
19
+ "cdp": {
20
+ "evaluate": true,
21
+ "input": true,
22
+ "reason": "Injects a MutationObserver, canvas draw recorder and reflex loop into the page, and sends real mouse clicks for realtime_autopilot."
23
+ },
24
+ "tools": {
25
+ "add": true,
26
+ "reason": "Registers realtime_canvas, realtime_reflex, realtime_autopilot and realtime_status."
27
+ },
28
+ "prompts": {
29
+ "add": true,
30
+ "reason": "Registers /realtime_watch to guide setting up 60Hz DOM or canvas reflexes with live page state."
31
+ },
32
+ "resources": {
33
+ "add": true,
34
+ "reason": "Registers @realtime_feed (realtime://feed) for live reflex, causal DOM, and canvas entity telemetry."
35
+ },
36
+ "hooks": {
37
+ "onBeforeAction": true,
38
+ "onAfterAction": true,
39
+ "reason": "Installs the page probe before any tool runs and appends {new ...} tags to browser_map output."
40
+ },
41
+ "network": {
42
+ "hosts": ["127.0.0.1", "localhost"],
43
+ "reason": "realtime_autopilot can POST candidates to a model_url the caller supplies (meant for a local OpenAI-compatible server)."
44
+ }
45
+ },
46
+ "tools": [
47
+ {
48
+ "name": "realtime_canvas",
49
+ "description": "Sample what a 2D canvas game draws: entities with signature, bbox and px/frame velocity.",
50
+ "inputSchema": {
51
+ "type": "object",
52
+ "properties": {
53
+ "action": { "type": "string", "enum": ["sample", "attach", "detach"], "default": "sample" },
54
+ "frames": { "type": "integer", "minimum": 2, "maximum": 15, "description": "frames to watch, default 6" }
55
+ }
56
+ }
57
+ },
58
+ {
59
+ "name": "realtime_reflex",
60
+ "description": "Register, list or remove rules that run inside the page every animation frame.",
61
+ "inputSchema": {
62
+ "type": "object",
63
+ "properties": {
64
+ "action": { "type": "string", "enum": ["add", "remove", "list", "clear"], "default": "add" },
65
+ "id": { "type": "string" },
66
+ "when": {
67
+ "type": "object",
68
+ "properties": {
69
+ "selector": { "type": "string" },
70
+ "text": { "type": "string" },
71
+ "only_new": { "type": "boolean" },
72
+ "enabled": { "type": "boolean" },
73
+ "canvas": {
74
+ "type": "object",
75
+ "properties": {
76
+ "names": { "type": "object", "additionalProperties": { "type": "string" } },
77
+ "js": { "type": "string" }
78
+ },
79
+ "required": ["names", "js"]
80
+ }
81
+ }
82
+ },
83
+ "do": {
84
+ "type": "object",
85
+ "properties": {
86
+ "type": { "type": "string", "enum": ["click", "click_xy", "key", "js"] },
87
+ "x": { "type": "number" },
88
+ "y": { "type": "number" },
89
+ "key": { "type": "string" },
90
+ "hold_ms": { "type": "number" },
91
+ "code": { "type": "string" }
92
+ },
93
+ "required": ["type"]
94
+ },
95
+ "cooldown_ms": { "type": "number" },
96
+ "max_hits": { "type": "number" },
97
+ "ttl_ms": { "type": "number" },
98
+ "stop_selector": { "type": "string" }
99
+ }
100
+ }
101
+ },
102
+ {
103
+ "name": "realtime_autopilot",
104
+ "description": "Bounded click loop for short UI sub-goals, ranked by label/goal word overlap and element recency.",
105
+ "inputSchema": {
106
+ "type": "object",
107
+ "properties": {
108
+ "goal": { "type": "string", "minLength": 3 },
109
+ "max_steps": { "type": "integer", "minimum": 1, "maximum": 25 },
110
+ "stop_when_selector": { "type": "string" },
111
+ "stop_when_text": { "type": "string" },
112
+ "stop_when_url": { "type": "string" },
113
+ "avoid_labels": { "type": "string" },
114
+ "model_url": { "type": "string", "format": "uri" },
115
+ "model": { "type": "string" },
116
+ "model_timeout_ms": { "type": "number" }
117
+ },
118
+ "required": ["goal"]
119
+ }
120
+ },
121
+ {
122
+ "name": "realtime_status",
123
+ "description": "Probe, canvas recorder and reflex state for the current page; wait_ms blocks until active reflexes finish.",
124
+ "inputSchema": {
125
+ "type": "object",
126
+ "properties": { "wait_ms": { "type": "number" } }
127
+ }
128
+ }
129
+ ],
130
+ "prompts": [
131
+ {
132
+ "name": "realtime_watch",
133
+ "title": "Watch & React (60Hz Reflex)",
134
+ "description": "Set up an in-page 60Hz reflex rule or canvas game loop and wait until it finishes.",
135
+ "arguments": [
136
+ {
137
+ "name": "goal",
138
+ "description": "What to watch for and trigger (e.g. 'click Claim as soon as it appears' or 'jump over green obstacles in the canvas game')",
139
+ "required": true
140
+ },
141
+ {
142
+ "name": "mode",
143
+ "description": "'dom', 'canvas', or 'auto' (default 'auto')",
144
+ "required": false
145
+ }
146
+ ]
147
+ }
148
+ ],
149
+ "resources": [
150
+ {
151
+ "name": "realtime_feed",
152
+ "title": "Realtime Page Feed",
153
+ "uri": "realtime://feed",
154
+ "mimeType": "application/json",
155
+ "description": "Live snapshot of active 60Hz reflex rules, recently mutated DOM elements with click distance, and tracked Canvas2D entities."
156
+ }
157
+ ]
158
+ }