create-windy 0.2.34 → 0.3.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 +167 -17
- package/package.json +1 -1
- package/template/.windy-template.json +2 -2
- package/template/AGENTS.md +5 -0
- package/template/README.md +3 -1
- 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/test_adk_adapter.py +305 -0
- package/template/apps/agent-server/tests/test_adk_web_agent.py +12 -0
- 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.ts +44 -3
- 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 +23 -6
- 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/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/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.ts +37 -9
- 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/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 +70 -36
- 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/SystemResourcePage.vue +91 -98
- 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/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/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/system-api-permissions.ts +54 -0
- 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-modules.test.ts +13 -1
- package/template/packages/modules/src/system-modules.ts +12 -0
- package/template/packages/modules/src/system-permissions.ts +9 -5
- package/template/packages/server-sdk/package.json +1 -1
- package/template/packages/shared/package.json +1 -1
- package/template/packages/shared/src/audit.ts +25 -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;
|
|
@@ -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.0 update --check --cwd .
|
|
82
|
+
bunx --bun create-windy@0.3.0 update --dry-run --cwd .
|
|
83
|
+
bunx --bun create-windy@0.3.0 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.0
|
|
192
216
|
|
|
193
|
-
|
|
217
|
+
`0.2.34 → 0.3.0` 使用连续 recipe,不需要逐个安装中间 CLI。确认 npm registry 已能
|
|
218
|
+
解析精确的 `0.3.0` 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.0 update --check
|
|
222
|
+
bunx --bun create-windy@0.3.0 update
|
|
198
223
|
bun install
|
|
199
224
|
```
|
|
200
225
|
|
|
201
226
|
升级器会更新受管模板文件;本地修改产生冲突时会 fail closed 并保留事务备份,不会静默
|
|
202
|
-
覆盖。升级后按上面的环境变量启用 `agent` profile
|
|
227
|
+
覆盖。升级后按上面的环境变量启用 `agent` profile;`agent-debug` 仍只用于无业务 Tool
|
|
228
|
+
的本机调试。自动化部署应固定精确版本,不能用未核验的 `latest` 替代。
|
|
@@ -17,11 +17,53 @@
|
|
|
17
17
|
|
|
18
18
|
批量任务派发器每 5 秒轮询一次。定时执行返回 `claimed: false` 代表没有业务任务可领取,Scheduler 完成并立即丢弃这条 idle run,也不写 `task.execute` 审计;人工触发即使没有领取任务仍保留执行历史和 `task.trigger` / `task.execute`,真实领取、失败、重试和 Guard 拒绝同样完整留痕。idle 判定由任务定义显式声明,普通任务不会自动套用。
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
审计页面支持关键字、操作者、动作以及本地开始/结束分钟筛选。操作者候选来自最近
|
|
21
|
+
100 条审计记录,动作候选来自 Module Composition 形成的稳定动作目录,并以中文名称和
|
|
22
|
+
说明展示;筛选请求继续传递稳定 action key。`datetime-local` 输入按用户浏览器本地时区
|
|
23
|
+
转换为 ISO:开始分钟使用 `00.000` 秒,结束分钟包含至 `59.999` 秒。内存与 PostgreSQL
|
|
24
|
+
Reader 使用同一组含边界时间;时间不是完整 ISO 或开始晚于结束时返回
|
|
25
|
+
`400 INVALID_AUDIT_TIME_RANGE`。
|
|
26
|
+
|
|
27
|
+
关键字 `q` 会做 NFKC、首尾空白和连续空白规范化,最长 120 个字符。搜索范围包括动作
|
|
28
|
+
Key、操作者 ID/名称、目标类型/ID/标签、IP、message、module、requestId,以及已经过
|
|
29
|
+
审计脱敏并持久化的 metadata 文本。PostgreSQL 查询使用参数绑定并把 `%`、`_` 和反斜线
|
|
30
|
+
当作普通字符,不执行客户端表达式,也不查询登录 Session、业务明文表或未持久化请求体。
|
|
31
|
+
列表和导出共享完全相同的 `q`、操作者、动作与时间筛选;导出的
|
|
32
|
+
`includeScheduler` 默认关闭,只有显式勾选时才包含调度器事件。
|
|
21
33
|
|
|
22
34
|
常规审计列表在服务端固定排除 `system_scheduler` 执行身份产生的日志,客户端不能构造参数绕过;调度执行结果统一到定时任务的执行历史查看。审计导出提供“包含定时器日志”选项,默认关闭,只影响本次导出,不改变页面列表状态。
|
|
23
35
|
|
|
24
|
-
|
|
36
|
+
动作列直接显示中文动作名,并优先使用 event.message、target、module 与已记录 metadata
|
|
37
|
+
组成一行操作摘要;CRUD、搜索、功能开关和 Agent Tool 等常见动作提供稳定中文表达,
|
|
38
|
+
未知业务动作保留 action key、对象和实际上下文,不推断未记录事实。“查看详情”使用
|
|
39
|
+
shadcn-vue Dialog,以安全纯文本展示动作说明、结果、操作者、IP、时间、操作对象、
|
|
40
|
+
message、module、requestId、User-Agent 和已脱敏 metadata,不使用 `v-html`。
|
|
41
|
+
非空通用资源搜索记录 `data.search`;审计列表自身不递归记录搜索。自由文本可能包含
|
|
42
|
+
手机号或邮箱,因此部署方应把审计访问权限、保留期和导出权限视为敏感数据治理边界;
|
|
43
|
+
默认字段名脱敏不等于自由文本内容脱敏。
|
|
44
|
+
|
|
45
|
+
## HTTP 请求来源与可信代理
|
|
46
|
+
|
|
47
|
+
平台 HTTP 审计的 IP 与 User-Agent 来自当前请求,不写回认证 Resolver 返回的 Session
|
|
48
|
+
Actor。Bun `server.requestIP(request)` 提供的直接对端先经过 IP 规范化;User-Agent
|
|
49
|
+
去除首尾空白并最多保留 1024 个字符。所有由 Guard、平台 Route、业务 Module 与
|
|
50
|
+
Agent Tool 在该请求内产生的审计事件,都通过 `RequestGuardContext` 复制这份来源。
|
|
51
|
+
|
|
52
|
+
默认 `WINDY_TRUST_PROXY=false`,此时无条件忽略客户端提交的 `X-Forwarded-For`,只记录
|
|
53
|
+
合法的直接对端 IP。只有同时满足以下条件时才采用转发来源:
|
|
54
|
+
|
|
55
|
+
1. `WINDY_TRUST_PROXY=true`;
|
|
56
|
+
2. 直接对端是 loopback,或在 `WINDY_TRUSTED_PROXY_IPS` 的逗号分隔精确 IP 列表内;
|
|
57
|
+
3. `X-Forwarded-For` 只包含一个合法 IPv4/IPv6 地址。
|
|
58
|
+
|
|
59
|
+
多段转发链、非法地址或不受信直接对端均退回直接对端 IP。当前策略有意不猜测多级代理
|
|
60
|
+
链;部署多个反向代理时,应由最后一个受信代理清洗并输出单一客户端地址。配置了
|
|
61
|
+
`WINDY_TRUSTED_PROXY_IPS` 却未显式启用信任、布尔值不是 `true/false`、或列表含非法
|
|
62
|
+
IP 时,Server 启动 fail closed。
|
|
63
|
+
|
|
64
|
+
数据库 schema 早已包含 `audit_logs.ip` 与 `user_agent`,因此本功能无需 migration。
|
|
65
|
+
历史日志、非 HTTP 后台事件,以及升级前未复制请求来源的事件仍可能没有 IP 或
|
|
66
|
+
User-Agent,管理端明确显示“未记录”,不会用当前 Session 或其它字段猜测补齐。
|
|
25
67
|
|
|
26
68
|
每次 PostgreSQL 写入默认最多尝试 3 次。重试间隔按指数增长,并受最大间隔约束,不会无限重试或无限阻塞请求。恢复队列也有最大条数;队列已满等同于队列不可用,关键动作会失败。
|
|
27
69
|
|
|
@@ -77,6 +119,8 @@ License 上传仍只记录候选摘要、可识别的 License ID、公钥 ID 和
|
|
|
77
119
|
| `WINDY_AUDIT_QUEUE_FILE` | `./data/audit/recovery-queue.json` | 关键事件恢复文件 |
|
|
78
120
|
| `WINDY_AUDIT_QUEUE_SIZE` | `1000` | 关键恢复队列最大事件数,上限 `100000` |
|
|
79
121
|
| `AUDIT_REDACT_FIELDS` | 空 | 在默认敏感字段之外追加的逗号分隔字段名 |
|
|
122
|
+
| `WINDY_TRUST_PROXY` | `false` | 是否允许从受信直接代理读取单一转发 IP |
|
|
123
|
+
| `WINDY_TRUSTED_PROXY_IPS` | 空 | 受信直接代理的精确 IP,逗号分隔 |
|
|
80
124
|
|
|
81
125
|
生产环境未配置审计持久化 writer 时,关键动作不会回退为内存 best-effort,而是直接 fail closed。普通审计仍维持业务可用性,但健康状态会显示写入失败。
|
|
82
126
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# 类型化模块配置与 Secret 边界
|
|
2
2
|
|
|
3
|
-
更新时间:2026-07-
|
|
3
|
+
更新时间:2026-07-26。
|
|
4
4
|
|
|
5
5
|
## 目标与边界
|
|
6
6
|
|
|
@@ -54,16 +54,25 @@ Secret 配置分成引用和运行时值两层:
|
|
|
54
54
|
- `applying`:版本已入库,正在调用模块 Apply Adapter;
|
|
55
55
|
- `effective`:当前生效版本,同模块同时只能有一个;
|
|
56
56
|
- `failed`:schema 已通过但运行时应用失败,稳定错误不包含底层异常正文;
|
|
57
|
+
- `pending-restart`:版本已持久化但包含需重启字段,当前进程不冒充已生效;下次启动校验并应用成功后提升为 `effective`;
|
|
57
58
|
- `superseded`:曾经生效,已被后续版本替代。
|
|
58
59
|
|
|
59
60
|
版本号由 PostgreSQL transaction 内的 module-key advisory lock 分配,唯一索引继续提供最终并发保护。应用成功后,Repository 在事务内把旧 `effective` 标为 `superseded` 并把新版本标为 `effective`;应用失败只标记新版本为 `failed`,原生效版本不变。Server 启动时先读取每个已注册模块的生效版本并通过同一 schema 与 Apply Adapter 恢复,再建立 foundation 和探活运行时。
|
|
60
61
|
|
|
61
|
-
|
|
62
|
+
当前 `platform.runtime` 白名单包括:
|
|
62
63
|
|
|
63
64
|
- `enableAI`:更新 foundation 平台能力投影;
|
|
64
65
|
- `cache.healthTimeoutMs`:后续 Redis 探活按最新值创建 probe。
|
|
66
|
+
- `security.sessionTtlSeconds`:即时影响新签发会话,已有会话保持原过期时间;
|
|
67
|
+
- `security.passwordMinLength/passwordMaxLength`:即时影响 Argon2 密码修改、重置校验和密码页服务端策略;
|
|
68
|
+
- `storage.bulkDataMaxByteSize/storage.brandingLogoMaxByteSize`:即时影响后续上传会话;
|
|
69
|
+
- `regional.timeZone`:即时影响公共平台设置投影和 Web 后续日期格式化;
|
|
70
|
+
- `mail.enabled/host/port/secure/fromAddress/connectTimeoutMs`:保存为
|
|
71
|
+
`pending-restart`,下次 Server 启动后进入 typed runtime config。
|
|
65
72
|
|
|
66
|
-
|
|
73
|
+
同一版本只要变更了需重启字段,就整体等待重启,避免一部分字段先应用造成原子性错觉。旧版本缺少后来新增的字段时,Apply Adapter 使用当前环境基线补齐,再经过完整 schema 校验;因此升级不要求回写历史行。数据库、Redis URL、SMTP URL、License 机器码、签名 Token 和 Vault Token 等 Secret 或引用均不进入版本表、diff、普通 API、管理界面或审计 metadata。`MAIL_SMTP_URL` 仍只由 env SecretResolver 注入进程内配置。
|
|
74
|
+
|
|
75
|
+
邮件字段由通知模块的标准 SMTP Adapter 在 Server 组合根消费。`mail.enabled=false` 时不注册邮件渠道;启用后可使用 env-only `MAIL_SMTP_URL`,或使用 `host/port/secure/fromAddress/connectTimeoutMs` 建立传输。保存配置仍是 `pending-restart`,只有重启后新建的 Adapter 才读取新值。配置存在不等于必然送达:实际结果由持久 outbox、用户偏好、收件地址和 SMTP 响应共同决定。短信供应商实现不属于平台配置,业务项目必须通过通知 ChannelAdapter Port 注入,平台不会猜供应商报文。
|
|
67
76
|
|
|
68
77
|
## 回滚、权限与审计
|
|
69
78
|
|
|
@@ -71,7 +80,7 @@ Secret 配置分成引用和运行时值两层:
|
|
|
71
80
|
|
|
72
81
|
配置读取要求 `system.config.read`,创建版本和回滚要求 `system.config.manage`;菜单、Web Router 和所有四个 API 绑定统一引用 `system.config` Feature 与 System Module Manifest。mutation 在改配置前先写 `critical` intent 审计,审计不可持久化或进入恢复队列时返回 `503 AUDIT_UNAVAILABLE` 且不创建版本。结果审计只记录 module Key、版本、状态、回滚来源和 changed paths,不记录 before/after 值、请求值、Secret 或 `SecretRef`。
|
|
73
82
|
|
|
74
|
-
管理端 `/system/configurations`
|
|
83
|
+
管理端 `/system/configurations` 只根据服务端字段描述生成编辑表单,显示每个字段即时/重启语义、环境/版本来源、生效版本、diff、失败或等待重启状态和历史;回滚必须经过带原因的显式确认。编辑值或原因未保存时,路由离开和刷新分别使用应用 Dialog 与原生 `beforeunload` 保护,保存失败会保留当前输入。
|
|
75
84
|
|
|
76
85
|
## 运维与验证
|
|
77
86
|
|