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.
Files changed (100) hide show
  1. package/CHANGELOG.md +91 -2
  2. package/README.md +5 -6
  3. package/README.zh-CN.md +4 -5
  4. package/bin/pi-web-ui.mjs +910 -237
  5. package/dist/server/agent-service.js +2655 -231
  6. package/dist/server/approval-rules.js +596 -0
  7. package/dist/server/attachment-store.js +129 -0
  8. package/dist/server/attachments.js +92 -174
  9. package/dist/server/bg-servers.js +45 -7
  10. package/dist/server/claim-files-tool.js +4 -8
  11. package/dist/server/claim-store.js +4 -2
  12. package/dist/server/client-state.js +67 -14
  13. package/dist/server/compact-context-tool.js +126 -0
  14. package/dist/server/composer-drafts.js +9 -0
  15. package/dist/server/context-budget.js +317 -0
  16. package/dist/server/control-socket.js +57 -28
  17. package/dist/server/conversation-read-tool.js +7 -17
  18. package/dist/server/dangling-tools.js +229 -0
  19. package/dist/server/delegate-task.js +25 -28
  20. package/dist/server/dsh/dsh-agent-service.js +69 -66
  21. package/dist/server/edit-soft-tool.js +84 -16
  22. package/dist/server/eval-tool.js +588 -0
  23. package/dist/server/file-archives.js +16 -5
  24. package/dist/server/files-service.js +65 -18
  25. package/dist/server/goal-service.js +371 -77
  26. package/dist/server/hashline-engine.js +703 -0
  27. package/dist/server/host-guard.js +94 -0
  28. package/dist/server/host-metrics.js +26 -2
  29. package/dist/server/i18n.js +3 -3
  30. package/dist/server/index.js +456 -80
  31. package/dist/server/lsp-tool.js +1371 -0
  32. package/dist/server/mcp-bridge.js +47 -3
  33. package/dist/server/model-admin.js +112 -32
  34. package/dist/server/office-parse.js +375 -0
  35. package/dist/server/patch-tool.js +85 -0
  36. package/dist/server/permission-preset.js +25 -0
  37. package/dist/server/plan-manager.js +114 -0
  38. package/dist/server/plugin-api-catalog.js +296 -0
  39. package/dist/server/plugin-catalog-sync.js +36 -19
  40. package/dist/server/plugin-catalog.js +11 -4
  41. package/dist/server/plugin-facilities.js +92 -21
  42. package/dist/server/plugin-install-spec.js +196 -0
  43. package/dist/server/plugin-installer.js +73 -0
  44. package/dist/server/plugin-llm.js +71 -65
  45. package/dist/server/plugin-manifest-validate.js +305 -0
  46. package/dist/server/plugin-project.js +117 -13
  47. package/dist/server/plugin-tool-guard.js +120 -0
  48. package/dist/server/plugin-updater.js +115 -12
  49. package/dist/server/plugins.js +950 -205
  50. package/dist/server/present-files-tool.js +9 -12
  51. package/dist/server/process-utils.js +16 -6
  52. package/dist/server/prompt-composer.js +9 -0
  53. package/dist/server/protocol-version.js +1 -1
  54. package/dist/server/read-tool.js +69 -30
  55. package/dist/server/resolve-global-sdk.js +30 -16
  56. package/dist/server/schedule-agent-tool.js +12 -15
  57. package/dist/server/scheduler-tasks.js +6 -0
  58. package/dist/server/sdk-origin.js +18 -2
  59. package/dist/server/serialize.js +111 -14
  60. package/dist/server/settings-service.js +112 -3
  61. package/dist/server/skill-tool.js +5 -7
  62. package/dist/server/subagent-templates.js +51 -0
  63. package/dist/server/subagents.js +83 -66
  64. package/dist/server/terminals.js +248 -81
  65. package/dist/server/tool-approval.js +84 -0
  66. package/dist/server/tool-manager.js +237 -12
  67. package/dist/server/tool-overrides.js +59 -0
  68. package/dist/server/update-check.js +28 -1
  69. package/dist/server/uploads.js +17 -2
  70. package/dist/server/wait-subscription-scan.js +18 -21
  71. package/dist/server/workspace-snapshot.js +113 -0
  72. package/dist/server/ws-client-id.js +29 -0
  73. package/dist/server/ws-pending-queue.js +60 -0
  74. package/extensions/webui.ts +60 -2
  75. package/package.json +5 -3
  76. package/plugin-sdk/README.md +25 -0
  77. package/plugin-sdk/index.d.ts +31 -38
  78. package/plugin-sdk/index.mjs +35 -19
  79. package/plugins/catalog.json +40 -0
  80. package/themes/aetheris.css +457 -0
  81. package/themes/ayu-light.css +6 -6
  82. package/themes/catppuccin-latte.css +6 -6
  83. package/themes/claude-code-dark.css +144 -0
  84. package/themes/codex.css +6 -6
  85. package/themes/everforest-light.css +6 -6
  86. package/themes/geist.css +6 -6
  87. package/themes/gruvbox-light.css +6 -6
  88. package/themes/kanagawa-lotus.css +6 -6
  89. package/themes/rose-pine-dawn.css +6 -6
  90. package/themes/solarized-light.css +6 -6
  91. package/themes/vs-code-dark.css +146 -0
  92. package/themes/zhupi-dark.css +658 -0
  93. package/themes/zhupi.css +711 -0
  94. package/web/dist/assets/{TerminalPanel-MVoxpJOA.js → TerminalPanel-DDChcYOh.js} +1 -1
  95. package/web/dist/assets/index-Do9RgJC3.js +366 -0
  96. package/web/dist/assets/index-DryOsILO.css +1 -0
  97. package/web/dist/assets/{markdown-eUQn_o9D.js → markdown-DXwnfD9T.js} +1 -1
  98. package/web/dist/index.html +3 -3
  99. package/web/dist/assets/index-B3S9MxnN.css +0 -1
  100. package/web/dist/assets/index-DVLrHI2E.js +0 -364
