@serkanalgur/opencode-nexus 1.2.7 → 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.
Files changed (2) hide show
  1. package/dist/index.js +93 -0
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -9740,6 +9740,7 @@ class NexusOrchestrator {
9740
9740
  return this._healthMonitor;
9741
9741
  }
9742
9742
  notifications = null;
9743
+ modelCosts = new Map;
9743
9744
  constructor(config, messageStoreConfig, memoryStoreConfig) {
9744
9745
  this.config = this.mergeConfig(config);
9745
9746
  this.budget = this.config.budget;
@@ -9761,6 +9762,7 @@ class NexusOrchestrator {
9761
9762
  this.ctx = ctx;
9762
9763
  this.onStateChange = onStateChange ?? null;
9763
9764
  this.configManager.loadFromPath(process.cwd());
9765
+ await this.loadModelCosts();
9764
9766
  this.cleanupInterval = setInterval(() => this.cleanupStaleData(), 300000);
9765
9767
  this._healthMonitor = new HealthMonitor({
9766
9768
  checkInterval: this.config.agents.healthCheckInterval
@@ -9776,6 +9778,56 @@ class NexusOrchestrator {
9776
9778
  };
9777
9779
  await this.moduleRegistry.setupAll(moduleCtx);
9778
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
+ }
9779
9831
  initBroadcaster(opts) {
9780
9832
  this.broadcaster = new StateBroadcaster(this, opts);
9781
9833
  const previousOnStateChange = this.onStateChange;
@@ -10354,6 +10406,10 @@ Please continue from where the previous agent left off.`;
10354
10406
  return this.selectBestModel(role, complexity);
10355
10407
  }
10356
10408
  estimateModelCost(model) {
10409
+ const realCost = this.modelCosts.get(model);
10410
+ if (realCost) {
10411
+ return (realCost.input * 1000 + realCost.output * 1000) / 2;
10412
+ }
10357
10413
  const costs = {
10358
10414
  "claude-sonnet-4-6": 0.15,
10359
10415
  "claude-opus-4-7": 15,
@@ -10905,6 +10961,43 @@ var src_default = define({
10905
10961
  return { content: "Dashboard stopped" };
10906
10962
  }
10907
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
+ });
10908
11001
  editor.add({
10909
11002
  name: "spawn",
10910
11003
  description: "Spawn a sub-agent for a task",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@serkanalgur/opencode-nexus",
3
- "version": "1.2.7",
3
+ "version": "1.3.0",
4
4
  "description": "Adaptive Multi-Agent Orchestration with Cost Intelligence for OpenCode V2",
5
5
  "keywords": [
6
6
  "opencode",