@krapht/ork 0.2.0 → 0.4.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 (68) hide show
  1. package/THIRD_PARTY_NOTICES.md +7 -0
  2. package/dist/__build_info__-CtSVVuZE.mjs +691 -0
  3. package/dist/bin.mjs +1654 -3532
  4. package/dist/clean-Dcr807be.mjs +88 -0
  5. package/dist/context-C_bksb0C.mjs +351 -0
  6. package/dist/find-DptmbhGd.mjs +47 -0
  7. package/dist/format-J-a21Yvn.mjs +393 -0
  8. package/dist/in-a-run-D1Qc5Mit.mjs +22 -0
  9. package/dist/limits-BDZCmGEu.mjs +44 -0
  10. package/dist/list-ZvY2OcLq.mjs +110 -0
  11. package/dist/mcp-CHVKKg3B.mjs +91 -0
  12. package/dist/output-CXSJO00X.mjs +900 -0
  13. package/dist/pause-BPSmg4yz.mjs +65 -0
  14. package/dist/relay-Do-aNbSD.mjs +122 -0
  15. package/dist/remove-AwaMMaHf.mjs +194 -0
  16. package/dist/report-DK4LGqwF.mjs +129 -0
  17. package/dist/send-DhGBkowy.mjs +163 -0
  18. package/dist/{src-DAsOYtoR.mjs → src-BHnxpJyH.mjs} +496 -193
  19. package/dist/src-CGqMati3.mjs +12505 -0
  20. package/dist/src-CpDKIulI.mjs +3118 -0
  21. package/dist/src-UWkk1sl9.mjs +1899 -0
  22. package/dist/ui/THIRD_PARTY_LICENSES.txt +631 -0
  23. package/dist/ui/assets/app-BgVDC_5l.js +67 -0
  24. package/dist/ui/assets/app-DVf68fj5.css +1 -0
  25. package/dist/ui/assets/geist-mono-cyrillic-400-normal-C51Di1Mf.woff2 +0 -0
  26. package/dist/ui/assets/geist-mono-cyrillic-400-normal-CPJFfJgk.woff +0 -0
  27. package/dist/ui/assets/geist-mono-cyrillic-500-normal-D2cXvDHF.woff2 +0 -0
  28. package/dist/ui/assets/geist-mono-cyrillic-500-normal-DfuvdVgn.woff +0 -0
  29. package/dist/ui/assets/geist-mono-cyrillic-ext-400-normal-CkxHOdCb.woff +0 -0
  30. package/dist/ui/assets/geist-mono-cyrillic-ext-400-normal-XB2BPmDK.woff2 +0 -0
  31. package/dist/ui/assets/geist-mono-cyrillic-ext-500-normal-BY1KTvO5.woff +0 -0
  32. package/dist/ui/assets/geist-mono-cyrillic-ext-500-normal-C7aYOB1D.woff2 +0 -0
  33. package/dist/ui/assets/geist-mono-latin-400-normal-B2yvC1Cq.woff +0 -0
  34. package/dist/ui/assets/geist-mono-latin-400-normal-DTRLJnHl.woff2 +0 -0
  35. package/dist/ui/assets/geist-mono-latin-500-normal-D-GG86Jb.woff +0 -0
  36. package/dist/ui/assets/geist-mono-latin-500-normal-YINYabwD.woff2 +0 -0
  37. package/dist/ui/assets/geist-mono-latin-ext-400-normal-CfzLURNc.woff +0 -0
  38. package/dist/ui/assets/geist-mono-latin-ext-400-normal-DI-rJ0UV.woff2 +0 -0
  39. package/dist/ui/assets/geist-mono-latin-ext-500-normal-Buglb9-a.woff2 +0 -0
  40. package/dist/ui/assets/geist-mono-latin-ext-500-normal-CrWEgvU_.woff +0 -0
  41. package/dist/ui/assets/geist-mono-symbols2-400-normal-BS9BGVxz.woff2 +0 -0
  42. package/dist/ui/assets/geist-mono-symbols2-400-normal-DjbiTEEA.woff +0 -0
  43. package/dist/ui/assets/geist-mono-symbols2-500-normal-CmnCPGa0.woff +0 -0
  44. package/dist/ui/assets/geist-mono-symbols2-500-normal-Q_ksOahU.woff2 +0 -0
  45. package/dist/ui/assets/geist-mono-vietnamese-400-normal-CKEaXIlx.woff2 +0 -0
  46. package/dist/ui/assets/geist-mono-vietnamese-400-normal-CZEPLOgu.woff +0 -0
  47. package/dist/ui/assets/geist-mono-vietnamese-500-normal-Bs29RrUZ.woff2 +0 -0
  48. package/dist/ui/assets/geist-mono-vietnamese-500-normal-Dx9-epDo.woff +0 -0
  49. package/dist/ui/assets/geist-sans-latin-400-normal-BOaIZNA2.woff +0 -0
  50. package/dist/ui/assets/geist-sans-latin-400-normal-gapTbOY8.woff2 +0 -0
  51. package/dist/ui/assets/geist-sans-latin-500-normal-CN2lyvyL.woff +0 -0
  52. package/dist/ui/assets/geist-sans-latin-500-normal-uokXdC-Q.woff2 +0 -0
  53. package/dist/ui/assets/geist-sans-latin-600-normal-CA1yjETN.woff +0 -0
  54. package/dist/ui/assets/geist-sans-latin-600-normal-DFOURf8L.woff2 +0 -0
  55. package/dist/ui/assets/index-BiO54L90.css +1 -0
  56. package/dist/ui/assets/index-MQNNgsyT.js +10 -0
  57. package/dist/ui/assets/theme-3xH5jxKs.js +1 -0
  58. package/dist/ui/favicon.ico +0 -0
  59. package/dist/ui/favicon.svg +4 -0
  60. package/dist/ui/index.html +30 -0
  61. package/dist/ui/ork-wordmark-small-on-light.svg +4 -0
  62. package/dist/ui/ork-wordmark-small.svg +4 -0
  63. package/dist/ui-DhutUMgQ.mjs +472 -0
  64. package/dist/usage-CT7Z-yjr.mjs +419 -0
  65. package/dist/watch-0Qr2UfCQ.mjs +394 -0
  66. package/dist/worktrees-DHl80k96.mjs +212 -0
  67. package/package.json +6 -4
  68. package/dist/src-CJ_zF0Zj.mjs +0 -5433
