@rexvane/piweb 0.3.14 → 0.3.16
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 +12 -9
- package/.next/build-manifest.json +3 -3
- package/.next/prerender-manifest.json +6 -6
- package/.next/react-loadable-manifest.json +16 -4
- package/.next/required-server-files.js +3 -1
- package/.next/required-server-files.json +3 -1
- package/.next/routes-manifest.json +20 -0
- package/.next/server/app/_global-error/page.js +2 -2
- 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/_tree.segment.rsc +1 -1
- package/.next/server/app/_not-found/page.js +1 -1
- 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 +7 -4
- package/.next/server/app/_not-found.segments/_full.segment.rsc +7 -4
- package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +5 -2
- package/.next/server/app/_not-found.segments/_tree.segment.rsc +5 -2
- package/.next/server/app/api/agent/[id]/events/route.js +2 -2
- package/.next/server/app/api/agent/[id]/events/route.js.nft.json +1 -1
- package/.next/server/app/api/agent/[id]/route.js +1 -1
- package/.next/server/app/api/agent/[id]/route.js.nft.json +1 -1
- package/.next/server/app/api/agent/[id]/tree/route.js +1 -1
- package/.next/server/app/api/agent/[id]/tree/route.js.nft.json +1 -1
- package/.next/server/app/api/agent/new/route.js +1 -1
- package/.next/server/app/api/agent/new/route.js.nft.json +1 -1
- package/.next/server/app/api/config/route.js +1 -1
- package/.next/server/app/api/config/route.js.nft.json +1 -1
- package/.next/server/app/api/files/route.js +1 -1
- package/.next/server/app/api/files/route.js.nft.json +1 -1
- package/.next/server/app/api/git/route.js +2 -2
- package/.next/server/app/api/git/route.js.nft.json +1 -1
- package/.next/server/app/api/growth/route.js +1 -1
- package/.next/server/app/api/growth/route.js.nft.json +1 -1
- package/.next/server/app/api/health/route.js +1 -1
- package/.next/server/app/api/models/route.js +1 -1
- package/.next/server/app/api/models/route.js.nft.json +1 -1
- package/.next/server/app/api/pi-settings/route.js +1 -1
- package/.next/server/app/api/pi-settings/route.js.nft.json +1 -1
- package/.next/server/app/api/plugins/route.js +1 -1
- package/.next/server/app/api/plugins/route.js.nft.json +1 -1
- package/.next/server/app/api/prompts/route.js +1 -1
- package/.next/server/app/api/prompts/route.js.nft.json +1 -1
- package/.next/server/app/api/security/route.js +1 -1
- package/.next/server/app/api/security/route.js.nft.json +1 -1
- package/.next/server/app/api/sessions/[id]/export/route.js +1 -1
- package/.next/server/app/api/sessions/[id]/export/route.js.nft.json +1 -1
- package/.next/server/app/api/sessions/[id]/route.js +1 -1
- package/.next/server/app/api/sessions/[id]/route.js.nft.json +1 -1
- package/.next/server/app/api/sessions/import/route.js +3 -3
- package/.next/server/app/api/sessions/import/route.js.nft.json +1 -1
- package/.next/server/app/api/sessions/route.js +1 -1
- package/.next/server/app/api/sessions/route.js.nft.json +1 -1
- package/.next/server/app/api/skills/route.js +2 -2
- package/.next/server/app/api/skills/route.js.nft.json +1 -1
- package/.next/server/app/api/swarm/[id]/route.js +1 -0
- package/.next/server/app/api/swarm/[id]/route.js.nft.json +1 -0
- package/.next/server/app/api/swarm/[id]/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/swarm/route.js +1 -0
- package/.next/server/app/api/swarm/route.js.nft.json +1 -0
- package/.next/server/app/api/swarm/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/tests/route.js +2 -0
- package/.next/server/app/api/tests/route.js.nft.json +1 -0
- package/.next/server/app/api/tests/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/update/route.js +2 -2
- package/.next/server/app/api/update/route.js.nft.json +1 -1
- package/.next/server/app/api/version/route.js +1 -1
- package/.next/server/app/api/version/route.js.nft.json +1 -1
- package/.next/server/app/api/web-auth/route.js +1 -1
- package/.next/server/app/api/workspaces/route.js +1 -1
- package/.next/server/app/api/workspaces/route.js.nft.json +1 -1
- package/.next/server/app/icon.svg/route.js +1 -1
- package/.next/server/app/index.html +1 -1
- package/.next/server/app/index.rsc +9 -6
- package/.next/server/app/index.segments/__PAGE__.segment.rsc +7 -4
- package/.next/server/app/index.segments/_full.segment.rsc +9 -6
- package/.next/server/app/index.segments/_tree.segment.rsc +5 -2
- package/.next/server/app/login/page.js +1 -1
- package/.next/server/app/login/page_client-reference-manifest.js +1 -1
- package/.next/server/app/login.html +1 -1
- package/.next/server/app/login.rsc +8 -5
- package/.next/server/app/login.segments/_full.segment.rsc +8 -5
- package/.next/server/app/login.segments/_tree.segment.rsc +5 -2
- package/.next/server/app/login.segments/login/__PAGE__.segment.rsc +6 -3
- package/.next/server/app/page.js +1 -1
- package/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/server/app-paths-manifest.json +12 -9
- package/.next/server/chunks/496.js +1 -1
- package/.next/server/chunks/532.js +1 -1
- package/.next/server/chunks/686.js +6 -2
- package/.next/server/chunks/757.js +1 -0
- package/.next/server/chunks/775.js +3 -0
- package/.next/server/middleware-build-manifest.js +1 -1
- package/.next/server/middleware-react-loadable-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/0t1jBUAuVaTFTXxRyIo3q/_buildManifest.js +1 -0
- package/.next/static/chunks/2608.43c58236c9cce8e6.js +1 -0
- package/.next/static/chunks/{2716.a9e808190a4e2424.js → 2716.4ec42df1d51354c9.js} +1 -1
- package/.next/static/chunks/4632.62139b565b657aac.js +1 -0
- package/.next/static/chunks/{4673.d3d0a7f2599080fb.js → 4673.7796104395deb8d7.js} +1 -1
- package/.next/static/chunks/4686-17f21a006739a596.js +1 -0
- package/.next/static/chunks/7577.10af9bb661652da8.js +1 -0
- package/.next/static/chunks/8866.38ec4ac4ee5a1b74.js +1 -0
- package/.next/static/chunks/app/_global-error/page-5148173b3f55e415.js +1 -0
- package/.next/static/chunks/app/api/agent/[id]/events/route-5148173b3f55e415.js +1 -0
- package/.next/static/chunks/app/api/agent/[id]/route-5148173b3f55e415.js +1 -0
- package/.next/static/chunks/app/api/agent/[id]/tree/route-5148173b3f55e415.js +1 -0
- package/.next/static/chunks/app/api/agent/new/route-5148173b3f55e415.js +1 -0
- package/.next/static/chunks/app/api/config/route-5148173b3f55e415.js +1 -0
- package/.next/static/chunks/app/api/files/route-5148173b3f55e415.js +1 -0
- package/.next/static/chunks/app/api/git/route-5148173b3f55e415.js +1 -0
- package/.next/static/chunks/app/api/growth/route-5148173b3f55e415.js +1 -0
- package/.next/static/chunks/app/api/health/route-5148173b3f55e415.js +1 -0
- package/.next/static/chunks/app/api/models/route-5148173b3f55e415.js +1 -0
- package/.next/static/chunks/app/api/pi-settings/route-5148173b3f55e415.js +1 -0
- package/.next/static/chunks/app/api/plugins/route-5148173b3f55e415.js +1 -0
- package/.next/static/chunks/app/api/prompts/route-5148173b3f55e415.js +1 -0
- package/.next/static/chunks/app/api/security/route-5148173b3f55e415.js +1 -0
- package/.next/static/chunks/app/api/sessions/[id]/export/route-5148173b3f55e415.js +1 -0
- package/.next/static/chunks/app/api/sessions/[id]/route-5148173b3f55e415.js +1 -0
- package/.next/static/chunks/app/api/sessions/import/route-5148173b3f55e415.js +1 -0
- package/.next/static/chunks/app/api/sessions/route-5148173b3f55e415.js +1 -0
- package/.next/static/chunks/app/api/skills/route-5148173b3f55e415.js +1 -0
- package/.next/static/chunks/app/api/swarm/[id]/route-5148173b3f55e415.js +1 -0
- package/.next/static/chunks/app/api/swarm/route-5148173b3f55e415.js +1 -0
- package/.next/static/chunks/app/api/tests/route-5148173b3f55e415.js +1 -0
- package/.next/static/chunks/app/api/update/route-5148173b3f55e415.js +1 -0
- package/.next/static/chunks/app/api/version/route-5148173b3f55e415.js +1 -0
- package/.next/static/chunks/app/api/web-auth/route-5148173b3f55e415.js +1 -0
- package/.next/static/chunks/app/api/workspaces/route-5148173b3f55e415.js +1 -0
- package/.next/static/chunks/app/page-14379b8060d5cf07.js +1 -0
- package/.next/static/chunks/next/dist/client/components/builtin/app-error-5148173b3f55e415.js +1 -0
- package/.next/static/chunks/next/dist/client/components/builtin/forbidden-5148173b3f55e415.js +1 -0
- package/.next/static/chunks/next/dist/client/components/builtin/not-found-5148173b3f55e415.js +1 -0
- package/.next/static/chunks/next/dist/client/components/builtin/unauthorized-5148173b3f55e415.js +1 -0
- package/.next/static/chunks/webpack-d1a69ad9edb38b11.js +1 -0
- package/.next/static/css/d0a0162cb481abe8.css +1 -0
- package/.next/types/app/api/swarm/[id]/route.ts +353 -0
- package/.next/types/app/api/swarm/route.ts +353 -0
- package/.next/types/app/api/tests/route.ts +353 -0
- package/.next/types/routes.d.ts +4 -1
- package/.next/types/validator.ts +27 -0
- package/README.md +61 -7
- package/assets/archive.png +0 -0
- package/assets/growth-1.png +0 -0
- package/assets/growth-2.png +0 -0
- package/assets/import.png +0 -0
- package/assets/main.png +0 -0
- package/assets/outline.png +0 -0
- package/assets/prompts-1.png +0 -0
- package/assets/prompts-2.png +0 -0
- package/assets/providers-1.png +0 -0
- package/assets/providers-2.png +0 -0
- package/assets/providers-3.png +0 -0
- package/assets/trace-1.png +0 -0
- package/assets/trace-2.png +0 -0
- package/assets/trace-3.png +0 -0
- package/assets/trace-4.png +0 -0
- package/assets/viewer-1.png +0 -0
- package/assets/viewer-2.png +0 -0
- package/next.config.ts +6 -2
- package/package.json +10 -5
- package/scripts/launcher.mjs +1 -1
- package/scripts/managed-niubash-assets.mjs +146 -0
- package/scripts/pick-folder.ps1 +11 -2
- package/scripts/prepare-niubash-packages.mjs +182 -0
- package/scripts/release.mjs +11 -31
- package/scripts/run-test-file.mjs +34 -0
- package/scripts/smoke-managed-niubash.mjs +62 -0
- package/scripts/verify-package.mjs +53 -0
- package/scripts/verify-release-config.mjs +24 -0
- package/src/app/api/config/route.ts +4 -1
- package/src/app/api/security/route.ts +8 -3
- package/src/app/api/swarm/[id]/route.ts +28 -0
- package/src/app/api/swarm/route.ts +24 -0
- package/src/app/api/tests/route.ts +27 -0
- package/src/app/api/update/route.ts +8 -2
- package/src/app/globals.css +777 -0
- package/src/app/layout.tsx +13 -6
- package/src/components/AppShell.tsx +505 -199
- package/src/components/ChatInput.tsx +153 -26
- package/src/components/ChatWindow.tsx +46 -40
- package/src/components/ContextMeter.tsx +11 -17
- package/src/components/FileViewer.tsx +1 -1
- package/src/components/GitPanel.tsx +13 -11
- package/src/components/ModelSelector.tsx +19 -6
- package/src/components/PiMark.tsx +5 -38
- package/src/components/ProjectPanel.tsx +1 -0
- package/src/components/PromptPanel.tsx +132 -51
- package/src/components/SessionImportSection.tsx +53 -37
- package/src/components/SessionSidebar.tsx +159 -57
- package/src/components/SettingsPanel.tsx +330 -193
- package/src/components/SwarmCoordinatorPod.tsx +1021 -0
- package/src/components/TestInspectorPod.tsx +549 -0
- package/src/components/icons.tsx +23 -0
- package/src/hooks/usePiWeb.ts +112 -8
- package/src/i18n.tsx +59 -3
- package/src/lib/agent-manager.ts +182 -11
- package/src/lib/command-validation.ts +9 -1
- package/src/lib/delegation.ts +93 -0
- package/src/lib/managed-niubash.ts +231 -0
- package/src/lib/pi.ts +3 -2
- package/src/lib/runtime-activity.ts +27 -0
- package/src/lib/session-import/claude.ts +13 -2
- package/src/lib/session-import/codex.ts +44 -8
- package/src/lib/session-import/files.ts +14 -0
- package/src/lib/session-import/grok.ts +54 -24
- package/src/lib/session-import/index.ts +5 -0
- package/src/lib/session-import/lock.ts +25 -0
- package/src/lib/session-import/types.ts +12 -0
- package/src/lib/session-import/writer.ts +8 -3
- package/src/lib/skills-service.ts +16 -9
- package/src/lib/swarm-service.ts +371 -0
- package/src/lib/test-run-service.ts +122 -0
- package/src/lib/theme.ts +68 -1
- package/src/lib/trajectory.ts +1 -0
- package/src/lib/types.ts +14 -0
- package/src/lib/workflow.ts +55 -0
- package/.next/static/LOFl9bqqMV2goBV8erESO/_buildManifest.js +0 -1
- package/.next/static/chunks/2608.ec8ff1f5767c9056.js +0 -1
- package/.next/static/chunks/4632.112d2e893d48d188.js +0 -1
- package/.next/static/chunks/4686-7d694def920ff093.js +0 -1
- package/.next/static/chunks/app/_global-error/page-f77e9ede9808b18e.js +0 -1
- package/.next/static/chunks/app/api/agent/[id]/events/route-f77e9ede9808b18e.js +0 -1
- package/.next/static/chunks/app/api/agent/[id]/route-f77e9ede9808b18e.js +0 -1
- package/.next/static/chunks/app/api/agent/[id]/tree/route-f77e9ede9808b18e.js +0 -1
- package/.next/static/chunks/app/api/agent/new/route-f77e9ede9808b18e.js +0 -1
- package/.next/static/chunks/app/api/config/route-f77e9ede9808b18e.js +0 -1
- package/.next/static/chunks/app/api/files/route-f77e9ede9808b18e.js +0 -1
- package/.next/static/chunks/app/api/git/route-f77e9ede9808b18e.js +0 -1
- package/.next/static/chunks/app/api/growth/route-f77e9ede9808b18e.js +0 -1
- package/.next/static/chunks/app/api/health/route-f77e9ede9808b18e.js +0 -1
- package/.next/static/chunks/app/api/models/route-f77e9ede9808b18e.js +0 -1
- package/.next/static/chunks/app/api/pi-settings/route-f77e9ede9808b18e.js +0 -1
- package/.next/static/chunks/app/api/plugins/route-f77e9ede9808b18e.js +0 -1
- package/.next/static/chunks/app/api/prompts/route-f77e9ede9808b18e.js +0 -1
- package/.next/static/chunks/app/api/security/route-f77e9ede9808b18e.js +0 -1
- package/.next/static/chunks/app/api/sessions/[id]/export/route-f77e9ede9808b18e.js +0 -1
- package/.next/static/chunks/app/api/sessions/[id]/route-f77e9ede9808b18e.js +0 -1
- package/.next/static/chunks/app/api/sessions/import/route-f77e9ede9808b18e.js +0 -1
- package/.next/static/chunks/app/api/sessions/route-f77e9ede9808b18e.js +0 -1
- package/.next/static/chunks/app/api/skills/route-f77e9ede9808b18e.js +0 -1
- package/.next/static/chunks/app/api/update/route-f77e9ede9808b18e.js +0 -1
- package/.next/static/chunks/app/api/version/route-f77e9ede9808b18e.js +0 -1
- package/.next/static/chunks/app/api/web-auth/route-f77e9ede9808b18e.js +0 -1
- package/.next/static/chunks/app/api/workspaces/route-f77e9ede9808b18e.js +0 -1
- package/.next/static/chunks/app/page-9a5440047c5a415c.js +0 -1
- package/.next/static/chunks/next/dist/client/components/builtin/app-error-f77e9ede9808b18e.js +0 -1
- package/.next/static/chunks/next/dist/client/components/builtin/forbidden-f77e9ede9808b18e.js +0 -1
- package/.next/static/chunks/next/dist/client/components/builtin/not-found-f77e9ede9808b18e.js +0 -1
- package/.next/static/chunks/next/dist/client/components/builtin/unauthorized-f77e9ede9808b18e.js +0 -1
- package/.next/static/chunks/webpack-d28e132e1b8c100c.js +0 -1
- package/.next/static/css/b2d35abc828d4713.css +0 -1
- package/assets/showcase.png +0 -0
- package/bin/pi.js +0 -50
- /package/.next/static/{LOFl9bqqMV2goBV8erESO → 0t1jBUAuVaTFTXxRyIo3q}/_ssgManifest.js +0 -0
package/src/hooks/usePiWeb.ts
CHANGED
|
@@ -13,6 +13,7 @@ import type {
|
|
|
13
13
|
WebMessage,
|
|
14
14
|
WebSnapshot,
|
|
15
15
|
ToolPreset,
|
|
16
|
+
WorkflowMode,
|
|
16
17
|
} from "@/lib/types";
|
|
17
18
|
|
|
18
19
|
export interface ToolCardState {
|
|
@@ -64,6 +65,22 @@ export interface PiWebState {
|
|
|
64
65
|
growth: { steps: GrowthStep[]; pending: string[]; error: string | null };
|
|
65
66
|
}
|
|
66
67
|
|
|
68
|
+
export function previewEditedBranch(messages: WebMessage[], entryId: string, text: string): WebMessage[] | null {
|
|
69
|
+
const index = messages.findIndex((message) => message.role === "user" && message.id === entryId);
|
|
70
|
+
if (index < 0) return null;
|
|
71
|
+
const original = messages[index];
|
|
72
|
+
return [
|
|
73
|
+
...messages.slice(0, index),
|
|
74
|
+
{
|
|
75
|
+
...original,
|
|
76
|
+
content: [
|
|
77
|
+
{ type: "text" as const, text },
|
|
78
|
+
...original.content.filter((part) => part.type === "image"),
|
|
79
|
+
],
|
|
80
|
+
},
|
|
81
|
+
];
|
|
82
|
+
}
|
|
83
|
+
|
|
67
84
|
export interface SessionListItem extends SessionSummary {
|
|
68
85
|
streaming: boolean;
|
|
69
86
|
/** 正在运行的会话最后一个工具步骤(侧栏副标题) */
|
|
@@ -191,6 +208,9 @@ export const foldPiWebEvent = (state: PiWebState, evt: WebEvent): PiWebState =>
|
|
|
191
208
|
tools: { active: evt.active, all: evt.all.map((n) => ({ name: n })) },
|
|
192
209
|
};
|
|
193
210
|
return s;
|
|
211
|
+
case "workflow":
|
|
212
|
+
if (s.snapshot) s.snapshot = { ...s.snapshot, workflow: evt.workflow };
|
|
213
|
+
return s;
|
|
194
214
|
case "resources":
|
|
195
215
|
if (s.snapshot) {
|
|
196
216
|
s.snapshot = {
|
|
@@ -335,14 +355,20 @@ export function usePiWeb() {
|
|
|
335
355
|
const [currentId, setCurrentId] = useState<string | null>(null);
|
|
336
356
|
const [currentPath, setCurrentPath] = useState<string | null>(null);
|
|
337
357
|
const [state, setState] = useState<PiWebState>(emptyState);
|
|
358
|
+
const stateRef = useRef(state);
|
|
359
|
+
stateRef.current = state;
|
|
338
360
|
/** 当前 SSE 连接的增量批量器:按帧合并事件,避免每个 token 一次整树渲染 */
|
|
339
361
|
const batcherRef = useRef<EventBatcher<WebEvent> | null>(null);
|
|
340
362
|
const [models, setModels] = useState<{ providers: any[]; models: any[] } | null>(null);
|
|
363
|
+
const [modelLoading, setModelLoading] = useState(true);
|
|
364
|
+
const [modelLoadError, setModelLoadError] = useState<string | null>(null);
|
|
365
|
+
const modelRequestRef = useRef(0);
|
|
341
366
|
const [addedWorkspaces, setAddedWorkspaces] = useState<string[]>([]);
|
|
342
367
|
const [removedWorkspaces, setRemovedWorkspaces] = useState<string[]>([]);
|
|
343
368
|
const [groupBy, setGroupByState] = useState<"workspace" | "flat">("workspace");
|
|
344
369
|
const [orderBy, setOrderByState] = useState<"updated" | "manual">("updated");
|
|
345
370
|
const [resyncNonce, setResyncNonce] = useState(0);
|
|
371
|
+
const resync = useCallback(() => setResyncNonce((nonce) => nonce + 1), []);
|
|
346
372
|
const [snapshotEpoch, setSnapshotEpoch] = useState(0);
|
|
347
373
|
const esRef = useRef<EventSource | null>(null);
|
|
348
374
|
const promptRequestsRef = useRef(new Set<string>());
|
|
@@ -350,6 +376,7 @@ export function usePiWeb() {
|
|
|
350
376
|
const presetRestorePromiseRef = useRef<Promise<void> | null>(null);
|
|
351
377
|
const activeSessionRef = useRef(currentId);
|
|
352
378
|
activeSessionRef.current = currentId;
|
|
379
|
+
const editSyncRef = useRef<{ sessionId: string; phase: "submitting" | "resyncing"; error?: string } | null>(null);
|
|
353
380
|
const subscribedSessionRef = useRef<string | null>(null);
|
|
354
381
|
const retrySessionList = useCallback(() => setSessionListRetryNonce((value) => value + 1), []);
|
|
355
382
|
const clearCompaction = useCallback(() => setState((current) => ({ ...current, compaction: null })), []);
|
|
@@ -505,6 +532,28 @@ export function usePiWeb() {
|
|
|
505
532
|
}
|
|
506
533
|
}, [refreshWorkspaces]);
|
|
507
534
|
|
|
535
|
+
/** 直接添加指定路径到工作区列表;成功返回规范化路径 */
|
|
536
|
+
const addWorkspaceByPath = useCallback(async (dir: string): Promise<string | null> => {
|
|
537
|
+
try {
|
|
538
|
+
const r = await fetch("/api/workspaces", {
|
|
539
|
+
method: "POST",
|
|
540
|
+
headers: { "Content-Type": "application/json" },
|
|
541
|
+
body: JSON.stringify({ action: "add", path: dir }),
|
|
542
|
+
});
|
|
543
|
+
const j = await r.json();
|
|
544
|
+
if (!r.ok || !j.success) throw new Error(j.error || `request failed (${r.status})`);
|
|
545
|
+
if (j.data?.workspaces) {
|
|
546
|
+
setAddedWorkspaces(j.data.workspaces);
|
|
547
|
+
}
|
|
548
|
+
await refreshWorkspaces();
|
|
549
|
+
return dir;
|
|
550
|
+
} catch (error) {
|
|
551
|
+
const message = error instanceof Error ? error.message : "failed to add workspace";
|
|
552
|
+
setState((current) => ({ ...current, error: message }));
|
|
553
|
+
throw error;
|
|
554
|
+
}
|
|
555
|
+
}, [refreshWorkspaces]);
|
|
556
|
+
|
|
508
557
|
|
|
509
558
|
const removeWorkspace = useCallback(
|
|
510
559
|
async (dir: string) => {
|
|
@@ -593,12 +642,20 @@ export function usePiWeb() {
|
|
|
593
642
|
|
|
594
643
|
// 模型目录
|
|
595
644
|
const refreshModels = useCallback(async () => {
|
|
645
|
+
const request = ++modelRequestRef.current;
|
|
646
|
+
setModelLoading(true);
|
|
647
|
+
setModelLoadError(null);
|
|
596
648
|
try {
|
|
597
649
|
const r = await fetch("/api/models");
|
|
598
650
|
const j = await r.json();
|
|
599
|
-
if (j.success
|
|
600
|
-
|
|
601
|
-
|
|
651
|
+
if (!r.ok || !j.success || !Array.isArray(j.data?.providers) || !Array.isArray(j.data?.models)) {
|
|
652
|
+
throw new Error(j.error || `request failed (${r.status})`);
|
|
653
|
+
}
|
|
654
|
+
if (request === modelRequestRef.current) setModels({ providers: j.data.providers, models: j.data.models });
|
|
655
|
+
} catch (error) {
|
|
656
|
+
if (request === modelRequestRef.current) setModelLoadError(error instanceof Error ? error.message : "model catalog unavailable");
|
|
657
|
+
} finally {
|
|
658
|
+
if (request === modelRequestRef.current) setModelLoading(false);
|
|
602
659
|
}
|
|
603
660
|
}, []);
|
|
604
661
|
useEffect(() => {
|
|
@@ -610,14 +667,17 @@ export function usePiWeb() {
|
|
|
610
667
|
esRef.current?.close();
|
|
611
668
|
esRef.current = null;
|
|
612
669
|
if (!currentId) {
|
|
670
|
+
editSyncRef.current = null;
|
|
613
671
|
subscribedSessionRef.current = null;
|
|
614
672
|
setState(emptyState(savedToolPreset()));
|
|
615
673
|
return;
|
|
616
674
|
}
|
|
617
675
|
const reconnectingCurrentSession = subscribedSessionRef.current === currentId;
|
|
676
|
+
if (!reconnectingCurrentSession) editSyncRef.current = null;
|
|
618
677
|
subscribedSessionRef.current = currentId;
|
|
678
|
+
const editResync = editSyncRef.current?.sessionId === currentId && editSyncRef.current.phase === "resyncing";
|
|
619
679
|
setState((current) => reconnectingCurrentSession
|
|
620
|
-
? { ...current, connected: false }
|
|
680
|
+
? { ...current, connected: editResync ? current.connected : false }
|
|
621
681
|
: { ...emptyState(savedToolPreset()), connected: false });
|
|
622
682
|
let retryDelayMs = 2000;
|
|
623
683
|
const connect = () => {
|
|
@@ -642,7 +702,10 @@ export function usePiWeb() {
|
|
|
642
702
|
};
|
|
643
703
|
// 流式事件按帧合并:token 速率再高也只每 16ms 渲染一次(见 event-batcher)
|
|
644
704
|
const batcher = createEventBatcher<WebEvent>({
|
|
645
|
-
flush: (events) =>
|
|
705
|
+
flush: (events) => {
|
|
706
|
+
if (editSyncRef.current?.sessionId === currentId) return;
|
|
707
|
+
setState((s) => events.reduce((acc, evt) => foldPiWebEvent(acc, evt), s));
|
|
708
|
+
},
|
|
646
709
|
});
|
|
647
710
|
batcherRef.current = batcher;
|
|
648
711
|
es.onmessage = (e) => {
|
|
@@ -654,6 +717,9 @@ export function usePiWeb() {
|
|
|
654
717
|
return;
|
|
655
718
|
}
|
|
656
719
|
if (parsed.type === "snapshot" && parsed.snapshot) {
|
|
720
|
+
const editSync = editSyncRef.current;
|
|
721
|
+
if (editSync?.sessionId === currentId && editSync.phase === "submitting") return;
|
|
722
|
+
if (editSync?.sessionId === currentId) editSyncRef.current = null;
|
|
657
723
|
// 快照是权威状态:之前缓冲的增量已经过时,直接丢弃
|
|
658
724
|
batcher.cancel();
|
|
659
725
|
retryDelayMs = 2000;
|
|
@@ -665,7 +731,7 @@ export function usePiWeb() {
|
|
|
665
731
|
tools: toolsFromMessages(snap.messages, snap.isStreaming),
|
|
666
732
|
toolPreset: snap.toolPreset,
|
|
667
733
|
connected: true,
|
|
668
|
-
error: null,
|
|
734
|
+
error: editSync?.sessionId === currentId ? editSync.error ?? null : null,
|
|
669
735
|
compaction: null,
|
|
670
736
|
retryNotice: null,
|
|
671
737
|
// 服务器当前 pending 集合是权威来源,刷新可恢复、已答请求不能复活。
|
|
@@ -677,6 +743,7 @@ export function usePiWeb() {
|
|
|
677
743
|
growth: { ...prev.growth, error: snap.growthError ?? null },
|
|
678
744
|
}));
|
|
679
745
|
} else {
|
|
746
|
+
if (editSyncRef.current?.sessionId === currentId) return;
|
|
680
747
|
batcher.push(parsed as WebEvent);
|
|
681
748
|
}
|
|
682
749
|
};
|
|
@@ -719,9 +786,42 @@ export function usePiWeb() {
|
|
|
719
786
|
}
|
|
720
787
|
}, [currentId]);
|
|
721
788
|
|
|
789
|
+
const editAndResend = useCallback(async (entryId: string, text: string) => {
|
|
790
|
+
if (editSyncRef.current?.sessionId === currentId) return { success: false, error: "edit already in progress" };
|
|
791
|
+
if (!currentId || !previewEditedBranch(stateRef.current.messages, entryId, text)) return { success: false, error: "message is not editable" };
|
|
792
|
+
editSyncRef.current = { sessionId: currentId, phase: "submitting" };
|
|
793
|
+
batcherRef.current?.cancel();
|
|
794
|
+
setState((previous) => {
|
|
795
|
+
const messages = previewEditedBranch(previous.messages, entryId, text);
|
|
796
|
+
if (!messages) return previous;
|
|
797
|
+
return {
|
|
798
|
+
...previous,
|
|
799
|
+
messages,
|
|
800
|
+
tools: toolsFromMessages(messages),
|
|
801
|
+
snapshot: previous.snapshot ? { ...previous.snapshot, messages, isStreaming: true } : null,
|
|
802
|
+
error: null,
|
|
803
|
+
retryNotice: null,
|
|
804
|
+
workingMessage: null,
|
|
805
|
+
};
|
|
806
|
+
});
|
|
807
|
+
const result = await sendCommand({ cmd: "editAndResend", entryId, text });
|
|
808
|
+
if (editSyncRef.current?.sessionId === currentId) {
|
|
809
|
+
editSyncRef.current = {
|
|
810
|
+
sessionId: currentId,
|
|
811
|
+
phase: "resyncing",
|
|
812
|
+
error: result?.success ? undefined : result?.error ?? "Failed to edit message",
|
|
813
|
+
};
|
|
814
|
+
batcherRef.current?.cancel();
|
|
815
|
+
esRef.current?.close();
|
|
816
|
+
esRef.current = null;
|
|
817
|
+
resync();
|
|
818
|
+
}
|
|
819
|
+
return result;
|
|
820
|
+
}, [currentId, resync, sendCommand]);
|
|
821
|
+
|
|
722
822
|
const newSession = useCallback(async (
|
|
723
823
|
cwd: string,
|
|
724
|
-
overrides: { provider?: string; modelId?: string; thinking?: string } = {},
|
|
824
|
+
overrides: { provider?: string; modelId?: string; thinking?: string; mode?: WorkflowMode } = {},
|
|
725
825
|
): Promise<string | null> => {
|
|
726
826
|
try {
|
|
727
827
|
const r = await fetch("/api/agent/new", {
|
|
@@ -735,6 +835,7 @@ export function usePiWeb() {
|
|
|
735
835
|
const id = idOf(p);
|
|
736
836
|
// 工具预设取通用设置的持久化选择;模型/思考级别由 Hero 页显式下发
|
|
737
837
|
const setup: Record<string, unknown>[] = [{ cmd: "setToolPreset", preset: savedToolPreset() }];
|
|
838
|
+
if (overrides.mode && overrides.mode !== "agent") setup.push({ cmd: "setWorkflowMode", mode: overrides.mode });
|
|
738
839
|
if (overrides.provider && overrides.modelId) setup.push({ cmd: "setModel", provider: overrides.provider, modelId: overrides.modelId });
|
|
739
840
|
if (overrides.thinking) setup.push({ cmd: "setThinkingLevel", level: overrides.thinking });
|
|
740
841
|
for (const command of setup) {
|
|
@@ -828,7 +929,6 @@ export function usePiWeb() {
|
|
|
828
929
|
// ---------- 会话预设已移除:工具档位见通用设置/工具启用情况,模型与思考级别由 Hero 页显式下发 ----------
|
|
829
930
|
|
|
830
931
|
/** 强制重连 SSE 拿新快照(navigate 等场景) */
|
|
831
|
-
const resync = useCallback(() => setResyncNonce((n) => n + 1), []);
|
|
832
932
|
/** 应答扩展对话框(select/confirm/input);cancelled=true 表示用户关掉了 */
|
|
833
933
|
const answerExtensionDialog = useCallback(
|
|
834
934
|
async (id: string, response: { value?: string; confirmed?: boolean; cancelled?: boolean }) => {
|
|
@@ -887,6 +987,8 @@ export function usePiWeb() {
|
|
|
887
987
|
currentPath,
|
|
888
988
|
state,
|
|
889
989
|
models,
|
|
990
|
+
modelLoading,
|
|
991
|
+
modelLoadError,
|
|
890
992
|
addedWorkspaces,
|
|
891
993
|
removedWorkspaces,
|
|
892
994
|
workspaceAliases,
|
|
@@ -901,9 +1003,11 @@ export function usePiWeb() {
|
|
|
901
1003
|
setGroupBy,
|
|
902
1004
|
setOrderBy,
|
|
903
1005
|
addWorkspaceByPicker,
|
|
1006
|
+
addWorkspaceByPath,
|
|
904
1007
|
removeWorkspace,
|
|
905
1008
|
refreshModels,
|
|
906
1009
|
sendCommand,
|
|
1010
|
+
editAndResend,
|
|
907
1011
|
newSession,
|
|
908
1012
|
openSession,
|
|
909
1013
|
closeSession,
|
package/src/i18n.tsx
CHANGED
|
@@ -13,6 +13,8 @@ const zh = {
|
|
|
13
13
|
rename: "重命名",
|
|
14
14
|
renameSession: "重命名会话",
|
|
15
15
|
renameWorkspace: "重命名工作区",
|
|
16
|
+
sessionActions: "会话操作",
|
|
17
|
+
workspaceActions: "工作区操作",
|
|
16
18
|
search: "搜索",
|
|
17
19
|
save: "保存",
|
|
18
20
|
close: "关闭",
|
|
@@ -71,6 +73,23 @@ const zh = {
|
|
|
71
73
|
backToBottom: "回到底部",
|
|
72
74
|
modeSteer: "插话",
|
|
73
75
|
modeQueue: "排队",
|
|
76
|
+
workflowAgent: "Agent",
|
|
77
|
+
workflowPlan: "Plan",
|
|
78
|
+
workflowGoal: "Goal",
|
|
79
|
+
workflowMenu: "选择工作模式",
|
|
80
|
+
workflowClear: "退出工作模式",
|
|
81
|
+
workflowPlanPlaceholder: "规划改动…",
|
|
82
|
+
workflowGoalPlaceholder: "描述要达成的目标…",
|
|
83
|
+
workflowAgentHint: "直接执行任务",
|
|
84
|
+
workflowPlanHint: "先只读规划,确认后执行",
|
|
85
|
+
workflowGoalHint: "围绕目标持续推进并验证",
|
|
86
|
+
planReady: "方案已就绪,等待你确认",
|
|
87
|
+
planReadyHint: "确认后开始修改代码并验证结果",
|
|
88
|
+
planRefine: "调整方案",
|
|
89
|
+
planApproving: "正在启动…",
|
|
90
|
+
planApprovalFailed: "启动失败,请重试",
|
|
91
|
+
planApprove: "执行方案",
|
|
92
|
+
goalActive: "当前目标",
|
|
74
93
|
sendSteer: "插话:当前工具跑完就送达",
|
|
75
94
|
sendQueue: "排队:本轮结束后发送",
|
|
76
95
|
queueEdit: "↑ 取回编辑",
|
|
@@ -197,6 +216,7 @@ const zh = {
|
|
|
197
216
|
webAuthOn: "已启用(PI_WEB_PASSWORD)",
|
|
198
217
|
webAuthOff: "未启用(仅本机可访问)",
|
|
199
218
|
logout: "退出登录",
|
|
219
|
+
logoutFailed: "退出登录失败,请检查网络后重试。",
|
|
200
220
|
authMethodApiKey: "API 密钥",
|
|
201
221
|
oauthDevicePrompt: "请在浏览器中完成授权",
|
|
202
222
|
oauthOpenAuth: "打开授权页面",
|
|
@@ -235,6 +255,8 @@ const zh = {
|
|
|
235
255
|
noSessions: "暂无会话",
|
|
236
256
|
// Hero
|
|
237
257
|
startWith: "新建工作区",
|
|
258
|
+
heroTitle: "让想法落到代码。",
|
|
259
|
+
heroDescription: "规划、执行、验证。一个专注于交付的轻量工作台。",
|
|
238
260
|
// 输入卡
|
|
239
261
|
inputPlaceholder: "给 pi 发消息…",
|
|
240
262
|
thinking: "思考",
|
|
@@ -244,6 +266,8 @@ const zh = {
|
|
|
244
266
|
triggerLoading: "正在加载模型…",
|
|
245
267
|
blockedComposer: "当前模型不可用,请先选择模型",
|
|
246
268
|
emptyModels: "没有可用的模型。",
|
|
269
|
+
modelsLoading: "正在加载模型…",
|
|
270
|
+
modelsLoadFailed: "模型列表加载失败。",
|
|
247
271
|
emptyEfforts: "当前模型未提供推理等级。",
|
|
248
272
|
thinkingUnsupported: "此模型不支持",
|
|
249
273
|
thinkingUnsupportedHint: "模型没有声明推理能力。自定义模型请在 ~/.pi/agent/models.json 里给该模型加 \"reasoning\": true(可选 thinkingLevelMap 指定档位)",
|
|
@@ -317,8 +341,9 @@ const zh = {
|
|
|
317
341
|
contextAgentToolCall: "Agent Tool Call",
|
|
318
342
|
contextToolOutput: "Tool Output",
|
|
319
343
|
contextCompacted: "Compacted Data",
|
|
320
|
-
contextAutoCompactBuffer: "
|
|
321
|
-
contextFreeSpace: "
|
|
344
|
+
contextAutoCompactBuffer: "自动压缩预留(非占用)",
|
|
345
|
+
contextFreeSpace: "剩余空间",
|
|
346
|
+
contextEstimateNote: "总量由 Pi 估算;下方分类按字符估算,不可相加。",
|
|
322
347
|
// 统计条
|
|
323
348
|
turns: "轮次",
|
|
324
349
|
toolCalls: "工具调用",
|
|
@@ -414,6 +439,7 @@ const zh = {
|
|
|
414
439
|
themeNordPaper: "北欧纸墨",
|
|
415
440
|
themePebrel: "Pebrel",
|
|
416
441
|
themeDsh: "海与墨",
|
|
442
|
+
themePiWeb: "PiWeb · 工作台",
|
|
417
443
|
fontSize: "正文字号",
|
|
418
444
|
fontUiSize: "界面字体",
|
|
419
445
|
fontUiSizeDesc: "侧栏、按钮、标签等界面文字",
|
|
@@ -506,6 +532,8 @@ const zh = {
|
|
|
506
532
|
updateFailed: "更新失败",
|
|
507
533
|
updateManualHint: "先停止 piweb,再执行上面的命令(Windows 无法替换运行中的安装目录)",
|
|
508
534
|
skillsDesc: "关闭后技能不会出现在系统提示中,仅可通过 /skill:名称 手动调用",
|
|
535
|
+
skillsLoading: "正在加载技能…",
|
|
536
|
+
skillsEmpty: "当前工作区没有可用技能",
|
|
509
537
|
confirmDeleteSkill: "确定删除技能“{name}”吗?将删除其所在技能目录,不可恢复。",
|
|
510
538
|
deleteSkillHint: "删除技能目录",
|
|
511
539
|
skillDeletePackageHint: "包技能请用禁用管理",
|
|
@@ -619,6 +647,8 @@ const en: Dict = {
|
|
|
619
647
|
rename: "Rename",
|
|
620
648
|
renameSession: "Rename session",
|
|
621
649
|
renameWorkspace: "Rename workspace",
|
|
650
|
+
sessionActions: "Session actions",
|
|
651
|
+
workspaceActions: "Workspace actions",
|
|
622
652
|
search: "Search",
|
|
623
653
|
save: "Save",
|
|
624
654
|
close: "Close",
|
|
@@ -676,6 +706,23 @@ const en: Dict = {
|
|
|
676
706
|
backToBottom: "Back to bottom",
|
|
677
707
|
modeSteer: "steer",
|
|
678
708
|
modeQueue: "queue",
|
|
709
|
+
workflowAgent: "Agent",
|
|
710
|
+
workflowPlan: "Plan",
|
|
711
|
+
workflowGoal: "Goal",
|
|
712
|
+
workflowMenu: "Choose workflow mode",
|
|
713
|
+
workflowClear: "Leave workflow mode",
|
|
714
|
+
workflowPlanPlaceholder: "Plan changes…",
|
|
715
|
+
workflowGoalPlaceholder: "Describe the goal…",
|
|
716
|
+
workflowAgentHint: "Execute the task directly",
|
|
717
|
+
workflowPlanHint: "Plan read-only, then request approval",
|
|
718
|
+
workflowGoalHint: "Pursue and verify an objective",
|
|
719
|
+
planReady: "Plan ready for your approval",
|
|
720
|
+
planReadyHint: "Confirm to make changes and verify the result",
|
|
721
|
+
planRefine: "Refine plan",
|
|
722
|
+
planApproving: "Starting…",
|
|
723
|
+
planApprovalFailed: "Could not start. Try again.",
|
|
724
|
+
planApprove: "Execute plan",
|
|
725
|
+
goalActive: "Current goal",
|
|
679
726
|
sendSteer: "Steer: delivered after the current tool call",
|
|
680
727
|
sendQueue: "Queue: sent after this turn ends",
|
|
681
728
|
queueEdit: "↑ Edit queued",
|
|
@@ -802,6 +849,7 @@ const en: Dict = {
|
|
|
802
849
|
webAuthOn: "Enabled (PI_WEB_PASSWORD)",
|
|
803
850
|
webAuthOff: "Disabled (local access only)",
|
|
804
851
|
logout: "Sign out",
|
|
852
|
+
logoutFailed: "Could not sign out. Check your connection and retry.",
|
|
805
853
|
authMethodApiKey: "API key",
|
|
806
854
|
oauthDevicePrompt: "Finish authorization in your browser",
|
|
807
855
|
oauthOpenAuth: "Open authorization page",
|
|
@@ -839,6 +887,8 @@ const en: Dict = {
|
|
|
839
887
|
oauthCancel: "Cancel login",
|
|
840
888
|
noSessions: "No sessions yet",
|
|
841
889
|
startWith: "New workspace",
|
|
890
|
+
heroTitle: "Move ideas into working code.",
|
|
891
|
+
heroDescription: "Plan, execute, verify. A focused workbench for shipping.",
|
|
842
892
|
inputPlaceholder: "Message pi…",
|
|
843
893
|
thinking: "Thinking",
|
|
844
894
|
thinkingOff: "Off",
|
|
@@ -847,6 +897,8 @@ const en: Dict = {
|
|
|
847
897
|
triggerLoading: "Loading models…",
|
|
848
898
|
blockedComposer: "This model is unavailable — select one to continue",
|
|
849
899
|
emptyModels: "No models available.",
|
|
900
|
+
modelsLoading: "Loading models…",
|
|
901
|
+
modelsLoadFailed: "Could not load models.",
|
|
850
902
|
emptyEfforts: "This model provides no reasoning effort levels.",
|
|
851
903
|
thinkingUnsupported: "Not supported by this model",
|
|
852
904
|
thinkingUnsupportedHint: "The model does not declare reasoning support. For custom models, add \"reasoning\": true (optionally a thinkingLevelMap) to the model in ~/.pi/agent/models.json",
|
|
@@ -920,8 +972,9 @@ const en: Dict = {
|
|
|
920
972
|
contextAgentToolCall: "Agent Tool Call",
|
|
921
973
|
contextToolOutput: "Tool Output",
|
|
922
974
|
contextCompacted: "Compacted Data",
|
|
923
|
-
contextAutoCompactBuffer: "Auto-
|
|
975
|
+
contextAutoCompactBuffer: "Auto-compact reserve (not used)",
|
|
924
976
|
contextFreeSpace: "Free Space",
|
|
977
|
+
contextEstimateNote: "Pi estimates total usage; content categories are rough estimates and do not add up.",
|
|
925
978
|
turns: "turns",
|
|
926
979
|
toolCalls: "tool calls",
|
|
927
980
|
cost: "cost",
|
|
@@ -1013,6 +1066,7 @@ const en: Dict = {
|
|
|
1013
1066
|
themeNordPaper: "Nord & Paper",
|
|
1014
1067
|
themePebrel: "Pebrel",
|
|
1015
1068
|
themeDsh: "Sea & Ink",
|
|
1069
|
+
themePiWeb: "PiWeb · Workbench",
|
|
1016
1070
|
fontSize: "Font size",
|
|
1017
1071
|
fontUiSize: "UI font size",
|
|
1018
1072
|
fontUiSizeDesc: "Sidebar, buttons, labels and other UI text",
|
|
@@ -1105,6 +1159,8 @@ const en: Dict = {
|
|
|
1105
1159
|
updateFailed: "Update failed",
|
|
1106
1160
|
updateManualHint: "Stop piweb, then run the command above (Windows cannot replace a running installation)",
|
|
1107
1161
|
skillsDesc: "Disabled skills are hidden from the system prompt and only callable via /skill:name",
|
|
1162
|
+
skillsLoading: "Loading skills…",
|
|
1163
|
+
skillsEmpty: "No skills available in this workspace",
|
|
1108
1164
|
confirmDeleteSkill: "Delete skill \"{name}\"? Its skill directory will be removed and cannot be recovered.",
|
|
1109
1165
|
deleteSkillHint: "Delete the skill directory",
|
|
1110
1166
|
skillDeletePackageHint: "package skill: use disable",
|