openxiangda 2.0.0-alpha.98 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (150) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +23 -20
  3. package/bin/distribution/commands.js +55 -0
  4. package/bin/distribution/launcher.js +49 -0
  5. package/bin/distribution/migrate.js +60 -0
  6. package/bin/distribution/releases.js +52 -0
  7. package/bin/distribution/skills.js +80 -0
  8. package/bin/distribution/update.js +68 -0
  9. package/bin/distribution/workspace.js +85 -0
  10. package/bin/run.js +9 -11
  11. package/dist/browser/AuthoritativeSelector.d.ts +3 -2
  12. package/dist/browser/AuthoritativeSelector.d.ts.map +1 -1
  13. package/dist/browser/AuthoritativeSelector.js +39 -24
  14. package/dist/browser/AuthoritativeSelector.js.map +1 -1
  15. package/dist/browser/Shell.d.ts.map +1 -1
  16. package/dist/browser/Shell.js +24 -18
  17. package/dist/browser/Shell.js.map +1 -1
  18. package/dist/browser/admin-information-architecture.d.ts +4 -2
  19. package/dist/browser/admin-information-architecture.d.ts.map +1 -1
  20. package/dist/browser/admin-information-architecture.js +2 -2
  21. package/dist/browser/admin-information-architecture.js.map +1 -1
  22. package/dist/browser/application.d.ts.map +1 -1
  23. package/dist/browser/application.js +3 -3
  24. package/dist/browser/application.js.map +1 -1
  25. package/dist/browser/components/platform-fields/AttachmentFileList.d.ts.map +1 -1
  26. package/dist/browser/components/platform-fields/AttachmentFileList.js +5 -1
  27. package/dist/browser/components/platform-fields/AttachmentFileList.js.map +1 -1
  28. package/dist/browser/components/platform-fields/MobileFieldControls.d.ts.map +1 -1
  29. package/dist/browser/components/platform-fields/MobileFieldControls.js +2 -2
  30. package/dist/browser/components/platform-fields/MobileFieldControls.js.map +1 -1
  31. package/dist/browser/components/platform-fields/ResourceReferenceField.d.ts +4 -2
  32. package/dist/browser/components/platform-fields/ResourceReferenceField.d.ts.map +1 -1
  33. package/dist/browser/components/platform-fields/ResourceReferenceField.js +2 -2
  34. package/dist/browser/components/platform-fields/ResourceReferenceField.js.map +1 -1
  35. package/dist/browser/components/platform-fields/SubtableField.d.ts.map +1 -1
  36. package/dist/browser/components/platform-fields/SubtableField.js +60 -80
  37. package/dist/browser/components/platform-fields/SubtableField.js.map +1 -1
  38. package/dist/browser/components/platform-fields/rich-text-value.d.ts.map +1 -1
  39. package/dist/browser/components/platform-fields/rich-text-value.js +11 -1
  40. package/dist/browser/components/platform-fields/rich-text-value.js.map +1 -1
  41. package/dist/browser/components/resource/GeneratedResourceCrud.d.ts.map +1 -1
  42. package/dist/browser/components/resource/GeneratedResourceCrud.js +33 -114
  43. package/dist/browser/components/resource/GeneratedResourceCrud.js.map +1 -1
  44. package/dist/browser/components/resource/GeneratedResourceForm.d.ts +3 -1
  45. package/dist/browser/components/resource/GeneratedResourceForm.d.ts.map +1 -1
  46. package/dist/browser/components/resource/GeneratedResourceForm.js +11 -4
  47. package/dist/browser/components/resource/GeneratedResourceForm.js.map +1 -1
  48. package/dist/browser/components/resource/RecordChangeHistory.d.ts +12 -0
  49. package/dist/browser/components/resource/RecordChangeHistory.d.ts.map +1 -0
  50. package/dist/browser/components/resource/RecordChangeHistory.js +61 -0
  51. package/dist/browser/components/resource/RecordChangeHistory.js.map +1 -0
  52. package/dist/browser/components/resource/RecordDetailFrame.d.ts +30 -0
  53. package/dist/browser/components/resource/RecordDetailFrame.d.ts.map +1 -0
  54. package/dist/browser/components/resource/RecordDetailFrame.js +25 -0
  55. package/dist/browser/components/resource/RecordDetailFrame.js.map +1 -0
  56. package/dist/browser/components/resource/ResourceFormFrame.d.ts +2 -1
  57. package/dist/browser/components/resource/ResourceFormFrame.d.ts.map +1 -1
  58. package/dist/browser/components/resource/ResourceFormFrame.js +4 -4
  59. package/dist/browser/components/resource/ResourceFormFrame.js.map +1 -1
  60. package/dist/browser/components/resource/StandardResourcePages.d.ts +2 -4
  61. package/dist/browser/components/resource/StandardResourcePages.d.ts.map +1 -1
  62. package/dist/browser/components/resource/StandardResourcePages.js +8 -26
  63. package/dist/browser/components/resource/StandardResourcePages.js.map +1 -1
  64. package/dist/browser/components/resource/SurfaceFields.d.ts +5 -3
  65. package/dist/browser/components/resource/SurfaceFields.d.ts.map +1 -1
  66. package/dist/browser/components/resource/SurfaceFields.js +37 -12
  67. package/dist/browser/components/resource/SurfaceFields.js.map +1 -1
  68. package/dist/browser/components/resource/resource-import.d.ts +16 -1
  69. package/dist/browser/components/resource/resource-import.d.ts.map +1 -1
  70. package/dist/browser/components/resource/resource-import.js +58 -34
  71. package/dist/browser/components/resource/resource-import.js.map +1 -1
  72. package/dist/browser/components/resource/useResourceFormDrafts.d.ts.map +1 -1
  73. package/dist/browser/components/todo/ApplicationTodoCenterPage.d.ts +5 -2
  74. package/dist/browser/components/todo/ApplicationTodoCenterPage.d.ts.map +1 -1
  75. package/dist/browser/components/todo/ApplicationTodoCenterPage.js +19 -14
  76. package/dist/browser/components/todo/ApplicationTodoCenterPage.js.map +1 -1
  77. package/dist/browser/components/workflow/StandardWorkflowPages.d.ts +21 -5
  78. package/dist/browser/components/workflow/StandardWorkflowPages.d.ts.map +1 -1
  79. package/dist/browser/components/workflow/StandardWorkflowPages.js +175 -192
  80. package/dist/browser/components/workflow/StandardWorkflowPages.js.map +1 -1
  81. package/dist/browser/components/workflow/WorkflowRecordEditor.d.ts +2 -1
  82. package/dist/browser/components/workflow/WorkflowRecordEditor.d.ts.map +1 -1
  83. package/dist/browser/components/workflow/WorkflowRecordEditor.js +7 -4
  84. package/dist/browser/components/workflow/WorkflowRecordEditor.js.map +1 -1
  85. package/dist/browser/platform-client.d.ts +10 -4
  86. package/dist/browser/platform-client.d.ts.map +1 -1
  87. package/dist/browser/platform-client.js +78 -17
  88. package/dist/browser/platform-client.js.map +1 -1
  89. package/dist/browser/record-detail.css +115 -0
  90. package/dist/browser/runtime.d.ts.map +1 -1
  91. package/dist/browser/runtime.js +26 -2
  92. package/dist/browser/runtime.js.map +1 -1
  93. package/dist/browser/styles.css +1 -0
  94. package/dist/browser/workflow-launch.d.ts +4 -1
  95. package/dist/browser/workflow-launch.d.ts.map +1 -1
  96. package/dist/browser/workflow-launch.js +32 -0
  97. package/dist/browser/workflow-launch.js.map +1 -1
  98. package/dist/core.d.ts +1 -1
  99. package/dist/core.d.ts.map +1 -1
  100. package/dist/core.js.map +1 -1
  101. package/documentation/AGENTS.md +26 -0
  102. package/documentation/administration.md +27 -0
  103. package/documentation/application-foundation.md +162 -0
  104. package/documentation/appspec.md +152 -0
  105. package/documentation/backend.md +132 -0
  106. package/documentation/concepts.md +61 -0
  107. package/documentation/data-authz.md +62 -0
  108. package/documentation/delivery.md +110 -0
  109. package/documentation/development.md +32 -0
  110. package/documentation/field-components.md +236 -0
  111. package/documentation/frontend.md +269 -0
  112. package/documentation/getting-started.md +66 -0
  113. package/documentation/interaction-patterns.md +56 -0
  114. package/documentation/manifest.json +120 -0
  115. package/documentation/product-design.md +142 -0
  116. package/documentation/public-access.md +167 -0
  117. package/documentation/reference/cli.md +27 -0
  118. package/documentation/reference/mcp.md +649 -0
  119. package/documentation/testing.md +63 -0
  120. package/documentation/upgrading.md +39 -0
  121. package/documentation/workflow-events.md +181 -0
  122. package/launcher-skill/openxiangda/SKILL.md +24 -0
  123. package/package.json +72 -9
  124. package/releases/2.0.0.json +50 -0
  125. package/skills/manifest.json +2 -2
  126. package/skills/openxiangda-v2/SKILL.md +64 -51
  127. package/skills/openxiangda-v2/agents/openai.yaml +2 -2
  128. package/skills/openxiangda-v2/references/administration.md +27 -0
  129. package/skills/openxiangda-v2/references/application-foundation.md +162 -0
  130. package/skills/openxiangda-v2/references/appspec.md +132 -47
  131. package/skills/openxiangda-v2/references/backend.md +101 -248
  132. package/skills/openxiangda-v2/references/cli.md +27 -0
  133. package/skills/openxiangda-v2/references/concepts.md +61 -0
  134. package/skills/openxiangda-v2/references/data-authz.md +36 -388
  135. package/skills/openxiangda-v2/references/delivery.md +110 -49
  136. package/skills/openxiangda-v2/references/development.md +32 -0
  137. package/skills/openxiangda-v2/references/field-components.md +236 -0
  138. package/skills/openxiangda-v2/references/frontend.md +254 -280
  139. package/skills/openxiangda-v2/references/getting-started.md +66 -0
  140. package/skills/openxiangda-v2/references/interaction-patterns.md +56 -0
  141. package/skills/openxiangda-v2/references/mcp.md +649 -0
  142. package/skills/openxiangda-v2/references/product-design.md +142 -0
  143. package/skills/openxiangda-v2/references/public-access.md +92 -84
  144. package/skills/openxiangda-v2/references/testing.md +45 -56
  145. package/skills/openxiangda-v2/references/upgrading.md +39 -0
  146. package/skills/openxiangda-v2/references/workflow-events.md +143 -266
  147. package/skills/openxiangda-v2/references/architecture.md +0 -9
  148. package/skills/openxiangda-v2/references/commands.md +0 -21
  149. package/skills/openxiangda-v2/references/discovery.md +0 -15
  150. package/skills/openxiangda-v2/references/workspace.md +0 -62