@@ -11,6 +11,7 @@ import { existsSync, mkdirSync, readdirSync, readFileSync, rmSync, cpSync, write
11
11
  import { join } from "node:path";
12
12
  import { execFile } from "node:child_process";
13
13
  import { pick } from "./i18n.js";
14
+ import { parseInstallSpec, manifestCandidateUrls } from "./plugin-install-spec.js";
14
15
  const PLUGIN_ID_RE = /^[A-Za-z0-9_-]+$/;
15
16
  /** 保留的备份份数(超出删除最旧的)。 */
16
17
  export const BACKUP_KEEP = 3;
@@ -150,10 +151,66 @@ export async function resolveRemoteSha(spec, exec = execGit) {
150
151
  const sha = res.stdout.match(/^([0-9a-f]{40,64})\s+HEAD/m)?.[1] ?? null;
151
152
  return sha ? sha.slice(0, 12) : null;
152
153
  }
153
- /** 扫描全部已装插件,对比本地 sha 与远端 sha,报告更新状态。 */
154
+ /** 简易数字 semver 比较:>0 代表 a 比 b 新。 */
155
+ export function compareVersions(a, b) {
156
+ const pa = a.split(".").map((n) => parseInt(n, 10) || 0);
157
+ const pb = b.split(".").map((n) => parseInt(n, 10) || 0);
158
+ for (let i = 0; i < 3; i++) {
159
+ const x = pa[i] ?? 0;
160
+ const y = pb[i] ?? 0;
161
+ if (x !== y)
162
+ return x - y;
163
+ }
164
+ return 0;
165
+ }
166
+ /** 判断某个插件是否属于随包维护的内置插件(官方插件)。 */
167
+ export function isBuiltinPlugin(id, source, builtinCatalogPath) {
168
+ if (source && /xing-shuyin\/pi-web-ui\/plugins\//i.test(source))
169
+ return true;
170
+ if (builtinCatalogPath && existsSync(builtinCatalogPath)) {
171
+ try {
172
+ const raw = JSON.parse(readFileSync(builtinCatalogPath, "utf8"));
173
+ if (Array.isArray(raw)) {
174
+ return raw.some((e) => e?.id === id);
175
+ }
176
+ }
177
+ catch {
178
+ /* ignore parse errors */
179
+ }
180
+ }
181
+ return false;
182
+ }
183
+ /** 探测远端 manifest 中的版本号(通过 raw.githubusercontent.com 或注入的 fetcher)。 */
184
+ async function fetchRemoteVersion(source, fetcher) {
185
+ const spec = parseInstallSpec(source);
186
+ const urls = manifestCandidateUrls(spec);
187
+ if (urls.length === 0)
188
+ return null;
189
+ const fetchImpl = fetcher ?? (typeof fetch === "function" ? fetch : null);
190
+ if (!fetchImpl)
191
+ return null;
192
+ for (const url of urls) {
193
+ try {
194
+ const ctrl = new AbortController();
195
+ const timer = setTimeout(() => ctrl.abort(), 6000);
196
+ const res = await fetchImpl(url, { signal: ctrl.signal }).finally(() => clearTimeout(timer));
197
+ if (!res.ok)
198
+ continue;
199
+ const data = (await res.json());
200
+ if (typeof data?.version === "string" && data.version.trim()) {
201
+ return data.version.trim();
202
+ }
203
+ }
204
+ catch {
205
+ // 单个 URL 失败继续尝试下一个候选
206
+ }
207
+ }
208
+ return null;
209
+ }
210
+ /** 扫描全部已装插件,对比本地 sha/version 与远端 sha/version,报告更新状态。 */
154
211
  export async function checkPluginUpdates(dataDir, exec = execGit,
155
212
  /** error 字段文案语言(默认英文);调用方可传 () => getLang() 实现跟随。 */
156
- lang) {
213
+ lang, opts) {
157
214
  const l = lang?.() ?? "en";
158
215
  const pluginsDir = join(dataDir, "plugins");
159
216
  let names = [];
@@ -180,6 +237,42 @@ lang) {
180
237
  catch {
181
238
  localSha = null; // 无 sha 记录 → 保守认为可更新(不知道装了哪个版本)
182
239
  }
240
+ let name;
241
+ let version;
242
+ try {
243
+ const m = JSON.parse(readFileSync(join(dir, "manifest.json"), "utf8"));
244
+ name = m.name;
245
+ version = m.version;
246
+ }
247
+ catch {
248
+ /* 坏 manifest:仍报告 */
249
+ }
250
+ const builtin = isBuiltinPlugin(n, source, opts?.builtinCatalogPath);
251
+ let latestVersion = null;
252
+ // 本地开发模式下,如果宿主包自带 plugins/<id>/manifest.json,可直接读本地最新版本号
253
+ if (opts?.pkgRoot) {
254
+ const localPkgManifest = join(opts.pkgRoot, "plugins", n, "manifest.json");
255
+ if (existsSync(localPkgManifest)) {
256
+ try {
257
+ const rawPkg = JSON.parse(readFileSync(localPkgManifest, "utf8"));
258
+ if (typeof rawPkg?.version === "string" && rawPkg.version.trim()) {
259
+ latestVersion = rawPkg.version.trim();
260
+ }
261
+ }
262
+ catch {
263
+ /* ignore */
264
+ }
265
+ }
266
+ }
267
+ // 若本地未取到最新版本号,则尝试通过 fetcher 探测远端 manifest.json
268
+ if (!latestVersion) {
269
+ try {
270
+ latestVersion = await fetchRemoteVersion(source, opts?.fetcher);
271
+ }
272
+ catch {
273
+ latestVersion = null;
274
+ }
275
+ }
183
276
  let remoteSha = null;
184
277
  let error;
185
278
  try {
@@ -189,27 +282,37 @@ lang) {
189
282
  error = err instanceof Error ? err.message : String(err);
190
283
  remoteSha = null;
191
284
  }
192
- if (!remoteSha && !error)
285
+ if (!remoteSha && !latestVersion && !error)
193
286
  error = pick(l, "无法检查(非 git 源或 git 不可用)", "Cannot check (non-git source or git unavailable)", "pluginupdate.cannot.check");
194
- let name;
195
- let version;
196
- try {
197
- const m = JSON.parse(readFileSync(join(dir, "manifest.json"), "utf8"));
198
- name = m.name;
199
- version = m.version;
287
+ let updatable = false;
288
+ if (latestVersion && version) {
289
+ const cmp = compareVersions(latestVersion, version);
290
+ if (cmp > 0) {
291
+ updatable = true;
292
+ }
293
+ else if (cmp === 0 && remoteSha && localSha) {
294
+ updatable = localSha !== remoteSha;
295
+ }
296
+ else if (cmp === 0 && remoteSha && !localSha) {
297
+ updatable = true;
298
+ }
200
299
  }
201
- catch {
202
- /* 坏 manifest:仍报告 */
300
+ else if (remoteSha) {
301
+ updatable = !localSha || localSha !== remoteSha;
302
+ }
303
+ else if (latestVersion && !version) {
304
+ updatable = true;
203
305
  }
204
- const updatable = !!remoteSha && (!localSha || localSha !== remoteSha);
205
306
  out.push({
206
307
  id: n,
207
308
  name,
208
309
  version,
310
+ latestVersion,
209
311
  source,
210
312
  localSha,
211
313
  remoteSha,
212
314
  updatable,
315
+ builtin,
213
316
  error,
214
317
  });
215
318
  }