pi-web-ui 0.94.1 → 0.96.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/CHANGELOG.md +91 -2
- package/README.md +5 -6
- package/README.zh-CN.md +4 -5
- package/bin/pi-web-ui.mjs +910 -237
- package/dist/server/agent-service.js +2655 -231
- package/dist/server/approval-rules.js +596 -0
- package/dist/server/attachment-store.js +129 -0
- package/dist/server/attachments.js +92 -174
- package/dist/server/bg-servers.js +45 -7
- package/dist/server/claim-files-tool.js +4 -8
- package/dist/server/claim-store.js +4 -2
- package/dist/server/client-state.js +67 -14
- package/dist/server/compact-context-tool.js +126 -0
- package/dist/server/composer-drafts.js +9 -0
- package/dist/server/context-budget.js +317 -0
- package/dist/server/control-socket.js +57 -28
- package/dist/server/conversation-read-tool.js +7 -17
- package/dist/server/dangling-tools.js +229 -0
- package/dist/server/delegate-task.js +25 -28
- package/dist/server/dsh/dsh-agent-service.js +69 -66
- package/dist/server/edit-soft-tool.js +84 -16
- package/dist/server/eval-tool.js +588 -0
- package/dist/server/file-archives.js +16 -5
- package/dist/server/files-service.js +65 -18
- package/dist/server/goal-service.js +371 -77
- package/dist/server/hashline-engine.js +703 -0
- package/dist/server/host-guard.js +94 -0
- package/dist/server/host-metrics.js +26 -2
- package/dist/server/i18n.js +3 -3
- package/dist/server/index.js +456 -80
- package/dist/server/lsp-tool.js +1371 -0
- package/dist/server/mcp-bridge.js +47 -3
- package/dist/server/model-admin.js +112 -32
- package/dist/server/office-parse.js +375 -0
- package/dist/server/patch-tool.js +85 -0
- package/dist/server/permission-preset.js +25 -0
- package/dist/server/plan-manager.js +114 -0
- package/dist/server/plugin-api-catalog.js +296 -0
- package/dist/server/plugin-catalog-sync.js +36 -19
- package/dist/server/plugin-catalog.js +11 -4
- package/dist/server/plugin-facilities.js +92 -21
- package/dist/server/plugin-install-spec.js +196 -0
- package/dist/server/plugin-installer.js +73 -0
- package/dist/server/plugin-llm.js +71 -65
- package/dist/server/plugin-manifest-validate.js +305 -0
- package/dist/server/plugin-project.js +117 -13
- package/dist/server/plugin-tool-guard.js +120 -0
- package/dist/server/plugin-updater.js +115 -12
- package/dist/server/plugins.js +950 -205
- package/dist/server/present-files-tool.js +9 -12
- package/dist/server/process-utils.js +16 -6
- package/dist/server/prompt-composer.js +9 -0
- package/dist/server/protocol-version.js +1 -1
- package/dist/server/read-tool.js +69 -30
- package/dist/server/resolve-global-sdk.js +30 -16
- package/dist/server/schedule-agent-tool.js +12 -15
- package/dist/server/scheduler-tasks.js +6 -0
- package/dist/server/sdk-origin.js +18 -2
- package/dist/server/serialize.js +111 -14
- package/dist/server/settings-service.js +112 -3
- package/dist/server/skill-tool.js +5 -7
- package/dist/server/subagent-templates.js +51 -0
- package/dist/server/subagents.js +83 -66
- package/dist/server/terminals.js +248 -81
- package/dist/server/tool-approval.js +84 -0
- package/dist/server/tool-manager.js +237 -12
- package/dist/server/tool-overrides.js +59 -0
- package/dist/server/update-check.js +28 -1
- package/dist/server/uploads.js +17 -2
- package/dist/server/wait-subscription-scan.js +18 -21
- package/dist/server/workspace-snapshot.js +113 -0
- package/dist/server/ws-client-id.js +29 -0
- package/dist/server/ws-pending-queue.js +60 -0
- package/extensions/webui.ts +60 -2
- package/package.json +5 -3
- package/plugin-sdk/README.md +25 -0
- package/plugin-sdk/index.d.ts +31 -38
- package/plugin-sdk/index.mjs +35 -19
- package/plugins/catalog.json +40 -0
- package/themes/aetheris.css +457 -0
- package/themes/ayu-light.css +6 -6
- package/themes/catppuccin-latte.css +6 -6
- package/themes/claude-code-dark.css +144 -0
- package/themes/codex.css +6 -6
- package/themes/everforest-light.css +6 -6
- package/themes/geist.css +6 -6
- package/themes/gruvbox-light.css +6 -6
- package/themes/kanagawa-lotus.css +6 -6
- package/themes/rose-pine-dawn.css +6 -6
- package/themes/solarized-light.css +6 -6
- package/themes/vs-code-dark.css +146 -0
- package/themes/zhupi-dark.css +658 -0
- package/themes/zhupi.css +711 -0
- package/web/dist/assets/{TerminalPanel-MVoxpJOA.js → TerminalPanel-DDChcYOh.js} +1 -1
- package/web/dist/assets/index-Do9RgJC3.js +366 -0
- package/web/dist/assets/index-DryOsILO.css +1 -0
- package/web/dist/assets/{markdown-eUQn_o9D.js → markdown-DXwnfD9T.js} +1 -1
- package/web/dist/index.html +3 -3
- package/web/dist/assets/index-B3S9MxnN.css +0 -1
- package/web/dist/assets/index-DVLrHI2E.js +0 -364
|
@@ -5,11 +5,12 @@
|
|
|
5
5
|
* 全部为无状态 fs 操作 + 两个自持的 watcher(当前列出目录、git dir),
|
|
6
6
|
* 经 FilesHost 回调与 ClientSession 解耦。
|
|
7
7
|
*/
|
|
8
|
-
import { Dirent, mkdirSync, readFileSync, statSync, writeFileSync, watch } from "node:fs";
|
|
8
|
+
import { Dirent, existsSync, mkdirSync, readFileSync, statSync, writeFileSync, watch } from "node:fs";
|
|
9
9
|
import { homedir } from "node:os";
|
|
10
|
-
import { resolve, relative, sep } from "node:path";
|
|
10
|
+
import { resolve, relative, sep, isAbsolute } from "node:path";
|
|
11
11
|
import { pick } from "./i18n.js";
|
|
12
12
|
import { previewKind, looksLikeText, decodeText, hexDump, countLines } from "./text-sniff.js";
|
|
13
|
+
import { extractOfficeText, isOfficeFile, OFFICE_MAX_FILE_BYTES } from "./office-parse.js";
|
|
13
14
|
import { gitDirOf, isNotRepoError, scmStatus, scmHistory, scmFileDiff, scmCommitDetail } from "./scm.js";
|
|
14
15
|
export const IS_WIN32 = process.platform === "win32";
|
|
15
16
|
/** 机器根虚拟路径:工作区「上一级」到达此处,列出所有盘符(Windows)/ "/"(posix)。
|
|
@@ -47,11 +48,15 @@ export function desktopDirWire(homeWire) {
|
|
|
47
48
|
}
|
|
48
49
|
/** wire 路径统一用 "/"。绝对 = posix "/...";win32 还有 "C:/..." / 裸 "C:"。
|
|
49
50
|
* 机器浏览(越过工作区根换盘符)发送这些路径;工作区相对树不会产生它们(Windows
|
|
50
|
-
* 文件名不能含 ":",相对路径经 relative() 归一化后也不以 "/" 开头)。
|
|
51
|
+
* 文件名不能含 ":",相对路径经 relative() 归一化后也不以 "/" 开头)。
|
|
52
|
+
* win32 还必须把盘符相对路径("D:x",冒号后无分隔符 —— resolve 会落到该盘的
|
|
53
|
+
* 当前目录)与 UNC("\\host\share";"//host/share" 已被上面的 "/" 前缀覆盖)
|
|
54
|
+
* 判为绝对:这些形态若被当工作区相对路径处理,会在跨盘/UNC 下绕过 ".." 检查
|
|
55
|
+
* 逃出工作区。posix 上 "C:x" 是合法文件名,故盘符检查必须限定在 win32。 */
|
|
51
56
|
export function isAbsoluteWirePath(p) {
|
|
52
|
-
if (p === MACHINE_ROOT || p.startsWith("/"))
|
|
57
|
+
if (p === MACHINE_ROOT || p.startsWith("/") || p.startsWith("\\\\"))
|
|
53
58
|
return true;
|
|
54
|
-
return IS_WIN32 && /^[A-Za-z]
|
|
59
|
+
return IS_WIN32 && /^[A-Za-z]:/.test(p);
|
|
55
60
|
}
|
|
56
61
|
/** 去掉结尾 "/"(保留 posix 根 "/" 本身),归一成规范的 wire 形式。前端面包屑
|
|
57
62
|
* 不产生结尾斜杠,这里只对补全/直接输入做防御性清理。 */
|
|
@@ -131,7 +136,11 @@ function ignoredEntries() {
|
|
|
131
136
|
export function workspacePath(root, raw) {
|
|
132
137
|
const abs = resolve(root, raw);
|
|
133
138
|
const rawRel = relative(root, abs);
|
|
134
|
-
|
|
139
|
+
// 只查 ".." 拦不住 Windows 的跨盘/UNC:relative("C:/a","D:/x") 与
|
|
140
|
+
// relative("C:/a","//h/s") 返回绝对路径("D:\x"、"\\h\s\"),不以 ".." 开头;
|
|
141
|
+
// 盘符相对路径("D:x")经 resolve 落到该盘当前目录后也归此形 —— 必须同时
|
|
142
|
+
// 拒绝 isAbsolute 的结果,否则 "D:x" 这类输入可逃出工作区根。
|
|
143
|
+
if (rawRel.startsWith("..") || rawRel.includes(`${sep}..`) || isAbsolute(rawRel))
|
|
135
144
|
return null;
|
|
136
145
|
// Normalize to forward slashes: the wire protocol and the frontend always
|
|
137
146
|
// use "/", but relative() returns "\\" on Windows.
|
|
@@ -769,6 +778,31 @@ export class FilesService {
|
|
|
769
778
|
return;
|
|
770
779
|
}
|
|
771
780
|
const name = relPath.split(/[\\/]/).pop() ?? relPath;
|
|
781
|
+
// Office 文档(docx/xlsx/xlsm):转 Markdown 文本下发,前端按 Markdown
|
|
782
|
+
// 渲染表格/段落——文件树、附件、预览弹窗随处可看。失败回落到底下旧分支。
|
|
783
|
+
if (isOfficeFile(name) && stat.size <= OFFICE_MAX_FILE_BYTES) {
|
|
784
|
+
try {
|
|
785
|
+
const data = await fs.readFile(abs);
|
|
786
|
+
const office = extractOfficeText(name, data);
|
|
787
|
+
if (office) {
|
|
788
|
+
this.host.emit({
|
|
789
|
+
type: "file_content",
|
|
790
|
+
path: rel,
|
|
791
|
+
name,
|
|
792
|
+
text: office.text,
|
|
793
|
+
truncated: office.truncated || data.length < stat.size,
|
|
794
|
+
binary: false,
|
|
795
|
+
kind: "text",
|
|
796
|
+
lines: countLines(Buffer.from(office.text)),
|
|
797
|
+
size: stat.size,
|
|
798
|
+
});
|
|
799
|
+
return;
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
catch {
|
|
803
|
+
/* 解析失败(加密/损坏/非预期结构)→ 回落旧的二进制嗅探分支 */
|
|
804
|
+
}
|
|
805
|
+
}
|
|
772
806
|
const kind = previewKind(name);
|
|
773
807
|
// Media previews stream over the /api/file HTTP endpoint, so only
|
|
774
808
|
// metadata is sent here — the raw bytes never touch the socket.
|
|
@@ -926,10 +960,14 @@ export class FilesService {
|
|
|
926
960
|
else {
|
|
927
961
|
wp = { abs: root, rel: "" };
|
|
928
962
|
}
|
|
929
|
-
//
|
|
930
|
-
//
|
|
931
|
-
|
|
932
|
-
const safe =
|
|
963
|
+
// 与 createEntry/renameEntry 同口径(this.sanitizeName):只取 basename,
|
|
964
|
+
// 空 / "." / ".." / Windows 保留名 / 尾点空格一律拒绝 —— 旧的内联替换
|
|
965
|
+
// 会放过 "con.txt"、"a.." 这类「建了删不掉」的名字。
|
|
966
|
+
const safe = this.sanitizeName(name);
|
|
967
|
+
if (!safe) {
|
|
968
|
+
emitErr(`文件名不合法:${name}`, `Invalid file name: ${name}`);
|
|
969
|
+
return;
|
|
970
|
+
}
|
|
933
971
|
const abs = resolve(wp.abs, safe);
|
|
934
972
|
let uploadRel;
|
|
935
973
|
if (absDir) {
|
|
@@ -958,7 +996,14 @@ export class FilesService {
|
|
|
958
996
|
return;
|
|
959
997
|
}
|
|
960
998
|
mkdirSync(wp.abs, { recursive: true });
|
|
961
|
-
|
|
999
|
+
if (existsSync(abs)) {
|
|
1000
|
+
emitErr(`已存在:${uploadRel}`, `Already exists: ${uploadRel}`);
|
|
1001
|
+
return;
|
|
1002
|
+
}
|
|
1003
|
+
// wx:与预检之间存在窗口(TOCTOU),并发同名上传走 EEXIST 落到 catch
|
|
1004
|
+
// 报错 —— 两个上传路径语义统一为「目标已存在拒绝」,绝不静默截断覆盖
|
|
1005
|
+
// (/api/file-transfer/upload 一直是 open(dest, "wx") 拒绝)。
|
|
1006
|
+
writeFileSync(abs, buf, { flag: "wx" });
|
|
962
1007
|
this.host.emit({
|
|
963
1008
|
type: "notice",
|
|
964
1009
|
level: "info",
|
|
@@ -1296,30 +1341,32 @@ export class FilesService {
|
|
|
1296
1341
|
try {
|
|
1297
1342
|
const fsp = await import("node:fs/promises");
|
|
1298
1343
|
const { dirname } = await import("node:path");
|
|
1299
|
-
const
|
|
1344
|
+
const trimmed = path.trim();
|
|
1345
|
+
const t = !trimmed ? { abs: resolve(this.host.getCwd()) } : this.resolveOpTarget(trimmed);
|
|
1300
1346
|
if (!t) {
|
|
1301
1347
|
err("此处不可定位:" + path, "Cannot reveal here: " + path);
|
|
1302
1348
|
return;
|
|
1303
1349
|
}
|
|
1304
|
-
const
|
|
1350
|
+
const abs = t.abs;
|
|
1351
|
+
const st = await fsp.stat(abs).catch(() => null);
|
|
1305
1352
|
if (!st) {
|
|
1306
1353
|
err("文件不存在:" + path, "Not found: " + path);
|
|
1307
1354
|
return;
|
|
1308
1355
|
}
|
|
1309
1356
|
const isDir = st.isDirectory();
|
|
1310
|
-
const segs =
|
|
1311
|
-
const base = segs[segs.length - 1] ??
|
|
1357
|
+
const segs = trimmed.split("/");
|
|
1358
|
+
const base = !trimmed ? t.abs.split(/[\\/]/).pop() || t.abs : (segs[segs.length - 1] ?? trimmed);
|
|
1312
1359
|
if (process.platform === "win32") {
|
|
1313
1360
|
// /select, 与路径分两个 argv 传(explorer 对此格式稳定支持,路径含空格也无碍)。
|
|
1314
1361
|
// 目录传 /n, 强制打开新窗口,防止若该目录已在后台打开时被 Windows 静默复用且因反抢焦点机制不置顶。
|
|
1315
|
-
await this.spawnDetached("explorer.exe", isDir ? ["/n,",
|
|
1362
|
+
await this.spawnDetached("explorer.exe", isDir ? ["/n,", abs] : ["/select,", abs], "已在资源管理器中显示:" + base, "Revealed in File Explorer: " + base);
|
|
1316
1363
|
}
|
|
1317
1364
|
else if (process.platform === "darwin") {
|
|
1318
|
-
await this.spawnDetached("open", isDir ? [
|
|
1365
|
+
await this.spawnDetached("open", isDir ? [abs] : ["-R", abs], "已在访达中显示:" + base, "Revealed in Finder: " + base);
|
|
1319
1366
|
}
|
|
1320
1367
|
else {
|
|
1321
1368
|
// Linux 无统一选中语义:打开其父目录(目录则打开自身)。
|
|
1322
|
-
await this.spawnDetached("xdg-open", [isDir ?
|
|
1369
|
+
await this.spawnDetached("xdg-open", [isDir ? abs : dirname(abs)], "已打开所在目录:" + base, "Opened containing folder: " + base);
|
|
1323
1370
|
}
|
|
1324
1371
|
}
|
|
1325
1372
|
catch (e) {
|