aios-management-web 0.1.0

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.
Files changed (91) hide show
  1. package/.env.json +21 -0
  2. package/README.md +257 -0
  3. package/data/management-console.db +0 -0
  4. package/data/management-console.db-shm +0 -0
  5. package/data/management-console.db-wal +0 -0
  6. package/dist/assets/index-CV_wjCAG.js +464 -0
  7. package/dist/assets/index-DfMPB0eV.css +1 -0
  8. package/dist/index.html +13 -0
  9. package/docs/spec.md +199 -0
  10. package/index.html +12 -0
  11. package/package.json +37 -0
  12. package/scripts/reset-kernel.js +59 -0
  13. package/scripts/reset-password.js +22 -0
  14. package/server/fakes.js +57 -0
  15. package/server/index.js +21 -0
  16. package/server/src/api/middleware/auth.js +29 -0
  17. package/server/src/api/middleware/internal.js +44 -0
  18. package/server/src/api/routes/index.js +677 -0
  19. package/server/src/app.js +90 -0
  20. package/server/src/background/index.js +106 -0
  21. package/server/src/background/protocol.js +15 -0
  22. package/server/src/config/env.js +90 -0
  23. package/server/src/db/index.js +501 -0
  24. package/server/src/infra/mqtt/management-rpc-client.js +213 -0
  25. package/server/src/infra/providers/hzg-provider-client.js +39 -0
  26. package/server/src/infra/s3/object-storage.js +97 -0
  27. package/server/src/services/agent-quota.js +54 -0
  28. package/server/src/services/agent-service.js +696 -0
  29. package/server/src/services/agent-status-sync-service.js +132 -0
  30. package/server/src/services/audit-log-service.js +39 -0
  31. package/server/src/services/auth-service.js +153 -0
  32. package/server/src/services/catalog-sync-service.js +712 -0
  33. package/server/src/services/external-service.js +308 -0
  34. package/server/src/services/kernel-reset-service.js +86 -0
  35. package/server/src/services/portal-service.js +555 -0
  36. package/server/src/services/system-service.js +580 -0
  37. package/server/src/services/topic-ping-service.js +282 -0
  38. package/server/src/utils/errors.js +36 -0
  39. package/server/src/utils/security.js +22 -0
  40. package/server/test/agent-service-alignment.test.js +316 -0
  41. package/server/test/agent-service-create.test.js +662 -0
  42. package/server/test/agent-status-sync-service.test.js +167 -0
  43. package/server/test/agent-update-audit.test.js +63 -0
  44. package/server/test/auth-middleware.test.js +71 -0
  45. package/server/test/background-services.test.js +160 -0
  46. package/server/test/catalog-sync-service.test.js +920 -0
  47. package/server/test/db-reset-migration.test.js +123 -0
  48. package/server/test/env-config.test.js +68 -0
  49. package/server/test/external-service.test.js +380 -0
  50. package/server/test/hzg-provider-client.test.js +50 -0
  51. package/server/test/internal-auth-middleware.test.js +66 -0
  52. package/server/test/kernel-reset-service.test.js +112 -0
  53. package/server/test/management-rpc-client.test.js +105 -0
  54. package/server/test/portal-service-access-tokens.test.js +121 -0
  55. package/server/test/portal-service-alignment.test.js +318 -0
  56. package/server/test/portal-service-management-logs.test.js +114 -0
  57. package/server/test/reset-kernel-cli.test.js +23 -0
  58. package/server/test/service-api-auth-middleware.test.js +59 -0
  59. package/server/test/system-service-alignment.test.js +265 -0
  60. package/server/test/topic-ping-service.test.js +182 -0
  61. package/server/test/usage-refresh-audit-route.test.js +82 -0
  62. package/src/App.jsx +1 -0
  63. package/src/api.js +1 -0
  64. package/src/app/App.jsx +346 -0
  65. package/src/app/api-client.js +112 -0
  66. package/src/components/AppShell.jsx +117 -0
  67. package/src/components/CardTitleWithReload.jsx +20 -0
  68. package/src/components/DeleteActionButton.jsx +31 -0
  69. package/src/main.jsx +14 -0
  70. package/src/pages/AgentsPage.jsx +647 -0
  71. package/src/pages/AiosUsersPage.jsx +151 -0
  72. package/src/pages/DashboardPage.jsx +72 -0
  73. package/src/pages/LoginPage.jsx +41 -0
  74. package/src/pages/SettingsPage.jsx +431 -0
  75. package/src/pages/SkillsPage.jsx +175 -0
  76. package/src/pages/SystemLogsPage.jsx +349 -0
  77. package/src/pages/SystemsPage.jsx +498 -0
  78. package/src/pages/TemplatesPage.jsx +207 -0
  79. package/src/pages/UserManagementPage.jsx +25 -0
  80. package/src/pages/UsersPage.jsx +192 -0
  81. package/src/pages/system-logs/SystemLogsTabs.jsx +362 -0
  82. package/src/styles.css +222 -0
  83. package/src/utils/format.js +63 -0
  84. package/test/.reports/fast-2026-05-25T08-32-39-420Z.json +299 -0
  85. package/test/integration/common.js +208 -0
  86. package/test/integration/fast.js +135 -0
  87. package/test/integration/full.js +306 -0
  88. package/test/run-browser-e2e.js +212 -0
  89. package/test/run-jasmine.js +21 -0
  90. package/test/setup.js +1 -0
  91. package/vite.config.js +12 -0
