create-windy 0.2.29 → 0.2.31

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 (45) hide show
  1. package/dist/cli.js +385 -9
  2. package/package.json +1 -1
  3. package/template/.windy-template.json +2 -2
  4. package/template/README.md +3 -0
  5. package/template/apps/agent-server/README.md +4 -2
  6. package/template/apps/agent-server/src/southwind_agent_server/config.py +48 -0
  7. package/template/apps/agent-server/src/southwind_agent_server/contracts.py +22 -2
  8. package/template/apps/agent-server/src/southwind_agent_server/engine.py +10 -1
  9. package/template/apps/agent-server/src/southwind_agent_server/openai_engine.py +20 -2
  10. package/template/apps/agent-server/src/southwind_agent_server/service.py +2 -0
  11. package/template/apps/agent-server/tests/test_api.py +28 -0
  12. package/template/apps/agent-server/tests/test_config.py +46 -0
  13. package/template/apps/agent-server/tests/test_openai_provider.py +61 -1
  14. package/template/apps/server/src/agent/remote-runtime.test.ts +14 -0
  15. package/template/apps/server/src/agent/remote-runtime.ts +2 -0
  16. package/template/apps/server/src/agent/run-contracts.ts +6 -0
  17. package/template/apps/server/src/agent/run-facade.ts +6 -0
  18. package/template/apps/server/src/agent/tool-host.test.ts +25 -0
  19. package/template/apps/server/src/agent/tool-host.ts +10 -1
  20. package/template/apps/server/src/work-order/drizzle-repository.ts +1 -2
  21. package/template/apps/web/src/router/shared-scaffold-neutrality.webtest.ts +7 -5
  22. package/template/docker-compose.yml +1 -0
  23. package/template/docs/architecture/ai-egress.md +80 -0
  24. package/template/docs/architecture/ai-runtime.md +13 -1
  25. package/template/docs/architecture/migration-bundle.md +151 -0
  26. package/template/docs/platform/agent-runtime.md +20 -1
  27. package/template/module-schema.lineages.json +8 -0
  28. package/template/packages/config/index.test.ts +20 -0
  29. package/template/packages/config/src/ai-openai-compatible.ts +58 -2
  30. package/template/packages/database/drizzle/0033_rich_george_stacy.sql +2 -0
  31. package/template/packages/database/drizzle/meta/0033_snapshot.json +7239 -0
  32. package/template/packages/database/drizzle/meta/_journal.json +7 -0
  33. package/template/packages/database/src/schema/index.ts +0 -3
  34. package/template/packages/example-work-order/drizzle/0000_bitter_the_santerians.sql +2 -0
  35. package/template/packages/example-work-order/drizzle/meta/0000_snapshot.json +179 -0
  36. package/template/packages/example-work-order/drizzle/meta/_journal.json +13 -0
  37. package/template/packages/example-work-order/drizzle.config.ts +8 -0
  38. package/template/packages/example-work-order/index.ts +1 -0
  39. package/template/packages/example-work-order/package.json +4 -0
  40. package/template/packages/{database/src/schema/work-orders.ts → example-work-order/src/drizzle-schema.ts} +16 -2
  41. package/template/packages/modules/index.ts +1 -0
  42. package/template/packages/modules/src/ai-network-policy.ts +50 -0
  43. package/template/packages/modules/src/ai-operation-composition.test.ts +50 -0
  44. package/template/packages/modules/src/ai-operation-composition.ts +2 -0
  45. package/template/packages/modules/src/manifest.ts +13 -1
@@ -232,6 +232,13 @@
232
232
  "when": 1784922914250,
233
233
  "tag": "0032_marvelous_pepper_potts",
234
234
  "breakpoints": true
235
+ },
236
+ {
237
+ "idx": 33,
238
+ "version": "7",
239
+ "when": 1784925586891,
240
+ "tag": "0033_rich_george_stacy",
241
+ "breakpoints": true
235
242
  }
