fastgrc-openclaw 1.0.28 → 1.0.29
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/bin.js +144 -20
- package/dist/bin.mjs +144 -20
- package/dist/plugin.d.mts +9 -1
- package/dist/plugin.d.ts +9 -1
- package/dist/plugin.js +86 -25
- package/dist/plugin.mjs +83 -25
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -24,9 +24,9 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
24
24
|
));
|
|
25
25
|
|
|
26
26
|
// src/bin.ts
|
|
27
|
-
var
|
|
28
|
-
var
|
|
29
|
-
var
|
|
27
|
+
var fs3 = __toESM(require("fs"));
|
|
28
|
+
var os3 = __toESM(require("os"));
|
|
29
|
+
var path3 = __toESM(require("path"));
|
|
30
30
|
|
|
31
31
|
// src/index.ts
|
|
32
32
|
var fs = __toESM(require("fs"));
|
|
@@ -93,21 +93,100 @@ args: ${JSON.stringify(args)}`;
|
|
|
93
93
|
}
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
+
// src/plugin.ts
|
|
97
|
+
var net = __toESM(require("net"));
|
|
98
|
+
var crypto = __toESM(require("crypto"));
|
|
99
|
+
var fs2 = __toESM(require("fs"));
|
|
100
|
+
var os2 = __toESM(require("os"));
|
|
101
|
+
var path2 = __toESM(require("path"));
|
|
102
|
+
function sign(payload, token) {
|
|
103
|
+
const nonce = crypto.randomBytes(16).toString("hex");
|
|
104
|
+
const ts = Math.floor(Date.now() / 1e3).toString();
|
|
105
|
+
const body = JSON.stringify(payload);
|
|
106
|
+
const mac = crypto.createHmac("sha256", Buffer.from(token, "base64")).update(`${nonce}:${ts}:${body}`).digest("hex");
|
|
107
|
+
return { ...payload, nonce, timestamp: ts, hmac: mac };
|
|
108
|
+
}
|
|
109
|
+
function verifyHmac(req, token) {
|
|
110
|
+
try {
|
|
111
|
+
const { nonce, timestamp, hmac, ...rest } = req;
|
|
112
|
+
const expected = crypto.createHmac("sha256", Buffer.from(token, "base64")).update(`${nonce}:${timestamp}:${JSON.stringify(rest)}`).digest("hex");
|
|
113
|
+
return hmac === expected;
|
|
114
|
+
} catch {
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
function readExecApprovalsConfig() {
|
|
119
|
+
const cfgPath = path2.join(os2.homedir(), ".openclaw", "exec-approvals.json");
|
|
120
|
+
try {
|
|
121
|
+
return JSON.parse(fs2.readFileSync(cfgPath, "utf8"));
|
|
122
|
+
} catch {
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
function startExecApprovalsClient(apiKey, policyId, baseUrl) {
|
|
127
|
+
const cfg = readExecApprovalsConfig();
|
|
128
|
+
if (!cfg?.socket?.path || !cfg?.socket?.token) return;
|
|
129
|
+
const { path: sockPath, token } = cfg.socket;
|
|
130
|
+
function connect() {
|
|
131
|
+
const client = net.createConnection(sockPath);
|
|
132
|
+
let buffer = "";
|
|
133
|
+
client.on("connect", () => {
|
|
134
|
+
console.log("[fastgrc] exec-approvals socket connected");
|
|
135
|
+
});
|
|
136
|
+
client.on("data", async (buf) => {
|
|
137
|
+
buffer += buf.toString();
|
|
138
|
+
let req;
|
|
139
|
+
try {
|
|
140
|
+
req = JSON.parse(buffer);
|
|
141
|
+
} catch {
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
buffer = "";
|
|
145
|
+
if (!verifyHmac(req, token)) {
|
|
146
|
+
console.warn("[fastgrc] exec-approvals: HMAC verification failed \u2014 ignoring request");
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
let decision = "allow-once";
|
|
150
|
+
try {
|
|
151
|
+
const result = await evaluate({
|
|
152
|
+
toolName: "Exec",
|
|
153
|
+
args: { command: req.command, rawCommand: req.rawCommand, cwd: req.cwd },
|
|
154
|
+
agentId: req.agentId,
|
|
155
|
+
apiKey,
|
|
156
|
+
policyId,
|
|
157
|
+
baseUrl
|
|
158
|
+
});
|
|
159
|
+
if (result && (result.decision === "block" || result.decision === "require_approval")) {
|
|
160
|
+
decision = "deny";
|
|
161
|
+
const msg = result.policyContext?.matchedRule ? `[fastgrc] exec blocked: [${result.policyContext.matchedRule}] ${result.reasoning}` : `[fastgrc] exec blocked: ${result.reasoning}`;
|
|
162
|
+
console.warn(msg);
|
|
163
|
+
}
|
|
164
|
+
} catch {
|
|
165
|
+
}
|
|
166
|
+
const response = sign({ approvalId: req.approvalId, decision }, token);
|
|
167
|
+
client.write(JSON.stringify(response));
|
|
168
|
+
});
|
|
169
|
+
client.on("error", () => setTimeout(connect, 5e3));
|
|
170
|
+
client.on("close", () => setTimeout(connect, 5e3));
|
|
171
|
+
}
|
|
172
|
+
connect();
|
|
173
|
+
}
|
|
174
|
+
|
|
96
175
|
// src/bin.ts
|
|
97
|
-
var CONFIG_PATH =
|
|
176
|
+
var CONFIG_PATH = path3.join(os3.homedir(), ".fastgrc.json");
|
|
98
177
|
function readConfig() {
|
|
99
178
|
try {
|
|
100
|
-
return JSON.parse(
|
|
179
|
+
return JSON.parse(fs3.readFileSync(CONFIG_PATH, "utf8"));
|
|
101
180
|
} catch {
|
|
102
181
|
return {};
|
|
103
182
|
}
|
|
104
183
|
}
|
|
105
184
|
function writeConfig(data) {
|
|
106
|
-
|
|
185
|
+
fs3.writeFileSync(CONFIG_PATH, JSON.stringify(data, null, 2), { mode: 384 });
|
|
107
186
|
}
|
|
108
187
|
function computeHandler() {
|
|
109
188
|
const binPath = process.argv[1];
|
|
110
|
-
const homeDir =
|
|
189
|
+
const homeDir = os3.homedir();
|
|
111
190
|
return { binPath, homeDir, handlerStr: `HOME=${homeDir} node ${binPath}` };
|
|
112
191
|
}
|
|
113
192
|
function printTestSnippet(handlerStr) {
|
|
@@ -121,7 +200,7 @@ Test it directly (bypasses OpenClaw):
|
|
|
121
200
|
);
|
|
122
201
|
}
|
|
123
202
|
function doInstallHook(targetDir) {
|
|
124
|
-
const hookMdPath =
|
|
203
|
+
const hookMdPath = path3.join(targetDir, "HOOK.md");
|
|
125
204
|
const { handlerStr } = computeHandler();
|
|
126
205
|
const HOOK_ENTRY = ` - matcher: PreToolUse
|
|
127
206
|
handler: "${handlerStr}"
|
|
@@ -131,8 +210,8 @@ function doInstallHook(targetDir) {
|
|
|
131
210
|
if (!hasKey) {
|
|
132
211
|
process.stdout.write("\u26A0 No API key set yet. Run: fastgrc-hook set-key fgrc_k1_your_key\n\n");
|
|
133
212
|
}
|
|
134
|
-
if (!
|
|
135
|
-
|
|
213
|
+
if (!fs3.existsSync(hookMdPath)) {
|
|
214
|
+
fs3.writeFileSync(hookMdPath, HOOK_BLOCK, "utf8");
|
|
136
215
|
process.stdout.write(`\u2713 Created ${hookMdPath}
|
|
137
216
|
Handler: ${handlerStr}
|
|
138
217
|
|
|
@@ -141,7 +220,7 @@ Restart OpenClaw \u2014 FastGRC will evaluate every tool call.
|
|
|
141
220
|
printTestSnippet(handlerStr);
|
|
142
221
|
return;
|
|
143
222
|
}
|
|
144
|
-
const existing =
|
|
223
|
+
const existing = fs3.readFileSync(hookMdPath, "utf8");
|
|
145
224
|
if (existing.includes(handlerStr)) {
|
|
146
225
|
process.stdout.write(`\u2713 FastGRC hook already up to date in ${hookMdPath}
|
|
147
226
|
Handler: ${handlerStr}
|
|
@@ -154,7 +233,7 @@ Restart OpenClaw \u2014 FastGRC will evaluate every tool call.
|
|
|
154
233
|
/handler:\s*"[^"]*(?:fastgrc-hook|fastgrc-openclaw)[^"]*"/,
|
|
155
234
|
`handler: "${handlerStr}"`
|
|
156
235
|
);
|
|
157
|
-
|
|
236
|
+
fs3.writeFileSync(hookMdPath, patched, "utf8");
|
|
158
237
|
process.stdout.write(`\u2713 Updated handler in ${hookMdPath} \u2014 now uses absolute path.
|
|
159
238
|
Handler: ${handlerStr}
|
|
160
239
|
|
|
@@ -173,7 +252,7 @@ ${HOOK_ENTRY}`;
|
|
|
173
252
|
} else {
|
|
174
253
|
updated = HOOK_BLOCK + "\n" + existing;
|
|
175
254
|
}
|
|
176
|
-
|
|
255
|
+
fs3.writeFileSync(hookMdPath, updated, "utf8");
|
|
177
256
|
process.stdout.write(`\u2713 Updated ${hookMdPath} \u2014 FastGRC hook added.
|
|
178
257
|
Handler: ${handlerStr}
|
|
179
258
|
|
|
@@ -182,12 +261,12 @@ Restart OpenClaw to activate.
|
|
|
182
261
|
printTestSnippet(handlerStr);
|
|
183
262
|
}
|
|
184
263
|
function doUninstallHook(targetDir) {
|
|
185
|
-
const hookMdPath =
|
|
186
|
-
if (!
|
|
264
|
+
const hookMdPath = path3.join(targetDir, "HOOK.md");
|
|
265
|
+
if (!fs3.existsSync(hookMdPath)) {
|
|
187
266
|
process.stdout.write("No HOOK.md found \u2014 nothing to remove.\n");
|
|
188
267
|
return;
|
|
189
268
|
}
|
|
190
|
-
const existing =
|
|
269
|
+
const existing = fs3.readFileSync(hookMdPath, "utf8");
|
|
191
270
|
const patched = existing.replace(
|
|
192
271
|
/[ \t]*-[ \t]*matcher:[ \t]*PreToolUse\n[ \t]*handler:[ \t]*"[^"]*(?:fastgrc-hook|fastgrc-openclaw)[^"]*"\n?/,
|
|
193
272
|
""
|
|
@@ -195,11 +274,36 @@ function doUninstallHook(targetDir) {
|
|
|
195
274
|
if (patched === existing) {
|
|
196
275
|
process.stdout.write("FastGRC hook not found in HOOK.md \u2014 nothing to remove.\n");
|
|
197
276
|
} else {
|
|
198
|
-
|
|
277
|
+
fs3.writeFileSync(hookMdPath, patched, "utf8");
|
|
199
278
|
process.stdout.write(`\u2713 FastGRC hook removed from ${hookMdPath}
|
|
200
279
|
`);
|
|
201
280
|
}
|
|
202
281
|
}
|
|
282
|
+
function doConfigureExecApprovals() {
|
|
283
|
+
const cfgPath = path3.join(os3.homedir(), ".openclaw", "exec-approvals.json");
|
|
284
|
+
if (!fs3.existsSync(cfgPath)) {
|
|
285
|
+
process.stdout.write(`\u26A0 ${cfgPath} not found \u2014 skipping exec-approvals config (OpenClaw may not be installed here).
|
|
286
|
+
`);
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
let existing = {};
|
|
290
|
+
try {
|
|
291
|
+
existing = JSON.parse(fs3.readFileSync(cfgPath, "utf8"));
|
|
292
|
+
} catch {
|
|
293
|
+
}
|
|
294
|
+
const updated = {
|
|
295
|
+
...existing,
|
|
296
|
+
defaults: {
|
|
297
|
+
security: "deny",
|
|
298
|
+
ask: "always",
|
|
299
|
+
askFallback: "deny",
|
|
300
|
+
autoAllowSkills: false
|
|
301
|
+
}
|
|
302
|
+
};
|
|
303
|
+
fs3.writeFileSync(cfgPath, JSON.stringify(updated, null, 2), "utf8");
|
|
304
|
+
process.stdout.write(`\u2713 exec-approvals.json configured \u2014 all webchat execs routed through FastGRC.
|
|
305
|
+
`);
|
|
306
|
+
}
|
|
203
307
|
var [, , cmd, arg] = process.argv;
|
|
204
308
|
if (cmd === "set-key") {
|
|
205
309
|
if (!arg) {
|
|
@@ -294,10 +398,30 @@ if (cmd === "setup") {
|
|
|
294
398
|
process.stdout.write(" (no policy ID \u2014 org-wide default will be used)\n");
|
|
295
399
|
}
|
|
296
400
|
doInstallHook(process.cwd());
|
|
297
|
-
|
|
298
|
-
process.stdout.write(
|
|
401
|
+
doConfigureExecApprovals();
|
|
402
|
+
process.stdout.write("\n\u2713 Config, HOOK.md, and exec-approvals done.\n");
|
|
403
|
+
process.stdout.write('Restart OpenClaw, then run "fastgrc-hook test" to verify.\n');
|
|
299
404
|
process.exit(0);
|
|
300
405
|
}
|
|
406
|
+
if (cmd === "serve-approvals") {
|
|
407
|
+
const apiKey = resolveApiKey();
|
|
408
|
+
if (!apiKey) {
|
|
409
|
+
process.stderr.write("No API key configured. Run: fastgrc-hook set-key fgrc_k1_...\n");
|
|
410
|
+
process.exit(1);
|
|
411
|
+
}
|
|
412
|
+
const cfg = readExecApprovalsConfig();
|
|
413
|
+
if (!cfg?.socket?.path || !cfg?.socket?.token) {
|
|
414
|
+
process.stderr.write(
|
|
415
|
+
'No exec-approvals socket configured.\nRun "fastgrc-hook setup" to configure OpenClaw exec-approvals, then restart OpenClaw.\n'
|
|
416
|
+
);
|
|
417
|
+
process.exit(1);
|
|
418
|
+
}
|
|
419
|
+
const policyId = process.env.FASTGRC_POLICY_ID ?? readConfig().policyId;
|
|
420
|
+
const baseUrl = process.env.FASTGRC_BASE_URL ?? "https://app.fastgrc.ai";
|
|
421
|
+
process.stdout.write(`[fastgrc] serve-approvals running \u2014 listening on ${cfg.socket.path}
|
|
422
|
+
`);
|
|
423
|
+
startExecApprovalsClient(apiKey, policyId, baseUrl);
|
|
424
|
+
}
|
|
301
425
|
if (cmd === "uninstall") {
|
|
302
426
|
const targetDir = arg || process.cwd();
|
|
303
427
|
const cfg = readConfig();
|
|
@@ -364,7 +488,7 @@ if (cmd === "test") {
|
|
|
364
488
|
`);
|
|
365
489
|
process.exit(0);
|
|
366
490
|
}
|
|
367
|
-
if (cmd === "test" || cmd === "where" || cmd === "which-hook" || cmd === "setup" || cmd === "uninstall" || cmd === "install-hook" || cmd === "uninstall-hook") {
|
|
491
|
+
if (cmd === "test" || cmd === "where" || cmd === "which-hook" || cmd === "setup" || cmd === "uninstall" || cmd === "install-hook" || cmd === "uninstall-hook" || cmd === "serve-approvals") {
|
|
368
492
|
} else {
|
|
369
493
|
const apiKey = resolveApiKey();
|
|
370
494
|
if (!apiKey) {
|
package/dist/bin.mjs
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/bin.ts
|
|
4
|
-
import * as
|
|
5
|
-
import * as
|
|
6
|
-
import * as
|
|
4
|
+
import * as fs3 from "fs";
|
|
5
|
+
import * as os3 from "os";
|
|
6
|
+
import * as path3 from "path";
|
|
7
7
|
|
|
8
8
|
// src/index.ts
|
|
9
9
|
import * as fs from "fs";
|
|
@@ -70,21 +70,100 @@ args: ${JSON.stringify(args)}`;
|
|
|
70
70
|
}
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
+
// src/plugin.ts
|
|
74
|
+
import * as net from "net";
|
|
75
|
+
import * as crypto from "crypto";
|
|
76
|
+
import * as fs2 from "fs";
|
|
77
|
+
import * as os2 from "os";
|
|
78
|
+
import * as path2 from "path";
|
|
79
|
+
function sign(payload, token) {
|
|
80
|
+
const nonce = crypto.randomBytes(16).toString("hex");
|
|
81
|
+
const ts = Math.floor(Date.now() / 1e3).toString();
|
|
82
|
+
const body = JSON.stringify(payload);
|
|
83
|
+
const mac = crypto.createHmac("sha256", Buffer.from(token, "base64")).update(`${nonce}:${ts}:${body}`).digest("hex");
|
|
84
|
+
return { ...payload, nonce, timestamp: ts, hmac: mac };
|
|
85
|
+
}
|
|
86
|
+
function verifyHmac(req, token) {
|
|
87
|
+
try {
|
|
88
|
+
const { nonce, timestamp, hmac, ...rest } = req;
|
|
89
|
+
const expected = crypto.createHmac("sha256", Buffer.from(token, "base64")).update(`${nonce}:${timestamp}:${JSON.stringify(rest)}`).digest("hex");
|
|
90
|
+
return hmac === expected;
|
|
91
|
+
} catch {
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
function readExecApprovalsConfig() {
|
|
96
|
+
const cfgPath = path2.join(os2.homedir(), ".openclaw", "exec-approvals.json");
|
|
97
|
+
try {
|
|
98
|
+
return JSON.parse(fs2.readFileSync(cfgPath, "utf8"));
|
|
99
|
+
} catch {
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
function startExecApprovalsClient(apiKey, policyId, baseUrl) {
|
|
104
|
+
const cfg = readExecApprovalsConfig();
|
|
105
|
+
if (!cfg?.socket?.path || !cfg?.socket?.token) return;
|
|
106
|
+
const { path: sockPath, token } = cfg.socket;
|
|
107
|
+
function connect() {
|
|
108
|
+
const client = net.createConnection(sockPath);
|
|
109
|
+
let buffer = "";
|
|
110
|
+
client.on("connect", () => {
|
|
111
|
+
console.log("[fastgrc] exec-approvals socket connected");
|
|
112
|
+
});
|
|
113
|
+
client.on("data", async (buf) => {
|
|
114
|
+
buffer += buf.toString();
|
|
115
|
+
let req;
|
|
116
|
+
try {
|
|
117
|
+
req = JSON.parse(buffer);
|
|
118
|
+
} catch {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
buffer = "";
|
|
122
|
+
if (!verifyHmac(req, token)) {
|
|
123
|
+
console.warn("[fastgrc] exec-approvals: HMAC verification failed \u2014 ignoring request");
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
let decision = "allow-once";
|
|
127
|
+
try {
|
|
128
|
+
const result = await evaluate({
|
|
129
|
+
toolName: "Exec",
|
|
130
|
+
args: { command: req.command, rawCommand: req.rawCommand, cwd: req.cwd },
|
|
131
|
+
agentId: req.agentId,
|
|
132
|
+
apiKey,
|
|
133
|
+
policyId,
|
|
134
|
+
baseUrl
|
|
135
|
+
});
|
|
136
|
+
if (result && (result.decision === "block" || result.decision === "require_approval")) {
|
|
137
|
+
decision = "deny";
|
|
138
|
+
const msg = result.policyContext?.matchedRule ? `[fastgrc] exec blocked: [${result.policyContext.matchedRule}] ${result.reasoning}` : `[fastgrc] exec blocked: ${result.reasoning}`;
|
|
139
|
+
console.warn(msg);
|
|
140
|
+
}
|
|
141
|
+
} catch {
|
|
142
|
+
}
|
|
143
|
+
const response = sign({ approvalId: req.approvalId, decision }, token);
|
|
144
|
+
client.write(JSON.stringify(response));
|
|
145
|
+
});
|
|
146
|
+
client.on("error", () => setTimeout(connect, 5e3));
|
|
147
|
+
client.on("close", () => setTimeout(connect, 5e3));
|
|
148
|
+
}
|
|
149
|
+
connect();
|
|
150
|
+
}
|
|
151
|
+
|
|
73
152
|
// src/bin.ts
|
|
74
|
-
var CONFIG_PATH =
|
|
153
|
+
var CONFIG_PATH = path3.join(os3.homedir(), ".fastgrc.json");
|
|
75
154
|
function readConfig() {
|
|
76
155
|
try {
|
|
77
|
-
return JSON.parse(
|
|
156
|
+
return JSON.parse(fs3.readFileSync(CONFIG_PATH, "utf8"));
|
|
78
157
|
} catch {
|
|
79
158
|
return {};
|
|
80
159
|
}
|
|
81
160
|
}
|
|
82
161
|
function writeConfig(data) {
|
|
83
|
-
|
|
162
|
+
fs3.writeFileSync(CONFIG_PATH, JSON.stringify(data, null, 2), { mode: 384 });
|
|
84
163
|
}
|
|
85
164
|
function computeHandler() {
|
|
86
165
|
const binPath = process.argv[1];
|
|
87
|
-
const homeDir =
|
|
166
|
+
const homeDir = os3.homedir();
|
|
88
167
|
return { binPath, homeDir, handlerStr: `HOME=${homeDir} node ${binPath}` };
|
|
89
168
|
}
|
|
90
169
|
function printTestSnippet(handlerStr) {
|
|
@@ -98,7 +177,7 @@ Test it directly (bypasses OpenClaw):
|
|
|
98
177
|
);
|
|
99
178
|
}
|
|
100
179
|
function doInstallHook(targetDir) {
|
|
101
|
-
const hookMdPath =
|
|
180
|
+
const hookMdPath = path3.join(targetDir, "HOOK.md");
|
|
102
181
|
const { handlerStr } = computeHandler();
|
|
103
182
|
const HOOK_ENTRY = ` - matcher: PreToolUse
|
|
104
183
|
handler: "${handlerStr}"
|
|
@@ -108,8 +187,8 @@ function doInstallHook(targetDir) {
|
|
|
108
187
|
if (!hasKey) {
|
|
109
188
|
process.stdout.write("\u26A0 No API key set yet. Run: fastgrc-hook set-key fgrc_k1_your_key\n\n");
|
|
110
189
|
}
|
|
111
|
-
if (!
|
|
112
|
-
|
|
190
|
+
if (!fs3.existsSync(hookMdPath)) {
|
|
191
|
+
fs3.writeFileSync(hookMdPath, HOOK_BLOCK, "utf8");
|
|
113
192
|
process.stdout.write(`\u2713 Created ${hookMdPath}
|
|
114
193
|
Handler: ${handlerStr}
|
|
115
194
|
|
|
@@ -118,7 +197,7 @@ Restart OpenClaw \u2014 FastGRC will evaluate every tool call.
|
|
|
118
197
|
printTestSnippet(handlerStr);
|
|
119
198
|
return;
|
|
120
199
|
}
|
|
121
|
-
const existing =
|
|
200
|
+
const existing = fs3.readFileSync(hookMdPath, "utf8");
|
|
122
201
|
if (existing.includes(handlerStr)) {
|
|
123
202
|
process.stdout.write(`\u2713 FastGRC hook already up to date in ${hookMdPath}
|
|
124
203
|
Handler: ${handlerStr}
|
|
@@ -131,7 +210,7 @@ Restart OpenClaw \u2014 FastGRC will evaluate every tool call.
|
|
|
131
210
|
/handler:\s*"[^"]*(?:fastgrc-hook|fastgrc-openclaw)[^"]*"/,
|
|
132
211
|
`handler: "${handlerStr}"`
|
|
133
212
|
);
|
|
134
|
-
|
|
213
|
+
fs3.writeFileSync(hookMdPath, patched, "utf8");
|
|
135
214
|
process.stdout.write(`\u2713 Updated handler in ${hookMdPath} \u2014 now uses absolute path.
|
|
136
215
|
Handler: ${handlerStr}
|
|
137
216
|
|
|
@@ -150,7 +229,7 @@ ${HOOK_ENTRY}`;
|
|
|
150
229
|
} else {
|
|
151
230
|
updated = HOOK_BLOCK + "\n" + existing;
|
|
152
231
|
}
|
|
153
|
-
|
|
232
|
+
fs3.writeFileSync(hookMdPath, updated, "utf8");
|
|
154
233
|
process.stdout.write(`\u2713 Updated ${hookMdPath} \u2014 FastGRC hook added.
|
|
155
234
|
Handler: ${handlerStr}
|
|
156
235
|
|
|
@@ -159,12 +238,12 @@ Restart OpenClaw to activate.
|
|
|
159
238
|
printTestSnippet(handlerStr);
|
|
160
239
|
}
|
|
161
240
|
function doUninstallHook(targetDir) {
|
|
162
|
-
const hookMdPath =
|
|
163
|
-
if (!
|
|
241
|
+
const hookMdPath = path3.join(targetDir, "HOOK.md");
|
|
242
|
+
if (!fs3.existsSync(hookMdPath)) {
|
|
164
243
|
process.stdout.write("No HOOK.md found \u2014 nothing to remove.\n");
|
|
165
244
|
return;
|
|
166
245
|
}
|
|
167
|
-
const existing =
|
|
246
|
+
const existing = fs3.readFileSync(hookMdPath, "utf8");
|
|
168
247
|
const patched = existing.replace(
|
|
169
248
|
/[ \t]*-[ \t]*matcher:[ \t]*PreToolUse\n[ \t]*handler:[ \t]*"[^"]*(?:fastgrc-hook|fastgrc-openclaw)[^"]*"\n?/,
|
|
170
249
|
""
|
|
@@ -172,11 +251,36 @@ function doUninstallHook(targetDir) {
|
|
|
172
251
|
if (patched === existing) {
|
|
173
252
|
process.stdout.write("FastGRC hook not found in HOOK.md \u2014 nothing to remove.\n");
|
|
174
253
|
} else {
|
|
175
|
-
|
|
254
|
+
fs3.writeFileSync(hookMdPath, patched, "utf8");
|
|
176
255
|
process.stdout.write(`\u2713 FastGRC hook removed from ${hookMdPath}
|
|
177
256
|
`);
|
|
178
257
|
}
|
|
179
258
|
}
|
|
259
|
+
function doConfigureExecApprovals() {
|
|
260
|
+
const cfgPath = path3.join(os3.homedir(), ".openclaw", "exec-approvals.json");
|
|
261
|
+
if (!fs3.existsSync(cfgPath)) {
|
|
262
|
+
process.stdout.write(`\u26A0 ${cfgPath} not found \u2014 skipping exec-approvals config (OpenClaw may not be installed here).
|
|
263
|
+
`);
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
let existing = {};
|
|
267
|
+
try {
|
|
268
|
+
existing = JSON.parse(fs3.readFileSync(cfgPath, "utf8"));
|
|
269
|
+
} catch {
|
|
270
|
+
}
|
|
271
|
+
const updated = {
|
|
272
|
+
...existing,
|
|
273
|
+
defaults: {
|
|
274
|
+
security: "deny",
|
|
275
|
+
ask: "always",
|
|
276
|
+
askFallback: "deny",
|
|
277
|
+
autoAllowSkills: false
|
|
278
|
+
}
|
|
279
|
+
};
|
|
280
|
+
fs3.writeFileSync(cfgPath, JSON.stringify(updated, null, 2), "utf8");
|
|
281
|
+
process.stdout.write(`\u2713 exec-approvals.json configured \u2014 all webchat execs routed through FastGRC.
|
|
282
|
+
`);
|
|
283
|
+
}
|
|
180
284
|
var [, , cmd, arg] = process.argv;
|
|
181
285
|
if (cmd === "set-key") {
|
|
182
286
|
if (!arg) {
|
|
@@ -271,10 +375,30 @@ if (cmd === "setup") {
|
|
|
271
375
|
process.stdout.write(" (no policy ID \u2014 org-wide default will be used)\n");
|
|
272
376
|
}
|
|
273
377
|
doInstallHook(process.cwd());
|
|
274
|
-
|
|
275
|
-
process.stdout.write(
|
|
378
|
+
doConfigureExecApprovals();
|
|
379
|
+
process.stdout.write("\n\u2713 Config, HOOK.md, and exec-approvals done.\n");
|
|
380
|
+
process.stdout.write('Restart OpenClaw, then run "fastgrc-hook test" to verify.\n');
|
|
276
381
|
process.exit(0);
|
|
277
382
|
}
|
|
383
|
+
if (cmd === "serve-approvals") {
|
|
384
|
+
const apiKey = resolveApiKey();
|
|
385
|
+
if (!apiKey) {
|
|
386
|
+
process.stderr.write("No API key configured. Run: fastgrc-hook set-key fgrc_k1_...\n");
|
|
387
|
+
process.exit(1);
|
|
388
|
+
}
|
|
389
|
+
const cfg = readExecApprovalsConfig();
|
|
390
|
+
if (!cfg?.socket?.path || !cfg?.socket?.token) {
|
|
391
|
+
process.stderr.write(
|
|
392
|
+
'No exec-approvals socket configured.\nRun "fastgrc-hook setup" to configure OpenClaw exec-approvals, then restart OpenClaw.\n'
|
|
393
|
+
);
|
|
394
|
+
process.exit(1);
|
|
395
|
+
}
|
|
396
|
+
const policyId = process.env.FASTGRC_POLICY_ID ?? readConfig().policyId;
|
|
397
|
+
const baseUrl = process.env.FASTGRC_BASE_URL ?? "https://app.fastgrc.ai";
|
|
398
|
+
process.stdout.write(`[fastgrc] serve-approvals running \u2014 listening on ${cfg.socket.path}
|
|
399
|
+
`);
|
|
400
|
+
startExecApprovalsClient(apiKey, policyId, baseUrl);
|
|
401
|
+
}
|
|
278
402
|
if (cmd === "uninstall") {
|
|
279
403
|
const targetDir = arg || process.cwd();
|
|
280
404
|
const cfg = readConfig();
|
|
@@ -341,7 +465,7 @@ if (cmd === "test") {
|
|
|
341
465
|
`);
|
|
342
466
|
process.exit(0);
|
|
343
467
|
}
|
|
344
|
-
if (cmd === "test" || cmd === "where" || cmd === "which-hook" || cmd === "setup" || cmd === "uninstall" || cmd === "install-hook" || cmd === "uninstall-hook") {
|
|
468
|
+
if (cmd === "test" || cmd === "where" || cmd === "which-hook" || cmd === "setup" || cmd === "uninstall" || cmd === "install-hook" || cmd === "uninstall-hook" || cmd === "serve-approvals") {
|
|
345
469
|
} else {
|
|
346
470
|
const apiKey = resolveApiKey();
|
|
347
471
|
if (!apiKey) {
|
package/dist/plugin.d.mts
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
interface ExecApprovalsConfig {
|
|
2
|
+
socket?: {
|
|
3
|
+
path?: string;
|
|
4
|
+
token?: string;
|
|
5
|
+
};
|
|
6
|
+
}
|
|
7
|
+
declare function readExecApprovalsConfig(): ExecApprovalsConfig | null;
|
|
8
|
+
declare function startExecApprovalsClient(apiKey: string, policyId?: string, baseUrl?: string): void;
|
|
1
9
|
declare const pluginEntry: {
|
|
2
10
|
id: string;
|
|
3
11
|
name: string;
|
|
@@ -5,4 +13,4 @@ declare const pluginEntry: {
|
|
|
5
13
|
register(api: any): void;
|
|
6
14
|
};
|
|
7
15
|
|
|
8
|
-
export { pluginEntry as default };
|
|
16
|
+
export { pluginEntry as default, readExecApprovalsConfig, startExecApprovalsClient };
|
package/dist/plugin.d.ts
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
interface ExecApprovalsConfig {
|
|
2
|
+
socket?: {
|
|
3
|
+
path?: string;
|
|
4
|
+
token?: string;
|
|
5
|
+
};
|
|
6
|
+
}
|
|
7
|
+
declare function readExecApprovalsConfig(): ExecApprovalsConfig | null;
|
|
8
|
+
declare function startExecApprovalsClient(apiKey: string, policyId?: string, baseUrl?: string): void;
|
|
1
9
|
declare const pluginEntry: {
|
|
2
10
|
id: string;
|
|
3
11
|
name: string;
|
|
@@ -5,4 +13,4 @@ declare const pluginEntry: {
|
|
|
5
13
|
register(api: any): void;
|
|
6
14
|
};
|
|
7
15
|
|
|
8
|
-
export { pluginEntry as default };
|
|
16
|
+
export { pluginEntry as default, readExecApprovalsConfig, startExecApprovalsClient };
|
package/dist/plugin.js
CHANGED
|
@@ -30,9 +30,16 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/plugin.ts
|
|
31
31
|
var plugin_exports = {};
|
|
32
32
|
__export(plugin_exports, {
|
|
33
|
-
default: () => plugin_default
|
|
33
|
+
default: () => plugin_default,
|
|
34
|
+
readExecApprovalsConfig: () => readExecApprovalsConfig,
|
|
35
|
+
startExecApprovalsClient: () => startExecApprovalsClient
|
|
34
36
|
});
|
|
35
37
|
module.exports = __toCommonJS(plugin_exports);
|
|
38
|
+
var net = __toESM(require("net"));
|
|
39
|
+
var crypto = __toESM(require("crypto"));
|
|
40
|
+
var fs2 = __toESM(require("fs"));
|
|
41
|
+
var os2 = __toESM(require("os"));
|
|
42
|
+
var path2 = __toESM(require("path"));
|
|
36
43
|
|
|
37
44
|
// src/index.ts
|
|
38
45
|
var fs = __toESM(require("fs"));
|
|
@@ -114,6 +121,78 @@ args: ${JSON.stringify(args)}`;
|
|
|
114
121
|
// src/plugin.ts
|
|
115
122
|
var DEFAULT_BASE_URL2 = "https://app.fastgrc.ai";
|
|
116
123
|
var DEFAULT_TIMEOUT_MS2 = 3e3;
|
|
124
|
+
function sign(payload, token) {
|
|
125
|
+
const nonce = crypto.randomBytes(16).toString("hex");
|
|
126
|
+
const ts = Math.floor(Date.now() / 1e3).toString();
|
|
127
|
+
const body = JSON.stringify(payload);
|
|
128
|
+
const mac = crypto.createHmac("sha256", Buffer.from(token, "base64")).update(`${nonce}:${ts}:${body}`).digest("hex");
|
|
129
|
+
return { ...payload, nonce, timestamp: ts, hmac: mac };
|
|
130
|
+
}
|
|
131
|
+
function verifyHmac(req, token) {
|
|
132
|
+
try {
|
|
133
|
+
const { nonce, timestamp, hmac, ...rest } = req;
|
|
134
|
+
const expected = crypto.createHmac("sha256", Buffer.from(token, "base64")).update(`${nonce}:${timestamp}:${JSON.stringify(rest)}`).digest("hex");
|
|
135
|
+
return hmac === expected;
|
|
136
|
+
} catch {
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
function readExecApprovalsConfig() {
|
|
141
|
+
const cfgPath = path2.join(os2.homedir(), ".openclaw", "exec-approvals.json");
|
|
142
|
+
try {
|
|
143
|
+
return JSON.parse(fs2.readFileSync(cfgPath, "utf8"));
|
|
144
|
+
} catch {
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
function startExecApprovalsClient(apiKey, policyId, baseUrl) {
|
|
149
|
+
const cfg = readExecApprovalsConfig();
|
|
150
|
+
if (!cfg?.socket?.path || !cfg?.socket?.token) return;
|
|
151
|
+
const { path: sockPath, token } = cfg.socket;
|
|
152
|
+
function connect() {
|
|
153
|
+
const client = net.createConnection(sockPath);
|
|
154
|
+
let buffer = "";
|
|
155
|
+
client.on("connect", () => {
|
|
156
|
+
console.log("[fastgrc] exec-approvals socket connected");
|
|
157
|
+
});
|
|
158
|
+
client.on("data", async (buf) => {
|
|
159
|
+
buffer += buf.toString();
|
|
160
|
+
let req;
|
|
161
|
+
try {
|
|
162
|
+
req = JSON.parse(buffer);
|
|
163
|
+
} catch {
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
buffer = "";
|
|
167
|
+
if (!verifyHmac(req, token)) {
|
|
168
|
+
console.warn("[fastgrc] exec-approvals: HMAC verification failed \u2014 ignoring request");
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
let decision = "allow-once";
|
|
172
|
+
try {
|
|
173
|
+
const result = await evaluate({
|
|
174
|
+
toolName: "Exec",
|
|
175
|
+
args: { command: req.command, rawCommand: req.rawCommand, cwd: req.cwd },
|
|
176
|
+
agentId: req.agentId,
|
|
177
|
+
apiKey,
|
|
178
|
+
policyId,
|
|
179
|
+
baseUrl
|
|
180
|
+
});
|
|
181
|
+
if (result && (result.decision === "block" || result.decision === "require_approval")) {
|
|
182
|
+
decision = "deny";
|
|
183
|
+
const msg = result.policyContext?.matchedRule ? `[fastgrc] exec blocked: [${result.policyContext.matchedRule}] ${result.reasoning}` : `[fastgrc] exec blocked: ${result.reasoning}`;
|
|
184
|
+
console.warn(msg);
|
|
185
|
+
}
|
|
186
|
+
} catch {
|
|
187
|
+
}
|
|
188
|
+
const response = sign({ approvalId: req.approvalId, decision }, token);
|
|
189
|
+
client.write(JSON.stringify(response));
|
|
190
|
+
});
|
|
191
|
+
client.on("error", () => setTimeout(connect, 5e3));
|
|
192
|
+
client.on("close", () => setTimeout(connect, 5e3));
|
|
193
|
+
}
|
|
194
|
+
connect();
|
|
195
|
+
}
|
|
117
196
|
var pluginEntry = {
|
|
118
197
|
id: "fastgrc",
|
|
119
198
|
name: "FastGRC Policy Router",
|
|
@@ -128,31 +207,7 @@ var pluginEntry = {
|
|
|
128
207
|
return;
|
|
129
208
|
}
|
|
130
209
|
console.log("[fastgrc] Plugin registered \u2014 before_tool_call hook active");
|
|
131
|
-
const probeEvents = [
|
|
132
|
-
"exec",
|
|
133
|
-
"before_exec",
|
|
134
|
-
"shell",
|
|
135
|
-
"before_shell",
|
|
136
|
-
"command",
|
|
137
|
-
"before_command",
|
|
138
|
-
"run",
|
|
139
|
-
"before_run",
|
|
140
|
-
"tool_call",
|
|
141
|
-
"before_tool",
|
|
142
|
-
"action",
|
|
143
|
-
"before_action",
|
|
144
|
-
"request",
|
|
145
|
-
"before_request",
|
|
146
|
-
"call",
|
|
147
|
-
"before_call"
|
|
148
|
-
];
|
|
149
|
-
for (const evt of probeEvents) {
|
|
150
|
-
api.on(evt, (...args) => {
|
|
151
|
-
console.log(`[fastgrc:hit] ${evt}`, JSON.stringify(args[0])?.slice(0, 120));
|
|
152
|
-
});
|
|
153
|
-
}
|
|
154
210
|
api.on("before_tool_call", async (event, ctx) => {
|
|
155
|
-
console.log(`[fastgrc] before_tool_call fired: ${event.toolName}`);
|
|
156
211
|
const result = await evaluate({
|
|
157
212
|
toolName: event.toolName,
|
|
158
213
|
args: event.params,
|
|
@@ -182,7 +237,13 @@ var pluginEntry = {
|
|
|
182
237
|
}
|
|
183
238
|
return void 0;
|
|
184
239
|
});
|
|
240
|
+
startExecApprovalsClient(apiKey, policyId, DEFAULT_BASE_URL2);
|
|
185
241
|
}
|
|
186
242
|
};
|
|
187
243
|
var plugin_default = pluginEntry;
|
|
244
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
245
|
+
0 && (module.exports = {
|
|
246
|
+
readExecApprovalsConfig,
|
|
247
|
+
startExecApprovalsClient
|
|
248
|
+
});
|
|
188
249
|
module.exports = module.exports.default ?? module.exports;
|
package/dist/plugin.mjs
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
// src/plugin.ts
|
|
2
|
+
import * as net from "net";
|
|
3
|
+
import * as crypto from "crypto";
|
|
4
|
+
import * as fs2 from "fs";
|
|
5
|
+
import * as os2 from "os";
|
|
6
|
+
import * as path2 from "path";
|
|
7
|
+
|
|
1
8
|
// src/index.ts
|
|
2
9
|
import * as fs from "fs";
|
|
3
10
|
import * as os from "os";
|
|
@@ -78,6 +85,78 @@ args: ${JSON.stringify(args)}`;
|
|
|
78
85
|
// src/plugin.ts
|
|
79
86
|
var DEFAULT_BASE_URL2 = "https://app.fastgrc.ai";
|
|
80
87
|
var DEFAULT_TIMEOUT_MS2 = 3e3;
|
|
88
|
+
function sign(payload, token) {
|
|
89
|
+
const nonce = crypto.randomBytes(16).toString("hex");
|
|
90
|
+
const ts = Math.floor(Date.now() / 1e3).toString();
|
|
91
|
+
const body = JSON.stringify(payload);
|
|
92
|
+
const mac = crypto.createHmac("sha256", Buffer.from(token, "base64")).update(`${nonce}:${ts}:${body}`).digest("hex");
|
|
93
|
+
return { ...payload, nonce, timestamp: ts, hmac: mac };
|
|
94
|
+
}
|
|
95
|
+
function verifyHmac(req, token) {
|
|
96
|
+
try {
|
|
97
|
+
const { nonce, timestamp, hmac, ...rest } = req;
|
|
98
|
+
const expected = crypto.createHmac("sha256", Buffer.from(token, "base64")).update(`${nonce}:${timestamp}:${JSON.stringify(rest)}`).digest("hex");
|
|
99
|
+
return hmac === expected;
|
|
100
|
+
} catch {
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
function readExecApprovalsConfig() {
|
|
105
|
+
const cfgPath = path2.join(os2.homedir(), ".openclaw", "exec-approvals.json");
|
|
106
|
+
try {
|
|
107
|
+
return JSON.parse(fs2.readFileSync(cfgPath, "utf8"));
|
|
108
|
+
} catch {
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
function startExecApprovalsClient(apiKey, policyId, baseUrl) {
|
|
113
|
+
const cfg = readExecApprovalsConfig();
|
|
114
|
+
if (!cfg?.socket?.path || !cfg?.socket?.token) return;
|
|
115
|
+
const { path: sockPath, token } = cfg.socket;
|
|
116
|
+
function connect() {
|
|
117
|
+
const client = net.createConnection(sockPath);
|
|
118
|
+
let buffer = "";
|
|
119
|
+
client.on("connect", () => {
|
|
120
|
+
console.log("[fastgrc] exec-approvals socket connected");
|
|
121
|
+
});
|
|
122
|
+
client.on("data", async (buf) => {
|
|
123
|
+
buffer += buf.toString();
|
|
124
|
+
let req;
|
|
125
|
+
try {
|
|
126
|
+
req = JSON.parse(buffer);
|
|
127
|
+
} catch {
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
buffer = "";
|
|
131
|
+
if (!verifyHmac(req, token)) {
|
|
132
|
+
console.warn("[fastgrc] exec-approvals: HMAC verification failed \u2014 ignoring request");
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
let decision = "allow-once";
|
|
136
|
+
try {
|
|
137
|
+
const result = await evaluate({
|
|
138
|
+
toolName: "Exec",
|
|
139
|
+
args: { command: req.command, rawCommand: req.rawCommand, cwd: req.cwd },
|
|
140
|
+
agentId: req.agentId,
|
|
141
|
+
apiKey,
|
|
142
|
+
policyId,
|
|
143
|
+
baseUrl
|
|
144
|
+
});
|
|
145
|
+
if (result && (result.decision === "block" || result.decision === "require_approval")) {
|
|
146
|
+
decision = "deny";
|
|
147
|
+
const msg = result.policyContext?.matchedRule ? `[fastgrc] exec blocked: [${result.policyContext.matchedRule}] ${result.reasoning}` : `[fastgrc] exec blocked: ${result.reasoning}`;
|
|
148
|
+
console.warn(msg);
|
|
149
|
+
}
|
|
150
|
+
} catch {
|
|
151
|
+
}
|
|
152
|
+
const response = sign({ approvalId: req.approvalId, decision }, token);
|
|
153
|
+
client.write(JSON.stringify(response));
|
|
154
|
+
});
|
|
155
|
+
client.on("error", () => setTimeout(connect, 5e3));
|
|
156
|
+
client.on("close", () => setTimeout(connect, 5e3));
|
|
157
|
+
}
|
|
158
|
+
connect();
|
|
159
|
+
}
|
|
81
160
|
var pluginEntry = {
|
|
82
161
|
id: "fastgrc",
|
|
83
162
|
name: "FastGRC Policy Router",
|
|
@@ -92,31 +171,7 @@ var pluginEntry = {
|
|
|
92
171
|
return;
|
|
93
172
|
}
|
|
94
173
|
console.log("[fastgrc] Plugin registered \u2014 before_tool_call hook active");
|
|
95
|
-
const probeEvents = [
|
|
96
|
-
"exec",
|
|
97
|
-
"before_exec",
|
|
98
|
-
"shell",
|
|
99
|
-
"before_shell",
|
|
100
|
-
"command",
|
|
101
|
-
"before_command",
|
|
102
|
-
"run",
|
|
103
|
-
"before_run",
|
|
104
|
-
"tool_call",
|
|
105
|
-
"before_tool",
|
|
106
|
-
"action",
|
|
107
|
-
"before_action",
|
|
108
|
-
"request",
|
|
109
|
-
"before_request",
|
|
110
|
-
"call",
|
|
111
|
-
"before_call"
|
|
112
|
-
];
|
|
113
|
-
for (const evt of probeEvents) {
|
|
114
|
-
api.on(evt, (...args) => {
|
|
115
|
-
console.log(`[fastgrc:hit] ${evt}`, JSON.stringify(args[0])?.slice(0, 120));
|
|
116
|
-
});
|
|
117
|
-
}
|
|
118
174
|
api.on("before_tool_call", async (event, ctx) => {
|
|
119
|
-
console.log(`[fastgrc] before_tool_call fired: ${event.toolName}`);
|
|
120
175
|
const result = await evaluate({
|
|
121
176
|
toolName: event.toolName,
|
|
122
177
|
args: event.params,
|
|
@@ -146,9 +201,12 @@ var pluginEntry = {
|
|
|
146
201
|
}
|
|
147
202
|
return void 0;
|
|
148
203
|
});
|
|
204
|
+
startExecApprovalsClient(apiKey, policyId, DEFAULT_BASE_URL2);
|
|
149
205
|
}
|
|
150
206
|
};
|
|
151
207
|
var plugin_default = pluginEntry;
|
|
152
208
|
export {
|
|
153
|
-
plugin_default as default
|
|
209
|
+
plugin_default as default,
|
|
210
|
+
readExecApprovalsConfig,
|
|
211
|
+
startExecApprovalsClient
|
|
154
212
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fastgrc-openclaw",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.29",
|
|
4
4
|
"description": "FastGRC agent compliance plugin for OpenClaw — evaluates every tool call against your policy before it executes",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|