@robbiesrobotics/alice-agents 1.4.0 → 1.4.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.
- package/README.md +1 -1
- package/lib/config-merger.mjs +56 -3
- package/lib/installer.mjs +5 -2
- package/package.json +1 -1
- package/tools/compatibility-checker.mjs +27 -0
package/README.md
CHANGED
|
@@ -74,7 +74,7 @@ When you install, the installer will **auto-detect your configured model** and u
|
|
|
74
74
|
# Interactive install
|
|
75
75
|
npx @robbiesrobotics/alice-agents
|
|
76
76
|
|
|
77
|
-
# Non-interactive with defaults (
|
|
77
|
+
# Non-interactive with defaults (detected model if available, otherwise Sonnet; Starter tier)
|
|
78
78
|
npx @robbiesrobotics/alice-agents --yes
|
|
79
79
|
|
|
80
80
|
# Show help
|
package/lib/config-merger.mjs
CHANGED
|
@@ -6,6 +6,33 @@ import { randomBytes } from 'node:crypto';
|
|
|
6
6
|
const OPENCLAW_DIR = join(homedir(), '.openclaw');
|
|
7
7
|
const CONFIG_PATH = join(OPENCLAW_DIR, 'openclaw.json');
|
|
8
8
|
|
|
9
|
+
function normalizeProviderId(provider) {
|
|
10
|
+
if (!provider) return null;
|
|
11
|
+
if (provider === 'openai-codex') return 'openai';
|
|
12
|
+
return provider;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function getModelProvider(model) {
|
|
16
|
+
if (!model || typeof model !== 'string' || !model.includes('/')) return null;
|
|
17
|
+
return normalizeProviderId(model.split('/')[0]);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function collectConfiguredProviders(config) {
|
|
21
|
+
const providers = new Set();
|
|
22
|
+
|
|
23
|
+
for (const provider of Object.keys(config?.models?.providers || {})) {
|
|
24
|
+
const normalized = normalizeProviderId(provider);
|
|
25
|
+
if (normalized) providers.add(normalized);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
for (const profile of Object.values(config?.auth?.profiles || {})) {
|
|
29
|
+
const normalized = normalizeProviderId(profile?.provider);
|
|
30
|
+
if (normalized) providers.add(normalized);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return [...providers];
|
|
34
|
+
}
|
|
35
|
+
|
|
9
36
|
export function configExists() {
|
|
10
37
|
return existsSync(CONFIG_PATH);
|
|
11
38
|
}
|
|
@@ -34,7 +61,7 @@ export function detectAvailableModels() {
|
|
|
34
61
|
const globalModel = config?.model;
|
|
35
62
|
|
|
36
63
|
// Configured provider keys (e.g. ['anthropic', 'openai', 'ollama'])
|
|
37
|
-
const providers =
|
|
64
|
+
const providers = collectConfiguredProviders(config);
|
|
38
65
|
|
|
39
66
|
const primary = agentDefaults.primary || globalModel || null;
|
|
40
67
|
const orchestrator = agentDefaults.orchestrator || primary;
|
|
@@ -127,6 +154,22 @@ function getModelConfig(preset, customModels, detectedModels) {
|
|
|
127
154
|
}
|
|
128
155
|
}
|
|
129
156
|
|
|
157
|
+
function isCompatibleWithConfiguredProviders(modelCfg, detectedModels) {
|
|
158
|
+
const providers = new Set(detectedModels?.providers || []);
|
|
159
|
+
if (providers.size === 0) return true;
|
|
160
|
+
|
|
161
|
+
const models = [
|
|
162
|
+
modelCfg?.primary,
|
|
163
|
+
modelCfg?.orchestrator,
|
|
164
|
+
...(modelCfg?.fallbacks || []),
|
|
165
|
+
].filter(Boolean);
|
|
166
|
+
|
|
167
|
+
return models.every((model) => {
|
|
168
|
+
const provider = getModelProvider(model);
|
|
169
|
+
return !provider || providers.has(provider);
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
|
|
130
173
|
function buildAgentEntry(agent, modelCfg) {
|
|
131
174
|
const entry = {
|
|
132
175
|
id: agent.id,
|
|
@@ -157,7 +200,17 @@ export function mergeConfig({ agents, mode, preset, customModels }) {
|
|
|
157
200
|
const backupPath = backupConfig();
|
|
158
201
|
const config = readConfig();
|
|
159
202
|
const detectedModels = detectAvailableModels();
|
|
160
|
-
|
|
203
|
+
let effectivePreset = preset;
|
|
204
|
+
let warning = null;
|
|
205
|
+
let modelCfg = getModelConfig(preset, customModels, detectedModels);
|
|
206
|
+
|
|
207
|
+
if (!modelCfg.preserve && !isCompatibleWithConfiguredProviders(modelCfg, detectedModels)) {
|
|
208
|
+
effectivePreset = detectedModels?.hasModel ? 'detected' : preset;
|
|
209
|
+
if (effectivePreset === 'detected') {
|
|
210
|
+
warning = `Preset "${preset}" does not match configured OpenClaw providers (${(detectedModels.providers || []).join(', ')}). Preserving existing model settings instead.`;
|
|
211
|
+
modelCfg = getModelConfig('detected', customModels, detectedModels);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
161
214
|
|
|
162
215
|
// Build agent entries
|
|
163
216
|
const aliceEntries = agents.map((a) => buildAgentEntry(a, modelCfg));
|
|
@@ -221,7 +274,7 @@ export function mergeConfig({ agents, mode, preset, customModels }) {
|
|
|
221
274
|
config.tools.agentToAgent.allow = [...aliceIds];
|
|
222
275
|
|
|
223
276
|
writeConfigAtomic(config);
|
|
224
|
-
return { backupPath, agentCount: aliceEntries.length };
|
|
277
|
+
return { backupPath, agentCount: aliceEntries.length, effectivePreset, warning };
|
|
225
278
|
}
|
|
226
279
|
|
|
227
280
|
export function removeAliceAgents(agentIds) {
|
package/lib/installer.mjs
CHANGED
|
@@ -665,13 +665,16 @@ export async function runInstall(options = {}) {
|
|
|
665
665
|
console.log('');
|
|
666
666
|
|
|
667
667
|
// Merge config
|
|
668
|
-
const { backupPath, agentCount } = mergeConfig({
|
|
668
|
+
const { backupPath, agentCount, effectivePreset, warning } = mergeConfig({
|
|
669
669
|
agents,
|
|
670
670
|
mode,
|
|
671
671
|
preset,
|
|
672
672
|
customModels,
|
|
673
673
|
});
|
|
674
674
|
printStepDone('Config updated', `backup: ${backupPath}`);
|
|
675
|
+
if (warning) {
|
|
676
|
+
console.log(` ${icons.warn} ${yellow(warning)}`);
|
|
677
|
+
}
|
|
675
678
|
|
|
676
679
|
// Scaffold workspaces
|
|
677
680
|
const results = scaffoldAll(agents, userInfo);
|
|
@@ -702,7 +705,7 @@ export async function runInstall(options = {}) {
|
|
|
702
705
|
agents: agents.map((a) => a.id),
|
|
703
706
|
userName: userInfo.name,
|
|
704
707
|
userTimezone: userInfo.timezone,
|
|
705
|
-
modelPreset:
|
|
708
|
+
modelPreset: effectivePreset,
|
|
706
709
|
});
|
|
707
710
|
printStepDone('Manifest written');
|
|
708
711
|
|
package/package.json
CHANGED
|
@@ -89,6 +89,33 @@ function diffConfigSchema(snapshot, liveConfig) {
|
|
|
89
89
|
});
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
+
const configuredProviders = new Set([
|
|
93
|
+
...Object.keys(liveConfig?.models?.providers || {}),
|
|
94
|
+
...Object.values(liveConfig?.auth?.profiles || {}).map((profile) => profile?.provider),
|
|
95
|
+
].filter(Boolean).map((provider) => provider === 'openai-codex' ? 'openai' : provider));
|
|
96
|
+
|
|
97
|
+
const referencedModels = [
|
|
98
|
+
defaults?.model?.primary,
|
|
99
|
+
defaults?.model?.orchestrator,
|
|
100
|
+
...(defaults?.model?.fallbacks || []),
|
|
101
|
+
...agents.map((agent) => agent?.model).filter(Boolean),
|
|
102
|
+
].filter(Boolean);
|
|
103
|
+
|
|
104
|
+
for (const model of referencedModels) {
|
|
105
|
+
const provider = typeof model === 'string' && model.includes('/') ? model.split('/')[0] : null;
|
|
106
|
+
const normalized = provider === 'openai-codex' ? 'openai' : provider;
|
|
107
|
+
if (normalized && configuredProviders.size > 0 && !configuredProviders.has(normalized)) {
|
|
108
|
+
changes.push({
|
|
109
|
+
category: 'config',
|
|
110
|
+
severity: 'high',
|
|
111
|
+
field: 'agents.defaults.model',
|
|
112
|
+
change: `Model '${model}' references provider '${normalized}', but that provider is not configured in OpenClaw auth/models`,
|
|
113
|
+
autoFixable: false
|
|
114
|
+
});
|
|
115
|
+
break;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
92
119
|
return changes;
|
|
93
120
|
}
|
|
94
121
|
|