236
243
  ]
237
244
  }
@@ -25,9 +25,6 @@ export * from "./platform-settings.js";
25
25
  // @windy-module system.scheduler begin
26
26
  export * from "./scheduler.js";
27
27
  // @windy-module system.scheduler end
28
- // @windy-module work-order begin
29
- export * from "./work-orders.js";
30
- // @windy-module work-order end
31
28
  // @windy-module system.workflow begin
32
29
  export * from "./workflow.js";
33
30
  // @windy-module system.workflow end
@@ -0,0 +1,2 @@
1
+ -- Windy module lineage baseline.
2
+ -- Metadata-only migration: intentionally no executable DDL.
@@ -0,0 +1,179 @@
1
+ {
2
+ "id": "6dc0667a-7382-40b9-8036-681641b826df",
3
+ "prevId": "00000000-0000-0000-0000-000000000000",
4
+ "version": "7",
5
+ "dialect": "postgresql",
6
+ "tables": {
7
+ "public.work_orders": {
8
+ "name": "work_orders",
9
+ "schema": "",
10
+ "columns": {
11
+ "id": {
12
+ "name": "id",
13
+ "type": "varchar(64)",
14
+ "primaryKey": true,
15
+ "notNull": true
16
+ },
17
+ "title": {
18
+ "name": "title",
19
+ "type": "varchar(120)",
20
+ "primaryKey": false,
21
+ "notNull": true
22
+ },
23
+ "description": {
24
+ "name": "description",
25
+ "type": "text",
26
+ "primaryKey": false,
27
+ "notNull": true
28
+ },
29
+ "priority": {
30
+ "name": "priority",
31
+ "type": "varchar(24)",
32
+ "primaryKey": false,
33
+ "notNull": true
34
+ },
35
+ "workflow_status": {
36
+ "name": "workflow_status",
37
+ "type": "varchar(32)",
38
+ "primaryKey": false,
39
+ "notNull": true
40
+ },
41
+ "owner_id": {
42
+ "name": "owner_id",
43
+ "type": "varchar(64)",
44
+ "primaryKey": false,
45
+ "notNull": true
46
+ },
47
+ "department_id": {
48
+ "name": "department_id",
49
+ "type": "varchar(64)",
50
+ "primaryKey": false,
51
+ "notNull": false
52
+ },
53
+ "status": {
54
+ "name": "status",
55
+ "type": "varchar(32)",
56
+ "primaryKey": false,
57
+ "notNull": true
58
+ },
59
+ "created_at": {
60
+ "name": "created_at",
61
+ "type": "timestamp with time zone",
62
+ "primaryKey": false,
63
+ "notNull": true,
64
+ "default": "now()"
65
+ },
66
+ "created_by": {
67
+ "name": "created_by",
68
+ "type": "varchar(64)",
69
+ "primaryKey": false,
70
+ "notNull": true
71
+ },
72
+ "updated_at": {
73
+ "name": "updated_at",
74
+ "type": "timestamp with time zone",
75
+ "primaryKey": false,
76
+ "notNull": true,
77
+ "default": "now()"
78
+ },
79
+ "updated_by": {
80
+ "name": "updated_by",
81
+ "type": "varchar(64)",
82
+ "primaryKey": false,
83
+ "notNull": true
84
+ },
85
+ "deleted_at": {
86
+ "name": "deleted_at",
87
+ "type": "timestamp with time zone",
88
+ "primaryKey": false,
89
+ "notNull": false
90
+ },
91
+ "deleted_by": {
92
+ "name": "deleted_by",
93
+ "type": "varchar(64)",
94
+ "primaryKey": false,
95
+ "notNull": false
96
+ }
97
+ },
98
+ "indexes": {
99
+ "idx_work_orders_owner": {
100
+ "name": "idx_work_orders_owner",
101
+ "columns": [
102
+ {
103
+ "expression": "owner_id",
104
+ "isExpression": false,
105
+ "asc": true,
106
+ "nulls": "last"
107
+ }
108
+ ],
109
+ "isUnique": false,
110
+ "concurrently": false,
111
+ "method": "btree",
112
+ "with": {}
113
+ },
114
+ "idx_work_orders_department": {
115
+ "name": "idx_work_orders_department",
116
+ "columns": [
117
+ {
118
+ "expression": "department_id",
119
+ "isExpression": false,
120
+ "asc": true,
121
+ "nulls": "last"
122
+ }
123
+ ],
124
+ "isUnique": false,
125
+ "concurrently": false,
126
+ "method": "btree",
127
+ "with": {}
128
+ },
129
+ "idx_work_orders_status": {
130
+ "name": "idx_work_orders_status",
131
+ "columns": [
132
+ {
133
+ "expression": "status",
134
+ "isExpression": false,
135
+ "asc": true,
136
+ "nulls": "last"
137
+ }
138
+ ],
139
+ "isUnique": false,
140
+ "concurrently": false,
141
+ "method": "btree",
142
+ "with": {}
143
+ },
144
+ "idx_work_orders_workflow": {
145
+ "name": "idx_work_orders_workflow",
146
+ "columns": [
147
+ {
148
+ "expression": "workflow_status",
149
+ "isExpression": false,
150
+ "asc": true,
151
+ "nulls": "last"
152
+ }
153
+ ],
154
+ "isUnique": false,
155
+ "concurrently": false,
156
+ "method": "btree",
157
+ "with": {}
158
+ }
159
+ },
160
+ "foreignKeys": {},
161
+ "compositePrimaryKeys": {},
162
+ "uniqueConstraints": {},
163
+ "policies": {},
164
+ "checkConstraints": {},
165
+ "isRLSEnabled": false
166
+ }
167
+ },
168
+ "enums": {},
169
+ "schemas": {},
170
+ "sequences": {},
171
+ "roles": {},
172
+ "policies": {},
173
+ "views": {},
174
+ "_meta": {
175
+ "columns": {},
176
+ "schemas": {},
177
+ "tables": {}
178
+ }
179
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "version": "7",
3
+ "dialect": "postgresql",
4
+ "entries": [
5
+ {
6
+ "idx": 0,
7
+ "version": "7",
8
+ "when": 1784925622159,
9
+ "tag": "0000_bitter_the_santerians",
10
+ "breakpoints": true
11
+ }
12
+ ]
13
+ }
@@ -0,0 +1,8 @@
1
+ import { defineConfig } from "drizzle-kit";
2
+
3
+ export default defineConfig({
4
+ dialect: "postgresql",
5
+ schema: "./src/drizzle-schema.ts",
6
+ out: "./drizzle",
7
+ casing: "snake_case",
8
+ });
@@ -1,4 +1,5 @@
1
1
  export * from "./src/contract.js";
