nexrall-code 0.5.109 → 0.5.110

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 +104 -9
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -3186,6 +3186,8 @@ var require_auth = __commonJS({
3186
3186
  exports2.getToken = getToken;
3187
3187
  exports2.getRefreshToken = getRefreshToken;
3188
3188
  exports2.updateTokens = updateTokens;
3189
+ exports2.getBillingTeamId = getBillingTeamId;
3190
+ exports2.setBillingTeamId = setBillingTeamId;
3189
3191
  var fs9 = __importStar(__require("fs"));
3190
3192
  var path8 = __importStar(__require("path"));
3191
3193
  var os6 = __importStar(__require("os"));
@@ -3263,6 +3265,30 @@ var require_auth = __commonJS({
3263
3265
  };
3264
3266
  fs9.writeFileSync(CONFIG_FILE, JSON.stringify(next, null, 2), { mode: 384 });
3265
3267
  }
3268
+ function getBillingTeamId() {
3269
+ const auth = loadAuth2();
3270
+ const v = auth?.billingTeamId;
3271
+ return typeof v === "number" && Number.isSafeInteger(v) && v > 0 ? v : null;
3272
+ }
3273
+ function setBillingTeamId(teamId) {
3274
+ let existing = {};
3275
+ try {
3276
+ if (fs9.existsSync(CONFIG_FILE)) {
3277
+ const parsed = JSON.parse(fs9.readFileSync(CONFIG_FILE, "utf-8"));
3278
+ if (typeof parsed === "object" && parsed !== null)
3279
+ existing = parsed;
3280
+ }
3281
+ } catch {
3282
+ }
3283
+ ensureConfigDir();
3284
+ const next = { ...existing };
3285
+ if (typeof teamId === "number" && Number.isSafeInteger(teamId) && teamId > 0) {
3286
+ next.billingTeamId = teamId;
3287
+ } else {
3288
+ delete next.billingTeamId;
3289
+ }
3290
+ fs9.writeFileSync(CONFIG_FILE, JSON.stringify(next, null, 2), { mode: 384 });
3291
+ }
3266
3292
  }
3267
3293
  });
3268
3294
 
