openclaw-safeclaw-plugin 0.9.1 → 1.0.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 -1
- package/dist/cli.js +33 -1
- package/dist/index.js +10 -18
- package/dist/tui/config.js +2 -2
- package/index.ts +10 -18
- package/package.json +1 -1
- package/tui/config.ts +2 -2
package/cli.tsx
CHANGED
|
@@ -134,7 +134,37 @@ if (command === 'connect') {
|
|
|
134
134
|
writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n');
|
|
135
135
|
chmodSync(configPath, 0o600);
|
|
136
136
|
|
|
137
|
-
console.log(`
|
|
137
|
+
console.log(`API key saved to ${configPath}`);
|
|
138
|
+
|
|
139
|
+
// Validate key via handshake
|
|
140
|
+
try {
|
|
141
|
+
const res = await fetch(`${serviceUrl}/handshake`, {
|
|
142
|
+
method: 'POST',
|
|
143
|
+
headers: {
|
|
144
|
+
'Content-Type': 'application/json',
|
|
145
|
+
'Authorization': `Bearer ${apiKey}`,
|
|
146
|
+
},
|
|
147
|
+
body: JSON.stringify({ pluginVersion: '0.1.3', configHash: '' }),
|
|
148
|
+
signal: AbortSignal.timeout(5000),
|
|
149
|
+
});
|
|
150
|
+
if (res.ok) {
|
|
151
|
+
const data = await res.json() as Record<string, unknown>;
|
|
152
|
+
console.log(`Connected! org=${data.orgId}, scope=${data.scope}`);
|
|
153
|
+
} else {
|
|
154
|
+
let detail = `HTTP ${res.status}`;
|
|
155
|
+
try {
|
|
156
|
+
const body = await res.json() as Record<string, unknown>;
|
|
157
|
+
detail = (body.error ?? body.detail ?? detail) as string;
|
|
158
|
+
} catch { /* ignore */ }
|
|
159
|
+
console.warn(`Warning: API key saved but handshake failed — ${detail}`);
|
|
160
|
+
if (res.status === 401) {
|
|
161
|
+
console.warn('The key may be invalid or revoked. Check https://safeclaw.eu/dashboard');
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
} catch {
|
|
165
|
+
console.warn(`Warning: API key saved but could not reach ${serviceUrl}`);
|
|
166
|
+
console.warn('Run "safeclaw status" later to verify the connection.');
|
|
167
|
+
}
|
|
138
168
|
|
|
139
169
|
// Register with OpenClaw
|
|
140
170
|
console.log('Registering SafeClaw plugin with OpenClaw...');
|
package/dist/cli.js
CHANGED
|
@@ -119,7 +119,39 @@ if (command === 'connect') {
|
|
|
119
119
|
mkdirSync(configDir, { recursive: true });
|
|
120
120
|
writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n');
|
|
121
121
|
chmodSync(configPath, 0o600);
|
|
122
|
-
console.log(`
|
|
122
|
+
console.log(`API key saved to ${configPath}`);
|
|
123
|
+
// Validate key via handshake
|
|
124
|
+
try {
|
|
125
|
+
const res = await fetch(`${serviceUrl}/handshake`, {
|
|
126
|
+
method: 'POST',
|
|
127
|
+
headers: {
|
|
128
|
+
'Content-Type': 'application/json',
|
|
129
|
+
'Authorization': `Bearer ${apiKey}`,
|
|
130
|
+
},
|
|
131
|
+
body: JSON.stringify({ pluginVersion: '0.1.3', configHash: '' }),
|
|
132
|
+
signal: AbortSignal.timeout(5000),
|
|
133
|
+
});
|
|
134
|
+
if (res.ok) {
|
|
135
|
+
const data = await res.json();
|
|
136
|
+
console.log(`Connected! org=${data.orgId}, scope=${data.scope}`);
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
let detail = `HTTP ${res.status}`;
|
|
140
|
+
try {
|
|
141
|
+
const body = await res.json();
|
|
142
|
+
detail = (body.error ?? body.detail ?? detail);
|
|
143
|
+
}
|
|
144
|
+
catch { /* ignore */ }
|
|
145
|
+
console.warn(`Warning: API key saved but handshake failed — ${detail}`);
|
|
146
|
+
if (res.status === 401) {
|
|
147
|
+
console.warn('The key may be invalid or revoked. Check https://safeclaw.eu/dashboard');
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
catch {
|
|
152
|
+
console.warn(`Warning: API key saved but could not reach ${serviceUrl}`);
|
|
153
|
+
console.warn('Run "safeclaw status" later to verify the connection.');
|
|
154
|
+
}
|
|
123
155
|
// Register with OpenClaw
|
|
124
156
|
console.log('Registering SafeClaw plugin with OpenClaw...');
|
|
125
157
|
const registered = registerWithOpenClaw();
|
package/dist/index.js
CHANGED
|
@@ -29,7 +29,8 @@ async function post(path, body) {
|
|
|
29
29
|
// Try to parse structured error body from service
|
|
30
30
|
try {
|
|
31
31
|
const errBody = await res.json();
|
|
32
|
-
const
|
|
32
|
+
const rawDetail = errBody.detail ?? `HTTP ${res.status}`;
|
|
33
|
+
const detail = typeof rawDetail === 'string' ? rawDetail : JSON.stringify(rawDetail);
|
|
33
34
|
const hint = errBody.hint ? ` (${errBody.hint})` : '';
|
|
34
35
|
console.warn(`[SafeClaw] ${path}: ${detail}${hint}`);
|
|
35
36
|
}
|
|
@@ -109,15 +110,10 @@ export default {
|
|
|
109
110
|
// Heartbeat watchdog — send config hash to service every 30s
|
|
110
111
|
const sendHeartbeat = async () => {
|
|
111
112
|
try {
|
|
112
|
-
await
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
agentId: config.agentId || 'default',
|
|
117
|
-
configHash: configHash(config),
|
|
118
|
-
status: 'alive',
|
|
119
|
-
}),
|
|
120
|
-
signal: AbortSignal.timeout(config.timeoutMs),
|
|
113
|
+
await post('/heartbeat', {
|
|
114
|
+
agentId: config.agentId || 'default',
|
|
115
|
+
configHash: configHash(config),
|
|
116
|
+
status: 'alive',
|
|
121
117
|
});
|
|
122
118
|
}
|
|
123
119
|
catch {
|
|
@@ -138,14 +134,10 @@ export default {
|
|
|
138
134
|
// Clean shutdown: send shutdown heartbeat and clear interval
|
|
139
135
|
const shutdown = () => {
|
|
140
136
|
clearInterval(heartbeatInterval);
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
agentId: config.agentId || 'default',
|
|
146
|
-
configHash: configHash(config),
|
|
147
|
-
status: 'shutdown',
|
|
148
|
-
}),
|
|
137
|
+
post('/heartbeat', {
|
|
138
|
+
agentId: config.agentId || 'default',
|
|
139
|
+
configHash: configHash(config),
|
|
140
|
+
status: 'shutdown',
|
|
149
141
|
}).catch(() => { });
|
|
150
142
|
};
|
|
151
143
|
process.on('exit', shutdown);
|
package/dist/tui/config.js
CHANGED
|
@@ -105,8 +105,8 @@ export function saveConfig(config) {
|
|
|
105
105
|
existing.enforcement.mode = config.enforcement;
|
|
106
106
|
existing.enforcement.failMode = config.failMode;
|
|
107
107
|
// Ensure parent directory exists
|
|
108
|
-
mkdirSync(dirname(CONFIG_PATH), { recursive: true });
|
|
109
|
-
writeFileSync(CONFIG_PATH, JSON.stringify(existing, null, 2) + '\n', 'utf-8');
|
|
108
|
+
mkdirSync(dirname(CONFIG_PATH), { recursive: true, mode: 0o700 });
|
|
109
|
+
writeFileSync(CONFIG_PATH, JSON.stringify(existing, null, 2) + '\n', { encoding: 'utf-8', mode: 0o600 });
|
|
110
110
|
}
|
|
111
111
|
/**
|
|
112
112
|
* SHA-256 hash of the four TUI-managed config fields.
|
package/index.ts
CHANGED
|
@@ -36,7 +36,8 @@ async function post(path: string, body: Record<string, unknown>): Promise<Record
|
|
|
36
36
|
// Try to parse structured error body from service
|
|
37
37
|
try {
|
|
38
38
|
const errBody = await res.json() as Record<string, unknown>;
|
|
39
|
-
const
|
|
39
|
+
const rawDetail = errBody.detail ?? `HTTP ${res.status}`;
|
|
40
|
+
const detail = typeof rawDetail === 'string' ? rawDetail : JSON.stringify(rawDetail);
|
|
40
41
|
const hint = errBody.hint ? ` (${errBody.hint})` : '';
|
|
41
42
|
console.warn(`[SafeClaw] ${path}: ${detail}${hint}`);
|
|
42
43
|
} catch {
|
|
@@ -141,15 +142,10 @@ export default {
|
|
|
141
142
|
// Heartbeat watchdog — send config hash to service every 30s
|
|
142
143
|
const sendHeartbeat = async () => {
|
|
143
144
|
try {
|
|
144
|
-
await
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
agentId: config.agentId || 'default',
|
|
149
|
-
configHash: configHash(config),
|
|
150
|
-
status: 'alive',
|
|
151
|
-
}),
|
|
152
|
-
signal: AbortSignal.timeout(config.timeoutMs),
|
|
145
|
+
await post('/heartbeat', {
|
|
146
|
+
agentId: config.agentId || 'default',
|
|
147
|
+
configHash: configHash(config),
|
|
148
|
+
status: 'alive',
|
|
153
149
|
});
|
|
154
150
|
} catch {
|
|
155
151
|
// Heartbeat failure is non-fatal
|
|
@@ -171,14 +167,10 @@ export default {
|
|
|
171
167
|
// Clean shutdown: send shutdown heartbeat and clear interval
|
|
172
168
|
const shutdown = () => {
|
|
173
169
|
clearInterval(heartbeatInterval);
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
agentId: config.agentId || 'default',
|
|
179
|
-
configHash: configHash(config),
|
|
180
|
-
status: 'shutdown',
|
|
181
|
-
}),
|
|
170
|
+
post('/heartbeat', {
|
|
171
|
+
agentId: config.agentId || 'default',
|
|
172
|
+
configHash: configHash(config),
|
|
173
|
+
status: 'shutdown',
|
|
182
174
|
}).catch(() => {});
|
|
183
175
|
};
|
|
184
176
|
process.on('exit', shutdown);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openclaw-safeclaw-plugin",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.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/config.ts
CHANGED
|
@@ -116,9 +116,9 @@ export function saveConfig(config: SafeClawConfig): void {
|
|
|
116
116
|
(existing.enforcement as Record<string, unknown>).failMode = config.failMode;
|
|
117
117
|
|
|
118
118
|
// Ensure parent directory exists
|
|
119
|
-
mkdirSync(dirname(CONFIG_PATH), { recursive: true });
|
|
119
|
+
mkdirSync(dirname(CONFIG_PATH), { recursive: true, mode: 0o700 });
|
|
120
120
|
|
|
121
|
-
writeFileSync(CONFIG_PATH, JSON.stringify(existing, null, 2) + '\n', 'utf-8');
|
|
121
|
+
writeFileSync(CONFIG_PATH, JSON.stringify(existing, null, 2) + '\n', { encoding: 'utf-8', mode: 0o600 });
|
|
122
122
|
}
|
|
123
123
|
|
|
124
124
|
/**
|