@sheepbun/yips 0.1.1
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/dist/agent/commands/command-catalog.js +243 -0
- package/dist/agent/commands/commands.js +418 -0
- package/dist/agent/conductor.js +118 -0
- package/dist/agent/context/code-context.js +68 -0
- package/dist/agent/context/memory-store.js +159 -0
- package/dist/agent/context/session-store.js +211 -0
- package/dist/agent/protocol/tool-protocol.js +160 -0
- package/dist/agent/skills/skills.js +327 -0
- package/dist/agent/tools/tool-executor.js +415 -0
- package/dist/agent/tools/tool-safety.js +52 -0
- package/dist/app/index.js +35 -0
- package/dist/app/repl.js +105 -0
- package/dist/app/update-check.js +132 -0
- package/dist/app/version.js +51 -0
- package/dist/code-context.js +68 -0
- package/dist/colors.js +204 -0
- package/dist/command-catalog.js +242 -0
- package/dist/commands.js +350 -0
- package/dist/conductor.js +94 -0
- package/dist/config/config.js +335 -0
- package/dist/config/hooks.js +187 -0
- package/dist/config.js +335 -0
- package/dist/downloader-state.js +302 -0
- package/dist/downloader-ui.js +289 -0
- package/dist/gateway/adapters/discord.js +108 -0
- package/dist/gateway/adapters/formatting.js +96 -0
- package/dist/gateway/adapters/telegram.js +106 -0
- package/dist/gateway/adapters/types.js +2 -0
- package/dist/gateway/adapters/whatsapp.js +124 -0
- package/dist/gateway/auth-policy.js +66 -0
- package/dist/gateway/core.js +87 -0
- package/dist/gateway/headless-conductor.js +328 -0
- package/dist/gateway/message-router.js +23 -0
- package/dist/gateway/rate-limiter.js +48 -0
- package/dist/gateway/runtime/backend-policy.js +18 -0
- package/dist/gateway/runtime/discord-bot.js +104 -0
- package/dist/gateway/runtime/discord-main.js +69 -0
- package/dist/gateway/session-manager.js +77 -0
- package/dist/gateway/types.js +2 -0
- package/dist/hardware.js +92 -0
- package/dist/hooks.js +187 -0
- package/dist/index.js +34 -0
- package/dist/input-engine.js +250 -0
- package/dist/llama-client.js +227 -0
- package/dist/llama-server.js +620 -0
- package/dist/llm/llama-client.js +227 -0
- package/dist/llm/llama-server.js +620 -0
- package/dist/llm/token-counter.js +47 -0
- package/dist/memory-store.js +159 -0
- package/dist/messages.js +59 -0
- package/dist/model-downloader.js +382 -0
- package/dist/model-manager-state.js +118 -0
- package/dist/model-manager-ui.js +194 -0
- package/dist/model-manager.js +190 -0
- package/dist/models/hardware.js +92 -0
- package/dist/models/model-downloader.js +382 -0
- package/dist/models/model-manager.js +190 -0
- package/dist/prompt-box.js +78 -0
- package/dist/prompt-composer.js +498 -0
- package/dist/repl.js +105 -0
- package/dist/session-store.js +211 -0
- package/dist/spinner.js +76 -0
- package/dist/title-box.js +388 -0
- package/dist/token-counter.js +47 -0
- package/dist/tool-executor.js +415 -0
- package/dist/tool-protocol.js +121 -0
- package/dist/tool-safety.js +52 -0
- package/dist/tui/app.js +2553 -0
- package/dist/tui/startup.js +56 -0
- package/dist/tui-input-routing.js +53 -0
- package/dist/tui.js +51 -0
- package/dist/types/app-types.js +2 -0
- package/dist/types.js +2 -0
- package/dist/ui/colors.js +204 -0
- package/dist/ui/downloader/downloader-state.js +302 -0
- package/dist/ui/downloader/downloader-ui.js +289 -0
- package/dist/ui/input/input-engine.js +250 -0
- package/dist/ui/input/tui-input-routing.js +53 -0
- package/dist/ui/input/vt-session.js +168 -0
- package/dist/ui/messages.js +59 -0
- package/dist/ui/model-manager/model-manager-state.js +118 -0
- package/dist/ui/model-manager/model-manager-ui.js +194 -0
- package/dist/ui/prompt/prompt-box.js +78 -0
- package/dist/ui/prompt/prompt-composer.js +498 -0
- package/dist/ui/spinner.js +76 -0
- package/dist/ui/title-box.js +388 -0
- package/dist/ui/tui/app.js +6 -0
- package/dist/ui/tui/autocomplete.js +85 -0
- package/dist/ui/tui/constants.js +18 -0
- package/dist/ui/tui/history.js +29 -0
- package/dist/ui/tui/layout.js +341 -0
- package/dist/ui/tui/runtime-core.js +2584 -0
- package/dist/ui/tui/runtime-utils.js +53 -0
- package/dist/ui/tui/start-tui.js +54 -0
- package/dist/ui/tui/startup.js +56 -0
- package/dist/version.js +51 -0
- package/dist/vt-session.js +168 -0
- package/install.sh +457 -0
- package/package.json +128 -0
package/dist/config.js
ADDED
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CONFIG_PATH_ENV_VAR = exports.DEFAULT_CONFIG_PATH = void 0;
|
|
4
|
+
exports.getDefaultConfig = getDefaultConfig;
|
|
5
|
+
exports.resolveConfigPath = resolveConfigPath;
|
|
6
|
+
exports.mergeConfig = mergeConfig;
|
|
7
|
+
exports.loadConfig = loadConfig;
|
|
8
|
+
exports.saveConfig = saveConfig;
|
|
9
|
+
exports.updateConfig = updateConfig;
|
|
10
|
+
const node_fs_1 = require("node:fs");
|
|
11
|
+
const promises_1 = require("node:fs/promises");
|
|
12
|
+
const node_os_1 = require("node:os");
|
|
13
|
+
const node_path_1 = require("node:path");
|
|
14
|
+
const SUPPORTED_BACKENDS = new Set(["llamacpp", "claude"]);
|
|
15
|
+
const PORT_CONFLICT_POLICIES = new Set([
|
|
16
|
+
"fail",
|
|
17
|
+
"kill-llama",
|
|
18
|
+
"kill-user"
|
|
19
|
+
]);
|
|
20
|
+
const SUPPORTED_HOOKS = [
|
|
21
|
+
"on-session-start",
|
|
22
|
+
"on-session-end",
|
|
23
|
+
"on-file-write",
|
|
24
|
+
"on-file-read",
|
|
25
|
+
"pre-commit"
|
|
26
|
+
];
|
|
27
|
+
const DEFAULT_HOOK_TIMEOUT_MS = 10_000;
|
|
28
|
+
const MAX_HOOK_TIMEOUT_MS = 120_000;
|
|
29
|
+
exports.DEFAULT_CONFIG_PATH = ".yips_config.json";
|
|
30
|
+
exports.CONFIG_PATH_ENV_VAR = "YIPS_CONFIG_PATH";
|
|
31
|
+
function getDefaultConfig() {
|
|
32
|
+
const llamaHost = "127.0.0.1";
|
|
33
|
+
const llamaPort = 8080;
|
|
34
|
+
return {
|
|
35
|
+
streaming: true,
|
|
36
|
+
verbose: false,
|
|
37
|
+
backend: "llamacpp",
|
|
38
|
+
llamaBaseUrl: buildBaseUrl(llamaHost, llamaPort),
|
|
39
|
+
llamaServerPath: "",
|
|
40
|
+
llamaModelsDir: (0, node_path_1.resolve)((0, node_os_1.homedir)(), ".yips", "models"),
|
|
41
|
+
llamaHost,
|
|
42
|
+
llamaPort,
|
|
43
|
+
llamaContextSize: 8192,
|
|
44
|
+
llamaGpuLayers: 999,
|
|
45
|
+
llamaAutoStart: true,
|
|
46
|
+
llamaPortConflictPolicy: "kill-user",
|
|
47
|
+
model: "default",
|
|
48
|
+
tokensMode: "auto",
|
|
49
|
+
tokensManualMax: 8192,
|
|
50
|
+
nicknames: {},
|
|
51
|
+
hooks: {}
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
function resolveConfigPath(configPath = exports.DEFAULT_CONFIG_PATH) {
|
|
55
|
+
if (configPath === exports.DEFAULT_CONFIG_PATH) {
|
|
56
|
+
const envPath = process.env[exports.CONFIG_PATH_ENV_VAR]?.trim();
|
|
57
|
+
if (envPath && envPath.length > 0) {
|
|
58
|
+
return (0, node_path_1.resolve)(envPath);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return (0, node_path_1.resolve)(process.cwd(), configPath);
|
|
62
|
+
}
|
|
63
|
+
function isRecord(value) {
|
|
64
|
+
return typeof value === "object" && value !== null;
|
|
65
|
+
}
|
|
66
|
+
function normalizeBoolean(value, fallback) {
|
|
67
|
+
return typeof value === "boolean" ? value : fallback;
|
|
68
|
+
}
|
|
69
|
+
function normalizeBackend(value, fallback) {
|
|
70
|
+
if (typeof value === "string" && SUPPORTED_BACKENDS.has(value)) {
|
|
71
|
+
return value;
|
|
72
|
+
}
|
|
73
|
+
return fallback;
|
|
74
|
+
}
|
|
75
|
+
function normalizeModel(value, fallback) {
|
|
76
|
+
if (typeof value !== "string") {
|
|
77
|
+
return fallback;
|
|
78
|
+
}
|
|
79
|
+
const trimmed = value.trim();
|
|
80
|
+
return trimmed.length > 0 ? trimmed : fallback;
|
|
81
|
+
}
|
|
82
|
+
function normalizeBaseUrl(value, fallback) {
|
|
83
|
+
if (typeof value !== "string") {
|
|
84
|
+
return fallback;
|
|
85
|
+
}
|
|
86
|
+
const trimmed = value.trim();
|
|
87
|
+
if (trimmed.length === 0) {
|
|
88
|
+
return fallback;
|
|
89
|
+
}
|
|
90
|
+
try {
|
|
91
|
+
const parsed = new URL(trimmed);
|
|
92
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
93
|
+
return fallback;
|
|
94
|
+
}
|
|
95
|
+
return parsed.toString().replace(/\/$/, "");
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
return fallback;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
function parseBoolean(value, fallback) {
|
|
102
|
+
if (value === undefined) {
|
|
103
|
+
return fallback;
|
|
104
|
+
}
|
|
105
|
+
const normalized = value.trim().toLowerCase();
|
|
106
|
+
if (normalized === "1" || normalized === "true" || normalized === "yes" || normalized === "on") {
|
|
107
|
+
return true;
|
|
108
|
+
}
|
|
109
|
+
if (normalized === "0" || normalized === "false" || normalized === "no" || normalized === "off") {
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
return fallback;
|
|
113
|
+
}
|
|
114
|
+
function normalizeString(value, fallback) {
|
|
115
|
+
if (typeof value !== "string") {
|
|
116
|
+
return fallback;
|
|
117
|
+
}
|
|
118
|
+
const trimmed = value.trim();
|
|
119
|
+
return trimmed.length > 0 ? trimmed : fallback;
|
|
120
|
+
}
|
|
121
|
+
function normalizeHost(value, fallback) {
|
|
122
|
+
return normalizeString(value, fallback);
|
|
123
|
+
}
|
|
124
|
+
function normalizePort(value, fallback) {
|
|
125
|
+
if (typeof value === "number" && Number.isInteger(value) && value > 0 && value <= 65535) {
|
|
126
|
+
return value;
|
|
127
|
+
}
|
|
128
|
+
if (typeof value === "string") {
|
|
129
|
+
const parsed = Number(value.trim());
|
|
130
|
+
if (Number.isInteger(parsed) && parsed > 0 && parsed <= 65535) {
|
|
131
|
+
return parsed;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return fallback;
|
|
135
|
+
}
|
|
136
|
+
function normalizePositiveInt(value, fallback) {
|
|
137
|
+
if (typeof value === "number" && Number.isInteger(value) && value > 0) {
|
|
138
|
+
return value;
|
|
139
|
+
}
|
|
140
|
+
if (typeof value === "string") {
|
|
141
|
+
const parsed = Number(value.trim());
|
|
142
|
+
if (Number.isInteger(parsed) && parsed > 0) {
|
|
143
|
+
return parsed;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return fallback;
|
|
147
|
+
}
|
|
148
|
+
function normalizePositiveIntWithMax(value, fallback, max) {
|
|
149
|
+
const normalized = normalizePositiveInt(value, fallback);
|
|
150
|
+
return Math.min(normalized, max);
|
|
151
|
+
}
|
|
152
|
+
function normalizePortConflictPolicy(value, fallback) {
|
|
153
|
+
if (typeof value === "string" && PORT_CONFLICT_POLICIES.has(value)) {
|
|
154
|
+
return value;
|
|
155
|
+
}
|
|
156
|
+
return fallback;
|
|
157
|
+
}
|
|
158
|
+
function normalizeTokensMode(value, fallback) {
|
|
159
|
+
if (value === "auto" || value === "manual") {
|
|
160
|
+
return value;
|
|
161
|
+
}
|
|
162
|
+
return fallback;
|
|
163
|
+
}
|
|
164
|
+
function buildBaseUrl(host, port) {
|
|
165
|
+
return `http://${host}:${port}`;
|
|
166
|
+
}
|
|
167
|
+
function parseBaseUrlHostPort(baseUrl) {
|
|
168
|
+
try {
|
|
169
|
+
const parsed = new URL(baseUrl);
|
|
170
|
+
if (!parsed.hostname || !parsed.port) {
|
|
171
|
+
return null;
|
|
172
|
+
}
|
|
173
|
+
const port = Number(parsed.port);
|
|
174
|
+
if (!Number.isInteger(port) || port <= 0 || port > 65535) {
|
|
175
|
+
return null;
|
|
176
|
+
}
|
|
177
|
+
return {
|
|
178
|
+
host: parsed.hostname,
|
|
179
|
+
port
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
catch {
|
|
183
|
+
return null;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
function normalizeNicknames(value, fallback) {
|
|
187
|
+
if (!isRecord(value)) {
|
|
188
|
+
return { ...fallback };
|
|
189
|
+
}
|
|
190
|
+
const next = {};
|
|
191
|
+
for (const [key, nickname] of Object.entries(value)) {
|
|
192
|
+
if (typeof key !== "string" || typeof nickname !== "string") {
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
195
|
+
const trimmedKey = key.trim();
|
|
196
|
+
const trimmedNickname = nickname.trim();
|
|
197
|
+
if (trimmedKey.length === 0 || trimmedNickname.length === 0) {
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
next[trimmedKey] = trimmedNickname;
|
|
201
|
+
}
|
|
202
|
+
return next;
|
|
203
|
+
}
|
|
204
|
+
function normalizeHookConfig(value) {
|
|
205
|
+
if (!isRecord(value)) {
|
|
206
|
+
return null;
|
|
207
|
+
}
|
|
208
|
+
const command = normalizeString(value.command, "");
|
|
209
|
+
if (command.length === 0) {
|
|
210
|
+
return null;
|
|
211
|
+
}
|
|
212
|
+
const timeoutMs = normalizePositiveIntWithMax(value.timeoutMs, DEFAULT_HOOK_TIMEOUT_MS, MAX_HOOK_TIMEOUT_MS);
|
|
213
|
+
return { command, timeoutMs };
|
|
214
|
+
}
|
|
215
|
+
function normalizeHooks(value, fallback) {
|
|
216
|
+
const next = {};
|
|
217
|
+
for (const hookName of SUPPORTED_HOOKS) {
|
|
218
|
+
const existing = fallback[hookName];
|
|
219
|
+
if (existing) {
|
|
220
|
+
next[hookName] = { ...existing };
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
if (!isRecord(value)) {
|
|
224
|
+
return next;
|
|
225
|
+
}
|
|
226
|
+
for (const hookName of SUPPORTED_HOOKS) {
|
|
227
|
+
const normalized = normalizeHookConfig(value[hookName]);
|
|
228
|
+
if (normalized) {
|
|
229
|
+
next[hookName] = normalized;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
return next;
|
|
233
|
+
}
|
|
234
|
+
function applyEnvOverrides(config) {
|
|
235
|
+
const envHost = normalizeHost(process.env["YIPS_LLAMA_HOST"], config.llamaHost);
|
|
236
|
+
const envPort = normalizePort(process.env["YIPS_LLAMA_PORT"], config.llamaPort);
|
|
237
|
+
const envBaseUrl = normalizeBaseUrl(process.env["YIPS_LLAMA_BASE_URL"], buildBaseUrl(envHost, envPort));
|
|
238
|
+
const envBaseUrlHostPort = parseBaseUrlHostPort(envBaseUrl);
|
|
239
|
+
return {
|
|
240
|
+
...config,
|
|
241
|
+
llamaHost: envBaseUrlHostPort?.host ?? envHost,
|
|
242
|
+
llamaPort: envBaseUrlHostPort?.port ?? envPort,
|
|
243
|
+
llamaBaseUrl: envBaseUrl,
|
|
244
|
+
llamaServerPath: normalizeString(process.env["YIPS_LLAMA_SERVER_PATH"], config.llamaServerPath),
|
|
245
|
+
llamaModelsDir: normalizeString(process.env["YIPS_LLAMA_MODELS_DIR"], config.llamaModelsDir),
|
|
246
|
+
llamaContextSize: normalizePositiveInt(process.env["YIPS_LLAMA_CONTEXT_SIZE"], config.llamaContextSize),
|
|
247
|
+
llamaGpuLayers: normalizePositiveInt(process.env["YIPS_LLAMA_GPU_LAYERS"], config.llamaGpuLayers),
|
|
248
|
+
llamaAutoStart: parseBoolean(process.env["YIPS_LLAMA_AUTO_START"], config.llamaAutoStart),
|
|
249
|
+
llamaPortConflictPolicy: normalizePortConflictPolicy(process.env["YIPS_LLAMA_PORT_CONFLICT_POLICY"], config.llamaPortConflictPolicy),
|
|
250
|
+
model: normalizeModel(process.env["YIPS_MODEL"], config.model),
|
|
251
|
+
tokensMode: normalizeTokensMode(process.env["YIPS_TOKENS_MODE"], config.tokensMode),
|
|
252
|
+
tokensManualMax: normalizePositiveInt(process.env["YIPS_TOKENS_MANUAL_MAX"], config.tokensManualMax),
|
|
253
|
+
hooks: normalizeHooks(config.hooks, config.hooks)
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
function mergeConfig(defaults, candidate) {
|
|
257
|
+
if (!isRecord(candidate)) {
|
|
258
|
+
return defaults;
|
|
259
|
+
}
|
|
260
|
+
const llamaHost = normalizeHost(candidate.llamaHost, defaults.llamaHost);
|
|
261
|
+
const llamaPort = normalizePort(candidate.llamaPort, defaults.llamaPort);
|
|
262
|
+
const llamaBaseUrl = normalizeBaseUrl(candidate.llamaBaseUrl, buildBaseUrl(llamaHost, llamaPort));
|
|
263
|
+
const baseUrlHostPort = parseBaseUrlHostPort(llamaBaseUrl);
|
|
264
|
+
return {
|
|
265
|
+
streaming: normalizeBoolean(candidate.streaming, defaults.streaming),
|
|
266
|
+
verbose: normalizeBoolean(candidate.verbose, defaults.verbose),
|
|
267
|
+
backend: normalizeBackend(candidate.backend, defaults.backend),
|
|
268
|
+
llamaBaseUrl,
|
|
269
|
+
llamaServerPath: normalizeString(candidate.llamaServerPath, defaults.llamaServerPath),
|
|
270
|
+
llamaModelsDir: normalizeString(candidate.llamaModelsDir, defaults.llamaModelsDir),
|
|
271
|
+
llamaHost: baseUrlHostPort?.host ?? llamaHost,
|
|
272
|
+
llamaPort: baseUrlHostPort?.port ?? llamaPort,
|
|
273
|
+
llamaContextSize: normalizePositiveInt(candidate.llamaContextSize, defaults.llamaContextSize),
|
|
274
|
+
llamaGpuLayers: normalizePositiveInt(candidate.llamaGpuLayers, defaults.llamaGpuLayers),
|
|
275
|
+
llamaAutoStart: normalizeBoolean(candidate.llamaAutoStart, defaults.llamaAutoStart),
|
|
276
|
+
llamaPortConflictPolicy: normalizePortConflictPolicy(candidate.llamaPortConflictPolicy, defaults.llamaPortConflictPolicy),
|
|
277
|
+
model: normalizeModel(candidate.model, defaults.model),
|
|
278
|
+
tokensMode: normalizeTokensMode(candidate.tokensMode, defaults.tokensMode),
|
|
279
|
+
tokensManualMax: normalizePositiveInt(candidate.tokensManualMax, defaults.tokensManualMax),
|
|
280
|
+
nicknames: normalizeNicknames(candidate.nicknames, defaults.nicknames),
|
|
281
|
+
hooks: normalizeHooks(candidate.hooks, defaults.hooks)
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
async function loadConfig(configPath = exports.DEFAULT_CONFIG_PATH) {
|
|
285
|
+
const defaults = getDefaultConfig();
|
|
286
|
+
const path = resolveConfigPath(configPath);
|
|
287
|
+
const legacyDefaultPath = (0, node_path_1.resolve)(process.cwd(), exports.DEFAULT_CONFIG_PATH);
|
|
288
|
+
const candidatePaths = [path];
|
|
289
|
+
if (configPath === exports.DEFAULT_CONFIG_PATH && path !== legacyDefaultPath) {
|
|
290
|
+
candidatePaths.push(legacyDefaultPath);
|
|
291
|
+
}
|
|
292
|
+
let readablePath = null;
|
|
293
|
+
for (const candidatePath of candidatePaths) {
|
|
294
|
+
try {
|
|
295
|
+
await (0, promises_1.access)(candidatePath, node_fs_1.constants.R_OK);
|
|
296
|
+
readablePath = candidatePath;
|
|
297
|
+
break;
|
|
298
|
+
}
|
|
299
|
+
catch {
|
|
300
|
+
// keep trying
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
if (!readablePath) {
|
|
304
|
+
return { config: applyEnvOverrides(defaults), path, source: "default" };
|
|
305
|
+
}
|
|
306
|
+
try {
|
|
307
|
+
const rawConfig = await (0, promises_1.readFile)(readablePath, "utf8");
|
|
308
|
+
const parsedConfig = JSON.parse(rawConfig);
|
|
309
|
+
return {
|
|
310
|
+
config: applyEnvOverrides(mergeConfig(defaults, parsedConfig)),
|
|
311
|
+
path: readablePath,
|
|
312
|
+
source: "file"
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
catch (error) {
|
|
316
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
317
|
+
return {
|
|
318
|
+
config: applyEnvOverrides(defaults),
|
|
319
|
+
path: readablePath,
|
|
320
|
+
source: "default",
|
|
321
|
+
warning: `Failed to load config at ${readablePath}: ${message}`
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
async function saveConfig(config, configPath = exports.DEFAULT_CONFIG_PATH) {
|
|
326
|
+
const path = resolveConfigPath(configPath);
|
|
327
|
+
const normalized = mergeConfig(getDefaultConfig(), config);
|
|
328
|
+
await (0, promises_1.writeFile)(path, `${JSON.stringify(normalized, null, 2)}\n`, "utf8");
|
|
329
|
+
}
|
|
330
|
+
async function updateConfig(patch, configPath = exports.DEFAULT_CONFIG_PATH) {
|
|
331
|
+
const loaded = await loadConfig(configPath);
|
|
332
|
+
const merged = mergeConfig(getDefaultConfig(), { ...loaded.config, ...patch });
|
|
333
|
+
await saveConfig(merged, configPath);
|
|
334
|
+
return merged;
|
|
335
|
+
}
|
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DOWNLOADER_TABS = void 0;
|
|
4
|
+
exports.tabToSort = tabToSort;
|
|
5
|
+
exports.createDownloaderState = createDownloaderState;
|
|
6
|
+
exports.cycleTab = cycleTab;
|
|
7
|
+
exports.setModels = setModels;
|
|
8
|
+
exports.setFiles = setFiles;
|
|
9
|
+
exports.setLoading = setLoading;
|
|
10
|
+
exports.setLoadingModels = setLoadingModels;
|
|
11
|
+
exports.setLoadingFiles = setLoadingFiles;
|
|
12
|
+
exports.startDownload = startDownload;
|
|
13
|
+
exports.updateDownloadProgress = updateDownloadProgress;
|
|
14
|
+
exports.finishDownload = finishDownload;
|
|
15
|
+
exports.setPreloadingTabs = setPreloadingTabs;
|
|
16
|
+
exports.setCachedModels = setCachedModels;
|
|
17
|
+
exports.getCachedModels = getCachedModels;
|
|
18
|
+
exports.resetModelCache = resetModelCache;
|
|
19
|
+
exports.setError = setError;
|
|
20
|
+
exports.setDownloaderError = setDownloaderError;
|
|
21
|
+
exports.closeFileView = closeFileView;
|
|
22
|
+
exports.openCancelConfirm = openCancelConfirm;
|
|
23
|
+
exports.closeCancelConfirm = closeCancelConfirm;
|
|
24
|
+
exports.moveModelSelection = moveModelSelection;
|
|
25
|
+
exports.moveFileSelection = moveFileSelection;
|
|
26
|
+
exports.DOWNLOADER_TABS = ["Most Downloaded", "Top Rated", "Newest"];
|
|
27
|
+
function tabToSort(tab) {
|
|
28
|
+
if (tab === "Top Rated") {
|
|
29
|
+
return "trendingScore";
|
|
30
|
+
}
|
|
31
|
+
if (tab === "Newest") {
|
|
32
|
+
return "lastModified";
|
|
33
|
+
}
|
|
34
|
+
return "downloads";
|
|
35
|
+
}
|
|
36
|
+
function createDownloaderState(memory) {
|
|
37
|
+
return {
|
|
38
|
+
isOpen: true,
|
|
39
|
+
view: "models",
|
|
40
|
+
tab: "Most Downloaded",
|
|
41
|
+
searchQuery: "",
|
|
42
|
+
models: [],
|
|
43
|
+
files: [],
|
|
44
|
+
selectedModelIndex: 0,
|
|
45
|
+
selectedFileIndex: 0,
|
|
46
|
+
modelScrollOffset: 0,
|
|
47
|
+
fileScrollOffset: 0,
|
|
48
|
+
phase: "idle",
|
|
49
|
+
loading: false,
|
|
50
|
+
loadingMessage: "Loading models...",
|
|
51
|
+
errorMessage: "",
|
|
52
|
+
download: null,
|
|
53
|
+
selectedRepoId: "",
|
|
54
|
+
ramGb: memory.ramGb,
|
|
55
|
+
vramGb: memory.vramGb,
|
|
56
|
+
totalMemoryGb: memory.totalMemoryGb,
|
|
57
|
+
diskFreeGb: memory.diskFreeGb ?? 0,
|
|
58
|
+
cacheQuery: "",
|
|
59
|
+
modelCacheByTab: {},
|
|
60
|
+
preloadingTabs: false,
|
|
61
|
+
cancelConfirmOpen: false
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
function cycleTab(state, direction) {
|
|
65
|
+
const currentIndex = exports.DOWNLOADER_TABS.indexOf(state.tab);
|
|
66
|
+
const nextIndex = (currentIndex + direction + exports.DOWNLOADER_TABS.length) % exports.DOWNLOADER_TABS.length;
|
|
67
|
+
return {
|
|
68
|
+
...state,
|
|
69
|
+
tab: exports.DOWNLOADER_TABS[nextIndex] ?? "Most Downloaded",
|
|
70
|
+
selectedModelIndex: 0,
|
|
71
|
+
modelScrollOffset: 0
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
function setModels(state, models) {
|
|
75
|
+
return {
|
|
76
|
+
...state,
|
|
77
|
+
models,
|
|
78
|
+
selectedModelIndex: models.length > 0 ? Math.min(state.selectedModelIndex, models.length - 1) : 0,
|
|
79
|
+
modelScrollOffset: 0,
|
|
80
|
+
phase: "idle",
|
|
81
|
+
loading: false,
|
|
82
|
+
errorMessage: "",
|
|
83
|
+
download: null,
|
|
84
|
+
cancelConfirmOpen: false
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
function setFiles(state, repoId, files) {
|
|
88
|
+
return {
|
|
89
|
+
...state,
|
|
90
|
+
selectedRepoId: repoId,
|
|
91
|
+
view: "files",
|
|
92
|
+
files,
|
|
93
|
+
selectedFileIndex: 0,
|
|
94
|
+
fileScrollOffset: 0,
|
|
95
|
+
phase: "idle",
|
|
96
|
+
loading: false,
|
|
97
|
+
errorMessage: "",
|
|
98
|
+
download: null,
|
|
99
|
+
cancelConfirmOpen: false
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
function setLoading(state, message) {
|
|
103
|
+
return {
|
|
104
|
+
...state,
|
|
105
|
+
phase: "loading-models",
|
|
106
|
+
loading: true,
|
|
107
|
+
loadingMessage: message,
|
|
108
|
+
errorMessage: "",
|
|
109
|
+
download: null,
|
|
110
|
+
cancelConfirmOpen: false
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
function setLoadingModels(state, message) {
|
|
114
|
+
return {
|
|
115
|
+
...state,
|
|
116
|
+
phase: "loading-models",
|
|
117
|
+
loading: true,
|
|
118
|
+
loadingMessage: message,
|
|
119
|
+
errorMessage: "",
|
|
120
|
+
download: null,
|
|
121
|
+
cancelConfirmOpen: false
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
function setLoadingFiles(state, message) {
|
|
125
|
+
return {
|
|
126
|
+
...state,
|
|
127
|
+
phase: "loading-files",
|
|
128
|
+
loading: true,
|
|
129
|
+
loadingMessage: message,
|
|
130
|
+
errorMessage: "",
|
|
131
|
+
download: null,
|
|
132
|
+
cancelConfirmOpen: false
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
function startDownload(state, repoId, filename, statusText) {
|
|
136
|
+
const now = Date.now();
|
|
137
|
+
return {
|
|
138
|
+
...state,
|
|
139
|
+
phase: "downloading",
|
|
140
|
+
loading: true,
|
|
141
|
+
loadingMessage: statusText,
|
|
142
|
+
errorMessage: "",
|
|
143
|
+
download: {
|
|
144
|
+
repoId,
|
|
145
|
+
filename,
|
|
146
|
+
bytesDownloaded: 0,
|
|
147
|
+
totalBytes: null,
|
|
148
|
+
startedAtMs: now,
|
|
149
|
+
lastUpdateAtMs: now,
|
|
150
|
+
statusText
|
|
151
|
+
},
|
|
152
|
+
cancelConfirmOpen: false
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
function updateDownloadProgress(state, update) {
|
|
156
|
+
if (state.phase !== "downloading" || !state.download) {
|
|
157
|
+
return state;
|
|
158
|
+
}
|
|
159
|
+
return {
|
|
160
|
+
...state,
|
|
161
|
+
loadingMessage: update.statusText,
|
|
162
|
+
download: {
|
|
163
|
+
...state.download,
|
|
164
|
+
bytesDownloaded: Math.max(0, update.bytesDownloaded),
|
|
165
|
+
totalBytes: update.totalBytes,
|
|
166
|
+
lastUpdateAtMs: Date.now(),
|
|
167
|
+
statusText: update.statusText
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
function finishDownload(state) {
|
|
172
|
+
return {
|
|
173
|
+
...state,
|
|
174
|
+
phase: "idle",
|
|
175
|
+
loading: false,
|
|
176
|
+
loadingMessage: "",
|
|
177
|
+
errorMessage: "",
|
|
178
|
+
download: null,
|
|
179
|
+
cancelConfirmOpen: false
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
function setPreloadingTabs(state, preloadingTabs) {
|
|
183
|
+
return {
|
|
184
|
+
...state,
|
|
185
|
+
preloadingTabs
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
function setCachedModels(state, tab, query, models) {
|
|
189
|
+
const normalizedQuery = query.trim();
|
|
190
|
+
const cache = state.cacheQuery === normalizedQuery ? state.modelCacheByTab : {};
|
|
191
|
+
return {
|
|
192
|
+
...state,
|
|
193
|
+
cacheQuery: normalizedQuery,
|
|
194
|
+
modelCacheByTab: {
|
|
195
|
+
...cache,
|
|
196
|
+
[tab]: models
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
function getCachedModels(state, tab, query) {
|
|
201
|
+
if (state.cacheQuery !== query.trim()) {
|
|
202
|
+
return null;
|
|
203
|
+
}
|
|
204
|
+
return state.modelCacheByTab[tab] ?? null;
|
|
205
|
+
}
|
|
206
|
+
function resetModelCache(state, query) {
|
|
207
|
+
return {
|
|
208
|
+
...state,
|
|
209
|
+
cacheQuery: query.trim(),
|
|
210
|
+
modelCacheByTab: {}
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
function setError(state, message) {
|
|
214
|
+
return {
|
|
215
|
+
...state,
|
|
216
|
+
phase: "error",
|
|
217
|
+
loading: false,
|
|
218
|
+
errorMessage: message,
|
|
219
|
+
download: null,
|
|
220
|
+
cancelConfirmOpen: false
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
function setDownloaderError(state, message) {
|
|
224
|
+
return {
|
|
225
|
+
...state,
|
|
226
|
+
phase: "error",
|
|
227
|
+
loading: false,
|
|
228
|
+
errorMessage: message,
|
|
229
|
+
download: null,
|
|
230
|
+
cancelConfirmOpen: false
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
function closeFileView(state) {
|
|
234
|
+
return {
|
|
235
|
+
...state,
|
|
236
|
+
view: "models",
|
|
237
|
+
files: [],
|
|
238
|
+
selectedFileIndex: 0,
|
|
239
|
+
fileScrollOffset: 0,
|
|
240
|
+
phase: "idle",
|
|
241
|
+
loading: false,
|
|
242
|
+
errorMessage: "",
|
|
243
|
+
download: null,
|
|
244
|
+
cancelConfirmOpen: false
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
function openCancelConfirm(state) {
|
|
248
|
+
if (state.phase !== "downloading" || !state.download) {
|
|
249
|
+
return state;
|
|
250
|
+
}
|
|
251
|
+
return {
|
|
252
|
+
...state,
|
|
253
|
+
cancelConfirmOpen: true
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
function closeCancelConfirm(state) {
|
|
257
|
+
if (!state.cancelConfirmOpen) {
|
|
258
|
+
return state;
|
|
259
|
+
}
|
|
260
|
+
return {
|
|
261
|
+
...state,
|
|
262
|
+
cancelConfirmOpen: false
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
function moveModelSelection(state, delta, windowSize) {
|
|
266
|
+
if (state.models.length === 0) {
|
|
267
|
+
return state;
|
|
268
|
+
}
|
|
269
|
+
const max = state.models.length - 1;
|
|
270
|
+
const nextIndex = Math.max(0, Math.min(max, state.selectedModelIndex + delta));
|
|
271
|
+
let nextOffset = state.modelScrollOffset;
|
|
272
|
+
if (nextIndex < nextOffset) {
|
|
273
|
+
nextOffset = nextIndex;
|
|
274
|
+
}
|
|
275
|
+
if (nextIndex >= nextOffset + windowSize) {
|
|
276
|
+
nextOffset = nextIndex - windowSize + 1;
|
|
277
|
+
}
|
|
278
|
+
return {
|
|
279
|
+
...state,
|
|
280
|
+
selectedModelIndex: nextIndex,
|
|
281
|
+
modelScrollOffset: Math.max(0, nextOffset)
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
function moveFileSelection(state, delta, windowSize) {
|
|
285
|
+
if (state.files.length === 0) {
|
|
286
|
+
return state;
|
|
287
|
+
}
|
|
288
|
+
const max = state.files.length - 1;
|
|
289
|
+
const nextIndex = Math.max(0, Math.min(max, state.selectedFileIndex + delta));
|
|
290
|
+
let nextOffset = state.fileScrollOffset;
|
|
291
|
+
if (nextIndex < nextOffset) {
|
|
292
|
+
nextOffset = nextIndex;
|
|
293
|
+
}
|
|
294
|
+
if (nextIndex >= nextOffset + windowSize) {
|
|
295
|
+
nextOffset = nextIndex - windowSize + 1;
|
|
296
|
+
}
|
|
297
|
+
return {
|
|
298
|
+
...state,
|
|
299
|
+
selectedFileIndex: nextIndex,
|
|
300
|
+
fileScrollOffset: Math.max(0, nextOffset)
|
|
301
|
+
};
|
|
302
|
+
}
|