pi-web-ui 0.35.0 → 0.36.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -21
- package/README.md +388 -388
- package/README.zh-CN.md +359 -359
- package/bin/pi-web-ui.mjs +1736 -1727
- package/deploy/com.xingshuyin.pi-web-ui.plist +48 -48
- package/deploy/nginx-subpath.conf +88 -88
- package/deploy/pi-web-ui-task.xml +71 -71
- package/deploy/pi-web-ui.service +31 -31
- package/dist/server/agent-service.js +34 -10
- package/dist/server/attachments.js +3 -3
- package/dist/server/index.js +4 -1
- package/dist/server/plugins.js +46 -4
- package/dist/server/protocol-version.js +1 -1
- package/dist/server/terminals.js +5 -5
- package/extensions/webui.ts +192 -192
- package/package.json +1 -1
- package/themes/md-preview.css +93 -0
- package/themes/white.css +93 -0
- package/web/dist/assets/{TerminalPanel-BIzzCNZ2.js → TerminalPanel-BxezvWth.js} +1 -1
- package/web/dist/assets/index-BP593LGC.js +19 -0
- package/web/dist/assets/index-DduTNQNx.css +10 -0
- package/web/dist/favicon.svg +8 -8
- package/web/dist/index.html +15 -15
- package/web/public/favicon.svg +8 -8
- package/web/dist/assets/index-CKIZpMV3.js +0 -19
- package/web/dist/assets/index-Diz_v1In.css +0 -10
|
@@ -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,88 +1,88 @@
|
|
|
1
|
-
# pi-web-ui behind nginx at a sub-path: http://<host>:83/pi/
|
|
2
|
-
# Backend app: http://127.0.0.1:8787 (default PORT env)
|
|
3
|
-
#
|
|
4
|
-
# IMPORTANT: pi-web-ui 0.23+ checks the WebSocket Origin against the request
|
|
5
|
-
# Host (hostname AND port). Every proxied location MUST forward the original
|
|
6
|
-
# Host with $http_host (keeps the port). Using $host (drops the port) or
|
|
7
|
-
# leaving Host unset (defaults to 127.0.0.1:8787) makes the upgrade fail with
|
|
8
|
-
# 403 — the page loads but chat/terminal keep reconnecting.
|
|
9
|
-
#
|
|
10
|
-
# Topology (two listeners on one port: frp + LAN coexist):
|
|
11
|
-
# frp (public) -> <PUBLIC_IP>:<PUBLIC_PORT> -> 127.0.0.1:83 (PROXY protocol v2)
|
|
12
|
-
# LAN users -> http://<LAN_IP>:83/pi/ (plain HTTP listener)
|
|
13
|
-
#
|
|
14
|
-
# frpc sends PROXY v2 to nginx (transport.proxyProtocolVersion = "v2"):
|
|
15
|
-
# * 127.0.0.1:83 proxy_protocol — only the local frp client connects
|
|
16
|
-
# here (it speaks PROXY v2; nginx then sees the real visitor IP).
|
|
17
|
-
# * <LAN_IP>:83 plain HTTP — LAN browsers, no PROXY header needed.
|
|
18
|
-
#
|
|
19
|
-
# Simpler alternative (no real client IPs): drop proxy_protocol entirely and
|
|
20
|
-
# use a single `listen 83;` — then frpc must NOT set proxyProtocolVersion.
|
|
21
|
-
#
|
|
22
|
-
# The frontend uses absolute paths (/ws WebSocket, /assets/*, /favicon.svg,
|
|
23
|
-
# /api/file…) so those get their own proxied locations next to /pi/.
|
|
24
|
-
|
|
25
|
-
# Reuse for Upgrade/Connection headers (WebSocket).
|
|
26
|
-
map $http_upgrade $connection_upgrade {
|
|
27
|
-
default upgrade;
|
|
28
|
-
'' close;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
server {
|
|
32
|
-
listen 127.0.0.1:83 proxy_protocol;
|
|
33
|
-
listen <LAN_IP>:83;
|
|
34
|
-
|
|
35
|
-
server_name _;
|
|
36
|
-
|
|
37
|
-
# Trust PROXY-protocol headers only from the local frp client; plain
|
|
38
|
-
# (LAN) connections keep their real $remote_addr untouched.
|
|
39
|
-
set_real_ip_from 127.0.0.1;
|
|
40
|
-
real_ip_header proxy_protocol;
|
|
41
|
-
|
|
42
|
-
# ---- main entry: strip /pi/ and forward to the app root ----
|
|
43
|
-
location /pi/ {
|
|
44
|
-
proxy_pass http://127.0.0.1:8787/;
|
|
45
|
-
proxy_http_version 1.1;
|
|
46
|
-
# $http_host keeps the port — origin check compares hostname AND port.
|
|
47
|
-
proxy_set_header Host $http_host;
|
|
48
|
-
proxy_set_header X-Real-IP $remote_addr;
|
|
49
|
-
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
50
|
-
proxy_set_header X-Forwarded-Proto $scheme;
|
|
51
|
-
proxy_set_header Upgrade $http_upgrade;
|
|
52
|
-
proxy_set_header Connection $connection_upgrade;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
# ---- WebSocket (the frontend connects to ws://<host>/ws) ----
|
|
56
|
-
location /ws {
|
|
57
|
-
proxy_pass http://127.0.0.1:8787;
|
|
58
|
-
proxy_http_version 1.1;
|
|
59
|
-
proxy_set_header Host $http_host;
|
|
60
|
-
proxy_set_header Upgrade $http_upgrade;
|
|
61
|
-
proxy_set_header Connection $connection_upgrade;
|
|
62
|
-
proxy_read_timeout 3600s;
|
|
63
|
-
proxy_send_timeout 3600s;
|
|
64
|
-
proxy_set_header X-Real-IP $remote_addr;
|
|
65
|
-
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
# ---- absolute asset paths baked into index.html ----
|
|
69
|
-
location /assets/ {
|
|
70
|
-
proxy_pass http://127.0.0.1:8787;
|
|
71
|
-
}
|
|
72
|
-
location = /favicon.svg {
|
|
73
|
-
proxy_pass http://127.0.0.1:8787;
|
|
74
|
-
}
|
|
75
|
-
location = /favicon-streaming.svg {
|
|
76
|
-
proxy_pass http://127.0.0.1:8787;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
# ---- media preview / download / health API ----
|
|
80
|
-
location /api/ {
|
|
81
|
-
proxy_pass http://127.0.0.1:8787;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
# bare root → app entry
|
|
85
|
-
location = / {
|
|
86
|
-
return 302 /pi/;
|
|
87
|
-
}
|
|
88
|
-
}
|
|
1
|
+
# pi-web-ui behind nginx at a sub-path: http://<host>:83/pi/
|
|
2
|
+
# Backend app: http://127.0.0.1:8787 (default PORT env)
|
|
3
|
+
#
|
|
4
|
+
# IMPORTANT: pi-web-ui 0.23+ checks the WebSocket Origin against the request
|
|
5
|
+
# Host (hostname AND port). Every proxied location MUST forward the original
|
|
6
|
+
# Host with $http_host (keeps the port). Using $host (drops the port) or
|
|
7
|
+
# leaving Host unset (defaults to 127.0.0.1:8787) makes the upgrade fail with
|
|
8
|
+
# 403 — the page loads but chat/terminal keep reconnecting.
|
|
9
|
+
#
|
|
10
|
+
# Topology (two listeners on one port: frp + LAN coexist):
|
|
11
|
+
# frp (public) -> <PUBLIC_IP>:<PUBLIC_PORT> -> 127.0.0.1:83 (PROXY protocol v2)
|
|
12
|
+
# LAN users -> http://<LAN_IP>:83/pi/ (plain HTTP listener)
|
|
13
|
+
#
|
|
14
|
+
# frpc sends PROXY v2 to nginx (transport.proxyProtocolVersion = "v2"):
|
|
15
|
+
# * 127.0.0.1:83 proxy_protocol — only the local frp client connects
|
|
16
|
+
# here (it speaks PROXY v2; nginx then sees the real visitor IP).
|
|
17
|
+
# * <LAN_IP>:83 plain HTTP — LAN browsers, no PROXY header needed.
|
|
18
|
+
#
|
|
19
|
+
# Simpler alternative (no real client IPs): drop proxy_protocol entirely and
|
|
20
|
+
# use a single `listen 83;` — then frpc must NOT set proxyProtocolVersion.
|
|
21
|
+
#
|
|
22
|
+
# The frontend uses absolute paths (/ws WebSocket, /assets/*, /favicon.svg,
|
|
23
|
+
# /api/file…) so those get their own proxied locations next to /pi/.
|
|
24
|
+
|
|
25
|
+
# Reuse for Upgrade/Connection headers (WebSocket).
|
|
26
|
+
map $http_upgrade $connection_upgrade {
|
|
27
|
+
default upgrade;
|
|
28
|
+
'' close;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
server {
|
|
32
|
+
listen 127.0.0.1:83 proxy_protocol;
|
|
33
|
+
listen <LAN_IP>:83;
|
|
34
|
+
|
|
35
|
+
server_name _;
|
|
36
|
+
|
|
37
|
+
# Trust PROXY-protocol headers only from the local frp client; plain
|
|
38
|
+
# (LAN) connections keep their real $remote_addr untouched.
|
|
39
|
+
set_real_ip_from 127.0.0.1;
|
|
40
|
+
real_ip_header proxy_protocol;
|
|
41
|
+
|
|
42
|
+
# ---- main entry: strip /pi/ and forward to the app root ----
|
|
43
|
+
location /pi/ {
|
|
44
|
+
proxy_pass http://127.0.0.1:8787/;
|
|
45
|
+
proxy_http_version 1.1;
|
|
46
|
+
# $http_host keeps the port — origin check compares hostname AND port.
|
|
47
|
+
proxy_set_header Host $http_host;
|
|
48
|
+
proxy_set_header X-Real-IP $remote_addr;
|
|
49
|
+
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
50
|
+
proxy_set_header X-Forwarded-Proto $scheme;
|
|
51
|
+
proxy_set_header Upgrade $http_upgrade;
|
|
52
|
+
proxy_set_header Connection $connection_upgrade;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
# ---- WebSocket (the frontend connects to ws://<host>/ws) ----
|
|
56
|
+
location /ws {
|
|
57
|
+
proxy_pass http://127.0.0.1:8787;
|
|
58
|
+
proxy_http_version 1.1;
|
|
59
|
+
proxy_set_header Host $http_host;
|
|
60
|
+
proxy_set_header Upgrade $http_upgrade;
|
|
61
|
+
proxy_set_header Connection $connection_upgrade;
|
|
62
|
+
proxy_read_timeout 3600s;
|
|
63
|
+
proxy_send_timeout 3600s;
|
|
64
|
+
proxy_set_header X-Real-IP $remote_addr;
|
|
65
|
+
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
# ---- absolute asset paths baked into index.html ----
|
|
69
|
+
location /assets/ {
|
|
70
|
+
proxy_pass http://127.0.0.1:8787;
|
|
71
|
+
}
|
|
72
|
+
location = /favicon.svg {
|
|
73
|
+
proxy_pass http://127.0.0.1:8787;
|
|
74
|
+
}
|
|
75
|
+
location = /favicon-streaming.svg {
|
|
76
|
+
proxy_pass http://127.0.0.1:8787;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
# ---- media preview / download / health API ----
|
|
80
|
+
location /api/ {
|
|
81
|
+
proxy_pass http://127.0.0.1:8787;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
# bare root → app entry
|
|
85
|
+
location = / {
|
|
86
|
+
return 302 /pi/;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
@@ -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>
|
package/deploy/pi-web-ui.service
CHANGED
|
@@ -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
|
|
@@ -80,14 +80,14 @@ export class QuiesceRejectedError extends Error {
|
|
|
80
80
|
* programs wait for input that never comes. Legacy Chinese files are often
|
|
81
81
|
* GBK/GB2312 — read them with the right encoding, never paste mojibake into
|
|
82
82
|
* reasoning/answers. */
|
|
83
|
-
const WINDOWS_PERSONA = `You are a coding agent running on Windows. The bash tool runs Git Bash (bash.exe), not PowerShell. Follow these rules to avoid hanging the session:
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
- ALWAYS pass a timeout parameter to the bash tool (in seconds). There is NO default timeout — a command that never finishes (servers, watchers, infinite loops, slow downloads/installs) will hang the entire conversation indefinitely. Pick a generous timeout for long-running work, but never omit it.
|
|
88
|
-
- NEVER run interactive or foreground long-running commands through the bash tool (vi, less, top, python -, node -, npm run dev, sleep 10000). For servers/daemons use background execution with output redirected to a log file, then poll the log; stop them when done.
|
|
89
|
-
- In the interactive terminal (TTY) — which is Git Bash too, not PowerShell — NEVER use heredocs (<<'EOF' ... EOF) or here-strings, and NEVER start interactive programs (vi, less, python -, node -, npm init): they wait for keyboard input that never arrives and hang the terminal forever. Prefer writing a temp script file (e.g. .pi-tmp.sh) and running it non-interactively. ALWAYS pass a timeout to long-running commands (e.g. \`timeout 120 npm run dev\`).
|
|
90
|
-
|
|
83
|
+
const WINDOWS_PERSONA = `You are a coding agent running on Windows. The bash tool runs Git Bash (bash.exe), not PowerShell. Follow these rules to avoid hanging the session:
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
- ALWAYS pass a timeout parameter to the bash tool (in seconds). There is NO default timeout — a command that never finishes (servers, watchers, infinite loops, slow downloads/installs) will hang the entire conversation indefinitely. Pick a generous timeout for long-running work, but never omit it.
|
|
88
|
+
- NEVER run interactive or foreground long-running commands through the bash tool (vi, less, top, python -, node -, npm run dev, sleep 10000). For servers/daemons use background execution with output redirected to a log file, then poll the log; stop them when done.
|
|
89
|
+
- In the interactive terminal (TTY) — which is Git Bash too, not PowerShell — NEVER use heredocs (<<'EOF' ... EOF) or here-strings, and NEVER start interactive programs (vi, less, python -, node -, npm init): they wait for keyboard input that never arrives and hang the terminal forever. Prefer writing a temp script file (e.g. .pi-tmp.sh) and running it non-interactively. ALWAYS pass a timeout to long-running commands (e.g. \`timeout 120 npm run dev\`).
|
|
90
|
+
|
|
91
91
|
Many legacy Chinese text files (.html/.txt/.md/.log, exported documents) are GBK/GB2312 encoded: the read tool decodes UTF-8 only and will show mojibake (乱码) for them. If a file's content looks garbled, read it through the terminal instead: in Git Bash use \`cat file | iconv -f GBK -t UTF-8\` (or \`iconv -f GBK -t UTF-8 file\`); in cmd use \`chcp 65001 && type file\`; in PowerShell use \`Get-Content -Encoding Default file\`. Never paste mojibake into your reasoning or answer — describe the decoded content instead.`;
|
|
92
92
|
/**
|
|
93
93
|
* Killable bash tool: wraps the SDK bash tool with operations that register
|
|
@@ -1399,6 +1399,10 @@ export class ClientSession {
|
|
|
1399
1399
|
}
|
|
1400
1400
|
/** Set by index.ts: called when /pi-web-ui:quit is invoked. */
|
|
1401
1401
|
onQuit = undefined;
|
|
1402
|
+
/** 本客户端成功切换工作区(set_cwd)后触发,参数为新绝对路径。
|
|
1403
|
+
* attach 时由 AgentService 接到全局 onClientCwdChanged —— 编辑器等
|
|
1404
|
+
* 工作区跟随型插件借此把根目录切到用户当前项目。 */
|
|
1405
|
+
onCwdChanged = undefined;
|
|
1402
1406
|
/** Ask the npm registry for the latest pi-web-ui version and report it. */
|
|
1403
1407
|
async checkUpdate() {
|
|
1404
1408
|
const current = ClientSession.currentAppVersion();
|
|
@@ -2212,8 +2216,14 @@ export class ClientSession {
|
|
|
2212
2216
|
* keeps everything up to (but not including) that question, then sends the
|
|
2213
2217
|
* edited text there. The original thread is untouched and stays in the
|
|
2214
2218
|
* session list, so nothing is ever lost.
|
|
2219
|
+
*
|
|
2220
|
+
* Attachments (attachments) travel through the SAME pipeline as prompt()
|
|
2221
|
+
* — the fork intentionally drops the original attachment asides because
|
|
2222
|
+
* they live on the old branch past the fork point, so the browser re-sends
|
|
2223
|
+
* the images it kept in the edit composer (original image blocks + any
|
|
2224
|
+
* newly pasted/dropped ones). Text-only edits pass undefined.
|
|
2215
2225
|
*/
|
|
2216
|
-
async editMessage(messageId, text) {
|
|
2226
|
+
async editMessage(messageId, text, attachments) {
|
|
2217
2227
|
if (this.quiesceBlocked())
|
|
2218
2228
|
return;
|
|
2219
2229
|
const trimmed = text.trim();
|
|
@@ -2248,7 +2258,7 @@ export class ClientSession {
|
|
|
2248
2258
|
return;
|
|
2249
2259
|
}
|
|
2250
2260
|
await this.bindSession();
|
|
2251
|
-
await this.prompt(trimmed);
|
|
2261
|
+
await this.prompt(trimmed, attachments);
|
|
2252
2262
|
this.emit({
|
|
2253
2263
|
type: "notice",
|
|
2254
2264
|
level: "info",
|
|
@@ -2402,6 +2412,13 @@ export class ClientSession {
|
|
|
2402
2412
|
this.conv.promptedSinceActive = false;
|
|
2403
2413
|
this.conv.lastActiveAt = Date.now();
|
|
2404
2414
|
this.cwd = abs;
|
|
2415
|
+
// 工作区跟随型插件(编辑器文件树等)同步切根。
|
|
2416
|
+
try {
|
|
2417
|
+
this.onCwdChanged?.(abs);
|
|
2418
|
+
}
|
|
2419
|
+
catch {
|
|
2420
|
+
/* 钩子异常不影响主流程 */
|
|
2421
|
+
}
|
|
2405
2422
|
// Remember the new workspace (restore target + recent-project entry).
|
|
2406
2423
|
this.stateStore.remember(this.clientId, abs);
|
|
2407
2424
|
void this.pushProjects();
|
|
@@ -2605,6 +2622,9 @@ export class AgentService {
|
|
|
2605
2622
|
stateStore;
|
|
2606
2623
|
/** Set by index.ts: called when /pi-web-ui:quit is invoked. */
|
|
2607
2624
|
onQuit = undefined;
|
|
2625
|
+
/** 任意客户端成功切换工作区后触发(新绝对路径)。index.ts 接到
|
|
2626
|
+
* PluginManager.notifyCwd,让插件宿主的 host.cwd 实时跟随当前项目。 */
|
|
2627
|
+
onClientCwdChanged = undefined;
|
|
2608
2628
|
constructor(cwd, stateFile) {
|
|
2609
2629
|
this.cwd = cwd;
|
|
2610
2630
|
this.stateStore = new ClientStateStore(stateFile);
|
|
@@ -2720,6 +2740,10 @@ export class AgentService {
|
|
|
2720
2740
|
cs.onToolEvent = this.onToolEvent;
|
|
2721
2741
|
cs.pluginToolsProvider = this.pluginToolsProvider;
|
|
2722
2742
|
cs.isQuiesced = () => this.quiesced;
|
|
2743
|
+
// 插件宿主工作区跟随:初次接入也同步一次(恢复的 lastCwd 可能≠服务启动目录),
|
|
2744
|
+
// notifyCwd 幂等去重;此后 set_cwd 成功时由 cs.onCwdChanged 继续驱动。
|
|
2745
|
+
cs.onCwdChanged = (abs) => this.onClientCwdChanged?.(abs);
|
|
2746
|
+
this.onClientCwdChanged?.(cs.cwd);
|
|
2723
2747
|
return cs;
|
|
2724
2748
|
}
|
|
2725
2749
|
/** 插件 AI 工具集合变化(注册/注销)时由 index.ts 触发:推送到所有客户端的全部会话。 */
|
|
@@ -397,9 +397,9 @@ export async function buildAttachmentMessages(ctx, attachments) {
|
|
|
397
397
|
content: [
|
|
398
398
|
{
|
|
399
399
|
type: "text",
|
|
400
|
-
text: `
|
|
401
|
-
<vision-bridge>
|
|
402
|
-
${transcript}
|
|
400
|
+
text: `
|
|
401
|
+
<vision-bridge>
|
|
402
|
+
${transcript}
|
|
403
403
|
</vision-bridge>`,
|
|
404
404
|
},
|
|
405
405
|
...(pathImg
|
package/dist/server/index.js
CHANGED
|
@@ -374,6 +374,9 @@ service.onToolEvent = (ev) => pluginMgr.emitToolEvent(ev);
|
|
|
374
374
|
// 变化时动态注入/移除已有会话。
|
|
375
375
|
service.pluginToolsProvider = () => pluginMgr.getAgentTools();
|
|
376
376
|
pluginMgr.onAgentToolsChanged = () => service.applyPluginAgentTools();
|
|
377
|
+
// 插件宿主工作区实时跟随当前项目:任意客户端 set_cwd 成功后同步给
|
|
378
|
+
// PluginManager,编辑器等工作区跟随型插件随即切根(详见 plugins.ts notifyCwd)。
|
|
379
|
+
service.onClientCwdChanged = (cwd) => pluginMgr.notifyCwd(cwd);
|
|
377
380
|
// ---------------------------------------------------------------------------
|
|
378
381
|
// Self-update
|
|
379
382
|
// ---------------------------------------------------------------------------
|
|
@@ -521,7 +524,7 @@ wss.on("connection", (ws) => {
|
|
|
521
524
|
void cs.newChat();
|
|
522
525
|
break;
|
|
523
526
|
case "edit_message":
|
|
524
|
-
void cs.editMessage(msg.messageId, msg.text);
|
|
527
|
+
void cs.editMessage(msg.messageId, msg.text, msg.attachments);
|
|
525
528
|
break;
|
|
526
529
|
case "cycle_model":
|
|
527
530
|
void cs.cycleModel();
|