create-windy 0.9.1 → 0.10.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.
Files changed (253) hide show
  1. package/dist/cli.js +1691 -1151
  2. package/package.json +2 -2
  3. package/template/.agents/skills/windy-business-module/SKILL.md +33 -1
  4. package/template/.windy-template.json +6 -2
  5. package/template/AGENTS.md +4 -0
  6. package/template/README.md +2 -0
  7. package/template/apps/agent-server/Dockerfile +1 -1
  8. package/template/apps/agent-server/README.md +1 -1
  9. package/template/apps/agent-server/native/EXTENSIONS.md +2 -2
  10. package/template/apps/agent-server/native/parallel-harness.ts +2 -0
  11. package/template/apps/agent-server/native/storage-lifecycle.test.ts +2 -1
  12. package/template/apps/agent-server/package.json +42 -41
  13. package/template/apps/agent-server/plugins/downloads/README.md +1 -1
  14. package/template/apps/agent-server/plugins/drop-overlay/README.md +1 -1
  15. package/template/apps/agent-server/plugins/settings/README.md +1 -1
  16. package/template/apps/agent-server/plugins/skill-library/client-chunks.test.ts +4 -1
  17. package/template/apps/agent-server/plugins/theme/README.md +5 -1
  18. package/template/apps/agent-server/plugins/theme/source-tokens.ts +0 -1
  19. package/template/apps/agent-server/plugins/theme/tokens.json +0 -4
  20. package/template/apps/agent-server/plugins/tool-catalog/README.md +6 -3
  21. package/template/apps/agent-server/plugins/tool-catalog/discovery.mjs +13 -10
  22. package/template/apps/agent-server/plugins/tool-catalog/discovery.test.ts +68 -6
  23. package/template/apps/agent-server/runtime/compatibility.ts +7 -1
  24. package/template/apps/agent-server/runtime/dsh-engine.ts +2 -0
  25. package/template/apps/agent-server/runtime/questions-workflow.nodecheck.ts +2 -0
  26. package/template/apps/server/Dockerfile +2 -1
  27. package/template/apps/server/package.json +11 -11
  28. package/template/apps/server/scripts/validate-module-composition-once.ts +2 -0
  29. package/template/apps/server/scripts/validate-module-composition.ts +8 -4
  30. package/template/apps/server/src/admin/routes.test.ts +29 -1
  31. package/template/apps/server/src/admin/routes.ts +20 -2
  32. package/template/apps/server/src/agent/client-session/completion.ts +1 -1
  33. package/template/apps/server/src/agent/client-session/manifest-admission.ts +1 -1
  34. package/template/apps/server/src/agent/client-session/request.ts +1 -1
  35. package/template/apps/server/src/agent/context-version-routes.ts +37 -13
  36. package/template/apps/server/src/agent/run-facade.ts +1 -1
  37. package/template/apps/server/src/agent/run-input-verification.ts +1 -1
  38. package/template/apps/server/src/agent/run-routes.ts +1 -14
  39. package/template/apps/server/src/agent/run-start-preparation.ts +1 -1
  40. package/template/apps/server/src/agent/runtime-bootstrap-input.ts +2 -0
  41. package/template/apps/server/src/agent/runtime-bootstrap.ts +49 -157
  42. package/template/apps/server/src/agent/runtime-disabled-composition.ts +2 -1
  43. package/template/apps/server/src/agent/session-invalidation/authorization-revocation.ts +54 -0
  44. package/template/apps/server/src/agent/session-runtime-composition.ts +1 -51
  45. package/template/apps/server/src/agent/unavailable-routes.ts +13 -2
  46. package/template/apps/server/src/agent-workflow/runtime-composition.ts +3 -0
  47. package/template/apps/server/src/agent-workflow/service.ts +17 -1
  48. package/template/apps/server/src/ai-artifact/media-resource-route.test.ts +134 -0
  49. package/template/apps/server/src/ai-artifact/routes.ts +1 -1
  50. package/template/apps/server/src/ai-artifact/runtime.ts +12 -0
  51. package/template/apps/server/src/ai-runtime-readiness/operation-port.test-support.ts +1 -1
  52. package/template/apps/server/src/ai-runtime-readiness/operation-port.ts +8 -0
  53. package/template/apps/server/src/ai-runtime-security/internal-token.ts +14 -0
  54. package/template/apps/server/src/app/antivirus-composition.test.ts +63 -0
  55. package/template/apps/server/src/app/antivirus-composition.ts +29 -0
  56. package/template/apps/server/src/app/dsh-workbench-socket-audit.test.ts +2 -1
  57. package/template/apps/server/src/configuration/definition.ts +15 -11
  58. package/template/apps/server/src/document/facade-support.ts +2 -2
  59. package/template/apps/server/src/document/facade.ts +2 -2
  60. package/template/apps/server/src/document/output-artifact-composition.ts +9 -5
  61. package/template/apps/server/src/document/output-artifact.test-support.ts +1 -1
  62. package/template/apps/server/src/embedding/facade.ts +1 -1
  63. package/template/apps/server/src/embedding/input.ts +1 -1
  64. package/template/apps/server/src/embedding/routes.ts +1 -1
  65. package/template/apps/server/src/index.ts +60 -60
  66. package/template/apps/server/src/license/routes.ts +4 -1
  67. package/template/apps/server/src/license/runtime-business-access.test.ts +261 -0
  68. package/template/apps/server/src/media-runtime/composition.ts +157 -0
  69. package/template/apps/server/src/media-runtime/media-only-composition.ts +44 -0
  70. package/template/apps/server/src/media-runtime/module-ai-port-factory.test.ts +69 -0
  71. package/template/apps/server/src/media-runtime/module-ai-port-factory.ts +72 -0
  72. package/template/apps/server/src/{agent → media-runtime}/runtime-readiness-sources.ts +14 -6
  73. package/template/apps/server/src/media-runtime/shared-composition.ts +104 -0
  74. package/template/apps/server/src/module-composition.test.ts +22 -0
  75. package/template/apps/server/src/module-composition.ts +48 -0
  76. package/template/apps/server/src/module-host/ai-operation-port.ts +97 -30
  77. package/template/apps/server/src/module-host/ai-operation-speech-normalize.test.ts +1 -1
  78. package/template/apps/server/src/module-host/http-runtime.ts +9 -3
  79. package/template/apps/server/src/module-host/static-route-authorization.test.ts +6 -3
  80. package/template/apps/server/src/module-host/storage-authorization-binding.test.ts +4 -0
  81. package/template/apps/server/src/module-runtime-validation.ts +2 -0
  82. package/template/apps/server/src/observability/diagnostic-index.ts +26 -1
  83. package/template/apps/server/src/observability/diagnostic-repository.ts +40 -11
  84. package/template/apps/server/src/platform-http-runtime.ts +1 -1
  85. package/template/apps/server/src/runtime-feature.ts +5 -1
  86. package/template/apps/server/src/runtime.ts +3 -0
  87. package/template/apps/server/src/settings/drizzle-watermark-repository.ts +35 -4
  88. package/template/apps/server/src/settings/watermark-repository.ts +14 -0
  89. package/template/apps/server/src/settings/watermark-routes.test.ts +84 -2
  90. package/template/apps/server/src/settings/watermark-routes.ts +109 -0
  91. package/template/apps/server/src/settings/watermark-transfer.ts +79 -0
  92. package/template/apps/server/src/speech/facade-validation.ts +1 -1
  93. package/template/apps/server/src/speech/facade.ts +2 -2
  94. package/template/apps/server/src/startup-composition-validation.test.ts +32 -0
  95. package/template/apps/server/src/startup-finalization.ts +2 -0
  96. package/template/apps/server/src/startup-runtime.ts +77 -0
  97. package/template/apps/server/src/system/ai-runtime-build.ts +3 -0
  98. package/template/apps/server/src/system/ai-runtime-configuration.test.ts +36 -0
  99. package/template/apps/server/src/system/ai-runtime-configuration.ts +58 -0
  100. package/template/apps/server/src/system/diagnostic-routes.test.ts +45 -0
  101. package/template/apps/server/src/system/diagnostic-routes.ts +35 -0
  102. package/template/apps/server/src/system/environment-policy-reader.test.ts +74 -0
  103. package/template/apps/server/src/system/environment-policy-reader.ts +62 -0
  104. package/template/apps/server/src/system/feature-environment-assessment.test.ts +148 -0
  105. package/template/apps/server/src/system/feature-environment-assessment.ts +179 -0
  106. package/template/apps/server/src/system/host-diagnostics.test.ts +155 -0
  107. package/template/apps/server/src/system/host-diagnostics.ts +225 -0
  108. package/template/apps/server/src/system/menu-registry.test.ts +26 -0
  109. package/template/apps/server/src/system/menu-registry.ts +9 -7
  110. package/template/apps/server/src/system/operations.test.ts +5 -0
  111. package/template/apps/server/src/system/operations.ts +36 -12
  112. package/template/apps/server/src/system/role-authorization-postgres.integration.test.ts +181 -0
  113. package/template/apps/web/Dockerfile +7 -3
  114. package/template/apps/web/dev/dev-port.test.ts +6 -6
  115. package/template/apps/web/dev/dev-port.ts +1 -1
  116. package/template/apps/web/dev/start.ts +6 -1
  117. package/template/apps/web/index.html +1 -0
  118. package/template/apps/web/package.json +16 -16
  119. package/template/apps/web/runtime-server.test.ts +22 -1
  120. package/template/apps/web/runtime-server.ts +6 -0
  121. package/template/apps/web/src/App.vue +6 -3
  122. package/template/apps/web/src/assets/windy.svg +4 -0
  123. package/template/apps/web/src/components/auth/LoginForm.vue +2 -2
  124. package/template/apps/web/src/components/auth/PasswordChangeForm.vue +14 -3
  125. package/template/apps/web/src/components/common/FormDialog.vue +1 -1
  126. package/template/apps/web/src/components/license/LicenseFlow.webtest.ts +3 -0
  127. package/template/apps/web/src/components/license/LicenseMachineCodeQr.vue +99 -0
  128. package/template/apps/web/src/components/license/LicenseMachineCodeQr.webtest.ts +83 -0
  129. package/template/apps/web/src/components/license/LicensePairingCard.vue +1 -1
  130. package/template/apps/web/src/components/license/LicenseStatusCard.vue +14 -2
  131. package/template/apps/web/src/composables/useLoginFlow.ts +2 -2
  132. package/template/apps/web/src/composables/usePlatformSettings.ts +1 -1
  133. package/template/apps/web/src/composables/useWatermarkSettings.ts +1 -0
  134. package/template/apps/web/src/layout/AppSidebar.vue +5 -9
  135. package/template/apps/web/src/layout/AppSidebar.webtest.ts +4 -1
  136. package/template/apps/web/src/pages/auth/ChangePasswordPage.vue +67 -6
  137. package/template/apps/web/src/pages/auth/LoginPage.vue +3 -12
  138. package/template/apps/web/src/pages/configuration/components/ModuleConfigurationEditor.vue +30 -0
  139. package/template/apps/web/src/pages/configuration/components/ModuleConfigurationWorkbench.vue +25 -14
  140. package/template/apps/web/src/pages/configuration/components/TimeZoneSelect.vue +55 -0
  141. package/template/apps/web/src/pages/profile/PersonalSettingsPage.vue +64 -11
  142. package/template/apps/web/src/pages/profile/PersonalSettingsPage.webtest.ts +21 -1
  143. package/template/apps/web/src/pages/settings/BrandingSettingsSection.vue +7 -18
  144. package/template/apps/web/src/pages/settings/BrandingSettingsSection.webtest.ts +9 -4
  145. package/template/apps/web/src/pages/settings/GlobalSettingsPage.vue +51 -6
  146. package/template/apps/web/src/pages/settings/GlobalSettingsPage.webtest.ts +19 -3
  147. package/template/apps/web/src/pages/settings/WatermarkSettingsSection.vue +1 -0
  148. package/template/apps/web/src/pages/settings/WatermarkYamlTransfer.vue +211 -0
  149. package/template/apps/web/src/pages/system/DiagnosticLookupPage.vue +18 -3
  150. package/template/apps/web/src/pages/system/DiagnosticLookupPage.webtest.ts +4 -1
  151. package/template/apps/web/src/pages/system/SystemOperationsPage.vue +23 -0
  152. package/template/apps/web/src/pages/system/components/DiagnosticLogList.vue +120 -0
  153. package/template/apps/web/src/pages/system/components/OperationEnvironmentPanel.vue +237 -0
  154. package/template/apps/web/src/pages/system/components/OperationFeatureAssessmentPanel.vue +131 -0
  155. package/template/apps/web/src/pages/system/components/PagedSelectionList.vue +31 -24
  156. package/template/apps/web/src/pages/system/components/SystemResourceActions.vue +2 -1
  157. package/template/apps/web/src/pages/system/components/SystemResourceEditor.vue +11 -2
  158. package/template/apps/web/src/pages/system/components/SystemResourcePage.vue +1 -0
  159. package/template/apps/web/src/pages/system/components/SystemResourcePage.webtest.ts +6 -2
  160. package/template/apps/web/src/pages/system/components/agent-skills/AgentSkillImportDialog.vue +18 -9
  161. package/template/apps/web/src/pages/system/components/agent-workbench/SessionReplayTimeline.vue +18 -9
  162. package/template/apps/web/src/pages/system/components/agent-workbench/TestRunComposer.vue +66 -34
  163. package/template/apps/web/src/pages/system/components/agent-workbench/ToolCatalogPanel.vue +48 -33
  164. package/template/apps/web/src/pages/system/components/agent-workbench/ToolCatalogPanel.webtest.ts +9 -2
  165. package/template/apps/web/src/pages/system/components/agent-workbench/ToolRuleDetail.vue +23 -16
  166. package/template/apps/web/src/pages/system/components/dictionary-bulk/DictionaryBulkJobDetail.vue +10 -7
  167. package/template/apps/web/src/pages/system/components/user-account/RolePermissionSummary.vue +81 -57
  168. package/template/apps/web/src/pages/system/components/user-account/UserRoleSelector.vue +6 -4
  169. package/template/apps/web/src/pages/system/components/user-account/UserRoleSelector.webtest.ts +20 -4
  170. package/template/apps/web/src/pages/system/resource-config.ts +9 -0
  171. package/template/apps/web/src/pages/work-orders/WorkOrderListPage.vue +1 -1
  172. package/template/apps/web/src/router/disabled-feature-loading.webtest.ts +78 -0
  173. package/template/apps/web/src/router/index.ts +3 -6
  174. package/template/apps/web/src/router/manifest-routes.ts +2 -3
  175. package/template/apps/web/src/router/platform-admin-pages.ts +21 -62
  176. package/template/apps/web/src/router/platform-admin-route-bindings.ts +4 -4
  177. package/template/apps/web/src/services/diagnostics-api.ts +13 -1
  178. package/template/apps/web/src/services/operations-api.ts +4 -0
  179. package/template/apps/web/src/services/platform-settings-api.ts +35 -1
  180. package/template/apps/web/vite.config.ts +9 -3
  181. package/template/deploy/environment/windy.environment.example.yaml +53 -0
  182. package/template/docker-compose.yml +4 -2
  183. package/template/docs/architecture/agent-admin-workbench-implementation.md +1 -1
  184. package/template/docs/architecture/agent-admin-workbench-plan.md +1 -1
  185. package/template/docs/architecture/agent-workflow.md +8 -0
  186. package/template/docs/architecture/durable-jobs.md +17 -0
  187. package/template/docs/architecture/embedded-dsh-integration.md +1 -1
  188. package/template/docs/development/dsh-plugins.md +1 -1
  189. package/template/docs/development/performance-engineering.md +24 -0
  190. package/template/docs/operations/admin-settings-yaml.md +29 -0
  191. package/template/docs/operations/dsh-agent-migration.md +2 -2
  192. package/template/docs/operations/environment-capabilities.md +185 -0
  193. package/template/docs/platform/module-configuration.md +18 -3
  194. package/template/docs/platform/platform-authentication.md +1 -1
  195. package/template/package.json +10 -8
  196. package/template/packages/config/package.json +2 -2
  197. package/template/packages/config/src/definition.ts +14 -4
  198. package/template/packages/config/src/environment-loader.ts +27 -3
  199. package/template/packages/config/src/platform-ai-runtime.test.ts +94 -0
  200. package/template/packages/config/src/platform.ts +5 -2
  201. package/template/packages/database/drizzle/0065_durable_job_lease_snapshot_lookup.sql +1 -0
  202. package/template/packages/database/drizzle/0066_agent_source_sequence_bigint.sql +3 -0
  203. package/template/packages/database/drizzle/0067_media_runtime_access.sql +46 -0
  204. package/template/packages/database/drizzle/meta/0065_snapshot.json +15370 -0
  205. package/template/packages/database/drizzle/meta/0066_snapshot.json +15370 -0
  206. package/template/packages/database/drizzle/meta/0067_snapshot.json +15370 -0
  207. package/template/packages/database/drizzle/meta/_journal.json +21 -0
  208. package/template/packages/database/package.json +3 -3
  209. package/template/packages/database/src/schema/agent-session.ts +4 -3
  210. package/template/packages/database/src/schema/durable-jobs.ts +4 -0
  211. package/template/packages/database/src/schema/index.ts +3 -1
  212. package/template/packages/database/src/schema-durable-recovery.test.ts +21 -1
  213. package/template/packages/database/src/schema-source-sequence.test.ts +24 -0
  214. package/template/packages/example-work-order/package.json +2 -2
  215. package/template/packages/modules/package.json +1 -1
  216. package/template/packages/modules/src/admin-ui-boundary.test.ts +33 -0
  217. package/template/packages/modules/src/composition.ts +11 -28
  218. package/template/packages/modules/src/manifest.ts +2 -0
  219. package/template/packages/modules/src/menu-composition.ts +24 -0
  220. package/template/packages/modules/src/system-admin-routes.ts +2 -2
  221. package/template/packages/modules/src/system-admin-ui-catalog.ts +6 -1
  222. package/template/packages/modules/src/system-ai-api-bindings.ts +0 -16
  223. package/template/packages/modules/src/system-api-control-plane.ts +9 -1
  224. package/template/packages/modules/src/system-api-permissions.ts +7 -0
  225. package/template/packages/modules/src/system-api-watermark.ts +21 -0
  226. package/template/packages/modules/src/system-audit-actions.ts +3 -1
  227. package/template/packages/modules/src/system-features.ts +21 -3
  228. package/template/packages/modules/src/system-media-api-bindings.ts +97 -0
  229. package/template/packages/modules/src/system-module-audit-selection.ts +2 -0
  230. package/template/packages/modules/src/system-module-catalog.test.ts +13 -6
  231. package/template/packages/modules/src/system-module-catalog.ts +6 -241
  232. package/template/packages/modules/src/system-module-definitions.ts +260 -0
  233. package/template/packages/modules/src/system-modules-access.test.ts +2 -0
  234. package/template/packages/modules/src/system-modules.test.ts +7 -1
  235. package/template/packages/modules/src/system-modules.ts +25 -13
  236. package/template/packages/modules/src/system-permissions.ts +12 -0
  237. package/template/packages/server-sdk/package.json +1 -1
  238. package/template/packages/shared/index.ts +3 -0
  239. package/template/packages/shared/package.json +1 -1
  240. package/template/packages/shared/src/environment-assessment.ts +68 -0
  241. package/template/packages/shared/src/environment-policy.ts +205 -0
  242. package/template/packages/shared/src/license-console-capabilities.ts +7 -0
  243. package/template/packages/shared/src/license.ts +1 -0
  244. package/template/packages/shared/src/module-configuration.ts +2 -1
  245. package/template/scripts/operations/collect-device-info.py +230 -0
  246. package/template/apps/server/src/agent/module-ai-port-factory.ts +0 -41
  247. /package/template/apps/server/src/{agent → ai-runtime-security}/execution-grants.ts +0 -0
  248. /package/template/apps/server/src/{agent → ai-runtime-security}/idempotency-key.test.ts +0 -0
  249. /package/template/apps/server/src/{agent → ai-runtime-security}/idempotency-key.ts +0 -0
  250. /package/template/apps/server/src/{agent → ai-runtime-security}/provider-input-governance.test.ts +0 -0
  251. /package/template/apps/server/src/{agent → ai-runtime-security}/provider-input-governance.ts +0 -0
  252. /package/template/apps/server/src/{agent → media-runtime}/required-environment.ts +0 -0
  253. /package/template/apps/server/src/{agent → media-runtime}/runtime-readiness-sources.test.ts +0 -0
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@southwind-ai/server-sdk",
3
- "version": "0.5.2",
3
+ "version": "0.5.3",
4
4
  "license": "UNLICENSED",
