aquaman-proxy 0.6.0 → 0.7.1
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 +10 -9
- package/dist/cli/index.js +154 -99
- package/dist/cli/index.js.map +1 -1
- package/dist/core/audit/index.d.ts +5 -0
- package/dist/core/audit/index.d.ts.map +1 -0
- package/dist/core/audit/index.js +5 -0
- package/dist/core/audit/index.js.map +1 -0
- package/dist/core/audit/logger.d.ts +53 -0
- package/dist/core/audit/logger.d.ts.map +1 -0
- package/dist/core/audit/logger.js +262 -0
- package/dist/core/audit/logger.js.map +1 -0
- package/dist/core/credentials/backends/keepassxc.d.ts +45 -0
- package/dist/core/credentials/backends/keepassxc.d.ts.map +1 -0
- package/dist/core/credentials/backends/keepassxc.js +229 -0
- package/dist/core/credentials/backends/keepassxc.js.map +1 -0
- package/dist/core/credentials/backends/onepassword.d.ts +38 -0
- package/dist/core/credentials/backends/onepassword.d.ts.map +1 -0
- package/dist/core/credentials/backends/onepassword.js +223 -0
- package/dist/core/credentials/backends/onepassword.js.map +1 -0
- package/dist/core/credentials/backends/vault.d.ts +56 -0
- package/dist/core/credentials/backends/vault.d.ts.map +1 -0
- package/dist/core/credentials/backends/vault.js +206 -0
- package/dist/core/credentials/backends/vault.js.map +1 -0
- package/dist/core/credentials/index.d.ts +8 -0
- package/dist/core/credentials/index.d.ts.map +1 -0
- package/dist/core/credentials/index.js +8 -0
- package/dist/core/credentials/index.js.map +1 -0
- package/dist/core/credentials/store.d.ts +102 -0
- package/dist/core/credentials/store.d.ts.map +1 -0
- package/dist/core/credentials/store.js +289 -0
- package/dist/core/credentials/store.js.map +1 -0
- package/dist/core/index.d.ts +14 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +18 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/types.d.ts +81 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +11 -0
- package/dist/core/types.js.map +1 -0
- package/dist/core/utils/config.d.ts +19 -0
- package/dist/core/utils/config.d.ts.map +1 -0
- package/dist/core/utils/config.js +136 -0
- package/dist/core/utils/config.js.map +1 -0
- package/dist/core/utils/hash.d.ts +27 -0
- package/dist/core/utils/hash.d.ts.map +1 -0
- package/dist/core/utils/hash.js +348 -0
- package/dist/core/utils/hash.js.map +1 -0
- package/dist/core/utils/index.d.ts +6 -0
- package/dist/core/utils/index.d.ts.map +1 -0
- package/dist/core/utils/index.js +6 -0
- package/dist/core/utils/index.js.map +1 -0
- package/dist/daemon.d.ts +4 -19
- package/dist/daemon.d.ts.map +1 -1
- package/dist/daemon.js +52 -101
- package/dist/daemon.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/migration/openclaw-migrator.d.ts +1 -1
- package/dist/migration/openclaw-migrator.d.ts.map +1 -1
- package/dist/oauth-token-cache.d.ts +1 -1
- package/dist/oauth-token-cache.d.ts.map +1 -1
- package/dist/openclaw/env-writer.d.ts +7 -7
- package/dist/openclaw/env-writer.d.ts.map +1 -1
- package/dist/openclaw/env-writer.js +8 -13
- package/dist/openclaw/env-writer.js.map +1 -1
- package/dist/openclaw/integration.d.ts +5 -3
- package/dist/openclaw/integration.d.ts.map +1 -1
- package/dist/openclaw/integration.js +7 -14
- package/dist/openclaw/integration.js.map +1 -1
- package/package.json +8 -3
package/README.md
CHANGED
|
@@ -8,15 +8,16 @@ Credential isolation proxy and CLI for [aquaman](https://github.com/tech4242/aqu
|
|
|
8
8
|
Agent / OpenClaw Gateway Aquaman Proxy
|
|
9
9
|
┌──────────────────────┐ ┌──────────────────────┐
|
|
10
10
|
│ │ │ │
|
|
11
|
-
│ ANTHROPIC_BASE_URL
|
|
12
|
-
│ =
|
|
13
|
-
│
|
|
14
|
-
│ fetch() interceptor
|
|
15
|
-
│ redirects channel │
|
|
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
|
-
│
|
|
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 package is the right side. A reverse proxy that
|
|
32
|
+
This package is the right side. A reverse proxy that listens on a Unix domain socket (`~/.aquaman/proxy.sock`) and injects credentials from secure backends. No TCP port, no network exposure. 23 builtin services, four auth modes.
|
|
32
33
|
|
|
33
34
|
## Quick Start
|
|
34
35
|
|
|
@@ -54,7 +55,7 @@ Standalone:
|
|
|
54
55
|
npm install -g aquaman-proxy
|
|
55
56
|
aquaman init
|
|
56
57
|
aquaman credentials add anthropic api_key
|
|
57
|
-
aquaman daemon
|
|
58
|
+
aquaman daemon # listens on ~/.aquaman/proxy.sock
|
|
58
59
|
```
|
|
59
60
|
|
|
60
61
|
Troubleshooting: `aquaman doctor`
|
package/dist/cli/index.js
CHANGED
|
@@ -9,10 +9,10 @@
|
|
|
9
9
|
* - Custom service registry: YAML-based service config
|
|
10
10
|
*/
|
|
11
11
|
import { Command } from 'commander';
|
|
12
|
-
import * as crypto from 'node:crypto';
|
|
13
12
|
import * as path from 'node:path';
|
|
14
13
|
import * as fs from 'node:fs';
|
|
15
|
-
import
|
|
14
|
+
import * as http from 'node:http';
|
|
15
|
+
import { loadConfig, getConfigDir, ensureConfigDir, getDefaultConfig, expandPath, createAuditLogger, createCredentialStore, saveConfig } from '../core/index.js';
|
|
16
16
|
import { fileURLToPath } from 'node:url';
|
|
17
17
|
import { createCredentialProxy } from '../daemon.js';
|
|
18
18
|
import { createServiceRegistry, ServiceRegistry } from '../service-registry.js';
|
|
@@ -54,6 +54,48 @@ const isProcessRunning = (pid) => {
|
|
|
54
54
|
return false;
|
|
55
55
|
}
|
|
56
56
|
};
|
|
57
|
+
/** Prompt for secret input with echo suppression (TTY) or plain readline (pipe) */
|
|
58
|
+
async function promptSecretInput(prompt) {
|
|
59
|
+
const readline = await import('node:readline');
|
|
60
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
61
|
+
if (process.stdin.isTTY) {
|
|
62
|
+
return new Promise((resolve) => {
|
|
63
|
+
process.stdout.write(prompt);
|
|
64
|
+
const stdin = process.stdin;
|
|
65
|
+
stdin.setRawMode(true);
|
|
66
|
+
stdin.resume();
|
|
67
|
+
let input = '';
|
|
68
|
+
const onData = (data) => {
|
|
69
|
+
const char = data.toString();
|
|
70
|
+
if (char === '\n' || char === '\r') {
|
|
71
|
+
stdin.setRawMode(false);
|
|
72
|
+
stdin.removeListener('data', onData);
|
|
73
|
+
stdin.pause();
|
|
74
|
+
rl.close();
|
|
75
|
+
process.stdout.write('\n');
|
|
76
|
+
resolve(input.trim());
|
|
77
|
+
}
|
|
78
|
+
else if (char === '\x7f' || char === '\b') {
|
|
79
|
+
input = input.slice(0, -1);
|
|
80
|
+
}
|
|
81
|
+
else if (char === '\x03') {
|
|
82
|
+
stdin.setRawMode(false);
|
|
83
|
+
process.exit(0);
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
input += char;
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
stdin.on('data', onData);
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
return new Promise((resolve) => {
|
|
93
|
+
rl.question(prompt, (answer) => {
|
|
94
|
+
rl.close();
|
|
95
|
+
resolve(answer.trim());
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
}
|
|
57
99
|
const program = new Command();
|
|
58
100
|
program
|
|
59
101
|
.name('aquaman')
|
|
@@ -83,8 +125,7 @@ program
|
|
|
83
125
|
if (options.dryRun) {
|
|
84
126
|
console.log('Dry run - would start with this configuration:\n');
|
|
85
127
|
console.log('Credential proxy:');
|
|
86
|
-
console.log(`
|
|
87
|
-
console.log(` TLS: ${config.credentials.tls?.enabled ? 'enabled' : 'disabled'}`);
|
|
128
|
+
console.log(` Socket: ${path.join(getConfigDir(), 'proxy.sock')}`);
|
|
88
129
|
console.log(` Backend: ${config.credentials.backend}`);
|
|
89
130
|
console.log(` Services: ${config.credentials.proxiedServices.join(', ')}`);
|
|
90
131
|
console.log('');
|
|
@@ -127,14 +168,12 @@ program
|
|
|
127
168
|
// Initialize service registry
|
|
128
169
|
const serviceRegistry = createServiceRegistry({ configPath: config.services.configPath });
|
|
129
170
|
// Start credential proxy
|
|
130
|
-
const
|
|
171
|
+
const socketPath = path.join(getConfigDir(), 'proxy.sock');
|
|
131
172
|
const credentialProxy = createCredentialProxy({
|
|
132
|
-
|
|
133
|
-
bindAddress: bindAddr,
|
|
173
|
+
socketPath,
|
|
134
174
|
store: credentialStore,
|
|
135
175
|
allowedServices: config.credentials.proxiedServices,
|
|
136
176
|
serviceRegistry,
|
|
137
|
-
tls: config.credentials.tls,
|
|
138
177
|
onRequest: (info) => {
|
|
139
178
|
auditLogger.logCredentialAccess('system', 'system', {
|
|
140
179
|
service: info.service,
|
|
@@ -145,8 +184,7 @@ program
|
|
|
145
184
|
}
|
|
146
185
|
});
|
|
147
186
|
await credentialProxy.start();
|
|
148
|
-
|
|
149
|
-
console.log(`Credential proxy started on ${protocol}://${bindAddr}:${config.credentials.proxyPort}`);
|
|
187
|
+
console.log(`Credential proxy started on ${socketPath}`);
|
|
150
188
|
if (options.launch !== false && config.openclaw.autoLaunch) {
|
|
151
189
|
// Get services for OpenClaw integration
|
|
152
190
|
const services = serviceRegistry.getAll().filter(s => config.credentials.proxiedServices.includes(s.name));
|
|
@@ -225,6 +263,11 @@ program
|
|
|
225
263
|
process.kill(pid, 'SIGKILL');
|
|
226
264
|
}
|
|
227
265
|
removePidFile();
|
|
266
|
+
const sockPath = path.join(getConfigDir(), 'proxy.sock');
|
|
267
|
+
try {
|
|
268
|
+
fs.unlinkSync(sockPath);
|
|
269
|
+
}
|
|
270
|
+
catch { /* already removed */ }
|
|
228
271
|
console.log('Daemon stopped.');
|
|
229
272
|
}
|
|
230
273
|
catch (err) {
|
|
@@ -235,8 +278,7 @@ program
|
|
|
235
278
|
program
|
|
236
279
|
.command('daemon')
|
|
237
280
|
.description('Run the credential proxy daemon (for advanced users)')
|
|
238
|
-
.
|
|
239
|
-
.action(async (options) => {
|
|
281
|
+
.action(async () => {
|
|
240
282
|
// Check if daemon is already running
|
|
241
283
|
const existingPid = readPidFile();
|
|
242
284
|
if (existingPid && isProcessRunning(existingPid)) {
|
|
@@ -244,6 +286,7 @@ program
|
|
|
244
286
|
process.exit(1);
|
|
245
287
|
}
|
|
246
288
|
const config = loadConfig();
|
|
289
|
+
const socketPath = path.join(getConfigDir(), 'proxy.sock');
|
|
247
290
|
console.log('Starting aquaman credential proxy daemon...\n');
|
|
248
291
|
// Initialize audit logger
|
|
249
292
|
const auditLogger = createAuditLogger({
|
|
@@ -274,18 +317,12 @@ program
|
|
|
274
317
|
}
|
|
275
318
|
// Initialize service registry
|
|
276
319
|
const serviceRegistry = createServiceRegistry({ configPath: config.services.configPath });
|
|
277
|
-
// Client token: CLI flag → env var → none
|
|
278
|
-
const daemonClientToken = options.token || process.env.AQUAMAN_CLIENT_TOKEN || undefined;
|
|
279
320
|
// Start credential proxy
|
|
280
|
-
const bindAddr = config.credentials.bindAddress || '127.0.0.1';
|
|
281
321
|
const credentialProxy = createCredentialProxy({
|
|
282
|
-
|
|
283
|
-
bindAddress: bindAddr,
|
|
322
|
+
socketPath,
|
|
284
323
|
store: credentialStore,
|
|
285
324
|
allowedServices: config.credentials.proxiedServices,
|
|
286
325
|
serviceRegistry,
|
|
287
|
-
tls: config.credentials.tls,
|
|
288
|
-
clientToken: daemonClientToken,
|
|
289
326
|
onRequest: (info) => {
|
|
290
327
|
auditLogger.logCredentialAccess('system', 'system', {
|
|
291
328
|
service: info.service,
|
|
@@ -298,9 +335,7 @@ program
|
|
|
298
335
|
await credentialProxy.start();
|
|
299
336
|
// Write PID file
|
|
300
337
|
writePidFile();
|
|
301
|
-
|
|
302
|
-
console.log(`Credential proxy: ${protocol}://${bindAddr}:${config.credentials.proxyPort}`);
|
|
303
|
-
console.log(`Client auth: ${daemonClientToken ? 'enabled' : 'disabled'}`);
|
|
338
|
+
console.log(`Credential proxy: ${socketPath}`);
|
|
304
339
|
console.log(`Audit logging: ${config.audit.enabled ? 'enabled' : 'disabled'}`);
|
|
305
340
|
console.log(`Credential backend: ${config.credentials.backend}`);
|
|
306
341
|
console.log(`PID file: ${getPidFile()}`);
|
|
@@ -311,6 +346,10 @@ program
|
|
|
311
346
|
console.log('\nShutting down daemon...');
|
|
312
347
|
await credentialProxy.stop();
|
|
313
348
|
removePidFile();
|
|
349
|
+
try {
|
|
350
|
+
fs.unlinkSync(socketPath);
|
|
351
|
+
}
|
|
352
|
+
catch { /* already removed */ }
|
|
314
353
|
process.exit(0);
|
|
315
354
|
};
|
|
316
355
|
process.on('SIGINT', shutdown);
|
|
@@ -320,13 +359,9 @@ program
|
|
|
320
359
|
program
|
|
321
360
|
.command('plugin-mode')
|
|
322
361
|
.description('Run in plugin mode (managed by OpenClaw plugin)')
|
|
323
|
-
.
|
|
324
|
-
.option('--token <token>', 'Client authentication token (generated if not provided)')
|
|
325
|
-
.option('--ipc', 'Use IPC instead of HTTP for communication')
|
|
326
|
-
.action(async (options) => {
|
|
362
|
+
.action(async () => {
|
|
327
363
|
const config = loadConfig();
|
|
328
|
-
const
|
|
329
|
-
const clientToken = options.token || crypto.randomBytes(32).toString('hex');
|
|
364
|
+
const socketPath = path.join(getConfigDir(), 'proxy.sock');
|
|
330
365
|
// Initialize credential store
|
|
331
366
|
let credentialStore;
|
|
332
367
|
try {
|
|
@@ -348,21 +383,31 @@ program
|
|
|
348
383
|
console.error('Fix the backend configuration and retry. Run: aquaman doctor');
|
|
349
384
|
process.exit(1);
|
|
350
385
|
}
|
|
386
|
+
// Initialize audit logger
|
|
387
|
+
const auditLogger = createAuditLogger({
|
|
388
|
+
logDir: config.audit.logDir,
|
|
389
|
+
enabled: config.audit.enabled
|
|
390
|
+
});
|
|
391
|
+
await auditLogger.initialize();
|
|
351
392
|
// Initialize service registry
|
|
352
393
|
const serviceRegistry = createServiceRegistry({ configPath: config.services.configPath });
|
|
353
394
|
// Start credential proxy
|
|
354
395
|
const credentialProxy = createCredentialProxy({
|
|
355
|
-
|
|
356
|
-
bindAddress: config.credentials.bindAddress || '127.0.0.1',
|
|
396
|
+
socketPath,
|
|
357
397
|
store: credentialStore,
|
|
358
398
|
allowedServices: config.credentials.proxiedServices,
|
|
359
399
|
serviceRegistry,
|
|
360
|
-
|
|
361
|
-
|
|
400
|
+
onRequest: (info) => {
|
|
401
|
+
auditLogger.logCredentialAccess('system', 'system', {
|
|
402
|
+
service: info.service,
|
|
403
|
+
operation: 'use',
|
|
404
|
+
success: !info.error,
|
|
405
|
+
error: info.error
|
|
406
|
+
});
|
|
407
|
+
}
|
|
362
408
|
});
|
|
363
409
|
await credentialProxy.start();
|
|
364
|
-
|
|
365
|
-
// Build host map from service registry for the plugin's fetch interceptor
|
|
410
|
+
// Build host map from service registry for the plugin's interceptor
|
|
366
411
|
const hostMap = serviceRegistry.buildHostMap();
|
|
367
412
|
const hostMapObj = {};
|
|
368
413
|
for (const [pattern, serviceName] of hostMap) {
|
|
@@ -371,18 +416,19 @@ program
|
|
|
371
416
|
// Output connection info as JSON for plugin to parse
|
|
372
417
|
const connectionInfo = {
|
|
373
418
|
ready: true,
|
|
374
|
-
|
|
375
|
-
protocol,
|
|
376
|
-
baseUrl: `${protocol}://127.0.0.1:${credentialProxy.getPort()}`,
|
|
419
|
+
socketPath,
|
|
377
420
|
services: config.credentials.proxiedServices,
|
|
378
421
|
backend: config.credentials.backend,
|
|
379
|
-
token: clientToken,
|
|
380
422
|
hostMap: hostMapObj
|
|
381
423
|
};
|
|
382
424
|
console.log(JSON.stringify(connectionInfo));
|
|
383
425
|
// Handle shutdown
|
|
384
426
|
const shutdown = async () => {
|
|
385
427
|
await credentialProxy.stop();
|
|
428
|
+
try {
|
|
429
|
+
fs.unlinkSync(socketPath);
|
|
430
|
+
}
|
|
431
|
+
catch { /* already removed */ }
|
|
386
432
|
process.exit(0);
|
|
387
433
|
};
|
|
388
434
|
process.on('SIGINT', shutdown);
|
|
@@ -400,7 +446,7 @@ program
|
|
|
400
446
|
const registry = createServiceRegistry({ configPath: config.services.configPath });
|
|
401
447
|
const services = registry.getAll().filter(s => config.credentials.proxiedServices.includes(s.name));
|
|
402
448
|
const integration = createOpenClawIntegration(config, services);
|
|
403
|
-
const env = await integration.configureOpenClaw(
|
|
449
|
+
const env = await integration.configureOpenClaw();
|
|
404
450
|
if (options.dryRun || options.method === 'env') {
|
|
405
451
|
console.log('Environment variables for OpenClaw:\n');
|
|
406
452
|
for (const [key, value] of Object.entries(env)) {
|
|
@@ -418,7 +464,6 @@ program
|
|
|
418
464
|
.command('init')
|
|
419
465
|
.description('Initialize aquaman configuration')
|
|
420
466
|
.option('--force', 'Overwrite existing configuration')
|
|
421
|
-
.option('--no-tls', 'Skip TLS certificate generation')
|
|
422
467
|
.action(async (options) => {
|
|
423
468
|
ensureConfigDir();
|
|
424
469
|
const configPath = path.join(getConfigDir(), 'config.yaml');
|
|
@@ -428,36 +473,12 @@ program
|
|
|
428
473
|
return;
|
|
429
474
|
}
|
|
430
475
|
const config = getDefaultConfig();
|
|
431
|
-
fs.writeFileSync(configPath, yamlStringify(config), 'utf-8');
|
|
476
|
+
fs.writeFileSync(configPath, yamlStringify(config), { encoding: 'utf-8', mode: 0o600 });
|
|
432
477
|
console.log(`Created ${configPath}`);
|
|
433
478
|
// Create audit directory
|
|
434
479
|
const auditDir = path.join(getConfigDir(), 'audit');
|
|
435
|
-
fs.mkdirSync(auditDir, { recursive: true });
|
|
480
|
+
fs.mkdirSync(auditDir, { recursive: true, mode: 0o700 });
|
|
436
481
|
console.log(`Created ${auditDir}`);
|
|
437
|
-
// Generate TLS certificates if enabled
|
|
438
|
-
if (options.tls !== false && config.credentials.tls?.autoGenerate) {
|
|
439
|
-
const certsDir = path.join(getConfigDir(), 'certs');
|
|
440
|
-
fs.mkdirSync(certsDir, { recursive: true });
|
|
441
|
-
const certPath = config.credentials.tls.certPath || path.join(certsDir, 'proxy.crt');
|
|
442
|
-
const keyPath = config.credentials.tls.keyPath || path.join(certsDir, 'proxy.key');
|
|
443
|
-
if (!fs.existsSync(certPath) || options.force) {
|
|
444
|
-
console.log('Generating TLS certificates...');
|
|
445
|
-
try {
|
|
446
|
-
const { cert, key } = generateSelfSignedCert('aquaman-proxy', 365);
|
|
447
|
-
fs.writeFileSync(certPath, cert, { mode: 0o644 });
|
|
448
|
-
fs.writeFileSync(keyPath, key, { mode: 0o600 });
|
|
449
|
-
console.log(`Created ${certPath}`);
|
|
450
|
-
console.log(`Created ${keyPath}`);
|
|
451
|
-
}
|
|
452
|
-
catch (error) {
|
|
453
|
-
console.error('Warning: Failed to generate TLS certificates:', error);
|
|
454
|
-
console.log('TLS will be disabled. Run "aquaman init --force" to retry.');
|
|
455
|
-
}
|
|
456
|
-
}
|
|
457
|
-
else {
|
|
458
|
-
console.log('TLS certificates already exist (use --force to regenerate)');
|
|
459
|
-
}
|
|
460
|
-
}
|
|
461
482
|
console.log('\nNext steps:');
|
|
462
483
|
console.log('1. Add your API keys: aquaman credentials add anthropic api_key');
|
|
463
484
|
console.log('2. Start the proxy: aquaman start');
|
|
@@ -574,12 +595,8 @@ program
|
|
|
574
595
|
process.exit(1);
|
|
575
596
|
}
|
|
576
597
|
else {
|
|
577
|
-
|
|
578
|
-
const
|
|
579
|
-
const password = await new Promise((resolve) => {
|
|
580
|
-
rl.question(' ? KeePassXC master password: ', resolve);
|
|
581
|
-
});
|
|
582
|
-
rl.close();
|
|
598
|
+
// Use hidden input to avoid echoing the master password
|
|
599
|
+
const password = await promptSecretInput(' ? KeePassXC master password: ');
|
|
583
600
|
if (!password.trim()) {
|
|
584
601
|
console.error(' KeePassXC backend requires a master password.');
|
|
585
602
|
process.exit(1);
|
|
@@ -588,15 +605,14 @@ program
|
|
|
588
605
|
}
|
|
589
606
|
}
|
|
590
607
|
}
|
|
591
|
-
// 2. Run init internally (create dirs, config
|
|
608
|
+
// 2. Run init internally (create dirs, config)
|
|
592
609
|
ensureConfigDir();
|
|
593
610
|
const config = getDefaultConfig();
|
|
594
611
|
config.credentials.backend = backend;
|
|
595
|
-
|
|
596
|
-
fs.writeFileSync(configPath, yamlStringify(config), 'utf-8');
|
|
612
|
+
fs.writeFileSync(configPath, yamlStringify(config), { encoding: 'utf-8', mode: 0o600 });
|
|
597
613
|
// Create audit directory
|
|
598
614
|
const auditDir = path.join(configDir, 'audit');
|
|
599
|
-
fs.mkdirSync(auditDir, { recursive: true });
|
|
615
|
+
fs.mkdirSync(auditDir, { recursive: true, mode: 0o700 });
|
|
600
616
|
// 3. Prompt for API keys (or read from env in non-interactive mode)
|
|
601
617
|
let store;
|
|
602
618
|
try {
|
|
@@ -761,10 +777,8 @@ program
|
|
|
761
777
|
openclawConfig.plugins.entries['aquaman-plugin'] = {
|
|
762
778
|
enabled: true,
|
|
763
779
|
config: {
|
|
764
|
-
mode: 'proxy',
|
|
765
780
|
backend,
|
|
766
781
|
services: storedServices.length > 0 ? storedServices : ['anthropic', 'openai'],
|
|
767
|
-
proxyPort: 8081
|
|
768
782
|
}
|
|
769
783
|
};
|
|
770
784
|
fs.writeFileSync(openclawJsonPath, JSON.stringify(openclawConfig, null, 2), 'utf-8');
|
|
@@ -786,8 +800,8 @@ program
|
|
|
786
800
|
}
|
|
787
801
|
}
|
|
788
802
|
const profilesDir = path.dirname(profilesPath);
|
|
789
|
-
fs.mkdirSync(profilesDir, { recursive: true });
|
|
790
|
-
fs.writeFileSync(profilesPath, JSON.stringify({ version: 1, profiles, order }, null, 2));
|
|
803
|
+
fs.mkdirSync(profilesDir, { recursive: true, mode: 0o700 });
|
|
804
|
+
fs.writeFileSync(profilesPath, JSON.stringify({ version: 1, profiles, order }, null, 2), { mode: 0o600 });
|
|
791
805
|
console.log(' \u2713 Auth profiles generated at ' + profilesPath);
|
|
792
806
|
}
|
|
793
807
|
}
|
|
@@ -900,35 +914,77 @@ program
|
|
|
900
914
|
config = loadConfig();
|
|
901
915
|
}
|
|
902
916
|
// 4. Proxy running
|
|
903
|
-
const
|
|
917
|
+
const sockPath = path.join(getConfigDir(), 'proxy.sock');
|
|
904
918
|
const pluginInstalled = fs.existsSync(path.join(openclawStateDir, 'extensions', 'aquaman-plugin'));
|
|
905
919
|
const proxyFix = pluginInstalled
|
|
906
920
|
? 'Proxy starts automatically with OpenClaw. Run: openclaw'
|
|
907
921
|
: 'Install plugin first: aquaman setup';
|
|
908
922
|
try {
|
|
909
|
-
const
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
+
const healthData = await new Promise((resolve, reject) => {
|
|
924
|
+
const req = http.request({ socketPath: sockPath, path: '/_health', method: 'GET' }, (res) => {
|
|
925
|
+
let body = '';
|
|
926
|
+
res.on('data', (chunk) => { body += chunk.toString(); });
|
|
927
|
+
res.on('end', () => {
|
|
928
|
+
if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300) {
|
|
929
|
+
resolve(body);
|
|
930
|
+
}
|
|
931
|
+
else {
|
|
932
|
+
reject(new Error(`HTTP ${res.statusCode}`));
|
|
933
|
+
}
|
|
934
|
+
});
|
|
935
|
+
});
|
|
936
|
+
req.on('error', reject);
|
|
937
|
+
req.end();
|
|
938
|
+
});
|
|
939
|
+
const health = JSON.parse(healthData);
|
|
940
|
+
const proxyVer = health.version || 'unknown';
|
|
941
|
+
console.log(` \u2713 ${aqua('Proxy')} running on socket (v${proxyVer})`);
|
|
942
|
+
if (health.version && health.version !== VERSION) {
|
|
943
|
+
console.log(` \u2717 ${aqua('Version mismatch:')} CLI v${VERSION} \u2260 proxy v${health.version}`);
|
|
944
|
+
console.log(' \u2192 Update: npm install -g aquaman-proxy');
|
|
923
945
|
issues++;
|
|
924
946
|
}
|
|
925
947
|
}
|
|
926
948
|
catch {
|
|
927
|
-
console.log(` \u2717 ${aqua('Proxy')} not running on
|
|
949
|
+
console.log(` \u2717 ${aqua('Proxy')} not running on socket`);
|
|
928
950
|
console.log(` \u2192 ${proxyFix}`);
|
|
929
951
|
issues++;
|
|
930
952
|
}
|
|
931
|
-
// 5.
|
|
953
|
+
// 5. Audit logger
|
|
954
|
+
if (config) {
|
|
955
|
+
const auditDir = config.audit.logDir;
|
|
956
|
+
const auditLog = path.join(auditDir, 'current.jsonl');
|
|
957
|
+
if (!config.audit.enabled) {
|
|
958
|
+
console.log(` - ${aqua('Audit')} disabled in config`);
|
|
959
|
+
}
|
|
960
|
+
else if (!fs.existsSync(auditDir)) {
|
|
961
|
+
console.log(` \u2717 ${aqua('Audit')} directory missing (${auditDir})`);
|
|
962
|
+
console.log(' \u2192 Run: aquaman init');
|
|
963
|
+
issues++;
|
|
964
|
+
}
|
|
965
|
+
else {
|
|
966
|
+
// Check log file exists and is writable
|
|
967
|
+
try {
|
|
968
|
+
if (fs.existsSync(auditLog)) {
|
|
969
|
+
fs.accessSync(auditLog, fs.constants.W_OK);
|
|
970
|
+
const content = fs.readFileSync(auditLog, 'utf-8').trim();
|
|
971
|
+
const entryCount = content ? content.split('\n').length : 0;
|
|
972
|
+
console.log(` \u2713 ${aqua('Audit')} log writable (${entryCount} entries)`);
|
|
973
|
+
}
|
|
974
|
+
else {
|
|
975
|
+
// Dir exists but no log yet — that's fine, first request will create it
|
|
976
|
+
fs.accessSync(auditDir, fs.constants.W_OK);
|
|
977
|
+
console.log(` \u2713 ${aqua('Audit')} directory writable (no entries yet)`);
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
catch {
|
|
981
|
+
console.log(` \u2717 ${aqua('Audit')} log not writable (${auditLog})`);
|
|
982
|
+
console.log(' \u2192 Check file permissions on ~/.aquaman/audit/');
|
|
983
|
+
issues++;
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
// 6. OpenClaw detection
|
|
932
988
|
let openclawDetected = false;
|
|
933
989
|
let cliFound = false;
|
|
934
990
|
try {
|
|
@@ -1832,8 +1888,7 @@ program
|
|
|
1832
1888
|
console.log('Configuration:');
|
|
1833
1889
|
console.log(` Config dir: ${getConfigDir()}`);
|
|
1834
1890
|
console.log(` Credential backend: ${config.credentials.backend}`);
|
|
1835
|
-
console.log(`
|
|
1836
|
-
console.log(` TLS: ${config.credentials.tls?.enabled ? 'enabled' : 'disabled'}`);
|
|
1891
|
+
console.log(` Socket path: ${path.join(getConfigDir(), 'proxy.sock')}`);
|
|
1837
1892
|
console.log(` Audit logging: ${config.audit.enabled ? 'enabled' : 'disabled'}`);
|
|
1838
1893
|
console.log('\nProxied services:');
|
|
1839
1894
|
for (const service of config.credentials.proxiedServices) {
|