openxiangda 1.0.231 → 1.0.232
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 +27 -2
- package/lib/cli.js +219 -29
- package/lib/delivery-v2-cli.js +216 -0
- package/lib/delivery-v2-executor.js +955 -0
- package/lib/delivery-v2-package.js +681 -0
- package/lib/policy.js +4 -0
- package/lib/workspace-bootstrap.js +1 -0
- package/lib/workspace-init.js +4 -20
- package/openxiangda-skills/SKILL.md +21 -1
- package/openxiangda-skills/skills/openxiangda-core/SKILL.md +17 -0
- package/package.json +4 -2
- package/packages/sdk/dist/build/index.cjs.map +1 -1
- package/packages/sdk/dist/build/index.d.mts +1 -0
- package/packages/sdk/dist/build/index.d.ts +1 -0
- package/packages/sdk/dist/build/index.mjs.map +1 -1
- package/policy/openxiangda-policy.json +8 -1
- package/templates/openxiangda-react-spa/.cursor/rules/openxiangda.mdc +6 -5
- package/templates/openxiangda-react-spa/.qoder/rules/openxiangda.md +6 -6
- package/templates/openxiangda-react-spa/AGENTS.md +5 -1
- package/templates/openxiangda-react-spa/DELIVERY.md +33 -0
- package/templates/openxiangda-react-spa/app-workspace.config.ts +3 -3
- package/templates/sy-lowcode-app-workspace/.cursor/rules/openxiangda.mdc +4 -1
- package/templates/sy-lowcode-app-workspace/.qoder/rules/openxiangda.md +4 -1
- package/templates/sy-lowcode-app-workspace/AGENTS.md +7 -4
- package/templates/sy-lowcode-app-workspace/DELIVERY.md +33 -0
- package/templates/sy-lowcode-app-workspace/app-workspace.config.ts +3 -3
- package/templates/sy-lowcode-app-workspace/src/types/app-workspace.types.ts +1 -0
package/lib/workspace-init.js
CHANGED
|
@@ -183,32 +183,16 @@ function buildNextSteps(targetDir, installedDependencies, bound, runtime) {
|
|
|
183
183
|
}
|
|
184
184
|
const profileArg = bound ? bound.profile : '<name>';
|
|
185
185
|
steps.push(`openxiangda env --profile ${profileArg}`);
|
|
186
|
-
steps.push('初始化并 push 权威 <main-branch>;此 canonical 检出以后只用于集成和发布');
|
|
187
186
|
steps.push(
|
|
188
|
-
|
|
187
|
+
'完成应用开发后运行 openxiangda check --environment preproduction'
|
|
189
188
|
);
|
|
190
|
-
steps.push(
|
|
191
|
-
steps.push('openxiangda sdd propose <change>');
|
|
192
|
-
steps.push('完成开发、测试、approve 并提交 codex/<change>,不要从 feature worktree 发布');
|
|
189
|
+
steps.push('openxiangda deploy preproduction --json');
|
|
193
190
|
steps.push(
|
|
194
|
-
|
|
191
|
+
'从预发结果复制 packageDigest,然后运行 openxiangda deploy production --package <packageDigest>'
|
|
195
192
|
);
|
|
196
193
|
steps.push(
|
|
197
|
-
'openxiangda
|
|
194
|
+
'失败时运行 openxiangda status <runId>,修复外部原因后运行 openxiangda retry <runId>'
|
|
198
195
|
);
|
|
199
|
-
steps.push(
|
|
200
|
-
'git add openspec/changes/<release-change> && git commit -m "chore: prepare <release-change>" && git push origin <main-branch>'
|
|
201
|
-
);
|
|
202
|
-
steps.push(
|
|
203
|
-
runtime === 'react-spa'
|
|
204
|
-
? `openxiangda runtime deploy --no-activate --change <release-change> --profile ${profileArg}`
|
|
205
|
-
: `openxiangda workspace publish --dry-run --only <exact-codes> --change <release-change> --profile ${profileArg}`
|
|
206
|
-
);
|
|
207
|
-
steps.push(
|
|
208
|
-
`原子激活后运行 openxiangda release integration-status --change <release-change> --profile ${profileArg} --check,再运行 openxiangda release end --change <release-change> --profile ${profileArg}`
|
|
209
|
-
);
|
|
210
|
-
steps.push('openxiangda workspace cleanup');
|
|
211
|
-
steps.push('确认所有目标均为 SAFE 后运行 openxiangda workspace cleanup --apply');
|
|
212
196
|
return steps;
|
|
213
197
|
}
|
|
214
198
|
|
|
@@ -3,13 +3,33 @@ name: openxiangda
|
|
|
3
3
|
description: "Use OpenXiangda for private low-code platform work: app workspaces, forms, pages, resources, functions, automations, workflows, permissions, publishing, deployment, diagnosis, profiles, and the openxiangda CLI."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
<!-- OpenXiangda-Policy-Version:
|
|
6
|
+
<!-- OpenXiangda-Policy-Version: 5 -->
|
|
7
7
|
# OpenXiangda
|
|
8
8
|
|
|
9
9
|
OpenXiangda connects an AI coding workspace to the private low-code platform through a normal-user profile and `/openxiangda-api/v1`. External backends using AK/SK are a separate `openxiangda-open-api` flow.
|
|
10
10
|
|
|
11
11
|
This file is a router and safety card. Read only the one or two subskills selected below; do not load every OpenXiangda reference into the same turn.
|
|
12
12
|
|
|
13
|
+
## Delivery V2 is the normal release path
|
|
14
|
+
|
|
15
|
+
When `app-workspace.config.ts` declares `deliveryVersion: 2`, all later V1 SDD,
|
|
16
|
+
Git-mainline, candidate/ship, `--change`, and `--only` release rules are
|
|
17
|
+
compatibility notes only. Do not assemble a V2 release from low-level commands.
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
openxiangda check --environment preproduction
|
|
21
|
+
openxiangda deploy preproduction --json
|
|
22
|
+
openxiangda deploy production --package <packageDigest> --json
|
|
23
|
+
openxiangda status <runId> --json
|
|
24
|
+
openxiangda retry <runId> --json
|
|
25
|
+
openxiangda rollback production --to <appReleaseId> --json
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
The V2 compiler derives the exact desired-state delta, excludes secrets, builds
|
|
29
|
+
Runtime once, uploads content-addressed layers, and creates a server-persisted
|
|
30
|
+
ReleaseRun. Production consumes the sealed preproduction package without a
|
|
31
|
+
rebuild. Keep the `runId`; failures preserve successful checkpoints.
|
|
32
|
+
|
|
13
33
|
## Keep the agent loop small
|
|
14
34
|
|
|
15
35
|
- Start with one broad CodeGraph exploration that names the complete flow or feature. Use at most one focused follow-up when the first result explicitly omits a required symbol; do not repeat overlapping surveys.
|
|
@@ -7,6 +7,23 @@ description: Core OpenXiangda CLI workflow for profiles, login, workspace state,
|
|
|
7
7
|
|
|
8
8
|
Use this skill for login/profile/state questions and any plan, publish, deploy, or release operation. It intentionally contains only release-critical rules; load domain skills for forms, pages, permissions, Functions, Automation, or Workflow behavior.
|
|
9
9
|
|
|
10
|
+
## Delivery V2
|
|
11
|
+
|
|
12
|
+
If `app-workspace.config.ts` declares `deliveryVersion: 2`, this section
|
|
13
|
+
overrides the V1 release material below. The normal flow has no SDD, Git clean,
|
|
14
|
+
mainline ancestry, explicit change id, `--only`, candidate, or ship gate:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
openxiangda check --environment preproduction
|
|
18
|
+
openxiangda deploy preproduction --json
|
|
19
|
+
openxiangda deploy production --package <packageDigest> --json
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Use `openxiangda status <runId>` and `openxiangda retry <runId>` for a failed or
|
|
23
|
+
interrupted run. Use `openxiangda rollback <environment> --to <appReleaseId>`
|
|
24
|
+
for rollback. Never ask the AI to choose or sequence low-level
|
|
25
|
+
`workspace/resource/runtime/app-finalize` commands for a V2 release.
|
|
26
|
+
|
|
10
27
|
## Resolve the boundary
|
|
11
28
|
|
|
12
29
|
Before a write or release, establish all four values:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openxiangda",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.232",
|
|
4
4
|
"description": "OpenXiangda CLI, workspace build tools, runtime SDK, and form components.",
|
|
5
5
|
"private": false,
|
|
6
6
|
"bin": {
|
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
"source:admin-list": "node scripts/sync-admin-list-source.mjs",
|
|
69
69
|
"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",
|
|
70
70
|
"test": "npm run test:fast",
|
|
71
|
-
"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 && node scripts/environment-policy-cli-smoke.mjs",
|
|
71
|
+
"test:fast": "npm run check && node scripts/delivery-v2-package-smoke.mjs && node scripts/delivery-v2-executor-smoke.mjs && 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 && node scripts/environment-policy-cli-smoke.mjs",
|
|
72
72
|
"test:changed": "npm run check && node scripts/run-test-suite.mjs --changed --evidence",
|
|
73
73
|
"test:contract": "npm run check && node scripts/run-test-suite.mjs --contract --evidence",
|
|
74
74
|
"test:release": "npm run check && node scripts/run-test-suite.mjs --release --evidence",
|
|
@@ -108,6 +108,8 @@
|
|
|
108
108
|
"test:release-telemetry": "node scripts/release-telemetry-smoke.mjs",
|
|
109
109
|
"test:release-explain": "node scripts/release-explain-smoke.mjs",
|
|
110
110
|
"test:release-error-classification": "node scripts/release-error-classification-smoke.mjs",
|
|
111
|
+
"test:delivery-v2-package": "node scripts/delivery-v2-package-smoke.mjs",
|
|
112
|
+
"test:delivery-v2-executor": "node scripts/delivery-v2-executor-smoke.mjs",
|
|
111
113
|
"test:task-status": "node scripts/task-status-smoke.mjs",
|
|
112
114
|
"test:policy": "node scripts/policy-smoke.mjs",
|
|
113
115
|
"test:release-plan": "node scripts/release-plan-smoke.mjs",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/build/index.ts"],"sourcesContent":["export type CssIsolation = 'namespace' | 'shadow' | 'none';\nexport type WorkspaceRuntimeMode = 'legacy' | 'react-spa';\n\nexport interface AppWorkspaceConfig {\n appType?: string;\n appName?: string;\n runtimeMode?: WorkspaceRuntimeMode;\n platformUrl?: string;\n servicePrefix?: string;\n appKey?: string;\n appSecret?: string;\n userId?: string;\n version?: string;\n buildId?: string;\n oss?: {\n region?: string;\n bucket?: string;\n accessKeyId?: string;\n accessKeySecret?: string;\n pathPrefix?: string;\n corsOrigins?: string[];\n skipCors?: boolean;\n };\n defaults?: {\n protocolVersion?: string;\n frameworkVersion?: string;\n cssIsolation?: CssIsolation;\n formMenuParentId?: string;\n formMenuIcon?: string;\n pageMenuParentId?: string;\n pageMenuIcon?: string;\n formBuilderVersion?: string;\n };\n forms?: { dir?: string; publishLegacyBundle?: boolean };\n pages?: { dir?: string };\n governance?: {\n sdd?: {\n enabled?: boolean;\n strictHighRisk?: boolean;\n strictDocumentation?: boolean;\n path?: string;\n };\n };\n}\n\nexport function defineAppWorkspaceConfig<T extends AppWorkspaceConfig>(config: T): T {\n return config;\n}\n\nconst DEFAULT_NAMESPACE_PREFIX = '.sy-app-workspace';\n\nfunction splitCssSelectors(selector: string) {\n const selectors: string[] = [];\n let depth = 0;\n let quote = '';\n let current = '';\n for (const char of selector) {\n if (quote) {\n current += char;\n if (char === quote) quote = '';\n continue;\n }\n if (char === '\"' || char === \"'\") {\n quote = char;\n current += char;\n continue;\n }\n if (char === '(' || char === '[') depth += 1;\n if (char === ')' || char === ']') depth -= 1;\n if (char === ',' && depth === 0) {\n selectors.push(current.trim());\n current = '';\n continue;\n }\n current += char;\n }\n if (current.trim()) selectors.push(current.trim());\n return selectors;\n}\n\nfunction shouldSkipNamespace(selector: string, prefix: string) {\n if (!selector || selector.includes(prefix)) return true;\n if (/^(html|body|:root)(\\b|:|\\[|$)/.test(selector)) return true;\n if (/^(@|from\\b|to\\b|\\d+%)/.test(selector)) return true;\n if (/^\\.(ant|sy-ant|anticon|adm)-/.test(selector)) return true;\n return false;\n}\n\nfunction createAntdSelectorAlias(selector: string) {\n const aliased = selector\n .replace(/(^|[^A-Za-z0-9_-])\\.ant-/g, '$1.sy-ant-')\n .replace(/(^|[^A-Za-z0-9_-])\\.anticon-/g, '$1.sy-anticon-')\n .replace(/(^|[^A-Za-z0-9_-])\\.anticon(?=$|[^A-Za-z0-9_-])/g, '$1.sy-anticon');\n return aliased === selector ? null : aliased;\n}\n\nexport function createOpenXiangdaNamespaceCssPlugin(\n prefix = DEFAULT_NAMESPACE_PREFIX,\n) {\n return {\n postcssPlugin: 'openxiangda-namespace-css',\n Rule(rule: { selector?: string; parent?: any }) {\n if (!rule.selector) return;\n let parent = rule.parent;\n while (parent) {\n if (parent.type === 'atrule' && /keyframes$/i.test(parent.name)) {\n return;\n }\n parent = parent.parent;\n }\n rule.selector = Array.from(\n new Set(\n splitCssSelectors(rule.selector).flatMap((selector) => {\n const scopedSelector = shouldSkipNamespace(selector, prefix)\n ? selector\n : `${prefix} ${selector}`;\n const antdAlias = createAntdSelectorAlias(scopedSelector);\n return antdAlias ? [scopedSelector, antdAlias] : [scopedSelector];\n }),\n ),\n ).join(', ');\n },\n };\n}\n\n(createOpenXiangdaNamespaceCssPlugin as any).postcss = true;\n\nexport const createNamespaceCssPlugin = createOpenXiangdaNamespaceCssPlugin;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;
|
|
1
|
+
{"version":3,"sources":["../../src/build/index.ts"],"sourcesContent":["export type CssIsolation = 'namespace' | 'shadow' | 'none';\nexport type WorkspaceRuntimeMode = 'legacy' | 'react-spa';\n\nexport interface AppWorkspaceConfig {\n deliveryVersion?: 2;\n appType?: string;\n appName?: string;\n runtimeMode?: WorkspaceRuntimeMode;\n platformUrl?: string;\n servicePrefix?: string;\n appKey?: string;\n appSecret?: string;\n userId?: string;\n version?: string;\n buildId?: string;\n oss?: {\n region?: string;\n bucket?: string;\n accessKeyId?: string;\n accessKeySecret?: string;\n pathPrefix?: string;\n corsOrigins?: string[];\n skipCors?: boolean;\n };\n defaults?: {\n protocolVersion?: string;\n frameworkVersion?: string;\n cssIsolation?: CssIsolation;\n formMenuParentId?: string;\n formMenuIcon?: string;\n pageMenuParentId?: string;\n pageMenuIcon?: string;\n formBuilderVersion?: string;\n };\n forms?: { dir?: string; publishLegacyBundle?: boolean };\n pages?: { dir?: string };\n governance?: {\n sdd?: {\n enabled?: boolean;\n strictHighRisk?: boolean;\n strictDocumentation?: boolean;\n path?: string;\n };\n };\n}\n\nexport function defineAppWorkspaceConfig<T extends AppWorkspaceConfig>(config: T): T {\n return config;\n}\n\nconst DEFAULT_NAMESPACE_PREFIX = '.sy-app-workspace';\n\nfunction splitCssSelectors(selector: string) {\n const selectors: string[] = [];\n let depth = 0;\n let quote = '';\n let current = '';\n for (const char of selector) {\n if (quote) {\n current += char;\n if (char === quote) quote = '';\n continue;\n }\n if (char === '\"' || char === \"'\") {\n quote = char;\n current += char;\n continue;\n }\n if (char === '(' || char === '[') depth += 1;\n if (char === ')' || char === ']') depth -= 1;\n if (char === ',' && depth === 0) {\n selectors.push(current.trim());\n current = '';\n continue;\n }\n current += char;\n }\n if (current.trim()) selectors.push(current.trim());\n return selectors;\n}\n\nfunction shouldSkipNamespace(selector: string, prefix: string) {\n if (!selector || selector.includes(prefix)) return true;\n if (/^(html|body|:root)(\\b|:|\\[|$)/.test(selector)) return true;\n if (/^(@|from\\b|to\\b|\\d+%)/.test(selector)) return true;\n if (/^\\.(ant|sy-ant|anticon|adm)-/.test(selector)) return true;\n return false;\n}\n\nfunction createAntdSelectorAlias(selector: string) {\n const aliased = selector\n .replace(/(^|[^A-Za-z0-9_-])\\.ant-/g, '$1.sy-ant-')\n .replace(/(^|[^A-Za-z0-9_-])\\.anticon-/g, '$1.sy-anticon-')\n .replace(/(^|[^A-Za-z0-9_-])\\.anticon(?=$|[^A-Za-z0-9_-])/g, '$1.sy-anticon');\n return aliased === selector ? null : aliased;\n}\n\nexport function createOpenXiangdaNamespaceCssPlugin(\n prefix = DEFAULT_NAMESPACE_PREFIX,\n) {\n return {\n postcssPlugin: 'openxiangda-namespace-css',\n Rule(rule: { selector?: string; parent?: any }) {\n if (!rule.selector) return;\n let parent = rule.parent;\n while (parent) {\n if (parent.type === 'atrule' && /keyframes$/i.test(parent.name)) {\n return;\n }\n parent = parent.parent;\n }\n rule.selector = Array.from(\n new Set(\n splitCssSelectors(rule.selector).flatMap((selector) => {\n const scopedSelector = shouldSkipNamespace(selector, prefix)\n ? selector\n : `${prefix} ${selector}`;\n const antdAlias = createAntdSelectorAlias(scopedSelector);\n return antdAlias ? [scopedSelector, antdAlias] : [scopedSelector];\n }),\n ),\n ).join(', ');\n },\n };\n}\n\n(createOpenXiangdaNamespaceCssPlugin as any).postcss = true;\n\nexport const createNamespaceCssPlugin = createOpenXiangdaNamespaceCssPlugin;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA8CO,SAAS,yBAAuD,QAAc;AACnF,SAAO;AACT;AAEA,IAAM,2BAA2B;AAEjC,SAAS,kBAAkB,UAAkB;AAC3C,QAAM,YAAsB,CAAC;AAC7B,MAAI,QAAQ;AACZ,MAAI,QAAQ;AACZ,MAAI,UAAU;AACd,aAAW,QAAQ,UAAU;AAC3B,QAAI,OAAO;AACT,iBAAW;AACX,UAAI,SAAS,MAAO,SAAQ;AAC5B;AAAA,IACF;AACA,QAAI,SAAS,OAAO,SAAS,KAAK;AAChC,cAAQ;AACR,iBAAW;AACX;AAAA,IACF;AACA,QAAI,SAAS,OAAO,SAAS,IAAK,UAAS;AAC3C,QAAI,SAAS,OAAO,SAAS,IAAK,UAAS;AAC3C,QAAI,SAAS,OAAO,UAAU,GAAG;AAC/B,gBAAU,KAAK,QAAQ,KAAK,CAAC;AAC7B,gBAAU;AACV;AAAA,IACF;AACA,eAAW;AAAA,EACb;AACA,MAAI,QAAQ,KAAK,EAAG,WAAU,KAAK,QAAQ,KAAK,CAAC;AACjD,SAAO;AACT;AAEA,SAAS,oBAAoB,UAAkB,QAAgB;AAC7D,MAAI,CAAC,YAAY,SAAS,SAAS,MAAM,EAAG,QAAO;AACnD,MAAI,gCAAgC,KAAK,QAAQ,EAAG,QAAO;AAC3D,MAAI,wBAAwB,KAAK,QAAQ,EAAG,QAAO;AACnD,MAAI,+BAA+B,KAAK,QAAQ,EAAG,QAAO;AAC1D,SAAO;AACT;AAEA,SAAS,wBAAwB,UAAkB;AACjD,QAAM,UAAU,SACb,QAAQ,6BAA6B,YAAY,EACjD,QAAQ,iCAAiC,gBAAgB,EACzD,QAAQ,oDAAoD,eAAe;AAC9E,SAAO,YAAY,WAAW,OAAO;AACvC;AAEO,SAAS,oCACd,SAAS,0BACT;AACA,SAAO;AAAA,IACL,eAAe;AAAA,IACf,KAAK,MAA2C;AAC9C,UAAI,CAAC,KAAK,SAAU;AACpB,UAAI,SAAS,KAAK;AAClB,aAAO,QAAQ;AACb,YAAI,OAAO,SAAS,YAAY,cAAc,KAAK,OAAO,IAAI,GAAG;AAC/D;AAAA,QACF;AACA,iBAAS,OAAO;AAAA,MAClB;AACA,WAAK,WAAW,MAAM;AAAA,QACpB,IAAI;AAAA,UACF,kBAAkB,KAAK,QAAQ,EAAE,QAAQ,CAAC,aAAa;AACrD,kBAAM,iBAAiB,oBAAoB,UAAU,MAAM,IACvD,WACA,GAAG,MAAM,IAAI,QAAQ;AACzB,kBAAM,YAAY,wBAAwB,cAAc;AACxD,mBAAO,YAAY,CAAC,gBAAgB,SAAS,IAAI,CAAC,cAAc;AAAA,UAClE,CAAC;AAAA,QACH;AAAA,MACF,EAAE,KAAK,IAAI;AAAA,IACb;AAAA,EACF;AACF;AAEC,oCAA4C,UAAU;AAEhD,IAAM,2BAA2B;","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/build/index.ts"],"sourcesContent":["export type CssIsolation = 'namespace' | 'shadow' | 'none';\nexport type WorkspaceRuntimeMode = 'legacy' | 'react-spa';\n\nexport interface AppWorkspaceConfig {\n appType?: string;\n appName?: string;\n runtimeMode?: WorkspaceRuntimeMode;\n platformUrl?: string;\n servicePrefix?: string;\n appKey?: string;\n appSecret?: string;\n userId?: string;\n version?: string;\n buildId?: string;\n oss?: {\n region?: string;\n bucket?: string;\n accessKeyId?: string;\n accessKeySecret?: string;\n pathPrefix?: string;\n corsOrigins?: string[];\n skipCors?: boolean;\n };\n defaults?: {\n protocolVersion?: string;\n frameworkVersion?: string;\n cssIsolation?: CssIsolation;\n formMenuParentId?: string;\n formMenuIcon?: string;\n pageMenuParentId?: string;\n pageMenuIcon?: string;\n formBuilderVersion?: string;\n };\n forms?: { dir?: string; publishLegacyBundle?: boolean };\n pages?: { dir?: string };\n governance?: {\n sdd?: {\n enabled?: boolean;\n strictHighRisk?: boolean;\n strictDocumentation?: boolean;\n path?: string;\n };\n };\n}\n\nexport function defineAppWorkspaceConfig<T extends AppWorkspaceConfig>(config: T): T {\n return config;\n}\n\nconst DEFAULT_NAMESPACE_PREFIX = '.sy-app-workspace';\n\nfunction splitCssSelectors(selector: string) {\n const selectors: string[] = [];\n let depth = 0;\n let quote = '';\n let current = '';\n for (const char of selector) {\n if (quote) {\n current += char;\n if (char === quote) quote = '';\n continue;\n }\n if (char === '\"' || char === \"'\") {\n quote = char;\n current += char;\n continue;\n }\n if (char === '(' || char === '[') depth += 1;\n if (char === ')' || char === ']') depth -= 1;\n if (char === ',' && depth === 0) {\n selectors.push(current.trim());\n current = '';\n continue;\n }\n current += char;\n }\n if (current.trim()) selectors.push(current.trim());\n return selectors;\n}\n\nfunction shouldSkipNamespace(selector: string, prefix: string) {\n if (!selector || selector.includes(prefix)) return true;\n if (/^(html|body|:root)(\\b|:|\\[|$)/.test(selector)) return true;\n if (/^(@|from\\b|to\\b|\\d+%)/.test(selector)) return true;\n if (/^\\.(ant|sy-ant|anticon|adm)-/.test(selector)) return true;\n return false;\n}\n\nfunction createAntdSelectorAlias(selector: string) {\n const aliased = selector\n .replace(/(^|[^A-Za-z0-9_-])\\.ant-/g, '$1.sy-ant-')\n .replace(/(^|[^A-Za-z0-9_-])\\.anticon-/g, '$1.sy-anticon-')\n .replace(/(^|[^A-Za-z0-9_-])\\.anticon(?=$|[^A-Za-z0-9_-])/g, '$1.sy-anticon');\n return aliased === selector ? null : aliased;\n}\n\nexport function createOpenXiangdaNamespaceCssPlugin(\n prefix = DEFAULT_NAMESPACE_PREFIX,\n) {\n return {\n postcssPlugin: 'openxiangda-namespace-css',\n Rule(rule: { selector?: string; parent?: any }) {\n if (!rule.selector) return;\n let parent = rule.parent;\n while (parent) {\n if (parent.type === 'atrule' && /keyframes$/i.test(parent.name)) {\n return;\n }\n parent = parent.parent;\n }\n rule.selector = Array.from(\n new Set(\n splitCssSelectors(rule.selector).flatMap((selector) => {\n const scopedSelector = shouldSkipNamespace(selector, prefix)\n ? selector\n : `${prefix} ${selector}`;\n const antdAlias = createAntdSelectorAlias(scopedSelector);\n return antdAlias ? [scopedSelector, antdAlias] : [scopedSelector];\n }),\n ),\n ).join(', ');\n },\n };\n}\n\n(createOpenXiangdaNamespaceCssPlugin as any).postcss = true;\n\nexport const createNamespaceCssPlugin = createOpenXiangdaNamespaceCssPlugin;\n"],"mappings":";
|
|
1
|
+
{"version":3,"sources":["../../src/build/index.ts"],"sourcesContent":["export type CssIsolation = 'namespace' | 'shadow' | 'none';\nexport type WorkspaceRuntimeMode = 'legacy' | 'react-spa';\n\nexport interface AppWorkspaceConfig {\n deliveryVersion?: 2;\n appType?: string;\n appName?: string;\n runtimeMode?: WorkspaceRuntimeMode;\n platformUrl?: string;\n servicePrefix?: string;\n appKey?: string;\n appSecret?: string;\n userId?: string;\n version?: string;\n buildId?: string;\n oss?: {\n region?: string;\n bucket?: string;\n accessKeyId?: string;\n accessKeySecret?: string;\n pathPrefix?: string;\n corsOrigins?: string[];\n skipCors?: boolean;\n };\n defaults?: {\n protocolVersion?: string;\n frameworkVersion?: string;\n cssIsolation?: CssIsolation;\n formMenuParentId?: string;\n formMenuIcon?: string;\n pageMenuParentId?: string;\n pageMenuIcon?: string;\n formBuilderVersion?: string;\n };\n forms?: { dir?: string; publishLegacyBundle?: boolean };\n pages?: { dir?: string };\n governance?: {\n sdd?: {\n enabled?: boolean;\n strictHighRisk?: boolean;\n strictDocumentation?: boolean;\n path?: string;\n };\n };\n}\n\nexport function defineAppWorkspaceConfig<T extends AppWorkspaceConfig>(config: T): T {\n return config;\n}\n\nconst DEFAULT_NAMESPACE_PREFIX = '.sy-app-workspace';\n\nfunction splitCssSelectors(selector: string) {\n const selectors: string[] = [];\n let depth = 0;\n let quote = '';\n let current = '';\n for (const char of selector) {\n if (quote) {\n current += char;\n if (char === quote) quote = '';\n continue;\n }\n if (char === '\"' || char === \"'\") {\n quote = char;\n current += char;\n continue;\n }\n if (char === '(' || char === '[') depth += 1;\n if (char === ')' || char === ']') depth -= 1;\n if (char === ',' && depth === 0) {\n selectors.push(current.trim());\n current = '';\n continue;\n }\n current += char;\n }\n if (current.trim()) selectors.push(current.trim());\n return selectors;\n}\n\nfunction shouldSkipNamespace(selector: string, prefix: string) {\n if (!selector || selector.includes(prefix)) return true;\n if (/^(html|body|:root)(\\b|:|\\[|$)/.test(selector)) return true;\n if (/^(@|from\\b|to\\b|\\d+%)/.test(selector)) return true;\n if (/^\\.(ant|sy-ant|anticon|adm)-/.test(selector)) return true;\n return false;\n}\n\nfunction createAntdSelectorAlias(selector: string) {\n const aliased = selector\n .replace(/(^|[^A-Za-z0-9_-])\\.ant-/g, '$1.sy-ant-')\n .replace(/(^|[^A-Za-z0-9_-])\\.anticon-/g, '$1.sy-anticon-')\n .replace(/(^|[^A-Za-z0-9_-])\\.anticon(?=$|[^A-Za-z0-9_-])/g, '$1.sy-anticon');\n return aliased === selector ? null : aliased;\n}\n\nexport function createOpenXiangdaNamespaceCssPlugin(\n prefix = DEFAULT_NAMESPACE_PREFIX,\n) {\n return {\n postcssPlugin: 'openxiangda-namespace-css',\n Rule(rule: { selector?: string; parent?: any }) {\n if (!rule.selector) return;\n let parent = rule.parent;\n while (parent) {\n if (parent.type === 'atrule' && /keyframes$/i.test(parent.name)) {\n return;\n }\n parent = parent.parent;\n }\n rule.selector = Array.from(\n new Set(\n splitCssSelectors(rule.selector).flatMap((selector) => {\n const scopedSelector = shouldSkipNamespace(selector, prefix)\n ? selector\n : `${prefix} ${selector}`;\n const antdAlias = createAntdSelectorAlias(scopedSelector);\n return antdAlias ? [scopedSelector, antdAlias] : [scopedSelector];\n }),\n ),\n ).join(', ');\n },\n };\n}\n\n(createOpenXiangdaNamespaceCssPlugin as any).postcss = true;\n\nexport const createNamespaceCssPlugin = createOpenXiangdaNamespaceCssPlugin;\n"],"mappings":";AA8CO,SAAS,yBAAuD,QAAc;AACnF,SAAO;AACT;AAEA,IAAM,2BAA2B;AAEjC,SAAS,kBAAkB,UAAkB;AAC3C,QAAM,YAAsB,CAAC;AAC7B,MAAI,QAAQ;AACZ,MAAI,QAAQ;AACZ,MAAI,UAAU;AACd,aAAW,QAAQ,UAAU;AAC3B,QAAI,OAAO;AACT,iBAAW;AACX,UAAI,SAAS,MAAO,SAAQ;AAC5B;AAAA,IACF;AACA,QAAI,SAAS,OAAO,SAAS,KAAK;AAChC,cAAQ;AACR,iBAAW;AACX;AAAA,IACF;AACA,QAAI,SAAS,OAAO,SAAS,IAAK,UAAS;AAC3C,QAAI,SAAS,OAAO,SAAS,IAAK,UAAS;AAC3C,QAAI,SAAS,OAAO,UAAU,GAAG;AAC/B,gBAAU,KAAK,QAAQ,KAAK,CAAC;AAC7B,gBAAU;AACV;AAAA,IACF;AACA,eAAW;AAAA,EACb;AACA,MAAI,QAAQ,KAAK,EAAG,WAAU,KAAK,QAAQ,KAAK,CAAC;AACjD,SAAO;AACT;AAEA,SAAS,oBAAoB,UAAkB,QAAgB;AAC7D,MAAI,CAAC,YAAY,SAAS,SAAS,MAAM,EAAG,QAAO;AACnD,MAAI,gCAAgC,KAAK,QAAQ,EAAG,QAAO;AAC3D,MAAI,wBAAwB,KAAK,QAAQ,EAAG,QAAO;AACnD,MAAI,+BAA+B,KAAK,QAAQ,EAAG,QAAO;AAC1D,SAAO;AACT;AAEA,SAAS,wBAAwB,UAAkB;AACjD,QAAM,UAAU,SACb,QAAQ,6BAA6B,YAAY,EACjD,QAAQ,iCAAiC,gBAAgB,EACzD,QAAQ,oDAAoD,eAAe;AAC9E,SAAO,YAAY,WAAW,OAAO;AACvC;AAEO,SAAS,oCACd,SAAS,0BACT;AACA,SAAO;AAAA,IACL,eAAe;AAAA,IACf,KAAK,MAA2C;AAC9C,UAAI,CAAC,KAAK,SAAU;AACpB,UAAI,SAAS,KAAK;AAClB,aAAO,QAAQ;AACb,YAAI,OAAO,SAAS,YAAY,cAAc,KAAK,OAAO,IAAI,GAAG;AAC/D;AAAA,QACF;AACA,iBAAS,OAAO;AAAA,MAClB;AACA,WAAK,WAAW,MAAM;AAAA,QACpB,IAAI;AAAA,UACF,kBAAkB,KAAK,QAAQ,EAAE,QAAQ,CAAC,aAAa;AACrD,kBAAM,iBAAiB,oBAAoB,UAAU,MAAM,IACvD,WACA,GAAG,MAAM,IAAI,QAAQ;AACzB,kBAAM,YAAY,wBAAwB,cAAc;AACxD,mBAAO,YAAY,CAAC,gBAAgB,SAAS,IAAI,CAAC,cAAc;AAAA,UAClE,CAAC;AAAA,QACH;AAAA,MACF,EAAE,KAAK,IAAI;AAAA,IACb;AAAA,EACF;AACF;AAEC,oCAA4C,UAAU;AAEhD,IAAM,2BAA2B;","names":[]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": "openxiangda_engineering_policy_v1",
|
|
3
|
-
"policyVersion":
|
|
3
|
+
"policyVersion": 5,
|
|
4
4
|
"risk": {
|
|
5
5
|
"levels": ["L0", "L1", "L2", "L3"],
|
|
6
6
|
"quickMaxLevel": "L1",
|
|
@@ -28,6 +28,13 @@
|
|
|
28
28
|
"requireRemoteMainlineContainment": true
|
|
29
29
|
},
|
|
30
30
|
"release": {
|
|
31
|
+
"normalDeliveryVersion": 2,
|
|
32
|
+
"trustedDeveloperMode": true,
|
|
33
|
+
"normalCommands": ["check", "deploy", "status", "retry", "rollback"],
|
|
34
|
+
"productionConsumesSealedPackage": true,
|
|
35
|
+
"serverPersistedReleaseRun": true,
|
|
36
|
+
"v2RequiresSdd": false,
|
|
37
|
+
"v2RequiresGitMainline": false,
|
|
31
38
|
"planAndVerifyAreReadOnly": true,
|
|
32
39
|
"applicationEnvironmentMode": "candidate-preproduction-evidence-production",
|
|
33
40
|
"candidateImmutable": true,
|
|
@@ -5,21 +5,22 @@ alwaysApply: true
|
|
|
5
5
|
|
|
6
6
|
# OpenXiangda React SPA Rule
|
|
7
7
|
|
|
8
|
-
This is an OpenXiangda React SPA workspace. See [AGENTS.md](mdc:AGENTS.md)
|
|
8
|
+
This is an OpenXiangda React SPA workspace using Delivery V2. See [DELIVERY.md](mdc:DELIVERY.md) and [AGENTS.md](mdc:AGENTS.md). Low-level V1 resource/runtime commands are compatibility tools.
|
|
9
9
|
|
|
10
10
|
## Hard route
|
|
11
11
|
|
|
12
12
|
| User says | Command |
|
|
13
13
|
|---|---|
|
|
14
|
-
| publish
|
|
15
|
-
|
|
|
16
|
-
|
|
|
17
|
-
|
|
|
14
|
+
| publish / deploy | `openxiangda check --environment preproduction` → `openxiangda deploy preproduction` |
|
|
15
|
+
| formal promotion | `openxiangda deploy production --package <packageDigest>` |
|
|
16
|
+
| status / retry | `openxiangda status <runId>` → `openxiangda retry <runId>` |
|
|
17
|
+
| rollback | `openxiangda rollback production --to <appReleaseId>` |
|
|
18
18
|
| diagnose | `openxiangda doctor --profile <name> --json` |
|
|
19
19
|
| external backend Open API | `openxiangda-open-api` + `openxiangda open-api spec describe ...` |
|
|
20
20
|
|
|
21
21
|
## Always
|
|
22
22
|
|
|
23
|
+
- Delivery V2 derives exact scope and does not require SDD, Git clean, `--change`, `--only`, candidate, or ship.
|
|
23
24
|
- Writes and deploys must pass `--profile <name>` explicitly.
|
|
24
25
|
- Architecture-class work is plan-gated: run `openxiangda design gates --topic <code> --json` and wait for confirmation.
|
|
25
26
|
- Give every task an isolated worktree/branch and one explicit development change; feature worktrees never publish. Merge approved commits to the remote default branch, create one `sdd bundle <release-change> --changes ...`, commit/push it, then publish once from the synchronized clean main/master checkout.
|
|
@@ -5,22 +5,22 @@ alwaysApply: true
|
|
|
5
5
|
|
|
6
6
|
# OpenXiangda React SPA Rule
|
|
7
7
|
|
|
8
|
-
This is an OpenXiangda React SPA workspace. Read [
|
|
8
|
+
This is an OpenXiangda React SPA workspace using Delivery V2. Read [DELIVERY.md](DELIVERY.md) and [AGENTS.md](AGENTS.md). Low-level V1 resource/runtime commands are compatibility tools, not the normal release entrypoint.
|
|
9
9
|
|
|
10
10
|
## Hard route
|
|
11
11
|
|
|
12
12
|
| 用户说 | 必用命令 |
|
|
13
13
|
|---|---|
|
|
14
|
-
|
|
|
15
|
-
|
|
|
16
|
-
|
|
|
17
|
-
|
|
|
18
|
-
| 资源计划 / diff | `openxiangda resource plan <type> --only <codes> --profile <name>` |
|
|
14
|
+
| 发布 / 完整部署 | `openxiangda check --environment preproduction` → `openxiangda deploy preproduction` |
|
|
15
|
+
| 正式 promotion | `openxiangda deploy production --package <packageDigest>` |
|
|
16
|
+
| 状态 / 失败重试 | `openxiangda status <runId>` → `openxiangda retry <runId>` |
|
|
17
|
+
| 回滚 | `openxiangda rollback production --to <appReleaseId>` |
|
|
19
18
|
| 诊断 / 环境 | `openxiangda doctor --profile <name> --json` / `openxiangda env --profile <name>` |
|
|
20
19
|
| 外部后端 / 三方系统开放接口 | `openxiangda-open-api` + `openxiangda open-api spec describe ...` |
|
|
21
20
|
|
|
22
21
|
## Always
|
|
23
22
|
|
|
23
|
+
- Delivery V2 自动计算精确范围,不要求 SDD、Git clean、`--change`、`--only` 或 candidate/ship。
|
|
24
24
|
- 发布和写平台资源必须显式传 `--profile <name>`。
|
|
25
25
|
- 架构类需求先跑 `openxiangda design gates --topic <code> --json` 并等用户确认。
|
|
26
26
|
- 每个任务使用独立 worktree/branch 和一个开发 change,但 feature worktree 不发布。批准提交先合并并 push 到远端默认主分支,再创建一个 `sdd bundle <release-change> --changes ...`,从同步且干净的 main/master 一次发布。
|
|
@@ -1,8 +1,12 @@
|
|
|
1
|
-
<!-- OpenXiangda-Policy-Version:
|
|
1
|
+
<!-- OpenXiangda-Policy-Version: 5 -->
|
|
2
2
|
# AGENTS.md — OpenXiangda React SPA 应用工作区
|
|
3
3
|
|
|
4
4
|
本工作区是标准 React 18 + Vite + React Router 应用。默认模板只提供应用壳、账号菜单和一个首页,不是开发验证控制台。
|
|
5
5
|
|
|
6
|
+
## Delivery V2(发布唯一入口)
|
|
7
|
+
|
|
8
|
+
本工作区声明 `deliveryVersion: 2`,正常发布只使用 `openxiangda check`、`openxiangda deploy`、`openxiangda status`、`openxiangda retry`、`openxiangda rollback`,完整约定见 [DELIVERY.md](DELIVERY.md)。V2 自动计算资源范围并在服务端持久化 ReleaseRun,不要求 SDD、Git clean、主线 ancestry、`--change`、`--only` 或生产确认参数。本文后续出现的 `resource publish`、`runtime deploy`、`release publish/ship`、candidate、SDD/mainline 发布门禁均为 V1 底层兼容说明,不得用于 V2 正常发布。
|
|
9
|
+
|
|
6
10
|
## 开发原则
|
|
7
11
|
|
|
8
12
|
- 架构类需求先运行 `openxiangda doctor --json` 和 `openxiangda design gates --topic <code> --json`。只有仍存在会改变实现方向的业务、安全或数据选择时才输出设计并等待确认;用户已经给出具体需求与验收标准时,直接记录结构化 SDD 范围并实现,不再写长篇设计或重复确认。
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# OpenXiangda Delivery V2
|
|
2
|
+
|
|
3
|
+
This workspace uses `deliveryVersion: 2`. Delivery V2 is desired-state based,
|
|
4
|
+
content-addressed, resumable, and independent from Git/SDD cleanliness.
|
|
5
|
+
|
|
6
|
+
Normal release commands are deliberately limited to:
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
openxiangda check --environment preproduction
|
|
10
|
+
openxiangda deploy preproduction --json
|
|
11
|
+
openxiangda deploy production --package <packageDigest> --json
|
|
12
|
+
openxiangda status <runId> --json
|
|
13
|
+
openxiangda retry <runId> --json
|
|
14
|
+
openxiangda rollback production --to <appReleaseId> --json
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Rules:
|
|
18
|
+
|
|
19
|
+
- Do not assemble a release by choosing low-level `workspace publish`,
|
|
20
|
+
`resource publish`, `runtime deploy`, `release publish`, `release ship`, or
|
|
21
|
+
`app-finalize` commands. Delivery V2 owns that orchestration.
|
|
22
|
+
- Do not add `--only`, `--change`, SDD bypass flags, Git-clean workarounds, or
|
|
23
|
+
production-confirmation flags. The package compiler derives the exact delta.
|
|
24
|
+
- Preproduction compiles and uploads one immutable App Package. Production
|
|
25
|
+
deploys that exact `packageDigest` without rebuilding.
|
|
26
|
+
- On failure, keep the `runId`. Read its compact structured status and use
|
|
27
|
+
`retry`; successful checkpoints are preserved.
|
|
28
|
+
- Project `.env`, credentials, private keys, `.git`, `node_modules`, `dist`,
|
|
29
|
+
and generated `.openxiangda` state are never included in the authored source
|
|
30
|
+
layer. Runtime output is a separate immutable layer.
|
|
31
|
+
|
|
32
|
+
Low-level V1 commands remain installed only for diagnostics and compatibility
|
|
33
|
+
with old workspaces that do not declare `deliveryVersion: 2`.
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { defineAppWorkspaceConfig } from "openxiangda/build";
|
|
2
2
|
|
|
3
3
|
export default defineAppWorkspaceConfig({
|
|
4
|
+
deliveryVersion: 2,
|
|
4
5
|
appType: process.env.APP_TYPE || process.env.OPENXIANGDA_APP_TYPE || "APP_XXXXXXXXXXXXXXXX",
|
|
5
6
|
appName: process.env.APP_NAME || "OpenXiangda React SPA",
|
|
6
7
|
runtimeMode: "react-spa",
|
|
@@ -38,9 +39,8 @@ export default defineAppWorkspaceConfig({
|
|
|
38
39
|
},
|
|
39
40
|
governance: {
|
|
40
41
|
sdd: {
|
|
41
|
-
enabled:
|
|
42
|
-
strictHighRisk:
|
|
43
|
-
process.env.OPENXIANGDA_SDD_STRICT_HIGH_RISK === "false" ? false : true,
|
|
42
|
+
enabled: false,
|
|
43
|
+
strictHighRisk: false,
|
|
44
44
|
strictDocumentation: false,
|
|
45
45
|
documentationMode: 'structured',
|
|
46
46
|
path: process.env.OPENXIANGDA_SDD_PATH || "openspec",
|
|
@@ -11,7 +11,9 @@ This is a `sy-lowcode-app-workspace` managed by the `openxiangda` CLI. See [AGEN
|
|
|
11
11
|
|
|
12
12
|
| User says | Skill | Command |
|
|
13
13
|
|---|---|---|
|
|
14
|
-
| publish / deploy / 发布 / 上线 / 部署 | `openxiangda-core` | `openxiangda
|
|
14
|
+
| publish / deploy / 发布 / 上线 / 部署 | `openxiangda-core` | `openxiangda check --environment preproduction` → `openxiangda deploy preproduction` |
|
|
15
|
+
| production promotion | `openxiangda-core` | `openxiangda deploy production --package <packageDigest>` |
|
|
16
|
+
| failed delivery / retry | `openxiangda-core` | `openxiangda status <runId>` → `openxiangda retry <runId>` |
|
|
15
17
|
| create / scaffold app / 创建应用 / 初始化 | `openxiangda-app` | `openxiangda workspace init <dir> --profile <name> --app-name "..."` |
|
|
16
18
|
| bind existing app / 绑定已有应用 | `openxiangda-app` | `openxiangda workspace bind --profile <name> --app-type APP_XXX` |
|
|
17
19
|
| edit form / schema / 表单字段 / 表单页 | `openxiangda-form` | edit `src/forms/<code>/` → `workspace publish --form <code>` |
|
|
@@ -25,6 +27,7 @@ This is a `sy-lowcode-app-workspace` managed by the `openxiangda` CLI. See [AGEN
|
|
|
25
27
|
|
|
26
28
|
## Always
|
|
27
29
|
|
|
30
|
+
- For `deliveryVersion: 2`, use only Delivery V2 commands. Later V1 SDD/mainline/`--only`/candidate/ship text is compatibility guidance and must not be mixed into V2.
|
|
28
31
|
- Give every task an isolated worktree/branch and one development change; feature worktrees never publish. Merge approved commits to the remote default branch, create one `sdd bundle`, commit/push it, and publish once from synchronized clean main/master.
|
|
29
32
|
- Close tasks from canonical main only. Review `openxiangda workspace cleanup`, then run `openxiangda workspace cleanup --apply` for that exact hashed `SAFE` plan; it revalidates managed worktrees under the owner lock, never picks up newly-safe entries, and never runs global `git worktree prune`. Review stale records separately.
|
|
30
33
|
- L0 read-only/docs/tests need no SDD; L1 narrow reversible fixes record exact scope without a redundant second confirmation; schema, business Functions, Automation/Workflow, permissions, auth/public access, data writes, and runtime/config are L2/L3 full-SDD work. Live evidence/archive are post-release stages.
|
|
@@ -11,7 +11,9 @@ This is a `sy-lowcode-app-workspace` managed by the `openxiangda` CLI. Read [AGE
|
|
|
11
11
|
|
|
12
12
|
| 用户说 | 必用 skill | 必用命令 |
|
|
13
13
|
|---|---|---|
|
|
14
|
-
| 发布 / 上线 / 部署 / publish / deploy | `openxiangda-core` | `openxiangda
|
|
14
|
+
| 发布 / 上线 / 部署 / publish / deploy | `openxiangda-core` | `openxiangda check --environment preproduction` → `openxiangda deploy preproduction` |
|
|
15
|
+
| 晋级生产 | `openxiangda-core` | `openxiangda deploy production --package <packageDigest>` |
|
|
16
|
+
| 发布失败 / 重试 | `openxiangda-core` | `openxiangda status <runId>` → `openxiangda retry <runId>` |
|
|
15
17
|
| 创建 / 初始化应用 | `openxiangda-app` | `openxiangda workspace init <dir> --profile <name> --app-name "..."` |
|
|
16
18
|
| 绑定已有应用 | `openxiangda-app` | `openxiangda workspace bind --profile <name> --app-type APP_XXX` |
|
|
17
19
|
| 改表单字段 / schema / 表单页 | `openxiangda-form` | 编辑 `src/forms/<code>/` → `workspace publish --form <code>` |
|
|
@@ -25,6 +27,7 @@ This is a `sy-lowcode-app-workspace` managed by the `openxiangda` CLI. Read [AGE
|
|
|
25
27
|
|
|
26
28
|
## Always
|
|
27
29
|
|
|
30
|
+
- `deliveryVersion: 2` 的正常发布只允许 Delivery V2 命令;后续 V1 SDD/mainline/`--only`/candidate/ship 内容仅作兼容说明,不得混入 V2。
|
|
28
31
|
- 每个任务使用独立 worktree/branch 和一个开发 change,但 feature worktree 不发布。批准提交先合并并 push 到远端默认主分支,再创建一个 `sdd bundle`,从同步且干净的 main/master 一次发布。
|
|
29
32
|
- 合并、push、发布和 `release end` 后,回到 canonical 主工作区先审阅 `openxiangda workspace cleanup`,再执行 `openxiangda workspace cleanup --apply` 应用精确哈希计划。CLI 在 owner lock 内复核,只删除已审阅的 managed `SAFE` 项,不顺带处理后来才安全的项,也不执行全局 `git worktree prune`;stale 记录单独人工审阅。
|
|
30
33
|
- L0 只读/文档/测试无需 SDD;L1 窄小可逆改动记录精确范围且不重复确认;表单结构、业务函数、自动化/流程、权限、登录/公开访问、数据写入、runtime/config 等 L2/L3 走完整 SDD,live evidence/archive 放到发布后。
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<!-- OpenXiangda-Policy-Version:
|
|
1
|
+
<!-- OpenXiangda-Policy-Version: 5 -->
|
|
2
2
|
# AGENTS.md — OpenXiangda 工作区 AI 强约束
|
|
3
3
|
|
|
4
4
|
> 任何 AI(Qoder / Claude / Codex / Cursor / Copilot 等)在本工作区操作前 **必须先读完本文件**。
|
|
@@ -6,7 +6,9 @@
|
|
|
6
6
|
|
|
7
7
|
## 一句话原则
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
**本工作区声明 `deliveryVersion: 2`。所有“发布 / 上线 / 部署 / publish / deploy / ship / release”请求只使用 `openxiangda check`、`openxiangda deploy`、`openxiangda status`、`openxiangda retry`、`openxiangda rollback`,完整约定见 [DELIVERY.md](DELIVERY.md)。**
|
|
10
|
+
|
|
11
|
+
Delivery V2 自动从期望状态计算精确资源范围,封存内容寻址 App Package,并在服务端持久化 ReleaseRun 和检查点。它不要求 SDD、Git clean、主线 ancestry、`--change`、`--only` 或生产确认参数。本文后续出现的 `workspace publish`、`resource publish`、`runtime deploy`、`release publish/ship`、candidate、SDD/mainline 发布门禁均属于 V1 底层兼容说明,不得用于 V2 正常发布。
|
|
10
12
|
|
|
11
13
|
**架构类需求先过设计门。** 新应用、复杂页面、登录注册、公开访问、权限数据范围、流程自动化、连接器/通知等需求,先 `openxiangda doctor --json` + `openxiangda design gates --topic <code> --json`。只有仍存在会改变实现方向的业务、安全或数据选择时才输出设计并等待确认;用户已经给出具体需求与验收标准时,直接记录结构化 SDD 范围并实现,不再写长篇设计或重复确认。
|
|
12
14
|
|
|
@@ -24,8 +26,9 @@
|
|
|
24
26
|
|
|
25
27
|
| 用户说(中 / 英) | 用 skill | 第一条命令 |
|
|
26
28
|
|---|---|---|
|
|
27
|
-
| 发布 / 上线 / 部署 / publish / deploy | `openxiangda-core` | `openxiangda
|
|
28
|
-
|
|
|
29
|
+
| 发布 / 上线 / 部署 / publish / deploy | `openxiangda-core` | `openxiangda check --environment preproduction` → `openxiangda deploy preproduction` |
|
|
30
|
+
| 晋级生产 | `openxiangda-core` | `openxiangda deploy production --package <packageDigest>` |
|
|
31
|
+
| 发布失败 / 断点续跑 | `openxiangda-core` | `openxiangda status <runId>` → `openxiangda retry <runId>` |
|
|
29
32
|
| 创建应用 / 新建 app / 初始化工作区 | `openxiangda-app` | `openxiangda workspace init <dir> --profile <name> --app-name "..."` |
|
|
30
33
|
| 绑定已有应用 | `openxiangda-app` | `openxiangda workspace bind --profile <name> --app-type APP_XXX` |
|
|
31
34
|
| 创建 / 改表单字段、schema、表单页 | `openxiangda-form` | 编辑表单并声明 form-setting bundle → `resource publish form-setting --only <code>` 暂存;整应用由 `app-finalize` 激活 |
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# OpenXiangda Delivery V2
|
|
2
|
+
|
|
3
|
+
This workspace uses `deliveryVersion: 2`. Delivery V2 is desired-state based,
|
|
4
|
+
content-addressed, resumable, and independent from Git/SDD cleanliness.
|
|
5
|
+
|
|
6
|
+
Normal release commands are deliberately limited to:
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
openxiangda check --environment preproduction
|
|
10
|
+
openxiangda deploy preproduction --json
|
|
11
|
+
openxiangda deploy production --package <packageDigest> --json
|
|
12
|
+
openxiangda status <runId> --json
|
|
13
|
+
openxiangda retry <runId> --json
|
|
14
|
+
openxiangda rollback production --to <appReleaseId> --json
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Rules:
|
|
18
|
+
|
|
19
|
+
- Do not assemble a release by choosing low-level `workspace publish`,
|
|
20
|
+
`resource publish`, `runtime deploy`, `release publish`, `release ship`, or
|
|
21
|
+
`app-finalize` commands. Delivery V2 owns that orchestration.
|
|
22
|
+
- Do not add `--only`, `--change`, SDD bypass flags, Git-clean workarounds, or
|
|
23
|
+
production-confirmation flags. The package compiler derives the exact delta.
|
|
24
|
+
- Preproduction compiles and uploads one immutable App Package. Production
|
|
25
|
+
deploys that exact `packageDigest` without rebuilding.
|
|
26
|
+
- On failure, keep the `runId`. Read its compact structured status and use
|
|
27
|
+
`retry`; successful checkpoints are preserved.
|
|
28
|
+
- Project `.env`, credentials, private keys, `.git`, `node_modules`, `dist`,
|
|
29
|
+
and generated `.openxiangda` state are never included in the authored source
|
|
30
|
+
layer. Runtime output is a separate immutable layer.
|
|
31
|
+
|
|
32
|
+
Low-level V1 commands remain installed only for diagnostics and compatibility
|
|
33
|
+
with old workspaces that do not declare `deliveryVersion: 2`.
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { defineAppWorkspaceConfig } from "openxiangda/build";
|
|
2
2
|
|
|
3
3
|
export default defineAppWorkspaceConfig({
|
|
4
|
+
deliveryVersion: 2,
|
|
4
5
|
appType: process.env.APP_TYPE || process.env.OPENXIANGDA_APP_TYPE || "APP_XXXXXXXXXXXXXXXX",
|
|
5
6
|
appName: process.env.APP_NAME || "低代码应用",
|
|
6
7
|
platformUrl: process.env.APP_PLATFORM_URL || process.env.OPENXIANGDA_BASE_URL || "http://yida.wisejob.cn/service",
|
|
@@ -39,9 +40,8 @@ export default defineAppWorkspaceConfig({
|
|
|
39
40
|
},
|
|
40
41
|
governance: {
|
|
41
42
|
sdd: {
|
|
42
|
-
enabled:
|
|
43
|
-
strictHighRisk:
|
|
44
|
-
process.env.OPENXIANGDA_SDD_STRICT_HIGH_RISK === "false" ? false : true,
|
|
43
|
+
enabled: false,
|
|
44
|
+
strictHighRisk: false,
|
|
45
45
|
strictDocumentation: false,
|
|
46
46
|
documentationMode: 'structured',
|
|
47
47
|
path: process.env.OPENXIANGDA_SDD_PATH || "openspec",
|