infinicode 2.8.27 → 2.8.28

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.
@@ -1,139 +1,139 @@
1
- import { createSignal, onCleanup, createMemo } from "solid-js";
2
- import { existsSync, readFileSync } from "fs";
3
- import { homedir } from "os";
4
- import { join } from "path";
5
-
6
- // ── Config reader (no external deps) ───────────────────────────────────────
7
-
8
- interface InfinicodeConfig {
9
- masterUrl: string;
10
- defaultModel: string;
11
- policy?: string;
12
- workerModels?: Record<string, { providerId: string; modelId: string }>;
13
- cloudProviders?: Array<{ id: string; name: string; enabled: boolean; apiKey?: string }>;
14
- }
15
-
16
- function findConfigPath(): string {
17
- const home = homedir();
18
- const paths = [
19
- // Windows — Conf appends "-nodejs" for Node projects
20
- join(home, "AppData", "Roaming", "infinicode-nodejs", "Config", "config.json"),
21
- join(home, "AppData", "Roaming", "infinicode", "Config", "config.json"),
22
- // Linux
23
- join(home, ".config", "infinicode", "config.json"),
24
- // macOS
25
- join(home, "Library", "Preferences", "infinicode", "config.json"),
26
- ];
27
- for (const p of paths) {
28
- if (existsSync(p)) return p;
29
- }
30
- return paths[0];
31
- }
32
-
33
- function loadConfig(): InfinicodeConfig | null {
34
- try {
35
- const path = findConfigPath();
36
- if (!existsSync(path)) return null;
37
- return JSON.parse(readFileSync(path, "utf-8")) as InfinicodeConfig;
38
- } catch {
39
- return null;
40
- }
41
- }
42
-
43
- // ── Routing info ───────────────────────────────────────────────────────────
44
-
45
- // Every policy auto-routes across providers except `offline`, which is locked
46
- // to the local machine — mirrors the kernel router's one locked mode.
47
- const LOCKED_POLICIES = new Set(["offline"]);
48
-
49
- interface RoutingBadge {
50
- glyph: string;
51
- text: string;
52
- /** true = actively auto-routing (drives the pulse highlight). */
53
- active: boolean;
54
- }
55
-
56
- function buildRoutingBadge(cfg: InfinicodeConfig | null, tuiModel?: string): RoutingBadge {
57
- if (!cfg) return { glyph: "◆", text: "AUTO (no config)", active: true };
58
-
59
- const enabledCloud = (cfg.cloudProviders ?? []).filter((p) => p.enabled && p.apiKey);
60
- const providerCount = 1 + enabledCloud.length; // ollama + cloud
61
- const pinned = Object.keys(cfg.workerModels ?? {}).length;
62
- const policy = cfg.policy ?? "balanced";
63
- const codingPin = cfg.workerModels?.["coding"];
64
- const autoModel = codingPin
65
- ? `${codingPin.providerId}/${codingPin.modelId}`
66
- : `ollama/${cfg.defaultModel}`;
67
-
68
- // If the user manually pinned a model in the TUI, that overrides routing.
69
- if (tuiModel && tuiModel !== autoModel) {
70
- const [prov, ...rest] = tuiModel.split("/");
71
- return { glyph: "◇", text: `SINGLE ${prov}/${rest.join("/")}`, active: false };
72
- }
73
-
74
- // The one locked mode: offline stays local-only.
75
- if (LOCKED_POLICIES.has(policy)) {
76
- return { glyph: "🔒", text: `LOCKED ${policy} (local only)`, active: false };
77
- }
78
-
79
- return {
80
- glyph: "◆",
81
- text: `AUTO policy=${policy} providers=${providerCount} pins=${pinned}`,
82
- active: true,
83
- };
84
- }
85
-
86
- // ── Plugin ──────────────────────────────────────────────────────────────────
87
-
88
- const tui = async (api: any) => {
89
- const [cfg] = createSignal(loadConfig());
90
-
91
- const getTuiModel = (): string | undefined => {
92
- try {
93
- return api.state?.config?.model;
94
- } catch {
95
- return undefined;
96
- }
97
- };
98
-
99
- const [tuiModel, setTuiModel] = createSignal<string | undefined>(getTuiModel());
100
-
101
- if (api.event?.on) {
102
- const unsub = api.event.on("session.next.model_switched", () => {
103
- setTuiModel(getTuiModel());
104
- });
105
- onCleanup(unsub);
106
- }
107
-
108
- const badge = createMemo(() => buildRoutingBadge(cfg(), tuiModel()));
109
-
110
- // Pulse the highlight color while auto-routing is active so the badge reads
111
- // as "live". Static gold when locked / single-pinned.
112
- const PULSE = ["#FFD86B", "#FFE9A6", "#F5C542"];
113
- const [pulse, setPulse] = createSignal(0);
114
- const timer = setInterval(() => setPulse((i) => (i + 1) % PULSE.length), 600);
115
- onCleanup(() => clearInterval(timer));
116
-
117
- const color = () => (badge().active ? PULSE[pulse()] : "#FFD86B");
118
- const render = () => (
119
- <text fg={color()}>{`${badge().glyph} ${badge().text}`}</text>
120
- );
121
-
122
- api.slots.register({
123
- slots: {
124
- // Right of the home prompt — routing badge
125
- home_prompt_right() {
126
- return render();
127
- },
128
-
129
- // Right of the session prompt — routing badge
130
- session_prompt_right(props: { session_id: string }) {
131
- return render();
132
- },
133
- },
134
- });
135
- };
136
-
137
- // The TUI plugin loader reads `mod.default` (strict mode) and requires an object
138
- // with a tui() function — a bare `export const tui` is silently skipped.
1
+ import { createSignal, onCleanup, createMemo } from "solid-js";
2
+ import { existsSync, readFileSync } from "fs";
3
+ import { homedir } from "os";
4
+ import { join } from "path";
5
+
6
+ // ── Config reader (no external deps) ───────────────────────────────────────
7
+
8
+ interface InfinicodeConfig {
9
+ masterUrl: string;
10
+ defaultModel: string;
11
+ policy?: string;
12
+ workerModels?: Record<string, { providerId: string; modelId: string }>;
13
+ cloudProviders?: Array<{ id: string; name: string; enabled: boolean; apiKey?: string }>;
14
+ }
15
+
16
+ function findConfigPath(): string {
17
+ const home = homedir();
18
+ const paths = [
19
+ // Windows — Conf appends "-nodejs" for Node projects
20
+ join(home, "AppData", "Roaming", "infinicode-nodejs", "Config", "config.json"),
21
+ join(home, "AppData", "Roaming", "infinicode", "Config", "config.json"),
22
+ // Linux
23
+ join(home, ".config", "infinicode", "config.json"),
24
+ // macOS
25
+ join(home, "Library", "Preferences", "infinicode", "config.json"),
26
+ ];
27
+ for (const p of paths) {
28
+ if (existsSync(p)) return p;
29
+ }
30
+ return paths[0];
31
+ }
32
+
33
+ function loadConfig(): InfinicodeConfig | null {
34
+ try {
35
+ const path = findConfigPath();
36
+ if (!existsSync(path)) return null;
37
+ return JSON.parse(readFileSync(path, "utf-8")) as InfinicodeConfig;
38
+ } catch {
39
+ return null;
40
+ }
41
+ }
42
+
43
+ // ── Routing info ───────────────────────────────────────────────────────────
44
+
45
+ // Every policy auto-routes across providers except `offline`, which is locked
46
+ // to the local machine — mirrors the kernel router's one locked mode.
47
+ const LOCKED_POLICIES = new Set(["offline"]);
48
+
49
+ interface RoutingBadge {
50
+ glyph: string;
51
+ text: string;
52
+ /** true = actively auto-routing (drives the pulse highlight). */
53
+ active: boolean;
54
+ }
55
+
56
+ function buildRoutingBadge(cfg: InfinicodeConfig | null, tuiModel?: string): RoutingBadge {
57
+ if (!cfg) return { glyph: "◆", text: "AUTO (no config)", active: true };
58
+
59
+ const enabledCloud = (cfg.cloudProviders ?? []).filter((p) => p.enabled && p.apiKey);
60
+ const providerCount = 1 + enabledCloud.length; // ollama + cloud
61
+ const pinned = Object.keys(cfg.workerModels ?? {}).length;
62
+ const policy = cfg.policy ?? "balanced";
63
+ const codingPin = cfg.workerModels?.["coding"];
64
+ const autoModel = codingPin
65
+ ? `${codingPin.providerId}/${codingPin.modelId}`
66
+ : `ollama/${cfg.defaultModel}`;
67
+
68
+ // If the user manually pinned a model in the TUI, that overrides routing.
69
+ if (tuiModel && tuiModel !== autoModel) {
70
+ const [prov, ...rest] = tuiModel.split("/");
71
+ return { glyph: "◇", text: `SINGLE ${prov}/${rest.join("/")}`, active: false };
72
+ }
73
+
74
+ // The one locked mode: offline stays local-only.
75
+ if (LOCKED_POLICIES.has(policy)) {
76
+ return { glyph: "🔒", text: `LOCKED ${policy} (local only)`, active: false };
77
+ }
78
+
79
+ return {
80
+ glyph: "◆",
81
+ text: `AUTO policy=${policy} providers=${providerCount} pins=${pinned}`,
82
+ active: true,
83
+ };
84
+ }
85
+
86
+ // ── Plugin ──────────────────────────────────────────────────────────────────
87
+
88
+ const tui = async (api: any) => {
89
+ const [cfg] = createSignal(loadConfig());
90
+
91
+ const getTuiModel = (): string | undefined => {
92
+ try {
93
+ return api.state?.config?.model;
94
+ } catch {
95
+ return undefined;
96
+ }
97
+ };
98
+
99
+ const [tuiModel, setTuiModel] = createSignal<string | undefined>(getTuiModel());
100
+
101
+ if (api.event?.on) {
102
+ const unsub = api.event.on("session.next.model_switched", () => {
103
+ setTuiModel(getTuiModel());
104
+ });
105
+ onCleanup(unsub);
106
+ }
107
+
108
+ const badge = createMemo(() => buildRoutingBadge(cfg(), tuiModel()));
109
+
110
+ // Pulse the highlight color while auto-routing is active so the badge reads
111
+ // as "live". Static gold when locked / single-pinned.
112
+ const PULSE = ["#FFD86B", "#FFE9A6", "#F5C542"];
113
+ const [pulse, setPulse] = createSignal(0);
114
+ const timer = setInterval(() => setPulse((i) => (i + 1) % PULSE.length), 600);
115
+ onCleanup(() => clearInterval(timer));
116
+
117
+ const color = () => (badge().active ? PULSE[pulse()] : "#FFD86B");
118
+ const render = () => (
119
+ <text fg={color()}>{`${badge().glyph} ${badge().text}`}</text>
120
+ );
121
+
122
+ api.slots.register({
123
+ slots: {
124
+ // Right of the home prompt — routing badge
125
+ home_prompt_right() {
126
+ return render();
127
+ },
128
+
129
+ // Right of the session prompt — routing badge
130
+ session_prompt_right(props: { session_id: string }) {
131
+ return render();
132
+ },
133
+ },
134
+ });
135
+ };
136
+
137
+ // The TUI plugin loader reads `mod.default` (strict mode) and requires an object
138
+ // with a tui() function — a bare `export const tui` is silently skipped.
139
139
  export default { id: "infinicode-routing-mode", tui };