chati-dev 4.5.13 → 4.5.15
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/framework/agents/build/dev.md +4 -5
- package/framework/agents/deploy/devops.md +9 -10
- package/framework/agents/discover/brief.md +7 -13
- package/framework/agents/discover/brownfield-wu.md +5 -11
- package/framework/agents/discover/greenfield-wu.md +7 -12
- package/framework/agents/plan/architect.md +1 -1
- package/framework/agents/plan/detail.md +5 -5
- package/framework/agents/plan/phases.md +4 -5
- package/framework/agents/plan/tasks.md +4 -5
- package/framework/agents/plan/ux-brand-architect.md +1 -1
- package/framework/agents/plan/ux.md +1 -1
- package/framework/agents/quality/qa-implementation.md +4 -5
- package/framework/agents/quality/qa-planning.md +4 -5
- package/framework/agents/quality/qa-visual.md +4 -4
- package/framework/agents/shared/visualizer.md +1 -1
- package/framework/config.yaml +5 -5
- package/framework/constitution.md +24 -26
- package/framework/context/governance.md +6 -6
- package/framework/context/root.md +4 -3
- package/framework/data/entity-registry.yaml +3 -3
- package/framework/data/model-limits.json +5 -2
- package/framework/hooks/model-governance.js +3 -2
- package/framework/hooks/prism-engine.js +1 -1
- package/framework/i18n/en.yaml +6 -2
- package/framework/i18n/es.yaml +6 -2
- package/framework/i18n/fr.yaml +6 -2
- package/framework/i18n/pt.yaml +6 -2
- package/framework/intelligence/context-engine.md +12 -16
- package/framework/manifest.json +63 -63
- package/framework/manifest.sig +1 -1
- package/framework/orchestrator/chati-router.js +40 -6
- package/framework/orchestrator/chati.md +12 -13
- package/framework/schemas/session.schema.json +1 -1
- package/package.json +1 -1
- package/src/config/context-file-generator.js +54 -246
- package/src/config/framework-adapter.js +45 -208
- package/src/context/bracket-tracker.js +3 -3
- package/src/context/engine.js +1 -1
- package/src/installer/core.js +190 -66
- package/src/installer/provider-overlay.js +1 -1
- package/src/installer/templates.js +55 -65
- package/src/installer-v2/index.js +2 -0
- package/src/memory/magic-docs.js +45 -26
- package/src/orchestrator/cli.js +50 -45
- package/src/orchestrator/handoff-engine.js +7 -5
- package/src/orchestrator/session-manager.js +24 -3
- package/src/terminal/cli-registry.js +5 -5
- package/src/terminal/prompt-builder.js +14 -10
- package/src/terminal/run-agent.js +10 -6
- package/src/terminal/run-parallel.js +6 -2
- package/src/terminal/run-team.js +7 -2
- package/src/terminal/spawner.js +34 -42
- package/src/utils/config-parser.js +2 -2
- package/src/utils/provider-limits.js +3 -1
- package/src/wizard/feedback.js +11 -0
- package/src/wizard/i18n.js +6 -2
- package/src/wizard/index.js +17 -7
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* CLI runner for single-agent terminal execution.
|
|
4
4
|
*
|
|
5
5
|
* Called by the orchestrator via the Bash tool to spawn an agent
|
|
6
|
-
* in a separate
|
|
6
|
+
* in a separate routed provider process with the exact model binding.
|
|
7
7
|
*
|
|
8
8
|
* Usage:
|
|
9
9
|
* node run-agent.js --agent detail --task-id expand-prd \
|
|
@@ -104,7 +104,11 @@ async function main() {
|
|
|
104
104
|
}
|
|
105
105
|
|
|
106
106
|
// Wait for rate limit slot before spawning
|
|
107
|
-
const spawnProvider = promptResult.provider || args.provider
|
|
107
|
+
const spawnProvider = promptResult.provider || args.provider;
|
|
108
|
+
if (!spawnProvider) {
|
|
109
|
+
outputError('No routed provider was supplied for agent execution');
|
|
110
|
+
process.exit(1);
|
|
111
|
+
}
|
|
108
112
|
const readiness = checkProviderReadiness(spawnProvider, { workingDir: projectDir });
|
|
109
113
|
if (!readiness.ready) {
|
|
110
114
|
outputResult({ status: 'error', error: readiness.detail, code: readiness.code, provider: spawnProvider });
|
|
@@ -164,7 +168,7 @@ async function main() {
|
|
|
164
168
|
const costRecord = tracker.recordExecution({
|
|
165
169
|
agent: args.agent,
|
|
166
170
|
model: promptResult.model,
|
|
167
|
-
provider:
|
|
171
|
+
provider: spawnProvider,
|
|
168
172
|
taskId: args['task-id'],
|
|
169
173
|
inputText: promptResult.prompt || '',
|
|
170
174
|
outputText: stdout,
|
|
@@ -227,7 +231,7 @@ async function main() {
|
|
|
227
231
|
status: parsed.handoff.status,
|
|
228
232
|
agent: args.agent,
|
|
229
233
|
model: promptResult.model,
|
|
230
|
-
provider:
|
|
234
|
+
provider: spawnProvider,
|
|
231
235
|
reasoningConfiguration: args['reasoning-configuration'] || null,
|
|
232
236
|
exitCode: handle.exitCode,
|
|
233
237
|
handoff: parsed.handoff,
|
|
@@ -242,7 +246,7 @@ async function main() {
|
|
|
242
246
|
status: 'needs_input',
|
|
243
247
|
agent: args.agent,
|
|
244
248
|
model: promptResult.model,
|
|
245
|
-
provider:
|
|
249
|
+
provider: spawnProvider,
|
|
246
250
|
exitCode: handle.exitCode,
|
|
247
251
|
handoff: recoverInteractiveHandoff(args.agent, stdout, handle.exitCode),
|
|
248
252
|
contractRecovery: 'interactive_output_wrapped',
|
|
@@ -257,7 +261,7 @@ async function main() {
|
|
|
257
261
|
code: parsed.found ? 'INVALID_HANDOFF' : 'MISSING_HANDOFF',
|
|
258
262
|
agent: args.agent,
|
|
259
263
|
model: promptResult.model,
|
|
260
|
-
provider:
|
|
264
|
+
provider: spawnProvider,
|
|
261
265
|
exitCode: handle.exitCode,
|
|
262
266
|
handoff: parsed.handoff,
|
|
263
267
|
handoffWarnings: parsed.warnings,
|
|
@@ -138,7 +138,11 @@ async function main() {
|
|
|
138
138
|
}
|
|
139
139
|
|
|
140
140
|
// Check rate limit capacity before spawning
|
|
141
|
-
const groupProvider = configs[0]?.provider
|
|
141
|
+
const groupProvider = configs[0]?.provider;
|
|
142
|
+
if (!groupProvider) {
|
|
143
|
+
outputError('No routed provider was supplied for parallel execution');
|
|
144
|
+
process.exit(1);
|
|
145
|
+
}
|
|
142
146
|
const limiter = getRateLimiter(groupProvider);
|
|
143
147
|
const rateStats = limiter.getStats();
|
|
144
148
|
const availableSlots = rateStats.limit - rateStats.used;
|
|
@@ -197,7 +201,7 @@ async function main() {
|
|
|
197
201
|
return {
|
|
198
202
|
agent: cfg.agent,
|
|
199
203
|
model: modelKey,
|
|
200
|
-
provider: cfg.provider || '
|
|
204
|
+
provider: cfg.provider || 'unbound',
|
|
201
205
|
inputTokens,
|
|
202
206
|
outputTokens,
|
|
203
207
|
estimatedCost: ((inputTokens + outputTokens) / 1000) * rate,
|
package/src/terminal/run-team.js
CHANGED
|
@@ -86,7 +86,7 @@ async function main() {
|
|
|
86
86
|
const teamType = args['team-type'];
|
|
87
87
|
const projectDir = args['project-dir'] || process.cwd();
|
|
88
88
|
const previousAgent = args['previous-agent'] || null;
|
|
89
|
-
const provider = args.provider ||
|
|
89
|
+
const provider = args.provider || null;
|
|
90
90
|
const timeout = parseInt(args.timeout, 10) || 1_800_000; // default 30 minutes
|
|
91
91
|
|
|
92
92
|
if (!teamId || !teamType) {
|
|
@@ -94,6 +94,11 @@ async function main() {
|
|
|
94
94
|
process.exit(1);
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
+
if (!provider) {
|
|
98
|
+
outputError('Missing required argument: --provider');
|
|
99
|
+
process.exit(1);
|
|
100
|
+
}
|
|
101
|
+
|
|
97
102
|
const members = TEAM_MEMBERS[teamType];
|
|
98
103
|
const taskIds = TEAM_TASK_IDS[teamType];
|
|
99
104
|
|
|
@@ -248,7 +253,7 @@ async function main() {
|
|
|
248
253
|
return {
|
|
249
254
|
agent: cfg.agent,
|
|
250
255
|
model: modelKey,
|
|
251
|
-
provider: cfg.provider
|
|
256
|
+
provider: cfg.provider,
|
|
252
257
|
inputTokens,
|
|
253
258
|
outputTokens,
|
|
254
259
|
estimatedCost: ((inputTokens + outputTokens) / 1000) * rate,
|
package/src/terminal/spawner.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @fileoverview Terminal spawner for multi-agent parallel execution.
|
|
3
3
|
*
|
|
4
|
-
* Spawns separate
|
|
4
|
+
* Spawns separate provider CLI processes so that multiple agents
|
|
5
5
|
* can work concurrently. The heavy lifting is split into pure,
|
|
6
6
|
* testable helpers (buildSpawnCommand) and a thin runtime layer
|
|
7
7
|
* (spawnTerminal) that actually calls child_process.spawn.
|
|
@@ -112,7 +112,6 @@ export function cleanParentEnv(env) {
|
|
|
112
112
|
* @property {string} [providerId] - Vendor provider identifier (anthropic, openai, xai)
|
|
113
113
|
* @property {string} [reasoningConfiguration] - Requested reasoning effort
|
|
114
114
|
* @property {string} [catalogSnapshotRef] - Capability catalog snapshot used for routing
|
|
115
|
-
* @property {boolean} [strictProvider] - Reject a missing provider rather than using the legacy fallback
|
|
116
115
|
* @property {string} [prompt] - Full prompt string (from prompt-builder, piped via stdin)
|
|
117
116
|
* @property {object} [contextPayload] - Context to inject via env var
|
|
118
117
|
* @property {string[]} [writeScope] - Override write scope
|
|
@@ -137,7 +136,7 @@ export function cleanParentEnv(env) {
|
|
|
137
136
|
|
|
138
137
|
/**
|
|
139
138
|
* Build the CLI command, arguments and environment for spawning a
|
|
140
|
-
*
|
|
139
|
+
* provider CLI terminal. This is a **pure function** -- it does not
|
|
141
140
|
* perform any I/O and is therefore fully testable in isolation.
|
|
142
141
|
*
|
|
143
142
|
* @param {SpawnConfig} config
|
|
@@ -153,6 +152,11 @@ export function buildSpawnCommand(config) {
|
|
|
153
152
|
if (!config.taskId || typeof config.taskId !== 'string') {
|
|
154
153
|
throw new Error('config.taskId is required and must be a string');
|
|
155
154
|
}
|
|
155
|
+
if (!config.provider || typeof config.provider !== 'string') {
|
|
156
|
+
const failure = new Error('config.provider is required and must be a string');
|
|
157
|
+
failure.code = 'PROVIDER_REQUIRED';
|
|
158
|
+
throw failure;
|
|
159
|
+
}
|
|
156
160
|
|
|
157
161
|
const terminalId = generateTerminalId(config.agent);
|
|
158
162
|
const isolationEnv = buildIsolationEnv(config.agent);
|
|
@@ -175,13 +179,14 @@ export function buildSpawnCommand(config) {
|
|
|
175
179
|
}
|
|
176
180
|
}
|
|
177
181
|
|
|
178
|
-
// Resolve
|
|
179
|
-
|
|
182
|
+
// Resolve the exact provider selected by the router. Never substitute a
|
|
183
|
+
// different vendor when credentials, catalog data, or a CLI are missing.
|
|
184
|
+
const providerName = config.provider;
|
|
180
185
|
let command, args, prompt;
|
|
181
|
-
|
|
182
|
-
|
|
186
|
+
const providerFallback = null;
|
|
187
|
+
const actualProvider = providerName;
|
|
183
188
|
let actualModel;
|
|
184
|
-
let actualReasoningConfiguration
|
|
189
|
+
let actualReasoningConfiguration;
|
|
185
190
|
|
|
186
191
|
try {
|
|
187
192
|
const provider = getProvider(providerName);
|
|
@@ -194,32 +199,9 @@ export function buildSpawnCommand(config) {
|
|
|
194
199
|
actualModel = adapterResult.effectiveModel;
|
|
195
200
|
actualReasoningConfiguration = adapterResult.effectiveReasoningConfiguration;
|
|
196
201
|
} catch (err) {
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
throw failure;
|
|
201
|
-
}
|
|
202
|
-
// Fallback to claude if provider resolution fails (backwards compatibility)
|
|
203
|
-
providerFallback = {
|
|
204
|
-
requested: providerName,
|
|
205
|
-
actual: 'claude',
|
|
206
|
-
reason: err.message,
|
|
207
|
-
timestamp: new Date().toISOString(),
|
|
208
|
-
};
|
|
209
|
-
actualProvider = 'claude';
|
|
210
|
-
console.error(`[chati] Provider "${providerName}" resolution failed: ${err.message}. Falling back to claude.`);
|
|
211
|
-
command = 'claude';
|
|
212
|
-
args = ['--print', '--dangerously-skip-permissions'];
|
|
213
|
-
const claudeProvider = getProvider('claude');
|
|
214
|
-
const requestedClaudeModel = config.model || 'sonnet';
|
|
215
|
-
actualModel = claudeProvider.modelMap[requestedClaudeModel]
|
|
216
|
-
|| (requestedClaudeModel.startsWith('claude-') ? requestedClaudeModel : claudeProvider.modelMap.sonnet);
|
|
217
|
-
args.push('--model', actualModel);
|
|
218
|
-
if (config.reasoningConfiguration) {
|
|
219
|
-
args.push('--effort', config.reasoningConfiguration);
|
|
220
|
-
actualReasoningConfiguration = config.reasoningConfiguration;
|
|
221
|
-
}
|
|
222
|
-
prompt = config.prompt || null;
|
|
202
|
+
const failure = new Error(`Selected provider "${providerName}" is unavailable: ${err.message}`);
|
|
203
|
+
failure.code = 'SELECTED_PROVIDER_UNAVAILABLE';
|
|
204
|
+
throw failure;
|
|
223
205
|
}
|
|
224
206
|
|
|
225
207
|
return {
|
|
@@ -247,10 +229,10 @@ export function spawnTerminal(config) {
|
|
|
247
229
|
agent: config.agent,
|
|
248
230
|
taskId: config.taskId,
|
|
249
231
|
provider: actualProvider,
|
|
250
|
-
providerId:
|
|
232
|
+
providerId: config.providerId || null,
|
|
251
233
|
model: actualModel,
|
|
252
234
|
reasoningConfiguration: actualReasoningConfiguration,
|
|
253
|
-
catalogSnapshotRef:
|
|
235
|
+
catalogSnapshotRef: config.catalogSnapshotRef || null,
|
|
254
236
|
});
|
|
255
237
|
if (!selectionRecord.saved) {
|
|
256
238
|
const auditError = new Error(selectionRecord.error || 'Failed to persist model selection audit');
|
|
@@ -355,12 +337,22 @@ export function spawnParallelGroup(configs) {
|
|
|
355
337
|
throw new Error(`Write scope conflicts detected: ${details}`);
|
|
356
338
|
}
|
|
357
339
|
|
|
358
|
-
// Preemptive
|
|
359
|
-
|
|
360
|
-
const
|
|
361
|
-
const
|
|
362
|
-
|
|
363
|
-
|
|
340
|
+
// Preemptive capacity check per provider. A parallel group may contain
|
|
341
|
+
// routed tasks from multiple vendors.
|
|
342
|
+
const providerCounts = new Map();
|
|
343
|
+
for (const config of configs) {
|
|
344
|
+
if (!config?.provider || typeof config.provider !== 'string') {
|
|
345
|
+
const failure = new Error('Every parallel config requires an explicit provider');
|
|
346
|
+
failure.code = 'PROVIDER_REQUIRED';
|
|
347
|
+
throw failure;
|
|
348
|
+
}
|
|
349
|
+
providerCounts.set(config.provider, (providerCounts.get(config.provider) || 0) + 1);
|
|
350
|
+
}
|
|
351
|
+
for (const [provider, requested] of providerCounts) {
|
|
352
|
+
const stats = getRateLimiter(provider).getStats();
|
|
353
|
+
if (stats.used + requested > stats.limit) {
|
|
354
|
+
console.error(`[chati] Rate limit warning for ${provider}: ${stats.used}/${stats.limit} slots used, requesting ${requested} more`);
|
|
355
|
+
}
|
|
364
356
|
}
|
|
365
357
|
|
|
366
358
|
const groupId = `group-${Date.now()}`;
|
|
@@ -14,12 +14,12 @@ import { resolveFrameworkDir } from './framework-dir.js';
|
|
|
14
14
|
* Parse provider configuration from config.yaml.
|
|
15
15
|
*
|
|
16
16
|
* @param {string} projectDir - Project root directory
|
|
17
|
-
* @returns {{ primary: string, enabled: string[], raw: string|null }}
|
|
17
|
+
* @returns {{ primary: string|null, enabled: string[], raw: string|null }}
|
|
18
18
|
*/
|
|
19
19
|
export function parseProviderConfig(projectDir) {
|
|
20
20
|
const configPath = join(projectDir, resolveFrameworkDir(projectDir), 'config.yaml');
|
|
21
21
|
if (!existsSync(configPath)) {
|
|
22
|
-
return { primary:
|
|
22
|
+
return { primary: null, enabled: [], raw: null };
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
const raw = readFileSync(configPath, 'utf-8');
|
|
@@ -38,7 +38,7 @@ function loadLimits() {
|
|
|
38
38
|
// branch exists so unit tests that run the module without sync still work.
|
|
39
39
|
// 2026-04-18 (context-window-auto-detect-v2): opus family is 1M by default.
|
|
40
40
|
return {
|
|
41
|
-
providers: { claude: 1_000_000, gemini: 1_000_000, codex: 128_000 },
|
|
41
|
+
providers: { claude: 1_000_000, gemini: 1_000_000, codex: 128_000, grok: 200_000 },
|
|
42
42
|
models: {
|
|
43
43
|
opus: 1_000_000,
|
|
44
44
|
'opus[1m]': 1_000_000,
|
|
@@ -48,6 +48,8 @@ function loadLimits() {
|
|
|
48
48
|
pro: 1_000_000,
|
|
49
49
|
flash: 1_000_000,
|
|
50
50
|
codex: 128_000,
|
|
51
|
+
'grok-4.5': 200_000,
|
|
52
|
+
'grok-4.6': 200_000,
|
|
51
53
|
},
|
|
52
54
|
};
|
|
53
55
|
}
|
package/src/wizard/feedback.js
CHANGED
|
@@ -13,6 +13,17 @@ export function createSpinner(text) {
|
|
|
13
13
|
});
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
+
/** Run one automatic operation with a spinner that always stops. */
|
|
17
|
+
export async function withSpinner(text, operation, { spinnerFactory = createSpinner } = {}) {
|
|
18
|
+
const spinner = spinnerFactory(text);
|
|
19
|
+
spinner.start();
|
|
20
|
+
try {
|
|
21
|
+
return await operation();
|
|
22
|
+
} finally {
|
|
23
|
+
spinner.stop();
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
16
27
|
/**
|
|
17
28
|
* Show installation progress step
|
|
18
29
|
*/
|
package/src/wizard/i18n.js
CHANGED
|
@@ -75,7 +75,7 @@ const FALLBACK_EN = {
|
|
|
75
75
|
created_commands: 'Created .claude/commands/ (thin router)',
|
|
76
76
|
installed_constitution: 'Installed Constitution (Articles I-XXV)',
|
|
77
77
|
created_session: 'Created session.yaml schema',
|
|
78
|
-
created_claude_md: 'Created
|
|
78
|
+
created_claude_md: 'Created provider-neutral context and native harness files',
|
|
79
79
|
configured_mcps: 'Configured MCPs:',
|
|
80
80
|
validating: 'Validating installation...',
|
|
81
81
|
agents_valid: 'All agents implement 8 protocols',
|
|
@@ -106,6 +106,9 @@ const FALLBACK_EN = {
|
|
|
106
106
|
system_prerequisites_failed: 'System prerequisites not met:',
|
|
107
107
|
fix_prerequisites: 'Fix the issues above and try again.',
|
|
108
108
|
system_check: 'System check:',
|
|
109
|
+
system_checking: 'Checking system requirements...',
|
|
110
|
+
catalog_resolving: 'Loading the signed model catalog...',
|
|
111
|
+
internal_authorizing: 'Verifying access to Internal mode...',
|
|
109
112
|
created_provider_claude: 'Created .claude/commands/ (thin router)',
|
|
110
113
|
created_provider_gemini: 'Created .gemini/commands/ (TOML command)',
|
|
111
114
|
created_provider_codex: 'Created .agents/skills/chati/ (Codex skill)',
|
|
@@ -120,6 +123,7 @@ const FALLBACK_EN = {
|
|
|
120
123
|
license_placeholder: 'CHATI-XXXX-XXXX-XXXX',
|
|
121
124
|
license_skipped: 'Skipped. Run: npx chati-dev activate --key=YOUR-KEY when ready.',
|
|
122
125
|
license_activating: 'Activating license...',
|
|
126
|
+
license_verifying: 'Verifying saved license...',
|
|
123
127
|
license_activated: 'License activated! Plan: {plan}, {days} day(s) remaining.',
|
|
124
128
|
license_activation_failed: 'Activation failed: {error}',
|
|
125
129
|
license_retry: 'Run: npx chati-dev activate --key=YOUR-KEY to try again.',
|
|
@@ -153,7 +157,7 @@ const FALLBACK_EN = {
|
|
|
153
157
|
},
|
|
154
158
|
errors: {
|
|
155
159
|
session_corrupted: 'Session file appears corrupted. Attempting recovery...',
|
|
156
|
-
handoff_missing: 'Handoff not found. Using session.yaml +
|
|
160
|
+
handoff_missing: 'Handoff not found. Using session.yaml + .chati/project-context.md as fallback.',
|
|
157
161
|
agent_failed: 'Agent failed after 3 attempts. Escalating to user.',
|
|
158
162
|
mcp_required: "Required MCP '{mcp}' is not configured. Installation instructions:",
|
|
159
163
|
mcp_optional: "Optional MCP '{mcp}' not configured. Skipping related functionality.",
|
package/src/wizard/index.js
CHANGED
|
@@ -4,7 +4,7 @@ import { join, dirname, basename } from 'path';
|
|
|
4
4
|
import { fileURLToPath } from 'url';
|
|
5
5
|
import { logBanner } from '../utils/logger.js';
|
|
6
6
|
import { WIZARD_BACK, stepLanguage, stepProjectType, stepProviderSelection, stepInstallationMode, stepConfirmation, stepTermsOfUse } from './questions.js';
|
|
7
|
-
import { createSpinner, showStep, showValidation, showQuickStart } from './feedback.js';
|
|
7
|
+
import { createSpinner, withSpinner, showStep, showValidation, showQuickStart } from './feedback.js';
|
|
8
8
|
import { installFramework } from '../installer/core.js';
|
|
9
9
|
import { INSTALLATION_ARTIFACT_PATH, dryRunV2, installV2, reconfigureV2 } from '../installer-v2/index.js';
|
|
10
10
|
import { buildWizardV2InstallationInput } from '../installer-v2/wizard-installation.js';
|
|
@@ -48,7 +48,7 @@ export async function runWizard(targetDir, options = {}) {
|
|
|
48
48
|
|
|
49
49
|
// Preflight: validate system prerequisites (Node >= 20, npm, Git, Playwright)
|
|
50
50
|
const { runPreflightCheck } = await import('../installer/preflight.js');
|
|
51
|
-
const preflight = await runPreflightCheck();
|
|
51
|
+
const preflight = await withSpinner(t('installer.system_checking'), () => runPreflightCheck());
|
|
52
52
|
const preflightFmt = preflight.checks.map(c => {
|
|
53
53
|
const icon = c.status === 'pass' ? '✓' : c.status === 'warn' ? '⚠' : '✗';
|
|
54
54
|
return ` ${icon} ${c.message}`;
|
|
@@ -128,9 +128,13 @@ export async function runWizard(targetDir, options = {}) {
|
|
|
128
128
|
continue;
|
|
129
129
|
}
|
|
130
130
|
selectedProviders = value;
|
|
131
|
-
catalogResolution
|
|
132
|
-
|
|
133
|
-
|
|
131
|
+
if (!catalogResolution) {
|
|
132
|
+
catalogResolution = await withSpinner(t('installer.catalog_resolving'), async () => (
|
|
133
|
+
options.signedCapabilityCatalogEnvelope
|
|
134
|
+
? { source: 'provided-signed', catalog: verifySignedCapabilityCatalog(options.signedCapabilityCatalogEnvelope, { publicKeyPem: options.catalogPublicKeyPem }) }
|
|
135
|
+
: resolveCapabilityCatalog({ projectDir: targetDir, catalogUrl: options.catalogUrl, fetchImpl: options.catalogFetch })
|
|
136
|
+
));
|
|
137
|
+
}
|
|
134
138
|
// Provider selection defines the permitted routing pool. Model discovery
|
|
135
139
|
// and task-specific choice stay inside the control plane.
|
|
136
140
|
modelSelections = options.modelSelections;
|
|
@@ -149,7 +153,10 @@ export async function runWizard(targetDir, options = {}) {
|
|
|
149
153
|
installationMode = value;
|
|
150
154
|
if (installationMode === 'internal') {
|
|
151
155
|
try {
|
|
152
|
-
await
|
|
156
|
+
await withSpinner(
|
|
157
|
+
t('installer.internal_authorizing'),
|
|
158
|
+
() => requireLicenseEntitlement('internal', { validate: options.licenseValidator }),
|
|
159
|
+
);
|
|
153
160
|
} catch (error) {
|
|
154
161
|
if (options.installationMode !== undefined) throw error;
|
|
155
162
|
p.log.error(error.message);
|
|
@@ -328,7 +335,10 @@ async function runLicenseActivationStep() {
|
|
|
328
335
|
const existingKey = getLicenseKey();
|
|
329
336
|
if (existingKey) {
|
|
330
337
|
try {
|
|
331
|
-
const status = await
|
|
338
|
+
const status = await withSpinner(
|
|
339
|
+
t('installer.license_verifying'),
|
|
340
|
+
() => ensureCurrentMachineLicense({ key: existingKey }),
|
|
341
|
+
);
|
|
332
342
|
if (status.status === 'VALID') return;
|
|
333
343
|
p.log.warn(t('installer.license_saved_invalid', { reason: status.reason || status.status }));
|
|
334
344
|
} catch (error) {
|