aquaman-plugin 0.6.0 → 0.7.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/README.md CHANGED
@@ -8,15 +8,16 @@ OpenClaw Gateway plugin for [aquaman](https://github.com/tech4242/aquaman) crede
8
8
  Agent / OpenClaw Gateway Aquaman Proxy
9
9
  ┌──────────────────────┐ ┌──────────────────────┐
10
10
  │ │ │ │
11
- │ ANTHROPIC_BASE_URL │──request────>│ Keychain / 1Pass / │
12
- │ = localhost:8081 Vault / Encrypted │
13
- │<─response────│
14
- │ fetch() interceptor │──channel────>│ + Auth injected: │
15
- │ redirects channel │ traffic │ header / url-path │
11
+ │ ANTHROPIC_BASE_URL │══ Unix ════>│ Keychain / 1Pass / │
12
+ │ = aquaman.local │ Domain │ Vault / Encrypted │
13
+ │<═ Socket ═══│
14
+ │ fetch() interceptor │══ (UDS) ══=>│ + Auth injected: │
15
+ │ redirects channel │ │ header / url-path │
16
16
  │ API traffic │ │ basic / oauth │
17
17
  │ │ │ │
18
- │ No credentials. │ │ │
19
- Nothing to steal. │ │
18
+ │ No credentials. │ ~/.aquaman/ │ │
19
+ No open ports. proxy.sock │ │
20
+ │ Nothing to steal. │ (chmod 600) │ │
20
21
  └──────────────────────┘ └───┬──────────┬───────┘
21
22
  │ │
22
23
  │ ▼
@@ -28,7 +29,7 @@ Agent / OpenClaw Gateway Aquaman Proxy
28
29
  slack.com/api ...
29
30
  ```
30
31
 
31
- This plugin makes the left side work. It routes all LLM and channel API traffic through the aquaman proxy so credentials never enter the Gateway process.
32
+ This plugin makes the left side work. It routes all LLM and channel API traffic through the aquaman proxy via Unix domain socket so credentials never enter the Gateway process. No TCP port is opened — traffic flows through `~/.aquaman/proxy.sock`.
32
33
 
33
34
  ## Quick Start
34
35
 
@@ -54,12 +55,10 @@ Troubleshooting: `aquaman doctor`
54
55
 
55
56
  | Key | Type | Default | Description |
56
57
  |-----|------|---------|-------------|
57
- | `mode` | `"embedded"` \| `"proxy"` | `"embedded"` | Isolation mode |
58
58
  | `backend` | `"keychain"` \| `"1password"` \| `"vault"` \| `"encrypted-file"` \| `"keepassxc"` | `"keychain"` | Credential store |
59
59
  | `services` | `string[]` | `["anthropic", "openai"]` | Services to proxy |
60
- | `proxyPort` | `number` | `8081` | Proxy listen port |
61
60
 
62
- > Advanced settings (TLS, audit, vault) go in `~/.aquaman/config.yaml`.
61
+ > Advanced settings (audit, vault) go in `~/.aquaman/config.yaml`.
63
62
 
64
63
  ## Security Audit Note
65
64
 
package/index.ts CHANGED
@@ -11,7 +11,7 @@
11
11
  *
12
12
  * The plugin will:
13
13
  * - Start the aquaman proxy on plugin load
14
- * - Set ANTHROPIC_BASE_URL, OPENAI_BASE_URL etc. to route through proxy
14
+ * - Set ANTHROPIC_BASE_URL, OPENAI_BASE_URL etc. to route through proxy via UDS
15
15
  * - The proxy injects credentials into requests
16
16
  * - Agent never sees the actual API keys
17
17
  */
@@ -50,12 +50,17 @@ try { PLUGIN_VERSION = JSON.parse(fs.readFileSync(pluginPkgPath, 'utf-8')).versi
50
50
 
51
51
  let proxyManager: ProxyManager | null = null;
52
52
  let httpInterceptor: HttpInterceptor | null = null;
53
- let clientToken: string | null = null;
53
+ let socketPath: string | null = null;
54
54
  let dynamicHostMap: Map<string, string> | null = null;
55
- const proxyPort = 8081;
56
55
  const services = ["anthropic", "openai"];
57
56
 
58
- /** Fallback host map used when proxy doesn't provide one (backward compat) */
57
+ /** Default socket path */
58
+ function getDefaultSocketPath(): string {
59
+ const configDir = path.join(os.homedir(), '.aquaman');
60
+ return path.join(configDir, 'proxy.sock');
61
+ }
62
+
63
+ /** Fallback host map used when proxy doesn't provide one */
59
64
  const FALLBACK_HOST_MAP = new Map<string, string>([
60
65
  ['api.anthropic.com', 'anthropic'],
61
66
  ['api.openai.com', 'openai'],
@@ -84,21 +89,6 @@ const FALLBACK_HOST_MAP = new Map<string, string>([
84
89
  ['chat.googleapis.com', 'google-chat'],
85
90
  ]);
86
91
 
87
- /**
88
- * Get external proxy URL from environment (for Docker two-container mode).
89
- * When set, the plugin skips spawning a local proxy and routes traffic to the external URL.
90
- */
91
- function getExternalProxyUrl(): string | null {
92
- return process.env.AQUAMAN_PROXY_URL || null;
93
- }
94
-
95
- /**
96
- * Get external client token from environment (for Docker two-container mode).
97
- */
98
- function getExternalClientToken(): string | null {
99
- return process.env.AQUAMAN_CLIENT_TOKEN || null;
100
- }
101
-
102
92
  /**
103
93
  * Check if aquaman CLI is installed (fs-based, no shell execution)
104
94
  */
@@ -109,12 +99,12 @@ function isAquamanInstalled(): boolean {
109
99
  /**
110
100
  * Start the aquaman proxy daemon using ProxyManager
111
101
  */
112
- async function startProxy(port: number, log: OpenClawPluginApi["logger"]): Promise<boolean> {
102
+ async function startProxy(log: OpenClawPluginApi["logger"]): Promise<boolean> {
113
103
  try {
114
104
  const mgr = createProxyManager({
115
- config: { proxyPort: port },
105
+ config: {},
116
106
  onReady: (info) => {
117
- clientToken = info.token || null;
107
+ socketPath = info.socketPath;
118
108
  if (info.hostMap && typeof info.hostMap === "object") {
119
109
  dynamicHostMap = new Map(Object.entries(info.hostMap));
120
110
  }
@@ -127,6 +117,7 @@ async function startProxy(port: number, log: OpenClawPluginApi["logger"]): Promi
127
117
  });
128
118
  await mgr.start();
129
119
  proxyManager = mgr;
120
+ socketPath = mgr.getSocketPath();
130
121
  return true;
131
122
  } catch (err) {
132
123
  log.error(`Failed to start proxy: ${err}`);
@@ -146,7 +137,7 @@ function stopProxy(): void {
146
137
  proxyManager.stop();
147
138
  proxyManager = null;
148
139
  }
149
- clientToken = null;
140
+ socketPath = null;
150
141
  }
151
142
 
152
143
  /**
@@ -154,16 +145,18 @@ function stopProxy(): void {
154
145
  * This is what provides credential isolation for channels that don't support base URL overrides.
155
146
  */
156
147
  function activateHttpInterceptor(log: OpenClawPluginApi["logger"]): void {
148
+ if (!socketPath) {
149
+ log.error("Cannot activate HTTP interceptor: no socket path");
150
+ return;
151
+ }
152
+
157
153
  // Use dynamic host map from proxy (includes custom services from services.yaml)
158
154
  // Falls back to builtin map for backward compatibility
159
155
  const hostMap = dynamicHostMap || FALLBACK_HOST_MAP;
160
156
 
161
- const baseUrl = getExternalProxyUrl() || `http://127.0.0.1:${proxyPort}`;
162
-
163
157
  httpInterceptor = createHttpInterceptor({
164
- proxyBaseUrl: baseUrl,
158
+ socketPath,
165
159
  hostMap,
166
- clientToken: clientToken || undefined,
167
160
  log: (msg) => log.info(msg),
168
161
  });
169
162
 
@@ -172,13 +165,11 @@ function activateHttpInterceptor(log: OpenClawPluginApi["logger"]): void {
172
165
  }
173
166
 
174
167
  /**
175
- * Set environment variables for SDK clients
168
+ * Set environment variables for SDK clients using sentinel hostname
176
169
  */
177
170
  function configureEnvironment(log: OpenClawPluginApi["logger"]): void {
178
- const baseUrl = getExternalProxyUrl() || `http://127.0.0.1:${proxyPort}`;
179
-
180
171
  for (const service of services) {
181
- const serviceUrl = `${baseUrl}/${service}`;
172
+ const serviceUrl = `http://aquaman.local/${service}`;
182
173
 
183
174
  switch (service) {
184
175
  case "anthropic":
@@ -202,10 +193,9 @@ function configureEnvironment(log: OpenClawPluginApi["logger"]): void {
202
193
  }
203
194
 
204
195
  /**
205
- * Register the aquaman_status tool (shared between local and external proxy modes)
196
+ * Register the aquaman_status tool
206
197
  */
207
198
  function registerStatusTool(api: OpenClawPluginApi): void {
208
- const externalUrl = getExternalProxyUrl();
209
199
  api.registerTool(
210
200
  () => {
211
201
  return {
@@ -219,10 +209,8 @@ function registerStatusTool(api: OpenClawPluginApi): void {
219
209
  },
220
210
  async execute() {
221
211
  return {
222
- externalProxy: externalUrl !== null,
223
- proxyUrl: externalUrl || `http://127.0.0.1:${proxyPort}`,
224
- proxyRunning: externalUrl !== null || proxyManager !== null,
225
- proxyPort,
212
+ proxyRunning: proxyManager !== null,
213
+ socketPath: socketPath || getDefaultSocketPath(),
226
214
  services,
227
215
  httpInterceptorActive: httpInterceptor?.isActive() ?? false,
228
216
  environmentVariables: Object.fromEntries(
@@ -297,37 +285,6 @@ export default function register(api: OpenClawPluginApi): void {
297
285
  // Auto-generate auth-profiles.json if missing
298
286
  ensureAuthProfiles(api.logger);
299
287
 
300
- const externalUrl = getExternalProxyUrl();
301
-
302
- // External proxy mode (Docker two-container architecture)
303
- if (externalUrl) {
304
- api.logger.info(`External proxy mode: ${externalUrl}`);
305
- clientToken = getExternalClientToken();
306
- configureEnvironment(api.logger);
307
-
308
- if (api.registerLifecycle) {
309
- api.registerLifecycle({
310
- async onGatewayStart() {
311
- // Load dynamic host map from external proxy (includes custom services)
312
- const map = await loadHostMap(externalUrl, clientToken);
313
- dynamicHostMap = map.size > 0 ? map : FALLBACK_HOST_MAP;
314
- activateHttpInterceptor(api.logger);
315
- api.logger.info("HTTP interceptor active (external proxy mode)");
316
- },
317
- async onGatewayStop() {
318
- if (httpInterceptor) {
319
- httpInterceptor.deactivate();
320
- httpInterceptor = null;
321
- }
322
- },
323
- });
324
- }
325
-
326
- registerStatusTool(api);
327
- api.logger.info("Aquaman plugin registered successfully");
328
- return;
329
- }
330
-
331
288
  // Local proxy mode — requires aquaman CLI
332
289
  if (!isAquamanInstalled()) {
333
290
  api.logger.warn(
@@ -342,22 +299,21 @@ export default function register(api: OpenClawPluginApi): void {
342
299
 
343
300
  api.logger.info("aquaman CLI found, will start proxy on gateway start");
344
301
 
345
- // Configure environment variables immediately
302
+ // Configure environment variables immediately (sentinel hostname)
346
303
  configureEnvironment(api.logger);
347
304
 
348
305
  // Register lifecycle hooks if available
349
306
  if (api.registerLifecycle) {
350
307
  api.registerLifecycle({
351
308
  async onGatewayStart() {
352
- api.logger.info(`Starting aquaman proxy on port ${proxyPort}...`);
309
+ api.logger.info("Starting aquaman proxy...");
353
310
 
354
- const started = await startProxy(proxyPort, api.logger);
355
- if (started) {
311
+ const started = await startProxy(api.logger);
312
+ if (started && socketPath) {
356
313
  api.logger.info("Aquaman proxy started successfully");
357
314
 
358
315
  // Check for version mismatch between plugin and proxy
359
- const proxyBaseUrl = `http://127.0.0.1:${proxyPort}`;
360
- const proxyVersion = await getProxyVersion(proxyBaseUrl);
316
+ const proxyVersion = await getProxyVersion(socketPath);
361
317
  if (proxyVersion && proxyVersion !== PLUGIN_VERSION) {
362
318
  api.logger.warn(
363
319
  `Warning: plugin version ${PLUGIN_VERSION} \u2260 proxy version ${proxyVersion}. ` +
@@ -368,19 +324,22 @@ export default function register(api: OpenClawPluginApi): void {
368
324
  // Activate HTTP interceptor to redirect channel traffic through proxy
369
325
  activateHttpInterceptor(api.logger);
370
326
  } else {
371
- api.logger.error(
372
- `Failed to start aquaman proxy on port ${proxyPort}`
373
- );
374
- // Check if another instance is already running on the port
375
- const alreadyRunning = await isProxyRunning(proxyPort);
327
+ api.logger.error("Failed to start aquaman proxy");
328
+ // Check if another instance is already running
329
+ const defaultSock = getDefaultSocketPath();
330
+ const alreadyRunning = await isProxyRunning(defaultSock);
376
331
  if (alreadyRunning) {
332
+ socketPath = defaultSock;
377
333
  api.logger.info(
378
- `Another aquaman instance is already running on port ${proxyPort} — using it`
334
+ "Another aquaman instance is already running — using it"
379
335
  );
336
+ // Load host map from existing proxy
337
+ const map = await loadHostMap(defaultSock);
338
+ dynamicHostMap = map.size > 0 ? map : FALLBACK_HOST_MAP;
380
339
  activateHttpInterceptor(api.logger);
381
340
  } else {
382
341
  api.logger.error(
383
- `Port ${proxyPort} may be in use. Check with: lsof -i :${proxyPort}`
342
+ "No running proxy found. Check: aquaman doctor"
384
343
  );
385
344
  }
386
345
  }
@@ -407,7 +366,7 @@ export default function register(api: OpenClawPluginApi): void {
407
366
  .action(() => {
408
367
  console.log("\nAquaman Status:");
409
368
  console.log(` Proxy running: ${proxyManager !== null}`);
410
- console.log(` Proxy port: ${proxyPort}`);
369
+ console.log(` Socket path: ${socketPath || getDefaultSocketPath()}`);
411
370
  console.log(` Services: ${services.join(", ")}`);
412
371
  console.log("\nEnvironment Variables:");
413
372
  for (const service of services) {
@@ -12,12 +12,6 @@
12
12
  "type": "object",
13
13
  "additionalProperties": false,
14
14
  "properties": {
15
- "mode": {
16
- "type": "string",
17
- "enum": ["embedded", "proxy"],
18
- "default": "embedded",
19
- "description": "embedded: credentials in gateway memory, proxy: separate process"
20
- },
21
15
  "backend": {
22
16
  "type": "string",
23
17
  "enum": ["keychain", "1password", "vault", "encrypted-file", "keepassxc"],
@@ -29,11 +23,6 @@
29
23
  "items": { "type": "string" },
30
24
  "default": ["anthropic", "openai"],
31
25
  "description": "Services to proxy credentials for"
32
- },
33
- "proxyPort": {
34
- "type": "number",
35
- "default": 8081,
36
- "description": "Port for credential proxy (proxy mode only)"
37
26
  }
38
27
  }
39
28
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aquaman-plugin",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
4
4
  "description": "Credential isolation plugin for OpenClaw",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -21,10 +21,12 @@
21
21
  ],
22
22
  "author": "tech4242",
23
23
  "license": "MIT",
24
- "dependencies": {},
24
+ "dependencies": {
25
+ "undici": "^7.0.0"
26
+ },
25
27
  "peerDependencies": {
26
28
  "openclaw": ">=2026.1.0",
27
- "aquaman-proxy": "0.6.0"
29
+ "aquaman-proxy": "0.7.0"
28
30
  },
29
31
  "peerDependenciesMeta": {
30
32
  "aquaman-proxy": {
package/src/commands.ts CHANGED
@@ -4,13 +4,11 @@
4
4
  * Provides /aquaman commands for users to interact with the plugin.
5
5
  */
6
6
 
7
- import type { EmbeddedMode } from './embedded.js';
8
7
  import type { ProxyManager } from './proxy-manager.js';
9
8
  import type { PluginConfig } from './config-schema.js';
10
9
 
11
10
  export interface CommandContext {
12
11
  config: PluginConfig;
13
- embeddedMode?: EmbeddedMode;
14
12
  proxyManager?: ProxyManager;
15
13
  }
16
14
 
@@ -37,43 +35,17 @@ export async function statusCommand(ctx: CommandContext): Promise<CommandResult>
37
35
 
38
36
  lines.push('aquaman plugin status');
39
37
  lines.push('');
40
- lines.push(`Mode: ${ctx.config.mode || 'embedded'}`);
41
38
  lines.push(`Backend: ${ctx.config.backend || 'keychain'}`);
42
39
  lines.push(`Services: ${(ctx.config.services || []).join(', ')}`);
43
40
 
44
- if (ctx.config.mode === 'proxy') {
45
- if (ctx.proxyManager?.isRunning()) {
46
- const info = ctx.proxyManager.getConnectionInfo();
47
- lines.push('');
48
- lines.push('Proxy Status: Running');
49
- lines.push(` URL: ${info?.baseUrl}`);
50
- lines.push(` Port: ${info?.port}`);
51
- lines.push(` Protocol: ${info?.protocol}`);
52
- } else {
53
- lines.push('');
54
- lines.push('Proxy Status: Not running');
55
- }
56
- } else if (ctx.embeddedMode) {
57
- const status = ctx.embeddedMode.getStatus();
41
+ if (ctx.proxyManager?.isRunning()) {
42
+ const info = ctx.proxyManager.getConnectionInfo();
58
43
  lines.push('');
59
- lines.push('Embedded Mode Status:');
60
- lines.push(` Initialized: ${status.initialized}`);
61
- lines.push(` Audit Enabled: ${status.auditEnabled}`);
62
- }
63
-
64
- // List credentials (without values)
65
- if (ctx.embeddedMode) {
66
- try {
67
- const creds = await ctx.embeddedMode.listCredentials();
68
- lines.push('');
69
- lines.push(`Stored Credentials: ${creds.length}`);
70
- for (const cred of creds) {
71
- lines.push(` - ${cred.service}/${cred.key}`);
72
- }
73
- } catch (error) {
74
- lines.push('');
75
- lines.push('Credentials: (unavailable)');
76
- }
44
+ lines.push('Proxy Status: Running');
45
+ lines.push(` Socket: ${info?.socketPath}`);
46
+ } else {
47
+ lines.push('');
48
+ lines.push('Proxy Status: Not running');
77
49
  }
78
50
 
79
51
  return {
@@ -86,19 +58,10 @@ export async function statusCommand(ctx: CommandContext): Promise<CommandResult>
86
58
  * /aquaman add <service> - Add a credential (prompts for value)
87
59
  */
88
60
  export async function addCommand(
89
- ctx: CommandContext,
61
+ _ctx: CommandContext,
90
62
  service: string,
91
63
  key: string = 'api_key'
92
64
  ): Promise<CommandResult> {
93
- if (!ctx.embeddedMode) {
94
- return {
95
- success: false,
96
- message: 'Embedded mode not available. Use proxy mode for credential management.'
97
- };
98
- }
99
-
100
- // Note: In a real OpenClaw plugin, this would trigger a secure input prompt
101
- // For now, we return instructions
102
65
  return {
103
66
  success: true,
104
67
  message: `To add a credential for ${service}/${key}:\n\n` +
@@ -111,143 +74,30 @@ export async function addCommand(
111
74
  /**
112
75
  * /aquaman list - List stored credentials
113
76
  */
114
- export async function listCommand(ctx: CommandContext): Promise<CommandResult> {
115
- if (!ctx.embeddedMode) {
116
- return {
117
- success: false,
118
- message: 'Embedded mode not available.'
119
- };
120
- }
121
-
122
- try {
123
- const creds = await ctx.embeddedMode.listCredentials();
124
-
125
- if (creds.length === 0) {
126
- return {
127
- success: true,
128
- message: 'No credentials stored.\n\nUse `aquaman credentials add <service> <key>` to add one.'
129
- };
130
- }
131
-
132
- const lines = ['Stored credentials:', ''];
133
- for (const cred of creds) {
134
- lines.push(` ${cred.service}/${cred.key}`);
135
- }
136
-
137
- return {
138
- success: true,
139
- message: lines.join('\n'),
140
- data: creds
141
- };
142
- } catch (error) {
143
- return {
144
- success: false,
145
- message: `Failed to list credentials: ${error}`
146
- };
147
- }
77
+ export async function listCommand(_ctx: CommandContext): Promise<CommandResult> {
78
+ return {
79
+ success: true,
80
+ message: 'Run in your terminal:\n aquaman credentials list'
81
+ };
148
82
  }
149
83
 
150
84
  /**
151
85
  * /aquaman logs - Show recent audit entries
152
86
  */
153
- export async function logsCommand(ctx: CommandContext, count: number = 10): Promise<CommandResult> {
154
- if (!ctx.embeddedMode) {
155
- return {
156
- success: false,
157
- message: 'Embedded mode not available.'
158
- };
159
- }
160
-
161
- try {
162
- const entries = await ctx.embeddedMode.getRecentAuditEntries(count);
163
-
164
- if (entries.length === 0) {
165
- return {
166
- success: true,
167
- message: 'No audit entries found.'
168
- };
169
- }
170
-
171
- const lines = [`Last ${entries.length} audit entries:`, ''];
172
- for (const entry of entries) {
173
- const time = new Date(entry.timestamp).toISOString();
174
- const type = entry.type.toUpperCase().padEnd(16);
175
- let details = '';
176
-
177
- if (entry.type === 'credential_access') {
178
- details = `${entry.data.service} ${entry.data.operation} ${entry.data.success ? 'OK' : 'FAIL'}`;
179
- } else {
180
- details = JSON.stringify(entry.data).slice(0, 60);
181
- }
182
-
183
- lines.push(`${time} [${type}] ${details}`);
184
- }
185
-
186
- return {
187
- success: true,
188
- message: lines.join('\n'),
189
- data: entries
190
- };
191
- } catch (error) {
192
- return {
193
- success: false,
194
- message: `Failed to get audit logs: ${error}`
195
- };
196
- }
87
+ export async function logsCommand(_ctx: CommandContext, _count: number = 10): Promise<CommandResult> {
88
+ return {
89
+ success: true,
90
+ message: 'Run in your terminal:\n aquaman audit tail'
91
+ };
197
92
  }
198
93
 
199
94
  /**
200
95
  * /aquaman verify - Verify audit log integrity
201
96
  */
202
- export async function verifyCommand(ctx: CommandContext): Promise<CommandResult> {
203
- if (!ctx.embeddedMode) {
204
- return {
205
- success: false,
206
- message: 'Embedded mode not available.'
207
- };
208
- }
209
-
210
- try {
211
- const result = await ctx.embeddedMode.verifyAuditIntegrity();
212
-
213
- if (result.valid) {
214
- return {
215
- success: true,
216
- message: 'Audit log integrity verified. No tampering detected.'
217
- };
218
- } else {
219
- return {
220
- success: false,
221
- message: 'Audit log integrity FAILED!\n\n' +
222
- 'Errors:\n' +
223
- result.errors.map(e => ` - ${e}`).join('\n')
224
- };
225
- }
226
- } catch (error) {
227
- return {
228
- success: false,
229
- message: `Failed to verify audit log: ${error}`
230
- };
231
- }
232
- }
233
-
234
- /**
235
- * /aquaman mode <embedded|proxy> - Switch mode
236
- */
237
- export async function modeCommand(ctx: CommandContext, mode: 'embedded' | 'proxy'): Promise<CommandResult> {
238
- if (mode !== 'embedded' && mode !== 'proxy') {
239
- return {
240
- success: false,
241
- message: 'Invalid mode. Use "embedded" or "proxy".'
242
- };
243
- }
244
-
245
- // Mode switching requires configuration change
97
+ export async function verifyCommand(_ctx: CommandContext): Promise<CommandResult> {
246
98
  return {
247
99
  success: true,
248
- message: `To switch to ${mode} mode, update your openclaw.json:\n\n` +
249
- `{\n "plugins": {\n "aquaman": {\n "mode": "${mode}"\n }\n }\n}\n\n` +
250
- `Then restart OpenClaw.`
100
+ message: 'Run in your terminal:\n aquaman audit verify'
251
101
  };
252
102
  }
253
103
 
@@ -272,36 +122,26 @@ export async function executeCommand(
272
122
  case 'list':
273
123
  return listCommand(ctx);
274
124
 
275
- case 'logs':
125
+ case 'logs': {
276
126
  const count = args[0] ? parseInt(args[0], 10) : 10;
277
127
  return logsCommand(ctx, count);
128
+ }
278
129
 
279
130
  case 'verify':
280
131
  return verifyCommand(ctx);
281
132
 
282
- case 'mode':
283
- if (args.length < 1) {
284
- return { success: false, message: 'Usage: /aquaman mode <embedded|proxy>' };
285
- }
286
- return modeCommand(ctx, args[0] as 'embedded' | 'proxy');
287
-
288
133
  case 'help':
289
134
  default:
290
135
  return {
291
136
  success: true,
292
137
  message: `aquaman plugin commands:
293
138
 
294
- /aquaman status - Show plugin status and stored credentials
139
+ /aquaman status - Show plugin status
295
140
  /aquaman add - Add a credential (shows instructions)
296
141
  /aquaman list - List stored credentials
297
- /aquaman logs [n] - Show recent audit entries (default: 10)
142
+ /aquaman logs [n] - Show recent audit entries
298
143
  /aquaman verify - Verify audit log integrity
299
- /aquaman mode - Switch between embedded and proxy mode
300
- /aquaman help - Show this help message
301
-
302
- Mode comparison:
303
- embedded: Simpler setup, credentials in Gateway memory
304
- proxy: Stronger isolation, credentials in separate process`
144
+ /aquaman help - Show this help message`
305
145
  };
306
146
  }
307
147
  }
@@ -313,7 +153,7 @@ export function getAvailableCommands(ctx: CommandContext): PluginCommand[] {
313
153
  return [
314
154
  {
315
155
  name: 'status',
316
- description: 'Show aquaman plugin status and stored credentials',
156
+ description: 'Show aquaman plugin status',
317
157
  execute: async () => {
318
158
  const result = await statusCommand(ctx);
319
159
  return result.message;
@@ -357,18 +197,6 @@ export function getAvailableCommands(ctx: CommandContext): PluginCommand[] {
357
197
  return result.message;
358
198
  }
359
199
  },
360
- {
361
- name: 'mode',
362
- description: 'Switch between embedded and proxy mode',
363
- execute: async (args) => {
364
- const mode = args.mode || args._?.[0];
365
- if (!mode) {
366
- return 'Usage: /aquaman mode <embedded|proxy>';
367
- }
368
- const result = await modeCommand(ctx, mode as 'embedded' | 'proxy');
369
- return result.message;
370
- }
371
- },
372
200
  {
373
201
  name: 'help',
374
202
  description: 'Show help for aquaman commands',