@mostra/cli 0.0.0-stage → 0.2.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.
Files changed (196) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +18 -2
  3. package/dist/bin.d.ts +1 -0
  4. package/dist/bin.js +79 -0
  5. package/dist/bin.js.map +1 -0
  6. package/dist/bootstrap-DfEe_yPr.js +113 -0
  7. package/dist/bootstrap-DfEe_yPr.js.map +1 -0
  8. package/dist/exit-CBIf8nAU.js +79 -0
  9. package/dist/exit-CBIf8nAU.js.map +1 -0
  10. package/dist/flush-BCuWs0Zh.js +718 -0
  11. package/dist/flush-BCuWs0Zh.js.map +1 -0
  12. package/dist/index.d.ts +230 -0
  13. package/dist/index.d.ts.map +1 -0
  14. package/dist/index.js +3 -0
  15. package/dist/reference/cli.md +977 -0
  16. package/dist/reference/diagnostics.json +1264 -0
  17. package/dist/reference/errors/MOSTRA_A11Y_MINOR.md +23 -0
  18. package/dist/reference/errors/MOSTRA_A11Y_SERIOUS.md +23 -0
  19. package/dist/reference/errors/MOSTRA_ACTION_CANCELLED.md +23 -0
  20. package/dist/reference/errors/MOSTRA_ACTION_EVENT_INVALID.md +23 -0
  21. package/dist/reference/errors/MOSTRA_ACTION_EXPORT_INVALID.md +23 -0
  22. package/dist/reference/errors/MOSTRA_ACTION_INTERRUPTED.md +23 -0
  23. package/dist/reference/errors/MOSTRA_ACTION_RUN_NOT_FOUND.md +23 -0
  24. package/dist/reference/errors/MOSTRA_ACTION_THREW.md +23 -0
  25. package/dist/reference/errors/MOSTRA_ACTION_TOO_LARGE.md +23 -0
  26. package/dist/reference/errors/MOSTRA_ACTION_UNKNOWN_FUNCTION.md +23 -0
  27. package/dist/reference/errors/MOSTRA_ACTION_UNSERIALIZABLE.md +23 -0
  28. package/dist/reference/errors/MOSTRA_ARTIFACT_DIR_NOT_EMPTY.md +23 -0
  29. package/dist/reference/errors/MOSTRA_ARTIFACT_ENTRY_AMBIGUOUS.md +23 -0
  30. package/dist/reference/errors/MOSTRA_ARTIFACT_NOT_FOUND.md +23 -0
  31. package/dist/reference/errors/MOSTRA_ARTIFACT_NO_ENTRY.md +23 -0
  32. package/dist/reference/errors/MOSTRA_ARTIFACT_NO_PAGE.md +23 -0
  33. package/dist/reference/errors/MOSTRA_ARTIFACT_SOURCE_MISSING.md +23 -0
  34. package/dist/reference/errors/MOSTRA_ARTIFACT_SOURCE_UNREADABLE.md +23 -0
  35. package/dist/reference/errors/MOSTRA_ARTIFACT_TEMPLATE_UNKNOWN.md +23 -0
  36. package/dist/reference/errors/MOSTRA_ARTIFACT_VERSION_COLLECTED.md +23 -0
  37. package/dist/reference/errors/MOSTRA_ARTIFACT_VERSION_NOT_FOUND.md +23 -0
  38. package/dist/reference/errors/MOSTRA_BUILD_CRASHED.md +23 -0
  39. package/dist/reference/errors/MOSTRA_BUILD_FAILED.md +23 -0
  40. package/dist/reference/errors/MOSTRA_BUILD_TIMEOUT.md +23 -0
  41. package/dist/reference/errors/MOSTRA_BUILD_UNRESOLVED.md +23 -0
  42. package/dist/reference/errors/MOSTRA_CLI_CONFIG_UNWRITABLE.md +23 -0
  43. package/dist/reference/errors/MOSTRA_CLI_UNKNOWN_COMMAND.md +23 -0
  44. package/dist/reference/errors/MOSTRA_CLI_USAGE.md +23 -0
  45. package/dist/reference/errors/MOSTRA_CONSOLE_ERROR.md +23 -0
  46. package/dist/reference/errors/MOSTRA_CONSOLE_WARNING.md +23 -0
  47. package/dist/reference/errors/MOSTRA_DEP_INSTALL_FAILED.md +23 -0
  48. package/dist/reference/errors/MOSTRA_DEP_MISSING.md +23 -0
  49. package/dist/reference/errors/MOSTRA_DEP_NOT_FOUND.md +23 -0
  50. package/dist/reference/errors/MOSTRA_DEP_OFFLINE.md +23 -0
  51. package/dist/reference/errors/MOSTRA_DEP_SPEC_INVALID.md +23 -0
  52. package/dist/reference/errors/MOSTRA_DEP_TIMEOUT.md +23 -0
  53. package/dist/reference/errors/MOSTRA_DEP_WARM_CONFLICT.md +23 -0
  54. package/dist/reference/errors/MOSTRA_ENV_BIND_FAILED.md +23 -0
  55. package/dist/reference/errors/MOSTRA_ENV_BROWSER_MISSING.md +23 -0
  56. package/dist/reference/errors/MOSTRA_ENV_BUNDLE_DOWNLOAD_FAILED.md +23 -0
  57. package/dist/reference/errors/MOSTRA_ENV_BUNDLE_INVALID.md +23 -0
  58. package/dist/reference/errors/MOSTRA_ENV_BUNDLE_UNAVAILABLE.md +23 -0
  59. package/dist/reference/errors/MOSTRA_ENV_NODE_UNSUPPORTED.md +23 -0
  60. package/dist/reference/errors/MOSTRA_ENV_PORT_TAKEN.md +23 -0
  61. package/dist/reference/errors/MOSTRA_ENV_RUNTIME_MISSING.md +23 -0
  62. package/dist/reference/errors/MOSTRA_ENV_SERVICE_FAILED.md +23 -0
  63. package/dist/reference/errors/MOSTRA_ENV_SHELL_MISSING.md +23 -0
  64. package/dist/reference/errors/MOSTRA_ENV_WARM_MISSING.md +23 -0
  65. package/dist/reference/errors/MOSTRA_FIELD_CONFLICT.md +23 -0
  66. package/dist/reference/errors/MOSTRA_FIELD_DUPLICATE_ID.md +23 -0
  67. package/dist/reference/errors/MOSTRA_FIELD_ID_INVALID.md +23 -0
  68. package/dist/reference/errors/MOSTRA_FIELD_ID_UNSTABLE.md +23 -0
  69. package/dist/reference/errors/MOSTRA_FIELD_MUTATION_ID_REUSED.md +23 -0
  70. package/dist/reference/errors/MOSTRA_FIELD_SHARED.md +23 -0
  71. package/dist/reference/errors/MOSTRA_FIELD_TOO_LARGE.md +23 -0
  72. package/dist/reference/errors/MOSTRA_FIELD_UNBOUND.md +23 -0
  73. package/dist/reference/errors/MOSTRA_HOME_DISK_FULL.md +23 -0
  74. package/dist/reference/errors/MOSTRA_HOME_GC_INCOMPLETE.md +23 -0
  75. package/dist/reference/errors/MOSTRA_HOME_GONE.md +23 -0
  76. package/dist/reference/errors/MOSTRA_HOME_INTEGRITY.md +23 -0
  77. package/dist/reference/errors/MOSTRA_HOME_LEDGER_UNREADABLE.md +23 -0
  78. package/dist/reference/errors/MOSTRA_HOME_LOCKED.md +23 -0
  79. package/dist/reference/errors/MOSTRA_HOME_MISMATCH.md +23 -0
  80. package/dist/reference/errors/MOSTRA_HOME_NOT_ALWAYS_ON.md +23 -0
  81. package/dist/reference/errors/MOSTRA_HOME_NOT_OWNED.md +23 -0
  82. package/dist/reference/errors/MOSTRA_HOME_SOCKET_TOO_LONG.md +23 -0
  83. package/dist/reference/errors/MOSTRA_HOME_TOO_NEW.md +23 -0
  84. package/dist/reference/errors/MOSTRA_HOST_CONNECTION_CLOSED.md +23 -0
  85. package/dist/reference/errors/MOSTRA_HOST_CURSOR_COMPACTED.md +23 -0
  86. package/dist/reference/errors/MOSTRA_HOST_IDENTITY_UNAVAILABLE.md +23 -0
  87. package/dist/reference/errors/MOSTRA_HOST_INITIALIZE_ORDER.md +23 -0
  88. package/dist/reference/errors/MOSTRA_HOST_INTERNAL_ERROR.md +23 -0
  89. package/dist/reference/errors/MOSTRA_HOST_INVALID_PARAMS.md +23 -0
  90. package/dist/reference/errors/MOSTRA_HOST_MALFORMED.md +23 -0
  91. package/dist/reference/errors/MOSTRA_HOST_MESSAGE_TOO_LARGE.md +23 -0
  92. package/dist/reference/errors/MOSTRA_HOST_METHOD_UNKNOWN.md +23 -0
  93. package/dist/reference/errors/MOSTRA_HOST_NOTIFICATION_UNSUPPORTED.md +23 -0
  94. package/dist/reference/errors/MOSTRA_HOST_PORT_UNSUPPORTED.md +23 -0
  95. package/dist/reference/errors/MOSTRA_HOST_REQUEST_ID_REUSED.md +23 -0
  96. package/dist/reference/errors/MOSTRA_HOST_REVISION_UNSUPPORTED.md +23 -0
  97. package/dist/reference/errors/MOSTRA_HOST_SHUTDOWN_REFUSED.md +23 -0
  98. package/dist/reference/errors/MOSTRA_HOST_SUBSCRIPTION_UNKNOWN.md +23 -0
  99. package/dist/reference/errors/MOSTRA_HOST_TIMEOUT.md +23 -0
  100. package/dist/reference/errors/MOSTRA_HYGIENE_CONTENT_TYPE.md +23 -0
  101. package/dist/reference/errors/MOSTRA_HYGIENE_CSRF_MISSING.md +23 -0
  102. package/dist/reference/errors/MOSTRA_HYGIENE_HEADERS_TOO_LARGE.md +23 -0
  103. package/dist/reference/errors/MOSTRA_HYGIENE_HOST_REFUSED.md +23 -0
  104. package/dist/reference/errors/MOSTRA_HYGIENE_NOT_SERVED.md +23 -0
  105. package/dist/reference/errors/MOSTRA_HYGIENE_ORIGIN_REFUSED.md +23 -0
  106. package/dist/reference/errors/MOSTRA_HYGIENE_RATE_LIMITED.md +23 -0
  107. package/dist/reference/errors/MOSTRA_HYGIENE_REQUEST_MALFORMED.md +23 -0
  108. package/dist/reference/errors/MOSTRA_HYGIENE_SCOPE_DENIED.md +23 -0
  109. package/dist/reference/errors/MOSTRA_HYGIENE_SESSION_CONFLICT.md +23 -0
  110. package/dist/reference/errors/MOSTRA_HYGIENE_SESSION_MISSING.md +23 -0
  111. package/dist/reference/errors/MOSTRA_HYGIENE_TOKEN_INVALID.md +23 -0
  112. package/dist/reference/errors/MOSTRA_HYGIENE_TOKEN_REVOKED.md +23 -0
  113. package/dist/reference/errors/MOSTRA_LIVE_SOURCE_FAILED.md +23 -0
  114. package/dist/reference/errors/MOSTRA_LIVE_TOO_LARGE.md +23 -0
  115. package/dist/reference/errors/MOSTRA_MANIFEST_INVALID.md +23 -0
  116. package/dist/reference/errors/MOSTRA_MANIFEST_NOT_LITERAL.md +23 -0
  117. package/dist/reference/errors/MOSTRA_MANIFEST_UNKNOWN_KEY.md +23 -0
  118. package/dist/reference/errors/MOSTRA_NETWORK_EXTERNAL_FAILED.md +23 -0
  119. package/dist/reference/errors/MOSTRA_NETWORK_REQUEST_FAILED.md +23 -0
  120. package/dist/reference/errors/MOSTRA_PLATFORM_UNSUPPORTED.md +23 -0
  121. package/dist/reference/errors/MOSTRA_PUBLISH_ID_CONFLICT.md +23 -0
  122. package/dist/reference/errors/MOSTRA_PUBLISH_SOURCE_CHANGED.md +23 -0
  123. package/dist/reference/errors/MOSTRA_PUBLISH_VERIFY_FAILED.md +23 -0
  124. package/dist/reference/errors/MOSTRA_REGRESSION_ACTION_REMOVED.md +23 -0
  125. package/dist/reference/errors/MOSTRA_REGRESSION_ARIA.md +23 -0
  126. package/dist/reference/errors/MOSTRA_REGRESSION_FIELD_REMOVED.md +23 -0
  127. package/dist/reference/errors/MOSTRA_REGRESSION_LIVE_REMOVED.md +23 -0
  128. package/dist/reference/errors/MOSTRA_REGRESSION_PAGE_REMOVED.md +23 -0
  129. package/dist/reference/errors/MOSTRA_REGRESSION_PIXELS.md +23 -0
  130. package/dist/reference/errors/MOSTRA_RUNTIME_BLANK.md +23 -0
  131. package/dist/reference/errors/MOSTRA_RUNTIME_NOT_READY.md +23 -0
  132. package/dist/reference/errors/MOSTRA_RUNTIME_PAGE_ERROR.md +23 -0
  133. package/dist/reference/errors/MOSTRA_RUNTIME_WORKER_CRASHED.md +23 -0
  134. package/dist/reference/errors/MOSTRA_RUNTIME_WORKER_PARKED.md +23 -0
  135. package/dist/reference/errors/MOSTRA_SECRET_MISSING.md +23 -0
  136. package/dist/reference/errors/MOSTRA_SMOKE_BUDGET.md +23 -0
  137. package/dist/reference/errors/MOSTRA_SMOKE_NO_EFFECT.md +23 -0
  138. package/dist/reference/errors/MOSTRA_SUBMIT_ALREADY.md +23 -0
  139. package/dist/reference/errors/MOSTRA_SUBMIT_BLOCKED.md +23 -0
  140. package/dist/reference/errors/MOSTRA_SUBMIT_CHANNEL_DENIED.md +23 -0
  141. package/dist/reference/errors/MOSTRA_SUBMIT_NOT_FOUND.md +23 -0
  142. package/dist/reference/errors/MOSTRA_SUBMIT_POLICY_NONE.md +23 -0
  143. package/dist/reference/errors/MOSTRA_SUBMIT_REVISION_MOVED.md +23 -0
  144. package/dist/reference/errors/MOSTRA_SUBMIT_SIGNAL_INVALID.md +23 -0
  145. package/dist/reference/errors/MOSTRA_SUBMIT_VIEW_INVALID.md +23 -0
  146. package/dist/reference/errors/MOSTRA_TARGET_UNAVAILABLE.md +23 -0
  147. package/dist/reference/errors/MOSTRA_TYPE_ERROR.md +23 -0
  148. package/dist/reference/errors/MOSTRA_TYPE_UNTYPED_PACKAGE.md +23 -0
  149. package/dist/reference/errors/MOSTRA_VERIFY_ACTION_SKIPPED.md +23 -0
  150. package/dist/reference/errors/MOSTRA_VERIFY_BASELINE_MISSING.md +23 -0
  151. package/dist/reference/errors/MOSTRA_VERIFY_CRASHED.md +23 -0
  152. package/dist/reference/errors/MOSTRA_VERIFY_LIVE_FAILED.md +23 -0
  153. package/dist/reference/errors/MOSTRA_VERIFY_NO_BASELINE.md +23 -0
  154. package/dist/reference/errors/MOSTRA_VERIFY_SECRET_MISSING.md +23 -0
  155. package/dist/reference/errors/MOSTRA_VERIFY_SKIP_CONSEQUENCE.md +23 -0
  156. package/dist/reference/errors/MOSTRA_VERIFY_TIMEOUT.md +23 -0
  157. package/dist/reference/telemetry.md +82 -0
  158. package/dist/run-BJaRnz-Q.js +3824 -0
  159. package/dist/run-BJaRnz-Q.js.map +1 -0
  160. package/dist/run-CMyjOm0Q.js +2 -0
  161. package/dist/telemetry/flush-main.d.ts +1 -0
  162. package/dist/telemetry/flush-main.js +14 -0
  163. package/dist/telemetry/flush-main.js.map +1 -0
  164. package/package.json +62 -3
  165. package/src/bin.ts +70 -0
  166. package/src/bootstrap.ts +156 -0
  167. package/src/browser.ts +186 -0
  168. package/src/client.ts +313 -0
  169. package/src/command.ts +147 -0
  170. package/src/commands/admin.ts +791 -0
  171. package/src/commands/authoring.ts +244 -0
  172. package/src/commands/dev.ts +106 -0
  173. package/src/commands/index.ts +71 -0
  174. package/src/commands/interaction.ts +288 -0
  175. package/src/commands/shared.ts +71 -0
  176. package/src/commands/viewing.ts +153 -0
  177. package/src/docs.ts +111 -0
  178. package/src/exit.ts +89 -0
  179. package/src/help.ts +170 -0
  180. package/src/home.ts +111 -0
  181. package/src/index.ts +23 -0
  182. package/src/node-gate.ts +15 -0
  183. package/src/parse.ts +246 -0
  184. package/src/run.ts +387 -0
  185. package/src/telemetry/decision.ts +101 -0
  186. package/src/telemetry/events.ts +147 -0
  187. package/src/telemetry/flush-main.ts +13 -0
  188. package/src/telemetry/flush.ts +143 -0
  189. package/src/telemetry/harness.ts +39 -0
  190. package/src/telemetry/index.ts +318 -0
  191. package/src/telemetry/store.ts +379 -0
  192. package/src/telemetry/timezone.ts +72 -0
  193. package/src/telemetry/words.ts +87 -0
  194. package/src/telemetry/zone-sets.ts +182 -0
  195. package/src/telemetry/zones.generated.ts +720 -0
  196. package/src/version.ts +43 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flush-BCuWs0Zh.js","names":["httpsRequest","httpRequest"],"sources":["../src/version.ts","../src/telemetry/decision.ts","../src/telemetry/harness.ts","../src/telemetry/events.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\"]),\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\";\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 * 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 writeFileSync,\n} from \"node:fs\";\nimport { 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\n constructor(dir: string) {\n this.dir = dir;\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 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 renameSync(temporary, this.queueFile);\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 renameSync(temporary, this.settingsFile);\n }\n\n #prepareDir(): void {\n mkdirSync(this.dir, { recursive: true, mode: 0o700 });\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 if ((error as NodeJS.ErrnoException).code !== \"EEXIST\") return undefined;\n if (this.#breakStale()) continue;\n if (Date.now() > deadline) return undefined;\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; `true` when the caller should try again at once. The lock\n * is moved aside under a name of this process's own (a rename is atomic, so of two processes\n * breaking it one gets it), and a lock that turns out to be another, live owner's goes back.\n */\n #breakStale(): boolean {\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 false;\n seen = readFileSync(handle, \"utf8\");\n } catch {\n return true;\n } finally {\n if (handle !== undefined) closeSync(handle);\n }\n if (age <= DEAD_LOCK_MS && ownerAlive(seen)) return false;\n const aside = `${this.#lockFile}.${process.pid}.${randomUUID()}.stale`;\n try {\n renameSync(this.#lockFile, aside);\n } catch {\n return true;\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 true;\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 */\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 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 env: { ...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":";;;;;;;;;;;AAKA,IAAI;AAEJ,SAAS,MAA2D;CAClE,IAAI,OAAO,OAAO;CAClB,IAAI,MAAM,QAAQ,cAAc,YAAY,GAAG,CAAC;CAChD,SAAS;EACP,IAAI;GACF,MAAM,WAAW,KAAK,MAAM,aAAa,KAAK,KAAK,cAAc,GAAG,MAAM,CAAC;GAI3E,IAAI,SAAS,SAAS,iBAAiB,SAAS,SAAS;IACvD,QAAQ;KAAE,MAAM;KAAK,SAAS,SAAS;IAAQ;IAC/C,OAAO;GACT;EACF,QAAQ,CAER;EACA,MAAM,SAAS,QAAQ,GAAG;EAC1B,IAAI,WAAW,KAAK,MAAM,IAAI,MAAM,2CAA2C;EAC/E,MAAM;CACR;AACF;;AAGA,SAAgB,aAAqB;CACnC,OAAO,IAAI,CAAC,CAAC;AACf;;AAGA,SAAgB,cAAsB;CACpC,OAAO,IAAI,CAAC,CAAC;AACf;;AAGA,SAAgB,aAAsB;CACpC,OAAO,YAAY,IAAI,SAAS,KAAK;AACvC;;;;AC9BA,SAAgB,gBAAwB;CACtC,OAAA;AACF;AAyBA,MAAM,6BAAa,IAAI,IAAI;CAAC;CAAK;CAAS;CAAO;AAAI,CAAC;AACtD,MAAM,4BAAY,IAAI,IAAI;CAAC;CAAK;CAAQ;CAAM;AAAK,CAAC;;AAGpD,SAAgB,OAAO,OAAoC;CACzD,IAAI,UAAU,KAAA,GAAW,OAAO;CAChC,MAAM,aAAa,MAAM,KAAK,CAAC,CAAC,YAAY;CAC5C,OAAO,eAAe,MAAM,eAAe,OAAO,eAAe;AACnE;;;;;AAMA,SAAgB,WAAW,KAAkB,OAAmC;CAC9E,MAAM,WAAW,IAAI;CACrB,MAAM,SAAS,aAAa,KAAA,KAAa,aAAa,KAAK,WAAW;CACtE,IAAI,WAAW,IAAI,OAAO,KAAA;CAC1B,IAAI;CACJ,IAAI;EACF,MAAM,IAAI,IAAI,MAAM;CACtB,QAAQ;EACN;CACF;CACA,IAAI,IAAI,aAAa,MAAM,IAAI,aAAa,IAAI,OAAO,KAAA;CACvD,IAAI,IAAI,aAAa,UAAU,OAAO,IAAI;CAC1C,MAAM,WACJ,IAAI,aAAa,eAAe,IAAI,aAAa,WAAW,IAAI,aAAa;CAC/E,OAAO,IAAI,aAAa,WAAW,WAAW,IAAI,OAAO,KAAA;AAC3D;;AAGA,SAAgB,OAAO,QAA2C;CAChE,MAAM,EAAE,aAAa;CACrB,MAAM,OAAO,YAAgD;EAC3D,SAAS;EACT;EACA;CACF;CACA,MAAM,WAAW,WAAW,OAAO,KAAK,OAAO,aAAa;CAC5D,IAAI,aAAa,KAAA,GAAW,OAAO,IAAI,cAAc;CACrD,MAAM,KAAK;EAAE;EAAU;CAAS;CAChC,IAAI,OAAO,UAAU,OAAO;EAAE,GAAG;EAAI,SAAS;EAAO,QAAQ;CAAW;CACxE,IAAI,OAAO,OAAO,IAAI,eAAe,GAAG,OAAO;EAAE,GAAG;EAAI,SAAS;EAAO,QAAQ;CAAe;CAE/F,MAAM,aADW,OAAO,IAAI,mBACD,EAAE,KAAK,CAAC,CAAC,YAAY;CAChD,IAAI,eAAe,KAAA,KAAa,WAAW,IAAI,UAAU,GACvD,OAAO;EAAE,GAAG;EAAI,SAAS;EAAO,QAAQ;CAAW;CAErD,IAAI,CAAC,OAAO,SAAS,IAAI,OAAO;EAAE,GAAG;EAAI,SAAS;EAAO,QAAQ;CAAa;CAC9E,MAAM,SAAS,OAAO,SAAS,SAAS;CACxC,IAAI,WAAW,OAAO,OAAO;EAAE,GAAG;EAAI,SAAS;EAAO,QAAQ;CAAU;CACxE,IAAI,eAAe,KAAA,KAAa,eAAe,IAC7C,OAAO,UAAU,IAAI,UAAU,IAC3B;EAAE,GAAG;EAAI,SAAS;EAAM,QAAQ;EAAY,SAAS;CAAW,IAChE;EAAE,GAAG;EAAI,SAAS;EAAO,QAAQ;CAAmB;CAE1D,IAAI,WAAW,MAAM,OAAO;EAAE,GAAG;EAAI,SAAS;EAAM,QAAQ;EAAW,SAAS;CAAW;CAC3F,OAAO,WACH;EAAE,GAAG;EAAI,SAAS;EAAO,QAAQ;CAAU,IAC3C;EAAE,GAAG;EAAI,SAAS;EAAM,QAAQ;EAAW,SAAS;CAAU;AACpE;;;;AC7FA,MAAa,YAAY;CAAC;CAAe;CAAS;CAAc;CAAY;AAAQ;;AAMpF,MAAM,UAAgE;CACpE,eAAe;CACf,OAAO;CACP,cAAc;CACd,UAAU;CACV,QAAQ;AACV;AAEA,MAAM,OAAO,UAAuC,UAAU,KAAA,KAAa,UAAU;;;;;;;AAQrF,SAAgB,cAAc,KAA2B;CACvD,MAAM,WAAW,IAAI;CACrB,IAAI,IAAI,QAAQ,GAAG;EACjB,MAAM,QAAQ,UAAU,MAAM,OAAO,UAAU,WAAW,EAAE,CAAC;EAC7D,IAAI,OAAO,OAAO;CACpB;CACA,KAAK,MAAM,MAAM,WAAW,IAAI,IAAI,IAAI,QAAQ,IAAI,GAAG,OAAO;CAC9D,IAAI,IAAI,QAAQ,KAAK,IAAI,IAAI,QAAQ,GAAG,OAAO;CAC/C,OAAO;AACT;;ACxBA,MAAa,YAAY;CAAC;CAAO;CAAQ;CAAS;CAAU;CAAS;AAAM;;AAG3E,SAAgB,eAAe,IAAwC;CACrE,IAAI,KAAK,KAAM,OAAO;CACtB,IAAI,KAAK,KAAM,OAAO;CACtB,IAAI,KAAK,KAAQ,OAAO;CACxB,IAAI,KAAK,MAAS,OAAO;CACzB,IAAI,KAAK,KAAS,OAAO;CACzB,OAAO;AACT;;AAGA,MAAM,OAAO,EACV,OAAO,CAAC,CACR,MAAM,IAAI,OAAO,YAAY,iBAAiB,KAAK,GAAG,EAAE,mBAAmB,CAAC;;AAG/E,MAAM,UAAU,EAAE,OAAO,CAAC,CAAC,MAAM,oBAAoB;AAErD,MAAM,SAAS;CACb,QAAQ,EAAE,QAAA,CAAc;CACxB,IAAI,EAAE,OAAO,CAAC,CAAC,MAAM,qCAAqC;CAC1D,WAAW,EAAE,KAAK;CAClB,SAAS,EAAE,OAAO,CAAC,CAAC,MAAM,mCAAmC;CAC7D,IAAI,EAAE,KAAK,CAAC,UAAU,OAAO,CAAC;CAC9B,MAAM,EAAE,OAAO,CAAC,CAAC,MAAM,kBAAkB;CACzC,MAAM,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,GAAG;CAC5B,SAAS,EAAE,KAAK;EAAC,GAAG;EAAW;EAAS;CAAM,CAAC;CAC/C,IAAI,EAAE,QAAQ;CACd,SAAS,EAAE,KAAK,CAAC,YAAY,SAAS,CAAC;AACzC;;AA+CA,MAAa,iBAA4C,EAAE,mBAAmB,SAAS;CACrF,EAAE,aAAa;EAAE,GAAG;EAAQ,OAAO,EAAE,QAAQ,eAAe;CAAE,CAAC;CAC/D,EAAE,aAAa;EACb,GAAG;EACH,OAAO,EAAE,QAAQ,aAAa;EAC9B;EACA,MAAM,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,GAAG;EAC5B,UAAU,EAAE,KAAK,SAAS;EAC1B,MAAM,EAAE,QAAQ;EAChB,OAAO,KAAK,SAAS;CACvB,CAAC;CACD,EAAE,aAAa;EACb,GAAG;EACH,OAAO,EAAE,QAAQ,eAAe;EAChC,QAAQ,EAAE,KAAK,CAAC,QAAQ,MAAM,CAAC;EAC/B,OAAO,EAAE,MAAM,IAAI,CAAC,CAAC,IAAI,EAAE;EAC3B,gBAAgB,EAAE,QAAQ;CAC5B,CAAC;CACD,EAAE,aAAa;EACb,GAAG;EACH,OAAO,EAAE,QAAQ,cAAc;EAC/B,QAAQ,EAAE,QAAQ;EAClB,QAAQ,EAAE,QAAQ;CACpB,CAAC;CAED,EAAE,aAAa;EACb,GAAG;EACH,OAAO,EAAE,QAAQ,YAAY;EAC7B,MAAM,EAAE,KAAK;GAAC;GAAW;GAAU;GAAc;EAAQ,CAAC;CAC5D,CAAC;CAED,EAAE,aAAa;EACb,GAAG;EACH,OAAO,EAAE,QAAQ,aAAa;EAC9B,QAAQ,EAAE,KAAK,CAAC,GAAG,WAAW,OAAO,CAAC;EACtC,eAAe,EACZ,OAAO,CAAC,CACR,MAAM,wBAAwB,CAAC,CAC/B,SAAS;CACd,CAAC;AACH,CAAC;;AAGD,SAAgB,OAAO,KAAmB;CACxC,OAAO,GAAG,IAAI,YAAY,CAAC,CAAC,MAAM,GAAG,EAAE,EAAE;AAC3C;;AAGA,SAAgB,aACd,OACA,OAC8C;CAC9C,MAAM,WAAW,CAAC,GAAG,IAAI,IAAI,MAAM,QAAQ,UAAU,MAAM,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK;CAC9E,OAAO;EAAE,OAAO,SAAS,MAAM,GAAG,EAAE;EAAG,gBAAgB,SAAS,SAAS;CAAG;AAC9E;;;;;;;;;;;;;;AC1FA,MAAM,gBAAgB;;;;;;AAMtB,MAAM,eAAe;;AAErB,MAAa,mBAAmB;AAGhC,MAAM,OAAO;;AAGb,IAAa,iBAAb,MAA4B;CAC1B;CACA;CACA;CACA;CACA;CAEA,YAAY,KAAa;EACvB,KAAK,MAAM;EACX,KAAK,eAAe,KAAK,KAAK,gBAAgB;EAC9C,KAAK,YAAY,KAAK,KAAK,wBAAwB;EACnD,KAAK,SAAS,KAAK,KAAK,cAAc;EACtC,KAAK,YAAY,KAAK,KAAK,gBAAgB;CAC7C;;CAGA,OAAqB;EACnB,MAAM,KAAK,KAAK,QAAQ;EACxB,IAAI,OAAO,MAAM,OAAO;GAAE,IAAI;GAAO,MAAM,KAAK;EAAO;EACvD,IAAI;EACJ,IAAI;GACF,OAAO,aAAa,KAAK,cAAc,MAAM;EAC/C,SAAS,OAAO;GACd,IAAK,MAAgC,SAAS,UAC5C,OAAO;IAAE,IAAI;IAAM,UAAU,OAAO,KAAA,IAAY,EAAE,WAAW,GAAG,IAAI,CAAC;GAAE;GACzE,OAAO;IAAE,IAAI;IAAO,MAAM,KAAK;GAAa;EAC9C;EACA,IAAI;GACF,MAAM,OAAO,KAAK,MAAM,IAAI;GAC5B,IAAI,OAAO,SAAS,YAAY,SAAS,QAAQ,MAAM,QAAQ,IAAI,GAAG,MAAM,IAAI,MAAM;GACtF,MAAM,EAAE,QAAQ,eAAe,uBAAuB,gBAAgB;GACtE,MAAM,YAAY;GAClB,IAAI,WAAW,KAAA,KAAa,WAAW,QAAQ,WAAW,OAAO,MAAM,IAAI,MAAM;GAEjF,IAAI,KAAK,iBAAiB,KAAA,GAAW,MAAM,IAAI,MAAM;GACrD,IAAI,kBAAkB,KAAA,KAAa,OAAO,kBAAkB,UAAU,MAAM,IAAI,MAAM;GACtF,IAAI,0BAA0B,KAAA,KAAa,OAAO,0BAA0B,UAC1E,MAAM,IAAI,MAAM;GAClB,IAAI,gBAAgB,KAAA,KAAa,OAAO,gBAAgB,UAAU,MAAM,IAAI,MAAM;GAClF,OAAO;IACL,IAAI;IACJ,UAAU;KACR,GAAI,WAAW,KAAA,IAAY,EAAE,OAAO,IAAI,CAAC;KACzC,GAAI,cAAc,KAAA,IAAY,EAAE,UAAU,IAAI,CAAC;KAC/C,GAAI,kBAAkB,KAAA,IAAY,EAAE,cAAc,IAAI,CAAC;KACvD,GAAI,0BAA0B,KAAA,IAAY,EAAE,sBAAsB,IAAI,CAAC;KACvE,GAAI,gBAAgB,KAAA,IAAY,EAAE,YAAY,IAAI,CAAC;IACrD;GACF;EACF,QAAQ;GACN,OAAO;IAAE,IAAI;IAAO,MAAM,KAAK;GAAa;EAC9C;CACF;;;;;CAMA,OAAO,QAA0F;EAC/F,OAAO,KAAK,aAAa;GACvB,MAAM,UAAU,KAAK,KAAK;GAC1B,IAAI,CAAC,QAAQ,IAAI,OAAO,KAAA;GACxB,MAAM,OAAO,OAAO,QAAQ,QAAQ;GACpC,KAAK,OAAO,IAAI;GAChB,OAAO;EACT,CAAC;CACH;;;;;;;CAQA,YAA4E;EAC1E,MAAM,UAAU,KAAK,KAAK;EAC1B,IAAI,CAAC,QAAQ,IAAI,OAAO,KAAA;EACxB,IAAI,QAAQ,SAAS,cAAc,KAAA,GACjC,OAAO;GAAE,IAAI,QAAQ,SAAS;GAAW,SAAS;EAAM;EAC1D,MAAM,YAAY,GAAG,KAAK,OAAO,GAAG,QAAQ,IAAI,GAAG,WAAW,EAAE;EAChE,MAAM,KAAK,WAAW;EACtB,IAAI;GACF,KAAK,YAAY;GACjB,cAAc,WAAW,GAAG,GAAG,KAAK,EAAE,MAAM,IAAM,CAAC;GACnD,SAAS,WAAW,KAAK,MAAM;GAC/B,OAAO;IAAE;IAAI,SAAS;GAAK;EAC7B,SAAS,OAAO;GACd,IAAK,MAAgC,SAAS,UAAU,OAAO,KAAA;GAC/D,MAAM,SAAS,KAAK,QAAQ;GAC5B,OAAO,SAAS;IAAE,IAAI;IAAQ,SAAS;GAAM,IAAI,KAAA;EACnD,UAAU;GACR,OAAO,WAAW,EAAE,OAAO,KAAK,CAAC;EACnC;CACF;;CAGA,UAAqC;EACnC,IAAI;EACJ,IAAI;GACF,OAAO,aAAa,KAAK,QAAQ,MAAM;EACzC,SAAS,OAAO;GACd,OAAQ,MAAgC,SAAS,WAAW,KAAA,IAAY;EAC1E;EACA,MAAM,KAAK,KAAK,KAAK;EACrB,OAAO,KAAK,KAAK,EAAE,IAAI,KAAK;CAC9B;;;;;CAMA,MAAM,UAAgD;EACpD,MAAM,EAAE,WAAW,KAAK,GAAG,SAAS;EACpC,KAAK,OAAO,IAAI;EAEhB,OAAO,KAAK,QAAQ,EAAE,OAAO,KAAK,CAAC;EACnC,IAAI,SAAS,WAAW,OAAO,OAAO,KAAK,WAAW,EAAE,OAAO,KAAK,CAAC;EACrE,OAAO;CACT;;CAGA,UAAmB;EACjB,OACE,KAAK,aAAa;GAChB,MAAM,UAAU,KAAK,KAAK;GAC1B,IAAI,CAAC,QAAQ,IAAI,OAAO;GACxB,MAAM,EAAE,WAAW,KAAK,GAAG,SAAS,QAAQ;GAC5C,KAAK,OAAO;IAAE,GAAG;IAAM,QAAQ;GAAM,CAAC;GACtC,OAAO,KAAK,QAAQ,EAAE,OAAO,KAAK,CAAC;GACnC,OAAO,KAAK,WAAW,EAAE,OAAO,KAAK,CAAC;GACtC,OAAO;EACT,CAAC,KAAK;CAEV;;CAGA,OAAO,OAA0B,KAAmB;EAClD,IAAI,MAAM,WAAW,GAAG;EACxB,KAAK,aAAa;GAChB,MAAM,OAAO,QAAQ,CAAC,GAAG,KAAK,QAAQ,GAAG,GAAG,KAAK,GAAG,GAAG;GACvD,KAAK,YAAY,IAAI;EACvB,CAAC;CACH;;CAGA,OAAO,KAAuB;EAC5B,OAAO,QAAQ,KAAK,QAAQ,GAAG,GAAG;CACpC;;;;;;CAOA,KAAK,MAAyB,KAAqB;EACjD,OACE,KAAK,aAAa;GAChB,MAAM,SAAS,QAAQ,KAAK,QAAQ,GAAG,GAAG;GAC1C,IAAI,QAAQ;GACZ,OAAO,QAAQ,KAAK,UAAU,OAAO,WAAW,KAAK,QAAQ,SAAS;GACtE,KAAK,YAAY,OAAO,MAAM,KAAK,CAAC;GACpC,OAAO;EACT,CAAC,KAAK;CAEV;CAEA,UAAoB;EAClB,IAAI;GACF,OAAO,aAAa,KAAK,WAAW,MAAM,CAAC,CACxC,MAAM,IAAI,CAAC,CACX,QAAQ,SAAS,KAAK,SAAS,CAAC;EACrC,QAAQ;GACN,OAAO,CAAC;EACV;CACF;CAEA,YAAY,OAAgC;EAC1C,IAAI,MAAM,WAAW,GAAG;GACtB,OAAO,KAAK,WAAW,EAAE,OAAO,KAAK,CAAC;GACtC;EACF;EACA,MAAM,YAAY,GAAG,KAAK,UAAU,GAAG,QAAQ,IAAI;EACnD,cAAc,WAAW,GAAG,MAAM,KAAK,IAAI,EAAE,KAAK,EAAE,MAAM,IAAM,CAAC;EACjE,WAAW,WAAW,KAAK,SAAS;CACtC;CAEA,OAAO,UAAmC;EACxC,MAAM,EAAE,WAAW,KAAK,GAAG,WAAW;EACtC,MAAM,YAAY,GAAG,KAAK,aAAa,GAAG,QAAQ,IAAI;EACtD,cAAc,WAAW,GAAG,KAAK,UAAU,QAAQ,MAAM,CAAC,EAAE,KAAK,EAAE,MAAM,IAAM,CAAC;EAChF,WAAW,WAAW,KAAK,YAAY;CACzC;CAEA,cAAoB;EAClB,UAAU,KAAK,KAAK;GAAE,WAAW;GAAM,MAAM;EAAM,CAAC;EACpD,IAAI;GACF,UAAU,KAAK,KAAK,GAAK;EAC3B,QAAQ,CAER;CACF;;;;;;;;;CAUA,OAAU,IAA4B;EACpC,IAAI;GACF,KAAK,YAAY;EACnB,QAAQ;GACN;EACF;EACA,MAAM,QAAQ,GAAG,QAAQ,IAAI,GAAG,WAAW;EAC3C,MAAM,WAAW,KAAK,IAAI,IAAI;EAC9B,SACE,IAAI;GACF,cAAc,KAAK,WAAW,OAAO;IAAE,MAAM;IAAM,MAAM;GAAM,CAAC;GAChE;EACF,SAAS,OAAO;GACd,IAAK,MAAgC,SAAS,UAAU,OAAO,KAAA;GAC/D,IAAI,KAAK,YAAY,GAAG;GACxB,IAAI,KAAK,IAAI,IAAI,UAAU,OAAO,KAAA;GAClC,QAAQ,KAAK,IAAI,WAAW,IAAI,kBAAkB,CAAC,CAAC,GAAG,GAAG,GAAG,EAAE;EACjE;EAEF,IAAI;GACF,OAAO,GAAG;EACZ,QAAQ;GACN;EACF,UAAU;GACR,KAAK,SAAS,KAAK;EACrB;CACF;;CAGA,SAAS,OAAqB;EAC5B,IAAI;GACF,IAAI,aAAa,KAAK,WAAW,MAAM,MAAM,OAAO,OAAO,KAAK,WAAW,EAAE,OAAO,KAAK,CAAC;EAC5F,QAAQ,CAER;CACF;;;;;;CAOA,cAAuB;EAErB,IAAI;EACJ,IAAI;EACJ,IAAI;EACJ,IAAI;GACF,SAAS,SAAS,KAAK,WAAW,GAAG;GACrC,MAAM,KAAK,IAAI,IAAI,UAAU,MAAM,CAAC,CAAC;GACrC,IAAI,OAAO,eAAe,OAAO;GACjC,OAAO,aAAa,QAAQ,MAAM;EACpC,QAAQ;GACN,OAAO;EACT,UAAU;GACR,IAAI,WAAW,KAAA,GAAW,UAAU,MAAM;EAC5C;EACA,IAAI,OAAO,gBAAgB,WAAW,IAAI,GAAG,OAAO;EACpD,MAAM,QAAQ,GAAG,KAAK,UAAU,GAAG,QAAQ,IAAI,GAAG,WAAW,EAAE;EAC/D,IAAI;GACF,WAAW,KAAK,WAAW,KAAK;EAClC,QAAQ;GACN,OAAO;EACT;EACA,IAAI;GACF,IAAI,aAAa,OAAO,MAAM,MAAM,MAAM,SAAS,OAAO,KAAK,SAAS;EAC1E,QAAQ,CAER,UAAU;GACR,OAAO,OAAO,EAAE,OAAO,KAAK,CAAC;EAC/B;EACA,OAAO;CACT;AACF;;AAGA,SAAS,WAAW,MAAuB;CACzC,MAAM,MAAM,OAAO,KAAK,MAAM,GAAG,CAAC,CAAC,EAAE;CACrC,IAAI,CAAC,OAAO,cAAc,GAAG,KAAK,OAAO,GAAG,OAAO;CACnD,IAAI;EACF,QAAQ,KAAK,KAAK,CAAC;EACnB,OAAO;CACT,SAAS,OAAO;EACd,OAAQ,MAAgC,SAAS;CACnD;AACF;;AAGA,SAAS,QAAQ,OAA0B,KAAuB;CAShE,OARc,MAAM,QAAQ,SAAS;EACnC,IAAI;GACF,MAAM,KAAK,KAAK,MAAO,KAAK,MAAM,IAAI,CAAC,CAAqB,MAAM,EAAE;GACpE,OAAO,OAAO,SAAS,EAAE,KAAK,MAAM,MAAA;EACtC,QAAQ;GACN,OAAO;EACT;CACF,CACW,CAAC,CAAC,MAAM,IAAiB;AACtC;;ACtWA,MAAa,oBAAoB;;AAEjC,SAAS,cAAc,KAA0B;CAC/C,MAAM,OAAO,OAAO,IAAI,gCAAgC;CACxD,OAAO,OAAO,UAAU,IAAI,KAAK,QAAQ,KAAK,OAAA,MAA2B,OAAO;AAClF;;AAGA,MAAM,qBAAqB;;AAG3B,SAAgB,KAAK,UAAkB,MAAgC;CACrE,OAAO,IAAI,SAAS,YAAY;EAC9B,MAAM,MAAM,IAAI,IAAI,QAAQ;EAE5B,MAAM,OADO,IAAI,aAAa,WAAWA,YAAeC,QAAAA,CAEtD,KACA;GACE,QAAQ;GACR,SAAS;IACP,gBAAgB;IAChB,cAAc,cAAc,WAAW;IACvC,kBAAkB,OAAO,WAAW,IAAI;GAC1C;GACA,SAAS;EACX,IACC,aAAa;GACZ,SAAS,OAAO;GAChB,SAAS,GAAG,aACV,SAAS,SAAS,cAAc,MAAM,QAAQ,SAAS,cAAc,KAAK,GAAG,CAC/E;GACA,SAAS,GAAG,eAAe,QAAQ,KAAK,CAAC;EAC3C,CACF;EACA,IAAI,GAAG,iBAAiB,IAAI,QAAQ,CAAC;EACrC,IAAI,GAAG,eAAe,QAAQ,KAAK,CAAC;EACpC,IAAI,IAAI,IAAI;CACd,CAAC;AACH;;;;;AAMA,eAAsB,MACpB,OACA,KACA,KACe;CACf,MAAM,WAAW,MAAM,KAAK;CAE5B,MAAM,WAAW,OAAO;EACtB;EACA,eAAe,cAAc;EAC7B,UAAU;EACV;EACA,UAAU;CACZ,CAAC;CACD,IAAI,CAAC,SAAS,WAAW,SAAS,aAAa,KAAA,GAAW;EAExD,IAAI,SAAS,WAAW,kBAAkB,SAAS,IAAI,MAAM,KAAK,MAAM,OAAO,IAAI,CAAC,GAAG,IAAI,CAAC;EAC5F;CACF;CAEA,KAAK,IAAI,QAAQ,GAAG,SAAS,KAAK,KAAK,mBAAA,GAAwB,GAAG,SAAS,GAAG;EAC5E,MAAM,QAAQ,MAAM,OAAO,IAAI,CAAC,CAAC,CAAC,MAAM,GAAA,GAAQ;EAChD,IAAI,MAAM,WAAW,GAAG;EACxB,MAAM,SAAoB,CAAC;EAC3B,KAAK,MAAM,QAAQ,OACjB,IAAI;GACF,MAAM,SAAS,eAAe,UAAU,KAAK,MAAM,IAAI,CAAC;GACxD,IAAI,OAAO,SAAS,OAAO,KAAK,OAAO,IAAI;EAC7C,QAAQ,CAER;EAGF,IAAI,EADS,OAAO,WAAW,KAAM,MAAM,KAAK,SAAS,UAAU,KAAK,UAAU,EAAE,OAAO,CAAC,CAAC,IAClF;EAGX,IAAI,MAAM,KAAK,OAAO,IAAI,CAAC,MAAM,GAAG;CACtC;AACF;;AAGA,SAAS,eAAiF;CACxF,MAAM,OAAO,YAAY;CACzB,OAAO,WAAW,IACd;EACE,OAAO,KAAK,MAAM,OAAO,aAAa,eAAe;EACrD,UAAU,CAAC,6BAA6B;CAC1C,IACA;EAAE,OAAO,KAAK,MAAM,QAAQ,aAAa,eAAe;EAAG,UAAU,CAAC;CAAE;AAC9E;;;;;;AAOA,SAAgB,aAAa,OAAuB,KAAkB,KAAmB;CACvF,IAAI,MAAM,OAAO,GAAG,CAAC,CAAC,WAAW,GAAG;CACpC,IAAI,MAAM;CACV,MAAM,QAAQ,YAAY;EACxB,MAAM,OAAO,QAAQ,gBAAgB,KAAA,IAAY,IAAI,KAAK,MAAM,QAAQ,WAAW;EACnF,IAAI,OAAO,SAAS,IAAI,KAAK,MAAM,OAAO,cAAc,GAAG,GAAG,OAAO;EACrE,MAAM;EACN,OAAO;GAAE,GAAG;GAAS,aAAa,IAAI,KAAK,GAAG,CAAC,CAAC,YAAY;EAAE;CAChE,CAAC;CACD,IAAI,CAAC,KAAK;CACV,MAAM,EAAE,OAAO,aAAa,aAAa;CACzC,IAAI;EACF,MAAM,QAAQ,MAAM,QAAQ,UAAU;GAAC,GAAG;GAAU;GAAO,MAAM;EAAG,GAAG;GACrE,UAAU;GACV,OAAO;GACP,KAAK;IAAE,GAAG,QAAQ;IAAK,GAAG;GAAI;EAChC,CAAC;EACD,MAAM,GAAG,eAAe,CAAC,CAAC;EAC1B,MAAM,MAAM;CACd,QAAQ,CAER;AACF"}
