aquaman-plugin 0.9.0 → 0.9.2
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 +1 -0
- package/index.ts +137 -92
- package/package.json +3 -3
package/README.md
CHANGED
package/index.ts
CHANGED
|
@@ -17,6 +17,16 @@
|
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
19
|
import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
|
|
20
|
+
|
|
21
|
+
// OpenClawPluginDefinition exists in the SDK internals but isn't re-exported from "openclaw/plugin-sdk".
|
|
22
|
+
// Mirror the type here until OpenClaw exposes it from the barrel.
|
|
23
|
+
type OpenClawPluginDefinition = {
|
|
24
|
+
id?: string;
|
|
25
|
+
name?: string;
|
|
26
|
+
description?: string;
|
|
27
|
+
version?: string;
|
|
28
|
+
register?: (api: OpenClawPluginApi) => void | Promise<void>;
|
|
29
|
+
};
|
|
20
30
|
import * as fs from "node:fs";
|
|
21
31
|
import * as path from "node:path";
|
|
22
32
|
import * as os from "node:os";
|
|
@@ -52,7 +62,7 @@ let proxyManager: ProxyManager | null = null;
|
|
|
52
62
|
let httpInterceptor: HttpInterceptor | null = null;
|
|
53
63
|
let socketPath: string | null = null;
|
|
54
64
|
let dynamicHostMap: Map<string, string> | null = null;
|
|
55
|
-
|
|
65
|
+
let configuredServices: string[] = ["anthropic", "openai"];
|
|
56
66
|
|
|
57
67
|
/** Default socket path */
|
|
58
68
|
function getDefaultSocketPath(): string {
|
|
@@ -67,6 +77,8 @@ const FALLBACK_HOST_MAP = new Map<string, string>([
|
|
|
67
77
|
['api.github.com', 'github'],
|
|
68
78
|
['api.x.ai', 'xai'],
|
|
69
79
|
['gateway.ai.cloudflare.com', 'cloudflare-ai'],
|
|
80
|
+
['api.mistral.ai', 'mistral'],
|
|
81
|
+
['api-inference.huggingface.co', 'huggingface'],
|
|
70
82
|
['slack.com', 'slack'],
|
|
71
83
|
['*.slack.com', 'slack'],
|
|
72
84
|
['discord.com', 'discord'],
|
|
@@ -167,7 +179,7 @@ function activateHttpInterceptor(log: OpenClawPluginApi["logger"]): void {
|
|
|
167
179
|
/**
|
|
168
180
|
* Set environment variables for SDK clients using sentinel hostname
|
|
169
181
|
*/
|
|
170
|
-
function configureEnvironment(log: OpenClawPluginApi["logger"]): void {
|
|
182
|
+
function configureEnvironment(log: OpenClawPluginApi["logger"], services: string[]): void {
|
|
171
183
|
for (const service of services) {
|
|
172
184
|
const serviceUrl = `http://aquaman.local/${service}`;
|
|
173
185
|
|
|
@@ -195,11 +207,12 @@ function configureEnvironment(log: OpenClawPluginApi["logger"]): void {
|
|
|
195
207
|
/**
|
|
196
208
|
* Register the aquaman_status tool
|
|
197
209
|
*/
|
|
198
|
-
function registerStatusTool(api: OpenClawPluginApi): void {
|
|
210
|
+
function registerStatusTool(api: OpenClawPluginApi, services: string[]): void {
|
|
199
211
|
api.registerTool(
|
|
200
212
|
() => {
|
|
201
213
|
return {
|
|
202
214
|
name: "aquaman_status",
|
|
215
|
+
label: "Aquaman Status",
|
|
203
216
|
description:
|
|
204
217
|
"Check aquaman credential proxy status and configured services",
|
|
205
218
|
parameters: {
|
|
@@ -207,8 +220,8 @@ function registerStatusTool(api: OpenClawPluginApi): void {
|
|
|
207
220
|
properties: {},
|
|
208
221
|
required: [] as string[],
|
|
209
222
|
},
|
|
210
|
-
async execute() {
|
|
211
|
-
|
|
223
|
+
async execute(_toolCallId: string, _params: unknown) {
|
|
224
|
+
const status = {
|
|
212
225
|
proxyRunning: proxyManager !== null,
|
|
213
226
|
socketPath: socketPath || getDefaultSocketPath(),
|
|
214
227
|
services,
|
|
@@ -225,6 +238,10 @@ function registerStatusTool(api: OpenClawPluginApi): void {
|
|
|
225
238
|
})
|
|
226
239
|
),
|
|
227
240
|
};
|
|
241
|
+
return {
|
|
242
|
+
content: [{ type: "text" as const, text: JSON.stringify(status, null, 2) }],
|
|
243
|
+
details: status,
|
|
244
|
+
};
|
|
228
245
|
},
|
|
229
246
|
};
|
|
230
247
|
},
|
|
@@ -237,7 +254,7 @@ function registerStatusTool(api: OpenClawPluginApi): void {
|
|
|
237
254
|
* OpenClaw checks its auth store before making API calls — without a placeholder
|
|
238
255
|
* key, requests are rejected before they ever reach the proxy.
|
|
239
256
|
*/
|
|
240
|
-
function ensureAuthProfiles(log: OpenClawPluginApi["logger"]): void {
|
|
257
|
+
function ensureAuthProfiles(log: OpenClawPluginApi["logger"], services: string[]): void {
|
|
241
258
|
const stateDir =
|
|
242
259
|
process.env.OPENCLAW_STATE_DIR ||
|
|
243
260
|
path.join(os.homedir(), ".openclaw");
|
|
@@ -278,127 +295,155 @@ function ensureAuthProfiles(log: OpenClawPluginApi["logger"]): void {
|
|
|
278
295
|
}
|
|
279
296
|
|
|
280
297
|
/**
|
|
281
|
-
* OpenClaw
|
|
298
|
+
* Aquaman OpenClaw Plugin Definition
|
|
282
299
|
*/
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
+
const plugin: OpenClawPluginDefinition = {
|
|
301
|
+
id: 'aquaman-plugin',
|
|
302
|
+
name: 'Aquaman Vault',
|
|
303
|
+
version: PLUGIN_VERSION,
|
|
304
|
+
description: 'Credential isolation for OpenClaw — API keys never enter the agent process',
|
|
305
|
+
|
|
306
|
+
register(api) {
|
|
307
|
+
api.logger.info("Aquaman plugin loaded");
|
|
308
|
+
|
|
309
|
+
// Read services from plugin config
|
|
310
|
+
const pluginCfg = api.pluginConfig as { backend?: string; services?: string[] } | undefined;
|
|
311
|
+
configuredServices = pluginCfg?.services ?? ["anthropic", "openai"];
|
|
312
|
+
|
|
313
|
+
// Auto-generate auth-profiles.json if missing
|
|
314
|
+
ensureAuthProfiles(api.logger, configuredServices);
|
|
315
|
+
|
|
316
|
+
// Local proxy mode — requires aquaman CLI
|
|
317
|
+
if (!isAquamanInstalled()) {
|
|
318
|
+
api.logger.warn(
|
|
319
|
+
"aquaman CLI not found. Install with: npm install -g aquaman-proxy"
|
|
320
|
+
);
|
|
321
|
+
api.logger.warn(
|
|
322
|
+
"Then run: aquaman setup"
|
|
323
|
+
);
|
|
324
|
+
configureEnvironment(api.logger, configuredServices);
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
300
327
|
|
|
301
|
-
|
|
328
|
+
api.logger.info("aquaman CLI found, will start proxy on gateway start");
|
|
302
329
|
|
|
303
|
-
|
|
304
|
-
|
|
330
|
+
// Configure environment variables immediately (sentinel hostname)
|
|
331
|
+
configureEnvironment(api.logger, configuredServices);
|
|
305
332
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
async
|
|
310
|
-
|
|
333
|
+
// Register service for proxy lifecycle management
|
|
334
|
+
api.registerService({
|
|
335
|
+
id: 'aquaman-proxy',
|
|
336
|
+
async start(ctx) {
|
|
337
|
+
ctx.logger.info("Starting aquaman proxy...");
|
|
311
338
|
|
|
312
|
-
const started = await startProxy(
|
|
339
|
+
const started = await startProxy(ctx.logger);
|
|
313
340
|
if (started && socketPath) {
|
|
314
|
-
|
|
341
|
+
ctx.logger.info("Aquaman proxy started successfully");
|
|
315
342
|
|
|
316
343
|
// Check for version mismatch between plugin and proxy
|
|
317
344
|
const proxyVersion = await getProxyVersion(socketPath);
|
|
318
345
|
if (proxyVersion && proxyVersion !== PLUGIN_VERSION) {
|
|
319
|
-
|
|
346
|
+
ctx.logger.warn(
|
|
320
347
|
`Warning: plugin version ${PLUGIN_VERSION} \u2260 proxy version ${proxyVersion}. ` +
|
|
321
348
|
`Update both: npm install -g aquaman-proxy && openclaw plugins install aquaman-plugin`
|
|
322
349
|
);
|
|
323
350
|
}
|
|
324
351
|
|
|
325
352
|
// Activate HTTP interceptor to redirect channel traffic through proxy
|
|
326
|
-
activateHttpInterceptor(
|
|
353
|
+
activateHttpInterceptor(ctx.logger);
|
|
327
354
|
} else {
|
|
328
|
-
|
|
355
|
+
ctx.logger.error("Failed to start aquaman proxy");
|
|
329
356
|
// Check if another instance is already running
|
|
330
357
|
const defaultSock = getDefaultSocketPath();
|
|
331
358
|
const alreadyRunning = await isProxyRunning(defaultSock);
|
|
332
359
|
if (alreadyRunning) {
|
|
333
360
|
socketPath = defaultSock;
|
|
334
|
-
|
|
361
|
+
ctx.logger.info(
|
|
335
362
|
"Another aquaman instance is already running — using it"
|
|
336
363
|
);
|
|
337
364
|
// Load host map from existing proxy
|
|
338
365
|
const map = await loadHostMap(defaultSock);
|
|
339
366
|
dynamicHostMap = map.size > 0 ? map : FALLBACK_HOST_MAP;
|
|
340
|
-
activateHttpInterceptor(
|
|
367
|
+
activateHttpInterceptor(ctx.logger);
|
|
341
368
|
} else {
|
|
342
|
-
|
|
369
|
+
ctx.logger.error(
|
|
343
370
|
"No running proxy found. Check: aquaman doctor"
|
|
344
371
|
);
|
|
345
372
|
}
|
|
346
373
|
}
|
|
347
374
|
},
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
api.logger.info("Stopping aquaman proxy...");
|
|
375
|
+
async stop(ctx) {
|
|
376
|
+
ctx.logger.info("Stopping aquaman proxy...");
|
|
351
377
|
stopProxy();
|
|
352
|
-
}
|
|
378
|
+
}
|
|
353
379
|
});
|
|
354
|
-
}
|
|
355
380
|
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
381
|
+
// Register /aquaman-status slash command for humans
|
|
382
|
+
api.registerCommand({
|
|
383
|
+
name: 'aquaman-status',
|
|
384
|
+
description: 'Show aquaman credential proxy status and configured services',
|
|
385
|
+
acceptsArgs: false,
|
|
386
|
+
requireAuth: true,
|
|
387
|
+
async handler() {
|
|
388
|
+
const status = {
|
|
389
|
+
proxyRunning: proxyManager !== null,
|
|
390
|
+
socketPath: socketPath || getDefaultSocketPath(),
|
|
391
|
+
services: configuredServices,
|
|
392
|
+
httpInterceptorActive: httpInterceptor?.isActive() ?? false,
|
|
393
|
+
};
|
|
394
|
+
return { text: JSON.stringify(status, null, 2) };
|
|
395
|
+
}
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
// Register CLI commands if available
|
|
399
|
+
if (api.registerCli) {
|
|
400
|
+
api.registerCli(
|
|
401
|
+
({ program }) => {
|
|
402
|
+
const aquamanCmd = program
|
|
403
|
+
.command("aquaman")
|
|
404
|
+
.description("Aquaman credential management");
|
|
405
|
+
|
|
406
|
+
aquamanCmd
|
|
407
|
+
.command("status")
|
|
408
|
+
.description("Show aquaman proxy status")
|
|
409
|
+
.action(() => {
|
|
410
|
+
console.log("\nAquaman Status:");
|
|
411
|
+
console.log(` Proxy running: ${proxyManager !== null}`);
|
|
412
|
+
console.log(` Socket path: ${socketPath || getDefaultSocketPath()}`);
|
|
413
|
+
console.log(` Services: ${configuredServices.join(", ")}`);
|
|
414
|
+
console.log("\nEnvironment Variables:");
|
|
415
|
+
for (const service of configuredServices) {
|
|
416
|
+
const envKey =
|
|
417
|
+
service === "anthropic"
|
|
418
|
+
? "ANTHROPIC_BASE_URL"
|
|
419
|
+
: service === "openai"
|
|
420
|
+
? "OPENAI_BASE_URL"
|
|
421
|
+
: `${service.toUpperCase()}_BASE_URL`;
|
|
422
|
+
console.log(` ${envKey}=${process.env[envKey] ?? "(not set)"}`);
|
|
423
|
+
}
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
aquamanCmd
|
|
427
|
+
.command("add <service> [key]")
|
|
428
|
+
.description("Add a credential (opens secure prompt)")
|
|
429
|
+
.action((service: string, key: string = "api_key") => {
|
|
430
|
+
console.log(`\n Run in your terminal:\n aquaman credentials add ${service} ${key}\n`);
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
aquamanCmd
|
|
434
|
+
.command("list")
|
|
435
|
+
.description("List stored credentials")
|
|
436
|
+
.action(() => {
|
|
437
|
+
console.log(`\n Run in your terminal:\n aquaman credentials list\n`);
|
|
438
|
+
});
|
|
439
|
+
},
|
|
440
|
+
{ commands: ["aquaman"] }
|
|
441
|
+
);
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
registerStatusTool(api, configuredServices);
|
|
445
|
+
api.logger.info("Aquaman plugin registered successfully");
|
|
400
446
|
}
|
|
447
|
+
};
|
|
401
448
|
|
|
402
|
-
|
|
403
|
-
api.logger.info("Aquaman plugin registered successfully");
|
|
404
|
-
}
|
|
449
|
+
export default plugin;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "aquaman-plugin",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.2",
|
|
4
4
|
"description": "Credential isolation plugin for OpenClaw",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
@@ -26,8 +26,8 @@
|
|
|
26
26
|
"undici": "^7.0.0"
|
|
27
27
|
},
|
|
28
28
|
"peerDependencies": {
|
|
29
|
-
"openclaw": ">=2026.1.
|
|
30
|
-
"aquaman-proxy": "0.9.
|
|
29
|
+
"openclaw": ">=2026.1.11",
|
|
30
|
+
"aquaman-proxy": "0.9.2"
|
|
31
31
|
},
|
|
32
32
|
"peerDependenciesMeta": {
|
|
33
33
|
"aquaman-proxy": {
|