openxiangda 1.0.134 → 1.0.136

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 (57) hide show
  1. package/README.md +6 -0
  2. package/lib/cli.js +947 -16
  3. package/lib/design-gates.js +40 -10
  4. package/lib/sdd.js +777 -0
  5. package/lib/workspace-init.js +3 -0
  6. package/openxiangda-skills/SKILL.md +11 -1
  7. package/openxiangda-skills/references/best-practices.md +32 -5
  8. package/openxiangda-skills/references/openxiangda-api.md +32 -6
  9. package/openxiangda-skills/references/pages/page-sdk.md +3 -0
  10. package/openxiangda-skills/references/pages/publish-flow.md +16 -0
  11. package/openxiangda-skills/references/permission-design-patterns.md +216 -0
  12. package/openxiangda-skills/references/permissions-settings.md +58 -0
  13. package/openxiangda-skills/references/platform-data-model.md +19 -4
  14. package/openxiangda-skills/references/resource-manifest-cheatsheet.md +32 -1
  15. package/openxiangda-skills/skills/openxiangda-architecture-design/SKILL.md +8 -0
  16. package/openxiangda-skills/skills/openxiangda-core/SKILL.md +2 -0
  17. package/openxiangda-skills/skills/openxiangda-permission-settings/SKILL.md +38 -1
  18. package/package.json +2 -1
  19. package/packages/sdk/dist/{ProcessPreview-Dfc4-wIq.d.mts → ProcessPreview-Ci8_UsbN.d.mts} +4 -0
  20. package/packages/sdk/dist/{ProcessPreview-Dfc4-wIq.d.ts → ProcessPreview-Ci8_UsbN.d.ts} +4 -0
  21. package/packages/sdk/dist/build/index.cjs.map +1 -1
  22. package/packages/sdk/dist/build/index.d.mts +7 -0
  23. package/packages/sdk/dist/build/index.d.ts +7 -0
  24. package/packages/sdk/dist/build/index.mjs.map +1 -1
  25. package/packages/sdk/dist/components/index.cjs +32 -4
  26. package/packages/sdk/dist/components/index.cjs.map +1 -1
  27. package/packages/sdk/dist/components/index.d.mts +8 -2
  28. package/packages/sdk/dist/components/index.d.ts +8 -2
  29. package/packages/sdk/dist/components/index.mjs +32 -4
  30. package/packages/sdk/dist/components/index.mjs.map +1 -1
  31. package/packages/sdk/dist/runtime/index.cjs +181 -28
  32. package/packages/sdk/dist/runtime/index.cjs.map +1 -1
  33. package/packages/sdk/dist/runtime/index.d.mts +2 -2
  34. package/packages/sdk/dist/runtime/index.d.ts +2 -2
  35. package/packages/sdk/dist/runtime/index.mjs +181 -28
  36. package/packages/sdk/dist/runtime/index.mjs.map +1 -1
  37. package/packages/sdk/dist/runtime/react.cjs +152 -25
  38. package/packages/sdk/dist/runtime/react.cjs.map +1 -1
  39. package/packages/sdk/dist/runtime/react.d.mts +57 -4
  40. package/packages/sdk/dist/runtime/react.d.ts +57 -4
  41. package/packages/sdk/dist/runtime/react.mjs +152 -25
  42. package/packages/sdk/dist/runtime/react.mjs.map +1 -1
  43. package/templates/openxiangda-react-spa/.cursor/rules/openxiangda.mdc +8 -1
  44. package/templates/openxiangda-react-spa/.qoder/rules/openxiangda.md +8 -1
  45. package/templates/openxiangda-react-spa/AGENTS.md +9 -1
  46. package/templates/openxiangda-react-spa/app-workspace.config.ts +14 -0
  47. package/templates/sy-lowcode-app-workspace/.cursor/rules/openxiangda-resources.mdc +5 -1
  48. package/templates/sy-lowcode-app-workspace/.cursor/rules/openxiangda.mdc +7 -1
  49. package/templates/sy-lowcode-app-workspace/.qoder/rules/openxiangda-resources.md +6 -0
  50. package/templates/sy-lowcode-app-workspace/.qoder/rules/openxiangda.md +7 -1
  51. package/templates/sy-lowcode-app-workspace/AGENTS.md +10 -1
  52. package/templates/sy-lowcode-app-workspace/app-workspace.config.ts +14 -0
  53. package/templates/sy-lowcode-app-workspace/examples/best-practices/README.md +3 -0
  54. package/templates/sy-lowcode-app-workspace/examples/best-practices/access-governance.md +239 -0
  55. package/templates/sy-lowcode-app-workspace/examples/best-practices/catalog.json +6 -0
  56. package/templates/sy-lowcode-app-workspace/examples/best-practices/decision-guide.md +25 -1
  57. package/templates/sy-lowcode-app-workspace/src/types/app-workspace.types.ts +12 -0
@@ -21,13 +21,20 @@ This is an OpenXiangda React SPA workspace. See [AGENTS.md](mdc:AGENTS.md) for f
21
21
 
22
22
  - Writes and deploys must pass `--profile <name>` explicitly.
23
23
  - Architecture-class work is plan-gated: run `openxiangda design gates --topic <code> --json` and wait for confirmation.
24
+ - High-risk changes must start with `openxiangda sdd context --json`, use an approved change, and run `openxiangda sdd verify <change> --changed` before release.
25
+ - Account/role/permission/RBAC/organization-account/query-param authorization work must run `openxiangda design gates --topic permissions --json`, choose `managed-platform-account`, `existing-platform-user-assignment`, `static-role-permission`, or `query-param-context`, and write the permission matrix.
26
+ - Roles that create roles, assign members, grant API permissions, maintain permission groups, or manage organization accounts must declare `apiPermissionCodes`, such as `app:role:manage`, `app:page-permission-group:manage`, `app:form-permission-group:manage`, and `app:organization:manage`.
24
27
  - `src/resources/**` is the resource source of truth; use `validate -> plan -> publish`.
25
28
  - React routes live in `src/app/router.tsx`; frontend artifacts are deployed with `openxiangda runtime deploy`.
