openxiangda 1.0.192 → 1.0.194
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 +4 -0
- package/lib/application-environments.js +24 -3
- package/lib/cli.js +91 -5
- package/openxiangda-skills/SKILL.md +1 -1
- package/openxiangda-skills/references/openxiangda-api.md +4 -0
- package/openxiangda-skills/skills/openxiangda-core/SKILL.md +1 -1
- package/package.json +3 -2
- package/templates/openxiangda-react-spa/.cursor/rules/openxiangda.mdc +1 -1
- package/templates/openxiangda-react-spa/.qoder/rules/openxiangda.md +1 -1
- package/templates/openxiangda-react-spa/AGENTS.md +1 -1
- package/templates/sy-lowcode-app-workspace/.cursor/rules/openxiangda.mdc +1 -1
- package/templates/sy-lowcode-app-workspace/.qoder/rules/openxiangda.md +1 -1
- package/templates/sy-lowcode-app-workspace/AGENTS.md +1 -1
package/README.md
CHANGED
|
@@ -53,6 +53,8 @@ openxiangda environment init --logical-app example-app --name "示例应用" --k
|
|
|
53
53
|
openxiangda environment bind production --app-type APP_PROD --profile dev
|
|
54
54
|
openxiangda environment attach --logical-app example-app --preproduction-target example-pre --production-target example-prod --profile dev
|
|
55
55
|
openxiangda environment status --json
|
|
56
|
+
# 仅限投产前环境角色重分类:原子交换两个既有应用的角色,应用数据和 Release Head 不移动
|
|
57
|
+
openxiangda environment swap --reason "投产前将现有业务应用调整为正式应用" --confirm-production --profile dev
|
|
56
58
|
openxiangda studio
|
|
57
59
|
# 环境托管应用日常发布:冻结候选 → 预发 → 测试证据 → 同候选晋级正式
|
|
58
60
|
openxiangda release candidate --change mainline-release --environment preproduction
|
|
@@ -71,6 +73,8 @@ An environment-managed workspace keeps one logical application with independent
|
|
|
71
73
|
|
|
72
74
|
Existing workspaces connect to a server-side environment set with `environment attach`. If the legacy `profiles.<profile>` binding has the same `appType` as one environment, its resource mappings are copied only into that matching target (normally preproduction). Production starts with an empty mapping, and later writes update only the selected target even when both targets reuse one login profile.
|
|
73
75
|
|
|
76
|
+
`environment swap` is a commissioning/reclassification operation, not a release shortcut. It requires `--confirm-production` and a reason, atomically exchanges the two existing environment roles, preserves each application's data and Release Heads, and remaps local resource IDs by appType. Existing side-effect policies remain restricted unless explicit replacement policy JSON is supplied.
|
|
77
|
+
|
|
74
78
|
`openxiangda studio` starts a loopback-only local Developer Center with a random session token. It shows target bindings, candidate/deployment status, Git state, test evidence, drift, and the next safe action. Its buttons invoke only registered OpenXiangda operations; it does not expose arbitrary shell execution. Production promotion and rollback still require an explicit production confirmation.
|
|
75
79
|
|
|
76
80
|
`resource plan` and `resource publish --dry-run` run behind a strict GET/HEAD-only HTTP guard. If a read receives HTTP 401, the command fails with `READ_ONLY_AUTH_REQUIRED` and never calls the token refresh POST from inside the plan. Run `openxiangda auth refresh --profile <name>` (or log in again) before retrying; a plan must not mutate auth state or platform resources.
|
|
@@ -182,9 +182,11 @@ function bindEnvironmentTarget(state, input) {
|
|
|
182
182
|
state.targets = state.targets || {};
|
|
183
183
|
const previous = state.targets[targetName] || {};
|
|
184
184
|
const previousResources = previous.resources || {};
|
|
185
|
-
const resources =
|
|
186
|
-
?
|
|
187
|
-
:
|
|
185
|
+
const resources = input.replaceResources
|
|
186
|
+
? cloneJsonValue(input.seedResources || {}) || {}
|
|
187
|
+
: hasStateResourceMappings(previousResources)
|
|
188
|
+
? previousResources
|
|
189
|
+
: cloneJsonValue(input.seedResources || previousResources) || {};
|
|
188
190
|
state.targets[targetName] = {
|
|
189
191
|
...previous,
|
|
190
192
|
targetName,
|
|
@@ -204,6 +206,25 @@ function bindEnvironmentTarget(state, input) {
|
|
|
204
206
|
resources,
|
|
205
207
|
updatedAt: new Date().toISOString(),
|
|
206
208
|
};
|
|
209
|
+
if (input.replaceAppScopedState) {
|
|
210
|
+
const seedAppScopedState = input.seedAppScopedState || {};
|
|
211
|
+
for (const key of [
|
|
212
|
+
'lastDeploymentId',
|
|
213
|
+
'lastCandidateId',
|
|
214
|
+
'lastDeploymentStatus',
|
|
215
|
+
'runtime',
|
|
216
|
+
]) {
|
|
217
|
+
delete state.targets[targetName][key];
|
|
218
|
+
if (
|
|
219
|
+
Object.prototype.hasOwnProperty.call(seedAppScopedState, key) &&
|
|
220
|
+
seedAppScopedState[key] !== undefined
|
|
221
|
+
) {
|
|
222
|
+
state.targets[targetName][key] = cloneJsonValue(
|
|
223
|
+
seedAppScopedState[key]
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
207
228
|
if (!state.currentTarget || kind === 'preproduction') {
|
|
208
229
|
state.currentTarget = targetName;
|
|
209
230
|
}
|
package/lib/cli.js
CHANGED
|
@@ -299,7 +299,7 @@ Usage:
|
|
|
299
299
|
openxiangda open-api credential list|get|create|update|rotate-secret [id] [--profile name] [--show-secret] [--yes] [--json]
|
|
300
300
|
openxiangda design gates|template|review [--topic code] [--json]
|
|
301
301
|
openxiangda sdd init|migrate|propose|quick|bundle|approve|status|context|verify|sync|archive [change] [--change id] [--json]
|
|
302
|
-
openxiangda environment init|attach|bind|list|status|diff|use [preproduction|production] [--logical-app code] [--environment target] [--json]
|
|
302
|
+
openxiangda environment init|attach|bind|swap|list|status|diff|use [preproduction|production] [--logical-app code] [--environment target] [--json]
|
|
303
303
|
openxiangda release publish|begin|status|integration-status|renew|end [--change id] [--profile name] [--watch] [--json]
|
|
304
304
|
openxiangda release candidate|deploy|test|promote|rollback [--candidate id] [--environment target] [--json]
|
|
305
305
|
openxiangda task status --change <id> [--profile name] [--watch] [--json]
|
|
@@ -1862,9 +1862,10 @@ function saveEnvironmentWorkspaceState(state, setDetail, environmentDetail, inpu
|
|
|
1862
1862
|
};
|
|
1863
1863
|
const legacyBound = state.profiles?.[input.profile];
|
|
1864
1864
|
const seedResources =
|
|
1865
|
-
|
|
1865
|
+
input.seedResources ??
|
|
1866
|
+
(legacyBound?.appType === environmentDetail.appType
|
|
1866
1867
|
? legacyBound.resources
|
|
1867
|
-
: undefined;
|
|
1868
|
+
: undefined);
|
|
1868
1869
|
const bound = bindEnvironmentTarget(state, {
|
|
1869
1870
|
targetName: input.targetName,
|
|
1870
1871
|
profile: input.profile,
|
|
@@ -1875,6 +1876,9 @@ function saveEnvironmentWorkspaceState(state, setDetail, environmentDetail, inpu
|
|
|
1875
1876
|
publicOrigin: environmentDetail.publicOrigin,
|
|
1876
1877
|
sideEffectPolicy: environmentDetail.sideEffectPolicy,
|
|
1877
1878
|
seedResources,
|
|
1879
|
+
replaceResources: input.replaceResources,
|
|
1880
|
+
seedAppScopedState: input.seedAppScopedState,
|
|
1881
|
+
replaceAppScopedState: input.replaceAppScopedState,
|
|
1878
1882
|
});
|
|
1879
1883
|
ensureResourceBuckets(bound);
|
|
1880
1884
|
saveProjectState(state);
|
|
@@ -1889,6 +1893,7 @@ async function environment(args) {
|
|
|
1889
1893
|
'用法: openxiangda environment init --logical-app <code> --name <name> --kind preproduction --app-type APP_XXX [--target name] [--profile name]',
|
|
1890
1894
|
' openxiangda environment attach --logical-app <code> [--preproduction-target name] [--production-target name] [--profile name]',
|
|
1891
1895
|
' openxiangda environment bind preproduction|production --app-type APP_XXX [--target name] [--profile name]',
|
|
1896
|
+
' openxiangda environment swap --reason <text> --confirm-production [--preproduction-side-effect-policy-json <JSON|file>] [--production-side-effect-policy-json <JSON|file>] [--profile name]',
|
|
1892
1897
|
' openxiangda environment list|status|diff [--logical-app code] [--profile name] [--json]',
|
|
1893
1898
|
' openxiangda environment use <target|preproduction|production>',
|
|
1894
1899
|
].join('\n'));
|
|
@@ -2049,6 +2054,87 @@ async function environment(args) {
|
|
|
2049
2054
|
Object.values(state.targets || {})[0]?.profile ||
|
|
2050
2055
|
config.currentProfile;
|
|
2051
2056
|
|
|
2057
|
+
if (subcommand === 'swap') {
|
|
2058
|
+
if (!flags['confirm-production']) {
|
|
2059
|
+
fail(
|
|
2060
|
+
'environment swap 会交换真实应用的预发/正式角色,必须提供 --confirm-production'
|
|
2061
|
+
);
|
|
2062
|
+
}
|
|
2063
|
+
const reason = readStringFlag(flags, 'reason');
|
|
2064
|
+
if (!reason || reason.trim().length < 10) {
|
|
2065
|
+
fail('environment swap 必须提供至少 10 个字符的 --reason');
|
|
2066
|
+
}
|
|
2067
|
+
const profile = getProfile(config, selectedProfile);
|
|
2068
|
+
const previousTargets = Object.values(state.targets || {}).map(target =>
|
|
2069
|
+
JSON.parse(JSON.stringify(target))
|
|
2070
|
+
);
|
|
2071
|
+
const readOptionalPolicy = flagName =>
|
|
2072
|
+
flags[flagName]
|
|
2073
|
+
? readJsonInput(flags[flagName], flagName)
|
|
2074
|
+
: undefined;
|
|
2075
|
+
const data = await requestWithAuth(
|
|
2076
|
+
config,
|
|
2077
|
+
profile.profileName,
|
|
2078
|
+
environmentSetApiPath(code, '/environments/swap-roles'),
|
|
2079
|
+
{
|
|
2080
|
+
method: 'POST',
|
|
2081
|
+
body: {
|
|
2082
|
+
confirmation: 'SWAP_PREPRODUCTION_AND_PRODUCTION',
|
|
2083
|
+
reason,
|
|
2084
|
+
preproduction: {
|
|
2085
|
+
displayName: flags['preproduction-display-name'],
|
|
2086
|
+
publicOrigin: flags['preproduction-public-origin'],
|
|
2087
|
+
sideEffectPolicy: readOptionalPolicy(
|
|
2088
|
+
'preproduction-side-effect-policy-json'
|
|
2089
|
+
),
|
|
2090
|
+
},
|
|
2091
|
+
production: {
|
|
2092
|
+
displayName: flags['production-display-name'],
|
|
2093
|
+
publicOrigin: flags['production-public-origin'],
|
|
2094
|
+
sideEffectPolicy: readOptionalPolicy(
|
|
2095
|
+
'production-side-effect-policy-json'
|
|
2096
|
+
),
|
|
2097
|
+
},
|
|
2098
|
+
},
|
|
2099
|
+
}
|
|
2100
|
+
);
|
|
2101
|
+
const targets = [];
|
|
2102
|
+
for (const environmentDetail of data.environments || []) {
|
|
2103
|
+
const previousKindTarget = previousTargets.find(
|
|
2104
|
+
target => target.kind === environmentDetail.kind
|
|
2105
|
+
);
|
|
2106
|
+
const previousAppTarget = previousTargets.find(
|
|
2107
|
+
target => target.appType === environmentDetail.appType
|
|
2108
|
+
);
|
|
2109
|
+
targets.push(
|
|
2110
|
+
saveEnvironmentWorkspaceState(state, data, environmentDetail, {
|
|
2111
|
+
targetName:
|
|
2112
|
+
previousKindTarget?.targetName || environmentDetail.kind,
|
|
2113
|
+
profile:
|
|
2114
|
+
previousKindTarget?.profile ||
|
|
2115
|
+
previousAppTarget?.profile ||
|
|
2116
|
+
profile.profileName,
|
|
2117
|
+
seedResources: previousAppTarget?.resources,
|
|
2118
|
+
replaceResources: true,
|
|
2119
|
+
seedAppScopedState: previousAppTarget,
|
|
2120
|
+
replaceAppScopedState: true,
|
|
2121
|
+
})
|
|
2122
|
+
);
|
|
2123
|
+
}
|
|
2124
|
+
const result = {
|
|
2125
|
+
logicalApp: state.logicalApp,
|
|
2126
|
+
targets,
|
|
2127
|
+
roleSwap: data.roleSwap,
|
|
2128
|
+
};
|
|
2129
|
+
if (flags.json) return writeJson(result);
|
|
2130
|
+
print(
|
|
2131
|
+
`应用环境角色已交换: ${targets
|
|
2132
|
+
.map(target => `${target.kind}=${target.appType}`)
|
|
2133
|
+
.join(', ')}`
|
|
2134
|
+
);
|
|
2135
|
+
return;
|
|
2136
|
+
}
|
|
2137
|
+
|
|
2052
2138
|
if (subcommand === 'bind') {
|
|
2053
2139
|
const kind = normalizeEnvironmentKind(positional[0] || flags.kind);
|
|
2054
2140
|
const appType = readStringFlag(flags, 'app-type');
|
|
@@ -2109,7 +2195,7 @@ async function environment(args) {
|
|
|
2109
2195
|
return;
|
|
2110
2196
|
}
|
|
2111
2197
|
|
|
2112
|
-
fail('用法: openxiangda environment init|attach|bind|list|status|diff|use');
|
|
2198
|
+
fail('用法: openxiangda environment init|attach|bind|swap|list|status|diff|use');
|
|
2113
2199
|
}
|
|
2114
2200
|
|
|
2115
2201
|
function buildEnvironmentStatusDiff(status) {
|
|
@@ -13305,7 +13391,7 @@ async function commands(args) {
|
|
|
13305
13391
|
'open-api credential list|get|create|update|rotate-secret [id]',
|
|
13306
13392
|
'design gates|template|review [--topic code]',
|
|
13307
13393
|
'sdd init|migrate|propose|quick|bundle|approve|status|context|verify|sync|archive',
|
|
13308
|
-
'environment init|attach|bind|list|status|diff|use',
|
|
13394
|
+
'environment init|attach|bind|swap|list|status|diff|use',
|
|
13309
13395
|
'release candidate|deploy|test|promote|rollback|publish|begin|status|integration-status|renew|end|backend-head|backend-list|backend-detail|backend-diff|backend-rollback|backend-abort|backend-retry|app-capture|app-head|app-list|app-detail|app-diff|app-prepare|app-verify|app-activate|app-finalize|app-rollback|app-abort',
|
|
13310
13396
|
'studio [--no-open] [--port 0]',
|
|
13311
13397
|
'env',
|
|
@@ -104,7 +104,7 @@ openxiangda release test --deployment <deployment-id> --environment preproductio
|
|
|
104
104
|
openxiangda release promote --candidate <candidate-id> --environment production --confirm-production
|
|
105
105
|
```
|
|
106
106
|
|
|
107
|
-
For a workspace registered by `openxiangda environment init` or connected to an existing set with `openxiangda environment attach`, production is never a direct publish target. The immutable candidate must first be deployed and passed in preproduction, and promotion must reuse the same candidate hash. Preproduction and production keep independent app/resource/data identities in `.openxiangda/state.json`; do not copy IDs between targets. `environment attach` may seed only the matching legacy app binding into preproduction and must leave production empty. Use `openxiangda studio` for the loopback-only developer view of bindings, drift, evidence, and safe next actions.
|
|
107
|
+
For a workspace registered by `openxiangda environment init` or connected to an existing set with `openxiangda environment attach`, production is never a direct publish target. The immutable candidate must first be deployed and passed in preproduction, and promotion must reuse the same candidate hash. Preproduction and production keep independent app/resource/data identities in `.openxiangda/state.json`; do not copy IDs between targets. `environment attach` may seed only the matching legacy app binding into preproduction and must leave production empty. Only during authorized commissioning/reclassification may `environment swap --reason "..." --confirm-production` atomically exchange the two existing roles; it preserves each app's data and Release Heads, remaps local resource IDs by appType, and does not enable production side effects by default. Use `openxiangda studio` for the loopback-only developer view of bindings, drift, evidence, and safe next actions.
|
|
108
108
|
|
|
109
109
|
`resource plan` and publish dry-runs are strictly GET/HEAD-only. `READ_ONLY_AUTH_REQUIRED` means the access token expired; run `openxiangda auth refresh --profile <name>` or log in again before retrying. Never add an automatic refresh POST inside a plan.
|
|
110
110
|
|
|
@@ -862,6 +862,10 @@ List environment sets or return one set with its bound environments.
|
|
|
862
862
|
|
|
863
863
|
Binds the missing `preproduction` or `production` app identity. An appType can belong to only one environment, and one set can contain at most one environment of each kind.
|
|
864
864
|
|
|
865
|
+
### POST `/environment-sets/:code/environments/swap-roles`
|
|
866
|
+
|
|
867
|
+
Atomically exchanges the roles of the two existing app identities during an explicitly confirmed commissioning/reclassification operation. The request requires `confirmation=SWAP_PREPRODUCTION_AND_PRODUCTION` and a reason. Application data and Release Heads stay with their appType; local target resources are remapped by appType. Existing side-effect restrictions remain unless the request explicitly supplies replacement policies.
|
|
868
|
+
|
|
865
869
|
### GET `/environment-sets/:code/status`
|
|
866
870
|
|
|
867
871
|
Returns each target's active heads, latest deployment, side-effect policy, and candidate drift state for the local Developer Center.
|
|
@@ -105,7 +105,7 @@ openxiangda release test --deployment <deployment-id> --environment preproductio
|
|
|
105
105
|
openxiangda release promote --candidate <candidate-id> --environment production --confirm-production
|
|
106
106
|
```
|
|
107
107
|
|
|
108
|
-
Once `environment init` registers a logical application, or `environment attach` connects an existing workspace, `preproduction` and `production` are separate target bindings with separate app/resource/data IDs. Never copy IDs between them. Existing legacy resource mappings may seed only the target whose appType matches (normally preproduction); production stays empty. The candidate is immutable and promotion must reuse the candidate that has fresh, passing, deployment-bound preproduction evidence. Direct `release publish` to either managed target fails closed. `openxiangda studio` is the local loopback-only developer view and exposes only registered safe actions.
|
|
108
|
+
Once `environment init` registers a logical application, or `environment attach` connects an existing workspace, `preproduction` and `production` are separate target bindings with separate app/resource/data IDs. Never copy IDs between them. Existing legacy resource mappings may seed only the target whose appType matches (normally preproduction); production stays empty. The candidate is immutable and promotion must reuse the candidate that has fresh, passing, deployment-bound preproduction evidence. Direct `release publish` to either managed target fails closed. Only authorized commissioning/reclassification may run `environment swap --reason "..." --confirm-production`; the atomic operation preserves each app's data and Release Heads, remaps local resources by appType, and keeps existing side-effect restrictions unless explicit replacement policies are supplied. `openxiangda studio` is the local loopback-only developer view and exposes only registered safe actions.
|
|
109
109
|
|
|
110
110
|
`release publish` is the normal whole-app entrypoint for legacy unmanaged workspaces: it verifies SDD without mutating reviewed files, waits for the promotion lease, freezes one App capture, stages the exact Form/Backend/Runtime children, resumes from a private execution journal, finalizes once, and releases the lease. Managed applications use candidate/deploy/test/promote and deployment-scoped journals. Individual release commands are recovery/diagnostic primitives.
|
|
111
111
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openxiangda",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.194",
|
|
4
4
|
"description": "OpenXiangda CLI, workspace build tools, runtime SDK, and form components.",
|
|
5
5
|
"private": false,
|
|
6
6
|
"bin": {
|
|
@@ -64,7 +64,7 @@
|
|
|
64
64
|
"build:sdk": "tsup --config packages/sdk/tsup.config.ts",
|
|
65
65
|
"check": "node --check bin/openxiangda.js && node --check lib/*.js && node --check packages/sdk/src/build-source/src/cli.mjs && node --check packages/sdk/src/build-source/scripts/*.mjs && node --check packages/sdk/src/build-source/scripts/utils/*.mjs",
|
|
66
66
|
"test": "npm run test:fast",
|
|
67
|
-
"test:fast": "npm run check && node scripts/http-proxy-smoke.mjs && node scripts/form-export-cli-smoke.mjs && node scripts/open-api-cli-smoke.mjs && node scripts/release-plan-smoke.mjs && node scripts/typed-resource-plan-smoke.mjs && node scripts/integration-bundle-smoke.mjs && node scripts/content-cache-smoke.mjs && node scripts/dependency-capsule-smoke.mjs && node scripts/sdd-stages-smoke.mjs",
|
|
67
|
+
"test:fast": "npm run check && node scripts/http-proxy-smoke.mjs && node scripts/form-export-cli-smoke.mjs && node scripts/open-api-cli-smoke.mjs && node scripts/release-plan-smoke.mjs && node scripts/typed-resource-plan-smoke.mjs && node scripts/integration-bundle-smoke.mjs && node scripts/content-cache-smoke.mjs && node scripts/dependency-capsule-smoke.mjs && node scripts/sdd-stages-smoke.mjs && node scripts/environment-swap-cli-smoke.mjs",
|
|
68
68
|
"test:changed": "npm run check && node scripts/run-test-suite.mjs --changed --evidence",
|
|
69
69
|
"test:contract": "npm run check && node scripts/run-test-suite.mjs --contract --evidence",
|
|
70
70
|
"test:release": "npm run check && node scripts/run-test-suite.mjs --release --evidence",
|
|
@@ -111,6 +111,7 @@
|
|
|
111
111
|
"test:page-release-cli": "node scripts/page-release-cli-smoke.mjs",
|
|
112
112
|
"test:app-release-cli": "node scripts/app-release-cli-smoke.mjs",
|
|
113
113
|
"test:application-environments": "node scripts/application-environments-smoke.mjs",
|
|
114
|
+
"test:environment-swap": "node scripts/environment-swap-cli-smoke.mjs",
|
|
114
115
|
"test:developer-center": "node scripts/developer-center-smoke.mjs",
|
|
115
116
|
"test:form-release-cas": "node scripts/form-release-cas-smoke.mjs",
|
|
116
117
|
"test:source-dependencies": "node scripts/source-dependencies-smoke.mjs",
|
|
@@ -27,7 +27,7 @@ This is an OpenXiangda React SPA workspace. See [AGENTS.md](mdc:AGENTS.md) for f
|
|
|
27
27
|
- Very small copy/style/binding changes may use `openxiangda sdd quick <change> ...`; quick mode records exact low-risk scope without a redundant proposal/approval loop when the user already requested it.
|
|
28
28
|
- SDD is streamlined by default: approval and exact structured scope are hard gates, while unfinished task/evidence/spec prose only warns. Configure `strictDocumentation: true` only when prose must block.
|
|
29
29
|
- Before platform writes, run `release begin` from a clean local main/master that exactly equals the authoritative remote tip. Feature branches and unpushed mainline commits fail before any write. After activation, run `integration-status` and `release end`; no post-release merge is needed.
|
|
30
|
-
- Managed preproduction and production targets own independent app/resource/data identities and side-effect policy. Existing workspaces use `environment attach`; only an appType-matching legacy binding may seed preproduction and production starts empty. Never direct-publish or copy IDs across them; use `openxiangda studio` to inspect candidate, evidence, and drift state.
|
|
30
|
+
- Managed preproduction and production targets own independent app/resource/data identities and side-effect policy. Existing workspaces use `environment attach`; only an appType-matching legacy binding may seed preproduction and production starts empty. Never direct-publish or copy IDs across them; use `openxiangda studio` to inspect candidate, evidence, and drift state. Only an explicitly authorized commissioning reclassification may use `environment swap --reason "..." --confirm-production`; it preserves app data/Release Heads and keeps side effects restricted by default.
|
|
31
31
|
- Account/role/permission/RBAC/organization-account/query-param authorization work must run `openxiangda design gates --topic permissions --json`, choose `managed-platform-account`, `existing-platform-user-assignment`, `static-role-permission`, or `query-param-context`, and write the permission matrix.
|
|
32
32
|
- Roles that create roles, assign members, grant API permissions, maintain permission groups, or manage organization accounts must declare `apiPermissionCodes`, such as `app:role:manage`, `app:page-permission-group:manage`, `app:form-permission-group:manage`, and `app:organization:manage`.
|
|
33
33
|
- `src/resources/**` is the resource source of truth; use `validate -> plan -> publish`.
|
|
@@ -31,7 +31,7 @@ This is an OpenXiangda React SPA workspace. Read [AGENTS.md](AGENTS.md) for full
|
|
|
31
31
|
- 默认按逻辑资源 code 使用 `--only` 或单资源 `--code`;全类型/全应用发布必须由批准的依赖闭包明确覆盖。
|
|
32
32
|
- Function/Automation 源码触发默认走服务端 source-field PATCH,保留线上 bindings/contracts/metadata/trigger/view/enabled/published state;整包 manifest 替换必须加 `--replace-manifest --reason "..."`。
|
|
33
33
|
- Promotion 必须持有 `release begin/end` 租约;`release begin` 只接受与权威远端 tip 完全一致的 clean main/master。feature branch 或未 push 主线在任何写入前失败。激活后直接运行 `integration-status` 和 `release end`,不再补做发布后合并。
|
|
34
|
-
- 已有工作区通过 `environment attach` 接入环境组,旧资源映射只迁移到 appType 相同的预发 target,正式 target 必须为空。preproduction / production 分别拥有独立 appType、资源 ID、数据和副作用策略;禁止直接 `release publish`,禁止跨环境复制 ID,使用 `openxiangda studio`
|
|
34
|
+
- 已有工作区通过 `environment attach` 接入环境组,旧资源映射只迁移到 appType 相同的预发 target,正式 target 必须为空。preproduction / production 分别拥有独立 appType、资源 ID、数据和副作用策略;禁止直接 `release publish`,禁止跨环境复制 ID,使用 `openxiangda studio` 查看状态。只有用户明确授权的投产前重分类可执行 `environment swap --reason "..." --confirm-production`;它不移动应用数据或 Release Head,且默认不放开副作用。
|
|
35
35
|
- React SPA 路由由 `src/app/router.tsx` 管理,前端包通过 `openxiangda runtime deploy` 发布。
|
|
36
36
|
- 表单附件预览使用 `AttachmentField` / `ImageField`;普通自定义页面使用 `AttachmentPreviewList` / `ImagePreviewGrid` / `useFilePreview`,不要伪造表单上下文、直接引用内部预览实现或维护扩展名白名单。
|
|
37
37
|
- 页面权限、表单权限、公开访问 grants、App Function 后端检查是授权依据;前端只做展示保护。
|
|
@@ -53,7 +53,7 @@ openxiangda commands --json
|
|
|
53
53
|
|
|
54
54
|
模板已停用无范围的 `pnpm deploy` 聚合入口。日常变更必须使用 `resource plan|publish <type> --only <codes>`(单资源可用 `--code <code>`)。Form bundle、Backend Release 和 Runtime 都先暂存;CLI 会按 `--change` 自动聚合 `.openxiangda/releases/<change>/staged-resources.json`,最后只由一次 Root App finalize 原子激活。不要使用 `workspace publish --form`、单独 `runtime activate`、`pnpm publish:all`、`pnpm openxiangda:publish` 或 `lowcode-workspace publish-all`。
|
|
55
55
|
|
|
56
|
-
工作区一旦通过 `environment init` 登记,或通过 `environment attach` 接入已有环境组,`release publish` 即不再是入口。必须冻结不可变 candidate,部署到 preproduction,提交与 deployment/candidate/appType/AppRelease 精确绑定且仍在有效期内的通过证据,再将同一 candidate 晋级 production。两套环境的 appType、资源 ID、数据和副作用策略完全独立;旧单目标资源映射只允许迁移到 appType 相同的预发 target,正式 target
|
|
56
|
+
工作区一旦通过 `environment init` 登记,或通过 `environment attach` 接入已有环境组,`release publish` 即不再是入口。必须冻结不可变 candidate,部署到 preproduction,提交与 deployment/candidate/appType/AppRelease 精确绑定且仍在有效期内的通过证据,再将同一 candidate 晋级 production。两套环境的 appType、资源 ID、数据和副作用策略完全独立;旧单目标资源映射只允许迁移到 appType 相同的预发 target,正式 target 必须为空。仅在用户明确授权的投产前重分类中使用 `environment swap --reason "..." --confirm-production` 原子交换两个既有应用的环境角色;数据和 Release Head 不移动,副作用默认不放开。`openxiangda studio` 提供仅本机访问的开发者页面,用于查看绑定、漂移、候选、部署、证据和下一安全动作。
|
|
57
57
|
|
|
58
58
|
`resource plan` 与 publish dry-run 严格只允许 GET/HEAD。遇到 `READ_ONLY_AUTH_REQUIRED` 时,先执行 `openxiangda auth refresh --profile <name>` 或重新登录再重试;不得在 plan 内自动 POST 刷新 token。
|
|
59
59
|
|
|
@@ -31,7 +31,7 @@ This is a `sy-lowcode-app-workspace` managed by the `openxiangda` CLI. See [AGEN
|
|
|
31
31
|
- SDD is streamlined by default: approval and exact structured scope are hard gates, while unfinished task/evidence/spec prose only warns. Use `strictDocumentation: true` only when prose must block.
|
|
32
32
|
- Source-triggered Function/Automation publishing uses server-side source-field PATCH by default and preserves online bindings, contracts, metadata, trigger/view configuration, and enabled/published state. Whole-manifest replacement requires exact `--only/--code` plus `--replace-manifest --reason "..."`.
|
|
33
33
|
- Before platform writes, run `release begin` only from clean main/master exactly equal to the authoritative remote tip. Feature branches and unpushed mainline commits fail before writes. After activation, run `integration-status` and `release end`; no post-release merge is needed.
|
|
34
|
-
- Environment-managed workspaces never publish directly. Existing workspaces use `environment attach`; only an appType-matching legacy binding may seed preproduction and production starts empty. Run `release candidate`, `release deploy --environment preproduction`, `release test`, then promote that same candidate with `release promote --environment production --confirm-production`. Keep app/resource/data IDs isolated and inspect state with `openxiangda studio`.
|
|
34
|
+
- Environment-managed workspaces never publish directly. Existing workspaces use `environment attach`; only an appType-matching legacy binding may seed preproduction and production starts empty. Run `release candidate`, `release deploy --environment preproduction`, `release test`, then promote that same candidate with `release promote --environment production --confirm-production`. Keep app/resource/data IDs isolated and inspect state with `openxiangda studio`. Only an explicitly authorized commissioning reclassification may use `environment swap --reason "..." --confirm-production`; it preserves app data/Release Heads and keeps side effects restricted by default.
|
|
35
35
|
- Routine edits should plan and publish exact change targets: `workspace plan --profile <name> --change <change> --changed`, then `workspace publish --profile <name> --change <change> --only pages/a,forms/b --dry-run`.
|
|
36
36
|
- Account/role/permission/RBAC/organization-account/query-param authorization work must choose a mode first: `managed-platform-account`, `existing-platform-user-assignment`, `static-role-permission`, or `query-param-context`, then write the permission matrix.
|
|
37
37
|
- Roles that create roles, assign members, grant API permissions, maintain permission groups, or manage organization accounts must declare `apiPermissionCodes` in `src/resources/roles/<code>.json`, such as `app:role:manage`, `app:page-permission-group:manage`, `app:form-permission-group:manage`, and `app:organization:manage`.
|
|
@@ -31,7 +31,7 @@ This is a `sy-lowcode-app-workspace` managed by the `openxiangda` CLI. Read [AGE
|
|
|
31
31
|
- SDD 默认 streamlined:approval 与结构化精确范围是硬门禁,未完成的 task/evidence/spec 文案只告警;只有 `strictDocumentation: true` 才阻断。
|
|
32
32
|
- Function/Automation 源码触发默认走服务端 source-field PATCH,保留线上 bindings/contracts/metadata/trigger/view/enabled/published state;整包 manifest 替换必须精确 `--only/--code` 并加 `--replace-manifest --reason "..."`。
|
|
33
33
|
- 写入平台前只从与权威远端 tip 完全一致的 clean main/master 执行 `release begin`。feature branch 或未 push 主线在任何写入前失败;激活后直接运行 `integration-status` 和 `release end`,无需发布后再合并。
|
|
34
|
-
- 环境托管工作区禁止直发:已有工作区先用 `environment attach` 接入,旧映射只能迁移到 appType 相同的预发 target,正式 target 必须为空。然后执行 `release candidate`、`release deploy --environment preproduction`、`release test`,最后将同一 candidate 用 `release promote --environment production --confirm-production` 晋级。两套环境的 appType、资源 ID 和数据不可互拷;用 `openxiangda studio`
|
|
34
|
+
- 环境托管工作区禁止直发:已有工作区先用 `environment attach` 接入,旧映射只能迁移到 appType 相同的预发 target,正式 target 必须为空。然后执行 `release candidate`、`release deploy --environment preproduction`、`release test`,最后将同一 candidate 用 `release promote --environment production --confirm-production` 晋级。两套环境的 appType、资源 ID 和数据不可互拷;用 `openxiangda studio` 查看状态。只有用户明确授权的投产前重分类可执行 `environment swap --reason "..." --confirm-production`;它不移动应用数据或 Release Head,且默认不放开副作用。
|
|
35
35
|
- 单文件改动默认按 change 和逻辑目标发布:先 `workspace plan --profile <name> --change <change> --changed`,再 `workspace publish --profile <name> --change <change> --only pages/a,forms/b --dry-run`。
|
|
36
36
|
- 账号/角色/权限/RBAC/组织账号/查询参数授权需求先选权限模式:`managed-platform-account` / `existing-platform-user-assignment` / `static-role-permission` / `query-param-context`,并输出权限矩阵。
|
|
37
37
|
- 角色能新增角色、分配成员、授接口权限、维护权限组或管理组织账号时,`src/resources/roles/<code>.json` 必须声明 `apiPermissionCodes`,例如 `app:role:manage`、`app:page-permission-group:manage`、`app:form-permission-group:manage`、`app:organization:manage`。
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
- ✅ `resource plan` 与 publish dry-run 严格只允许 GET/HEAD;遇到 `READ_ONLY_AUTH_REQUIRED` 时先执行 `openxiangda auth refresh --profile <name>` 或重新登录,不得在 plan 内自动 POST 刷新 token。
|
|
58
58
|
- ✅ Function/Automation 使用 Backend Release v2;正式多资源发布用精确 `--only/--code` 加 `--stage-only` 暂存,同一 child 可混合 create、source-only update 和显式 manifest replacement,再由 Root App finalize 原子激活。整包 manifest 替换必须另加 `--replace-manifest --reason "..."`。
|
|
59
59
|
- ✅ 未登记环境的旧工作区,正式 promotion 先聚合 mainline bundle 并 commit/push,再运行 `release publish --change <id> --profile <name>`。
|
|
60
|
-
- ✅ 已通过 `environment init` 登记或 `environment attach` 接入的工作区必须走 `release candidate → release deploy --environment preproduction → release test → release promote --environment production --confirm-production`。候选不可变,正式晋级必须复用预发通过的同一候选;preproduction / production 的 appType、资源 ID、数据和副作用策略完全隔离,严禁跨环境复制 ID 或直接 `release publish`。旧单目标映射只允许按相同 appType
|
|
60
|
+
- ✅ 已通过 `environment init` 登记或 `environment attach` 接入的工作区必须走 `release candidate → release deploy --environment preproduction → release test → release promote --environment production --confirm-production`。候选不可变,正式晋级必须复用预发通过的同一候选;preproduction / production 的 appType、资源 ID、数据和副作用策略完全隔离,严禁跨环境复制 ID 或直接 `release publish`。旧单目标映射只允许按相同 appType 迁移到预发。仅在用户明确授权的投产前重分类中使用 `environment swap --reason "..." --confirm-production` 原子交换两个既有应用的环境角色;数据和 Release Head 不移动,副作用默认不放开。
|
|
61
61
|
- ✅ 本地开发者可运行 `openxiangda studio` 查看两套环境、差异、候选、部署和测试证据;该页面只监听回环地址且只暴露注册动作,生产操作仍需显式确认。
|
|
62
62
|
|
|
63
63
|
## 严禁
|