@mostra/cli 0.4.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-C2lroV03.js → flush-CFJkvi2R.js} +38 -6
- package/dist/flush-CFJkvi2R.js.map +1 -0
- package/dist/index.js +1 -1
- package/dist/{run-DP-RoOv_.js → run-BpVu5uQ8.js} +17 -4
- 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/commands/admin.ts +12 -1
- package/src/commands/authoring.ts +3 -1
- package/src/commands/interaction.ts +11 -0
- package/src/telemetry/store.ts +57 -8
- package/dist/flush-C2lroV03.js.map +0 -1
- package/dist/run-DP-RoOv_.js.map +0 -1
- package/dist/run-DdLe9hN3.js +0 -2
package/dist/bin.js
CHANGED
|
@@ -54,7 +54,7 @@ async function start() {
|
|
|
54
54
|
},
|
|
55
55
|
stderrTty: process.stderr.isTTY === true,
|
|
56
56
|
terminal: process.stdin.isTTY === true || process.stdout.isTTY === true || process.stderr.isTTY === true
|
|
57
|
-
}) ?? await (await import("./run-
|
|
57
|
+
}) ?? await (await import("./run-CC8PNXLU.js")).run({
|
|
58
58
|
argv: process.argv.slice(2),
|
|
59
59
|
env: process.env,
|
|
60
60
|
cwd: process.cwd(),
|
|
@@ -543,13 +543,13 @@ var TelemetryStore = class {
|
|
|
543
543
|
}
|
|
544
544
|
const temporary = `${this.queueFile}.${process.pid}.tmp`;
|
|
545
545
|
writeFileSync(temporary, `${lines.join("\n")}\n`, { mode: 384 });
|
|
546
|
-
|
|
546
|
+
replace(temporary, this.queueFile, this.#platform);
|
|
547
547
|
}
|
|
548
548
|
#write(settings) {
|
|
549
549
|
const { installId: _id, ...stored } = settings;
|
|
550
550
|
const temporary = `${this.settingsFile}.${process.pid}.tmp`;
|
|
551
551
|
writeFileSync(temporary, `${JSON.stringify(stored, null, 2)}\n`, { mode: 384 });
|
|
552
|
-
|
|
552
|
+
replace(temporary, this.settingsFile, this.#platform);
|
|
553
553
|
}
|
|
554
554
|
#prepareDir() {
|
|
555
555
|
mkdirSync(this.dir, {
|
|
@@ -590,9 +590,8 @@ var TelemetryStore = class {
|
|
|
590
590
|
const next = this.#breakStale();
|
|
591
591
|
if (next === "absent") {
|
|
592
592
|
if (code === "EEXIST") continue;
|
|
593
|
-
return;
|
|
594
|
-
}
|
|
595
|
-
if (next === "broken") continue;
|
|
593
|
+
if (!writable(this.dir)) return void 0;
|
|
594
|
+
} else if (next === "broken") continue;
|
|
596
595
|
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 20);
|
|
597
596
|
}
|
|
598
597
|
try {
|
|
@@ -646,6 +645,39 @@ var TelemetryStore = class {
|
|
|
646
645
|
}
|
|
647
646
|
};
|
|
648
647
|
/**
|
|
648
|
+
* Renames `from` over `to`. Windows refuses to replace a file another process has open, as a
|
|
649
|
+
* process reading the settings outside the lock does, with `EPERM` (MT-01: CI's install-ID race,
|
|
650
|
+
* traced on `windows-latest`); the rename is tried again for up to a second, as the runtime's
|
|
651
|
+
* renames are (D150 item 8), and never turned into a copy.
|
|
652
|
+
*/
|
|
653
|
+
function replace(from, to, platform, rename = renameSync) {
|
|
654
|
+
const deadline = Date.now() + 1e3;
|
|
655
|
+
for (let pause = 5;; pause = Math.min(pause * 2, 100)) try {
|
|
656
|
+
rename(from, to);
|
|
657
|
+
return;
|
|
658
|
+
} catch (error) {
|
|
659
|
+
const code = error.code;
|
|
660
|
+
if (platform !== "win32" || !(code === "EPERM" || code === "EACCES" || code === "EBUSY") || Date.now() > deadline) throw error;
|
|
661
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, pause);
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
/** Whether this process can create a file in `dir`: a probe of its own, removed at once. */
|
|
665
|
+
function writable(dir, remove = (path) => rmSync(path, { force: true })) {
|
|
666
|
+
const probe = join(dir, `.probe-${process.pid}-${randomUUID()}`);
|
|
667
|
+
try {
|
|
668
|
+
writeFileSync(probe, "", {
|
|
669
|
+
flag: "wx",
|
|
670
|
+
mode: 384
|
|
671
|
+
});
|
|
672
|
+
} catch {
|
|
673
|
+
return false;
|
|
674
|
+
}
|
|
675
|
+
try {
|
|
676
|
+
remove(probe);
|
|
677
|
+
} catch {}
|
|
678
|
+
return true;
|
|
679
|
+
}
|
|
680
|
+
/**
|
|
649
681
|
* Whether a lock's exclusive create failed because another process holds it. On Windows a lock
|
|
650
682
|
* its owner just removed stays "delete pending" while anyone still has it open (another process
|
|
651
683
|
* judging its age), and creating it again meanwhile is refused as access denied, not `EEXIST`
|
|
@@ -801,4 +833,4 @@ function startFlusher(store, env, now) {
|
|
|
801
833
|
//#endregion
|
|
802
834
|
export { detachedEnvironment as a, durationBucket as c, buildEndpoint as d, decide as f, packageRoot as g, fromSource as h, detachedDirectory as i, minute as l, cliVersion as m, startFlusher as n, TelemetryEvent as o, truthy as p, TelemetryStore as r, catalogCodes as s, flush as t, detectHarness as u };
|
|
803
835
|
|
|
804
|
-
//# sourceMappingURL=flush-
|
|
836
|
+
//# sourceMappingURL=flush-CFJkvi2R.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flush-CFJkvi2R.js","names":["httpsRequest","httpRequest"],"sources":["../src/version.ts","../src/telemetry/decision.ts","../src/telemetry/harness.ts","../src/telemetry/events.ts","../src/detached.ts","../src/telemetry/store.ts","../src/telemetry/flush.ts"],"sourcesContent":["/** The CLI's own package: its root directory and its release. */\nimport { readFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nlet found: { readonly root: string; readonly version: string } | undefined;\n\nfunction own(): { readonly root: string; readonly version: string } {\n if (found) return found;\n let dir = dirname(fileURLToPath(import.meta.url));\n for (;;) {\n try {\n const manifest = JSON.parse(readFileSync(join(dir, \"package.json\"), \"utf8\")) as {\n name?: string;\n version?: string;\n };\n if (manifest.name === \"@mostra/cli\" && manifest.version) {\n found = { root: dir, version: manifest.version };\n return found;\n }\n } catch {\n // Keep looking upwards.\n }\n const parent = dirname(dir);\n if (parent === dir) throw new Error(\"Couldn't find @mostra/cli's package.json.\");\n dir = parent;\n }\n}\n\n/** This CLI's version (`mostra --version`, the client version it reports, telemetry's `version`). */\nexport function cliVersion(): string {\n return own().version;\n}\n\n/** The CLI package's directory: its `dist/`, `src/` and bundled reference live under it. */\nexport function packageRoot(): string {\n return own().root;\n}\n\n/** Whether this CLI runs from its TypeScript sources (tests), not its build. */\nexport function fromSource(): boolean {\n return import.meta.url.endsWith(\".ts\");\n}\n","/**\n * Whether telemetry sends, and why (D140 item 3). The first match decides, so an explicit \"off\"\n * anywhere wins over any \"on\", and withdrawn consent sticks.\n */\nimport type { Environment } from \"../home.ts\";\nimport type { ReadSettings } from \"./store.ts\";\nimport type { TelemetryReason } from \"./words.ts\";\n\n/** The endpoint this build sends to, set only by the release workflow at bundle time (D140 item 7). */\ndeclare const __MOSTRA_TELEMETRY_ENDPOINT__: string | undefined;\n\n/** The build's endpoint: empty in this package and in every development build. */\nexport function buildEndpoint(): string {\n return typeof __MOSTRA_TELEMETRY_ENDPOINT__ === \"string\" ? __MOSTRA_TELEMETRY_ENDPOINT__ : \"\";\n}\n\n/** What the decision reads. */\nexport interface DecisionInputs {\n readonly env: Environment;\n /** The build's endpoint ({@link buildEndpoint}); tests pass their own. */\n readonly buildEndpoint: string;\n /** Whether this use is embedded: a host-spawned instance, or a home a host configures. */\n readonly embedded: boolean;\n readonly settings: ReadSettings;\n /** Whether this machine's time zone asks first. */\n readonly askFirst: boolean;\n}\n\n/** The decision. */\nexport interface TelemetryDecision {\n readonly enabled: boolean;\n readonly reason: TelemetryReason;\n readonly askFirst: boolean;\n /** The endpoint events go to, when there is one. */\n readonly endpoint?: string;\n /** For an enabled decision: whether the person said yes, or it is the default. */\n readonly consent?: \"explicit\" | \"default\";\n}\n\nconst OFF_VALUES = new Set([\"0\", \"false\", \"off\", \"no\"]);\nconst ON_VALUES = new Set([\"1\", \"true\", \"on\", \"yes\"]);\n\n/** `DO_NOT_TRACK` and `MOSTRA_OFFLINE`'s parser: set to anything but empty, `0` or `false`. */\nexport function truthy(value: string | undefined): boolean {\n if (value === undefined) return false;\n const normalized = value.trim().toLowerCase();\n return normalized !== \"\" && normalized !== \"0\" && normalized !== \"false\";\n}\n\n/**\n * The endpoint in use: `MOSTRA_TELEMETRY_ENDPOINT` when it is `https:` or `http:` to a loopback\n * address, else the build's; `undefined` when there is none (D140 item 7).\n */\nexport function endpointOf(env: Environment, build: string): string | undefined {\n const override = env[\"MOSTRA_TELEMETRY_ENDPOINT\"];\n const chosen = override !== undefined && override !== \"\" ? override : build;\n if (chosen === \"\") return undefined;\n let url: URL;\n try {\n url = new URL(chosen);\n } catch {\n return undefined;\n }\n if (url.username !== \"\" || url.password !== \"\") return undefined;\n if (url.protocol === \"https:\") return url.href;\n const loopback =\n url.hostname === \"127.0.0.1\" || url.hostname === \"[::1]\" || url.hostname === \"localhost\";\n return url.protocol === \"http:\" && loopback ? url.href : undefined;\n}\n\n/** Decides, in D140's order. */\nexport function decide(inputs: DecisionInputs): TelemetryDecision {\n const { askFirst } = inputs;\n const off = (reason: TelemetryReason): TelemetryDecision => ({\n enabled: false,\n reason,\n askFirst,\n });\n const endpoint = endpointOf(inputs.env, inputs.buildEndpoint);\n if (endpoint === undefined) return off(\"no-collector\");\n const at = { askFirst, endpoint };\n if (inputs.embedded) return { ...at, enabled: false, reason: \"embedded\" };\n if (truthy(inputs.env[\"DO_NOT_TRACK\"])) return { ...at, enabled: false, reason: \"do-not-track\" };\n const variable = inputs.env[\"MOSTRA_TELEMETRY\"];\n const normalized = variable?.trim().toLowerCase();\n if (normalized !== undefined && OFF_VALUES.has(normalized)) {\n return { ...at, enabled: false, reason: \"variable\" };\n }\n if (!inputs.settings.ok) return { ...at, enabled: false, reason: \"unreadable\" };\n const choice = inputs.settings.settings.choice;\n if (choice === \"off\") return { ...at, enabled: false, reason: \"setting\" };\n if (normalized !== undefined && normalized !== \"\") {\n return ON_VALUES.has(normalized)\n ? { ...at, enabled: true, reason: \"variable\", consent: \"explicit\" }\n : { ...at, enabled: false, reason: \"variable-invalid\" };\n }\n if (choice === \"on\") return { ...at, enabled: true, reason: \"setting\", consent: \"explicit\" };\n return askFirst\n ? { ...at, enabled: false, reason: \"default\" }\n : { ...at, enabled: true, reason: \"default\", consent: \"default\" };\n}\n","/**\n * Which agent ran the CLI (D140 item 9), from environment markers each agent documents or sets in\n * its source (VERIFIED_FACTS, WP-10). Only the agent's ID is ever sent, never a marker's value.\n */\nimport type { Environment } from \"../home.ts\";\n\n/** The agents Mostra recognizes, in the order their markers are checked. */\nexport const HARNESSES = [\"claude-code\", \"codex\", \"gemini-cli\", \"opencode\", \"cursor\"] as const;\n\n/** A recognized agent, `other` for an agent Mostra doesn't know, `none` for no agent at all. */\nexport type Harness = (typeof HARNESSES)[number] | \"other\" | \"none\";\n\n/** Each agent's own marker. */\nconst MARKERS: Readonly<Record<(typeof HARNESSES)[number], string>> = {\n \"claude-code\": \"CLAUDECODE\",\n codex: \"CODEX_THREAD_ID\",\n \"gemini-cli\": \"GEMINI_CLI\",\n opencode: \"OPENCODE\",\n cursor: \"CURSOR_AGENT\",\n};\n\nconst set = (value: string | undefined): boolean => value !== undefined && value !== \"\";\n\n/**\n * The agent that ran this process: `AI_AGENT` when it starts with a known ID (the convention\n * `@vercel/detect-agent` reads), else the first agent whose own marker is set, else `other` when\n * `AI_AGENT` or `AGENT` names something else, else `none`. Cursor's editor terminal sets\n * `CURSOR_TRACE_ID` without `CURSOR_AGENT`: that is a person, so it isn't `cursor`.\n */\nexport function detectHarness(env: Environment): Harness {\n const declared = env[\"AI_AGENT\"];\n if (set(declared)) {\n const known = HARNESSES.find((id) => declared?.startsWith(id));\n if (known) return known;\n }\n for (const id of HARNESSES) if (set(env[MARKERS[id]])) return id;\n if (set(declared) || set(env[\"AGENT\"])) return \"other\";\n return \"none\";\n}\n","/**\n * Telemetry's event schema 1 (D140 item 8), the one published in `docs/reference/telemetry.md`.\n * Every event is checked against it before it is queued and again before it is sent, so nothing\n * that isn't on that page can leave the machine: the objects are strict, every string is from a\n * closed set or a pattern, and no field takes free text.\n */\nimport { DIAGNOSTIC_AREAS } from \"@mostra/protocol\";\nimport * as z from \"zod\";\nimport { HARNESSES, type Harness } from \"./harness.ts\";\n\n/** The schema version every event carries. */\nexport const SCHEMA = 1;\n\n/** The duration buckets of `cli.command`. */\nexport const DURATIONS = [\"<1s\", \"1-5s\", \"5-30s\", \"30s-2m\", \"2-10m\", \">10m\"] as const;\n\n/** A command's duration bucket. */\nexport function durationBucket(ms: number): (typeof DURATIONS)[number] {\n if (ms < 1000) return \"<1s\";\n if (ms < 5000) return \"1-5s\";\n if (ms < 30_000) return \"5-30s\";\n if (ms < 120_000) return \"30s-2m\";\n if (ms < 600_000) return \"2-10m\";\n return \">10m\";\n}\n\n/** A catalog-shaped diagnostic code: `MOSTRA_<AREA>_<NAME>`, never a message. */\nconst code = z\n .string()\n .regex(new RegExp(`^MOSTRA_(${DIAGNOSTIC_AREAS.join(\"|\")})_[A-Z0-9_]{1,64}$`));\n\n/** A command path from the definitions: lowercase words. */\nconst command = z.string().regex(/^[a-z]+( [a-z]+)?$/);\n\nconst common = {\n schema: z.literal(SCHEMA),\n at: z.string().regex(/^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:00Z$/),\n installId: z.uuid(),\n version: z.string().regex(/^\\d+\\.\\d+\\.\\d+(-[0-9A-Za-z.-]+)?$/),\n os: z.enum([\"darwin\", \"linux\", \"win32\"]),\n arch: z.string().regex(/^[a-z0-9]{2,12}$/),\n node: z.int().min(0).max(999),\n harness: z.enum([...HARNESSES, \"other\", \"none\"]),\n ci: z.boolean(),\n consent: z.enum([\"explicit\", \"default\"]),\n};\n\n/** The fields every event carries. */\nexport interface CommonFields {\n readonly schema: typeof SCHEMA;\n readonly at: string;\n readonly installId: string;\n readonly version: string;\n readonly os: \"darwin\" | \"linux\" | \"win32\";\n readonly arch: string;\n readonly node: number;\n readonly harness: Harness;\n readonly ci: boolean;\n readonly consent: \"explicit\" | \"default\";\n}\n\n/** One event, as sent (D140 item 8). */\nexport type TelemetryEvent = CommonFields &\n (\n | { readonly event: \"cli.first_run\" }\n | {\n readonly event: \"cli.command\";\n readonly command: string;\n readonly exit: number;\n readonly duration: (typeof DURATIONS)[number];\n readonly json: boolean;\n readonly error?: string | undefined;\n }\n | {\n readonly event: \"verify.result\";\n readonly status: \"pass\" | \"fail\";\n readonly codes: readonly string[];\n readonly codesTruncated: boolean;\n }\n | { readonly event: \"publish.done\"; readonly reused: boolean; readonly forced: boolean }\n | {\n readonly event: \"share.done\";\n readonly kind: \"tailnet\" | \"funnel\" | \"cloudflare\" | \"remote\";\n }\n | {\n readonly event: \"mcp.session\";\n readonly client: (typeof HARNESSES)[number] | \"other\";\n readonly clientVersion?: string | undefined;\n }\n );\n\n/** Every event Mostra may send, as a strict union on `event`. */\nexport const TelemetryEvent: z.ZodType<TelemetryEvent> = z.discriminatedUnion(\"event\", [\n z.strictObject({ ...common, event: z.literal(\"cli.first_run\") }),\n z.strictObject({\n ...common,\n event: z.literal(\"cli.command\"),\n command,\n exit: z.int().min(0).max(255),\n duration: z.enum(DURATIONS),\n json: z.boolean(),\n error: code.optional(),\n }),\n z.strictObject({\n ...common,\n event: z.literal(\"verify.result\"),\n status: z.enum([\"pass\", \"fail\"]),\n codes: z.array(code).max(50),\n codesTruncated: z.boolean(),\n }),\n z.strictObject({\n ...common,\n event: z.literal(\"publish.done\"),\n reused: z.boolean(),\n forced: z.boolean(),\n }),\n // Emitted by WP-13 and WP-29.\n z.strictObject({\n ...common,\n event: z.literal(\"share.done\"),\n kind: z.enum([\"tailnet\", \"funnel\", \"cloudflare\", \"remote\"]),\n }),\n // Emitted by WP-15: a known agent's ID for the MCP client, or `other`; its version only then.\n z.strictObject({\n ...common,\n event: z.literal(\"mcp.session\"),\n client: z.enum([...HARNESSES, \"other\"]),\n clientVersion: z\n .string()\n .regex(/^[0-9A-Za-z.+-]{1,32}$/)\n .optional(),\n }),\n]);\n\n/** `at`: now in UTC, rounded down to the minute. */\nexport function minute(now: Date): string {\n return `${now.toISOString().slice(0, 16)}:00Z`;\n}\n\n/** The distinct catalog codes of `codes`, sorted, at most 50, and whether some were left out. */\nexport function catalogCodes(\n codes: readonly string[],\n known: ReadonlySet<string>,\n): { codes: string[]; codesTruncated: boolean } {\n const distinct = [...new Set(codes.filter((value) => known.has(value)))].sort();\n return { codes: distinct.slice(0, 50), codesTruncated: distinct.length > 50 };\n}\n","/**\n * The directory and environment of the processes the CLI leaves running: its home's daemon and\n * the telemetry flusher. They run in their file system's root, because on Windows nobody can\n * rename or remove a running process's working directory, and they outlive the command (WP-W2,\n * D151 item 3). Every path the environment names relative to the command's directory is made\n * absolute first, so it still names what it named for the command (Opus review of WP-W2,\n * finding 3).\n */\nimport { isAbsolute, parse, resolve } from \"node:path\";\n\n/**\n * The variables whose values are paths: Mostra's own, as the runtime, the SDK and the CLI read\n * them. A test keeps the list complete.\n */\nexport const PATH_VARIABLES: readonly string[] = [\n \"MOSTRA_HOME\",\n \"MOSTRA_RUNTIME_DIR\",\n \"MOSTRA_CONFIG_DIR\",\n \"MOSTRA_CACHE_DIR\",\n \"MOSTRA_BUNDLE\",\n \"MOSTRA_RUNTIME_BIN\",\n \"MOSTRA_SHELL_DIR\",\n \"MOSTRA_WARM_DIR\",\n \"MOSTRA_WORKER_ENTRY\",\n \"MOSTRA_VERIFY_ENTRY\",\n \"MOSTRA_WIN_HELPER\",\n \"MOSTRA_TEST_WORKER_HOST\",\n \"MOSTRA_TEST_BUNDLE_PINS\",\n];\n\n/** `env` with every relative path variable resolved against `cwd`. */\nexport function detachedEnvironment(\n env: Readonly<Record<string, string | undefined>>,\n cwd: string = process.cwd(),\n): Record<string, string | undefined> {\n const result: Record<string, string | undefined> = { ...env };\n for (const name of PATH_VARIABLES) {\n const value = result[name];\n if (value !== undefined && value !== \"\" && !isAbsolute(value)) {\n result[name] = resolve(cwd, value);\n }\n }\n return result;\n}\n\n/** The working directory of a process the CLI leaves running: the root of `anchor`'s file system. */\nexport function detachedDirectory(anchor: string): string {\n return parse(resolve(anchor)).root;\n}\n","/**\n * Telemetry's files in the CLI's config directory (D140 item 5): `telemetry.json` (the choice,\n * when the notice was shown, when a flush last started), `telemetry-id` (the random install ID)\n * and `telemetry-queue.ndjson` (events waiting to be sent). The directory is 0700 and the files\n * 0600; `telemetry.json` and the queue are written by rename. The install ID is made by an\n * exclusive create (a hard link that fails when the file exists), so processes on a first run\n * agree on one ID whatever happens to the lock. Every other change goes through one lock file,\n * owned by a token and taken over only when its holder has died, so a flusher never loses an\n * event appended while it sends.\n */\nimport { randomUUID } from \"node:crypto\";\nimport {\n chmodSync,\n closeSync,\n fstatSync,\n linkSync,\n mkdirSync,\n openSync,\n readFileSync,\n renameSync,\n rmSync,\n statSync,\n writeFileSync,\n} from \"node:fs\";\nimport { dirname, join } from \"node:path\";\n\n/** What `telemetry.json` holds. */\nexport interface TelemetrySettings {\n /** The person's choice: `mostra telemetry on|off`, or their answer to the question. */\n readonly choice?: \"on\" | \"off\";\n /**\n * The random install ID, made on the first run that sends; deleted when telemetry turns off.\n * Read from `telemetry-id`, never written into `telemetry.json`.\n */\n readonly installId?: string;\n /**\n * When the notice of what is sent was shown where sending follows: the default notice, the\n * question, or `mostra telemetry on`'s. A `default`-consent event needs it (Opus review\n * finding 3).\n */\n readonly noticeShownAt?: string;\n /**\n * When the ask-first notice (\"Nothing is sent unless you run: mostra telemetry on\") was\n * shown. It never stands for the default notice: a later run where telemetry is on by default\n * still shows that one before anything is sent.\n */\n readonly askFirstNoticeShownAt?: string;\n /** When a flush last started. */\n readonly lastFlushAt?: string;\n}\n\n/** The settings, or why they can't be read (which means off, D140 item 5). */\nexport type ReadSettings =\n | { readonly ok: true; readonly settings: TelemetrySettings }\n | { readonly ok: false; readonly file: string };\n\n/** A lock older than this whose owner no longer runs is taken over as stale. */\nconst STALE_LOCK_MS = 10_000;\n/**\n * A lock older than this is taken over whoever it names: a held section takes milliseconds, and\n * a PID can name an unrelated live process once the owner died (in a container, PID 1 always\n * runs), which would otherwise hold every later command up forever (Opus review finding 5).\n */\nconst DEAD_LOCK_MS = 60_000;\n/** The queue's bounds (D140 item 6). */\nexport const QUEUE_MAX_EVENTS = 1000;\nexport const QUEUE_MAX_AGE_MS: number = 7 * 24 * 60 * 60 * 1000;\n\nconst UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;\n\n/** Telemetry's files in one config directory. */\nexport class TelemetryStore {\n readonly dir: string;\n readonly settingsFile: string;\n readonly queueFile: string;\n readonly idFile: string;\n readonly #lockFile: string;\n readonly #platform: string;\n\n /** `platform` is the system whose lock rules apply: this process's, but for tests. */\n constructor(dir: string, platform: string = process.platform) {\n this.dir = dir;\n this.#platform = platform;\n this.settingsFile = join(dir, \"telemetry.json\");\n this.queueFile = join(dir, \"telemetry-queue.ndjson\");\n this.idFile = join(dir, \"telemetry-id\");\n this.#lockFile = join(dir, \"telemetry.lock\");\n }\n\n /** The settings; a file that isn't telemetry settings is unreadable, never guessed at. */\n read(): ReadSettings {\n // A directory that can never exist (a part of its path is a file) is unreadable, never\n // \"nothing stored yet\". POSIX says so for every read (ENOTDIR); Windows answers ENOENT, which\n // would read as the default, on, though nothing could ever be recorded (WP-W2).\n if (blocked(this.dir)) return { ok: false, file: this.dir };\n const id = this.#readId();\n if (id === null) return { ok: false, file: this.idFile };\n let text: string;\n try {\n text = readFileSync(this.settingsFile, \"utf8\");\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\")\n return { ok: true, settings: id !== undefined ? { installId: id } : {} };\n return { ok: false, file: this.settingsFile };\n }\n try {\n const data = JSON.parse(text) as Record<string, unknown>;\n if (typeof data !== \"object\" || data === null || Array.isArray(data)) throw new Error();\n const { choice, noticeShownAt, askFirstNoticeShownAt, lastFlushAt } = data;\n const installId = id;\n if (choice !== undefined && choice !== \"on\" && choice !== \"off\") throw new Error();\n // An install ID belongs in `telemetry-id`; one here is a file Mostra didn't write.\n if (data[\"installId\"] !== undefined) throw new Error();\n if (noticeShownAt !== undefined && typeof noticeShownAt !== \"string\") throw new Error();\n if (askFirstNoticeShownAt !== undefined && typeof askFirstNoticeShownAt !== \"string\")\n throw new Error();\n if (lastFlushAt !== undefined && typeof lastFlushAt !== \"string\") throw new Error();\n return {\n ok: true,\n settings: {\n ...(choice !== undefined ? { choice } : {}),\n ...(installId !== undefined ? { installId } : {}),\n ...(noticeShownAt !== undefined ? { noticeShownAt } : {}),\n ...(askFirstNoticeShownAt !== undefined ? { askFirstNoticeShownAt } : {}),\n ...(lastFlushAt !== undefined ? { lastFlushAt } : {}),\n },\n };\n } catch {\n return { ok: false, file: this.settingsFile };\n }\n }\n\n /**\n * Changes the settings under the lock and returns what they became; `undefined` when the file\n * can't be read (nothing is written over it).\n */\n update(change: (current: TelemetrySettings) => TelemetrySettings): TelemetrySettings | undefined {\n return this.locked(() => {\n const current = this.read();\n if (!current.ok) return undefined;\n const next = change(current.settings);\n this.#write(next);\n return next;\n });\n }\n\n /**\n * The install ID, made when there is none yet. A new ID is written to a file of this process's\n * own, then hard-linked to `telemetry-id`, which fails when the file exists: of any processes\n * making one at once exactly one wins, and the others read the winner's. `undefined` when the\n * settings can't be read.\n */\n installId(): { readonly id: string; readonly created: boolean } | undefined {\n const current = this.read();\n if (!current.ok) return undefined;\n if (current.settings.installId !== undefined)\n return { id: current.settings.installId, created: false };\n const candidate = `${this.idFile}.${process.pid}.${randomUUID()}.tmp`;\n const id = randomUUID();\n try {\n this.#prepareDir();\n writeFileSync(candidate, `${id}\\n`, { mode: 0o600 });\n linkSync(candidate, this.idFile);\n return { id, created: true };\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== \"EEXIST\") return undefined;\n const winner = this.#readId();\n return winner ? { id: winner, created: false } : undefined;\n } finally {\n rmSync(candidate, { force: true });\n }\n }\n\n /** The install ID in `telemetry-id`: `undefined` when there is none, `null` when it's damaged. */\n #readId(): string | undefined | null {\n let text: string;\n try {\n text = readFileSync(this.idFile, \"utf8\");\n } catch (error) {\n return (error as NodeJS.ErrnoException).code === \"ENOENT\" ? undefined : null;\n }\n const id = text.trim();\n return UUID.test(id) ? id : null;\n }\n\n /**\n * Replaces a file that can't be read with these settings; only `telemetry on|off` do, as the\n * person's explicit choice. Call it holding the lock.\n */\n reset(settings: TelemetrySettings): TelemetrySettings {\n const { installId: _id, ...rest } = settings;\n this.#write(rest);\n // A damaged install ID goes too; `on` makes a new one on its first send.\n rmSync(this.idFile, { force: true });\n if (settings.choice === \"off\") rmSync(this.queueFile, { force: true });\n return rest;\n }\n\n /** Turns telemetry off: the choice, and the install ID and the queue deleted at once. */\n turnOff(): boolean {\n return (\n this.locked(() => {\n const current = this.read();\n if (!current.ok) return false;\n const { installId: _id, ...rest } = current.settings;\n this.#write({ ...rest, choice: \"off\" });\n rmSync(this.idFile, { force: true });\n rmSync(this.queueFile, { force: true });\n return true;\n }) ?? false\n );\n }\n\n /** Appends events to the queue, keeping its bounds. */\n append(lines: readonly string[], now: number): void {\n if (lines.length === 0) return;\n this.locked(() => {\n const kept = bounded([...this.#queued(), ...lines], now);\n this.#writeQueue(kept);\n });\n }\n\n /** The queued events, within their bounds. */\n queued(now: number): string[] {\n return bounded(this.#queued(), now);\n }\n\n /**\n * Removes the lines a flush sent from the head of the queue, after pruning what expired or\n * fell outside the bounds, as {@link queued} did; how many sent lines were removed (0 when the\n * lock can't be had). A flusher stops when this is 0, so it never sends a batch twice.\n */\n drop(sent: readonly string[], now: number): number {\n return (\n this.locked(() => {\n const queued = bounded(this.#queued(), now);\n let index = 0;\n while (index < sent.length && queued[index] === sent[index]) index += 1;\n this.#writeQueue(queued.slice(index));\n return index;\n }) ?? 0\n );\n }\n\n #queued(): string[] {\n try {\n return readFileSync(this.queueFile, \"utf8\")\n .split(\"\\n\")\n .filter((line) => line.length > 0);\n } catch {\n return [];\n }\n }\n\n #writeQueue(lines: readonly string[]): void {\n if (lines.length === 0) {\n rmSync(this.queueFile, { force: true });\n return;\n }\n const temporary = `${this.queueFile}.${process.pid}.tmp`;\n writeFileSync(temporary, `${lines.join(\"\\n\")}\\n`, { mode: 0o600 });\n replace(temporary, this.queueFile, this.#platform);\n }\n\n #write(settings: TelemetrySettings): void {\n const { installId: _id, ...stored } = settings;\n const temporary = `${this.settingsFile}.${process.pid}.tmp`;\n writeFileSync(temporary, `${JSON.stringify(stored, null, 2)}\\n`, { mode: 0o600 });\n replace(temporary, this.settingsFile, this.#platform);\n }\n\n #prepareDir(): void {\n mkdirSync(this.dir, { recursive: true, mode: 0o700 });\n // Windows has no modes: the directory keeps the access list `%APPDATA%` hands down.\n if (this.#platform === \"win32\") return;\n try {\n chmodSync(this.dir, 0o700);\n } catch {\n // A directory someone else owns: the files below fail or succeed on their own terms.\n }\n }\n\n /**\n * Runs `fn` holding the lock file; `undefined` when the lock can't be had within 2 s, or the\n * directory or a file in it can't be written (a full disk, a directory someone else owns):\n * telemetry never stops a command (D140 item 5). The lock\n * holds its owner's PID and a token of its own, is released only by its owner, and is taken\n * over when it is older than 10 s and its owner is no longer running (a crash), or older than\n * 60 s whoever it names.\n */\n locked<T>(fn: () => T): T | undefined {\n try {\n this.#prepareDir();\n } catch {\n return undefined;\n }\n const token = `${process.pid} ${randomUUID()}`;\n const deadline = Date.now() + 2000;\n for (;;) {\n try {\n writeFileSync(this.#lockFile, token, { flag: \"wx\", mode: 0o600 });\n break;\n } catch (error) {\n const code = (error as NodeJS.ErrnoException).code;\n if (!held(code, this.#platform)) return undefined;\n // Every path back to the create checks the deadline, so nothing can hold the CLI here\n // (GPT review of WP-W2), and only a lock that just went is tried again without a pause,\n // so nothing spins either (Opus review of WP-W2, finding 2).\n if (Date.now() > deadline) return undefined;\n const next = this.#breakStale();\n if (next === \"absent\") {\n // No lock there, though the create was refused. After `EEXIST` it was removed in\n // between: try again. After access denied, either a lock Windows was deleting went in\n // between (twelve processes at once keep that happening), or this user can't write the\n // directory and waiting changes nothing (Opus review of WP-W2, finding 2). A file of\n // this process's own tells which, whatever the timing (MT-01: a count of such rounds\n // in a row still gave up under contention).\n if (code === \"EEXIST\") continue;\n if (!writable(this.dir)) return undefined;\n } else if (next === \"broken\") continue;\n Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 20);\n }\n }\n try {\n return fn();\n } catch {\n return undefined;\n } finally {\n this.#release(token);\n }\n }\n\n /** Removes the lock when it is still this owner's. */\n #release(token: string): void {\n try {\n if (readFileSync(this.#lockFile, \"utf8\") === token) rmSync(this.#lockFile, { force: true });\n } catch {\n // Gone already.\n }\n }\n\n /**\n * Takes a crashed owner's lock away. The lock is moved aside under a name of this process's own\n * (a rename is atomic, so of two processes breaking it one gets it), and a lock that turns out\n * to be another, live owner's goes back. Answers `broken` when the caller should try again at\n * once, `absent` when no lock file exists, and `held` when the caller should wait: a live\n * owner's lock, or one that can't be read (on Windows, a lock being deleted).\n */\n #breakStale(): \"broken\" | \"absent\" | \"held\" {\n // One descriptor for the age and the contents, so both describe the same file.\n let seen: string;\n let age: number;\n let handle: number | undefined;\n try {\n handle = openSync(this.#lockFile, \"r\");\n age = Date.now() - fstatSync(handle).mtimeMs;\n if (age <= STALE_LOCK_MS) return \"held\";\n seen = readFileSync(handle, \"utf8\");\n } catch (error) {\n return (error as NodeJS.ErrnoException).code === \"ENOENT\" ? \"absent\" : \"held\";\n } finally {\n if (handle !== undefined) closeSync(handle);\n }\n if (age <= DEAD_LOCK_MS && ownerAlive(seen)) return \"held\";\n const aside = `${this.#lockFile}.${process.pid}.${randomUUID()}.stale`;\n try {\n renameSync(this.#lockFile, aside);\n } catch (error) {\n // Another breaker moved it first: try for it at once; anything else, wait.\n return (error as NodeJS.ErrnoException).code === \"ENOENT\" ? \"broken\" : \"held\";\n }\n try {\n if (readFileSync(aside, \"utf8\") !== seen) linkSync(aside, this.#lockFile);\n } catch {\n // Another process holds a new lock already; ours goes, and the caller waits for theirs.\n } finally {\n rmSync(aside, { force: true });\n }\n return \"broken\";\n }\n}\n\n/**\n * Renames `from` over `to`. Windows refuses to replace a file another process has open, as a\n * process reading the settings outside the lock does, with `EPERM` (MT-01: CI's install-ID race,\n * traced on `windows-latest`); the rename is tried again for up to a second, as the runtime's\n * renames are (D150 item 8), and never turned into a copy.\n */\nexport function replace(\n from: string,\n to: string,\n platform: string,\n rename: (from: string, to: string) => void = renameSync,\n): void {\n const deadline = Date.now() + 1000;\n for (let pause = 5; ; pause = Math.min(pause * 2, 100)) {\n try {\n rename(from, to);\n return;\n } catch (error) {\n const code = (error as NodeJS.ErrnoException).code;\n const shared = code === \"EPERM\" || code === \"EACCES\" || code === \"EBUSY\";\n if (platform !== \"win32\" || !shared || Date.now() > deadline) throw error;\n Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, pause);\n }\n }\n}\n\n/** Whether this process can create a file in `dir`: a probe of its own, removed at once. */\nexport function writable(\n dir: string,\n remove: (path: string) => void = (path) => rmSync(path, { force: true }),\n): boolean {\n const probe = join(dir, `.probe-${process.pid}-${randomUUID()}`);\n try {\n writeFileSync(probe, \"\", { flag: \"wx\", mode: 0o600 });\n } catch {\n return false;\n }\n // The probe was made, so the directory is writable whatever its removal meets: on Windows an\n // antivirus or the indexer may hold the new file for a moment (EBUSY, EPERM), and telemetry never\n // stops a command (D140 item 5; Opus review of MT-01, 8). An empty probe left behind is harmless.\n try {\n remove(probe);\n } catch {\n // Left for the next probe's directory to carry; nothing reads it.\n }\n return true;\n}\n\n/**\n * Whether a lock's exclusive create failed because another process holds it. On Windows a lock\n * its owner just removed stays \"delete pending\" while anyone still has it open (another process\n * judging its age), and creating it again meanwhile is refused as access denied, not `EEXIST`\n * (WP-W2: twelve first runs at once on an arm64 runner): that is contention too, never a reason\n * to give up.\n */\nfunction held(code: string | undefined, platform: string): boolean {\n return code === \"EEXIST\" || (platform === \"win32\" && (code === \"EPERM\" || code === \"EACCES\"));\n}\n\n/** Whether `dir` can never be created: its nearest existing ancestor isn't a directory. */\nfunction blocked(dir: string): boolean {\n for (let path = dir; ; path = dirname(path)) {\n try {\n return !statSync(path).isDirectory();\n } catch (error) {\n const code = (error as NodeJS.ErrnoException).code;\n if (code === \"ENOTDIR\") return true;\n if (code !== \"ENOENT\") return false;\n }\n if (dirname(path) === path) return false;\n }\n}\n\n/** Whether the process a lock names still runs; a lock whose owner can't be read counts as live. */\nfunction ownerAlive(lock: string): boolean {\n const pid = Number(lock.split(\" \")[0]);\n if (!Number.isSafeInteger(pid) || pid <= 0) return false;\n try {\n process.kill(pid, 0);\n return true;\n } catch (error) {\n return (error as NodeJS.ErrnoException).code === \"EPERM\";\n }\n}\n\n/** The newest events within 1,000, none older than 7 days; lines that aren't JSON go. */\nfunction bounded(lines: readonly string[], now: number): string[] {\n const fresh = lines.filter((line) => {\n try {\n const at = Date.parse((JSON.parse(line) as { at?: string }).at ?? \"\");\n return Number.isFinite(at) && now - at <= QUEUE_MAX_AGE_MS;\n } catch {\n return false;\n }\n });\n return fresh.slice(-QUEUE_MAX_EVENTS);\n}\n","/**\n * Sending the queue (D140 item 6): a detached process the CLI starts after a command, at most one\n * at a time and at most once a minute. It reads the decision again before it sends, so consent\n * withdrawn in between drops the events; it posts at most 100 events per request with no header\n * but `Content-Type`, `User-Agent` and the ones HTTP itself needs, and keeps what a failed\n * attempt couldn't send for the next flush.\n */\n\nimport { spawn } from \"node:child_process\";\nimport { request as httpRequest } from \"node:http\";\nimport { request as httpsRequest } from \"node:https\";\nimport { join } from \"node:path\";\nimport { detachedDirectory, detachedEnvironment } from \"../detached.ts\";\nimport type { Environment } from \"../home.ts\";\nimport { cliVersion, fromSource, packageRoot } from \"../version.ts\";\nimport { buildEndpoint, decide } from \"./decision.ts\";\nimport { TelemetryEvent } from \"./events.ts\";\nimport { QUEUE_MAX_EVENTS, type TelemetryStore } from \"./store.ts\";\n\n/** At most this many events per request. */\nexport const BATCH = 100;\n/** A flush starts at most this often. */\nexport const FLUSH_INTERVAL_MS = 60_000;\n/** The flush interval; `MOSTRA_TEST_FLUSH_INTERVAL_MS` shortens it, for tests only. */\nfunction flushInterval(env: Environment): number {\n const test = Number(env[\"MOSTRA_TEST_FLUSH_INTERVAL_MS\"]);\n return Number.isInteger(test) && test >= 0 && test < FLUSH_INTERVAL_MS ? test : FLUSH_INTERVAL_MS;\n}\n\n/** One request's limit. */\nconst REQUEST_TIMEOUT_MS = 5000;\n\n/** Posts one batch; resolves with whether the collector took it (any 2xx). */\nexport function post(endpoint: string, body: string): Promise<boolean> {\n return new Promise((resolve) => {\n const url = new URL(endpoint);\n const send = url.protocol === \"https:\" ? httpsRequest : httpRequest;\n const req = send(\n url,\n {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"User-Agent\": `mostra-cli/${cliVersion()}`,\n \"Content-Length\": Buffer.byteLength(body),\n },\n timeout: REQUEST_TIMEOUT_MS,\n },\n (response) => {\n response.resume();\n response.on(\"end\", () =>\n resolve((response.statusCode ?? 0) >= 200 && (response.statusCode ?? 0) < 300),\n );\n response.on(\"error\", () => resolve(false));\n },\n );\n req.on(\"timeout\", () => req.destroy());\n req.on(\"error\", () => resolve(false));\n req.end(body);\n });\n}\n\n/**\n * Sends the queue: the decision again, then batches until one fails. Events the schema refuses\n * never leave the machine.\n */\nexport async function flush(\n store: TelemetryStore,\n env: Environment,\n now: () => number,\n): Promise<void> {\n const settings = store.read();\n // The queue holds only events of uses that aren't embedded; the rest of the order applies again.\n const decision = decide({\n env,\n buildEndpoint: buildEndpoint(),\n embedded: false,\n settings,\n askFirst: false,\n });\n if (!decision.enabled || decision.endpoint === undefined) {\n // Consent withdrawn (or never given) since the events were queued: they go unsent.\n if (decision.reason !== \"no-collector\" && settings.ok) store.drop(store.queued(now()), now());\n return;\n }\n // The queue holds at most QUEUE_MAX_EVENTS, so a flush needs at most this many batches.\n for (let round = 0; round <= Math.ceil(QUEUE_MAX_EVENTS / BATCH); round += 1) {\n const lines = store.queued(now()).slice(0, BATCH);\n if (lines.length === 0) return;\n const events: unknown[] = [];\n for (const line of lines) {\n try {\n const parsed = TelemetryEvent.safeParse(JSON.parse(line));\n if (parsed.success) events.push(parsed.data);\n } catch {\n // Not an event: dropped with the batch.\n }\n }\n const sent = events.length === 0 || (await post(decision.endpoint, JSON.stringify({ events })));\n if (!sent) return;\n // Nothing removed (another process changed the queue, or the lock was out of reach): stop\n // rather than send the same batch again.\n if (store.drop(lines, now()) === 0) return;\n }\n}\n\n/** The flusher's entry in the CLI's package. */\nfunction flusherEntry(): { readonly entry: string; readonly nodeArgs: readonly string[] } {\n const root = packageRoot();\n return fromSource()\n ? {\n entry: join(root, \"src\", \"telemetry\", \"flush-main.ts\"),\n nodeArgs: [\"--conditions=@mostra/source\"],\n }\n : { entry: join(root, \"dist\", \"telemetry\", \"flush-main.js\"), nodeArgs: [] };\n}\n\n/**\n * Starts a flusher when the queue has events and no flush started in the last minute, fully\n * detached: no stdio, its own process group, never waited for, so `mostra … | jq` and every\n * harness reading to end of file finish when the command does.\n */\nexport function startFlusher(store: TelemetryStore, env: Environment, now: number): void {\n if (store.queued(now).length === 0) return;\n let due = false;\n store.update((current) => {\n const last = current.lastFlushAt === undefined ? 0 : Date.parse(current.lastFlushAt);\n if (Number.isFinite(last) && now - last < flushInterval(env)) return current;\n due = true;\n return { ...current, lastFlushAt: new Date(now).toISOString() };\n });\n if (!due) return;\n const { entry, nodeArgs } = flusherEntry();\n try {\n const child = spawn(process.execPath, [...nodeArgs, entry, store.dir], {\n detached: true,\n stdio: \"ignore\",\n // Not the command's directory but its file system's root, which always exists: on Windows\n // a process's working directory can't be removed or renamed while it runs, and the flusher\n // outlives the command (WP-W2).\n cwd: detachedDirectory(store.dir),\n env: detachedEnvironment({ ...process.env, ...env }) as NodeJS.ProcessEnv,\n });\n child.on(\"error\", () => {});\n child.unref();\n } catch {\n // Telemetry never changes a command's outcome.\n }\n}\n"],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { a as COMMANDS, i as referenceMarkdown, n as commandDefinition, r as commandHelp, t as run } from "./run-
|
|
1
|
+
import { a as COMMANDS, i as referenceMarkdown, n as commandDefinition, r as commandHelp, t as run } from "./run-BpVu5uQ8.js";
|
|
2
2
|
import { n as exitCodeFor, t as EXIT } from "./exit-CBIf8nAU.js";
|
|
3
3
|
export { COMMANDS, EXIT, commandDefinition, commandHelp, exitCodeFor, referenceMarkdown, run };
|
|
@@ -1,4 +1,4 @@
|
|
|
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-
|
|
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
4
|
import { existsSync, lstatSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, readlinkSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
@@ -1909,6 +1909,14 @@ const gcCommand = defineCommand({
|
|
|
1909
1909
|
}
|
|
1910
1910
|
});
|
|
1911
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
|
+
}
|
|
1912
1920
|
const doctorCommand = defineCommand({
|
|
1913
1921
|
path: ["doctor"],
|
|
1914
1922
|
summary: "Check the platform, the runtime, the home, its daemon and ledger, and the login service.",
|
|
@@ -1994,7 +2002,7 @@ const doctorCommand = defineCommand({
|
|
|
1994
2002
|
status: "ok",
|
|
1995
2003
|
detail: telemetry.text.split("\n")[0]
|
|
1996
2004
|
});
|
|
1997
|
-
const firstError = checks
|
|
2005
|
+
const firstError = firstFailing(checks);
|
|
1998
2006
|
const human = checks.map((check) => {
|
|
1999
2007
|
const mark = check.status === "ok" ? "ok " : check.status === "warning" ? "warn" : "FAIL";
|
|
2000
2008
|
const what = check.diagnostic ? `${check.diagnostic.code}: ${check.diagnostic.message}` : check.detail ?? "";
|
|
@@ -2373,7 +2381,7 @@ const verifyCommand = defineCommand({
|
|
|
2373
2381
|
return {
|
|
2374
2382
|
result: verdict,
|
|
2375
2383
|
human: verdictText(verdict),
|
|
2376
|
-
...verdict.status === "
|
|
2384
|
+
...verdict.status === "pass" ? {} : { exit: EXIT.failure }
|
|
2377
2385
|
};
|
|
2378
2386
|
}
|
|
2379
2387
|
});
|
|
@@ -2639,6 +2647,11 @@ const waitCommand = defineCommand({
|
|
|
2639
2647
|
result,
|
|
2640
2648
|
human: `${result.event.type} at cursor ${result.cursor}\n`
|
|
2641
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
|
+
};
|
|
2642
2655
|
if (deadline === void 0) {
|
|
2643
2656
|
after = result.cursor;
|
|
2644
2657
|
continue;
|
|
@@ -3905,4 +3918,4 @@ async function runRuntime(io, args, stdio) {
|
|
|
3905
3918
|
//#endregion
|
|
3906
3919
|
export { COMMANDS as a, referenceMarkdown as i, commandDefinition as n, commandHelp as r, run as t };
|
|
3907
3920
|
|
|
3908
|
-
//# sourceMappingURL=run-
|
|
3921
|
+
//# sourceMappingURL=run-BpVu5uQ8.js.map
|