@@ -0,0 +1,4 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" shape-rendering="crispEdges">
2
+ <rect width="16" height="16" rx="3" fill="#0a0d0b" shape-rendering="geometricPrecision"/>
3
+ <rect x="3" y="4" width="1" height="1" fill="#3dff8a"/><rect x="4" y="4" width="1" height="1" fill="#3dff8a"/><rect x="4" y="5" width="1" height="1" fill="#3dff8a"/><rect x="5" y="5" width="1" height="1" fill="#3dff8a"/><rect x="5" y="6" width="1" height="1" fill="#3dff8a"/><rect x="6" y="6" width="1" height="1" fill="#3dff8a"/><rect x="6" y="7" width="1" height="1" fill="#3dff8a"/><rect x="7" y="7" width="1" height="1" fill="#3dff8a"/><rect x="10" y="7" width="1" height="1" fill="#ffb000"/><rect x="11" y="7" width="1" height="1" fill="#ffb000"/><rect x="12" y="7" width="1" height="1" fill="#ffb000"/><rect x="13" y="7" width="1" height="1" fill="#ffb000"/><rect x="7" y="8" width="1" height="1" fill="#3dff8a"/><rect x="8" y="8" width="1" height="1" fill="#3dff8a"/><rect x="10" y="8" width="1" height="1" fill="#ffb000"/><rect x="11" y="8" width="1" height="1" fill="#ffb000"/><rect x="12" y="8" width="1" height="1" fill="#ffb000"/><rect x="13" y="8" width="1" height="1" fill="#ffb000"/><rect x="6" y="9" width="1" height="1" fill="#3dff8a"/><rect x="7" y="9" width="1" height="1" fill="#3dff8a"/><rect x="10" y="9" width="1" height="1" fill="#ffb000"/><rect x="11" y="9" width="1" height="1" fill="#ffb000"/><rect x="12" y="9" width="1" height="1" fill="#ffb000"/><rect x="13" y="9" width="1" height="1" fill="#ffb000"/><rect x="5" y="10" width="1" height="1" fill="#3dff8a"/><rect x="6" y="10" width="1" height="1" fill="#3dff8a"/><rect x="10" y="10" width="1" height="1" fill="#ffb000"/><rect x="11" y="10" width="1" height="1" fill="#ffb000"/><rect x="12" y="10" width="1" height="1" fill="#ffb000"/><rect x="13" y="10" width="1" height="1" fill="#ffb000"/><rect x="4" y="11" width="1" height="1" fill="#3dff8a"/><rect x="5" y="11" width="1" height="1" fill="#3dff8a"/><rect x="10" y="11" width="1" height="1" fill="#ffb000"/><rect x="11" y="11" width="1" height="1" fill="#ffb000"/><rect x="12" y="11" width="1" height="1" fill="#ffb000"/><rect x="13" y="11" width="1" height="1" fill="#ffb000"/><rect x="3" y="12" width="1" height="1" fill="#3dff8a"/><rect x="4" y="12" width="1" height="1" fill="#3dff8a"/><rect x="10" y="12" width="1" height="1" fill="#ffb000"/><rect x="11" y="12" width="1" height="1" fill="#ffb000"/><rect x="12" y="12" width="1" height="1" fill="#ffb000"/><rect x="13" y="12" width="1" height="1" fill="#ffb000"/>
4
+ </svg>
@@ -0,0 +1,30 @@
1
+ <!doctype html>
2
+ <html
3
+ lang="en"
4
+ data-theme="dark"
5
+ data-contrast="standard"
6
+ >
7
+ <head>
8
+ <meta charset="utf-8" />
9
+ <meta
10
+ name="viewport"
11
+ content="width=device-width, initial-scale=1"
12
+ />
13
+ <meta
14
+ name="color-scheme"
15
+ content="dark light"
16
+ />
17
+ <title>Ork</title>
18
+ <link
19
+ rel="icon"
20
+ href="/favicon.svg"
21
+ type="image/svg+xml"
22
+ />
23
+ <script type="module" crossorigin src="/assets/index-MQNNgsyT.js"></script>
24
+ <link rel="modulepreload" crossorigin href="/assets/theme-3xH5jxKs.js">
25
+ <link rel="stylesheet" crossorigin href="/assets/index-BiO54L90.css">
26
+ </head>
27
+ <body>
28
+ <div id="root"></div>
29
+ </body>
30
+ </html>
@@ -0,0 +1,4 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 55 15" shape-rendering="crispEdges" role="img" aria-label="Ork">
2
+ <title>Ork</title>
3
+ <rect x="0" y="0" width="3" height="3" fill="#00753b"/><rect x="2" y="2" width="3" height="3" fill="#00753b"/><rect x="4" y="4" width="3" height="3" fill="#00753b"/><rect x="10" y="4" width="3" height="3" fill="#ffb000"/><rect x="12" y="4" width="3" height="3" fill="#ffb000"/><rect x="14" y="4" width="3" height="3" fill="#ffb000"/><rect x="6" y="6" width="3" height="3" fill="#00753b"/><rect x="10" y="6" width="3" height="3" fill="#ffb000"/><rect x="12" y="6" width="3" height="3" fill="#ffb000"/><rect x="14" y="6" width="3" height="3" fill="#ffb000"/><rect x="4" y="8" width="3" height="3" fill="#00753b"/><rect x="10" y="8" width="3" height="3" fill="#ffb000"/><rect x="12" y="8" width="3" height="3" fill="#ffb000"/><rect x="14" y="8" width="3" height="3" fill="#ffb000"/><rect x="2" y="10" width="3" height="3" fill="#00753b"/><rect x="10" y="10" width="3" height="3" fill="#ffb000"/><rect x="12" y="10" width="3" height="3" fill="#ffb000"/><rect x="14" y="10" width="3" height="3" fill="#ffb000"/><rect x="0" y="12" width="3" height="3" fill="#00753b"/><rect x="10" y="12" width="3" height="3" fill="#ffb000"/><rect x="12" y="12" width="3" height="3" fill="#ffb000"/><rect x="14" y="12" width="3" height="3" fill="#ffb000"/><rect x="22" y="4" width="3" height="3" fill="#00753b"/><rect x="24" y="4" width="3" height="3" fill="#00753b"/><rect x="26" y="4" width="3" height="3" fill="#00753b"/><rect x="20" y="6" width="3" height="3" fill="#00753b"/><rect x="28" y="6" width="3" height="3" fill="#00753b"/><rect x="20" y="8" width="3" height="3" fill="#00753b"/><rect x="28" y="8" width="3" height="3" fill="#00753b"/><rect x="20" y="10" width="3" height="3" fill="#00753b"/><rect x="28" y="10" width="3" height="3" fill="#00753b"/><rect x="22" y="12" width="3" height="3" fill="#00753b"/><rect x="24" y="12" width="3" height="3" fill="#00753b"/><rect x="26" y="12" width="3" height="3" fill="#00753b"/><rect x="32" y="4" width="3" height="3" fill="#00753b"/><rect x="36" y="4" width="3" height="3" fill="#00753b"/><rect x="38" y="4" width="3" height="3" fill="#00753b"/><rect x="32" y="6" width="3" height="3" fill="#00753b"/><rect x="34" y="6" width="3" height="3" fill="#00753b"/><rect x="40" y="6" width="3" height="3" fill="#00753b"/><rect x="32" y="8" width="3" height="3" fill="#00753b"/><rect x="32" y="10" width="3" height="3" fill="#00753b"/><rect x="32" y="12" width="3" height="3" fill="#00753b"/><rect x="44" y="0" width="3" height="3" fill="#00753b"/><rect x="44" y="2" width="3" height="3" fill="#00753b"/><rect x="44" y="4" width="3" height="3" fill="#00753b"/><rect x="50" y="4" width="3" height="3" fill="#00753b"/><rect x="44" y="6" width="3" height="3" fill="#00753b"/><rect x="48" y="6" width="3" height="3" fill="#00753b"/><rect x="44" y="8" width="3" height="3" fill="#00753b"/><rect x="46" y="8" width="3" height="3" fill="#00753b"/><rect x="44" y="10" width="3" height="3" fill="#00753b"/><rect x="48" y="10" width="3" height="3" fill="#00753b"/><rect x="44" y="12" width="3" height="3" fill="#00753b"/><rect x="50" y="12" width="3" height="3" fill="#00753b"/>
4
+ </svg>
@@ -0,0 +1,4 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 55 15" shape-rendering="crispEdges" role="img" aria-label="Ork">
2
+ <title>Ork</title>
3
+ <rect x="0" y="0" width="3" height="3" fill="#3dff8a"/><rect x="2" y="2" width="3" height="3" fill="#3dff8a"/><rect x="4" y="4" width="3" height="3" fill="#3dff8a"/><rect x="10" y="4" width="3" height="3" fill="#ffb000"/><rect x="12" y="4" width="3" height="3" fill="#ffb000"/><rect x="14" y="4" width="3" height="3" fill="#ffb000"/><rect x="6" y="6" width="3" height="3" fill="#3dff8a"/><rect x="10" y="6" width="3" height="3" fill="#ffb000"/><rect x="12" y="6" width="3" height="3" fill="#ffb000"/><rect x="14" y="6" width="3" height="3" fill="#ffb000"/><rect x="4" y="8" width="3" height="3" fill="#3dff8a"/><rect x="10" y="8" width="3" height="3" fill="#ffb000"/><rect x="12" y="8" width="3" height="3" fill="#ffb000"/><rect x="14" y="8" width="3" height="3" fill="#ffb000"/><rect x="2" y="10" width="3" height="3" fill="#3dff8a"/><rect x="10" y="10" width="3" height="3" fill="#ffb000"/><rect x="12" y="10" width="3" height="3" fill="#ffb000"/><rect x="14" y="10" width="3" height="3" fill="#ffb000"/><rect x="0" y="12" width="3" height="3" fill="#3dff8a"/><rect x="10" y="12" width="3" height="3" fill="#ffb000"/><rect x="12" y="12" width="3" height="3" fill="#ffb000"/><rect x="14" y="12" width="3" height="3" fill="#ffb000"/><rect x="22" y="4" width="3" height="3" fill="#3dff8a"/><rect x="24" y="4" width="3" height="3" fill="#3dff8a"/><rect x="26" y="4" width="3" height="3" fill="#3dff8a"/><rect x="20" y="6" width="3" height="3" fill="#3dff8a"/><rect x="28" y="6" width="3" height="3" fill="#3dff8a"/><rect x="20" y="8" width="3" height="3" fill="#3dff8a"/><rect x="28" y="8" width="3" height="3" fill="#3dff8a"/><rect x="20" y="10" width="3" height="3" fill="#3dff8a"/><rect x="28" y="10" width="3" height="3" fill="#3dff8a"/><rect x="22" y="12" width="3" height="3" fill="#3dff8a"/><rect x="24" y="12" width="3" height="3" fill="#3dff8a"/><rect x="26" y="12" width="3" height="3" fill="#3dff8a"/><rect x="32" y="4" width="3" height="3" fill="#3dff8a"/><rect x="36" y="4" width="3" height="3" fill="#3dff8a"/><rect x="38" y="4" width="3" height="3" fill="#3dff8a"/><rect x="32" y="6" width="3" height="3" fill="#3dff8a"/><rect x="34" y="6" width="3" height="3" fill="#3dff8a"/><rect x="40" y="6" width="3" height="3" fill="#3dff8a"/><rect x="32" y="8" width="3" height="3" fill="#3dff8a"/><rect x="32" y="10" width="3" height="3" fill="#3dff8a"/><rect x="32" y="12" width="3" height="3" fill="#3dff8a"/><rect x="44" y="0" width="3" height="3" fill="#3dff8a"/><rect x="44" y="2" width="3" height="3" fill="#3dff8a"/><rect x="44" y="4" width="3" height="3" fill="#3dff8a"/><rect x="50" y="4" width="3" height="3" fill="#3dff8a"/><rect x="44" y="6" width="3" height="3" fill="#3dff8a"/><rect x="48" y="6" width="3" height="3" fill="#3dff8a"/><rect x="44" y="8" width="3" height="3" fill="#3dff8a"/><rect x="46" y="8" width="3" height="3" fill="#3dff8a"/><rect x="44" y="10" width="3" height="3" fill="#3dff8a"/><rect x="48" y="10" width="3" height="3" fill="#3dff8a"/><rect x="44" y="12" width="3" height="3" fill="#3dff8a"/><rect x="50" y="12" width="3" height="3" fill="#3dff8a"/>
4
+ </svg>
@@ -0,0 +1,472 @@
1
+ import { n as ORK_VERSION, t as ORK_NAME } from "./__build_info__-CtSVVuZE.mjs";
2
+ import { a as write, i as style, o as writeErr } from "./output-CXSJO00X.mjs";
3
+ import { i as uiFilePath, l as newToken, n as startServer, r as runningServer, t as DEFAULT_PORT } from "./src-CpDKIulI.mjs";
4
+ import { a as mayShowSignIn } from "./in-a-run-D1Qc5Mit.mjs";
5
+ import { defineCommand } from "citty";
6
+ import path from "node:path";
7
+ import { constants } from "node:os";
8
+ import { spawn } from "node:child_process";
9
+ import { fileURLToPath } from "node:url";
10
+ // src/browser/open.ts
11
+ /**
12
+ * @file Opening a link in the user's browser, for `ork ui`. Each way is an
13
+ * opener in a registry, tried in order when it applies to this platform:
14
+ * `open` on macOS, `wslview` then `xdg-open` under WSL, `xdg-open` on Linux.
15
+ * Adding a platform is one more entry. When none starts, the caller prints
16
+ * the link instead.
17
+ */
18
+ const wsl = (env) => env["WSL_DISTRO_NAME"] !== void 0;
19
+ const OPENERS = [
20
+ {
21
+ id: "macos",
22
+ applies: (platform) => platform === "darwin",
23
+ program: "open"
24
+ },
25
+ {
26
+ id: "wslview",
27
+ applies: (platform, env) => platform === "linux" && wsl(env),
28
+ program: "wslview"
29
+ },
30
+ {
31
+ id: "xdg-open",
32
+ applies: (platform) => platform === "linux" || platform === "freebsd",
33
+ program: "xdg-open"
34
+ }
35
+ ];
36
+ /** Starts one program with the link; false when it cannot be started. */
37
+ const start = (program, url) => new Promise((resolve) => {
38
+ const child = spawn(program, [url], {
39
+ detached: true,
40
+ stdio: "ignore"
41
+ });
42
+ child.once("error", () => {
43
+ resolve(false);
44
+ });
45
+ child.once("spawn", () => {
46
+ child.unref();
47
+ resolve(true);
48
+ });
49
+ });
50
+ /** Opens `url` with the first opener that starts; false when none did. */
51
+ const openInBrowser = async (url, options = {}) => {
52
+ const platform = options.platform ?? process.platform;
53
+ const env = options.env ?? process.env;
54
+ for (const opener of options.openers ?? OPENERS) {
55
+ if (!opener.applies(platform, env)) continue;
56
+ if (await start(opener.program, url)) return true;
57
+ }
58
+ return false;
59
+ };
60
+ // src/browser/folder.ts
61
+ /**
62
+ * @file Where `ork ui` finds the built UI: `dist/ui` beside the published
63
+ * bundle, or, run from source, `packages/ui/dist` once it is built (`pnpm
64
+ * --filter @krapht/ork-ui build`). The CLI takes the UI's built files, never
65
+ * its source; without them the server shows its placeholder page.
66
+ */
67
+ /** The UI package's built folder in the workspace, or null outside it. */
68
+ const workspaceUi = () => {
69
+ try {
70
+ const manifest = fileURLToPath(import.meta.resolve("@krapht/ork-ui/package.json"));
71
+ return path.join(path.dirname(manifest), "dist");
72
+ } catch {
73
+ return null;
74
+ }
75
+ };
76
+ /** The folders that may hold the built UI, in the order they are tried. */
77
+ const uiFolders = () => {
78
+ const workspace = workspaceUi();
79
+ return [path.join(import.meta.dirname, "ui"), ...workspace === null ? [] : [workspace]];
80
+ };
81
+ // src/browser/ui-protocol.ts
82
+ const isRecord = (value) => typeof value === "object" && value !== null;
83
+ const TYPES = /* @__PURE__ */ new Set([
84
+ "ork-ui-hello",
85
+ "ork-ui-ready",
86
+ "ork-ui-failed",
87
+ "ork-ui-restart"
88
+ ]);
89
+ /** A message from the child, or `null` for anything else. */
90
+ const childMessage = (value) => {
91
+ if (!isRecord(value) || !TYPES.has(value["type"])) return null;
92
+ const { type, port, version, error } = value;
93
+ if (type === "ork-ui-ready") return typeof port === "number" && typeof version === "string" ? {
94
+ type,
95
+ port,
96
+ version
97
+ } : null;
98
+ if (type === "ork-ui-failed") return {
99
+ type,
100
+ error: typeof error === "string" ? error : "It failed."
101
+ };
102
+ return type === "ork-ui-hello" ? { type } : { type: "ork-ui-restart" };
103
+ };
104
+ /** The supervisor's `start`, or `null` for anything else. */
105
+ const startMessage = (value) => isRecord(value) && value["type"] === "ork-ui-start" && typeof value["port"] === "number" && typeof value["token"] === "string" ? {
106
+ type: "ork-ui-start",
107
+ port: value["port"],
108
+ token: value["token"]
109
+ } : null;
110
+ // src/browser/serve.ts
111
+ /**
112
+ * @file The server's side of `ork ui` (ADR-060): `ork ui --serve`, which only
113
+ * `ork ui` starts. It says hello over its IPC channel, takes the port and the
114
+ * token from the supervisor, serves, and says it's ready. A restart asked by
115
+ * the page tells the supervisor, then closes and exits, so the supervisor
116
+ * starts the installed `ork` in its place. Ctrl-C, SIGTERM, or losing the
117
+ * supervisor closes it too, so no server is left behind; an upgrade still
118
+ * running is waited for first.
119
+ */
120
+ const NOT_SUPERVISED = "`ork ui --serve` is how `ork ui` starts its server; run `ork ui`.";
121
+ const send = (message) => new Promise((resolve) => {
122
+ if (!process.connected || process.send === void 0) {
123
+ resolve();
124
+ return;
125
+ }
126
+ process.send(message, void 0, {}, () => {
127
+ resolve();
128
+ });
129
+ });
130
+ /** The supervisor's `start`, after saying hello. */
131
+ const started = () => new Promise((resolve) => {
132
+ const listen = (raw) => {
133
+ const message = startMessage(raw);
134
+ if (message === null) return;
135
+ process.off("message", listen);
136
+ resolve(message);
137
+ };
138
+ process.on("message", listen);
139
+ send({ type: "ork-ui-hello" });
140
+ });
141
+ const ignore = () => {};
142
+ /** What the terminal says while an upgrade holds the stop up. */
143
+ const WAITING_FOR_UPGRADE = "Waiting for the upgrade to finish… Press Ctrl-C again to stop now.";
144
+ /**
145
+ * Ends on Ctrl-C, SIGTERM, the supervisor's channel closing, or `end`. Each
146
+ * Ctrl-C reaches the child twice (from the terminal, and passed on by the
147
+ * supervisor), so while the supervisor is there, forcing a stop is its job: it
148
+ * kills the child on the second. Without it, a signal after the first stop
149
+ * exits at once.
150
+ */
151
+ const stopper = () => {
152
+ let end = ignore;
153
+ let stopped = false;
154
+ const promise = new Promise((resolve) => {
155
+ end = () => {
156
+ stopped = true;
157
+ resolve();
158
+ };
159
+ });
160
+ const onSignal = (signal) => {
161
+ if (stopped && !process.connected) process.exit(128 + constants.signals[signal]);
162
+ end();
163
+ };
164
+ process.on("SIGINT", onSignal);
165
+ process.on("SIGTERM", onSignal);
166
+ process.once("disconnect", () => {
167
+ end();
168
+ });
169
+ return {
170
+ promise,
171
+ end: () => {
172
+ end();
173
+ }
174
+ };
175
+ };
176
+ /**
177
+ * Waits for an upgrade still running before the server closes, saying so on the
178
+ * terminal: exiting mid-install could leave `ork` half-installed. The
179
+ * installer's output is read to its end meanwhile.
180
+ */
181
+ const upgradeFinished = async (server, say) => {
182
+ const running = server.upgrading();
183
+ if (running === null) return;
184
+ say(WAITING_FOR_UPGRADE);
185
+ await running.catch(ignore);
186
+ };
187
+ const serve = async (start, end) => {
188
+ try {
189
+ return await startServer({
190
+ version: ORK_VERSION,
191
+ port: start.port,
192
+ token: start.token,
193
+ ui: uiFolders(),
194
+ update: { name: ORK_NAME },
195
+ restart: () => {
196
+ send({ type: "ork-ui-restart" }).then(end);
197
+ }
198
+ });
199
+ } catch (error) {
200
+ await send({
201
+ type: "ork-ui-failed",
202
+ error: error instanceof Error ? error.message : String(error)
203
+ });
204
+ return null;
205
+ }
206
+ };
207
+ /** `ork ui --serve`: serves until stopped; exits when it closes. */
208
+ const serveForSupervisor = async () => {
209
+ if (process.send === void 0) {
210
+ writeErr(NOT_SUPERVISED);
211
+ process.exitCode = 1;
212
+ return;
213
+ }
214
+ const stop = stopper();
215
+ const start = await Promise.race([started(), stop.promise.then(() => null)]);
216
+ const server = start === null ? null : await serve(start, stop.end);
217
+ if (server !== null) {
218
+ await send({
219
+ type: "ork-ui-ready",
220
+ port: server.port,
221
+ version: ORK_VERSION
222
+ });
223
+ await stop.promise;
224
+ await upgradeFinished(server, writeErr);
225
+ await server.close();
226
+ }
227
+ process.exit(server === null ? 1 : 0);
228
+ };
229
+ // src/browser/supervise.ts
230
+ /**
231
+ * @file `ork ui`'s process model (ADR-060): the command in the terminal only
232
+ * supervises; its server runs in a child, the launcher the user ran
233
+ * (`process.argv[1]` as given, so after an upgrade it is the new install's)
234
+ * with `ui --serve`. The port and the token go over the child's IPC channel.
235
+ * When the page asks for a restart, the child says so and exits, and the
236
+ * supervisor starts the launcher again on the same port with the same token,
237
+ * so the open tab stays signed in. There is only ever one child; Ctrl-C (or
238
+ * SIGTERM) stops it and then the supervisor, which exits with its code. The
239
+ * child may wait for an upgrade to finish first; a second Ctrl-C (or SIGTERM)
240
+ * kills it and exits at once.
241
+ */
242
+ /**
243
+ * How long an exited child's channel may take to close before the supervisor
244
+ * decides without it; a child's channel closes with it, so this is only a
245
+ * backstop.
246
+ */
247
+ const CHANNEL_CLOSE_MS = 2e3;
248
+ /** A shell's exit code for a process a signal stopped. */
249
+ const signalled = (signal) => 128 + constants.signals[signal];
250
+ /** Supervises the server until it stops; resolves the exit code. */
251
+ const supervise = (options) => new Promise((resolve) => {
252
+ const { token, say, sayErr } = options;
253
+ let port = options.port;
254
+ let stopping = false;
255
+ let ended = false;
256
+ let restarts = 0;
257
+ let current = null;
258
+ const end = (code) => {
259
+ if (ended) return;
260
+ ended = true;
261
+ resolve(code);
262
+ };
263
+ const start = () => {
264
+ let restarting = false;
265
+ let ready = false;
266
+ let exited = null;
267
+ let disconnected = false;
268
+ let backstop;
269
+ const child = options.spawnServer();
270
+ current = child;
271
+ const decide = () => {
272
+ clearTimeout(backstop);
273
+ if (exited === null || ended || current !== child) return;
274
+ const { code } = exited;
275
+ if (restarting && !stopping) {
276
+ restarts += 1;
277
+ say("Restarting Ork's UI from the installed `ork`…");
278
+ start();
279
+ return;
280
+ }
281
+ if (!ready && restarts > 0) sayErr(`Ork's UI did not start again (exit ${String(code)}). Start it with: ork ui --port ${String(port)}`);
282
+ else if (ready && code !== 0 && !stopping) sayErr(`Ork's UI stopped: its server exited with ${String(code)}.`);
283
+ if (ready) say("Ork's UI stopped.");
284
+ end(code ?? (stopping ? 0 : 1));
285
+ };
286
+ child.onMessage((raw) => {
287
+ const message = childMessage(raw);
288
+ if (message?.type === "ork-ui-hello") child.send({
289
+ type: "ork-ui-start",
290
+ port,
291
+ token
292
+ });
293
+ else if (message?.type === "ork-ui-ready") {
294
+ ready = true;
295
+ ({port} = message);
296
+ options.onReady({
297
+ port,
298
+ version: message.version,
299
+ restarted: restarts > 0
300
+ });
301
+ } else if (message?.type === "ork-ui-failed") sayErr(message.error);
302
+ else if (message?.type === "ork-ui-restart") restarting = true;
303
+ });
304
+ child.onExit((code) => {
305
+ exited = { code };
306
+ if (disconnected) decide();
307
+ else backstop = setTimeout(decide, CHANNEL_CLOSE_MS);
308
+ });
309
+ child.onDisconnect(() => {
310
+ disconnected = true;
311
+ decide();
312
+ });
313
+ };
314
+ options.onStop((signal) => {
315
+ if (!stopping) {
316
+ stopping = true;
317
+ current?.kill(signal);
318
+ return;
319
+ }
320
+ current?.kill("SIGKILL");
321
+ say("Ork's UI stopped.");
322
+ end(signalled(signal));
323
+ });
324
+ start();
325
+ });
326
+ /**
327
+ * Starts `launcher ui --serve` with the same Node, on this terminal, with an
328
+ * IPC channel for the port and the token.
329
+ */
330
+ const spawnServerFrom = (launcher) => () => {
331
+ const child = spawn(process.execPath, [
332
+ ...process.execArgv,
333
+ launcher,
334
+ "ui",
335
+ "--serve"
336
+ ], { stdio: [
337
+ "inherit",
338
+ "inherit",
339
+ "inherit",
340
+ "ipc"
341
+ ] });
342
+ return {
343
+ send: (message) => {
344
+ if (child.connected) child.send(message);
345
+ },
346
+ kill: (signal) => {
347
+ child.kill(signal);
348
+ },
349
+ onMessage: (listener) => {
350
+ child.on("message", listener);
351
+ },
352
+ onExit: (listener) => {
353
+ let told = false;
354
+ const once = (code) => {
355
+ if (told) return;
356
+ told = true;
357
+ listener(code);
358
+ };
359
+ child.on("error", () => {
360
+ if (child.pid === void 0) once(1);
361
+ });
362
+ child.on("exit", once);
363
+ },
364
+ onDisconnect: (listener) => {
365
+ let told = false;
366
+ const once = () => {
367
+ if (told) return;
368
+ told = true;
369
+ listener();
370
+ };
371
+ child.on("error", () => {
372
+ if (child.pid === void 0) once();
373
+ });
374
+ child.on("disconnect", once);
375
+ }
376
+ };
377
+ };
378
+ /** Ctrl-C and SIGTERM, for {@link SuperviseOptions.onStop}. */
379
+ const onStopSignals = (stop) => {
380
+ for (const signal of ["SIGINT", "SIGTERM"]) process.on(signal, () => {
381
+ stop(signal);
382
+ });
383
+ };
384
+ // src/commands/ui.ts
385
+ /**
386
+ * @file `ork ui`: starts the local server (ADR-039) on loopback and opens it in
387
+ * the browser, signed in with the server's token; or, when one is already
388
+ * running (`~/.ork/ui.json`), opens that one. The command supervises the
389
+ * server in a child process, so the page can restart it onto a newly
390
+ * installed `ork` (ADR-060). The server runs nothing: runs it starts keep
391
+ * running when it stops, and the CLI works without it.
392
+ */
393
+ /** The link that signs the browser in; the server swaps it for a cookie. */
394
+ const signInLink = (port, token) => `http://127.0.0.1:${String(port)}/?token=${token}`;
395
+ /** `--port`: 0 to 65535 (0 picks a free port); `null` when invalid. */
396
+ const portArg = (value) => {
397
+ if (value === void 0) return DEFAULT_PORT;
398
+ const port = Number(value);
399
+ return Number.isInteger(port) && port >= 0 && port <= 65535 ? port : null;
400
+ };
401
+ /** Opens the link, or prints it when there is no browser to open. */
402
+ const show = async (link, open) => {
403
+ if (!mayShowSignIn()) {
404
+ writeErr("A program or an agent in a run does not get the sign-in link, which acts as you. Run `ork ui` in your own terminal.");
405
+ return;
406
+ }
407
+ if (!(open && process.stdout.isTTY && await openInBrowser(link))) write(`Open it with this link, which signs you in: ${link}`);
408
+ };
409
+ /** What `ork ui` says once its server serves, the first time or again. */
410
+ const ready = async ({ port, version, restarted }, token, open) => {
411
+ const url = `http://127.0.0.1:${String(port)}`;
412
+ if (restarted) {
413
+ write(`Ork's UI restarted: Ork ${version} at ${url}`);
414
+ return;
415
+ }
416
+ write(`Ork's UI is at ${url}`);
417
+ write(style("dim", "Runs keep running when it stops, and every ork command works without it. Ctrl-C stops it."));
418
+ await show(signInLink(port, token), open);
419
+ };
420
+ const ui = defineCommand({
421
+ meta: {
422
+ name: "ui",
423
+ description: "Start Ork's local UI and open it, or open the running one"
424
+ },
425
+ args: {
426
+ port: {
427
+ type: "string",
428
+ description: `The port to serve on (default ${String(DEFAULT_PORT)})`
429
+ },
430
+ open: {
431
+ type: "boolean",
432
+ description: "Open the browser (--no-open only prints the address)",
433
+ default: true
434
+ },
435
+ serve: {
436
+ type: "boolean",
437
+ description: "Only for `ork ui` itself: be the server it supervises",
438
+ default: false
439
+ }
440
+ },
441
+ run: async ({ args }) => {
442
+ const port = portArg(args.port);
443
+ if (port === null) {
444
+ writeErr(`--port must be a number from 0 to 65535, not \`${args.port ?? ""}\`.`);
445
+ process.exitCode = 1;
446
+ return;
447
+ }
448
+ if (args.serve) {
449
+ await serveForSupervisor();
450
+ return;
451
+ }
452
+ const running = runningServer(uiFilePath());
453
+ if (running !== null) {
454
+ write(`Ork's UI is already running at http://127.0.0.1:${String(running.port)} (process ${String(running.pid)}).`);
455
+ await show(signInLink(running.port, running.token), args.open);
456
+ return;
457
+ }
458
+ const token = newToken();
459
+ process.exitCode = await supervise({
460
+ port,
461
+ token,
462
+ spawnServer: spawnServerFrom(process.argv[1] ?? ""),
463
+ onReady: (answer) => ready(answer, token, args.open),
464
+ onStop: onStopSignals,
465
+ say: (line) => {
466
+ write(style("dim", line));
467
+ },
468
+ sayErr: writeErr
469
+ });
470
+ }
471
+ });
472
+ export { ui };