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,90 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+
4
+ import express from "express";
5
+
6
+ import { createRoutes } from "./api/routes/index.js";
7
+ import { loadEnv } from "./config/env.js";
8
+ import { db } from "./db/index.js";
9
+ import { ManagementRpcClient } from "./infra/mqtt/management-rpc-client.js";
10
+ import { HzgProviderClient } from "./infra/providers/hzg-provider-client.js";
11
+ import { ObjectStorage } from "./infra/s3/object-storage.js";
12
+ import { AgentService } from "./services/agent-service.js";
13
+ import { AgentStatusSyncService } from "./services/agent-status-sync-service.js";
14
+ import { AuditLogService } from "./services/audit-log-service.js";
15
+ import { AuthService } from "./services/auth-service.js";
16
+ import { CatalogSyncService } from "./services/catalog-sync-service.js";
17
+ import { ExternalService } from "./services/external-service.js";
18
+ import { PortalService } from "./services/portal-service.js";
19
+ import { SystemService } from "./services/system-service.js";
20
+ import { TopicPingService } from "./services/topic-ping-service.js";
21
+ import { AppError } from "./utils/errors.js";
22
+
23
+ export function createServerApp() {
24
+ const env = loadEnv();
25
+ const objectStorage = new ObjectStorage(env);
26
+ const authService = new AuthService({ db, env });
27
+ const rpcClient = new ManagementRpcClient({ env, db });
28
+ const hzgProviderClient = new HzgProviderClient();
29
+ const auditLogService = new AuditLogService({ db });
30
+ const catalogSyncService = new CatalogSyncService({ db, rpcClient, auditLogService, env });
31
+ const agentStatusSyncService = new AgentStatusSyncService({ db, rpcClient, auditLogService });
32
+ const portalService = new PortalService({ db, objectStorage, rpcClient, authService });
33
+ const topicPingService = new TopicPingService({ db, env, rpcClient });
34
+ const agentService = new AgentService({ db, rpcClient, objectStorage, env });
35
+ const systemService = new SystemService({ db, objectStorage, rpcClient });
36
+ const externalService = new ExternalService({
37
+ db,
38
+ agentService,
39
+ systemService,
40
+ hzgProviderClient,
41
+ auditLogService
42
+ });
43
+
44
+ const services = {
45
+ env,
46
+ authService,
47
+ portalService,
48
+ agentService,
49
+ topicPingService,
50
+ auditLogService,
51
+ systemService,
52
+ externalService,
53
+ catalogSyncService,
54
+ agentStatusSyncService,
55
+ rpcClient,
56
+ hzgProviderClient
57
+ };
58
+
59
+ const app = express();
60
+ app.use(express.json({ limit: "5mb" }));
61
+ app.use(express.urlencoded({ extended: true }));
62
+ app.use("/api", createRoutes(services));
63
+
64
+ const distDir = path.resolve("dist");
65
+ if (fs.existsSync(distDir)) {
66
+ app.use(express.static(distDir));
67
+ app.get(/^(?!\/api).*/, (_req, res) => {
68
+ res.sendFile(path.join(distDir, "index.html"));
69
+ });
70
+ }
71
+
72
+ app.use((error, _req, res, _next) => {
73
+ if (error instanceof AppError) {
74
+ res.status(error.status).json({
75
+ code: error.code,
76
+ message: error.message,
77
+ details: error.details
78
+ });
79
+ return;
80
+ }
81
+
82
+ console.error(error);
83
+ res.status(500).json({
84
+ code: "internal_error",
85
+ message: error?.message || "服务器内部错误"
86
+ });
87
+ });
88
+
89
+ return { app, env, services };
90
+ }
@@ -0,0 +1,106 @@
1
+ export function startBackgroundServices({ env, services }) {
2
+ const rpcStarter = services.rpcClient.start();
3
+ const starters = [rpcStarter];
4
+ let schedulerStarterTimer = null;
5
+ let schedulerTimer = null;
6
+ let scheduledTaskIndex = 0;
7
+
8
+ function createStartupTasks() {
9
+ return [
10
+ () => services.catalogSyncService.syncAgentsTask({ trigger: "startup" }),
11
+ () => services.catalogSyncService.refreshAgentUsage({ trigger: "startup" }),
12
+ () => services.catalogSyncService.syncSkillsTask({ trigger: "startup" }),
13
+ () => services.catalogSyncService.syncTemplatesTask({ trigger: "startup" }),
14
+ () => services.catalogSyncService.syncSystemsTask({ trigger: "startup" })
15
+ ];
16
+ }
17
+
18
+ function createScheduledTasks() {
19
+ return [
20
+ {
21
+ label: "agent catalog sync",
22
+ run: () => services.catalogSyncService.syncAgentsTask({ trigger: "scheduled" })
23
+ },
24
+ {
25
+ label: "agent usage refresh",
26
+ run: () => services.catalogSyncService.refreshAgentUsage({ trigger: "scheduled" })
27
+ },
28
+ {
29
+ label: "skill catalog sync",
30
+ run: () => services.catalogSyncService.syncSkillsTask({ trigger: "scheduled" })
31
+ },
32
+ {
33
+ label: "template catalog sync",
34
+ run: () => services.catalogSyncService.syncTemplatesTask({ trigger: "scheduled" })
35
+ },
36
+ {
37
+ label: "ontology catalog sync",
38
+ run: () => services.catalogSyncService.syncSystemsTask({ trigger: "scheduled" })
39
+ }
40
+ ];
41
+ }
42
+
43
+ function scheduleGlobalClock() {
44
+ const startupDelayMs = 5 * 60 * 1000;
45
+ const intervalMs = 60 * 1000;
46
+ const scheduledTasks = createScheduledTasks();
47
+ const runNextScheduledTask = () => {
48
+ const task = scheduledTasks[scheduledTaskIndex];
49
+ scheduledTaskIndex = (scheduledTaskIndex + 1) % scheduledTasks.length;
50
+ task.run().catch((error) => {
51
+ console.error(`Scheduled ${task.label} failed`, error);
52
+ });
53
+ };
54
+
55
+ if (schedulerStarterTimer) {
56
+ clearTimeout(schedulerStarterTimer);
57
+ }
58
+ if (schedulerTimer) {
59
+ clearInterval(schedulerTimer);
60
+ }
61
+
62
+ scheduledTaskIndex = 0;
63
+ schedulerStarterTimer = setTimeout(() => {
64
+ schedulerStarterTimer = null;
65
+ runNextScheduledTask();
66
+ schedulerTimer = setInterval(runNextScheduledTask, intervalMs);
67
+ }, startupDelayMs);
68
+ }
69
+
70
+ Promise.allSettled(starters).then((results) => {
71
+ for (const result of results) {
72
+ if (result.status === "rejected") {
73
+ console.error("Background service failed to start", result.reason);
74
+ }
75
+ }
76
+ });
77
+
78
+ rpcStarter
79
+ .then(async () => {
80
+ for (const run of createStartupTasks()) {
81
+ await run();
82
+ }
83
+ scheduleGlobalClock();
84
+ })
85
+ .catch((error) => {
86
+ console.error("Background startup sync failed", error);
87
+ });
88
+
89
+ return {
90
+ async stop() {
91
+ if (schedulerStarterTimer) {
92
+ clearTimeout(schedulerStarterTimer);
93
+ schedulerStarterTimer = null;
94
+ }
95
+
96
+ if (schedulerTimer) {
97
+ clearInterval(schedulerTimer);
98
+ schedulerTimer = null;
99
+ }
100
+
101
+ await Promise.allSettled([
102
+ services.rpcClient.stop()
103
+ ]);
104
+ }
105
+ };
106
+ }
@@ -0,0 +1,15 @@
1
+ export function newSuccessResponse(traceId, data) {
2
+ return {
3
+ traceId,
4
+ code: 0,
5
+ data
6
+ };
7
+ }
8
+
9
+ export function newErrorResponse(traceId, code, message) {
10
+ return {
11
+ traceId,
12
+ code,
13
+ message
14
+ };
15
+ }
@@ -0,0 +1,90 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+
4
+ let fileEnvCache = null;
5
+
6
+ function loadFileEnv() {
7
+ if (fileEnvCache !== null) {
8
+ return fileEnvCache;
9
+ }
10
+
11
+ const envFilePath = path.resolve(".env.json");
12
+ if (!fs.existsSync(envFilePath)) {
13
+ fileEnvCache = {};
14
+ return fileEnvCache;
15
+ }
16
+
17
+ try {
18
+ const parsed = JSON.parse(fs.readFileSync(envFilePath, "utf8"));
19
+ fileEnvCache = parsed && typeof parsed === "object" ? parsed : {};
20
+ } catch {
21
+ fileEnvCache = {};
22
+ }
23
+
24
+ return fileEnvCache;
25
+ }
26
+
27
+ function readEnvValue(name, fallback = "") {
28
+ const envValue = process.env[name];
29
+ if (envValue !== undefined && envValue !== null && String(envValue).trim() !== "") {
30
+ return String(envValue).trim();
31
+ }
32
+
33
+ const fileEnvValue = loadFileEnv()[name];
34
+ if (fileEnvValue !== undefined && fileEnvValue !== null && String(fileEnvValue).trim() !== "") {
35
+ return String(fileEnvValue).trim();
36
+ }
37
+
38
+ return fallback;
39
+ }
40
+
41
+ function readEnvBoolean(name, fallback = false) {
42
+ const value = readEnvValue(name, fallback ? "true" : "false");
43
+ return ["1", "true", "yes", "on"].includes(value.toLowerCase());
44
+ }
45
+
46
+ function readEnvNumber(name, fallback) {
47
+ const value = Number(readEnvValue(name, String(fallback)));
48
+ return Number.isFinite(value) && value > 0 ? value : fallback;
49
+ }
50
+
51
+ export function loadEnv() {
52
+ const aiosRoot = readEnvValue("AIOS_ROOT", "");
53
+ const defaultDataDir = aiosRoot
54
+ ? path.join(aiosRoot, "data", "web")
55
+ : path.join("data");
56
+ const dataDir = path.resolve(readEnvValue("AIOS_WEB_DATA_DIR", defaultDataDir));
57
+
58
+ return {
59
+ port: 3030,
60
+ dataDir,
61
+ sessionTtlHours: Number(readEnvValue("AIOS_WEB_SESSION_TTL_HOURS", "12")),
62
+ managementTimeoutMs: readEnvNumber("AIOS_MANAGEMENT_WEBSITE_TIMEOUT", 120) * 1000,
63
+ mqtt: {
64
+ brokerUrl: readEnvValue("AIOS_MQTT_CHANNEL_BROKER"),
65
+ username: readEnvValue("AIOS_MQTT_CHANNEL_USERNAME"),
66
+ password: readEnvValue("AIOS_MQTT_CHANNEL_PASSWORD"),
67
+ adminInboundTopic: readEnvValue("AIOS_ADMIN_INBOUND_TOPIC", "aios/admin/inbound"),
68
+ adminOutboundTopic: readEnvValue("AIOS_ADMIN_OUTBOUND_TOPIC", "aios/admin/outbound"),
69
+ agentInboundTopicTemplate: readEnvValue(
70
+ "AIOS_AGENT_CHANNEL_INBOUND_TOPIC_TEMPLATE",
71
+ "aios/agent/{agentId}/inbound"
72
+ ),
73
+ agentOutboundTopicTemplate: readEnvValue(
74
+ "AIOS_AGENT_CHANNEL_OUTBOUND_TOPIC_TEMPLATE",
75
+ "aios/agent/{agentId}/outbound"
76
+ )
77
+ },
78
+ s3: {
79
+ endpoint: readEnvValue("AIOS_S3_ENDPOINT"),
80
+ region: readEnvValue("AIOS_S3_REGION", "local"),
81
+ accessKeyId: readEnvValue("AIOS_S3_ACCESS_KEY_ID"),
82
+ secretAccessKey: readEnvValue("AIOS_S3_SECRET_ACCESS_KEY"),
83
+ forcePathStyle: readEnvBoolean("AIOS_S3_FORCE_PATH_STYLE", true),
84
+ adminInboxBucket: readEnvValue("AIOS_S3_ADMIN_INBOX_BUCKET"),
85
+ adminOutboxBucket: readEnvValue("AIOS_S3_ADMIN_OUTBOX_BUCKET"),
86
+ agentInboxBucket: readEnvValue("AIOS_S3_AGENT_INBOX_BUCKET"),
87
+ agentOutboxBucket: readEnvValue("AIOS_S3_AGENT_OUTBOX_BUCKET")
88
+ }
89
+ };
90
+ }