create-windy 0.2.19 → 0.2.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (127) hide show
  1. package/README.md +5 -1
  2. package/dist/cli.js +392 -160
  3. package/package.json +1 -1
  4. package/template/.windy-template.json +2 -2
  5. package/template/README.md +1 -0
  6. package/template/apps/agent-server/Dockerfile +20 -0
  7. package/template/apps/agent-server/README.md +46 -0
  8. package/template/apps/agent-server/pyproject.toml +44 -0
  9. package/template/apps/agent-server/src/southwind_agent_server/__init__.py +5 -0
  10. package/template/apps/agent-server/src/southwind_agent_server/adk_adapter.py +39 -0
  11. package/template/apps/agent-server/src/southwind_agent_server/app.py +263 -0
  12. package/template/apps/agent-server/src/southwind_agent_server/config.py +141 -0
  13. package/template/apps/agent-server/src/southwind_agent_server/contracts.py +99 -0
  14. package/template/apps/agent-server/src/southwind_agent_server/deterministic.py +52 -0
  15. package/template/apps/agent-server/src/southwind_agent_server/engine.py +85 -0
  16. package/template/apps/agent-server/src/southwind_agent_server/openai_engine.py +197 -0
  17. package/template/apps/agent-server/src/southwind_agent_server/openai_provider.py +246 -0
  18. package/template/apps/agent-server/src/southwind_agent_server/service.py +180 -0
  19. package/template/apps/agent-server/src/southwind_agent_server/sqlite_store.py +317 -0
  20. package/template/apps/agent-server/src/southwind_agent_server/sse.py +41 -0
  21. package/template/apps/agent-server/src/southwind_agent_server/store.py +144 -0
  22. package/template/apps/agent-server/src/southwind_agent_server/tool_host.py +80 -0
  23. package/template/apps/agent-server/tests/test_api.py +207 -0
  24. package/template/apps/agent-server/tests/test_config.py +56 -0
  25. package/template/apps/agent-server/tests/test_openai_provider.py +224 -0
  26. package/template/apps/agent-server/tests/test_sqlite_store.py +93 -0
  27. package/template/apps/agent-server/uv.lock +925 -0
  28. package/template/apps/server/src/agent/execution-grants.ts +89 -0
  29. package/template/apps/server/src/agent/remote-runtime.ts +128 -0
  30. package/template/apps/server/src/agent/run-contracts.ts +43 -0
  31. package/template/apps/server/src/agent/run-facade.test.ts +257 -0
  32. package/template/apps/server/src/agent/run-facade.ts +190 -0
  33. package/template/apps/server/src/agent/run-routes.ts +222 -0
  34. package/template/apps/server/src/agent/runtime-bootstrap.ts +53 -0
  35. package/template/apps/server/src/agent/tool-host.test.ts +242 -0
  36. package/template/apps/server/src/agent/tool-host.ts +153 -0
  37. package/template/apps/server/src/application-services.ts +2 -2
  38. package/template/apps/server/src/audit/search-summary.ts +15 -8
  39. package/template/apps/server/src/example-modules.ts +58 -70
  40. package/template/apps/server/src/foundation.ts +8 -4
  41. package/template/apps/server/src/guards.test.ts +13 -0
  42. package/template/apps/server/src/guards.ts +12 -2
  43. package/template/apps/server/src/index.ts +48 -95
  44. package/template/apps/server/src/installed-business-modules.ts +23 -4
  45. package/template/apps/server/src/module-bootstrap.ts +83 -0
  46. package/template/apps/server/src/module-composition.test.ts +54 -1
  47. package/template/apps/server/src/module-composition.ts +58 -0
  48. package/template/apps/server/src/module-host/initialize-contracts.ts +67 -0
  49. package/template/apps/server/src/module-host/initialize.test.ts +192 -0
  50. package/template/apps/server/src/module-host/initialize.ts +328 -0
  51. package/template/apps/server/src/module-host/request-context.test.ts +66 -0
  52. package/template/apps/server/src/module-host/request-context.ts +156 -0
  53. package/template/apps/server/src/module-host/role-presets.test.ts +94 -0
  54. package/template/apps/server/src/module-host/role-presets.ts +76 -0
  55. package/template/apps/server/src/module-host/route-installer.test.ts +317 -0
  56. package/template/apps/server/src/module-host/route-installer.ts +97 -0
  57. package/template/apps/server/src/module-host/route-registration.test.ts +160 -0
  58. package/template/apps/server/src/module-host/search-providers.ts +31 -0
  59. package/template/apps/server/src/module-host/storage-port.test.ts +165 -0
  60. package/template/apps/server/src/module-host/storage-port.ts +59 -0
  61. package/template/apps/server/src/module-host/task-definitions.ts +41 -0
  62. package/template/apps/server/src/module-host/test-fixtures.ts +26 -0
  63. package/template/apps/server/src/module-host/tool-handlers.ts +25 -0
  64. package/template/apps/server/src/module-host.test.ts +202 -58
  65. package/template/apps/server/src/module-host.ts +35 -113
  66. package/template/apps/server/src/module-runtime-validation.ts +40 -0
  67. package/template/apps/server/src/route-guards.test.ts +112 -1
  68. package/template/apps/server/src/runtime-feature.ts +66 -31
  69. package/template/apps/server/src/runtime.test.ts +1 -0
  70. package/template/apps/server/src/runtime.ts +3 -1
  71. package/template/apps/server/src/system/built-in-roles.ts +1 -1
  72. package/template/apps/server/src/work-order/foundation-modules.test.ts +30 -10
  73. package/template/apps/server/src/work-order/routes.test.ts +30 -18
  74. package/template/apps/server/src/work-order/routes.ts +104 -135
  75. package/template/apps/server/src/work-order/search-api.test.ts +14 -3
  76. package/template/apps/server/src/work-order/search-provider.test.ts +23 -15
  77. package/template/apps/server/src/work-order/search-provider.ts +17 -12
  78. package/template/apps/server/src/work-order/task.test.ts +16 -11
  79. package/template/apps/web/Dockerfile +4 -1
  80. package/template/apps/web/src/layout/GlobalWatermark.layering.webtest.ts +4 -1
  81. package/template/apps/web/src/layout/GlobalWatermark.vue +2 -0
  82. package/template/apps/web/src/layout/GlobalWatermark.webtest.ts +4 -1
  83. package/template/docker-compose.yml +25 -0
  84. package/template/docs/architecture/ai-runtime.md +744 -0
  85. package/template/docs/architecture/object-storage.md +12 -0
  86. package/template/docs/platform/agent-runtime.md +128 -0
  87. package/template/docs/platform/agent-tools.md +8 -1
  88. package/template/package.json +1 -0
  89. package/template/packages/config/index.test.ts +100 -0
  90. package/template/packages/config/index.ts +1 -0
  91. package/template/packages/config/package.json +2 -1
  92. package/template/packages/config/src/ai-openai-compatible.ts +131 -0
  93. package/template/packages/config/src/platform.ts +28 -1
  94. package/template/packages/database/package.json +1 -1
  95. package/template/packages/database/src/runner.ts +7 -9
  96. package/template/packages/example-work-order/index.ts +1 -0
  97. package/template/packages/example-work-order/package.json +2 -0
  98. package/template/packages/example-work-order/src/server-module.ts +61 -0
  99. package/template/packages/jobs/package.json +1 -1
  100. package/template/packages/jobs/src/types.ts +7 -1
  101. package/template/packages/modules/package.json +1 -1
  102. package/template/packages/modules/src/catalog-validation.test.ts +212 -0
  103. package/template/packages/modules/src/catalog-validation.ts +19 -2
  104. package/template/packages/modules/src/migration-bundle-validation.test.ts +191 -0
  105. package/template/packages/server-sdk/index.ts +53 -0
  106. package/template/packages/server-sdk/package.json +18 -3
  107. package/template/packages/server-sdk/src/actor.ts +15 -0
  108. package/template/packages/server-sdk/src/ai-operation-registration.ts +31 -0
  109. package/template/packages/server-sdk/src/audit-port.ts +22 -0
  110. package/template/packages/server-sdk/src/jobs-port.ts +24 -0
  111. package/template/packages/server-sdk/src/module-host.ts +41 -1
  112. package/template/packages/server-sdk/src/namespace.test.ts +26 -0
  113. package/template/packages/server-sdk/src/namespace.ts +13 -0
  114. package/template/packages/server-sdk/src/request-context.ts +21 -0
  115. package/template/packages/server-sdk/src/role-preset.ts +18 -0
  116. package/template/packages/server-sdk/src/route-definition.ts +26 -0
  117. package/template/packages/server-sdk/src/search-provider-port.ts +48 -0
  118. package/template/packages/server-sdk/src/storage-port.ts +25 -0
  119. package/template/packages/server-sdk/src/task-registration.ts +18 -0
  120. package/template/packages/server-sdk/src/tool-host-port.ts +22 -0
  121. package/template/packages/server-sdk/src/tool-registration.ts +29 -0
  122. package/template/packages/shared/index.ts +1 -0
  123. package/template/packages/shared/package.json +1 -1
  124. package/template/packages/shared/src/license-catalog.test.ts +73 -1
  125. package/template/packages/shared/src/license-catalog.ts +95 -0
  126. package/template/packages/{database/src/bundle-validation.ts → shared/src/migration-bundle-validation.ts} +2 -1
  127. package/template/apps/server/src/work-order/task.ts +0 -30