@@ -0,0 +1,230 @@
1
+ import { Diagnostic, MethodDefinition } from "@mostra/protocol";
2
+ //#region src/home.d.ts
3
+ /** The environment the CLI reads. */
4
+ type Environment = Readonly<Record<string, string | undefined>>;
5
+ //#endregion
6
+ //#region src/client.d.ts
7
+ /** The instance a command reached: the `instance` of the `--json` envelope (the attach target). */
8
+ interface InstanceSummary {
9
+ /** The home it owns. */
10
+ readonly home: string;
11
+ /** The control socket the CLI reached. */
12
+ readonly socket: string;
13
+ /** Its process ID. */
14
+ readonly pid: number;
15
+ /** `daemon`, or `host` for an instance a host spawned (telemetry sends nothing then, D140). */
16
+ readonly mode: "host" | "daemon";
17
+ /** The runtime's release. */
18
+ readonly version: string;
19
+ /** Whether this command started the daemon. */
20
+ readonly started: boolean;
21
+ }
22
+ /** An open session with the home's instance. */
23
+ interface InstanceConnection {
24
+ /** What the instance is. */
25
+ readonly summary: InstanceSummary;
26
+ /** `mostra/initialize`'s warnings. */
27
+ readonly warnings: readonly Diagnostic[];
28
+ /** Calls a host protocol method. */
29
+ call<P, R>(method: MethodDefinition<P, R>, params: P, timeoutMs?: number): Promise<R>;
30
+ /** Registers a handler for a notification (`artifact/progress`, `dev/notify`). */
31
+ onNotification(method: string, handler: (params: unknown) => void): void;
32
+ /** Resolves when the connection closes. */
33
+ readonly closed: Promise<void>;
34
+ /** Ends the session. */
35
+ close(): void;
36
+ }
37
+ //#endregion
38
+ //#region src/command.d.ts
39
+ /** A flag's value kind. Every flag is `--long`; booleans take no value. */
40
+ type FlagKind = "boolean" | "string" | "number" | "duration" | "list";
41
+ /** One flag of a command. */
42
+ interface FlagDef {
43
+ /** The flag's name, without the dashes. */
44
+ readonly name: string;
45
+ /** What it takes. */
46
+ readonly kind: FlagKind;
47
+ /** One line for help: what it does. */
48
+ readonly summary: string;
49
+ /** The placeholder in usage and help: `<path>`, `<d>` … */
50
+ readonly value?: string;
51
+ /** The allowed values, for a closed set. */
52
+ readonly choices?: readonly string[];
53
+ /** The default, as help shows it. */
54
+ readonly default?: string;
55
+ }
56
+ /** One positional argument. */
57
+ interface ArgDef {
58
+ /** The argument's name, as help shows it. */
59
+ readonly name: string;
60
+ /** One line for help: what it is. */
61
+ readonly summary: string;
62
+ /** Whether it may be left out. */
63
+ readonly optional?: boolean;
64
+ /** Whether it takes every remaining word (`<package…>`). */
65
+ readonly variadic?: boolean;
66
+ }
67
+ /** What the runner gives a command. */
68
+ interface CommandContext {
69
+ /** The positional arguments, after the command path, by name. */
70
+ readonly args: Readonly<Record<string, string | readonly string[] | undefined>>;
71
+ /** The command's flags, parsed by kind (durations in milliseconds, lists as arrays). */
72
+ readonly flags: Readonly<Record<string, string | number | boolean | readonly string[] | undefined>>;
73
+ /** The working directory relative paths resolve from (`--cwd`, else the process's). */
74
+ readonly cwd: string;
75
+ /** Whether `--json` was given. */
76
+ readonly json: boolean;
77
+ /** The request timeout from `--timeout`, in milliseconds. */
78
+ readonly timeoutMs: number | undefined;
79
+ /** Calls a host protocol method on the home's instance, starting its daemon when needed. */
80
+ call<P, R>(method: MethodDefinition<P, R>, params: P): Promise<R>;
81
+ /** Reads the whole of stdin, for commands that take a value there. */
82
+ readStdin(): Promise<string>;
83
+ /** Writes one line of progress or a log line to stderr (never stdout). */
84
+ log(line: string): void;
85
+ /** Emits one event of a streaming command (an NDJSON line in `--json` mode). */
86
+ emit(event: unknown, human?: string): void;
87
+ /** Adds a warning to the envelope (and stderr). */
88
+ warn(diagnostic: Diagnostic): void;
89
+ /** The environment the CLI runs in. */
90
+ readonly env: Readonly<Record<string, string | undefined>>;
91
+ /** Whether stdout and stdin are terminals. */
92
+ readonly tty: {
93
+ readonly stdin: boolean;
94
+ readonly stdout: boolean;
95
+ readonly stderr: boolean;
96
+ };
97
+ /** The resolved home and the CLI's own facts, for commands that report them. */
98
+ readonly home: string;
99
+ /** Records a telemetry event of this command (D140 item 8), when telemetry allows it. */
100
+ record(event: TelemetryEventInput): void;
101
+ /**
102
+ * The session with the home's instance, starting its daemon unless `autostart` is false; then
103
+ * `undefined` when none runs. `call` uses the same session.
104
+ */
105
+ connection(autostart?: boolean): Promise<InstanceConnection | undefined>;
106
+ /** Runs the runtime binary (`mostra-runtime …`) and returns its exit code and stdout. */
107
+ runtime(args: readonly string[], stdio?: "pipe" | "inherit"): Promise<{
108
+ code: number;
109
+ stdout: string;
110
+ }>;
111
+ /** The CLI's config directory (D140 item 5). */
112
+ readonly configDir: string;
113
+ /** Writes to stderr. */
114
+ readonly stderr: (text: string) => void;
115
+ /** Writes to stdout as the command runs (`host`, `dev`'s stream without `--json`). */
116
+ readonly stdout: (text: string) => void;
117
+ }
118
+ /** A command-specific telemetry event: its name and its own fields. */
119
+ interface TelemetryEventInput {
120
+ /** The event's name. */
121
+ readonly event: "verify.result" | "publish.done";
122
+ /** Its own fields; the schema keeps only the published ones. */
123
+ readonly fields: Readonly<Record<string, unknown>>;
124
+ }
125
+ /** What a command's run returns. */
126
+ interface CommandOutcome {
127
+ /** The `result` of the envelope. */
128
+ readonly result: unknown;
129
+ /** The text for people (stdout); default: the result as indented JSON. */
130
+ readonly human?: string;
131
+ /** The exit code when it isn't 0: a failing verdict (1), `still_waiting` (5), conflicts (6). */
132
+ readonly exit?: number;
133
+ /** The command wrote its own stdout (`host`): the runner prints no envelope and no text. */
134
+ readonly raw?: boolean;
135
+ }
136
+ /** One command. */
137
+ interface CommandDef {
138
+ /** Its words after `mostra`: `["daemon", "install"]`. */
139
+ readonly path: readonly string[];
140
+ /** One line: what it does. */
141
+ readonly summary: string;
142
+ /** Its positional arguments, in order. */
143
+ readonly args?: readonly ArgDef[];
144
+ /** Its own flags, besides the global ones. */
145
+ readonly flags?: readonly FlagDef[];
146
+ /** The result type `--json` puts in `result`, and its `mostra docs` topic. */
147
+ readonly output: {
148
+ readonly type: string;
149
+ readonly topic: string;
150
+ };
151
+ /** The exit codes it can end with, besides 2 and 3, which every command can. */
152
+ readonly exits: readonly number[];
153
+ /** One example invocation, without `mostra`. */
154
+ readonly example: string;
155
+ /** It reads stdin, so telemetry's question is never asked before it (D140 item 11). */
156
+ readonly readsStdin?: boolean | ((flags: CommandContext["flags"]) => boolean);
157
+ /** It never reaches an instance (`docs`, `telemetry`): no `instance` in its envelope. */
158
+ readonly local?: boolean;
159
+ /** It writes a stream of events before its result (NDJSON in `--json` mode). */
160
+ readonly streams?: boolean;
161
+ /** Telemetry and its question leave it alone (`telemetry`, `docs`, D140 item 11). */
162
+ readonly quiet?: boolean;
163
+ /** Does the command's work. */
164
+ run(context: CommandContext): Promise<CommandOutcome>;
165
+ }
166
+ //#endregion
167
+ //#region src/commands/index.d.ts
168
+ /** The commands, in help's order: the authoring loop first. */
169
+ export declare const COMMANDS: readonly CommandDef[];
170
+ //#endregion
171
+ //#region src/exit.d.ts
172
+ /** The exit codes of INTERFACES §9. */
173
+ export declare const EXIT: {
174
+ /** Success, or `verify` passed. */
175
+ readonly ok: 0;
176
+ /** Artifact failure: verify failed, publish refused, a request the artifact's state refused. */
177
+ readonly failure: 1;
178
+ /** Usage error. */
179
+ readonly usage: 2;
180
+ /** Environment error: no runtime, browser or socket; the home; an unsupported platform. */
181
+ readonly environment: 3;
182
+ /** Timeout: `--timeout` passed before the instance answered. */
183
+ readonly timeout: 4;
184
+ /** `wait` ended `still_waiting`: not an error, the status is in the result. */
185
+ readonly stillWaiting: 5;
186
+ /** Conflict: `once` already submitted, a compare-and-set mismatch, a reused request ID. */
187
+ readonly conflict: 6;
188
+ };
189
+ /** One of {@link EXIT}'s codes. */
190
+ type ExitCode = (typeof EXIT)[keyof typeof EXIT];
191
+ /** The exit code a diagnostic ends a command with. An unknown area is an environment error. */
192
+ export declare function exitCodeFor(code: string): ExitCode;
193
+ //#endregion
194
+ //#region src/help.d.ts
195
+ /** One command's help, for people. */
196
+ export declare function commandHelp(command: CommandDef): string;
197
+ /** One command's machine-readable definition (`--help --json`). */
198
+ export declare function commandDefinition(command: CommandDef): Record<string, unknown>;
199
+ /** `docs/reference/cli.md`: every command's help as one page. */
200
+ export declare function referenceMarkdown(commands: readonly CommandDef[]): string;
201
+ //#endregion
202
+ //#region src/run.d.ts
203
+ /** What the runner reads and writes; the binary passes the process's, tests their own. */
204
+ interface RunnerIo {
205
+ /** The words after mostra. */
206
+ readonly argv: readonly string[];
207
+ /** The environment. */
208
+ readonly env: Environment;
209
+ /** The working directory. */
210
+ readonly cwd: string;
211
+ /** Writes to stdout. */
212
+ readonly stdout: (text: string) => void;
213
+ /** Writes to stderr. */
214
+ readonly stderr: (text: string) => void;
215
+ /** Standard input. */
216
+ readonly stdin: NodeJS.ReadableStream;
217
+ /** Which of the streams are terminals. */
218
+ readonly tty: {
219
+ readonly stdin: boolean;
220
+ readonly stdout: boolean;
221
+ readonly stderr: boolean;
222
+ };
223
+ /** The platform; default process.platform. */
224
+ readonly platform?: string;
225
+ }
226
+ /** Runs one command line and returns its exit code. */
227
+ export declare function run(io: RunnerIo): Promise<number>;
228
+ //#endregion
229
+ export type { ArgDef, CommandContext, CommandDef, CommandOutcome, Environment, ExitCode, FlagDef, FlagKind, InstanceConnection, InstanceSummary, RunnerIo, TelemetryEventInput };
230
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/home.ts","../src/client.ts","../src/command.ts","../src/commands/index.ts","../src/exit.ts","../src/help.ts","../src/run.ts"],"mappings":";;;KAWY,cAAc,SAAS;;;;UCkBlB;;WAEN;;WAEA;;WAEA;;WAEA;;WAEA;;WAEA;;;UAIM;;WAEN,SAAS;;WAET,mBAAmB;;EAE5B,KAAK,GAAG,GAAG,QAAQ,iBAAiB,GAAG,IAAI,QAAQ,GAAG,qBAAqB,QAAQ;;EAEnF,eAAe,gBAAgB,UAAU;;WAEhC,QAAQ;;EAEjB;;;;;KC/CU;;UAGK;;WAEN;;WAEA,MAAM;;WAEN;;WAEA;;WAEA;;WAEA;;;UAIM;;WAEN;;WAEA;;WAEA;;WAEA;;;UAIM;;WAEN,MAAM,SAAS;;WAEf,OAAO,SACd;;WAGO;;WAEA;;WAEA;;EAET,KAAK,GAAG,GAAG,QAAQ,iBAAiB,GAAG,IAAI,QAAQ,IAAI,QAAQ;;EAE/D,aAAa;;EAEb,IAAI;;EAEJ,KAAK,gBAAgB;;EAErB,KAAK,YAAY;;WAER,KAAK,SAAS;;WAEd;aAAgB;aAAyB;aAA0B;;;WAEnE;;EAET,OAAO,OAAO;;;;;EAKd,WAAW,sBAAsB,QAAQ;;EAEzC,QACE,yBACA,6BACC;IAAU;IAAc;;;WAElB;;WAEA,SAAS;;WAET,SAAS;;;UAIH;;WAEN;;WAEA,QAAQ,SAAS;;;UAIX;;WAEN;;WAEA;;WAEA;;WAEA;;;UAIM;;WAEN;;WAEA;;WAEA,gBAAgB;;WAEhB,iBAAiB;;WAEjB;aAAmB;aAAuB;;;WAE1C;;WAEA;;WAEA,yBAAyB,OAAO;;WAEhC;;WAEA;;WAEA;;EAET,IAAI,SAAS,iBAAiB,QAAQ;;;;;qBChG3B,mBAAmB;;;;qBC9BnB;;WAEX;;WAEA;;WAEA;;WAEA;;WAEA;;WAEA;;WAEA;;;KAIU,mBAAmB,mBAAmB;;wBAwDlC,YAAY,eAAe;;;;wBCb3B,YAAY,SAAS;;wBAqDrB,kBAAkB,SAAS,aAAa;;wBA4BxC,kBAAkB,mBAAmB;;;;UChHpC;;WAEN;;WAEA,KAAK;;WAEL;;WAEA,SAAS;;WAET,SAAS;;WAET,OAAO,OAAO;;WAEd;aAAgB;aAAyB;aAA0B;;;WAEnE;;;wBA+DW,IAAI,IAAI,WAAW"}
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ import { a as COMMANDS, i as referenceMarkdown, n as commandDefinition, r as commandHelp, t as run } from "./run-BJaRnz-Q.js";
2
+ import { n as exitCodeFor, t as EXIT } from "./exit-CBIf8nAU.js";
3
+ export { COMMANDS, EXIT, commandDefinition, commandHelp, exitCodeFor, referenceMarkdown, run };