aquaman-plugin 0.9.1 → 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.
Files changed (2) hide show
  1. package/index.ts +135 -92
  2. package/package.json +3 -3
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
- const services = ["anthropic", "openai"];
65
+ let configuredServices: string[] = ["anthropic", "openai"];
56
66
 
57
67
  /** Default socket path */
58
68
  function getDefaultSocketPath(): string {
@@ -169,7 +179,7 @@ function activateHttpInterceptor(log: OpenClawPluginApi["logger"]): void {
169
179
  /**
170
180
  * Set environment variables for SDK clients using sentinel hostname
171
181
  */
172
- function configureEnvironment(log: OpenClawPluginApi["logger"]): void {
182
+ function configureEnvironment(log: OpenClawPluginApi["logger"], services: string[]): void {
173
183
  for (const service of services) {
174
184
  const serviceUrl = `http://aquaman.local/${service}`;
175
185
 
@@ -197,11 +207,12 @@ function configureEnvironment(log: OpenClawPluginApi["logger"]): void {
197
207
  /**
198
208
  * Register the aquaman_status tool
199
209
  */
200
- function registerStatusTool(api: OpenClawPluginApi): void {
210
+ function registerStatusTool(api: OpenClawPluginApi, services: string[]): void {
201
211
  api.registerTool(
202
212
  () => {
203
213
  return {
204
214
  name: "aquaman_status",
215
+ label: "Aquaman Status",
205
216
  description:
206
217
  "Check aquaman credential proxy status and configured services",
207
218
  parameters: {
@@ -209,8 +220,8 @@ function registerStatusTool(api: OpenClawPluginApi): void {
209
220
  properties: {},
210
221
  required: [] as string[],
211
222
  },
212
- async execute() {
213
- return {
223
+ async execute(_toolCallId: string, _params: unknown) {
224
+ const status = {
214
225
  proxyRunning: proxyManager !== null,
215
226
  socketPath: socketPath || getDefaultSocketPath(),
216
227
  services,
@@ -227,6 +238,10 @@ function registerStatusTool(api: OpenClawPluginApi): void {
227
238
  })
228
239
  ),
229
240
  };
241
+ return {
242
+ content: [{ type: "text" as const, text: JSON.stringify(status, null, 2) }],
243
+ details: status,
244
+ };
230
245
  },
231
246
  };
232
247
  },
@@ -239,7 +254,7 @@ function registerStatusTool(api: OpenClawPluginApi): void {
239
254
  * OpenClaw checks its auth store before making API calls — without a placeholder
240
255
  * key, requests are rejected before they ever reach the proxy.
241
256
  */
242
- function ensureAuthProfiles(log: OpenClawPluginApi["logger"]): void {
257
+ function ensureAuthProfiles(log: OpenClawPluginApi["logger"], services: string[]): void {
243
258
  const stateDir =
244
259
  process.env.OPENCLAW_STATE_DIR ||
245
260
  path.join(os.homedir(), ".openclaw");
@@ -280,127 +295,155 @@ function ensureAuthProfiles(log: OpenClawPluginApi["logger"]): void {
280
295
  }
281
296
 
282
297
  /**
283
- * OpenClaw plugin register function
298
+ * Aquaman OpenClaw Plugin Definition
284
299
  */
285
- export default function register(api: OpenClawPluginApi): void {
286
- api.logger.info("Aquaman plugin loaded");
287
-
288
- // Auto-generate auth-profiles.json if missing
289
- ensureAuthProfiles(api.logger);
290
-
291
- // Local proxy mode — requires aquaman CLI
292
- if (!isAquamanInstalled()) {
293
- api.logger.warn(
294
- "aquaman CLI not found. Install with: npm install -g aquaman-proxy"
295
- );
296
- api.logger.warn(
297
- "Then run: aquaman setup"
298
- );
299
- configureEnvironment(api.logger);
300
- return;
301
- }
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
+ }
302
327
 
303
- api.logger.info("aquaman CLI found, will start proxy on gateway start");
328
+ api.logger.info("aquaman CLI found, will start proxy on gateway start");
304
329
 
305
- // Configure environment variables immediately (sentinel hostname)
306
- configureEnvironment(api.logger);
330
+ // Configure environment variables immediately (sentinel hostname)
331
+ configureEnvironment(api.logger, configuredServices);
307
332
 
308
- // Register lifecycle hooks if available
309
- if (api.registerLifecycle) {
310
- api.registerLifecycle({
311
- async onGatewayStart() {
312
- api.logger.info("Starting aquaman proxy...");
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...");
313
338
 
314
- const started = await startProxy(api.logger);
339
+ const started = await startProxy(ctx.logger);
315
340
  if (started && socketPath) {
316
- api.logger.info("Aquaman proxy started successfully");
341
+ ctx.logger.info("Aquaman proxy started successfully");
317
342
 
318
343
  // Check for version mismatch between plugin and proxy
319
344
  const proxyVersion = await getProxyVersion(socketPath);
320
345
  if (proxyVersion && proxyVersion !== PLUGIN_VERSION) {
321
- api.logger.warn(
346
+ ctx.logger.warn(
322
347
  `Warning: plugin version ${PLUGIN_VERSION} \u2260 proxy version ${proxyVersion}. ` +
323
348
  `Update both: npm install -g aquaman-proxy && openclaw plugins install aquaman-plugin`
324
349
  );
325
350
  }
326
351
 
327
352
  // Activate HTTP interceptor to redirect channel traffic through proxy
328
- activateHttpInterceptor(api.logger);
353
+ activateHttpInterceptor(ctx.logger);
329
354
  } else {
330
- api.logger.error("Failed to start aquaman proxy");
355
+ ctx.logger.error("Failed to start aquaman proxy");
331
356
  // Check if another instance is already running
332
357
  const defaultSock = getDefaultSocketPath();
333
358
  const alreadyRunning = await isProxyRunning(defaultSock);
334
359
  if (alreadyRunning) {
335
360
  socketPath = defaultSock;
336
- api.logger.info(
361
+ ctx.logger.info(
337
362
  "Another aquaman instance is already running — using it"
338
363
  );
339
364
  // Load host map from existing proxy
340
365
  const map = await loadHostMap(defaultSock);
341
366
  dynamicHostMap = map.size > 0 ? map : FALLBACK_HOST_MAP;
342
- activateHttpInterceptor(api.logger);
367
+ activateHttpInterceptor(ctx.logger);
343
368
  } else {
344
- api.logger.error(
369
+ ctx.logger.error(
345
370
  "No running proxy found. Check: aquaman doctor"
346
371
  );
347
372
  }
348
373
  }
349
374
  },
350
-
351
- async onGatewayStop() {
352
- api.logger.info("Stopping aquaman proxy...");
375
+ async stop(ctx) {
376
+ ctx.logger.info("Stopping aquaman proxy...");
353
377
  stopProxy();
354
- },
378
+ }
355
379
  });
356
- }
357
380
 
358
- // Register CLI commands if available
359
- if (api.registerCli) {
360
- api.registerCli(
361
- ({ program }) => {
362
- const aquamanCmd = program
363
- .command("aquaman")
364
- .description("Aquaman credential management");
365
-
366
- aquamanCmd
367
- .command("status")
368
- .description("Show aquaman proxy status")
369
- .action(() => {
370
- console.log("\nAquaman Status:");
371
- console.log(` Proxy running: ${proxyManager !== null}`);
372
- console.log(` Socket path: ${socketPath || getDefaultSocketPath()}`);
373
- console.log(` Services: ${services.join(", ")}`);
374
- console.log("\nEnvironment Variables:");
375
- for (const service of services) {
376
- const envKey =
377
- service === "anthropic"
378
- ? "ANTHROPIC_BASE_URL"
379
- : service === "openai"
380
- ? "OPENAI_BASE_URL"
381
- : `${service.toUpperCase()}_BASE_URL`;
382
- console.log(` ${envKey}=${process.env[envKey] ?? "(not set)"}`);
383
- }
384
- });
385
-
386
- aquamanCmd
387
- .command("add <service> [key]")
388
- .description("Add a credential (opens secure prompt)")
389
- .action((service: string, key: string = "api_key") => {
390
- console.log(`\n Run in your terminal:\n aquaman credentials add ${service} ${key}\n`);
391
- });
392
-
393
- aquamanCmd
394
- .command("list")
395
- .description("List stored credentials")
396
- .action(() => {
397
- console.log(`\n Run in your terminal:\n aquaman credentials list\n`);
398
- });
399
- },
400
- { commands: ["aquaman"] }
401
- );
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");
402
446
  }
447
+ };
403
448
 
404
- registerStatusTool(api);
405
- api.logger.info("Aquaman plugin registered successfully");
406
- }
449
+ export default plugin;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aquaman-plugin",
3
- "version": "0.9.1",
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.0",
30
- "aquaman-proxy": "0.9.1"
29
+ "openclaw": ">=2026.1.11",
30
+ "aquaman-proxy": "0.9.2"
31
31
  },
32
32
  "peerDependenciesMeta": {
33
33
  "aquaman-proxy": {