@patcherly/nodejs-connector 2.2.2 → 2.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/README.md +17 -1
- package/lib/ingest_severity.js +55 -2
- package/node_agent.js +11 -4
- package/package.json +6 -2
package/README.md
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# Patcherly Node.js connector
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Patcherly detects bugs in real time in your app. It creates a customized AI patch, and once you approve it, backs up your code, fixes it & tests the patch for you. If anything is off, it rolls back the changes automatically, or you can always roll it back in a click.
|
|
4
|
+
|
|
5
|
+
**[patcherly.com](https://patcherly.com)** · **[Node.js connector docs](https://help.patcherly.com/connectors/nodejs/)**
|
|
4
6
|
|
|
5
7
|
## Recommended install
|
|
6
8
|
|
|
@@ -19,6 +21,20 @@ npx patcherly login
|
|
|
19
21
|
patcherly login
|
|
20
22
|
```
|
|
21
23
|
|
|
24
|
+
## Test ingest
|
|
25
|
+
|
|
26
|
+
After pairing, verify the pipeline without waiting for a real exception. In your [Patcherly dashboard](https://app.patcherly.com/targets), open **Targets → your target → Test Mode** (30-minute window per target), then run:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
npx patcherly send-test
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
The sample is tagged `is_test_sample` — it does not affect metrics or notifications. See [Verify ingest with send-test](https://help.patcherly.com/connectors/nodejs/#verify-ingest-end-to-end-with-patcherly-send-test) in the help center.
|
|
33
|
+
|
|
34
|
+
## Security
|
|
35
|
+
|
|
36
|
+
OAuth pairing and per-token **HMAC signing**; fix payloads verified before apply. See [Connectors overview](https://help.patcherly.com/connectors/overview/), [Prompt injection protection](https://help.patcherly.com/security/prompt-injection-protection.md), and [Custom sanitiser patterns](https://help.patcherly.com/security/custom-sanitizer-patterns.md). Node.js-specific detail: [Node.js connector — security](https://help.patcherly.com/connectors/nodejs/#hmac-signing).
|
|
37
|
+
|
|
22
38
|
## Documentation
|
|
23
39
|
|
|
24
40
|
- [Node.js connector guide](https://help.patcherly.com/connectors/nodejs/)
|
package/lib/ingest_severity.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* AUTO-GENERATED from config/settings_schema.yaml
|
|
3
|
-
* Shared log-line → error_type → severity inference
|
|
2
|
+
* AUTO-GENERATED from config/settings_schema.yaml + log_ingest_skip_patterns.yaml — do not edit by hand.
|
|
3
|
+
* Shared log-line → error_type → severity inference and pre-ingest noise filtering.
|
|
4
4
|
*/
|
|
5
5
|
'use strict';
|
|
6
6
|
|
|
@@ -22,6 +22,58 @@ const DEFAULT_ERROR_TYPE_SEVERITIES = {
|
|
|
22
22
|
"warning": "Low",
|
|
23
23
|
};
|
|
24
24
|
|
|
25
|
+
const LOG_INGEST_KEEP_PATTERNS = [
|
|
26
|
+
"PHP\\s+Fatal",
|
|
27
|
+
"PHP\\s+Parse\\s+error",
|
|
28
|
+
"PHP\\s+Recoverable\\s+fatal",
|
|
29
|
+
"^Fatal error:",
|
|
30
|
+
"Maximum execution time|Allowed memory size",
|
|
31
|
+
"Uncaught\\s+\\S*(Error|Exception)",
|
|
32
|
+
"^Traceback\\s",
|
|
33
|
+
"^\\w+(Error|Exception):",
|
|
34
|
+
"^Exception:",
|
|
35
|
+
"^Error:\\s",
|
|
36
|
+
"Unhandled\\s+(Promise\\s+)?Rejection",
|
|
37
|
+
"uncaughtException|uncaught exception",
|
|
38
|
+
"Assertion failed",
|
|
39
|
+
"File\\s+[\"']",
|
|
40
|
+
"^\\s*#\\d+\\s+",
|
|
41
|
+
"^\\s+at\\s+",
|
|
42
|
+
"\"level\"\\s*:\\s*\"(error|fatal|critical)\"",
|
|
43
|
+
"\"level\"\\s*:\\s*50\\b",
|
|
44
|
+
"\"severity\"\\s*:\\s*\"(ERROR|CRITICAL|FATAL)\"",
|
|
45
|
+
].map((p) => new RegExp(p, 'i'));
|
|
46
|
+
const LOG_INGEST_SKIP_PHP_PREFIX = new RegExp("^PHP\\s+(Notice|Deprecated|Warning|Strict\\s+standards|Info)\\s*:", 'i');
|
|
47
|
+
const LOG_INGEST_SKIP_PATTERNS = [
|
|
48
|
+
"^\\(node:\\d+\\)\\s+\\[DEP\\d+\\]",
|
|
49
|
+
"DeprecationWarning:",
|
|
50
|
+
"^UserWarning:",
|
|
51
|
+
"^\\[info\\]",
|
|
52
|
+
"^INFO:",
|
|
53
|
+
"^DEBUG:",
|
|
54
|
+
].map((p) => new RegExp(p, 'i'));
|
|
55
|
+
const LOG_INGEST_SKIP_SUBSTRINGS = ["auditor:scan", "\"kind\":\"installed-plugin\""];
|
|
56
|
+
const LOG_INGEST_FAILURE_SIGNAL = new RegExp("\\b(error|exception|traceback|fatal|critical|panic|failed|failure|rejection|errno|segfault)\\b|^\\w+(Error|Exception):", 'i');
|
|
57
|
+
|
|
58
|
+
function shouldSkipLogLineForIngest(logLine) {
|
|
59
|
+
const line = String(logLine || '').trim();
|
|
60
|
+
if (!line) return true;
|
|
61
|
+
for (const re of LOG_INGEST_KEEP_PATTERNS) {
|
|
62
|
+
if (re.test(line)) return false;
|
|
63
|
+
}
|
|
64
|
+
if (LOG_INGEST_SKIP_PHP_PREFIX && LOG_INGEST_SKIP_PHP_PREFIX.test(line)) return true;
|
|
65
|
+
for (const re of LOG_INGEST_SKIP_PATTERNS) {
|
|
66
|
+
if (re.test(line)) return true;
|
|
67
|
+
}
|
|
68
|
+
const lower = line.toLowerCase();
|
|
69
|
+
for (const sub of LOG_INGEST_SKIP_SUBSTRINGS) {
|
|
70
|
+
if (sub && lower.includes(String(sub).toLowerCase())) return true;
|
|
71
|
+
}
|
|
72
|
+
if (!LOG_INGEST_FAILURE_SIGNAL.test(line)) return true;
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
|
|
25
77
|
function inferErrorTypeFromLogLine(logLine) {
|
|
26
78
|
const line = String(logLine || '').toLowerCase();
|
|
27
79
|
if (line.includes('parse error')) return 'parse';
|
|
@@ -48,6 +100,7 @@ function buildIngestSeverityFields(logLine) {
|
|
|
48
100
|
|
|
49
101
|
module.exports = {
|
|
50
102
|
DEFAULT_ERROR_TYPE_SEVERITIES,
|
|
103
|
+
shouldSkipLogLineForIngest,
|
|
51
104
|
inferErrorTypeFromLogLine,
|
|
52
105
|
severityForErrorType,
|
|
53
106
|
buildIngestSeverityFields,
|
package/node_agent.js
CHANGED
|
@@ -20,7 +20,7 @@ const path = require('path');
|
|
|
20
20
|
const { execFile } = require('child_process');
|
|
21
21
|
const util = require('util');
|
|
22
22
|
const execFileAsync = util.promisify(execFile);
|
|
23
|
-
const { buildIngestSeverityFields } = require('./lib/ingest_severity.js');
|
|
23
|
+
const { buildIngestSeverityFields, shouldSkipLogLineForIngest } = require('./lib/ingest_severity.js');
|
|
24
24
|
const apiPaths = require('./lib/api_paths.js');
|
|
25
25
|
const { namedPaths, appPath } = apiPaths;
|
|
26
26
|
|
|
@@ -193,7 +193,7 @@ const { DEFAULT_API_URL, getConfiguredServerUrl, isExplicitApiBaseConfigured } =
|
|
|
193
193
|
* update-release-latest.yml workflow so the value baked into every released tarball matches
|
|
194
194
|
* the GitHub release tag. Reported to the API on every context upload.
|
|
195
195
|
*/
|
|
196
|
-
const PATCHERLY_CONNECTOR_VERSION = '2.2.
|
|
196
|
+
const PATCHERLY_CONNECTOR_VERSION = '2.2.4';
|
|
197
197
|
let CENTRAL_SERVER_URL = getConfiguredServerUrl();
|
|
198
198
|
const IDS_PATH = process.env.PATCHERLY_IDS_PATH || path.join(__dirname, 'patcherly_ids.json');
|
|
199
199
|
const QUEUE_PATH = process.env.PATCHERLY_QUEUE_PATH || path.join(__dirname, 'patcherly_queue.jsonl');
|
|
@@ -1004,7 +1004,7 @@ function extractErrorContext(logData) {
|
|
|
1004
1004
|
const lines = logData.split('\n');
|
|
1005
1005
|
const events = [];
|
|
1006
1006
|
let current = [];
|
|
1007
|
-
const startOrCont = /^(Traceback\s|File\s+["']|Exception:|Error:\s|PHP\s+Fatal
|
|
1007
|
+
const startOrCont = /^(Traceback\s|File\s+["']|Exception:|Error:\s|PHP\s+Fatal|^\s+at\s+|\s*#\d+\s+)/i;
|
|
1008
1008
|
const errorWord = /\b(error|exception|traceback|fatal)\b/i;
|
|
1009
1009
|
// Python exception type line (e.g. "ValueError: bad") — treat as continuation when in a block
|
|
1010
1010
|
const pythonExceptionLine = /^\w+(?:Error|Exception):\s/i;
|
|
@@ -1033,7 +1033,10 @@ function extractErrorContext(logData) {
|
|
|
1033
1033
|
}
|
|
1034
1034
|
flush();
|
|
1035
1035
|
if (events.length === 0) {
|
|
1036
|
-
const errorLines = lines.filter(l =>
|
|
1036
|
+
const errorLines = lines.filter(l =>
|
|
1037
|
+
/\b(error|exception|traceback|fatal|critical|failed|failure|rejection)\b/i.test(l)
|
|
1038
|
+
|| /^\s*\w+(Error|Exception):/i.test(l)
|
|
1039
|
+
);
|
|
1037
1040
|
if (errorLines.length) events.push(errorLines.join('\n'));
|
|
1038
1041
|
}
|
|
1039
1042
|
return events;
|
|
@@ -1261,6 +1264,10 @@ async function processError(errorContext) {
|
|
|
1261
1264
|
// ingest (errorContext is string or object; server expects log_line string)
|
|
1262
1265
|
const logLine = typeof errorContext === 'string' ? errorContext : JSON.stringify(errorContext);
|
|
1263
1266
|
const logLineSanitized = sanitizeLogLineForIngest(logLine);
|
|
1267
|
+
if (shouldSkipLogLineForIngest(logLineSanitized)) {
|
|
1268
|
+
console.log('Non-error log noise skipped.');
|
|
1269
|
+
return;
|
|
1270
|
+
}
|
|
1264
1271
|
const payload = { log_line: logLineSanitized, source: 'log_monitor' };
|
|
1265
1272
|
Object.assign(payload, buildIngestSeverityFields(logLineSanitized));
|
|
1266
1273
|
if (TENANT_ID && TARGET_ID){ payload.tenant_id = TENANT_ID; payload.target_id = TARGET_ID; }
|
package/package.json
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@patcherly/nodejs-connector",
|
|
3
|
-
"version": "2.2.
|
|
4
|
-
"description": "Patcherly
|
|
3
|
+
"version": "2.2.4",
|
|
4
|
+
"description": "Patcherly detects bugs in real time in your app. It creates a customized AI patch, and once you approve it, backs up your code, fixes it & tests the patch for you. If anything is off, it rolls back the changes automatically, or you can always roll it back in a click. Website: https://patcherly.com · Docs: https://help.patcherly.com/connectors/nodejs/",
|
|
5
5
|
"license": "MIT",
|
|
6
|
+
"homepage": "https://patcherly.com",
|
|
7
|
+
"bugs": {
|
|
8
|
+
"url": "https://github.com/Patcherly-Official/patcherly-connector-packages/issues"
|
|
9
|
+
},
|
|
6
10
|
"publishConfig": {
|
|
7
11
|
"access": "public"
|
|
8
12
|
},
|