@farmslot/adapter-web 0.7.1 → 0.8.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/CHANGELOG.md +10 -0
- package/dist/web-dapp/lib/hud.d.mts +1 -0
- package/package.json +4 -4
- package/src/web-dapp/index.mjs +22 -0
- package/src/web-dapp/lib/hud.mjs +15 -3
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,16 @@ All notable changes to `@farmslot/adapter-web` are tracked here.
|
|
|
4
4
|
|
|
5
5
|
## Unreleased
|
|
6
6
|
|
|
7
|
+
- Active-development baseline; add user-facing changes here before release or package publication.
|
|
8
|
+
|
|
9
|
+
## 0.8.0 - 2026-10-13
|
|
10
|
+
|
|
11
|
+
- Honor HUD top/bottom placement through automatic progress and capture the painted final outcome.
|
|
12
|
+
|
|
13
|
+
## 0.7.2 - 2026-10-12
|
|
14
|
+
|
|
15
|
+
- Record web-dapp recipes through the shared recorder, selecting only the owned browser on the requested CDP port.
|
|
16
|
+
|
|
7
17
|
## 0.7.1 - 2026-10-12
|
|
8
18
|
|
|
9
19
|
- Update shared runtime dependencies for corrected keyboard events and bounded log observations.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@farmslot/adapter-web",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "Web platform adapter for Farmslot recipes: resolve and launch an isolated Chromium, own its CDP port, load unpacked extensions, and keep macOS focus.",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"exports": {
|
|
@@ -83,9 +83,9 @@
|
|
|
83
83
|
"prepublishOnly": "node ../../scripts/quality/check-farmslot-package-readiness.mjs --publish --packages @farmslot/adapter-web"
|
|
84
84
|
},
|
|
85
85
|
"dependencies": {
|
|
86
|
-
"@farmslot/adapter-sdk": "0.
|
|
87
|
-
"@farmslot/protocol": "0.
|
|
88
|
-
"@farmslot/recipe-runner": "0.
|
|
86
|
+
"@farmslot/adapter-sdk": "0.10.0",
|
|
87
|
+
"@farmslot/protocol": "0.41.0",
|
|
88
|
+
"@farmslot/recipe-runner": "0.32.0",
|
|
89
89
|
"ws": "^8.18.0"
|
|
90
90
|
},
|
|
91
91
|
"devDependencies": {
|
package/src/web-dapp/index.mjs
CHANGED
|
@@ -17,7 +17,9 @@ import { fileURLToPath } from 'node:url';
|
|
|
17
17
|
|
|
18
18
|
import { recipeRuntimePath, shellQuote } from './lib/paths.mjs';
|
|
19
19
|
import { POLICY_ENV, webDappAdapterId, webDappPolicy } from './lib/policy.mjs';
|
|
20
|
+
import { cdpListenerPids, ownsProcess } from './lib/processes.mjs';
|
|
20
21
|
import { check, webDappReadiness } from './lib/readiness.mjs';
|
|
22
|
+
import { cdpPort, readBrowserState, webDappRuntimeDir } from './lib/runtime.mjs';
|
|
21
23
|
import { defaultSigner, loadSigners, SIGNER_MODULE_ENV } from './lib/signers.mjs';
|
|
22
24
|
import { resolveBrowser } from './launch.mjs';
|
|
23
25
|
|
|
@@ -324,6 +326,26 @@ export function createWebDappAdapter(factoryOptions = {}) {
|
|
|
324
326
|
sdkVersion: 1,
|
|
325
327
|
headless: false,
|
|
326
328
|
|
|
329
|
+
recording: {
|
|
330
|
+
async target(context) {
|
|
331
|
+
const port = cdpPort(context, context.env);
|
|
332
|
+
const state = await readBrowserState(context.projectRoot);
|
|
333
|
+
const pid = state?.browserPid;
|
|
334
|
+
if (
|
|
335
|
+
!Number.isInteger(pid) ||
|
|
336
|
+
pid <= 0 ||
|
|
337
|
+
state.cdpPort !== port ||
|
|
338
|
+
!ownsProcess('browser', pid, webDappRuntimeDir(context.projectRoot)) ||
|
|
339
|
+
!cdpListenerPids(port).includes(pid)
|
|
340
|
+
) {
|
|
341
|
+
throw new Error(
|
|
342
|
+
`Could not resolve the owned ${webDappAdapterId()} browser for recording on CDP ${port}. Next: launch and verify the selected slot browser.`,
|
|
343
|
+
);
|
|
344
|
+
}
|
|
345
|
+
return { kind: 'pid', pid };
|
|
346
|
+
},
|
|
347
|
+
},
|
|
348
|
+
|
|
327
349
|
resolveSlotPorts() {
|
|
328
350
|
// Ports come from the slot (Farmslot {{port}} / {{cdp_port}}) through
|
|
329
351
|
// --watcher-port / --cdp-port or the runtime context; nothing is claimed here.
|
package/src/web-dapp/lib/hud.mjs
CHANGED
|
@@ -13,7 +13,13 @@
|
|
|
13
13
|
import { readFileSync, renameSync, rmSync, writeFileSync } from 'node:fs';
|
|
14
14
|
import path from 'node:path';
|
|
15
15
|
|
|
16
|
-
import {
|
|
16
|
+
import {
|
|
17
|
+
HUD_PANEL_BOUNDS,
|
|
18
|
+
hudIntentDisplay,
|
|
19
|
+
hudPlacement,
|
|
20
|
+
hudSecondaryText,
|
|
21
|
+
waitForHudPaint,
|
|
22
|
+
} from '@farmslot/protocol/recipe';
|
|
17
23
|
|
|
18
24
|
export const HUD_ELEMENT = 'mm-harness-hud';
|
|
19
25
|
export const HUD_FILE = 'hud.json';
|
|
@@ -37,12 +43,14 @@ export function nextHudState(prior, node = {}, nodeId = '') {
|
|
|
37
43
|
const intent =
|
|
38
44
|
hudIntentDisplay(node.intent ?? node.text ?? node.detail).text || hudIntentDisplay(nodeId).text;
|
|
39
45
|
const id = hudSecondaryText(node.node_id ?? node.nodeId ?? nodeId, 80);
|
|
46
|
+
const position = hudPlacement(node.display) ?? previous?.position;
|
|
40
47
|
const state = {
|
|
41
48
|
seq: (previous?.seq ?? 0) + 1,
|
|
42
49
|
title: hudSecondaryText(node.title, 80) || previous?.title || 'Recipe run',
|
|
43
50
|
current: previous?.current ?? null,
|
|
44
51
|
last: previous?.last ?? null,
|
|
45
52
|
finished: null,
|
|
53
|
+
...(position ? { position } : {}),
|
|
46
54
|
};
|
|
47
55
|
if (status === 'running') {
|
|
48
56
|
state.current = {
|
|
@@ -117,7 +125,7 @@ export function hudRenderExpression(state, appOrigin) {
|
|
|
117
125
|
if (location.origin !== ${JSON.stringify(String(appOrigin).replace(/\/$/u, ''))}) return { hud: false, reason: 'not-app-origin' };
|
|
118
126
|
const existing = document.documentElement.querySelector(':scope > ${HUD_ELEMENT}');
|
|
119
127
|
if (!state) { existing?.remove(); return { hud: false, cleared: true }; }
|
|
120
|
-
if (existing && existing.dataset.seq === String(state.seq)) return { hud: true, seq: state.seq, unchanged: true };
|
|
128
|
+
if (existing && existing.dataset.seq === String(state.seq)) return (${waitForHudPaint.toString()})(requestAnimationFrame).then(() => ({ hud: true, seq: state.seq, unchanged: true }));
|
|
121
129
|
existing?.remove();
|
|
122
130
|
const host = document.createElement('${HUD_ELEMENT}');
|
|
123
131
|
host.dataset.seq = String(state.seq);
|
|
@@ -129,6 +137,10 @@ export function hudRenderExpression(state, appOrigin) {
|
|
|
129
137
|
const el = (tag, cls, value) => { const node = document.createElement(tag); if (cls) node.className = cls; if (value != null) node.textContent = value; return node; };
|
|
130
138
|
const style = el('style', null, ${JSON.stringify(STYLE)});
|
|
131
139
|
const box = el('div', 'hud');
|
|
140
|
+
if (state.position) {
|
|
141
|
+
box.style.top = state.position === 'top' ? '12px' : 'auto';
|
|
142
|
+
box.style.bottom = state.position === 'bottom' ? '12px' : 'auto';
|
|
143
|
+
}
|
|
132
144
|
const intentStyle = ${JSON.stringify(hudIntentDisplay('').webStyle)};
|
|
133
145
|
const head = el('div', 'row');
|
|
134
146
|
head.append(el('span', 'title', state.title));
|
|
@@ -153,6 +165,6 @@ export function hudRenderExpression(state, appOrigin) {
|
|
|
153
165
|
if (state.last && state.last.error) box.append(el('div', 'last error', state.last.error));
|
|
154
166
|
root.append(style, box);
|
|
155
167
|
document.documentElement.append(host);
|
|
156
|
-
return { hud: true, seq: state.seq };
|
|
168
|
+
return (${waitForHudPaint.toString()})(requestAnimationFrame).then(() => ({ hud: true, seq: state.seq }));
|
|
157
169
|
})()`;
|
|
158
170
|
}
|