openxiangda-cli 2.0.0-alpha.78 → 2.0.0-alpha.80
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/package.json +4 -4
- package/template/AGENTS.md +4 -2
- package/template/README.md +6 -5
- package/template/apps/server/package.json +1 -1
- package/template/apps/web/package.json +1 -1
- package/template/apps/web/src/Shell.tsx +1 -1
- package/template/apps/web/src/components/resource/GeneratedResourceCrud.tsx +82 -25
- package/template/package.json +3 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openxiangda-cli",
|
|
3
|
-
"version": "2.0.0-alpha.
|
|
3
|
+
"version": "2.0.0-alpha.80",
|
|
4
4
|
"description": "Thin application-level CLI for OpenXiangda 2.0.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -23,9 +23,9 @@
|
|
|
23
23
|
],
|
|
24
24
|
"dependencies": {
|
|
25
25
|
"@oclif/core": "4.13.3",
|
|
26
|
-
"openxiangda-contracts": "2.0.0-alpha.
|
|
27
|
-
"openxiangda-devkit-core": "2.0.0-alpha.
|
|
28
|
-
"openxiangda-mcp": "2.0.0-alpha.
|
|
26
|
+
"openxiangda-contracts": "2.0.0-alpha.36",
|
|
27
|
+
"openxiangda-devkit-core": "2.0.0-alpha.47",
|
|
28
|
+
"openxiangda-mcp": "2.0.0-alpha.47"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
31
|
"tsx": "4.23.12",
|
package/template/AGENTS.md
CHANGED
|
@@ -2,14 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
- Use only the workspace-pinned CLI: `pnpm openxiangda <command>`. Never invoke a bare global `openxiangda` inside a 2.0 task.
|
|
4
4
|
- Use Vite, React Router, Refine Core and Ant Design. Do not add Umi, ProComponents or another admin shell.
|
|
5
|
-
- Declare each resource once in `openxiangda.config.ts
|
|
5
|
+
- Declare each resource once in `openxiangda.config.ts` with only `code`, `name`, `fields` and optional list/layout/data-policy settings. Each field owns type, label, required state, Surface flags, reference/file metadata and access. Resource codes are lower kebab-case.
|
|
6
|
+
- Never write resource-level `schemaVersion`, `appCode`, `schema`, `surface`, `capabilities`, `fieldPolicies` or `platform/data` modules. The compiler derives the strict DataResource, CRUD capabilities, Surface and AI Schema. The application manifest still starts with its one top-level `schemaVersion: 3`.
|
|
6
7
|
- Ordinary list/get/create/update/delete, filters, export and batch operations use the platform Native Data API. Do not create Function CRUD or NestJS wrappers.
|
|
7
8
|
- Add NestJS only for a named business action that needs a cross-resource transaction, an invariant or an external side effect.
|
|
8
9
|
- A NestJS backend declares only `enabled`, `isolation: 'shared' | 'dedicated'` and `resourceProfile: 'light' | 'standard'`. Never put raw Kubernetes resources, replicas, ports or environment maps in application metadata; the platform owns capacity and scaling.
|
|
9
10
|
- Use the current logged-in user and the union of application roles. Do not select RoleSession, persist platform Token or implement a second authorization path.
|
|
10
|
-
-
|
|
11
|
+
- Fields inherit the resource read/create/update capabilities. Use field `access` only to tighten them; arrays are all-of and `false` is explicit deny. There is no `write` fallback.
|
|
11
12
|
- Desktop and mobile pages share values, validation and authorization, but use separate renderers. Members, departments, resources and attachments use platform-owned selectors and stable IDs.
|
|
12
13
|
- A single directory user reference uses a `string` field; a single department or same-app resource reference uses `uuid`; every `multiple: true` reference uses `json`. Roles that consume directory-backed fields explicitly include `app:<app-code>:directory:read`.
|
|
14
|
+
- Derive role grants with `resourceCapabilityCodes(appCode, resourceCode)`. Declare current-user rows only with `currentUserDataPolicy(...)`; do not invent operators, values or alternate current-user spellings.
|
|
13
15
|
- Do not add compatibility aliases, migration branches or silent fallbacks for an earlier 2.0 alpha contract. Replace an incorrect contract and regenerate the application.
|
|
14
16
|
- Run `pnpm openxiangda check` after contract changes. Deploy with `pnpm openxiangda deploy`, inspect with `pnpm openxiangda status` and `pnpm openxiangda logs`, and use the platform rollback command rather than mutating K3s directly.
|
|
15
17
|
- AI-native clients start the workspace protocol through the same pinned binary: `pnpm exec openxiangda --mcp-stdio --cwd <workspace>`. Read `openxiangda://workspace/contracts` or call `contract_describe`, and require its `aiCatalog` and `aiCatalogDigest` to match the normal compiler output. This is a stdio transport mode, not a ninth CLI command. Never write an application MCP server, Catalog file, preview store or second authorization path.
|
package/template/README.md
CHANGED
|
@@ -3,17 +3,18 @@
|
|
|
3
3
|
这是干净的资源 CRUD 模板:Vite + React Router + Refine Core + Ant Design 前端、NestJS 平台启动层,以及平台 Data/AuthZ 声明。
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
|
-
pnpm dlx openxiangda-cli@latest login --base-url https://platform.example.com
|
|
7
|
-
pnpm dlx openxiangda-cli@latest create visitor-reservations
|
|
8
|
-
cd visitor-reservations
|
|
9
6
|
pnpm openxiangda dev
|
|
10
7
|
```
|
|
11
8
|
|
|
12
|
-
|
|
9
|
+
当前应用已经把 CLI 和全部 2.0 包精确锁定在 `package.json` 与 lockfile 中。创建另一个应用时使用当前安装的 OpenXiangda 2.0 Skill 给出的精确 CLI 版本,不使用 `latest`、`alpha` 或全局命令。
|
|
10
|
+
|
|
11
|
+
在 `openxiangda.config.ts` 的 `data.resources` 中只声明资源 `code`、`name`、`fields` 和可选列表/布局/数据策略。每个字段一次写完 type、label、required、列表/筛选标记、reference/file 与 access;不要再写 `schema`、`surface`、资源 capabilities、资源 fieldPolicies 或 `platform/data` 模块。编译器据此生成标准后台列表、筛选、表单、详情、审计、文件上传、独立移动端页面和 AI Schema。
|
|
12
|
+
|
|
13
|
+
成员、部门和资源关系分别使用 `reference: { kind: 'directory-user' }`、`directory-department`、`resource`,只保存稳定 ID,不复制目录数据。单选成员字段使用 `string`,单选部门和资源引用使用 `uuid`,多选引用统一使用 `json`。需要目录选择器的业务角色必须显式拥有 `app:<app-code>:directory:read` 能力。用 `resourceCapabilityCodes` 生成资源 CRUD 权限,用 `currentUserDataPolicy` 声明唯一的当前用户行规则。
|
|
13
14
|
|
|
14
15
|
所有 CRUD 位于 `apps/web/src/platform-client.ts` 的 Data API adapter;Nest 只在需要复杂事务或外部集成时增加 App API。选择器显式携带当前环境、使用同源 Cookie,浏览器不读取 Token,也不发送 RoleSession header。
|
|
15
16
|
|
|
16
|
-
前端读取平台 current-user
|
|
17
|
+
前端读取平台 current-user 的角色/能力并集进行展示保护,服务端始终权威。字段默认继承资源 read/create/update;`access` 数组为 all-of,`false` 为明确拒绝,无权字段在提交前从 payload 删除。
|
|
17
18
|
|
|
18
19
|
```bash
|
|
19
20
|
pnpm openxiangda check
|
|
@@ -352,7 +352,7 @@ export function Shell({ children }: { children: ReactNode }) {
|
|
|
352
352
|
export function EmptyApplicationPage() {
|
|
353
353
|
return (
|
|
354
354
|
<Shell>
|
|
355
|
-
<Empty description="还没有声明数据资源;请在
|
|
355
|
+
<Empty description="还没有声明数据资源;请在 openxiangda.config.ts 中添加资源和字段" />
|
|
356
356
|
</Shell>
|
|
357
357
|
);
|
|
358
358
|
}
|
|
@@ -87,7 +87,16 @@ function definitionFor(resourceCode: string) {
|
|
|
87
87
|
|
|
88
88
|
function fieldFor(surface: DataResourceSurface, key: string): SurfaceField {
|
|
89
89
|
const field = surface.fields[key];
|
|
90
|
-
return field
|
|
90
|
+
return field
|
|
91
|
+
? { key, ...field }
|
|
92
|
+
: {
|
|
93
|
+
key,
|
|
94
|
+
label: key,
|
|
95
|
+
widget: 'text',
|
|
96
|
+
readCapabilities: [],
|
|
97
|
+
createCapabilities: [],
|
|
98
|
+
updateCapabilities: [],
|
|
99
|
+
};
|
|
91
100
|
}
|
|
92
101
|
|
|
93
102
|
function fieldsBySection(surface: DataResourceSurface) {
|
|
@@ -131,10 +140,13 @@ function normalizeFormValues(values: Record<string, unknown>, surface: DataResou
|
|
|
131
140
|
}
|
|
132
141
|
|
|
133
142
|
function fieldWritable(field: SurfaceField, mode: 'create' | 'edit', hasCapability: (code: string) => boolean, isAppSuperAdmin: boolean) {
|
|
134
|
-
const capability = mode === 'create' ? field.createCapability : field.updateCapability;
|
|
135
143
|
if (isAppSuperAdmin) return true;
|
|
136
|
-
|
|
137
|
-
return
|
|
144
|
+
const capabilities = mode === 'create' ? field.createCapabilities : field.updateCapabilities;
|
|
145
|
+
return capabilities.length > 0 && capabilities.every(hasCapability);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function fieldReadable(field: SurfaceField, hasCapability: (code: string) => boolean, isAppSuperAdmin: boolean) {
|
|
149
|
+
return isAppSuperAdmin || (field.readCapabilities.length > 0 && field.readCapabilities.every(hasCapability));
|
|
138
150
|
}
|
|
139
151
|
|
|
140
152
|
function auditOperationLabel(operation: DataAuditEntry['operation']) {
|
|
@@ -179,11 +191,11 @@ async function exportResourceRows(
|
|
|
179
191
|
code: string,
|
|
180
192
|
name: string,
|
|
181
193
|
surface: DataResourceSurface,
|
|
194
|
+
fields: SurfaceField[],
|
|
182
195
|
query: Partial<GenericResourceQuery>,
|
|
183
196
|
sort: { field: string; order: 'asc' | 'desc' }
|
|
184
197
|
) {
|
|
185
198
|
const client = createNativeResourceClient(code, surface);
|
|
186
|
-
const fields = listSurfaceFields(surface.fields);
|
|
187
199
|
const rows: ResourceRecord[] = [];
|
|
188
200
|
const pageSize = 500;
|
|
189
201
|
let page = 1;
|
|
@@ -215,8 +227,8 @@ async function exportResourceRows(
|
|
|
215
227
|
return { count: rows.length, truncated: rows.length < total };
|
|
216
228
|
}
|
|
217
229
|
|
|
218
|
-
function AuditEntry({ entry, surface }: { entry: DataAuditEntry; surface: DataResourceSurface }) {
|
|
219
|
-
const fields = auditChangeFields(entry, surface);
|
|
230
|
+
function AuditEntry({ entry, surface, readable }: { entry: DataAuditEntry; surface: DataResourceSurface; readable: (field: SurfaceField) => boolean }) {
|
|
231
|
+
const fields = auditChangeFields(entry, surface).filter(readable);
|
|
220
232
|
const changes = entry.changes || entry.record || entry.before || {};
|
|
221
233
|
return (
|
|
222
234
|
<div className="oxa-audit-entry">
|
|
@@ -261,14 +273,21 @@ function AuditEntry({ entry, surface }: { entry: DataAuditEntry; surface: DataRe
|
|
|
261
273
|
);
|
|
262
274
|
}
|
|
263
275
|
|
|
264
|
-
function useGeneratedList(definition: GeneratedDefinition) {
|
|
276
|
+
function useGeneratedList(definition: GeneratedDefinition, readableFieldCodes: Set<string>) {
|
|
265
277
|
const { code, surface } = definition;
|
|
266
|
-
const
|
|
278
|
+
const declaredDefaultSort = surface.list?.defaultSort?.field;
|
|
279
|
+
const defaultSortField =
|
|
280
|
+
(declaredDefaultSort && readableFieldCodes.has(declaredDefaultSort)
|
|
281
|
+
? declaredDefaultSort
|
|
282
|
+
: Object.keys(surface.fields).find((field) => readableFieldCodes.has(field))) || 'id';
|
|
267
283
|
const defaultSortOrder = surface.list?.defaultSort?.order || 'asc';
|
|
268
284
|
const defaultPageSize = surface.list?.defaultPageSize || 20;
|
|
285
|
+
const defaultSearchField = (surface.list?.searchableFields || []).find((field) =>
|
|
286
|
+
readableFieldCodes.has(field)
|
|
287
|
+
);
|
|
269
288
|
const [page, setPage] = useState(1);
|
|
270
289
|
const [pageSize, setPageSize] = useState(defaultPageSize);
|
|
271
|
-
const [draft, setDraft] = useState<Partial<GenericResourceQuery>>({});
|
|
290
|
+
const [draft, setDraft] = useState<Partial<GenericResourceQuery>>({ searchField: defaultSearchField });
|
|
272
291
|
const [query, setQuery] = useState<Partial<GenericResourceQuery>>({});
|
|
273
292
|
const [sort, setSort] = useState({
|
|
274
293
|
field: defaultSortField,
|
|
@@ -277,13 +296,13 @@ function useGeneratedList(definition: GeneratedDefinition) {
|
|
|
277
296
|
useEffect(() => {
|
|
278
297
|
setPage(1);
|
|
279
298
|
setPageSize(defaultPageSize);
|
|
280
|
-
setDraft({});
|
|
299
|
+
setDraft({ searchField: defaultSearchField });
|
|
281
300
|
setQuery({});
|
|
282
301
|
setSort({
|
|
283
302
|
field: defaultSortField,
|
|
284
303
|
order: defaultSortOrder as 'asc' | 'desc',
|
|
285
304
|
});
|
|
286
|
-
}, [code, defaultPageSize, defaultSortField, defaultSortOrder]);
|
|
305
|
+
}, [code, defaultPageSize, defaultSearchField, defaultSortField, defaultSortOrder]);
|
|
287
306
|
const list = useList<ResourceRecord>({
|
|
288
307
|
resource: code,
|
|
289
308
|
pagination: { currentPage: page, pageSize },
|
|
@@ -332,9 +351,16 @@ function GeneratedDesktopList({ definition }: { definition: GeneratedDefinition
|
|
|
332
351
|
const navigate = useNavigate();
|
|
333
352
|
const { message } = App.useApp();
|
|
334
353
|
const { hasCapability, identity } = useRuntime();
|
|
335
|
-
const state = useGeneratedList(definition);
|
|
336
354
|
const { code, name, surface, capabilities } = definition;
|
|
337
|
-
const fields = listSurfaceFields(surface.fields)
|
|
355
|
+
const fields = listSurfaceFields(surface.fields).filter((field) =>
|
|
356
|
+
fieldReadable(field, hasCapability, identity.isAppSuperAdmin)
|
|
357
|
+
);
|
|
358
|
+
const readableFieldCodes = new Set(
|
|
359
|
+
Object.entries(surface.fields)
|
|
360
|
+
.filter(([, field]) => fieldReadable({ key: '', ...field }, hasCapability, identity.isAppSuperAdmin))
|
|
361
|
+
.map(([key]) => key)
|
|
362
|
+
);
|
|
363
|
+
const state = useGeneratedList(definition, readableFieldCodes);
|
|
338
364
|
const [exporting, setExporting] = useState(false);
|
|
339
365
|
const [filtersExpanded, setFiltersExpanded] = useState(false);
|
|
340
366
|
const [density, setDensity] = useState<'small' | 'middle' | 'large'>('middle');
|
|
@@ -447,7 +473,7 @@ function GeneratedDesktopList({ definition }: { definition: GeneratedDefinition
|
|
|
447
473
|
const handleExport = async () => {
|
|
448
474
|
setExporting(true);
|
|
449
475
|
try {
|
|
450
|
-
const result = await exportResourceRows(code, name, surface, state.query, state.sort);
|
|
476
|
+
const result = await exportResourceRows(code, name, surface, fields, state.query, state.sort);
|
|
451
477
|
message.success(result.truncated ? `已导出 ${result.count} 条(最多导出 10000 条)` : `已导出 ${result.count} 条`);
|
|
452
478
|
} catch (error) {
|
|
453
479
|
message.error(error instanceof Error ? error.message : '导出失败');
|
|
@@ -455,7 +481,12 @@ function GeneratedDesktopList({ definition }: { definition: GeneratedDefinition
|
|
|
455
481
|
setExporting(false);
|
|
456
482
|
}
|
|
457
483
|
};
|
|
458
|
-
const filterFields = surface.list?.filterFields || []
|
|
484
|
+
const filterFields = (surface.list?.filterFields || []).filter((field) =>
|
|
485
|
+
readableFieldCodes.has(field)
|
|
486
|
+
);
|
|
487
|
+
const searchableFields = (surface.list?.searchableFields || []).filter((field) =>
|
|
488
|
+
readableFieldCodes.has(field)
|
|
489
|
+
);
|
|
459
490
|
const hasMoreFilters = filterFields.length > 2;
|
|
460
491
|
const columnMenu: MenuProps = {
|
|
461
492
|
items: fields.map((field) => ({ key: field.key, label: field.label })),
|
|
@@ -505,7 +536,7 @@ function GeneratedDesktopList({ definition }: { definition: GeneratedDefinition
|
|
|
505
536
|
<div className="oxa-filter-item">
|
|
506
537
|
<Select
|
|
507
538
|
allowClear
|
|
508
|
-
options={
|
|
539
|
+
options={searchableFields.map((key) => ({
|
|
509
540
|
label: fieldFor(surface, key).label,
|
|
510
541
|
value: key,
|
|
511
542
|
}))}
|
|
@@ -679,17 +710,24 @@ function GeneratedDesktopList({ definition }: { definition: GeneratedDefinition
|
|
|
679
710
|
|
|
680
711
|
function GeneratedMobileList({ definition }: { definition: GeneratedDefinition }) {
|
|
681
712
|
const navigate = useNavigate();
|
|
682
|
-
const { hasCapability } = useRuntime();
|
|
713
|
+
const { hasCapability, identity } = useRuntime();
|
|
683
714
|
const { message } = App.useApp();
|
|
684
715
|
const { code, name, surface, capabilities } = definition;
|
|
685
|
-
const
|
|
716
|
+
const readableFieldCodes = new Set(
|
|
717
|
+
Object.entries(surface.fields)
|
|
718
|
+
.filter(([, field]) => fieldReadable({ key: '', ...field }, hasCapability, identity.isAppSuperAdmin))
|
|
719
|
+
.map(([key]) => key)
|
|
720
|
+
);
|
|
721
|
+
const state = useGeneratedList(definition, readableFieldCodes);
|
|
686
722
|
const [exporting, setExporting] = useState(false);
|
|
687
723
|
const rows = (state.list.result.data || []) as unknown as ResourceRecord[];
|
|
688
|
-
const fields = listSurfaceFields(surface.fields)
|
|
724
|
+
const fields = listSurfaceFields(surface.fields)
|
|
725
|
+
.filter((field) => fieldReadable(field, hasCapability, identity.isAppSuperAdmin))
|
|
726
|
+
.slice(0, 4);
|
|
689
727
|
const handleExport = async () => {
|
|
690
728
|
setExporting(true);
|
|
691
729
|
try {
|
|
692
|
-
const result = await exportResourceRows(code, name, surface, state.query, state.sort);
|
|
730
|
+
const result = await exportResourceRows(code, name, surface, fields, state.query, state.sort);
|
|
693
731
|
message.success(result.truncated ? `已导出 ${result.count} 条(最多导出 10000 条)` : `已导出 ${result.count} 条`);
|
|
694
732
|
} catch (error) {
|
|
695
733
|
message.error(error instanceof Error ? error.message : '导出失败');
|
|
@@ -833,7 +871,7 @@ function GeneratedResourceFormPage({
|
|
|
833
871
|
Object.fromEntries(
|
|
834
872
|
Object.entries(values).filter(([key]) =>
|
|
835
873
|
fieldWritable(
|
|
836
|
-
surface
|
|
874
|
+
fieldFor(surface, key),
|
|
837
875
|
mode,
|
|
838
876
|
hasCapability,
|
|
839
877
|
identity.isAppSuperAdmin
|
|
@@ -878,7 +916,13 @@ function GeneratedResourceFormPage({
|
|
|
878
916
|
onFinish={(values) => void submit(values as Record<string, unknown>)}
|
|
879
917
|
>
|
|
880
918
|
<div className="oxa-sections">
|
|
881
|
-
{fieldsBySection(surface).map((group) => (
|
|
919
|
+
{fieldsBySection(surface).map((group) => ({
|
|
920
|
+
...group,
|
|
921
|
+
fields: group.fields.filter((field) =>
|
|
922
|
+
fieldReadable(field, hasCapability, identity.isAppSuperAdmin) ||
|
|
923
|
+
fieldWritable(field, mode, hasCapability, identity.isAppSuperAdmin)
|
|
924
|
+
),
|
|
925
|
+
})).filter((group) => group.fields.length > 0).map((group) => (
|
|
882
926
|
<Card className="oxa-section-card" key={group.section} title={group.section === 'default' ? '基本信息' : group.section}>
|
|
883
927
|
<div className="oxa-grid">
|
|
884
928
|
{group.fields.map((field) => (
|
|
@@ -911,6 +955,7 @@ function GeneratedResourceFormPage({
|
|
|
911
955
|
function GeneratedResourceDetailPage({ definition, variant }: { definition: GeneratedDefinition; variant: 'desktop' | 'mobile' }) {
|
|
912
956
|
const { id = '' } = useParams();
|
|
913
957
|
const navigate = useNavigate();
|
|
958
|
+
const { hasCapability, identity } = useRuntime();
|
|
914
959
|
const query = useOne<ResourceRecord>({
|
|
915
960
|
resource: definition.code,
|
|
916
961
|
id,
|
|
@@ -970,14 +1015,26 @@ function GeneratedResourceDetailPage({ definition, variant }: { definition: Gene
|
|
|
970
1015
|
<Spin size="small" /> 正在读取操作记录
|
|
971
1016
|
</div>
|
|
972
1017
|
) : auditEntries.length ? (
|
|
973
|
-
auditEntries.slice(0, 5).map((entry) =>
|
|
1018
|
+
auditEntries.slice(0, 5).map((entry) => (
|
|
1019
|
+
<AuditEntry
|
|
1020
|
+
entry={entry}
|
|
1021
|
+
key={entry.id}
|
|
1022
|
+
readable={(field) => fieldReadable(field, hasCapability, identity.isAppSuperAdmin)}
|
|
1023
|
+
surface={definition.surface}
|
|
1024
|
+
/>
|
|
1025
|
+
))
|
|
974
1026
|
) : (
|
|
975
1027
|
<Typography.Text type="secondary">暂无变更记录</Typography.Text>
|
|
976
1028
|
);
|
|
977
1029
|
const content = record ? (
|
|
978
1030
|
<div className="oxa-detail-layout">
|
|
979
1031
|
<div className="oxa-detail-main">
|
|
980
|
-
{fieldsBySection(definition.surface).map((group) => (
|
|
1032
|
+
{fieldsBySection(definition.surface).map((group) => ({
|
|
1033
|
+
...group,
|
|
1034
|
+
fields: group.fields.filter((field) =>
|
|
1035
|
+
fieldReadable(field, hasCapability, identity.isAppSuperAdmin)
|
|
1036
|
+
),
|
|
1037
|
+
})).filter((group) => group.fields.length > 0).map((group) => (
|
|
981
1038
|
<Card className="oxa-section-card" key={group.section} title={group.section === 'default' ? '基本信息' : group.section}>
|
|
982
1039
|
<Descriptions
|
|
983
1040
|
column={{ xs: 1, sm: 2, lg: 3 }}
|
package/template/package.json
CHANGED
|
@@ -22,9 +22,9 @@
|
|
|
22
22
|
"build": "pnpm --recursive build"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
|
-
"openxiangda-cli": "2.0.0-alpha.
|
|
26
|
-
"openxiangda-contracts": "2.0.0-alpha.
|
|
27
|
-
"openxiangda-devkit-core": "2.0.0-alpha.
|
|
25
|
+
"openxiangda-cli": "2.0.0-alpha.80",
|
|
26
|
+
"openxiangda-contracts": "2.0.0-alpha.36",
|
|
27
|
+
"openxiangda-devkit-core": "2.0.0-alpha.47",
|
|
28
28
|
"typescript": "5.9.3"
|
|
29
29
|
}
|
|
30
30
|
}
|