@@ -9903,6 +9929,7 @@ var require_client = __commonJS({
9903
9929
  exports2.revokeRefreshToken = revokeRefreshToken2;
9904
9930
  exports2.describeAttachment = describeAttachment2;
9905
9931
  exports2.getBalance = getBalance3;
9932
+ exports2.getOrgs = getOrgs;
9906
9933
  exports2.getCodeModels = getCodeModels;
9907
9934
  exports2.getUsageDaily = getUsageDaily;
9908
9935
  exports2.exchangeVscodeCode = exchangeVscodeCode;
@@ -10091,7 +10118,7 @@ var require_client = __commonJS({
10091
10118
  {
10092
10119
  method: "POST",
10093
10120
  headers: authHeaders(),
10094
- body: JSON.stringify({ messages, model, env: env4, editorContext, nexrallMd, mode, effort, clientType, extraTools, agents, skills, turnId, canWriteSharedMemory, hasOwnAgentStore, canSpawnSubAgents, canBrowseWeb }),
10121
+ body: JSON.stringify({ messages, model, env: env4, editorContext, nexrallMd, mode, effort, clientType, extraTools, agents, skills, turnId, canWriteSharedMemory, hasOwnAgentStore, canSpawnSubAgents, canBrowseWeb, teamId: (0, index_1.getBillingTeamId)() }),
10095
10122
  signal
10096
10123
  }
10097
10124
  ];
@@ -10271,18 +10298,31 @@ var require_client = __commonJS({
10271
10298
  const errText = errorBodyOverride ?? await response.text();
10272
10299
  let errMsg = `API error ${response.status}`;
10273
10300
  let balance;
10301
+ let code;
10302
+ let remaining;
10303
+ let teamId;
10274
10304
  try {
10275
10305
  const parsed = JSON.parse(errText);
10276
10306
  if (parsed.error)
10277
10307
  errMsg = parsed.error;
10278
10308
  if (typeof parsed.balance === "number")
10279
10309
  balance = parsed.balance;
10310
+ if (typeof parsed.code === "string")
10311
+ code = parsed.code;
10312
+ if (typeof parsed.remaining === "number")
10313
+ remaining = parsed.remaining;
10314
+ if (typeof parsed.teamId === "number")
10315
+ teamId = parsed.teamId;
10280
10316
  } catch {
10281
10317
  errMsg = errText || errMsg;
10282
10318
  }
10283
10319
  throw Object.assign(new Error(errMsg), {
10284
10320
  status: response.status,
10285
10321
  balance,
10322
+ // Team-billing context (migration 139) — see the parse above.
10323
+ ...code ? { code } : {},
10324
+ ...remaining !== void 0 ? { remaining } : {},
10325
+ ...teamId !== void 0 ? { teamId } : {},
10286
10326
  ...forbiddenExhausted ? { retryable: true } : {},
10287
10327
  ...sessionExpired ? { authExpired: true } : {}
10288
10328
  });
@@ -10650,7 +10690,7 @@ var require_client = __commonJS({
10650
10690
  const fetchOnce = () => (0, node_fetch_1.default)(`${exports2.API_BASE}/api/code/assets/describe`, {
10651
10691
  method: "POST",
10652
10692
  headers: { ...authHeaders(), "Content-Type": "application/json" },
10653
- body: JSON.stringify({ kind, data, media_type: mediaType, name })
10693
+ body: JSON.stringify({ kind, data, media_type: mediaType, name, teamId: (0, index_1.getBillingTeamId)() })
10654
10694
  });
10655
10695
  let response = await fetchOnce();
10656
10696
  if (response.status === 401 || response.status === 403) {
@@ -10695,6 +10735,26 @@ var require_client = __commonJS({
10695
10735
  const data = await response.json();
10696
10736
  return typeof data.balance === "number" ? data.balance : 0;
10697
10737
  }
10738
+ async function getOrgs() {
10739
+ const fetchOnce = () => (0, node_fetch_1.default)(`${exports2.API_BASE}/api/code/orgs`, { method: "GET", headers: authHeaders() });
10740
+ let response = await fetchOnce();
10741
+ if (response.status === 401 || response.status === 403) {
10742
+ const body = await response.text().catch(() => "");
10743
+ if (isExpiredTokenResponse(response.status, body) && await refreshAccessToken()) {
10744
+ response = await fetchOnce();
10745
+ } else {
10746
+ throw new Error(`API error ${response.status}: ${body}`);
10747
+ }
10748
+ }
10749
+ if (!response.ok) {
10750
+ throw new Error(`API error ${response.status}`);
10751
+ }
10752
+ const json = await response.json();
10753
+ return {
10754
+ personal: { balance: typeof json.personal?.balance === "number" ? json.personal.balance : 0 },
10755
+ teams: Array.isArray(json.teams) ? json.teams : []
10756
+ };
10757
+ }
10698
10758
  async function getCodeModels() {
10699
10759
  const fetchOnce = () => (0, node_fetch_1.default)(`${exports2.API_BASE}/api/code/models`, { method: "GET", headers: authHeaders() });
10700
10760
  let response = await fetchOnce();
@@ -102947,7 +103007,7 @@ ${lines.join("\n")}`;
102947
103007
  let cost;
102948
103008
  let balance;
102949
103009
  try {
102950
- const r2 = await _httpsPost(`${client_1.API_BASE}/api/code/assets/generate`, JSON.stringify({ prompt: prompt2, aspect_ratio: aspectRatio }), { "Content-Type": "application/json", Authorization: `Bearer ${token}` }, 17e4, abortSignal);
103010
+ const r2 = await _httpsPost(`${client_1.API_BASE}/api/code/assets/generate`, JSON.stringify({ prompt: prompt2, aspect_ratio: aspectRatio, teamId: (0, auth_1.getBillingTeamId)() }), { "Content-Type": "application/json", Authorization: `Bearer ${token}` }, 17e4, abortSignal);
102951
103011
  let json = {};
102952
103012
  try {
102953
103013
  json = JSON.parse(r2.body);
@@ -103052,7 +103112,7 @@ ${lines.join("\n")}` };
103052
103112
  if (!token)
103053
103113
  return { error: "Not authenticated. Run `nexrall-code login` first." };
103054
103114
  try {
103055
- const r2 = await _httpsPost(`${client_1.API_BASE}/api/code/tools/web_search`, JSON.stringify({ query }), { "Content-Type": "application/json", Authorization: `Bearer ${token}` }, 3e4, abortSignal);
103115
+ const r2 = await _httpsPost(`${client_1.API_BASE}/api/code/tools/web_search`, JSON.stringify({ query, teamId: (0, auth_1.getBillingTeamId)() }), { "Content-Type": "application/json", Authorization: `Bearer ${token}` }, 3e4, abortSignal);
103056
103116
  let json = {};
103057
103117
  try {
103058
103118
  json = JSON.parse(r2.body);
@@ -104831,6 +104891,7 @@ var require_loop = __commonJS({
104831
104891
  exports2.trimToResumableBoundary = trimToResumableBoundary;
104832
104892
  var types_1 = require_types();
104833
104893
  var client_1 = require_client();
104894
+ var index_1 = require_auth();
104834
104895
  var executor_1 = require_executor();
104835
104896
  var agentTypes_1 = require_agentTypes();
104836
104897
  var skills_1 = require_skills();
@@ -104843,7 +104904,7 @@ var require_loop = __commonJS({
104843
104904
  var worktreeEnforcement_1 = require_worktreeEnforcement();
104844
104905
  var agentRegistry_1 = require_agentRegistry();
104845
104906
  var sandbox_1 = require_sandbox();
104846
- var index_1 = require_plugins();
104907
+ var index_2 = require_plugins();
104847
104908
  var testIntegrity_1 = require_testIntegrity();
104848
104909
  var flaky_1 = require_flaky();
104849
104910
  var claimEvidence_1 = require_claimEvidence();
@@ -104863,7 +104924,7 @@ var require_loop = __commonJS({
104863
104924
  fromSettings = JSON.parse(fs9.readFileSync(p, "utf-8")).hooks ?? {};
104864
104925
  } catch {
104865
104926
  }
104866
- const fromPlugins = (0, index_1.pluginHooks)(workDir);
104927
+ const fromPlugins = (0, index_2.pluginHooks)(workDir);
104867
104928
  const merged = { ...fromSettings };
104868
104929
  for (const phase of Object.keys(fromPlugins)) {
104869
104930
  const extra = fromPlugins[phase];
@@ -105015,6 +105076,14 @@ var require_loop = __commonJS({
105015
105076
  case "no-balance":
105016
105077
  return `
105017
105078
  \u{1F4B3} Stopped: your balance is empty, so the request was rejected before it started. Top up and send "continue" \u2014 no tokens were used for this turn.
105079
+ `;
105080
+ case "no-team-budget":
105081
+ return `
105082
+ \u{1F4B3} Stopped: the team budget you are billing to is empty, so the request was rejected before it started. Ask your team admin to add funds \u2014 or switch "Bill to" back to Personal \u2014 and send "continue". No tokens were used for this turn.
105083
+ `;
105084
+ case "team-unavailable":
105085
+ return `
105086
+ \u26A0\uFE0F Stopped: that team is no longer available for billing (you may have been removed, or the team was suspended). "Bill to" has been reset to your Personal account, so sending "continue" will run on your own wallet.
105018
105087
  `;
105019
105088
  case "stalled":
105020
105089
  return `
@@ -105037,6 +105106,13 @@ ${ctx.repeatError}
105037
105106
  `;
105038
105107
  }
105039
105108
  }
105109
+ var TEAM_SCOPE_ERROR_CODES = /* @__PURE__ */ new Set([
105110
+ "team_unavailable",
105111
+ "team_not_found",
105112
+ "not_a_member",
105113
+ "member_suspended",
105114
+ "team_suspended"
105115
+ ]);
105040
105116
  function resolveMaxIterations(optionValue, settingsRaw) {
105041
105117
  const fromEnv = Number(process.env.NEXRALL_MAX_ITERATIONS);
105042
105118
  const fromSettings = Number(settingsRaw.maxIterations);
@@ -106356,12 +106432,25 @@ Continue the work from here.` }] });
106356
106432
  break;
106357
106433
  }
106358
106434
  const status = err.status;
106435
+ const code = err.code;
106436
+ if (status === 402 && code === "team_budget_exhausted") {
106437
+ const remaining = err.remaining ?? 0;
106438
+ const teamId = err.teamId;
106439
+ options.onTeamBudgetStatus?.({ teamId, remaining });
106440
+ stopReason = options.onTeamBudgetStatus ? "reported-elsewhere" : "no-team-budget";
106441
+ break;
106442
+ }
106359
106443
  if (status === 402) {
106360
106444
  const balance = err.balance ?? 0;
106361
106445
  options.onBalanceStatus?.(balance, true);
106362
106446
  stopReason = options.onBalanceStatus ? "reported-elsewhere" : "no-balance";
106363
106447
  break;
106364
106448
  }
106449
+ if (status === 403 && typeof code === "string" && TEAM_SCOPE_ERROR_CODES.has(code)) {
106450
+ (0, index_1.setBillingTeamId)(null);
106451
+ stopReason = "team-unavailable";
106452
+ break;
106453
+ }
106365
106454
  runSimpleHooks(hooks.OnError, options.workDir);
106366
106455
  throw new types_1.AgentTurnError(`Stream failed: ${err.message}`, trimToResumableBoundary(messages), completedRounds, err);
106367
106456
  }
@@ -154737,8 +154826,8 @@ var CLI_VERSION = (() => {
154737
154826
  })();
154738
154827
  var MODEL_LABELS = {
154739
154828
  "claude-sonnet-5": "Claude Sonnet 5",
154740
- "claude-opus-5-5": "Claude Opus 5",
154741
- "claude-fable-5-1": "Claude Fable 5",
154829
+ "claude-opus-5-5": "Claude Opus 5.5",
154830
+ "claude-fable-5-1": "Claude Fable 5.1",
154742
154831
  "claude-haiku-4-5-20251001": "Claude Haiku 4.5",
154743
154832
  "gpt-5.6-sol": "GPT-5.6 Sol",
154744
154833
  "gpt-5.6-terra": "GPT-5.6 Terra",
@@ -154769,7 +154858,13 @@ var SELECTABLE_MODELS = [
154769
154858
  "gpt-5.4-mini",
154770
154859
  "gpt-4.1",
154771
154860
  "deepseek-flash",
154772
- "qwen3.7-max",
154861
+ // qwen3.7-max deliberately NOT offered as of 2026-09-27 — it is
154862
+ // `selectable: false` in the backend's services/codeModels.js (every
154863
+ // request 400s with Alibaba's `Arrearage` error), so liveSelectableModelIds()
154864
+ // never returns it. Dropped from this static fallback too so an offline /
154865
+ // older-backend session offers the same list instead of a row that cannot
154866
+ // run. MODEL_LABELS keeps its entry so a session already saved on it still
154867
+ // shows a real name rather than the raw id. Re-add both together.
154773
154868
  "glm-5.3"
154774
154869
  ];
154775
154870
  var MODEL_COST_MULTIPLIER = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nexrall-code",
3
- "version": "0.5.109",
3
+ "version": "0.5.110",
4
4
  "description": "Nexrall Code — AI coding assistant for your terminal (headless agent for scripts, CI and automation)",
5
5
  "keywords": [
6
6
  "ai",