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.
Files changed (3) hide show
  1. package/cli.tsx +82 -4
  2. package/dist/cli.js +90 -4
  3. 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(`Connected! API key saved to ${configPath}`);
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. OpenClaw installed
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
- // 6. Plugin extension files exist
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
- // 7. Plugin enabled in OpenClaw config
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(`Connected! API key saved to ${configPath}`);
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. OpenClaw installed
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
- // 6. Plugin extension files exist
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
- // 7. Plugin enabled in OpenClaw config
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.0",
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",