@@ -26,8 +26,61 @@ describe("Server Module Composition", () => {
26
26
  { method: "POST", path: "/api/auth/users/:id/password" },
27
27
  ],
28
28
  }),
29
+ ).toThrow("API Route Handler Key 重复:POST /api/auth/users/:id/password");
30
+ });
31
+
32
+ test("Host 注册实现必须在 Manifest 声明", () => {
33
+ const app = new Elysia({ prefix: "/api" }).post(
34
+ "/auth/users/:id/password",
35
+ () => null,
36
+ );
37
+ const base = {
38
+ modules: [fixture()],
39
+ routes: app.routes,
40
+ tasks: [],
41
+ tools: new Map(),
42
+ providers: [],
43
+ };
44
+
45
+ expect(() =>
46
+ validateServerModuleComposition({
47
+ ...base,
48
+ moduleRegistrations: {
49
+ routes: ["POST /api/auth/users/:id/password"],
50
+ },
51
+ }),
52
+ ).not.toThrow();
53
+
54
+ expect(() =>
55
+ validateServerModuleComposition({
56
+ ...base,
57
+ moduleRegistrations: { routes: ["GET /api/auth/users"] },
58
+ }),
59
+ ).toThrow("模块路由已注册但未在 Manifest 声明:GET /api/auth/users");
60
+
61
+ expect(() =>
62
+ validateServerModuleComposition({
63
+ ...base,
64
+ moduleRegistrations: { tasks: ["system.unknown-task"] },
65
+ }),
66
+ ).toThrow(
67
+ "定时任务 Handler已注册但未在 Manifest 声明:system.unknown-task",
68
+ );
69
+
70
+ expect(() =>
71
+ validateServerModuleComposition({
72
+ ...base,
73
+ moduleRegistrations: { providers: ["system.unknown-search"] },
74
+ }),
75
+ ).toThrow("搜索 Provider已注册但未在 Manifest 声明:system.unknown-search");
76
+
77
+ expect(() =>
78
+ validateServerModuleComposition({
79
+ ...base,
80
+ moduleRegistrations: { aiOperations: ["system.unknown-operation"] },
81
+ }),
29
82
  ).toThrow(
30
- "API Route Handler Key 重复:POST /api/auth/users/:id/password",
83
+ "AI Operation Handler已注册但未在 Manifest 声明:system.unknown-operation",
31
84
  );
32
85
  });
