@jmfederico/pi-web 1.202605.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +321 -0
  3. package/dist/cli.js +256 -0
  4. package/dist/cli.js.map +1 -0
  5. package/dist/client/assets/CodeViewer-DsXI9VCn.js +4 -0
  6. package/dist/client/assets/TerminalPanel-CpzJEFv1.js +47 -0
  7. package/dist/client/assets/index-Cbr8EG8h.js +687 -0
  8. package/dist/client/assets/vendor-editor-core-hulUn3GY.js +12 -0
  9. package/dist/client/assets/vendor-editor-languages-Cjllm-a8.js +26 -0
  10. package/dist/client/assets/vendor-editor-legacy-B4QLsWF8.js +1 -0
  11. package/dist/client/assets/vendor-terminal-DDGTF8rc.css +1 -0
  12. package/dist/client/assets/vendor-terminal-DjQ08hXu.js +16 -0
  13. package/dist/client/index.html +16 -0
  14. package/dist/config.js +92 -0
  15. package/dist/config.js.map +1 -0
  16. package/dist/server/app.js +80 -0
  17. package/dist/server/app.js.map +1 -0
  18. package/dist/server/git/gitService.js +118 -0
  19. package/dist/server/git/gitService.js.map +1 -0
  20. package/dist/server/gitRoutes.js +23 -0
  21. package/dist/server/gitRoutes.js.map +1 -0
  22. package/dist/server/index.js +7 -0
  23. package/dist/server/index.js.map +1 -0
  24. package/dist/server/projects/directorySuggestions.js +37 -0
  25. package/dist/server/projects/directorySuggestions.js.map +1 -0
  26. package/dist/server/projects/projectService.js +31 -0
  27. package/dist/server/projects/projectService.js.map +1 -0
  28. package/dist/server/realtime/sessionEventHub.js +36 -0
  29. package/dist/server/realtime/sessionEventHub.js.map +1 -0
  30. package/dist/server/sessiond/config.js +9 -0
  31. package/dist/server/sessiond/config.js.map +1 -0
  32. package/dist/server/sessiond/sessionDaemonClient.js +65 -0
  33. package/dist/server/sessiond/sessionDaemonClient.js.map +1 -0
  34. package/dist/server/sessiond/sessionProxyRoutes.js +63 -0
  35. package/dist/server/sessiond/sessionProxyRoutes.js.map +1 -0
  36. package/dist/server/sessiond.js +45 -0
  37. package/dist/server/sessiond.js.map +1 -0
  38. package/dist/server/sessions/builtinCommands.js +27 -0
  39. package/dist/server/sessions/builtinCommands.js.map +1 -0
  40. package/dist/server/sessions/piSessionService.js +517 -0
  41. package/dist/server/sessions/piSessionService.js.map +1 -0
  42. package/dist/server/sessions/sessionArchiveStore.js +68 -0
  43. package/dist/server/sessions/sessionArchiveStore.js.map +1 -0
  44. package/dist/server/sessions/sessionCommandService.js +134 -0
  45. package/dist/server/sessions/sessionCommandService.js.map +1 -0
  46. package/dist/server/sessions/sessionNameGenerator.js +68 -0
  47. package/dist/server/sessions/sessionNameGenerator.js.map +1 -0
  48. package/dist/server/sessions/sessionRoutes.js +116 -0
  49. package/dist/server/sessions/sessionRoutes.js.map +1 -0
  50. package/dist/server/sessions/sessionRuntimeStore.js +2 -0
  51. package/dist/server/sessions/sessionRuntimeStore.js.map +1 -0
  52. package/dist/server/storage/projectStore.js +88 -0
  53. package/dist/server/storage/projectStore.js.map +1 -0
  54. package/dist/server/terminalProxyRoutes.js +70 -0
  55. package/dist/server/terminalProxyRoutes.js.map +1 -0
  56. package/dist/server/terminals/terminalRoutes.js +70 -0
  57. package/dist/server/terminals/terminalRoutes.js.map +1 -0
  58. package/dist/server/terminals/terminalService.js +115 -0
  59. package/dist/server/terminals/terminalService.js.map +1 -0
  60. package/dist/server/types.js +2 -0
  61. package/dist/server/types.js.map +1 -0
  62. package/dist/server/workspaceExplorerRoutes.js +24 -0
  63. package/dist/server/workspaceExplorerRoutes.js.map +1 -0
  64. package/dist/server/workspaces/fileContentService.js +50 -0
  65. package/dist/server/workspaces/fileContentService.js.map +1 -0
  66. package/dist/server/workspaces/fileSuggestions.js +84 -0
  67. package/dist/server/workspaces/fileSuggestions.js.map +1 -0
  68. package/dist/server/workspaces/fileTreeService.js +26 -0
  69. package/dist/server/workspaces/fileTreeService.js.map +1 -0
  70. package/dist/server/workspaces/gitWorktreeDiscovery.js +33 -0
  71. package/dist/server/workspaces/gitWorktreeDiscovery.js.map +1 -0
  72. package/dist/server/workspaces/pathSafety.js +38 -0
  73. package/dist/server/workspaces/pathSafety.js.map +1 -0
  74. package/dist/server/workspaces/workspaceContext.js +8 -0
  75. package/dist/server/workspaces/workspaceContext.js.map +1 -0
  76. package/dist/server/workspaces/workspaceService.js +39 -0
  77. package/dist/server/workspaces/workspaceService.js.map +1 -0
  78. package/dist/shared/apiTypes.js +2 -0
  79. package/dist/shared/apiTypes.js.map +1 -0
  80. package/extensions/pi-web.ts +144 -0
  81. package/install.sh +5 -0
  82. package/package.json +107 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Federico Jaramillo Martinez
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,321 @@
1
+ # Pi Web
2
+
3
+ ![Pi Web](docs/assets/pi-web-banner.png)
4
+
5
+ ![Pi Web demo](docs/assets/pi-web-demo.gif)
6
+
7
+ **Run AI coding agents on your own machine or server, keep them alive in real workspaces, and control everything from a browser.**
8
+
9
+ Pi Web is a web control plane for [Pi Coding Agent](https://github.com/earendil-works/pi/tree/main/packages/coding-agent). Add your repositories once, open project workspaces and git worktrees, start agent sessions inside them, and come back later without losing the work. Your browser becomes the cockpit; your server becomes the persistent development environment. Start on your laptop, check in from your phone, and continue from an iPad or another machine whenever that is the device you have at hand.
10
+
11
+ With Pi Web you can:
12
+
13
+ - launch and supervise multiple coding-agent sessions in parallel;
14
+ - keep sessions running when your browser disconnects or the UI restarts;
15
+ - organize agent work by project, workspace, branch, experiment, or review;
16
+ - use git worktrees to isolate concurrent features and fixes;
17
+ - chat with Pi Coding Agent through a realtime web UI;
18
+ - move fluidly between laptop, phone, tablet, and desktop without moving the development environment;
19
+ - turn any server, desktop, or remote dev box into an agent-first development hub.
20
+
21
+ ## Why use Pi Web?
22
+
23
+ Agentic development works best when agents are not trapped inside a single local terminal. They need stable environments, access to real repositories, and room to work across branches and tasks. Humans need the opposite: a clear place to supervise, redirect, review, and decide.
24
+
25
+ Pi Web connects those two worlds. The work stays in the server-side environment while you move between devices: laptop for deep focus, phone for a quick check-in, tablet for review, desktop when you are back at a desk. It is not trying to recreate the old desktop IDE in a browser; it is a control surface for persistent, parallel, human-in-the-loop agent work.
26
+
27
+ ## Core model
28
+
29
+ Pi Web organizes work into three levels:
30
+
31
+ ```text
32
+ Project a folder on the server
33
+ Workspace a git worktree, or the project folder for non-git projects
34
+ Session a chat with Pi Coding Agent running inside a workspace
35
+ ```
36
+
37
+ This maps naturally to real development work:
38
+
39
+ - add a project once;
40
+ - use worktrees to separate branches, features, experiments, and reviews;
41
+ - start one or more agent sessions inside each workspace;
42
+ - leave sessions running even when the browser disconnects or the UI restarts.
43
+
44
+ ## Features
45
+
46
+ - Add and list server-side projects.
47
+ - Discover git worktrees automatically with `git worktree list --porcelain`.
48
+ - Support non-git folders as single-workspace projects.
49
+ - Start, resume, archive, and restore Pi sessions per workspace.
50
+ - Chat with Pi Coding Agent through realtime WebSocket events.
51
+ - Keep active agent runtimes alive across browser disconnects and web/API restarts.
52
+ - Explicitly stop or abort active session work.
53
+ - View live session status: streaming, compaction, bash activity, token usage, cost, model, and context usage.
54
+ - Send prompts, shell input, and supported commands through the Pi SDK path.
55
+ - Reuse your existing Pi auth and model configuration from `~/.pi/agent`.
56
+
57
+ ## Architecture
58
+
59
+ Pi Web uses a split-process architecture so agent runtimes are not owned by the browser-facing dev server.
60
+
61
+ ```text
62
+ Browser UI
63
+
64
+
65
+ Fastify Web/API process
66
+ │ HTTP + WebSocket proxy
67
+
68
+ Session daemon
69
+
70
+
71
+ Pi Coding Agent SDK
72
+ ```
73
+
74
+ ### Session daemon
75
+
76
+ The session daemon owns active Pi session runtimes. It is intended to be long-lived so sessions can survive browser disconnects and web/API restarts.
77
+
78
+ ### Web/API/UI server
79
+
80
+ The web process serves the API and browser UI. In development it can autoreload freely while active sessions continue running in the daemon.
81
+
82
+ ## State model
83
+
84
+ Pi Web keeps its own state intentionally small:
85
+
86
+ - Projects: `~/.pi-web/projects.json`
87
+ - Workspaces: discovered from git worktrees, not stored
88
+ - Sessions and chat history: Pi's default JSONL session storage
89
+ - Active session runtimes and WebSockets: memory in the session daemon
90
+
91
+ ## Install
92
+
93
+ Recommended install uses npm plus systemd user services:
94
+
95
+ ```bash
96
+ npm install -g @jmfederico/pi-web
97
+
98
+ # Recommended on servers: keep user services running after logout/reboot.
99
+ sudo loginctl enable-linger "$USER"
100
+
101
+ pi-web install
102
+ ```
103
+
104
+ `loginctl enable-linger` is optional for local desktop use, but recommended on servers. It lets the user systemd manager start at boot and continue running after you log out, so Pi Web remains available without an active SSH/login session.
105
+
106
+ This writes and starts:
107
+
108
+ - `~/.config/systemd/user/pi-web-sessiond.service`
109
+ - `~/.config/systemd/user/pi-web.service`
110
+
111
+ The generated services run through `bash -lc` so they see a shell environment similar to running `pi` from your terminal.
112
+
113
+ To check whether lingering is enabled:
114
+
115
+ ```bash
116
+ loginctl show-user "$USER" -p Linger
117
+ ```
118
+
119
+ Open <http://127.0.0.1:8504>.
120
+
121
+ Useful commands:
122
+
123
+ ```bash
124
+ pi-web status
125
+ pi-web logs
126
+ pi-web restart
127
+ pi-web doctor
128
+ pi-web uninstall
129
+ ```
130
+
131
+ One-line install is also available for users who prefer it:
132
+
133
+ ```bash
134
+ curl -fsSL https://raw.githubusercontent.com/jmfederico/pi-web/main/install.sh | sh
135
+ ```
136
+
137
+ Pi Web is also published as a Pi package. Installing it through Pi exposes a `/pi-web` command inside Pi:
138
+
139
+ ```bash
140
+ pi install npm:@jmfederico/pi-web
141
+ ```
142
+
143
+ Then in Pi:
144
+
145
+ ```text
146
+ /pi-web install
147
+ /pi-web status
148
+ /pi-web logs
149
+ /pi-web restart
150
+ /pi-web doctor
151
+ ```
152
+
153
+ The Pi command is a convenience wrapper around the same service installer. `/pi-web logs` shows the last 100 journal lines; use `pi-web logs` in a shell when you want to follow logs continuously.
154
+
155
+ Advanced users may run the binaries however they prefer:
156
+
157
+ ```bash
158
+ pi-web-sessiond
159
+ PI_WEB_PORT=8504 pi-web-server
160
+ ```
161
+
162
+ To install directly from the GitHub repository instead of npm:
163
+
164
+ ```bash
165
+ npm install -g github:jmfederico/pi-web#main
166
+ pi-web install
167
+ ```
168
+
169
+ For git installs, `dist/` must already be present in the repository because npm does not run the package's `prepack` script for git dependencies.
170
+
171
+ ## Development quick start
172
+
173
+ ```bash
174
+ npm install
175
+ npm run dev
176
+ ```
177
+
178
+ Open the Vite URL, usually <http://localhost:8505>.
179
+
180
+ For the recommended split development setup, run these in separate terminals:
181
+
182
+ ```bash
183
+ npm run dev:sessiond
184
+ npm run dev:web
185
+ npm run dev:client
186
+ ```
187
+
188
+ You can restart `dev:web` or `dev:client` without stopping active Pi sessions.
189
+
190
+ ## Production-style run from a checkout
191
+
192
+ ```bash
193
+ npm run build
194
+ npm run start:sessiond
195
+ PI_WEB_PORT=8504 npm start
196
+ ```
197
+
198
+ ## Packaging and publishing
199
+
200
+ ```bash
201
+ npm run verify
202
+ npm run pack:dry
203
+ npm publish --access public
204
+ ```
205
+
206
+ `prepack` builds `dist/` before npm creates the tarball, and `prepublishOnly` runs verification before publishing. Releases can also be published by the GitHub Actions npm workflow when a GitHub release is published.
207
+
208
+ Pi Web uses a single-line CalVer-inspired npm version: `MAJOR.YYYYMM.SEQUENCE`, for example `1.202605.1`. The major number signals breaking-change eras; the middle number is the release month; the final number increments for additional releases in that month. Older major eras may be deprecated rather than maintained in parallel.
209
+
210
+ Pi Web declares `@earendil-works/pi-coding-agent` as a peer dependency (`>=0.74.0 <1`) and a development dependency for local builds. This keeps published installs flexible: npm 7+ installs the peer automatically, and users can upgrade the Pi package within the compatible range without Pi Web pinning a separate copy.
211
+
212
+
213
+ The web server defaults to `127.0.0.1:8504`. Set `PI_WEB_HOST=0.0.0.0` only when you intentionally want to bind directly on all interfaces.
214
+
215
+ The session daemon defaults to a private Unix socket at:
216
+
217
+ ```text
218
+ ~/.pi-web/sessiond.sock
219
+ ```
220
+
221
+ Environment variables:
222
+
223
+ - `PI_WEB_PORT` / `PORT` — web server port. Defaults to `8504`.
224
+ - `PI_WEB_HOST` — web server bind host. Defaults to `127.0.0.1`.
225
+ - `PI_WEB_DATA_DIR` — Pi Web data directory. Defaults to `~/.pi-web`.
226
+ - `PI_WEB_SESSIOND_SOCKET` — Unix socket path used by both the daemon and web process when `PI_WEB_SESSIOND_URL` is not set. Defaults to `$PI_WEB_DATA_DIR/sessiond.sock`.
227
+ - `PI_WEB_SESSIOND_PORT` — optional TCP port for the daemon. If unset, the daemon listens on the Unix socket instead.
228
+ - `PI_WEB_SESSIOND_HOST` — daemon TCP bind host when `PI_WEB_SESSIOND_PORT` is set. Defaults to `127.0.0.1`.
229
+ - `PI_WEB_SESSIOND_URL` — daemon URL used by the web process when connecting over TCP, for example `http://127.0.0.1:3001`. If you set `PI_WEB_SESSIOND_PORT`, set this for the web process too.
230
+ - `PI_WEB_PROJECTS_FILE` — optional override for the projects storage JSON file. Defaults to `$PI_WEB_DATA_DIR/projects.json`.
231
+
232
+ ## systemd user services
233
+
234
+ A practical local or server setup is two user services:
235
+
236
+ - `pi-web-sessiond.service` runs `npm run start:sessiond` without autoreload.
237
+ - `pi-web-ui-dev.service` runs `npm run dev:web` and `npm run dev:client` for API reloads and Vite HMR.
238
+
239
+ Example units:
240
+
241
+ ```ini
242
+ # ~/.config/systemd/user/pi-web-sessiond.service
243
+ [Unit]
244
+ Description=Pi Web session daemon
245
+
246
+ [Service]
247
+ Type=simple
248
+ WorkingDirectory=/srv/dev/pi-web
249
+ ExecStart=/bin/bash -lc 'exec npm run start:sessiond'
250
+ Restart=no
251
+
252
+ [Install]
253
+ WantedBy=default.target
254
+ ```
255
+
256
+ ```ini
257
+ # ~/.config/systemd/user/pi-web-ui-dev.service
258
+ [Unit]
259
+ Description=Pi Web UI dev server
260
+ After=pi-web-sessiond.service
261
+ Wants=pi-web-sessiond.service
262
+
263
+ [Service]
264
+ Type=simple
265
+ WorkingDirectory=/srv/dev/pi-web
266
+ ExecStart=/bin/bash -lc 'trap "kill 0" EXIT; npm run dev:web & npm run dev:client & wait'
267
+ Restart=no
268
+
269
+ [Install]
270
+ WantedBy=default.target
271
+ ```
272
+
273
+ On servers, enable persistent user services so the user systemd manager starts at boot and remains running after logout:
274
+
275
+ ```bash
276
+ sudo loginctl enable-linger "$USER"
277
+ loginctl show-user "$USER" -p Linger
278
+ ```
279
+
280
+ After creating or changing units:
281
+
282
+ ```bash
283
+ systemctl --user daemon-reload
284
+ systemctl --user enable --now pi-web-sessiond.service
285
+ systemctl --user enable --now pi-web-ui-dev.service
286
+ ```
287
+
288
+ Useful logs:
289
+
290
+ ```bash
291
+ journalctl --user -u pi-web-sessiond.service -f
292
+ journalctl --user -u pi-web-ui-dev.service -f
293
+ ```
294
+
295
+ If code affecting the session daemon changes, restart it manually:
296
+
297
+ ```bash
298
+ systemctl --user restart pi-web-sessiond.service
299
+ ```
300
+
301
+ ## Current limitations
302
+
303
+ - Assumes trusted users and trusted server paths.
304
+ - Not a sandbox, permission model, or secure multi-tenant platform.
305
+ - Some Pi TUI slash-command behavior is not yet represented exactly in the web UI.
306
+ - Workspaces are discovered from existing git worktrees; UI-driven worktree management is a natural next step.
307
+
308
+ ## Vision
309
+
310
+ Pi Web is the beginning of an agent-first development environment:
311
+
312
+ - agents run persistently on servers;
313
+ - humans connect through the browser;
314
+ - work is organized by projects, workspaces, and sessions;
315
+ - the UI grows around the needs of agentic development rather than the habits of local IDEs.
316
+
317
+ The goal is simple: make it practical to run more development remotely, in parallel, with agents as first-class participants and humans focused on direction, judgment, and review.
318
+
319
+ ## License
320
+
321
+ MIT © 2026 Federico Jaramillo Martinez. See [LICENSE](LICENSE).
package/dist/cli.js ADDED
@@ -0,0 +1,256 @@
1
+ #!/usr/bin/env node
2
+ import { existsSync } from "node:fs";
3
+ import { mkdir, rm, writeFile } from "node:fs/promises";
4
+ import { homedir, userInfo } from "node:os";
5
+ import { dirname, join, resolve } from "node:path";
6
+ import { spawnSync } from "node:child_process";
7
+ import { defaultPiWebConfigPath, examplePiWebConfig } from "./config.js";
8
+ const serviceDir = join(homedir(), ".config", "systemd", "user");
9
+ const sessiondServiceName = "pi-web-sessiond.service";
10
+ const webServiceName = "pi-web.service";
11
+ function run(command, args, options = {}) {
12
+ const result = spawnSync(command, args, { stdio: "inherit" });
13
+ const status = result.status ?? 1;
14
+ if (options.check === true && status !== 0)
15
+ process.exit(status);
16
+ return status;
17
+ }
18
+ function capture(command, args) {
19
+ const result = spawnSync(command, args, { encoding: "utf8" });
20
+ return { status: result.status ?? 1, stdout: result.stdout, stderr: result.stderr };
21
+ }
22
+ function hasCommand(command) {
23
+ return capture("/usr/bin/env", ["bash", "-lc", `command -v ${command}`]).status === 0;
24
+ }
25
+ function isLingerEnabled() {
26
+ if (!hasCommand("loginctl"))
27
+ return undefined;
28
+ const result = capture("loginctl", ["show-user", userInfo().username, "-p", "Linger"]);
29
+ if (result.status !== 0)
30
+ return undefined;
31
+ const value = result.stdout.trim();
32
+ if (value === "Linger=yes")
33
+ return true;
34
+ if (value === "Linger=no")
35
+ return false;
36
+ return undefined;
37
+ }
38
+ function parseInstallOptions(args) {
39
+ const options = { host: "127.0.0.1", port: "8504" };
40
+ for (let i = 0; i < args.length; i += 1) {
41
+ const arg = args[i];
42
+ if (arg === undefined)
43
+ continue;
44
+ if (arg === "--host") {
45
+ const value = args[i + 1];
46
+ if (value === undefined)
47
+ throw new Error("--host requires a value");
48
+ options.host = value;
49
+ i += 1;
50
+ }
51
+ else if (arg.startsWith("--host=")) {
52
+ options.host = arg.slice("--host=".length);
53
+ }
54
+ else if (arg === "--port") {
55
+ const value = args[i + 1];
56
+ if (value === undefined)
57
+ throw new Error("--port requires a value");
58
+ options.port = value;
59
+ i += 1;
60
+ }
61
+ else if (arg.startsWith("--port=")) {
62
+ options.port = arg.slice("--port=".length);
63
+ }
64
+ else if (arg === "--config") {
65
+ const value = args[i + 1];
66
+ if (value === undefined)
67
+ throw new Error("--config requires a value");
68
+ options.config = value;
69
+ i += 1;
70
+ }
71
+ else if (arg.startsWith("--config=")) {
72
+ options.config = arg.slice("--config=".length);
73
+ }
74
+ else if (arg === "--user-systemd") {
75
+ // Accepted for readability; user systemd is the only installer target for now.
76
+ }
77
+ else {
78
+ throw new Error(`Unknown install option: ${arg}`);
79
+ }
80
+ }
81
+ return options;
82
+ }
83
+ function shellSingleQuote(value) {
84
+ return `'${value.replaceAll("'", "'\\''")}'`;
85
+ }
86
+ function systemdEscape(value) {
87
+ return value.replaceAll("\\", "\\\\").replaceAll('"', '\\"');
88
+ }
89
+ function sessiondExec() {
90
+ const configured = process.env["PI_WEB_SESSIOND_EXEC"]?.trim();
91
+ return configured === undefined || configured === "" ? "pi-web-sessiond" : configured;
92
+ }
93
+ function webExec() {
94
+ const configured = process.env["PI_WEB_SERVER_EXEC"]?.trim();
95
+ return configured === undefined || configured === "" ? "pi-web-server" : configured;
96
+ }
97
+ function sessiondUnit() {
98
+ return `[Unit]
99
+ Description=Pi Web session daemon
100
+
101
+ [Service]
102
+ Type=simple
103
+ ExecStart=/usr/bin/env bash -lc ${shellSingleQuote(`exec ${sessiondExec()}`)}
104
+ Restart=on-failure
105
+ RestartSec=2
106
+
107
+ [Install]
108
+ WantedBy=default.target
109
+ `;
110
+ }
111
+ function webUnit(options) {
112
+ const configEnvironment = options.config === undefined ? "" : `Environment="PI_WEB_CONFIG=${systemdEscape(resolve(options.config))}"\n`;
113
+ return `[Unit]
114
+ Description=Pi Web server
115
+ After=${sessiondServiceName}
116
+ Wants=${sessiondServiceName}
117
+
118
+ [Service]
119
+ Type=simple
120
+ ${configEnvironment}ExecStart=/usr/bin/env bash -lc ${shellSingleQuote(`exec ${webExec()}`)}
121
+ Restart=on-failure
122
+ RestartSec=2
123
+
124
+ [Install]
125
+ WantedBy=default.target
126
+ `;
127
+ }
128
+ async function writeInitialConfig(options) {
129
+ const configPath = options.config === undefined ? defaultPiWebConfigPath() : resolve(options.config);
130
+ await mkdir(dirname(configPath), { recursive: true });
131
+ if (!existsSync(configPath)) {
132
+ await writeFile(configPath, examplePiWebConfig({ host: options.host, port: Number(options.port) }));
133
+ }
134
+ return configPath;
135
+ }
136
+ async function install(args) {
137
+ const options = parseInstallOptions(args);
138
+ if (!hasCommand("systemctl"))
139
+ throw new Error("systemctl was not found in a bash login shell");
140
+ if (process.env["PI_WEB_SERVER_EXEC"] === undefined && !hasCommand("pi-web-server"))
141
+ throw new Error("pi-web-server was not found in a bash login shell. Is pi-web installed globally?");
142
+ if (process.env["PI_WEB_SESSIOND_EXEC"] === undefined && !hasCommand("pi-web-sessiond"))
143
+ throw new Error("pi-web-sessiond was not found in a bash login shell. Is pi-web installed globally?");
144
+ const configPath = await writeInitialConfig(options);
145
+ await mkdir(serviceDir, { recursive: true });
146
+ await writeFile(join(serviceDir, sessiondServiceName), sessiondUnit());
147
+ await writeFile(join(serviceDir, webServiceName), webUnit(options));
148
+ run("systemctl", ["--user", "daemon-reload"], { check: true });
149
+ run("systemctl", ["--user", "enable", "--now", sessiondServiceName], { check: true });
150
+ run("systemctl", ["--user", "enable", "--now", webServiceName], { check: true });
151
+ console.log(`\nPi Web is installed and starting.`);
152
+ console.log(`Config: ${configPath}`);
153
+ console.log(`Open: http://${options.host === "0.0.0.0" ? "127.0.0.1" : options.host}:${options.port}`);
154
+ const linger = isLingerEnabled();
155
+ if (linger === false) {
156
+ console.log("\nRecommended for server use: keep user services running after logout/reboot:");
157
+ console.log(` sudo loginctl enable-linger ${userInfo().username}`);
158
+ }
159
+ else if (linger === undefined) {
160
+ console.log("\nRecommended for server use: enable systemd user lingering so services survive logout/reboot:");
161
+ console.log(` sudo loginctl enable-linger ${userInfo().username}`);
162
+ }
163
+ console.log("\nUseful commands:");
164
+ console.log(" pi-web status");
165
+ console.log(" pi-web logs");
166
+ console.log(" pi-web restart");
167
+ }
168
+ async function uninstall() {
169
+ run("systemctl", ["--user", "disable", "--now", webServiceName]);
170
+ run("systemctl", ["--user", "disable", "--now", sessiondServiceName]);
171
+ await rm(join(serviceDir, webServiceName), { force: true });
172
+ await rm(join(serviceDir, sessiondServiceName), { force: true });
173
+ run("systemctl", ["--user", "daemon-reload"]);
174
+ console.log("Pi Web systemd user services removed.");
175
+ }
176
+ function serviceAction(action) {
177
+ run("systemctl", ["--user", action, sessiondServiceName, webServiceName], { check: action !== "status" });
178
+ }
179
+ function logs() {
180
+ run("journalctl", ["--user", "-u", sessiondServiceName, "-u", webServiceName, "-f"]);
181
+ }
182
+ function doctor() {
183
+ const checks = [
184
+ ["systemctl --user", ["systemctl", "--user", "--version"]],
185
+ ["bash login shell can find node", ["/usr/bin/env", "bash", "-lc", "command -v node"]],
186
+ ["bash login shell can find npm", ["/usr/bin/env", "bash", "-lc", "command -v npm"]],
187
+ ["bash login shell can find pi", ["/usr/bin/env", "bash", "-lc", "command -v pi"]],
188
+ ["bash login shell can find pi-web-server", ["/usr/bin/env", "bash", "-lc", "command -v pi-web-server"]],
189
+ ["bash login shell can find pi-web-sessiond", ["/usr/bin/env", "bash", "-lc", "command -v pi-web-sessiond"]],
190
+ ];
191
+ let failed = false;
192
+ for (const [label, command] of checks) {
193
+ const [bin, ...args] = command;
194
+ if (bin === undefined)
195
+ continue;
196
+ const result = capture(bin, args);
197
+ const ok = result.status === 0;
198
+ failed ||= !ok;
199
+ console.log(`${ok ? "✓" : "✗"} ${label}`);
200
+ const output = (result.stdout || result.stderr).trim();
201
+ if (output !== "")
202
+ console.log(` ${output.split("\n")[0] ?? ""}`);
203
+ }
204
+ const linger = isLingerEnabled();
205
+ if (linger === true) {
206
+ console.log("✓ systemd user lingering enabled");
207
+ }
208
+ else if (linger === false) {
209
+ console.log("✗ systemd user lingering disabled");
210
+ console.log(` Recommended on servers: sudo loginctl enable-linger ${userInfo().username}`);
211
+ }
212
+ else {
213
+ console.log("? systemd user lingering unknown");
214
+ console.log(` Recommended on servers: sudo loginctl enable-linger ${userInfo().username}`);
215
+ }
216
+ if (failed) {
217
+ console.log("\nIf a command works in your terminal but fails here, make sure your bash login files set PATH the same way.");
218
+ process.exitCode = 1;
219
+ }
220
+ }
221
+ function help() {
222
+ console.log(`Pi Web
223
+
224
+ Usage:
225
+ pi-web install [--host 127.0.0.1] [--port 8504] [--config ~/.config/pi-web/config.json]
226
+ pi-web uninstall
227
+ pi-web start|stop|restart|status|logs
228
+ pi-web doctor
229
+
230
+ Recommended install:
231
+ npm install -g @jmfederico/pi-web
232
+ pi-web install
233
+ `);
234
+ }
235
+ async function main() {
236
+ const [command = "help", ...args] = process.argv.slice(2);
237
+ if (command === "install")
238
+ await install(args);
239
+ else if (command === "uninstall")
240
+ await uninstall();
241
+ else if (command === "start" || command === "stop" || command === "restart" || command === "status")
242
+ serviceAction(command);
243
+ else if (command === "logs")
244
+ logs();
245
+ else if (command === "doctor")
246
+ doctor();
247
+ else if (command === "help" || command === "--help" || command === "-h")
248
+ help();
249
+ else
250
+ throw new Error(`Unknown command: ${command}`);
251
+ }
252
+ main().catch((error) => {
253
+ console.error(error instanceof Error ? error.message : String(error));
254
+ process.exit(1);
255
+ });
256
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import{n as a,J as d,Q as h,A as f,R as m,S as g,U as v,V as y}from"./vendor-editor-core-hulUn3GY.js";import{g as w,r as b,p as C,h as j,c as x,m as k,j as E,a as u}from"./vendor-editor-languages-Cjllm-a8.js";import{d as H}from"./vendor-editor-legacy-B4QLsWF8.js";import{i as S,b as _,a as O,n as p,e as P,t as L}from"./index-Cbr8EG8h.js";var R=Object.defineProperty,V=Object.getOwnPropertyDescriptor,n=(e,o,i,s)=>{for(var t=s>1?void 0:s?V(o,i):o,c=e.length-1,l;c>=0;c--)(l=e[c])&&(t=(s?l(o,i,t):l(t))||t);return s&&t&&R(o,i,t),t};let r=class extends S{constructor(){super(...arguments),this.content=""}firstUpdated(){this.recreateEditor()}updated(e){(e.has("content")||e.has("language"))&&this.recreateEditor()}disconnectedCallback(){this.view?.destroy(),this.view=void 0,super.disconnectedCallback()}render(){return _`<div class="host"></div>`}recreateEditor(){this.editorHost&&(this.view?.destroy(),this.view=new a({parent:this.editorHost,state:d.create({doc:this.content,extensions:[h(),f.of(m),g(y,{fallback:!0}),d.readOnly.of(!0),a.editable.of(!1),a.lineWrapping,D,...F(this.language)]})}))}};r.styles=O`
2
+ :host { display: block; min-height: 0; height: 100%; }
3
+ .host { height: 100%; min-height: 0; overflow: auto; }
4
+ `;n([p()],r.prototype,"content",2);n([p()],r.prototype,"language",2);n([P(".host")],r.prototype,"editorHost",2);r=n([L("code-viewer")],r);const D=a.theme({"&":{height:"100%",color:"#e6edf3",backgroundColor:"#0d1117",fontSize:"12px"},".cm-scroller":{fontFamily:"ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",lineHeight:"1.45"},".cm-gutters":{backgroundColor:"#0d1117",color:"#6e7681",borderRight:"1px solid #21262d"},".cm-activeLineGutter":{backgroundColor:"transparent"},".cm-activeLine":{backgroundColor:"transparent"},".cm-content":{caretColor:"transparent"},"&.cm-focused":{outline:"none"}});function F(e){if(e===void 0)return[];switch(e){case"typescript":return[u({typescript:!0})];case"javascript":return[u()];case"json":return[E()];case"markdown":return[k()];case"css":return[x()];case"html":return[j()];case"python":return[C()];case"rust":return[b()];case"go":return[w()];case"diff":return[v.define(H)];default:return[]}}export{r as CodeViewer};