pi-web-ui 0.8.7 → 0.8.8

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/bin/pi-web-ui.mjs CHANGED
File without changes
@@ -1,48 +1,48 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
- <!--
4
- pi-web-ui launchd agent — macOS auto-start at login.
5
-
6
- Install:
7
- npm i -g pi-web-ui
8
- cp deploy/com.xingshuyin.pi-web-ui.plist ~/Library/LaunchAgents/
9
- # edit ProgramArguments / WorkingDirectory / PI_WEB_CWD for your setup
10
- launchctl load ~/Library/LaunchAgents/com.xingshuyin.pi-web-ui.plist
11
- # (or: launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.xingshuyin.pi-web-ui.plist)
12
-
13
- Find the pi-web-ui binary path with: which pi-web-ui
14
- -->
15
- <plist version="1.0">
16
- <dict>
17
- <key>Label</key>
18
- <string>com.xingshuyin.pi-web-ui</string>
19
-
20
- <key>ProgramArguments</key>
21
- <array>
22
- <string>/usr/local/bin/pi-web-ui</string>
23
- </array>
24
-
25
- <key>RunAtLoad</key>
26
- <true/>
27
-
28
- <!-- Restart if it crashes -->
29
- <key>KeepAlive</key>
30
- <true/>
31
-
32
- <key>WorkingDirectory</key>
33
- <string>/Users/YOUR_USER</string>
34
-
35
- <key>EnvironmentVariables</key>
36
- <dict>
37
- <key>PORT</key>
38
- <string>8787</string>
39
- <key>PI_WEB_CWD</key>
40
- <string>/Users/YOUR_USER</string>
41
- </dict>
42
-
43
- <key>StandardOutPath</key>
44
- <string>/tmp/pi-web-ui.log</string>
45
- <key>StandardErrorPath</key>
46
- <string>/tmp/pi-web-ui.err</string>
47
- </dict>
48
- </plist>
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+ <!--
4
+ pi-web-ui launchd agent — macOS auto-start at login.
5
+
6
+ Install:
7
+ npm i -g pi-web-ui
8
+ cp deploy/com.xingshuyin.pi-web-ui.plist ~/Library/LaunchAgents/
9
+ # edit ProgramArguments / WorkingDirectory / PI_WEB_CWD for your setup
10
+ launchctl load ~/Library/LaunchAgents/com.xingshuyin.pi-web-ui.plist
11
+ # (or: launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.xingshuyin.pi-web-ui.plist)
12
+
13
+ Find the pi-web-ui binary path with: which pi-web-ui
14
+ -->
15
+ <plist version="1.0">
16
+ <dict>
17
+ <key>Label</key>
18
+ <string>com.xingshuyin.pi-web-ui</string>
19
+
20
+ <key>ProgramArguments</key>
21
+ <array>
22
+ <string>/usr/local/bin/pi-web-ui</string>
23
+ </array>
24
+
25
+ <key>RunAtLoad</key>
26
+ <true/>
27
+
28
+ <!-- Restart if it crashes -->
29
+ <key>KeepAlive</key>
30
+ <true/>
31
+
32
+ <key>WorkingDirectory</key>
33
+ <string>/Users/YOUR_USER</string>
34
+
35
+ <key>EnvironmentVariables</key>
36
+ <dict>
37
+ <key>PORT</key>
38
+ <string>8787</string>
39
+ <key>PI_WEB_CWD</key>
40
+ <string>/Users/YOUR_USER</string>
41
+ </dict>
42
+
43
+ <key>StandardOutPath</key>
44
+ <string>/tmp/pi-web-ui.log</string>
45
+ <key>StandardErrorPath</key>
46
+ <string>/tmp/pi-web-ui.err</string>
47
+ </dict>
48
+ </plist>
@@ -1,71 +1,71 @@
1
- <?xml version="1.0" encoding="UTF-16"?>
2
- <!--
3
- pi-web-ui Task Scheduler task — Windows auto-start at logon.
4
-
5
- The easy way (no admin needed, generates everything for you):
6
- npm i -g pi-web-ui
7
- pi-web-ui server install --port 8787 --cwd C:\path\to\project
8
- pi-web-ui server status | restart | stop | uninstall
9
-
10
- Manual install with this template (edit the paths below first):
11
- schtasks /Create /TN "pi-web-ui" /XML pi-web-ui-task.xml /F
12
- schtasks /Run /TN "pi-web-ui"
13
-
14
- Notes:
15
- - The task runs the PowerShell launcher the CLI generates at
16
- %APPDATA%\pi-web-ui\pi-web-ui.ps1 with -WindowStyle Hidden, so the
17
- server runs with no black console window (nothing to accidentally
18
- close/kill). The ps1 sets PORT/PI_WEB_CWD, cd's to the workspace,
19
- launches node, and appends output to %USERPROFILE%\pi-web-ui.log.
20
- Preview both generated files with: pi-web-ui server install --print
21
- - Save this file as UTF-16 LE (schtasks requires it; the CLI does this
22
- automatically when it writes the task XML).
23
- - LogonTrigger = starts when you log in, same as a launchd user agent.
24
- For boot-start without login, use Docker instead (see README).
25
- -->
26
- <Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
27
- <RegistrationInfo>
28
- <Description>pi-web-ui — web chat for the pi coding agent (auto-start at logon)</Description>
29
- </RegistrationInfo>
30
- <Triggers>
31
- <LogonTrigger>
32
- <Enabled>true</Enabled>
33
- </LogonTrigger>
34
- </Triggers>
35
- <Principals>
36
- <Principal id="Author">
37
- <LogonType>InteractiveToken</LogonType>
38
- <RunLevel>LeastPrivilege</RunLevel>
39
- </Principal>
40
- </Principals>
41
- <Settings>
42
- <MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>
43
- <DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries>
44
- <StopIfGoingOnBatteries>false</StopIfGoingOnBatteries>
45
- <AllowHardTerminate>true</AllowHardTerminate>
46
- <StartWhenAvailable>false</StartWhenAvailable>
47
- <RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable>
48
- <IdleSettings>
49
- <StopOnIdleEnd>false</StopOnIdleEnd>
50
- <RestartOnIdle>false</RestartOnIdle>
51
- </IdleSettings>
52
- <AllowStartOnDemand>true</AllowStartOnDemand>
53
- <Enabled>true</Enabled>
54
- <Hidden>false</Hidden>
55
- <RunOnlyIfIdle>false</RunOnlyIfIdle>
56
- <WakeToRun>false</WakeToRun>
57
- <ExecutionTimeLimit>PT0S</ExecutionTimeLimit>
58
- <Priority>7</Priority>
59
- <RestartOnFailure>
60
- <Interval>PT1M</Interval>
61
- <Count>3</Count>
62
- </RestartOnFailure>
63
- </Settings>
64
- <Actions Context="Author">
65
- <Exec>
66
- <Command>C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe</Command>
67
- <Arguments>-NoProfile -NonInteractive -ExecutionPolicy Bypass -WindowStyle Hidden -File "%APPDATA%\pi-web-ui\pi-web-ui.ps1"</Arguments>
68
- <WorkingDirectory>%USERPROFILE%</WorkingDirectory>
69
- </Exec>
70
- </Actions>
71
- </Task>
1
+ <?xml version="1.0" encoding="UTF-16"?>
2
+ <!--
3
+ pi-web-ui Task Scheduler task — Windows auto-start at logon.
4
+
5
+ The easy way (no admin needed, generates everything for you):
6
+ npm i -g pi-web-ui
7
+ pi-web-ui server install --port 8787 --cwd C:\path\to\project
8
+ pi-web-ui server status | restart | stop | uninstall
9
+
10
+ Manual install with this template (edit the paths below first):
11
+ schtasks /Create /TN "pi-web-ui" /XML pi-web-ui-task.xml /F
12
+ schtasks /Run /TN "pi-web-ui"
13
+
14
+ Notes:
15
+ - The task runs the PowerShell launcher the CLI generates at
16
+ %APPDATA%\pi-web-ui\pi-web-ui.ps1 with -WindowStyle Hidden, so the
17
+ server runs with no black console window (nothing to accidentally
18
+ close/kill). The ps1 sets PORT/PI_WEB_CWD, cd's to the workspace,
19
+ launches node, and appends output to %USERPROFILE%\pi-web-ui.log.
20
+ Preview both generated files with: pi-web-ui server install --print
21
+ - Save this file as UTF-16 LE (schtasks requires it; the CLI does this
22
+ automatically when it writes the task XML).
23
+ - LogonTrigger = starts when you log in, same as a launchd user agent.
24
+ For boot-start without login, use Docker instead (see README).
25
+ -->
26
+ <Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
27
+ <RegistrationInfo>
28
+ <Description>pi-web-ui — web chat for the pi coding agent (auto-start at logon)</Description>
29
+ </RegistrationInfo>
30
+ <Triggers>
31
+ <LogonTrigger>
32
+ <Enabled>true</Enabled>
33
+ </LogonTrigger>
34
+ </Triggers>
35
+ <Principals>
36
+ <Principal id="Author">
37
+ <LogonType>InteractiveToken</LogonType>
38
+ <RunLevel>LeastPrivilege</RunLevel>
39
+ </Principal>
40
+ </Principals>
41
+ <Settings>
42
+ <MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>
43
+ <DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries>
44
+ <StopIfGoingOnBatteries>false</StopIfGoingOnBatteries>
45
+ <AllowHardTerminate>true</AllowHardTerminate>
46
+ <StartWhenAvailable>false</StartWhenAvailable>
47
+ <RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable>
48
+ <IdleSettings>
49
+ <StopOnIdleEnd>false</StopOnIdleEnd>
50
+ <RestartOnIdle>false</RestartOnIdle>
51
+ </IdleSettings>
52
+ <AllowStartOnDemand>true</AllowStartOnDemand>
53
+ <Enabled>true</Enabled>
54
+ <Hidden>false</Hidden>
55
+ <RunOnlyIfIdle>false</RunOnlyIfIdle>
56
+ <WakeToRun>false</WakeToRun>
57
+ <ExecutionTimeLimit>PT0S</ExecutionTimeLimit>
58
+ <Priority>7</Priority>
59
+ <RestartOnFailure>
60
+ <Interval>PT1M</Interval>
61
+ <Count>3</Count>
62
+ </RestartOnFailure>
63
+ </Settings>
64
+ <Actions Context="Author">
65
+ <Exec>
66
+ <Command>C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe</Command>
67
+ <Arguments>-NoProfile -NonInteractive -ExecutionPolicy Bypass -WindowStyle Hidden -File "%APPDATA%\pi-web-ui\pi-web-ui.ps1"</Arguments>
68
+ <WorkingDirectory>%USERPROFILE%</WorkingDirectory>
69
+ </Exec>
70
+ </Actions>
71
+ </Task>
@@ -1,31 +1,31 @@
1
- # pi-web-ui systemd unit — Linux auto-start on boot.
2
- #
3
- # Install (adjust User/WorkingDirectory/Environment to taste):
4
- # sudo npm i -g pi-web-ui
5
- # sudo cp deploy/pi-web-ui.service /etc/systemd/system/
6
- # sudo systemctl daemon-reload
7
- # sudo systemctl enable --now pi-web-ui
8
- # sudo systemctl status pi-web-ui
9
- #
10
- # Logs: journalctl -u pi-web-ui -f
11
-
12
- [Unit]
13
- Description=pi-web-ui — web chat for the pi coding agent
14
- After=network.target
15
-
16
- [Service]
17
- Type=simple
18
- # Run as an unprivileged user (change to your user, e.g. yourname)
19
- User=YOUR_USER
20
- # The workspace the agent operates in (read/edit/bash/write)
21
- WorkingDirectory=/home/YOUR_USER
22
- Environment=PORT=8787
23
- # Point at your pi config dir if it's not the default ~/.pi/agent
24
- #Environment=PI_CODING_AGENT_DIR=/home/YOUR_USER/.pi/agent
25
- ExecStart=/usr/bin/pi-web-ui
26
- Restart=on-failure
27
- RestartSec=5
28
- # npm global bin may live elsewhere (nvm, etc.) — find with: which pi-web-ui
29
-
30
- [Install]
31
- WantedBy=multi-user.target
1
+ # pi-web-ui systemd unit — Linux auto-start on boot.
2
+ #
3
+ # Install (adjust User/WorkingDirectory/Environment to taste):
4
+ # sudo npm i -g pi-web-ui
5
+ # sudo cp deploy/pi-web-ui.service /etc/systemd/system/
6
+ # sudo systemctl daemon-reload
7
+ # sudo systemctl enable --now pi-web-ui
8
+ # sudo systemctl status pi-web-ui
9
+ #
10
+ # Logs: journalctl -u pi-web-ui -f
11
+
12
+ [Unit]
13
+ Description=pi-web-ui — web chat for the pi coding agent
14
+ After=network.target
15
+
16
+ [Service]
17
+ Type=simple
18
+ # Run as an unprivileged user (change to your user, e.g. yourname)
19
+ User=YOUR_USER
20
+ # The workspace the agent operates in (read/edit/bash/write)
21
+ WorkingDirectory=/home/YOUR_USER
22
+ Environment=PORT=8787
23
+ # Point at your pi config dir if it's not the default ~/.pi/agent
24
+ #Environment=PI_CODING_AGENT_DIR=/home/YOUR_USER/.pi/agent
25
+ ExecStart=/usr/bin/pi-web-ui
26
+ Restart=on-failure
27
+ RestartSec=5
28
+ # npm global bin may live elsewhere (nvm, etc.) — find with: which pi-web-ui
29
+
30
+ [Install]
31
+ WantedBy=multi-user.target
@@ -126,6 +126,7 @@ const PREVIEW_TEXT_EXTS = new Set([
126
126
  "json",
127
127
  "jsonc",
128
128
  "json5",
129
+ "jsonl",
129
130
  "md",
130
131
  "mdx",
131
132
  "markdown",
@@ -178,6 +179,43 @@ export function previewKind(name) {
178
179
  return "text";
179
180
  return "none";
180
181
  }
182
+ /**
183
+ * Content sniff for the preview: any data that has no NUL bytes and no
184
+ * meaningful control-char ratio is treated as text — so files with unknown
185
+ * or absent extensions (jsonl, .log.1, …) still open as text. NULs catch
186
+ * zip/sqlite/png/… even when the extension claims text.
187
+ */
188
+ function looksLikeText(buf) {
189
+ if (buf.length === 0)
190
+ return true;
191
+ if (buf.includes(0))
192
+ return false;
193
+ const text = buf.toString("utf8");
194
+ let control = 0;
195
+ for (const ch of text) {
196
+ const c = ch.charCodeAt(0);
197
+ // Keep \t \n \r \f (and \b); everything else < 0x20 is binary-ish.
198
+ if (c < 0x20 && c !== 9 && c !== 10 && c !== 12 && c !== 13)
199
+ control++;
200
+ }
201
+ return control / Math.max(text.length, 1) < 0.02;
202
+ }
203
+ /** First few KB of binary data as a classic hex + ASCII dump (preview only). */
204
+ function hexDump(buf, maxBytes = 4096) {
205
+ const data = buf.subarray(0, Math.min(buf.length, maxBytes));
206
+ const rows = [];
207
+ for (let off = 0; off < data.length; off += 16) {
208
+ const chunk = data.subarray(off, off + 16);
209
+ const hex = [...chunk]
210
+ .map((b) => b.toString(16).padStart(2, "0"))
211
+ .join(" ");
212
+ const ascii = [...chunk]
213
+ .map((b) => (b >= 0x20 && b < 0x7f ? String.fromCharCode(b) : "."))
214
+ .join("");
215
+ rows.push(`${off.toString(16).padStart(8, "0")} ${hex.padEnd(47, " ")} ${ascii}`);
216
+ }
217
+ return rows.join("\n");
218
+ }
181
219
  // ---------------------------------------------------------------------------
182
220
  // Web UI context adapter — bridges extension UI calls (setWidget/notify) to the
183
221
  // browser. Extensions like rpiv-todo render a TUI widget via
@@ -2077,21 +2115,6 @@ export class ClientSession {
2077
2115
  }
2078
2116
  const name = relPath.split("/").pop() ?? relPath;
2079
2117
  const kind = previewKind(name);
2080
- // Not previewable (exe, jar, archives, …) — never read or sent.
2081
- if (kind === "none") {
2082
- this.emit({
2083
- type: "file_content",
2084
- path: rel,
2085
- name,
2086
- text: "",
2087
- truncated: false,
2088
- binary: true,
2089
- kind: "none",
2090
- lines: 0,
2091
- size: stat.size,
2092
- });
2093
- return;
2094
- }
2095
2118
  // Media previews stream over the /api/file HTTP endpoint, so only
2096
2119
  // metadata is sent here — the raw bytes never touch the socket.
2097
2120
  if (kind === "image" || kind === "video") {
@@ -2108,38 +2131,41 @@ export class ClientSession {
2108
2131
  });
2109
2132
  return;
2110
2133
  }
2111
- // Text: read only the first MAX_PREVIEW_BYTES so huge files can't
2112
- // exhaust memory or flood the socket.
2134
+ // Everything else: read a capped prefix and sniff the content.
2135
+ // Anything that looks like text previews as text regardless of its
2136
+ // extension (jsonl, .log.1, weird suffixes, …); binary content gets
2137
+ // a hex dump of the first few KB instead of being refused.
2113
2138
  const handle = await fs.open(abs, "r");
2114
2139
  try {
2115
2140
  const buf = Buffer.alloc(Math.min(stat.size, MAX_PREVIEW_BYTES));
2116
2141
  const { bytesRead } = await handle.read(buf, 0, buf.length, 0);
2117
2142
  const data = buf.subarray(0, bytesRead);
2118
- if (data.includes(0)) {
2143
+ if (looksLikeText(data)) {
2119
2144
  this.emit({
2120
2145
  type: "file_content",
2121
2146
  path: rel,
2122
2147
  name,
2123
- text: "",
2124
- truncated: false,
2125
- binary: true,
2148
+ text: data.toString("utf8"),
2149
+ truncated: bytesRead < stat.size,
2150
+ binary: false,
2126
2151
  kind: "text",
2152
+ lines: countLines(data),
2153
+ size: stat.size,
2154
+ });
2155
+ }
2156
+ else {
2157
+ this.emit({
2158
+ type: "file_content",
2159
+ path: rel,
2160
+ name,
2161
+ text: hexDump(data),
2162
+ truncated: bytesRead < stat.size,
2163
+ binary: true,
2164
+ kind: kind === "text" ? "text" : "none",
2127
2165
  lines: 0,
2128
2166
  size: stat.size,
2129
2167
  });
2130
- return;
2131
2168
  }
2132
- this.emit({
2133
- type: "file_content",
2134
- path: rel,
2135
- name,
2136
- text: data.toString("utf8"),
2137
- truncated: bytesRead < stat.size,
2138
- binary: false,
2139
- kind: "text",
2140
- lines: countLines(data),
2141
- size: stat.size,
2142
- });
2143
2169
  }
2144
2170
  finally {
2145
2171
  await handle.close();
@@ -2263,6 +2289,9 @@ export class ClientSession {
2263
2289
  conv.runtime = newRuntime;
2264
2290
  conv.session = newRuntime.session;
2265
2291
  conv.cwd = abs;
2292
+ // The rebuilt runtime resumed the new project's most recent session —
2293
+ // refresh the title too, or the conversation list shows the old name.
2294
+ conv.title = conversationTitle(newRuntime.session);
2266
2295
  this.cwd = abs;
2267
2296
  // Remember the new workspace (restore target + recent-project entry).
2268
2297
  this.stateStore.remember(this.clientId, abs);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-web-ui",
3
- "version": "0.8.7",
3
+ "version": "0.8.8",
4
4
  "description": "Web chat interface for the pi coding agent, powered by the pi SDK (@earendil-works/pi-coding-agent) — one-command run, Docker/systemd/launchd deployable",
5
5
  "license": "MIT",
6
6
  "type": "module",