openclaw-safeclaw-plugin 0.7.0 → 0.7.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 (4) hide show
  1. package/README.md +16 -21
  2. package/cli.tsx +122 -72
  3. package/dist/cli.js +125 -67
  4. package/package.json +1 -1
package/README.md CHANGED
@@ -1,38 +1,33 @@
1
1
  # openclaw-safeclaw-plugin
2
2
 
3
- Neurosymbolic governance plugin for OpenClaw AI agents. Validates every tool call, message, and action against OWL ontologies and SHACL constraints before execution.
3
+ Neurosymbolic governance plugin for OpenClaw AI agents. Validates every tool call, message, and action against safety constraints before execution.
4
4
 
5
5
  ## Install
6
6
 
7
7
  ```bash
8
- openclaw plugins install openclaw-safeclaw-plugin
8
+ npm install -g openclaw-safeclaw-plugin
9
9
  ```
10
10
 
11
11
  ## Quick Start
12
12
 
13
- Install and go the plugin connects to SafeClaw's hosted service by default:
13
+ 1. Sign up at [safeclaw.eu](https://safeclaw.eu) and create an API key
14
+ 2. Install and connect:
14
15
 
15
16
  ```bash
16
- openclaw plugins install openclaw-safeclaw-plugin
17
+ npm install -g openclaw-safeclaw-plugin
18
+ safeclaw connect <your-api-key>
19
+ safeclaw restart-openclaw
17
20
  ```
18
21
 
19
- No configuration needed. The default service URL is `https://api.safeclaw.eu/api/v1`.
22
+ That's it. Every tool call your AI agent makes is now governed by SafeClaw.
20
23
 
21
- ## Self-Hosted
24
+ ## Commands
22
25
 
23
- To run your own SafeClaw service, override the URL:
24
-
25
- ```bash
26
- export SAFECLAW_URL="http://localhost:8420/api/v1"
27
-
28
- # Start the SafeClaw service
29
- git clone https://github.com/tendlyeu/SafeClaw.git
30
- cd SafeClaw/safeclaw-service
31
- python -m venv .venv && source .venv/bin/activate
32
- pip install -e ".[dev]"
33
- safeclaw init --user-id yourname
34
- safeclaw serve
35
- # Engine ready on http://localhost:8420
26
+ ```
27
+ safeclaw connect <api-key> Connect to SafeClaw and register with OpenClaw
28
+ safeclaw setup Register plugin with OpenClaw (no key needed)
29
+ safeclaw tui Open the interactive settings TUI
30
+ safeclaw restart-openclaw Restart the OpenClaw daemon
36
31
  ```
37
32
 
38
33
  ## What It Does
@@ -60,11 +55,11 @@ Set via environment variables or `~/.safeclaw/config.json`:
60
55
  | Variable | Default | Description |
61
56
  |----------|---------|-------------|
62
57
  | `SAFECLAW_URL` | `https://api.safeclaw.eu/api/v1` | SafeClaw service URL |
63
- | `SAFECLAW_API_KEY` | *(empty)* | API key for cloud mode |
58
+ | `SAFECLAW_API_KEY` | *(empty)* | API key (set automatically by `safeclaw connect`) |
64
59
  | `SAFECLAW_TIMEOUT_MS` | `500` | Request timeout in ms |
65
60
  | `SAFECLAW_ENABLED` | `true` | Set `false` to disable |
66
61
  | `SAFECLAW_ENFORCEMENT` | `enforce` | `enforce`, `warn-only`, `audit-only`, or `disabled` |
67
- | `SAFECLAW_FAIL_MODE` | `closed` | `open` (allow on failure) or `closed` (block on failure) |
62
+ | `SAFECLAW_FAIL_MODE` | `open` | `open` (allow on failure) or `closed` (block on failure) |
68
63
 
69
64
  ## Enforcement Modes
70
65
 
package/cli.tsx CHANGED
@@ -2,81 +2,38 @@
2
2
  import React from 'react';
3
3
  import { render } from 'ink';
4
4
  import { execSync } from 'child_process';
5
- import { readFileSync, writeFileSync, mkdirSync, chmodSync, existsSync, symlinkSync, lstatSync, unlinkSync, realpathSync } from 'fs';
6
- import { join, dirname } from 'path';
5
+ import { readFileSync, writeFileSync, mkdirSync, chmodSync, existsSync } from 'fs';
6
+ import { join } from 'path';
7
7
  import { homedir } from 'os';
8
- import { fileURLToPath } from 'url';
9
8
  import App from './tui/App.js';
10
9
 
11
- const __dirname = dirname(fileURLToPath(import.meta.url));
12
-
13
- function readJson(path: string): Record<string, unknown> {
14
- try {
15
- return JSON.parse(readFileSync(path, 'utf-8'));
16
- } catch {
17
- return {};
18
- }
19
- }
20
-
21
10
  function registerWithOpenClaw(): boolean {
22
- // Plugin root is one level up from dist/
23
- const pluginRoot = join(__dirname, '..');
24
- const extensionsDir = join(homedir(), '.openclaw', 'extensions');
25
- const linkPath = join(extensionsDir, 'safeclaw');
26
-
27
- // 1. Create symlink in ~/.openclaw/extensions/safeclaw -> plugin root
11
+ // Use OpenClaw's native plugin install mechanism
28
12
  try {
29
- mkdirSync(extensionsDir, { recursive: true });
30
-
31
- // Remove existing symlink/dir if it points elsewhere
32
- if (existsSync(linkPath)) {
33
- const stat = lstatSync(linkPath);
34
- if (stat.isSymbolicLink()) {
35
- const target = realpathSync(linkPath);
36
- if (target === realpathSync(pluginRoot)) {
37
- // Already linked correctly
38
- } else {
39
- unlinkSync(linkPath);
40
- symlinkSync(pluginRoot, linkPath);
41
- }
13
+ execSync('openclaw plugins install openclaw-safeclaw-plugin', {
14
+ encoding: 'utf-8',
15
+ timeout: 30000,
16
+ stdio: 'pipe',
17
+ });
18
+ return true;
19
+ } catch {
20
+ // Fallback: try linking from the global npm install location
21
+ try {
22
+ const globalRoot = execSync('npm root -g', { encoding: 'utf-8', timeout: 5000 }).trim();
23
+ const pluginPath = join(globalRoot, 'openclaw-safeclaw-plugin');
24
+ if (existsSync(pluginPath)) {
25
+ execSync(`openclaw plugins install --link "${pluginPath}"`, {
26
+ encoding: 'utf-8',
27
+ timeout: 15000,
28
+ stdio: 'pipe',
29
+ });
30
+ return true;
42
31
  }
43
- // If it's a real directory (from openclaw plugins install), leave it
44
- } else {
45
- symlinkSync(pluginRoot, linkPath);
32
+ } catch {
33
+ // Both methods failed
46
34
  }
47
- } catch (e) {
48
- console.warn(`Warning: Could not create extension symlink: ${e instanceof Error ? e.message : e}`);
49
- return false;
50
- }
51
-
52
- // 2. Enable plugin in ~/.openclaw/openclaw.json
53
- const openclawConfigPath = join(homedir(), '.openclaw', 'openclaw.json');
54
- const ocConfig = readJson(openclawConfigPath);
55
-
56
- if (!ocConfig.plugins || typeof ocConfig.plugins !== 'object') {
57
- ocConfig.plugins = {};
58
35
  }
59
- const plugins = ocConfig.plugins as Record<string, unknown>;
60
- if (!plugins.entries || typeof plugins.entries !== 'object') {
61
- plugins.entries = {};
62
- }
63
- const entries = plugins.entries as Record<string, unknown>;
64
-
65
- // Only add/update if not already configured
66
- if (!entries.safeclaw || typeof entries.safeclaw !== 'object') {
67
- entries.safeclaw = { enabled: true };
68
- } else {
69
- (entries.safeclaw as Record<string, unknown>).enabled = true;
70
- }
71
-
72
- try {
73
- writeFileSync(openclawConfigPath, JSON.stringify(ocConfig, null, 2) + '\n');
74
- } catch (e) {
75
- console.warn(`Warning: Could not update OpenClaw config: ${e instanceof Error ? e.message : e}`);
76
- return false;
77
- }
78
-
79
- return true;
36
+ return false;
80
37
  }
81
38
 
82
39
  const args = process.argv.slice(2);
@@ -94,6 +51,12 @@ if (command === 'connect') {
94
51
  process.exit(1);
95
52
  }
96
53
 
54
+ if (!apiKey.startsWith('sc_')) {
55
+ console.error('Error: Invalid API key. Keys start with "sc_".');
56
+ console.error('Get your key at https://safeclaw.eu/dashboard');
57
+ process.exit(1);
58
+ }
59
+
97
60
  const configDir = join(homedir(), '.safeclaw');
98
61
  const configPath = join(configDir, 'config.json');
99
62
 
@@ -122,18 +85,18 @@ if (command === 'connect') {
122
85
  console.log(`Connected! API key saved to ${configPath}`);
123
86
 
124
87
  // Register with OpenClaw
88
+ console.log('Registering SafeClaw plugin with OpenClaw...');
125
89
  const registered = registerWithOpenClaw();
126
90
  if (registered) {
127
91
  console.log('SafeClaw plugin registered with OpenClaw.');
128
92
  console.log('');
129
93
  console.log('Restart OpenClaw to activate:');
130
94
  console.log(' safeclaw restart-openclaw');
131
- console.log(' — or —');
132
- console.log(' openclaw daemon restart');
133
95
  } else {
134
96
  console.log('');
135
97
  console.log('Could not auto-register with OpenClaw.');
136
- console.log('Register manually: openclaw plugins install openclaw-safeclaw-plugin');
98
+ console.log('Register manually:');
99
+ console.log(' openclaw plugins install openclaw-safeclaw-plugin');
137
100
  }
138
101
  } else if (command === 'tui') {
139
102
  render(React.createElement(App));
@@ -148,7 +111,7 @@ if (command === 'connect') {
148
111
  process.exit(1);
149
112
  }
150
113
  } else if (command === 'setup') {
151
- // Just register with OpenClaw (no API key needed)
114
+ console.log('Registering SafeClaw plugin with OpenClaw...');
152
115
  const registered = registerWithOpenClaw();
153
116
  if (registered) {
154
117
  console.log('SafeClaw plugin registered with OpenClaw.');
@@ -158,7 +121,93 @@ if (command === 'connect') {
158
121
  console.log(' 2. Run: safeclaw connect <your-api-key>');
159
122
  console.log(' 3. Run: safeclaw restart-openclaw');
160
123
  } else {
161
- console.log('Could not auto-register. Try: openclaw plugins install openclaw-safeclaw-plugin');
124
+ console.log('Could not auto-register.');
125
+ console.log('Try: openclaw plugins install openclaw-safeclaw-plugin');
126
+ }
127
+ } else if (command === 'status') {
128
+ const configPath = join(homedir(), '.safeclaw', 'config.json');
129
+ let allOk = true;
130
+
131
+ // 1. Config file
132
+ if (existsSync(configPath)) {
133
+ console.log('[ok] Config file: ' + configPath);
134
+ } else {
135
+ console.log('[!!] Config file not found. Run: safeclaw connect <api-key>');
136
+ allOk = false;
137
+ }
138
+
139
+ // 2. API key
140
+ let apiKey = '';
141
+ let serviceUrl = 'https://api.safeclaw.eu/api/v1';
142
+ if (existsSync(configPath)) {
143
+ try {
144
+ const cfg = JSON.parse(readFileSync(configPath, 'utf-8'));
145
+ const remote = cfg.remote as Record<string, string> | undefined;
146
+ apiKey = remote?.apiKey ?? '';
147
+ serviceUrl = remote?.serviceUrl ?? serviceUrl;
148
+ } catch { /* ignore */ }
149
+ }
150
+
151
+ if (apiKey && apiKey.startsWith('sc_')) {
152
+ console.log('[ok] API key: configured (sc_...)');
153
+ } else if (apiKey) {
154
+ console.log('[!!] API key: invalid (must start with sc_)');
155
+ allOk = false;
156
+ } else {
157
+ console.log('[!!] API key: not set. Run: safeclaw connect <api-key>');
158
+ allOk = false;
159
+ }
160
+
161
+ // 3. SafeClaw service reachable
162
+ try {
163
+ const res = await fetch(`${serviceUrl}/health`, {
164
+ signal: AbortSignal.timeout(5000),
165
+ headers: apiKey ? { 'Authorization': `Bearer ${apiKey}` } : {},
166
+ });
167
+ if (res.ok) {
168
+ const data = await res.json() as Record<string, unknown>;
169
+ console.log(`[ok] SafeClaw service: ${data.status ?? 'ok'} (${serviceUrl})`);
170
+ } else {
171
+ console.log(`[!!] SafeClaw service: HTTP ${res.status} (${serviceUrl})`);
172
+ allOk = false;
173
+ }
174
+ } catch {
175
+ console.log(`[!!] SafeClaw service: unreachable (${serviceUrl})`);
176
+ allOk = false;
177
+ }
178
+
179
+ // 4. OpenClaw installed
180
+ try {
181
+ execSync('which openclaw', { encoding: 'utf-8', stdio: 'pipe' });
182
+ console.log('[ok] OpenClaw: installed');
183
+ } catch {
184
+ console.log('[!!] OpenClaw: not found in PATH');
185
+ allOk = false;
186
+ }
187
+
188
+ // 5. Plugin registered with OpenClaw
189
+ try {
190
+ const pluginList = execSync('openclaw plugins list', {
191
+ encoding: 'utf-8',
192
+ timeout: 10000,
193
+ stdio: 'pipe',
194
+ });
195
+ if (pluginList.includes('safeclaw')) {
196
+ console.log('[ok] Plugin: registered with OpenClaw');
197
+ } else {
198
+ console.log('[!!] Plugin: not registered with OpenClaw. Run: safeclaw setup');
199
+ allOk = false;
200
+ }
201
+ } catch {
202
+ console.log('[??] Plugin: could not check OpenClaw plugin list');
203
+ }
204
+
205
+ // Summary
206
+ console.log('');
207
+ if (allOk) {
208
+ console.log('All checks passed. SafeClaw is ready.');
209
+ } else {
210
+ console.log('Some checks failed. Fix the issues above.');
162
211
  }
163
212
  } else {
164
213
  console.log('Usage: safeclaw <command>');
@@ -166,6 +215,7 @@ if (command === 'connect') {
166
215
  console.log('Commands:');
167
216
  console.log(' connect <api-key> Connect to SafeClaw and register with OpenClaw');
168
217
  console.log(' setup Register SafeClaw plugin with OpenClaw (no key needed)');
218
+ console.log(' status Check SafeClaw + OpenClaw connection status');
169
219
  console.log(' tui Open the interactive SafeClaw settings TUI');
170
220
  console.log(' restart-openclaw Restart the OpenClaw daemon');
171
221
  process.exit(0);
package/dist/cli.js CHANGED
@@ -2,77 +2,39 @@
2
2
  import React from 'react';
3
3
  import { render } from 'ink';
4
4
  import { execSync } from 'child_process';
5
- import { readFileSync, writeFileSync, mkdirSync, chmodSync, existsSync, symlinkSync, lstatSync, unlinkSync, realpathSync } from 'fs';
6
- import { join, dirname } from 'path';
5
+ import { readFileSync, writeFileSync, mkdirSync, chmodSync, existsSync } from 'fs';
6
+ import { join } from 'path';
7
7
  import { homedir } from 'os';
8
- import { fileURLToPath } from 'url';
9
8
  import App from './tui/App.js';
10
- const __dirname = dirname(fileURLToPath(import.meta.url));
11
- function readJson(path) {
9
+ function registerWithOpenClaw() {
10
+ // Use OpenClaw's native plugin install mechanism
12
11
  try {
13
- return JSON.parse(readFileSync(path, 'utf-8'));
12
+ execSync('openclaw plugins install openclaw-safeclaw-plugin', {
13
+ encoding: 'utf-8',
14
+ timeout: 30000,
15
+ stdio: 'pipe',
16
+ });
17
+ return true;
14
18
  }
15
19
  catch {
16
- return {};
17
- }
18
- }
19
- function registerWithOpenClaw() {
20
- // Plugin root is one level up from dist/
21
- const pluginRoot = join(__dirname, '..');
22
- const extensionsDir = join(homedir(), '.openclaw', 'extensions');
23
- const linkPath = join(extensionsDir, 'safeclaw');
24
- // 1. Create symlink in ~/.openclaw/extensions/safeclaw -> plugin root
25
- try {
26
- mkdirSync(extensionsDir, { recursive: true });
27
- // Remove existing symlink/dir if it points elsewhere
28
- if (existsSync(linkPath)) {
29
- const stat = lstatSync(linkPath);
30
- if (stat.isSymbolicLink()) {
31
- const target = realpathSync(linkPath);
32
- if (target === realpathSync(pluginRoot)) {
33
- // Already linked correctly
34
- }
35
- else {
36
- unlinkSync(linkPath);
37
- symlinkSync(pluginRoot, linkPath);
38
- }
20
+ // Fallback: try linking from the global npm install location
21
+ try {
22
+ const globalRoot = execSync('npm root -g', { encoding: 'utf-8', timeout: 5000 }).trim();
23
+ const pluginPath = join(globalRoot, 'openclaw-safeclaw-plugin');
24
+ if (existsSync(pluginPath)) {
25
+ execSync(`openclaw plugins install --link "${pluginPath}"`, {
26
+ encoding: 'utf-8',
27
+ timeout: 15000,
28
+ stdio: 'pipe',
29
+ });
30
+ return true;
39
31
  }
40
- // If it's a real directory (from openclaw plugins install), leave it
41
32
  }
42
- else {
43
- symlinkSync(pluginRoot, linkPath);
33
+ catch {
34
+ // Both methods failed
44
35
  }
45
36
  }
46
- catch (e) {
47
- console.warn(`Warning: Could not create extension symlink: ${e instanceof Error ? e.message : e}`);
48
- return false;
49
- }
50
- // 2. Enable plugin in ~/.openclaw/openclaw.json
51
- const openclawConfigPath = join(homedir(), '.openclaw', 'openclaw.json');
52
- const ocConfig = readJson(openclawConfigPath);
53
- if (!ocConfig.plugins || typeof ocConfig.plugins !== 'object') {
54
- ocConfig.plugins = {};
55
- }
56
- const plugins = ocConfig.plugins;
57
- if (!plugins.entries || typeof plugins.entries !== 'object') {
58
- plugins.entries = {};
59
- }
60
- const entries = plugins.entries;
61
- // Only add/update if not already configured
62
- if (!entries.safeclaw || typeof entries.safeclaw !== 'object') {
63
- entries.safeclaw = { enabled: true };
64
- }
65
- else {
66
- entries.safeclaw.enabled = true;
67
- }
68
- try {
69
- writeFileSync(openclawConfigPath, JSON.stringify(ocConfig, null, 2) + '\n');
70
- }
71
- catch (e) {
72
- console.warn(`Warning: Could not update OpenClaw config: ${e instanceof Error ? e.message : e}`);
73
- return false;
74
- }
75
- return true;
37
+ return false;
76
38
  }
77
39
  const args = process.argv.slice(2);
78
40
  const command = args[0];
@@ -86,6 +48,11 @@ if (command === 'connect') {
86
48
  console.error('Usage: safeclaw connect <api-key> [--service-url <url>]');
87
49
  process.exit(1);
88
50
  }
51
+ if (!apiKey.startsWith('sc_')) {
52
+ console.error('Error: Invalid API key. Keys start with "sc_".');
53
+ console.error('Get your key at https://safeclaw.eu/dashboard');
54
+ process.exit(1);
55
+ }
89
56
  const configDir = join(homedir(), '.safeclaw');
90
57
  const configPath = join(configDir, 'config.json');
91
58
  // Load existing config or start fresh
@@ -110,19 +77,19 @@ if (command === 'connect') {
110
77
  chmodSync(configPath, 0o600);
111
78
  console.log(`Connected! API key saved to ${configPath}`);
112
79
  // Register with OpenClaw
80
+ console.log('Registering SafeClaw plugin with OpenClaw...');
113
81
  const registered = registerWithOpenClaw();
114
82
  if (registered) {
115
83
  console.log('SafeClaw plugin registered with OpenClaw.');
116
84
  console.log('');
117
85
  console.log('Restart OpenClaw to activate:');
118
86
  console.log(' safeclaw restart-openclaw');
119
- console.log(' — or —');
120
- console.log(' openclaw daemon restart');
121
87
  }
122
88
  else {
123
89
  console.log('');
124
90
  console.log('Could not auto-register with OpenClaw.');
125
- console.log('Register manually: openclaw plugins install openclaw-safeclaw-plugin');
91
+ console.log('Register manually:');
92
+ console.log(' openclaw plugins install openclaw-safeclaw-plugin');
126
93
  }
127
94
  }
128
95
  else if (command === 'tui') {
@@ -141,7 +108,7 @@ else if (command === 'restart-openclaw') {
141
108
  }
142
109
  }
143
110
  else if (command === 'setup') {
144
- // Just register with OpenClaw (no API key needed)
111
+ console.log('Registering SafeClaw plugin with OpenClaw...');
145
112
  const registered = registerWithOpenClaw();
146
113
  if (registered) {
147
114
  console.log('SafeClaw plugin registered with OpenClaw.');
@@ -152,7 +119,97 @@ else if (command === 'setup') {
152
119
  console.log(' 3. Run: safeclaw restart-openclaw');
153
120
  }
154
121
  else {
155
- console.log('Could not auto-register. Try: openclaw plugins install openclaw-safeclaw-plugin');
122
+ console.log('Could not auto-register.');
123
+ console.log('Try: openclaw plugins install openclaw-safeclaw-plugin');
124
+ }
125
+ }
126
+ else if (command === 'status') {
127
+ const configPath = join(homedir(), '.safeclaw', 'config.json');
128
+ let allOk = true;
129
+ // 1. Config file
130
+ if (existsSync(configPath)) {
131
+ console.log('[ok] Config file: ' + configPath);
132
+ }
133
+ else {
134
+ console.log('[!!] Config file not found. Run: safeclaw connect <api-key>');
135
+ allOk = false;
136
+ }
137
+ // 2. API key
138
+ let apiKey = '';
139
+ let serviceUrl = 'https://api.safeclaw.eu/api/v1';
140
+ if (existsSync(configPath)) {
141
+ try {
142
+ const cfg = JSON.parse(readFileSync(configPath, 'utf-8'));
143
+ const remote = cfg.remote;
144
+ apiKey = remote?.apiKey ?? '';
145
+ serviceUrl = remote?.serviceUrl ?? serviceUrl;
146
+ }
147
+ catch { /* ignore */ }
148
+ }
149
+ if (apiKey && apiKey.startsWith('sc_')) {
150
+ console.log('[ok] API key: configured (sc_...)');
151
+ }
152
+ else if (apiKey) {
153
+ console.log('[!!] API key: invalid (must start with sc_)');
154
+ allOk = false;
155
+ }
156
+ else {
157
+ console.log('[!!] API key: not set. Run: safeclaw connect <api-key>');
158
+ allOk = false;
159
+ }
160
+ // 3. SafeClaw service reachable
161
+ try {
162
+ const res = await fetch(`${serviceUrl}/health`, {
163
+ signal: AbortSignal.timeout(5000),
164
+ headers: apiKey ? { 'Authorization': `Bearer ${apiKey}` } : {},
165
+ });
166
+ if (res.ok) {
167
+ const data = await res.json();
168
+ console.log(`[ok] SafeClaw service: ${data.status ?? 'ok'} (${serviceUrl})`);
169
+ }
170
+ else {
171
+ console.log(`[!!] SafeClaw service: HTTP ${res.status} (${serviceUrl})`);
172
+ allOk = false;
173
+ }
174
+ }
175
+ catch {
176
+ console.log(`[!!] SafeClaw service: unreachable (${serviceUrl})`);
177
+ allOk = false;
178
+ }
179
+ // 4. OpenClaw installed
180
+ try {
181
+ execSync('which openclaw', { encoding: 'utf-8', stdio: 'pipe' });
182
+ console.log('[ok] OpenClaw: installed');
183
+ }
184
+ catch {
185
+ console.log('[!!] OpenClaw: not found in PATH');
186
+ allOk = false;
187
+ }
188
+ // 5. Plugin registered with OpenClaw
189
+ try {
190
+ const pluginList = execSync('openclaw plugins list', {
191
+ encoding: 'utf-8',
192
+ timeout: 10000,
193
+ stdio: 'pipe',
194
+ });
195
+ if (pluginList.includes('safeclaw')) {
196
+ console.log('[ok] Plugin: registered with OpenClaw');
197
+ }
198
+ else {
199
+ console.log('[!!] Plugin: not registered with OpenClaw. Run: safeclaw setup');
200
+ allOk = false;
201
+ }
202
+ }
203
+ catch {
204
+ console.log('[??] Plugin: could not check OpenClaw plugin list');
205
+ }
206
+ // Summary
207
+ console.log('');
208
+ if (allOk) {
209
+ console.log('All checks passed. SafeClaw is ready.');
210
+ }
211
+ else {
212
+ console.log('Some checks failed. Fix the issues above.');
156
213
  }
157
214
  }
158
215
  else {
@@ -161,6 +218,7 @@ else {
161
218
  console.log('Commands:');
162
219
  console.log(' connect <api-key> Connect to SafeClaw and register with OpenClaw');
163
220
  console.log(' setup Register SafeClaw plugin with OpenClaw (no key needed)');
221
+ console.log(' status Check SafeClaw + OpenClaw connection status');
164
222
  console.log(' tui Open the interactive SafeClaw settings TUI');
165
223
  console.log(' restart-openclaw Restart the OpenClaw daemon');
166
224
  process.exit(0);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openclaw-safeclaw-plugin",
3
- "version": "0.7.0",
3
+ "version": "0.7.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",