openclaw-safeclaw-plugin 0.5.1 → 0.7.1

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 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
@@ -7,6 +7,35 @@ import { join } from 'path';
7
7
  import { homedir } from 'os';
8
8
  import App from './tui/App.js';
9
9
 
10
+ function registerWithOpenClaw(): boolean {
11
+ // Use OpenClaw's native plugin install mechanism
12
+ try {
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;
31
+ }
32
+ } catch {
33
+ // Both methods failed
34
+ }
35
+ }
36
+ return false;
37
+ }
38
+
10
39
  const args = process.argv.slice(2);
11
40
  const command = args[0];
12
41
 
@@ -22,6 +51,12 @@ if (command === 'connect') {
22
51
  process.exit(1);
23
52
  }
24
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
+
25
60
  const configDir = join(homedir(), '.safeclaw');
26
61
  const configPath = join(configDir, 'config.json');
27
62
 
@@ -47,7 +82,22 @@ if (command === 'connect') {
47
82
  writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n');
48
83
  chmodSync(configPath, 0o600);
49
84
 
50
- console.log(`Connected! Your API key has been saved to ${configPath}`);
85
+ console.log(`Connected! API key saved to ${configPath}`);
86
+
87
+ // Register with OpenClaw
88
+ console.log('Registering SafeClaw plugin with OpenClaw...');
89
+ const registered = registerWithOpenClaw();
90
+ if (registered) {
91
+ console.log('SafeClaw plugin registered with OpenClaw.');
92
+ console.log('');
93
+ console.log('Restart OpenClaw to activate:');
94
+ console.log(' safeclaw restart-openclaw');
95
+ } else {
96
+ console.log('');
97
+ console.log('Could not auto-register with OpenClaw.');
98
+ console.log('Register manually:');
99
+ console.log(' openclaw plugins install openclaw-safeclaw-plugin');
100
+ }
51
101
  } else if (command === 'tui') {
52
102
  render(React.createElement(App));
53
103
  } else if (command === 'restart-openclaw') {
@@ -60,11 +110,112 @@ if (command === 'connect') {
60
110
  console.error('Failed to restart OpenClaw daemon:', message);
61
111
  process.exit(1);
62
112
  }
113
+ } else if (command === 'setup') {
114
+ console.log('Registering SafeClaw plugin with OpenClaw...');
115
+ const registered = registerWithOpenClaw();
116
+ if (registered) {
117
+ console.log('SafeClaw plugin registered with OpenClaw.');
118
+ console.log('');
119
+ console.log('Next steps:');
120
+ console.log(' 1. Get an API key at https://safeclaw.eu/dashboard');
121
+ console.log(' 2. Run: safeclaw connect <your-api-key>');
122
+ console.log(' 3. Run: safeclaw restart-openclaw');
123
+ } else {
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.');
211
+ }
63
212
  } else {
64
213
  console.log('Usage: safeclaw <command>');
65
214
  console.log('');
66
215
  console.log('Commands:');
67
- console.log(' connect <api-key> Save your API key to ~/.safeclaw/config.json');
216
+ console.log(' connect <api-key> Connect to SafeClaw and register with OpenClaw');
217
+ console.log(' setup Register SafeClaw plugin with OpenClaw (no key needed)');
218
+ console.log(' status Check SafeClaw + OpenClaw connection status');
68
219
  console.log(' tui Open the interactive SafeClaw settings TUI');
69
220
  console.log(' restart-openclaw Restart the OpenClaw daemon');
70
221
  process.exit(0);
package/dist/cli.js CHANGED
@@ -6,6 +6,36 @@ import { readFileSync, writeFileSync, mkdirSync, chmodSync, existsSync } from 'f
6
6
  import { join } from 'path';
7
7
  import { homedir } from 'os';
8
8
  import App from './tui/App.js';
9
+ function registerWithOpenClaw() {
10
+ // Use OpenClaw's native plugin install mechanism
11
+ try {
12
+ execSync('openclaw plugins install openclaw-safeclaw-plugin', {
13
+ encoding: 'utf-8',
14
+ timeout: 30000,
15
+ stdio: 'pipe',
16
+ });
17
+ return true;
18
+ }
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;
31
+ }
32
+ }
33
+ catch {
34
+ // Both methods failed
35
+ }
36
+ }
37
+ return false;
38
+ }
9
39
  const args = process.argv.slice(2);
10
40
  const command = args[0];
