openxiangda-cli 2.0.0-alpha.151 → 2.0.0-alpha.154

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-cli",
3
- "version": "2.0.0-alpha.151",
3
+ "version": "2.0.0-alpha.154",
4
4
  "description": "Thin application-level CLI for OpenXiangda 2.0.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -21,10 +21,10 @@
21
21
  ],
22
22
  "dependencies": {
23
23
  "@oclif/core": "4.13.3",
24
- "openxiangda-contracts": "2.0.0-alpha.78",
25
- "openxiangda-devkit-core": "2.0.0-alpha.98",
26
- "openxiangda-mcp": "2.0.0-alpha.98",
27
- "openxiangda-skill-kit": "2.0.0-alpha.120"
24
+ "openxiangda-contracts": "2.0.0-alpha.80",
25
+ "openxiangda-devkit-core": "2.0.0-alpha.101",
26
+ "openxiangda-mcp": "2.0.0-alpha.101",
27
+ "openxiangda-skill-kit": "2.0.0-alpha.123"
28
28
  },
29
29
  "devDependencies": {
30
30
  "tsx": "4.23.12",
@@ -42,6 +42,6 @@
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
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.
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
- - 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` use the compiler-owned process operation and Durable Business Process Command; 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 `adminApplicationTodoCenterPage()`; never query Notification Hub management APIs from a user page.
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.
47
47
  - AI-native clients start the workspace protocol through the same pinned binary: `pnpm exec openxiangda --mcp-stdio --cwd <workspace>`. Read `openxiangda://workspace/contracts` or call `contract_describe`; for first-time menu authoring, copy `data.adminNavigationAuthoring.suggestion.expression` once into `frontend.admin.navigation` with its listed `openxiangda/config` imports, then edit that application-owned declaration. Never treat the proposal as runtime discovery. When AppSpec is enabled, also read `openxiangda://workspace/appspec` or call `appspec_context`. Require `aiCatalog` and `aiCatalogDigest` to match the normal compiler output. This is a stdio transport mode, not another application-owned server. Never write an application MCP server, Catalog file, preview store or second authorization path.
@@ -23,8 +23,8 @@ pnpm openxiangda dev
23
23
  逐项绑定本地组件并传给 `OpenXiangdaApplication`。后者是唯一的
24
24
  BrowserRouter、RuntimeBoundary、Refine、generated routes 和后台 Shell
25
25
  所有者;用户页可以拥有独立布局,但不要再建 router/Shell。要使用通用应用待办中心,
26
- 在显式后台导航组中加入 `adminApplicationTodoCenterPage()`;平台生成
27
- `/admin/todos` 与 `/m/todos`,只读取当前用户的消息/待办投影并统一解析目标页面。
26
+ 声明 `frontend.user.applicationTodoCenter: true`;平台生成 `/todos` 与 `/m/todos`,
27
+ 只读取当前用户的消息/待办投影并统一解析目标页面。
28
28
 
29
29
  自定义后台页使用 `OpenXiangdaAdminPage` 作为页面根;Ant Design 组件自动继承平台
30
30
  `ConfigProvider`,图表等 TypeScript 渲染值读取 `theme.useToken()`,自定义 CSS
@@ -15,7 +15,7 @@
15
15
  "@nestjs/common": "11.1.29",
16
16
  "@nestjs/core": "11.1.29",
17
17
  "@nestjs/platform-fastify": "11.1.29",
18
- "openxiangda": "2.0.0-alpha.70",
18
+ "openxiangda": "2.0.0-alpha.74",
19
19
  "reflect-metadata": "0.2.2",
20
20
  "rxjs": "7.8.2"
21
21
  },
@@ -90,44 +90,7 @@ const applicationContributions = defineApplicationContributions(detailRoutes, {
90
90
  },
91
91
  });
92
92
 
