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,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
+ });