openyida 2026.8.27-beta.0 → 2026.8.28

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 (86) hide show
  1. package/README.md +54 -31
  2. package/bin/yida.js +26 -80
  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/services/form-mode-service.js +8 -7
  8. package/lib/asset/ai-image.js +1 -1
  9. package/lib/auth/oauth-loopback.js +128 -11
  10. package/lib/connector/action-generator.js +1 -1
  11. package/lib/connector/api.js +26 -18
  12. package/lib/connector/connector-action-update.js +404 -0
  13. package/lib/connector/connector-add-action.js +10 -35
  14. package/lib/connector/connector-create-connection.js +16 -10
  15. package/lib/connector/connector-create.js +24 -15
  16. package/lib/connector/connector-delete-action.js +2 -1
  17. package/lib/connector/connector-detail.js +13 -1
  18. package/lib/connector/connector-list-actions.js +13 -1
  19. package/lib/connector/connector-list-connections.js +15 -2
  20. package/lib/connector/connector-smart-create.js +8 -4
  21. package/lib/connector/connector-test.js +193 -168
  22. package/lib/connector/connector-update-action.js +115 -0
  23. package/lib/connector/contract.js +79 -4
  24. package/lib/connector/doc-parser.js +8 -4
  25. package/lib/connector/operation-normalizer.js +13 -1
  26. package/lib/core/browser-handoff.js +1 -1
  27. package/lib/core/check-update.js +66 -21
  28. package/lib/core/command-manifest.js +9 -21
  29. package/lib/core/copy.js +37 -76
  30. package/lib/core/doctor.js +0 -1
  31. package/lib/core/env-cmd.js +0 -10
  32. package/lib/core/env.js +2 -10
  33. package/lib/core/locales/en.js +216 -22
  34. package/lib/core/locales/zh.js +216 -22
  35. package/lib/core/update.js +324 -15
  36. package/lib/core/utils.js +58 -115
  37. package/lib/integration/connector-presets.js +4 -34
  38. package/lib/integration/integration-api.js +32 -0
  39. package/lib/integration/integration-check.js +42 -5
  40. package/lib/integration/integration-connector-schema.js +135 -0
  41. package/lib/integration/integration-create.js +231 -32
  42. package/lib/integration/integration-list.js +44 -22
  43. package/lib/integration/integration-readback.js +212 -0
  44. package/lib/integration/integration-spec-builder.js +208 -20
  45. package/lib/integration/integration-view-builder.js +18 -14
  46. package/lib/page-config/save-share-config.js +180 -35
  47. package/lib/permission/get-permission.js +64 -51
  48. package/lib/permission/permit-package-service.js +95 -0
  49. package/lib/permission/save-permission.js +268 -47
  50. package/lib/process/configure-process.js +526 -237
  51. package/lib/process/create-process.js +97 -105
  52. package/lib/process/process-diagnostics.js +8 -0
  53. package/lib/process/services/process-compiler.js +33 -2
  54. package/lib/process/services/process-view-verifier.js +140 -0
  55. package/lib/report/append.js +130 -146
  56. package/lib/report/capability-registry.js +81 -0
  57. package/lib/report/chart-builder.js +100 -168
  58. package/lib/report/constants.js +10 -11
  59. package/lib/report/contract.js +137 -24
  60. package/lib/report/data-model.js +51 -49
  61. package/lib/report/field-utils.js +50 -14
  62. package/lib/report/i18n-messages.js +160 -0
  63. package/lib/report/index.js +378 -251
  64. package/lib/report/inspect.js +125 -0
  65. package/lib/report/layout.js +113 -0
  66. package/lib/report/validation.js +204 -0
  67. package/package.json +3 -3
  68. package/scripts/postinstall.js +2 -17
  69. package/yida-skills/SKILL.md +2 -1
  70. package/yida-skills/references/routing-supplement.md +0 -1
  71. package/yida-skills/references/task-retrospective.md +1 -1
  72. package/yida-skills/skills/yida-aggregate-table/SKILL.md +75 -0
  73. package/yida-skills/skills/yida-connector/SKILL.md +20 -6
  74. package/yida-skills/skills/yida-connector-safe-actions/SKILL.md +32 -5
  75. package/yida-skills/skills/yida-create-process/SKILL.md +13 -3
  76. package/yida-skills/skills/yida-form-detail/SKILL.md +0 -1
  77. package/yida-skills/skills/yida-form-permission/SKILL.md +13 -10
  78. package/yida-skills/skills/yida-integration/SKILL.md +40 -8
  79. package/yida-skills/skills/yida-integration/references/integration-node-schemas.md +35 -2
  80. package/yida-skills/skills/yida-page-config/SKILL.md +5 -5
  81. package/yida-skills/skills/yida-process-rule/SKILL.md +28 -2
  82. package/yida-skills/skills/yida-report/SKILL.md +13 -10
  83. package/yida-skills/skills/yida-report/references/examples.md +3 -0
  84. package/yida-skills/skills/yida-report/references/report-api-guide.md +2 -0
  85. package/yida-skills/skills/yida-report/references/schema-builder-details.md +3 -1
  86. 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
 
