lynkr 7.2.3 → 7.2.4
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": "lynkr",
|
|
3
|
-
"version": "7.2.
|
|
3
|
+
"version": "7.2.4",
|
|
4
4
|
"description": "Self-hosted Claude Code & Cursor proxy with Databricks,AWS BedRock,Azure adapters, openrouter, Ollama,llamacpp,LM Studio, workspace tooling, and MCP integration.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
@@ -8,9 +8,9 @@
|
|
|
8
8
|
"lynkr-setup": "./scripts/setup.js"
|
|
9
9
|
},
|
|
10
10
|
"scripts": {
|
|
11
|
-
"prestart": "docker compose --profile headroom up -d headroom 2>/dev/null || echo 'Headroom
|
|
11
|
+
"prestart": "node -e \"if(process.env.HEADROOM_ENABLED==='true'&&process.env.HEADROOM_DOCKER_ENABLED!=='false'){process.exit(0)}else{process.exit(1)}\" && docker compose --profile headroom up -d headroom 2>/dev/null || echo 'Headroom skipped (disabled or Docker not running)'",
|
|
12
12
|
"start": "node index.js 2>&1 | npx pino-pretty --sync",
|
|
13
|
-
"stop": "docker compose --profile headroom down",
|
|
13
|
+
"stop": "node -e \"if(process.env.HEADROOM_ENABLED==='true'&&process.env.HEADROOM_DOCKER_ENABLED!=='false'){process.exit(0)}else{process.exit(1)}\" && docker compose --profile headroom down || echo 'Headroom skipped (disabled or Docker not running)'",
|
|
14
14
|
"dev": "nodemon index.js",
|
|
15
15
|
"lint": "eslint src index.js",
|
|
16
16
|
"test": "npm run test:unit && npm run test:performance",
|
package/src/clients/retry.js
CHANGED
|
@@ -10,7 +10,7 @@ const DEFAULT_CONFIG = {
|
|
|
10
10
|
backoffMultiplier: 2,
|
|
11
11
|
jitterFactor: 0.1, // 10% jitter
|
|
12
12
|
retryableStatuses: [429, 500, 502, 503, 504],
|
|
13
|
-
retryableErrors: ['ECONNRESET', 'ETIMEDOUT', 'ENOTFOUND', 'ENETUNREACH'],
|
|
13
|
+
retryableErrors: ['ECONNRESET', 'ETIMEDOUT', 'ENOTFOUND', 'ENETUNREACH', 'ECONNREFUSED'],
|
|
14
14
|
};
|
|
15
15
|
|
|
16
16
|
/**
|
|
@@ -44,6 +44,11 @@ function isRetryable(error, response, config) {
|
|
|
44
44
|
return true;
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
+
// Check nested cause (Node undici wraps connection errors as TypeError)
|
|
48
|
+
if (error && error.cause?.code && config.retryableErrors.includes(error.cause.code)) {
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
|
|
47
52
|
// Check for network errors
|
|
48
53
|
if (error && (error.name === 'FetchError' || error.name === 'AbortError')) {
|
|
49
54
|
return true;
|
|
@@ -1694,7 +1694,33 @@ IMPORTANT TOOL USAGE RULES:
|
|
|
1694
1694
|
});
|
|
1695
1695
|
}
|
|
1696
1696
|
|
|
1697
|
-
|
|
1697
|
+
let databricksResponse;
|
|
1698
|
+
try {
|
|
1699
|
+
databricksResponse = await invokeModel(cleanPayload);
|
|
1700
|
+
} catch (modelError) {
|
|
1701
|
+
const isConnectionError = modelError.cause?.code === 'ECONNREFUSED'
|
|
1702
|
+
|| modelError.message?.includes('fetch failed')
|
|
1703
|
+
|| modelError.code === 'ECONNREFUSED';
|
|
1704
|
+
if (isConnectionError) {
|
|
1705
|
+
logger.error(`Provider ${providerType} is unreachable (connection refused). Is it running?`);
|
|
1706
|
+
return {
|
|
1707
|
+
response: {
|
|
1708
|
+
status: 503,
|
|
1709
|
+
body: {
|
|
1710
|
+
error: {
|
|
1711
|
+
type: "provider_unreachable",
|
|
1712
|
+
message: `Provider ${providerType} is unreachable. Is the service running?`,
|
|
1713
|
+
},
|
|
1714
|
+
},
|
|
1715
|
+
terminationReason: "provider_unreachable",
|
|
1716
|
+
},
|
|
1717
|
+
steps,
|
|
1718
|
+
durationMs: Date.now() - start,
|
|
1719
|
+
terminationReason: "provider_unreachable",
|
|
1720
|
+
};
|
|
1721
|
+
}
|
|
1722
|
+
throw modelError;
|
|
1723
|
+
}
|
|
1698
1724
|
|
|
1699
1725
|
// Extract and log actual token usage
|
|
1700
1726
|
const actualUsage = databricksResponse.ok && config.tokenTracking?.enabled !== false
|
|
@@ -9,6 +9,9 @@
|
|
|
9
9
|
|
|
10
10
|
const logger = require('../logger');
|
|
11
11
|
|
|
12
|
+
// Strip system-reminder blocks injected by the CLI before classification
|
|
13
|
+
const SYSTEM_REMINDER_PATTERN = /<system-reminder>[\s\S]*?<\/system-reminder>/g;
|
|
14
|
+
|
|
12
15
|
// Pre-compiled regex patterns for performance (avoid recompiling on every request)
|
|
13
16
|
const GREETING_PATTERN = /^(hi|hello|hey|good morning|good afternoon|good evening|howdy|greetings|sup|yo)[\s\.\!\?]*$/i;
|
|
14
17
|
const QUESTION_PATTERN = /^(what is|what's|how does|when|where|why|explain|define|tell me about|can you explain)/i;
|
|
@@ -190,7 +193,10 @@ function classifyRequestType(payload) {
|
|
|
190
193
|
return { type: 'coding', confidence: 0.5, keywords: [] };
|
|
191
194
|
}
|
|
192
195
|
|
|
193
|
-
const
|
|
196
|
+
const rawContent = extractContent(lastMessage);
|
|
197
|
+
// Strip <system-reminder> blocks before classification to prevent
|
|
198
|
+
// CLI-injected keywords (search, explain, documentation) from polluting results
|
|
199
|
+
const content = rawContent.replace(SYSTEM_REMINDER_PATTERN, '').trim();
|
|
194
200
|
const contentLower = content.toLowerCase();
|
|
195
201
|
const messageCount = payload.messages?.length ?? 0;
|
|
196
202
|
|