pi-web-ui 0.35.1 → 0.36.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/dist/server/agent-service.js +18 -0
- package/dist/server/index.js +3 -0
- package/dist/server/plugins.js +33 -4
- package/package.json +96 -96
|
@@ -1399,6 +1399,10 @@ export class ClientSession {
|
|
|
1399
1399
|
}
|
|
1400
1400
|
/** Set by index.ts: called when /pi-web-ui:quit is invoked. */
|
|
1401
1401
|
onQuit = undefined;
|
|
1402
|
+
/** 本客户端成功切换工作区(set_cwd)后触发,参数为新绝对路径。
|
|
1403
|
+
* attach 时由 AgentService 接到全局 onClientCwdChanged —— 编辑器等
|
|
1404
|
+
* 工作区跟随型插件借此把根目录切到用户当前项目。 */
|
|
1405
|
+
onCwdChanged = undefined;
|
|
1402
1406
|
/** Ask the npm registry for the latest pi-web-ui version and report it. */
|
|
1403
1407
|
async checkUpdate() {
|
|
1404
1408
|
const current = ClientSession.currentAppVersion();
|
|
@@ -2408,6 +2412,13 @@ export class ClientSession {
|
|
|
2408
2412
|
this.conv.promptedSinceActive = false;
|
|
2409
2413
|
this.conv.lastActiveAt = Date.now();
|
|
2410
2414
|
this.cwd = abs;
|
|
2415
|
+
// 工作区跟随型插件(编辑器文件树等)同步切根。
|
|
2416
|
+
try {
|
|
2417
|
+
this.onCwdChanged?.(abs);
|
|
2418
|
+
}
|
|
2419
|
+
catch {
|
|
2420
|
+
/* 钩子异常不影响主流程 */
|
|
2421
|
+
}
|
|
2411
2422
|
// Remember the new workspace (restore target + recent-project entry).
|
|
2412
2423
|
this.stateStore.remember(this.clientId, abs);
|
|
2413
2424
|
void this.pushProjects();
|
|
@@ -2611,6 +2622,9 @@ export class AgentService {
|
|
|
2611
2622
|
stateStore;
|
|
2612
2623
|
/** Set by index.ts: called when /pi-web-ui:quit is invoked. */
|
|
2613
2624
|
onQuit = undefined;
|
|
2625
|
+
/** 任意客户端成功切换工作区后触发(新绝对路径)。index.ts 接到
|
|
2626
|
+
* PluginManager.notifyCwd,让插件宿主的 host.cwd 实时跟随当前项目。 */
|
|
2627
|
+
onClientCwdChanged = undefined;
|
|
2614
2628
|
constructor(cwd, stateFile) {
|
|
2615
2629
|
this.cwd = cwd;
|
|
2616
2630
|
this.stateStore = new ClientStateStore(stateFile);
|
|
@@ -2726,6 +2740,10 @@ export class AgentService {
|
|
|
2726
2740
|
cs.onToolEvent = this.onToolEvent;
|
|
2727
2741
|
cs.pluginToolsProvider = this.pluginToolsProvider;
|
|
2728
2742
|
cs.isQuiesced = () => this.quiesced;
|
|
2743
|
+
// 插件宿主工作区跟随:初次接入也同步一次(恢复的 lastCwd 可能≠服务启动目录),
|
|
2744
|
+
// notifyCwd 幂等去重;此后 set_cwd 成功时由 cs.onCwdChanged 继续驱动。
|
|
2745
|
+
cs.onCwdChanged = (abs) => this.onClientCwdChanged?.(abs);
|
|
2746
|
+
this.onClientCwdChanged?.(cs.cwd);
|
|
2729
2747
|
return cs;
|
|
2730
2748
|
}
|
|
2731
2749
|
/** 插件 AI 工具集合变化(注册/注销)时由 index.ts 触发:推送到所有客户端的全部会话。 */
|
package/dist/server/index.js
CHANGED
|
@@ -374,6 +374,9 @@ service.onToolEvent = (ev) => pluginMgr.emitToolEvent(ev);
|
|
|
374
374
|
// 变化时动态注入/移除已有会话。
|
|
375
375
|
service.pluginToolsProvider = () => pluginMgr.getAgentTools();
|
|
376
376
|
pluginMgr.onAgentToolsChanged = () => service.applyPluginAgentTools();
|
|
377
|
+
// 插件宿主工作区实时跟随当前项目:任意客户端 set_cwd 成功后同步给
|
|
378
|
+
// PluginManager,编辑器等工作区跟随型插件随即切根(详见 plugins.ts notifyCwd)。
|
|
379
|
+
service.onClientCwdChanged = (cwd) => pluginMgr.notifyCwd(cwd);
|
|
377
380
|
// ---------------------------------------------------------------------------
|
|
378
381
|
// Self-update
|
|
379
382
|
// ---------------------------------------------------------------------------
|
package/dist/server/plugins.js
CHANGED
|
@@ -24,7 +24,6 @@ import { pathToFileURL } from "node:url";
|
|
|
24
24
|
const ID_RE = /^[A-Za-z0-9_-]+$/;
|
|
25
25
|
export class PluginManager {
|
|
26
26
|
dataDir;
|
|
27
|
-
cwd;
|
|
28
27
|
loaded = new Map();
|
|
29
28
|
/** 已 import 过但无入口/失败的目录——避免重复 import 与重复报错。 */
|
|
30
29
|
attempted = new Set();
|
|
@@ -36,9 +35,29 @@ export class PluginManager {
|
|
|
36
35
|
onAgentToolsChanged = undefined;
|
|
37
36
|
/** 服务端重载纪元:每次 reload() +1,前端用作 import 缓存击穿参数。 */
|
|
38
37
|
epochCounter = 0;
|
|
38
|
+
/** 当前全局工作区(host.cwd 的背后存储)——随 notifyCwd 更新。 */
|
|
39
|
+
cwdValue;
|
|
39
40
|
constructor(dataDir, cwd) {
|
|
40
41
|
this.dataDir = dataDir;
|
|
41
|
-
this.
|
|
42
|
+
this.cwdValue = resolve(cwd);
|
|
43
|
+
}
|
|
44
|
+
/** index.ts 在客户端 set_cwd 成功后调用:更新全局工作区并扇出给
|
|
45
|
+
* 所有已激活插件的 onCwdChange 钩子(异常隔离,不炸主进程)。 */
|
|
46
|
+
notifyCwd(next) {
|
|
47
|
+
const abs = resolve(next);
|
|
48
|
+
if (abs === this.cwdValue)
|
|
49
|
+
return; // 幂等:重复通知/同路径 no-op
|
|
50
|
+
this.cwdValue = abs;
|
|
51
|
+
for (const [id, p] of this.loaded) {
|
|
52
|
+
for (const h of p.cwdHandlers) {
|
|
53
|
+
try {
|
|
54
|
+
h(abs);
|
|
55
|
+
}
|
|
56
|
+
catch (err) {
|
|
57
|
+
console.error(`[plugin:${id}] cwd-change handler failed:`, err);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
42
61
|
}
|
|
43
62
|
get pluginsDir() {
|
|
44
63
|
return join(this.dataDir, "plugins");
|
|
@@ -314,8 +333,10 @@ export class PluginManager {
|
|
|
314
333
|
this.messageHandlers.set(info.id, handlers);
|
|
315
334
|
const toolHandlers = new Set();
|
|
316
335
|
const attachHandlers = new Set();
|
|
336
|
+
const cwdHandlers = new Set();
|
|
317
337
|
const unregisterTools = [];
|
|
318
|
-
const p = { info, toolHandlers, attachHandlers };
|
|
338
|
+
const p = { info, toolHandlers, attachHandlers, cwdHandlers };
|
|
339
|
+
const self = this; // 对象字面量 getter 里不能用插件宿主的 this
|
|
319
340
|
const host = {
|
|
320
341
|
broadcast: (payload) => this.broadcast(info.id, payload),
|
|
321
342
|
notify: (level, text) => this.notifyAll(level, text),
|
|
@@ -332,6 +353,10 @@ export class PluginManager {
|
|
|
332
353
|
attachHandlers.add(h);
|
|
333
354
|
return () => attachHandlers.delete(h);
|
|
334
355
|
},
|
|
356
|
+
onCwdChange: (h) => {
|
|
357
|
+
cwdHandlers.add(h);
|
|
358
|
+
return () => cwdHandlers.delete(h);
|
|
359
|
+
},
|
|
335
360
|
// 包一层:插件反激活时自动注销它注册的全部 AI 工具,不留悬挂项。
|
|
336
361
|
registerAgentTool: (tool) => {
|
|
337
362
|
const off = this.registerAgentTool(info.id, tool);
|
|
@@ -345,7 +370,9 @@ export class PluginManager {
|
|
|
345
370
|
},
|
|
346
371
|
dir,
|
|
347
372
|
dataDir: this.dataDir,
|
|
348
|
-
cwd
|
|
373
|
+
get cwd() {
|
|
374
|
+
return self.cwdValue;
|
|
375
|
+
},
|
|
349
376
|
log: (...args) => console.log(`[plugin:${info.id}]`, ...args),
|
|
350
377
|
};
|
|
351
378
|
try {
|
|
@@ -358,6 +385,7 @@ export class PluginManager {
|
|
|
358
385
|
deactivate: typeof ret === "function" ? ret : undefined,
|
|
359
386
|
toolHandlers,
|
|
360
387
|
attachHandlers,
|
|
388
|
+
cwdHandlers,
|
|
361
389
|
agentToolUnsubscribers: unregisterTools,
|
|
362
390
|
});
|
|
363
391
|
console.log(`[plugin:${info.id}] activated (v${info.version ?? "?"})`);
|
|
@@ -367,6 +395,7 @@ export class PluginManager {
|
|
|
367
395
|
info: { ...info, error: err.message },
|
|
368
396
|
toolHandlers,
|
|
369
397
|
attachHandlers,
|
|
398
|
+
cwdHandlers,
|
|
370
399
|
});
|
|
371
400
|
console.error(`[plugin:${info.id}] activate failed:`, err);
|
|
372
401
|
}
|
package/package.json
CHANGED
|
@@ -1,96 +1,96 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "pi-web-ui",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Web chat interface for the pi coding agent, powered by the pi SDK (@earendil-works/pi-coding-agent) — one-command run, Docker/systemd/launchd deployable",
|
|
5
|
-
"license": "MIT",
|
|
6
|
-
"type": "module",
|
|
7
|
-
"bin": {
|
|
8
|
-
"pi-web-ui": "bin/pi-web-ui.mjs"
|
|
9
|
-
},
|
|
10
|
-
"main": "dist/server/index.js",
|
|
11
|
-
"files": [
|
|
12
|
-
"bin/",
|
|
13
|
-
"dist/",
|
|
14
|
-
"web/dist/",
|
|
15
|
-
"web/public/",
|
|
16
|
-
"themes/",
|
|
17
|
-
"deploy/",
|
|
18
|
-
"extensions/",
|
|
19
|
-
"README.md",
|
|
20
|
-
"LICENSE"
|
|
21
|
-
],
|
|
22
|
-
"keywords": [
|
|
23
|
-
"pi",
|
|
24
|
-
"pi-package",
|
|
25
|
-
"coding-agent",
|
|
26
|
-
"ai",
|
|
27
|
-
"chat",
|
|
28
|
-
"web-ui",
|
|
29
|
-
"terminal",
|
|
30
|
-
"llm"
|
|
31
|
-
],
|
|
32
|
-
"repository": {
|
|
33
|
-
"type": "git",
|
|
34
|
-
"url": "git+https://github.com/xing-shuyin/pi-web-ui.git"
|
|
35
|
-
},
|
|
36
|
-
"pi": {
|
|
37
|
-
"extensions": [
|
|
38
|
-
"./extensions"
|
|
39
|
-
]
|
|
40
|
-
},
|
|
41
|
-
"engines": {
|
|
42
|
-
"node": ">=22.19.0"
|
|
43
|
-
},
|
|
44
|
-
"scripts": {
|
|
45
|
-
"prepublishOnly": "npm run build",
|
|
46
|
-
"dev": "concurrently -k -n server,web -c blue,green \"npm:dev:server\" \"npm:dev:web\"",
|
|
47
|
-
"dev:server": "cross-env PORT=8788 PI_WEB_ALLOW_ORIGINS=http://localhost:5173,http://127.0.0.1:5173 node --watch --import tsx server/index.ts",
|
|
48
|
-
"dev:web": "vite --config web/vite.config.ts",
|
|
49
|
-
"build": "npm run build:web && npm run build:server",
|
|
50
|
-
"build:web": "vite build --config web/vite.config.ts",
|
|
51
|
-
"build:server": "tsc -p tsconfig.server.json",
|
|
52
|
-
"typecheck": "tsc -p tsconfig.server.json --noEmit && tsc -p web/tsconfig.json --noEmit && tsc -p tsconfig.tests.json --noEmit",
|
|
53
|
-
"test": "vitest run",
|
|
54
|
-
"test:unit": "vitest run",
|
|
55
|
-
"test:smoke": "node tests/run-smoke.mjs",
|
|
56
|
-
"check:protocol": "node scripts/check-protocol-sync.mjs",
|
|
57
|
-
"test:freeze": "node tests/freeze-test.mjs",
|
|
58
|
-
"start": "node dist/server/index.js"
|
|
59
|
-
},
|
|
60
|
-
"dependencies": {
|
|
61
|
-
"@earendil-works/pi-coding-agent": "^0.84.2",
|
|
62
|
-
"@xterm/addon-fit": "^0.11.0",
|
|
63
|
-
"@xterm/xterm": "^6.0.0",
|
|
64
|
-
"compression": "^1.8.1",
|
|
65
|
-
"express": "^4.21.2",
|
|
66
|
-
"highlight.js": "^11.10.0",
|
|
67
|
-
"node-pty": "^1.1.0",
|
|
68
|
-
"react": "^18.3.1",
|
|
69
|
-
"react-dom": "^18.3.1",
|
|
70
|
-
"react-icons": "^5.7.0",
|
|
71
|
-
"react-markdown": "^9.0.1",
|
|
72
|
-
"rehype-highlight": "^7.0.1",
|
|
73
|
-
"remark-gfm": "^4.0.0",
|
|
74
|
-
"typebox": "^1.3.14",
|
|
75
|
-
"ws": "^8.18.0"
|
|
76
|
-
},
|
|
77
|
-
"devDependencies": {
|
|
78
|
-
"@types/compression": "^1.8.1",
|
|
79
|
-
"@types/express": "^4.17.21",
|
|
80
|
-
"@types/node": "^22.10.0",
|
|
81
|
-
"@types/react": "^18.3.12",
|
|
82
|
-
"@types/react-dom": "^18.3.1",
|
|
83
|
-
"@types/ws": "^8.5.13",
|
|
84
|
-
"@vitejs/plugin-react": "^4.3.4",
|
|
85
|
-
"concurrently": "^9.1.0",
|
|
86
|
-
"cross-env": "^10.1.0",
|
|
87
|
-
"playwright-core": "^1.62.1",
|
|
88
|
-
"tsx": "^4.19.2",
|
|
89
|
-
"typescript": "^5.7.2",
|
|
90
|
-
"vite": "^6.0.3",
|
|
91
|
-
"vitest": "^4.1.11"
|
|
92
|
-
},
|
|
93
|
-
"allowScripts": {
|
|
94
|
-
"node-pty@1.1.0": true
|
|
95
|
-
}
|
|
96
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "pi-web-ui",
|
|
3
|
+
"version": "0.36.0",
|
|
4
|
+
"description": "Web chat interface for the pi coding agent, powered by the pi SDK (@earendil-works/pi-coding-agent) — one-command run, Docker/systemd/launchd deployable",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"pi-web-ui": "bin/pi-web-ui.mjs"
|
|
9
|
+
},
|
|
10
|
+
"main": "dist/server/index.js",
|
|
11
|
+
"files": [
|
|
12
|
+
"bin/",
|
|
13
|
+
"dist/",
|
|
14
|
+
"web/dist/",
|
|
15
|
+
"web/public/",
|
|
16
|
+
"themes/",
|
|
17
|
+
"deploy/",
|
|
18
|
+
"extensions/",
|
|
19
|
+
"README.md",
|
|
20
|
+
"LICENSE"
|
|
21
|
+
],
|
|
22
|
+
"keywords": [
|
|
23
|
+
"pi",
|
|
24
|
+
"pi-package",
|
|
25
|
+
"coding-agent",
|
|
26
|
+
"ai",
|
|
27
|
+
"chat",
|
|
28
|
+
"web-ui",
|
|
29
|
+
"terminal",
|
|
30
|
+
"llm"
|
|
31
|
+
],
|
|
32
|
+
"repository": {
|
|
33
|
+
"type": "git",
|
|
34
|
+
"url": "git+https://github.com/xing-shuyin/pi-web-ui.git"
|
|
35
|
+
},
|
|
36
|
+
"pi": {
|
|
37
|
+
"extensions": [
|
|
38
|
+
"./extensions"
|
|
39
|
+
]
|
|
40
|
+
},
|
|
41
|
+
"engines": {
|
|
42
|
+
"node": ">=22.19.0"
|
|
43
|
+
},
|
|
44
|
+
"scripts": {
|
|
45
|
+
"prepublishOnly": "npm run build",
|
|
46
|
+
"dev": "concurrently -k -n server,web -c blue,green \"npm:dev:server\" \"npm:dev:web\"",
|
|
47
|
+
"dev:server": "cross-env PORT=8788 PI_WEB_ALLOW_ORIGINS=http://localhost:5173,http://127.0.0.1:5173 node --watch --import tsx server/index.ts",
|
|
48
|
+
"dev:web": "vite --config web/vite.config.ts",
|
|
49
|
+
"build": "npm run build:web && npm run build:server",
|
|
50
|
+
"build:web": "vite build --config web/vite.config.ts",
|
|
51
|
+
"build:server": "tsc -p tsconfig.server.json",
|
|
52
|
+
"typecheck": "tsc -p tsconfig.server.json --noEmit && tsc -p web/tsconfig.json --noEmit && tsc -p tsconfig.tests.json --noEmit",
|
|
53
|
+
"test": "vitest run",
|
|
54
|
+
"test:unit": "vitest run",
|
|
55
|
+
"test:smoke": "node tests/run-smoke.mjs",
|
|
56
|
+
"check:protocol": "node scripts/check-protocol-sync.mjs",
|
|
57
|
+
"test:freeze": "node tests/freeze-test.mjs",
|
|
58
|
+
"start": "node dist/server/index.js"
|
|
59
|
+
},
|
|
60
|
+
"dependencies": {
|
|
61
|
+
"@earendil-works/pi-coding-agent": "^0.84.2",
|
|
62
|
+
"@xterm/addon-fit": "^0.11.0",
|
|
63
|
+
"@xterm/xterm": "^6.0.0",
|
|
64
|
+
"compression": "^1.8.1",
|
|
65
|
+
"express": "^4.21.2",
|
|
66
|
+
"highlight.js": "^11.10.0",
|
|
67
|
+
"node-pty": "^1.1.0",
|
|
68
|
+
"react": "^18.3.1",
|
|
69
|
+
"react-dom": "^18.3.1",
|
|
70
|
+
"react-icons": "^5.7.0",
|
|
71
|
+
"react-markdown": "^9.0.1",
|
|
72
|
+
"rehype-highlight": "^7.0.1",
|
|
73
|
+
"remark-gfm": "^4.0.0",
|
|
74
|
+
"typebox": "^1.3.14",
|
|
75
|
+
"ws": "^8.18.0"
|
|
76
|
+
},
|
|
77
|
+
"devDependencies": {
|
|
78
|
+
"@types/compression": "^1.8.1",
|
|
79
|
+
"@types/express": "^4.17.21",
|
|
80
|
+
"@types/node": "^22.10.0",
|
|
81
|
+
"@types/react": "^18.3.12",
|
|
82
|
+
"@types/react-dom": "^18.3.1",
|
|
83
|
+
"@types/ws": "^8.5.13",
|
|
84
|
+
"@vitejs/plugin-react": "^4.3.4",
|
|
85
|
+
"concurrently": "^9.1.0",
|
|
86
|
+
"cross-env": "^10.1.0",
|
|
87
|
+
"playwright-core": "^1.62.1",
|
|
88
|
+
"tsx": "^4.19.2",
|
|
89
|
+
"typescript": "^5.7.2",
|
|
90
|
+
"vite": "^6.0.3",
|
|
91
|
+
"vitest": "^4.1.11"
|
|
92
|
+
},
|
|
93
|
+
"allowScripts": {
|
|
94
|
+
"node-pty@1.1.0": true
|
|
95
|
+
}
|
|
96
|
+
}
|