@@ -320,8 +313,6 @@ OPENYIDA_E2E=1 npm run eval:all -- --skill yida-dashboard --screenshot
320
313
  # whitelist. Use the Node version you start it with, so run `nvm use 20` first.
321
314
  npm run eval:dashboard # http://127.0.0.1:4500
322
315
 
323
- # You can also run eval directly via the CLI:
324
- openyida eval --mode comprehensive --skill yida-dashboard
325
316
  ```
326
317
 
327
318
  Configuration precedence is `CLI flag > env (OPENYIDA_EVAL_*) > scripts/eval/eval.config.json > defaults`.
@@ -354,8 +345,19 @@ openyida integration update APP_XXX FORM_XXX LPROC_XXX \
354
345
  --spec .cache/openyida/integration/desired-spec.json
355
346
  openyida create-report APP_XXX "Sales Dashboard" .cache/openyida/reports/charts.json
356
347
  openyida append-chart APP_XXX REPORT_XXX .cache/openyida/reports/chart.json
348
+
349
+ # Aggregate tables use two independent optimistic-concurrency axes.
350
+ openyida aggregate-table inspect APP_XXX FORM_XXX --json
351
+ openyida aggregate-table preview APP_XXX FORM_XXX .cache/openyida/aggregate/design.json --json
352
+ openyida aggregate-table save APP_XXX FORM_XXX .cache/openyida/aggregate/design.json --json --no-open
353
+ openyida aggregate-table publish APP_XXX FORM_XXX .cache/openyida/aggregate/design.json --json --no-open
354
+ openyida aggregate-table status APP_XXX FORM_XXX --json
357
355
  ```
358
356
 
357
+ 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.
358
+
359
+ 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.
360
+
359
361
  ## CLI Reference
360
362
 
361
363
  Run `openyida --help` or `openyida <command> --help` for detailed usage.
@@ -436,8 +438,8 @@ Run `openyida --help` or `openyida <command> --help` for detailed usage.
436
438
 
437
439
  | Command | Description |
438
440
  |---------|-------------|
439
- | `openyida configure-process <appType> ...` | Configure and publish process rules |
440
- | `openyida create-process <appType> ...` | Create process form (all-in-one) |
441
+ | `openyida configure-process <appType> <formUuid> <definition> [processCode] [--replace]` | Configure and publish process rules |
442
+ | `openyida create-process <appType> ... [--replace]` | Create process form (all-in-one) |
441
443
  | `openyida ai-form-setting <get\|fields\|models\|enable\|disable\|save> <appType> ...` | Manage process form AI approval prompts |
442
444
  | `openyida process preview <appType> ...` | Preview process instance (visual flowchart) |
443
445
 
@@ -456,6 +458,7 @@ Run `openyida --help` or `openyida <command> --help` for detailed usage.
456
458
  |---------|-------------|
457
459
  | `openyida create-report <appType> "<name>" ... [--open\|--no-open]` | Create a Yida report |
458
460
  | `openyida append-chart <appType> <reportId> ... [--open\|--no-open]` | Append chart to existing report |
461
+ | `openyida report inspect <appType> <reportId> --json` | Inspect report runtime bindings (read-only) |
459
462
 
460
463
  ### Connectors
461
464
 