33
86
  });
@@ -8,12 +8,26 @@ interface RegisteredHttpRoute {
8
8
  path: string;
9
9
  }
10
10
 
11
+ /**
12
+ * 模块经 Host 注册的实现 Key,用于校验“实现了但没在 Manifest 声明”。
13
+ * 反向(声明了没实现)由 validateModuleComposition 的实现绑定校验覆盖。
14
+ */
15
+ export interface ModuleImplementationRegistrations {
16
+ routes?: readonly string[];
17
+ tasks?: readonly string[];
18
+ tools?: readonly string[];
19
+ aiOperations?: readonly string[];
20
+ providers?: readonly string[];
21
+ }
22
+
11
23
  export interface ServerCompositionInput {
12
24
  modules: readonly ModuleManifest[];
13
25
  routes: readonly RegisteredHttpRoute[];
14
26
  tasks?: readonly { key: string }[];
15
27
  tools?: ReadonlyMap<string, unknown>;
28
+ aiOperations?: readonly string[];
16
29
  providers?: readonly string[];
30
+ moduleRegistrations?: ModuleImplementationRegistrations;
17
31
  }
18
32
 
19
33
  export function validateServerModuleComposition(
@@ -23,6 +37,50 @@ export function validateServerModuleComposition(
23
37
  apiRoutes: input.routes.map(apiRouteKey),
24
38
  tasks: (input.tasks || []).map(({ key }) => key),
25
39
  tools: [...(input.tools?.keys() || [])],
40
+ aiOperations: input.aiOperations,
26
41
  providers: input.providers || [],
27
42
  });