@@ -1,295 +1,269 @@
1
- # OpenXiangda 2.0 Frontend
2
-
3
- Read [Data and authorization](data-authz.md) for every resource or permission
4
- change and keep the generated [workspace contract](workspace.md) authoritative.
5
-
6
- Use the official Vite + React Router + Refine Core + Ant Design template. Import the platform-owned runtime from `openxiangda/react`, field controls from `openxiangda/field-kit` and clients/types from `openxiangda/core`. Do not copy these implementations into application source and do not add per-resource function wrappers.
7
-
8
- Generated desktop lists already own row selection, atomic batch update/delete,
9
- CSV/XLS/XLSX preview import, export, filters, column settings and density. Keep
10
- imports at 100 rows per transaction, use exact declared labels or field codes,
11
- and leave attachment values to the platform managed-file component.
12
-
13
- Declare searchable and filterable fields on the resource and let the standard
14
- CRUD renderer compose one toolbar. Keyword search spans the declared searchable
15
- fields; all declared filters open on demand in the platform modal. Business
16
- columns are the default; audit columns and density are optional list settings.
17
- Desktop create/edit opens a drawer over the retained list; existing full-page
18
- routes use the same form lifecycle. The renderer retains values after failed
19
- saves, prevents repeated submission and asks before discarding an edited form.
20
- Declare shared form/detail groups in `crud[].sections` with `title` and `fields`.
21
- Form/detail field selections remain exhaustive and ordered. Use lightweight
22
- section headings, two ordinary columns on PC and one on mobile; small forms
23
- need no artificial group. Do not copy a list/form renderer or add application
24
- search toolbars, field ordering maps or authorization/debug explanations.
25
-
26
- Read the current user, complete application-role union and capabilities from the platform runtime authorization endpoint. There is no application-selected active role or identity switch. The standard Shell owns the optional Perspective selector. Use `hasReadCapability` for navigation, list/detail visibility and readable fields; continue to use `hasCapability` for create/update/delete, workflows and custom actions. The standard client sends the selected Perspective automatically, and the server repeats the projection for rows, fields and RLS. Never implement resource-specific frontend filters for it. Strip unauthorized fields from create and update payloads and never treat a disabled input as server authorization. Verify with `pnpm openxiangda check`.
27
-
28
- Use the standard `Shell` unchanged. It owns the authenticated user's display
29
- name, account identifier, safe avatar upload, localized application-role names,
30
- Perspective selection, collapsible menu groups and navigation scroll restoration. It
31
- also owns directory field requests under the current logged-in user's role
32
- union. Never fetch identity for the header, display role codes, add a second
33
- identity selector, or copy Shell and
34
- directory-client implementations into application source.
35
-
36
- For a custom application role-management page, use only the typed functions in
37
- `openxiangda/core`: `loadRoleManagementCatalog`, `listRoleMemberships`,
38
- `searchRoleManagementUsers`, the membership mutations, and the
39
- role-management-grant mutations. The catalog's `roleManagement` projection
40
- drives button visibility, while the platform repeats the check for every
41
- request. The “Custom role-management pages” section in
42
- [Data and authorization](data-authz.md) defines the delegation and CAS
43
- contract. Do not use the developer control-plane client in browser code.
44
-
45
- The application owns the editable admin information architecture through the
46
- typed `defineAdminNavigation`, `adminNavigationGroup`, `adminResourcePage`,
47
- and `adminOperationPage` helpers in `openxiangda/config`. The compiler emits
48
- all reachable `adminPages`, but the Shell renders only pages explicitly
49
- referenced by generated `adminNavigation`, then applies current-user access as
50
- a final filter. Page existence, route reachability and menu visibility are
51
- separate: detail/new/edit/dynamic/handoff pages never become menu entries by
52
- discovery, and permission logic cannot create entries. Read
53
- `openxiangda://workspace/contracts` or call `contract_describe`, then copy
54
- `data.adminNavigationAuthoring.suggestion.expression` once into
55
- `frontend.admin.navigation` with the listed `openxiangda/config` imports. The
56
- proposal is deterministic and editable; the compiler/runtime never invokes it
57
- or appends newly added resources later.
58
-
59
- Generated desktop resource CRUD routes use the compiler-owned
60
- `/admin/resources/<resourceCode>...` namespace and always render inside the
61
- platform's one Shell. Their independent mobile admin surface is projected from
62
- the same generated route catalog under `/m/admin/resources/<resourceCode>...`.
63
- Generated pages consume that catalog for every list/create/detail/edit/back
64
- navigation; do not reconstruct root resource paths in application code. Root
65
- and ordinary `/m/...` paths remain available to explicit user routes. The
66
- compiler rejects an explicit route whose canonical path shape conflicts with
67
- any explicit or platform-generated route, including dynamic routes that differ
68
- only by parameter name. Do not add aliases, redirects or route-order branches
69
- for earlier alpha paths.
70
-
71
- Declare the application-wide admin boundary at `frontend.admin.access` with
72
- `allOf` and/or `anyOf` capability arrays. Import generated `adminAccess` and
73
- pass it to `OpenXiangdaApplication`; the runtime evaluates that same immutable
74
- expression before `/admin/**`, `/m/admin/**`, the Shell, generated resources or
75
- admin contribution pages mount. Portal shortcuts may use the exported pure
76
- `isAdminAccessAllowed` predicate, while Data/App/Workflow APIs remain the
77
- server-side authority.
78
-
79
- Declare custom routes in `openxiangda.config.ts`, import the generated
80
- `appRoutes`, and bind every route key to exactly one local React page with
81
- `defineApplicationContributions` from `openxiangda/react`. Pass the result to
82
- `OpenXiangdaApplication`. The runtime registers `surface: 'admin'` routes inside
83
- the one platform Shell; do not wrap them in another Shell. It renders
84
- `surface: 'user'` routes without the admin Shell so mobile/user experiences can
85
- own their page layout without creating another router, identity provider or
86
- permission store. Only static admin routes explicitly referenced by
87
- `frontend.admin.navigation` enter the menu. Hidden routes retain the same
88
- `capability` or `access.allOf/anyOf`, including ancestor constraints. Keep admin
89
- operation pages under `/admin/operations`; parameterized routes remain
90
- reachable but cannot be menu references. `defineAdminContributions` remains an
91
- admin-only helper and intentionally rejects `user` routes.
92
-
93
- When a Workflow Surface must link back to an application-owned business record,
94
- declare `detailRouteCode: { desktop, mobile }` on that resource. Both codes must
95
- reference explicit `surface: 'user'` routes that require the resource's read
96
- capability. The desktop path must stay outside `/m`, the mobile path must start
97
- with `/m/`, and each path must contain exactly one bounded dynamic record
98
- parameter; its name may follow the application domain, such as
99
- `:applicationId`. The compiler emits the pair in the resource Contract and in
100
- `resourceDefinitions`; the platform substitutes the record id from the
101
- authorized Workflow Surface. Never derive `/<resourceCode>/<recordId>`, add an
102
- alias, or build the destination in the browser. Omitting the declaration means
103
- there is no application business-record target; a broken published mapping
104
- fails closed.
105
-
106
- Wrap bespoke admin content in `OpenXiangdaAdminPage` and use Ant Design default
107
- components. The platform UI provider supplies Chinese locale and contextual
108
- feedback only. Do not generate appearance preferences, color settings or palette
109
- registries. Keep component CSS scoped and verify real PC/mobile interactions,
110
- including selectors, dialogs, form submission and failure recovery.
111
-
112
- Declare application-owned login visuals with optional
113
- `frontend.authentication`. The only supported account contract is
114
- `existing-platform-users-only` with `registration.mode: 'reject'`; use exact
115
- desktop `/login` and mobile `/m/login`, each pointing to a static protected user
116
- default route. Import generated `authenticationSurfaces` and bind an exact
117
- desktop/mobile renderer map alongside `appRoutes`:
1
+ # 前端架构
118
2
 
