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.
- package/README.md +63 -33
- package/bin/yida.js +69 -74
- package/lib/aggregate-table/aggregate-table.js +90 -18
- package/lib/aggregate-table/contract.js +135 -7
- package/lib/app/builder-ai-source.js +0 -2
- package/lib/app/form-navigation.js +4 -1
- package/lib/app/get-schema.js +58 -19
- package/lib/app/schema-semantic-analysis.js +223 -0
- package/lib/app/services/form-mode-service.js +8 -7
- package/lib/asset/ai-image.js +1 -1
- package/lib/auth/oauth-loopback.js +128 -11
- package/lib/connector/action-generator.js +1 -1
- package/lib/connector/api.js +26 -18
- package/lib/connector/connector-action-update.js +404 -0
- package/lib/connector/connector-add-action.js +10 -35
- package/lib/connector/connector-create-connection.js +16 -10
- package/lib/connector/connector-create.js +24 -15
- package/lib/connector/connector-delete-action.js +2 -1
- package/lib/connector/connector-detail.js +13 -1
- package/lib/connector/connector-list-actions.js +13 -1
- package/lib/connector/connector-list-connections.js +15 -2
- package/lib/connector/connector-smart-create.js +8 -4
- package/lib/connector/connector-test.js +193 -168
- package/lib/connector/connector-update-action.js +115 -0
- package/lib/connector/contract.js +79 -4
- package/lib/connector/doc-parser.js +8 -4
- package/lib/connector/operation-normalizer.js +13 -1
- package/lib/core/agent-capabilities.js +5 -0
- package/lib/core/browser-handoff.js +1 -1
- package/lib/core/check-update.js +66 -21
- package/lib/core/command-manifest.js +35 -12
- package/lib/core/copy.js +37 -76
- package/lib/core/doctor.js +0 -1
- package/lib/core/env-cmd.js +0 -10
- package/lib/core/env.js +2 -10
- package/lib/core/locales/en.js +216 -21
- package/lib/core/locales/zh.js +216 -21
- package/lib/core/update.js +324 -15
- package/lib/core/utils.js +58 -115
- package/lib/integration/connector-presets.js +4 -34
- package/lib/integration/integration-api.js +32 -0
- package/lib/integration/integration-check.js +49 -11
- package/lib/integration/integration-connector-schema.js +135 -0
- package/lib/integration/integration-create.js +231 -32
- package/lib/integration/integration-list.js +50 -22
- package/lib/integration/integration-readback.js +212 -0
- package/lib/integration/integration-spec-builder.js +208 -20
- package/lib/integration/integration-view-builder.js +18 -14
- package/lib/page-config/save-share-config.js +194 -38
- package/lib/permission/get-permission.js +66 -51
- package/lib/permission/permit-package-service.js +95 -0
- package/lib/permission/save-permission.js +268 -47
- package/lib/process/configure-process.js +526 -237
- package/lib/process/create-process.js +97 -105
- package/lib/process/process-diagnostics.js +8 -0
- package/lib/process/services/process-compiler.js +33 -2
- package/lib/process/services/process-view-verifier.js +140 -0
- package/lib/report/append.js +130 -146
- package/lib/report/capability-registry.js +89 -0
- package/lib/report/chart-builder.js +151 -169
- package/lib/report/constants.js +10 -11
- package/lib/report/contract.js +208 -24
- package/lib/report/data-model.js +92 -49
- package/lib/report/field-utils.js +50 -14
- package/lib/report/i18n-messages.js +160 -0
- package/lib/report/index.js +378 -251
- package/lib/report/inspect.js +125 -0
- package/lib/report/layout.js +113 -0
- package/lib/report/validation.js +204 -0
- package/package.json +4 -3
- package/scripts/postinstall.js +2 -17
- package/yida-skills/SKILL.md +2 -1
- package/yida-skills/references/routing-supplement.md +0 -1
- package/yida-skills/references/task-retrospective.md +1 -1
- package/yida-skills/skills/yida-aggregate-table/SKILL.md +75 -0
- package/yida-skills/skills/yida-app/SKILL.md +2 -1
- package/yida-skills/skills/yida-app/workflow/step-4-forms-processes.md +3 -0
- package/yida-skills/skills/yida-app/workflow/step-9-output-finish.md +2 -0
- package/yida-skills/skills/yida-connector/SKILL.md +20 -6
- package/yida-skills/skills/yida-connector-safe-actions/SKILL.md +32 -5
- package/yida-skills/skills/yida-create-process/SKILL.md +13 -3
- package/yida-skills/skills/yida-form-detail/SKILL.md +0 -1
- package/yida-skills/skills/yida-form-permission/SKILL.md +14 -10
- package/yida-skills/skills/yida-get-schema/SKILL.md +12 -2
- package/yida-skills/skills/yida-integration/SKILL.md +43 -8
- package/yida-skills/skills/yida-integration/references/integration-node-schemas.md +35 -2
- package/yida-skills/skills/yida-nav-group/SKILL.md +2 -0
- package/yida-skills/skills/yida-page-config/SKILL.md +5 -5
- package/yida-skills/skills/yida-process-rule/SKILL.md +28 -2
- package/yida-skills/skills/yida-report/SKILL.md +19 -10
- package/yida-skills/skills/yida-report/references/examples.md +3 -0
- package/yida-skills/skills/yida-report/references/report-api-guide.md +2 -0
- package/yida-skills/skills/yida-report/references/schema-builder-details.md +3 -1
- 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,
|
|
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
|
-
| [
|
|
137
|
-
| [Qoder](https://qoder.com) | Full support |
|
|
138
|
-
| [
|
|
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 <
|
|
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
|
|
429
|
-
| `openyida save-permission <appType> <formUuid>
|
|
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>
|
|
438
|
-
| `openyida create-process <appType>
|
|
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
|
|
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 "..."` |
|
|
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
|
|
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] =
|
|
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
|
|
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
|
|
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__: {
|