43
+ if (input.moduleRegistrations) {
44
+ validateRegistrationConsistency(input.modules, input.moduleRegistrations);
45
+ }
46
+ }
47
+
48
+ function validateRegistrationConsistency(
49
+ modules: readonly ModuleManifest[],
50
+ registrations: ModuleImplementationRegistrations,
51
+ ): void {
52
+ const declaredRoutes = new Set(
53
+ modules.flatMap((module) => module.apiPermissions.map(apiRouteKey)),
54
+ );
55
+ const declaredTasks = new Set(
56
+ modules.flatMap((module) => module.tasks.map(({ key }) => key)),
57
+ );
58
+ const declaredTools = new Set(
59
+ modules.flatMap((module) => module.tools.map(({ key }) => key)),
60
+ );
61
+ const declaredAiOperations = new Set(
62
+ modules.flatMap((module) =>
63
+ (module.aiOperations ?? []).map(({ key }) => key),
64
+ ),
65
+ );
66
+ const declaredProviders = new Set(
67
+ modules.flatMap((module) => module.providers.map(({ key }) => key)),
68
+ );
69
+ for (const [label, registered, declared] of [
70
+ ["模块路由", registrations.routes || [], declaredRoutes],
71
+ ["定时任务 Handler", registrations.tasks || [], declaredTasks],
72
+ ["Agent Tool Handler", registrations.tools || [], declaredTools],
73
+ [
74
+ "AI Operation Handler",
75
+ registrations.aiOperations || [],
76
+ declaredAiOperations,
77
+ ],
78
+ ["搜索 Provider", registrations.providers || [], declaredProviders],
79
+ ] as const) {
80
+ for (const key of registered) {
81
+ if (!declared.has(key)) {
82
+ throw new Error(`${label}已注册但未在 Manifest 声明:${key}`);
83
+ }
84
+ }
85
+ }
28
86
  }
