openyida 2026.10.16 → 2026.10.18-2

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 (83) hide show
  1. package/README.md +20 -7
  2. package/bin/yida.js +16 -1
  3. package/lib/agent/runtime-catalog.json +12 -12
  4. package/lib/app/check-prd-completeness.js +63 -1
  5. package/lib/app/form-navigation.js +7 -3
  6. package/lib/app/update-app.js +3 -0
  7. package/lib/core/command-manifest.js +42 -12
  8. package/lib/core/locales/en.js +78 -5
  9. package/lib/core/locales/zh.js +78 -5
  10. package/lib/core/process-approver-query.js +152 -0
  11. package/lib/core/query-data.js +19 -1
  12. package/lib/core/redact.js +4 -3
  13. package/lib/design-plan/entry-navigation.js +29 -2
  14. package/lib/design-plan/materialize.js +30 -4
  15. package/lib/integration/integration-api.js +41 -4
  16. package/lib/integration/integration-baseline.js +68 -0
  17. package/lib/integration/integration-batch-data.js +177 -0
  18. package/lib/integration/integration-control.js +44 -0
  19. package/lib/integration/integration-create.js +30 -13
  20. package/lib/integration/integration-data-retrieve.js +237 -0
  21. package/lib/integration/integration-detail-log.js +105 -0
  22. package/lib/integration/integration-manage.js +133 -0
  23. package/lib/integration/integration-message-roles.js +49 -0
  24. package/lib/integration/integration-process-builder.js +17 -1
  25. package/lib/integration/integration-readback.js +7 -0
  26. package/lib/integration/integration-spec-builder.js +101 -233
  27. package/lib/integration/integration-triggers.js +73 -0
  28. package/lib/integration/integration-update.js +97 -98
  29. package/lib/permission/get-permission.js +14 -6
  30. package/lib/permission/matrix-availability.js +46 -0
  31. package/lib/permission/matrix-manage.js +80 -0
  32. package/lib/permission/package-type.js +29 -0
  33. package/lib/permission/permission-conditions.js +78 -0
  34. package/lib/permission/permission-manage.js +82 -0
  35. package/lib/permission/permission-scope.js +119 -0
  36. package/lib/permission/permit-package-service.js +37 -4
  37. package/lib/permission/save-permission.js +122 -21
  38. package/lib/process/configure-process.js +39 -0
  39. package/lib/process/process-fields.js +22 -0
  40. package/lib/process/services/process-actor-config.js +42 -0
  41. package/lib/process/services/process-compiler.js +18 -9
  42. package/lib/process/services/process-conditional-actors.js +90 -0
  43. package/lib/process/services/process-field-permissions.js +101 -0
  44. package/lib/process/services/process-form-fields.js +94 -0
  45. package/lib/process/services/process-view-verifier.js +4 -0
  46. package/package.json +5 -5
  47. package/yida-skills/SKILL.md +14 -4
  48. package/yida-skills/skills/yida-app/SKILL.md +4 -0
  49. package/yida-skills/skills/yida-app/references/entry-navigation.md +14 -1
  50. package/yida-skills/skills/yida-app/workflow/step-3-create-or-reuse-app.md +2 -0
  51. package/yida-skills/skills/yida-app/workflow/step-4-forms-processes.md +1 -1
  52. package/yida-skills/skills/yida-app/workflow/step-7-page-code.md +1 -1
  53. package/yida-skills/skills/yida-app/workflow/step-8-publish-navigation.md +4 -2
  54. package/yida-skills/skills/yida-app/workflow/step-9-output-finish.md +7 -1
  55. package/yida-skills/skills/yida-canvas-custom-page/SKILL.md +2 -0
  56. package/yida-skills/skills/yida-create-process/SKILL.md +3 -1
  57. package/yida-skills/skills/yida-data-management/SKILL.md +24 -2
  58. package/yida-skills/skills/yida-design/SKILL.md +1 -1
  59. package/yida-skills/skills/yida-design/references/application-theme-consistency.md +6 -0
  60. package/yida-skills/skills/yida-flow-node-config/SKILL.md +52 -0
  61. package/yida-skills/skills/{yida-process-rule/references/approval-actions.md → yida-flow-node-config/references/actions.md} +14 -3
  62. package/yida-skills/skills/yida-flow-node-config/references/actors.md +239 -0
  63. package/yida-skills/skills/yida-flow-node-config/references/conditions-variables.md +171 -0
  64. package/yida-skills/skills/yida-flow-node-config/references/control.md +230 -0
  65. package/yida-skills/skills/yida-flow-node-config/references/data.md +56 -0
  66. package/yida-skills/skills/yida-flow-node-config/references/extensions.md +50 -0
  67. package/yida-skills/skills/yida-flow-node-config/references/fields.md +67 -0
  68. package/yida-skills/skills/yida-flow-node-config/references/human.md +79 -0
  69. package/yida-skills/skills/yida-flow-node-config/references/notifications.md +63 -0
  70. package/yida-skills/skills/yida-flow-node-config/references/start.md +44 -0
  71. package/yida-skills/skills/yida-form-permission/SKILL.md +78 -21
  72. package/yida-skills/skills/yida-form-permission/references/management.md +29 -0
  73. package/yida-skills/skills/yida-form-permission/references/row-field-access.md +144 -0
  74. package/yida-skills/skills/yida-integration/SKILL.md +103 -444
  75. package/yida-skills/skills/yida-integration/references/batch-data.md +76 -0
  76. package/yida-skills/skills/yida-integration/references/cli-options.md +154 -0
  77. package/yida-skills/skills/yida-integration/references/integration-node-schemas.md +4 -1
  78. package/yida-skills/skills/yida-integration/references/operations.md +134 -0
  79. package/yida-skills/skills/yida-integration/references/spec-authoring.md +195 -0
  80. package/yida-skills/skills/yida-integration/references/triggers.md +102 -0
  81. package/yida-skills/skills/yida-process-rule/SKILL.md +82 -416
  82. package/yida-skills/skills/yida-process-rule/references/official-component-nodes.md +64 -39
  83. package/yida-skills/skills-index.json +34 -13
package/README.md CHANGED
@@ -245,11 +245,14 @@ openyida configure-process APP_XXX FORM_XXX .cache/openyida/process/process.json
245
245
  openyida process preview APP_XXX PROC_INST_XXX --output .cache/openyida/process/process.html
