@spfn/cli 0.0.1 → 0.0.2
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/lib/login.js +69 -25
- package/package.json +2 -2
package/lib/login.js
CHANGED
@@ -6,10 +6,33 @@ import path from "path";
|
|
6
6
|
|
7
7
|
const PORT = 5678;
|
8
8
|
|
9
|
+
/**
|
10
|
+
* templates 폴더에서 templateName 파일을 읽고,
|
11
|
+
* {{KEY}} 플레이스홀더를 vars[KEY]로 치환하여 outputPath에 씁니다.
|
12
|
+
*/
|
13
|
+
function applyTemplate(
|
14
|
+
templateName,
|
15
|
+
outputPath,
|
16
|
+
vars
|
17
|
+
) {
|
18
|
+
const src = path.join(process.cwd(), "templates", templateName);
|
19
|
+
if (!fs.existsSync(src)) {
|
20
|
+
throw new Error(`Template not found: ${src}`);
|
21
|
+
}
|
22
|
+
const template = fs.readFileSync(src, "utf-8");
|
23
|
+
const result = template.replace(/\{\{(\w+)\}\}/g, (_, key) =>
|
24
|
+
vars[key] ?? ""
|
25
|
+
);
|
26
|
+
|
27
|
+
fs.mkdirSync(path.dirname(outputPath), { recursive: true });
|
28
|
+
fs.writeFileSync(outputPath, result, "utf-8");
|
29
|
+
console.log(`✔️ Applied template ${templateName} → ${outputPath}`);
|
30
|
+
}
|
31
|
+
|
9
32
|
export function login() {
|
10
33
|
try {
|
11
34
|
const server = http.createServer((req, res) => {
|
12
|
-
// CORS
|
35
|
+
// CORS headers
|
13
36
|
res.setHeader("Access-Control-Allow-Origin", "*");
|
14
37
|
res.setHeader("Access-Control-Allow-Methods", "POST, OPTIONS");
|
15
38
|
res.setHeader("Access-Control-Allow-Headers", "Content-Type");
|
@@ -22,47 +45,68 @@ export function login() {
|
|
22
45
|
|
23
46
|
if (req.url === "/callback" && req.method === "POST") {
|
24
47
|
let body = "";
|
25
|
-
|
26
|
-
req.on("data", chunk => {
|
27
|
-
body += chunk;
|
28
|
-
});
|
29
|
-
|
48
|
+
req.on("data", (chunk) => (body += chunk));
|
30
49
|
req.on("end", () => {
|
31
50
|
try {
|
32
51
|
const { username, token, projectSlug } = JSON.parse(body);
|
33
|
-
|
34
|
-
if (!token || !username) {
|
52
|
+
if (!username || !token) {
|
35
53
|
res.writeHead(400).end("Missing username or token");
|
36
54
|
return;
|
37
55
|
}
|
38
56
|
|
39
|
-
|
40
|
-
|
41
|
-
const
|
42
|
-
|
57
|
+
// 1) 템플릿 적용
|
58
|
+
// 1-1) Gradle init
|
59
|
+
const gradleDest = path.join(
|
60
|
+
os.homedir(),
|
61
|
+
".gradle",
|
62
|
+
"init.gradle"
|
63
|
+
);
|
64
|
+
applyTemplate("gradle.tmpl", gradleDest, {
|
65
|
+
SF_USERNAME: username,
|
66
|
+
SF_AUTH_TOKEN: token,
|
67
|
+
});
|
43
68
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
69
|
+
// 1-2) npmrc
|
70
|
+
const npmDest = path.join(os.homedir(), ".npmrc");
|
71
|
+
applyTemplate("npm.tmpl", npmDest, {
|
72
|
+
SF_AUTH_TOKEN: token,
|
73
|
+
});
|
74
|
+
if (process.platform !== "win32") {
|
75
|
+
try {
|
76
|
+
fs.chmodSync(npmDest, 0o600);
|
77
|
+
} catch {}
|
78
|
+
}
|
48
79
|
|
49
|
-
|
50
|
-
|
80
|
+
// 1-3) netrc (_netrc on Windows)
|
81
|
+
const netrcName =
|
82
|
+
process.platform === "win32" ? "_netrc" : ".netrc";
|
83
|
+
const netrcDest = path.join(os.homedir(), netrcName);
|
84
|
+
applyTemplate("netrc.tmpl", netrcDest, {
|
85
|
+
SF_USERNAME: username,
|
86
|
+
SF_AUTH_TOKEN: token,
|
87
|
+
});
|
88
|
+
if (process.platform !== "win32") {
|
89
|
+
fs.chmodSync(netrcDest, 0o600);
|
90
|
+
}
|
51
91
|
|
92
|
+
// 3) 응답 & 팁
|
52
93
|
res.writeHead(200, { "Content-Type": "text/plain" });
|
53
94
|
res.end("✅ Login successful! You can now close this window.");
|
54
95
|
|
55
|
-
console.log("✔️ Login approved via browser.");
|
56
|
-
console.log(`✔️ Credentials updated in ~/${shell}`);
|
57
|
-
console.log(`👉 Run: source ~/${shell}`);
|
58
96
|
console.log();
|
59
|
-
console.log(
|
60
|
-
console.log(
|
97
|
+
console.log("💡 Tips:");
|
98
|
+
console.log(
|
99
|
+
` git clone git.superfunctions.ai/${projectSlug}/${projectSlug}-server`
|
100
|
+
);
|
101
|
+
console.log(
|
102
|
+
` git clone git.superfunctions.ai/${projectSlug}/${projectSlug}-app`
|
103
|
+
);
|
104
|
+
console.log();
|
61
105
|
|
62
106
|
server.close();
|
63
107
|
} catch (err) {
|
64
|
-
res.writeHead(500).end("Invalid request format");
|
65
108
|
console.error(err);
|
109
|
+
res.writeHead(500).end("Error during login callback");
|
66
110
|
server.close();
|
67
111
|
}
|
68
112
|
});
|
@@ -73,7 +117,7 @@ export function login() {
|
|
73
117
|
|
74
118
|
server.listen(PORT, () => {
|
75
119
|
console.log("🌐 Opening browser for login...");
|
76
|
-
open(`
|
120
|
+
open(`https://console.superfunctions.ai/cli-login?port=${PORT}`);
|
77
121
|
});
|
78
122
|
} catch (err) {
|
79
123
|
console.error(err);
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@spfn/cli",
|
3
|
-
"version": "0.0.
|
3
|
+
"version": "0.0.2",
|
4
4
|
"description": "Superfunction CLI",
|
5
5
|
"keywords": [
|
6
6
|
"cli",
|
@@ -9,7 +9,7 @@
|
|
9
9
|
"devtools",
|
10
10
|
"automation"
|
11
11
|
],
|
12
|
-
"homepage": "https://superfunctions.ai",
|
12
|
+
"homepage": "https://console.superfunctions.ai",
|
13
13
|
"repository": {
|
14
14
|
"type": "git",
|
15
15
|
"url": "https://git.superfunctions.ai/sf/sf-cli.git"
|