getmyenv 0.5.3 → 0.6.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/README.md +57 -13
- package/dist/bin.js +1 -24
- package/dist/bin.js.map +1 -1
- package/dist/cli.d.ts +10 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +179 -106
- package/dist/cli.js.map +1 -1
- package/dist/commands/backup.d.ts +2 -1
- package/dist/commands/backup.d.ts.map +1 -1
- package/dist/commands/backup.js +15 -19
- package/dist/commands/backup.js.map +1 -1
- package/dist/commands/claim.d.ts +2 -1
- package/dist/commands/claim.d.ts.map +1 -1
- package/dist/commands/claim.js +114 -80
- package/dist/commands/claim.js.map +1 -1
- package/dist/commands/context.d.ts +11 -0
- package/dist/commands/context.d.ts.map +1 -0
- package/dist/commands/context.js +53 -0
- package/dist/commands/context.js.map +1 -0
- package/dist/commands/export.d.ts +11 -0
- package/dist/commands/export.d.ts.map +1 -0
- package/dist/commands/export.js +67 -0
- package/dist/commands/export.js.map +1 -0
- package/dist/commands/guest.d.ts +9 -3
- package/dist/commands/guest.d.ts.map +1 -1
- package/dist/commands/guest.js +32 -28
- package/dist/commands/guest.js.map +1 -1
- package/dist/commands/import.d.ts +26 -0
- package/dist/commands/import.d.ts.map +1 -0
- package/dist/commands/import.js +168 -0
- package/dist/commands/import.js.map +1 -0
- package/dist/commands/lock.d.ts +5 -0
- package/dist/commands/lock.d.ts.map +1 -0
- package/dist/commands/lock.js +18 -0
- package/dist/commands/lock.js.map +1 -0
- package/dist/commands/ls.d.ts +6 -0
- package/dist/commands/ls.d.ts.map +1 -0
- package/dist/commands/ls.js +37 -0
- package/dist/commands/ls.js.map +1 -0
- package/dist/commands/restore.d.ts +7 -2
- package/dist/commands/restore.d.ts.map +1 -1
- package/dist/commands/restore.js +57 -49
- package/dist/commands/restore.js.map +1 -1
- package/dist/commands/run.d.ts +7 -5
- package/dist/commands/run.d.ts.map +1 -1
- package/dist/commands/run.js +98 -187
- package/dist/commands/run.js.map +1 -1
- package/dist/commands/set.d.ts +16 -0
- package/dist/commands/set.d.ts.map +1 -0
- package/dist/commands/set.js +111 -0
- package/dist/commands/set.js.map +1 -0
- package/dist/commands/start.d.ts +16 -0
- package/dist/commands/start.d.ts.map +1 -0
- package/dist/commands/start.js +118 -0
- package/dist/commands/start.js.map +1 -0
- package/dist/commands/status.d.ts +5 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +40 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/unlink.d.ts +9 -0
- package/dist/commands/unlink.d.ts.map +1 -0
- package/dist/commands/unlink.js +70 -0
- package/dist/commands/unlink.js.map +1 -0
- package/dist/lib/api-url.d.ts +12 -0
- package/dist/lib/api-url.d.ts.map +1 -1
- package/dist/lib/api-url.js +32 -0
- package/dist/lib/api-url.js.map +1 -1
- package/dist/lib/api.d.ts +35 -23
- package/dist/lib/api.d.ts.map +1 -1
- package/dist/lib/api.js +23 -59
- package/dist/lib/api.js.map +1 -1
- package/dist/lib/authorize.d.ts +13 -6
- package/dist/lib/authorize.d.ts.map +1 -1
- package/dist/lib/authorize.js +68 -61
- package/dist/lib/authorize.js.map +1 -1
- package/dist/lib/bootstrap.d.ts +18 -15
- package/dist/lib/bootstrap.d.ts.map +1 -1
- package/dist/lib/bootstrap.js +59 -69
- package/dist/lib/bootstrap.js.map +1 -1
- package/dist/lib/confirm.js +4 -4
- package/dist/lib/confirm.js.map +1 -1
- package/dist/lib/context.d.ts +12 -0
- package/dist/lib/context.d.ts.map +1 -0
- package/dist/lib/context.js +43 -0
- package/dist/lib/context.js.map +1 -0
- package/dist/lib/credentials.d.ts +8 -13
- package/dist/lib/credentials.d.ts.map +1 -1
- package/dist/lib/credentials.js +2 -9
- package/dist/lib/credentials.js.map +1 -1
- package/dist/lib/fs.d.ts +26 -40
- package/dist/lib/fs.d.ts.map +1 -1
- package/dist/lib/fs.js +112 -326
- package/dist/lib/fs.js.map +1 -1
- package/dist/lib/guest-key.d.ts +2 -5
- package/dist/lib/guest-key.d.ts.map +1 -1
- package/dist/lib/guest-key.js +9 -2
- package/dist/lib/guest-key.js.map +1 -1
- package/dist/lib/identity.d.ts +9 -11
- package/dist/lib/identity.d.ts.map +1 -1
- package/dist/lib/identity.js +34 -24
- package/dist/lib/identity.js.map +1 -1
- package/dist/lib/keychain.d.ts +21 -0
- package/dist/lib/keychain.d.ts.map +1 -0
- package/dist/lib/keychain.js +124 -0
- package/dist/lib/keychain.js.map +1 -0
- package/dist/lib/pek.d.ts +15 -24
- package/dist/lib/pek.d.ts.map +1 -1
- package/dist/lib/pek.js +30 -54
- package/dist/lib/pek.js.map +1 -1
- package/dist/lib/run-env.d.ts +50 -0
- package/dist/lib/run-env.d.ts.map +1 -0
- package/dist/lib/run-env.js +114 -0
- package/dist/lib/run-env.js.map +1 -0
- package/dist/lib/unlock.d.ts +24 -0
- package/dist/lib/unlock.d.ts.map +1 -0
- package/dist/lib/unlock.js +87 -0
- package/dist/lib/unlock.js.map +1 -0
- package/dist/lib/update-check.d.ts +4 -2
- package/dist/lib/update-check.d.ts.map +1 -1
- package/dist/lib/update-check.js +54 -68
- package/dist/lib/update-check.js.map +1 -1
- package/dist/lib/upload.d.ts +21 -0
- package/dist/lib/upload.d.ts.map +1 -0
- package/dist/lib/upload.js +53 -0
- package/dist/lib/upload.js.map +1 -0
- package/package.json +5 -3
- package/dist/commands/delete.d.ts +0 -6
- package/dist/commands/delete.d.ts.map +0 -1
- package/dist/commands/delete.js +0 -55
- package/dist/commands/delete.js.map +0 -1
- package/dist/commands/link.d.ts +0 -16
- package/dist/commands/link.d.ts.map +0 -1
- package/dist/commands/link.js +0 -142
- package/dist/commands/link.js.map +0 -1
- package/dist/commands/pull.d.ts +0 -15
- package/dist/commands/pull.d.ts.map +0 -1
- package/dist/commands/pull.js +0 -155
- package/dist/commands/pull.js.map +0 -1
- package/dist/commands/push.d.ts +0 -15
- package/dist/commands/push.d.ts.map +0 -1
- package/dist/commands/push.js +0 -261
- package/dist/commands/push.js.map +0 -1
- package/dist/commands/remove.d.ts +0 -11
- package/dist/commands/remove.d.ts.map +0 -1
- package/dist/commands/remove.js +0 -39
- package/dist/commands/remove.js.map +0 -1
- package/dist/commands/token.d.ts +0 -14
- package/dist/commands/token.d.ts.map +0 -1
- package/dist/commands/token.js +0 -132
- package/dist/commands/token.js.map +0 -1
- package/dist/commands/whoami.d.ts +0 -6
- package/dist/commands/whoami.d.ts.map +0 -1
- package/dist/commands/whoami.js +0 -46
- package/dist/commands/whoami.js.map +0 -1
- package/dist/lib/env-name.d.ts +0 -3
- package/dist/lib/env-name.d.ts.map +0 -1
- package/dist/lib/env-name.js +0 -5
- package/dist/lib/env-name.js.map +0 -1
- package/dist/lib/guest-key.test.d.ts +0 -2
- package/dist/lib/guest-key.test.d.ts.map +0 -1
- package/dist/lib/guest-key.test.js.map +0 -1
package/dist/lib/authorize.js
CHANGED
|
@@ -1,18 +1,39 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
2
|
import pc from "picocolors";
|
|
3
3
|
import { ensureGitignoreHasEnv, readLine, readProjectMeta, writeProjectMeta, } from "./fs.js";
|
|
4
|
-
import { writeFolderToken
|
|
5
|
-
import { ApiClient, resolveApiUrl } from "./api.js";
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
4
|
+
import { writeFolderToken } from "./credentials.js";
|
|
5
|
+
import { ApiClient, parseSafeUrl, resolveApiUrl } from "./api.js";
|
|
6
|
+
/** Command + argv to open a URL. No shell, so the URL is never parsed as a command. */
|
|
7
|
+
export function browserCommand(raw, platform = process.platform) {
|
|
8
|
+
const url = parseSafeUrl(raw);
|
|
9
|
+
if (!url)
|
|
10
|
+
return null;
|
|
11
|
+
const href = url.href;
|
|
12
|
+
if (platform === "win32") {
|
|
13
|
+
return { cmd: "rundll32.exe", args: ["url.dll,FileProtocolHandler", href] };
|
|
14
|
+
}
|
|
15
|
+
if (platform === "darwin")
|
|
16
|
+
return { cmd: "open", args: [href] };
|
|
17
|
+
return { cmd: "xdg-open", args: [href] };
|
|
18
|
+
}
|
|
19
|
+
export function openBrowser(url) {
|
|
20
|
+
const command = browserCommand(url);
|
|
21
|
+
if (!command)
|
|
22
|
+
return;
|
|
23
|
+
try {
|
|
24
|
+
const child = spawn(command.cmd, command.args, {
|
|
25
|
+
stdio: "ignore",
|
|
26
|
+
detached: true,
|
|
27
|
+
windowsHide: true,
|
|
28
|
+
});
|
|
29
|
+
child.on("error", () => undefined);
|
|
30
|
+
child.unref();
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
/* the URL is printed too; opening is best effort */
|
|
34
|
+
}
|
|
14
35
|
}
|
|
15
|
-
function sleep(ms) {
|
|
36
|
+
export function sleep(ms) {
|
|
16
37
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
17
38
|
}
|
|
18
39
|
/** Write token.json + project.json together. Authorize = link. */
|
|
@@ -26,31 +47,26 @@ async function saveVerifiedToken(token, apiUrl) {
|
|
|
26
47
|
const msg = err instanceof Error ? err.message : String(err);
|
|
27
48
|
throw new Error(`Could not verify token: ${msg}`);
|
|
28
49
|
}
|
|
29
|
-
|
|
30
|
-
writeFolderToken({
|
|
31
|
-
token,
|
|
32
|
-
apiUrl,
|
|
33
|
-
cliScope: scope,
|
|
34
|
-
projectId: who.projectId,
|
|
35
|
-
});
|
|
50
|
+
writeFolderToken({ token, apiUrl, projectId: who.project.id });
|
|
36
51
|
const existing = readProjectMeta() ?? {};
|
|
52
|
+
const sameProject = existing.projectId === who.project.id;
|
|
37
53
|
writeProjectMeta({
|
|
38
|
-
...existing,
|
|
39
|
-
projectId: who.
|
|
40
|
-
projectName: who.
|
|
41
|
-
apiUrl,
|
|
54
|
+
...(sameProject ? existing : {}),
|
|
55
|
+
projectId: who.project.id,
|
|
56
|
+
projectName: who.project.name,
|
|
42
57
|
});
|
|
43
58
|
ensureGitignoreHasEnv();
|
|
44
|
-
console.
|
|
59
|
+
console.error(`\n${pc.green("✓")} Signed in. Project: ${pc.bold(who.project.name)}\n`);
|
|
60
|
+
return who;
|
|
45
61
|
}
|
|
46
62
|
async function authorizeViaPaste(apiUrl) {
|
|
47
63
|
const cliPage = `${apiUrl.replace(/\/$/, "")}/cli`;
|
|
48
|
-
console.
|
|
49
|
-
const token = (await readLine("Paste
|
|
64
|
+
console.error(pc.dim(`Create or copy a folder token: ${cliPage}\n`));
|
|
65
|
+
const token = (await readLine("Paste CLI token: ")).trim();
|
|
50
66
|
if (!token) {
|
|
51
67
|
throw new Error("No token entered.");
|
|
52
68
|
}
|
|
53
|
-
|
|
69
|
+
return saveVerifiedToken(token, apiUrl);
|
|
54
70
|
}
|
|
55
71
|
async function authorizeViaBrowser(apiUrl) {
|
|
56
72
|
const client = new ApiClient(apiUrl);
|
|
@@ -60,63 +76,54 @@ async function authorizeViaBrowser(apiUrl) {
|
|
|
60
76
|
}
|
|
61
77
|
catch (err) {
|
|
62
78
|
const msg = err instanceof Error ? err.message : String(err);
|
|
63
|
-
console.
|
|
64
|
-
console.
|
|
65
|
-
|
|
66
|
-
return;
|
|
67
|
-
}
|
|
68
|
-
console.log("Open this URL to authorize the CLI:\n");
|
|
69
|
-
console.log(` ${start.verifyUrl}\n`);
|
|
70
|
-
if (process.stdin.isTTY) {
|
|
71
|
-
openBrowser(start.verifyUrl);
|
|
72
|
-
console.log(pc.dim("Opened in your browser. Waiting...\n"));
|
|
73
|
-
}
|
|
74
|
-
else {
|
|
75
|
-
console.log(pc.dim("Waiting for approval...\n"));
|
|
79
|
+
console.error(pc.yellow(`Browser sign-in unavailable (${msg}).`));
|
|
80
|
+
console.error(pc.dim("Falling back to paste.\n"));
|
|
81
|
+
return authorizeViaPaste(apiUrl);
|
|
76
82
|
}
|
|
83
|
+
const linked = readProjectMeta()?.projectId;
|
|
84
|
+
const verifyUrl = linked
|
|
85
|
+
? `${start.verifyUrl}${start.verifyUrl.includes("?") ? "&" : "?"}project=${encodeURIComponent(linked)}`
|
|
86
|
+
: start.verifyUrl;
|
|
87
|
+
console.error("Open this URL to sign in and pick a project:\n");
|
|
88
|
+
console.error(` ${verifyUrl}\n`);
|
|
89
|
+
console.error(`Check the page shows this code: ${pc.bold(start.userCode)}\n`);
|
|
90
|
+
openBrowser(verifyUrl);
|
|
91
|
+
console.error(pc.dim("Opened in your browser. Waiting...\n"));
|
|
77
92
|
const deadline = new Date(start.expiresAt).getTime();
|
|
78
93
|
while (Date.now() < deadline) {
|
|
79
94
|
let poll;
|
|
80
95
|
try {
|
|
81
|
-
poll = await client.request("GET", `/api/cli-auth/poll?sessionId=${encodeURIComponent(start.sessionId)}
|
|
96
|
+
poll = await client.request("GET", `/api/cli-auth/poll?sessionId=${encodeURIComponent(start.sessionId)}`, undefined, { "X-Getmyenv-Poll-Secret": start.pollSecret });
|
|
82
97
|
}
|
|
83
98
|
catch (err) {
|
|
84
99
|
const msg = err instanceof Error ? err.message : String(err);
|
|
85
|
-
throw new Error(`Could not poll
|
|
100
|
+
throw new Error(`Could not poll sign-in: ${msg}`);
|
|
86
101
|
}
|
|
87
102
|
if (poll.status === "approved" && poll.token) {
|
|
88
|
-
process.
|
|
89
|
-
|
|
90
|
-
return;
|
|
103
|
+
process.stderr.write("\n");
|
|
104
|
+
return saveVerifiedToken(poll.token, apiUrl);
|
|
91
105
|
}
|
|
92
106
|
if (poll.status === "denied") {
|
|
93
|
-
throw new Error("
|
|
107
|
+
throw new Error("Sign-in was denied in the browser.");
|
|
94
108
|
}
|
|
95
109
|
if (poll.status === "expired") {
|
|
96
|
-
throw new Error("
|
|
110
|
+
throw new Error("Sign-in expired. Run the command again.");
|
|
97
111
|
}
|
|
98
112
|
const remaining = Math.max(0, Math.ceil((deadline - Date.now()) / 1000));
|
|
99
|
-
process.
|
|
113
|
+
process.stderr.write(`\r${pc.dim(`Waiting... ${String(remaining).padStart(2, "0")}s`)} `);
|
|
100
114
|
await sleep(2000);
|
|
101
115
|
}
|
|
102
|
-
process.
|
|
103
|
-
throw new Error("
|
|
116
|
+
process.stderr.write("\n");
|
|
117
|
+
throw new Error("Sign-in timed out. Run the command again.");
|
|
104
118
|
}
|
|
105
119
|
/**
|
|
106
|
-
*
|
|
107
|
-
*
|
|
120
|
+
* Sign in via the browser and link this folder (writes token.json + project.json).
|
|
121
|
+
* CI uses GETMYENV_TOKEN instead.
|
|
108
122
|
*/
|
|
109
123
|
export async function authorizeFolder(opts = {}) {
|
|
110
124
|
if (!process.stdin.isTTY) {
|
|
111
|
-
throw new Error("No CLI token in this folder. Set GETMYENV_TOKEN for CI, or run in a
|
|
112
|
-
}
|
|
113
|
-
const apiUrl = opts.apiUrl?.trim() ||
|
|
114
|
-
process.env.GETMYENV_API_URL?.trim() ||
|
|
115
|
-
resolveApiUrl(opts.apiUrl);
|
|
116
|
-
const hasMeta = Boolean(readProjectMeta()?.projectId);
|
|
117
|
-
if (!hasMeta) {
|
|
118
|
-
console.log(pc.dim("No project in this folder yet. AI scaffolding without an account: npx getmyenv guest --name <project>\n"));
|
|
125
|
+
throw new Error("No CLI token in this folder. Set GETMYENV_TOKEN for CI, or run in a terminal.");
|
|
119
126
|
}
|
|
120
|
-
|
|
127
|
+
return authorizeViaBrowser(resolveApiUrl(opts.apiUrl));
|
|
121
128
|
}
|
|
122
129
|
//# sourceMappingURL=authorize.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"authorize.js","sourceRoot":"","sources":["../../src/lib/authorize.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"authorize.js","sourceRoot":"","sources":["../../src/lib/authorize.ts"],"names":[],"mappings":"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"}
|
package/dist/lib/bootstrap.d.ts
CHANGED
|
@@ -1,19 +1,22 @@
|
|
|
1
|
-
import { type
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
/** Verb name (unused for prompts; kept for callers). */
|
|
5
|
-
action: "push" | "pull" | "run" | "backup" | "restore";
|
|
6
|
-
};
|
|
7
|
-
/**
|
|
8
|
-
* Ensure this folder is authorized and linked.
|
|
9
|
-
* Token write also syncs project.json. If meta is missing or stale on name/env,
|
|
10
|
-
* refresh silently from whoami. No "Link and run?" confirm.
|
|
11
|
-
*/
|
|
12
|
-
export declare function ensureTokenAndLink(opts: BootstrapOpts): Promise<{
|
|
1
|
+
import { type ProjectMeta } from "./fs.js";
|
|
2
|
+
import { ApiClient, type WhoamiResponse } from "./api.js";
|
|
3
|
+
export type Linked = {
|
|
13
4
|
who: WhoamiResponse;
|
|
14
5
|
apiUrl: string;
|
|
15
6
|
token: string;
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
}
|
|
7
|
+
client: ApiClient;
|
|
8
|
+
meta: ProjectMeta | null;
|
|
9
|
+
};
|
|
10
|
+
export declare const NOT_LINKED_STEPS: string;
|
|
11
|
+
export declare function printGuestBanner(who: WhoamiResponse): void;
|
|
12
|
+
export declare function fetchWhoami(client: ApiClient): Promise<WhoamiResponse>;
|
|
13
|
+
/**
|
|
14
|
+
* Make sure this folder has a token, then load whoami.
|
|
15
|
+
* No token: a terminal runs guided setup; anything else gets next steps.
|
|
16
|
+
* project.json is refreshed from whoami (name, id) and keeps its context.
|
|
17
|
+
*/
|
|
18
|
+
export declare function ensureTokenAndLink(opts?: {
|
|
19
|
+
apiUrl?: string;
|
|
20
|
+
quiet?: boolean;
|
|
21
|
+
}): Promise<Linked>;
|
|
19
22
|
//# sourceMappingURL=bootstrap.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bootstrap.d.ts","sourceRoot":"","sources":["../../src/lib/bootstrap.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"bootstrap.d.ts","sourceRoot":"","sources":["../../src/lib/bootstrap.ts"],"names":[],"mappings":"AACA,OAAO,EAKL,KAAK,WAAW,EACjB,MAAM,SAAS,CAAC;AACjB,OAAO,EACL,SAAS,EAIT,KAAK,cAAc,EACpB,MAAM,UAAU,CAAC;AAElB,MAAM,MAAM,MAAM,GAAG;IACnB,GAAG,EAAE,cAAc,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,SAAS,CAAC;IAClB,IAAI,EAAE,WAAW,GAAG,IAAI,CAAC;CAC1B,CAAC;AAEF,eAAO,MAAM,gBAAgB,QAKjB,CAAC;AAEb,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,cAAc,GAAG,IAAI,CAkB1D;AAED,wBAAsB,WAAW,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,cAAc,CAAC,CAgB5E;AAED;;;;GAIG;AACH,wBAAsB,kBAAkB,CAAC,IAAI,GAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAsCzG"}
|
package/dist/lib/bootstrap.js
CHANGED
|
@@ -1,92 +1,82 @@
|
|
|
1
1
|
import pc from "picocolors";
|
|
2
2
|
import { ensureGitignoreHasEnv, readClaimFile, readProjectMeta, writeProjectMeta, } from "./fs.js";
|
|
3
|
-
import { ApiClient, ApiError,
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
import { ApiClient, ApiError, getSessionToken, resolveApiUrl, } from "./api.js";
|
|
4
|
+
export const NOT_LINKED_STEPS = [
|
|
5
|
+
"This folder is not linked to a getmyenv project.",
|
|
6
|
+
"Start in a terminal: npx getmyenv",
|
|
7
|
+
"Without an account: npx getmyenv guest",
|
|
8
|
+
"In CI: set GETMYENV_TOKEN",
|
|
9
|
+
].join("\n");
|
|
10
|
+
export function printGuestBanner(who) {
|
|
7
11
|
if (!who.isGuest)
|
|
8
12
|
return;
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
const needClaim = claim ? "" : " Need .getmyenv/claim.json.";
|
|
18
|
-
console.log(pc.yellow(`Guest. Expires in ~${days}. Claim: npx getmyenv claim.${needClaim}\n`));
|
|
19
|
-
}
|
|
20
|
-
/**
|
|
21
|
-
* Ensure this folder is authorized and linked.
|
|
22
|
-
* Token write also syncs project.json. If meta is missing or stale on name/env,
|
|
23
|
-
* refresh silently from whoami. No "Link and run?" confirm.
|
|
24
|
-
*/
|
|
25
|
-
export async function ensureTokenAndLink(opts) {
|
|
26
|
-
if (!hasSessionToken()) {
|
|
27
|
-
if (!process.stdin.isTTY) {
|
|
28
|
-
throw new Error("No CLI token in this folder. Set GETMYENV_TOKEN for CI, or run in a TTY.");
|
|
29
|
-
}
|
|
30
|
-
await authorizeFolder({ apiUrl: opts.apiUrl });
|
|
13
|
+
const DAY = 24 * 60 * 60 * 1000;
|
|
14
|
+
const expires = who.guestExpiresAt ? new Date(who.guestExpiresAt).getTime() : NaN;
|
|
15
|
+
const needClaim = readClaimFile() ? "" : " Needs .getmyenv/claim.json.";
|
|
16
|
+
const plural = (n) => (n === 1 ? "1 day" : `${n} days`);
|
|
17
|
+
if (Number.isFinite(expires) && expires <= Date.now()) {
|
|
18
|
+
const graceLeft = Math.max(0, Math.ceil((expires + 30 * DAY - Date.now()) / DAY));
|
|
19
|
+
console.error(pc.yellow(`Guest project expired. Read-only for ${plural(graceLeft)}. Export or claim it: npx getmyenv claim.${needClaim}\n`));
|
|
20
|
+
return;
|
|
31
21
|
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
22
|
+
const daysLeft = Number.isFinite(expires) ? Math.max(0, Math.ceil((expires - Date.now()) / DAY)) : null;
|
|
23
|
+
const days = daysLeft == null ? "soon" : plural(daysLeft);
|
|
24
|
+
console.error(pc.yellow(`Guest project. Expires in ${days}. Keep it: npx getmyenv claim.${needClaim}\n`));
|
|
25
|
+
}
|
|
26
|
+
export async function fetchWhoami(client) {
|
|
37
27
|
try {
|
|
38
|
-
|
|
28
|
+
return await client.request("GET", "/api/cli/whoami");
|
|
39
29
|
}
|
|
40
30
|
catch (err) {
|
|
41
31
|
if (err instanceof ApiError && err.status === 410) {
|
|
42
|
-
throw new Error(
|
|
32
|
+
throw new Error(err.message);
|
|
33
|
+
}
|
|
34
|
+
if (err instanceof ApiError && err.status === 401) {
|
|
35
|
+
throw new Error(process.env.GETMYENV_TOKEN?.trim()
|
|
36
|
+
? `${err.message}\nGETMYENV_TOKEN no longer works. Create a new token on the CLI page.`
|
|
37
|
+
: `${err.message}\nThis folder's token no longer works. Run: npx getmyenv unlink, then npx getmyenv`);
|
|
43
38
|
}
|
|
44
39
|
throw err;
|
|
45
40
|
}
|
|
46
|
-
|
|
47
|
-
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Make sure this folder has a token, then load whoami.
|
|
44
|
+
* No token: a terminal runs guided setup; anything else gets next steps.
|
|
45
|
+
* project.json is refreshed from whoami (name, id) and keeps its context.
|
|
46
|
+
*/
|
|
47
|
+
export async function ensureTokenAndLink(opts = {}) {
|
|
48
|
+
if (!getSessionToken()) {
|
|
49
|
+
if (!process.stdin.isTTY) {
|
|
50
|
+
throw new Error(NOT_LINKED_STEPS);
|
|
51
|
+
}
|
|
52
|
+
const { startSetup } = await import("../commands/start.js");
|
|
53
|
+
await startSetup({ apiUrl: opts.apiUrl, thenContinue: true });
|
|
54
|
+
}
|
|
55
|
+
const token = getSessionToken();
|
|
56
|
+
if (!token)
|
|
57
|
+
throw new Error(NOT_LINKED_STEPS);
|
|
58
|
+
const apiUrl = resolveApiUrl(opts.apiUrl);
|
|
59
|
+
const client = new ApiClient(apiUrl, token);
|
|
60
|
+
const who = await fetchWhoami(client);
|
|
61
|
+
if (!opts.quiet)
|
|
62
|
+
printGuestBanner(who);
|
|
63
|
+
let meta = readProjectMeta();
|
|
64
|
+
if (meta?.projectId && meta.projectId !== who.project.id) {
|
|
48
65
|
throw new Error([
|
|
49
|
-
`
|
|
50
|
-
"Run: npx getmyenv
|
|
66
|
+
`project.json points at ${meta.projectName ?? meta.projectId}, but the token is for ${who.project.name}.`,
|
|
67
|
+
"Run: npx getmyenv unlink (then run the command again)",
|
|
51
68
|
].join("\n"));
|
|
52
69
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
if (folder?.projectName) {
|
|
56
|
-
writeFolderToken({
|
|
57
|
-
token: folder.token,
|
|
58
|
-
cliScope: folder.cliScope,
|
|
59
|
-
projectId: folder.projectId,
|
|
60
|
-
apiUrl: folder.apiUrl,
|
|
61
|
-
addedAt: folder.addedAt,
|
|
62
|
-
});
|
|
63
|
-
}
|
|
64
|
-
// Drop legacy project.json.environment when present (cliScope is source of truth).
|
|
65
|
-
const needsSync = !meta?.projectId ||
|
|
66
|
-
meta.projectName !== who.projectName ||
|
|
67
|
-
meta.environment != null;
|
|
68
|
-
if (needsSync) {
|
|
70
|
+
const usingEnvToken = Boolean(process.env.GETMYENV_TOKEN?.trim());
|
|
71
|
+
if (!usingEnvToken && (!meta?.projectId || meta.projectName !== who.project.name)) {
|
|
69
72
|
writeProjectMeta({
|
|
70
73
|
...(meta ?? {}),
|
|
71
|
-
projectId: who.
|
|
72
|
-
projectName: who.
|
|
73
|
-
apiUrl,
|
|
74
|
+
projectId: who.project.id,
|
|
75
|
+
projectName: who.project.name,
|
|
74
76
|
});
|
|
75
77
|
ensureGitignoreHasEnv();
|
|
76
78
|
meta = readProjectMeta();
|
|
77
79
|
}
|
|
78
|
-
|
|
79
|
-
throw new Error([
|
|
80
|
-
"No project is linked to this folder.",
|
|
81
|
-
"Re-run this command to authorize, or set GETMYENV_TOKEN for CI.",
|
|
82
|
-
].join("\n"));
|
|
83
|
-
}
|
|
84
|
-
return {
|
|
85
|
-
who,
|
|
86
|
-
apiUrl,
|
|
87
|
-
token,
|
|
88
|
-
projectId: String(meta.projectId),
|
|
89
|
-
projectName: meta.projectName ?? who.projectName,
|
|
90
|
-
};
|
|
80
|
+
return { who, apiUrl, token, client, meta };
|
|
91
81
|
}
|
|
92
82
|
//# sourceMappingURL=bootstrap.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bootstrap.js","sourceRoot":"","sources":["../../src/lib/bootstrap.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,YAAY,CAAC;AAC5B,OAAO,EACL,qBAAqB,EACrB,aAAa,EACb,eAAe,EACf,gBAAgB,
|
|
1
|
+
{"version":3,"file":"bootstrap.js","sourceRoot":"","sources":["../../src/lib/bootstrap.ts"],"names":[],"mappings":"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"}
|
package/dist/lib/confirm.js
CHANGED
|
@@ -8,14 +8,14 @@ export async function confirmDiff(opts) {
|
|
|
8
8
|
const { conflicts, action, yes } = opts;
|
|
9
9
|
if (conflicts.length === 0)
|
|
10
10
|
return true;
|
|
11
|
-
console.
|
|
11
|
+
console.error(pc.yellow(`\n${conflicts.length} conflict(s):`));
|
|
12
12
|
for (const c of conflicts.slice(0, 20)) {
|
|
13
|
-
console.
|
|
13
|
+
console.error(` ${c.name}${c.detail ? pc.dim(` - ${c.detail}`) : ""}`);
|
|
14
14
|
}
|
|
15
15
|
if (conflicts.length > 20) {
|
|
16
|
-
console.
|
|
16
|
+
console.error(pc.dim(` …and ${conflicts.length - 20} more`));
|
|
17
17
|
}
|
|
18
|
-
console.
|
|
18
|
+
console.error("");
|
|
19
19
|
if (yes)
|
|
20
20
|
return true;
|
|
21
21
|
if (!process.stdin.isTTY) {
|
package/dist/lib/confirm.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"confirm.js","sourceRoot":"","sources":["../../src/lib/confirm.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,YAAY,CAAC;AAC5B,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAQxC;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,IAMjC;IACC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;IACxC,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAExC,OAAO,CAAC,
|
|
1
|
+
{"version":3,"file":"confirm.js","sourceRoot":"","sources":["../../src/lib/confirm.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,YAAY,CAAC;AAC5B,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAQxC;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,IAMjC;IACC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;IACxC,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAExC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,MAAM,CAAC,KAAK,SAAS,CAAC,MAAM,eAAe,CAAC,CAAC,CAAC;IAC/D,KAAK,MAAM,CAAC,IAAI,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC;QACvC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAC1E,CAAC;IACD,IAAI,SAAS,CAAC,MAAM,GAAG,EAAE,EAAE,CAAC;QAC1B,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,GAAG,CAAC,UAAU,SAAS,CAAC,MAAM,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC;IAChE,CAAC;IACD,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAElB,IAAI,GAAG;QAAE,OAAO,IAAI,CAAC;IAErB,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;QACzB,MAAM,IAAI,KAAK,CACb,wDAAwD,MAAM,CAAC,WAAW,EAAE,GAAG,CAChF,CAAC;IACJ,CAAC;IAED,OAAO,aAAa,CAAC,GAAG,MAAM,GAAG,EAAE,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC,CAAC;AAC5D,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ContextInfo, WhoamiResponse } from "./api.js";
|
|
2
|
+
import type { ProjectMeta } from "./fs.js";
|
|
3
|
+
/**
|
|
4
|
+
* Default context for this folder:
|
|
5
|
+
* server token's bound context, then project.json `context`, then `development`.
|
|
6
|
+
*/
|
|
7
|
+
export declare function defaultContextSlug(who: Pick<WhoamiResponse, "token" | "contexts">, meta: ProjectMeta | null): string;
|
|
8
|
+
/** Find a context by slug, or by name ("Customer Acme" finds customer-acme). */
|
|
9
|
+
export declare function findContext(contexts: ContextInfo[], input: string): ContextInfo | null;
|
|
10
|
+
/** The context a command acts on. Throws with the available slugs when unknown. */
|
|
11
|
+
export declare function resolveContext(who: Pick<WhoamiResponse, "token" | "contexts">, meta: ProjectMeta | null, explicit?: string): ContextInfo;
|
|
12
|
+
//# sourceMappingURL=context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/lib/context.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAC5D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAE3C;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,IAAI,CAAC,cAAc,EAAE,OAAO,GAAG,UAAU,CAAC,EAC/C,IAAI,EAAE,WAAW,GAAG,IAAI,GACvB,MAAM,CAMR;AAED,gFAAgF;AAChF,wBAAgB,WAAW,CACzB,QAAQ,EAAE,WAAW,EAAE,EACvB,KAAK,EAAE,MAAM,GACZ,WAAW,GAAG,IAAI,CAUpB;AAED,mFAAmF;AACnF,wBAAgB,cAAc,CAC5B,GAAG,EAAE,IAAI,CAAC,cAAc,EAAE,OAAO,GAAG,UAAU,CAAC,EAC/C,IAAI,EAAE,WAAW,GAAG,IAAI,EACxB,QAAQ,CAAC,EAAE,MAAM,GAChB,WAAW,CAab"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { DEFAULT_CONTEXT_SLUG, slugify } from "@getmyenv/shared";
|
|
2
|
+
/**
|
|
3
|
+
* Default context for this folder:
|
|
4
|
+
* server token's bound context, then project.json `context`, then `development`.
|
|
5
|
+
*/
|
|
6
|
+
export function defaultContextSlug(who, meta) {
|
|
7
|
+
if (who.token.kind === "server" && who.token.context)
|
|
8
|
+
return who.token.context;
|
|
9
|
+
const slugs = new Set(who.contexts.map((c) => c.slug));
|
|
10
|
+
if (meta?.context && slugs.has(meta.context))
|
|
11
|
+
return meta.context;
|
|
12
|
+
if (slugs.has(DEFAULT_CONTEXT_SLUG))
|
|
13
|
+
return DEFAULT_CONTEXT_SLUG;
|
|
14
|
+
return who.contexts[0]?.slug ?? DEFAULT_CONTEXT_SLUG;
|
|
15
|
+
}
|
|
16
|
+
/** Find a context by slug, or by name ("Customer Acme" finds customer-acme). */
|
|
17
|
+
export function findContext(contexts, input) {
|
|
18
|
+
const raw = input.trim();
|
|
19
|
+
if (!raw)
|
|
20
|
+
return null;
|
|
21
|
+
const exact = contexts.find((c) => c.slug === raw);
|
|
22
|
+
if (exact)
|
|
23
|
+
return exact;
|
|
24
|
+
const lower = raw.toLowerCase();
|
|
25
|
+
const byName = contexts.find((c) => c.name.toLowerCase() === lower);
|
|
26
|
+
if (byName)
|
|
27
|
+
return byName;
|
|
28
|
+
const slug = slugify(raw);
|
|
29
|
+
return contexts.find((c) => c.slug === slug) ?? null;
|
|
30
|
+
}
|
|
31
|
+
/** The context a command acts on. Throws with the available slugs when unknown. */
|
|
32
|
+
export function resolveContext(who, meta, explicit) {
|
|
33
|
+
const wanted = explicit?.trim() || defaultContextSlug(who, meta);
|
|
34
|
+
const found = findContext(who.contexts, wanted);
|
|
35
|
+
if (found)
|
|
36
|
+
return found;
|
|
37
|
+
if (who.token.kind === "server") {
|
|
38
|
+
throw new Error(`This server token reads ${who.token.context ?? "one locked context"} only.`);
|
|
39
|
+
}
|
|
40
|
+
const available = who.contexts.map((c) => c.slug).join(", ") || "none";
|
|
41
|
+
throw new Error(`No context "${wanted}". Available: ${available}\nCreate one: npx getmyenv context create "${wanted}"`);
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/lib/context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAIjE;;;GAGG;AACH,MAAM,UAAU,kBAAkB,CAChC,GAA+C,EAC/C,IAAwB;IAExB,IAAI,GAAG,CAAC,KAAK,CAAC,IAAI,KAAK,QAAQ,IAAI,GAAG,CAAC,KAAK,CAAC,OAAO;QAAE,OAAO,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC;IAC/E,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IACvD,IAAI,IAAI,EAAE,OAAO,IAAI,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC;QAAE,OAAO,IAAI,CAAC,OAAO,CAAC;IAClE,IAAI,KAAK,CAAC,GAAG,CAAC,oBAAoB,CAAC;QAAE,OAAO,oBAAoB,CAAC;IACjE,OAAO,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,IAAI,IAAI,oBAAoB,CAAC;AACvD,CAAC;AAED,gFAAgF;AAChF,MAAM,UAAU,WAAW,CACzB,QAAuB,EACvB,KAAa;IAEb,MAAM,GAAG,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IACzB,IAAI,CAAC,GAAG;QAAE,OAAO,IAAI,CAAC;IACtB,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,GAAG,CAAC,CAAC;IACnD,IAAI,KAAK;QAAE,OAAO,KAAK,CAAC;IACxB,MAAM,KAAK,GAAG,GAAG,CAAC,WAAW,EAAE,CAAC;IAChC,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,KAAK,KAAK,CAAC,CAAC;IACpE,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC;IAC1B,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;IAC1B,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,IAAI,IAAI,CAAC;AACvD,CAAC;AAED,mFAAmF;AACnF,MAAM,UAAU,cAAc,CAC5B,GAA+C,EAC/C,IAAwB,EACxB,QAAiB;IAEjB,MAAM,MAAM,GAAG,QAAQ,EAAE,IAAI,EAAE,IAAI,kBAAkB,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IACjE,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IAChD,IAAI,KAAK;QAAE,OAAO,KAAK,CAAC;IACxB,IAAI,GAAG,CAAC,KAAK,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QAChC,MAAM,IAAI,KAAK,CACb,2BAA2B,GAAG,CAAC,KAAK,CAAC,OAAO,IAAI,oBAAoB,QAAQ,CAC7E,CAAC;IACJ,CAAC;IACD,MAAM,SAAS,GAAG,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,MAAM,CAAC;IACvE,MAAM,IAAI,KAAK,CACb,eAAe,MAAM,iBAAiB,SAAS,8CAA8C,MAAM,GAAG,CACvG,CAAC;AACJ,CAAC"}
|
|
@@ -1,27 +1,22 @@
|
|
|
1
|
-
export type CliScope = "development" | "production";
|
|
2
1
|
/**
|
|
3
|
-
* Per-folder token stored in `.getmyenv/token.json
|
|
4
|
-
* One folder = one project = one token.
|
|
5
|
-
*
|
|
2
|
+
* Per-folder token stored in `.getmyenv/token.json` (gitignored).
|
|
3
|
+
* One folder = one project = one token. What the token can reach is decided
|
|
4
|
+
* by the server (folder, guest or server token), not by this file.
|
|
6
5
|
*/
|
|
7
6
|
export type FolderToken = {
|
|
8
|
-
version:
|
|
7
|
+
version: 2;
|
|
9
8
|
token: string;
|
|
10
|
-
cliScope: CliScope;
|
|
11
9
|
apiUrl?: string;
|
|
12
10
|
projectId: string;
|
|
13
|
-
/** Legacy; tolerated on read, no longer written. */
|
|
14
|
-
projectName?: string;
|
|
15
|
-
/** Legacy field; no longer written. Tolerated on read. */
|
|
16
|
-
label?: string;
|
|
17
11
|
addedAt: string;
|
|
18
12
|
};
|
|
19
13
|
export declare function tokenFilePath(cwd?: string): string;
|
|
20
14
|
export declare function readFolderToken(cwd?: string): FolderToken | null;
|
|
21
|
-
export declare function writeFolderToken(entry:
|
|
15
|
+
export declare function writeFolderToken(entry: {
|
|
16
|
+
token: string;
|
|
17
|
+
apiUrl?: string;
|
|
18
|
+
projectId: string;
|
|
22
19
|
addedAt?: string;
|
|
23
|
-
/** Ignored; project name lives in project.json. */
|
|
24
|
-
projectName?: string;
|
|
25
20
|
}, cwd?: string): string;
|
|
26
21
|
export declare function clearFolderToken(cwd?: string): boolean;
|
|
27
22
|
//# sourceMappingURL=credentials.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"credentials.d.ts","sourceRoot":"","sources":["../../src/lib/credentials.ts"],"names":[],"mappings":"AAKA
|
|
1
|
+
{"version":3,"file":"credentials.d.ts","sourceRoot":"","sources":["../../src/lib/credentials.ts"],"names":[],"mappings":"AAKA;;;;GAIG;AACH,MAAM,MAAM,WAAW,GAAG;IACxB,OAAO,EAAE,CAAC,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,wBAAgB,aAAa,CAAC,GAAG,SAAgB,GAAG,MAAM,CAEzD;AAED,wBAAgB,eAAe,CAAC,GAAG,SAAgB,GAAG,WAAW,GAAG,IAAI,CA4BvE;AAED,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,EAC9E,GAAG,SAAgB,GAClB,MAAM,CAeR;AAED,wBAAgB,gBAAgB,CAAC,GAAG,SAAgB,GAAG,OAAO,CAK7D"}
|
package/dist/lib/credentials.js
CHANGED
|
@@ -24,19 +24,13 @@ export function readFolderToken(cwd = process.cwd()) {
|
|
|
24
24
|
if (typeof d.projectId !== "string" && typeof d.projectId !== "number") {
|
|
25
25
|
return null;
|
|
26
26
|
}
|
|
27
|
-
const scope = d.cliScope === "production" ? "production" : "development";
|
|
28
27
|
return {
|
|
29
|
-
version:
|
|
28
|
+
version: 2,
|
|
30
29
|
token: d.token.trim(),
|
|
31
|
-
cliScope: scope,
|
|
32
30
|
apiUrl: typeof d.apiUrl === "string" && d.apiUrl.trim()
|
|
33
31
|
? d.apiUrl.trim()
|
|
34
32
|
: undefined,
|
|
35
33
|
projectId: String(d.projectId),
|
|
36
|
-
...(typeof d.projectName === "string" && d.projectName.trim()
|
|
37
|
-
? { projectName: d.projectName.trim() }
|
|
38
|
-
: {}),
|
|
39
|
-
label: typeof d.label === "string" && d.label.trim() ? d.label.trim() : undefined,
|
|
40
34
|
addedAt: typeof d.addedAt === "string" && d.addedAt
|
|
41
35
|
? d.addedAt
|
|
42
36
|
: new Date().toISOString(),
|
|
@@ -47,9 +41,8 @@ export function writeFolderToken(entry, cwd = process.cwd()) {
|
|
|
47
41
|
fs.mkdirSync(path.dirname(file), { recursive: true, mode: 0o700 });
|
|
48
42
|
const diskApi = apiUrlForDisk(entry.apiUrl);
|
|
49
43
|
const payload = {
|
|
50
|
-
version:
|
|
44
|
+
version: 2,
|
|
51
45
|
token: entry.token.trim(),
|
|
52
|
-
cliScope: entry.cliScope,
|
|
53
46
|
projectId: String(entry.projectId),
|
|
54
47
|
...(diskApi ? { apiUrl: diskApi } : {}),
|
|
55
48
|
addedAt: entry.addedAt ?? new Date().toISOString(),
|