infinicode 1.0.0 → 2.0.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 +527 -75
- package/dist/ascii-video-animation.d.ts +2 -0
- package/dist/ascii-video-animation.js +243 -0
- package/dist/cli.js +195 -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 +12 -0
- package/dist/commands/mcp.js +96 -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 +18 -0
- package/dist/commands/serve.js +127 -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 +53 -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 +126 -0
- package/dist/kernel/federation/federation.js +335 -0
- package/dist/kernel/federation/index.d.ts +31 -0
- package/dist/kernel/federation/index.js +23 -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
package/dist/commands/setup.js
CHANGED
|
@@ -1,54 +1,57 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
import ora from 'ora';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
3
|
+
function buildMasterUrl(host, port) {
|
|
4
|
+
if (host.startsWith('http://') || host.startsWith('https://')) {
|
|
5
|
+
return host;
|
|
6
|
+
}
|
|
7
|
+
return `http://${host}:${port}`;
|
|
8
|
+
}
|
|
9
|
+
async function testOllama(masterUrl) {
|
|
10
|
+
const response = await fetch(`${masterUrl}/api/tags`, {
|
|
11
|
+
signal: AbortSignal.timeout(10000)
|
|
12
|
+
});
|
|
13
|
+
if (!response.ok) {
|
|
14
|
+
throw new Error(`HTTP ${response.status}`);
|
|
15
|
+
}
|
|
16
|
+
const data = await response.json();
|
|
17
|
+
return data.models || [];
|
|
18
|
+
}
|
|
19
|
+
export async function setupMaster(masterIp, port, model, config, tailscaleMasterIp, tailscalePort = port) {
|
|
20
|
+
const masterUrl = buildMasterUrl(masterIp, port);
|
|
21
|
+
const tailscaleMasterUrl = tailscaleMasterIp ? buildMasterUrl(tailscaleMasterIp, tailscalePort) : undefined;
|
|
22
|
+
console.log(chalk.bold('\nInfinicode Setup'));
|
|
23
|
+
console.log(chalk.dim('-'.repeat(40)));
|
|
24
|
+
console.log(` LAN Master: ${chalk.cyan(masterUrl)}`);
|
|
25
|
+
if (tailscaleMasterUrl) {
|
|
26
|
+
console.log(` Tailscale Master: ${chalk.cyan(tailscaleMasterUrl)}`);
|
|
27
|
+
}
|
|
28
|
+
console.log(` Model: ${chalk.cyan(model)}`);
|
|
9
29
|
console.log();
|
|
10
|
-
|
|
30
|
+
let models;
|
|
31
|
+
const spinner = ora('Testing LAN connection...').start();
|
|
11
32
|
try {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
console.log(chalk.bold('\nAvailable models:'));
|
|
25
|
-
const models = data.models || [];
|
|
26
|
-
if (models.length === 0) {
|
|
27
|
-
console.log(chalk.yellow(' No models found. Pull one with:'));
|
|
28
|
-
console.log(chalk.dim(` ollama pull ${model}`));
|
|
29
|
-
}
|
|
30
|
-
else {
|
|
31
|
-
for (const m of models.slice(0, 10)) {
|
|
32
|
-
const isDefault = m.name === model || m.name.startsWith(model.split(':')[0]);
|
|
33
|
-
console.log(` ${isDefault ? chalk.green('●') : chalk.dim('○')} ${m.name}`);
|
|
34
|
-
}
|
|
35
|
-
if (models.length > 10) {
|
|
36
|
-
console.log(chalk.dim(` ... and ${models.length - 10} more`));
|
|
37
|
-
}
|
|
33
|
+
models = await testOllama(masterUrl);
|
|
34
|
+
spinner.succeed('Connected to Ollama over LAN');
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
spinner.fail('LAN connection failed');
|
|
38
|
+
}
|
|
39
|
+
if (tailscaleMasterUrl) {
|
|
40
|
+
const tailscaleSpinner = ora('Testing Tailscale fallback...').start();
|
|
41
|
+
try {
|
|
42
|
+
const tailscaleModels = await testOllama(tailscaleMasterUrl);
|
|
43
|
+
tailscaleSpinner.succeed('Tailscale fallback is reachable');
|
|
44
|
+
models ??= tailscaleModels;
|
|
38
45
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
if (!hasModel && models.length > 0) {
|
|
42
|
-
console.log(chalk.yellow(`\n⚠️ Model '${model}' not found.`));
|
|
43
|
-
console.log(chalk.dim(` Available: ${models.slice(0, 3).map(m => m.name).join(', ')}`));
|
|
44
|
-
console.log(chalk.dim(` Pull it: ollama pull ${model}`));
|
|
46
|
+
catch {
|
|
47
|
+
tailscaleSpinner.warn('Tailscale fallback saved, but not reachable right now');
|
|
45
48
|
}
|
|
46
|
-
console.log(chalk.green('\n✓ Configuration saved!'));
|
|
47
|
-
console.log(chalk.dim('\nRun `infinicode` or `ic` to start coding.\n'));
|
|
48
49
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
50
|
+
if (!models) {
|
|
51
|
+
console.log(chalk.red(`\nCould not connect to ${masterUrl}`));
|
|
52
|
+
if (tailscaleMasterUrl) {
|
|
53
|
+
console.log(chalk.red(`Could not connect to ${tailscaleMasterUrl}`));
|
|
54
|
+
}
|
|
52
55
|
console.log(chalk.dim('\nMake sure Ollama is running with network access:'));
|
|
53
56
|
console.log(chalk.cyan(' OLLAMA_HOST=0.0.0.0:11434 ollama serve'));
|
|
54
57
|
console.log(chalk.dim('\nOr via systemd:'));
|
|
@@ -57,4 +60,34 @@ export async function setupMaster(masterIp, port, model, config) {
|
|
|
57
60
|
console.log(chalk.cyan(' sudo systemctl restart ollama\n'));
|
|
58
61
|
process.exit(1);
|
|
59
62
|
}
|
|
63
|
+
config.set('masterUrl', masterUrl);
|
|
64
|
+
config.set('defaultModel', model);
|
|
65
|
+
if (tailscaleMasterUrl) {
|
|
66
|
+
config.set('tailscaleMasterUrl', tailscaleMasterUrl);
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
config.delete('tailscaleMasterUrl');
|
|
70
|
+
}
|
|
71
|
+
console.log(chalk.bold('\nAvailable models:'));
|
|
72
|
+
if (models.length === 0) {
|
|
73
|
+
console.log(chalk.yellow(' No models found. Pull one with:'));
|
|
74
|
+
console.log(chalk.dim(` ollama pull ${model}`));
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
for (const m of models.slice(0, 10)) {
|
|
78
|
+
const isDefault = m.name === model || m.name.startsWith(model.split(':')[0]);
|
|
79
|
+
console.log(` ${isDefault ? chalk.green('*') : chalk.dim('-')} ${m.name}`);
|
|
80
|
+
}
|
|
81
|
+
if (models.length > 10) {
|
|
82
|
+
console.log(chalk.dim(` ... and ${models.length - 10} more`));
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
const hasModel = models.some(m => m.name === model || m.name.startsWith(model.split(':')[0]));
|
|
86
|
+
if (!hasModel && models.length > 0) {
|
|
87
|
+
console.log(chalk.yellow(`\nModel '${model}' not found.`));
|
|
88
|
+
console.log(chalk.dim(` Available: ${models.slice(0, 3).map(m => m.name).join(', ')}`));
|
|
89
|
+
console.log(chalk.dim(` Pull it: ollama pull ${model}`));
|
|
90
|
+
}
|
|
91
|
+
console.log(chalk.green('\nConfiguration saved.'));
|
|
92
|
+
console.log(chalk.dim('\nRun `infinicode` or `ic` to start coding.\n'));
|
|
60
93
|
}
|
package/dist/commands/status.js
CHANGED
|
@@ -1,40 +1,56 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
import ora from 'ora';
|
|
3
|
+
import { buildKernelFromConfig } from '../kernel/setup.js';
|
|
3
4
|
export async function showStatus(config) {
|
|
4
5
|
const masterUrl = config.get('masterUrl');
|
|
5
6
|
const defaultModel = config.get('defaultModel');
|
|
6
|
-
|
|
7
|
-
console.log(chalk.
|
|
8
|
-
console.log(
|
|
9
|
-
console.log(` Default Model:
|
|
10
|
-
|
|
7
|
+
const policy = config.get('policy') ?? 'balanced';
|
|
8
|
+
console.log(chalk.bold('\nInfinicode Status'));
|
|
9
|
+
console.log(chalk.dim('-'.repeat(50)));
|
|
10
|
+
console.log(` Default Model: ${chalk.cyan(defaultModel)}`);
|
|
11
|
+
console.log(` Policy: ${chalk.cyan(policy)}`);
|
|
12
|
+
console.log(` Ollama master: ${chalk.cyan(masterUrl)}`);
|
|
13
|
+
if (config.get('tailscaleMasterUrl')) {
|
|
14
|
+
console.log(` Tailscale: ${chalk.cyan(config.get('tailscaleMasterUrl'))}`);
|
|
15
|
+
}
|
|
16
|
+
console.log();
|
|
17
|
+
const kernel = buildKernelFromConfig(config);
|
|
18
|
+
const spinner = ora('Checking all providers...').start();
|
|
11
19
|
try {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
throw new Error(`HTTP ${response.status}`);
|
|
17
|
-
}
|
|
18
|
-
const data = await response.json();
|
|
19
|
-
spinner.succeed('Ollama connected');
|
|
20
|
-
const models = data.models || [];
|
|
21
|
-
console.log(` Models: ${chalk.green(models.length)} available`);
|
|
22
|
-
// Check if default model is available
|
|
23
|
-
const hasDefault = models.some(m => m.name === defaultModel || m.name.startsWith(defaultModel.split(':')[0]));
|
|
24
|
-
if (hasDefault) {
|
|
25
|
-
console.log(` Status: ${chalk.green('● Ready')}`);
|
|
26
|
-
}
|
|
27
|
-
else {
|
|
28
|
-
console.log(` Status: ${chalk.yellow('● Missing model')}`);
|
|
29
|
-
console.log(chalk.yellow(`\n ⚠️ Default model '${defaultModel}' not found.`));
|
|
30
|
-
console.log(chalk.dim(` Pull it: ollama pull ${defaultModel}`));
|
|
31
|
-
}
|
|
20
|
+
// Give the token-verified health checks a moment to complete.
|
|
21
|
+
await new Promise(r => setTimeout(r, 3000));
|
|
22
|
+
await printProviderHealth(kernel);
|
|
23
|
+
spinner.succeed('Provider check complete');
|
|
32
24
|
}
|
|
33
|
-
catch (
|
|
34
|
-
spinner.fail('
|
|
35
|
-
console.
|
|
36
|
-
|
|
37
|
-
|
|
25
|
+
catch (err) {
|
|
26
|
+
spinner.fail('Provider check failed');
|
|
27
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
28
|
+
}
|
|
29
|
+
finally {
|
|
30
|
+
kernel.destroy();
|
|
38
31
|
}
|
|
39
32
|
console.log();
|
|
40
33
|
}
|
|
34
|
+
async function printProviderHealth(kernel) {
|
|
35
|
+
const infos = kernel.providerManager.listInfos();
|
|
36
|
+
if (infos.length === 0) {
|
|
37
|
+
console.log(chalk.yellow(' No providers registered.'));
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
console.log(chalk.bold('\n Providers'));
|
|
41
|
+
console.log(chalk.dim(' ' + '-'.repeat(46)));
|
|
42
|
+
for (const p of infos) {
|
|
43
|
+
const status = p.healthy ? chalk.green('●') : chalk.red('○');
|
|
44
|
+
const latency = p.latencyMs ? `${p.latencyMs}ms` : '-';
|
|
45
|
+
const sr = p.successRate !== undefined ? `${(p.successRate * 100).toFixed(0)}%` : '-';
|
|
46
|
+
const quota = p.quotaRemaining !== undefined ? `q=${p.quotaRemaining}` : '';
|
|
47
|
+
console.log(` ${status} ${chalk.cyan(p.id.padEnd(12))} ${(p.name ?? '').padEnd(18)} ${chalk.dim(`lat=${latency} ok=${sr} ${quota}`)}`);
|
|
48
|
+
if (p.error) {
|
|
49
|
+
console.log(chalk.dim(` error: ${p.error}`));
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
const healthy = infos.filter(p => p.healthy).length;
|
|
53
|
+
console.log(chalk.dim(`\n ${healthy}/${infos.length} providers healthy`));
|
|
54
|
+
const workerModels = kernel.workerRuntime.listRegisteredDefinitions();
|
|
55
|
+
console.log(chalk.dim(` ${workerModels.length} worker types registered`));
|
|
56
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type Conf from 'conf';
|
|
2
|
+
import type { InfinicodeConfig } from '../kernel/config-schema.js';
|
|
3
|
+
export declare function workersList(config: Conf<InfinicodeConfig>): Promise<void>;
|
|
4
|
+
export declare function workersEdit(config: Conf<InfinicodeConfig>): Promise<void>;
|
|
5
|
+
export declare function workersReset(config: Conf<InfinicodeConfig>): Promise<void>;
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* infinicode workers — view + edit per-worker model preferences.
|
|
3
|
+
*
|
|
4
|
+
* infinicode workers list current pins
|
|
5
|
+
* infinicode workers edit interactive editor (arrow-key)
|
|
6
|
+
* infinicode workers reset clear all pins (router decides)
|
|
7
|
+
*/
|
|
8
|
+
import chalk from 'chalk';
|
|
9
|
+
import inquirer from 'inquirer';
|
|
10
|
+
import { WORKER_TYPES_ORDER, WORKER_DESCRIPTIONS } from '../kernel/config-schema.js';
|
|
11
|
+
import { discoverAllModels, modelLabel, suggestModelForWorker } from '../kernel/provider-discovery.js';
|
|
12
|
+
import ora from 'ora';
|
|
13
|
+
export async function workersList(config) {
|
|
14
|
+
const workerModels = config.get('workerModels') ?? {};
|
|
15
|
+
console.log(chalk.bold('\nWorker Model Preferences'));
|
|
16
|
+
console.log(chalk.dim('-'.repeat(60)));
|
|
17
|
+
for (const type of WORKER_TYPES_ORDER) {
|
|
18
|
+
const pref = workerModels[type];
|
|
19
|
+
const desc = WORKER_DESCRIPTIONS[type];
|
|
20
|
+
if (pref) {
|
|
21
|
+
console.log(` ${chalk.cyan(type.padEnd(14))} ${chalk.green(pref.providerId + '/' + pref.modelId)}`);
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
console.log(` ${chalk.cyan(type.padEnd(14))} ${chalk.dim('auto (router decides)')}`);
|
|
25
|
+
}
|
|
26
|
+
console.log(chalk.dim(` ${''.padEnd(14)} ${desc}`));
|
|
27
|
+
}
|
|
28
|
+
const pinned = Object.keys(workerModels).length;
|
|
29
|
+
console.log(chalk.dim(`\n ${pinned}/${WORKER_TYPES_ORDER.length} pinned. Run ${chalk.cyan('infinicode workers edit')} to change.\n`));
|
|
30
|
+
}
|
|
31
|
+
export async function workersEdit(config) {
|
|
32
|
+
console.log(chalk.bold('\nWorker Model Editor'));
|
|
33
|
+
console.log(chalk.dim(' Pick a model for each worker type. Choose "auto" to let the router decide.\n'));
|
|
34
|
+
const spinner = ora('Discovering models...').start();
|
|
35
|
+
let discovered = [];
|
|
36
|
+
try {
|
|
37
|
+
discovered = await discoverAllModels({
|
|
38
|
+
masterUrl: config.get('masterUrl'),
|
|
39
|
+
tailscaleMasterUrl: config.get('tailscaleMasterUrl'),
|
|
40
|
+
cloudProviders: config.get('cloudProviders') ?? [],
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
finally {
|
|
44
|
+
spinner.stop();
|
|
45
|
+
}
|
|
46
|
+
if (discovered.length === 0) {
|
|
47
|
+
console.log(chalk.yellow(' No models discovered. Make sure Ollama or a cloud provider is reachable.'));
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
const current = config.get('workerModels') ?? {};
|
|
51
|
+
const next = { ...current };
|
|
52
|
+
for (const type of WORKER_TYPES_ORDER) {
|
|
53
|
+
const suggestion = suggestModelForWorker(type, discovered);
|
|
54
|
+
const currentKey = current[type] ? `${current[type].providerId}/${current[type].modelId}` : '';
|
|
55
|
+
const suggestedKey = suggestion ? `${suggestion.providerId}/${suggestion.model.id}` : '';
|
|
56
|
+
const options = [
|
|
57
|
+
{ name: chalk.dim('— auto (router decides) —'), value: null, short: 'auto' },
|
|
58
|
+
];
|
|
59
|
+
const seen = new Set();
|
|
60
|
+
for (const dm of discovered) {
|
|
61
|
+
const key = `${dm.providerId}/${dm.model.id}`;
|
|
62
|
+
if (seen.has(key))
|
|
63
|
+
continue;
|
|
64
|
+
seen.add(key);
|
|
65
|
+
const marker = key === suggestedKey ? chalk.green(' (recommended)') : '';
|
|
66
|
+
options.push({ name: modelLabel(dm) + marker, value: key, short: key });
|
|
67
|
+
}
|
|
68
|
+
const { pick } = await inquirer.prompt([
|
|
69
|
+
{
|
|
70
|
+
type: 'list',
|
|
71
|
+
name: 'pick',
|
|
72
|
+
message: `${chalk.cyan(type.padEnd(14))} ${chalk.dim(WORKER_DESCRIPTIONS[type])}`,
|
|
73
|
+
choices: options,
|
|
74
|
+
default: currentKey || suggestedKey || null,
|
|
75
|
+
pageSize: 12,
|
|
76
|
+
},
|
|
77
|
+
]);
|
|
78
|
+
if (pick) {
|
|
79
|
+
const [providerId, ...modelParts] = pick.split('/');
|
|
80
|
+
next[type] = { providerId, modelId: modelParts.join('/') };
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
delete next[type];
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
config.set('workerModels', next);
|
|
87
|
+
const pinned = Object.keys(next).length;
|
|
88
|
+
console.log(chalk.green(`\n ✓ Saved. ${pinned}/${WORKER_TYPES_ORDER.length} worker types pinned.\n`));
|
|
89
|
+
}
|
|
90
|
+
export async function workersReset(config) {
|
|
91
|
+
const { confirm } = await inquirer.prompt([
|
|
92
|
+
{
|
|
93
|
+
type: 'confirm',
|
|
94
|
+
name: 'confirm',
|
|
95
|
+
message: 'Clear all worker model pins? (router will decide for every worker type)',
|
|
96
|
+
default: false,
|
|
97
|
+
},
|
|
98
|
+
]);
|
|
99
|
+
if (!confirm)
|
|
100
|
+
return;
|
|
101
|
+
config.set('workerModels', {});
|
|
102
|
+
console.log(chalk.green('✓ All worker pins cleared. The router will decide.'));
|
|
103
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { Capability, KernelLike, Logger } from '../types.js';
|
|
2
|
+
export type GoalTrigger = {
|
|
3
|
+
kind: 'interval';
|
|
4
|
+
everyMs: number;
|
|
5
|
+
} | {
|
|
6
|
+
kind: 'idle';
|
|
7
|
+
afterMs: number;
|
|
8
|
+
} | {
|
|
9
|
+
kind: 'once';
|
|
10
|
+
};
|
|
11
|
+
export interface Goal {
|
|
12
|
+
id: string;
|
|
13
|
+
prompt: string;
|
|
14
|
+
capabilities: Capability[];
|
|
15
|
+
trigger: GoalTrigger;
|
|
16
|
+
enabled: boolean;
|
|
17
|
+
runCount: number;
|
|
18
|
+
lastRunAt?: number;
|
|
19
|
+
running: boolean;
|
|
20
|
+
}
|
|
21
|
+
export interface GoalInput {
|
|
22
|
+
prompt: string;
|
|
23
|
+
capabilities?: Capability[];
|
|
24
|
+
trigger: GoalTrigger;
|
|
25
|
+
}
|
|
26
|
+
export interface GoalLoopDeps {
|
|
27
|
+
kernel: KernelLike;
|
|
28
|
+
logger: Logger;
|
|
29
|
+
/** Tick cadence for evaluating triggers (default 1s). */
|
|
30
|
+
tickMs?: number;
|
|
31
|
+
}
|
|
32
|
+
export declare class GoalLoop {
|
|
33
|
+
private deps;
|
|
34
|
+
private goals;
|
|
35
|
+
private timer?;
|
|
36
|
+
private unsub?;
|
|
37
|
+
private lastActivity;
|
|
38
|
+
private readonly tickMs;
|
|
39
|
+
constructor(deps: GoalLoopDeps);
|
|
40
|
+
add(input: GoalInput): Goal;
|
|
41
|
+
remove(id: string): boolean;
|
|
42
|
+
setEnabled(id: string, enabled: boolean): void;
|
|
43
|
+
list(): Goal[];
|
|
44
|
+
start(): void;
|
|
45
|
+
private tick;
|
|
46
|
+
private shouldRun;
|
|
47
|
+
private run;
|
|
48
|
+
stop(): void;
|
|
49
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenKernel — Autonomy Goal Loop
|
|
3
|
+
*
|
|
4
|
+
* Native proactive capability: a node runs goals unattended on triggers —
|
|
5
|
+
* every N seconds, after an idle period, or once at start — executing each via
|
|
6
|
+
* the local kernel with this device's persistent role injected. This is what
|
|
7
|
+
* makes a Pi "wake up and do its job" without a human in the loop.
|
|
8
|
+
*
|
|
9
|
+
* Deliberately small: setInterval-based (no cron dependency), an overlap guard
|
|
10
|
+
* per goal, and idle detection off the kernel EventBus.
|
|
11
|
+
*/
|
|
12
|
+
import { nanoid } from 'nanoid';
|
|
13
|
+
import { loadRole, rolePreamble } from '../federation/role-context.js';
|
|
14
|
+
export class GoalLoop {
|
|
15
|
+
deps;
|
|
16
|
+
goals = new Map();
|
|
17
|
+
timer;
|
|
18
|
+
unsub;
|
|
19
|
+
lastActivity = Date.now();
|
|
20
|
+
tickMs;
|
|
21
|
+
constructor(deps) {
|
|
22
|
+
this.deps = deps;
|
|
23
|
+
this.tickMs = deps.tickMs ?? 1000;
|
|
24
|
+
}
|
|
25
|
+
add(input) {
|
|
26
|
+
const goal = {
|
|
27
|
+
id: `goal_${nanoid(8)}`,
|
|
28
|
+
prompt: input.prompt,
|
|
29
|
+
capabilities: input.capabilities ?? ['reasoning'],
|
|
30
|
+
trigger: input.trigger,
|
|
31
|
+
enabled: true,
|
|
32
|
+
runCount: 0,
|
|
33
|
+
running: false,
|
|
34
|
+
};
|
|
35
|
+
this.goals.set(goal.id, goal);
|
|
36
|
+
this.deps.logger.info(`[autonomy] goal added ${goal.id} (${goal.trigger.kind})`);
|
|
37
|
+
return goal;
|
|
38
|
+
}
|
|
39
|
+
remove(id) {
|
|
40
|
+
return this.goals.delete(id);
|
|
41
|
+
}
|
|
42
|
+
setEnabled(id, enabled) {
|
|
43
|
+
const g = this.goals.get(id);
|
|
44
|
+
if (g)
|
|
45
|
+
g.enabled = enabled;
|
|
46
|
+
}
|
|
47
|
+
list() {
|
|
48
|
+
return [...this.goals.values()];
|
|
49
|
+
}
|
|
50
|
+
start() {
|
|
51
|
+
if (this.timer)
|
|
52
|
+
return;
|
|
53
|
+
// Idle detection: any kernel event counts as activity.
|
|
54
|
+
this.unsub = this.deps.kernel.subscribeAll(() => { this.lastActivity = Date.now(); });
|
|
55
|
+
this.timer = setInterval(() => void this.tick(), this.tickMs);
|
|
56
|
+
this.deps.logger.info('[autonomy] goal loop started');
|
|
57
|
+
}
|
|
58
|
+
async tick() {
|
|
59
|
+
const now = Date.now();
|
|
60
|
+
for (const goal of this.goals.values()) {
|
|
61
|
+
if (!goal.enabled || goal.running)
|
|
62
|
+
continue;
|
|
63
|
+
if (this.shouldRun(goal, now))
|
|
64
|
+
void this.run(goal);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
shouldRun(goal, now) {
|
|
68
|
+
switch (goal.trigger.kind) {
|
|
69
|
+
case 'once':
|
|
70
|
+
return goal.runCount === 0;
|
|
71
|
+
case 'interval':
|
|
72
|
+
return goal.lastRunAt === undefined || now - goal.lastRunAt >= goal.trigger.everyMs;
|
|
73
|
+
case 'idle': {
|
|
74
|
+
const idleFor = now - this.lastActivity;
|
|
75
|
+
// Fire when idle long enough, but not more than once per idle window.
|
|
76
|
+
const cooled = goal.lastRunAt === undefined || now - goal.lastRunAt >= goal.trigger.afterMs;
|
|
77
|
+
return idleFor >= goal.trigger.afterMs && cooled;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
async run(goal) {
|
|
82
|
+
goal.running = true;
|
|
83
|
+
goal.lastRunAt = Date.now();
|
|
84
|
+
goal.runCount++;
|
|
85
|
+
const role = loadRole();
|
|
86
|
+
const preamble = rolePreamble(role);
|
|
87
|
+
const prompt = preamble ? `${preamble}\n\n${goal.prompt}` : goal.prompt;
|
|
88
|
+
this.deps.logger.info(`[autonomy] running goal ${goal.id} (#${goal.runCount})`);
|
|
89
|
+
try {
|
|
90
|
+
await this.deps.kernel.execute({
|
|
91
|
+
description: goal.prompt.slice(0, 80),
|
|
92
|
+
goal: goal.prompt,
|
|
93
|
+
tasks: [{
|
|
94
|
+
description: goal.prompt.slice(0, 80),
|
|
95
|
+
capabilities: goal.capabilities,
|
|
96
|
+
input: { prompt, context: { proactive: true, role: role?.role } },
|
|
97
|
+
}],
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
catch (err) {
|
|
101
|
+
this.deps.logger.warn(`[autonomy] goal ${goal.id} failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
102
|
+
}
|
|
103
|
+
finally {
|
|
104
|
+
goal.running = false;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
stop() {
|
|
108
|
+
if (this.timer)
|
|
109
|
+
clearInterval(this.timer);
|
|
110
|
+
this.timer = undefined;
|
|
111
|
+
this.unsub?.();
|
|
112
|
+
}
|
|
113
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenKernel — Autonomy public API
|
|
3
|
+
*
|
|
4
|
+
* Proactive, unattended execution: goals that fire on interval/idle/once
|
|
5
|
+
* triggers, each run with the device's persistent role context.
|
|
6
|
+
*/
|
|
7
|
+
export { GoalLoop } from './goal-loop.js';
|
|
8
|
+
export type { Goal, GoalInput, GoalTrigger, GoalLoopDeps } from './goal-loop.js';
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenKernel — Browser Controller
|
|
3
|
+
*
|
|
4
|
+
* Default browser layer: Playwright.
|
|
5
|
+
* Browser Worker → Browser Controller → DOM → Page → Events
|
|
6
|
+
*
|
|
7
|
+
* Playwright is an optional peer dependency. If unavailable, the controller
|
|
8
|
+
* falls back to a fetch-based read-only mode (no JS execution, no events).
|
|
9
|
+
* The worker handler detects which mode is active and adapts.
|
|
10
|
+
*/
|
|
11
|
+
import type { Artifact } from '../types.js';
|
|
12
|
+
export interface BrowserAction {
|
|
13
|
+
type: 'navigate' | 'click' | 'type' | 'extract' | 'screenshot' | 'evaluate' | 'wait' | 'scroll';
|
|
14
|
+
selector?: string;
|
|
15
|
+
url?: string;
|
|
16
|
+
text?: string;
|
|
17
|
+
script?: string;
|
|
18
|
+
ms?: number;
|
|
19
|
+
}
|
|
20
|
+
export interface BrowserActionResult {
|
|
21
|
+
ok: boolean;
|
|
22
|
+
url?: string;
|
|
23
|
+
title?: string;
|
|
24
|
+
text?: string;
|
|
25
|
+
html?: string;
|
|
26
|
+
markdown?: string;
|
|
27
|
+
screenshotPath?: string;
|
|
28
|
+
error?: string;
|
|
29
|
+
}
|
|
30
|
+
export interface BrowserControllerOptions {
|
|
31
|
+
headless?: boolean;
|
|
32
|
+
/** Override browser choice; default chromium. */
|
|
33
|
+
browser?: 'chromium' | 'firefox' | 'webkit';
|
|
34
|
+
/** Default navigation timeout ms. */
|
|
35
|
+
timeoutMs?: number;
|
|
36
|
+
/** Working directory for screenshots. */
|
|
37
|
+
screenshotDir?: string;
|
|
38
|
+
}
|
|
39
|
+
export declare class BrowserController {
|
|
40
|
+
private playwright;
|
|
41
|
+
private browser;
|
|
42
|
+
private page;
|
|
43
|
+
private mode;
|
|
44
|
+
private opts;
|
|
45
|
+
constructor(opts?: BrowserControllerOptions);
|
|
46
|
+
init(): Promise<void>;
|
|
47
|
+
getMode(): 'playwright' | 'fetch' | 'unavailable';
|
|
48
|
+
runAction(action: BrowserAction): Promise<BrowserActionResult>;
|
|
49
|
+
close(): Promise<void>;
|
|
50
|
+
private ensurePage;
|
|
51
|
+
private runPlaywright;
|
|
52
|
+
private runFetch;
|
|
53
|
+
}
|
|
54
|
+
export declare function htmlToMarkdown(html: string): string;
|
|
55
|
+
export declare function stripHtml(html: string): string;
|
|
56
|
+
export declare function extractTitle(html: string): string;
|
|
57
|
+
export declare function actionArtifacts(results: BrowserActionResult[]): Artifact[];
|