@henlii/pidance 0.2.5 → 0.2.7
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/.next/BUILD_ID +1 -1
- package/.next/app-path-routes-manifest.json +7 -6
- package/.next/build-manifest.json +3 -3
- package/.next/prerender-manifest.json +3 -3
- package/.next/required-server-files.json +4 -2
- package/.next/routes-manifest.json +6 -0
- package/.next/server/app/_global-error/page.js +3 -3
- package/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/.next/server/app/_global-error.html +1 -1
- package/.next/server/app/_global-error.rsc +1 -1
- package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/_not-found/page.js +2 -2
- package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/server/app/_not-found.html +1 -1
- package/.next/server/app/_not-found.rsc +3 -3
- package/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
- package/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_index.segment.rsc +3 -3
- package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/api/about/route.js +1 -1
- package/.next/server/app/api/agent/[id]/bash-output/route.js +1 -1
- package/.next/server/app/api/agent/[id]/events/route.js +2 -2
- package/.next/server/app/api/agent/[id]/route.js +1 -1
- package/.next/server/app/api/agent/new/route.js +1 -1
- package/.next/server/app/api/agent/running/events/route.js +1 -1
- package/.next/server/app/api/agent/running/route.js +1 -1
- package/.next/server/app/api/agent-settings/route.js +2 -2
- package/.next/server/app/api/attachments/route.js +1 -1
- package/.next/server/app/api/auth/all-providers/route.js +1 -1
- package/.next/server/app/api/auth/api-key/[provider]/route.js +1 -1
- package/.next/server/app/api/auth/login/[provider]/route.js +2 -2
- package/.next/server/app/api/auth/logout/[provider]/route.js +1 -1
- package/.next/server/app/api/auth/providers/route.js +1 -1
- package/.next/server/app/api/auth/ui-session/route.js +1 -1
- package/.next/server/app/api/cwd/browse/route.js +1 -1
- package/.next/server/app/api/cwd/validate/route.js +1 -1
- package/.next/server/app/api/default-cwd/route.js +1 -1
- package/.next/server/app/api/file-index/route.js +1 -1
- package/.next/server/app/api/files/[...path]/route.js +2 -2
- package/.next/server/app/api/git/diff/route.js +1 -1
- package/.next/server/app/api/git/status/route.js +1 -1
- package/.next/server/app/api/home/route.js +1 -1
- package/.next/server/app/api/models/route.js +1 -1
- package/.next/server/app/api/models-config/raw/route.js +1 -1
- package/.next/server/app/api/models-config/route.js +1 -1
- package/.next/server/app/api/models-config/test/route.js +1 -1
- package/.next/server/app/api/plugins/route.js +1 -1
- package/.next/server/app/api/plugins/search/route.js +1 -1
- package/.next/server/app/api/plugins/updates/route.js +1 -1
- package/.next/server/app/api/preferences/route.js +1 -1
- package/.next/server/app/api/project-assets/route.js +2 -2
- package/.next/server/app/api/prompts/route.js +1 -1
- package/.next/server/app/api/pty/route.js +3 -0
- package/.next/server/app/api/pty/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/sessions/[id]/archive/route.js +1 -1
- package/.next/server/app/api/sessions/[id]/auto-name/route.js +3 -3
- package/.next/server/app/api/sessions/[id]/command-entry/route.js +1 -1
- package/.next/server/app/api/sessions/[id]/context/route.js +1 -1
- package/.next/server/app/api/sessions/[id]/entries/[entryId]/thinking/route.js +1 -1
- package/.next/server/app/api/sessions/[id]/export/route.js +3 -3
- package/.next/server/app/api/sessions/[id]/info/route.js +1 -1
- package/.next/server/app/api/sessions/[id]/route.js +1 -1
- package/.next/server/app/api/sessions/[id]/state/route.js +1 -1
- package/.next/server/app/api/sessions/[id]/tool-results/[toolCallId]/route.js +1 -1
- package/.next/server/app/api/sessions/route.js +1 -1
- package/.next/server/app/api/sessions/search/route.js +1 -1
- package/.next/server/app/api/settings/raw/route.js +2 -2
- package/.next/server/app/api/skills/check/route.js +1 -1
- package/.next/server/app/api/skills/install/route.js +1 -1
- package/.next/server/app/api/skills/route.js +2 -2
- package/.next/server/app/api/skills/search/route.js +1 -1
- package/.next/server/app/api/skills/update/route.js +1 -1
- package/.next/server/app/api/subagent-runs/route.js +1 -1
- package/.next/server/app/api/update/apply/route.js +3 -3
- package/.next/server/app/api/update/check/route.js +2 -2
- package/.next/server/app/api/worktrees/route.js +1 -1
- package/.next/server/app/icon.png/route.js +1 -1
- package/.next/server/app/index.html +1 -1
- package/.next/server/app/index.rsc +6 -5
- package/.next/server/app/index.segments/__PAGE__.segment.rsc +4 -3
- package/.next/server/app/index.segments/_full.segment.rsc +6 -5
- package/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/.next/server/app/index.segments/_index.segment.rsc +3 -3
- package/.next/server/app/index.segments/_tree.segment.rsc +3 -2
- package/.next/server/app/page.js +22 -6
- package/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/server/app-paths-manifest.json +7 -6
- package/.next/server/chunks/188.js +1 -1
- package/.next/server/chunks/5582.js +1 -1
- package/.next/server/chunks/7594.js +1 -1
- package/.next/server/middleware-build-manifest.js +1 -1
- package/.next/server/pages/404.html +1 -1
- package/.next/server/pages/500.html +1 -1
- package/.next/server/server-reference-manifest.json +1 -1
- package/.next/static/01veEEO0MbroyjY72tWyr/_buildManifest.js +1 -0
- package/.next/static/chunks/7874-4f48b670898adf75.js +1 -0
- package/.next/static/chunks/9780-96bbb56af7ed70f1.js +1 -0
- package/.next/static/chunks/app/_global-error/page-212bed9d7023fb3f.js +1 -0
- package/.next/static/chunks/app/api/about/route-212bed9d7023fb3f.js +1 -0
- package/.next/static/chunks/app/api/agent/[id]/bash-output/route-212bed9d7023fb3f.js +1 -0
- package/.next/static/chunks/app/api/agent/[id]/events/route-212bed9d7023fb3f.js +1 -0
- package/.next/static/chunks/app/api/agent/[id]/route-212bed9d7023fb3f.js +1 -0
- package/.next/static/chunks/app/api/agent/new/route-212bed9d7023fb3f.js +1 -0
- package/.next/static/chunks/app/api/agent/running/events/route-212bed9d7023fb3f.js +1 -0
- package/.next/static/chunks/app/api/agent/running/route-212bed9d7023fb3f.js +1 -0
- package/.next/static/chunks/app/api/agent-settings/route-212bed9d7023fb3f.js +1 -0
- package/.next/static/chunks/app/api/attachments/route-212bed9d7023fb3f.js +1 -0
- package/.next/static/chunks/app/api/auth/all-providers/route-212bed9d7023fb3f.js +1 -0
- package/.next/static/chunks/app/api/auth/api-key/[provider]/route-212bed9d7023fb3f.js +1 -0
- package/.next/static/chunks/app/api/auth/login/[provider]/route-212bed9d7023fb3f.js +1 -0
- package/.next/static/chunks/app/api/auth/logout/[provider]/route-212bed9d7023fb3f.js +1 -0
- package/.next/static/chunks/app/api/auth/providers/route-212bed9d7023fb3f.js +1 -0
- package/.next/static/chunks/app/api/auth/ui-session/route-212bed9d7023fb3f.js +1 -0
- package/.next/static/chunks/app/api/cwd/browse/route-212bed9d7023fb3f.js +1 -0
- package/.next/static/chunks/app/api/cwd/validate/route-212bed9d7023fb3f.js +1 -0
- package/.next/static/chunks/app/api/default-cwd/route-212bed9d7023fb3f.js +1 -0
- package/.next/static/chunks/app/api/file-index/route-212bed9d7023fb3f.js +1 -0
- package/.next/static/chunks/app/api/files/[...path]/route-212bed9d7023fb3f.js +1 -0
- package/.next/static/chunks/app/api/git/diff/route-212bed9d7023fb3f.js +1 -0
- package/.next/static/chunks/app/api/git/status/route-212bed9d7023fb3f.js +1 -0
- package/.next/static/chunks/app/api/home/route-212bed9d7023fb3f.js +1 -0
- package/.next/static/chunks/app/api/models/route-212bed9d7023fb3f.js +1 -0
- package/.next/static/chunks/app/api/models-config/raw/route-212bed9d7023fb3f.js +1 -0
- package/.next/static/chunks/app/api/models-config/route-212bed9d7023fb3f.js +1 -0
- package/.next/static/chunks/app/api/models-config/test/route-212bed9d7023fb3f.js +1 -0
- package/.next/static/chunks/app/api/plugins/route-212bed9d7023fb3f.js +1 -0
- package/.next/static/chunks/app/api/plugins/search/route-212bed9d7023fb3f.js +1 -0
- package/.next/static/chunks/app/api/plugins/updates/route-212bed9d7023fb3f.js +1 -0
- package/.next/static/chunks/app/api/preferences/route-212bed9d7023fb3f.js +1 -0
- package/.next/static/chunks/app/api/project-assets/route-212bed9d7023fb3f.js +1 -0
- package/.next/static/chunks/app/api/prompts/route-212bed9d7023fb3f.js +1 -0
- package/.next/static/chunks/app/api/pty/route-212bed9d7023fb3f.js +1 -0
- package/.next/static/chunks/app/api/sessions/[id]/archive/route-212bed9d7023fb3f.js +1 -0
- package/.next/static/chunks/app/api/sessions/[id]/auto-name/route-212bed9d7023fb3f.js +1 -0
- package/.next/static/chunks/app/api/sessions/[id]/command-entry/route-212bed9d7023fb3f.js +1 -0
- package/.next/static/chunks/app/api/sessions/[id]/context/route-212bed9d7023fb3f.js +1 -0
- package/.next/static/chunks/app/api/sessions/[id]/entries/[entryId]/thinking/route-212bed9d7023fb3f.js +1 -0
- package/.next/static/chunks/app/api/sessions/[id]/export/route-212bed9d7023fb3f.js +1 -0
- package/.next/static/chunks/app/api/sessions/[id]/info/route-212bed9d7023fb3f.js +1 -0
- package/.next/static/chunks/app/api/sessions/[id]/route-212bed9d7023fb3f.js +1 -0
- package/.next/static/chunks/app/api/sessions/[id]/state/route-212bed9d7023fb3f.js +1 -0
- package/.next/static/chunks/app/api/sessions/[id]/tool-results/[toolCallId]/route-212bed9d7023fb3f.js +1 -0
- package/.next/static/chunks/app/api/sessions/route-212bed9d7023fb3f.js +1 -0
- package/.next/static/chunks/app/api/sessions/search/route-212bed9d7023fb3f.js +1 -0
- package/.next/static/chunks/app/api/settings/raw/route-212bed9d7023fb3f.js +1 -0
- package/.next/static/chunks/app/api/skills/check/route-212bed9d7023fb3f.js +1 -0
- package/.next/static/chunks/app/api/skills/install/route-212bed9d7023fb3f.js +1 -0
- package/.next/static/chunks/app/api/skills/route-212bed9d7023fb3f.js +1 -0
- package/.next/static/chunks/app/api/skills/search/route-212bed9d7023fb3f.js +1 -0
- package/.next/static/chunks/app/api/skills/update/route-212bed9d7023fb3f.js +1 -0
- package/.next/static/chunks/app/api/subagent-runs/route-212bed9d7023fb3f.js +1 -0
- package/.next/static/chunks/app/api/update/apply/route-212bed9d7023fb3f.js +1 -0
- package/.next/static/chunks/app/api/update/check/route-212bed9d7023fb3f.js +1 -0
- package/.next/static/chunks/app/api/worktrees/route-212bed9d7023fb3f.js +1 -0
- package/.next/static/chunks/app/page-c76a709acf636b23.js +25 -0
- package/.next/static/chunks/e868780c-16e43c5968ccf2bf.js +18 -0
- package/.next/static/chunks/next/dist/client/components/builtin/app-error-212bed9d7023fb3f.js +1 -0
- package/.next/static/chunks/next/dist/client/components/builtin/forbidden-212bed9d7023fb3f.js +1 -0
- package/.next/static/chunks/next/dist/client/components/builtin/not-found-212bed9d7023fb3f.js +1 -0
- package/.next/static/chunks/next/dist/client/components/builtin/unauthorized-212bed9d7023fb3f.js +1 -0
- package/.next/static/chunks/{webpack-9f5e68be126b5e81.js → webpack-d2379fd13c5c78c6.js} +1 -1
- package/.next/static/css/659eccb5db697b76.css +1 -0
- package/.next/static/css/{6faea79e75763a6a.css → b890757d9e0dd456.css} +1 -1
- package/README.md +1 -1
- package/bin/pidance-http-server.js +101 -0
- package/bin/pidance.js +45 -48
- package/bin/pty-manager.cjs +149 -0
- package/bin/pty-worker.js +48 -0
- package/bin/pty-ws.js +271 -0
- package/package.json +13 -3
- package/.next/static/chunks/1-a030cc9570b4428a.js +0 -1
- package/.next/static/chunks/9780-6931a654abc0ac16.js +0 -1
- package/.next/static/chunks/app/_global-error/page-c74291295bcd24a9.js +0 -1
- package/.next/static/chunks/app/api/about/route-c74291295bcd24a9.js +0 -1
- package/.next/static/chunks/app/api/agent/[id]/bash-output/route-c74291295bcd24a9.js +0 -1
- package/.next/static/chunks/app/api/agent/[id]/events/route-c74291295bcd24a9.js +0 -1
- package/.next/static/chunks/app/api/agent/[id]/route-c74291295bcd24a9.js +0 -1
- package/.next/static/chunks/app/api/agent/new/route-c74291295bcd24a9.js +0 -1
- package/.next/static/chunks/app/api/agent/running/events/route-c74291295bcd24a9.js +0 -1
- package/.next/static/chunks/app/api/agent/running/route-c74291295bcd24a9.js +0 -1
- package/.next/static/chunks/app/api/agent-settings/route-c74291295bcd24a9.js +0 -1
- package/.next/static/chunks/app/api/attachments/route-c74291295bcd24a9.js +0 -1
- package/.next/static/chunks/app/api/auth/all-providers/route-c74291295bcd24a9.js +0 -1
- package/.next/static/chunks/app/api/auth/api-key/[provider]/route-c74291295bcd24a9.js +0 -1
- package/.next/static/chunks/app/api/auth/login/[provider]/route-c74291295bcd24a9.js +0 -1
- package/.next/static/chunks/app/api/auth/logout/[provider]/route-c74291295bcd24a9.js +0 -1
- package/.next/static/chunks/app/api/auth/providers/route-c74291295bcd24a9.js +0 -1
- package/.next/static/chunks/app/api/auth/ui-session/route-c74291295bcd24a9.js +0 -1
- package/.next/static/chunks/app/api/cwd/browse/route-c74291295bcd24a9.js +0 -1
- package/.next/static/chunks/app/api/cwd/validate/route-c74291295bcd24a9.js +0 -1
- package/.next/static/chunks/app/api/default-cwd/route-c74291295bcd24a9.js +0 -1
- package/.next/static/chunks/app/api/file-index/route-c74291295bcd24a9.js +0 -1
- package/.next/static/chunks/app/api/files/[...path]/route-c74291295bcd24a9.js +0 -1
- package/.next/static/chunks/app/api/git/diff/route-c74291295bcd24a9.js +0 -1
- package/.next/static/chunks/app/api/git/status/route-c74291295bcd24a9.js +0 -1
- package/.next/static/chunks/app/api/home/route-c74291295bcd24a9.js +0 -1
- package/.next/static/chunks/app/api/models/route-c74291295bcd24a9.js +0 -1
- package/.next/static/chunks/app/api/models-config/raw/route-c74291295bcd24a9.js +0 -1
- package/.next/static/chunks/app/api/models-config/route-c74291295bcd24a9.js +0 -1
- package/.next/static/chunks/app/api/models-config/test/route-c74291295bcd24a9.js +0 -1
- package/.next/static/chunks/app/api/plugins/route-c74291295bcd24a9.js +0 -1
- package/.next/static/chunks/app/api/plugins/search/route-c74291295bcd24a9.js +0 -1
- package/.next/static/chunks/app/api/plugins/updates/route-c74291295bcd24a9.js +0 -1
- package/.next/static/chunks/app/api/preferences/route-c74291295bcd24a9.js +0 -1
- package/.next/static/chunks/app/api/project-assets/route-c74291295bcd24a9.js +0 -1
- package/.next/static/chunks/app/api/prompts/route-c74291295bcd24a9.js +0 -1
- package/.next/static/chunks/app/api/sessions/[id]/archive/route-c74291295bcd24a9.js +0 -1
- package/.next/static/chunks/app/api/sessions/[id]/auto-name/route-c74291295bcd24a9.js +0 -1
- package/.next/static/chunks/app/api/sessions/[id]/command-entry/route-c74291295bcd24a9.js +0 -1
- package/.next/static/chunks/app/api/sessions/[id]/context/route-c74291295bcd24a9.js +0 -1
- package/.next/static/chunks/app/api/sessions/[id]/entries/[entryId]/thinking/route-c74291295bcd24a9.js +0 -1
- package/.next/static/chunks/app/api/sessions/[id]/export/route-c74291295bcd24a9.js +0 -1
- package/.next/static/chunks/app/api/sessions/[id]/info/route-c74291295bcd24a9.js +0 -1
- package/.next/static/chunks/app/api/sessions/[id]/route-c74291295bcd24a9.js +0 -1
- package/.next/static/chunks/app/api/sessions/[id]/state/route-c74291295bcd24a9.js +0 -1
- package/.next/static/chunks/app/api/sessions/[id]/tool-results/[toolCallId]/route-c74291295bcd24a9.js +0 -1
- package/.next/static/chunks/app/api/sessions/route-c74291295bcd24a9.js +0 -1
- package/.next/static/chunks/app/api/sessions/search/route-c74291295bcd24a9.js +0 -1
- package/.next/static/chunks/app/api/settings/raw/route-c74291295bcd24a9.js +0 -1
- package/.next/static/chunks/app/api/skills/check/route-c74291295bcd24a9.js +0 -1
- package/.next/static/chunks/app/api/skills/install/route-c74291295bcd24a9.js +0 -1
- package/.next/static/chunks/app/api/skills/route-c74291295bcd24a9.js +0 -1
- package/.next/static/chunks/app/api/skills/search/route-c74291295bcd24a9.js +0 -1
- package/.next/static/chunks/app/api/skills/update/route-c74291295bcd24a9.js +0 -1
- package/.next/static/chunks/app/api/subagent-runs/route-c74291295bcd24a9.js +0 -1
- package/.next/static/chunks/app/api/update/apply/route-c74291295bcd24a9.js +0 -1
- package/.next/static/chunks/app/api/update/check/route-c74291295bcd24a9.js +0 -1
- package/.next/static/chunks/app/api/worktrees/route-c74291295bcd24a9.js +0 -1
- package/.next/static/chunks/app/page-c7126319d333426f.js +0 -25
- package/.next/static/chunks/next/dist/client/components/builtin/app-error-c74291295bcd24a9.js +0 -1
- package/.next/static/chunks/next/dist/client/components/builtin/forbidden-c74291295bcd24a9.js +0 -1
- package/.next/static/chunks/next/dist/client/components/builtin/not-found-c74291295bcd24a9.js +0 -1
- package/.next/static/chunks/next/dist/client/components/builtin/unauthorized-c74291295bcd24a9.js +0 -1
- package/.next/static/eBSMREOce0GNNfdZMBj6n/_buildManifest.js +0 -1
- /package/.next/static/{eBSMREOce0GNNfdZMBj6n → 01veEEO0MbroyjY72tWyr}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/* eslint-disable @typescript-eslint/no-require-imports */
|
|
3
|
+
|
|
4
|
+
const { existsSync, realpathSync } = require("fs");
|
|
5
|
+
const { WebSocketServer } = require("ws");
|
|
6
|
+
const ptyWss = new WebSocketServer({ noServer: true });
|
|
7
|
+
|
|
8
|
+
function sanitizeEnv(env) {
|
|
9
|
+
const next = { ...env };
|
|
10
|
+
delete next.PI_WEB_PASSWORD;
|
|
11
|
+
delete next.PIDANCE_PASSWORD;
|
|
12
|
+
return next;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function tryLoadNodePty() {
|
|
16
|
+
try {
|
|
17
|
+
const loaded = require("node-pty");
|
|
18
|
+
if (typeof loaded.spawn !== "function") return null;
|
|
19
|
+
return loaded;
|
|
20
|
+
} catch {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function resolvePtyCwd(cwd) {
|
|
26
|
+
if (!existsSync(cwd)) throw new Error("PTY cwd 不存在");
|
|
27
|
+
return realpathSync(cwd);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function startPtySession(options) {
|
|
31
|
+
const spawner = options.pty === undefined ? tryLoadNodePty() : options.pty;
|
|
32
|
+
if (!spawner) throw new Error("node-pty 不可用");
|
|
33
|
+
const cwd = resolvePtyCwd(options.cwd);
|
|
34
|
+
const cols = options.cols && options.cols > 0 ? options.cols : 80;
|
|
35
|
+
const rows = options.rows && options.rows > 0 ? options.rows : 24;
|
|
36
|
+
const shell = process.env.SHELL && process.env.SHELL.length > 0 ? process.env.SHELL : "/bin/bash";
|
|
37
|
+
const proc = spawner.spawn(shell, [], {
|
|
38
|
+
name: "xterm-256color",
|
|
39
|
+
cols,
|
|
40
|
+
rows,
|
|
41
|
+
cwd,
|
|
42
|
+
env: sanitizeEnv(options.env ?? process.env),
|
|
43
|
+
});
|
|
44
|
+
let disposed = false;
|
|
45
|
+
const onChunk = (data) => {
|
|
46
|
+
if (!disposed) {
|
|
47
|
+
const text = typeof data === "string" ? data : data.toString();
|
|
48
|
+
options.onData(text);
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
if (typeof proc.on === "function") proc.on("data", onChunk);
|
|
52
|
+
else if (typeof proc.onData === "function") proc.onData(onChunk);
|
|
53
|
+
const onGone = (e) => {
|
|
54
|
+
if (!disposed) options.onExit(typeof e === "object" && e ? e.exitCode : 0);
|
|
55
|
+
};
|
|
56
|
+
if (typeof proc.on === "function") proc.on("exit", onGone);
|
|
57
|
+
else if (typeof proc.onExit === "function") proc.onExit(onGone);
|
|
58
|
+
return {
|
|
59
|
+
pid: proc.pid,
|
|
60
|
+
write: (data) => {
|
|
61
|
+
if (!disposed) proc.write(data);
|
|
62
|
+
},
|
|
63
|
+
resize: (nextCols, nextRows) => {
|
|
64
|
+
if (!disposed && nextCols > 0 && nextRows > 0) proc.resize(nextCols, nextRows);
|
|
65
|
+
},
|
|
66
|
+
dispose: () => {
|
|
67
|
+
if (disposed) return;
|
|
68
|
+
disposed = true;
|
|
69
|
+
try {
|
|
70
|
+
if (proc.pid > 0) process.kill(-proc.pid, "SIGTERM");
|
|
71
|
+
} catch {
|
|
72
|
+
/* 进程组可能已不在 */
|
|
73
|
+
}
|
|
74
|
+
try {
|
|
75
|
+
proc.kill("SIGTERM");
|
|
76
|
+
} catch {
|
|
77
|
+
/* 已退出 */
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function attachPtyToWebSocket(ws, cwd) {
|
|
84
|
+
const { spawn } = require("child_process");
|
|
85
|
+
const path = require("path");
|
|
86
|
+
const workerPath = path.join(__dirname, "pty-worker.js");
|
|
87
|
+
const child = spawn(process.execPath, [workerPath, cwd, "80", "24"], {
|
|
88
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
89
|
+
env: process.env,
|
|
90
|
+
});
|
|
91
|
+
child.stderr.setEncoding("utf8");
|
|
92
|
+
child.stderr.on("data", (data) => console.warn("[pidance] pty-worker stderr", data));
|
|
93
|
+
child.on("error", (error) => console.warn("[pidance] pty-worker spawn", error.message));
|
|
94
|
+
const session = {
|
|
95
|
+
pid: child.pid,
|
|
96
|
+
write: (data) => child.stdin.write(`${JSON.stringify({ type: "in", d: data })}\n`),
|
|
97
|
+
resize: (cols, rows) => child.stdin.write(`${JSON.stringify({ type: "rs", cols, rows })}\n`),
|
|
98
|
+
dispose: () => {
|
|
99
|
+
try { child.kill("SIGTERM"); } catch { /* ignore */ }
|
|
100
|
+
},
|
|
101
|
+
};
|
|
102
|
+
child.stdout.setEncoding("utf8");
|
|
103
|
+
// pty-worker 输出按 JSON Lines 帧化:{type:"out",d:...},避免与终端原始输出混淆。
|
|
104
|
+
let stdoutBuf = "";
|
|
105
|
+
child.stdout.on("data", (data) => {
|
|
106
|
+
stdoutBuf += typeof data === "string" ? data : Buffer.from(data).toString("utf8");
|
|
107
|
+
let idx;
|
|
108
|
+
while ((idx = stdoutBuf.indexOf("\n")) >= 0) {
|
|
109
|
+
const line = stdoutBuf.slice(0, idx);
|
|
110
|
+
stdoutBuf = stdoutBuf.slice(idx + 1);
|
|
111
|
+
if (!line) continue;
|
|
112
|
+
try {
|
|
113
|
+
const msg = JSON.parse(line);
|
|
114
|
+
if (msg && msg.type === "out" && typeof msg.d === "string") {
|
|
115
|
+
// 统一用 JSON 帧发给浏览器,避免普通输出与控制消息混淆。
|
|
116
|
+
ws.send(JSON.stringify({ type: "out", d: msg.d }));
|
|
117
|
+
}
|
|
118
|
+
} catch { /* 非 JSON 的旧协议输出,尽量原样转发 */ try { ws.send(line); } catch { /* ignore */ } }
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
child.on("exit", (code) => {
|
|
122
|
+
try {
|
|
123
|
+
if (ws.readyState === 1) {
|
|
124
|
+
ws.send(JSON.stringify({ type: "exit", code: code ?? 0 }));
|
|
125
|
+
ws.close();
|
|
126
|
+
}
|
|
127
|
+
} catch { /* ignore */ }
|
|
128
|
+
});
|
|
129
|
+
ws.on("message", (raw) => {
|
|
130
|
+
const text = typeof raw === "string" ? raw : raw.toString();
|
|
131
|
+
let parsed;
|
|
132
|
+
try { parsed = JSON.parse(text); } catch { return; }
|
|
133
|
+
if (parsed && parsed.type === "in" && typeof parsed.d === "string") session.write(parsed.d);
|
|
134
|
+
if (parsed && parsed.type === "rs" && parsed.cols > 0 && parsed.rows > 0) session.resize(parsed.cols, parsed.rows);
|
|
135
|
+
});
|
|
136
|
+
ws.on("close", () => session.dispose());
|
|
137
|
+
ws.on("error", () => session.dispose());
|
|
138
|
+
try { ws.send(JSON.stringify({ type: "out", d: "\r\n[pidance] terminal ready\r\n" })); } catch { /* ignore */ }
|
|
139
|
+
return session;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function completePtyUpgrade(req, socket, head, cwd) {
|
|
143
|
+
ptyWss.handleUpgrade(req, socket, head, (ws) => {
|
|
144
|
+
ptyWss.emit("connection", ws, req);
|
|
145
|
+
attachPtyToWebSocket(ws, cwd);
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
module.exports = { startPtySession, tryLoadNodePty, resolvePtyCwd, attachPtyToWebSocket, completePtyUpgrade };
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/* eslint-disable @typescript-eslint/no-require-imports */
|
|
3
|
+
|
|
4
|
+
const pty = require("node-pty");
|
|
5
|
+
|
|
6
|
+
const cwd = process.argv[2] || process.cwd();
|
|
7
|
+
const cols = Number(process.argv[3]) || 80;
|
|
8
|
+
const rows = Number(process.argv[4]) || 24;
|
|
9
|
+
const shell = process.env.SHELL && process.env.SHELL.length > 0 ? process.env.SHELL : "/bin/bash";
|
|
10
|
+
const env = { ...process.env };
|
|
11
|
+
delete env.PI_WEB_PASSWORD;
|
|
12
|
+
delete env.PIDANCE_PASSWORD;
|
|
13
|
+
|
|
14
|
+
const proc = pty.spawn(shell, [], {
|
|
15
|
+
name: "xterm-256color",
|
|
16
|
+
cols,
|
|
17
|
+
rows,
|
|
18
|
+
cwd,
|
|
19
|
+
env,
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
proc.onData((data) => {
|
|
23
|
+
process.stdout.write(JSON.stringify({ type: "out", d: data }) + "\n");
|
|
24
|
+
});
|
|
25
|
+
proc.onExit((e) => {
|
|
26
|
+
process.exit(e.exitCode ?? 0);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
process.stdin.setEncoding("utf8");
|
|
30
|
+
let buf = "";
|
|
31
|
+
process.stdin.on("data", (chunk) => {
|
|
32
|
+
buf += chunk;
|
|
33
|
+
let idx;
|
|
34
|
+
while ((idx = buf.indexOf("\n")) >= 0) {
|
|
35
|
+
const line = buf.slice(0, idx);
|
|
36
|
+
buf = buf.slice(idx + 1);
|
|
37
|
+
if (!line) continue;
|
|
38
|
+
let msg;
|
|
39
|
+
try { msg = JSON.parse(line); } catch { continue; }
|
|
40
|
+
if (msg.type === "in" && typeof msg.d === "string") proc.write(msg.d);
|
|
41
|
+
if (msg.type === "rs" && msg.cols > 0 && msg.rows > 0) proc.resize(msg.cols, msg.rows);
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
process.on("SIGTERM", () => {
|
|
46
|
+
try { proc.kill("SIGTERM"); } catch { /* ignore */ }
|
|
47
|
+
process.exit(0);
|
|
48
|
+
});
|
package/bin/pty-ws.js
ADDED
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
// Pidance PTY WebSocket upgrade 处理器(发布包自包含:不依赖 lib/ 源码树)。
|
|
4
|
+
// 守卫逻辑与 lib/request-guard.ts / lib/ui-session.ts 保持一致的精简内联版:
|
|
5
|
+
// - Host 白名单(DNS rebinding 防护)
|
|
6
|
+
// - 可选认证:PIDANCE_PASSWORD / PI_WEB_PASSWORD 启用时接受 UI 会话 Cookie(JWT)或 Basic
|
|
7
|
+
// - 兜底:未设置密码时仅放行回环请求(fail-closed)
|
|
8
|
+
// WebSocket 升级不做 CSRF 校验:部分手机浏览器 Origin / sec-fetch 不完整。
|
|
9
|
+
|
|
10
|
+
/* eslint-disable @typescript-eslint/no-require-imports */
|
|
11
|
+
const { existsSync } = require("fs");
|
|
12
|
+
const { homedir } = require("os");
|
|
13
|
+
const { createHash, createHmac, randomBytes, timingSafeEqual } = require("crypto");
|
|
14
|
+
const { isIP } = require("net");
|
|
15
|
+
const fs = require("fs");
|
|
16
|
+
const path = require("path");
|
|
17
|
+
const { tryLoadNodePty, completePtyUpgrade } = require("./pty-manager.cjs");
|
|
18
|
+
|
|
19
|
+
// ── Host 白名单(对齐 lib/request-guard.ts)──────────────────────────────
|
|
20
|
+
|
|
21
|
+
function normalizeHostname(hostname) {
|
|
22
|
+
return (hostname.startsWith("[") && hostname.endsWith("]") ? hostname.slice(1, -1) : hostname)
|
|
23
|
+
.toLowerCase()
|
|
24
|
+
.replace(/\.$/, "");
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function hostnameFromHostHeader(host) {
|
|
28
|
+
if (!host || /[\s/@\\]/.test(host)) return null;
|
|
29
|
+
try {
|
|
30
|
+
const url = new URL(`http://${host}`);
|
|
31
|
+
if (url.username || url.password || url.pathname !== "/" || url.search || url.hash) return null;
|
|
32
|
+
return normalizeHostname(url.hostname);
|
|
33
|
+
} catch {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function allowedHosts(env) {
|
|
39
|
+
return [
|
|
40
|
+
env.PI_WEB_HOSTNAME,
|
|
41
|
+
...(env.PI_WEB_ALLOWED_HOSTS ? String(env.PI_WEB_ALLOWED_HOSTS).split(",") : []),
|
|
42
|
+
]
|
|
43
|
+
.filter((h) => typeof h === "string" && h.trim().length > 0)
|
|
44
|
+
.map((h) => h.trim());
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function isTrustedHost(hostHeader, env) {
|
|
48
|
+
const hostname = hostHeader ? hostnameFromHostHeader(hostHeader) : null;
|
|
49
|
+
if (!hostname) return false;
|
|
50
|
+
if (hostname === "localhost" || hostname.endsWith(".localhost") || isIP(hostname) !== 0) return true;
|
|
51
|
+
const names = allowedHosts(env)
|
|
52
|
+
.map((h) => hostnameFromHostHeader(h))
|
|
53
|
+
.filter((n) => n !== null);
|
|
54
|
+
return names.includes(hostname);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function isLoopbackHost(hostHeader) {
|
|
58
|
+
const hostname = hostHeader ? hostnameFromHostHeader(hostHeader) : null;
|
|
59
|
+
if (!hostname) return false;
|
|
60
|
+
if (hostname === "localhost" || hostname.endsWith(".localhost")) return true;
|
|
61
|
+
const ip = isIP(hostname);
|
|
62
|
+
if (ip === 4) return hostname.startsWith("127.");
|
|
63
|
+
if (ip === 6) return hostname === "::1" || hostname === "0:0:0:0:0:0:0:1";
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// ── 认证(对齐 lib/request-guard.ts + lib/ui-session.ts 精简版)────────────
|
|
68
|
+
|
|
69
|
+
const UI_SESSION_COOKIE_NAME = "pidance_ui_session";
|
|
70
|
+
|
|
71
|
+
function resolvePassword(env) {
|
|
72
|
+
const p =
|
|
73
|
+
env && env.PIDANCE_PASSWORD && env.PIDANCE_PASSWORD.length > 0
|
|
74
|
+
? env.PIDANCE_PASSWORD
|
|
75
|
+
: env && env.PI_WEB_PASSWORD;
|
|
76
|
+
return typeof p === "string" && p.length > 0 ? p : null;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function passwordEnabled(env) {
|
|
80
|
+
return resolvePassword(env) !== null;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function sha256(input) {
|
|
84
|
+
return createHash("sha256").update(input, "utf8").digest();
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function safeEqual(a, b) {
|
|
88
|
+
return timingSafeEqual(sha256(a), sha256(b));
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function checkBasicAuth(headers, env) {
|
|
92
|
+
const password = resolvePassword(env);
|
|
93
|
+
if (!passwordEnabled(env) || !password) return false;
|
|
94
|
+
const auth = headers.authorization;
|
|
95
|
+
if (!auth) return false;
|
|
96
|
+
const match = /^Basic\s+(\S+)$/i.exec(auth);
|
|
97
|
+
if (!match) return false;
|
|
98
|
+
let decoded;
|
|
99
|
+
try {
|
|
100
|
+
const buf = Buffer.from(match[1], "base64");
|
|
101
|
+
if (buf.toString("base64") !== match[1]) return false;
|
|
102
|
+
decoded = new TextDecoder("utf-8", { fatal: true }).decode(buf);
|
|
103
|
+
} catch {
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
const idx = decoded.indexOf(":");
|
|
107
|
+
if (idx === -1) return false;
|
|
108
|
+
return safeEqual(decoded.slice(0, idx), "pi") && safeEqual(decoded.slice(idx + 1), password);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function parseCookieValue(cookieHeader, name) {
|
|
112
|
+
if (!cookieHeader) return null;
|
|
113
|
+
for (const part of String(cookieHeader).split(";")) {
|
|
114
|
+
const idx = part.indexOf("=");
|
|
115
|
+
if (idx <= 0) continue;
|
|
116
|
+
const key = part.slice(0, idx).trim();
|
|
117
|
+
if (key !== name) continue;
|
|
118
|
+
const raw = part.slice(idx + 1).trim();
|
|
119
|
+
try {
|
|
120
|
+
return decodeURIComponent(raw);
|
|
121
|
+
} catch {
|
|
122
|
+
return raw;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function defaultSecretFilePath() {
|
|
129
|
+
const root = process.env.PI_CODING_AGENT_DIR || path.join(homedir(), ".pi", "agent");
|
|
130
|
+
return path.join(root, "pidance-ui-jwt-secret");
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function getOrCreateJwtSecret(env) {
|
|
134
|
+
const fromEnv = env.PIDANCE_UI_JWT_SECRET || env.OPENCODE_JWT_SECRET;
|
|
135
|
+
if (typeof fromEnv === "string" && fromEnv.length > 0) return fromEnv.trim();
|
|
136
|
+
const filePath = defaultSecretFilePath();
|
|
137
|
+
try {
|
|
138
|
+
if (fs.existsSync(filePath)) {
|
|
139
|
+
const existing = fs.readFileSync(filePath, "utf8").trim();
|
|
140
|
+
if (existing) return existing;
|
|
141
|
+
}
|
|
142
|
+
} catch {
|
|
143
|
+
// 读失败则重建
|
|
144
|
+
}
|
|
145
|
+
const secret = randomBytes(32).toString("hex");
|
|
146
|
+
try {
|
|
147
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
148
|
+
fs.writeFileSync(filePath, secret, { mode: 0o600 });
|
|
149
|
+
} catch {
|
|
150
|
+
// 无法落盘时仍返回内存密钥(进程内有效)
|
|
151
|
+
}
|
|
152
|
+
return secret;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function verifyUiSessionJwt(token, secret, nowMs) {
|
|
156
|
+
if (!token || !secret) return false;
|
|
157
|
+
const parts = token.split(".");
|
|
158
|
+
if (parts.length !== 3) return false;
|
|
159
|
+
const [header, payload, sig] = parts;
|
|
160
|
+
const data = `${header}.${payload}`;
|
|
161
|
+
const expected = createHmac("sha256", secret).update(data).digest();
|
|
162
|
+
let actual;
|
|
163
|
+
try {
|
|
164
|
+
actual = Buffer.from(sig, "base64url");
|
|
165
|
+
} catch {
|
|
166
|
+
return false;
|
|
167
|
+
}
|
|
168
|
+
if (actual.length !== expected.length || !timingSafeEqual(actual, expected)) return false;
|
|
169
|
+
try {
|
|
170
|
+
const body = JSON.parse(Buffer.from(payload, "base64url").toString("utf8"));
|
|
171
|
+
if (body.type !== "ui-session") return false;
|
|
172
|
+
if (typeof body.exp !== "number" || body.exp * 1000 <= nowMs) return false;
|
|
173
|
+
return true;
|
|
174
|
+
} catch {
|
|
175
|
+
return false;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function checkUiSessionCookie(headers, env) {
|
|
180
|
+
if (!passwordEnabled(env)) return false;
|
|
181
|
+
const token = parseCookieValue(headers.cookie, UI_SESSION_COOKIE_NAME);
|
|
182
|
+
if (!token) return false;
|
|
183
|
+
return verifyUiSessionJwt(token, getOrCreateJwtSecret(env), Date.now());
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function checkAuthenticated(headers, env) {
|
|
187
|
+
return checkUiSessionCookie(headers, env) || checkBasicAuth(headers, env);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// ── upgrade 处理(原 lib/pty-ws.ts)─────────────────────────────────────
|
|
191
|
+
|
|
192
|
+
function header(req, name) {
|
|
193
|
+
const value = req.headers[name];
|
|
194
|
+
if (typeof value === "string") return value;
|
|
195
|
+
if (Array.isArray(value) && typeof value[0] === "string") return value[0];
|
|
196
|
+
return null;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function ptyGuardHeaders(req) {
|
|
200
|
+
const host = header(req, "host");
|
|
201
|
+
const rawUrl = req.url || "/api/pty";
|
|
202
|
+
const url = `http://${host || "127.0.0.1"}${rawUrl}`;
|
|
203
|
+
let pathname = "/api/pty";
|
|
204
|
+
try {
|
|
205
|
+
pathname = new URL(url).pathname;
|
|
206
|
+
} catch {
|
|
207
|
+
// 用默认
|
|
208
|
+
}
|
|
209
|
+
return {
|
|
210
|
+
host,
|
|
211
|
+
origin: header(req, "origin"),
|
|
212
|
+
secFetchSite: header(req, "sec-fetch-site"),
|
|
213
|
+
secFetchMode: header(req, "sec-fetch-mode"),
|
|
214
|
+
secFetchDest: header(req, "sec-fetch-dest"),
|
|
215
|
+
secFetchUser: header(req, "sec-fetch-user"),
|
|
216
|
+
authorization: header(req, "authorization"),
|
|
217
|
+
cookie: header(req, "cookie"),
|
|
218
|
+
method: "GET",
|
|
219
|
+
url,
|
|
220
|
+
pathname,
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function reject(socket, status, message) {
|
|
225
|
+
try {
|
|
226
|
+
socket.write(`HTTP/1.1 ${status} ${message}\r\nConnection: close\r\nContent-Length: 0\r\n\r\n`);
|
|
227
|
+
} catch {
|
|
228
|
+
// ignore
|
|
229
|
+
}
|
|
230
|
+
try {
|
|
231
|
+
socket.destroy();
|
|
232
|
+
} catch {
|
|
233
|
+
// ignore
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
async function handlePtyUpgrade(req, socket, head) {
|
|
238
|
+
if (!tryLoadNodePty()) {
|
|
239
|
+
reject(socket, 503, "PTY Unavailable");
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
const guardHeaders = ptyGuardHeaders(req);
|
|
243
|
+
if (!guardHeaders.pathname.startsWith("/api/pty")) {
|
|
244
|
+
reject(socket, 404, "Not Found");
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
// WebSocket 升级:部分手机浏览器 Origin / sec-fetch 不完整,CSRF 会误杀。
|
|
248
|
+
// 仍校验 Host 白名单和登录(Cookie / Basic)。
|
|
249
|
+
if (!isTrustedHost(guardHeaders.host, process.env)) {
|
|
250
|
+
reject(socket, 403, "untrusted-host");
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
if (passwordEnabled(process.env)) {
|
|
254
|
+
if (!checkAuthenticated(guardHeaders, process.env)) {
|
|
255
|
+
reject(socket, 401, "auth-required");
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
} else if (!isLoopbackHost(guardHeaders.host)) {
|
|
259
|
+
// fail-closed 兜底:未设密码时仅放行回环请求(与 middleware 一致)。
|
|
260
|
+
reject(socket, 401, "auth-required");
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
const cwd = homedir();
|
|
264
|
+
if (!existsSync(cwd)) {
|
|
265
|
+
reject(socket, 500, "home not found");
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
completePtyUpgrade(req, socket, head, cwd);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
module.exports = { handlePtyUpgrade, ptyGuardHeaders };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@henlii/pidance",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.7",
|
|
4
4
|
"description": "Pidance:面向 Pi coding agent 的开源 Web 客户端",
|
|
5
5
|
"homepage": "https://github.com/henlii/pidance#readme",
|
|
6
6
|
"repository": {
|
|
@@ -44,24 +44,30 @@
|
|
|
44
44
|
"scripts": {
|
|
45
45
|
"dev": "next dev -p 31415",
|
|
46
46
|
"build": "next build --webpack",
|
|
47
|
-
"start": "
|
|
47
|
+
"start": "node bin/pidance.js",
|
|
48
48
|
"lint": "eslint .",
|
|
49
49
|
"typecheck": "tsc --noEmit",
|
|
50
50
|
"test": "node --test lib/*.test.mjs components/*.test.mjs hooks/*.test.mjs bin/*.test.mjs",
|
|
51
51
|
"verify:render-bridge": "node scripts/verify-subagent-render-bridge.mjs",
|
|
52
|
+
"verify:render-bridge:installed": "node scripts/verify-installed-render-bridge.mjs",
|
|
52
53
|
"test:browser": "node --test scripts/browser-regression.test.mjs",
|
|
53
54
|
"check": "npm run typecheck && npm run lint && npm run test",
|
|
54
55
|
"release:audit": "node scripts/audit-release-package.mjs --pre",
|
|
55
56
|
"release:audit:tgz": "node scripts/audit-release-package.mjs --tgz",
|
|
57
|
+
"package:candidate": "node scripts/make-candidate-package.mjs",
|
|
56
58
|
"release:check": "npm run check && npm run build && npm run release:audit"
|
|
57
59
|
},
|
|
58
60
|
"dependencies": {
|
|
59
61
|
"@earendil-works/pi-coding-agent": "0.84.1",
|
|
62
|
+
"@xterm/addon-fit": "^0.11.0",
|
|
63
|
+
"@xterm/xterm": "^6.0.0",
|
|
64
|
+
"jiti": "^2.7.0",
|
|
60
65
|
"lucide-react": "^0.562.0",
|
|
61
66
|
"next": "16.2.11",
|
|
62
67
|
"react": "^19.2.4",
|
|
63
68
|
"react-dom": "^19.2.4",
|
|
64
|
-
"undici": "8.5.0"
|
|
69
|
+
"undici": "8.5.0",
|
|
70
|
+
"ws": "^8.21.3"
|
|
65
71
|
},
|
|
66
72
|
"devDependencies": {
|
|
67
73
|
"@lobehub/icons": "^5.6.0",
|
|
@@ -70,6 +76,7 @@
|
|
|
70
76
|
"@types/react": "^19",
|
|
71
77
|
"@types/react-dom": "^19",
|
|
72
78
|
"@types/react-syntax-highlighter": "^15.5.13",
|
|
79
|
+
"@types/ws": "^8.18.1",
|
|
73
80
|
"eslint": "^9",
|
|
74
81
|
"eslint-config-next": "16.2.11",
|
|
75
82
|
"jiti": "^2.7.0",
|
|
@@ -86,5 +93,8 @@
|
|
|
86
93
|
"remark-math": "^6.0.0",
|
|
87
94
|
"tailwindcss": "^4.2.2",
|
|
88
95
|
"typescript": "^5"
|
|
96
|
+
},
|
|
97
|
+
"optionalDependencies": {
|
|
98
|
+
"node-pty": "^1.1.0"
|
|
89
99
|
}
|
|
90
100
|
}
|