remobi 0.2.1 → 0.2.3
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/CHANGELOG.md +9 -23
- package/LICENSE +1 -1
- package/README.md +17 -6
- package/dist/cli.mjs +91 -16
- package/dist/cli.mjs.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,49 +1,35 @@
|
|
|
1
|
-
## [0.2.
|
|
1
|
+
## [0.2.3](https://github.com/connorads/remobi/compare/v0.2.2...v0.2.3) (2026-03-16)
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
### Bug Fixes
|
|
5
5
|
|
|
6
|
-
*
|
|
7
|
-
|
|
8
|
-
# [0.2.0](https://github.com/connorads/remobi/compare/v0.1.0...v0.2.0) (2026-03-15)
|
|
6
|
+
* **serve:** default remobi serve to localhost ([36eb0ff](https://github.com/connorads/remobi/commit/36eb0ff54b780b1a4bf336051af6d80f00f9f1cf))
|
|
9
7
|
|
|
8
|
+
## [0.2.2](https://github.com/connorads/remobi/compare/v0.2.1...v0.2.2) (2026-03-16)
|
|
10
9
|
|
|
11
10
|
### Bug Fixes
|
|
12
11
|
|
|
13
|
-
*
|
|
14
|
-
* exclude package.json from Biome formatter ([044183f](https://github.com/connorads/remobi/commit/044183fd636fe56eb17f1cd650338c85be46ce5e))
|
|
15
|
-
* remove leading ./ from bin path for npm 11 compatibility ([33ba38d](https://github.com/connorads/remobi/commit/33ba38daab05224fc6eeb28402bb9860c36873d2))
|
|
16
|
-
* remove redundant checks from prepublishOnly ([efdb742](https://github.com/connorads/remobi/commit/efdb742087842ba131ed704e3e29676aa672ab87))
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
### Features
|
|
20
|
-
|
|
21
|
-
* add pixel R> logo and integrate across project ([44b238a](https://github.com/connorads/remobi/commit/44b238aa7909cec2c408acb5bde32f9f2c645e26))
|
|
22
|
-
|
|
23
|
-
## [0.2.2](https://github.com/connorads/remobi/compare/v0.2.1...v0.2.2) (2026-03-15)
|
|
12
|
+
* guard process.argv[1] for strict index access ([32eabce](https://github.com/connorads/remobi/commit/32eabce2a86651aa101c4756f5b59935c49e8154))
|
|
24
13
|
|
|
14
|
+
## [0.2.1](https://github.com/connorads/remobi/compare/v0.2.0...v0.2.1) (2026-03-15)
|
|
25
15
|
|
|
26
16
|
### Bug Fixes
|
|
27
17
|
|
|
28
|
-
*
|
|
29
|
-
|
|
30
|
-
## [0.2.1](https://github.com/connorads/remobi/compare/v0.2.0...v0.2.1) (2026-03-15)
|
|
18
|
+
* resolve symlink in entry guard so npx execution works ([4eab06d](https://github.com/connorads/remobi/commit/4eab06dcb2144788a65d2cd5e503b5e3e27b350c))
|
|
31
19
|
|
|
20
|
+
## [0.2.0](https://github.com/connorads/remobi/compare/v0.1.0...v0.2.0) (2026-03-15)
|
|
32
21
|
|
|
33
22
|
### Bug Fixes
|
|
34
23
|
|
|
24
|
+
* **ci:** add npm to mise.toml for OIDC trusted publishing ([44bb745](https://github.com/connorads/remobi/commit/44bb7452d0d9e3496bacc4aaf5a63f2236a8f80f))
|
|
35
25
|
* exclude package.json from Biome formatter ([044183f](https://github.com/connorads/remobi/commit/044183fd636fe56eb17f1cd650338c85be46ce5e))
|
|
26
|
+
* remove leading ./ from bin path for npm 11 compatibility ([33ba38d](https://github.com/connorads/remobi/commit/33ba38daab05224fc6eeb28402bb9860c36873d2))
|
|
36
27
|
* remove redundant checks from prepublishOnly ([efdb742](https://github.com/connorads/remobi/commit/efdb742087842ba131ed704e3e29676aa672ab87))
|
|
37
28
|
|
|
38
|
-
# [0.2.0](https://github.com/connorads/remobi/compare/v0.1.0...v0.2.0) (2026-03-15)
|
|
39
|
-
|
|
40
|
-
|
|
41
29
|
### Features
|
|
42
30
|
|
|
43
31
|
* add pixel R> logo and integrate across project ([44b238a](https://github.com/connorads/remobi/commit/44b238aa7909cec2c408acb5bde32f9f2c645e26))
|
|
44
32
|
|
|
45
|
-
# Changelog
|
|
46
|
-
|
|
47
33
|
## 0.1.0 — 2026-03-15
|
|
48
34
|
|
|
49
35
|
- Changed: overlay is now pre-built as an IIFE during `build:dist` — faster `remobi serve` startup (no runtime esbuild), smaller install footprint (esbuild moved to devDependencies). Dev mode falls back to esbuild-from-source when `dist/overlay.iife.js` is absent.
|
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -32,7 +32,7 @@ Running coding agents in tmux? remobi lets you monitor and control them from you
|
|
|
32
32
|
- **Pinch to zoom** — resize text like every other app on your phone
|
|
33
33
|
- **Install to your home screen** — standalone PWA, looks and feels native
|
|
34
34
|
- **Config-driven** — your buttons, your gestures, your layout. Or let an AI agent configure it for you
|
|
35
|
-
- **Self-hosted** —
|
|
35
|
+
- **Self-hosted** — local-first by default. Bring your own access layer (Tailscale, Cloudflare, ngrok)
|
|
36
36
|
|
|
37
37
|
## Requirements
|
|
38
38
|
|
|
@@ -48,20 +48,31 @@ remobi uses standard ttyd flags (`--writable`, `-t`, `-i`) and should work with
|
|
|
48
48
|
# 1. Install
|
|
49
49
|
npm install -g remobi
|
|
50
50
|
|
|
51
|
-
# 2. Start (builds overlay, manages ttyd, serves with PWA support)
|
|
51
|
+
# 2. Start (builds overlay, manages ttyd, serves with PWA support on 127.0.0.1:7681)
|
|
52
52
|
remobi serve
|
|
53
53
|
```
|
|
54
54
|
|
|
55
55
|
For local development, use `pnpm link --global` from the repo root instead of `npm install -g remobi`.
|
|
56
56
|
|
|
57
|
-
Open `http://localhost:7681` on
|
|
57
|
+
Open `http://localhost:7681` on the same machine to verify it works. For phone access, put a trusted proxy/tunnel in front of it, for example [Tailscale Serve](docs/guides/tailscale-serve.md).
|
|
58
|
+
|
|
59
|
+
## Security model
|
|
60
|
+
|
|
61
|
+
`remobi` is a remote-control surface for your terminal. Anyone who can reach it can drive the tmux session with your user privileges.
|
|
62
|
+
|
|
63
|
+
- `remobi serve` binds to `127.0.0.1` by default.
|
|
64
|
+
- The inner `ttyd` process also binds to `127.0.0.1`.
|
|
65
|
+
- There is no built-in login, password, or ACL in remobi itself.
|
|
66
|
+
- Safe default: keep it on localhost and publish it through a trusted layer like Tailscale Serve.
|
|
67
|
+
- If you use `remobi serve --host 0.0.0.0`, you are exposing terminal control to your LAN/whatever can route to that port. Do that only if you intentionally want direct network exposure and have separate network controls in place.
|
|
58
68
|
|
|
59
69
|
## CLI reference
|
|
60
70
|
|
|
61
|
-
```
|
|
62
|
-
remobi serve [--config <path>] [--port <n>] [-- <command...>]
|
|
71
|
+
```text
|
|
72
|
+
remobi serve [--config <path>] [--port <n>] [--host <addr>] [-- <command...>]
|
|
63
73
|
Build overlay in memory, manage ttyd, serve with PWA support.
|
|
64
|
-
Default port: 7681. Default command: tmux new-session -A -s main
|
|
74
|
+
Default host: 127.0.0.1. Default port: 7681. Default command: tmux new-session -A -s main
|
|
75
|
+
Example: remobi serve --host 0.0.0.0 --port 8080
|
|
65
76
|
Example: remobi serve --port 8080 -- tmux new -As dev
|
|
66
77
|
|
|
67
78
|
remobi build [--config <path>] [--output <path>] [--dry-run]
|
package/dist/cli.mjs
CHANGED
|
@@ -49,6 +49,7 @@ function parseCliArgs(args) {
|
|
|
49
49
|
let outputPath;
|
|
50
50
|
let dryRun = false;
|
|
51
51
|
let port;
|
|
52
|
+
let host;
|
|
52
53
|
let noSleep = false;
|
|
53
54
|
let trailingCommand = [];
|
|
54
55
|
for (let index = 1; index < args.length; index++) {
|
|
@@ -127,6 +128,19 @@ function parseCliArgs(args) {
|
|
|
127
128
|
index++;
|
|
128
129
|
continue;
|
|
129
130
|
}
|
|
131
|
+
if (arg === "--host") {
|
|
132
|
+
if (commandToken !== "serve") return {
|
|
133
|
+
ok: false,
|
|
134
|
+
error: `${arg} is only valid for 'serve'`
|
|
135
|
+
};
|
|
136
|
+
if (isMissingOptionValue(nextArg)) return {
|
|
137
|
+
ok: false,
|
|
138
|
+
error: "Missing value for --host"
|
|
139
|
+
};
|
|
140
|
+
host = nextArg;
|
|
141
|
+
index++;
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
130
144
|
if (arg === "--no-sleep") {
|
|
131
145
|
if (commandToken !== "serve") return {
|
|
132
146
|
ok: false,
|
|
@@ -152,6 +166,7 @@ function parseCliArgs(args) {
|
|
|
152
166
|
outputPath,
|
|
153
167
|
dryRun,
|
|
154
168
|
port,
|
|
169
|
+
host,
|
|
155
170
|
noSleep,
|
|
156
171
|
command_: trailingCommand
|
|
157
172
|
}
|
|
@@ -482,6 +497,7 @@ function manifestToJson(name, pwa) {
|
|
|
482
497
|
//#endregion
|
|
483
498
|
//#region src/serve.ts
|
|
484
499
|
const DEFAULT_PORT = 7681;
|
|
500
|
+
const DEFAULT_HOST = "127.0.0.1";
|
|
485
501
|
const DEFAULT_COMMAND = [
|
|
486
502
|
"tmux",
|
|
487
503
|
"new-session",
|
|
@@ -499,6 +515,56 @@ function findIconsDir() {
|
|
|
499
515
|
return resolve(import.meta.dirname, "pwa/icons");
|
|
500
516
|
}
|
|
501
517
|
const ICONS_DIR = findIconsDir();
|
|
518
|
+
const RESPONSE_SECURITY_HEADERS = {
|
|
519
|
+
"content-security-policy": "frame-ancestors 'none'; base-uri 'none'; form-action 'self'; object-src 'none'",
|
|
520
|
+
"x-frame-options": "DENY",
|
|
521
|
+
"x-content-type-options": "nosniff",
|
|
522
|
+
"referrer-policy": "no-referrer",
|
|
523
|
+
"cross-origin-resource-policy": "same-origin",
|
|
524
|
+
"permissions-policy": "camera=(), microphone=(), geolocation=()"
|
|
525
|
+
};
|
|
526
|
+
const STRIPPED_PROXY_REQUEST_HEADERS = new Set([
|
|
527
|
+
"connection",
|
|
528
|
+
"content-length",
|
|
529
|
+
"host",
|
|
530
|
+
"keep-alive",
|
|
531
|
+
"origin",
|
|
532
|
+
"proxy-authenticate",
|
|
533
|
+
"proxy-authorization",
|
|
534
|
+
"te",
|
|
535
|
+
"trailer",
|
|
536
|
+
"transfer-encoding",
|
|
537
|
+
"upgrade"
|
|
538
|
+
]);
|
|
539
|
+
function isLoopbackHost(host) {
|
|
540
|
+
return host === "127.0.0.1" || host === "::1" || host === "localhost";
|
|
541
|
+
}
|
|
542
|
+
function isAllowedWebSocketOrigin(originHeader, hostHeader) {
|
|
543
|
+
if (originHeader === void 0) return true;
|
|
544
|
+
if (hostHeader === void 0) return false;
|
|
545
|
+
try {
|
|
546
|
+
return new URL(originHeader).host === hostHeader;
|
|
547
|
+
} catch {
|
|
548
|
+
return false;
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
function buildProxyRequestHeaders(source) {
|
|
552
|
+
const headers = new Headers();
|
|
553
|
+
for (const [name, value] of source.entries()) {
|
|
554
|
+
if (STRIPPED_PROXY_REQUEST_HEADERS.has(name.toLowerCase())) continue;
|
|
555
|
+
headers.append(name, value);
|
|
556
|
+
}
|
|
557
|
+
return headers;
|
|
558
|
+
}
|
|
559
|
+
function withSecurityHeaders(response) {
|
|
560
|
+
const headers = new Headers(response.headers);
|
|
561
|
+
for (const [name, value] of Object.entries(RESPONSE_SECURITY_HEADERS)) headers.set(name, value);
|
|
562
|
+
return new Response(response.body, {
|
|
563
|
+
status: response.status,
|
|
564
|
+
statusText: response.statusText,
|
|
565
|
+
headers
|
|
566
|
+
});
|
|
567
|
+
}
|
|
502
568
|
/** Poll until ttyd is accepting connections on the given port */
|
|
503
569
|
async function waitForTtyd(port, retries = 40, intervalMs = 200) {
|
|
504
570
|
for (let i = 0; i < retries; i++) {
|
|
@@ -564,7 +630,7 @@ function spawnCaffeinate(pid) {
|
|
|
564
630
|
}
|
|
565
631
|
}
|
|
566
632
|
/** Start remobi serve: builds overlay in memory, manages ttyd, serves HTTP + WS */
|
|
567
|
-
async function serve$1(config, port = DEFAULT_PORT, command = DEFAULT_COMMAND, noSleep = false) {
|
|
633
|
+
async function serve$1(config, port = DEFAULT_PORT, command = DEFAULT_COMMAND, noSleep = false, host = DEFAULT_HOST) {
|
|
568
634
|
console.log("remobi: building overlay...");
|
|
569
635
|
const { js, css } = await bundleOverlay(config);
|
|
570
636
|
const internalPort = randomInternalPort();
|
|
@@ -585,6 +651,10 @@ async function serve$1(config, port = DEFAULT_PORT, command = DEFAULT_COMMAND, n
|
|
|
585
651
|
const connections = /* @__PURE__ */ new WeakMap();
|
|
586
652
|
const app = new Hono();
|
|
587
653
|
const { injectWebSocket, upgradeWebSocket } = createNodeWebSocket({ app });
|
|
654
|
+
app.use("/ws", async (c, next) => {
|
|
655
|
+
if (!isAllowedWebSocketOrigin(c.req.header("origin"), c.req.header("host"))) return withSecurityHeaders(c.text("Forbidden", 403));
|
|
656
|
+
await next();
|
|
657
|
+
});
|
|
588
658
|
app.get("/ws", upgradeWebSocket(() => ({
|
|
589
659
|
onOpen(_event, ws) {
|
|
590
660
|
const raw = ws.raw;
|
|
@@ -631,38 +701,40 @@ async function serve$1(config, port = DEFAULT_PORT, command = DEFAULT_COMMAND, n
|
|
|
631
701
|
connections.delete(raw);
|
|
632
702
|
}
|
|
633
703
|
})));
|
|
634
|
-
app.get("/", (c) => c.html(html));
|
|
704
|
+
app.get("/", (c) => withSecurityHeaders(c.html(html)));
|
|
635
705
|
if (manifestJson !== null) app.get("/manifest.json", (c) => {
|
|
636
|
-
return c.json(JSON.parse(manifestJson));
|
|
706
|
+
return withSecurityHeaders(c.json(JSON.parse(manifestJson)));
|
|
637
707
|
});
|
|
638
708
|
if (icon180) app.get("/apple-touch-icon.png", () => {
|
|
639
|
-
return new Response(Uint8Array.from(icon180), { headers: { "content-type": "image/png" } });
|
|
709
|
+
return withSecurityHeaders(new Response(Uint8Array.from(icon180), { headers: { "content-type": "image/png" } }));
|
|
640
710
|
});
|
|
641
711
|
if (icon192) app.get("/icon-192.png", () => {
|
|
642
|
-
return new Response(Uint8Array.from(icon192), { headers: { "content-type": "image/png" } });
|
|
712
|
+
return withSecurityHeaders(new Response(Uint8Array.from(icon192), { headers: { "content-type": "image/png" } }));
|
|
643
713
|
});
|
|
644
714
|
if (icon512) app.get("/icon-512.png", () => {
|
|
645
|
-
return new Response(Uint8Array.from(icon512), { headers: { "content-type": "image/png" } });
|
|
715
|
+
return withSecurityHeaders(new Response(Uint8Array.from(icon512), { headers: { "content-type": "image/png" } }));
|
|
646
716
|
});
|
|
647
717
|
app.all("/*", async (c) => {
|
|
648
718
|
const url = new URL(c.req.url);
|
|
649
719
|
const backendUrl = `http://127.0.0.1:${internalPort}${url.pathname}${url.search}`;
|
|
650
720
|
const resp = await fetch(backendUrl, {
|
|
651
721
|
method: c.req.method,
|
|
652
|
-
headers: c.req.raw.headers,
|
|
722
|
+
headers: buildProxyRequestHeaders(c.req.raw.headers),
|
|
653
723
|
body: c.req.raw.body
|
|
654
724
|
});
|
|
655
|
-
return new Response(resp.body, {
|
|
725
|
+
return withSecurityHeaders(new Response(resp.body, {
|
|
656
726
|
status: resp.status,
|
|
657
727
|
headers: resp.headers
|
|
658
|
-
});
|
|
728
|
+
}));
|
|
659
729
|
});
|
|
660
730
|
const server = serve({
|
|
661
731
|
fetch: app.fetch,
|
|
662
|
-
port
|
|
732
|
+
port,
|
|
733
|
+
hostname: host
|
|
663
734
|
});
|
|
664
735
|
injectWebSocket(server);
|
|
665
|
-
console.log(`remobi: serving on http
|
|
736
|
+
console.log(`remobi: serving on http://${isLoopbackHost(host) ? "localhost" : host}:${port}`);
|
|
737
|
+
if (!isLoopbackHost(host)) console.warn(`remobi: warning: --host ${host} exposes terminal control beyond localhost`);
|
|
666
738
|
const cleanup = () => {
|
|
667
739
|
console.log("\nremobi: shutting down...");
|
|
668
740
|
server.close();
|
|
@@ -693,9 +765,9 @@ function usage() {
|
|
|
693
765
|
console.log(`remobi v${VERSION} — mobile-friendly terminal overlay for ttyd + tmux
|
|
694
766
|
|
|
695
767
|
Usage:
|
|
696
|
-
remobi serve [--config <path>] [--port <n>] [--no-sleep] [-- <command...>]
|
|
768
|
+
remobi serve [--config <path>] [--port <n>] [--host <addr>] [--no-sleep] [-- <command...>]
|
|
697
769
|
Build overlay in memory, manage ttyd, serve with PWA support.
|
|
698
|
-
Default port: 7681. Default command: tmux new-session -A -s main
|
|
770
|
+
Default host: 127.0.0.1. Default port: 7681. Default command: tmux new-session -A -s main
|
|
699
771
|
|
|
700
772
|
remobi build [--config <path>] [--output <path>] [--dry-run]
|
|
701
773
|
Build patched index.html for ttyd --index flag.
|
|
@@ -717,12 +789,14 @@ Flags:
|
|
|
717
789
|
-c, --config <path> Use a specific config file (build/inject/serve)
|
|
718
790
|
-o, --output <path> Build output path (build only)
|
|
719
791
|
-p, --port <n> Port to serve on (serve only, default 7681)
|
|
792
|
+
--host <addr> Host/interface to bind (serve only, default 127.0.0.1)
|
|
720
793
|
-n, --dry-run Validate + print plan only (build/inject)
|
|
721
794
|
--no-sleep Prevent macOS sleep while serving (caffeinate -s, serve only)
|
|
722
795
|
|
|
723
796
|
Examples:
|
|
724
797
|
remobi serve
|
|
725
798
|
remobi serve --no-sleep
|
|
799
|
+
remobi serve --host 0.0.0.0 --port 8080
|
|
726
800
|
remobi serve --port 8080 -- tmux new -As dev
|
|
727
801
|
remobi build -c ./remobi.config.ts -o ./dist/index.html
|
|
728
802
|
remobi build --dry-run
|
|
@@ -817,10 +891,10 @@ async function main() {
|
|
|
817
891
|
usage();
|
|
818
892
|
process.exit(1);
|
|
819
893
|
}
|
|
820
|
-
const { command, configPath, outputPath, dryRun, port, noSleep, command_ } = parsed.value;
|
|
894
|
+
const { command, configPath, outputPath, dryRun, port, host, noSleep, command_ } = parsed.value;
|
|
821
895
|
switch (command) {
|
|
822
896
|
case "serve":
|
|
823
|
-
await serve$1((await loadConfig(configPath)).config, port, command_.length > 0 ? command_ : void 0, noSleep);
|
|
897
|
+
await serve$1((await loadConfig(configPath)).config, port, command_.length > 0 ? command_ : void 0, noSleep, host);
|
|
824
898
|
break;
|
|
825
899
|
case "build": {
|
|
826
900
|
const loaded = await loadConfig(configPath);
|
|
@@ -935,7 +1009,8 @@ export default defineConfig({
|
|
|
935
1009
|
process.exit(1);
|
|
936
1010
|
}
|
|
937
1011
|
}
|
|
938
|
-
|
|
1012
|
+
const entryScript = process.argv[1];
|
|
1013
|
+
if (entryScript && import.meta.filename === realpathSync(entryScript)) main().catch((err) => {
|
|
939
1014
|
console.error(err);
|
|
940
1015
|
process.exit(1);
|
|
941
1016
|
});
|
package/dist/cli.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.mjs","names":["serve","honoServe","serve"],"sources":["../src/cli/args.ts","../src/config-schema.ts","../src/config-validate.ts","../src/pwa/manifest.ts","../src/serve.ts","../cli.ts"],"sourcesContent":["type CliCommand = 'build' | 'inject' | 'init' | 'serve' | 'help' | 'version'\n\ninterface ParsedCliArgs {\n\treadonly command: CliCommand\n\treadonly configPath?: string\n\treadonly outputPath?: string\n\treadonly dryRun: boolean\n\treadonly port?: number\n\treadonly noSleep: boolean\n\treadonly command_: readonly string[]\n}\n\ninterface ParseSuccess {\n\treadonly ok: true\n\treadonly value: ParsedCliArgs\n}\n\ninterface ParseFailure {\n\treadonly ok: false\n\treadonly error: string\n}\n\ntype ParseCliResult = ParseSuccess | ParseFailure\n\nfunction isHelpCommand(value: string): boolean {\n\treturn value === '--help' || value === '-h' || value === 'help'\n}\n\nfunction isVersionCommand(value: string): boolean {\n\treturn value === '--version' || value === '-v' || value === 'version'\n}\n\nfunction isMissingOptionValue(value: string | undefined): boolean {\n\treturn value === undefined || value.startsWith('-')\n}\n\nexport function parseCliArgs(args: readonly string[]): ParseCliResult {\n\tconst commandToken = args[0]\n\tif (!commandToken || isHelpCommand(commandToken)) {\n\t\treturn { ok: true, value: { command: 'help', dryRun: false, noSleep: false, command_: [] } }\n\t}\n\n\tif (isVersionCommand(commandToken)) {\n\t\treturn { ok: true, value: { command: 'version', dryRun: false, noSleep: false, command_: [] } }\n\t}\n\n\tif (\n\t\tcommandToken !== 'build' &&\n\t\tcommandToken !== 'inject' &&\n\t\tcommandToken !== 'init' &&\n\t\tcommandToken !== 'serve'\n\t) {\n\t\treturn { ok: false, error: `Unknown command: ${commandToken}` }\n\t}\n\n\tlet configPath: string | undefined\n\tlet outputPath: string | undefined\n\tlet dryRun = false\n\tlet port: number | undefined\n\tlet noSleep = false\n\tlet trailingCommand: readonly string[] = []\n\n\tfor (let index = 1; index < args.length; index++) {\n\t\tconst arg = args[index]\n\t\tconst nextArg = args[index + 1]\n\t\tif (!arg) {\n\t\t\treturn { ok: false, error: 'Invalid argument list' }\n\t\t}\n\n\t\t// -- separator: everything after is the trailing command\n\t\tif (arg === '--') {\n\t\t\ttrailingCommand = args.slice(index + 1)\n\t\t\tbreak\n\t\t}\n\n\t\tif (arg === '--help' || arg === '-h') {\n\t\t\treturn { ok: true, value: { command: 'help', dryRun: false, noSleep: false, command_: [] } }\n\t\t}\n\n\t\tif (!arg.startsWith('-')) {\n\t\t\treturn { ok: false, error: `Unexpected positional argument: ${arg}` }\n\t\t}\n\n\t\tif (arg === '--config' || arg === '-c') {\n\t\t\tif (commandToken !== 'build' && commandToken !== 'inject' && commandToken !== 'serve') {\n\t\t\t\treturn { ok: false, error: `${arg} is only valid for 'build', 'inject', or 'serve'` }\n\t\t\t}\n\t\t\tif (isMissingOptionValue(nextArg)) {\n\t\t\t\treturn { ok: false, error: 'Missing value for --config' }\n\t\t\t}\n\t\t\tconfigPath = nextArg\n\t\t\tindex++\n\t\t\tcontinue\n\t\t}\n\n\t\tif (arg === '--output' || arg === '-o') {\n\t\t\tif (commandToken !== 'build') {\n\t\t\t\treturn { ok: false, error: `${arg} is only valid for 'build'` }\n\t\t\t}\n\t\t\tif (isMissingOptionValue(nextArg)) {\n\t\t\t\treturn { ok: false, error: 'Missing value for --output' }\n\t\t\t}\n\t\t\toutputPath = nextArg\n\t\t\tindex++\n\t\t\tcontinue\n\t\t}\n\n\t\tif (arg === '--dry-run' || arg === '-n') {\n\t\t\tif (commandToken !== 'build' && commandToken !== 'inject') {\n\t\t\t\treturn { ok: false, error: `${arg} is only valid for 'build' or 'inject'` }\n\t\t\t}\n\t\t\tdryRun = true\n\t\t\tcontinue\n\t\t}\n\n\t\tif (arg === '--port' || arg === '-p') {\n\t\t\tif (commandToken !== 'serve') {\n\t\t\t\treturn { ok: false, error: `${arg} is only valid for 'serve'` }\n\t\t\t}\n\t\t\tif (isMissingOptionValue(nextArg)) {\n\t\t\t\treturn { ok: false, error: 'Missing value for --port' }\n\t\t\t}\n\t\t\tconst portNum = Number(nextArg)\n\t\t\tif (!Number.isInteger(portNum) || portNum < 1 || portNum > 65535) {\n\t\t\t\treturn { ok: false, error: `Invalid port: ${nextArg}` }\n\t\t\t}\n\t\t\tport = portNum\n\t\t\tindex++\n\t\t\tcontinue\n\t\t}\n\n\t\tif (arg === '--no-sleep') {\n\t\t\tif (commandToken !== 'serve') {\n\t\t\t\treturn { ok: false, error: `${arg} is only valid for 'serve'` }\n\t\t\t}\n\t\t\tnoSleep = true\n\t\t\tcontinue\n\t\t}\n\n\t\tif (isVersionCommand(arg)) {\n\t\t\treturn { ok: false, error: `${arg} is only valid as a top-level command` }\n\t\t}\n\n\t\treturn { ok: false, error: `Unknown flag: ${arg}` }\n\t}\n\n\treturn {\n\t\tok: true,\n\t\tvalue: {\n\t\t\tcommand: commandToken,\n\t\t\tconfigPath,\n\t\t\toutputPath,\n\t\t\tdryRun,\n\t\t\tport,\n\t\t\tnoSleep,\n\t\t\tcommand_: trailingCommand,\n\t\t},\n\t}\n}\n","/**\n * Valibot schemas for remobi config validation.\n * Only used at CLI time (build/inject/serve) — never in the browser bundle.\n */\nimport * as v from 'valibot'\n\n// --- Primitives ---\n\nconst finiteNumber = v.pipe(v.number(), v.finite())\n\n// --- Button action (discriminated union) ---\n\nconst sendActionSchema = v.strictObject({\n\ttype: v.literal('send'),\n\tdata: v.string(),\n\tkeyLabel: v.optional(v.string()),\n})\n\nconst ctrlModifierActionSchema = v.strictObject({ type: v.literal('ctrl-modifier') })\nconst pasteActionSchema = v.strictObject({ type: v.literal('paste') })\nconst comboPickerActionSchema = v.strictObject({ type: v.literal('combo-picker') })\nconst drawerToggleActionSchema = v.strictObject({ type: v.literal('drawer-toggle') })\n\nconst buttonActionSchema = v.variant('type', [\n\tsendActionSchema,\n\tctrlModifierActionSchema,\n\tpasteActionSchema,\n\tcomboPickerActionSchema,\n\tdrawerToggleActionSchema,\n])\n\n// --- Control button ---\n\nconst controlButtonSchema = v.strictObject({\n\tid: v.string(),\n\tlabel: v.string(),\n\tdescription: v.string(),\n\taction: buttonActionSchema,\n})\n\n// --- Button array input (array | function) ---\n// Uses v.custom for type check + v.rawCheck for deep array validation,\n// avoiding v.union which loses path context for nested issues.\n\nconst buttonArrayInputSchema = v.pipe(\n\tv.custom<readonly Record<string, unknown>[] | ((...args: readonly unknown[]) => unknown)>(\n\t\t(input) => Array.isArray(input) || typeof input === 'function',\n\t\t'array or function',\n\t),\n\tv.rawCheck(({ dataset, addIssue }) => {\n\t\tif (!dataset.typed || !Array.isArray(dataset.value)) return\n\t\tconst arr = dataset.value\n\t\tfor (let i = 0; i < arr.length; i++) {\n\t\t\tconst result = v.safeParse(controlButtonSchema, arr[i])\n\t\t\tif (!result.success) {\n\t\t\t\tfor (const issue of result.issues) {\n\t\t\t\t\taddIssue({\n\t\t\t\t\t\tmessage: issue.message,\n\t\t\t\t\t\texpected: issue.expected,\n\t\t\t\t\t\treceived: issue.received,\n\t\t\t\t\t\tpath: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\ttype: 'array',\n\t\t\t\t\t\t\t\torigin: 'value',\n\t\t\t\t\t\t\t\tinput: arr,\n\t\t\t\t\t\t\t\tkey: i,\n\t\t\t\t\t\t\t\t// oxlint-disable-next-line typescript/consistent-type-assertions -- Valibot path segment requires typed value\n\t\t\t\t\t\t\t\tvalue: arr[i] as Record<string, unknown>,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t...(issue.path ?? []),\n\t\t\t\t\t\t],\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}),\n)\n\n// --- Theme ---\n\nconst themeColourSchema = v.optional(v.string())\n\nconst termThemeOverridesSchema = v.strictObject({\n\tbackground: themeColourSchema,\n\tforeground: themeColourSchema,\n\tcursor: themeColourSchema,\n\tcursorAccent: themeColourSchema,\n\tselectionBackground: themeColourSchema,\n\tblack: themeColourSchema,\n\tred: themeColourSchema,\n\tgreen: themeColourSchema,\n\tyellow: themeColourSchema,\n\tblue: themeColourSchema,\n\tmagenta: themeColourSchema,\n\tcyan: themeColourSchema,\n\twhite: themeColourSchema,\n\tbrightBlack: themeColourSchema,\n\tbrightRed: themeColourSchema,\n\tbrightGreen: themeColourSchema,\n\tbrightYellow: themeColourSchema,\n\tbrightBlue: themeColourSchema,\n\tbrightMagenta: themeColourSchema,\n\tbrightCyan: themeColourSchema,\n\tbrightWhite: themeColourSchema,\n})\n\nconst termThemeResolvedSchema = v.strictObject({\n\tbackground: v.string(),\n\tforeground: v.string(),\n\tcursor: v.string(),\n\tcursorAccent: v.string(),\n\tselectionBackground: v.string(),\n\tblack: v.string(),\n\tred: v.string(),\n\tgreen: v.string(),\n\tyellow: v.string(),\n\tblue: v.string(),\n\tmagenta: v.string(),\n\tcyan: v.string(),\n\twhite: v.string(),\n\tbrightBlack: v.string(),\n\tbrightRed: v.string(),\n\tbrightGreen: v.string(),\n\tbrightYellow: v.string(),\n\tbrightBlue: v.string(),\n\tbrightMagenta: v.string(),\n\tbrightCyan: v.string(),\n\tbrightWhite: v.string(),\n})\n\n// --- Font ---\n\nconst fontOverridesSchema = v.strictObject({\n\tfamily: v.optional(v.string()),\n\tcdnUrl: v.optional(v.string()),\n\tmobileSizeDefault: v.optional(finiteNumber),\n\tsizeRange: v.optional(v.pipe(v.tuple([finiteNumber, finiteNumber]))),\n})\n\nconst fontResolvedSchema = v.strictObject({\n\tfamily: v.string(),\n\tcdnUrl: v.string(),\n\tmobileSizeDefault: finiteNumber,\n\tsizeRange: v.pipe(v.tuple([finiteNumber, finiteNumber])),\n})\n\n// --- Gestures ---\n\nconst swipeOverridesSchema = v.strictObject({\n\tenabled: v.optional(v.boolean()),\n\tthreshold: v.optional(finiteNumber),\n\tmaxDuration: v.optional(finiteNumber),\n\tleft: v.optional(v.string()),\n\tright: v.optional(v.string()),\n\tleftLabel: v.optional(v.string()),\n\trightLabel: v.optional(v.string()),\n})\n\nconst swipeResolvedSchema = v.strictObject({\n\tenabled: v.boolean(),\n\tthreshold: finiteNumber,\n\tmaxDuration: finiteNumber,\n\tleft: v.string(),\n\tright: v.string(),\n\tleftLabel: v.string(),\n\trightLabel: v.string(),\n})\n\nconst pinchOverridesSchema = v.strictObject({\n\tenabled: v.optional(v.boolean()),\n})\n\nconst pinchResolvedSchema = v.strictObject({\n\tenabled: v.boolean(),\n})\n\nconst scrollStrategySchema = v.picklist(['keys', 'wheel'])\n\nconst scrollOverridesSchema = v.strictObject({\n\tenabled: v.optional(v.boolean()),\n\tsensitivity: v.optional(finiteNumber),\n\tstrategy: v.optional(scrollStrategySchema),\n\twheelIntervalMs: v.optional(finiteNumber),\n})\n\nconst scrollResolvedSchema = v.strictObject({\n\tenabled: v.boolean(),\n\tsensitivity: finiteNumber,\n\tstrategy: scrollStrategySchema,\n\twheelIntervalMs: finiteNumber,\n})\n\nconst gestureOverridesSchema = v.strictObject({\n\tswipe: v.optional(swipeOverridesSchema),\n\tpinch: v.optional(pinchOverridesSchema),\n\tscroll: v.optional(scrollOverridesSchema),\n})\n\nconst gestureResolvedSchema = v.strictObject({\n\tswipe: swipeResolvedSchema,\n\tpinch: pinchResolvedSchema,\n\tscroll: scrollResolvedSchema,\n})\n\n// --- Mobile ---\n\nconst mobileOverridesSchema = v.strictObject({\n\tinitData: v.optional(v.nullable(v.string())),\n\twidthThreshold: v.optional(finiteNumber),\n})\n\nconst mobileResolvedSchema = v.strictObject({\n\tinitData: v.nullable(v.string()),\n\twidthThreshold: finiteNumber,\n})\n\n// --- Floating buttons ---\n\nconst floatingPositionSchema = v.picklist([\n\t'top-left',\n\t'top-right',\n\t'top-centre',\n\t'bottom-left',\n\t'bottom-right',\n\t'bottom-centre',\n\t'centre-left',\n\t'centre-right',\n])\n\nconst floatingDirectionSchema = v.picklist(['row', 'column'])\n\nconst floatingButtonGroupSchema = v.strictObject({\n\tposition: floatingPositionSchema,\n\tdirection: v.optional(floatingDirectionSchema),\n\tbuttons: v.array(controlButtonSchema),\n})\n\n// --- PWA ---\n\nconst pwaOverridesSchema = v.strictObject({\n\tenabled: v.optional(v.boolean()),\n\tshortName: v.optional(v.string()),\n\tthemeColor: v.optional(v.string()),\n})\n\nconst pwaResolvedSchema = v.strictObject({\n\tenabled: v.boolean(),\n\tshortName: v.optional(v.string()),\n\tthemeColor: v.string(),\n})\n\n// --- Reconnect ---\n\nconst reconnectOverridesSchema = v.strictObject({\n\tenabled: v.optional(v.boolean()),\n})\n\nconst reconnectResolvedSchema = v.strictObject({\n\tenabled: v.boolean(),\n})\n\n// --- Top-level schemas ---\n\n/** Schema for config overrides (all fields optional, button arrays accept array | function) */\nexport const remobiConfigOverridesSchema = v.strictObject({\n\tname: v.optional(v.string()),\n\ttheme: v.optional(termThemeOverridesSchema),\n\tfont: v.optional(fontOverridesSchema),\n\ttoolbar: v.optional(\n\t\tv.strictObject({\n\t\t\trow1: v.optional(buttonArrayInputSchema),\n\t\t\trow2: v.optional(buttonArrayInputSchema),\n\t\t}),\n\t),\n\tdrawer: v.optional(\n\t\tv.strictObject({\n\t\t\tbuttons: v.optional(buttonArrayInputSchema),\n\t\t}),\n\t),\n\tgestures: v.optional(gestureOverridesSchema),\n\tmobile: v.optional(mobileOverridesSchema),\n\tfloatingButtons: v.optional(v.array(floatingButtonGroupSchema)),\n\tpwa: v.optional(pwaOverridesSchema),\n\treconnect: v.optional(reconnectOverridesSchema),\n})\n\n/** Schema for fully resolved config (all required fields, plain button arrays) */\nexport const remobiConfigResolvedSchema = v.strictObject({\n\tname: v.string(),\n\ttheme: termThemeResolvedSchema,\n\tfont: fontResolvedSchema,\n\ttoolbar: v.strictObject({\n\t\trow1: v.array(controlButtonSchema),\n\t\trow2: v.array(controlButtonSchema),\n\t}),\n\tdrawer: v.strictObject({\n\t\tbuttons: v.array(controlButtonSchema),\n\t}),\n\tgestures: gestureResolvedSchema,\n\tmobile: mobileResolvedSchema,\n\tfloatingButtons: v.array(floatingButtonGroupSchema),\n\tpwa: pwaResolvedSchema,\n\treconnect: reconnectResolvedSchema,\n})\n","import * as v from 'valibot'\nimport { remobiConfigOverridesSchema, remobiConfigResolvedSchema } from './config-schema'\nimport type { RemobiConfig, RemobiConfigOverrides } from './types'\n\ninterface ValidationIssue {\n\treadonly path: string\n\treadonly expected: string\n\treadonly received: string\n}\n\nexport class ConfigValidationError extends Error {\n\treadonly issues: readonly ValidationIssue[]\n\n\tconstructor(issues: readonly ValidationIssue[]) {\n\t\tsuper(formatIssues(issues))\n\t\tthis.name = 'ConfigValidationError'\n\t\tthis.issues = issues\n\t}\n}\n\nfunction formatIssues(issues: readonly ValidationIssue[]): string {\n\tconst lines = ['Invalid remobi config:']\n\tfor (const issue of issues) {\n\t\tlines.push(`- ${issue.path}: expected ${issue.expected}, received ${issue.received}`)\n\t}\n\treturn lines.join('\\n')\n}\n\nfunction truncate(value: string, maxLength: number): string {\n\tif (value.length <= maxLength) {\n\t\treturn value\n\t}\n\treturn `${value.slice(0, maxLength - 3)}...`\n}\n\nfunction describeReceived(value: unknown): string {\n\tif (value === null) return 'null'\n\tif (Array.isArray(value)) return `array(len=${value.length})`\n\tif (typeof value === 'string') return `string(${JSON.stringify(truncate(value, 80))})`\n\tif (typeof value === 'number') return `number(${String(value)})`\n\tif (typeof value === 'boolean') return `boolean(${String(value)})`\n\tif (typeof value === 'bigint') return `bigint(${String(value)})`\n\tif (typeof value === 'undefined') return 'undefined'\n\tif (typeof value === 'function')\n\t\treturn value.name.length > 0 ? `function(${value.name})` : 'function'\n\tif (typeof value === 'object') {\n\t\tconst keys = Object.keys(value)\n\t\tif (keys.length === 0) return 'object(empty)'\n\t\tconst shown = keys.slice(0, 3).join(', ')\n\t\tconst suffix = keys.length > 3 ? ', ...' : ''\n\t\treturn `object(keys: ${shown}${suffix})`\n\t}\n\treturn typeof value\n}\n\n/** Convert Valibot issue path to dotted string */\nfunction issuePath(issue: v.BaseIssue<unknown>): string {\n\tif (!issue.path || issue.path.length === 0) return 'config'\n\tconst segments: string[] = ['config']\n\tfor (const segment of issue.path) {\n\t\tif (typeof segment.key === 'number') {\n\t\t\tsegments.push(`[${String(segment.key)}]`)\n\t\t} else {\n\t\t\tsegments.push(`.${String(segment.key)}`)\n\t\t}\n\t}\n\treturn segments.join('').replaceAll('.[', '[')\n}\n\n/** Extract human-readable expected string from a Valibot issue */\nfunction issueExpected(issue: v.BaseIssue<unknown>): string {\n\t// v.custom() sets expected to \"unknown\" — use the message instead\n\tif (issue.expected === 'unknown' || !issue.expected) {\n\t\treturn issue.message\n\t}\n\treturn issue.expected\n}\n\n/** Map Valibot flat issues to our ValidationIssue format */\nfunction toValidationIssues(issues: readonly v.BaseIssue<unknown>[]): ValidationIssue[] {\n\tconst result: ValidationIssue[] = []\n\tfor (const issue of issues) {\n\t\t// Leaf issues only — skip container issues that have nested issues\n\t\tif (issue.issues && issue.issues.length > 0) {\n\t\t\tresult.push(...toValidationIssues(issue.issues))\n\t\t\tcontinue\n\t\t}\n\t\tconst received = issue.input !== undefined ? issue.input : undefined\n\t\tresult.push({\n\t\t\tpath: issuePath(issue),\n\t\t\texpected: issueExpected(issue),\n\t\t\treceived: describeReceived(received),\n\t\t})\n\t}\n\treturn result\n}\n\nexport function assertValidConfigOverrides(value: unknown): asserts value is RemobiConfigOverrides {\n\tconst result = v.safeParse(remobiConfigOverridesSchema, value)\n\tif (!result.success) {\n\t\tthrow new ConfigValidationError(toValidationIssues(result.issues))\n\t}\n}\n\nexport function assertValidResolvedConfig(value: unknown): asserts value is RemobiConfig {\n\tconst result = v.safeParse(remobiConfigResolvedSchema, value)\n\tif (!result.success) {\n\t\tthrow new ConfigValidationError(toValidationIssues(result.issues))\n\t}\n}\n","import type { PwaConfig } from '../types'\n\ninterface WebAppManifest {\n\treadonly name: string\n\treadonly short_name: string\n\treadonly start_url: string\n\treadonly display: string\n\treadonly background_color: string\n\treadonly theme_color: string\n\treadonly icons: readonly {\n\t\treadonly src: string\n\t\treadonly sizes: string\n\t\treadonly type: string\n\t\treadonly purpose?: string\n\t}[]\n}\n\n/** Generate a web app manifest object from pwa config */\nexport function generateManifest(name: string, pwa: PwaConfig): WebAppManifest {\n\treturn {\n\t\tname,\n\t\tshort_name: pwa.shortName ?? name,\n\t\tstart_url: '/',\n\t\tdisplay: 'standalone',\n\t\tbackground_color: pwa.themeColor,\n\t\ttheme_color: pwa.themeColor,\n\t\ticons: [\n\t\t\t{ src: '/icon-192.png', sizes: '192x192', type: 'image/png', purpose: 'any maskable' },\n\t\t\t{ src: '/icon-512.png', sizes: '512x512', type: 'image/png' },\n\t\t],\n\t}\n}\n\n/** Serialise manifest to JSON string */\nexport function manifestToJson(name: string, pwa: PwaConfig): string {\n\treturn JSON.stringify(generateManifest(name, pwa), null, 2)\n}\n","import { existsSync, readFileSync } from 'node:fs'\nimport { dirname, resolve } from 'node:path'\nimport { serve as honoServe } from '@hono/node-server'\nimport { createNodeWebSocket } from '@hono/node-ws'\nimport { Hono } from 'hono'\nimport type { WSContext } from 'hono/ws'\nimport WebSocket from 'ws'\nimport { bundleOverlay, injectOverlay } from '../build'\nimport { serialiseThemeForTtyd } from './config'\nimport { manifestToJson } from './pwa/manifest'\nimport type { RemobiConfig } from './types'\nimport { sleep, spawnProcess } from './util/node-compat'\nimport type { SpawnedProcess } from './util/node-compat'\n\nconst DEFAULT_PORT = 7681\nconst DEFAULT_COMMAND = ['tmux', 'new-session', '-A', '-s', 'main']\n// Walk up from module location to find package root, then resolve icons\nfunction findIconsDir(): string {\n\tlet dir = import.meta.dirname\n\tfor (let i = 0; i < 5; i++) {\n\t\tconst candidate = resolve(dir, 'src/pwa/icons')\n\t\tif (existsSync(candidate)) return candidate\n\t\tdir = dirname(dir)\n\t}\n\t// Fallback for source layout (running from src/)\n\treturn resolve(import.meta.dirname, 'pwa/icons')\n}\n\nconst ICONS_DIR = findIconsDir()\n\ninterface WsData {\n\tbackend: WebSocket | null\n\tbuffer: (string | Uint8Array)[]\n}\n\n/** Poll until ttyd is accepting connections on the given port */\nasync function waitForTtyd(port: number, retries = 40, intervalMs = 200): Promise<void> {\n\tfor (let i = 0; i < retries; i++) {\n\t\ttry {\n\t\t\tconst resp = await fetch(`http://127.0.0.1:${port}/`)\n\t\t\tif (resp.ok) return\n\t\t} catch {\n\t\t\t// not ready yet\n\t\t}\n\t\tawait sleep(intervalMs)\n\t}\n\tthrow new Error(\n\t\t`ttyd did not start on port ${port} — is ttyd installed and on PATH?\\nInstall ttyd: macOS \\`brew install ttyd\\`; Linux use your distro package manager or build from source: https://github.com/tsl0922/ttyd#installation`,\n\t)\n}\n\n/** Pick a random internal port */\nexport function randomInternalPort(): number {\n\treturn 19000 + Math.floor(Math.random() * 1000)\n}\n\n/** Build ttyd args from remobi config */\nexport function buildTtydArgs(\n\tconfig: RemobiConfig,\n\tinternalPort: number,\n\tcommand: readonly string[],\n): string[] {\n\treturn [\n\t\t'--writable',\n\t\t'-i',\n\t\t'127.0.0.1',\n\t\t'--port',\n\t\tString(internalPort),\n\t\t'-t',\n\t\t`theme=${serialiseThemeForTtyd(config)}`,\n\t\t'-t',\n\t\t`fontFamily=\"${config.font.family}\"`,\n\t\t'-t',\n\t\t'scrollSensitivity=3',\n\t\t'-t',\n\t\t'disableLeaveAlert=true',\n\t\t...command,\n\t]\n}\n\n/** Read a PNG icon, returns undefined if not found */\nfunction readIcon(filename: string): Uint8Array | undefined {\n\ttry {\n\t\treturn readFileSync(resolve(ICONS_DIR, filename))\n\t} catch {\n\t\treturn undefined\n\t}\n}\n\n/** Spawn caffeinate to prevent system sleep while ttyd is running (macOS only).\n * Uses -s (system sleep on AC) and -w <pid> so the assertion drops when ttyd exits. */\nfunction spawnCaffeinate(pid: number): SpawnedProcess | null {\n\ttry {\n\t\tconst proc = spawnProcess(['caffeinate', '-s', '-w', String(pid)], {\n\t\t\tstdout: 'ignore',\n\t\t\tstderr: 'ignore',\n\t\t})\n\t\t// Catch async spawn errors (e.g. caffeinate not found on Linux)\n\t\tproc.exited.catch(() => {\n\t\t\tconsole.warn('remobi: --no-sleep requires caffeinate (macOS only), ignoring')\n\t\t})\n\t\tconsole.log(`remobi: sleep prevention active (caffeinate -s -w ${pid})`)\n\t\treturn proc\n\t} catch {\n\t\tconsole.warn('remobi: --no-sleep requires caffeinate (macOS only), ignoring')\n\t\treturn null\n\t}\n}\n\n/** Start remobi serve: builds overlay in memory, manages ttyd, serves HTTP + WS */\nexport async function serve(\n\tconfig: RemobiConfig,\n\tport: number = DEFAULT_PORT,\n\tcommand: readonly string[] = DEFAULT_COMMAND,\n\tnoSleep = false,\n): Promise<void> {\n\tconsole.log('remobi: building overlay...')\n\tconst { js, css } = await bundleOverlay(config)\n\n\tconst internalPort = randomInternalPort()\n\tconst ttydArgs = buildTtydArgs(config, internalPort, command)\n\n\tconsole.log(`remobi: starting ttyd on internal port ${internalPort}...`)\n\tconst ttydProc = spawnProcess(['ttyd', ...ttydArgs], {\n\t\tstdout: 'ignore',\n\t\tstderr: 'ignore',\n\t})\n\n\tconst caffeinateProc = noSleep && ttydProc.pid ? spawnCaffeinate(ttydProc.pid) : null\n\n\tawait waitForTtyd(internalPort)\n\n\tconst baseResp = await fetch(`http://127.0.0.1:${internalPort}/`)\n\tconst baseHtml = await baseResp.text()\n\tconst html = injectOverlay(baseHtml, js, css, config)\n\n\tconsole.log('remobi: overlay ready')\n\n\tconst manifestJson = config.pwa.enabled ? manifestToJson(config.name, config.pwa) : null\n\tconst icon180 = readIcon('icon-180.png')\n\tconst icon192 = readIcon('icon-192.png')\n\tconst icon512 = readIcon('icon-512.png')\n\n\t// Per-connection data via WeakMap (replaces Bun's ws.data)\n\tconst connections = new WeakMap<WebSocket, WsData>()\n\n\tconst app = new Hono()\n\tconst { injectWebSocket, upgradeWebSocket } = createNodeWebSocket({ app })\n\n\tapp.get(\n\t\t'/ws',\n\t\tupgradeWebSocket(() => ({\n\t\t\tonOpen(_event: Event, ws: WSContext<WebSocket>) {\n\t\t\t\tconst raw = ws.raw\n\t\t\t\tif (!raw) return\n\n\t\t\t\tconst data: WsData = { backend: null, buffer: [] }\n\t\t\t\tconnections.set(raw, data)\n\n\t\t\t\tconst backend = new WebSocket(`ws://127.0.0.1:${internalPort}/ws`, ['tty'])\n\t\t\t\tbackend.binaryType = 'arraybuffer'\n\t\t\t\tdata.backend = backend\n\n\t\t\t\tbackend.on('open', () => {\n\t\t\t\t\tfor (const msg of data.buffer) {\n\t\t\t\t\t\tbackend.send(msg)\n\t\t\t\t\t}\n\t\t\t\t\tdata.buffer = []\n\t\t\t\t})\n\n\t\t\t\tbackend.on('message', (message: WebSocket.RawData, isBinary: boolean) => {\n\t\t\t\t\tif (isBinary && message instanceof ArrayBuffer) {\n\t\t\t\t\t\tws.send(new Uint8Array(message))\n\t\t\t\t\t} else {\n\t\t\t\t\t\tws.send(message.toString())\n\t\t\t\t\t}\n\t\t\t\t})\n\n\t\t\t\tbackend.on('error', () => {\n\t\t\t\t\tws.close()\n\t\t\t\t})\n\n\t\t\t\tbackend.on('close', () => {\n\t\t\t\t\tws.close()\n\t\t\t\t})\n\t\t\t},\n\t\t\tonMessage(event: MessageEvent, ws: WSContext<WebSocket>) {\n\t\t\t\tconst raw = ws.raw\n\t\t\t\tif (!raw) return\n\t\t\t\tconst data = connections.get(raw)\n\t\t\t\tif (!data) return\n\n\t\t\t\tconst { backend, buffer } = data\n\t\t\t\tif (backend !== null && backend.readyState === WebSocket.OPEN) {\n\t\t\t\t\t// oxlint-disable-next-line typescript/consistent-type-assertions -- WSMessageReceive union needs narrowing for ws.send()\n\t\t\t\t\tbackend.send(event.data as string | ArrayBuffer)\n\t\t\t\t} else {\n\t\t\t\t\tconst msg = event.data\n\t\t\t\t\t// oxlint-disable-next-line typescript/consistent-type-assertions -- WSMessageReceive union needs narrowing for Uint8Array ctor\n\t\t\t\t\tbuffer.push(typeof msg === 'string' ? msg : new Uint8Array(msg as ArrayBuffer))\n\t\t\t\t}\n\t\t\t},\n\t\t\tonClose(_event: CloseEvent, ws: WSContext<WebSocket>) {\n\t\t\t\tconst raw = ws.raw\n\t\t\t\tif (!raw) return\n\t\t\t\tconnections.get(raw)?.backend?.close()\n\t\t\t\tconnections.delete(raw)\n\t\t\t},\n\t\t})),\n\t)\n\n\tapp.get('/', (c) => c.html(html))\n\n\tif (manifestJson !== null) {\n\t\tapp.get('/manifest.json', (c) => {\n\t\t\t// oxlint-disable-next-line typescript/consistent-type-assertions -- JSON.parse returns unknown, safe for manifest\n\t\t\treturn c.json(JSON.parse(manifestJson) as Record<string, unknown>)\n\t\t})\n\t}\n\n\tif (icon180) {\n\t\tapp.get('/apple-touch-icon.png', () => {\n\t\t\treturn new Response(Uint8Array.from(icon180), {\n\t\t\t\theaders: { 'content-type': 'image/png' },\n\t\t\t})\n\t\t})\n\t}\n\n\tif (icon192) {\n\t\tapp.get('/icon-192.png', () => {\n\t\t\treturn new Response(Uint8Array.from(icon192), {\n\t\t\t\theaders: { 'content-type': 'image/png' },\n\t\t\t})\n\t\t})\n\t}\n\n\tif (icon512) {\n\t\tapp.get('/icon-512.png', () => {\n\t\t\treturn new Response(Uint8Array.from(icon512), {\n\t\t\t\theaders: { 'content-type': 'image/png' },\n\t\t\t})\n\t\t})\n\t}\n\n\t// Proxy remaining requests to ttyd (e.g. /token)\n\tapp.all('/*', async (c) => {\n\t\tconst url = new URL(c.req.url)\n\t\tconst backendUrl = `http://127.0.0.1:${internalPort}${url.pathname}${url.search}`\n\t\tconst resp = await fetch(backendUrl, {\n\t\t\tmethod: c.req.method,\n\t\t\theaders: c.req.raw.headers,\n\t\t\tbody: c.req.raw.body,\n\t\t})\n\t\treturn new Response(resp.body, {\n\t\t\tstatus: resp.status,\n\t\t\theaders: resp.headers,\n\t\t})\n\t})\n\n\tconst server = honoServe({ fetch: app.fetch, port })\n\tinjectWebSocket(server)\n\n\tconsole.log(`remobi: serving on http://localhost:${port}`)\n\n\t// Clean shutdown on SIGINT / SIGTERM\n\tconst cleanup = () => {\n\t\tconsole.log('\\nremobi: shutting down...')\n\t\tserver.close()\n\t\tttydProc.kill()\n\t\tcaffeinateProc?.kill()\n\t\tprocess.exit(0)\n\t}\n\n\tprocess.on('SIGINT', cleanup)\n\tprocess.on('SIGTERM', cleanup)\n\n\t// Keep process alive until ttyd exits\n\tawait ttydProc.exited\n\tserver.close()\n}\n","#!/usr/bin/env node\nimport { existsSync, mkdirSync, readFileSync, realpathSync, writeFileSync } from 'node:fs'\nimport { homedir } from 'node:os'\nimport { dirname, join, resolve } from 'node:path'\nimport { build, injectFromStdin } from './build'\nimport { parseCliArgs } from './src/cli/args'\nimport { defaultConfig, defineConfig, mergeConfig } from './src/config'\nimport {\n\tConfigValidationError,\n\tassertValidConfigOverrides,\n\tassertValidResolvedConfig,\n} from './src/config-validate'\nimport { serve } from './src/serve'\nimport type { RemobiConfig, RemobiConfigOverrides } from './src/types'\nimport { readStdin } from './src/util/node-compat'\n\n// Walk up from module location to find package.json — works from both source and dist/\nfunction loadPackageVersion(): string {\n\tlet dir = import.meta.dirname\n\tfor (let i = 0; i < 5; i++) {\n\t\ttry {\n\t\t\tconst content = readFileSync(resolve(dir, 'package.json'), 'utf-8')\n\t\t\t// oxlint-disable-next-line typescript/consistent-type-assertions -- JSON.parse returns unknown\n\t\t\treturn (JSON.parse(content) as { version: string }).version\n\t\t} catch {\n\t\t\tdir = dirname(dir)\n\t\t}\n\t}\n\treturn '0.0.0'\n}\n\nconst VERSION: string = loadPackageVersion()\n\nfunction usage(): void {\n\tconsole.log(`remobi v${VERSION} — mobile-friendly terminal overlay for ttyd + tmux\n\nUsage:\n remobi serve [--config <path>] [--port <n>] [--no-sleep] [-- <command...>]\n Build overlay in memory, manage ttyd, serve with PWA support.\n Default port: 7681. Default command: tmux new-session -A -s main\n\n remobi build [--config <path>] [--output <path>] [--dry-run]\n Build patched index.html for ttyd --index flag.\n Starts temp ttyd, fetches base HTML, injects overlay.\n\n remobi inject [--config <path>] [--dry-run]\n Pipe mode: reads ttyd HTML from stdin, outputs patched HTML to stdout.\n\n remobi init\n Scaffold a remobi.config.ts with defaults.\n\n remobi --version\n Print version.\n\n remobi --help\n Show this help.\n\nFlags:\n -c, --config <path> Use a specific config file (build/inject/serve)\n -o, --output <path> Build output path (build only)\n -p, --port <n> Port to serve on (serve only, default 7681)\n -n, --dry-run Validate + print plan only (build/inject)\n --no-sleep Prevent macOS sleep while serving (caffeinate -s, serve only)\n\nExamples:\n remobi serve\n remobi serve --no-sleep\n remobi serve --port 8080 -- tmux new -As dev\n remobi build -c ./remobi.config.ts -o ./dist/index.html\n remobi build --dry-run\n curl -s http://127.0.0.1:7681/ | remobi inject --dry-run`)\n}\n\ninterface LoadedConfig {\n\treadonly config: RemobiConfig\n\treadonly source: string\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\nfunction extractDefaultExport(value: unknown): unknown | undefined {\n\tif (!isRecord(value)) return undefined\n\tif (!('default' in value)) return undefined\n\treturn value.default\n}\n\nfunction ensureInjectInputMode(context: string): void {\n\tif (process.stdin.isTTY) {\n\t\tthrow new Error(`${context} expects piped ttyd HTML on stdin`)\n\t}\n}\n\nfunction throwConfigValidationError(source: string, error: ConfigValidationError): never {\n\tthrow new Error(`Config validation failed for ${source}\\n${error.message}`)\n}\n\n/** Convert a config path to its .local sibling, e.g. remobi.config.ts → remobi.config.local.ts */\nfunction toLocalPath(configPath: string): string {\n\tconst dotIdx = configPath.lastIndexOf('.')\n\tif (dotIdx === -1) {\n\t\treturn `${configPath}.local`\n\t}\n\treturn `${configPath.slice(0, dotIdx)}.local${configPath.slice(dotIdx)}`\n}\n\n/** Try to load a .local config override file. Returns undefined if the file does not exist. */\nasync function loadLocalOverrides(localPath: string): Promise<RemobiConfigOverrides | undefined> {\n\tif (!existsSync(localPath)) {\n\t\treturn undefined\n\t}\n\n\tconst mod = await import(localPath)\n\tconst defaultExport = extractDefaultExport(mod)\n\tif (defaultExport === undefined) {\n\t\tthrow new Error(`Local config file has no default export: ${localPath}`)\n\t}\n\n\tassertValidOverridesOrThrow(defaultExport, localPath)\n\treturn defaultExport\n}\n\nfunction assertValidOverridesOrThrow(\n\tvalue: unknown,\n\tsource: string,\n): asserts value is RemobiConfigOverrides {\n\ttry {\n\t\tassertValidConfigOverrides(value)\n\t} catch (error) {\n\t\tif (error instanceof ConfigValidationError) {\n\t\t\tthrowConfigValidationError(source, error)\n\t\t}\n\t\tthrow error\n\t}\n}\n\nfunction assertValidResolvedOrThrow(value: unknown, source: string): asserts value is RemobiConfig {\n\ttry {\n\t\tassertValidResolvedConfig(value)\n\t} catch (error) {\n\t\tif (error instanceof ConfigValidationError) {\n\t\t\tthrowConfigValidationError(source, error)\n\t\t}\n\t\tthrow error\n\t}\n}\n\nasync function loadConfig(configPath: string | undefined): Promise<LoadedConfig> {\n\tlet resolved = configPath\n\tif (!resolved) {\n\t\t// Search order: cwd → XDG config dir (~/.config/remobi/)\n\t\tconst names = ['remobi.config.ts', 'remobi.config.js']\n\t\tconst searchDirs = [\n\t\t\tprocess.cwd(),\n\t\t\tjoin(process.env.XDG_CONFIG_HOME ?? join(homedir(), '.config'), 'remobi'),\n\t\t]\n\t\tfor (const dir of searchDirs) {\n\t\t\tfor (const name of names) {\n\t\t\t\tconst full = join(dir, name)\n\t\t\t\tif (existsSync(full)) {\n\t\t\t\t\tresolved = full\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (resolved) break\n\t\t}\n\t}\n\n\tif (resolved) {\n\t\tconst abs = resolve(process.cwd(), resolved)\n\t\tconst mod = await import(abs)\n\t\tconst defaultExport = extractDefaultExport(mod)\n\t\tif (defaultExport === undefined) {\n\t\t\tthrow new Error(`Config file has no default export: ${abs}`)\n\t\t}\n\n\t\tassertValidOverridesOrThrow(defaultExport, abs)\n\t\tconst sharedConfig = defineConfig(defaultExport)\n\n\t\t// Apply .local overrides on top of the shared config\n\t\tconst localPath = toLocalPath(abs)\n\t\tconst localOverrides = await loadLocalOverrides(localPath)\n\t\tconst config =\n\t\t\tlocalOverrides !== undefined ? mergeConfig(sharedConfig, localOverrides) : sharedConfig\n\n\t\tconst sourceLabel = localOverrides !== undefined ? `${abs} + ${localPath}` : abs\n\t\tassertValidResolvedOrThrow(config, sourceLabel)\n\t\treturn { config, source: sourceLabel }\n\t}\n\n\tassertValidResolvedOrThrow(defaultConfig, 'built-in defaults')\n\n\treturn {\n\t\tconfig: defaultConfig,\n\t\tsource: 'built-in defaults',\n\t}\n}\n\nasync function main(): Promise<void> {\n\tconst parsed = parseCliArgs(process.argv.slice(2))\n\tif (!parsed.ok) {\n\t\tconsole.error(parsed.error)\n\t\tusage()\n\t\tprocess.exit(1)\n\t}\n\n\tconst { command, configPath, outputPath, dryRun, port, noSleep, command_ } = parsed.value\n\n\tswitch (command) {\n\t\tcase 'serve': {\n\t\t\tconst loaded = await loadConfig(configPath)\n\t\t\tawait serve(loaded.config, port, command_.length > 0 ? command_ : undefined, noSleep)\n\t\t\tbreak\n\t\t}\n\n\t\tcase 'build': {\n\t\t\tconst loaded = await loadConfig(configPath)\n\t\t\tconst targetPath = outputPath\n\t\t\t\t? resolve(process.cwd(), outputPath)\n\t\t\t\t: resolve(process.cwd(), 'dist/index.html')\n\n\t\t\tif (dryRun) {\n\t\t\t\tconsole.log('Dry run: build')\n\t\t\t\tconsole.log(`- config: ${loaded.source}`)\n\t\t\t\tconsole.log(`- output: ${targetPath}`)\n\t\t\t\tconsole.log('- action: would bundle overlay, fetch ttyd base HTML, inject, and write file')\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\t// Ensure output directory exists\n\t\t\tmkdirSync(dirname(targetPath), { recursive: true })\n\n\t\t\tawait build(loaded.config, targetPath)\n\t\t\tconsole.log(`Built: ${targetPath}`)\n\t\t\tbreak\n\t\t}\n\n\t\tcase 'inject': {\n\t\t\tconst loaded = await loadConfig(configPath)\n\t\t\tif (dryRun) {\n\t\t\t\tensureInjectInputMode('remobi inject --dry-run')\n\t\t\t\tconst dryRunStdin = await readStdin()\n\t\t\t\tif (dryRunStdin.trim().length === 0) {\n\t\t\t\t\tthrow new Error('remobi inject --dry-run expects piped ttyd HTML on stdin')\n\t\t\t\t}\n\t\t\t\tconsole.log('Dry run: inject')\n\t\t\t\tconsole.log(`- config: ${loaded.source}`)\n\t\t\t\tconsole.log('- stdin: piped input detected')\n\t\t\t\tconsole.log('- action: would read stdin HTML, inject overlay, and write to stdout')\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\tensureInjectInputMode('remobi inject')\n\t\t\tconst result = await injectFromStdin(loaded.config)\n\t\t\tprocess.stdout.write(result)\n\t\t\tbreak\n\t\t}\n\n\t\tcase 'init': {\n\t\t\tconst targetPath = resolve(process.cwd(), 'remobi.config.ts')\n\t\t\tif (existsSync(targetPath)) {\n\t\t\t\tconsole.error('remobi.config.ts already exists')\n\t\t\t\tprocess.exit(1)\n\t\t\t}\n\t\t\tconst template = `import { defineConfig } from 'remobi'\n\nexport default defineConfig({\n // name: 'remobi', // app name (tab title, PWA home screen label)\n // theme: 'catppuccin-mocha',\n // font: {\n // family: 'JetBrainsMono NFM, monospace',\n // mobileSizeDefault: 16,\n // sizeRange: [8, 32],\n // },\n //\n // Toolbar/drawer accept a plain array (replace) or a function (transform):\n //\n // toolbar: { row1: [{ id, label, description, action }], row2: [...] },\n //\n // drawer: {\n // buttons: [\n // { id: 'sessions', label: 'Sessions', description: 'Choose tmux session', action: { type: 'send', data: '\\\\x02s' } },\n // ],\n // },\n //\n // toolbar: {\n // row2: (defaults) => defaults.filter((b) => b.id !== 'q'),\n // },\n //\n // drawer: {\n // buttons: (defaults) => [\n // ...defaults,\n // { id: 'my-btn', label: 'X', description: 'Send x', action: { type: 'send', data: 'x' } },\n // ],\n // },\n //\n // gestures: {\n // swipe: {\n // enabled: true,\n // left: '\\\\x02n', // data sent on swipe left (default: next tmux window)\n // right: '\\\\x02p', // data sent on swipe right (default: prev tmux window)\n // leftLabel: 'Next tmux window',\n // rightLabel: 'Previous tmux window',\n // },\n // pinch: { enabled: true },\n // scroll: { strategy: 'wheel', sensitivity: 40 },\n // },\n // mobile: {\n // initData: '\\\\x02z', // send on load when viewport < widthThreshold (auto-zoom pane)\n // widthThreshold: 768, // px — default matches phone/tablet breakpoint\n // },\n // floatingButtons: [\n // // Always-visible top-left buttons (touch devices only)\n // { position: 'top-left', buttons: [{ id: 'zoom', label: 'Zoom', description: 'Toggle pane zoom', action: { type: 'send', data: '\\\\x02z' } }] },\n // ],\n // pwa: {\n // enabled: true, // enable PWA manifest + meta tags (used by remobi serve)\n // shortName: 'remobi', // short name for home screen icon (defaults to name)\n // themeColor: '#1e1e2e', // theme-color meta tag + manifest\n // },\n // reconnect: {\n // enabled: true, // show overlay + auto-reload on connection loss (default true)\n // },\n})\n`\n\t\t\twriteFileSync(targetPath, template)\n\t\t\tconsole.log(`Created: ${targetPath}`)\n\t\t\tbreak\n\t\t}\n\n\t\tcase 'version':\n\t\t\tconsole.log(VERSION)\n\t\t\tbreak\n\n\t\tcase 'help':\n\t\t\tusage()\n\t\t\tbreak\n\n\t\tdefault:\n\t\t\tconsole.error(`Unknown command: ${command}`)\n\t\t\tusage()\n\t\t\tprocess.exit(1)\n\t}\n}\n\nif (import.meta.filename === realpathSync(process.argv[1])) {\n\tmain().catch((err: unknown) => {\n\t\tconsole.error(err)\n\t\tprocess.exit(1)\n\t})\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"cli.mjs","names":["serve","honoServe","serve"],"sources":["../src/cli/args.ts","../src/config-schema.ts","../src/config-validate.ts","../src/pwa/manifest.ts","../src/serve.ts","../cli.ts"],"sourcesContent":["type CliCommand = 'build' | 'inject' | 'init' | 'serve' | 'help' | 'version'\n\ninterface ParsedCliArgs {\n\treadonly command: CliCommand\n\treadonly configPath?: string\n\treadonly outputPath?: string\n\treadonly dryRun: boolean\n\treadonly port?: number\n\treadonly host?: string\n\treadonly noSleep: boolean\n\treadonly command_: readonly string[]\n}\n\ninterface ParseSuccess {\n\treadonly ok: true\n\treadonly value: ParsedCliArgs\n}\n\ninterface ParseFailure {\n\treadonly ok: false\n\treadonly error: string\n}\n\ntype ParseCliResult = ParseSuccess | ParseFailure\n\nfunction isHelpCommand(value: string): boolean {\n\treturn value === '--help' || value === '-h' || value === 'help'\n}\n\nfunction isVersionCommand(value: string): boolean {\n\treturn value === '--version' || value === '-v' || value === 'version'\n}\n\nfunction isMissingOptionValue(value: string | undefined): boolean {\n\treturn value === undefined || value.startsWith('-')\n}\n\nexport function parseCliArgs(args: readonly string[]): ParseCliResult {\n\tconst commandToken = args[0]\n\tif (!commandToken || isHelpCommand(commandToken)) {\n\t\treturn { ok: true, value: { command: 'help', dryRun: false, noSleep: false, command_: [] } }\n\t}\n\n\tif (isVersionCommand(commandToken)) {\n\t\treturn { ok: true, value: { command: 'version', dryRun: false, noSleep: false, command_: [] } }\n\t}\n\n\tif (\n\t\tcommandToken !== 'build' &&\n\t\tcommandToken !== 'inject' &&\n\t\tcommandToken !== 'init' &&\n\t\tcommandToken !== 'serve'\n\t) {\n\t\treturn { ok: false, error: `Unknown command: ${commandToken}` }\n\t}\n\n\tlet configPath: string | undefined\n\tlet outputPath: string | undefined\n\tlet dryRun = false\n\tlet port: number | undefined\n\tlet host: string | undefined\n\tlet noSleep = false\n\tlet trailingCommand: readonly string[] = []\n\n\tfor (let index = 1; index < args.length; index++) {\n\t\tconst arg = args[index]\n\t\tconst nextArg = args[index + 1]\n\t\tif (!arg) {\n\t\t\treturn { ok: false, error: 'Invalid argument list' }\n\t\t}\n\n\t\t// -- separator: everything after is the trailing command\n\t\tif (arg === '--') {\n\t\t\ttrailingCommand = args.slice(index + 1)\n\t\t\tbreak\n\t\t}\n\n\t\tif (arg === '--help' || arg === '-h') {\n\t\t\treturn { ok: true, value: { command: 'help', dryRun: false, noSleep: false, command_: [] } }\n\t\t}\n\n\t\tif (!arg.startsWith('-')) {\n\t\t\treturn { ok: false, error: `Unexpected positional argument: ${arg}` }\n\t\t}\n\n\t\tif (arg === '--config' || arg === '-c') {\n\t\t\tif (commandToken !== 'build' && commandToken !== 'inject' && commandToken !== 'serve') {\n\t\t\t\treturn { ok: false, error: `${arg} is only valid for 'build', 'inject', or 'serve'` }\n\t\t\t}\n\t\t\tif (isMissingOptionValue(nextArg)) {\n\t\t\t\treturn { ok: false, error: 'Missing value for --config' }\n\t\t\t}\n\t\t\tconfigPath = nextArg\n\t\t\tindex++\n\t\t\tcontinue\n\t\t}\n\n\t\tif (arg === '--output' || arg === '-o') {\n\t\t\tif (commandToken !== 'build') {\n\t\t\t\treturn { ok: false, error: `${arg} is only valid for 'build'` }\n\t\t\t}\n\t\t\tif (isMissingOptionValue(nextArg)) {\n\t\t\t\treturn { ok: false, error: 'Missing value for --output' }\n\t\t\t}\n\t\t\toutputPath = nextArg\n\t\t\tindex++\n\t\t\tcontinue\n\t\t}\n\n\t\tif (arg === '--dry-run' || arg === '-n') {\n\t\t\tif (commandToken !== 'build' && commandToken !== 'inject') {\n\t\t\t\treturn { ok: false, error: `${arg} is only valid for 'build' or 'inject'` }\n\t\t\t}\n\t\t\tdryRun = true\n\t\t\tcontinue\n\t\t}\n\n\t\tif (arg === '--port' || arg === '-p') {\n\t\t\tif (commandToken !== 'serve') {\n\t\t\t\treturn { ok: false, error: `${arg} is only valid for 'serve'` }\n\t\t\t}\n\t\t\tif (isMissingOptionValue(nextArg)) {\n\t\t\t\treturn { ok: false, error: 'Missing value for --port' }\n\t\t\t}\n\t\t\tconst portNum = Number(nextArg)\n\t\t\tif (!Number.isInteger(portNum) || portNum < 1 || portNum > 65535) {\n\t\t\t\treturn { ok: false, error: `Invalid port: ${nextArg}` }\n\t\t\t}\n\t\t\tport = portNum\n\t\t\tindex++\n\t\t\tcontinue\n\t\t}\n\n\t\tif (arg === '--host') {\n\t\t\tif (commandToken !== 'serve') {\n\t\t\t\treturn { ok: false, error: `${arg} is only valid for 'serve'` }\n\t\t\t}\n\t\t\tif (isMissingOptionValue(nextArg)) {\n\t\t\t\treturn { ok: false, error: 'Missing value for --host' }\n\t\t\t}\n\t\t\thost = nextArg\n\t\t\tindex++\n\t\t\tcontinue\n\t\t}\n\n\t\tif (arg === '--no-sleep') {\n\t\t\tif (commandToken !== 'serve') {\n\t\t\t\treturn { ok: false, error: `${arg} is only valid for 'serve'` }\n\t\t\t}\n\t\t\tnoSleep = true\n\t\t\tcontinue\n\t\t}\n\n\t\tif (isVersionCommand(arg)) {\n\t\t\treturn { ok: false, error: `${arg} is only valid as a top-level command` }\n\t\t}\n\n\t\treturn { ok: false, error: `Unknown flag: ${arg}` }\n\t}\n\n\treturn {\n\t\tok: true,\n\t\tvalue: {\n\t\t\tcommand: commandToken,\n\t\t\tconfigPath,\n\t\t\toutputPath,\n\t\t\tdryRun,\n\t\t\tport,\n\t\t\thost,\n\t\t\tnoSleep,\n\t\t\tcommand_: trailingCommand,\n\t\t},\n\t}\n}\n","/**\n * Valibot schemas for remobi config validation.\n * Only used at CLI time (build/inject/serve) — never in the browser bundle.\n */\nimport * as v from 'valibot'\n\n// --- Primitives ---\n\nconst finiteNumber = v.pipe(v.number(), v.finite())\n\n// --- Button action (discriminated union) ---\n\nconst sendActionSchema = v.strictObject({\n\ttype: v.literal('send'),\n\tdata: v.string(),\n\tkeyLabel: v.optional(v.string()),\n})\n\nconst ctrlModifierActionSchema = v.strictObject({ type: v.literal('ctrl-modifier') })\nconst pasteActionSchema = v.strictObject({ type: v.literal('paste') })\nconst comboPickerActionSchema = v.strictObject({ type: v.literal('combo-picker') })\nconst drawerToggleActionSchema = v.strictObject({ type: v.literal('drawer-toggle') })\n\nconst buttonActionSchema = v.variant('type', [\n\tsendActionSchema,\n\tctrlModifierActionSchema,\n\tpasteActionSchema,\n\tcomboPickerActionSchema,\n\tdrawerToggleActionSchema,\n])\n\n// --- Control button ---\n\nconst controlButtonSchema = v.strictObject({\n\tid: v.string(),\n\tlabel: v.string(),\n\tdescription: v.string(),\n\taction: buttonActionSchema,\n})\n\n// --- Button array input (array | function) ---\n// Uses v.custom for type check + v.rawCheck for deep array validation,\n// avoiding v.union which loses path context for nested issues.\n\nconst buttonArrayInputSchema = v.pipe(\n\tv.custom<readonly Record<string, unknown>[] | ((...args: readonly unknown[]) => unknown)>(\n\t\t(input) => Array.isArray(input) || typeof input === 'function',\n\t\t'array or function',\n\t),\n\tv.rawCheck(({ dataset, addIssue }) => {\n\t\tif (!dataset.typed || !Array.isArray(dataset.value)) return\n\t\tconst arr = dataset.value\n\t\tfor (let i = 0; i < arr.length; i++) {\n\t\t\tconst result = v.safeParse(controlButtonSchema, arr[i])\n\t\t\tif (!result.success) {\n\t\t\t\tfor (const issue of result.issues) {\n\t\t\t\t\taddIssue({\n\t\t\t\t\t\tmessage: issue.message,\n\t\t\t\t\t\texpected: issue.expected,\n\t\t\t\t\t\treceived: issue.received,\n\t\t\t\t\t\tpath: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\ttype: 'array',\n\t\t\t\t\t\t\t\torigin: 'value',\n\t\t\t\t\t\t\t\tinput: arr,\n\t\t\t\t\t\t\t\tkey: i,\n\t\t\t\t\t\t\t\t// oxlint-disable-next-line typescript/consistent-type-assertions -- Valibot path segment requires typed value\n\t\t\t\t\t\t\t\tvalue: arr[i] as Record<string, unknown>,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t...(issue.path ?? []),\n\t\t\t\t\t\t],\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}),\n)\n\n// --- Theme ---\n\nconst themeColourSchema = v.optional(v.string())\n\nconst termThemeOverridesSchema = v.strictObject({\n\tbackground: themeColourSchema,\n\tforeground: themeColourSchema,\n\tcursor: themeColourSchema,\n\tcursorAccent: themeColourSchema,\n\tselectionBackground: themeColourSchema,\n\tblack: themeColourSchema,\n\tred: themeColourSchema,\n\tgreen: themeColourSchema,\n\tyellow: themeColourSchema,\n\tblue: themeColourSchema,\n\tmagenta: themeColourSchema,\n\tcyan: themeColourSchema,\n\twhite: themeColourSchema,\n\tbrightBlack: themeColourSchema,\n\tbrightRed: themeColourSchema,\n\tbrightGreen: themeColourSchema,\n\tbrightYellow: themeColourSchema,\n\tbrightBlue: themeColourSchema,\n\tbrightMagenta: themeColourSchema,\n\tbrightCyan: themeColourSchema,\n\tbrightWhite: themeColourSchema,\n})\n\nconst termThemeResolvedSchema = v.strictObject({\n\tbackground: v.string(),\n\tforeground: v.string(),\n\tcursor: v.string(),\n\tcursorAccent: v.string(),\n\tselectionBackground: v.string(),\n\tblack: v.string(),\n\tred: v.string(),\n\tgreen: v.string(),\n\tyellow: v.string(),\n\tblue: v.string(),\n\tmagenta: v.string(),\n\tcyan: v.string(),\n\twhite: v.string(),\n\tbrightBlack: v.string(),\n\tbrightRed: v.string(),\n\tbrightGreen: v.string(),\n\tbrightYellow: v.string(),\n\tbrightBlue: v.string(),\n\tbrightMagenta: v.string(),\n\tbrightCyan: v.string(),\n\tbrightWhite: v.string(),\n})\n\n// --- Font ---\n\nconst fontOverridesSchema = v.strictObject({\n\tfamily: v.optional(v.string()),\n\tcdnUrl: v.optional(v.string()),\n\tmobileSizeDefault: v.optional(finiteNumber),\n\tsizeRange: v.optional(v.pipe(v.tuple([finiteNumber, finiteNumber]))),\n})\n\nconst fontResolvedSchema = v.strictObject({\n\tfamily: v.string(),\n\tcdnUrl: v.string(),\n\tmobileSizeDefault: finiteNumber,\n\tsizeRange: v.pipe(v.tuple([finiteNumber, finiteNumber])),\n})\n\n// --- Gestures ---\n\nconst swipeOverridesSchema = v.strictObject({\n\tenabled: v.optional(v.boolean()),\n\tthreshold: v.optional(finiteNumber),\n\tmaxDuration: v.optional(finiteNumber),\n\tleft: v.optional(v.string()),\n\tright: v.optional(v.string()),\n\tleftLabel: v.optional(v.string()),\n\trightLabel: v.optional(v.string()),\n})\n\nconst swipeResolvedSchema = v.strictObject({\n\tenabled: v.boolean(),\n\tthreshold: finiteNumber,\n\tmaxDuration: finiteNumber,\n\tleft: v.string(),\n\tright: v.string(),\n\tleftLabel: v.string(),\n\trightLabel: v.string(),\n})\n\nconst pinchOverridesSchema = v.strictObject({\n\tenabled: v.optional(v.boolean()),\n})\n\nconst pinchResolvedSchema = v.strictObject({\n\tenabled: v.boolean(),\n})\n\nconst scrollStrategySchema = v.picklist(['keys', 'wheel'])\n\nconst scrollOverridesSchema = v.strictObject({\n\tenabled: v.optional(v.boolean()),\n\tsensitivity: v.optional(finiteNumber),\n\tstrategy: v.optional(scrollStrategySchema),\n\twheelIntervalMs: v.optional(finiteNumber),\n})\n\nconst scrollResolvedSchema = v.strictObject({\n\tenabled: v.boolean(),\n\tsensitivity: finiteNumber,\n\tstrategy: scrollStrategySchema,\n\twheelIntervalMs: finiteNumber,\n})\n\nconst gestureOverridesSchema = v.strictObject({\n\tswipe: v.optional(swipeOverridesSchema),\n\tpinch: v.optional(pinchOverridesSchema),\n\tscroll: v.optional(scrollOverridesSchema),\n})\n\nconst gestureResolvedSchema = v.strictObject({\n\tswipe: swipeResolvedSchema,\n\tpinch: pinchResolvedSchema,\n\tscroll: scrollResolvedSchema,\n})\n\n// --- Mobile ---\n\nconst mobileOverridesSchema = v.strictObject({\n\tinitData: v.optional(v.nullable(v.string())),\n\twidthThreshold: v.optional(finiteNumber),\n})\n\nconst mobileResolvedSchema = v.strictObject({\n\tinitData: v.nullable(v.string()),\n\twidthThreshold: finiteNumber,\n})\n\n// --- Floating buttons ---\n\nconst floatingPositionSchema = v.picklist([\n\t'top-left',\n\t'top-right',\n\t'top-centre',\n\t'bottom-left',\n\t'bottom-right',\n\t'bottom-centre',\n\t'centre-left',\n\t'centre-right',\n])\n\nconst floatingDirectionSchema = v.picklist(['row', 'column'])\n\nconst floatingButtonGroupSchema = v.strictObject({\n\tposition: floatingPositionSchema,\n\tdirection: v.optional(floatingDirectionSchema),\n\tbuttons: v.array(controlButtonSchema),\n})\n\n// --- PWA ---\n\nconst pwaOverridesSchema = v.strictObject({\n\tenabled: v.optional(v.boolean()),\n\tshortName: v.optional(v.string()),\n\tthemeColor: v.optional(v.string()),\n})\n\nconst pwaResolvedSchema = v.strictObject({\n\tenabled: v.boolean(),\n\tshortName: v.optional(v.string()),\n\tthemeColor: v.string(),\n})\n\n// --- Reconnect ---\n\nconst reconnectOverridesSchema = v.strictObject({\n\tenabled: v.optional(v.boolean()),\n})\n\nconst reconnectResolvedSchema = v.strictObject({\n\tenabled: v.boolean(),\n})\n\n// --- Top-level schemas ---\n\n/** Schema for config overrides (all fields optional, button arrays accept array | function) */\nexport const remobiConfigOverridesSchema = v.strictObject({\n\tname: v.optional(v.string()),\n\ttheme: v.optional(termThemeOverridesSchema),\n\tfont: v.optional(fontOverridesSchema),\n\ttoolbar: v.optional(\n\t\tv.strictObject({\n\t\t\trow1: v.optional(buttonArrayInputSchema),\n\t\t\trow2: v.optional(buttonArrayInputSchema),\n\t\t}),\n\t),\n\tdrawer: v.optional(\n\t\tv.strictObject({\n\t\t\tbuttons: v.optional(buttonArrayInputSchema),\n\t\t}),\n\t),\n\tgestures: v.optional(gestureOverridesSchema),\n\tmobile: v.optional(mobileOverridesSchema),\n\tfloatingButtons: v.optional(v.array(floatingButtonGroupSchema)),\n\tpwa: v.optional(pwaOverridesSchema),\n\treconnect: v.optional(reconnectOverridesSchema),\n})\n\n/** Schema for fully resolved config (all required fields, plain button arrays) */\nexport const remobiConfigResolvedSchema = v.strictObject({\n\tname: v.string(),\n\ttheme: termThemeResolvedSchema,\n\tfont: fontResolvedSchema,\n\ttoolbar: v.strictObject({\n\t\trow1: v.array(controlButtonSchema),\n\t\trow2: v.array(controlButtonSchema),\n\t}),\n\tdrawer: v.strictObject({\n\t\tbuttons: v.array(controlButtonSchema),\n\t}),\n\tgestures: gestureResolvedSchema,\n\tmobile: mobileResolvedSchema,\n\tfloatingButtons: v.array(floatingButtonGroupSchema),\n\tpwa: pwaResolvedSchema,\n\treconnect: reconnectResolvedSchema,\n})\n","import * as v from 'valibot'\nimport { remobiConfigOverridesSchema, remobiConfigResolvedSchema } from './config-schema'\nimport type { RemobiConfig, RemobiConfigOverrides } from './types'\n\ninterface ValidationIssue {\n\treadonly path: string\n\treadonly expected: string\n\treadonly received: string\n}\n\nexport class ConfigValidationError extends Error {\n\treadonly issues: readonly ValidationIssue[]\n\n\tconstructor(issues: readonly ValidationIssue[]) {\n\t\tsuper(formatIssues(issues))\n\t\tthis.name = 'ConfigValidationError'\n\t\tthis.issues = issues\n\t}\n}\n\nfunction formatIssues(issues: readonly ValidationIssue[]): string {\n\tconst lines = ['Invalid remobi config:']\n\tfor (const issue of issues) {\n\t\tlines.push(`- ${issue.path}: expected ${issue.expected}, received ${issue.received}`)\n\t}\n\treturn lines.join('\\n')\n}\n\nfunction truncate(value: string, maxLength: number): string {\n\tif (value.length <= maxLength) {\n\t\treturn value\n\t}\n\treturn `${value.slice(0, maxLength - 3)}...`\n}\n\nfunction describeReceived(value: unknown): string {\n\tif (value === null) return 'null'\n\tif (Array.isArray(value)) return `array(len=${value.length})`\n\tif (typeof value === 'string') return `string(${JSON.stringify(truncate(value, 80))})`\n\tif (typeof value === 'number') return `number(${String(value)})`\n\tif (typeof value === 'boolean') return `boolean(${String(value)})`\n\tif (typeof value === 'bigint') return `bigint(${String(value)})`\n\tif (typeof value === 'undefined') return 'undefined'\n\tif (typeof value === 'function')\n\t\treturn value.name.length > 0 ? `function(${value.name})` : 'function'\n\tif (typeof value === 'object') {\n\t\tconst keys = Object.keys(value)\n\t\tif (keys.length === 0) return 'object(empty)'\n\t\tconst shown = keys.slice(0, 3).join(', ')\n\t\tconst suffix = keys.length > 3 ? ', ...' : ''\n\t\treturn `object(keys: ${shown}${suffix})`\n\t}\n\treturn typeof value\n}\n\n/** Convert Valibot issue path to dotted string */\nfunction issuePath(issue: v.BaseIssue<unknown>): string {\n\tif (!issue.path || issue.path.length === 0) return 'config'\n\tconst segments: string[] = ['config']\n\tfor (const segment of issue.path) {\n\t\tif (typeof segment.key === 'number') {\n\t\t\tsegments.push(`[${String(segment.key)}]`)\n\t\t} else {\n\t\t\tsegments.push(`.${String(segment.key)}`)\n\t\t}\n\t}\n\treturn segments.join('').replaceAll('.[', '[')\n}\n\n/** Extract human-readable expected string from a Valibot issue */\nfunction issueExpected(issue: v.BaseIssue<unknown>): string {\n\t// v.custom() sets expected to \"unknown\" — use the message instead\n\tif (issue.expected === 'unknown' || !issue.expected) {\n\t\treturn issue.message\n\t}\n\treturn issue.expected\n}\n\n/** Map Valibot flat issues to our ValidationIssue format */\nfunction toValidationIssues(issues: readonly v.BaseIssue<unknown>[]): ValidationIssue[] {\n\tconst result: ValidationIssue[] = []\n\tfor (const issue of issues) {\n\t\t// Leaf issues only — skip container issues that have nested issues\n\t\tif (issue.issues && issue.issues.length > 0) {\n\t\t\tresult.push(...toValidationIssues(issue.issues))\n\t\t\tcontinue\n\t\t}\n\t\tconst received = issue.input !== undefined ? issue.input : undefined\n\t\tresult.push({\n\t\t\tpath: issuePath(issue),\n\t\t\texpected: issueExpected(issue),\n\t\t\treceived: describeReceived(received),\n\t\t})\n\t}\n\treturn result\n}\n\nexport function assertValidConfigOverrides(value: unknown): asserts value is RemobiConfigOverrides {\n\tconst result = v.safeParse(remobiConfigOverridesSchema, value)\n\tif (!result.success) {\n\t\tthrow new ConfigValidationError(toValidationIssues(result.issues))\n\t}\n}\n\nexport function assertValidResolvedConfig(value: unknown): asserts value is RemobiConfig {\n\tconst result = v.safeParse(remobiConfigResolvedSchema, value)\n\tif (!result.success) {\n\t\tthrow new ConfigValidationError(toValidationIssues(result.issues))\n\t}\n}\n","import type { PwaConfig } from '../types'\n\ninterface WebAppManifest {\n\treadonly name: string\n\treadonly short_name: string\n\treadonly start_url: string\n\treadonly display: string\n\treadonly background_color: string\n\treadonly theme_color: string\n\treadonly icons: readonly {\n\t\treadonly src: string\n\t\treadonly sizes: string\n\t\treadonly type: string\n\t\treadonly purpose?: string\n\t}[]\n}\n\n/** Generate a web app manifest object from pwa config */\nexport function generateManifest(name: string, pwa: PwaConfig): WebAppManifest {\n\treturn {\n\t\tname,\n\t\tshort_name: pwa.shortName ?? name,\n\t\tstart_url: '/',\n\t\tdisplay: 'standalone',\n\t\tbackground_color: pwa.themeColor,\n\t\ttheme_color: pwa.themeColor,\n\t\ticons: [\n\t\t\t{ src: '/icon-192.png', sizes: '192x192', type: 'image/png', purpose: 'any maskable' },\n\t\t\t{ src: '/icon-512.png', sizes: '512x512', type: 'image/png' },\n\t\t],\n\t}\n}\n\n/** Serialise manifest to JSON string */\nexport function manifestToJson(name: string, pwa: PwaConfig): string {\n\treturn JSON.stringify(generateManifest(name, pwa), null, 2)\n}\n","import { existsSync, readFileSync } from 'node:fs'\nimport { dirname, resolve } from 'node:path'\nimport { serve as honoServe } from '@hono/node-server'\nimport { createNodeWebSocket } from '@hono/node-ws'\nimport { Hono } from 'hono'\nimport type { WSContext } from 'hono/ws'\nimport WebSocket from 'ws'\nimport { bundleOverlay, injectOverlay } from '../build'\nimport { serialiseThemeForTtyd } from './config'\nimport { manifestToJson } from './pwa/manifest'\nimport type { RemobiConfig } from './types'\nimport { sleep, spawnProcess } from './util/node-compat'\nimport type { SpawnedProcess } from './util/node-compat'\n\nconst DEFAULT_PORT = 7681\nconst DEFAULT_HOST = '127.0.0.1'\nconst DEFAULT_COMMAND = ['tmux', 'new-session', '-A', '-s', 'main']\n// Walk up from module location to find package root, then resolve icons\nfunction findIconsDir(): string {\n\tlet dir = import.meta.dirname\n\tfor (let i = 0; i < 5; i++) {\n\t\tconst candidate = resolve(dir, 'src/pwa/icons')\n\t\tif (existsSync(candidate)) return candidate\n\t\tdir = dirname(dir)\n\t}\n\t// Fallback for source layout (running from src/)\n\treturn resolve(import.meta.dirname, 'pwa/icons')\n}\n\nconst ICONS_DIR = findIconsDir()\n\ninterface WsData {\n\tbackend: WebSocket | null\n\tbuffer: (string | Uint8Array)[]\n}\n\nconst RESPONSE_SECURITY_HEADERS = {\n\t'content-security-policy':\n\t\t\"frame-ancestors 'none'; base-uri 'none'; form-action 'self'; object-src 'none'\",\n\t'x-frame-options': 'DENY',\n\t'x-content-type-options': 'nosniff',\n\t'referrer-policy': 'no-referrer',\n\t'cross-origin-resource-policy': 'same-origin',\n\t'permissions-policy': 'camera=(), microphone=(), geolocation=()',\n} as const\n\nconst STRIPPED_PROXY_REQUEST_HEADERS = new Set([\n\t'connection',\n\t'content-length',\n\t'host',\n\t'keep-alive',\n\t'origin',\n\t'proxy-authenticate',\n\t'proxy-authorization',\n\t'te',\n\t'trailer',\n\t'transfer-encoding',\n\t'upgrade',\n])\n\nexport function isLoopbackHost(host: string): boolean {\n\treturn host === '127.0.0.1' || host === '::1' || host === 'localhost'\n}\n\nexport function isAllowedWebSocketOrigin(\n\toriginHeader: string | undefined,\n\thostHeader: string | undefined,\n): boolean {\n\tif (originHeader === undefined) {\n\t\treturn true\n\t}\n\tif (hostHeader === undefined) {\n\t\treturn false\n\t}\n\n\ttry {\n\t\treturn new URL(originHeader).host === hostHeader\n\t} catch {\n\t\treturn false\n\t}\n}\n\nexport function buildProxyRequestHeaders(source: Headers): Headers {\n\tconst headers = new Headers()\n\n\tfor (const [name, value] of source.entries()) {\n\t\tif (STRIPPED_PROXY_REQUEST_HEADERS.has(name.toLowerCase())) {\n\t\t\tcontinue\n\t\t}\n\t\theaders.append(name, value)\n\t}\n\n\treturn headers\n}\n\nexport function withSecurityHeaders(response: Response): Response {\n\tconst headers = new Headers(response.headers)\n\n\tfor (const [name, value] of Object.entries(RESPONSE_SECURITY_HEADERS)) {\n\t\theaders.set(name, value)\n\t}\n\n\treturn new Response(response.body, {\n\t\tstatus: response.status,\n\t\tstatusText: response.statusText,\n\t\theaders,\n\t})\n}\n\n/** Poll until ttyd is accepting connections on the given port */\nasync function waitForTtyd(port: number, retries = 40, intervalMs = 200): Promise<void> {\n\tfor (let i = 0; i < retries; i++) {\n\t\ttry {\n\t\t\tconst resp = await fetch(`http://127.0.0.1:${port}/`)\n\t\t\tif (resp.ok) return\n\t\t} catch {\n\t\t\t// not ready yet\n\t\t}\n\t\tawait sleep(intervalMs)\n\t}\n\tthrow new Error(\n\t\t`ttyd did not start on port ${port} — is ttyd installed and on PATH?\\nInstall ttyd: macOS \\`brew install ttyd\\`; Linux use your distro package manager or build from source: https://github.com/tsl0922/ttyd#installation`,\n\t)\n}\n\n/** Pick a random internal port */\nexport function randomInternalPort(): number {\n\treturn 19000 + Math.floor(Math.random() * 1000)\n}\n\n/** Build ttyd args from remobi config */\nexport function buildTtydArgs(\n\tconfig: RemobiConfig,\n\tinternalPort: number,\n\tcommand: readonly string[],\n): string[] {\n\treturn [\n\t\t'--writable',\n\t\t'-i',\n\t\t'127.0.0.1',\n\t\t'--port',\n\t\tString(internalPort),\n\t\t'-t',\n\t\t`theme=${serialiseThemeForTtyd(config)}`,\n\t\t'-t',\n\t\t`fontFamily=\"${config.font.family}\"`,\n\t\t'-t',\n\t\t'scrollSensitivity=3',\n\t\t'-t',\n\t\t'disableLeaveAlert=true',\n\t\t...command,\n\t]\n}\n\n/** Read a PNG icon, returns undefined if not found */\nfunction readIcon(filename: string): Uint8Array | undefined {\n\ttry {\n\t\treturn readFileSync(resolve(ICONS_DIR, filename))\n\t} catch {\n\t\treturn undefined\n\t}\n}\n\n/** Spawn caffeinate to prevent system sleep while ttyd is running (macOS only).\n * Uses -s (system sleep on AC) and -w <pid> so the assertion drops when ttyd exits. */\nfunction spawnCaffeinate(pid: number): SpawnedProcess | null {\n\ttry {\n\t\tconst proc = spawnProcess(['caffeinate', '-s', '-w', String(pid)], {\n\t\t\tstdout: 'ignore',\n\t\t\tstderr: 'ignore',\n\t\t})\n\t\t// Catch async spawn errors (e.g. caffeinate not found on Linux)\n\t\tproc.exited.catch(() => {\n\t\t\tconsole.warn('remobi: --no-sleep requires caffeinate (macOS only), ignoring')\n\t\t})\n\t\tconsole.log(`remobi: sleep prevention active (caffeinate -s -w ${pid})`)\n\t\treturn proc\n\t} catch {\n\t\tconsole.warn('remobi: --no-sleep requires caffeinate (macOS only), ignoring')\n\t\treturn null\n\t}\n}\n\n/** Start remobi serve: builds overlay in memory, manages ttyd, serves HTTP + WS */\nexport async function serve(\n\tconfig: RemobiConfig,\n\tport: number = DEFAULT_PORT,\n\tcommand: readonly string[] = DEFAULT_COMMAND,\n\tnoSleep = false,\n\thost: string = DEFAULT_HOST,\n): Promise<void> {\n\tconsole.log('remobi: building overlay...')\n\tconst { js, css } = await bundleOverlay(config)\n\n\tconst internalPort = randomInternalPort()\n\tconst ttydArgs = buildTtydArgs(config, internalPort, command)\n\n\tconsole.log(`remobi: starting ttyd on internal port ${internalPort}...`)\n\tconst ttydProc = spawnProcess(['ttyd', ...ttydArgs], {\n\t\tstdout: 'ignore',\n\t\tstderr: 'ignore',\n\t})\n\n\tconst caffeinateProc = noSleep && ttydProc.pid ? spawnCaffeinate(ttydProc.pid) : null\n\n\tawait waitForTtyd(internalPort)\n\n\tconst baseResp = await fetch(`http://127.0.0.1:${internalPort}/`)\n\tconst baseHtml = await baseResp.text()\n\tconst html = injectOverlay(baseHtml, js, css, config)\n\n\tconsole.log('remobi: overlay ready')\n\n\tconst manifestJson = config.pwa.enabled ? manifestToJson(config.name, config.pwa) : null\n\tconst icon180 = readIcon('icon-180.png')\n\tconst icon192 = readIcon('icon-192.png')\n\tconst icon512 = readIcon('icon-512.png')\n\n\t// Per-connection data via WeakMap (replaces Bun's ws.data)\n\tconst connections = new WeakMap<WebSocket, WsData>()\n\n\tconst app = new Hono()\n\tconst { injectWebSocket, upgradeWebSocket } = createNodeWebSocket({ app })\n\n\tapp.use('/ws', async (c, next) => {\n\t\tif (!isAllowedWebSocketOrigin(c.req.header('origin'), c.req.header('host'))) {\n\t\t\treturn withSecurityHeaders(c.text('Forbidden', 403))\n\t\t}\n\t\tawait next()\n\t})\n\n\tapp.get(\n\t\t'/ws',\n\t\tupgradeWebSocket(() => ({\n\t\t\tonOpen(_event: Event, ws: WSContext<WebSocket>) {\n\t\t\t\tconst raw = ws.raw\n\t\t\t\tif (!raw) return\n\n\t\t\t\tconst data: WsData = { backend: null, buffer: [] }\n\t\t\t\tconnections.set(raw, data)\n\n\t\t\t\tconst backend = new WebSocket(`ws://127.0.0.1:${internalPort}/ws`, ['tty'])\n\t\t\t\tbackend.binaryType = 'arraybuffer'\n\t\t\t\tdata.backend = backend\n\n\t\t\t\tbackend.on('open', () => {\n\t\t\t\t\tfor (const msg of data.buffer) {\n\t\t\t\t\t\tbackend.send(msg)\n\t\t\t\t\t}\n\t\t\t\t\tdata.buffer = []\n\t\t\t\t})\n\n\t\t\t\tbackend.on('message', (message: WebSocket.RawData, isBinary: boolean) => {\n\t\t\t\t\tif (isBinary && message instanceof ArrayBuffer) {\n\t\t\t\t\t\tws.send(new Uint8Array(message))\n\t\t\t\t\t} else {\n\t\t\t\t\t\tws.send(message.toString())\n\t\t\t\t\t}\n\t\t\t\t})\n\n\t\t\t\tbackend.on('error', () => {\n\t\t\t\t\tws.close()\n\t\t\t\t})\n\n\t\t\t\tbackend.on('close', () => {\n\t\t\t\t\tws.close()\n\t\t\t\t})\n\t\t\t},\n\t\t\tonMessage(event: MessageEvent, ws: WSContext<WebSocket>) {\n\t\t\t\tconst raw = ws.raw\n\t\t\t\tif (!raw) return\n\t\t\t\tconst data = connections.get(raw)\n\t\t\t\tif (!data) return\n\n\t\t\t\tconst { backend, buffer } = data\n\t\t\t\tif (backend !== null && backend.readyState === WebSocket.OPEN) {\n\t\t\t\t\t// oxlint-disable-next-line typescript/consistent-type-assertions -- WSMessageReceive union needs narrowing for ws.send()\n\t\t\t\t\tbackend.send(event.data as string | ArrayBuffer)\n\t\t\t\t} else {\n\t\t\t\t\tconst msg = event.data\n\t\t\t\t\t// oxlint-disable-next-line typescript/consistent-type-assertions -- WSMessageReceive union needs narrowing for Uint8Array ctor\n\t\t\t\t\tbuffer.push(typeof msg === 'string' ? msg : new Uint8Array(msg as ArrayBuffer))\n\t\t\t\t}\n\t\t\t},\n\t\t\tonClose(_event: CloseEvent, ws: WSContext<WebSocket>) {\n\t\t\t\tconst raw = ws.raw\n\t\t\t\tif (!raw) return\n\t\t\t\tconnections.get(raw)?.backend?.close()\n\t\t\t\tconnections.delete(raw)\n\t\t\t},\n\t\t})),\n\t)\n\n\tapp.get('/', (c) => withSecurityHeaders(c.html(html)))\n\n\tif (manifestJson !== null) {\n\t\tapp.get('/manifest.json', (c) => {\n\t\t\t// oxlint-disable-next-line typescript/consistent-type-assertions -- JSON.parse returns unknown, safe for manifest\n\t\t\treturn withSecurityHeaders(c.json(JSON.parse(manifestJson) as Record<string, unknown>))\n\t\t})\n\t}\n\n\tif (icon180) {\n\t\tapp.get('/apple-touch-icon.png', () => {\n\t\t\treturn withSecurityHeaders(\n\t\t\t\tnew Response(Uint8Array.from(icon180), {\n\t\t\t\t\theaders: { 'content-type': 'image/png' },\n\t\t\t\t}),\n\t\t\t)\n\t\t})\n\t}\n\n\tif (icon192) {\n\t\tapp.get('/icon-192.png', () => {\n\t\t\treturn withSecurityHeaders(\n\t\t\t\tnew Response(Uint8Array.from(icon192), {\n\t\t\t\t\theaders: { 'content-type': 'image/png' },\n\t\t\t\t}),\n\t\t\t)\n\t\t})\n\t}\n\n\tif (icon512) {\n\t\tapp.get('/icon-512.png', () => {\n\t\t\treturn withSecurityHeaders(\n\t\t\t\tnew Response(Uint8Array.from(icon512), {\n\t\t\t\t\theaders: { 'content-type': 'image/png' },\n\t\t\t\t}),\n\t\t\t)\n\t\t})\n\t}\n\n\t// Proxy remaining requests to ttyd (e.g. /token)\n\tapp.all('/*', async (c) => {\n\t\tconst url = new URL(c.req.url)\n\t\tconst backendUrl = `http://127.0.0.1:${internalPort}${url.pathname}${url.search}`\n\t\tconst resp = await fetch(backendUrl, {\n\t\t\tmethod: c.req.method,\n\t\t\theaders: buildProxyRequestHeaders(c.req.raw.headers),\n\t\t\tbody: c.req.raw.body,\n\t\t})\n\t\treturn withSecurityHeaders(\n\t\t\tnew Response(resp.body, {\n\t\t\t\tstatus: resp.status,\n\t\t\t\theaders: resp.headers,\n\t\t\t}),\n\t\t)\n\t})\n\n\tconst server = honoServe({ fetch: app.fetch, port, hostname: host })\n\tinjectWebSocket(server)\n\n\tconsole.log(`remobi: serving on http://${isLoopbackHost(host) ? 'localhost' : host}:${port}`)\n\tif (!isLoopbackHost(host)) {\n\t\tconsole.warn(`remobi: warning: --host ${host} exposes terminal control beyond localhost`)\n\t}\n\n\t// Clean shutdown on SIGINT / SIGTERM\n\tconst cleanup = () => {\n\t\tconsole.log('\\nremobi: shutting down...')\n\t\tserver.close()\n\t\tttydProc.kill()\n\t\tcaffeinateProc?.kill()\n\t\tprocess.exit(0)\n\t}\n\n\tprocess.on('SIGINT', cleanup)\n\tprocess.on('SIGTERM', cleanup)\n\n\t// Keep process alive until ttyd exits\n\tawait ttydProc.exited\n\tserver.close()\n}\n","#!/usr/bin/env node\nimport { existsSync, mkdirSync, readFileSync, realpathSync, writeFileSync } from 'node:fs'\nimport { homedir } from 'node:os'\nimport { dirname, join, resolve } from 'node:path'\nimport { build, injectFromStdin } from './build'\nimport { parseCliArgs } from './src/cli/args'\nimport { defaultConfig, defineConfig, mergeConfig } from './src/config'\nimport {\n\tConfigValidationError,\n\tassertValidConfigOverrides,\n\tassertValidResolvedConfig,\n} from './src/config-validate'\nimport { serve } from './src/serve'\nimport type { RemobiConfig, RemobiConfigOverrides } from './src/types'\nimport { readStdin } from './src/util/node-compat'\n\n// Walk up from module location to find package.json — works from both source and dist/\nfunction loadPackageVersion(): string {\n\tlet dir = import.meta.dirname\n\tfor (let i = 0; i < 5; i++) {\n\t\ttry {\n\t\t\tconst content = readFileSync(resolve(dir, 'package.json'), 'utf-8')\n\t\t\t// oxlint-disable-next-line typescript/consistent-type-assertions -- JSON.parse returns unknown\n\t\t\treturn (JSON.parse(content) as { version: string }).version\n\t\t} catch {\n\t\t\tdir = dirname(dir)\n\t\t}\n\t}\n\treturn '0.0.0'\n}\n\nconst VERSION: string = loadPackageVersion()\n\nfunction usage(): void {\n\tconsole.log(`remobi v${VERSION} — mobile-friendly terminal overlay for ttyd + tmux\n\nUsage:\n remobi serve [--config <path>] [--port <n>] [--host <addr>] [--no-sleep] [-- <command...>]\n Build overlay in memory, manage ttyd, serve with PWA support.\n Default host: 127.0.0.1. Default port: 7681. Default command: tmux new-session -A -s main\n\n remobi build [--config <path>] [--output <path>] [--dry-run]\n Build patched index.html for ttyd --index flag.\n Starts temp ttyd, fetches base HTML, injects overlay.\n\n remobi inject [--config <path>] [--dry-run]\n Pipe mode: reads ttyd HTML from stdin, outputs patched HTML to stdout.\n\n remobi init\n Scaffold a remobi.config.ts with defaults.\n\n remobi --version\n Print version.\n\n remobi --help\n Show this help.\n\nFlags:\n -c, --config <path> Use a specific config file (build/inject/serve)\n -o, --output <path> Build output path (build only)\n -p, --port <n> Port to serve on (serve only, default 7681)\n --host <addr> Host/interface to bind (serve only, default 127.0.0.1)\n -n, --dry-run Validate + print plan only (build/inject)\n --no-sleep Prevent macOS sleep while serving (caffeinate -s, serve only)\n\nExamples:\n remobi serve\n remobi serve --no-sleep\n remobi serve --host 0.0.0.0 --port 8080\n remobi serve --port 8080 -- tmux new -As dev\n remobi build -c ./remobi.config.ts -o ./dist/index.html\n remobi build --dry-run\n curl -s http://127.0.0.1:7681/ | remobi inject --dry-run`)\n}\n\ninterface LoadedConfig {\n\treadonly config: RemobiConfig\n\treadonly source: string\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\nfunction extractDefaultExport(value: unknown): unknown | undefined {\n\tif (!isRecord(value)) return undefined\n\tif (!('default' in value)) return undefined\n\treturn value.default\n}\n\nfunction ensureInjectInputMode(context: string): void {\n\tif (process.stdin.isTTY) {\n\t\tthrow new Error(`${context} expects piped ttyd HTML on stdin`)\n\t}\n}\n\nfunction throwConfigValidationError(source: string, error: ConfigValidationError): never {\n\tthrow new Error(`Config validation failed for ${source}\\n${error.message}`)\n}\n\n/** Convert a config path to its .local sibling, e.g. remobi.config.ts → remobi.config.local.ts */\nfunction toLocalPath(configPath: string): string {\n\tconst dotIdx = configPath.lastIndexOf('.')\n\tif (dotIdx === -1) {\n\t\treturn `${configPath}.local`\n\t}\n\treturn `${configPath.slice(0, dotIdx)}.local${configPath.slice(dotIdx)}`\n}\n\n/** Try to load a .local config override file. Returns undefined if the file does not exist. */\nasync function loadLocalOverrides(localPath: string): Promise<RemobiConfigOverrides | undefined> {\n\tif (!existsSync(localPath)) {\n\t\treturn undefined\n\t}\n\n\tconst mod = await import(localPath)\n\tconst defaultExport = extractDefaultExport(mod)\n\tif (defaultExport === undefined) {\n\t\tthrow new Error(`Local config file has no default export: ${localPath}`)\n\t}\n\n\tassertValidOverridesOrThrow(defaultExport, localPath)\n\treturn defaultExport\n}\n\nfunction assertValidOverridesOrThrow(\n\tvalue: unknown,\n\tsource: string,\n): asserts value is RemobiConfigOverrides {\n\ttry {\n\t\tassertValidConfigOverrides(value)\n\t} catch (error) {\n\t\tif (error instanceof ConfigValidationError) {\n\t\t\tthrowConfigValidationError(source, error)\n\t\t}\n\t\tthrow error\n\t}\n}\n\nfunction assertValidResolvedOrThrow(value: unknown, source: string): asserts value is RemobiConfig {\n\ttry {\n\t\tassertValidResolvedConfig(value)\n\t} catch (error) {\n\t\tif (error instanceof ConfigValidationError) {\n\t\t\tthrowConfigValidationError(source, error)\n\t\t}\n\t\tthrow error\n\t}\n}\n\nasync function loadConfig(configPath: string | undefined): Promise<LoadedConfig> {\n\tlet resolved = configPath\n\tif (!resolved) {\n\t\t// Search order: cwd → XDG config dir (~/.config/remobi/)\n\t\tconst names = ['remobi.config.ts', 'remobi.config.js']\n\t\tconst searchDirs = [\n\t\t\tprocess.cwd(),\n\t\t\tjoin(process.env.XDG_CONFIG_HOME ?? join(homedir(), '.config'), 'remobi'),\n\t\t]\n\t\tfor (const dir of searchDirs) {\n\t\t\tfor (const name of names) {\n\t\t\t\tconst full = join(dir, name)\n\t\t\t\tif (existsSync(full)) {\n\t\t\t\t\tresolved = full\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (resolved) break\n\t\t}\n\t}\n\n\tif (resolved) {\n\t\tconst abs = resolve(process.cwd(), resolved)\n\t\tconst mod = await import(abs)\n\t\tconst defaultExport = extractDefaultExport(mod)\n\t\tif (defaultExport === undefined) {\n\t\t\tthrow new Error(`Config file has no default export: ${abs}`)\n\t\t}\n\n\t\tassertValidOverridesOrThrow(defaultExport, abs)\n\t\tconst sharedConfig = defineConfig(defaultExport)\n\n\t\t// Apply .local overrides on top of the shared config\n\t\tconst localPath = toLocalPath(abs)\n\t\tconst localOverrides = await loadLocalOverrides(localPath)\n\t\tconst config =\n\t\t\tlocalOverrides !== undefined ? mergeConfig(sharedConfig, localOverrides) : sharedConfig\n\n\t\tconst sourceLabel = localOverrides !== undefined ? `${abs} + ${localPath}` : abs\n\t\tassertValidResolvedOrThrow(config, sourceLabel)\n\t\treturn { config, source: sourceLabel }\n\t}\n\n\tassertValidResolvedOrThrow(defaultConfig, 'built-in defaults')\n\n\treturn {\n\t\tconfig: defaultConfig,\n\t\tsource: 'built-in defaults',\n\t}\n}\n\nasync function main(): Promise<void> {\n\tconst parsed = parseCliArgs(process.argv.slice(2))\n\tif (!parsed.ok) {\n\t\tconsole.error(parsed.error)\n\t\tusage()\n\t\tprocess.exit(1)\n\t}\n\n\tconst { command, configPath, outputPath, dryRun, port, host, noSleep, command_ } = parsed.value\n\n\tswitch (command) {\n\t\tcase 'serve': {\n\t\t\tconst loaded = await loadConfig(configPath)\n\t\t\tawait serve(loaded.config, port, command_.length > 0 ? command_ : undefined, noSleep, host)\n\t\t\tbreak\n\t\t}\n\n\t\tcase 'build': {\n\t\t\tconst loaded = await loadConfig(configPath)\n\t\t\tconst targetPath = outputPath\n\t\t\t\t? resolve(process.cwd(), outputPath)\n\t\t\t\t: resolve(process.cwd(), 'dist/index.html')\n\n\t\t\tif (dryRun) {\n\t\t\t\tconsole.log('Dry run: build')\n\t\t\t\tconsole.log(`- config: ${loaded.source}`)\n\t\t\t\tconsole.log(`- output: ${targetPath}`)\n\t\t\t\tconsole.log('- action: would bundle overlay, fetch ttyd base HTML, inject, and write file')\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\t// Ensure output directory exists\n\t\t\tmkdirSync(dirname(targetPath), { recursive: true })\n\n\t\t\tawait build(loaded.config, targetPath)\n\t\t\tconsole.log(`Built: ${targetPath}`)\n\t\t\tbreak\n\t\t}\n\n\t\tcase 'inject': {\n\t\t\tconst loaded = await loadConfig(configPath)\n\t\t\tif (dryRun) {\n\t\t\t\tensureInjectInputMode('remobi inject --dry-run')\n\t\t\t\tconst dryRunStdin = await readStdin()\n\t\t\t\tif (dryRunStdin.trim().length === 0) {\n\t\t\t\t\tthrow new Error('remobi inject --dry-run expects piped ttyd HTML on stdin')\n\t\t\t\t}\n\t\t\t\tconsole.log('Dry run: inject')\n\t\t\t\tconsole.log(`- config: ${loaded.source}`)\n\t\t\t\tconsole.log('- stdin: piped input detected')\n\t\t\t\tconsole.log('- action: would read stdin HTML, inject overlay, and write to stdout')\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\tensureInjectInputMode('remobi inject')\n\t\t\tconst result = await injectFromStdin(loaded.config)\n\t\t\tprocess.stdout.write(result)\n\t\t\tbreak\n\t\t}\n\n\t\tcase 'init': {\n\t\t\tconst targetPath = resolve(process.cwd(), 'remobi.config.ts')\n\t\t\tif (existsSync(targetPath)) {\n\t\t\t\tconsole.error('remobi.config.ts already exists')\n\t\t\t\tprocess.exit(1)\n\t\t\t}\n\t\t\tconst template = `import { defineConfig } from 'remobi'\n\nexport default defineConfig({\n // name: 'remobi', // app name (tab title, PWA home screen label)\n // theme: 'catppuccin-mocha',\n // font: {\n // family: 'JetBrainsMono NFM, monospace',\n // mobileSizeDefault: 16,\n // sizeRange: [8, 32],\n // },\n //\n // Toolbar/drawer accept a plain array (replace) or a function (transform):\n //\n // toolbar: { row1: [{ id, label, description, action }], row2: [...] },\n //\n // drawer: {\n // buttons: [\n // { id: 'sessions', label: 'Sessions', description: 'Choose tmux session', action: { type: 'send', data: '\\\\x02s' } },\n // ],\n // },\n //\n // toolbar: {\n // row2: (defaults) => defaults.filter((b) => b.id !== 'q'),\n // },\n //\n // drawer: {\n // buttons: (defaults) => [\n // ...defaults,\n // { id: 'my-btn', label: 'X', description: 'Send x', action: { type: 'send', data: 'x' } },\n // ],\n // },\n //\n // gestures: {\n // swipe: {\n // enabled: true,\n // left: '\\\\x02n', // data sent on swipe left (default: next tmux window)\n // right: '\\\\x02p', // data sent on swipe right (default: prev tmux window)\n // leftLabel: 'Next tmux window',\n // rightLabel: 'Previous tmux window',\n // },\n // pinch: { enabled: true },\n // scroll: { strategy: 'wheel', sensitivity: 40 },\n // },\n // mobile: {\n // initData: '\\\\x02z', // send on load when viewport < widthThreshold (auto-zoom pane)\n // widthThreshold: 768, // px — default matches phone/tablet breakpoint\n // },\n // floatingButtons: [\n // // Always-visible top-left buttons (touch devices only)\n // { position: 'top-left', buttons: [{ id: 'zoom', label: 'Zoom', description: 'Toggle pane zoom', action: { type: 'send', data: '\\\\x02z' } }] },\n // ],\n // pwa: {\n // enabled: true, // enable PWA manifest + meta tags (used by remobi serve)\n // shortName: 'remobi', // short name for home screen icon (defaults to name)\n // themeColor: '#1e1e2e', // theme-color meta tag + manifest\n // },\n // reconnect: {\n // enabled: true, // show overlay + auto-reload on connection loss (default true)\n // },\n})\n`\n\t\t\twriteFileSync(targetPath, template)\n\t\t\tconsole.log(`Created: ${targetPath}`)\n\t\t\tbreak\n\t\t}\n\n\t\tcase 'version':\n\t\t\tconsole.log(VERSION)\n\t\t\tbreak\n\n\t\tcase 'help':\n\t\t\tusage()\n\t\t\tbreak\n\n\t\tdefault:\n\t\t\tconsole.error(`Unknown command: ${command}`)\n\t\t\tusage()\n\t\t\tprocess.exit(1)\n\t}\n}\n\nconst entryScript = process.argv[1]\nif (entryScript && import.meta.filename === realpathSync(entryScript)) {\n\tmain().catch((err: unknown) => {\n\t\tconsole.error(err)\n\t\tprocess.exit(1)\n\t})\n}\n"],"mappings":"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"}
|