@plexor-dev/claude-code-plugin-staging 0.1.0-beta.16 → 0.1.0-beta.17

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.
@@ -74,9 +74,16 @@ function main() {
74
74
 
75
75
  config.settings = config.settings || {};
76
76
  config.settings.preferred_provider = requested;
77
+ if (requested !== 'auto') {
78
+ delete config.settings.preferred_model;
79
+ delete config.settings.preferredModel;
80
+ }
77
81
  if (!saveConfig(config)) { process.exit(1); }
78
82
 
79
83
  console.log(`Provider: ${current.value} → ${requested}`);
84
+ if (requested !== 'auto') {
85
+ console.log(' Cleared preferred_model to keep provider/model force hints mutually exclusive.');
86
+ }
80
87
  console.log(` ${DESC[requested]}`);
81
88
  console.log(' Takes effect on next request.');
82
89
  }
@@ -127,6 +127,10 @@ function setDimension(dimension, value, config) {
127
127
 
128
128
  config.settings = config.settings || {};
129
129
  config.settings[configKey] = resolved;
130
+ if (configKey === 'preferred_provider' && resolved !== 'auto') {
131
+ delete config.settings.preferred_model;
132
+ delete config.settings.preferredModel;
133
+ }
130
134
  if (configKeyAlt && config.settings[configKeyAlt]) {
131
135
  delete config.settings[configKeyAlt];
132
136
  }
@@ -134,6 +138,9 @@ function setDimension(dimension, value, config) {
134
138
  if (!saveConfig(config)) { process.exit(1); }
135
139
 
136
140
  console.log(`${label} updated: ${current.value} → ${resolved}`);
141
+ if (configKey === 'preferred_provider' && resolved !== 'auto') {
142
+ console.log(' Cleared preferred_model to keep provider/model force hints mutually exclusive.');
143
+ }
137
144
  console.log(` ${descMap[resolved]}`);
138
145
  console.log(` Takes effect on next request.`);
139
146
  }
@@ -54,6 +54,8 @@ const VALID_PROVIDER_HINTS = new Set([
54
54
  'cohere'
55
55
  ]);
56
56
 
57
+ const DISABLED_MODEL_HINTS = new Set(['auto', 'none', 'off']);
58
+
57
59
  function normalizeOrchestrationMode(mode) {
58
60
  if (typeof mode !== 'string') {
59
61
  return null;
@@ -120,6 +122,42 @@ function resolvePreferredProvider(settings) {
120
122
  return cfgProvider || 'auto';
121
123
  }
122
124
 
125
+ function normalizePreferredModel(model) {
126
+ if (typeof model !== 'string') {
127
+ return null;
128
+ }
129
+ const trimmed = model.trim();
130
+ if (!trimmed) {
131
+ return null;
132
+ }
133
+ if (DISABLED_MODEL_HINTS.has(trimmed.toLowerCase())) {
134
+ return null;
135
+ }
136
+ return trimmed;
137
+ }
138
+
139
+ function resolvePreferredModel(settings) {
140
+ const envModel = normalizePreferredModel(
141
+ process.env.PLEXOR_MODEL || process.env.PLEXOR_PREFERRED_MODEL
142
+ );
143
+ if (envModel) {
144
+ return envModel;
145
+ }
146
+ const cfgModel = normalizePreferredModel(settings?.preferredModel || settings?.preferred_model);
147
+ return cfgModel || null;
148
+ }
149
+
150
+ function validateForceHintSelection(preferredProvider, preferredModel) {
151
+ if (!preferredModel || preferredProvider === 'auto') {
152
+ return;
153
+ }
154
+ const error = new Error(
155
+ 'Invalid Plexor config: force only one hint. Set preferred_provider OR preferred_model, not both.'
156
+ );
157
+ error.code = 'PLEXOR_CONFIG_CONFLICT';
158
+ throw error;
159
+ }
160
+
123
161
  // Try to load lib modules, fall back to inline implementations
124
162
  let ConfigManager, SessionManager, LocalCache, Logger, PlexorClient;
125
163
  let config, session, cache, logger;
@@ -175,6 +213,7 @@ try {
175
213
  localCacheEnabled: cfg.settings?.localCacheEnabled ?? false,
176
214
  mode: cfg.settings?.mode || 'balanced',
177
215
  preferredProvider: cfg.settings?.preferred_provider || 'auto',
216
+ preferredModel: cfg.settings?.preferredModel || cfg.settings?.preferred_model || null,
178
217
  orchestrationMode:
179
218
  cfg.settings?.orchestrationMode || cfg.settings?.orchestration_mode || 'autonomous'
180
219
  };
@@ -298,6 +337,8 @@ async function main() {
298
337
  const orchestrationMode = resolveOrchestrationMode(settings);
299
338
  const gatewayMode = resolveGatewayMode(settings);
300
339
  const preferredProvider = resolvePreferredProvider(settings);
340
+ const preferredModel = resolvePreferredModel(settings);
341
+ validateForceHintSelection(preferredProvider, preferredModel);
301
342
 
302
343
  // Phase 3 Hypervisor Mode Detection
303
344
  // When ANTHROPIC_BASE_URL points to Plexor, all intelligence is server-side
@@ -316,6 +357,7 @@ async function main() {
316
357
  // Add session tracking metadata (server will use this for analytics)
317
358
  return output({
318
359
  ...request,
360
+ ...(preferredModel ? { model: preferredModel } : {}),
319
361
  plexor_mode: gatewayMode,
320
362
  ...(preferredProvider !== 'auto' ? { plexor_provider: preferredProvider } : {}),
321
363
  plexor_orchestration_mode: orchestrationMode,
@@ -326,6 +368,7 @@ async function main() {
326
368
  orchestration_mode: orchestrationMode,
327
369
  plexor_mode: gatewayMode,
328
370
  preferred_provider: preferredProvider,
371
+ preferred_model: preferredModel,
329
372
  cwd: process.cwd(),
330
373
  timestamp: Date.now(),
331
374
  }
@@ -488,6 +531,13 @@ async function main() {
488
531
  return output(optimizedRequest);
489
532
 
490
533
  } catch (error) {
534
+ if (error?.code === 'PLEXOR_CONFIG_CONFLICT') {
535
+ logger.error(`Configuration error: ${error.message}`);
536
+ logger.ux(error.message);
537
+ process.stderr.write(`\n[Plexor] ${error.message}\n`);
538
+ process.exit(1);
539
+ }
540
+
491
541
  logger.error(`Error: ${error.message}`);
492
542
  logger.ux(`Optimization hook error: ${error.message}`);
493
543
  logger.debug(error.stack);
@@ -7,6 +7,50 @@ const path = require('path');
7
7
  const crypto = require('crypto');
8
8
  const { PLEXOR_DIR, CONFIG_PATH } = require('./constants');
9
9
 
10
+ const DISABLED_HINT_VALUES = new Set(['', 'auto', 'none', 'off']);
11
+
12
+ function normalizeForcedProvider(value) {
13
+ if (typeof value !== 'string') {
14
+ return null;
15
+ }
16
+ const normalized = value.trim().toLowerCase();
17
+ if (!normalized || normalized === 'auto') {
18
+ return 'auto';
19
+ }
20
+ return normalized;
21
+ }
22
+
23
+ function normalizeForcedModel(value) {
24
+ if (typeof value !== 'string') {
25
+ return null;
26
+ }
27
+ const normalized = value.trim();
28
+ if (!normalized || DISABLED_HINT_VALUES.has(normalized.toLowerCase())) {
29
+ return null;
30
+ }
31
+ return normalized;
32
+ }
33
+
34
+ function hasForcedHintConflict(config) {
35
+ const settings = config?.settings || {};
36
+ const provider = normalizeForcedProvider(
37
+ settings.preferred_provider ?? settings.preferredProvider ?? 'auto'
38
+ );
39
+ const model = normalizeForcedModel(settings.preferred_model ?? settings.preferredModel);
40
+ return Boolean(model) && provider !== 'auto';
41
+ }
42
+
43
+ function validateForcedHintConfig(config) {
44
+ if (!hasForcedHintConflict(config)) {
45
+ return { ok: true };
46
+ }
47
+ return {
48
+ ok: false,
49
+ message:
50
+ 'Invalid Plexor config: set only one force hint. Use preferred_provider OR preferred_model, not both.'
51
+ };
52
+ }
53
+
10
54
  function loadConfig() {
11
55
  try {
12
56
  if (!fs.existsSync(CONFIG_PATH)) {
@@ -31,6 +75,12 @@ function loadConfig() {
31
75
 
32
76
  function saveConfig(config) {
33
77
  try {
78
+ const validation = validateForcedHintConfig(config);
79
+ if (!validation.ok) {
80
+ console.error(`Error: ${validation.message}`);
81
+ return false;
82
+ }
83
+
34
84
  if (!fs.existsSync(PLEXOR_DIR)) {
35
85
  fs.mkdirSync(PLEXOR_DIR, { recursive: true, mode: 0o700 });
36
86
  }
@@ -71,4 +121,4 @@ function readSetting(config, configKey, configKeyAlt, envVar, validValues, defau
71
121
  return { value: defaultValue, source: 'default' };
72
122
  }
73
123
 
74
- module.exports = { loadConfig, saveConfig, readSetting };
124
+ module.exports = { loadConfig, saveConfig, readSetting, hasForcedHintConflict, validateForcedHintConfig };
package/lib/config.js CHANGED
@@ -5,6 +5,7 @@
5
5
  const fs = require('fs');
6
6
  const path = require('path');
7
7
  const { CONFIG_PATH, PLEXOR_DIR, DEFAULT_API_URL, DEFAULT_TIMEOUT } = require('./constants');
8
+ const { validateForcedHintConfig } = require('./config-utils');
8
9
 
9
10
  class ConfigManager {
10
11
  constructor() {
@@ -23,6 +24,7 @@ class ConfigManager {
23
24
  localCacheEnabled: cfg.settings?.localCacheEnabled ?? false,
24
25
  mode: cfg.settings?.mode || 'balanced',
25
26
  preferredProvider: cfg.settings?.preferred_provider || 'auto',
27
+ preferredModel: cfg.settings?.preferredModel || cfg.settings?.preferred_model || null,
26
28
  orchestrationMode:
27
29
  cfg.settings?.orchestrationMode || cfg.settings?.orchestration_mode || 'autonomous'
28
30
  };
@@ -55,6 +57,11 @@ class ConfigManager {
55
57
  localCacheEnabled: config.localCacheEnabled ?? existing.settings?.localCacheEnabled,
56
58
  mode: config.mode ?? existing.settings?.mode,
57
59
  preferred_provider: config.preferredProvider ?? existing.settings?.preferred_provider,
60
+ preferred_model:
61
+ config.preferredModel ??
62
+ config.preferred_model ??
63
+ existing.settings?.preferredModel ??
64
+ existing.settings?.preferred_model,
58
65
  orchestrationMode:
59
66
  config.orchestrationMode ??
60
67
  config.orchestration_mode ??
@@ -63,6 +70,11 @@ class ConfigManager {
63
70
  }
64
71
  };
65
72
 
73
+ const validation = validateForcedHintConfig(updated);
74
+ if (!validation.ok) {
75
+ return false;
76
+ }
77
+
66
78
  fs.writeFileSync(this.configPath, JSON.stringify(updated, null, 2), { mode: 0o600 });
67
79
  return true;
68
80
  } catch {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plexor-dev/claude-code-plugin-staging",
3
- "version": "0.1.0-beta.16",
3
+ "version": "0.1.0-beta.17",
4
4
  "description": "STAGING - LLM cost optimization plugin for Claude Code (internal testing)",
5
5
  "main": "lib/constants.js",
6
6
  "bin": {