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 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, rmSync } from 'fs';
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
- // Fall through to the else block at the bottom which prints full help
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)}...` : '(not set)'}`);
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.log('safeclaw-plugin OpenClaw plugin CLI for SafeClaw governance');
483
- console.log('');
484
- console.log('Usage: safeclaw-plugin <command> [options]');
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
- // Fall through to the else block at the bottom which prints full help
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)}...` : '(not set)'}`);
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.log('safeclaw-plugin OpenClaw plugin CLI for SafeClaw governance');
494
- console.log('');
495
- console.log('Usage: safeclaw-plugin <command> [options]');
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
- console.log('[SafeClaw] Plugin disabled');
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
@@ -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();
@@ -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
- defaults.timeoutMs = raw.remote.timeoutMs;
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
- if (e.code === 'EROFS' || e.code === 'EACCES') {
167
- // Sandbox filesystem is read-only — silently skip
168
- return;
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
- console.log('[SafeClaw] Plugin disabled');
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
 
@@ -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.2.0",
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.0",
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
@@ -141,7 +141,7 @@ export default function Status({ config }: StatusProps) {
141
141
  checkOpenClaw();
142
142
  }, 10000);
143
143
  return () => clearInterval(interval);
144
- }, []);
144
+ }, [config.serviceUrl, config.apiKey]);
145
145
 
146
146
  useInput((input) => {
147
147
  if (input === 'r') {
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) defaults.timeoutMs = 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
- if ((e as NodeJS.ErrnoException).code === 'EROFS' || (e as NodeJS.ErrnoException).code === 'EACCES') {
181
- // Sandbox filesystem is read-only — silently skip
182
- return;
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
  }