@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.
Files changed (2) hide show
  1. package/lib/login.js +69 -25
  2. 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
- const shell = process.env.SHELL?.includes("zsh") ? ".zshrc" : ".bashrc";
40
- const rcFile = path.join(os.homedir(), shell);
41
- const exportUsername = `export SF_USERNAME=${username}`;
42
- const exportToken = `export SF_AUTH_TOKEN=${token}`;
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
- let content = fs.readFileSync(rcFile, "utf-8");
45
- content = content
46
- .replace(/^# Added by sf-cli[\s\S]*?export SF_USERNAME=.*\n?/m, "")
47
- .replace(/^export SF_AUTH_TOKEN=.*\n?/m, "");
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
- content += `\n# Added by sf-cli\n${exportUsername}\n${exportToken}\n`;
50
- fs.writeFileSync(rcFile, content, "utf-8");
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(`💡 Tip: Run \`git clone git.superfunctions.ai/${projectSlug}/${projectSlug}-server\` to start your server project.`);
60
- console.log(`💡 Tip: Run \`git clone git.superfunctions.ai/${projectSlug}/${projectSlug}-app\` to start your app project.`);
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(`http://localhost:3000/cli-login?port=${PORT}`);
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.1",
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"