93
- const adminPages = [
94
- {
95
- code: 'application:todo-center',
96
- kind: 'application-todo-center',
97
- path: '/admin/todos',
98
- label: '待办中心',
99
- navigationEligible: true,
100
- },
101
- {
102
- code: 'workflow:work-center',
103
- kind: 'workflow-work-center',
104
- path: '/admin/work-center',
105
- label: '我的审批',
106
- navigationEligible: true,
107
- },
108
- {
109
- code: 'workflow:purchase-approval:launch',
110
- kind: 'workflow-launch',
111
- path: '/admin/workflows/purchase-approval/new',
112
- label: '发起采购审批',
113
- navigationEligible: true,
114
- workflowCode: 'purchase-approval',
115
- },
116
- {
117
- code: 'workflow:task',
118
- kind: 'workflow-task',
119
- path: '/tasks/:taskId',
120
- label: '审批任务',
121
- navigationEligible: false,
122
- },
123
- {
124
- code: 'workflow:instance',
125
- kind: 'workflow-instance',
126
- path: '/workflows/:instanceId',
127
- label: '流程详情',
128
- navigationEligible: false,
129
- },
130
- ] as const satisfies AdminPagesInput;
93
+ const adminPages = [] as const satisfies AdminPagesInput;
131
94
 