246
246
  openyida data query form APP_XXX FORM_XXX --page 1 --size 20
247
247
  openyida data query form APP_XXX FORM_XXX --dynamic-order '{"dateField_xxx":"-"}'
248
+ openyida data query process APP_XXX FORM_XXX --current-approver USER_ID --count
248
249
  openyida data create form APP_XXX FORM_XXX --expect-form-name 客户 --expect-form-type receipt --data-file .cache/openyida/data-import/record.json
249
250
  openyida get-permission APP_XXX FORM_XXX
250
251
  ```
251
252
 
252
- Append and forward permissions are configured in the process definition through `nodes[].actions.normalActions/appendActions`. Both `create-process` and `configure-process` compile these settings into matching designer and runtime properties. See [approval action configuration](yida-skills/skills/yida-process-rule/references/approval-actions.md) for the JSON example, defaults, and verification limits.
253
+ `--current-approver` takes an exact userId, not a display name. It scans running instances and their current TODO records, counts each matching instance once, and respects the current login's server-authorized data scope. Omit `--count` to include matching instance data. An incomplete scan or permission failure is reported as an error; the result does not claim application-wide visibility.
254
+
255
+ Append and forward permissions are configured in the process definition through `nodes[].actions.normalActions/appendActions`. Both `create-process` and `configure-process` compile these settings into matching designer and runtime properties. See [approval action configuration](yida-skills/skills/yida-flow-node-config/references/actions.md) for the JSON example, defaults, and verification limits.
253
256
 
254
257
  `configure-process` 的流程 JSON 中,审批人可配置为发起人、指定成员、指定角色、部门主管或直属主管,例如:
255
258
 
@@ -387,6 +390,8 @@ A zero-dependency local dashboard (`npm run eval:dashboard`, then open `http://1
387
390
 
388
391
  ### Connectors, Integrations, and Reports
389
392
 
