@troykelly/openclaw-projects 0.0.12 → 0.0.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.
- package/dist/config.d.ts +8 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +5 -0
- package/dist/config.js.map +1 -1
- package/dist/hooks.d.ts.map +1 -1
- package/dist/hooks.js +7 -2
- package/dist/hooks.js.map +1 -1
- package/dist/register-openclaw.d.ts.map +1 -1
- package/dist/register-openclaw.js +57 -28
- package/dist/register-openclaw.js.map +1 -1
- package/dist/tools/context-search.d.ts +2 -2
- package/dist/tools/context-search.d.ts.map +1 -1
- package/dist/tools/context-search.js +7 -5
- package/dist/tools/context-search.js.map +1 -1
- package/dist/tools/entity-links.d.ts +12 -12
- package/dist/tools/message-search.d.ts +1 -1
- package/dist/tools/message-search.d.ts.map +1 -1
- package/dist/tools/message-search.js +26 -11
- package/dist/tools/message-search.js.map +1 -1
- package/dist/tools/skill-store.d.ts +8 -8
- package/dist/tools/threads.d.ts +1 -1
- package/dist/tools/threads.d.ts.map +1 -1
- package/dist/tools/threads.js +27 -13
- package/dist/tools/threads.js.map +1 -1
- package/dist/tools/todo-search.d.ts +2 -2
- package/dist/utils/injection-log-rate-limiter.d.ts +62 -0
- package/dist/utils/injection-log-rate-limiter.d.ts.map +1 -0
- package/dist/utils/injection-log-rate-limiter.js +106 -0
- package/dist/utils/injection-log-rate-limiter.js.map +1 -0
- package/dist/utils/injection-protection.d.ts +55 -3
- package/dist/utils/injection-protection.d.ts.map +1 -1
- package/dist/utils/injection-protection.js +100 -27
- package/dist/utils/injection-protection.js.map +1 -1
- package/dist/utils/prompt-guard-client.d.ts +59 -0
- package/dist/utils/prompt-guard-client.d.ts.map +1 -0
- package/dist/utils/prompt-guard-client.js +99 -0
- package/dist/utils/prompt-guard-client.js.map +1 -0
- package/openclaw.plugin.json +23 -1
- package/package.json +12 -11
- package/LICENSE +0 -21
|
@@ -3,14 +3,36 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Provides layered defence for external message content before LLM exposure:
|
|
5
5
|
* 1. Unicode/control character sanitisation
|
|
6
|
-
* 2. Data boundary marking (spotlighting)
|
|
6
|
+
* 2. Data boundary marking (spotlighting) with per-session nonce
|
|
7
7
|
* 3. Suspicious pattern detection and logging
|
|
8
|
+
* 4. PromptGuard-2 classifier integration (optional, async)
|
|
8
9
|
*
|
|
9
|
-
* Issue #1224
|
|
10
|
+
* Issue #1224, #1255, #1256
|
|
10
11
|
*/
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
import { randomBytes } from 'node:crypto';
|
|
13
|
+
import { classifyText } from './prompt-guard-client.js';
|
|
14
|
+
/** Regex to validate nonce format: 1-32 lowercase hex characters */
|
|
15
|
+
const VALID_NONCE_RE = /^[0-9a-f]{1,32}$/;
|
|
16
|
+
/**
|
|
17
|
+
* Create boundary markers with a per-session cryptographic nonce.
|
|
18
|
+
*
|
|
19
|
+
* If no nonce is provided, one is generated from 4 random bytes (8 hex chars).
|
|
20
|
+
* Callers should create markers once per session/hook invocation and reuse
|
|
21
|
+
* them for all messages in that session, rather than generating per-message.
|
|
22
|
+
*
|
|
23
|
+
* @throws {Error} If a provided nonce contains non-hex characters.
|
|
24
|
+
*/
|
|
25
|
+
export function createBoundaryMarkers(nonce) {
|
|
26
|
+
const n = nonce ?? randomBytes(4).toString('hex');
|
|
27
|
+
if (!VALID_NONCE_RE.test(n)) {
|
|
28
|
+
throw new Error('Boundary marker nonce must be 1-32 lowercase hex characters');
|
|
29
|
+
}
|
|
30
|
+
return {
|
|
31
|
+
start: `[EXTERNAL_MSG_${n}_START]`,
|
|
32
|
+
end: `[EXTERNAL_MSG_${n}_END]`,
|
|
33
|
+
nonce: n,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
14
36
|
/**
|
|
15
37
|
* Regex matching control characters to strip (ASCII 0x00-0x08, 0x0B, 0x0C, 0x0E-0x1F, 0x7F).
|
|
16
38
|
* Preserves tab (0x09), newline (0x0A), and carriage return (0x0D).
|
|
@@ -86,29 +108,35 @@ const INJECTION_PATTERNS = [
|
|
|
86
108
|
* characters (tab, newline, carriage return).
|
|
87
109
|
*/
|
|
88
110
|
export function sanitizeExternalMessage(text) {
|
|
89
|
-
return text
|
|
90
|
-
.replace(CONTROL_CHARS_REGEX, '')
|
|
91
|
-
.replace(UNICODE_INVISIBLE_REGEX, '')
|
|
92
|
-
.trim();
|
|
111
|
+
return text.replace(CONTROL_CHARS_REGEX, '').replace(UNICODE_INVISIBLE_REGEX, '').trim();
|
|
93
112
|
}
|
|
94
113
|
/**
|
|
95
114
|
* Escape boundary marker keywords in a string to prevent breakout attacks.
|
|
96
|
-
*
|
|
97
|
-
*
|
|
98
|
-
*
|
|
115
|
+
*
|
|
116
|
+
* When a nonce is provided, escapes markers containing that specific nonce.
|
|
117
|
+
* Also escapes the generic `EXTERNAL_MSG_START` / `EXTERNAL_MSG_END` keywords
|
|
118
|
+
* (without nonce) so that attackers cannot inject markers from the public source.
|
|
99
119
|
*/
|
|
100
|
-
function escapeBoundaryMarkers(text) {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
120
|
+
function escapeBoundaryMarkers(text, nonce) {
|
|
121
|
+
let result = text;
|
|
122
|
+
if (nonce) {
|
|
123
|
+
// Escape nonce-specific markers first (more specific match)
|
|
124
|
+
result = result
|
|
125
|
+
.replace(new RegExp(`EXTERNAL_MSG_${nonce}_START`, 'g'), `EXTERNAL_MSG_${nonce}_START_ESCAPED`)
|
|
126
|
+
.replace(new RegExp(`EXTERNAL_MSG_${nonce}_END`, 'g'), `EXTERNAL_MSG_${nonce}_END_ESCAPED`);
|
|
127
|
+
}
|
|
128
|
+
// Always escape the generic (non-nonce) marker keywords to prevent
|
|
129
|
+
// attackers from using the old hardcoded pattern
|
|
130
|
+
result = result.replace(/EXTERNAL_MSG_START/g, 'EXTERNAL_MSG_START_ESCAPED').replace(/EXTERNAL_MSG_END/g, 'EXTERNAL_MSG_END_ESCAPED');
|
|
131
|
+
return result;
|
|
104
132
|
}
|
|
105
133
|
/**
|
|
106
134
|
* Sanitize a metadata field (sender, channel) for safe insertion into
|
|
107
135
|
* the boundary wrapper header. Strips control chars, invisible Unicode,
|
|
108
136
|
* newlines (which could break out of the header line), and boundary markers.
|
|
109
137
|
*/
|
|
110
|
-
export function sanitizeMetadataField(field) {
|
|
111
|
-
return escapeBoundaryMarkers(sanitizeExternalMessage(field).replace(/[\r\n]/g, ' '));
|
|
138
|
+
export function sanitizeMetadataField(field, nonce) {
|
|
139
|
+
return escapeBoundaryMarkers(sanitizeExternalMessage(field).replace(/[\r\n]/g, ' '), nonce);
|
|
112
140
|
}
|
|
113
141
|
/**
|
|
114
142
|
* Wrap external message content with data boundary markers.
|
|
@@ -119,22 +147,24 @@ export function sanitizeMetadataField(field) {
|
|
|
119
147
|
*
|
|
120
148
|
* Content, sender, and channel are all sanitized and have boundary markers
|
|
121
149
|
* escaped before wrapping to prevent breakout attacks.
|
|
150
|
+
*
|
|
151
|
+
* When `options.nonce` is provided, markers use that nonce. Otherwise a
|
|
152
|
+
* fresh cryptographic nonce is generated per call.
|
|
122
153
|
*/
|
|
123
154
|
export function wrapExternalMessage(content, options = {}) {
|
|
155
|
+
const markers = createBoundaryMarkers(options.nonce);
|
|
124
156
|
const sanitized = sanitizeExternalMessage(content);
|
|
125
157
|
// Escape any existing boundary markers in the content to prevent breakout
|
|
126
|
-
const escaped = escapeBoundaryMarkers(sanitized);
|
|
158
|
+
const escaped = escapeBoundaryMarkers(sanitized, markers.nonce);
|
|
127
159
|
const attribution = [];
|
|
128
160
|
if (options.channel) {
|
|
129
|
-
attribution.push(`[${sanitizeMetadataField(options.channel)}]`);
|
|
161
|
+
attribution.push(`[${sanitizeMetadataField(options.channel, markers.nonce)}]`);
|
|
130
162
|
}
|
|
131
163
|
if (options.sender) {
|
|
132
|
-
attribution.push(`from: ${sanitizeMetadataField(options.sender)}`);
|
|
164
|
+
attribution.push(`from: ${sanitizeMetadataField(options.sender, markers.nonce)}`);
|
|
133
165
|
}
|
|
134
|
-
const header = attribution.length > 0
|
|
135
|
-
|
|
136
|
-
: EXTERNAL_MSG_START;
|
|
137
|
-
return `${header}\n${escaped}\n${EXTERNAL_MSG_END}`;
|
|
166
|
+
const header = attribution.length > 0 ? `${markers.start} ${attribution.join(' ')}` : markers.start;
|
|
167
|
+
return `${header}\n${escaped}\n${markers.end}`;
|
|
138
168
|
}
|
|
139
169
|
/**
|
|
140
170
|
* Detect suspicious prompt injection patterns in message content.
|
|
@@ -160,6 +190,49 @@ export function detectInjectionPatterns(text) {
|
|
|
160
190
|
patterns: matched,
|
|
161
191
|
};
|
|
162
192
|
}
|
|
193
|
+
/**
|
|
194
|
+
* Detect suspicious prompt injection patterns using both regex and
|
|
195
|
+
* (optionally) the PromptGuard-2 classifier.
|
|
196
|
+
*
|
|
197
|
+
* When `promptGuardUrl` is configured, the classifier is called in parallel
|
|
198
|
+
* with regex scanning. If the classifier is unavailable or times out,
|
|
199
|
+
* the function gracefully falls back to regex-only results.
|
|
200
|
+
*
|
|
201
|
+
* Detection remains monitoring-only (no blocking).
|
|
202
|
+
*
|
|
203
|
+
* Issue #1256
|
|
204
|
+
*/
|
|
205
|
+
export async function detectInjectionPatternsAsync(text, options = {}) {
|
|
206
|
+
// Always run regex detection (fast, synchronous)
|
|
207
|
+
const regexResult = detectInjectionPatterns(text);
|
|
208
|
+
// If no classifier URL configured, return regex-only result
|
|
209
|
+
if (!options.promptGuardUrl) {
|
|
210
|
+
return { ...regexResult, source: 'regex' };
|
|
211
|
+
}
|
|
212
|
+
// Call classifier in parallel (with timeout)
|
|
213
|
+
const classifierResult = await classifyText(options.promptGuardUrl, text, options.classifierTimeoutMs ?? 500);
|
|
214
|
+
// Classifier unavailable — fall back to regex
|
|
215
|
+
if (!classifierResult) {
|
|
216
|
+
return { ...regexResult, source: 'regex' };
|
|
217
|
+
}
|
|
218
|
+
// Merge results: detected if either regex or classifier flags it
|
|
219
|
+
const classifierDetected = classifierResult.injection || classifierResult.jailbreak;
|
|
220
|
+
const patterns = [...regexResult.patterns];
|
|
221
|
+
if (classifierResult.injection) {
|
|
222
|
+
patterns.push('classifier:injection');
|
|
223
|
+
}
|
|
224
|
+
if (classifierResult.jailbreak) {
|
|
225
|
+
patterns.push('classifier:jailbreak');
|
|
226
|
+
}
|
|
227
|
+
const detected = regexResult.detected || classifierDetected;
|
|
228
|
+
const source = regexResult.detected && classifierDetected ? 'both' : classifierDetected ? 'classifier' : 'regex';
|
|
229
|
+
return {
|
|
230
|
+
detected,
|
|
231
|
+
patterns,
|
|
232
|
+
classifier: classifierResult,
|
|
233
|
+
source,
|
|
234
|
+
};
|
|
235
|
+
}
|
|
163
236
|
/**
|
|
164
237
|
* Sanitize message content for safe inclusion in LLM context.
|
|
165
238
|
*
|
|
@@ -170,10 +243,10 @@ export function detectInjectionPatterns(text) {
|
|
|
170
243
|
* for any LLM-facing output (auto-recall context, tool results, etc.).
|
|
171
244
|
*/
|
|
172
245
|
export function sanitizeMessageForContext(content, options = {}) {
|
|
173
|
-
const { direction = 'inbound', channel, sender } = options;
|
|
246
|
+
const { direction = 'inbound', channel, sender, nonce } = options;
|
|
174
247
|
if (direction === 'outbound') {
|
|
175
248
|
return sanitizeExternalMessage(content);
|
|
176
249
|
}
|
|
177
|
-
return wrapExternalMessage(content, { channel, sender });
|
|
250
|
+
return wrapExternalMessage(content, { channel, sender, nonce });
|
|
178
251
|
}
|
|
179
252
|
//# sourceMappingURL=injection-protection.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"injection-protection.js","sourceRoot":"","sources":["../../src/utils/injection-protection.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"injection-protection.js","sourceRoot":"","sources":["../../src/utils/injection-protection.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client for the PromptGuard-2 classifier service.
|
|
3
|
+
*
|
|
4
|
+
* Provides async HTTP-based classification of text for prompt injection
|
|
5
|
+
* and jailbreak detection using Meta's PromptGuard-2 model.
|
|
6
|
+
*
|
|
7
|
+
* Falls back gracefully when the service is unavailable.
|
|
8
|
+
*
|
|
9
|
+
* Issue #1256
|
|
10
|
+
*/
|
|
11
|
+
/** Classification result from the PromptGuard service */
|
|
12
|
+
export interface PromptGuardResult {
|
|
13
|
+
/** Whether injection was detected */
|
|
14
|
+
injection: boolean;
|
|
15
|
+
/** Whether jailbreak was detected */
|
|
16
|
+
jailbreak: boolean;
|
|
17
|
+
/** Top label: BENIGN, INJECTION, or JAILBREAK */
|
|
18
|
+
label: string;
|
|
19
|
+
/** Confidence scores per class */
|
|
20
|
+
scores: {
|
|
21
|
+
benign: number;
|
|
22
|
+
injection: number;
|
|
23
|
+
jailbreak: number;
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
/** Health check response from the PromptGuard service */
|
|
27
|
+
export interface PromptGuardHealth {
|
|
28
|
+
ok: boolean;
|
|
29
|
+
model: string;
|
|
30
|
+
ready: boolean;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Classify a single text for prompt injection / jailbreak.
|
|
34
|
+
*
|
|
35
|
+
* Returns `null` if the service is unavailable, not ready, or times out.
|
|
36
|
+
* Callers should fall back to regex detection when this returns null.
|
|
37
|
+
*
|
|
38
|
+
* @param baseUrl - The PromptGuard service base URL (e.g. http://localhost:8190)
|
|
39
|
+
* @param text - The text to classify
|
|
40
|
+
* @param timeoutMs - Request timeout in milliseconds (default 500ms)
|
|
41
|
+
*/
|
|
42
|
+
export declare function classifyText(baseUrl: string, text: string, timeoutMs?: number): Promise<PromptGuardResult | null>;
|
|
43
|
+
/**
|
|
44
|
+
* Classify multiple texts in a single batch request.
|
|
45
|
+
*
|
|
46
|
+
* Returns `null` if the service is unavailable, not ready, or times out.
|
|
47
|
+
*
|
|
48
|
+
* @param baseUrl - The PromptGuard service base URL
|
|
49
|
+
* @param texts - Array of texts to classify
|
|
50
|
+
* @param timeoutMs - Request timeout in milliseconds (default 500ms)
|
|
51
|
+
*/
|
|
52
|
+
export declare function classifyBatch(baseUrl: string, texts: string[], timeoutMs?: number): Promise<PromptGuardResult[] | null>;
|
|
53
|
+
/**
|
|
54
|
+
* Check the health of the PromptGuard service.
|
|
55
|
+
*
|
|
56
|
+
* Returns `null` if the service is unreachable.
|
|
57
|
+
*/
|
|
58
|
+
export declare function checkHealth(baseUrl: string, timeoutMs?: number): Promise<PromptGuardHealth | null>;
|
|
59
|
+
//# sourceMappingURL=prompt-guard-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompt-guard-client.d.ts","sourceRoot":"","sources":["../../src/utils/prompt-guard-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,yDAAyD;AACzD,MAAM,WAAW,iBAAiB;IAChC,qCAAqC;IACrC,SAAS,EAAE,OAAO,CAAC;IACnB,qCAAqC;IACrC,SAAS,EAAE,OAAO,CAAC;IACnB,iDAAiD;IACjD,KAAK,EAAE,MAAM,CAAC;IACd,kCAAkC;IAClC,MAAM,EAAE;QACN,MAAM,EAAE,MAAM,CAAC;QACf,SAAS,EAAE,MAAM,CAAC;QAClB,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;CACH;AAED,yDAAyD;AACzD,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,OAAO,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,OAAO,CAAC;CAChB;AAKD;;;;;;;;;GASG;AACH,wBAAsB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,GAAE,MAA2B,GAAG,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAuB3I;AAED;;;;;;;;GAQG;AACH,wBAAsB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,SAAS,GAAE,MAA2B,GAAG,OAAO,CAAC,iBAAiB,EAAE,GAAG,IAAI,CAAC,CAyBjJ;AAED;;;;GAIG;AACH,wBAAsB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,GAAE,MAA2B,GAAG,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAoB5H"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client for the PromptGuard-2 classifier service.
|
|
3
|
+
*
|
|
4
|
+
* Provides async HTTP-based classification of text for prompt injection
|
|
5
|
+
* and jailbreak detection using Meta's PromptGuard-2 model.
|
|
6
|
+
*
|
|
7
|
+
* Falls back gracefully when the service is unavailable.
|
|
8
|
+
*
|
|
9
|
+
* Issue #1256
|
|
10
|
+
*/
|
|
11
|
+
/** Default timeout for classifier requests (500ms) */
|
|
12
|
+
const DEFAULT_TIMEOUT_MS = 500;
|
|
13
|
+
/**
|
|
14
|
+
* Classify a single text for prompt injection / jailbreak.
|
|
15
|
+
*
|
|
16
|
+
* Returns `null` if the service is unavailable, not ready, or times out.
|
|
17
|
+
* Callers should fall back to regex detection when this returns null.
|
|
18
|
+
*
|
|
19
|
+
* @param baseUrl - The PromptGuard service base URL (e.g. http://localhost:8190)
|
|
20
|
+
* @param text - The text to classify
|
|
21
|
+
* @param timeoutMs - Request timeout in milliseconds (default 500ms)
|
|
22
|
+
*/
|
|
23
|
+
export async function classifyText(baseUrl, text, timeoutMs = DEFAULT_TIMEOUT_MS) {
|
|
24
|
+
try {
|
|
25
|
+
const controller = new AbortController();
|
|
26
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
27
|
+
const response = await fetch(`${baseUrl}/classify`, {
|
|
28
|
+
method: 'POST',
|
|
29
|
+
headers: { 'Content-Type': 'application/json' },
|
|
30
|
+
body: JSON.stringify({ text }),
|
|
31
|
+
signal: controller.signal,
|
|
32
|
+
});
|
|
33
|
+
clearTimeout(timer);
|
|
34
|
+
if (!response.ok) {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
return (await response.json());
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
// Network error, timeout, or abort — graceful degradation
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Classify multiple texts in a single batch request.
|
|
46
|
+
*
|
|
47
|
+
* Returns `null` if the service is unavailable, not ready, or times out.
|
|
48
|
+
*
|
|
49
|
+
* @param baseUrl - The PromptGuard service base URL
|
|
50
|
+
* @param texts - Array of texts to classify
|
|
51
|
+
* @param timeoutMs - Request timeout in milliseconds (default 500ms)
|
|
52
|
+
*/
|
|
53
|
+
export async function classifyBatch(baseUrl, texts, timeoutMs = DEFAULT_TIMEOUT_MS) {
|
|
54
|
+
if (texts.length === 0)
|
|
55
|
+
return [];
|
|
56
|
+
try {
|
|
57
|
+
const controller = new AbortController();
|
|
58
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
59
|
+
const response = await fetch(`${baseUrl}/classify/batch`, {
|
|
60
|
+
method: 'POST',
|
|
61
|
+
headers: { 'Content-Type': 'application/json' },
|
|
62
|
+
body: JSON.stringify({ texts }),
|
|
63
|
+
signal: controller.signal,
|
|
64
|
+
});
|
|
65
|
+
clearTimeout(timer);
|
|
66
|
+
if (!response.ok) {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
return (await response.json());
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
// Network error, timeout, or abort — graceful degradation
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Check the health of the PromptGuard service.
|
|
78
|
+
*
|
|
79
|
+
* Returns `null` if the service is unreachable.
|
|
80
|
+
*/
|
|
81
|
+
export async function checkHealth(baseUrl, timeoutMs = DEFAULT_TIMEOUT_MS) {
|
|
82
|
+
try {
|
|
83
|
+
const controller = new AbortController();
|
|
84
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
85
|
+
const response = await fetch(`${baseUrl}/health`, {
|
|
86
|
+
method: 'GET',
|
|
87
|
+
signal: controller.signal,
|
|
88
|
+
});
|
|
89
|
+
clearTimeout(timer);
|
|
90
|
+
if (!response.ok) {
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
return (await response.json());
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=prompt-guard-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompt-guard-client.js","sourceRoot":"","sources":["../../src/utils/prompt-guard-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAyBH,sDAAsD;AACtD,MAAM,kBAAkB,GAAG,GAAG,CAAC;AAE/B;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,OAAe,EAAE,IAAY,EAAE,YAAoB,kBAAkB;IACtG,IAAI,CAAC;QACH,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;QACzC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,SAAS,CAAC,CAAC;QAE9D,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,OAAO,WAAW,EAAE;YAClD,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;YAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,CAAC;YAC9B,MAAM,EAAE,UAAU,CAAC,MAAM;SAC1B,CAAC,CAAC;QAEH,YAAY,CAAC,KAAK,CAAC,CAAC;QAEpB,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,OAAO,IAAI,CAAC;QACd,CAAC;QAED,OAAO,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAsB,CAAC;IACtD,CAAC;IAAC,MAAM,CAAC;QACP,0DAA0D;QAC1D,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,OAAe,EAAE,KAAe,EAAE,YAAoB,kBAAkB;IAC1G,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAElC,IAAI,CAAC;QACH,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;QACzC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,SAAS,CAAC,CAAC;QAE9D,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,OAAO,iBAAiB,EAAE;YACxD,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;YAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,CAAC;YAC/B,MAAM,EAAE,UAAU,CAAC,MAAM;SAC1B,CAAC,CAAC;QAEH,YAAY,CAAC,KAAK,CAAC,CAAC;QAEpB,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,OAAO,IAAI,CAAC;QACd,CAAC;QAED,OAAO,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAwB,CAAC;IACxD,CAAC;IAAC,MAAM,CAAC;QACP,0DAA0D;QAC1D,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,OAAe,EAAE,YAAoB,kBAAkB;IACvF,IAAI,CAAC;QACH,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;QACzC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,SAAS,CAAC,CAAC;QAE9D,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,OAAO,SAAS,EAAE;YAChD,MAAM,EAAE,KAAK;YACb,MAAM,EAAE,UAAU,CAAC,MAAM;SAC1B,CAAC,CAAC;QAEH,YAAY,CAAC,KAAK,CAAC,CAAC;QAEpB,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,OAAO,IAAI,CAAC;QACd,CAAC;QAED,OAAO,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAsB,CAAC;IACtD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
|
package/openclaw.plugin.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "openclaw-projects",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.13",
|
|
4
4
|
"name": "OpenClaw Projects Plugin",
|
|
5
5
|
"description": "Memory provider with projects, todos, and contacts integration",
|
|
6
6
|
"kind": "memory",
|
|
@@ -145,6 +145,16 @@
|
|
|
145
145
|
"type": "string",
|
|
146
146
|
"pattern": "^https?://",
|
|
147
147
|
"description": "Base URL for web app (used for generating note/notebook URLs)"
|
|
148
|
+
},
|
|
149
|
+
"nominatimUrl": {
|
|
150
|
+
"type": "string",
|
|
151
|
+
"pattern": "^https?://",
|
|
152
|
+
"description": "Nominatim reverse geocoding URL (e.g., http://nominatim:8080)"
|
|
153
|
+
},
|
|
154
|
+
"promptGuardUrl": {
|
|
155
|
+
"type": "string",
|
|
156
|
+
"pattern": "^https?://",
|
|
157
|
+
"description": "PromptGuard-2 classifier URL for multilingual injection detection (e.g., http://prompt-guard:8190)"
|
|
148
158
|
}
|
|
149
159
|
},
|
|
150
160
|
"additionalProperties": false,
|
|
@@ -288,6 +298,18 @@
|
|
|
288
298
|
"placeholder": "https://app.example.com",
|
|
289
299
|
"help": "Base URL for the web app (used for generating links)",
|
|
290
300
|
"group": "Advanced"
|
|
301
|
+
},
|
|
302
|
+
"nominatimUrl": {
|
|
303
|
+
"label": "Nominatim URL",
|
|
304
|
+
"placeholder": "http://nominatim:8080",
|
|
305
|
+
"help": "URL for self-hosted Nominatim reverse geocoding service (enables geo-contextual search)",
|
|
306
|
+
"group": "Advanced"
|
|
307
|
+
},
|
|
308
|
+
"promptGuardUrl": {
|
|
309
|
+
"label": "PromptGuard URL",
|
|
310
|
+
"placeholder": "http://prompt-guard:8190",
|
|
311
|
+
"help": "URL for PromptGuard-2 classifier service (enables multilingual prompt injection detection)",
|
|
312
|
+
"group": "Advanced"
|
|
291
313
|
}
|
|
292
314
|
}
|
|
293
315
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@troykelly/openclaw-projects",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.13",
|
|
4
4
|
"description": "OpenClaw memory plugin with projects, todos, and contacts integration",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -20,6 +20,16 @@
|
|
|
20
20
|
"openclaw.plugin.json",
|
|
21
21
|
"skills"
|
|
22
22
|
],
|
|
23
|
+
"scripts": {
|
|
24
|
+
"build": "tsc",
|
|
25
|
+
"clean": "rm -rf dist",
|
|
26
|
+
"test": "vitest run",
|
|
27
|
+
"test:coverage": "vitest run --coverage",
|
|
28
|
+
"test:watch": "vitest",
|
|
29
|
+
"typecheck": "tsc --noEmit",
|
|
30
|
+
"lint": "biome lint .",
|
|
31
|
+
"prepublishOnly": "pnpm run clean && pnpm run build && pnpm run test && pnpm run typecheck && pnpm run lint"
|
|
32
|
+
},
|
|
23
33
|
"repository": {
|
|
24
34
|
"type": "git",
|
|
25
35
|
"url": "git+https://github.com/troykelly/openclaw-projects.git",
|
|
@@ -64,14 +74,5 @@
|
|
|
64
74
|
"extensions": [
|
|
65
75
|
"dist/register-openclaw.js"
|
|
66
76
|
]
|
|
67
|
-
},
|
|
68
|
-
"scripts": {
|
|
69
|
-
"build": "tsc",
|
|
70
|
-
"clean": "rm -rf dist",
|
|
71
|
-
"test": "vitest run",
|
|
72
|
-
"test:coverage": "vitest run --coverage",
|
|
73
|
-
"test:watch": "vitest",
|
|
74
|
-
"typecheck": "tsc --noEmit",
|
|
75
|
-
"lint": "biome lint ."
|
|
76
77
|
}
|
|
77
|
-
}
|
|
78
|
+
}
|
package/LICENSE
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2026 Troy Kelly
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|