@plexor-dev/claude-code-plugin-localhost 0.1.0-beta.11 → 0.1.0-beta.13

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.
@@ -2,9 +2,7 @@
2
2
  description: Enable or disable Plexor proxy (routes all traffic through Plexor API) (user)
3
3
  ---
4
4
 
5
- # Plexor Enabled
6
-
7
- Toggle Plexor proxy on or off.
5
+ **RULE: Execute this workflow EXACTLY ONCE. After completing all steps, STOP. DO NOT restart the workflow. DO NOT re-execute any commands. DO NOT call any other tools.**
8
6
 
9
7
  ## If argument provided (on/off/true/false)
10
8
 
@@ -14,11 +12,11 @@ Run the command directly with the argument:
14
12
  node ~/.claude/plugins/plexor/commands/plexor-enabled.js <argument>
15
13
  ```
16
14
 
17
- Then display the output and STOP.
15
+ Then present the output to the user and STOP.
18
16
 
19
17
  ## If NO argument provided
20
18
 
21
- **Step 1**: First get current status by running:
19
+ **Step 1**: Get current status by running:
22
20
 
23
21
  ```bash
24
22
  node ~/.claude/plugins/plexor/commands/plexor-enabled.js
@@ -26,14 +24,14 @@ node ~/.claude/plugins/plexor/commands/plexor-enabled.js
26
24
 
27
25
  **Step 2**: Based on the output, use AskUserQuestion to present options:
28
26
 
29
- If currently ENABLED (shows "Enabled" or "Active"):
27
+ If currently ENABLED (shows "Enabled" or "Active"):
30
28
  - Question: "Plexor is currently ON. What would you like to do?"
31
29
  - Header: "Plexor"
32
30
  - Options:
33
31
  1. **Turn OFF** - Disable Plexor routing (connect directly to Anthropic)
34
32
  2. **Keep ON** - No change
35
33
 
36
- If currently DISABLED (shows "Disabled" or "Inactive"):
34
+ If currently DISABLED (shows "Disabled" or "Inactive"):
37
35
  - Question: "Plexor is currently OFF. What would you like to do?"
38
36
  - Header: "Plexor"
39
37
  - Options:
@@ -45,4 +43,4 @@ If currently DISABLED (shows "○ Disabled" or "○ Inactive"):
45
43
  - If "Turn ON" selected: Run `node ~/.claude/plugins/plexor/commands/plexor-enabled.js true`
46
44
  - If "Keep" selected: Do nothing, just confirm current state
47
45
 
48
- **IMPORTANT**: After completing, STOP. Do not read files, explore codebase, or run additional commands.
46
+ After completing all steps, STOP. DO NOT restart the workflow.
@@ -76,6 +76,18 @@ function saveConfig(config) {
76
76
  }
77
77
  }
78
78
 