5
5
  "type": "module",
6
6
  "engines": {
@@ -47,3 +47,6 @@ export * from "./src/diagnostics.js";
47
47
  // @windy-module system.agent begin
48
48
  export * from "./src/agent-workflow-presets.js";
49
49
  // @windy-module system.agent end
50
+
51
+ export * from "./src/environment-assessment.js";
52
+ export * from "./src/environment-policy.js";
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@southwind-ai/shared",
3
- "version": "0.3.2",
3
+ "version": "0.3.3",
4
4
  "license": "UNLICENSED",
5
5
  "type": "module",
6
6
  "engines": {
@@ -0,0 +1,68 @@
1
+ /** Host evidence is supplied by an explicitly mounted collector report, never inferred from a container. */
2
+ export const HOST_PROBE_KEYS = [
3
+ "cpu",
4
+ "memory",
5
+ "blockDevices",
6
+ "disks",
7
+ "mounts",
8
+ "network",
9
+ "systemd",
10
+ "docker",
11
+ "compose",
12
+ "containers",
13
+ "gpu",
14
+ ] as const;
15
+ export type HostProbeKey = (typeof HOST_PROBE_KEYS)[number];
16
+ export interface HostDiagnosticProbe {
17
+ status:
18
+ | "ok"
19
+ | "missing"
20
+ | "permission-denied"
21
+ | "timeout"
22
+ | "failed"
23
+ | "unsupported";
24
+ exitCode: number | null;
25
+ output: string;
26
+ truncated: boolean;
27
+ }
28
+ export interface HostDiagnosticsReport {
29
+ schemaVersion: 1;
30
+ scope: "host";
31
+ collector: "windy-host-diagnostics-v1";
32
+ collectedAt: string;
33
+ facts: {
34
+ os: string;
35
+ distribution?: string;
36
+ kernel: string;
37
+ architecture: string;
38
+ cpuLogicalCount: number;
39
+ memoryTotalBytes: number | null;
40
+ /** Present only when a successful GPU query establishes the count. */
41
+ gpuCount?: number;
42
+ };
43
+ probes: Record<HostProbeKey, HostDiagnosticProbe>;
44
+ deployment?: {
45
+ status: "available" | "invalid" | "unreadable";
46
+ releaseId?: string;
47
+ gitSha?: string;
48
+ platform?: string;
49
+ };
50
+ }
51
+ export interface EnvironmentSnapshot {
52
+ generatedAt: string;
53
+ process: {
54
+ scope: "process";
55
+ platform: string;
56
+ architecture: string;
57
+ nodeVersion: string;
58
+ bunVersion?: string;
59
+ containerized: boolean;
60
+ logicalCpuCount: number;
61
+ memoryTotalBytes: number;
62
+ };
63
+ host: {
64
+ status: "available" | "not-configured" | "invalid" | "stale" | "unreadable";
65
+ message: string;
66
+ report?: HostDiagnosticsReport;
67
+ };
68
+ }
@@ -0,0 +1,205 @@
1
+ /** Deployment requirements are recommendations, not authorization or probe results. */
2
+ export interface FeatureEnvironmentPolicy {
3
+ key: string;
4
+ title: string;
5
+ execution: "none" | "cpu" | "local-gpu" | "remote";
6
+ recommendedEnabled?: boolean;
7
+ supportedPlatforms?: string[];
8
+ supportedArchitectures?: string[];
9
+ minMemoryGiB?: number;
10
+ requiredEnvironmentKeys?: string[];
11
+ }
12
+ export const AI_RUNTIME_KINDS = [
13
+ "agent",
14
+ "document",
15
+ "speech",
16
+ "embedding",
17
+ ] as const;
18
+ export type AiRuntimeKind = (typeof AI_RUNTIME_KINDS)[number];
19
+ export type AiRuntimeSwitches = Partial<Record<AiRuntimeKind, boolean>>;
20
+
21
+ export interface AntivirusEnvironmentPolicy {
22
+ packaged: boolean;
23
+ enabled: boolean;
24
+ }
25
+
26
+ export interface EnvironmentPolicy {
27
+ schemaVersion: 1;
28
+ build: { modules: string[]; runtimes?: AiRuntimeSwitches };
29
+ runtime?: AiRuntimeSwitches;
30
+ antivirus?: AntivirusEnvironmentPolicy;
31
+ features: FeatureEnvironmentPolicy[];
32
+ }
33
+ export interface FeatureEnvironmentAssessment {
34
+ key: string;
35
+ title: string;
36
+ status:
37
+ | "hardware"
38
+ | "software"
39
+ | "not-packaged"
40
+ | "configuration"
41
+ | "unknown"
42
+ | "available"
43
+ | "disabled";
44
+ message: string;
45
+ evidence: string[];
46
+ recommendedEnabled?: boolean;
47
+ }
48
+ export interface EnvironmentAssessmentSnapshot {
49
+ status: "available" | "not-configured" | "invalid";
50
+ message: string;
51
+ features: FeatureEnvironmentAssessment[];
52
+ }
53
+
54
+ function object(
55
+ value: unknown,
56
+ required: string[],
57
+ optional: string[] = [],
58
+ ): Record<string, unknown> {
59
+ if (!value || typeof value !== "object" || Array.isArray(value))
60
+ throw new Error("环境预设必须为对象");
61
+ const record = value as Record<string, unknown>;
62
+ if (
63
+ required.some((key) => !Object.hasOwn(record, key)) ||
64
+ Object.keys(record).some((key) => ![...required, ...optional].includes(key))
65
+ )
66
+ throw new Error("环境预设缺少必需字段或包含未知字段");
67
+ return record;
68
+ }
69
+ function strings(value: unknown, pattern: RegExp, max = 128): string[] {
70
+ if (
71
+ !Array.isArray(value) ||
72
+ value.length > max ||
73
+ value.some(
74
+ (item) =>
75
+ typeof item !== "string" || item.length > 128 || !pattern.test(item),
76
+ ) ||
77
+ new Set(value).size !== value.length
78
+ )
79
+ throw new Error("环境预设列表无效或重复");
80
+ return value as string[];
81
+ }
82
+ export function parseEnvironmentPolicy(value: unknown): EnvironmentPolicy {
83
+ const root = object(
84
+ value,
85
+ ["schemaVersion", "build", "features"],
86
+ ["runtime", "antivirus"],
87
+ );
88
+ if (root.schemaVersion !== 1) throw new Error("不支持的环境预设版本");
89
+ const build = object(root.build, ["modules"], ["runtimes"]);
90
+ const modules = strings(build.modules, /^[a-z][a-z0-9.-]*$/);
91
+ if (!Array.isArray(root.features) || root.features.length > 128)
92
+ throw new Error("功能评估配置超出上限");
93
+ const features = root.features.map((value): FeatureEnvironmentPolicy => {
94
+ const row = object(
95
+ value,
96
+ ["key", "title", "execution"],
97
+ [
98
+ "recommendedEnabled",
99
+ "supportedPlatforms",
100
+ "supportedArchitectures",
101
+ "minMemoryGiB",
102
+ "requiredEnvironmentKeys",
103
+ ],
104
+ );
105
+ const [key] = strings([row.key], /^[a-z][a-z0-9.-]*$/);
106
+ if (
107
+ typeof row.title !== "string" ||
108
+ !row.title.trim() ||
109
+ row.title.length > 120
110
+ )
111
+ throw new Error("功能评估标题无效");
112
+ if (!["none", "cpu", "local-gpu", "remote"].includes(String(row.execution)))
113
+ throw new Error("功能执行方式无效");
114
+ if (
115
+ row.recommendedEnabled !== undefined &&
116
+ typeof row.recommendedEnabled !== "boolean"
117
+ )
118
+ throw new Error("推荐开关必须为布尔值");
119
+ if (
120
+ row.minMemoryGiB !== undefined &&
121
+ (typeof row.minMemoryGiB !== "number" ||
122
+ !Number.isFinite(row.minMemoryGiB) ||
123
+ row.minMemoryGiB <= 0 ||
124
+ row.minMemoryGiB > 65536)
125
+ )
126
+ throw new Error("最低内存要求无效");
127
+ return {
128
+ key: key!,
129
+ title: row.title,
130
+ execution: row.execution as FeatureEnvironmentPolicy["execution"],
131
+ ...(row.recommendedEnabled === undefined
132
+ ? {}
133
+ : { recommendedEnabled: row.recommendedEnabled as boolean }),
134
+ ...(row.minMemoryGiB === undefined
135
+ ? {}
136
+ : { minMemoryGiB: row.minMemoryGiB as number }),
137
+ ...(row.supportedPlatforms === undefined
138
+ ? {}
139
+ : {
140
+ supportedPlatforms: strings(
141
+ row.supportedPlatforms,
142
+ /^(linux|darwin|win32)$/,
143
+ ),
144
+ }),
145
+ ...(row.supportedArchitectures === undefined
146
+ ? {}
147
+ : {
148
+ supportedArchitectures: strings(
149
+ row.supportedArchitectures,
150
+ /^(x64|arm64|arm|ia32|ppc64|s390x|riscv64|loong64)$/,
151
+ ),
152
+ }),
153
+ ...(row.requiredEnvironmentKeys === undefined
154
+ ? {}
155
+ : {
156
+ requiredEnvironmentKeys: strings(
157
+ row.requiredEnvironmentKeys,
158
+ /^[A-Z][A-Z0-9_]*$/,
159
+ ),
160
+ }),
161
+ };
162
+ });
163
+ if (new Set(features.map(({ key }) => key)).size !== features.length)
164
+ throw new Error("功能评估 Key 重复");
165
+ const runtime = Object.hasOwn(root, "runtime")
166
+ ? parseRuntimeSwitches(root.runtime)
167
+ : undefined;
168
+ const runtimes = Object.hasOwn(build, "runtimes")
169
+ ? parseRuntimeSwitches(build.runtimes)
170
+ : undefined;
171
+ for (const kind of AI_RUNTIME_KINDS) {
172
+ if (runtimes?.[kind] === false && runtime?.[kind] === true)
173
+ throw new Error(`运行时未随包交付:${kind}`);
174
+ }
175
+ const antivirus = Object.hasOwn(root, "antivirus")
176
+ ? parseAntivirusPolicy(root.antivirus)
177
+ : undefined;
178
+ return {
179
+ schemaVersion: 1,
180
+ build: { modules, ...(runtimes ? { runtimes } : {}) },
181
+ features,
182
+ ...(runtime ? { runtime } : {}),
183
+ ...(antivirus ? { antivirus } : {}),
184
+ };
185
+ }
186
+
187
+ function parseRuntimeSwitches(value: unknown): AiRuntimeSwitches {
188
+ const switches = object(value, [], [...AI_RUNTIME_KINDS]);
189
+ const runtime: AiRuntimeSwitches = {};
190
+ for (const kind of AI_RUNTIME_KINDS) {
191
+ if (!Object.hasOwn(switches, kind)) continue;
192
+ const enabled = switches[kind];
193
+ if (typeof enabled !== "boolean") throw new Error("运行时开关必须为布尔值");
194
+ runtime[kind] = enabled;
195
+ }
196
+ return runtime;
197
+ }
198
+
199
+ function parseAntivirusPolicy(value: unknown): AntivirusEnvironmentPolicy {
200
+ const { packaged, enabled } = object(value, ["packaged", "enabled"]);
201
+ if (typeof packaged !== "boolean" || typeof enabled !== "boolean")
202
+ throw new Error("病毒扫描交付和启用设置必须为布尔值");
203
+ if (enabled && !packaged) throw new Error("病毒扫描未随项目交付,不能启用");
204
+ return { packaged, enabled };
205
+ }
@@ -101,6 +101,13 @@ export const LICENSE_CONSOLE_CAPABILITIES = {
101
101
  apis: ["POST /api/auth/sessions/:id/revoke"],
102
102
  auditActions: ["auth.session.revoke"],
103
103
  },
104
+ profile: {
105
+ feature: "license.profile",
106
+ permission: "license.profile.update",
107
+ entry: "profile",
108
+ apis: ["PATCH /api/auth/me"],
109
+ auditActions: ["license.operator.updated"],
110
+ },
104
111
  audit: {
105
112
  feature: "license.audit",
106
113
  permission: "license.audit.read",
@@ -205,6 +205,7 @@ export type LicenseOperatorRole =
205
205
  export type LicenseOperatorPermission =
206
206
  | "license.record.read"
207
207
  | "license.audit.read"
208
+ | "license.profile.update"
208
209
  | "license.issue"
209
210
  | "license.project.manage"
210
211
  | "license.key.rotate"
@@ -20,7 +20,8 @@ export interface ModuleConfigFieldDescriptor {
20
20
  path: string;
21
21
  label: string;
22
22
  description: string;
23
- input: "boolean" | "integer" | "text";
23
+ input: "boolean" | "integer" | "text" | "time-zone";
24
+ unit?: "bytes";
24
25
  applyMode?: ModuleConfigApplyMode;
25
26
  minimum?: number;
26
27
  maximum?: number;
@@ -0,0 +1,230 @@
1
+ #!/usr/bin/env python3
2
+ """Emit bounded, read-only host diagnostics JSON to stdout. Run ON THE HOST.
3
+
4
+ No package installation, service restart, Docker inspect, environment dump or
5
+ credential reads. Redirect stdout to a report and mount that file read-only in
6
+ Server as WINDY_HOST_DIAGNOSTICS_FILE. Missing tools do not abort the report.
7
+ """
8
+ from __future__ import annotations
9
+
10
+ import argparse
11
+ import datetime
12
+ import json
13
+ import os
14
+ import platform
15
+ import re
16
+ import stat
17
+ import selectors
18
+ import shutil
19
+ import signal
20
+ import subprocess
21
+ import time
22
+ from pathlib import Path
23
+
24
+ MAX_OUTPUT = 16384
25
+ TIMEOUT_SECONDS = 5
26
+ PROBE_KEYS = (
27
+ "cpu", "memory", "blockDevices", "disks", "mounts", "network", "systemd",
28
+ "docker", "compose", "containers", "gpu",
29
+ )
30
+
31
+
32
+ def result(status, output="", exit_code=None, truncated=False):
33
+ return {"status": status, "exitCode": exit_code,
34
+ "output": output, "truncated": truncated}
35
+
36
+
37
+ def stop(child):
38
+ if child.poll() is None:
39
+ try:
40
+ os.killpg(child.pid, signal.SIGKILL)
41
+ except ProcessLookupError:
42
+ pass
43
+ child.wait()
44
+
45
+
46
+ def probe(command):
47
+ """Fixed argv only; output and wall time are independently bounded."""
48
+ if not shutil.which(command[0]):
49
+ return result("missing")
50
+ environment = {"PATH": os.defpath + ":/usr/local/bin:/opt/homebrew/bin",
51
+ "LANG": "C", "LC_ALL": "C"}
52
+ # Never inherit remote Docker contexts or credentials from the invoking shell.
53
+ child = None
54
+ try:
55
+ child = subprocess.Popen(command, stdout=subprocess.PIPE,
56
+ stderr=subprocess.STDOUT, env=environment,
57
+ start_new_session=True)
58
+ output = bytearray()
59
+ deadline = time.monotonic() + TIMEOUT_SECONDS
60
+ with selectors.DefaultSelector() as selector:
61
+ selector.register(child.stdout, selectors.EVENT_READ)
62
+ while selector.get_map():
63
+ remaining = deadline - time.monotonic()
64
+ if remaining <= 0:
65
+ stop(child)
66
+ return result("timeout", output.decode("utf-8", "replace"))
67
+ for key, _ in selector.select(min(remaining, 0.1)):
68
+ chunk = os.read(key.fileobj.fileno(), 4096)
69
+ if not chunk:
70
+ selector.unregister(key.fileobj)
71
+ continue
72
+ available = MAX_OUTPUT - len(output)
73
+ output.extend(chunk[:available])
74
+ if len(chunk) > available:
75
+ stop(child)
76
+ return result("failed", output.decode("utf-8", "replace"),
77
+ child.returncode, True)
78
+ try:
79
+ code = child.wait(timeout=max(0.01, deadline - time.monotonic()))
80
+ except subprocess.TimeoutExpired:
81
+ stop(child)
82
+ return result("timeout", output.decode("utf-8", "replace"))
83
+ text = output.decode("utf-8", "replace")
84
+ denied = any(term in text.lower() for term in (
85
+ "permission denied", "operation not permitted", "access is denied"))
86
+ status = "ok" if code == 0 else "permission-denied" if denied else "failed"
87
+ return result(status, text, code)
88
+ except PermissionError:
89
+ return result("permission-denied")
90
+ except OSError:
91
+ return result("failed")
92
+ finally:
93
+ if child is not None:
94
+ stop(child)
95
+ if child.stdout:
96
+ child.stdout.close()
97
+
98
+
99
+ def memory_bytes(system):
100
+ try:
101
+ if system == "Linux":
102
+ with Path("/proc/meminfo").open() as stream:
103
+ for line in stream.read(4096).splitlines():
104
+ if line.startswith("MemTotal:"):
105
+ return int(line.split()[1]) * 1024
106
+ if system == "Darwin":
107
+ value = probe(["sysctl", "-n", "hw.memsize"])
108
+ return int(value["output"].strip()) if value["status"] == "ok" else None
109
+ except (OSError, ValueError):
110
+ pass
111
+ return None
112
+
113
+
114
+ def commands(system, systemd_unit=None):
115
+ common = {
116
+ "docker": ["docker", "--host", "unix:///var/run/docker.sock", "version", "--format", "{{json .}}"],
117
+ "compose": ["docker", "compose", "version", "--short"],
118
+ "containers": ["docker", "--host", "unix:///var/run/docker.sock", "ps", "-a", "--format",
119
+ "{{.Names}}\t{{.Image}}\t{{.State}}\t{{.Status}}\t{{.Ports}}"],
120
+ "gpu": ["nvidia-smi", "--query-gpu=name,memory.total,driver_version",
121
+ "--format=csv,noheader,nounits"],
122
+ }
123
+ if system == "Linux":
124
+ return {**common,
125
+ "cpu": ["lscpu", "--json"],
126
+ "memory": ["free", "-b"],
127
+ "blockDevices": ["lsblk", "--json", "--bytes", "--output",
128
+ "NAME,TYPE,SIZE,FSTYPE,MOUNTPOINTS,ROTA"],
129
+ "disks": ["df", "-Pk"],
130
+ "mounts": ["findmnt", "--json", "--output", "TARGET,SOURCE,FSTYPE"],
131
+ "network": ["ip", "-brief", "address"],
132
+ "systemd": (["systemctl", "show", "--no-pager",
133
+ "--property=Id,LoadState,ActiveState,SubState,UnitFileState,Result,ExecMainStatus,ActiveEnterTimestamp",
134
+ "--", unit_name(systemd_unit)] if systemd_unit else
135
+ ["systemctl", "is-system-running"])}
136
+ if system == "Darwin":
137
+ return {**common,
138
+ "cpu": ["sysctl", "machdep.cpu.brand_string", "hw.logicalcpu"],
139
+ "memory": ["sysctl", "hw.memsize"],
140
+ "blockDevices": ["diskutil", "list"],
141
+ "disks": ["df", "-Pk"],
142
+ "mounts": ["mount"],
143
+ "network": ["ifconfig", "-a"]}
144
+ return {}
145
+
146
+
147
+ def distribution(system):
148
+ if system == "Linux":
149
+ try:
150
+ with Path("/etc/os-release").open() as stream:
151
+ for line in stream.read(16384).splitlines():
152
+ if line.startswith("PRETTY_NAME="):
153
+ return line.split("=", 1)[1].strip().strip("\"")[:256]
154
+ except OSError:
155
+ pass
156
+ return platform.platform()[:256]
157
+
158
+
159
+ def unit_name(value):
160
+ if not re.fullmatch(r"[A-Za-z0-9][A-Za-z0-9_.@-]{0,239}\.(service|target|socket|timer|mount)", value):
161
+ raise argparse.ArgumentTypeError("systemd unit must be a literal unit name")
162
+ return value
163
+
164
+
165
+ def deployment_manifest(path):
166
+ """Read a small regular file; only release identity fields reach the report."""
167
+ descriptor = None
168
+ try:
169
+ descriptor = os.open(path, os.O_RDONLY | getattr(os, "O_NOFOLLOW", 0) |
170
+ getattr(os, "O_NONBLOCK", 0))
171
+ metadata = os.fstat(descriptor)
172
+ if not stat.S_ISREG(metadata.st_mode) or metadata.st_size > 65536:
173
+ return {"status": "invalid"}
174
+ with os.fdopen(descriptor, "rb") as stream:
175
+ descriptor = None
176
+ content = stream.read(65537)
177
+ if len(content) > 65536:
178
+ return {"status": "invalid"}
179
+ data = json.loads(content)
180
+ if not isinstance(data, dict):
181
+ return {"status": "invalid"}
182
+ selected = {}
183
+ patterns = {"releaseId": r"[A-Za-z0-9._-]{1,128}",
184
+ "gitSha": r"[a-fA-F0-9]{7,64}",
185
+ "platform": r"[A-Za-z0-9._/-]{1,64}"}
186
+ for key, pattern in patterns.items():
187
+ if key in data:
188
+ if not isinstance(data[key], str) or not re.fullmatch(pattern, data[key]):
189
+ return {"status": "invalid"}
190
+ selected[key] = data[key]
191
+ return {"status": "available", **selected} if selected else {"status": "invalid"}
192
+ except (ValueError, UnicodeError):
193
+ return {"status": "invalid"}
194
+ except OSError:
195
+ return {"status": "unreadable"}
196
+ finally:
197
+ if descriptor is not None:
198
+ os.close(descriptor)
199
+
200
+
201
+ def collect(systemd_unit=None, manifest=None):
202
+ system = platform.system()
203
+ fixed_commands = commands(system, systemd_unit)
204
+ probes = {key: probe(fixed_commands[key]) if key in fixed_commands
205
+ else result("unsupported") for key in PROBE_KEYS}
206
+ gpu = probes["gpu"]
207
+ gpu_fact = {"gpuCount": len(gpu["output"].strip().splitlines())} if (
208
+ gpu["status"] == "ok" and not gpu["truncated"]) else {}
209
+ return {
210
+ "schemaVersion": 1, "scope": "host",
211
+ "collector": "windy-host-diagnostics-v1",
212
+ "collectedAt": datetime.datetime.now(datetime.timezone.utc).isoformat(),
213
+ "facts": {"os": {"Linux": "linux", "Darwin": "darwin", "Windows": "win32"}.get(system, system.lower()),
214
+ "distribution": distribution(system), "kernel": platform.release(),
215
+ "architecture": platform.machine(),
216
+ "cpuLogicalCount": os.cpu_count() or 0,
217
+ "memoryTotalBytes": memory_bytes(system), **gpu_fact},
218
+ "probes": probes,
219
+ **({"deployment": deployment_manifest(manifest)} if manifest else {}),
220
+ }
221
+
222
+
223
+ if __name__ == "__main__":
224
+ parser = argparse.ArgumentParser(description=__doc__)
225
+ parser.add_argument("--systemd-unit", type=unit_name,
226
+ help="Inspect one installation unit using fixed read-only properties")
227
+ parser.add_argument("--manifest", type=Path,
228
+ help="Read releaseId/gitSha/platform from a <=64 KiB release manifest")
229
+ args = parser.parse_args()
230
+ print(json.dumps(collect(args.systemd_unit, args.manifest), ensure_ascii=False, indent=2))
@@ -1,41 +0,0 @@
1
- import type { ModuleManifest } from "@southwind-ai/modules";
2
- import { createModuleAiOperationReadinessPort } from "../ai-runtime-readiness/operation-port.js";
3
- import type { AiRuntimeReadinessTrustStoreV1 } from "../ai-runtime-readiness/trust-store.js";
4
- import { createModuleAiOperationPort } from "../module-host/ai-operation-port.js";
5
- import type { createServerRuntime } from "../runtime.js";
6
-
7
- type OperationRuntime = Parameters<typeof createModuleAiOperationPort>[3];
8
- type ReadinessInput = Parameters<typeof createModuleAiOperationReadinessPort>[0];
9
-
10
- export function createModuleAiPorts(input: {
11
- readonly modules: readonly ModuleManifest[];
12
- readonly runtime: ReturnType<typeof createServerRuntime>;
13
- readonly sources: ReadinessInput["sources"];
14
- readonly trustStore: AiRuntimeReadinessTrustStoreV1;
15
- readonly operations: OperationRuntime;
16
- }) {
17
- const operations = (
18
- moduleName: string,
19
- guardContext: Parameters<typeof createModuleAiOperationPort>[1],
20
- signal: AbortSignal,
21
- ) => createModuleAiOperationPort(
22
- moduleName,
23
- guardContext,
24
- signal,
25
- input.operations,
26
- );
27
- const readiness = (
28
- moduleName: string,
29
- guardContext: Parameters<typeof createModuleAiOperationPort>[1],
30
- signal: AbortSignal,
31
- ) => createModuleAiOperationReadinessPort({
32
- moduleName,
33
- guardContext,
34
- signal,
35
- modules: input.modules,
36
- sources: input.sources,
37
- trustStore: input.trustStore,
38
- runtime: input.runtime,
39
- });
40
- return { operations, readiness };
41
- }