openxiangda-skill-kit 2.0.0-alpha.124 → 2.0.0-alpha.126

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openxiangda-skill-kit",
3
- "version": "2.0.0-alpha.124",
3
+ "version": "2.0.0-alpha.126",
4
4
  "description": "Validation and deterministic packaging for OpenXiangda 2.0 AI skills.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -17,7 +17,7 @@
17
17
  "README.md"
18
18
  ],
19
19
  "dependencies": {
20
- "openxiangda-devkit-core": "2.0.0-alpha.102"
20
+ "openxiangda-devkit-core": "2.0.0-alpha.104"
21
21
  },
22
22
  "devDependencies": {
23
23
  "tsx": "4.23.12",
@@ -4,7 +4,7 @@
4
4
  {
5
5
  "name": "openxiangda-v2",
6
6
  "description": "Use when researching, designing, building, testing, or delivering an OpenXiangda 2.0 application, including anonymous public forms and other no-account external-user pages.",
7
- "sha256": "1f5bfa9cd4c29a971b9f77dbaf4023a339ddeaef6feb3bf5dcc8f8a90b261d8b"
7
+ "sha256": "99c39264c2159b1236db8b61a58825433b3083fd996ed63e29be4298f8b8f45f"
8
8
  }
9
9
  ]
10
10
  }
@@ -27,6 +27,7 @@ Use this order:
27
27
  3. If the workspace contains `appspec/`, the user asks to maintain requirements, or the task changes observable behavior, read [AppSpec](references/appspec.md), load the bounded index with `pnpm openxiangda spec context --json`, then select only the relevant stable ID. AppSpec is optional and never a release gate; L0 changes create no record.
28
28
  4. Read [Architecture](references/architecture.md), assign one owner to each capability and keep ordinary CRUD on platform Data API.
29
29
  5. For every resource or permission change, read [Data and authorization](references/data-authz.md) before editing `openxiangda.config.ts`.
30
+ When a custom authenticated page lets business users maintain application-role memberships or delegate that maintenance authority, also read [Frontend](references/frontend.md). Use the current-user role-management browser SDK documented there; never add actor selection, an application-owned grant table, or a proxy authorization service.
30
31
  6. If the request mentions external users without platform accounts, anonymous or guest access, a public form/page, resumable submission, duplicate checks, public uploads, or letting a visitor read their own submissions, read [Anonymous public access](references/public-access.md). Use the declared platform contract; do not invent a guest role, public Native Data API, fingerprint identity or application-owned token.
31
32
  7. Read [Frontend](references/frontend.md) for pages or fields and [Backend actions](references/backend.md) only for a real business action.
32
33
  8. When the application explicitly enables standard approval, application events or notifications, read [Workflow, Events and Notification Hub](references/workflow-events.md). Keep them optional and out of ordinary CRUD.
