@chanlerdev/scorel 0.0.1
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/README.md +110 -0
- package/dist/index.js +6675 -0
- package/dist/index.js.map +7 -0
- package/docs/CHANGELOG.md +12 -0
- package/docs/README.md +116 -0
- package/docs/ROADMAP.md +669 -0
- package/docs/SHIP.md +242 -0
- package/docs/spec/channels.md +156 -0
- package/docs/spec/client.md +326 -0
- package/docs/spec/daemon.md +408 -0
- package/docs/spec/events.md +423 -0
- package/docs/spec/extensions.md +255 -0
- package/docs/spec/relay.md +391 -0
- package/docs/spec/runtime.md +251 -0
- package/docs/spec/session.md +380 -0
- package/docs/spec/ship/S0001-docs-baseline.md +41 -0
- package/docs/spec/ship/S0002-package-skeleton.md +56 -0
- package/docs/spec/ship/S0003-protocol-contracts.md +49 -0
- package/docs/spec/ship/S0004-session-core.md +50 -0
- package/docs/spec/ship/S0005-runtime-loop.md +48 -0
- package/docs/spec/ship/S0006-embedded-daemon-client.md +51 -0
- package/docs/spec/ship/S0007-cli-alpha.md +49 -0
- package/docs/spec/ship/S0008-coding-tools.md +107 -0
- package/docs/spec/ship/S0009-code-discovery-tools.md +82 -0
- package/docs/spec/ship/S0010-todo-tool-and-cli.md +81 -0
- package/docs/spec/ship/S0011-coding-agent-alpha-smoke.md +110 -0
- package/docs/spec/ship/S0012-coding-tools-maturity.md +143 -0
- package/docs/spec/ship/S0013-local-daemon-protocol.md +57 -0
- package/docs/spec/ship/S0014-local-daemon-lifecycle.md +64 -0
- package/docs/spec/ship/S0015-local-attach-and-broadcast.md +58 -0
- package/docs/spec/ship/S0016-local-daemon-resync-smoke.md +60 -0
- package/docs/spec/ship/S0017-grep-files-output-mode.md +49 -0
- package/docs/spec/ship/S0018-daemon-entrypoint-smoke.md +48 -0
- package/docs/spec/ship/S0019-remote-transport-contract.md +59 -0
- package/docs/spec/ship/S0020-remote-websocket-server.md +56 -0
- package/docs/spec/ship/S0021-remote-websocket-client-transport.md +55 -0
- package/docs/spec/ship/S0022-remote-daemon-cli-lifecycle.md +60 -0
- package/docs/spec/ship/S0023-remote-control-e2e-validation.md +66 -0
- package/docs/spec/ship/S0024-remote-attach-interactive-stream.md +49 -0
- package/docs/spec/ship/S0025-remote-attach-session-event-view.md +57 -0
- package/docs/spec/ship/S0026-attach-project-cache-and-dual-seq-reconnect.md +87 -0
- package/docs/spec/ship/S0027-session-diagnostics-log.md +77 -0
- package/docs/spec/ship/S0028-client-attach-diagnostics-log.md +70 -0
- package/docs/spec/ship/S0029-project-index-for-session-lookup.md +119 -0
- package/docs/spec/ship/S0030-webui-product-intent.md +73 -0
- package/docs/spec/ship/S0031-daemon-projectslug-rule.md +72 -0
- package/docs/spec/ship/S0032-daemon-protocol-completion.md +123 -0
- package/docs/spec/ship/S0033-webui-skeleton-routing.md +92 -0
- package/docs/spec/ship/S0034-webui-device-settings.md +121 -0
- package/docs/spec/ship/S0035-webui-device-handshake.md +83 -0
- package/docs/spec/ship/S0036-webui-project-session-sync.md +70 -0
- package/docs/spec/ship/S0037-webui-chatbox-v1.md +97 -0
- package/docs/spec/ship/S0038-webui-cancel-multiclient.md +65 -0
- package/docs/spec/ship/S0039-webui-e2e-newchat.md +74 -0
- package/docs/spec/ship/S0040-webui-codex-visual-tokens.md +227 -0
- package/docs/spec/ship/S0041-webui-markdown-and-tool-block.md +248 -0
- package/docs/spec/ship/S0042-webui-streaming-ux-autoscroll.md +130 -0
- package/docs/spec/ship/S0043-startup-ergonomics.md +278 -0
- package/docs/spec/ship/S0044-webui-chatbox-rebuild.md +556 -0
- package/docs/spec/ship/S0045-webui-card-sidebar-and-session-fixes.md +469 -0
- package/docs/spec/ship/S0046-webui-empty-composer-and-lazy-session.md +428 -0
- package/docs/spec/ship/S0047-webui-project-hover-newchat-and-dynamic-greeting.md +176 -0
- package/docs/spec/ship/S0048-device-level-host-project-registry.md +253 -0
- package/docs/spec/ship/S0049-webui-add-project-directory-browser.md +217 -0
- package/docs/spec/ship/S0050-instruction-snapshot-and-agents-assembly.md +338 -0
- package/docs/spec/ship/S0051-harness-item-and-system-reminder.md +190 -0
- package/docs/spec/ship/S0052-follow-up-queue-and-dual-loop.md +195 -0
- package/docs/spec/ship/S0053-skill-index-and-skill-tool.md +252 -0
- package/docs/spec/ship/S0054-webui-running-message-behavior.md +72 -0
- package/docs/spec/ship/S0055-webui-composer-acceptance-and-queue-strip.md +68 -0
- package/docs/spec/ship/S0056-relay-and-hosted-webui-contract.md +106 -0
- package/docs/spec/ship/S0057-relay-service-protocol-skeleton.md +161 -0
- package/docs/spec/ship/S0058-host-outbound-relay-and-pair-command.md +138 -0
- package/docs/spec/ship/S0059-relay-transport-and-hosted-webui-connector.md +140 -0
- package/docs/spec/ship/S0060-relay-hosted-webui-e2e-validation.md +132 -0
- package/docs/spec/ship/S0060-relay-hosted-webui-e2e-validation.verification.md +90 -0
- package/docs/spec/ship/S0061-hosted-defaults-and-cli-command-surface.md +208 -0
- package/docs/spec/ship/S0062-npm-package-and-release-workflow.md +166 -0
- package/docs/spec/tools.md +173 -0
- package/package.json +51 -0
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
# S0048 — Device-level Host And Project Registry
|
|
2
|
+
|
|
3
|
+
## Goal
|
|
4
|
+
|
|
5
|
+
把当前“一次 daemon serve 只服务一个启动 cwd”的实现升级为 Device-level Host:
|
|
6
|
+
|
|
7
|
+
- 一个 Device 只有一个逻辑 Host。
|
|
8
|
+
- Host 持久管理本机多个 Project。
|
|
9
|
+
- `projectId` 取代 `projectSlug` 成为稳定身份。
|
|
10
|
+
- Session 必须归属 Project。
|
|
11
|
+
- Runtime 创建必须根据 Session 的 Project 解析 canonical `workDir`。
|
|
12
|
+
|
|
13
|
+
这是 GUI、WebUI 添加项目、SSH 远程设备和纯 HTTP API 的共同地基。
|
|
14
|
+
|
|
15
|
+
## Why Now
|
|
16
|
+
|
|
17
|
+
当前 M5 WebUI 通过 Session JSONL 聚合 `projectSlug`,只能展示 daemon 启动时的单一工作目录。这个模型无法支持:
|
|
18
|
+
|
|
19
|
+
- 在同一个 Device 上添加多个工作目录。
|
|
20
|
+
- GUI 像 Codex App 一样管理本地和远程 Project。
|
|
21
|
+
- WebUI 从侧边栏浏览目录并添加 Project。
|
|
22
|
+
- API 客户端先注册 Project,再创建 Session。
|
|
23
|
+
|
|
24
|
+
在继续堆 UI 前必须先把 Host 的领域模型改正确。
|
|
25
|
+
|
|
26
|
+
## Scope
|
|
27
|
+
|
|
28
|
+
### 1. Protocol
|
|
29
|
+
|
|
30
|
+
新增:
|
|
31
|
+
|
|
32
|
+
```typescript
|
|
33
|
+
type ProjectId = string & { readonly __brand: "ProjectId" };
|
|
34
|
+
|
|
35
|
+
interface HostProject {
|
|
36
|
+
projectId: ProjectId;
|
|
37
|
+
displayName: string;
|
|
38
|
+
workDir: string;
|
|
39
|
+
createdAt: number;
|
|
40
|
+
updatedAt: number;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
interface DirectoryListing {
|
|
44
|
+
path: string;
|
|
45
|
+
parentPath?: string;
|
|
46
|
+
entries: Array<{
|
|
47
|
+
name: string;
|
|
48
|
+
path: string;
|
|
49
|
+
kind: "directory";
|
|
50
|
+
}>;
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
修改:
|
|
55
|
+
|
|
56
|
+
- `SessionMeta.projectId` 必填。
|
|
57
|
+
- `SessionSummary.projectId` 必填。
|
|
58
|
+
- `connected` handshake 只报告 Device identity,不报告单 Project identity。
|
|
59
|
+
- `list_sessions` filter 改为 `{ projectId?: ProjectId; limit?: number }`。
|
|
60
|
+
- `list_projects` 从 Registry 返回 `HostProject[]`。
|
|
61
|
+
- 新增 `list_directories`、`register_project`、`remove_project`。
|
|
62
|
+
- bump `protocolVersion`。
|
|
63
|
+
|
|
64
|
+
删除:
|
|
65
|
+
|
|
66
|
+
- `projectSlug`
|
|
67
|
+
- `workDirHint`
|
|
68
|
+
- 任何把 daemon startup cwd 当连接身份的字段
|
|
69
|
+
|
|
70
|
+
### 2. Project Registry
|
|
71
|
+
|
|
72
|
+
新增 Host-owned persistence:
|
|
73
|
+
|
|
74
|
+
```text
|
|
75
|
+
~/.scorel/projects.json
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
规则:
|
|
79
|
+
|
|
80
|
+
- Registry 是 Project 的权威索引。
|
|
81
|
+
- `registerProject(workDir)` 先 realpath/canonicalize,再校验 directory 存在。
|
|
82
|
+
- 同一 canonical path 重复注册返回已有 Project,保持幂等。
|
|
83
|
+
- `projectId` 由 Host 生成,不能由 client 路径 slug 计算。
|
|
84
|
+
- `displayName` 默认取目录 basename,后续可扩展 rename。
|
|
85
|
+
- `removeProject(projectId)` 只允许移除没有 Session 的 Registry 项,不删除 session JSONL 或工作区文件。仍有 Session 时返回明确冲突错误。
|
|
86
|
+
|
|
87
|
+
### 3. Device-level Host
|
|
88
|
+
|
|
89
|
+
将 `EmbeddedDaemon` 的核心能力升级为 `ScorelHost`。允许在实现过程中保留内部类名作为短期机械步骤,但公开概念、API 和测试必须使用 Host 语义。
|
|
90
|
+
|
|
91
|
+
Host options 不再接收单一 `workDir`。改为:
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
interface ScorelHostOptions {
|
|
95
|
+
deviceId: DeviceId;
|
|
96
|
+
sessionsDir: string;
|
|
97
|
+
projectsPath: string;
|
|
98
|
+
createRuntime(project: HostProject, sessionId: SessionId): Promise<ScorelRuntime>;
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Host 根据 `SessionMeta.projectId` 查询 Registry,再把 Project 交给 runtime factory。
|
|
103
|
+
|
|
104
|
+
### 4. CLI Entry
|
|
105
|
+
|
|
106
|
+
- `scorel chat --cwd <path>`:创建 embedded Host,注册 cwd,创建或恢复该 Project 下的 Session。
|
|
107
|
+
- `scorel daemon serve`:启动 Device-level WS Host,不把 `--cwd` 当身份。
|
|
108
|
+
- `scorel up --cwd <path>`:可把 cwd 作为启动后的首个注册 Project,作为便捷入口。
|
|
109
|
+
- 新增最小 CLI 管理面:
|
|
110
|
+
|
|
111
|
+
```text
|
|
112
|
+
scorel project list
|
|
113
|
+
scorel project add <path>
|
|
114
|
+
scorel project remove <projectId>
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
CLI 管理命令连接已有 WS Host;若实现选择不在 S0048 暴露命令,也必须至少提供等价 integration helper 供 S0049 和测试调用。
|
|
118
|
+
|
|
119
|
+
### 5. Directory Browsing
|
|
120
|
+
|
|
121
|
+
Host 暴露 `listDirectories(path?)`:
|
|
122
|
+
|
|
123
|
+
- path 省略时从用户 home 开始。
|
|
124
|
+
- 返回 canonical current path、可选 parent path、直接子目录。
|
|
125
|
+
- 只返回 directory。
|
|
126
|
+
- 稳定排序。
|
|
127
|
+
- 当前开发阶段不做 workspace root restriction、ACL 或 permission prompt。
|
|
128
|
+
- filesystem error 映射为明确 wire error。
|
|
129
|
+
|
|
130
|
+
### 6. Session Persistence
|
|
131
|
+
|
|
132
|
+
新的 Session header 必须写入 `meta.projectId`。旧 `projectSlug` header 不迁移。
|
|
133
|
+
|
|
134
|
+
实现切换时允许清理开发机上的旧产物:
|
|
135
|
+
|
|
136
|
+
```text
|
|
137
|
+
~/.scorel/sessions/*
|
|
138
|
+
~/.scorel/attach-cache/*
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Registry 文件使用新的 `~/.scorel/projects.json`。不继续写 `~/.scorel/project-index.json`。
|
|
142
|
+
|
|
143
|
+
### 7. Attach Cache
|
|
144
|
+
|
|
145
|
+
远程 attach scope 改为:
|
|
146
|
+
|
|
147
|
+
```text
|
|
148
|
+
deviceId + projectId + sessionId
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
删除旧 slug-based cache key 和 fallback。
|
|
152
|
+
|
|
153
|
+
### 8. Diagnostics
|
|
154
|
+
|
|
155
|
+
日志增加:
|
|
156
|
+
|
|
157
|
+
- `project_registered`
|
|
158
|
+
- `project_removed`
|
|
159
|
+
- `project_resolved`
|
|
160
|
+
- `directory_listed`
|
|
161
|
+
|
|
162
|
+
日志可记录 `projectId` 和 canonical path,不得记录 token、API key 或 SSH secret。
|
|
163
|
+
|
|
164
|
+
## Explicitly Not In Scope
|
|
165
|
+
|
|
166
|
+
- WebUI Add Project UI,见 S0049。
|
|
167
|
+
- GUI desktop app。
|
|
168
|
+
- SSH 安装、SSH proxy 和远端 supervisor。
|
|
169
|
+
- HTTP API adapter。
|
|
170
|
+
- ACL、角色、scope、sandbox 和 permission approval。
|
|
171
|
+
- Project rename。
|
|
172
|
+
- 每个 Project 一个 worker process。
|
|
173
|
+
- 旧状态迁移或协议兼容。
|
|
174
|
+
|
|
175
|
+
## Destructive Refactor Rules
|
|
176
|
+
|
|
177
|
+
Scorel 当前 pre-1.0。本 spec 直接删除旧模型:
|
|
178
|
+
|
|
179
|
+
- 不保留 `projectSlug` alias。
|
|
180
|
+
- 不做 `projectSlug -> projectId` dual write。
|
|
181
|
+
- 不读取旧 `project-index.json`。
|
|
182
|
+
- 不读取旧 WebUI local storage shape。
|
|
183
|
+
- 不保留旧 client/server protocol negotiation。
|
|
184
|
+
|
|
185
|
+
旧 Client 连接新 Host 时应因 `protocolVersion` 不匹配明确失败。
|
|
186
|
+
|
|
187
|
+
## Required Tests
|
|
188
|
+
|
|
189
|
+
### Protocol
|
|
190
|
+
|
|
191
|
+
- `SessionMeta.projectId` 和新增 request/response schema round-trip。
|
|
192
|
+
- 不再导出 `projectSlug` 或 `workDirHint`。
|
|
193
|
+
|
|
194
|
+
### Registry
|
|
195
|
+
|
|
196
|
+
- 注册 canonical path 幂等。
|
|
197
|
+
- 不存在路径和非目录路径失败。
|
|
198
|
+
- Registry 重启后可恢复。
|
|
199
|
+
- remove 对仍有 Session 的 Project 返回冲突错误;成功 remove 不删除 workspace 文件。
|
|
200
|
+
|
|
201
|
+
### Host
|
|
202
|
+
|
|
203
|
+
- 同一 Host 注册两个临时仓库。
|
|
204
|
+
- 分别在两个 Project 创建 Session。
|
|
205
|
+
- 两个 Runtime 的 cwd 正确隔离。
|
|
206
|
+
- `listSessions({ projectId })` 只返回对应 Session。
|
|
207
|
+
- Host 重启后 Registry 和 Session 归属仍正确。
|
|
208
|
+
|
|
209
|
+
### Directory Browser
|
|
210
|
+
|
|
211
|
+
- home 默认起点。
|
|
212
|
+
- parent path。
|
|
213
|
+
- 只返回子目录并稳定排序。
|
|
214
|
+
- filesystem error 映射。
|
|
215
|
+
|
|
216
|
+
### Client
|
|
217
|
+
|
|
218
|
+
- daemon-only handshake 后可 list/register/remove Project。
|
|
219
|
+
- session-bound 操作仍要求绑定 Session。
|
|
220
|
+
- attach cache scope 使用 `deviceId + projectId + sessionId`。
|
|
221
|
+
|
|
222
|
+
### CLI And Real Smoke
|
|
223
|
+
|
|
224
|
+
- `scorel chat --cwd <repo-a>` 能使用真实 provider 完成一轮。
|
|
225
|
+
- 同一 `scorel daemon serve` 注册 `<repo-a>` 和 `<repo-b>`。
|
|
226
|
+
- 对两个 Project 分别创建 Session,真实 provider 回答中可观测 cwd 不串线。
|
|
227
|
+
- `scorel attach --remote` 可恢复其中一个 Session。
|
|
228
|
+
|
|
229
|
+
不要使用 fake/mock provider 代替真实产品 smoke。
|
|
230
|
+
|
|
231
|
+
## Likely Files
|
|
232
|
+
|
|
233
|
+
```text
|
|
234
|
+
packages/protocol/src/ids.ts
|
|
235
|
+
packages/protocol/src/events.ts
|
|
236
|
+
packages/protocol/src/wire.ts
|
|
237
|
+
packages/daemon/src/index.ts
|
|
238
|
+
packages/daemon/src/projects/registry.ts
|
|
239
|
+
packages/daemon/src/ws-server.ts
|
|
240
|
+
packages/client/src/index.ts
|
|
241
|
+
apps/cli/src/index.ts
|
|
242
|
+
apps/cli/src/daemon-cli.ts
|
|
243
|
+
apps/cli/src/up-cli.ts
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
## Done When
|
|
247
|
+
|
|
248
|
+
- 一个 WS Host 能管理至少两个 Project。
|
|
249
|
+
- Project 来自持久 Registry,不再来自 Session 聚合。
|
|
250
|
+
- Session 和 Runtime 都按 `projectId` 解析 cwd。
|
|
251
|
+
- `projectSlug` 和 `workDirHint` 从当前代码、协议和非历史文档中删除。
|
|
252
|
+
- 自动测试、typecheck 和真实双 Project smoke 通过。
|
|
253
|
+
- 完成后 commit:`S0048: feat: add device-level host project registry`
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
# S0049 — WebUI Add Project And Directory Browser
|
|
2
|
+
|
|
3
|
+
## Goal
|
|
4
|
+
|
|
5
|
+
让 WebUI 侧边栏出现正式的“添加项目”入口。用户选择 Device,浏览该 Device 的目录,注册工作空间,并立即在 Device -> Project -> Session 树中看到新 Project。
|
|
6
|
+
|
|
7
|
+
S0049 继续保持 Device-first:
|
|
8
|
+
|
|
9
|
+
- WebUI 添加的是 Device,不是 Project subscription。
|
|
10
|
+
- Project 只是 owning Host 上 canonical 工作目录的稳定抽象。
|
|
11
|
+
- 连接 Device 后,WebUI 展示该 Host Registry 中的全部 Project。
|
|
12
|
+
- 展开 Project 时才按 `projectId` 查询 Session。
|
|
13
|
+
- 点击 Session 时才加载 JSONL,并按需创建 Runtime。
|
|
14
|
+
|
|
15
|
+
## Dependency
|
|
16
|
+
|
|
17
|
+
必须先完成 [`S0048`](S0048-device-level-host-project-registry.md)。
|
|
18
|
+
|
|
19
|
+
S0048 已完成以下基础迁移,S0049 不重复实现:
|
|
20
|
+
|
|
21
|
+
- WebUI Project 身份已经从 `projectSlug` 切到 `projectId`。
|
|
22
|
+
- Project route 已经使用 `[projectId]`。
|
|
23
|
+
- attach cache scope 已经使用 `deviceId + projectId + sessionId`。
|
|
24
|
+
- `syncProjects(deviceId)` 已经使用 Registry 返回值。
|
|
25
|
+
- `syncSessions(deviceId, projectId)` 已经发送 `projectId` filter,并按 Device + Project 去重。
|
|
26
|
+
- New Session 已经传递 `meta.projectId`。
|
|
27
|
+
|
|
28
|
+
## Product Model
|
|
29
|
+
|
|
30
|
+
```text
|
|
31
|
+
Device
|
|
32
|
+
-> listProjects() 展示 Host Registry 中全部 Project
|
|
33
|
+
-> 展开 Project 时 listSessions({ projectId })
|
|
34
|
+
-> 点击 Session 时 loadSession(sessionId)
|
|
35
|
+
-> Host 按 projectId 解析 canonical workDir
|
|
36
|
+
-> 按需创建 Runtime
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Project Registry 是 Host 已知工作目录的权威集合。UI 不再增加一套 visible Project、pin、disable 或 archive 状态。
|
|
40
|
+
|
|
41
|
+
Session JSONL 是 Project 下的持久历史。UI 不增加 Session delete、archive 或额外状态文件。
|
|
42
|
+
|
|
43
|
+
## Product Flow
|
|
44
|
+
|
|
45
|
+
```text
|
|
46
|
+
点击侧边栏“添加项目”
|
|
47
|
+
-> 选择 Device
|
|
48
|
+
-> 浏览该 Device 文件夹
|
|
49
|
+
-> 选择当前工作目录
|
|
50
|
+
-> register_project
|
|
51
|
+
-> Host canonicalize 路径并幂等返回 Project
|
|
52
|
+
-> WebUI 重新调用 list_projects
|
|
53
|
+
-> 展示该 Device 的完整 Project Registry
|
|
54
|
+
-> 自动展开并选中新 Project
|
|
55
|
+
-> 可直接新增 Session
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
本地 Device 和远程 Device 使用同一套 Host API。WebUI 不直接读取浏览器所在机器的文件系统,也不自行解释远端路径。
|
|
59
|
+
|
|
60
|
+
## Scope
|
|
61
|
+
|
|
62
|
+
### 1. Sidebar Add Project
|
|
63
|
+
|
|
64
|
+
在 sidebar 的 Device / Project 区域增加正式“添加项目”入口:
|
|
65
|
+
|
|
66
|
+
- 没有 Device 时,引导用户先添加 Device。
|
|
67
|
+
- 有一个 Device 时,直接进入该 Device 的目录浏览。
|
|
68
|
+
- 有多个 Device 时,先选择 Device。
|
|
69
|
+
- 注册成功后重新同步该 Device 的完整 Project 列表。
|
|
70
|
+
- 自动展开并选中新 Project。
|
|
71
|
+
- 注册失败时显示明确错误,不静默吞掉。
|
|
72
|
+
|
|
73
|
+
### 2. Directory Browser Dialog
|
|
74
|
+
|
|
75
|
+
新增 modal/dialog:
|
|
76
|
+
|
|
77
|
+
- 显示当前 Device。
|
|
78
|
+
- 显示 Host 返回的 canonical current path。
|
|
79
|
+
- 支持进入直接子目录。
|
|
80
|
+
- 支持返回 Host 返回的 parent path。
|
|
81
|
+
- 支持选择当前目录作为 Project。
|
|
82
|
+
- loading、empty、filesystem error 都有明确状态。
|
|
83
|
+
- 浏览器不得自行拼接、规范化或反向解析路径。
|
|
84
|
+
- 目录导航只使用 Host 返回的 `path` 和 `parentPath`。
|
|
85
|
+
|
|
86
|
+
### 3. Registry Refresh
|
|
87
|
+
|
|
88
|
+
注册成功后:
|
|
89
|
+
|
|
90
|
+
1. 调用 `client.registerProject(workDir)`。
|
|
91
|
+
2. 调用现有 `syncProjects({ client, store, deviceId })`。
|
|
92
|
+
3. 使用返回 Project 的 `projectId` 展开并跳转:
|
|
93
|
+
|
|
94
|
+
```text
|
|
95
|
+
/devices/:deviceId/projects/:projectId
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
`registerProject()` 对同一 canonical path 必须幂等。重复添加同一路径时复用已有 `projectId`,不能制造重复 Project。
|
|
99
|
+
|
|
100
|
+
### 4. Session Lazy Loading
|
|
101
|
+
|
|
102
|
+
保持现有懒加载边界,不引入额外 Session 生命周期:
|
|
103
|
+
|
|
104
|
+
- `syncProjects()` 不加载 Session。
|
|
105
|
+
- 展开或进入 Project 时调用 `listSessions({ projectId })`。
|
|
106
|
+
- 点击 Session 时调用 `loadSession(sessionId)`。
|
|
107
|
+
- Session JSONL 不删除、不归档。
|
|
108
|
+
- Host 不为 Registry 中每个 Project 常驻创建 Runtime。
|
|
109
|
+
|
|
110
|
+
### 5. Existing Device Behavior
|
|
111
|
+
|
|
112
|
+
连接已有 Device 后,WebUI 继续调用 `listProjects()` 并展示 Host Registry 全集。
|
|
113
|
+
|
|
114
|
+
- 不保存客户端侧 visible Project 子集。
|
|
115
|
+
- 不实现 Project pin / unpin。
|
|
116
|
+
- 不区分本地 Device 与远端 Device 的 Project 展示规则。
|
|
117
|
+
- 添加 Project 始终通过目标 Device 的 Host 目录浏览完成。
|
|
118
|
+
|
|
119
|
+
## Explicitly Not In Scope
|
|
120
|
+
|
|
121
|
+
- GUI desktop app。
|
|
122
|
+
- SSH 配置导入、安装远端 Scorel、SSH proxy。
|
|
123
|
+
- 账号密码、权限分级、ACL。
|
|
124
|
+
- Project rename、pin、recent list、disable、archive。
|
|
125
|
+
- 普通 WebUI 的 Project remove 操作。
|
|
126
|
+
- Session delete、archive、unarchive 或额外 `session-state.json`。
|
|
127
|
+
- IDE-style file explorer。
|
|
128
|
+
- HTTP API。
|
|
129
|
+
- 为每个 Registry Project 常驻 Runtime。
|
|
130
|
+
- S0048 已完成的 `projectId` route、cache 和 store 迁移。
|
|
131
|
+
|
|
132
|
+
`remove_project` 若继续存在,只是底层 Host 管理 API。S0049 普通 WebUI 不调用它,也不扩展它。
|
|
133
|
+
|
|
134
|
+
## Required Tests
|
|
135
|
+
|
|
136
|
+
### Add Project Dialog
|
|
137
|
+
|
|
138
|
+
- 无 Device 时显示添加 Device 引导。
|
|
139
|
+
- 单 Device 时直接浏览该 Device。
|
|
140
|
+
- 多 Device 时先选择 Device。
|
|
141
|
+
- 初始目录加载状态。
|
|
142
|
+
- 目录列表 empty 状态。
|
|
143
|
+
- filesystem error 状态。
|
|
144
|
+
- child navigation 使用 Host 返回的 child `path`。
|
|
145
|
+
- parent navigation 使用 Host 返回的 `parentPath`。
|
|
146
|
+
- 选择当前目录后调用 `registerProject(workDir)`。
|
|
147
|
+
- 重复注册同一目录时接受 Host 返回的已有 Project。
|
|
148
|
+
- 注册失败时保持 dialog 打开并显示错误。
|
|
149
|
+
|
|
150
|
+
### Sidebar Integration
|
|
151
|
+
|
|
152
|
+
- sidebar 存在“添加项目”入口。
|
|
153
|
+
- 注册成功后调用 `syncProjects(deviceId)`,而不是仅向浏览器 store 手工 append。
|
|
154
|
+
- 同步后展示 Device Registry 全集。
|
|
155
|
+
- 注册成功后自动展开并跳转到 `projectId` route。
|
|
156
|
+
- 普通 UI 不调用 `removeProject()`。
|
|
157
|
+
|
|
158
|
+
### Lazy Loading Regression
|
|
159
|
+
|
|
160
|
+
- `syncProjects(deviceId)` 不调用 `listSessions()`。
|
|
161
|
+
- 展开或进入 Project 时调用 `listSessions({ projectId })`。
|
|
162
|
+
- 点击 Session 时调用 `loadSession(sessionId)`。
|
|
163
|
+
- 不新增 Session archive / delete 状态。
|
|
164
|
+
|
|
165
|
+
### Existing S0048 Regression
|
|
166
|
+
|
|
167
|
+
- Project route 继续使用 `projectId`。
|
|
168
|
+
- New Session 继续携带 `meta.projectId`。
|
|
169
|
+
- attach cache scope 继续使用 `deviceId + projectId + sessionId`。
|
|
170
|
+
- browser store 不读取旧 slug snapshot。
|
|
171
|
+
|
|
172
|
+
### Real Manual Smoke
|
|
173
|
+
|
|
174
|
+
1. `scorel up` 启动真实 Host 和 WebUI。
|
|
175
|
+
2. 在 WebUI 添加 Device。
|
|
176
|
+
3. 在侧边栏点击“添加项目”。
|
|
177
|
+
4. 通过目标 Device 的目录浏览注册两个不同真实临时仓库。
|
|
178
|
+
5. 确认 Device 下展示 Host Registry 的两个 Project。
|
|
179
|
+
6. 分别展开两个 Project,确认按 Project 懒加载 Session。
|
|
180
|
+
7. 分别创建 Session。
|
|
181
|
+
8. 通过真实 provider 在两个 Session 发送 prompt,确认 Runtime cwd 不串线。
|
|
182
|
+
9. 刷新页面,确认 Device -> Project -> Session 树恢复。
|
|
183
|
+
10. CLI attach 其中一个 Session,确认 WebUI 可见同一事件流。
|
|
184
|
+
|
|
185
|
+
不要使用 fake/mock provider 代替真实产品 smoke。
|
|
186
|
+
|
|
187
|
+
## Likely Files
|
|
188
|
+
|
|
189
|
+
```text
|
|
190
|
+
apps/webui/components/shell/sidebar.tsx
|
|
191
|
+
apps/webui/components/shell/sidebar.test.tsx
|
|
192
|
+
apps/webui/components/projects/add-project-dialog.tsx
|
|
193
|
+
apps/webui/components/projects/add-project-dialog.test.tsx
|
|
194
|
+
apps/webui/lib/connection/pool.ts
|
|
195
|
+
apps/webui/lib/connection/use-connection.ts
|
|
196
|
+
apps/webui/lib/sync/projects.ts
|
|
197
|
+
apps/webui/lib/sync/projects.test.ts
|
|
198
|
+
apps/webui/README.md
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
## Risks And Boundaries
|
|
202
|
+
|
|
203
|
+
- 目录浏览发生在目标 Device Host,不是浏览器所在机器。
|
|
204
|
+
- 浏览器不得假设 POSIX 路径,也不得自行拼接路径。
|
|
205
|
+
- Registry Project 数量增长时,`listProjects()` 仍是 Device 级全集;客户端侧筛选不是 S0049 的优化方向。
|
|
206
|
+
- Session 列表必须保持按 Project 懒加载,避免 Device 连接时扫描并加载全部 Session。
|
|
207
|
+
- 普通 UI 不暴露底层 `remove_project`,避免把工作目录管理误解为 Session 删除。
|
|
208
|
+
|
|
209
|
+
## Done When
|
|
210
|
+
|
|
211
|
+
- WebUI 侧边栏可通过 Host 目录浏览注册 Project。
|
|
212
|
+
- 添加 Device 后展示该 Host Registry 的全部 Project。
|
|
213
|
+
- 重复注册同一路径不会产生重复 Project。
|
|
214
|
+
- Session 继续按 Project 懒加载,Runtime 继续按 Session 按需创建。
|
|
215
|
+
- 普通 WebUI 不提供 Project remove、Project disable 或 Session archive。
|
|
216
|
+
- 自动测试和真实双 Project WebUI smoke 通过。
|
|
217
|
+
- 完成后 commit:`S0049: feat: add webui project directory browser`
|