11
41
  if (command === 'connect') {
@@ -18,6 +48,11 @@ if (command === 'connect') {
18
48
  console.error('Usage: safeclaw connect <api-key> [--service-url <url>]');
19
49
  process.exit(1);
20
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
+ }
21
56
  const configDir = join(homedir(), '.safeclaw');
22
57
  const configPath = join(configDir, 'config.json');
23
58
  // Load existing config or start fresh
@@ -40,7 +75,22 @@ if (command === 'connect') {
40
75
  mkdirSync(configDir, { recursive: true });
41
76
  writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n');
42
77
  chmodSync(configPath, 0o600);
43
- console.log(`Connected! Your API key has been saved to ${configPath}`);
78
+ console.log(`Connected! API key saved to ${configPath}`);
79
+ // Register with OpenClaw
80
+ console.log('Registering SafeClaw plugin with OpenClaw...');
81
+ const registered = registerWithOpenClaw();
82
+ if (registered) {
83
+ console.log('SafeClaw plugin registered with OpenClaw.');
84
+ console.log('');
85
+ console.log('Restart OpenClaw to activate:');
86
+ console.log(' safeclaw restart-openclaw');
87
+ }
88
+ else {
89
+ console.log('');
90
+ console.log('Could not auto-register with OpenClaw.');
91
+ console.log('Register manually:');
92
+ console.log(' openclaw plugins install openclaw-safeclaw-plugin');
93
+ }
44
94
  }
45
95
  else if (command === 'tui') {
46
96
  render(React.createElement(App));
@@ -57,11 +107,118 @@ else if (command === 'restart-openclaw') {
57
107
  process.exit(1);
58
108
  }
59
109
  }
110
+ else if (command === 'setup') {
111
+ console.log('Registering SafeClaw plugin with OpenClaw...');
112
+ const registered = registerWithOpenClaw();
113
+ if (registered) {
114
+ console.log('SafeClaw plugin registered with OpenClaw.');
115
+ console.log('');
116
+ console.log('Next steps:');
117
+ console.log(' 1. Get an API key at https://safeclaw.eu/dashboard');
118
+ console.log(' 2. Run: safeclaw connect <your-api-key>');
119
+ console.log(' 3. Run: safeclaw restart-openclaw');
120
+ }
121
+ else {
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.');
213
+ }
214
+ }
60
215
  else {
61
216
  console.log('Usage: safeclaw <command>');
62
217
  console.log('');
63
218
  console.log('Commands:');
64
- console.log(' connect <api-key> Save your API key to ~/.safeclaw/config.json');
219
+ console.log(' connect <api-key> Connect to SafeClaw and register with OpenClaw');
220
+ console.log(' setup Register SafeClaw plugin with OpenClaw (no key needed)');
221
+ console.log(' status Check SafeClaw + OpenClaw connection status');
65
222
  console.log(' tui Open the interactive SafeClaw settings TUI');
66
223
  console.log(' restart-openclaw Restart the OpenClaw daemon');
67
224
  process.exit(0);
package/dist/index.js CHANGED
@@ -80,7 +80,7 @@ async function checkConnection() {
80
80
  }
81
81
  }
82
82
  export default {
83
- id: 'openclaw-safeclaw-plugin',
83
+ id: 'safeclaw',
84
84
  name: 'SafeClaw Neurosymbolic Governance',
85
85
  version: '0.1.2',
86
86
  register(api) {
package/index.ts CHANGED
@@ -105,7 +105,7 @@ async function checkConnection(): Promise<void> {
105
105
  }
106
106
 
107
107
  export default {
108
- id: 'openclaw-safeclaw-plugin',
108
+ id: 'safeclaw',
109
109
  name: 'SafeClaw Neurosymbolic Governance',
110
110
  version: '0.1.2',
111
111
 
@@ -0,0 +1,31 @@
1
+ {
2
+ "id": "safeclaw",
3
+ "name": "SafeClaw Neurosymbolic Governance",
4
+ "description": "Validates AI agent actions against OWL ontologies and SHACL constraints before execution",
5
+ "version": "0.5.1",
6
+ "configSchema": {
7
+ "type": "object",
8
+ "additionalProperties": false,
9
+ "properties": {
10
+ "serviceUrl": {
11
+ "type": "string",
12
+ "description": "SafeClaw service URL",
13
+ "default": "https://api.safeclaw.eu/api/v1"
14
+ },
15
+ "apiKey": {
16
+ "type": "string",
17
+ "description": "SafeClaw API key"
18
+ },
19
+ "enforcement": {
20
+ "type": "string",
21
+ "enum": ["enforce", "warn-only", "audit-only", "disabled"],
22
+ "default": "enforce"
23
+ },
24
+ "failMode": {
25
+ "type": "string",
26
+ "enum": ["open", "closed"],
27
+ "default": "open"
28
+ }
29
+ }
30
+ }
31
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openclaw-safeclaw-plugin",
3
- "version": "0.5.1",
3
+ "version": "0.7.1",
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",
@@ -13,8 +13,14 @@
13
13
  "typecheck": "tsc --noEmit",
14
14
  "prepublishOnly": "npm run build"
15
15
  },
16
+ "openclaw": {
17
+ "extensions": [
18
+ "dist/index.js"
19
+ ]
20
+ },
16
21
  "files": [
17
22
  "dist/",
23
+ "openclaw.plugin.json",
18
24
  "index.ts",
19
25
  "cli.tsx",
20
26
  "tui/",