openclaw-safeclaw-plugin 0.9.0 → 0.9.2
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/cli.tsx +82 -4
- package/dist/cli.js +90 -4
- package/package.json +1 -1
package/cli.tsx
CHANGED
|
@@ -134,7 +134,37 @@ if (command === 'connect') {
|
|
|
134
134
|
writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n');
|
|
135
135
|
chmodSync(configPath, 0o600);
|
|
136
136
|
|
|
137
|
-
console.log(`
|
|
137
|
+
console.log(`API key saved to ${configPath}`);
|
|
138
|
+
|
|
139
|
+
// Validate key via handshake
|
|
140
|
+
try {
|
|
141
|
+
const res = await fetch(`${serviceUrl}/handshake`, {
|
|
142
|
+
method: 'POST',
|
|
143
|
+
headers: {
|
|
144
|
+
'Content-Type': 'application/json',
|
|
145
|
+
'Authorization': `Bearer ${apiKey}`,
|
|
146
|
+
},
|
|
147
|
+
body: JSON.stringify({ pluginVersion: '0.1.3', configHash: '' }),
|
|
148
|
+
signal: AbortSignal.timeout(5000),
|
|
149
|
+
});
|
|
150
|
+
if (res.ok) {
|
|
151
|
+
const data = await res.json() as Record<string, unknown>;
|
|
152
|
+
console.log(`Connected! org=${data.orgId}, scope=${data.scope}`);
|
|
153
|
+
} else {
|
|
154
|
+
let detail = `HTTP ${res.status}`;
|
|
155
|
+
try {
|
|
156
|
+
const body = await res.json() as Record<string, unknown>;
|
|
157
|
+
detail = (body.error ?? body.detail ?? detail) as string;
|
|
158
|
+
} catch { /* ignore */ }
|
|
159
|
+
console.warn(`Warning: API key saved but handshake failed — ${detail}`);
|
|
160
|
+
if (res.status === 401) {
|
|
161
|
+
console.warn('The key may be invalid or revoked. Check https://safeclaw.eu/dashboard');
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
} catch {
|
|
165
|
+
console.warn(`Warning: API key saved but could not reach ${serviceUrl}`);
|
|
166
|
+
console.warn('Run "safeclaw status" later to verify the connection.');
|
|
167
|
+
}
|
|
138
168
|
|
|
139
169
|
// Register with OpenClaw
|
|
140
170
|
console.log('Registering SafeClaw plugin with OpenClaw...');
|
|
@@ -267,7 +297,55 @@ if (command === 'connect') {
|
|
|
267
297
|
}
|
|
268
298
|
}
|
|
269
299
|
|
|
270
|
-
// 5.
|
|
300
|
+
// 5. Handshake — validates API key actually works
|
|
301
|
+
if (serviceHealthy && cfg.apiKey) {
|
|
302
|
+
try {
|
|
303
|
+
const res = await fetch(`${cfg.serviceUrl}/handshake`, {
|
|
304
|
+
method: 'POST',
|
|
305
|
+
headers: {
|
|
306
|
+
'Content-Type': 'application/json',
|
|
307
|
+
'Authorization': `Bearer ${cfg.apiKey}`,
|
|
308
|
+
},
|
|
309
|
+
body: JSON.stringify({ pluginVersion: '0.1.3', configHash: '' }),
|
|
310
|
+
signal: AbortSignal.timeout(cfg.timeoutMs),
|
|
311
|
+
});
|
|
312
|
+
if (res.ok) {
|
|
313
|
+
const data = await res.json() as Record<string, unknown>;
|
|
314
|
+
console.log(`[ok] Handshake: org=${data.orgId}, scope=${data.scope}, engine=${data.engineReady ? 'ready' : 'not ready'}`);
|
|
315
|
+
} else {
|
|
316
|
+
let detail = `HTTP ${res.status}`;
|
|
317
|
+
try {
|
|
318
|
+
const body = await res.json() as Record<string, unknown>;
|
|
319
|
+
detail = (body.error ?? body.detail ?? detail) as string;
|
|
320
|
+
} catch { /* ignore parse errors */ }
|
|
321
|
+
console.log(`[!!] Handshake failed: ${detail}`);
|
|
322
|
+
if (res.status === 401) {
|
|
323
|
+
console.log(' ↳ API key is invalid or revoked. Get a new key at https://safeclaw.eu/dashboard');
|
|
324
|
+
} else if (res.status === 403) {
|
|
325
|
+
console.log(' ↳ API key lacks required scope. Check key permissions in your dashboard.');
|
|
326
|
+
} else if (res.status === 500) {
|
|
327
|
+
console.log(' ↳ Server error — check service logs for details.');
|
|
328
|
+
}
|
|
329
|
+
allOk = false;
|
|
330
|
+
}
|
|
331
|
+
} catch (e) {
|
|
332
|
+
const isTimeout = e instanceof DOMException && e.name === 'TimeoutError';
|
|
333
|
+
if (isTimeout) {
|
|
334
|
+
console.log(`[!!] Handshake failed: timeout after ${cfg.timeoutMs}ms`);
|
|
335
|
+
console.log(' ↳ Service may be overloaded. Try increasing SAFECLAW_TIMEOUT_MS.');
|
|
336
|
+
} else {
|
|
337
|
+
console.log('[!!] Handshake failed: could not connect');
|
|
338
|
+
console.log(` ↳ Is the service running at ${cfg.serviceUrl}?`);
|
|
339
|
+
}
|
|
340
|
+
allOk = false;
|
|
341
|
+
}
|
|
342
|
+
} else if (serviceHealthy && !cfg.apiKey) {
|
|
343
|
+
console.log('[!!] Handshake: skipped — no API key configured');
|
|
344
|
+
console.log(' ↳ Run: safeclaw connect <your-api-key>');
|
|
345
|
+
allOk = false;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// 6. OpenClaw installed
|
|
271
349
|
try {
|
|
272
350
|
execSync('which openclaw', { encoding: 'utf-8', stdio: 'pipe' });
|
|
273
351
|
console.log('[ok] OpenClaw: installed');
|
|
@@ -276,7 +354,7 @@ if (command === 'connect') {
|
|
|
276
354
|
allOk = false;
|
|
277
355
|
}
|
|
278
356
|
|
|
279
|
-
//
|
|
357
|
+
// 7. Plugin extension files exist
|
|
280
358
|
const extensionDir = join(homedir(), '.openclaw', 'extensions', 'safeclaw');
|
|
281
359
|
const hasManifest = existsSync(join(extensionDir, 'openclaw.plugin.json'));
|
|
282
360
|
const hasEntry = existsSync(join(extensionDir, 'index.js'));
|
|
@@ -295,7 +373,7 @@ if (command === 'connect') {
|
|
|
295
373
|
allOk = false;
|
|
296
374
|
}
|
|
297
375
|
|
|
298
|
-
//
|
|
376
|
+
// 8. Plugin enabled in OpenClaw config
|
|
299
377
|
const ocConfigPath = join(homedir(), '.openclaw', 'openclaw.json');
|
|
300
378
|
if (existsSync(ocConfigPath)) {
|
|
301
379
|
const ocConfig = readJson(ocConfigPath);
|
package/dist/cli.js
CHANGED
|
@@ -119,7 +119,39 @@ if (command === 'connect') {
|
|
|
119
119
|
mkdirSync(configDir, { recursive: true });
|
|
120
120
|
writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n');
|
|
121
121
|
chmodSync(configPath, 0o600);
|
|
122
|
-
console.log(`
|
|
122
|
+
console.log(`API key saved to ${configPath}`);
|
|
123
|
+
// Validate key via handshake
|
|
124
|
+
try {
|
|
125
|
+
const res = await fetch(`${serviceUrl}/handshake`, {
|
|
126
|
+
method: 'POST',
|
|
127
|
+
headers: {
|
|
128
|
+
'Content-Type': 'application/json',
|
|
129
|
+
'Authorization': `Bearer ${apiKey}`,
|
|
130
|
+
},
|
|
131
|
+
body: JSON.stringify({ pluginVersion: '0.1.3', configHash: '' }),
|
|
132
|
+
signal: AbortSignal.timeout(5000),
|
|
133
|
+
});
|
|
134
|
+
if (res.ok) {
|
|
135
|
+
const data = await res.json();
|
|
136
|
+
console.log(`Connected! org=${data.orgId}, scope=${data.scope}`);
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
let detail = `HTTP ${res.status}`;
|
|
140
|
+
try {
|
|
141
|
+
const body = await res.json();
|
|
142
|
+
detail = (body.error ?? body.detail ?? detail);
|
|
143
|
+
}
|
|
144
|
+
catch { /* ignore */ }
|
|
145
|
+
console.warn(`Warning: API key saved but handshake failed — ${detail}`);
|
|
146
|
+
if (res.status === 401) {
|
|
147
|
+
console.warn('The key may be invalid or revoked. Check https://safeclaw.eu/dashboard');
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
catch {
|
|
152
|
+
console.warn(`Warning: API key saved but could not reach ${serviceUrl}`);
|
|
153
|
+
console.warn('Run "safeclaw status" later to verify the connection.');
|
|
154
|
+
}
|
|
123
155
|
// Register with OpenClaw
|
|
124
156
|
console.log('Registering SafeClaw plugin with OpenClaw...');
|
|
125
157
|
const registered = registerWithOpenClaw();
|
|
@@ -261,7 +293,61 @@ else if (command === 'status') {
|
|
|
261
293
|
allOk = false;
|
|
262
294
|
}
|
|
263
295
|
}
|
|
264
|
-
// 5.
|
|
296
|
+
// 5. Handshake — validates API key actually works
|
|
297
|
+
if (serviceHealthy && cfg.apiKey) {
|
|
298
|
+
try {
|
|
299
|
+
const res = await fetch(`${cfg.serviceUrl}/handshake`, {
|
|
300
|
+
method: 'POST',
|
|
301
|
+
headers: {
|
|
302
|
+
'Content-Type': 'application/json',
|
|
303
|
+
'Authorization': `Bearer ${cfg.apiKey}`,
|
|
304
|
+
},
|
|
305
|
+
body: JSON.stringify({ pluginVersion: '0.1.3', configHash: '' }),
|
|
306
|
+
signal: AbortSignal.timeout(cfg.timeoutMs),
|
|
307
|
+
});
|
|
308
|
+
if (res.ok) {
|
|
309
|
+
const data = await res.json();
|
|
310
|
+
console.log(`[ok] Handshake: org=${data.orgId}, scope=${data.scope}, engine=${data.engineReady ? 'ready' : 'not ready'}`);
|
|
311
|
+
}
|
|
312
|
+
else {
|
|
313
|
+
let detail = `HTTP ${res.status}`;
|
|
314
|
+
try {
|
|
315
|
+
const body = await res.json();
|
|
316
|
+
detail = (body.error ?? body.detail ?? detail);
|
|
317
|
+
}
|
|
318
|
+
catch { /* ignore parse errors */ }
|
|
319
|
+
console.log(`[!!] Handshake failed: ${detail}`);
|
|
320
|
+
if (res.status === 401) {
|
|
321
|
+
console.log(' ↳ API key is invalid or revoked. Get a new key at https://safeclaw.eu/dashboard');
|
|
322
|
+
}
|
|
323
|
+
else if (res.status === 403) {
|
|
324
|
+
console.log(' ↳ API key lacks required scope. Check key permissions in your dashboard.');
|
|
325
|
+
}
|
|
326
|
+
else if (res.status === 500) {
|
|
327
|
+
console.log(' ↳ Server error — check service logs for details.');
|
|
328
|
+
}
|
|
329
|
+
allOk = false;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
catch (e) {
|
|
333
|
+
const isTimeout = e instanceof DOMException && e.name === 'TimeoutError';
|
|
334
|
+
if (isTimeout) {
|
|
335
|
+
console.log(`[!!] Handshake failed: timeout after ${cfg.timeoutMs}ms`);
|
|
336
|
+
console.log(' ↳ Service may be overloaded. Try increasing SAFECLAW_TIMEOUT_MS.');
|
|
337
|
+
}
|
|
338
|
+
else {
|
|
339
|
+
console.log('[!!] Handshake failed: could not connect');
|
|
340
|
+
console.log(` ↳ Is the service running at ${cfg.serviceUrl}?`);
|
|
341
|
+
}
|
|
342
|
+
allOk = false;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
else if (serviceHealthy && !cfg.apiKey) {
|
|
346
|
+
console.log('[!!] Handshake: skipped — no API key configured');
|
|
347
|
+
console.log(' ↳ Run: safeclaw connect <your-api-key>');
|
|
348
|
+
allOk = false;
|
|
349
|
+
}
|
|
350
|
+
// 6. OpenClaw installed
|
|
265
351
|
try {
|
|
266
352
|
execSync('which openclaw', { encoding: 'utf-8', stdio: 'pipe' });
|
|
267
353
|
console.log('[ok] OpenClaw: installed');
|
|
@@ -270,7 +356,7 @@ else if (command === 'status') {
|
|
|
270
356
|
console.log('[!!] OpenClaw: not found in PATH');
|
|
271
357
|
allOk = false;
|
|
272
358
|
}
|
|
273
|
-
//
|
|
359
|
+
// 7. Plugin extension files exist
|
|
274
360
|
const extensionDir = join(homedir(), '.openclaw', 'extensions', 'safeclaw');
|
|
275
361
|
const hasManifest = existsSync(join(extensionDir, 'openclaw.plugin.json'));
|
|
276
362
|
const hasEntry = existsSync(join(extensionDir, 'index.js'));
|
|
@@ -291,7 +377,7 @@ else if (command === 'status') {
|
|
|
291
377
|
console.log('[!!] Plugin: not installed. Run: safeclaw setup');
|
|
292
378
|
allOk = false;
|
|
293
379
|
}
|
|
294
|
-
//
|
|
380
|
+
// 8. Plugin enabled in OpenClaw config
|
|
295
381
|
const ocConfigPath = join(homedir(), '.openclaw', 'openclaw.json');
|
|
296
382
|
if (existsSync(ocConfigPath)) {
|
|
297
383
|
const ocConfig = readJson(ocConfigPath);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openclaw-safeclaw-plugin",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.2",
|
|
4
4
|
"description": "SafeClaw Neurosymbolic Governance plugin for OpenClaw — validates AI agent actions against OWL ontologies and SHACL constraints",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|