@farmslot/adapter-web 0.6.1 → 0.6.2
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 +5 -0
- package/dist/launch-browser.d.cts +19 -0
- package/package.json +1 -1
- package/src/launch-browser.cjs +48 -20
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,11 @@ All notable changes to `@farmslot/adapter-web` are tracked here.
|
|
|
6
6
|
|
|
7
7
|
- Active-development baseline; add user-facing changes here before release or package publication.
|
|
8
8
|
|
|
9
|
+
## 0.6.2 - 2026-10-08
|
|
10
|
+
|
|
11
|
+
- Fix: `launch-browser` no longer builds its `node -e` CDP request from the port, path or target id. They reach the child as argv, `cdpHttp` takes only an integer port from 1 to 65535 and the paths it uses (`/json/list`, `/json/version`, `/json/close/<id>`), and closing duplicate home tabs skips, with a warning, a target id from `/json/list` that is not a CDP id (letters, digits, `-`). Before, a hostile or substituted local CDP endpoint could run code in that child through a crafted target id. `cdpHttp` and `pruneExtraHomeTabs` are exported for tests. ([#859](https://github.com/deeeed/farmslot/pull/859))
|
|
12
|
+
- Publish with adapter-sdk 0.7.1 and recipe-runner 0.27.0 so consumers share one adapter-sdk and one recipe-runner copy.
|
|
13
|
+
|
|
9
14
|
## 0.6.1 - 2026-10-08
|
|
10
15
|
|
|
11
16
|
- `web-dapp` refuses a headful launch while the macOS login session is locked (`SESSION_LOCKED`, with the unlock step), before starting, reusing or stopping a browser, as mm-harness does since its #363 (0.6.0 shipped `web-dapp` without it). `macosSessionLocked()`, `parseSessionLocked()`, `SESSION_LOCKED_MESSAGE` and `SESSION_LOCKED_USER_ACTION` are exported.
|
|
@@ -116,3 +116,22 @@ export function homeTabsToClose(targets: unknown[], { extensionId, homePage, def
|
|
|
116
116
|
id: string;
|
|
117
117
|
url: string;
|
|
118
118
|
}[];
|
|
119
|
+
/**
|
|
120
|
+
* GET a CDP HTTP path on 127.0.0.1. Returns the body, or null when the port or
|
|
121
|
+
* path is not one the launcher uses, or the request fails.
|
|
122
|
+
* @param {number} port
|
|
123
|
+
* @param {string} pathname `/json/list`, `/json/version` or `/json/close/<target id>`
|
|
124
|
+
* @returns {string | null}
|
|
125
|
+
*/
|
|
126
|
+
export function cdpHttp(port: number, pathname: string): string | null;
|
|
127
|
+
/**
|
|
128
|
+
* Close the extra `homePage` tabs and stray blank tabs the browser on `port` has.
|
|
129
|
+
* @param {number} port CDP port on 127.0.0.1.
|
|
130
|
+
* @param {string} extensionDir The unpacked extension, for its id.
|
|
131
|
+
* @param {{ homePage?: string, defaultTitle?: string }} options
|
|
132
|
+
* @returns {string[]} What could not be done, one line each; empty when all went well.
|
|
133
|
+
*/
|
|
134
|
+
export function pruneExtraHomeTabs(port: number, extensionDir: string, { homePage, defaultTitle }: {
|
|
135
|
+
homePage?: string;
|
|
136
|
+
defaultTitle?: string;
|
|
137
|
+
}): string[];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@farmslot/adapter-web",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.2",
|
|
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": {
|
package/src/launch-browser.cjs
CHANGED
|
@@ -643,25 +643,39 @@ function extensionPageUrl(extensionDir, page) {
|
|
|
643
643
|
return id ? `chrome-extension://${id}/${page}` : '';
|
|
644
644
|
}
|
|
645
645
|
|
|
646
|
+
// The CDP HTTP paths the launcher reads: the target list, the version, or one
|
|
647
|
+
// target to close. CDP target ids are hex or UUIDs.
|
|
648
|
+
const CDP_HTTP_PATH = /^\/json\/(?:list|version|close\/[A-Za-z0-9-]+)$/u;
|
|
649
|
+
const CDP_TARGET_ID = /^[A-Za-z0-9-]+$/u;
|
|
650
|
+
// A child node keeps cdpHttp synchronous. The port and path reach it as argv,
|
|
651
|
+
// never as script source, so a path cannot change what it runs.
|
|
652
|
+
const CDP_HTTP_SCRIPT = `
|
|
653
|
+
const http = require('http');
|
|
654
|
+
const [port, pathname] = process.argv.slice(1);
|
|
655
|
+
http.get({ host: '127.0.0.1', port: Number(port), path: pathname }, (res) => {
|
|
656
|
+
let body = '';
|
|
657
|
+
res.on('data', (chunk) => { body += chunk; });
|
|
658
|
+
res.on('end', () => {
|
|
659
|
+
process.stdout.write(body);
|
|
660
|
+
process.exitCode = res.statusCode === 200 ? 0 : 1;
|
|
661
|
+
});
|
|
662
|
+
}).on('error', () => process.exit(1));
|
|
663
|
+
`;
|
|
664
|
+
|
|
665
|
+
/**
|
|
666
|
+
* GET a CDP HTTP path on 127.0.0.1. Returns the body, or null when the port or
|
|
667
|
+
* path is not one the launcher uses, or the request fails.
|
|
668
|
+
* @param {number} port
|
|
669
|
+
* @param {string} pathname `/json/list`, `/json/version` or `/json/close/<target id>`
|
|
670
|
+
* @returns {string | null}
|
|
671
|
+
*/
|
|
646
672
|
function cdpHttp(port, pathname) {
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
http.get('http://127.0.0.1:${Number(port)}${pathname}', (res) => {
|
|
654
|
-
let body = '';
|
|
655
|
-
res.on('data', (chunk) => { body += chunk; });
|
|
656
|
-
res.on('end', () => {
|
|
657
|
-
process.stdout.write(body);
|
|
658
|
-
process.exitCode = res.statusCode === 200 ? 0 : 1;
|
|
659
|
-
});
|
|
660
|
-
}).on('error', () => process.exit(1));
|
|
661
|
-
`,
|
|
662
|
-
],
|
|
663
|
-
{ encoding: 'utf8', timeout: 4000 },
|
|
664
|
-
);
|
|
673
|
+
if (!Number.isInteger(port) || port < 1 || port > 65535) return null;
|
|
674
|
+
if (typeof pathname !== 'string' || !CDP_HTTP_PATH.test(pathname)) return null;
|
|
675
|
+
const result = spawnSync(process.execPath, ['-e', CDP_HTTP_SCRIPT, String(port), pathname], {
|
|
676
|
+
encoding: 'utf8',
|
|
677
|
+
timeout: 4000,
|
|
678
|
+
});
|
|
665
679
|
if (result.status !== 0) return null;
|
|
666
680
|
return result.stdout;
|
|
667
681
|
}
|
|
@@ -710,6 +724,13 @@ function homeTabsToClose(targets, { extensionId, homePage, defaultTitle }) {
|
|
|
710
724
|
// readiness converges home tabs again, so a pruning failure must not fail the
|
|
711
725
|
// launch. Every failure is returned so the launch log says why a duplicate tab
|
|
712
726
|
// survived instead of hiding it.
|
|
727
|
+
/**
|
|
728
|
+
* Close the extra `homePage` tabs and stray blank tabs the browser on `port` has.
|
|
729
|
+
* @param {number} port CDP port on 127.0.0.1.
|
|
730
|
+
* @param {string} extensionDir The unpacked extension, for its id.
|
|
731
|
+
* @param {{ homePage?: string, defaultTitle?: string }} options
|
|
732
|
+
* @returns {string[]} What could not be done, one line each; empty when all went well.
|
|
733
|
+
*/
|
|
713
734
|
function pruneExtraHomeTabs(port, extensionDir, { homePage, defaultTitle }) {
|
|
714
735
|
if (!homePage) return [];
|
|
715
736
|
const extensionId = extensionIdFromExtensionDir(extensionDir);
|
|
@@ -725,10 +746,17 @@ function pruneExtraHomeTabs(port, extensionDir, { homePage, defaultTitle }) {
|
|
|
725
746
|
if (!Array.isArray(targets)) return ['/json/list did not return a target list'];
|
|
726
747
|
const problems = [];
|
|
727
748
|
for (const target of homeTabsToClose(targets, { extensionId, homePage, defaultTitle })) {
|
|
728
|
-
|
|
749
|
+
// The id comes from the endpoint's own response: close only a CDP-shaped one.
|
|
750
|
+
if (!CDP_TARGET_ID.test(target.id)) {
|
|
751
|
+
problems.push(
|
|
752
|
+
`skipped target with an invalid id: ${JSON.stringify(target.id)} (${target.url})`,
|
|
753
|
+
);
|
|
754
|
+
continue;
|
|
755
|
+
}
|
|
756
|
+
if (cdpHttp(port, `/json/close/${encodeURIComponent(target.id)}`) === null)
|
|
729
757
|
problems.push(`could not close ${target.url} (${target.id})`);
|
|
730
758
|
}
|
|
731
759
|
return problems;
|
|
732
760
|
}
|
|
733
761
|
|
|
734
|
-
module.exports = { launchBrowser, homeTabsToClose };
|
|
762
|
+
module.exports = { launchBrowser, homeTabsToClose, cdpHttp, pruneExtraHomeTabs };
|