infinicode 1.0.0 → 2.1.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/.opencode/plugins/home-logo-animation.tsx +120 -0
- package/.opencode/plugins/routing-mode-display.tsx +135 -0
- package/.opencode/themes/infinibot-gold.json +222 -0
- package/.opencode/tui.json +8 -0
- package/README.md +564 -72
- package/dist/ascii-video-animation.d.ts +2 -0
- package/dist/ascii-video-animation.js +243 -0
- package/dist/cli.js +199 -50
- package/dist/commands/console.d.ts +6 -0
- package/dist/commands/console.js +111 -0
- package/dist/commands/kernel-setup.d.ts +3 -0
- package/dist/commands/kernel-setup.js +303 -0
- package/dist/commands/mcp.d.ts +14 -0
- package/dist/commands/mcp.js +100 -0
- package/dist/commands/mission.d.ts +16 -0
- package/dist/commands/mission.js +301 -0
- package/dist/commands/models.d.ts +3 -1
- package/dist/commands/models.js +111 -55
- package/dist/commands/providers.d.ts +6 -0
- package/dist/commands/providers.js +95 -0
- package/dist/commands/run.d.ts +2 -1
- package/dist/commands/run.js +349 -59
- package/dist/commands/serve.d.ts +20 -0
- package/dist/commands/serve.js +132 -0
- package/dist/commands/setup.d.ts +1 -1
- package/dist/commands/setup.js +77 -44
- package/dist/commands/status.d.ts +2 -1
- package/dist/commands/status.js +46 -30
- package/dist/commands/workers.d.ts +5 -0
- package/dist/commands/workers.js +103 -0
- package/dist/kernel/autonomy/goal-loop.d.ts +49 -0
- package/dist/kernel/autonomy/goal-loop.js +113 -0
- package/dist/kernel/autonomy/index.d.ts +8 -0
- package/dist/kernel/autonomy/index.js +7 -0
- package/dist/kernel/browser/browser-controller.d.ts +57 -0
- package/dist/kernel/browser/browser-controller.js +175 -0
- package/dist/kernel/checkpoint-engine.d.ts +17 -0
- package/dist/kernel/checkpoint-engine.js +128 -0
- package/dist/kernel/command-system.d.ts +37 -0
- package/dist/kernel/command-system.js +239 -0
- package/dist/kernel/config-schema.d.ts +57 -0
- package/dist/kernel/config-schema.js +36 -0
- package/dist/kernel/event-bus.d.ts +19 -0
- package/dist/kernel/event-bus.js +65 -0
- package/dist/kernel/federation/auto-update.d.ts +36 -0
- package/dist/kernel/federation/auto-update.js +57 -0
- package/dist/kernel/federation/compute-router.d.ts +27 -0
- package/dist/kernel/federation/compute-router.js +44 -0
- package/dist/kernel/federation/config-sync.d.ts +33 -0
- package/dist/kernel/federation/config-sync.js +37 -0
- package/dist/kernel/federation/discovery.d.ts +11 -0
- package/dist/kernel/federation/discovery.js +44 -0
- package/dist/kernel/federation/event-bridge.d.ts +30 -0
- package/dist/kernel/federation/event-bridge.js +61 -0
- package/dist/kernel/federation/federation-plugin.d.ts +24 -0
- package/dist/kernel/federation/federation-plugin.js +35 -0
- package/dist/kernel/federation/federation.d.ts +135 -0
- package/dist/kernel/federation/federation.js +378 -0
- package/dist/kernel/federation/index.d.ts +33 -0
- package/dist/kernel/federation/index.js +24 -0
- package/dist/kernel/federation/lan-discovery.d.ts +43 -0
- package/dist/kernel/federation/lan-discovery.js +135 -0
- package/dist/kernel/federation/moltfed-adapter.d.ts +30 -0
- package/dist/kernel/federation/moltfed-adapter.js +52 -0
- package/dist/kernel/federation/moltfed-connector.d.ts +98 -0
- package/dist/kernel/federation/moltfed-connector.js +193 -0
- package/dist/kernel/federation/node-identity.d.ts +19 -0
- package/dist/kernel/federation/node-identity.js +86 -0
- package/dist/kernel/federation/peer-mesh.d.ts +46 -0
- package/dist/kernel/federation/peer-mesh.js +117 -0
- package/dist/kernel/federation/protocol.d.ts +20 -0
- package/dist/kernel/federation/protocol.js +61 -0
- package/dist/kernel/federation/role-context.d.ts +5 -0
- package/dist/kernel/federation/role-context.js +45 -0
- package/dist/kernel/federation/telemetry.d.ts +21 -0
- package/dist/kernel/federation/telemetry.js +138 -0
- package/dist/kernel/federation/transport-http.d.ts +44 -0
- package/dist/kernel/federation/transport-http.js +207 -0
- package/dist/kernel/federation/types.d.ts +212 -0
- package/dist/kernel/federation/types.js +3 -0
- package/dist/kernel/free-providers.d.ts +28 -0
- package/dist/kernel/free-providers.js +162 -0
- package/dist/kernel/frontend-scoring.d.ts +21 -0
- package/dist/kernel/frontend-scoring.js +125 -0
- package/dist/kernel/index.d.ts +63 -0
- package/dist/kernel/index.js +45 -0
- package/dist/kernel/kernel.d.ts +75 -0
- package/dist/kernel/kernel.js +223 -0
- package/dist/kernel/logger.d.ts +18 -0
- package/dist/kernel/logger.js +26 -0
- package/dist/kernel/mcp/index.d.ts +9 -0
- package/dist/kernel/mcp/index.js +8 -0
- package/dist/kernel/mcp/mcp-server.d.ts +27 -0
- package/dist/kernel/mcp/mcp-server.js +178 -0
- package/dist/kernel/mission-engine.d.ts +42 -0
- package/dist/kernel/mission-engine.js +160 -0
- package/dist/kernel/orchestrator.d.ts +51 -0
- package/dist/kernel/orchestrator.js +226 -0
- package/dist/kernel/plugin-manager.d.ts +28 -0
- package/dist/kernel/plugin-manager.js +76 -0
- package/dist/kernel/plugins/browser-plugin.d.ts +22 -0
- package/dist/kernel/plugins/browser-plugin.js +34 -0
- package/dist/kernel/plugins/dashboard-plugin.d.ts +17 -0
- package/dist/kernel/plugins/dashboard-plugin.js +72 -0
- package/dist/kernel/plugins/discord-plugin.d.ts +11 -0
- package/dist/kernel/plugins/discord-plugin.js +35 -0
- package/dist/kernel/plugins/metrics-plugin.d.ts +33 -0
- package/dist/kernel/plugins/metrics-plugin.js +86 -0
- package/dist/kernel/plugins/search-plugin.d.ts +17 -0
- package/dist/kernel/plugins/search-plugin.js +20 -0
- package/dist/kernel/plugins/slack-plugin.d.ts +11 -0
- package/dist/kernel/plugins/slack-plugin.js +35 -0
- package/dist/kernel/plugins/telegram-plugin.d.ts +20 -0
- package/dist/kernel/plugins/telegram-plugin.js +122 -0
- package/dist/kernel/policies.d.ts +33 -0
- package/dist/kernel/policies.js +105 -0
- package/dist/kernel/provider-discovery.d.ts +41 -0
- package/dist/kernel/provider-discovery.js +179 -0
- package/dist/kernel/provider-manager.d.ts +38 -0
- package/dist/kernel/provider-manager.js +206 -0
- package/dist/kernel/provider-url.d.ts +18 -0
- package/dist/kernel/provider-url.js +45 -0
- package/dist/kernel/providers/gemini-provider.d.ts +43 -0
- package/dist/kernel/providers/gemini-provider.js +203 -0
- package/dist/kernel/providers/ollama-provider.d.ts +44 -0
- package/dist/kernel/providers/ollama-provider.js +241 -0
- package/dist/kernel/providers/openai-compatible-provider.d.ts +43 -0
- package/dist/kernel/providers/openai-compatible-provider.js +233 -0
- package/dist/kernel/recovery-manager.d.ts +38 -0
- package/dist/kernel/recovery-manager.js +156 -0
- package/dist/kernel/router.d.ts +44 -0
- package/dist/kernel/router.js +222 -0
- package/dist/kernel/sample-missions.d.ts +11 -0
- package/dist/kernel/sample-missions.js +132 -0
- package/dist/kernel/scheduler.d.ts +30 -0
- package/dist/kernel/scheduler.js +147 -0
- package/dist/kernel/sdk/harness-sdk.d.ts +37 -0
- package/dist/kernel/sdk/harness-sdk.js +48 -0
- package/dist/kernel/sdk/plugin-sdk.d.ts +27 -0
- package/dist/kernel/sdk/plugin-sdk.js +40 -0
- package/dist/kernel/search/search-controller.d.ts +32 -0
- package/dist/kernel/search/search-controller.js +141 -0
- package/dist/kernel/setup.d.ts +13 -0
- package/dist/kernel/setup.js +59 -0
- package/dist/kernel/types.d.ts +479 -0
- package/dist/kernel/types.js +7 -0
- package/dist/kernel/verification-engine.d.ts +33 -0
- package/dist/kernel/verification-engine.js +287 -0
- package/dist/kernel/worker-runtime.d.ts +66 -0
- package/dist/kernel/worker-runtime.js +261 -0
- package/dist/kernel/workers/browser-worker.d.ts +6 -0
- package/dist/kernel/workers/browser-worker.js +92 -0
- package/dist/kernel/workers/builtin-workers.d.ts +20 -0
- package/dist/kernel/workers/builtin-workers.js +120 -0
- package/dist/kernel/workers/research-worker.d.ts +5 -0
- package/dist/kernel/workers/research-worker.js +90 -0
- package/dist/prompt-ascii-video-animation.d.ts +2 -0
- package/dist/prompt-ascii-video-animation.js +243 -0
- package/package.json +43 -9
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* infinicode kernel-setup — friendly interactive wizard.
|
|
3
|
+
*
|
|
4
|
+
* Flow:
|
|
5
|
+
* 1. Ollama master (optional — LAN + Tailscale fallback)
|
|
6
|
+
* 2. Cloud providers — choose which to set up, enter API key, test, load models
|
|
7
|
+
* (Groq, OpenRouter, GitHub Models, NVIDIA, HF, Gemini)
|
|
8
|
+
* 3. Discover all models across all healthy providers
|
|
9
|
+
* 4. Per-worker model pinning with arrow-key lists
|
|
10
|
+
* 5. Pick a default policy
|
|
11
|
+
* 6. Save + summary
|
|
12
|
+
*/
|
|
13
|
+
import chalk from 'chalk';
|
|
14
|
+
import inquirer from 'inquirer';
|
|
15
|
+
import ora from 'ora';
|
|
16
|
+
import { WORKER_TYPES_ORDER, WORKER_DESCRIPTIONS } from '../kernel/config-schema.js';
|
|
17
|
+
import { FREE_PROVIDERS, getProviderPreset, toCloudProviderConfig } from '../kernel/free-providers.js';
|
|
18
|
+
import { testOllamaProvider, testCloudProvider, testProviderInteractive, discoverAllModels, modelLabel, suggestAllWorkerModels, } from '../kernel/provider-discovery.js';
|
|
19
|
+
export async function kernelSetupWizard(config) {
|
|
20
|
+
console.log(chalk.bold.cyan('\n infinicode Setup\n ────────────────'));
|
|
21
|
+
console.log(chalk.dim(' Provider-agnostic AI execution kernel.'));
|
|
22
|
+
console.log(chalk.dim(' Set up Ollama (optional) + cloud providers, then pick a model per worker type.\n'));
|
|
23
|
+
await step1Ollama(config);
|
|
24
|
+
await step2CloudProviders(config);
|
|
25
|
+
await step3WorkerModels(config);
|
|
26
|
+
await step4Policy(config);
|
|
27
|
+
await step5Save(config);
|
|
28
|
+
}
|
|
29
|
+
// ── Step 1: Ollama (optional) ──────────────────────────────────────────────
|
|
30
|
+
async function step1Ollama(config) {
|
|
31
|
+
console.log(chalk.bold('\n Step 1 — Ollama master (local inference, optional)'));
|
|
32
|
+
console.log(chalk.dim(' The kernel prefers local inference when available.'));
|
|
33
|
+
console.log(chalk.dim(' Skip this if you only want cloud providers.\n'));
|
|
34
|
+
const current = config.get('masterUrl');
|
|
35
|
+
const { setupOllama } = await inquirer.prompt([
|
|
36
|
+
{
|
|
37
|
+
type: 'confirm',
|
|
38
|
+
name: 'setupOllama',
|
|
39
|
+
message: `Configure Ollama master? (current: ${chalk.cyan(current)})`,
|
|
40
|
+
default: true,
|
|
41
|
+
},
|
|
42
|
+
]);
|
|
43
|
+
if (!setupOllama) {
|
|
44
|
+
console.log(chalk.dim(' Skipping Ollama — cloud providers only.\n'));
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
const { masterIp, port, model, tailscaleIp } = await inquirer.prompt([
|
|
48
|
+
{
|
|
49
|
+
type: 'input',
|
|
50
|
+
name: 'masterIp',
|
|
51
|
+
message: 'Ollama master IP or URL:',
|
|
52
|
+
default: 'localhost',
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
type: 'input',
|
|
56
|
+
name: 'port',
|
|
57
|
+
message: 'Port:',
|
|
58
|
+
default: '11434',
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
type: 'input',
|
|
62
|
+
name: 'model',
|
|
63
|
+
message: 'Default Ollama model:',
|
|
64
|
+
default: config.get('defaultModel') ?? 'qwen2.5-coder:14b',
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
type: 'input',
|
|
68
|
+
name: 'tailscaleIp',
|
|
69
|
+
message: 'Tailscale fallback IP/host (optional):',
|
|
70
|
+
default: '',
|
|
71
|
+
},
|
|
72
|
+
]);
|
|
73
|
+
const masterUrl = masterIp.startsWith('http://') || masterIp.startsWith('https://')
|
|
74
|
+
? masterIp
|
|
75
|
+
: `http://${masterIp}:${port}`;
|
|
76
|
+
const tailscaleMasterUrl = tailscaleIp
|
|
77
|
+
? (tailscaleIp.startsWith('http://') || tailscaleIp.startsWith('https://')
|
|
78
|
+
? tailscaleIp
|
|
79
|
+
: `http://${tailscaleIp}:${port}`)
|
|
80
|
+
: undefined;
|
|
81
|
+
const spinner = ora('Testing Ollama connection...').start();
|
|
82
|
+
try {
|
|
83
|
+
const result = await testOllamaProvider(masterUrl, tailscaleMasterUrl);
|
|
84
|
+
if (result.ok) {
|
|
85
|
+
spinner.succeed(`Ollama connected — ${chalk.green(String(result.modelCount))} models, ${result.latencyMs ?? 0}ms`);
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
spinner.warn(`Ollama not reachable, but config saved. You can start it later.`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
spinner.warn(`Ollama not reachable, but config saved. You can start it later.`);
|
|
93
|
+
}
|
|
94
|
+
config.set('masterUrl', masterUrl);
|
|
95
|
+
config.set('defaultModel', model);
|
|
96
|
+
if (tailscaleMasterUrl) {
|
|
97
|
+
config.set('tailscaleMasterUrl', tailscaleMasterUrl);
|
|
98
|
+
}
|
|
99
|
+
console.log(chalk.green(` ✓ Ollama config saved: ${chalk.cyan(masterUrl)}\n`));
|
|
100
|
+
}
|
|
101
|
+
// ── Step 2: Cloud providers ────────────────────────────────────────────────
|
|
102
|
+
async function step2CloudProviders(config) {
|
|
103
|
+
console.log(chalk.bold('\n Step 2 — Cloud providers'));
|
|
104
|
+
console.log(chalk.dim(' Choose which providers to set up. For each: enter API key,'));
|
|
105
|
+
console.log(chalk.dim(' test connection, load models. More providers = better rotation.\n'));
|
|
106
|
+
const existing = new Map((config.get('cloudProviders') ?? []).map(p => [p.id, p]));
|
|
107
|
+
const choices = FREE_PROVIDERS.map(p => {
|
|
108
|
+
const configured = existing.get(p.id);
|
|
109
|
+
const status = configured?.enabled
|
|
110
|
+
? chalk.green('(configured ✓)')
|
|
111
|
+
: configured
|
|
112
|
+
? chalk.yellow('(disabled)')
|
|
113
|
+
: chalk.dim('(not set)');
|
|
114
|
+
return {
|
|
115
|
+
name: `${p.name} ${status} — ${p.notes ?? ''}`,
|
|
116
|
+
value: p.id,
|
|
117
|
+
short: p.name,
|
|
118
|
+
};
|
|
119
|
+
});
|
|
120
|
+
const { selected } = await inquirer.prompt([
|
|
121
|
+
{
|
|
122
|
+
type: 'checkbox',
|
|
123
|
+
name: 'selected',
|
|
124
|
+
message: 'Select cloud providers to set up (space to toggle, enter to confirm):',
|
|
125
|
+
choices,
|
|
126
|
+
default: FREE_PROVIDERS.filter(p => existing.has(p.id) && existing.get(p.id)?.enabled).map(p => p.id),
|
|
127
|
+
pageSize: 12,
|
|
128
|
+
},
|
|
129
|
+
]);
|
|
130
|
+
if (selected.length === 0) {
|
|
131
|
+
console.log(chalk.dim(' No cloud providers selected.\n'));
|
|
132
|
+
// keep existing
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
const nextProviders = [];
|
|
136
|
+
for (const presetId of selected) {
|
|
137
|
+
const preset = getProviderPreset(presetId);
|
|
138
|
+
if (!preset)
|
|
139
|
+
continue;
|
|
140
|
+
console.log(chalk.bold.cyan(`\n ── ${preset.name} ──────────────────────────────`));
|
|
141
|
+
console.log(chalk.dim(` Get a free API key at: ${chalk.cyan(preset.keyUrl)}`));
|
|
142
|
+
if (preset.notes)
|
|
143
|
+
console.log(chalk.dim(` ${preset.notes}`));
|
|
144
|
+
const existingCfg = existing.get(presetId);
|
|
145
|
+
const { apiKey } = await inquirer.prompt([
|
|
146
|
+
{
|
|
147
|
+
type: 'password',
|
|
148
|
+
name: 'apiKey',
|
|
149
|
+
message: `API key for ${preset.name}:`,
|
|
150
|
+
default: existingCfg?.apiKey ?? '',
|
|
151
|
+
mask: '*',
|
|
152
|
+
},
|
|
153
|
+
]);
|
|
154
|
+
if (!apiKey) {
|
|
155
|
+
const { skip } = await inquirer.prompt([
|
|
156
|
+
{
|
|
157
|
+
type: 'confirm',
|
|
158
|
+
name: 'skip',
|
|
159
|
+
message: `No key entered — skip ${preset.name}?`,
|
|
160
|
+
default: true,
|
|
161
|
+
},
|
|
162
|
+
]);
|
|
163
|
+
if (skip)
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
const cfg = toCloudProviderConfig(preset, apiKey);
|
|
167
|
+
// Test connection + load models
|
|
168
|
+
const result = await testProviderInteractive(preset.name, () => testCloudProvider(cfg));
|
|
169
|
+
cfg.enabled = result.ok;
|
|
170
|
+
if (result.ok) {
|
|
171
|
+
console.log(chalk.green(` ✓ ${preset.name} connected — ${result.modelCount} models loaded`));
|
|
172
|
+
}
|
|
173
|
+
else {
|
|
174
|
+
console.log(chalk.yellow(` ⚠ ${preset.name} test failed, but config saved (enable later with \`infinicode providers test\`)`));
|
|
175
|
+
}
|
|
176
|
+
nextProviders.push(cfg);
|
|
177
|
+
}
|
|
178
|
+
// keep previously configured providers that weren't re-visited
|
|
179
|
+
for (const [id, cfg] of existing) {
|
|
180
|
+
if (!nextProviders.find(p => p.id === id)) {
|
|
181
|
+
nextProviders.push(cfg);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
config.set('cloudProviders', nextProviders);
|
|
185
|
+
const enabledCount = nextProviders.filter(p => p.enabled).length;
|
|
186
|
+
console.log(chalk.green(`\n ✓ ${enabledCount} cloud provider(s) enabled for rotation.\n`));
|
|
187
|
+
}
|
|
188
|
+
// ── Step 3: Worker model pinning ────────────────────────────────────────────
|
|
189
|
+
async function step3WorkerModels(config) {
|
|
190
|
+
console.log(chalk.bold('\n Step 3 — Pick a model for each worker type'));
|
|
191
|
+
console.log(chalk.dim(' Workers request capabilities, the router picks the model.'));
|
|
192
|
+
console.log(chalk.dim(' Pinning a model overrides the router for that worker type.\n'));
|
|
193
|
+
const spinner = ora('Discovering models across all providers...').start();
|
|
194
|
+
let discovered = [];
|
|
195
|
+
try {
|
|
196
|
+
discovered = await discoverAllModels({
|
|
197
|
+
masterUrl: config.get('masterUrl'),
|
|
198
|
+
tailscaleMasterUrl: config.get('tailscaleMasterUrl'),
|
|
199
|
+
cloudProviders: config.get('cloudProviders') ?? [],
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
finally {
|
|
203
|
+
spinner.stop();
|
|
204
|
+
}
|
|
205
|
+
if (discovered.length === 0) {
|
|
206
|
+
console.log(chalk.yellow(' No models discovered — skipping worker pinning.'));
|
|
207
|
+
console.log(chalk.dim(' The router will use whatever models providers report at runtime.\n'));
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
console.log(chalk.green(` Found ${discovered.length} models across providers.\n`));
|
|
211
|
+
const suggestions = suggestAllWorkerModels(discovered);
|
|
212
|
+
const currentPrefs = config.get('workerModels') ?? {};
|
|
213
|
+
const workerModels = { ...currentPrefs };
|
|
214
|
+
for (const type of WORKER_TYPES_ORDER) {
|
|
215
|
+
const suggestion = suggestions[type];
|
|
216
|
+
const description = WORKER_DESCRIPTIONS[type];
|
|
217
|
+
const options = [
|
|
218
|
+
{ name: chalk.dim('— router decides (no pin) —'), value: null, short: 'auto' },
|
|
219
|
+
];
|
|
220
|
+
const valueToKey = (v) => (v ? `${v.providerId}/${v.modelId}` : '');
|
|
221
|
+
// dedupe by provider/model
|
|
222
|
+
const seen = new Set();
|
|
223
|
+
for (const dm of discovered) {
|
|
224
|
+
const key = `${dm.providerId}/${dm.model.id}`;
|
|
225
|
+
if (seen.has(key))
|
|
226
|
+
continue;
|
|
227
|
+
seen.add(key);
|
|
228
|
+
options.push({ name: modelLabel(dm), value: key, short: key });
|
|
229
|
+
}
|
|
230
|
+
const suggestedKey = suggestion ? `${suggestion.providerId}/${suggestion.modelId}` : null;
|
|
231
|
+
const currentKey = valueToKey(currentPrefs[type]);
|
|
232
|
+
const defaultKey = currentKey || suggestedKey || null;
|
|
233
|
+
const { pick } = await inquirer.prompt([
|
|
234
|
+
{
|
|
235
|
+
type: 'list',
|
|
236
|
+
name: 'pick',
|
|
237
|
+
message: `${chalk.cyan(type.padEnd(14))} ${chalk.dim(description)}`,
|
|
238
|
+
choices: options,
|
|
239
|
+
default: defaultKey,
|
|
240
|
+
pageSize: 12,
|
|
241
|
+
},
|
|
242
|
+
]);
|
|
243
|
+
if (pick) {
|
|
244
|
+
const [providerId, ...modelParts] = pick.split('/');
|
|
245
|
+
workerModels[type] = { providerId, modelId: modelParts.join('/') };
|
|
246
|
+
}
|
|
247
|
+
else {
|
|
248
|
+
delete workerModels[type];
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
config.set('workerModels', workerModels);
|
|
252
|
+
const pinnedCount = Object.keys(workerModels).length;
|
|
253
|
+
console.log(chalk.green(`\n ✓ ${pinnedCount} worker type(s) pinned.\n`));
|
|
254
|
+
}
|
|
255
|
+
// ── Step 4: Policy ────────────────────────────────────────────────────────────
|
|
256
|
+
async function step4Policy(config) {
|
|
257
|
+
console.log(chalk.bold('\n Step 4 — Default routing policy'));
|
|
258
|
+
console.log(chalk.dim(' Policies decide how the router scores providers + models.\n'));
|
|
259
|
+
const policyChoices = [
|
|
260
|
+
{ name: 'local-first — prefer Ollama, fall back to cloud', value: 'local-first' },
|
|
261
|
+
{ name: 'free-first — prefer free providers across the board', value: 'free-first' },
|
|
262
|
+
{ name: 'fastest — lowest latency wins', value: 'fastest' },
|
|
263
|
+
{ name: 'highest-quality — bigger/slower models preferred', value: 'highest-quality' },
|
|
264
|
+
{ name: 'privacy-first — local only, never send to cloud', value: 'privacy-first' },
|
|
265
|
+
{ name: 'balanced — mix of all factors', value: 'balanced' },
|
|
266
|
+
{ name: 'offline — local providers only', value: 'offline' },
|
|
267
|
+
];
|
|
268
|
+
const { policy } = await inquirer.prompt([
|
|
269
|
+
{
|
|
270
|
+
type: 'list',
|
|
271
|
+
name: 'policy',
|
|
272
|
+
message: 'Default policy:',
|
|
273
|
+
choices: policyChoices,
|
|
274
|
+
default: config.get('policy') ?? 'local-first',
|
|
275
|
+
pageSize: 10,
|
|
276
|
+
},
|
|
277
|
+
]);
|
|
278
|
+
config.set('policy', policy);
|
|
279
|
+
console.log(chalk.green(` ✓ Policy set to ${chalk.cyan(policy)}\n`));
|
|
280
|
+
}
|
|
281
|
+
// ── Step 5: Save + summary ───────────────────────────────────────────────────
|
|
282
|
+
async function step5Save(config) {
|
|
283
|
+
console.log(chalk.bold.cyan('\n Setup complete!\n ─────────────'));
|
|
284
|
+
console.log(chalk.dim(' Configuration saved.\n'));
|
|
285
|
+
const cloud = config.get('cloudProviders') ?? [];
|
|
286
|
+
const enabledCloud = cloud.filter(p => p.enabled);
|
|
287
|
+
const workerModels = config.get('workerModels') ?? {};
|
|
288
|
+
const pinnedCount = Object.keys(workerModels).length;
|
|
289
|
+
console.log(` Ollama master: ${chalk.cyan(config.get('masterUrl'))}`);
|
|
290
|
+
console.log(` Default model: ${chalk.cyan(config.get('defaultModel'))}`);
|
|
291
|
+
console.log(` Cloud providers: ${chalk.green(String(enabledCloud.length))} enabled`);
|
|
292
|
+
for (const p of enabledCloud) {
|
|
293
|
+
console.log(` ${chalk.dim('•')} ${p.name} (${p.id})`);
|
|
294
|
+
}
|
|
295
|
+
console.log(` Worker pins: ${chalk.green(String(pinnedCount))}/${WORKER_TYPES_ORDER.length}`);
|
|
296
|
+
console.log(` Default policy: ${chalk.cyan(config.get('policy') ?? 'local-first')}`);
|
|
297
|
+
console.log();
|
|
298
|
+
console.log(chalk.dim(' Start the TUI: ') + chalk.cyan('infinicode'));
|
|
299
|
+
console.log(chalk.dim(' Run a mission: ') + chalk.cyan('infinicode mission run --sample hello-code'));
|
|
300
|
+
console.log(chalk.dim(' Edit workers: ') + chalk.cyan('infinicode workers edit'));
|
|
301
|
+
console.log(chalk.dim(' Edit providers: ') + chalk.cyan('infinicode providers'));
|
|
302
|
+
console.log();
|
|
303
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type Conf from 'conf';
|
|
2
|
+
import type { InfinicodeConfig } from '../kernel/setup.js';
|
|
3
|
+
export interface McpOptions {
|
|
4
|
+
role?: string;
|
|
5
|
+
name?: string;
|
|
6
|
+
meshPort?: string;
|
|
7
|
+
token?: string;
|
|
8
|
+
seed?: string[];
|
|
9
|
+
tailscale?: boolean;
|
|
10
|
+
tag?: string;
|
|
11
|
+
lan?: boolean;
|
|
12
|
+
lanPort?: string;
|
|
13
|
+
}
|
|
14
|
+
export declare function mcpCommand(config: Conf<InfinicodeConfig>, opts: McpOptions): Promise<void>;
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* infinicode mcp — run the MCP control server (stdio) + an in-process mesh node.
|
|
3
|
+
*
|
|
4
|
+
* This is what an MCP host (InfiniBot, Claude Code, Cursor) spawns. It exposes
|
|
5
|
+
* the spawn/dispatch/command/role/voice/map tools and joins the device mesh so
|
|
6
|
+
* those tools reach every device.
|
|
7
|
+
*
|
|
8
|
+
* ~/.claude.json → { "mcpServers": { "infinicode": { "command": "infinicode", "args": ["mcp"] } } }
|
|
9
|
+
*
|
|
10
|
+
* CRITICAL: stdio MCP uses stdout for the protocol, so EVERY log goes to stderr.
|
|
11
|
+
*/
|
|
12
|
+
import { readFileSync } from 'node:fs';
|
|
13
|
+
import { fileURLToPath } from 'node:url';
|
|
14
|
+
import { dirname, join } from 'node:path';
|
|
15
|
+
import { buildKernelFromConfig } from '../kernel/setup.js';
|
|
16
|
+
import { Federation, GoalLoop, loadOrCreateIdentity, serveMcpStdio, SilentLogger, } from '../kernel/index.js';
|
|
17
|
+
function pkgVersion() {
|
|
18
|
+
try {
|
|
19
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
20
|
+
return JSON.parse(readFileSync(join(here, '..', '..', 'package.json'), 'utf8')).version ?? '0.0.0';
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return '0.0.0';
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
/** Logger that writes ONLY to stderr — safe alongside the stdio MCP channel. */
|
|
27
|
+
const stderrLogger = {
|
|
28
|
+
debug: () => { },
|
|
29
|
+
info: (m, ...a) => console.error('[mcp]', m, ...a),
|
|
30
|
+
warn: (m, ...a) => console.error('[mcp]', m, ...a),
|
|
31
|
+
error: (m, ...a) => console.error('[mcp]', m, ...a),
|
|
32
|
+
};
|
|
33
|
+
export async function mcpCommand(config, opts) {
|
|
34
|
+
const fed = config.get('federation') ?? {};
|
|
35
|
+
const role = (opts.role ?? fed.role ?? 'peer');
|
|
36
|
+
// Default to 47914 so a co-located `infinicode serve` (47913) doesn't clash.
|
|
37
|
+
const port = opts.meshPort ? parseInt(opts.meshPort, 10) : (fed.port ?? 47913) + 1;
|
|
38
|
+
const token = opts.token ?? fed.token;
|
|
39
|
+
const version = pkgVersion();
|
|
40
|
+
const kernel = buildKernelFromConfig(config, { logger: new SilentLogger() });
|
|
41
|
+
const identity = loadOrCreateIdentity({ role, displayName: opts.name ?? fed.displayName });
|
|
42
|
+
const describe = () => {
|
|
43
|
+
const defs = kernel.workerRuntime.listRegisteredDefinitions();
|
|
44
|
+
const caps = [...new Set(defs.flatMap(d => d.capabilities))];
|
|
45
|
+
return {
|
|
46
|
+
version,
|
|
47
|
+
capabilities: caps,
|
|
48
|
+
workers: defs.map(d => d.type),
|
|
49
|
+
commands: kernel.commands.list().map(c => c.name),
|
|
50
|
+
providers: kernel.providerManager.listInfos().map(p => p.id),
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
const applyConfig = (cfg) => {
|
|
54
|
+
if (cfg.cloudProviders)
|
|
55
|
+
config.set('cloudProviders', cfg.cloudProviders);
|
|
56
|
+
if (cfg.policy)
|
|
57
|
+
config.set('policy', cfg.policy);
|
|
58
|
+
if (cfg.defaultModel)
|
|
59
|
+
config.set('defaultModel', cfg.defaultModel);
|
|
60
|
+
stderrLogger.info(`applied shared config (rev ${cfg.revision})`);
|
|
61
|
+
};
|
|
62
|
+
const federation = new Federation({
|
|
63
|
+
kernel,
|
|
64
|
+
identity,
|
|
65
|
+
describe,
|
|
66
|
+
logger: stderrLogger,
|
|
67
|
+
options: { version, port, token, autoUpdate: fed.autoUpdate, applyConfig },
|
|
68
|
+
});
|
|
69
|
+
try {
|
|
70
|
+
await federation.start();
|
|
71
|
+
}
|
|
72
|
+
catch (err) {
|
|
73
|
+
stderrLogger.error(`mesh node failed to start on :${port} — ${err instanceof Error ? err.message : String(err)}`);
|
|
74
|
+
// Continue serving MCP tools; dispatch will report "no peers" until fixed.
|
|
75
|
+
}
|
|
76
|
+
// Join the mesh: static seeds + optional Tailscale discovery.
|
|
77
|
+
const seeds = [...(fed.seeds ?? []), ...(opts.seed ?? [])];
|
|
78
|
+
for (const url of seeds)
|
|
79
|
+
await federation.connect(url).catch(() => null);
|
|
80
|
+
if (opts.tailscale) {
|
|
81
|
+
const n = await federation.discoverAndConnect(opts.tag).catch(() => 0);
|
|
82
|
+
stderrLogger.info(`tailscale discovery connected ${n} peer(s)`);
|
|
83
|
+
}
|
|
84
|
+
if (opts.lan ?? fed.lan) {
|
|
85
|
+
federation.startLanDiscovery(opts.tag, opts.lanPort ? parseInt(opts.lanPort, 10) : fed.lanPort);
|
|
86
|
+
stderrLogger.info('LAN auto-discovery on (udp broadcast)');
|
|
87
|
+
}
|
|
88
|
+
// Proactive autonomy: a goal loop the MCP host can populate (add_goal/…).
|
|
89
|
+
const autonomy = new GoalLoop({ kernel, logger: stderrLogger });
|
|
90
|
+
autonomy.start();
|
|
91
|
+
await serveMcpStdio({ kernel, federation, logger: stderrLogger, version, autonomy });
|
|
92
|
+
const shutdown = async () => {
|
|
93
|
+
autonomy.stop();
|
|
94
|
+
await federation.stop();
|
|
95
|
+
kernel.destroy();
|
|
96
|
+
process.exit(0);
|
|
97
|
+
};
|
|
98
|
+
process.on('SIGINT', () => void shutdown());
|
|
99
|
+
process.on('SIGTERM', () => void shutdown());
|
|
100
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type Conf from 'conf';
|
|
2
|
+
import type { InfinicodeConfig } from '../kernel/setup.js';
|
|
3
|
+
export declare function missionRun(config: Conf<InfinicodeConfig>, opts: {
|
|
4
|
+
goal: string;
|
|
5
|
+
name?: string;
|
|
6
|
+
description?: string;
|
|
7
|
+
task?: string[];
|
|
8
|
+
cap?: string[];
|
|
9
|
+
policy?: string;
|
|
10
|
+
stream?: boolean;
|
|
11
|
+
sample?: string;
|
|
12
|
+
}): Promise<void>;
|
|
13
|
+
export declare function missionStatus(config: Conf<InfinicodeConfig>, missionId: string): Promise<void>;
|
|
14
|
+
export declare function missionList(config: Conf<InfinicodeConfig>): Promise<void>;
|
|
15
|
+
export declare function missionResume(config: Conf<InfinicodeConfig>, missionId: string): Promise<void>;
|
|
16
|
+
export declare function missionCancel(config: Conf<InfinicodeConfig>, missionId: string): Promise<void>;
|