nothumanallowed 16.0.59 → 16.0.60

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nothumanallowed",
3
- "version": "16.0.59",
3
+ "version": "16.0.60",
4
4
  "description": "Local AI assistant: 80 tools (Gmail, Calendar, Drive, GitHub, Slack, browser, code, files), 38 agents, visual workflows (Studio, AWF, WebCraft). Install with `npm i -g nothumanallowed`, run with `nha ui`. Free tier built-in (Liara), no API key required. Your data stays on your PC — OAuth tokens local, no cloud. Open-source MIT.",
5
5
  "type": "module",
6
6
  "bin": {
package/src/constants.mjs CHANGED
@@ -5,7 +5,7 @@ import { fileURLToPath } from 'url';
5
5
  const __filename = fileURLToPath(import.meta.url);
6
6
  const __dirname = path.dirname(__filename);
7
7
 
8
- export const VERSION = '16.0.59';
8
+ export const VERSION = '16.0.60';
9
9
  export const BASE_URL = 'https://nothumanallowed.com/cli';
10
10
  export const API_BASE = 'https://nothumanallowed.com/api/v1';
11
11
 
@@ -605,7 +605,11 @@ export function _autoPrefixOpenRouterModel(model) {
605
605
  * Model names: "anthropic/claude-sonnet-4.5", "openai/gpt-4o", "google/gemini-2.5-pro"...
606
606
  */
607
607
  export async function callOpenRouter(apiKey, model, systemPrompt, userMessage, stream = false, opts = {}) {
608
+ const originalModel = model;
608
609
  model = _autoPrefixOpenRouterModel(model);
610
+ if (process.env.NHA_LOG_OPENROUTER === '1') {
611
+ console.error(`[openrouter] model resolved: "${originalModel}" → "${model}"`);
612
+ }
609
613
  const body = {
610
614
  model: model || 'anthropic/claude-sonnet-4.5',
611
615
  max_tokens: opts.max_tokens || 8192,
@@ -630,16 +634,34 @@ export async function callOpenRouter(apiKey, model, systemPrompt, userMessage, s
630
634
  });
631
635
  if (!res.ok) {
632
636
  const err = await res.text();
637
+ // Auto-recovery: if OpenRouter routed to OpenAI (model is openai/*) and
638
+ // returned 401 mentioning platform.openai.com, the OpenRouter account has
639
+ // BYOK enabled with an invalid/missing OpenAI key. Retry once with safe
640
+ // default model anthropic/claude-sonnet-4.5 (works with OpenRouter credit).
641
+ const isBYOKFailure = res.status === 401 &&
642
+ /platform\.openai\.com|Incorrect API key/i.test(err) &&
643
+ model.startsWith('openai/') &&
644
+ !opts._retriedFromByokFailure;
645
+ if (isBYOKFailure) {
646
+ if (process.env.NHA_LOG_OPENROUTER === '1') {
647
+ console.error(`[openrouter] BYOK failure on ${model}, retrying with anthropic/claude-sonnet-4.5`);
648
+ }
649
+ return callOpenRouter(apiKey, 'anthropic/claude-sonnet-4.5', systemPrompt, userMessage, stream, { ...opts, _retriedFromByokFailure: true });
650
+ }
633
651
  // OpenRouter often passes through misleading "platform.openai.com" key errors
634
652
  // when the real cause is (a) invalid OpenRouter key, (b) model name without
635
653
  // vendor prefix, (c) model requires BYOK. Provide an actionable hint.
636
654
  let hint = '';
637
655
  if (res.status === 401) {
638
- hint = '\n → 401 from OpenRouter usually means: (1) your sk-or-v1-... key is invalid/expired (regenerate at openrouter.ai/keys), or (2) the model name "' + model + '" needs a vendor prefix like "anthropic/' + model.replace(/^[a-z-]+\//, '') + '". This call auto-prefixed to "' + model + '".';
656
+ if (/platform\.openai\.com/i.test(err)) {
657
+ hint = `\n → 401 with "platform.openai.com" hint: model "${model}" routes via OpenAI but your OpenRouter account either lacks credit or has BYOK enabled with an invalid OpenAI key.\n Fix: (1) use an Anthropic/Gemini model instead (e.g. "anthropic/claude-sonnet-4.5"), OR (2) add credit at openrouter.ai/credits, OR (3) disable BYOK in OpenRouter Settings → Integrations.`;
658
+ } else {
659
+ hint = '\n → 401 from OpenRouter: your sk-or-v1-... key is invalid/expired. Regenerate at openrouter.ai/keys.';
660
+ }
639
661
  } else if (res.status === 404) {
640
- hint = '\n → 404 from OpenRouter usually means the model "' + model + '" does not exist. See openrouter.ai/models for valid IDs.';
662
+ hint = '\n → 404 from OpenRouter: model "' + model + '" does not exist. See openrouter.ai/models for valid IDs.';
641
663
  } else if (res.status === 402) {
642
- hint = '\n → 402 from OpenRouter means insufficient credits. Top up at openrouter.ai/credits.';
664
+ hint = '\n → 402 from OpenRouter: insufficient credits. Top up at openrouter.ai/credits.';
643
665
  }
644
666
  throw new Error(`OpenRouter ${res.status}: ${err}${hint}`);
645
667
  }