limits-openclaw 0.0.11 → 0.0.13
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/README.md +50 -30
- package/dist/config.js +1 -1
- package/dist/configure-wizard.js +73 -42
- package/dist/index.js +2 -2
- package/dist/logger.js +2 -2
- package/package.json +3 -2
- package/skills/limits-policy-generator/SKILL.md +110 -0
package/README.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
# limits-openclaw
|
|
1
|
+
# limits-openclaw
|
|
2
2
|
|
|
3
|
-
[](https://www.npmjs.com/package/limits-openclaw)
|
|
4
4
|
[](https://opensource.org/licenses/MIT)
|
|
5
5
|
[](https://www.typescriptlang.org/)
|
|
6
6
|
[](https://nodejs.org/)
|
|
7
|
-
[](https://github.com/limitsdev/limits-openclaw
|
|
7
|
+
[](https://github.com/limitsdev/limits-openclaw)
|
|
8
8
|
|
|
9
9
|
**Official** OpenClaw plugin for the [Limits](https://limits.dev) platform. Delegates policy enforcement via HTTP. Every decision is made by calling `POST {baseUrl}/openclaw/enforce` before and after each tool call. Optional policy-generator tools let the agent create and update policies from natural language.
|
|
10
10
|
|
|
@@ -31,23 +31,43 @@
|
|
|
31
31
|
## Installation
|
|
32
32
|
|
|
33
33
|
```bash
|
|
34
|
-
npm install limits-openclaw
|
|
34
|
+
npm install limits-openclaw
|
|
35
35
|
```
|
|
36
36
|
|
|
37
37
|
```bash
|
|
38
|
-
yarn add limits-openclaw
|
|
38
|
+
yarn add limits-openclaw
|
|
39
39
|
```
|
|
40
40
|
|
|
41
41
|
```bash
|
|
42
|
-
pnpm add limits-openclaw
|
|
42
|
+
pnpm add limits-openclaw
|
|
43
43
|
```
|
|
44
44
|
|
|
45
45
|
Then register the plugin with OpenClaw:
|
|
46
46
|
|
|
47
47
|
```bash
|
|
48
|
-
openclaw plugins install ./node_modules/limits-openclaw
|
|
48
|
+
openclaw plugins install ./node_modules/limits-openclaw
|
|
49
49
|
openclaw gateway restart
|
|
50
|
-
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
**Add `limits-openclaw` to the Plugin Allowlist** (required before enabling):
|
|
53
|
+
|
|
54
|
+
**Option A — Dashboard:**
|
|
55
|
+
|
|
56
|
+
1. Open the OpenClaw dashboard and go to **Config → Plugins → Plugin Allowlist**.
|
|
57
|
+
2. Click **Add** and type `limits-openclaw`, then save and apply.
|
|
58
|
+
|
|
59
|
+
**Option B — CLI:**
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
current=$(openclaw config get plugins.allow 2>/dev/null | grep -v '^\[.\+\] ')
|
|
63
|
+
updated=$(echo "${current:-[]}" | jq -c '(. // []) + ["limits-openclaw"] | unique')
|
|
64
|
+
openclaw config set plugins.allow "$updated" --json
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Then enable the plugin:
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
openclaw plugins enable "limits-openclaw"
|
|
51
71
|
```
|
|
52
72
|
|
|
53
73
|
---
|
|
@@ -64,7 +84,7 @@ openclaw limits configure
|
|
|
64
84
|
If you see **`error: unknown command 'limits'`**, the OpenClaw CLI may not load plugin commands in your environment. Run the configure script directly (from a directory where the package is installed):
|
|
65
85
|
|
|
66
86
|
```bash
|
|
67
|
-
node node_modules/limits-openclaw
|
|
87
|
+
node node_modules/limits-openclaw/scripts/configure.js
|
|
68
88
|
```
|
|
69
89
|
|
|
70
90
|
Or set config manually:
|
|
@@ -73,7 +93,7 @@ Or set config manually:
|
|
|
73
93
|
{
|
|
74
94
|
"plugins": {
|
|
75
95
|
"entries": {
|
|
76
|
-
"limits-openclaw
|
|
96
|
+
"limits-openclaw": {
|
|
77
97
|
"enabled": true,
|
|
78
98
|
"config": {
|
|
79
99
|
"apiToken": "sk_your_organization_api_key"
|
|
@@ -92,7 +112,7 @@ Or set config manually:
|
|
|
92
112
|
"list": [
|
|
93
113
|
{
|
|
94
114
|
"id": "main",
|
|
95
|
-
"tools": { "allow": ["limits-openclaw
|
|
115
|
+
"tools": { "allow": ["limits-openclaw"] }
|
|
96
116
|
}
|
|
97
117
|
]
|
|
98
118
|
}
|
|
@@ -127,7 +147,7 @@ flowchart LR
|
|
|
127
147
|
1. **Before each tool call** (`before_tool_call`): the plugin POSTs `phase: "pre"` with tool name and args. Limits returns `ALLOW`, `BLOCK`, or `REWRITE` (with `rewriteArgs`). The plugin blocks the call, allows it, or replaces the tool arguments accordingly.
|
|
128
148
|
2. **After each tool call** (`after_tool_call`): the plugin POSTs `phase: "post"` with tool name, args, and result. Limits returns `ALLOW`, `BLOCK`, `REDACT`, or `REWRITE` (with `redactedResult` or `rewrittenResult`). The plugin leaves the result unchanged, replaces it with a safe blocked response, or replaces it with the redacted/rewritten value.
|
|
129
149
|
|
|
130
|
-
**Compatibility:** This plugin requires an OpenClaw build where tool hooks are wired into the execution path. If you see **`[limits-openclaw
|
|
150
|
+
**Compatibility:** This plugin requires an OpenClaw build where tool hooks are wired into the execution path. If you see **`[limits-openclaw] before_tool_call observed`** in logs once after a tool runs, hooks are working. See [OpenClaw #6535](https://github.com/openclaw/openclaw/issues/6535) if hooks never fire.
|
|
131
151
|
|
|
132
152
|
---
|
|
133
153
|
|
|
@@ -150,16 +170,16 @@ Optionally, you can copy it manually. The example below is for Unix; on Windows
|
|
|
150
170
|
|
|
151
171
|
```bash
|
|
152
172
|
mkdir -p ~/.openclaw/workspace/skills/limits-policy-generator
|
|
153
|
-
cp -r ./node_modules/limits-openclaw
|
|
173
|
+
cp -r ./node_modules/limits-openclaw/skills/limits-policy-generator/. ~/.openclaw/workspace/skills/limits-policy-generator/
|
|
154
174
|
```
|
|
155
175
|
|
|
156
176
|
### Expose tools to the agent
|
|
157
177
|
|
|
158
|
-
The plugin registers these tools as **optional**. Add the plugin to the agent's tool allowlist (see [Quick Start](#quick-start)) so the agent can call them. Use `"limits-openclaw
|
|
178
|
+
The plugin registers these tools as **optional**. Add the plugin to the agent's tool allowlist (see [Quick Start](#quick-start)) so the agent can call them. Use `"limits-openclaw"` to allow all tools from this plugin, or allow by name: `["limits_generate_create_policy", "limits_generate_update_policy"]`.
|
|
159
179
|
|
|
160
180
|
### Sandboxed agents
|
|
161
181
|
|
|
162
|
-
If the agent runs in a **sandbox**, add `"limits-openclaw
|
|
182
|
+
If the agent runs in a **sandbox**, add `"limits-openclaw"` (or the tool names) to `tools.sandbox.tools.allow` as well so the sandboxed agent can call the policy tools. Otherwise the agent can have the skill but cannot invoke the tools from inside the sandbox.
|
|
163
183
|
|
|
164
184
|
---
|
|
165
185
|
|
|
@@ -190,7 +210,7 @@ If the agent runs in a **sandbox**, add `"limits-openclaw "` (or the tool names)
|
|
|
190
210
|
{
|
|
191
211
|
"plugins": {
|
|
192
212
|
"entries": {
|
|
193
|
-
"limits-openclaw
|
|
213
|
+
"limits-openclaw": {
|
|
194
214
|
"enabled": true,
|
|
195
215
|
"config": {
|
|
196
216
|
"apiToken": "sk_your_organization_api_key",
|
|
@@ -253,12 +273,12 @@ Policies are scoped to OpenClaw tool calls using **tags** in the Limits dashboar
|
|
|
253
273
|
|---------|-------------|
|
|
254
274
|
| `openclaw plugins install <path>` | Install plugin from path. |
|
|
255
275
|
| `openclaw plugins install -l <path>` | Link plugin (no copy, for development). |
|
|
256
|
-
| `openclaw plugins enable "limits-openclaw
|
|
276
|
+
| `openclaw plugins enable "limits-openclaw"` | Enable the plugin. |
|
|
257
277
|
| `openclaw plugins list` | List installed plugins. |
|
|
258
278
|
| `openclaw plugins doctor` | Check plugin health. |
|
|
259
279
|
| `openclaw limits configure` | Interactive wizard to set API token and sandbox allowlist. |
|
|
260
|
-
| `openclaw config get 'plugins.entries["limits-openclaw
|
|
261
|
-
| `openclaw config set 'plugins.entries["limits-openclaw
|
|
280
|
+
| `openclaw config get 'plugins.entries["limits-openclaw"]'` | Show plugin config. |
|
|
281
|
+
| `openclaw config set 'plugins.entries["limits-openclaw"].config.apiToken' "sk_..."` | Set apiToken manually. |
|
|
262
282
|
|
|
263
283
|
---
|
|
264
284
|
|
|
@@ -283,29 +303,29 @@ If you were using the plugin under the old name **limits-enforcer**, update your
|
|
|
283
303
|
|
|
284
304
|
| Before | After |
|
|
285
305
|
|--------|-------|
|
|
286
|
-
| Plugin ID / allowlist: `"limits-enforcer"` | `"limits-openclaw
|
|
287
|
-
| Config path: `plugins.entries.limits-enforcer` | `plugins.entries["limits-openclaw
|
|
306
|
+
| Plugin ID / allowlist: `"limits-enforcer"` | `"limits-openclaw"` |
|
|
307
|
+
| Config path: `plugins.entries.limits-enforcer` | `plugins.entries["limits-openclaw"]` |
|
|
288
308
|
| CLI command: `openclaw limits-enforcer configure` | `openclaw limits configure` |
|
|
289
|
-
| Log prefix: `[limits-enforcer]` | `[limits-openclaw
|
|
309
|
+
| Log prefix: `[limits-enforcer]` | `[limits-openclaw]` |
|
|
290
310
|
|
|
291
|
-
After renaming, reinstall the plugin and enable `"limits-openclaw
|
|
311
|
+
After renaming, reinstall the plugin and enable `"limits-openclaw"`.
|
|
292
312
|
|
|
293
313
|
---
|
|
294
314
|
|
|
295
315
|
## Troubleshooting
|
|
296
316
|
|
|
297
|
-
- **Config warning "plugin id mismatch (manifest uses 'limits-openclaw
|
|
298
|
-
Some OpenClaw gateways install scoped packages under `extensions/openclaw` instead of `extensions/limits-openclaw
|
|
317
|
+
- **Config warning "plugin id mismatch (manifest uses 'limits-openclaw', entry hints 'openclaw')"**
|
|
318
|
+
Some OpenClaw gateways install scoped packages under `extensions/openclaw` instead of `extensions/limits-openclaw`. The gateway then infers the plugin id from the folder name (`openclaw`), which does not match the manifest id `limits-openclaw`, so the plugin may not load and `openclaw limits configure` shows **unknown command 'limits'**.
|
|
299
319
|
|
|
300
320
|
**Workaround — install under the scoped path so the gateway matches the config entry:**
|
|
301
321
|
```bash
|
|
302
322
|
# Create the scope directory and install the package there (adjust if you use npm -g or another path)
|
|
303
323
|
mkdir -p ~/.openclaw/extensions/@limits
|
|
304
|
-
cp -r ./node_modules/limits-openclaw
|
|
324
|
+
cp -r ./node_modules/limits-openclaw ~/.openclaw/extensions/@limits/
|
|
305
325
|
|
|
306
326
|
# Tell OpenClaw to load the plugin from that path
|
|
307
|
-
openclaw plugins install ~/.openclaw/extensions/limits-openclaw
|
|
308
|
-
openclaw plugins enable "limits-openclaw
|
|
327
|
+
openclaw plugins install ~/.openclaw/extensions/limits-openclaw
|
|
328
|
+
openclaw plugins enable "limits-openclaw"
|
|
309
329
|
openclaw gateway restart
|
|
310
330
|
```
|
|
311
331
|
Then run `openclaw limits configure`.
|
|
@@ -313,7 +333,7 @@ After renaming, reinstall the plugin and enable `"limits-openclaw "`.
|
|
|
313
333
|
- **`error: unknown command 'limits'`**
|
|
314
334
|
The plugin did not load (often due to the id mismatch above). Use the workaround above, or run the configure wizard without the OpenClaw CLI:
|
|
315
335
|
```bash
|
|
316
|
-
node node_modules/limits-openclaw
|
|
336
|
+
node node_modules/limits-openclaw/scripts/configure.js
|
|
317
337
|
```
|
|
318
338
|
Or from the plugin repo: `npm run configure`.
|
|
319
339
|
|
|
@@ -322,4 +342,4 @@ After renaming, reinstall the plugin and enable `"limits-openclaw "`.
|
|
|
322
342
|
|
|
323
343
|
## License
|
|
324
344
|
|
|
325
|
-
MIT — [Limits](https://limits.dev) — [GitHub](https://github.com/limitsdev/limits-openclaw
|
|
345
|
+
MIT — [Limits](https://limits.dev) — [GitHub](https://github.com/limitsdev/limits-openclaw)
|
package/dist/config.js
CHANGED
|
@@ -10,7 +10,7 @@ const DEFAULTS = {
|
|
|
10
10
|
redactLogs: true,
|
|
11
11
|
};
|
|
12
12
|
export function loadConfig(api) {
|
|
13
|
-
const raw = api.config?.plugins?.entries?.["limits-openclaw
|
|
13
|
+
const raw = api.config?.plugins?.entries?.["limits-openclaw"]?.config ?? {};
|
|
14
14
|
// baseUrl defaults to static; not in wizard or schema so users don't edit it (config override only for tests/advanced)
|
|
15
15
|
const baseUrl = raw.baseUrl ?? DEFAULTS.baseUrl;
|
|
16
16
|
const timeoutMs = typeof process.env.LIMITS_ENFORCER_TIMEOUT_MS === "string"
|
package/dist/configure-wizard.js
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import { createInterface } from "node:readline";
|
|
2
|
-
import { spawn } from "node:child_process";
|
|
3
2
|
import { dirname, join } from "node:path";
|
|
4
3
|
import { fileURLToPath } from "node:url";
|
|
5
4
|
import fs from "node:fs";
|
|
6
5
|
import os from "node:os";
|
|
7
|
-
const CONFIG_PREFIX = 'plugins.entries["limits-openclaw
|
|
6
|
+
const CONFIG_PREFIX = 'plugins.entries["limits-openclaw"].config';
|
|
8
7
|
const SKILL_NAME = "limits-policy-generator";
|
|
9
8
|
function getPluginRoot() {
|
|
10
9
|
const currentDir = dirname(fileURLToPath(import.meta.url));
|
|
@@ -31,6 +30,71 @@ function copySkillToWorkspace() {
|
|
|
31
30
|
console.error("\nFailed to copy skill:", err instanceof Error ? err.message : String(err));
|
|
32
31
|
}
|
|
33
32
|
}
|
|
33
|
+
function getConfigFilePath() {
|
|
34
|
+
return process.env.OPENCLAW_CONFIG || join(os.homedir(), ".openclaw", "openclaw.json");
|
|
35
|
+
}
|
|
36
|
+
function readConfigFile() {
|
|
37
|
+
try {
|
|
38
|
+
return JSON.parse(fs.readFileSync(getConfigFilePath(), "utf8"));
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return {};
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function writeConfigFile(cfg) {
|
|
45
|
+
fs.writeFileSync(getConfigFilePath(), JSON.stringify(cfg, null, 2) + "\n", "utf8");
|
|
46
|
+
}
|
|
47
|
+
function parseConfigPath(path) {
|
|
48
|
+
const segments = [];
|
|
49
|
+
const re = /\[["']([^"']+)["']\]|\.?([^.[\]]+)/g;
|
|
50
|
+
let m;
|
|
51
|
+
while ((m = re.exec(path)) !== null) {
|
|
52
|
+
const seg = m[1] ?? m[2];
|
|
53
|
+
if (seg)
|
|
54
|
+
segments.push(seg);
|
|
55
|
+
}
|
|
56
|
+
return segments;
|
|
57
|
+
}
|
|
58
|
+
function configGet(cfg, path) {
|
|
59
|
+
let cur = cfg;
|
|
60
|
+
for (const seg of parseConfigPath(path)) {
|
|
61
|
+
if (cur == null || typeof cur !== "object")
|
|
62
|
+
return undefined;
|
|
63
|
+
cur = cur[seg];
|
|
64
|
+
}
|
|
65
|
+
return cur;
|
|
66
|
+
}
|
|
67
|
+
function configSet(cfg, path, value) {
|
|
68
|
+
const segments = parseConfigPath(path);
|
|
69
|
+
let cur = cfg;
|
|
70
|
+
for (let i = 0; i < segments.length - 1; i++) {
|
|
71
|
+
const seg = segments[i];
|
|
72
|
+
if (cur[seg] == null || typeof cur[seg] !== "object")
|
|
73
|
+
cur[seg] = {};
|
|
74
|
+
cur = cur[seg];
|
|
75
|
+
}
|
|
76
|
+
cur[segments[segments.length - 1]] = value;
|
|
77
|
+
}
|
|
78
|
+
function runConfigSet(key, jsonValue) {
|
|
79
|
+
const fullKey = `${CONFIG_PREFIX}.${key}`;
|
|
80
|
+
const cfg = readConfigFile();
|
|
81
|
+
configSet(cfg, fullKey, JSON.parse(jsonValue));
|
|
82
|
+
writeConfigFile(cfg);
|
|
83
|
+
console.log(`Updated ${fullKey}.`);
|
|
84
|
+
return Promise.resolve();
|
|
85
|
+
}
|
|
86
|
+
function runConfigGet(fullKey) {
|
|
87
|
+
const cfg = readConfigFile();
|
|
88
|
+
const val = configGet(cfg, fullKey);
|
|
89
|
+
return Promise.resolve(val !== undefined ? JSON.stringify(val) : "");
|
|
90
|
+
}
|
|
91
|
+
function runConfigSetFull(fullKey, jsonValue) {
|
|
92
|
+
const cfg = readConfigFile();
|
|
93
|
+
configSet(cfg, fullKey, JSON.parse(jsonValue));
|
|
94
|
+
writeConfigFile(cfg);
|
|
95
|
+
console.log(`Updated ${fullKey}.`);
|
|
96
|
+
return Promise.resolve();
|
|
97
|
+
}
|
|
34
98
|
export function ask(rl, question, defaultValue = "") {
|
|
35
99
|
const prompt = defaultValue ? `${question} [${defaultValue}]: ` : `${question}: `;
|
|
36
100
|
return new Promise((resolve) => {
|
|
@@ -39,41 +103,8 @@ export function ask(rl, question, defaultValue = "") {
|
|
|
39
103
|
});
|
|
40
104
|
});
|
|
41
105
|
}
|
|
42
|
-
function runConfigSet(key, value) {
|
|
43
|
-
return new Promise((resolve, reject) => {
|
|
44
|
-
const fullKey = `${CONFIG_PREFIX}.${key}`;
|
|
45
|
-
const child = spawn("openclaw", ["config", "set", fullKey, value], {
|
|
46
|
-
stdio: "inherit",
|
|
47
|
-
shell: true,
|
|
48
|
-
});
|
|
49
|
-
child.on("close", (code) => code === 0 ? resolve() : reject(new Error(`openclaw config set exited ${code}`)));
|
|
50
|
-
child.on("error", reject);
|
|
51
|
-
});
|
|
52
|
-
}
|
|
53
|
-
function runConfigGet(fullKey) {
|
|
54
|
-
return new Promise((resolve) => {
|
|
55
|
-
const child = spawn("openclaw", ["config", "get", fullKey], {
|
|
56
|
-
stdio: ["inherit", "pipe", "inherit"],
|
|
57
|
-
shell: true,
|
|
58
|
-
});
|
|
59
|
-
let out = "";
|
|
60
|
-
child.stdout?.on("data", (d) => (out += d.toString()));
|
|
61
|
-
child.on("close", () => resolve(out.trim()));
|
|
62
|
-
child.on("error", () => resolve(""));
|
|
63
|
-
});
|
|
64
|
-
}
|
|
65
|
-
function runConfigSetFull(fullKey, value) {
|
|
66
|
-
return new Promise((resolve, reject) => {
|
|
67
|
-
const child = spawn("openclaw", ["config", "set", fullKey, value], {
|
|
68
|
-
stdio: "inherit",
|
|
69
|
-
shell: true,
|
|
70
|
-
});
|
|
71
|
-
child.on("close", (code) => code === 0 ? resolve() : reject(new Error(`openclaw config set exited ${code}`)));
|
|
72
|
-
child.on("error", reject);
|
|
73
|
-
});
|
|
74
|
-
}
|
|
75
106
|
/**
|
|
76
|
-
* Run the configure wizard (interactive prompts, then
|
|
107
|
+
* Run the configure wizard (interactive prompts, then direct config file write).
|
|
77
108
|
* Call this when the user runs `openclaw limits configure` or after link.
|
|
78
109
|
*/
|
|
79
110
|
export async function runConfigureWizard() {
|
|
@@ -82,7 +113,7 @@ export async function runConfigureWizard() {
|
|
|
82
113
|
console.log(" Base URL is fixed. apiToken is used for /openclaw/enforce and policy-generator tools. Run after: openclaw plugins install -l <path>\n");
|
|
83
114
|
const apiToken = await ask(rl, "Organization API key (apiToken) — required for enforce and policy-generator tools", process.env.LIMITS_ENFORCER_API_TOKEN ?? "");
|
|
84
115
|
const sandboxAnswer = await ask(rl, "Do you run agents inside a sandbox?", "N");
|
|
85
|
-
const addSkillAnswer = await ask(rl, "Add limits-policy-generator skill to OpenClaw workspace?", "Y");
|
|
116
|
+
const addSkillAnswer = await ask(rl, "Add limits-policy-generator skill to OpenClaw workspace? (Recommended)", "Y");
|
|
86
117
|
rl.close();
|
|
87
118
|
if (apiToken)
|
|
88
119
|
await runConfigSet("apiToken", JSON.stringify(apiToken));
|
|
@@ -100,18 +131,18 @@ export async function runConfigureWizard() {
|
|
|
100
131
|
allow = [];
|
|
101
132
|
}
|
|
102
133
|
}
|
|
103
|
-
if (allow.includes("limits-openclaw
|
|
104
|
-
console.log("\nlimits-openclaw
|
|
134
|
+
if (allow.includes("limits-openclaw")) {
|
|
135
|
+
console.log("\nlimits-openclaw is already in tools.sandbox.tools.allow, skipping.");
|
|
105
136
|
}
|
|
106
137
|
else {
|
|
107
|
-
allow.push("limits-openclaw
|
|
138
|
+
allow.push("limits-openclaw");
|
|
108
139
|
await runConfigSetFull(SANDBOX_ALLOW_KEY, JSON.stringify(allow));
|
|
109
|
-
console.log("\nAdded limits-openclaw
|
|
140
|
+
console.log("\nAdded limits-openclaw to tools.sandbox.tools.allow.");
|
|
110
141
|
}
|
|
111
142
|
}
|
|
112
143
|
const addSkillYes = /^y(es)?$/i.test(addSkillAnswer.trim());
|
|
113
144
|
if (addSkillYes)
|
|
114
145
|
copySkillToWorkspace();
|
|
115
146
|
console.log("\nDone. Restart the gateway if it is running.");
|
|
116
|
-
console.log('Verify: openclaw config get plugins.entries["limits-openclaw
|
|
147
|
+
console.log('Verify: openclaw config get plugins.entries["limits-openclaw"]\n');
|
|
117
148
|
}
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* limits-openclaw
|
|
2
|
+
* limits-openclaw: register(api) entrypoint — wires before_tool_call and after_tool_call.
|
|
3
3
|
*/
|
|
4
4
|
import { loadConfig } from "./config.js";
|
|
5
5
|
import { callEnforce } from "./enforcer.js";
|
|
@@ -182,7 +182,7 @@ function registerPolicyTools(api) {
|
|
|
182
182
|
log("policy-generator tools registered (limits_generate_create_policy, limits_generate_update_policy)");
|
|
183
183
|
}
|
|
184
184
|
export function register(api) {
|
|
185
|
-
log("limits-openclaw
|
|
185
|
+
log("limits-openclaw loaded");
|
|
186
186
|
if (typeof api.on !== "function") {
|
|
187
187
|
log("api.on not available, hooks not registered");
|
|
188
188
|
return;
|
package/dist/logger.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Safe logger for limits-openclaw
|
|
2
|
+
* Safe logger for limits-openclaw. Never logs apiToken, request body, or tool args.
|
|
3
3
|
*/
|
|
4
|
-
const PREFIX = "[limits-openclaw
|
|
4
|
+
const PREFIX = "[limits-openclaw]";
|
|
5
5
|
export function log(message, ...safeArgs) {
|
|
6
6
|
const safe = safeArgs.map((a) => typeof a === "string" || typeof a === "number" || typeof a === "boolean"
|
|
7
7
|
? a
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
|
-
"name": "limits-openclaw
|
|
3
|
-
"version": "0.0.
|
|
2
|
+
"name": "limits-openclaw",
|
|
3
|
+
"version": "0.0.13",
|
|
4
4
|
"description": "Delegates policy enforcement to the Limits platform before and after every tool call.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"openclaw",
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
},
|
|
19
19
|
"files": [
|
|
20
20
|
"dist",
|
|
21
|
+
"skills",
|
|
21
22
|
"openclaw.plugin.json",
|
|
22
23
|
"README.md",
|
|
23
24
|
"LICENSE",
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: limits-policy-generator
|
|
3
|
+
description: "Generate and create or update policy rules on the Limits SaaS from natural language. Use when the user asks to add, change, or create policy rules enforced by the Limits backend."
|
|
4
|
+
metadata: {"openclaw": {"emoji": "📜", "requires": {"config": ["plugins.entries[\"limits-openclaw\"].config.apiToken"]}}}
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Limits Policy Generator
|
|
8
|
+
|
|
9
|
+
You have two tools for creating and updating policies on the Limits SaaS from natural language. They call the Limits backend so the user's enforcement rules are created or updated there (and apply to tool-call enforcement via the limits-openclaw plugin).
|
|
10
|
+
|
|
11
|
+
## When to use this skill
|
|
12
|
+
|
|
13
|
+
Use these tools when the user says things like:
|
|
14
|
+
|
|
15
|
+
- "Create a policy that blocks all payment tools"
|
|
16
|
+
- "Add a rule: never run bash with rm -rf"
|
|
17
|
+
- "Generate a policy from this: block transactions over 500"
|
|
18
|
+
- "Update my policy to also block stripe_* tools"
|
|
19
|
+
- "I want to add a guardrail that redacts emails from tool output"
|
|
20
|
+
- "Change the payment limit to 700"
|
|
21
|
+
|
|
22
|
+
## Tools available
|
|
23
|
+
|
|
24
|
+
### `limits_generate_create_policy`
|
|
25
|
+
|
|
26
|
+
Generate a new policy from natural language and create it on the Limits backend. Use when the user wants to **add** a new policy.
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
limits_generate_create_policy(
|
|
30
|
+
input="Block any tool whose name starts with stripe_ or payment_. Allow all other tools.",
|
|
31
|
+
mode="INSTRUCTIONS",
|
|
32
|
+
tools=["stripe_.*", "payment_.*"]
|
|
33
|
+
)
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
- **input** (required): Natural-language description of the policy (what to block, allow, or require).
|
|
37
|
+
- **mode** (optional): `"INSTRUCTIONS"` | `"CONDITIONS"` | `"GUARDRAIL"`. Default is INSTRUCTIONS. Use GUARDRAIL for rules that scan tool **output** (e.g. redact PII).
|
|
38
|
+
- **tools** (required): Which tool calls this policy applies to. See "Where to apply" section below.
|
|
39
|
+
|
|
40
|
+
### `limits_generate_update_policy`
|
|
41
|
+
|
|
42
|
+
Generate updates from natural language and apply them to an **existing** policy. Use when the user wants to **change** a policy they already have.
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
limits_generate_update_policy(
|
|
46
|
+
policyId="uuid-of-existing-policy",
|
|
47
|
+
input="Also block transfer_money. Keep the existing amount limit.",
|
|
48
|
+
mode="INSTRUCTIONS"
|
|
49
|
+
)
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
- **policyId** (required): The ID of the existing policy to update (UUID).
|
|
53
|
+
- **input** (required): Natural-language description of the changes or additions.
|
|
54
|
+
- **mode** (optional): Same as above.
|
|
55
|
+
- **Note:** Update does **not** change which tools the policy applies to — scope is fixed at creation time. If the user wants to change scope, they should create a new policy.
|
|
56
|
+
|
|
57
|
+
## Where to apply (tools parameter)
|
|
58
|
+
|
|
59
|
+
The `tools` parameter on `limits_generate_create_policy` is **required** and controls which tool calls the policy is enforced on.
|
|
60
|
+
|
|
61
|
+
### Values
|
|
62
|
+
|
|
63
|
+
| Value | Meaning |
|
|
64
|
+
|-------|---------|
|
|
65
|
+
| `["*"]` | Apply to **all** tool calls / all requests |
|
|
66
|
+
| `["<tool_name>"]` | Apply to the exact tool (use only names from the agent's actual tool list) |
|
|
67
|
+
| `["<prefix>.*"]` | Apply to all tools whose name starts with that prefix (e.g. `payment_.*`) |
|
|
68
|
+
|
|
69
|
+
Use `prefix.*` (dot-star) for prefix matching. The backend also accepts `prefix_*` and normalizes it to `prefix_.*`. **Use only tool names or prefixes that exist in the user's / agent's available tools** — do not invent names.
|
|
70
|
+
|
|
71
|
+
### Ask-once logic (required)
|
|
72
|
+
|
|
73
|
+
**Do not call `limits_generate_create_policy` until you know scope.** If the user did not say scope, ask once.
|
|
74
|
+
|
|
75
|
+
- User says **"everywhere"**, **"all requests"**, **"globally"**, or **"all tools"** → use `["*"]` without asking.
|
|
76
|
+
- User **explicitly names specific tools** (e.g. "for read_file", "payment tools", "stripe_*") → use those tools without asking.
|
|
77
|
+
- User **does not say** "all" / "everywhere" / "globally" and **does not name any tools** (e.g. "create a policy if currency is JOD allow request", "create a policy that blocks payments") → you **must ask once** before calling the tool: _"Which tools should this policy apply to: all tool calls, or only specific tools? If specific, which tool names from your available tools?"_ Then call the tool with the user's answer.
|
|
78
|
+
|
|
79
|
+
Do not assume scope. Do not call the create-policy tool immediately when the user only describes the rule (e.g. "if currency is JOD allow") without stating where it applies. Ask once, then call.
|
|
80
|
+
|
|
81
|
+
### Tool names: use source of truth only
|
|
82
|
+
|
|
83
|
+
When setting the `tools` parameter, use **only tool names from the agent's actual available tools** (the list of tools you have access to). Do not invent or assume tool names. If the user wants "specific tools", list the relevant tools from your real tool list and use those exact names (or prefix patterns like `name_.*` that match them). If you do not have a list of available tools, ask the user which tools should be in scope and use only names they confirm.
|
|
84
|
+
|
|
85
|
+
## Recommended flow
|
|
86
|
+
|
|
87
|
+
1. If the user wants a **new** policy:
|
|
88
|
+
- **First determine scope.** If the user did not say "all tools" / "everywhere" / "globally" and did not name specific tools, ask once: "Which tools should this apply to: all tool calls or specific tools? If specific, which ones?"
|
|
89
|
+
- Only then call `limits_generate_create_policy` with `input`, `tools` (from user or from their actual tool list), and optionally `mode`.
|
|
90
|
+
- Confirm what was created and **where it applies** (e.g. "This policy applies to all tools" or "This policy applies to [tool names]").
|
|
91
|
+
2. If the user wants to **change** an existing policy:
|
|
92
|
+
- Use `limits_generate_update_policy` with the policy id and the change description. (You may need to list or find the policy id first if the user says "update my payment policy".)
|
|
93
|
+
- Remind the user that **scope (which tools the policy applies to) cannot be changed via update** — it stays as set when the policy was created.
|
|
94
|
+
3. Policies on the Limits backend apply to tool-call enforcement (via limits-openclaw) for that organization.
|
|
95
|
+
|
|
96
|
+
## Sandbox vs host
|
|
97
|
+
|
|
98
|
+
**If you are running inside a sandbox:** The `limits_generate_create_policy` and `limits_generate_update_policy` tools are only available if the gateway config allows them in the sandbox (e.g. `tools.sandbox.tools.allow` includes `"limits-openclaw"`). If those tools are not in your tool list, you cannot invoke them from this environment.
|
|
99
|
+
|
|
100
|
+
- **To run the policy tools from the sandbox:** The admin must add `limits-openclaw` to the agent allowlist and to `tools.sandbox.tools.allow` (see the limits-openclaw README). After that, you can call the tools from here.
|
|
101
|
+
- **If the tools are still not available:** Tell the user to run the policy command on the **host** or an agent instance that has the Limits backend wired up and the tools allowed.
|
|
102
|
+
|
|
103
|
+
## Configuration
|
|
104
|
+
|
|
105
|
+
The plugin must be configured with:
|
|
106
|
+
|
|
107
|
+
- **baseUrl**: Base URL of the Limits API (e.g. `https://api.limits.dev`). Used for enforce and policy-generator tools.
|
|
108
|
+
- **apiToken**: Organization API key (Bearer) for the Limits backend. Used for enforce and policy-generator tools.
|
|
109
|
+
|
|
110
|
+
Without these, the tools will not be available or will return an error asking the user to configure them.
|