@@ -0,0 +1,67 @@
1
+ import type { DurableHandlerRegistry } from "@southwind-ai/jobs";
2
+ import type { ModuleManifest } from "@southwind-ai/modules";
3
+ import type {
4
+ InstallableServerModule,
5
+ ModuleAiOperationHandlerRegistration,
6
+ ModuleInitializer,
7
+ ModuleRolePreset,
8
+ ModuleRouteDefinition,
9
+ ModuleSearchProvider,
10
+ ModuleTaskHandlerRegistration,
11
+ ModuleToolHandlerRegistration,
12
+ ModuleUploadPolicy,
13
+ } from "@southwind-ai/server-sdk";
14
+
15
+ export interface RegisteredModuleRoute {
16
+ readonly moduleName: string;
17
+ readonly definition: ModuleRouteDefinition;
18
+ }
19
+
20
+ export interface RegisteredModuleTaskHandler {
21
+ readonly moduleName: string;
22
+ readonly registration: ModuleTaskHandlerRegistration;
23
+ }
24
+
25
+ export interface RegisteredModuleToolHandler {
26
+ readonly moduleName: string;
27
+ readonly registration: ModuleToolHandlerRegistration;
28
+ }
29
+
30
+ export interface RegisteredModuleAiOperationHandler {
31
+ readonly moduleName: string;
32
+ readonly registration: ModuleAiOperationHandlerRegistration;
33
+ }
34
+
35
+ export interface RegisteredModuleRolePreset {
36
+ readonly moduleName: string;
37
+ readonly preset: ModuleRolePreset;
38
+ }
39
+
40
+ export interface RegisteredModuleSearchProvider {
41
+ readonly moduleName: string;
42
+ readonly provider: ModuleSearchProvider;
43
+ }
44
+
45
+ export interface InitializeModuleHostsInput {
46
+ modules: readonly ModuleManifest[];
47
+ /** Manifest 与 setup 由同一业务模块安装单元贡献。 */
48
+ installations?: readonly InstallableServerModule<ModuleManifest>[];
49
+ /** @deprecated 仅供旧模块迁移;新模块必须使用 installations。 */
50
+ initializers?: readonly ModuleInitializer[];
51
+ baseUploadPolicies?: readonly ModuleUploadPolicy[];
52
+ }
53
+
54
+ export interface InitializedModuleHosts {
55
+ readonly manifests: readonly ModuleManifest[];
56
+ readonly uploadPolicies: readonly ModuleUploadPolicy[];
57
+ readonly routes: readonly RegisteredModuleRoute[];
58
+ readonly taskHandlers: readonly RegisteredModuleTaskHandler[];
59
+ readonly toolHandlers: readonly RegisteredModuleToolHandler[];
60
+ readonly aiOperationHandlers: readonly RegisteredModuleAiOperationHandler[];
61
+ readonly rolePresets: readonly RegisteredModuleRolePreset[];
62
+ readonly searchProviders: readonly RegisteredModuleSearchProvider[];
63
+ manifestOf(moduleName: string): ModuleManifest;
64
+ /** 该模块经 Host 注册的上传用途集合(不含平台基础用途)。 */
65
+ uploadPurposesOf(moduleName: string): ReadonlySet<string>;
66
+ installDurableHandlers(registry: DurableHandlerRegistry): void;
67
+ }
@@ -0,0 +1,192 @@
1
+ import { describe, expect, test } from "bun:test";
2
+ import { DurableHandlerRegistry } from "@southwind-ai/jobs";
3
+ import type { ModuleInitializer } from "@southwind-ai/server-sdk";
4
+ import { initializeModuleHosts } from "./initialize.js";
5
+ import { manifest } from "./test-fixtures.js";
6
+
7
+ describe("模块 Host 初始化", () => {
8
+ test("按安装顺序完成初始化后再创建运行时注册表", async () => {
9
+ const events: string[] = [];
10
+ const first = initializer("alpha", async (host) => {
11
+ events.push("alpha:start");
12
+ await Promise.resolve();
13
+ host.registerDurableHandler({
14
+ key: "alpha.refresh",
15
+ payload: { parse: () => ({}) },
16
+ execute: () => undefined,
17
+ });
18
+ events.push("alpha:end");
19
+ });
20
+ const second = initializer("beta", () => {
21
+ events.push("beta");
22
+ });
23
+
24
+ const initialized = await initializeModuleHosts({
25
+ modules: [manifest("alpha"), manifest("beta")],
26
+ initializers: [first, second],
27
+ });
28
+ expect(events).toEqual(["alpha:start", "alpha:end", "beta"]);
29
+
30
+ const registry = new DurableHandlerRegistry();
31
+ initialized.installDurableHandlers(registry);
32
+ expect(registry.keys()).toEqual(["alpha.refresh"]);
33
+ });
34
+
35
+ test("未知模块、错误命名空间和重复注册立即失败", async () => {
36
+ await expect(
37
+ initializeModuleHosts({
38
+ modules: [manifest("known")],
39
+ initializers: [initializer("missing", () => undefined)],
40
+ }),
41
+ ).rejects.toThrow("模块初始化器与已安装模块不匹配:missing");
42
+
43
+ await expect(
44
+ initializeModuleHosts({
45
+ modules: [manifest("known")],
46
+ initializers: [
47
+ initializer("known", (host) => {
48
+ host.registerUploadPolicy(policy("other.attachment"));
49
+ }),
50
+ ],
51
+ }),
52
+ ).rejects.toThrow("上传用途不属于模块 known:other.attachment");
53
+
54
+ await expect(
55
+ initializeModuleHosts({
56
+ modules: [manifest("known")],
57
+ initializers: [
58
+ initializer("known", (host) => {
59
+ host.registerUploadPolicy(policy("known.attachment"));
60
+ host.registerUploadPolicy(policy("known.attachment"));
61
+ }),
62
+ ],
63
+ }),
64
+ ).rejects.toThrow("上传用途重复注册:known.attachment");
65
+
66
+ await expect(
67
+ initializeModuleHosts({
68
+ modules: [],
69
+ initializers: [],
70
+ baseUploadPolicies: [policy("platform.file"), policy("platform.file")],
71
+ }),
72
+ ).rejects.toThrow("上传用途重复注册:platform.file");
73
+
74
+ await expect(
75
+ initializeModuleHosts({
76
+ modules: [manifest("known")],
77
+ initializers: [
78
+ initializer("known", (host) => {
79
+ const definition = {
80
+ key: "known.refresh",
81
+ payload: { parse: () => ({}) },
82
+ execute: () => undefined,
83
+ };
84
+ host.registerDurableHandler(definition);
85
+ host.registerDurableHandler(definition);
86
+ }),
87
+ ],
88
+ }),
89
+ ).rejects.toThrow("任务 handler 重复注册:known.refresh");
90
+ });
91
+
92
+ test("安装单元的 Manifest 必须来自同一平台能力目录", async () => {
93
+ await expect(
94
+ initializeModuleHosts({
95
+ modules: [manifest("known")],
96
+ installations: [
97
+ {
98
+ manifest: manifest("known"),
99
+ setup: () => undefined,
100
+ },
101
+ ],
102
+ }),
103
+ ).rejects.toThrow("Server Module Manifest 与能力目录不是同一声明:known");
104
+ });
105
+
106
+ test("AI Operation Handler 只注册本模块 Manifest 声明", async () => {
107
+ const operation = {
108
+ key: "known.summarize",
109
+ kind: "agent" as const,
110
+ featureKey: "known.ai",
111
+ permissionKey: "known.ai.execute",
112
+ auditAction: "known.ai.execute",
113
+ inputSchemaKey: "known.input.v1",
114
+ outputSchemaKey: "known.output.v1",
115
+ requiredCapabilities: ["text-generation"],
116
+ allowedToolKeys: [],
117
+ dataClassification: "internal" as const,
118
+ execution: "streaming" as const,
119
+ publicNetworkAccess: "forbidden" as const,
120
+ limits: {
121
+ maxDurationSeconds: 60,
122
+ maxInputBytes: 4096,
123
+ maxOutputBytes: 4096,
124
+ },
125
+ };
126
+ const known = manifest("known", { aiOperations: [operation] });
127
+ const initialized = await initializeModuleHosts({
128
+ modules: [known],
129
+ installations: [
130
+ {
131
+ manifest: known,
132
+ setup(host) {
133
+ host.registerAiOperationHandlers([
134
+ {
135
+ operationKey: operation.key,
136
+ prepare: () => ({
137
+ instructions: "按要求回答。",
138
+ userPrompt: "你好",
139
+ }),
140
+ },
141
+ ]);
142
+ },
143
+ },
144
+ ],
145
+ });
146
+ expect(
147
+ initialized.aiOperationHandlers.map(
148
+ ({ registration }) => registration.operationKey,
149
+ ),
150
+ ).toEqual(["known.summarize"]);
151
+
152
+ await expect(
153
+ initializeModuleHosts({
154
+ modules: [known],
155
+ installations: [
156
+ {
157
+ manifest: known,
158
+ setup(host) {
159
+ host.registerAiOperationHandlers([
160
+ {
161
+ operationKey: "known.unknown",
162
+ prepare: () => ({
163
+ instructions: "x",
164
+ userPrompt: "x",
165
+ }),
166
+ },
167
+ ]);
168
+ },
169
+ },
170
+ ],
171
+ }),
172
+ ).rejects.toThrow("AI Operation Handler 未在 Manifest 声明");
173
+ });
174
+ });
175
+
176
+ function initializer(
177
+ moduleName: string,
178
+ initialize: ModuleInitializer["initialize"],
179
+ ): ModuleInitializer {
180
+ return { moduleName, initialize };
181
+ }
182
+
183
+ function policy(purpose: string) {
184
+ return {
185
+ purpose,
186
+ maxByteSize: 1024,
187
+ maxPartByteSize: 512,
188
+ sessionTtlMs: 60_000,
189
+ allowedTypes: { "text/plain": ["txt"] },
190
+ signature: "none" as const,
191
+ };
192
+ }