create-windy 0.4.1 → 0.5.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/dist/cli.js +258 -121
- package/package.json +1 -1
- package/template/.windy-template.json +2 -2
- package/template/apps/agent-server/adk_web_agents/canonical_debug/agent.py +29 -9
- package/template/apps/agent-server/adk_web_agents/services.py +25 -0
- package/template/apps/agent-server/src/southwind_agent_server/adk_adapter.py +40 -11
- package/template/apps/agent-server/src/southwind_agent_server/adk_debug_sessions.py +325 -0
- package/template/apps/agent-server/src/southwind_agent_server/adk_model.py +70 -6
- package/template/apps/agent-server/src/southwind_agent_server/adk_tools.py +1 -1
- package/template/apps/agent-server/src/southwind_agent_server/app.py +16 -4
- package/template/apps/agent-server/src/southwind_agent_server/approval_resolution.py +30 -0
- package/template/apps/agent-server/src/southwind_agent_server/contracts.py +9 -0
- package/template/apps/agent-server/src/southwind_agent_server/deterministic.py +2 -1
- package/template/apps/agent-server/src/southwind_agent_server/engine.py +3 -1
- package/template/apps/agent-server/src/southwind_agent_server/openai_engine.py +95 -130
- package/template/apps/agent-server/src/southwind_agent_server/openai_engine_validation.py +133 -0
- package/template/apps/agent-server/src/southwind_agent_server/openai_provider.py +89 -20
- package/template/apps/agent-server/src/southwind_agent_server/postgres_report_insert.py +34 -0
- package/template/apps/agent-server/src/southwind_agent_server/postgres_report_queries.py +234 -0
- package/template/apps/agent-server/src/southwind_agent_server/postgres_report_replay.py +44 -0
- package/template/apps/agent-server/src/southwind_agent_server/postgres_report_status.py +83 -0
- package/template/apps/agent-server/src/southwind_agent_server/postgres_report_store.py +331 -0
- package/template/apps/agent-server/src/southwind_agent_server/postgres_report_summary.py +304 -0
- package/template/apps/agent-server/src/southwind_agent_server/report_lifecycle.py +217 -0
- package/template/apps/agent-server/src/southwind_agent_server/report_metrics.py +149 -0
- package/template/apps/agent-server/src/southwind_agent_server/report_presenter.py +280 -0
- package/template/apps/agent-server/src/southwind_agent_server/report_routes.py +126 -0
- package/template/apps/agent-server/src/southwind_agent_server/report_store.py +344 -0
- package/template/apps/agent-server/src/southwind_agent_server/report_types.py +111 -0
- package/template/apps/agent-server/src/southwind_agent_server/runtime_factory.py +31 -2
- package/template/apps/agent-server/src/southwind_agent_server/service.py +56 -51
- package/template/apps/agent-server/src/southwind_agent_server/signal_events.py +18 -1
- package/template/apps/agent-server/tests/test_adk_adapter.py +7 -9
- package/template/apps/agent-server/tests/test_adk_debug_sessions.py +215 -0
- package/template/apps/agent-server/tests/test_adk_web_canonical_debug.py +48 -1
- package/template/apps/agent-server/tests/test_openai_provider.py +58 -6
- package/template/apps/agent-server/tests/test_postgres_report_status.py +59 -0
- package/template/apps/agent-server/tests/test_postgres_report_summary.py +298 -0
- package/template/apps/agent-server/tests/test_provider_attempt_reporting.py +152 -0
- package/template/apps/agent-server/tests/test_report_attempt_lifecycle.py +126 -0
- package/template/apps/agent-server/tests/test_report_resilience.py +181 -0
- package/template/apps/agent-server/tests/test_report_store.py +286 -0
- package/template/apps/agent-server/tests/test_report_summary_parity.py +115 -0
- package/template/apps/agent-server/tests/test_report_terminal_sync.py +177 -0
- package/template/apps/server/src/agent/debug-session-facade-provider-failure.test.ts +269 -0
- package/template/apps/server/src/agent/debug-session-facade.test.ts +335 -0
- package/template/apps/server/src/agent/debug-session-facade.ts +302 -0
- package/template/apps/server/src/agent/debug-session-id.test.ts +37 -0
- package/template/apps/server/src/agent/debug-session-id.ts +37 -0
- package/template/apps/server/src/agent/debug-session-provider-reader.ts +54 -0
- package/template/apps/server/src/agent/debug-session-routes.test.ts +64 -0
- package/template/apps/server/src/agent/debug-session-routes.ts +73 -0
- package/template/apps/server/src/agent/debug-session-validation.ts +106 -0
- package/template/apps/server/src/agent/drizzle-run-binding-repository.ts +2 -0
- package/template/apps/server/src/agent/provider-input-governance.test.ts +18 -0
- package/template/apps/server/src/agent/provider-input-governance.ts +37 -1
- package/template/apps/server/src/agent/remote-runtime.test.ts +7 -0
- package/template/apps/server/src/agent/remote-runtime.ts +1 -0
- package/template/apps/server/src/agent/run-authorization.test.ts +98 -0
- package/template/apps/server/src/agent/run-authorization.ts +32 -1
- package/template/apps/server/src/agent/run-binding-repository.ts +1 -0
- package/template/apps/server/src/agent/run-contracts.ts +5 -0
- package/template/apps/server/src/agent/run-facade-adk-debug.test.ts +39 -0
- package/template/apps/server/src/agent/run-facade.test.ts +78 -0
- package/template/apps/server/src/agent/run-facade.ts +33 -3
- package/template/apps/server/src/agent/run-input-commitment.ts +50 -0
- package/template/apps/server/src/agent/run-input-verification.ts +41 -0
- package/template/apps/server/src/agent/runtime-bootstrap-report.test.ts +136 -0
- package/template/apps/server/src/agent/runtime-bootstrap.ts +66 -1
- package/template/apps/server/src/agent/tool-host.test.ts +1 -0
- package/template/apps/server/src/agent/unavailable-routes.ts +4 -0
- package/template/apps/server/src/ai-runtime-report/actor-directory.ts +94 -0
- package/template/apps/server/src/ai-runtime-report/actor-handle.test.ts +27 -0
- package/template/apps/server/src/ai-runtime-report/actor-handle.ts +81 -0
- package/template/apps/server/src/ai-runtime-report/contracts.ts +91 -0
- package/template/apps/server/src/ai-runtime-report/remote-parser.ts +212 -0
- package/template/apps/server/src/ai-runtime-report/remote-port.ts +138 -0
- package/template/apps/server/src/ai-runtime-report/routes.ts +172 -0
- package/template/apps/server/src/ai-runtime-report/scope.ts +63 -0
- package/template/apps/server/src/ai-runtime-report/service.test.ts +271 -0
- package/template/apps/server/src/ai-runtime-report/service.ts +206 -0
- package/template/apps/server/src/license/runtime-audit-reliability.test.ts +2 -0
- package/template/apps/server/src/license/runtime-service.test.ts +6 -0
- package/template/apps/server/src/module-host/ai-operation-port.test.ts +63 -0
- package/template/apps/server/src/module-host/ai-operation-port.ts +30 -5
- package/template/apps/server/src/module-host/debug-session-provider-registration.test.ts +162 -0
- package/template/apps/server/src/module-host/debug-session-provider-registration.ts +84 -0
- package/template/apps/server/src/module-host/initialize-contracts.ts +7 -0
- package/template/apps/server/src/module-host/initialize.ts +6 -197
- package/template/apps/server/src/module-host/request-context.ts +1 -0
- package/template/apps/server/src/module-host/scoped-host.ts +214 -0
- package/template/apps/server/src/module-host.ts +1 -0
- package/template/apps/server/src/runtime-adk-debug.test.ts +25 -1
- package/template/apps/server/src/runtime-adk-debug.ts +8 -2
- package/template/apps/server/src/system/operations.test.ts +54 -0
- package/template/apps/server/src/system/operations.ts +23 -1
- package/template/apps/server/src/system/seed.test.ts +4 -0
- package/template/apps/server/src/system/system-resource-monitoring.test.ts +82 -0
- package/template/apps/server/src/system/system-resource-monitoring.ts +300 -0
- package/template/apps/web/src/layout/AppSidebar.vue +6 -1
- package/template/apps/web/src/layout/AppSidebar.webtest.ts +2 -0
- package/template/apps/web/src/pages/auth/LoginPage.vue +6 -1
- package/template/apps/web/src/pages/settings/BrandingSettingsSection.vue +20 -3
- package/template/apps/web/src/pages/settings/BrandingSettingsSection.webtest.ts +80 -0
- package/template/apps/web/src/pages/system/AiRuntimePage.vue +129 -0
- package/template/apps/web/src/pages/system/SystemOperationsPage.vue +2 -1
- package/template/apps/web/src/pages/system/ai-runtime-presenter.ts +60 -0
- package/template/apps/web/src/pages/system/ai-runtime-presenter.webtest.ts +48 -0
- package/template/apps/web/src/pages/system/audit-time-range.ts +34 -2
- package/template/apps/web/src/pages/system/components/AuditLogFilters.vue +4 -3
- package/template/apps/web/src/pages/system/components/AuditLogFilters.webtest.ts +26 -1
- package/template/apps/web/src/pages/system/components/OperationGpuMonitor.vue +98 -0
- package/template/apps/web/src/pages/system/components/OperationGpuMonitor.webtest.ts +61 -0
- package/template/apps/web/src/pages/system/components/OperationServiceMonitor.vue +53 -0
- package/template/apps/web/src/pages/system/components/OperationServiceMonitor.webtest.ts +20 -1
- package/template/apps/web/src/pages/system/components/SystemResourcePage.webtest.ts +14 -1
- package/template/apps/web/src/pages/system/components/ai-runtime/AiRuntimeActivityChart.vue +85 -0
- package/template/apps/web/src/pages/system/components/ai-runtime/AiRuntimeCharts.webtest.ts +82 -0
- package/template/apps/web/src/pages/system/components/ai-runtime/AiRuntimeFilters.vue +98 -0
- package/template/apps/web/src/pages/system/components/ai-runtime/AiRuntimeFilters.webtest.ts +27 -0
- package/template/apps/web/src/pages/system/components/ai-runtime/AiRuntimeRunDetailDialog.vue +168 -0
- package/template/apps/web/src/pages/system/components/ai-runtime/AiRuntimeRunTable.vue +130 -0
- package/template/apps/web/src/pages/system/components/ai-runtime/AiRuntimeSummaryCards.vue +82 -0
- package/template/apps/web/src/pages/system/components/ai-runtime/AiRuntimeSummaryCards.webtest.ts +102 -0
- package/template/apps/web/src/pages/system/components/ai-runtime/AiRuntimeTokenByModelChart.vue +101 -0
- package/template/apps/web/src/pages/system/composables/useAiRuntimeReport.ts +185 -0
- package/template/apps/web/src/pages/system/composables/useAiRuntimeReport.webtest.ts +158 -0
- package/template/apps/web/src/pages/system/composables/useSystemResource.ts +21 -7
- package/template/apps/web/src/router/error-navigation.ts +22 -0
- package/template/apps/web/src/router/index.ts +12 -23
- package/template/apps/web/src/router/platform-admin-route-bindings.ts +4 -0
- package/template/apps/web/src/services/ai-runtime-api.ts +164 -0
- package/template/apps/web/src/services/ai-runtime-api.webtest.ts +111 -0
- package/template/apps/web/src/services/operations-api.ts +28 -0
- package/template/docker-compose.agent-debug.yml +1 -1
- package/template/docs/architecture/ai-runtime.md +2 -1
- package/template/docs/architecture/module-host.md +3 -0
- package/template/docs/platform/agent-run-attestation.md +65 -0
- package/template/docs/platform/agent-runtime-observability.md +135 -0
- package/template/docs/platform/agent-runtime.md +47 -3
- package/template/docs/platform/operations-overview.md +13 -0
- package/template/docs/platform/platform-authentication.md +4 -3
- package/template/packages/config/package.json +1 -1
- package/template/packages/database/drizzle/0045_tiny_cassandra_nova.sql +65 -0
- package/template/packages/database/drizzle/0046_tranquil_living_mummy.sql +6 -0
- package/template/packages/database/drizzle/0047_oval_luke_cage.sql +18 -0
- package/template/packages/database/drizzle/meta/0045_snapshot.json +8814 -0
- package/template/packages/database/drizzle/meta/0046_snapshot.json +8820 -0
- package/template/packages/database/drizzle/meta/0047_snapshot.json +8835 -0
- package/template/packages/database/drizzle/meta/_journal.json +21 -0
- package/template/packages/database/package.json +1 -1
- package/template/packages/database/src/agent-runtime-schema.test.ts +54 -0
- package/template/packages/database/src/schema/agent-runtime.ts +114 -0
- package/template/packages/jobs/package.json +1 -1
- package/template/packages/modules/package.json +1 -1
- package/template/packages/modules/src/system-admin-routes.ts +10 -0
- package/template/packages/modules/src/system-admin-ui-catalog.ts +1 -1
- package/template/packages/modules/src/system-api-control-plane.ts +17 -1
- package/template/packages/modules/src/system-audit-actions.ts +10 -0
- package/template/packages/modules/src/system-features.ts +8 -0
- package/template/packages/modules/src/system-module-catalog.test.ts +35 -0
- package/template/packages/modules/src/system-module-catalog.ts +15 -4
- package/template/packages/modules/src/system-modules-access.test.ts +69 -0
- package/template/packages/modules/src/system-modules.ts +13 -0
- package/template/packages/modules/src/system-permissions.ts +2 -0
- package/template/packages/server-sdk/index.ts +11 -0
- package/template/packages/server-sdk/package.json +2 -1
- package/template/packages/server-sdk/src/ai-operation-port.ts +5 -0
- package/template/packages/server-sdk/src/debug-session-provider.ts +63 -0
- package/template/packages/server-sdk/src/module-host.ts +5 -0
- package/template/packages/shared/index.ts +3 -0
- package/template/packages/shared/package.json +1 -1
- package/template/packages/shared/src/ai-runtime-report.ts +134 -0
- package/template/scripts/lib/postgres-regression.ts +201 -0
|
@@ -133,6 +133,7 @@ function unavailableModuleAiOperationPort(
|
|
|
133
133
|
};
|
|
134
134
|
return {
|
|
135
135
|
startAgent: async () => unavailable(),
|
|
136
|
+
verifyAgentRun: async () => unavailable(),
|
|
136
137
|
startDocument: async () => unavailable(),
|
|
137
138
|
startSpeech: async () => unavailable(),
|
|
138
139
|
embed: async () => unavailable(),
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import type { DurableHandlerRegistry } from "@southwind-ai/jobs";
|
|
2
|
+
import type { ModuleManifest } from "@southwind-ai/modules";
|
|
3
|
+
import {
|
|
4
|
+
requireModuleNamespace,
|
|
5
|
+
type ModuleHost,
|
|
6
|
+
type ModuleResourceFileAccessPolicy,
|
|
7
|
+
type ModuleUploadPolicy,
|
|
8
|
+
} from "@southwind-ai/server-sdk";
|
|
9
|
+
import type {
|
|
10
|
+
RegisteredModuleAiOperationHandler,
|
|
11
|
+
RegisteredModuleDebugSessionProvider,
|
|
12
|
+
RegisteredModuleDurableHandler,
|
|
13
|
+
RegisteredModuleGovernedResource,
|
|
14
|
+
RegisteredModuleRolePreset,
|
|
15
|
+
RegisteredModuleRoute,
|
|
16
|
+
RegisteredModuleSandboxToolHandler,
|
|
17
|
+
RegisteredModuleSearchProvider,
|
|
18
|
+
RegisteredModuleTaskHandler,
|
|
19
|
+
RegisteredModuleToolHandler,
|
|
20
|
+
} from "./initialize-contracts.js";
|
|
21
|
+
import { validateDebugSessionProviderRegistration } from "./debug-session-provider-registration.js";
|
|
22
|
+
import { registerModuleDurableHandler } from "./durable-handler-registration.js";
|
|
23
|
+
import { registerGovernedResources } from "./governed-resources.js";
|
|
24
|
+
import { registerResourceFileAccessPolicies } from "./resource-file-policies.js";
|
|
25
|
+
import { validateRouteDeclaration } from "./route-declaration.js";
|
|
26
|
+
import { validateSearchProviderRegistration } from "./search-provider-registration.js";
|
|
27
|
+
|
|
28
|
+
type HandlerInstaller = (registry: DurableHandlerRegistry) => void;
|
|
29
|
+
|
|
30
|
+
export interface HostCollections {
|
|
31
|
+
uploadPolicies: ModuleUploadPolicy[];
|
|
32
|
+
uploadPurposes: Set<string>;
|
|
33
|
+
modulePurposes: Map<string, Set<string>>;
|
|
34
|
+
moduleDependencies: ReadonlyMap<string, readonly string[]>;
|
|
35
|
+
resourceFileAccessPolicies: ModuleResourceFileAccessPolicy[];
|
|
36
|
+
moduleResourceFileAccessPolicies: Map<
|
|
37
|
+
string,
|
|
38
|
+
ModuleResourceFileAccessPolicy[]
|
|
39
|
+
>;
|
|
40
|
+
resourceFileAccessPolicyKeys: Set<string>;
|
|
41
|
+
handlerKeys: Set<string>;
|
|
42
|
+
handlerInstallers: HandlerInstaller[];
|
|
43
|
+
durableHandlers: RegisteredModuleDurableHandler[];
|
|
44
|
+
routes: RegisteredModuleRoute[];
|
|
45
|
+
routeKeys: Set<string>;
|
|
46
|
+
taskHandlers: RegisteredModuleTaskHandler[];
|
|
47
|
+
taskKeys: Set<string>;
|
|
48
|
+
toolHandlers: RegisteredModuleToolHandler[];
|
|
49
|
+
sandboxToolHandlers: RegisteredModuleSandboxToolHandler[];
|
|
50
|
+
toolKeys: Set<string>;
|
|
51
|
+
aiOperationHandlers: RegisteredModuleAiOperationHandler[];
|
|
52
|
+
aiOperationKeys: Set<string>;
|
|
53
|
+
governedResources: RegisteredModuleGovernedResource[];
|
|
54
|
+
governedResourceKeys: Set<string>;
|
|
55
|
+
rolePresets: RegisteredModuleRolePreset[];
|
|
56
|
+
rolePresetKeys: Set<string>;
|
|
57
|
+
searchProviders: RegisteredModuleSearchProvider[];
|
|
58
|
+
searchProviderKeys: Set<string>;
|
|
59
|
+
debugSessionProviders: RegisteredModuleDebugSessionProvider[];
|
|
60
|
+
debugSessionProviderKeys: Set<string>;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function createScopedHost(
|
|
64
|
+
manifest: ModuleManifest,
|
|
65
|
+
state: HostCollections,
|
|
66
|
+
): ModuleHost {
|
|
67
|
+
const moduleName = manifest.name;
|
|
68
|
+
return {
|
|
69
|
+
moduleName,
|
|
70
|
+
registerUploadPolicy(policy) {
|
|
71
|
+
requireModuleNamespace("上传用途", policy.purpose, moduleName);
|
|
72
|
+
if (state.uploadPurposes.has(policy.purpose)) {
|
|
73
|
+
throw new Error(`上传用途重复注册:${policy.purpose}`);
|
|
74
|
+
}
|
|
75
|
+
state.uploadPurposes.add(policy.purpose);
|
|
76
|
+
state.uploadPolicies.push(policy);
|
|
77
|
+
const purposes =
|
|
78
|
+
state.modulePurposes.get(moduleName) ?? new Set<string>();
|
|
79
|
+
purposes.add(policy.purpose);
|
|
80
|
+
state.modulePurposes.set(moduleName, purposes);
|
|
81
|
+
},
|
|
82
|
+
registerResourceFileAccessPolicies(policies) {
|
|
83
|
+
registerResourceFileAccessPolicies(manifest, state, policies);
|
|
84
|
+
},
|
|
85
|
+
registerDurableHandler(definition) {
|
|
86
|
+
requireModuleNamespace("任务 handler", definition.key, moduleName);
|
|
87
|
+
registerModuleDurableHandler(manifest, state, definition);
|
|
88
|
+
},
|
|
89
|
+
registerGovernedResources(registrations) {
|
|
90
|
+
registerGovernedResources(manifest, state, registrations);
|
|
91
|
+
},
|
|
92
|
+
registerRoutes(routes) {
|
|
93
|
+
for (const definition of routes) {
|
|
94
|
+
requireModuleNamespace(
|
|
95
|
+
"路由权限",
|
|
96
|
+
definition.permissionKey,
|
|
97
|
+
moduleName,
|
|
98
|
+
);
|
|
99
|
+
validateRouteDeclaration(manifest, definition);
|
|
100
|
+
const key = `${definition.method} ${definition.path}`;
|
|
101
|
+
if (state.routeKeys.has(key)) {
|
|
102
|
+
throw new Error(`模块路由重复注册:${key}`);
|
|
103
|
+
}
|
|
104
|
+
state.routeKeys.add(key);
|
|
105
|
+
state.routes.push({ moduleName, definition });
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
registerTaskHandlers(registrations) {
|
|
109
|
+
for (const registration of registrations) {
|
|
110
|
+
requireModuleNamespace("定时任务", registration.taskKey, moduleName);
|
|
111
|
+
if (!manifest.tasks.some(({ key }) => key === registration.taskKey)) {
|
|
112
|
+
throw new Error(
|
|
113
|
+
`定时任务 Handler 未在 Manifest 声明:${registration.taskKey}`,
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
if (state.taskKeys.has(registration.taskKey)) {
|
|
117
|
+
throw new Error(`定时任务 Handler 重复注册:${registration.taskKey}`);
|
|
118
|
+
}
|
|
119
|
+
state.taskKeys.add(registration.taskKey);
|
|
120
|
+
state.taskHandlers.push({ moduleName, registration });
|
|
121
|
+
}
|
|
122
|
+
},
|
|
123
|
+
registerToolHandlers(registrations) {
|
|
124
|
+
for (const registration of registrations) {
|
|
125
|
+
requireModuleNamespace("Agent Tool", registration.toolKey, moduleName);
|
|
126
|
+
if (!manifest.tools.some(({ key }) => key === registration.toolKey)) {
|
|
127
|
+
throw new Error(
|
|
128
|
+
`Agent Tool Handler 未在 Manifest 声明:${registration.toolKey}`,
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
if (state.toolKeys.has(registration.toolKey)) {
|
|
132
|
+
throw new Error(
|
|
133
|
+
`Agent Tool Handler 重复注册:${registration.toolKey}`,
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
state.toolKeys.add(registration.toolKey);
|
|
137
|
+
state.toolHandlers.push({ moduleName, registration });
|
|
138
|
+
}
|
|
139
|
+
},
|
|
140
|
+
registerAiOperationHandlers(registrations) {
|
|
141
|
+
for (const registration of registrations) {
|
|
142
|
+
requireModuleNamespace(
|
|
143
|
+
"AI Operation",
|
|
144
|
+
registration.operationKey,
|
|
145
|
+
moduleName,
|
|
146
|
+
);
|
|
147
|
+
const operation = manifest.aiOperations?.find(
|
|
148
|
+
({ key }) => key === registration.operationKey,
|
|
149
|
+
);
|
|
150
|
+
if (!operation) {
|
|
151
|
+
throw new Error(
|
|
152
|
+
`AI Operation Handler 未在 Manifest 声明:${registration.operationKey}`,
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
if (
|
|
156
|
+
(operation.kind === "agent" && !registration.prepare) ||
|
|
157
|
+
(operation.kind === "document" && !registration.prepareDocument) ||
|
|
158
|
+
(operation.kind === "speech" && !registration.prepareSpeech) ||
|
|
159
|
+
(operation.kind === "embedding" && !registration.prepareEmbedding)
|
|
160
|
+
) {
|
|
161
|
+
throw new Error(
|
|
162
|
+
`AI Operation Handler 与 kind 不匹配:${registration.operationKey}`,
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
if (state.aiOperationKeys.has(registration.operationKey)) {
|
|
166
|
+
throw new Error(
|
|
167
|
+
`AI Operation Handler 重复注册:${registration.operationKey}`,
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
state.aiOperationKeys.add(registration.operationKey);
|
|
171
|
+
state.aiOperationHandlers.push({ moduleName, registration });
|
|
172
|
+
}
|
|
173
|
+
},
|
|
174
|
+
registerRolePresets(presets) {
|
|
175
|
+
for (const preset of presets) {
|
|
176
|
+
requireModuleNamespace("角色预设", preset.key, moduleName);
|
|
177
|
+
if (state.rolePresetKeys.has(preset.key)) {
|
|
178
|
+
throw new Error(`角色预设重复注册:${preset.key}`);
|
|
179
|
+
}
|
|
180
|
+
for (const permissionKey of preset.permissionKeys) {
|
|
181
|
+
requireModuleNamespace("角色预设权限", permissionKey, moduleName);
|
|
182
|
+
if (!manifest.permissions.some(({ key }) => key === permissionKey)) {
|
|
183
|
+
throw new Error(
|
|
184
|
+
`角色预设引用了模块 ${moduleName} 未声明权限:${permissionKey}`,
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
state.rolePresetKeys.add(preset.key);
|
|
189
|
+
state.rolePresets.push({ moduleName, preset });
|
|
190
|
+
}
|
|
191
|
+
},
|
|
192
|
+
registerSearchProviders(providers) {
|
|
193
|
+
for (const provider of providers) {
|
|
194
|
+
requireModuleNamespace("搜索 Provider", provider.key, moduleName);
|
|
195
|
+
validateSearchProviderRegistration(manifest, provider);
|
|
196
|
+
if (state.searchProviderKeys.has(provider.key)) {
|
|
197
|
+
throw new Error(`搜索 Provider 重复注册:${provider.key}`);
|
|
198
|
+
}
|
|
199
|
+
state.searchProviderKeys.add(provider.key);
|
|
200
|
+
state.searchProviders.push({ moduleName, provider });
|
|
201
|
+
}
|
|
202
|
+
},
|
|
203
|
+
registerDebugSessionProviders(providers) {
|
|
204
|
+
for (const provider of providers) {
|
|
205
|
+
validateDebugSessionProviderRegistration(manifest, provider);
|
|
206
|
+
if (state.debugSessionProviderKeys.has(provider.key)) {
|
|
207
|
+
throw new Error(`调试会话 Provider 重复注册:${provider.key}`);
|
|
208
|
+
}
|
|
209
|
+
state.debugSessionProviderKeys.add(provider.key);
|
|
210
|
+
state.debugSessionProviders.push({ moduleName, provider });
|
|
211
|
+
}
|
|
212
|
+
},
|
|
213
|
+
};
|
|
214
|
+
}
|
|
@@ -7,7 +7,7 @@ import { createAdkDebugRuntime } from "./runtime-adk-debug.js";
|
|
|
7
7
|
const token = "debug-token-012345678901234567890123";
|
|
8
8
|
|
|
9
9
|
describe("ADK Web 专用调试身份", () => {
|
|
10
|
-
test("只在 canonical Run
|
|
10
|
+
test("只在 canonical Run 与只读 debug-session 路径委托真实用户", async () => {
|
|
11
11
|
const runtime = createRuntime();
|
|
12
12
|
expect(
|
|
13
13
|
runtime.claimsRequest(request("/api/foundation", "GET", token)),
|
|
@@ -28,6 +28,30 @@ describe("ADK Web 专用调试身份", () => {
|
|
|
28
28
|
expect(
|
|
29
29
|
await runtime.resolveRequest(request("/api/foundation", "GET", token)),
|
|
30
30
|
).toBeUndefined();
|
|
31
|
+
expect(
|
|
32
|
+
await runtime.resolveRequest(
|
|
33
|
+
request("/api/agent/debug/sessions/v1", "GET", token),
|
|
34
|
+
),
|
|
35
|
+
).toMatchObject({ id: "service_adk_debug" });
|
|
36
|
+
expect(
|
|
37
|
+
await runtime.resolveRequest(
|
|
38
|
+
request(
|
|
39
|
+
"/api/agent/debug/sessions/v1/wdbg1_" + "a".repeat(43),
|
|
40
|
+
"GET",
|
|
41
|
+
token,
|
|
42
|
+
),
|
|
43
|
+
),
|
|
44
|
+
).toMatchObject({ id: "service_adk_debug" });
|
|
45
|
+
expect(
|
|
46
|
+
await runtime.resolveRequest(
|
|
47
|
+
request("/api/agent/debug/sessions/v1", "POST", token),
|
|
48
|
+
),
|
|
49
|
+
).toBeUndefined();
|
|
50
|
+
expect(
|
|
51
|
+
await runtime.resolveRequest(
|
|
52
|
+
request("/api/agent/debug/sessions/v1/one/events", "GET", token),
|
|
53
|
+
),
|
|
54
|
+
).toBeUndefined();
|
|
31
55
|
expect(
|
|
32
56
|
await runtime.resolveRequest(
|
|
33
57
|
request("/api/agent/runs/run_1/result", "GET"),
|
|
@@ -45,7 +45,7 @@ export function createAdkDebugRuntime(
|
|
|
45
45
|
return {
|
|
46
46
|
claimsRequest,
|
|
47
47
|
async resolveRequest(request: Request): Promise<RequestActor | undefined> {
|
|
48
|
-
if (!claimsRequest(request) || !
|
|
48
|
+
if (!claimsRequest(request) || !isAdkDebugRequest(request)) {
|
|
49
49
|
return undefined;
|
|
50
50
|
}
|
|
51
51
|
return resolveDelegatedActor(actorResolver, dataActorId, permissionKeys);
|
|
@@ -123,8 +123,14 @@ function resolvePermissionClosure(
|
|
|
123
123
|
return [...new Set([operation.permissionKey, ...toolPermissions])];
|
|
124
124
|
}
|
|
125
125
|
|
|
126
|
-
function
|
|
126
|
+
function isAdkDebugRequest(request: Request): boolean {
|
|
127
127
|
const path = new URL(request.url).pathname.replace(/\/+$/, "");
|
|
128
|
+
if (
|
|
129
|
+
request.method === "GET" &&
|
|
130
|
+
/^\/api\/agent\/debug\/sessions\/v1(?:\/[^/]+)?$/.test(path)
|
|
131
|
+
) {
|
|
132
|
+
return true;
|
|
133
|
+
}
|
|
128
134
|
if (request.method === "POST" && path === "/api/agent/runs") return true;
|
|
129
135
|
const match = path.match(
|
|
130
136
|
/^\/api\/agent\/runs\/[^/]+\/(events|result|cancel|attach)$/,
|
|
@@ -84,6 +84,37 @@ describe("system operation routes", () => {
|
|
|
84
84
|
path: "/imports",
|
|
85
85
|
usedBytes: 500,
|
|
86
86
|
}),
|
|
87
|
+
cpuMonitorProbe: async () => ({
|
|
88
|
+
status: "available",
|
|
89
|
+
logicalCores: 16,
|
|
90
|
+
hostUsagePercent: 42.5,
|
|
91
|
+
processUsagePercent: 3.25,
|
|
92
|
+
loadAverage1m: 2.1,
|
|
93
|
+
}),
|
|
94
|
+
gpuMonitorProbe: async () => ({
|
|
95
|
+
status: "available",
|
|
96
|
+
provider: "NVIDIA",
|
|
97
|
+
message: "GPU 指标可用。",
|
|
98
|
+
devices: [
|
|
99
|
+
{
|
|
100
|
+
index: 0,
|
|
101
|
+
uuid: "GPU-abc",
|
|
102
|
+
name: "NVIDIA RTX 5000",
|
|
103
|
+
usagePercent: 75,
|
|
104
|
+
memoryUsedBytes: 8_192,
|
|
105
|
+
memoryTotalBytes: 32_768,
|
|
106
|
+
memoryUsagePercent: 25,
|
|
107
|
+
temperatureCelsius: 62,
|
|
108
|
+
processes: [
|
|
109
|
+
{
|
|
110
|
+
pid: 4321,
|
|
111
|
+
name: "python ai-service.py",
|
|
112
|
+
usedMemoryBytes: 7_168,
|
|
113
|
+
},
|
|
114
|
+
],
|
|
115
|
+
},
|
|
116
|
+
],
|
|
117
|
+
}),
|
|
87
118
|
},
|
|
88
119
|
);
|
|
89
120
|
const guardContext = await runtime.createGuardContext(
|
|
@@ -104,6 +135,15 @@ describe("system operation routes", () => {
|
|
|
104
135
|
totalBytes: number;
|
|
105
136
|
usagePercent: number;
|
|
106
137
|
};
|
|
138
|
+
cpu: {
|
|
139
|
+
logicalCores: number;
|
|
140
|
+
hostUsagePercent: number;
|
|
141
|
+
processUsagePercent: number;
|
|
142
|
+
};
|
|
143
|
+
};
|
|
144
|
+
gpuMonitor: {
|
|
145
|
+
status: string;
|
|
146
|
+
devices: Array<{ name: string; processes: Array<{ pid: number }> }>;
|
|
107
147
|
};
|
|
108
148
|
databaseMonitor: { engine: string; version: string };
|
|
109
149
|
diskMonitor: {
|
|
@@ -118,6 +158,20 @@ describe("system operation routes", () => {
|
|
|
118
158
|
expect(snapshot.serviceMonitor.status).toBe("running");
|
|
119
159
|
expect(snapshot.serviceMonitor.startedAt).toMatch(/^\d{4}-\d{2}-\d{2}T/);
|
|
120
160
|
expect(snapshot.serviceMonitor.memory.usagePercent).toBeGreaterThan(0);
|
|
161
|
+
expect(snapshot.serviceMonitor.cpu).toMatchObject({
|
|
162
|
+
logicalCores: 16,
|
|
163
|
+
hostUsagePercent: 42.5,
|
|
164
|
+
processUsagePercent: 3.25,
|
|
165
|
+
});
|
|
166
|
+
expect(snapshot.gpuMonitor).toMatchObject({
|
|
167
|
+
status: "available",
|
|
168
|
+
devices: [
|
|
169
|
+
{
|
|
170
|
+
name: "NVIDIA RTX 5000",
|
|
171
|
+
processes: [{ pid: 4321 }],
|
|
172
|
+
},
|
|
173
|
+
],
|
|
174
|
+
});
|
|
121
175
|
expect(snapshot.databaseMonitor).toMatchObject({
|
|
122
176
|
engine: "PostgreSQL",
|
|
123
177
|
version: "16.3",
|
|
@@ -18,6 +18,12 @@ import {
|
|
|
18
18
|
type DirectoryUsageProbe,
|
|
19
19
|
type HostDiskUsageProbe,
|
|
20
20
|
} from "./operation-monitoring.js";
|
|
21
|
+
import {
|
|
22
|
+
createCpuMonitorProbe,
|
|
23
|
+
createGpuMonitorProbe,
|
|
24
|
+
type CpuMonitorProbe,
|
|
25
|
+
type GpuMonitorProbe,
|
|
26
|
+
} from "./system-resource-monitoring.js";
|
|
21
27
|
|
|
22
28
|
type ServerRuntime = ReturnType<typeof createServerRuntime>;
|
|
23
29
|
|
|
@@ -31,6 +37,8 @@ export interface OperationRouteOptions {
|
|
|
31
37
|
hostDiskUsageProbe?: HostDiskUsageProbe;
|
|
32
38
|
systemUsageProbe?: DirectoryUsageProbe;
|
|
33
39
|
importDataUsageProbe?: DirectoryUsageProbe;
|
|
40
|
+
cpuMonitorProbe?: CpuMonitorProbe;
|
|
41
|
+
gpuMonitorProbe?: GpuMonitorProbe;
|
|
34
42
|
onlineUserProvider?: () => Promise<ActiveAuthSession[]>;
|
|
35
43
|
}
|
|
36
44
|
|
|
@@ -69,6 +77,8 @@ export function registerSystemOperationRoutes(
|
|
|
69
77
|
process.env.WINDY_IMPORT_DATA_PATH,
|
|
70
78
|
),
|
|
71
79
|
);
|
|
80
|
+
const cpuMonitorProbe = options.cpuMonitorProbe ?? createCpuMonitorProbe();
|
|
81
|
+
const gpuMonitorProbe = options.gpuMonitorProbe ?? createGpuMonitorProbe();
|
|
72
82
|
const onlineUserProvider = options.onlineUserProvider ?? (async () => []);
|
|
73
83
|
app.get("/system/operations", async (rawContext) => {
|
|
74
84
|
const { guardContext } = rawContext as OperationRouteContext;
|
|
@@ -87,6 +97,8 @@ export function registerSystemOperationRoutes(
|
|
|
87
97
|
hostDiskUsageProbe,
|
|
88
98
|
systemUsageProbe,
|
|
89
99
|
importDataUsageProbe,
|
|
100
|
+
cpuMonitorProbe,
|
|
101
|
+
gpuMonitorProbe,
|
|
90
102
|
onlineUserProvider,
|
|
91
103
|
)
|
|
92
104
|
: forbidden(decision.reason);
|
|
@@ -108,6 +120,8 @@ async function createOperationSnapshot(
|
|
|
108
120
|
hostDiskUsageProbe: HostDiskUsageProbe,
|
|
109
121
|
systemUsageProbe: DirectoryUsageProbe,
|
|
110
122
|
importDataUsageProbe: DirectoryUsageProbe,
|
|
123
|
+
cpuMonitorProbe: CpuMonitorProbe,
|
|
124
|
+
gpuMonitorProbe: GpuMonitorProbe,
|
|
111
125
|
onlineUserProvider: () => Promise<ActiveAuthSession[]>,
|
|
112
126
|
) {
|
|
113
127
|
const [
|
|
@@ -116,6 +130,8 @@ async function createOperationSnapshot(
|
|
|
116
130
|
hostDiskUsage,
|
|
117
131
|
systemUsage,
|
|
118
132
|
importDataUsage,
|
|
133
|
+
cpuMonitor,
|
|
134
|
+
gpuMonitor,
|
|
119
135
|
activeSessions,
|
|
120
136
|
] = await Promise.all([
|
|
121
137
|
redisHealthProbe(),
|
|
@@ -123,6 +139,8 @@ async function createOperationSnapshot(
|
|
|
123
139
|
hostDiskUsageProbe(),
|
|
124
140
|
systemUsageProbe(),
|
|
125
141
|
importDataUsageProbe(),
|
|
142
|
+
cpuMonitorProbe(),
|
|
143
|
+
gpuMonitorProbe(),
|
|
126
144
|
onlineUserProvider(),
|
|
127
145
|
]);
|
|
128
146
|
return {
|
|
@@ -138,7 +156,11 @@ async function createOperationSnapshot(
|
|
|
138
156
|
lastSeenAt: (session.lastSeenAt || session.issuedAt).toISOString(),
|
|
139
157
|
expiresAt: session.expiresAt.toISOString(),
|
|
140
158
|
})),
|
|
141
|
-
serviceMonitor:
|
|
159
|
+
serviceMonitor: {
|
|
160
|
+
...serviceMonitorSnapshot(),
|
|
161
|
+
cpu: cpuMonitor,
|
|
162
|
+
},
|
|
163
|
+
gpuMonitor,
|
|
142
164
|
databaseMonitor,
|
|
143
165
|
diskMonitor: diskMonitorSnapshot(
|
|
144
166
|
hostDiskUsage,
|
|
@@ -33,6 +33,7 @@ describe("系统内置角色同步", () => {
|
|
|
33
33
|
// @windy-module system.settings end
|
|
34
34
|
expect(role?.permissionKeys).not.toContain("system.user.reveal");
|
|
35
35
|
expect(role?.permissionKeys).not.toContain("system.user.export");
|
|
36
|
+
expect(role?.permissionKeys).not.toContain("system.ai-runtime.read");
|
|
36
37
|
expect(role?.permissionKeys).toContain("custom.permission");
|
|
37
38
|
// @windy-module system.settings begin
|
|
38
39
|
expect(role?.menuKeys).toContain("system.settings");
|
|
@@ -54,6 +55,9 @@ describe("系统内置角色同步", () => {
|
|
|
54
55
|
expect(
|
|
55
56
|
(await repositories.roles.get("role_developer"))?.permissionKeys,
|
|
56
57
|
).not.toEqual(expect.arrayContaining(["system.user.reveal"]));
|
|
58
|
+
expect(
|
|
59
|
+
(await repositories.roles.get("role_developer"))?.permissionKeys,
|
|
60
|
+
).not.toContain("system.ai-runtime.read");
|
|
57
61
|
|
|
58
62
|
await repositories.roles.update(
|
|
59
63
|
"role_developer",
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import {
|
|
3
|
+
calculateCpuMonitor,
|
|
4
|
+
createGpuMonitorProbe,
|
|
5
|
+
type CpuSample,
|
|
6
|
+
} from "./system-resource-monitoring.js";
|
|
7
|
+
|
|
8
|
+
describe("系统 CPU 与 GPU 监控", () => {
|
|
9
|
+
test("CPU 占用按宿主机时间和逻辑核心归一化进程时间计算", () => {
|
|
10
|
+
const previous: CpuSample = {
|
|
11
|
+
hostIdleMilliseconds: 2_000,
|
|
12
|
+
hostTotalMilliseconds: 10_000,
|
|
13
|
+
processMicroseconds: 1_000_000,
|
|
14
|
+
observedAtMicroseconds: 5_000_000,
|
|
15
|
+
logicalCores: 4,
|
|
16
|
+
loadAverage1m: 1,
|
|
17
|
+
};
|
|
18
|
+
const snapshot = calculateCpuMonitor(previous, {
|
|
19
|
+
hostIdleMilliseconds: 2_400,
|
|
20
|
+
hostTotalMilliseconds: 11_000,
|
|
21
|
+
processMicroseconds: 1_800_000,
|
|
22
|
+
observedAtMicroseconds: 6_000_000,
|
|
23
|
+
logicalCores: 4,
|
|
24
|
+
loadAverage1m: 1.5,
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
expect(snapshot).toMatchObject({
|
|
28
|
+
hostUsagePercent: 60,
|
|
29
|
+
processUsagePercent: 20,
|
|
30
|
+
logicalCores: 4,
|
|
31
|
+
loadAverage1m: 1.5,
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test("NVIDIA 指标关联设备利用率与 AI 计算进程显存", async () => {
|
|
36
|
+
const probe = createGpuMonitorProbe(async (args) =>
|
|
37
|
+
args[0]?.startsWith("--query-gpu=")
|
|
38
|
+
? {
|
|
39
|
+
exitCode: 0,
|
|
40
|
+
stdout: '0, GPU-abc, "NVIDIA RTX 5000", 75, 8192, 32768, 62\n',
|
|
41
|
+
}
|
|
42
|
+
: {
|
|
43
|
+
exitCode: 0,
|
|
44
|
+
stdout: 'GPU-abc, 4321, "python ai-service.py", 7168\n',
|
|
45
|
+
},
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
await expect(probe()).resolves.toMatchObject({
|
|
49
|
+
status: "available",
|
|
50
|
+
devices: [
|
|
51
|
+
{
|
|
52
|
+
index: 0,
|
|
53
|
+
name: "NVIDIA RTX 5000",
|
|
54
|
+
usagePercent: 75,
|
|
55
|
+
memoryUsagePercent: 25,
|
|
56
|
+
temperatureCelsius: 62,
|
|
57
|
+
processes: [
|
|
58
|
+
{
|
|
59
|
+
pid: 4321,
|
|
60
|
+
name: "python ai-service.py",
|
|
61
|
+
usedMemoryBytes: 7_168 * 1_024 ** 2,
|
|
62
|
+
},
|
|
63
|
+
],
|
|
64
|
+
},
|
|
65
|
+
],
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test("没有 nvidia-smi 时返回可解释的不可探测状态", async () => {
|
|
70
|
+
const probe = createGpuMonitorProbe(async () => ({
|
|
71
|
+
exitCode: null,
|
|
72
|
+
stdout: "",
|
|
73
|
+
missing: true,
|
|
74
|
+
}));
|
|
75
|
+
|
|
76
|
+
await expect(probe()).resolves.toMatchObject({
|
|
77
|
+
status: "not-detected",
|
|
78
|
+
provider: "NVIDIA",
|
|
79
|
+
devices: [],
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
});
|