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.
- package/README.md +54 -28
- package/bin/yida.js +26 -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/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/browser-handoff.js +1 -1
- package/lib/core/check-update.js +66 -21
- package/lib/core/command-manifest.js +9 -3
- 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 +42 -5
- package/lib/integration/integration-connector-schema.js +135 -0
- package/lib/integration/integration-create.js +231 -32
- package/lib/integration/integration-list.js +44 -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 +180 -35
- package/lib/permission/get-permission.js +64 -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 +81 -0
- package/lib/report/chart-builder.js +100 -168
- package/lib/report/constants.js +10 -11
- package/lib/report/contract.js +137 -24
- package/lib/report/data-model.js +51 -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 +3 -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-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 +13 -10
- package/yida-skills/skills/yida-integration/SKILL.md +40 -8
- package/yida-skills/skills/yida-integration/references/integration-node-schemas.md +35 -2
- 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 +13 -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
|
|
|
@@ -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>
|
|
438
|
-
| `openyida create-process <appType>
|
|
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
|
|
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 "..."` |
|
|
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
|
|
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] =
|
|
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__: {
|