scalemax 0.1.21 → 0.1.22

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.
@@ -3,6 +3,7 @@ import { loadApiKey } from '../utils/keyStore.js';
3
3
  import { validateApiKey } from '../utils/api.js';
4
4
  export declare const SCALEMAX_HELPER_REFUSAL: string;
5
5
  export declare const SCALEMAX_HELPER_SYSTEM_PROMPT: string;
6
+ export declare const SCALEMAX_HELPER_INVALID_PROVIDER_RESPONSE: string;
6
7
  export interface HelperBotArgs {
7
8
  question?: string;
8
9
  model?: string;
@@ -11,6 +12,10 @@ export interface HelperConnection {
11
12
  apiKey: string;
12
13
  baseUrl: string;
13
14
  model: string;
15
+ platform?: NodeJS.Platform;
16
+ providerScope?: 'openai' | 'anthropic' | 'both';
17
+ keyMode?: string;
18
+ availableModels?: string[];
14
19
  }
15
20
  export type HelperCompletion = (options: ChatCompletionOptions) => Promise<ChatCompletionResult>;
16
21
  export interface HelperBotDependencies {
@@ -22,6 +27,15 @@ export interface HelperBotDependencies {
22
27
  export declare function parseHelperBotArgs(args: string[]): HelperBotArgs;
23
28
  export declare function redactHelperSecrets(value: unknown, activeKey?: string): string;
24
29
  export declare function isScaleMaxHelperQuestion(input: string, hasHistory?: boolean): boolean;
30
+ export declare function isProviderIdentityOverride(value: string): boolean;
31
+ export declare function helperRuntimeContext(connection: HelperConnection): string;
32
+ export declare function helperConnectionSummary(connection: HelperConnection): string;
33
+ /**
34
+ * Answer stable configuration questions locally. Besides being faster and free,
35
+ * this keeps provider-owned personas out of the installation path where exact
36
+ * field values matter more than generated prose.
37
+ */
38
+ export declare function localHelperReply(question: string, connection: HelperConnection): string | undefined;
25
39
  export declare function selectHelperModel(models: ModelInfo[], requested?: string): string;
26
40
  export declare function requestHelperReply(question: string, history: ChatMessage[], connection: HelperConnection, complete?: HelperCompletion): Promise<string>;
27
41
  export declare function helperBotCommand(args?: string[], home?: string, dependencies?: Partial<HelperBotDependencies>): Promise<void>;
@@ -534,6 +534,26 @@ const CLEARLY_UNRELATED_PATTERN = /\b(?:homework|poem|story|roleplay|translate|t
534
534
  const SCALEMAX_TOPIC_PATTERN = /\b(?:scalemax|api\s*key|base\s*url|endpoint|provider|model|token|credit|billing|pricing|authentication|configuration|configure|install|update|repair|verify|health\s*check|environment\s*variable|vscode|visual\s+studio\s+code|cursor|codex(?:\s+cli)?|claude(?:\s+code)?|opencode|openclaw|roo\s+code|continue|windsurf|hermes|cherry\s+studio|api\s+code)\b/i;
535
535
  const TROUBLESHOOTING_PATTERN = /\b(?:error|failed?|failure|broken|not\s+working|doesn['’]?t\s+work|cannot|can['’]?t|timeout|invalid|unauthorized|forbidden|help)\b/i;
536
536
  const GREETING_PATTERN = /^(?:hi|hello|hey|help|what can you do)[!.?\s]*$/i;
537
+ const CLAUDE_DESKTOP_PATTERN = /\bclaude\s+desktop\b/i;
538
+ const CLAUDE_DESKTOP_CONFIGURATION_PATTERN = /\b(?:configur(?:e|ation)|setup|set\s*up|gateway|base\s*url|api\s*key|connect(?:ion)?|third[- ]party|inference|field|install|verify)\b/i;
539
+ const CODEX_CLI_PATTERN = /\bcodex(?:\s+cli)?\b/i;
540
+ const CLIENT_CONFIGURATION_PATTERN = /\b(?:configur(?:e|ation)|setup|set\s*up|base\s*url|api\s*key|connect(?:ion)?|provider|install|repair|verify|update)\b/i;
541
+ const KNOWN_CONTEXT_REASK_PATTERN = /(?:\b(?:need|send|provide|share|tell)\b.{0,100}\b(?:api\s*(?:key|endpoint)|base\s*url|operating\s+system|\bos\b|model\s+(?:name|catalog))\b|YOUR-SCALEMAX-ENDPOINT|your-key)/is;
542
+ const PROVIDER_IDENTITY_OVERRIDE_PATTERNS = [
543
+ /\bkiro\b/i,
544
+ /\b(?:i\s+am|i['’]m)\s+(?:an?\s+)?(?:claude|chatgpt|codex)\b/i,
545
+ /\bas\s+(?:claude|chatgpt|codex)(?:\s+(?:ai|assistant|model))?\s*[,—-]/i,
546
+ /\b(?:i\s+am|i['’]m)\s+(?:an?\s+)?(?:ai[- ]powered\s+)?(?:development\s+environment|ai\s+assistant|assistant|language\s+model)\s+(?:made|built|created|developed)\s+by\s+(?:anthropic|openai)\b/i,
547
+ /\bi\s+(?:was|am)\s+(?:made|built|created|developed)\s+by\s+(?:anthropic|openai)\b/i,
548
+ /\b(?:i\s+am|i['’]m)\s+not\s+(?:the\s+)?(?:official\s+)?scalemax\s+helper\b/i,
549
+ /\bi\s+(?:cannot|can['’]?t|won['’]?t)\s+(?:become|act\s+as|identify\s+as)\s+(?:the\s+)?(?:official\s+)?scalemax\s+helper\b/i,
550
+ /\b(?:not|isn['’]?t)\s+(?:the\s+)?(?:official\s+)?scalemax\s+helper\b/i,
551
+ /\b(?:that|the|your|supplied)\s+(?:system\s+)?prompt\b.{0,120}\b(?:does\s+not|doesn['’]?t|cannot|can['’]?t|won['’]?t)\s+(?:override|change|replace)\b.{0,100}\b(?:identity|instructions?|role)\b/is,
552
+ /\bscalemax\b.{0,100}\b(?:does\s+not|doesn['’]?t)\s+(?:appear\s+to\s+)?(?:exist|be\s+real)\b/is
553
+ ];
554
+ export const SCALEMAX_HELPER_INVALID_PROVIDER_RESPONSE = `ScaleMax Helper blocked an invalid provider response because it conflicted with the Helper's required identity.
555
+
556
+ The requested ScaleMax task was not completed or verified. Run \`scalemax models\`, then retry with another available model using \`scalemax helper-bot --model <id> "your ScaleMax question"\`.`;
537
557
  const defaultDependencies = {
538
558
  loadKey: loadApiKey,
539
559
  validateKey: validateApiKey,
@@ -573,6 +593,91 @@ export function isScaleMaxHelperQuestion(input, hasHistory = false) {
573
593
  return false;
574
594
  return hasHistory || GREETING_PATTERN.test(value) || SCALEMAX_TOPIC_PATTERN.test(value) || TROUBLESHOOTING_PATTERN.test(value);
575
595
  }
596
+ export function isProviderIdentityOverride(value) {
597
+ return PROVIDER_IDENTITY_OVERRIDE_PATTERNS.some(pattern => pattern.test(value));
598
+ }
599
+ function claudeDesktopGatewayBaseUrl(baseUrl) {
600
+ return baseUrl.trim().replace(/\/+$/, '').replace(/\/v1$/i, '');
601
+ }
602
+ function helperBillingMode(connection) {
603
+ return /\/token(?:\/|$)/i.test(connection.baseUrl) || /token/i.test(connection.keyMode || '') ? 'API token' : 'API credit';
604
+ }
605
+ function helperPlatformName(platform = process.platform) {
606
+ if (platform === 'win32')
607
+ return 'Windows';
608
+ if (platform === 'darwin')
609
+ return 'macOS';
610
+ if (platform === 'linux')
611
+ return 'Linux';
612
+ return platform;
613
+ }
614
+ export function helperRuntimeContext(connection) {
615
+ const models = (connection.availableModels || []).filter(Boolean);
616
+ return `# AUTHENTICATED SCALEMAX RUNTIME CONTEXT
617
+
618
+ The ScaleMax CLI already verified these authoritative facts before starting this conversation:
619
+
620
+ - Stored customer API key: present and authenticated. Never ask the user to paste, provide, repeat, or reveal it.
621
+ - Billing mode: ${helperBillingMode(connection)}
622
+ - OpenAI-compatible base URL: ${connection.baseUrl}
623
+ - Claude Desktop gateway base URL: ${claudeDesktopGatewayBaseUrl(connection.baseUrl)}
624
+ - Operating system: ${helperPlatformName(connection.platform)}
625
+ - Provider scope: ${connection.providerScope || 'both'}
626
+ - Active Helper model: ${connection.model}
627
+ - Authenticated model catalog: ${models.length ? models.join(', ') : 'already validated by the CLI'}
628
+
629
+ Mandatory behavior:
630
+
631
+ 1. Treat the stored key, base URLs, operating system, provider scope, and model catalog as already known. Do not request them again.
632
+ 2. Never output placeholders such as YOUR-SCALEMAX-ENDPOINT or your-key when an exact known value can be used.
633
+ 3. Prefer the automatic CLI workflow. For an installed supported client, tell the user to exit the Helper with /quit, run \`scalemax update\`, then run \`scalemax doctor\` to verify it.
634
+ 4. The Helper request itself uses the stored customer key and therefore consumes the small number of tokens used by the selected Helper model. The key is never included in the prompt text.
635
+ 5. Do not claim you executed terminal commands. The Helper has no shell tools; give the shortest exact ScaleMax command sequence when user action is required.`;
636
+ }
637
+ export function helperConnectionSummary(connection) {
638
+ return `Authenticated ${helperBillingMode(connection)} key · ${helperPlatformName(connection.platform)} · ${connection.providerScope || 'both'} scope · ${connection.baseUrl} · helper model ${connection.model}`;
639
+ }
640
+ /**
641
+ * Answer stable configuration questions locally. Besides being faster and free,
642
+ * this keeps provider-owned personas out of the installation path where exact
643
+ * field values matter more than generated prose.
644
+ */
645
+ export function localHelperReply(question, connection) {
646
+ if (CLAUDE_DESKTOP_PATTERN.test(question) && CLAUDE_DESKTOP_CONFIGURATION_PATTERN.test(question)) {
647
+ const gatewayBaseUrl = claudeDesktopGatewayBaseUrl(connection.baseUrl);
648
+ return `ScaleMax Helper detected and authenticated the stored ${helperBillingMode(connection)} key.
649
+
650
+ Configure Claude Desktop → Settings → Developer → Edit Config → Configure third-party inference with:
651
+
652
+ - Connection: Gateway
653
+ - Gateway base URL: ${gatewayBaseUrl}
654
+ - Gateway API key: use the stored ScaleMax key (do not paste it into chat)
655
+ - Authentication scheme: Bearer
656
+ - Credential kind: Static API key
657
+ - Model discovery: On
658
+
659
+ Select Save Changes, then Apply Changes. Start a new Claude Desktop chat and send a short test message with one discovered model. The key and endpoint are verified; the Claude Desktop GUI is only verified after that test succeeds.`;
660
+ }
661
+ if (CODEX_CLI_PATTERN.test(question) && CLIENT_CONFIGURATION_PATTERN.test(question)) {
662
+ return `ScaleMax Helper already authenticated the stored ${helperBillingMode(connection)} key for ${helperPlatformName(connection.platform)}.
663
+
664
+ You do not need to provide the API key or base URL again. This key uses:
665
+
666
+ - Base URL: ${connection.baseUrl}
667
+ - Provider scope: ${connection.providerScope || 'both'}
668
+
669
+ Exit the Helper with \`/quit\`, then run:
670
+
671
+ \`\`\`powershell
672
+ scalemax update
673
+ scalemax doctor
674
+ codex
675
+ \`\`\`
676
+
677
+ \`scalemax update\` configures detected Codex CLI from the stored key, and \`scalemax doctor\` verifies the resulting ScaleMax connection before Codex starts.`;
678
+ }
679
+ return undefined;
680
+ }
576
681
  export function selectHelperModel(models, requested) {
577
682
  if (requested) {
578
683
  const exact = models.find(model => model.id.toLowerCase() === requested.toLowerCase());
@@ -580,7 +685,7 @@ export function selectHelperModel(models, requested) {
580
685
  throw new Error(`Model "${requested}" is not available to this API key. Run \`scalemax models\` to see available models.`);
581
686
  return exact.id;
582
687
  }
583
- const preferred = ['gpt-5.6-luna', 'claude-haiku-4-5', 'fable-5', 'gpt-5.6-terra', 'gpt-5.6-sol'];
688
+ const preferred = ['gpt-5.6-luna', 'claude-haiku-4-5-20251001', 'fable-5', 'gpt-5.6-terra', 'gpt-5.6-sol'];
584
689
  for (const id of preferred) {
585
690
  const match = models.find(model => model.id.toLowerCase() === id);
586
691
  if (match)
@@ -596,20 +701,38 @@ export async function requestHelperReply(question, history, connection, complete
596
701
  throw new Error(`Please keep each helper request under ${MAX_USER_CHARS} characters.`);
597
702
  if (!isScaleMaxHelperQuestion(cleanQuestion, history.length > 0))
598
703
  return SCALEMAX_HELPER_REFUSAL;
704
+ const localReply = localHelperReply(cleanQuestion, connection);
705
+ if (localReply)
706
+ return localReply;
599
707
  const recentHistory = history.slice(-MAX_HISTORY_MESSAGES).map(message => ({ ...message, content: redactHelperSecrets(message.content, connection.apiKey) }));
600
708
  const result = await complete({
601
709
  apiKey: connection.apiKey,
602
710
  baseUrl: connection.baseUrl,
603
711
  model: connection.model,
604
712
  maxTokens: DEFAULT_MAX_TOKENS,
605
- messages: [{ role: 'system', content: SCALEMAX_HELPER_SYSTEM_PROMPT }, ...recentHistory, { role: 'user', content: cleanQuestion }]
713
+ messages: [
714
+ { role: 'system', content: SCALEMAX_HELPER_SYSTEM_PROMPT },
715
+ { role: 'system', content: helperRuntimeContext(connection) },
716
+ ...recentHistory,
717
+ { role: 'user', content: cleanQuestion }
718
+ ]
606
719
  });
607
720
  if (!result.ok || !result.content)
608
721
  throw new Error(redactHelperSecrets(result.error || 'ScaleMax Helper did not return a response.', connection.apiKey));
609
- return redactHelperSecrets(result.content, connection.apiKey);
722
+ const reply = redactHelperSecrets(result.content, connection.apiKey);
723
+ if (isProviderIdentityOverride(reply))
724
+ return SCALEMAX_HELPER_INVALID_PROVIDER_RESPONSE;
725
+ if (KNOWN_CONTEXT_REASK_PATTERN.test(reply)) {
726
+ return `ScaleMax Helper already authenticated the stored ${helperBillingMode(connection)} key. You do not need to provide the key, endpoint, operating system, or model catalog again.
727
+
728
+ Verified base URL: ${connection.baseUrl}
729
+
730
+ For automatic configuration, exit with \`/quit\`, run \`scalemax update\`, then run \`scalemax doctor\`.`;
731
+ }
732
+ return reply;
610
733
  }
611
734
  function printSessionHelp(write) {
612
- write('Commands: /help shows this help; /quit exits. Ask only ScaleMax installation, configuration, authentication, or troubleshooting questions.');
735
+ write('Commands: /help shows this help; /status shows the authenticated non-secret connection details; /quit exits. Ask only ScaleMax installation, configuration, authentication, or troubleshooting questions.');
613
736
  }
614
737
  export async function helperBotCommand(args = [], home = homeDir(), dependencies = {}) {
615
738
  const deps = { ...defaultDependencies, ...dependencies };
@@ -621,7 +744,15 @@ export async function helperBotCommand(args = [], home = homeDir(), dependencies
621
744
  if (!validation.ok || !validation.baseUrl)
622
745
  throw new Error(redactHelperSecrets(validation.error || 'Unable to validate the ScaleMax API key.', key));
623
746
  const model = selectHelperModel(validation.models, options.model);
624
- const connection = { apiKey: key, baseUrl: validation.baseUrl, model };
747
+ const connection = {
748
+ apiKey: key,
749
+ baseUrl: validation.baseUrl,
750
+ model,
751
+ platform: process.platform,
752
+ providerScope: validation.key?.provider_scope || 'both',
753
+ keyMode: validation.key?.mode,
754
+ availableModels: validation.models.map(item => item.id)
755
+ };
625
756
  if (options.question) {
626
757
  deps.write(await requestHelperReply(options.question, [], connection, deps.complete));
627
758
  return;
@@ -629,7 +760,8 @@ export async function helperBotCommand(args = [], home = homeDir(), dependencies
629
760
  if (!process.stdin.isTTY)
630
761
  throw new Error('Interactive helper-bot requires a terminal. Pass a quoted question for non-interactive use.');
631
762
  deps.write(chalk.cyan('ScaleMax Helper'));
632
- deps.write(chalk.dim(`Authenticated and ready with ${model}. Type /help or /quit.`));
763
+ deps.write(chalk.dim(helperConnectionSummary(connection)));
764
+ deps.write(chalk.dim('The stored customer key is reused for Helper requests; it will not be requested or displayed. Type /help or /quit.'));
633
765
  const reader = createInterface({ input: process.stdin, output: process.stdout });
634
766
  const history = [];
635
767
  try {
@@ -650,6 +782,10 @@ export async function helperBotCommand(args = [], home = homeDir(), dependencies
650
782
  printSessionHelp(deps.write);
651
783
  continue;
652
784
  }
785
+ if (/^\/?status$/i.test(input)) {
786
+ deps.write(helperConnectionSummary(connection));
787
+ continue;
788
+ }
653
789
  try {
654
790
  const reply = await requestHelperReply(input, history, connection, deps.complete);
655
791
  deps.write(reply);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "scalemax",
3
- "version": "0.1.21",
3
+ "version": "0.1.22",
4
4
  "description": "Universal ScaleMax installer for AI coding clients",
5
5
  "type": "module",
6
6
  "bin": {