@openparachute/vault 0.6.0 → 0.6.2-rc.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 +31 -6
- package/core/src/content-range.test.ts +374 -0
- package/core/src/content-range.ts +185 -0
- package/core/src/links.ts +76 -21
- package/core/src/mcp.ts +53 -1
- package/core/src/notes.ts +128 -40
- package/core/src/query-perf-routing.test.ts +208 -0
- package/core/src/schema.ts +30 -1
- package/package.json +1 -1
- package/src/cli.ts +90 -25
- package/src/content-range-routes.test.ts +178 -0
- package/src/github-device-flow.test.ts +265 -6
- package/src/github-device-flow.ts +297 -45
- package/src/init-summary.test.ts +125 -125
- package/src/init-summary.ts +89 -54
- package/src/init.test.ts +128 -0
- package/src/mirror-credentials.test.ts +20 -0
- package/src/mirror-credentials.ts +6 -2
- package/src/mirror-remote-guard.test.ts +269 -0
- package/src/mirror-remote-guard.ts +273 -0
- package/src/mirror-routes.test.ts +1118 -46
- package/src/mirror-routes.ts +405 -32
- package/src/routes.ts +69 -3
- package/src/routing.ts +8 -0
- package/src/vault.test.ts +56 -0
- package/web/ui/dist/assets/index-BPgyIjR7.js +61 -0
- package/web/ui/dist/index.html +1 -1
- package/web/ui/dist/assets/index-CGL256oe.js +0 -60
package/src/init-summary.test.ts
CHANGED
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Tests for `buildInitSummaryLines` — the post-install summary printed at the
|
|
3
|
-
* end of `vault init`.
|
|
4
|
-
*
|
|
5
|
-
*
|
|
3
|
+
* end of `vault init`.
|
|
4
|
+
*
|
|
5
|
+
* 2026-06-23 messaging realignment: the site no longer claims "Claude Code is
|
|
6
|
+
* auto-configured," and init no longer writes ~/.claude.json by default. The
|
|
7
|
+
* summary now (1) leads with the web setup wizard hand-off and (2) always
|
|
8
|
+
* surfaces the self-serve connect info — the connector URL plus a
|
|
9
|
+
* ready-to-paste `claude mcp add` command — so a Claude Code user opts in by
|
|
10
|
+
* copy-paste rather than via a silent side effect. These tests pin that copy
|
|
11
|
+
* across the (addMcp, addToken) decision matrix.
|
|
6
12
|
*/
|
|
7
13
|
|
|
8
14
|
import { describe, test, expect } from "bun:test";
|
|
@@ -13,6 +19,7 @@ const baseInput = {
|
|
|
13
19
|
bindHost: "127.0.0.1",
|
|
14
20
|
port: 1940,
|
|
15
21
|
mcpUrl: "http://127.0.0.1:1940/vault/default/mcp",
|
|
22
|
+
wizardUrl: "http://127.0.0.1:1939/admin/setup",
|
|
16
23
|
vaultName: "default",
|
|
17
24
|
};
|
|
18
25
|
|
|
@@ -21,161 +28,154 @@ function lines(addMcp: boolean, addToken: boolean, apiKey: string | undefined) {
|
|
|
21
28
|
}
|
|
22
29
|
|
|
23
30
|
describe("buildInitSummaryLines", () => {
|
|
24
|
-
|
|
25
|
-
|
|
31
|
+
// The wizard hand-off + self-serve connect info are the load-bearing pieces
|
|
32
|
+
// of the new messaging — they must appear in every branch.
|
|
33
|
+
describe("always surfaces the wizard + the copy-paste connect info", () => {
|
|
34
|
+
for (const [addMcp, addToken] of [
|
|
35
|
+
[true, true],
|
|
36
|
+
[true, false],
|
|
37
|
+
[false, true],
|
|
38
|
+
[false, false],
|
|
39
|
+
] as const) {
|
|
40
|
+
const out = lines(addMcp, addToken, addMcp || addToken ? "pvt_k" : undefined).join("\n");
|
|
26
41
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
42
|
+
test(`(addMcp=${addMcp}, addToken=${addToken}) prints the web wizard URL prominently`, () => {
|
|
43
|
+
expect(out).toContain("Finish setup in your browser:");
|
|
44
|
+
expect(out).toContain("http://127.0.0.1:1939/admin/setup");
|
|
45
|
+
});
|
|
30
46
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
47
|
+
test(`(addMcp=${addMcp}, addToken=${addToken}) surfaces the connector URL`, () => {
|
|
48
|
+
expect(out).toContain("http://127.0.0.1:1940/vault/default/mcp");
|
|
49
|
+
});
|
|
34
50
|
|
|
35
|
-
|
|
36
|
-
|
|
51
|
+
test(`(addMcp=${addMcp}, addToken=${addToken}) always prints Config: and Server: lines`, () => {
|
|
52
|
+
expect(out).toContain("Config: /tmp/parachute");
|
|
53
|
+
expect(out).toContain("Server: http://127.0.0.1:1940");
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
// The new DEFAULT init path — no MCP write, no token minted (per-user OAuth).
|
|
59
|
+
// init pointed the operator at the wizard and surfaced the copy-paste connect
|
|
60
|
+
// info; it did NOT write ~/.claude.json.
|
|
61
|
+
describe("DEFAULT (addMcp=N, token=N) — wizard hand-off + copy-paste opt-in", () => {
|
|
62
|
+
const out = lines(false, false, undefined).join("\n");
|
|
63
|
+
|
|
64
|
+
test("does NOT claim Claude Code is already wired in", () => {
|
|
65
|
+
expect(out).not.toContain("already wired in");
|
|
66
|
+
expect(out).not.toContain("Baked into ~/.claude.json");
|
|
37
67
|
});
|
|
38
68
|
|
|
39
|
-
test("
|
|
69
|
+
test("offers the ready-to-paste `claude mcp add` opt-in command", () => {
|
|
40
70
|
expect(out).toContain(
|
|
41
|
-
|
|
71
|
+
"claude mcp add --transport http parachute-vault http://127.0.0.1:1940/vault/default/mcp",
|
|
42
72
|
);
|
|
43
73
|
});
|
|
44
74
|
|
|
45
|
-
test("
|
|
46
|
-
expect(out).toContain("
|
|
47
|
-
});
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
describe("MCP=Y + token=N (MCP wired, token not surfaced)", () => {
|
|
51
|
-
const out = lines(true, false, "pvt_secret").join("\n");
|
|
52
|
-
|
|
53
|
-
test("does not print the token prominently", () => {
|
|
54
|
-
expect(out).not.toContain("pvt_secret");
|
|
75
|
+
test("points at the guided installer as an alternative", () => {
|
|
76
|
+
expect(out).toContain("parachute-vault mcp-install");
|
|
55
77
|
});
|
|
56
78
|
|
|
57
|
-
test("
|
|
58
|
-
expect(out).
|
|
79
|
+
test("frames OAuth-first connect — no token needed", () => {
|
|
80
|
+
expect(out).toContain("no token needed, you'll sign in on first use");
|
|
59
81
|
});
|
|
60
82
|
|
|
61
|
-
test("
|
|
62
|
-
|
|
63
|
-
|
|
83
|
+
test("offers the scope-narrow opt-in mint for scripts (full vault:<name>:read, never admin)", () => {
|
|
84
|
+
// Must be the three-segment named-resource form the hub mint-token model
|
|
85
|
+
// requires — a bare `vault:read` would mint a malformed scope (vault#443).
|
|
86
|
+
expect(out).toContain("parachute auth mint-token --scope vault:default:read");
|
|
87
|
+
expect(out).not.toContain("vault:admin");
|
|
64
88
|
});
|
|
65
89
|
|
|
66
|
-
test("
|
|
90
|
+
test("does not print any token", () => {
|
|
91
|
+
expect(out).not.toContain("Your API token:");
|
|
92
|
+
expect(out).not.toMatch(/pvt_/);
|
|
67
93
|
expect(out).not.toContain("Authorization: Bearer");
|
|
68
94
|
});
|
|
69
95
|
|
|
70
|
-
test("
|
|
71
|
-
|
|
96
|
+
test("threads a non-default vault name into the mint-token scope + connector URL", () => {
|
|
97
|
+
const out2 = buildInitSummaryLines({
|
|
98
|
+
...baseInput,
|
|
99
|
+
vaultName: "journal",
|
|
100
|
+
mcpUrl: "http://127.0.0.1:1940/vault/journal/mcp",
|
|
101
|
+
addMcp: false,
|
|
102
|
+
addToken: false,
|
|
103
|
+
apiKey: undefined,
|
|
104
|
+
}).join("\n");
|
|
105
|
+
expect(out2).toContain("parachute auth mint-token --scope vault:journal:read");
|
|
106
|
+
expect(out2).toContain("http://127.0.0.1:1940/vault/journal/mcp");
|
|
72
107
|
});
|
|
73
108
|
});
|
|
74
109
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
test("prints token prominently", () => {
|
|
79
|
-
expect(out).toContain("Your API token: pvt_xyz");
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
test("omits the 'Baked into' bullet (no claude.json entry written)", () => {
|
|
83
|
-
expect(out).not.toContain("Baked into ~/.claude.json");
|
|
84
|
-
});
|
|
110
|
+
// Opt-in: operator passed --configure-claude-code, so init DID write the entry.
|
|
111
|
+
describe("opted into MCP write (addMcp=Y, token=N, OAuth)", () => {
|
|
112
|
+
const out = lines(true, false, undefined).join("\n");
|
|
85
113
|
|
|
86
|
-
test("
|
|
87
|
-
expect(out).toContain(
|
|
114
|
+
test("tells the user Claude Code is already wired in", () => {
|
|
115
|
+
expect(out).toContain("Claude Code is already wired in");
|
|
88
116
|
});
|
|
89
117
|
|
|
90
|
-
test("
|
|
91
|
-
expect(out).toContain("Point any local MCP client");
|
|
118
|
+
test("still surfaces the connector URL for other clients", () => {
|
|
92
119
|
expect(out).toContain("http://127.0.0.1:1940/vault/default/mcp");
|
|
93
120
|
});
|
|
94
121
|
|
|
95
|
-
test("
|
|
96
|
-
expect(out).toContain("
|
|
122
|
+
test("does NOT print or imply any minted token", () => {
|
|
123
|
+
expect(out).not.toContain("Your API token:");
|
|
124
|
+
expect(out).not.toContain("Authorization: Bearer");
|
|
97
125
|
});
|
|
98
126
|
});
|
|
99
127
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
// never surface the old "no token issued" failure copy.
|
|
103
|
-
describe("MCP=Y + no token (vault#442 OAuth default)", () => {
|
|
104
|
-
const out = lines(true, false, undefined).join("\n");
|
|
105
|
-
|
|
106
|
-
test("leads with the OAuth connect message — no token needed", () => {
|
|
107
|
-
expect(out).toContain("no token needed, you'll sign in on first use");
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
test("tells the user Claude Code is already wired in", () => {
|
|
111
|
-
expect(out).toContain("Claude Code is already wired in");
|
|
112
|
-
});
|
|
128
|
+
describe("opted into MCP write + token minted (addMcp=Y, token=Y)", () => {
|
|
129
|
+
const out = lines(true, true, "pvt_abc123").join("\n");
|
|
113
130
|
|
|
114
|
-
test("
|
|
115
|
-
expect(out).toContain(
|
|
116
|
-
"claude mcp add --transport http parachute-vault http://127.0.0.1:1940/vault/default/mcp",
|
|
117
|
-
);
|
|
131
|
+
test("prints token prominently", () => {
|
|
132
|
+
expect(out).toContain("Your API token: pvt_abc123");
|
|
118
133
|
});
|
|
119
134
|
|
|
120
|
-
test("
|
|
121
|
-
|
|
122
|
-
// requires — a bare `vault:read` would mint a malformed scope (vault#443).
|
|
123
|
-
expect(out).toContain("parachute auth mint-token --scope vault:default:read");
|
|
124
|
-
expect(out).not.toContain("--scope vault:read ");
|
|
125
|
-
expect(out).not.toMatch(/--scope vault:read$/m);
|
|
126
|
-
expect(out).not.toContain("vault:admin");
|
|
135
|
+
test("notes token is baked into ~/.claude.json", () => {
|
|
136
|
+
expect(out).toContain("Baked into ~/.claude.json for Claude Code");
|
|
127
137
|
});
|
|
128
138
|
|
|
129
|
-
test("
|
|
130
|
-
expect(out).
|
|
131
|
-
expect(out).not.toContain("Baked into ~/.claude.json");
|
|
132
|
-
expect(out).not.toContain("Authorization: Bearer");
|
|
139
|
+
test("includes save-it-now warning", () => {
|
|
140
|
+
expect(out).toContain("Won't be shown again — save it now.");
|
|
133
141
|
});
|
|
134
142
|
|
|
135
|
-
test("
|
|
136
|
-
expect(out).
|
|
143
|
+
test("includes Bearer curl example", () => {
|
|
144
|
+
expect(out).toContain(
|
|
145
|
+
'curl -H "Authorization: Bearer pvt_abc123" http://localhost:1940/api/notes',
|
|
146
|
+
);
|
|
137
147
|
});
|
|
138
148
|
|
|
139
|
-
test("
|
|
140
|
-
|
|
141
|
-
...baseInput,
|
|
142
|
-
vaultName: "journal",
|
|
143
|
-
mcpUrl: "http://127.0.0.1:1940/vault/journal/mcp",
|
|
144
|
-
addMcp: true,
|
|
145
|
-
addToken: false,
|
|
146
|
-
apiKey: undefined,
|
|
147
|
-
}).join("\n");
|
|
148
|
-
expect(out2).toContain("parachute auth mint-token --scope vault:journal:read");
|
|
149
|
-
expect(out2).not.toContain("vault:default:read");
|
|
149
|
+
test("Next steps mentions starting a Claude Code session", () => {
|
|
150
|
+
expect(out).toContain("Start a new Claude Code session");
|
|
150
151
|
});
|
|
151
152
|
});
|
|
152
153
|
|
|
153
|
-
describe("MCP=N
|
|
154
|
-
const out = lines(false,
|
|
154
|
+
describe("token only, no MCP write (addMcp=N, token=Y, minted)", () => {
|
|
155
|
+
const out = lines(false, true, "pvt_xyz").join("\n");
|
|
155
156
|
|
|
156
|
-
test("
|
|
157
|
-
expect(out).toContain("
|
|
158
|
-
expect(out).not.toContain("your vault isn't reachable by any client");
|
|
157
|
+
test("prints token prominently", () => {
|
|
158
|
+
expect(out).toContain("Your API token: pvt_xyz");
|
|
159
159
|
});
|
|
160
160
|
|
|
161
|
-
test("
|
|
162
|
-
expect(out).toContain("
|
|
163
|
-
expect(out).not.toContain("mints a hub JWT");
|
|
161
|
+
test("omits the 'Baked into' bullet (no claude.json entry written)", () => {
|
|
162
|
+
expect(out).not.toContain("Baked into ~/.claude.json");
|
|
164
163
|
});
|
|
165
164
|
|
|
166
|
-
test("
|
|
167
|
-
expect(out).
|
|
168
|
-
expect(out).not.toMatch(/pvt_/);
|
|
165
|
+
test("includes Bearer curl example", () => {
|
|
166
|
+
expect(out).toContain("Authorization: Bearer pvt_xyz");
|
|
169
167
|
});
|
|
170
168
|
|
|
171
|
-
test("
|
|
172
|
-
expect(out).
|
|
169
|
+
test("surfaces the connector URL + a copy-paste Claude Code opt-in", () => {
|
|
170
|
+
expect(out).toContain("http://127.0.0.1:1940/vault/default/mcp");
|
|
171
|
+
expect(out).toContain(
|
|
172
|
+
"claude mcp add --transport http parachute-vault http://127.0.0.1:1940/vault/default/mcp",
|
|
173
|
+
);
|
|
173
174
|
});
|
|
174
175
|
});
|
|
175
176
|
|
|
176
|
-
// Explicit opt-in but no hub reachable to mint (vault#282 Stage 2
|
|
177
|
-
|
|
178
|
-
describe("MCP=N + token=Y but no hub (opt-in mint failed, standalone)", () => {
|
|
177
|
+
// Explicit opt-in to a token but no hub reachable to mint (vault#282 Stage 2).
|
|
178
|
+
describe("token opt-in but no hub (standalone, mint failed)", () => {
|
|
179
179
|
const out = buildInitSummaryLines({
|
|
180
180
|
...baseInput,
|
|
181
181
|
addMcp: false,
|
|
@@ -198,12 +198,14 @@ describe("buildInitSummaryLines", () => {
|
|
|
198
198
|
test("does NOT claim the vault is reachable (no hub present)", () => {
|
|
199
199
|
expect(out).not.toContain("Your vault is still reachable");
|
|
200
200
|
});
|
|
201
|
+
|
|
202
|
+
test("still surfaces the connector URL for self-serve connect", () => {
|
|
203
|
+
expect(out).toContain("http://127.0.0.1:1940/vault/default/mcp");
|
|
204
|
+
});
|
|
201
205
|
});
|
|
202
206
|
|
|
203
|
-
// #445: opted into a token, none minted, but a HUB IS PRESENT.
|
|
204
|
-
|
|
205
|
-
// so the standalone "isn't reachable" framing would be false here.
|
|
206
|
-
describe("MCP=N + token=Y, no token minted, but hub present (#445)", () => {
|
|
207
|
+
// #445: opted into a token, none minted, but a HUB IS PRESENT.
|
|
208
|
+
describe("token opt-in, none minted, hub present (#445)", () => {
|
|
207
209
|
const out = buildInitSummaryLines({
|
|
208
210
|
...baseInput,
|
|
209
211
|
addMcp: false,
|
|
@@ -227,22 +229,20 @@ describe("buildInitSummaryLines", () => {
|
|
|
227
229
|
expect(out).not.toContain("Once a hub is running");
|
|
228
230
|
expect(out).not.toContain("VAULT_AUTH_TOKEN");
|
|
229
231
|
});
|
|
230
|
-
|
|
231
|
-
test("never claims the vault isn't reachable by any client", () => {
|
|
232
|
-
expect(out).not.toContain("isn't reachable by any client");
|
|
233
|
-
});
|
|
234
232
|
});
|
|
235
233
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
234
|
+
// Defensive: the summary must still render coherently if no wizard URL is
|
|
235
|
+
// supplied (e.g. an older caller / a hub-origin resolution failure).
|
|
236
|
+
test("omits the wizard hand-off cleanly when wizardUrl is absent", () => {
|
|
237
|
+
const out = buildInitSummaryLines({
|
|
238
|
+
...baseInput,
|
|
239
|
+
wizardUrl: undefined,
|
|
240
|
+
addMcp: false,
|
|
241
|
+
addToken: false,
|
|
242
|
+
apiKey: undefined,
|
|
243
|
+
}).join("\n");
|
|
244
|
+
expect(out).not.toContain("Finish setup in your browser:");
|
|
245
|
+
// The connect info is still surfaced.
|
|
246
|
+
expect(out).toContain("http://127.0.0.1:1940/vault/default/mcp");
|
|
247
247
|
});
|
|
248
248
|
});
|
package/src/init-summary.ts
CHANGED
|
@@ -5,13 +5,32 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
export type InitSummaryInput = {
|
|
8
|
+
/**
|
|
9
|
+
* Whether init WROTE the Claude Code MCP config (~/.claude.json) this run.
|
|
10
|
+
* As of 2026-06-23 this is opt-in (default false) — init's primary job is to
|
|
11
|
+
* point the operator at the web setup wizard and surface the self-serve
|
|
12
|
+
* connect info, not to write a config file as a side effect.
|
|
13
|
+
*/
|
|
8
14
|
addMcp: boolean;
|
|
9
15
|
addToken: boolean;
|
|
10
16
|
apiKey: string | undefined;
|
|
11
17
|
configDir: string;
|
|
12
18
|
bindHost: string;
|
|
13
19
|
port: number;
|
|
20
|
+
/**
|
|
21
|
+
* The vault's MCP connector URL — `<hub-origin>/vault/<name>/mcp` (hub-origin
|
|
22
|
+
* / expose-state aware). Surfaced in the summary for self-serve copy-paste:
|
|
23
|
+
* a ready-to-run `claude mcp add ...` command is built from it so a Claude
|
|
24
|
+
* Code user can opt in by pasting one line, AND it's printed plain so any
|
|
25
|
+
* other MCP client can be pointed at it.
|
|
26
|
+
*/
|
|
14
27
|
mcpUrl: string;
|
|
28
|
+
/**
|
|
29
|
+
* The web setup wizard URL — `<hub-origin>/admin/setup`. init's primary job
|
|
30
|
+
* is to get the operator into this wizard, so it's printed prominently at the
|
|
31
|
+
* top of the summary.
|
|
32
|
+
*/
|
|
33
|
+
wizardUrl?: string | undefined;
|
|
15
34
|
/**
|
|
16
35
|
* The default vault's name — used to emit the three-segment
|
|
17
36
|
* `vault:<vaultName>:read` scope in the OAuth-first mint-token suggestion
|
|
@@ -22,8 +41,8 @@ export type InitSummaryInput = {
|
|
|
22
41
|
/**
|
|
23
42
|
* Guidance from the bootstrap-credential step when no token could be issued
|
|
24
43
|
* (standalone install, no hub reachable — vault#282 Stage 2). Surfaced when
|
|
25
|
-
* the operator wanted a token (`
|
|
26
|
-
*
|
|
44
|
+
* the operator wanted a token (`addToken`) but `apiKey` is undefined, so they
|
|
45
|
+
* know why and how to make the vault reachable.
|
|
27
46
|
*/
|
|
28
47
|
noTokenGuidance?: string | undefined;
|
|
29
48
|
/**
|
|
@@ -38,54 +57,66 @@ export type InitSummaryInput = {
|
|
|
38
57
|
};
|
|
39
58
|
|
|
40
59
|
/**
|
|
41
|
-
* Build the post-install summary lines for `vault init
|
|
42
|
-
*
|
|
60
|
+
* Build the post-install summary lines for `vault init`.
|
|
61
|
+
*
|
|
62
|
+
* 2026-06-23 messaging realignment: the site no longer claims "Claude Code is
|
|
63
|
+
* auto-configured," and init no longer writes `~/.claude.json` by default.
|
|
64
|
+
* init's job is to (1) point the operator at the web setup wizard, and
|
|
65
|
+
* (2) SURFACE the self-serve connect info — the connector URL + a ready-to-paste
|
|
66
|
+
* `claude mcp add ...` line — so a Claude Code user opts in by copy-paste rather
|
|
67
|
+
* than a silent side effect.
|
|
43
68
|
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
69
|
+
* The summary is built in three parts:
|
|
70
|
+
* 1. Wizard hand-off (always) — "finish setup in your browser: <wizardUrl>".
|
|
71
|
+
* 2. Connect-your-AI block — the connector URL + copy-paste `claude mcp add`,
|
|
72
|
+
* branched on whether init wrote the MCP entry / minted a token.
|
|
73
|
+
* 3. Config / server / next-steps footer.
|
|
48
74
|
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
* addMcp, !addToken, apiKey → token baked into claude.json, hint
|
|
52
|
-
* !addMcp, addToken, apiKey → token printed prominently
|
|
53
|
-
* !addMcp, addToken, !apiKey → opted into a token but no hub reachable
|
|
54
|
-
* !addMcp, !addToken → OAuth-first: add Claude Code later
|
|
75
|
+
* vault#442: per-user OAuth is the default — no token is minted unless the
|
|
76
|
+
* operator opts in (`addToken`), and then it's scope-narrow.
|
|
55
77
|
*/
|
|
56
78
|
export function buildInitSummaryLines(input: InitSummaryInput): string[] {
|
|
57
|
-
const { addMcp, addToken, apiKey, configDir, bindHost, port, mcpUrl, vaultName, noTokenGuidance, hubPresent } = input;
|
|
79
|
+
const { addMcp, addToken, apiKey, configDir, bindHost, port, mcpUrl, wizardUrl, vaultName, noTokenGuidance, hubPresent } = input;
|
|
58
80
|
const lines: string[] = [];
|
|
59
81
|
lines.push("");
|
|
60
82
|
lines.push("---");
|
|
61
83
|
|
|
62
|
-
|
|
84
|
+
// 1. Wizard hand-off — the primary purpose of init is to get the operator
|
|
85
|
+
// into the web setup wizard. Lead with it.
|
|
86
|
+
if (wizardUrl) {
|
|
63
87
|
lines.push("");
|
|
64
|
-
lines.push(
|
|
65
|
-
lines.push(`
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
//
|
|
76
|
-
|
|
77
|
-
lines.push("Connect your AI — no token needed, you'll sign in on first use:");
|
|
78
|
-
lines.push(` Claude Code is already wired in (~/.claude.json) — just start a session.`);
|
|
79
|
-
lines.push(` Other clients: claude mcp add --transport http parachute-vault ${mcpUrl}`);
|
|
80
|
-
lines.push(` Need a header-auth token for a script? parachute auth mint-token --scope vault:${vaultName}:read`);
|
|
81
|
-
} else if (!addMcp && addToken && apiKey) {
|
|
88
|
+
lines.push("Finish setup in your browser:");
|
|
89
|
+
lines.push(` ${wizardUrl}`);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// The copy-paste opt-in line for Claude Code (and any client that speaks the
|
|
93
|
+
// `claude mcp add` form). Built from the connector URL so it's the real
|
|
94
|
+
// endpoint, hub-origin aware.
|
|
95
|
+
const claudeAddCmd = `claude mcp add --transport http parachute-vault ${mcpUrl}`;
|
|
96
|
+
|
|
97
|
+
// 2. Connect-your-AI — surface the self-serve connect info every time.
|
|
98
|
+
if (addToken && apiKey) {
|
|
99
|
+
// Operator opted into a header-auth token AND it was minted. Surface it
|
|
100
|
+
// prominently (won't be shown again), plus the connector URL.
|
|
82
101
|
lines.push("");
|
|
83
102
|
lines.push(`Your API token: ${apiKey}`);
|
|
84
|
-
|
|
103
|
+
if (addMcp) {
|
|
104
|
+
lines.push(` - Baked into ~/.claude.json for Claude Code ✓`);
|
|
105
|
+
}
|
|
106
|
+
lines.push(` - Paste into another MCP client's config, or use as Authorization: Bearer <token>`);
|
|
85
107
|
lines.push(` - Won't be shown again — save it now.`);
|
|
86
|
-
|
|
108
|
+
lines.push("");
|
|
109
|
+
lines.push("Connector URL (point any MCP client here):");
|
|
110
|
+
lines.push(` ${mcpUrl}`);
|
|
111
|
+
if (!addMcp) {
|
|
112
|
+
lines.push("");
|
|
113
|
+
lines.push("Add Claude Code by copy-paste:");
|
|
114
|
+
lines.push(` ${claudeAddCmd}`);
|
|
115
|
+
}
|
|
116
|
+
} else if (addToken && !apiKey) {
|
|
87
117
|
// Explicitly opted into a token but none was minted (vault#282 Stage 2 —
|
|
88
|
-
// vault no longer mints local pvt_* tokens). Surface why + recovery
|
|
118
|
+
// vault no longer mints local pvt_* tokens). Surface why + recovery, then
|
|
119
|
+
// still print the self-serve connect info.
|
|
89
120
|
lines.push("");
|
|
90
121
|
lines.push(
|
|
91
122
|
noTokenGuidance ??
|
|
@@ -113,15 +144,23 @@ export function buildInitSummaryLines(input: InitSummaryInput): string[] {
|
|
|
113
144
|
" or set VAULT_AUTH_TOKEN for an operator-channel bearer.",
|
|
114
145
|
);
|
|
115
146
|
}
|
|
116
|
-
} else if (!addMcp && !addToken) {
|
|
117
|
-
// OAuth-first, but the operator skipped wiring Claude Code too.
|
|
118
147
|
lines.push("");
|
|
119
|
-
lines.push(
|
|
120
|
-
|
|
121
|
-
);
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
);
|
|
148
|
+
lines.push("Connect your AI — no token needed, you'll sign in on first use:");
|
|
149
|
+
lines.push(` Connector URL: ${mcpUrl}`);
|
|
150
|
+
lines.push(` Claude Code: ${claudeAddCmd}`);
|
|
151
|
+
} else {
|
|
152
|
+
// Default path (no token). Per-user OAuth — sign in on first connect.
|
|
153
|
+
lines.push("");
|
|
154
|
+
lines.push("Connect your AI — no token needed, you'll sign in on first use:");
|
|
155
|
+
lines.push(` Connector URL: ${mcpUrl}`);
|
|
156
|
+
if (addMcp) {
|
|
157
|
+
lines.push(` Claude Code is already wired in (~/.claude.json) — just start a session.`);
|
|
158
|
+
lines.push(` Other clients: ${claudeAddCmd}`);
|
|
159
|
+
} else {
|
|
160
|
+
lines.push(` Claude Code: ${claudeAddCmd}`);
|
|
161
|
+
lines.push(` Other clients (Codex, Goose, OpenCode, Cursor, Zed, Cline): point them at the connector URL above.`);
|
|
162
|
+
}
|
|
163
|
+
lines.push(` Need a header-auth token for a script? parachute auth mint-token --scope vault:${vaultName}:read`);
|
|
125
164
|
}
|
|
126
165
|
|
|
127
166
|
lines.push("");
|
|
@@ -137,19 +176,15 @@ export function buildInitSummaryLines(input: InitSummaryInput): string[] {
|
|
|
137
176
|
|
|
138
177
|
lines.push("");
|
|
139
178
|
lines.push(`Next steps:`);
|
|
179
|
+
if (wizardUrl) {
|
|
180
|
+
lines.push(` - Finish setup in the web wizard: ${wizardUrl}`);
|
|
181
|
+
}
|
|
140
182
|
if (addMcp) {
|
|
141
183
|
lines.push(` - Start a new Claude Code session — your Vault is already wired in. Try:`);
|
|
142
184
|
lines.push(` claude "Help me set up my parachute vault"`);
|
|
143
|
-
lines.push(` - Or point any other local MCP client (Codex, Goose, OpenCode, Cursor,`);
|
|
144
|
-
lines.push(` Zed, Cline, your own agent) at:`);
|
|
145
|
-
lines.push(` ${mcpUrl}`);
|
|
146
|
-
} else if (addToken) {
|
|
147
|
-
lines.push(` - Point any local MCP client (Codex, Goose, OpenCode, Cursor, Zed,`);
|
|
148
|
-
lines.push(` Cline, your own agent) at:`);
|
|
149
|
-
lines.push(` ${mcpUrl}`);
|
|
150
|
-
lines.push(` - Or add Claude Code back anytime: parachute-vault mcp-install`);
|
|
151
185
|
} else {
|
|
152
|
-
lines.push(` -
|
|
186
|
+
lines.push(` - Wire Claude Code (copy-paste): ${claudeAddCmd}`);
|
|
187
|
+
lines.push(` or run the guided installer: parachute-vault mcp-install`);
|
|
153
188
|
}
|
|
154
189
|
lines.push(` - Check status: parachute-vault status`);
|
|
155
190
|
lines.push(` - Edit config: parachute-vault config`);
|