@jobshimo/browser-link 0.39.0 → 0.41.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 +23 -8
- package/dist/bridge/dispatch.js +17 -7
- package/dist/bridge/dispatch.js.map +1 -1
- package/dist/bridge/ws-bridge.d.ts +28 -3
- package/dist/bridge/ws-bridge.js +51 -7
- package/dist/bridge/ws-bridge.js.map +1 -1
- package/dist/cli.js +6 -2
- package/dist/cli.js.map +1 -1
- package/dist/commands/about.js +10 -0
- package/dist/commands/about.js.map +1 -1
- package/dist/commands/doctor.d.ts +24 -0
- package/dist/commands/doctor.js +62 -1
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/free-port.d.ts +40 -1
- package/dist/commands/free-port.js +72 -7
- package/dist/commands/free-port.js.map +1 -1
- package/dist/config.d.ts +7 -0
- package/dist/config.js +9 -0
- package/dist/config.js.map +1 -1
- package/dist/extension/background.js +100 -8
- package/dist/extension/background.js.map +1 -1
- package/dist/extension/manifest.json +1 -1
- package/dist/extension/notice-policy.d.ts +48 -0
- package/dist/extension/notice-policy.js +83 -0
- package/dist/extension/notice-policy.js.map +1 -0
- package/dist/extension/popup.html +99 -0
- package/dist/extension/popup.js +139 -1
- package/dist/extension/popup.js.map +1 -1
- package/dist/extension/repair-policy.d.ts +60 -0
- package/dist/extension/repair-policy.js +79 -0
- package/dist/extension/repair-policy.js.map +1 -0
- package/dist/extension/version-status.d.ts +44 -0
- package/dist/extension/version-status.js +60 -0
- package/dist/extension/version-status.js.map +1 -0
- package/dist/lifecycle/election.d.ts +98 -0
- package/dist/lifecycle/election.js +187 -0
- package/dist/lifecycle/election.js.map +1 -0
- package/dist/lifecycle/processes.d.ts +67 -0
- package/dist/lifecycle/processes.js +239 -0
- package/dist/lifecycle/processes.js.map +1 -0
- package/dist/lifecycle/repair.d.ts +48 -0
- package/dist/lifecycle/repair.js +67 -0
- package/dist/lifecycle/repair.js.map +1 -0
- package/dist/lifecycle/version-endpoint.d.ts +16 -0
- package/dist/lifecycle/version-endpoint.js +50 -0
- package/dist/lifecycle/version-endpoint.js.map +1 -0
- package/dist/manual/topics.js +1 -1
- package/dist/manual/topics.js.map +1 -1
- package/dist/messages.d.ts +31 -2
- package/dist/notice.d.ts +14 -0
- package/dist/notice.js +25 -0
- package/dist/notice.js.map +1 -0
- package/dist/server.js +90 -1
- package/dist/server.js.map +1 -1
- package/dist/tools/browser-dispatch.d.ts +8 -0
- package/dist/tools/browser-dispatch.js +15 -1
- package/dist/tools/browser-dispatch.js.map +1 -1
- package/dist/ui/app.d.ts +4 -1
- package/dist/ui/app.js +12 -3
- package/dist/ui/app.js.map +1 -1
- package/dist/ui/screens/about.js +3 -1
- package/dist/ui/screens/about.js.map +1 -1
- package/dist/ui/screens/free-port.js +4 -2
- package/dist/ui/screens/free-port.js.map +1 -1
- package/dist/ui/screens/index.d.ts +1 -0
- package/dist/ui/screens/index.js +1 -0
- package/dist/ui/screens/index.js.map +1 -1
- package/dist/ui/screens/notice.d.ts +6 -0
- package/dist/ui/screens/notice.js +17 -0
- package/dist/ui/screens/notice.js.map +1 -0
- package/dist/ui/start.js +2 -1
- package/dist/ui/start.js.map +1 -1
- package/dist/version.d.ts +8 -0
- package/dist/version.js +16 -0
- package/dist/version.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Version facts the popup shows, computed from what the server reported in
|
|
3
|
+
* `tab.registered`. Pure on purpose: background.ts gathers the inputs, this
|
|
4
|
+
* module decides, and popup.ts only renders.
|
|
5
|
+
*
|
|
6
|
+
* Three versions are in play:
|
|
7
|
+
* - `extension`: this extension's manifest version.
|
|
8
|
+
* - `server`: the version the running server booted with (`serverVersion`).
|
|
9
|
+
* - `installed`: the package version on disk right now (`installedVersion`,
|
|
10
|
+
* servers from 0.41.0). When it is newer than `server`, the package was
|
|
11
|
+
* updated while an old process kept serving: the server is outdated.
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* Compare two `major.minor.patch` versions numerically. A leading `v` and any
|
|
15
|
+
* prerelease or build suffix are ignored; missing or non-numeric parts count
|
|
16
|
+
* as 0. Returns a negative number, 0 or a positive number like a sort
|
|
17
|
+
* comparator.
|
|
18
|
+
*/
|
|
19
|
+
export declare function compareSemver(a: string, b: string): number;
|
|
20
|
+
export interface VersionCheckInput {
|
|
21
|
+
extension: string;
|
|
22
|
+
server: string | null;
|
|
23
|
+
installed: string | null;
|
|
24
|
+
}
|
|
25
|
+
/** What background.ts answers to the popup's `versionCheck`. `extension`,
|
|
26
|
+
* `server` and `aligned` keep their pre-0.41.0 meaning; `installed` and
|
|
27
|
+
* `serverOutdated` are additive. */
|
|
28
|
+
export interface VersionCheck {
|
|
29
|
+
extension: string;
|
|
30
|
+
server: string | null;
|
|
31
|
+
installed: string | null;
|
|
32
|
+
/** Extension and server run the same version; null while no server has
|
|
33
|
+
* reported one. */
|
|
34
|
+
aligned: boolean | null;
|
|
35
|
+
/** The installed package is newer than the code the server is running. */
|
|
36
|
+
serverOutdated: boolean;
|
|
37
|
+
}
|
|
38
|
+
export declare function computeVersionCheck(input: VersionCheckInput): VersionCheck;
|
|
39
|
+
/** The existing "Version mismatch" banner: shown only when a server version
|
|
40
|
+
* is known and differs from the extension's. Unchanged by 0.41.0. */
|
|
41
|
+
export declare function shouldShowVersionBanner(check: VersionCheck): boolean;
|
|
42
|
+
/** "Server running X · installed Y" when the server runs a different version
|
|
43
|
+
* than the one installed on disk; null when they match or either is unknown. */
|
|
44
|
+
export declare function runningInstalledLine(check: VersionCheck): string | null;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Version facts the popup shows, computed from what the server reported in
|
|
3
|
+
* `tab.registered`. Pure on purpose: background.ts gathers the inputs, this
|
|
4
|
+
* module decides, and popup.ts only renders.
|
|
5
|
+
*
|
|
6
|
+
* Three versions are in play:
|
|
7
|
+
* - `extension`: this extension's manifest version.
|
|
8
|
+
* - `server`: the version the running server booted with (`serverVersion`).
|
|
9
|
+
* - `installed`: the package version on disk right now (`installedVersion`,
|
|
10
|
+
* servers from 0.41.0). When it is newer than `server`, the package was
|
|
11
|
+
* updated while an old process kept serving: the server is outdated.
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* Compare two `major.minor.patch` versions numerically. A leading `v` and any
|
|
15
|
+
* prerelease or build suffix are ignored; missing or non-numeric parts count
|
|
16
|
+
* as 0. Returns a negative number, 0 or a positive number like a sort
|
|
17
|
+
* comparator.
|
|
18
|
+
*/
|
|
19
|
+
export function compareSemver(a, b) {
|
|
20
|
+
const pa = semverParts(a);
|
|
21
|
+
const pb = semverParts(b);
|
|
22
|
+
for (let i = 0; i < 3; i += 1) {
|
|
23
|
+
const diff = (pa[i] ?? 0) - (pb[i] ?? 0);
|
|
24
|
+
if (diff !== 0)
|
|
25
|
+
return diff;
|
|
26
|
+
}
|
|
27
|
+
return 0;
|
|
28
|
+
}
|
|
29
|
+
function semverParts(version) {
|
|
30
|
+
const core = version.trim().replace(/^v/i, '').split(/[-+]/, 1)[0] ?? '';
|
|
31
|
+
return core.split('.').map((part) => {
|
|
32
|
+
const n = Number.parseInt(part, 10);
|
|
33
|
+
return Number.isFinite(n) && n >= 0 ? n : 0;
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
export function computeVersionCheck(input) {
|
|
37
|
+
const { extension, server, installed } = input;
|
|
38
|
+
return {
|
|
39
|
+
extension,
|
|
40
|
+
server,
|
|
41
|
+
installed,
|
|
42
|
+
aligned: server === null ? null : server === extension,
|
|
43
|
+
serverOutdated: server !== null && installed !== null && compareSemver(installed, server) > 0,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
/** The existing "Version mismatch" banner: shown only when a server version
|
|
47
|
+
* is known and differs from the extension's. Unchanged by 0.41.0. */
|
|
48
|
+
export function shouldShowVersionBanner(check) {
|
|
49
|
+
return check.server !== null && check.aligned === false;
|
|
50
|
+
}
|
|
51
|
+
/** "Server running X · installed Y" when the server runs a different version
|
|
52
|
+
* than the one installed on disk; null when they match or either is unknown. */
|
|
53
|
+
export function runningInstalledLine(check) {
|
|
54
|
+
if (check.server === null || check.installed === null)
|
|
55
|
+
return null;
|
|
56
|
+
if (check.installed === check.server)
|
|
57
|
+
return null;
|
|
58
|
+
return `Server running ${check.server} · installed ${check.installed}`;
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=version-status.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"version-status.js","sourceRoot":"","sources":["../src/version-status.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAAC,CAAS,EAAE,CAAS;IAChD,MAAM,EAAE,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC;IAC1B,MAAM,EAAE,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC;IAC1B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QAC9B,MAAM,IAAI,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;QACzC,IAAI,IAAI,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC;IAC9B,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC;AAED,SAAS,WAAW,CAAC,OAAe;IAClC,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IACzE,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;QAClC,MAAM,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QACpC,OAAO,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC9C,CAAC,CAAC,CAAC;AACL,CAAC;AAsBD,MAAM,UAAU,mBAAmB,CAAC,KAAwB;IAC1D,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,KAAK,CAAC;IAC/C,OAAO;QACL,SAAS;QACT,MAAM;QACN,SAAS;QACT,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,KAAK,SAAS;QACtD,cAAc,EAAE,MAAM,KAAK,IAAI,IAAI,SAAS,KAAK,IAAI,IAAI,aAAa,CAAC,SAAS,EAAE,MAAM,CAAC,GAAG,CAAC;KAC9F,CAAC;AACJ,CAAC;AAED;qEACqE;AACrE,MAAM,UAAU,uBAAuB,CAAC,KAAmB;IACzD,OAAO,KAAK,CAAC,MAAM,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,KAAK,KAAK,CAAC;AAC1D,CAAC;AAED;gFACgF;AAChF,MAAM,UAAU,oBAAoB,CAAC,KAAmB;IACtD,IAAI,KAAK,CAAC,MAAM,KAAK,IAAI,IAAI,KAAK,CAAC,SAAS,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IACnE,IAAI,KAAK,CAAC,SAAS,KAAK,KAAK,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IAClD,OAAO,kBAAkB,KAAK,CAAC,MAAM,gBAAgB,KAAK,CAAC,SAAS,EAAE,CAAC;AACzE,CAAC"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { type ExecFileLike } from './processes.js';
|
|
2
|
+
/**
|
|
3
|
+
* Version-aware primary election.
|
|
4
|
+
*
|
|
5
|
+
* Without this, the primary is simply whoever bound 127.0.0.1:17529 first,
|
|
6
|
+
* so an old server kept serving after an update until somebody killed it.
|
|
7
|
+
* When a starting process finds the port taken it probes the running
|
|
8
|
+
* primary's `GET /version` (see `version-endpoint.ts`) and decides:
|
|
9
|
+
*
|
|
10
|
+
* (a) remote version known and older than ours → take over;
|
|
11
|
+
* (b) remote answers HTTP but has no `/version` (a primary older than
|
|
12
|
+
* 0.41.0) and its process started before the installed code was last
|
|
13
|
+
* written: it is necessarily running stale code → take over;
|
|
14
|
+
* (c) anything else (same or newer version, unknown start
|
|
15
|
+
* time, nothing answering HTTP) → defer.
|
|
16
|
+
*
|
|
17
|
+
* Deferring is the existing behaviour: proxy mode with multi-agent on, the
|
|
18
|
+
* existing error otherwise. Taking over = step the owner down (a kill: old
|
|
19
|
+
* primaries understand no step-down frame), then bind with retries. The hard
|
|
20
|
+
* kill closes the extension sockets uncleanly, which is exactly what the
|
|
21
|
+
* extension's auto-reconnect waits for.
|
|
22
|
+
*
|
|
23
|
+
* Every effect is injected so the decision is testable without killing or
|
|
24
|
+
* binding anything real; `server.ts` only wires the real implementations.
|
|
25
|
+
*/
|
|
26
|
+
export interface VersionInfo {
|
|
27
|
+
name: string;
|
|
28
|
+
version: string;
|
|
29
|
+
pid: number;
|
|
30
|
+
startedAt: string;
|
|
31
|
+
installedVersion: string | null;
|
|
32
|
+
}
|
|
33
|
+
export type VersionProbe =
|
|
34
|
+
/** The primary answered `/version`. */
|
|
35
|
+
{
|
|
36
|
+
kind: 'version';
|
|
37
|
+
info: VersionInfo;
|
|
38
|
+
}
|
|
39
|
+
/** Something answered HTTP on the port, but not a valid `/version`. */
|
|
40
|
+
| {
|
|
41
|
+
kind: 'no-endpoint';
|
|
42
|
+
}
|
|
43
|
+
/** Nothing answered HTTP (refused, timeout, not an HTTP server). */
|
|
44
|
+
| {
|
|
45
|
+
kind: 'unreachable';
|
|
46
|
+
};
|
|
47
|
+
export type ElectionDecision = 'take-over' | 'defer';
|
|
48
|
+
export interface ElectionOutcome {
|
|
49
|
+
decision: ElectionDecision;
|
|
50
|
+
/** The process stepped down (take-over) or that would have been. */
|
|
51
|
+
pid: number | null;
|
|
52
|
+
reason: string;
|
|
53
|
+
}
|
|
54
|
+
export interface DecideInput {
|
|
55
|
+
local: string;
|
|
56
|
+
probe: VersionProbe;
|
|
57
|
+
ownerPid: number | null;
|
|
58
|
+
ownerStartedAt: number | null;
|
|
59
|
+
installedCodeMtime: number | null;
|
|
60
|
+
}
|
|
61
|
+
/** The pure decision table (a)/(b)/(c) above. */
|
|
62
|
+
export declare function decideElection(input: DecideInput): ElectionOutcome;
|
|
63
|
+
export interface ElectionDeps {
|
|
64
|
+
localVersion: string;
|
|
65
|
+
probe: () => Promise<VersionProbe>;
|
|
66
|
+
/** Pid listening on the bridge port; used only when `/version` is absent. */
|
|
67
|
+
findOwnerPid: () => number | null;
|
|
68
|
+
processStartTime: (pid: number) => number | null;
|
|
69
|
+
installedCodeMtime: () => number | null;
|
|
70
|
+
stepDown: (pid: number) => Promise<void>;
|
|
71
|
+
}
|
|
72
|
+
/** Probe, decide and, on take-over, step the owner down. The caller binds
|
|
73
|
+
* afterwards (see `retryBind`). A step-down that fails turns into `defer`. */
|
|
74
|
+
export declare function runElection(deps: ElectionDeps): Promise<ElectionOutcome>;
|
|
75
|
+
/** `GET http://<host>:<port>/version`. Never rejects. */
|
|
76
|
+
export declare function probeVersion(port: number, timeoutMs?: number, host?: string): Promise<VersionProbe>;
|
|
77
|
+
/** Run `bind` until it succeeds, retrying only errors `isRetryable` accepts
|
|
78
|
+
* (a busy port), at most `attempts` times with `delayMs` between tries. */
|
|
79
|
+
export declare function retryBind<T>(bind: () => Promise<T>, opts: {
|
|
80
|
+
attempts: number;
|
|
81
|
+
delayMs: number;
|
|
82
|
+
isRetryable: (err: unknown) => boolean;
|
|
83
|
+
sleep?: (ms: number) => Promise<void>;
|
|
84
|
+
}): Promise<T>;
|
|
85
|
+
/** Unix grace period between SIGTERM and SIGKILL. */
|
|
86
|
+
export declare const STEP_DOWN_GRACE_MS = 3000;
|
|
87
|
+
/**
|
|
88
|
+
* Stop a process: Windows `taskkill /PID n /F`; Unix `SIGTERM`, wait up to
|
|
89
|
+
* 3 s for it to exit, then `SIGKILL`. Throws when the kill itself fails.
|
|
90
|
+
*/
|
|
91
|
+
export declare function stepDownProcess(pid: number, deps?: {
|
|
92
|
+
os?: NodeJS.Platform;
|
|
93
|
+
execFile?: ExecFileLike;
|
|
94
|
+
kill?: (pid: number, signal: NodeJS.Signals) => void;
|
|
95
|
+
isAlive?: (pid: number) => boolean;
|
|
96
|
+
sleep?: (ms: number) => Promise<void>;
|
|
97
|
+
now?: () => number;
|
|
98
|
+
}): Promise<void>;
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import { request } from 'node:http';
|
|
2
|
+
import { platform } from 'node:os';
|
|
3
|
+
import { compareSemver } from '../utils/semver.js';
|
|
4
|
+
import { defaultExecFile } from './processes.js';
|
|
5
|
+
/** The pure decision table (a)/(b)/(c) above. */
|
|
6
|
+
export function decideElection(input) {
|
|
7
|
+
const { probe } = input;
|
|
8
|
+
if (probe.kind === 'version') {
|
|
9
|
+
const remote = probe.info.version;
|
|
10
|
+
if (compareSemver(remote, input.local) < 0) {
|
|
11
|
+
return {
|
|
12
|
+
decision: 'take-over',
|
|
13
|
+
pid: probe.info.pid,
|
|
14
|
+
reason: `running primary ${remote} is older than ${input.local}`,
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
return {
|
|
18
|
+
decision: 'defer',
|
|
19
|
+
pid: probe.info.pid,
|
|
20
|
+
reason: `running primary ${remote} is not older than ${input.local}`,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
if (probe.kind === 'unreachable') {
|
|
24
|
+
return { decision: 'defer', pid: input.ownerPid, reason: 'port holder does not answer HTTP' };
|
|
25
|
+
}
|
|
26
|
+
const { ownerPid, ownerStartedAt, installedCodeMtime } = input;
|
|
27
|
+
if (ownerPid === null || ownerStartedAt === null || installedCodeMtime === null) {
|
|
28
|
+
return {
|
|
29
|
+
decision: 'defer',
|
|
30
|
+
pid: ownerPid,
|
|
31
|
+
reason: 'primary has no /version and its start time cannot be compared',
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
if (ownerStartedAt < installedCodeMtime) {
|
|
35
|
+
return {
|
|
36
|
+
decision: 'take-over',
|
|
37
|
+
pid: ownerPid,
|
|
38
|
+
reason: 'primary has no /version and started before the installed code was written',
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
return {
|
|
42
|
+
decision: 'defer',
|
|
43
|
+
pid: ownerPid,
|
|
44
|
+
reason: 'primary has no /version but started after the installed code was written',
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
/** Probe, decide and, on take-over, step the owner down. The caller binds
|
|
48
|
+
* afterwards (see `retryBind`). A step-down that fails turns into `defer`. */
|
|
49
|
+
export async function runElection(deps) {
|
|
50
|
+
const probe = await deps.probe();
|
|
51
|
+
let ownerPid = null;
|
|
52
|
+
let ownerStartedAt = null;
|
|
53
|
+
let installedCodeMtime = null;
|
|
54
|
+
if (probe.kind === 'no-endpoint') {
|
|
55
|
+
ownerPid = deps.findOwnerPid();
|
|
56
|
+
ownerStartedAt = ownerPid === null ? null : deps.processStartTime(ownerPid);
|
|
57
|
+
installedCodeMtime = deps.installedCodeMtime();
|
|
58
|
+
}
|
|
59
|
+
const outcome = decideElection({
|
|
60
|
+
local: deps.localVersion,
|
|
61
|
+
probe,
|
|
62
|
+
ownerPid,
|
|
63
|
+
ownerStartedAt,
|
|
64
|
+
installedCodeMtime,
|
|
65
|
+
});
|
|
66
|
+
if (outcome.decision !== 'take-over' || outcome.pid === null)
|
|
67
|
+
return outcome;
|
|
68
|
+
try {
|
|
69
|
+
await deps.stepDown(outcome.pid);
|
|
70
|
+
return outcome;
|
|
71
|
+
}
|
|
72
|
+
catch (err) {
|
|
73
|
+
return {
|
|
74
|
+
decision: 'defer',
|
|
75
|
+
pid: outcome.pid,
|
|
76
|
+
reason: `step-down of PID ${outcome.pid} failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
function isVersionInfo(v) {
|
|
81
|
+
if (typeof v !== 'object' || v === null)
|
|
82
|
+
return false;
|
|
83
|
+
const o = v;
|
|
84
|
+
return (typeof o.name === 'string' &&
|
|
85
|
+
typeof o.version === 'string' &&
|
|
86
|
+
typeof o.pid === 'number' &&
|
|
87
|
+
Number.isInteger(o.pid) &&
|
|
88
|
+
o.pid > 0 &&
|
|
89
|
+
typeof o.startedAt === 'string' &&
|
|
90
|
+
(o.installedVersion === null || typeof o.installedVersion === 'string'));
|
|
91
|
+
}
|
|
92
|
+
/** `GET http://<host>:<port>/version`. Never rejects. */
|
|
93
|
+
export function probeVersion(port, timeoutMs = 1000, host = '127.0.0.1') {
|
|
94
|
+
return new Promise((resolve) => {
|
|
95
|
+
let settled = false;
|
|
96
|
+
const done = (probe) => {
|
|
97
|
+
if (settled)
|
|
98
|
+
return;
|
|
99
|
+
settled = true;
|
|
100
|
+
resolve(probe);
|
|
101
|
+
};
|
|
102
|
+
const req = request({ host, port, path: '/version', method: 'GET', timeout: timeoutMs }, (res) => {
|
|
103
|
+
const chunks = [];
|
|
104
|
+
res.on('data', (c) => chunks.push(c));
|
|
105
|
+
res.on('end', () => {
|
|
106
|
+
if (res.statusCode !== 200) {
|
|
107
|
+
done({ kind: 'no-endpoint' });
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
try {
|
|
111
|
+
const body = JSON.parse(Buffer.concat(chunks).toString('utf8'));
|
|
112
|
+
done(isVersionInfo(body) ? { kind: 'version', info: body } : { kind: 'no-endpoint' });
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
done({ kind: 'no-endpoint' });
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
res.on('error', () => {
|
|
119
|
+
done({ kind: 'no-endpoint' });
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
req.on('timeout', () => {
|
|
123
|
+
req.destroy();
|
|
124
|
+
done({ kind: 'unreachable' });
|
|
125
|
+
});
|
|
126
|
+
req.on('error', () => {
|
|
127
|
+
done({ kind: 'unreachable' });
|
|
128
|
+
});
|
|
129
|
+
req.end();
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
const sleepReal = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
133
|
+
/** Run `bind` until it succeeds, retrying only errors `isRetryable` accepts
|
|
134
|
+
* (a busy port), at most `attempts` times with `delayMs` between tries. */
|
|
135
|
+
export async function retryBind(bind, opts) {
|
|
136
|
+
const sleep = opts.sleep ?? sleepReal;
|
|
137
|
+
for (let attempt = 1;; attempt++) {
|
|
138
|
+
try {
|
|
139
|
+
return await bind();
|
|
140
|
+
}
|
|
141
|
+
catch (err) {
|
|
142
|
+
if (!opts.isRetryable(err) || attempt >= opts.attempts)
|
|
143
|
+
throw err;
|
|
144
|
+
await sleep(opts.delayMs);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
/** Unix grace period between SIGTERM and SIGKILL. */
|
|
149
|
+
export const STEP_DOWN_GRACE_MS = 3000;
|
|
150
|
+
const STEP_DOWN_POLL_MS = 100;
|
|
151
|
+
function isAliveReal(pid) {
|
|
152
|
+
try {
|
|
153
|
+
process.kill(pid, 0);
|
|
154
|
+
return true;
|
|
155
|
+
}
|
|
156
|
+
catch (err) {
|
|
157
|
+
// EPERM: it exists but belongs to someone else.
|
|
158
|
+
return err.code === 'EPERM';
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Stop a process: Windows `taskkill /PID n /F`; Unix `SIGTERM`, wait up to
|
|
163
|
+
* 3 s for it to exit, then `SIGKILL`. Throws when the kill itself fails.
|
|
164
|
+
*/
|
|
165
|
+
export async function stepDownProcess(pid, deps = {}) {
|
|
166
|
+
const os = deps.os ?? platform();
|
|
167
|
+
if (os === 'win32') {
|
|
168
|
+
(deps.execFile ?? defaultExecFile)('taskkill', ['/PID', String(pid), '/F'], {
|
|
169
|
+
timeout: 5000,
|
|
170
|
+
});
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
const kill = deps.kill ?? ((p, s) => void process.kill(p, s));
|
|
174
|
+
const isAlive = deps.isAlive ?? isAliveReal;
|
|
175
|
+
const sleep = deps.sleep ?? sleepReal;
|
|
176
|
+
const now = deps.now ?? Date.now;
|
|
177
|
+
kill(pid, 'SIGTERM');
|
|
178
|
+
const deadline = now() + STEP_DOWN_GRACE_MS;
|
|
179
|
+
while (isAlive(pid)) {
|
|
180
|
+
if (now() >= deadline) {
|
|
181
|
+
kill(pid, 'SIGKILL');
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
await sleep(STEP_DOWN_POLL_MS);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
//# sourceMappingURL=election.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"election.js","sourceRoot":"","sources":["../../src/lifecycle/election.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,eAAe,EAAqB,MAAM,gBAAgB,CAAC;AA4DpE,iDAAiD;AACjD,MAAM,UAAU,cAAc,CAAC,KAAkB;IAC/C,MAAM,EAAE,KAAK,EAAE,GAAG,KAAK,CAAC;IACxB,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QAC7B,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC;QAClC,IAAI,aAAa,CAAC,MAAM,EAAE,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;YAC3C,OAAO;gBACL,QAAQ,EAAE,WAAW;gBACrB,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,GAAG;gBACnB,MAAM,EAAE,mBAAmB,MAAM,kBAAkB,KAAK,CAAC,KAAK,EAAE;aACjE,CAAC;QACJ,CAAC;QACD,OAAO;YACL,QAAQ,EAAE,OAAO;YACjB,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,GAAG;YACnB,MAAM,EAAE,mBAAmB,MAAM,sBAAsB,KAAK,CAAC,KAAK,EAAE;SACrE,CAAC;IACJ,CAAC;IACD,IAAI,KAAK,CAAC,IAAI,KAAK,aAAa,EAAE,CAAC;QACjC,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,EAAE,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,kCAAkC,EAAE,CAAC;IAChG,CAAC;IACD,MAAM,EAAE,QAAQ,EAAE,cAAc,EAAE,kBAAkB,EAAE,GAAG,KAAK,CAAC;IAC/D,IAAI,QAAQ,KAAK,IAAI,IAAI,cAAc,KAAK,IAAI,IAAI,kBAAkB,KAAK,IAAI,EAAE,CAAC;QAChF,OAAO;YACL,QAAQ,EAAE,OAAO;YACjB,GAAG,EAAE,QAAQ;YACb,MAAM,EAAE,+DAA+D;SACxE,CAAC;IACJ,CAAC;IACD,IAAI,cAAc,GAAG,kBAAkB,EAAE,CAAC;QACxC,OAAO;YACL,QAAQ,EAAE,WAAW;YACrB,GAAG,EAAE,QAAQ;YACb,MAAM,EAAE,2EAA2E;SACpF,CAAC;IACJ,CAAC;IACD,OAAO;QACL,QAAQ,EAAE,OAAO;QACjB,GAAG,EAAE,QAAQ;QACb,MAAM,EAAE,0EAA0E;KACnF,CAAC;AACJ,CAAC;AAYD;8EAC8E;AAC9E,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,IAAkB;IAClD,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC;IACjC,IAAI,QAAQ,GAAkB,IAAI,CAAC;IACnC,IAAI,cAAc,GAAkB,IAAI,CAAC;IACzC,IAAI,kBAAkB,GAAkB,IAAI,CAAC;IAC7C,IAAI,KAAK,CAAC,IAAI,KAAK,aAAa,EAAE,CAAC;QACjC,QAAQ,GAAG,IAAI,CAAC,YAAY,EAAE,CAAC;QAC/B,cAAc,GAAG,QAAQ,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC,CAAC;QAC5E,kBAAkB,GAAG,IAAI,CAAC,kBAAkB,EAAE,CAAC;IACjD,CAAC;IACD,MAAM,OAAO,GAAG,cAAc,CAAC;QAC7B,KAAK,EAAE,IAAI,CAAC,YAAY;QACxB,KAAK;QACL,QAAQ;QACR,cAAc;QACd,kBAAkB;KACnB,CAAC,CAAC;IACH,IAAI,OAAO,CAAC,QAAQ,KAAK,WAAW,IAAI,OAAO,CAAC,GAAG,KAAK,IAAI;QAAE,OAAO,OAAO,CAAC;IAC7E,IAAI,CAAC;QACH,MAAM,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACjC,OAAO,OAAO,CAAC;IACjB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO;YACL,QAAQ,EAAE,OAAO;YACjB,GAAG,EAAE,OAAO,CAAC,GAAG;YAChB,MAAM,EAAE,oBAAoB,OAAO,CAAC,GAAG,YAAY,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE;SACtG,CAAC;IACJ,CAAC;AACH,CAAC;AAED,SAAS,aAAa,CAAC,CAAU;IAC/B,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IACtD,MAAM,CAAC,GAAG,CAA4B,CAAC;IACvC,OAAO,CACL,OAAO,CAAC,CAAC,IAAI,KAAK,QAAQ;QAC1B,OAAO,CAAC,CAAC,OAAO,KAAK,QAAQ;QAC7B,OAAO,CAAC,CAAC,GAAG,KAAK,QAAQ;QACzB,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC;QACvB,CAAC,CAAC,GAAG,GAAG,CAAC;QACT,OAAO,CAAC,CAAC,SAAS,KAAK,QAAQ;QAC/B,CAAC,CAAC,CAAC,gBAAgB,KAAK,IAAI,IAAI,OAAO,CAAC,CAAC,gBAAgB,KAAK,QAAQ,CAAC,CACxE,CAAC;AACJ,CAAC;AAED,yDAAyD;AACzD,MAAM,UAAU,YAAY,CAC1B,IAAY,EACZ,SAAS,GAAG,IAAI,EAChB,IAAI,GAAG,WAAW;IAElB,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,IAAI,OAAO,GAAG,KAAK,CAAC;QACpB,MAAM,IAAI,GAAG,CAAC,KAAmB,EAAQ,EAAE;YACzC,IAAI,OAAO;gBAAE,OAAO;YACpB,OAAO,GAAG,IAAI,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,CAAC;QACjB,CAAC,CAAC;QACF,MAAM,GAAG,GAAG,OAAO,CACjB,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,EACnE,CAAC,GAAG,EAAE,EAAE;YACN,MAAM,MAAM,GAAa,EAAE,CAAC;YAC5B,GAAG,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,CAAS,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;YAC9C,GAAG,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE;gBACjB,IAAI,GAAG,CAAC,UAAU,KAAK,GAAG,EAAE,CAAC;oBAC3B,IAAI,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,CAAC,CAAC;oBAC9B,OAAO;gBACT,CAAC;gBACD,IAAI,CAAC;oBACH,MAAM,IAAI,GAAY,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;oBACzE,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,CAAC,CAAC;gBACxF,CAAC;gBAAC,MAAM,CAAC;oBACP,IAAI,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,CAAC,CAAC;gBAChC,CAAC;YACH,CAAC,CAAC,CAAC;YACH,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;gBACnB,IAAI,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,CAAC,CAAC;YAChC,CAAC,CAAC,CAAC;QACL,CAAC,CACF,CAAC;QACF,GAAG,CAAC,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE;YACrB,GAAG,CAAC,OAAO,EAAE,CAAC;YACd,IAAI,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,CAAC,CAAC;QAChC,CAAC,CAAC,CAAC;QACH,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;YACnB,IAAI,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,CAAC,CAAC;QAChC,CAAC,CAAC,CAAC;QACH,GAAG,CAAC,GAAG,EAAE,CAAC;IACZ,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,SAAS,GAAG,CAAC,EAAU,EAAiB,EAAE,CAAC,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC;AAEnG;2EAC2E;AAC3E,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,IAAsB,EACtB,IAKC;IAED,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,SAAS,CAAC;IACtC,KAAK,IAAI,OAAO,GAAG,CAAC,GAAI,OAAO,EAAE,EAAE,CAAC;QAClC,IAAI,CAAC;YACH,OAAO,MAAM,IAAI,EAAE,CAAC;QACtB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,OAAO,IAAI,IAAI,CAAC,QAAQ;gBAAE,MAAM,GAAG,CAAC;YAClE,MAAM,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC5B,CAAC;IACH,CAAC;AACH,CAAC;AAED,qDAAqD;AACrD,MAAM,CAAC,MAAM,kBAAkB,GAAG,IAAI,CAAC;AACvC,MAAM,iBAAiB,GAAG,GAAG,CAAC;AAE9B,SAAS,WAAW,CAAC,GAAW;IAC9B,IAAI,CAAC;QACH,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;QACrB,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,gDAAgD;QAChD,OAAQ,GAA6B,CAAC,IAAI,KAAK,OAAO,CAAC;IACzD,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,GAAW,EACX,OAOI,EAAE;IAEN,MAAM,EAAE,GAAG,IAAI,CAAC,EAAE,IAAI,QAAQ,EAAE,CAAC;IACjC,IAAI,EAAE,KAAK,OAAO,EAAE,CAAC;QACnB,CAAC,IAAI,CAAC,QAAQ,IAAI,eAAe,CAAC,CAAC,UAAU,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC,EAAE;YAC1E,OAAO,EAAE,IAAI;SACd,CAAC,CAAC;QACH,OAAO;IACT,CAAC;IACD,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,IAAI,CAAC,CAAC,CAAS,EAAE,CAAiB,EAAE,EAAE,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IACtF,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,IAAI,WAAW,CAAC;IAC5C,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,SAAS,CAAC;IACtC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC;IACjC,IAAI,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;IACrB,MAAM,QAAQ,GAAG,GAAG,EAAE,GAAG,kBAAkB,CAAC;IAC5C,OAAO,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QACpB,IAAI,GAAG,EAAE,IAAI,QAAQ,EAAE,CAAC;YACtB,IAAI,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;YACrB,OAAO;QACT,CAAC;QACD,MAAM,KAAK,CAAC,iBAAiB,CAAC,CAAC;IACjC,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Process enumeration for the lifecycle features: `browser-link stop`
|
|
3
|
+
* (kill every live bridge, not only the port owner), `doctor` (list them)
|
|
4
|
+
* and the primary election (when did the port owner start?).
|
|
5
|
+
*
|
|
6
|
+
* Every OS call goes through an injectable `ExecFileLike`, the same
|
|
7
|
+
* dependency-injection pattern `auth/silent-debugger.ts` uses, but
|
|
8
|
+
* synchronous: `runFreePort` is synchronous and consumed as such by the CLI,
|
|
9
|
+
* the TUI and `self-update`. Nothing in this module throws: a failed or
|
|
10
|
+
* unparseable listing degrades to "no processes" / `null`.
|
|
11
|
+
*
|
|
12
|
+
* Windows uses `Get-CimInstance Win32_Process` (never `wmic`: deprecated and
|
|
13
|
+
* absent on newer Windows 11 installs). Unix uses BSD-style `ps -axo`,
|
|
14
|
+
* accepted by both macOS and procps.
|
|
15
|
+
*/
|
|
16
|
+
/** `execFileSync`-shaped primitive: argv array, no shell, stdout as text. */
|
|
17
|
+
export type ExecFileLike = (file: string, args: readonly string[], options: {
|
|
18
|
+
timeout: number;
|
|
19
|
+
}) => string;
|
|
20
|
+
/** One row of the OS process table. `startedAt` is epoch milliseconds, or
|
|
21
|
+
* `null` when the OS did not report a usable start time. */
|
|
22
|
+
export interface ProcessEntry {
|
|
23
|
+
pid: number;
|
|
24
|
+
ppid: number;
|
|
25
|
+
startedAt: number | null;
|
|
26
|
+
command: string;
|
|
27
|
+
}
|
|
28
|
+
export declare const defaultExecFile: ExecFileLike;
|
|
29
|
+
/**
|
|
30
|
+
* Parse a process start time in any of the forms the OS tools print:
|
|
31
|
+
* PowerShell 5.1's JSON `/Date(ms)/`, ISO 8601 (PowerShell 7 JSON,
|
|
32
|
+
* `ToString('o')`), a WMI datetime `yyyyMMddHHmmss.ffffff±UUU` (offset in
|
|
33
|
+
* minutes) and `ps`'s `lstart` (`Mon Nov 9 02:17:44 2026`, local time).
|
|
34
|
+
*/
|
|
35
|
+
export declare function parseProcessTime(raw: string): number | null;
|
|
36
|
+
/** Parse `Get-CimInstance … | ConvertTo-Json -Compress`. One process comes
|
|
37
|
+
* out as a bare object, several as an array; both are accepted. */
|
|
38
|
+
export declare function parseWindowsProcessList(stdout: string): ProcessEntry[];
|
|
39
|
+
/** Parse `ps -axo pid=,ppid=,lstart=,command=` (C locale). */
|
|
40
|
+
export declare function parseUnixProcessList(stdout: string): ProcessEntry[];
|
|
41
|
+
/** The whole process table, or `[]` when it cannot be read. */
|
|
42
|
+
export declare function listProcesses(os?: NodeJS.Platform, exec?: ExecFileLike): ProcessEntry[];
|
|
43
|
+
/** Start time of one process (epoch ms), or `null` when unknown. */
|
|
44
|
+
export declare function readProcessStartTime(pid: number, opts?: {
|
|
45
|
+
os?: NodeJS.Platform;
|
|
46
|
+
exec?: ExecFileLike;
|
|
47
|
+
}): number | null;
|
|
48
|
+
/**
|
|
49
|
+
* Whether a command line runs this package, and with which subcommand
|
|
50
|
+
* (`null` = none, i.e. the MCP server or the TUI). Only the script argument
|
|
51
|
+
* counts (the first non-flag argument after the runtime, or the package
|
|
52
|
+
* spec after npm's `npx-cli.js`), so a process that merely mentions a
|
|
53
|
+
* browser-link path as an option value is never matched.
|
|
54
|
+
*/
|
|
55
|
+
export declare function matchBrowserLinkCommand(command: string): {
|
|
56
|
+
subcommand: string | null;
|
|
57
|
+
} | null;
|
|
58
|
+
/** Live browser-link bridges in `list`, excluding `selfPid`, its ancestor
|
|
59
|
+
* chain (the shell or client that launched it) and non-bridge commands. */
|
|
60
|
+
export declare function findBridgeProcesses(list: readonly ProcessEntry[], selfPid: number): ProcessEntry[];
|
|
61
|
+
export declare function listBridgeProcesses(opts?: {
|
|
62
|
+
os?: NodeJS.Platform;
|
|
63
|
+
exec?: ExecFileLike;
|
|
64
|
+
selfPid?: number;
|
|
65
|
+
}): ProcessEntry[];
|
|
66
|
+
/** Short, display-only form: runtime name, `browser-link`, then arguments. */
|
|
67
|
+
export declare function shortCommand(command: string, max?: number): string;
|