@comecaramelos/dsh-chrome-mcp 0.0.1 → 2.0.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 +44 -95
- package/assets/dsh-chrome-mcp-social.png +0 -0
- package/assets/icon.svg +71 -0
- package/assets/prelaunch-windows-chrome.html +277 -0
- package/lib/client.js +3005 -568
- package/lib/client.js.map +7 -0
- package/lib/host/apply.d.ts +40 -0
- package/lib/host/apply.d.ts.map +1 -0
- package/lib/host/apply.js +57 -0
- package/lib/host/apply.js.map +1 -0
- package/lib/host/bridge.d.ts +74 -0
- package/lib/host/bridge.d.ts.map +1 -0
- package/lib/host/bridge.js +168 -0
- package/lib/host/bridge.js.map +1 -0
- package/lib/host/catalog.d.ts +31 -0
- package/lib/host/catalog.d.ts.map +1 -0
- package/lib/host/catalog.js +92 -0
- package/lib/host/catalog.js.map +1 -0
- package/lib/host/commands/chrome-path.d.ts +4 -0
- package/lib/host/commands/chrome-path.d.ts.map +1 -0
- package/lib/host/commands/chrome-path.js +65 -0
- package/lib/host/commands/chrome-path.js.map +1 -0
- package/lib/host/commands/chrome-refresh.d.ts +17 -0
- package/lib/host/commands/chrome-refresh.d.ts.map +1 -0
- package/lib/host/commands/chrome-refresh.js +37 -0
- package/lib/host/commands/chrome-refresh.js.map +1 -0
- package/lib/host/commands/index.d.ts +26 -0
- package/lib/host/commands/index.d.ts.map +1 -0
- package/lib/host/commands/index.js +41 -0
- package/lib/host/commands/index.js.map +1 -0
- package/lib/host/constants.d.ts +306 -0
- package/lib/host/constants.d.ts.map +1 -0
- package/lib/host/constants.js +319 -0
- package/lib/host/constants.js.map +1 -0
- package/lib/host/controller/_trace.d.ts +3 -0
- package/lib/host/controller/_trace.d.ts.map +1 -0
- package/lib/host/controller/_trace.js +14 -0
- package/lib/host/controller/_trace.js.map +1 -0
- package/lib/host/controller/bridge-run.d.ts +40 -0
- package/lib/host/controller/bridge-run.d.ts.map +1 -0
- package/lib/host/controller/bridge-run.js +85 -0
- package/lib/host/controller/bridge-run.js.map +1 -0
- package/lib/host/controller/captures.d.ts +15 -0
- package/lib/host/controller/captures.d.ts.map +1 -0
- package/lib/host/controller/captures.js +85 -0
- package/lib/host/controller/captures.js.map +1 -0
- package/lib/host/controller/commits.d.ts +4 -0
- package/lib/host/controller/commits.d.ts.map +1 -0
- package/lib/host/controller/commits.js +131 -0
- package/lib/host/controller/commits.js.map +1 -0
- package/lib/host/controller/connections.d.ts +38 -0
- package/lib/host/controller/connections.d.ts.map +1 -0
- package/lib/host/controller/connections.js +71 -0
- package/lib/host/controller/connections.js.map +1 -0
- package/lib/host/controller/controller.d.ts +35 -0
- package/lib/host/controller/controller.d.ts.map +1 -0
- package/lib/host/controller/controller.js +234 -0
- package/lib/host/controller/controller.js.map +1 -0
- package/lib/host/controller/index.d.ts +30 -0
- package/lib/host/controller/index.d.ts.map +1 -0
- package/lib/host/controller/index.js +27 -0
- package/lib/host/controller/index.js.map +1 -0
- package/lib/host/controller/notes.d.ts +26 -0
- package/lib/host/controller/notes.d.ts.map +1 -0
- package/lib/host/controller/notes.js +44 -0
- package/lib/host/controller/notes.js.map +1 -0
- package/lib/host/controller/persist.d.ts +16 -0
- package/lib/host/controller/persist.d.ts.map +1 -0
- package/lib/host/controller/persist.js +98 -0
- package/lib/host/controller/persist.js.map +1 -0
- package/lib/host/controller/publish.d.ts +4 -0
- package/lib/host/controller/publish.d.ts.map +1 -0
- package/lib/host/controller/publish.js +56 -0
- package/lib/host/controller/publish.js.map +1 -0
- package/lib/host/controller/runs.d.ts +80 -0
- package/lib/host/controller/runs.d.ts.map +1 -0
- package/lib/host/controller/runs.js +394 -0
- package/lib/host/controller/runs.js.map +1 -0
- package/lib/host/controller/state.d.ts +172 -0
- package/lib/host/controller/state.d.ts.map +1 -0
- package/lib/host/controller/state.js +2 -0
- package/lib/host/controller/state.js.map +1 -0
- package/lib/host/controller/windows-run.d.ts +17 -0
- package/lib/host/controller/windows-run.d.ts.map +1 -0
- package/lib/host/controller/windows-run.js +109 -0
- package/lib/host/controller/windows-run.js.map +1 -0
- package/lib/host/discovery/candidates.d.ts +47 -0
- package/lib/host/discovery/candidates.d.ts.map +1 -0
- package/lib/host/discovery/candidates.js +68 -0
- package/lib/host/discovery/candidates.js.map +1 -0
- package/lib/host/discovery/index.d.ts +19 -0
- package/lib/host/discovery/index.d.ts.map +1 -0
- package/lib/host/discovery/index.js +19 -0
- package/lib/host/discovery/index.js.map +1 -0
- package/lib/host/discovery/probe.d.ts +29 -0
- package/lib/host/discovery/probe.d.ts.map +1 -0
- package/lib/host/discovery/probe.js +155 -0
- package/lib/host/discovery/probe.js.map +1 -0
- package/lib/host/discovery/scan.d.ts +72 -0
- package/lib/host/discovery/scan.d.ts.map +1 -0
- package/lib/host/discovery/scan.js +195 -0
- package/lib/host/discovery/scan.js.map +1 -0
- package/lib/host/flags.d.ts +65 -0
- package/lib/host/flags.d.ts.map +1 -0
- package/lib/host/flags.js +186 -0
- package/lib/host/flags.js.map +1 -0
- package/lib/host/kind.d.ts +45 -0
- package/lib/host/kind.d.ts.map +1 -0
- package/lib/host/kind.js +251 -0
- package/lib/host/kind.js.map +1 -0
- package/lib/host/manager.d.ts +21 -0
- package/lib/host/manager.d.ts.map +1 -0
- package/lib/host/manager.js +113 -0
- package/lib/host/manager.js.map +1 -0
- package/lib/host/migration.d.ts +26 -0
- package/lib/host/migration.d.ts.map +1 -0
- package/lib/host/migration.js +127 -0
- package/lib/host/migration.js.map +1 -0
- package/lib/host/platform.d.ts +38 -0
- package/lib/host/platform.d.ts.map +1 -0
- package/lib/host/platform.js +64 -0
- package/lib/host/platform.js.map +1 -0
- package/lib/host/plugin-meta.d.ts +21 -0
- package/lib/host/plugin-meta.d.ts.map +1 -0
- package/lib/host/plugin-meta.js +21 -0
- package/lib/host/plugin-meta.js.map +1 -0
- package/lib/host/schema.d.ts +361 -0
- package/lib/host/schema.d.ts.map +1 -0
- package/lib/host/schema.js +196 -0
- package/lib/host/schema.js.map +1 -0
- package/lib/host/settings.d.ts +28 -0
- package/lib/host/settings.d.ts.map +1 -0
- package/lib/host/settings.js +91 -0
- package/lib/host/settings.js.map +1 -0
- package/lib/host/status.d.ts +25 -0
- package/lib/host/status.d.ts.map +1 -0
- package/lib/host/status.js +45 -0
- package/lib/host/status.js.map +1 -0
- package/lib/host/tool.d.ts +65 -0
- package/lib/host/tool.d.ts.map +1 -0
- package/lib/host/tool.js +52 -0
- package/lib/host/tool.js.map +1 -0
- package/lib/host/types/config.d.ts +101 -0
- package/lib/host/types/config.d.ts.map +1 -0
- package/lib/host/types/config.js +2 -0
- package/lib/host/types/config.js.map +1 -0
- package/lib/host/types/executable.d.ts +35 -0
- package/lib/host/types/executable.d.ts.map +1 -0
- package/lib/host/types/executable.js +9 -0
- package/lib/host/types/executable.js.map +1 -0
- package/lib/host/types/index.d.ts +24 -0
- package/lib/host/types/index.d.ts.map +1 -0
- package/lib/host/types/index.js +2 -0
- package/lib/host/types/index.js.map +1 -0
- package/lib/host/types/plugin.d.ts +38 -0
- package/lib/host/types/plugin.d.ts.map +1 -0
- package/lib/host/types/plugin.js +10 -0
- package/lib/host/types/plugin.js.map +1 -0
- package/lib/host/types/services.d.ts +26 -0
- package/lib/host/types/services.d.ts.map +1 -0
- package/lib/host/types/services.js +9 -0
- package/lib/host/types/services.js.map +1 -0
- package/lib/host/types/settings.d.ts +180 -0
- package/lib/host/types/settings.d.ts.map +1 -0
- package/lib/host/types/settings.js +2 -0
- package/lib/host/types/settings.js.map +1 -0
- package/lib/host/types/stderr.d.ts +51 -0
- package/lib/host/types/stderr.d.ts.map +1 -0
- package/lib/host/types/stderr.js +9 -0
- package/lib/host/types/stderr.js.map +1 -0
- package/lib/host/types/windows.d.ts +39 -0
- package/lib/host/types/windows.d.ts.map +1 -0
- package/lib/host/types/windows.js +9 -0
- package/lib/host/types/windows.js.map +1 -0
- package/lib/host/values.d.ts +204 -0
- package/lib/host/values.d.ts.map +1 -0
- package/lib/host/values.js +404 -0
- package/lib/host/values.js.map +1 -0
- package/lib/host/windows/connect-flags.d.ts +27 -0
- package/lib/host/windows/connect-flags.d.ts.map +1 -0
- package/lib/host/windows/connect-flags.js +97 -0
- package/lib/host/windows/connect-flags.js.map +1 -0
- package/lib/host/windows/index.d.ts +31 -0
- package/lib/host/windows/index.d.ts.map +1 -0
- package/lib/host/windows/index.js +30 -0
- package/lib/host/windows/index.js.map +1 -0
- package/lib/host/windows/landing.d.ts +47 -0
- package/lib/host/windows/landing.d.ts.map +1 -0
- package/lib/host/windows/landing.js +174 -0
- package/lib/host/windows/landing.js.map +1 -0
- package/lib/host/windows/link.d.ts +11 -0
- package/lib/host/windows/link.d.ts.map +1 -0
- package/lib/host/windows/link.js +52 -0
- package/lib/host/windows/link.js.map +1 -0
- package/lib/host/windows/paths.d.ts +64 -0
- package/lib/host/windows/paths.d.ts.map +1 -0
- package/lib/host/windows/paths.js +148 -0
- package/lib/host/windows/paths.js.map +1 -0
- package/lib/host/windows/run.d.ts +74 -0
- package/lib/host/windows/run.d.ts.map +1 -0
- package/lib/host/windows/run.js +211 -0
- package/lib/host/windows/run.js.map +1 -0
- package/lib/host/windows/spawn.d.ts +22 -0
- package/lib/host/windows/spawn.d.ts.map +1 -0
- package/lib/host/windows/spawn.js +73 -0
- package/lib/host/windows/spawn.js.map +1 -0
- package/lib/index.d.ts +102 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +102 -636
- package/lib/index.js.map +1 -0
- package/locale/en.json +6 -0
- package/locale/zh.json +6 -0
- package/package.json +57 -14
- package/cordis.patch.yml +0 -5
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Windows Chrome run — path mapping, locate, and profile/profile-root helpers.
|
|
3
|
+
*
|
|
4
|
+
* The pure half of the run: the drive-letter spelling mapped onto the WSL
|
|
5
|
+
* interop mount, the located executable, the Windows-native profile directory
|
|
6
|
+
* (never one rooted in the WSL checkout — 9P cannot carry Chrome's SQLite/
|
|
7
|
+
* singleton locks), the LOCALAPPDATA root the profile hangs under, and the
|
|
8
|
+
* project directory that roots the workspace `.chrome` view. Every one is
|
|
9
|
+
* injectable or takes its own answer so a test never depends on the machine it
|
|
10
|
+
* runs on. The orchestrator lives in ./run.ts; the workspace-view link in
|
|
11
|
+
* ./link.ts; the raw net/spawn seams in ./spawn.ts; the connect address and
|
|
12
|
+
* connect-mode flag in ./connect-flags.ts.
|
|
13
|
+
*/
|
|
14
|
+
import { execFileSync } from "node:child_process";
|
|
15
|
+
import { existsSync } from "node:fs";
|
|
16
|
+
import { WINDOWS_CHROME_CANDIDATES, WINDOWS_CHROME_PROFILE_DIRNAME } from "../constants.js";
|
|
17
|
+
/**
|
|
18
|
+
* The Windows spelling mapped onto the interop mount (`C:\Program Files\…` →
|
|
19
|
+
* `/mnt/c/Program Files/…`), which is the form WSL can stat. A path already
|
|
20
|
+
* spelled as the mount maps to itself — the mount spelling is what the WSL side
|
|
21
|
+
* can stat and therefore what a saved row or the row config may carry — and a
|
|
22
|
+
* path that is neither maps nowhere and yields "".
|
|
23
|
+
*
|
|
24
|
+
* A path may have been written or copied shell-style, where a space is escaped
|
|
25
|
+
* (`Program\ Files`). Such a backslash escapes the space that follows it and is
|
|
26
|
+
* not a separator, so it folds away to the plain space first; every other
|
|
27
|
+
* backslash is a Windows separator and folds to a slash. Turning one space
|
|
28
|
+
* escape blindly into a slash produced `Program/ Files` — a path that exists
|
|
29
|
+
* nowhere and answers `not-found` for a Chrome the run had actually located
|
|
30
|
+
* (measured live 2026-10-12 against a saved mount-spelled selection).
|
|
31
|
+
*/
|
|
32
|
+
export function windowsPathToMount(path) {
|
|
33
|
+
if (typeof path !== "string")
|
|
34
|
+
return "";
|
|
35
|
+
const candidate = path.trim().replace(/\\ /gu, " ").replace(/\\/gu, "/");
|
|
36
|
+
const windows = candidate.match(/^([a-z]):\/(.*)$/iu);
|
|
37
|
+
if (windows !== null)
|
|
38
|
+
return `/mnt/${windows[1].toLowerCase()}/${windows[2]}`;
|
|
39
|
+
const mounted = candidate.match(/^\/mnt\/([a-z])\/(.*)$/iu);
|
|
40
|
+
if (mounted === null)
|
|
41
|
+
return "";
|
|
42
|
+
return `/mnt/${mounted[1].toLowerCase()}/${mounted[2]}`;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Locate the Windows-side Chrome through the interop mount: the first candidate
|
|
46
|
+
* whose mount path exists, "" when this machine carries none of them. The
|
|
47
|
+
* returned path keeps its **Windows** spelling — it is what the `start`
|
|
48
|
+
* invocation takes and what the card names.
|
|
49
|
+
*/
|
|
50
|
+
export function findWindowsChromeExecutable(exists = (path) => existsSync(path), candidates = WINDOWS_CHROME_CANDIDATES) {
|
|
51
|
+
return findWindowsChromeExecutables(exists, candidates)[0] ?? "";
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Every Windows-side executable this host carries, in the list's own preference
|
|
55
|
+
* order and in its **Windows** spelling — the whole located set, not only the
|
|
56
|
+
* first. This is what the executable run folds into its candidate list, so the
|
|
57
|
+
* picker offers the Windows browser instead of hiding it. A machine that carries
|
|
58
|
+
* none of them answers nothing, so the list is what is installed here, never a
|
|
59
|
+
* fixed set of install locations. Nothing on the list is ever spawned from WSL
|
|
60
|
+
* (see `blockedForPlatform`); it is offered so that the **Prelaunch Windows
|
|
61
|
+
* Chrome** run can be pointed at the one the picker selected.
|
|
62
|
+
*/
|
|
63
|
+
export function findWindowsChromeExecutables(exists = (path) => existsSync(path), candidates = WINDOWS_CHROME_CANDIDATES) {
|
|
64
|
+
const found = [];
|
|
65
|
+
for (const candidate of Array.isArray(candidates) ? candidates : []) {
|
|
66
|
+
const path = typeof candidate === "string" ? candidate.trim() : "";
|
|
67
|
+
if (path === "" || found.includes(path))
|
|
68
|
+
continue;
|
|
69
|
+
if (exists(windowsPathToMount(path)))
|
|
70
|
+
found.push(path);
|
|
71
|
+
}
|
|
72
|
+
return found;
|
|
73
|
+
}
|
|
74
|
+
/** Cached answer of the Windows-side profile root (`windowsChromeLocalAppData`). */
|
|
75
|
+
let cachedLocalAppData;
|
|
76
|
+
/**
|
|
77
|
+
* The Windows-side profile directory: `<localAppData>\<dir>\.chrome[-<port>]`.
|
|
78
|
+
* The profile has to live on the Windows filesystem — Chrome takes real locks
|
|
79
|
+
* for its SQLite tables and its singleton socket, and the WSL-side directories
|
|
80
|
+
* Windows sees through the interop mount (9P) cannot carry them: a profile
|
|
81
|
+
* rooted there creates every table as a zero-byte file, never answers the
|
|
82
|
+
* singleton lock, and shows "an error occurred while opening your profile" on
|
|
83
|
+
* every launch (measured live 2026-10-04 against Chrome 154). "" answers when
|
|
84
|
+
* LOCALAPPDATA is not there.
|
|
85
|
+
*/
|
|
86
|
+
export function windowsChromeProfileDir(localAppData, leaf = ".chrome") {
|
|
87
|
+
const root = typeof localAppData === "string" ? localAppData.trim().replace(/[\\/]+$/u, "") : "";
|
|
88
|
+
if (root === "")
|
|
89
|
+
return "";
|
|
90
|
+
const profile = String(leaf ?? "").replace(/[\\/]+/gu, "");
|
|
91
|
+
if (profile === "")
|
|
92
|
+
return "";
|
|
93
|
+
return `${root}\\${WINDOWS_CHROME_PROFILE_DIRNAME}\\${profile}`;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Resolves `%LOCALAPPDATA%` on the Windows side through the Known Folder API
|
|
97
|
+
* (never the inherited environment — the interop hand-off drops it, so
|
|
98
|
+
* `echo %LOCALAPPDATA%` comes back empty), cached across the process:
|
|
99
|
+
* powershell answers it exactly once per host.
|
|
100
|
+
*/
|
|
101
|
+
export function windowsChromeLocalAppData() {
|
|
102
|
+
if (typeof cachedLocalAppData === "string")
|
|
103
|
+
return cachedLocalAppData;
|
|
104
|
+
try {
|
|
105
|
+
const out = execFileSync("/mnt/c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe", ["-NoProfile", "-NonInteractive", "-Command", "[Environment]::GetFolderPath('LocalApplicationData')"], { encoding: "utf8", timeout: 5000 });
|
|
106
|
+
const value = String(out).trim();
|
|
107
|
+
cachedLocalAppData = value;
|
|
108
|
+
return value;
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
cachedLocalAppData = "";
|
|
112
|
+
return "";
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* The directory the Windows run roots its profile view under — the project
|
|
117
|
+
* whose checkout the run answers: the row-config `cwd`, else the **live
|
|
118
|
+
* session workspace** (the run answers to the GUI the user is driving, not to
|
|
119
|
+
* whatever directory `dsh web` happened to be launched from — a run rooted at
|
|
120
|
+
* `~` links `~/.chrome` instead of the workspace), else the host's own cwd.
|
|
121
|
+
* @param configCwd - the row-config `cwd`, "" when unset.
|
|
122
|
+
* @param sessions - the sessions service, resolved optionally.
|
|
123
|
+
* @param hostCwd - the host process' cwd.
|
|
124
|
+
*/
|
|
125
|
+
export function windowsChromeProjectDir(configCwd, sessions, hostCwd) {
|
|
126
|
+
if (typeof configCwd === "string" && configCwd.trim() !== "")
|
|
127
|
+
return configCwd.trim();
|
|
128
|
+
if (sessions === null || typeof sessions !== "object")
|
|
129
|
+
return hostCwd;
|
|
130
|
+
const list = sessions.list;
|
|
131
|
+
if (typeof list !== "function")
|
|
132
|
+
return hostCwd;
|
|
133
|
+
let live = "";
|
|
134
|
+
let best = -1;
|
|
135
|
+
for (const session of (list.call(sessions) ?? [])) {
|
|
136
|
+
const header = session?.header;
|
|
137
|
+
const cwd = typeof header?.cwd === "string" ? header.cwd.trim() : "";
|
|
138
|
+
if (cwd === "")
|
|
139
|
+
continue;
|
|
140
|
+
const createdAt = Number.isFinite(header?.createdAt) ? Number(header?.createdAt) : 0;
|
|
141
|
+
if (createdAt >= best) {
|
|
142
|
+
best = createdAt;
|
|
143
|
+
live = cwd;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return live !== "" ? live : hostCwd;
|
|
147
|
+
}
|
|
148
|
+
//# sourceMappingURL=paths.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paths.js","sourceRoot":"","sources":["../../../src/host/windows/paths.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import type { WindowsChromeLinkAnswer, WindowsChromeRun } from "../types/index.js";
|
|
2
|
+
/** Everything {@link runWindowsChromeConnect} consults; every field defaults
|
|
3
|
+
* to what the running host actually is. */
|
|
4
|
+
export type WindowsChromeRunOptions = {
|
|
5
|
+
/** Locates the Windows executable through the interop mount. Defaults to
|
|
6
|
+
* `existsSync`. */
|
|
7
|
+
exists?: (path: string) => boolean;
|
|
8
|
+
/** The Windows-side profile root (%LOCALAPPDATA%). Defaults to the Known
|
|
9
|
+
* Folder API answer, cached per host. */
|
|
10
|
+
localAppData?: string;
|
|
11
|
+
/** The workspace `.chrome` view refresh; defaults to `linkWindowsChromeProfile`.
|
|
12
|
+
* Injected so tests never touch the real filesystem. */
|
|
13
|
+
linkProfile?: (linkPath: string, profileWin: string) => WindowsChromeLinkAnswer;
|
|
14
|
+
/** The project directory the Windows-side profile directory hangs under.
|
|
15
|
+
* Defaults to `process.cwd()`. */
|
|
16
|
+
cwd?: string;
|
|
17
|
+
/** The Windows executables to look for, in preference order. */
|
|
18
|
+
candidates?: readonly string[];
|
|
19
|
+
/** The extra-flags list the bridge currently launches with. A live
|
|
20
|
+
* `--browserUrl` entry inside it is re-checked instead of re-launched
|
|
21
|
+
* (idempotency: a second click never opens a second window or duplicates
|
|
22
|
+
* the flag). */
|
|
23
|
+
currentFlags?: readonly string[];
|
|
24
|
+
/** The debugging-port pool: first entry is the preferred port, later
|
|
25
|
+
* entries only used when an earlier one already answers reachable. */
|
|
26
|
+
ports?: readonly number[];
|
|
27
|
+
/** Ports only Windows listens on — the prereq reachability probe. */
|
|
28
|
+
prereqPorts?: readonly number[];
|
|
29
|
+
/** The Windows-side working directory the launch runs with. Omit/skip when
|
|
30
|
+
* it is not there. */
|
|
31
|
+
startCwd?: string;
|
|
32
|
+
/** Answers whether `host:port` accepts a connection right now. */
|
|
33
|
+
reachable?: (host: string, port: number) => Promise<boolean>;
|
|
34
|
+
/** Launches the executable and resolves the moment the OS accepted the
|
|
35
|
+
* process; rejects with the reason when the launch itself failed. */
|
|
36
|
+
start?: (executable: string, args: string[], cwd: string | undefined) => Promise<void>;
|
|
37
|
+
/** Writes the landing page beside the profile directory and answers the
|
|
38
|
+
* `file:///…` URL to open in the launched window ("" = launch with no
|
|
39
|
+
* page). Defaults to the packaged page rendered with this run's answers;
|
|
40
|
+
* injected so a unit run never writes through the interop mount. */
|
|
41
|
+
landing?: (profileDir: string, url: string, port: number) => string;
|
|
42
|
+
/** How long to wait for the launched browser to bind its port. */
|
|
43
|
+
waitMs?: number;
|
|
44
|
+
/** Poll cadence while waiting. */
|
|
45
|
+
pollMs?: number;
|
|
46
|
+
/** One reachability sample's timeout. */
|
|
47
|
+
sampleMs?: number;
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* The whole **Prelaunch Windows Chrome** run, in order:
|
|
51
|
+
*
|
|
52
|
+
* 1. locate a Windows Chrome/Edge — none answers `not-found`;
|
|
53
|
+
* 2. prove the prereq — one Windows-only port must answer reachable, or
|
|
54
|
+
* nothing is launched and the answer is `unreachable` carrying the one
|
|
55
|
+
* manual step (mirrored networking);
|
|
56
|
+
* 3. pick the port — the preferred one when it is free, else the first free
|
|
57
|
+
* entry of the pool (`WINDOWS_CHROME_PORT_POOL`); a listener already
|
|
58
|
+
* carrying connect mode means the address is live and nothing is launched;
|
|
59
|
+
* 4. launch by exec'ing the located executable through the interop mount,
|
|
60
|
+
* with `--remote-debugging-port=<P>`, a dedicated profile directory under
|
|
61
|
+
* the project (never the default profile: an in-use default profile
|
|
62
|
+
* silently ignores the port), the first-run-page flags and the landing page
|
|
63
|
+
* that states what the window is for ("" = no page, never a reason for the
|
|
64
|
+
* launch not to happen);
|
|
65
|
+
* 5. wait for the port to come up — `connected` when it does, `unreachable`
|
|
66
|
+
* (with its own error) when it never binds.
|
|
67
|
+
*
|
|
68
|
+
* The run never kills what it started: closing the browser belongs to the
|
|
69
|
+
* user, and a bridge in connect mode survives a browser restart.
|
|
70
|
+
*
|
|
71
|
+
* @param options - what to consult (every half injectable).
|
|
72
|
+
*/
|
|
73
|
+
export declare function runWindowsChromeConnect(options?: WindowsChromeRunOptions): Promise<WindowsChromeRun>;
|
|
74
|
+
//# sourceMappingURL=run.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../../src/host/windows/run.ts"],"names":[],"mappings":"AA6CA,OAAO,KAAK,EAAE,uBAAuB,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAQnF;2CAC2C;AAC3C,MAAM,MAAM,uBAAuB,GAAG;IAClC;uBACmB;IACnB,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;IACnC;6CACyC;IACzC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;4DACwD;IACxD,WAAW,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,KAAK,uBAAuB,CAAC;IAChF;sCACkC;IAClC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,gEAAgE;IAChE,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC/B;;;oBAGgB;IAChB,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC;0EACsE;IACtE,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC1B,qEAAqE;IACrE,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAChC;0BACsB;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,kEAAkE;IAClE,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC7D;yEACqE;IACrE,KAAK,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,EAAE,MAAM,GAAG,SAAS,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACvF;;;wEAGoE;IACpE,OAAO,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IACpE,kEAAkE;IAClE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,kCAAkC;IAClC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,yCAAyC;IACzC,QAAQ,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAsB,uBAAuB,CAAC,OAAO,GAAE,uBAA4B,GAAG,OAAO,CAAC,gBAAgB,CAAC,CA8J9G"}
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Windows Chrome run — the orchestrator behind the card's **Open Windows
|
|
3
|
+
* Chrome** button (and the `prelaunch_windows_chrome` tool).
|
|
4
|
+
*
|
|
5
|
+
* Driving the Windows browser in connect mode is the only Windows path ever
|
|
6
|
+
* measured to work: launch it with `--remote-debugging-port=<P>` and a dedicated
|
|
7
|
+
* profile, then let the bridge connect to `http://127.0.0.1:<P>` instead of
|
|
8
|
+
* launching anything locally. This module sequences the whole run — locate,
|
|
9
|
+
* prove the prereq (the mirrored-loopback reachability), pick the port, launch,
|
|
10
|
+
* wait for the port to bind — and answers through one state union. The pieces it
|
|
11
|
+
* drives live alongside it: ./paths.ts (map + locate + profile directory),
|
|
12
|
+
* ./link.ts (the workspace `.chrome` view), ./connect-flags.ts (the address +
|
|
13
|
+
* the connect-mode flag), ./spawn.ts (the raw reachability sample + interop
|
|
14
|
+
* launch). Every half is injectable so tests never depend on the machine.
|
|
15
|
+
*
|
|
16
|
+
* Launching the Windows `chrome.exe` in *launch* mode stays out entirely (hard
|
|
17
|
+
* constraint — the debug pipes do not survive the interop boundary), as does
|
|
18
|
+
* the default profile (an in-use one silently ignores the port) and the
|
|
19
|
+
* `cmd.exe /c start` wrapper (it never returns while the browser lives). The run
|
|
20
|
+
* never kills what it started: closing the browser belongs to the user, and a
|
|
21
|
+
* connect-mode bridge survives a browser restart. The field evidence behind each
|
|
22
|
+
* choice is `.probe/windows-chrome-connect-probe.log`.
|
|
23
|
+
*
|
|
24
|
+
* A launched window also carries its own explanation: the launch hands the
|
|
25
|
+
* browser a packaged landing page (./landing.ts) rendered with the run's port,
|
|
26
|
+
* connect address and profile directory. It is not a precondition — every
|
|
27
|
+
* landing failure is "" and the launch goes ahead exactly as it did before — but
|
|
28
|
+
* a window that opens with nothing on screen reads as a malfunction, which is
|
|
29
|
+
* what the page is for.
|
|
30
|
+
*/
|
|
31
|
+
import { existsSync } from "node:fs";
|
|
32
|
+
import { WINDOWS_CHROME_CANDIDATES, WINDOWS_CHROME_FIRST_RUN_FLAGS, WINDOWS_CHROME_POLL_MS, WINDOWS_CHROME_PORT, WINDOWS_CHROME_PORT_POOL, WINDOWS_CHROME_PREREQ_ERROR, WINDOWS_CHROME_PREREQ_PORTS, WINDOWS_CHROME_SAMPLE_MS, WINDOWS_CHROME_START_CWD, WINDOWS_CHROME_WAIT_MS } from "../constants.js";
|
|
33
|
+
import { firstLine } from "../status.js";
|
|
34
|
+
import { browserUrlFlagPort, windowsChromeUrl } from "./connect-flags.js";
|
|
35
|
+
import { windowsChromeLandingUrl } from "./landing.js";
|
|
36
|
+
import { linkWindowsChromeProfile } from "./link.js";
|
|
37
|
+
import { findWindowsChromeExecutable, windowsChromeLocalAppData, windowsChromeProfileDir, windowsPathToMount } from "./paths.js";
|
|
38
|
+
import { defaultReachable, defaultStart } from "./spawn.js";
|
|
39
|
+
/**
|
|
40
|
+
* The whole **Prelaunch Windows Chrome** run, in order:
|
|
41
|
+
*
|
|
42
|
+
* 1. locate a Windows Chrome/Edge — none answers `not-found`;
|
|
43
|
+
* 2. prove the prereq — one Windows-only port must answer reachable, or
|
|
44
|
+
* nothing is launched and the answer is `unreachable` carrying the one
|
|
45
|
+
* manual step (mirrored networking);
|
|
46
|
+
* 3. pick the port — the preferred one when it is free, else the first free
|
|
47
|
+
* entry of the pool (`WINDOWS_CHROME_PORT_POOL`); a listener already
|
|
48
|
+
* carrying connect mode means the address is live and nothing is launched;
|
|
49
|
+
* 4. launch by exec'ing the located executable through the interop mount,
|
|
50
|
+
* with `--remote-debugging-port=<P>`, a dedicated profile directory under
|
|
51
|
+
* the project (never the default profile: an in-use default profile
|
|
52
|
+
* silently ignores the port), the first-run-page flags and the landing page
|
|
53
|
+
* that states what the window is for ("" = no page, never a reason for the
|
|
54
|
+
* launch not to happen);
|
|
55
|
+
* 5. wait for the port to come up — `connected` when it does, `unreachable`
|
|
56
|
+
* (with its own error) when it never binds.
|
|
57
|
+
*
|
|
58
|
+
* The run never kills what it started: closing the browser belongs to the
|
|
59
|
+
* user, and a bridge in connect mode survives a browser restart.
|
|
60
|
+
*
|
|
61
|
+
* @param options - what to consult (every half injectable).
|
|
62
|
+
*/
|
|
63
|
+
export async function runWindowsChromeConnect(options = {}) {
|
|
64
|
+
const exists = typeof options.exists === "function" ? options.exists : (path) => existsSync(path);
|
|
65
|
+
const cwd = typeof options.cwd === "string" ? options.cwd : process.cwd();
|
|
66
|
+
const candidates = Array.isArray(options.candidates) ? options.candidates : WINDOWS_CHROME_CANDIDATES;
|
|
67
|
+
const ports = (Array.isArray(options.ports) ? options.ports : [...WINDOWS_CHROME_PORT_POOL]).map((port) => Math.trunc(port));
|
|
68
|
+
const prereqPorts = Array.isArray(options.prereqPorts) ? options.prereqPorts : [...WINDOWS_CHROME_PREREQ_PORTS];
|
|
69
|
+
const startCwd = typeof options.startCwd === "string" ? options.startCwd : WINDOWS_CHROME_START_CWD;
|
|
70
|
+
const waitMs = Number.isFinite(options.waitMs) ? Number(options.waitMs) : WINDOWS_CHROME_WAIT_MS;
|
|
71
|
+
const pollMs = Number.isFinite(options.pollMs) ? Number(options.pollMs) : WINDOWS_CHROME_POLL_MS;
|
|
72
|
+
const sampleMs = Number.isFinite(options.sampleMs) ? Number(options.sampleMs) : WINDOWS_CHROME_SAMPLE_MS;
|
|
73
|
+
const reachable = (host, port) => typeof options.reachable === "function"
|
|
74
|
+
? Promise.resolve(options.reachable(host, port)).then((value) => value === true)
|
|
75
|
+
: defaultReachable(host, port, sampleMs);
|
|
76
|
+
const start = (command, args, cwdValue) => typeof options.start === "function" ? Promise.resolve(options.start(command, args, cwdValue)) : defaultStart(command, args, cwdValue);
|
|
77
|
+
// The profile root (the Windows-side LOCALAPPDATA) is only needed on the
|
|
78
|
+
// branches that connect, so it resolves lazily and stays cached.
|
|
79
|
+
const localAppData = () => typeof options.localAppData === "string" ? options.localAppData : windowsChromeLocalAppData();
|
|
80
|
+
const linkProfile = (linkPath, profileWin) => typeof options.linkProfile === "function" ? options.linkProfile(linkPath, profileWin) : linkWindowsChromeProfile(linkPath, profileWin);
|
|
81
|
+
const landing = (profileWin, connectUrl, portValue) => typeof options.landing === "function" ? options.landing(profileWin, connectUrl, portValue) : windowsChromeLandingUrl(profileWin, connectUrl, portValue);
|
|
82
|
+
// The workspace view the successful run keeps pointing at its profile —
|
|
83
|
+
// the same `<project>/.chrome` the **Flags WSL** recommended flag names.
|
|
84
|
+
const profileLinkPath = `${cwd.replace(/[\\/]+$/gu, "")}/.chrome`;
|
|
85
|
+
if (ports.length === 0)
|
|
86
|
+
ports.push(WINDOWS_CHROME_PORT);
|
|
87
|
+
// 1. locate.
|
|
88
|
+
const executable = findWindowsChromeExecutable(exists, candidates);
|
|
89
|
+
if (executable === "") {
|
|
90
|
+
return {
|
|
91
|
+
state: "not-found",
|
|
92
|
+
port: 0,
|
|
93
|
+
url: "",
|
|
94
|
+
error: `no Windows Chrome found (checked: ${Array.isArray(candidates) ? candidates.join(", ") : ""})`,
|
|
95
|
+
profileDir: ""
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
// 2. prereq: the Windows loopback has to be reachable before anything is
|
|
99
|
+
// launched. Nothing reachable means no launch — the window would be dead
|
|
100
|
+
// weight, and the run would have paid for a browser it cannot drive.
|
|
101
|
+
let prereqOk = false;
|
|
102
|
+
for (const port of prereqPorts) {
|
|
103
|
+
if (await reachable("127.0.0.1", port)) {
|
|
104
|
+
prereqOk = true;
|
|
105
|
+
break;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
if (!prereqOk)
|
|
109
|
+
return { state: "unreachable", port: 0, url: "", profileDir: "", error: WINDOWS_CHROME_PREREQ_ERROR };
|
|
110
|
+
// 3. port. A live connect entry already answers — in every spelling
|
|
111
|
+
// `flags.ts` accepts, not only the one the run itself writes — so it is
|
|
112
|
+
// re-checked and settled on without a second launch when it still holds.
|
|
113
|
+
const livePort = browserUrlFlagPort(options.currentFlags);
|
|
114
|
+
if (livePort > 0 && (await reachable("127.0.0.1", livePort))) {
|
|
115
|
+
// The already-live session is profiled in the same per-port directory a
|
|
116
|
+
// launch would have chosen, so the workspace view keeps pointing at
|
|
117
|
+
// whatever is actually running.
|
|
118
|
+
const liveLeaf = livePort === WINDOWS_CHROME_PORT ? ".chrome" : `.chrome-${String(livePort)}`;
|
|
119
|
+
const liveProfileDir = windowsChromeProfileDir(localAppData(), liveLeaf);
|
|
120
|
+
return {
|
|
121
|
+
state: "connected",
|
|
122
|
+
port: livePort,
|
|
123
|
+
url: windowsChromeUrl(livePort),
|
|
124
|
+
error: "",
|
|
125
|
+
profileDir: liveProfileDir,
|
|
126
|
+
profileLink: linkProfile(profileLinkPath, liveProfileDir)
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
const pool = livePort > 0 ? [livePort, ...ports.filter((port) => port !== livePort)] : ports;
|
|
130
|
+
let port = 0;
|
|
131
|
+
for (const candidate of pool) {
|
|
132
|
+
if (!(await reachable("127.0.0.1", candidate))) {
|
|
133
|
+
port = candidate;
|
|
134
|
+
break;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
if (port === 0)
|
|
138
|
+
port = pool[pool.length - 1];
|
|
139
|
+
// 4. profile + launch. The profile directory is Windows-side (a profile
|
|
140
|
+
// rooted in the WSL checkout cannot carry Chrome's locks, and a profile
|
|
141
|
+
// that cannot lock answers "an error occurred while opening your profile");
|
|
142
|
+
// it is never the default profile (an in-use one silently ignores the
|
|
143
|
+
// port); and it never goes through the `cmd.exe /c start` wrapper (see
|
|
144
|
+
// ./spawn.ts: under interop that wrapper never returns while the browser
|
|
145
|
+
// lives, and the whole run silently stalls). The locate answer is a Windows
|
|
146
|
+
// spelling (what the card may quote); interop execs through the mount, so
|
|
147
|
+
// the launch maps it back first.
|
|
148
|
+
const leaf = port === WINDOWS_CHROME_PORT ? ".chrome" : `.chrome-${String(port)}`;
|
|
149
|
+
const profileDir = windowsChromeProfileDir(localAppData(), leaf);
|
|
150
|
+
if (profileDir === "") {
|
|
151
|
+
return {
|
|
152
|
+
state: "launch-failed",
|
|
153
|
+
port,
|
|
154
|
+
url: "",
|
|
155
|
+
profileDir: "",
|
|
156
|
+
error: "cannot derive the Windows-side profile directory: LOCALAPPDATA did not answer"
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
// The launch's argv: the port, the profile, the first-run flags, and — last
|
|
160
|
+
// — one positional URL, the landing page that says what this window is for.
|
|
161
|
+
// A page that could not be rendered or written answers "" and simply drops
|
|
162
|
+
// out of argv: the run's answer never depends on prose.
|
|
163
|
+
const url = windowsChromeUrl(port);
|
|
164
|
+
const landingUrl = landing(profileDir, url, port);
|
|
165
|
+
try {
|
|
166
|
+
await start(windowsPathToMount(executable) || executable, [
|
|
167
|
+
`--remote-debugging-port=${String(port)}`,
|
|
168
|
+
`--user-data-dir=${profileDir}`,
|
|
169
|
+
// A brand-new profile is a first run: without these, Windows
|
|
170
|
+
// Chrome opens the welcome tab and the search-engine chooser
|
|
171
|
+
// and connect mode never clicks past them.
|
|
172
|
+
...WINDOWS_CHROME_FIRST_RUN_FLAGS,
|
|
173
|
+
...(landingUrl === "" ? [] : [landingUrl])
|
|
174
|
+
], exists(startCwd) ? startCwd : undefined);
|
|
175
|
+
}
|
|
176
|
+
catch (error) {
|
|
177
|
+
return {
|
|
178
|
+
state: "launch-failed",
|
|
179
|
+
port,
|
|
180
|
+
url: "",
|
|
181
|
+
profileDir: "",
|
|
182
|
+
error: `could not start Windows Chrome: ${firstLine(error instanceof Error ? error.message : String(error))}`
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
// 5. wait for the browser to bind its port. A browser that never binds it
|
|
186
|
+
// is not reachable, so the answer is the prereq's failure to be useful.
|
|
187
|
+
const deadline = Date.now() + waitMs;
|
|
188
|
+
for (;;) {
|
|
189
|
+
if (await reachable("127.0.0.1", port)) {
|
|
190
|
+
return {
|
|
191
|
+
state: "connected",
|
|
192
|
+
port,
|
|
193
|
+
url,
|
|
194
|
+
error: "",
|
|
195
|
+
profileDir,
|
|
196
|
+
profileLink: linkProfile(profileLinkPath, profileDir)
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
if (Date.now() >= deadline) {
|
|
200
|
+
return {
|
|
201
|
+
state: "unreachable",
|
|
202
|
+
port,
|
|
203
|
+
url: "",
|
|
204
|
+
profileDir: "",
|
|
205
|
+
error: `Windows Chrome started but did not open a debugging port on 127.0.0.1:${String(port)} — ${WINDOWS_CHROME_PREREQ_ERROR}`
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
await new Promise((resolve) => setTimeout(resolve, pollMs));
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
//# sourceMappingURL=run.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run.js","sourceRoot":"","sources":["../../../src/host/windows/run.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/** One TCP sample: connect success answers reachable; anything else (refusal,
|
|
2
|
+
* timeout) answers unreachable — the NAT host's honest behaviour. */
|
|
3
|
+
export declare function defaultReachable(host: string, port: number, sampleMs?: number): Promise<boolean>;
|
|
4
|
+
/**
|
|
5
|
+
* Launch the Windows-side browser through the interop mount by exec'ing it
|
|
6
|
+
* directly, resolving the moment the OS accepted the process (the browser
|
|
7
|
+
* itself keeps running — the run must never wait for it, and the port wait
|
|
8
|
+
* below is what turns a silently dead launch into `unreachable`).
|
|
9
|
+
*
|
|
10
|
+
* The interop `start` wrapper is deliberately NOT used here even though the
|
|
11
|
+
* plan quoted `cmd.exe /c start`: measured 2026-10-06 against Windows Chrome
|
|
12
|
+
* 154 (see .probe/windows-chrome-connect-probe.log), the WSL interop process
|
|
13
|
+
* for `cmd.exe /c start "" <browser> <args…>` does not exit while the GUI
|
|
14
|
+
* browser lives, so step 4 could never report its answer and the card sat in
|
|
15
|
+
* "launching" until the spawn timed out — a silent 30 s. A direct
|
|
16
|
+
* `execve`/CreateProcess of the executable accepted in 3 ms and bound the
|
|
17
|
+
* port in ~1 s; this is the only launch shape that returns. It also keeps
|
|
18
|
+
* Chrome's working directory out of any UNC path (the old `start` warning),
|
|
19
|
+
* since the launch takes a Windows-side cwd.
|
|
20
|
+
*/
|
|
21
|
+
export declare function defaultStart(executable: string, args: string[], cwd: string | undefined): Promise<void>;
|
|
22
|
+
//# sourceMappingURL=spawn.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spawn.d.ts","sourceRoot":"","sources":["../../../src/host/windows/spawn.ts"],"names":[],"mappings":"AAmBA;qEACqE;AACrE,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,GAAE,MAAiC,GAAG,OAAO,CAAC,OAAO,CAAC,CAe1H;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAkBvG"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Windows Chrome run — the raw machine seams: a TCP reachability sample and a
|
|
3
|
+
* direct interop launch.
|
|
4
|
+
*
|
|
5
|
+
* The two halves of the run that reach outside the process, injected by
|
|
6
|
+
* ./run.ts (and replaced wholesale by the tests) so a unit run never opens a
|
|
7
|
+
* real socket or spawns through the interop mount. The TCP sample answers
|
|
8
|
+
* reachable only on a genuine connect — a refusal or a timeout both read
|
|
9
|
+
* unreachable, the honest NAT-host behaviour. The launch is the interop exec of
|
|
10
|
+
* the located executable (NOT the `cmd.exe /c start` wrapper the plan quoted:
|
|
11
|
+
* under interop that wrapper never returns while the GUI browser lives, so the
|
|
12
|
+
* whole run silently stalls — measured 2026-10-06 against Chrome 154), resolving
|
|
13
|
+
* the moment the OS accepts the process.
|
|
14
|
+
*/
|
|
15
|
+
import { spawn } from "node:child_process";
|
|
16
|
+
import { connect } from "node:net";
|
|
17
|
+
import { WINDOWS_CHROME_SAMPLE_MS } from "../constants.js";
|
|
18
|
+
/** One TCP sample: connect success answers reachable; anything else (refusal,
|
|
19
|
+
* timeout) answers unreachable — the NAT host's honest behaviour. */
|
|
20
|
+
export function defaultReachable(host, port, sampleMs = WINDOWS_CHROME_SAMPLE_MS) {
|
|
21
|
+
return new Promise((resolve) => {
|
|
22
|
+
let settled = false;
|
|
23
|
+
const socket = connect({ host, port });
|
|
24
|
+
const finish = (value) => {
|
|
25
|
+
if (settled)
|
|
26
|
+
return;
|
|
27
|
+
settled = true;
|
|
28
|
+
socket.destroy();
|
|
29
|
+
resolve(value);
|
|
30
|
+
};
|
|
31
|
+
socket.setTimeout(sampleMs);
|
|
32
|
+
socket.on("connect", () => finish(true));
|
|
33
|
+
socket.on("timeout", () => finish(false));
|
|
34
|
+
socket.on("error", () => finish(false));
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Launch the Windows-side browser through the interop mount by exec'ing it
|
|
39
|
+
* directly, resolving the moment the OS accepted the process (the browser
|
|
40
|
+
* itself keeps running — the run must never wait for it, and the port wait
|
|
41
|
+
* below is what turns a silently dead launch into `unreachable`).
|
|
42
|
+
*
|
|
43
|
+
* The interop `start` wrapper is deliberately NOT used here even though the
|
|
44
|
+
* plan quoted `cmd.exe /c start`: measured 2026-10-06 against Windows Chrome
|
|
45
|
+
* 154 (see .probe/windows-chrome-connect-probe.log), the WSL interop process
|
|
46
|
+
* for `cmd.exe /c start "" <browser> <args…>` does not exit while the GUI
|
|
47
|
+
* browser lives, so step 4 could never report its answer and the card sat in
|
|
48
|
+
* "launching" until the spawn timed out — a silent 30 s. A direct
|
|
49
|
+
* `execve`/CreateProcess of the executable accepted in 3 ms and bound the
|
|
50
|
+
* port in ~1 s; this is the only launch shape that returns. It also keeps
|
|
51
|
+
* Chrome's working directory out of any UNC path (the old `start` warning),
|
|
52
|
+
* since the launch takes a Windows-side cwd.
|
|
53
|
+
*/
|
|
54
|
+
export function defaultStart(executable, args, cwd) {
|
|
55
|
+
return new Promise((resolve, reject) => {
|
|
56
|
+
let child;
|
|
57
|
+
try {
|
|
58
|
+
child = spawn(executable, args, {
|
|
59
|
+
stdio: "ignore",
|
|
60
|
+
...(cwd === undefined || cwd === "" ? {} : { cwd })
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
reject(new Error(`could not run "${executable} ${args.join(" ")}": ${String(error)}`));
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
// `spawn` is the OS accepting the process; no other half is waited on.
|
|
68
|
+
child.once("spawn", resolve);
|
|
69
|
+
child.once("error", (error) => reject(new Error(`could not run "${executable} ${args.join(" ")}": ${String(error)}`)));
|
|
70
|
+
child.unref();
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=spawn.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spawn.js","sourceRoot":"","sources":["../../../src/host/windows/spawn.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAEnC,OAAO,EAAE,wBAAwB,EAAE,MAAM,iBAAiB,CAAC;AAE3D;qEACqE;AACrE,MAAM,UAAU,gBAAgB,CAAC,IAAY,EAAE,IAAY,EAAE,WAAmB,wBAAwB;IACpG,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC3B,IAAI,OAAO,GAAG,KAAK,CAAC;QACpB,MAAM,MAAM,GAAG,OAAO,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QACvC,MAAM,MAAM,GAAG,CAAC,KAAc,EAAE,EAAE;YAC9B,IAAI,OAAO;gBAAE,OAAO;YACpB,OAAO,GAAG,IAAI,CAAC;YACf,MAAM,CAAC,OAAO,EAAE,CAAC;YACjB,OAAO,CAAC,KAAK,CAAC,CAAC;QACnB,CAAC,CAAC;QACF,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;QAC5B,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;QACzC,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;QAC1C,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;IAC5C,CAAC,CAAC,CAAC;AACP,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,YAAY,CAAC,UAAkB,EAAE,IAAc,EAAE,GAAuB;IACpF,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACnC,IAAI,KAA+B,CAAC;QACpC,IAAI,CAAC;YACD,KAAK,GAAG,KAAK,CAAC,UAAU,EAAE,IAAI,EAAE;gBAC5B,KAAK,EAAE,QAAQ;gBACf,GAAG,CAAC,GAAG,KAAK,SAAS,IAAI,GAAG,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC;aACtD,CAAC,CAAC;QACP,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,MAAM,CAAC,IAAI,KAAK,CAAC,kBAAkB,UAAU,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC;YACvF,OAAO;QACX,CAAC;QACD,uEAAuE;QACvE,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QAC7B,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,KAA4B,EAAE,EAAE,CACjD,MAAM,CAAC,IAAI,KAAK,CAAC,kBAAkB,UAAU,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QAC5F,KAAK,CAAC,KAAK,EAAE,CAAC;IAClB,CAAC,CAAC,CAAC;AACP,CAAC"}
|