@miosa/cli 1.0.5 → 1.0.7

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 (66) hide show
  1. package/dist/bin/miosa.js +8 -0
  2. package/dist/bin/miosa.js.map +1 -1
  3. package/dist/commands/checkpoints.d.ts.map +1 -1
  4. package/dist/commands/checkpoints.js +166 -6
  5. package/dist/commands/checkpoints.js.map +1 -1
  6. package/dist/commands/computers.d.ts.map +1 -1
  7. package/dist/commands/computers.js +166 -5
  8. package/dist/commands/computers.js.map +1 -1
  9. package/dist/commands/dashboard.d.ts +13 -0
  10. package/dist/commands/dashboard.d.ts.map +1 -0
  11. package/dist/commands/dashboard.js +54 -0
  12. package/dist/commands/dashboard.js.map +1 -0
  13. package/dist/commands/deploy.d.ts.map +1 -1
  14. package/dist/commands/deploy.js +134 -2
  15. package/dist/commands/deploy.js.map +1 -1
  16. package/dist/commands/doctor.d.ts.map +1 -1
  17. package/dist/commands/doctor.js +69 -25
  18. package/dist/commands/doctor.js.map +1 -1
  19. package/dist/commands/domains.d.ts.map +1 -1
  20. package/dist/commands/domains.js +118 -9
  21. package/dist/commands/domains.js.map +1 -1
  22. package/dist/commands/enterprise-util.d.ts.map +1 -1
  23. package/dist/commands/enterprise-util.js +14 -10
  24. package/dist/commands/enterprise-util.js.map +1 -1
  25. package/dist/commands/login.d.ts.map +1 -1
  26. package/dist/commands/login.js +89 -21
  27. package/dist/commands/login.js.map +1 -1
  28. package/dist/commands/logout.d.ts.map +1 -1
  29. package/dist/commands/logout.js +27 -4
  30. package/dist/commands/logout.js.map +1 -1
  31. package/dist/commands/logs.d.ts.map +1 -1
  32. package/dist/commands/logs.js +59 -1
  33. package/dist/commands/logs.js.map +1 -1
  34. package/dist/commands/mcp.d.ts.map +1 -1
  35. package/dist/commands/mcp.js +228 -1380
  36. package/dist/commands/mcp.js.map +1 -1
  37. package/dist/commands/menu.d.ts +16 -0
  38. package/dist/commands/menu.d.ts.map +1 -0
  39. package/dist/commands/menu.js +113 -0
  40. package/dist/commands/menu.js.map +1 -0
  41. package/dist/commands/network-policy.d.ts.map +1 -1
  42. package/dist/commands/network-policy.js +120 -5
  43. package/dist/commands/network-policy.js.map +1 -1
  44. package/dist/commands/sandbox.d.ts.map +1 -1
  45. package/dist/commands/sandbox.js +157 -5
  46. package/dist/commands/sandbox.js.map +1 -1
  47. package/dist/commands/whoami.d.ts.map +1 -1
  48. package/dist/commands/whoami.js +76 -19
  49. package/dist/commands/whoami.js.map +1 -1
  50. package/dist/tui/dashboard.d.ts +35 -0
  51. package/dist/tui/dashboard.d.ts.map +1 -0
  52. package/dist/tui/dashboard.js +191 -0
  53. package/dist/tui/dashboard.js.map +1 -0
  54. package/dist/tui/logs.d.ts +26 -0
  55. package/dist/tui/logs.d.ts.map +1 -0
  56. package/dist/tui/logs.js +276 -0
  57. package/dist/tui/logs.js.map +1 -0
  58. package/dist/ui/picker.d.ts +41 -0
  59. package/dist/ui/picker.d.ts.map +1 -0
  60. package/dist/ui/picker.js +77 -0
  61. package/dist/ui/picker.js.map +1 -0
  62. package/dist/ui/render.d.ts +91 -0
  63. package/dist/ui/render.d.ts.map +1 -0
  64. package/dist/ui/render.js +138 -0
  65. package/dist/ui/render.js.map +1 -0
  66. package/package.json +4 -1
