pi-web-ui 0.64.0 → 0.64.2
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 +504 -504
- package/README.zh-CN.md +427 -427
- package/bin/pi-web-ui.mjs +1809 -1809
- package/deploy/com.xingshuyin.pi-web-ui.plist +48 -48
- package/deploy/nginx-subpath.conf +88 -88
- package/deploy/pi-web-ui-task.xml +54 -54
- package/deploy/pi-web-ui.service +31 -31
- package/dist/server/agent-service.js +45 -22
- package/dist/server/attachments.js +16 -11
- package/dist/server/dsh/dsh-agent-service.js +27 -7
- package/dist/server/dsh/runtime/cordis.yml +1 -1
- package/dist/server/dsh/runtime/goal-rpc.mjs +645 -645
- package/dist/server/dsh/runtime/launcher.mjs +164 -164
- package/dist/server/dsh/runtime/override.patch.yml +71 -71
- package/dist/server/dsh/runtime/runtime-root.mjs +86 -86
- package/dist/server/files-service.js +235 -43
- package/dist/server/goal-service.js +4 -1
- package/dist/server/index.js +48 -13
- package/dist/server/marker-service.js +8 -3
- package/dist/server/markers/builtins/rename.js +3 -3
- package/dist/server/model-admin.js +12 -6
- package/dist/server/plugins.js +4 -4
- package/dist/server/slash-commands.js +3 -0
- package/dist/server/terminals.js +25 -15
- package/dist/server/vision-bridge.js +9 -9
- package/dist/server/webui-context.js +2 -2
- package/extensions/webui.ts +190 -190
- package/package.json +109 -109
- package/themes/cyberpunk.css +81 -81
- package/themes/dazzle.css +81 -81
- package/themes/md-preview.css +98 -98
- package/themes/white.css +160 -160
- package/web/dist/assets/TerminalPanel-BXISCcrQ.js +6 -0
- package/web/dist/assets/index-BxuvIAJQ.js +326 -0
- package/web/dist/assets/{index-CMgDryBL.css → index-DVnuQrK5.css} +1 -1
- package/web/dist/favicon.svg +8 -8
- package/web/dist/index.html +21 -21
- package/web/dist/manifest.webmanifest +50 -50
- package/web/dist/sw.js +126 -126
- package/web/public/favicon.svg +8 -8
- package/web/public/manifest.webmanifest +50 -50
- package/web/public/sw.js +126 -126
- package/web/dist/assets/TerminalPanel-DNkAT34Z.js +0 -2
- package/web/dist/assets/index-_1vyEugI.js +0 -326
|
@@ -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>PI_WEB_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>PI_WEB_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 PI_WEB_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 PI_WEB_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,54 +1,54 @@
|
|
|
1
|
-
<?xml version="1.0" encoding="UTF-16"?>
|
|
2
|
-
<!--
|
|
3
|
-
pi-web-ui Task Scheduler task — Windows auto-start at logon.
|
|
4
|
-
|
|
5
|
-
pi-web-ui server install 默认使用「登录 Run 键 + wscript 隐藏启动」(HKCU,无需管理员、
|
|
6
|
-
无黑窗),并自动迁移旧版计划任务安装。本 XML 仅保留给需要手动注册计划任务的场景
|
|
7
|
-
(例如管理员环境),不是默认路径。
|
|
8
|
-
-->
|
|
9
|
-
<Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
|
|
10
|
-
<RegistrationInfo>
|
|
11
|
-
<Description>pi-web-ui — web chat for the pi coding agent (auto-start at logon)</Description>
|
|
12
|
-
</RegistrationInfo>
|
|
13
|
-
<Triggers>
|
|
14
|
-
<LogonTrigger>
|
|
15
|
-
<Enabled>true</Enabled>
|
|
16
|
-
</LogonTrigger>
|
|
17
|
-
</Triggers>
|
|
18
|
-
<Principals>
|
|
19
|
-
<Principal id="Author">
|
|
20
|
-
<LogonType>InteractiveToken</LogonType>
|
|
21
|
-
<RunLevel>LeastPrivilege</RunLevel>
|
|
22
|
-
</Principal>
|
|
23
|
-
</Principals>
|
|
24
|
-
<Settings>
|
|
25
|
-
<MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>
|
|
26
|
-
<DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries>
|
|
27
|
-
<StopIfGoingOnBatteries>false</StopIfGoingOnBatteries>
|
|
28
|
-
<AllowHardTerminate>true</AllowHardTerminate>
|
|
29
|
-
<StartWhenAvailable>false</StartWhenAvailable>
|
|
30
|
-
<RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable>
|
|
31
|
-
<IdleSettings>
|
|
32
|
-
<StopOnIdleEnd>false</StopOnIdleEnd>
|
|
33
|
-
<RestartOnIdle>false</RestartOnIdle>
|
|
34
|
-
</IdleSettings>
|
|
35
|
-
<AllowStartOnDemand>true</AllowStartOnDemand>
|
|
36
|
-
<Enabled>true</Enabled>
|
|
37
|
-
<Hidden>false</Hidden>
|
|
38
|
-
<RunOnlyIfIdle>false</RunOnlyIfIdle>
|
|
39
|
-
<WakeToRun>false</WakeToRun>
|
|
40
|
-
<ExecutionTimeLimit>PT0S</ExecutionTimeLimit>
|
|
41
|
-
<Priority>7</Priority>
|
|
42
|
-
<RestartOnFailure>
|
|
43
|
-
<Interval>PT1M</Interval>
|
|
44
|
-
<Count>3</Count>
|
|
45
|
-
</RestartOnFailure>
|
|
46
|
-
</Settings>
|
|
47
|
-
<Actions Context="Author">
|
|
48
|
-
<Exec>
|
|
49
|
-
<Command>C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe</Command>
|
|
50
|
-
<Arguments>-NoProfile -NonInteractive -ExecutionPolicy Bypass -WindowStyle Hidden -File "%APPDATA%\pi-web-ui\pi-web-ui.ps1"</Arguments>
|
|
51
|
-
<WorkingDirectory>%USERPROFILE%</WorkingDirectory>
|
|
52
|
-
</Exec>
|
|
53
|
-
</Actions>
|
|
54
|
-
</Task>
|
|
1
|
+
<?xml version="1.0" encoding="UTF-16"?>
|
|
2
|
+
<!--
|
|
3
|
+
pi-web-ui Task Scheduler task — Windows auto-start at logon.
|
|
4
|
+
|
|
5
|
+
pi-web-ui server install 默认使用「登录 Run 键 + wscript 隐藏启动」(HKCU,无需管理员、
|
|
6
|
+
无黑窗),并自动迁移旧版计划任务安装。本 XML 仅保留给需要手动注册计划任务的场景
|
|
7
|
+
(例如管理员环境),不是默认路径。
|
|
8
|
+
-->
|
|
9
|
+
<Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
|
|
10
|
+
<RegistrationInfo>
|
|
11
|
+
<Description>pi-web-ui — web chat for the pi coding agent (auto-start at logon)</Description>
|
|
12
|
+
</RegistrationInfo>
|
|
13
|
+
<Triggers>
|
|
14
|
+
<LogonTrigger>
|
|
15
|
+
<Enabled>true</Enabled>
|
|
16
|
+
</LogonTrigger>
|
|
17
|
+
</Triggers>
|
|
18
|
+
<Principals>
|
|
19
|
+
<Principal id="Author">
|
|
20
|
+
<LogonType>InteractiveToken</LogonType>
|
|
21
|
+
<RunLevel>LeastPrivilege</RunLevel>
|
|
22
|
+
</Principal>
|
|
23
|
+
</Principals>
|
|
24
|
+
<Settings>
|
|
25
|
+
<MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>
|
|
26
|
+
<DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries>
|
|
27
|
+
<StopIfGoingOnBatteries>false</StopIfGoingOnBatteries>
|
|
28
|
+
<AllowHardTerminate>true</AllowHardTerminate>
|
|
29
|
+
<StartWhenAvailable>false</StartWhenAvailable>
|
|
30
|
+
<RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable>
|
|
31
|
+
<IdleSettings>
|
|
32
|
+
<StopOnIdleEnd>false</StopOnIdleEnd>
|
|
33
|
+
<RestartOnIdle>false</RestartOnIdle>
|
|
34
|
+
</IdleSettings>
|
|
35
|
+
<AllowStartOnDemand>true</AllowStartOnDemand>
|
|
36
|
+
<Enabled>true</Enabled>
|
|
37
|
+
<Hidden>false</Hidden>
|
|
38
|
+
<RunOnlyIfIdle>false</RunOnlyIfIdle>
|
|
39
|
+
<WakeToRun>false</WakeToRun>
|
|
40
|
+
<ExecutionTimeLimit>PT0S</ExecutionTimeLimit>
|
|
41
|
+
<Priority>7</Priority>
|
|
42
|
+
<RestartOnFailure>
|
|
43
|
+
<Interval>PT1M</Interval>
|
|
44
|
+
<Count>3</Count>
|
|
45
|
+
</RestartOnFailure>
|
|
46
|
+
</Settings>
|
|
47
|
+
<Actions Context="Author">
|
|
48
|
+
<Exec>
|
|
49
|
+
<Command>C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe</Command>
|
|
50
|
+
<Arguments>-NoProfile -NonInteractive -ExecutionPolicy Bypass -WindowStyle Hidden -File "%APPDATA%\pi-web-ui\pi-web-ui.ps1"</Arguments>
|
|
51
|
+
<WorkingDirectory>%USERPROFILE%</WorkingDirectory>
|
|
52
|
+
</Exec>
|
|
53
|
+
</Actions>
|
|
54
|
+
</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=PI_WEB_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=PI_WEB_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
|
|
@@ -84,14 +84,14 @@ export class QuiesceRejectedError extends Error {
|
|
|
84
84
|
* programs wait for input that never comes. Legacy Chinese files are often
|
|
85
85
|
* GBK/GB2312 — read them with the right encoding, never paste mojibake into
|
|
86
86
|
* reasoning/answers. */
|
|
87
|
-
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:
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
- 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.
|
|
92
|
-
- 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.
|
|
93
|
-
- 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\`).
|
|
94
|
-
|
|
87
|
+
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:
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
- 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.
|
|
92
|
+
- 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.
|
|
93
|
+
- 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\`).
|
|
94
|
+
|
|
95
95
|
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.`;
|
|
96
96
|
/** bash 工具输出限制/过滤管道引导:模型习惯套 `| tail/-n`、`| head`、`| grep`、`| less`
|
|
97
97
|
* 等限输出。这些管道在持久终端里会①缓冲(可见终端全程哑火、看不到实时进度)②把退出码
|
|
@@ -595,7 +595,7 @@ export class ClientSession {
|
|
|
595
595
|
try {
|
|
596
596
|
await conv.session.bindExtensions({
|
|
597
597
|
mode: "rpc",
|
|
598
|
-
onError: (err) => this.emit({ type: "notice", level: "error", text: err.error }),
|
|
598
|
+
onError: (err) => this.emit({ type: "notice", level: "error", text: err.error, textEn: err.error }),
|
|
599
599
|
});
|
|
600
600
|
}
|
|
601
601
|
catch {
|
|
@@ -607,6 +607,7 @@ export class ClientSession {
|
|
|
607
607
|
type: "notice",
|
|
608
608
|
level: "error",
|
|
609
609
|
text: `子代理 ${conversationId} 启动失败: ${err instanceof Error ? err.message : String(err)}`,
|
|
610
|
+
textEn: `Subagent ${conversationId} failed to start: ${err instanceof Error ? err.message : String(err)}`,
|
|
610
611
|
});
|
|
611
612
|
});
|
|
612
613
|
this.emitConversations();
|
|
@@ -910,6 +911,7 @@ export class ClientSession {
|
|
|
910
911
|
type: "notice",
|
|
911
912
|
level: d.type,
|
|
912
913
|
text: d.message,
|
|
914
|
+
textEn: d.message,
|
|
913
915
|
});
|
|
914
916
|
}
|
|
915
917
|
}
|
|
@@ -1180,7 +1182,7 @@ export class ClientSession {
|
|
|
1180
1182
|
mode: "rpc",
|
|
1181
1183
|
uiContext: this.webUi,
|
|
1182
1184
|
onError: (err) => {
|
|
1183
|
-
this.emit({ type: "notice", level: "error", text: err.error });
|
|
1185
|
+
this.emit({ type: "notice", level: "error", text: err.error, textEn: err.error });
|
|
1184
1186
|
},
|
|
1185
1187
|
});
|
|
1186
1188
|
conv.unsubscribe = conv.session.subscribe((event) => this.onEvent(conv, event));
|
|
@@ -1437,7 +1439,7 @@ export class ClientSession {
|
|
|
1437
1439
|
if (aborted) {
|
|
1438
1440
|
const stopNotice = this.goalSvc.onAgentEnd(conv, true);
|
|
1439
1441
|
if (stopNotice) {
|
|
1440
|
-
this.emit({ type: "notice", level: "warning", text: stopNotice });
|
|
1442
|
+
this.emit({ type: "notice", level: "warning", text: stopNotice.text, textEn: stopNotice.textEn });
|
|
1441
1443
|
}
|
|
1442
1444
|
break;
|
|
1443
1445
|
}
|
|
@@ -2017,7 +2019,7 @@ export class ClientSession {
|
|
|
2017
2019
|
const result = await def.run(args, { clientId: this.clientId });
|
|
2018
2020
|
// 字符串返回值 → 通知条回显给发起人;富展示用 broadcast/sendTo。
|
|
2019
2021
|
if (typeof result === "string" && result.trim()) {
|
|
2020
|
-
this.emit({ type: "notice", level: "info", text: result });
|
|
2022
|
+
this.emit({ type: "notice", level: "info", text: result, textEn: result });
|
|
2021
2023
|
}
|
|
2022
2024
|
}
|
|
2023
2025
|
catch (err) {
|
|
@@ -2518,8 +2520,8 @@ export class ClientSession {
|
|
|
2518
2520
|
this.bg.push();
|
|
2519
2521
|
}
|
|
2520
2522
|
/** 插件设置保存结果等需要从 index.ts 发 notice 时用(emit 是私有的)。 */
|
|
2521
|
-
emitNotice(level, text) {
|
|
2522
|
-
this.emit({ type: "notice", level, text });
|
|
2523
|
+
emitNotice(level, text, textEn) {
|
|
2524
|
+
this.emit({ type: "notice", level, text, textEn });
|
|
2523
2525
|
}
|
|
2524
2526
|
/** Kill ONE background server (by port); returns whether anything was killed. */
|
|
2525
2527
|
/** Kill ONE background server (by port) OR a plugin task (by taskId). */
|
|
@@ -2649,7 +2651,12 @@ export class ClientSession {
|
|
|
2649
2651
|
});
|
|
2650
2652
|
conv.runtime = runtime;
|
|
2651
2653
|
conv.session = runtime.session;
|
|
2652
|
-
this.emit({
|
|
2654
|
+
this.emit({
|
|
2655
|
+
type: "notice",
|
|
2656
|
+
level: "warning",
|
|
2657
|
+
text: reason,
|
|
2658
|
+
textEn: `${reason} (forced reset: run did not terminate)`,
|
|
2659
|
+
});
|
|
2653
2660
|
await this.bindSession();
|
|
2654
2661
|
this.emitConversations();
|
|
2655
2662
|
void this.pushSlashCommands();
|
|
@@ -3041,6 +3048,9 @@ export class ClientSession {
|
|
|
3041
3048
|
text: stillRunning
|
|
3042
3049
|
? "对话仍在后台运行,已停止删除;请等待其结束后再删除"
|
|
3043
3050
|
: "未能切换到其他对话,已取消删除本次操作",
|
|
3051
|
+
textEn: stillRunning
|
|
3052
|
+
? "Conversation is still running in the background; delete aborted — wait for it to finish and retry"
|
|
3053
|
+
: "Could not switch to another conversation; delete cancelled",
|
|
3044
3054
|
});
|
|
3045
3055
|
return;
|
|
3046
3056
|
}
|
|
@@ -3147,11 +3157,21 @@ export class ClientSession {
|
|
|
3147
3157
|
async dismissConversation(id) {
|
|
3148
3158
|
const conv = this.convs.get(id);
|
|
3149
3159
|
if (!conv) {
|
|
3150
|
-
this.emit({
|
|
3160
|
+
this.emit({
|
|
3161
|
+
type: "notice",
|
|
3162
|
+
level: "warning",
|
|
3163
|
+
text: "该对话不存在或已关闭",
|
|
3164
|
+
textEn: "This conversation does not exist or is already closed",
|
|
3165
|
+
});
|
|
3151
3166
|
return;
|
|
3152
3167
|
}
|
|
3153
3168
|
if (id === this.activeId) {
|
|
3154
|
-
this.emit({
|
|
3169
|
+
this.emit({
|
|
3170
|
+
type: "notice",
|
|
3171
|
+
level: "warning",
|
|
3172
|
+
text: "当前对话不能直接移出,请先切换到其他对话",
|
|
3173
|
+
textEn: "The active conversation cannot be removed directly — switch to another conversation first",
|
|
3174
|
+
});
|
|
3155
3175
|
return;
|
|
3156
3176
|
}
|
|
3157
3177
|
if (!conv.listed) {
|
|
@@ -3176,6 +3196,7 @@ export class ClientSession {
|
|
|
3176
3196
|
type: "notice",
|
|
3177
3197
|
level: "warning",
|
|
3178
3198
|
text: `对话「${conv.title}」仍在运行中,请先等待结束或点击停止后再移出`,
|
|
3199
|
+
textEn: `Conversation "${conv.title}" is still running — wait for it to finish or press Stop before removing`,
|
|
3179
3200
|
});
|
|
3180
3201
|
}
|
|
3181
3202
|
else if (conv.terminals.list().length > 0) {
|
|
@@ -3183,6 +3204,7 @@ export class ClientSession {
|
|
|
3183
3204
|
type: "notice",
|
|
3184
3205
|
level: "warning",
|
|
3185
3206
|
text: `对话「${conv.title}」还有未关闭的终端,请先关闭终端后再移出`,
|
|
3207
|
+
textEn: `Conversation "${conv.title}" still has open terminals — close them before removing`,
|
|
3186
3208
|
});
|
|
3187
3209
|
}
|
|
3188
3210
|
else {
|
|
@@ -3190,6 +3212,7 @@ export class ClientSession {
|
|
|
3190
3212
|
type: "notice",
|
|
3191
3213
|
level: "warning",
|
|
3192
3214
|
text: `对话「${conv.title}」暂时无法移出(存在待处理的后台任务/审查)`,
|
|
3215
|
+
textEn: `Conversation "${conv.title}" cannot be removed right now (pending background task/review)`,
|
|
3193
3216
|
});
|
|
3194
3217
|
}
|
|
3195
3218
|
return;
|
|
@@ -3573,7 +3596,7 @@ export class ClientSession {
|
|
|
3573
3596
|
this.removeConversation(displaced.id);
|
|
3574
3597
|
for (const d of newRuntime.diagnostics) {
|
|
3575
3598
|
if (d.type !== "info") {
|
|
3576
|
-
this.emit({ type: "notice", level: d.type, text: d.message });
|
|
3599
|
+
this.emit({ type: "notice", level: d.type, text: d.message, textEn: d.message });
|
|
3577
3600
|
}
|
|
3578
3601
|
}
|
|
3579
3602
|
await this.bindSession();
|
|
@@ -3733,17 +3756,17 @@ export class ClientSession {
|
|
|
3733
3756
|
}
|
|
3734
3757
|
/** Push the user command list (.pi/commands.json) to the client. */
|
|
3735
3758
|
async listCommands() {
|
|
3736
|
-
const { commands, path, warning } = await loadCommands(this.cwd);
|
|
3759
|
+
const { commands, path, warning, warningEn } = await loadCommands(this.cwd);
|
|
3737
3760
|
if (warning) {
|
|
3738
|
-
this.emit({ type: "notice", level: "warning", text: warning });
|
|
3761
|
+
this.emit({ type: "notice", level: "warning", text: warning, textEn: warningEn });
|
|
3739
3762
|
}
|
|
3740
3763
|
this.emit({ type: "commands", commands, path });
|
|
3741
3764
|
}
|
|
3742
3765
|
/** Persist the user command list (.pi/commands.json). */
|
|
3743
3766
|
async saveCommands(commands) {
|
|
3744
|
-
const { path, error } = await saveCommandsFile(this.cwd, commands);
|
|
3767
|
+
const { path, error, errorEn } = await saveCommandsFile(this.cwd, commands);
|
|
3745
3768
|
if (error) {
|
|
3746
|
-
this.emit({ type: "notice", level: "error", text: error });
|
|
3769
|
+
this.emit({ type: "notice", level: "error", text: error, textEn: errorEn });
|
|
3747
3770
|
return;
|
|
3748
3771
|
}
|
|
3749
3772
|
this.emit({ type: "commands", commands, path });
|