getmyenv 0.1.1 → 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/LICENSE +1 -1
- package/README.md +12 -4
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +81 -51
- package/dist/cli.js.map +1 -1
- package/dist/commands/backup.d.ts.map +1 -1
- package/dist/commands/backup.js +15 -6
- package/dist/commands/backup.js.map +1 -1
- package/dist/commands/link.d.ts +16 -0
- package/dist/commands/link.d.ts.map +1 -0
- package/dist/commands/link.js +116 -0
- package/dist/commands/link.js.map +1 -0
- package/dist/commands/pull.d.ts +15 -0
- package/dist/commands/pull.d.ts.map +1 -0
- package/dist/commands/pull.js +157 -0
- package/dist/commands/pull.js.map +1 -0
- package/dist/commands/push.d.ts +15 -0
- package/dist/commands/push.d.ts.map +1 -0
- package/dist/commands/push.js +259 -0
- package/dist/commands/push.js.map +1 -0
- package/dist/commands/restore.d.ts.map +1 -1
- package/dist/commands/restore.js +27 -14
- package/dist/commands/restore.js.map +1 -1
- package/dist/commands/run.d.ts +1 -0
- package/dist/commands/run.d.ts.map +1 -1
- package/dist/commands/run.js +66 -25
- package/dist/commands/run.js.map +1 -1
- package/dist/commands/token.d.ts +19 -0
- package/dist/commands/token.d.ts.map +1 -0
- package/dist/commands/token.js +188 -0
- package/dist/commands/token.js.map +1 -0
- package/dist/commands/whoami.d.ts +6 -0
- package/dist/commands/whoami.d.ts.map +1 -0
- package/dist/commands/whoami.js +51 -0
- package/dist/commands/whoami.js.map +1 -0
- package/dist/lib/api.d.ts +17 -10
- package/dist/lib/api.d.ts.map +1 -1
- package/dist/lib/api.js +77 -28
- package/dist/lib/api.js.map +1 -1
- package/dist/lib/bootstrap.d.ts +20 -0
- package/dist/lib/bootstrap.d.ts.map +1 -0
- package/dist/lib/bootstrap.js +64 -0
- package/dist/lib/bootstrap.js.map +1 -0
- package/dist/lib/confirm.d.ts +17 -0
- package/dist/lib/confirm.d.ts.map +1 -0
- package/dist/lib/confirm.js +26 -0
- package/dist/lib/confirm.js.map +1 -0
- package/dist/lib/credentials.d.ts +65 -4
- package/dist/lib/credentials.d.ts.map +1 -1
- package/dist/lib/credentials.js +256 -17
- package/dist/lib/credentials.js.map +1 -1
- package/dist/lib/fs.d.ts +19 -1
- package/dist/lib/fs.d.ts.map +1 -1
- package/dist/lib/fs.js +88 -59
- package/dist/lib/fs.js.map +1 -1
- package/dist/lib/pek.d.ts +32 -0
- package/dist/lib/pek.d.ts.map +1 -0
- package/dist/lib/pek.js +81 -0
- package/dist/lib/pek.js.map +1 -0
- package/package.json +1 -1
- package/dist/commands/add.d.ts +0 -8
- package/dist/commands/add.d.ts.map +0 -1
- package/dist/commands/add.js +0 -64
- package/dist/commands/add.js.map +0 -1
- package/dist/commands/export.d.ts +0 -9
- package/dist/commands/export.d.ts.map +0 -1
- package/dist/commands/export.js +0 -48
- package/dist/commands/export.js.map +0 -1
- package/dist/commands/init.d.ts +0 -2
- package/dist/commands/init.d.ts.map +0 -1
- package/dist/commands/init.js +0 -58
- package/dist/commands/init.js.map +0 -1
- package/dist/commands/login.d.ts +0 -6
- package/dist/commands/login.d.ts.map +0 -1
- package/dist/commands/login.js +0 -35
- package/dist/commands/login.js.map +0 -1
- package/dist/commands/logout.d.ts +0 -2
- package/dist/commands/logout.d.ts.map +0 -1
- package/dist/commands/logout.js +0 -13
- package/dist/commands/logout.js.map +0 -1
- package/dist/commands/migrate.d.ts +0 -13
- package/dist/commands/migrate.d.ts.map +0 -1
- package/dist/commands/migrate.js +0 -127
- package/dist/commands/migrate.js.map +0 -1
- package/dist/commands/status.d.ts +0 -7
- package/dist/commands/status.d.ts.map +0 -1
- package/dist/commands/status.js +0 -76
- package/dist/commands/status.js.map +0 -1
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -3,11 +3,19 @@
|
|
|
3
3
|
CLI for [getmyenv](https://getmyenv.com) - encrypted environment variables for any language.
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
|
-
npx getmyenv
|
|
7
|
-
npx getmyenv
|
|
8
|
-
npx getmyenv run -- npm start
|
|
6
|
+
npx getmyenv token # paste per-project token
|
|
7
|
+
npx getmyenv link # link this folder to the project
|
|
8
|
+
npx getmyenv run -- npm run dev # start app with secrets as env vars
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
Create a
|
|
11
|
+
Create a project at https://getmyenv.com/projects/new, create a per-project CLI token at https://getmyenv.com/cli, then paste it with `token`. Not a full account login. Daily Development work prefers `run`. `push` and `pull` ask for token and link if missing.
|
|
12
|
+
|
|
13
|
+
- Development token: push / pull / restore / run. Push lists `.env*` files and skips any basename containing `production`.
|
|
14
|
+
- Production token: read-only (`run` / `backup`). Set Production values in the dashboard.
|
|
15
|
+
- Environment comes from token scope. Omit `-e` day to day.
|
|
16
|
+
|
|
17
|
+
API defaults to `https://getmyenv.com`. Use `token --api-url` or `--api-url` for another origin.
|
|
18
|
+
|
|
19
|
+
Commands: `token`, `link`, `push`, `pull`, `run`, `whoami`, `backup`, `restore`.
|
|
12
20
|
|
|
13
21
|
See https://getmyenv.com/docs
|
package/dist/cli.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAgCA,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAmK1D"}
|
package/dist/cli.js
CHANGED
|
@@ -1,77 +1,102 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
|
-
import { initCommand } from "./commands/init.js";
|
|
3
|
-
import { migrateCommand } from "./commands/migrate.js";
|
|
4
|
-
import { addCommand } from "./commands/add.js";
|
|
5
|
-
import { statusCommand } from "./commands/status.js";
|
|
6
|
-
import { exportCommand } from "./commands/export.js";
|
|
7
2
|
import { backupCommand } from "./commands/backup.js";
|
|
8
3
|
import { restoreCommand } from "./commands/restore.js";
|
|
9
4
|
import { runCommand } from "./commands/run.js";
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
5
|
+
import { tokenCommand, tokenListCommand, tokenRemoveCommand, tokenUseCommand, } from "./commands/token.js";
|
|
6
|
+
import { linkCommand } from "./commands/link.js";
|
|
7
|
+
import { pushCommand } from "./commands/push.js";
|
|
8
|
+
import { pullCommand } from "./commands/pull.js";
|
|
9
|
+
import { whoamiCommand } from "./commands/whoami.js";
|
|
12
10
|
import { normalizeEnvName } from "./lib/env-name.js";
|
|
13
11
|
import { resolveApiUrl } from "./lib/api.js";
|
|
12
|
+
import { readProjectMeta } from "./lib/fs.js";
|
|
13
|
+
function envWasDefaultFromArgv() {
|
|
14
|
+
return !process.argv.includes("-e") && !process.argv.includes("--env");
|
|
15
|
+
}
|
|
16
|
+
function defaultEnvOption() {
|
|
17
|
+
const meta = readProjectMeta();
|
|
18
|
+
return meta?.defaultEnv ?? "development";
|
|
19
|
+
}
|
|
14
20
|
const ENV_OPTION = [
|
|
15
21
|
"-e, --env <name>",
|
|
16
|
-
"Environment (
|
|
17
|
-
"development",
|
|
22
|
+
"Environment (must match token scope; omit to use the token)",
|
|
18
23
|
];
|
|
19
|
-
function withNormalizedEnv(fn) {
|
|
20
|
-
return (opts) => fn({ ...opts, env: normalizeEnvName(opts.env) });
|
|
21
|
-
}
|
|
22
24
|
export async function runCli(argv) {
|
|
23
25
|
const apiDefault = resolveApiUrl();
|
|
24
26
|
const program = new Command();
|
|
25
27
|
program
|
|
26
28
|
.name("getmyenv")
|
|
27
29
|
.description("Keep using environment variables. Stop managing them as files.")
|
|
28
|
-
.version("0.
|
|
29
|
-
program
|
|
30
|
-
.command("
|
|
31
|
-
.description("Save a
|
|
30
|
+
.version("0.3.0");
|
|
31
|
+
const token = program
|
|
32
|
+
.command("token")
|
|
33
|
+
.description("Save a per-project CLI token on this machine (not a full account login)")
|
|
32
34
|
.option("--api-url <url>", "API base URL to remember (default production)")
|
|
33
|
-
.action(
|
|
35
|
+
.action((opts) => tokenCommand(opts));
|
|
36
|
+
token
|
|
37
|
+
.command("list")
|
|
38
|
+
.description("List saved per-project tokens")
|
|
39
|
+
.action(() => tokenListCommand());
|
|
40
|
+
token
|
|
41
|
+
.command("use <nameOrId>")
|
|
42
|
+
.description("Set the default saved token")
|
|
43
|
+
.action((nameOrId) => tokenUseCommand(nameOrId));
|
|
44
|
+
token
|
|
45
|
+
.command("remove")
|
|
46
|
+
.description("Remove a saved per-project token")
|
|
47
|
+
.option("--project <name|id>", "Remove token for a specific project")
|
|
48
|
+
.option("--all", "Remove all saved tokens")
|
|
49
|
+
.option("--fresh", "Also clear local .getmyenv/wrapped-key.json")
|
|
50
|
+
.action(tokenRemoveCommand);
|
|
34
51
|
program
|
|
35
|
-
.command("
|
|
36
|
-
.description("
|
|
37
|
-
.
|
|
38
|
-
|
|
39
|
-
.
|
|
40
|
-
.
|
|
41
|
-
.
|
|
52
|
+
.command("link [projectId]")
|
|
53
|
+
.description("Link this folder to the token's project")
|
|
54
|
+
.option("--name <name>", "Expect project name (exact or unique substring)")
|
|
55
|
+
.option("--expect <name>", "Alias for --name")
|
|
56
|
+
.option("--env <name>", "Set defaultEnv (must match token scope)")
|
|
57
|
+
.option("--force", "Replace an existing link without prompting")
|
|
58
|
+
.option("--clear", "Remove the linked project from this folder")
|
|
59
|
+
.option("--api-url <url>", "API base URL", apiDefault)
|
|
60
|
+
.action((projectId, opts) => linkCommand(projectId, opts));
|
|
42
61
|
program
|
|
43
|
-
.command("
|
|
44
|
-
.description("Encrypt
|
|
62
|
+
.command("push")
|
|
63
|
+
.description("Encrypt local secrets and upload to the linked token project")
|
|
45
64
|
.option(...ENV_OPTION)
|
|
46
|
-
.option("-f, --file <path>", "Path to .env file to
|
|
65
|
+
.option("-f, --file <path>", "Path to .env file to push")
|
|
66
|
+
.option("-k, --key <NAME=VALUE>", "Push a single secret (NAME or NAME=VALUE)")
|
|
67
|
+
.option("--create", "If unlinked, write project.json from the token then upload")
|
|
68
|
+
.option("--prune", "Delete hosted secrets that are not in the local file")
|
|
69
|
+
.option("--yes", "Apply changes without prompting on conflicts")
|
|
70
|
+
.option("--dry-run", "Show what would change without uploading")
|
|
47
71
|
.option("--api-url <url>", "API base URL", apiDefault)
|
|
48
72
|
.option("--vault-password-stdin", "Read vault password from stdin (non-interactive)")
|
|
49
|
-
.action((opts) =>
|
|
73
|
+
.action((opts) => pushCommand({
|
|
50
74
|
...opts,
|
|
51
|
-
env: normalizeEnvName(opts.env),
|
|
52
|
-
envWasDefault:
|
|
75
|
+
env: normalizeEnvName(opts.env ?? defaultEnvOption()),
|
|
76
|
+
envWasDefault: envWasDefaultFromArgv(),
|
|
53
77
|
}));
|
|
54
78
|
program
|
|
55
|
-
.command("
|
|
56
|
-
.description("
|
|
57
|
-
.option(...ENV_OPTION)
|
|
58
|
-
.option("--api-url <url>", "API base URL", apiDefault)
|
|
59
|
-
.option("--vault-password-stdin", "Read vault password from stdin (non-interactive)")
|
|
60
|
-
.action((name, opts) => addCommand(name, { ...opts, env: normalizeEnvName(opts.env) }));
|
|
61
|
-
program
|
|
62
|
-
.command("status")
|
|
63
|
-
.description("Show secret status for the current project")
|
|
64
|
-
.option(...ENV_OPTION)
|
|
65
|
-
.option("--api-url <url>", "API base URL", apiDefault)
|
|
66
|
-
.action(withNormalizedEnv(statusCommand));
|
|
67
|
-
program
|
|
68
|
-
.command("export")
|
|
69
|
-
.description("Export secrets to a plaintext .env file")
|
|
79
|
+
.command("pull")
|
|
80
|
+
.description("Download secrets to a plaintext .env file")
|
|
70
81
|
.option(...ENV_OPTION)
|
|
71
82
|
.option("-o, --output <file>", "Output file", ".env")
|
|
83
|
+
.option("--to <file>", "Alias for --output")
|
|
84
|
+
.option("--stdout", "Write to stdout instead of a file")
|
|
85
|
+
.option("--yes", "Overwrite conflicting local values without prompting")
|
|
86
|
+
.option("--merge", "Only fill missing keys; keep local values")
|
|
87
|
+
.option("--dry-run", "Show what would change without writing")
|
|
72
88
|
.option("--api-url <url>", "API base URL", apiDefault)
|
|
73
89
|
.option("--vault-password-stdin", "Read vault password from stdin (non-interactive)")
|
|
74
|
-
.action(
|
|
90
|
+
.action((opts) => pullCommand({
|
|
91
|
+
...opts,
|
|
92
|
+
env: normalizeEnvName(opts.env ?? defaultEnvOption()),
|
|
93
|
+
envWasDefault: envWasDefaultFromArgv(),
|
|
94
|
+
}));
|
|
95
|
+
program
|
|
96
|
+
.command("whoami")
|
|
97
|
+
.description("Show the active CLI token, project, and defaults")
|
|
98
|
+
.option("--api-url <url>", "API base URL")
|
|
99
|
+
.action(whoamiCommand);
|
|
75
100
|
program
|
|
76
101
|
.command("backup")
|
|
77
102
|
.description("Create a portable encrypted .getmyenv backup")
|
|
@@ -80,14 +105,14 @@ export async function runCli(argv) {
|
|
|
80
105
|
.action(backupCommand);
|
|
81
106
|
program
|
|
82
107
|
.command("restore <file>")
|
|
83
|
-
.description("Restore from an encrypted .getmyenv backup
|
|
108
|
+
.description("Restore from an encrypted .getmyenv backup into the token's project")
|
|
84
109
|
.option("--api-url <url>", "API base URL", apiDefault)
|
|
85
|
-
.option("--into-linked", "
|
|
110
|
+
.option("--into-linked", "Require a linked folder (restore still targets the token project)")
|
|
86
111
|
.option("--vault-password-stdin", "Read vault password from stdin (non-interactive)")
|
|
87
112
|
.action(restoreCommand);
|
|
88
113
|
program
|
|
89
114
|
.command("run")
|
|
90
|
-
.description("Decrypt authorized secrets and run a command
|
|
115
|
+
.description("Decrypt authorized secrets and run a command (token picks Development or Production)")
|
|
91
116
|
.option(...ENV_OPTION)
|
|
92
117
|
.option("--api-url <url>", "API base URL", apiDefault)
|
|
93
118
|
.option("--vault-password-stdin", "Read vault password from stdin (non-interactive)")
|
|
@@ -95,10 +120,15 @@ export async function runCli(argv) {
|
|
|
95
120
|
.allowExcessArguments(true)
|
|
96
121
|
.action(async (opts, cmd) => {
|
|
97
122
|
const args = cmd.args;
|
|
98
|
-
// Support: getmyenv run -- npm
|
|
123
|
+
// Support: getmyenv run -- npm run dev
|
|
99
124
|
const dashIdx = process.argv.indexOf("--");
|
|
100
125
|
const childArgs = dashIdx >= 0 ? process.argv.slice(dashIdx + 1) : args;
|
|
101
|
-
await runCommand({
|
|
126
|
+
await runCommand({
|
|
127
|
+
...opts,
|
|
128
|
+
env: normalizeEnvName(opts.env ?? defaultEnvOption()),
|
|
129
|
+
envWasDefault: envWasDefaultFromArgv(),
|
|
130
|
+
apiUrl: opts.apiUrl ?? apiDefault,
|
|
131
|
+
}, childArgs);
|
|
102
132
|
});
|
|
103
133
|
await program.parseAsync(argv);
|
|
104
134
|
}
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"backup.d.ts","sourceRoot":"","sources":["../../src/commands/backup.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"backup.d.ts","sourceRoot":"","sources":["../../src/commands/backup.ts"],"names":[],"mappings":"AAiBA,KAAK,UAAU,GAAG;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAEtD,wBAAsB,aAAa,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CA2DnE"}
|
package/dist/commands/backup.js
CHANGED
|
@@ -2,16 +2,22 @@ import fs from "node:fs";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import pc from "picocolors";
|
|
4
4
|
import { createBackupEnvelope, parsePayload, parseWrappedKey, serializeBackup, } from "@getmyenv/crypto";
|
|
5
|
-
import { readProjectMeta } from "../lib/fs.js";
|
|
6
|
-
import { ApiClient, ensureSessionToken } from "../lib/api.js";
|
|
5
|
+
import { readProjectMeta, requireLinkedProject } from "../lib/fs.js";
|
|
6
|
+
import { ApiClient, ensureSessionToken, resolveApiUrl, } from "../lib/api.js";
|
|
7
7
|
export async function backupCommand(opts) {
|
|
8
8
|
console.log(pc.bold("\ngetmyenv backup\n"));
|
|
9
9
|
const meta = readProjectMeta();
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
requireLinkedProject(meta);
|
|
11
|
+
const token = await ensureSessionToken(meta.projectId);
|
|
12
|
+
const apiUrl = resolveApiUrl(opts.apiUrl, meta.projectId);
|
|
13
|
+
const client = new ApiClient(apiUrl, token);
|
|
14
|
+
const who = await client.request("GET", "/api/cli/whoami");
|
|
15
|
+
if (String(meta.projectId) !== String(who.projectId)) {
|
|
16
|
+
throw new Error([
|
|
17
|
+
`Linked project (${meta.projectName ?? meta.projectId}) does not match this token (${who.projectName}, ${who.projectId}).`,
|
|
18
|
+
"Run: npx getmyenv link",
|
|
19
|
+
].join("\n"));
|
|
12
20
|
}
|
|
13
|
-
const token = await ensureSessionToken();
|
|
14
|
-
const client = new ApiClient(opts.apiUrl, token);
|
|
15
21
|
const data = await client.request("GET", `/api/cli/projects/${meta.projectId}/backup`);
|
|
16
22
|
const envelope = createBackupEnvelope({
|
|
17
23
|
projectName: data.projectName,
|
|
@@ -30,6 +36,9 @@ export async function backupCommand(opts) {
|
|
|
30
36
|
path.join(process.cwd(), `${data.projectName.replace(/\s+/g, "-").toLowerCase()}.getmyenv`);
|
|
31
37
|
fs.writeFileSync(out, serializeBackup(envelope), { mode: 0o600 });
|
|
32
38
|
console.log(`${pc.green("✓")} Wrote encrypted backup: ${out}`);
|
|
39
|
+
if (meta.defaultEnv) {
|
|
40
|
+
console.log(pc.dim(`Linked defaultEnv: ${meta.defaultEnv}`));
|
|
41
|
+
}
|
|
33
42
|
console.log(pc.dim("Portable. No plaintext. Usable offline with your vault password.\n"));
|
|
34
43
|
}
|
|
35
44
|
//# sourceMappingURL=backup.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"backup.js","sourceRoot":"","sources":["../../src/commands/backup.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,MAAM,YAAY,CAAC;AAC5B,OAAO,EACL,oBAAoB,EACpB,YAAY,EACZ,eAAe,EACf,eAAe,GAChB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"backup.js","sourceRoot":"","sources":["../../src/commands/backup.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
type LinkOpts = {
|
|
2
|
+
apiUrl?: string;
|
|
3
|
+
name?: string;
|
|
4
|
+
expect?: string;
|
|
5
|
+
force?: boolean;
|
|
6
|
+
clear?: boolean;
|
|
7
|
+
env?: string;
|
|
8
|
+
/** When true, do not offer push after link (caller is already pushing). */
|
|
9
|
+
skipPushOffer?: boolean;
|
|
10
|
+
};
|
|
11
|
+
/** Clear the linked project from this folder (link --clear). */
|
|
12
|
+
export declare function clearLink(): Promise<void>;
|
|
13
|
+
/** Link this folder to the project bound to the saved CLI token. */
|
|
14
|
+
export declare function linkCommand(_projectIdArg: string | undefined, opts: LinkOpts): Promise<void>;
|
|
15
|
+
export {};
|
|
16
|
+
//# sourceMappingURL=link.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"link.d.ts","sourceRoot":"","sources":["../../src/commands/link.ts"],"names":[],"mappings":"AAmBA,KAAK,QAAQ,GAAG;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,2EAA2E;IAC3E,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB,CAAC;AAEF,gEAAgE;AAChE,wBAAsB,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC,CAqB/C;AAED,oEAAoE;AACpE,wBAAsB,WAAW,CAC/B,aAAa,EAAE,MAAM,GAAG,SAAS,EACjC,IAAI,EAAE,QAAQ,GACb,OAAO,CAAC,IAAI,CAAC,CA6If"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import pc from "picocolors";
|
|
2
|
+
import { ensureGitignoreHasEnv, listPushEnvFiles, promptConfirm, readProjectMeta, writeProjectMeta, } from "../lib/fs.js";
|
|
3
|
+
import { ApiClient, ensureSessionToken, getSessionToken, resolveApiUrl, } from "../lib/api.js";
|
|
4
|
+
import { normalizeEnvName } from "../lib/env-name.js";
|
|
5
|
+
import { tokenCommand } from "./token.js";
|
|
6
|
+
import { pushCommand } from "./push.js";
|
|
7
|
+
/** Clear the linked project from this folder (link --clear). */
|
|
8
|
+
export async function clearLink() {
|
|
9
|
+
console.log(pc.bold("\ngetmyenv link --clear\n"));
|
|
10
|
+
const meta = readProjectMeta();
|
|
11
|
+
if (!meta?.projectId) {
|
|
12
|
+
console.log(pc.dim("No project is linked to this folder.\n"));
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
if (process.stdin.isTTY) {
|
|
16
|
+
const ok = await promptConfirm(`Unlink ${meta.projectName ?? meta.projectId}?`, { defaultYes: false });
|
|
17
|
+
if (!ok) {
|
|
18
|
+
console.log(pc.dim("Kept link.\n"));
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
const { projectId: _id, projectName: _name, defaultEnv: _env, ...rest } = meta;
|
|
23
|
+
writeProjectMeta(rest);
|
|
24
|
+
console.log(`${pc.green("✓")} Unlinked. Local .getmyenv metadata kept.\n`);
|
|
25
|
+
}
|
|
26
|
+
/** Link this folder to the project bound to the saved CLI token. */
|
|
27
|
+
export async function linkCommand(_projectIdArg, opts) {
|
|
28
|
+
if (opts.clear) {
|
|
29
|
+
await clearLink();
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
console.log(pc.bold("\ngetmyenv link\n"));
|
|
33
|
+
if (!getSessionToken()) {
|
|
34
|
+
if (!process.stdin.isTTY) {
|
|
35
|
+
throw new Error("No CLI token. Run: npx getmyenv token");
|
|
36
|
+
}
|
|
37
|
+
await tokenCommand({
|
|
38
|
+
apiUrl: opts.apiUrl,
|
|
39
|
+
skipLinkOffer: true,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
const token = await ensureSessionToken();
|
|
43
|
+
const apiUrl = resolveApiUrl(opts.apiUrl);
|
|
44
|
+
const client = new ApiClient(apiUrl, token);
|
|
45
|
+
const who = await client.request("GET", "/api/cli/whoami");
|
|
46
|
+
const expectName = opts.expect?.trim() || opts.name?.trim();
|
|
47
|
+
if (expectName) {
|
|
48
|
+
const needle = expectName.toLowerCase();
|
|
49
|
+
const matches = who.projectName.toLowerCase() === needle ||
|
|
50
|
+
who.projectName.toLowerCase().includes(needle) ||
|
|
51
|
+
String(who.projectId) === expectName;
|
|
52
|
+
if (!matches) {
|
|
53
|
+
throw new Error(`Token project "${who.projectName}" (${who.projectId}) does not match --expect ${expectName}`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
let defaultEnv = who.cliScope === "production" ? "production" : "development";
|
|
57
|
+
if (opts.env?.trim()) {
|
|
58
|
+
const requested = normalizeEnvName(opts.env);
|
|
59
|
+
if (requested !== who.cliScope) {
|
|
60
|
+
throw new Error(`Token scope is ${who.cliScope}, but --env ${requested} was passed. Use a matching token or omit --env.`);
|
|
61
|
+
}
|
|
62
|
+
defaultEnv = requested;
|
|
63
|
+
}
|
|
64
|
+
const meta = readProjectMeta() ?? {};
|
|
65
|
+
if (meta.projectId && String(meta.projectId) !== String(who.projectId)) {
|
|
66
|
+
console.log(pc.yellow(`This folder is linked to ${meta.projectName ?? meta.projectId}.`));
|
|
67
|
+
console.log(pc.dim(`Token is for ${who.projectName} (${who.projectId}, ${who.cliScope}).\n`));
|
|
68
|
+
if (!opts.force) {
|
|
69
|
+
if (!process.stdin.isTTY) {
|
|
70
|
+
throw new Error("Already linked to a different project. Re-run with --force, or: npx getmyenv link --clear");
|
|
71
|
+
}
|
|
72
|
+
const ok = await promptConfirm("Replace with token project?", {
|
|
73
|
+
defaultYes: false,
|
|
74
|
+
});
|
|
75
|
+
if (!ok) {
|
|
76
|
+
console.log(pc.dim("Kept existing link.\n"));
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
writeProjectMeta({
|
|
82
|
+
...meta,
|
|
83
|
+
projectId: who.projectId,
|
|
84
|
+
projectName: who.projectName,
|
|
85
|
+
apiUrl,
|
|
86
|
+
defaultEnv,
|
|
87
|
+
});
|
|
88
|
+
ensureGitignoreHasEnv();
|
|
89
|
+
console.log(`${pc.green("✓")} Linked to ${pc.bold(who.projectName)} (${who.projectId})`);
|
|
90
|
+
console.log(pc.dim(`Token scope: ${who.cliScope}`));
|
|
91
|
+
console.log(pc.dim(`defaultEnv: ${defaultEnv}`));
|
|
92
|
+
if (who.cliScope === "production") {
|
|
93
|
+
console.log(pc.dim("Next: npx getmyenv run -- <cmd> # start app with secrets as env vars (Production is read-only)\n"));
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
if (opts.skipPushOffer || !process.stdin.isTTY) {
|
|
97
|
+
console.log(pc.dim("Next: npx getmyenv run -- <cmd> # start app with secrets as env vars\n"));
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
const pushable = listPushEnvFiles();
|
|
101
|
+
if (pushable.length === 0) {
|
|
102
|
+
console.log(pc.dim("Next: npx getmyenv run -- <cmd> # start app with secrets as env vars\n"));
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
const doPush = await promptConfirm("Push and migrate a local .env file now?", { defaultYes: true });
|
|
106
|
+
if (!doPush) {
|
|
107
|
+
console.log(pc.dim("When you are ready: npx getmyenv run -- <cmd> # start app with secrets as env vars\n"));
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
await pushCommand({
|
|
111
|
+
env: who.cliScope,
|
|
112
|
+
apiUrl,
|
|
113
|
+
envWasDefault: true,
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
//# sourceMappingURL=link.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"link.js","sourceRoot":"","sources":["../../src/commands/link.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
type PullOpts = {
|
|
2
|
+
env: string;
|
|
3
|
+
output?: string;
|
|
4
|
+
to?: string;
|
|
5
|
+
stdout?: boolean;
|
|
6
|
+
yes?: boolean;
|
|
7
|
+
merge?: boolean;
|
|
8
|
+
dryRun?: boolean;
|
|
9
|
+
apiUrl: string;
|
|
10
|
+
vaultPasswordStdin?: boolean;
|
|
11
|
+
envWasDefault?: boolean;
|
|
12
|
+
};
|
|
13
|
+
export declare function pullCommand(opts: PullOpts): Promise<void>;
|
|
14
|
+
export {};
|
|
15
|
+
//# sourceMappingURL=pull.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pull.d.ts","sourceRoot":"","sources":["../../src/commands/pull.ts"],"names":[],"mappings":"AAmBA,KAAK,QAAQ,GAAG;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB,CAAC;AAEF,wBAAsB,WAAW,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CA4K/D"}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import pc from "picocolors";
|
|
3
|
+
import { decryptSecret, parsePayload, } from "@getmyenv/crypto";
|
|
4
|
+
import { EXPORT_WARNING } from "@getmyenv/shared";
|
|
5
|
+
import { parseEnvFile, promptHidden, readProjectMeta, } from "../lib/fs.js";
|
|
6
|
+
import { ApiClient } from "../lib/api.js";
|
|
7
|
+
import { unwrapPek } from "../lib/pek.js";
|
|
8
|
+
import { confirmDiff } from "../lib/confirm.js";
|
|
9
|
+
import { normalizeEnvName } from "../lib/env-name.js";
|
|
10
|
+
import { ensureTokenAndLink } from "../lib/bootstrap.js";
|
|
11
|
+
export async function pullCommand(opts) {
|
|
12
|
+
console.log(pc.bold("\ngetmyenv pull\n"));
|
|
13
|
+
console.log(pc.yellow(EXPORT_WARNING));
|
|
14
|
+
console.log("");
|
|
15
|
+
const { who, apiUrl, token, projectId } = await ensureTokenAndLink({
|
|
16
|
+
apiUrl: opts.apiUrl,
|
|
17
|
+
action: "pull",
|
|
18
|
+
});
|
|
19
|
+
if (who.cliScope === "production") {
|
|
20
|
+
throw new Error([
|
|
21
|
+
"Production CLI is read-only.",
|
|
22
|
+
"Use npx getmyenv run to pass secrets as env vars without writing them to disk.",
|
|
23
|
+
"Set or change Production values in the dashboard.",
|
|
24
|
+
].join("\n"));
|
|
25
|
+
}
|
|
26
|
+
const meta = readProjectMeta();
|
|
27
|
+
let environment = normalizeEnvName(opts.env);
|
|
28
|
+
if (opts.envWasDefault && meta?.defaultEnv) {
|
|
29
|
+
environment = meta.defaultEnv;
|
|
30
|
+
}
|
|
31
|
+
if (environment !== who.cliScope) {
|
|
32
|
+
if (opts.envWasDefault) {
|
|
33
|
+
environment = who.cliScope;
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
throw new Error([
|
|
37
|
+
`Token scope is ${who.cliScope}, but you passed -e ${environment}.`,
|
|
38
|
+
"Use a matching token or omit -e.",
|
|
39
|
+
].join("\n"));
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
const password = await promptHidden("Vault password: ", {
|
|
43
|
+
allowStdin: opts.vaultPasswordStdin,
|
|
44
|
+
});
|
|
45
|
+
const client = new ApiClient(apiUrl, token);
|
|
46
|
+
const data = await client.request("GET", `/api/cli/projects/${projectId}/export?environment=${encodeURIComponent(environment)}`);
|
|
47
|
+
const pek = await unwrapPek({
|
|
48
|
+
password,
|
|
49
|
+
scope: who.cliScope,
|
|
50
|
+
wrapped: data.wrappedProjectKey,
|
|
51
|
+
});
|
|
52
|
+
const remote = {};
|
|
53
|
+
const lines = [
|
|
54
|
+
"# Pulled by getmyenv - PLAINTEXT SECRETS",
|
|
55
|
+
`# Environment: ${environment}`,
|
|
56
|
+
"# Do not commit this file.",
|
|
57
|
+
"",
|
|
58
|
+
];
|
|
59
|
+
for (const secret of data.secrets) {
|
|
60
|
+
if (secret.status === "revoked") {
|
|
61
|
+
lines.push("# revoked - getmyenv is not providing this");
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
if (secret.status === "placeholder" ||
|
|
65
|
+
secret.ciphertext == null ||
|
|
66
|
+
secret.ciphertext === "") {
|
|
67
|
+
lines.push(`# placeholder - no value yet`);
|
|
68
|
+
lines.push(`${secret.name}=`);
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
const value = decryptSecret(pek, parsePayload(secret.ciphertext));
|
|
72
|
+
remote[secret.name] = value;
|
|
73
|
+
lines.push(`${secret.name}=${value}`);
|
|
74
|
+
}
|
|
75
|
+
if (opts.stdout) {
|
|
76
|
+
process.stdout.write(lines.join("\n") + "\n");
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
const outPath = opts.to || opts.output || ".env";
|
|
80
|
+
const exists = fs.existsSync(outPath);
|
|
81
|
+
const local = exists ? parseEnvFile(outPath) : {};
|
|
82
|
+
if (opts.dryRun) {
|
|
83
|
+
console.log(pc.dim(`Dry run - would write ${outPath}\n`));
|
|
84
|
+
for (const name of Object.keys(remote)) {
|
|
85
|
+
if (!(name in local))
|
|
86
|
+
console.log(` + ${name}`);
|
|
87
|
+
else if (local[name] !== remote[name])
|
|
88
|
+
console.log(` ~ ${name}`);
|
|
89
|
+
else
|
|
90
|
+
console.log(` = ${name}`);
|
|
91
|
+
}
|
|
92
|
+
for (const name of Object.keys(local)) {
|
|
93
|
+
if (!(name in remote))
|
|
94
|
+
console.log(` - ${name} (local only)`);
|
|
95
|
+
}
|
|
96
|
+
console.log("");
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
if (opts.merge && exists) {
|
|
100
|
+
const merged = { ...local };
|
|
101
|
+
for (const [name, value] of Object.entries(remote)) {
|
|
102
|
+
if (!(name in merged) || merged[name] === "") {
|
|
103
|
+
merged[name] = value;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
const mergedLines = [
|
|
107
|
+
"# Pulled by getmyenv - PLAINTEXT SECRETS (merged)",
|
|
108
|
+
`# Environment: ${environment}`,
|
|
109
|
+
"# Do not commit this file.",
|
|
110
|
+
"",
|
|
111
|
+
...Object.entries(merged).map(([k, v]) => `${k}=${v}`),
|
|
112
|
+
];
|
|
113
|
+
fs.writeFileSync(outPath, mergedLines.join("\n") + "\n", { mode: 0o600 });
|
|
114
|
+
console.log(`${pc.green("✓")} Merged into ${outPath}`);
|
|
115
|
+
console.log(pc.dim("Your secrets are yours. Protect this file.\n"));
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
const conflicts = [];
|
|
119
|
+
for (const [name, value] of Object.entries(remote)) {
|
|
120
|
+
if (name in local && local[name] !== value) {
|
|
121
|
+
conflicts.push({ name, detail: "local differs from remote" });
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
if (conflicts.length > 0) {
|
|
125
|
+
const ok = await confirmDiff({
|
|
126
|
+
conflicts,
|
|
127
|
+
action: "Overwrite conflicting local values",
|
|
128
|
+
yes: opts.yes,
|
|
129
|
+
});
|
|
130
|
+
if (!ok) {
|
|
131
|
+
console.log(pc.dim("Pull cancelled.\n"));
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
if (exists && conflicts.length === 0 && Object.keys(local).length > 0) {
|
|
136
|
+
const remoteKeys = new Set(Object.keys(remote));
|
|
137
|
+
const localOnly = Object.keys(local).filter((k) => !remoteKeys.has(k));
|
|
138
|
+
if (localOnly.length > 0 && !opts.yes) {
|
|
139
|
+
const ok = await confirmDiff({
|
|
140
|
+
conflicts: localOnly.map((name) => ({
|
|
141
|
+
name,
|
|
142
|
+
detail: "present locally, absent remotely (will be dropped)",
|
|
143
|
+
})),
|
|
144
|
+
action: "Replace local file",
|
|
145
|
+
yes: opts.yes,
|
|
146
|
+
});
|
|
147
|
+
if (!ok) {
|
|
148
|
+
console.log(pc.dim("Pull cancelled.\n"));
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
fs.writeFileSync(outPath, lines.join("\n") + "\n", { mode: 0o600 });
|
|
154
|
+
console.log(`${pc.green("✓")} Wrote ${outPath}`);
|
|
155
|
+
console.log(pc.dim("Your secrets are yours. Protect this file.\n"));
|
|
156
|
+
}
|
|
157
|
+
//# sourceMappingURL=pull.js.map
|