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.
- package/.env.json +21 -0
- package/README.md +257 -0
- package/data/management-console.db +0 -0
- package/data/management-console.db-shm +0 -0
- package/data/management-console.db-wal +0 -0
- package/dist/assets/index-CV_wjCAG.js +464 -0
- package/dist/assets/index-DfMPB0eV.css +1 -0
- package/dist/index.html +13 -0
- package/docs/spec.md +199 -0
- package/index.html +12 -0
- package/package.json +37 -0
- package/scripts/reset-kernel.js +59 -0
- package/scripts/reset-password.js +22 -0
- package/server/fakes.js +57 -0
- package/server/index.js +21 -0
- package/server/src/api/middleware/auth.js +29 -0
- package/server/src/api/middleware/internal.js +44 -0
- package/server/src/api/routes/index.js +677 -0
- package/server/src/app.js +90 -0
- package/server/src/background/index.js +106 -0
- package/server/src/background/protocol.js +15 -0
- package/server/src/config/env.js +90 -0
- package/server/src/db/index.js +501 -0
- package/server/src/infra/mqtt/management-rpc-client.js +213 -0
- package/server/src/infra/providers/hzg-provider-client.js +39 -0
- package/server/src/infra/s3/object-storage.js +97 -0
- package/server/src/services/agent-quota.js +54 -0
- package/server/src/services/agent-service.js +696 -0
- package/server/src/services/agent-status-sync-service.js +132 -0
- package/server/src/services/audit-log-service.js +39 -0
- package/server/src/services/auth-service.js +153 -0
- package/server/src/services/catalog-sync-service.js +712 -0
- package/server/src/services/external-service.js +308 -0
- package/server/src/services/kernel-reset-service.js +86 -0
- package/server/src/services/portal-service.js +555 -0
- package/server/src/services/system-service.js +580 -0
- package/server/src/services/topic-ping-service.js +282 -0
- package/server/src/utils/errors.js +36 -0
- package/server/src/utils/security.js +22 -0
- package/server/test/agent-service-alignment.test.js +316 -0
- package/server/test/agent-service-create.test.js +662 -0
- package/server/test/agent-status-sync-service.test.js +167 -0
- package/server/test/agent-update-audit.test.js +63 -0
- package/server/test/auth-middleware.test.js +71 -0
- package/server/test/background-services.test.js +160 -0
- package/server/test/catalog-sync-service.test.js +920 -0
- package/server/test/db-reset-migration.test.js +123 -0
- package/server/test/env-config.test.js +68 -0
- package/server/test/external-service.test.js +380 -0
- package/server/test/hzg-provider-client.test.js +50 -0
- package/server/test/internal-auth-middleware.test.js +66 -0
- package/server/test/kernel-reset-service.test.js +112 -0
- package/server/test/management-rpc-client.test.js +105 -0
- package/server/test/portal-service-access-tokens.test.js +121 -0
- package/server/test/portal-service-alignment.test.js +318 -0
- package/server/test/portal-service-management-logs.test.js +114 -0
- package/server/test/reset-kernel-cli.test.js +23 -0
- package/server/test/service-api-auth-middleware.test.js +59 -0
- package/server/test/system-service-alignment.test.js +265 -0
- package/server/test/topic-ping-service.test.js +182 -0
- package/server/test/usage-refresh-audit-route.test.js +82 -0
- package/src/App.jsx +1 -0
- package/src/api.js +1 -0
- package/src/app/App.jsx +346 -0
- package/src/app/api-client.js +112 -0
- package/src/components/AppShell.jsx +117 -0
- package/src/components/CardTitleWithReload.jsx +20 -0
- package/src/components/DeleteActionButton.jsx +31 -0
- package/src/main.jsx +14 -0
- package/src/pages/AgentsPage.jsx +647 -0
- package/src/pages/AiosUsersPage.jsx +151 -0
- package/src/pages/DashboardPage.jsx +72 -0
- package/src/pages/LoginPage.jsx +41 -0
- package/src/pages/SettingsPage.jsx +431 -0
- package/src/pages/SkillsPage.jsx +175 -0
- package/src/pages/SystemLogsPage.jsx +349 -0
- package/src/pages/SystemsPage.jsx +498 -0
- package/src/pages/TemplatesPage.jsx +207 -0
- package/src/pages/UserManagementPage.jsx +25 -0
- package/src/pages/UsersPage.jsx +192 -0
- package/src/pages/system-logs/SystemLogsTabs.jsx +362 -0
- package/src/styles.css +222 -0
- package/src/utils/format.js +63 -0
- package/test/.reports/fast-2026-05-25T08-32-39-420Z.json +299 -0
- package/test/integration/common.js +208 -0
- package/test/integration/fast.js +135 -0
- package/test/integration/full.js +306 -0
- package/test/run-browser-e2e.js +212 -0
- package/test/run-jasmine.js +21 -0
- package/test/setup.js +1 -0
- package/vite.config.js +12 -0
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
|
|
4
|
+
import mqtt from "mqtt";
|
|
5
|
+
|
|
6
|
+
const DEFAULT_BASE_URL = "http://127.0.0.1:3030";
|
|
7
|
+
const DEFAULT_ARTIFACT_ROOT = "E:\\tmp\\aios4";
|
|
8
|
+
|
|
9
|
+
export function loadSettings() {
|
|
10
|
+
const artifactRoot = path.resolve(process.env.AIOS_TEST_ARTIFACT_ROOT || DEFAULT_ARTIFACT_ROOT);
|
|
11
|
+
const envJsonPath = path.resolve(process.env.AIOS_TEST_ENV_JSON || ".env.json");
|
|
12
|
+
const envConfig = JSON.parse(fs.readFileSync(envJsonPath, "utf8"));
|
|
13
|
+
const moduleDir = path.dirname(new URL(import.meta.url).pathname.replace(/^\//, ""));
|
|
14
|
+
const reportDir = path.resolve(process.env.AIOS_TEST_REPORT_DIR || path.join(moduleDir, "..", ".reports"));
|
|
15
|
+
|
|
16
|
+
return {
|
|
17
|
+
baseUrl: process.env.AIOS_WEB_BASE_URL || DEFAULT_BASE_URL,
|
|
18
|
+
artifactRoot,
|
|
19
|
+
envJsonPath,
|
|
20
|
+
envConfig,
|
|
21
|
+
reportDir,
|
|
22
|
+
ontologyZip: path.join(artifactRoot, ".ontology-demo", "hzg-ontology-demo-wms.zip"),
|
|
23
|
+
templateZip: path.join(artifactRoot, ".workspace-demo", "finance.zip"),
|
|
24
|
+
skipCleanup: process.env.AIOS_TEST_SKIP_CLEANUP === "1"
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function newReport(name, extra = {}) {
|
|
29
|
+
return {
|
|
30
|
+
name,
|
|
31
|
+
startedAt: new Date().toISOString(),
|
|
32
|
+
actions: [],
|
|
33
|
+
cleanup: [],
|
|
34
|
+
error: null,
|
|
35
|
+
...extra
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function addAction(report, action, ok, status, body, note) {
|
|
40
|
+
report.actions.push({
|
|
41
|
+
action,
|
|
42
|
+
ok,
|
|
43
|
+
status,
|
|
44
|
+
body,
|
|
45
|
+
note
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export async function runCleanup(report, action, fn) {
|
|
50
|
+
try {
|
|
51
|
+
const result = await fn();
|
|
52
|
+
report.cleanup.push({
|
|
53
|
+
action,
|
|
54
|
+
ok: true,
|
|
55
|
+
result
|
|
56
|
+
});
|
|
57
|
+
} catch (error) {
|
|
58
|
+
report.cleanup.push({
|
|
59
|
+
action,
|
|
60
|
+
ok: false,
|
|
61
|
+
error: error instanceof Error ? error.message : String(error)
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function finishReport(report) {
|
|
67
|
+
report.finishedAt = new Date().toISOString();
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function writeReport(settings, report) {
|
|
71
|
+
fs.mkdirSync(settings.reportDir, { recursive: true });
|
|
72
|
+
const stamp = new Date().toISOString().replace(/[:.]/g, "-");
|
|
73
|
+
const filePath = path.join(settings.reportDir, `${report.name}-${stamp}.json`);
|
|
74
|
+
fs.writeFileSync(filePath, `${JSON.stringify(report, null, 2)}\n`, "utf8");
|
|
75
|
+
return filePath;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export class HttpSession {
|
|
79
|
+
constructor(baseUrl) {
|
|
80
|
+
this.baseUrl = baseUrl;
|
|
81
|
+
this.token = "";
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
setToken(token) {
|
|
85
|
+
this.token = token;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async request(method, route, options = {}) {
|
|
89
|
+
const headers = new Headers(options.headers || {});
|
|
90
|
+
if (this.token) {
|
|
91
|
+
headers.set("Authorization", `Bearer ${this.token}`);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
let body = undefined;
|
|
95
|
+
if (options.json !== undefined) {
|
|
96
|
+
headers.set("Content-Type", "application/json");
|
|
97
|
+
body = JSON.stringify(options.json);
|
|
98
|
+
} else if (options.formData) {
|
|
99
|
+
body = options.formData;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const response = await fetch(`${this.baseUrl}${route}`, {
|
|
103
|
+
method,
|
|
104
|
+
headers,
|
|
105
|
+
body
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
if (response.status === 204) {
|
|
109
|
+
return {
|
|
110
|
+
ok: response.ok,
|
|
111
|
+
status: response.status,
|
|
112
|
+
body: null
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const payload = await response.json();
|
|
117
|
+
return {
|
|
118
|
+
ok: response.ok,
|
|
119
|
+
status: response.status,
|
|
120
|
+
body: payload
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
get(route) {
|
|
125
|
+
return this.request("GET", route);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
post(route, json) {
|
|
129
|
+
return this.request("POST", route, { json });
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
put(route, json) {
|
|
133
|
+
return this.request("PUT", route, { json });
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
delete(route) {
|
|
137
|
+
return this.request("DELETE", route);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
multipart(method, route, fields, fileField) {
|
|
141
|
+
const form = new FormData();
|
|
142
|
+
for (const [key, value] of Object.entries(fields)) {
|
|
143
|
+
if (value === undefined || value === null) {
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
form.set(key, Array.isArray(value) ? JSON.stringify(value) : String(value));
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (fileField) {
|
|
150
|
+
const buffer = fs.readFileSync(fileField.path);
|
|
151
|
+
form.set(fileField.name, new Blob([buffer]), fileField.filename);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return this.request(method, route, { formData: form });
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export async function loginAsAdmin(session, report) {
|
|
159
|
+
const response = await session.post("/api/auth/login", {
|
|
160
|
+
username: "aios",
|
|
161
|
+
password: "123456"
|
|
162
|
+
});
|
|
163
|
+
addAction(report, "auth.login", response.ok, response.status, response.body);
|
|
164
|
+
if (!response.ok) {
|
|
165
|
+
throw new Error(`auth.login failed: ${JSON.stringify(response.body)}`);
|
|
166
|
+
}
|
|
167
|
+
session.setToken(response.body.token);
|
|
168
|
+
return response.body;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export async function mqttPublish(envConfig, topic, payload) {
|
|
172
|
+
await new Promise((resolve, reject) => {
|
|
173
|
+
const client = mqtt.connect(envConfig.AIOS_MQTT_CHANNEL_BROKER, {
|
|
174
|
+
clientId: `aios-web-it-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
|
|
175
|
+
username: envConfig.AIOS_MQTT_CHANNEL_USERNAME || undefined,
|
|
176
|
+
password: envConfig.AIOS_MQTT_CHANNEL_PASSWORD || undefined,
|
|
177
|
+
clean: true,
|
|
178
|
+
reconnectPeriod: 1000
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
const fail = (error) => {
|
|
182
|
+
client.end(true);
|
|
183
|
+
reject(error);
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
client.once("error", fail);
|
|
187
|
+
client.once("connect", () => {
|
|
188
|
+
client.publish(topic, JSON.stringify(payload), { qos: 1, retain: false }, (error) => {
|
|
189
|
+
if (error) {
|
|
190
|
+
fail(error);
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
client.end(true, {}, () => resolve());
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
export function buildAgentTopics(envConfig, agentId) {
|
|
200
|
+
return {
|
|
201
|
+
inbound: envConfig.AIOS_AGENT_CHANNEL_INBOUND_TOPIC_TEMPLATE.replace("{agentId}", agentId),
|
|
202
|
+
outbound: envConfig.AIOS_AGENT_CHANNEL_OUTBOUND_TOPIC_TEMPLATE.replace("{agentId}", agentId)
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
export function findBy(items, key, value) {
|
|
207
|
+
return Array.isArray(items) ? items.find((item) => item?.[key] === value) : undefined;
|
|
208
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import {
|
|
2
|
+
HttpSession,
|
|
3
|
+
addAction,
|
|
4
|
+
finishReport,
|
|
5
|
+
findBy,
|
|
6
|
+
loadSettings,
|
|
7
|
+
loginAsAdmin,
|
|
8
|
+
newReport,
|
|
9
|
+
runCleanup,
|
|
10
|
+
writeReport
|
|
11
|
+
} from "./common.js";
|
|
12
|
+
|
|
13
|
+
async function main() {
|
|
14
|
+
const settings = loadSettings();
|
|
15
|
+
const report = newReport("fast", {
|
|
16
|
+
baseUrl: settings.baseUrl,
|
|
17
|
+
ontologyZip: settings.ontologyZip,
|
|
18
|
+
templateZip: settings.templateZip
|
|
19
|
+
});
|
|
20
|
+
const session = new HttpSession(settings.baseUrl);
|
|
21
|
+
const stamp = String(Date.now());
|
|
22
|
+
const templateName = `tmpl${stamp}`;
|
|
23
|
+
const agentSlug = `agent${stamp}`;
|
|
24
|
+
const applicationName = `app-${stamp}`;
|
|
25
|
+
|
|
26
|
+
let templateRow = null;
|
|
27
|
+
let agentRow = null;
|
|
28
|
+
let systemRow = null;
|
|
29
|
+
|
|
30
|
+
try {
|
|
31
|
+
await loginAsAdmin(session, report);
|
|
32
|
+
|
|
33
|
+
const templateCreate = await session.multipart("POST", "/api/templates", {
|
|
34
|
+
template_name: templateName,
|
|
35
|
+
description: "fast integration template"
|
|
36
|
+
}, {
|
|
37
|
+
name: "artifact",
|
|
38
|
+
path: settings.templateZip,
|
|
39
|
+
filename: "finance.zip"
|
|
40
|
+
});
|
|
41
|
+
addAction(report, "templates.create", templateCreate.ok, templateCreate.status, templateCreate.body);
|
|
42
|
+
if (!templateCreate.ok) {
|
|
43
|
+
throw new Error(`templates.create failed: ${JSON.stringify(templateCreate.body)}`);
|
|
44
|
+
}
|
|
45
|
+
templateRow = templateCreate.body;
|
|
46
|
+
|
|
47
|
+
const agentCreate = await session.post("/api/agents", {
|
|
48
|
+
slug: agentSlug,
|
|
49
|
+
agent_name: "Fast Integration Agent",
|
|
50
|
+
template_name: templateName,
|
|
51
|
+
description: "fast integration agent",
|
|
52
|
+
docs_content: "# Agent\n\nFast integration test.\n",
|
|
53
|
+
tags: ["fast"],
|
|
54
|
+
permission_usernames: ["zhangsan"],
|
|
55
|
+
skill_slugs: [],
|
|
56
|
+
restart: true
|
|
57
|
+
});
|
|
58
|
+
addAction(report, "agents.create", agentCreate.ok && agentCreate.body?.status === "normal", agentCreate.status, agentCreate.body);
|
|
59
|
+
if (!agentCreate.ok) {
|
|
60
|
+
throw new Error(`agents.create failed: ${JSON.stringify(agentCreate.body)}`);
|
|
61
|
+
}
|
|
62
|
+
agentRow = agentCreate.body;
|
|
63
|
+
|
|
64
|
+
const syncStatus = await session.post("/api/settings/agent-sync", {});
|
|
65
|
+
addAction(report, "settings.agent-sync", syncStatus.ok && syncStatus.body?.status === "success", syncStatus.status, syncStatus.body);
|
|
66
|
+
if (!syncStatus.ok) {
|
|
67
|
+
throw new Error(`settings.agent-sync failed: ${JSON.stringify(syncStatus.body)}`);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const systemCreate = await session.multipart("POST", "/api/systems", {
|
|
71
|
+
provider: "hzg",
|
|
72
|
+
application_name: applicationName,
|
|
73
|
+
description: "fast integration system",
|
|
74
|
+
scheme: "https",
|
|
75
|
+
host: "example.com",
|
|
76
|
+
port: 443,
|
|
77
|
+
status: "active"
|
|
78
|
+
}, {
|
|
79
|
+
name: "ontology",
|
|
80
|
+
path: settings.ontologyZip,
|
|
81
|
+
filename: "hzg-ontology-demo-wms.zip"
|
|
82
|
+
});
|
|
83
|
+
addAction(report, "systems.create", systemCreate.ok, systemCreate.status, systemCreate.body);
|
|
84
|
+
if (!systemCreate.ok) {
|
|
85
|
+
throw new Error(`systems.create failed: ${JSON.stringify(systemCreate.body)}`);
|
|
86
|
+
}
|
|
87
|
+
systemRow = systemCreate.body;
|
|
88
|
+
|
|
89
|
+
const templatesList = await session.get("/api/templates");
|
|
90
|
+
addAction(report, "templates.list", templatesList.ok && Boolean(findBy(templatesList.body, "template_name", templateName)), templatesList.status, templatesList.body);
|
|
91
|
+
|
|
92
|
+
const agentsList = await session.get("/api/agents");
|
|
93
|
+
addAction(report, "agents.list", agentsList.ok && Boolean(findBy(agentsList.body, "slug", agentSlug)), agentsList.status, agentsList.body);
|
|
94
|
+
|
|
95
|
+
const internalAgents = await session.get("/api/internal/cui/agents");
|
|
96
|
+
const normalAgent = Array.isArray(internalAgents.body?.items)
|
|
97
|
+
? findBy(internalAgents.body.items, "agent_id", agentSlug)
|
|
98
|
+
: null;
|
|
99
|
+
addAction(
|
|
100
|
+
report,
|
|
101
|
+
"internal.cui.agents",
|
|
102
|
+
internalAgents.ok
|
|
103
|
+
&& Boolean(normalAgent),
|
|
104
|
+
internalAgents.status,
|
|
105
|
+
internalAgents.body
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
const systemsList = await session.get("/api/systems");
|
|
109
|
+
addAction(report, "systems.list", systemsList.ok && Boolean(findBy(systemsList.body, "application_name", applicationName)), systemsList.status, systemsList.body);
|
|
110
|
+
} catch (error) {
|
|
111
|
+
report.error = error instanceof Error ? error.message : String(error);
|
|
112
|
+
} finally {
|
|
113
|
+
if (!settings.skipCleanup) {
|
|
114
|
+
if (systemRow?.id) {
|
|
115
|
+
await runCleanup(report, "systems.delete", async () => (await session.delete(`/api/systems/${systemRow.id}`)).body);
|
|
116
|
+
}
|
|
117
|
+
if (agentRow?.id) {
|
|
118
|
+
await runCleanup(report, "agents.delete", async () => (await session.delete(`/api/agents/${agentRow.id}`)).body);
|
|
119
|
+
}
|
|
120
|
+
if (templateRow?.template_name) {
|
|
121
|
+
await runCleanup(report, "templates.delete", async () => (await session.delete(`/api/templates/${templateRow.template_name}`)).body);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
finishReport(report);
|
|
126
|
+
const reportPath = writeReport(settings, report);
|
|
127
|
+
console.log(reportPath);
|
|
128
|
+
process.exit(report.error ? 1 : 0);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
main().catch((error) => {
|
|
133
|
+
console.error(error instanceof Error ? error.stack : String(error));
|
|
134
|
+
process.exit(1);
|
|
135
|
+
});
|
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
import {
|
|
2
|
+
HttpSession,
|
|
3
|
+
addAction,
|
|
4
|
+
buildAgentTopics,
|
|
5
|
+
finishReport,
|
|
6
|
+
findBy,
|
|
7
|
+
loadSettings,
|
|
8
|
+
loginAsAdmin,
|
|
9
|
+
mqttPublish,
|
|
10
|
+
newReport,
|
|
11
|
+
runCleanup,
|
|
12
|
+
writeReport
|
|
13
|
+
} from "./common.js";
|
|
14
|
+
|
|
15
|
+
function assertOk(response, label) {
|
|
16
|
+
if (!response.ok) {
|
|
17
|
+
throw new Error(`${label} failed: ${JSON.stringify(response.body)}`);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async function main() {
|
|
22
|
+
const settings = loadSettings();
|
|
23
|
+
const report = newReport("full", {
|
|
24
|
+
baseUrl: settings.baseUrl,
|
|
25
|
+
skipped: ["skills"]
|
|
26
|
+
});
|
|
27
|
+
const session = new HttpSession(settings.baseUrl);
|
|
28
|
+
const stamp = String(Date.now());
|
|
29
|
+
const templateName = `tmpl${stamp}`;
|
|
30
|
+
const agentSlug = `agent${stamp}`;
|
|
31
|
+
const applicationName = `app-${stamp}`;
|
|
32
|
+
const username = `user${stamp}`;
|
|
33
|
+
const sessionId = `session-${stamp}`;
|
|
34
|
+
const traceId = `invoke-${stamp}`;
|
|
35
|
+
|
|
36
|
+
let templateRow = null;
|
|
37
|
+
let agentRow = null;
|
|
38
|
+
let systemRow = null;
|
|
39
|
+
let userRow = null;
|
|
40
|
+
|
|
41
|
+
try {
|
|
42
|
+
await loginAsAdmin(session, report);
|
|
43
|
+
|
|
44
|
+
for (const route of ["/api/auth/me", "/api/bootstrap", "/api/dashboard", "/api/settings"]) {
|
|
45
|
+
const response = await session.get(route);
|
|
46
|
+
addAction(report, route, response.ok, response.status, response.body);
|
|
47
|
+
assertOk(response, route);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const usersInitial = await session.get("/api/users");
|
|
51
|
+
addAction(report, "users.list.initial", usersInitial.ok && Array.isArray(usersInitial.body) && usersInitial.body.length >= 3, usersInitial.status, usersInitial.body);
|
|
52
|
+
assertOk(usersInitial, "users.list.initial");
|
|
53
|
+
|
|
54
|
+
const userCreate = await session.post("/api/users", {
|
|
55
|
+
role: "aios-admin",
|
|
56
|
+
username,
|
|
57
|
+
display_name: "Integration User",
|
|
58
|
+
status: "active",
|
|
59
|
+
password: "12345678",
|
|
60
|
+
tags: ["it"]
|
|
61
|
+
});
|
|
62
|
+
addAction(report, "users.create", userCreate.ok, userCreate.status, userCreate.body);
|
|
63
|
+
assertOk(userCreate, "users.create");
|
|
64
|
+
userRow = userCreate.body;
|
|
65
|
+
|
|
66
|
+
const userUpdate = await session.put(`/api/users/${userRow.id}`, {
|
|
67
|
+
display_name: "Integration User Updated",
|
|
68
|
+
status: "disabled",
|
|
69
|
+
tags: ["it", "disabled"]
|
|
70
|
+
});
|
|
71
|
+
addAction(report, "users.update", userUpdate.ok && userUpdate.body?.status === "disabled", userUpdate.status, userUpdate.body);
|
|
72
|
+
assertOk(userUpdate, "users.update");
|
|
73
|
+
|
|
74
|
+
const templateCreate = await session.multipart("POST", "/api/templates", {
|
|
75
|
+
template_name: templateName,
|
|
76
|
+
description: "full integration template"
|
|
77
|
+
}, {
|
|
78
|
+
name: "artifact",
|
|
79
|
+
path: settings.templateZip,
|
|
80
|
+
filename: "finance.zip"
|
|
81
|
+
});
|
|
82
|
+
addAction(report, "templates.create", templateCreate.ok, templateCreate.status, templateCreate.body);
|
|
83
|
+
assertOk(templateCreate, "templates.create");
|
|
84
|
+
templateRow = templateCreate.body;
|
|
85
|
+
|
|
86
|
+
const templatesList = await session.get("/api/templates");
|
|
87
|
+
addAction(report, "templates.list.verify", templatesList.ok && Boolean(findBy(templatesList.body, "template_name", templateName)), templatesList.status, templatesList.body);
|
|
88
|
+
assertOk(templatesList, "templates.list.verify");
|
|
89
|
+
|
|
90
|
+
const agentCreate = await session.post("/api/agents", {
|
|
91
|
+
slug: agentSlug,
|
|
92
|
+
agent_name: "Integration Agent",
|
|
93
|
+
template_name: templateName,
|
|
94
|
+
description: "integration agent",
|
|
95
|
+
docs_content: "# Agent\n\nIntegration test.\n",
|
|
96
|
+
tags: ["it"],
|
|
97
|
+
permission_usernames: ["zhangsan"],
|
|
98
|
+
skill_slugs: [],
|
|
99
|
+
restart: true
|
|
100
|
+
});
|
|
101
|
+
addAction(report, "agents.create", agentCreate.ok && agentCreate.body?.status === "normal", agentCreate.status, agentCreate.body);
|
|
102
|
+
assertOk(agentCreate, "agents.create");
|
|
103
|
+
agentRow = agentCreate.body;
|
|
104
|
+
|
|
105
|
+
const agentsList = await session.get("/api/agents");
|
|
106
|
+
addAction(report, "agents.list.verify", agentsList.ok && Boolean(findBy(agentsList.body, "slug", agentSlug)), agentsList.status, agentsList.body);
|
|
107
|
+
assertOk(agentsList, "agents.list.verify");
|
|
108
|
+
|
|
109
|
+
const topics = buildAgentTopics(settings.envConfig, agentSlug);
|
|
110
|
+
const syncStatus = await session.post("/api/settings/agent-sync", {});
|
|
111
|
+
addAction(report, "settings.agent-sync", syncStatus.ok && syncStatus.body?.status === "success", syncStatus.status, syncStatus.body);
|
|
112
|
+
assertOk(syncStatus, "settings.agent-sync");
|
|
113
|
+
|
|
114
|
+
const internalAgents = await session.get("/api/internal/cui/agents");
|
|
115
|
+
const normalAgent = Array.isArray(internalAgents.body?.items)
|
|
116
|
+
? findBy(internalAgents.body.items, "agent_id", agentSlug)
|
|
117
|
+
: null;
|
|
118
|
+
addAction(
|
|
119
|
+
report,
|
|
120
|
+
"internal.cui.agents.normal-visible",
|
|
121
|
+
internalAgents.ok
|
|
122
|
+
&& Boolean(normalAgent),
|
|
123
|
+
internalAgents.status,
|
|
124
|
+
internalAgents.body
|
|
125
|
+
);
|
|
126
|
+
assertOk(internalAgents, "internal.cui.agents.normal-visible");
|
|
127
|
+
await mqttPublish(settings.envConfig, topics.inbound, {
|
|
128
|
+
senderId: "employee-a",
|
|
129
|
+
senderName: "Employee A",
|
|
130
|
+
sessionId,
|
|
131
|
+
traceId: `trace-in-${stamp}`,
|
|
132
|
+
text: "please see file_input://admin-in/test/sample.txt"
|
|
133
|
+
});
|
|
134
|
+
await mqttPublish(settings.envConfig, topics.outbound, {
|
|
135
|
+
sessionId,
|
|
136
|
+
traceId: `trace-out-${stamp}`,
|
|
137
|
+
text: "processed file_output://admin-out/test/result.txt"
|
|
138
|
+
});
|
|
139
|
+
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
140
|
+
|
|
141
|
+
const threads = await session.get("/api/audit/threads");
|
|
142
|
+
const thread = Array.isArray(threads.body)
|
|
143
|
+
? threads.body.find((item) => item.agent_slug === agentSlug && item.session_id === sessionId)
|
|
144
|
+
: null;
|
|
145
|
+
addAction(report, "audit.threads", threads.ok && Boolean(thread), threads.status, threads.body);
|
|
146
|
+
assertOk(threads, "audit.threads");
|
|
147
|
+
if (!thread) {
|
|
148
|
+
throw new Error("audit thread not found");
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const messages = await session.get(`/api/audit/threads/${thread.id}/messages`);
|
|
152
|
+
addAction(report, "audit.messages", messages.ok && Array.isArray(messages.body) && messages.body.length >= 2, messages.status, messages.body);
|
|
153
|
+
assertOk(messages, "audit.messages");
|
|
154
|
+
|
|
155
|
+
const files = await session.get("/api/files?q=file_");
|
|
156
|
+
const fileHits = Array.isArray(files.body) ? files.body.filter((item) => item.agent_slug === agentSlug) : [];
|
|
157
|
+
addAction(report, "files.query", files.ok && fileHits.length >= 2, files.status, files.body);
|
|
158
|
+
assertOk(files, "files.query");
|
|
159
|
+
if (fileHits.length < 2) {
|
|
160
|
+
throw new Error("audit files missing");
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const agentDisable = await session.put(`/api/agents/${agentRow.id}`, {
|
|
164
|
+
agent_name: "Integration Agent Updated",
|
|
165
|
+
description: "integration agent updated",
|
|
166
|
+
docs_content: "# Agent\n\nUpdated.\n",
|
|
167
|
+
status: "disabled",
|
|
168
|
+
tags: ["it", "updated"],
|
|
169
|
+
permission_usernames: ["lisi"],
|
|
170
|
+
skill_slugs: [],
|
|
171
|
+
restart: true
|
|
172
|
+
});
|
|
173
|
+
addAction(report, "agents.update.disable", agentDisable.ok && agentDisable.body?.status === "disabled", agentDisable.status, agentDisable.body);
|
|
174
|
+
assertOk(agentDisable, "agents.update.disable");
|
|
175
|
+
|
|
176
|
+
const internalAgentsDisabled = await session.get("/api/internal/cui/agents");
|
|
177
|
+
const disabledAgent = Array.isArray(internalAgentsDisabled.body?.items)
|
|
178
|
+
? findBy(internalAgentsDisabled.body.items, "agent_id", agentSlug)
|
|
179
|
+
: null;
|
|
180
|
+
addAction(
|
|
181
|
+
report,
|
|
182
|
+
"internal.cui.agents.disabled-filtered",
|
|
183
|
+
internalAgentsDisabled.ok && !disabledAgent,
|
|
184
|
+
internalAgentsDisabled.status,
|
|
185
|
+
internalAgentsDisabled.body
|
|
186
|
+
);
|
|
187
|
+
assertOk(internalAgentsDisabled, "internal.cui.agents.disabled-filtered");
|
|
188
|
+
|
|
189
|
+
const systemCreate = await session.multipart("POST", "/api/systems", {
|
|
190
|
+
provider: "hzg",
|
|
191
|
+
application_name: applicationName,
|
|
192
|
+
description: "integration system",
|
|
193
|
+
scheme: "https",
|
|
194
|
+
host: "example.com",
|
|
195
|
+
port: 443,
|
|
196
|
+
status: "active"
|
|
197
|
+
}, {
|
|
198
|
+
name: "ontology",
|
|
199
|
+
path: settings.ontologyZip,
|
|
200
|
+
filename: "hzg-ontology-demo-wms.zip"
|
|
201
|
+
});
|
|
202
|
+
addAction(report, "systems.create", systemCreate.ok, systemCreate.status, systemCreate.body);
|
|
203
|
+
assertOk(systemCreate, "systems.create");
|
|
204
|
+
systemRow = systemCreate.body;
|
|
205
|
+
|
|
206
|
+
const systemUpdate = await session.multipart("PUT", `/api/systems/${systemRow.id}`, {
|
|
207
|
+
description: "integration system updated",
|
|
208
|
+
host: "service2.example.com",
|
|
209
|
+
status: "disabled"
|
|
210
|
+
});
|
|
211
|
+
addAction(report, "systems.update", systemUpdate.ok, systemUpdate.status, systemUpdate.body);
|
|
212
|
+
assertOk(systemUpdate, "systems.update");
|
|
213
|
+
|
|
214
|
+
const systemEnable = await session.post(`/api/systems/${systemRow.id}/status`, {
|
|
215
|
+
status: "active"
|
|
216
|
+
});
|
|
217
|
+
addAction(report, "systems.status.active", systemEnable.ok && systemEnable.body?.status === "active", systemEnable.status, systemEnable.body);
|
|
218
|
+
assertOk(systemEnable, "systems.status.active");
|
|
219
|
+
|
|
220
|
+
const systemTest = await session.request("POST", `/api/systems/${systemRow.id}/test`);
|
|
221
|
+
addAction(report, "systems.test", systemTest.ok && systemTest.body?.ok === true, systemTest.status, systemTest.body);
|
|
222
|
+
assertOk(systemTest, "systems.test");
|
|
223
|
+
|
|
224
|
+
const internalSystem = await session.get(`/api/internal/app-invoke/base-url?applicationName=${applicationName}`);
|
|
225
|
+
addAction(report, "internal.app-invoke.base-url", internalSystem.ok && internalSystem.body?.baseUrl === `https://service2.example.com:443/${applicationName}`, internalSystem.status, internalSystem.body);
|
|
226
|
+
assertOk(internalSystem, "internal.app-invoke.base-url");
|
|
227
|
+
|
|
228
|
+
const internalLog = await session.post("/api/internal/app-invoke/logs", {
|
|
229
|
+
trace_id: traceId,
|
|
230
|
+
agent_slug: agentSlug,
|
|
231
|
+
session_id: sessionId,
|
|
232
|
+
provider: "hzg",
|
|
233
|
+
application_name: applicationName,
|
|
234
|
+
command_name: "ping",
|
|
235
|
+
request_payload: { hello: "world" },
|
|
236
|
+
response_payload: { ok: true },
|
|
237
|
+
response_time_ms: 123,
|
|
238
|
+
success: true
|
|
239
|
+
});
|
|
240
|
+
addAction(report, "internal.app-invoke.logs", internalLog.ok, internalLog.status, internalLog.body);
|
|
241
|
+
assertOk(internalLog, "internal.app-invoke.logs");
|
|
242
|
+
|
|
243
|
+
const systemsLogs = await session.get(`/api/systems/logs?application_name=${applicationName}&agent_slug=${agentSlug}`);
|
|
244
|
+
const foundLog = Array.isArray(systemsLogs.body) ? systemsLogs.body.find((item) => item.trace_id === traceId) : null;
|
|
245
|
+
addAction(report, "systems.logs", systemsLogs.ok && Boolean(foundLog), systemsLogs.status, systemsLogs.body);
|
|
246
|
+
assertOk(systemsLogs, "systems.logs");
|
|
247
|
+
|
|
248
|
+
const systemsStats = await session.get(`/api/systems/stats?application_name=${applicationName}&agent_slug=${agentSlug}`);
|
|
249
|
+
addAction(report, "systems.stats", systemsStats.ok && Array.isArray(systemsStats.body) && systemsStats.body.length >= 1, systemsStats.status, systemsStats.body);
|
|
250
|
+
assertOk(systemsStats, "systems.stats");
|
|
251
|
+
|
|
252
|
+
const settingsUpdate = await session.put("/api/settings", {
|
|
253
|
+
portal_name: "AIOS IT",
|
|
254
|
+
brand_subtitle: "Integration",
|
|
255
|
+
theme_color: "#123456"
|
|
256
|
+
});
|
|
257
|
+
addAction(report, "settings.update", settingsUpdate.ok && settingsUpdate.body?.theme_color === "#123456", settingsUpdate.status, settingsUpdate.body);
|
|
258
|
+
assertOk(settingsUpdate, "settings.update");
|
|
259
|
+
|
|
260
|
+
const dashboardVerify = await session.get("/api/dashboard");
|
|
261
|
+
addAction(
|
|
262
|
+
report,
|
|
263
|
+
"dashboard.verify",
|
|
264
|
+
dashboardVerify.ok
|
|
265
|
+
&& dashboardVerify.body?.stats?.agents >= 1
|
|
266
|
+
&& dashboardVerify.body?.stats?.systems >= 1
|
|
267
|
+
&& dashboardVerify.body?.stats?.threads >= 1,
|
|
268
|
+
dashboardVerify.status,
|
|
269
|
+
dashboardVerify.body
|
|
270
|
+
);
|
|
271
|
+
assertOk(dashboardVerify, "dashboard.verify");
|
|
272
|
+
|
|
273
|
+
const logout = await session.request("POST", "/api/auth/logout");
|
|
274
|
+
addAction(report, "auth.logout", logout.status === 204, logout.status, logout.body);
|
|
275
|
+
} catch (error) {
|
|
276
|
+
report.error = error instanceof Error ? error.message : String(error);
|
|
277
|
+
} finally {
|
|
278
|
+
if (!settings.skipCleanup) {
|
|
279
|
+
if (systemRow?.id) {
|
|
280
|
+
await runCleanup(report, "systems.delete", async () => (await session.delete(`/api/systems/${systemRow.id}`)).body);
|
|
281
|
+
}
|
|
282
|
+
if (agentRow?.id) {
|
|
283
|
+
await runCleanup(report, "agents.delete", async () => (await session.delete(`/api/agents/${agentRow.id}`)).body);
|
|
284
|
+
}
|
|
285
|
+
if (templateRow?.template_name) {
|
|
286
|
+
await runCleanup(report, "templates.delete", async () => (await session.delete(`/api/templates/${templateRow.template_name}`)).body);
|
|
287
|
+
}
|
|
288
|
+
if (userRow?.id) {
|
|
289
|
+
await runCleanup(report, "users.delete", async () => {
|
|
290
|
+
const response = await session.delete(`/api/users/${userRow.id}`);
|
|
291
|
+
return { status: response.status };
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
finishReport(report);
|
|
297
|
+
const reportPath = writeReport(settings, report);
|
|
298
|
+
console.log(reportPath);
|
|
299
|
+
process.exit(report.error ? 1 : 0);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
main().catch((error) => {
|
|
304
|
+
console.error(error instanceof Error ? error.stack : String(error));
|
|
305
|
+
process.exit(1);
|
|
306
|
+
});
|