26
- - Backend permissions and public-access grants are authoritative.
29
+ - Backend permissions, public-access grants, and App Function role/scope checks are authoritative.
30
+ - Query parameters are context, filters, or ticket input only; they are not sensitive authorization.
27
31
 
28
32
  ## Never
29
33
 
30
34
  - Do not run `lowcode-workspace publish-all`, `pnpm publish:all`, or `pnpm openxiangda:publish` directly.
31
35
  - Do not deploy without `--profile`.
32
36
  - Do not use legacy `?publicAccess=guest`, `isRenderNav`, or workbench page parameters for new React SPA routes.
37
+ - Do not rely on frontend-only button hiding, query-param checks, hardcoded roles, mock permissions, fake account IDs, or `PermissionBoundary` alone for real authorization.
38
+ - Do not assign a business admin role without the role-setting API permissions needed for role/member management.
39
+ - Do not bypass `sdk.organization` / `ctx.organization` for platform account/department writes.
33
40
  - Do not commit tokens, AK/SK, or third-party secrets.
@@ -21,13 +21,20 @@ This is an OpenXiangda React SPA workspace. Read [AGENTS.md](AGENTS.md) for full
21
21
 
22
22
  - 发布和写平台资源必须显式传 `--profile <name>`。
23
23
  - 架构类需求先跑 `openxiangda design gates --topic <code> --json` 并等用户确认。
24
+ - 高风险变更先跑 `openxiangda sdd context --json`,创建/继续 change,并在用户确认后 `openxiangda sdd approve <change>`;发布前 `openxiangda sdd verify <change> --changed`。
25
+ - 账号/角色/权限/RBAC/组织账号/查询参数授权需求先跑 `openxiangda design gates --topic permissions --json`,选择 `managed-platform-account` / `existing-platform-user-assignment` / `static-role-permission` / `query-param-context` 并输出权限矩阵。
26
+ - 角色能新增角色、分配成员、授接口权限、维护权限组或管理组织账号时,角色资源必须声明 `apiPermissionCodes`,例如 `app:role:manage`、`app:page-permission-group:manage`、`app:form-permission-group:manage`、`app:organization:manage`。
24
27
  - `src/resources/**` 是工程化资源来源,正式多资源变更走 `validate -> plan -> publish`。
25
28
  - React SPA 路由由 `src/app/router.tsx` 管理,前端包通过 `openxiangda runtime deploy` 发布。
26
- - 页面权限、表单权限、公开访问 grants 以后端资源为准;前端只做展示保护。
29
+ - 页面权限、表单权限、公开访问 grants、App Function 后端检查是授权依据;前端只做展示保护。
30
+ - 查询参数只能做上下文、筛选或 ticket 输入,不能作为敏感数据授权依据。
27
31
 
28
32
  ## Never
29
33
 
30
34
  - 不要直接运行 `lowcode-workspace publish-all`、`pnpm publish:all` 或 `pnpm openxiangda:publish`。
31
35
  - 不要省略 `--profile` 发布。
32
36
  - 不要使用旧 `?publicAccess=guest`、`isRenderNav` 或 workbench 参数模型开发新 React SPA。
37
+ - 不要只隐藏按钮、只判断 query 参数、硬编码角色、前端模拟权限、假账号/假 ID、或用 `PermissionBoundary` 代替真实授权。
38
+ - 不要给用户挂“管理员”业务角色但不给该角色绑定 `app:role:manage` 等角色设置接口权限。
39
+ - 不要绕过 `sdk.organization` / `ctx.organization` 直接写平台账号/部门。
33
40
  - 不要把 token、AK、SK、第三方密钥写入项目文件。
@@ -5,12 +5,18 @@
5
5
  ## 开发原则
6
6
 
7
7
  - 架构类需求默认只规划、不实现。新应用、复杂页面、登录注册、公开访问、权限数据范围、流程自动化、连接器/通知等需求,先运行 `openxiangda doctor --json` 和 `openxiangda design gates --topic <code> --json`,输出设计并等用户确认;确认前只允许读取、快照、dry-run、提问和写设计文档,不允许改源码、写平台、发布或发送通知。
8
+ - 高风险变更必须挂到 SDD change。修改 `src/forms/**`、`src/pages/**`、`src/resources/**`、`src/functions/**`、`src/automations/**`、`src/workflows/**`、`src/js-code-nodes/**`、`src/runtime/**` 或 `app-workspace.config.ts` 前,先运行 `openxiangda sdd context --json`,没有合适 change 就 `openxiangda sdd propose <change>`,等用户确认后运行 `openxiangda sdd approve <change>` 再实现。发布前运行 `openxiangda sdd verify <change> --changed`、`openxiangda workspace check --changed`、`openxiangda resource plan --profile <name>`。
9
+ - 账号、角色、权限、数据范围、组织账号、RBAC、查询参数授权需求必须先运行 `openxiangda design gates --topic permissions --json`,选择 `managed-platform-account` / `existing-platform-user-assignment` / `static-role-permission` / `query-param-context`,输出权限矩阵后再实现。
10
+ - 应用角色如果要创建角色、分配角色成员、给角色授接口权限、维护页面/表单权限组或管理组织账号,必须在角色资源声明 `apiPermissionCodes`,例如 `app:role:manage`、`app:page-permission-group:manage`、`app:form-permission-group:manage`、`app:organization:manage`。
8
11
  - 默认用户界面保持克制:左侧应用导航、顶部账号信息、首页内容区域。
9
12
  - 不在默认可见页面展示 SDK、Runtime、Cookie、Proxy、Playwright、AI 验证、调试上下文、构建号等开发语言。
10
13
  - 使用 React Router 管理路由,路由定义在 `src/app/router.tsx`。
11
14
  - 使用 Tailwind CSS 表达样式,不依赖平台 theme tokens。
12
15
  - 菜单和首页文案优先改 `src/app/navigation.ts`、`src/app/starter-content.ts` 与 `src/pages/admin/AdminDashboardPage.tsx`。
13
16
  - 页面、表单、字段、数据范围、流程动作、文件、连接器权限以后端接口为准;前端只做展示保护和清晰状态页。
