openxiangda 1.0.109 → 1.0.111

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/lib/cli.js CHANGED
@@ -132,7 +132,7 @@ Usage:
132
132
  openxiangda menu update|sort [--json-file file] [--write-manifest]
133
133
  openxiangda menu bind <menuCode> --menu-id <id>
134
134
  openxiangda workflow compile <workflow.ts> [--check] [--stdout] [--out-definition file] [--out-preview file]
135
- openxiangda workflow list [--profile name] [--form-code code] [--json]
135
+ openxiangda workflow list [--profile name] [--form-code code] [--json] [--all]
136
136
  openxiangda workflow create <workflowCode> --form-code <formCode> --definition-json <file>
137
137
  openxiangda workflow bind <workflowCode> --workflow-id <id>
138
138
  openxiangda workflow publish <workflowCode|workflowId>
@@ -171,6 +171,7 @@ JS_CODE V2 使用 trusted_node;AI 源码必须写在 src/js-code-nodes/<script
171
171
  }
172
172
 
173
173
  const SUBCOMMAND_BOOLEAN_FLAGS = new Set([
174
+ '--all',
174
175
  '--dry-run',
175
176
  '--enabled',
176
177
  '--force',
@@ -1377,7 +1378,7 @@ async function workspace(args) {
1377
1378
  const [subcommand, ...rest] = args;
1378
1379
  const { flags, positional } = parseArgs(rest);
1379
1380
  if (wantsSubcommandHelp(subcommand, flags)) {
1380
- print('用法: openxiangda workspace init|bind|publish [--changed [--since ref]|--form code|--page code|--only list] [--dry-run] [--force] [--resources|--skip-resources]');
1381
+ printWorkspaceHelp();
1381
1382
  return;
1382
1383
  }
1383
1384
  const config = loadConfig();
@@ -1483,7 +1484,25 @@ async function workspace(args) {
1483
1484
  profileName,
1484
1485
  `/openxiangda-api/v1/apps/${encodeURIComponent(bound.appType)}/snapshot`
1485
1486
  );
1486
- runWorkspacePublish(profileName, profile, bound.appType, publishOptions.workspaceArgs);
1487
+ const reactSpaWorkspace = isReactSpaWorkspace();
1488
+ if (reactSpaWorkspace && !publishOptions.legacyFormBundle) {
1489
+ if (publishOptions.targetForm && !publishOptions.targetPage && !publishOptions.only && !publishOptions.changed) {
1490
+ runReactSpaFormSchemaPublish(profileName, profile, bound.appType, publishOptions);
1491
+ } else {
1492
+ fail(
1493
+ [
1494
+ 'React SPA 工作区不使用 workspace publish 发布前端页面。',
1495
+ '如需发布表单 schema,请运行:',
1496
+ ` openxiangda workspace publish --profile ${profileName} --form <formCode>`,
1497
+ '如需发布资源和前端运行时,请运行:',
1498
+ ` openxiangda resource publish --profile ${profileName}`,
1499
+ ` openxiangda runtime deploy --profile ${profileName}`,
1500
+ ].join('\n')
1501
+ );
1502
+ }
1503
+ } else {
1504
+ runWorkspacePublish(profileName, profile, bound.appType, publishOptions.workspaceArgs);
1505
+ }
1487
1506
  if (publishOptions.includeResources) {
1488
1507
  if (publishOptions.dryRun) {
1489
1508
  const manifest = loadWorkspaceResources();
@@ -1509,6 +1528,34 @@ async function workspace(args) {
1509
1528
  fail('用法: openxiangda workspace init|bind|publish [--changed [--since ref]|--form code|--page code|--only list] [--dry-run] [--force] [--resources|--skip-resources]');
1510
1529
  }
1511
1530
 
1531
+ function printWorkspaceHelp() {
1532
+ print(`OpenXiangda workspace
1533
+
1534
+ Usage:
1535
+ openxiangda workspace init [dir] [--name package-name] [--runtime legacy|react-spa] [--install] [--force]
1536
+ openxiangda workspace init [dir] --profile <name> --app-name <app-name> [--runtime legacy|react-spa] [--description text] [--install] [--force]
1537
+ openxiangda workspace init [dir] --profile <name> --app-type <APP_XXX> [--runtime legacy|react-spa] [--install] [--force]
1538
+ openxiangda workspace bind --profile <name> --app-type <APP_XXX>
1539
+ openxiangda workspace publish --profile <name> [--changed [--since ref]|--form code|--page code|--only list] [--dry-run] [--force] [--resources|--skip-resources] [--prune]
1540
+
1541
+ React SPA:
1542
+ openxiangda workspace publish --profile <name> --form <formCode> # sync form schema only
1543
+ openxiangda resource publish --profile <name>
1544
+ openxiangda runtime deploy --profile <name>
1545
+
1546
+ Options:
1547
+ --runtime legacy|react-spa Workspace template/runtime mode.
1548
+ --app-name <name> Create and bind a new platform app.
1549
+ --app-type <APP_XXX> Bind an existing platform app.
1550
+ --form <code> Publish one form schema/page.
1551
+ --page <code> Publish one classic page.
1552
+ --only pages/a,forms/b Publish selected classic resources.
1553
+ --changed [--since ref] Publish changed classic resources.
1554
+ --dry-run Show planned work without writing.
1555
+ --resources / --skip-resources Include or skip src/resources publish.
1556
+ --legacy-form-bundle Force legacy workspace publish in React SPA workspaces.`);
1557
+ }
1558
+
1512
1559
  async function app(args) {
1513
1560
  const [subcommand, ...rest] = args;
1514
1561
  const { flags, positional } = parseArgs(rest);
@@ -1962,7 +2009,7 @@ async function workflow(args) {
1962
2009
  const { subcommand, rest } = parseSubcommandArgs(args);
1963
2010
  const { flags, positional } = parseArgs(rest);
1964
2011
  if (wantsSubcommandHelp(subcommand, flags)) {
1965
- print('用法: openxiangda workflow compile|list|create|bind|pull|publish|delete|validate [--profile name] [--json]');
2012
+ printWorkflowHelp();
1966
2013
  return;
1967
2014
  }
1968
2015
  const config = loadConfig();
@@ -2023,8 +2070,9 @@ async function workflow(args) {
2023
2070
  }
2024
2071
  )
2025
2072
  );
2026
- if (flags.json) return writeJson(data);
2027
- print(JSON.stringify(data, null, 2));
2073
+ const output = flags.all ? data : filterManifestBackedWorkflows(data);
2074
+ if (flags.json) return writeJson(output);
2075
+ print(JSON.stringify(output, null, 2));
2028
2076
  return;
2029
2077
  }
2030
2078
 
@@ -2066,8 +2114,8 @@ async function workflow(args) {
2066
2114
  body: {
2067
2115
  formUuid,
2068
2116
  definitionJson,
2069
- ...(flags['view-json']
2070
- ? { viewJson: readJsonArg(flags['view-json'], 'view-json') }
2117
+ ...(flags['preview-json'] || flags['view-json']
2118
+ ? { viewJson: readJsonArg(flags['preview-json'] || flags['view-json'], 'preview-json') }
2071
2119
  : {}),
2072
2120
  },
2073
2121
  }
@@ -2174,6 +2222,49 @@ async function workflow(args) {
2174
2222
  fail('用法: openxiangda workflow compile|list|create|bind|pull|publish|delete|validate');
2175
2223
  }
2176
2224
 
2225
+ function printWorkflowHelp() {
2226
+ print(`OpenXiangda workflow
2227
+
2228
+ Usage:
2229
+ openxiangda workflow compile <workflow.ts> [--check] [--stdout|--json] [--out-definition file] [--out-preview file]
2230
+ openxiangda workflow list [--profile name] [--form-code code|--form-uuid FORM_XXX] [--published true|false] [--page n] [--page-size n] [--json] [--all]
2231
+ openxiangda workflow create <workflowCode> --form-code <formCode> --definition-json <file> [--preview-json file] [--publish]
2232
+ openxiangda workflow bind <workflowCode> --workflow-id <id> [--form-code code|--form-uuid FORM_XXX]
2233
+ openxiangda workflow pull <workflowCode|workflowId> [--out file] [--json]
2234
+ openxiangda workflow publish <workflowCode|workflowId> [--profile name]
2235
+ openxiangda workflow delete <workflowCode|workflowId> [--yes]
2236
+ openxiangda workflow validate --definition-json <file> [--strict] [--publish]
2237
+
2238
+ Notes:
2239
+ - Prefer semantic DSL: src/workflows/<code>/workflow.ts + defineWorkflow().
2240
+ - Run workspace publish --form <formCode> before publishing workflow resources for React SPA process forms.
2241
+ - workflow list hides platform shell workflows with empty resourceCode by default; pass --all to inspect raw platform rows.`);
2242
+ }
2243
+
2244
+ function filterManifestBackedWorkflows(data) {
2245
+ const shouldKeep = item => item && String(item.resourceCode || '').trim();
2246
+ if (Array.isArray(data)) {
2247
+ return data.filter(shouldKeep);
2248
+ }
2249
+ if (!data || typeof data !== 'object') return data;
2250
+ for (const key of ['data', 'items', 'list', 'records']) {
2251
+ if (Array.isArray(data[key])) {
2252
+ const filtered = data[key].filter(shouldKeep);
2253
+ return {
2254
+ ...data,
2255
+ [key]: filtered,
2256
+ totalCount: Object.prototype.hasOwnProperty.call(data, 'totalCount')
2257
+ ? filtered.length
2258
+ : data.totalCount,
2259
+ total: Object.prototype.hasOwnProperty.call(data, 'total')
2260
+ ? filtered.length
2261
+ : data.total,
2262
+ };
2263
+ }
2264
+ }
2265
+ return data;
2266
+ }
2267
+
2177
2268
  async function automation(args) {
2178
2269
  const { subcommand, rest } = parseSubcommandArgs(args);
2179
2270
  const { flags, positional } = parseArgs(rest);
@@ -3649,7 +3740,9 @@ async function resource(args) {
3649
3740
  return;
3650
3741
  }
3651
3742
 
3743
+ const target = getWorkspaceTarget(config, profileName, flags);
3652
3744
  const validation = validateWorkspaceResources(manifest);
3745
+ validateWorkspaceResourceBindings(manifest, target.bound, validation);
3653
3746
  await validateCompiledWorkflowResources(manifest, validation);
3654
3747
  if (subcommand === 'validate') {
3655
3748
  if (flags.json) return writeJson(validation);
@@ -3663,7 +3756,6 @@ async function resource(args) {
3663
3756
  fail(`资源配置校验失败: ${validation.errors[0]}`);
3664
3757
  }
3665
3758
 
3666
- const target = getWorkspaceTarget(config, profileName, flags);
3667
3759
  const dryRun = subcommand === 'publish' && Boolean(flags['dry-run']);
3668
3760
  if (subcommand === 'plan' || dryRun) {
3669
3761
  const plan = await buildResourcePlan(config, target, manifest);
@@ -5358,6 +5450,34 @@ function validateWorkspaceResources(manifest) {
5358
5450
  };
5359
5451
  }
5360
5452
 
5453
+ function validateWorkspaceResourceBindings(manifest, bound, validation) {
5454
+ for (const item of manifest.workflows || []) {
5455
+ if (!item.formCode && !item.form) continue;
5456
+ const formCode = item.formCode || item.form;
5457
+ const formBinding = bound?.resources?.forms?.[formCode];
5458
+ const formUuid = formBinding?.formUuid;
5459
+ if (!formUuid) {
5460
+ validation.errors.push(
5461
+ `${resourceLabel('workflow', item)}: formCode ${formCode} 未绑定。请先运行 openxiangda workspace publish --profile <name> --form ${formCode}`
5462
+ );
5463
+ continue;
5464
+ }
5465
+ const localSchemaPath = path.join(process.cwd(), 'src', 'forms', formCode, 'schema.ts');
5466
+ if (fs.existsSync(localSchemaPath) && !formBinding.schemaSyncedAt) {
5467
+ validation.errors.push(
5468
+ `${resourceLabel('workflow', item)}: formCode ${formCode} 已绑定但本地 schema 尚未同步。请先运行 openxiangda workspace publish --profile <name> --form ${formCode}`
5469
+ );
5470
+ }
5471
+ if (formBinding.formType && formBinding.formType !== 'process') {
5472
+ validation.errors.push(
5473
+ `${resourceLabel('workflow', item)}: formCode ${formCode} 当前 formType=${formBinding.formType},流程表单需要 process`
5474
+ );
5475
+ }
5476
+ }
5477
+ validation.valid = validation.errors.length === 0;
5478
+ return validation;
5479
+ }
5480
+
5361
5481
  async function validateCompiledWorkflowResources(manifest, validation) {
5362
5482
  for (const item of manifest.workflows || []) {
5363
5483
  if (!item.workflowFile) continue;
@@ -9663,6 +9783,7 @@ function normalizeWorkspacePublishOptions(flags) {
9663
9783
  if (since) workspaceArgs.push('--since', since);
9664
9784
  if (dryRun) workspaceArgs.push('--dry-run');
9665
9785
  if (force) workspaceArgs.push('--force');
9786
+ if (flags['legacy-form-bundle']) workspaceArgs.push('--legacy-form-bundle');
9666
9787
 
9667
9788
  const targeted = Boolean(targetForm || targetPage || only || changed);
9668
9789
  const includeResources =
@@ -9671,6 +9792,11 @@ function normalizeWorkspacePublishOptions(flags) {
9671
9792
  return {
9672
9793
  workspaceArgs,
9673
9794
  dryRun,
9795
+ targetForm,
9796
+ targetPage,
9797
+ only,
9798
+ changed,
9799
+ legacyFormBundle: Boolean(flags['legacy-form-bundle']),
9674
9800
  includeResources,
9675
9801
  quietResourceSkip: !targeted || skipResources,
9676
9802
  };
@@ -9708,6 +9834,43 @@ function runWorkspacePublish(profileName, profile, appType, publishArgs = []) {
9708
9834
  }
9709
9835
  }
9710
9836
 
9837
+ function isReactSpaWorkspace() {
9838
+ const configFile = path.join(process.cwd(), 'app-workspace.config.ts');
9839
+ if (!fs.existsSync(configFile)) return false;
9840
+ const content = fs.readFileSync(configFile, 'utf8');
9841
+ return /runtimeMode\s*:\s*['"]react-spa['"]/.test(content);
9842
+ }
9843
+
9844
+ function runReactSpaFormSchemaPublish(profileName, profile, appType, publishOptions) {
9845
+ const packageFile = path.join(process.cwd(), 'package.json');
9846
+ if (!fs.existsSync(packageFile)) {
9847
+ fail('当前目录没有 package.json,无法识别工作区发布脚本');
9848
+ }
9849
+ const pkg = JSON.parse(fs.readFileSync(packageFile, 'utf8'));
9850
+ const scripts = pkg.scripts || {};
9851
+ const usePnpm = fs.existsSync(path.join(process.cwd(), 'pnpm-lock.yaml'));
9852
+ const command = usePnpm ? 'pnpm' : 'npm';
9853
+ const syncArgs = ['--form', publishOptions.targetForm];
9854
+ if (publishOptions.dryRun) syncArgs.push('--dry-run');
9855
+ const args = scripts['sync-schema']
9856
+ ? ['run', 'sync-schema', '--', ...syncArgs]
9857
+ : usePnpm
9858
+ ? ['exec', 'lowcode-workspace', 'sync-schema', ...syncArgs]
9859
+ : ['exec', '--', 'lowcode-workspace', 'sync-schema', ...syncArgs];
9860
+ print(
9861
+ `React SPA 表单 schema 发布${publishOptions.dryRun ? ' (dry-run)' : ''}: ${publishOptions.targetForm}`
9862
+ );
9863
+ const globalEnv = loadGlobalEnv();
9864
+ const result = spawnSync(command, args, {
9865
+ cwd: process.cwd(),
9866
+ stdio: 'inherit',
9867
+ env: buildWorkspacePublishEnv(profileName, profile, appType, globalEnv),
9868
+ });
9869
+ if (result.status !== 0) {
9870
+ process.exit(result.status || 1);
9871
+ }
9872
+ }
9873
+
9711
9874
  function buildWorkspacePublishEnv(profileName, profile, appType, globalEnv) {
9712
9875
  const env = { ...process.env };
9713
9876
  const injectedGlobalKeys = [];
@@ -498,6 +498,7 @@ const RESOURCE_EXPLAINS = {
498
498
  enable: true,
499
499
  },
500
500
  commands: [
501
+ 'openxiangda workspace publish --profile <name> --form expense_request',
501
502
  'openxiangda workflow compile src/workflows/expense_approval/workflow.ts --check',
502
503
  'openxiangda workflow compile src/workflows/expense_approval/workflow.ts --out-definition src/generated/workflows/expense_approval/definition.v3.json --out-preview src/generated/workflows/expense_approval/preview.json',
503
504
  'openxiangda resource validate workflow',
@@ -38,7 +38,7 @@ OpenXiangda supports two workspace modes. Classic `sy-lowcode-app-workspace` pub
38
38
 
39
39
  ### Hard rules — always
40
40
 
41
- - ✅ Publish classic workspaces through `openxiangda workspace publish --profile <name>`; publish React SPA workspaces through `openxiangda resource validate` → `openxiangda resource publish --dry-run` → `openxiangda resource publish` → `openxiangda runtime deploy`.
41
+ - ✅ Publish classic workspaces through `openxiangda workspace publish --profile <name>`; publish React SPA forms first with `openxiangda workspace publish --form <formCode>`, then resources/runtime through `openxiangda resource validate` → `openxiangda resource publish --dry-run` → `openxiangda resource publish` → `openxiangda runtime deploy`.
42
42
  - ✅ Architecture-class requests are plan-gated by default. For 新应用、复杂页面、登录注册、公开访问、权限数据范围、流程自动化、连接器/通知、外部集成, first run `openxiangda doctor --json` when a workspace exists and `openxiangda design gates --topic <code> --json`; then ask the user to confirm the design. Before confirmation, only read files, inspect/snapshot, dry-run, ask questions, and write/output design docs. Do not edit source files, write platform resources, send notifications, publish, or deploy.
43
43
  - ✅ `runtime deploy` defaults to staged multipart `dist/` uploads plus a final manifest. Use `--upload-mode legacy-json` only for old platform servers.
44
44
  - ✅ User token lives in `~/.openxiangda/profiles.json`; project state in `.openxiangda/state.json` (IDs only).
@@ -162,8 +162,8 @@ When the user provides a root domain such as `https://yida.wisejob.cn/`, use it
162
162
  - Use JS_CODE for node-local cross-form data queries, create/update/batch update operations, process termination, platform API calls, external HTTP calls, and backend trigger orchestration. For logic that pages, automations, and workflows should reuse through a stable backend entry, prefer App Function. Do not use JS_CODE for simple UI interactions, ordinary form validation, or display-only page behavior.
163
163
  - For workflow/automation JS_CODE nodes, prefer V2 `runtimeMode: "trusted_node"`. AI-authored source must be TypeScript under `src/js-code-nodes/<scriptCode>/index.ts`, `src/automations/<resourceCode>/index.ts`, or `src/functions/<functionCode>/index.ts`. `pnpm build-js-code --script <scriptCode>` runs TypeScript validation before bundling, and `sourceFile.localPath` should point to the TS source; the CLI builds, uploads, and replaces it with snapshot metadata during validate/create.
164
164
  - Form permission group resources must have stable local codes. Use unique `code` values such as `ticket_reporter_view` and `ticket_repairer_view`; do not rely on the form code when one form has multiple groups.
165
- - Workflow resources should prefer `workflowFile` + `defineWorkflow` semantic DSL over hand-written raw JSON. Run `openxiangda workflow compile <workflow.ts> --check` and `openxiangda resource publish --dry-run --profile <name>` before publish; use `sdk.process.resolveCapabilities(...)` or `ProcessActionBar` / `ProcessTimeline` from `openxiangda/runtime/react` for runtime buttons and timelines instead of hard-coded operation lists.
166
- - Workflow resources can be created as drafts. After resource publish, verify `openxiangda workflow list --profile <name> --json` and run `openxiangda workflow publish <workflowCode> --profile <name>` until `isPublished: true` is visible.
165
+ - Workflow resources should prefer `workflowFile` + `defineWorkflow` semantic DSL over hand-written raw JSON. For workflows bound by `formCode`, run `openxiangda workspace publish --form <formCode>` first so the process form schema/table is initialized, then run `openxiangda workflow compile <workflow.ts> --check` and `openxiangda resource publish --dry-run --profile <name>` before publish; use `sdk.process.resolveCapabilities(...)` or `ProcessActionBar` / `ProcessTimeline` from `openxiangda/runtime/react` for runtime buttons and timelines instead of hard-coded operation lists.
166
+ - Workflow resources can be created as drafts. After resource publish, verify `openxiangda workflow list --profile <name> --json` and run `openxiangda workflow publish <workflowCode> --profile <name>` until `isPublished: true` is visible. `workflow list` hides platform shell workflows with empty `resourceCode` by default; pass `--all` only when diagnosing raw platform rows.
167
167
 
168
168
  ## Subskills
169
169
 
@@ -37,6 +37,7 @@ openxiangda workspace publish --profile <name>
37
37
  For Phase 6 React SPA workspaces (`workspace init --runtime react-spa`), do not use `workspace publish` for frontend routes. Publish in two explicit steps:
38
38
 
39
39
  ```bash
40
+ openxiangda workspace publish --profile <name> --form <formCode> # required for forms/workflows
40
41
  openxiangda resource validate --profile <name>
41
42
  openxiangda resource plan --profile <name>
42
43
  openxiangda resource publish --profile <name> --dry-run
@@ -93,7 +93,7 @@ openxiangda form bind customer --form-uuid FORM_XXX --profile dev
93
93
  openxiangda workflow list --profile dev --json
94
94
  ```
95
95
 
96
- Use `workflow pull` to inspect the live definition. Use `workflow list --json` after publishing and confirm the target shows `isPublished: true`; resource publish can create/update a draft without activating it. Use logical workflow codes locally; never copy a workflow ID from one profile to another.
96
+ Use `workflow pull` to inspect the live definition. Use `workflow list --json` after publishing and confirm the target shows `isPublished: true`; resource publish can create/update a draft without activating it. `workflow list` hides platform shell workflows with empty `resourceCode` by default; use `--all` only for raw platform diagnostics. Use logical workflow codes locally; never copy a workflow ID from one profile to another.
97
97
 
98
98
  ## JS_CODE V2
99
99
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openxiangda",
3
- "version": "1.0.109",
3
+ "version": "1.0.111",
4
4
  "description": "OpenXiangda CLI, workspace build tools, runtime SDK, and form components.",
5
5
  "private": false,
6
6
  "bin": {
@@ -22,6 +22,7 @@ import {
22
22
  ensureSchemaFormUuid,
23
23
  getOpenApiAccessToken,
24
24
  isOpenXiangdaMode,
25
+ markWorkspaceFormSchemaSynced,
25
26
  } from "./utils/form-api.mjs";
26
27
  import {
27
28
  assertSchemaSyncResult,
@@ -305,6 +306,12 @@ async function main() {
305
306
  } else {
306
307
  try {
307
308
  const response = await sendToApi(apiPayload, config, accessToken);
309
+ markWorkspaceFormSchemaSynced(config, form.name, apiPayload.formUuid, {
310
+ title: schema.formMeta?.title || form.name,
311
+ formType: apiPayload.formType || schema.formMeta?.formType || "receipt",
312
+ fieldCount: apiPayload.fieldCount,
313
+ tableName: response?.data?.tableName,
314
+ });
308
315
  console.log(` ✅ 同步成功,tableName=${response.data.tableName}`);
309
316
  results.push({ name: form.name, success: true, response });
310
317
  } catch (error) {
@@ -116,6 +116,13 @@ function saveWorkspaceFormBinding(config, formName, formUuid, extra = {}) {
116
116
  return true;
117
117
  }
118
118
 
119
+ export function markWorkspaceFormSchemaSynced(config, formName, formUuid, extra = {}) {
120
+ return saveWorkspaceFormBinding(config, formName, formUuid, {
121
+ ...extra,
122
+ schemaSyncedAt: new Date().toISOString(),
123
+ });
124
+ }
125
+
119
126
  function readExportedStringConstant(filePath, exportName) {
120
127
  if (!filePath || !fs.existsSync(filePath)) return "";
121
128
  const content = fs.readFileSync(filePath, "utf-8");
@@ -33,7 +33,7 @@ openxiangda runtime deploy --profile <name>
33
33
  openxiangda commands --json
34
34
  ```
35
35
 
36
- `pnpm deploy -- --profile <name>` 会按顺序执行 `openxiangda resource publish --profile <name>` 与 `openxiangda runtime deploy --profile <name>`。不要直接运行 `pnpm publish:all`、`pnpm openxiangda:publish` 或 `lowcode-workspace publish-all`;这些是 legacy classic workspace 的内部发布入口,会被 `scripts/guard-publish.mjs` 拦截。
36
+ `pnpm deploy -- --profile <name>` 会按顺序执行 `openxiangda resource publish --profile <name>` 与 `openxiangda runtime deploy --profile <name>`。如果应用包含 `src/forms/<formCode>/schema.ts`,先执行 `openxiangda workspace publish --profile <name> --form <formCode>` 初始化表单 schema/table。不要直接运行 `pnpm publish:all`、`pnpm openxiangda:publish` 或 `lowcode-workspace publish-all`;这些是 legacy classic workspace 的内部发布入口,会被 `scripts/guard-publish.mjs` 拦截。
37
37
 
38
38
  完整发布顺序:
39
39
 
@@ -4,7 +4,8 @@ const lines = [
4
4
  '',
5
5
  '错误:React SPA 工作区不要直接调用 lowcode-workspace publish-all。',
6
6
  '',
7
- 'React SPA 的发布入口分两步,且必须显式指定 profile:',
7
+ 'React SPA 的发布入口必须显式指定 profile:',
8
+ ' openxiangda workspace publish --profile <name> --form <formCode> # 仅同步表单 schema',
8
9
  ' openxiangda resource publish --profile <name>',
9
10
  ' openxiangda runtime deploy --profile <name>',
10
11
  '',