pi-web-ui 0.8.4 → 0.8.6

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
@@ -2,8 +2,9 @@
2
2
  /**
3
3
  * pi-web-ui CLI.
4
4
  *
5
- * pi-web-ui 启动生产服务器(前台,Ctrl+C 停止)
5
+ * pi-web-ui 启动生产服务器(前台,Ctrl+C 停止,自动打开浏览器)
6
6
  * pi-web-ui --port 9000 --cwd /path 同上,覆盖端口 / 工作目录 / 数据目录
7
+ * pi-web-ui --no-browser 启动但不自动打开浏览器
7
8
  * pi-web-ui --version | --help
8
9
  * pi-web-ui server install [选项] 安装系统服务(开机自启)并启动
9
10
  * pi-web-ui server uninstall [选项] 卸载系统服务
@@ -21,6 +22,7 @@
21
22
  * PI_CODING_AGENT_DIR。
22
23
  */
23
24
  import { spawnSync } from "node:child_process";
25
+ import { get as httpGet } from "node:http";
24
26
  import {
25
27
  existsSync,
26
28
  mkdirSync,
@@ -47,8 +49,9 @@ try {
47
49
  const HELP = `pi-web-ui v${pkg.version} — web chat for the pi coding agent
48
50
 
49
51
  用法:
50
- pi-web-ui 启动服务器(前台,Ctrl+C 停止)
52
+ pi-web-ui 启动服务器(前台,Ctrl+C 停止,自动打开浏览器)
51
53
  pi-web-ui --port 9000 --cwd /path 启动并指定端口 / 工作目录 / 数据目录
54
+ pi-web-ui --no-browser 启动但不自动打开浏览器
52
55
  pi-web-ui server install [选项] 安装系统服务(开机自启)并启动
53
56
  pi-web-ui server uninstall [选项] 卸载系统服务
54
57
  pi-web-ui server start|stop|restart|status [选项]
@@ -109,6 +112,7 @@ function parseFlags(argv) {
109
112
  dataDir: undefined,
110
113
  name: undefined,
111
114
  print: false,
115
+ noBrowser: false,
112
116
  help: false,
113
117
  };
114
118
  const positionals = [];
@@ -141,6 +145,9 @@ function parseFlags(argv) {
141
145
  case "--print":
142
146
  opts.print = true;
143
147
  break;
148
+ case "--no-browser":
149
+ opts.noBrowser = true;
150
+ break;
144
151
  case "--help":
145
152
  case "-h":
146
153
  opts.help = true;
@@ -157,11 +164,52 @@ function parseFlags(argv) {
157
164
  // 前台启动
158
165
  // ---------------------------------------------------------------------------
159
166
 
167
+ /** Open a URL in the OS default browser; failures are ignored (best-effort). */
168
+ function openBrowser(url) {
169
+ if (isMac) {
170
+ spawnSync("open", [url], { stdio: "ignore" });
171
+ } else if (isWin) {
172
+ spawnSync("cmd", ["/c", "start", "", url], { stdio: "ignore" });
173
+ } else {
174
+ spawnSync("xdg-open", [url], { stdio: "ignore" });
175
+ }
176
+ }
177
+
178
+ /**
179
+ * Poll `url` until the server answers (or ~15s pass), then open it in the
180
+ * default browser. The foreground server runs in this process, so the first
181
+ * HTTP response is the "listening" signal; if the server crashes meanwhile
182
+ * (e.g. port already taken), the pending timers die with the process and
183
+ * nothing is opened.
184
+ */
185
+ function openBrowserWhenUp(url) {
186
+ const deadline = Date.now() + 15_000;
187
+ const attempt = () => {
188
+ const req = httpGet(url, (res) => {
189
+ res.resume();
190
+ console.log(` 🌐 已自动打开浏览器:${url}(--no-browser 可关闭)`);
191
+ openBrowser(url);
192
+ });
193
+ req.setTimeout(1000, () => {
194
+ req.destroy();
195
+ if (Date.now() < deadline) setTimeout(attempt, 150);
196
+ });
197
+ req.on("error", () => {
198
+ if (Date.now() < deadline) setTimeout(attempt, 150);
199
+ });
200
+ };
201
+ attempt();
202
+ }
203
+
160
204
  async function startForeground(opts) {
161
205
  if (opts.port) process.env.PORT = opts.port;
162
206
  if (opts.cwd) process.env.PI_WEB_CWD = resolve(opts.cwd);
163
207
  if (opts.dataDir) process.env.PI_WEB_DATA_DIR = resolve(opts.dataDir);
208
+ const url = `http://localhost:${String(
209
+ opts.port ?? process.env.PORT ?? "8787",
210
+ )}`;
164
211
  await import(pathToFileURL(SERVER_ENTRY).href);
212
+ if (!opts.noBrowser) openBrowserWhenUp(url);
165
213
  }
166
214
 
167
215
  // ---------------------------------------------------------------------------
@@ -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
@@ -955,18 +955,23 @@ export class ClientSession {
955
955
  async checkUpdate() {
956
956
  const current = ClientSession.currentAppVersion();
957
957
  try {
958
- const res = await fetch("https://registry.npmjs.org/pi-web-ui/latest", {
958
+ // Fetch the full package doc (not /latest): it carries the per-version
959
+ // publish timestamps so the UI can hint when a version was JUST
960
+ // published and the registry/CDN caches may not have caught up yet.
961
+ const res = await fetch("https://registry.npmjs.org/pi-web-ui", {
959
962
  signal: AbortSignal.timeout(8_000),
960
963
  });
961
964
  if (!res.ok)
962
965
  throw new Error(`HTTP ${res.status}`);
963
966
  const data = (await res.json());
964
- const latest = data.version ?? null;
967
+ const latest = data["dist-tags"]?.latest ?? null;
968
+ const latestPublishedAt = latest && data.time ? (data.time[latest] ?? null) : null;
965
969
  const upToDate = latest === null || ClientSession.compareVersions(current, latest) >= 0;
966
970
  this.emit({
967
971
  type: "update_status",
968
972
  current,
969
973
  latest,
974
+ latestPublishedAt,
970
975
  upToDate,
971
976
  pendingRestart: this.pendingRestart,
972
977
  });
@@ -976,6 +981,7 @@ export class ClientSession {
976
981
  type: "update_status",
977
982
  current,
978
983
  latest: null,
984
+ latestPublishedAt: null,
979
985
  upToDate: false,
980
986
  pendingRestart: this.pendingRestart,
981
987
  error: `检查更新失败:${err.message}`,
@@ -1751,7 +1757,11 @@ export class ClientSession {
1751
1757
  isStreaming,
1752
1758
  });
1753
1759
  }
1754
- this.emit({ type: "conversations", conversations, activeId: this.activeId });
1760
+ this.emit({
1761
+ type: "conversations",
1762
+ conversations,
1763
+ activeId: this.activeId,
1764
+ });
1755
1765
  }
1756
1766
  /** List persisted sessions for this client, newest first. */
1757
1767
  /** Push the persisted session list to the client (client-requested). */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-web-ui",
3
- "version": "0.8.4",
3
+ "version": "0.8.6",
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",