aquaman-plugin 0.1.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/LICENSE +21 -0
- package/README.md +95 -0
- package/index.ts +328 -0
- package/openclaw.plugin.json +34 -0
- package/package.json +47 -0
- package/src/commands.ts +381 -0
- package/src/config-schema.ts +101 -0
- package/src/embedded.ts +182 -0
- package/src/http-interceptor.ts +168 -0
- package/src/index.ts +74 -0
- package/src/plugin.ts +408 -0
- package/src/proxy-manager.ts +265 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 tech4242
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# aquaman-plugin
|
|
2
|
+
|
|
3
|
+
OpenClaw Gateway plugin for [aquaman](https://github.com/tech4242/aquaman) credential isolation.
|
|
4
|
+
|
|
5
|
+
## What This Is
|
|
6
|
+
|
|
7
|
+
`aquaman-plugin` integrates aquaman's credential isolation proxy with the OpenClaw Gateway. When loaded, it routes all LLM and channel API traffic through the aquaman proxy so credentials never enter the Gateway process.
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
openclaw plugins install aquaman-plugin
|
|
13
|
+
npm install -g aquaman-proxy
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Configuration
|
|
17
|
+
|
|
18
|
+
Add to `~/.openclaw/openclaw.json`:
|
|
19
|
+
|
|
20
|
+
```json
|
|
21
|
+
{
|
|
22
|
+
"plugins": {
|
|
23
|
+
"entries": {
|
|
24
|
+
"aquaman-plugin": {
|
|
25
|
+
"enabled": true,
|
|
26
|
+
"config": {
|
|
27
|
+
"mode": "proxy",
|
|
28
|
+
"backend": "keychain",
|
|
29
|
+
"services": ["anthropic", "openai"],
|
|
30
|
+
"proxyPort": 8081
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Config Options
|
|
39
|
+
|
|
40
|
+
| Key | Type | Default | Description |
|
|
41
|
+
|-----|------|---------|-------------|
|
|
42
|
+
| `mode` | `"embedded"` \| `"proxy"` | `"embedded"` | Isolation mode |
|
|
43
|
+
| `backend` | `"keychain"` \| `"1password"` \| `"vault"` \| `"encrypted-file"` | `"keychain"` | Credential store |
|
|
44
|
+
| `services` | `string[]` | `["anthropic", "openai"]` | Services to proxy |
|
|
45
|
+
| `proxyPort` | `number` | `8081` | Proxy listen port |
|
|
46
|
+
|
|
47
|
+
> Advanced settings (TLS, audit, vault) are configured in `~/.aquaman/config.yaml`.
|
|
48
|
+
|
|
49
|
+
## Setup
|
|
50
|
+
|
|
51
|
+
**1. Add credentials:**
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
aquaman credentials add anthropic api_key
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
**2. Register a placeholder key with OpenClaw:**
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
mkdir -p ~/.openclaw/agents/main/agent
|
|
61
|
+
cat > ~/.openclaw/agents/main/agent/auth-profiles.json << 'EOF'
|
|
62
|
+
{
|
|
63
|
+
"version": 1,
|
|
64
|
+
"profiles": {
|
|
65
|
+
"anthropic:default": {
|
|
66
|
+
"type": "api_key",
|
|
67
|
+
"provider": "anthropic",
|
|
68
|
+
"key": "aquaman-proxy-managed"
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
"order": { "anthropic": ["anthropic:default"] }
|
|
72
|
+
}
|
|
73
|
+
EOF
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
**3. Launch OpenClaw:**
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
openclaw
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
The plugin auto-starts the proxy, sets `ANTHROPIC_BASE_URL` to route through it, and intercepts channel API traffic via `globalThis.fetch`.
|
|
83
|
+
|
|
84
|
+
## How It Works
|
|
85
|
+
|
|
86
|
+
- **Proxy mode** — Spawns aquaman as a child process. Credentials live in a separate OS process. Even if the agent is compromised, it cannot access keys.
|
|
87
|
+
- **Embedded mode** — Credentials loaded in-process. Simpler setup, less isolation. Good for local development.
|
|
88
|
+
|
|
89
|
+
## Documentation
|
|
90
|
+
|
|
91
|
+
See the [main README](https://github.com/tech4242/aquaman#readme) for full documentation, architecture details, and manual testing steps.
|
|
92
|
+
|
|
93
|
+
## License
|
|
94
|
+
|
|
95
|
+
MIT
|
package/index.ts
ADDED
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Aquaman OpenClaw Plugin
|
|
3
|
+
*
|
|
4
|
+
* Credential isolation for OpenClaw.
|
|
5
|
+
* Credentials never enter the agent process - they're managed by a separate proxy.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* 1. Install aquaman: npm install -g aquaman-proxy
|
|
9
|
+
* 2. Store credentials: aquaman credentials add anthropic api_key
|
|
10
|
+
* 3. Enable this plugin in openclaw.json
|
|
11
|
+
*
|
|
12
|
+
* The plugin will:
|
|
13
|
+
* - Start the aquaman proxy on plugin load
|
|
14
|
+
* - Set ANTHROPIC_BASE_URL, OPENAI_BASE_URL etc. to route through proxy
|
|
15
|
+
* - The proxy injects credentials into requests
|
|
16
|
+
* - Agent never sees the actual API keys
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
|
|
20
|
+
import { spawn, execSync, type ChildProcess } from "node:child_process";
|
|
21
|
+
import { HttpInterceptor, createHttpInterceptor } from "./src/http-interceptor.js";
|
|
22
|
+
|
|
23
|
+
let proxyProcess: ChildProcess | null = null;
|
|
24
|
+
let httpInterceptor: HttpInterceptor | null = null;
|
|
25
|
+
const proxyPort = 8081;
|
|
26
|
+
const services = ["anthropic", "openai"];
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Check if aquaman CLI is installed
|
|
30
|
+
*/
|
|
31
|
+
function isAquamanInstalled(): boolean {
|
|
32
|
+
try {
|
|
33
|
+
execSync("which aquaman", { stdio: "pipe" });
|
|
34
|
+
return true;
|
|
35
|
+
} catch {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Start the aquaman proxy daemon
|
|
42
|
+
*/
|
|
43
|
+
async function startProxy(port: number, log: OpenClawPluginApi["logger"]): Promise<boolean> {
|
|
44
|
+
return new Promise((resolve) => {
|
|
45
|
+
try {
|
|
46
|
+
proxyProcess = spawn("aquaman", ["plugin-mode", "--port", String(port)], {
|
|
47
|
+
stdio: "pipe",
|
|
48
|
+
detached: false,
|
|
49
|
+
});
|
|
50
|
+
} catch (err) {
|
|
51
|
+
log.error(`Failed to spawn aquaman: ${err}`);
|
|
52
|
+
resolve(false);
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
let started = false;
|
|
57
|
+
|
|
58
|
+
proxyProcess.stdout?.on("data", (data: Buffer) => {
|
|
59
|
+
const output = data.toString();
|
|
60
|
+
log.debug(`[aquaman] ${output.trim()}`);
|
|
61
|
+
if (output.includes("listening") || output.includes("started")) {
|
|
62
|
+
started = true;
|
|
63
|
+
resolve(true);
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
proxyProcess.stderr?.on("data", (data: Buffer) => {
|
|
68
|
+
log.warn(`[aquaman] ${data.toString().trim()}`);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
proxyProcess.on("error", (err) => {
|
|
72
|
+
log.error(`Failed to start proxy: ${err.message}`);
|
|
73
|
+
resolve(false);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
proxyProcess.on("exit", (code) => {
|
|
77
|
+
if (!started) {
|
|
78
|
+
log.warn(`Proxy exited with code ${code} before starting`);
|
|
79
|
+
resolve(false);
|
|
80
|
+
}
|
|
81
|
+
proxyProcess = null;
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
// Timeout after 5 seconds
|
|
85
|
+
setTimeout(() => {
|
|
86
|
+
if (!started) {
|
|
87
|
+
log.warn("Proxy start timed out");
|
|
88
|
+
resolve(false);
|
|
89
|
+
}
|
|
90
|
+
}, 5000);
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Stop the proxy daemon and deactivate the HTTP interceptor
|
|
96
|
+
*/
|
|
97
|
+
function stopProxy(): void {
|
|
98
|
+
if (httpInterceptor) {
|
|
99
|
+
httpInterceptor.deactivate();
|
|
100
|
+
httpInterceptor = null;
|
|
101
|
+
}
|
|
102
|
+
if (proxyProcess) {
|
|
103
|
+
proxyProcess.kill("SIGTERM");
|
|
104
|
+
proxyProcess = null;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Activate the HTTP fetch interceptor to redirect channel API traffic through the proxy.
|
|
110
|
+
* This is what provides credential isolation for channels that don't support base URL overrides.
|
|
111
|
+
*/
|
|
112
|
+
function activateHttpInterceptor(log: OpenClawPluginApi["logger"]): void {
|
|
113
|
+
// Build host-to-service map for all known channel APIs
|
|
114
|
+
const hostMap = new Map<string, string>([
|
|
115
|
+
// LLM providers (also have env var overrides, but interceptor provides defense-in-depth)
|
|
116
|
+
['api.anthropic.com', 'anthropic'],
|
|
117
|
+
['api.openai.com', 'openai'],
|
|
118
|
+
['api.github.com', 'github'],
|
|
119
|
+
// Channel APIs
|
|
120
|
+
['slack.com', 'slack'],
|
|
121
|
+
['*.slack.com', 'slack'],
|
|
122
|
+
['discord.com', 'discord'],
|
|
123
|
+
['*.discord.com', 'discord'],
|
|
124
|
+
['api.telegram.org', 'telegram'],
|
|
125
|
+
['matrix.org', 'matrix'],
|
|
126
|
+
['*.matrix.org', 'matrix'],
|
|
127
|
+
['api.line.me', 'line'],
|
|
128
|
+
['api-data.line.me', 'line'],
|
|
129
|
+
['api.twitch.tv', 'twitch'],
|
|
130
|
+
['id.twitch.tv', 'twitch'],
|
|
131
|
+
['api.twilio.com', 'twilio'],
|
|
132
|
+
['*.twilio.com', 'twilio'],
|
|
133
|
+
['api.telnyx.com', 'telnyx'],
|
|
134
|
+
['api.elevenlabs.io', 'elevenlabs'],
|
|
135
|
+
['openapi.zalo.me', 'zalo'],
|
|
136
|
+
['graph.microsoft.com', 'ms-teams'],
|
|
137
|
+
['open.feishu.cn', 'feishu'],
|
|
138
|
+
['open.larksuite.com', 'feishu'],
|
|
139
|
+
['chat.googleapis.com', 'google-chat'],
|
|
140
|
+
]);
|
|
141
|
+
|
|
142
|
+
const baseUrl = `http://127.0.0.1:${proxyPort}`;
|
|
143
|
+
|
|
144
|
+
httpInterceptor = createHttpInterceptor({
|
|
145
|
+
proxyBaseUrl: baseUrl,
|
|
146
|
+
hostMap,
|
|
147
|
+
log: (msg) => log.info(msg),
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
httpInterceptor.activate();
|
|
151
|
+
log.info(`HTTP interceptor active: ${hostMap.size} host patterns redirected through proxy`);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Set environment variables for SDK clients
|
|
156
|
+
*/
|
|
157
|
+
function configureEnvironment(log: OpenClawPluginApi["logger"]): void {
|
|
158
|
+
const baseUrl = `http://127.0.0.1:${proxyPort}`;
|
|
159
|
+
|
|
160
|
+
for (const service of services) {
|
|
161
|
+
const serviceUrl = `${baseUrl}/${service}`;
|
|
162
|
+
|
|
163
|
+
switch (service) {
|
|
164
|
+
case "anthropic":
|
|
165
|
+
process.env["ANTHROPIC_BASE_URL"] = serviceUrl;
|
|
166
|
+
log.info(`Set ANTHROPIC_BASE_URL=${serviceUrl}`);
|
|
167
|
+
break;
|
|
168
|
+
case "openai":
|
|
169
|
+
process.env["OPENAI_BASE_URL"] = serviceUrl;
|
|
170
|
+
log.info(`Set OPENAI_BASE_URL=${serviceUrl}`);
|
|
171
|
+
break;
|
|
172
|
+
case "github":
|
|
173
|
+
process.env["GITHUB_API_URL"] = serviceUrl;
|
|
174
|
+
log.info(`Set GITHUB_API_URL=${serviceUrl}`);
|
|
175
|
+
break;
|
|
176
|
+
default:
|
|
177
|
+
const envKey = `${service.toUpperCase().replace(/-/g, "_")}_BASE_URL`;
|
|
178
|
+
process.env[envKey] = serviceUrl;
|
|
179
|
+
log.info(`Set ${envKey}=${serviceUrl}`);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* OpenClaw plugin register function
|
|
186
|
+
*/
|
|
187
|
+
export default function register(api: OpenClawPluginApi): void {
|
|
188
|
+
api.logger.info("Aquaman plugin loaded");
|
|
189
|
+
|
|
190
|
+
// Check if aquaman CLI is installed
|
|
191
|
+
if (!isAquamanInstalled()) {
|
|
192
|
+
api.logger.warn(
|
|
193
|
+
"aquaman CLI not found. Install with: npm install -g aquaman-proxy"
|
|
194
|
+
);
|
|
195
|
+
api.logger.warn(
|
|
196
|
+
"Configuring environment variables, but credential injection requires the proxy to be running"
|
|
197
|
+
);
|
|
198
|
+
configureEnvironment(api.logger);
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
api.logger.info("aquaman CLI found, will start proxy on gateway start");
|
|
203
|
+
|
|
204
|
+
// Configure environment variables immediately
|
|
205
|
+
configureEnvironment(api.logger);
|
|
206
|
+
|
|
207
|
+
// Register lifecycle hooks if available
|
|
208
|
+
if (api.registerLifecycle) {
|
|
209
|
+
api.registerLifecycle({
|
|
210
|
+
async onGatewayStart() {
|
|
211
|
+
api.logger.info(`Starting aquaman proxy on port ${proxyPort}...`);
|
|
212
|
+
|
|
213
|
+
const started = await startProxy(proxyPort, api.logger);
|
|
214
|
+
if (started) {
|
|
215
|
+
api.logger.info("Aquaman proxy started successfully");
|
|
216
|
+
// Activate fetch interceptor to redirect channel HTTP traffic through proxy
|
|
217
|
+
activateHttpInterceptor(api.logger);
|
|
218
|
+
} else {
|
|
219
|
+
api.logger.error("Failed to start aquaman proxy");
|
|
220
|
+
api.logger.warn("Run 'aquaman daemon' manually to enable credential injection");
|
|
221
|
+
}
|
|
222
|
+
},
|
|
223
|
+
|
|
224
|
+
async onGatewayStop() {
|
|
225
|
+
api.logger.info("Stopping aquaman proxy...");
|
|
226
|
+
stopProxy();
|
|
227
|
+
},
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Register CLI commands if available
|
|
232
|
+
if (api.registerCli) {
|
|
233
|
+
api.registerCli(
|
|
234
|
+
({ program }) => {
|
|
235
|
+
const aquamanCmd = program
|
|
236
|
+
.command("aquaman")
|
|
237
|
+
.description("Aquaman credential management");
|
|
238
|
+
|
|
239
|
+
aquamanCmd
|
|
240
|
+
.command("status")
|
|
241
|
+
.description("Show aquaman proxy status")
|
|
242
|
+
.action(() => {
|
|
243
|
+
console.log("\nAquaman Status:");
|
|
244
|
+
console.log(` Proxy running: ${proxyProcess !== null}`);
|
|
245
|
+
console.log(` Proxy port: ${proxyPort}`);
|
|
246
|
+
console.log(` Services: ${services.join(", ")}`);
|
|
247
|
+
console.log("\nEnvironment Variables:");
|
|
248
|
+
for (const service of services) {
|
|
249
|
+
const envKey =
|
|
250
|
+
service === "anthropic"
|
|
251
|
+
? "ANTHROPIC_BASE_URL"
|
|
252
|
+
: service === "openai"
|
|
253
|
+
? "OPENAI_BASE_URL"
|
|
254
|
+
: `${service.toUpperCase()}_BASE_URL`;
|
|
255
|
+
console.log(` ${envKey}=${process.env[envKey] ?? "(not set)"}`);
|
|
256
|
+
}
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
aquamanCmd
|
|
260
|
+
.command("add <service> [key]")
|
|
261
|
+
.description("Add a credential (opens secure prompt)")
|
|
262
|
+
.action((service: string, key: string = "api_key") => {
|
|
263
|
+
try {
|
|
264
|
+
execSync(`aquaman credentials add ${service} ${key}`, {
|
|
265
|
+
stdio: "inherit",
|
|
266
|
+
});
|
|
267
|
+
} catch {
|
|
268
|
+
console.error(
|
|
269
|
+
"Failed to add credential. Is aquaman installed? npm install -g aquaman-proxy"
|
|
270
|
+
);
|
|
271
|
+
}
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
aquamanCmd
|
|
275
|
+
.command("list")
|
|
276
|
+
.description("List stored credentials")
|
|
277
|
+
.action(() => {
|
|
278
|
+
try {
|
|
279
|
+
execSync("aquaman credentials list", { stdio: "inherit" });
|
|
280
|
+
} catch {
|
|
281
|
+
console.error(
|
|
282
|
+
"Failed to list credentials. Is aquaman installed?"
|
|
283
|
+
);
|
|
284
|
+
}
|
|
285
|
+
});
|
|
286
|
+
},
|
|
287
|
+
{ commands: ["aquaman"] }
|
|
288
|
+
);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// Register a tool for agents to check credential status
|
|
292
|
+
api.registerTool(
|
|
293
|
+
() => {
|
|
294
|
+
return {
|
|
295
|
+
name: "aquaman_status",
|
|
296
|
+
description:
|
|
297
|
+
"Check aquaman credential proxy status and configured services",
|
|
298
|
+
parameters: {
|
|
299
|
+
type: "object" as const,
|
|
300
|
+
properties: {},
|
|
301
|
+
required: [] as string[],
|
|
302
|
+
},
|
|
303
|
+
async execute() {
|
|
304
|
+
return {
|
|
305
|
+
proxyRunning: proxyProcess !== null,
|
|
306
|
+
proxyPort,
|
|
307
|
+
services,
|
|
308
|
+
httpInterceptorActive: httpInterceptor?.isActive() ?? false,
|
|
309
|
+
environmentVariables: Object.fromEntries(
|
|
310
|
+
services.map((s) => {
|
|
311
|
+
const key =
|
|
312
|
+
s === "anthropic"
|
|
313
|
+
? "ANTHROPIC_BASE_URL"
|
|
314
|
+
: s === "openai"
|
|
315
|
+
? "OPENAI_BASE_URL"
|
|
316
|
+
: `${s.toUpperCase()}_BASE_URL`;
|
|
317
|
+
return [key, process.env[key] ?? null];
|
|
318
|
+
})
|
|
319
|
+
),
|
|
320
|
+
};
|
|
321
|
+
},
|
|
322
|
+
};
|
|
323
|
+
},
|
|
324
|
+
{ names: ["aquaman_status"] }
|
|
325
|
+
);
|
|
326
|
+
|
|
327
|
+
api.logger.info("Aquaman plugin registered successfully");
|
|
328
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "aquaman-plugin",
|
|
3
|
+
"name": "Aquaman Vault",
|
|
4
|
+
"description": "Credential isolation - API keys never touch the agent process",
|
|
5
|
+
"configSchema": {
|
|
6
|
+
"type": "object",
|
|
7
|
+
"additionalProperties": false,
|
|
8
|
+
"properties": {
|
|
9
|
+
"mode": {
|
|
10
|
+
"type": "string",
|
|
11
|
+
"enum": ["embedded", "proxy"],
|
|
12
|
+
"default": "embedded",
|
|
13
|
+
"description": "embedded: credentials in gateway memory, proxy: separate process"
|
|
14
|
+
},
|
|
15
|
+
"backend": {
|
|
16
|
+
"type": "string",
|
|
17
|
+
"enum": ["keychain", "1password", "vault", "encrypted-file"],
|
|
18
|
+
"default": "keychain",
|
|
19
|
+
"description": "Credential storage backend"
|
|
20
|
+
},
|
|
21
|
+
"services": {
|
|
22
|
+
"type": "array",
|
|
23
|
+
"items": { "type": "string" },
|
|
24
|
+
"default": ["anthropic", "openai"],
|
|
25
|
+
"description": "Services to proxy credentials for"
|
|
26
|
+
},
|
|
27
|
+
"proxyPort": {
|
|
28
|
+
"type": "number",
|
|
29
|
+
"default": 8081,
|
|
30
|
+
"description": "Port for credential proxy (proxy mode only)"
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "aquaman-plugin",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Credential isolation plugin for OpenClaw",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"build": "echo 'OpenClaw runs TypeScript directly - no build needed'",
|
|
8
|
+
"clean": "echo 'Nothing to clean'"
|
|
9
|
+
},
|
|
10
|
+
"openclaw": {
|
|
11
|
+
"extensions": ["./index.ts"]
|
|
12
|
+
},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"aquaman",
|
|
15
|
+
"openclaw",
|
|
16
|
+
"plugin",
|
|
17
|
+
"security",
|
|
18
|
+
"credentials",
|
|
19
|
+
"vault",
|
|
20
|
+
"1password"
|
|
21
|
+
],
|
|
22
|
+
"author": "tech4242",
|
|
23
|
+
"license": "MIT",
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"aquaman-core": "^0.1.0"
|
|
26
|
+
},
|
|
27
|
+
"peerDependencies": {
|
|
28
|
+
"openclaw": ">=2026.1.0"
|
|
29
|
+
},
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"@types/node": "^20.10.0",
|
|
32
|
+
"typescript": "^5.3.0"
|
|
33
|
+
},
|
|
34
|
+
"files": ["index.ts", "src", "openclaw.plugin.json", "LICENSE", "README.md"],
|
|
35
|
+
"repository": {
|
|
36
|
+
"type": "git",
|
|
37
|
+
"url": "https://github.com/tech4242/aquaman.git",
|
|
38
|
+
"directory": "packages/plugin"
|
|
39
|
+
},
|
|
40
|
+
"engines": {
|
|
41
|
+
"node": ">=18.0.0"
|
|
42
|
+
},
|
|
43
|
+
"homepage": "https://github.com/tech4242/aquaman/tree/main/packages/plugin#readme",
|
|
44
|
+
"bugs": {
|
|
45
|
+
"url": "https://github.com/tech4242/aquaman/issues"
|
|
46
|
+
}
|
|
47
|
+
}
|