-
Notifications
You must be signed in to change notification settings - Fork 22
Expand file tree
/
Copy pathrepository-structure.html
More file actions
247 lines (239 loc) · 20.3 KB
/
Copy pathrepository-structure.html
File metadata and controls
247 lines (239 loc) · 20.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Repository structure — OpenProgram</title>
<style>
:root {
color-scheme: dark;
--bg: #0c111b;
--panel: #121a28;
--panel-2: #182235;
--line: #2a3952;
--text: #e7edf8;
--muted: #9ba9bd;
--blue: #79a9ff;
--green: #72d6a0;
--amber: #ffc66d;
--red: #ff8d96;
}
* { box-sizing: border-box; }
body {
margin: 0;
background: var(--bg);
color: var(--text);
font: 15px/1.65 -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
}
main { width: min(1120px, calc(100% - 32px)); margin: 0 auto; padding: 52px 0 80px; }
h1 { margin: 0; font-size: clamp(30px, 5vw, 48px); line-height: 1.12; }
h2 { margin: 44px 0 14px; font-size: 22px; }
h3 { margin: 0 0 8px; font-size: 16px; }
p { max-width: 84ch; }
.eyebrow { color: var(--blue); font-size: 12px; font-weight: 700; letter-spacing: .13em; text-transform: uppercase; }
.lead { color: var(--muted); font-size: 17px; }
.meta-strip {
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr));
gap: 1px;
margin: 24px 0 0;
overflow: hidden;
border: 1px solid var(--line);
border-radius: 12px;
background: var(--line);
}
.meta-strip div { padding: 13px 14px; background: var(--panel); }
.meta-strip dt { margin: 0 0 3px; color: var(--muted); font-size: 11px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; }
.meta-strip dd { margin: 0; font-size: 13px; }
.grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px; }
main, .grid > *, .flow > *, .meta-strip > * { min-width: 0; }
p, li, dd, td { overflow-wrap: anywhere; }
.card, .flow, .table-wrap {
border: 1px solid var(--line);
border-radius: 14px;
background: var(--panel);
}
.card { padding: 18px; }
.card p, .card ul { margin-bottom: 0; color: var(--muted); }
.flow { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 1px; overflow: hidden; }
.flow div { padding: 18px; background: var(--panel); }
.flow strong, code { color: var(--blue); }
.table-wrap { overflow-x: auto; }
table { width: 100%; min-width: 760px; border-collapse: collapse; }
th, td { padding: 12px 14px; text-align: left; vertical-align: top; border-bottom: 1px solid var(--line); }
th { color: var(--muted); font-size: 12px; letter-spacing: .05em; }
tr:last-child td { border-bottom: 0; }
code { font: 13px/1.5 ui-monospace, SFMono-Regular, Menlo, monospace; }
pre { max-width: 100%; margin: 12px 0; padding: 16px 18px; overflow-x: auto; border: 1px solid var(--line);
border-radius: 12px; background: #0a101a; color: var(--text);
font: 13px/1.55 ui-monospace, SFMono-Regular, Menlo, monospace; }
.state { display: inline-block; padding: 2px 8px; border-radius: 999px; font-size: 12px; font-weight: 700; }
.now { color: var(--green); background: color-mix(in srgb, var(--green) 12%, transparent); }
.later { color: var(--amber); background: color-mix(in srgb, var(--amber) 12%, transparent); }
.no { color: var(--red); background: color-mix(in srgb, var(--red) 12%, transparent); }
ul { padding-left: 20px; }
.note { border-left: 3px solid var(--blue); padding: 8px 0 8px 16px; color: var(--muted); }
a { color: var(--blue); }
@media (max-width: 760px) {
main { width: min(100% - 24px, 1120px); padding-top: 32px; }
.grid, .flow, .meta-strip { grid-template-columns: 1fr; }
}
</style>
</head>
<body>
<main>
<div class="eyebrow">OpenProgram · Engineering design</div>
<h1>Repository structure</h1>
<p class="lead">本页定义 core-first monorepo:根目录 <code>openprogram/</code> 是 Agent 核心;Server、Web、Desktop 与 CLI 是使用核心的可运行应用,统一归入 <code>apps/</code>。目标是让目录直接表达运行边界和依赖方向,同时保持产品行为与用户数据兼容。</p>
<dl class="meta-strip">
<div><dt>Status</dt><dd>Approved target · migration in progress</dd></div>
<div><dt>Audience</dt><dd>Maintainers and reviewers</dd></div>
<div><dt>Scope</dt><dd>Core, applications, build and release layout</dd></div>
<div><dt>Excluded</dt><dd>Product behavior and public API changes</dd></div>
</dl>
<h2>1. 设计原则</h2>
<div class="grid">
<section class="card">
<h3>核心与应用分离</h3>
<p><code>openprogram/</code> 只承载 Agent、Context、Session、Provider、Program、Tool、Memory 与 Runtime 等核心能力。所有可运行入口放在 <code>apps/</code>。核心业务域不能依赖应用;Core import package 内为保留旧公开入口而存在的 compatibility adapter 可以单向委托应用 package,但不能承载应用实现。</p>
</section>
<section class="card">
<h3>按可验证边界拆分</h3>
<p>只有当一段代码具有独立输入、输出和测试入口时才移动。单纯因为文件长而引入 mixin、manager 或 facade 不属于有效拆分。</p>
</section>
<section class="card">
<h3>公共接口保持稳定</h3>
<p>迁移期间保留现有 CLI 命令、HTTP/WS 协议、端口、用户数据目录和必要的 Python import 兼容入口。每个应用独立移动、验证和提交。</p>
</section>
<section class="card">
<h3>当前设计与实施记录分离</h3>
<p>本 HTML 只描述长期结构;具体提交、测试结果和未完成工作记录在独立的 implementation ledger 中。</p>
</section>
</div>
<h2>2. 顶层职责</h2>
<pre>OpenProgram/
apps/
server/ FastAPI、HTTP、WebSocket、认证与静态资源托管
web/ Next.js Web 应用
desktop/ Electron 桌面应用
cli/ Python CLI 与 Ink TUI 应用
openprogram/ OpenProgram Agent 核心与 Python SDK
tests/ Python 测试:层级 / 产品域
docs/ 用户文档与设计记录
scripts/ 可执行或可导入的开发、验证和发布工具
references/ 参考实现快照与本地比较语料
website/ 官网源码
pyproject.toml 单一 Python aggregate project 配置
uv.lock Python 唯一锁文件
package.json npm workspace 入口
package-lock.json npm workspace 唯一锁文件</pre>
<p><code>.github/</code>、<code>.codegraph/</code> 和 <code>.superpowers/</code> 是仓库基础设施,不是产品运行时目录。根目录只保留两套生态入口与锁文件、项目元数据和入口文档。根 <code>pyproject.toml</code> 继续定义用户安装的单一 <code>openprogram</code> aggregate distribution,并与唯一 <code>uv.lock</code> 管理 Python 开发环境;G2 不创建没有成员的 <code>[tool.uv.workspace]</code>,也不把 Core、Server、CLI 拆成三个公开 distributions。Web、Desktop 和 Ink CLI 由根 <code>package.json</code> 声明 npm workspaces,共用唯一 <code>package-lock.json</code>。这些文件是源码与构建配置,不进入普通用户的安装目录。</p>
<p>Web 固定 React 18,Ink CLI 固定 React 19。npm 默认 hoisting 会让公共位置的 React peer 与类型依赖影响另一应用,因此根私有 package 以精确版本声明 React 18、<code>react-reconciler</code> 0.29 及其类型作为依赖布局约束;CLI 的 React 19 与 <code>react-reconciler</code> 0.33 保持在自己的 workspace。CLI 不依赖从根解析 React 的第三方 hook package。该约束不新增产品运行时功能,也不统一两个应用的 React 主版本。</p>
<p>核心 Python import package 位于根目录 <code>openprogram/</code>,它是 Server、CLI 与 Harness 共用的 Agent Core / SDK,不是普通应用。仓库当前没有第二个共享 Python package,因此不建立 <code>packages/</code>。源码归属与发布 distribution 是两个边界:项目保持一个用户可安装的 <code>openprogram</code> wheel,其中继续包含 <code>openprogram</code>、<code>openprogram_server</code> 和 <code>openprogram_cli</code>。应用专用脚本放在 <code>apps/<name>/scripts/</code>,跨应用的开发与验证命令放在 <code>scripts/</code>,正式 runtime、installer 与 release 命令放在 <code>scripts/release/</code>。</p>
<p>现有公开地址继续提供 <code>scripts/install-release.sh</code>。该文件只承担兼容分发:完整 checkout 中执行 <code>scripts/release/install-release.sh</code>;被单独下载时,从同一已验证 repository 和不可变 tag 下载正式实现到临时文件后执行。<code>scripts/install.sh</code>、<code>scripts/install.ps1</code>、<code>scripts/refresh-local-app.sh</code> 和 <code>scripts/promote_stable.sh</code> 保持路径不变;本地刷新继续复用归入 <code>scripts/release/</code> 的 staging 与版本验证 helper。</p>
<p>当前 checkout 已完成发布脚本和 Node workspace 整理:正式发布脚本归入 <code>scripts/release/</code>;Web、Desktop 和 Ink CLI 由根 npm workspace 管理并共享唯一 <code>package-lock.json</code>;Python Core 保持在根目录 <code>openprogram/</code>。根 <code>pyproject.toml</code> 继续发布包含 Core、Server 和 Python CLI 的单一 <code>openprogram</code> wheel。</p>
<p>OpenProgram 不随安装包分发默认 skill;产品内置工作流由 Programs 提供。<code>openprogram/skills/</code> 只实现 AgentSkills 兼容的加载能力,来源为远端缓存、插件、用户目录 <code>~/.openprogram/skills/</code> 和当前项目 <code><cwd>/skills/</code>。仓库不建立 <code>skills_bundled/</code> 或第二份隐藏副本。</p>
<p><code>tests/</code> 中的 Python 用例使用 <code>tests/<layer>/<product-domain>/test_*.py</code>,按最强实际依赖选择层级。具体边界由 <a href="testing/test-system.html">Testing system</a> 和仓库 contracts 维护。</p>
<h3>核心 package 与应用 package</h3>
<p><code>openprogram/</code> 的一级目录只保留可独立说明的核心产品域或稳定协议边界,例如 <code>agent/</code>、<code>programs/</code>、<code>context/</code>、<code>memory/</code> 和 <code>providers/</code>。子能力必须归入所属产品域;Context 的 Git DAG 位于 <code>context/git/</code>,不再占用独立的 <code>contextgit/</code> 一级目录。FastAPI route、WebSocket handler、静态前端托管、Electron 与界面代码不属于核心 package。</p>
<p><code>apps/server/openprogram_server/</code> 是 Python 服务应用;FastAPI routes、WebSocket actions、owner auth、静态资源托管与 Server-only helpers 位于其 <code>_webui/</code> 内。兼容期内这些文件仍以既有 <code>openprogram.webui.*</code> 名称加载,根 package 只负责定位同一份应用源码,不复制模块状态。<code>apps/cli/</code> 是 CLI 应用 workspace,Ink TUI 使用其中的 <code>src/</code>、<code>tests/</code> 和 <code>package.json</code>,Python parser、dispatch、Rich fallback 与 setup flow 位于 <code>apps/cli/python/openprogram_cli/</code>。<code>openprogram.cli</code> 只保留兼容 loader 和 module entry;两处兼容边界均不承载新的应用实现。</p>
<p>工具配置、收藏夹和其他 profile 状态属于用户数据,写入 <code>~/.openprogram[-profile]/</code>,不作为源码或 wheel seed 保存在 <code>openprogram/</code>。兼容读取只检查旧版 <code>openprogram/webui/</code> package 位置并一次性复制到 profile state;它不能依赖已迁移到 <code>apps/server</code> 的 Server module 路径。</p>
<p>仓库当前不建立 <code>packages/</code>。只有出现第二个被两个及以上应用消费、具有独立稳定公共入口的共享 package 时,才重新评估是否需要该目录;不为未来可能复用的协议、UI 或配置预建空 package。</p>
<p><code>__pycache__/</code>、<code>.DS_Store</code>、构建产物和 Finder 冲突副本不是仓库结构。它们保持 Git ignored,可安全清理,但不能用作代码分层或验收证据。用户安装的 <code>programs/applications/</code> checkout 属于本地产品数据,不随源码目录整理删除。</p>
<h2>3. 超长文件处理</h2>
<div class="table-wrap">
<table>
<thead><tr><th>文件</th><th>现状</th><th>决定</th><th>边界</th></tr></thead>
<tbody>
<tr>
<td><code>openprogram/cli/</code></td>
<td>只保留兼容 loader、<code>python -m openprogram.cli</code> 入口和边界说明</td>
<td><span class="state yes">已迁移</span></td>
<td>应用实现位于 <code>apps/cli/python/openprogram_cli/</code>;源码开发通过 editable install 或 <code>uv run</code> 保留 <code>python -m openprogram</code> 与 <code>python -m openprogram.cli</code>,wheel 额外保留 canonical <code>python -m openprogram_cli</code> 和 console script。</td>
</tr>
<tr>
<td><code>apps/desktop/main.js</code></td>
<td>窗口、更新、WebView、标签转移和菜单共处,接近 4,000 行</td>
<td><span class="state later">分批实施</span></td>
<td>纯菜单几何计算已提取到 <code>menu-geometry.js</code>,纯 worker recovery 状态已提取到 <code>worker-recovery-state.js</code>,transfer payload validation 已提取到 <code>tab-transfer-validation.js</code>;HTTP 探测、进程与窗口生命周期、原生 WebView 所有权变更、标签转移协调和菜单宿主只在各自具备执行级验收后移动。</td>
</tr>
<tr>
<td><code>apps/web/lib/desktop-bridge.ts</code></td>
<td>类型、视图状态和标签转移共处,接近 2,000 行</td>
<td><span class="state later">分批实施</span></td>
<td>WebTab 与 Desktop 服务公共类型已提取到 <code>desktop-bridge-types.ts</code>,preload-facing transfer contracts 已提取到 <code>desktop-transfer-types.ts</code>,并由原文件 re-export;aggregate bridge、view state、journal persistence 与 tab transfer coordination 保持在原处,直到识别出可独立执行验证的职责。</td>
</tr>
<tr>
<td><code>runtime.py</code>、<code>runner.py</code>、<code>resource_governance.py</code></td>
<td>文件较长,但核心状态机和不变量集中</td>
<td><span class="state no">不按长度拆</span></td>
<td>只有出现可独立验证的职责或重复实现时再拆;不引入 mixin 层。</td>
</tr>
<tr>
<td>大型 Web / Desktop 检查脚本</td>
<td>少数脚本同时包含 fixture、场景执行和多项结构断言,最长超过 3,000 行</td>
<td><span class="state later">后续批次</span></td>
<td>先提取共享 fixture 与可独立执行的场景;保留原命令入口,不为缩短文件引入测试框架。</td>
</tr>
</tbody>
</table>
</div>
<h2>4. 文档信息架构</h2>
<div class="grid">
<section class="card">
<h3>用户文档</h3>
<p><code>start/</code>、<code>install/</code>、<code>capabilities/</code>、<code>interfaces/</code>、<code>models/</code>、<code>integrations/</code>、<code>server/</code>、<code>reference/</code>。进入公开导航和搜索。</p>
</section>
<section class="card">
<h3>当前设计</h3>
<p><code>reference/design/</code>。按产品域展示当前有效的设计说明;UI 文档按基础、会话编辑、浏览器标签、设置目录、工作区分组。</p>
</section>
<section class="card">
<h3>实施记录</h3>
<p>与设计页相邻的 implementation ledger 以及 <code>reference/design/plans/</code>,只记录提交、门禁、剩余任务和实施历史,不把执行过程写进概念设计。</p>
</section>
<section class="card">
<h3>内部计划</h3>
<p><code>docs/superpowers/</code> 保留在 Git 中供维护者追溯,不进入公开站点。仍被当前设计引用的 <code>reference/design/plans/</code> 继续发布,并保持独立 Plans 分组。</p>
</section>
</div>
<p class="note">根目录 <code>README.md</code> 解释完整产品;<code>openprogram/README.md</code> 解释核心 SDK;<code>apps/server/README.md</code>、<code>apps/web/README.md</code>、<code>apps/desktop/README.md</code> 和 <code>apps/cli/README.md</code> 分别解释各运行入口。入口 README 只描述当前职责、依赖方向和验证命令。</p>
<h2>5. 采用与拒绝</h2>
<div class="table-wrap">
<table>
<thead><tr><th>选择</th><th>结论</th><th>原因</th></tr></thead>
<tbody>
<tr><td>根目录 <code>openprogram/</code> 核心 + <code>apps/</code> 运行入口</td><td><span class="state now">采用</span></td><td>核心保持项目一级入口,Server、Web、Desktop 与 CLI 的应用边界在 <code>apps/</code> 中可见。</td></tr>
<tr><td>move-only 应用迁移</td><td><span class="state now">采用</span></td><td>先移动路径并修复构建、import 和脚本,再单独处理内部大文件拆分,避免同时改变行为。</td></tr>
<tr><td>通过导航分组整理文档</td><td><span class="state now">采用</span></td><td>避免批量移动文件造成旧链接失效。</td></tr>
<tr><td>把 <code>openprogram/</code> 命名为 backend</td><td><span class="state no">拒绝</span></td><td>OpenProgram 是 Agent 核心与 SDK,Server 只是它的一个应用入口。</td></tr>
<tr><td>保持 Server 实现在 <code>openprogram/webui/</code></td><td><span class="state no">拒绝</span></td><td>该目录混合服务端、旧静态 UI、构建产物和 Web 命名,无法表达真实职责。</td></tr>
<tr><td>按固定行数自动拆文件</td><td><span class="state no">拒绝</span></td><td>行数不能证明职责独立,容易产生只转发调用的模块。</td></tr>
<tr><td>为拆分引入新的依赖注入框架</td><td><span class="state no">拒绝</span></td><td>现有模块函数和显式参数足以保留边界。</td></tr>
<tr><td>一次提交移动所有应用</td><td><span class="state no">拒绝</span></td><td>应用分别迁移和验证;最终状态统一进入 <code>apps/</code>,但不以一个不可审查的提交完成。</td></tr>
</tbody>
</table>
</div>
<h2>6. 验收合同</h2>
<ul>
<li><code>openprogram/</code> 不包含 FastAPI route、WebSocket handler、前端源码、Electron 源码或持久化前端构建产物。</li>
<li><code>apps/server</code>、<code>apps/web</code>、<code>apps/desktop</code> 和 <code>apps/cli</code> 均有独立入口、README、测试命令和构建命令。</li>
<li><code>openprogram.cli.build_parser</code> 在兼容期保持可导入,完整命令树、console script 与 TUI 行为一致。</li>
<li>默认端口仍为 <code>18100</code>;同一 Worker 提供 API、WebSocket 和 Web 静态文件,不恢复旧双端口 Next.js Server。</li>
<li>浏览器与 Electron 继续加载同一套 Web 应用;Electron 专属能力只通过 preload/IPC 暴露。</li>
<li>Agent Harness 可以直接导入 <code>openprogram</code>,不依赖 Server、Web 或 Desktop。</li>
<li>旧 <code>openprogram/webui/static/</code> 和无生产调用的旧双端口实现被删除。</li>
<li>前端构建产物只进入 ignored build/staging 目录和最终发行包,不作为核心源码长期维护。</li>
<li>可变 profile 状态不进入源码树或 wheel;旧 package-local JSON 只作为升级兼容输入读取,不在新版本继续跟踪。</li>
<li>公开文档构建不包含 <code>docs/superpowers/</code>,所有公开链接有效。</li>
<li>设计导航按产品功能分组;源文件路径保持不变。</li>
<li>Git tracked 顶层目录必须在结构 contract 中声明,根目录不出现开发脚本;不含实现的 source-checkout module entry 必须被 contract 单独列出。</li>
<li>有效 README、技能和安装说明不得引用已删除的源码根路径。</li>
<li>根目录不再保留旧的 <code>web/</code>、<code>desktop/</code> 和 <code>cli/</code> workspace;有效源码分别位于对应 <code>apps/</code> 子目录。</li>
<li>一级 Python 包的目录 README 与 <code>__init__.py</code> docstring 保持同步。</li>
<li>每个结构批次独立提交,先通过直接相关测试,再进入规格与质量复核。</li>
</ul>
<p class="note">实现状态和可复现命令见 <a href="repository-structure-implementation.html">Repository structure implementation ledger</a>。</p>
</main>
</body>
</html>