@mostra/cli 0.3.0 → 0.5.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/dist/bin.js +1 -1
- package/dist/{flush-BCuWs0Zh.js → flush-CFJkvi2R.js} +140 -22
- package/dist/flush-CFJkvi2R.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/reference/cli.md +3 -3
- package/dist/reference/diagnostics.json +3 -3
- package/dist/reference/errors/MOSTRA_ENV_SERVICE_FAILED.md +2 -2
- package/dist/reference/errors/MOSTRA_HOME_NOT_ALWAYS_ON.md +1 -1
- package/dist/reference/telemetry.md +1 -1
- package/dist/{run-BJaRnz-Q.js → run-BpVu5uQ8.js} +152 -55
- package/dist/run-BpVu5uQ8.js.map +1 -0
- package/dist/run-CC8PNXLU.js +2 -0
- package/dist/telemetry/flush-main.js +1 -1
- package/package.json +5 -5
- package/src/browser.ts +72 -14
- package/src/client.ts +28 -27
- package/src/commands/admin.ts +19 -11
- package/src/commands/authoring.ts +3 -1
- package/src/commands/dev.ts +5 -5
- package/src/commands/interaction.ts +11 -0
- package/src/commands/viewing.ts +5 -2
- package/src/detached.ts +49 -0
- package/src/home.ts +36 -8
- package/src/platform.ts +24 -0
- package/src/run.ts +5 -7
- package/src/telemetry/events.ts +2 -2
- package/src/telemetry/flush.ts +7 -1
- package/src/telemetry/index.ts +4 -1
- package/src/telemetry/store.ts +117 -17
- package/dist/flush-BCuWs0Zh.js.map +0 -1
- package/dist/run-BJaRnz-Q.js.map +0 -1
- package/dist/run-CMyjOm0Q.js +0 -2
|
@@ -1,15 +1,14 @@
|
|
|
1
|
-
import { a as
|
|
1
|
+
import { a as detachedEnvironment, c as durationBucket, d as buildEndpoint, f as decide, g as packageRoot, h as fromSource, i as detachedDirectory, l as minute, m as cliVersion, n as startFlusher, o as TelemetryEvent, p as truthy, r as TelemetryStore, s as catalogCodes, u as detectHarness } from "./flush-CFJkvi2R.js";
|
|
2
2
|
import { n as exitCodeFor, t as EXIT } from "./exit-CBIf8nAU.js";
|
|
3
3
|
import { spawn } from "node:child_process";
|
|
4
|
-
import { existsSync, lstatSync, mkdtempSync, readFileSync, readdirSync, readlinkSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
5
|
-
import { isAbsolute, join, resolve } from "node:path";
|
|
4
|
+
import { existsSync, lstatSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, readlinkSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
5
|
+
import { isAbsolute, join, resolve, win32 } from "node:path";
|
|
6
6
|
import { ARTIFACT_PROGRESS, CLI_CLIENT_NAME, DEFAULT_DOCS_BASE, DEV_NOTIFY, FieldOp, HOST_PROTOCOL_REVISIONS, LineSplitter, MutationId, RpcError, RpcPeer, artifactCreateMethod, artifactGetMethod, artifactListMethod, artifactPublishMethod, artifactVerifyMethod, createDiagnostic, depsAddMethod, devStartMethod, devStopMethod, diagnosticCatalog, encodeLine, gcRunMethod, instanceCheckMethod, instanceStatusMethod, livePushMethod, mostraInitializeMethod, mostraShutdownMethod, resultsGetMethod, secretDeleteMethod, secretListMethod, secretSetMethod, stateGetMethod, statePatchMethod, viewDefaultMethod, viewLaunchMethod, viewRevokeMethod, viewRotateMethod, viewUrlMethod, waitMethod } from "@mostra/protocol";
|
|
7
|
-
import { checkBrowser, checkFiles, enclosingBundle, readBundle, readSetup, resolveRuntime } from "@mostra/sdk";
|
|
7
|
+
import { checkBrowser, checkFiles, connectControl, enclosingBundle, readBundle, readSetup, resolveRuntime } from "@mostra/sdk";
|
|
8
8
|
import { homedir, tmpdir } from "node:os";
|
|
9
9
|
import { parseArgs } from "node:util";
|
|
10
10
|
import { randomUUID } from "node:crypto";
|
|
11
11
|
import { createInterface } from "node:readline";
|
|
12
|
-
import { createConnection } from "node:net";
|
|
13
12
|
//#region src/command.ts
|
|
14
13
|
/** Defines a command; the identity function, for types. */
|
|
15
14
|
function defineCommand(command) {
|
|
@@ -115,24 +114,35 @@ function distance(a, b) {
|
|
|
115
114
|
const nonEmpty = (value) => value === void 0 || value === "" ? void 0 : value;
|
|
116
115
|
/**
|
|
117
116
|
* The home, in D84's order: `--home`, then `MOSTRA_HOME`, then the platform default (`~/.mostra`
|
|
118
|
-
* on macOS, `${XDG_DATA_HOME:-~/.local/share}/mostra` on Linux
|
|
117
|
+
* on macOS, `${XDG_DATA_HOME:-~/.local/share}/mostra` on Linux, `%LOCALAPPDATA%\mostra` on
|
|
118
|
+
* Windows), the same order and defaults the runtime uses (its `platform` module).
|
|
119
119
|
*/
|
|
120
120
|
function resolveHome(options) {
|
|
121
121
|
const chosen = nonEmpty(options.home) ?? nonEmpty(options.env["MOSTRA_HOME"]);
|
|
122
122
|
if (chosen !== void 0) return resolve(options.cwd, chosen);
|
|
123
|
+
const platform = options.platform ?? process.platform;
|
|
124
|
+
if (platform === "win32") {
|
|
125
|
+
const local = nonEmpty(options.env["LOCALAPPDATA"]);
|
|
126
|
+
return win32.join(local !== void 0 && win32.isAbsolute(local) ? local : win32.join(homedir(), "AppData", "Local"), "mostra");
|
|
127
|
+
}
|
|
123
128
|
const user = nonEmpty(options.env["HOME"]) ?? homedir();
|
|
124
|
-
if (
|
|
129
|
+
if (platform === "darwin") return join(user, ".mostra");
|
|
125
130
|
const data = nonEmpty(options.env["XDG_DATA_HOME"]);
|
|
126
131
|
return join(data !== void 0 && isAbsolute(data) ? data : join(user, ".local", "share"), "mostra");
|
|
127
132
|
}
|
|
128
133
|
/**
|
|
129
134
|
* The CLI's config directory (D140 item 5): `MOSTRA_CONFIG_DIR`, else
|
|
130
|
-
* `${XDG_CONFIG_HOME:-~/.config}/mostra
|
|
131
|
-
*
|
|
135
|
+
* `${XDG_CONFIG_HOME:-~/.config}/mostra` on macOS and Linux, and `%APPDATA%\mostra` on Windows,
|
|
136
|
+
* where per-user settings live (D151). It holds telemetry's choice and queue, per
|
|
137
|
+
* operating-system user, never per home.
|
|
132
138
|
*/
|
|
133
|
-
function configDir(env, cwd) {
|
|
139
|
+
function configDir(env, cwd, platform = process.platform) {
|
|
134
140
|
const override = nonEmpty(env["MOSTRA_CONFIG_DIR"]);
|
|
135
141
|
if (override !== void 0) return resolve(cwd, override);
|
|
142
|
+
if (platform === "win32") {
|
|
143
|
+
const roaming = nonEmpty(env["APPDATA"]);
|
|
144
|
+
return win32.join(roaming !== void 0 && win32.isAbsolute(roaming) ? roaming : win32.join(homedir(), "AppData", "Roaming"), "mostra");
|
|
145
|
+
}
|
|
136
146
|
const xdg = nonEmpty(env["XDG_CONFIG_HOME"]);
|
|
137
147
|
const base = xdg !== void 0 && isAbsolute(xdg) ? xdg : join(nonEmpty(env["HOME"]) ?? homedir(), ".config");
|
|
138
148
|
return join(base, "mostra");
|
|
@@ -147,7 +157,8 @@ async function locateRuntime(env) {
|
|
|
147
157
|
const runtime = await resolveRuntime({ env });
|
|
148
158
|
return {
|
|
149
159
|
node: runtime.node,
|
|
150
|
-
entry: runtime.runtimeEntry
|
|
160
|
+
entry: runtime.runtimeEntry,
|
|
161
|
+
runtime
|
|
151
162
|
};
|
|
152
163
|
}
|
|
153
164
|
/**
|
|
@@ -380,6 +391,23 @@ function parseCommandLine(commands, argv) {
|
|
|
380
391
|
};
|
|
381
392
|
}
|
|
382
393
|
//#endregion
|
|
394
|
+
//#region src/platform.ts
|
|
395
|
+
/**
|
|
396
|
+
* The CLI's platform gate (D35, D146, RT-07), the runtime's rule (`home/platform.ts` in
|
|
397
|
+
* `@mostra/runtime`) repeated where the CLI must decide before it reaches any runtime: macOS and
|
|
398
|
+
* Linux on x64 and arm64; Windows on x64 and arm64 only behind the development switch
|
|
399
|
+
* `MOSTRA_DEV_WINDOWS=1`, which only Mostra's CI sets until WP-W3; everything else fails closed.
|
|
400
|
+
*/
|
|
401
|
+
/** `MOSTRA_PLATFORM_UNSUPPORTED` for a platform Mostra doesn't run on, else `undefined`. */
|
|
402
|
+
function checkPlatform(platform, arch, env) {
|
|
403
|
+
if (arch === "x64" || arch === "arm64") {
|
|
404
|
+
if (platform === "darwin" || platform === "linux") return void 0;
|
|
405
|
+
if (platform === "win32" && env["MOSTRA_DEV_WINDOWS"] === "1") return void 0;
|
|
406
|
+
}
|
|
407
|
+
const name = platform === "win32" ? `Windows (${platform}-${arch})` : `${platform}-${arch}`;
|
|
408
|
+
return createDiagnostic("MOSTRA_PLATFORM_UNSUPPORTED", { message: `Mostra doesn't run on ${name}; it supports macOS and Linux on x64 and arm64.` });
|
|
409
|
+
}
|
|
410
|
+
//#endregion
|
|
383
411
|
//#region src/telemetry/zones.generated.ts
|
|
384
412
|
/** The time zones where Mostra asks before sending telemetry (D140 item 10), matched as given. */
|
|
385
413
|
const ASK_FIRST_ZONES = [
|
|
@@ -1321,7 +1349,7 @@ function telemetrySession(options) {
|
|
|
1321
1349
|
at: minute(new Date(now())),
|
|
1322
1350
|
installId: id.id,
|
|
1323
1351
|
version: cliVersion(),
|
|
1324
|
-
os: process.platform === "darwin"
|
|
1352
|
+
os: process.platform === "darwin" || process.platform === "win32" ? process.platform : "linux",
|
|
1325
1353
|
arch: process.arch,
|
|
1326
1354
|
node: Number(process.versions.node.split(".")[0]),
|
|
1327
1355
|
harness,
|
|
@@ -1635,7 +1663,7 @@ async function service(context, action) {
|
|
|
1635
1663
|
const noStart = {
|
|
1636
1664
|
name: "no-start",
|
|
1637
1665
|
kind: "boolean",
|
|
1638
|
-
summary: "Write or remove the service's
|
|
1666
|
+
summary: "Write or remove the service's definition without asking launchd, systemd or Task Scheduler to load it."
|
|
1639
1667
|
};
|
|
1640
1668
|
/** `mostra daemon start`. */
|
|
1641
1669
|
const daemonStartCommand = defineCommand({
|
|
@@ -1747,7 +1775,7 @@ const daemonStatusCommand = defineCommand({
|
|
|
1747
1775
|
/** `mostra daemon install`. */
|
|
1748
1776
|
const daemonInstallCommand = defineCommand({
|
|
1749
1777
|
path: ["daemon", "install"],
|
|
1750
|
-
summary: "Run the home's daemon as a login service (launchd or
|
|
1778
|
+
summary: "Run the home's daemon as a login service (launchd, systemd or Task Scheduler), so links answer at any hour.",
|
|
1751
1779
|
flags: [noStart],
|
|
1752
1780
|
output: {
|
|
1753
1781
|
type: "ServiceStatus",
|
|
@@ -1881,6 +1909,14 @@ const gcCommand = defineCommand({
|
|
|
1881
1909
|
}
|
|
1882
1910
|
});
|
|
1883
1911
|
/** `mostra doctor`. */
|
|
1912
|
+
/**
|
|
1913
|
+
* The check that fails `mostra doctor`: the first whose status isn't one this CLI knows is no
|
|
1914
|
+
* failure, so a newer instance's status it doesn't know counts as an error (Opus review of MT-01,
|
|
1915
|
+
* 2), never as passing.
|
|
1916
|
+
*/
|
|
1917
|
+
function firstFailing(checks) {
|
|
1918
|
+
return checks.find((check) => check.status !== "ok" && check.status !== "warning");
|
|
1919
|
+
}
|
|
1884
1920
|
const doctorCommand = defineCommand({
|
|
1885
1921
|
path: ["doctor"],
|
|
1886
1922
|
summary: "Check the platform, the runtime, the home, its daemon and ledger, and the login service.",
|
|
@@ -1898,14 +1934,15 @@ const doctorCommand = defineCommand({
|
|
|
1898
1934
|
async run(context) {
|
|
1899
1935
|
const checks = [];
|
|
1900
1936
|
const node = Number(process.versions.node.split(".")[0]);
|
|
1901
|
-
|
|
1937
|
+
const unsupported = checkPlatform(process.platform, process.arch, context.env);
|
|
1938
|
+
checks.push(unsupported === void 0 ? {
|
|
1902
1939
|
name: "platform",
|
|
1903
1940
|
status: "ok",
|
|
1904
1941
|
detail: `${process.platform} ${process.arch}, Node ${process.versions.node}`
|
|
1905
1942
|
} : {
|
|
1906
1943
|
name: "platform",
|
|
1907
1944
|
status: "error",
|
|
1908
|
-
diagnostic:
|
|
1945
|
+
diagnostic: unsupported
|
|
1909
1946
|
});
|
|
1910
1947
|
if (node < 24) checks.push({
|
|
1911
1948
|
name: "node",
|
|
@@ -1965,7 +2002,7 @@ const doctorCommand = defineCommand({
|
|
|
1965
2002
|
status: "ok",
|
|
1966
2003
|
detail: telemetry.text.split("\n")[0]
|
|
1967
2004
|
});
|
|
1968
|
-
const firstError = checks
|
|
2005
|
+
const firstError = firstFailing(checks);
|
|
1969
2006
|
const human = checks.map((check) => {
|
|
1970
2007
|
const mark = check.status === "ok" ? "ok " : check.status === "warning" ? "warn" : "FAIL";
|
|
1971
2008
|
const what = check.diagnostic ? `${check.diagnostic.code}: ${check.diagnostic.message}` : check.detail ?? "";
|
|
@@ -2344,7 +2381,7 @@ const verifyCommand = defineCommand({
|
|
|
2344
2381
|
return {
|
|
2345
2382
|
result: verdict,
|
|
2346
2383
|
human: verdictText(verdict),
|
|
2347
|
-
...verdict.status === "
|
|
2384
|
+
...verdict.status === "pass" ? {} : { exit: EXIT.failure }
|
|
2348
2385
|
};
|
|
2349
2386
|
}
|
|
2350
2387
|
});
|
|
@@ -2523,14 +2560,17 @@ const devCommand = defineCommand({
|
|
|
2523
2560
|
}, `Development preview, not published: ${started.url}\nPress Ctrl-C to stop.\n`);
|
|
2524
2561
|
for (const event of early.splice(0)) if (event.sessionId === sessionId) show(event);
|
|
2525
2562
|
let stop = () => {};
|
|
2563
|
+
const signals = [
|
|
2564
|
+
"SIGINT",
|
|
2565
|
+
"SIGTERM",
|
|
2566
|
+
"SIGBREAK"
|
|
2567
|
+
];
|
|
2526
2568
|
const ended = await new Promise((done) => {
|
|
2527
2569
|
stop = () => done("signal");
|
|
2528
|
-
process.once(
|
|
2529
|
-
process.once("SIGTERM", stop);
|
|
2570
|
+
for (const signal of signals) process.once(signal, stop);
|
|
2530
2571
|
connection?.closed.then(() => done("closed"));
|
|
2531
2572
|
});
|
|
2532
|
-
process.off(
|
|
2533
|
-
process.off("SIGTERM", stop);
|
|
2573
|
+
for (const signal of signals) process.off(signal, stop);
|
|
2534
2574
|
if (ended === "closed") throw new RpcError(createDiagnostic("MOSTRA_HOST_CONNECTION_CLOSED", {
|
|
2535
2575
|
message: "The instance ended the development preview (it stopped or restarted).",
|
|
2536
2576
|
hint: "Run mostra dev again."
|
|
@@ -2607,6 +2647,11 @@ const waitCommand = defineCommand({
|
|
|
2607
2647
|
result,
|
|
2608
2648
|
human: `${result.event.type} at cursor ${result.cursor}\n`
|
|
2609
2649
|
};
|
|
2650
|
+
if (result.status !== "still_waiting") return {
|
|
2651
|
+
result,
|
|
2652
|
+
human: `wait ended with "${String(result.status)}", which this CLI doesn't know\n`,
|
|
2653
|
+
exit: EXIT.failure
|
|
2654
|
+
};
|
|
2610
2655
|
if (deadline === void 0) {
|
|
2611
2656
|
after = result.cursor;
|
|
2612
2657
|
continue;
|
|
@@ -2876,8 +2921,11 @@ const statePatchCommand = defineCommand({
|
|
|
2876
2921
|
* review finding 4). A program's arguments are readable by every local user (`ps`,
|
|
2877
2922
|
* `/proc/<pid>/cmdline`), so the opener gets the path of a page that redirects to the launch URL,
|
|
2878
2923
|
* written mode 0600 in a fresh directory of mode 0700, as Jupyter's server does for its token.
|
|
2879
|
-
*
|
|
2880
|
-
*
|
|
2924
|
+
* Windows has no modes: there the page goes under the user's own profile
|
|
2925
|
+
* (`%LOCALAPPDATA%\mostra-cli\open`), whose access list only the user, `SYSTEM` and
|
|
2926
|
+
* administrators pass, never under `%TEMP%`, which may name a folder other users can read (Opus
|
|
2927
|
+
* review of WP-W2, finding 1). The launch ticket in the page is single-use and lives a minute, so a
|
|
2928
|
+
* page left behind is harmless; each launch removes the ones older than that.
|
|
2881
2929
|
*/
|
|
2882
2930
|
/** The prefix of the directories launch pages live in. */
|
|
2883
2931
|
const PREFIX = "mostra-open-";
|
|
@@ -2975,17 +3023,49 @@ function splitCommand(command) {
|
|
|
2975
3023
|
if (inWord) words.push(word);
|
|
2976
3024
|
return words;
|
|
2977
3025
|
}
|
|
3026
|
+
/** The program that opens a file with its default application, by platform. */
|
|
3027
|
+
function platformOpener(platform) {
|
|
3028
|
+
if (platform === "darwin") return ["open"];
|
|
3029
|
+
if (platform === "win32") return ["explorer.exe"];
|
|
3030
|
+
return ["xdg-open"];
|
|
3031
|
+
}
|
|
2978
3032
|
/**
|
|
2979
|
-
*
|
|
2980
|
-
*
|
|
2981
|
-
*
|
|
3033
|
+
* Splits a command as Windows programs read their command lines, for `$BROWSER` there: words
|
|
3034
|
+
* separated by spaces, double quotes grouping words with spaces (`"C:\Program Files\Firefox\
|
|
3035
|
+
* firefox.exe" %s`), and backslashes kept as they are.
|
|
3036
|
+
*/
|
|
3037
|
+
function splitWindowsCommand(command) {
|
|
3038
|
+
const words = [];
|
|
3039
|
+
let word = "";
|
|
3040
|
+
let inWord = false;
|
|
3041
|
+
let quoted = false;
|
|
3042
|
+
for (const char of command) if (char === "\"") {
|
|
3043
|
+
quoted = !quoted;
|
|
3044
|
+
inWord = true;
|
|
3045
|
+
} else if (!quoted && /\s/.test(char)) {
|
|
3046
|
+
if (inWord) words.push(word);
|
|
3047
|
+
word = "";
|
|
3048
|
+
inWord = false;
|
|
3049
|
+
} else {
|
|
3050
|
+
word += char;
|
|
3051
|
+
inWord = true;
|
|
3052
|
+
}
|
|
3053
|
+
if (inWord) words.push(word);
|
|
3054
|
+
return words;
|
|
3055
|
+
}
|
|
3056
|
+
/**
|
|
3057
|
+
* The commands that may open `target`, in order: each entry of `$BROWSER` (separated by the
|
|
3058
|
+
* platform's path delimiter, `:` or `;` on Windows, its `%s` replaced by the target, else the
|
|
3059
|
+
* target appended, `%%` a literal `%`, as `xdg-open` and Python's `webbrowser` read it), else the
|
|
3060
|
+
* platform's opener.
|
|
2982
3061
|
*/
|
|
2983
3062
|
function browserCommands(target, env, platform = process.platform) {
|
|
2984
3063
|
const chosen = env["BROWSER"];
|
|
2985
|
-
|
|
3064
|
+
if (chosen === void 0 || chosen.trim() === "") return [[...platformOpener(platform), target]];
|
|
3065
|
+
const entries = chosen.split(platform === "win32" ? ";" : ":").filter((entry) => entry.trim() !== "");
|
|
2986
3066
|
const commands = [];
|
|
2987
3067
|
for (const entry of entries) {
|
|
2988
|
-
const words = splitCommand(entry);
|
|
3068
|
+
const words = platform === "win32" ? splitWindowsCommand(entry) : splitCommand(entry);
|
|
2989
3069
|
if (words.length === 0) continue;
|
|
2990
3070
|
let substituted = false;
|
|
2991
3071
|
const argv = words.map((word) => word.replaceAll(/%%|%s/g, (match) => {
|
|
@@ -3017,11 +3097,26 @@ function start(argv) {
|
|
|
3017
3097
|
}
|
|
3018
3098
|
});
|
|
3019
3099
|
}
|
|
3100
|
+
/**
|
|
3101
|
+
* Where launch pages go: the system's temporary directory on POSIX (the page's modes protect it),
|
|
3102
|
+
* the user's own profile on Windows, whatever `%TEMP%` names.
|
|
3103
|
+
*/
|
|
3104
|
+
function launchBase(env, platform = process.platform) {
|
|
3105
|
+
if (platform !== "win32") return tmpdir();
|
|
3106
|
+
const local = env["LOCALAPPDATA"];
|
|
3107
|
+
const base = local !== void 0 && local !== "" && win32.isAbsolute(local) ? local : win32.join(homedir(), "AppData", "Local");
|
|
3108
|
+
return win32.join(base, "mostra-cli", "open");
|
|
3109
|
+
}
|
|
3020
3110
|
/** Opens `url` in a browser through a launch page; whether a browser command started. */
|
|
3021
3111
|
async function openInBrowser(url, env) {
|
|
3022
3112
|
let page;
|
|
3023
3113
|
try {
|
|
3024
|
-
|
|
3114
|
+
const base = launchBase(env);
|
|
3115
|
+
mkdirSync(base, {
|
|
3116
|
+
recursive: true,
|
|
3117
|
+
mode: 448
|
|
3118
|
+
});
|
|
3119
|
+
page = writeLaunchPage(url, base);
|
|
3025
3120
|
} catch {
|
|
3026
3121
|
return false;
|
|
3027
3122
|
}
|
|
@@ -3036,10 +3131,13 @@ async function openInBrowser(url, env) {
|
|
|
3036
3131
|
* (`browser.ts`). A client of `view/default`, `view/url`,
|
|
3037
3132
|
* `view/rotate`, `view/revoke` and `view/launch`.
|
|
3038
3133
|
*/
|
|
3039
|
-
/**
|
|
3134
|
+
/**
|
|
3135
|
+
* Whether this session can show a browser: not over SSH without a display. On macOS and Windows a
|
|
3136
|
+
* local session always has the user's desktop; an SSH session there has none.
|
|
3137
|
+
*/
|
|
3040
3138
|
function hasDisplay(env, platform = process.platform) {
|
|
3041
3139
|
const ssh = env["SSH_CONNECTION"] !== void 0 || env["SSH_TTY"] !== void 0;
|
|
3042
|
-
if (platform === "darwin") return !ssh;
|
|
3140
|
+
if (platform === "darwin" || platform === "win32") return !ssh;
|
|
3043
3141
|
return env["DISPLAY"] !== void 0 || env["WAYLAND_DISPLAY"] !== void 0;
|
|
3044
3142
|
}
|
|
3045
3143
|
/** Whether `open` should launch a browser: a terminal without `--json`, or `--browser`. */
|
|
@@ -3398,6 +3496,19 @@ async function connect(options) {
|
|
|
3398
3496
|
}
|
|
3399
3497
|
return open(started.socket, options, started.status === "started");
|
|
3400
3498
|
}
|
|
3499
|
+
/**
|
|
3500
|
+
* Opens the endpoint `instance.json` or a starting daemon named: a Unix socket, or on Windows the
|
|
3501
|
+
* owner-only pipe through the runtime's helper, which refuses a pipe another user created under
|
|
3502
|
+
* the name (D150, the SDK's `connectControl`). Only Windows needs the runtime resolved first.
|
|
3503
|
+
*/
|
|
3504
|
+
async function openEndpoint(endpoint, options) {
|
|
3505
|
+
const runtime = process.platform === "win32" ? (await locateRuntime(options.env)).runtime : void 0;
|
|
3506
|
+
return connectControl(endpoint, {
|
|
3507
|
+
runtime,
|
|
3508
|
+
env: options.env,
|
|
3509
|
+
timeoutMs: ATTACH_TIMEOUT_MS
|
|
3510
|
+
});
|
|
3511
|
+
}
|
|
3401
3512
|
/** The socket `instance.json` records (D81), if the file exists and reads. */
|
|
3402
3513
|
function readSocket(home) {
|
|
3403
3514
|
try {
|
|
@@ -3417,7 +3528,7 @@ async function startDaemon(options) {
|
|
|
3417
3528
|
const runtime = await locateRuntime(options.env);
|
|
3418
3529
|
if (Date.now() >= deadlineAt) throw timedOut();
|
|
3419
3530
|
const args = [
|
|
3420
|
-
runtime.entry,
|
|
3531
|
+
resolve(runtime.entry),
|
|
3421
3532
|
"daemon",
|
|
3422
3533
|
"--home",
|
|
3423
3534
|
options.home
|
|
@@ -3425,15 +3536,17 @@ async function startDaemon(options) {
|
|
|
3425
3536
|
return new Promise((resolve, reject) => {
|
|
3426
3537
|
const child = spawn(runtime.node, args, {
|
|
3427
3538
|
detached: true,
|
|
3539
|
+
cwd: detachedDirectory(options.home),
|
|
3540
|
+
windowsHide: true,
|
|
3428
3541
|
stdio: [
|
|
3429
3542
|
"ignore",
|
|
3430
3543
|
"pipe",
|
|
3431
3544
|
"ignore"
|
|
3432
3545
|
],
|
|
3433
|
-
env: {
|
|
3546
|
+
env: detachedEnvironment({
|
|
3434
3547
|
...process.env,
|
|
3435
3548
|
...options.env
|
|
3436
|
-
}
|
|
3549
|
+
})
|
|
3437
3550
|
});
|
|
3438
3551
|
const splitter = new LineSplitter();
|
|
3439
3552
|
let settled = false;
|
|
@@ -3468,21 +3581,7 @@ async function startDaemon(options) {
|
|
|
3468
3581
|
});
|
|
3469
3582
|
}
|
|
3470
3583
|
async function open(socketPath, options, started) {
|
|
3471
|
-
const socket = await
|
|
3472
|
-
const connection = createConnection(socketPath);
|
|
3473
|
-
const timer = setTimeout(() => {
|
|
3474
|
-
connection.destroy();
|
|
3475
|
-
reject(closed(socketPath, "timed out"));
|
|
3476
|
-
}, ATTACH_TIMEOUT_MS);
|
|
3477
|
-
connection.once("error", (error) => {
|
|
3478
|
-
clearTimeout(timer);
|
|
3479
|
-
reject(closed(socketPath, error.code ?? "failed"));
|
|
3480
|
-
});
|
|
3481
|
-
connection.once("connect", () => {
|
|
3482
|
-
clearTimeout(timer);
|
|
3483
|
-
resolve(connection);
|
|
3484
|
-
});
|
|
3485
|
-
});
|
|
3584
|
+
const socket = await openEndpoint(socketPath, options);
|
|
3486
3585
|
const handlers = /* @__PURE__ */ new Map();
|
|
3487
3586
|
const peer = new RpcPeer({
|
|
3488
3587
|
send: (message) => {
|
|
@@ -3543,9 +3642,6 @@ async function open(socketPath, options, started) {
|
|
|
3543
3642
|
}
|
|
3544
3643
|
};
|
|
3545
3644
|
}
|
|
3546
|
-
function closed(path, why) {
|
|
3547
|
-
return new RpcError(createDiagnostic("MOSTRA_HOST_CONNECTION_CLOSED", { message: `Couldn't reach the instance at ${path} (${why}).` }));
|
|
3548
|
-
}
|
|
3549
3645
|
/** A limit as people write it: `250 ms`, `5 s`, `1.5 s`. */
|
|
3550
3646
|
function formatDuration(ms) {
|
|
3551
3647
|
if (ms < 1e3) return `${ms} ms`;
|
|
@@ -3630,7 +3726,8 @@ async function run(io) {
|
|
|
3630
3726
|
return exitCodeFor(diagnostic.code);
|
|
3631
3727
|
};
|
|
3632
3728
|
const platform = io.platform ?? process.platform;
|
|
3633
|
-
|
|
3729
|
+
const unsupported = checkPlatform(platform, io.arch ?? process.arch, io.env);
|
|
3730
|
+
if (unsupported) return fail(unsupported);
|
|
3634
3731
|
let parsed;
|
|
3635
3732
|
try {
|
|
3636
3733
|
parsed = parseCommandLine(COMMANDS, io.argv);
|
|
@@ -3821,4 +3918,4 @@ async function runRuntime(io, args, stdio) {
|
|
|
3821
3918
|
//#endregion
|
|
3822
3919
|
export { COMMANDS as a, referenceMarkdown as i, commandDefinition as n, commandHelp as r, run as t };
|
|
3823
3920
|
|
|
3824
|
-
//# sourceMappingURL=run-
|
|
3921
|
+
//# sourceMappingURL=run-BpVu5uQ8.js.map
|