119
- ```tsx
120
- const contributions = defineApplicationContributions(
121
- { routes: appRoutes, authenticationSurfaces },
122
- {
123
- pages,
124
- authentication: {
125
- applicationLogin: DesktopLogin,
126
- applicationLoginMobile: MobileLogin,
3
+ 状态:Vite/Refine 已成为 OpenXiangda 2.0 唯一默认前端栈。决策与实测见
4
+ [核心架构](concepts.md)。
5
+
6
+ ## 默认技术栈
7
+
8
+ - Vite 7:开发服务器与生产构建,只监听 `127.0.0.1`;
9
+ - React 19 + React Router:普通应用路由;
10
+ - Refine Core:资源查询、分页、排序和 mutation 状态;
11
+ - Ant Design 6:B 端页面组件。
12
+
13
+ 不维护 Umi/Pro 双栈,也不在新模板中依赖已退休的前端框架包或旧身份 Provider。
14
+
15
+ ## 数据和权限
16
+
17
+ `modules/` 中的业务模型由编译器派生 DataResource 契约,`openxiangda.config.ts` 声明页面
18
+ capability、应用角色和数据策略。前端只根据当前登录用户完整应用角色并集的
19
+ `capabilityCodes` 隐藏页面、按钮和只读字段;这些只是展示保护,平台 Data API
20
+ 每次请求仍按同一角色并集做权威的行、字段和操作授权。
21
+
22
+ 自定义角色管理页只调用 `openxiangda/core` 的角色管理 SDK。目录搜索使用
23
+ `searchRoleManagementUsers`,页面按钮按 `loadRoleManagementCatalog()` 返回的
24
+ `roleManagement` 投影显示,但前端显示不能替代服务端复核。不要在应用中复制角色表、
25
+ 权限表或通过 NestJS 转发开发者凭据。
26
+
27
+ 列表查询必须使用服务端过滤、排序和分页;新增、读取、更新、删除和文件上传都经过
28
+ 可替换 Data API adapter。不要调用自定义 Nest CRUD、Function 或 Workflow 来绕过
29
+ Data API。只有真正需要事务或外部系统的动作才使用同源 `/api`。
30
+
31
+ 默认仪器模块有 30 个字段,其中 `id/revision` 是 Data API 系统字段,28 个业务
32
+ 字段由资源声明。新增、编辑和详情共用同一份字段元数据。五个边界字段使用五个独立
33
+ capability,不使用角色名或影子字段判断。
34
+
35
+ 字段策略用 `create` / `update` 分别声明能力;显式空数组表示拒绝。学校管理员通过
36
+ `unrestrictedRoleCodes` 跳过行谓词,但仍受资源和字段能力
37
+ 约束;学院管理员创建时可填写五个边界字段,更新时禁止修改 `collegeId`;仪器管理员
38
+ 更新时禁止修改这五个字段。表单提交必须从 payload 删除无权字段。
39
+
40
+ DataQuery 使用有界 where 条件树,支持 and/or/not。标准列表的筛选、关键词、分页和导出共用已声明字段与同一查询条件,不在页面重写查询协议。
41
+
42
+ ## 标准后台扩展
43
+
44
+ 默认 CRUD、统一 Shell、current-user 权限和 Data Provider 都由 `openxiangda/react`
45
+ 维护;应用拥有后台信息架构声明。页面实现、路由可达、菜单可见是三个不同合同:
46
+
47
+ - `resource.generated` 与 `frontend.routes` 决定有哪些标准页或 operation route;
48
+ - 编译器把全部可达页生成到 `adminPages`,detail/new/edit/handoff 可以存在但默认不进菜单;
49
+ - `frontend.admin.navigation` 是菜单的唯一权威声明,Shell 只渲染其中引用的页面,最后再按
50
+ current-user 权限过滤。权限不能发现或创建菜单项。
51
+
52
+ 生成式资源后台固定使用 `/admin/resources/<resourceCode>` 命名空间,detail、create、
53
+ update 分别追加 `/:id`、`/new`、`/:id/edit`;独立移动后台面使用同一编译目录投影出的
54
+ `/m/admin/resources/<resourceCode>...`。所有桌面生成页由平台放进唯一 `Shell`,页面内部
55
+ 跳转也只消费生成路径。普通 `/activities`、`/m/activities` 等产品路径留给 `user`
56
+ route。显式 route 与任何平台生成 route 的路径形状冲突时编译失败;即使动态参数名不同,
57
+ 例如 `/:id` 与 `/:recordId`,也视为同一路径。不要添加旧根路径别名、重定向或通过注册顺序
58
+ 解决冲突。
59
+
60
+ 使用 typed helper 声明业务分组、用户名称、顺序、图标与页面引用,不维护 raw JSON:
61
+
62
+ ```ts
63
+ import {
64
+ adminNavigationGroup,
65
+ adminOperationPage,
66
+ adminResourcePage,
67
+ defineAdminNavigation,
68
+ defineOpenXiangdaApp,
69
+ } from 'openxiangda/config';
70
+
71
+ export default defineOpenXiangdaApp({
72
+ // ...
73
+ frontend: {
74
+ root: 'apps/web',
75
+ routes: [{
76
+ code: 'instrument-import',
77
+ path: '/admin/operations/instrument-import',
78
+ label: '仪器导入',
79
+ surface: 'admin',
80
+ }],
81
+ user: { applicationTodoCenter: true },
82
+ admin: {
83
+ access: { anyOf: ['app:example:admin:view'] },
84
+ navigation: defineAdminNavigation([
85
+ adminNavigationGroup('instrument-center', '仪器管理', [
86
+ adminResourcePage('instruments'),
87
+ adminOperationPage('instrument-import'),
88
+ ], { icon: 'database', order: 100 }),
89
+ ]),
127
90
  },
128
91
  },
129
- );
92
+ });
130
93
  ```
131
94
 
132
- Renderers receive only `ApplicationLoginSurfaceProps`: safe method descriptors,
133
- state/error metadata, normalized `returnTo`, and platform callbacks. They never
134
- receive credentials, provider configuration, OAuth state, tokens, roles, or
135
- authorization facts. The platform owns the one Router and authentication
136
- boundary, tokenless v2 facade, Secure HttpOnly session/refresh family and
137
- logout. Do not put login in `appRoutes`, call v1 auth APIs, persist tokens, or
138
- translate network/5xx and authenticated 403 states into login. Generated
139
- `platformAuthManifest` is the independent login QA denominator and does not
140
- change protected user route counts.
141
-
142
- For a deliberately anonymous user page, first read
143
- [Anonymous public access](public-access.md), declare one static `surface: 'user'`
144
- route and bind it through `frontend.publicAccess`. The policy must name one
145
- resource, its writable/returnable fields and only the bounded operations needed
146
- by the page (`draft.read`, `draft.update`, `validate`, `create`, `own.list`,
147
- `own.read`). A public route must not also declare `capability` or `access`.
148
- Pass generated `anonymousPublicAccess` to
149
- `<OpenXiangdaApplication publicAccess={anonymousPublicAccess}>`, then use
150
- `createAnonymousPublicClient({ routeCode })` from `openxiangda/react` inside
151
- the page. This client owns bootstrap, current-draft CAS, named validation,
152
- managed upload, idempotent submission and owner-scoped list/detail calls. Do
153
- not call the general Native Data API, store an identity in local storage, or
154
- derive ownership from IP, user-agent or a browser fingerprint. The HttpOnly
155
- browser credential identifies only this browser profile; clearing it or using
156
- another browser loses access by design.
157
-
158
- This is also the whole-application composition contract: pass the resulting
159
- `contributions` to `OpenXiangdaApplication` and let that component remain the
160
- only owner of `BrowserRouter`, `RuntimeBoundary`, Refine, generated resource and
161
- Workflow routes, and the admin Shell. A user page may render an independent PC
162
- or mobile layout, but it must not add a nested router/Shell or copy generated
163
- routes. Route parameters continue to come from React Router, and generated
164
- capability plus ancestor access guards run before the component renders.
165
-
166
- To expose the standard application-level todo center, declare
167
- `frontend.user.applicationTodoCenter: true`. The compiler generates `/todos`;
168
- the same runtime exposes the independent mobile `/m/todos` page. The page reads only the authenticated user's
169
- Notification Hub recipient projection and deep-links to the platform-resolved
170
- target. Do not query Notification Hub management endpoints or recreate a local
171
- message state store.
172
-
173
- When an application needs branded user-end composition, keep those generated
174
- routes and contribute renderers from the application entry through the public
175
- `openxiangda/react` contract:
95
+ AI 先读取 `openxiangda://workspace/contracts` 的有界索引,再调用
96
+ `contract_describe` 并传入 `{ "selector": "navigation" }`,从 `data.selection.adminNavigationAuthoring.suggestion` 取得确定性的首次建议:
97
+ `proposal` 是有界机器可读声明,`imports` 是 `openxiangda/config` 的 typed helper,
98
+ `expression` 可一次性写入
99
+ `frontend.admin.navigation`,然后由应用正常编辑。该结果明确标记
100
+ `applyMode: "copy-once"` 和 `automaticRuntimeDiscovery: false`;compiler 和 runtime
101
+ 从不调用建议器,也不会在以后新增内部资源时偷偷扩展生产菜单。直接使用 compiler API 的
102
+ tooling 也可调用 `renderAdminNavigationSuggestion(config)` 获得同一 proposal。
103
+
104
+ 用 `defineApplicationContributions` 将每个生成的 `appRoutes` route 精确绑定到一个本地
105
+ React page。`admin` page 由 runtime 放入唯一的 `Shell`,component 不再次嵌套;`user`
106
+ page 不套 admin Shell,可独立实现移动/用户端布局,但仍共享当前用户、权限和 Router:
176
107
 