393
+ Automation spec `sendMessage` nodes accept `toRoles: [{"roleCode":"REAL_ROLE_ID","roleName":"Finance"}]` for NORMAL messages, alongside users and member fields. Process approval, operator, and CC nodes can also select roles. Role selection differs from conditional actor selection, which does not yet have a complete CLI shorthand. See the `yida-flow-node-config` skill for shared node guidance.
394
+
390
395
  ```bash
391
396
  openyida connector smart-create --curl "curl https://api.example.com/users"
392
397
  openyida connector list
@@ -476,7 +481,7 @@ Run `openyida --help` or `openyida <command> --help` for detailed usage.
476
481
  | `openyida list-forms <appType> [--keyword <text>]` | List forms/pages in an app |
477
482
  | `openyida aggregate-table <list\|create-empty\|inspect\|preview\|save\|publish\|status> <appType> ...` | Manage aggregate tables (virtualView) |
478
483
  | `openyida get-schema <appType> <formUuid\|--all> [--summary-json\|--field-map-json\|--analysis-json]` | Get one form Schema or all form Schemas |
479
- | `openyida check-prd-completeness <prd.md> --app-type <appType> [--build-manifest <file>] [--json]` | Check PRD page/resource count risk |
484
+ | `openyida check-prd-completeness <prd.md> --app-type <appType> [--build-manifest <file>] [--json]` | Check resources, frontend menu hiding and application theme delivery |
480
485
  | `openyida check-design <design.md> [--prd <prd.md>] [--base-dir <dir>] [--json]` | Validate design documents, theme tokens and PRD handoff |
481
486
  | `openyida er <appType> [--format mermaid\|json] [--output file] [--include-system] [--include-pages]` | Export app entity relationship diagram |
482
487
  | `openyida create-page <appType> "<name>" [--mode dashboard] [--hide-nav] [--locale zh_CN\|en_US\|ja_JP] [--open\|--no-open]` | Create a custom display page |
@@ -496,8 +501,10 @@ Run `openyida --help` or `openyida <command> --help` for detailed usage.
496
501
  | `openyida basic-info <overview\|commodity\|grant\|capacity\|quota\|abs-path\|dataflow\|i18n\|domain>` | Query organization basic info, capacity, quotas, and domain settings |
497
502
  | `openyida read-dingtalk-doc <docUrl> [--output <file>] [--json]` | Fetch Markdown content from a DingTalk document |
498
503
  | `openyida read-dingtalk-tingji <taskUuid> [--json]` | Fetch DingTalk Tingji details by task UUID |
499
- | `openyida get-permission <appType> <formUuid> [--package-uuid <packageUuid>] [--json]` | Query form permission config |
500
- | `openyida save-permission <appType> <formUuid> --package-uuid <packageUuid> [--data-permission <json>\|--action-permission <json>\|--field-permission <json>]` | Save form permission config |
504
+ | `openyida get-permission <appType> <formUuid> [--package-uuid <packageUuid>] [--package-type <FORM_PACKAGE_VIEW\|FORM_PACKAGE_START>] [--check-matrix] [--json]` | Query form permission config |
505
+ | `openyida save-permission <appType> <formUuid> [--package-type <FORM_PACKAGE_VIEW\|FORM_PACKAGE_START>] [--package-uuid <packageUuid>\|--create --name <name>] [--data-permission <json>] [--action-permission <json>] [--field-permission <json>] [--members <userIds>\|--roles <roleIds>\|--all-members\|--matrix <json>] [--confirm-member-replace] [--conditions-file <conditions.json>] [--owner-field <fieldId>] [--field-access <fieldId:view\|edit\|hidden\|encrypt>] [--field-default <access>] [--dry-run]` | Save form permission config |
506
+ | `openyida permission-manage <copy\|rename\|delete> <appType> <formUuid> <packageUuid> [--name <name>] [--package-type <type>] [--dry-run] [--confirm]` | Copy, rename or delete a permission group |
507
+ | `openyida matrix <list\|detail\|references\|rows\|create\|update\|delete\|upsert-rows\|delete-rows> [matrixId] [--file config.json] [--page 1] [--limit 20] [--expect hash] [--dry-run] [--confirm]` | Manage permission matrix definitions, references and rows |
501
508
  | `openyida corp-manager <search-user\|list\|add\|remove\|address-book> ...` | Manage platform admins and address book permissions |
502
509
  | `openyida agent-center <list\|create\|update\|cancel\|range\|search-user> ...` | Manage process and departure delegation |
503
510
 
@@ -509,6 +516,7 @@ Run `openyida --help` or `openyida <command> --help` for detailed usage.
509
516
  | `openyida create-process <appType> <formTitle> <fieldsJsonFile> <processDefinitionFile> [--replace] \| create-process <appType> --formUuid <formUuid> <processDefinitionFile> [--replace]` | Create process form (all-in-one); Supports append/forward via JSON nodes[].actions.normalActions/appendActions |
510
517
  | `openyida ai-form-setting <get\|fields\|models\|enable\|disable\|save> <appType> ...` | Manage process form AI approval prompts |
511
518
  | `openyida process preview <appType> ...` | Preview process instance (visual flowchart) |
519
+ | `openyida process fields <appType> <formUuid> [--json]` | Check process designer field loading and subtable field paths |
512
520
 
513
521
  ### Page Config & Sharing
514
522
 
@@ -550,12 +558,17 @@ Run `openyida --help` or `openyida <command> --help` for detailed usage.
550
558
 
551
559
  | Command | Description |
552
560
  |---------|-------------|
553
- | `openyida integration create <appType> ... [--spec file.json] [--connector-system-token-app <appType>]` | Create integration automation flow |
554
- | `openyida integration update <appType> <formUuid> <processCode> --spec <desired-spec.json> [--publish]` | Probe integration update capability (currently blocked without full readback) |
561
+ | `openyida integration create <appType> <formUuid> <flowName> [--spec file.json] [--publish] [--connector-system-token-app <appType>]` | Create integration automation flow |
562
+ | `openyida integration update <appType> <formUuid> <processCode> --patch <patch.json> [--dry-run] [--expect <fingerprint>] [--publish]` | Patch automation using a verified local dual-definition baseline |
563
+ | `openyida integration copy <appType> <formUuid> <processCode> --name <name> [--dry-run]` | Copy an automation as disabled |
564
+ | `openyida integration rename <appType> <formUuid> <processCode> --name <name> [--dry-run]` | Rename an automation |
565
+ | `openyida integration delete <appType> <formUuid> <processCode> --confirm [--dry-run]` | Delete a disabled automation |
566
+ | `openyida integration monitor <appType> [--file monitor.json] [--dry-run] [--json]` | Read or save automation error monitors |
555
567
  | `openyida integration list <appType> [--flow-types 1,2,3,5,6] [--form-uuid <uuid>] [--status y\|n] [--json]` | List integration automation flows |
556
568
  | `openyida integration enable <appType> <formUuid> <processCode>` | Enable integration automation flow |
557
569
  | `openyida integration disable <appType> <formUuid> <processCode>` | Disable integration automation flow |
558
570
  | `openyida integration check <appType...>` | Check abnormal integration automation run logs |
571
+ | `openyida integration detail-log <appType> <procInstId> [--json] [--include-params]` | View integration automation run log details |
559
572
  | `openyida integration diagnose (--text <text>\|--file <path>\|--rules) [--json]` | Diagnose integration automation tickets and common pitfalls |
560
573
  | `openyida dws <command> [args]` | DingTalk CLI (contacts/calendar/todo/approval etc.) |
561
574
  | `openyida dws contact user search --keyword <text>` | DingTalk CLI (contacts/calendar/todo/approval etc.) |
@@ -634,7 +647,7 @@ Form field definitions can include `alias` or `componentAlias` to populate Yida
634
647
 
635
648
  #### Workflow, Reports, and Integrations
636
649
 
637
- `openyida integration create` supports form events (`insert`, `update`, `delete`, `comment`) and approval events (`processFinish`, `activityTask`; aliases: `approval`, `approvalNode`). Approval events require `--approval-actions agree,disagree,terminated`; `activityTask` also requires `--approval-node-ids <nodeId,...>`. Passing `--process-code` selects a full graph replacement and now requires explicit `--replace`; it is not a safe update. `integration update` currently performs capability detection only: because full platform `processJson` + `viewJson` readback is unproven, it writes a redacted local probe artifact and returns `PLATFORM_PROBE_REQUIRED` before authentication, spec reading, or remote writes. It does not edit a flow.
650
+ `openyida integration create` supports form events (`insert`, `update`, `delete`, `comment`) and approval events (`processFinish`, `activityTask`; aliases: `approval`, `approvalNode`). Approval events require `--approval-actions agree,disagree,terminated`; `activityTask` also requires `--approval-node-ids <nodeId,...>`. Passing `--process-code` selects a full graph replacement and now requires explicit `--replace`; it is not a safe update. `integration update` supports previewing and applying limited node patches to disabled automations with a trusted local baseline, including node names, descriptions and message titles/bodies. It checks the remote version, saves a backup and verifies the saved result. Without a trusted baseline or when the patch exceeds the supported fields, it returns `PLATFORM_PROBE_REQUIRED` without writing. See the [maintenance guide](yida-skills/skills/yida-integration/references/operations.md) for preview, fingerprint and publication examples.
638
651
 
639
652
  ### Connect a Local Agent
640
653
 
package/bin/yida.js CHANGED
@@ -1082,7 +1082,9 @@ async function main() {
1082
1082
  const subCommand = args[0];
1083
1083
  const subArgs = args.slice(1);
1084
1084
 
1085
- if (subCommand === 'preview') {
1085
+ if (subCommand === 'fields') {
1086
+ await require('../lib/process/process-fields').run(subArgs);
1087
+ } else if (subCommand === 'preview') {
1086
1088
  if (subArgs.length < 2) {
1087
1089
  throwCliUsage(t('cli.process_preview_usage'), t('cli.process_preview_example'));
1088
1090
  }
@@ -1220,6 +1222,14 @@ async function main() {
1220
1222
  break;
1221
1223
  }
1222
1224
 
1225
+ case 'permission-manage': {
1226
+ await require('../lib/permission/permission-manage').run(args);
1227
+ break;
1228
+ }
1229
+ case 'matrix': {
1230
+ await require('../lib/permission/matrix-manage').run(args);
1231
+ break;
1232
+ }
1223
1233
  case 'integration': {
1224
1234
  const subCommand = args[0];
1225
1235
  const subArgs = args.slice(1); // 路由层消费 subCommand,传递剩余参数
@@ -1235,6 +1245,8 @@ async function main() {
1235
1245
  } else if (subCommand === 'update') {
1236
1246
  const { run: runIntegrationUpdate } = require('../lib/integration/integration-update');
1237
1247
  await runIntegrationUpdate(subArgs);
1248
+ } else if (['monitor', 'copy', 'rename', 'delete'].includes(subCommand)) {
1249
+ await require('../lib/integration/integration-manage').run(subCommand, subArgs);
1238
1250
  } else if (subCommand === 'list') {
1239
1251
  const { runList } = require('../lib/integration/integration-list');
1240
1252
  await runList(subArgs);
@@ -1247,6 +1259,9 @@ async function main() {
1247
1259
  } else if (subCommand === 'check') {
1248
1260
  const { run: runIntegrationCheck } = require('../lib/integration/integration-check');
1249
1261
  await runIntegrationCheck(subArgs);
1262
+ } else if (subCommand === 'detail-log') {
1263
+ const { run: runIntegrationDetailLog } = require('../lib/integration/integration-detail-log');
1264
+ await runIntegrationDetailLog(subArgs);
1250
1265
  } else if (subCommand === 'diagnose' || subCommand === 'doctor') {
1251
1266
  const { run: runIntegrationDiagnose } = require('../lib/integration/integration-diagnose');
1252
1267
  await runIntegrationDiagnose(subArgs);
@@ -13,33 +13,33 @@
13
13
  "platform": "darwin",
14
14
  "arch": "arm64",
15
15
  "packageName": "@openyida/local-agent-runtime-darwin-arm64",
16
- "packageVersion": "0.1.3",
17
- "runtimeVersion": "0.1.3",
18
- "manifestSha256": "1a9f5e148491c350457a41f095de9598775a2a349d872c43877c9fad63c09e28"
16
+ "packageVersion": "0.1.4",
17
+ "runtimeVersion": "0.1.4",
18
+ "manifestSha256": "48dc2a4473422c663e3330840c4f96e6646b2ff70ed61452fef8f1e68bf4a258"
19
19
  },
20
20
  {
21
21
  "platform": "darwin",
22
22
  "arch": "x64",
23
23
  "packageName": "@openyida/local-agent-runtime-darwin-x64",
24
- "packageVersion": "0.1.3",
25
- "runtimeVersion": "0.1.3",
26
- "manifestSha256": "46918d51efd40b73a645487525f2f7bdca94124ffb3ad6cdae6e9641f28b3963"
24
+ "packageVersion": "0.1.4",
25
+ "runtimeVersion": "0.1.4",
26
+ "manifestSha256": "1c93114b7fc9f2af7de1088d801ccbd3eaf32aabd967b5bd56917e8c1ec0661b"
27
27
  },
28
28
  {
29
29
  "platform": "win32",
30
30
  "arch": "x64",
31
31
  "packageName": "@openyida/local-agent-runtime-win32-x64",
32
- "packageVersion": "0.1.3",
33
- "runtimeVersion": "0.1.3",
34
- "manifestSha256": "64d471a37c54650e25c214e08d7ca7fa8dde068decfcbe71bd8abcbb2b7e74c6"
32
+ "packageVersion": "0.1.4",
33
+ "runtimeVersion": "0.1.4",
34
+ "manifestSha256": "13a7fc58b87a7f3f2592ba5239992c415463ab8b3fbf32312944db8c611fb813"
35
35
  },
36
36
  {
37
37
  "platform": "win32",
38
38
  "arch": "arm64",
39
39
  "packageName": "@openyida/local-agent-runtime-win32-arm64",
40
- "packageVersion": "0.1.3",
41
- "runtimeVersion": "0.1.3",
42
- "manifestSha256": "7b1ff527dc54285084eac0773d273fdfb892ce5ca352a810767c64eefc8e59c0"
40
+ "packageVersion": "0.1.4",
41
+ "runtimeVersion": "0.1.4",
42
+ "manifestSha256": "49e63f81730e1ee495863a48b9e90f687e9529e700788665196c169a630ed094"
43
43
  }
44
44
  ]
45
45
  }
@@ -268,8 +268,19 @@ function extractResources(markdown) {
268
268
  }
269
269
 
270
270
  function parsePrdMarkdown(markdown) {
271
+ const navigationVisibility = [];
272
+ let applicationTheme;
273
+ for (const match of String(markdown || '').matchAll(/```json\s*\n([\s\S]*?)\n```/g)) {
274
+ try {
275
+ const handoff = JSON.parse(match[1]);
276
+ if (Array.isArray(handoff.navigationVisibility)) {navigationVisibility.push(...handoff.navigationVisibility);}
277
+ if (Object.prototype.hasOwnProperty.call(handoff, 'applicationTheme')) {applicationTheme = handoff.applicationTheme;}
278
+ } catch { /* Other fenced examples are not the structured execution handoff. */ }
279
+ }
271
280
  return {
272
281
  resources: extractResources(markdown),
282
+ navigationVisibility,
283
+ ...(applicationTheme !== undefined ? { applicationTheme } : {}),
273
284
  };
274
285
  }
275
286
 
@@ -451,6 +462,7 @@ async function evaluatePrdCompleteness(markdown, options = {}, deps = {}) {
451
462
  const parsedManifest = parseBuildManifest(options.buildManifest);
452
463
  const items = [];
453
464
  const assetExecution = auditAssetExecution(options.buildManifest?.assetExecution, options.buildManifest?.assetTasks);
465
+ let themeVerification;
454
466
 
455
467
  function addItem(id, source, status, severity, message, details = {}) {
456
468
  items.push(makeRiskItem(id, source, status, severity, message, details));
@@ -465,6 +477,7 @@ async function evaluatePrdCompleteness(markdown, options = {}, deps = {}) {
465
477
  verdict,
466
478
  mode: 'delivery_risk_radar',
467
479
  assetExecution,
480
+ ...(themeVerification ? { themeVerification } : {}),
468
481
  appType,
469
482
  prdPath: options.prdPath || '',
470
483
  buildManifestPath: options.buildManifestPath || '',
@@ -510,7 +523,7 @@ async function evaluatePrdCompleteness(markdown, options = {}, deps = {}) {
510
523
  const fetchForms = deps.fetchForms || fetchFormPageList;
511
524
  let forms = [];
512
525
  try {
513
- forms = await fetchForms(appType, authRef);
526
+ forms = await fetchForms(appType, authRef, { includeNavigation: true });
514
527
  } catch (error) {
515
528
  addItem('app_resource_list_read_failed', 'remote.resource_list', 'fail', 'blocker', '目标应用资源列表读取失败,无法执行数量完整性检查', {
516
529
  expected: { appType, readable: true },
@@ -522,6 +535,55 @@ async function evaluatePrdCompleteness(markdown, options = {}, deps = {}) {
522
535
  const actualResources = forms.map(makeActualResource);
523
536
  const manifestResources = parsedManifest.resources.filter(resource => resource.required);
524
537
 
538
+ if (parsedPrd.applicationTheme !== undefined) {
539
+ const expected = parsedPrd.applicationTheme;
540
+ const { normalizeCssColorToHex } = require('./custom-theme');
541
+ const expectedColor = normalizeCssColorToHex(expected?.themeColor);
542
+ themeVerification = { configurationReadback: false, verified: false, runtimeVerified: false };
543
+ try {
544
+ if (!expectedColor || !['light', 'dark'].includes(expected?.navTheme)) {throw new Error('INVALID_THEME_EXPECTATION');}
545
+ const { fetchCurrentApp, getThemeSettings, getNavigationSettings } = require('./update-app');
546
+ const app = await (deps.fetchApp || fetchCurrentApp)(appType, authRef);
547
+ if (!app || typeof app !== 'object' || Array.isArray(app) || !Object.keys(app).length) {throw new Error('APP_SETTINGS_UNAVAILABLE');}
548
+ const settings = getThemeSettings(app);
549
+ const rawNav = getNavigationSettings(app, ['navTheme']).navTheme;
550
+ const actual = {
551
+ colour: settings.colour,
552
+ themeColor: normalizeCssColorToHex(settings.themeColor),
553
+ navTheme: ['white', 'gray'].includes(rawNav) ? 'light' : rawNav,
554
+ customThemeEnabled: settings.customThemeStyle?.enabled,
555
+ hasCssUrl: typeof settings.customThemeStyle?.cssUrl === 'string' && !!settings.customThemeStyle.cssUrl.trim(),
556
+ };
557
+ const matched = actual.colour === 'custom' && actual.themeColor === expectedColor
558
+ && actual.navTheme === expected.navTheme && actual.customThemeEnabled === true && actual.hasCssUrl;
559
+ themeVerification = { ...themeVerification, configurationReadback: true, verified: matched, expected, actual };
560
+ if (!matched) {
561
+ addItem('application_theme_mismatch', 'remote.application_theme', 'fail', 'blocker',
562
+ t('check_prd_completeness.application_theme_mismatch'), { expected, actual });
563
+ }
564
+ } catch {
565
+ addItem('application_theme_not_checked', 'remote.application_theme', 'not_checked', 'warning',
566
+ t('check_prd_completeness.application_theme_not_checked'), { expected });
567
+ }
568
+ }
569
+
570
+ // Reuse the resource-list readback. Page renderNav and menu visibility are
571
+ // independent; a successful publication cannot prove the latter.
572
+ for (const expected of parsedPrd.navigationVisibility) {
573
+ const matches = parsedManifest.resources.filter(resource => resource.name === expected?.name && resource.kind === 'display-page');
574
+ const ids = [...new Set(matches.map(resource => resource.formUuid).filter(Boolean))];
575
+ const actual = ids.length === 1 ? forms.find(form => form.formUuid === ids[0]) : undefined;
576
+ const known = value => typeof value === 'boolean';
577
+ const valid = expected?.hidden === true && expected?.mobileHidden === true;
578
+ if (valid && actual && (actual.hidden === false || actual.mobileHidden === false)) {
579
+ addItem('frontend_navigation_visible', 'remote.navigation', 'fail', 'blocker',
580
+ t('check_prd_completeness.frontend_navigation_visible', expected.name), { expected, actual });
581
+ } else if (!valid || !actual || !known(actual.hidden) || !known(actual.mobileHidden)) {
582
+ addItem('frontend_navigation_not_checked', 'prd.navigationVisibility', 'not_checked', 'warning',
583
+ t('check_prd_completeness.frontend_navigation_not_checked', expected?.name || ''), { expected, actual: actual || null });
584
+ }
585
+ }
586
+
525
587
  if (!options.buildManifest) {
526
588
  addItem('build_manifest_missing', 'build_manifest', 'not_checked', 'info', '未提供 build-manifest.json,只能基于 PRD 资源 hints 做风险提示', {
527
589
  expected: '--build-manifest <file>',
@@ -8,7 +8,7 @@ function resolveLocalizedText(value, fallback = '') {
8
8
  return parseYidaI18n(value, fallback, { parseJson: false });
9
9
  }
10
10
 
11
- function normalizeFormNavigationNode(node) {
11
+ function normalizeFormNavigationNode(node, options = {}) {
12
12
  if (!node || node.navType === 'SYSTEM' || !node.formUuid) {
13
13
  return null;
14
14
  }
@@ -21,10 +21,14 @@ function normalizeFormNavigationNode(node) {
21
21
  ),
22
22
  formType: node.formType || '',
23
23
  pathName: node.pathName || '',
24
+ ...(options.includeNavigation ? {
25
+ hidden: ['y', true].includes(node.hidden) ? true : ['n', false].includes(node.hidden) ? false : null,
26
+ mobileHidden: ['y', true].includes(node.mobileHidden) ? true : ['n', false].includes(node.mobileHidden) ? false : null,
27
+ } : {}),
24
28
  };
25
29
  }
26
30
 
27
- async function fetchFormPageList(appType, authRef) {
31
+ async function fetchFormPageList(appType, authRef, options = {}) {
28
32
  const result = await createYidaClient({ authRef }).get(
29
33
  `/dingtalk/web/${appType}/query/formnav/getFormNavigationListByOrder.json`,
30
34
  { _api: 'Nav.queryList', _mock: false }
@@ -39,7 +43,7 @@ async function fetchFormPageList(appType, authRef) {
39
43
 
40
44
  const items = Array.isArray(result.content) ? result.content : [];
41
45
  return items
42
- .map(normalizeFormNavigationNode)
46
+ .map(node => normalizeFormNavigationNode(node, options))
43
47
  .filter(Boolean);
44
48
  }
45
49
 
@@ -819,6 +819,9 @@ async function run(args) {
819
819
  }
820
820
 
821
821
  module.exports = {
822
+ fetchCurrentApp,
823
+ getThemeSettings,
824
+ getNavigationSettings,
822
825
  run,
823
826
  parseArgs,
824
827
  hasShellUpdate,
@@ -369,6 +369,12 @@ function findCommandSuggestion(argv = []) {
369
369
  }
370
370
 
371
371
  const COMMAND_SIDE_EFFECTS = new Map([
372
+ ['integration.copy', sideEffect('remote_write')],
373
+ ['integration.rename', sideEffect('remote_write')],
374
+ ['integration.delete', sideEffect('remote_write')],
375
+ ['integration.monitor', sideEffect('mixed', { read_actions: ['default', '--dry-run'], mutating_actions: ['--file'] })],
376
+ ['permission-manage', sideEffect('mixed', { read_actions: ['--dry-run'], mutating_actions: ['copy', 'rename', 'delete'] })],
377
+ ['matrix', sideEffect('mixed', { read_actions: ['list', 'detail', 'references', 'rows', '--dry-run'], mutating_actions: ['create', 'update', 'delete', 'upsert-rows', 'delete-rows'] })],
372
378
  ['data-manage-style.guide', sideEffect('local_read')],
373
379
  ['data-manage-style.list', sideEffect('local_read')],
374
380
  ['data-manage-style.generate', sideEffect('local_write')],
@@ -424,9 +430,11 @@ const COMMAND_SIDE_EFFECTS = new Map([
424
430
  'get-permission',
425
431
  'get-schema',
426
432
  'integration.check',
433
+ 'integration.detail-log',
427
434
  'integration.list',
428
435
  'list-forms',
429
436
  'process.preview',
437
+ 'process.fields',
430
438
  'read-dingtalk-tingji',
431
439
  'report.inspect',
432
440
  'verify-short-url',
@@ -542,10 +550,8 @@ const COMMAND_SIDE_EFFECTS = new Map([
542
550
  read_actions: [],
543
551
  mutating_actions: ['depends on commands in batch'],
544
552
  })],
545
- ['integration.update', sideEffect('local_write', {
546
- mutates_yida: false,
547
- mutates_local: true,
548
- })],
553
+ ['integration.update', sideEffect('mixed', { mutates_yida: true, mutates_local: true,
554
+ read_actions: ['--dry-run'], mutating_actions: ['apply patch'] })],
549
555
  ['basic-info', sideEffect('mixed', {
550
556
  mutates_yida: true,
551
557
  mutates_local: false,
@@ -672,10 +678,12 @@ const COMMAND_PERMISSIONS = new Map([
672
678
  'get-permission',
673
679
  'get-schema',
674
680
  'integration.check',
681
+ 'integration.detail-log',
675
682
  'integration.diagnose',
676
683
  'integration.list',
677
684
  'list-forms',
678
685
  'process.preview',
686
+ 'process.fields',
679
687
  'read-dingtalk-tingji',
680
688
  'report.inspect',
681
689
  'verify-short-url',
@@ -1493,7 +1501,7 @@ const COMMAND_GROUPS = [
1493
1501
  }),
1494
1502
  command('check-prd-completeness', ['check-prd-completeness'], 'check-prd-completeness <prd.md> --app-type <appType> [--build-manifest <file>] [--json]', 'help.cmd_check_prd_completeness', { managedRun: { scope: 'app', appParser: 'prd-completeness' },
1495
1503
  output: 'json',
1496
- notes: ['Checks resource counts and optional build-manifest.assetExecution recorded intervals. Include materialize.assetTasks and taskState records plus businessWork startedAt/endedAt; missing dispatch proof, missing overlap or assets before business in synchronous fallback produces needs_review. This is record consistency, not independent verification of host events.'],
1504
+ notes: ['Checks resource counts and optional build-manifest.assetExecution recorded intervals. Include materialize.assetTasks and taskState records plus businessWork startedAt/endedAt; missing dispatch proof, missing overlap or assets before business in synchronous fallback produces needs_review. This is record consistency, not independent verification of host events.', 'When the PRD execution handoff contains navigationVisibility, resolves frontend display pages through build-manifest resource IDs and checks PC/mobile menu hiding using the same remote resource-list read. A visible menu fails; unknown visibility or missing ID binding needs review. Checks PRD applicationTheme against live application settings: selected brand color, custom CSS enabled with a resource URL, and navigation tone. Mismatch fails; unreadable settings or invalid expectations need review. Reuses update-app readback field handling. Does not verify page renderNav, actual CSS content/loading, default routes or browser appearance.'],
1497
1505
  }),
1498
1506
  command('check-design', ['check-design'], 'check-design <design.md> [--prd <prd.md>] [--base-dir <dir>] [--json]', 'help.cmd_check_design', { managedRun: { scope: 'local' },
1499
1507
  requiresLogin: false,
@@ -1625,13 +1633,14 @@ const COMMAND_GROUPS = [
1625
1633
  } },
1626
1634
  } },
1627
1635
  output: 'json',
1628
- noteKeys: ['help.data_notes'],
1636
+ noteKeys: ['help.data_notes', 'query_data.approver_query_help'],
1629
1637
  notes: [
1630
1638
  'Each condition uses key (field ID), componentName, type, operator and value. Query types: TextField/TextareaField/TableField TEXT; NumberField/RateField/DateField/CascadeDateField DOUBLE; RadioField/SelectField/CheckboxField/MultiSelectField/DepartmentSelectField ARRAY; EmployeeField/CascadeSelectField/CitySelectField STRING; ImageField/AttachmentField TEXT.',
1631
1639
  'Ordinary form searchFormDatas accepts AND condition arrays via --search-json/--search-file. DateField: type DOUBLE, is/is_not, value ""; zero is filled. Query range endpoints as DateField with .start/.end: both empty means unfilled, both filled means complete.',
1632
1640
  'Use synchronized NumberField counts (eq 0/gt 0) for any filled endpoint, multi-choice options or subform rows; maintain every write entry and backfill history. --resolve-aliases supports rangeAlias.start/.end and parentId aliases. Use server pagination and dynamicOrder. Guide: yida-skills/references/query-condition-guide.md; sample: yida-data-management empty-query.',
1633
1641
  ],
1634
1642
  examples: [
1643
+ 'openyida data query process APP_XXX FORM_XXX --current-approver USER_ID --count',
1635
1644
  'openyida data query form APP_XXX FORM_XXX --page 1 --size 20 --search-json \'[{"key":"dateField_due","componentName":"DateField","type":"DOUBLE","operator":"is","value":""}]\'',
1636
1645
  'openyida data create form APP_XXX FORM_XXX --expect-form-name 客户 --expect-form-type receipt --data-json \'{"textField_name":"客户 A","dateField_followUp":1787932800000}\'',
1637
1646
  'openyida data delete form APP_XXX FORM_XXX --inst-id FINST_XXX --expect-form-name 客户 --expect-form-type receipt --confirm --json',
@@ -1647,13 +1656,16 @@ const COMMAND_GROUPS = [
1647
1656
  command('read-dingtalk-tingji', ['read-dingtalk-tingji'], 'read-dingtalk-tingji <taskUuid> [--json]', 'help.cmd_read_dingtalk_tingji', { managedRun: { scope: 'selected_input', inputType: 'tingji', inputField: 'taskUuid' },
1648
1657
  output: 'json',
1649
1658
  }),
1650
- command('get-permission', ['get-permission'], 'get-permission <appType> <formUuid> [--package-uuid <packageUuid>] [--json]', 'help.cmd_get_permission', { managedRun: { scope: 'app', appPosition: 0 } }),
1651
- command('save-permission', ['save-permission'], 'save-permission <appType> <formUuid> --package-uuid <packageUuid> [--data-permission <json>|--action-permission <json>|--field-permission <json>]', 'help.cmd_save_permission', { managedRun: { scope: 'app', appPosition: 0, mutation: { operation: 'update_form_permission', formUuidPosition: 1 } },
1659
+ command('get-permission', ['get-permission'], 'get-permission <appType> <formUuid> [--package-uuid <packageUuid>] [--package-type <FORM_PACKAGE_VIEW|FORM_PACKAGE_START>] [--check-matrix] [--json]', 'help.cmd_get_permission', { managedRun: { scope: 'app', appPosition: 0 }, noteKeys: ['permission_scope.matrix_notice'] }),
1660
+ command('save-permission', ['save-permission'], 'save-permission <appType> <formUuid> [--package-type <FORM_PACKAGE_VIEW|FORM_PACKAGE_START>] [--package-uuid <packageUuid>|--create --name <name>] [--data-permission <json>] [--action-permission <json>] [--field-permission <json>] [--members <userIds>|--roles <roleIds>|--all-members|--matrix <json>] [--confirm-member-replace] [--conditions-file <conditions.json>] [--owner-field <fieldId>] [--field-access <fieldId:view|edit|hidden|encrypt>] [--field-default <access>] [--dry-run]', 'help.cmd_save_permission', { managedRun: { scope: 'app', appPosition: 0, mutation: { operation: 'update_form_permission', formUuidPosition: 1, forbiddenFlags: ['--dry-run'] } },
1661
+ noteKeys: ['permission_scope.options_help', 'permission_scope.overlap_notice'],
1652
1662
  examples: [
1653
1663
  'openyida get-permission APP_XXX FORM_XXX --json',
1654
1664
  'openyida save-permission APP_XXX FORM_XXX --package-uuid PACKAGE_XXX --data-permission \'{"dataRange":"ORIGINATOR"}\'',
1655
1665
  ],
1656
1666
  }),
1667
+ command('permission-manage', ['permission-manage'], 'permission-manage <copy|rename|delete> <appType> <formUuid> <packageUuid> [--name <name>] [--package-type <type>] [--dry-run] [--confirm]', 'permission_manage.description', { managedRun: { scope: 'denied', reason: 'scope_not_audited' }, permission: actionDependentPermission({ preauthorized_actions: ['copy', 'rename', '--dry-run'], ask_actions: ['delete'] }), output: 'json' }),
1668
+ command('matrix', ['matrix'], 'matrix <list|detail|references|rows|create|update|delete|upsert-rows|delete-rows> [matrixId] [--file config.json] [--page 1] [--limit 20] [--expect hash] [--dry-run] [--confirm]', 'matrix_manage.description', { managedRun: { scope: 'denied', reason: 'scope_not_audited' }, permission: actionDependentPermission({ preauthorized_actions: ['list', 'detail', 'references', 'rows', 'create', 'update', 'upsert-rows', '--dry-run'], ask_actions: ['delete', 'delete-rows'] }), output: 'json' }),
1657
1669
  command('corp-manager', ['corp-manager'], 'corp-manager <search-user|list|add|remove|address-book> ...', 'help.cmd_corp_manager', { managedRun: { scope: 'denied', reason: 'scope_not_audited' }, output: 'json' }),
1658
1670
  command('agent-center', ['agent-center'], 'agent-center <list|create|update|cancel|range|search-user> ...', 'help.cmd_agent_center', { managedRun: { scope: 'denied', reason: 'scope_not_audited' }, output: 'json' }),
1659
1671
  ],
@@ -1689,6 +1701,11 @@ const COMMAND_GROUPS = [
1689
1701
  aliases: ['ai-approve', 'aiFormSetting'],
1690
1702
  }),
1691
1703
  command('process.preview', ['process', 'preview'], 'process preview <appType> ...', 'help.cmd_process_preview', { managedRun: { scope: 'app', appPosition: 1 } }),
1704
+ command('process.fields', ['process', 'fields'], 'process fields <appType> <formUuid> [--json]', 'help.cmd_process_fields', {
1705
+ managedRun: { scope: 'app', appPosition: 1 },
1706
+ permission: permission('allow', 'read', { reason: 'Read-only process designer field diagnostics.' }),
1707
+ output: 'json',
1708
+ }),
1692
1709
  ],
1693
1710
  },
1694
1711
  {
@@ -1736,11 +1753,19 @@ const COMMAND_GROUPS = [
1736
1753
  id: 'integration',
1737
1754
  titleKey: 'help.group_integration',
1738
1755
  commands: [
1739
- command('integration.create', ['integration', 'create'], 'integration create <appType> ... [--spec file.json] [--connector-system-token-app <appType>]', 'help.cmd_integration', { managedRun: { scope: 'app', appPosition: 1, mutation: { operation: 'create_process' } } }),
1740
- command('integration.update', ['integration', 'update'], 'integration update <appType> <formUuid> <processCode> --spec <desired-spec.json> [--publish]', 'help.cmd_integration_update', { managedRun: { scope: 'app', appPosition: 1 },
1741
- requiresLogin: false,
1756
+ command('integration.create', ['integration', 'create'], 'integration create <appType> <formUuid> <flowName> [--spec file.json] [--publish] [--connector-system-token-app <appType>]', 'help.cmd_integration', { managedRun: { scope: 'app', appPosition: 1, mutation: { operation: 'create_process' } },
1757
+ examples: [
1758
+ 'openyida integration create APP_XXX FORM_XXX "批量复制明细" --spec .cache/openyida/task/integration/batch-data.json',
1759
+ 'openyida integration create APP_XXX FORM_XXX "库存不足通知" --receivers USER_ID --trigger-condition "numberField_stock:当前库存:LessThan:numberField_minStock:NumberField:processVar"',
1760
+ ],
1761
+ }),
1762
+ command('integration.update', ['integration', 'update'], 'integration update <appType> <formUuid> <processCode> --patch <patch.json> [--dry-run] [--expect <fingerprint>] [--publish]', 'help.cmd_integration_update', { permission: actionDependentPermission({ preauthorized_actions: ['--dry-run', 'apply patch'] }), managedRun: { scope: 'app', appPosition: 1, mutation: { operation: 'update_process', formUuidPosition: 2, forbiddenFlags: ['--dry-run'] } },
1742
1763
  output: 'json',
1743
1764
  }),
1765
+ command('integration.copy', ['integration', 'copy'], 'integration copy <appType> <formUuid> <processCode> --name <name> [--dry-run]', 'integration_manage.desc_copy', { managedRun: { scope: 'app', appPosition: 1, mutation: { operation: 'update_process', formUuidPosition: 2, forbiddenFlags: ['--dry-run'] } }, permission: permission('allow', 'write'), output: 'json' }),
1766
+ command('integration.rename', ['integration', 'rename'], 'integration rename <appType> <formUuid> <processCode> --name <name> [--dry-run]', 'integration_manage.desc_rename', { managedRun: { scope: 'app', appPosition: 1, mutation: { operation: 'update_process', formUuidPosition: 2, forbiddenFlags: ['--dry-run'] } }, permission: permission('allow', 'write'), output: 'json' }),
1767
+ command('integration.delete', ['integration', 'delete'], 'integration delete <appType> <formUuid> <processCode> --confirm [--dry-run]', 'integration_manage.desc_delete', { managedRun: { scope: 'denied', reason: 'scope_not_audited' }, permission: permission('ask', 'write'), output: 'json' }),
1768
+ command('integration.monitor', ['integration', 'monitor'], 'integration monitor <appType> [--file monitor.json] [--dry-run] [--json]', 'integration_manage.desc_monitor', { managedRun: { scope: 'denied', reason: 'scope_not_audited' }, permission: actionDependentPermission({ preauthorized_actions: ['default', '--dry-run', '--file'] }), output: 'json' }),
1744
1769
  command('integration.list', ['integration', 'list'], 'integration list <appType> [--flow-types 1,2,3,5,6] [--form-uuid <uuid>] [--status y|n] [--json]', 'help.cmd_integration_list', { managedRun: { scope: 'app', appPosition: 1 },
1745
1770
  output: 'json',
1746
1771
  }),
@@ -1751,6 +1776,9 @@ const COMMAND_GROUPS = [
1751
1776
  output: 'json',
1752
1777
  }),
1753
1778
  command('integration.check', ['integration', 'check'], 'integration check <appType...>', 'help.cmd_integration_check', { managedRun: { scope: 'app', appPosition: 1 } }),
1779
+ command('integration.detail-log', ['integration', 'detail-log'], 'integration detail-log <appType> <procInstId> [--json] [--include-params]', 'help.cmd_integration_detail_log', { managedRun: { scope: 'app', appPosition: 1 },
1780
+ output: 'text|json',
1781
+ }),
1754
1782
  command('integration.diagnose', ['integration', 'diagnose'], 'integration diagnose (--text <text>|--file <path>|--rules) [--json]', 'help.cmd_integration_diagnose', { managedRun: { scope: 'local' },
1755
1783
  requiresLogin: false,
1756
1784
  output: 'text|json',
@@ -2084,7 +2112,9 @@ function summarizeLocalizedCommands(commands) {
2084
2112
  derived_summary: 'execution.entryModeSummary is generated for PRD and preview from the selected mode; do not author it in build-plan.json.',
2085
2113
  roles: ['service', 'management', 'workspace'],
2086
2114
  entry_required_fields: ['key', 'name', 'role', 'menu', 'defaultMenuKey'],
2087
- service_page_binding: 'sceneKey of the frontend page; standalone by default, preserve explicit platform-shell',
2115
+ service_page_binding: 'sceneKey of the frontend page; standalone by default, preserve explicit platform-shell. Missing page navigation for a multi-task service entry derives a custom top menu; single-task entries and explicit none retain their choice.',
2116
+ application_theme_handoff: 'Derived applicationTheme in the PRD execution handoff records final design themeColor/navTheme/designFile for full builds. Fast records the same contract. check-prd-completeness reads current application settings; publish does not install application themes. Resource-only changes add no theme task. Configuration matching is separate from browser acceptance and custom-page token consumption.',
2117
+ menu_visibility_handoff: 'Derived navigationVisibility in the PRD execution handoff, not a build-plan input: service-management hides frontend-only standalone display pages in PC/mobile platform menus. Shared forms and explicitly management-linked pages are preserved. Resolve names to build-manifest resource IDs; nav-group hide and readback are separate from page renderNav=false. Resource-only changes schedule no menu hiding.',
2088
2118
  page_navigation_policy: getPageNavigationContract(),
2089
2119
  local_menu_binding: 'Every entry containing local menus, including management/workspace, requires sceneKey matching exactly one page; each local menu requires resource equal to that page name and a nonempty viewKey.',
2090
2120
  menu_target_types: ['local', 'submission', 'page', 'custom'],