create-windy 0.2.34 → 0.3.1
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/README.md +26 -3
- package/dist/cli.js +8121 -3746
- package/package.json +3 -3
- package/template/.windy-template.json +2 -2
- package/template/AGENTS.md +5 -0
- package/template/README.md +28 -4
- package/template/_gitignore +4 -0
- package/template/apps/agent-server/Dockerfile +1 -0
- package/template/apps/agent-server/README.md +18 -5
- package/template/apps/agent-server/adk_web_agents/__init__.py +1 -0
- package/template/apps/agent-server/adk_web_agents/safe_debug/__init__.py +3 -0
- package/template/apps/agent-server/adk_web_agents/safe_debug/agent.py +40 -0
- package/template/apps/agent-server/pyproject.toml +1 -1
- package/template/apps/agent-server/src/southwind_agent_server/adk_adapter.py +191 -16
- package/template/apps/agent-server/src/southwind_agent_server/adk_failure.py +13 -0
- package/template/apps/agent-server/src/southwind_agent_server/adk_model.py +225 -0
- package/template/apps/agent-server/src/southwind_agent_server/adk_tools.py +76 -0
- package/template/apps/agent-server/src/southwind_agent_server/app.py +1 -2
- package/template/apps/agent-server/tests/__init__.py +1 -0
- package/template/apps/agent-server/tests/openai_provider_test_support.py +131 -0
- package/template/apps/agent-server/tests/test_adk_adapter.py +305 -0
- package/template/apps/agent-server/tests/test_adk_web_agent.py +12 -0
- package/template/apps/agent-server/tests/test_openai_provider.py +7 -123
- package/template/apps/server/package.json +4 -1
- package/template/apps/server/src/agent/run-facade.ts +2 -7
- package/template/apps/server/src/agent/tool-registry.ts +2 -6
- package/template/apps/server/src/application-services.ts +50 -0
- package/template/apps/server/src/audit/drizzle-log-store.ts +25 -0
- package/template/apps/server/src/audit/search-summary.ts +2 -6
- package/template/apps/server/src/auth/credential-restriction.ts +1 -0
- package/template/apps/server/src/auth/password.ts +30 -21
- package/template/apps/server/src/auth/routes.test-support.ts +94 -0
- package/template/apps/server/src/auth/routes.test.ts +51 -78
- package/template/apps/server/src/auth/routes.ts +6 -1
- package/template/apps/server/src/auth/runtime.ts +4 -1
- package/template/apps/server/src/auth/service.ts +25 -7
- package/template/apps/server/src/configuration/bootstrap.ts +20 -1
- package/template/apps/server/src/configuration/definition.test.ts +71 -0
- package/template/apps/server/src/configuration/definition.ts +242 -12
- package/template/apps/server/src/configuration/drizzle-repository.ts +36 -2
- package/template/apps/server/src/configuration/repository.ts +27 -2
- package/template/apps/server/src/configuration/routes.ts +4 -1
- package/template/apps/server/src/configuration/service.test.ts +100 -0
- package/template/apps/server/src/configuration/service.ts +50 -10
- package/template/apps/server/src/data-access/scoped-repository.integration.test.ts +3 -2
- package/template/apps/server/src/data-governance/export/service.ts +2 -6
- package/template/apps/server/src/example-modules.ts +4 -1
- package/template/apps/server/src/guards.ts +18 -5
- package/template/apps/server/src/http/request-source.test.ts +85 -0
- package/template/apps/server/src/http/request-source.ts +90 -0
- package/template/apps/server/src/index.ts +40 -40
- package/template/apps/server/src/license/routes.ts +2 -0
- package/template/apps/server/src/license/runtime-service.ts +10 -11
- package/template/apps/server/src/mcp/adapter.test.ts +3 -2
- package/template/apps/server/src/mcp/adapter.ts +9 -9
- package/template/apps/server/src/mcp/routes.ts +4 -3
- package/template/apps/server/src/module-bootstrap.ts +2 -2
- package/template/apps/server/src/module-host/request-context.ts +6 -6
- package/template/apps/server/src/module-host/storage-scope.ts +2 -9
- package/template/apps/server/src/module-host/user-directory-port.ts +2 -6
- package/template/apps/server/src/notification/channel-adapter.ts +46 -0
- package/template/apps/server/src/notification/delivery-policy.test.ts +59 -0
- package/template/apps/server/src/notification/delivery-policy.ts +37 -0
- package/template/apps/server/src/notification/delivery-repository.ts +198 -0
- package/template/apps/server/src/notification/delivery-worker.test.ts +87 -0
- package/template/apps/server/src/notification/delivery-worker.ts +88 -0
- package/template/apps/server/src/notification/drizzle-management.ts +180 -0
- package/template/apps/server/src/notification/drizzle-query.ts +160 -0
- package/template/apps/server/src/notification/drizzle-repository.integration.test.ts +301 -0
- package/template/apps/server/src/notification/drizzle-repository.ts +156 -90
- package/template/apps/server/src/notification/extension-routes.test.ts +119 -0
- package/template/apps/server/src/notification/extension-routes.ts +111 -0
- package/template/apps/server/src/notification/memory-delivery-repository.ts +137 -0
- package/template/apps/server/src/notification/memory-support.ts +72 -0
- package/template/apps/server/src/notification/preference-repository.ts +95 -0
- package/template/apps/server/src/notification/recipient-directory.ts +104 -0
- package/template/apps/server/src/notification/repository.test.ts +171 -0
- package/template/apps/server/src/notification/repository.ts +179 -32
- package/template/apps/server/src/notification/revision-routes.test.ts +186 -0
- package/template/apps/server/src/notification/route-support.ts +225 -0
- package/template/apps/server/src/notification/routes.test.ts +182 -0
- package/template/apps/server/src/notification/routes.ts +225 -132
- package/template/apps/server/src/notification/service.test.ts +104 -0
- package/template/apps/server/src/notification/service.ts +140 -0
- package/template/apps/server/src/notification/smtp-adapter.test.ts +65 -0
- package/template/apps/server/src/notification/smtp-adapter.ts +68 -0
- package/template/apps/server/src/persistence.integration.test.ts +41 -2
- package/template/apps/server/src/platform-http-runtime.ts +87 -0
- package/template/apps/server/src/route-guards.ts +1 -0
- package/template/apps/server/src/runtime-audit-list.test.ts +39 -0
- package/template/apps/server/src/runtime-development.ts +1 -0
- package/template/apps/server/src/runtime.test.ts +23 -9
- package/template/apps/server/src/runtime.ts +39 -2
- package/template/apps/server/src/scheduler/audit.ts +2 -11
- package/template/apps/server/src/scheduler/definitions.test.ts +6 -0
- package/template/apps/server/src/scheduler/definitions.ts +7 -0
- package/template/apps/server/src/scheduler/recovery-service.ts +2 -11
- package/template/apps/server/src/scheduler/routes.ts +2 -7
- package/template/apps/server/src/search/opensearch-client.test.ts +45 -0
- package/template/apps/server/src/search/opensearch-client.ts +145 -0
- package/template/apps/server/src/search/service.ts +2 -6
- package/template/apps/server/src/settings/routes.ts +15 -3
- package/template/apps/server/src/settings/runtime.ts +10 -1
- package/template/apps/server/src/storage/runtime.ts +13 -0
- package/template/apps/server/src/system/audit-query.ts +33 -4
- package/template/apps/server/src/system/audit-routes.test.ts +58 -4
- package/template/apps/server/src/system/audit-routes.ts +6 -5
- package/template/apps/server/src/system/department-organization.ts +320 -0
- package/template/apps/server/src/system/department-routes.test.ts +207 -48
- package/template/apps/server/src/system/department-routes.ts +114 -33
- package/template/apps/server/src/system/drizzle-repository.ts +15 -7
- package/template/apps/server/src/system/drizzle-scoped-repository.ts +5 -3
- package/template/apps/server/src/system/drizzle-system-configs.ts +319 -0
- package/template/apps/server/src/system/drizzle-system.ts +59 -311
- package/template/apps/server/src/system/drizzle-transaction-context.ts +14 -0
- package/template/apps/server/src/system/identity-route-config.ts +7 -2
- package/template/apps/server/src/system/mutation-runner.ts +23 -0
- package/template/apps/server/src/system/organization-postgres.integration.test.ts +176 -0
- package/template/apps/server/src/system/resource-access-response.ts +12 -6
- package/template/apps/server/src/system/resource-routes.ts +270 -0
- package/template/apps/server/src/system/role-department-scope-policy.ts +55 -0
- package/template/apps/server/src/system/route-helpers.ts +2 -5
- package/template/apps/server/src/system/route-types.ts +12 -3
- package/template/apps/server/src/system/routes-validation.test.ts +26 -0
- package/template/apps/server/src/system/routes.test.ts +10 -2
- package/template/apps/server/src/system/routes.ts +35 -267
- package/template/apps/server/src/system/seed.ts +7 -0
- package/template/apps/server/src/work-order/data-scope.integration.test.ts +9 -2
- package/template/apps/server/src/work-order/opensearch-index.test.ts +97 -0
- package/template/apps/server/src/work-order/opensearch-index.ts +313 -0
- package/template/apps/server/src/work-order/search-provider.ts +60 -1
- package/template/apps/server/src/work-order/service.ts +42 -10
- package/template/apps/web/src/app/error-pages.ts +9 -0
- package/template/apps/web/src/components/auth/PasswordChangeForm.vue +16 -11
- package/template/apps/web/src/components/auth/PasswordChangeForm.webtest.ts +14 -0
- package/template/apps/web/src/components/common/FormDialog.vue +16 -2
- package/template/apps/web/src/components/common/ModalLayout.webtest.ts +20 -1
- package/template/apps/web/src/composables/useGlobalSearch.ts +68 -0
- package/template/apps/web/src/composables/usePlatformSettings.ts +8 -7
- package/template/apps/web/src/layout/GlobalSearchResults.vue +58 -0
- package/template/apps/web/src/layout/NavigationSearchDialog.vue +42 -6
- package/template/apps/web/src/layout/NavigationSearchDialog.webtest.ts +71 -0
- package/template/apps/web/src/layout/NotificationMenu.vue +73 -31
- package/template/apps/web/src/layout/NotificationMenu.webtest.ts +44 -1
- package/template/apps/web/src/layout/NotificationMenuItem.vue +61 -0
- package/template/apps/web/src/layout/NotificationPreferencesDialog.vue +181 -0
- package/template/apps/web/src/layout/NotificationPreferencesDialog.webtest.ts +80 -0
- package/template/apps/web/src/lib/date-time.ts +22 -10
- package/template/apps/web/src/lib/date-time.webtest.ts +9 -1
- package/template/apps/web/src/main.ts +14 -1
- package/template/apps/web/src/pages/auth/ChangePasswordPage.vue +16 -1
- package/template/apps/web/src/pages/configuration/components/ModuleConfigVersionList.vue +1 -0
- package/template/apps/web/src/pages/configuration/components/ModuleConfigurationEditor.vue +10 -0
- package/template/apps/web/src/pages/configuration/components/ModuleConfigurationWorkbench.vue +37 -6
- package/template/apps/web/src/pages/errors/PlatformErrorPage.vue +115 -0
- package/template/apps/web/src/pages/errors/PlatformErrorPage.webtest.ts +44 -0
- package/template/apps/web/src/pages/errors/error-page-extension.ts +26 -0
- package/template/apps/web/src/pages/system/SystemNotificationsPage.vue +136 -68
- package/template/apps/web/src/pages/system/SystemNotificationsPage.webtest.ts +224 -12
- package/template/apps/web/src/pages/system/audit-log-presenter.ts +86 -0
- package/template/apps/web/src/pages/system/audit-time-range.ts +28 -0
- package/template/apps/web/src/pages/system/components/AuditActionCell.vue +10 -69
- package/template/apps/web/src/pages/system/components/AuditActionCell.webtest.ts +20 -27
- package/template/apps/web/src/pages/system/components/AuditLogDetailDialog.vue +134 -0
- package/template/apps/web/src/pages/system/components/AuditLogDetailDialog.webtest.ts +76 -0
- package/template/apps/web/src/pages/system/components/AuditLogFilters.vue +116 -52
- package/template/apps/web/src/pages/system/components/AuditLogFilters.webtest.ts +52 -15
- package/template/apps/web/src/pages/system/components/NotificationHistoryDialog.vue +129 -0
- package/template/apps/web/src/pages/system/components/NotificationManagementCard.vue +91 -0
- package/template/apps/web/src/pages/system/components/NotificationPublishDialog.vue +121 -0
- package/template/apps/web/src/pages/system/components/NotificationPublishForm.vue +162 -1
- package/template/apps/web/src/pages/system/components/SystemResourceActions.vue +68 -0
- package/template/apps/web/src/pages/system/components/SystemResourceEditor.vue +74 -0
- package/template/apps/web/src/pages/system/components/SystemResourceFilters.vue +3 -4
- package/template/apps/web/src/pages/system/components/SystemResourceLoadError.vue +21 -0
- package/template/apps/web/src/pages/system/components/SystemResourceLoadError.webtest.ts +17 -0
- package/template/apps/web/src/pages/system/components/SystemResourcePage.vue +96 -108
- package/template/apps/web/src/pages/system/components/SystemResourcePage.webtest.ts +68 -0
- package/template/apps/web/src/pages/system/components/SystemResourceTable.vue +20 -1
- package/template/apps/web/src/pages/system/components/SystemResourceTable.webtest.ts +28 -7
- package/template/apps/web/src/pages/system/components/department/DepartmentOrganizationForm.vue +210 -0
- package/template/apps/web/src/pages/system/components/department/DepartmentTransitionDialog.vue +112 -0
- package/template/apps/web/src/pages/system/components/user-account/UserAccountForm.vue +17 -4
- package/template/apps/web/src/pages/system/composables/useDepartmentTransitions.ts +79 -0
- package/template/apps/web/src/pages/system/composables/useNotificationPermissions.ts +14 -0
- package/template/apps/web/src/pages/system/composables/useSystemResource.ts +2 -0
- package/template/apps/web/src/pages/system/composables/useSystemResource.webtest.ts +15 -2
- package/template/apps/web/src/pages/system/resource-config.ts +12 -4
- package/template/apps/web/src/router/error-navigation.ts +50 -0
- package/template/apps/web/src/router/error-navigation.webtest.ts +106 -0
- package/template/apps/web/src/router/index.ts +71 -0
- package/template/apps/web/src/router/index.webtest.ts +29 -0
- package/template/apps/web/src/router/manifest-routes.webtest.ts +4 -0
- package/template/apps/web/src/router/shared-scaffold-neutrality.webtest.ts +12 -0
- package/template/apps/web/src/services/auth-api.ts +5 -0
- package/template/apps/web/src/services/http.ts +43 -6
- package/template/apps/web/src/services/http.webtest.ts +70 -0
- package/template/apps/web/src/services/notification-api.ts +85 -2
- package/template/apps/web/src/services/notification-api.webtest.ts +110 -0
- package/template/apps/web/src/services/search-api.ts +39 -0
- package/template/apps/web/src/services/system-api.ts +18 -0
- package/template/apps/web/vitest.config.ts +1 -0
- package/template/docker-compose.yml +66 -0
- package/template/docs/architecture/ai-runtime.md +14 -5
- package/template/docs/architecture/mcp-business-capabilities.md +107 -0
- package/template/docs/architecture/search-provider-protocol.md +38 -2
- package/template/docs/development/custom-error-pages.md +78 -0
- package/template/docs/development/custom-login-page.md +3 -3
- package/template/docs/platform/agent-runtime.md +32 -6
- package/template/docs/platform/agent-tools.md +4 -0
- package/template/docs/platform/audit-reliability.md +46 -2
- package/template/docs/platform/module-configuration.md +13 -4
- package/template/docs/platform/notifications.md +67 -11
- package/template/docs/platform/organization-membership.md +17 -3
- package/template/docs/platform/system-crud-api.md +16 -0
- package/template/docs/platform/web-system-crud.md +6 -2
- package/template/package.json +4 -0
- package/template/packages/config/index.test.ts +45 -0
- package/template/packages/config/package.json +1 -1
- package/template/packages/config/src/platform.ts +169 -48
- package/template/packages/database/drizzle/0035_material_nightshade.sql +10 -0
- package/template/packages/database/drizzle/0036_hesitant_speed.sql +35 -0
- package/template/packages/database/drizzle/0037_absent_nick_fury.sql +2 -0
- package/template/packages/database/drizzle/0038_uneven_wasp.sql +38 -0
- package/template/packages/database/drizzle/meta/0035_snapshot.json +7331 -0
- package/template/packages/database/drizzle/meta/0036_snapshot.json +7436 -0
- package/template/packages/database/drizzle/meta/0037_snapshot.json +7457 -0
- package/template/packages/database/drizzle/meta/0038_snapshot.json +7749 -0
- package/template/packages/database/drizzle/meta/_journal.json +28 -0
- package/template/packages/database/package.json +1 -1
- package/template/packages/database/src/schema/identity.ts +7 -0
- package/template/packages/database/src/schema/notifications.ts +135 -0
- package/template/packages/database/src/schema-notification-delivery.test.ts +54 -0
- package/template/packages/database/src/schema-workflow-notification.test.ts +40 -1
- package/template/packages/modules/package.json +1 -1
- package/template/packages/modules/src/composition-fixtures.test-support.ts +164 -0
- package/template/packages/modules/src/composition.test.ts +5 -158
- package/template/packages/modules/src/system-api-operational.ts +234 -0
- package/template/packages/modules/src/system-api-permissions.ts +14 -181
- package/template/packages/modules/src/system-audit-actions.ts +10 -0
- package/template/packages/modules/src/system-features.ts +1 -1
- package/template/packages/modules/src/system-module-catalog.ts +2 -1
- package/template/packages/modules/src/system-module-tasks.ts +86 -0
- package/template/packages/modules/src/system-modules-access.test.ts +60 -0
- package/template/packages/modules/src/system-modules.test.ts +13 -54
- package/template/packages/modules/src/system-modules.ts +2 -72
- package/template/packages/modules/src/system-permissions.ts +9 -5
- package/template/packages/server-sdk/package.json +1 -1
- package/template/packages/shared/index.ts +1 -0
- package/template/packages/shared/package.json +1 -1
- package/template/packages/shared/src/audit.ts +25 -0
- package/template/packages/shared/src/mcp.test.ts +12 -0
- package/template/packages/shared/src/mcp.ts +7 -0
- package/template/packages/shared/src/module-configuration.ts +5 -0
- package/template/packages/shared/src/notification.ts +92 -1
- package/template/packages/shared/src/password.ts +7 -2
- package/template/packages/shared/src/platform-settings.ts +2 -0
- package/template/packages/storage/package.json +1 -1
- package/template/packages/storage/src/upload-service.test.ts +16 -0
- package/template/packages/storage/src/upload-service.ts +5 -1
- package/template/scripts/notification-postgres-regression.ts +27 -0
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, test, vi } from "vitest";
|
|
2
|
+
import {
|
|
3
|
+
favoriteNotification,
|
|
4
|
+
getNotificationAudienceOptions,
|
|
5
|
+
getNotificationPreference,
|
|
6
|
+
listNotificationRevisions,
|
|
7
|
+
listNotifications,
|
|
8
|
+
unfavoriteNotification,
|
|
9
|
+
updateNotificationPreference,
|
|
10
|
+
updateNotification,
|
|
11
|
+
} from "./notification-api";
|
|
12
|
+
|
|
13
|
+
const mocks = vi.hoisted(() => ({
|
|
14
|
+
request: vi.fn(async () => ({ items: [], total: 0, page: 1, pageSize: 10 })),
|
|
15
|
+
}));
|
|
16
|
+
|
|
17
|
+
vi.mock("./http", () => ({ apiRequest: mocks.request }));
|
|
18
|
+
|
|
19
|
+
describe("普通用户通知 API", () => {
|
|
20
|
+
beforeEach(() => {
|
|
21
|
+
mocks.request.mockClear();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test("旧分页调用保持原 URL 兼容", async () => {
|
|
25
|
+
await listNotifications(2, 20);
|
|
26
|
+
|
|
27
|
+
expect(mocks.request).toHaveBeenCalledWith(
|
|
28
|
+
"/notifications?page=2&pageSize=20",
|
|
29
|
+
);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
test("关键词、分类、已读和收藏筛选统一编码到服务端查询", async () => {
|
|
33
|
+
await listNotifications(3, 15, {
|
|
34
|
+
q: "维护 窗口",
|
|
35
|
+
category: "系统/安全",
|
|
36
|
+
read: "unread",
|
|
37
|
+
favorite: false,
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
expect(mocks.request).toHaveBeenCalledWith(
|
|
41
|
+
"/notifications?page=3&pageSize=15&q=%E7%BB%B4%E6%8A%A4+%E7%AA%97%E5%8F%A3&category=%E7%B3%BB%E7%BB%9F%2F%E5%AE%89%E5%85%A8&read=unread&favorite=false",
|
|
42
|
+
);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
test("收藏与取消收藏使用显式幂等方法", async () => {
|
|
46
|
+
await favoriteNotification("notice/1");
|
|
47
|
+
await unfavoriteNotification("notice/1");
|
|
48
|
+
|
|
49
|
+
expect(mocks.request).toHaveBeenNthCalledWith(
|
|
50
|
+
1,
|
|
51
|
+
"/notifications/notice%2F1/favorite",
|
|
52
|
+
{ method: "PUT" },
|
|
53
|
+
);
|
|
54
|
+
expect(mocks.request).toHaveBeenNthCalledWith(
|
|
55
|
+
2,
|
|
56
|
+
"/notifications/notice%2F1/favorite",
|
|
57
|
+
{ method: "DELETE" },
|
|
58
|
+
);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test("管理端修改公告并分页读取版本历史", async () => {
|
|
62
|
+
const input = {
|
|
63
|
+
title: "修改公告",
|
|
64
|
+
summary: "修改摘要",
|
|
65
|
+
content: "修改正文",
|
|
66
|
+
category: "系统",
|
|
67
|
+
};
|
|
68
|
+
await updateNotification("notice/1", input);
|
|
69
|
+
await listNotificationRevisions("notice/1", 2, 20);
|
|
70
|
+
|
|
71
|
+
expect(mocks.request).toHaveBeenNthCalledWith(
|
|
72
|
+
1,
|
|
73
|
+
"/system/notifications/notice%2F1",
|
|
74
|
+
{ method: "PUT", body: JSON.stringify(input) },
|
|
75
|
+
);
|
|
76
|
+
expect(mocks.request).toHaveBeenNthCalledWith(
|
|
77
|
+
2,
|
|
78
|
+
"/system/notifications/notice%2F1/revisions?page=2&pageSize=20",
|
|
79
|
+
);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
test("偏好和管理端受众选项走受 Guard 保护的稳定端点", async () => {
|
|
83
|
+
await getNotificationPreference();
|
|
84
|
+
await updateNotificationPreference({
|
|
85
|
+
enabledChannels: ["email"],
|
|
86
|
+
disabledCategories: ["营销"],
|
|
87
|
+
});
|
|
88
|
+
await getNotificationAudienceOptions();
|
|
89
|
+
|
|
90
|
+
expect(mocks.request).toHaveBeenNthCalledWith(
|
|
91
|
+
1,
|
|
92
|
+
"/notifications/preferences",
|
|
93
|
+
);
|
|
94
|
+
expect(mocks.request).toHaveBeenNthCalledWith(
|
|
95
|
+
2,
|
|
96
|
+
"/notifications/preferences",
|
|
97
|
+
{
|
|
98
|
+
method: "PUT",
|
|
99
|
+
body: JSON.stringify({
|
|
100
|
+
enabledChannels: ["email"],
|
|
101
|
+
disabledCategories: ["营销"],
|
|
102
|
+
}),
|
|
103
|
+
},
|
|
104
|
+
);
|
|
105
|
+
expect(mocks.request).toHaveBeenNthCalledWith(
|
|
106
|
+
3,
|
|
107
|
+
"/system/notifications/audience-options",
|
|
108
|
+
);
|
|
109
|
+
});
|
|
110
|
+
});
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { apiRequest } from "./http";
|
|
2
|
+
|
|
3
|
+
export interface SearchHit {
|
|
4
|
+
id: string;
|
|
5
|
+
resourceType: string;
|
|
6
|
+
maskedTitle: string;
|
|
7
|
+
maskedExcerpt?: string;
|
|
8
|
+
score?: number;
|
|
9
|
+
providerKey: string;
|
|
10
|
+
deepLink: {
|
|
11
|
+
surface: "business";
|
|
12
|
+
routeTemplate: string;
|
|
13
|
+
params: Record<string, string>;
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface SearchResultGroup {
|
|
18
|
+
providerKey: string;
|
|
19
|
+
label: string;
|
|
20
|
+
hits: readonly SearchHit[];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface SearchResultPage {
|
|
24
|
+
groups: readonly SearchResultGroup[];
|
|
25
|
+
partial: boolean;
|
|
26
|
+
failures: Array<{
|
|
27
|
+
providerKey: string;
|
|
28
|
+
code: string;
|
|
29
|
+
message: string;
|
|
30
|
+
}>;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function searchPlatform(query: string, signal?: AbortSignal) {
|
|
34
|
+
return apiRequest<SearchResultPage>("/search", {
|
|
35
|
+
method: "POST",
|
|
36
|
+
body: JSON.stringify({ query, limit: 8 }),
|
|
37
|
+
signal,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
@@ -154,3 +154,21 @@ export function quickCreateDepartment(payload: Record<string, unknown>) {
|
|
|
154
154
|
body: JSON.stringify(payload),
|
|
155
155
|
});
|
|
156
156
|
}
|
|
157
|
+
|
|
158
|
+
export function transitionDepartment(
|
|
159
|
+
departmentId: string,
|
|
160
|
+
payload: {
|
|
161
|
+
action: "disable" | "delete";
|
|
162
|
+
targetDepartmentId?: string;
|
|
163
|
+
},
|
|
164
|
+
) {
|
|
165
|
+
return apiRequest<{
|
|
166
|
+
department: SystemRecord;
|
|
167
|
+
movedChildren: number;
|
|
168
|
+
movedUsers: number;
|
|
169
|
+
updatedRoles: number;
|
|
170
|
+
}>(`/system/departments/${departmentId}/transition`, {
|
|
171
|
+
method: "POST",
|
|
172
|
+
body: JSON.stringify(payload),
|
|
173
|
+
});
|
|
174
|
+
}
|
|
@@ -46,6 +46,10 @@ services:
|
|
|
46
46
|
AGENT_SERVER_URL: http://agent-server:8080
|
|
47
47
|
AGENT_TOOL_HOST_URL: http://server:9747
|
|
48
48
|
AGENT_SERVER_INTERNAL_TOKEN: ${AGENT_SERVER_INTERNAL_TOKEN:-}
|
|
49
|
+
OPENSEARCH_URL: ${OPENSEARCH_URL:-}
|
|
50
|
+
OPENSEARCH_USERNAME: ${OPENSEARCH_USERNAME:-}
|
|
51
|
+
OPENSEARCH_PASSWORD: ${OPENSEARCH_PASSWORD:-}
|
|
52
|
+
OPENSEARCH_WORK_ORDER_INDEX: ${OPENSEARCH_WORK_ORDER_INDEX:-windy-work-orders-v1}
|
|
49
53
|
ports:
|
|
50
54
|
- "9747:9747"
|
|
51
55
|
volumes:
|
|
@@ -56,6 +60,29 @@ services:
|
|
|
56
60
|
migrate:
|
|
57
61
|
condition: service_completed_successfully
|
|
58
62
|
|
|
63
|
+
opensearch:
|
|
64
|
+
profiles: ["search"]
|
|
65
|
+
image: opensearchproject/opensearch:2.19.3
|
|
66
|
+
environment:
|
|
67
|
+
discovery.type: single-node
|
|
68
|
+
DISABLE_INSTALL_DEMO_CONFIG: "true"
|
|
69
|
+
DISABLE_SECURITY_PLUGIN: "true"
|
|
70
|
+
OPENSEARCH_JAVA_OPTS: "-Xms512m -Xmx512m"
|
|
71
|
+
ports:
|
|
72
|
+
- "127.0.0.1:19200:9200"
|
|
73
|
+
volumes:
|
|
74
|
+
- opensearch-data:/usr/share/opensearch/data
|
|
75
|
+
healthcheck:
|
|
76
|
+
test:
|
|
77
|
+
[
|
|
78
|
+
"CMD-SHELL",
|
|
79
|
+
"curl -fsS http://127.0.0.1:9200/_cluster/health?wait_for_status=yellow",
|
|
80
|
+
]
|
|
81
|
+
interval: 10s
|
|
82
|
+
timeout: 5s
|
|
83
|
+
retries: 12
|
|
84
|
+
|
|
85
|
+
|
|
59
86
|
agent-server:
|
|
60
87
|
profiles: ["agent"]
|
|
61
88
|
build:
|
|
@@ -79,6 +106,44 @@ services:
|
|
|
79
106
|
server:
|
|
80
107
|
condition: service_started
|
|
81
108
|
|
|
109
|
+
adk-web-debug:
|
|
110
|
+
profiles: ["agent-debug"]
|
|
111
|
+
build:
|
|
112
|
+
context: .
|
|
113
|
+
dockerfile: apps/agent-server/Dockerfile
|
|
114
|
+
command:
|
|
115
|
+
[
|
|
116
|
+
"adk",
|
|
117
|
+
"web",
|
|
118
|
+
"--host",
|
|
119
|
+
"0.0.0.0",
|
|
120
|
+
"--port",
|
|
121
|
+
"8000",
|
|
122
|
+
"--no-reload",
|
|
123
|
+
"--session_service_uri",
|
|
124
|
+
"memory://",
|
|
125
|
+
"--artifact_service_uri",
|
|
126
|
+
"memory://",
|
|
127
|
+
"--logo-text",
|
|
128
|
+
"Windy ADK 开发调试(无业务工具)",
|
|
129
|
+
"--logo-image-url",
|
|
130
|
+
"data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%2F%3E",
|
|
131
|
+
"/app/apps/agent-server/adk_web_agents",
|
|
132
|
+
]
|
|
133
|
+
ports:
|
|
134
|
+
- "127.0.0.1:8000:8000"
|
|
135
|
+
healthcheck:
|
|
136
|
+
test:
|
|
137
|
+
[
|
|
138
|
+
"CMD",
|
|
139
|
+
"python",
|
|
140
|
+
"-c",
|
|
141
|
+
"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/', timeout=3)",
|
|
142
|
+
]
|
|
143
|
+
interval: 10s
|
|
144
|
+
timeout: 5s
|
|
145
|
+
retries: 5
|
|
146
|
+
|
|
82
147
|
web:
|
|
83
148
|
build:
|
|
84
149
|
context: .
|
|
@@ -113,3 +178,4 @@ volumes:
|
|
|
113
178
|
postgres-data:
|
|
114
179
|
server-audit-data:
|
|
115
180
|
agent-run-data:
|
|
181
|
+
opensearch-data:
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
# Southwind AI Runtime 架构
|
|
2
2
|
|
|
3
|
-
更新时间:2026-07-
|
|
3
|
+
更新时间:2026-07-26。
|
|
4
4
|
|
|
5
5
|
状态:架构已确认,首个可消费的文本 Agent 切片已实现。当前包含 Module AI Operation
|
|
6
6
|
注册、Guarded Run facade、短时 Execution Grant、受限 Tool Host、OpenAI-compatible
|
|
7
7
|
Provider、canonical HTTP/SSE、取消、cursor 重放和单副本有界 SQLite Store。未配置
|
|
8
|
-
Provider
|
|
9
|
-
|
|
8
|
+
Provider 时仍安全拒绝。真实业务 Run 已在内部使用 ADK 2.5 Runner、Session、LlmAgent
|
|
9
|
+
和 Event,并把事件转换为既有 canonical signal。ADK Workflow、LiteLLM 多 Provider
|
|
10
|
+
路由、PostgreSQL 多副本 Store、Document AI 和 Speech 运行进程尚未实现,不能据此
|
|
11
|
+
宣称完整 AI Runtime 已交付。
|
|
10
12
|
|
|
11
13
|
## 目标
|
|
12
14
|
|
|
@@ -643,6 +645,12 @@ type DictationEvent =
|
|
|
643
645
|
|
|
644
646
|
ADK Web 只在 developer profile 启动,绑定 localhost 或受控开发网络。它不是生产 Prompt 管理或生产 Debug 控制面。
|
|
645
647
|
|
|
648
|
+
当前 Compose 使用独立 `agent-debug` profile,宿主机只发布
|
|
649
|
+
`127.0.0.1:8000`。由于原生 ADK Web 没有 Southwind AI Server 签发的 Actor 与
|
|
650
|
+
Execution Grant,它只加载一个无 Provider、无业务 Tool 的确定性调试 Agent。真实业务
|
|
651
|
+
Operation 只能走 canonical HTTP/SSE → ADK Adapter → 受治理 Provider/Tool Host 链路;
|
|
652
|
+
不得把 ADK Web 的调试会话解释为生产授权或业务执行证据。
|
|
653
|
+
|
|
646
654
|
Prompt 和 Workflow 使用:
|
|
647
655
|
|
|
648
656
|
```text
|
|
@@ -728,8 +736,9 @@ LiteLLM 版本必须固定并通过供应链审查,客户现场禁止动态安
|
|
|
728
736
|
|
|
729
737
|
当前已完成第 1、2、4 项和第 5 项的受限 Tool Host 基础闭环,并交付可直接连接
|
|
730
738
|
OpenAI-compatible Provider 的文本 Agent Adapter。SQLite Store 提供单副本有界持久化,
|
|
731
|
-
重启后保留终态和 cursor,但不替代后续 PostgreSQL 多副本 Store
|
|
732
|
-
|
|
739
|
+
重启后保留终态和 cursor,但不替代后续 PostgreSQL 多副本 Store。配置 Provider 后,
|
|
740
|
+
文本与 Tool Run 由 ADK Runner/Session/Event 执行;未配置 Provider 时同一 Adapter
|
|
741
|
+
fail closed。开发 ADK Web 已作为 localhost-only 的受控调试入口交付,不是业务入口。
|
|
733
742
|
|
|
734
743
|
1. 定义 Agent、Document、Speech Operation 与 Module Manifest 组合校验;
|
|
735
744
|
2. 建立 Python Agent Server、ADK Adapter 和 deterministic test Adapter;
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# MCP 业务能力暴露
|
|
2
|
+
|
|
3
|
+
## 决策
|
|
4
|
+
|
|
5
|
+
Windy 以 Module Manifest、Server SDK Handler 和 `GovernedToolRegistry` 组成业务能力深
|
|
6
|
+
模块。MCP、原生 Agent Tool Route 和 Agent Runtime Tool Host 都是该 Interface 的
|
|
7
|
+
Adapter,不拥有业务能力、权限或审计定义。
|
|
8
|
+
|
|
9
|
+
```text
|
|
10
|
+
业务 Module
|
|
11
|
+
├── Manifest:Schema / Tool / Feature / Permission / Audit
|
|
12
|
+
└── Handler:类型化 JSON 实现
|
|
13
|
+
│
|
|
14
|
+
▼
|
|
15
|
+
GovernedToolRegistry
|
|
16
|
+
├── Schema 与大小限制
|
|
17
|
+
├── License / Feature / RBAC / Data Scope
|
|
18
|
+
├── Timeout / Audit
|
|
19
|
+
└── 结构化结果
|
|
20
|
+
│
|
|
21
|
+
┌───┴───────────┐
|
|
22
|
+
▼ ▼
|
|
23
|
+
Agent Tool Route MCP Adapter
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
删除 Registry 后,输入输出校验、Guard、Scope、超时和审计复杂度会重新散落到多个调用面;
|
|
27
|
+
因此该模块承担了真实的平台复杂度,而不是协议转发层。
|
|
28
|
+
|
|
29
|
+
## 为什么不直接包装 OpenAPI
|
|
30
|
+
|
|
31
|
+
HTTP Route 面向确定性客户端,参数经常包含分页、传输和页面交互细节。MCP Tool 面向模型
|
|
32
|
+
选择和结构化调用,需要更小、语义明确、风险可治理的 Interface。自动包装所有 OpenAPI
|
|
33
|
+
会导致:
|
|
34
|
+
|
|
35
|
+
- 模型看到大量浅层 CRUD;
|
|
36
|
+
- 页面传输参数成为模型契约;
|
|
37
|
+
- 写操作缺少幂等和审批语义;
|
|
38
|
+
- Route、Tool、权限和审计产生多套漂移定义。
|
|
39
|
+
|
|
40
|
+
因此业务模块显式选择哪些能力成为 Tool;OpenAPI 和 MCP 可以调用同一业务 Service,
|
|
41
|
+
但不会相互自动推导。
|
|
42
|
+
|
|
43
|
+
## MCP Transport
|
|
44
|
+
|
|
45
|
+
远程接入已采用官方 MCP TypeScript SDK `1.29.0` 和无状态 Streamable HTTP,Endpoint
|
|
46
|
+
为 `/api/mcp`。v2 在当前实施时仍为预发布,不进入生产依赖。无状态模式适合当前只读
|
|
47
|
+
请求,也避免把授权快照保存为长期 Session 状态。
|
|
48
|
+
|
|
49
|
+
MCP Endpoint 必须位于现有 Server 身份解析之后:
|
|
50
|
+
|
|
51
|
+
- Bearer/Cookie 只用于解析当前 Server Actor,入口另需通过 `system.agent` Feature 与
|
|
52
|
+
`system.agent.read` Permission;
|
|
53
|
+
- `tools/list` 只返回当前 Actor 获准的只读 Tool;
|
|
54
|
+
- `tools/call` 每次重新经过 Registry,不复用列表阶段的授权结论;
|
|
55
|
+
- 客户端参数不能提供 Permission、Feature、License、Actor 或 Scope;
|
|
56
|
+
- MCP 协议错误与业务 Guard 错误使用稳定、脱敏的错误信息。
|
|
57
|
+
|
|
58
|
+
## Tool、Resource 与 Skill
|
|
59
|
+
|
|
60
|
+
当前只提供 Tool:
|
|
61
|
+
|
|
62
|
+
- Tool 对应可执行的类型化业务查询;
|
|
63
|
+
- Resource 等出现明确的只读文档或静态能力目录消费场景后再增加;
|
|
64
|
+
- Prompt 不作为业务规则来源。
|
|
65
|
+
|
|
66
|
+
Skill 解决开发流程,不属于运行时授权 Interface。仓库内开发 Skill 指导 Agent:
|
|
67
|
+
|
|
68
|
+
1. 创建业务模块和 Manifest;
|
|
69
|
+
2. 注册 Feature、Permission、Audit 与 JSON Schema;
|
|
70
|
+
3. 实现 Repository、Service 和 Tool Handler;
|
|
71
|
+
4. 增加 Composition、Guard、Data Scope 与 Schema 测试;
|
|
72
|
+
5. 使用脚手架和真实生成回归验证。
|
|
73
|
+
|
|
74
|
+
Skill 内容可以从稳定契约和模板派生,但不能独立声明 Tool Key 或授权事实。
|
|
75
|
+
`.agents/skills/windy-business-module/SKILL.md` 随 Starter 一起分发,并指向 Manifest、
|
|
76
|
+
Module Host 和 Tool Handler 的代码事实源。生成组合未包含 `system.agent` 时,Skill
|
|
77
|
+
要求明确物理边界,不在业务层另建 MCP 或授权实现。
|
|
78
|
+
|
|
79
|
+
## 写操作门槛
|
|
80
|
+
|
|
81
|
+
MCP Adapter 只暴露 `operation=query`。开放 `command/background` 前必须完成:
|
|
82
|
+
|
|
83
|
+
- 强制幂等键及结果重放语义;
|
|
84
|
+
- 风险分级和人工审批;
|
|
85
|
+
- Durable Job/结果状态引用;
|
|
86
|
+
- Actor、请求、审批和业务副作用的完整关联;
|
|
87
|
+
- 超时后真实副作用是否仍继续的明确语义;
|
|
88
|
+
- 重试、取消和补偿回归。
|
|
89
|
+
|
|
90
|
+
在这些条件满足前,Manifest 可以预先描述写能力,但 MCP Adapter 必须过滤它们。
|
|
91
|
+
|
|
92
|
+
## 运维与验证
|
|
93
|
+
|
|
94
|
+
- Server 启动前校验声明、Schema 和 Handler 一一对应。
|
|
95
|
+
- MCP 不可用不能影响普通业务 Route 和非 AI 能力启动。
|
|
96
|
+
- 审计只记录 Tool Key、Actor、Request ID、输入输出字节数和结果,不保存原始内容。
|
|
97
|
+
- 回归至少覆盖未授权不可发现、直接调用重新授权、Schema 拒绝、数据范围不可扩大和
|
|
98
|
+
Handler 输出漂移。
|
|
99
|
+
- MCP API Path、Server 身份、Route 与 Manifest Binding 使用
|
|
100
|
+
`packages/shared/src/mcp.ts` 的共享 Contract;生成 README 通过跨层测试校对该路径。
|
|
101
|
+
- Tool 目录从当前 Module Manifest 组合,不在 README 或 Skill 固化。新模板通过发布时
|
|
102
|
+
`template:sync` 更新;既有客户项目只通过显式 `windy update` 合并,不远程静默改写。
|
|
103
|
+
|
|
104
|
+
官方协议与 SDK 资料:
|
|
105
|
+
|
|
106
|
+
- [MCP TypeScript SDK v1](https://ts.sdk.modelcontextprotocol.io/)
|
|
107
|
+
- [MCP Server 与 Streamable HTTP](https://ts.sdk.modelcontextprotocol.io/server)
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
平台通过一个稳定的 `SearchService` 聚合业务搜索结果,但不读取业务模块 Repository,也不把用户输入转换成跨模块 SQL。业务模块通过 Manifest 声明 `kind: "search"` 的 Provider,并提供同 Key、同版本的 Handler。Composition Root 启动时要求声明与实现一一对应,未知 Handler、缺失 Handler、重复 Key、协议版本或 Provider 版本漂移都会 fail-fast。
|
|
6
6
|
|
|
7
|
-
默认部署不引入 OpenSearch、Elasticsearch 或其它外部搜索引擎。小数据量模块可以实现 Repository Provider
|
|
7
|
+
默认部署不引入 OpenSearch、Elasticsearch 或其它外部搜索引擎。小数据量模块可以实现 Repository Provider;需要外部索引时可以显式启动 OpenSearch 开发 profile。两种实现共用同一 Provider 协议、调用方与业务详情授权边界,业务数据库始终是事实源。
|
|
8
8
|
|
|
9
9
|
## 深模块边界
|
|
10
10
|
|
|
@@ -48,11 +48,47 @@ Provider 只负责在自身模块边界内执行搜索。它接收平台从已
|
|
|
48
48
|
- `SEARCH_PROVIDER_KEYS` 是逗号分隔的启动白名单;未配置时按 Manifest 的 `enabledByDefault` 启用,显式空字符串关闭全部 Provider,未知、重复或非法 Key 使启动失败。
|
|
49
49
|
- `SEARCH_PROVIDER_TIMEOUT_MS`、`SEARCH_HEALTH_TIMEOUT_MS` 允许 10-5000 毫秒;`SEARCH_MAX_REQUESTS_PER_MINUTE` 允许 1-300。非整数或越界配置使启动失败,不能被静默截断或钳制。
|
|
50
50
|
|
|
51
|
+
## Web 全局入口
|
|
52
|
+
|
|
53
|
+
Header 的 `Ctrl/⌘ K` 全局搜索同时展示两类结果:
|
|
54
|
+
|
|
55
|
+
- 平台导航在浏览器内只过滤 Admin Bootstrap 已下发且当前账号获准的菜单;
|
|
56
|
+
- 业务数据只通过 `POST /api/search` 获取,并按 Manifest Provider 分组展示脱敏标题、摘要和业务深链。
|
|
57
|
+
|
|
58
|
+
输入使用 250ms 延迟并在关键词变化或 Dialog 关闭时中止旧请求,避免迟到响应覆盖新结果。单个 Provider 超时或故障时显示“结果可能不完整”,其它 Provider 和菜单仍可使用。Web 不维护 Provider Key、目标权限或 Feature 的副本,也不接收 OpenSearch 地址和凭据。
|
|
59
|
+
|
|
60
|
+
## 可选 OpenSearch Adapter
|
|
61
|
+
|
|
62
|
+
根项目和包含 `system.search` 的 Starter 提供显式开发入口:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
bun run dev:search
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
该命令设置容器内 `OPENSEARCH_URL=http://opensearch:9200`,并启动 Compose 的 `search` profile。普通 `bun run dev` 不设置地址,也不会启动或连接 OpenSearch。开发 profile 使用固定镜像、持久卷和健康检查;宿主端口只绑定 `127.0.0.1:19200`。`system.search` 被物理裁剪时,生成物同时移除 OpenSearch service、Server 环境变量、volume 和启动脚本。
|
|
69
|
+
|
|
70
|
+
Server 可读取以下配置:
|
|
71
|
+
|
|
72
|
+
- `OPENSEARCH_URL`:不含凭据、路径、查询参数的 HTTP(S) 根地址;未配置即使用原 Repository Provider;
|
|
73
|
+
- `OPENSEARCH_USERNAME` / `OPENSEARCH_PASSWORD`:必须成对配置,只进入 Server 到 OpenSearch 的 Authorization Header;
|
|
74
|
+
- `OPENSEARCH_REQUEST_TIMEOUT_MS`:100-30000ms,默认 3000ms;
|
|
75
|
+
- `OPENSEARCH_WORK_ORDER_INDEX`:示例工单的可重建索引名,默认 `windy-work-orders-v1`。
|
|
76
|
+
|
|
77
|
+
示例工单 Adapter 展示了项目业务层的正确接法:
|
|
78
|
+
|
|
79
|
+
1. 第一次查询或索引失效后,从 PostgreSQL 事实源读取当前快照并重建索引;
|
|
80
|
+
2. 创建、更新和归档成功后增量更新索引;OpenSearch 写失败只标记投影失效,不回滚已提交的业务事实;
|
|
81
|
+
3. Provider 把服务端 `ScopeContext` 编译为 OpenSearch `term` / `terms` Filter,无授权分支使用 `match_none`;
|
|
82
|
+
4. 命中 ID 返回前再次调用业务 Service 读取目标资源,防止陈旧索引绕过详情权限;
|
|
83
|
+
5. 标题和摘要只从重新授权后的业务实体生成脱敏投影。
|
|
84
|
+
|
|
85
|
+
这个 Compose profile 用于本地接入与回归,不是生产拓扑。生产环境应启用 OpenSearch TLS、认证、最小网络可达性、容量和快照策略;大规模增量索引应由项目自有 Outbox/Durable Job 驱动,不能把索引成功当成业务事务提交条件,也不能直接把开发 profile 暴露到客户网络。
|
|
86
|
+
|
|
51
87
|
Composition Root 把 Server 实际注册的搜索 API 与 Provider Handler Key 一并交给 Module Composition 校验。没有业务 Provider 声明的项目允许 Core 以空 Registry 启动;只要 Manifest 与 Handler 不一致,启动就在监听端口前失败。
|
|
52
88
|
|
|
53
89
|
## 当前验证范围
|
|
54
90
|
|
|
55
|
-
协议单元测试覆盖声明/实现/版本 fail-fast、启停与健康发现、目标权限裁剪、客户端不能扩大服务端身份和组织范围、分组与游标、Manifest 深链、明文响应拒绝、故障/超时隔离、部分失败、按 Actor 限流,以及审计中不出现原始查询词。Server API 测试另行覆盖入口权限先行、目标权限二次裁剪、Feature 关闭、只接受 POST body、稳定错误状态和直接 API 伪造身份/部门不能扩大 scoped Repository 结果。
|
|
91
|
+
协议单元测试覆盖声明/实现/版本 fail-fast、启停与健康发现、目标权限裁剪、客户端不能扩大服务端身份和组织范围、分组与游标、Manifest 深链、明文响应拒绝、故障/超时隔离、部分失败、按 Actor 限流,以及审计中不出现原始查询词。Server API 测试另行覆盖入口权限先行、目标权限二次裁剪、Feature 关闭、只接受 POST body、稳定错误状态和直接 API 伪造身份/部门不能扩大 scoped Repository 结果。OpenSearch 测试覆盖默认关闭、配置拒绝、凭据只进入 Server 请求头、索引重建、范围 Filter 与无授权 `match_none`;Web 测试覆盖菜单检索、业务分组结果和深链跳转。
|
|
56
92
|
|
|
57
93
|
示例 Work Order 已注册 `work-order.ticket.search`,Provider 只调用 `WorkOrderService.list`,后者把查询、count、分页与服务端 dataScope 一起交给 scoped Repository;测试会让裸 `Repository.list` 主动抛错,并确认本人范围不会返回其他负责人的匹配工单。另一个实现相同 Key/版本的测试 Provider 可以在不修改 Core 的情况下替换工单 Adapter。
|
|
58
94
|
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# 业务项目定制错误页
|
|
2
|
+
|
|
3
|
+
Windy 为 Web 提供统一的 404、500、维护中和网络异常页面,同时允许业务项目替换任意
|
|
4
|
+
一种页面。业务选择入口是:
|
|
5
|
+
|
|
6
|
+
```text
|
|
7
|
+
apps/web/src/app/error-pages.ts
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
该文件在 `.windy/files.json` 中属于 `project-owned`。Windy 升级会给尚无此文件的
|
|
11
|
+
项目创建默认选择器;文件存在后,后续更新只保留业务选择。平台默认页面
|
|
12
|
+
`apps/web/src/pages/errors/PlatformErrorPage.vue` 属于 `windy-managed`,Router 和
|
|
13
|
+
全局错误导航属于 `shared-scaffold`,业务项目不应为换视觉而修改这些文件。
|
|
14
|
+
|
|
15
|
+
## 默认行为
|
|
16
|
+
|
|
17
|
+
默认选择器让四种错误都使用平台页面:
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
import PlatformErrorPage from "@/pages/errors/PlatformErrorPage.vue";
|
|
21
|
+
import { defineErrorPageComponents } from "@/pages/errors/error-page-extension";
|
|
22
|
+
|
|
23
|
+
export const errorPageComponents = defineErrorPageComponents({
|
|
24
|
+
notFound: PlatformErrorPage,
|
|
25
|
+
internal: PlatformErrorPage,
|
|
26
|
+
maintenance: PlatformErrorPage,
|
|
27
|
+
network: PlatformErrorPage,
|
|
28
|
+
});
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
错误来源与页面的稳定映射如下:
|
|
32
|
+
|
|
33
|
+
| 来源 | 页面 |
|
|
34
|
+
| --------------------------------------------------- | -------- |
|
|
35
|
+
| Router 没有匹配业务路由 | 404 |
|
|
36
|
+
| Vue 渲染异常、Router 加载异常、HTTP 5xx(503 除外) | 500 |
|
|
37
|
+
| HTTP 503 | 维护中 |
|
|
38
|
+
| `fetch` 无法建立连接或传输中断 | 网络异常 |
|
|
39
|
+
|
|
40
|
+
HTTP 5xx 响应中的 `x-request-id` 会进入错误页,用户可将它直接提供给维护人员。调用方若
|
|
41
|
+
要在当前交互内自行处理后台重试,可以给 `apiRequest` / `apiResponse` 传入
|
|
42
|
+
`{ suppressFailureHandler: true }`,避免跳转全局错误页;这不改变原 Promise 的失败
|
|
43
|
+
语义。
|
|
44
|
+
|
|
45
|
+
## 替换业务页面
|
|
46
|
+
|
|
47
|
+
把业务 SFC 放在 `apps/web/src/app/` 下,再只修改选择器。例如只替换网络异常页:
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
import NetworkErrorPage from "./NetworkErrorPage.vue";
|
|
51
|
+
import PlatformErrorPage from "@/pages/errors/PlatformErrorPage.vue";
|
|
52
|
+
import { defineErrorPageComponents } from "@/pages/errors/error-page-extension";
|
|
53
|
+
|
|
54
|
+
export const errorPageComponents = defineErrorPageComponents({
|
|
55
|
+
notFound: PlatformErrorPage,
|
|
56
|
+
internal: PlatformErrorPage,
|
|
57
|
+
maintenance: PlatformErrorPage,
|
|
58
|
+
network: NetworkErrorPage,
|
|
59
|
+
});
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
业务组件接收统一的只读 Props:
|
|
63
|
+
|
|
64
|
+
```ts
|
|
65
|
+
interface ErrorPageProps {
|
|
66
|
+
kind: "not-found" | "internal" | "maintenance" | "network";
|
|
67
|
+
from: string;
|
|
68
|
+
requestId?: string;
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
`from` 已限制为站内绝对路径,错误页自身和外部 URL 会回退到 `/`。业务组件应使用
|
|
73
|
+
文本插值展示地址和 Request ID,不使用 `v-html`;重试只导航到 `from`,不得把查询
|
|
74
|
+
参数解释为外部链接。错误页是公共恢复入口,不应读取管理权限快照或把敏感信息写入
|
|
75
|
+
页面。
|
|
76
|
+
|
|
77
|
+
完整自定义也必须保留四个注册项。`defineErrorPageComponents` 会在 TypeScript 阶段
|
|
78
|
+
检查 Key 是否齐全;Router 只依赖这个稳定导出,不感知业务组件实现。
|
|
@@ -78,9 +78,9 @@ composable。
|
|
|
78
78
|
先提交当前业务改动并保持 Git 工作区干净,然后执行:
|
|
79
79
|
|
|
80
80
|
```bash
|
|
81
|
-
bunx --bun create-windy@0.
|
|
82
|
-
bunx --bun create-windy@0.
|
|
83
|
-
bunx --bun create-windy@0.
|
|
81
|
+
bunx --bun create-windy@0.3.1 update --check --cwd .
|
|
82
|
+
bunx --bun create-windy@0.3.1 update --dry-run --cwd .
|
|
83
|
+
bunx --bun create-windy@0.3.1 update --cwd .
|
|
84
84
|
bun install
|
|
85
85
|
```
|
|
86
86
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Agent Runtime 接入与运行
|
|
2
2
|
|
|
3
|
-
更新时间:2026-07-
|
|
3
|
+
更新时间:2026-07-26。
|
|
4
4
|
|
|
5
5
|
Windy 的 Agent Runtime 是可选平台能力。业务模块声明稳定的 AI Operation 和允许调用的
|
|
6
6
|
Tool;Southwind AI Server 负责身份、Feature、Permission、License、Scope、Execution
|
|
@@ -9,6 +9,8 @@ Grant 与审计;独立 Agent Server 只负责模型循环、canonical event
|
|
|
9
9
|
|
|
10
10
|
## 当前可用边界
|
|
11
11
|
|
|
12
|
+
- 真实业务 Run 使用 Google ADK 2.5 `Runner`、`InMemorySessionService`、`LlmAgent`
|
|
13
|
+
与 `Event`;ADK 类型只存在于 Python Adapter 内,外部 HTTP/SSE 契约不变。
|
|
12
14
|
- OpenAI-compatible `/chat/completions` Provider,支持流式文本和 function calling。
|
|
13
15
|
- `QWEN_API_KEY` 使用服务端 `SecretRef`,只在 Provider Adapter 发请求前解析。
|
|
14
16
|
- 公网 Endpoint 必须为 HTTPS,并精确命中
|
|
@@ -173,6 +175,28 @@ Agent profile 还必须挂载 `AI_PROVIDER_AUDIT_STORE_PATH`。create-windy Star
|
|
|
173
175
|
`/app/data/agent/provider-audit.sqlite3` 并复用单副本持久卷;自行维护 Compose 的项目
|
|
174
176
|
需要显式补齐该变量。未配置时 Provider 启动 fail closed,并给出缺失变量名。
|
|
175
177
|
|
|
178
|
+
## Developer-only ADK Web
|
|
179
|
+
|
|
180
|
+
ADK Web 使用独立 profile 启动:
|
|
181
|
+
|
|
182
|
+
```bash
|
|
183
|
+
bun run dev:adk-web
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
访问地址为 `http://127.0.0.1:8000`。Compose 服务在容器内监听 `0.0.0.0:8000`,
|
|
187
|
+
但端口只发布到宿主机 `127.0.0.1`。普通 `dev`、`agent` profile 和生产 Compose 均不
|
|
188
|
+
包含该服务。
|
|
189
|
+
|
|
190
|
+
该界面明确显示“Windy ADK 开发调试(无业务工具)”,并只加载一个确定性 Agent:
|
|
191
|
+
|
|
192
|
+
- 不读取 `QWEN_*`、Provider URL/Secret 或 `AGENT_SERVER_INTERNAL_TOKEN`;
|
|
193
|
+
- 不注册业务 Tool,不连接 Tool Host 或业务 Repository;
|
|
194
|
+
- 不接收 Actor、Scope 或 Execution Grant;
|
|
195
|
+
- 只用于观察 ADK Web、Session 和 Event,不代表真实业务 Operation。
|
|
196
|
+
|
|
197
|
+
真实业务调试必须继续从 canonical Run API 发起并提供 Southwind AI Server 签发的
|
|
198
|
+
Execution Grant;不能通过 ADK Web 补造或绕过这条授权链。
|
|
199
|
+
|
|
176
200
|
## 客户端协议
|
|
177
201
|
|
|
178
202
|
浏览器继续使用 `@southwind-ai/ai-client` 访问同源 Southwind AI Server:
|
|
@@ -188,15 +212,17 @@ SSE 事件包括 `output.delta`、`tool.requested`、`tool.completed`、usage,
|
|
|
188
212
|
`run.completed`、`run.failed` 或 `run.cancelled` 终态。断线后使用已确认 sequence 作为
|
|
189
213
|
cursor 恢复;不要重新创建 Run 或重复执行 Tool。
|
|
190
214
|
|
|
191
|
-
## 从 create-windy 0.2.
|
|
215
|
+
## 从 create-windy 0.2.34 升级到 0.3.1
|
|
192
216
|
|
|
193
|
-
|
|
217
|
+
目标 CLI 会一次解析 `0.2.34 → 0.3.0 → 0.3.1` 连续 recipe,不需要逐个安装中间
|
|
218
|
+
CLI。确认 npm registry 已能解析精确的 `0.3.1` artifact 后,先检查计划,再应用:
|
|
194
219
|
|
|
195
220
|
```bash
|
|
196
|
-
bunx --bun create-windy@0.
|
|
197
|
-
bunx --bun create-windy@0.
|
|
221
|
+
bunx --bun create-windy@0.3.1 update --check
|
|
222
|
+
bunx --bun create-windy@0.3.1 update
|
|
198
223
|
bun install
|
|
199
224
|
```
|
|
200
225
|
|
|
201
226
|
升级器会更新受管模板文件;本地修改产生冲突时会 fail closed 并保留事务备份,不会静默
|
|
202
|
-
覆盖。升级后按上面的环境变量启用 `agent` profile
|
|
227
|
+
覆盖。升级后按上面的环境变量启用 `agent` profile;`agent-debug` 仍只用于无业务 Tool
|
|
228
|
+
的本机调试。自动化部署应固定精确版本,不能用未核验的 `latest` 替代。
|
|
@@ -94,6 +94,10 @@ Transport 返回协议允许的状态。
|
|
|
94
94
|
http://localhost:9747/api/mcp
|
|
95
95
|
```
|
|
96
96
|
|
|
97
|
+
普通 Server 已提供该 Endpoint,不需要启动 `dev:agent`。非浏览器 MCP 客户端先通过
|
|
98
|
+
`POST /api/auth/api/login` 获取 Bearer Token,再调用 `tools/list` 获取当前账号实时可见
|
|
99
|
+
的 Tool 与 JSON Schema;客户端和文档都不应固化 Tool 清单。
|
|
100
|
+
|
|
97
101
|
MCP 使用无状态模式,不保存授权快照或长期 Session。Feature、角色或数据范围变化会在下一次
|
|
98
102
|
调用立即生效。
|
|
99
103
|
|