@@ -0,0 +1,25 @@
1
+ import React from "react";
2
+ import { Tabs } from "antd";
3
+
4
+ import { AiosUsersPage } from "./AiosUsersPage.jsx";
5
+ import { UsersPage } from "./UsersPage.jsx";
6
+
7
+ export function UserManagementPage({ currentUser }) {
8
+ return (
9
+ <Tabs
10
+ defaultActiveKey="aiosUsers"
11
+ items={[
12
+ {
13
+ key: "aiosUsers",
14
+ label: "AIOS 用户",
15
+ children: <AiosUsersPage />
16
+ },
17
+ {
18
+ key: "users",
19
+ label: "AIOS 管理员",
20
+ children: <UsersPage currentUser={currentUser} />
21
+ }
22
+ ]}
23
+ />
24
+ );
25
+ }
@@ -0,0 +1,192 @@
1
+ import React, { useEffect, useState } from "react";
2
+ import { Button, Card, Form, Input, Modal, Select, Space, Table, Tag, message } from "antd";
3
+
4
+ import { api } from "../app/api-client.js";
5
+ import { CardTitleWithReload } from "../components/CardTitleWithReload.jsx";
6
+ import { DeleteActionButton } from "../components/DeleteActionButton.jsx";
7
+
8
+ function isBuiltinAdmin(row) {
9
+ return row.is_builtin || row.username === "aios";
10
+ }
11
+
12
+ function renderUserStatus(status) {
13
+ return <Tag color={status === "active" ? "green" : "default"}>{status === "active" ? "启用" : "停用"}</Tag>;
14
+ }
15
+
16
+ export function UsersPage({ currentUser }) {
17
+ const [items, setItems] = useState([]);
18
+ const [loading, setLoading] = useState(false);
19
+ const [editing, setEditing] = useState(null);
20
+ const [open, setOpen] = useState(false);
21
+ const [form] = Form.useForm();
22
+ const [messageApi, contextHolder] = message.useMessage();
23
+
24
+ const load = async () => {
25
+ setLoading(true);
26
+ try {
27
+ setItems(await api.get("/api/users?role=aios-admin"));
28
+ } finally {
29
+ setLoading(false);
30
+ }
31
+ };
32
+
33
+ useEffect(() => {
34
+ void load();
35
+ }, []);
36
+
37
+ const isSelf = (row) => Number(row.id) === Number(currentUser?.id);
38
+
39
+ return (
40
+ <>
41
+ {contextHolder}
42
+ <Card
43
+ className="page-card"
44
+ title={<CardTitleWithReload title="AIOS 管理员" loading={loading} onReload={() => load()} />}
45
+ extra={(
46
+ <Button
47
+ type="primary"
48
+ onClick={() => {
49
+ setEditing(null);
50
+ form.resetFields();
51
+ setOpen(true);
52
+ }}
53
+ >
54
+ 新建管理员账号
55
+ </Button>
56
+ )}
57
+ >
58
+ <Table
59
+ rowKey="id"
60
+ dataSource={items}
61
+ loading={loading}
62
+ pagination={{
63
+ pageSize: 50,
64
+ showSizeChanger: false
65
+ }}
66
+ columns={[
67
+ { title: "用户名", dataIndex: "username" },
68
+ { title: "显示名", dataIndex: "display_name" },
69
+ { title: "状态", dataIndex: "status", render: renderUserStatus },
70
+ {
71
+ title: "操作",
72
+ render: (_, row) => (
73
+ <Space>
74
+ {!isBuiltinAdmin(row) ? (
75
+ <Button
76
+ size="small"
77
+ onClick={() => {
78
+ setEditing(row);
79
+ form.setFieldsValue({
80
+ display_name: row.display_name,
81
+ status: row.status
82
+ });
83
+ setOpen(true);
84
+ }}
85
+ >
86
+ 编辑
87
+ </Button>
88
+ ) : null}
89
+ {!isBuiltinAdmin(row) ? (
90
+ <Button
91
+ size="small"
92
+ onClick={async () => {
93
+ try {
94
+ await api.post(`/api/users/${row.id}/reset-password`, { password: "123456" });
95
+ messageApi.success(`已将用户 ${row.username} 的密码重置为 123456`);
96
+ await load();
97
+ } catch (error) {
98
+ messageApi.error(error.message || "重置密码失败");
99
+ }
100
+ }}
101
+ >
102
+ 重置密码
103
+ </Button>
104
+ ) : null}
105
+ <DeleteActionButton
106
+ hidden={isBuiltinAdmin(row) || isSelf(row)}
107
+ title={`确认删除管理员账号 ${row.username} 吗?`}
108
+ description="删除后将无法恢复该管理员账号。"
109
+ onConfirm={async () => {
110
+ try {
111
+ await api.delete(`/api/users/${row.id}`);
112
+ messageApi.success(`已删除管理员账号 ${row.username}`);
113
+ await load();
114
+ } catch (error) {
115
+ messageApi.error(error.message || "删除管理员账号失败");
116
+ }
117
+ }}
118
+ />
119
+ </Space>
120
+ )
121
+ }
122
+ ]}
123
+ />
124
+ <Modal
125
+ open={open}
126
+ title={editing ? "编辑管理员账号" : "新建管理员账号"}
127
+ okText="确认"
128
+ cancelText="取消"
129
+ onCancel={() => {
130
+ setOpen(false);
131
+ form.resetFields();
132
+ }}
133
+ onOk={async () => {
134
+ try {
135
+ const values = await form.validateFields();
136
+ if (editing) {
137
+ await api.put(`/api/users/${editing.id}`, values);
138
+ messageApi.success(`已更新管理员账号 ${editing.username}`);
139
+ } else {
140
+ await api.post("/api/users", {
141
+ username: values.username,
142
+ display_name: values.display_name,
143
+ password: values.password,
144
+ role: "aios-admin"
145
+ });
146
+ messageApi.success(`已创建管理员账号 ${values.username}`);
147
+ }
148
+ setOpen(false);
149
+ form.resetFields();
150
+ await load();
151
+ } catch (error) {
152
+ if (error?.errorFields) {
153
+ return;
154
+ }
155
+ messageApi.error(error.message || "保存管理员账号失败");
156
+ }
157
+ }}
158
+ >
159
+ <Form form={form} layout="vertical">
160
+ {!editing ? (
161
+ <>
162
+ <Form.Item name="username" label="用户名" rules={[{ required: true, message: "请输入用户名" }]}>
163
+ <Input />
164
+ </Form.Item>
165
+ <Form.Item name="display_name" label="显示名" rules={[{ required: true, message: "请输入显示名" }]}>
166
+ <Input />
167
+ </Form.Item>
168
+ <Form.Item name="password" label="密码">
169
+ <Input.Password placeholder="留空则默认使用 123456" />
170
+ </Form.Item>
171
+ </>
172
+ ) : (
173
+ <>
174
+ <Form.Item name="display_name" label="显示名" rules={[{ required: true, message: "请输入显示名" }]}>
175
+ <Input />
176
+ </Form.Item>
177
+ <Form.Item name="status" label="状态" rules={[{ required: true, message: "请选择状态" }]}>
178
+ <Select
179
+ options={[
180
+ { value: "active", label: "启用" },
181
+ { value: "disabled", label: "停用" }
182
+ ]}
183
+ />
184
+ </Form.Item>
185
+ </>
186
+ )}
187
+ </Form>
188
+ </Modal>
189
+ </Card>
190
+ </>
191
+ );
192
+ }
@@ -0,0 +1,362 @@
1
+ import React, { useEffect, useState } from "react";
2
+ import { Button, Card, Descriptions, Drawer, Space, Table, Tabs, Tag, message } from "antd";
3
+
4
+ import { api } from "../../app/api-client.js";
5
+ import { CardTitleWithReload } from "../../components/CardTitleWithReload.jsx";
6
+ import { formatDateTime } from "../../utils/format.js";
7
+
8
+ const jsonBlockStyle = {
9
+ margin: 0,
10
+ padding: 12,
11
+ borderRadius: 8,
12
+ background: "#fafafa",
13
+ border: "1px solid #f0f0f0",
14
+ overflowX: "auto",
15
+ whiteSpace: "pre-wrap",
16
+ wordBreak: "break-word",
17
+ fontSize: 12,
18
+ lineHeight: 1.6
19
+ };
20
+
21
+ function formatJson(value) {
22
+ if (value === undefined || value === null) {
23
+ return "-";
24
+ }
25
+
26
+ try {
27
+ return JSON.stringify(value, null, 2);
28
+ } catch {
29
+ return String(value);
30
+ }
31
+ }
32
+
33
+ function renderResultTag(value) {
34
+ if (value === null || value === undefined) {
35
+ return <Tag>处理中</Tag>;
36
+ }
37
+
38
+ return <Tag color={value ? "green" : "red"}>{value ? "成功" : "失败"}</Tag>;
39
+ }
40
+
41
+ function getSystemInvocationDetailItems(log) {
42
+ if (!log) {
43
+ return [];
44
+ }
45
+
46
+ return [
47
+ { key: "created_at", label: "时间", children: formatDateTime(log.created_at) },
48
+ { key: "provider", label: "提供方", children: log.provider || "-" },
49
+ { key: "application_name", label: "应用名", children: log.application_name || "-" },
50
+ { key: "command_name", label: "指令", children: log.command_name || "-" },
51
+ { key: "agent_slug", label: "数字员工", children: log.agent_slug || "-" },
52
+ { key: "session_id", label: "会话 ID", children: log.session_id || "-" },
53
+ { key: "trace_id", label: "请求追踪 ID", children: log.trace_id || "-" },
54
+ {
55
+ key: "response_time_ms",
56
+ label: "耗时",
57
+ children: typeof log.response_time_ms === "number" ? `${log.response_time_ms} ms` : "-"
58
+ },
59
+ {
60
+ key: "success",
61
+ label: "实际成功",
62
+ children: renderResultTag(log.success)
63
+ },
64
+ { key: "error_message", label: "错误", children: log.error_message || "-" }
65
+ ];
66
+ }
67
+
68
+ function getKernelInvocationDetailItems(log) {
69
+ if (!log) {
70
+ return [];
71
+ }
72
+
73
+ return [
74
+ { key: "request_id", label: "请求 ID", children: log.request_id || "-" },
75
+ { key: "created_at", label: "发送时间", children: formatDateTime(log.created_at) },
76
+ { key: "completed_at", label: "完成时间", children: formatDateTime(log.completed_at) },
77
+ { key: "action", label: "指令", children: log.action || "-" },
78
+ {
79
+ key: "ok",
80
+ label: "实际成功",
81
+ children: renderResultTag(log.ok)
82
+ },
83
+ {
84
+ key: "response_time_ms",
85
+ label: "耗时",
86
+ children: typeof log.response_time_ms === "number" ? `${log.response_time_ms} ms` : "-"
87
+ }
88
+ ];
89
+ }
90
+
91
+ export function SystemLogsTabs({ defaultActiveKey = "audit" }) {
92
+ const [messageApi, contextHolder] = message.useMessage();
93
+ const [auditItems, setAuditItems] = useState([]);
94
+ const [auditPage, setAuditPage] = useState(1);
95
+ const [auditPageSize] = useState(50);
96
+ const [auditTotal, setAuditTotal] = useState(0);
97
+ const [auditLoading, setAuditLoading] = useState(false);
98
+
99
+ const [systemLogs, setSystemLogs] = useState([]);
100
+ const [systemLogsLoading, setSystemLogsLoading] = useState(false);
101
+ const [systemLogDetail, setSystemLogDetail] = useState(null);
102
+ const [systemLogDetailOpen, setSystemLogDetailOpen] = useState(false);
103
+
104
+ const [kernelItems, setKernelItems] = useState([]);
105
+ const [kernelPage, setKernelPage] = useState(1);
106
+ const [kernelPageSize] = useState(50);
107
+ const [kernelTotal, setKernelTotal] = useState(0);
108
+ const [kernelLoading, setKernelLoading] = useState(false);
109
+ const [kernelDetail, setKernelDetail] = useState(null);
110
+ const [kernelDetailOpen, setKernelDetailOpen] = useState(false);
111
+
112
+ const reportLoadError = (error, fallbackMessage) => {
113
+ const normalized = api.normalizeError(error, fallbackMessage);
114
+ messageApi.error(normalized.message || fallbackMessage);
115
+ };
116
+
117
+ const loadAuditLogs = async (nextPage = auditPage) => {
118
+ setAuditLoading(true);
119
+ try {
120
+ const result = await api.get(`/api/audit-logs?page=${nextPage}&pageSize=${auditPageSize}`);
121
+ setAuditItems(result.items || []);
122
+ setAuditTotal(result.total || 0);
123
+ setAuditPage(result.page || nextPage);
124
+ } catch (error) {
125
+ reportLoadError(error, "加载审计日志失败");
126
+ } finally {
127
+ setAuditLoading(false);
128
+ }
129
+ };
130
+
131
+ const loadSystemInvocationLogs = async () => {
132
+ setSystemLogsLoading(true);
133
+ try {
134
+ const result = await api.get("/api/systems/logs");
135
+ setSystemLogs(Array.isArray(result) ? result : []);
136
+ } catch (error) {
137
+ reportLoadError(error, "加载业务系统调用日志失败");
138
+ } finally {
139
+ setSystemLogsLoading(false);
140
+ }
141
+ };
142
+
143
+ const loadKernelLogs = async (nextPage = kernelPage) => {
144
+ setKernelLoading(true);
145
+ try {
146
+ const result = await api.get(`/api/kernel/logs?page=${nextPage}&pageSize=${kernelPageSize}`);
147
+ setKernelItems(result.items || []);
148
+ setKernelTotal(result.total || 0);
149
+ setKernelPage(result.page || nextPage);
150
+ } catch (error) {
151
+ reportLoadError(error, "加载内核调用日志失败");
152
+ } finally {
153
+ setKernelLoading(false);
154
+ }
155
+ };
156
+
157
+ useEffect(() => {
158
+ void Promise.all([
159
+ loadAuditLogs(1),
160
+ loadSystemInvocationLogs(),
161
+ loadKernelLogs(1)
162
+ ]);
163
+ }, []);
164
+
165
+ return (
166
+ <>
167
+ {contextHolder}
168
+ <Tabs
169
+ defaultActiveKey={defaultActiveKey}
170
+ items={[
171
+ {
172
+ key: "audit",
173
+ label: "审计日志",
174
+ children: (
175
+ <Card
176
+ className="page-card"
177
+ title={<CardTitleWithReload title="审计日志" loading={auditLoading} onReload={() => loadAuditLogs()} />}
178
+ >
179
+ <Table
180
+ rowKey="id"
181
+ loading={auditLoading}
182
+ dataSource={auditItems}
183
+ pagination={{
184
+ current: auditPage,
185
+ pageSize: auditPageSize,
186
+ total: auditTotal,
187
+ showSizeChanger: false,
188
+ onChange: (nextPage) => {
189
+ void loadAuditLogs(nextPage);
190
+ }
191
+ }}
192
+ columns={[
193
+ { title: "时间", dataIndex: "created_at", render: formatDateTime, width: 180 },
194
+ { title: "操作人", dataIndex: "username", width: 160 },
195
+ { title: "操作", dataIndex: "action", width: 220 },
196
+ { title: "详情", dataIndex: "detail" }
197
+ ]}
198
+ />
199
+ </Card>
200
+ )
201
+ },
202
+ {
203
+ key: "system",
204
+ label: "业务系统调用日志",
205
+ children: (
206
+ <Card
207
+ className="page-card"
208
+ title={<CardTitleWithReload title="业务系统调用日志" loading={systemLogsLoading} onReload={() => loadSystemInvocationLogs()} />}
209
+ >
210
+ <Table
211
+ rowKey="trace_id"
212
+ loading={systemLogsLoading}
213
+ dataSource={systemLogs}
214
+ pagination={{
215
+ pageSize: 50,
216
+ showSizeChanger: false
217
+ }}
218
+ columns={[
219
+ { title: "时间", dataIndex: "created_at", render: formatDateTime },
220
+ { title: "应用名", dataIndex: "application_name" },
221
+ { title: "数字员工", dataIndex: "agent_slug", render: (value) => value || "-" },
222
+ { title: "接口", render: (_, row) => `${row.provider}/${row.application_name}/${row.command_name}` },
223
+ {
224
+ title: "耗时",
225
+ dataIndex: "response_time_ms",
226
+ render: (value) => (typeof value === "number" ? `${value} ms` : "-")
227
+ },
228
+ {
229
+ title: "实际成功",
230
+ dataIndex: "success",
231
+ render: renderResultTag
232
+ },
233
+ {
234
+ title: "操作",
235
+ render: (_, row) => (
236
+ <Button
237
+ size="small"
238
+ onClick={() => {
239
+ setSystemLogDetail(row);
240
+ setSystemLogDetailOpen(true);
241
+ }}
242
+ >
243
+ 详情
244
+ </Button>
245
+ )
246
+ }
247
+ ]}
248
+ />
249
+ </Card>
250
+ )
251
+ },
252
+ {
253
+ key: "kernel",
254
+ label: "内核调用日志",
255
+ children: (
256
+ <Card
257
+ className="page-card"
258
+ title={<CardTitleWithReload title="内核调用日志" loading={kernelLoading} onReload={() => loadKernelLogs()} />}
259
+ >
260
+ <Table
261
+ rowKey="request_id"
262
+ loading={kernelLoading}
263
+ dataSource={kernelItems}
264
+ pagination={{
265
+ current: kernelPage,
266
+ pageSize: kernelPageSize,
267
+ total: kernelTotal,
268
+ showSizeChanger: false,
269
+ onChange: (nextPage) => {
270
+ void loadKernelLogs(nextPage);
271
+ }
272
+ }}
273
+ columns={[
274
+ { title: "请求 ID", dataIndex: "request_id" },
275
+ { title: "发送时间", dataIndex: "created_at", render: formatDateTime, width: 180 },
276
+ { title: "指令", dataIndex: "action" },
277
+ {
278
+ title: "实际成功",
279
+ dataIndex: "ok",
280
+ width: 120,
281
+ render: renderResultTag
282
+ },
283
+ {
284
+ title: "操作",
285
+ width: 100,
286
+ render: (_, row) => (
287
+ <Button
288
+ size="small"
289
+ onClick={() => {
290
+ setKernelDetail(row);
291
+ setKernelDetailOpen(true);
292
+ }}
293
+ >
294
+ 详情
295
+ </Button>
296
+ )
297
+ }
298
+ ]}
299
+ />
300
+ </Card>
301
+ )
302
+ }
303
+ ]}
304
+ />
305
+ <Drawer
306
+ open={systemLogDetailOpen}
307
+ title="业务系统调用详情"
308
+ width={860}
309
+ onClose={() => {
310
+ setSystemLogDetailOpen(false);
311
+ setSystemLogDetail(null);
312
+ }}
313
+ >
314
+ <Space direction="vertical" size={16} style={{ width: "100%" }}>
315
+ <Card className="page-card" title="基本信息">
316
+ <Descriptions
317
+ bordered
318
+ size="small"
319
+ column={1}
320
+ items={getSystemInvocationDetailItems(systemLogDetail)}
321
+ />
322
+ </Card>
323
+ <Card className="page-card" title="请求参数">
324
+ <pre style={jsonBlockStyle}>{formatJson(systemLogDetail?.request_payload)}</pre>
325
+ </Card>
326
+ <Card className="page-card" title="返回结果">
327
+ <pre style={jsonBlockStyle}>{formatJson(systemLogDetail?.response_payload)}</pre>
328
+ </Card>
329
+ </Space>
330
+ </Drawer>
331
+ <Drawer
332
+ open={kernelDetailOpen}
333
+ title="内核调用详情"
334
+ width={860}
335
+ onClose={() => {
336
+ setKernelDetailOpen(false);
337
+ setKernelDetail(null);
338
+ }}
339
+ >
340
+ <Space direction="vertical" size={16} style={{ width: "100%" }}>
341
+ <Card className="page-card" title="基本信息">
342
+ <Descriptions
343
+ bordered
344
+ size="small"
345
+ column={1}
346
+ items={getKernelInvocationDetailItems(kernelDetail)}
347
+ />
348
+ </Card>
349
+ <Card className="page-card" title="参数">
350
+ <pre style={jsonBlockStyle}>{formatJson(kernelDetail?.params)}</pre>
351
+ </Card>
352
+ <Card className="page-card" title="返回结果">
353
+ <pre style={jsonBlockStyle}>{formatJson(kernelDetail?.result)}</pre>
354
+ </Card>
355
+ <Card className="page-card" title="错误">
356
+ <pre style={jsonBlockStyle}>{formatJson(kernelDetail?.error)}</pre>
357
+ </Card>
358
+ </Space>
359
+ </Drawer>
360
+ </>
361
+ );
362
+ }