openclaw-safeclaw-plugin 0.9.2 → 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/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 detail = errBody.detail ?? `HTTP ${res.status}`;
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 fetch(`${config.serviceUrl}/heartbeat`, {
113
- method: 'POST',
114
- headers: { 'Content-Type': 'application/json' },
115
- body: JSON.stringify({
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
- fetch(`${config.serviceUrl}/heartbeat`, {
142
- method: 'POST',
143
- headers: { 'Content-Type': 'application/json' },
144
- body: JSON.stringify({
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);
@@ -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 detail = errBody.detail ?? `HTTP ${res.status}`;
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 fetch(`${config.serviceUrl}/heartbeat`, {
145
- method: 'POST',
146
- headers: { 'Content-Type': 'application/json' },
147
- body: JSON.stringify({
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
- fetch(`${config.serviceUrl}/heartbeat`, {
175
- method: 'POST',
176
- headers: { 'Content-Type': 'application/json' },
177
- body: JSON.stringify({
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.9.2",
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
  /**