79
+ function isPlexorApiKey(token) {
80
+ return typeof token === 'string' && token.startsWith('plx_') && token.length >= 20;
81
+ }
82
+
83
+ function isJwtToken(token) {
84
+ if (typeof token !== 'string' || !token.startsWith('eyJ')) {
85
+ return false;
86
+ }
87
+ const parts = token.split('.');
88
+ return parts.length === 3 && parts.every(part => part.length > 0);
89
+ }
90
+
79
91
  function validateApiKey(apiUrl, apiKey) {
80
92
  return new Promise((resolve, reject) => {
81
93
  const url = new URL(`${apiUrl}/v1/user`);
@@ -119,6 +131,56 @@ function validateApiKey(apiUrl, apiKey) {
119
131
  });
120
132
  }
121
133
 
134
+ function validateJwtToken(apiUrl, jwtToken) {
135
+ return new Promise((resolve, reject) => {
136
+ const url = new URL(`${apiUrl}/v1/auth/me`);
137
+ const isHttps = url.protocol === 'https:';
138
+ const lib = isHttps ? https : http;
139
+
140
+ const options = {
141
+ hostname: url.hostname,
142
+ port: url.port || (isHttps ? 443 : 80),
143
+ path: url.pathname,
144
+ method: 'GET',
145
+ headers: {
146
+ 'Authorization': `Bearer ${jwtToken}`
147
+ }
148
+ };
149
+
150
+ const req = lib.request(options, (res) => {
151
+ let data = '';
152
+ res.on('data', chunk => data += chunk);
153
+ res.on('end', () => {
154
+ if (res.statusCode === 200) {
155
+ try {
156
+ resolve(JSON.parse(data));
157
+ } catch {
158
+ reject(new Error('Invalid response from server'));
159
+ }
160
+ } else if (res.statusCode === 401) {
161
+ reject(new Error('Invalid login token'));
162
+ } else {
163
+ reject(new Error(`Server error: ${res.statusCode}`));
164
+ }
165
+ });
166
+ });
167
+
168
+ req.on('error', (err) => reject(new Error(`Connection failed: ${err.message}`)));
169
+ req.setTimeout(10000, () => {
170
+ req.destroy();
171
+ reject(new Error('Connection timeout'));
172
+ });
173
+ req.end();
174
+ });
175
+ }
176
+
177
+ function validateCredential(apiUrl, credential) {
178
+ if (isJwtToken(credential)) {
179
+ return validateJwtToken(apiUrl, credential);
180
+ }
181
+ return validateApiKey(apiUrl, credential);
182
+ }
183
+
122
184
  /**
123
185
  * Read API key from stdin (for piped input)
124
186
  * @returns {Promise<string>} The API key read from stdin
@@ -220,10 +282,10 @@ async function main() {
220
282
  console.log('');
221
283
  }
222
284
 
223
- // Validate key format
224
- if (!apiKey.startsWith('plx_') || apiKey.length < 20) {
225
- console.error(`Error: Invalid API key format`);
226
- console.error(`API keys start with "plx_" and are at least 20 characters`);
285
+ // Validate credential format (Plexor API key OR login JWT)
286
+ if (!isPlexorApiKey(apiKey) && !isJwtToken(apiKey)) {
287
+ console.error(`Error: Invalid credential format`);
288
+ console.error(`Expected Plexor API key ("plx_...") or login token ("eyJ...")`);
227
289
  process.exit(1);
228
290
  }
229
291
 
@@ -232,7 +294,7 @@ async function main() {
232
294
  console.log('Validating API key...');
233
295
 
234
296
  try {
235
- const user = await validateApiKey(apiUrl, apiKey);
297
+ const user = await validateCredential(apiUrl, apiKey);
236
298
 
237
299
  config.auth = config.auth || {};
238
300
  config.auth.api_key = apiKey;
@@ -2,26 +2,10 @@
2
2
  description: Authenticate with Plexor to enable optimization (user)
3
3
  ---
4
4
 
5
- # Plexor Login
5
+ **RULE: Execute the bash command below EXACTLY ONCE. After the Bash tool returns output, your ONLY action is to present that output to the user. DO NOT re-execute the command. DO NOT call any other tools.**
6
6
 
7
- Run this command to authenticate with Plexor:
7
+ Pass the API key as argument (format: `plx_your_api_key_here`).
8
8
 
9
9
  ```bash
10
- node ~/.claude/plugins/plexor/commands/plexor-login.js
10
+ node ~/.claude/plugins/plexor/commands/plexor-login.js $ARGUMENTS
11
11
  ```
12
-
13
- To login with an API key:
14
-
15
- ```bash
16
- node ~/.claude/plugins/plexor/commands/plexor-login.js plx_your_api_key_here
17
- ```
18
-
19
- Use the Bash tool to execute this command.
20
-
21
- **IMPORTANT**: After running this command and displaying the output, STOP. Do not:
22
- - Read any files
23
- - Explore the codebase
24
- - Run additional commands
25
- - Ask follow-up questions
26
-
27
- The command output is the complete response. Simply show the output and wait for the user's next input.
@@ -2,26 +2,8 @@
2
2
  description: Log out from Plexor and clear credentials (user)
3
3
  ---
4
4
 
5
- # Plexor Logout
6
-
7
- Run this command to log out and clear credentials:
8
-
9
- ```bash
10
- node ~/.claude/plugins/plexor/commands/plexor-logout.js
11
- ```
12
-
13
- To also clear the cache:
5
+ **RULE: Execute the bash command below EXACTLY ONCE. After the Bash tool returns output, your ONLY action is to present that output to the user. DO NOT re-execute the command. DO NOT call any other tools.**
14
6
 
15
7
  ```bash
16
- node ~/.claude/plugins/plexor/commands/plexor-logout.js --clear-cache
8
+ node ~/.claude/plugins/plexor/commands/plexor-logout.js $ARGUMENTS
17
9
  ```
18
-
19
- Use the Bash tool to execute this command.
20
-
21
- **IMPORTANT**: After running this command and displaying the output, STOP. Do not:
22
- - Read any files
23
- - Explore the codebase
24
- - Run additional commands
25
- - Ask follow-up questions
26
-
27
- The command output is the complete response. Simply show the output and wait for the user's next input.
@@ -2,6 +2,8 @@
2
2
  description: First-time setup wizard for Plexor with Claude Code (user)
3
3
  ---
4
4
 
5
+ **RULE: Execute this workflow EXACTLY ONCE. After completing all steps, STOP. DO NOT restart the workflow. DO NOT re-execute any commands. DO NOT call any other tools.**
6
+
5
7
  # Plexor Setup Wizard
6
8
 
7
9
  Guide users through first-time Plexor setup. **No manual environment variable configuration required!**
@@ -165,8 +167,9 @@ Commands:
165
167
  Changes take effect immediately in all Claude Code sessions!
166
168
  ```
167
169
 
168
- **IMPORTANT NOTES**:
170
+ **NOTES**:
169
171
  - The `~/.claude/settings.json` env block is the KEY mechanism that routes Claude Code through Plexor
170
172
  - ANTHROPIC_AUTH_TOKEN takes precedence over ANTHROPIC_API_KEY (use AUTH_TOKEN for the Plexor key)
171
173
  - Changes take effect immediately - no shell restart needed
172
- - After completing setup, STOP. Do not run additional commands.
174
+
175
+ After completing all steps, STOP. DO NOT restart the workflow. DO NOT re-execute any commands.
@@ -2,20 +2,8 @@
2
2
  description: Show Plexor optimization statistics and savings (user)
3
3
  ---
4
4
 
5
- # Plexor Status
6
-
7
- Run this command to display Plexor statistics:
5
+ **RULE: Execute the bash command below EXACTLY ONCE. After the Bash tool returns output, your ONLY action is to present that output to the user. DO NOT re-execute the command. DO NOT call any other tools.**
8
6
 
9
7
  ```bash
10
8
  node ~/.claude/plugins/plexor/commands/plexor-status.js
11
9
  ```
12
-
13
- Use the Bash tool to execute this single command.
14
-
15
- **IMPORTANT**: After running this command and displaying the output, STOP. Do not:
16
- - Read any files
17
- - Explore the codebase
18
- - Run additional commands
19
- - Ask follow-up questions
20
-
21
- The command output is the complete response. Simply show the output and wait for the user's next input.
@@ -179,6 +179,17 @@ async function main() {
179
179
  input = await readStdin();
180
180
  request = JSON.parse(input);
181
181
 
182
+ // Issue #2042: Check for slash commands FIRST, before ANY other processing
183
+ // Slash commands must pass through completely clean — no metadata injection
184
+ // Adding _plexor_client or _plexor to slash command requests adds context noise
185
+ // that causes the model to re-execute commands in a loop
186
+ // Note: session.recordPassthrough() intentionally omitted — slash commands are
187
+ // not API requests and should not pollute session analytics
188
+ if (isSlashCommand(request)) {
189
+ logger.debug('Slash command detected, clean passthrough (no metadata)');
190
+ return output(request); // Completely clean — no metadata added
191
+ }
192
+
182
193
  // Phase 3 Hypervisor Mode Detection
183
194
  // When ANTHROPIC_BASE_URL points to Plexor, all intelligence is server-side
184
195
  // The plugin just passes through - server handles optimization, routing, quality
@@ -202,25 +213,6 @@ async function main() {
202
213
  });
203
214
  }
204
215
 
205
- // CRITICAL: Check for slash commands FIRST (before agentic check)
206
- // Slash commands like /plexor-status should pass through unchanged
207
- // Must check before isAgenticRequest since all Claude Code requests have tools
208
- if (isSlashCommand(request)) {
209
- logger.debug('Slash command detected, passing through unchanged');
210
- session.recordPassthrough();
211
- return output({
212
- ...request,
213
- plexor_cwd: process.cwd(),
214
- _plexor: {
215
- request_id: generateRequestId('slash'), // Issue #701: Add request_id for tracking
216
- source: 'passthrough_slash_command',
217
- reason: 'slash_command_detected',
218
- cwd: process.cwd(),
219
- latency_ms: Date.now() - startTime
220
- }
221
- });
222
- }
223
-
224
216
  // CRITICAL: Skip optimization for CLI commands requiring tool execution
225
217
  // Azure CLI, AWS CLI, kubectl, etc. need tools to be preserved
226
218
  if (requiresToolExecution(request)) {
@@ -578,12 +570,16 @@ function isSlashCommand(request) {
578
570
  }
579
571
 
580
572
  // Check for system messages with skill instructions
573
+ // Issue #2042: Updated to match new RULE-based .md format (old H1 headers removed)
581
574
  for (const msg of messages) {
582
575
  if (msg.role === 'system') {
583
576
  const content = typeof msg.content === 'string' ? msg.content : '';
584
577
  if (/# Plexor (?:Status|Login|Logout|Mode|Provider|Enabled|Settings)/i.test(content)) {
585
578
  return true;
586
579
  }
580
+ if (/plexor\/commands\/plexor-/i.test(content)) {
581
+ return true;
582
+ }
587
583
  }
588
584
  }
589
585
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plexor-dev/claude-code-plugin-localhost",
3
- "version": "0.1.0-beta.11",
3
+ "version": "0.1.0-beta.13",
4
4
  "description": "LOCALHOST - LLM cost optimization plugin for Claude Code (local dev testing via tunnel)",
5
5
  "main": "lib/constants.js",
6
6
  "bin": {
@@ -60,7 +60,19 @@ function main() {
60
60
  }
61
61
  }
62
62
 
63
- if (removed.length > 0 || routingDisabled) {
63
+ // Remove plugin directory (~/.claude/plugins/plexor/)
64
+ let pluginDirRemoved = false;
65
+ try {
66
+ const pluginDir = path.join(os.homedir(), '.claude', 'plugins', 'plexor');
67
+ if (fs.existsSync(pluginDir)) {
68
+ fs.rmSync(pluginDir, { recursive: true, force: true });
69
+ pluginDirRemoved = true;
70
+ }
71
+ } catch (e) {
72
+ console.warn(` Warning: Could not remove plugin directory: ${e.message}`);
73
+ }
74
+
75
+ if (removed.length > 0 || routingDisabled || pluginDirRemoved) {
64
76
  console.log('');
65
77
  console.log(' Plexor plugin uninstalled');
66
78
  console.log('');
@@ -76,6 +88,11 @@ function main() {
76
88
  removed.forEach(cmd => console.log(` /${cmd}`));
77
89
  }
78
90
 
91
+ if (pluginDirRemoved) {
92
+ console.log('');
93
+ console.log(' ✓ Removed plugin directory (~/.claude/plugins/plexor/)');
94
+ }
95
+
79
96
  if (restored.length > 0) {
80
97
  console.log('');
81
98
  console.log(' Restored from backup:');
package/lib/cache.js DELETED
@@ -1,107 +0,0 @@
1
- /**
2
- * Plexor Local Cache
3
- *
4
- * Stores request/response metadata for cache hit detection.
5
- */
6
-
7
- const fs = require('fs');
8
- const { CACHE_PATH, PLEXOR_DIR } = require('./constants');
9
-
10
- const CACHE_TTL_MS = 5 * 60 * 1000; // 5 minutes
11
-
12
- class LocalCache {
13
- constructor() {
14
- this.cachePath = CACHE_PATH;
15
- this.cache = this.load();
16
- }
17
-
18
- load() {
19
- try {
20
- const data = fs.readFileSync(this.cachePath, 'utf8');
21
- return JSON.parse(data);
22
- } catch {
23
- return { entries: {}, metadata: {} };
24
- }
25
- }
26
-
27
- save() {
28
- try {
29
- if (!fs.existsSync(PLEXOR_DIR)) {
30
- fs.mkdirSync(PLEXOR_DIR, { recursive: true });
31
- }
32
- fs.writeFileSync(this.cachePath, JSON.stringify(this.cache, null, 2));
33
- return true;
34
- } catch {
35
- return false;
36
- }
37
- }
38
-
39
- generateKey(messages) {
40
- const str = JSON.stringify(messages);
41
- let hash = 0;
42
- for (let i = 0; i < str.length; i++) {
43
- hash = ((hash << 5) - hash) + str.charCodeAt(i);
44
- hash |= 0;
45
- }
46
- return `cache_${Math.abs(hash)}`;
47
- }
48
-
49
- async get(key) {
50
- const entry = this.cache.entries[key];
51
- if (!entry) {
52
- return null;
53
- }
54
-
55
- // Check if expired
56
- if (Date.now() - entry.timestamp > CACHE_TTL_MS) {
57
- delete this.cache.entries[key];
58
- this.save();
59
- return null;
60
- }
61
-
62
- return entry.value;
63
- }
64
-
65
- async set(key, value) {
66
- this.cache.entries[key] = {
67
- value,
68
- timestamp: Date.now()
69
- };
70
- this.cleanup();
71
- this.save();
72
- }
73
-
74
- async getMetadata(requestId) {
75
- return this.cache.metadata[requestId] || null;
76
- }
77
-
78
- async setMetadata(requestId, metadata) {
79
- this.cache.metadata[requestId] = {
80
- ...metadata,
81
- timestamp: Date.now()
82
- };
83
- this.cleanupMetadata();
84
- this.save();
85
- }
86
-
87
- cleanup() {
88
- const now = Date.now();
89
- for (const key of Object.keys(this.cache.entries)) {
90
- if (now - this.cache.entries[key].timestamp > CACHE_TTL_MS) {
91
- delete this.cache.entries[key];
92
- }
93
- }
94
- }
95
-
96
- cleanupMetadata() {
97
- const now = Date.now();
98
- const METADATA_TTL_MS = 60 * 60 * 1000; // 1 hour
99
- for (const key of Object.keys(this.cache.metadata)) {
100
- if (now - this.cache.metadata[key].timestamp > METADATA_TTL_MS) {
101
- delete this.cache.metadata[key];
102
- }
103
- }
104
- }
105
- }
106
-
107
- module.exports = LocalCache;
package/lib/config.js DELETED
@@ -1,67 +0,0 @@
1
- /**
2
- * Plexor Configuration Manager
3
- */
4
-
5
- const fs = require('fs');
6
- const path = require('path');
7
- const { CONFIG_PATH, PLEXOR_DIR, DEFAULT_API_URL, DEFAULT_TIMEOUT } = require('./constants');
8
-
9
- class ConfigManager {
10
- constructor() {
11
- this.configPath = CONFIG_PATH;
12
- }
13
-
14
- async load() {
15
- try {
16
- const data = fs.readFileSync(this.configPath, 'utf8');
17
- const cfg = JSON.parse(data);
18
- return {
19
- enabled: cfg.settings?.enabled ?? false,
20
- apiKey: cfg.auth?.api_key || '',
21
- apiUrl: cfg.settings?.apiUrl || DEFAULT_API_URL,
22
- timeout: cfg.settings?.timeout || DEFAULT_TIMEOUT,
23
- localCacheEnabled: cfg.settings?.localCacheEnabled ?? false,
24
- mode: cfg.settings?.mode || 'balanced',
25
- preferredProvider: cfg.settings?.preferred_provider || 'auto'
26
- };
27
- } catch {
28
- return { enabled: false, apiKey: '', apiUrl: DEFAULT_API_URL };
29
- }
30
- }
31
-
32
- async save(config) {
33
- try {
34
- if (!fs.existsSync(PLEXOR_DIR)) {
35
- fs.mkdirSync(PLEXOR_DIR, { recursive: true });
36
- }
37
-
38
- let existing = {};
39
- try {
40
- const data = fs.readFileSync(this.configPath, 'utf8');
41
- existing = JSON.parse(data);
42
- } catch {
43
- existing = { version: 1, auth: {}, settings: {} };
44
- }
45
-
46
- const updated = {
47
- ...existing,
48
- settings: {
49
- ...existing.settings,
50
- enabled: config.enabled ?? existing.settings?.enabled,
51
- apiUrl: config.apiUrl ?? existing.settings?.apiUrl,
52
- timeout: config.timeout ?? existing.settings?.timeout,
53
- localCacheEnabled: config.localCacheEnabled ?? existing.settings?.localCacheEnabled,
54
- mode: config.mode ?? existing.settings?.mode,
55
- preferred_provider: config.preferredProvider ?? existing.settings?.preferred_provider
56
- }
57
- };
58
-
59
- fs.writeFileSync(this.configPath, JSON.stringify(updated, null, 2), { mode: 0o600 });
60
- return true;
61
- } catch {
62
- return false;
63
- }
64
- }
65
- }
66
-
67
- module.exports = ConfigManager;
package/lib/constants.js DELETED
@@ -1,44 +0,0 @@
1
- /**
2
- * Plexor Plugin Constants
3
- */
4
-
5
- const path = require('path');
6
-
7
- /**
8
- * Get the user's home directory with proper validation
9
- * @returns {string} Home directory path
10
- * @throws {Error} If HOME is not set or empty
11
- */
12
- function getHomeDir() {
13
- const home = process.env.HOME || process.env.USERPROFILE;
14
- if (!home || home.trim() === '') {
15
- console.error('Error: HOME environment variable is not set.');
16
- console.error(' Please set HOME to your user directory before running Plexor commands.');
17
- process.exit(1);
18
- }
19
- return home;
20
- }
21
-
22
- const HOME_DIR = getHomeDir();
23
- const PLEXOR_DIR = path.join(HOME_DIR, '.plexor');
24
- const CONFIG_PATH = path.join(PLEXOR_DIR, 'config.json');
25
- const SESSION_PATH = path.join(PLEXOR_DIR, 'session.json');
26
- const CACHE_PATH = path.join(PLEXOR_DIR, 'cache.json');
27
-
28
- const SESSION_TIMEOUT_MS = 30 * 60 * 1000; // 30 minutes
29
-
30
- // STAGING PACKAGE - uses staging API
31
- const DEFAULT_API_URL = 'http://localhost:8000';
32
- const DEFAULT_TIMEOUT = 5000;
33
-
34
- module.exports = {
35
- getHomeDir,
36
- HOME_DIR,
37
- PLEXOR_DIR,
38
- CONFIG_PATH,
39
- SESSION_PATH,
40
- CACHE_PATH,
41
- SESSION_TIMEOUT_MS,
42
- DEFAULT_API_URL,
43
- DEFAULT_TIMEOUT
44
- };
package/lib/index.js DELETED
@@ -1,19 +0,0 @@
1
- /**
2
- * Plexor Plugin Library
3
- */
4
-
5
- const ConfigManager = require('./config');
6
- const SessionManager = require('./session');
7
- const LocalCache = require('./cache');
8
- const Logger = require('./logger');
9
- const PlexorClient = require('./plexor-client');
10
- const constants = require('./constants');
11
-
12
- module.exports = {
13
- ConfigManager,
14
- SessionManager,
15
- LocalCache,
16
- Logger,
17
- PlexorClient,
18
- ...constants
19
- };
package/lib/logger.js DELETED
@@ -1,36 +0,0 @@
1
- /**
2
- * Plexor Logger
3
- *
4
- * Simple logger that outputs to stderr to avoid interfering with stdout JSON.
5
- */
6
-
7
- class Logger {
8
- constructor(component = 'plexor') {
9
- this.component = component;
10
- this.debug_enabled = process.env.PLEXOR_DEBUG === '1' || process.env.PLEXOR_DEBUG === 'true';
11
- }
12
-
13
- debug(msg, data = null) {
14
- if (this.debug_enabled) {
15
- const output = data ? `[DEBUG][${this.component}] ${msg} ${JSON.stringify(data)}` : `[DEBUG][${this.component}] ${msg}`;
16
- console.error(output);
17
- }
18
- }
19
-
20
- info(msg, data = null) {
21
- const output = data ? `${msg} ${JSON.stringify(data)}` : msg;
22
- console.error(output);
23
- }
24
-
25
- warn(msg, data = null) {
26
- const output = data ? `[WARN][${this.component}] ${msg} ${JSON.stringify(data)}` : `[WARN][${this.component}] ${msg}`;
27
- console.error(output);
28
- }
29
-
30
- error(msg, data = null) {
31
- const output = data ? `[ERROR][${this.component}] ${msg} ${JSON.stringify(data)}` : `[ERROR][${this.component}] ${msg}`;
32
- console.error(output);
33
- }
34
- }
35
-
36
- module.exports = Logger;