foldrun 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 +202 -0
- package/README.md +149 -0
- package/bin/foldrun.mjs +165 -0
- package/package.json +43 -0
- package/src/commands.mjs +1780 -0
- package/src/credentials.mjs +76 -0
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
// Where `foldrun login` puts what it was given.
|
|
2
|
+
//
|
|
3
|
+
// `~/.foldrun/credentials.json`, 0600, one entry per platform URL, and a
|
|
4
|
+
// `default` naming the one a bare command talks to — the same shape every
|
|
5
|
+
// CLI that signs in keeps, so the person who has used Vercel's or Fly's
|
|
6
|
+
// finds nothing to learn. FOLDRUN_HOME moves the directory, for tests and
|
|
7
|
+
// for machines where HOME is not where things should be kept.
|
|
8
|
+
//
|
|
9
|
+
// The value stored is an ordinary API key, minted at approval and listed on
|
|
10
|
+
// Settings → API keys with the machine's name. Nothing here is a second
|
|
11
|
+
// kind of credential; `--token` and FOLDRUN_TOKEN still win over it, so a
|
|
12
|
+
// CI job with a key in the environment never reads a file.
|
|
13
|
+
|
|
14
|
+
import fs from "node:fs";
|
|
15
|
+
import os from "node:os";
|
|
16
|
+
import path from "node:path";
|
|
17
|
+
|
|
18
|
+
export const credentialsDir = () => process.env.FOLDRUN_HOME ?? path.join(os.homedir(), ".foldrun");
|
|
19
|
+
const file = () => path.join(credentialsDir(), "credentials.json");
|
|
20
|
+
|
|
21
|
+
/** A platform URL as the map keys it: scheme and host, no trailing slash. */
|
|
22
|
+
export const normaliseUrl = (url) => {
|
|
23
|
+
const u = new URL(url);
|
|
24
|
+
return `${u.protocol}//${u.host}${u.pathname.replace(/\/+$/, "")}`;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export function readCredentials() {
|
|
28
|
+
try {
|
|
29
|
+
const parsed = JSON.parse(fs.readFileSync(file(), "utf8"));
|
|
30
|
+
return { default: parsed.default ?? null, platforms: parsed.platforms ?? {} };
|
|
31
|
+
} catch {
|
|
32
|
+
return { default: null, platforms: {} };
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function writeCredentials(creds) {
|
|
37
|
+
fs.mkdirSync(credentialsDir(), { recursive: true, mode: 0o700 });
|
|
38
|
+
fs.writeFileSync(file(), JSON.stringify(creds, null, 2) + "\n", { mode: 0o600 });
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Remember a platform; it becomes the default unless one is already set
|
|
42
|
+
* and the caller says to leave it. */
|
|
43
|
+
export function saveCredential(url, entry, { makeDefault = true } = {}) {
|
|
44
|
+
const key = normaliseUrl(url);
|
|
45
|
+
const creds = readCredentials();
|
|
46
|
+
creds.platforms[key] = { ...entry, loggedInAt: new Date().toISOString() };
|
|
47
|
+
if (makeDefault || !creds.default) creds.default = key;
|
|
48
|
+
writeCredentials(creds);
|
|
49
|
+
return key;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Forget a platform. Returns what was there, for a best-effort revoke. */
|
|
53
|
+
export function removeCredential(url) {
|
|
54
|
+
const key = normaliseUrl(url);
|
|
55
|
+
const creds = readCredentials();
|
|
56
|
+
const gone = creds.platforms[key] ?? null;
|
|
57
|
+
delete creds.platforms[key];
|
|
58
|
+
if (creds.default === key) creds.default = Object.keys(creds.platforms)[0] ?? null;
|
|
59
|
+
writeCredentials(creds);
|
|
60
|
+
return gone;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** The stored entry for a URL, or null. */
|
|
64
|
+
export function credentialFor(url) {
|
|
65
|
+
if (!url) return null;
|
|
66
|
+
try {
|
|
67
|
+
return readCredentials().platforms[normaliseUrl(url)] ?? null;
|
|
68
|
+
} catch {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** The platform a bare command talks to: the last one signed in to. */
|
|
74
|
+
export function defaultPlatform() {
|
|
75
|
+
return readCredentials().default;
|
|
76
|
+
}
|