@valni/cli-darwin-x64 0.0.0-stage → 0.1.3
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/LICENSE +52 -0
- package/package.json +18 -5
- package/runtime/LICENSE +52 -0
- package/runtime/assets/clankolas.png +0 -0
- package/runtime/examples/extensions/auto-commit-on-exit.ts +49 -0
- package/runtime/examples/extensions/bash-spawn-hook.ts +30 -0
- package/runtime/examples/extensions/bookmark.ts +50 -0
- package/runtime/examples/extensions/border-status-editor.ts +145 -0
- package/runtime/examples/extensions/built-in-tool-renderer.ts +249 -0
- package/runtime/examples/extensions/claude-rules.ts +86 -0
- package/runtime/examples/extensions/commands.ts +72 -0
- package/runtime/examples/extensions/confirm-destructive.ts +59 -0
- package/runtime/examples/extensions/custom-footer.ts +64 -0
- package/runtime/examples/extensions/custom-header.ts +73 -0
- package/runtime/examples/extensions/custom-provider-anthropic/index.ts +604 -0
- package/runtime/examples/extensions/custom-provider-anthropic/package-lock.json +24 -0
- package/runtime/examples/extensions/custom-provider-anthropic/package.json +19 -0
- package/runtime/examples/extensions/dirty-repo-guard.ts +56 -0
- package/runtime/examples/extensions/doom-overlay/doom/build/doom.js +21 -0
- package/runtime/examples/extensions/doom-overlay/doom/build/doom.wasm +0 -0
- package/runtime/examples/extensions/doom-overlay/doom/build.sh +152 -0
- package/runtime/examples/extensions/doom-overlay/doom/doomgeneric_valni.c +72 -0
- package/runtime/examples/extensions/doom-overlay/doom-component.ts +132 -0
- package/runtime/examples/extensions/doom-overlay/doom-engine.ts +173 -0
- package/runtime/examples/extensions/doom-overlay/doom-keys.ts +104 -0
- package/runtime/examples/extensions/doom-overlay/index.ts +74 -0
- package/runtime/examples/extensions/doom-overlay/wad-finder.ts +51 -0
- package/runtime/examples/extensions/dynamic-resources/dynamic.json +78 -0
- package/runtime/examples/extensions/dynamic-resources/index.ts +13 -0
- package/runtime/examples/extensions/dynamic-tools.ts +74 -0
- package/runtime/examples/extensions/entry-renderer.ts +41 -0
- package/runtime/examples/extensions/event-bus.ts +43 -0
- package/runtime/examples/extensions/file-trigger.ts +41 -0
- package/runtime/examples/extensions/git-checkpoint.ts +53 -0
- package/runtime/examples/extensions/git-merge-and-resolve.ts +115 -0
- package/runtime/examples/extensions/github-issue-autocomplete.ts +185 -0
- package/runtime/examples/extensions/hello.ts +26 -0
- package/runtime/examples/extensions/hidden-thinking-label.ts +53 -0
- package/runtime/examples/extensions/inline-bash.ts +94 -0
- package/runtime/examples/extensions/input-transform-streaming.ts +39 -0
- package/runtime/examples/extensions/input-transform.ts +43 -0
- package/runtime/examples/extensions/interactive-shell.ts +196 -0
- package/runtime/examples/extensions/mac-system-theme.ts +47 -0
- package/runtime/examples/extensions/message-renderer.ts +59 -0
- package/runtime/examples/extensions/minimal-mode.ts +426 -0
- package/runtime/examples/extensions/modal-editor.ts +85 -0
- package/runtime/examples/extensions/model-status.ts +31 -0
- package/runtime/examples/extensions/notify.ts +55 -0
- package/runtime/examples/extensions/overlay-qa-tests.ts +1450 -0
- package/runtime/examples/extensions/overlay-test.ts +153 -0
- package/runtime/examples/extensions/permission-gate.ts +34 -0
- package/runtime/examples/extensions/pirate.ts +47 -0
- package/runtime/examples/extensions/plan-mode/index.ts +390 -0
- package/runtime/examples/extensions/plan-mode/utils.ts +168 -0
- package/runtime/examples/extensions/preset.ts +436 -0
- package/runtime/examples/extensions/project-trust.ts +64 -0
- package/runtime/examples/extensions/prompt-customizer.ts +97 -0
- package/runtime/examples/extensions/protected-paths.ts +30 -0
- package/runtime/examples/extensions/provider-payload.ts +18 -0
- package/runtime/examples/extensions/question.ts +278 -0
- package/runtime/examples/extensions/questionnaire.ts +440 -0
- package/runtime/examples/extensions/rainbow-editor.ts +88 -0
- package/runtime/examples/extensions/reload-runtime.ts +37 -0
- package/runtime/examples/extensions/rpc-demo.ts +118 -0
- package/runtime/examples/extensions/sandbox/index.ts +321 -0
- package/runtime/examples/extensions/sandbox/package-lock.json +92 -0
- package/runtime/examples/extensions/sandbox/package.json +19 -0
- package/runtime/examples/extensions/send-user-message.ts +97 -0
- package/runtime/examples/extensions/session-name.ts +27 -0
- package/runtime/examples/extensions/shutdown-command.ts +63 -0
- package/runtime/examples/extensions/snake.ts +343 -0
- package/runtime/examples/extensions/space-invaders.ts +560 -0
- package/runtime/examples/extensions/ssh.ts +220 -0
- package/runtime/examples/extensions/status-line.ts +32 -0
- package/runtime/examples/extensions/structured-output.ts +65 -0
- package/runtime/examples/extensions/subagent/agents.ts +126 -0
- package/runtime/examples/extensions/subagent/index.ts +1009 -0
- package/runtime/examples/extensions/system-prompt-header.ts +17 -0
- package/runtime/examples/extensions/tic-tac-toe.ts +1008 -0
- package/runtime/examples/extensions/timed-confirm.ts +70 -0
- package/runtime/examples/extensions/titlebar-spinner.ts +58 -0
- package/runtime/examples/extensions/todo.ts +297 -0
- package/runtime/examples/extensions/tool-override.ts +144 -0
- package/runtime/examples/extensions/tools.ts +146 -0
- package/runtime/examples/extensions/trigger-compact.ts +50 -0
- package/runtime/examples/extensions/truncated-tool.ts +195 -0
- package/runtime/examples/extensions/widget-placement.ts +9 -0
- package/runtime/examples/extensions/with-deps/index.ts +32 -0
- package/runtime/examples/extensions/with-deps/package-lock.json +31 -0
- package/runtime/examples/extensions/with-deps/package.json +22 -0
- package/runtime/examples/extensions/working-indicator.ts +123 -0
- package/runtime/examples/extensions/working-message-test.ts +25 -0
- package/runtime/examples/rpc-extension-ui.ts +632 -0
- package/runtime/examples/sdk/01-minimal.ts +26 -0
- package/runtime/examples/sdk/02-custom-model.ts +43 -0
- package/runtime/examples/sdk/03-custom-prompt.ts +70 -0
- package/runtime/examples/sdk/04-skills.ts +55 -0
- package/runtime/examples/sdk/05-tools.ts +48 -0
- package/runtime/examples/sdk/06-extensions.ts +94 -0
- package/runtime/examples/sdk/07-context-files.ts +42 -0
- package/runtime/examples/sdk/08-prompt-templates.ts +51 -0
- package/runtime/examples/sdk/09-api-keys-and-oauth.ts +34 -0
- package/runtime/examples/sdk/10-settings.ts +53 -0
- package/runtime/examples/sdk/11-sessions.ts +52 -0
- package/runtime/examples/sdk/12-full-control.ts +71 -0
- package/runtime/examples/sdk/13-session-runtime.ts +67 -0
- package/runtime/export-html/ansi-to-html.js +249 -0
- package/runtime/export-html/index.js +226 -0
- package/runtime/export-html/template.css +1066 -0
- package/runtime/export-html/template.html +55 -0
- package/runtime/export-html/template.js +1864 -0
- package/runtime/export-html/tool-renderer.js +108 -0
- package/runtime/export-html/vendor/highlight.min.js +1213 -0
- package/runtime/export-html/vendor/marked.min.js +78 -0
- package/runtime/extensions/pi-switchboard/.github/workflows/ci.yml +51 -0
- package/runtime/extensions/pi-switchboard/LICENSE +22 -0
- package/runtime/extensions/pi-switchboard/README.md +85 -0
- package/runtime/extensions/pi-switchboard/extensions/switchboard/catalog.ts +183 -0
- package/runtime/extensions/pi-switchboard/extensions/switchboard/config.ts +97 -0
- package/runtime/extensions/pi-switchboard/extensions/switchboard/connections.ts +386 -0
- package/runtime/extensions/pi-switchboard/extensions/switchboard/constants.ts +12 -0
- package/runtime/extensions/pi-switchboard/extensions/switchboard/device-auth.ts +127 -0
- package/runtime/extensions/pi-switchboard/extensions/switchboard/envelope.ts +199 -0
- package/runtime/extensions/pi-switchboard/extensions/switchboard/errors.ts +180 -0
- package/runtime/extensions/pi-switchboard/extensions/switchboard/index.ts +125 -0
- package/runtime/extensions/pi-switchboard/extensions/switchboard/participant.ts +17 -0
- package/runtime/extensions/pi-switchboard/extensions/switchboard/sessionStream.ts +131 -0
- package/runtime/extensions/pi-switchboard/extensions/switchboard/toolProxy.ts +136 -0
- package/runtime/extensions/pi-switchboard/extensions/switchboard/tools.ts +16 -0
- package/runtime/extensions/pi-switchboard/extensions/switchboard/types.ts +37 -0
- package/runtime/extensions/pi-switchboard/package.json +30 -0
- package/runtime/extensions/pi-switchboard/tsconfig.json +13 -0
- package/runtime/extensions/switchboard-local/.github/workflows/ci.yml +80 -0
- package/runtime/extensions/switchboard-local/.github/workflows/release.yml +46 -0
- package/runtime/extensions/switchboard-local/README.md +98 -0
- package/runtime/extensions/switchboard-local/extensions/local/engine.ts +28 -0
- package/runtime/extensions/switchboard-local/extensions/local/engines/gguf.ts +324 -0
- package/runtime/extensions/switchboard-local/extensions/local/engines/llamaBuild.ts +62 -0
- package/runtime/extensions/switchboard-local/extensions/local/engines/mlx.ts +245 -0
- package/runtime/extensions/switchboard-local/extensions/local/engines/select.ts +38 -0
- package/runtime/extensions/switchboard-local/extensions/local/engines/support.ts +151 -0
- package/runtime/extensions/switchboard-local/extensions/local/hf.ts +106 -0
- package/runtime/extensions/switchboard-local/extensions/local/index.ts +67 -0
- package/runtime/extensions/switchboard-local/extensions/local/nativeStream.ts +602 -0
- package/runtime/extensions/switchboard-local/extensions/local/paths.ts +44 -0
- package/runtime/extensions/switchboard-local/extensions/local/provider.ts +57 -0
- package/runtime/extensions/switchboard-local/extensions/local/store.ts +82 -0
- package/runtime/extensions/switchboard-local/extensions/local/ui.ts +60 -0
- package/runtime/extensions/switchboard-local/node_modules/@valni/switchboard/dist/apiTypes.js +74 -0
- package/runtime/extensions/switchboard-local/node_modules/@valni/switchboard/dist/client.js +292 -0
- package/runtime/extensions/switchboard-local/node_modules/@valni/switchboard/dist/deviceAuth.js +155 -0
- package/runtime/extensions/switchboard-local/node_modules/@valni/switchboard/dist/errors.js +95 -0
- package/runtime/extensions/switchboard-local/node_modules/@valni/switchboard/dist/index.js +8 -0
- package/runtime/extensions/switchboard-local/node_modules/@valni/switchboard/dist/messages.js +253 -0
- package/runtime/extensions/switchboard-local/node_modules/@valni/switchboard/dist/models.js +19 -0
- package/runtime/extensions/switchboard-local/node_modules/@valni/switchboard/dist/native.js +886 -0
- package/runtime/extensions/switchboard-local/node_modules/@valni/switchboard/dist/wire.js +85 -0
- package/runtime/extensions/switchboard-local/node_modules/@valni/switchboard/package.json +39 -0
- package/runtime/extensions/switchboard-local/node_modules/@valni/switchboard-native/package.json +29 -0
- package/runtime/extensions/switchboard-local/node_modules/@valni/switchboard-native/src/anthropic.ts +127 -0
- package/runtime/extensions/switchboard-local/node_modules/@valni/switchboard-native/src/google.ts +111 -0
- package/runtime/extensions/switchboard-local/node_modules/@valni/switchboard-native/src/index.ts +8 -0
- package/runtime/extensions/switchboard-local/node_modules/@valni/switchboard-native/src/modelRecord.test.ts +25 -0
- package/runtime/extensions/switchboard-local/node_modules/@valni/switchboard-native/src/modelRecord.ts +57 -0
- package/runtime/extensions/switchboard-local/node_modules/@valni/switchboard-native/src/openaiGeneric.ts +103 -0
- package/runtime/extensions/switchboard-local/node_modules/@valni/switchboard-native/src/openaiPro.ts +89 -0
- package/runtime/extensions/switchboard-local/node_modules/@valni/switchboard-native/src/profile.ts +120 -0
- package/runtime/extensions/switchboard-local/node_modules/@valni/switchboard-native/src/routing.test.ts +82 -0
- package/runtime/extensions/switchboard-local/node_modules/@valni/switchboard-native/src/routing.ts +74 -0
- package/runtime/extensions/switchboard-local/node_modules/@valni/switchboard-native/src/validateConfig.google.test.ts +137 -0
- package/runtime/extensions/switchboard-local/node_modules/@valni/switchboard-native/src/validateConfig.openai.test.ts +241 -0
- package/runtime/extensions/switchboard-local/node_modules/@valni/switchboard-native/src/validateConfig.test.ts +174 -0
- package/runtime/extensions/switchboard-local/node_modules/@valni/switchboard-native/src/validateConfig.ts +438 -0
- package/runtime/extensions/switchboard-local/package.json +34 -0
- package/runtime/extensions/switchboard-local/tsconfig.json +13 -0
- package/runtime/native/darwin/prebuilds/darwin-x64/darwin-modifiers.node +0 -0
- package/runtime/package.json +98 -0
- package/runtime/photon_rs_bg.wasm +0 -0
- package/runtime/theme/dark.json +86 -0
- package/runtime/theme/light.json +85 -0
- package/runtime/theme/theme-schema.json +340 -0
- package/runtime/valni +4 -0
- package/README.md +0 -3
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { spawn, spawnSync } from "node:child_process";
|
|
2
|
+
import { cpSync, readdirSync, statSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import type { ChildProcess } from "node:child_process";
|
|
5
|
+
|
|
6
|
+
export function run(
|
|
7
|
+
command: string,
|
|
8
|
+
args: readonly string[],
|
|
9
|
+
options: { cwd?: string; env?: NodeJS.ProcessEnv; signal?: AbortSignal } = {},
|
|
10
|
+
): Promise<void> {
|
|
11
|
+
return new Promise((resolve, reject) => {
|
|
12
|
+
const child = spawn(command, [...args], {
|
|
13
|
+
cwd: options.cwd,
|
|
14
|
+
env: options.env,
|
|
15
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
16
|
+
});
|
|
17
|
+
let stderr = "";
|
|
18
|
+
child.stderr?.on("data", (chunk: Buffer) => {
|
|
19
|
+
stderr = `${stderr}${chunk.toString()}`.slice(-4000);
|
|
20
|
+
});
|
|
21
|
+
const abort = () => child.kill("SIGTERM");
|
|
22
|
+
options.signal?.addEventListener("abort", abort, { once: true });
|
|
23
|
+
child.once("error", (error) => {
|
|
24
|
+
options.signal?.removeEventListener("abort", abort);
|
|
25
|
+
reject(error);
|
|
26
|
+
});
|
|
27
|
+
child.once("close", (code) => {
|
|
28
|
+
options.signal?.removeEventListener("abort", abort);
|
|
29
|
+
if (code === 0) return resolve();
|
|
30
|
+
const tail = stderr.trim().split("\n").slice(-4).join("\n");
|
|
31
|
+
reject(new Error(`${command} exited with code ${code}${tail ? `\n${tail}` : ""}`));
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function directorySize(path: string): number {
|
|
37
|
+
let total = 0;
|
|
38
|
+
let entries: string[];
|
|
39
|
+
try {
|
|
40
|
+
entries = readdirSync(path);
|
|
41
|
+
} catch {
|
|
42
|
+
return 0;
|
|
43
|
+
}
|
|
44
|
+
for (const entry of entries) {
|
|
45
|
+
const child = join(path, entry);
|
|
46
|
+
try {
|
|
47
|
+
const stats = statSync(child);
|
|
48
|
+
total += stats.isDirectory() ? directorySize(child) : stats.size;
|
|
49
|
+
} catch {
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return total;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function findFile(root: string, name: string): string | undefined {
|
|
57
|
+
let entries: string[];
|
|
58
|
+
try {
|
|
59
|
+
entries = readdirSync(root);
|
|
60
|
+
} catch {
|
|
61
|
+
return undefined;
|
|
62
|
+
}
|
|
63
|
+
for (const entry of entries) {
|
|
64
|
+
const path = join(root, entry);
|
|
65
|
+
let stats;
|
|
66
|
+
try {
|
|
67
|
+
stats = statSync(path);
|
|
68
|
+
} catch {
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
if (stats.isDirectory()) {
|
|
72
|
+
const found = findFile(path, name);
|
|
73
|
+
if (found !== undefined) return found;
|
|
74
|
+
} else if (entry === name) {
|
|
75
|
+
return path;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return undefined;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export async function healthy(url: string, timeoutMs = 1_000): Promise<boolean> {
|
|
82
|
+
try {
|
|
83
|
+
const response = await fetch(url, { signal: AbortSignal.timeout(timeoutMs) });
|
|
84
|
+
return response.ok;
|
|
85
|
+
} catch {
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function detachChild(child: ChildProcess): void {
|
|
91
|
+
for (const pipe of [child.stderr, child.stdout]) {
|
|
92
|
+
(pipe as { unref?: () => void } | null)?.unref?.();
|
|
93
|
+
}
|
|
94
|
+
child.unref();
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function copyDir(from: string, to: string): void {
|
|
98
|
+
cpSync(from, to, { recursive: true });
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export function killPid(pid: number, signal: NodeJS.Signals): void {
|
|
102
|
+
if (process.platform === "win32") {
|
|
103
|
+
try {
|
|
104
|
+
spawnSync("taskkill", ["/pid", String(pid), "/T", "/F"], { stdio: "ignore" });
|
|
105
|
+
} catch {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
try {
|
|
111
|
+
process.kill(-pid, signal);
|
|
112
|
+
} catch {
|
|
113
|
+
try {
|
|
114
|
+
process.kill(pid, signal);
|
|
115
|
+
} catch {
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export function killOnExit(release: () => number | undefined): void {
|
|
122
|
+
process.on("exit", () => {
|
|
123
|
+
const pid = release();
|
|
124
|
+
if (pid === undefined) return;
|
|
125
|
+
killPid(pid, "SIGKILL");
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export function watchSpawn(child: ChildProcess): { failure: () => Error | undefined } {
|
|
130
|
+
let failure: Error | undefined;
|
|
131
|
+
child.on("error", (error: Error) => {
|
|
132
|
+
failure = error;
|
|
133
|
+
});
|
|
134
|
+
return { failure: () => failure };
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export async function stopChild(child: ChildProcess, graceMs: number): Promise<void> {
|
|
138
|
+
if (child.exitCode !== null || child.pid === undefined) return;
|
|
139
|
+
const pid = child.pid;
|
|
140
|
+
killPid(pid, "SIGTERM");
|
|
141
|
+
await new Promise((resolve) => {
|
|
142
|
+
const timer = setTimeout(() => {
|
|
143
|
+
killPid(pid, "SIGKILL");
|
|
144
|
+
resolve(undefined);
|
|
145
|
+
}, graceMs);
|
|
146
|
+
child.once("exit", () => {
|
|
147
|
+
clearTimeout(timer);
|
|
148
|
+
resolve(undefined);
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
const HOSTS = ["huggingface.co", "hf.co"];
|
|
2
|
+
|
|
3
|
+
export function parseRepoId(input: string): string | null {
|
|
4
|
+
let text = input.trim();
|
|
5
|
+
for (const prefix of ["https://", "http://"]) {
|
|
6
|
+
if (text.toLowerCase().startsWith(prefix)) text = text.slice(prefix.length);
|
|
7
|
+
}
|
|
8
|
+
for (const host of HOSTS) {
|
|
9
|
+
if (text.toLowerCase().startsWith(`${host}/`)) text = text.slice(host.length + 1);
|
|
10
|
+
}
|
|
11
|
+
const cut = text.search(/[?#]/u);
|
|
12
|
+
if (cut !== -1) text = text.slice(0, cut);
|
|
13
|
+
text = text.replace(/^\/+|\/+$/gu, "");
|
|
14
|
+
if (text.includes(" ")) return null;
|
|
15
|
+
const parts = text.split("/");
|
|
16
|
+
if (parts.length !== 2) return null;
|
|
17
|
+
if (!parts[0] || !parts[1]) return null;
|
|
18
|
+
return text;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface ModelRef {
|
|
22
|
+
repoId: string;
|
|
23
|
+
quant?: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function parseModelRef(input: string): ModelRef | null {
|
|
27
|
+
const trimmed = input.trim().replace(/\/+$/u, "");
|
|
28
|
+
const colon = trimmed.lastIndexOf(":");
|
|
29
|
+
const slash = trimmed.lastIndexOf("/");
|
|
30
|
+
if (colon > slash && colon !== -1) {
|
|
31
|
+
const repoId = parseRepoId(trimmed.slice(0, colon));
|
|
32
|
+
let quant = trimmed.slice(colon + 1).trim();
|
|
33
|
+
const cut = quant.search(/[?#]/u);
|
|
34
|
+
if (cut !== -1) quant = quant.slice(0, cut);
|
|
35
|
+
quant = quant.replace(/\/+$/u, "").trim();
|
|
36
|
+
if (repoId === null || !quant) return null;
|
|
37
|
+
return { repoId, quant: quant.toUpperCase() };
|
|
38
|
+
}
|
|
39
|
+
const repoId = parseRepoId(trimmed);
|
|
40
|
+
return repoId === null ? null : { repoId };
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface RepoFile {
|
|
44
|
+
path: string;
|
|
45
|
+
size: number;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export async function repoFiles(repoId: string, signal?: AbortSignal): Promise<RepoFile[]> {
|
|
49
|
+
const encoded = repoId.split("/").map(encodeURIComponent).join("/");
|
|
50
|
+
const url = `https://huggingface.co/api/models/${encoded}/tree/main?recursive=1`;
|
|
51
|
+
const timeout = AbortSignal.timeout(20_000);
|
|
52
|
+
const response = await fetch(url, { signal: signal ? AbortSignal.any([signal, timeout]) : timeout });
|
|
53
|
+
if (!response.ok) {
|
|
54
|
+
if (response.status === 401 || response.status === 404) {
|
|
55
|
+
throw new Error(`${repoId} was not found on Hugging Face, or it is gated`);
|
|
56
|
+
}
|
|
57
|
+
throw new Error(`Hugging Face returned HTTP ${response.status} for ${repoId}`);
|
|
58
|
+
}
|
|
59
|
+
const payload: unknown = await response.json();
|
|
60
|
+
if (!Array.isArray(payload)) throw new Error("Hugging Face returned an unexpected tree listing");
|
|
61
|
+
return payload.flatMap((value) => {
|
|
62
|
+
if (typeof value !== "object" || value === null) return [];
|
|
63
|
+
const entry = value as { path?: unknown; size?: unknown; type?: unknown };
|
|
64
|
+
if (entry.type !== "file" || typeof entry.path !== "string") return [];
|
|
65
|
+
return [{ path: entry.path, size: typeof entry.size === "number" ? entry.size : 0 }];
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function totalBytes(files: readonly RepoFile[]): number {
|
|
70
|
+
return files.reduce((sum, file) => sum + file.size, 0);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function looksLikeMlxRepo(files: readonly RepoFile[]): boolean {
|
|
74
|
+
const names = new Set(files.map((file) => file.path));
|
|
75
|
+
return names.has("config.json") && [...names].some((name) => name.endsWith(".safetensors"));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const QUANT_PATTERN =
|
|
79
|
+
/(?:^|[-_.])((?:UD-)?(?:IQ\d(?:_[A-Z0-9]+)+|Q\d(?:_[A-Z0-9]+)+|BF16|F16|F32|MXFP\d(?:_[A-Z0-9]+)*))$/iu;
|
|
80
|
+
const SHARD_PATTERN = /-\d{5}-of-\d{5}$/u;
|
|
81
|
+
const PREFERRED_QUANT = "Q4_K_M";
|
|
82
|
+
|
|
83
|
+
export interface Quantization {
|
|
84
|
+
name: string;
|
|
85
|
+
size: number;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export function ggufQuants(files: readonly RepoFile[]): Quantization[] {
|
|
89
|
+
const sizes = new Map<string, number>();
|
|
90
|
+
for (const file of files) {
|
|
91
|
+
const name = file.path.split("/").at(-1) ?? "";
|
|
92
|
+
if (!name.toLowerCase().endsWith(".gguf")) continue;
|
|
93
|
+
if (name.toLowerCase().startsWith("mmproj")) continue;
|
|
94
|
+
const stem = name.slice(0, -5).replace(SHARD_PATTERN, "");
|
|
95
|
+
const quant = stem.match(QUANT_PATTERN)?.[1]?.toUpperCase();
|
|
96
|
+
if (!quant) continue;
|
|
97
|
+
sizes.set(quant, (sizes.get(quant) ?? 0) + file.size);
|
|
98
|
+
}
|
|
99
|
+
return [...sizes]
|
|
100
|
+
.map(([name, size]) => ({ name, size }))
|
|
101
|
+
.sort((left, right) => {
|
|
102
|
+
if (left.name === PREFERRED_QUANT) return -1;
|
|
103
|
+
if (right.name === PREFERRED_QUANT) return 1;
|
|
104
|
+
return left.size - right.size || left.name.localeCompare(right.name);
|
|
105
|
+
});
|
|
106
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import type { ExtensionAPI, ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
|
|
5
|
+
import { streamLocalModel } from "./nativeStream.ts";
|
|
6
|
+
import type { LocalEngine } from "./engine.ts";
|
|
7
|
+
import { GgufEngine } from "./engines/gguf.ts";
|
|
8
|
+
import { MlxEngine } from "./engines/mlx.ts";
|
|
9
|
+
import { PROVIDER_ID, PROVIDER_NAME, toModelConfigs } from "./provider.ts";
|
|
10
|
+
import { addModel, showMenu } from "./ui.ts";
|
|
11
|
+
|
|
12
|
+
const COMMAND = "switchboard-local";
|
|
13
|
+
|
|
14
|
+
function extensionVersion(): string {
|
|
15
|
+
try {
|
|
16
|
+
const packagePath = join(dirname(fileURLToPath(import.meta.url)), "..", "..", "package.json");
|
|
17
|
+
return (JSON.parse(readFileSync(packagePath, "utf8")) as { version: string }).version;
|
|
18
|
+
} catch {
|
|
19
|
+
return "unknown";
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async function readyModels(engines: readonly LocalEngine[]): Promise<ReturnType<typeof toModelConfigs>> {
|
|
24
|
+
const listed = await Promise.all(
|
|
25
|
+
engines.map(async (engine) => {
|
|
26
|
+
if (!engine.supported() || !engine.isInstalled()) return [];
|
|
27
|
+
try {
|
|
28
|
+
if (!engine.isRunning()) await engine.ensureReady(() => undefined);
|
|
29
|
+
return await engine.listModels();
|
|
30
|
+
} catch (error) {
|
|
31
|
+
console.error(`switchboard-local: the ${engine.label} engine could not list models`, error);
|
|
32
|
+
return [];
|
|
33
|
+
}
|
|
34
|
+
}),
|
|
35
|
+
);
|
|
36
|
+
return toModelConfigs(listed.flat());
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export default async function (pi: ExtensionAPI) {
|
|
40
|
+
console.error(`switchboard-local v${extensionVersion()}`);
|
|
41
|
+
const engines: LocalEngine[] = [new MlxEngine(), new GgufEngine()];
|
|
42
|
+
|
|
43
|
+
pi.registerCommand(COMMAND, {
|
|
44
|
+
description: "Download and manage local models",
|
|
45
|
+
handler: async (args: string, ctx: ExtensionCommandContext) => {
|
|
46
|
+
if (args.trim()) {
|
|
47
|
+
await addModel(engines, ctx, args);
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
await showMenu(engines, ctx);
|
|
51
|
+
},
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
pi.on("session_shutdown", async () => {
|
|
55
|
+
await Promise.all(engines.map((engine) => engine.stop()));
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
pi.registerProvider(PROVIDER_ID, {
|
|
59
|
+
name: PROVIDER_NAME,
|
|
60
|
+
baseUrl: "http://127.0.0.1/v1",
|
|
61
|
+
api: "openai-completions",
|
|
62
|
+
apiKey: "local",
|
|
63
|
+
streamSimple: streamLocalModel,
|
|
64
|
+
models: [],
|
|
65
|
+
refreshModels: async () => await readyModels(engines),
|
|
66
|
+
});
|
|
67
|
+
}
|