aquaman-proxy 0.15.0 → 0.16.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 +3 -1
- package/dist/cli/index.js +170 -18
- package/dist/cli/index.js.map +1 -1
- package/dist/core/credentials/backends/keeper.d.ts +69 -0
- package/dist/core/credentials/backends/keeper.d.ts.map +1 -0
- package/dist/core/credentials/backends/keeper.js +189 -0
- package/dist/core/credentials/backends/keeper.js.map +1 -0
- package/dist/core/credentials/store.d.ts +2 -0
- package/dist/core/credentials/store.d.ts.map +1 -1
- package/dist/core/credentials/store.js +7 -0
- package/dist/core/credentials/store.js.map +1 -1
- package/dist/core/redactor/index.d.ts.map +1 -1
- package/dist/core/redactor/index.js +10 -0
- package/dist/core/redactor/index.js.map +1 -1
- package/dist/core/types.d.ts +5 -3
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/utils/config.d.ts +2 -1
- package/dist/core/utils/config.d.ts.map +1 -1
- package/dist/core/utils/config.js +10 -3
- package/dist/core/utils/config.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -78,7 +78,7 @@ The Telegram Bot API has no auth header, so there the token travels in the `/bot
|
|
|
78
78
|
|
|
79
79
|
Trade-off: any local process can reach a loopback port, including other users, where the socket's `0600` shuts them out. The token is the gate there, so the listener stays off until `aquaman hermes setup` or `aquaman openclaw setup` turns it on.
|
|
80
80
|
|
|
81
|
-
**Bring your own vault.** Aquaman has no house vault. It injects credentials from the secret store you already run: Keychain, 1Password, HashiCorp Vault, Bitwarden, KeePassXC, systemd-creds, or encrypted-file. No new store to adopt, no migration.
|
|
81
|
+
**Bring your own vault.** Aquaman has no house vault. It injects credentials from the secret store you already run: Keychain, 1Password, HashiCorp Vault, Bitwarden, Keeper, KeePassXC, systemd-creds, or encrypted-file. No new store to adopt, no migration.
|
|
82
82
|
|
|
83
83
|
## Broker endpoint (v0.12.0+)
|
|
84
84
|
|
|
@@ -97,6 +97,8 @@ Scoped since v0.15.0, because it hands out values rather than injecting them:
|
|
|
97
97
|
- Over the loopback listener the LLM provider keys are never materialized, declared or not (`404 broker_ref_isolated`). They stay on the proxy path.
|
|
98
98
|
- Declaring a ref is an explicit opt-in to handing that value to processes running as you. Refusals and resolves are both audited.
|
|
99
99
|
|
|
100
|
+
`aquaman get <aquaman://service/key>` (v0.16.0+) is the command-line form, for tools that take a command printing a secret: Docker Sandboxes (`sbx secret set <svc> --command`), Codex (`model_providers.<id>.auth.command`), Claude Code (`apiKeyHelper`), OpenClaw exec secret providers and Hermes command secret sources. It only talks to the running daemon, so the same scope applies and every read is audited. It refuses to print to an interactive terminal without `--show`, and prints no trailing newline when another program reads it. The value goes to the program that runs the command.
|
|
101
|
+
|
|
100
102
|
## Documentation
|
|
101
103
|
|
|
102
104
|
- **[Root README](https://github.com/tech4242/aquaman#readme)**: value prop, three-path Quick Start, security model
|
package/dist/cli/index.js
CHANGED
|
@@ -82,7 +82,9 @@ function storeOptionsFromConfig(config) {
|
|
|
82
82
|
keepassxcKeyFilePath: config.credentials.keepassxcKeyFilePath,
|
|
83
83
|
bitwardenFolder: config.credentials.bitwardenFolder,
|
|
84
84
|
bitwardenOrganizationId: config.credentials.bitwardenOrganizationId,
|
|
85
|
-
bitwardenCollectionId: config.credentials.bitwardenCollectionId
|
|
85
|
+
bitwardenCollectionId: config.credentials.bitwardenCollectionId,
|
|
86
|
+
keeperFolderUid: config.credentials.keeperFolderUid,
|
|
87
|
+
keeperConfigPath: config.credentials.keeperConfigPath
|
|
86
88
|
};
|
|
87
89
|
}
|
|
88
90
|
/**
|
|
@@ -151,6 +153,58 @@ function updateBrokerAllowedRefs(refs, mode) {
|
|
|
151
153
|
console.log('Note: broker.enabled is false in config.yaml, so the broker is off regardless.');
|
|
152
154
|
}
|
|
153
155
|
}
|
|
156
|
+
/**
|
|
157
|
+
* `POST /broker/resolve` against the running daemon over UDS. Always goes
|
|
158
|
+
* through the daemon (never the vault directly), so `aquaman get` is
|
|
159
|
+
* scope-checked and audited exactly like a coding-agent or Hermes resolve.
|
|
160
|
+
*/
|
|
161
|
+
function brokerResolve(socketPath, service, key, timeoutMs) {
|
|
162
|
+
const body = JSON.stringify({ service, key });
|
|
163
|
+
return new Promise((resolve) => {
|
|
164
|
+
const req = http.request({
|
|
165
|
+
socketPath,
|
|
166
|
+
path: '/broker/resolve',
|
|
167
|
+
method: 'POST',
|
|
168
|
+
headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(body) },
|
|
169
|
+
timeout: timeoutMs,
|
|
170
|
+
}, (res) => {
|
|
171
|
+
let payload = '';
|
|
172
|
+
res.on('data', (chunk) => { payload += chunk; });
|
|
173
|
+
res.on('end', () => {
|
|
174
|
+
let json;
|
|
175
|
+
try {
|
|
176
|
+
json = JSON.parse(payload);
|
|
177
|
+
}
|
|
178
|
+
catch {
|
|
179
|
+
resolve({ error: `Proxy returned a non-JSON response (HTTP ${res.statusCode})` });
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
if (res.statusCode === 200 && typeof json.value === 'string') {
|
|
183
|
+
resolve({ value: json.value });
|
|
184
|
+
}
|
|
185
|
+
else {
|
|
186
|
+
resolve({ error: json.error || `Broker error (HTTP ${res.statusCode})`, fix: json.fix, code: json.code });
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
});
|
|
190
|
+
req.on('error', (err) => {
|
|
191
|
+
if (err.code === 'ENOENT' || err.code === 'ECONNREFUSED') {
|
|
192
|
+
resolve({ error: `Cannot reach the aquaman proxy at ${socketPath}`, fix: 'Start it with: aquaman daemon', code: 'proxy_unreachable' });
|
|
193
|
+
}
|
|
194
|
+
else if (err.code === 'EPERM' || err.code === 'EACCES') {
|
|
195
|
+
resolve({ error: `Connecting to ${socketPath} was denied (${err.code})`, fix: 'If this runs inside a sandbox, allowlist the socket; otherwise check its owner and 0600 mode', code: 'socket_blocked' });
|
|
196
|
+
}
|
|
197
|
+
else {
|
|
198
|
+
resolve({ error: err.message });
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
req.on('timeout', () => {
|
|
202
|
+
req.destroy();
|
|
203
|
+
resolve({ error: `Broker request timed out after ${timeoutMs}ms`, fix: 'A vault that prompts (e.g. 1Password biometrics) may need longer: pass --timeout <ms>' });
|
|
204
|
+
});
|
|
205
|
+
req.end(body);
|
|
206
|
+
});
|
|
207
|
+
}
|
|
154
208
|
function describeBroker(scope) {
|
|
155
209
|
if (!scope)
|
|
156
210
|
return 'disabled (broker.enabled: false)';
|
|
@@ -312,7 +366,7 @@ program
|
|
|
312
366
|
}
|
|
313
367
|
lines.push('');
|
|
314
368
|
lines.push(aqua('Vault management'));
|
|
315
|
-
for (const name of ['credentials', 'audit', 'services', 'policy']) {
|
|
369
|
+
for (const name of ['credentials', 'get', 'broker', 'audit', 'services', 'policy']) {
|
|
316
370
|
const c = byName.get(name);
|
|
317
371
|
if (c)
|
|
318
372
|
lines.push(renderCmd(c));
|
|
@@ -336,13 +390,12 @@ program
|
|
|
336
390
|
}
|
|
337
391
|
// --- Coder namespace (shim \u2014 list documented subcommands) ---
|
|
338
392
|
if (byName.has('coder')) {
|
|
339
|
-
lines.push(aqua('AI coding-agent integration (Claude Code
|
|
393
|
+
lines.push(aqua('AI coding-agent integration (Claude Code, Codex)'));
|
|
340
394
|
const coderSubs = [
|
|
341
|
-
['coder setup <agent>', 'Install hooks for an agent (claude-code
|
|
395
|
+
['coder setup <agent>', 'Install hooks for an agent (claude-code, codex)'],
|
|
342
396
|
['coder doctor', 'Deep diagnostic \u2014 projects, broker, per-project vault'],
|
|
343
397
|
['coder status', 'Configured projects + hook wiring + broker activity'],
|
|
344
398
|
['coder project list/add/remove', 'Manage ~/.aquaman/projects.yaml'],
|
|
345
|
-
['coder get <ref>', 'Resolve an aquaman://service/key reference'],
|
|
346
399
|
['coder exec <cmd>', 'Run command with project env injected + output redacted'],
|
|
347
400
|
];
|
|
348
401
|
for (const [term, desc] of coderSubs) {
|
|
@@ -389,7 +442,7 @@ program
|
|
|
389
442
|
program
|
|
390
443
|
.command('setup')
|
|
391
444
|
.description('Vault-only setup wizard \u2014 backend + credentials (use `aquaman openclaw setup` or `aquaman coder setup` for full bundles)')
|
|
392
|
-
.option('--backend <backend>', 'Credential backend (keychain, encrypted-file, keepassxc, 1password, vault, systemd-creds, bitwarden)')
|
|
445
|
+
.option('--backend <backend>', 'Credential backend (keychain, encrypted-file, keepassxc, 1password, vault, systemd-creds, bitwarden, keeper)')
|
|
393
446
|
.option('--no-policy', 'Skip request policy preset configuration')
|
|
394
447
|
.option('--non-interactive', 'Use environment variables instead of prompts (for CI)')
|
|
395
448
|
.action(async (options) => {
|
|
@@ -614,7 +667,7 @@ async function runVaultSetup(opts) {
|
|
|
614
667
|
}
|
|
615
668
|
}
|
|
616
669
|
}
|
|
617
|
-
const validBackends = ['keychain', 'encrypted-file', 'keepassxc', '1password', 'vault', 'systemd-creds', 'bitwarden'];
|
|
670
|
+
const validBackends = ['keychain', 'encrypted-file', 'keepassxc', '1password', 'vault', 'systemd-creds', 'bitwarden', 'keeper'];
|
|
618
671
|
if (!validBackends.includes(backend)) {
|
|
619
672
|
console.error(` Invalid backend: ${backend}. Valid: ${validBackends.join(', ')}`);
|
|
620
673
|
process.exit(1);
|
|
@@ -630,6 +683,9 @@ async function runVaultSetup(opts) {
|
|
|
630
683
|
config = loadConfig();
|
|
631
684
|
}
|
|
632
685
|
config.credentials.backend = backend;
|
|
686
|
+
if (backend === 'keeper' && process.env['AQUAMAN_KEEPER_FOLDER_UID']) {
|
|
687
|
+
config.credentials.keeperFolderUid = process.env['AQUAMAN_KEEPER_FOLDER_UID'];
|
|
688
|
+
}
|
|
633
689
|
fs.writeFileSync(configPath, yamlStringify(config), { encoding: 'utf-8', mode: 0o600 });
|
|
634
690
|
const auditDir = path.join(configDir, 'audit');
|
|
635
691
|
fs.mkdirSync(auditDir, { recursive: true, mode: 0o700 });
|
|
@@ -785,7 +841,8 @@ openclaw
|
|
|
785
841
|
onRequest: (info) => {
|
|
786
842
|
auditLogger.logCredentialAccess('system', 'system', {
|
|
787
843
|
service: info.service,
|
|
788
|
-
|
|
844
|
+
// A broker resolve hands the value out; everything else injects it.
|
|
845
|
+
operation: info.method === 'BROKER' ? 'read' : 'use',
|
|
789
846
|
success: !info.error,
|
|
790
847
|
error: info.error
|
|
791
848
|
});
|
|
@@ -931,7 +988,8 @@ program
|
|
|
931
988
|
onRequest: (info) => {
|
|
932
989
|
auditLogger.logCredentialAccess('system', 'system', {
|
|
933
990
|
service: info.service,
|
|
934
|
-
|
|
991
|
+
// A broker resolve hands the value out; everything else injects it.
|
|
992
|
+
operation: info.method === 'BROKER' ? 'read' : 'use',
|
|
935
993
|
success: !info.error,
|
|
936
994
|
error: info.error
|
|
937
995
|
});
|
|
@@ -1004,7 +1062,8 @@ openclaw
|
|
|
1004
1062
|
onRequest: (info) => {
|
|
1005
1063
|
auditLogger.logCredentialAccess('system', 'system', {
|
|
1006
1064
|
service: info.service,
|
|
1007
|
-
|
|
1065
|
+
// A broker resolve hands the value out; everything else injects it.
|
|
1066
|
+
operation: info.method === 'BROKER' ? 'read' : 'use',
|
|
1008
1067
|
success: !info.error,
|
|
1009
1068
|
error: info.error
|
|
1010
1069
|
});
|
|
@@ -1382,7 +1441,7 @@ program
|
|
|
1382
1441
|
openclaw
|
|
1383
1442
|
.command('setup')
|
|
1384
1443
|
.description('Full setup for OpenClaw — vault + plugin + auth profiles + (optional) auto-migrate')
|
|
1385
|
-
.option('--backend <backend>', 'Credential backend (keychain, encrypted-file, keepassxc, 1password, vault, systemd-creds, bitwarden)')
|
|
1444
|
+
.option('--backend <backend>', 'Credential backend (keychain, encrypted-file, keepassxc, 1password, vault, systemd-creds, bitwarden, keeper)')
|
|
1386
1445
|
.option('--no-openclaw', 'Skip OpenClaw plugin installation step (run vault setup only)')
|
|
1387
1446
|
.option('--no-policy', 'Skip request policy preset configuration')
|
|
1388
1447
|
.option('--non-interactive', 'Use environment variables instead of prompts (for CI)')
|
|
@@ -1429,7 +1488,7 @@ openclaw
|
|
|
1429
1488
|
}
|
|
1430
1489
|
}
|
|
1431
1490
|
// Validate backend
|
|
1432
|
-
const validBackends = ['keychain', 'encrypted-file', 'keepassxc', '1password', 'vault', 'systemd-creds', 'bitwarden'];
|
|
1491
|
+
const validBackends = ['keychain', 'encrypted-file', 'keepassxc', '1password', 'vault', 'systemd-creds', 'bitwarden', 'keeper'];
|
|
1433
1492
|
if (!validBackends.includes(backend)) {
|
|
1434
1493
|
console.error(` Invalid backend: ${backend}`);
|
|
1435
1494
|
console.error(` Valid options: ${validBackends.join(', ')}`);
|
|
@@ -1547,10 +1606,30 @@ openclaw
|
|
|
1547
1606
|
process.exit(1);
|
|
1548
1607
|
}
|
|
1549
1608
|
}
|
|
1609
|
+
else if (backend === 'keeper') {
|
|
1610
|
+
const { KeeperStore } = await import('../core/credentials/backends/keeper.js');
|
|
1611
|
+
if (!KeeperStore.isAvailable()) {
|
|
1612
|
+
console.error(' Keeper Commander (keeper) not found.');
|
|
1613
|
+
console.error(' Install: pip install keepercommander');
|
|
1614
|
+
console.error(' Then, once: keeper shell -> login -> this-device register -> this-device persistent-login on');
|
|
1615
|
+
process.exit(1);
|
|
1616
|
+
}
|
|
1617
|
+
if (!process.env['AQUAMAN_KEEPER_FOLDER_UID']) {
|
|
1618
|
+
console.error(' Keeper backend needs the folder that will hold aquaman records.');
|
|
1619
|
+
console.error(' Create one in Keeper, then: export AQUAMAN_KEEPER_FOLDER_UID=<folder UID>');
|
|
1620
|
+
console.error(' (find the UID with: keeper get "<folder name>" --format json)');
|
|
1621
|
+
process.exit(1);
|
|
1622
|
+
}
|
|
1623
|
+
}
|
|
1550
1624
|
// 2. Run init internally (create dirs, config)
|
|
1551
1625
|
ensureConfigDir();
|
|
1552
1626
|
const config = getDefaultConfig();
|
|
1553
1627
|
config.credentials.backend = backend;
|
|
1628
|
+
if (backend === 'keeper') {
|
|
1629
|
+
config.credentials.keeperFolderUid = process.env['AQUAMAN_KEEPER_FOLDER_UID'];
|
|
1630
|
+
if (process.env['AQUAMAN_KEEPER_CONFIG'])
|
|
1631
|
+
config.credentials.keeperConfigPath = process.env['AQUAMAN_KEEPER_CONFIG'];
|
|
1632
|
+
}
|
|
1554
1633
|
fs.writeFileSync(configPath, yamlStringify(config), { encoding: 'utf-8', mode: 0o600 });
|
|
1555
1634
|
// Create audit directory
|
|
1556
1635
|
const auditDir = path.join(configDir, 'audit');
|
|
@@ -2007,6 +2086,12 @@ openclaw
|
|
|
2007
2086
|
throw new Error('systemd-creds backend requires systemd >= 256 with --user support');
|
|
2008
2087
|
}
|
|
2009
2088
|
}
|
|
2089
|
+
if (config.credentials.backend === 'keeper') {
|
|
2090
|
+
const { KeeperStore } = await import('../core/credentials/backends/keeper.js');
|
|
2091
|
+
if (!KeeperStore.isAvailable()) {
|
|
2092
|
+
throw new Error('Keeper Commander (keeper) not found. Install: pip install keepercommander');
|
|
2093
|
+
}
|
|
2094
|
+
}
|
|
2010
2095
|
if (config.credentials.backend === 'bitwarden') {
|
|
2011
2096
|
const { BitwardenStore } = await import('../core/credentials/backends/bitwarden.js');
|
|
2012
2097
|
if (!BitwardenStore.isAvailable()) {
|
|
@@ -2027,7 +2112,9 @@ openclaw
|
|
|
2027
2112
|
keepassxcKeyFilePath: config.credentials.keepassxcKeyFilePath,
|
|
2028
2113
|
bitwardenFolder: config.credentials.bitwardenFolder,
|
|
2029
2114
|
bitwardenOrganizationId: config.credentials.bitwardenOrganizationId,
|
|
2030
|
-
bitwardenCollectionId: config.credentials.bitwardenCollectionId
|
|
2115
|
+
bitwardenCollectionId: config.credentials.bitwardenCollectionId,
|
|
2116
|
+
keeperFolderUid: config.credentials.keeperFolderUid,
|
|
2117
|
+
keeperConfigPath: config.credentials.keeperConfigPath
|
|
2031
2118
|
});
|
|
2032
2119
|
// 3. Count credentials
|
|
2033
2120
|
const creds = await store.list();
|
|
@@ -2582,7 +2669,9 @@ credentials
|
|
|
2582
2669
|
keepassxcKeyFilePath: config.credentials.keepassxcKeyFilePath,
|
|
2583
2670
|
bitwardenFolder: config.credentials.bitwardenFolder,
|
|
2584
2671
|
bitwardenOrganizationId: config.credentials.bitwardenOrganizationId,
|
|
2585
|
-
bitwardenCollectionId: config.credentials.bitwardenCollectionId
|
|
2672
|
+
bitwardenCollectionId: config.credentials.bitwardenCollectionId,
|
|
2673
|
+
keeperFolderUid: config.credentials.keeperFolderUid,
|
|
2674
|
+
keeperConfigPath: config.credentials.keeperConfigPath
|
|
2586
2675
|
});
|
|
2587
2676
|
}
|
|
2588
2677
|
catch (error) {
|
|
@@ -2665,7 +2754,9 @@ credentials
|
|
|
2665
2754
|
keepassxcKeyFilePath: config.credentials.keepassxcKeyFilePath,
|
|
2666
2755
|
bitwardenFolder: config.credentials.bitwardenFolder,
|
|
2667
2756
|
bitwardenOrganizationId: config.credentials.bitwardenOrganizationId,
|
|
2668
|
-
bitwardenCollectionId: config.credentials.bitwardenCollectionId
|
|
2757
|
+
bitwardenCollectionId: config.credentials.bitwardenCollectionId,
|
|
2758
|
+
keeperFolderUid: config.credentials.keeperFolderUid,
|
|
2759
|
+
keeperConfigPath: config.credentials.keeperConfigPath
|
|
2669
2760
|
});
|
|
2670
2761
|
}
|
|
2671
2762
|
catch {
|
|
@@ -2702,7 +2793,9 @@ credentials
|
|
|
2702
2793
|
keepassxcKeyFilePath: config.credentials.keepassxcKeyFilePath,
|
|
2703
2794
|
bitwardenFolder: config.credentials.bitwardenFolder,
|
|
2704
2795
|
bitwardenOrganizationId: config.credentials.bitwardenOrganizationId,
|
|
2705
|
-
bitwardenCollectionId: config.credentials.bitwardenCollectionId
|
|
2796
|
+
bitwardenCollectionId: config.credentials.bitwardenCollectionId,
|
|
2797
|
+
keeperFolderUid: config.credentials.keeperFolderUid,
|
|
2798
|
+
keeperConfigPath: config.credentials.keeperConfigPath
|
|
2706
2799
|
});
|
|
2707
2800
|
}
|
|
2708
2801
|
catch {
|
|
@@ -2720,7 +2813,7 @@ credentials
|
|
|
2720
2813
|
credentials
|
|
2721
2814
|
.command('guide')
|
|
2722
2815
|
.description('Show setup commands for seeding credentials based on your backend')
|
|
2723
|
-
.option('--backend <backend>', 'Override backend (keychain, encrypted-file, keepassxc, 1password, vault, systemd-creds, bitwarden)')
|
|
2816
|
+
.option('--backend <backend>', 'Override backend (keychain, encrypted-file, keepassxc, 1password, vault, systemd-creds, bitwarden, keeper)')
|
|
2724
2817
|
.option('--service <name>', 'Show commands for a single service only')
|
|
2725
2818
|
.action(async (options) => {
|
|
2726
2819
|
const config = loadConfig();
|
|
@@ -2923,6 +3016,63 @@ broker
|
|
|
2923
3016
|
.action((refs) => {
|
|
2924
3017
|
updateBrokerAllowedRefs(refs, 'revoke');
|
|
2925
3018
|
});
|
|
3019
|
+
// v0.16.0: fills the "run a command, read the secret from stdout" slot every
|
|
3020
|
+
// host now has (sbx secret set --command, Codex model_providers.*.auth.command,
|
|
3021
|
+
// Claude Code apiKeyHelper, OpenClaw exec providers, Hermes command sources).
|
|
3022
|
+
// This materializes the value into the CALLING process, so it is not process
|
|
3023
|
+
// isolation; it is scope-checked and audited because it goes through the daemon.
|
|
3024
|
+
program
|
|
3025
|
+
.command('get')
|
|
3026
|
+
.description('Print a declared credential to stdout, via the running daemon (for host credential-helper commands)')
|
|
3027
|
+
.argument('<ref>', 'aquaman://service/key, declared with `aquaman broker allow` or in projects.yaml')
|
|
3028
|
+
.option('--show', 'Allow printing to an interactive terminal')
|
|
3029
|
+
.option('--timeout <ms>', 'How long to wait for the daemon (vaults that prompt may need longer)', '15000')
|
|
3030
|
+
.addHelpText('after', `
|
|
3031
|
+
Examples:
|
|
3032
|
+
sbx secret set github --command "$(command -v node) $(command -v aquaman) get aquaman://github/token"
|
|
3033
|
+
Codex config.toml: [model_providers.openai.auth] command = "aquaman" args = ["get", "aquaman://openai/api_key"]
|
|
3034
|
+
Claude Code: "apiKeyHelper": "aquaman get aquaman://anthropic/api_key"
|
|
3035
|
+
|
|
3036
|
+
The value lands in whichever process runs this command. Every read is checked
|
|
3037
|
+
against the broker scope and written to the audit log.`)
|
|
3038
|
+
.action(async (ref, opts) => {
|
|
3039
|
+
const parsed = parseAquamanRef(ref);
|
|
3040
|
+
if (!parsed) {
|
|
3041
|
+
console.error(`Invalid ref: ${ref}`);
|
|
3042
|
+
console.error('Refs look like aquaman://<service>/<key>, e.g. aquaman://github/token');
|
|
3043
|
+
process.exit(1);
|
|
3044
|
+
}
|
|
3045
|
+
const timeoutMs = Number(opts.timeout);
|
|
3046
|
+
if (!Number.isInteger(timeoutMs) || timeoutMs < 1) {
|
|
3047
|
+
console.error(`Invalid --timeout: ${opts.timeout} (milliseconds, a positive integer)`);
|
|
3048
|
+
process.exit(1);
|
|
3049
|
+
}
|
|
3050
|
+
// Refuse before resolving, so an accidental run at a prompt neither
|
|
3051
|
+
// prints the secret into scrollback nor spends an audited read.
|
|
3052
|
+
if (process.stdout.isTTY && !opts.show) {
|
|
3053
|
+
console.error('Refusing to print a credential to a terminal.');
|
|
3054
|
+
console.error('This command is meant to be run by another program. To print it anyway, pass --show.');
|
|
3055
|
+
process.exit(1);
|
|
3056
|
+
}
|
|
3057
|
+
const result = await brokerResolve(path.join(getConfigDir(), 'proxy.sock'), parsed.service, parsed.key, timeoutMs);
|
|
3058
|
+
if ('value' in result) {
|
|
3059
|
+
// No trailing newline when piped: credential-helper consumers differ on
|
|
3060
|
+
// whether they trim, and a stray "\n" in an auth header breaks requests.
|
|
3061
|
+
process.stdout.write(process.stdout.isTTY ? `${result.value}\n` : result.value);
|
|
3062
|
+
return;
|
|
3063
|
+
}
|
|
3064
|
+
console.error(`Error: ${result.error}`);
|
|
3065
|
+
if (result.code === 'broker_ref_not_declared') {
|
|
3066
|
+
console.error(`Fix: aquaman broker allow ${ref}`);
|
|
3067
|
+
}
|
|
3068
|
+
else if (result.code === 'broker_disabled') {
|
|
3069
|
+
console.error('Fix: the proxy on this socket does not serve the broker. If the OpenClaw plugin started it, run `aquaman daemon` instead (the last proxy started owns the socket).');
|
|
3070
|
+
}
|
|
3071
|
+
else if (result.fix) {
|
|
3072
|
+
console.error(`Fix: ${result.fix}`);
|
|
3073
|
+
}
|
|
3074
|
+
process.exit(1);
|
|
3075
|
+
});
|
|
2926
3076
|
// Migration commands
|
|
2927
3077
|
openclaw
|
|
2928
3078
|
.command('migrate')
|
|
@@ -3356,7 +3506,9 @@ openclaw
|
|
|
3356
3506
|
keepassxcKeyFilePath: config.credentials.keepassxcKeyFilePath,
|
|
3357
3507
|
bitwardenFolder: config.credentials.bitwardenFolder,
|
|
3358
3508
|
bitwardenOrganizationId: config.credentials.bitwardenOrganizationId,
|
|
3359
|
-
bitwardenCollectionId: config.credentials.bitwardenCollectionId
|
|
3509
|
+
bitwardenCollectionId: config.credentials.bitwardenCollectionId,
|
|
3510
|
+
keeperFolderUid: config.credentials.keeperFolderUid,
|
|
3511
|
+
keeperConfigPath: config.credentials.keeperConfigPath
|
|
3360
3512
|
});
|
|
3361
3513
|
const creds = await store.list();
|
|
3362
3514
|
console.log(`\nStored credentials: ${creds.length}`);
|