@dutodev/cli 0.6.0 → 1.0.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.
@@ -1,13 +1,18 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
+ C,
4
+ TRUNCATED_NOTE,
5
+ bodyNote,
6
+ fit,
3
7
  formatBytes,
4
8
  formatDuration,
5
9
  formatTime,
6
10
  localLabel,
7
11
  normalizeName,
8
12
  statusColor,
13
+ tunnelColor,
9
14
  validateName
10
- } from "./chunk-26EQC6HB.js";
15
+ } from "./chunk-BPOXISIM.js";
11
16
 
12
17
  // src/ui/fullscreen.tsx
13
18
  import { render } from "ink";
@@ -18,30 +23,10 @@ import { Box as Box5, Text as Text6, useInput, useStdout } from "ink";
18
23
 
19
24
  // src/ui/Detail.tsx
20
25
  import { Box, Text } from "ink";
21
-
22
- // src/ui/theme.ts
23
- var C = {
24
- signal: "#2dd4bf",
25
- muted: "#8a8f8b",
26
- strong: "#f3f4f1",
27
- ok: "#4ade80",
28
- warn: "#facc15",
29
- err: "#f87171"
30
- };
31
- var TUNNEL_COLORS = ["#93c5fd", "#c4b5fd", "#facc15", "#4ade80", "#f9a8d4", "#fdba74", "#67e8f9"];
32
- function tunnelColor(index) {
33
- return TUNNEL_COLORS[index % TUNNEL_COLORS.length];
34
- }
35
- function fit(text, width) {
36
- if (width <= 0) return "";
37
- return text.length > width ? `${text.slice(0, Math.max(0, width - 1))}\u2026` : text.padEnd(width);
38
- }
39
-
40
- // src/ui/Detail.tsx
41
26
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
42
27
  var MAX_HEADERS = 12;
43
28
  var MAX_BODY_LINES = 8;
44
- function Section({ title, headers, body, truncated }) {
29
+ function Section({ title, headers, body, truncated, note: note2 }) {
45
30
  const entries = Object.entries(headers);
46
31
  const lines = body ? body.split("\n") : [];
47
32
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginTop: 1, children: [
@@ -55,13 +40,13 @@ function Section({ title, headers, body, truncated }) {
55
40
  v
56
41
  ] }, k)),
57
42
  entries.length > MAX_HEADERS && /* @__PURE__ */ jsxs(Text, { color: C.muted, children: [
58
- "\u2026 mais ",
43
+ "\u2026 ",
59
44
  entries.length - MAX_HEADERS,
60
- " cabe\xE7alhos"
45
+ " more headers"
61
46
  ] }),
62
- body === null ? /* @__PURE__ */ jsx(Text, { color: C.muted, children: "(corpo bin\xE1rio ou n\xE3o capturado)" }) : /* @__PURE__ */ jsxs(Fragment, { children: [
47
+ body === null ? /* @__PURE__ */ jsx(Text, { color: C.muted, children: note2 ?? "" }) : /* @__PURE__ */ jsxs(Fragment, { children: [
63
48
  lines.slice(0, MAX_BODY_LINES).map((l, i) => /* @__PURE__ */ jsx(Text, { wrap: "truncate-end", children: l }, i)),
64
- (lines.length > MAX_BODY_LINES || truncated) && /* @__PURE__ */ jsx(Text, { color: C.muted, children: truncated ? "\u2026 cortado em 64 KB" : "\u2026" })
49
+ (lines.length > MAX_BODY_LINES || truncated) && /* @__PURE__ */ jsx(Text, { color: C.muted, children: truncated ? TRUNCATED_NOTE : "\u2026" })
65
50
  ] })
66
51
  ] });
67
52
  }
@@ -85,15 +70,15 @@ function Detail({ record }) {
85
70
  ] })
86
71
  ] }),
87
72
  record.replayOf !== void 0 && /* @__PURE__ */ jsxs(Text, { color: C.muted, children: [
88
- "\u21BB reenvio de #",
73
+ "\u21BB replay of #",
89
74
  record.replayOf
90
75
  ] }),
91
76
  record.error && /* @__PURE__ */ jsx(Text, { color: C.err, children: record.error }),
