openyida 2026.8.27 → 2026.8.29

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 (94) hide show
  1. package/README.md +63 -33
  2. package/bin/yida.js +69 -74
  3. package/lib/aggregate-table/aggregate-table.js +90 -18
  4. package/lib/aggregate-table/contract.js +135 -7
  5. package/lib/app/builder-ai-source.js +0 -2
  6. package/lib/app/form-navigation.js +4 -1
  7. package/lib/app/get-schema.js +58 -19
  8. package/lib/app/schema-semantic-analysis.js +223 -0
  9. package/lib/app/services/form-mode-service.js +8 -7
  10. package/lib/asset/ai-image.js +1 -1
  11. package/lib/auth/oauth-loopback.js +128 -11
  12. package/lib/connector/action-generator.js +1 -1
  13. package/lib/connector/api.js +26 -18
  14. package/lib/connector/connector-action-update.js +404 -0
  15. package/lib/connector/connector-add-action.js +10 -35
  16. package/lib/connector/connector-create-connection.js +16 -10
  17. package/lib/connector/connector-create.js +24 -15
  18. package/lib/connector/connector-delete-action.js +2 -1
  19. package/lib/connector/connector-detail.js +13 -1
  20. package/lib/connector/connector-list-actions.js +13 -1
  21. package/lib/connector/connector-list-connections.js +15 -2
  22. package/lib/connector/connector-smart-create.js +8 -4
  23. package/lib/connector/connector-test.js +193 -168
  24. package/lib/connector/connector-update-action.js +115 -0
  25. package/lib/connector/contract.js +79 -4
  26. package/lib/connector/doc-parser.js +8 -4
  27. package/lib/connector/operation-normalizer.js +13 -1
  28. package/lib/core/agent-capabilities.js +5 -0
  29. package/lib/core/browser-handoff.js +1 -1
  30. package/lib/core/check-update.js +66 -21
  31. package/lib/core/command-manifest.js +35 -12
  32. package/lib/core/copy.js +37 -76
  33. package/lib/core/doctor.js +0 -1
  34. package/lib/core/env-cmd.js +0 -10
  35. package/lib/core/env.js +2 -10
  36. package/lib/core/locales/en.js +216 -21
  37. package/lib/core/locales/zh.js +216 -21
  38. package/lib/core/update.js +324 -15
  39. package/lib/core/utils.js +58 -115
  40. package/lib/integration/connector-presets.js +4 -34
  41. package/lib/integration/integration-api.js +32 -0
  42. package/lib/integration/integration-check.js +49 -11
  43. package/lib/integration/integration-connector-schema.js +135 -0
  44. package/lib/integration/integration-create.js +231 -32
  45. package/lib/integration/integration-list.js +50 -22
  46. package/lib/integration/integration-readback.js +212 -0
  47. package/lib/integration/integration-spec-builder.js +208 -20
  48. package/lib/integration/integration-view-builder.js +18 -14
  49. package/lib/page-config/save-share-config.js +194 -38
  50. package/lib/permission/get-permission.js +66 -51
  51. package/lib/permission/permit-package-service.js +95 -0
  52. package/lib/permission/save-permission.js +268 -47
  53. package/lib/process/configure-process.js +526 -237
  54. package/lib/process/create-process.js +97 -105
  55. package/lib/process/process-diagnostics.js +8 -0
  56. package/lib/process/services/process-compiler.js +33 -2
  57. package/lib/process/services/process-view-verifier.js +140 -0
  58. package/lib/report/append.js +130 -146
  59. package/lib/report/capability-registry.js +89 -0
  60. package/lib/report/chart-builder.js +151 -169
  61. package/lib/report/constants.js +10 -11
  62. package/lib/report/contract.js +208 -24
  63. package/lib/report/data-model.js +92 -49
  64. package/lib/report/field-utils.js +50 -14
  65. package/lib/report/i18n-messages.js +160 -0
  66. package/lib/report/index.js +378 -251
  67. package/lib/report/inspect.js +125 -0
  68. package/lib/report/layout.js +113 -0
  69. package/lib/report/validation.js +204 -0
  70. package/package.json +4 -3
  71. package/scripts/postinstall.js +2 -17
  72. package/yida-skills/SKILL.md +2 -1
  73. package/yida-skills/references/routing-supplement.md +0 -1
  74. package/yida-skills/references/task-retrospective.md +1 -1
  75. package/yida-skills/skills/yida-aggregate-table/SKILL.md +75 -0
  76. package/yida-skills/skills/yida-app/SKILL.md +2 -1
  77. package/yida-skills/skills/yida-app/workflow/step-4-forms-processes.md +3 -0
  78. package/yida-skills/skills/yida-app/workflow/step-9-output-finish.md +2 -0
  79. package/yida-skills/skills/yida-connector/SKILL.md +20 -6
  80. package/yida-skills/skills/yida-connector-safe-actions/SKILL.md +32 -5
  81. package/yida-skills/skills/yida-create-process/SKILL.md +13 -3
  82. package/yida-skills/skills/yida-form-detail/SKILL.md +0 -1
  83. package/yida-skills/skills/yida-form-permission/SKILL.md +14 -10
  84. package/yida-skills/skills/yida-get-schema/SKILL.md +12 -2
  85. package/yida-skills/skills/yida-integration/SKILL.md +43 -8
  86. package/yida-skills/skills/yida-integration/references/integration-node-schemas.md +35 -2
  87. package/yida-skills/skills/yida-nav-group/SKILL.md +2 -0
  88. package/yida-skills/skills/yida-page-config/SKILL.md +5 -5
  89. package/yida-skills/skills/yida-process-rule/SKILL.md +28 -2
  90. package/yida-skills/skills/yida-report/SKILL.md +19 -10
  91. package/yida-skills/skills/yida-report/references/examples.md +3 -0
  92. package/yida-skills/skills/yida-report/references/report-api-guide.md +2 -0
  93. package/yida-skills/skills/yida-report/references/schema-builder-details.md +3 -1
  94. package/yida-skills/skills-index.json +38 -1