@@ -0,0 +1,276 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * `miosa logs <id> --follow` — live log tail TUI.
4
+ *
5
+ * React component (rendered by ink) that polls the MIOSA API every 2 seconds
6
+ * and streams the last 200 log lines into a scrollable pane with a header bar.
7
+ *
8
+ * How it differs from one-shot `miosa logs`:
9
+ * - Runs interactively in the terminal instead of dumping once and exiting.
10
+ * - Polls continuously, appending new lines to an in-memory ring buffer.
11
+ * - Renders a header bar (resource id, status, region, age, clock).
12
+ * - Colors stdout/stderr/error lines differently.
13
+ * - Responds to keyboard: q / Ctrl+C to quit, / to toggle filter mode.
14
+ *
15
+ * Mounted by `src/commands/logs.ts` when --follow / -f is passed and
16
+ * process.stdout.isTTY is true.
17
+ */
18
+ import { useEffect, useMemo, useRef, useState, } from "react";
19
+ import { Box, Text, useApp, useInput } from "ink";
20
+ import { MiosaClient } from "../client.js";
21
+ // ── Constants ─────────────────────────────────────────────────────────────────
22
+ const POLL_INTERVAL_MS = 2_000;
23
+ const MAX_LINES = 200;
24
+ // ── Helpers ───────────────────────────────────────────────────────────────────
25
+ let seqCounter = 0;
26
+ function nextSeq() {
27
+ return ++seqCounter;
28
+ }
29
+ function nowHHMMSS() {
30
+ return new Date().toLocaleTimeString("en-US", {
31
+ hour12: false,
32
+ hour: "2-digit",
33
+ minute: "2-digit",
34
+ second: "2-digit",
35
+ });
36
+ }
37
+ function ageLabel(isoOrNull) {
38
+ if (!isoOrNull)
39
+ return "—";
40
+ const ms = Date.now() - new Date(isoOrNull).getTime();
41
+ if (Number.isNaN(ms) || ms < 0)
42
+ return "—";
43
+ if (ms < 60_000)
44
+ return `${Math.floor(ms / 1_000)}s`;
45
+ if (ms < 3_600_000)
46
+ return `${Math.floor(ms / 60_000)}m`;
47
+ if (ms < 86_400_000)
48
+ return `${Math.floor(ms / 3_600_000)}h`;
49
+ return `${Math.floor(ms / 86_400_000)}d`;
50
+ }
51
+ function logEndpointFor(kind, id) {
52
+ switch (kind) {
53
+ case "computer":
54
+ return `/api/v1/computers/${encodeURIComponent(id)}/logs`;
55
+ case "sandbox":
56
+ return `/api/v1/sandboxes/${encodeURIComponent(id)}/logs`;
57
+ case "deployment":
58
+ return `/api/v1/deployments/${encodeURIComponent(id)}/logs`;
59
+ case "database":
60
+ return `/api/v1/databases/${encodeURIComponent(id)}/logs`;
61
+ }
62
+ }
63
+ function metaEndpointFor(kind, id) {
64
+ switch (kind) {
65
+ case "computer":
66
+ return `/api/v1/computers/${encodeURIComponent(id)}`;
67
+ case "sandbox":
68
+ return `/api/v1/sandboxes/${encodeURIComponent(id)}`;
69
+ case "deployment":
70
+ return `/api/v1/deployments/${encodeURIComponent(id)}`;
71
+ case "database":
72
+ return `/api/v1/databases/${encodeURIComponent(id)}`;
73
+ }
74
+ }
75
+ /**
76
+ * Parse an arbitrary API log payload (array of rows OR wrapped object with
77
+ * a "data"/"logs"/"lines" key) into typed LogLine entries. Handles both the
78
+ * plain-array format used by computer/sandbox endpoints and the JSON-row
79
+ * format seen in deployment responses.
80
+ */
81
+ function parsePayload(raw, existingCount) {
82
+ let rows = [];
83
+ if (Array.isArray(raw)) {
84
+ rows = raw;
85
+ }
86
+ else if (raw && typeof raw === "object") {
87
+ const rec = raw;
88
+ for (const key of ["logs", "data", "lines", "items"]) {
89
+ if (Array.isArray(rec[key])) {
90
+ rows = rec[key];
91
+ break;
92
+ }
93
+ }
94
+ }
95
+ return rows.slice(-MAX_LINES).map((row, idx) => {
96
+ if (typeof row === "string") {
97
+ return {
98
+ seq: nextSeq(),
99
+ timestamp: nowHHMMSS(),
100
+ stream: "stdout",
101
+ text: row,
102
+ };
103
+ }
104
+ if (row && typeof row === "object") {
105
+ const r = row;
106
+ const stream = (() => {
107
+ const s = String(r["stream"] ?? r["level"] ?? "stdout").toLowerCase();
108
+ if (s === "stderr" || s === "error" || s === "stdout")
109
+ return s;
110
+ return "unknown";
111
+ })();
112
+ const text = typeof r["line"] === "string"
113
+ ? r["line"]
114
+ : typeof r["message"] === "string"
115
+ ? r["message"]
116
+ : typeof r["text"] === "string"
117
+ ? r["text"]
118
+ : JSON.stringify(row);
119
+ const timestamp = typeof r["timestamp"] === "string"
120
+ ? r["timestamp"].slice(11, 19) // HH:MM:SS from ISO
121
+ : typeof r["inserted_at"] === "string"
122
+ ? r["inserted_at"].slice(11, 19)
123
+ : nowHHMMSS();
124
+ return { seq: nextSeq(), timestamp, stream, text };
125
+ }
126
+ return {
127
+ seq: nextSeq(),
128
+ timestamp: nowHHMMSS(),
129
+ stream: "unknown",
130
+ text: String(row),
131
+ };
132
+ });
133
+ }
134
+ function extractMeta(raw) {
135
+ if (!raw || typeof raw !== "object")
136
+ return {};
137
+ const r = raw;
138
+ return {
139
+ status: typeof r["status"] === "string"
140
+ ? r["status"]
141
+ : typeof r["state"] === "string"
142
+ ? String(r["state"])
143
+ : undefined,
144
+ region: typeof r["region"] === "string" ? r["region"] : undefined,
145
+ created_at: typeof r["inserted_at"] === "string"
146
+ ? r["inserted_at"]
147
+ : typeof r["created_at"] === "string"
148
+ ? r["created_at"]
149
+ : null,
150
+ };
151
+ }
152
+ function streamColor(stream) {
153
+ switch (stream) {
154
+ case "stderr":
155
+ return "yellow";
156
+ case "error":
157
+ return "red";
158
+ default:
159
+ return undefined;
160
+ }
161
+ }
162
+ const HeaderBar = ({ resourceId, resourceKind, meta, clock, fetchError, filterActive, filterText, }) => {
163
+ const statusColor = meta.status === "running" ||
164
+ meta.status === "active" ||
165
+ meta.status === "ready"
166
+ ? "green"
167
+ : meta.status === "provisioning" ||
168
+ meta.status === "building" ||
169
+ meta.status === "pending"
170
+ ? "yellow"
171
+ : meta.status === "error" || meta.status === "failed"
172
+ ? "red"
173
+ : "gray";
174
+ return (_jsxs(Box, { flexDirection: "column", marginBottom: 0, children: [_jsxs(Box, { justifyContent: "space-between", borderStyle: "single", borderColor: "cyan", paddingX: 1, children: [_jsxs(Text, { children: [_jsx(Text, { color: "cyan", bold: true, children: "MIOSA" }), _jsx(Text, { dimColor: true, children: " \u00B7 logs \u00B7 " }), _jsx(Text, { bold: true, children: resourceKind }), _jsx(Text, { dimColor: true, children: "/" }), _jsx(Text, { color: "cyan", children: resourceId })] }), _jsx(Text, { dimColor: true, children: clock })] }), _jsx(Box, { paddingX: 2, marginBottom: 0, children: _jsxs(Text, { children: [_jsx(Text, { dimColor: true, children: "status: " }), _jsx(Text, { color: statusColor, bold: true, children: meta.status || "—" }), " ", _jsx(Text, { dimColor: true, children: "region: " }), _jsx(Text, { children: meta.region || "—" }), " ", _jsx(Text, { dimColor: true, children: "age: " }), _jsx(Text, { children: ageLabel(meta.created_at) })] }) }), fetchError && (_jsxs(Box, { paddingX: 2, children: [_jsx(Text, { color: "red", children: "\u2717 " }), _jsx(Text, { color: "red", children: fetchError }), _jsx(Text, { dimColor: true, children: " (last known lines still shown)" })] })), filterActive && (_jsxs(Box, { paddingX: 2, children: [_jsx(Text, { dimColor: true, children: "filter: " }), _jsx(Text, { color: "cyan", children: filterText || "_" }), _jsx(Text, { dimColor: true, children: " (Esc to clear)" })] }))] }));
175
+ };
176
+ // ── Main component ────────────────────────────────────────────────────────────
177
+ export const LogsTUI = ({ resourceId, resourceKind, config, }) => {
178
+ const { exit } = useApp();
179
+ const [lines, setLines] = useState([]);
180
+ const [meta, setMeta] = useState({
181
+ status: "—",
182
+ region: "—",
183
+ created_at: null,
184
+ });
185
+ const [clock, setClock] = useState(nowHHMMSS());
186
+ const [fetchError, setFetchError] = useState(null);
187
+ const [filterActive, setFilterActive] = useState(false);
188
+ const [filterText, setFilterText] = useState("");
189
+ // Track last-seen seq so we only append genuinely new lines each poll.
190
+ const lastSeenCount = useRef(0);
191
+ const client = useMemo(() => new MiosaClient(config), [config]);
192
+ // ── Keyboard input ──────────────────────────────────────────────────────────
193
+ useInput((input, key) => {
194
+ if (input === "q" || (key.ctrl && input === "c")) {
195
+ exit();
196
+ return;
197
+ }
198
+ if (filterActive) {
199
+ if (key.escape) {
200
+ setFilterActive(false);
201
+ setFilterText("");
202
+ return;
203
+ }
204
+ if (key.backspace || key.delete) {
205
+ setFilterText((t) => t.slice(0, -1));
206
+ return;
207
+ }
208
+ if (!key.ctrl && !key.meta && input.length === 1) {
209
+ setFilterText((t) => t + input);
210
+ return;
211
+ }
212
+ return;
213
+ }
214
+ if (input === "/") {
215
+ setFilterActive(true);
216
+ }
217
+ });
218
+ // ── Clock tick (1 s) ────────────────────────────────────────────────────────
219
+ useEffect(() => {
220
+ const id = setInterval(() => setClock(nowHHMMSS()), 1_000);
221
+ return () => clearInterval(id);
222
+ }, []);
223
+ // ── Poll loop ───────────────────────────────────────────────────────────────
224
+ useEffect(() => {
225
+ let cancelled = false;
226
+ const poll = async () => {
227
+ // Fetch meta and logs in parallel; tolerate individual failures.
228
+ const [metaResult, logsResult] = await Promise.allSettled([
229
+ client.apiGet(metaEndpointFor(resourceKind, resourceId)),
230
+ client.apiGet(logEndpointFor(resourceKind, resourceId)),
231
+ ]);
232
+ if (cancelled)
233
+ return;
234
+ if (metaResult.status === "fulfilled") {
235
+ const m = extractMeta(metaResult.value);
236
+ setMeta((prev) => ({
237
+ status: m.status ?? prev.status,
238
+ region: m.region ?? prev.region,
239
+ created_at: m.created_at !== undefined ? m.created_at : prev.created_at,
240
+ }));
241
+ setFetchError(null);
242
+ }
243
+ if (logsResult.status === "fulfilled") {
244
+ const parsed = parsePayload(logsResult.value, lastSeenCount.current);
245
+ setLines((prev) => {
246
+ // Deduplicate by keeping only lines we haven't seen (by count).
247
+ // Because the API returns the full history each time, we keep only
248
+ // the tail beyond what we last displayed.
249
+ const combined = parsed.length >= prev.length ? parsed : prev;
250
+ const trimmed = combined.slice(-MAX_LINES);
251
+ lastSeenCount.current = trimmed.length;
252
+ return trimmed;
253
+ });
254
+ }
255
+ else if (logsResult.status === "rejected") {
256
+ const err = logsResult.reason;
257
+ setFetchError(err instanceof Error ? err.message : String(err));
258
+ }
259
+ };
260
+ void poll();
261
+ const id = setInterval(() => void poll(), POLL_INTERVAL_MS);
262
+ return () => {
263
+ cancelled = true;
264
+ clearInterval(id);
265
+ };
266
+ }, [client, resourceId, resourceKind]);
267
+ // ── Filtered view ───────────────────────────────────────────────────────────
268
+ const visibleLines = useMemo(() => {
269
+ if (!filterActive || filterText === "")
270
+ return lines;
271
+ const needle = filterText.toLowerCase();
272
+ return lines.filter((l) => l.text.toLowerCase().includes(needle));
273
+ }, [lines, filterActive, filterText]);
274
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(HeaderBar, { resourceId: resourceId, resourceKind: resourceKind, meta: meta, clock: clock, fetchError: fetchError, filterActive: filterActive, filterText: filterText }), _jsx(Box, { flexDirection: "column", paddingX: 1, children: visibleLines.length === 0 ? (_jsx(Text, { dimColor: true, children: " (waiting for logs\u2026)" })) : (visibleLines.map((line) => (_jsxs(Box, { children: [_jsxs(Text, { dimColor: true, children: [line.timestamp, " "] }), _jsx(Text, { color: streamColor(line.stream), bold: true, children: line.stream.padEnd(6) }), _jsx(Text, { children: " " }), _jsx(Text, { color: streamColor(line.stream), children: line.text })] }, line.seq)))) }), _jsx(Box, { paddingX: 1, marginTop: 1, children: _jsxs(Text, { dimColor: true, children: ["press ", _jsx(Text, { bold: true, children: "q" }), " to quit", " · ", _jsx(Text, { bold: true, children: "/" }), " to filter", " · ", "poll every ", POLL_INTERVAL_MS / 1_000, "s"] }) })] }));
275
+ };
276
+ //# sourceMappingURL=logs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logs.js","sourceRoot":"","sources":["../../src/tui/logs.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * MIOSA CLI resource picker.
3
+ *
4
+ * When a user runs a command that needs a sandbox / computer / deployment
5
+ * id and they didn't supply one, we present a searchable list instead of
6
+ * forcing them to grep `miosa computers list | awk …`.
7
+ *
8
+ * Built on the existing `inquirer` dep — no new packages. Dynamic import
9
+ * keeps the cold-start fast for commands that never prompt.
10
+ */
11
+ export interface PickItem<T = unknown> {
12
+ /** Stable id used as the prompt return value. */
13
+ id: string;
14
+ /** First line of the choice as rendered. */
15
+ label: string;
16
+ /** Dim second-line metadata: status, region, age, etc. */
17
+ hint?: string;
18
+ /** Original record, returned to the caller for convenience. */
19
+ data: T;
20
+ }
21
+ /**
22
+ * Show a list, return the selected item. Throws `UserError` when the
23
+ * list is empty so the caller can print a friendly "create one first"
24
+ * message instead of an inquirer crash.
25
+ *
26
+ * @param items Choices to render. Pre-filter to what's relevant for
27
+ * the command (e.g. `status === "active"` for `exec`).
28
+ * @param prompt The question shown above the list.
29
+ */
30
+ export declare function pickOne<T>(items: PickItem<T>[], prompt: string): Promise<PickItem<T>>;
31
+ /**
32
+ * Convenience helper for confirm dialogs. Returns the user's answer
33
+ * (defaulting to `false`) and prints "Cancelled." + exits the parent
34
+ * promise chain naturally when the user says no.
35
+ *
36
+ * Use this for any destructive action (`destroy`, `revoke`, `delete`).
37
+ */
38
+ export declare function confirm(message: string, opts?: {
39
+ default?: boolean;
40
+ }): Promise<boolean>;
41
+ //# sourceMappingURL=picker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"picker.d.ts","sourceRoot":"","sources":["../../src/ui/picker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAKH,MAAM,WAAW,QAAQ,CAAC,CAAC,GAAG,OAAO;IACnC,iDAAiD;IACjD,EAAE,EAAE,MAAM,CAAC;IACX,4CAA4C;IAC5C,KAAK,EAAE,MAAM,CAAC;IACd,0DAA0D;IAC1D,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,+DAA+D;IAC/D,IAAI,EAAE,CAAC,CAAC;CACT;AAED;;;;;;;;GAQG;AACH,wBAAsB,OAAO,CAAC,CAAC,EAC7B,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,EACpB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAwCtB;AAED;;;;;;GAMG;AACH,wBAAsB,OAAO,CAC3B,OAAO,EAAE,MAAM,EACf,IAAI,GAAE;IAAE,OAAO,CAAC,EAAE,OAAO,CAAA;CAAO,GAC/B,OAAO,CAAC,OAAO,CAAC,CAelB"}
@@ -0,0 +1,77 @@
1
+ /**
2
+ * MIOSA CLI resource picker.
3
+ *
4
+ * When a user runs a command that needs a sandbox / computer / deployment
5
+ * id and they didn't supply one, we present a searchable list instead of
6
+ * forcing them to grep `miosa computers list | awk …`.
7
+ *
8
+ * Built on the existing `inquirer` dep — no new packages. Dynamic import
9
+ * keeps the cold-start fast for commands that never prompt.
10
+ */
11
+ import chalk from "chalk";
12
+ import { UserError } from "../errors.js";
13
+ /**
14
+ * Show a list, return the selected item. Throws `UserError` when the
15
+ * list is empty so the caller can print a friendly "create one first"
16
+ * message instead of an inquirer crash.
17
+ *
18
+ * @param items Choices to render. Pre-filter to what's relevant for
19
+ * the command (e.g. `status === "active"` for `exec`).
20
+ * @param prompt The question shown above the list.
21
+ */
22
+ export async function pickOne(items, prompt) {
23
+ if (items.length === 0) {
24
+ throw new UserError("Nothing to pick from.", "Create a resource first, then re-run this command.");
25
+ }
26
+ // If we're not on a TTY (CI, pipe), refuse to prompt — the caller
27
+ // should have supplied the id explicitly. Surfacing the error here
28
+ // is friendlier than hanging on stdin.
29
+ if (!process.stdin.isTTY) {
30
+ throw new UserError("Cannot prompt for selection in a non-interactive shell.", "Pass the resource id explicitly.");
31
+ }
32
+ const { default: inquirer } = await import("inquirer");
33
+ const { picked } = await inquirer.prompt([
34
+ {
35
+ type: "list",
36
+ name: "picked",
37
+ message: prompt,
38
+ pageSize: Math.min(10, items.length),
39
+ choices: items.map((it) => ({
40
+ name: it.hint ? `${it.label} ${chalk.dim(it.hint)}` : it.label,
41
+ value: it.id,
42
+ short: it.label,
43
+ })),
44
+ },
45
+ ]);
46
+ const found = items.find((it) => it.id === picked);
47
+ if (!found) {
48
+ // Defensive — inquirer should always return one of the supplied
49
+ // values. If it doesn't, something is structurally wrong.
50
+ throw new Error(`Picker returned unknown id: ${picked}`);
51
+ }
52
+ return found;
53
+ }
54
+ /**
55
+ * Convenience helper for confirm dialogs. Returns the user's answer
56
+ * (defaulting to `false`) and prints "Cancelled." + exits the parent
57
+ * promise chain naturally when the user says no.
58
+ *
59
+ * Use this for any destructive action (`destroy`, `revoke`, `delete`).
60
+ */
61
+ export async function confirm(message, opts = {}) {
62
+ if (!process.stdin.isTTY) {
63
+ // No TTY → assume "no" so scripts don't accidentally proceed.
64
+ return false;
65
+ }
66
+ const { default: inquirer } = await import("inquirer");
67
+ const { ok } = await inquirer.prompt([
68
+ {
69
+ type: "confirm",
70
+ name: "ok",
71
+ message,
72
+ default: opts.default ?? false,
73
+ },
74
+ ]);
75
+ return ok;
76
+ }
77
+ //# sourceMappingURL=picker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"picker.js","sourceRoot":"","sources":["../../src/ui/picker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAazC;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,OAAO,CAC3B,KAAoB,EACpB,MAAc;IAEd,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,MAAM,IAAI,SAAS,CACjB,uBAAuB,EACvB,oDAAoD,CACrD,CAAC;IACJ,CAAC;IAED,kEAAkE;IAClE,mEAAmE;IACnE,uCAAuC;IACvC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;QACzB,MAAM,IAAI,SAAS,CACjB,yDAAyD,EACzD,kCAAkC,CACnC,CAAC;IACJ,CAAC;IAED,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,GAAG,MAAM,MAAM,CAAC,UAAU,CAAC,CAAC;IACvD,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,QAAQ,CAAC,MAAM,CAAqB;QAC3D;YACE,IAAI,EAAE,MAAM;YACZ,IAAI,EAAE,QAAQ;YACd,OAAO,EAAE,MAAM;YACf,QAAQ,EAAE,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,KAAK,CAAC,MAAM,CAAC;YACpC,OAAO,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;gBAC1B,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,KAAK,KAAK,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK;gBAC/D,KAAK,EAAE,EAAE,CAAC,EAAE;gBACZ,KAAK,EAAE,EAAE,CAAC,KAAK;aAChB,CAAC,CAAC;SACJ;KACF,CAAC,CAAC;IAEH,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,MAAM,CAAC,CAAC;IACnD,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,gEAAgE;QAChE,0DAA0D;QAC1D,MAAM,IAAI,KAAK,CAAC,+BAA+B,MAAM,EAAE,CAAC,CAAC;IAC3D,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,OAAO,CAC3B,OAAe,EACf,OAA8B,EAAE;IAEhC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;QACzB,8DAA8D;QAC9D,OAAO,KAAK,CAAC;IACf,CAAC;IACD,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,GAAG,MAAM,MAAM,CAAC,UAAU,CAAC,CAAC;IACvD,MAAM,EAAE,EAAE,EAAE,GAAG,MAAM,QAAQ,CAAC,MAAM,CAAkB;QACpD;YACE,IAAI,EAAE,SAAS;YACf,IAAI,EAAE,IAAI;YACV,OAAO;YACP,OAAO,EAAE,IAAI,CAAC,OAAO,IAAI,KAAK;SAC/B;KACF,CAAC,CAAC;IACH,OAAO,EAAE,CAAC;AACZ,CAAC"}
@@ -0,0 +1,91 @@
1
+ /**
2
+ * MIOSA CLI render vocabulary.
3
+ *
4
+ * Single source of truth for the visible look-and-feel: banner, status
5
+ * icons, key/value panels, hint blocks, error envelopes, and elapsed
6
+ * timing. Every command should compose its output from these primitives
7
+ * so the brand stays consistent.
8
+ *
9
+ * Design rules:
10
+ * - One accent color (cyan) for the brand.
11
+ * - Semantic colors for status (green/yellow/red/dim).
12
+ * - Never print colors when not a TTY (CI, pipes) — chalk handles
13
+ * this automatically via FORCE_COLOR=0 / NO_COLOR; we just call it.
14
+ * - Plain ASCII only. No unicode that breaks in cmd.exe / Putty.
15
+ * - Every "section" the user sees should fit inside one of these
16
+ * primitives; the command does NOT reach for raw `console.log` +
17
+ * ad-hoc chalk except for the body of a tool result.
18
+ */
19
+ export declare const icon: {
20
+ ok: string;
21
+ fail: string;
22
+ warn: string;
23
+ info: string;
24
+ bullet: string;
25
+ arrow: string;
26
+ pending: string;
27
+ };
28
+ /**
29
+ * Single-line MIOSA banner used at the top of lifecycle commands
30
+ * (login / logout / mcp install). Compact and unobtrusive.
31
+ *
32
+ * ▮▮▮ MIOSA ▮▮▮ v1.0.4
33
+ */
34
+ export declare function banner(opts?: {
35
+ version?: string;
36
+ subtitle?: string;
37
+ }): string;
38
+ export interface KVRow {
39
+ /** Left column label. Will be bolded. */
40
+ label: string;
41
+ /** Right column value. Pre-formatted; no further styling applied. */
42
+ value: string;
43
+ /** Optional leading icon (e.g. `icon.ok`). */
44
+ icon?: string;
45
+ }
46
+ /**
47
+ * Render a list of key/value rows with aligned columns. Used for the
48
+ * "✓ Authenticated ClinicIQ · Pro plan" output blocks.
49
+ */
50
+ export declare function kvPanel(rows: KVRow[]): string;
51
+ /**
52
+ * Suggest what the user can do next. Rendered as:
53
+ *
54
+ * → Next: miosa computers list
55
+ * miosa mcp install
56
+ */
57
+ export declare function hintBlock(label: string, commands: string[]): string;
58
+ export interface ErrorEnvelope {
59
+ title: string;
60
+ body?: string;
61
+ /** Suggested commands or doc URL. */
62
+ suggest?: string[];
63
+ /** Show "Run with --debug for full trace" hint. */
64
+ withDebugHint?: boolean;
65
+ }
66
+ /**
67
+ * Format an error in the brand's red envelope. Use this in the catch
68
+ * block of every command instead of bare `console.error`.
69
+ */
70
+ export declare function errorEnvelope(env: ErrorEnvelope): string;
71
+ /**
72
+ * Wrap an async block and append a "Took 1.2s" line on success.
73
+ * On error, re-throws so the caller can format its own envelope.
74
+ */
75
+ export declare function withElapsed<T>(fn: () => Promise<T>): Promise<{
76
+ result: T;
77
+ elapsed: string;
78
+ }>;
79
+ export declare function formatDuration(ms: number): string;
80
+ /** Insert a blank line. Cheap readability tool. */
81
+ export declare const blank: () => void;
82
+ /** Print the banner — convenience over `console.log(banner(...))`. */
83
+ export declare function printBanner(opts?: {
84
+ version?: string;
85
+ subtitle?: string;
86
+ }): void;
87
+ /** Print the elapsed line in dim text below a successful command. */
88
+ export declare function printElapsed(elapsed: string): void;
89
+ /** Print a section header (dim small-caps). */
90
+ export declare function sectionHeader(label: string): void;
91
+ //# sourceMappingURL=render.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../../src/ui/render.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAOH,eAAO,MAAM,IAAI;;;;;;;;CAQhB,CAAC;AAIF;;;;;GAKG;AACH,wBAAgB,MAAM,CACpB,IAAI,GAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAO,GACjD,MAAM,CAMR;AAID,MAAM,WAAW,KAAK;IACpB,yCAAyC;IACzC,KAAK,EAAE,MAAM,CAAC;IACd,qEAAqE;IACrE,KAAK,EAAE,MAAM,CAAC;IACd,8CAA8C;IAC9C,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;GAGG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,CAU7C;AAID;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,MAAM,CAQnE;AAID,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,qCAAqC;IACrC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,mDAAmD;IACnD,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAgBxD;AAID;;;GAGG;AACH,wBAAsB,WAAW,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC;IAClE,MAAM,EAAE,CAAC,CAAC;IACV,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC,CAKD;AAED,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAOjD;AAID,mDAAmD;AACnD,eAAO,MAAM,KAAK,QAAO,IAAqB,CAAC;AAE/C,sEAAsE;AACtE,wBAAgB,WAAW,CACzB,IAAI,GAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAO,GACjD,IAAI,CAIN;AAED,qEAAqE;AACrE,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAGlD;AAED,+CAA+C;AAC/C,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAGjD"}
@@ -0,0 +1,138 @@
1
+ /**
2
+ * MIOSA CLI render vocabulary.
3
+ *
4
+ * Single source of truth for the visible look-and-feel: banner, status
5
+ * icons, key/value panels, hint blocks, error envelopes, and elapsed
6
+ * timing. Every command should compose its output from these primitives
7
+ * so the brand stays consistent.
8
+ *
9
+ * Design rules:
10
+ * - One accent color (cyan) for the brand.
11
+ * - Semantic colors for status (green/yellow/red/dim).
12
+ * - Never print colors when not a TTY (CI, pipes) — chalk handles
13
+ * this automatically via FORCE_COLOR=0 / NO_COLOR; we just call it.
14
+ * - Plain ASCII only. No unicode that breaks in cmd.exe / Putty.
15
+ * - Every "section" the user sees should fit inside one of these
16
+ * primitives; the command does NOT reach for raw `console.log` +
17
+ * ad-hoc chalk except for the body of a tool result.
18
+ */
19
+ import chalk from "chalk";
20
+ // ── Status icons ──────────────────────────────────────────────────────────
21
+ // Plain ASCII so they render everywhere. Colors applied via chalk.
22
+ export const icon = {
23
+ ok: chalk.green("✓"),
24
+ fail: chalk.red("✗"),
25
+ warn: chalk.yellow("⚠"),
26
+ info: chalk.cyan("›"),
27
+ bullet: chalk.dim("•"),
28
+ arrow: chalk.dim("→"),
29
+ pending: chalk.yellow("·"),
30
+ };
31
+ // ── Brand banner ──────────────────────────────────────────────────────────
32
+ /**
33
+ * Single-line MIOSA banner used at the top of lifecycle commands
34
+ * (login / logout / mcp install). Compact and unobtrusive.
35
+ *
36
+ * ▮▮▮ MIOSA ▮▮▮ v1.0.4
37
+ */
38
+ export function banner(opts = {}) {
39
+ const bar = chalk.cyan("▮▮▮");
40
+ const wordmark = chalk.bold("MIOSA");
41
+ const version = opts.version ? chalk.dim(` v${opts.version}`) : "";
42
+ const subtitle = opts.subtitle ? chalk.dim(` ${opts.subtitle}`) : "";
43
+ return `${bar} ${wordmark} ${bar}${version}${subtitle}`;
44
+ }
45
+ /**
46
+ * Render a list of key/value rows with aligned columns. Used for the
47
+ * "✓ Authenticated ClinicIQ · Pro plan" output blocks.
48
+ */
49
+ export function kvPanel(rows) {
50
+ if (rows.length === 0)
51
+ return "";
52
+ const labelWidth = Math.max(...rows.map((r) => r.label.length));
53
+ return rows
54
+ .map((r) => {
55
+ const lead = r.icon ? `${r.icon} ` : " ";
56
+ const label = chalk.bold(r.label.padEnd(labelWidth));
57
+ return ` ${lead}${label} ${r.value}`;
58
+ })
59
+ .join("\n");
60
+ }
61
+ // ── Hint block ────────────────────────────────────────────────────────────
62
+ /**
63
+ * Suggest what the user can do next. Rendered as:
64
+ *
65
+ * → Next: miosa computers list
66
+ * miosa mcp install
67
+ */
68
+ export function hintBlock(label, commands) {
69
+ if (commands.length === 0)
70
+ return "";
71
+ const head = ` ${chalk.dim("→")} ${chalk.bold(label)}`;
72
+ const pad = " ".repeat(label.length + 5); // align with first command
73
+ const lines = commands.map((c, i) => i === 0 ? `${head} ${chalk.cyan(c)}` : ` ${pad}${chalk.cyan(c)}`);
74
+ return lines.join("\n");
75
+ }
76
+ /**
77
+ * Format an error in the brand's red envelope. Use this in the catch
78
+ * block of every command instead of bare `console.error`.
79
+ */
80
+ export function errorEnvelope(env) {
81
+ const lines = [];
82
+ lines.push(` ${chalk.red("✗")} ${chalk.bold.red(env.title)}`);
83
+ if (env.body)
84
+ lines.push(` ${chalk.dim(env.body)}`);
85
+ if (env.suggest && env.suggest.length > 0) {
86
+ lines.push("");
87
+ lines.push(` ${chalk.dim("→")} ${chalk.bold("Try")}`);
88
+ for (const s of env.suggest)
89
+ lines.push(` ${chalk.cyan(s)}`);
90
+ }
91
+ if (env.withDebugHint) {
92
+ lines.push("");
93
+ lines.push(` ${chalk.dim("Run with MIOSA_DEBUG=1 for the full stack trace.")}`);
94
+ }
95
+ return lines.join("\n");
96
+ }
97
+ // ── Elapsed timer ─────────────────────────────────────────────────────────
98
+ /**
99
+ * Wrap an async block and append a "Took 1.2s" line on success.
100
+ * On error, re-throws so the caller can format its own envelope.
101
+ */
102
+ export async function withElapsed(fn) {
103
+ const start = Date.now();
104
+ const result = await fn();
105
+ const ms = Date.now() - start;
106
+ return { result, elapsed: formatDuration(ms) };
107
+ }
108
+ export function formatDuration(ms) {
109
+ if (ms < 1000)
110
+ return `${ms}ms`;
111
+ if (ms < 10_000)
112
+ return `${(ms / 1000).toFixed(1)}s`;
113
+ if (ms < 60_000)
114
+ return `${Math.round(ms / 1000)}s`;
115
+ const m = Math.floor(ms / 60_000);
116
+ const s = Math.round((ms % 60_000) / 1000);
117
+ return `${m}m ${s}s`;
118
+ }
119
+ // ── Composition helpers ──────────────────────────────────────────────────
120
+ /** Insert a blank line. Cheap readability tool. */
121
+ export const blank = () => console.log();
122
+ /** Print the banner — convenience over `console.log(banner(...))`. */
123
+ export function printBanner(opts = {}) {
124
+ console.log();
125
+ console.log(` ${banner(opts)}`);
126
+ console.log();
127
+ }
128
+ /** Print the elapsed line in dim text below a successful command. */
129
+ export function printElapsed(elapsed) {
130
+ console.log();
131
+ console.log(` ${chalk.dim(`Took ${elapsed}`)}`);
132
+ }
133
+ /** Print a section header (dim small-caps). */
134
+ export function sectionHeader(label) {
135
+ console.log();
136
+ console.log(` ${chalk.dim.bold(label.toUpperCase())}`);
137
+ }
138
+ //# sourceMappingURL=render.js.map