92
- /* @__PURE__ */ jsx(Section, { title: "Requisi\xE7\xE3o", headers: record.reqHeaders, body: record.reqBody, truncated: record.reqTruncated }),
93
- /* @__PURE__ */ jsx(Section, { title: "Resposta", headers: record.resHeaders, body: record.resBody, truncated: record.resTruncated }),
77
+ /* @__PURE__ */ jsx(Section, { title: "Request", headers: record.reqHeaders, body: record.reqBody, truncated: record.reqTruncated, note: bodyNote(record, "req") }),
78
+ /* @__PURE__ */ jsx(Section, { title: "Response", headers: record.resHeaders, body: record.resBody, truncated: record.resTruncated, note: bodyNote(record, "res") }),
94
79
  /* @__PURE__ */ jsxs(Text, { color: C.muted, children: [
95
80
  "\n",
96
- "r reenvia \xB7 esc volta para a lista"
81
+ "r replay \xB7 esc back to list"
97
82
  ] })
98
83
  ] });
99
84
  }
@@ -116,16 +101,16 @@ import { Box as Box2, Text as Text3 } from "ink";
116
101
 
117
102
  // src/ui/qr.ts
118
103
  import qrcode from "qrcode-generator";
119
- var MARGEM = 2;
104
+ var MARGIN = 2;
120
105
  function qrLines(text) {
121
106
  const qr = qrcode(0, "L");
122
107
  qr.addData(text);
123
108
  qr.make();
124
109
  const n = qr.getModuleCount();
125
- const size = n + MARGEM * 2;
110
+ const size = n + MARGIN * 2;
126
111
  const dark = (r, c) => {
127
- const rr = r - MARGEM;
128
- const cc = c - MARGEM;
112
+ const rr = r - MARGIN;
113
+ const cc = c - MARGIN;
129
114
  return rr >= 0 && cc >= 0 && rr < n && cc < n && qr.isDark(rr, cc);
130
115
  };
131
116
  const lines = [];
@@ -152,7 +137,7 @@ function QrView({ tunnel, link, now }) {
152
137
  const left = link.expiresAt === null ? null : link.expiresAt - now;
153
138
  const expired = left !== null && Math.ceil(left / 1e3) <= 0;
154
139
  return /* @__PURE__ */ jsxs3(Box2, { flexDirection: "column", children: [
155
- /* @__PURE__ */ jsx3(Text3, { color: C.muted, children: "Abra no celular" }),
140
+ /* @__PURE__ */ jsx3(Text3, { color: C.muted, children: "Open on your phone" }),
156
141
  /* @__PURE__ */ jsxs3(Box2, { children: [
157
142
  /* @__PURE__ */ jsx3(Box2, { flexDirection: "column", marginRight: 3, children: lines.map((l, i) => /* @__PURE__ */ jsx3(Text3, { backgroundColor: "white", color: "black", children: l }, i)) }),
158
143
  /* @__PURE__ */ jsxs3(Box2, { flexDirection: "column", children: [
@@ -166,14 +151,14 @@ function QrView({ tunnel, link, now }) {
166
151
  ] }),
167
152
  left !== null && /* @__PURE__ */ jsxs3(Fragment2, { children: [
168
153
  /* @__PURE__ */ jsx3(Text3, { children: " " }),
169
- /* @__PURE__ */ jsx3(Text3, { children: "\u{1F512} Este t\xFAnel tem senha." }),
170
- /* @__PURE__ */ jsx3(Text3, { color: C.muted, children: "O QR entra direto, sem digitar a senha." }),
171
- /* @__PURE__ */ jsx3(Text3, { color: C.muted, children: "Vale por 5 minutos e s\xF3 para um aparelho." }),
154
+ /* @__PURE__ */ jsx3(Text3, { children: "\u{1F512} This tunnel has a password." }),
155
+ /* @__PURE__ */ jsx3(Text3, { color: C.muted, children: "The QR signs you in, no password to type." }),
156
+ /* @__PURE__ */ jsx3(Text3, { color: C.muted, children: "Valid for 5 minutes, one device only." }),
172
157
  /* @__PURE__ */ jsx3(Text3, { children: " " }),
173
- expired ? /* @__PURE__ */ jsx3(Text3, { color: C.warn, children: "Expirou. Aperte g para gerar outro." }) : /* @__PURE__ */ jsx3(Text3, { color: C.signal, children: `Expira em ${countdown(left)}` })
158
+ expired ? /* @__PURE__ */ jsx3(Text3, { color: C.warn, children: "Expired. Press g to generate another." }) : /* @__PURE__ */ jsx3(Text3, { color: C.signal, children: `Expires in ${countdown(left)}` })
174
159
  ] }),
175
160
  /* @__PURE__ */ jsx3(Text3, { children: " " }),
176
- /* @__PURE__ */ jsx3(Text3, { color: C.muted, children: left !== null ? "g gerar outro \xB7 esc voltar" : "esc voltar" })
161
+ /* @__PURE__ */ jsx3(Text3, { color: C.muted, children: left !== null ? "g new code \xB7 esc back" : "esc back" })
177
162
  ] })
178
163
  ] })
179
164
  ] });
