@openparachute/vault 0.2.4 → 0.3.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/.claude/settings.local.json +2 -25
- package/CHANGELOG.md +64 -0
- package/CLAUDE.md +17 -7
- package/README.md +169 -136
- package/core/src/core.test.ts +591 -19
- package/core/src/hooks.ts +111 -3
- package/core/src/indexed-fields.test.ts +285 -0
- package/core/src/indexed-fields.ts +238 -0
- package/core/src/mcp.ts +127 -6
- package/core/src/notes.ts +153 -11
- package/core/src/query-operators.ts +174 -0
- package/core/src/schema.ts +69 -2
- package/core/src/store.ts +95 -1
- package/core/src/tag-schemas.ts +5 -0
- package/core/src/types.ts +28 -1
- package/docs/HTTP_API.md +105 -1
- package/docs/auth-model.md +340 -0
- package/package/package.json +32 -0
- package/package.json +2 -2
- package/src/auth.test.ts +83 -114
- package/src/auth.ts +68 -6
- package/src/backup-launchd.ts +1 -1
- package/src/backup.test.ts +1 -1
- package/src/backup.ts +18 -17
- package/src/bind.test.ts +28 -0
- package/src/bind.ts +19 -0
- package/src/cli.ts +228 -133
- package/src/config-triggers.test.ts +49 -0
- package/src/config.test.ts +317 -2
- package/src/config.ts +420 -40
- package/src/context.test.ts +136 -0
- package/src/context.ts +115 -0
- package/src/daemon.ts +17 -16
- package/src/doctor.test.ts +9 -7
- package/src/launchd.test.ts +1 -1
- package/src/launchd.ts +6 -6
- package/src/mcp-http.ts +75 -21
- package/src/mcp-install.test.ts +125 -0
- package/src/mcp-install.ts +60 -0
- package/src/mcp-tools.ts +34 -96
- package/src/module-config.ts +109 -0
- package/src/oauth.test.ts +345 -57
- package/src/oauth.ts +155 -35
- package/src/published.test.ts +2 -2
- package/src/routes.ts +209 -33
- package/src/routing.test.ts +817 -300
- package/src/routing.ts +204 -202
- package/src/scopes.test.ts +294 -0
- package/src/scopes.ts +253 -0
- package/src/scribe-env.test.ts +49 -0
- package/src/scribe-env.ts +33 -0
- package/src/server.ts +73 -9
- package/src/services-manifest.test.ts +140 -0
- package/src/services-manifest.ts +99 -0
- package/src/systemd.ts +3 -3
- package/src/token-store.ts +42 -9
- package/src/transcription-worker.test.ts +864 -0
- package/src/transcription-worker.ts +501 -0
- package/src/triggers.test.ts +191 -1
- package/src/triggers.ts +17 -2
- package/src/vault.test.ts +693 -77
- package/src/version.test.ts +1 -1
- package/.playwright-mcp/console-2026-04-14T04-17-25-395Z.log +0 -2
- package/.playwright-mcp/console-2026-04-14T04-18-11-767Z.log +0 -1
- package/.playwright-mcp/console-2026-04-14T04-19-07-733Z.log +0 -2
- package/.playwright-mcp/console-2026-04-14T04-20-45-440Z.log +0 -2
- package/.playwright-mcp/page-2026-04-14T04-17-25-536Z.yml +0 -1
- package/.playwright-mcp/page-2026-04-14T04-18-11-816Z.yml +0 -1
- package/.playwright-mcp/page-2026-04-14T04-18-31-674Z.yml +0 -211
- package/.playwright-mcp/page-2026-04-14T04-19-07-795Z.yml +0 -59
- package/.playwright-mcp/page-2026-04-14T04-19-36-239Z.yml +0 -232
- package/.playwright-mcp/page-2026-04-14T04-19-58-327Z.yml +0 -182
- package/.playwright-mcp/page-2026-04-14T04-20-10-517Z.yml +0 -91
- package/.playwright-mcp/page-2026-04-14T04-20-14-796Z.yml +0 -70
- package/.playwright-mcp/page-2026-04-14T04-20-45-509Z.yml +0 -59
- package/religions-abrahamic-filter.png +0 -0
- package/religions-buddhism-v2.png +0 -0
- package/religions-buddhism.png +0 -0
- package/religions-final.png +0 -0
- package/religions-v1.png +0 -0
- package/religions-v2.png +0 -0
- package/religions-zen.png +0 -0
- package/web/README.md +0 -73
- package/web/bun.lock +0 -827
- package/web/eslint.config.js +0 -23
- package/web/index.html +0 -15
- package/web/package.json +0 -36
- package/web/public/favicon.svg +0 -1
- package/web/public/icons.svg +0 -24
- package/web/src/App.tsx +0 -149
- package/web/src/Graph.tsx +0 -200
- package/web/src/NoteView.tsx +0 -155
- package/web/src/Sidebar.tsx +0 -186
- package/web/src/api.ts +0 -21
- package/web/src/index.css +0 -50
- package/web/src/main.tsx +0 -10
- package/web/src/types.ts +0 -37
- package/web/src/utils.ts +0 -107
- package/web/tsconfig.app.json +0 -25
- package/web/tsconfig.json +0 -7
- package/web/tsconfig.node.json +0 -24
- package/web/vite.config.ts +0 -16
package/src/cli.ts
CHANGED
|
@@ -4,16 +4,16 @@
|
|
|
4
4
|
* Parachute Vault CLI.
|
|
5
5
|
*
|
|
6
6
|
* Usage:
|
|
7
|
-
* parachute
|
|
8
|
-
* parachute
|
|
9
|
-
* parachute
|
|
10
|
-
* parachute
|
|
11
|
-
* parachute
|
|
12
|
-
* parachute
|
|
13
|
-
* parachute
|
|
14
|
-
* parachute
|
|
15
|
-
* parachute
|
|
16
|
-
* parachute
|
|
7
|
+
* parachute-vault init — set up everything, one command
|
|
8
|
+
* parachute-vault create <name> — create a new vault
|
|
9
|
+
* parachute-vault list — list all vaults
|
|
10
|
+
* parachute-vault mcp-install <name> — add vault MCP to ~/.claude.json
|
|
11
|
+
* parachute-vault remove <name> — remove a vault
|
|
12
|
+
* parachute-vault config — show all config
|
|
13
|
+
* parachute-vault config set <key> <val> — set a config value
|
|
14
|
+
* parachute-vault config unset <key> — remove a config value
|
|
15
|
+
* parachute-vault serve — run the server (foreground)
|
|
16
|
+
* parachute-vault status — show full status
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
19
|
import { resolve } from "path";
|
|
@@ -25,6 +25,7 @@ import { existsSync, readFileSync, writeFileSync, rmSync, mkdirSync } from "fs";
|
|
|
25
25
|
import pkg from "../package.json" with { type: "json" };
|
|
26
26
|
import {
|
|
27
27
|
ensureConfigDirSync,
|
|
28
|
+
migrateFromLegacyLayout,
|
|
28
29
|
readVaultConfig,
|
|
29
30
|
writeVaultConfig,
|
|
30
31
|
readGlobalConfig,
|
|
@@ -45,8 +46,9 @@ import {
|
|
|
45
46
|
GLOBAL_CONFIG_PATH,
|
|
46
47
|
} from "./config.ts";
|
|
47
48
|
import type { VaultConfig } from "./config.ts";
|
|
48
|
-
import {
|
|
49
|
+
import { DATA_DIR } from "./config.ts";
|
|
49
50
|
import { installAgent, uninstallAgent, isAgentLoaded, restartAgent } from "./launchd.ts";
|
|
51
|
+
import { chooseMcpUrl } from "./mcp-install.ts";
|
|
50
52
|
import {
|
|
51
53
|
runBackup,
|
|
52
54
|
readLastBackup,
|
|
@@ -77,7 +79,9 @@ import {
|
|
|
77
79
|
import { confirm, ask, askPassword, choose } from "./prompt.ts";
|
|
78
80
|
import { generateToken, createToken, listTokens, revokeToken, migrateVaultKeys } from "./token-store.ts";
|
|
79
81
|
import type { TokenPermission } from "./token-store.ts";
|
|
82
|
+
import { resolveCreateTokenFlags, VAULT_SCOPES } from "./scopes.ts";
|
|
80
83
|
import { getVaultStore } from "./vault-store.ts";
|
|
84
|
+
import { upsertService, ServicesManifestError } from "./services-manifest.ts";
|
|
81
85
|
import {
|
|
82
86
|
hasOwnerPassword,
|
|
83
87
|
setOwnerPassword,
|
|
@@ -103,7 +107,7 @@ import {
|
|
|
103
107
|
|
|
104
108
|
const args = process.argv.slice(2);
|
|
105
109
|
|
|
106
|
-
// Support both `parachute
|
|
110
|
+
// Support both `parachute-vault <cmd>` and `parachute <cmd>` patterns
|
|
107
111
|
let command: string;
|
|
108
112
|
let cmdArgs: string[];
|
|
109
113
|
|
|
@@ -119,9 +123,20 @@ if (args[0] === "vault") {
|
|
|
119
123
|
// Commands
|
|
120
124
|
// ---------------------------------------------------------------------------
|
|
121
125
|
|
|
126
|
+
// Pre-0.3 installs kept vault state directly under ~/.parachute/. Run the
|
|
127
|
+
// migration unconditionally so any command (including read-only ones like
|
|
128
|
+
// `doctor` and `url`) picks up the relocated state on first post-upgrade run.
|
|
129
|
+
// No-op when no legacy paths exist. Skipped for `help` and `version`, which
|
|
130
|
+
// are expected to produce *only* their documented output — and because scripts
|
|
131
|
+
// piping `parachute-vault --version` shouldn't get migration chatter on stderr.
|
|
132
|
+
const SKIP_MIGRATION = new Set(["help", "--help", "-h", "version", "--version", "-v"]);
|
|
133
|
+
if (!SKIP_MIGRATION.has(command)) {
|
|
134
|
+
migrateFromLegacyLayout();
|
|
135
|
+
}
|
|
136
|
+
|
|
122
137
|
switch (command) {
|
|
123
138
|
case "init":
|
|
124
|
-
await cmdInit();
|
|
139
|
+
await cmdInit(cmdArgs);
|
|
125
140
|
break;
|
|
126
141
|
case "create":
|
|
127
142
|
cmdCreate(cmdArgs);
|
|
@@ -188,7 +203,7 @@ switch (command) {
|
|
|
188
203
|
case "--version":
|
|
189
204
|
case "-v":
|
|
190
205
|
// Intentionally minimal — just the version string on stdout. Scripts
|
|
191
|
-
// (and `parachute
|
|
206
|
+
// (and `parachute-vault doctor` in a future check) rely on this being
|
|
192
207
|
// a bare-number line; anything else belongs in `vault status`.
|
|
193
208
|
console.log(pkg.version);
|
|
194
209
|
break;
|
|
@@ -202,9 +217,16 @@ switch (command) {
|
|
|
202
217
|
// Command implementations
|
|
203
218
|
// ---------------------------------------------------------------------------
|
|
204
219
|
|
|
205
|
-
async function cmdInit() {
|
|
220
|
+
async function cmdInit(args: string[] = []) {
|
|
206
221
|
ensureConfigDirSync();
|
|
207
222
|
|
|
223
|
+
// Flags: --mcp installs MCP in ~/.claude.json without prompting;
|
|
224
|
+
// --no-mcp skips it without prompting. If both passed, --no-mcp wins
|
|
225
|
+
// (safer default). Neither → prompt in a TTY, default-yes in a
|
|
226
|
+
// non-TTY for back-compat with existing piped install scripts.
|
|
227
|
+
const flagMcpOn = args.includes("--mcp");
|
|
228
|
+
const flagMcpOff = args.includes("--no-mcp");
|
|
229
|
+
|
|
208
230
|
const isMac = process.platform === "darwin";
|
|
209
231
|
const isLinux = process.platform === "linux";
|
|
210
232
|
const isFirstRun = !existsSync(ENV_PATH);
|
|
@@ -249,6 +271,32 @@ async function cmdInit() {
|
|
|
249
271
|
}
|
|
250
272
|
writeGlobalConfig(globalConfig);
|
|
251
273
|
|
|
274
|
+
// 2a. Register in the shared services manifest so the @openparachute/cli
|
|
275
|
+
// dispatcher can discover this service and its health endpoint. Upserts
|
|
276
|
+
// by name, preserving entries for other services. Non-fatal on failure —
|
|
277
|
+
// init can complete without the manifest, just with a warning.
|
|
278
|
+
//
|
|
279
|
+
// `paths[0]` is the canonical mount point — CLI uses it for the
|
|
280
|
+
// `.well-known/parachute.json` URL and for `parachute expose`. Advertise
|
|
281
|
+
// `/vault/<default_vault>` so MCP clients land at the scoped endpoint.
|
|
282
|
+
// When no default vault exists yet (multi-vault, no fallback), fall back
|
|
283
|
+
// to "/" — the CLI can detect and prompt.
|
|
284
|
+
const servicePath = globalConfig.default_vault
|
|
285
|
+
? `/vault/${globalConfig.default_vault}`
|
|
286
|
+
: "/";
|
|
287
|
+
try {
|
|
288
|
+
upsertService({
|
|
289
|
+
name: "parachute-vault",
|
|
290
|
+
port: globalConfig.port || DEFAULT_PORT,
|
|
291
|
+
paths: [servicePath],
|
|
292
|
+
health: "/health",
|
|
293
|
+
version: pkg.version,
|
|
294
|
+
});
|
|
295
|
+
} catch (err) {
|
|
296
|
+
const msg = err instanceof ServicesManifestError ? err.message : String(err);
|
|
297
|
+
console.log(` Warning: could not update ~/.parachute/services.json: ${msg}`);
|
|
298
|
+
}
|
|
299
|
+
|
|
252
300
|
// 2b. Migrate existing legacy keys into per-vault token tables
|
|
253
301
|
for (const v of listVaults()) {
|
|
254
302
|
try {
|
|
@@ -297,13 +345,32 @@ async function cmdInit() {
|
|
|
297
345
|
}
|
|
298
346
|
if (serverPath) {
|
|
299
347
|
console.log(` Server path: ${serverPath}`);
|
|
300
|
-
console.log(` Wrapper: ~/.parachute/start.sh`);
|
|
348
|
+
console.log(` Wrapper: ~/.parachute/vault/start.sh`);
|
|
301
349
|
}
|
|
302
350
|
console.log(` Listening on http://0.0.0.0:${globalConfig.port || DEFAULT_PORT}`);
|
|
303
351
|
|
|
304
|
-
// 7. Install MCP for Claude Code (with token for auth)
|
|
305
|
-
|
|
306
|
-
|
|
352
|
+
// 7. Install MCP for Claude Code (with token for auth) — user confirms
|
|
353
|
+
// unless --mcp / --no-mcp explicitly passed. Writing to ~/.claude.json
|
|
354
|
+
// is a side effect some users don't want; default-yes in a TTY since
|
|
355
|
+
// most users installing vault want Claude Code to see it, but ask.
|
|
356
|
+
let addMcp: boolean;
|
|
357
|
+
if (flagMcpOff) {
|
|
358
|
+
addMcp = false;
|
|
359
|
+
} else if (flagMcpOn) {
|
|
360
|
+
addMcp = true;
|
|
361
|
+
} else if (process.stdin.isTTY) {
|
|
362
|
+
addMcp = await confirm("Add Vault MCP to Claude Code (~/.claude.json)?", true);
|
|
363
|
+
} else {
|
|
364
|
+
addMcp = true; // non-interactive: preserve the installable-via-pipe default
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
if (addMcp) {
|
|
368
|
+
installMcpConfig(apiKey);
|
|
369
|
+
console.log(` MCP server added to ~/.claude.json`);
|
|
370
|
+
} else {
|
|
371
|
+
console.log(" Skipped adding MCP to ~/.claude.json.");
|
|
372
|
+
console.log(" Run `parachute-vault mcp-install` later if you want it.");
|
|
373
|
+
}
|
|
307
374
|
|
|
308
375
|
// 8. Summary
|
|
309
376
|
console.log("\n---");
|
|
@@ -326,8 +393,8 @@ async function cmdInit() {
|
|
|
326
393
|
}
|
|
327
394
|
|
|
328
395
|
console.log(`\nNext steps:`);
|
|
329
|
-
console.log(` parachute
|
|
330
|
-
console.log(` parachute
|
|
396
|
+
console.log(` parachute-vault status check everything is running`);
|
|
397
|
+
console.log(` parachute-vault config view/edit configuration`);
|
|
331
398
|
}
|
|
332
399
|
|
|
333
400
|
async function promptForOwnerPassword(purpose: string): Promise<boolean> {
|
|
@@ -339,7 +406,7 @@ async function promptForOwnerPassword(purpose: string): Promise<boolean> {
|
|
|
339
406
|
while (true) {
|
|
340
407
|
const pw = await askPassword(" Password (or leave blank to skip)");
|
|
341
408
|
if (!pw) {
|
|
342
|
-
console.log(" Skipped — you can set one later with `parachute
|
|
409
|
+
console.log(" Skipped — you can set one later with `parachute-vault set-password`.");
|
|
343
410
|
return false;
|
|
344
411
|
}
|
|
345
412
|
|
|
@@ -391,13 +458,13 @@ async function cmdSetPassword(args: string[]) {
|
|
|
391
458
|
}
|
|
392
459
|
|
|
393
460
|
// ---------------------------------------------------------------------------
|
|
394
|
-
// 2FA — parachute
|
|
461
|
+
// 2FA — parachute-vault 2fa [enroll | disable | backup-codes | status]
|
|
395
462
|
// ---------------------------------------------------------------------------
|
|
396
463
|
|
|
397
464
|
async function confirmOwnerPassword(purpose: string): Promise<boolean> {
|
|
398
465
|
const hash = getOwnerPasswordHash();
|
|
399
466
|
if (!hash) {
|
|
400
|
-
console.error("No owner password is set. Run: parachute
|
|
467
|
+
console.error("No owner password is set. Run: parachute-vault set-password");
|
|
401
468
|
return false;
|
|
402
469
|
}
|
|
403
470
|
console.log(purpose);
|
|
@@ -460,14 +527,14 @@ async function cmd2fa(args: string[]) {
|
|
|
460
527
|
console.log(`2FA: enabled (${getBackupCodeCount()} backup code(s) remaining)`);
|
|
461
528
|
} else {
|
|
462
529
|
console.log("2FA: not enabled");
|
|
463
|
-
console.log(" Enable with: parachute
|
|
530
|
+
console.log(" Enable with: parachute-vault 2fa enroll");
|
|
464
531
|
}
|
|
465
532
|
return;
|
|
466
533
|
}
|
|
467
534
|
|
|
468
535
|
if (sub === "enroll") {
|
|
469
536
|
if (!hasOwnerPassword()) {
|
|
470
|
-
console.error("Set an owner password first: parachute
|
|
537
|
+
console.error("Set an owner password first: parachute-vault set-password");
|
|
471
538
|
process.exit(1);
|
|
472
539
|
}
|
|
473
540
|
if (hasTotpEnrolled()) {
|
|
@@ -511,7 +578,7 @@ async function cmd2fa(args: string[]) {
|
|
|
511
578
|
console.log(` Incorrect code. (${2 - attempt} attempt(s) left)`);
|
|
512
579
|
}
|
|
513
580
|
if (!confirmed) {
|
|
514
|
-
console.error("Enrollment failed — rolling back. Re-run `parachute
|
|
581
|
+
console.error("Enrollment failed — rolling back. Re-run `parachute-vault 2fa enroll` to try again.");
|
|
515
582
|
disableTotp();
|
|
516
583
|
process.exit(1);
|
|
517
584
|
}
|
|
@@ -539,7 +606,7 @@ async function cmd2fa(args: string[]) {
|
|
|
539
606
|
|
|
540
607
|
if (sub === "backup-codes") {
|
|
541
608
|
if (!hasTotpEnrolled()) {
|
|
542
|
-
console.error("2FA is not enabled. Run: parachute
|
|
609
|
+
console.error("2FA is not enabled. Run: parachute-vault 2fa enroll");
|
|
543
610
|
process.exit(1);
|
|
544
611
|
}
|
|
545
612
|
if (!(await confirmForTwoFactor("Confirm ownership to regenerate backup codes:"))) {
|
|
@@ -554,14 +621,14 @@ async function cmd2fa(args: string[]) {
|
|
|
554
621
|
}
|
|
555
622
|
|
|
556
623
|
console.error(`Unknown 2fa command: ${sub}`);
|
|
557
|
-
console.error("Usage: parachute
|
|
624
|
+
console.error("Usage: parachute-vault 2fa [status | enroll | disable | backup-codes]");
|
|
558
625
|
process.exit(1);
|
|
559
626
|
}
|
|
560
627
|
|
|
561
628
|
function cmdCreate(args: string[]) {
|
|
562
629
|
const name = args[0];
|
|
563
630
|
if (!name) {
|
|
564
|
-
console.error("Usage: parachute
|
|
631
|
+
console.error("Usage: parachute-vault create <name>");
|
|
565
632
|
process.exit(1);
|
|
566
633
|
}
|
|
567
634
|
|
|
@@ -570,9 +637,9 @@ function cmdCreate(args: string[]) {
|
|
|
570
637
|
process.exit(1);
|
|
571
638
|
}
|
|
572
639
|
if (name === "list") {
|
|
573
|
-
// Reserved —
|
|
574
|
-
//
|
|
575
|
-
//
|
|
640
|
+
// Reserved — keeps the "list" vault name out of play even though per-vault
|
|
641
|
+
// routes now live under /vault/<name>/ and no longer collide with the
|
|
642
|
+
// /vaults/list discovery endpoint.
|
|
576
643
|
console.error(`"list" is a reserved vault name.`);
|
|
577
644
|
process.exit(1);
|
|
578
645
|
}
|
|
@@ -610,13 +677,13 @@ function cmdCreate(args: string[]) {
|
|
|
610
677
|
console.log(` ${defaultNote}`);
|
|
611
678
|
}
|
|
612
679
|
console.log();
|
|
613
|
-
console.log(`To add MCP to Claude: parachute
|
|
680
|
+
console.log(`To add MCP to Claude: parachute-vault mcp-install ${name}`);
|
|
614
681
|
}
|
|
615
682
|
|
|
616
683
|
function cmdList() {
|
|
617
684
|
const vaults = listVaults();
|
|
618
685
|
if (vaults.length === 0) {
|
|
619
|
-
console.log("No vaults. Run: parachute
|
|
686
|
+
console.log("No vaults. Run: parachute-vault init");
|
|
620
687
|
return;
|
|
621
688
|
}
|
|
622
689
|
|
|
@@ -637,7 +704,7 @@ function cmdMcpInstall(_args: string[]) {
|
|
|
637
704
|
function cmdRemove(args: string[]) {
|
|
638
705
|
const name = args[0];
|
|
639
706
|
if (!name) {
|
|
640
|
-
console.error("Usage: parachute
|
|
707
|
+
console.error("Usage: parachute-vault remove <name>");
|
|
641
708
|
process.exit(1);
|
|
642
709
|
}
|
|
643
710
|
|
|
@@ -651,7 +718,7 @@ function cmdRemove(args: string[]) {
|
|
|
651
718
|
if (!force) {
|
|
652
719
|
console.log(`This will permanently delete vault "${name}" and all its data.`);
|
|
653
720
|
console.log(` Path: ${vaultDir(name)}`);
|
|
654
|
-
console.log(`\nTo confirm: parachute
|
|
721
|
+
console.log(`\nTo confirm: parachute-vault remove ${name} --yes`);
|
|
655
722
|
return;
|
|
656
723
|
}
|
|
657
724
|
|
|
@@ -680,7 +747,7 @@ function cmdRemove(args: string[]) {
|
|
|
680
747
|
async function cmdConfig(args: string[]) {
|
|
681
748
|
const subcmd = args[0];
|
|
682
749
|
|
|
683
|
-
// parachute
|
|
750
|
+
// parachute-vault config — show current config
|
|
684
751
|
if (!subcmd) {
|
|
685
752
|
loadEnvFile();
|
|
686
753
|
const env = readEnvFile();
|
|
@@ -693,7 +760,7 @@ async function cmdConfig(args: string[]) {
|
|
|
693
760
|
console.log();
|
|
694
761
|
|
|
695
762
|
if (Object.keys(env).length === 0) {
|
|
696
|
-
console.log(" No env vars set. Use: parachute
|
|
763
|
+
console.log(" No env vars set. Use: parachute-vault config set <key> <value>");
|
|
697
764
|
} else {
|
|
698
765
|
for (const [key, val] of Object.entries(env)) {
|
|
699
766
|
// Mask sensitive values
|
|
@@ -707,46 +774,46 @@ async function cmdConfig(args: string[]) {
|
|
|
707
774
|
return;
|
|
708
775
|
}
|
|
709
776
|
|
|
710
|
-
// parachute
|
|
777
|
+
// parachute-vault config set <key> <value>
|
|
711
778
|
if (subcmd === "set") {
|
|
712
779
|
const key = args[1];
|
|
713
780
|
const value = args.slice(2).join(" ");
|
|
714
781
|
if (!key || !value) {
|
|
715
|
-
console.error("Usage: parachute
|
|
782
|
+
console.error("Usage: parachute-vault config set <key> <value>");
|
|
716
783
|
process.exit(1);
|
|
717
784
|
}
|
|
718
785
|
setEnvVar(key, value);
|
|
719
786
|
console.log(`Set ${key}=${key.includes("KEY") ? value.slice(0, 8) + "..." : value}`);
|
|
720
|
-
console.log("Restart the daemon to apply: parachute
|
|
787
|
+
console.log("Restart the daemon to apply: parachute-vault restart");
|
|
721
788
|
return;
|
|
722
789
|
}
|
|
723
790
|
|
|
724
|
-
// parachute
|
|
791
|
+
// parachute-vault config unset <key>
|
|
725
792
|
if (subcmd === "unset") {
|
|
726
793
|
const key = args[1];
|
|
727
794
|
if (!key) {
|
|
728
|
-
console.error("Usage: parachute
|
|
795
|
+
console.error("Usage: parachute-vault config unset <key>");
|
|
729
796
|
process.exit(1);
|
|
730
797
|
}
|
|
731
798
|
unsetEnvVar(key);
|
|
732
799
|
console.log(`Removed ${key}`);
|
|
733
|
-
console.log("Restart the daemon to apply: parachute
|
|
800
|
+
console.log("Restart the daemon to apply: parachute-vault restart");
|
|
734
801
|
return;
|
|
735
802
|
}
|
|
736
803
|
|
|
737
804
|
console.error(`Unknown config command: ${subcmd}`);
|
|
738
|
-
console.error("Usage: parachute
|
|
805
|
+
console.error("Usage: parachute-vault config [set <key> <value> | unset <key>]");
|
|
739
806
|
process.exit(1);
|
|
740
807
|
}
|
|
741
808
|
|
|
742
809
|
// ---------------------------------------------------------------------------
|
|
743
|
-
// Tokens — parachute
|
|
810
|
+
// Tokens — parachute-vault tokens [create | list | revoke]
|
|
744
811
|
// ---------------------------------------------------------------------------
|
|
745
812
|
|
|
746
813
|
function cmdTokens(args: string[]) {
|
|
747
814
|
const subcmd = args[0];
|
|
748
815
|
|
|
749
|
-
// parachute
|
|
816
|
+
// parachute-vault tokens — list all tokens (across all vaults)
|
|
750
817
|
if (!subcmd || subcmd === "list") {
|
|
751
818
|
const vaults = listVaults();
|
|
752
819
|
let anyTokens = false;
|
|
@@ -773,12 +840,13 @@ function cmdTokens(args: string[]) {
|
|
|
773
840
|
}
|
|
774
841
|
|
|
775
842
|
if (!anyTokens) {
|
|
776
|
-
console.log("No tokens found. Create one: parachute
|
|
843
|
+
console.log("No tokens found. Create one: parachute-vault tokens create");
|
|
777
844
|
}
|
|
778
845
|
return;
|
|
779
846
|
}
|
|
780
847
|
|
|
781
|
-
// parachute
|
|
848
|
+
// parachute-vault tokens create --vault <name>
|
|
849
|
+
// [--scope vault:read,vault:write | --read | --permission full|read]
|
|
782
850
|
// [--expires <duration>] [--label <label>]
|
|
783
851
|
if (subcmd === "create") {
|
|
784
852
|
const vaultFlag = args.indexOf("--vault");
|
|
@@ -790,15 +858,17 @@ function cmdTokens(args: string[]) {
|
|
|
790
858
|
process.exit(1);
|
|
791
859
|
}
|
|
792
860
|
|
|
793
|
-
// --read
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
const
|
|
798
|
-
if (
|
|
799
|
-
console.error(
|
|
861
|
+
// Combining --scope / --read / --permission is always an error: a
|
|
862
|
+
// user minting a token expects exactly one narrowing signal, and
|
|
863
|
+
// silently picking one would mint the opposite of what the other
|
|
864
|
+
// reading intended. See resolveCreateTokenFlags.
|
|
865
|
+
const resolved = resolveCreateTokenFlags(args);
|
|
866
|
+
if (resolved.error) {
|
|
867
|
+
console.error(resolved.error);
|
|
800
868
|
process.exit(1);
|
|
801
869
|
}
|
|
870
|
+
const scopes = resolved.scopes;
|
|
871
|
+
const permission: TokenPermission = resolved.permission;
|
|
802
872
|
|
|
803
873
|
const expiresFlag = args.indexOf("--expires");
|
|
804
874
|
let expiresAt: string | null = null;
|
|
@@ -819,12 +889,15 @@ function cmdTokens(args: string[]) {
|
|
|
819
889
|
createToken(store.db, fullToken, {
|
|
820
890
|
label,
|
|
821
891
|
permission,
|
|
892
|
+
scopes,
|
|
822
893
|
expires_at: expiresAt,
|
|
823
894
|
});
|
|
824
895
|
|
|
896
|
+
const displayScopes = scopes ?? [...VAULT_SCOPES];
|
|
825
897
|
console.log(`Created token for vault "${vaultName}":`);
|
|
826
898
|
console.log(` Token: ${fullToken}`);
|
|
827
899
|
console.log(` Permission: ${permission}`);
|
|
900
|
+
console.log(` Scopes: ${displayScopes.join(" ")}`);
|
|
828
901
|
if (expiresAt) console.log(` Expires: ${expiresAt}`);
|
|
829
902
|
console.log(` Label: ${label}`);
|
|
830
903
|
console.log();
|
|
@@ -832,11 +905,11 @@ function cmdTokens(args: string[]) {
|
|
|
832
905
|
return;
|
|
833
906
|
}
|
|
834
907
|
|
|
835
|
-
// parachute
|
|
908
|
+
// parachute-vault tokens revoke <token-id> --vault <name>
|
|
836
909
|
if (subcmd === "revoke") {
|
|
837
910
|
const tokenId = args[1];
|
|
838
911
|
if (!tokenId) {
|
|
839
|
-
console.error("Usage: parachute
|
|
912
|
+
console.error("Usage: parachute-vault tokens revoke <token-id> --vault <name>");
|
|
840
913
|
process.exit(1);
|
|
841
914
|
}
|
|
842
915
|
|
|
@@ -860,7 +933,7 @@ function cmdTokens(args: string[]) {
|
|
|
860
933
|
}
|
|
861
934
|
|
|
862
935
|
console.error(`Unknown tokens command: ${subcmd}`);
|
|
863
|
-
console.error("Usage: parachute
|
|
936
|
+
console.error("Usage: parachute-vault tokens [create | list | revoke <id>]");
|
|
864
937
|
process.exit(1);
|
|
865
938
|
}
|
|
866
939
|
|
|
@@ -1029,7 +1102,7 @@ async function cmdUninstall(argsList: string[]) {
|
|
|
1029
1102
|
if (wipe) {
|
|
1030
1103
|
console.log("`--wipe` will ALSO remove vaults, .env, config.yaml, and daemon logs.\n");
|
|
1031
1104
|
} else {
|
|
1032
|
-
console.log("User data (~/.parachute/
|
|
1105
|
+
console.log("User data (~/.parachute/vault/) is left alone.\n");
|
|
1033
1106
|
}
|
|
1034
1107
|
|
|
1035
1108
|
// Scripted `--yes --wipe` bypasses both interactive confirms. That's the
|
|
@@ -1039,7 +1112,7 @@ async function cmdUninstall(argsList: string[]) {
|
|
|
1039
1112
|
// miss this; interactive users already see the prompts.
|
|
1040
1113
|
if (skipPrompts && wipe) {
|
|
1041
1114
|
const ts = new Date().toISOString();
|
|
1042
|
-
const targets = [
|
|
1115
|
+
const targets = [DATA_DIR, ENV_PATH, GLOBAL_CONFIG_PATH, LOG_PATH, ERR_PATH].join(", ");
|
|
1043
1116
|
console.log(`[${ts}] scripted destructive wipe: ${targets}`);
|
|
1044
1117
|
}
|
|
1045
1118
|
|
|
@@ -1083,7 +1156,7 @@ async function cmdUninstall(argsList: string[]) {
|
|
|
1083
1156
|
// Inventory what's actually on disk. Paths that don't exist are a
|
|
1084
1157
|
// silent no-op on removal, but we also skip listing them so the
|
|
1085
1158
|
// "would be removed" summary doesn't lie to the user.
|
|
1086
|
-
const vaultsExist = existsSync(
|
|
1159
|
+
const vaultsExist = existsSync(DATA_DIR);
|
|
1087
1160
|
const envExists = existsSync(ENV_PATH);
|
|
1088
1161
|
const configExists = existsSync(GLOBAL_CONFIG_PATH);
|
|
1089
1162
|
const logExists = existsSync(LOG_PATH);
|
|
@@ -1094,7 +1167,7 @@ async function cmdUninstall(argsList: string[]) {
|
|
|
1094
1167
|
console.log("No user data to remove.");
|
|
1095
1168
|
} else {
|
|
1096
1169
|
console.log("\nUser data that would be removed:");
|
|
1097
|
-
if (vaultsExist) console.log(` ${
|
|
1170
|
+
if (vaultsExist) console.log(` ${DATA_DIR} (per-vault SQLite data)`);
|
|
1098
1171
|
if (envExists) console.log(` ${ENV_PATH} (.env config + secrets)`);
|
|
1099
1172
|
if (configExists) console.log(` ${GLOBAL_CONFIG_PATH} (global config)`);
|
|
1100
1173
|
if (logExists) console.log(` ${LOG_PATH} (daemon log)`);
|
|
@@ -1108,7 +1181,7 @@ async function cmdUninstall(argsList: string[]) {
|
|
|
1108
1181
|
doWipe = await confirm("Delete this data? (cannot be undone)", false);
|
|
1109
1182
|
}
|
|
1110
1183
|
if (doWipe) {
|
|
1111
|
-
if (vaultsExist) rmSync(
|
|
1184
|
+
if (vaultsExist) rmSync(DATA_DIR, { recursive: true, force: true });
|
|
1112
1185
|
if (envExists) rmSync(ENV_PATH, { force: true });
|
|
1113
1186
|
if (configExists) rmSync(GLOBAL_CONFIG_PATH, { force: true });
|
|
1114
1187
|
if (logExists) rmSync(LOG_PATH, { force: true });
|
|
@@ -1120,7 +1193,7 @@ async function cmdUninstall(argsList: string[]) {
|
|
|
1120
1193
|
}
|
|
1121
1194
|
}
|
|
1122
1195
|
|
|
1123
|
-
console.log("\nDone. To reinstall: `parachute
|
|
1196
|
+
console.log("\nDone. To reinstall: `parachute-vault init`.");
|
|
1124
1197
|
}
|
|
1125
1198
|
|
|
1126
1199
|
interface DoctorCheck {
|
|
@@ -1139,7 +1212,7 @@ async function cmdDoctor() {
|
|
|
1139
1212
|
name: "server-path pointer",
|
|
1140
1213
|
status: "fail",
|
|
1141
1214
|
detail: `missing: ${SERVER_PATH_FILE}`,
|
|
1142
|
-
fix: "Run `parachute
|
|
1215
|
+
fix: "Run `parachute-vault init` to create it.",
|
|
1143
1216
|
});
|
|
1144
1217
|
} else {
|
|
1145
1218
|
const pointed = readServerPathPointer();
|
|
@@ -1148,14 +1221,14 @@ async function cmdDoctor() {
|
|
|
1148
1221
|
name: "server-path pointer",
|
|
1149
1222
|
status: "fail",
|
|
1150
1223
|
detail: `empty: ${SERVER_PATH_FILE}`,
|
|
1151
|
-
fix: "Run `parachute
|
|
1224
|
+
fix: "Run `parachute-vault init` to rewrite it.",
|
|
1152
1225
|
});
|
|
1153
1226
|
} else if (!existsSync(pointed)) {
|
|
1154
1227
|
checks.push({
|
|
1155
1228
|
name: "server.ts at pointer target",
|
|
1156
1229
|
status: "fail",
|
|
1157
1230
|
detail: `points to ${pointed}, which does not exist`,
|
|
1158
|
-
fix: "Run `parachute
|
|
1231
|
+
fix: "Run `parachute-vault init` from the current repo location.",
|
|
1159
1232
|
});
|
|
1160
1233
|
} else {
|
|
1161
1234
|
checks.push({
|
|
@@ -1173,7 +1246,7 @@ async function cmdDoctor() {
|
|
|
1173
1246
|
name: "wrapper script",
|
|
1174
1247
|
status: "fail",
|
|
1175
1248
|
detail: `missing: ${WRAPPER_PATH}`,
|
|
1176
|
-
fix: "Run `parachute
|
|
1249
|
+
fix: "Run `parachute-vault init`.",
|
|
1177
1250
|
});
|
|
1178
1251
|
} else {
|
|
1179
1252
|
checks.push({ name: "wrapper script", status: "pass", detail: WRAPPER_PATH });
|
|
@@ -1186,7 +1259,7 @@ async function cmdDoctor() {
|
|
|
1186
1259
|
name: "launchd agent",
|
|
1187
1260
|
status: loaded ? "pass" : "warn",
|
|
1188
1261
|
detail: loaded ? "loaded" : "not loaded",
|
|
1189
|
-
fix: loaded ? undefined : "Run `parachute
|
|
1262
|
+
fix: loaded ? undefined : "Run `parachute-vault init` or `parachute-vault restart`.",
|
|
1190
1263
|
});
|
|
1191
1264
|
} else if (isSystemdAvailable()) {
|
|
1192
1265
|
const active = await isServiceActive();
|
|
@@ -1194,7 +1267,7 @@ async function cmdDoctor() {
|
|
|
1194
1267
|
name: "systemd service",
|
|
1195
1268
|
status: active ? "pass" : "warn",
|
|
1196
1269
|
detail: active ? "active" : "not active",
|
|
1197
|
-
fix: active ? undefined : "Run `parachute
|
|
1270
|
+
fix: active ? undefined : "Run `parachute-vault init` or `parachute-vault restart`.",
|
|
1198
1271
|
});
|
|
1199
1272
|
}
|
|
1200
1273
|
|
|
@@ -1225,7 +1298,7 @@ async function cmdDoctor() {
|
|
|
1225
1298
|
name: "MCP entry in ~/.claude.json",
|
|
1226
1299
|
status: "warn",
|
|
1227
1300
|
detail: mcpEntry.reason,
|
|
1228
|
-
fix: "Run `parachute
|
|
1301
|
+
fix: "Run `parachute-vault mcp-install` to register the vault with Claude.",
|
|
1229
1302
|
});
|
|
1230
1303
|
} else {
|
|
1231
1304
|
checks.push({
|
|
@@ -1248,7 +1321,7 @@ async function cmdDoctor() {
|
|
|
1248
1321
|
name: "MCP URL port matches vault",
|
|
1249
1322
|
status: "warn",
|
|
1250
1323
|
detail: `MCP URL port ${mcpEntry.port ?? "(unparseable)"} ≠ vault port ${port}`,
|
|
1251
|
-
fix: "Re-run `parachute
|
|
1324
|
+
fix: "Re-run `parachute-vault mcp-install` to refresh the MCP URL.",
|
|
1252
1325
|
});
|
|
1253
1326
|
}
|
|
1254
1327
|
|
|
@@ -1268,7 +1341,7 @@ async function cmdDoctor() {
|
|
|
1268
1341
|
name: "MCP URL reachable",
|
|
1269
1342
|
status: "warn",
|
|
1270
1343
|
detail: reach.detail,
|
|
1271
|
-
fix: "Start the daemon: `parachute
|
|
1344
|
+
fix: "Start the daemon: `parachute-vault restart` (or `init` if not yet installed).",
|
|
1272
1345
|
});
|
|
1273
1346
|
}
|
|
1274
1347
|
}
|
|
@@ -1297,7 +1370,7 @@ async function cmdDoctor() {
|
|
|
1297
1370
|
name: `port ${port} availability`,
|
|
1298
1371
|
status: "warn",
|
|
1299
1372
|
detail: `port in use by non-vault process: ${collision.detail}`,
|
|
1300
|
-
fix: "Stop the conflicting process, or set a different PORT in ~/.parachute/.env and re-run `parachute
|
|
1373
|
+
fix: "Stop the conflicting process, or set a different PORT in ~/.parachute/vault/.env and re-run `parachute-vault init`.",
|
|
1301
1374
|
});
|
|
1302
1375
|
break;
|
|
1303
1376
|
case "unknown":
|
|
@@ -1319,7 +1392,7 @@ async function cmdDoctor() {
|
|
|
1319
1392
|
name: "backup agent",
|
|
1320
1393
|
status: loaded ? "pass" : "warn",
|
|
1321
1394
|
detail: loaded ? `loaded (schedule: ${backupCfg.schedule})` : `not loaded (schedule: ${backupCfg.schedule})`,
|
|
1322
|
-
fix: loaded ? undefined : `Re-run \`parachute
|
|
1395
|
+
fix: loaded ? undefined : `Re-run \`parachute-vault backup --schedule ${backupCfg.schedule}\` to reinstall the agent.`,
|
|
1323
1396
|
});
|
|
1324
1397
|
}
|
|
1325
1398
|
|
|
@@ -1332,7 +1405,7 @@ async function cmdDoctor() {
|
|
|
1332
1405
|
name: "backup destinations",
|
|
1333
1406
|
status: "warn",
|
|
1334
1407
|
detail: "schedule is active but no destinations configured",
|
|
1335
|
-
fix: "Edit ~/.parachute/config.yaml and add at least one destination under `backup.destinations`.",
|
|
1408
|
+
fix: "Edit ~/.parachute/vault/config.yaml and add at least one destination under `backup.destinations`.",
|
|
1336
1409
|
});
|
|
1337
1410
|
} else {
|
|
1338
1411
|
for (const dest of backupCfg.destinations) {
|
|
@@ -1341,7 +1414,7 @@ async function cmdDoctor() {
|
|
|
1341
1414
|
name: `backup destination (${dest.kind})`,
|
|
1342
1415
|
status: res.ok ? "pass" : "warn",
|
|
1343
1416
|
detail: res.ok ? res.path : `${res.path}: ${res.error}`,
|
|
1344
|
-
fix: res.ok ? undefined : "Ensure the path exists and is writable, or update it in ~/.parachute/config.yaml.",
|
|
1417
|
+
fix: res.ok ? undefined : "Ensure the path exists and is writable, or update it in ~/.parachute/vault/config.yaml.",
|
|
1345
1418
|
});
|
|
1346
1419
|
}
|
|
1347
1420
|
}
|
|
@@ -1361,12 +1434,12 @@ async function cmdDoctor() {
|
|
|
1361
1434
|
const hasWarn = checks.some((c) => c.status === "warn");
|
|
1362
1435
|
console.log();
|
|
1363
1436
|
if (hasFailure) {
|
|
1364
|
-
console.log("doctor: problems found (exit 1). See `parachute
|
|
1437
|
+
console.log("doctor: problems found (exit 1). See `parachute-vault status` for runtime details.");
|
|
1365
1438
|
process.exit(1);
|
|
1366
1439
|
} else if (hasWarn) {
|
|
1367
|
-
console.log("doctor: warnings only. `parachute
|
|
1440
|
+
console.log("doctor: warnings only. `parachute-vault status` has live runtime detail.");
|
|
1368
1441
|
} else {
|
|
1369
|
-
console.log("doctor: all checks passed. For live runtime state: `parachute
|
|
1442
|
+
console.log("doctor: all checks passed. For live runtime state: `parachute-vault status`.");
|
|
1370
1443
|
}
|
|
1371
1444
|
}
|
|
1372
1445
|
|
|
@@ -1377,7 +1450,7 @@ function cmdUrl() {
|
|
|
1377
1450
|
|
|
1378
1451
|
/**
|
|
1379
1452
|
* Resolve the vault's port the way `status`, `restart`, `url`, and `doctor`
|
|
1380
|
-
* all need to agree on: env override (~/.parachute/.env) wins, then
|
|
1453
|
+
* all need to agree on: env override (~/.parachute/vault/.env) wins, then
|
|
1381
1454
|
* config.yaml, then DEFAULT_PORT. Sources .env as a side effect so callers
|
|
1382
1455
|
* running this before any env read still see PORT.
|
|
1383
1456
|
*/
|
|
@@ -1398,7 +1471,7 @@ type McpEntryLookup =
|
|
|
1398
1471
|
/**
|
|
1399
1472
|
* Read `~/.claude.json` and return the shape of the `parachute-vault` MCP
|
|
1400
1473
|
* entry if present. The entry is always an HTTP MCP pointing at the local
|
|
1401
|
-
* daemon — `{ type: "http", url: "http://127.0.0.1:<port>/
|
|
1474
|
+
* daemon — `{ type: "http", url: "http://127.0.0.1:<port>/vault/<name>/mcp" }`
|
|
1402
1475
|
* — so we parse the URL's port for the port-match check.
|
|
1403
1476
|
*
|
|
1404
1477
|
* Invariant: the check is NON-fatal. A missing ~/.claude.json is a warn,
|
|
@@ -1573,7 +1646,7 @@ async function describeProcess(pid: number): Promise<string | null> {
|
|
|
1573
1646
|
}
|
|
1574
1647
|
|
|
1575
1648
|
// ---------------------------------------------------------------------------
|
|
1576
|
-
// Backup — parachute
|
|
1649
|
+
// Backup — parachute-vault backup [--schedule <freq> | status]
|
|
1577
1650
|
// ---------------------------------------------------------------------------
|
|
1578
1651
|
|
|
1579
1652
|
async function cmdBackup(args: string[]) {
|
|
@@ -1588,7 +1661,7 @@ async function cmdBackup(args: string[]) {
|
|
|
1588
1661
|
if (schedFlag !== -1) {
|
|
1589
1662
|
const raw = args[schedFlag + 1];
|
|
1590
1663
|
if (!raw) {
|
|
1591
|
-
console.error("Usage: parachute
|
|
1664
|
+
console.error("Usage: parachute-vault backup --schedule <hourly|daily|weekly|manual>");
|
|
1592
1665
|
process.exit(1);
|
|
1593
1666
|
}
|
|
1594
1667
|
if (raw !== "hourly" && raw !== "daily" && raw !== "weekly" && raw !== "manual") {
|
|
@@ -1606,7 +1679,7 @@ async function cmdBackup(args: string[]) {
|
|
|
1606
1679
|
async function cmdBackupRun() {
|
|
1607
1680
|
const cfg = readGlobalConfig().backup ?? defaultBackupConfig();
|
|
1608
1681
|
if (cfg.destinations.length === 0) {
|
|
1609
|
-
console.error("No backup destinations configured. Edit ~/.parachute/config.yaml:");
|
|
1682
|
+
console.error("No backup destinations configured. Edit ~/.parachute/vault/config.yaml:");
|
|
1610
1683
|
console.error(" backup:");
|
|
1611
1684
|
console.error(" destinations:");
|
|
1612
1685
|
console.error(" - kind: local");
|
|
@@ -1648,7 +1721,7 @@ async function cmdBackupSchedule(schedule: BackupSchedule) {
|
|
|
1648
1721
|
if (schedule === "manual") {
|
|
1649
1722
|
await uninstallBackupAgent();
|
|
1650
1723
|
console.log("Schedule: manual — backup agent removed.");
|
|
1651
|
-
console.log("Run `parachute
|
|
1724
|
+
console.log("Run `parachute-vault backup` to trigger a backup on demand.");
|
|
1652
1725
|
return;
|
|
1653
1726
|
}
|
|
1654
1727
|
|
|
@@ -1656,7 +1729,7 @@ async function cmdBackupSchedule(schedule: BackupSchedule) {
|
|
|
1656
1729
|
console.log(`Schedule set to: ${schedule}`);
|
|
1657
1730
|
console.log();
|
|
1658
1731
|
console.log("WARNING: no destinations configured — scheduled runs will fail.");
|
|
1659
|
-
console.log("Edit ~/.parachute/config.yaml and add at least one destination under `backup.destinations`.");
|
|
1732
|
+
console.log("Edit ~/.parachute/vault/config.yaml and add at least one destination under `backup.destinations`.");
|
|
1660
1733
|
}
|
|
1661
1734
|
|
|
1662
1735
|
await installBackupAgent(schedule);
|
|
@@ -1759,7 +1832,7 @@ async function cmdImport(args: string[]) {
|
|
|
1759
1832
|
sourcePath = positional[0] ?? "";
|
|
1760
1833
|
|
|
1761
1834
|
if (!sourcePath) {
|
|
1762
|
-
console.error("Usage: parachute
|
|
1835
|
+
console.error("Usage: parachute-vault import <path> [--vault <name>] [--dry-run]");
|
|
1763
1836
|
console.error("\nImports an Obsidian vault into Parachute Vault.");
|
|
1764
1837
|
console.error("\nOptions:");
|
|
1765
1838
|
console.error(" --vault <name> Target vault (default: 'default')");
|
|
@@ -1778,7 +1851,7 @@ async function cmdImport(args: string[]) {
|
|
|
1778
1851
|
// Verify vault exists
|
|
1779
1852
|
const config = readVaultConfig(vaultName);
|
|
1780
1853
|
if (!config) {
|
|
1781
|
-
console.error(`Vault "${vaultName}" not found. Run: parachute
|
|
1854
|
+
console.error(`Vault "${vaultName}" not found. Run: parachute-vault create ${vaultName}`);
|
|
1782
1855
|
process.exit(1);
|
|
1783
1856
|
}
|
|
1784
1857
|
|
|
@@ -1865,7 +1938,7 @@ async function cmdExport(args: string[]) {
|
|
|
1865
1938
|
outputPath = positional[0] ?? "";
|
|
1866
1939
|
|
|
1867
1940
|
if (!outputPath) {
|
|
1868
|
-
console.error("Usage: parachute
|
|
1941
|
+
console.error("Usage: parachute-vault export <output-path> [--vault <name>]");
|
|
1869
1942
|
console.error("\nExports a Parachute Vault as Obsidian-compatible markdown files.");
|
|
1870
1943
|
process.exit(1);
|
|
1871
1944
|
}
|
|
@@ -1945,12 +2018,16 @@ function installMcpConfig(apiKey?: string) {
|
|
|
1945
2018
|
}
|
|
1946
2019
|
}
|
|
1947
2020
|
|
|
1948
|
-
// Single HTTP MCP entry — use per-vault endpoint so pvt_ tokens work
|
|
2021
|
+
// Single HTTP MCP entry — use per-vault endpoint so pvt_ tokens work.
|
|
2022
|
+
// Pick the URL that matches the OAuth issuer vault will advertise, in this
|
|
2023
|
+
// order: explicit hub origin env > active tailnet/public exposure >
|
|
2024
|
+
// loopback. Otherwise a strict MCP client (Claude Code) hits a loopback URL
|
|
2025
|
+
// whose discovery issuer points at the hub and rejects on origin mismatch
|
|
2026
|
+
// (RFC 8414).
|
|
1949
2027
|
const defaultVault = globalConfig.default_vault || "default";
|
|
1950
|
-
const
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
};
|
|
2028
|
+
const { url: mcpUrl, source } = chooseMcpUrl(defaultVault, port);
|
|
2029
|
+
console.log(`MCP URL: ${mcpUrl} (${source})`);
|
|
2030
|
+
const mcpEntry: Record<string, unknown> = { type: "http", url: mcpUrl };
|
|
1954
2031
|
if (apiKey) {
|
|
1955
2032
|
mcpEntry.headers = { Authorization: `Bearer ${apiKey}` };
|
|
1956
2033
|
}
|
|
@@ -1979,58 +2056,76 @@ function usage() {
|
|
|
1979
2056
|
console.log(`
|
|
1980
2057
|
Parachute Vault — self-hosted knowledge graph
|
|
1981
2058
|
|
|
2059
|
+
If you installed via the Parachute CLI, prefer the wrapper commands for
|
|
2060
|
+
lifecycle — \`parachute start vault\`, \`parachute stop vault\`,
|
|
2061
|
+
\`parachute status\` — and use the vault-direct commands below for setup,
|
|
2062
|
+
data, and debugging.
|
|
2063
|
+
|
|
2064
|
+
── Standard use ───────────────────────────────────────────────────────
|
|
2065
|
+
|
|
1982
2066
|
Setup:
|
|
1983
|
-
parachute
|
|
1984
|
-
parachute
|
|
1985
|
-
parachute
|
|
1986
|
-
parachute vault uninstall [--wipe] [--yes]
|
|
2067
|
+
parachute-vault init [--mcp | --no-mcp] Set up everything (one command, idempotent)
|
|
2068
|
+
parachute-vault doctor Diagnose install/config issues
|
|
2069
|
+
parachute-vault uninstall [--wipe] [--yes]
|
|
1987
2070
|
Remove daemon + MCP entry; --wipe also removes vaults, .env,
|
|
1988
2071
|
config.yaml, and daemon logs (vault.log, vault.err).
|
|
1989
2072
|
--yes skips prompts (DANGEROUS with --wipe: no confirmation).
|
|
1990
|
-
parachute
|
|
2073
|
+
parachute-vault url Print the local server URL (for scripts)
|
|
1991
2074
|
parachute --version Print the installed version (alias: -v, version)
|
|
1992
2075
|
|
|
1993
2076
|
Vaults:
|
|
1994
|
-
parachute
|
|
1995
|
-
parachute
|
|
1996
|
-
parachute
|
|
1997
|
-
parachute
|
|
2077
|
+
parachute-vault create <name> Create a new vault
|
|
2078
|
+
parachute-vault list List all vaults
|
|
2079
|
+
parachute-vault remove <name> [--yes] Remove a vault
|
|
2080
|
+
parachute-vault mcp-install Add vault MCP to Claude
|
|
1998
2081
|
|
|
1999
2082
|
Tokens:
|
|
2000
|
-
parachute
|
|
2001
|
-
parachute
|
|
2002
|
-
parachute
|
|
2003
|
-
parachute
|
|
2004
|
-
parachute
|
|
2005
|
-
|
|
2006
|
-
|
|
2083
|
+
parachute-vault tokens List all tokens
|
|
2084
|
+
parachute-vault tokens create Create a full-access token in the default vault
|
|
2085
|
+
parachute-vault tokens create --vault <name> Create a token in a specific vault
|
|
2086
|
+
parachute-vault tokens create --read Read-only token (shorthand for --scope vault:read)
|
|
2087
|
+
parachute-vault tokens create --scope vault:write
|
|
2088
|
+
Narrow the token's scopes. Accepts a comma-separated
|
|
2089
|
+
list or repeated --scope flags. Valid scopes:
|
|
2090
|
+
vault:read, vault:write, vault:admin.
|
|
2091
|
+
parachute-vault tokens create --label x Set a label
|
|
2092
|
+
parachute-vault tokens create --expires 30d Expiring token
|
|
2093
|
+
parachute-vault tokens revoke <token-id> Revoke a token (default vault)
|
|
2007
2094
|
|
|
2008
2095
|
OAuth:
|
|
2009
|
-
parachute
|
|
2010
|
-
parachute
|
|
2011
|
-
parachute
|
|
2012
|
-
parachute
|
|
2013
|
-
parachute
|
|
2014
|
-
parachute
|
|
2096
|
+
parachute-vault set-password Set/change the owner password (for consent page)
|
|
2097
|
+
parachute-vault set-password --clear Remove the owner password
|
|
2098
|
+
parachute-vault 2fa status Show 2FA state
|
|
2099
|
+
parachute-vault 2fa enroll Enable TOTP 2FA (QR + backup codes)
|
|
2100
|
+
parachute-vault 2fa disable Disable 2FA (requires password)
|
|
2101
|
+
parachute-vault 2fa backup-codes Regenerate backup codes
|
|
2015
2102
|
|
|
2016
2103
|
Config:
|
|
2017
|
-
parachute
|
|
2018
|
-
parachute
|
|
2019
|
-
parachute
|
|
2104
|
+
parachute-vault config Show current configuration
|
|
2105
|
+
parachute-vault config set <key> <val> Set a config value
|
|
2106
|
+
parachute-vault config unset <key> Remove a config value
|
|
2020
2107
|
|
|
2021
2108
|
Backup:
|
|
2022
|
-
parachute
|
|
2023
|
-
parachute
|
|
2024
|
-
parachute
|
|
2109
|
+
parachute-vault backup One-shot backup to configured destinations
|
|
2110
|
+
parachute-vault backup --schedule <freq> hourly | daily | weekly | manual (macOS launchd)
|
|
2111
|
+
parachute-vault backup status Show schedule, last run, destinations, next run
|
|
2025
2112
|
|
|
2026
2113
|
Import/Export:
|
|
2027
|
-
parachute
|
|
2028
|
-
parachute
|
|
2029
|
-
parachute
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2114
|
+
parachute-vault import <path> Import an Obsidian vault
|
|
2115
|
+
parachute-vault import <path> --dry-run Preview import without writing
|
|
2116
|
+
parachute-vault export <path> Export vault as Obsidian markdown
|
|
2117
|
+
|
|
2118
|
+
── Advanced / standalone ──────────────────────────────────────────────
|
|
2119
|
+
|
|
2120
|
+
Direct daemon controls. For normal use, prefer the Parachute CLI wrappers
|
|
2121
|
+
— they add PID tracking, log rotation, and cross-service \`parachute status\`
|
|
2122
|
+
visibility. Use these when running vault without the CLI or when debugging.
|
|
2123
|
+
|
|
2124
|
+
parachute-vault serve Run server in the foreground (no PID tracking).
|
|
2125
|
+
Prefer \`parachute start vault\` for managed lifecycle.
|
|
2126
|
+
parachute-vault status Vault-only daemon status.
|
|
2127
|
+
Prefer \`parachute status\` for a cross-service view.
|
|
2128
|
+
parachute-vault logs Stream server logs
|
|
2129
|
+
parachute-vault restart Restart the daemon
|
|
2035
2130
|
`);
|
|
2036
2131
|
}
|