@serkanalgur/opencode-nexus 1.2.6 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +99 -32
- package/dist/tui.js +6 -32
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -8559,12 +8559,12 @@ class NexusConfigManager {
|
|
|
8559
8559
|
}
|
|
8560
8560
|
saveProjectConfig(basePath) {
|
|
8561
8561
|
const projectPath = join(basePath, ".opencode", "nexus.jsonc");
|
|
8562
|
-
const config = this.
|
|
8562
|
+
const config = this.getSaveableConfig();
|
|
8563
8563
|
this.writeJsoncFile(projectPath, config);
|
|
8564
8564
|
}
|
|
8565
8565
|
saveGlobalConfig() {
|
|
8566
8566
|
const globalPath = join(homedir(), ".config", "opencode", "nexus.jsonc");
|
|
8567
|
-
const config = this.
|
|
8567
|
+
const config = this.getSaveableConfig();
|
|
8568
8568
|
this.writeJsoncFile(globalPath, config);
|
|
8569
8569
|
}
|
|
8570
8570
|
initProjectConfig(basePath) {
|
|
@@ -8582,38 +8582,12 @@ class NexusConfigManager {
|
|
|
8582
8582
|
this.saveGlobalConfig();
|
|
8583
8583
|
}
|
|
8584
8584
|
}
|
|
8585
|
-
|
|
8585
|
+
getSaveableConfig() {
|
|
8586
8586
|
const current = this.getConfig();
|
|
8587
8587
|
const result = {};
|
|
8588
|
-
|
|
8589
|
-
|
|
8590
|
-
|
|
8591
|
-
models[role] = current.models[role];
|
|
8592
|
-
}
|
|
8593
|
-
}
|
|
8594
|
-
if (Object.keys(models).length > 0) {
|
|
8595
|
-
result.models = models;
|
|
8596
|
-
}
|
|
8597
|
-
const budget = {};
|
|
8598
|
-
const budgetKeys = ["maxTotalCost", "maxCostPerTask", "maxCostPerAgent", "alertThreshold"];
|
|
8599
|
-
for (const key of budgetKeys) {
|
|
8600
|
-
if (current.budget[key] !== DEFAULT_CONFIG.budget[key]) {
|
|
8601
|
-
budget[key] = current.budget[key];
|
|
8602
|
-
}
|
|
8603
|
-
}
|
|
8604
|
-
if (Object.keys(budget).length > 0) {
|
|
8605
|
-
result.budget = budget;
|
|
8606
|
-
}
|
|
8607
|
-
const selfHealing = {};
|
|
8608
|
-
const shKeys = ["enabled", "maxRetries", "contextTransfer"];
|
|
8609
|
-
for (const key of shKeys) {
|
|
8610
|
-
if (current.selfHealing[key] !== DEFAULT_CONFIG.selfHealing[key]) {
|
|
8611
|
-
selfHealing[key] = current.selfHealing[key];
|
|
8612
|
-
}
|
|
8613
|
-
}
|
|
8614
|
-
if (Object.keys(selfHealing).length > 0) {
|
|
8615
|
-
result.selfHealing = selfHealing;
|
|
8616
|
-
}
|
|
8588
|
+
result.models = { ...current.models };
|
|
8589
|
+
result.budget = { ...current.budget };
|
|
8590
|
+
result.selfHealing = { ...current.selfHealing };
|
|
8617
8591
|
return result;
|
|
8618
8592
|
}
|
|
8619
8593
|
resetToDefaults() {
|
|
@@ -9766,6 +9740,7 @@ class NexusOrchestrator {
|
|
|
9766
9740
|
return this._healthMonitor;
|
|
9767
9741
|
}
|
|
9768
9742
|
notifications = null;
|
|
9743
|
+
modelCosts = new Map;
|
|
9769
9744
|
constructor(config, messageStoreConfig, memoryStoreConfig) {
|
|
9770
9745
|
this.config = this.mergeConfig(config);
|
|
9771
9746
|
this.budget = this.config.budget;
|
|
@@ -9787,6 +9762,7 @@ class NexusOrchestrator {
|
|
|
9787
9762
|
this.ctx = ctx;
|
|
9788
9763
|
this.onStateChange = onStateChange ?? null;
|
|
9789
9764
|
this.configManager.loadFromPath(process.cwd());
|
|
9765
|
+
await this.loadModelCosts();
|
|
9790
9766
|
this.cleanupInterval = setInterval(() => this.cleanupStaleData(), 300000);
|
|
9791
9767
|
this._healthMonitor = new HealthMonitor({
|
|
9792
9768
|
checkInterval: this.config.agents.healthCheckInterval
|
|
@@ -9802,6 +9778,56 @@ class NexusOrchestrator {
|
|
|
9802
9778
|
};
|
|
9803
9779
|
await this.moduleRegistry.setupAll(moduleCtx);
|
|
9804
9780
|
}
|
|
9781
|
+
async loadModelCosts() {
|
|
9782
|
+
try {
|
|
9783
|
+
if (!this.ctx)
|
|
9784
|
+
return;
|
|
9785
|
+
if (this.ctx.client?.model?.list) {
|
|
9786
|
+
const result = await this.ctx.client.model.list();
|
|
9787
|
+
const models = result?.data?.data ?? result?.data ?? [];
|
|
9788
|
+
if (Array.isArray(models)) {
|
|
9789
|
+
for (const model of models) {
|
|
9790
|
+
if (model.cost && Array.isArray(model.cost) && model.cost.length > 0) {
|
|
9791
|
+
const baseCost = model.cost[0];
|
|
9792
|
+
this.modelCosts.set(model.id, {
|
|
9793
|
+
input: baseCost.input || 0,
|
|
9794
|
+
output: baseCost.output || 0,
|
|
9795
|
+
cacheRead: baseCost.cache?.read || 0,
|
|
9796
|
+
cacheWrite: baseCost.cache?.write || 0
|
|
9797
|
+
});
|
|
9798
|
+
}
|
|
9799
|
+
}
|
|
9800
|
+
}
|
|
9801
|
+
return;
|
|
9802
|
+
}
|
|
9803
|
+
const location = this.ctx.location ?? this.ctx.data?.location?.default();
|
|
9804
|
+
if (location && this.ctx.data?.location?.model) {
|
|
9805
|
+
await this.ctx.data.location.model.sync(location);
|
|
9806
|
+
const models = this.ctx.data.location.model.list(location) ?? [];
|
|
9807
|
+
for (const model of models) {
|
|
9808
|
+
if (model.cost && Array.isArray(model.cost) && model.cost.length > 0) {
|
|
9809
|
+
const baseCost = model.cost[0];
|
|
9810
|
+
this.modelCosts.set(model.id, {
|
|
9811
|
+
input: baseCost.input || 0,
|
|
9812
|
+
output: baseCost.output || 0,
|
|
9813
|
+
cacheRead: baseCost.cache?.read || 0,
|
|
9814
|
+
cacheWrite: baseCost.cache?.write || 0
|
|
9815
|
+
});
|
|
9816
|
+
}
|
|
9817
|
+
}
|
|
9818
|
+
}
|
|
9819
|
+
} catch {}
|
|
9820
|
+
}
|
|
9821
|
+
setModelCosts(costs) {
|
|
9822
|
+
for (const [model, cost] of Object.entries(costs)) {
|
|
9823
|
+
this.modelCosts.set(model, {
|
|
9824
|
+
input: cost.input,
|
|
9825
|
+
output: cost.output,
|
|
9826
|
+
cacheRead: cost.cacheRead || 0,
|
|
9827
|
+
cacheWrite: cost.cacheWrite || 0
|
|
9828
|
+
});
|
|
9829
|
+
}
|
|
9830
|
+
}
|
|
9805
9831
|
initBroadcaster(opts) {
|
|
9806
9832
|
this.broadcaster = new StateBroadcaster(this, opts);
|
|
9807
9833
|
const previousOnStateChange = this.onStateChange;
|
|
@@ -10380,6 +10406,10 @@ Please continue from where the previous agent left off.`;
|
|
|
10380
10406
|
return this.selectBestModel(role, complexity);
|
|
10381
10407
|
}
|
|
10382
10408
|
estimateModelCost(model) {
|
|
10409
|
+
const realCost = this.modelCosts.get(model);
|
|
10410
|
+
if (realCost) {
|
|
10411
|
+
return (realCost.input * 1000 + realCost.output * 1000) / 2;
|
|
10412
|
+
}
|
|
10383
10413
|
const costs = {
|
|
10384
10414
|
"claude-sonnet-4-6": 0.15,
|
|
10385
10415
|
"claude-opus-4-7": 15,
|
|
@@ -10931,6 +10961,43 @@ var src_default = define({
|
|
|
10931
10961
|
return { content: "Dashboard stopped" };
|
|
10932
10962
|
}
|
|
10933
10963
|
});
|
|
10964
|
+
editor.add({
|
|
10965
|
+
name: "model.costs",
|
|
10966
|
+
description: "Show real model pricing from OpenCode or set custom costs",
|
|
10967
|
+
input: {
|
|
10968
|
+
type: "object",
|
|
10969
|
+
properties: {
|
|
10970
|
+
model: { type: "string", description: "Model ID to show cost for (optional, shows all if omitted)" },
|
|
10971
|
+
setInput: { type: "number", description: "Set input cost per token for a model" },
|
|
10972
|
+
setOutput: { type: "number", description: "Set output cost per token for a model" }
|
|
10973
|
+
},
|
|
10974
|
+
additionalProperties: false
|
|
10975
|
+
},
|
|
10976
|
+
execute: async (input) => {
|
|
10977
|
+
const { model, setInput, setOutput } = input;
|
|
10978
|
+
if (model && setInput !== undefined && setOutput !== undefined) {
|
|
10979
|
+
orchestrator.setModelCosts({ [model]: { input: setInput, output: setOutput } });
|
|
10980
|
+
return { content: `Set ${model}: input=$${setInput}/token, output=$${setOutput}/token` };
|
|
10981
|
+
}
|
|
10982
|
+
if (model) {
|
|
10983
|
+
const cost = orchestrator.modelCosts.get(model);
|
|
10984
|
+
if (cost) {
|
|
10985
|
+
return { content: `${model}: input=$${cost.input}/token, output=$${cost.output}/token, cache_read=$${cost.cacheRead}/token, cache_write=$${cost.cacheWrite}/token` };
|
|
10986
|
+
}
|
|
10987
|
+
const estimate = orchestrator["estimateModelCost"](model);
|
|
10988
|
+
return { content: `${model}: no real pricing data (estimated $${estimate}/1K tokens)` };
|
|
10989
|
+
}
|
|
10990
|
+
if (orchestrator.modelCosts.size > 0) {
|
|
10991
|
+
const lines = ["\uD83D\uDCCA Model Pricing (from OpenCode):"];
|
|
10992
|
+
for (const [id, cost] of orchestrator.modelCosts) {
|
|
10993
|
+
lines.push(` ${id}: $${cost.input}/token in, $${cost.output}/token out`);
|
|
10994
|
+
}
|
|
10995
|
+
return { content: lines.join(`
|
|
10996
|
+
`) };
|
|
10997
|
+
}
|
|
10998
|
+
return { content: "No real pricing data loaded. Using hardcoded estimates." };
|
|
10999
|
+
}
|
|
11000
|
+
});
|
|
10934
11001
|
editor.add({
|
|
10935
11002
|
name: "spawn",
|
|
10936
11003
|
description: "Spawn a sub-agent for a task",
|
package/dist/tui.js
CHANGED
|
@@ -302,12 +302,12 @@ class NexusConfigManager {
|
|
|
302
302
|
}
|
|
303
303
|
saveProjectConfig(basePath) {
|
|
304
304
|
const projectPath = join(basePath, ".opencode", "nexus.jsonc");
|
|
305
|
-
const config = this.
|
|
305
|
+
const config = this.getSaveableConfig();
|
|
306
306
|
this.writeJsoncFile(projectPath, config);
|
|
307
307
|
}
|
|
308
308
|
saveGlobalConfig() {
|
|
309
309
|
const globalPath = join(homedir(), ".config", "opencode", "nexus.jsonc");
|
|
310
|
-
const config = this.
|
|
310
|
+
const config = this.getSaveableConfig();
|
|
311
311
|
this.writeJsoncFile(globalPath, config);
|
|
312
312
|
}
|
|
313
313
|
initProjectConfig(basePath) {
|
|
@@ -325,38 +325,12 @@ class NexusConfigManager {
|
|
|
325
325
|
this.saveGlobalConfig();
|
|
326
326
|
}
|
|
327
327
|
}
|
|
328
|
-
|
|
328
|
+
getSaveableConfig() {
|
|
329
329
|
const current = this.getConfig();
|
|
330
330
|
const result = {};
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
models[role] = current.models[role];
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
if (Object.keys(models).length > 0) {
|
|
338
|
-
result.models = models;
|
|
339
|
-
}
|
|
340
|
-
const budget = {};
|
|
341
|
-
const budgetKeys = ["maxTotalCost", "maxCostPerTask", "maxCostPerAgent", "alertThreshold"];
|
|
342
|
-
for (const key of budgetKeys) {
|
|
343
|
-
if (current.budget[key] !== DEFAULT_CONFIG.budget[key]) {
|
|
344
|
-
budget[key] = current.budget[key];
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
if (Object.keys(budget).length > 0) {
|
|
348
|
-
result.budget = budget;
|
|
349
|
-
}
|
|
350
|
-
const selfHealing = {};
|
|
351
|
-
const shKeys = ["enabled", "maxRetries", "contextTransfer"];
|
|
352
|
-
for (const key of shKeys) {
|
|
353
|
-
if (current.selfHealing[key] !== DEFAULT_CONFIG.selfHealing[key]) {
|
|
354
|
-
selfHealing[key] = current.selfHealing[key];
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
if (Object.keys(selfHealing).length > 0) {
|
|
358
|
-
result.selfHealing = selfHealing;
|
|
359
|
-
}
|
|
331
|
+
result.models = { ...current.models };
|
|
332
|
+
result.budget = { ...current.budget };
|
|
333
|
+
result.selfHealing = { ...current.selfHealing };
|
|
360
334
|
return result;
|
|
361
335
|
}
|
|
362
336
|
resetToDefaults() {
|