132
95
  const routeManifest = {
133
96
  schemaVersion: 'openxiangda.application-route-manifest/v3',
@@ -148,8 +111,8 @@ const routeManifest = {
148
111
  kind: 'application-todo-center',
149
112
  desktop: {
150
113
  routeCode: 'application.todo-center.desktop',
151
- path: '/admin/todos',
152
- surface: 'admin',
114
+ path: '/todos',
115
+ surface: 'user',
153
116
  pathParams: [],
154
117
  requiresAuthentication: true,
155
118
  },
@@ -166,8 +129,8 @@ const routeManifest = {
166
129
  kind: 'workflow-work-center',
167
130
  desktop: {
168
131
  routeCode: 'workflow.work.center.desktop',
169
- path: '/admin/work-center',
170
- surface: 'admin',
132
+ path: '/work-center',
133
+ surface: 'user',
171
134
  pathParams: [],
172
135
  requiresAuthentication: true,
173
136
  },
@@ -185,7 +148,7 @@ const routeManifest = {
185
148
  desktop: {
186
149
  routeCode: 'workflow.task.desktop',
187
150
  path: '/tasks/:taskId',
188
- surface: 'admin',
151
+ surface: 'user',
189
152
  pathParams: ['taskId'],
190
153
  requiresAuthentication: true,
191
154
  },
@@ -203,7 +166,7 @@ const routeManifest = {
203
166
  desktop: {
204
167
  routeCode: 'workflow.instance.desktop',
205
168
  path: '/workflows/:instanceId',
206
- surface: 'admin',
169
+ surface: 'user',
207
170
  pathParams: ['instanceId'],
208
171
  requiresAuthentication: true,
209
172
  },
@@ -221,9 +184,9 @@ const routeManifest = {
221
184
  workflowCode: 'purchase-approval',
222
185
  desktop: {
223
186
  routeCode: 'workflow.purchase-approval.launch.desktop',
224
- path: '/admin/workflows/purchase-approval/new',
225
- surface: 'admin',
226
- pathParams: [],
187
+ path: '/workflows/:workflowCode/start',
188
+ surface: 'user',
189
+ pathParams: ['workflowCode'],
227
190
  requiresAuthentication: true,
228
191
  },
229
192
  mobile: {
@@ -238,26 +201,10 @@ const routeManifest = {
238
201
  digest: '34cc08a02cd187a07d9e2d2f99c1080b5b86f3a4512f9e0f43ff4cb05b7195fa',
239
202
  } as const;
240
203
 
241
- const adminNavigation = [
242
- {
243
- code: 'workflow-center',
244
- label: '流程中心',
245
- icon: 'workflow',
246
- order: 0,
247
- items: [
248
- { pageCode: 'application:todo-center', label: '待办中心', order: 0 },
249
- { pageCode: 'workflow:work-center', label: '我的审批', order: 1 },
250
- {
251
- pageCode: 'workflow:purchase-approval:launch',
252
- label: '发起采购审批',
253
- order: 2,
254
- },
255
- ],
256
- },
257
- ] as const satisfies AdminNavigationInput;
204
+ const adminNavigation = [] as const satisfies AdminNavigationInput;
258
205
 
259
206
  const fixtureParams = new URLSearchParams(window.location.search);
260
- const initialPath = fixtureParams.get('initial') || '/admin/work-center';
207
+ const initialPath = fixtureParams.get('initial') || '/work-center';
261
208
  const runtimeBase = fixtureParams.get('base')?.trim();
262
209
  if (runtimeBase) {
263
210
  for (const [name, content] of [
@@ -409,8 +409,8 @@ function detail(completed = false, customDetail = false) {
409
409
 
410
410
  function launchSurface() {
411
411
  return {
412
- schemaVersion: 'openxiangda.workflow-launch-surface/v2',
413
- protocolVersion: 'workflow_launch_surface_v2',
412
+ schemaVersion: 'openxiangda.workflow-launch-surface/v3',
413
+ protocolVersion: 'workflow_launch_surface_v3',
414
414
  surfaceRevision: 'launch-revision-1',
415
415
  engineVersion: '2.0',
416
416
  appCode,
@@ -434,7 +434,7 @@ function launchSurface() {
434
434
  contractRevisionId: 'contract-revision-1',
435
435
  },
436
436
  paths: {
437
- desktop: '/admin/workflows/purchase-approval/new',
437
+ desktop: '/workflows/purchase-approval/start',
438
438
  mobile: '/m/workflows/purchase-approval/start',
439
439
  },
440
440
  commit: {
@@ -442,6 +442,15 @@ function launchSurface() {
442
442
  href: `/openxiangda-api/v2/applications/${appCode}/business-process/standard-commands`,
443
443
  idempotencyRequired: true,
444
444
  },
445
+ submission: {
446
+ kind: 'standard-process',
447
+ processOperationCode: 'openxiangda.workflow.purchase-approval.submit',
448
+ commit: {
449
+ method: 'POST',
450
+ href: `/openxiangda-api/v2/applications/${appCode}/business-process/standard-commands`,
451
+ idempotencyRequired: true,
452
+ },
453
+ },
445
454
  };
446
455
  }
447
456
 
@@ -785,7 +794,7 @@ test('renders a paged desktop work center from the current-user role union', asy
785
794
  page,
786
795
  }) => {
787
796
  await mockWorkflow(page);
788
- await page.goto('/workflow-experience.e2e.html?initial=/admin/work-center');
797
+ await page.goto('/workflow-experience.e2e.html?initial=/work-center');
789
798
  await expect(page.getByRole('heading', { name: '审批工作台' })).toBeVisible();
790
799
  await expect(page.getByRole('link', { name: '采购申请审批' })).toBeVisible();
791
800
  await expect(page.getByText('PO-2026-0825')).toBeVisible();
@@ -795,7 +804,7 @@ test('renders the generic desktop and mobile application todo center without a s
795
804
  page,
796
805
  }) => {
797
806
  await mockWorkflow(page);
798
- await page.goto('/workflow-experience.e2e.html?initial=/admin/todos');
807
+ await page.goto('/workflow-experience.e2e.html?initial=/todos');
799
808
  await expect(page.getByRole('heading', { name: '待办中心' })).toBeVisible();
800
809
  await expect(page.locator('.oxa-todo-table')).toBeVisible();
801
810
  await expect(page.locator('.oxa-todo-preview-card')).toHaveCount(0);
@@ -853,7 +862,7 @@ test('routes desktop and mobile work-center entries to the declared custom detai
853
862
  page,
854
863
  }) => {
855
864
  await mockWorkflow(page, { customDetail: true });
856
- await page.goto('/workflow-experience.e2e.html?initial=/admin/work-center');
865
+ await page.goto('/workflow-experience.e2e.html?initial=/work-center');
857
866
  await page.getByRole('link', { name: '采购申请审批' }).click();
858
867
  await expect(page.getByTestId('custom-workflow-detail')).toContainText(
859
868
  `${instanceId} / ${taskId}`
@@ -885,16 +894,16 @@ test('redirects a direct standard task URL to the declared custom detail page',
885
894
  );
886
895
  });
887
896
 
888
- test('renders a standalone desktop detail and redirects the legacy admin URL', async ({
897
+ test('renders a standalone desktop detail on the canonical user URL', async ({
889
898
  page,
890
899
  }) => {
891
900
  await mockWorkflow(page);
892
901
  await page.goto(
893
- workflowFixtureUrl(`/admin/tasks/${taskId}?from=legacy#approval`),
902
+ workflowFixtureUrl(`/tasks/${taskId}?from=todo#approval`),
894
903
  );
895
904
  await expect(page.locator('.oxa-workflow-desktop-standalone-page')).toBeVisible();
896
905
  await expect(page).toHaveURL(
897
- new RegExp(`/tasks/${taskId}\\?from=legacy#approval$`),
906
+ new RegExp(`/tasks/${taskId}\\?from=todo#approval$`),
898
907
  );
899
908
  await expect(page.locator('.oxa-app-layout')).toHaveCount(0);
900
909
  await expect(page.locator('.oxa-topbar')).toHaveCount(0);
@@ -942,7 +951,7 @@ test('shows an explicit authorization error instead of an empty work center', as
942
951
  }) => {
943
952
  test.setTimeout(45_000);
944
953
  await mockWorkflow(page, { forbidden: true });
945
- await page.goto('/workflow-experience.e2e.html?initial=/admin/work-center', {
954
+ await page.goto('/workflow-experience.e2e.html?initial=/work-center', {
946
955
  waitUntil: 'domcontentloaded',
947
956
  });
948
957
  await expect(page.getByRole('alert')).toContainText('403: forbidden', {
@@ -957,7 +966,7 @@ test('negotiates direct standard routes after BrowserRouter listener setup', asy
957
966
  await mockWorkflow(page);
958
967
  await page.setViewportSize({ width: 360, height: 844 });
959
968
  await page.goto(
960
- workflowFixtureUrl('/admin/todos?view=pending#unread', {
969
+ workflowFixtureUrl('/todos?view=pending#unread', {
961
970
  state: 'deep-link',
962
971
  }),
963
972
  );
@@ -982,13 +991,13 @@ test('negotiates direct standard routes after BrowserRouter listener setup', asy
982
991
  await expect(page.locator('.oxa-todo-center-mobile')).toBeVisible();
983
992
  await page.setViewportSize({ width: 901, height: 844 });
984
993
  await expect(page.locator('.oxa-todo-center-desktop')).toBeVisible();
985
- await expect(page).toHaveURL(/\/admin\/todos\?view=pending#unread$/);
994
+ await expect(page).toHaveURL(/\/todos\?view=pending#unread$/);
986
995
  expect(await page.evaluate(() => window.history.length)).toBe(historyLength);
987
996
  expect(await page.evaluate(() => window.history.state?.usr)).toEqual({
988
997
  routeNegotiationState: 'deep-link',
989
998
  });
990
999
  await page.goBack();
991
- await expect(page).not.toHaveURL(/\/admin\/todos\?view=pending#unread$/);
1000
+ await expect(page).not.toHaveURL(/\/todos\?view=pending#unread$/);
992
1001
 
993
1002
  // Direct task and instance URLs carry their dynamic parameter and deep-link
994
1003
  // context to the independent mobile renderer at the narrow width.
@@ -1018,7 +1027,7 @@ test('negotiates launch pairs in both directions and preserves the mount basenam
1018
1027
  await page.setViewportSize({ width: 360, height: 844 });
1019
1028
  await page.goto(
1020
1029
  workflowFixtureUrl(
1021
- `${base}/admin/workflows/purchase-approval/new?source=todo#launch`,
1030
+ `${base}/workflows/purchase-approval/start?source=todo#launch`,
1022
1031
  { base },
1023
1032
  ),
1024
1033
  );
@@ -1033,7 +1042,7 @@ test('negotiates launch pairs in both directions and preserves the mount basenam
1033
1042
  await expect(page.locator('.oxa-workflow-submission-desktop')).toBeVisible();
1034
1043
  await expect(page).toHaveURL(
1035
1044
  new RegExp(
1036
- `${base}/admin/workflows/purchase-approval/new\\?source=todo#launch$`,
1045
+ `${base}/workflows/purchase-approval/start\\?source=todo#launch$`,
1037
1046
  ),
1038
1047
  );
1039
1048
  });
@@ -14,7 +14,7 @@
14
14
  "@ant-design/icons": "6.2.3",
15
15
  "@refinedev/core": "5.0.12",
16
16
  "antd": "6.4.3",
17
- "openxiangda": "2.0.0-alpha.70",
17
+ "openxiangda": "2.0.0-alpha.74",
18
18
  "react": "19.2.8",
19
19
  "react-dom": "19.2.8",
20
20
  "react-router-dom": "7.8.2"
@@ -16,6 +16,7 @@ import {
16
16
  authenticationSurfaces,
17
17
  anonymousPublicAccess,
18
18
  adminNavigation,
19
+ adminAccess,
19
20
  adminPages,
20
21
  resourceDefinitions,
21
22
  workflowDefinitions,
@@ -50,6 +51,7 @@ ReactDOM.createRoot(document.getElementById('root')!).render(
50
51
  <OpenXiangdaApplication
51
52
  appCode={appCode}
52
53
  appName={appName}
54
+ adminAccess={adminAccess}
53
55
  adminNavigation={adminNavigation}
54
56
  adminPages={adminPages}
55
57
  routeManifest={routeManifest}
@@ -102,7 +102,7 @@ test('published E2E harnesses do not write evidence into the platform source rep
102
102
  assert.doesNotMatch(workflowSource, /docs\/architecture|evidenceDirectory/);
103
103
  });
104
104
 
105
- test('E2E experience fixtures explicitly own every tested admin route', () => {
105
+ test('E2E experience fixtures explicitly own every tested route', () => {
106
106
  const resourceFixture = readFileSync(
107
107
  new URL('../e2e/resource-experience-fixture.tsx', import.meta.url),
108
108
  'utf8'
@@ -121,7 +121,13 @@ test('E2E experience fixtures explicitly own every tested admin route', () => {
121
121
  assert.match(workflowFixture, /code: 'workflow:work-center'/);
122
122
  assert.match(workflowFixture, /code: 'workflow:task'/);
123
123
  assert.match(workflowFixture, /code: 'workflow:instance'/);
124
- assert.match(workflowFixture, /pageCode: 'workflow:work-center'/);
124
+ assert.match(workflowFixture, /path: '\/work-center'/);
125
+ assert.match(workflowFixture, /path: '\/tasks\/:taskId'/);
126
+ assert.match(workflowFixture, /path: '\/workflows\/:instanceId'/);
127
+ assert.doesNotMatch(
128
+ workflowFixture,
129
+ /path: '\/(?:m\/)?admin\/(?:todos|work-center|tasks|workflows)/
130
+ );
125
131
  });
126
132
 
127
133
  test('browser acceptance serializes the prewarmed Vite HTML entrypoints', () => {
@@ -23,7 +23,7 @@
23
23
  "build": "pnpm --recursive build"
24
24
  },
25
25
  "devDependencies": {
26
- "openxiangda": "2.0.0-alpha.70",
26
+ "openxiangda": "2.0.0-alpha.74",
27
27
  "typescript": "5.9.3"
28
28
  }
29
29
  }
@@ -113,6 +113,7 @@ export const platformAuthManifest = {
113
113
  }
114
114
  ]
115
115
  } as const;
116
+ export const adminAccess = undefined;
116
117
  export const routeManifest = {
117
118
  "schemaVersion": "openxiangda.application-route-manifest/v3",
118
119
  "appCode": "openxiangda-application",
@@ -165,6 +166,7 @@ export type ApplicationAuthenticationMethod = (typeof authenticationMethods)[num
165
166
  export type ApplicationAuthenticationSurface = (typeof authenticationSurfaces)[keyof typeof authenticationSurfaces];
166
167
  export type ApplicationAuthenticationSurfaceCode = ApplicationAuthenticationSurface["routeCode"];
167
168
  export type AnonymousPublicAccess = typeof anonymousPublicAccess;
169
+ export type AdminAccess = typeof adminAccess;
168
170
  export type RouteManifest = typeof routeManifest;
169
171
  export type AdminPage = (typeof adminPages)[number];
170
172
  export type AdminNavigationGroup = (typeof adminNavigation)[number];