@zhin.js/scaffold-wizard 0.1.5 → 0.1.7
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/lib/adapter.d.ts.map +1 -1
- package/lib/adapter.js +2 -1
- package/lib/adapter.js.map +1 -1
- package/lib/ai.d.ts +6 -0
- package/lib/ai.d.ts.map +1 -1
- package/lib/ai.js +64 -34
- package/lib/ai.js.map +1 -1
- package/lib/apply.js +2 -2
- package/lib/apply.js.map +1 -1
- package/lib/index.d.ts +10 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +5 -1
- package/lib/index.js.map +1 -1
- package/lib/optional-peers.d.ts +21 -0
- package/lib/optional-peers.d.ts.map +1 -0
- package/lib/optional-peers.js +105 -0
- package/lib/optional-peers.js.map +1 -0
- package/lib/project-config-plan.d.ts +53 -0
- package/lib/project-config-plan.d.ts.map +1 -0
- package/lib/project-config-plan.js +337 -0
- package/lib/project-config-plan.js.map +1 -0
- package/lib/project-deps.d.ts +26 -3
- package/lib/project-deps.d.ts.map +1 -1
- package/lib/project-deps.js +211 -26
- package/lib/project-deps.js.map +1 -1
- package/lib/upgrade-l4.d.ts +15 -0
- package/lib/upgrade-l4.d.ts.map +1 -0
- package/lib/upgrade-l4.js +68 -0
- package/lib/upgrade-l4.js.map +1 -0
- package/lib/zhin-stack-deps.d.ts +66 -0
- package/lib/zhin-stack-deps.d.ts.map +1 -0
- package/lib/zhin-stack-deps.js +202 -0
- package/lib/zhin-stack-deps.js.map +1 -0
- package/package.json +7 -5
- package/src/adapter.ts +2 -1
- package/src/ai.ts +76 -32
- package/src/apply.ts +2 -2
- package/src/index.ts +53 -0
- package/src/optional-peers.ts +149 -0
- package/src/project-config-plan.ts +400 -0
- package/src/project-deps.ts +228 -27
- package/src/stack-versions.generated.json +48 -0
- package/src/upgrade-l4.ts +103 -0
- package/src/zhin-stack-deps.ts +252 -0
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import { createRequire } from 'node:module';
|
|
2
|
+
import fs from 'fs-extra';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { DATABASE_PACKAGES } from './types.js';
|
|
5
|
+
import { findMissingPackageDependencies, findUnresolvedPackageInstalls, isAiEnabledInConfig, } from './project-deps.js';
|
|
6
|
+
/**
|
|
7
|
+
* User project dependency policy: scaffolded dependencies intentionally float to latest.
|
|
8
|
+
*/
|
|
9
|
+
export const ZHIN_STACK_VERSIONS = {
|
|
10
|
+
'zhin.js': 'latest',
|
|
11
|
+
'@zhin.js/cli': 'latest',
|
|
12
|
+
'@zhin.js/agent': 'latest',
|
|
13
|
+
'@zhin.js/database': 'latest',
|
|
14
|
+
'@zhin.js/host-router': 'latest',
|
|
15
|
+
'@zhin.js/host-api': 'latest',
|
|
16
|
+
'@zhin.js/client': 'latest',
|
|
17
|
+
'@zhin.js/contract': 'latest',
|
|
18
|
+
'@zhin.js/satori': 'latest',
|
|
19
|
+
'@zhin.js/speech': 'latest',
|
|
20
|
+
'@zhin.js/html-renderer': 'latest',
|
|
21
|
+
'@zhin.js/mcp': 'latest',
|
|
22
|
+
'@zhin.js/adapter-sandbox': 'latest',
|
|
23
|
+
'@zhin.js/adapter-dingtalk': 'latest',
|
|
24
|
+
'@zhin.js/adapter-discord': 'latest',
|
|
25
|
+
'@zhin.js/adapter-email': 'latest',
|
|
26
|
+
'@zhin.js/adapter-github': 'latest',
|
|
27
|
+
'@zhin.js/adapter-icqq': 'latest',
|
|
28
|
+
'@zhin.js/adapter-kook': 'latest',
|
|
29
|
+
'@zhin.js/adapter-lark': 'latest',
|
|
30
|
+
'@zhin.js/adapter-line': 'latest',
|
|
31
|
+
'@zhin.js/adapter-milky': 'latest',
|
|
32
|
+
'@zhin.js/adapter-napcat': 'latest',
|
|
33
|
+
'@zhin.js/adapter-onebot11': 'latest',
|
|
34
|
+
'@zhin.js/adapter-onebot12': 'latest',
|
|
35
|
+
'@zhin.js/adapter-qq': 'latest',
|
|
36
|
+
'@zhin.js/adapter-satori': 'latest',
|
|
37
|
+
'@zhin.js/adapter-slack': 'latest',
|
|
38
|
+
'@zhin.js/adapter-telegram': 'latest',
|
|
39
|
+
'@zhin.js/adapter-wechat-mp': 'latest',
|
|
40
|
+
'@zhin.js/adapter-wecom': 'latest',
|
|
41
|
+
'@zhin.js/adapter-weixin-ilink': 'latest',
|
|
42
|
+
};
|
|
43
|
+
const HOST_CONSOLE_PLUGINS = new Set([
|
|
44
|
+
'@zhin.js/host-router',
|
|
45
|
+
'@zhin.js/host-api',
|
|
46
|
+
]);
|
|
47
|
+
function parseMajor(versionSpec) {
|
|
48
|
+
if (!versionSpec?.trim())
|
|
49
|
+
return undefined;
|
|
50
|
+
if (versionSpec === 'workspace:*')
|
|
51
|
+
return undefined;
|
|
52
|
+
const match = versionSpec.trim().replace(/^[\^~>=<]+/, '').match(/^(\d+)/);
|
|
53
|
+
return match ? Number(match[1]) : undefined;
|
|
54
|
+
}
|
|
55
|
+
function getDeclaredPackageVersion(pkg, name) {
|
|
56
|
+
return pkg.dependencies?.[name] ?? pkg.devDependencies?.[name];
|
|
57
|
+
}
|
|
58
|
+
function readInstalledMajor(cwd, packageName) {
|
|
59
|
+
const pkgJson = path.join(cwd, 'package.json');
|
|
60
|
+
if (!fs.existsSync(pkgJson))
|
|
61
|
+
return undefined;
|
|
62
|
+
try {
|
|
63
|
+
const req = createRequire(pkgJson);
|
|
64
|
+
const installed = req(`${packageName}/package.json`);
|
|
65
|
+
return parseMajor(installed.version);
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
return undefined;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
function resolvePackageMajor(cwd, pkg, packageName) {
|
|
72
|
+
return readInstalledMajor(cwd, packageName) ?? parseMajor(getDeclaredPackageVersion(pkg, packageName));
|
|
73
|
+
}
|
|
74
|
+
function resolveStackVersion(packageName) {
|
|
75
|
+
return ZHIN_STACK_VERSIONS[packageName] ?? 'latest';
|
|
76
|
+
}
|
|
77
|
+
function collectZhinPluginsFromConfig(config) {
|
|
78
|
+
const plugins = Array.isArray(config.plugins)
|
|
79
|
+
? config.plugins.filter((p) => typeof p === 'string')
|
|
80
|
+
: [];
|
|
81
|
+
return [...new Set(plugins.filter((p) => p.startsWith('@zhin.js/')))];
|
|
82
|
+
}
|
|
83
|
+
/** create-zhin / minimal-bot 默认 Host 端口(避免与常见 8086 占用冲突) */
|
|
84
|
+
export const DEFAULT_CREATE_BOT_HTTP_PORT = 8068;
|
|
85
|
+
/** create-zhin 生成项目的 .npmrc(避免全局 strict-peer-dependencies 阻断 AI 栈安装) */
|
|
86
|
+
export const CREATE_BOT_NPMRC = 'strict-peer-dependencies=false\n';
|
|
87
|
+
/** create-zhin 生成项目的 pnpm 配置 */
|
|
88
|
+
export function getCreateBotPnpmConfig(_aiEnabled) {
|
|
89
|
+
return {
|
|
90
|
+
onlyBuiltDependencies: ['esbuild'],
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
/** create-zhin-app 默认 bot 骨架依赖(不含适配器 / AI / 数据库驱动) */
|
|
94
|
+
export function getCreateBotBaseDependencies() {
|
|
95
|
+
return {
|
|
96
|
+
'zhin.js': ZHIN_STACK_VERSIONS['zhin.js'],
|
|
97
|
+
'@zhin.js/cli': ZHIN_STACK_VERSIONS['@zhin.js/cli'],
|
|
98
|
+
'@zhin.js/host-router': ZHIN_STACK_VERSIONS['@zhin.js/host-router'],
|
|
99
|
+
'@zhin.js/client': ZHIN_STACK_VERSIONS['@zhin.js/client'],
|
|
100
|
+
'@zhin.js/host-api': ZHIN_STACK_VERSIONS['@zhin.js/host-api'],
|
|
101
|
+
'@zhin.js/contract': ZHIN_STACK_VERSIONS['@zhin.js/contract'],
|
|
102
|
+
'@zhin.js/satori': ZHIN_STACK_VERSIONS['@zhin.js/satori'],
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
/** 根据 zhin.config 推导应声明的 @zhin.js/* / zhin.js 依赖 */
|
|
106
|
+
export function getRequiredZhinDependenciesForConfig(config) {
|
|
107
|
+
const deps = {
|
|
108
|
+
'zhin.js': ZHIN_STACK_VERSIONS['zhin.js'],
|
|
109
|
+
};
|
|
110
|
+
for (const plugin of collectZhinPluginsFromConfig(config)) {
|
|
111
|
+
deps[plugin] = resolveStackVersion(plugin);
|
|
112
|
+
}
|
|
113
|
+
const database = config.database;
|
|
114
|
+
if (database && typeof database === 'object' && !Array.isArray(database)) {
|
|
115
|
+
deps['@zhin.js/database'] = ZHIN_STACK_VERSIONS['@zhin.js/database'];
|
|
116
|
+
const dialect = String(database.dialect ?? '');
|
|
117
|
+
const driver = DATABASE_PACKAGES[dialect];
|
|
118
|
+
if (driver) {
|
|
119
|
+
deps[driver] = 'latest';
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
const usesHost = collectZhinPluginsFromConfig(config).some((p) => HOST_CONSOLE_PLUGINS.has(p));
|
|
123
|
+
if (usesHost) {
|
|
124
|
+
for (const pkg of ['@zhin.js/host-router', '@zhin.js/host-api']) {
|
|
125
|
+
deps[pkg] = ZHIN_STACK_VERSIONS[pkg];
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return deps;
|
|
129
|
+
}
|
|
130
|
+
export function findOutdatedZhinStackInPackageJson(pkg, required) {
|
|
131
|
+
const outdated = [];
|
|
132
|
+
for (const [name, minSpec] of Object.entries(required)) {
|
|
133
|
+
const declared = getDeclaredPackageVersion(pkg, name);
|
|
134
|
+
if (!declared || declared === 'workspace:*')
|
|
135
|
+
continue;
|
|
136
|
+
const declaredMajor = parseMajor(declared);
|
|
137
|
+
const minMajor = parseMajor(minSpec);
|
|
138
|
+
if (declaredMajor != null && minMajor != null && declaredMajor < minMajor) {
|
|
139
|
+
outdated.push(name);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return outdated;
|
|
143
|
+
}
|
|
144
|
+
export function findInstalledZhinStackIncompatibilities(cwd, pkg, config) {
|
|
145
|
+
const issues = [];
|
|
146
|
+
const zhinMajor = resolvePackageMajor(cwd, pkg, 'zhin.js');
|
|
147
|
+
const agentDeclared = getDeclaredPackageVersion(pkg, '@zhin.js/agent');
|
|
148
|
+
const aiEnabled = isAiEnabledInConfig(config);
|
|
149
|
+
if (zhinMajor != null && zhinMajor < 4 && (aiEnabled || agentDeclared)) {
|
|
150
|
+
issues.push({
|
|
151
|
+
package: 'zhin.js',
|
|
152
|
+
reason: 'zhin.js 4.x 才支持 @zhin.js/agent 与 AI 安装分层(ADR 0019),请升级 zhin.js',
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
if (aiEnabled && !agentDeclared) {
|
|
156
|
+
// missing 由 missingFromPackageJson 处理;此处只报版本组合问题
|
|
157
|
+
}
|
|
158
|
+
const agentMajor = resolvePackageMajor(cwd, pkg, '@zhin.js/agent');
|
|
159
|
+
if (agentMajor != null && agentMajor < 1 && aiEnabled) {
|
|
160
|
+
issues.push({
|
|
161
|
+
package: '@zhin.js/agent',
|
|
162
|
+
reason: '@zhin.js/agent 1.x 与 zhin.js 4.x / AI SDK 7 配套,请升级 @zhin.js/agent',
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
return issues;
|
|
166
|
+
}
|
|
167
|
+
export function packagesNeedingZhinStackFix(diagnosis) {
|
|
168
|
+
const names = new Set([
|
|
169
|
+
...diagnosis.missingFromPackageJson,
|
|
170
|
+
...diagnosis.outdatedInPackageJson,
|
|
171
|
+
]);
|
|
172
|
+
for (const issue of diagnosis.incompatibleInstalled) {
|
|
173
|
+
names.add(issue.package);
|
|
174
|
+
}
|
|
175
|
+
return [...names];
|
|
176
|
+
}
|
|
177
|
+
export function diagnoseZhinStackDependencies(cwd, config, pkg) {
|
|
178
|
+
const required = getRequiredZhinDependenciesForConfig(config);
|
|
179
|
+
const packageJsonPath = path.join(cwd, 'package.json');
|
|
180
|
+
const packageJson = pkg ?? (fs.existsSync(packageJsonPath)
|
|
181
|
+
? fs.readJsonSync(packageJsonPath)
|
|
182
|
+
: {});
|
|
183
|
+
const missingFromPackageJson = findMissingPackageDependencies(packageJson, required);
|
|
184
|
+
const outdatedInPackageJson = findOutdatedZhinStackInPackageJson(packageJson, required);
|
|
185
|
+
const incompatibleInstalled = findInstalledZhinStackIncompatibilities(cwd, packageJson, config);
|
|
186
|
+
const declared = Object.keys(required).filter((name) => !missingFromPackageJson.includes(name));
|
|
187
|
+
const notInstalled = findUnresolvedPackageInstalls(cwd, declared);
|
|
188
|
+
return {
|
|
189
|
+
required,
|
|
190
|
+
missingFromPackageJson,
|
|
191
|
+
outdatedInPackageJson,
|
|
192
|
+
incompatibleInstalled,
|
|
193
|
+
notInstalled,
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
export function formatZhinStackFixCommand(packages, required) {
|
|
197
|
+
if (packages.length === 0)
|
|
198
|
+
return 'pnpm install';
|
|
199
|
+
const specs = packages.map((name) => `${name}@${required[name] ?? 'latest'}`);
|
|
200
|
+
return `pnpm add ${specs.join(' ')}`;
|
|
201
|
+
}
|
|
202
|
+
//# sourceMappingURL=zhin-stack-deps.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"zhin-stack-deps.js","sourceRoot":"","sources":["../src/zhin-stack-deps.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zhin.js/scaffold-wizard",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.7",
|
|
4
4
|
"description": "Shared interactive scaffold wizard for create-zhin-app and @zhin.js/cli",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./lib/index.js",
|
|
@@ -18,13 +18,15 @@
|
|
|
18
18
|
],
|
|
19
19
|
"dependencies": {
|
|
20
20
|
"chalk": "^5.6.2",
|
|
21
|
-
"fs-extra": "^11.3.
|
|
22
|
-
"inquirer": "^14.0.2"
|
|
21
|
+
"fs-extra": "^11.3.6",
|
|
22
|
+
"inquirer": "^14.0.2",
|
|
23
|
+
"smol-toml": "^1.7.0",
|
|
24
|
+
"yaml": "^2.9.0"
|
|
23
25
|
},
|
|
24
26
|
"devDependencies": {
|
|
25
27
|
"@types/fs-extra": "^11.0.4",
|
|
26
|
-
"@types/inquirer": "^9.0.
|
|
27
|
-
"@types/node": "^
|
|
28
|
+
"@types/inquirer": "^9.0.10",
|
|
29
|
+
"@types/node": "^26.1.0",
|
|
28
30
|
"typescript": "^6.0.3"
|
|
29
31
|
},
|
|
30
32
|
"engines": {
|
package/src/adapter.ts
CHANGED
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
configureSlackEndpoint,
|
|
11
11
|
configureTelegramEndpoint,
|
|
12
12
|
} from './adapter-configurers.js';
|
|
13
|
+
import { ZHIN_STACK_VERSIONS } from './zhin-stack-deps.js';
|
|
13
14
|
|
|
14
15
|
export interface AdapterSetupResult {
|
|
15
16
|
packages: string[];
|
|
@@ -519,7 +520,7 @@ export function getAdapterDependencies(result: AdapterSetupResult): Record<strin
|
|
|
519
520
|
deps[name] = version;
|
|
520
521
|
}
|
|
521
522
|
} else {
|
|
522
|
-
deps[pkg] = 'latest';
|
|
523
|
+
deps[pkg] = ZHIN_STACK_VERSIONS[pkg as keyof typeof ZHIN_STACK_VERSIONS] ?? 'latest';
|
|
523
524
|
}
|
|
524
525
|
} else {
|
|
525
526
|
deps[pkg] = 'latest';
|
package/src/ai.ts
CHANGED
|
@@ -3,8 +3,16 @@ import chalk from 'chalk';
|
|
|
3
3
|
|
|
4
4
|
export interface AISetupConfig {
|
|
5
5
|
enabled: boolean;
|
|
6
|
+
agentProvider?: string;
|
|
7
|
+
/** @deprecated Use agentProvider. Kept for one compatibility cycle. */
|
|
6
8
|
defaultProvider?: string;
|
|
7
|
-
providers?: Record<string, {
|
|
9
|
+
providers?: Record<string, {
|
|
10
|
+
sdk?: string;
|
|
11
|
+
apiKey?: string;
|
|
12
|
+
host?: string;
|
|
13
|
+
models?: string[];
|
|
14
|
+
baseUrl?: string;
|
|
15
|
+
}>;
|
|
8
16
|
sessions?: { useDatabase: boolean; maxHistory: number; expireMs: number };
|
|
9
17
|
context?: { enabled: boolean; maxRecentMessages: number; summaryThreshold: number; keepAfterSummary: number };
|
|
10
18
|
agent?: {
|
|
@@ -76,7 +84,7 @@ const PROVIDERS = [
|
|
|
76
84
|
},
|
|
77
85
|
] as const;
|
|
78
86
|
|
|
79
|
-
function providerSdkFor(name: string): string {
|
|
87
|
+
export function providerSdkFor(name: string): string {
|
|
80
88
|
switch (name) {
|
|
81
89
|
case 'anthropic':
|
|
82
90
|
return 'anthropic';
|
|
@@ -100,6 +108,60 @@ function defaultModelForProvider(driver: string): string {
|
|
|
100
108
|
return entry?.defaultModel ?? '';
|
|
101
109
|
}
|
|
102
110
|
|
|
111
|
+
export function resolveAISetupAgentProvider(config: Pick<AISetupConfig, 'agentProvider' | 'defaultProvider' | 'providers'>): string {
|
|
112
|
+
return config.agentProvider
|
|
113
|
+
?? config.defaultProvider
|
|
114
|
+
?? Object.keys(config.providers ?? {})[0]
|
|
115
|
+
?? 'openai';
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function resolveAISetupAgentModel(config: AISetupConfig, providerAlias: string): string {
|
|
119
|
+
return config.providers?.[providerAlias]?.models?.[0]
|
|
120
|
+
?? defaultModelForProvider(providerAlias)
|
|
121
|
+
?? '';
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export function materializeAIConfig(config: AISetupConfig): Record<string, unknown> {
|
|
125
|
+
const providerAlias = resolveAISetupAgentProvider(config);
|
|
126
|
+
const providers: Record<string, Record<string, unknown>> = {};
|
|
127
|
+
|
|
128
|
+
for (const [name, providerConfig] of Object.entries(config.providers ?? {})) {
|
|
129
|
+
const clean: Record<string, unknown> = {
|
|
130
|
+
sdk: providerConfig.sdk ?? providerSdkFor(name),
|
|
131
|
+
};
|
|
132
|
+
if (providerConfig.apiKey) clean.apiKey = providerConfig.apiKey;
|
|
133
|
+
if (providerConfig.host) clean.host = providerConfig.host;
|
|
134
|
+
if (providerConfig.models) clean.models = providerConfig.models;
|
|
135
|
+
if (providerConfig.baseUrl) clean.baseUrl = providerConfig.baseUrl;
|
|
136
|
+
providers[name] = clean;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (Object.keys(providers).length === 0) {
|
|
140
|
+
providers[providerAlias] = { sdk: providerSdkFor(providerAlias) };
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const agent: Record<string, unknown> = {
|
|
144
|
+
provider: providerAlias,
|
|
145
|
+
};
|
|
146
|
+
const model = resolveAISetupAgentModel(config, providerAlias);
|
|
147
|
+
if (model) agent.model = model;
|
|
148
|
+
|
|
149
|
+
const obj: Record<string, unknown> = {
|
|
150
|
+
providers,
|
|
151
|
+
agents: {
|
|
152
|
+
zhin: agent,
|
|
153
|
+
},
|
|
154
|
+
sessions: config.sessions,
|
|
155
|
+
context: config.context,
|
|
156
|
+
agent: config.agent,
|
|
157
|
+
trigger: config.trigger ? { ...config.trigger } : undefined,
|
|
158
|
+
memoryMcp: config.memoryMcp ?? false,
|
|
159
|
+
mcpServers: config.mcpServers ?? [],
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
return Object.fromEntries(Object.entries(obj).filter(([, value]) => value !== undefined));
|
|
163
|
+
}
|
|
164
|
+
|
|
103
165
|
export const RECOMMENDED_AI_DEFAULTS = {
|
|
104
166
|
sessions: {
|
|
105
167
|
useDatabase: true,
|
|
@@ -292,7 +354,7 @@ export async function configureAI(): Promise<AISetupConfig> {
|
|
|
292
354
|
|
|
293
355
|
return {
|
|
294
356
|
enabled: true,
|
|
295
|
-
|
|
357
|
+
agentProvider: provider,
|
|
296
358
|
providers: {
|
|
297
359
|
[provider]: providerConfig,
|
|
298
360
|
},
|
|
@@ -340,7 +402,7 @@ export function generateAIEnvVars(config: AISetupConfig): string {
|
|
|
340
402
|
export function generateAIConfigYaml(config: AISetupConfig): string {
|
|
341
403
|
if (!config.enabled) return '';
|
|
342
404
|
|
|
343
|
-
const providerAlias =
|
|
405
|
+
const providerAlias = resolveAISetupAgentProvider(config);
|
|
344
406
|
|
|
345
407
|
const lines: string[] = [
|
|
346
408
|
'',
|
|
@@ -351,7 +413,7 @@ export function generateAIConfigYaml(config: AISetupConfig): string {
|
|
|
351
413
|
if (config.providers) {
|
|
352
414
|
for (const [name, providerConfig] of Object.entries(config.providers)) {
|
|
353
415
|
lines.push(` ${name}:`);
|
|
354
|
-
lines.push(` sdk: ${providerSdkFor(name)}`);
|
|
416
|
+
lines.push(` sdk: ${providerConfig.sdk ?? providerSdkFor(name)}`);
|
|
355
417
|
if (providerConfig.apiKey) {
|
|
356
418
|
lines.push(` apiKey: ${providerConfig.apiKey}`);
|
|
357
419
|
}
|
|
@@ -370,9 +432,7 @@ export function generateAIConfigYaml(config: AISetupConfig): string {
|
|
|
370
432
|
}
|
|
371
433
|
}
|
|
372
434
|
|
|
373
|
-
const zhinModel =
|
|
374
|
-
|| config.providers?.[providerAlias]?.models?.[0]
|
|
375
|
-
|| '';
|
|
435
|
+
const zhinModel = resolveAISetupAgentModel(config, providerAlias);
|
|
376
436
|
lines.push(' agents:');
|
|
377
437
|
lines.push(' zhin:');
|
|
378
438
|
lines.push(` provider: ${providerAlias}`);
|
|
@@ -436,13 +496,13 @@ export function generateAIConfigToml(config: AISetupConfig): string {
|
|
|
436
496
|
const lines: string[] = [
|
|
437
497
|
'',
|
|
438
498
|
'[ai]',
|
|
439
|
-
`defaultProvider = "${config.defaultProvider}"`,
|
|
440
499
|
`memoryMcp = ${config.memoryMcp ?? false}`,
|
|
441
500
|
];
|
|
442
501
|
|
|
443
502
|
if (config.providers) {
|
|
444
503
|
for (const [name, providerConfig] of Object.entries(config.providers)) {
|
|
445
504
|
lines.push('', `[ai.providers.${name}]`);
|
|
505
|
+
lines.push(`sdk = "${providerConfig.sdk ?? providerSdkFor(name)}"`);
|
|
446
506
|
if (providerConfig.apiKey) lines.push(`apiKey = "${providerConfig.apiKey}"`);
|
|
447
507
|
if (providerConfig.host) lines.push(`host = "${providerConfig.host}"`);
|
|
448
508
|
if (providerConfig.models?.length) {
|
|
@@ -452,6 +512,12 @@ export function generateAIConfigToml(config: AISetupConfig): string {
|
|
|
452
512
|
}
|
|
453
513
|
}
|
|
454
514
|
|
|
515
|
+
const providerAlias = resolveAISetupAgentProvider(config);
|
|
516
|
+
lines.push('', '[ai.agents.zhin]');
|
|
517
|
+
lines.push(`provider = "${providerAlias}"`);
|
|
518
|
+
const zhinModel = resolveAISetupAgentModel(config, providerAlias);
|
|
519
|
+
if (zhinModel) lines.push(`model = "${zhinModel}"`);
|
|
520
|
+
|
|
455
521
|
if (config.trigger) {
|
|
456
522
|
lines.push('', '[ai.trigger]');
|
|
457
523
|
lines.push(`respondToAt = ${config.trigger.respondToAt}`);
|
|
@@ -493,29 +559,7 @@ export function generateAIConfigToml(config: AISetupConfig): string {
|
|
|
493
559
|
export function generateAIConfigJSON(config: AISetupConfig): string {
|
|
494
560
|
if (!config.enabled) return '';
|
|
495
561
|
|
|
496
|
-
const obj
|
|
497
|
-
defaultProvider: config.defaultProvider,
|
|
498
|
-
providers: {},
|
|
499
|
-
sessions: config.sessions,
|
|
500
|
-
context: config.context,
|
|
501
|
-
agent: config.agent,
|
|
502
|
-
trigger: config.trigger ? {
|
|
503
|
-
...config.trigger,
|
|
504
|
-
} : undefined,
|
|
505
|
-
memoryMcp: config.memoryMcp ?? false,
|
|
506
|
-
mcpServers: config.mcpServers ?? [],
|
|
507
|
-
};
|
|
508
|
-
|
|
509
|
-
if (config.providers) {
|
|
510
|
-
for (const [name, providerConfig] of Object.entries(config.providers)) {
|
|
511
|
-
const clean: any = {};
|
|
512
|
-
if (providerConfig.apiKey) clean.apiKey = providerConfig.apiKey;
|
|
513
|
-
if (providerConfig.host) clean.host = providerConfig.host;
|
|
514
|
-
if (providerConfig.models) clean.models = providerConfig.models;
|
|
515
|
-
if (providerConfig.baseUrl) clean.baseUrl = providerConfig.baseUrl;
|
|
516
|
-
obj.providers[name] = clean;
|
|
517
|
-
}
|
|
518
|
-
}
|
|
562
|
+
const obj = materializeAIConfig(config);
|
|
519
563
|
|
|
520
564
|
return `"ai": ${JSON.stringify(obj, null, 4).replace(/^/gm, ' ').trimStart()}`;
|
|
521
565
|
}
|
package/src/apply.ts
CHANGED
|
@@ -2,7 +2,7 @@ import fs from 'fs-extra';
|
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import type { AdapterSetupResult, AISetupConfig, DatabaseConfig, InitOptions } from './types.js';
|
|
4
4
|
import { generateAdapterEnvVars, getAdapterDependencies } from './adapter.js';
|
|
5
|
-
import { generateAIEnvVars } from './ai.js';
|
|
5
|
+
import { generateAIEnvVars, materializeAIConfig } from './ai.js';
|
|
6
6
|
import { ensureDatabaseForAdapters, ensureDatabaseForAI, getAIDependencies } from './project-deps.js';
|
|
7
7
|
|
|
8
8
|
/** Host 插件(Webhook / Console 必需) */
|
|
@@ -36,7 +36,7 @@ export function applyAdaptersToConfig(config: Record<string, unknown>, result: A
|
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
export function applyAIToConfig(config: Record<string, unknown>, ai: AISetupConfig): void {
|
|
39
|
-
config.ai = ai;
|
|
39
|
+
config.ai = materializeAIConfig(ai);
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
export async function appendWizardEnvVars(
|
package/src/index.ts
CHANGED
|
@@ -32,6 +32,7 @@ export {
|
|
|
32
32
|
export { configureDatabaseOptions } from './database.js';
|
|
33
33
|
|
|
34
34
|
export {
|
|
35
|
+
AI_STACK_VERSIONS,
|
|
35
36
|
MCP_SDK_VERSION,
|
|
36
37
|
getAIDependencies,
|
|
37
38
|
listAIDependencyNames,
|
|
@@ -41,9 +42,61 @@ export {
|
|
|
41
42
|
getRequiredAIDependenciesForConfig,
|
|
42
43
|
diagnoseAIDependencies,
|
|
43
44
|
formatAIDependencyFixCommand,
|
|
45
|
+
packagesNeedingAiStackFix,
|
|
46
|
+
findOutdatedAiStackInPackageJson,
|
|
47
|
+
findInstalledAiStackIncompatibilities,
|
|
44
48
|
ensureDatabaseForAI,
|
|
45
49
|
ensureDatabaseForAdapters,
|
|
46
50
|
} from './project-deps.js';
|
|
51
|
+
export type { AiStackIncompatibility } from './project-deps.js';
|
|
52
|
+
|
|
53
|
+
export {
|
|
54
|
+
ZHIN_STACK_VERSIONS,
|
|
55
|
+
DEFAULT_CREATE_BOT_HTTP_PORT,
|
|
56
|
+
CREATE_BOT_NPMRC,
|
|
57
|
+
getCreateBotBaseDependencies,
|
|
58
|
+
getCreateBotPnpmConfig,
|
|
59
|
+
getRequiredZhinDependenciesForConfig,
|
|
60
|
+
diagnoseZhinStackDependencies,
|
|
61
|
+
formatZhinStackFixCommand,
|
|
62
|
+
packagesNeedingZhinStackFix,
|
|
63
|
+
findOutdatedZhinStackInPackageJson,
|
|
64
|
+
findInstalledZhinStackIncompatibilities,
|
|
65
|
+
} from './zhin-stack-deps.js';
|
|
66
|
+
export type { ZhinStackDiagnosis, ZhinStackIncompatibility } from './zhin-stack-deps.js';
|
|
67
|
+
|
|
68
|
+
export {
|
|
69
|
+
SPEECH_PACKAGE,
|
|
70
|
+
HTML_RENDERER_PACKAGE,
|
|
71
|
+
ADAPTERS_PREFER_HTML_IMAGE,
|
|
72
|
+
diagnoseOptionalPeers,
|
|
73
|
+
formatOptionalPeerFixCommand,
|
|
74
|
+
getOptionalPeerDependencies,
|
|
75
|
+
} from './optional-peers.js';
|
|
76
|
+
export type { OptionalPeerDiagnosis, OptionalPeersDiagnosis } from './optional-peers.js';
|
|
77
|
+
|
|
78
|
+
export {
|
|
79
|
+
diagnoseUpgradeToL4,
|
|
80
|
+
getUpgradeToL4Dependencies,
|
|
81
|
+
} from './upgrade-l4.js';
|
|
82
|
+
export type { UpgradeToL4Diagnosis } from './upgrade-l4.js';
|
|
83
|
+
|
|
84
|
+
export {
|
|
85
|
+
loadProjectConfig,
|
|
86
|
+
createProjectConfigPlan,
|
|
87
|
+
renderProjectConfigPatch,
|
|
88
|
+
applyProjectConfigPlan,
|
|
89
|
+
migrateAiLegacyConfig,
|
|
90
|
+
diagnoseConsoleConfig,
|
|
91
|
+
applyConsoleConfigFixes,
|
|
92
|
+
} from './project-config-plan.js';
|
|
93
|
+
export type {
|
|
94
|
+
LoadedProjectConfig,
|
|
95
|
+
ProjectConfigFormat,
|
|
96
|
+
ProjectConfigPlan,
|
|
97
|
+
ProjectConfigPlanOptions,
|
|
98
|
+
ConsoleConfigDiagnosis,
|
|
99
|
+
} from './project-config-plan.js';
|
|
47
100
|
|
|
48
101
|
export {
|
|
49
102
|
CONSOLE_HOST_PLUGINS,
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import fs from 'fs-extra';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import type { PackageJsonLike } from './project-deps.js';
|
|
4
|
+
import {
|
|
5
|
+
findMissingPackageDependencies,
|
|
6
|
+
findUnresolvedPackageInstalls,
|
|
7
|
+
} from './project-deps.js';
|
|
8
|
+
|
|
9
|
+
export const SPEECH_PACKAGE = '@zhin.js/speech';
|
|
10
|
+
export const HTML_RENDERER_PACKAGE = '@zhin.js/html-renderer';
|
|
11
|
+
|
|
12
|
+
/** 默认 html/markdown policy 为 image 的 adapter context 名 */
|
|
13
|
+
export const ADAPTERS_PREFER_HTML_IMAGE = new Set([
|
|
14
|
+
'kook',
|
|
15
|
+
'qq',
|
|
16
|
+
'weixin-ilink',
|
|
17
|
+
'email',
|
|
18
|
+
]);
|
|
19
|
+
|
|
20
|
+
export interface OptionalPeerDiagnosis {
|
|
21
|
+
packageName: string;
|
|
22
|
+
required: boolean;
|
|
23
|
+
reason: string;
|
|
24
|
+
missingFromPackageJson: string[];
|
|
25
|
+
notInstalled: string[];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface OptionalPeersDiagnosis {
|
|
29
|
+
speech?: OptionalPeerDiagnosis;
|
|
30
|
+
htmlRenderer?: OptionalPeerDiagnosis;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function readSpeechStrategy(config: Record<string, unknown>): string | undefined {
|
|
34
|
+
const ai = config.ai;
|
|
35
|
+
if (!ai || typeof ai !== 'object' || Array.isArray(ai)) return undefined;
|
|
36
|
+
const multimodal = (ai as Record<string, unknown>).multimodal;
|
|
37
|
+
if (!multimodal || typeof multimodal !== 'object' || Array.isArray(multimodal)) return undefined;
|
|
38
|
+
const audio = (multimodal as Record<string, unknown>).audio;
|
|
39
|
+
if (!audio || typeof audio !== 'object' || Array.isArray(audio)) return undefined;
|
|
40
|
+
const strategy = (audio as Record<string, unknown>).strategy;
|
|
41
|
+
return strategy != null ? String(strategy) : undefined;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function configUsesHtmlImageAdapter(config: Record<string, unknown>): boolean {
|
|
45
|
+
const plugins = Array.isArray(config.plugins)
|
|
46
|
+
? config.plugins.filter((p): p is string => typeof p === 'string')
|
|
47
|
+
: [];
|
|
48
|
+
for (const plugin of plugins) {
|
|
49
|
+
for (const ctx of ADAPTERS_PREFER_HTML_IMAGE) {
|
|
50
|
+
if (plugin.includes(`adapter-${ctx}`)) return true;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
const endpoints = Array.isArray(config.endpoints) ? config.endpoints : [];
|
|
54
|
+
for (const ep of endpoints) {
|
|
55
|
+
if (!ep || typeof ep !== 'object' || Array.isArray(ep)) continue;
|
|
56
|
+
const context = (ep as Record<string, unknown>).context;
|
|
57
|
+
if (typeof context === 'string' && ADAPTERS_PREFER_HTML_IMAGE.has(context)) {
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function diagnosePeer(
|
|
65
|
+
cwd: string,
|
|
66
|
+
pkg: PackageJsonLike,
|
|
67
|
+
packageName: string,
|
|
68
|
+
required: boolean,
|
|
69
|
+
reason: string,
|
|
70
|
+
): OptionalPeerDiagnosis | undefined {
|
|
71
|
+
if (!required) return undefined;
|
|
72
|
+
const requiredDeps = {
|
|
73
|
+
[packageName]: 'latest',
|
|
74
|
+
};
|
|
75
|
+
const missingFromPackageJson = findMissingPackageDependencies(pkg, requiredDeps);
|
|
76
|
+
const declared = [packageName].filter((n) => !missingFromPackageJson.includes(n));
|
|
77
|
+
const notInstalled = findUnresolvedPackageInstalls(cwd, declared);
|
|
78
|
+
return {
|
|
79
|
+
packageName,
|
|
80
|
+
required: true,
|
|
81
|
+
reason,
|
|
82
|
+
missingFromPackageJson,
|
|
83
|
+
notInstalled,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** 按 zhin.config 推断 optional peer(speech / html-renderer)是否应安装 */
|
|
88
|
+
export function diagnoseOptionalPeers(
|
|
89
|
+
cwd: string,
|
|
90
|
+
config: Record<string, unknown>,
|
|
91
|
+
pkg?: PackageJsonLike,
|
|
92
|
+
): OptionalPeersDiagnosis {
|
|
93
|
+
const packageJsonPath = path.join(cwd, 'package.json');
|
|
94
|
+
const packageJson = pkg ?? (fs.existsSync(packageJsonPath)
|
|
95
|
+
? fs.readJsonSync(packageJsonPath) as PackageJsonLike
|
|
96
|
+
: {});
|
|
97
|
+
|
|
98
|
+
const speechRequired =
|
|
99
|
+
config.speech != null && typeof config.speech === 'object'
|
|
100
|
+
|| readSpeechStrategy(config) === 'transcribe';
|
|
101
|
+
|
|
102
|
+
const htmlRendererRequired =
|
|
103
|
+
config.htmlRenderer != null && typeof config.htmlRenderer === 'object'
|
|
104
|
+
|| configUsesHtmlImageAdapter(config);
|
|
105
|
+
|
|
106
|
+
const speech = diagnosePeer(
|
|
107
|
+
cwd,
|
|
108
|
+
packageJson,
|
|
109
|
+
SPEECH_PACKAGE,
|
|
110
|
+
speechRequired,
|
|
111
|
+
speechRequired
|
|
112
|
+
? (readSpeechStrategy(config) === 'transcribe'
|
|
113
|
+
? 'ai.multimodal.audio.strategy 为 transcribe'
|
|
114
|
+
: '已配置 speech:')
|
|
115
|
+
: '',
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
const htmlRenderer = diagnosePeer(
|
|
119
|
+
cwd,
|
|
120
|
+
packageJson,
|
|
121
|
+
HTML_RENDERER_PACKAGE,
|
|
122
|
+
htmlRendererRequired,
|
|
123
|
+
htmlRendererRequired
|
|
124
|
+
? (config.htmlRenderer != null
|
|
125
|
+
? '已配置 htmlRenderer:'
|
|
126
|
+
: 'adapter 出站 policy 使用 html/markdown→image')
|
|
127
|
+
: '',
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
return { speech, htmlRenderer };
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export function formatOptionalPeerFixCommand(diagnosis: OptionalPeerDiagnosis): string {
|
|
134
|
+
const missing = [
|
|
135
|
+
...diagnosis.missingFromPackageJson,
|
|
136
|
+
...diagnosis.notInstalled.filter((n) => !diagnosis.missingFromPackageJson.includes(n)),
|
|
137
|
+
];
|
|
138
|
+
const unique = [...new Set(missing)];
|
|
139
|
+
if (unique.length === 0) return 'pnpm install';
|
|
140
|
+
return `pnpm add ${unique.map((n) => `${n}@latest`).join(' ')}`;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export function getOptionalPeerDependencies(config: Record<string, unknown>): Record<string, string> {
|
|
144
|
+
const peers = diagnoseOptionalPeers(process.cwd(), config);
|
|
145
|
+
const deps: Record<string, string> = {};
|
|
146
|
+
if (peers.speech?.required) deps[SPEECH_PACKAGE] = 'latest';
|
|
147
|
+
if (peers.htmlRenderer?.required) deps[HTML_RENDERER_PACKAGE] = 'latest';
|
|
148
|
+
return deps;
|
|
149
|
+
}
|