pi-web-ui 0.25.0 → 0.26.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -21
- package/README.md +235 -167
- package/README.zh-CN.md +218 -155
- package/bin/pi-web-ui.mjs +0 -0
- package/deploy/com.xingshuyin.pi-web-ui.plist +48 -48
- package/deploy/nginx-subpath.conf +88 -78
- package/deploy/pi-web-ui-task.xml +71 -71
- package/deploy/pi-web-ui.service +31 -31
- package/dist/server/agent-service.js +100 -16
- package/dist/server/index.js +3 -0
- package/extensions/webui.ts +192 -192
- package/package.json +1 -1
- package/web/dist/assets/index-CFbrzMlt.js +129 -0
- package/web/dist/assets/index-nGxyor_h.css +41 -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-BfpjVv5g.css +0 -41
- package/web/dist/assets/index-CNppIDRF.js +0 -128
|
@@ -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
|
|
@@ -18,7 +18,7 @@ import { createAgentSessionFromServices, createAgentSessionRuntime, createAgentS
|
|
|
18
18
|
import { Type } from "typebox";
|
|
19
19
|
import { serializeMessage, serializeStreamingMessage, } from "./serialize.js";
|
|
20
20
|
import { loadCommands, saveCommandsFile, TerminalManager, } from "./terminals.js";
|
|
21
|
-
import { buildVisionBridgePrompt, findVisionModels, transcribeImages, } from "./vision-bridge.js";
|
|
21
|
+
import { buildVisionBridgePrompt, findVisionModels, SYSTEM_PROMPT, transcribeImages, } from "./vision-bridge.js";
|
|
22
22
|
const SNAPSHOT_INTERVAL_MS = 60;
|
|
23
23
|
const WIDGET_REFRESH_MS = 2000;
|
|
24
24
|
const WIDGET_WIDTH = 80;
|
|
@@ -274,14 +274,14 @@ function sniffImageMime(buf, ext) {
|
|
|
274
274
|
* programs wait for input that never comes. Legacy Chinese files are often
|
|
275
275
|
* GBK/GB2312 — read them with the right encoding, never paste mojibake into
|
|
276
276
|
* reasoning/answers. */
|
|
277
|
-
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:
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
- 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.
|
|
282
|
-
- 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.
|
|
283
|
-
- 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\`).
|
|
284
|
-
|
|
277
|
+
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:
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
- 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.
|
|
282
|
+
- 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.
|
|
283
|
+
- 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\`).
|
|
284
|
+
|
|
285
285
|
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.`;
|
|
286
286
|
/**
|
|
287
287
|
* Killable bash tool: wraps the SDK bash tool with operations that register
|
|
@@ -1114,6 +1114,30 @@ export class ClientSession {
|
|
|
1114
1114
|
* a question doesn't re-burn tokens on re-transcribing identical screenshots.
|
|
1115
1115
|
*/
|
|
1116
1116
|
visionBridgeCache = new Map();
|
|
1117
|
+
/** Most recent built-in (default) system prompt observed by the
|
|
1118
|
+
* resource-loader override — surfaced via settings_state so the
|
|
1119
|
+
* replace-mode editor can show the prompt it would otherwise replace.
|
|
1120
|
+
* Only non-empty when the user has a system-prompt file. */
|
|
1121
|
+
lastBaseSystemPrompt = "";
|
|
1122
|
+
/** The system prompt the replace-mode editor should show as its seed:
|
|
1123
|
+
* the user's system-prompt file content if one exists, otherwise the
|
|
1124
|
+
* SDK's built-in default actually in effect (agent.state.systemPrompt,
|
|
1125
|
+
* which the loader rebuilds at session init). If the user HAS a custom
|
|
1126
|
+
* prompt the seed is only cosmetic — an unmodified seed is saved as
|
|
1127
|
+
* empty and the server falls back to the true base. */
|
|
1128
|
+
effectiveDefaultSystemPrompt() {
|
|
1129
|
+
if (this.lastBaseSystemPrompt)
|
|
1130
|
+
return this.lastBaseSystemPrompt;
|
|
1131
|
+
try {
|
|
1132
|
+
const sp = this.session.agent.state.systemPrompt;
|
|
1133
|
+
if (typeof sp === "string" && sp)
|
|
1134
|
+
return sp;
|
|
1135
|
+
}
|
|
1136
|
+
catch {
|
|
1137
|
+
// Session not ready yet.
|
|
1138
|
+
}
|
|
1139
|
+
return "";
|
|
1140
|
+
}
|
|
1117
1141
|
/** Web-facing extension UI context (widgets, notifications). */
|
|
1118
1142
|
webUi = new WebUIContext((msg) => this.emit(msg));
|
|
1119
1143
|
widgetsTimer = null;
|
|
@@ -1203,10 +1227,17 @@ export class ClientSession {
|
|
|
1203
1227
|
// 新对话(新 runtime)也会自动带上当前设置。
|
|
1204
1228
|
resourceLoaderOptions: {
|
|
1205
1229
|
// 系统提示词:replace 模式整体替换;append 模式追加到提示词末尾。
|
|
1206
|
-
systemPromptOverride: (base) =>
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1230
|
+
systemPromptOverride: (base) => {
|
|
1231
|
+
// Remember the built-in default so the settings panel can show
|
|
1232
|
+
// it when the user edits in replace mode.
|
|
1233
|
+
if (typeof base === "string" && base) {
|
|
1234
|
+
this.lastBaseSystemPrompt = base;
|
|
1235
|
+
}
|
|
1236
|
+
return this.settings.promptMode === "replace" &&
|
|
1237
|
+
this.settings.customSystemPrompt.trim()
|
|
1238
|
+
? this.settings.customSystemPrompt
|
|
1239
|
+
: base;
|
|
1240
|
+
},
|
|
1210
1241
|
appendSystemPromptOverride: (base) => {
|
|
1211
1242
|
const out = [...base];
|
|
1212
1243
|
const custom = this.settings.customSystemPrompt.trim();
|
|
@@ -2539,6 +2570,11 @@ export class ClientSession {
|
|
|
2539
2570
|
visionBridgeModel: this.settings.visionBridgeModel,
|
|
2540
2571
|
visionBridgePromptMode: this.settings.visionBridgePromptMode,
|
|
2541
2572
|
visionBridgePrompt: this.settings.visionBridgePrompt,
|
|
2573
|
+
// The built-in prompts, so the replace-mode editors can prefill the
|
|
2574
|
+
// text they would otherwise replace (empty until the resource-loader
|
|
2575
|
+
// has run once for the system prompt).
|
|
2576
|
+
defaultSystemPrompt: this.effectiveDefaultSystemPrompt(),
|
|
2577
|
+
visionBridgeDefaultPrompt: SYSTEM_PROMPT,
|
|
2542
2578
|
visionModels: this.collectVisionModels(),
|
|
2543
2579
|
disabledSkills: [...this.settings.disabledSkills],
|
|
2544
2580
|
disabledExtensions: [...this.settings.disabledExtensions],
|
|
@@ -3196,9 +3232,9 @@ export class ClientSession {
|
|
|
3196
3232
|
content: [
|
|
3197
3233
|
{
|
|
3198
3234
|
type: "text",
|
|
3199
|
-
text: `
|
|
3200
|
-
<vision-bridge>
|
|
3201
|
-
${transcript}
|
|
3235
|
+
text: `
|
|
3236
|
+
<vision-bridge>
|
|
3237
|
+
${transcript}
|
|
3202
3238
|
</vision-bridge>`,
|
|
3203
3239
|
},
|
|
3204
3240
|
...(pathImg
|
|
@@ -4170,6 +4206,54 @@ ${transcript}
|
|
|
4170
4206
|
});
|
|
4171
4207
|
}
|
|
4172
4208
|
}
|
|
4209
|
+
/** Save text from the file preview panel within the active workspace. */
|
|
4210
|
+
async writeFile(relPath, text) {
|
|
4211
|
+
try {
|
|
4212
|
+
const root = resolve(this.cwd);
|
|
4213
|
+
const wp = workspacePath(root, relPath);
|
|
4214
|
+
if (!wp) {
|
|
4215
|
+
this.emit({
|
|
4216
|
+
type: "notice",
|
|
4217
|
+
level: "warning",
|
|
4218
|
+
text: `路径超出工作区:${relPath}`,
|
|
4219
|
+
});
|
|
4220
|
+
return;
|
|
4221
|
+
}
|
|
4222
|
+
if (Buffer.byteLength(text, "utf8") > 2 * 1024 * 1024) {
|
|
4223
|
+
this.emit({
|
|
4224
|
+
type: "notice",
|
|
4225
|
+
level: "warning",
|
|
4226
|
+
text: "文件内容过大,无法保存(上限 2MB)",
|
|
4227
|
+
});
|
|
4228
|
+
return;
|
|
4229
|
+
}
|
|
4230
|
+
const stat = statSync(wp.abs);
|
|
4231
|
+
if (!stat.isFile()) {
|
|
4232
|
+
this.emit({
|
|
4233
|
+
type: "notice",
|
|
4234
|
+
level: "warning",
|
|
4235
|
+
text: `不是文件:${relPath}`,
|
|
4236
|
+
});
|
|
4237
|
+
return;
|
|
4238
|
+
}
|
|
4239
|
+
writeFileSync(wp.abs, text, "utf8");
|
|
4240
|
+
this.emit({
|
|
4241
|
+
type: "notice",
|
|
4242
|
+
level: "info",
|
|
4243
|
+
text: `已保存:${wp.rel}`,
|
|
4244
|
+
});
|
|
4245
|
+
// Re-read through the same path as the preview request so the client
|
|
4246
|
+
// gets the canonical content, line count and file size after saving.
|
|
4247
|
+
await this.readFile(wp.rel);
|
|
4248
|
+
}
|
|
4249
|
+
catch (err) {
|
|
4250
|
+
this.emit({
|
|
4251
|
+
type: "notice",
|
|
4252
|
+
level: "error",
|
|
4253
|
+
text: `保存文件失败:${err.message}`,
|
|
4254
|
+
});
|
|
4255
|
+
}
|
|
4256
|
+
}
|
|
4173
4257
|
async cycleModel() {
|
|
4174
4258
|
try {
|
|
4175
4259
|
await this.session.cycleModel();
|
package/dist/server/index.js
CHANGED