openyida 2026.8.27 → 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 -28
  2. package/bin/yida.js +26 -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/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 -3
  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 -21
  34. package/lib/core/locales/zh.js +216 -21
  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
 
@@ -352,8 +345,19 @@ openyida integration update APP_XXX FORM_XXX LPROC_XXX \
352
345
  --spec .cache/openyida/integration/desired-spec.json
353
346
  openyida create-report APP_XXX "Sales Dashboard" .cache/openyida/reports/charts.json
354
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
355
355
  ```
356
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
+
357
361
  ## CLI Reference
358
362
 
359
363
  Run `openyida --help` or `openyida <command> --help` for detailed usage.
@@ -434,8 +438,8 @@ Run `openyida --help` or `openyida <command> --help` for detailed usage.
434
438
 
435
439
  | Command | Description |
436
440
  |---------|-------------|
437
- | `openyida configure-process <appType> ...` | Configure and publish process rules |
438
- | `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) |
439
443
  | `openyida ai-form-setting <get\|fields\|models\|enable\|disable\|save> <appType> ...` | Manage process form AI approval prompts |
440
444
  | `openyida process preview <appType> ...` | Preview process instance (visual flowchart) |
441
445
 
@@ -454,6 +458,7 @@ Run `openyida --help` or `openyida <command> --help` for detailed usage.
454
458
  |---------|-------------|
455
459
  | `openyida create-report <appType> "<name>" ... [--open\|--no-open]` | Create a Yida report |
456
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) |
457
462
 
458
463
  ### Connectors
459
464
 
@@ -464,12 +469,13 @@ Run `openyida --help` or `openyida <command> --help` for detailed usage.
464
469
  | `openyida connector detail <id>` | View connector details |
465
470
  | `openyida connector delete <id> [--force]` | Show manual deletion guidance (CLI does not delete) |
466
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 |
467
473
  | `openyida connector list-actions <id>` | List actions |
468
474
  | `openyida connector delete-action <id> <operation-id>` | Delete an action |
469
- | `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 |
470
476
  | `openyida connector list-connections <id>` | List auth connections |
471
477
  | `openyida connector create-connection <id> <name>` | Create an auth connection |
472
- | `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) |
473
479
  | `openyida connector parse-api [options]` | Parse API information |
474
480
  | `openyida connector gen-template [output]` | Generate API document template |
475
481
 
@@ -516,6 +522,28 @@ Run `openyida --help` or `openyida <command> --help` for detailed usage.
516
522
 
517
523
  ### CLI Notes
518
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
+
519
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.
520
548
 
521
549
  #### Environment and Localization
@@ -542,22 +570,20 @@ Form field definitions can include `alias` or `componentAlias` to populate Yida
542
570
 
543
571
  ## Agent Skills
544
572
 
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`.
573
+ The `yida-skills/` directory is the source skill library used by OpenYida during development and distributed with the npm package.
546
574
 
547
575
  | Path | Purpose |
548
576
  |------|---------|
549
577
  | `yida-skills/SKILL.md` | Entry point and skill index |
550
578
  | `yida-skills/skills/` | Self-contained sub-skills for app, form, process, page, data, and integration work |
551
579
  | `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
580
 
555
581
  When OpenYida is used inside a supported AI coding environment, these skills help the agent choose the right command sequence and file conventions.
556
582
 
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
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.
560
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
+
561
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.
562
588
 
563
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
 
@@ -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__: {