17
+ - 查询参数只能作为上下文、筛选或 ticket 输入,不能作为敏感数据授权依据;敏感读写必须由 public-access grants、平台角色、页面/表单权限组或 App Function 后端校验保护。
18
+ - 应用管理平台账号/部门时,必须走 `sdk.organization` / `ctx.organization`,并要求当前操作者具备 `app:organization:manage`。
19
+ - 只给用户分配“管理员”业务角色不等于授权其设置角色;缺少 `app:role:manage` 时角色创建、成员分配和角色接口权限授予会被后端拒绝。
14
20
  - 本地开发通过 Vite `/service` 代理远端平台,保持 HttpOnly Cookie 同域访问。
15
21
  - 新公开访问页使用 `/view/:appType/public/*`、`src/resources/routes/`、`src/resources/public-access/` 和 `PublicAccessGate`。不要使用旧 `?publicAccess=guest`。
16
22
 
@@ -27,6 +33,8 @@ pnpm deploy -- --profile <name>
27
33
  openxiangda workspace publish --profile <name> --form <formCode>
28
34
  openxiangda doctor --profile <name> --json
29
35
  openxiangda design gates --topic public-access --json
36
+ openxiangda sdd context --json
37
+ openxiangda sdd verify <change> --changed
30
38
  openxiangda resource plan --profile <name>
31
39
  openxiangda resource publish --profile <name>
32
40
  openxiangda runtime deploy --profile <name>
@@ -75,7 +83,7 @@ React SPA 页面需要声明菜单 code、route code 和 path pattern。默认
75
83
  import { PermissionBoundary, useAppMenus, useRuntimeBootstrap } from "openxiangda/runtime/react";