@@ -466,12 +469,13 @@ Run `openyida --help` or `openyida <command> --help` for detailed usage.
466
469
  | `openyida connector detail <id>` | View connector details |
467
470
  | `openyida connector delete <id> [--force]` | Show manual deletion guidance (CLI does not delete) |
468
471
  | `openyida connector add-action --operations <file> --connector-id <id>` | Add an action |
472
+ | `openyida connector update-action --connector-id <id> --action <operationId> --query-json JSON --confirm` | Safely update action query defaults |
469
473
  | `openyida connector list-actions <id>` | List actions |
470
474
  | `openyida connector delete-action <id> <operation-id>` | Delete an action |
471
- | `openyida connector test --connector-id <id> --action <actionId>` | Test an action |
475
+ | `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 |
472
476
  | `openyida connector list-connections <id>` | List auth connections |
473
477
  | `openyida connector create-connection <id> <name>` | Create an auth connection |
474
- | `openyida connector smart-create --curl "..."` | Smart create connector (from cURL) |
478
+ | `openyida connector smart-create --curl "..."` | Generate a redacted action draft from cURL (no remote create) |
475
479
  | `openyida connector parse-api [options]` | Parse API information |
476
480
  | `openyida connector gen-template [output]` | Generate API document template |
477
481
 
@@ -501,7 +505,6 @@ Run `openyida --help` or `openyida <command> --help` for detailed usage.
501
505
  | `openyida copy [--force]` | Copy project working directory |
502
506
  | `openyida sample [--list]` | Output code samples/templates |
503
507
  | `openyida doctor [--fix]` | Environment diagnostics & auto-fix |
504
- | `openyida eval --mode <mode> [--skill <name>] [--runs N]` | Multi-dimensional skill evaluation (doc quality, routing accuracy, safety, etc.) |
505
508
  | `openyida db-seq-fix [--fix]` | Detect and repair PostgreSQL sequence drift |
506
509
  | `openyida formula evaluate <formula\|file> [--schema file]` | Static-check Yida formula syntax and field refs |
507
510
  | `openyida update` | Check and update to latest version |
@@ -519,6 +522,28 @@ Run `openyida --help` or `openyida <command> --help` for detailed usage.
519
522
 
520
523
  ### CLI Notes
521
524
 
525
+ #### Connector Safety and Real E2E
526
+
527
+ `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.
528
+
529
+ `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.
530
+
531
+ The opt-in connector E2E is intentionally separate from the shared full runner:
532
+
533
+ ```bash
534
+ OPENYIDA_E2E=1 \
535
+ OPENYIDA_E2E_CONNECTOR=1 \
536
+ OPENYIDA_E2E_CORP_ID='<target-corp-id>' \
537
+ OPENYIDA_E2E_CONNECTOR_ECHO_URL='https://<team-controlled-host>/<stable-echo-path>' \
538
+ OPENYIDA_E2E_CONNECTOR_FIXTURE_MARKER='<expected-response-marker>' \
539
+ OPENYIDA_E2E_CONNECTOR_FIXTURE_OWNER='<expected-owner-header-value>' \
540
+ node scripts/e2e-real/connector/runner.js
541
+ ```
542
+
543
+ 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.
544
+
545
+ 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.
546
+
522
547
  `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.
523
548
 
524
549
  #### Environment and Localization
@@ -545,22 +570,20 @@ Form field definitions can include `alias` or `componentAlias` to populate Yida
545
570
 
546
571
  ## Agent Skills
547
572
 
548
- 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`.
573
+ The `yida-skills/` directory is the source skill library used by OpenYida during development and distributed with the npm package.
549
574
 
550
575
  | Path | Purpose |
551
576
  |------|---------|
552
577
  | `yida-skills/SKILL.md` | Entry point and skill index |
553
578
  | `yida-skills/skills/` | Self-contained sub-skills for app, form, process, page, data, and integration work |
554
579
  | `yida-skills/references/` | Shared Yida API, model API, and query-condition references |
555
- | `dist/skills/openyida/` | Generated Wukong upload package root; contains one root `SKILL.md` and reference-only subskill docs |
556
- | `openyida-skills.zip` | Generated Wukong upload package; upload this file in Wukong |
557
580
 
558
581
  When OpenYida is used inside a supported AI coding environment, these skills help the agent choose the right command sequence and file conventions.
559
582
 
560
- 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/`.
561
-
562
583
  For QwenWork, use the same user-level global skills layout as QoderWork: `~/.qwenworkcn/skills/yida-skills/`. Skip QwenWork setup when `~/.qwenworkcn` is not present.
