openxiangda 1.0.266 → 1.0.268
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/LICENSE +21 -0
- package/README.md +12 -7
- package/lib/cli.js +30 -13
- package/lib/generation-update.js +42 -0
- package/lib/skills.js +57 -62
- package/openxiangda-skills/SKILL.md +5 -3
- package/openxiangda-skills/references/openxiangda-api.md +1 -1
- package/openxiangda-skills/references/resource-manifest-cheatsheet.md +1 -1
- package/openxiangda-skills/references/workspace-state.md +2 -2
- package/package.json +52 -3
- package/templates/openxiangda-react-spa/app-workspace.config.ts +1 -1
- package/templates/sy-lowcode-app-workspace/AGENTS.md +1 -1
- package/templates/sy-lowcode-app-workspace/app-workspace.config.ts +1 -1
- package/v2/README.md +0 -35
- package/v2/skills/manifest.json +0 -45
- package/v2/skills/openxiangda-v1-maintenance/SKILL.md +0 -21
- package/v2/skills/openxiangda-v1-maintenance/agents/openai.yaml +0 -4
- package/v2/skills/openxiangda-v2/SKILL.md +0 -38
- package/v2/skills/openxiangda-v2/agents/openai.yaml +0 -4
- package/v2/skills/openxiangda-v2-architecture/SKILL.md +0 -29
- package/v2/skills/openxiangda-v2-architecture/agents/openai.yaml +0 -4
- package/v2/skills/openxiangda-v2-backend/SKILL.md +0 -28
- package/v2/skills/openxiangda-v2-backend/agents/openai.yaml +0 -4
- package/v2/skills/openxiangda-v2-data-authz/SKILL.md +0 -28
- package/v2/skills/openxiangda-v2-data-authz/agents/openai.yaml +0 -4
- package/v2/skills/openxiangda-v2-delivery/SKILL.md +0 -30
- package/v2/skills/openxiangda-v2-delivery/agents/openai.yaml +0 -4
- package/v2/skills/openxiangda-v2-frontend/SKILL.md +0 -21
- package/v2/skills/openxiangda-v2-frontend/agents/openai.yaml +0 -4
- package/v2/skills/openxiangda-v2-workflow-events/SKILL.md +0 -28
- package/v2/skills/openxiangda-v2-workflow-events/agents/openai.yaml +0 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 OpenXiangda contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -4,18 +4,23 @@
|
|
|
4
4
|
|
|
5
5
|
OpenXiangda is a lightweight CLI and skill package for private low-code platforms.
|
|
6
6
|
|
|
7
|
-
##
|
|
7
|
+
## Repository boundary
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
This repository contains only the stable OpenXiangda 1.x maintenance toolchain.
|
|
10
|
+
It maintains existing resource-based applications and its legacy Delivery V2
|
|
11
|
+
protocol. Stable applications keep this runtime and are not migrated by a
|
|
12
|
+
platform upgrade.
|
|
10
13
|
|
|
11
|
-
|
|
12
|
-
-
|
|
13
|
-
|
|
14
|
-
|
|
14
|
+
OpenXiangda 2.0 is developed and released from the independent
|
|
15
|
+
`tools/openxiangda-v2` repository pinned by the platform orchestration
|
|
16
|
+
repository. A new 2.0 workspace must use that repository's current public
|
|
17
|
+
packages, unified AI skill, templates, and documentation. It must not use the
|
|
18
|
+
1.x resource/SDD publishing commands or any historical embedded copy of the
|
|
19
|
+
2.0 toolchain.
|
|
15
20
|
|
|
16
21
|
Normal OpenXiangda app development uses platform-user login tokens through `/openxiangda-api/v1`; it does not use AK/SK. External backend and third-party integrations use the separate `openxiangda-open-api` skill, `/dingtalk-api/v1.0`, and a platform-managed AK/SK credential.
|
|
17
22
|
|
|
18
|
-
Private platform routing is fixed: backend APIs are under `/service`, platform management is under `/platform`, and app runtime access is under `/view`. Passing a root domain such as `https://
|
|
23
|
+
Private platform routing is fixed: backend APIs are under `/service`, platform management is under `/platform`, and app runtime access is under `/view`. Passing a root domain such as `https://platform.example.com/` to the CLI is supported; OpenXiangda stores the API base as `https://platform.example.com/service`.
|
|
19
24
|
|
|
20
25
|
## OpenXiangda 1.x Delivery V2 (maintenance)
|
|
21
26
|
|
package/lib/cli.js
CHANGED
|
@@ -114,6 +114,7 @@ const {
|
|
|
114
114
|
summarizeBindingDifferences,
|
|
115
115
|
} = require('./resource-binding-contract');
|
|
116
116
|
const { getSkillStatusReport, installSkills } = require('./skills');
|
|
117
|
+
const { resolveUpdateTarget, buildGenerationUpdateCommand } = require('./generation-update');
|
|
117
118
|
const {
|
|
118
119
|
assertOrClaimWorktreeOwner,
|
|
119
120
|
claimWorktreeOwner,
|
|
@@ -532,9 +533,10 @@ async function update(args) {
|
|
|
532
533
|
return;
|
|
533
534
|
}
|
|
534
535
|
const registry = normalizeNpmRegistry(flags.registry || OFFICIAL_NPM_REGISTRY);
|
|
536
|
+
const selection = resolveUpdateTarget({ target: flags.target });
|
|
535
537
|
|
|
536
538
|
if (requestedSubcommand === 'check') {
|
|
537
|
-
const result = checkOpenXiangdaUpdate(registry);
|
|
539
|
+
const result = checkOpenXiangdaUpdate(registry, selection);
|
|
538
540
|
if (flags.json) return writeJson(result);
|
|
539
541
|
printUpdateCheck(result);
|
|
540
542
|
return;
|
|
@@ -545,6 +547,7 @@ async function update(args) {
|
|
|
545
547
|
json: Boolean(flags.json),
|
|
546
548
|
skipSkills: Boolean(flags['no-skills']),
|
|
547
549
|
timeoutSeconds: flags['timeout-seconds'],
|
|
550
|
+
...selection,
|
|
548
551
|
});
|
|
549
552
|
if (flags.json) return writeJson(result);
|
|
550
553
|
print('OpenXiangda 已更新。');
|
|
@@ -562,8 +565,8 @@ function normalizeNpmRegistry(value) {
|
|
|
562
565
|
return registry.replace(/\/+$/, '') || OFFICIAL_NPM_REGISTRY;
|
|
563
566
|
}
|
|
564
567
|
|
|
565
|
-
function checkOpenXiangdaUpdate(registry) {
|
|
566
|
-
const latestVersion = fetchLatestOpenXiangdaVersion(registry);
|
|
568
|
+
function checkOpenXiangdaUpdate(registry, selection = resolveUpdateTarget()) {
|
|
569
|
+
const latestVersion = fetchLatestOpenXiangdaVersion(registry, selection.channel);
|
|
567
570
|
const versionComparison = compareVersions(latestVersion, CURRENT_VERSION);
|
|
568
571
|
const updateAvailable = versionComparison > 0;
|
|
569
572
|
return {
|
|
@@ -577,8 +580,10 @@ function checkOpenXiangdaUpdate(registry) {
|
|
|
577
580
|
: versionComparison < 0
|
|
578
581
|
? 'local_newer_than_registry'
|
|
579
582
|
: 'latest',
|
|
580
|
-
|
|
581
|
-
|
|
583
|
+
target: selection.target,
|
|
584
|
+
channel: selection.channel,
|
|
585
|
+
installCommand: `openxiangda update install --target ${selection.target}`,
|
|
586
|
+
skillInstallCommand: 'openxiangda skill install',
|
|
582
587
|
compatibilityCheckCommand: 'openxiangda commands --json',
|
|
583
588
|
checkedAt: new Date().toISOString(),
|
|
584
589
|
notes: [
|
|
@@ -588,10 +593,10 @@ function checkOpenXiangdaUpdate(registry) {
|
|
|
588
593
|
};
|
|
589
594
|
}
|
|
590
595
|
|
|
591
|
-
function fetchLatestOpenXiangdaVersion(registry) {
|
|
596
|
+
function fetchLatestOpenXiangdaVersion(registry, channel = 'v1') {
|
|
592
597
|
const result = spawnSync(
|
|
593
598
|
'npm',
|
|
594
|
-
['view', `${NPM_PACKAGE_NAME}
|
|
599
|
+
['view', `${NPM_PACKAGE_NAME}@${channel}`, 'version', `--registry=${registry}`, '--json'],
|
|
595
600
|
{
|
|
596
601
|
encoding: 'utf8',
|
|
597
602
|
timeout: 30000,
|
|
@@ -630,17 +635,22 @@ function normalizeUpdateInstallTimeoutSeconds(value) {
|
|
|
630
635
|
|
|
631
636
|
function buildOpenXiangdaUpdateInstallPlan(registryInput, options = {}) {
|
|
632
637
|
const registry = normalizeNpmRegistry(registryInput);
|
|
638
|
+
const selection = resolveUpdateTarget(options);
|
|
639
|
+
const version = options.version || fetchLatestOpenXiangdaVersion(registry, selection.channel);
|
|
640
|
+
const execution = buildGenerationUpdateCommand(selection, version);
|
|
633
641
|
const timeoutSeconds = normalizeUpdateInstallTimeoutSeconds(
|
|
634
642
|
options.timeoutSeconds
|
|
635
643
|
);
|
|
636
644
|
return {
|
|
637
645
|
registry,
|
|
638
646
|
timeoutSeconds,
|
|
647
|
+
...selection,
|
|
648
|
+
command: execution.command,
|
|
649
|
+
version,
|
|
639
650
|
args: [
|
|
640
|
-
|
|
641
|
-
'-g',
|
|
642
|
-
`${NPM_PACKAGE_NAME}@latest`,
|
|
651
|
+
...execution.args,
|
|
643
652
|
`--registry=${registry}`,
|
|
653
|
+
...(execution.command === 'npm' ? [
|
|
644
654
|
'--prefer-offline',
|
|
645
655
|
'--legacy-peer-deps',
|
|
646
656
|
'--no-audit',
|
|
@@ -650,6 +660,7 @@ function buildOpenXiangdaUpdateInstallPlan(registryInput, options = {}) {
|
|
|
650
660
|
'--fetch-timeout=60000',
|
|
651
661
|
'--fetch-retry-mintimeout=1000',
|
|
652
662
|
'--fetch-retry-maxtimeout=10000',
|
|
663
|
+
] : []),
|
|
653
664
|
],
|
|
654
665
|
};
|
|
655
666
|
}
|
|
@@ -662,6 +673,7 @@ async function runCommandWithHeartbeat(command, args, options = {}) {
|
|
|
662
673
|
let child;
|
|
663
674
|
try {
|
|
664
675
|
child = spawn(command, args, {
|
|
676
|
+
cwd: options.cwd || process.cwd(),
|
|
665
677
|
encoding: 'utf8',
|
|
666
678
|
stdio: quiet ? ['ignore', 'pipe', 'pipe'] : 'inherit',
|
|
667
679
|
env: options.env || process.env,
|
|
@@ -731,7 +743,8 @@ async function installOpenXiangdaUpdate(registry, options = {}) {
|
|
|
731
743
|
if (!quiet) {
|
|
732
744
|
print(`执行: npm ${npmArgs.join(' ')}`);
|
|
733
745
|
}
|
|
734
|
-
const installResult = await runCommandWithHeartbeat(
|
|
746
|
+
const installResult = await runCommandWithHeartbeat(plan.command, npmArgs, {
|
|
747
|
+
cwd: plan.cwd,
|
|
735
748
|
quiet,
|
|
736
749
|
timeoutMs: plan.timeoutSeconds * 1000,
|
|
737
750
|
env: { ...process.env, npm_config_registry: plan.registry },
|
|
@@ -763,11 +776,15 @@ async function installOpenXiangdaUpdate(registry, options = {}) {
|
|
|
763
776
|
},
|
|
764
777
|
};
|
|
765
778
|
|
|
766
|
-
if (!options.skipSkills) {
|
|
779
|
+
if (!options.skipSkills && plan.target === 'workspace') {
|
|
767
780
|
if (!quiet) {
|
|
768
781
|
print('刷新 OpenXiangda skills: openxiangda skill install --force');
|
|
769
782
|
}
|
|
770
|
-
const
|
|
783
|
+
const localManifest = path.join(plan.cwd, 'node_modules/openxiangda/package.json');
|
|
784
|
+
const localPackage = JSON.parse(fs.readFileSync(localManifest, 'utf8'));
|
|
785
|
+
const localBin = path.resolve(path.dirname(localManifest), typeof localPackage.bin === 'string' ? localPackage.bin : localPackage.bin.openxiangda);
|
|
786
|
+
const skillResult = spawnSync(process.execPath, [localBin, 'skill', 'install'], {
|
|
787
|
+
cwd: plan.cwd,
|
|
771
788
|
encoding: 'utf8',
|
|
772
789
|
stdio: quiet ? 'pipe' : 'inherit',
|
|
773
790
|
timeout: 60000,
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
function resolveUpdateTarget(options = {}) {
|
|
5
|
+
let root = path.resolve(options.cwd || process.cwd());
|
|
6
|
+
let workspace = null;
|
|
7
|
+
while (true) {
|
|
8
|
+
const v1 = fs.existsSync(path.join(root, 'app-workspace.config.ts')) || fs.existsSync(path.join(root, '.openxiangda/state.json'));
|
|
9
|
+
const v2 = fs.existsSync(path.join(root, 'openxiangda.config.ts')) || fs.existsSync(path.join(root, 'openxiangda-app.config.ts'));
|
|
10
|
+
if (v1 && v2) throw new Error('WORKSPACE_GENERATION_CONFLICT: 同一目录存在 V1/V2 工作区标记');
|
|
11
|
+
if (v2) throw new Error('WORKSPACE_ENGINE_GENERATION_MISMATCH: V1 CLI 不能升级 V2 项目;请使用统一入口');
|
|
12
|
+
if (v1) { workspace = root; break; }
|
|
13
|
+
const parent = path.dirname(root);
|
|
14
|
+
if (parent === root) break;
|
|
15
|
+
root = parent;
|
|
16
|
+
}
|
|
17
|
+
const target = options.target || (workspace ? 'workspace' : 'launcher');
|
|
18
|
+
if (!['workspace', 'launcher'].includes(target)) throw new Error('DISTRIBUTION_UPDATE_TARGET_INVALID: 使用 workspace 或 launcher');
|
|
19
|
+
if (target === 'workspace' && !workspace) throw new Error('DISTRIBUTION_WORKSPACE_REQUIRED: 未找到 V1 工作区');
|
|
20
|
+
return { target, channel: target === 'workspace' ? 'legacy-v1' : 'latest', cwd: workspace || process.cwd() };
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function buildGenerationUpdateCommand(selection, version) {
|
|
24
|
+
if (!/^[12]\.\d+\.\d+$/.test(version)) throw new Error('DISTRIBUTION_UPDATE_VERSION_INVALID: 升级目标必须是已发布的稳定版本');
|
|
25
|
+
if (selection.target === 'launcher') {
|
|
26
|
+
if (!version.startsWith('2.')) throw new Error('DISTRIBUTION_UPDATE_GENERATION_MISMATCH: 全局入口需要 V2');
|
|
27
|
+
if (Number(process.versions.node.split('.')[0]) < 24) throw new Error('DISTRIBUTION_NODE_VERSION_REQUIRED: 全局统一入口需要 Node.js 24;当前 V1 项目环境不会自动改变');
|
|
28
|
+
return { command: 'npm', args: ['install', '-g', `openxiangda@${version}`] };
|
|
29
|
+
}
|
|
30
|
+
if (!version.startsWith('1.')) throw new Error('DISTRIBUTION_UPDATE_GENERATION_MISMATCH: V1 项目只允许安装 V1');
|
|
31
|
+
const file = path.join(selection.cwd, 'package.json');
|
|
32
|
+
if (!fs.existsSync(file)) throw new Error('DISTRIBUTION_PACKAGE_MANIFEST_REQUIRED: 工作区缺少 package.json');
|
|
33
|
+
const manifest = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
34
|
+
const pnpm = fs.existsSync(path.join(selection.cwd, 'pnpm-lock.yaml')) || manifest.packageManager?.startsWith('pnpm@');
|
|
35
|
+
if (pnpm && fs.existsSync(path.join(selection.cwd, 'package-lock.json'))) throw new Error('DISTRIBUTION_PACKAGE_MANAGER_CONFLICT: 多个包管理器锁文件');
|
|
36
|
+
if (fs.existsSync(path.join(selection.cwd, 'yarn.lock'))) throw new Error('DISTRIBUTION_PACKAGE_MANAGER_UNSUPPORTED: 请通过项目 Yarn 命令显式安装 openxiangda@legacy-v1');
|
|
37
|
+
return { command: pnpm ? 'pnpm' : 'npm', args: pnpm
|
|
38
|
+
? ['add', ...(manifest.dependencies?.openxiangda ? [] : ['--save-dev']), '--save-exact', `openxiangda@${version}`, ...(fs.existsSync(path.join(selection.cwd, 'pnpm-workspace.yaml')) ? ['--workspace-root'] : [])]
|
|
39
|
+
: ['install', manifest.dependencies?.openxiangda ? '--save-prod' : '--save-dev', '--save-exact', `openxiangda@${version}`] };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
module.exports = { resolveUpdateTarget, buildGenerationUpdateCommand };
|
package/lib/skills.js
CHANGED
|
@@ -11,9 +11,9 @@ const MANAGER = 'openxiangda';
|
|
|
11
11
|
|
|
12
12
|
const SKILL_SPECS = [
|
|
13
13
|
{
|
|
14
|
-
name: 'openxiangda',
|
|
15
|
-
displayName: 'OpenXiangda',
|
|
16
|
-
shortDescription: '
|
|
14
|
+
name: 'openxiangda-v1',
|
|
15
|
+
displayName: 'OpenXiangda V1',
|
|
16
|
+
shortDescription: 'OpenXiangda V1 维护入口,仅用于 V1 工作区。',
|
|
17
17
|
sourceRelativePath: 'openxiangda-skills',
|
|
18
18
|
type: 'root',
|
|
19
19
|
},
|
|
@@ -82,64 +82,11 @@ const SKILL_SPECS = [
|
|
|
82
82
|
sourceRelativePath: 'openxiangda-skills/skills/openxiangda-open-api',
|
|
83
83
|
type: 'subskill',
|
|
84
84
|
},
|
|
85
|
-
{
|
|
86
|
-
name: 'openxiangda-v2',
|
|
87
|
-
displayName: 'OpenXiangda 2.0',
|
|
88
|
-
shortDescription: '标准 React/NestJS 应用与应用级交付入口。',
|
|
89
|
-
sourceRelativePath: 'v2/skills/openxiangda-v2',
|
|
90
|
-
type: 'subskill',
|
|
91
|
-
},
|
|
92
|
-
{
|
|
93
|
-
name: 'openxiangda-v2-architecture',
|
|
94
|
-
displayName: 'OpenXiangda 2.0 Architecture',
|
|
95
|
-
shortDescription: '设计 2.0 应用边界与类型化平台契约。',
|
|
96
|
-
sourceRelativePath: 'v2/skills/openxiangda-v2-architecture',
|
|
97
|
-
type: 'subskill',
|
|
98
|
-
},
|
|
99
|
-
{
|
|
100
|
-
name: 'openxiangda-v2-frontend',
|
|
101
|
-
displayName: 'OpenXiangda 2.0 Frontend',
|
|
102
|
-
shortDescription: '开发 React、Ant Design 与标准后台页面。',
|
|
103
|
-
sourceRelativePath: 'v2/skills/openxiangda-v2-frontend',
|
|
104
|
-
type: 'subskill',
|
|
105
|
-
},
|
|
106
|
-
{
|
|
107
|
-
name: 'openxiangda-v2-backend',
|
|
108
|
-
displayName: 'OpenXiangda 2.0 Backend',
|
|
109
|
-
shortDescription: '开发平台托管的标准 NestJS 应用后端。',
|
|
110
|
-
sourceRelativePath: 'v2/skills/openxiangda-v2-backend',
|
|
111
|
-
type: 'subskill',
|
|
112
|
-
},
|
|
113
|
-
{
|
|
114
|
-
name: 'openxiangda-v2-data-authz',
|
|
115
|
-
displayName: 'OpenXiangda 2.0 Data and AuthZ',
|
|
116
|
-
shortDescription: '设计 Data API、RBAC 与上下文数据权限。',
|
|
117
|
-
sourceRelativePath: 'v2/skills/openxiangda-v2-data-authz',
|
|
118
|
-
type: 'subskill',
|
|
119
|
-
},
|
|
120
|
-
{
|
|
121
|
-
name: 'openxiangda-v2-workflow-events',
|
|
122
|
-
displayName: 'OpenXiangda 2.0 Workflow and Events',
|
|
123
|
-
shortDescription: '开发 Workflow Kernel v2 与持久事件消费者。',
|
|
124
|
-
sourceRelativePath: 'v2/skills/openxiangda-v2-workflow-events',
|
|
125
|
-
type: 'subskill',
|
|
126
|
-
},
|
|
127
|
-
{
|
|
128
|
-
name: 'openxiangda-v2-delivery',
|
|
129
|
-
displayName: 'OpenXiangda 2.0 Delivery',
|
|
130
|
-
shortDescription: '构建、部署、观察、晋级和回滚完整应用。',
|
|
131
|
-
sourceRelativePath: 'v2/skills/openxiangda-v2-delivery',
|
|
132
|
-
type: 'subskill',
|
|
133
|
-
},
|
|
134
|
-
{
|
|
135
|
-
name: 'openxiangda-v1-maintenance',
|
|
136
|
-
displayName: 'OpenXiangda 1.x Maintenance',
|
|
137
|
-
shortDescription: '不迁移地安全维护稳定 1.x 应用。',
|
|
138
|
-
sourceRelativePath: 'v2/skills/openxiangda-v1-maintenance',
|
|
139
|
-
type: 'subskill',
|
|
140
|
-
},
|
|
141
85
|
];
|
|
142
86
|
|
|
87
|
+
// V1 does not own V2 or the unified routing skill, including historical installs.
|
|
88
|
+
const RETIRED_SKILL_SPECS = [];
|
|
89
|
+
|
|
143
90
|
function getDefaultCodexSkillsDir(env = process.env) {
|
|
144
91
|
const codexHome = env.CODEX_HOME || path.join(os.homedir(), '.codex');
|
|
145
92
|
return path.join(codexHome, 'skills');
|
|
@@ -242,20 +189,52 @@ function getSkillStatus(spec, skillsDir) {
|
|
|
242
189
|
};
|
|
243
190
|
}
|
|
244
191
|
|
|
192
|
+
function getRetiredSkillStatus(spec, skillsDir) {
|
|
193
|
+
const targetDir = path.join(skillsDir, spec.name);
|
|
194
|
+
if (!fs.existsSync(targetDir)) {
|
|
195
|
+
return {
|
|
196
|
+
name: spec.name,
|
|
197
|
+
status: 'absent',
|
|
198
|
+
targetDir,
|
|
199
|
+
sourceRelativePath: spec.sourceRelativePath,
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const manifest = readManifest(targetDir);
|
|
204
|
+
const isManagedRetiredSkill =
|
|
205
|
+
manifest?.manager === MANAGER &&
|
|
206
|
+
manifest.sourceRelativePath === spec.sourceRelativePath;
|
|
207
|
+
return {
|
|
208
|
+
name: spec.name,
|
|
209
|
+
status: isManagedRetiredSkill ? 'retired-managed' : 'preserved-external',
|
|
210
|
+
targetDir,
|
|
211
|
+
sourceRelativePath: spec.sourceRelativePath,
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function removeRetiredManagedSkill(spec, skillsDir) {
|
|
216
|
+
const status = getRetiredSkillStatus(spec, skillsDir);
|
|
217
|
+
if (status.status === 'retired-managed') {
|
|
218
|
+
fs.rmSync(status.targetDir, { recursive: true, force: true });
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
245
222
|
function getSkillStatusReport(options = {}) {
|
|
246
223
|
const agent = validateAgent(options.agent);
|
|
247
224
|
const env = options.env || process.env;
|
|
248
225
|
|
|
249
226
|
// 获取目标目录列表
|
|
250
|
-
const skillsDirs = getDualSkillsDirs(agent, env);
|
|
227
|
+
const skillsDirs = options.dest ? [path.resolve(options.dest)] : getDualSkillsDirs(agent, env);
|
|
251
228
|
|
|
252
229
|
const results = [];
|
|
253
230
|
for (const skillsDir of skillsDirs) {
|
|
254
231
|
const skills = SKILL_SPECS.map(spec => getSkillStatus(spec, skillsDir));
|
|
232
|
+
const retiredSkills = RETIRED_SKILL_SPECS.map(spec => getRetiredSkillStatus(spec, skillsDir));
|
|
255
233
|
results.push({
|
|
256
234
|
agent,
|
|
257
235
|
skillsDir,
|
|
258
236
|
skills,
|
|
237
|
+
retiredSkills,
|
|
259
238
|
});
|
|
260
239
|
}
|
|
261
240
|
|
|
@@ -278,11 +257,12 @@ function installSkills(options = {}) {
|
|
|
278
257
|
const force = Boolean(options.force);
|
|
279
258
|
|
|
280
259
|
// 获取目标目录列表
|
|
281
|
-
const skillsDirs = getDualSkillsDirs(agent, options.env);
|
|
260
|
+
const skillsDirs = options.dest ? [path.resolve(options.dest)] : getDualSkillsDirs(agent, options.env);
|
|
282
261
|
|
|
283
262
|
const results = [];
|
|
284
263
|
for (const skillsDir of skillsDirs) {
|
|
285
264
|
const before = SKILL_SPECS.map(spec => getSkillStatus(spec, skillsDir));
|
|
265
|
+
const retiredBefore = RETIRED_SKILL_SPECS.map(spec => getRetiredSkillStatus(spec, skillsDir));
|
|
286
266
|
const conflicts = before.filter(item => item.status === 'foreign');
|
|
287
267
|
|
|
288
268
|
if (conflicts.length > 0 && !force && !dryRun) {
|
|
@@ -298,17 +278,29 @@ function installSkills(options = {}) {
|
|
|
298
278
|
targetDir: item.targetDir,
|
|
299
279
|
sourceRelativePath: item.sourceRelativePath,
|
|
300
280
|
}));
|
|
281
|
+
const retiredOperations = retiredBefore.map(item => ({
|
|
282
|
+
name: item.name,
|
|
283
|
+
operation: item.status === 'retired-managed' ? 'remove-retired-managed' : 'preserve',
|
|
284
|
+
targetDir: item.targetDir,
|
|
285
|
+
sourceRelativePath: item.sourceRelativePath,
|
|
286
|
+
}));
|
|
301
287
|
|
|
302
288
|
if (!dryRun) {
|
|
303
289
|
fs.mkdirSync(skillsDir, { recursive: true });
|
|
304
290
|
for (const spec of SKILL_SPECS) {
|
|
305
291
|
installOneSkill(spec, skillsDir);
|
|
306
292
|
}
|
|
293
|
+
for (const spec of RETIRED_SKILL_SPECS) {
|
|
294
|
+
removeRetiredManagedSkill(spec, skillsDir);
|
|
295
|
+
}
|
|
307
296
|
}
|
|
308
297
|
|
|
309
298
|
const after = dryRun
|
|
310
299
|
? before
|
|
311
300
|
: SKILL_SPECS.map(spec => getSkillStatus(spec, skillsDir));
|
|
301
|
+
const retiredAfter = dryRun
|
|
302
|
+
? retiredBefore
|
|
303
|
+
: RETIRED_SKILL_SPECS.map(spec => getRetiredSkillStatus(spec, skillsDir));
|
|
312
304
|
|
|
313
305
|
results.push({
|
|
314
306
|
agent,
|
|
@@ -317,7 +309,9 @@ function installSkills(options = {}) {
|
|
|
317
309
|
dryRun,
|
|
318
310
|
force,
|
|
319
311
|
operations,
|
|
312
|
+
retiredOperations,
|
|
320
313
|
skills: after,
|
|
314
|
+
retiredSkills: retiredAfter,
|
|
321
315
|
});
|
|
322
316
|
}
|
|
323
317
|
|
|
@@ -408,7 +402,7 @@ function writeAgentMetadata(spec, skillDir) {
|
|
|
408
402
|
const defaultPrompt =
|
|
409
403
|
spec.defaultPrompt ||
|
|
410
404
|
(spec.name === 'openxiangda'
|
|
411
|
-
? '使用 $openxiangda
|
|
405
|
+
? '使用 $openxiangda-v1 处理 V1 工作区的登录、发布和诊断任务。'
|
|
412
406
|
: `使用 $${spec.name} 处理对应的 OpenXiangda 低代码平台任务。`);
|
|
413
407
|
const content = [
|
|
414
408
|
'interface:',
|
|
@@ -446,6 +440,7 @@ function buildWarnings(legacySkills) {
|
|
|
446
440
|
|
|
447
441
|
module.exports = {
|
|
448
442
|
INSTALL_MANIFEST,
|
|
443
|
+
RETIRED_SKILL_SPECS,
|
|
449
444
|
SKILL_SPECS,
|
|
450
445
|
getSkillStatusReport,
|
|
451
446
|
installSkills,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: openxiangda
|
|
3
|
-
description: "
|
|
2
|
+
name: openxiangda-v1
|
|
3
|
+
description: "Maintain existing OpenXiangda V1 workspaces: forms, pages, resources, workflows, permissions, publishing and diagnosis. New applications default to V2; assess V2 adoption for V1 projects still in testing when capability coverage and migration cost permit."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
<!-- OpenXiangda-Policy-Version: 7 -->
|
|
@@ -13,10 +13,12 @@ This file is a router and safety card. Read only the one or two subskills select
|
|
|
13
13
|
|
|
14
14
|
## Select the runtime generation first
|
|
15
15
|
|
|
16
|
-
If the workspace contains `openxiangda.config.ts
|
|
16
|
+
If the nearest workspace contains `openxiangda.config.ts` or `openxiangda-app.config.ts`, it is a platform-2.0 application; a Nest server is optional. Stop this 1.x resource flow and use the independently released `$openxiangda-v2` skill. The 2.0 CLI operates on one immutable application package and does not use SDD or per-resource publishing.
|
|
17
17
|
|
|
18
18
|
If the workspace contains `app-workspace.config.ts`, forms/pages/resource manifests, or an existing 1.x state directory, continue with this router. Never migrate a stable 1.x application merely because platform 2.0 is available.
|
|
19
19
|
|
|
20
|
+
For an existing V1 project, proactively check whether V2 covers its required capabilities, whether it is still in testing, and whether migration cost is manageable. When those conditions hold, recommend V2 first and explain the benefit, rebuild scope and verification cost. Use the unified entry's `openxiangda migrate assess --to v2` for source pointers; confirm the project design, data/workflow mapping, acceptance and rollback before implementing migration. Keep unknown conditions explicit and continue current maintenance with the matching engine until migration is authorized.
|
|
21
|
+
|
|
20
22
|
## 1.x Delivery V2 is the normal 1.x release path
|
|
21
23
|
|
|
22
24
|
When `app-workspace.config.ts` declares `deliveryVersion: 2`, all later V1 SDD,
|
|
@@ -930,7 +930,7 @@ Body:
|
|
|
930
930
|
"allowedExtensions": ["txt", "pdf", "png"],
|
|
931
931
|
"cors": {
|
|
932
932
|
"managed": true,
|
|
933
|
-
"allowedOrigins": ["https://
|
|
933
|
+
"allowedOrigins": ["https://platform.example.com"],
|
|
934
934
|
"allowedMethods": ["PUT", "GET", "HEAD"],
|
|
935
935
|
"allowedHeaders": ["content-type", "x-oss-*"],
|
|
936
936
|
"exposeHeaders": ["ETag", "x-oss-request-id"],
|
|
@@ -173,7 +173,7 @@ await auth.phoneCodeLogin({ phone, code, challengeId: sent.challengeId });
|
|
|
173
173
|
"allowedExtensions": ["txt", "pdf", "png"],
|
|
174
174
|
"cors": {
|
|
175
175
|
"managed": true,
|
|
176
|
-
"allowedOrigins": ["https://
|
|
176
|
+
"allowedOrigins": ["https://platform.example.com"],
|
|
177
177
|
"allowedMethods": ["PUT", "GET", "HEAD"],
|
|
178
178
|
"allowedHeaders": ["content-type", "x-oss-*"],
|
|
179
179
|
"exposeHeaders": ["ETag", "x-oss-request-id"],
|
|
@@ -64,7 +64,7 @@ Environment-managed workspaces add a logical application and target-specific bin
|
|
|
64
64
|
"currentTarget": "preproduction",
|
|
65
65
|
"targets": {
|
|
66
66
|
"preproduction": {
|
|
67
|
-
"profile": "
|
|
67
|
+
"profile": "instrument-example",
|
|
68
68
|
"environmentId": "PRE_ENV_UUID",
|
|
69
69
|
"kind": "preproduction",
|
|
70
70
|
"appType": "APP_PRE",
|
|
@@ -82,7 +82,7 @@ Environment-managed workspaces add a logical application and target-specific bin
|
|
|
82
82
|
"resources": {}
|
|
83
83
|
},
|
|
84
84
|
"production": {
|
|
85
|
-
"profile": "
|
|
85
|
+
"profile": "instrument-example",
|
|
86
86
|
"environmentId": "PROD_ENV_UUID",
|
|
87
87
|
"kind": "production",
|
|
88
88
|
"appType": "APP_PROD",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openxiangda",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.268",
|
|
4
4
|
"description": "OpenXiangda CLI, workspace build tools, runtime SDK, and form components.",
|
|
5
5
|
"private": false,
|
|
6
6
|
"bin": {
|
|
@@ -55,7 +55,6 @@
|
|
|
55
55
|
"bin/",
|
|
56
56
|
"lib/",
|
|
57
57
|
"openxiangda-skills/",
|
|
58
|
-
"v2/skills/",
|
|
59
58
|
"policy/",
|
|
60
59
|
"templates/",
|
|
61
60
|
"packages/sdk/bin/",
|
|
@@ -216,8 +215,58 @@
|
|
|
216
215
|
"node": ">=18"
|
|
217
216
|
},
|
|
218
217
|
"publishConfig": {
|
|
218
|
+
"tag": "legacy-v1",
|
|
219
219
|
"access": "public",
|
|
220
220
|
"registry": "https://registry.npmjs.org/"
|
|
221
221
|
},
|
|
222
|
-
"license": "MIT"
|
|
222
|
+
"license": "MIT",
|
|
223
|
+
"repository": {
|
|
224
|
+
"type": "git",
|
|
225
|
+
"url": "git+https://github.com/1377385356/openxiangda-v1.git"
|
|
226
|
+
},
|
|
227
|
+
"homepage": "https://github.com/1377385356/openxiangda-v1",
|
|
228
|
+
"bugs": {
|
|
229
|
+
"url": "https://github.com/1377385356/openxiangda-v1/issues"
|
|
230
|
+
},
|
|
231
|
+
"openxiangdaRelease": {
|
|
232
|
+
"schemaVersion": "openxiangda.release-notes/v1",
|
|
233
|
+
"version": "1.0.268",
|
|
234
|
+
"title": "OpenXiangda V1 维护版 1.0.268",
|
|
235
|
+
"status": "reviewed",
|
|
236
|
+
"summary": "旧项目继续使用 V1 引擎,项目升级保持在 V1 维护渠道;同时兼容新的全局统一入口与独立的 V2 Skill。",
|
|
237
|
+
"newFeatures": [
|
|
238
|
+
"V1 工具源码从经审查的公开基线发布到独立 GitHub 仓库,并提供对应版本说明。"
|
|
239
|
+
],
|
|
240
|
+
"fixes": [
|
|
241
|
+
"在 V1 项目内执行 update 默认更新项目依赖,使用 v1 维护渠道并固定具体版本。",
|
|
242
|
+
"全局入口更新单独使用 --target launcher,检查 Node.js 24 要求并安装统一 V2 入口。",
|
|
243
|
+
"V1 主 Skill 使用 openxiangda-v1 名称,安装时保留统一入口和 V2 技能。",
|
|
244
|
+
"V1 发布仅允许 legacy-v1 渠道,防止维护版覆盖 V2 的 latest。"
|
|
245
|
+
],
|
|
246
|
+
"affectedUsers": [
|
|
247
|
+
"维护既有 V1 项目的开发者",
|
|
248
|
+
"已经安装全局统一入口的 V1 用户"
|
|
249
|
+
],
|
|
250
|
+
"compatibility": {
|
|
251
|
+
"node": ">=18;全局 V2 统一入口需要 >=24",
|
|
252
|
+
"workspaceGenerations": [
|
|
253
|
+
"v1"
|
|
254
|
+
],
|
|
255
|
+
"workspacePolicy": "沿用现有 app-workspace.config.ts、平台绑定、登录态、业务数据及流程。",
|
|
256
|
+
"platformPolicy": "本次维护不改变平台协议或部署状态。"
|
|
257
|
+
},
|
|
258
|
+
"upgradeSteps": [
|
|
259
|
+
"项目依赖通过 v1 维护渠道升级,例如 npm install --save-dev --save-exact openxiangda@1.0.268;按项目实际包管理器和依赖类型调整。",
|
|
260
|
+
"安装全局 V2 统一入口后,进入旧项目运行 openxiangda version --json 核对 V1 引擎来源。",
|
|
261
|
+
"刷新对应项目的 V1 Skill,检查依赖与锁文件差异,并执行原有项目测试。",
|
|
262
|
+
"V2 能力满足、项目仍在测试阶段且迁移成本可控时,优先建议 V1 项目采用 V2;先完成项目评估、设计确认和迁移验收。"
|
|
263
|
+
],
|
|
264
|
+
"knownLimitations": [
|
|
265
|
+
"本版本不会自动把 V1 应用迁移到 V2;跨代迁移需要独立设计、映射、演练和批准。",
|
|
266
|
+
"既有 1.0.267 及更早版本的全局更新命令可能安装 latest;新统一入口仍按旧项目标记调用 V1 引擎。"
|
|
267
|
+
],
|
|
268
|
+
"issues": [],
|
|
269
|
+
"sha256": "7aaa99d31b91f725b785259031daaeaf3841cb18a95f6155e43009f1f601e6c9",
|
|
270
|
+
"url": "https://github.com/1377385356/openxiangda-v1/releases/tag/v1.0.268"
|
|
271
|
+
}
|
|
223
272
|
}
|
|
@@ -8,7 +8,7 @@ export default defineAppWorkspaceConfig({
|
|
|
8
8
|
platformUrl:
|
|
9
9
|
process.env.APP_PLATFORM_URL ||
|
|
10
10
|
process.env.OPENXIANGDA_BASE_URL ||
|
|
11
|
-
"https://
|
|
11
|
+
"https://platform.example.com/service",
|
|
12
12
|
servicePrefix: process.env.APP_SERVICE_PREFIX || "/service",
|
|
13
13
|
appKey: process.env.APP_KEY || "",
|
|
14
14
|
appSecret: process.env.APP_SECRET || "",
|
|
@@ -139,7 +139,7 @@ sy-lowcode-app-workspace/
|
|
|
139
139
|
|
|
140
140
|
## 还想看更细的
|
|
141
141
|
|
|
142
|
-
- 全局 skill:`~/.qoder/skills/openxiangda/SKILL.md`(
|
|
142
|
+
- 全局 V1 skill:`~/.qoder/skills/openxiangda-v1/SKILL.md`(V1 决策卡)+ 9 个子 skill;`openxiangda` 由统一分发入口管理。
|
|
143
143
|
- 资源 / 连接器 manifest:`docs/openxiangda-resources-and-connectors.md`(来自 openxiangda 仓库)。
|
|
144
144
|
- 平台数据模型:`references/platform-data-model.md`(option `{label, value}`、附件、成员字段等持久化形态)。
|
|
145
145
|
- 排错清单:`references/troubleshooting.md`。
|
|
@@ -4,7 +4,7 @@ export default defineAppWorkspaceConfig({
|
|
|
4
4
|
deliveryVersion: 2,
|
|
5
5
|
appType: process.env.APP_TYPE || process.env.OPENXIANGDA_APP_TYPE || "APP_XXXXXXXXXXXXXXXX",
|
|
6
6
|
appName: process.env.APP_NAME || "低代码应用",
|
|
7
|
-
platformUrl: process.env.APP_PLATFORM_URL || process.env.OPENXIANGDA_BASE_URL || "http://
|
|
7
|
+
platformUrl: process.env.APP_PLATFORM_URL || process.env.OPENXIANGDA_BASE_URL || "http://platform.example.com/service",
|
|
8
8
|
servicePrefix: process.env.APP_SERVICE_PREFIX || "/service",
|
|
9
9
|
appKey: process.env.APP_KEY || "",
|
|
10
10
|
appSecret: process.env.APP_SECRET || "",
|
package/v2/README.md
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
# OpenXiangda 2.0 toolchain
|
|
2
|
-
|
|
3
|
-
This directory is the isolated OpenXiangda 2.0 package workspace. It does not
|
|
4
|
-
import or dispatch the 1.x CLI implementation in `lib/cli.js`.
|
|
5
|
-
|
|
6
|
-
Current packages:
|
|
7
|
-
|
|
8
|
-
- `@openxiangda/contracts`: versioned wire contracts and JSON Schemas.
|
|
9
|
-
- `@openxiangda/compiler`: deterministic configuration, generated contracts and AppPackage sealing.
|
|
10
|
-
- `@openxiangda/devkit-core`: framework-neutral workspace, package and control-plane use cases.
|
|
11
|
-
- `@openxiangda/nest`: trusted Principal/RoleSession Guard, Data API client,
|
|
12
|
-
signed CloudEvents receiver and standard backend health endpoints.
|
|
13
|
-
- `@openxiangda/admin`: React + Ant Design application shell, capability-aware
|
|
14
|
-
data pages and protocol-driven Workflow v2/1.x compatibility pages.
|
|
15
|
-
- `@openxiangda/testing`: permission fixtures, allow/deny matrix assertions and
|
|
16
|
-
local event record/replay/simulation.
|
|
17
|
-
- `create-openxiangda`: distributable standard application workspace creator.
|
|
18
|
-
- `@openxiangda/cli`: thin Oclif application-level CLI.
|
|
19
|
-
- `@openxiangda/mcp`: structured AI resources and tools over the same services.
|
|
20
|
-
- `@openxiangda/skill-kit`: validation, packaging and installation for the 2.0 domain skills.
|
|
21
|
-
|
|
22
|
-
`templates/application` is the build-verified React 19 + NestJS 11 golden
|
|
23
|
-
workspace. None of these packages imports or dispatches the 1.x CLI, so this
|
|
24
|
-
toolchain can evolve without increasing the stable 1.x launcher surface.
|
|
25
|
-
|
|
26
|
-
```bash
|
|
27
|
-
pnpm install
|
|
28
|
-
pnpm check
|
|
29
|
-
pnpm test
|
|
30
|
-
pnpm template:build
|
|
31
|
-
pnpm skills:check
|
|
32
|
-
pnpm docs:build
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
The fixed Changesets group publishes the packages as one compatible version train. See `docs/getting-started.md` for application usage and `docs/migration.md` for the strict 1.x coexistence boundary.
|
package/v2/skills/manifest.json
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"schemaVersion": 1,
|
|
3
|
-
"skills": [
|
|
4
|
-
{
|
|
5
|
-
"name": "openxiangda-v1-maintenance",
|
|
6
|
-
"description": "Maintain an existing stable OpenXiangda 1.x application without migrating it. Use for scoped fixes to legacy pages, forms, resources, App Function code, automation, workflow, or 1.x release records.",
|
|
7
|
-
"sha256": "7f554b8092397a0994f6aba70be38d2ec7eaf7524a075d68fb99ecbc95303525"
|
|
8
|
-
},
|
|
9
|
-
{
|
|
10
|
-
"name": "openxiangda-v2",
|
|
11
|
-
"description": "Build, inspect, validate, and deliver a complete OpenXiangda 2.0 application workspace. Use when a task spans the React frontend, NestJS backend, Data API, authorization, workflow, events, or whole-application delivery.",
|
|
12
|
-
"sha256": "5645e5cb1c18731d5b5fb21c9ad0e59b9760600794705bb4adcad1d0e316010b"
|
|
13
|
-
},
|
|
14
|
-
{
|
|
15
|
-
"name": "openxiangda-v2-architecture",
|
|
16
|
-
"description": "Design OpenXiangda 2.0 application boundaries and typed contracts. Use when creating an app, decomposing frontend and backend responsibilities, or changing Data, AuthZ, workflow, event, or deployment declarations.",
|
|
17
|
-
"sha256": "02768b17e048c623a13b9a99e5cedc72c9081eaa71b346c3d9db3ca66c95bf5e"
|
|
18
|
-
},
|
|
19
|
-
{
|
|
20
|
-
"name": "openxiangda-v2-backend",
|
|
21
|
-
"description": "Build the standard OpenXiangda 2.0 NestJS application backend. Use when implementing App APIs, Data API transactions, identity-aware business services, event consumers, health checks, or external integration endpoints.",
|
|
22
|
-
"sha256": "04c3de82f4406eaeb378910fbd53e2721c13e1cb9afbeb39f1c63f352522dd39"
|
|
23
|
-
},
|
|
24
|
-
{
|
|
25
|
-
"name": "openxiangda-v2-data-authz",
|
|
26
|
-
"description": "Design OpenXiangda 2.0 Data API and contextual authorization contracts. Use for RBAC, active-role behavior, department or record attributes, application-admin bypass, field policies, and restricted transactions.",
|
|
27
|
-
"sha256": "595fe9b8edc1e9b33720de21ed8758d4c266143c84fcb9eccc25a6adb5cce061"
|
|
28
|
-
},
|
|
29
|
-
{
|
|
30
|
-
"name": "openxiangda-v2-delivery",
|
|
31
|
-
"description": "Validate, build, deploy, observe, promote, retry, and roll back an OpenXiangda 2.0 application. Use when preparing an immutable AppPackage or changing a platform-managed environment.",
|
|
32
|
-
"sha256": "0ee0ffef78ebe37f60319bbf5684522ce61f8d6d084c360f46dc7bb2a31d1906"
|
|
33
|
-
},
|
|
34
|
-
{
|
|
35
|
-
"name": "openxiangda-v2-frontend",
|
|
36
|
-
"description": "Build OpenXiangda 2.0 React and Ant Design admin experiences. Use when implementing menus, routes, CRUD pages, role switching, workflow task pages, or application-defined action components.",
|
|
37
|
-
"sha256": "26add1dadeaf9a0a25d41163fb65cb70c1c5c7d882d57922193c3bd11d9d3e05"
|
|
38
|
-
},
|
|
39
|
-
{
|
|
40
|
-
"name": "openxiangda-v2-workflow-events",
|
|
41
|
-
"description": "Implement OpenXiangda Workflow Kernel v2 and durable application events. Use for approval definitions, assignee providers, task actions, delegation, add-sign, previews, data-change events, workflow events, or timers.",
|
|
42
|
-
"sha256": "e32a5dc84e21d32a00fbfff62a349d848b04d44a569eb4f89b390901f54bb19e"
|
|
43
|
-
}
|
|
44
|
-
]
|
|
45
|
-
}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: openxiangda-v1-maintenance
|
|
3
|
-
description: Maintain an existing stable OpenXiangda 1.x application without migrating it. Use for scoped fixes to legacy pages, forms, resources, App Function code, automation, workflow, or 1.x release records.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# OpenXiangda 1.x Maintenance
|
|
7
|
-
|
|
8
|
-
Preserve the installed 1.x runtime and its existing SDD, resource, function, automation, and workflow release process. Do not reinterpret the workspace as a 2.0 application package.
|
|
9
|
-
|
|
10
|
-
## Maintenance Workflow
|
|
11
|
-
|
|
12
|
-
1. Inspect the existing workspace and platform state with the installed 1.x OpenXiangda skills.
|
|
13
|
-
2. Run `openxiangda sdd context --json` and create or select the required SDD change.
|
|
14
|
-
3. Change only the named legacy resource. Keep existing identifiers and dependency order.
|
|
15
|
-
4. Validate 1.x JS_CODE, App Function, page, form, automation, or workflow artifacts with their dedicated 1.x skill.
|
|
16
|
-
5. Run `openxiangda sdd verify <change> --changed` and use the established 1.x publish gate.
|
|
17
|
-
6. Verify the live application without migrating its runtime, data model, or deployment topology.
|
|
18
|
-
|
|
19
|
-
Do not use 2.0 build, deploy, promote, or rollback commands for a 1.x workspace. Migration is a separate, explicitly approved project.
|
|
20
|
-
|
|
21
|
-
Read [1.x Migration Boundary](../../docs/migration.md) before proposing any coexistence or migration work.
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: openxiangda-v2
|
|
3
|
-
description: Build, inspect, validate, and deliver a complete OpenXiangda 2.0 application workspace. Use when a task spans the React frontend, NestJS backend, Data API, authorization, workflow, events, or whole-application delivery.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# OpenXiangda 2.0
|
|
7
|
-
|
|
8
|
-
Treat the repository as one typed application product. Work through package contracts and deterministic commands; do not mutate platform resources one at a time.
|
|
9
|
-
|
|
10
|
-
## Start Here
|
|
11
|
-
|
|
12
|
-
1. Run `openxiangda app info` and inspect the returned workspace and platform contract versions.
|
|
13
|
-
2. Read `openxiangda.config.ts`, then identify which domain skill applies:
|
|
14
|
-
- architecture: `$openxiangda-v2-architecture`
|
|
15
|
-
- frontend: `$openxiangda-v2-frontend`
|
|
16
|
-
- backend: `$openxiangda-v2-backend`
|
|
17
|
-
- data and authorization: `$openxiangda-v2-data-authz`
|
|
18
|
-
- workflow and events: `$openxiangda-v2-workflow-events`
|
|
19
|
-
- delivery: `$openxiangda-v2-delivery`
|
|
20
|
-
3. Keep generated contracts current with `openxiangda generate`.
|
|
21
|
-
4. Before delivery, run `openxiangda check` and `openxiangda test`.
|
|
22
|
-
|
|
23
|
-
For a new repository, link it to the target platform and let an authorized platform administrator run `openxiangda app provision` once. The command is idempotent and must not be used to migrate an existing 1.x identity.
|
|
24
|
-
|
|
25
|
-
## Boundaries
|
|
26
|
-
|
|
27
|
-
- Put browser code in `apps/web`, server code in `apps/server`, shared domain types in `packages/domain`, and generated declarations in `packages/contracts`.
|
|
28
|
-
- Persist business records only through Data API or an App API implemented by the application backend.
|
|
29
|
-
- Treat the active role and context attributes as explicit request state.
|
|
30
|
-
- Use durable event consumers for side effects and idempotency keys for retries.
|
|
31
|
-
- Build one immutable application package; the platform owns deployment execution, health gates, promotion, and rollback.
|
|
32
|
-
- For a stable 1.x workspace, stop and use `$openxiangda-v1-maintenance`.
|
|
33
|
-
|
|
34
|
-
## Delivery Gate
|
|
35
|
-
|
|
36
|
-
Use `openxiangda build --backend-image <immutable-image>` only after checks pass. Use `openxiangda deploy`, `openxiangda status`, and `openxiangda logs` only when the user has authorized the environment change.
|
|
37
|
-
|
|
38
|
-
Read [Getting Started](../../docs/getting-started.md) for the complete development loop.
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: openxiangda-v2-architecture
|
|
3
|
-
description: Design OpenXiangda 2.0 application boundaries and typed contracts. Use when creating an app, decomposing frontend and backend responsibilities, or changing Data, AuthZ, workflow, event, or deployment declarations.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# OpenXiangda 2.0 Architecture
|
|
7
|
-
|
|
8
|
-
Design one independently versioned application with a standard React frontend and NestJS backend.
|
|
9
|
-
|
|
10
|
-
## Design Sequence
|
|
11
|
-
|
|
12
|
-
1. Inspect the workspace with `openxiangda app info`.
|
|
13
|
-
2. Define business aggregates and invariants in `packages/domain`.
|
|
14
|
-
3. Declare Data API resources, capabilities, policies, workflow definitions, event subscriptions, and timers in `openxiangda.config.ts`.
|
|
15
|
-
4. Keep synchronous user interactions in App APIs; move retryable side effects to event consumers.
|
|
16
|
-
5. Use a workflow provider when assignee resolution or a workflow action depends on application data.
|
|
17
|
-
6. Generate types with `openxiangda generate`, then run `openxiangda check`.
|
|
18
|
-
|
|
19
|
-
## Required Decisions
|
|
20
|
-
|
|
21
|
-
- Name every resource, capability, event type, workflow, and provider with a stable application-scoped code.
|
|
22
|
-
- Separate business data from workflow runtime state.
|
|
23
|
-
- Define the authorization context needed for each operation, including active role and data attributes.
|
|
24
|
-
- Define idempotency and concurrency behavior before implementing writes.
|
|
25
|
-
- Define health, readiness, and rollback expectations as part of the application contract.
|
|
26
|
-
|
|
27
|
-
Do not design environment-specific code paths. Environment values and secrets are injected by the platform at deployment time.
|
|
28
|
-
|
|
29
|
-
Read [Concepts](../../docs/concepts.md) before introducing a new platform-facing contract.
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: openxiangda-v2-backend
|
|
3
|
-
description: Build the standard OpenXiangda 2.0 NestJS application backend. Use when implementing App APIs, Data API transactions, identity-aware business services, event consumers, health checks, or external integration endpoints.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# OpenXiangda 2.0 Backend
|
|
7
|
-
|
|
8
|
-
Build a normal NestJS service in `apps/server`. The platform deploys one container per application and injects identity, platform endpoints, environment configuration, and secrets.
|
|
9
|
-
|
|
10
|
-
## Request Path
|
|
11
|
-
|
|
12
|
-
1. Use the platform guard to verify the user token and construct typed request context.
|
|
13
|
-
2. Authorize the capability and contextual data policy before accessing records.
|
|
14
|
-
3. Keep invariants in domain services, not controllers.
|
|
15
|
-
4. Use Data API for persistence and its restricted transaction endpoint for atomic batches.
|
|
16
|
-
5. Require revision or idempotency keys for retryable writes.
|
|
17
|
-
6. Return stable application contracts and structured errors.
|
|
18
|
-
|
|
19
|
-
## Runtime Path
|
|
20
|
-
|
|
21
|
-
- Expose liveness and readiness endpoints.
|
|
22
|
-
- Consume platform events with signature verification and durable idempotency.
|
|
23
|
-
- Keep outbound integrations behind adapters with timeouts and correlation IDs.
|
|
24
|
-
- Read secrets only from injected environment references; never place them in source or application packages.
|
|
25
|
-
|
|
26
|
-
Use `openxiangda dev` for local orchestration. Run `openxiangda check`, `openxiangda test`, and `openxiangda build --backend-image <immutable-image>` before deployment.
|
|
27
|
-
|
|
28
|
-
Read [Backend](../../docs/backend.md) for the standard module layout.
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: openxiangda-v2-data-authz
|
|
3
|
-
description: Design OpenXiangda 2.0 Data API and contextual authorization contracts. Use for RBAC, active-role behavior, department or record attributes, application-admin bypass, field policies, and restricted transactions.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# OpenXiangda 2.0 Data and AuthZ
|
|
7
|
-
|
|
8
|
-
Model permissions as capability checks plus contextual data predicates. A role grants capabilities; policies decide which records and fields are available in the current role context.
|
|
9
|
-
|
|
10
|
-
## Authorization Model
|
|
11
|
-
|
|
12
|
-
1. Declare application roles and stable capability codes.
|
|
13
|
-
2. Treat the selected active role as part of each request. Do not silently union all user roles.
|
|
14
|
-
3. Resolve subject attributes such as departments and managed colleges from trusted platform or application providers.
|
|
15
|
-
4. Resolve resource attributes such as instrument manager and college from the target record.
|
|
16
|
-
5. Express allow rules as explicit subject, action, resource, and environment predicates.
|
|
17
|
-
6. Grant application administrators the declared application-admin bypass and audit every bypassed write.
|
|
18
|
-
|
|
19
|
-
## Data Rules
|
|
20
|
-
|
|
21
|
-
- Declare resources and fields in `openxiangda.config.ts`; generate types with `openxiangda generate`.
|
|
22
|
-
- Prefer server-enforced filters and field policies over client filtering.
|
|
23
|
-
- Use revision checks for updates and restricted transaction batches for related writes.
|
|
24
|
-
- Keep policy tests for multiple roles, role switching, cross-department denial, and administrator access.
|
|
25
|
-
|
|
26
|
-
Run `openxiangda check` and `openxiangda test` after every policy or schema change.
|
|
27
|
-
|
|
28
|
-
Read [Data and Authorization](../../docs/data-authz.md) for policy examples.
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: openxiangda-v2-delivery
|
|
3
|
-
description: Validate, build, deploy, observe, promote, retry, and roll back an OpenXiangda 2.0 application. Use when preparing an immutable AppPackage or changing a platform-managed environment.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# OpenXiangda 2.0 Delivery
|
|
7
|
-
|
|
8
|
-
Deliver the whole application as one immutable version. The client submits intent; the platform owns durable execution and environment state.
|
|
9
|
-
|
|
10
|
-
## Preflight
|
|
11
|
-
|
|
12
|
-
1. Run `openxiangda doctor` and confirm client/platform contract compatibility.
|
|
13
|
-
2. For a first deployment only, confirm an authorized platform administrator has run `openxiangda app provision`.
|
|
14
|
-
3. Run `openxiangda generate --check`, `openxiangda check`, and `openxiangda test`.
|
|
15
|
-
4. Build and push the backend image; use an immutable digest reference.
|
|
16
|
-
5. Run `openxiangda build --backend-image <immutable-image>` and retain the returned package digest.
|
|
17
|
-
|
|
18
|
-
## Deployment
|
|
19
|
-
|
|
20
|
-
- Inspect the plan before changing an environment.
|
|
21
|
-
- Use `openxiangda deploy` only with explicit user authorization.
|
|
22
|
-
- Follow the durable run with `openxiangda status` and `openxiangda logs`.
|
|
23
|
-
- Use `openxiangda retry` only for retryable failed runs.
|
|
24
|
-
- Use `openxiangda cancel <deploymentId>` to stop an obsolete or blocked run before submitting a replacement package.
|
|
25
|
-
- Use `openxiangda promote` to move the exact same application version between environments.
|
|
26
|
-
- Use `openxiangda rollback` to create a new run that activates a known historical version.
|
|
27
|
-
|
|
28
|
-
Do not rebuild during promotion or rollback. Do not expose injected secrets in logs, manifests returned to clients, or package metadata.
|
|
29
|
-
|
|
30
|
-
Read [Delivery](../../docs/delivery.md) for gates and failure handling.
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: openxiangda-v2-frontend
|
|
3
|
-
description: Build OpenXiangda 2.0 React and Ant Design admin experiences. Use when implementing menus, routes, CRUD pages, role switching, workflow task pages, or application-defined action components.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# OpenXiangda 2.0 Frontend
|
|
7
|
-
|
|
8
|
-
Build inside `apps/web` with `@openxiangda/admin`. Preserve the standard shell, role session, route guards, error handling, and typed clients.
|
|
9
|
-
|
|
10
|
-
## Implementation Rules
|
|
11
|
-
|
|
12
|
-
1. Register menus and routes through the application contribution API.
|
|
13
|
-
2. Gate navigation and actions with generated capability names; the server remains authoritative.
|
|
14
|
-
3. Use the active-role session supplied by the platform. Switching roles must refresh permissions and scoped data.
|
|
15
|
-
4. Use the standard CRUD and workflow modules first; add application components only for real domain interactions.
|
|
16
|
-
5. Render workflow actions and field policies from the backend protocol. Do not duplicate workflow transition rules in the browser.
|
|
17
|
-
6. Keep data calls in typed client modules rather than components.
|
|
18
|
-
|
|
19
|
-
Run `openxiangda generate`, `openxiangda check`, and `openxiangda test` after changing contracts or pages. Use accessible Ant Design patterns and run the repository's Ant Design lint when available.
|
|
20
|
-
|
|
21
|
-
Read [Frontend](../../docs/frontend.md) for route, menu, role, and workflow page examples.
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: openxiangda-v2-workflow-events
|
|
3
|
-
description: Implement OpenXiangda Workflow Kernel v2 and durable application events. Use for approval definitions, assignee providers, task actions, delegation, add-sign, previews, data-change events, workflow events, or timers.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# OpenXiangda 2.0 Workflow and Events
|
|
7
|
-
|
|
8
|
-
The workflow kernel owns definitions, instances, tasks, transitions, delegation, add-sign, audit, and field policies. The application owns business records and application-specific logic.
|
|
9
|
-
|
|
10
|
-
## Workflow
|
|
11
|
-
|
|
12
|
-
1. Declare approval and conditional nodes in `openxiangda.config.ts`.
|
|
13
|
-
2. Use provider contracts for application-data-dependent assignee resolution and custom action decisions.
|
|
14
|
-
3. Keep submit preview and actual start on the same definition version and subject context.
|
|
15
|
-
4. Return allowed actions, field policies, presentation hints, and concurrency tokens from the backend.
|
|
16
|
-
5. Persist business changes through Data API or App API, then correlate them to the workflow action.
|
|
17
|
-
6. Test approve, reject, transfer, return, delegation, add-sign, duplicate requests, and stale revisions.
|
|
18
|
-
|
|
19
|
-
## Events
|
|
20
|
-
|
|
21
|
-
- Subscribe to typed data and workflow events; implement idempotent consumers.
|
|
22
|
-
- Use timers as durable event producers.
|
|
23
|
-
- Verify signatures, propagate correlation IDs, and fail retryably for transient dependencies.
|
|
24
|
-
- Never assume event ordering unless the contract explicitly provides a partition key.
|
|
25
|
-
|
|
26
|
-
Use `openxiangda generate`, `openxiangda check`, and `openxiangda test` to keep providers and event payloads aligned.
|
|
27
|
-
|
|
28
|
-
Read [Workflow and Events](../../docs/workflow-events.md) for protocol details.
|