aquaman-proxy 0.9.2 → 0.10.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 +29 -28
- package/dist/cli/index.js +153 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/core/credentials/backends/vault.d.ts +1 -0
- package/dist/core/credentials/backends/vault.d.ts.map +1 -1
- package/dist/core/credentials/backends/vault.js +8 -0
- package/dist/core/credentials/backends/vault.js.map +1 -1
- package/dist/core/types.d.ts +8 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/utils/config.d.ts.map +1 -1
- package/dist/core/utils/config.js +2 -1
- package/dist/core/utils/config.js.map +1 -1
- package/dist/daemon.d.ts +2 -0
- package/dist/daemon.d.ts.map +1 -1
- package/dist/daemon.js +18 -1
- package/dist/daemon.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/oauth-token-cache.d.ts.map +1 -1
- package/dist/oauth-token-cache.js +15 -0
- package/dist/oauth-token-cache.js.map +1 -1
- package/dist/openclaw/env-writer.d.ts.map +1 -1
- package/dist/openclaw/env-writer.js +5 -0
- package/dist/openclaw/env-writer.js.map +1 -1
- package/dist/request-policy.d.ts +56 -0
- package/dist/request-policy.d.ts.map +1 -0
- package/dist/request-policy.js +158 -0
- package/dist/request-policy.js.map +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,19 +1,18 @@
|
|
|
1
1
|
# aquaman-proxy
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
## How It Works
|
|
3
|
+
The proxy daemon and CLI for [aquaman](https://github.com/tech4242/aquaman) — credential isolation for OpenClaw.
|
|
6
4
|
|
|
7
5
|
```
|
|
8
6
|
Agent / OpenClaw Gateway Aquaman Proxy
|
|
9
7
|
┌──────────────────────┐ ┌──────────────────────┐
|
|
10
8
|
│ │ │ │
|
|
11
|
-
│ ANTHROPIC_BASE_URL │══ Unix
|
|
12
|
-
│ = aquaman.local │ Domain
|
|
13
|
-
│ │<═ Socket
|
|
14
|
-
│ fetch() interceptor │══ (UDS)
|
|
15
|
-
│ redirects channel │ │
|
|
16
|
-
│ API traffic │ │
|
|
9
|
+
│ ANTHROPIC_BASE_URL │══ Unix ═════>│ Keychain / 1Pass / │
|
|
10
|
+
│ = aquaman.local │ Domain │ Vault / Encrypted │
|
|
11
|
+
│ │<═ Socket ════│ │
|
|
12
|
+
│ fetch() interceptor │══ (UDS) ════>│ + Policy enforced │
|
|
13
|
+
│ redirects channel │ │ + Auth injected: │
|
|
14
|
+
│ API traffic │ │ header / url-path │
|
|
15
|
+
│ │ │ basic / oauth │
|
|
17
16
|
│ │ │ │
|
|
18
17
|
│ No credentials. │ ~/.aquaman/ │ │
|
|
19
18
|
│ No open ports. │ proxy.sock │ │
|
|
@@ -30,33 +29,22 @@ Agent / OpenClaw Gateway Aquaman Proxy
|
|
|
30
29
|
slack.com/api ...
|
|
31
30
|
```
|
|
32
31
|
|
|
33
|
-
This package is the right side
|
|
32
|
+
This package is the right side — a reverse proxy on a Unix domain socket that stores credentials in secure backends, enforces request policies, injects auth headers, and logs every access. The agent never sees a key.
|
|
34
33
|
|
|
35
34
|
## Quick Start
|
|
36
35
|
|
|
37
|
-
With OpenClaw:
|
|
38
|
-
|
|
39
36
|
```bash
|
|
40
|
-
npm install -g aquaman-proxy
|
|
41
|
-
aquaman setup
|
|
42
|
-
openclaw
|
|
37
|
+
npm install -g aquaman-proxy
|
|
38
|
+
aquaman setup # stores keys, installs OpenClaw plugin, applies policy defaults
|
|
39
|
+
openclaw # proxy starts automatically via plugin
|
|
43
40
|
```
|
|
44
41
|
|
|
45
|
-
|
|
46
|
-
> Linux defaults to encrypted file. Override with `--backend`:
|
|
47
|
-
> `aquaman setup --backend keepassxc`
|
|
48
|
-
> Options: `keychain`, `encrypted-file`, `keepassxc`, `1password`, `vault`, `systemd-creds`, `bitwarden`
|
|
49
|
-
|
|
50
|
-
Existing plaintext credentials are migrated automatically during setup.
|
|
51
|
-
Run again anytime to migrate new credentials: `aquaman migrate openclaw --auto`
|
|
52
|
-
|
|
53
|
-
Standalone:
|
|
42
|
+
Standalone (without OpenClaw):
|
|
54
43
|
|
|
55
44
|
```bash
|
|
56
|
-
npm install -g aquaman-proxy
|
|
57
45
|
aquaman init
|
|
58
46
|
aquaman credentials add anthropic api_key
|
|
59
|
-
aquaman daemon
|
|
47
|
+
aquaman daemon # listens on ~/.aquaman/proxy.sock
|
|
60
48
|
```
|
|
61
49
|
|
|
62
50
|
Troubleshooting: `aquaman doctor`
|
|
@@ -65,7 +53,7 @@ Troubleshooting: `aquaman doctor`
|
|
|
65
53
|
|
|
66
54
|
| Command | Description |
|
|
67
55
|
|---------|-------------|
|
|
68
|
-
| `aquaman setup` | Guided onboarding (stores keys, installs plugin) |
|
|
56
|
+
| `aquaman setup` | Guided onboarding (stores keys, installs plugin, applies policy defaults) |
|
|
69
57
|
| `aquaman doctor` | Diagnose issues with actionable fixes |
|
|
70
58
|
| `aquaman credentials add <svc> <key>` | Store a credential |
|
|
71
59
|
| `aquaman credentials list` | List stored credentials |
|
|
@@ -74,6 +62,8 @@ Troubleshooting: `aquaman doctor`
|
|
|
74
62
|
| `aquaman start` | Start proxy + launch OpenClaw |
|
|
75
63
|
| `aquaman stop` | Stop running proxy |
|
|
76
64
|
| `aquaman status` | Show config and proxy status |
|
|
65
|
+
| `aquaman policy list` | List configured policy rules |
|
|
66
|
+
| `aquaman policy test <svc> <method> <path>` | Dry-run a request against policy rules |
|
|
77
67
|
| `aquaman audit tail` | Recent audit entries |
|
|
78
68
|
| `aquaman audit verify` | Verify hash chain integrity |
|
|
79
69
|
|
|
@@ -88,9 +78,20 @@ Troubleshooting: `aquaman doctor`
|
|
|
88
78
|
| **Channels (OAuth)** | MS Teams, Feishu, Google Chat |
|
|
89
79
|
| **At-rest only** | Nostr, Tlon |
|
|
90
80
|
|
|
81
|
+
## Security
|
|
82
|
+
|
|
83
|
+
The proxy enforces four layers of protection:
|
|
84
|
+
|
|
85
|
+
- **Process isolation** — credentials in a separate address space, connected via UDS (`chmod 600`)
|
|
86
|
+
- **Service allowlisting** — `proxiedServices` controls which APIs the agent can reach
|
|
87
|
+
- **Request policies** — method + path rules per service, checked *before* credential injection ([details](https://github.com/tech4242/aquaman#request-policies))
|
|
88
|
+
- **Audit trail** — SHA-256 hash-chained logs of every credential use
|
|
89
|
+
|
|
90
|
+
7 credential backends: Keychain, 1Password, Vault, Bitwarden, KeePassXC, systemd-creds, encrypted-file.
|
|
91
|
+
|
|
91
92
|
## Documentation
|
|
92
93
|
|
|
93
|
-
See the [main README](https://github.com/tech4242/aquaman#readme) for
|
|
94
|
+
See the [main README](https://github.com/tech4242/aquaman#readme) for the full security model, request policy config, Docker deployment, and architecture diagrams.
|
|
94
95
|
|
|
95
96
|
## License
|
|
96
97
|
|
package/dist/cli/index.js
CHANGED
|
@@ -17,6 +17,7 @@ import { fileURLToPath } from 'node:url';
|
|
|
17
17
|
import { createCredentialProxy } from '../daemon.js';
|
|
18
18
|
import { createServiceRegistry, ServiceRegistry } from '../service-registry.js';
|
|
19
19
|
import { createOpenClawIntegration } from '../openclaw/integration.js';
|
|
20
|
+
import { loadPolicyFromConfig, validatePolicyConfig, getDefaultPolicyPresets, matchPolicy } from '../request-policy.js';
|
|
20
21
|
import { stringify as yamlStringify, parse as yamlParse } from 'yaml';
|
|
21
22
|
// Read version from package.json (single source of truth)
|
|
22
23
|
const __cliFilename = fileURLToPath(import.meta.url);
|
|
@@ -28,6 +29,14 @@ const VERSION = pkgJson.version;
|
|
|
28
29
|
const noColor = process.env['NO_COLOR'] !== undefined ||
|
|
29
30
|
(!process.stdout.isTTY && process.env['FORCE_COLOR'] === undefined);
|
|
30
31
|
const aqua = (s) => noColor ? s : `\x1b[38;2;127;255;212m${s}\x1b[0m`;
|
|
32
|
+
// Credential name validation — shared pattern from daemon.ts and systemd-creds backend
|
|
33
|
+
const SAFE_CRED_NAME = /^[a-z0-9][a-z0-9._-]*$/;
|
|
34
|
+
function validateCredName(label, value) {
|
|
35
|
+
if (!SAFE_CRED_NAME.test(value)) {
|
|
36
|
+
console.error(`Invalid ${label}: "${value}". Allowed: lowercase alphanumeric, dots, hyphens, underscores.`);
|
|
37
|
+
process.exit(1);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
31
40
|
// PID file management
|
|
32
41
|
const getPidFile = () => path.join(getConfigDir(), 'daemon.pid');
|
|
33
42
|
const writePidFile = () => {
|
|
@@ -172,11 +181,13 @@ program
|
|
|
172
181
|
const serviceRegistry = createServiceRegistry({ configPath: config.services.configPath });
|
|
173
182
|
// Start credential proxy
|
|
174
183
|
const socketPath = path.join(getConfigDir(), 'proxy.sock');
|
|
184
|
+
const policyConfig = loadPolicyFromConfig(config);
|
|
175
185
|
const credentialProxy = createCredentialProxy({
|
|
176
186
|
socketPath,
|
|
177
187
|
store: credentialStore,
|
|
178
188
|
allowedServices: config.credentials.proxiedServices,
|
|
179
189
|
serviceRegistry,
|
|
190
|
+
policyConfig,
|
|
180
191
|
onRequest: (info) => {
|
|
181
192
|
auditLogger.logCredentialAccess('system', 'system', {
|
|
182
193
|
service: info.service,
|
|
@@ -324,11 +335,13 @@ program
|
|
|
324
335
|
// Initialize service registry
|
|
325
336
|
const serviceRegistry = createServiceRegistry({ configPath: config.services.configPath });
|
|
326
337
|
// Start credential proxy
|
|
338
|
+
const policyConfig2 = loadPolicyFromConfig(config);
|
|
327
339
|
const credentialProxy = createCredentialProxy({
|
|
328
340
|
socketPath,
|
|
329
341
|
store: credentialStore,
|
|
330
342
|
allowedServices: config.credentials.proxiedServices,
|
|
331
343
|
serviceRegistry,
|
|
344
|
+
policyConfig: policyConfig2,
|
|
332
345
|
onRequest: (info) => {
|
|
333
346
|
auditLogger.logCredentialAccess('system', 'system', {
|
|
334
347
|
service: info.service,
|
|
@@ -401,11 +414,13 @@ program
|
|
|
401
414
|
// Initialize service registry
|
|
402
415
|
const serviceRegistry = createServiceRegistry({ configPath: config.services.configPath });
|
|
403
416
|
// Start credential proxy
|
|
417
|
+
const policyConfig3 = loadPolicyFromConfig(config);
|
|
404
418
|
const credentialProxy = createCredentialProxy({
|
|
405
419
|
socketPath,
|
|
406
420
|
store: credentialStore,
|
|
407
421
|
allowedServices: config.credentials.proxiedServices,
|
|
408
422
|
serviceRegistry,
|
|
423
|
+
policyConfig: policyConfig3,
|
|
409
424
|
onRequest: (info) => {
|
|
410
425
|
auditLogger.logCredentialAccess('system', 'system', {
|
|
411
426
|
service: info.service,
|
|
@@ -498,6 +513,7 @@ program
|
|
|
498
513
|
.description('All-in-one setup wizard — creates config, stores credentials, installs plugin')
|
|
499
514
|
.option('--backend <backend>', 'Credential backend (keychain, encrypted-file, keepassxc, 1password, vault, systemd-creds, bitwarden)')
|
|
500
515
|
.option('--no-openclaw', 'Skip OpenClaw plugin installation')
|
|
516
|
+
.option('--no-policy', 'Skip request policy preset configuration')
|
|
501
517
|
.option('--non-interactive', 'Use environment variables instead of prompts (for CI)')
|
|
502
518
|
.action(async (options) => {
|
|
503
519
|
const os = await import('node:os');
|
|
@@ -769,6 +785,43 @@ program
|
|
|
769
785
|
console.log(' Skipped\n');
|
|
770
786
|
}
|
|
771
787
|
}
|
|
788
|
+
// 3.5. Apply request policy presets
|
|
789
|
+
if (options.policy !== false && storedServices.length > 0) {
|
|
790
|
+
let shouldApplyPolicy = true;
|
|
791
|
+
if (!isNonInteractive) {
|
|
792
|
+
const rl = (await import('node:readline')).createInterface({
|
|
793
|
+
input: process.stdin,
|
|
794
|
+
output: process.stdout
|
|
795
|
+
});
|
|
796
|
+
const answer = await new Promise((resolve) => {
|
|
797
|
+
rl.question(' ? Enable API request policies? (Y/n): ', resolve);
|
|
798
|
+
});
|
|
799
|
+
rl.close();
|
|
800
|
+
shouldApplyPolicy = answer.toLowerCase() !== 'n';
|
|
801
|
+
if (shouldApplyPolicy) {
|
|
802
|
+
console.log(' Policies restrict which API endpoints agents can call.\n');
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
if (shouldApplyPolicy) {
|
|
806
|
+
const presets = getDefaultPolicyPresets();
|
|
807
|
+
const policyToApply = {};
|
|
808
|
+
for (const svc of storedServices) {
|
|
809
|
+
if (presets[svc]) {
|
|
810
|
+
policyToApply[svc] = presets[svc];
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
if (Object.keys(policyToApply).length > 0) {
|
|
814
|
+
config.policy = policyToApply;
|
|
815
|
+
saveConfig(config);
|
|
816
|
+
console.log(' Applying default policy presets:\n');
|
|
817
|
+
for (const [svc, sp] of Object.entries(policyToApply)) {
|
|
818
|
+
console.log(formatServicePolicy(svc, sp, ' '));
|
|
819
|
+
console.log('');
|
|
820
|
+
}
|
|
821
|
+
console.log(' Customize policies later: ~/.aquaman/config.yaml\n');
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
}
|
|
772
825
|
// 4. Detect OpenClaw and install plugin
|
|
773
826
|
if (options.openclaw !== false) {
|
|
774
827
|
const openclawDetected = fs.existsSync(openclawStateDir);
|
|
@@ -1060,6 +1113,42 @@ program
|
|
|
1060
1113
|
}
|
|
1061
1114
|
}
|
|
1062
1115
|
}
|
|
1116
|
+
// 5.5 Policy config
|
|
1117
|
+
if (config?.policy && Object.keys(config.policy).length > 0) {
|
|
1118
|
+
const policyConfigDoc = loadPolicyFromConfig(config);
|
|
1119
|
+
const validation = validatePolicyConfig(policyConfigDoc);
|
|
1120
|
+
if (validation.valid) {
|
|
1121
|
+
const serviceCount = Object.keys(policyConfigDoc).length;
|
|
1122
|
+
const ruleCount = Object.values(policyConfigDoc).reduce((sum, sp) => sum + sp.rules.length, 0);
|
|
1123
|
+
console.log(` \u2713 ${aqua('Policy')} valid (${serviceCount} service${serviceCount !== 1 ? 's' : ''}, ${ruleCount} rule${ruleCount !== 1 ? 's' : ''})`);
|
|
1124
|
+
for (const [svc, sp] of Object.entries(policyConfigDoc)) {
|
|
1125
|
+
const denyRules = sp.rules.filter(r => r.action === 'deny');
|
|
1126
|
+
if (denyRules.length > 0) {
|
|
1127
|
+
const summary = denyRules.map(r => r.method !== '*' ? `${r.action} ${r.method} ${r.path}` : `${r.action} ${r.path}`).join(', ');
|
|
1128
|
+
console.log(` ${svc}: ${summary}`);
|
|
1129
|
+
}
|
|
1130
|
+
}
|
|
1131
|
+
// Warn about policies for non-proxied services
|
|
1132
|
+
if (config.credentials.proxiedServices) {
|
|
1133
|
+
for (const svc of Object.keys(policyConfigDoc)) {
|
|
1134
|
+
if (!config.credentials.proxiedServices.includes(svc)) {
|
|
1135
|
+
console.log(` \u26a0 ${aqua('Policy')} service "${svc}" has rules but is not in proxiedServices`);
|
|
1136
|
+
issues++;
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
1140
|
+
}
|
|
1141
|
+
else {
|
|
1142
|
+
for (const err of validation.errors) {
|
|
1143
|
+
console.log(` \u2717 ${aqua('Policy')} ${err}`);
|
|
1144
|
+
}
|
|
1145
|
+
issues++;
|
|
1146
|
+
}
|
|
1147
|
+
}
|
|
1148
|
+
else {
|
|
1149
|
+
console.log(` \u2139 ${aqua('Policy')} not configured \u2014 agents can call any endpoint on proxied services`);
|
|
1150
|
+
console.log(' \u2192 Run: aquaman setup (or add policy rules to ~/.aquaman/config.yaml)');
|
|
1151
|
+
}
|
|
1063
1152
|
// 6. OpenClaw detection
|
|
1064
1153
|
let openclawDetected = false;
|
|
1065
1154
|
let cliFound = false;
|
|
@@ -1323,6 +1412,8 @@ credentials
|
|
|
1323
1412
|
.description('Add a credential')
|
|
1324
1413
|
.option('--backend <backend>', 'Override credential backend')
|
|
1325
1414
|
.action(async (service, key, options) => {
|
|
1415
|
+
validateCredName('service', service);
|
|
1416
|
+
validateCredName('key', key);
|
|
1326
1417
|
const config = loadConfig();
|
|
1327
1418
|
const backend = options.backend || config.credentials.backend;
|
|
1328
1419
|
let store;
|
|
@@ -1418,6 +1509,8 @@ credentials
|
|
|
1418
1509
|
.command('delete <service> <key>')
|
|
1419
1510
|
.description('Delete a credential')
|
|
1420
1511
|
.action(async (service, key) => {
|
|
1512
|
+
validateCredName('service', service);
|
|
1513
|
+
validateCredName('key', key);
|
|
1421
1514
|
const config = loadConfig();
|
|
1422
1515
|
let store;
|
|
1423
1516
|
try {
|
|
@@ -1572,6 +1665,42 @@ services
|
|
|
1572
1665
|
process.exit(1);
|
|
1573
1666
|
}
|
|
1574
1667
|
});
|
|
1668
|
+
// Policy commands
|
|
1669
|
+
const policy = program.command('policy').description('Request policy management');
|
|
1670
|
+
policy
|
|
1671
|
+
.command('list')
|
|
1672
|
+
.description('List configured policy rules')
|
|
1673
|
+
.action(async () => {
|
|
1674
|
+
const config = loadConfig();
|
|
1675
|
+
const policyConfig = loadPolicyFromConfig(config);
|
|
1676
|
+
if (Object.keys(policyConfig).length === 0) {
|
|
1677
|
+
console.log('No policies configured. Run: aquaman setup');
|
|
1678
|
+
return;
|
|
1679
|
+
}
|
|
1680
|
+
for (const [name, sp] of Object.entries(policyConfig)) {
|
|
1681
|
+
console.log(formatServicePolicy(name, sp));
|
|
1682
|
+
}
|
|
1683
|
+
});
|
|
1684
|
+
policy
|
|
1685
|
+
.command('test <service> <method> <path>')
|
|
1686
|
+
.description('Test whether a request would be allowed or denied')
|
|
1687
|
+
.action(async (service, method, reqPath) => {
|
|
1688
|
+
const config = loadConfig();
|
|
1689
|
+
const policyConfig = loadPolicyFromConfig(config);
|
|
1690
|
+
const result = matchPolicy(service, method.toUpperCase(), reqPath, policyConfig);
|
|
1691
|
+
if (!policyConfig[service]) {
|
|
1692
|
+
console.log(` \u2713 ALLOWED (no policy for service "${service}")`);
|
|
1693
|
+
return;
|
|
1694
|
+
}
|
|
1695
|
+
if (result.allowed) {
|
|
1696
|
+
const svcPolicy = policyConfig[service];
|
|
1697
|
+
console.log(` \u2713 ALLOWED (no matching rule, default: ${svcPolicy.defaultAction})`);
|
|
1698
|
+
}
|
|
1699
|
+
else {
|
|
1700
|
+
const rule = result.matchedRule;
|
|
1701
|
+
console.log(` \u2717 DENIED by rule: ${rule.method} ${rule.path} \u2192 ${rule.action}`);
|
|
1702
|
+
}
|
|
1703
|
+
});
|
|
1575
1704
|
// Migration commands
|
|
1576
1705
|
const migrate = program.command('migrate').description('Migrate credentials from other sources');
|
|
1577
1706
|
migrate
|
|
@@ -1979,6 +2108,19 @@ program
|
|
|
1979
2108
|
for (const service of config.credentials.proxiedServices) {
|
|
1980
2109
|
console.log(` - ${service}`);
|
|
1981
2110
|
}
|
|
2111
|
+
// Policy summary
|
|
2112
|
+
const policyConfig = loadPolicyFromConfig(config);
|
|
2113
|
+
const policySvcCount = Object.keys(policyConfig).length;
|
|
2114
|
+
if (policySvcCount > 0) {
|
|
2115
|
+
const ruleCount = Object.values(policyConfig).reduce((sum, sp) => sum + sp.rules.length, 0);
|
|
2116
|
+
console.log(`\nRequest policies: ${policySvcCount} service${policySvcCount !== 1 ? 's' : ''}, ${ruleCount} rule${ruleCount !== 1 ? 's' : ''}`);
|
|
2117
|
+
for (const [svc, sp] of Object.entries(policyConfig)) {
|
|
2118
|
+
console.log(` - ${svc}: ${sp.rules.length} rule${sp.rules.length !== 1 ? 's' : ''} (default: ${sp.defaultAction})`);
|
|
2119
|
+
}
|
|
2120
|
+
}
|
|
2121
|
+
else {
|
|
2122
|
+
console.log('\nRequest policies: not configured');
|
|
2123
|
+
}
|
|
1982
2124
|
// Check for stored credentials
|
|
1983
2125
|
try {
|
|
1984
2126
|
const store = await createCredentialStore({
|
|
@@ -2007,6 +2149,16 @@ program
|
|
|
2007
2149
|
const info = await integration.detectOpenClaw();
|
|
2008
2150
|
console.log(`\nOpenClaw: ${info.installed ? `installed (${info.version})` : 'not found'}`);
|
|
2009
2151
|
});
|
|
2152
|
+
/** Format a single service policy for display (used by policy list, setup, doctor) */
|
|
2153
|
+
function formatServicePolicy(name, sp, indent = ' ') {
|
|
2154
|
+
const lines = [];
|
|
2155
|
+
lines.push(`${indent}${name} (default: ${sp.defaultAction})`);
|
|
2156
|
+
for (const rule of sp.rules) {
|
|
2157
|
+
const method = rule.method.padEnd(6);
|
|
2158
|
+
lines.push(`${indent} ${rule.action === 'deny' ? 'deny' : 'allow'} ${method} ${rule.path}`);
|
|
2159
|
+
}
|
|
2160
|
+
return lines.join('\n');
|
|
2161
|
+
}
|
|
2010
2162
|
function formatEntry(entry) {
|
|
2011
2163
|
switch (entry.type) {
|
|
2012
2164
|
case 'tool_call':
|
|
@@ -2014,7 +2166,7 @@ function formatEntry(entry) {
|
|
|
2014
2166
|
case 'tool_result':
|
|
2015
2167
|
return `Result for ${entry.data.toolCallId}`;
|
|
2016
2168
|
case 'credential_access':
|
|
2017
|
-
return `${entry.data.service} ${entry.data.operation} ${entry.data.success ? 'OK' :
|
|
2169
|
+
return `${entry.data.service} ${entry.data.operation} ${entry.data.success ? 'OK' : `FAIL: ${entry.data.error || 'unknown'}`}`;
|
|
2018
2170
|
default:
|
|
2019
2171
|
return JSON.stringify(entry.data).slice(0, 80);
|
|
2020
2172
|
}
|