unified-tvdevelopment-cli 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.
package/dist/cli.mjs ADDED
@@ -0,0 +1,2376 @@
1
+ #!/usr/bin/env node
2
+ var __defProp = Object.defineProperty;
3
+ var __export = (target, all) => {
4
+ for (var name in all)
5
+ __defProp(target, name, { get: all[name], enumerable: true });
6
+ };
7
+
8
+ // src/index.js
9
+ import React19 from "react";
10
+ import { render } from "ink";
11
+
12
+ // src/App.js
13
+ import React18, { useState as useState15 } from "react";
14
+ import { Box as Box18, useInput as useInput14, useApp as useApp2 } from "ink";
15
+
16
+ // src/components/Header.js
17
+ import React from "react";
18
+ import { Box, Text } from "ink";
19
+
20
+ // src/theme.js
21
+ import chalk from "chalk";
22
+ var PLATFORM_IDS = ["webos", "tizen", "amazon", "android"];
23
+ var PLATFORM_META = {
24
+ webos: { label: "LG webOS", color: "#00C3E3", icon: "\u2B21", hint: "ares-cli" },
25
+ tizen: { label: "Samsung Tizen", color: "#4C7BF4", icon: "\u25C8", hint: "tizen + sdb" },
26
+ amazon: { label: "Amazon Fire TV", color: "#FF9900", icon: "\u25C6", hint: "adb + inputd-cli" },
27
+ android: { label: "Android TV", color: "#3DDC84", icon: "\u25C9", hint: "adb + gradle" }
28
+ };
29
+ var COLORS = {
30
+ primary: "#818CF8",
31
+ accent: "#F59E0B",
32
+ success: "#22C55E",
33
+ error: "#EF4444",
34
+ warning: "#F97316",
35
+ muted: "#64748B",
36
+ text: "#E2E8F0",
37
+ dim: "#475569",
38
+ highlight: "#C084FC"
39
+ };
40
+ var c = {
41
+ primary: chalk.hex(COLORS.primary),
42
+ accent: chalk.hex(COLORS.accent),
43
+ success: chalk.hex(COLORS.success),
44
+ error: chalk.hex(COLORS.error),
45
+ warning: chalk.hex(COLORS.warning),
46
+ muted: chalk.hex(COLORS.muted),
47
+ text: chalk.hex(COLORS.text),
48
+ dim: chalk.hex(COLORS.dim),
49
+ highlight: chalk.hex(COLORS.highlight),
50
+ bold: chalk.bold,
51
+ primaryBold: chalk.hex(COLORS.primary).bold,
52
+ accentBold: chalk.hex(COLORS.accent).bold,
53
+ successBold: chalk.hex(COLORS.success).bold,
54
+ errorBold: chalk.hex(COLORS.error).bold,
55
+ warningBold: chalk.hex(COLORS.warning).bold,
56
+ platform: (p) => chalk.hex(PLATFORM_META[p]?.color ?? COLORS.primary)
57
+ };
58
+ var ICONS = {
59
+ dashboard: "\u25C9",
60
+ device: "\u2B21",
61
+ generate: "\u2726",
62
+ package: "\u229F",
63
+ apps: "\u2295",
64
+ logs: "\u2261",
65
+ transfer: "\u21C4",
66
+ shell: "$",
67
+ debug: "\u25CE",
68
+ build: "\u2699",
69
+ input: "\u25C8",
70
+ emulator: "\u25A3",
71
+ check: "\u2713",
72
+ cross: "\u2717",
73
+ warn: "\u26A0",
74
+ info: "\u25CF",
75
+ arrow: "\u203A",
76
+ arrowRight: "\u2192",
77
+ connected: "\u25CF",
78
+ disconnected: "\u25CB",
79
+ running: "\u25B6",
80
+ stopped: "\u25A0",
81
+ selected: "\u25B6",
82
+ spinner: ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"]
83
+ };
84
+ var NAV_ITEMS = {
85
+ webos: [
86
+ { id: "dashboard", label: "Dashboard", icon: ICONS.dashboard },
87
+ { id: "devices", label: "Devices", icon: ICONS.device },
88
+ { id: "generate", label: "Generate App", icon: ICONS.generate },
89
+ { id: "deploy", label: "Package & Deploy", icon: ICONS.package },
90
+ { id: "apps", label: "App Manager", icon: ICONS.apps },
91
+ { id: "logs", label: "Log Viewer", icon: ICONS.logs },
92
+ { id: "transfer", label: "File Transfer", icon: ICONS.transfer },
93
+ { id: "shell", label: "Shell", icon: ICONS.shell },
94
+ { id: "inspector", label: "Inspector", icon: ICONS.debug }
95
+ ],
96
+ tizen: [
97
+ { id: "dashboard", label: "Dashboard", icon: ICONS.dashboard },
98
+ { id: "devices", label: "Devices", icon: ICONS.device },
99
+ { id: "generate", label: "Generate App", icon: ICONS.generate },
100
+ { id: "deploy", label: "Package & Deploy", icon: ICONS.package },
101
+ { id: "apps", label: "App Manager", icon: ICONS.apps },
102
+ { id: "logs", label: "Log Viewer", icon: ICONS.logs },
103
+ { id: "transfer", label: "File Transfer", icon: ICONS.transfer },
104
+ { id: "shell", label: "Shell", icon: ICONS.shell },
105
+ { id: "emulator", label: "Emulator", icon: ICONS.emulator }
106
+ ],
107
+ amazon: [
108
+ { id: "dashboard", label: "Dashboard", icon: ICONS.dashboard },
109
+ { id: "devices", label: "Devices", icon: ICONS.device },
110
+ { id: "deploy", label: "Install APK", icon: ICONS.package },
111
+ { id: "apps", label: "App Manager", icon: ICONS.apps },
112
+ { id: "input", label: "Input Simulator", icon: ICONS.input },
113
+ { id: "logs", label: "Log Viewer", icon: ICONS.logs },
114
+ { id: "transfer", label: "File Transfer", icon: ICONS.transfer },
115
+ { id: "shell", label: "Shell", icon: ICONS.shell },
116
+ { id: "inspector", label: "Debug Info", icon: ICONS.debug }
117
+ ],
118
+ android: [
119
+ { id: "dashboard", label: "Dashboard", icon: ICONS.dashboard },
120
+ { id: "devices", label: "Devices", icon: ICONS.device },
121
+ { id: "build", label: "Build", icon: ICONS.build },
122
+ { id: "deploy", label: "Install APK", icon: ICONS.package },
123
+ { id: "apps", label: "App Manager", icon: ICONS.apps },
124
+ { id: "logs", label: "Log Viewer", icon: ICONS.logs },
125
+ { id: "transfer", label: "File Transfer", icon: ICONS.transfer },
126
+ { id: "shell", label: "Shell", icon: ICONS.shell },
127
+ { id: "emulator", label: "Emulator", icon: ICONS.emulator }
128
+ ]
129
+ };
130
+
131
+ // src/components/Header.js
132
+ function Header({ currentDevice, platform, onChangePlatform }) {
133
+ const meta = platform ? PLATFORM_META[platform] : null;
134
+ const width = process.stdout.columns || 80;
135
+ const deviceText = currentDevice ? `${ICONS.connected} ${currentDevice.name} (${currentDevice.host})` : `${ICONS.disconnected} No device`;
136
+ return /* @__PURE__ */ React.createElement(
137
+ Box,
138
+ {
139
+ borderStyle: "round",
140
+ borderColor: meta?.color ?? "gray",
141
+ paddingX: 1,
142
+ width,
143
+ justifyContent: "space-between"
144
+ },
145
+ /* @__PURE__ */ React.createElement(Box, { gap: 1 }, /* @__PURE__ */ React.createElement(Text, { bold: true, color: meta?.color ?? "white" }, meta?.icon ?? "\u25C9", " TV Dev Manager"), meta && /* @__PURE__ */ React.createElement(Text, { color: meta.color, dimColor: true }, "[", meta.label, "]")),
146
+ /* @__PURE__ */ React.createElement(Box, { gap: 2 }, /* @__PURE__ */ React.createElement(Text, { color: currentDevice ? "green" : "gray" }, deviceText), platform && /* @__PURE__ */ React.createElement(Text, { color: "#818CF8", dimColor: true }, "[p] switch platform"))
147
+ );
148
+ }
149
+
150
+ // src/components/Sidebar.js
151
+ import React2 from "react";
152
+ import { Box as Box2, Text as Text2 } from "ink";
153
+ function Sidebar({ menuIndex, currentScreen, focused, platform }) {
154
+ const items = platform ? NAV_ITEMS[platform] : [];
155
+ const meta = platform ? PLATFORM_META[platform] : null;
156
+ const color = meta?.color ?? "gray";
157
+ return /* @__PURE__ */ React2.createElement(
158
+ Box2,
159
+ {
160
+ flexDirection: "column",
161
+ borderStyle: "round",
162
+ borderColor: focused ? color : "gray",
163
+ width: 24,
164
+ paddingX: 1
165
+ },
166
+ /* @__PURE__ */ React2.createElement(Text2, { bold: true, color: focused ? color : "gray" }, "Navigation"),
167
+ /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column", marginTop: 1 }, items.map((item, i) => {
168
+ const isCursor = focused && i === menuIndex;
169
+ const isCurrent = !focused && item.id === currentScreen;
170
+ const isActive = isCursor || isCurrent;
171
+ return /* @__PURE__ */ React2.createElement(Box2, { key: item.id }, /* @__PURE__ */ React2.createElement(
172
+ Text2,
173
+ {
174
+ bold: isActive,
175
+ color: isCursor ? color : isCurrent ? "white" : "gray"
176
+ },
177
+ isCursor ? "\u25B6 " : " ",
178
+ item.icon,
179
+ " ",
180
+ item.label
181
+ ));
182
+ })),
183
+ /* @__PURE__ */ React2.createElement(Box2, { marginTop: 1 }, /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")),
184
+ /* @__PURE__ */ React2.createElement(Text2, { dimColor: true, color: "gray" }, "\u2191\u2193 navigate"),
185
+ /* @__PURE__ */ React2.createElement(Text2, { dimColor: true, color: "gray" }, "\u23CE select")
186
+ );
187
+ }
188
+
189
+ // src/components/StatusBar.js
190
+ import React3 from "react";
191
+ import { Box as Box3, Text as Text3 } from "ink";
192
+ var SIDEBAR_HINTS = "\u2191\u2193 Navigate \u23CE Select p Platform q Quit";
193
+ var SCREEN_HINTS = "Esc Back q Quit";
194
+ function StatusBar({ sidebarFocused, platform }) {
195
+ const meta = platform ? PLATFORM_META[platform] : null;
196
+ const width = (process.stdout.columns || 80) - 4;
197
+ const hints = sidebarFocused ? SIDEBAR_HINTS : SCREEN_HINTS;
198
+ return /* @__PURE__ */ React3.createElement(
199
+ Box3,
200
+ {
201
+ borderStyle: "round",
202
+ borderColor: "gray",
203
+ paddingX: 1,
204
+ width: process.stdout.columns || 80,
205
+ justifyContent: "space-between"
206
+ },
207
+ /* @__PURE__ */ React3.createElement(Text3, { dimColor: true }, hints),
208
+ meta && /* @__PURE__ */ React3.createElement(Text3, { color: meta.color, dimColor: true }, meta.hint)
209
+ );
210
+ }
211
+
212
+ // src/components/PlatformPicker.js
213
+ import React4, { useState } from "react";
214
+ import { Box as Box4, Text as Text4, useInput, useApp } from "ink";
215
+ function PlatformPicker({ onSelect }) {
216
+ const { exit } = useApp();
217
+ const [cursor, setCursor] = useState(0);
218
+ useInput((input, key) => {
219
+ if (key.ctrl && input === "c") {
220
+ exit();
221
+ return;
222
+ }
223
+ if (input === "q") {
224
+ exit();
225
+ return;
226
+ }
227
+ if (key.upArrow) setCursor((i) => Math.max(0, i - 1));
228
+ if (key.downArrow) setCursor((i) => Math.min(PLATFORM_IDS.length - 1, i + 1));
229
+ if (key.return) onSelect(PLATFORM_IDS[cursor]);
230
+ const num = parseInt(input);
231
+ if (num >= 1 && num <= PLATFORM_IDS.length) {
232
+ onSelect(PLATFORM_IDS[num - 1]);
233
+ }
234
+ });
235
+ const cols = process.stdout.columns || 80;
236
+ return /* @__PURE__ */ React4.createElement(Box4, { flexDirection: "column", width: cols, padding: 2, gap: 1 }, /* @__PURE__ */ React4.createElement(
237
+ Box4,
238
+ {
239
+ borderStyle: "double",
240
+ borderColor: "#818CF8",
241
+ paddingX: 2,
242
+ paddingY: 1,
243
+ flexDirection: "column",
244
+ alignItems: "center"
245
+ },
246
+ /* @__PURE__ */ React4.createElement(Text4, { bold: true, color: "#818CF8" }, "\u25C9 TV Dev Manager"),
247
+ /* @__PURE__ */ React4.createElement(Text4, { color: "#64748B" }, "Universal Smart TV Development CLI")
248
+ ), /* @__PURE__ */ React4.createElement(Box4, { flexDirection: "column", borderStyle: "round", borderColor: "#475569", paddingX: 2, paddingY: 1, gap: 1 }, /* @__PURE__ */ React4.createElement(Text4, { bold: true, color: "#E2E8F0" }, "Select Platform"), /* @__PURE__ */ React4.createElement(Text4, { color: "#64748B", dimColor: true }, "\u2191\u2193 navigate \u23CE select 1-4 quick pick q quit"), /* @__PURE__ */ React4.createElement(Box4, { flexDirection: "column", marginTop: 1, gap: 1 }, PLATFORM_IDS.map((id, i) => {
249
+ const meta = PLATFORM_META[id];
250
+ const active = i === cursor;
251
+ return /* @__PURE__ */ React4.createElement(Box4, { key: id, gap: 2 }, /* @__PURE__ */ React4.createElement(Text4, { bold: true, color: active ? meta.color : "#475569" }, active ? "\u25B6" : " ", " ", i + 1, "."), /* @__PURE__ */ React4.createElement(Box4, { flexDirection: "column" }, /* @__PURE__ */ React4.createElement(Text4, { bold: active, color: active ? meta.color : "#94A3B8" }, meta.icon, " ", meta.label), /* @__PURE__ */ React4.createElement(Text4, { color: "#64748B", dimColor: true }, " ", meta.hint)));
252
+ }))));
253
+ }
254
+
255
+ // src/screens/Dashboard.js
256
+ import React6, { useState as useState3, useEffect as useEffect2 } from "react";
257
+ import { Box as Box6, Text as Text6, useInput as useInput2 } from "ink";
258
+
259
+ // src/components/Loader.js
260
+ import React5, { useState as useState2, useEffect } from "react";
261
+ import { Box as Box5, Text as Text5 } from "ink";
262
+ function Loader({ label = "Loading...", color = "cyan" }) {
263
+ const [frame, setFrame] = useState2(0);
264
+ useEffect(() => {
265
+ const t = setInterval(() => setFrame((f) => (f + 1) % ICONS.spinner.length), 80);
266
+ return () => clearInterval(t);
267
+ }, []);
268
+ return /* @__PURE__ */ React5.createElement(Box5, null, /* @__PURE__ */ React5.createElement(Text5, { color }, ICONS.spinner[frame], " "), /* @__PURE__ */ React5.createElement(Text5, { color: "gray" }, label));
269
+ }
270
+
271
+ // src/utils/webos.js
272
+ var webos_exports = {};
273
+ __export(webos_exports, {
274
+ INSTALL_HINT: () => INSTALL_HINT,
275
+ addDevice: () => addDevice,
276
+ checkAvailable: () => checkAvailable,
277
+ closeApp: () => closeApp,
278
+ generateApp: () => generateApp,
279
+ getDeviceInfo: () => getDeviceInfo,
280
+ installApp: () => installApp,
281
+ launchApp: () => launchApp,
282
+ launchInspector: () => launchInspector,
283
+ listDevices: () => listDevices,
284
+ listInstalledApps: () => listInstalledApps,
285
+ listRunningApps: () => listRunningApps,
286
+ listTemplates: () => listTemplates,
287
+ packageApp: () => packageApp,
288
+ pullFiles: () => pullFiles,
289
+ pushFiles: () => pushFiles,
290
+ removeApp: () => removeApp,
291
+ removeDevice: () => removeDevice,
292
+ runShellCommand: () => runShellCommand,
293
+ setDefaultDevice: () => setDefaultDevice,
294
+ spawnLogStream: () => spawnLogStream
295
+ });
296
+ import { execa } from "execa";
297
+
298
+ // src/backend/parsers/webos.js
299
+ function parseDeviceTable(output) {
300
+ return output.split("\n").filter((l) => l.trim() && !l.startsWith("----") && !l.startsWith("name")).map((line) => {
301
+ const parts = line.trim().split(/\s+/);
302
+ return {
303
+ name: parts[0] || "",
304
+ host: parts[1] || "",
305
+ port: parts[2] || "22",
306
+ username: parts[3] || "root",
307
+ default: line.includes("(default)")
308
+ };
309
+ }).filter((d) => d.name);
310
+ }
311
+ function parseInstalledApps(output) {
312
+ return output.split("\n").filter((l) => l.trim() && !l.toLowerCase().startsWith("list")).map((line) => {
313
+ const parts = line.trim().split(/\s+/);
314
+ return { id: parts[0], version: parts[1] || "", title: parts.slice(2).join(" ") };
315
+ }).filter((a) => a.id);
316
+ }
317
+ function parseRunningApps(output) {
318
+ return output.split("\n").filter((l) => l.trim() && !l.toLowerCase().startsWith("running")).map((l) => l.trim()).filter(Boolean);
319
+ }
320
+ function parseTemplates(output) {
321
+ return output.split("\n").filter((l) => l.trim() && !l.startsWith("Available")).map((l) => {
322
+ const parts = l.trim().split(/\s{2,}/);
323
+ return { name: parts[0], description: parts[1] || "" };
324
+ }).filter((t) => t.name);
325
+ }
326
+ function parseIpkFilename(output, outDir) {
327
+ const match = output.match(/[\w.-]+\.ipk/);
328
+ return match ? `${outDir}/${match[0]}` : null;
329
+ }
330
+
331
+ // src/backend/commands/webos.js
332
+ function buildAddDeviceArgs({ name, host, port = 22, username = "root", password = "" }) {
333
+ const args = ["--add", name, "--info", `host=${host}`, "--info", `port=${port}`, "--info", `username=${username}`];
334
+ if (password) args.push("--info", `password=${password}`);
335
+ return args;
336
+ }
337
+ var buildRemoveDeviceArgs = (name) => ["--remove", name];
338
+ var buildSetDefaultDeviceArgs = (name) => ["--default", name];
339
+ function buildGenerateArgs({ template, outDir, appId, title, version = "1.0.0" }) {
340
+ const args = ["-t", template, outDir];
341
+ if (appId) args.push("-p", `id=${appId}`);
342
+ if (title) args.push("-p", `title=${title}`);
343
+ if (version) args.push("-p", `version=${version}`);
344
+ return args;
345
+ }
346
+ function buildPackageArgs({ appDir, outDir = ".", excludes = [] }) {
347
+ const args = [appDir, "-o", outDir];
348
+ for (const e of excludes) args.push("-e", e);
349
+ return args;
350
+ }
351
+ function buildInstallArgs({ ipkPath, device }) {
352
+ const args = [ipkPath];
353
+ if (device) args.push("-d", device);
354
+ return args;
355
+ }
356
+ function buildListAppsArgs(device) {
357
+ const args = ["--list"];
358
+ if (device) args.push("-d", device);
359
+ return args;
360
+ }
361
+ function buildRemoveAppArgs({ appId, device }) {
362
+ const args = ["--remove", appId];
363
+ if (device) args.push("-d", device);
364
+ return args;
365
+ }
366
+ function buildLaunchArgs({ appId, device, params }) {
367
+ const args = [appId];
368
+ if (device) args.push("-d", device);
369
+ if (params) args.push("-p", params);
370
+ return args;
371
+ }
372
+ function buildCloseArgs({ appId, device }) {
373
+ const args = [appId, "--close"];
374
+ if (device) args.push("-d", device);
375
+ return args;
376
+ }
377
+ function buildRunningAppsArgs(device) {
378
+ const args = ["--running"];
379
+ if (device) args.push("-d", device);
380
+ return args;
381
+ }
382
+ function buildPushArgs({ localPath, remotePath, device }) {
383
+ const args = [localPath, remotePath];
384
+ if (device) args.push("-d", device);
385
+ return args;
386
+ }
387
+ function buildPullArgs({ remotePath, localPath, device }) {
388
+ const args = [remotePath, localPath];
389
+ if (device) args.push("-d", device);
390
+ return args;
391
+ }
392
+ function buildShellArgs({ command, device }) {
393
+ const args = [];
394
+ if (device) args.push("-d", device);
395
+ args.push("--cmd", command);
396
+ return args;
397
+ }
398
+ function buildLogArgs({ device, follow = true, lines = 100, appId }) {
399
+ const args = [];
400
+ if (device) args.push("-d", device);
401
+ if (follow) args.push("-f");
402
+ args.push("-n", String(lines));
403
+ if (appId) args.push("-id", appId);
404
+ return args;
405
+ }
406
+ function buildDeviceInfoArgs(device) {
407
+ const args = [];
408
+ if (device) args.push("-d", device);
409
+ return args;
410
+ }
411
+ function buildInspectorArgs({ appId, device, serviceId }) {
412
+ const args = [appId];
413
+ if (device) args.push("-d", device);
414
+ if (serviceId) args.push("-s", serviceId);
415
+ return args;
416
+ }
417
+
418
+ // src/utils/webos.js
419
+ async function checkAvailable() {
420
+ try {
421
+ await execa("ares-setup-device", ["--version"], { timeout: 5e3 });
422
+ return true;
423
+ } catch {
424
+ return false;
425
+ }
426
+ }
427
+ async function listDevices() {
428
+ const { stdout } = await execa("ares-setup-device", ["--listfull"], { timeout: 1e4 });
429
+ try {
430
+ return JSON.parse(stdout);
431
+ } catch {
432
+ return parseDeviceTable(stdout);
433
+ }
434
+ }
435
+ async function addDevice(opts) {
436
+ const { stdout } = await execa("ares-setup-device", buildAddDeviceArgs(opts), { timeout: 15e3 });
437
+ return stdout;
438
+ }
439
+ async function removeDevice(name) {
440
+ const { stdout } = await execa("ares-setup-device", buildRemoveDeviceArgs(name), { timeout: 1e4 });
441
+ return stdout;
442
+ }
443
+ async function setDefaultDevice(name) {
444
+ const { stdout } = await execa("ares-setup-device", buildSetDefaultDeviceArgs(name), { timeout: 1e4 });
445
+ return stdout;
446
+ }
447
+ async function listTemplates() {
448
+ const { stdout } = await execa("ares-generate", ["--list"], { timeout: 1e4 });
449
+ return parseTemplates(stdout);
450
+ }
451
+ async function generateApp(opts) {
452
+ const { stdout } = await execa("ares-generate", buildGenerateArgs(opts), { timeout: 3e4 });
453
+ return stdout;
454
+ }
455
+ async function packageApp(opts) {
456
+ const { stdout } = await execa("ares-package", buildPackageArgs(opts), { timeout: 6e4 });
457
+ return { output: stdout, pkgFile: parseIpkFilename(stdout, opts.outDir ?? ".") };
458
+ }
459
+ async function installApp(opts) {
460
+ const { stdout } = await execa("ares-install", buildInstallArgs(opts), { timeout: 6e4 });
461
+ return stdout;
462
+ }
463
+ async function listInstalledApps(device) {
464
+ const { stdout } = await execa("ares-install", buildListAppsArgs(device), { timeout: 15e3 });
465
+ return parseInstalledApps(stdout);
466
+ }
467
+ async function removeApp(opts) {
468
+ const { stdout } = await execa("ares-install", buildRemoveAppArgs(opts), { timeout: 3e4 });
469
+ return stdout;
470
+ }
471
+ async function launchApp(opts) {
472
+ const { stdout } = await execa("ares-launch", buildLaunchArgs(opts), { timeout: 15e3 });
473
+ return stdout;
474
+ }
475
+ async function closeApp(opts) {
476
+ const { stdout } = await execa("ares-launch", buildCloseArgs(opts), { timeout: 15e3 });
477
+ return stdout;
478
+ }
479
+ async function listRunningApps(device) {
480
+ const { stdout } = await execa("ares-launch", buildRunningAppsArgs(device), { timeout: 1e4 });
481
+ return parseRunningApps(stdout);
482
+ }
483
+ async function pushFiles(opts) {
484
+ const { stdout } = await execa("ares-push", buildPushArgs(opts), { timeout: 6e4 });
485
+ return stdout;
486
+ }
487
+ async function pullFiles(opts) {
488
+ const { stdout } = await execa("ares-pull", buildPullArgs(opts), { timeout: 6e4 });
489
+ return stdout;
490
+ }
491
+ async function runShellCommand(opts) {
492
+ const { stdout, stderr } = await execa("ares-shell", buildShellArgs(opts), { timeout: 3e4 });
493
+ return (stdout || "") + (stderr || "");
494
+ }
495
+ function spawnLogStream(opts) {
496
+ return execa("ares-log", buildLogArgs(opts), { timeout: 0 });
497
+ }
498
+ async function getDeviceInfo(device) {
499
+ const { stdout } = await execa("ares-device", buildDeviceInfoArgs(device), { timeout: 15e3 });
500
+ return stdout;
501
+ }
502
+ async function launchInspector(opts) {
503
+ const { stdout } = await execa("ares-inspect", buildInspectorArgs(opts), { timeout: 3e4 });
504
+ return stdout;
505
+ }
506
+ var INSTALL_HINT = "npm install -g @webosose/ares-cli";
507
+
508
+ // src/utils/tizen.js
509
+ var tizen_exports = {};
510
+ __export(tizen_exports, {
511
+ INSTALL_HINT: () => INSTALL_HINT2,
512
+ buildApp: () => buildApp,
513
+ checkAvailable: () => checkAvailable2,
514
+ connectDevice: () => connectDevice,
515
+ disconnectDevice: () => disconnectDevice,
516
+ generateApp: () => generateApp2,
517
+ installApp: () => installApp2,
518
+ launchApp: () => launchApp2,
519
+ launchEmulator: () => launchEmulator,
520
+ listDevices: () => listDevices2,
521
+ listInstalledApps: () => listInstalledApps2,
522
+ listTemplates: () => listTemplates2,
523
+ packageApp: () => packageApp2,
524
+ pullFiles: () => pullFiles2,
525
+ pushFiles: () => pushFiles2,
526
+ removeApp: () => removeApp2,
527
+ runShellCommand: () => runShellCommand2,
528
+ spawnLogStream: () => spawnLogStream2
529
+ });
530
+ import { execa as execa2 } from "execa";
531
+
532
+ // src/backend/parsers/tizen.js
533
+ function parseSdbDevices(output) {
534
+ return output.split("\n").filter((l) => l.trim() && !l.startsWith("List of devices")).map((line) => {
535
+ const parts = line.trim().split(/\s+/);
536
+ const serial = parts[0] || "";
537
+ const status = parts[1] || "";
538
+ const extra = parts.slice(2).join(" ");
539
+ const hostMatch = serial.match(/^(.+):(\d+)$/);
540
+ return {
541
+ name: extra || serial,
542
+ serial,
543
+ host: hostMatch ? hostMatch[1] : serial,
544
+ port: hostMatch ? hostMatch[2] : "26101",
545
+ status,
546
+ default: false
547
+ };
548
+ }).filter((d) => d.serial && d.status === "device");
549
+ }
550
+ function parseTizenTemplates(output) {
551
+ return output.split("\n").filter((l) => l.trim() && /^\d+\./.test(l.trim())).map((l) => {
552
+ const match = l.trim().match(/^\d+\.\s+(\S+)\s*(.*)/);
553
+ return match ? { name: match[1], description: match[2].trim() } : null;
554
+ }).filter(Boolean);
555
+ }
556
+ function parseTizenApps(output) {
557
+ return output.split("\n").filter((l) => l.includes("pkgid")).map((l) => {
558
+ const idMatch = l.match(/pkgid\s*:\s*(\S+)/);
559
+ const verMatch = l.match(/version\s*:\s*(\S+)/);
560
+ return idMatch ? { id: idMatch[1], version: verMatch?.[1] || "" } : null;
561
+ }).filter(Boolean);
562
+ }
563
+ function parseWgtFilename(output, outDir) {
564
+ const match = output.match(/[\w.-]+\.wgt/);
565
+ return match ? `${outDir}/${match[0]}` : null;
566
+ }
567
+
568
+ // src/backend/commands/tizen.js
569
+ function buildSdbDevicesArgs() {
570
+ return ["devices"];
571
+ }
572
+ function buildSdbConnectArgs({ host, port = 26101 }) {
573
+ return ["connect", `${host}:${port}`];
574
+ }
575
+ function buildSdbDisconnectArgs({ host, port = 26101 }) {
576
+ return ["disconnect", `${host}:${port}`];
577
+ }
578
+ function buildSdbShellArgs({ command, serial }) {
579
+ const args = [];
580
+ if (serial) args.push("-s", serial);
581
+ args.push("shell");
582
+ if (command) args.push(command);
583
+ return args;
584
+ }
585
+ function buildSdbPushArgs({ localPath, remotePath, serial }) {
586
+ const args = [];
587
+ if (serial) args.push("-s", serial);
588
+ args.push("push", localPath, remotePath);
589
+ return args;
590
+ }
591
+ function buildSdbPullArgs({ remotePath, localPath, serial }) {
592
+ const args = [];
593
+ if (serial) args.push("-s", serial);
594
+ args.push("pull", remotePath, localPath);
595
+ return args;
596
+ }
597
+ function buildSdbLogArgs({ serial, follow = true }) {
598
+ const args = [];
599
+ if (serial) args.push("-s", serial);
600
+ args.push("dlog");
601
+ if (follow) args.push("-v", "time");
602
+ return args;
603
+ }
604
+ function buildTizenCreateArgs({ template = "WebBasicapp", name, outDir = "." }) {
605
+ return ["create", "web-project", "-t", template, "-n", name, "--", outDir];
606
+ }
607
+ function buildTizenBuildArgs({ projectDir }) {
608
+ return ["build-web", "--", projectDir];
609
+ }
610
+ function buildTizenPackageArgs({ projectDir, type = "wgt", cert }) {
611
+ const args = ["package", "-t", type];
612
+ if (cert) args.push("-s", cert);
613
+ args.push("--", projectDir);
614
+ return args;
615
+ }
616
+ function buildTizenInstallArgs({ wgtPath, serial }) {
617
+ const args = ["install"];
618
+ if (serial) args.push("-t", serial);
619
+ args.push("-n", wgtPath);
620
+ return args;
621
+ }
622
+ function buildTizenRunArgs({ appId, serial }) {
623
+ const args = ["run", "-p", appId];
624
+ if (serial) args.push("-t", serial);
625
+ return args;
626
+ }
627
+ function buildTizenUninstallArgs({ appId, serial }) {
628
+ const args = ["uninstall", "-p", appId];
629
+ if (serial) args.push("-t", serial);
630
+ return args;
631
+ }
632
+ function buildEmulatorLaunchArgs({ name }) {
633
+ return ["launch", "--name", name];
634
+ }
635
+
636
+ // src/utils/tizen.js
637
+ async function checkAvailable2() {
638
+ try {
639
+ await execa2("sdb", ["version"], { timeout: 5e3 });
640
+ return true;
641
+ } catch {
642
+ return false;
643
+ }
644
+ }
645
+ async function listDevices2() {
646
+ const { stdout } = await execa2("sdb", buildSdbDevicesArgs(), { timeout: 1e4 });
647
+ return parseSdbDevices(stdout);
648
+ }
649
+ async function connectDevice(opts) {
650
+ const { stdout } = await execa2("sdb", buildSdbConnectArgs(opts), { timeout: 15e3 });
651
+ return stdout;
652
+ }
653
+ async function disconnectDevice(opts) {
654
+ const { stdout } = await execa2("sdb", buildSdbDisconnectArgs(opts), { timeout: 1e4 });
655
+ return stdout;
656
+ }
657
+ async function listTemplates2() {
658
+ const { stdout } = await execa2("tizen", ["list-templates"], { timeout: 1e4 });
659
+ return parseTizenTemplates(stdout);
660
+ }
661
+ async function generateApp2(opts) {
662
+ const { stdout } = await execa2("tizen", buildTizenCreateArgs(opts), { timeout: 3e4 });
663
+ return stdout;
664
+ }
665
+ async function buildApp(opts) {
666
+ const { stdout, stderr } = await execa2("tizen", buildTizenBuildArgs(opts), { timeout: 12e4 });
667
+ return (stdout || "") + (stderr || "");
668
+ }
669
+ async function packageApp2(opts) {
670
+ const { stdout } = await execa2("tizen", buildTizenPackageArgs(opts), { timeout: 6e4 });
671
+ return { output: stdout, pkgFile: parseWgtFilename(stdout, opts.projectDir ?? ".") };
672
+ }
673
+ async function installApp2(opts) {
674
+ const { stdout } = await execa2("tizen", buildTizenInstallArgs(opts), { timeout: 6e4 });
675
+ return stdout;
676
+ }
677
+ async function listInstalledApps2(device) {
678
+ const { stdout } = await execa2("sdb", buildSdbShellArgs({ command: "0 pkgcmd -l", serial: device }), { timeout: 15e3 });
679
+ return parseTizenApps(stdout);
680
+ }
681
+ async function launchApp2(opts) {
682
+ const { stdout } = await execa2("tizen", buildTizenRunArgs(opts), { timeout: 15e3 });
683
+ return stdout;
684
+ }
685
+ async function removeApp2(opts) {
686
+ const { stdout } = await execa2("tizen", buildTizenUninstallArgs(opts), { timeout: 3e4 });
687
+ return stdout;
688
+ }
689
+ async function pushFiles2(opts) {
690
+ const { stdout } = await execa2("sdb", buildSdbPushArgs(opts), { timeout: 6e4 });
691
+ return stdout;
692
+ }
693
+ async function pullFiles2(opts) {
694
+ const { stdout } = await execa2("sdb", buildSdbPullArgs(opts), { timeout: 6e4 });
695
+ return stdout;
696
+ }
697
+ async function runShellCommand2(opts) {
698
+ const { stdout, stderr } = await execa2("sdb", buildSdbShellArgs(opts), { timeout: 3e4 });
699
+ return (stdout || "") + (stderr || "");
700
+ }
701
+ function spawnLogStream2(opts) {
702
+ return execa2("sdb", buildSdbLogArgs(opts), { timeout: 0 });
703
+ }
704
+ async function launchEmulator(opts) {
705
+ const { stdout } = await execa2("tizen", buildEmulatorLaunchArgs(opts), { timeout: 3e4 });
706
+ return stdout;
707
+ }
708
+ var INSTALL_HINT2 = "Install Tizen Studio from developer.samsung.com/smarttv";
709
+
710
+ // src/utils/amazon.js
711
+ var amazon_exports = {};
712
+ __export(amazon_exports, {
713
+ INSTALL_HINT: () => INSTALL_HINT3,
714
+ checkAvailable: () => checkAvailable3,
715
+ connectDevice: () => connectDevice2,
716
+ disconnectDevice: () => disconnectDevice2,
717
+ installApp: () => installApp3,
718
+ launchApp: () => launchApp3,
719
+ listDevices: () => listDevices3,
720
+ listInstalledApps: () => listInstalledApps3,
721
+ loggingCtl: () => loggingCtl,
722
+ pullFiles: () => pullFiles3,
723
+ pushFiles: () => pushFiles3,
724
+ removeApp: () => removeApp3,
725
+ runShellCommand: () => runShellCommand3,
726
+ sendInput: () => sendInput,
727
+ spawnLogStream: () => spawnLogStream3,
728
+ stopApp: () => stopApp
729
+ });
730
+ import { execa as execa3 } from "execa";
731
+
732
+ // src/backend/parsers/amazon.js
733
+ function parseAdbDevices(output) {
734
+ return output.split("\n").filter((l) => l.trim() && !l.startsWith("List of devices")).map((line) => {
735
+ const parts = line.trim().split(/\s+/);
736
+ const serial = parts[0] || "";
737
+ const status = parts[1] || "";
738
+ const hostMatch = serial.match(/^(.+):(\d+)$/);
739
+ const modelMatch = line.match(/model:(\S+)/);
740
+ return {
741
+ name: modelMatch ? modelMatch[1].replace(/_/g, " ") : serial,
742
+ serial,
743
+ host: hostMatch ? hostMatch[1] : serial,
744
+ port: hostMatch ? hostMatch[2] : "5555",
745
+ status,
746
+ default: false
747
+ };
748
+ }).filter((d) => d.serial && d.status === "device");
749
+ }
750
+ function parseAdbPackages(output) {
751
+ return output.split("\n").filter((l) => l.startsWith("package:")).map((l) => ({
752
+ id: l.replace("package:", "").trim(),
753
+ version: ""
754
+ }));
755
+ }
756
+
757
+ // src/backend/commands/amazon.js
758
+ function buildAdbDevicesArgs() {
759
+ return ["devices", "-l"];
760
+ }
761
+ function buildAdbConnectArgs({ host, port = 5555 }) {
762
+ return ["connect", `${host}:${port}`];
763
+ }
764
+ function buildAdbDisconnectArgs({ host, port = 5555 }) {
765
+ return ["disconnect", `${host}:${port}`];
766
+ }
767
+ function buildAdbInstallArgs({ apkPath, serial, replace = true }) {
768
+ const args = [];
769
+ if (serial) args.push("-s", serial);
770
+ args.push("install");
771
+ if (replace) args.push("-r");
772
+ args.push(apkPath);
773
+ return args;
774
+ }
775
+ function buildAdbLaunchArgs({ packageName, activity, serial }) {
776
+ const args = [];
777
+ if (serial) args.push("-s", serial);
778
+ args.push("shell", "am", "start", "-n", `${packageName}/${activity}`);
779
+ return args;
780
+ }
781
+ function buildAdbStopArgs({ packageName, serial }) {
782
+ const args = [];
783
+ if (serial) args.push("-s", serial);
784
+ args.push("shell", "am", "force-stop", packageName);
785
+ return args;
786
+ }
787
+ function buildAdbListAppsArgs({ serial, includeSystem = false }) {
788
+ const args = [];
789
+ if (serial) args.push("-s", serial);
790
+ args.push("shell", "pm", "list", "packages");
791
+ if (!includeSystem) args.push("-3");
792
+ return args;
793
+ }
794
+ function buildAdbUninstallArgs({ packageName, serial }) {
795
+ const args = [];
796
+ if (serial) args.push("-s", serial);
797
+ args.push("uninstall", packageName);
798
+ return args;
799
+ }
800
+ function buildAdbLogcatArgs({ serial, filter, clear = false }) {
801
+ const args = [];
802
+ if (serial) args.push("-s", serial);
803
+ args.push("logcat");
804
+ if (clear) args.push("-c");
805
+ if (filter) args.push(filter);
806
+ return args;
807
+ }
808
+ function buildAdbShellArgs({ command, serial }) {
809
+ const args = [];
810
+ if (serial) args.push("-s", serial);
811
+ args.push("shell");
812
+ if (command) args.push(command);
813
+ return args;
814
+ }
815
+ function buildAdbPushArgs({ localPath, remotePath, serial }) {
816
+ const args = [];
817
+ if (serial) args.push("-s", serial);
818
+ args.push("push", localPath, remotePath);
819
+ return args;
820
+ }
821
+ function buildAdbPullArgs({ remotePath, localPath, serial }) {
822
+ const args = [];
823
+ if (serial) args.push("-s", serial);
824
+ args.push("pull", remotePath, localPath);
825
+ return args;
826
+ }
827
+ function buildInputdArgs({ key }) {
828
+ return ["send", key];
829
+ }
830
+ function buildLoggingCtlArgs({ action }) {
831
+ return [action];
832
+ }
833
+
834
+ // src/utils/amazon.js
835
+ async function checkAvailable3() {
836
+ try {
837
+ await execa3("adb", ["version"], { timeout: 5e3 });
838
+ return true;
839
+ } catch {
840
+ return false;
841
+ }
842
+ }
843
+ async function listDevices3() {
844
+ const { stdout } = await execa3("adb", buildAdbDevicesArgs(), { timeout: 1e4 });
845
+ return parseAdbDevices(stdout);
846
+ }
847
+ async function connectDevice2(opts) {
848
+ const { stdout } = await execa3("adb", buildAdbConnectArgs(opts), { timeout: 15e3 });
849
+ return stdout;
850
+ }
851
+ async function disconnectDevice2(opts) {
852
+ const { stdout } = await execa3("adb", buildAdbDisconnectArgs(opts), { timeout: 1e4 });
853
+ return stdout;
854
+ }
855
+ async function installApp3(opts) {
856
+ const { stdout, stderr } = await execa3("adb", buildAdbInstallArgs(opts), { timeout: 12e4 });
857
+ return (stdout || "") + (stderr || "");
858
+ }
859
+ async function launchApp3(opts) {
860
+ const { stdout } = await execa3("adb", buildAdbLaunchArgs(opts), { timeout: 15e3 });
861
+ return stdout;
862
+ }
863
+ async function stopApp(opts) {
864
+ const { stdout } = await execa3("adb", buildAdbStopArgs(opts), { timeout: 15e3 });
865
+ return stdout;
866
+ }
867
+ async function listInstalledApps3(device) {
868
+ const { stdout } = await execa3("adb", buildAdbListAppsArgs({ serial: device }), { timeout: 15e3 });
869
+ return parseAdbPackages(stdout);
870
+ }
871
+ async function removeApp3(opts) {
872
+ const { stdout } = await execa3("adb", buildAdbUninstallArgs(opts), { timeout: 3e4 });
873
+ return stdout;
874
+ }
875
+ function spawnLogStream3(opts) {
876
+ return execa3("adb", buildAdbLogcatArgs(opts), { timeout: 0 });
877
+ }
878
+ async function runShellCommand3(opts) {
879
+ const { stdout, stderr } = await execa3("adb", buildAdbShellArgs(opts), { timeout: 3e4 });
880
+ return (stdout || "") + (stderr || "");
881
+ }
882
+ async function pushFiles3(opts) {
883
+ const { stdout } = await execa3("adb", buildAdbPushArgs(opts), { timeout: 6e4 });
884
+ return stdout;
885
+ }
886
+ async function pullFiles3(opts) {
887
+ const { stdout } = await execa3("adb", buildAdbPullArgs(opts), { timeout: 6e4 });
888
+ return stdout;
889
+ }
890
+ async function sendInput(key) {
891
+ const { stdout } = await execa3("inputd-cli", buildInputdArgs({ key }), { timeout: 5e3 });
892
+ return stdout;
893
+ }
894
+ async function loggingCtl(action) {
895
+ const { stdout } = await execa3("LoggingCtl", buildLoggingCtlArgs({ action }), { timeout: 3e4 });
896
+ return stdout;
897
+ }
898
+ var INSTALL_HINT3 = "Install ADB: brew install android-platform-tools (macOS) or see developer.amazon.com/docs/fire-tv/connecting-adb";
899
+
900
+ // src/utils/android.js
901
+ var android_exports = {};
902
+ __export(android_exports, {
903
+ INSTALL_HINT: () => INSTALL_HINT4,
904
+ buildProject: () => buildProject,
905
+ checkAvailable: () => checkAvailable4,
906
+ connectDevice: () => connectDevice3,
907
+ disconnectDevice: () => disconnectDevice3,
908
+ installApp: () => installApp4,
909
+ launchApp: () => launchApp4,
910
+ launchEmulator: () => launchEmulator2,
911
+ listAvds: () => listAvds,
912
+ listDevices: () => listDevices4,
913
+ listInstalledApps: () => listInstalledApps4,
914
+ pullFiles: () => pullFiles4,
915
+ pushFiles: () => pushFiles4,
916
+ removeApp: () => removeApp4,
917
+ runShellCommand: () => runShellCommand4,
918
+ spawnLogStream: () => spawnLogStream4,
919
+ stopApp: () => stopApp2
920
+ });
921
+ import { execa as execa4 } from "execa";
922
+
923
+ // src/backend/parsers/android.js
924
+ function parseAdbDevices2(output) {
925
+ return output.split("\n").filter((l) => l.trim() && !l.startsWith("List of devices")).map((line) => {
926
+ const parts = line.trim().split(/\s+/);
927
+ const serial = parts[0] || "";
928
+ const status = parts[1] || "";
929
+ const hostMatch = serial.match(/^(.+):(\d+)$/);
930
+ const modelMatch = line.match(/model:(\S+)/);
931
+ const prodMatch = line.match(/product:(\S+)/);
932
+ return {
933
+ name: modelMatch ? modelMatch[1].replace(/_/g, " ") : prodMatch?.[1] ?? serial,
934
+ serial,
935
+ host: hostMatch ? hostMatch[1] : serial,
936
+ port: hostMatch ? hostMatch[2] : "5555",
937
+ status,
938
+ default: false
939
+ };
940
+ }).filter((d) => d.serial && d.status === "device");
941
+ }
942
+ function parseAdbPackages2(output) {
943
+ return output.split("\n").filter((l) => l.startsWith("package:")).map((l) => ({
944
+ id: l.replace("package:", "").trim(),
945
+ version: ""
946
+ }));
947
+ }
948
+ function parseAvdList(output) {
949
+ const avds = [];
950
+ let current = null;
951
+ for (const line of output.split("\n")) {
952
+ const nameMatch = line.match(/Name:\s+(.+)/);
953
+ const typeMatch = line.match(/Type:\s+(.+)/);
954
+ const apiMatch = line.match(/API level:\s+(\d+)/);
955
+ if (nameMatch) {
956
+ current = { name: nameMatch[1].trim(), type: "", api: "" };
957
+ avds.push(current);
958
+ }
959
+ if (current && typeMatch) current.type = typeMatch[1].trim();
960
+ if (current && apiMatch) current.api = apiMatch[1];
961
+ }
962
+ return avds;
963
+ }
964
+ function parseGradleOutput(output) {
965
+ const successMatch = output.match(/BUILD SUCCESSFUL/);
966
+ const failMatch = output.match(/BUILD FAILED/);
967
+ const apkMatch = output.match(/[\w\/.-]+\.apk/);
968
+ return {
969
+ success: !!successMatch,
970
+ failed: !!failMatch,
971
+ apkPath: apkMatch ? apkMatch[0] : null
972
+ };
973
+ }
974
+
975
+ // src/backend/commands/android.js
976
+ function buildAdbDevicesArgs2() {
977
+ return ["devices", "-l"];
978
+ }
979
+ function buildAdbConnectArgs2({ host, port = 5555 }) {
980
+ return ["connect", `${host}:${port}`];
981
+ }
982
+ function buildAdbDisconnectArgs2({ host, port = 5555 }) {
983
+ return ["disconnect", `${host}:${port}`];
984
+ }
985
+ function buildAdbInstallArgs2({ apkPath, serial, replace = true }) {
986
+ const args = [];
987
+ if (serial) args.push("-s", serial);
988
+ args.push("install");
989
+ if (replace) args.push("-r");
990
+ args.push(apkPath);
991
+ return args;
992
+ }
993
+ function buildAdbLaunchArgs2({ packageName, activity, serial }) {
994
+ const args = [];
995
+ if (serial) args.push("-s", serial);
996
+ args.push("shell", "am", "start", "-n", `${packageName}/${activity}`);
997
+ return args;
998
+ }
999
+ function buildAdbStopArgs2({ packageName, serial }) {
1000
+ const args = [];
1001
+ if (serial) args.push("-s", serial);
1002
+ args.push("shell", "am", "force-stop", packageName);
1003
+ return args;
1004
+ }
1005
+ function buildAdbListAppsArgs2({ serial, includeSystem = false }) {
1006
+ const args = [];
1007
+ if (serial) args.push("-s", serial);
1008
+ args.push("shell", "pm", "list", "packages");
1009
+ if (!includeSystem) args.push("-3");
1010
+ return args;
1011
+ }
1012
+ function buildAdbUninstallArgs2({ packageName, serial }) {
1013
+ const args = [];
1014
+ if (serial) args.push("-s", serial);
1015
+ args.push("uninstall", packageName);
1016
+ return args;
1017
+ }
1018
+ function buildAdbLogcatArgs2({ serial, filter, tag }) {
1019
+ const args = [];
1020
+ if (serial) args.push("-s", serial);
1021
+ args.push("logcat");
1022
+ if (tag) args.push(`${tag}:V`, "*:S");
1023
+ if (filter) args.push(filter);
1024
+ return args;
1025
+ }
1026
+ function buildAdbShellArgs2({ command, serial }) {
1027
+ const args = [];
1028
+ if (serial) args.push("-s", serial);
1029
+ args.push("shell");
1030
+ if (command) args.push(command);
1031
+ return args;
1032
+ }
1033
+ function buildAdbPushArgs2({ localPath, remotePath, serial }) {
1034
+ const args = [];
1035
+ if (serial) args.push("-s", serial);
1036
+ args.push("push", localPath, remotePath);
1037
+ return args;
1038
+ }
1039
+ function buildAdbPullArgs2({ remotePath, localPath, serial }) {
1040
+ const args = [];
1041
+ if (serial) args.push("-s", serial);
1042
+ args.push("pull", remotePath, localPath);
1043
+ return args;
1044
+ }
1045
+ function buildAvdListArgs() {
1046
+ return ["list", "avd"];
1047
+ }
1048
+ function buildEmulatorArgs({ avdName }) {
1049
+ return ["-avd", avdName];
1050
+ }
1051
+
1052
+ // src/utils/android.js
1053
+ async function checkAvailable4() {
1054
+ try {
1055
+ await execa4("adb", ["version"], { timeout: 5e3 });
1056
+ return true;
1057
+ } catch {
1058
+ return false;
1059
+ }
1060
+ }
1061
+ async function listDevices4() {
1062
+ const { stdout } = await execa4("adb", buildAdbDevicesArgs2(), { timeout: 1e4 });
1063
+ return parseAdbDevices2(stdout);
1064
+ }
1065
+ async function connectDevice3(opts) {
1066
+ const { stdout } = await execa4("adb", buildAdbConnectArgs2(opts), { timeout: 15e3 });
1067
+ return stdout;
1068
+ }
1069
+ async function disconnectDevice3(opts) {
1070
+ const { stdout } = await execa4("adb", buildAdbDisconnectArgs2(opts), { timeout: 1e4 });
1071
+ return stdout;
1072
+ }
1073
+ async function installApp4(opts) {
1074
+ const { stdout, stderr } = await execa4("adb", buildAdbInstallArgs2(opts), { timeout: 12e4 });
1075
+ return (stdout || "") + (stderr || "");
1076
+ }
1077
+ async function launchApp4(opts) {
1078
+ const { stdout } = await execa4("adb", buildAdbLaunchArgs2(opts), { timeout: 15e3 });
1079
+ return stdout;
1080
+ }
1081
+ async function stopApp2(opts) {
1082
+ const { stdout } = await execa4("adb", buildAdbStopArgs2(opts), { timeout: 15e3 });
1083
+ return stdout;
1084
+ }
1085
+ async function listInstalledApps4(device) {
1086
+ const { stdout } = await execa4("adb", buildAdbListAppsArgs2({ serial: device }), { timeout: 15e3 });
1087
+ return parseAdbPackages2(stdout);
1088
+ }
1089
+ async function removeApp4(opts) {
1090
+ const { stdout } = await execa4("adb", buildAdbUninstallArgs2(opts), { timeout: 3e4 });
1091
+ return stdout;
1092
+ }
1093
+ function spawnLogStream4(opts) {
1094
+ return execa4("adb", buildAdbLogcatArgs2(opts), { timeout: 0 });
1095
+ }
1096
+ async function runShellCommand4(opts) {
1097
+ const { stdout, stderr } = await execa4("adb", buildAdbShellArgs2(opts), { timeout: 3e4 });
1098
+ return (stdout || "") + (stderr || "");
1099
+ }
1100
+ async function pushFiles4(opts) {
1101
+ const { stdout } = await execa4("adb", buildAdbPushArgs2(opts), { timeout: 6e4 });
1102
+ return stdout;
1103
+ }
1104
+ async function pullFiles4(opts) {
1105
+ const { stdout } = await execa4("adb", buildAdbPullArgs2(opts), { timeout: 6e4 });
1106
+ return stdout;
1107
+ }
1108
+ async function buildProject(opts) {
1109
+ const gradlew = opts.useWrapper ? "./gradlew" : "gradle";
1110
+ const { stdout, stderr } = await execa4(gradlew, [opts.task ?? "assembleDebug"], {
1111
+ cwd: opts.projectDir,
1112
+ timeout: 3e5
1113
+ });
1114
+ return parseGradleOutput((stdout || "") + (stderr || ""));
1115
+ }
1116
+ async function listAvds() {
1117
+ const { stdout } = await execa4("avdmanager", buildAvdListArgs(), { timeout: 15e3 });
1118
+ return parseAvdList(stdout);
1119
+ }
1120
+ async function launchEmulator2(opts) {
1121
+ return execa4("emulator", buildEmulatorArgs(opts), { timeout: 0, detached: true });
1122
+ }
1123
+ var INSTALL_HINT4 = "Install Android SDK: see developer.android.com/studio or brew install --cask android-studio";
1124
+
1125
+ // src/screens/Dashboard.js
1126
+ var UTILS = { webos: webos_exports, tizen: tizen_exports, amazon: amazon_exports, android: android_exports };
1127
+ function Dashboard({ focused, currentDevice, platform, onDeviceChange, onNavigate }) {
1128
+ const [devices, setDevices] = useState3([]);
1129
+ const [apps, setApps] = useState3([]);
1130
+ const [cliOk, setCliOk] = useState3(null);
1131
+ const [loading, setLoading] = useState3(true);
1132
+ const [menuIdx, setMenuIdx] = useState3(0);
1133
+ const meta = PLATFORM_META[platform];
1134
+ const utils = UTILS[platform];
1135
+ const navItems = NAV_ITEMS[platform];
1136
+ const quickActions = navItems.filter((n) => n.id !== "dashboard").slice(0, 6).map((n, i) => ({
1137
+ key: String(i + 1),
1138
+ label: n.label,
1139
+ screen: n.id,
1140
+ icon: n.icon
1141
+ }));
1142
+ useEffect2(() => {
1143
+ async function load() {
1144
+ setLoading(true);
1145
+ const ok = await utils.checkAvailable().catch(() => false);
1146
+ setCliOk(ok);
1147
+ if (ok) {
1148
+ const devs = await utils.listDevices().catch(() => []);
1149
+ setDevices(devs);
1150
+ const def = devs.find((d) => d.default) || devs[0];
1151
+ if (def && !currentDevice) onDeviceChange(def);
1152
+ if (def && utils.listInstalledApps) {
1153
+ const a = await utils.listInstalledApps(def.name ?? def.serial).catch(() => []);
1154
+ setApps(a);
1155
+ }
1156
+ }
1157
+ setLoading(false);
1158
+ }
1159
+ load();
1160
+ }, [platform]);
1161
+ useInput2((input, key) => {
1162
+ if (!focused) return;
1163
+ if (key.upArrow) setMenuIdx((i) => Math.max(0, i - 1));
1164
+ if (key.downArrow) setMenuIdx((i) => Math.min(quickActions.length - 1, i + 1));
1165
+ if (key.return) onNavigate(quickActions[menuIdx].screen);
1166
+ const action = quickActions.find((a) => a.key === input);
1167
+ if (action) onNavigate(action.screen);
1168
+ }, { isActive: focused });
1169
+ if (loading) {
1170
+ return /* @__PURE__ */ React6.createElement(Box6, { flexDirection: "column", padding: 2 }, /* @__PURE__ */ React6.createElement(Loader, { label: `Loading ${meta.label} info...`, color: meta.color }));
1171
+ }
1172
+ return /* @__PURE__ */ React6.createElement(Box6, { flexDirection: "column", padding: 1, gap: 1 }, !cliOk && /* @__PURE__ */ React6.createElement(Box6, { borderStyle: "round", borderColor: "red", paddingX: 2, paddingY: 1 }, /* @__PURE__ */ React6.createElement(Text6, { color: "red", bold: true }, ICONS.warn, " CLI tools not found. "), /* @__PURE__ */ React6.createElement(Text6, { color: "gray" }, utils.INSTALL_HINT)), /* @__PURE__ */ React6.createElement(Box6, { gap: 2 }, /* @__PURE__ */ React6.createElement(Box6, { flexDirection: "column", borderStyle: "round", borderColor: meta.color, paddingX: 1, paddingY: 0, width: 34 }, /* @__PURE__ */ React6.createElement(Text6, { bold: true, color: meta.color }, meta.icon, " Devices"), /* @__PURE__ */ React6.createElement(Box6, { flexDirection: "column", marginTop: 1 }, devices.length === 0 && /* @__PURE__ */ React6.createElement(Text6, { color: "gray" }, " No devices found"), devices.map((d) => /* @__PURE__ */ React6.createElement(Box6, { key: d.name ?? d.serial }, /* @__PURE__ */ React6.createElement(Text6, { color: d.default ? "green" : "gray" }, d.default ? ICONS.connected : ICONS.disconnected, " ", /* @__PURE__ */ React6.createElement(Text6, { bold: d.default }, d.name ?? d.serial), /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, " ", d.host, ":", d.port)))))), /* @__PURE__ */ React6.createElement(Box6, { flexDirection: "column", borderStyle: "round", borderColor: "magenta", paddingX: 1, paddingY: 0, width: 40 }, /* @__PURE__ */ React6.createElement(Text6, { bold: true, color: "magenta" }, ICONS.apps, " Installed Apps", currentDevice && /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, " [", currentDevice.name ?? currentDevice.serial, "]")), /* @__PURE__ */ React6.createElement(Box6, { flexDirection: "column", marginTop: 1 }, apps.length === 0 && /* @__PURE__ */ React6.createElement(Text6, { color: "gray" }, " No apps / select a device first"), apps.slice(0, 8).map((app) => /* @__PURE__ */ React6.createElement(Box6, { key: app.id }, /* @__PURE__ */ React6.createElement(Text6, { color: "white" }, ICONS.arrow, " "), /* @__PURE__ */ React6.createElement(Text6, null, app.id), app.version && /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, " v", app.version))), apps.length > 8 && /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, " ... +", apps.length - 8, " more")))), /* @__PURE__ */ React6.createElement(Box6, { flexDirection: "column", borderStyle: "round", borderColor: focused ? meta.color : "gray", paddingX: 1, paddingY: 0 }, /* @__PURE__ */ React6.createElement(Text6, { bold: true, color: focused ? meta.color : "gray" }, "Quick Actions"), /* @__PURE__ */ React6.createElement(Box6, { flexDirection: "column", marginTop: 1 }, quickActions.map((a, i) => /* @__PURE__ */ React6.createElement(Box6, { key: a.screen }, /* @__PURE__ */ React6.createElement(
1173
+ Text6,
1174
+ {
1175
+ bold: focused && i === menuIdx,
1176
+ color: focused && i === menuIdx ? meta.color : "white"
1177
+ },
1178
+ focused && i === menuIdx ? "\u25B6 " : " ",
1179
+ /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, "[", a.key, "]"),
1180
+ " ",
1181
+ a.icon,
1182
+ " ",
1183
+ a.label
1184
+ )))), focused && /* @__PURE__ */ React6.createElement(Text6, { dimColor: true, marginTop: 1 }, "\u2191\u2193 navigate \u23CE open or press shortcut key")));
1185
+ }
1186
+
1187
+ // src/screens/Devices.js
1188
+ import React7, { useState as useState4, useEffect as useEffect3, useCallback } from "react";
1189
+ import { Box as Box7, Text as Text7, useInput as useInput3 } from "ink";
1190
+ import TextInput from "ink-text-input";
1191
+ var UTILS2 = { webos: webos_exports, tizen: tizen_exports, amazon: amazon_exports, android: android_exports };
1192
+ var MODES = { LIST: "list", ADD: "add", CONFIRM_REMOVE: "confirm_remove" };
1193
+ var WEBOS_FIELDS = ["name", "host", "port", "username", "password"];
1194
+ var ADB_FIELDS = ["host", "port"];
1195
+ var TIZEN_FIELDS = ["host", "port"];
1196
+ function getFields(platform) {
1197
+ if (platform === "webos") return WEBOS_FIELDS;
1198
+ if (platform === "tizen") return TIZEN_FIELDS;
1199
+ return ADB_FIELDS;
1200
+ }
1201
+ function getDefaultForm(platform) {
1202
+ if (platform === "webos") return { name: "", host: "", port: "22", username: "root", password: "" };
1203
+ if (platform === "tizen") return { host: "", port: "26101" };
1204
+ return { host: "", port: "5555" };
1205
+ }
1206
+ function getFormLabel(field, platform) {
1207
+ if (field === "port" && platform === "webos") return "SSH Port";
1208
+ if (field === "port" && platform === "tizen") return "SDB Port";
1209
+ if (field === "port") return "ADB Port";
1210
+ return field.charAt(0).toUpperCase() + field.slice(1);
1211
+ }
1212
+ function Devices({ focused, platform, onDeviceChange }) {
1213
+ const [devices, setDevices] = useState4([]);
1214
+ const [loading, setLoading] = useState4(true);
1215
+ const [mode, setMode] = useState4(MODES.LIST);
1216
+ const [cursor, setCursor] = useState4(0);
1217
+ const [status, setStatus] = useState4(null);
1218
+ const [working, setWorking] = useState4(false);
1219
+ const meta = PLATFORM_META[platform];
1220
+ const utils = UTILS2[platform];
1221
+ const fields = getFields(platform);
1222
+ const [form, setForm] = useState4(getDefaultForm(platform));
1223
+ const [formField, setFormField] = useState4(0);
1224
+ const load = useCallback(async () => {
1225
+ setLoading(true);
1226
+ const devs = await utils.listDevices().catch(() => []);
1227
+ setDevices(devs);
1228
+ setLoading(false);
1229
+ }, [platform]);
1230
+ useEffect3(() => {
1231
+ load();
1232
+ }, [platform]);
1233
+ useInput3(async (input, key) => {
1234
+ if (!focused) return;
1235
+ if (mode === MODES.LIST) {
1236
+ if (key.upArrow) setCursor((c2) => Math.max(0, c2 - 1));
1237
+ if (key.downArrow) setCursor((c2) => Math.min(devices.length - 1, c2 + 1));
1238
+ if (input === "a") {
1239
+ setMode(MODES.ADD);
1240
+ setForm(getDefaultForm(platform));
1241
+ setFormField(0);
1242
+ }
1243
+ if (input === "d" && devices[cursor]) setMode(MODES.CONFIRM_REMOVE);
1244
+ if (input === "r") {
1245
+ setStatus(null);
1246
+ load();
1247
+ }
1248
+ if (input === "s" && devices[cursor] && platform === "webos") {
1249
+ setWorking(true);
1250
+ try {
1251
+ await utils.setDefaultDevice(devices[cursor].name);
1252
+ onDeviceChange(devices[cursor]);
1253
+ setStatus({ ok: true, msg: `Default set to ${devices[cursor].name}` });
1254
+ await load();
1255
+ } catch (e) {
1256
+ setStatus({ ok: false, msg: e.message });
1257
+ }
1258
+ setWorking(false);
1259
+ }
1260
+ if ((key.return || input === "s") && devices[cursor] && platform !== "webos") {
1261
+ onDeviceChange({ ...devices[cursor], default: true });
1262
+ setStatus({ ok: true, msg: `Active device set to ${devices[cursor].name ?? devices[cursor].serial}` });
1263
+ }
1264
+ }
1265
+ if (mode === MODES.ADD) {
1266
+ if (key.escape) {
1267
+ setMode(MODES.LIST);
1268
+ return;
1269
+ }
1270
+ if (key.tab || key.return) {
1271
+ if (formField < fields.length - 1) {
1272
+ setFormField((f) => f + 1);
1273
+ } else {
1274
+ setWorking(true);
1275
+ try {
1276
+ if (platform === "webos") {
1277
+ await utils.addDevice(form);
1278
+ setStatus({ ok: true, msg: `Device '${form.name}' added` });
1279
+ } else {
1280
+ const out = await utils.connectDevice({ host: form.host, port: parseInt(form.port) || void 0 });
1281
+ setStatus({ ok: true, msg: out || `Connected to ${form.host}:${form.port}` });
1282
+ }
1283
+ await load();
1284
+ setMode(MODES.LIST);
1285
+ } catch (e) {
1286
+ setStatus({ ok: false, msg: e.message });
1287
+ }
1288
+ setWorking(false);
1289
+ }
1290
+ }
1291
+ }
1292
+ if (mode === MODES.CONFIRM_REMOVE) {
1293
+ if (input === "y") {
1294
+ setWorking(true);
1295
+ try {
1296
+ if (platform === "webos") {
1297
+ await utils.removeDevice(devices[cursor].name);
1298
+ } else {
1299
+ await utils.disconnectDevice({ host: devices[cursor].host, port: devices[cursor].port });
1300
+ }
1301
+ setStatus({ ok: true, msg: `Removed ${devices[cursor].name ?? devices[cursor].serial}` });
1302
+ setCursor(0);
1303
+ await load();
1304
+ } catch (e) {
1305
+ setStatus({ ok: false, msg: e.message });
1306
+ }
1307
+ setMode(MODES.LIST);
1308
+ setWorking(false);
1309
+ }
1310
+ if (input === "n" || key.escape) setMode(MODES.LIST);
1311
+ }
1312
+ }, { isActive: focused });
1313
+ if (loading) return /* @__PURE__ */ React7.createElement(Box7, { padding: 2 }, /* @__PURE__ */ React7.createElement(Loader, { label: "Loading devices...", color: meta.color }));
1314
+ const addLabel = platform === "webos" ? "Add Device" : "Connect Device";
1315
+ const removeLabel = platform === "webos" ? "Remove" : "Disconnect";
1316
+ return /* @__PURE__ */ React7.createElement(Box7, { flexDirection: "column", padding: 1, gap: 1 }, /* @__PURE__ */ React7.createElement(Text7, { bold: true, color: meta.color }, meta.icon, " Device Manager"), status && /* @__PURE__ */ React7.createElement(Box7, { borderStyle: "round", borderColor: status.ok ? "green" : "red", paddingX: 1 }, /* @__PURE__ */ React7.createElement(Text7, { color: status.ok ? "green" : "red" }, status.ok ? ICONS.check : ICONS.cross, " ", status.msg)), working && /* @__PURE__ */ React7.createElement(Loader, { label: "Working...", color: meta.color }), mode === MODES.LIST && /* @__PURE__ */ React7.createElement(React7.Fragment, null, /* @__PURE__ */ React7.createElement(Box7, { flexDirection: "column", borderStyle: "round", borderColor: meta.color, paddingX: 1 }, /* @__PURE__ */ React7.createElement(Box7, { marginBottom: 1 }, /* @__PURE__ */ React7.createElement(Text7, { bold: true }, "Name / Serial".padEnd(24)), /* @__PURE__ */ React7.createElement(Text7, { bold: true }, "Host".padEnd(18)), /* @__PURE__ */ React7.createElement(Text7, { bold: true }, "Port")), devices.length === 0 && /* @__PURE__ */ React7.createElement(Text7, { color: "gray" }, " No devices. Press [a] to add / connect."), devices.map((d, i) => /* @__PURE__ */ React7.createElement(Box7, { key: d.name ?? d.serial }, /* @__PURE__ */ React7.createElement(
1317
+ Text7,
1318
+ {
1319
+ bold: focused && i === cursor,
1320
+ color: focused && i === cursor ? meta.color : d.default ? "green" : "white"
1321
+ },
1322
+ focused && i === cursor ? "\u25B6 " : " ",
1323
+ (d.name ?? d.serial).padEnd(22),
1324
+ /* @__PURE__ */ React7.createElement(Text7, { color: "gray" }, " " + (d.host ?? "").padEnd(16)),
1325
+ /* @__PURE__ */ React7.createElement(Text7, { color: "gray" }, " " + String(d.port ?? "")),
1326
+ d.default && /* @__PURE__ */ React7.createElement(Text7, { color: "green" }, " \u2605")
1327
+ )))), /* @__PURE__ */ React7.createElement(Text7, { dimColor: true }, "[a] ", addLabel, " [d] ", removeLabel, " ", platform === "webos" ? "[s] Set Default " : "[\u23CE] Set Active ", "[r] Refresh")), mode === MODES.ADD && /* @__PURE__ */ React7.createElement(Box7, { flexDirection: "column", borderStyle: "round", borderColor: meta.color, paddingX: 2, paddingY: 1, gap: 1 }, /* @__PURE__ */ React7.createElement(Text7, { bold: true, color: meta.color }, addLabel), fields.map((field, i) => /* @__PURE__ */ React7.createElement(Box7, { key: field }, /* @__PURE__ */ React7.createElement(Text7, { color: formField === i ? meta.color : "gray" }, getFormLabel(field, platform).padEnd(12)), formField === i ? /* @__PURE__ */ React7.createElement(
1328
+ TextInput,
1329
+ {
1330
+ value: form[field] ?? "",
1331
+ onChange: (v) => setForm((f) => ({ ...f, [field]: v })),
1332
+ placeholder: field === "port" ? platform === "tizen" ? "26101" : "5555" : `Enter ${field}...`
1333
+ }
1334
+ ) : /* @__PURE__ */ React7.createElement(Text7, { color: "white" }, form[field] || /* @__PURE__ */ React7.createElement(Text7, { dimColor: true }, "(empty)")))), /* @__PURE__ */ React7.createElement(Text7, { dimColor: true }, "Tab/Enter next Last field Enter submits Esc cancel")), mode === MODES.CONFIRM_REMOVE && devices[cursor] && /* @__PURE__ */ React7.createElement(Box7, { borderStyle: "round", borderColor: "red", paddingX: 2, paddingY: 1 }, /* @__PURE__ */ React7.createElement(Text7, { color: "red" }, removeLabel, " "), /* @__PURE__ */ React7.createElement(Text7, { bold: true, color: "white" }, devices[cursor].name ?? devices[cursor].serial), /* @__PURE__ */ React7.createElement(Text7, { color: "red" }, "? "), /* @__PURE__ */ React7.createElement(Text7, { color: "yellow" }, "[y] Yes [n] No")));
1335
+ }
1336
+
1337
+ // src/screens/Generate.js
1338
+ import React8, { useState as useState5, useEffect as useEffect4 } from "react";
1339
+ import { Box as Box8, Text as Text8, useInput as useInput4 } from "ink";
1340
+ import TextInput2 from "ink-text-input";
1341
+ var UTILS3 = { webos: webos_exports, tizen: tizen_exports };
1342
+ var WEBOS_FALLBACK = [
1343
+ { name: "webapp", description: "Basic web application" },
1344
+ { name: "hosted_webapp", description: "Hosted web application" },
1345
+ { name: "js_service", description: "JavaScript service" },
1346
+ { name: "qmlapp", description: "QML application" }
1347
+ ];
1348
+ var TIZEN_FALLBACK = [
1349
+ { name: "WebBasicapp", description: "Basic Tizen web app" },
1350
+ { name: "WebUIApplication", description: "Tizen UI framework app" },
1351
+ { name: "BasicProject", description: "Minimal project" }
1352
+ ];
1353
+ var STEPS = { SELECT_TPL: 0, FILL_FORM: 1, DONE: 2 };
1354
+ var WEBOS_FIELDS2 = ["appId", "title", "version", "outDir"];
1355
+ var TIZEN_FIELDS2 = ["name", "outDir"];
1356
+ var FIELD_LABELS = {
1357
+ appId: "App ID",
1358
+ title: "Title",
1359
+ version: "Version",
1360
+ outDir: "Output Dir",
1361
+ name: "Project Name"
1362
+ };
1363
+ var FIELD_DEFAULTS = {
1364
+ webos: { appId: "com.example.app", title: "My App", version: "1.0.0", outDir: "./myapp" },
1365
+ tizen: { name: "MyTizenApp", outDir: "." }
1366
+ };
1367
+ function Generate({ focused, platform }) {
1368
+ const [templates, setTemplates] = useState5([]);
1369
+ const [loading, setLoading] = useState5(true);
1370
+ const [step, setStep] = useState5(STEPS.SELECT_TPL);
1371
+ const [tplIdx, setTplIdx] = useState5(0);
1372
+ const [fieldIdx, setFieldIdx] = useState5(0);
1373
+ const [form, setForm] = useState5({ ...FIELD_DEFAULTS[platform] ?? FIELD_DEFAULTS.webos });
1374
+ const [working, setWorking] = useState5(false);
1375
+ const [result, setResult] = useState5(null);
1376
+ const [error, setError] = useState5(null);
1377
+ const meta = PLATFORM_META[platform];
1378
+ const utils = UTILS3[platform];
1379
+ const fields = platform === "tizen" ? TIZEN_FIELDS2 : WEBOS_FIELDS2;
1380
+ const fallback = platform === "tizen" ? TIZEN_FALLBACK : WEBOS_FALLBACK;
1381
+ useEffect4(() => {
1382
+ utils.listTemplates().then((t) => setTemplates(t.length ? t : fallback)).catch(() => setTemplates(fallback)).finally(() => setLoading(false));
1383
+ }, [platform]);
1384
+ useInput4(async (input, key) => {
1385
+ if (!focused) return;
1386
+ if (step === STEPS.SELECT_TPL) {
1387
+ if (key.upArrow) setTplIdx((i) => Math.max(0, i - 1));
1388
+ if (key.downArrow) setTplIdx((i) => Math.min(templates.length - 1, i + 1));
1389
+ if (key.return) setStep(STEPS.FILL_FORM);
1390
+ }
1391
+ if (step === STEPS.FILL_FORM) {
1392
+ if (key.escape) {
1393
+ setStep(STEPS.SELECT_TPL);
1394
+ setFieldIdx(0);
1395
+ return;
1396
+ }
1397
+ if (key.tab || key.return) {
1398
+ if (fieldIdx < fields.length - 1) {
1399
+ setFieldIdx((f) => f + 1);
1400
+ } else {
1401
+ setWorking(true);
1402
+ setError(null);
1403
+ try {
1404
+ let out;
1405
+ if (platform === "tizen") {
1406
+ out = await utils.generateApp({
1407
+ template: templates[tplIdx].name,
1408
+ name: form.name,
1409
+ outDir: form.outDir
1410
+ });
1411
+ } else {
1412
+ out = await utils.generateApp({
1413
+ template: templates[tplIdx].name,
1414
+ outDir: form.outDir,
1415
+ appId: form.appId,
1416
+ title: form.title,
1417
+ version: form.version
1418
+ });
1419
+ }
1420
+ setResult(out || `App created at ${form.outDir ?? form.name}`);
1421
+ setStep(STEPS.DONE);
1422
+ } catch (e) {
1423
+ setError(e.message);
1424
+ }
1425
+ setWorking(false);
1426
+ }
1427
+ }
1428
+ }
1429
+ if (step === STEPS.DONE) {
1430
+ if (key.return || input === "r") {
1431
+ setStep(STEPS.SELECT_TPL);
1432
+ setResult(null);
1433
+ setForm({ ...FIELD_DEFAULTS[platform] ?? FIELD_DEFAULTS.webos });
1434
+ setFieldIdx(0);
1435
+ }
1436
+ }
1437
+ }, { isActive: focused });
1438
+ if (loading) return /* @__PURE__ */ React8.createElement(Box8, { padding: 2 }, /* @__PURE__ */ React8.createElement(Loader, { label: "Loading templates...", color: meta.color }));
1439
+ return /* @__PURE__ */ React8.createElement(Box8, { flexDirection: "column", padding: 1, gap: 1 }, /* @__PURE__ */ React8.createElement(Text8, { bold: true, color: meta.color }, ICONS.generate, " Generate App ", /* @__PURE__ */ React8.createElement(Text8, { color: "gray" }, "[", meta.label, "]")), error && /* @__PURE__ */ React8.createElement(Box8, { borderStyle: "round", borderColor: "red", paddingX: 1 }, /* @__PURE__ */ React8.createElement(Text8, { color: "red" }, ICONS.cross, " ", error)), working && /* @__PURE__ */ React8.createElement(Loader, { label: "Generating app...", color: meta.color }), step === STEPS.SELECT_TPL && !working && /* @__PURE__ */ React8.createElement(Box8, { flexDirection: "column", borderStyle: "round", borderColor: meta.color, paddingX: 1, paddingY: 1, gap: 1 }, /* @__PURE__ */ React8.createElement(Text8, { bold: true }, "Select Template"), templates.map((t, i) => /* @__PURE__ */ React8.createElement(Box8, { key: t.name }, /* @__PURE__ */ React8.createElement(
1440
+ Text8,
1441
+ {
1442
+ bold: focused && i === tplIdx,
1443
+ color: focused && i === tplIdx ? meta.color : "white"
1444
+ },
1445
+ focused && i === tplIdx ? "\u25B6 " : " ",
1446
+ /* @__PURE__ */ React8.createElement(Text8, { bold: true }, t.name.padEnd(20)),
1447
+ /* @__PURE__ */ React8.createElement(Text8, { dimColor: true }, " ", t.description)
1448
+ ))), /* @__PURE__ */ React8.createElement(Text8, { dimColor: true, marginTop: 1 }, "\u2191\u2193 select \u23CE continue")), step === STEPS.FILL_FORM && !working && /* @__PURE__ */ React8.createElement(Box8, { flexDirection: "column", borderStyle: "round", borderColor: meta.color, paddingX: 2, paddingY: 1, gap: 1 }, /* @__PURE__ */ React8.createElement(Text8, { bold: true }, "Template: ", /* @__PURE__ */ React8.createElement(Text8, { color: meta.color }, templates[tplIdx]?.name)), fields.map((field, i) => /* @__PURE__ */ React8.createElement(Box8, { key: field }, /* @__PURE__ */ React8.createElement(Text8, { color: fieldIdx === i ? meta.color : "gray" }, (FIELD_LABELS[field] ?? field).padEnd(14)), fieldIdx === i ? /* @__PURE__ */ React8.createElement(
1449
+ TextInput2,
1450
+ {
1451
+ value: form[field] ?? "",
1452
+ onChange: (v) => setForm((f) => ({ ...f, [field]: v })),
1453
+ placeholder: FIELD_DEFAULTS[platform]?.[field] ?? field
1454
+ }
1455
+ ) : /* @__PURE__ */ React8.createElement(Text8, { color: "white" }, form[field] ?? ""))), /* @__PURE__ */ React8.createElement(Text8, { dimColor: true }, "Tab/Enter next Last field Enter generates Esc back")), step === STEPS.DONE && /* @__PURE__ */ React8.createElement(Box8, { flexDirection: "column", borderStyle: "round", borderColor: "green", paddingX: 2, paddingY: 1, gap: 1 }, /* @__PURE__ */ React8.createElement(Text8, { bold: true, color: "green" }, ICONS.check, " App Generated!"), /* @__PURE__ */ React8.createElement(Text8, { color: "gray" }, result), /* @__PURE__ */ React8.createElement(Text8, { dimColor: true }, "\u23CE or [r] to generate another")));
1456
+ }
1457
+
1458
+ // src/screens/Deploy.js
1459
+ import React9, { useState as useState6 } from "react";
1460
+ import { Box as Box9, Text as Text9, useInput as useInput5 } from "ink";
1461
+ import TextInput3 from "ink-text-input";
1462
+ var UTILS4 = { webos: webos_exports, tizen: tizen_exports, amazon: amazon_exports, android: android_exports };
1463
+ var STEPS2 = { INPUT: 0, PACKAGING: 1, CONFIRM_INSTALL: 2, INSTALLING: 3, CONFIRM_LAUNCH: 4, DONE: 5 };
1464
+ function getPkgLabel(platform) {
1465
+ if (platform === "webos") return "App Source Directory";
1466
+ if (platform === "tizen") return "Project Directory";
1467
+ return "APK File Path";
1468
+ }
1469
+ function getInstallLabel(platform) {
1470
+ if (platform === "webos" || platform === "tizen") return "Package & Install";
1471
+ return "Install APK";
1472
+ }
1473
+ var NEEDS_PACKAGING = ["webos", "tizen"];
1474
+ function Deploy({ focused, currentDevice, platform }) {
1475
+ const meta = PLATFORM_META[platform];
1476
+ const utils = UTILS4[platform];
1477
+ const device = currentDevice?.name ?? currentDevice?.serial;
1478
+ const [step, setStep] = useState6(STEPS2.INPUT);
1479
+ const [srcPath, setSrcPath] = useState6("");
1480
+ const [outDir, setOutDir] = useState6("./dist");
1481
+ const [pkgFile, setPkgFile] = useState6("");
1482
+ const [output, setOutput] = useState6([]);
1483
+ const [error, setError] = useState6(null);
1484
+ const [inputField, setInputField] = useState6(0);
1485
+ const needsPkg = NEEDS_PACKAGING.includes(platform);
1486
+ const addOutput = (msg, ok = true) => setOutput((o) => [...o, { msg, ok }]);
1487
+ const runDeploy = async () => {
1488
+ setStep(STEPS2.PACKAGING);
1489
+ setError(null);
1490
+ try {
1491
+ if (needsPkg) {
1492
+ const { output: out, pkgFile: pf } = await utils.packageApp({ appDir: srcPath, projectDir: srcPath, outDir });
1493
+ addOutput(out || `Packaged to ${outDir}`);
1494
+ setPkgFile(pf || `${outDir}/app.${platform === "tizen" ? "wgt" : "ipk"}`);
1495
+ } else {
1496
+ setPkgFile(srcPath);
1497
+ }
1498
+ setStep(STEPS2.CONFIRM_INSTALL);
1499
+ } catch (e) {
1500
+ setError(e.message);
1501
+ setStep(STEPS2.INPUT);
1502
+ }
1503
+ };
1504
+ const runInstall = async () => {
1505
+ setStep(STEPS2.INSTALLING);
1506
+ try {
1507
+ let out;
1508
+ if (platform === "webos") {
1509
+ out = await utils.installApp({ ipkPath: pkgFile, device });
1510
+ } else if (platform === "tizen") {
1511
+ out = await utils.installApp({ wgtPath: pkgFile, serial: device });
1512
+ } else {
1513
+ out = await utils.installApp({ apkPath: pkgFile, serial: device });
1514
+ }
1515
+ addOutput(out || "Installed successfully");
1516
+ setStep(STEPS2.CONFIRM_LAUNCH);
1517
+ } catch (e) {
1518
+ setError(e.message);
1519
+ setStep(STEPS2.CONFIRM_INSTALL);
1520
+ }
1521
+ };
1522
+ const runLaunch = async () => {
1523
+ try {
1524
+ const idGuess = pkgFile.replace(/.*\//, "").replace(/[_\.].*/, "");
1525
+ let out;
1526
+ if (platform === "webos") {
1527
+ out = await utils.launchApp({ appId: idGuess, device });
1528
+ } else if (platform === "tizen") {
1529
+ out = await utils.launchApp({ appId: idGuess, serial: device });
1530
+ } else {
1531
+ out = await utils.launchApp({ packageName: idGuess, activity: ".MainActivity", serial: device });
1532
+ }
1533
+ addOutput(out || `Launched ${idGuess}`);
1534
+ } catch (e) {
1535
+ addOutput(e.message, false);
1536
+ }
1537
+ setStep(STEPS2.DONE);
1538
+ };
1539
+ useInput5(async (input, key) => {
1540
+ if (!focused) return;
1541
+ if (step === STEPS2.INPUT) {
1542
+ if (key.return) {
1543
+ if (needsPkg && inputField === 0) {
1544
+ setInputField(1);
1545
+ return;
1546
+ }
1547
+ await runDeploy();
1548
+ }
1549
+ if (key.escape) setInputField(0);
1550
+ }
1551
+ if (step === STEPS2.CONFIRM_INSTALL) {
1552
+ if (input === "y" || key.return) await runInstall();
1553
+ if (input === "n") setStep(STEPS2.DONE);
1554
+ }
1555
+ if (step === STEPS2.CONFIRM_LAUNCH) {
1556
+ if (input === "y" || key.return) await runLaunch();
1557
+ if (input === "n") setStep(STEPS2.DONE);
1558
+ }
1559
+ if (step === STEPS2.DONE) {
1560
+ if (input === "r" || key.return) {
1561
+ setStep(STEPS2.INPUT);
1562
+ setOutput([]);
1563
+ setError(null);
1564
+ setPkgFile("");
1565
+ setInputField(0);
1566
+ }
1567
+ }
1568
+ }, { isActive: focused });
1569
+ const isLoading = step === STEPS2.PACKAGING || step === STEPS2.INSTALLING;
1570
+ return /* @__PURE__ */ React9.createElement(Box9, { flexDirection: "column", padding: 1, gap: 1 }, /* @__PURE__ */ React9.createElement(Text9, { bold: true, color: meta.color }, ICONS.package, " ", getInstallLabel(platform)), device && /* @__PURE__ */ React9.createElement(Text9, { color: "gray" }, "Device: ", /* @__PURE__ */ React9.createElement(Text9, { color: "green" }, device)), !device && /* @__PURE__ */ React9.createElement(Text9, { color: "yellow" }, ICONS.warn, " No device selected \u2014 go to Devices screen first"), error && /* @__PURE__ */ React9.createElement(Box9, { borderStyle: "round", borderColor: "red", paddingX: 1 }, /* @__PURE__ */ React9.createElement(Text9, { color: "red" }, ICONS.cross, " ", error)), isLoading && /* @__PURE__ */ React9.createElement(
1571
+ Loader,
1572
+ {
1573
+ label: step === STEPS2.PACKAGING ? "Packaging..." : "Installing...",
1574
+ color: meta.color
1575
+ }
1576
+ ), step === STEPS2.INPUT && /* @__PURE__ */ React9.createElement(Box9, { flexDirection: "column", borderStyle: "round", borderColor: meta.color, paddingX: 2, paddingY: 1, gap: 1 }, /* @__PURE__ */ React9.createElement(Box9, null, /* @__PURE__ */ React9.createElement(Text9, { color: inputField === 0 ? meta.color : "gray" }, getPkgLabel(platform), ": "), inputField === 0 ? /* @__PURE__ */ React9.createElement(
1577
+ TextInput3,
1578
+ {
1579
+ value: srcPath,
1580
+ onChange: setSrcPath,
1581
+ placeholder: needsPkg ? "./myapp" : "./app.apk"
1582
+ }
1583
+ ) : /* @__PURE__ */ React9.createElement(Text9, { color: "white" }, srcPath)), needsPkg && /* @__PURE__ */ React9.createElement(Box9, null, /* @__PURE__ */ React9.createElement(Text9, { color: inputField === 1 ? meta.color : "gray" }, "Output Directory: "), inputField === 1 ? /* @__PURE__ */ React9.createElement(
1584
+ TextInput3,
1585
+ {
1586
+ value: outDir,
1587
+ onChange: setOutDir,
1588
+ placeholder: "./dist"
1589
+ }
1590
+ ) : /* @__PURE__ */ React9.createElement(Text9, { color: "white" }, outDir)), /* @__PURE__ */ React9.createElement(Text9, { dimColor: true }, "\u23CE to ", needsPkg ? "continue" : "install")), step === STEPS2.CONFIRM_INSTALL && /* @__PURE__ */ React9.createElement(Box9, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 2, paddingY: 1, gap: 1 }, /* @__PURE__ */ React9.createElement(Text9, { bold: true, color: "yellow" }, "Install package?"), /* @__PURE__ */ React9.createElement(Text9, { color: "gray" }, "File: ", /* @__PURE__ */ React9.createElement(Text9, { color: "white" }, pkgFile)), /* @__PURE__ */ React9.createElement(Text9, { color: "gray" }, "Device: ", /* @__PURE__ */ React9.createElement(Text9, { color: device ? "green" : "red" }, device || "none")), /* @__PURE__ */ React9.createElement(Text9, { color: "yellow" }, "[y] Install [n] Skip")), step === STEPS2.CONFIRM_LAUNCH && /* @__PURE__ */ React9.createElement(Box9, { flexDirection: "column", borderStyle: "round", borderColor: meta.color, paddingX: 2, paddingY: 1, gap: 1 }, /* @__PURE__ */ React9.createElement(Text9, { bold: true, color: meta.color }, "Launch app?"), /* @__PURE__ */ React9.createElement(Text9, { color: "yellow" }, "[y] Launch [n] Skip")), output.length > 0 && /* @__PURE__ */ React9.createElement(Box9, { flexDirection: "column", borderStyle: "round", borderColor: "gray", paddingX: 1 }, /* @__PURE__ */ React9.createElement(Text9, { bold: true, color: "gray" }, "Output"), output.map((o, i) => /* @__PURE__ */ React9.createElement(Text9, { key: i, color: o.ok ? "green" : "red" }, o.ok ? ICONS.check : ICONS.cross, " ", o.msg))), step === STEPS2.DONE && /* @__PURE__ */ React9.createElement(Box9, null, /* @__PURE__ */ React9.createElement(Text9, { bold: true, color: "green" }, ICONS.check, " Done! "), /* @__PURE__ */ React9.createElement(Text9, { dimColor: true }, "\u23CE or [r] restart")));
1591
+ }
1592
+
1593
+ // src/screens/Apps.js
1594
+ import React10, { useState as useState7, useEffect as useEffect5, useCallback as useCallback2 } from "react";
1595
+ import { Box as Box10, Text as Text10, useInput as useInput6 } from "ink";
1596
+ var UTILS5 = { webos: webos_exports, tizen: tizen_exports, amazon: amazon_exports, android: android_exports };
1597
+ var MODES2 = { LIST: "list", CONFIRM_REMOVE: "confirm_remove", LAUNCH_OPTS: "launch_opts" };
1598
+ function Apps({ focused, currentDevice, platform }) {
1599
+ const [apps, setApps] = useState7([]);
1600
+ const [running, setRunning] = useState7([]);
1601
+ const [loading, setLoading] = useState7(true);
1602
+ const [cursor, setCursor] = useState7(0);
1603
+ const [mode, setMode] = useState7(MODES2.LIST);
1604
+ const [status, setStatus] = useState7(null);
1605
+ const [working, setWorking] = useState7(false);
1606
+ const meta = PLATFORM_META[platform];
1607
+ const utils = UTILS5[platform];
1608
+ const device = currentDevice?.name ?? currentDevice?.serial;
1609
+ const load = useCallback2(async () => {
1610
+ if (!device) {
1611
+ setLoading(false);
1612
+ return;
1613
+ }
1614
+ setLoading(true);
1615
+ const ins = await utils.listInstalledApps(device).catch(() => []);
1616
+ setApps(ins);
1617
+ if (utils.listRunningApps) {
1618
+ const run = await utils.listRunningApps(device).catch(() => []);
1619
+ setRunning(run);
1620
+ }
1621
+ setCursor(0);
1622
+ setLoading(false);
1623
+ }, [device, platform]);
1624
+ useEffect5(() => {
1625
+ load();
1626
+ }, [device, platform]);
1627
+ const isRunning = (id) => running.includes(id);
1628
+ useInput6(async (input, key) => {
1629
+ if (!focused) return;
1630
+ if (mode === MODES2.LIST) {
1631
+ if (key.upArrow) setCursor((c2) => Math.max(0, c2 - 1));
1632
+ if (key.downArrow) setCursor((c2) => Math.min(apps.length - 1, c2 + 1));
1633
+ if (input === "r") {
1634
+ setStatus(null);
1635
+ load();
1636
+ return;
1637
+ }
1638
+ const app = apps[cursor];
1639
+ if (!app) return;
1640
+ if (input === "l" || key.return) {
1641
+ setWorking(true);
1642
+ try {
1643
+ if (platform === "webos") {
1644
+ if (isRunning(app.id)) {
1645
+ await utils.closeApp({ appId: app.id, device });
1646
+ setStatus({ ok: true, msg: `Stopped ${app.id}` });
1647
+ } else {
1648
+ await utils.launchApp({ appId: app.id, device });
1649
+ setStatus({ ok: true, msg: `Launched ${app.id}` });
1650
+ }
1651
+ } else if (platform === "tizen") {
1652
+ await utils.launchApp({ appId: app.id, serial: device });
1653
+ setStatus({ ok: true, msg: `Launched ${app.id}` });
1654
+ } else {
1655
+ if (isRunning(app.id)) {
1656
+ await utils.stopApp({ packageName: app.id, serial: device });
1657
+ setStatus({ ok: true, msg: `Stopped ${app.id}` });
1658
+ } else {
1659
+ await utils.launchApp({ packageName: app.id, activity: ".MainActivity", serial: device });
1660
+ setStatus({ ok: true, msg: `Launched ${app.id}` });
1661
+ }
1662
+ }
1663
+ await load();
1664
+ } catch (e) {
1665
+ setStatus({ ok: false, msg: e.message });
1666
+ }
1667
+ setWorking(false);
1668
+ }
1669
+ if (input === "d") setMode(MODES2.CONFIRM_REMOVE);
1670
+ }
1671
+ if (mode === MODES2.CONFIRM_REMOVE) {
1672
+ if (input === "y") {
1673
+ setWorking(true);
1674
+ try {
1675
+ const app = apps[cursor];
1676
+ if (platform === "webos") {
1677
+ await utils.removeApp({ appId: app.id, device });
1678
+ } else if (platform === "tizen") {
1679
+ await utils.removeApp({ appId: app.id, serial: device });
1680
+ } else {
1681
+ await utils.removeApp({ packageName: app.id, serial: device });
1682
+ }
1683
+ setStatus({ ok: true, msg: `Removed ${apps[cursor].id}` });
1684
+ await load();
1685
+ } catch (e) {
1686
+ setStatus({ ok: false, msg: e.message });
1687
+ }
1688
+ setMode(MODES2.LIST);
1689
+ setWorking(false);
1690
+ }
1691
+ if (input === "n" || key.escape) setMode(MODES2.LIST);
1692
+ }
1693
+ }, { isActive: focused });
1694
+ if (!device) {
1695
+ return /* @__PURE__ */ React10.createElement(Box10, { flexDirection: "column", padding: 2 }, /* @__PURE__ */ React10.createElement(Text10, { color: "yellow" }, ICONS.warn, " No device selected."), /* @__PURE__ */ React10.createElement(Text10, { color: "gray" }, "Go to Devices screen and set an active device."));
1696
+ }
1697
+ if (loading) return /* @__PURE__ */ React10.createElement(Box10, { padding: 2 }, /* @__PURE__ */ React10.createElement(Loader, { label: "Loading apps...", color: meta.color }));
1698
+ return /* @__PURE__ */ React10.createElement(Box10, { flexDirection: "column", padding: 1, gap: 1 }, /* @__PURE__ */ React10.createElement(Text10, { bold: true, color: meta.color }, ICONS.apps, " App Manager ", /* @__PURE__ */ React10.createElement(Text10, { color: "gray" }, "[", device, "]")), status && /* @__PURE__ */ React10.createElement(Box10, { borderStyle: "round", borderColor: status.ok ? "green" : "red", paddingX: 1 }, /* @__PURE__ */ React10.createElement(Text10, { color: status.ok ? "green" : "red" }, status.ok ? ICONS.check : ICONS.cross, " ", status.msg)), working && /* @__PURE__ */ React10.createElement(Loader, { label: "Working...", color: meta.color }), /* @__PURE__ */ React10.createElement(Box10, { flexDirection: "column", borderStyle: "round", borderColor: meta.color, paddingX: 1 }, /* @__PURE__ */ React10.createElement(Box10, { marginBottom: 1 }, /* @__PURE__ */ React10.createElement(Text10, { bold: true, color: "gray" }, "Status".padEnd(10)), /* @__PURE__ */ React10.createElement(Text10, { bold: true, color: "gray" }, "Package / App ID".padEnd(40)), /* @__PURE__ */ React10.createElement(Text10, { bold: true, color: "gray" }, "Version")), apps.length === 0 && /* @__PURE__ */ React10.createElement(Text10, { color: "gray" }, " No apps installed on ", device), apps.map((app, i) => {
1699
+ const run = isRunning(app.id);
1700
+ return /* @__PURE__ */ React10.createElement(Box10, { key: app.id }, /* @__PURE__ */ React10.createElement(
1701
+ Text10,
1702
+ {
1703
+ bold: focused && i === cursor,
1704
+ color: focused && i === cursor ? meta.color : "white"
1705
+ },
1706
+ focused && i === cursor ? "\u25B6 " : " ",
1707
+ /* @__PURE__ */ React10.createElement(Text10, { color: run ? "green" : "gray" }, run ? `${ICONS.running} RUN ` : `${ICONS.stopped} STOP`),
1708
+ " ",
1709
+ app.id.padEnd(38),
1710
+ /* @__PURE__ */ React10.createElement(Text10, { color: "gray" }, " ", app.version)
1711
+ ));
1712
+ })), mode === MODES2.CONFIRM_REMOVE && apps[cursor] && /* @__PURE__ */ React10.createElement(Box10, { borderStyle: "round", borderColor: "red", paddingX: 2, paddingY: 1 }, /* @__PURE__ */ React10.createElement(Text10, { color: "red" }, "Remove "), /* @__PURE__ */ React10.createElement(Text10, { bold: true, color: "white" }, apps[cursor].id), /* @__PURE__ */ React10.createElement(Text10, { color: "red" }, "? "), /* @__PURE__ */ React10.createElement(Text10, { color: "yellow" }, "[y] Yes [n] No")), /* @__PURE__ */ React10.createElement(Text10, { dimColor: true }, "\u2191\u2193 navigate \u23CE/[l] Launch/Stop [d] Remove [r] Refresh"));
1713
+ }
1714
+
1715
+ // src/screens/Logs.js
1716
+ import React11, { useState as useState8, useEffect as useEffect6, useRef } from "react";
1717
+ import { Box as Box11, Text as Text11, useInput as useInput7 } from "ink";
1718
+ var UTILS6 = { webos: webos_exports, tizen: tizen_exports, amazon: amazon_exports, android: android_exports };
1719
+ var MAX_LINES = 50;
1720
+ function Logs({ focused, currentDevice, platform }) {
1721
+ const [lines, setLines] = useState8([]);
1722
+ const [paused, setPaused] = useState8(false);
1723
+ const [streaming, setStreaming] = useState8(false);
1724
+ const [error, setError] = useState8(null);
1725
+ const procRef = useRef(null);
1726
+ const meta = PLATFORM_META[platform];
1727
+ const utils = UTILS6[platform];
1728
+ const device = currentDevice?.name ?? currentDevice?.serial;
1729
+ function startStream() {
1730
+ if (procRef.current) {
1731
+ procRef.current.kill();
1732
+ procRef.current = null;
1733
+ }
1734
+ setLines([]);
1735
+ setError(null);
1736
+ setStreaming(true);
1737
+ try {
1738
+ const opts = platform === "webos" ? { device, follow: true, lines: 50 } : { serial: device };
1739
+ const proc = utils.spawnLogStream(opts);
1740
+ procRef.current = proc;
1741
+ proc.stdout?.on("data", (chunk) => {
1742
+ if (paused) return;
1743
+ const newLines = chunk.toString().split("\n").filter(Boolean);
1744
+ setLines((prev) => [...prev, ...newLines].slice(-MAX_LINES));
1745
+ });
1746
+ proc.stderr?.on("data", (chunk) => {
1747
+ setLines((prev) => [...prev, `[stderr] ${chunk.toString().trim()}`].slice(-MAX_LINES));
1748
+ });
1749
+ proc.catch((e) => {
1750
+ if (e.signal !== "SIGTERM") setError(e.message);
1751
+ setStreaming(false);
1752
+ });
1753
+ } catch (e) {
1754
+ setError(e.message);
1755
+ setStreaming(false);
1756
+ }
1757
+ }
1758
+ useEffect6(() => {
1759
+ if (device && focused) startStream();
1760
+ return () => {
1761
+ procRef.current?.kill();
1762
+ procRef.current = null;
1763
+ };
1764
+ }, [device, focused, platform]);
1765
+ useInput7((input) => {
1766
+ if (!focused) return;
1767
+ if (input === "p") setPaused((p) => !p);
1768
+ if (input === "c") setLines([]);
1769
+ if (input === "r") startStream();
1770
+ }, { isActive: focused });
1771
+ if (!device) {
1772
+ return /* @__PURE__ */ React11.createElement(Box11, { flexDirection: "column", padding: 2 }, /* @__PURE__ */ React11.createElement(Text11, { color: "yellow" }, ICONS.warn, " No device selected."));
1773
+ }
1774
+ return /* @__PURE__ */ React11.createElement(Box11, { flexDirection: "column", padding: 1, gap: 1 }, /* @__PURE__ */ React11.createElement(Box11, { justifyContent: "space-between" }, /* @__PURE__ */ React11.createElement(Text11, { bold: true, color: meta.color }, ICONS.logs, " Log Viewer ", /* @__PURE__ */ React11.createElement(Text11, { color: "gray" }, "[", device, "]")), /* @__PURE__ */ React11.createElement(Text11, { color: paused ? "yellow" : "green" }, paused ? `${ICONS.stopped} PAUSED` : streaming ? `${ICONS.running} LIVE` : `${ICONS.disconnected} STOPPED`)), error && /* @__PURE__ */ React11.createElement(Box11, { borderStyle: "round", borderColor: "red", paddingX: 1 }, /* @__PURE__ */ React11.createElement(Text11, { color: "red" }, ICONS.cross, " ", error)), !streaming && !error && /* @__PURE__ */ React11.createElement(Loader, { label: "Starting log stream...", color: meta.color }), /* @__PURE__ */ React11.createElement(
1775
+ Box11,
1776
+ {
1777
+ flexDirection: "column",
1778
+ borderStyle: "round",
1779
+ borderColor: "gray",
1780
+ paddingX: 1,
1781
+ height: Math.max(10, (process.stdout.rows || 30) - 12)
1782
+ },
1783
+ lines.length === 0 && /* @__PURE__ */ React11.createElement(Text11, { color: "gray" }, " Waiting for log output..."),
1784
+ lines.map((line, i) => {
1785
+ const isError = /error|fatal|critical|\sE\//i.test(line);
1786
+ const isWarning = /warn|\sW\//i.test(line);
1787
+ return /* @__PURE__ */ React11.createElement(
1788
+ Text11,
1789
+ {
1790
+ key: i,
1791
+ color: isError ? "red" : isWarning ? "yellow" : "white",
1792
+ wrap: "truncate"
1793
+ },
1794
+ line
1795
+ );
1796
+ })
1797
+ ), /* @__PURE__ */ React11.createElement(Text11, { dimColor: true }, "[p] Pause/Resume [c] Clear [r] Restart ", lines.length, "/", MAX_LINES, " lines"));
1798
+ }
1799
+
1800
+ // src/screens/Transfer.js
1801
+ import React12, { useState as useState9 } from "react";
1802
+ import { Box as Box12, Text as Text12, useInput as useInput8 } from "ink";
1803
+ import TextInput4 from "ink-text-input";
1804
+ var UTILS7 = { webos: webos_exports, tizen: tizen_exports, amazon: amazon_exports, android: android_exports };
1805
+ var DIR = { PUSH: "push", PULL: "pull" };
1806
+ var STEP = { SELECT_DIR: 0, SRC: 1, DEST: 2, DONE: 3 };
1807
+ function Transfer({ focused, currentDevice, platform }) {
1808
+ const [direction, setDirection] = useState9(DIR.PUSH);
1809
+ const [step, setStep] = useState9(STEP.SELECT_DIR);
1810
+ const [src, setSrc] = useState9("");
1811
+ const [dest, setDest] = useState9("");
1812
+ const [working, setWorking] = useState9(false);
1813
+ const [result, setResult] = useState9(null);
1814
+ const [error, setError] = useState9(null);
1815
+ const meta = PLATFORM_META[platform];
1816
+ const utils = UTILS7[platform];
1817
+ const device = currentDevice?.name ?? currentDevice?.serial;
1818
+ async function runTransfer() {
1819
+ setWorking(true);
1820
+ setError(null);
1821
+ try {
1822
+ let out;
1823
+ const isPush2 = direction === DIR.PUSH;
1824
+ const pushOpts = platform === "webos" ? { localPath: src, remotePath: dest, device } : { localPath: src, remotePath: dest, serial: device };
1825
+ const pullOpts = platform === "webos" ? { remotePath: src, localPath: dest, device } : { remotePath: src, localPath: dest, serial: device };
1826
+ out = isPush2 ? await utils.pushFiles(pushOpts) : await utils.pullFiles(pullOpts);
1827
+ setResult(out || "Transfer complete");
1828
+ setStep(STEP.DONE);
1829
+ } catch (e) {
1830
+ setError(e.message);
1831
+ }
1832
+ setWorking(false);
1833
+ }
1834
+ function reset() {
1835
+ setStep(STEP.SELECT_DIR);
1836
+ setSrc("");
1837
+ setDest("");
1838
+ setResult(null);
1839
+ setError(null);
1840
+ }
1841
+ useInput8((input, key) => {
1842
+ if (!focused) return;
1843
+ if (step === STEP.SELECT_DIR) {
1844
+ if (input === "p") {
1845
+ setDirection(DIR.PUSH);
1846
+ setStep(STEP.SRC);
1847
+ }
1848
+ if (input === "l") {
1849
+ setDirection(DIR.PULL);
1850
+ setStep(STEP.SRC);
1851
+ }
1852
+ }
1853
+ if (step === STEP.SRC && key.return) setStep(STEP.DEST);
1854
+ if (step === STEP.DEST && key.return) runTransfer();
1855
+ if (step === STEP.DONE && (key.return || input === "r")) reset();
1856
+ if (key.escape && step !== STEP.SELECT_DIR) setStep((s) => Math.max(0, s - 1));
1857
+ }, { isActive: focused });
1858
+ if (!device) {
1859
+ return /* @__PURE__ */ React12.createElement(Box12, { flexDirection: "column", padding: 2 }, /* @__PURE__ */ React12.createElement(Text12, { color: "yellow" }, ICONS.warn, " No device selected."));
1860
+ }
1861
+ const isPush = direction === DIR.PUSH;
1862
+ const srcLabel = isPush ? "Local path (host)" : "Remote path (device)";
1863
+ const destLabel = isPush ? "Remote path (device)" : "Local path (host)";
1864
+ return /* @__PURE__ */ React12.createElement(Box12, { flexDirection: "column", padding: 1, gap: 1 }, /* @__PURE__ */ React12.createElement(Text12, { bold: true, color: meta.color }, ICONS.transfer, " File Transfer ", /* @__PURE__ */ React12.createElement(Text12, { color: "gray" }, "[", device, "]")), error && /* @__PURE__ */ React12.createElement(Box12, { borderStyle: "round", borderColor: "red", paddingX: 1 }, /* @__PURE__ */ React12.createElement(Text12, { color: "red" }, ICONS.cross, " ", error)), working && /* @__PURE__ */ React12.createElement(Loader, { label: `${isPush ? "Pushing" : "Pulling"} files...`, color: meta.color }), step === STEP.SELECT_DIR && /* @__PURE__ */ React12.createElement(Box12, { flexDirection: "column", borderStyle: "round", borderColor: meta.color, paddingX: 2, paddingY: 1, gap: 1 }, /* @__PURE__ */ React12.createElement(Text12, { bold: true }, "Transfer Direction"), /* @__PURE__ */ React12.createElement(Box12, null, /* @__PURE__ */ React12.createElement(Text12, { color: meta.color }, "\u25B6 "), /* @__PURE__ */ React12.createElement(Text12, { bold: true }, "[p] Push "), /* @__PURE__ */ React12.createElement(Text12, { color: "gray" }, "Host \u2192 Device")), /* @__PURE__ */ React12.createElement(Box12, null, /* @__PURE__ */ React12.createElement(Text12, { color: "magenta" }, "\u25C0 "), /* @__PURE__ */ React12.createElement(Text12, { bold: true }, "[l] Pull "), /* @__PURE__ */ React12.createElement(Text12, { color: "gray" }, "Device \u2192 Host"))), step === STEP.SRC && !working && /* @__PURE__ */ React12.createElement(Box12, { flexDirection: "column", borderStyle: "round", borderColor: meta.color, paddingX: 2, paddingY: 1, gap: 1 }, /* @__PURE__ */ React12.createElement(Text12, { bold: true, color: isPush ? meta.color : "magenta" }, isPush ? "\u25B6 Push" : "\u25C0 Pull"), /* @__PURE__ */ React12.createElement(Box12, null, /* @__PURE__ */ React12.createElement(Text12, { color: meta.color }, srcLabel, ": "), /* @__PURE__ */ React12.createElement(
1865
+ TextInput4,
1866
+ {
1867
+ value: src,
1868
+ onChange: setSrc,
1869
+ placeholder: isPush ? "./local/file.txt" : "/data/file.txt"
1870
+ }
1871
+ )), /* @__PURE__ */ React12.createElement(Text12, { dimColor: true }, "\u23CE next Esc back")), step === STEP.DEST && !working && /* @__PURE__ */ React12.createElement(Box12, { flexDirection: "column", borderStyle: "round", borderColor: meta.color, paddingX: 2, paddingY: 1, gap: 1 }, /* @__PURE__ */ React12.createElement(Text12, { bold: true }, srcLabel, ": ", /* @__PURE__ */ React12.createElement(Text12, { color: "white" }, src)), /* @__PURE__ */ React12.createElement(Box12, null, /* @__PURE__ */ React12.createElement(Text12, { color: meta.color }, destLabel, ": "), /* @__PURE__ */ React12.createElement(
1872
+ TextInput4,
1873
+ {
1874
+ value: dest,
1875
+ onChange: setDest,
1876
+ placeholder: isPush ? "/data/file.txt" : "./local/file.txt"
1877
+ }
1878
+ )), /* @__PURE__ */ React12.createElement(Text12, { dimColor: true }, "\u23CE transfer Esc back")), step === STEP.DONE && /* @__PURE__ */ React12.createElement(Box12, { flexDirection: "column", borderStyle: "round", borderColor: "green", paddingX: 2, paddingY: 1, gap: 1 }, /* @__PURE__ */ React12.createElement(Text12, { bold: true, color: "green" }, ICONS.check, " Transfer Complete!"), /* @__PURE__ */ React12.createElement(Text12, { color: "gray" }, result), /* @__PURE__ */ React12.createElement(Text12, { dimColor: true }, "\u23CE or [r] again")));
1879
+ }
1880
+
1881
+ // src/screens/Shell.js
1882
+ import React13, { useState as useState10 } from "react";
1883
+ import { Box as Box13, Text as Text13, useInput as useInput9 } from "ink";
1884
+ import TextInput5 from "ink-text-input";
1885
+ var UTILS8 = { webos: webos_exports, tizen: tizen_exports, amazon: amazon_exports, android: android_exports };
1886
+ var MAX_HISTORY = 100;
1887
+ function Shell({ focused, currentDevice, platform }) {
1888
+ const [input, setInput] = useState10("");
1889
+ const [history, setHistory] = useState10([]);
1890
+ const [cmdHist, setCmdHist] = useState10([]);
1891
+ const [histIdx, setHistIdx] = useState10(-1);
1892
+ const [working, setWorking] = useState10(false);
1893
+ const meta = PLATFORM_META[platform];
1894
+ const utils = UTILS8[platform];
1895
+ const device = currentDevice?.name ?? currentDevice?.serial;
1896
+ async function execute(cmd) {
1897
+ if (!cmd.trim()) return;
1898
+ const entry = { cmd, output: null, error: null, ts: (/* @__PURE__ */ new Date()).toLocaleTimeString() };
1899
+ setHistory((h) => [...h, entry].slice(-MAX_HISTORY));
1900
+ setCmdHist((h) => [cmd, ...h.filter((c2) => c2 !== cmd)].slice(-50));
1901
+ setHistIdx(-1);
1902
+ setInput("");
1903
+ setWorking(true);
1904
+ try {
1905
+ const opts = platform === "webos" ? { command: cmd, device } : { command: cmd, serial: device };
1906
+ const out = await utils.runShellCommand(opts);
1907
+ setHistory((h) => {
1908
+ const copy = [...h];
1909
+ copy[copy.length - 1] = { ...copy[copy.length - 1], output: out };
1910
+ return copy;
1911
+ });
1912
+ } catch (e) {
1913
+ setHistory((h) => {
1914
+ const copy = [...h];
1915
+ copy[copy.length - 1] = { ...copy[copy.length - 1], error: e.message };
1916
+ return copy;
1917
+ });
1918
+ }
1919
+ setWorking(false);
1920
+ }
1921
+ useInput9((inp, key) => {
1922
+ if (!focused) return;
1923
+ if (key.upArrow && cmdHist.length > 0) {
1924
+ const next = Math.min(histIdx + 1, cmdHist.length - 1);
1925
+ setHistIdx(next);
1926
+ setInput(cmdHist[next]);
1927
+ }
1928
+ if (key.downArrow) {
1929
+ const next = histIdx - 1;
1930
+ if (next < 0) {
1931
+ setHistIdx(-1);
1932
+ setInput("");
1933
+ } else {
1934
+ setHistIdx(next);
1935
+ setInput(cmdHist[next]);
1936
+ }
1937
+ }
1938
+ if (inp === "c" && key.ctrl) setHistory([]);
1939
+ }, { isActive: focused });
1940
+ if (!device) {
1941
+ return /* @__PURE__ */ React13.createElement(Box13, { flexDirection: "column", padding: 2 }, /* @__PURE__ */ React13.createElement(Text13, { color: "yellow" }, ICONS.warn, " No device selected."));
1942
+ }
1943
+ const visible = history.slice(-(process.stdout.rows - 12 || 10));
1944
+ return /* @__PURE__ */ React13.createElement(Box13, { flexDirection: "column", padding: 1, gap: 1 }, /* @__PURE__ */ React13.createElement(Text13, { bold: true, color: meta.color }, ICONS.shell, " Shell ", /* @__PURE__ */ React13.createElement(Text13, { color: "gray" }, "[", device, "]")), /* @__PURE__ */ React13.createElement(
1945
+ Box13,
1946
+ {
1947
+ flexDirection: "column",
1948
+ borderStyle: "round",
1949
+ borderColor: "gray",
1950
+ paddingX: 1,
1951
+ height: Math.max(8, (process.stdout.rows || 30) - 12)
1952
+ },
1953
+ history.length === 0 && /* @__PURE__ */ React13.createElement(Text13, { color: "gray" }, " Type a command below and press Enter to run it on the device."),
1954
+ visible.map((entry, i) => /* @__PURE__ */ React13.createElement(Box13, { key: i, flexDirection: "column" }, /* @__PURE__ */ React13.createElement(Text13, { color: meta.color }, /* @__PURE__ */ React13.createElement(Text13, { dimColor: true }, "[", entry.ts, "] "), "$ ", entry.cmd), entry.output != null && /* @__PURE__ */ React13.createElement(Text13, { color: "white", wrap: "wrap" }, entry.output), entry.error != null && /* @__PURE__ */ React13.createElement(Text13, { color: "red" }, ICONS.cross, " ", entry.error))),
1955
+ working && /* @__PURE__ */ React13.createElement(Loader, { label: "Running...", color: meta.color })
1956
+ ), /* @__PURE__ */ React13.createElement(Box13, { borderStyle: "round", borderColor: focused ? meta.color : "gray", paddingX: 1 }, /* @__PURE__ */ React13.createElement(Text13, { color: meta.color }, "$ "), /* @__PURE__ */ React13.createElement(
1957
+ TextInput5,
1958
+ {
1959
+ value: input,
1960
+ onChange: setInput,
1961
+ onSubmit: execute,
1962
+ placeholder: "Enter command... (\u2191\u2193 history, Ctrl+C clear)",
1963
+ focus: focused && !working
1964
+ }
1965
+ )), /* @__PURE__ */ React13.createElement(Text13, { dimColor: true }, "\u2191\u2193 command history Ctrl+C clear output"));
1966
+ }
1967
+
1968
+ // src/screens/Inspector.js
1969
+ import React14, { useState as useState11, useEffect as useEffect7, useCallback as useCallback3 } from "react";
1970
+ import { Box as Box14, Text as Text14, useInput as useInput10 } from "ink";
1971
+ function Inspector({ focused, currentDevice, platform }) {
1972
+ const [apps, setApps] = useState11([]);
1973
+ const [loading, setLoading] = useState11(true);
1974
+ const [cursor, setCursor] = useState11(0);
1975
+ const [working, setWorking] = useState11(false);
1976
+ const [result, setResult] = useState11(null);
1977
+ const [error, setError] = useState11(null);
1978
+ const meta = PLATFORM_META[platform];
1979
+ const device = currentDevice?.name ?? currentDevice?.serial;
1980
+ const load = useCallback3(async () => {
1981
+ if (!device || platform !== "webos") {
1982
+ setLoading(false);
1983
+ return;
1984
+ }
1985
+ setLoading(true);
1986
+ const list = await listInstalledApps(device).catch(() => []);
1987
+ setApps(list);
1988
+ setLoading(false);
1989
+ }, [device, platform]);
1990
+ useEffect7(() => {
1991
+ load();
1992
+ }, [device, platform]);
1993
+ useInput10(async (input, key) => {
1994
+ if (!focused) return;
1995
+ if (platform === "webos") {
1996
+ if (key.upArrow) setCursor((c2) => Math.max(0, c2 - 1));
1997
+ if (key.downArrow) setCursor((c2) => Math.min(apps.length - 1, c2 + 1));
1998
+ if (input === "r") {
1999
+ setResult(null);
2000
+ setError(null);
2001
+ load();
2002
+ }
2003
+ if (key.return && apps[cursor]) {
2004
+ setWorking(true);
2005
+ setResult(null);
2006
+ setError(null);
2007
+ try {
2008
+ const out = await launchInspector({ appId: apps[cursor].id, device });
2009
+ setResult(out || "Web Inspector launched. Open the URL in your browser.");
2010
+ } catch (e) {
2011
+ setError(e.message);
2012
+ }
2013
+ setWorking(false);
2014
+ }
2015
+ }
2016
+ }, { isActive: focused });
2017
+ if (!device) {
2018
+ return /* @__PURE__ */ React14.createElement(Box14, { flexDirection: "column", padding: 2 }, /* @__PURE__ */ React14.createElement(Text14, { color: "yellow" }, ICONS.warn, " No device selected."));
2019
+ }
2020
+ if (platform !== "webos") {
2021
+ const debugInfo = {
2022
+ amazon: [
2023
+ "Enable ADB debugging in Fire TV Settings \u2192 My Fire TV \u2192 Developer Options",
2024
+ "Use Chrome DevTools via: adb forward tcp:9222 localabstract:chrome_devtools_remote",
2025
+ "Open chrome://inspect in Chrome to see connected targets",
2026
+ "LoggingCtl tool available for system log management"
2027
+ ],
2028
+ tizen: [
2029
+ "Enable Developer Mode in Tizen Settings \u2192 General \u2192 System Manager",
2030
+ "Use Remote Web Inspector via Tizen Studio Tools \u2192 Device Manager",
2031
+ "Connect via Chrome DevTools with sdb forward"
2032
+ ],
2033
+ android: [
2034
+ "Enable USB Debugging in Developer Options",
2035
+ "Use Android Studio Profiler or Chrome DevTools",
2036
+ "adb shell am start --enable-debugging for debug builds",
2037
+ "chrome://inspect works for WebView-based apps"
2038
+ ]
2039
+ };
2040
+ return /* @__PURE__ */ React14.createElement(Box14, { flexDirection: "column", padding: 1, gap: 1 }, /* @__PURE__ */ React14.createElement(Text14, { bold: true, color: meta.color }, ICONS.debug, " Debug Info ", /* @__PURE__ */ React14.createElement(Text14, { color: "gray" }, "[", meta.label, "]")), /* @__PURE__ */ React14.createElement(Box14, { flexDirection: "column", borderStyle: "round", borderColor: meta.color, paddingX: 2, paddingY: 1, gap: 1 }, /* @__PURE__ */ React14.createElement(Text14, { bold: true }, "Setup Steps"), (debugInfo[platform] ?? []).map((line, i) => /* @__PURE__ */ React14.createElement(Box14, { key: i }, /* @__PURE__ */ React14.createElement(Text14, { color: meta.color }, ICONS.arrow, " "), /* @__PURE__ */ React14.createElement(Text14, { color: "white", wrap: "wrap" }, line)))), /* @__PURE__ */ React14.createElement(Box14, { borderStyle: "round", borderColor: "gray", paddingX: 2, paddingY: 1 }, /* @__PURE__ */ React14.createElement(Text14, { dimColor: true }, ICONS.info, " For ", meta.label, " debugging, use the platform's native tools alongside tvdev.")));
2041
+ }
2042
+ if (loading) return /* @__PURE__ */ React14.createElement(Box14, { padding: 2 }, /* @__PURE__ */ React14.createElement(Loader, { label: "Loading apps...", color: meta.color }));
2043
+ return /* @__PURE__ */ React14.createElement(Box14, { flexDirection: "column", padding: 1, gap: 1 }, /* @__PURE__ */ React14.createElement(Text14, { bold: true, color: meta.color }, ICONS.debug, " Web Inspector ", /* @__PURE__ */ React14.createElement(Text14, { color: "gray" }, "[", device, "]")), /* @__PURE__ */ React14.createElement(Text14, { color: "gray" }, "Select an app to open the Web Inspector (ares-inspect)."), result && /* @__PURE__ */ React14.createElement(Box14, { borderStyle: "round", borderColor: "green", paddingX: 2, paddingY: 1 }, /* @__PURE__ */ React14.createElement(Text14, { bold: true, color: "green" }, ICONS.check, " Inspector launched!"), /* @__PURE__ */ React14.createElement(Text14, { color: "gray" }, "\n", result)), error && /* @__PURE__ */ React14.createElement(Box14, { borderStyle: "round", borderColor: "red", paddingX: 1 }, /* @__PURE__ */ React14.createElement(Text14, { color: "red" }, ICONS.cross, " ", error)), working && /* @__PURE__ */ React14.createElement(Loader, { label: "Launching Inspector...", color: meta.color }), !working && /* @__PURE__ */ React14.createElement(Box14, { flexDirection: "column", borderStyle: "round", borderColor: meta.color, paddingX: 1, paddingY: 1 }, /* @__PURE__ */ React14.createElement(Text14, { bold: true, marginBottom: 1 }, "Installed Apps"), apps.length === 0 && /* @__PURE__ */ React14.createElement(Text14, { color: "gray" }, " No apps found on device."), apps.map((app, i) => /* @__PURE__ */ React14.createElement(Box14, { key: app.id }, /* @__PURE__ */ React14.createElement(
2044
+ Text14,
2045
+ {
2046
+ bold: focused && i === cursor,
2047
+ color: focused && i === cursor ? meta.color : "white"
2048
+ },
2049
+ focused && i === cursor ? "\u25B6 " : " ",
2050
+ app.id,
2051
+ app.version && /* @__PURE__ */ React14.createElement(Text14, { dimColor: true }, " v", app.version)
2052
+ )))), /* @__PURE__ */ React14.createElement(Box14, { flexDirection: "column", borderStyle: "round", borderColor: "gray", paddingX: 1 }, /* @__PURE__ */ React14.createElement(Text14, { dimColor: true }, ICONS.info, " After launching, open the URL in your browser to debug the app.")), /* @__PURE__ */ React14.createElement(Text14, { dimColor: true }, "\u2191\u2193 select \u23CE launch inspector [r] refresh"));
2053
+ }
2054
+
2055
+ // src/screens/Build.js
2056
+ import React15, { useState as useState12 } from "react";
2057
+ import { Box as Box15, Text as Text15, useInput as useInput11 } from "ink";
2058
+ import TextInput6 from "ink-text-input";
2059
+ var UTILS9 = { tizen: tizen_exports, android: android_exports };
2060
+ var TASKS = {
2061
+ android: [
2062
+ { id: "assembleDebug", label: "Debug Build", hint: "./gradlew assembleDebug" },
2063
+ { id: "assembleRelease", label: "Release Build", hint: "./gradlew assembleRelease" },
2064
+ { id: "test", label: "Run Tests", hint: "./gradlew test" },
2065
+ { id: "clean", label: "Clean", hint: "./gradlew clean" }
2066
+ ],
2067
+ tizen: [
2068
+ { id: "build-web", label: "Build Web App", hint: "tizen build-web" },
2069
+ { id: "package-wgt", label: "Package \u2192 .wgt", hint: "tizen package -t wgt" }
2070
+ ]
2071
+ };
2072
+ var STEPS3 = { SELECT: 0, DIR: 1, BUILDING: 2, DONE: 3 };
2073
+ function Build({ focused, platform }) {
2074
+ const meta = PLATFORM_META[platform];
2075
+ const utils = UTILS9[platform];
2076
+ const tasks = TASKS[platform] ?? TASKS.android;
2077
+ const [taskIdx, setTaskIdx] = useState12(0);
2078
+ const [step, setStep] = useState12(STEPS3.SELECT);
2079
+ const [projDir, setProjDir] = useState12(".");
2080
+ const [working, setWorking] = useState12(false);
2081
+ const [result, setResult] = useState12(null);
2082
+ const [error, setError] = useState12(null);
2083
+ async function runBuild() {
2084
+ setStep(STEPS3.BUILDING);
2085
+ setError(null);
2086
+ setWorking(true);
2087
+ try {
2088
+ let out;
2089
+ if (platform === "android") {
2090
+ out = await utils.buildProject({
2091
+ task: tasks[taskIdx].id,
2092
+ projectDir: projDir,
2093
+ useWrapper: true
2094
+ });
2095
+ setResult(out.success ? `Build successful${out.apkPath ? `
2096
+ APK: ${out.apkPath}` : ""}` : "Build failed \u2014 check output");
2097
+ } else {
2098
+ out = await utils.buildApp({ projectDir: projDir });
2099
+ setResult(out || "Build complete");
2100
+ }
2101
+ setStep(STEPS3.DONE);
2102
+ } catch (e) {
2103
+ setError(e.message);
2104
+ setStep(STEPS3.DIR);
2105
+ }
2106
+ setWorking(false);
2107
+ }
2108
+ useInput11(async (input, key) => {
2109
+ if (!focused) return;
2110
+ if (step === STEPS3.SELECT) {
2111
+ if (key.upArrow) setTaskIdx((i) => Math.max(0, i - 1));
2112
+ if (key.downArrow) setTaskIdx((i) => Math.min(tasks.length - 1, i + 1));
2113
+ if (key.return) setStep(STEPS3.DIR);
2114
+ }
2115
+ if (step === STEPS3.DIR && key.return) await runBuild();
2116
+ if (step === STEPS3.DIR && key.escape) setStep(STEPS3.SELECT);
2117
+ if (step === STEPS3.DONE && (key.return || input === "r")) {
2118
+ setStep(STEPS3.SELECT);
2119
+ setResult(null);
2120
+ setError(null);
2121
+ }
2122
+ }, { isActive: focused });
2123
+ return /* @__PURE__ */ React15.createElement(Box15, { flexDirection: "column", padding: 1, gap: 1 }, /* @__PURE__ */ React15.createElement(Text15, { bold: true, color: meta.color }, ICONS.build, " Build ", /* @__PURE__ */ React15.createElement(Text15, { color: "gray" }, "[", meta.label, "]")), error && /* @__PURE__ */ React15.createElement(Box15, { borderStyle: "round", borderColor: "red", paddingX: 1 }, /* @__PURE__ */ React15.createElement(Text15, { color: "red" }, ICONS.cross, " ", error)), working && /* @__PURE__ */ React15.createElement(Loader, { label: "Building...", color: meta.color }), step === STEPS3.SELECT && /* @__PURE__ */ React15.createElement(Box15, { flexDirection: "column", borderStyle: "round", borderColor: meta.color, paddingX: 1, paddingY: 1, gap: 1 }, /* @__PURE__ */ React15.createElement(Text15, { bold: true }, "Select Build Task"), tasks.map((t, i) => /* @__PURE__ */ React15.createElement(Box15, { key: t.id }, /* @__PURE__ */ React15.createElement(
2124
+ Text15,
2125
+ {
2126
+ bold: focused && i === taskIdx,
2127
+ color: focused && i === taskIdx ? meta.color : "white"
2128
+ },
2129
+ focused && i === taskIdx ? "\u25B6 " : " ",
2130
+ t.label.padEnd(20),
2131
+ /* @__PURE__ */ React15.createElement(Text15, { dimColor: true }, " ", t.hint)
2132
+ ))), /* @__PURE__ */ React15.createElement(Text15, { dimColor: true, marginTop: 1 }, "\u2191\u2193 select \u23CE continue")), step === STEPS3.DIR && !working && /* @__PURE__ */ React15.createElement(Box15, { flexDirection: "column", borderStyle: "round", borderColor: meta.color, paddingX: 2, paddingY: 1, gap: 1 }, /* @__PURE__ */ React15.createElement(Text15, { bold: true }, "Task: ", /* @__PURE__ */ React15.createElement(Text15, { color: meta.color }, tasks[taskIdx]?.label)), /* @__PURE__ */ React15.createElement(Box15, null, /* @__PURE__ */ React15.createElement(Text15, { color: meta.color }, "Project Dir: "), /* @__PURE__ */ React15.createElement(
2133
+ TextInput6,
2134
+ {
2135
+ value: projDir,
2136
+ onChange: setProjDir,
2137
+ placeholder: "."
2138
+ }
2139
+ )), /* @__PURE__ */ React15.createElement(Text15, { dimColor: true }, "\u23CE build Esc back")), step === STEPS3.DONE && /* @__PURE__ */ React15.createElement(Box15, { flexDirection: "column", borderStyle: "round", borderColor: "green", paddingX: 2, paddingY: 1, gap: 1 }, /* @__PURE__ */ React15.createElement(Text15, { bold: true, color: "green" }, ICONS.check, " Build Complete!"), /* @__PURE__ */ React15.createElement(Text15, { color: "gray", wrap: "wrap" }, result), /* @__PURE__ */ React15.createElement(Text15, { dimColor: true }, "\u23CE or [r] again")));
2140
+ }
2141
+
2142
+ // src/screens/Emulator.js
2143
+ import React16, { useState as useState13, useEffect as useEffect8 } from "react";
2144
+ import { Box as Box16, Text as Text16, useInput as useInput12 } from "ink";
2145
+ var UTILS10 = { tizen: tizen_exports, android: android_exports };
2146
+ function Emulator({ focused, platform }) {
2147
+ const [avds, setAvds] = useState13([]);
2148
+ const [loading, setLoading] = useState13(true);
2149
+ const [cursor, setCursor] = useState13(0);
2150
+ const [working, setWorking] = useState13(false);
2151
+ const [status, setStatus] = useState13(null);
2152
+ const meta = PLATFORM_META[platform];
2153
+ const utils = UTILS10[platform];
2154
+ useEffect8(() => {
2155
+ async function load() {
2156
+ setLoading(true);
2157
+ try {
2158
+ const list = await utils.listAvds();
2159
+ setAvds(list);
2160
+ } catch {
2161
+ setAvds([]);
2162
+ }
2163
+ setLoading(false);
2164
+ }
2165
+ load();
2166
+ }, [platform]);
2167
+ useInput12(async (input, key) => {
2168
+ if (!focused) return;
2169
+ if (key.upArrow) setCursor((c2) => Math.max(0, c2 - 1));
2170
+ if (key.downArrow) setCursor((c2) => Math.min(avds.length - 1, c2 + 1));
2171
+ if (input === "r") {
2172
+ setLoading(true);
2173
+ const list = await utils.listAvds().catch(() => []);
2174
+ setAvds(list);
2175
+ setLoading(false);
2176
+ }
2177
+ if (key.return && avds[cursor]) {
2178
+ setWorking(true);
2179
+ setStatus(null);
2180
+ try {
2181
+ await utils.launchEmulator({ name: avds[cursor].name });
2182
+ setStatus({ ok: true, msg: `Emulator ${avds[cursor].name} launching...` });
2183
+ } catch (e) {
2184
+ setStatus({ ok: false, msg: e.message });
2185
+ }
2186
+ setWorking(false);
2187
+ }
2188
+ }, { isActive: focused });
2189
+ if (loading) return /* @__PURE__ */ React16.createElement(Box16, { padding: 2 }, /* @__PURE__ */ React16.createElement(Loader, { label: "Loading emulators...", color: meta.color }));
2190
+ return /* @__PURE__ */ React16.createElement(Box16, { flexDirection: "column", padding: 1, gap: 1 }, /* @__PURE__ */ React16.createElement(Text16, { bold: true, color: meta.color }, ICONS.emulator, " Emulator Manager ", /* @__PURE__ */ React16.createElement(Text16, { color: "gray" }, "[", meta.label, "]")), status && /* @__PURE__ */ React16.createElement(Box16, { borderStyle: "round", borderColor: status.ok ? "green" : "red", paddingX: 1 }, /* @__PURE__ */ React16.createElement(Text16, { color: status.ok ? "green" : "red" }, status.ok ? ICONS.check : ICONS.cross, " ", status.msg)), working && /* @__PURE__ */ React16.createElement(Loader, { label: "Launching emulator...", color: meta.color }), /* @__PURE__ */ React16.createElement(Box16, { flexDirection: "column", borderStyle: "round", borderColor: meta.color, paddingX: 1, paddingY: 1 }, /* @__PURE__ */ React16.createElement(Text16, { bold: true, marginBottom: 1 }, "Available Emulators / AVDs"), avds.length === 0 && /* @__PURE__ */ React16.createElement(Box16, { flexDirection: "column", gap: 1 }, /* @__PURE__ */ React16.createElement(Text16, { color: "gray" }, " No emulators found."), platform === "android" && /* @__PURE__ */ React16.createElement(Text16, { color: "gray" }, ' Create one with: avdmanager create avd -n <name> -k "system-images;android-33;google_tv;x86_64"'), platform === "tizen" && /* @__PURE__ */ React16.createElement(Text16, { color: "gray" }, " Create one in Tizen Studio \u2192 Emulator Manager")), avds.map((avd, i) => /* @__PURE__ */ React16.createElement(Box16, { key: avd.name }, /* @__PURE__ */ React16.createElement(
2191
+ Text16,
2192
+ {
2193
+ bold: focused && i === cursor,
2194
+ color: focused && i === cursor ? meta.color : "white"
2195
+ },
2196
+ focused && i === cursor ? "\u25B6 " : " ",
2197
+ avd.name.padEnd(30),
2198
+ avd.api && /* @__PURE__ */ React16.createElement(Text16, { color: "gray" }, " API ", avd.api),
2199
+ avd.type && /* @__PURE__ */ React16.createElement(Text16, { dimColor: true }, " ", avd.type)
2200
+ )))), /* @__PURE__ */ React16.createElement(Text16, { color: "gray", dimColor: true }, "\u2191\u2193 select \u23CE launch [r] refresh"));
2201
+ }
2202
+
2203
+ // src/screens/InputSim.js
2204
+ import React17, { useState as useState14 } from "react";
2205
+ import { Box as Box17, Text as Text17, useInput as useInput13 } from "ink";
2206
+ var KEY_GROUPS = [
2207
+ { label: "Navigation", keys: ["KEY_UP", "KEY_DOWN", "KEY_LEFT", "KEY_RIGHT", "KEY_ENTER"] },
2208
+ { label: "System", keys: ["KEY_HOME", "KEY_BACK", "KEY_MENU"] },
2209
+ { label: "Media", keys: ["KEY_PLAY", "KEY_PAUSE", "KEY_PLAYPAUSE", "KEY_REWIND", "KEY_FASTFORWARD"] },
2210
+ { label: "Volume", keys: ["KEY_VOLUMEUP", "KEY_VOLUMEDOWN", "KEY_MUTE"] },
2211
+ { label: "Numbers", keys: ["KEY_0", "KEY_1", "KEY_2", "KEY_3", "KEY_4", "KEY_5", "KEY_6", "KEY_7", "KEY_8", "KEY_9"] }
2212
+ ];
2213
+ function InputSim({ focused, currentDevice, platform }) {
2214
+ const [groupIdx, setGroupIdx] = useState14(0);
2215
+ const [keyIdx, setKeyIdx] = useState14(0);
2216
+ const [working, setWorking] = useState14(false);
2217
+ const [status, setStatus] = useState14(null);
2218
+ const [inKey, setInKey] = useState14(false);
2219
+ const meta = PLATFORM_META[platform];
2220
+ const device = currentDevice?.name ?? currentDevice?.serial;
2221
+ const currentGroup = KEY_GROUPS[groupIdx];
2222
+ const currentKeys = currentGroup.keys;
2223
+ useInput13(async (input, key) => {
2224
+ if (!focused) return;
2225
+ if (!inKey) {
2226
+ if (key.upArrow) setGroupIdx((g) => Math.max(0, g - 1));
2227
+ if (key.downArrow) setGroupIdx((g) => Math.min(KEY_GROUPS.length - 1, g + 1));
2228
+ if (key.rightArrow) setInKey(true);
2229
+ if (key.return) setInKey(true);
2230
+ } else {
2231
+ if (key.leftArrow || key.escape) {
2232
+ setInKey(false);
2233
+ return;
2234
+ }
2235
+ if (key.upArrow) setKeyIdx((k) => Math.max(0, k - 1));
2236
+ if (key.downArrow) setKeyIdx((k) => Math.min(currentKeys.length - 1, k + 1));
2237
+ if (key.return) {
2238
+ const tvKey = currentKeys[keyIdx];
2239
+ setWorking(true);
2240
+ setStatus(null);
2241
+ try {
2242
+ await sendInput(tvKey);
2243
+ setStatus({ ok: true, msg: `Sent: ${tvKey}` });
2244
+ } catch (e) {
2245
+ setStatus({ ok: false, msg: e.message });
2246
+ }
2247
+ setWorking(false);
2248
+ }
2249
+ }
2250
+ }, { isActive: focused });
2251
+ if (!device) {
2252
+ return /* @__PURE__ */ React17.createElement(Box17, { flexDirection: "column", padding: 2 }, /* @__PURE__ */ React17.createElement(Text17, { color: "yellow" }, ICONS.warn, " No device selected."), /* @__PURE__ */ React17.createElement(Text17, { color: "gray" }, "Go to Devices screen and connect a Fire TV device."));
2253
+ }
2254
+ return /* @__PURE__ */ React17.createElement(Box17, { flexDirection: "column", padding: 1, gap: 1 }, /* @__PURE__ */ React17.createElement(Text17, { bold: true, color: meta.color }, ICONS.input, " Input Simulator ", /* @__PURE__ */ React17.createElement(Text17, { color: "gray" }, "[", device, "]")), /* @__PURE__ */ React17.createElement(Text17, { color: "gray", dimColor: true }, "Sends remote control key events via inputd-cli"), status && /* @__PURE__ */ React17.createElement(Box17, { borderStyle: "round", borderColor: status.ok ? "green" : "red", paddingX: 1 }, /* @__PURE__ */ React17.createElement(Text17, { color: status.ok ? "green" : "red" }, status.ok ? ICONS.check : ICONS.cross, " ", status.msg)), working && /* @__PURE__ */ React17.createElement(Loader, { label: "Sending key...", color: meta.color }), /* @__PURE__ */ React17.createElement(Box17, { gap: 2 }, /* @__PURE__ */ React17.createElement(Box17, { flexDirection: "column", borderStyle: "round", borderColor: !inKey ? meta.color : "gray", paddingX: 1, paddingY: 1, width: 16 }, /* @__PURE__ */ React17.createElement(Text17, { bold: true, color: !inKey ? meta.color : "gray" }, "Groups"), KEY_GROUPS.map((g, i) => /* @__PURE__ */ React17.createElement(Box17, { key: g.label }, /* @__PURE__ */ React17.createElement(
2255
+ Text17,
2256
+ {
2257
+ bold: i === groupIdx,
2258
+ color: i === groupIdx ? meta.color : "gray"
2259
+ },
2260
+ i === groupIdx ? "\u25B6 " : " ",
2261
+ g.label
2262
+ )))), /* @__PURE__ */ React17.createElement(Box17, { flexDirection: "column", borderStyle: "round", borderColor: inKey ? meta.color : "gray", paddingX: 1, paddingY: 1, width: 26 }, /* @__PURE__ */ React17.createElement(Text17, { bold: true, color: inKey ? meta.color : "gray" }, currentGroup.label), currentKeys.map((k, i) => /* @__PURE__ */ React17.createElement(Box17, { key: k }, /* @__PURE__ */ React17.createElement(
2263
+ Text17,
2264
+ {
2265
+ bold: inKey && i === keyIdx,
2266
+ color: inKey && i === keyIdx ? meta.color : "white"
2267
+ },
2268
+ inKey && i === keyIdx ? "\u25B6 " : " ",
2269
+ k.replace("KEY_", "")
2270
+ ))))), /* @__PURE__ */ React17.createElement(Text17, { dimColor: true }, !inKey ? "\u2191\u2193 group \u2192 / \u23CE into keys" : "\u2191\u2193 key \u23CE send \u2190 / Esc back"));
2271
+ }
2272
+
2273
+ // src/App.js
2274
+ var SCREENS = {
2275
+ dashboard: Dashboard,
2276
+ devices: Devices,
2277
+ generate: Generate,
2278
+ deploy: Deploy,
2279
+ apps: Apps,
2280
+ logs: Logs,
2281
+ transfer: Transfer,
2282
+ shell: Shell,
2283
+ inspector: Inspector,
2284
+ build: Build,
2285
+ emulator: Emulator,
2286
+ input: InputSim
2287
+ };
2288
+ function App() {
2289
+ const { exit } = useApp2();
2290
+ const [platform, setPlatform] = useState15(null);
2291
+ const [menuIndex, setMenuIndex] = useState15(0);
2292
+ const [currentScreen, setCurrentScreen] = useState15("dashboard");
2293
+ const [sidebarFocused, setSidebarFocused] = useState15(true);
2294
+ const [currentDevice, setCurrentDevice] = useState15(null);
2295
+ const navItems = platform ? NAV_ITEMS[platform] : [];
2296
+ useInput14((input, key) => {
2297
+ if (!platform) return;
2298
+ if (key.ctrl && input === "c") {
2299
+ exit();
2300
+ return;
2301
+ }
2302
+ if (input === "q" && sidebarFocused) {
2303
+ exit();
2304
+ return;
2305
+ }
2306
+ if (sidebarFocused) {
2307
+ if (key.upArrow) setMenuIndex((i) => Math.max(0, i - 1));
2308
+ if (key.downArrow) setMenuIndex((i) => Math.min(navItems.length - 1, i + 1));
2309
+ if (key.return) {
2310
+ setCurrentScreen(navItems[menuIndex].id);
2311
+ setSidebarFocused(false);
2312
+ }
2313
+ if (input === "p") {
2314
+ setPlatform(null);
2315
+ setCurrentDevice(null);
2316
+ setMenuIndex(0);
2317
+ setCurrentScreen("dashboard");
2318
+ setSidebarFocused(true);
2319
+ return;
2320
+ }
2321
+ } else {
2322
+ if (key.escape) setSidebarFocused(true);
2323
+ if (input === "q") {
2324
+ exit();
2325
+ return;
2326
+ }
2327
+ }
2328
+ });
2329
+ if (!platform) {
2330
+ return /* @__PURE__ */ React18.createElement(
2331
+ PlatformPicker,
2332
+ {
2333
+ onSelect: (p) => {
2334
+ setPlatform(p);
2335
+ setCurrentScreen("dashboard");
2336
+ setMenuIndex(0);
2337
+ setCurrentDevice(null);
2338
+ }
2339
+ }
2340
+ );
2341
+ }
2342
+ const ScreenComponent = SCREENS[currentScreen] || Dashboard;
2343
+ const cols = process.stdout.columns || 80;
2344
+ return /* @__PURE__ */ React18.createElement(Box18, { flexDirection: "column", width: cols }, /* @__PURE__ */ React18.createElement(Header, { currentDevice, platform, onChangePlatform: () => {
2345
+ setPlatform(null);
2346
+ setCurrentDevice(null);
2347
+ setMenuIndex(0);
2348
+ setCurrentScreen("dashboard");
2349
+ setSidebarFocused(true);
2350
+ } }), /* @__PURE__ */ React18.createElement(Box18, null, /* @__PURE__ */ React18.createElement(
2351
+ Sidebar,
2352
+ {
2353
+ menuIndex,
2354
+ currentScreen,
2355
+ focused: sidebarFocused,
2356
+ platform
2357
+ }
2358
+ ), /* @__PURE__ */ React18.createElement(Box18, { flexGrow: 1, flexDirection: "column" }, /* @__PURE__ */ React18.createElement(
2359
+ ScreenComponent,
2360
+ {
2361
+ focused: !sidebarFocused,
2362
+ currentDevice,
2363
+ platform,
2364
+ onDeviceChange: setCurrentDevice,
2365
+ onNavigate: (screen) => {
2366
+ const idx = navItems.findIndex((m) => m.id === screen);
2367
+ if (idx >= 0) setMenuIndex(idx);
2368
+ setCurrentScreen(screen);
2369
+ setSidebarFocused(false);
2370
+ }
2371
+ }
2372
+ ))), /* @__PURE__ */ React18.createElement(StatusBar, { sidebarFocused, platform }));
2373
+ }
2374
+
2375
+ // src/index.js
2376
+ render(/* @__PURE__ */ React19.createElement(App, null), { exitOnCtrlC: false });