pi-web-ui 0.95.0 → 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 +42 -1
- package/bin/pi-web-ui.mjs +159 -31
- package/dist/server/agent-service.js +880 -240
- package/dist/server/approval-rules.js +56 -10
- package/dist/server/attachment-store.js +21 -4
- package/dist/server/attachments.js +30 -3
- 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 +63 -14
- package/dist/server/compact-context-tool.js +6 -5
- package/dist/server/composer-drafts.js +9 -0
- package/dist/server/control-socket.js +57 -28
- package/dist/server/conversation-read-tool.js +7 -17
- package/dist/server/dangling-tools.js +72 -5
- package/dist/server/delegate-task.js +25 -28
- package/dist/server/dsh/dsh-agent-service.js +19 -6
- package/dist/server/edit-soft-tool.js +14 -11
- package/dist/server/eval-tool.js +67 -14
- package/dist/server/file-archives.js +16 -5
- package/dist/server/files-service.js +30 -11
- package/dist/server/goal-service.js +141 -19
- package/dist/server/hashline-engine.js +59 -21
- package/dist/server/host-guard.js +94 -0
- package/dist/server/i18n.js +3 -3
- package/dist/server/index.js +306 -78
- package/dist/server/lsp-tool.js +752 -60
- package/dist/server/mcp-bridge.js +47 -3
- package/dist/server/model-admin.js +94 -34
- package/dist/server/office-parse.js +96 -30
- package/dist/server/patch-tool.js +9 -15
- package/dist/server/permission-preset.js +25 -0
- package/dist/server/plan-manager.js +11 -1
- 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-llm.js +71 -65
- package/dist/server/plugin-project.js +70 -2
- package/dist/server/plugin-updater.js +115 -12
- package/dist/server/plugins.js +205 -44
- package/dist/server/present-files-tool.js +9 -12
- package/dist/server/process-utils.js +4 -0
- 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/sdk-origin.js +18 -2
- package/dist/server/serialize.js +108 -21
- package/dist/server/settings-service.js +4 -2
- package/dist/server/skill-tool.js +5 -7
- package/dist/server/subagent-templates.js +51 -0
- package/dist/server/subagents.js +48 -72
- package/dist/server/terminals.js +181 -69
- package/dist/server/tool-manager.js +24 -7
- 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/ws-client-id.js +29 -0
- package/dist/server/ws-pending-queue.js +60 -0
- package/extensions/webui.ts +60 -2
- package/package.json +4 -2
- package/plugins/catalog.json +20 -0
- package/themes/aetheris.css +457 -0
- package/themes/zhupi-dark.css +658 -0
- package/themes/zhupi.css +711 -0
- package/web/dist/assets/{TerminalPanel-DlGBF2MD.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-BXnCQL9p.js +0 -364
- package/web/dist/assets/index-RpIGFyi_.css +0 -1
package/dist/server/uploads.js
CHANGED
|
@@ -23,11 +23,26 @@ export function uploadRetentionDays() {
|
|
|
23
23
|
const v = Number(process.env.PI_WEB_UPLOAD_RETENTION_DAYS);
|
|
24
24
|
return Number.isFinite(v) && v >= 0 ? v : 14;
|
|
25
25
|
}
|
|
26
|
-
/**
|
|
26
|
+
/** clientId 白名单:clientId 来自浏览器自报(ws hello),却直接拼进上传目录
|
|
27
|
+
* 路径 —— 只允许安全的单段字符(字母数字、:、_、-,1-128 长),否则可
|
|
28
|
+
* "../" 遍历写任意目录。不匹配整条拒绝抛错:绝不能静默换 randomUUID,
|
|
29
|
+
* 上传目录必须与 ws 会话一一对应(编辑重问靠 uploadPath 按同一目录找回)。 */
|
|
30
|
+
export const CLIENT_ID_PATTERN = /^[A-Za-z0-9:_-]{1,128}$/;
|
|
31
|
+
/** Persist an uploaded buffer; returns the absolute path + sanitized display name.
|
|
32
|
+
* clientId 不匹配白名单、displayName 为空 / "." / ".." 时抛错,调用方负责报错。 */
|
|
27
33
|
export function saveUpload(clientId, name, buf, dataDir = resolveDataDir()) {
|
|
34
|
+
if (!CLIENT_ID_PATTERN.test(clientId)) {
|
|
35
|
+
throw new Error(`Invalid upload clientId: ${JSON.stringify(clientId.slice(0, 129))}`);
|
|
36
|
+
}
|
|
37
|
+
// displayName 只取 basename 并拒绝 "." / "..":防 ".." 与绝对路径拼接穿越
|
|
38
|
+
// (最终文件名带 <ts>- 前缀兜底,这里从源头收紧)。
|
|
39
|
+
const base = name.split(/[\\/]/).pop() ?? "";
|
|
40
|
+
if (!base || base === "." || base === "..") {
|
|
41
|
+
throw new Error(`Invalid upload name: ${JSON.stringify(name.slice(0, 129))}`);
|
|
42
|
+
}
|
|
43
|
+
const displayName = base.replace(/[\\/:*?"<>|\x00-\x1f]/g, "_").slice(0, 80) || "file";
|
|
28
44
|
const dir = join(uploadsRoot(dataDir), clientId);
|
|
29
45
|
mkdirSync(dir, { recursive: true });
|
|
30
|
-
const displayName = name.replace(/[\\/:*?"<>|\x00-\x1f]/g, "_").slice(0, 80) || "file";
|
|
31
46
|
const abs = join(dir, `${Date.now()}-${displayName}`);
|
|
32
47
|
writeFileSync(abs, buf);
|
|
33
48
|
return { abs, displayName };
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
// background subagent run, so its runtime must be kept alive across project /
|
|
25
25
|
// conversation switches. See the Chinese block above for the persistence layout
|
|
26
26
|
// and the fail-open vs fail-closed rationale.
|
|
27
|
-
import { readdirSync, readFileSync
|
|
27
|
+
import { readdirSync, readFileSync } from "node:fs";
|
|
28
28
|
import { homedir, tmpdir, userInfo } from "node:os";
|
|
29
29
|
import path from "node:path";
|
|
30
30
|
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
@@ -114,25 +114,32 @@ export function resolveAsyncRunsDir(env = process.env) {
|
|
|
114
114
|
return path.join(resolveTempRoot(env), "async-subagent-runs");
|
|
115
115
|
}
|
|
116
116
|
/**
|
|
117
|
-
*
|
|
118
|
-
*
|
|
119
|
-
*
|
|
117
|
+
* 【已废弃】按 marker mtime 的 24h 孤儿守卫(原 DEFAULT_STALE_ACTIVE_MARKER_MS)
|
|
118
|
+
* 已移除:run 是否终结改由 status.json 的 state 判定(见 hasActiveSubagentRun)。
|
|
119
|
+
* mtime 只能说明「marker 很久没动」,对 >24h 的超长 run 会误杀活跃证据。
|
|
120
120
|
*/
|
|
121
|
-
export const DEFAULT_STALE_ACTIVE_MARKER_MS = 24 * 60 * 60 * 1000;
|
|
122
121
|
/** 活跃 marker 索引目录名,与 pi-subagents 的 ACTIVE_RUN_INDEX_DIR 一致。 */
|
|
123
122
|
const ACTIVE_RUN_INDEX_DIR = ".active-runs";
|
|
124
123
|
/**
|
|
125
124
|
* 磁盘扫描:该会话是否还有活跃(queued/running)的 pi-subagents 异步 run。
|
|
126
|
-
* 任何 I/O /
|
|
125
|
+
* 任何 I/O / 解析错误都按「无证据」处理(fail-open,见文件头说明)。
|
|
127
126
|
* ENOENT(目录/文件尚不存在、marker 刚被清掉)保持静默;其余 I/O 错误
|
|
128
127
|
* console.warn 一次(与 hasPendingWaitSubscription 对齐)。
|
|
128
|
+
*
|
|
129
|
+
* run 是否已终结的廉价判据是 status.json 的 state 字段——它本来就在同一循环
|
|
130
|
+
* 里逐 marker 读取,零额外 I/O。孤儿清理因此只作用于「已终结 run」的遗留
|
|
131
|
+
* marker(终结态在下方无条件判为无证据);曾按 marker mtime 超 24h 一律
|
|
132
|
+
* 跳过的旧守卫已移除:它同样命中「超长 run(>24h 仍在跑)」的活跃 marker,
|
|
133
|
+
* 会把正常运行误杀成孤儿、提前释放运行时,父会话永远等不到 wake。与上游
|
|
134
|
+
* pi-subagents 的 DEFAULT_STALE_TERMINAL_ACTIVE_MARKER_MS 语义对齐——24h
|
|
135
|
+
* 自限只该管 terminal marker,不该管还声称存活的 run。代价:进程崩溃遗留
|
|
136
|
+
* 的 queued/running status 不再随 mtime 过期自动失效,其代价(多保留一个
|
|
137
|
+
* 空闲运行时)远小于误杀超长 run 的静默停滞。
|
|
129
138
|
*/
|
|
130
139
|
export function hasActiveSubagentRun(options) {
|
|
131
140
|
const sessionId = options.sessionId;
|
|
132
141
|
if (!sessionId)
|
|
133
142
|
return false;
|
|
134
|
-
const now = options.now ?? Date.now;
|
|
135
|
-
const staleMarkerMs = options.staleMarkerMs ?? DEFAULT_STALE_ACTIVE_MARKER_MS;
|
|
136
143
|
const warn = options.warn ?? ((message, error) => console.warn(message, error));
|
|
137
144
|
const isNotFound = (error) => typeof error === "object" &&
|
|
138
145
|
error !== null &&
|
|
@@ -152,18 +159,6 @@ export function hasActiveSubagentRun(options) {
|
|
|
152
159
|
for (const runId of markers) {
|
|
153
160
|
if (runId.startsWith("."))
|
|
154
161
|
continue; // 不把隐藏文件当 run 证据
|
|
155
|
-
const markerFile = path.join(indexDir, runId);
|
|
156
|
-
// 孤儿防护:marker 太久没被 touch(崩溃遗留,run 早已不在)→ 无证据。
|
|
157
|
-
try {
|
|
158
|
-
const ageMs = now() - statSync(markerFile).mtimeMs;
|
|
159
|
-
if (ageMs > staleMarkerMs)
|
|
160
|
-
continue;
|
|
161
|
-
}
|
|
162
|
-
catch (error) {
|
|
163
|
-
if (!isNotFound(error))
|
|
164
|
-
warn(`Failed to stat active-run marker '${markerFile}':`, error);
|
|
165
|
-
continue; // ENOENT(刚被清除的竞态)→ 无证据
|
|
166
|
-
}
|
|
167
162
|
let status;
|
|
168
163
|
try {
|
|
169
164
|
status = JSON.parse(readFileSync(path.join(runsDir, runId, "status.json"), "utf-8"));
|
|
@@ -178,8 +173,10 @@ export function hasActiveSubagentRun(options) {
|
|
|
178
173
|
const probe = status;
|
|
179
174
|
if (probe.sessionId !== sessionId)
|
|
180
175
|
continue; // 别的会话的 run
|
|
176
|
+
// 终结态(complete/failed/stopped/…)→ run 已结束:遗留 marker 按孤儿
|
|
177
|
+
// 清理对待,无条件不算证据;只有 queued/running 才是活跃证据。
|
|
181
178
|
if (probe.state !== "queued" && probe.state !== "running")
|
|
182
|
-
continue;
|
|
179
|
+
continue;
|
|
183
180
|
return true;
|
|
184
181
|
}
|
|
185
182
|
return false;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WS hello 的 clientId 校验 —— 独立成模块是为了可单测
|
|
3
|
+
* (`server/index.ts` 起 WS + SDK,不适合在单测里 import)。
|
|
4
|
+
*
|
|
5
|
+
* 背景:hello.clientId 原样进入 service.attach → ClientSession → saveUpload
|
|
6
|
+
* (`<dataDir>/uploads/<clientId>/`,见 attachments.ts / dsh-agent-service.ts),
|
|
7
|
+
* 无校验时外部可传对象/数字/路径穿越串。合法客户端用 uuid(含连字符,在字符集
|
|
8
|
+
* 内);服务端伪客户端是 `scheduler:`/`plugin:` 前缀(冒号在字符集内),但那些
|
|
9
|
+
* 不走 WS hello——收进字符集只是不挡未来的复用,不构成放行面。
|
|
10
|
+
*/
|
|
11
|
+
/** clientId 长度上限:uuid 36 字符的数倍余量,防超长串写盘/进日志。 */
|
|
12
|
+
export const MAX_CLIENT_ID_LEN = 128;
|
|
13
|
+
/** clientId 字符集:uuid(含 -)、伪客户端前缀(含 :)、下划线。 */
|
|
14
|
+
const CLIENT_ID_RE = /^[A-Za-z0-9:_-]+$/;
|
|
15
|
+
/**
|
|
16
|
+
* 校验 hello.clientId:必须是 string、1-128 字符、限定字符集。
|
|
17
|
+
* 合法原样返回;不合法(类型不对/空串/超长/含非法字符)返回 null,
|
|
18
|
+
* 调用方回退 randomUUID()——校验必须在 hello 入口统一做(服务端所有把
|
|
19
|
+
* clientId 落盘/当目录名的路径都从 attach 这一条链来)。
|
|
20
|
+
*/
|
|
21
|
+
export function validateClientId(input) {
|
|
22
|
+
if (typeof input !== "string")
|
|
23
|
+
return null;
|
|
24
|
+
if (input.length < 1 || input.length > MAX_CLIENT_ID_LEN)
|
|
25
|
+
return null;
|
|
26
|
+
if (!CLIENT_ID_RE.test(input))
|
|
27
|
+
return null;
|
|
28
|
+
return input;
|
|
29
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* attach 完成前暂存客户端命令的队列 —— 独立成模块是为了可单测
|
|
3
|
+
* (`server/index.ts` 起 WS + SDK,不适合在单测里 import)。
|
|
4
|
+
*
|
|
5
|
+
* 背景:hello 先回 ready(issue #295)后,attach/插件链就绪前收到的命令一律进
|
|
6
|
+
* pending 队列等重放;attach 挂死(坏挂载/家目录扫描数十秒)或失败保活时队列
|
|
7
|
+
* 原先无上限——失控/恶意连接可在此期间灌消息把内存顶爆。加上限后超限丢最旧并
|
|
8
|
+
* 计数(做法对齐 ws-unknown-types 的「上限 + 溢出计数 + 节流 warn」先例)。
|
|
9
|
+
*/
|
|
10
|
+
/** 队列上限:正常前端在 attach 窗口内只发 get_state 等零星几条,256 绰绰有余。 */
|
|
11
|
+
export const MAX_PENDING_WS_MESSAGES = 256;
|
|
12
|
+
/** 同一进程两次超限告警的最小间隔(防刷屏,对齐 ws-unknown-types 的节流思路)。 */
|
|
13
|
+
export const PENDING_DROP_WARN_INTERVAL_MS = 60_000;
|
|
14
|
+
let lastWarnAt = 0;
|
|
15
|
+
/** 超限丢弃的节流告警:`PENDING_DROP_WARN_INTERVAL_MS` 内最多一条。log 注入便于单测。 */
|
|
16
|
+
export function warnPendingDrop(totalDropped, log) {
|
|
17
|
+
const now = Date.now();
|
|
18
|
+
if (now - lastWarnAt < PENDING_DROP_WARN_INTERVAL_MS)
|
|
19
|
+
return;
|
|
20
|
+
lastWarnAt = now;
|
|
21
|
+
log(`[ws] attach 未完成,命令队列超限丢最旧(累计 ${totalDropped} 条)`);
|
|
22
|
+
}
|
|
23
|
+
/** 单测隔离用。 */
|
|
24
|
+
export function resetPendingDropWarn() {
|
|
25
|
+
lastWarnAt = 0;
|
|
26
|
+
}
|
|
27
|
+
/** attach 前的命令暂存队列:超限丢最旧(shift),丢弃只计数,不阻断新消息入队。 */
|
|
28
|
+
export class PendingCommandQueue {
|
|
29
|
+
cap;
|
|
30
|
+
items = [];
|
|
31
|
+
droppedTotal = 0;
|
|
32
|
+
constructor(cap = MAX_PENDING_WS_MESSAGES) {
|
|
33
|
+
this.cap = cap;
|
|
34
|
+
}
|
|
35
|
+
push(msg) {
|
|
36
|
+
if (this.items.length >= this.cap) {
|
|
37
|
+
this.items.shift();
|
|
38
|
+
this.droppedTotal++;
|
|
39
|
+
warnPendingDrop(this.droppedTotal, (line) => console.warn(line));
|
|
40
|
+
}
|
|
41
|
+
this.items.push(msg);
|
|
42
|
+
}
|
|
43
|
+
/** 取走全部并清空(attach 完成后的重放)。 */
|
|
44
|
+
drain() {
|
|
45
|
+
const out = this.items;
|
|
46
|
+
this.items = [];
|
|
47
|
+
return out;
|
|
48
|
+
}
|
|
49
|
+
/** attach 失败保活 / 连接关闭时清空(用户修好后重发的 get_state 会重新入队)。 */
|
|
50
|
+
clear() {
|
|
51
|
+
this.items = [];
|
|
52
|
+
}
|
|
53
|
+
/** 累计丢弃条数(排障用)。 */
|
|
54
|
+
get dropped() {
|
|
55
|
+
return this.droppedTotal;
|
|
56
|
+
}
|
|
57
|
+
get size() {
|
|
58
|
+
return this.items.length;
|
|
59
|
+
}
|
|
60
|
+
}
|
package/extensions/webui.ts
CHANGED
|
@@ -17,8 +17,9 @@
|
|
|
17
17
|
* - 每个 pi 会话管理一个子进程;session_shutdown 时清理,避免孤儿进程。
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
|
-
import { spawn } from "node:child_process";
|
|
20
|
+
import { spawn, execSync } from "node:child_process";
|
|
21
21
|
import { existsSync } from "node:fs";
|
|
22
|
+
import { homedir } from "node:os";
|
|
22
23
|
import { dirname, join, resolve } from "node:path";
|
|
23
24
|
import { fileURLToPath } from "node:url";
|
|
24
25
|
import net from "node:net";
|
|
@@ -27,7 +28,63 @@ import type { ExtensionAPI, ExtensionCommandContext } from "@earendil-works/pi-c
|
|
|
27
28
|
// 本文件位于 <pkg>/extensions/webui.ts → 包根在上一级
|
|
28
29
|
const PKG_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
|
29
30
|
const SERVER_ENTRY = join(PKG_ROOT, "dist", "server", "index.js");
|
|
30
|
-
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* 解析真实的 node 可执行路径。
|
|
34
|
+
*
|
|
35
|
+
* pi 0.87+ 的 TUI 版本是 Bun 打包的二进制(pi.exe),此时 process.execPath 指向
|
|
36
|
+
* pi.exe 而非 node。用 pi.exe 去 spawn(index.js) 会静默失败(pi.exe 不能当
|
|
37
|
+
* node 执行脚本)。此函数在 Bun 环境或 execPath 非 node 时,从 PATH 和常见安装
|
|
38
|
+
* 位置回退查找真实 node。
|
|
39
|
+
*/
|
|
40
|
+
function resolveNode(): string {
|
|
41
|
+
const base = process.execPath.toLowerCase();
|
|
42
|
+
// 普通 node 直接返回(未被 Bun 接管)
|
|
43
|
+
if (!(process as any).isBun && /(^|[/\\])(node|nodejs)(\.exe)?$/.test(base)) {
|
|
44
|
+
return process.execPath;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const candidates: string[] = [];
|
|
48
|
+
// 1. 从 PATH 里找(跨平台:Windows 用 where,其余用 which)
|
|
49
|
+
try {
|
|
50
|
+
const cmd = process.platform === "win32" ? "where node" : "which node";
|
|
51
|
+
const out = execSync(cmd, { windowsHide: true }).toString().trim();
|
|
52
|
+
for (const line of out.split(/\r?\n/)) {
|
|
53
|
+
const p = line.trim();
|
|
54
|
+
// 过滤掉 pi 自身
|
|
55
|
+
if (p && !/pi[/\\]pi(\.exe)?$/i.test(p)) candidates.push(p);
|
|
56
|
+
}
|
|
57
|
+
} catch {
|
|
58
|
+
/* node 不在 PATH */
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// 2. 常见安装位置兜底(Windows 为主,也兼顾 Unix)
|
|
62
|
+
const home = homedir();
|
|
63
|
+
const extra: string[] = [];
|
|
64
|
+
if (process.env.PNPM_HOME) extra.push(join(process.env.PNPM_HOME, "node.exe"));
|
|
65
|
+
for (const rel of [
|
|
66
|
+
"scoop/apps/nodejs/current/node.exe",
|
|
67
|
+
"AppData/Roaming/nvm/current/node.exe",
|
|
68
|
+
".nvm/versions/node/current/bin/node",
|
|
69
|
+
".local/share/pnpm/node.exe",
|
|
70
|
+
".local/bin/node",
|
|
71
|
+
]) {
|
|
72
|
+
extra.push(join(home, ...rel.split("/")));
|
|
73
|
+
}
|
|
74
|
+
for (const c of extra) {
|
|
75
|
+
if (existsSync(c) && !/pi[/\\]pi(\.exe)?$/i.test(c)) candidates.push(c);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (candidates.length > 0) return candidates[0];
|
|
79
|
+
|
|
80
|
+
console.warn(
|
|
81
|
+
"[webui] 未找到真实 node(当前 process.execPath 指向非 node 进程,疑似 Bun 环境)。" +
|
|
82
|
+
"将退回 process.execPath,子进程可能无法启动。请确保 node 在 PATH 中。",
|
|
83
|
+
);
|
|
84
|
+
return process.execPath;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const NODE = resolveNode();
|
|
31
88
|
|
|
32
89
|
/** 每个会话的服务器子进程 + 元数据 */
|
|
33
90
|
interface RunningServer {
|
|
@@ -161,6 +218,7 @@ export default function (pi: ExtensionAPI): void {
|
|
|
161
218
|
const env = {
|
|
162
219
|
...process.env,
|
|
163
220
|
PORT: String(port),
|
|
221
|
+
PI_WEB_PORT: String(port), // server/index.js 读取 PI_WEB_PORT
|
|
164
222
|
PI_WEB_CWD: cwd,
|
|
165
223
|
...(process.env.PI_WEB_DATA_DIR ? {} : { PI_WEB_DATA_DIR: join(cwd, ".pi-web") }),
|
|
166
224
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-web-ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.96.0",
|
|
4
4
|
"description": "Just open your browser to get all your work done — browser cockpit for AI coding agents (chat, code, files, terminal, Git in one tab)",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": {
|
|
@@ -67,12 +67,13 @@
|
|
|
67
67
|
"test": "vitest run",
|
|
68
68
|
"test:unit": "vitest run",
|
|
69
69
|
"test:smoke": "node tests/run-smoke.mjs",
|
|
70
|
+
"test:smoke:core": "node tests/run-smoke.mjs --core",
|
|
70
71
|
"check:protocol": "node scripts/check-protocol-sync.mjs",
|
|
71
72
|
"i18n:diff": "node scripts/i18n-diff.mjs",
|
|
72
73
|
"release:notes": "node scripts/release-notes.mjs",
|
|
73
74
|
"changelog:i18n": "node scripts/release-notes.mjs --unreleased",
|
|
74
75
|
"test:freeze": "node tests/freeze-test.mjs",
|
|
75
|
-
"start": "node dist/server/index.js",
|
|
76
|
+
"start": "node --import ./dist/server/resolve-global-sdk.js dist/server/index.js",
|
|
76
77
|
"build:desktop": "tsc -p desktop/tsconfig.json",
|
|
77
78
|
"desktop:dev": "npm run build:desktop && electron dist/desktop/main.js",
|
|
78
79
|
"desktop:dist": "npm run build && npm run build:desktop && electron-builder -c desktop/electron-builder.yml",
|
|
@@ -101,6 +102,7 @@
|
|
|
101
102
|
"rehype-highlight": "^7.0.2",
|
|
102
103
|
"rehype-katex": "^7.0.1",
|
|
103
104
|
"rehype-raw": "^7.0.0",
|
|
105
|
+
"rehype-sanitize": "^6.0.0",
|
|
104
106
|
"remark-breaks": "^4.0.0",
|
|
105
107
|
"remark-gfm": "^4.0.1",
|
|
106
108
|
"remark-math": "^6.0.0",
|
package/plugins/catalog.json
CHANGED
|
@@ -138,5 +138,25 @@
|
|
|
138
138
|
"descriptionEn": "Reconnection & service recovery: inspect web service and companion watchdog health, reconnect or restart services in one click (supports multi-service restarts with companion watchdog).",
|
|
139
139
|
"source": "xieweimo/pi-web-ui-reconnect-watchdog/reconnect-plugin",
|
|
140
140
|
"homepage": "https://github.com/xieweimo/pi-web-ui-reconnect-watchdog"
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
"id": "sol-savings",
|
|
144
|
+
"name": "SoL-Pi 状态",
|
|
145
|
+
"icon": "⚡",
|
|
146
|
+
"iconSvg": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polygon points=\"13 2 3 14 12 14 11 22 21 10 12 10 13 2\"/></svg>",
|
|
147
|
+
"description": "SoL-Pi 会话节省统计与规划底栏:实时追踪大工具输出打包(Observation Pack)节省的上下文 Token 与在线计划(Plan)进度,借鉴 pi-sol-plan-footer 在底栏显示。",
|
|
148
|
+
"descriptionEn": "SoL-Pi session savings and plan status in the footer bar: real-time tracking of context tokens saved by Observation Pack and active Plan progress, inspired by pi-sol-plan-footer.",
|
|
149
|
+
"source": "xing-shuyin/pi-web-ui/plugins/sol-savings",
|
|
150
|
+
"homepage": "https://github.com/xing-shuyin/pi-web-ui/tree/main/plugins/sol-savings"
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
"id": "session-migrate",
|
|
154
|
+
"name": "会话迁移",
|
|
155
|
+
"icon": "🔄",
|
|
156
|
+
"iconSvg": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8\"/><path d=\"M21 3v5h-5\"/><path d=\"M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16\"/><path d=\"M8 16H3v5\"/></svg>",
|
|
157
|
+
"description": "扫描 ~/.omp/agent/sessions 与 ~/.pi/agent/sessions 的历史会话,只读预览后一键导入 pi 会话库(issue 286 Phase 1)。",
|
|
158
|
+
"descriptionEn": "Scan ~/.omp/agent/sessions and ~/.pi/agent/sessions history, preview read-only, then import into the pi session store (issue 286 Phase 1).",
|
|
159
|
+
"source": "xing-shuyin/pi-web-ui/plugins/session-migrate",
|
|
160
|
+
"homepage": "https://github.com/xing-shuyin/pi-web-ui/tree/main/plugins/session-migrate"
|
|
141
161
|
}
|
|
142
162
|
]
|