openclaw-safeclaw-plugin 1.3.0 → 1.4.0
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 +31 -29
- package/dist/cli.js +30 -28
- package/dist/index.js +3 -3
- package/dist/tui/Status.js +1 -1
- package/dist/tui/config.js +14 -5
- package/index.ts +4 -4
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/tui/Status.tsx +1 -1
- package/tui/config.ts +12 -4
package/cli.tsx
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import React from 'react';
|
|
3
3
|
import { render } from 'ink';
|
|
4
4
|
import { execSync } from 'child_process';
|
|
5
|
-
import { readFileSync, writeFileSync, mkdirSync, existsSync, copyFileSync, lstatSync, unlinkSync
|
|
5
|
+
import { readFileSync, writeFileSync, mkdirSync, existsSync, copyFileSync, lstatSync, unlinkSync } from 'fs';
|
|
6
6
|
import { join, dirname } from 'path';
|
|
7
7
|
import { homedir } from 'os';
|
|
8
8
|
import { fileURLToPath } from 'url';
|
|
@@ -94,7 +94,32 @@ const command = args[0];
|
|
|
94
94
|
|
|
95
95
|
// Handle --help / -h for any command position
|
|
96
96
|
if (!command || command === '--help' || command === '-h' || command === 'help') {
|
|
97
|
-
|
|
97
|
+
console.log('safeclaw-plugin — OpenClaw plugin CLI for SafeClaw governance');
|
|
98
|
+
console.log('');
|
|
99
|
+
console.log('Usage: safeclaw-plugin <command> [options]');
|
|
100
|
+
console.log('');
|
|
101
|
+
console.log('Setup:');
|
|
102
|
+
console.log(' connect <api-key> Save API key, validate via handshake, register with OpenClaw');
|
|
103
|
+
console.log(' Keys start with "sc_". Get yours at https://safeclaw.eu/dashboard');
|
|
104
|
+
console.log(' setup Register plugin with OpenClaw without an API key (manual setup)');
|
|
105
|
+
console.log(' restart-openclaw Restart the OpenClaw daemon to pick up plugin changes');
|
|
106
|
+
console.log('');
|
|
107
|
+
console.log('Diagnostics:');
|
|
108
|
+
console.log(' status Run 9 checks: config, API key, service health, evaluate endpoint,');
|
|
109
|
+
console.log(' handshake, OpenClaw binary, plugin files, OpenClaw config, NemoClaw');
|
|
110
|
+
console.log('');
|
|
111
|
+
console.log('Configuration:');
|
|
112
|
+
console.log(' config show Show current enforcement, failMode, enabled, serviceUrl, apiKey');
|
|
113
|
+
console.log(' config set <k> <v> Set a config value. Keys: enforcement, failMode, enabled, serviceUrl');
|
|
114
|
+
console.log(' enforcement: enforce | warn-only | audit-only | disabled');
|
|
115
|
+
console.log(' failMode: open (allow on error) | closed (block on error)');
|
|
116
|
+
console.log(' enabled: true | false');
|
|
117
|
+
console.log('');
|
|
118
|
+
console.log('Interactive:');
|
|
119
|
+
console.log(' tui Open the interactive settings TUI (Status, Settings, About tabs)');
|
|
120
|
+
console.log('');
|
|
121
|
+
console.log('For the service CLI (serve, audit, policy, pref), use the "safeclaw" command.');
|
|
122
|
+
process.exit(0);
|
|
98
123
|
} else if (command === 'connect') {
|
|
99
124
|
const apiKey = args[1];
|
|
100
125
|
const serviceUrlIdx = args.indexOf('--service-url');
|
|
@@ -192,7 +217,7 @@ if (!command || command === '--help' || command === '-h' || command === 'help')
|
|
|
192
217
|
console.log(`enforcement: ${cfg.enforcement}`);
|
|
193
218
|
console.log(`failMode: ${cfg.failMode}`);
|
|
194
219
|
console.log(`serviceUrl: ${cfg.serviceUrl}`);
|
|
195
|
-
console.log(`apiKey: ${cfg.apiKey ? `${cfg.apiKey.slice(0, 6)}
|
|
220
|
+
console.log(`apiKey: ${cfg.apiKey ? `${cfg.apiKey.slice(0, 6)}..${cfg.apiKey.slice(-4)}` : '(not set)'}`);
|
|
196
221
|
console.log(`timeoutMs: ${cfg.timeoutMs}`);
|
|
197
222
|
} else if (subcommand === 'set') {
|
|
198
223
|
const key = args[2];
|
|
@@ -479,30 +504,7 @@ if (!command || command === '--help' || command === '-h' || command === 'help')
|
|
|
479
504
|
console.log('Some checks failed. Fix the issues above.');
|
|
480
505
|
}
|
|
481
506
|
} else {
|
|
482
|
-
console.
|
|
483
|
-
console.
|
|
484
|
-
|
|
485
|
-
console.log('');
|
|
486
|
-
console.log('Setup:');
|
|
487
|
-
console.log(' connect <api-key> Save API key, validate via handshake, register with OpenClaw');
|
|
488
|
-
console.log(' Keys start with "sc_". Get yours at https://safeclaw.eu/dashboard');
|
|
489
|
-
console.log(' setup Register plugin with OpenClaw without an API key (manual setup)');
|
|
490
|
-
console.log(' restart-openclaw Restart the OpenClaw daemon to pick up plugin changes');
|
|
491
|
-
console.log('');
|
|
492
|
-
console.log('Diagnostics:');
|
|
493
|
-
console.log(' status Run 9 checks: config, API key, service health, evaluate endpoint,');
|
|
494
|
-
console.log(' handshake, OpenClaw binary, plugin files, OpenClaw config, NemoClaw');
|
|
495
|
-
console.log('');
|
|
496
|
-
console.log('Configuration:');
|
|
497
|
-
console.log(' config show Show current enforcement, failMode, enabled, serviceUrl, apiKey');
|
|
498
|
-
console.log(' config set <k> <v> Set a config value. Keys: enforcement, failMode, enabled, serviceUrl');
|
|
499
|
-
console.log(' enforcement: enforce | warn-only | audit-only | disabled');
|
|
500
|
-
console.log(' failMode: open (allow on error) | closed (block on error)');
|
|
501
|
-
console.log(' enabled: true | false');
|
|
502
|
-
console.log('');
|
|
503
|
-
console.log('Interactive:');
|
|
504
|
-
console.log(' tui Open the interactive settings TUI (Status, Settings, About tabs)');
|
|
505
|
-
console.log('');
|
|
506
|
-
console.log('For the service CLI (serve, audit, policy, pref), use the "safeclaw" command.');
|
|
507
|
-
process.exit(0);
|
|
507
|
+
console.error(`Unknown command: "${command}"`);
|
|
508
|
+
console.error('Run "safeclaw-plugin --help" for usage information.');
|
|
509
|
+
process.exit(1);
|
|
508
510
|
}
|
package/dist/cli.js
CHANGED
|
@@ -85,7 +85,32 @@ const args = process.argv.slice(2);
|
|
|
85
85
|
const command = args[0];
|
|
86
86
|
// Handle --help / -h for any command position
|
|
87
87
|
if (!command || command === '--help' || command === '-h' || command === 'help') {
|
|
88
|
-
|
|
88
|
+
console.log('safeclaw-plugin — OpenClaw plugin CLI for SafeClaw governance');
|
|
89
|
+
console.log('');
|
|
90
|
+
console.log('Usage: safeclaw-plugin <command> [options]');
|
|
91
|
+
console.log('');
|
|
92
|
+
console.log('Setup:');
|
|
93
|
+
console.log(' connect <api-key> Save API key, validate via handshake, register with OpenClaw');
|
|
94
|
+
console.log(' Keys start with "sc_". Get yours at https://safeclaw.eu/dashboard');
|
|
95
|
+
console.log(' setup Register plugin with OpenClaw without an API key (manual setup)');
|
|
96
|
+
console.log(' restart-openclaw Restart the OpenClaw daemon to pick up plugin changes');
|
|
97
|
+
console.log('');
|
|
98
|
+
console.log('Diagnostics:');
|
|
99
|
+
console.log(' status Run 9 checks: config, API key, service health, evaluate endpoint,');
|
|
100
|
+
console.log(' handshake, OpenClaw binary, plugin files, OpenClaw config, NemoClaw');
|
|
101
|
+
console.log('');
|
|
102
|
+
console.log('Configuration:');
|
|
103
|
+
console.log(' config show Show current enforcement, failMode, enabled, serviceUrl, apiKey');
|
|
104
|
+
console.log(' config set <k> <v> Set a config value. Keys: enforcement, failMode, enabled, serviceUrl');
|
|
105
|
+
console.log(' enforcement: enforce | warn-only | audit-only | disabled');
|
|
106
|
+
console.log(' failMode: open (allow on error) | closed (block on error)');
|
|
107
|
+
console.log(' enabled: true | false');
|
|
108
|
+
console.log('');
|
|
109
|
+
console.log('Interactive:');
|
|
110
|
+
console.log(' tui Open the interactive settings TUI (Status, Settings, About tabs)');
|
|
111
|
+
console.log('');
|
|
112
|
+
console.log('For the service CLI (serve, audit, policy, pref), use the "safeclaw" command.');
|
|
113
|
+
process.exit(0);
|
|
89
114
|
}
|
|
90
115
|
else if (command === 'connect') {
|
|
91
116
|
const apiKey = args[1];
|
|
@@ -180,7 +205,7 @@ else if (command === 'config') {
|
|
|
180
205
|
console.log(`enforcement: ${cfg.enforcement}`);
|
|
181
206
|
console.log(`failMode: ${cfg.failMode}`);
|
|
182
207
|
console.log(`serviceUrl: ${cfg.serviceUrl}`);
|
|
183
|
-
console.log(`apiKey: ${cfg.apiKey ? `${cfg.apiKey.slice(0, 6)}
|
|
208
|
+
console.log(`apiKey: ${cfg.apiKey ? `${cfg.apiKey.slice(0, 6)}..${cfg.apiKey.slice(-4)}` : '(not set)'}`);
|
|
184
209
|
console.log(`timeoutMs: ${cfg.timeoutMs}`);
|
|
185
210
|
}
|
|
186
211
|
else if (subcommand === 'set') {
|
|
@@ -490,30 +515,7 @@ else if (command === 'status') {
|
|
|
490
515
|
}
|
|
491
516
|
}
|
|
492
517
|
else {
|
|
493
|
-
console.
|
|
494
|
-
console.
|
|
495
|
-
|
|
496
|
-
console.log('');
|
|
497
|
-
console.log('Setup:');
|
|
498
|
-
console.log(' connect <api-key> Save API key, validate via handshake, register with OpenClaw');
|
|
499
|
-
console.log(' Keys start with "sc_". Get yours at https://safeclaw.eu/dashboard');
|
|
500
|
-
console.log(' setup Register plugin with OpenClaw without an API key (manual setup)');
|
|
501
|
-
console.log(' restart-openclaw Restart the OpenClaw daemon to pick up plugin changes');
|
|
502
|
-
console.log('');
|
|
503
|
-
console.log('Diagnostics:');
|
|
504
|
-
console.log(' status Run 9 checks: config, API key, service health, evaluate endpoint,');
|
|
505
|
-
console.log(' handshake, OpenClaw binary, plugin files, OpenClaw config, NemoClaw');
|
|
506
|
-
console.log('');
|
|
507
|
-
console.log('Configuration:');
|
|
508
|
-
console.log(' config show Show current enforcement, failMode, enabled, serviceUrl, apiKey');
|
|
509
|
-
console.log(' config set <k> <v> Set a config value. Keys: enforcement, failMode, enabled, serviceUrl');
|
|
510
|
-
console.log(' enforcement: enforce | warn-only | audit-only | disabled');
|
|
511
|
-
console.log(' failMode: open (allow on error) | closed (block on error)');
|
|
512
|
-
console.log(' enabled: true | false');
|
|
513
|
-
console.log('');
|
|
514
|
-
console.log('Interactive:');
|
|
515
|
-
console.log(' tui Open the interactive settings TUI (Status, Settings, About tabs)');
|
|
516
|
-
console.log('');
|
|
517
|
-
console.log('For the service CLI (serve, audit, policy, pref), use the "safeclaw" command.');
|
|
518
|
-
process.exit(0);
|
|
518
|
+
console.error(`Unknown command: "${command}"`);
|
|
519
|
+
console.error('Run "safeclaw-plugin --help" for usage information.');
|
|
520
|
+
process.exit(1);
|
|
519
521
|
}
|
package/dist/index.js
CHANGED
|
@@ -149,12 +149,12 @@ export default {
|
|
|
149
149
|
name: 'SafeClaw Neurosymbolic Governance',
|
|
150
150
|
version: PLUGIN_VERSION,
|
|
151
151
|
register(api) {
|
|
152
|
+
_ocPluginConfig = api.pluginConfig ?? {};
|
|
153
|
+
log = api.logger ?? console;
|
|
152
154
|
if (!getConfig().enabled) {
|
|
153
|
-
|
|
155
|
+
log.info('[SafeClaw] Plugin disabled');
|
|
154
156
|
return;
|
|
155
157
|
}
|
|
156
|
-
log = api.logger ?? console;
|
|
157
|
-
_ocPluginConfig = api.pluginConfig ?? {};
|
|
158
158
|
// Generate a unique instance ID for this plugin run (fallback when agentId is not configured)
|
|
159
159
|
const instanceId = getConfig().agentId || `instance-${crypto.randomUUID()}`;
|
|
160
160
|
// Heartbeat watchdog — send config hash to service every 30s
|
package/dist/tui/Status.js
CHANGED
|
@@ -129,7 +129,7 @@ export default function Status({ config }) {
|
|
|
129
129
|
checkOpenClaw();
|
|
130
130
|
}, 10000);
|
|
131
131
|
return () => clearInterval(interval);
|
|
132
|
-
}, []);
|
|
132
|
+
}, [config.serviceUrl, config.apiKey]);
|
|
133
133
|
useInput((input) => {
|
|
134
134
|
if (input === 'r') {
|
|
135
135
|
restartOpenClaw();
|
package/dist/tui/config.js
CHANGED
|
@@ -33,8 +33,11 @@ export function loadConfig() {
|
|
|
33
33
|
defaults.serviceUrl = raw.remote.serviceUrl;
|
|
34
34
|
if (raw.remote?.apiKey)
|
|
35
35
|
defaults.apiKey = raw.remote.apiKey;
|
|
36
|
-
if (raw.remote?.timeoutMs)
|
|
37
|
-
|
|
36
|
+
if (raw.remote?.timeoutMs != null) {
|
|
37
|
+
const t = Number(raw.remote.timeoutMs);
|
|
38
|
+
if (Number.isFinite(t) && t > 0)
|
|
39
|
+
defaults.timeoutMs = t;
|
|
40
|
+
}
|
|
38
41
|
if (raw.enforcement?.mode)
|
|
39
42
|
defaults.enforcement = raw.enforcement.mode;
|
|
40
43
|
if (raw.enforcement?.failMode)
|
|
@@ -67,6 +70,8 @@ export function loadConfig() {
|
|
|
67
70
|
}
|
|
68
71
|
if (process.env.SAFECLAW_ENABLED === 'false')
|
|
69
72
|
defaults.enabled = false;
|
|
73
|
+
else if (process.env.SAFECLAW_ENABLED === 'true')
|
|
74
|
+
defaults.enabled = true;
|
|
70
75
|
const enforcement = process.env.SAFECLAW_ENFORCEMENT;
|
|
71
76
|
if (enforcement && ['enforce', 'warn-only', 'audit-only', 'disabled'].includes(enforcement)) {
|
|
72
77
|
defaults.enforcement = enforcement;
|
|
@@ -140,6 +145,7 @@ export function saveConfig(config) {
|
|
|
140
145
|
existing.remote = {};
|
|
141
146
|
}
|
|
142
147
|
existing.remote.serviceUrl = config.serviceUrl;
|
|
148
|
+
existing.remote.timeoutMs = config.timeoutMs;
|
|
143
149
|
if (config.apiKey) {
|
|
144
150
|
existing.remote.apiKey = config.apiKey;
|
|
145
151
|
}
|
|
@@ -163,9 +169,12 @@ export function saveConfig(config) {
|
|
|
163
169
|
writeFileSync(CONFIG_PATH, JSON.stringify(existing, null, 2) + '\n', { encoding: 'utf-8', mode: 0o600 });
|
|
164
170
|
}
|
|
165
171
|
catch (e) {
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
172
|
+
const code = e.code;
|
|
173
|
+
if (code === 'EROFS') {
|
|
174
|
+
throw new Error('Cannot save config: filesystem is read-only (sandbox environment)');
|
|
175
|
+
}
|
|
176
|
+
if (code === 'EACCES') {
|
|
177
|
+
throw new Error(`Cannot save config: permission denied for ${CONFIG_PATH}`);
|
|
169
178
|
}
|
|
170
179
|
throw e;
|
|
171
180
|
}
|
package/index.ts
CHANGED
|
@@ -162,14 +162,14 @@ export default {
|
|
|
162
162
|
version: PLUGIN_VERSION,
|
|
163
163
|
|
|
164
164
|
register(api: OpenClawPluginApi) {
|
|
165
|
+
_ocPluginConfig = api.pluginConfig ?? {};
|
|
166
|
+
log = api.logger ?? console;
|
|
167
|
+
|
|
165
168
|
if (!getConfig().enabled) {
|
|
166
|
-
|
|
169
|
+
log.info('[SafeClaw] Plugin disabled');
|
|
167
170
|
return;
|
|
168
171
|
}
|
|
169
172
|
|
|
170
|
-
log = api.logger ?? console;
|
|
171
|
-
_ocPluginConfig = api.pluginConfig ?? {};
|
|
172
|
-
|
|
173
173
|
// Generate a unique instance ID for this plugin run (fallback when agentId is not configured)
|
|
174
174
|
const instanceId = getConfig().agentId || `instance-${crypto.randomUUID()}`;
|
|
175
175
|
|
package/openclaw.plugin.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"id": "safeclaw",
|
|
3
3
|
"name": "SafeClaw Neurosymbolic Governance",
|
|
4
4
|
"description": "Validates AI agent actions against OWL ontologies and SHACL constraints before execution",
|
|
5
|
-
"version": "1.
|
|
5
|
+
"version": "1.3.0",
|
|
6
6
|
"author": "Tendly EU",
|
|
7
7
|
"license": "MIT",
|
|
8
8
|
"homepage": "https://safeclaw.eu",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openclaw-safeclaw-plugin",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
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",
|
package/tui/Status.tsx
CHANGED
package/tui/config.ts
CHANGED
|
@@ -49,7 +49,10 @@ export function loadConfig(): SafeClawConfig {
|
|
|
49
49
|
if (raw.enabled === false) defaults.enabled = false;
|
|
50
50
|
if (raw.remote?.serviceUrl) defaults.serviceUrl = raw.remote.serviceUrl;
|
|
51
51
|
if (raw.remote?.apiKey) defaults.apiKey = raw.remote.apiKey;
|
|
52
|
-
if (raw.remote?.timeoutMs
|
|
52
|
+
if (raw.remote?.timeoutMs != null) {
|
|
53
|
+
const t = Number(raw.remote.timeoutMs);
|
|
54
|
+
if (Number.isFinite(t) && t > 0) defaults.timeoutMs = t;
|
|
55
|
+
}
|
|
53
56
|
if (raw.enforcement?.mode) defaults.enforcement = raw.enforcement.mode;
|
|
54
57
|
if (raw.enforcement?.failMode) defaults.failMode = raw.enforcement.failMode;
|
|
55
58
|
if (raw.agentId) defaults.agentId = raw.agentId;
|
|
@@ -73,6 +76,7 @@ export function loadConfig(): SafeClawConfig {
|
|
|
73
76
|
}
|
|
74
77
|
}
|
|
75
78
|
if (process.env.SAFECLAW_ENABLED === 'false') defaults.enabled = false;
|
|
79
|
+
else if (process.env.SAFECLAW_ENABLED === 'true') defaults.enabled = true;
|
|
76
80
|
const enforcement = process.env.SAFECLAW_ENFORCEMENT;
|
|
77
81
|
if (enforcement && ['enforce', 'warn-only', 'audit-only', 'disabled'].includes(enforcement)) {
|
|
78
82
|
defaults.enforcement = enforcement as SafeClawConfig['enforcement'];
|
|
@@ -152,6 +156,7 @@ export function saveConfig(config: SafeClawConfig): void {
|
|
|
152
156
|
existing.remote = {};
|
|
153
157
|
}
|
|
154
158
|
(existing.remote as Record<string, unknown>).serviceUrl = config.serviceUrl;
|
|
159
|
+
(existing.remote as Record<string, unknown>).timeoutMs = config.timeoutMs;
|
|
155
160
|
if (config.apiKey) {
|
|
156
161
|
(existing.remote as Record<string, unknown>).apiKey = config.apiKey;
|
|
157
162
|
} else {
|
|
@@ -177,9 +182,12 @@ export function saveConfig(config: SafeClawConfig): void {
|
|
|
177
182
|
try {
|
|
178
183
|
writeFileSync(CONFIG_PATH, JSON.stringify(existing, null, 2) + '\n', { encoding: 'utf-8', mode: 0o600 });
|
|
179
184
|
} catch (e) {
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
185
|
+
const code = (e as NodeJS.ErrnoException).code;
|
|
186
|
+
if (code === 'EROFS') {
|
|
187
|
+
throw new Error('Cannot save config: filesystem is read-only (sandbox environment)');
|
|
188
|
+
}
|
|
189
|
+
if (code === 'EACCES') {
|
|
190
|
+
throw new Error(`Cannot save config: permission denied for ${CONFIG_PATH}`);
|
|
183
191
|
}
|
|
184
192
|
throw e;
|
|
185
193
|
}
|