@@ -186,7 +171,7 @@ function accessLabel(a) {
186
171
  if (!a) return null;
187
172
  if (a.kind === "user") return { text: a.user, color: C.muted };
188
173
  if (a.kind === "allow") return { text: a.reason, color: C.muted };
189
- return { text: "sem senha", color: C.warn };
174
+ return { text: "no password", color: C.warn };
190
175
  }
191
176
  function RequestList(props) {
192
177
  const { records, selected, focused, height, width } = props;
@@ -194,14 +179,14 @@ function RequestList(props) {
194
179
  const visible = records.slice(start, start + height);
195
180
  const accessW = Math.max(0, ...visible.map((r) => (accessLabel(r.access)?.text.length ?? -1) + 1));
196
181
  const pathW = Math.max(10, width - 54 - accessW);
197
- const title = props.filter ? `REQUISI\xC7\xD5ES \xB7 filtro: ${props.filter} ` : "REQUISI\xC7\xD5ES ";
182
+ const title = props.filter ? `REQUESTS \xB7 filter: ${props.filter} ` : "REQUESTS ";
198
183
  return /* @__PURE__ */ jsxs4(Box3, { flexDirection: "column", children: [
199
184
  /* @__PURE__ */ jsx4(Text4, { color: C.muted, children: title + "\u2500".repeat(Math.max(0, width - title.length)) }),
200
- records.length === 0 ? /* @__PURE__ */ jsx4(Text4, { color: C.muted, children: props.filter ? "Nenhuma requisi\xE7\xE3o com esse filtro." : "Nenhuma requisi\xE7\xE3o ainda. Elas aparecem aqui assim que chegarem." }) : visible.map((r, i) => {
185
+ records.length === 0 ? /* @__PURE__ */ jsx4(Text4, { color: C.muted, children: props.filter ? "No requests match that filter." : "No requests yet. They show up here as they arrive." }) : visible.map((r, i) => {
201
186
  const sel = focused && start + i === selected;
202
187
  const method = r.websocket ? "WS" : r.method;
203
188
  const status = r.status === null ? "\u2026" : String(r.status);
204
- const tail = r.websocket && r.status === 101 ? "aberto" : r.durationMs === null ? "" : formatDuration(r.durationMs);
189
+ const tail = r.websocket && r.status === 101 ? "open" : r.durationMs === null ? "" : formatDuration(r.durationMs);
205
190
  const acc = accessLabel(r.access);
206
191
  return /* @__PURE__ */ jsxs4(Text4, { inverse: sel, wrap: "truncate-end", children: [
207
192
  /* @__PURE__ */ jsx4(Text4, { color: C.muted, children: formatTime(r.startedAt) }),
@@ -227,10 +212,10 @@ function RequestList(props) {
227
212
  import { Box as Box4, Text as Text5 } from "ink";
228
213
  import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
229
214
  function note(t) {
230
- if (t.status === "failed") return { text: t.error ?? "falhou", color: C.err };
231
- if (t.status === "no-listener") return { text: `nada escutando na ${t.port}`, color: C.warn };
232
- if (t.status === "opening") return { text: "abrindo\u2026", color: C.muted };
233
- if (t.adHoc) return { text: "s\xF3 nesta sess\xE3o", color: C.muted };
215
+ if (t.status === "failed") return { text: t.error ?? "failed", color: C.err };
216
+ if (t.status === "no-listener") return { text: `nothing on port ${t.port}`, color: C.warn };
217
+ if (t.status === "opening") return { text: "opening\u2026", color: C.muted };
218
+ if (t.adHoc) return { text: "this session only", color: C.muted };
234
219
  return null;
235
220
  }
236
221
  function dot(t) {
@@ -248,7 +233,7 @@ function TunnelTable({ tunnels, selected, focused }) {
248
233
  return /* @__PURE__ */ jsxs5(Box4, { flexDirection: "column", children: [
249
234
  /* @__PURE__ */ jsxs5(Text5, { color: C.muted, children: [
250
235
  " ",
251
- fit("T\xDANEL", nameW),
236
+ fit("TUNNEL", nameW),
252
237
  " ",
253
238
  fit("URL", urlW),
254
239
  anyAuth ? " " : "",
@@ -257,7 +242,7 @@ function TunnelTable({ tunnels, selected, focused }) {
257
242
  " ",
258
243
  "REQ".padStart(4),
259
244
  " ",
260
- "ERROS".padStart(5)
245
+ "ERR".padStart(5)
261
246
  ] }),
262
247
  tunnels.map((t, i) => {
263
248
  const d = dot(t);
@@ -288,16 +273,17 @@ function TunnelTable({ tunnels, selected, focused }) {
288
273
  // src/ui/App.tsx
289
274
  import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
290
275
  var KEYS = [
291
- ["n", "novo t\xFAnel"],
292
- ["c", "copiar URL"],
293
- ["o", "abrir no navegador"],
294
- ["m", "QR no celular"],
295
- ["f", "filtrar"],
296
- ["\u21B5", "detalhes"],
297
- ["tab", "foco"],
298
- ["q", "sair"]
276
+ ["n", "new tunnel"],
277
+ ["c", "copy URL"],
278
+ ["o", "open in browser"],
279
+ ["i", "inspector"],
280
+ ["m", "QR for phone"],
281
+ ["f", "filter"],
282
+ ["\u21B5", "details"],
283
+ ["tab", "focus"],
284
+ ["q", "quit"]
299
285
  ];
300
- function App({ session, version, system, onExit }) {
286
+ function App({ session, version, system, onExit, inspector = null, inspectorError }) {
301
287
  const subState = useCallback((fn) => session.subscribe(fn), [session]);
302
288
  const subRecords = useCallback((fn) => session.records.subscribe(fn), [session]);
303
289
  const state = useSyncExternalStore(subState, () => session.getState());
@@ -310,7 +296,7 @@ function App({ session, version, system, onExit }) {
310
296
  const [focus, setFocus] = useState("requests");
311
297
  const [selReq, setSelReq] = useState(0);
312
298
  const [selTun, setSelTun] = useState(0);
313
- const [message, setMessage] = useState(null);
299
+ const [message, setMessage] = useState(inspectorError ? { text: `Inspector failed to start: ${inspectorError}`, tone: "warn" } : null);
314
300
  const [now, setNow] = useState(() => Date.now());
315
301
  const quitting = useRef(false);
316
302
  const records = useMemo(() => {
@@ -346,31 +332,36 @@ function App({ session, version, system, onExit }) {
346
332
  async function quit() {
347
333
  if (quitting.current) return;
348
334
  quitting.current = true;
349
- setMessage({ text: "Encerrando os t\xFAneis\u2026", tone: "muted" });
335
+ setMessage({ text: "Closing tunnels\u2026", tone: "muted" });
350
336
  await session.stop();
351
337
  onExit();
352
338
  }
339
+ async function openInspector() {
340
+ if (!inspector) return setMessage({ text: "Inspector is off (--no-inspect).", tone: "warn" });
341
+ const ok = await system.open(inspector.openUrl);
342
+ setMessage(ok ? { text: "Opening the inspector", tone: "signal" } : { text: "Could not open the browser.", tone: "warn" });
343
+ }
353
344
  async function act(kind) {
354
345
  const t = currentTunnel();
355
- if (!t?.url) return setMessage({ text: "Esse t\xFAnel ainda n\xE3o tem URL.", tone: "warn" });
346
+ if (!t?.url) return setMessage({ text: "That tunnel has no URL yet.", tone: "warn" });
356
347
  if (kind === "copy") {
357
348
  const ok = await system.copy(t.url);
358
- setMessage(ok ? { text: `URL copiada: ${t.url}`, tone: "signal" } : { text: "N\xE3o consegui copiar \u2014 selecione a URL na tabela", tone: "warn" });
349
+ setMessage(ok ? { text: `URL copied: ${t.url}`, tone: "signal" } : { text: "Could not copy \u2014 select the URL in the table", tone: "warn" });
359
350
  } else {
360
351
  const ok = await system.open(t.url);
361
- setMessage(ok ? { text: `Abrindo ${t.url}`, tone: "signal" } : { text: "N\xE3o consegui abrir o navegador", tone: "warn" });
352
+ setMessage(ok ? { text: `Opening ${t.url}`, tone: "signal" } : { text: "Could not open the browser", tone: "warn" });
362
353
  }
363
354
  }
364
355
  function showQr() {
365
356
  const t = currentTunnel();
366
357
  const link = t ? session.qrLink(t.key) : null;
367
- if (!t || !link) return setMessage({ text: "Esse t\xFAnel ainda n\xE3o tem URL.", tone: "warn" });
358
+ if (!t || !link) return setMessage({ text: "That tunnel has no URL yet.", tone: "warn" });
368
359
  setNow(Date.now());
369
360
  setMode({ kind: "qr", key: t.key, link });
370
361
  }
371
362
  function submitPort(value) {
372
363
  const port = Number(value.trim());
373
- if (!Number.isInteger(port) || port < 1 || port > 65535) return setMessage({ text: `Porta inv\xE1lida: ${value || "(vazia)"} (use 1\u201365535)`, tone: "err" });
364
+ if (!Number.isInteger(port) || port < 1 || port > 65535) return setMessage({ text: `Invalid port: ${value || "(empty)"} (use 1\u201365535)`, tone: "err" });
374
365
  setMode({ kind: "name", value: "", port });
375
366
  }
376
367
  function submitName(value, port) {
@@ -378,12 +369,12 @@ function App({ session, version, system, onExit }) {
378
369
  const name = raw ? normalizeName(raw) : null;
379
370
  if (name !== null) {
380
371
  const v = validateName(name);
381
- if (!v.ok) return setMessage({ text: v.reason === "reserved" ? `Nome reservado: "${raw}"` : "Nome inv\xE1lido: use s\xF3 letras min\xFAsculas e n\xFAmeros (sem h\xEDfen)", tone: "err" });
372
+ if (!v.ok) return setMessage({ text: v.reason === "reserved" ? `Reserved name: "${raw}"` : "Invalid name: use lowercase letters and numbers only (no hyphens)", tone: "err" });
382
373
  }
383
374
  setMode({ kind: "list" });
384
375
  void session.add({ name, port });
385
376
  setMessage(
386
- name ? { text: `Para manter, adicione ao duto.yml: ${name}: ${port}`, tone: "muted" } : { text: `Abrindo localhost:${port} s\xF3 nesta sess\xE3o`, tone: "muted" }
377
+ name ? { text: `To keep it, add to duto.yml: ${name}: ${port}`, tone: "muted" } : { text: `Opening localhost:${port} for this session only`, tone: "muted" }
387
378
  );
388
379
  }
389
380
  useInput((input, key) => {
@@ -436,13 +427,14 @@ function App({ session, version, system, onExit }) {
436
427
  if (key.escape && filter) return setFilter("");
437
428
  if (input === "c") return void act("copy");
438
429
  if (input === "o") return void act("open");
430
+ if (input === "i") return void openInspector();
439
431
  if (input === "m") return showQr();
440
432
  if (input === "f") return setMode({ kind: "filter", value: filter });
441
433
  if (input === "n") return setMode({ kind: "port", value: "" });
442
434
  });
443
435
  const live = state.tunnels.filter((t) => t.status === "live").length;
444
- const conn = state.connection === "online" ? { text: "conectado \u25CF", color: C.signal } : state.connection === "connecting" ? { text: "conectando\u2026", color: C.muted } : { text: "reconectando\u2026", color: C.warn };
445
- const listHeight = Math.max(3, rows - state.tunnels.length - 9);
436
+ const conn = state.connection === "online" ? { text: "connected \u25CF", color: C.signal } : state.connection === "connecting" ? { text: "connecting\u2026", color: C.muted } : { text: "reconnecting\u2026", color: C.warn };
437
+ const listHeight = Math.max(3, rows - state.tunnels.length - (inspector ? 10 : 9));
446
438
  const detailRecord = mode.kind === "detail" ? all.find((r) => r.id === mode.id) : void 0;
447
439
  const qrTunnel = mode.kind === "qr" ? state.tunnels.find((t) => t.key === mode.key) : void 0;
448
440
  return /* @__PURE__ */ jsxs6(Box5, { flexDirection: "column", width, children: [
@@ -462,19 +454,20 @@ function App({ session, version, system, onExit }) {
462
454
  live,
463
455
  "/",
464
456
  state.tunnels.length,
465
- " no ar"
457
+ " live"
466
458
  ] })
467
459
  ] }),
468
460
  /* @__PURE__ */ jsx6(Text6, { color: conn.color, children: conn.text })
469
461
  ] }),
470
462
  /* @__PURE__ */ jsx6(Text6, { children: " " }),
471
463
  /* @__PURE__ */ jsx6(TunnelTable, { tunnels: state.tunnels, selected: selTun, focused: focus === "tunnels" }),
464
+ inspector && /* @__PURE__ */ jsx6(Text6, { color: C.muted, children: `inspector ${inspector.url}` }),
472
465
  /* @__PURE__ */ jsx6(Text6, { children: " " }),
473
466
  mode.kind === "detail" && detailRecord ? /* @__PURE__ */ jsx6(Detail, { record: detailRecord }) : mode.kind === "qr" && qrTunnel ? /* @__PURE__ */ jsx6(QrView, { tunnel: qrTunnel, link: mode.link, now }) : /* @__PURE__ */ jsx6(RequestList, { records, tunnelIndex, selected: selReq, focused: focus === "requests", height: listHeight, width, filter }),
474
467
  /* @__PURE__ */ jsx6(Text6, { children: " " }),
475
- mode.kind === "filter" && /* @__PURE__ */ jsx6(Prompt, { label: "Filtrar (t\xFAnel, m\xE9todo, caminho ou status):", value: mode.value, hint: "\u21B5 aplica \xB7 esc cancela" }),
476
- mode.kind === "port" && /* @__PURE__ */ jsx6(Prompt, { label: "Novo t\xFAnel \u2014 porta local:", value: mode.value, hint: "\u21B5 continua \xB7 esc cancela" }),
477
- mode.kind === "name" && /* @__PURE__ */ jsx6(Prompt, { label: `Nome para localhost:${mode.port} (opcional):`, value: mode.value, hint: "\u21B5 abre \xB7 esc cancela" }),
468
+ mode.kind === "filter" && /* @__PURE__ */ jsx6(Prompt, { label: "Filter (tunnel, method, path or status):", value: mode.value, hint: "\u21B5 apply \xB7 esc cancel" }),
469
+ mode.kind === "port" && /* @__PURE__ */ jsx6(Prompt, { label: "New tunnel \u2014 local port:", value: mode.value, hint: "\u21B5 continue \xB7 esc cancel" }),
470
+ mode.kind === "name" && /* @__PURE__ */ jsx6(Prompt, { label: `Name for localhost:${mode.port} (optional):`, value: mode.value, hint: "\u21B5 open \xB7 esc cancel" }),
478
471
  mode.kind !== "filter" && mode.kind !== "port" && mode.kind !== "name" && /* @__PURE__ */ jsx6(Text6, { color: message ? C[message.tone] : C.muted, children: message?.text ?? " " }),
479
472
  /* @__PURE__ */ jsx6(Text6, { wrap: "truncate-end", children: KEYS.map(([k, label]) => /* @__PURE__ */ jsxs6(Text6, { children: [
480
473
  /* @__PURE__ */ jsx6(Text6, { inverse: true, color: C.signal, children: ` ${k} ` }),
@@ -554,7 +547,7 @@ var realSystem = {
554
547
 
555
548
  // src/ui/fullscreen.tsx
556
549
  import { jsx as jsx7 } from "react/jsx-runtime";
557
- function runFullscreen(session, specs, version) {
550
+ function runFullscreen(session, specs, version, inspector, inspectorError) {
558
551
  return withFullscreen(
559
552
  process.stdout,
560
553
  (signal) => new Promise((resolve) => {
@@ -565,7 +558,7 @@ function runFullscreen(session, specs, version) {
565
558
  app.unmount();
566
559
  resolve(code);
567
560
  };
568
- const app = render(/* @__PURE__ */ jsx7(App, { session, version, system: realSystem, onExit: () => finish(0) }), { exitOnCtrlC: false });
561
+ const app = render(/* @__PURE__ */ jsx7(App, { session, version, system: realSystem, inspector, inspectorError, onExit: () => finish(0) }), { exitOnCtrlC: false });
569
562
  signal.addEventListener("abort", () => void session.stop().then(() => finish(0)), { once: true });
570
563
  session.start(specs).catch(() => void 0);
571
564
  })