package/README.md CHANGED
@@ -50,6 +50,10 @@ npm install -g openyida
50
50
 
51
51
  OpenYida requires Node.js 18 or later. The package exposes both `openyida` and `yida` commands.
52
52
 
53
+ For npm global installations, OpenYida checks the npm registry before a normal command at most once every 24 hours. When a newer version is available, it installs that exact version and then reruns the original command. This behavior is identical whether the local command is launched from a terminal, Codex, Claude Code, or Qoder. Managed cloud Agent runtimes are excluded before any cache access, registry request, or npm invocation.
54
+
55
+ Set `OPENYIDA_NO_AUTO_UPDATE=1` to disable automatic updates. `OPENYIDA_AUTO_UPDATE_SECS` can override the check interval for development and testing. The explicit `openyida update` command remains available.
56
+
53
57
  If Codex is already installed, OpenYida also imports a local Codex plugin during postinstall. Restart Codex after installation, then type `@宜搭` or `@openyida` in the composer to attach the OpenYida context.
54
58
 
55
59
  ### 2. Check Your Environment
@@ -105,21 +109,7 @@ Build an IPD workflow for chip production, including approval nodes and dashboar
105
109
  Generate a public landing page and publish it to my Yida app.
106
110
  ```
107
111
 
108
- The agent can then call OpenYida commands to create the application, generate source files, publish pages, and return the final Yida URLs. In Codex, QwenWork, QoderWork, Qoder, and Wukong environments, successful creation and publish commands also include a browser handoff so the agent can open the resulting Yida page in the in-app browser. Use `--open` to force this handoff or `--no-open` to suppress it.
109
-
110
- ## Wukong Installation
111
-
112
- Wukong uses manual skill package installation instead of npm:
113
-
114
- 1. Download the latest `.zip` skill package from [GitHub Releases](https://github.com/openyida/openyida/releases).
115
- 2. Open Wukong.
116
- 3. Go to **Skill Center** > **Upload Skill** and select the downloaded package.
117
-
118
- For Wukong terminal work, make sure its bundled Node.js path is active before running `node`, `npm`, or `npx` commands:
119
-
120
- ```bash
121
- export PATH="$HOME/.real/.bin/node/bin:$PATH"
122
- ```
112
+ The agent can then call OpenYida commands to create the application, generate source files, publish pages, and return the final Yida URLs. In Codex, QwenWork, Qoder, Qoder IDE, and QoderWork environments, successful creation and publish commands also include a browser handoff so the agent can open the resulting Yida page in the in-app browser. Use `--open` to force this handoff or `--no-open` to suppress it.
123
113
 
124
114
  ## Supported AI Coding Tools
125
115
 
@@ -128,14 +118,13 @@ export PATH="$HOME/.real/.bin/node/bin:$PATH"
128
118
  | [Codex](https://openai.com/codex/) | Full support |
129
119
  | [Claude Code](https://claude.ai/code) | Full support |
130
120
  | [MuleRun](https://mulerun.com) | Full support |
131
- | [Aone Copilot](https://copilot.code.alibaba-inc.com) | Full support |
132
121
  | [OpenCode](https://opencode.ai) | Full support |
133
122
  | [Cursor](https://cursor.com/) | Full support |
134
123
  | [Visual Studio Code](https://code.visualstudio.com/) | Full support |
135
124
  | QwenWork(千问办公) | Full support |
136
- | [QoderWork](https://qoder.com) | Full support |
137
- | [Qoder](https://qoder.com) | Full support |
138
- | [Wukong](https://dingtalk.com/wukong) | Full support |
125
+ | [Qoder](https://qoder.com/download) | Full support |
126
+ | [Qoder IDE](https://qoder.com/ide) | Full support |
127
+ | [QoderWork](https://qoder.com/download) | Full support |
139
128
 
140
129
  ## How It Works
141
130
 
@@ -232,6 +221,10 @@ openyida get-permission APP_XXX FORM_XXX
232
221
  }
233
222
  ```
234
223
 
224
+ `configure-process` 会先通过表单绑定和流程版本只读接口证明 `processCode` ownership。目标已有 PUBLISHED 流程或 SAVED 草稿时,整图替换必须在人工确认后显式传入 `--replace`;未确认或 ownership 不匹配时远程写入数为 0。draft/save/publish 均为 one-shot,认证异常返回 `NON_IDEMPOTENT_RESULT_UNKNOWN`,不得自动重试。
225
+
226
+ 发布成功还必须精确回读同一 PUBLISHED `processId/processVersion` 的 `getProcessById` 平台视图,并验证可见节点的组件、名称、顺序和审批模式。只有输出 `verificationLevel: "PLATFORM_VIEW_VERIFIED"` 才表示平台 view 已验证;`PUBLISHED_UNVERIFIED` 表示发布可能已生效但回读不完整,不能宣称 `processJson` 已验证,也不能直接重放写请求。
227
+
235
228
  When creating or updating test data with `openyida data`, Yida date fields must use 13-digit millisecond timestamps, for example `"dateField_xxx": 1719705600000`. Do not submit `YYYY-MM-DD` strings for `DateField` or `CascadeDateField` values.
236
229
  Temporary JSON, CSV, and one-off import scripts should live under `.cache/openyida/` so generated run artifacts do not clutter the repository root.
237
230
 
@@ -312,6 +305,10 @@ OPENYIDA_E2E=1 npm run eval:e2e -- --skill yida-dashboard --screenshot --auto-sc
312
305
  # above, here the agent self-orchestrates.
313
306
  OPENYIDA_E2E=1 npm run eval:generate -- --screenshot
