@southwind-ai/shared 0.3.2 → 0.3.3
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/index.ts
CHANGED
|
@@ -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";
|
package/package.json
CHANGED
|
@@ -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",
|
package/src/license.ts
CHANGED
|
@@ -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;
|