@antodevs/groundtruth 0.1.1 → 0.1.5
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 +4 -6
- package/assets/banner.svg +106 -0
- package/index.js +7 -4
- package/package.json +4 -2
- package/specification.yaml +1 -1
- package/src/circuit-breaker.js +8 -3
- package/src/cli.js +4 -2
- package/src/env.js +40 -0
- package/src/inject.js +1 -0
- package/src/packages.js +3 -1
- package/src/proxy.js +3 -2
- package/src/sanitize.js +35 -0
- package/src/search.js +3 -2
- package/src/state.js +3 -2
- package/src/watcher.js +4 -3
- package/assets/banner.png +0 -0
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-

|
|
2
2
|
|
|
3
3
|
# GroundTruth
|
|
4
4
|
|
|
@@ -25,7 +25,7 @@ npx @antodevs/groundtruth --antigravity
|
|
|
25
25
|
Intercepts outbound API calls and injects fresh documentation directly into the message payload.
|
|
26
26
|
|
|
27
27
|
```bash
|
|
28
|
-
npx @antodevs/groundtruth --
|
|
28
|
+
npx @antodevs/groundtruth --claude-code
|
|
29
29
|
```
|
|
30
30
|
|
|
31
31
|
## Installation
|
|
@@ -97,12 +97,10 @@ flowchart TD
|
|
|
97
97
|
|
|
98
98
|
---
|
|
99
99
|
|
|
100
|
-
## Installation & Usage
|
|
101
|
-
|
|
102
100
|
### Usage with Claude Code
|
|
103
101
|
```bash
|
|
104
102
|
# Initialize GroundTruth in proxy mode (auto-exports ANTHROPIC_BASE_URL)
|
|
105
|
-
npx groundtruth --claude-code
|
|
103
|
+
npx @antodevs/groundtruth --claude-code
|
|
106
104
|
|
|
107
105
|
# Execute your agent in a separate TTY
|
|
108
106
|
claude
|
|
@@ -114,7 +112,7 @@ claude
|
|
|
114
112
|
cd /workspace/your-project
|
|
115
113
|
|
|
116
114
|
# Initialize the daemon in file watcher mode
|
|
117
|
-
npx groundtruth --antigravity
|
|
115
|
+
npx @antodevs/groundtruth --antigravity
|
|
118
116
|
```
|
|
119
117
|
> **Note:** GroundTruth will continuously poll and sync documentation based on your `package.json` manifest.
|
|
120
118
|
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 300" width="100%">
|
|
2
|
+
<defs>
|
|
3
|
+
<!-- Background Gradient -->
|
|
4
|
+
<linearGradient id="bg" x1="0%" y1="0%" x2="100%" y2="100%">
|
|
5
|
+
<stop offset="0%" stop-color="#090a0f" />
|
|
6
|
+
<stop offset="50%" stop-color="#121521" />
|
|
7
|
+
<stop offset="100%" stop-color="#040508" />
|
|
8
|
+
</linearGradient>
|
|
9
|
+
|
|
10
|
+
<!-- Glowing Text Gradient -->
|
|
11
|
+
<linearGradient id="textGlow" x1="0%" y1="0%" x2="100%" y2="0%">
|
|
12
|
+
<stop offset="0%" stop-color="#4F46E5" />
|
|
13
|
+
<stop offset="50%" stop-color="#0ea5e9" />
|
|
14
|
+
<stop offset="100%" stop-color="#8b5cf6" />
|
|
15
|
+
</linearGradient>
|
|
16
|
+
|
|
17
|
+
<!-- Node Gradient -->
|
|
18
|
+
<linearGradient id="nodeGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
|
19
|
+
<stop offset="0%" stop-color="#38bdf8" />
|
|
20
|
+
<stop offset="100%" stop-color="#818cf8" />
|
|
21
|
+
</linearGradient>
|
|
22
|
+
|
|
23
|
+
<!-- Grid Pattern -->
|
|
24
|
+
<pattern id="grid" width="40" height="40" patternUnits="userSpaceOnUse">
|
|
25
|
+
<path d="M 40 0 L 0 0 0 40" fill="none" stroke="rgba(255, 255, 255, 0.03)" stroke-width="1"/>
|
|
26
|
+
</pattern>
|
|
27
|
+
|
|
28
|
+
<filter id="glow" x="-20%" y="-20%" width="140%" height="140%">
|
|
29
|
+
<feGaussianBlur stdDeviation="6" result="blur" />
|
|
30
|
+
<feMerge>
|
|
31
|
+
<feMergeNode in="blur" />
|
|
32
|
+
<feMergeNode in="SourceGraphic" />
|
|
33
|
+
</feMerge>
|
|
34
|
+
</filter>
|
|
35
|
+
</defs>
|
|
36
|
+
|
|
37
|
+
<!-- Deep Dark Solid Background -->
|
|
38
|
+
<rect width="100%" height="100%" fill="url(#bg)" />
|
|
39
|
+
|
|
40
|
+
<!-- Overlay Subtle Grid -->
|
|
41
|
+
<rect width="100%" height="100%" fill="url(#grid)" />
|
|
42
|
+
|
|
43
|
+
<!-- Connecting Lines representing Context Injection (Left) -->
|
|
44
|
+
<g opacity="0.6" stroke-width="2" fill="none" filter="url(#glow)">
|
|
45
|
+
<!-- Primary flow -->
|
|
46
|
+
<path d="M0 150 C 200 150, 300 100, 450 150" stroke="#0ea5e9" opacity="0.8"/>
|
|
47
|
+
<path d="M0 80 C 150 80, 250 150, 400 130" stroke="#4F46E5" opacity="0.5"/>
|
|
48
|
+
<path d="M0 220 C 180 220, 280 150, 420 170" stroke="#8b5cf6" opacity="0.5"/>
|
|
49
|
+
|
|
50
|
+
<!-- Minor branches -->
|
|
51
|
+
<path d="M100 80 L 150 110" stroke="#0ea5e9"/>
|
|
52
|
+
<path d="M200 220 L 250 185" stroke="#8b5cf6"/>
|
|
53
|
+
<path d="M300 100 L 320 130" stroke="#4F46E5"/>
|
|
54
|
+
</g>
|
|
55
|
+
|
|
56
|
+
<!-- Connecting Lines representing Context Injection (Right) -->
|
|
57
|
+
<g opacity="0.6" stroke-width="2" fill="none" filter="url(#glow)">
|
|
58
|
+
<!-- Primary flow -->
|
|
59
|
+
<path d="M1200 150 C 1000 150, 900 200, 750 150" stroke="#0ea5e9" opacity="0.8"/>
|
|
60
|
+
<path d="M1200 80 C 1050 80, 950 150, 800 130" stroke="#8b5cf6" opacity="0.5"/>
|
|
61
|
+
<path d="M1200 220 C 1020 220, 920 150, 780 170" stroke="#4F46E5" opacity="0.5"/>
|
|
62
|
+
|
|
63
|
+
<!-- Minor branches -->
|
|
64
|
+
<path d="M1100 80 L 1050 110" stroke="#0ea5e9"/>
|
|
65
|
+
<path d="M1000 220 L 950 185" stroke="#4F46E5"/>
|
|
66
|
+
<path d="M900 200 L 880 170" stroke="#8b5cf6"/>
|
|
67
|
+
</g>
|
|
68
|
+
|
|
69
|
+
<!-- Data Nodes (Left) -->
|
|
70
|
+
<g fill="url(#nodeGrad)" filter="url(#glow)">
|
|
71
|
+
<circle cx="100" cy="80" r="5" />
|
|
72
|
+
<circle cx="150" cy="110" r="4" />
|
|
73
|
+
<circle cx="200" cy="220" r="6" />
|
|
74
|
+
<circle cx="250" cy="185" r="4" />
|
|
75
|
+
<circle cx="300" cy="100" r="7" />
|
|
76
|
+
<circle cx="320" cy="130" r="3" />
|
|
77
|
+
<circle cx="450" cy="150" r="8" fill="#ffffff"/>
|
|
78
|
+
</g>
|
|
79
|
+
|
|
80
|
+
<!-- Data Nodes (Right) -->
|
|
81
|
+
<g fill="url(#nodeGrad)" filter="url(#glow)">
|
|
82
|
+
<circle cx="1100" cy="80" r="5" />
|
|
83
|
+
<circle cx="1050" cy="110" r="4" />
|
|
84
|
+
<circle cx="1000" cy="220" r="6" />
|
|
85
|
+
<circle cx="950" cy="185" r="4" />
|
|
86
|
+
<circle cx="900" cy="200" r="7" />
|
|
87
|
+
<circle cx="880" cy="170" r="3" />
|
|
88
|
+
<circle cx="750" cy="150" r="8" fill="#ffffff"/>
|
|
89
|
+
</g>
|
|
90
|
+
|
|
91
|
+
<!-- Typography -->
|
|
92
|
+
<text x="50%" y="48%" text-anchor="middle" font-family="-apple-system, system-ui, BlinkMacSystemFont, Segoe UI, Roboto, sans-serif" font-size="94" font-weight="900" fill="url(#textGlow)" filter="url(#glow)" letter-spacing="2">
|
|
93
|
+
GROUNDTRUTH
|
|
94
|
+
</text>
|
|
95
|
+
|
|
96
|
+
<text x="50%" y="48%" text-anchor="middle" font-family="-apple-system, system-ui, BlinkMacSystemFont, Segoe UI, Roboto, sans-serif" font-size="94" font-weight="900" fill="#ffffff" letter-spacing="2">
|
|
97
|
+
GROUNDTRUTH
|
|
98
|
+
</text>
|
|
99
|
+
|
|
100
|
+
<text x="50%" y="68%" text-anchor="middle" font-family="-apple-system, system-ui, BlinkMacSystemFont, Segoe UI, Roboto, sans-serif" font-size="28" font-weight="500" fill="#94a3b8" letter-spacing="4">
|
|
101
|
+
ZERO ZERO-CONFIGURATION CONTEXT INJECTION
|
|
102
|
+
</text>
|
|
103
|
+
|
|
104
|
+
<rect x="350" y="72%" width="500" height="2" fill="rgba(255,255,255,0.1)" />
|
|
105
|
+
<rect x="450" y="72%" width="300" height="2" fill="#0ea5e9" filter="url(#glow)" />
|
|
106
|
+
</svg>
|
package/index.js
CHANGED
|
@@ -4,14 +4,18 @@
|
|
|
4
4
|
* @description Entry point runtime groundtruth delegazione CLI o proxy flow logic.
|
|
5
5
|
*/
|
|
6
6
|
import { chalk, label } from './src/logger.js';
|
|
7
|
-
import { usePackageJson, antigravityMode, claudeCodeMode, port, intervalMinutes, batchSize, version } from './src/cli.js';
|
|
7
|
+
import { usePackageJson, antigravityMode, claudeCodeMode, uninstallMode, port, intervalMinutes, batchSize, version } from './src/cli.js';
|
|
8
8
|
import { createServer } from './src/proxy.js';
|
|
9
|
-
import { autoSetEnv } from './src/env.js';
|
|
9
|
+
import { autoSetEnv, removeEnv } from './src/env.js';
|
|
10
10
|
import { startWatcher } from './src/watcher.js';
|
|
11
11
|
|
|
12
12
|
// ─── Dispatcher start app logic ──────────────────────
|
|
13
13
|
|
|
14
|
-
if (
|
|
14
|
+
if (uninstallMode) {
|
|
15
|
+
console.log(`\n ${chalk.white.bold('GroundTruth')} ${chalk.gray(`v${version}`)} ${chalk.gray('[uninstall]')}\n`);
|
|
16
|
+
await removeEnv();
|
|
17
|
+
process.exit(0);
|
|
18
|
+
} else if (antigravityMode) {
|
|
15
19
|
startWatcher({ intervalMinutes, usePackageJson, batchSize });
|
|
16
20
|
} else if (claudeCodeMode) {
|
|
17
21
|
const server = await createServer(usePackageJson);
|
|
@@ -22,7 +26,6 @@ if (antigravityMode) {
|
|
|
22
26
|
console.log(`\n ${chalk.white.bold('GroundTruth')} ${chalk.gray(`v${version}`)} ${chalk.gray('[claude-code mode]')}\n`);
|
|
23
27
|
console.log(label('◆', 'proxy', `localhost:${p}`));
|
|
24
28
|
console.log(label('◆', 'anthropic', '/v1/messages'));
|
|
25
|
-
console.log(label('◆', 'gemini', '/v1beta/…'));
|
|
26
29
|
console.log(label('◆', 'context', 'DuckDuckGo → live'));
|
|
27
30
|
console.log(`\n ${chalk.cyan('✻')} Listening. Set ANTHROPIC_BASE_URL=http://localhost:${p}\n`);
|
|
28
31
|
await autoSetEnv(p);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@antodevs/groundtruth",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "Lightweight Node.js proxy to intercept API requests from coding agents and inject fresh web context",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -36,7 +36,9 @@
|
|
|
36
36
|
},
|
|
37
37
|
"scripts": {
|
|
38
38
|
"start": "node index.js",
|
|
39
|
-
"dev": "node --watch index.js"
|
|
39
|
+
"dev": "node --watch index.js",
|
|
40
|
+
"release": "git add . && git commit -m \"chore: auto-commit before release\" || true && npm version patch && git push origin main --tags && npm publish",
|
|
41
|
+
"release:minor": "git add . && git commit -m \"chore: auto-commit before release\" || true && npm version minor && git push origin main --tags && npm publish"
|
|
40
42
|
},
|
|
41
43
|
"dependencies": {
|
|
42
44
|
"@mozilla/readability": "^0.5.0",
|
package/specification.yaml
CHANGED
package/src/circuit-breaker.js
CHANGED
|
@@ -35,7 +35,7 @@ export class CircuitBreaker {
|
|
|
35
35
|
this.onSuccess();
|
|
36
36
|
return result;
|
|
37
37
|
} catch (err) {
|
|
38
|
-
this.onFailure();
|
|
38
|
+
this.onFailure(err);
|
|
39
39
|
throw err;
|
|
40
40
|
}
|
|
41
41
|
}
|
|
@@ -53,8 +53,13 @@ export class CircuitBreaker {
|
|
|
53
53
|
}
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
onFailure() {
|
|
57
|
-
|
|
56
|
+
onFailure(err) {
|
|
57
|
+
// 429 rate limit apre il circuito immediatamente
|
|
58
|
+
if (err?.message?.includes('429')) {
|
|
59
|
+
this.failures = this.failureThreshold;
|
|
60
|
+
} else {
|
|
61
|
+
this.failures++;
|
|
62
|
+
}
|
|
58
63
|
this.lastFailureTime = Date.now();
|
|
59
64
|
if (this.failures >= this.failureThreshold) {
|
|
60
65
|
this.state = 'OPEN';
|
package/src/cli.js
CHANGED
|
@@ -13,15 +13,17 @@ const args = process.argv.slice(2);
|
|
|
13
13
|
const usePackageJson = args.includes('--use-package-json');
|
|
14
14
|
const antigravityMode = args.includes('--antigravity');
|
|
15
15
|
const claudeCodeMode = args.includes('--claude-code');
|
|
16
|
+
const uninstallMode = args.includes('--uninstall');
|
|
16
17
|
|
|
17
18
|
// Stop immediato se nessun mode definito
|
|
18
|
-
if (!antigravityMode && !claudeCodeMode) {
|
|
19
|
+
if (!antigravityMode && !claudeCodeMode && !uninstallMode) {
|
|
19
20
|
console.log();
|
|
20
21
|
console.log(` ${chalk.white.bold('GroundTruth')} ${chalk.gray(`v${version}`)}`);
|
|
21
22
|
console.log();
|
|
22
23
|
console.log(` Usage:`);
|
|
23
24
|
console.log(` groundtruth --claude-code proxy mode (Claude Code)`);
|
|
24
25
|
console.log(` groundtruth --antigravity rules mode (Antigravity/Gemini)`);
|
|
26
|
+
console.log(` groundtruth --uninstall remove shell env config`);
|
|
25
27
|
console.log();
|
|
26
28
|
console.log(` Options:`);
|
|
27
29
|
console.log(` --use-package-json use package.json as search query`);
|
|
@@ -55,4 +57,4 @@ const batchSize = batchSizeIndex !== -1
|
|
|
55
57
|
? Math.max(2, Math.min(parseInt(args[batchSizeIndex + 1]) || 3, 5))
|
|
56
58
|
: 3;
|
|
57
59
|
|
|
58
|
-
export { args, usePackageJson, antigravityMode, claudeCodeMode, port, intervalMinutes, batchSize, version };
|
|
60
|
+
export { args, usePackageJson, antigravityMode, claudeCodeMode, uninstallMode, port, intervalMinutes, batchSize, version };
|
package/src/env.js
CHANGED
|
@@ -118,3 +118,43 @@ export async function autoSetEnv(p) {
|
|
|
118
118
|
log(LOG_WARN, chalk.yellow, chalk.white('env setup error') + ` → ${chalk.yellow(err.message)}`);
|
|
119
119
|
}
|
|
120
120
|
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* @description Rimuove ANTHROPIC_BASE_URL da tutti i file di configurazione shell.
|
|
124
|
+
* @returns {Promise<void>}
|
|
125
|
+
*/
|
|
126
|
+
export async function removeEnv() {
|
|
127
|
+
const homeDir = os.homedir();
|
|
128
|
+
const targets = [
|
|
129
|
+
{ file: path.join(homeDir, '.zshrc'), pattern: /^export ANTHROPIC_BASE_URL=.*\n?/gm },
|
|
130
|
+
{ file: path.join(homeDir, '.bashrc'), pattern: /^export ANTHROPIC_BASE_URL=.*\n?/gm },
|
|
131
|
+
{ file: path.join(homeDir, '.bash_profile'), pattern: /^export ANTHROPIC_BASE_URL=.*\n?/gm },
|
|
132
|
+
{ file: path.join(homeDir, '.profile'), pattern: /^export ANTHROPIC_BASE_URL=.*\n?/gm },
|
|
133
|
+
{ file: path.join(homeDir, '.config', 'fish', 'config.fish'), pattern: /^set -gx ANTHROPIC_BASE_URL .*\n?/gm },
|
|
134
|
+
];
|
|
135
|
+
|
|
136
|
+
let cleaned = 0;
|
|
137
|
+
for (const t of targets) {
|
|
138
|
+
if (!existsSync(t.file)) continue;
|
|
139
|
+
try {
|
|
140
|
+
const content = await fs.readFile(t.file, 'utf8');
|
|
141
|
+
const result = content.replace(t.pattern, '').replace(/\n{3,}/g, '\n\n');
|
|
142
|
+
if (result !== content) {
|
|
143
|
+
await atomicWrite(t.file, result);
|
|
144
|
+
const rel = t.file.replace(homeDir, '~');
|
|
145
|
+
log(LOG_OK, chalk.green, chalk.white('removed ANTHROPIC_BASE_URL from') + ' ' + chalk.white(rel));
|
|
146
|
+
cleaned++;
|
|
147
|
+
}
|
|
148
|
+
} catch (e) {
|
|
149
|
+
log(LOG_WARN, chalk.yellow, chalk.white(`cannot clean ${path.basename(t.file)}`) + ` → ${chalk.yellow(e.message)}`);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (cleaned === 0) {
|
|
154
|
+
log(LOG_WARN, chalk.yellow, chalk.white('nothing to clean') + ` → ${chalk.yellow('no ANTHROPIC_BASE_URL found in shell configs')}`);
|
|
155
|
+
} else {
|
|
156
|
+
log(LOG_OK, chalk.green, chalk.white(`cleaned ${cleaned} file(s)`));
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
delete process.env.ANTHROPIC_BASE_URL;
|
|
160
|
+
}
|
package/src/inject.js
CHANGED
|
@@ -33,6 +33,7 @@ export async function injectBlock(filePath, content, blockId) {
|
|
|
33
33
|
if (startIndex !== -1 && endIndex !== -1 && endIndex > startIndex) {
|
|
34
34
|
fileContent = fileContent.slice(0, startIndex) + block + fileContent.slice(endIndex + endTag.length);
|
|
35
35
|
} else {
|
|
36
|
+
fileContent = fileContent.trimEnd() + '\n\n' + block + '\n';
|
|
36
37
|
}
|
|
37
38
|
|
|
38
39
|
await atomicWrite(filePath, fileContent);
|
package/src/packages.js
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
import fs from 'fs/promises';
|
|
6
6
|
import path from 'path';
|
|
7
7
|
import { createHash } from 'crypto';
|
|
8
|
+
import { chalk, log, LOG_WARN } from './logger.js';
|
|
8
9
|
|
|
9
10
|
// ─── Logica Dipendenze ───────────────────────────────
|
|
10
11
|
|
|
@@ -41,7 +42,8 @@ export async function readPackageDeps() {
|
|
|
41
42
|
selected = selected.concat(filterAndFormat(pkg.devDependencies));
|
|
42
43
|
|
|
43
44
|
return selected.length > 0 ? selected : null;
|
|
44
|
-
} catch (
|
|
45
|
+
} catch (err) {
|
|
46
|
+
log(LOG_WARN, chalk.yellow, chalk.white('package.json parse error') + ` → ${chalk.yellow(err.message)}`);
|
|
45
47
|
return null;
|
|
46
48
|
}
|
|
47
49
|
}
|
package/src/proxy.js
CHANGED
|
@@ -8,6 +8,7 @@ import { webSearch } from './search.js';
|
|
|
8
8
|
import { readPackageDeps, buildQuery } from './packages.js';
|
|
9
9
|
import { chalk, log, LOG_WARN, LOG_BOLT } from './logger.js';
|
|
10
10
|
import { httpsAgent } from './http-agent.js';
|
|
11
|
+
import { sanitizeWebContent } from './sanitize.js';
|
|
11
12
|
|
|
12
13
|
// ─── HTTP Node server daemon ─────────────────────────
|
|
13
14
|
|
|
@@ -98,9 +99,9 @@ export async function createServer(usePackageJson) {
|
|
|
98
99
|
|
|
99
100
|
contextBlock = `\n\n--- WEB CONTEXT (live, ${new Date().toISOString()}) ---\n`;
|
|
100
101
|
results.forEach((r, i) => {
|
|
101
|
-
contextBlock += `${i + 1}. ${r.title}: ${r.snippet} (${r.url})\n`;
|
|
102
|
+
contextBlock += `${i + 1}. ${r.title}: ${sanitizeWebContent(r.snippet, 500)} (${r.url})\n`;
|
|
102
103
|
});
|
|
103
|
-
if (pageText) contextBlock += `\nFULL TEXT:\n${pageText}\n`;
|
|
104
|
+
if (pageText) contextBlock += `\nFULL TEXT:\n${sanitizeWebContent(pageText)}\n`;
|
|
104
105
|
contextBlock += `--- END WEB CONTEXT ---\n`;
|
|
105
106
|
didInject = true;
|
|
106
107
|
} catch (_) {
|
package/src/sanitize.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module sanitize
|
|
3
|
+
* @description Sanitizzazione contenuto web contro prompt injection attacks.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// Pattern noti di prompt injection che devono essere filtrati
|
|
7
|
+
const DANGEROUS_PATTERNS = [
|
|
8
|
+
/ignore\s+(all\s+)?previous\s+instructions?/gi,
|
|
9
|
+
/disregard\s+(all\s+)?previous/gi,
|
|
10
|
+
/you\s+are\s+now\s+/gi,
|
|
11
|
+
/forget\s+(all\s+)?(your\s+)?instructions?/gi,
|
|
12
|
+
/new\s+instructions?\s*:/gi,
|
|
13
|
+
/system\s*prompt\s*:/gi,
|
|
14
|
+
/\[INST\]/gi,
|
|
15
|
+
/<\|im_start\|>/gi,
|
|
16
|
+
/<\|im_end\|>/gi,
|
|
17
|
+
/```system/gi,
|
|
18
|
+
/ASSISTANT:\s/gi,
|
|
19
|
+
/HUMAN:\s/gi,
|
|
20
|
+
];
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @description Filtra pattern pericolosi di prompt injection dal testo web scrappato.
|
|
24
|
+
* @param {string} text - Testo raw proveniente da web scraping
|
|
25
|
+
* @param {number} maxLen - Lunghezza massima output (default 8000)
|
|
26
|
+
* @returns {string} Testo sanitizzato
|
|
27
|
+
*/
|
|
28
|
+
export function sanitizeWebContent(text, maxLen = 8000) {
|
|
29
|
+
if (!text || typeof text !== 'string') return '';
|
|
30
|
+
let cleaned = text;
|
|
31
|
+
for (const p of DANGEROUS_PATTERNS) {
|
|
32
|
+
cleaned = cleaned.replace(p, '[FILTERED]');
|
|
33
|
+
}
|
|
34
|
+
return cleaned.slice(0, maxLen);
|
|
35
|
+
}
|
package/src/search.js
CHANGED
|
@@ -9,6 +9,7 @@ import { DOMParser } from 'linkedom';
|
|
|
9
9
|
import { searchCache } from './cache.js';
|
|
10
10
|
import { CircuitBreaker } from './circuit-breaker.js';
|
|
11
11
|
import { httpAgent, httpsAgent } from './http-agent.js';
|
|
12
|
+
import { sanitizeWebContent } from './sanitize.js';
|
|
12
13
|
|
|
13
14
|
// ─── Config & Cache ──────────────────────────────────
|
|
14
15
|
|
|
@@ -118,7 +119,7 @@ export async function webSearch(query, parallel = false) {
|
|
|
118
119
|
} catch (_) {
|
|
119
120
|
text = document.body?.textContent || '';
|
|
120
121
|
}
|
|
121
|
-
if (text) return text.replace(/\s+/g, ' ')
|
|
122
|
+
if (text) return sanitizeWebContent(text.replace(/\s+/g, ' '), 4000);
|
|
122
123
|
}
|
|
123
124
|
} catch (_) { // fail silenzioso parallelo tollerato per timeout link third-party
|
|
124
125
|
}
|
|
@@ -143,7 +144,7 @@ export async function webSearch(query, parallel = false) {
|
|
|
143
144
|
text = document.body?.textContent || '';
|
|
144
145
|
}
|
|
145
146
|
if (text) {
|
|
146
|
-
pageText = text.replace(/\s+/g, ' ')
|
|
147
|
+
pageText = sanitizeWebContent(text.replace(/\s+/g, ' '), 4000);
|
|
147
148
|
}
|
|
148
149
|
}
|
|
149
150
|
}
|
package/src/state.js
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
* @module state
|
|
3
3
|
* @description Persiste la memoria di antigravity prev-hash per fault tolleranza riavvii.
|
|
4
4
|
*/
|
|
5
|
-
import { readFile,
|
|
5
|
+
import { readFile, mkdir } from 'fs/promises';
|
|
6
|
+
import { atomicWrite } from './utils/atomic-write.js';
|
|
6
7
|
import { existsSync } from 'fs';
|
|
7
8
|
import path from 'path';
|
|
8
9
|
import os from 'os';
|
|
@@ -33,5 +34,5 @@ export async function loadBatchState() {
|
|
|
33
34
|
export async function saveBatchState(map) {
|
|
34
35
|
await mkdir(STATE_DIR, { recursive: true });
|
|
35
36
|
const obj = Object.fromEntries(map);
|
|
36
|
-
await
|
|
37
|
+
await atomicWrite(STATE_FILE, JSON.stringify(obj, null, 2), { backup: false });
|
|
37
38
|
}
|
package/src/watcher.js
CHANGED
|
@@ -6,6 +6,7 @@ import os from 'os';
|
|
|
6
6
|
import path from 'path';
|
|
7
7
|
import { webSearch } from './search.js';
|
|
8
8
|
import { readPackageDeps, buildQuery, groupIntoBatches, batchHash } from './packages.js';
|
|
9
|
+
import { sanitizeWebContent } from './sanitize.js';
|
|
9
10
|
import { updateGeminiFiles, removeStaleBlocks } from './inject.js';
|
|
10
11
|
import { chalk, label, log, LOG_WARN, LOG_REFRESH } from './logger.js';
|
|
11
12
|
import { version } from './cli.js';
|
|
@@ -84,16 +85,16 @@ export function startWatcher({ intervalMinutes, usePackageJson, batchSize }) {
|
|
|
84
85
|
globalMd += `**Query:** ${query}\n\n`;
|
|
85
86
|
if (results.length > 0) {
|
|
86
87
|
globalMd += `### ${results[0].title}\n`;
|
|
87
|
-
globalMd += `${results[0].snippet
|
|
88
|
+
globalMd += `${sanitizeWebContent(results[0].snippet, 300)} — ${results[0].url}\n`;
|
|
88
89
|
}
|
|
89
90
|
|
|
90
91
|
let md = `## Live Context — ${batchTitle} (${nowStr})\n`;
|
|
91
92
|
md += `**Query:** ${query}\n\n`;
|
|
92
93
|
for (const r of results) {
|
|
93
|
-
md += `### ${r.title}\n${r.snippet} — ${r.url}\n\n`;
|
|
94
|
+
md += `### ${r.title}\n${sanitizeWebContent(r.snippet, 500)} — ${r.url}\n\n`;
|
|
94
95
|
}
|
|
95
96
|
if (pageText) {
|
|
96
|
-
md += `FULL TEXT: ${pageText}\n`;
|
|
97
|
+
md += `FULL TEXT: ${sanitizeWebContent(pageText)}\n`;
|
|
97
98
|
}
|
|
98
99
|
|
|
99
100
|
await updateGeminiFiles([{
|
package/assets/banner.png
DELETED
|
Binary file
|