563
584
 
585
+ Qoder and Qoder IDE share the user-level `~/.qoder/skills/yida-skills/` directory. QoderWork remains a separate product and uses `~/.qoderwork/skills/yida-skills/`.
586
+
564
587
  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.
565
588
 
566
589
  ## 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) {
@@ -598,6 +526,18 @@ async function main() {
598
526
  applyQuietFlag();
599
527
  applyGlobalEnvironmentFlags();
600
528
 
529
+ const { maybeAutoUpdate } = require('../lib/core/update');
530
+ const updateResult = await maybeAutoUpdate({
531
+ currentVersion,
532
+ command,
533
+ args,
534
+ argv: process.argv,
535
+ });
536
+ if (updateResult.reexecuted) {
537
+ process.exitCode = updateResult.exitCode;
538
+ return;
539
+ }
540
+
601
541
  if (!command || command === '--help' || command === '-h') {
602
542
  handleFirstRunGuide();
603
543
  printHelp();
@@ -1059,6 +999,16 @@ async function main() {
1059
999
  break;
1060
1000
  }
1061
1001
 
1002
+ case 'report': {
1003
+ const subCommand = args[0];
1004
+ if (subCommand !== 'inspect') {
1005
+ throwCliUsage('用法: openyida report inspect <appType> <reportId> --json');
1006
+ }
1007
+ const { run } = require('../lib/report/inspect');
1008
+ await run(args.slice(1));
1009
+ break;
1010
+ }
1011
+
1062
1012
  case 'cdn-config': {
1063
1013
  const { run: runCdnConfig } = require('../lib/cdn/cdn-config-cmd');
1064
1014
  await runCdnConfig(args);
@@ -1093,6 +1043,7 @@ async function main() {
1093
1043
  'detail': '../lib/connector/connector-detail',
1094
1044
  'delete': '../lib/connector/connector-delete',
1095
1045
  'add-action': '../lib/connector/connector-add-action',
1046
+ 'update-action': '../lib/connector/connector-update-action',
1096
1047
  'list-actions': '../lib/connector/connector-list-actions',
1097
1048
  'delete-action': '../lib/connector/connector-delete-action',
1098
1049
  'test': '../lib/connector/connector-test',
@@ -1113,12 +1064,13 @@ async function main() {
1113
1064
  detail <connector-id> 查看连接器详情
1114
1065
  delete <connector-id> [--force] 删除连接器
1115
1066
  add-action --operations <file> --connector-id <id> 添加执行动作
1067
+ update-action --connector-id <id> --action <id> --query-json <JSON> --confirm 安全更新动作 Query 默认值
1116
1068
  list-actions <connector-id> 列出执行动作
1117
1069
  delete-action <connector-id> <operation-id> 删除执行动作
1118
- test --connector-id <id> --action <actionId> 测试执行动作
1070
+ test --connector-id <id> --action <actionId> [结构化 JSON 参数] 测试执行动作
1119
1071
  list-connections <connector-id> 列出鉴权账号
1120
1072
  create-connection <connector-id> <name> 创建鉴权账号
1121
- smart-create --curl "curl命令" 智能创建连接器
1073
+ smart-create --curl "curl命令" 生成脱敏连接器动作草稿(不创建远端资源)
1122
1074
  parse-api [选项] 解析接口信息
1123
1075
  gen-template [输出路径] 生成接口文档模板
1124
1076
 
@@ -1258,12 +1210,6 @@ async function main() {
1258
1210
  break;
1259
1211
  }
1260
1212
 
1261
- case 'eval': {
1262
- const { main: evalMain } = require('../scripts/eval/runner');
1263
- await evalMain(args);
1264
- break;
1265
- }
1266
-
1267
1213
  default: {
1268
1214
  throwUnknownCommand(command, args);
1269
1215
  }
@@ -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__: {