177
108
  ```tsx
178
109
  import {
179
110
  defineApplicationContributions,
180
- type StandardApplicationTodoCenterProps,
181
- type StandardUserPageFrameProps,
182
- type StandardUserSurfaceContributions,
111
+ OpenXiangdaApplication,
183
112
  } from 'openxiangda/react';
184
-
185
- const standardUserSurfaces = {
186
- frame: {
187
- desktop: DesktopUserFrame,
188
- mobile: MobileUserFrame,
113
+ import {
114
+ adminNavigation,
115
+ adminAccess,
116
+ adminPages,
117
+ appRoutes,
118
+ routeManifest,
119
+ } from '@app/contracts/generated';
120
+ import { InstrumentCalibrationPage } from './operations/InstrumentCalibrationPage';
121
+
122
+ const contributions = defineApplicationContributions(appRoutes, {
123
+ pages: {
124
+ instrumentCalibration: InstrumentCalibrationPage,
189
125
  },
190
- applicationTodoCenter: {
191
- desktop: DesktopTodoCenter,
192
- mobile: MobileTodoCenter,
126
+ });
127
+
128
+ <OpenXiangdaApplication
129
+ adminNavigation={adminNavigation}
130
+ adminAccess={adminAccess}
131
+ adminPages={adminPages}
132
+ routeManifest={routeManifest}
133
+ contributions={contributions}
134
+ />;
135
+ ```
136
+
137
+ `pages` 的键必须与生成的 `appRoutes` 完全一致。隐藏 route 仍执行同一 `capability` 或
138
+ `access.allOf/anyOf`,子 route 同时继承全部祖先约束。页面代码随应用不可变前端制品构建,
139
+ 不能从平台下载 component/module URL。admin operation page 固定使用
140
+ `/admin/operations` 或其子路径;带参数 route 只可直接访问,不能被导航引用。
141
+ `defineAdminContributions` 是保留的 admin-only helper,会主动拒绝 `user` routes。
142
+
143
+ 应用登录使用可选 `frontend.authentication` 声明:仅允许现有平台用户、拒绝注册,桌面
144
+ 固定 `/login`、移动固定 `/m/login`,并分别引用同设备的静态 user 默认 route。登录面由
145
+ 编译器生成到独立 `authenticationSurfaces`,不进入受保护 `appRoutes`。应用通过
146
+ `defineApplicationContributions({ routes: appRoutes, authenticationSurfaces },
147
+ { pages, authentication })` 绑定独立 PC/移动 renderer;renderer 只拥有品牌视觉和本地
148
+ 展示状态,并调用 `ApplicationLoginSurfaceProps` 的平台回调。密码、租户 provider、一次性
149
+ OAuth state/callback、Secure HttpOnly 会话与 refresh family、当前身份和 AuthZ 始终由平台
150
+ 唯一持有。禁止调用 v1 auth API、在浏览器保存 Token、把登录页塞入受保护路由或创建第二
151
+ Router/identity provider。生成的 `platformAuthManifest` 是独立登录 QA 清单,不改变用户路由
152
+ 覆盖数。
153
+
154
+ ## 匿名公开用户页
155
+
156
+ 没有平台账号的外部用户不进入应用登录或角色并集。此类页面使用专门的
157
+ [`frontend.publicAccess` 匿名公开访问合同](public-access.md),由平台生成 HttpOnly 浏览器凭证,
158
+ 并通过 `createAnonymousPublicClient` 提供草稿、附件、具名重复校验、幂等提交和同一浏览器的
159
+ 本人列表/详情。不要创建 guest 账号、公开一般 Data API、保存本地身份或使用 IP/指纹判断所有权。
160
+
161
+ 这也是完整应用的一等组合边界:`OpenXiangdaApplication` 始终唯一持有
162
+ `BrowserRouter`、`RuntimeBoundary`、Refine、generated resource/workflow routes
163
+ 和后台 `Shell`。应用不要把它包在第二个 Router/Shell 中。桌面/移动用户端可以绑定不同
164
+ 组件并拥有各自布局,但仍共享同一个 runtime identity 和 capability ancestor guard。
165
+
166
+ 通用应用级待办中心通过用户 Surface 声明开启:
167
+
168
+ ```ts
169
+ frontend: {
170
+ user: { applicationTodoCenter: true },
171
+ // ...
172
+ }
173
+ ```
174
+
175
+ 编译器生成 `/todos` 和 `/m/todos`。页面只读取当前登录用户的
176
+ Notification Hub 收件人投影;不调用 management API,不复制消息状态库。桌面端使用
177
+ 无常驻详情的全宽列表,移动端使用独立卡片列表;`查看详情` 统一进入平台解析后的
178
+ Workflow/custom application route。
179
+
180
+ 编译器同时生成必填的 `routeManifest`。它是 Workflow/Todo 标准页的唯一桌面/移动成对路由来源,
181
+ 每个 entry 都携带稳定 `routeCode`、参数名、访问能力和 `requiresAuthentication: true`,顶层
182
+ `digest` 绑定完整 catalog。模板把该产物直接传给 `OpenXiangdaApplication`;runtime 在注册
183
+ Router 和页面前校验成对 user surface、参数与访问元数据。应用不得重新声明 `/todos`、
184
+ `/m/todos` 或标准 Workflow 路径,也不得添加别名、重定向或第二份
185
+ 路由状态。digest 的计算与合同校验由 compiler/platform preflight 负责,浏览器只接受有效格式并
186
+ fail-closed 校验 entry/pair 元数据。
187
+
188
+ 标准 Workflow 详情页直接渲染 Surface 的 `presentation.businessDetail`、`summary`、
189
+ typed timeline 和 operation descriptors。业务字段继续使用平台 `SurfaceFieldValue` 语义;
190
+ 父子表、附件、富文本图片和签名不由应用另写 renderer 或拼接 Data API 文件 URL。
191
+ PC canonical 路径为 `/tasks/:taskId` 和 `/workflows/:instanceId`,使用独立全屏页面而不进入
192
+ 后台 Shell;旧 admin 详情路径不存在。桌面和移动使用独立
193
+ renderer,但共享同一授权与命令生命周期。主决策操作固定在底部,
194
+ 低频操作统一进入“更多操作”;意见输入延迟到动作确认层,页面不常驻空白意见表单。
195
+ 页面只显示非系统业务字段与节点内操作,不展示 UUID、revision、事件序列或技术信息区;
196
+ `stale` 不产生常驻提示,真实命令冲突才显示刷新提示。
197
+ 应用只有在业务交互确实不能由标准页表达时才声明成对的 custom detail route。
198
+
199
+ 标准流程提交使用浏览器客户端的 `loadBusinessProcessReceipt(commandId)` 和
200
+ `pollBusinessProcessCommand(commandId, afterRevision)`(Nest 使用对应的
201
+ `OpenXiangdaBusinessProcessService.receipt/poll`)。首个回执可能是 `accepted`;按
202
+ `nextPoll` 继续读取直到 `terminal`,再消费 typed command/surface。不要把 accepted 当作提交失败,
203
+ 也不要对平台端点发起未类型化的 `fetch`。
204
+
205
+ 资源用 `mutationOwner: 'native' | 'action' | 'readonly' | 'workflow'` 声明 mutation owner,
206
+ 并可用 `generated.list/detail/create/update/delete` 精确选择标准 surface。非 Native owner
207
+ 不能生成或向应用角色授予 Native mutation;零可写业务字段不能开放 create/update。
208
+ Workflow definition 用 `launch.mode` 声明 `standalone`、`custom-page`、`hidden-handoff` 或
209
+ `work-center-only`;只有 `standalone` 可进入菜单,`hidden-handoff` 保留同一标准 PC/移动路由
210
+ 但不进菜单。缺省 submission 使用 compiler 生成的标准 process operation。action-owned 资源
211
+ 则在 `standalone`/`hidden-handoff` 上声明 `submission.kind: 'named-operation'`,把 create/existing
212
+ 表单字段、平台幂等键、当前用户、subject id/revision 和响应结果显式绑定到原 named operation。
213
+ 标准页仍使用 generated field components,但最终由应用服务端完成业务校验、原子写入并决定
214
+ 是否返回 durable process command;缺少或返回 null command 表示本次无需审批。浏览器不提供
215
+ save callback,也不直接调用 prepare/start。`custom-page` 仍只能由 verified Named Action 注入
216
+ `OpenXiangdaBusinessProcessService` 发起。
217
+
218
+ 标准资源页提供 `toolbar`、`row`、`detail` 三类 action slot。slot 声明稳定 code、label、
219
+ 可选 order 和 capability/access,render 只获得当前 resource、已授权 record/selection 与
220
+ 受控 `refresh()`:
221
+
222
+ ```tsx
223
+ const contributions = defineApplicationContributions(appRoutes, {
224
+ pages: { instrumentCalibration: InstrumentCalibrationPage },
225
+ resources: {
226
+ instruments: {
227
+ row: [{
228
+ code: 'calibrate',
229
+ label: '校准',
230
+ capability: 'instrument.calibration.run',
231
+ render: ({ record, refresh }) => (
232
+ <CalibrationButton recordId={String(record.id)} onDone={refresh} />
233
+ ),
234
+ }],
235
+ },
193
236
  },
194
- } satisfies StandardUserSurfaceContributions;
237
+ });
238
+ ```
195
239
 
