auramaxx 0.0.13 → 0.0.14
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/.next/BUILD_ID +1 -1
- package/.next/app-build-manifest.json +24 -24
- package/.next/app-path-routes-manifest.json +6 -6
- package/.next/build-manifest.json +2 -2
- package/.next/prerender-manifest.json +36 -36
- package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/server/app/_not-found.html +1 -1
- package/.next/server/app/_not-found.rsc +1 -1
- package/.next/server/app/api/[...doc]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/api/agent-requests/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/apps/install/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/apps/manifests/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/apps/static/[...path]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/docs/plain/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/events/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/import-from-openclaw/[channel]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/import-from-openclaw/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/import-from-openclaw/validate/[channel]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/page_client-reference-manifest.js +1 -1
- package/.next/server/app/api/restart/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/update/route.js +7 -8
- package/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/version/route.js +1 -1
- package/.next/server/app/api/version/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/workspace/[id]/apps/[wid]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/workspace/[id]/apps/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/workspace/[id]/export/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/workspace/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/workspace/config/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/workspace/import/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/workspace/route_client-reference-manifest.js +1 -1
- package/.next/server/app/app-legacy-do-not-use/page_client-reference-manifest.js +1 -1
- package/.next/server/app/app-legacy-do-not-use.html +1 -1
- package/.next/server/app/app-legacy-do-not-use.rsc +1 -1
- package/.next/server/app/approve/[actionId]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/docs/[...doc]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/docs/page_client-reference-manifest.js +1 -1
- package/.next/server/app/health/page_client-reference-manifest.js +1 -1
- package/.next/server/app/health.html +1 -1
- package/.next/server/app/health.rsc +1 -1
- package/.next/server/app/hello/page_client-reference-manifest.js +1 -1
- package/.next/server/app/hello.html +1 -1
- package/.next/server/app/hello.rsc +1 -1
- package/.next/server/app/index.html +1 -1
- package/.next/server/app/index.rsc +2 -2
- package/.next/server/app/page.js +1 -1
- package/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/server/app/privacy/page_client-reference-manifest.js +1 -1
- package/.next/server/app/privacy.html +1 -1
- package/.next/server/app/privacy.rsc +1 -1
- package/.next/server/app/share/[token]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/terms/page_client-reference-manifest.js +1 -1
- package/.next/server/app/terms.html +1 -1
- package/.next/server/app/terms.rsc +1 -1
- package/.next/server/app/yo/page.js +1 -1
- package/.next/server/app/yo/page_client-reference-manifest.js +1 -1
- package/.next/server/app/yo.html +1 -1
- package/.next/server/app/yo.rsc +2 -2
- package/.next/server/app-paths-manifest.json +6 -6
- package/.next/server/functions-config-manifest.json +1 -1
- package/.next/server/pages/404.html +1 -1
- package/.next/server/pages/500.html +1 -1
- package/.next/server/server-reference-manifest.json +1 -1
- package/.next/static/chunks/app/{page-16dfcd1c7cc88bcc.js → page-617dd0e03d79e94f.js} +1 -1
- package/.next/static/chunks/app/yo/page-fceb03605805cb44.js +1 -0
- package/.next/trace +28 -28
- package/README.md +1 -1
- package/docs/AGENT_SETUP.md +1 -1
- package/docs/AUTH.md +1 -1
- package/docs/MCP.md +12 -0
- package/docs/SKILLS.md +1 -1
- package/docs/api/secrets/credentials.md +9 -0
- package/docs/external/POLICY.md +2 -2
- package/package.json +1 -1
- package/skills/auramaxx/SKILL.md +13 -31
- package/skills/auramaxx/docs/AGENT_SETUP.md +1 -1
- package/src/app/UnlockPageClient.tsx +9 -9
- package/src/app/api/update/route.ts +9 -10
- package/src/app/yo/page.tsx +1 -1
- package/src/server/cli/commands/agent.ts +58 -0
- package/src/server/cli/commands/init.ts +9 -9
- package/src/server/cli/lib/credential-resolve.ts +20 -1
- package/src/server/cli/lib/local-agent-trust.ts +4 -3
- package/src/server/lib/agent-profiles.ts +4 -3
- package/src/server/lib/update-check.ts +4 -0
- package/src/server/mcp/server.ts +83 -14
- package/src/server/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -1
- package/src/server/routes/credentials.ts +48 -5
- package/src/server/tests/cli/agent-auth.test.ts +114 -0
- package/src/server/tests/cli/local-agent-trust.test.ts +11 -6
- package/src/server/tests/endpoints/credentials.test.ts +40 -18
- package/src/server/tests/endpoints/escalation-migration-gate.test.ts +1 -1
- package/src/server/tests/lib/agent-profiles.test.ts +6 -0
- package/src/server/tests/lib/update-check.test.ts +9 -1
- package/.next/static/chunks/app/yo/page-719dc5f213fdfb30.js +0 -1
- /package/.next/static/{WshFGr6RxGYP6AbWuT9OG → 8ep63d3doVXsmbm1zj5k5}/_buildManifest.js +0 -0
- /package/.next/static/{WshFGr6RxGYP6AbWuT9OG → 8ep63d3doVXsmbm1zj5k5}/_ssgManifest.js +0 -0
package/README.md
CHANGED
package/docs/AGENT_SETUP.md
CHANGED
package/docs/AUTH.md
CHANGED
|
@@ -230,7 +230,7 @@ Rejected:
|
|
|
230
230
|
| Profile | Permissions | Read Scopes | Write Scopes | Excluded Fields | TTL | Max Reads |
|
|
231
231
|
|---------|------------|-------------|-------------|-----------------|-----|-----------|
|
|
232
232
|
| `strict` | `secret:read` | `agent:primary, agent:agent` | none | `password, cvv, privateKey, seedPhrase, refresh_token` | 1 hour | 50 |
|
|
233
|
-
| `dev` | `wallet:list, secret:read, secret:write, action:create, action:read, action:resolve` | `agent
|
|
233
|
+
| `dev` | `wallet:list, secret:read, secret:write, action:create, action:read, action:resolve` | `agent:primary, agent:agent` | `agent:primary, agent:agent` | `cvv, seedPhrase, privateKey, refresh_token` | 7 days | 500 |
|
|
234
234
|
| `admin` | `admin:*` | `*` | `*` | none | 7 days | unlimited |
|
|
235
235
|
|
|
236
236
|
Strict one-shot (temp) approval claims are capped to 5 minutes (`300s`).
|
package/docs/MCP.md
CHANGED
|
@@ -51,6 +51,15 @@ Use this flow first. It mirrors the `WORKING_WITH_SECRETS.md` command patterns.
|
|
|
51
51
|
|
|
52
52
|
- This returns redacted output (`"secret": "*******"`) and sets `AURA_DONTLOOK` in MCP server process scope.
|
|
53
53
|
|
|
54
|
+
Explicit protected field request:
|
|
55
|
+
|
|
56
|
+
```json
|
|
57
|
+
{
|
|
58
|
+
"name": "DONTLOOK",
|
|
59
|
+
"field": "password"
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
54
63
|
Command-scoped injection (recommended):
|
|
55
64
|
|
|
56
65
|
```json
|
|
@@ -90,6 +99,7 @@ Filter by name/tag/agent:
|
|
|
90
99
|
```json
|
|
91
100
|
{
|
|
92
101
|
"name": "DONTLOOK",
|
|
102
|
+
"field": "password",
|
|
93
103
|
"envVar": "AURA_DONTNOTE",
|
|
94
104
|
"command": ["/bin/zsh", "-lc", "printenv AURA_DONTNOTE"]
|
|
95
105
|
}
|
|
@@ -164,7 +174,9 @@ Diary notes use canonical note field key `content` (`value` is accepted as a leg
|
|
|
164
174
|
Note:
|
|
165
175
|
- Typed tools (`get_secret`, `put_secret`, `del_secret`, `share_secret`, `inject_secret`, `write_diary`, `approve`) use the active MCP token directly.
|
|
166
176
|
- `get_secret` and `inject_secret` are redacted by default (`secret: "*******"`). Set `dangerPlaintext: true` only for break-glass local debugging.
|
|
177
|
+
- `dangerPlaintext` only controls output masking. It does not request additional credential fields and does not trigger approval by itself.
|
|
167
178
|
- `get_secret` uses the default env var name `AURA_{SECRETNAME}`.
|
|
179
|
+
- `get_secret` and `inject_secret` accept optional `field`. When set, MCP sends `requestedFields` to `/credentials/:id/read` so excluded-field approvals happen only for explicitly requested protected fields.
|
|
168
180
|
- Both tools accept optional `command` (array of executable + args). When omitted, they return a `whatDo` guidance block and keep scope in MCP server process.
|
|
169
181
|
- Typed helpers have **built-in 403 escalation** — on permission denied they automatically return a structured `requiresHumanApproval` response. You do not need to detect 403s yourself for typed tools.
|
|
170
182
|
- `auth` does not auto-poll in background. It returns explicit `approveUrl` + `pollUrl` + `claim` guidance plus typed actions (`claimAction`, `retryAction`); callers must claim explicitly via `get_token`.
|
package/docs/SKILLS.md
CHANGED
|
@@ -74,6 +74,15 @@ Read:
|
|
|
74
74
|
```http
|
|
75
75
|
POST /credentials/:id/read
|
|
76
76
|
Authorization: Bearer <token>
|
|
77
|
+
Content-Type: application/json
|
|
78
|
+
|
|
79
|
+
{
|
|
80
|
+
"requestedFields": ["password"]
|
|
81
|
+
}
|
|
77
82
|
```
|
|
78
83
|
|
|
79
84
|
Response returns encrypted payload (never plaintext secret fields for non-admin agents).
|
|
85
|
+
|
|
86
|
+
`requestedFields` is optional:
|
|
87
|
+
- If omitted, excluded fields are redacted/omitted and no excluded-field approval is raised.
|
|
88
|
+
- If present and it explicitly includes an excluded field (for example `cvv`), the route can return `DENY_EXCLUDED_FIELD` and one-shot approval metadata.
|
package/docs/external/POLICY.md
CHANGED
|
@@ -21,8 +21,8 @@ Canonical token payload shape:
|
|
|
21
21
|
},
|
|
22
22
|
"walletAccess": ["0x..."],
|
|
23
23
|
"credentialAccess": {
|
|
24
|
-
"read": ["agent
|
|
25
|
-
"write": ["agent
|
|
24
|
+
"read": ["agent:primary"],
|
|
25
|
+
"write": ["agent:primary"],
|
|
26
26
|
"excludeFields": ["cvv", "password"],
|
|
27
27
|
"ttl": 3600,
|
|
28
28
|
"maxReads": 500
|
package/package.json
CHANGED
package/skills/auramaxx/SKILL.md
CHANGED
|
@@ -23,48 +23,30 @@ Always use this skill when user intent matches any of these:
|
|
|
23
23
|
- "give me my credit card details"
|
|
24
24
|
- "run this command with my <secret>"
|
|
25
25
|
|
|
26
|
-
## Auth Precedence
|
|
27
|
-
|
|
28
|
-
CLI auth order:
|
|
29
|
-
|
|
30
|
-
1. `AURA_TOKEN` env
|
|
31
|
-
2. socket bootstrap
|
|
32
|
-
3. `/auth` fallback
|
|
33
|
-
|
|
34
|
-
Notes:
|
|
35
|
-
|
|
36
|
-
- No stored-session fallback in this chain.
|
|
37
|
-
- If `--profile` / `--profile-version` are omitted, CLI resolves from `trust.localProfile` / `trust.localProfileVersion`, then seed defaults (`admin`, `v1`).
|
|
38
|
-
- `AURA_AUTH_PROFILE` / `AURA_AGENT_PROFILE` are not used for CLI fallback profile selection.
|
|
39
|
-
|
|
40
26
|
## Retrieval Flow (Do This First)
|
|
41
27
|
|
|
42
28
|
For login/API key/card/password requests:
|
|
43
29
|
|
|
44
|
-
1.
|
|
45
|
-
2.
|
|
46
|
-
3.
|
|
30
|
+
1. Use `npx auramaxx get SECRET` first to trigger the protected read.
|
|
31
|
+
2. If it returns `reqId` + `approveUrl`, send the `approveUrl` to the human.
|
|
32
|
+
3. After approval, claim the request by `reqId`.
|
|
33
|
+
4. Retry the original read once with `--reqId`.
|
|
47
34
|
|
|
48
35
|
Examples:
|
|
49
36
|
|
|
50
37
|
```bash
|
|
51
|
-
|
|
52
|
-
aura
|
|
53
|
-
|
|
38
|
+
npx auramaxx get SECRET
|
|
39
|
+
aura auth claim <reqId> --json
|
|
40
|
+
npx auramaxx get SECRET --reqId <reqId>
|
|
54
41
|
```
|
|
55
42
|
|
|
56
|
-
If
|
|
43
|
+
If you need to find the exact credential name first, list with a scoped query:
|
|
57
44
|
|
|
58
45
|
```bash
|
|
46
|
+
aura list --name facebook --json
|
|
59
47
|
aura list --name facebook --field username --json
|
|
60
48
|
```
|
|
61
49
|
|
|
62
|
-
API equivalent:
|
|
63
|
-
|
|
64
|
-
```text
|
|
65
|
-
GET /credentials?q=facebook
|
|
66
|
-
```
|
|
67
|
-
|
|
68
50
|
## Concrete CLI Examples
|
|
69
51
|
|
|
70
52
|
```bash
|
|
@@ -102,11 +84,11 @@ aura get FACEBOOK_LOGIN --reqId <reqId>
|
|
|
102
84
|
|
|
103
85
|
## 403 Handling
|
|
104
86
|
|
|
105
|
-
If response includes `reqId`:
|
|
87
|
+
If the denial response includes `reqId` + `approveUrl`:
|
|
106
88
|
|
|
107
|
-
1.
|
|
108
|
-
2. Claim
|
|
109
|
-
3. Retry original command with
|
|
89
|
+
1. Send the human the direct `approveUrl`.
|
|
90
|
+
2. Claim with `aura auth claim <reqId> --json`.
|
|
91
|
+
3. Retry the original command with `--reqId <reqId>`.
|
|
110
92
|
|
|
111
93
|
If 403 has no `reqId`:
|
|
112
94
|
|
|
@@ -47,23 +47,23 @@ type LocalPolicySettings = {
|
|
|
47
47
|
projectScopeMode: ProjectScopeMode;
|
|
48
48
|
};
|
|
49
49
|
|
|
50
|
-
const LOCAL_POLICY_PROFILES: LocalAgentMode[] = ['
|
|
50
|
+
const LOCAL_POLICY_PROFILES: LocalAgentMode[] = ['admin', 'dev', 'strict'];
|
|
51
51
|
const LOCAL_PROJECT_SCOPE_MODES: ProjectScopeMode[] = ['auto', 'strict', 'off'];
|
|
52
52
|
const LOCAL_PROFILE_ITEM_OPTIONS = [
|
|
53
53
|
{
|
|
54
|
-
value: '
|
|
55
|
-
label: '
|
|
56
|
-
description: '
|
|
54
|
+
value: 'admin',
|
|
55
|
+
label: 'maxx (admin)',
|
|
56
|
+
description: 'Full access. Use only when you fully trust the agent.',
|
|
57
57
|
},
|
|
58
58
|
{
|
|
59
59
|
value: 'dev',
|
|
60
|
-
label: 'dev',
|
|
61
|
-
description: '
|
|
60
|
+
label: 'mid (dev)',
|
|
61
|
+
description: 'Access to most things. Human approval for stuff like CVV.',
|
|
62
62
|
},
|
|
63
63
|
{
|
|
64
|
-
value: '
|
|
65
|
-
label: '
|
|
66
|
-
description: '
|
|
64
|
+
value: 'strict',
|
|
65
|
+
label: 'sus (local)',
|
|
66
|
+
description: 'Most locked down. Every request needs manual approval.',
|
|
67
67
|
},
|
|
68
68
|
] as const;
|
|
69
69
|
const LOCAL_PROJECT_SCOPE_ITEM_OPTIONS = [
|
|
@@ -3,12 +3,13 @@ import { spawn } from 'child_process';
|
|
|
3
3
|
import { join } from 'path';
|
|
4
4
|
import { mkdirSync } from 'fs';
|
|
5
5
|
import { homedir } from 'os';
|
|
6
|
-
import { buildUpdateCommand, buildUpdateFallbackCommand } from '@/server/lib/update-check';
|
|
6
|
+
import { buildUpdateCommand, buildUpdateFallbackCommand, buildUpdateForceCommand } from '@/server/lib/update-check';
|
|
7
7
|
import { clearVersionCheckCache } from '@/server/lib/version-check-cache';
|
|
8
8
|
|
|
9
9
|
export async function POST() {
|
|
10
10
|
try {
|
|
11
11
|
const primaryCommand = buildUpdateCommand();
|
|
12
|
+
const forceCommand = buildUpdateForceCommand();
|
|
12
13
|
const fallbackCommand = buildUpdateFallbackCommand();
|
|
13
14
|
const projectRoot = process.cwd();
|
|
14
15
|
const cliEntrypoint = join(projectRoot, 'bin', 'auramaxx.js');
|
|
@@ -19,7 +20,6 @@ export async function POST() {
|
|
|
19
20
|
|
|
20
21
|
const stopCommand = `${JSON.stringify(process.execPath)} ${JSON.stringify(cliEntrypoint)} stop`;
|
|
21
22
|
const startCommand = `${JSON.stringify(process.execPath)} ${JSON.stringify(cliEntrypoint)} start`;
|
|
22
|
-
const uninstallCommand = 'npm uninstall -g auramaxx';
|
|
23
23
|
|
|
24
24
|
// Detached worker allows update to stop/restart services without
|
|
25
25
|
// depending on the current dashboard process staying alive.
|
|
@@ -28,10 +28,10 @@ const { execSync } = require('child_process');
|
|
|
28
28
|
const fs = require('fs');
|
|
29
29
|
const logPath = process.env.AURA_UPDATE_LOG;
|
|
30
30
|
const primaryCommand = process.env.AURA_UPDATE_PRIMARY;
|
|
31
|
+
const forceCommand = process.env.AURA_UPDATE_FORCE;
|
|
31
32
|
const fallbackCommand = process.env.AURA_UPDATE_FALLBACK;
|
|
32
33
|
const stopCommand = process.env.AURA_UPDATE_STOP;
|
|
33
34
|
const startCommand = process.env.AURA_UPDATE_START;
|
|
34
|
-
const uninstallCommand = process.env.AURA_UPDATE_UNINSTALL;
|
|
35
35
|
|
|
36
36
|
function now() {
|
|
37
37
|
return new Date().toISOString();
|
|
@@ -73,15 +73,14 @@ if (run(primaryCommand, { allowFailure: true })) {
|
|
|
73
73
|
process.exit(0);
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
-
append(\`[\${now()}] primary install failed; retrying
|
|
77
|
-
run(
|
|
78
|
-
if (run(primaryCommand, { allowFailure: true })) {
|
|
76
|
+
append(\`[\${now()}] primary install failed; retrying forced install\`);
|
|
77
|
+
if (run(forceCommand, { allowFailure: true })) {
|
|
79
78
|
run(startCommand, { allowFailure: true });
|
|
80
|
-
append(\`[\${now()}] update workflow completed (
|
|
79
|
+
append(\`[\${now()}] update workflow completed (forced install)\`);
|
|
81
80
|
process.exit(0);
|
|
82
81
|
}
|
|
83
82
|
|
|
84
|
-
append(\`[\${now()}]
|
|
83
|
+
append(\`[\${now()}] forced install failed; falling back to npx start\`);
|
|
85
84
|
const fallbackOk = run(fallbackCommand, { allowFailure: true });
|
|
86
85
|
append(\`[\${now()}] update workflow completed (\${fallbackOk ? 'npx fallback' : 'failed'})\`);
|
|
87
86
|
process.exit(fallbackOk ? 0 : 1);
|
|
@@ -93,10 +92,10 @@ process.exit(fallbackOk ? 0 : 1);
|
|
|
93
92
|
...process.env,
|
|
94
93
|
AURA_UPDATE_LOG: logPath,
|
|
95
94
|
AURA_UPDATE_PRIMARY: primaryCommand,
|
|
95
|
+
AURA_UPDATE_FORCE: forceCommand,
|
|
96
96
|
AURA_UPDATE_FALLBACK: fallbackCommand,
|
|
97
97
|
AURA_UPDATE_STOP: stopCommand,
|
|
98
98
|
AURA_UPDATE_START: startCommand,
|
|
99
|
-
AURA_UPDATE_UNINSTALL: uninstallCommand,
|
|
100
99
|
},
|
|
101
100
|
detached: true,
|
|
102
101
|
stdio: 'ignore',
|
|
@@ -107,7 +106,7 @@ process.exit(fallbackOk ? 0 : 1);
|
|
|
107
106
|
return NextResponse.json({
|
|
108
107
|
success: true,
|
|
109
108
|
deferred: true,
|
|
110
|
-
message: 'Update workflow started: stop -> install ->
|
|
109
|
+
message: 'Update workflow started: stop -> install -> force-install retry -> npx fallback.',
|
|
111
110
|
output: `Running in background. Log: ${logPath}`,
|
|
112
111
|
logPath,
|
|
113
112
|
pid: worker.pid,
|
package/src/app/yo/page.tsx
CHANGED
|
@@ -310,7 +310,7 @@ export default function YoPage() {
|
|
|
310
310
|
rel="noopener noreferrer"
|
|
311
311
|
className="text-[10px] text-[var(--color-text-muted,#6b7280)] hover:text-[var(--color-text,#0a0a0a)] transition-colors"
|
|
312
312
|
>
|
|
313
|
-
by @hi_im_nico, with love
|
|
313
|
+
🗿 by @hi_im_nico, with love
|
|
314
314
|
</a>
|
|
315
315
|
</div>
|
|
316
316
|
</div>
|
|
@@ -51,6 +51,30 @@ interface AuthSession {
|
|
|
51
51
|
privateKeyPem?: string;
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
+
function decodeAgentTokenPayload(
|
|
55
|
+
token: string,
|
|
56
|
+
): { permissions?: unknown } | undefined {
|
|
57
|
+
const trimmed = token.trim();
|
|
58
|
+
if (!trimmed) return undefined;
|
|
59
|
+
const [payloadSegment] = trimmed.split('.', 1);
|
|
60
|
+
if (!payloadSegment) return undefined;
|
|
61
|
+
try {
|
|
62
|
+
const decoded = Buffer.from(payloadSegment, 'base64url').toString('utf8');
|
|
63
|
+
const parsed = JSON.parse(decoded) as unknown;
|
|
64
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) return undefined;
|
|
65
|
+
return parsed as { permissions?: unknown };
|
|
66
|
+
} catch {
|
|
67
|
+
return undefined;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function resolveAdminPermissionFromToken(token: string): boolean | undefined {
|
|
72
|
+
const payload = decodeAgentTokenPayload(token);
|
|
73
|
+
if (!payload) return undefined;
|
|
74
|
+
if (!Array.isArray(payload.permissions)) return false;
|
|
75
|
+
return payload.permissions.some((permission) => permission === 'admin:*');
|
|
76
|
+
}
|
|
77
|
+
|
|
54
78
|
async function getAuthToken(
|
|
55
79
|
keypair: EphemeralKeypair,
|
|
56
80
|
authSelection?: ProfileIssuanceSelection,
|
|
@@ -109,6 +133,13 @@ async function getReadToken(input: {
|
|
|
109
133
|
authSelection?: ProfileIssuanceSelection;
|
|
110
134
|
fallbackDecryptPrivateKeyPem: string;
|
|
111
135
|
}): Promise<{ readToken: string; decryptPrivateKeyPem: string }> {
|
|
136
|
+
const hasAdminPermission = resolveAdminPermissionFromToken(input.authToken);
|
|
137
|
+
if (hasAdminPermission === false) {
|
|
138
|
+
// Non-admin session tokens cannot call /actions/token.
|
|
139
|
+
// Skip delegated token minting to avoid a guaranteed 403 escalation cycle.
|
|
140
|
+
return { readToken: input.authToken, decryptPrivateKeyPem: input.fallbackDecryptPrivateKeyPem };
|
|
141
|
+
}
|
|
142
|
+
|
|
112
143
|
try {
|
|
113
144
|
const readToken = await createReadToken(
|
|
114
145
|
serverUrl(),
|
|
@@ -245,15 +276,26 @@ async function readCredential(
|
|
|
245
276
|
options?: {
|
|
246
277
|
retryCommandTemplate?: string;
|
|
247
278
|
originalCommand?: string;
|
|
279
|
+
requestedFields?: string[];
|
|
248
280
|
},
|
|
249
281
|
): Promise<DecryptedCredential> {
|
|
250
282
|
const originalCommand = String(options?.originalCommand || '').trim();
|
|
283
|
+
const requestedFields = Array.from(new Set(
|
|
284
|
+
(options?.requestedFields || [])
|
|
285
|
+
.map((value) => String(value || '').trim())
|
|
286
|
+
.filter((value) => value.length > 0),
|
|
287
|
+
));
|
|
288
|
+
const requestBody = requestedFields.length > 0
|
|
289
|
+
? JSON.stringify({ requestedFields })
|
|
290
|
+
: undefined;
|
|
251
291
|
const res = await fetch(`${serverUrl()}/credentials/${credentialId}/read`, {
|
|
252
292
|
method: 'POST',
|
|
253
293
|
headers: {
|
|
254
294
|
'Authorization': `Bearer ${readToken}`,
|
|
295
|
+
...(requestBody ? { 'Content-Type': 'application/json' } : {}),
|
|
255
296
|
...(originalCommand ? { 'X-Aura-Original-Command': originalCommand } : {}),
|
|
256
297
|
},
|
|
298
|
+
...(requestBody ? { body: requestBody } : {}),
|
|
257
299
|
signal: AbortSignal.timeout(8_000),
|
|
258
300
|
});
|
|
259
301
|
if (!res.ok) {
|
|
@@ -1250,9 +1292,13 @@ export async function runAgentCli(args: string[]): Promise<number> {
|
|
|
1250
1292
|
if (!readAuthContext) return 1;
|
|
1251
1293
|
|
|
1252
1294
|
try {
|
|
1295
|
+
const requestedFields = [
|
|
1296
|
+
getCredentialPrimaryFieldKey(normalizeCredentialType(target.type)),
|
|
1297
|
+
];
|
|
1253
1298
|
const decrypted = await readCredential(target.id, readAuthContext.readToken, readAuthContext.privateKeyPem, {
|
|
1254
1299
|
retryCommandTemplate,
|
|
1255
1300
|
originalCommand,
|
|
1301
|
+
requestedFields,
|
|
1256
1302
|
});
|
|
1257
1303
|
const primarySecret = resolvePrimarySecretField(
|
|
1258
1304
|
normalizeCredentialType(decrypted.type || target.type),
|
|
@@ -1299,11 +1345,23 @@ export async function runAgentCli(args: string[]): Promise<number> {
|
|
|
1299
1345
|
});
|
|
1300
1346
|
if (!readAuthContext) return 1;
|
|
1301
1347
|
|
|
1348
|
+
const resolvedTargetType = normalizeCredentialType(target.type);
|
|
1349
|
+
const requestedReadFields = flagJson
|
|
1350
|
+
? (fieldName
|
|
1351
|
+
? [canonicalizeCredentialFieldKey(resolvedTargetType, fieldName)]
|
|
1352
|
+
: ['*'])
|
|
1353
|
+
: execCommand.length > 0
|
|
1354
|
+
? [getCredentialPrimaryFieldKey(resolvedTargetType)]
|
|
1355
|
+
: fieldName
|
|
1356
|
+
? [canonicalizeCredentialFieldKey(resolvedTargetType, fieldName)]
|
|
1357
|
+
: [getCredentialPrimaryFieldKey(resolvedTargetType)];
|
|
1358
|
+
|
|
1302
1359
|
let decrypted: DecryptedCredential;
|
|
1303
1360
|
try {
|
|
1304
1361
|
decrypted = await readCredential(target.id, readAuthContext.readToken, readAuthContext.privateKeyPem, {
|
|
1305
1362
|
retryCommandTemplate,
|
|
1306
1363
|
originalCommand,
|
|
1364
|
+
requestedFields: requestedReadFields,
|
|
1307
1365
|
});
|
|
1308
1366
|
} finally {
|
|
1309
1367
|
finalizeReadAuthContext(readAuthContext);
|
|
@@ -335,30 +335,30 @@ async function readPasswordFromStdin(timeoutMs = 15_000): Promise<string> {
|
|
|
335
335
|
}
|
|
336
336
|
|
|
337
337
|
async function configureLocalSocketTrust(token: string): Promise<void> {
|
|
338
|
-
printSection('Local Agent Trust', '
|
|
338
|
+
printSection('Local Agent Trust', 'How much do you trust your agent?');
|
|
339
339
|
|
|
340
340
|
const profile = resolveLocalAgentModeChoice(
|
|
341
341
|
await promptSelect(
|
|
342
|
-
'
|
|
342
|
+
' How much do you trust your agent?',
|
|
343
343
|
[
|
|
344
|
-
{ value: '
|
|
345
|
-
{ value: '
|
|
346
|
-
{ value: '
|
|
344
|
+
{ value: 'admin', label: 'maxx (admin)', aliases: ['1', 'default', 'maxx', 'work'] },
|
|
345
|
+
{ value: 'dev', label: 'mid (dev)', aliases: ['2', 'mid', 'dev', 'recommended'] },
|
|
346
|
+
{ value: 'strict', label: 'sus (local)', aliases: ['3', 'sus', 'local', 'strict'] },
|
|
347
347
|
],
|
|
348
|
-
'
|
|
348
|
+
'admin',
|
|
349
349
|
),
|
|
350
350
|
);
|
|
351
351
|
await persistLocalAgentTrustDefaults(token, profile);
|
|
352
352
|
|
|
353
353
|
if (profile === 'strict') {
|
|
354
|
-
console.log(' ✓
|
|
354
|
+
console.log(' ✓ Sus mode enabled. Local auto-approve is OFF; agent requests require manual approval.\n');
|
|
355
355
|
return;
|
|
356
356
|
}
|
|
357
357
|
if (profile === 'admin') {
|
|
358
|
-
console.log(' ✓
|
|
358
|
+
console.log(' ✓ Maxx mode enabled. WARNING: local agents get broad access.\n');
|
|
359
359
|
return;
|
|
360
360
|
}
|
|
361
|
-
console.log(' ✓
|
|
361
|
+
console.log(' ✓ Mid mode enabled. Local auto-approve remains ON with scoped profile.\n');
|
|
362
362
|
}
|
|
363
363
|
|
|
364
364
|
async function configureApiKey(
|
|
@@ -146,10 +146,23 @@ export async function readCredential(
|
|
|
146
146
|
readToken: string,
|
|
147
147
|
credentialId: string,
|
|
148
148
|
decryptFn: (encrypted: string) => string,
|
|
149
|
+
requestedFields?: string[],
|
|
149
150
|
): Promise<DecryptedCredential> {
|
|
151
|
+
const normalizedRequestedFields = Array.from(new Set(
|
|
152
|
+
(requestedFields || [])
|
|
153
|
+
.map((value) => String(value || '').trim())
|
|
154
|
+
.filter((value) => value.length > 0),
|
|
155
|
+
));
|
|
156
|
+
const requestBody = normalizedRequestedFields.length > 0
|
|
157
|
+
? JSON.stringify({ requestedFields: normalizedRequestedFields })
|
|
158
|
+
: undefined;
|
|
150
159
|
const res = await fetch(`${baseUrl}/credentials/${credentialId}/read`, {
|
|
151
160
|
method: 'POST',
|
|
152
|
-
headers: {
|
|
161
|
+
headers: {
|
|
162
|
+
Authorization: `Bearer ${readToken}`,
|
|
163
|
+
...(requestBody ? { 'Content-Type': 'application/json' } : {}),
|
|
164
|
+
},
|
|
165
|
+
...(requestBody ? { body: requestBody } : {}),
|
|
153
166
|
signal: AbortSignal.timeout(5000),
|
|
154
167
|
});
|
|
155
168
|
if (!res.ok) {
|
|
@@ -190,9 +203,13 @@ export async function resolveMappings(
|
|
|
190
203
|
}
|
|
191
204
|
|
|
192
205
|
const uniqueTargets = new Map<string, AuraMapping>();
|
|
206
|
+
const requestedFieldsByTarget = new Map<string, Set<string>>();
|
|
193
207
|
for (const mapping of mappings) {
|
|
194
208
|
const key = `${(mapping.agent || '').toLowerCase()}::${mapping.credentialName.toLowerCase()}`;
|
|
195
209
|
if (!uniqueTargets.has(key)) uniqueTargets.set(key, mapping);
|
|
210
|
+
const requested = requestedFieldsByTarget.get(key) || new Set<string>();
|
|
211
|
+
requested.add(mapping.field);
|
|
212
|
+
requestedFieldsByTarget.set(key, requested);
|
|
196
213
|
}
|
|
197
214
|
|
|
198
215
|
const targetList = [...uniqueTargets.values()];
|
|
@@ -228,11 +245,13 @@ export async function resolveMappings(
|
|
|
228
245
|
}
|
|
229
246
|
|
|
230
247
|
try {
|
|
248
|
+
const requestedFields = Array.from(requestedFieldsByTarget.get(cacheKey) || []);
|
|
231
249
|
const decrypted = await readCredential(
|
|
232
250
|
baseUrl,
|
|
233
251
|
readToken,
|
|
234
252
|
meta.id,
|
|
235
253
|
decryptFn,
|
|
254
|
+
requestedFields,
|
|
236
255
|
);
|
|
237
256
|
credentialCache.set(cacheKey, decrypted);
|
|
238
257
|
} catch (err) {
|
|
@@ -9,9 +9,10 @@ export interface LocalAgentTrustDefaults {
|
|
|
9
9
|
|
|
10
10
|
export function resolveLocalAgentModeChoice(input: string): LocalAgentProfileMode {
|
|
11
11
|
const normalized = input.trim().toLowerCase();
|
|
12
|
-
if (normalized === '
|
|
13
|
-
if (normalized === '3' || normalized === '
|
|
14
|
-
return 'dev';
|
|
12
|
+
if (normalized === '1' || normalized === 'admin' || normalized === 'maxx' || normalized === 'work') return 'admin';
|
|
13
|
+
if (normalized === '3' || normalized === 'strict' || normalized === 'sus' || normalized === 'local') return 'strict';
|
|
14
|
+
if (normalized === '2' || normalized === 'dev' || normalized === 'mid' || normalized === 'recommended') return 'dev';
|
|
15
|
+
return 'admin';
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
export function toLocalAgentTrustDefaults(profile: LocalAgentProfileMode): LocalAgentTrustDefaults {
|
|
@@ -94,13 +94,14 @@ const BUILTIN_PROFILES: AgentPolicyProfileV1[] = [
|
|
|
94
94
|
rationale: 'Use for day-to-day local dev workflows without granting financial operations.',
|
|
95
95
|
permissions: ['wallet:list', 'secret:read', 'secret:write', 'action:create', 'action:read', 'action:resolve'],
|
|
96
96
|
credentialAccess: {
|
|
97
|
-
|
|
98
|
-
|
|
97
|
+
// Keep legacy `agent` alias for migrated installs while scoping to primary-only access.
|
|
98
|
+
readScopes: ['agent:primary', 'agent:agent'],
|
|
99
|
+
writeScopes: ['agent:primary', 'agent:agent'],
|
|
99
100
|
excludeFields: ['cvv', 'seedPhrase', 'privateKey', 'refresh_token'],
|
|
100
101
|
maxReads: 500,
|
|
101
102
|
},
|
|
102
103
|
tokenDefaults: { ttlSeconds: 7 * 24 * 60 * 60, maxReads: 500 },
|
|
103
|
-
warnings: ['Includes secret:write.
|
|
104
|
+
warnings: ['Includes secret:write. Credential scope is limited to the primary agent (legacy alias included).'],
|
|
104
105
|
},
|
|
105
106
|
{
|
|
106
107
|
id: 'admin',
|
|
@@ -34,6 +34,10 @@ export function buildUpdateCommand(packageName = 'auramaxx'): string {
|
|
|
34
34
|
return `npm install -g ${packageName} --foreground-scripts`;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
+
export function buildUpdateForceCommand(packageName = 'auramaxx'): string {
|
|
38
|
+
return `${buildUpdateCommand(packageName)} --force`;
|
|
39
|
+
}
|
|
40
|
+
|
|
37
41
|
export function buildNpxLatestCommand(packageName = 'auramaxx', args: string[] = []): string {
|
|
38
42
|
const suffix = args.length > 0 ? ` ${args.join(' ')}` : '';
|
|
39
43
|
return `npx --yes ${packageName}@latest${suffix}`;
|