openclaw-safeclaw-plugin 0.4.0 → 0.5.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/cli.tsx CHANGED
@@ -2,12 +2,53 @@
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 } from 'fs';
6
+ import { join } from 'path';
7
+ import { homedir } from 'os';
5
8
  import App from './tui/App.js';
6
9
 
7
10
  const args = process.argv.slice(2);
8
11
  const command = args[0];
9
12
 
10
- if (command === 'tui') {
13
+ if (command === 'connect') {
14
+ const apiKey = args[1];
15
+ const serviceUrlIdx = args.indexOf('--service-url');
16
+ const serviceUrl = serviceUrlIdx !== -1 && args[serviceUrlIdx + 1]
17
+ ? args[serviceUrlIdx + 1]
18
+ : 'https://api.safeclaw.eu/api/v1';
19
+
20
+ if (!apiKey || apiKey.startsWith('--')) {
21
+ console.error('Usage: safeclaw connect <api-key> [--service-url <url>]');
22
+ process.exit(1);
23
+ }
24
+
25
+ const configDir = join(homedir(), '.safeclaw');
26
+ const configPath = join(configDir, 'config.json');
27
+
28
+ // Load existing config or start fresh
29
+ let config: Record<string, unknown> = {};
30
+ if (existsSync(configPath)) {
31
+ try {
32
+ config = JSON.parse(readFileSync(configPath, 'utf-8'));
33
+ } catch {
34
+ // Start fresh
35
+ }
36
+ }
37
+
38
+ // Set remote config
39
+ if (!config.remote || typeof config.remote !== 'object') {
40
+ config.remote = {};
41
+ }
42
+ (config.remote as Record<string, string>).apiKey = apiKey;
43
+ (config.remote as Record<string, string>).serviceUrl = serviceUrl;
44
+
45
+ // Write config with owner-only permissions
46
+ mkdirSync(configDir, { recursive: true });
47
+ writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n');
48
+ chmodSync(configPath, 0o600);
49
+
50
+ console.log(`Connected! Your API key has been saved to ${configPath}`);
51
+ } else if (command === 'tui') {
11
52
  render(React.createElement(App));
12
53
  } else if (command === 'restart-openclaw') {
13
54
  try {
@@ -23,6 +64,7 @@ if (command === 'tui') {
23
64
  console.log('Usage: safeclaw <command>');
24
65
  console.log('');
25
66
  console.log('Commands:');
67
+ console.log(' connect <api-key> Save your API key to ~/.safeclaw/config.json');
26
68
  console.log(' tui Open the interactive SafeClaw settings TUI');
27
69
  console.log(' restart-openclaw Restart the OpenClaw daemon');
28
70
  process.exit(0);
package/dist/cli.js CHANGED
@@ -2,10 +2,47 @@
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 } from 'fs';
6
+ import { join } from 'path';
7
+ import { homedir } from 'os';
5
8
  import App from './tui/App.js';
6
9
  const args = process.argv.slice(2);
7
10
  const command = args[0];
8
- if (command === 'tui') {
11
+ if (command === 'connect') {
12
+ const apiKey = args[1];
13
+ const serviceUrlIdx = args.indexOf('--service-url');
14
+ const serviceUrl = serviceUrlIdx !== -1 && args[serviceUrlIdx + 1]
15
+ ? args[serviceUrlIdx + 1]
16
+ : 'https://api.safeclaw.eu/api/v1';
17
+ if (!apiKey || apiKey.startsWith('--')) {
18
+ console.error('Usage: safeclaw connect <api-key> [--service-url <url>]');
19
+ process.exit(1);
20
+ }
21
+ const configDir = join(homedir(), '.safeclaw');
22
+ const configPath = join(configDir, 'config.json');
23
+ // Load existing config or start fresh
24
+ let config = {};
25
+ if (existsSync(configPath)) {
26
+ try {
27
+ config = JSON.parse(readFileSync(configPath, 'utf-8'));
28
+ }
29
+ catch {
30
+ // Start fresh
31
+ }
32
+ }
33
+ // Set remote config
34
+ if (!config.remote || typeof config.remote !== 'object') {
35
+ config.remote = {};
36
+ }
37
+ config.remote.apiKey = apiKey;
38
+ config.remote.serviceUrl = serviceUrl;
39
+ // Write config with owner-only permissions
40
+ mkdirSync(configDir, { recursive: true });
41
+ writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n');
42
+ chmodSync(configPath, 0o600);
43
+ console.log(`Connected! Your API key has been saved to ${configPath}`);
44
+ }
45
+ else if (command === 'tui') {
9
46
  render(React.createElement(App));
10
47
  }
11
48
  else if (command === 'restart-openclaw') {
@@ -24,6 +61,7 @@ else {
24
61
  console.log('Usage: safeclaw <command>');
25
62
  console.log('');
26
63
  console.log('Commands:');
64
+ console.log(' connect <api-key> Save your API key to ~/.safeclaw/config.json');
27
65
  console.log(' tui Open the interactive SafeClaw settings TUI');
28
66
  console.log(' restart-openclaw Restart the OpenClaw daemon');
29
67
  process.exit(0);
package/dist/tui/App.js CHANGED
@@ -1,10 +1,15 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useState } from 'react';
3
3
  import { Text, Box, useInput, useApp } from 'ink';
4
+ import { readFileSync } from 'fs';
5
+ import { dirname, join } from 'path';
6
+ import { fileURLToPath } from 'url';
4
7
  import { loadConfig } from './config.js';
5
8
  import Status from './Status.js';
6
9
  import Settings from './Settings.js';
7
10
  import About from './About.js';
11
+ const __dirname = dirname(fileURLToPath(import.meta.url));
12
+ const PKG_VERSION = JSON.parse(readFileSync(join(__dirname, '..', '..', 'package.json'), 'utf-8')).version;
8
13
  const TABS = ['Status', 'Settings', 'About'];
9
14
  export default function App() {
10
15
  const { exit } = useApp();
@@ -28,5 +33,5 @@ export default function App() {
28
33
  }
29
34
  }
30
35
  });
31
- return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { borderStyle: "single", borderColor: "green", paddingX: 1, children: [_jsx(Text, { bold: true, color: "green", children: "SafeClaw " }), _jsx(Text, { dimColor: true, children: "v0.2.0" })] }), _jsxs(Box, { paddingX: 1, gap: 2, children: [TABS.map((t, i) => (_jsx(Text, { bold: tab === t, color: tab === t ? 'cyan' : 'white', dimColor: tab !== t, children: `${i + 1}:${t}` }, t))), _jsx(Text, { dimColor: true, children: " tab/1-3 to switch" })] }), _jsxs(Box, { marginTop: 1, children: [tab === 'Status' && _jsx(Status, { config: config }), tab === 'Settings' && (_jsx(Settings, { config: config, onConfigChange: setConfig })), tab === 'About' && _jsx(About, {})] })] }));
36
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { borderStyle: "single", borderColor: "green", paddingX: 1, children: [_jsx(Text, { bold: true, color: "green", children: "SafeClaw " }), _jsxs(Text, { dimColor: true, children: ["v", PKG_VERSION] })] }), _jsxs(Box, { paddingX: 1, gap: 2, children: [TABS.map((t, i) => (_jsx(Text, { bold: tab === t, color: tab === t ? 'cyan' : 'white', dimColor: tab !== t, children: `${i + 1}:${t}` }, t))), _jsx(Text, { dimColor: true, children: " tab/1-3 to switch" })] }), _jsxs(Box, { marginTop: 1, children: [tab === 'Status' && _jsx(Status, { config: config }), tab === 'Settings' && (_jsx(Settings, { config: config, onConfigChange: setConfig })), tab === 'About' && _jsx(About, {})] })] }));
32
37
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openclaw-safeclaw-plugin",
3
- "version": "0.4.0",
3
+ "version": "0.5.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",
package/tui/App.tsx CHANGED
@@ -1,10 +1,16 @@
1
1
  import React, { useState } from 'react';
2
2
  import { Text, Box, useInput, useApp } from 'ink';
3
+ import { readFileSync } from 'fs';
4
+ import { dirname, join } from 'path';
5
+ import { fileURLToPath } from 'url';
3
6
  import { loadConfig, type SafeClawConfig } from './config.js';
4
7
  import Status from './Status.js';
5
8
  import Settings from './Settings.js';
6
9
  import About from './About.js';
7
10
 
11
+ const __dirname = dirname(fileURLToPath(import.meta.url));
12
+ const PKG_VERSION = JSON.parse(readFileSync(join(__dirname, '..', '..', 'package.json'), 'utf-8')).version as string;
13
+
8
14
  const TABS = ['Status', 'Settings', 'About'] as const;
9
15
  type Tab = typeof TABS[number];
10
16
 
@@ -34,7 +40,7 @@ export default function App() {
34
40
  {/* Header */}
35
41
  <Box borderStyle="single" borderColor="green" paddingX={1}>
36
42
  <Text bold color="green">SafeClaw </Text>
37
- <Text dimColor>v0.2.0</Text>
43
+ <Text dimColor>v{PKG_VERSION}</Text>
38
44
  </Box>
39
45
 
40
46
  {/* Tab bar */}