196
- export const applicationContributions = defineApplicationContributions(
197
- { routes: appRoutes, authenticationSurfaces },
198
- { pages, authentication, standardUserSurfaces },
199
- );
240
+ slot 只扩展动作区域,不接管查询、字段策略、revision、保存或审计。跨资源事务、外部副作用
241
+ 和业务不变量仍调用有 `@OpenXiangdaOperation` 门禁的 Nest App Operation;普通 CRUD 继续
242
+ 直接使用 Native Data API。UI 隐藏不是服务端授权。
243
+
244
+ 学院是应用自有 `colleges` Native Resource,仪器 `collegeId` 保存该资源的系统 UUID。
245
+ 人员和部门选择器调用平台 Directory 的分页 search,并用 exact resolve 恢复已选 ID;学院
246
+ 选择器调用 membership-bound scope-values search/resolve。空结果和错误直接展示,不回退
247
+ 静态人员、部门或学院。组织部门与学院属于不同 owner,不能互相推断。
248
+
249
+ 发布态 app/environment 只读取平台为每个 index 注入的
250
+ `openxiangda-runtime-base`、`openxiangda-app-code` 和
251
+ `openxiangda-environment` meta;不得使用 Vite 构建变量或手填环境覆盖它。
252
+ 当前用户使用同源 `openxiangda.runtime-authorization/v2` 合同,每个 Data API query/body
253
+ 都显式携带 environmentKey。标准客户端读取当前用户角色并集,不保存 Token,也不持久化
254
+ 授权结果。该角色并集合同未部署的平台版本
255
+ 必须在应用写入前 fail closed。
256
+
257
+ ## 本地与浏览器门禁
258
+
259
+ ```bash
260
+ openxiangda dev
261
+ pnpm --filter @app/web check
262
+ pnpm --filter @app/web test
263
+ pnpm --filter @app/web test:e2e
264
+ pnpm --filter @app/web build
200
265
  ```