2
+ export * from "./src/drizzle-schema.js";
2
3
  export * from "./src/manifest.js";
3
4
  export * from "./src/migration-bundle.js";
4
5
  export * from "./src/schema.js";
@@ -3,10 +3,14 @@
3
3
  "version": "0.1.0",
4
4
  "private": true,
5
5
  "type": "module",
6
+ "scripts": {
7
+ "db:generate": "drizzle-kit generate --config drizzle.config.ts"
8
+ },
6
9
  "dependencies": {
7
10
  "@southwind-ai/modules": "workspace:*",
8
11
  "@southwind-ai/server-sdk": "workspace:*",
9
12
  "@southwind-ai/shared": "workspace:*",
13
+ "drizzle-orm": "^0.45.2",
10
14
  "zod": "^4.4.3"
11
15
  },
12
16
  "exports": {
@@ -1,5 +1,19 @@
1
- import { index, pgTable, text, varchar } from "drizzle-orm/pg-core";
2
- import { auditColumns, idColumn, statusColumn } from "./common.js";
1
+ import { index, pgTable, text, timestamp, varchar } from "drizzle-orm/pg-core";
2
+
3
+ const idColumn = () => varchar("id", { length: 64 });
4
+ const statusColumn = () => varchar("status", { length: 32 });
5
+ const auditColumns = {
6
+ createdAt: timestamp("created_at", { withTimezone: true })
7
+ .notNull()
8
+ .defaultNow(),
9
+ createdBy: varchar("created_by", { length: 64 }).notNull(),
10
+ updatedAt: timestamp("updated_at", { withTimezone: true })
11
+ .notNull()
12
+ .defaultNow(),
13
+ updatedBy: varchar("updated_by", { length: 64 }).notNull(),
14
+ deletedAt: timestamp("deleted_at", { withTimezone: true }),
15
+ deletedBy: varchar("deleted_by", { length: 64 }),
16
+ };
3
17
 
4
18
  export const workOrders = pgTable(
5
19
  "work_orders",
@@ -7,3 +7,4 @@ export * from "./src/system-modules.js";
7
7
  export * from "./src/system-module-catalog.js";
8
8
  export * from "./src/system-api-permissions.js";
9
9
  export * from "./src/tool-composition.js";
10
+ export * from "./src/ai-network-policy.js";
@@ -0,0 +1,50 @@
1
+ import type {
2
+ AiProviderEgressPolicy,
3
+ AiToolNetworkPolicy,
4
+ ModuleAiOperationDefinition,
5
+ } from "./manifest.js";
6
+
7
+ const providerForbidden: AiProviderEgressPolicy = Object.freeze({
8
+ mode: "forbidden",
9
+ });
10
+ const toolNetworkForbidden: AiToolNetworkPolicy = Object.freeze({
11
+ mode: "forbidden",
12
+ });
13
+
14
+ export function resolveProviderEgress(
15
+ operation: ModuleAiOperationDefinition,
16
+ ): AiProviderEgressPolicy {
17
+ return operation.providerEgress ?? providerForbidden;
18
+ }
19
+
20
+ export function resolveToolNetworkPolicy(
21
+ operation: ModuleAiOperationDefinition,
22
+ ): AiToolNetworkPolicy {
23
+ return operation.toolNetworkPolicy ?? toolNetworkForbidden;
24
+ }
25
+
26
+ export function validateAiNetworkPolicy(
27
+ operation: ModuleAiOperationDefinition,
28
+ ): void {
29
+ const provider = resolveProviderEgress(operation);
30
+ if (provider.mode === "configured-endpoint") {
31
+ if (!provider.deploymentKey.trim()) {
32
+ throw new Error(`${operation.key} Provider Deployment Key 不能为空`);
33
+ }
34
+ } else if (provider.mode !== "forbidden") {
35
+ throw new Error(`${operation.key} Provider 出网策略无效`);
36
+ }
37
+
38
+ const tool = resolveToolNetworkPolicy(operation);
39
+ if (tool.mode !== "forbidden") {
40
+ throw new Error(`${operation.key} Tool 网络策略无效`);
41
+ }
42
+ if (
43
+ operation.publicNetworkAccess === "allowed" &&
44
+ operation.providerEgress === undefined
45
+ ) {
46
+ throw new Error(
47
+ `${operation.key} 旧 publicNetworkAccess=allowed 不能安全映射,请声明 providerEgress`,
48
+ );
49
+ }
50
+ }
@@ -98,6 +98,56 @@ describe("AI Operation Composition", () => {
98
98
  expect(() => validateModuleComposition([module])).toThrow("Tool 不能为空");
99
99
  });
100
100
 
101
+ test("Provider 与 Tool 网络边界独立且旧声明默认 fail closed", () => {
102
+ const module = fixture("business");
103
+ const legacy = operation("business.legacy");
104
+ module.aiOperations = [legacy];
105
+ expect(() => validateModuleComposition([module])).not.toThrow();
106
+
107
+ module.aiOperations = [
108
+ {
109
+ ...operation("business.generate"),
110
+ publicNetworkAccess: undefined,
111
+ providerEgress: {
112
+ mode: "configured-endpoint",
113
+ deploymentKey: "ai.openai-compatible",
114
+ },
115
+ toolNetworkPolicy: { mode: "forbidden" },
116
+ },
117
+ ];
118
+ expect(() => validateModuleComposition([module])).not.toThrow();
119
+
120
+ module.aiOperations[0] = {
121
+ ...operation("business.generate"),
122
+ publicNetworkAccess: "allowed",
123
+ };
124
+ expect(() => validateModuleComposition([module])).toThrow("不能安全映射");
125
+ });
126
+
127
+ test("拒绝空 Provider Deployment 与未知 Tool 网络模式", () => {
128
+ const module = fixture("business");
129
+ module.aiOperations = [
130
+ {
131
+ ...operation("business.generate"),
132
+ providerEgress: {
133
+ mode: "configured-endpoint",
134
+ deploymentKey: " ",
135
+ },
136
+ },
137
+ ];
138
+ expect(() => validateModuleComposition([module])).toThrow(
139
+ "Deployment Key 不能为空",
140
+ );
141
+
142
+ module.aiOperations[0] = {
143
+ ...operation("business.generate"),
144
+ toolNetworkPolicy: { mode: "allow" },
145
+ } as unknown as ModuleAiOperationDefinition;
146
+ expect(() => validateModuleComposition([module])).toThrow(
147
+ "Tool 网络策略无效",
148
+ );
149
+ });
150
+
101
151
  test("Operation Handler 必须与 Manifest 声明一一对应", () => {
102
152
  const module = fixture("business");
103
153
  module.aiOperations = [operation("business.generate")];
@@ -2,6 +2,7 @@ import type {
2
2
  ModuleAiOperationDefinition,
3
3
  ModuleManifest,
4
4
  } from "./manifest.js";
5
+ import { validateAiNetworkPolicy } from "./ai-network-policy.js";
5
6
 
6
7
  interface AiOperationCatalogs {
7
8
  permissions: ReadonlySet<string>;
@@ -100,6 +101,7 @@ function validateOperation(
100
101
  );
101
102
  validateCapabilities(operation);
102
103
  validateTools(operation, catalogs.tools);
104
+ validateAiNetworkPolicy(operation);
103
105
  validateLimits(operation);
104
106
  }
105
107
 
@@ -88,6 +88,15 @@ export type AiOperationExecution =
88
88
  | "background"
89
89
  | "live";
90
90
  export type AiOperationPublicNetworkAccess = "forbidden" | "allowed";
91
+ export type AiProviderEgressPolicy =
92
+ | { mode: "forbidden" }
93
+ | {
94
+ mode: "configured-endpoint";
95
+ deploymentKey: string;
96
+ };
97
+ export interface AiToolNetworkPolicy {
98
+ mode: "forbidden";
99
+ }
91
100
 
92
101
  export interface AiOperationLimits {
93
102
  maxDurationSeconds: number;
@@ -107,7 +116,10 @@ export interface ModuleAiOperationDefinition {
107
116
  allowedToolKeys: readonly string[];
108
117
  dataClassification: AiOperationDataClassification;
109
118
  execution: AiOperationExecution;
110
- publicNetworkAccess: AiOperationPublicNetworkAccess;
119
+ /** @deprecated 使用 providerEgress 与 toolNetworkPolicy 拆分两类网络边界。 */
120
+ publicNetworkAccess?: AiOperationPublicNetworkAccess;
121
+ providerEgress?: AiProviderEgressPolicy;
122
+ toolNetworkPolicy?: AiToolNetworkPolicy;
111
123
  limits: AiOperationLimits;
112
124
  }
113
125