76
84
  ```
77
85
 
78
- `PermissionBoundary` 只能做展示保护,不能替代后端权限。后端返回 401 时跳登录,403 时展示无权限状态。
86
+ `PermissionBoundary` 只能做展示保护,不能替代后端权限。不要硬编码角色、前端模拟权限、只判断 query 参数或用空数组兜底伪装成功。后端返回 401 时跳登录,403 时展示无权限状态。
79
87
 
80
88
  公开页面需要同时声明 route 和 public access policy:
81
89
 
@@ -30,4 +30,18 @@ export default defineAppWorkspaceConfig({
30
30
  pageMenuParentId: process.env.APP_PAGE_MENU_PARENT_ID || "",
31
31
  pageMenuIcon: process.env.APP_PAGE_MENU_ICON || "",
32
32
  },
33
+ compatibility: {
34
+ apiContracts:
35
+ process.env.OPENXIANGDA_API_CONTRACTS === "legacy" ? "legacy" : "strict",
36
+ legacyFallbacks: process.env.OPENXIANGDA_LEGACY_FALLBACKS === "true",
37
+ requestTrace: process.env.OPENXIANGDA_REQUEST_TRACE === "true",
38
+ },
39
+ governance: {
40
+ sdd: {
41
+ enabled: process.env.OPENXIANGDA_SDD_ENABLED === "false" ? false : true,
42
+ strictHighRisk:
43
+ process.env.OPENXIANGDA_SDD_STRICT_HIGH_RISK === "false" ? false : true,
44
+ path: process.env.OPENXIANGDA_SDD_PATH || "openspec",
45
+ },
46
+ },
33
47
  });
@@ -25,4 +25,8 @@ See `references/resource-manifest-cheatsheet.md` for copy-paste skeletons.
25
25
 
26
26
  Commands: `openxiangda resource validate|plan|publish|pull --profile <name>`.
27
27
 
28
- Forbidden: `formUuid` / `pageId` / `workflowId` 等平台 ID 写进 manifest;把 API key / token / secret / authorization 写进 manifest;data view 用作单表 CRUD / linkedForm / 写回 / 强实时;page 源里 hardcode 通知或 connector URL。
28
+ Before editing `roles`, `permissions/page-groups`, or `permissions/form-groups` for account/role/data-scope/RBAC/query-param authorization work, run `openxiangda design gates --topic permissions --json`, choose the permission mode, and write the permission matrix.
29
+
30
+ If a `roles/<code>.json` role can create roles, assign members, grant API permissions, maintain permission groups, or manage organization accounts, it must declare `apiPermissionCodes`; a business role name alone does not grant backend API permission.
31
+
32
+ Forbidden: 把 `formUuid` / `pageId` / `workflowId` 等平台 ID 写进 manifest;把 API key / token / secret / authorization 写进 manifest;data view 用作单表 CRUD / linkedForm / 写回 / 强实时;只靠 query 参数、前端隐藏按钮、硬编码角色、mock 权限或 `PermissionBoundary` 作为敏感授权;管理型角色缺少 `app:role:manage` / `app:page-permission-group:manage` / `app:form-permission-group:manage` / `app:organization:manage`;page 源里 hardcode 通知或 connector URL。
@@ -18,13 +18,16 @@ This is a `sy-lowcode-app-workspace` managed by the `openxiangda` CLI. See [AGEN
18
18
  | edit code page / portal / dashboard / 代码页 | `openxiangda-page` | edit `src/pages/<code>/` → `workspace publish --page <code>` |
19
19
  | approval workflow / 审批流程 / JS_CODE | `openxiangda-workflow-automation` | `openxiangda workflow ...` |
20
20
  | automation / cron / 自动化 / 定时 | `openxiangda-workflow-automation` | `openxiangda automation ...` |
21
- | role / permission / 公开访问 | `openxiangda-permission-settings` | `openxiangda permission ...` / `openxiangda settings ...` |
21
+ | account / role / permission / data scope / query-param authorization | `openxiangda-permission-settings` | run `openxiangda design gates --topic permissions --json` first, then `openxiangda permission ...` / `openxiangda settings ...` |
22
22
  | diagnose / snapshot / 排查 / 报错 | `openxiangda-inspect` | `openxiangda app snapshot APP_XXX --profile <name> --json` |
23
23
  | login / token / switch profile | `openxiangda-core` | `openxiangda env --profile <name>` / `openxiangda auth status` |
24
24
 
25
25
  ## Always
26
26
 
27
27
  - For routine edits, default to incremental publish: `workspace publish --profile <name> --changed --dry-run` then `--changed` or targeted `--page <code>` / `--form <code>`.
28
+ - High-risk changes must use SDD first: `openxiangda sdd context --json` -> `openxiangda sdd propose <change>` -> user confirmation -> `openxiangda sdd approve <change>`; before release run `openxiangda sdd verify <change> --changed`.
29
+ - Account/role/permission/RBAC/organization-account/query-param authorization work must choose a mode first: `managed-platform-account`, `existing-platform-user-assignment`, `static-role-permission`, or `query-param-context`, then write the permission matrix.
30
+ - Roles that create roles, assign members, grant API permissions, maintain permission groups, or manage organization accounts must declare `apiPermissionCodes` in `src/resources/roles/<code>.json`, such as `app:role:manage`, `app:page-permission-group:manage`, `app:form-permission-group:manage`, and `app:organization:manage`.
28
31
  - Confirm current profile before any write: `openxiangda env --profile <name>`.
29
32
  - `.openxiangda/state.json` is the authoritative profile → appType → resource-ID map; CLI maintains it, do not hand-edit.
30
33
  - User tokens live in `~/.openxiangda/profiles.json`; shared env in `~/.openxiangda/.env`.
@@ -40,6 +43,9 @@ This is a `sy-lowcode-app-workspace` managed by the `openxiangda` CLI. See [AGEN
40
43
  - ❌ Search the platform for similar app names when `.openxiangda/state.json` has no binding.
41
44
  - ❌ Copy `formUuid` / `pageId` / `workflowId` / `automationId` across profiles.
42
45
  - ❌ Run a full publish for a single-file edit.
46
+ - ❌ Frontend-only button hiding, query-param checks, hardcoded roles, mock permissions, fake account IDs, or `PermissionBoundary` alone as real authorization.
47
+ - ❌ Business admin roles without `app:role:manage` or required role-setting API permissions when they are expected to manage roles or members.
48
+ - ❌ Direct platform account/department writes that bypass `sdk.organization` / `ctx.organization`.
43
49
  - ❌ Use `openxiangda form create` / `form publish` / `page publish` as the normal page generation path.
44
50
  - ❌ Store tokens / AK / SK / third-party secrets in project files.
45
51
  - ❌ `npm publish` — this workspace is `"private": true`, not an npm package.
@@ -12,6 +12,10 @@ You are editing engineering-managed resource manifests under `src/resources/`. *
12
12
 
13
13
  `references/resource-manifest-cheatsheet.md`(installed at `~/.qoder/skills/openxiangda/references/resource-manifest-cheatsheet.md`)— connector / data-view / notification / workflow / automation / JS_CODE / role / page-group / form-group / settings / menu 骨架。
14
14
 
15
+ 编辑 `roles`、`permissions/page-groups`、`permissions/form-groups` 前,如果需求涉及账号、角色、数据范围、组织账号、RBAC 或查询参数授权,先运行 `openxiangda design gates --topic permissions --json`,选择权限模式并输出权限矩阵。
16
+
17
+ 如果 `roles/<code>.json` 代表的角色需要新增角色、分配成员、授接口权限、维护权限组或管理组织账号,必须声明 `apiPermissionCodes`;只写业务角色名不会获得这些后端接口权限。
18
+
15
19
  ## 文件夹与对应 skill
16
20
 
17
21
  | 路径 | 用途 | 主导 skill |
@@ -42,5 +46,7 @@ openxiangda resource pull --profile <name> # 平台 → 本地
42
46
  - ❌ 把 `formUuid` / `pageId` / `workflowId` 等平台 ID 直接写进 manifest(CLI 解析逻辑 code)。
43
47
  - ❌ 把 API key / token / secret / password / authorization / headers / credential 写进 manifest(平台后台配置)。
44
48
  - ❌ data view 用作单表 CRUD、`linkedForm` 下拉、写回、强实时数据源。
49
+ - ❌ 只靠 query 参数、前端隐藏按钮、硬编码角色、mock 权限或 `PermissionBoundary` 作为敏感授权。
50
+ - ❌ 管理型角色缺少 `app:role:manage`、`app:page-permission-group:manage`、`app:form-permission-group:manage` 或 `app:organization:manage`。
45
51
  - ❌ 在 page 源里 hardcode `/api/notification-config/*` 或 `/connectors/actions/invoke`(用 `sdk.notification` / `sdk.connector`)。
46
52
  - ❌ 同时在 manifest 与平台后台编辑同一个资源(漂移源)。
@@ -18,13 +18,16 @@ This is a `sy-lowcode-app-workspace` managed by the `openxiangda` CLI. Read [AGE
18
18
  | 改代码页 / portal / dashboard | `openxiangda-page` | 编辑 `src/pages/<code>/` → `workspace publish --page <code>` |
19
19
  | 审批流程 / workflow / JS_CODE | `openxiangda-workflow-automation` | `openxiangda workflow ...` |
20
20
  | 自动化 / 定时 / 提交触发 | `openxiangda-workflow-automation` | `openxiangda automation ...` |
21
- | 角色 / 权限 / 公开访问 | `openxiangda-permission-settings` | `openxiangda permission ...` / `openxiangda settings ...` |
21
+ | 账号 / 角色 / 权限 / 数据范围 / 查询参数授权 | `openxiangda-permission-settings` | `openxiangda design gates --topic permissions --json`,再 `openxiangda permission ...` / `openxiangda settings ...` |
22
22
  | 排查 / 快照 / 对比 / 报错 | `openxiangda-inspect` | `openxiangda app snapshot APP_XXX --profile <name> --json` |
23
23
  | 登录 / token / 切平台 | `openxiangda-core` | `openxiangda env --profile <name>` / `openxiangda auth status` |
24
24
 
25
25
  ## Always
26
26
 
27
27
  - 单文件改动默认增量发布:先 `workspace publish --profile <name> --changed --dry-run`,再 `--changed` 或 `--page <code>` / `--form <code>`。
28
+ - 高风险变更先走 SDD:`openxiangda sdd context --json` → `openxiangda sdd propose <change>` → 用户确认 → `openxiangda sdd approve <change>`;发布前 `openxiangda sdd verify <change> --changed`。
29
+ - 账号/角色/权限/RBAC/组织账号/查询参数授权需求先选权限模式:`managed-platform-account` / `existing-platform-user-assignment` / `static-role-permission` / `query-param-context`,并输出权限矩阵。
30
+ - 角色能新增角色、分配成员、授接口权限、维护权限组或管理组织账号时,`src/resources/roles/<code>.json` 必须声明 `apiPermissionCodes`,例如 `app:role:manage`、`app:page-permission-group:manage`、`app:form-permission-group:manage`、`app:organization:manage`。
28
31
  - 任何写操作前确认当前 profile:`openxiangda env --profile <name>`。
29
32
  - `.openxiangda/state.json` 是 profile 到 appType / 资源 ID 的权威映射,由 CLI 维护,不要手改。
30
33
  - 用户 token 在 `~/.openxiangda/profiles.json`;共享 env 在 `~/.openxiangda/.env`。
@@ -40,6 +43,9 @@ This is a `sy-lowcode-app-workspace` managed by the `openxiangda` CLI. Read [AGE
40
43
  - ❌ 在没有本地 `.openxiangda/state.json` 绑定时,去平台搜索同名应用"复用"。
41
44
  - ❌ 把 `formUuid` / `pageId` / `workflowId` / `automationId` 从 dev 复制到 prod。
42
45
  - ❌ 改一处就全量发布。
46
+ - ❌ 只隐藏按钮、只判断 query 参数、硬编码角色、前端模拟权限、假账号/假 ID、或用 `PermissionBoundary` 代替真实授权。
47
+ - ❌ 给用户挂“管理员”业务角色但不给该角色绑定 `app:role:manage` 等角色设置接口权限。
48
+ - ❌ 绕过 `sdk.organization` / `ctx.organization` 直接写平台账号/部门。
43
49
  - ❌ `openxiangda form create` / `form publish` / `page publish` 当作页面生成方式(仅底层修复)。
44
50
  - ❌ 把 token / AK / SK / 第三方密钥写进项目文件。
45
51
  - ❌ `npm publish` — 本工作区 `"private": true`,不发 npm 包。
@@ -9,6 +9,8 @@
9
9
 
10
10
  **架构类需求默认只规划、不实现。** 新应用、复杂页面、登录注册、公开访问、权限数据范围、流程自动化、连接器/通知等需求,先 `openxiangda doctor --json` + `openxiangda design gates --topic <code> --json`,输出设计并等待用户确认;确认前只允许读取、快照、dry-run、提问和写设计文档,不允许改源码、写平台、发布、部署或发送通知。
11
11
 
12
+ **高风险变更必须挂到 SDD change。** 修改 `src/forms/**`、`src/pages/**`、`src/resources/**`、`src/functions/**`、`src/automations/**`、`src/workflows/**`、`src/js-code-nodes/**`、`src/runtime/**` 或 `app-workspace.config.ts` 前,先运行 `openxiangda sdd context --json`,没有合适 change 就 `openxiangda sdd propose <change>`,等用户确认后运行 `openxiangda sdd approve <change>` 再实现。发布前运行 `openxiangda sdd verify <change> --changed` 和 `openxiangda workspace check --changed`。
13
+
12
14
  工作区内的 npm scripts(`publish:all` / `publish:oss` / `register`)已加 `_guard:publish` 守卫;缺少 `OPENXIANGDA_PROFILE` 环境变量时会立即 fail,提示重新走 CLI 入口。
13
15
 
14
16
  ## 路由表 — 用户意图 → 命令 / skill
@@ -23,7 +25,7 @@
23
25
  | 创建 / 改自定义代码页、portal、看板 | `openxiangda-page` | 编辑 `src/pages/<code>/` → `workspace publish --page <code>` |
24
26
  | 审批流程 / 流程节点 / JS_CODE | `openxiangda-workflow-automation` | `openxiangda workflow validate / create / publish` |
25
27
  | 自动化 / 定时任务 / 提交触发 / cron | `openxiangda-workflow-automation` | `openxiangda automation validate / create / publish / enable` |
26
- | 角色 / 权限组 / 字段权限 / 公开访问 | `openxiangda-permission-settings` | `openxiangda permission ...` / `openxiangda settings ...` |
28
+ | 账号 / 角色 / 权限组 / 字段权限 / 数据范围 / 查询参数授权 | `openxiangda-permission-settings` | `openxiangda design gates --topic permissions --json`,再 `openxiangda permission ...` / `openxiangda settings ...` |
27
29
  | 查应用结构 / 快照 / 对比 / 排查 / 报错 | `openxiangda-inspect` | `openxiangda app snapshot <APP_XXX> --profile <name> --json` |
28
30
  | 登录 / 切平台 / token / whoami | `openxiangda-core` | `openxiangda env --profile <name>` / `openxiangda auth status` |
29
31
  | 多表只读联表查询 / 报表数据源 | `openxiangda-form`(data view) | 在 `src/resources/data-views/<code>.json` 声明 → `resource publish` |
@@ -34,6 +36,9 @@
34
36
 
35
37
  - ✅ 先 preflight:`openxiangda env --profile <name>` + `openxiangda auth status --profile <name>` + `openxiangda update check --json`。
36
38
  - ✅ 架构类需求先 plan gate:`openxiangda doctor --profile <name> --json` + `openxiangda design gates --topic <code> --json`,用户确认后再实现。
39
+ - ✅ 高风险变更先 SDD gate:`openxiangda sdd context --json` → `openxiangda sdd propose <change>` → 用户确认 → `openxiangda sdd approve <change>`。
40
+ - ✅ 涉及账号、角色、权限、数据范围、组织账号、RBAC、查询参数授权时,先跑 `openxiangda design gates --topic permissions --json`,选择 `managed-platform-account` / `existing-platform-user-assignment` / `static-role-permission` / `query-param-context`,并输出权限矩阵后再改资源。
41
+ - ✅ 某个应用角色如果要创建角色、分配角色成员、给角色授接口权限、维护页面/表单权限组或管理组织账号,必须在 `src/resources/roles/<code>.json` 声明 `apiPermissionCodes`,例如 `app:role:manage`、`app:page-permission-group:manage`、`app:form-permission-group:manage`、`app:organization:manage`。
37
42
  - ✅ 单文件改动默认增量发布:`workspace publish --profile <name> --changed --dry-run` → `--changed` / `--page` / `--form`。
38
43
  - ✅ 用户 token 在 `~/.openxiangda/profiles.json`;项目 state 在 `.openxiangda/state.json`(只存 ID)。
39
44
  - ✅ 共享环境(`APP_OSS_*`、反馈机器人等)在 `~/.openxiangda/.env`,项目 `.env` 仅做 per-workspace override。
@@ -51,6 +56,9 @@
51
56
  - ❌ 把 `openxiangda form create` / `form publish` / `page publish` 当作日常页面生成方式。它们仅作底层修复 / 诊断。
52
57
  - ❌ 改一个文件就 `workspace publish` 全量。
53
58
  - ❌ 用户确认架构设计前就实现、写平台资源、发布、部署或发送通知。
59
+ - ❌ 只隐藏按钮、只判断 query 参数、硬编码角色、前端模拟权限、假账号/假 ID、或用 `PermissionBoundary` 代替真实授权。敏感数据必须由角色、页面/表单权限组、public-access grants 或 App Function 后端校验保护。
60
+ - ❌ 只给用户分配“管理员”业务角色,却不给该角色绑定 `app:role:manage` 等角色设置接口权限,然后期望他能新增账号、角色或权限组。
61
+ - ❌ 直接绕过 SDK 调平台账号/组织写接口;应用管理平台账号/部门必须使用 `sdk.organization` / `ctx.organization`,且当前操作者具备 `app:organization:manage`。
54
62
  - ❌ 把 token、AK、SK、第三方密钥写进项目文件。
55
63
  - ❌ `npm publish` —— 这个工作区是 `"private": true` 的应用工作区,不是要发到 npm 的 package。
56
64
  - ❌ 在 AI 生成的 `src/forms/**` / `src/pages/**` 里直接写原生 `<input>` / `<select>` / `<textarea>` / `<input type="file">`、手写 picker、手写 uploader、手写人员/部门选择器。原生控件只允许出现在 OpenXiangda SDK / 平台组件内部。
@@ -75,6 +83,7 @@ sy-lowcode-app-workspace/
75
83
  ├── AGENTS.md # 本文件
76
84
  ├── .qoder/rules/openxiangda.md # Qoder always-on 项目规则
77
85
  ├── .cursor/rules/openxiangda.mdc # Cursor always-on 项目规则
86
+ ├── openspec/ # SDD specs / changes / archive
78
87
  ├── .openxiangda/state.json # profile → appType → 资源 ID 的权威映射(CLI 维护)
79
88
  ├── app-workspace.config.ts
80
89
  ├── package.json # publish:all/publish:oss/register 都被 _guard:publish 守卫
@@ -31,4 +31,18 @@ export default defineAppWorkspaceConfig({
31
31
  pageMenuParentId: process.env.APP_PAGE_MENU_PARENT_ID || "",
32
32
  pageMenuIcon: process.env.APP_PAGE_MENU_ICON || "",
33
33
  },
34
+ compatibility: {
35
+ apiContracts:
36
+ process.env.OPENXIANGDA_API_CONTRACTS === "legacy" ? "legacy" : "strict",
37
+ legacyFallbacks: process.env.OPENXIANGDA_LEGACY_FALLBACKS === "true",
38
+ requestTrace: process.env.OPENXIANGDA_REQUEST_TRACE === "true",
39
+ },
40
+ governance: {
41
+ sdd: {
42
+ enabled: process.env.OPENXIANGDA_SDD_ENABLED === "false" ? false : true,
43
+ strictHighRisk:
44
+ process.env.OPENXIANGDA_SDD_STRICT_HIGH_RISK === "false" ? false : true,
45
+ path: process.env.OPENXIANGDA_SDD_PATH || "openspec",
46
+ },
47
+ },
34
48
  });
@@ -27,6 +27,9 @@ Run `pnpm examples:check` after editing examples, and run the normal
27
27
  `shared/components/`.
28
28
  - Use state fields and operation logs for business lifecycle flows.
29
29
  - Use workflow only for real approval tasks.
30
+ - For account, role, data scope, organization-account, RBAC, or query-param
31
+ authorization work, run `openxiangda design gates --topic permissions --json`
32
+ and choose an access governance mode before copying examples.
30
33
  - Query with pagination and structured conditions. Avoid large page sizes,
31
34
  client-side filtering, and broad `searchKeyWord` queries.
32
35
  - For admin UI, choose one optional default from `glass-home-dashboard`,
@@ -0,0 +1,239 @@
1
+ # Access Governance Pattern
2
+
3
+ This example is a generic account and permission governance skeleton. Copy only
4
+ the parts required by the selected permission mode. Do not publish this example
5
+ directly without changing codes, names, roles, and data scopes.
6
+
7
+ Start with:
8
+
9
+ ```bash
10
+ openxiangda design gates --topic permissions --json
11
+ ```
12
+
13
+ Choose one mode:
14
+
15
+ - `managed-platform-account`: app creates/maintains platform departments,
16
+ accounts, and role members.
17
+ - `existing-platform-user-assignment`: app assigns existing platform users to
18
+ app roles and business scopes.
19
+ - `static-role-permission`: fixed roles and permission groups only.
20
+ - `query-param-context`: query parameters are only context, filters, or ticket
21
+ input; they do not grant sensitive data access.
22
+
23
+ ## Resource Closure
24
+
25
+ ```text
26
+ src/forms/organization_unit/schema.ts
27
+ src/forms/system_account/schema.ts
28
+ src/forms/role_assignment/schema.ts
29
+ src/functions/sync_access_governance/index.ts
30
+ src/resources/roles/access-roles.json
31
+ src/resources/permissions/page-groups/access-admin-pages.json
32
+ src/resources/permissions/form-groups/role_assignment/access-admin.json
33
+ ```
34
+
35
+ ## Form Skeletons
36
+
37
+ `organization_unit` stores business organization units and the platform
38
+ department sync result:
39
+
40
+ ```ts
41
+ export const organizationUnitFields = [
42
+ { fieldId: "unitName", label: "组织名称", componentName: "TextField", placeholder: "请输入组织名称" },
43
+ { fieldId: "unitCode", label: "组织编码", componentName: "TextField", placeholder: "请输入稳定组织编码" },
44
+ { fieldId: "parentUnit", label: "上级组织", componentName: "SelectField", placeholder: "请选择上级组织", options: [] },
45
+ { fieldId: "manager", label: "负责人", componentName: "UserSelectField", placeholder: "请选择负责人" },
46
+ { fieldId: "platformDeptId", label: "平台部门ID", componentName: "TextField", behavior: "HIDDEN" },
47
+ {
48
+ fieldId: "syncStatus",
49
+ label: "同步状态",
50
+ componentName: "SelectField",
51
+ behavior: "HIDDEN",
52
+ options: [
53
+ { label: "待同步", value: "pending" },
54
+ { label: "已同步", value: "synced" },
55
+ { label: "同步失败", value: "failed" },
56
+ ],
57
+ },
58
+ { fieldId: "syncMessage", label: "同步说明", componentName: "TextAreaField", behavior: "HIDDEN" },
59
+ { fieldId: "lastSyncedAt", label: "最后同步时间", componentName: "DateField", behavior: "HIDDEN" },
60
+ ];
61
+ ```
62
+
63
+ `system_account` stores the app-owned account record and the platform user sync
64
+ result:
65
+
66
+ ```ts
67
+ export const systemAccountFields = [
68
+ { fieldId: "displayName", label: "姓名", componentName: "TextField", placeholder: "请输入姓名" },
69
+ { fieldId: "loginName", label: "登录名", componentName: "TextField", placeholder: "请输入登录名" },
70
+ { fieldId: "phone", label: "手机号", componentName: "TextField", placeholder: "请输入手机号" },
71
+ { fieldId: "unitCode", label: "所属组织", componentName: "SelectField", placeholder: "请选择所属组织", options: [] },
72
+ {
73
+ fieldId: "enabled",
74
+ label: "账号状态",
75
+ componentName: "RadioField",
76
+ placeholder: "请选择账号状态",
77
+ options: [
78
+ { label: "启用", value: "enabled" },
79
+ { label: "停用", value: "disabled" },
80
+ ],
81
+ },
82
+ { fieldId: "platformUserId", label: "平台用户ID", componentName: "TextField", behavior: "HIDDEN" },
83
+ { fieldId: "platformDeptId", label: "平台部门ID", componentName: "TextField", behavior: "HIDDEN" },
84
+ {
85
+ fieldId: "syncStatus",
86
+ label: "同步状态",
87
+ componentName: "SelectField",
88
+ behavior: "HIDDEN",
89
+ options: [
90
+ { label: "待同步", value: "pending" },
91
+ { label: "已同步", value: "synced" },
92
+ { label: "同步失败", value: "failed" },
93
+ ],
94
+ },
95
+ { fieldId: "lastSyncedAt", label: "最后同步时间", componentName: "DateField", behavior: "HIDDEN" },
96
+ ];
97
+ ```
98
+
99
+ `role_assignment` maps accounts or existing platform users to roles and business
100
+ scope keys:
101
+
102
+ ```ts
103
+ export const roleAssignmentFields = [
104
+ { fieldId: "account", label: "账号", componentName: "SelectField", placeholder: "请选择账号", options: [] },
105
+ { fieldId: "platformUser", label: "平台用户", componentName: "UserSelectField", placeholder: "请选择平台用户" },
106
+ {
107
+ fieldId: "roleCodes",
108
+ label: "角色",
109
+ componentName: "CheckboxField",
110
+ placeholder: "请选择角色",
111
+ options: [
112
+ { label: "权限管理员", value: "access_admin" },
113
+ { label: "范围内经办人", value: "scope_operator" },
114
+ ],
115
+ },
116
+ { fieldId: "businessUnit", label: "业务组织", componentName: "SelectField", placeholder: "请选择业务组织", options: [] },
117
+ { fieldId: "businessScopeKey", label: "业务范围键", componentName: "TextField", behavior: "HIDDEN" },
118
+ {
119
+ fieldId: "syncStatus",
120
+ label: "同步状态",
121
+ componentName: "SelectField",
122
+ behavior: "HIDDEN",
123
+ options: [
124
+ { label: "待同步", value: "pending" },
125
+ { label: "已同步", value: "synced" },
126
+ { label: "同步失败", value: "failed" },
127
+ ],
128
+ },
129
+ { fieldId: "syncMessage", label: "同步说明", componentName: "TextAreaField", behavior: "HIDDEN" },
130
+ ];
131
+ ```
132
+
133
+ ## Sync App Function Skeleton
134
+
135
+ ```ts
136
+ export default async function syncAccessGovernance(ctx, input) {
137
+ const roleCodes = ctx.operator?.roleCodes || [];
138
+ if (!roleCodes.includes("access_admin") && !ctx.operator?.hasFullAccess) {
139
+ throw new Error("ACCESS_DENIED");
140
+ }
141
+
142
+ if (input.action === "syncAccount") {
143
+ if (!ctx.organization?.accounts) throw new Error("ORGANIZATION_API_UNAVAILABLE");
144
+ // Current operator must have app:organization:manage.
145
+ return ctx.organization.accounts.create({
146
+ name: input.displayName,
147
+ loginName: input.loginName,
148
+ phone: input.phone,
149
+ departmentId: input.platformDeptId,
150
+ });
151
+ }
152
+
153
+ if (input.action === "syncRoleMembers") {
154
+ // Read role_assignment records, resolve platformUserId values, and update
155
+ // app role members through the platform role API exposed by OpenXiangda.
156
+ return { ok: true };
157
+ }
158
+
159
+ throw new Error("UNKNOWN_ACTION");
160
+ }
161
+ ```
162
+
163
+ ## Role Resource
164
+
165
+ ```json
166
+ [
167
+ {
168
+ "code": "access_admin",
169
+ "name": "权限管理员",
170
+ "description": "维护组织、账号、角色和权限资源",
171
+ "apiPermissionMode": "merge",
172
+ "apiPermissionCodes": [
173
+ "app:role:manage",
174
+ "app:page-permission-group:manage",
175
+ "app:form-permission-group:manage",
176
+ "app:organization:manage"
177
+ ]
178
+ },
179
+ {
180
+ "code": "scope_operator",
181
+ "name": "范围内经办人",
182
+ "description": "只能访问授权业务范围内的数据"
183
+ }
184
+ ]
185
+ ```
186
+
187
+ The initial grant of `access_admin` is normally done by a platform administrator
188
+ or an existing app administrator. If `access_admin` creates another role that
189
+ also manages accounts, roles, or permission groups, publish that new role with
190
+ its own `apiPermissionCodes`; management power is not inherited from the
191
+ creator.
192
+
193
+ ## Page Permission Group
194
+
195
+ ```json
196
+ {
197
+ "code": "access_admin_pages",
198
+ "name": "权限管理员页面",
199
+ "roles": ["access_admin"],
200
+ "menuCodes": ["access_governance"],
201
+ "routeCodes": ["access.governance"],
202
+ "pathPatterns": ["/view/:appType/admin/access/*"]
203
+ }
204
+ ```
205
+
206
+ ## Form Permission Group
207
+
208
+ ```json
209
+ {
210
+ "code": "role_assignment_admin",
211
+ "formCode": "role_assignment",
212
+ "name": "角色分配管理员",
213
+ "type": "view",
214
+ "roles": ["access_admin"],
215
+ "operations": ["view", "edit", "delete"],
216
+ "dataScope": [{ "type": "all" }],
217
+ "fieldAccessPolicy": {
218
+ "defaultAccess": "edit",
219
+ "fields": [
220
+ { "fieldId": "syncMessage", "access": "readonly" }
221
+ ]
222
+ }
223
+ }
224
+ ```
225
+
226
+ For `scope_operator`, use condition data permission against hidden scalar keys,
227
+ for example `businessScopeKey = ${CURRENT_USER_SCOPE}`. Do not fetch all rows
228
+ and filter them in the page.
229
+
230
+ ## Acceptance
231
+
232
+ - Permission mode and matrix are written in the SDD change or design document.
233
+ - `openxiangda permission audit --json` has no high-risk gaps.
234
+ - Allowed and denied role paths are both tested.
235
+ - Query parameter tampering does not expand sensitive access.
236
+ - Managed account writes are executed only by a user with
237
+ `app:organization:manage`.
238
+ - Roles that create roles, assign members, or maintain permission groups include
239
+ `apiPermissionCodes` such as `app:role:manage`.
@@ -53,6 +53,12 @@
53
53
  "path": "src/pages/service-ticket-ops",
54
54
  "description": "DataManagementList-based ops page with modular actions, drawer, timeline, hook, and service."
55
55
  },
56
+ {
57
+ "code": "access-governance",
58
+ "type": "permissions",
59
+ "path": "access-governance.md",
60
+ "description": "Generic account/role permission mode decision with organization, account, role assignment, sync function, role resource, page group, and form group skeletons."
61
+ },
56
62
  {
57
63
  "code": "role-governance",
58
64
  "type": "permissions",
@@ -59,6 +59,21 @@ Frontend code must not be responsible for data isolation or background jobs.
59
59
 
60
60
  ## Permissions
61
61
 
62
+ Run `openxiangda design gates --topic permissions --json` before implementing
63
+ account, role, RBAC, organization-account, data-scope, or query-param
64
+ authorization requirements.
65
+
66
+ Choose one pattern:
67
+
68
+ - `managed-platform-account`: app creates and maintains platform departments,
69
+ accounts, and role members. Start from `access-governance.md`.
70
+ - `existing-platform-user-assignment`: app selects existing platform users and
71
+ assigns roles/business scopes. Use a role assignment form with
72
+ `UserSelectField`.
73
+ - `static-role-permission`: fixed roles and permission groups only.
74
+ - `query-param-context`: query parameters are context, filters, or ticket input
75
+ only; they cannot grant sensitive access.
76
+
62
77
  For dynamic multi-role apps, create a business role table and synchronize it to
63
78
  platform app roles. User-facing scope fields should be maintainable controls:
64
79
  personnel/department fields for people and orgs, enum fields for fixed option
@@ -67,4 +82,13 @@ customers, and other records maintained by forms. Use `remoteSearch: true` when
67
82
  the source form can have many records. If the platform permission condition
68
83
  needs a scalar value, derive a hidden scope key such as `collegeScopeKey` or
69
84
  `ownerDeptScopeKey`. Use form permission groups with condition-style data
70
- permissions to enforce data isolation.
85
+ permissions and App Function role/scope checks to enforce data isolation.
86
+
87
+ When an app role is expected to create roles, assign role members, grant role
88
+ API permissions, maintain permission groups, or manage organization accounts,
89
+ declare the matching `apiPermissionCodes` on that role. `app:role:manage` is
90
+ required for app role creation and member assignment; page/form permission group
91
+ maintenance and organization account management use their own permission codes.
92
+
93
+ Frontend hiding and query parameters are not authorization. They must not be the
94
+ only barrier for sensitive data or write actions.
@@ -29,6 +29,18 @@ export interface AppWorkspaceConfig {
29
29
  dir?: string;
30
30
  publishLegacyBundle?: boolean;
31
31
  };
32
+ compatibility?: {
33
+ apiContracts?: "strict" | "legacy";
34
+ legacyFallbacks?: boolean;
35
+ requestTrace?: boolean;
36
+ };
37
+ governance?: {
38
+ sdd?: {
39
+ enabled?: boolean;
40
+ strictHighRisk?: boolean;
41
+ path?: string;
42
+ };
43
+ };
32
44
  }
33
45
 
34
46
  export type CustomPageEntryMode = "app-shell" | "plain-page";