314
307
 
308
+ # Re-evaluate an archived real CLI trace against the latest scenario contract and
309
+ # current read-only platform state. This does not run an agent or mutate resources.
310
+ npm run eval:replay -- --report <generation-report.json> --scenario <scenario.json> --app-type APP_XXX
311
+
315
312
  # Run all three in one pass (routing + tool-pipeline baseline + real generation).
316
313
  OPENYIDA_E2E=1 npm run eval:all -- --skill yida-dashboard --screenshot
317
314
 
@@ -352,8 +349,19 @@ openyida integration update APP_XXX FORM_XXX LPROC_XXX \
352
349
  --spec .cache/openyida/integration/desired-spec.json
353
350
  openyida create-report APP_XXX "Sales Dashboard" .cache/openyida/reports/charts.json
354
351
  openyida append-chart APP_XXX REPORT_XXX .cache/openyida/reports/chart.json
352
+
353
+ # Aggregate tables use two independent optimistic-concurrency axes.
354
+ openyida aggregate-table inspect APP_XXX FORM_XXX --json
355
+ openyida aggregate-table preview APP_XXX FORM_XXX .cache/openyida/aggregate/design.json --json
356
+ openyida aggregate-table save APP_XXX FORM_XXX .cache/openyida/aggregate/design.json --json --no-open
357
+ openyida aggregate-table publish APP_XXX FORM_XXX .cache/openyida/aggregate/design.json --json --no-open
358
+ openyida aggregate-table status APP_XXX FORM_XXX --json
355
359
  ```
356
360
 
361
+ Aggregate-table `save` verifies the draft `stashGmtModified` axis; `publish` verifies the live `gmtModified` axis. Both commands require the corresponding GET readback revision to advance; when a response revision exists it must equal that readback axis. Both commands also require canonical readback of `relationForms`, `relationships`, `aggregatedFields`, `auxFields`, `formulaFields`, and `validators`. The CLI intentionally does not expose aggregate-table deletion, AI authoring, a high-level design DSL, or tenant-dynamic limits until the corresponding platform contracts are proven.
362
+
363
+ The opt-in aggregate real-E2E runner additionally requires `OPENYIDA_AGGREGATE_E2E_RUN_ID` and an exact `OPENYIDA_AGGREGATE_E2E_OWNED_MARKER` beginning with `<runId>__`. Before its first write it verifies the exact list/inspect identity and name, persists a redacted manifest, registry, and baseline snapshot, then conditionally restores the baseline using the latest live revision. Missing ownership proof or concurrent revision movement produces `PLATFORM_PROBE_REQUIRED` / `restore_blocked` without a restore write.
364
+
357
365
  ## CLI Reference
358
366
 
359
367
  Run `openyida --help` or `openyida <command> --help` for detailed usage.
@@ -402,7 +410,7 @@ Run `openyida --help` or `openyida <command> --help` for detailed usage.
402
410
  | `openyida create-form add-option <appType> <formUuid> <fieldLabel> <option1> [option2] ...` | Update a form page |
403
411
  | `openyida list-forms <appType> [--keyword <text>]` | List forms/pages in an app |
404
412
  | `openyida aggregate-table <list\|create-empty\|inspect\|preview\|save\|publish\|status> <appType> ...` | Manage aggregate tables (virtualView) |
405
- | `openyida get-schema <appType> <formUuid\|--all> [--summary-json\|--field-map-json]` | Get one form Schema or all form Schemas |
413
+ | `openyida get-schema <appType> <formUuid\|--all> [--summary-json\|--field-map-json\|--analysis-json]` | Get one form Schema or all form Schemas |
406
414
  | `openyida check-prd-completeness <prd.md> --app-type <appType> [--build-manifest <file>] [--json]` | Check PRD page/resource count risk |
407
415
  | `openyida er <appType> [--format mermaid\|json] [--output file] [--include-system] [--include-pages]` | Export app entity relationship diagram |
408
416
  | `openyida create-page <appType> "<name>" [--mode dashboard] [--hide-nav] [--locale zh_CN\|en_US\|ja_JP] [--open\|--no-open]` | Create a custom display page |
@@ -420,13 +428,13 @@ Run `openyida --help` or `openyida <command> --help` for detailed usage.
420
428
 
421
429
  | Command | Description |
422
430
  |---------|-------------|
423
- | `openyida data <action> <resource> [args]` | Unified data management (form/process/task/subform) |
431
+ | `openyida data <query\|get\|create\|update\|delete> <form\|process\|subform\|tasks\|operation-records\|task> [args]` | Unified data management (form/process/task/subform) |
424
432
  | `openyida task-center <type> [options]` | Global task center (todo/processed/cc etc.) |
425
433
  | `openyida basic-info <overview\|commodity\|grant\|capacity\|quota\|abs-path\|dataflow\|i18n\|domain>` | Query organization basic info, capacity, quotas, and domain settings |
426
434
  | `openyida read-dingtalk-doc <docUrl> [--output <file>] [--json]` | Fetch Markdown content from a DingTalk document |
427
435
  | `openyida read-dingtalk-tingji <taskUuid> [--json]` | Fetch DingTalk Tingji details by task UUID |
428
- | `openyida get-permission <appType> <formUuid>` | Query form permission config |
429
- | `openyida save-permission <appType> <formUuid> ...` | Save form permission config |
436
+ | `openyida get-permission <appType> <formUuid> [--package-uuid <packageUuid>] [--json]` | Query form permission config |
437
+ | `openyida save-permission <appType> <formUuid> --package-uuid <packageUuid> [--data-permission <json>\|--action-permission <json>\|--field-permission <json>]` | Save form permission config |
430
438
  | `openyida corp-manager <search-user\|list\|add\|remove\|address-book> ...` | Manage platform admins and address book permissions |
431
439
  | `openyida agent-center <list\|create\|update\|cancel\|range\|search-user> ...` | Manage process and departure delegation |
432
440
 
@@ -434,8 +442,8 @@ Run `openyida --help` or `openyida <command> --help` for detailed usage.
434
442
 
435
443
  | Command | Description |
436
444
  |---------|-------------|
437
- | `openyida configure-process <appType> ...` | Configure and publish process rules |
438
- | `openyida create-process <appType> ...` | Create process form (all-in-one) |
445
+ | `openyida configure-process <appType> <formUuid> <definition> [processCode] [--replace]` | Configure and publish process rules |
446
+ | `openyida create-process <appType> ... [--replace]` | Create process form (all-in-one) |
439
447
  | `openyida ai-form-setting <get\|fields\|models\|enable\|disable\|save> <appType> ...` | Manage process form AI approval prompts |
440
448
  | `openyida process preview <appType> ...` | Preview process instance (visual flowchart) |
441
449
 
@@ -454,6 +462,7 @@ Run `openyida --help` or `openyida <command> --help` for detailed usage.
454
462
  |---------|-------------|
455
463
  | `openyida create-report <appType> "<name>" ... [--open\|--no-open]` | Create a Yida report |
456
464
  | `openyida append-chart <appType> <reportId> ... [--open\|--no-open]` | Append chart to existing report |
465
+ | `openyida report inspect <appType> <reportId> --json` | Inspect report runtime bindings (read-only) |
457
466
 
458
467
  ### Connectors
459
468
 
@@ -464,12 +473,13 @@ Run `openyida --help` or `openyida <command> --help` for detailed usage.
464
473
  | `openyida connector detail <id>` | View connector details |
465
474
  | `openyida connector delete <id> [--force]` | Show manual deletion guidance (CLI does not delete) |
466
475
  | `openyida connector add-action --operations <file> --connector-id <id>` | Add an action |
476
+ | `openyida connector update-action --connector-id <id> --action <operationId> --query-json JSON --confirm` | Safely update action query defaults |
467
477
  | `openyida connector list-actions <id>` | List actions |
468
478
  | `openyida connector delete-action <id> <operation-id>` | Delete an action |
469
- | `openyida connector test --connector-id <id> --action <actionId>` | Test an action |
479
+ | `openyida connector test --connector-id <id> --action <actionId> [--path-json JSON] [--query-json JSON] [--header-json JSON] [--body-json JSON] [--account-id <id>]` | Test an action |
470
480
  | `openyida connector list-connections <id>` | List auth connections |
471
481
  | `openyida connector create-connection <id> <name>` | Create an auth connection |
472
- | `openyida connector smart-create --curl "..."` | Smart create connector (from cURL) |
482
+ | `openyida connector smart-create --curl "..."` | Generate a redacted action draft from cURL (no remote create) |
473
483
  | `openyida connector parse-api [options]` | Parse API information |
474
484
  | `openyida connector gen-template [output]` | Generate API document template |
475
485
 
@@ -479,7 +489,7 @@ Run `openyida --help` or `openyida <command> --help` for detailed usage.
479
489
  |---------|-------------|
480
490
  | `openyida integration create <appType> ... [--spec file.json]` | Create integration automation flow |
481
491
  | `openyida integration update <appType> <formUuid> <processCode> --spec <desired-spec.json> [--publish]` | Probe integration update capability (currently blocked without full readback) |
482
- | `openyida integration list <appType> [--form-uuid <uuid>] [--status y\|n] [--json]` | List integration automation flows |
492
+ | `openyida integration list <appType> [--flow-types 1,2,3,5,6] [--form-uuid <uuid>] [--status y\|n] [--json]` | List integration automation flows |
483
493
  | `openyida integration enable <appType> <formUuid> <processCode>` | Enable integration automation flow |
484
494
  | `openyida integration disable <appType> <formUuid> <processCode>` | Disable integration automation flow |
485
495
  | `openyida integration check <appType...>` | Check abnormal integration automation run logs |
@@ -516,6 +526,28 @@ Run `openyida --help` or `openyida <command> --help` for detailed usage.
516
526
 
517
527
  ### CLI Notes
518
528
 
529
+ #### Connector Safety and Real E2E
530
+
531
+ `connector test` keeps the legacy flat `--params` option, but dispatches each key only to the location proven by the action schema. Unknown or ambiguous keys fail closed. Authenticated connectors require `--account-id`, and that account must belong to the selected connector. The test response follows the frontend canonical contract `{statusLine,responseHeaders,content}`; unknown envelopes and non-2xx status lines are failures.
532
+
533
+ `connector update-action --connector-id <id> --action <operationId> --query-json '{"currentPage":"1"}' --confirm` is the narrow safe path for editing existing query defaults. It requires a complete connector/action preflight, changes only declared query defaults mirrored in `inputs` and `parameters`, submits the complete action collection once, and verifies an unchanged connector fingerprint, action count, non-target actions, and stable IDs. Missing/empty/unknown parameters, duplicate IDs, incomplete readback, and unknown write outcomes fail closed without automatic retry. `add-action` no longer overwrites an existing action ID; use `update-action` for query-only edits.
534
+
535
+ The opt-in connector E2E is intentionally separate from the shared full runner:
536
+
537
+ ```bash
538
+ OPENYIDA_E2E=1 \
539
+ OPENYIDA_E2E_CONNECTOR=1 \
540
+ OPENYIDA_E2E_CORP_ID='<target-corp-id>' \
541
+ OPENYIDA_E2E_CONNECTOR_ECHO_URL='https://<team-controlled-host>/<stable-echo-path>' \
542
+ OPENYIDA_E2E_CONNECTOR_FIXTURE_MARKER='<expected-response-marker>' \
543
+ OPENYIDA_E2E_CONNECTOR_FIXTURE_OWNER='<expected-owner-header-value>' \
544
+ node scripts/e2e-real/connector/runner.js
545
+ ```
546
+
547
+ The runner rejects public generic echo services such as httpbin and example.com. Before its first remote write it selects and verifies the explicit organization profile, prints a redacted resource plan, and persists synchronized registry/manifest evidence plus the SHA-256 of the preserved operations fixture. The controlled fixture must return exact JSON fields `content.runId`, `content.fixtureMarker`, and `content.authorization === "Basic ***"`, plus the exact `x-openyida-fixture-owner` response header. Substring matches, malformed JSON, and wrong fields fail closed. Each remote create is persisted as `attempted` before execution and becomes `completed` only after exact readback; an exception is recorded as `outcome_unknown` with a non-owned residual candidate and is never retried. The runner deletes only its temporary local copy and reports remote connector/account cleanup as blocked because the CLI has no proven delete API. If organization or fixture ownership cannot be proven, it returns `PLATFORM_PROBE_REQUIRED` with zero remote writes.
548
+
549
+ The opt-in `node scripts/e2e-real/connector/action-update-runner.js` regression uses the owner-confirmed login-free `www.aliwork.com` fixture and a single owned NONE-auth connector containing a target action plus one preservation sentinel. Enable it with `OPENYIDA_E2E=1 OPENYIDA_E2E_CONNECTOR_ACTION_UPDATE=1`. It verifies isolated `currentPage`, `pageSize`, `userLanguage`, `searchFieldJson`, and dynamic `_stamp` edits, restores each field and the final baseline, and persists only response structure, data count, and SHA-256. It never stores response row values or auth/profile/corp identifiers, never retries unknown writes, and intentionally leaves the owned connector as a `cleanup_blocked` residual because no proven delete API exists.
550
+
519
551
  `openyida asset resolve --hero <path-or-url> --product <path-or-url> --require-hero --upload-assets --json` is the preferred preflight for homepage visuals. It verifies public image URLs, uploads local images when CDN is configured, mirrors verified external images to CDN when `--upload-assets` is passed, and returns `materialStatus: final|draft|none` so agents do not claim an unfinished visual page is final.
520
552
 
521
553
  #### Environment and Localization
@@ -542,22 +574,20 @@ Form field definitions can include `alias` or `componentAlias` to populate Yida
542
574
 
543
575
  ## Agent Skills
544
576
 
545
- The `yida-skills/` directory is the source skill library used by OpenYida during development. Release assets for Wukong are generated by `npm run build:skills`: the expanded package is written to `dist/skills/openyida/`, and the upload-ready zip is written to `openyida-skills.zip`.
577
+ The `yida-skills/` directory is the source skill library used by OpenYida during development and distributed with the npm package.
546
578
 
547
579
  | Path | Purpose |
548
580
  |------|---------|
549
581
  | `yida-skills/SKILL.md` | Entry point and skill index |
550
582
  | `yida-skills/skills/` | Self-contained sub-skills for app, form, process, page, data, and integration work |
551
583
  | `yida-skills/references/` | Shared Yida API, model API, and query-condition references |
552
- | `dist/skills/openyida/` | Generated Wukong upload package root; contains one root `SKILL.md` and reference-only subskill docs |
553
- | `openyida-skills.zip` | Generated Wukong upload package; upload this file in Wukong |
554
584
 
555
585
  When OpenYida is used inside a supported AI coding environment, these skills help the agent choose the right command sequence and file conventions.
556
586
 
557
- For Wukong manual import, upload the generated `openyida-skills.zip`. The package follows Wukong's custom skill rules: folder name and `frontmatter.name` are both `openyida`, root frontmatter only contains `name` and `description`, and long references live under `references/`.
558
-
559
587
  For QwenWork, use the same user-level global skills layout as QoderWork: `~/.qwenworkcn/skills/yida-skills/`. Skip QwenWork setup when `~/.qwenworkcn` is not present.
560
588
 
589
+ Qoder and Qoder IDE share the user-level `~/.qoder/skills/yida-skills/` directory. QoderWork remains a separate product and uses `~/.qoderwork/skills/yida-skills/`.
590
+
561
591
  For Codex, `npm install -g openyida` additionally creates a local plugin marketplace under `~/.openyida/codex-plugin` and enables `openyida@openyida` in `~/.codex/config.toml` when Codex is detected. This makes OpenYida show up in Codex's `@` plugin menu as **宜搭** after Codex reloads.
562
592
 
563
593
  ## Examples
package/bin/yida.js CHANGED
@@ -20,77 +20,6 @@ const command = process.argv[2];
20
20
  const args = process.argv.slice(3);
21
21
  const rawArgs = process.argv.slice(3);
22
22
 
23
- function isAgentEnvironment(env) {
24
- return !!(
25
- env.CODEX_SHELL ||
26
- env.CODEX_CI ||
27
- env.CODEX_THREAD_ID ||
28
- env.CODEX_HOME ||
29
- env.CLAUDE_CODE ||
30
- env.CLAUDE_CODE_ENTRYPOINT ||
31
- env.MULERUN_CHAT_ID ||
32
- env.MULE_DATA_DIR ||
33
- env.MULE_WORKSPACE_DIR ||
34
- env.OPENCODE ||
35
- env.OPENCODE_CLIENT ||
36
- env.QODER_IDE ||
37
- env.QODER_AGENT ||
38
- env.QODERCLI_INTEGRATION_MODE ||
39
- env.QODER_WORK_INTEGRATION_PRODUCT ||
40
- env.QWENWORK_INTEGRATION_MODE ||
41
- env.QWENWORKCN_INTEGRATION_MODE ||
42
- env.QWENWORK ||
43
- env.QWENWORK_CLIENT ||
44
- env.QWENWORK_WORKSPACE_DIR ||
45
- env.QWENWORK_SANDBOX_ID ||
46
- env.QWENWORK_PREVIEW_URL ||
47
- env.QWENWORK_VNC_URL ||
48
- (env.AGENT_PLATFORM || '').toLowerCase().includes('qwenwork') ||
49
- env.CURSOR_TRACE_ID ||
50
- env.AGENT_WORK_ROOT ||
51
- env.OPENYIDA_AGENT_MODE ||
52
- (env.__CFBundleIdentifier || '').toLowerCase().includes('codex') ||
53
- (env.__CFBundleIdentifier || '').toLowerCase().includes('qoder') ||
54
- (env.__CFBundleIdentifier || '').toLowerCase().includes('qwenwork') ||
55
- (env.__CFBundleIdentifier || '').toLowerCase().includes('qwen-work')
56
- );
57
- }
58
-
59
- function shouldRunUpdateCheck() {
60
- if (process.env.OPENYIDA_SKIP_UPDATE_CHECK || process.env.NO_UPDATE_NOTIFIER) {
61
- return false;
62
- }
63
- if (process.env.CI || isAgentEnvironment(process.env)) {
64
- return false;
65
- }
66
- if (!process.stderr.isTTY) {
67
- return false;
68
- }
69
- if (!command || command === '--help' || command === '-h' || command === '--version' || command === '-v') {
70
- return false;
71
- }
72
- if (args.includes('--help') || args.includes('-h')) {
73
- return false;
74
- }
75
- if (command === 'commands' || command === 'agent-capabilities' || command === 'mcp') {
76
- return false;
77
- }
78
- if (args.includes('--json') || args.includes('--check-only')) {
79
- return false;
80
- }
81
- return true;
82
- }
83
-
84
- function maybeCheckForUpdate() {
85
- if (!shouldRunUpdateCheck()) {
86
- return;
87
- }
88
- const { checkUpdate } = require('../lib/core/check-update');
89
- checkUpdate(currentVersion).catch(() => {});
90
- }
91
-
92
- maybeCheckForUpdate();
93
-
94
23
  function shouldUseEnvManagement(argsList) {
95
24
  const subCommand = argsList[0];
96
25
  return !!subCommand && subCommand !== '--json';
@@ -369,7 +298,6 @@ const UNSUPPORTED_LEGACY_LOGIN_FLAGS = new Set([
369
298
  '--browser',
370
299
  '--codex',
371
300
  '--qoder',
372
- '--wukong',
373
301
  ]);
374
302
 
375
303
  function findUnsupportedLegacyLoginFlag(...argLists) {
@@ -556,6 +484,45 @@ function printCommandUsage(...lines) {
556
484
  console.log(lines.filter(Boolean).join('\n'));
557
485
  }
558
486
 
487
+ const MANIFEST_HELP_PATHS = Object.freeze({
488
+ 'get-schema': ['get-schema'],
489
+ 'query-data': ['data'],
490
+ 'data-manage': ['data'],
491
+ data: ['data'],
492
+ report: ['report'],
493
+ 'create-process': ['create-process'],
494
+ 'create-report': ['create-report'],
495
+ 'save-share-config': ['save-share-config'],
496
+ 'verify-short-url': ['verify-short-url'],
497
+ 'integration-create': ['integration', 'create'],
498
+ 'save-permission': ['save-permission'],
499
+ 'get-permission': ['get-permission'],
500
+ });
501
+
502
+ function printManifestCommandHelp(commandName) {
503
+ const canonicalPath = MANIFEST_HELP_PATHS[commandName];
504
+ if (!canonicalPath) {
505
+ return false;
506
+ }
507
+
508
+ const manifest = buildCommandManifest({ t, version: currentVersion });
509
+ const matches = manifest.commands.filter((entry) =>
510
+ canonicalPath.every((token, index) => entry.path[index] === token)
511
+ );
512
+ const exact = matches.find((entry) => entry.path.length === canonicalPath.length);
513
+ const entries = exact ? [exact] : matches;
514
+ if (entries.length === 0) {
515
+ return false;
516
+ }
517
+
518
+ printCommandUsage(...entries.flatMap((entry) => [
519
+ entry.usage,
520
+ entry.description,
521
+ ...(entry.examples || []),
522
+ ]));
523
+ return true;
524
+ }
525
+
559
526
  function printLoginHelp() {
560
527
  printCommandUsage(t('cli.login_usage'), t('cli.login_example'));
561
528
  }
@@ -598,6 +565,18 @@ async function main() {
598
565
  applyQuietFlag();
599
566
  applyGlobalEnvironmentFlags();
600
567
 
568
+ const { maybeAutoUpdate } = require('../lib/core/update');
569
+ const updateResult = await maybeAutoUpdate({
570
+ currentVersion,
571
+ command,
572
+ args,
573
+ argv: process.argv,
574
+ });
575
+ if (updateResult.reexecuted) {
576
+ process.exitCode = updateResult.exitCode;
577
+ return;
578
+ }
579
+
601
580
  if (!command || command === '--help' || command === '-h') {
602
581
  handleFirstRunGuide();
603
582
  printHelp();
@@ -609,6 +588,10 @@ async function main() {
609
588
  return;
610
589
  }
611
590
 
591
+ if (hasHelpFlag(args) && printManifestCommandHelp(command)) {
592
+ return;
593
+ }
594
+
612
595
  switch (command) {
613
596
  case 'commands': {
614
597
  if (args[0] === 'validate' || args[0] === 'build') {
@@ -1059,6 +1042,16 @@ async function main() {
1059
1042
  break;
1060
1043
  }
1061
1044
 
1045
+ case 'report': {
1046
+ const subCommand = args[0];
1047
+ if (subCommand !== 'inspect') {
1048
+ throwCliUsage('用法: openyida report inspect <appType> <reportId> --json');
1049
+ }
1050
+ const { run } = require('../lib/report/inspect');
1051
+ await run(args.slice(1));
1052
+ break;
1053
+ }
1054
+
1062
1055
  case 'cdn-config': {
1063
1056
  const { run: runCdnConfig } = require('../lib/cdn/cdn-config-cmd');
1064
1057
  await runCdnConfig(args);
@@ -1093,6 +1086,7 @@ async function main() {
1093
1086
  'detail': '../lib/connector/connector-detail',
1094
1087
  'delete': '../lib/connector/connector-delete',
1095
1088
  'add-action': '../lib/connector/connector-add-action',
1089
+ 'update-action': '../lib/connector/connector-update-action',
1096
1090
  'list-actions': '../lib/connector/connector-list-actions',
1097
1091
  'delete-action': '../lib/connector/connector-delete-action',
1098
1092
  'test': '../lib/connector/connector-test',
@@ -1113,12 +1107,13 @@ async function main() {
1113
1107
  detail <connector-id> 查看连接器详情
1114
1108
  delete <connector-id> [--force] 删除连接器
1115
1109
  add-action --operations <file> --connector-id <id> 添加执行动作
1110
+ update-action --connector-id <id> --action <id> --query-json <JSON> --confirm 安全更新动作 Query 默认值
1116
1111
  list-actions <connector-id> 列出执行动作
1117
1112
  delete-action <connector-id> <operation-id> 删除执行动作
1118
- test --connector-id <id> --action <actionId> 测试执行动作
1113
+ test --connector-id <id> --action <actionId> [结构化 JSON 参数] 测试执行动作
1119
1114
  list-connections <connector-id> 列出鉴权账号
1120
1115
  create-connection <connector-id> <name> 创建鉴权账号
1121
- smart-create --curl "curl命令" 智能创建连接器
1116
+ smart-create --curl "curl命令" 生成脱敏连接器动作草稿(不创建远端资源)
1122
1117
  parse-api [选项] 解析接口信息
1123
1118
  gen-template [输出路径] 生成接口文档模板
1124
1119
 
@@ -3,6 +3,7 @@
3
3
  const fs = require('fs');
4
4
  const querystring = require('querystring');
5
5
 
6
+ const { CliError } = require('../core/cli-error');
6
7
  const { createAuthRef: createCoreAuthRef, createYidaClient, isAuthRefReady } = require('../core/yida-client');
7
8
  const { t } = require('../core/i18n');
8
9
  const { buildYidaTitleI18n, normalizeYidaLocale, resolveContentLocale } = require('../core/yida-i18n');
@@ -15,6 +16,10 @@ const {
15
16
  } = require('./contract');
16
17
 
17
18
  const FORM_TYPE_VIRTUAL_VIEW = 'virtualView';
19
+ const ACTION_REVISION_AXES = Object.freeze({
20
+ save: 'stashGmtModified',
21
+ publish: 'gmtModified',
22
+ });
18
23
 
19
24
  function hasHelpFlag(args) {
20
25
  return (args || []).includes('--help') || (args || []).includes('-h');
@@ -48,6 +53,7 @@ function parseFlags(args) {
48
53
  json: false,
49
54
  keyword: '',
50
55
  locale: null,
56
+ expectedRevision: null,
51
57
  };
52
58
 
53
59
  for (let index = 0; index < openOption.args.length; index++) {
@@ -68,6 +74,10 @@ function parseFlags(args) {
68
74
  flags.locale = locale;
69
75
  continue;
70
76
  }
77
+ if (arg === '--expected-revision' && openOption.args[index + 1]) {
78
+ flags.expectedRevision = openOption.args[++index];
79
+ continue;
80
+ }
71
81
  positional.push(arg);
72
82
  }
73
83
 
@@ -236,11 +246,67 @@ function normalizeDesignConfig(rawConfig, formUuid) {
236
246
 
237
247
  const normalized = { formUuid };
238
248
  for (const key of DESIGN_KEYS) {
239
- normalized[key] = Array.isArray(config[key]) ? config[key] : [];
249
+ normalized[key] = config[key] === undefined ? [] : config[key];
240
250
  }
241
251
  return normalized;
242
252
  }
243
253
 
254
+ function getActionRevisionAxis(action) {
255
+ const axis = ACTION_REVISION_AXES[action];
256
+ if (!axis) {
257
+ throw new Error(`Unknown aggregate table revision action: ${action}`);
258
+ }
259
+ return axis;
260
+ }
261
+
262
+ function selectActionRevision(config, action) {
263
+ const axis = getActionRevisionAxis(action);
264
+ return config && config[axis];
265
+ }
266
+
267
+ function isMissingRevision(value) {
268
+ return value === undefined || value === null || value === '';
269
+ }
270
+
271
+ function assertActionRevisionAdvanced(action, beforeConfig, responseContent, readbackConfig) {
272
+ const revisionAxis = getActionRevisionAxis(action);
273
+ const beforeRevision = selectActionRevision(beforeConfig, action);
274
+ const responseRevision = responseContent && responseContent.gmtModified;
275
+ const readbackRevision = selectActionRevision(readbackConfig, action);
276
+
277
+ if (isMissingRevision(readbackRevision)) {
278
+ const error = new Error('AGGREGATE_WRITE_READBACK_REVISION_MISSING');
279
+ error.code = 'AGGREGATE_WRITE_READBACK_REVISION_MISSING';
280
+ throw error;
281
+ }
282
+ if (!isMissingRevision(beforeRevision) && String(readbackRevision) === String(beforeRevision)) {
283
+ const error = new Error('AGGREGATE_WRITE_READBACK_REVISION_UNCHANGED');
284
+ error.code = 'AGGREGATE_WRITE_READBACK_REVISION_UNCHANGED';
285
+ throw error;
286
+ }
287
+ if (!isMissingRevision(responseRevision) && String(responseRevision) !== String(readbackRevision)) {
288
+ const error = new Error('AGGREGATE_WRITE_RESPONSE_READBACK_REVISION_MISMATCH');
289
+ error.code = 'AGGREGATE_WRITE_RESPONSE_READBACK_REVISION_MISMATCH';
290
+ error.details = {
291
+ revisionAxis,
292
+ responseRevision,
293
+ readbackRevision,
294
+ };
295
+ throw error;
296
+ }
297
+
298
+ const revisionSource = isMissingRevision(responseRevision) ? 'readback' : 'response';
299
+
300
+ return {
301
+ revisionAxis,
302
+ revisionSource,
303
+ verifiedRevision: readbackRevision,
304
+ beforeRevision,
305
+ responseRevision: isMissingRevision(responseRevision) ? null : responseRevision,
306
+ readbackRevision,
307
+ };
308
+ }
309
+
244
310
  function summarizeDesignConfig(config) {
245
311
  const safeConfig = config || {};
246
312
  return {
@@ -388,9 +454,7 @@ async function loadDesignForMutation(authRef, appType, formUuid, input, action)
388
454
  const designInfo = normalizeDesignConfig(rawConfig, formUuid);
389
455
  const currentResult = await getVirtualViewConfig(authRef, appType, formUuid);
390
456
  const currentConfig = assertSuccess(currentResult, t('aggregate_table.inspect'));
391
- const gmtModified = action === 'save'
392
- ? currentConfig.stashGmtModified
393
- : currentConfig.gmtModified;
457
+ const gmtModified = selectActionRevision(currentConfig, action);
394
458
  return { designInfo, currentConfig, gmtModified };
395
459
  }
396
460
 
@@ -447,6 +511,18 @@ async function runSaveOrPublish(args, action) {
447
511
 
448
512
  const authRef = await createAuthRef();
449
513
  const { designInfo, currentConfig, gmtModified } = await loadDesignForMutation(authRef, appType, formUuid, input, action);
514
+ if (flags.expectedRevision !== null && (
515
+ isMissingRevision(gmtModified) || String(gmtModified) !== String(flags.expectedRevision)
516
+ )) {
517
+ throw new CliError('AGGREGATE_WRITE_PRECONDITION_FAILED', {
518
+ code: 'AGGREGATE_WRITE_PRECONDITION_FAILED',
519
+ details: {
520
+ revisionAxis: getActionRevisionAxis(action),
521
+ expectedRevision: flags.expectedRevision,
522
+ observedRevision: isMissingRevision(gmtModified) ? null : gmtModified,
523
+ },
524
+ });
525
+ }
450
526
  assertAggregateDesignConfig(designInfo, { mode: isPublish ? 'publish' : 'draft' });
451
527
 
452
528
  const result = await postVirtualViewDesign(authRef, appType, formUuid, designInfo, action, gmtModified);
@@ -454,20 +530,7 @@ async function runSaveOrPublish(args, action) {
454
530
  const readbackResult = await getVirtualViewConfig(authRef, appType, formUuid);
455
531
  const readback = assertSuccess(readbackResult, t('aggregate_table.inspect'));
456
532
  assertAggregateDesignReadback(designInfo, readback);
457
- const responseRevision = content && content.gmtModified;
458
- const readbackRevision = readback && readback.gmtModified;
459
- if ((responseRevision === undefined || responseRevision === null) &&
460
- (readbackRevision === undefined || readbackRevision === null)) {
461
- const error = new Error('AGGREGATE_WRITE_REVISION_MISSING');
462
- error.code = 'AGGREGATE_WRITE_REVISION_MISSING';
463
- throw error;
464
- }
465
- if ((responseRevision === undefined || responseRevision === null) &&
466
- currentConfig && String(readbackRevision) === String(currentConfig.gmtModified)) {
467
- const error = new Error('AGGREGATE_WRITE_REVISION_UNCHANGED');
468
- error.code = 'AGGREGATE_WRITE_REVISION_UNCHANGED';
469
- throw error;
470
- }
533
+ const revisionProof = assertActionRevisionAdvanced(action, currentConfig, content, readback);
471
534
  const designUrl = buildDesignUrl(authRef.baseUrl, appType, formUuid);
472
535
  const workbenchUrl = buildWorkbenchUrl(authRef.baseUrl, appType, formUuid);
473
536
 
@@ -486,6 +549,12 @@ async function runSaveOrPublish(args, action) {
486
549
  aggregateTableId: formUuid,
487
550
  formUuid,
488
551
  gmtModified: content && content.gmtModified,
552
+ revisionAxis: revisionProof.revisionAxis,
553
+ revision: revisionProof.verifiedRevision,
554
+ revisionSource: revisionProof.revisionSource,
555
+ beforeRevision: revisionProof.beforeRevision,
556
+ responseRevision: revisionProof.responseRevision,
557
+ readbackRevision: revisionProof.readbackRevision,
489
558
  response: content,
490
559
  readbackVerified: true,
491
560
  designUrl,
@@ -561,6 +630,8 @@ async function run(args) {
561
630
  module.exports = {
562
631
  DESIGN_KEYS,
563
632
  FORM_TYPE_VIRTUAL_VIEW,
633
+ ACTION_REVISION_AXES,
634
+ assertActionRevisionAdvanced,
564
635
  buildCreateEmptyPostData,
565
636
  buildDesignPostData,
566
637
  filterAggregateTables,
@@ -568,6 +639,7 @@ module.exports = {
568
639
  normalizeDesignConfig,
569
640
  parseFlags,
570
641
  readJsonInput,
642
+ selectActionRevision,
571
643
  summarizeDesignConfig,
572
644
  run,
573
645
  __api__: {