@wrongstack/cli 0.51.3 → 0.54.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/index.js +532 -52
- package/dist/index.js.map +1 -1
- package/package.json +11 -11
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import * as path8 from 'path';
|
|
3
3
|
import { join } from 'path';
|
|
4
4
|
import * as fsp3 from 'fs/promises';
|
|
5
|
-
import { color, writeErr, DefaultTaskStore, TaskTracker, renderProgress, SpecStore, TaskGraphStore, analyzeCriticalPath, getTemplate, listTemplates, templateToMarkdown, SpecParser, renderSpecAnalysis, AISpecBuilder, renderTaskGraph, SpecVersioning, atomicWrite, DefaultPathResolver, TOKENS, DefaultSystemPromptBuilder, makeAutonomyPromptContributor, ToolRegistry, createContextManagerTool, EventBus, SlashCommandRegistry, BrainDecisionQueue, ObservableBrainArbiter, HumanEscalatingBrainArbiter, DefaultBrainArbiter, createDelegateTool, FLEET_ROSTER, createMcpControlTool, DefaultLogger, DefaultModelsRegistry, isStdinTTY, writeOut, runProviderWithRetry, ReplayLogStore, ReplayProviderRunner, ProviderRegistry, InMemoryMetricsSink, wireMetricsToEvents, DefaultHealthRegistry, startMetricsServer, RecoveryLock, DefaultAttachmentStore, QueueStore, Context, loadTodosCheckpoint, attachTodosCheckpoint, loadDirectorState, loadPlan, createDefaultPipelines, AutoCompactionMiddleware, estimateRequestTokensCalibrated, Agent, loadPlugins, FleetManager, makeDirectorSessionFactory, Director, makeFleetEmitTool, makeFleetStatusTool, resolveModelMatrix, AutoApprovePermissionPolicy, PhaseStore, AutoPhasePlanner, PhaseGraphBuilder, WorktreeManager, PhaseOrchestrator, makeLLMClassifier, ParallelEternalEngine, EternalAutonomyEngine, allServers as allServers$1, bootConfig as bootConfig$1, setRawMode, DefaultSessionReader, resolveWstackPaths, ToolAuditLog, DefaultSessionRewinder, DefaultSessionStore, DefaultPluginAPI, makeAgentSubagentRunner, NULL_FLEET_BUS, buildChildEnv, formatContextWindowModeList, repairToolUseAdjacency, getContextWindowMode, resolveContextWindowPolicy, AGENTS_BY_PHASE, dispatchAgent, formatTodosList, SessionRecovery, loadGoal, goalFilePath, summarizeUsage, saveGoal, emptyGoal, buildGoalPreamble, formatGoal, pendingBtwCount, setBtwNote, MATRIX_PHASE_KEYS, AGENT_CATALOG, matrixKeyKind, onResize, decryptConfigSecrets as decryptConfigSecrets$1, encryptConfigSecrets as encryptConfigSecrets$1, InputBuilder, FsError, ERROR_CODES } from '@wrongstack/core';
|
|
5
|
+
import { color, writeErr, DefaultTaskStore, TaskTracker, renderProgress, SpecStore, TaskGraphStore, analyzeCriticalPath, getTemplate, listTemplates, templateToMarkdown, SpecParser, renderSpecAnalysis, AISpecBuilder, renderTaskGraph, SpecVersioning, DefaultSecretScrubber, atomicWrite, DefaultPathResolver, TOKENS, mergeCustomModelDefs, DefaultSystemPromptBuilder, makeAutonomyPromptContributor, ToolRegistry, createContextManagerTool, EventBus, SlashCommandRegistry, BrainDecisionQueue, ObservableBrainArbiter, HumanEscalatingBrainArbiter, DefaultBrainArbiter, createDelegateTool, FLEET_ROSTER, createMcpControlTool, DefaultLogger, DefaultModelsRegistry, isStdinTTY, writeOut, runProviderWithRetry, ReplayLogStore, ReplayProviderRunner, ProviderRegistry, InMemoryMetricsSink, wireMetricsToEvents, DefaultHealthRegistry, startMetricsServer, RecoveryLock, DefaultAttachmentStore, QueueStore, Context, loadTodosCheckpoint, attachTodosCheckpoint, loadDirectorState, loadPlan, createDefaultPipelines, AutoCompactionMiddleware, estimateRequestTokensCalibrated, Agent, loadPlugins, FleetManager, makeDirectorSessionFactory, Director, makeFleetEmitTool, makeFleetStatusTool, resolveModelMatrix, AutoApprovePermissionPolicy, PhaseStore, AutoPhasePlanner, PhaseGraphBuilder, WorktreeManager, PhaseOrchestrator, makeLLMClassifier, ParallelEternalEngine, EternalAutonomyEngine, allServers as allServers$1, bootConfig as bootConfig$1, setRawMode, DefaultSessionReader, resolveWstackPaths, ToolAuditLog, DefaultSessionRewinder, DefaultSessionStore, DefaultPluginAPI, makeAgentSubagentRunner, NULL_FLEET_BUS, buildChildEnv, formatContextWindowModeList, repairToolUseAdjacency, getContextWindowMode, resolveContextWindowPolicy, AGENTS_BY_PHASE, dispatchAgent, formatTodosList, SessionRecovery, loadGoal, goalFilePath, summarizeUsage, saveGoal, emptyGoal, buildGoalPreamble, formatGoal, pendingBtwCount, setBtwNote, MATRIX_PHASE_KEYS, AGENT_CATALOG, matrixKeyKind, onResize, decryptConfigSecrets as decryptConfigSecrets$1, encryptConfigSecrets as encryptConfigSecrets$1, InputBuilder, FsError, ERROR_CODES } from '@wrongstack/core';
|
|
6
6
|
import { createRequire } from 'module';
|
|
7
7
|
import * as os2 from 'os';
|
|
8
8
|
import os2__default from 'os';
|
|
@@ -16,7 +16,7 @@ import { createDefaultContainer, routeImagesForModel, readClipboardImage } from
|
|
|
16
16
|
import { builtinToolsPack, rememberTool, forgetTool } from '@wrongstack/tools';
|
|
17
17
|
import { fileURLToPath } from 'url';
|
|
18
18
|
import * as readline from 'readline';
|
|
19
|
-
import * as
|
|
19
|
+
import * as fs11 from 'fs';
|
|
20
20
|
import { writeFileSync, existsSync, readFileSync } from 'fs';
|
|
21
21
|
import { WrongStackACPServer } from '@wrongstack/acp/agent';
|
|
22
22
|
import { ACP_AGENT_COMMANDS, makeACPSubagentRunner, makeACPSubagentRunnerWithStop } from '@wrongstack/acp';
|
|
@@ -1696,6 +1696,7 @@ __export(webui_server_exports, {
|
|
|
1696
1696
|
async function runWebUI(opts) {
|
|
1697
1697
|
const port = opts.port ?? 3457;
|
|
1698
1698
|
const clients = /* @__PURE__ */ new Map();
|
|
1699
|
+
const secretScrubber = new DefaultSecretScrubber();
|
|
1699
1700
|
let abortController = null;
|
|
1700
1701
|
const authToken = crypto2.randomBytes(16).toString("hex");
|
|
1701
1702
|
const wss = new WebSocketServer({ port, host: "127.0.0.1", maxPayload: 1 * 1024 * 1024 });
|
|
@@ -1735,7 +1736,7 @@ async function runWebUI(opts) {
|
|
|
1735
1736
|
payload: {
|
|
1736
1737
|
id: e.id,
|
|
1737
1738
|
name: e.name,
|
|
1738
|
-
input: e.input,
|
|
1739
|
+
input: secretScrubber.scrubObject(e.input),
|
|
1739
1740
|
messageId: `tool_${e.id}`
|
|
1740
1741
|
}
|
|
1741
1742
|
});
|
|
@@ -1764,8 +1765,8 @@ async function runWebUI(opts) {
|
|
|
1764
1765
|
name: e.name,
|
|
1765
1766
|
durationMs: e.durationMs,
|
|
1766
1767
|
ok: e.ok,
|
|
1767
|
-
input: e.input,
|
|
1768
|
-
output: e.output
|
|
1768
|
+
input: secretScrubber.scrubObject(e.input),
|
|
1769
|
+
output: secretScrubber.scrubObject(e.output)
|
|
1769
1770
|
}
|
|
1770
1771
|
});
|
|
1771
1772
|
})
|
|
@@ -2349,11 +2350,21 @@ async function resolveRuntimeMaxContext(input) {
|
|
|
2349
2350
|
providerConfig?.baseUrl || topLevelBaseUrlApplies && input.config.baseUrl
|
|
2350
2351
|
);
|
|
2351
2352
|
if (input.modelsRegistry && !hasCustomBaseUrl) {
|
|
2352
|
-
const
|
|
2353
|
-
|
|
2353
|
+
const mergedModels = mergeCustomModelDefs(
|
|
2354
|
+
providerConfig?.customModels,
|
|
2355
|
+
input.config.models
|
|
2354
2356
|
);
|
|
2357
|
+
const caps = await capabilitiesFor(
|
|
2358
|
+
input.modelsRegistry,
|
|
2359
|
+
input.providerId,
|
|
2360
|
+
input.modelId,
|
|
2361
|
+
mergedModels
|
|
2362
|
+
).catch(() => void 0);
|
|
2355
2363
|
const catalogMax = positiveNumber(caps?.maxContext);
|
|
2356
2364
|
if (catalogMax) return catalogMax;
|
|
2365
|
+
const directModel = await input.modelsRegistry.getModel(input.providerId, input.modelId).catch(() => void 0);
|
|
2366
|
+
const directMax = positiveNumber(directModel?.capabilities.maxContext);
|
|
2367
|
+
if (directMax) return directMax;
|
|
2357
2368
|
}
|
|
2358
2369
|
return positiveNumber(input.provider.capabilities.maxContext) ?? 0;
|
|
2359
2370
|
}
|
|
@@ -2389,6 +2400,7 @@ var BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
|
|
|
2389
2400
|
"metrics",
|
|
2390
2401
|
"webui",
|
|
2391
2402
|
"no-check",
|
|
2403
|
+
"no-models-refresh",
|
|
2392
2404
|
"director",
|
|
2393
2405
|
"no-director",
|
|
2394
2406
|
"no-autonomy",
|
|
@@ -5195,6 +5207,224 @@ ${targetMode.description}`
|
|
|
5195
5207
|
}
|
|
5196
5208
|
};
|
|
5197
5209
|
}
|
|
5210
|
+
var noOpVault = {
|
|
5211
|
+
encrypt: (v) => v,
|
|
5212
|
+
decrypt: (v) => v,
|
|
5213
|
+
isEncrypted: () => false
|
|
5214
|
+
};
|
|
5215
|
+
async function patchGlobalConfig(globalConfigPath, mutate) {
|
|
5216
|
+
let raw = "{}";
|
|
5217
|
+
let fileExists = true;
|
|
5218
|
+
try {
|
|
5219
|
+
raw = await fsp3.readFile(globalConfigPath, "utf8");
|
|
5220
|
+
} catch (err) {
|
|
5221
|
+
if (err.code !== "ENOENT") throw err;
|
|
5222
|
+
fileExists = false;
|
|
5223
|
+
}
|
|
5224
|
+
let parsed;
|
|
5225
|
+
try {
|
|
5226
|
+
parsed = JSON.parse(raw);
|
|
5227
|
+
} catch (err) {
|
|
5228
|
+
if (fileExists) {
|
|
5229
|
+
throw new Error(`Config at ${globalConfigPath} is not valid JSON: ${err.message}`);
|
|
5230
|
+
}
|
|
5231
|
+
parsed = {};
|
|
5232
|
+
}
|
|
5233
|
+
const decrypted = decryptConfigSecrets$1(parsed, noOpVault);
|
|
5234
|
+
mutate(decrypted);
|
|
5235
|
+
const encrypted = encryptConfigSecrets$1(decrypted, noOpVault);
|
|
5236
|
+
await atomicWrite(globalConfigPath, JSON.stringify(encrypted, null, 2), { mode: 384 });
|
|
5237
|
+
return decrypted;
|
|
5238
|
+
}
|
|
5239
|
+
function fmtModel(id, def) {
|
|
5240
|
+
const parts = [];
|
|
5241
|
+
if (def.provider) parts.push(`${color.dim("provider:")} ${color.cyan(def.provider)}`);
|
|
5242
|
+
if (def.name) parts.push(`${color.dim("name:")} ${def.name}`);
|
|
5243
|
+
const caps = def.capabilities;
|
|
5244
|
+
if (caps) {
|
|
5245
|
+
if (caps.maxContext) parts.push(`${color.dim("maxContext:")} ${color.yellow(String(caps.maxContext))}`);
|
|
5246
|
+
const flags = [];
|
|
5247
|
+
if (caps.tools) flags.push("tools");
|
|
5248
|
+
if (caps.vision) flags.push("vision");
|
|
5249
|
+
if (caps.reasoning) flags.push("reasoning");
|
|
5250
|
+
if (caps.streaming) flags.push("streaming");
|
|
5251
|
+
if (caps.jsonMode) flags.push("json");
|
|
5252
|
+
if (flags.length) parts.push(`${color.dim("caps:")} ${flags.join(", ")}`);
|
|
5253
|
+
}
|
|
5254
|
+
if (def.maxOutput) parts.push(`${color.dim("maxOutput:")} ${color.yellow(String(def.maxOutput))}`);
|
|
5255
|
+
return ` ${color.amber(id)} ${parts.join(" ")}`;
|
|
5256
|
+
}
|
|
5257
|
+
function safeAt(arr, idx) {
|
|
5258
|
+
const v = arr[idx];
|
|
5259
|
+
if (v === void 0) throw new Error(`Missing value at position ${idx}`);
|
|
5260
|
+
return v;
|
|
5261
|
+
}
|
|
5262
|
+
function parseFlags(tokens) {
|
|
5263
|
+
let modelId = "";
|
|
5264
|
+
const caps = {};
|
|
5265
|
+
let provider;
|
|
5266
|
+
let name;
|
|
5267
|
+
let maxOutput;
|
|
5268
|
+
let i = 0;
|
|
5269
|
+
while (i < tokens.length) {
|
|
5270
|
+
const t = tokens[i];
|
|
5271
|
+
if (t.startsWith("--")) {
|
|
5272
|
+
const key = t.slice(2);
|
|
5273
|
+
switch (key) {
|
|
5274
|
+
case "provider":
|
|
5275
|
+
provider = safeAt(tokens, ++i);
|
|
5276
|
+
break;
|
|
5277
|
+
case "name":
|
|
5278
|
+
name = safeAt(tokens, ++i);
|
|
5279
|
+
break;
|
|
5280
|
+
case "max-context":
|
|
5281
|
+
caps.maxContext = Number(safeAt(tokens, ++i));
|
|
5282
|
+
break;
|
|
5283
|
+
case "max-output":
|
|
5284
|
+
maxOutput = Number(safeAt(tokens, ++i));
|
|
5285
|
+
break;
|
|
5286
|
+
case "tools":
|
|
5287
|
+
caps.tools = true;
|
|
5288
|
+
break;
|
|
5289
|
+
case "vision":
|
|
5290
|
+
caps.vision = true;
|
|
5291
|
+
break;
|
|
5292
|
+
case "streaming":
|
|
5293
|
+
caps.streaming = true;
|
|
5294
|
+
break;
|
|
5295
|
+
case "reasoning":
|
|
5296
|
+
caps.reasoning = true;
|
|
5297
|
+
break;
|
|
5298
|
+
case "json-mode":
|
|
5299
|
+
caps.jsonMode = true;
|
|
5300
|
+
break;
|
|
5301
|
+
default:
|
|
5302
|
+
return { modelId: "", error: `Unknown flag: --${key}` };
|
|
5303
|
+
}
|
|
5304
|
+
} else if (!t.startsWith("-") && !modelId) {
|
|
5305
|
+
modelId = t;
|
|
5306
|
+
}
|
|
5307
|
+
i++;
|
|
5308
|
+
}
|
|
5309
|
+
if (!modelId) return { modelId: "", error: "missing model id" };
|
|
5310
|
+
const hasCaps = Object.keys(caps).length > 0;
|
|
5311
|
+
const def = {};
|
|
5312
|
+
if (provider !== void 0) def.provider = provider;
|
|
5313
|
+
if (name !== void 0) def.name = name;
|
|
5314
|
+
if (maxOutput !== void 0) def.maxOutput = maxOutput;
|
|
5315
|
+
if (hasCaps) def.capabilities = caps;
|
|
5316
|
+
return { modelId, def: Object.keys(def).length ? def : void 0 };
|
|
5317
|
+
}
|
|
5318
|
+
function buildModelsCommand(opts) {
|
|
5319
|
+
const help = [
|
|
5320
|
+
"Usage:",
|
|
5321
|
+
" /models List custom model definitions",
|
|
5322
|
+
" /models add <id> [flags] Add or update a custom model",
|
|
5323
|
+
" /models remove <id> Remove a custom model",
|
|
5324
|
+
"",
|
|
5325
|
+
"Flags for add:",
|
|
5326
|
+
" --provider <id> Owning provider",
|
|
5327
|
+
' --name "Display" Display name',
|
|
5328
|
+
" --max-context <N> Context window override",
|
|
5329
|
+
" --max-output <N> Max output tokens",
|
|
5330
|
+
" --tools Tool-capable",
|
|
5331
|
+
" --vision Vision-capable",
|
|
5332
|
+
" --streaming Streaming support",
|
|
5333
|
+
" --reasoning Reasoning support",
|
|
5334
|
+
" --json-mode JSON mode support",
|
|
5335
|
+
"",
|
|
5336
|
+
"Persisted to ~/.wrongstack/config.json."
|
|
5337
|
+
].join("\n");
|
|
5338
|
+
return {
|
|
5339
|
+
name: "models",
|
|
5340
|
+
description: "Manage custom model definitions.",
|
|
5341
|
+
help,
|
|
5342
|
+
async run(args) {
|
|
5343
|
+
const parts = args.trim().split(/\s+/).filter(Boolean);
|
|
5344
|
+
const sub = (parts[0] ?? "").toLowerCase();
|
|
5345
|
+
if (sub === "help" || sub === "--help") return { message: help };
|
|
5346
|
+
if (!opts.configStore || !opts.paths) {
|
|
5347
|
+
return { message: `${color.red("Error")} config store not available.` };
|
|
5348
|
+
}
|
|
5349
|
+
const config = opts.configStore.get();
|
|
5350
|
+
const globalConfigPath = opts.paths.globalConfig;
|
|
5351
|
+
if (!sub) {
|
|
5352
|
+
const models = config.models ?? {};
|
|
5353
|
+
const ids = Object.keys(models);
|
|
5354
|
+
if (ids.length === 0) {
|
|
5355
|
+
return {
|
|
5356
|
+
message: [
|
|
5357
|
+
`${color.bold("Custom Models")} ${color.dim("(none defined)")}`,
|
|
5358
|
+
"",
|
|
5359
|
+
color.dim(" Add one: /models add <id> --max-context 128000 --tools")
|
|
5360
|
+
].join("\n")
|
|
5361
|
+
};
|
|
5362
|
+
}
|
|
5363
|
+
return {
|
|
5364
|
+
message: [
|
|
5365
|
+
`${color.bold("Custom Models")} ${color.dim(`(${ids.length})`)}`,
|
|
5366
|
+
...ids.sort().map((id) => fmtModel(id, models[id]))
|
|
5367
|
+
].join("\n")
|
|
5368
|
+
};
|
|
5369
|
+
}
|
|
5370
|
+
try {
|
|
5371
|
+
if (sub === "add") {
|
|
5372
|
+
const { modelId, def, error } = parseFlags(parts.slice(1));
|
|
5373
|
+
if (error) {
|
|
5374
|
+
return { message: `${color.red("Error")}: ${error}. ${color.dim("/models help")}` };
|
|
5375
|
+
}
|
|
5376
|
+
if (!def && !error) {
|
|
5377
|
+
return { message: `${color.amber("Usage:")} /models add <id> [--max-context N] [--tools] ... ${color.dim("/models help")}` };
|
|
5378
|
+
}
|
|
5379
|
+
const existingModels = config.models ?? {};
|
|
5380
|
+
const existed = modelId in existingModels;
|
|
5381
|
+
const decrypted = await patchGlobalConfig(globalConfigPath, (cfg) => {
|
|
5382
|
+
const models = { ...cfg.models ?? {} };
|
|
5383
|
+
models[modelId] = {
|
|
5384
|
+
...models[modelId],
|
|
5385
|
+
...def,
|
|
5386
|
+
capabilities: {
|
|
5387
|
+
...models[modelId]?.capabilities,
|
|
5388
|
+
...def?.capabilities
|
|
5389
|
+
}
|
|
5390
|
+
};
|
|
5391
|
+
cfg.models = models;
|
|
5392
|
+
});
|
|
5393
|
+
opts.configStore.update({
|
|
5394
|
+
models: decrypted.models
|
|
5395
|
+
});
|
|
5396
|
+
return { message: `${color.green("\u2713")} ${color.amber(modelId)} ${existed ? "updated" : "added"}.` };
|
|
5397
|
+
}
|
|
5398
|
+
if (sub === "remove" || sub === "rm") {
|
|
5399
|
+
const modelId = parts[1];
|
|
5400
|
+
if (!modelId) {
|
|
5401
|
+
return { message: `${color.amber("Usage:")} /models remove <id>` };
|
|
5402
|
+
}
|
|
5403
|
+
const existing = config.models ?? {};
|
|
5404
|
+
if (!(modelId in existing)) {
|
|
5405
|
+
return { message: `${color.amber("Not found")}: custom model "${modelId}" is not defined.` };
|
|
5406
|
+
}
|
|
5407
|
+
const decrypted = await patchGlobalConfig(globalConfigPath, (cfg) => {
|
|
5408
|
+
const models = { ...cfg.models ?? {} };
|
|
5409
|
+
delete models[modelId];
|
|
5410
|
+
cfg.models = models;
|
|
5411
|
+
});
|
|
5412
|
+
opts.configStore.update({
|
|
5413
|
+
models: decrypted.models
|
|
5414
|
+
});
|
|
5415
|
+
return { message: `${color.green("\u2713")} removed ${color.amber(modelId)}` };
|
|
5416
|
+
}
|
|
5417
|
+
return {
|
|
5418
|
+
message: `${color.red("Unknown subcommand")} "${sub}". Try ${color.dim("/models")}, ${color.dim("/models add")}, or ${color.dim("/models help")}.`
|
|
5419
|
+
};
|
|
5420
|
+
} catch (err) {
|
|
5421
|
+
return {
|
|
5422
|
+
message: `${color.red("models error")}: ${err instanceof Error ? err.message : String(err)}`
|
|
5423
|
+
};
|
|
5424
|
+
}
|
|
5425
|
+
}
|
|
5426
|
+
};
|
|
5427
|
+
}
|
|
5198
5428
|
function buildNextCommand(opts) {
|
|
5199
5429
|
return {
|
|
5200
5430
|
name: "next",
|
|
@@ -5452,7 +5682,7 @@ function summariseEvent(ev) {
|
|
|
5452
5682
|
return color.dim("\u2026");
|
|
5453
5683
|
}
|
|
5454
5684
|
}
|
|
5455
|
-
var
|
|
5685
|
+
var noOpVault2 = {
|
|
5456
5686
|
encrypt: (v) => v,
|
|
5457
5687
|
decrypt: (v) => v,
|
|
5458
5688
|
isEncrypted: () => false
|
|
@@ -5487,7 +5717,7 @@ function parseTarget(tokens) {
|
|
|
5487
5717
|
function fmtEntry(e) {
|
|
5488
5718
|
return e.provider ? `${e.provider}/${e.model}` : `${e.model} ${color.dim("(leader provider)")}`;
|
|
5489
5719
|
}
|
|
5490
|
-
async function
|
|
5720
|
+
async function patchGlobalConfig2(globalConfigPath, mutate) {
|
|
5491
5721
|
let raw = "{}";
|
|
5492
5722
|
let fileExists = true;
|
|
5493
5723
|
try {
|
|
@@ -5504,9 +5734,9 @@ async function patchGlobalConfig(globalConfigPath, mutate) {
|
|
|
5504
5734
|
throw new Error(`Config at ${globalConfigPath} is not valid JSON: ${err.message}`);
|
|
5505
5735
|
parsed = {};
|
|
5506
5736
|
}
|
|
5507
|
-
const decrypted = decryptConfigSecrets$1(parsed,
|
|
5737
|
+
const decrypted = decryptConfigSecrets$1(parsed, noOpVault2);
|
|
5508
5738
|
mutate(decrypted);
|
|
5509
|
-
const encrypted = encryptConfigSecrets$1(decrypted,
|
|
5739
|
+
const encrypted = encryptConfigSecrets$1(decrypted, noOpVault2);
|
|
5510
5740
|
await atomicWrite(globalConfigPath, JSON.stringify(encrypted, null, 2), { mode: 384 });
|
|
5511
5741
|
return decrypted;
|
|
5512
5742
|
}
|
|
@@ -5597,7 +5827,7 @@ function buildSetModelCommand(opts) {
|
|
|
5597
5827
|
message: `${color.red("Provider not available")}: "${provider}". Keyed: ${keyed.join(", ") || "(none)"}. ${color.dim("/setmodel list")}`
|
|
5598
5828
|
};
|
|
5599
5829
|
}
|
|
5600
|
-
const decrypted = await
|
|
5830
|
+
const decrypted = await patchGlobalConfig2(globalConfigPath, (cfg) => {
|
|
5601
5831
|
cfg.provider = provider;
|
|
5602
5832
|
cfg.model = model;
|
|
5603
5833
|
});
|
|
@@ -5628,7 +5858,7 @@ function buildSetModelCommand(opts) {
|
|
|
5628
5858
|
message: `${color.red("Provider not available")}: "${parsed.provider}". Keyed: ${keyed.join(", ") || "(none)"}.`
|
|
5629
5859
|
};
|
|
5630
5860
|
}
|
|
5631
|
-
const decrypted = await
|
|
5861
|
+
const decrypted = await patchGlobalConfig2(globalConfigPath, (cfg) => {
|
|
5632
5862
|
const matrix = { ...cfg.modelMatrix ?? {} };
|
|
5633
5863
|
matrix[key] = parsed.provider ? { provider: parsed.provider, model: parsed.model } : { model: parsed.model };
|
|
5634
5864
|
cfg.modelMatrix = matrix;
|
|
@@ -5645,7 +5875,7 @@ function buildSetModelCommand(opts) {
|
|
|
5645
5875
|
if (!(key in existing)) {
|
|
5646
5876
|
return { message: `${color.amber("No matrix entry")} for "${key}".` };
|
|
5647
5877
|
}
|
|
5648
|
-
const decrypted = await
|
|
5878
|
+
const decrypted = await patchGlobalConfig2(globalConfigPath, (cfg) => {
|
|
5649
5879
|
const matrix = { ...cfg.modelMatrix ?? {} };
|
|
5650
5880
|
delete matrix[key];
|
|
5651
5881
|
cfg.modelMatrix = matrix;
|
|
@@ -5697,7 +5927,7 @@ async function persistAutonomySetting(deps, mutator) {
|
|
|
5697
5927
|
}
|
|
5698
5928
|
|
|
5699
5929
|
// src/slash-commands/settings.ts
|
|
5700
|
-
var
|
|
5930
|
+
var noOpVault3 = {
|
|
5701
5931
|
encrypt: (v) => v,
|
|
5702
5932
|
decrypt: (v) => v,
|
|
5703
5933
|
isEncrypted: () => false
|
|
@@ -5762,7 +5992,7 @@ function buildSettingsCommand(opts) {
|
|
|
5762
5992
|
const persistDeps = {
|
|
5763
5993
|
configStore: opts.configStore,
|
|
5764
5994
|
globalConfigPath: opts.paths.globalConfig,
|
|
5765
|
-
vault:
|
|
5995
|
+
vault: noOpVault3
|
|
5766
5996
|
};
|
|
5767
5997
|
try {
|
|
5768
5998
|
if (sub === "delay") {
|
|
@@ -6186,6 +6416,7 @@ function buildBuiltinSlashCommands(opts) {
|
|
|
6186
6416
|
buildWorktreeCommand(opts),
|
|
6187
6417
|
buildSettingsCommand(opts),
|
|
6188
6418
|
buildSetModelCommand(opts),
|
|
6419
|
+
buildModelsCommand(opts),
|
|
6189
6420
|
buildCollabCommand(opts),
|
|
6190
6421
|
buildStatuslineCommand({
|
|
6191
6422
|
cwd: opts.cwd,
|
|
@@ -6907,11 +7138,11 @@ async function restoreLast(homeFn = defaultHomeDir) {
|
|
|
6907
7138
|
var theme = { primary: color.amber };
|
|
6908
7139
|
async function saveToGlobalConfig(configPath2, provider, model, homeFn = () => process.env.HOME ?? __require("os").homedir()) {
|
|
6909
7140
|
try {
|
|
6910
|
-
const { atomicWrite:
|
|
6911
|
-
const
|
|
7141
|
+
const { atomicWrite: atomicWrite12 } = await import('@wrongstack/core');
|
|
7142
|
+
const fs24 = await import('fs/promises');
|
|
6912
7143
|
let existing = {};
|
|
6913
7144
|
try {
|
|
6914
|
-
const raw = await
|
|
7145
|
+
const raw = await fs24.readFile(configPath2, "utf8");
|
|
6915
7146
|
existing = JSON.parse(raw);
|
|
6916
7147
|
} catch {
|
|
6917
7148
|
}
|
|
@@ -6919,7 +7150,7 @@ async function saveToGlobalConfig(configPath2, provider, model, homeFn = () => p
|
|
|
6919
7150
|
existing.provider = provider;
|
|
6920
7151
|
existing.model = model;
|
|
6921
7152
|
await backupCurrent(homeFn);
|
|
6922
|
-
await
|
|
7153
|
+
await atomicWrite12(configPath2, JSON.stringify(existing, null, 2), { mode: 384 });
|
|
6923
7154
|
try {
|
|
6924
7155
|
await appendHistory(
|
|
6925
7156
|
oldCfg,
|
|
@@ -7242,12 +7473,12 @@ function pickGroupIndex(opts) {
|
|
|
7242
7473
|
try {
|
|
7243
7474
|
let current = 0;
|
|
7244
7475
|
try {
|
|
7245
|
-
const parsed = Number.parseInt(
|
|
7476
|
+
const parsed = Number.parseInt(fs11.readFileSync(opts.cursorFile, "utf8").trim(), 10);
|
|
7246
7477
|
if (Number.isFinite(parsed)) current = wrap(parsed);
|
|
7247
7478
|
} catch {
|
|
7248
7479
|
}
|
|
7249
|
-
|
|
7250
|
-
|
|
7480
|
+
fs11.mkdirSync(path8.dirname(opts.cursorFile), { recursive: true });
|
|
7481
|
+
fs11.writeFileSync(opts.cursorFile, String(wrap(current + 1)));
|
|
7251
7482
|
return current;
|
|
7252
7483
|
} catch {
|
|
7253
7484
|
}
|
|
@@ -9154,8 +9385,49 @@ ${color.dim(`Current: ${deps.config.provider ?? "<unset>"} / ${deps.config.model
|
|
|
9154
9385
|
return 1;
|
|
9155
9386
|
}
|
|
9156
9387
|
};
|
|
9388
|
+
function parseFlags2(args) {
|
|
9389
|
+
const flags = {};
|
|
9390
|
+
for (let i = 0; i < args.length; i++) {
|
|
9391
|
+
const a = args[i];
|
|
9392
|
+
if (a.startsWith("--")) {
|
|
9393
|
+
const eq = a.indexOf("=");
|
|
9394
|
+
if (eq !== -1) {
|
|
9395
|
+
flags[a.slice(2, eq)] = a.slice(eq + 1);
|
|
9396
|
+
} else {
|
|
9397
|
+
const name = a.slice(2);
|
|
9398
|
+
if (i + 1 < args.length && !args[i + 1].startsWith("--")) {
|
|
9399
|
+
flags[name] = args[++i] ?? "";
|
|
9400
|
+
} else {
|
|
9401
|
+
flags[name] = true;
|
|
9402
|
+
}
|
|
9403
|
+
}
|
|
9404
|
+
}
|
|
9405
|
+
}
|
|
9406
|
+
return flags;
|
|
9407
|
+
}
|
|
9408
|
+
function positionals(args) {
|
|
9409
|
+
const out = [];
|
|
9410
|
+
for (let i = 0; i < args.length; i++) {
|
|
9411
|
+
const a = args[i];
|
|
9412
|
+
if (a.startsWith("--")) {
|
|
9413
|
+
const eq = a.indexOf("=");
|
|
9414
|
+
if (eq === -1) {
|
|
9415
|
+
if (i + 1 < args.length && !args[i + 1].startsWith("--")) {
|
|
9416
|
+
i++;
|
|
9417
|
+
}
|
|
9418
|
+
}
|
|
9419
|
+
continue;
|
|
9420
|
+
}
|
|
9421
|
+
out.push(a);
|
|
9422
|
+
}
|
|
9423
|
+
return out;
|
|
9424
|
+
}
|
|
9425
|
+
var DEFAULT_PER_PAGE = 15;
|
|
9157
9426
|
var modelsCmd = async (args, deps) => {
|
|
9158
9427
|
const sub = args[0];
|
|
9428
|
+
if (sub === "add") return modelsAdd(args.slice(1), deps);
|
|
9429
|
+
if (sub === "remove") return modelsRemove(args.slice(1), deps);
|
|
9430
|
+
if (sub === "list") return modelsList(args.slice(1), deps);
|
|
9159
9431
|
if (sub === "refresh") {
|
|
9160
9432
|
deps.renderer.writeInfo("Refreshing models.dev cache\u2026");
|
|
9161
9433
|
try {
|
|
@@ -9169,9 +9441,13 @@ var modelsCmd = async (args, deps) => {
|
|
|
9169
9441
|
return 1;
|
|
9170
9442
|
}
|
|
9171
9443
|
}
|
|
9172
|
-
const
|
|
9444
|
+
const flags = parseFlags2(args);
|
|
9445
|
+
const search = typeof flags["search"] === "string" ? flags["search"].toLowerCase() : "";
|
|
9446
|
+
const perPage = Number(flags["per-page"]) > 0 ? Number(flags["per-page"]) : DEFAULT_PER_PAGE;
|
|
9447
|
+
const page = Math.max(1, Number(flags["page"]) || 1);
|
|
9448
|
+
const providerId = sub ?? deps.config.provider ?? "";
|
|
9173
9449
|
if (!providerId) {
|
|
9174
|
-
deps.renderer.writeError("Usage: wstack models <provider>
|
|
9450
|
+
deps.renderer.writeError("Usage: wstack models <provider> [--search <term>] [--page N] [--per-page N]");
|
|
9175
9451
|
return 1;
|
|
9176
9452
|
}
|
|
9177
9453
|
let lookupId = providerId;
|
|
@@ -9195,34 +9471,209 @@ var modelsCmd = async (args, deps) => {
|
|
|
9195
9471
|
`));
|
|
9196
9472
|
const userModels = deps.config.providers?.[providerId]?.models;
|
|
9197
9473
|
const catalogById = new Map(provider.models.map((m) => [m.id, m]));
|
|
9198
|
-
const
|
|
9474
|
+
const allSorted = userModels && userModels.length > 0 ? userModels.map((id) => catalogById.get(id) ?? { id, name: id }) : [...provider.models].sort(
|
|
9199
9475
|
(a, b) => (b.release_date ?? "").localeCompare(a.release_date ?? "")
|
|
9200
9476
|
);
|
|
9201
9477
|
if (userModels && userModels.length > 0)
|
|
9202
9478
|
deps.renderer.write(color.dim(`(${userModels.length} model(s) from your saved config)
|
|
9203
9479
|
`));
|
|
9204
|
-
|
|
9205
|
-
|
|
9206
|
-
|
|
9207
|
-
|
|
9208
|
-
|
|
9209
|
-
|
|
9210
|
-
|
|
9211
|
-
|
|
9212
|
-
|
|
9480
|
+
const filtered = search ? allSorted.filter((m) => m.id.toLowerCase().includes(search)) : allSorted;
|
|
9481
|
+
const total = filtered.length;
|
|
9482
|
+
const totalPages = Math.max(1, Math.ceil(total / perPage));
|
|
9483
|
+
const actualPage = Math.min(page, totalPages);
|
|
9484
|
+
const start = (actualPage - 1) * perPage;
|
|
9485
|
+
const pageItems = filtered.slice(start, start + perPage);
|
|
9486
|
+
const end = Math.min(start + pageItems.length, total);
|
|
9487
|
+
const pageHint = totalPages > 1 ? color.cyan(`[page ${actualPage}/${totalPages}]`) : "";
|
|
9488
|
+
const searchHint = search ? color.yellow(` (filtered: "${search}" \u2014 ${total} match${total === 1 ? "" : "es"})`) : color.dim(` (${total} model${total === 1 ? "" : "s"})`);
|
|
9489
|
+
deps.renderer.write(`${pageHint}${searchHint}
|
|
9490
|
+
`);
|
|
9491
|
+
if (pageItems.length === 0) {
|
|
9492
|
+
deps.renderer.write(color.dim("(no models match)\n"));
|
|
9493
|
+
} else {
|
|
9494
|
+
if (start > 0)
|
|
9495
|
+
deps.renderer.write(color.dim(` ${String.fromCharCode(8593)} ${start} above
|
|
9496
|
+
`));
|
|
9497
|
+
for (const m of pageItems) {
|
|
9498
|
+
const caps = [];
|
|
9499
|
+
if ("tool_call" in m && m.tool_call) caps.push("tools");
|
|
9500
|
+
if ("reasoning" in m && m.reasoning) caps.push("reasoning");
|
|
9501
|
+
if ("modalities" in m && m.modalities?.input?.includes("image")) caps.push("vision");
|
|
9502
|
+
const ctx = "limit" in m && m.limit?.context ? `${(m.limit.context / 1e3).toFixed(0)}k` : "?";
|
|
9503
|
+
const cost = "cost" in m && m.cost?.input !== void 0 ? `${m.cost.input}/${m.cost.output ?? "?"}` : "";
|
|
9504
|
+
deps.renderer.write(
|
|
9505
|
+
` ${m.id.padEnd(40)} ${color.dim(ctx.padStart(6))} ${color.dim(cost.padEnd(14))} ${color.dim(caps.join(","))}
|
|
9213
9506
|
`
|
|
9214
|
-
|
|
9507
|
+
);
|
|
9508
|
+
}
|
|
9509
|
+
if (end < total)
|
|
9510
|
+
deps.renderer.write(color.dim(` ${String.fromCharCode(8595)} ${total - end} below
|
|
9511
|
+
`));
|
|
9512
|
+
}
|
|
9513
|
+
const navLines = [];
|
|
9514
|
+
if (totalPages > 1) {
|
|
9515
|
+
if (actualPage > 1) navLines.push(`--page ${actualPage - 1} (prev)`);
|
|
9516
|
+
if (actualPage < totalPages) navLines.push(`--page ${actualPage + 1} (next)`);
|
|
9215
9517
|
}
|
|
9518
|
+
navLines.push("--search <term> (filter)");
|
|
9519
|
+
deps.renderer.write(color.dim(`
|
|
9520
|
+
${navLines.join(" \xB7 ")}
|
|
9521
|
+
`));
|
|
9216
9522
|
const age = await deps.modelsRegistry.ageSeconds();
|
|
9217
9523
|
deps.renderer.write(
|
|
9218
9524
|
color.dim(
|
|
9219
|
-
`
|
|
9220
|
-
Cache age: ${isFinite(age) ? `${Math.round(age / 60)}m` : "never fetched"}. Run \`wstack models refresh\` to update.
|
|
9525
|
+
`Cache age: ${isFinite(age) ? `${Math.round(age / 60)}m` : "never fetched"}. Run \`wstack models refresh\` to update.
|
|
9221
9526
|
`
|
|
9222
9527
|
)
|
|
9223
9528
|
);
|
|
9224
9529
|
return 0;
|
|
9225
9530
|
};
|
|
9531
|
+
async function mutateModelsConfig(deps, mutator) {
|
|
9532
|
+
const vault = deps.vault;
|
|
9533
|
+
const configPath2 = deps.paths.globalConfig;
|
|
9534
|
+
let fileExists = true;
|
|
9535
|
+
let raw;
|
|
9536
|
+
try {
|
|
9537
|
+
raw = await fsp3.readFile(configPath2, "utf8");
|
|
9538
|
+
} catch (err) {
|
|
9539
|
+
if (err.code !== "ENOENT") throw err;
|
|
9540
|
+
fileExists = false;
|
|
9541
|
+
raw = "{}";
|
|
9542
|
+
}
|
|
9543
|
+
let parsed;
|
|
9544
|
+
try {
|
|
9545
|
+
parsed = JSON.parse(raw);
|
|
9546
|
+
} catch (err) {
|
|
9547
|
+
if (fileExists) {
|
|
9548
|
+
throw new Error(
|
|
9549
|
+
`Refusing to overwrite corrupt config at ${configPath2} (${err.message}).`
|
|
9550
|
+
);
|
|
9551
|
+
}
|
|
9552
|
+
parsed = {};
|
|
9553
|
+
}
|
|
9554
|
+
const decrypted = decryptConfigSecrets$1(parsed, vault);
|
|
9555
|
+
const models = decrypted.models ?? {};
|
|
9556
|
+
mutator(models);
|
|
9557
|
+
decrypted.models = models;
|
|
9558
|
+
const encrypted = encryptConfigSecrets$1(decrypted, vault);
|
|
9559
|
+
await atomicWrite(configPath2, JSON.stringify(encrypted, null, 2), { mode: 384 });
|
|
9560
|
+
}
|
|
9561
|
+
function parseSizeFlag(raw) {
|
|
9562
|
+
if (!raw) return void 0;
|
|
9563
|
+
const s = raw.trim().toLowerCase();
|
|
9564
|
+
const match = /^(\d+(?:\.\d+)?)\s*(k|m|b)?$/.exec(s);
|
|
9565
|
+
if (!match) return void 0;
|
|
9566
|
+
const num = Number.parseFloat(match[1]);
|
|
9567
|
+
const unit = match[2];
|
|
9568
|
+
if (unit === "b") return Math.round(num * 1e9);
|
|
9569
|
+
if (unit === "m") return Math.round(num * 1e6);
|
|
9570
|
+
if (unit === "k") return Math.round(num * 1e3);
|
|
9571
|
+
return Math.round(num);
|
|
9572
|
+
}
|
|
9573
|
+
function parseBoolFlag(flags, key) {
|
|
9574
|
+
if (flags[key] === true || flags[key] === "true") return true;
|
|
9575
|
+
if (flags[`no-${key}`] !== void 0) return false;
|
|
9576
|
+
return void 0;
|
|
9577
|
+
}
|
|
9578
|
+
async function modelsAdd(args, deps) {
|
|
9579
|
+
const flags = parseFlags2(args);
|
|
9580
|
+
const pos = positionals(args);
|
|
9581
|
+
const modelId = pos[0];
|
|
9582
|
+
if (!modelId) {
|
|
9583
|
+
deps.renderer.writeError(
|
|
9584
|
+
"Usage: wstack models add <modelId> [--provider <id>] [--name <name>] [--max-context <N>] [--max-output <N>] [--tools] [--no-tools] [--vision] [--no-vision] [--reasoning] [--streaming] [--no-streaming] [--json-mode]"
|
|
9585
|
+
);
|
|
9586
|
+
return 1;
|
|
9587
|
+
}
|
|
9588
|
+
const existing = deps.config.models?.[modelId];
|
|
9589
|
+
if (existing) {
|
|
9590
|
+
deps.renderer.writeWarning(
|
|
9591
|
+
`Model "${modelId}" already defined. Overwriting.`
|
|
9592
|
+
);
|
|
9593
|
+
}
|
|
9594
|
+
const capabilities = {};
|
|
9595
|
+
const toolsVal = parseBoolFlag(flags, "tools");
|
|
9596
|
+
if (toolsVal !== void 0) capabilities.tools = toolsVal;
|
|
9597
|
+
const visionVal = parseBoolFlag(flags, "vision");
|
|
9598
|
+
if (visionVal !== void 0) capabilities.vision = visionVal;
|
|
9599
|
+
const streamingVal = parseBoolFlag(flags, "streaming");
|
|
9600
|
+
if (streamingVal !== void 0) capabilities.streaming = streamingVal;
|
|
9601
|
+
const reasoningVal = parseBoolFlag(flags, "reasoning");
|
|
9602
|
+
if (reasoningVal !== void 0) capabilities.reasoning = reasoningVal;
|
|
9603
|
+
const jsonModeVal = parseBoolFlag(flags, "json-mode");
|
|
9604
|
+
if (jsonModeVal !== void 0) capabilities.jsonMode = jsonModeVal;
|
|
9605
|
+
const maxContextRaw = typeof flags["max-context"] === "string" ? flags["max-context"] : void 0;
|
|
9606
|
+
const maxContext = parseSizeFlag(maxContextRaw);
|
|
9607
|
+
if (maxContext !== void 0) capabilities.maxContext = maxContext;
|
|
9608
|
+
const def = {};
|
|
9609
|
+
const nameFlag = typeof flags["name"] === "string" ? flags["name"] : void 0;
|
|
9610
|
+
const providerFlag = typeof flags["provider"] === "string" ? flags["provider"] : void 0;
|
|
9611
|
+
if (nameFlag) def.name = nameFlag;
|
|
9612
|
+
if (providerFlag) def.provider = providerFlag;
|
|
9613
|
+
if (Object.keys(capabilities).length > 0) def.capabilities = capabilities;
|
|
9614
|
+
const maxOutputRaw = typeof flags["max-output"] === "string" ? flags["max-output"] : void 0;
|
|
9615
|
+
const maxOutput = parseSizeFlag(maxOutputRaw);
|
|
9616
|
+
if (maxOutput !== void 0) def.maxOutput = maxOutput;
|
|
9617
|
+
await mutateModelsConfig(deps, (models) => {
|
|
9618
|
+
models[modelId] = def;
|
|
9619
|
+
});
|
|
9620
|
+
deps.renderer.writeInfo(`Custom model "${modelId}" ${existing ? "updated" : "added"}.`);
|
|
9621
|
+
const capLines = [];
|
|
9622
|
+
if (def.capabilities) {
|
|
9623
|
+
for (const [k, v] of Object.entries(def.capabilities)) {
|
|
9624
|
+
capLines.push(` ${k}: ${v}`);
|
|
9625
|
+
}
|
|
9626
|
+
}
|
|
9627
|
+
if (def.maxOutput !== void 0) capLines.push(` maxOutput: ${def.maxOutput}`);
|
|
9628
|
+
if (capLines.length > 0) {
|
|
9629
|
+
deps.renderer.write(color.dim(capLines.join("\n") + "\n"));
|
|
9630
|
+
}
|
|
9631
|
+
return 0;
|
|
9632
|
+
}
|
|
9633
|
+
async function modelsRemove(args, deps) {
|
|
9634
|
+
const modelId = args[0];
|
|
9635
|
+
if (!modelId) {
|
|
9636
|
+
deps.renderer.writeError("Usage: wstack models remove <modelId>");
|
|
9637
|
+
return 1;
|
|
9638
|
+
}
|
|
9639
|
+
const existing = deps.config.models?.[modelId];
|
|
9640
|
+
if (!existing) {
|
|
9641
|
+
deps.renderer.writeError(`No custom model "${modelId}" found.`);
|
|
9642
|
+
return 1;
|
|
9643
|
+
}
|
|
9644
|
+
await mutateModelsConfig(deps, (models) => {
|
|
9645
|
+
delete models[modelId];
|
|
9646
|
+
});
|
|
9647
|
+
deps.renderer.writeInfo(`Removed custom model "${modelId}".`);
|
|
9648
|
+
return 0;
|
|
9649
|
+
}
|
|
9650
|
+
async function modelsList(_args, deps) {
|
|
9651
|
+
const models = deps.config.models ?? {};
|
|
9652
|
+
const entries = Object.entries(models);
|
|
9653
|
+
if (entries.length === 0) {
|
|
9654
|
+
deps.renderer.write(color.dim("No custom models defined.\n"));
|
|
9655
|
+
deps.renderer.write(color.dim("Use `wstack models add <modelId> --max-context 128k --tools`\n"));
|
|
9656
|
+
return 0;
|
|
9657
|
+
}
|
|
9658
|
+
deps.renderer.write(color.bold("Custom models\n"));
|
|
9659
|
+
for (const [id, def] of entries.sort(([a], [b]) => a.localeCompare(b))) {
|
|
9660
|
+
const label = def.name ?? id;
|
|
9661
|
+
const provider = def.provider ? ` ${color.dim(`(${def.provider})`)}` : "";
|
|
9662
|
+
deps.renderer.write(` ${color.bold(label)}${provider}
|
|
9663
|
+
`);
|
|
9664
|
+
if (def.capabilities) {
|
|
9665
|
+
for (const [k, v] of Object.entries(def.capabilities)) {
|
|
9666
|
+
deps.renderer.write(` ${color.dim(`${k}:`)} ${v}
|
|
9667
|
+
`);
|
|
9668
|
+
}
|
|
9669
|
+
}
|
|
9670
|
+
if (def.maxOutput !== void 0) {
|
|
9671
|
+
deps.renderer.write(` ${color.dim("maxOutput:")} ${def.maxOutput}
|
|
9672
|
+
`);
|
|
9673
|
+
}
|
|
9674
|
+
}
|
|
9675
|
+
return 0;
|
|
9676
|
+
}
|
|
9226
9677
|
function redactKeys(obj) {
|
|
9227
9678
|
if (!obj || typeof obj !== "object") return obj;
|
|
9228
9679
|
if (Array.isArray(obj)) return obj.map(redactKeys);
|
|
@@ -9815,10 +10266,10 @@ var auditCmd = async (args, deps) => {
|
|
|
9815
10266
|
return verify.ok ? 0 : 1;
|
|
9816
10267
|
};
|
|
9817
10268
|
async function listAudits(log, dir, deps) {
|
|
9818
|
-
const
|
|
10269
|
+
const fs24 = await import('fs/promises');
|
|
9819
10270
|
let entries;
|
|
9820
10271
|
try {
|
|
9821
|
-
entries = await
|
|
10272
|
+
entries = await fs24.readdir(dir);
|
|
9822
10273
|
} catch {
|
|
9823
10274
|
deps.renderer.write(
|
|
9824
10275
|
color.dim(`No sessions dir found at ${dir}. Run a session first.`) + "\n"
|
|
@@ -9895,6 +10346,9 @@ var helpCmd = async (_args, deps) => {
|
|
|
9895
10346
|
" wstack providers [--all] List providers from models.dev",
|
|
9896
10347
|
" wstack models [<provider>] List models",
|
|
9897
10348
|
" wstack models refresh Force-refresh cache",
|
|
10349
|
+
" wstack models add <mid> Add/override custom model (--max-context, --tools, --vision, \u2026)",
|
|
10350
|
+
" wstack models remove <mid> Remove a custom model",
|
|
10351
|
+
" wstack models list List all custom models",
|
|
9898
10352
|
" wstack mcp [list] List MCP servers",
|
|
9899
10353
|
" wstack plugin [list|status|official|install|add|remove|enable|disable] Manage plugins",
|
|
9900
10354
|
" wstack projects List tracked projects",
|
|
@@ -9955,22 +10409,22 @@ function fmtDuration(ms) {
|
|
|
9955
10409
|
const remMin = m - h * 60;
|
|
9956
10410
|
return `${h}h${remMin}m`;
|
|
9957
10411
|
}
|
|
9958
|
-
function fmtTaskResultLine(r,
|
|
10412
|
+
function fmtTaskResultLine(r, color46) {
|
|
9959
10413
|
const stats = `${r.iterations}it ${r.toolCalls}tc ${fmtDuration(r.durationMs)}`;
|
|
9960
10414
|
const errMsg = typeof r.error === "string" ? r.error : r.error?.message;
|
|
9961
10415
|
const errKind = typeof r.error === "object" ? r.error?.kind : void 0;
|
|
9962
10416
|
const errTail = errMsg ? ` \u2014 ${errMsg.replace(/\s+/g, " ").slice(0, 80)}${errMsg.length > 80 ? "\u2026" : ""}` : "";
|
|
9963
|
-
const errKindChip = errKind ?
|
|
9964
|
-
const errSnip = errMsg || errKind ? `${errKindChip}${
|
|
10417
|
+
const errKindChip = errKind ? color46.dim(` [${errKind}]`) : "";
|
|
10418
|
+
const errSnip = errMsg || errKind ? `${errKindChip}${color46.dim(errTail)}` : "";
|
|
9965
10419
|
switch (r.status) {
|
|
9966
10420
|
case "success":
|
|
9967
|
-
return { mark:
|
|
10421
|
+
return { mark: color46.green("\u2713"), stats, tail: "" };
|
|
9968
10422
|
case "timeout":
|
|
9969
|
-
return { mark:
|
|
10423
|
+
return { mark: color46.yellow("\u23F1"), stats: `${color46.yellow("timeout")} ${stats}`, tail: errSnip };
|
|
9970
10424
|
case "stopped":
|
|
9971
|
-
return { mark:
|
|
10425
|
+
return { mark: color46.dim("\u2298"), stats: `${color46.dim("stopped")} ${stats}`, tail: errSnip };
|
|
9972
10426
|
case "failed":
|
|
9973
|
-
return { mark:
|
|
10427
|
+
return { mark: color46.red("\u2717"), stats: `${color46.red("failed")} ${stats}`, tail: errSnip };
|
|
9974
10428
|
}
|
|
9975
10429
|
}
|
|
9976
10430
|
|
|
@@ -10038,6 +10492,15 @@ async function boot(argv) {
|
|
|
10038
10492
|
}).catch(() => {
|
|
10039
10493
|
});
|
|
10040
10494
|
}
|
|
10495
|
+
if (!flags["no-models-refresh"]) {
|
|
10496
|
+
try {
|
|
10497
|
+
await modelsRegistry.refresh();
|
|
10498
|
+
logger.info("models.dev catalog refreshed");
|
|
10499
|
+
} catch (err) {
|
|
10500
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
10501
|
+
logger.warn(`models.dev refresh failed (${msg}); using cached catalog`);
|
|
10502
|
+
}
|
|
10503
|
+
}
|
|
10041
10504
|
const first = positional[0];
|
|
10042
10505
|
if (first && subcommands[first]) {
|
|
10043
10506
|
const container = createDefaultContainer({
|
|
@@ -11139,7 +11602,17 @@ async function execute(deps) {
|
|
|
11139
11602
|
const visionAdapters = () => createToolVisionAdapters(agent.tools);
|
|
11140
11603
|
const supportsVision = async () => {
|
|
11141
11604
|
try {
|
|
11142
|
-
const
|
|
11605
|
+
const providerConfig = config.providers?.[context.provider.id];
|
|
11606
|
+
const mergedModels = mergeCustomModelDefs(
|
|
11607
|
+
providerConfig?.customModels,
|
|
11608
|
+
config.models
|
|
11609
|
+
);
|
|
11610
|
+
const caps = await capabilitiesFor(
|
|
11611
|
+
modelsRegistry,
|
|
11612
|
+
context.provider.id,
|
|
11613
|
+
context.model,
|
|
11614
|
+
mergedModels
|
|
11615
|
+
);
|
|
11143
11616
|
return caps.vision;
|
|
11144
11617
|
} catch {
|
|
11145
11618
|
return context.provider.capabilities.vision;
|
|
@@ -12833,7 +13306,7 @@ async function setupCompaction(params) {
|
|
|
12833
13306
|
modelId: config.model ?? context.model
|
|
12834
13307
|
});
|
|
12835
13308
|
let autoCompactor;
|
|
12836
|
-
if (config.context.autoCompact !== false) {
|
|
13309
|
+
if (config.context.autoCompact !== false && effectiveMaxContext > 0) {
|
|
12837
13310
|
const auditLevel = resolveAuditLevel(fullConfig ?? config);
|
|
12838
13311
|
const sessionBridge = providedBridge ?? createSessionEventBridge(sessionWriter, auditLevel);
|
|
12839
13312
|
autoCompactor = new AutoCompactionMiddleware(
|
|
@@ -13424,7 +13897,12 @@ async function main(argv) {
|
|
|
13424
13897
|
const modeId = activeMode?.id ?? "default";
|
|
13425
13898
|
const modePrompt = activeMode?.prompt ?? "";
|
|
13426
13899
|
const [resolvedCaps, resolvedModel] = await Promise.all([
|
|
13427
|
-
capabilitiesFor(
|
|
13900
|
+
capabilitiesFor(
|
|
13901
|
+
modelsRegistry,
|
|
13902
|
+
provider.id,
|
|
13903
|
+
config.model,
|
|
13904
|
+
mergeCustomModelDefs(config.providers?.[provider.id]?.customModels, config.models)
|
|
13905
|
+
).catch(() => void 0),
|
|
13428
13906
|
modelsRegistry.getModel(config.provider, config.model).catch(() => void 0)
|
|
13429
13907
|
]);
|
|
13430
13908
|
const modelCapabilities = resolvedCaps ? {
|
|
@@ -13668,9 +14146,11 @@ async function main(argv) {
|
|
|
13668
14146
|
providerId,
|
|
13669
14147
|
modelId
|
|
13670
14148
|
});
|
|
13671
|
-
effectiveMaxContext = mc
|
|
14149
|
+
effectiveMaxContext = mc;
|
|
13672
14150
|
context.provider.capabilities.maxContext = effectiveMaxContext;
|
|
13673
|
-
|
|
14151
|
+
if (effectiveMaxContext > 0) {
|
|
14152
|
+
autoCompactor?.setMaxContext(effectiveMaxContext);
|
|
14153
|
+
}
|
|
13674
14154
|
events.emit("ctx.max_context", { providerId, modelId, maxContext: effectiveMaxContext });
|
|
13675
14155
|
updateSpinnerContext();
|
|
13676
14156
|
};
|