@coffre/cli 0.1.4 → 0.1.5
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 +1 -1
- package/dist/{keys-RhB7ayOY.js → keys-BCKUEb-Y.js} +174 -19
- package/dist/main.js +6 -69
- package/dist/setup-D3SXt_P-.js +3341 -0
- package/dist/templates/node/package.json +3 -3
- package/dist/templates/workers/README.md +6 -0
- package/dist/templates/workers/package.json +4 -4
- package/package.json +5 -4
- package/dist/setup-3OJc_VCA.js +0 -756
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@ The `coffre` command: secrets in your shell, and a new deployment.
|
|
|
4
4
|
|
|
5
5
|
```sh
|
|
6
6
|
npx @coffre/cli init --workers my-coffre # or --node
|
|
7
|
-
npx @coffre/cli setup # its database
|
|
7
|
+
npx @coffre/cli setup # its database, keys and, on Workers, Cloudflare
|
|
8
8
|
coffre login https://secrets.acme.example
|
|
9
9
|
coffre run market/prod -- node server.js
|
|
10
10
|
```
|
|
@@ -1,9 +1,78 @@
|
|
|
1
1
|
import { parseArgs } from "node:util";
|
|
2
2
|
import { spawn } from "node:child_process";
|
|
3
|
-
import { openSync } from "node:fs";
|
|
3
|
+
import { existsSync, mkdirSync, openSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
|
|
4
|
+
import { basename, dirname, join } from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
4
6
|
import { randomBytes } from "node:crypto";
|
|
5
7
|
import { emitKeypressEvents } from "node:readline";
|
|
6
8
|
import tty from "node:tty";
|
|
9
|
+
//#region src/init.ts
|
|
10
|
+
const KINDS = ["workers", "node"];
|
|
11
|
+
/** Where a template lives: in the package when published, else in the repository. */
|
|
12
|
+
function templateDir(kind) {
|
|
13
|
+
const packed = fileURLToPath(new URL(`./templates/${kind}/`, import.meta.url));
|
|
14
|
+
if (existsSync(packed)) return packed;
|
|
15
|
+
return fileURLToPath(new URL(`../../../examples/${kind}/`, import.meta.url));
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* A template's files, relative and sorted: everything but what its own
|
|
19
|
+
* `.gitignore` names, such as installed packages, local state and secrets.
|
|
20
|
+
* Patterns are file or directory names, `*` standing for any run of
|
|
21
|
+
* characters.
|
|
22
|
+
*/
|
|
23
|
+
function templateFiles(dir) {
|
|
24
|
+
const ignoreFile = [join(dir, ".gitignore"), join(dir, "gitignore")].find((path) => existsSync(path));
|
|
25
|
+
const ignored = (ignoreFile === void 0 ? "" : readFileSync(ignoreFile, "utf8")).split("\n").map((line) => line.trim()).filter((line) => line !== "" && !line.startsWith("#")).map((glob) => new RegExp(`^${glob.split("*").map((part) => part.replace(/[.+?^${}()|[\]\\]/g, "\\$&")).join(".*")}$`));
|
|
26
|
+
const files = [];
|
|
27
|
+
const walk = (relative) => {
|
|
28
|
+
for (const entry of readdirSync(join(dir, relative), { withFileTypes: true })) {
|
|
29
|
+
if (ignored.some((pattern) => pattern.test(entry.name))) continue;
|
|
30
|
+
const path = relative === "" ? entry.name : `${relative}/${entry.name}`;
|
|
31
|
+
if (entry.isDirectory()) walk(path);
|
|
32
|
+
else files.push(path);
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
walk("");
|
|
36
|
+
return files.sort();
|
|
37
|
+
}
|
|
38
|
+
/** The name a template file has in a project. */
|
|
39
|
+
function projectPath(templatePath) {
|
|
40
|
+
return templatePath === "gitignore" ? ".gitignore" : templatePath;
|
|
41
|
+
}
|
|
42
|
+
/** A package name from a directory's, e.g. `Acme Secrets` → `acme-secrets`. */
|
|
43
|
+
function packageName(dir) {
|
|
44
|
+
const name = basename(dir).toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^[-._]+|-+$/g, "");
|
|
45
|
+
return name === "" ? "coffre" : name;
|
|
46
|
+
}
|
|
47
|
+
/** The template's package.json, for this project and this version of coffre. */
|
|
48
|
+
function manifest(source, name, version) {
|
|
49
|
+
const pkg = JSON.parse(source);
|
|
50
|
+
pkg.name = name;
|
|
51
|
+
for (const field of ["dependencies", "devDependencies"]) {
|
|
52
|
+
const deps = pkg[field];
|
|
53
|
+
for (const dep of Object.keys(deps ?? {})) if (dep.startsWith("@coffre/")) deps[dep] = version;
|
|
54
|
+
}
|
|
55
|
+
return `${JSON.stringify(pkg, null, 2)}\n`;
|
|
56
|
+
}
|
|
57
|
+
/** Whether a directory holds nothing yet, or nothing but `.git`: a repository made first, then cloned. */
|
|
58
|
+
function isEmpty(dir) {
|
|
59
|
+
return !existsSync(dir) || readdirSync(dir).every((entry) => entry === ".git");
|
|
60
|
+
}
|
|
61
|
+
/** Write a new deployment into `target`, which must be empty (`isEmpty`) or absent. Returns the files written. */
|
|
62
|
+
function init(kind, target, version) {
|
|
63
|
+
if (!isEmpty(target)) throw new Error(`${target} is not empty; give \`coffre init\` a new directory`);
|
|
64
|
+
const source = templateDir(kind);
|
|
65
|
+
const written = [];
|
|
66
|
+
for (const file of templateFiles(source)) {
|
|
67
|
+
const path = projectPath(file);
|
|
68
|
+
const content = readFileSync(join(source, file), "utf8");
|
|
69
|
+
mkdirSync(dirname(join(target, path)), { recursive: true });
|
|
70
|
+
writeFileSync(join(target, path), path === "package.json" ? manifest(content, packageName(target), version) : content);
|
|
71
|
+
written.push(path);
|
|
72
|
+
}
|
|
73
|
+
return written;
|
|
74
|
+
}
|
|
75
|
+
//#endregion
|
|
7
76
|
//#region src/clipboard.ts
|
|
8
77
|
const CLEAR_AFTER_MS = 3e4;
|
|
9
78
|
/** The system's clipboard tool, by platform and display server; null for none known. */
|
|
@@ -224,6 +293,10 @@ function paragraph(out, text, indent = 2) {
|
|
|
224
293
|
const pad = " ".repeat(indent);
|
|
225
294
|
return wrap(text, Math.max(20, (out.columns || 80) - indent - 1)).map((line) => `${pad}${line}`).join("\n");
|
|
226
295
|
}
|
|
296
|
+
/** "a, b or c". */
|
|
297
|
+
function listed(items, last) {
|
|
298
|
+
return items.length < 2 ? items.join("") : `${items.slice(0, -1).join(", ")} ${last} ${items.at(-1)}`;
|
|
299
|
+
}
|
|
227
300
|
/** A labelled row: `label` in a column of `column`, `text` wrapped beside it with a hanging indent. */
|
|
228
301
|
function row(out, s, label, text, indent = 4, column = 10) {
|
|
229
302
|
const lines = wrap(text, Math.max(20, (out.columns || 80) - indent - column - 1));
|
|
@@ -257,25 +330,107 @@ function release(keys) {
|
|
|
257
330
|
keys.pause();
|
|
258
331
|
if (keys !== process.stdin) keys.destroy?.();
|
|
259
332
|
}
|
|
260
|
-
/**
|
|
261
|
-
|
|
333
|
+
/**
|
|
334
|
+
* Every key pressed, in raw mode, until `until` returns true, or `signal`
|
|
335
|
+
* aborts the reading. Raw mode is on when this returns: a prompt shown after
|
|
336
|
+
* it gets every key, none echoed or edited by the terminal first.
|
|
337
|
+
*/
|
|
338
|
+
function readKeys(keys, until, signal) {
|
|
262
339
|
emitKeypressEvents(keys);
|
|
263
340
|
keys.setRawMode?.(true);
|
|
264
341
|
keys.resume();
|
|
265
342
|
return new Promise((resolve, reject) => {
|
|
266
343
|
let busy = Promise.resolve();
|
|
344
|
+
const stop = () => {
|
|
345
|
+
keys.off("keypress", onKey);
|
|
346
|
+
signal?.removeEventListener("abort", stop);
|
|
347
|
+
keys.setRawMode?.(false);
|
|
348
|
+
keys.pause();
|
|
349
|
+
resolve();
|
|
350
|
+
};
|
|
267
351
|
const onKey = (sequence, key) => {
|
|
268
352
|
busy = busy.then(async () => {
|
|
269
|
-
if (await until(key ?? { sequence }, sequence ?? key?.sequence ?? ""))
|
|
270
|
-
keys.off("keypress", onKey);
|
|
271
|
-
keys.setRawMode?.(false);
|
|
272
|
-
keys.pause();
|
|
273
|
-
resolve();
|
|
274
|
-
}
|
|
353
|
+
if (await until(key ?? { sequence }, sequence ?? key?.sequence ?? "")) stop();
|
|
275
354
|
}).catch(reject);
|
|
276
355
|
};
|
|
277
356
|
keys.on("keypress", onKey);
|
|
357
|
+
if (signal?.aborted) stop();
|
|
358
|
+
else signal?.addEventListener("abort", stop);
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
/** What a typed key does to a line: its new text, or `submit`, or `cancel`. */
|
|
362
|
+
function edit(text, key, sequence) {
|
|
363
|
+
if (key.ctrl && key.name === "c") return "cancel";
|
|
364
|
+
if (key.name === "return" || key.name === "enter") return "submit";
|
|
365
|
+
if (key.name === "backspace") return text.slice(0, -1);
|
|
366
|
+
if (key.ctrl && key.name === "u") return "";
|
|
367
|
+
if (!key.ctrl && !key.meta && sequence.length > 0 && !sequence.startsWith("\x1B") && sequence >= " ") return text + sequence;
|
|
368
|
+
return text;
|
|
369
|
+
}
|
|
370
|
+
/**
|
|
371
|
+
* A question answered by typing, shown as typed, `initial` to start from;
|
|
372
|
+
* `note`, dim under it while it is asked; `check` says why an answer will
|
|
373
|
+
* not do, under it. Once given, the question becomes one line: what was
|
|
374
|
+
* asked, and the answer. Ctrl-C cancels.
|
|
375
|
+
*/
|
|
376
|
+
async function textLine(keys, out, s, question, hint, options = {}) {
|
|
377
|
+
const columns = Math.max(20, (out.columns || 80) - 1);
|
|
378
|
+
let typed = options.initial ?? "";
|
|
379
|
+
let error = null;
|
|
380
|
+
const draw = () => {
|
|
381
|
+
const below = [...error === null ? [] : [s.red(error)], ...options.note === void 0 ? [] : wrap(options.note, columns - 4).map(s.dim)].map((line) => `\n${truncate(` ${line}`, columns)}`);
|
|
382
|
+
out.write(`\r\x1b[J${below.join("")}${below.length > 0 ? `\x1b[${below.length}A` : ""}\r${truncate(` ${s.accent("›")} ${typed}`, columns)}`);
|
|
383
|
+
};
|
|
384
|
+
let cancelled = false;
|
|
385
|
+
const answered = readKeys(keys, async (key, sequence) => {
|
|
386
|
+
const next = edit(typed, key, sequence);
|
|
387
|
+
if (next === "cancel") cancelled = true;
|
|
388
|
+
else if (next === "submit") {
|
|
389
|
+
if (typed.trim() === "") return false;
|
|
390
|
+
error = await options.check?.(typed.trim()) ?? null;
|
|
391
|
+
if (error === null) return true;
|
|
392
|
+
} else {
|
|
393
|
+
typed = next;
|
|
394
|
+
error = null;
|
|
395
|
+
}
|
|
396
|
+
if (cancelled) return true;
|
|
397
|
+
draw();
|
|
398
|
+
return false;
|
|
278
399
|
});
|
|
400
|
+
out.write(`${truncate(` ${s.accent("?")} ${s.bold(question)} ${s.dim(hint)}`, columns)}\n`);
|
|
401
|
+
draw();
|
|
402
|
+
await answered;
|
|
403
|
+
out.write("\r\x1B[J\x1B[1A\x1B[2K");
|
|
404
|
+
if (cancelled) throw new Cancelled();
|
|
405
|
+
out.write(`${truncate(` ${s.green("✓")} ${s.dim(question)} ${typed.trim()}`, columns)}\n`);
|
|
406
|
+
return typed.trim();
|
|
407
|
+
}
|
|
408
|
+
/** One of `options`, chosen with the arrows and Enter; then one line, as `textLine` leaves. Ctrl-C cancels. */
|
|
409
|
+
async function select(keys, out, s, question, options) {
|
|
410
|
+
const columns = Math.max(20, (out.columns || 80) - 1);
|
|
411
|
+
let at = 0;
|
|
412
|
+
let drawn = 0;
|
|
413
|
+
const draw = () => {
|
|
414
|
+
const lines = [` ${s.accent("?")} ${s.bold(question)} ${s.dim("↑↓ then Enter")}`, ...options.map((option, i) => i === at ? ` ${s.accent("›")} ${s.accent(option)}` : ` ${option}`)];
|
|
415
|
+
out.write(`${drawn > 0 ? `\x1b[${drawn}F` : ""}${lines.map((line) => `${truncate(line, columns)}\x1b[K\n`).join("")}`);
|
|
416
|
+
drawn = lines.length;
|
|
417
|
+
};
|
|
418
|
+
let cancelled = false;
|
|
419
|
+
const chosen = readKeys(keys, (key) => {
|
|
420
|
+
if (key.ctrl && key.name === "c") cancelled = true;
|
|
421
|
+
else if (key.name === "return" || key.name === "enter") return true;
|
|
422
|
+
else if (key.name === "up" || key.name === "k") at = (at - 1 + options.length) % options.length;
|
|
423
|
+
else if (key.name === "down" || key.name === "j" || key.name === "tab") at = (at + 1) % options.length;
|
|
424
|
+
if (cancelled) return true;
|
|
425
|
+
draw();
|
|
426
|
+
return false;
|
|
427
|
+
});
|
|
428
|
+
draw();
|
|
429
|
+
await chosen;
|
|
430
|
+
out.write(`\x1b[${drawn}F\x1b[J`);
|
|
431
|
+
if (cancelled) throw new Cancelled();
|
|
432
|
+
out.write(`${truncate(` ${s.green("✓")} ${s.dim(question)} ${options[at]}`, columns)}\n`);
|
|
433
|
+
return at;
|
|
279
434
|
}
|
|
280
435
|
var Cancelled = class extends Error {
|
|
281
436
|
constructor() {
|
|
@@ -289,24 +444,24 @@ var Cancelled = class extends Error {
|
|
|
289
444
|
*/
|
|
290
445
|
async function hiddenLine(keys, out, s, question, hint) {
|
|
291
446
|
const columns = Math.max(20, (out.columns || 80) - 1);
|
|
292
|
-
out.write(`${truncate(` ${s.accent("?")} ${s.bold(question)}`, columns)}\n${truncate(` ${s.dim(hint)}`, columns)}\n`);
|
|
293
447
|
let typed = "";
|
|
294
448
|
const draw = () => {
|
|
295
449
|
const count = typed.length > 0 ? s.dim(` ${typed.length} characters`) : "";
|
|
296
450
|
out.write(`\r\x1b[2K${truncate(` ${s.accent("›")} ${"•".repeat(typed.length)}`, columns - width(count))}${count}`);
|
|
297
451
|
};
|
|
298
|
-
draw();
|
|
299
452
|
let cancelled = false;
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
else if (
|
|
304
|
-
else
|
|
305
|
-
else if (!key.ctrl && !key.meta && sequence.length > 0 && !sequence.startsWith("\x1B") && sequence >= " ") typed += sequence;
|
|
453
|
+
const typedIn = readKeys(keys, (key, sequence) => {
|
|
454
|
+
const next = edit(typed, key, sequence);
|
|
455
|
+
if (next === "cancel") cancelled = true;
|
|
456
|
+
else if (next === "submit") return true;
|
|
457
|
+
else typed = next;
|
|
306
458
|
if (cancelled) return true;
|
|
307
459
|
draw();
|
|
308
460
|
return false;
|
|
309
461
|
});
|
|
462
|
+
out.write(`${truncate(` ${s.accent("?")} ${s.bold(question)}`, columns)}\n${truncate(` ${s.dim(hint)}`, columns)}\n`);
|
|
463
|
+
draw();
|
|
464
|
+
await typedIn;
|
|
310
465
|
out.write("\r\x1B[2K\x1B[1A\x1B[2K\x1B[1A\x1B[2K");
|
|
311
466
|
if (cancelled) throw new Cancelled();
|
|
312
467
|
return typed.trim();
|
|
@@ -389,7 +544,7 @@ function render(screen, state, columns, rows, s) {
|
|
|
389
544
|
"",
|
|
390
545
|
`${title}${" ".repeat(Math.max(1, columns - width(title) - width(right) - 2))}${right}`,
|
|
391
546
|
rule,
|
|
392
|
-
...state.view === "values" ? wrap("Save each one in your password manager now. They aren't stored anywhere, and won't be shown again.", columns - 4).map((line) => ` ${line}`) : [` ${s.bold("Where these go")}`],
|
|
547
|
+
...state.view === "values" ? wrap(screen.intro ?? "Save each one in your password manager now. They aren't stored anywhere, and won't be shown again.", columns - 4).map((line) => ` ${line}`) : [` ${s.bold("Where these go")}`],
|
|
393
548
|
""
|
|
394
549
|
];
|
|
395
550
|
const body = [];
|
|
@@ -723,4 +878,4 @@ async function keys(args) {
|
|
|
723
878
|
].join("\n"));
|
|
724
879
|
}
|
|
725
880
|
//#endregion
|
|
726
|
-
export { yes as _, keys as a, Cancelled as c,
|
|
881
|
+
export { yes as C, isEmpty as E, wrap as S, init as T, select as _, keys as a, truncate as b, Cancelled as c, listed as d, openTerminal as f, row as g, release as h, keyValues as i, edit as l, readKeys as m, jsonWarning as n, needsTerminal as o, paragraph as p, keyGuide as r, showSecrets as s, generateKeys as t, hiddenLine as u, style as v, KINDS as w, width as x, textLine as y };
|
package/dist/main.js
CHANGED
|
@@ -1,75 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { a as keys } from "./keys-
|
|
2
|
+
import { T as init, a as keys, w as KINDS } from "./keys-BCKUEb-Y.js";
|
|
3
3
|
import { i as isRole, r as assignableToEnvironment, t as ROLES } from "./access-CO1kGjWW.js";
|
|
4
4
|
import { parseArgs, promisify } from "node:util";
|
|
5
5
|
import { execFile, spawn } from "node:child_process";
|
|
6
|
-
import { chmodSync, existsSync, lstatSync, mkdirSync, readFileSync,
|
|
6
|
+
import { chmodSync, existsSync, lstatSync, mkdirSync, readFileSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
7
7
|
import { homedir, hostname } from "node:os";
|
|
8
|
-
import {
|
|
9
|
-
import { fileURLToPath } from "node:url";
|
|
10
|
-
//#region src/init.ts
|
|
11
|
-
const KINDS = ["workers", "node"];
|
|
12
|
-
/** Where a template lives: in the package when published, else in the repository. */
|
|
13
|
-
function templateDir(kind) {
|
|
14
|
-
const packed = fileURLToPath(new URL(`./templates/${kind}/`, import.meta.url));
|
|
15
|
-
if (existsSync(packed)) return packed;
|
|
16
|
-
return fileURLToPath(new URL(`../../../examples/${kind}/`, import.meta.url));
|
|
17
|
-
}
|
|
18
|
-
/**
|
|
19
|
-
* A template's files, relative and sorted: everything but what its own
|
|
20
|
-
* `.gitignore` names, such as installed packages, local state and secrets.
|
|
21
|
-
* Patterns are file or directory names, `*` standing for any run of
|
|
22
|
-
* characters.
|
|
23
|
-
*/
|
|
24
|
-
function templateFiles(dir) {
|
|
25
|
-
const ignoreFile = [join(dir, ".gitignore"), join(dir, "gitignore")].find((path) => existsSync(path));
|
|
26
|
-
const ignored = (ignoreFile === void 0 ? "" : readFileSync(ignoreFile, "utf8")).split("\n").map((line) => line.trim()).filter((line) => line !== "" && !line.startsWith("#")).map((glob) => new RegExp(`^${glob.split("*").map((part) => part.replace(/[.+?^${}()|[\]\\]/g, "\\$&")).join(".*")}$`));
|
|
27
|
-
const files = [];
|
|
28
|
-
const walk = (relative) => {
|
|
29
|
-
for (const entry of readdirSync(join(dir, relative), { withFileTypes: true })) {
|
|
30
|
-
if (ignored.some((pattern) => pattern.test(entry.name))) continue;
|
|
31
|
-
const path = relative === "" ? entry.name : `${relative}/${entry.name}`;
|
|
32
|
-
if (entry.isDirectory()) walk(path);
|
|
33
|
-
else files.push(path);
|
|
34
|
-
}
|
|
35
|
-
};
|
|
36
|
-
walk("");
|
|
37
|
-
return files.sort();
|
|
38
|
-
}
|
|
39
|
-
/** The name a template file has in a project. */
|
|
40
|
-
function projectPath(templatePath) {
|
|
41
|
-
return templatePath === "gitignore" ? ".gitignore" : templatePath;
|
|
42
|
-
}
|
|
43
|
-
/** A package name from a directory's, e.g. `Acme Secrets` → `acme-secrets`. */
|
|
44
|
-
function packageName(dir) {
|
|
45
|
-
const name = basename(dir).toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^[-._]+|-+$/g, "");
|
|
46
|
-
return name === "" ? "coffre" : name;
|
|
47
|
-
}
|
|
48
|
-
/** The template's package.json, for this project and this version of coffre. */
|
|
49
|
-
function manifest(source, name, version) {
|
|
50
|
-
const pkg = JSON.parse(source);
|
|
51
|
-
pkg.name = name;
|
|
52
|
-
for (const field of ["dependencies", "devDependencies"]) {
|
|
53
|
-
const deps = pkg[field];
|
|
54
|
-
for (const dep of Object.keys(deps ?? {})) if (dep.startsWith("@coffre/")) deps[dep] = version;
|
|
55
|
-
}
|
|
56
|
-
return `${JSON.stringify(pkg, null, 2)}\n`;
|
|
57
|
-
}
|
|
58
|
-
/** Write a new deployment into `target`, which must be empty or absent. Returns the files written. */
|
|
59
|
-
function init(kind, target, version) {
|
|
60
|
-
if (existsSync(target) && readdirSync(target).length > 0) throw new Error(`${target} is not empty; give \`coffre init\` a new directory`);
|
|
61
|
-
const source = templateDir(kind);
|
|
62
|
-
const written = [];
|
|
63
|
-
for (const file of templateFiles(source)) {
|
|
64
|
-
const path = projectPath(file);
|
|
65
|
-
const content = readFileSync(join(source, file), "utf8");
|
|
66
|
-
mkdirSync(dirname(join(target, path)), { recursive: true });
|
|
67
|
-
writeFileSync(join(target, path), path === "package.json" ? manifest(content, packageName(target), version) : content);
|
|
68
|
-
written.push(path);
|
|
69
|
-
}
|
|
70
|
-
return written;
|
|
71
|
-
}
|
|
72
|
-
//#endregion
|
|
8
|
+
import { dirname, join, resolve } from "node:path";
|
|
73
9
|
//#region src/instance.ts
|
|
74
10
|
const MODES = ["signin", "cloudflare"];
|
|
75
11
|
function emptyStore() {
|
|
@@ -1462,7 +1398,8 @@ const USAGE = `coffre - secrets, with an audit log
|
|
|
1462
1398
|
coffre init --node [<dir>] a Node server, and its vault beside it
|
|
1463
1399
|
coffre setup [--reset-passwords] [--json]
|
|
1464
1400
|
its database logins, migrations and keys in one go,
|
|
1465
|
-
shown once on a screen of their own
|
|
1401
|
+
shown once on a screen of their own; on Workers,
|
|
1402
|
+
Cloudflare too, and in an empty directory, the deployment
|
|
1466
1403
|
coffre keys [--json] the app key, vault key and vault ID alone, shown the same way
|
|
1467
1404
|
|
|
1468
1405
|
Session
|
|
@@ -1516,7 +1453,7 @@ switch (command) {
|
|
|
1516
1453
|
await keys(rest);
|
|
1517
1454
|
break;
|
|
1518
1455
|
case "setup":
|
|
1519
|
-
await (await import("./setup-
|
|
1456
|
+
await (await import("./setup-D3SXt_P-.js")).setup(rest);
|
|
1520
1457
|
break;
|
|
1521
1458
|
case "login":
|
|
1522
1459
|
await login(rest);
|