201
266
 
202
- Both desktop/mobile members and both groups are mandatory once
203
- `standardUserSurfaces` is present; partial families fail at compile time and
204
- again at runtime. Omit the property to retain the platform defaults.
205
-
206
- `StandardUserPageFrameProps` contains `pageKind`, `device`, `mobile`, rendered
207
- `children`, safe `route` metadata, `canGoBack` and the platform-owned `back()`
208
- callback. The frame wraps Todo and standard Workflow work-center, launch, task
209
- and instance pages. It must not create a Router, resolve identity, redirect a
210
- standard path or reinterpret route metadata as authorization.
211
-
212
- `StandardApplicationTodoCenterProps` contains only the authenticated current
213
- user's `items`, aggregate `counts`, `total`, `loading`, `loadingMore`, `error`,
214
- immutable `query`, `hasMore`, and the bounded callbacks `setQuery`, `refresh`,
215
- `loadMore`, `recordInteraction` and `openItem`. `setQuery` owns view, keyword,
216
- unread and paged-offset changes; `loadMore` appends and de-duplicates the next
217
- platform page; `openItem` records a click best-effort and uses the
218
- platform-resolved desktop/mobile target. Render only these props. Never import
219
- the private platform client, call Notification Hub endpoints, persist a Todo
220
- copy, accept a user/token parameter, or navigate from an item object not
221
- supplied by the current render. A renderer exception is contained by the
222
- platform error boundary without replacing `RuntimeBoundary` or the Router.
223
-
224
- The compiler also emits the required generated `routeManifest`. This is the
225
- single desktop/mobile paired catalog for standard Workflow and Todo pages. Each
226
- entry carries stable route codes, path parameters, access metadata and a
227
- `requiresAuthentication: true` marker; the top-level digest binds the complete
228
- catalog. Pass it directly to `OpenXiangdaApplication`. The runtime validates
229
- user Surface pairs, parameter names and shared access metadata before registering
230
- the Router. Applications must not recreate standard `/todos`, `/m/todos`, or
231
- Workflow paths, add aliases/redirects, or maintain a second route state. The
232
- compiler/platform preflight is the digest authority; the browser validates the
233
- digest shape and fails closed on malformed or inconsistent entries.
234
-
235
- For standard process submission, consume the typed browser helpers
236
- `loadBusinessProcessReceipt(commandId)` and
237
- `pollBusinessProcessCommand(commandId, afterRevision)` (or the matching Nest
238
- `receipt`/`poll` methods). A first response may be `accepted`; follow
239
- `nextPoll` until `terminal` instead of treating it as failure or issuing a
240
- generic request to the platform endpoint.
241
-
242
- For a small business action on a generated resource page, use the typed
243
- `resources[resourceCode].toolbar`, `.row` or `.detail` slots accepted by
244
- `defineApplicationContributions`. Declare a stable action code, label and
245
- capability/access expression. The render context contains only the current
246
- resource, already-authorized record or selection, and a bounded `refresh()`;
247
- it does not own CRUD, revisions, fields or authorization. Invoke a guarded Nest
248
- App Operation for cross-resource transactions, invariants or side effects and
249
- leave ordinary CRUD on the Native Data API. Do not copy a generated page just
250
- to insert a button.
251
-
252
- Generated contracts intentionally emit each resource Surface once in
253
- `resourceSurfaces`; `resourceDefinitions[code].surface` references that shared
254
- object, while an explicit resource `detailRouteCode` is preserved beside it.
255
- Import the generated runtime definitions normally. Do not serialize, inline or
256
- duplicate Surface literals in application code.
257
-
258
- The standard Workflow detail renderers consume only the authoritative Surface:
259
- `presentation.businessDetail`, `presentation.summary`, the typed timeline and
260
- operation descriptors. Continue to use `SurfaceFieldValue` for complete field
261
- semantics and the Workflow-scoped file client for attachments, rich-text images
262
- and signatures. Keep desktop and mobile renderers structurally independent;
263
- place decision actions in the fixed primary group, all low-frequency actions in
264
- the single more-actions entry, and collect comments in the action confirmation
265
- layer. The canonical desktop paths are `/tasks/:taskId` and
266
- `/workflows/:instanceId`; they render as standalone full-screen pages outside
267
- the admin Shell, while the platform alone replace-redirects historical admin
268
- detail URLs. Filter `system: true` and internal fields, omit empty sections,
269
- merge operation actor/action/reason/time into its vertical node, and never show
270
- UUIDs or revision/event diagnostics. Treat `stale` as metadata and show the
271
- friendly refresh prompt only after a real command token/CAS conflict. Do not
272
- copy the standard page into an application merely to show full business fields
273
- or rearrange platform-owned operations.
274
-
275
- ## Foundation module and field selection
276
-
277
- New applications plan task pages first and compose data models and selected CRUD
278
- views through `defineApplicationModule`. Internal tables need no page. List, form
279
- and detail selections are exhaustive; `hidden` controls presentation independently
280
- of Data API permissions. Keep root configuration short by importing business modules.
281
-
282
- Use platform Field Kit before custom input controls. PC input may use `antd`;
283
- mobile input uses the scoped `openxiangda/mobile` Ant Design Mobile adapter, inside
284
- `MobileSurface` with `openxiangda/mobile/styles.css` (already included in React styles).
285
- Do not import the `antd-mobile` root, which resets global page styles.
286
- Do not write raw business input/select/textarea or
287
- contentEditable elements. Keep mobile entrypoints in src/mobile/, Mobile*.tsx or
288
- *.mobile.tsx so the application check can inspect their static local imports.
289
- Basic text/number/boolean/option/date controls have a mobile adapter; complex field
290
- migration remains staged, and each field must pass actual interaction acceptance.
291
-
292
-
293
- 标准列表使用高级条件分组、显示列浮层、冻结和拖动列序、多排序。仅从页面声明的可读业务字段提供选择;导出与列表共用条件和排序。依赖所选记录的 toolbar contribution 声明 `requiresSelection: true`。显示列修改即时预览,显式保存个人偏好。
294
-
295
- 标准表单使用“暂存 / 提交”及平台草稿箱,不自行使用 localStorage、业务表或 Nest 保存草稿。PC 抽屉头部提供全屏、新开页面、关闭;新开页面由平台保存并交接草稿。移动端为分组字段行、简单标题和固定底部操作,草稿/恢复确认用移动底部弹层;不显示“返回列表”。草稿字段必须保持权限裁剪,编辑草稿不得用最新 revision 覆盖原编辑版本。
267
+ 测试必须真实断言 Vite LAN 不可达、production 警示 DOM 常驻、Data/Directory/App API
268
+ 使用同一当前用户角色并集、Perspective 读取投影协议,并约束源码文件数、LOC、构建
269
+ 体积和 gzip 体积。
@@ -0,0 +1,66 @@
1
+ # 安装与开始开发
2
+
3
+ OpenXiangda 2.0 默认生成 React 应用和共享契约。普通 CRUD、标准审批和通知使用远端平台能力;只有真实服务端业务动作才按需增加 NestJS。本地无需启动平台、Docker 或 PostgreSQL。
4
+
5
+ ## 准备 {#prerequisites}
6
+
7
+ 准备平台地址、具有应用开发权限的账号、Node.js 24 和 pnpm 10.15.1。向平台维护者取得已验证的 OpenXiangda 2.0 精确版本,并核对平台能力是否支持。`openxiangda@latest` 可能属于 1.x;不能用它选择 2.0。
8
+
9
+ ## 安装与创建 {#create}
10
+
11
+ 以下命令的版本占位符由随包资料替换为该根包的精确版本。网站源码阅读者应先确认要使用的发行版本。
12
+
13
+ ```bash
14
+ pnpm dlx openxiangda@2.0.0 skill install --force
15
+ pnpm dlx openxiangda@2.0.0 auth status --base-url <平台地址> --json
16
+ pnpm dlx openxiangda@2.0.0 login --base-url https://platform.example.com
17
+ pnpm dlx openxiangda@2.0.0 create my-app --base-url https://platform.example.com
18
+ cd my-app
19
+ pnpm openxiangda context --json
20
+ pnpm openxiangda dev
21
+ ```
22
+
23
+ 将两处示例平台地址替换为同一个目标地址。`create` 先核对目标与当前登录平台,再创建本地目录、安装依赖并初始化远端应用;新应用不会自动沿用文件中最后登录的站点。CI 使用原有成对的 `OPENXIANGDA_BASE_URL` 和 `OPENXIANGDA_TOKEN` 时,可以由该显式地址指定目标。
24
+
25
+ 初始化中断后重试同一命令;已有目录会核对原平台绑定,不能通过 `create` 改绑到其他站点。地址不一致时先检查目标并登录正确的平台,不修改 link 文件绕过检查,也不要使用内部 provision 接口另建应用。创建操作应在用户要求创建应用的范围内执行。
26
+
27
+ 进入项目后使用 `pnpm openxiangda`,由项目依赖和锁文件决定版本。查看使用资料运行 `pnpm openxiangda docs`;查看单一主题运行 `pnpm openxiangda docs frontend`。安装到其他 AI 工具时使用 `skill install --destination <Skill根目录>`。
28
+
29
+ ## 项目结构 {#workspace}
30
+
31
+ ```text
32
+ apps/web 页面与应用入口
33
+ packages/contracts 平台编译器生成的契约
34
+ modules 业务模型、页面选择和模块声明
35
+ openxiangda.config.ts 应用装配、导航、权限和按需能力
36
+ appspec 需求、架构、变更和验收记录
37
+ AGENTS.md 平台约定与项目自有说明
38
+ ```
39
+
40
+ 以实际模板输出为准。资源与页面通过模块声明组合,不创建 `platform/data`。`apps/server` 仅在启用后端时初始化;后续保留用户业务代码。
41
+
42
+ AppSpec 随开发持续维护:测试发布前补齐总纲、关联变更与验收计划,部署后记录真实业务结果,生产晋级核对该测试版本的验收报告。新应用业务实现前先完成[产品设计与确认基线](product-design.md);研究、示例原型和技术检查可用于逐步完善设计。具体步骤见[全流程记录](appspec.md)。
43
+
44
+ ## 连接开发 {#connected-development}
45
+
46
+ `dev` 监听本机回环地址,页面通过同源代理访问平台测试数据。浏览器不持久化平台凭据。终端和页面显示当前环境;只有生产环境时会持续提示生产数据风险。开发数据写入仍是远端真实写入,按任务范围操作。
47
+
48
+ 纯 CRUD 修改优先使用标准模型、字段和页面;跨模型事务或外部副作用再选择后端。角色、行和字段权限在平台执行。详见[开发流程](development.md)、[模型与标准 CRUD](application-foundation.md)和[按需后端](backend.md)。
49
+
50
+ ## 检查与交付 {#delivery}
51
+
52
+ 只检查时运行 `pnpm openxiangda check`。需要部署测试环境时直接运行 `pnpm openxiangda deploy`,它已包含检查、测试和构建;无需再连续重复运行全部脚本。
53
+
54
+ 生产必须复用成功测试运行。部署状态、真实角色验收和回滚步骤见[应用交付](delivery.md)。纯前端发布不要求 Docker;启用后端后才需要官方镜像构建条件。
55
+
56
+ ## 接入 MCP {#mcp}
57
+
58
+ MCP 使用相同的项目 CLI:
59
+
60
+ ```bash
61
+ pnpm exec openxiangda --mcp-stdio --cwd <应用绝对路径>
62
+ ```
63
+
64
+ 先调用 `workspace_context`,再按任务读取 `docs_read` 和当前契约。配置示例与工具参数见[MCP 参考](mcp.md)。登录、创建和长期 dev 进程继续由 CLI/终端管理。
65
+
66
+ 指定站点授权可用 `auth status --base-url <平台地址> --json` 或 MCP `authorization_status` 只读核验,无需工作区。状态为 `authorized` 才证明当前 access 被平台接受;`missing`/`platform_mismatch`/`refresh_required` 需处理会话,`unauthorized` 表示平台拒绝,`unavailable` 表示暂时无法核验,不能当成过期。查询不刷新、不打开浏览器、不修改绑定;应用管理权限需另行核验。