create-windy 0.9.1 → 0.10.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 +1769 -1277
- package/package.json +2 -2
- package/template/.agents/skills/windy-business-module/SKILL.md +33 -1
- package/template/.windy-template.json +6 -2
- package/template/AGENTS.md +4 -0
- package/template/README.md +2 -0
- package/template/apps/agent-server/Dockerfile +1 -1
- package/template/apps/agent-server/README.md +1 -1
- package/template/apps/agent-server/native/EXTENSIONS.md +2 -2
- package/template/apps/agent-server/native/parallel-harness.ts +2 -0
- package/template/apps/agent-server/native/storage-lifecycle.test.ts +2 -1
- package/template/apps/agent-server/package.json +42 -41
- package/template/apps/agent-server/plugins/downloads/README.md +1 -1
- package/template/apps/agent-server/plugins/drop-overlay/README.md +1 -1
- package/template/apps/agent-server/plugins/settings/README.md +1 -1
- package/template/apps/agent-server/plugins/skill-library/client-chunks.test.ts +4 -1
- package/template/apps/agent-server/plugins/theme/README.md +5 -1
- package/template/apps/agent-server/plugins/theme/source-tokens.ts +0 -1
- package/template/apps/agent-server/plugins/theme/tokens.json +0 -4
- package/template/apps/agent-server/plugins/tool-catalog/README.md +6 -3
- package/template/apps/agent-server/plugins/tool-catalog/discovery.mjs +13 -10
- package/template/apps/agent-server/plugins/tool-catalog/discovery.test.ts +68 -6
- package/template/apps/agent-server/runtime/compatibility.ts +7 -1
- package/template/apps/agent-server/runtime/dsh-engine.ts +2 -0
- package/template/apps/agent-server/runtime/questions-workflow.nodecheck.ts +2 -0
- package/template/apps/server/Dockerfile +2 -1
- package/template/apps/server/package.json +11 -11
- package/template/apps/server/scripts/validate-module-composition-once.ts +2 -0
- package/template/apps/server/scripts/validate-module-composition.ts +8 -4
- package/template/apps/server/src/admin/routes.test.ts +29 -1
- package/template/apps/server/src/admin/routes.ts +20 -2
- package/template/apps/server/src/agent/client-session/completion.ts +1 -1
- package/template/apps/server/src/agent/client-session/manifest-admission.ts +1 -1
- package/template/apps/server/src/agent/client-session/request.ts +1 -1
- package/template/apps/server/src/agent/context-version-routes.ts +37 -13
- package/template/apps/server/src/agent/run-facade.ts +1 -1
- package/template/apps/server/src/agent/run-input-verification.ts +1 -1
- package/template/apps/server/src/agent/run-routes.ts +1 -14
- package/template/apps/server/src/agent/run-start-preparation.ts +1 -1
- package/template/apps/server/src/agent/runtime-bootstrap-input.ts +2 -0
- package/template/apps/server/src/agent/runtime-bootstrap.ts +49 -157
- package/template/apps/server/src/agent/runtime-disabled-composition.ts +2 -1
- package/template/apps/server/src/agent/session-invalidation/authorization-revocation.ts +54 -0
- package/template/apps/server/src/agent/session-runtime-composition.ts +1 -51
- package/template/apps/server/src/agent/unavailable-routes.ts +13 -2
- package/template/apps/server/src/agent-workflow/runtime-composition.ts +3 -0
- package/template/apps/server/src/agent-workflow/service.ts +17 -1
- package/template/apps/server/src/ai-artifact/media-resource-route.test.ts +134 -0
- package/template/apps/server/src/ai-artifact/routes.ts +1 -1
- package/template/apps/server/src/ai-artifact/runtime.ts +12 -0
- package/template/apps/server/src/ai-runtime-readiness/operation-port.test-support.ts +1 -1
- package/template/apps/server/src/ai-runtime-readiness/operation-port.ts +8 -0
- package/template/apps/server/src/ai-runtime-security/internal-token.ts +14 -0
- package/template/apps/server/src/app/antivirus-composition.test.ts +63 -0
- package/template/apps/server/src/app/antivirus-composition.ts +29 -0
- package/template/apps/server/src/app/dsh-workbench-socket-audit.test.ts +2 -1
- package/template/apps/server/src/configuration/definition.ts +15 -11
- package/template/apps/server/src/document/facade-support.ts +2 -2
- package/template/apps/server/src/document/facade.ts +2 -2
- package/template/apps/server/src/document/output-artifact-composition.ts +9 -5
- package/template/apps/server/src/document/output-artifact.test-support.ts +1 -1
- package/template/apps/server/src/embedding/facade.ts +1 -1
- package/template/apps/server/src/embedding/input.ts +1 -1
- package/template/apps/server/src/embedding/routes.ts +1 -1
- package/template/apps/server/src/index.ts +60 -60
- package/template/apps/server/src/license/routes.ts +4 -1
- package/template/apps/server/src/license/runtime-business-access.test.ts +261 -0
- package/template/apps/server/src/media-runtime/composition.ts +157 -0
- package/template/apps/server/src/media-runtime/media-only-composition.ts +44 -0
- package/template/apps/server/src/media-runtime/module-ai-port-factory.test.ts +69 -0
- package/template/apps/server/src/media-runtime/module-ai-port-factory.ts +72 -0
- package/template/apps/server/src/{agent → media-runtime}/runtime-readiness-sources.ts +14 -6
- package/template/apps/server/src/media-runtime/shared-composition.ts +104 -0
- package/template/apps/server/src/module-composition.test.ts +22 -0
- package/template/apps/server/src/module-composition.ts +48 -0
- package/template/apps/server/src/module-host/ai-operation-port.ts +97 -30
- package/template/apps/server/src/module-host/ai-operation-speech-normalize.test.ts +1 -1
- package/template/apps/server/src/module-host/http-runtime.ts +9 -3
- package/template/apps/server/src/module-host/static-route-authorization.test.ts +6 -3
- package/template/apps/server/src/module-host/storage-authorization-binding.test.ts +4 -0
- package/template/apps/server/src/module-runtime-validation.ts +2 -0
- package/template/apps/server/src/observability/diagnostic-index.ts +26 -1
- package/template/apps/server/src/observability/diagnostic-repository.ts +40 -11
- package/template/apps/server/src/platform-http-runtime.ts +1 -1
- package/template/apps/server/src/runtime-feature.ts +5 -1
- package/template/apps/server/src/runtime.ts +3 -0
- package/template/apps/server/src/settings/drizzle-watermark-repository.ts +35 -4
- package/template/apps/server/src/settings/watermark-repository.ts +14 -0
- package/template/apps/server/src/settings/watermark-routes.test.ts +84 -2
- package/template/apps/server/src/settings/watermark-routes.ts +109 -0
- package/template/apps/server/src/settings/watermark-transfer.ts +79 -0
- package/template/apps/server/src/speech/facade-validation.ts +1 -1
- package/template/apps/server/src/speech/facade.ts +2 -2
- package/template/apps/server/src/startup-composition-validation.test.ts +32 -0
- package/template/apps/server/src/startup-finalization.ts +2 -0
- package/template/apps/server/src/startup-runtime.ts +77 -0
- package/template/apps/server/src/system/ai-runtime-build.ts +3 -0
- package/template/apps/server/src/system/ai-runtime-configuration.test.ts +36 -0
- package/template/apps/server/src/system/ai-runtime-configuration.ts +58 -0
- package/template/apps/server/src/system/diagnostic-routes.test.ts +45 -0
- package/template/apps/server/src/system/diagnostic-routes.ts +35 -0
- package/template/apps/server/src/system/environment-policy-reader.test.ts +74 -0
- package/template/apps/server/src/system/environment-policy-reader.ts +62 -0
- package/template/apps/server/src/system/feature-environment-assessment.test.ts +148 -0
- package/template/apps/server/src/system/feature-environment-assessment.ts +179 -0
- package/template/apps/server/src/system/host-diagnostics.test.ts +155 -0
- package/template/apps/server/src/system/host-diagnostics.ts +225 -0
- package/template/apps/server/src/system/menu-registry.test.ts +26 -0
- package/template/apps/server/src/system/menu-registry.ts +9 -7
- package/template/apps/server/src/system/operations.test.ts +5 -0
- package/template/apps/server/src/system/operations.ts +36 -12
- package/template/apps/server/src/system/role-authorization-postgres.integration.test.ts +181 -0
- package/template/apps/web/Dockerfile +7 -3
- package/template/apps/web/dev/dev-port.test.ts +6 -6
- package/template/apps/web/dev/dev-port.ts +1 -1
- package/template/apps/web/dev/start.ts +6 -1
- package/template/apps/web/index.html +1 -0
- package/template/apps/web/package.json +16 -16
- package/template/apps/web/runtime-server.test.ts +22 -1
- package/template/apps/web/runtime-server.ts +6 -0
- package/template/apps/web/src/App.vue +6 -3
- package/template/apps/web/src/assets/windy.svg +4 -0
- package/template/apps/web/src/components/auth/LoginForm.vue +2 -2
- package/template/apps/web/src/components/auth/PasswordChangeForm.vue +14 -3
- package/template/apps/web/src/components/common/FormDialog.vue +1 -1
- package/template/apps/web/src/components/license/LicenseFlow.webtest.ts +3 -0
- package/template/apps/web/src/components/license/LicenseMachineCodeQr.vue +99 -0
- package/template/apps/web/src/components/license/LicenseMachineCodeQr.webtest.ts +83 -0
- package/template/apps/web/src/components/license/LicensePairingCard.vue +1 -1
- package/template/apps/web/src/components/license/LicenseStatusCard.vue +14 -2
- package/template/apps/web/src/composables/useLoginFlow.ts +2 -2
- package/template/apps/web/src/composables/usePlatformSettings.ts +1 -1
- package/template/apps/web/src/composables/useWatermarkSettings.ts +1 -0
- package/template/apps/web/src/layout/AppSidebar.vue +5 -9
- package/template/apps/web/src/layout/AppSidebar.webtest.ts +4 -1
- package/template/apps/web/src/pages/auth/ChangePasswordPage.vue +67 -6
- package/template/apps/web/src/pages/auth/LoginPage.vue +3 -12
- package/template/apps/web/src/pages/configuration/components/ModuleConfigurationEditor.vue +30 -0
- package/template/apps/web/src/pages/configuration/components/ModuleConfigurationWorkbench.vue +25 -14
- package/template/apps/web/src/pages/configuration/components/TimeZoneSelect.vue +55 -0
- package/template/apps/web/src/pages/profile/PersonalSettingsPage.vue +64 -11
- package/template/apps/web/src/pages/profile/PersonalSettingsPage.webtest.ts +21 -1
- package/template/apps/web/src/pages/settings/BrandingSettingsSection.vue +7 -18
- package/template/apps/web/src/pages/settings/BrandingSettingsSection.webtest.ts +9 -4
- package/template/apps/web/src/pages/settings/GlobalSettingsPage.vue +51 -6
- package/template/apps/web/src/pages/settings/GlobalSettingsPage.webtest.ts +19 -3
- package/template/apps/web/src/pages/settings/WatermarkSettingsSection.vue +1 -0
- package/template/apps/web/src/pages/settings/WatermarkYamlTransfer.vue +211 -0
- package/template/apps/web/src/pages/system/DiagnosticLookupPage.vue +18 -3
- package/template/apps/web/src/pages/system/DiagnosticLookupPage.webtest.ts +4 -1
- package/template/apps/web/src/pages/system/SystemOperationsPage.vue +23 -0
- package/template/apps/web/src/pages/system/components/DiagnosticLogList.vue +120 -0
- package/template/apps/web/src/pages/system/components/OperationEnvironmentPanel.vue +237 -0
- package/template/apps/web/src/pages/system/components/OperationFeatureAssessmentPanel.vue +131 -0
- package/template/apps/web/src/pages/system/components/PagedSelectionList.vue +31 -24
- package/template/apps/web/src/pages/system/components/SystemResourceActions.vue +2 -1
- package/template/apps/web/src/pages/system/components/SystemResourceEditor.vue +11 -2
- package/template/apps/web/src/pages/system/components/SystemResourcePage.vue +1 -0
- package/template/apps/web/src/pages/system/components/SystemResourcePage.webtest.ts +6 -2
- package/template/apps/web/src/pages/system/components/agent-skills/AgentSkillImportDialog.vue +18 -9
- package/template/apps/web/src/pages/system/components/agent-workbench/SessionReplayTimeline.vue +18 -9
- package/template/apps/web/src/pages/system/components/agent-workbench/TestRunComposer.vue +66 -34
- package/template/apps/web/src/pages/system/components/agent-workbench/ToolCatalogPanel.vue +48 -33
- package/template/apps/web/src/pages/system/components/agent-workbench/ToolCatalogPanel.webtest.ts +9 -2
- package/template/apps/web/src/pages/system/components/agent-workbench/ToolRuleDetail.vue +23 -16
- package/template/apps/web/src/pages/system/components/dictionary-bulk/DictionaryBulkJobDetail.vue +10 -7
- package/template/apps/web/src/pages/system/components/user-account/RolePermissionSummary.vue +81 -57
- package/template/apps/web/src/pages/system/components/user-account/UserRoleSelector.vue +6 -4
- package/template/apps/web/src/pages/system/components/user-account/UserRoleSelector.webtest.ts +20 -4
- package/template/apps/web/src/pages/system/resource-config.ts +9 -0
- package/template/apps/web/src/pages/work-orders/WorkOrderListPage.vue +1 -1
- package/template/apps/web/src/router/disabled-feature-loading.webtest.ts +78 -0
- package/template/apps/web/src/router/index.ts +3 -6
- package/template/apps/web/src/router/manifest-routes.ts +2 -3
- package/template/apps/web/src/router/platform-admin-pages.ts +21 -62
- package/template/apps/web/src/router/platform-admin-route-bindings.ts +4 -4
- package/template/apps/web/src/services/diagnostics-api.ts +13 -1
- package/template/apps/web/src/services/operations-api.ts +4 -0
- package/template/apps/web/src/services/platform-settings-api.ts +35 -1
- package/template/apps/web/vite.config.ts +9 -3
- package/template/deploy/environment/windy.environment.example.yaml +53 -0
- package/template/docker-compose.yml +4 -2
- package/template/docs/architecture/agent-admin-workbench-implementation.md +1 -1
- package/template/docs/architecture/agent-admin-workbench-plan.md +1 -1
- package/template/docs/architecture/agent-workflow.md +8 -0
- package/template/docs/architecture/durable-jobs.md +17 -0
- package/template/docs/architecture/embedded-dsh-integration.md +1 -1
- package/template/docs/development/dsh-plugins.md +1 -1
- package/template/docs/development/performance-engineering.md +24 -0
- package/template/docs/operations/admin-settings-yaml.md +29 -0
- package/template/docs/operations/dsh-agent-migration.md +2 -2
- package/template/docs/operations/environment-capabilities.md +185 -0
- package/template/docs/platform/module-configuration.md +18 -3
- package/template/docs/platform/platform-authentication.md +1 -1
- package/template/package.json +10 -8
- package/template/packages/config/package.json +2 -2
- package/template/packages/config/src/definition.ts +14 -4
- package/template/packages/config/src/environment-loader.ts +27 -3
- package/template/packages/config/src/platform-ai-runtime.test.ts +94 -0
- package/template/packages/config/src/platform.ts +5 -2
- package/template/packages/database/drizzle/0065_durable_job_lease_snapshot_lookup.sql +1 -0
- package/template/packages/database/drizzle/0066_agent_source_sequence_bigint.sql +3 -0
- package/template/packages/database/drizzle/0067_media_runtime_access.sql +46 -0
- package/template/packages/database/drizzle/meta/0065_snapshot.json +15370 -0
- package/template/packages/database/drizzle/meta/0066_snapshot.json +15370 -0
- package/template/packages/database/drizzle/meta/0067_snapshot.json +15370 -0
- package/template/packages/database/drizzle/meta/_journal.json +21 -0
- package/template/packages/database/package.json +3 -3
- package/template/packages/database/src/schema/agent-session.ts +4 -3
- package/template/packages/database/src/schema/durable-jobs.ts +4 -0
- package/template/packages/database/src/schema/index.ts +3 -1
- package/template/packages/database/src/schema-durable-recovery.test.ts +21 -1
- package/template/packages/database/src/schema-source-sequence.test.ts +24 -0
- package/template/packages/example-work-order/package.json +2 -2
- package/template/packages/modules/package.json +1 -1
- package/template/packages/modules/src/admin-ui-boundary.test.ts +33 -0
- package/template/packages/modules/src/composition.ts +11 -28
- package/template/packages/modules/src/manifest.ts +2 -0
- package/template/packages/modules/src/menu-composition.ts +24 -0
- package/template/packages/modules/src/system-admin-routes.ts +2 -2
- package/template/packages/modules/src/system-admin-ui-catalog.ts +6 -1
- package/template/packages/modules/src/system-ai-api-bindings.ts +0 -16
- package/template/packages/modules/src/system-api-control-plane.ts +9 -1
- package/template/packages/modules/src/system-api-permissions.ts +7 -0
- package/template/packages/modules/src/system-api-watermark.ts +21 -0
- package/template/packages/modules/src/system-audit-actions.ts +3 -1
- package/template/packages/modules/src/system-features.ts +21 -3
- package/template/packages/modules/src/system-media-api-bindings.ts +97 -0
- package/template/packages/modules/src/system-module-audit-selection.ts +2 -0
- package/template/packages/modules/src/system-module-catalog.test.ts +13 -6
- package/template/packages/modules/src/system-module-catalog.ts +6 -241
- package/template/packages/modules/src/system-module-definitions.ts +260 -0
- package/template/packages/modules/src/system-modules-access.test.ts +2 -0
- package/template/packages/modules/src/system-modules.test.ts +7 -1
- package/template/packages/modules/src/system-modules.ts +25 -13
- package/template/packages/modules/src/system-permissions.ts +12 -0
- package/template/packages/server-sdk/package.json +1 -1
- package/template/packages/shared/index.ts +3 -0
- package/template/packages/shared/package.json +1 -1
- package/template/packages/shared/src/environment-assessment.ts +68 -0
- package/template/packages/shared/src/environment-policy.ts +205 -0
- package/template/packages/shared/src/license-console-capabilities.ts +7 -0
- package/template/packages/shared/src/license.ts +1 -0
- package/template/packages/shared/src/module-configuration.ts +2 -1
- package/template/scripts/operations/collect-device-info.py +230 -0
- package/template/apps/server/src/agent/module-ai-port-factory.ts +0 -41
- /package/template/apps/server/src/{agent → ai-runtime-security}/execution-grants.ts +0 -0
- /package/template/apps/server/src/{agent → ai-runtime-security}/idempotency-key.test.ts +0 -0
- /package/template/apps/server/src/{agent → ai-runtime-security}/idempotency-key.ts +0 -0
- /package/template/apps/server/src/{agent → ai-runtime-security}/provider-input-governance.test.ts +0 -0
- /package/template/apps/server/src/{agent → ai-runtime-security}/provider-input-governance.ts +0 -0
- /package/template/apps/server/src/{agent → media-runtime}/required-environment.ts +0 -0
- /package/template/apps/server/src/{agent → media-runtime}/runtime-readiness-sources.test.ts +0 -0
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
import { createEnvironmentPolicyReader } from "./environment-policy-reader.js";
|
|
2
|
+
import { assessFeatureEnvironment } from "./feature-environment-assessment.js";
|
|
3
|
+
import {
|
|
4
|
+
createEnvironmentProbe,
|
|
5
|
+
type EnvironmentProbe,
|
|
6
|
+
} from "./host-diagnostics.js";
|
|
1
7
|
import type { FeatureFlagDefinition, HttpMethod } from "@southwind-ai/shared";
|
|
2
8
|
import type { ActiveAuthSession } from "../auth/repository.js";
|
|
3
9
|
import type { RequestGuardContext } from "../guards.js";
|
|
@@ -32,6 +38,7 @@ interface OperationRouteContext {
|
|
|
32
38
|
}
|
|
33
39
|
|
|
34
40
|
export interface OperationRouteOptions {
|
|
41
|
+
environmentProbe?: EnvironmentProbe;
|
|
35
42
|
redisHealthProbe?: RedisHealthProbe;
|
|
36
43
|
databaseMonitorProbe?: DatabaseMonitorProbe;
|
|
37
44
|
hostDiskUsageProbe?: HostDiskUsageProbe;
|
|
@@ -79,6 +86,8 @@ export function registerSystemOperationRoutes(
|
|
|
79
86
|
);
|
|
80
87
|
const cpuMonitorProbe = options.cpuMonitorProbe ?? createCpuMonitorProbe();
|
|
81
88
|
const gpuMonitorProbe = options.gpuMonitorProbe ?? createGpuMonitorProbe();
|
|
89
|
+
const readEnvironmentPolicy = createEnvironmentPolicyReader();
|
|
90
|
+
const environmentProbe = options.environmentProbe ?? createEnvironmentProbe();
|
|
82
91
|
const onlineUserProvider = options.onlineUserProvider ?? (async () => []);
|
|
83
92
|
app.get("/system/operations", async (rawContext) => {
|
|
84
93
|
const { guardContext } = rawContext as OperationRouteContext;
|
|
@@ -90,18 +99,29 @@ export function registerSystemOperationRoutes(
|
|
|
90
99
|
},
|
|
91
100
|
feature: findSystemFeature(features, "ops"),
|
|
92
101
|
});
|
|
93
|
-
return decision.
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
102
|
+
if (!decision.allowed) return forbidden(decision.reason);
|
|
103
|
+
const snapshot = await createOperationSnapshot(
|
|
104
|
+
redisHealthProbe,
|
|
105
|
+
databaseMonitorProbe,
|
|
106
|
+
hostDiskUsageProbe,
|
|
107
|
+
systemUsageProbe,
|
|
108
|
+
importDataUsageProbe,
|
|
109
|
+
cpuMonitorProbe,
|
|
110
|
+
gpuMonitorProbe,
|
|
111
|
+
onlineUserProvider,
|
|
112
|
+
environmentProbe,
|
|
113
|
+
);
|
|
114
|
+
const featureAssessment = await assessFeatureEnvironment(
|
|
115
|
+
await readEnvironmentPolicy(),
|
|
116
|
+
{
|
|
117
|
+
environment: snapshot.environment,
|
|
118
|
+
definitions: runtime.featureDefinitions,
|
|
119
|
+
resolveEnabled: async (feature) =>
|
|
120
|
+
(await runtime.resolveFeature(guardContext, feature)).enabled,
|
|
121
|
+
environmentValues: process.env,
|
|
122
|
+
},
|
|
123
|
+
);
|
|
124
|
+
return { ...snapshot, featureAssessment };
|
|
105
125
|
});
|
|
106
126
|
|
|
107
127
|
return app;
|
|
@@ -123,6 +143,7 @@ async function createOperationSnapshot(
|
|
|
123
143
|
cpuMonitorProbe: CpuMonitorProbe,
|
|
124
144
|
gpuMonitorProbe: GpuMonitorProbe,
|
|
125
145
|
onlineUserProvider: () => Promise<ActiveAuthSession[]>,
|
|
146
|
+
environmentProbe: EnvironmentProbe,
|
|
126
147
|
) {
|
|
127
148
|
const [
|
|
128
149
|
cacheMonitor,
|
|
@@ -133,6 +154,7 @@ async function createOperationSnapshot(
|
|
|
133
154
|
cpuMonitor,
|
|
134
155
|
gpuMonitor,
|
|
135
156
|
activeSessions,
|
|
157
|
+
environment,
|
|
136
158
|
] = await Promise.all([
|
|
137
159
|
redisHealthProbe(),
|
|
138
160
|
databaseMonitorProbe(),
|
|
@@ -142,9 +164,11 @@ async function createOperationSnapshot(
|
|
|
142
164
|
cpuMonitorProbe(),
|
|
143
165
|
gpuMonitorProbe(),
|
|
144
166
|
onlineUserProvider(),
|
|
167
|
+
environmentProbe(),
|
|
145
168
|
]);
|
|
146
169
|
return {
|
|
147
170
|
generatedAt: new Date().toISOString(),
|
|
171
|
+
environment,
|
|
148
172
|
onlineUsers: activeSessions.map((session) => ({
|
|
149
173
|
sessionId: session.sessionId,
|
|
150
174
|
userId: session.userId,
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
// @windy-module system.agent begin
|
|
2
|
+
import {
|
|
3
|
+
afterAll,
|
|
4
|
+
beforeAll,
|
|
5
|
+
beforeEach,
|
|
6
|
+
describe,
|
|
7
|
+
expect,
|
|
8
|
+
test,
|
|
9
|
+
} from "bun:test";
|
|
10
|
+
import { resolve } from "node:path";
|
|
11
|
+
import { drizzle } from "drizzle-orm/node-postgres";
|
|
12
|
+
import { migrate } from "drizzle-orm/node-postgres/migrator";
|
|
13
|
+
import { Pool } from "pg";
|
|
14
|
+
import { agentSourceInvalidations } from "@southwind-ai/database";
|
|
15
|
+
import { createFoundationSnapshot } from "../foundation.js";
|
|
16
|
+
import type { RequestActor } from "../guards.js";
|
|
17
|
+
import { createSystemRepositories } from "./seed.js";
|
|
18
|
+
import { UserAccountPolicy } from "./user-account-policy.js";
|
|
19
|
+
import { invalidateActorAuthorization } from "../agent/session-invalidation/authorization-revocation.js";
|
|
20
|
+
import { ModuleAuthoritativeSourceAccessRegistry } from "../agent/context-memory/authoritative-source-registry.js";
|
|
21
|
+
import { DrizzleSessionInvalidationOutbox } from "../agent/session-persistence/drizzle-invalidation-outbox.js";
|
|
22
|
+
|
|
23
|
+
const databaseUrl = process.env.ORGANIZATION_TEST_DATABASE_URL;
|
|
24
|
+
const describePostgres = databaseUrl ? describe : describe.skip;
|
|
25
|
+
const originalPermissions = ["home.read", "case.read"];
|
|
26
|
+
const nextPermissions = [
|
|
27
|
+
...originalPermissions,
|
|
28
|
+
"intelligence.read",
|
|
29
|
+
"analysis.overview.read",
|
|
30
|
+
];
|
|
31
|
+
const actor: RequestActor = {
|
|
32
|
+
id: "role-test-admin",
|
|
33
|
+
type: "user",
|
|
34
|
+
name: "测试管理员",
|
|
35
|
+
roleCodes: ["super-admin"],
|
|
36
|
+
permissionKeys: ["system.role.manage"],
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
describePostgres("角色更新与 Agent 失效事件原子提交", () => {
|
|
40
|
+
let pool: Pool;
|
|
41
|
+
let repositories: ReturnType<typeof createSystemRepositories>;
|
|
42
|
+
let policy: UserAccountPolicy;
|
|
43
|
+
let failAfterPublish = false;
|
|
44
|
+
|
|
45
|
+
beforeAll(async () => {
|
|
46
|
+
pool = new Pool({ connectionString: databaseUrl });
|
|
47
|
+
await migrate(drizzle(pool), {
|
|
48
|
+
migrationsFolder: resolve(
|
|
49
|
+
import.meta.dir,
|
|
50
|
+
"../../../../packages/database/drizzle",
|
|
51
|
+
),
|
|
52
|
+
migrationsSchema: "public",
|
|
53
|
+
migrationsTable: "__organization_test_migrations",
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
beforeEach(async () => {
|
|
57
|
+
await pool.query(
|
|
58
|
+
"truncate table users, roles, departments, agent_source_invalidations cascade",
|
|
59
|
+
);
|
|
60
|
+
const db = drizzle(pool);
|
|
61
|
+
repositories = createSystemRepositories(createFoundationSnapshot(), { db });
|
|
62
|
+
await repositories.departments.all();
|
|
63
|
+
await repositories.roles.create(
|
|
64
|
+
{
|
|
65
|
+
id: "test-role",
|
|
66
|
+
code: "demo.leader",
|
|
67
|
+
name: "测试领导",
|
|
68
|
+
dataScope: "department-and-children",
|
|
69
|
+
departmentIds: [],
|
|
70
|
+
permissionKeys: originalPermissions,
|
|
71
|
+
},
|
|
72
|
+
actor,
|
|
73
|
+
);
|
|
74
|
+
await repositories.users.create(
|
|
75
|
+
{
|
|
76
|
+
id: "test-officer",
|
|
77
|
+
username: "test-officer",
|
|
78
|
+
displayName: "测试用户",
|
|
79
|
+
departmentId: "dept_platform",
|
|
80
|
+
roleIds: ["test-role"],
|
|
81
|
+
},
|
|
82
|
+
actor,
|
|
83
|
+
);
|
|
84
|
+
const sources = new ModuleAuthoritativeSourceAccessRegistry([
|
|
85
|
+
{
|
|
86
|
+
moduleKey: "system.agent",
|
|
87
|
+
access: {
|
|
88
|
+
authorizeCurrent: async () => ({
|
|
89
|
+
outcome: "denied",
|
|
90
|
+
reason: "authorization-revoked",
|
|
91
|
+
}),
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
]);
|
|
95
|
+
const outbox = new DrizzleSessionInvalidationOutbox(db);
|
|
96
|
+
failAfterPublish = false;
|
|
97
|
+
policy = new UserAccountPolicy(
|
|
98
|
+
repositories.users,
|
|
99
|
+
repositories.roles,
|
|
100
|
+
undefined,
|
|
101
|
+
{
|
|
102
|
+
async publish(event) {
|
|
103
|
+
const database =
|
|
104
|
+
repositories.organizationTransaction?.currentDatabase();
|
|
105
|
+
if (!database) throw new Error("missing organization transaction");
|
|
106
|
+
// 固定复现日志中的毫秒值;第二次更新也使用该时间,验证 latest + 1。
|
|
107
|
+
await invalidateActorAuthorization(
|
|
108
|
+
{ ...event, occurredAt: "2026-11-06T15:34:43.424Z", database },
|
|
109
|
+
sources,
|
|
110
|
+
outbox,
|
|
111
|
+
);
|
|
112
|
+
if (failAfterPublish) throw new Error("forced transaction failure");
|
|
113
|
+
},
|
|
114
|
+
},
|
|
115
|
+
);
|
|
116
|
+
});
|
|
117
|
+
afterAll(async () => {
|
|
118
|
+
await pool?.end();
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
async function updatePermissions(permissionKeys: string[]) {
|
|
122
|
+
return repositories.organizationMutationRunner.run(async () => {
|
|
123
|
+
const current = await repositories.roles.get("test-role");
|
|
124
|
+
if (!current) throw new Error("missing role");
|
|
125
|
+
const updated = await repositories.roles.update(
|
|
126
|
+
current.id,
|
|
127
|
+
{ permissionKeys },
|
|
128
|
+
actor,
|
|
129
|
+
);
|
|
130
|
+
if (!updated) throw new Error("missing updated role");
|
|
131
|
+
await policy.afterRoleUpdate(current, updated);
|
|
132
|
+
return updated;
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
test("现实时钟下增加、撤回权限均保存,并按 number 精确读取单调序号", async () => {
|
|
137
|
+
await updatePermissions(nextPermissions);
|
|
138
|
+
expect((await repositories.roles.get("test-role"))?.permissionKeys).toEqual(
|
|
139
|
+
nextPermissions,
|
|
140
|
+
);
|
|
141
|
+
await updatePermissions(originalPermissions);
|
|
142
|
+
const rows = await drizzle(pool)
|
|
143
|
+
.select()
|
|
144
|
+
.from(agentSourceInvalidations)
|
|
145
|
+
.orderBy(agentSourceInvalidations.sequence);
|
|
146
|
+
expect(rows.map(({ sequence }) => sequence)).toEqual([
|
|
147
|
+
1793979283424, 1793979283425,
|
|
148
|
+
]);
|
|
149
|
+
expect(rows.every(({ cause }) => cause === "authorization-revoked")).toBe(
|
|
150
|
+
true,
|
|
151
|
+
);
|
|
152
|
+
const deliveries = await pool.query(
|
|
153
|
+
"select count(*)::int as count from agent_source_invalidation_deliveries",
|
|
154
|
+
);
|
|
155
|
+
expect(deliveries.rows[0].count).toBeGreaterThan(0);
|
|
156
|
+
expect((await repositories.roles.get("test-role"))?.permissionKeys).toEqual(
|
|
157
|
+
originalPermissions,
|
|
158
|
+
);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
test("事件追加后发生错误仍同时回滚角色和 outbox", async () => {
|
|
162
|
+
failAfterPublish = true;
|
|
163
|
+
await expect(updatePermissions(nextPermissions)).rejects.toThrow(
|
|
164
|
+
"forced transaction failure",
|
|
165
|
+
);
|
|
166
|
+
expect((await repositories.roles.get("test-role"))?.permissionKeys).toEqual(
|
|
167
|
+
originalPermissions,
|
|
168
|
+
);
|
|
169
|
+
expect(await drizzle(pool).select().from(agentSourceInvalidations)).toEqual(
|
|
170
|
+
[],
|
|
171
|
+
);
|
|
172
|
+
expect(
|
|
173
|
+
(
|
|
174
|
+
await pool.query(
|
|
175
|
+
"select count(*)::int as count from agent_source_invalidation_deliveries",
|
|
176
|
+
)
|
|
177
|
+
).rows[0].count,
|
|
178
|
+
).toBe(0);
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
// @windy-module system.agent end
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
# syntax=docker/dockerfile:1
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
ARG WINDY_NODE_IMAGE=node:24.7.0-alpine
|
|
4
|
+
ARG WINDY_WEB_BUN_IMAGE=oven/bun:1.3.14-alpine
|
|
4
5
|
|
|
5
|
-
FROM
|
|
6
|
+
FROM ${WINDY_NODE_IMAGE} AS node
|
|
7
|
+
|
|
8
|
+
FROM ${WINDY_WEB_BUN_IMAGE} AS deps
|
|
6
9
|
COPY --from=node /usr/local/bin/node /usr/local/bin/node
|
|
7
10
|
WORKDIR /app
|
|
8
11
|
|
|
@@ -30,7 +33,8 @@ COPY packages/server-sdk/package.json packages/server-sdk/package.json
|
|
|
30
33
|
COPY packages/shared/package.json packages/shared/package.json
|
|
31
34
|
COPY packages/storage/package.json packages/storage/package.json
|
|
32
35
|
# windy-copy:deps end
|
|
33
|
-
|
|
36
|
+
ARG WINDY_NPM_REGISTRY=https://registry.npmjs.org
|
|
37
|
+
RUN --mount=type=cache,id=windy-bun-1.3.14,target=/root/.bun/install/cache,sharing=shared bun -e 'const child=Bun.spawn(["bun","install","--frozen-lockfile","--no-progress","--filter","*","--filter","!./apps/agent-server","--registry",process.env.WINDY_NPM_REGISTRY||"https://registry.npmjs.org","--backend=copyfile","--linker=hoisted","--ignore-scripts"],{stdin:"inherit",stdout:"inherit",stderr:"inherit"});const started=Date.now();const heartbeat=setInterval(()=>console.log("[windy] Bun 依赖安装尚未完成,已耗时 "+Math.round((Date.now()-started)/1000)+" 秒"),15000);const deadline=setTimeout(()=>{console.error("[windy] Bun 安装超过 300 秒,请检查 npm 下载网络或通过 WINDY_NPM_REGISTRY 指定可用源");child.kill("SIGKILL")},300000);const exitCode=await child.exited;clearInterval(heartbeat);clearTimeout(deadline);process.exit(exitCode)' && bun --cwd apps/server -e 'await import("argon2")'
|
|
34
38
|
|
|
35
39
|
FROM deps AS build
|
|
36
40
|
# windy-copy:build begin
|
|
@@ -3,26 +3,26 @@ import { selectDevPort, type PortState } from "./dev-port";
|
|
|
3
3
|
import { windyDevIdentity } from "./dev-identity";
|
|
4
4
|
|
|
5
5
|
describe("Windy 本地开发端口", () => {
|
|
6
|
-
test("默认选择
|
|
6
|
+
test("默认选择 28746", async () => {
|
|
7
7
|
const result = await selectDevPort({
|
|
8
8
|
fingerprint: "project-a",
|
|
9
9
|
inspect: async () => ({ kind: "available" }),
|
|
10
10
|
});
|
|
11
11
|
|
|
12
|
-
expect(result).toEqual({ kind: "selected", port:
|
|
12
|
+
expect(result).toEqual({ kind: "selected", port: 28_746 });
|
|
13
13
|
});
|
|
14
14
|
|
|
15
15
|
test("外部服务或其他 Windy 项目占用时自动递增", async () => {
|
|
16
16
|
const states = new Map<number, PortState>([
|
|
17
|
-
[
|
|
18
|
-
[
|
|
17
|
+
[28_746, { kind: "occupied" }],
|
|
18
|
+
[28_747, { kind: "occupied", identity: windyDevIdentity("project-b") }],
|
|
19
19
|
]);
|
|
20
20
|
const result = await selectDevPort({
|
|
21
21
|
fingerprint: "project-a",
|
|
22
22
|
inspect: async (port) => states.get(port) || { kind: "available" },
|
|
23
23
|
});
|
|
24
24
|
|
|
25
|
-
expect(result).toEqual({ kind: "selected", port:
|
|
25
|
+
expect(result).toEqual({ kind: "selected", port: 28_748 });
|
|
26
26
|
});
|
|
27
27
|
|
|
28
28
|
test("检测到同一项目时返回已有地址而不启动重复服务", async () => {
|
|
@@ -34,7 +34,7 @@ describe("Windy 本地开发端口", () => {
|
|
|
34
34
|
}),
|
|
35
35
|
});
|
|
36
36
|
|
|
37
|
-
expect(result).toEqual({ kind: "existing", port:
|
|
37
|
+
expect(result).toEqual({ kind: "existing", port: 28_746 });
|
|
38
38
|
});
|
|
39
39
|
|
|
40
40
|
test("最多扫描 100 个端口", async () => {
|
|
@@ -4,7 +4,9 @@ import { projectFingerprint } from "./dev-identity";
|
|
|
4
4
|
import { selectDevPort } from "./dev-port";
|
|
5
5
|
|
|
6
6
|
const fingerprint = projectFingerprint(process.cwd());
|
|
7
|
-
const configuredPort = parseConfiguredPort(
|
|
7
|
+
const configuredPort = parseConfiguredPort(
|
|
8
|
+
process.env.WINDY_DEV_PORT ?? process.env.WINDY_WEB_PORT,
|
|
9
|
+
);
|
|
8
10
|
const selection = configuredPort
|
|
9
11
|
? { kind: "selected" as const, port: configuredPort }
|
|
10
12
|
: await selectDevPort({ fingerprint });
|
|
@@ -29,6 +31,9 @@ const child = Bun.spawn(
|
|
|
29
31
|
env: {
|
|
30
32
|
...process.env,
|
|
31
33
|
WINDY_DEV_PORT: String(selection.port),
|
|
34
|
+
API_UPSTREAM:
|
|
35
|
+
process.env.API_UPSTREAM ??
|
|
36
|
+
`http://127.0.0.1:${process.env.WINDY_SERVER_PORT ?? "19747"}`,
|
|
32
37
|
WINDY_DEV_FINGERPRINT: fingerprint,
|
|
33
38
|
},
|
|
34
39
|
stdin: "inherit",
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<link rel="icon" href="/src/assets/windy.svg" type="image/svg+xml" />
|
|
6
7
|
<title>Windy Platform Template</title>
|
|
7
8
|
<!-- Tailwind CSS -->
|
|
8
9
|
<link rel="stylesheet" href="/src/style.css" />
|
|
@@ -13,38 +13,38 @@
|
|
|
13
13
|
},
|
|
14
14
|
"dependencies": {
|
|
15
15
|
"@antv/g2": "^5.4.8",
|
|
16
|
-
"@lucide/vue": "^1.
|
|
16
|
+
"@lucide/vue": "^1.70.0",
|
|
17
17
|
"@southwind-ai/example-work-order": "workspace:*",
|
|
18
18
|
"@southwind-ai/license": "workspace:*",
|
|
19
19
|
"@southwind-ai/shared": "workspace:*",
|
|
20
|
-
"@tailwindcss/vite": "^4.3.
|
|
21
|
-
"@tanstack/vue-form": "^1.33.
|
|
20
|
+
"@tailwindcss/vite": "^4.3.4",
|
|
21
|
+
"@tanstack/vue-form": "^1.33.5",
|
|
22
22
|
"@tanstack/vue-table": "^8.21.3",
|
|
23
23
|
"@vue-flow/core": "^1.48.2",
|
|
24
|
-
"@vueuse/core": "^14.
|
|
24
|
+
"@vueuse/core": "^14.4.0",
|
|
25
25
|
"class-variance-authority": "^0.7.1",
|
|
26
26
|
"clsx": "^2.1.1",
|
|
27
27
|
"pinia": "^3.0.4",
|
|
28
28
|
"qrcode": "^1.5.4",
|
|
29
|
-
"reka-ui": "^2.
|
|
30
|
-
"tailwind-merge": "^3.
|
|
31
|
-
"tailwindcss": "^4.3.
|
|
29
|
+
"reka-ui": "^2.11.0",
|
|
30
|
+
"tailwind-merge": "^3.7.0",
|
|
31
|
+
"tailwindcss": "^4.3.4",
|
|
32
32
|
"vee-validate": "^4.15.1",
|
|
33
|
-
"vue": "^3.5.
|
|
34
|
-
"vue-router": "^5.
|
|
33
|
+
"vue": "^3.5.45",
|
|
34
|
+
"vue-router": "^5.4.0",
|
|
35
35
|
"vue-sonner": "^2.0.9",
|
|
36
|
-
"ws": "^8.22.
|
|
36
|
+
"ws": "^8.22.1"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
39
|
"@southwind-ai/modules": "workspace:*",
|
|
40
40
|
"@types/qrcode": "^1.5.6",
|
|
41
41
|
"@types/ws": "^8.18.2",
|
|
42
|
-
"@vitejs/plugin-vue": "^6.0.
|
|
43
|
-
"@vue/test-utils": "^2.
|
|
44
|
-
"happy-dom": "^20.
|
|
42
|
+
"@vitejs/plugin-vue": "^6.0.10",
|
|
43
|
+
"@vue/test-utils": "^2.5.1",
|
|
44
|
+
"happy-dom": "^20.14.6",
|
|
45
45
|
"tw-animate-css": "^1.4.0",
|
|
46
|
-
"vite": "^8.
|
|
47
|
-
"vitest": "^4.1.
|
|
48
|
-
"vue-tsc": "^3.3.
|
|
46
|
+
"vite": "^8.3.6",
|
|
47
|
+
"vitest": "^4.1.12",
|
|
48
|
+
"vue-tsc": "^3.3.14"
|
|
49
49
|
}
|
|
50
50
|
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { describe, expect, mock, test } from "bun:test";
|
|
2
|
-
import { mkdir, rm } from "node:fs/promises";
|
|
2
|
+
import { mkdir, mkdtemp, rm } from "node:fs/promises";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
3
5
|
import { createWebFetchHandler } from "./runtime-server";
|
|
4
6
|
|
|
5
7
|
describe("Web 生产同源 API 代理", () => {
|
|
@@ -84,3 +86,22 @@ describe("Web 生产同源 API 代理", () => {
|
|
|
84
86
|
}
|
|
85
87
|
});
|
|
86
88
|
});
|
|
89
|
+
|
|
90
|
+
test("缺失的构建资源返回 404,业务深链接仍返回 SPA 入口", async () => {
|
|
91
|
+
const distDir = await mkdtemp(join(tmpdir(), "windy-web-assets-"));
|
|
92
|
+
try {
|
|
93
|
+
await Bun.write(join(distDir, "index.html"), "<html>Windy</html>");
|
|
94
|
+
const handler = createWebFetchHandler({ distDir });
|
|
95
|
+
const missing = await handler(new Request("http://web/assets/stale.js"));
|
|
96
|
+
expect(missing.status).toBe(404);
|
|
97
|
+
expect(missing.headers.get("content-type")).toBe(
|
|
98
|
+
"text/plain; charset=utf-8",
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
const route = await handler(new Request("http://web/admin/system/users"));
|
|
102
|
+
expect(route.status).toBe(200);
|
|
103
|
+
expect(await route.text()).toBe("<html>Windy</html>");
|
|
104
|
+
} finally {
|
|
105
|
+
await rm(distDir, { recursive: true, force: true });
|
|
106
|
+
}
|
|
107
|
+
});
|
|
@@ -53,6 +53,12 @@ async function serveStatic(
|
|
|
53
53
|
},
|
|
54
54
|
});
|
|
55
55
|
}
|
|
56
|
+
if (pathname.startsWith("/assets/")) {
|
|
57
|
+
return new Response("Not Found", {
|
|
58
|
+
status: 404,
|
|
59
|
+
headers: { "content-type": "text/plain; charset=utf-8" },
|
|
60
|
+
});
|
|
61
|
+
}
|
|
56
62
|
return new Response(Bun.file(join(distDir, "index.html")), {
|
|
57
63
|
headers: { "content-type": contentTypes[".html"] },
|
|
58
64
|
});
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import
|
|
3
|
-
import AppLayout from "@/app/layouts/AppLayout.vue";
|
|
4
|
-
import { computed } from "vue";
|
|
2
|
+
import { computed, defineAsyncComponent } from "vue";
|
|
5
3
|
import { useRoute } from "vue-router";
|
|
6
4
|
import { usePlatformSettings } from "@/composables/usePlatformSettings";
|
|
7
5
|
|
|
6
|
+
const AppShell = defineAsyncComponent(() => import("@/layout/AppShell.vue"));
|
|
7
|
+
const AppLayout = defineAsyncComponent(
|
|
8
|
+
() => import("@/app/layouts/AppLayout.vue"),
|
|
9
|
+
);
|
|
10
|
+
|
|
8
11
|
const route = useRoute();
|
|
9
12
|
const platformSettings = usePlatformSettings();
|
|
10
13
|
const layout = computed(() => route.meta.layout);
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
|
|
2
|
+
<rect width="64" height="64" rx="14" fill="#164b35"/>
|
|
3
|
+
<path d="M15 24h20a5 5 0 1 0-5-5M11 33h37a5 5 0 1 0-5-5M17 42h20a5 5 0 1 1-5 5" fill="none" stroke="#fff" stroke-width="4.5" stroke-linecap="round"/>
|
|
4
|
+
</svg>
|
|
@@ -34,7 +34,7 @@ function submit() {
|
|
|
34
34
|
|
|
35
35
|
<template>
|
|
36
36
|
<form class="space-y-4" data-testid="login-form" @submit.prevent="submit">
|
|
37
|
-
<div class="
|
|
37
|
+
<div class="grid gap-2">
|
|
38
38
|
<label for="username" class="text-sm font-medium">用户名</label>
|
|
39
39
|
<Input
|
|
40
40
|
id="username"
|
|
@@ -46,7 +46,7 @@ function submit() {
|
|
|
46
46
|
required
|
|
47
47
|
/>
|
|
48
48
|
</div>
|
|
49
|
-
<div class="
|
|
49
|
+
<div class="grid gap-2">
|
|
50
50
|
<label for="password" class="text-sm font-medium">密码</label>
|
|
51
51
|
<Input
|
|
52
52
|
id="password"
|
|
@@ -48,13 +48,24 @@ const canSubmit = computed(
|
|
|
48
48
|
newPassword.value === confirmation.value,
|
|
49
49
|
);
|
|
50
50
|
|
|
51
|
-
function
|
|
51
|
+
function getInput(): PasswordChangeInput | undefined {
|
|
52
52
|
if (!canSubmit.value) return;
|
|
53
|
-
|
|
53
|
+
return {
|
|
54
54
|
currentPassword: currentPassword.value,
|
|
55
55
|
newPassword: newPassword.value,
|
|
56
|
-
}
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function submit() {
|
|
60
|
+
const input = getInput();
|
|
61
|
+
if (input) emit("submit", input);
|
|
57
62
|
}
|
|
63
|
+
|
|
64
|
+
defineExpose({
|
|
65
|
+
getInput,
|
|
66
|
+
isDirty: () =>
|
|
67
|
+
Boolean(currentPassword.value || newPassword.value || confirmation.value),
|
|
68
|
+
});
|
|
58
69
|
</script>
|
|
59
70
|
|
|
60
71
|
<template>
|
|
@@ -25,6 +25,9 @@ describe("License Web 流程", () => {
|
|
|
25
25
|
"联系系统管理员",
|
|
26
26
|
);
|
|
27
27
|
expect(wrapper.find('[data-testid="machine-code"]').exists()).toBe(false);
|
|
28
|
+
expect(
|
|
29
|
+
wrapper.find('[data-testid="machine-code-qr-toggle"]').exists(),
|
|
30
|
+
).toBe(false);
|
|
28
31
|
expect(wrapper.text()).not.toContain("客户一");
|
|
29
32
|
expect(wrapper.text()).not.toContain("capability.audit");
|
|
30
33
|
});
|