@@ -61,8 +61,13 @@ const visitorReservations = {
61
61
  ```
62
62
 
63
63
  Put it in `data: { resources: [visitorReservations] }`. Resource CRUD
64
- capabilities are derived by `resourceCapabilityCodes`; grant the needed values
65
- to roles. Directory-backed roles also require
64
+ capabilities are derived by `resourceCapabilityCodes`. For a Native resource,
65
+ grant `read` to each role that may enter its generated page; the compiler also
66
+ grants that role every enabled generated create/update/delete operation. Remove
67
+ an operation only when the role must be restricted, using the corresponding
68
+ code in `deniedCapabilities`. The compiler validates the denial and seals only
69
+ the resulting allow list, so runtime authorization has no second deny model.
70
+ Non-Native mutation owners never receive this expansion. Directory-backed roles also require
66
71
  `app:<app-code>:directory:read`. Explicit field `access` capability codes are
67
72
  also granted only to the intended roles. They are owned and exported by the
68
73
  generated field policy, so do not repeat them in `authz.capabilities`.
@@ -301,4 +306,100 @@ rows or recover a dead-letter job. Rebuild/recovery are idempotent and accept an
301
306
  input. Do not write `sourceCode`, canonical membership rows or relationship
302
307
  grant rows yourself.
303
308
 
309
+ ## Custom role-management pages
310
+
311
+ Use the current-user browser SDK when a custom desktop or mobile page must
312
+ maintain this application's role members. Do not create an application role
313
+ table, call the platform `role` controller, forward a developer token, or add a
314
+ NestJS endpoint that accepts a user/tenant/role from the browser.
315
+
316
+ An application or platform super administrator initializes delegation by
317
+ attaching one role-management grant to a business role. `manageAllRoles: true`
318
+ means every current application role and therefore requires
319
+ `managedRoleCodes: []`; otherwise list the exact role codes. Every grant must
320
+ include `membership.read` and may add `membership.assign`,
321
+ `membership.update`, `membership.revoke`, and `management.delegate`:
322
+
323
+ ```ts
324
+ import {
325
+ createRoleManagementGrant,
326
+ loadRoleManagementCatalog,
327
+ } from 'openxiangda/core';
328
+
329
+ const catalog = await loadRoleManagementCatalog();
330
+ const managerAuthority = catalog.roleManagement.roles.find(
331
+ item => item.roleCode === 'business_manager',
332
+ );
333
+
334
+ await createRoleManagementGrant({
335
+ operationId: crypto.randomUUID(),
336
+ reason: '允许业务管理员维护场馆操作员',
337
+ subjectRoleCode: 'business_manager',
338
+ manageAllRoles: false,
339
+ managedRoleCodes: ['venue_operator'],
340
+ actions: [
341
+ 'membership.read',
342
+ 'membership.assign',
343
+ 'membership.update',
344
+ 'membership.revoke',
345
+ 'management.delegate',
346
+ ],
347
+ });
348
+ ```
349
+
350
+ The platform always evaluates the current logged-in user's complete
351
+ application-role union. A delegated manager can pass on only role/action pairs
352
+ already present in that union and needs `management.delegate` for both the
353
+ recipient role and every managed role. It cannot manufacture an all-role grant
354
+ from several selected-role grants.
355
+
356
+ Build the member page only from these SDK calls:
357
+
358
+ ```ts
359
+ import {
360
+ createRoleMembership,
361
+ listRoleMemberships,
362
+ searchRoleManagementUsers,
363
+ updateRoleMembership,
364
+ revokeRoleMembership,
365
+ } from 'openxiangda/core';
366
+
367
+ const users = await searchRoleManagementUsers({ keyword: '张' });
368
+ const page = await listRoleMemberships({
369
+ roleCode: 'venue_operator',
370
+ status: 'active',
371
+ limit: 20,
372
+ offset: 0,
373
+ });
374
+
375
+ await createRoleMembership({
376
+ operationId: crypto.randomUUID(),
377
+ reason: '张老师负责场馆日常运营',
378
+ userId: users.items[0]!.id,
379
+ roleCode: 'venue_operator',
380
+ scopeGrants: [],
381
+ });
382
+
383
+ const membership = page.items[0]!;
384
+ if (membership.maintainable) {
385
+ await updateRoleMembership(membership.id, {
386
+ operationId: crypto.randomUUID(),
387
+ reason: '调整角色有效期',
388
+ expectedRevision: membership.revision,
389
+ scopeGrants: membership.scopeGrants,
390
+ validFrom: null,
391
+ validTo: '2027-01-01T00:00:00.000Z',
392
+ });
393
+ }
394
+ ```
395
+
396
+ Use `listRoleManagementGrants`, `updateRoleManagementGrant` and
397
+ `revokeRoleManagementGrant` for the delegation page. All updates/revocations
398
+ require the row's latest `revision`. On HTTP 409, reload catalog and rows; never
399
+ retry with a guessed revision. Every successful mutation returns an immutable
400
+ `receipt`; `loadAuthorizationMutationReceipt(operationId)` reads it again for
401
+ the same actor. Show `immutableReason` for an authenticated-user or projection
402
+ membership and never try to mutate it. A hidden button is only UX—the server
403
+ returns 403 for every undelegated role/action.
404
+
304
405
  Run `pnpm openxiangda check` after every declaration or permission change.
@@ -28,6 +28,15 @@ union. Never fetch identity for the header, display role codes, add a second
28
28
  identity selector, or copy Shell and
29
29
  directory-client implementations into application source.
30
30
 
31
+ For a custom application role-management page, use only the typed functions in
32
+ `openxiangda/core`: `loadRoleManagementCatalog`, `listRoleMemberships`,
33
+ `searchRoleManagementUsers`, the membership mutations, and the
34
+ role-management-grant mutations. The catalog's `roleManagement` projection
35
+ drives button visibility, while the platform repeats the check for every
36
+ request. The “Custom role-management pages” section in
37
+ [Data and authorization](data-authz.md) defines the delegation and CAS
38
+ contract. Do not use the developer control-plane client in browser code.
39
+
31
40
  The application owns the editable admin information architecture through the
32
41
  typed `defineAdminNavigation`, `adminNavigationGroup`, `adminResourcePage`,
33
42
  and `adminOperationPage` helpers in `openxiangda/config`. The compiler emits
@@ -40,7 +40,7 @@
40
40
  - Visitor duplicate protection uses `createVisitorReservation({ duplicateMatch: { fieldCode: submittedValue }, ... })`. `duplicateMatch` is a non-empty value map, never a field-name array, and no mutable pre-read is allowed.
41
41
  - Desktop and mobile pages share values, validation and authorization, but use separate renderers. Options, members and departments store display snapshots; resource references store direct JSON display values; attachments, images and signatures use platform-managed file references.
42
42
  - `option.*`, `user.*`, `department.*` and `resource-ref.*` values never collapse to scalar IDs. Single values store one labeled object and multiple values store object arrays. `resource-ref.*` JSON is not a foreign key, trusted target snapshot or automatically refreshed copy; current target state is read by `resourceCode` plus `value`. A resource source `labelField` must be `text.short` or `text.long`; `serial-number` is allowed in source search, description and snapshot fields, but not as the label. `location` accepts only exact WGS84 coordinates captured by DingTalk or browser geolocation; it has no manual input or `manual` source. Roles that consume directory-backed fields explicitly include `app:<app-code>:directory:read`.
43
- - Derive role grants with `resourceCapabilityCodes(appCode, resourceCode)`. Declare current-user rows only with `currentUserDataPolicy(...)`; do not invent operators, values or alternate current-user spellings.
43
+ - Derive role grants with `resourceCapabilityCodes(appCode, resourceCode)`. Grant a Native resource's `read` capability to every role allowed to enter its generated page; the compiler adds enabled generated create/update/delete capabilities by default. Tighten only exceptional roles with `deniedCapabilities`, which is compile-time authoring input and is removed from the sealed role. Non-Native mutation owners never receive this expansion. Declare current-user rows only with `currentUserDataPolicy(...)`; do not invent operators, values or alternate current-user spellings.
44
44
  - Do not add compatibility aliases, migration branches or silent fallbacks for an earlier 2.0 alpha contract. Replace an incorrect contract and regenerate the application.
45
45
  - Standard Workflow and Notification Hub are optional 2.0 modules. Enable them only through canonical `openxiangda.config.ts` declarations and generated clients. `standalone`/`hidden-handoff` default to the compiler-owned process operation; action-owned submission must instead declare `launch.submission.kind: 'named-operation'` with sealed create/existing input and output bindings so the standard PC/mobile page calls the original Named Action and accepts an explicit no-Workflow result. Never add a browser save callback or call Workflow prepare/start directly. Custom pages launch only through a verified Named Action using `OpenXiangdaBusinessProcessService`. PC and mobile render the same `ProcessCommandSurface` independently and recover only by `commandId`. To replace Workflow detail, declare both desktop and mobile `detailRouteCode` values whose routes contain exactly `:instanceId`. Add the current-user todo page only with `frontend.user.applicationTodoCenter: true`; never query Notification Hub management APIs from a user page.
46
46
  - Run `pnpm openxiangda check --json` after contract changes and read `data.sealedArtifact`; check never seals, and an older `.openxiangda/build/app-package.json` is not the current check result. Use `pnpm openxiangda accept --plan <file>` only for optional real preproduction identity acceptance; it never blocks delivery. Deploy with `pnpm openxiangda deploy`, inspect with `pnpm openxiangda status` and `pnpm openxiangda logs`, and use the platform rollback command rather than mutating K3s directly.