infinicode 2.1.0 → 2.2.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/README.md +4 -2
- package/dist/cli.js +1 -1
- package/dist/commands/mcp.js +24 -6
- package/dist/commands/serve.js +30 -14
- package/dist/kernel/federation/federation.d.ts +12 -3
- package/dist/kernel/federation/federation.js +33 -6
- package/dist/kernel/federation/transport-http.d.ts +2 -0
- package/dist/kernel/federation/transport-http.js +5 -0
- package/dist/kernel/setup.d.ts +8 -1
- package/dist/kernel/setup.js +27 -15
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -109,10 +109,12 @@ infinicode serve --role satellite --tailscale --tag tag:robopark
|
|
|
109
109
|
# or point at exact peers
|
|
110
110
|
infinicode serve --role satellite --seed http://192.168.1.20:47913
|
|
111
111
|
|
|
112
|
-
# verify the mesh
|
|
113
|
-
|
|
112
|
+
# verify the mesh — ask any running node for its peers (self + connected)
|
|
113
|
+
curl http://localhost:47913/fed/nodes
|
|
114
114
|
```
|
|
115
115
|
|
|
116
|
+
Set your cloud provider keys + policy **once on the hub**; every satellite auto-sources them (over `--seed`/`--lan`/`--tailscale`) and registers the providers + models live, no restart.
|
|
117
|
+
|
|
116
118
|
Drive the fleet from an MCP host — register the control server (spawn / dispatch / follow / role / voice / mesh-map tools):
|
|
117
119
|
|
|
118
120
|
```jsonc
|
package/dist/cli.js
CHANGED
|
@@ -15,7 +15,7 @@ import { consoleRepl, consoleRun } from './commands/console.js';
|
|
|
15
15
|
import { serve } from './commands/serve.js';
|
|
16
16
|
import { mcpCommand } from './commands/mcp.js';
|
|
17
17
|
import { DEFAULT_CONFIG } from './kernel/config-schema.js';
|
|
18
|
-
const VERSION = '2.
|
|
18
|
+
const VERSION = '2.2.0';
|
|
19
19
|
const config = new Conf({
|
|
20
20
|
projectName: 'infinicode',
|
|
21
21
|
defaults: DEFAULT_CONFIG,
|
package/dist/commands/mcp.js
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
import { readFileSync } from 'node:fs';
|
|
13
13
|
import { fileURLToPath } from 'node:url';
|
|
14
14
|
import { dirname, join } from 'node:path';
|
|
15
|
-
import { buildKernelFromConfig } from '../kernel/setup.js';
|
|
15
|
+
import { buildKernelFromConfig, registerCloudProviders } from '../kernel/setup.js';
|
|
16
16
|
import { Federation, GoalLoop, loadOrCreateIdentity, serveMcpStdio, SilentLogger, } from '../kernel/index.js';
|
|
17
17
|
function pkgVersion() {
|
|
18
18
|
try {
|
|
@@ -51,21 +51,39 @@ export async function mcpCommand(config, opts) {
|
|
|
51
51
|
};
|
|
52
52
|
};
|
|
53
53
|
const applyConfig = (cfg) => {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
54
|
+
let live = 0;
|
|
55
|
+
if (cfg.cloudProviders) {
|
|
56
|
+
const providers = cfg.cloudProviders;
|
|
57
|
+
config.set('cloudProviders', providers);
|
|
58
|
+
live = registerCloudProviders(kernel, providers); // live → models available now
|
|
59
|
+
}
|
|
60
|
+
if (cfg.policy) {
|
|
57
61
|
config.set('policy', cfg.policy);
|
|
62
|
+
kernel.setDefaultPolicy(cfg.policy);
|
|
63
|
+
}
|
|
58
64
|
if (cfg.defaultModel)
|
|
59
65
|
config.set('defaultModel', cfg.defaultModel);
|
|
60
|
-
stderrLogger.info(`
|
|
66
|
+
stderrLogger.info(`sourced shared config from hub (rev ${cfg.revision}) — ${live} provider(s) live`);
|
|
61
67
|
};
|
|
62
68
|
const federation = new Federation({
|
|
63
69
|
kernel,
|
|
64
70
|
identity,
|
|
65
71
|
describe,
|
|
66
72
|
logger: stderrLogger,
|
|
67
|
-
options: { version, port, token, autoUpdate: fed.autoUpdate, applyConfig },
|
|
73
|
+
options: { version, port, token, autoUpdate: fed.autoUpdate, applyConfig, autoPullConfig: role === 'satellite' },
|
|
68
74
|
});
|
|
75
|
+
// Hub/peer/relay: publish local providers + policy for satellites to source.
|
|
76
|
+
const sharedFromConf = () => ({
|
|
77
|
+
cloudProviders: config.get('cloudProviders'),
|
|
78
|
+
policy: config.get('policy'),
|
|
79
|
+
defaultModel: config.get('defaultModel'),
|
|
80
|
+
});
|
|
81
|
+
if (role !== 'satellite') {
|
|
82
|
+
federation.publishConfig(sharedFromConf());
|
|
83
|
+
for (const key of ['cloudProviders', 'policy', 'defaultModel']) {
|
|
84
|
+
config.onDidChange(key, () => federation.publishConfig(sharedFromConf()));
|
|
85
|
+
}
|
|
86
|
+
}
|
|
69
87
|
try {
|
|
70
88
|
await federation.start();
|
|
71
89
|
}
|
package/dist/commands/serve.js
CHANGED
|
@@ -13,7 +13,7 @@ import chalk from 'chalk';
|
|
|
13
13
|
import { readFileSync } from 'node:fs';
|
|
14
14
|
import { fileURLToPath } from 'node:url';
|
|
15
15
|
import { dirname, join } from 'node:path';
|
|
16
|
-
import { buildKernelFromConfig } from '../kernel/setup.js';
|
|
16
|
+
import { buildKernelFromConfig, registerCloudProviders } from '../kernel/setup.js';
|
|
17
17
|
import { Federation, MoltfedConnector, loadOrCreateIdentity } from '../kernel/index.js';
|
|
18
18
|
function pkgVersion() {
|
|
19
19
|
try {
|
|
@@ -44,15 +44,22 @@ export async function serve(config, opts) {
|
|
|
44
44
|
providers: kernel.providerManager.listInfos().map(p => p.id),
|
|
45
45
|
};
|
|
46
46
|
};
|
|
47
|
-
// Satellite: persist shared config
|
|
47
|
+
// Satellite: persist shared config AND hot-apply it to the running kernel so
|
|
48
|
+
// the hub's providers + their models are available immediately (no restart).
|
|
48
49
|
const applyConfig = (cfg) => {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
50
|
+
let live = 0;
|
|
51
|
+
if (cfg.cloudProviders) {
|
|
52
|
+
const providers = cfg.cloudProviders;
|
|
53
|
+
config.set('cloudProviders', providers);
|
|
54
|
+
live = registerCloudProviders(kernel, providers); // live registration → models appear now
|
|
55
|
+
}
|
|
56
|
+
if (cfg.policy) {
|
|
52
57
|
config.set('policy', cfg.policy);
|
|
58
|
+
kernel.setDefaultPolicy(cfg.policy);
|
|
59
|
+
}
|
|
53
60
|
if (cfg.defaultModel)
|
|
54
61
|
config.set('defaultModel', cfg.defaultModel);
|
|
55
|
-
console.log(chalk.green(` ✓
|
|
62
|
+
console.log(chalk.green(` ✓ sourced shared config from hub (rev ${cfg.revision}) — ${live} provider(s) live`));
|
|
56
63
|
};
|
|
57
64
|
const federation = new Federation({
|
|
58
65
|
kernel,
|
|
@@ -67,8 +74,22 @@ export async function serve(config, opts) {
|
|
|
67
74
|
heartbeatMs: (fed.heartbeatSec ?? 30) * 1000,
|
|
68
75
|
autoUpdate: opts.autoUpdate ?? fed.autoUpdate,
|
|
69
76
|
applyConfig,
|
|
77
|
+
autoPullConfig: role === 'satellite',
|
|
70
78
|
},
|
|
71
79
|
});
|
|
80
|
+
// Hub/peer/relay: publish this machine's providers + policy so satellites can
|
|
81
|
+
// auto-source them. Re-publish when the local config changes (live).
|
|
82
|
+
const sharedFromConf = () => ({
|
|
83
|
+
cloudProviders: config.get('cloudProviders'),
|
|
84
|
+
policy: config.get('policy'),
|
|
85
|
+
defaultModel: config.get('defaultModel'),
|
|
86
|
+
});
|
|
87
|
+
if (role !== 'satellite') {
|
|
88
|
+
federation.publishConfig(sharedFromConf());
|
|
89
|
+
for (const key of ['cloudProviders', 'policy', 'defaultModel']) {
|
|
90
|
+
config.onDidChange(key, () => federation.publishConfig(sharedFromConf()));
|
|
91
|
+
}
|
|
92
|
+
}
|
|
72
93
|
console.log(chalk.bold(`\n infinicode mesh node`));
|
|
73
94
|
console.log(chalk.dim(' ' + '-'.repeat(46)));
|
|
74
95
|
console.log(` node: ${chalk.cyan(identity.displayName)} ${chalk.dim('(' + identity.nodeId + ')')}`);
|
|
@@ -78,17 +99,12 @@ export async function serve(config, opts) {
|
|
|
78
99
|
console.log(` auth: ${chalk.green('token required')}`);
|
|
79
100
|
console.log();
|
|
80
101
|
await federation.start();
|
|
81
|
-
// Connect to seeds
|
|
102
|
+
// Connect to seeds. Satellites auto-source shared config from any hub/relay
|
|
103
|
+
// they connect to (handled inside federation.connect()).
|
|
82
104
|
for (const url of seeds) {
|
|
83
105
|
const peer = await federation.connect(url);
|
|
84
|
-
if (peer)
|
|
106
|
+
if (peer)
|
|
85
107
|
console.log(chalk.green(` ✓ connected to ${peer.displayName} @ ${url}`));
|
|
86
|
-
if (role === 'satellite') {
|
|
87
|
-
const applied = await federation.pullConfig(peer.nodeId).catch(() => false);
|
|
88
|
-
if (applied)
|
|
89
|
-
console.log(chalk.green(` ✓ pulled shared config from ${peer.displayName}`));
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
108
|
}
|
|
93
109
|
// Optional: auto-discover peers over Tailscale (no --seed needed).
|
|
94
110
|
if (opts.tailscale) {
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* (heartbeat / task.dispatch / command / config / update).
|
|
12
12
|
*/
|
|
13
13
|
import type { Capability, KernelLike, Logger } from '../types.js';
|
|
14
|
-
import type { HardwareSnapshot, NodeIdentity, NodeStatus, SharedConfig, StreamFrame } from './types.js';
|
|
14
|
+
import type { HardwareSnapshot, NodeIdentity, NodeStatus, PeerInfo, SharedConfig, StreamFrame } from './types.js';
|
|
15
15
|
import { ConfigSync } from './config-sync.js';
|
|
16
16
|
import { AutoUpdate } from './auto-update.js';
|
|
17
17
|
import { ComputeRouter, type ComputeNeed } from './compute-router.js';
|
|
@@ -36,6 +36,10 @@ export interface FederationOptions {
|
|
|
36
36
|
autoUpdate?: boolean;
|
|
37
37
|
/** Satellite: how to apply an incoming shared config (register providers etc.). */
|
|
38
38
|
applyConfig?: (cfg: SharedConfig) => void | Promise<void>;
|
|
39
|
+
/** Satellite: auto-pull shared config from any hub/relay we connect to. */
|
|
40
|
+
autoPullConfig?: boolean;
|
|
41
|
+
/** Satellite: how often to re-pull shared config (ms, default 30000; 0 disables). */
|
|
42
|
+
configPullMs?: number;
|
|
39
43
|
}
|
|
40
44
|
export interface FederationDeps {
|
|
41
45
|
kernel: KernelLike;
|
|
@@ -57,6 +61,7 @@ export declare class Federation {
|
|
|
57
61
|
private lan?;
|
|
58
62
|
/** nodeIds we're currently dialing via LAN discovery (dedup guard). */
|
|
59
63
|
private lanConnecting;
|
|
64
|
+
private configPullTimer?;
|
|
60
65
|
readonly configSync: ConfigSync;
|
|
61
66
|
readonly autoUpdate: AutoUpdate;
|
|
62
67
|
readonly compute: ComputeRouter;
|
|
@@ -109,7 +114,11 @@ export declare class Federation {
|
|
|
109
114
|
publishConfig(cfg: Omit<SharedConfig, 'revision' | 'updatedAt'>): SharedConfig;
|
|
110
115
|
/** Satellite: pull shared config from a peer (usually the hub). */
|
|
111
116
|
pullConfig(nodeId: string): Promise<boolean>;
|
|
112
|
-
connect(url: string): Promise<
|
|
117
|
+
connect(url: string): Promise<PeerInfo | null>;
|
|
118
|
+
/** Satellite: pull shared config from a freshly-connected hub/relay. */
|
|
119
|
+
private maybeAutoPull;
|
|
120
|
+
/** Satellite: re-pull from the first connected hub/relay (periodic refresh). */
|
|
121
|
+
private autoPullFromHub;
|
|
113
122
|
/** Auto-discover peers over Tailscale and connect to each (skips self/known). */
|
|
114
123
|
discoverAndConnect(tag?: string): Promise<number>;
|
|
115
124
|
/**
|
|
@@ -120,7 +129,7 @@ export declare class Federation {
|
|
|
120
129
|
startLanDiscovery(tag?: string, discoveryPort?: number): void;
|
|
121
130
|
/** Neutral node-status list (self + peers) — feeds the mesh map. */
|
|
122
131
|
nodeStatuses(): NodeStatus[];
|
|
123
|
-
peers():
|
|
132
|
+
peers(): PeerInfo[];
|
|
124
133
|
/** Latest local hardware snapshot (for the MOLTFED connector / panels). */
|
|
125
134
|
latestHardware(): HardwareSnapshot | undefined;
|
|
126
135
|
/** Subscribe to the merged mesh frame stream (local + peer frames). */
|
|
@@ -24,6 +24,7 @@ export class Federation {
|
|
|
24
24
|
lan;
|
|
25
25
|
/** nodeIds we're currently dialing via LAN discovery (dedup guard). */
|
|
26
26
|
lanConnecting = new Set();
|
|
27
|
+
configPullTimer;
|
|
27
28
|
configSync;
|
|
28
29
|
autoUpdate;
|
|
29
30
|
compute = new ComputeRouter();
|
|
@@ -57,6 +58,7 @@ export class Federation {
|
|
|
57
58
|
token: options.token,
|
|
58
59
|
logger,
|
|
59
60
|
getManifest: () => buildManifest(identity, this.deps.describe()),
|
|
61
|
+
getNodes: () => this.nodeStatuses(),
|
|
60
62
|
onRpc: (env, remote) => this.onRpc(env, remote),
|
|
61
63
|
});
|
|
62
64
|
await this.server.start();
|
|
@@ -82,9 +84,14 @@ export class Federation {
|
|
|
82
84
|
this.lastHw = this.telemetry.collect();
|
|
83
85
|
this.server?.broadcast(frame('hw', identity.nodeId, this.lastHw));
|
|
84
86
|
}, telMs);
|
|
85
|
-
// Connect to seed peers.
|
|
87
|
+
// Connect to seed peers (via this.connect so satellites auto-pull config).
|
|
86
88
|
for (const url of options.seeds ?? [])
|
|
87
|
-
void this.
|
|
89
|
+
void this.connect(url);
|
|
90
|
+
// Satellite: periodically re-pull shared config so hub changes (new
|
|
91
|
+
// providers/keys/policy) propagate without a reconnect.
|
|
92
|
+
if (options.autoPullConfig && options.configPullMs !== 0) {
|
|
93
|
+
this.configPullTimer = setInterval(() => void this.autoPullFromHub(), options.configPullMs ?? 30_000);
|
|
94
|
+
}
|
|
88
95
|
logger.info(`[federation] node ${identity.displayName} (${identity.role}) online on :${port}`);
|
|
89
96
|
}
|
|
90
97
|
// ── Inbound RPC ────────────────────────────────────────────────────────────
|
|
@@ -275,7 +282,26 @@ export class Federation {
|
|
|
275
282
|
return false;
|
|
276
283
|
}
|
|
277
284
|
async connect(url) {
|
|
278
|
-
|
|
285
|
+
const peer = (await this.mesh?.connect(url)) ?? null;
|
|
286
|
+
if (peer)
|
|
287
|
+
await this.maybeAutoPull(peer);
|
|
288
|
+
return peer;
|
|
289
|
+
}
|
|
290
|
+
/** Satellite: pull shared config from a freshly-connected hub/relay. */
|
|
291
|
+
async maybeAutoPull(peer) {
|
|
292
|
+
if (!this.deps.options.autoPullConfig)
|
|
293
|
+
return;
|
|
294
|
+
if (peer.role !== 'hub' && peer.role !== 'relay')
|
|
295
|
+
return;
|
|
296
|
+
const applied = await this.pullConfig(peer.nodeId).catch(() => false);
|
|
297
|
+
if (applied)
|
|
298
|
+
this.deps.logger.info(`[federation] pulled shared config from ${peer.displayName}`);
|
|
299
|
+
}
|
|
300
|
+
/** Satellite: re-pull from the first connected hub/relay (periodic refresh). */
|
|
301
|
+
async autoPullFromHub() {
|
|
302
|
+
const hub = (this.mesh?.connected() ?? []).find(p => p.role === 'hub' || p.role === 'relay');
|
|
303
|
+
if (hub)
|
|
304
|
+
await this.maybeAutoPull(hub);
|
|
279
305
|
}
|
|
280
306
|
/** Auto-discover peers over Tailscale and connect to each (skips self/known). */
|
|
281
307
|
async discoverAndConnect(tag) {
|
|
@@ -283,7 +309,7 @@ export class Federation {
|
|
|
283
309
|
const urls = await discoverTailscalePeers({ port, tag, logger: this.deps.logger });
|
|
284
310
|
let connected = 0;
|
|
285
311
|
for (const url of urls) {
|
|
286
|
-
const peer = await this.
|
|
312
|
+
const peer = await this.connect(url);
|
|
287
313
|
if (peer && peer.nodeId !== this.deps.identity.nodeId)
|
|
288
314
|
connected++;
|
|
289
315
|
}
|
|
@@ -315,8 +341,7 @@ export class Federation {
|
|
|
315
341
|
if (this.lanConnecting.has(nodeId))
|
|
316
342
|
return; // dial in flight
|
|
317
343
|
this.lanConnecting.add(nodeId);
|
|
318
|
-
void this.
|
|
319
|
-
?.connect(url)
|
|
344
|
+
void this.connect(url)
|
|
320
345
|
.then(p => {
|
|
321
346
|
if (p)
|
|
322
347
|
logger.info(`[federation] lan discovered ${p.displayName ?? name} @ ${url}`);
|
|
@@ -370,6 +395,8 @@ export class Federation {
|
|
|
370
395
|
this.unsub?.();
|
|
371
396
|
this.bridge?.stop();
|
|
372
397
|
this.lan?.stop();
|
|
398
|
+
if (this.configPullTimer)
|
|
399
|
+
clearInterval(this.configPullTimer);
|
|
373
400
|
if (this.telemetryTimer)
|
|
374
401
|
clearInterval(this.telemetryTimer);
|
|
375
402
|
this.mesh?.stop();
|
|
@@ -7,6 +7,8 @@ export interface MeshServerOptions {
|
|
|
7
7
|
/** Optional shared token; requests must present it as Bearer auth. */
|
|
8
8
|
token?: string;
|
|
9
9
|
getManifest: () => NodeManifest;
|
|
10
|
+
/** Optional: full mesh view (self + peers) for the GET /fed/nodes probe. */
|
|
11
|
+
getNodes?: () => unknown;
|
|
10
12
|
onRpc: RpcHandler;
|
|
11
13
|
logger: Logger;
|
|
12
14
|
}
|
|
@@ -69,6 +69,11 @@ export class MeshServer {
|
|
|
69
69
|
res.end(JSON.stringify(this.opts.getManifest()));
|
|
70
70
|
return;
|
|
71
71
|
}
|
|
72
|
+
if (req.method === 'GET' && url.startsWith('/fed/nodes')) {
|
|
73
|
+
res.writeHead(200, { 'content-type': 'application/json' });
|
|
74
|
+
res.end(JSON.stringify(this.opts.getNodes?.() ?? []));
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
72
77
|
if (req.method === 'GET' && url.startsWith('/fed/stream')) {
|
|
73
78
|
this.openStream(req, res);
|
|
74
79
|
return;
|
package/dist/kernel/setup.d.ts
CHANGED
|
@@ -6,8 +6,15 @@
|
|
|
6
6
|
import type Conf from 'conf';
|
|
7
7
|
import { Kernel } from '../kernel/index.js';
|
|
8
8
|
import type { Logger } from '../kernel/index.js';
|
|
9
|
-
import type { InfinicodeConfig } from './config-schema.js';
|
|
9
|
+
import type { InfinicodeConfig, CloudProviderConfig } from './config-schema.js';
|
|
10
10
|
export type { InfinicodeConfig, CloudProviderConfig, WorkerModelPref } from './config-schema.js';
|
|
11
|
+
/**
|
|
12
|
+
* Register (or re-register) cloud providers on a live kernel from config.
|
|
13
|
+
* Used at build time and for satellite hot-apply of hub-shared config, so new
|
|
14
|
+
* providers + their models become available without a restart. Returns the
|
|
15
|
+
* count of enabled providers registered.
|
|
16
|
+
*/
|
|
17
|
+
export declare function registerCloudProviders(kernel: Kernel, cloudProviders: CloudProviderConfig[]): number;
|
|
11
18
|
export declare function buildKernelFromConfig(config: Conf<InfinicodeConfig>, options?: {
|
|
12
19
|
logger?: Logger;
|
|
13
20
|
}): Kernel;
|
package/dist/kernel/setup.js
CHANGED
|
@@ -1,20 +1,13 @@
|
|
|
1
1
|
import { Kernel, OllamaProvider, OpenAICompatibleProvider, GeminiProvider } from '../kernel/index.js';
|
|
2
2
|
import { getProviderPreset } from './free-providers.js';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
fallbackURL: tailscaleMasterUrl,
|
|
12
|
-
defaultModel,
|
|
13
|
-
};
|
|
14
|
-
const kernel = new Kernel({ loadBuiltinWorkers: true, logger: options?.logger });
|
|
15
|
-
kernel.registerProvider('ollama', new OllamaProvider(ollamaOptions));
|
|
16
|
-
// Register cloud providers
|
|
17
|
-
const cloudProviders = config.get('cloudProviders') ?? [];
|
|
3
|
+
/**
|
|
4
|
+
* Register (or re-register) cloud providers on a live kernel from config.
|
|
5
|
+
* Used at build time and for satellite hot-apply of hub-shared config, so new
|
|
6
|
+
* providers + their models become available without a restart. Returns the
|
|
7
|
+
* count of enabled providers registered.
|
|
8
|
+
*/
|
|
9
|
+
export function registerCloudProviders(kernel, cloudProviders) {
|
|
10
|
+
let n = 0;
|
|
18
11
|
for (const cfg of cloudProviders) {
|
|
19
12
|
if (!cfg.enabled)
|
|
20
13
|
continue;
|
|
@@ -28,6 +21,7 @@ export function buildKernelFromConfig(config, options) {
|
|
|
28
21
|
knownModels: preset?.knownModels ?? [],
|
|
29
22
|
};
|
|
30
23
|
kernel.registerProvider(cfg.id, new GeminiProvider(geminiOptions));
|
|
24
|
+
n++;
|
|
31
25
|
continue;
|
|
32
26
|
}
|
|
33
27
|
const options = {
|
|
@@ -39,7 +33,25 @@ export function buildKernelFromConfig(config, options) {
|
|
|
39
33
|
knownModels: preset?.knownModels ?? [],
|
|
40
34
|
};
|
|
41
35
|
kernel.registerProvider(cfg.id, new OpenAICompatibleProvider(options));
|
|
36
|
+
n++;
|
|
42
37
|
}
|
|
38
|
+
return n;
|
|
39
|
+
}
|
|
40
|
+
export function buildKernelFromConfig(config, options) {
|
|
41
|
+
const masterUrl = config.get('masterUrl');
|
|
42
|
+
const tailscaleMasterUrl = config.get('tailscaleMasterUrl');
|
|
43
|
+
const defaultModel = config.get('defaultModel');
|
|
44
|
+
const ollamaOptions = {
|
|
45
|
+
id: 'ollama',
|
|
46
|
+
name: 'Ollama',
|
|
47
|
+
baseURL: masterUrl,
|
|
48
|
+
fallbackURL: tailscaleMasterUrl,
|
|
49
|
+
defaultModel,
|
|
50
|
+
};
|
|
51
|
+
const kernel = new Kernel({ loadBuiltinWorkers: true, logger: options?.logger });
|
|
52
|
+
kernel.registerProvider('ollama', new OllamaProvider(ollamaOptions));
|
|
53
|
+
// Register cloud providers
|
|
54
|
+
registerCloudProviders(kernel, config.get('cloudProviders') ?? []);
|
|
43
55
|
// Apply per-worker model pins — the worker runtime honors them when routing.
|
|
44
56
|
const workerModels = config.get('workerModels') ?? {};
|
|
45
57
|
if (workerModels && Object.keys(workerModels).length > 0) {
|
package/package.json
CHANGED