novacloud22-cli 1.0.1 → 1.0.3
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/index.js +60 -56
- package/package.json +2 -2
package/index.js
CHANGED
|
@@ -86,74 +86,78 @@ program
|
|
|
86
86
|
.description('Authenticate with your NovaCloud22 account')
|
|
87
87
|
.action(async () => {
|
|
88
88
|
banner()
|
|
89
|
-
const spinner = ora('Starting authentication...').start()
|
|
90
89
|
|
|
91
|
-
//
|
|
92
|
-
const
|
|
93
|
-
|
|
94
|
-
const token = url.searchParams.get('token')
|
|
90
|
+
// Generate unique state ID for polling
|
|
91
|
+
const state = require('crypto').randomBytes(16).toString('hex')
|
|
92
|
+
const loginUrl = `${FRONTEND_URL}/cli-auth?state=${state}`
|
|
95
93
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
94
|
+
console.log(chalk.bold('\n NovaCloud22 CLI — Login\n'))
|
|
95
|
+
console.log(chalk.gray(' ' + '─'.repeat(50)))
|
|
96
|
+
console.log(chalk.cyan('\n Login URL:'))
|
|
97
|
+
console.log(chalk.blue.underline(` ${loginUrl}\n`))
|
|
98
|
+
console.log(chalk.gray(' ' + '─'.repeat(50)))
|
|
99
|
+
|
|
100
|
+
const { action } = await inquirer.prompt([{
|
|
101
|
+
type: 'list',
|
|
102
|
+
name: 'action',
|
|
103
|
+
message: 'What would you like to do?',
|
|
104
|
+
choices: [
|
|
105
|
+
{ name: '🌐 Open in browser', value: 'open' },
|
|
106
|
+
{ name: '📋 Copy link', value: 'copy' },
|
|
107
|
+
{ name: '✕ Cancel', value: 'cancel' },
|
|
108
|
+
]
|
|
109
|
+
}])
|
|
110
|
+
|
|
111
|
+
if (action === 'cancel') {
|
|
112
|
+
console.log(chalk.gray('\n Login cancelled.\n'))
|
|
113
|
+
process.exit(0)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (action === 'open') {
|
|
117
|
+
await open(loginUrl)
|
|
118
|
+
console.log(chalk.gray('\n Browser opened. Complete sign in there.\n'))
|
|
119
|
+
} else if (action === 'copy') {
|
|
120
|
+
const { execSync } = require('child_process')
|
|
121
|
+
try {
|
|
122
|
+
const platform = process.platform
|
|
123
|
+
if (platform === 'darwin') execSync(`echo '${loginUrl}' | pbcopy`)
|
|
124
|
+
else if (platform === 'win32') execSync(`echo ${loginUrl} | clip`)
|
|
125
|
+
else execSync(`echo '${loginUrl}' | xclip -selection clipboard`)
|
|
126
|
+
console.log(chalk.green('\n ✓ Link copied! Paste it in your browser.\n'))
|
|
127
|
+
} catch {
|
|
128
|
+
console.log(chalk.yellow('\n Copy this link manually:'))
|
|
129
|
+
console.log(chalk.blue.underline(` ${loginUrl}\n`))
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Poll backend every 2s until token received (max 5 min)
|
|
134
|
+
const spinner = ora('Waiting for authentication...').start()
|
|
135
|
+
const maxWait = 150
|
|
136
|
+
let waited = 0
|
|
137
|
+
|
|
138
|
+
while (waited < maxWait) {
|
|
139
|
+
await new Promise(r => setTimeout(r, 2000))
|
|
140
|
+
waited++
|
|
141
|
+
try {
|
|
142
|
+
const res = await axios.get(`${API_BASE}/cli-auth/poll?state=${state}`)
|
|
143
|
+
if (res.data.ready && res.data.token) {
|
|
144
|
+
const token = res.data.token
|
|
99
145
|
const verifyRes = await axios.get(`${API_BASE}/user/profile`, {
|
|
100
146
|
headers: { Authorization: `Bearer ${token}` }
|
|
101
147
|
})
|
|
102
148
|
saveToken(token)
|
|
103
|
-
res.writeHead(200, { 'Content-Type': 'text/html' })
|
|
104
|
-
res.end(`
|
|
105
|
-
<html><body style="font-family:sans-serif;text-align:center;padding:3rem;background:#0f0c29;color:white">
|
|
106
|
-
<h2>✅ Logged in successfully!</h2>
|
|
107
|
-
<p>You can close this tab and return to the terminal.</p>
|
|
108
|
-
<p style="color:rgba(255,255,255,0.5);font-size:0.9rem">NovaCloud22 CLI</p>
|
|
109
|
-
</body></html>
|
|
110
|
-
`)
|
|
111
|
-
server.close()
|
|
112
149
|
spinner.succeed(chalk.green(`Logged in as ${verifyRes.data.email}`))
|
|
113
150
|
console.log(chalk.gray('\n Run novacloud22 init to set up your project\n'))
|
|
114
|
-
|
|
115
|
-
res.writeHead(400, { 'Content-Type': 'text/html' })
|
|
116
|
-
res.end('<html><body>Authentication failed. Please try again.</body></html>')
|
|
117
|
-
server.close()
|
|
118
|
-
spinner.fail('Authentication failed')
|
|
151
|
+
process.exit(0)
|
|
119
152
|
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
res.end('No token received')
|
|
153
|
+
} catch {
|
|
154
|
+
// keep polling
|
|
123
155
|
}
|
|
124
|
-
})
|
|
125
|
-
|
|
126
|
-
server.listen(9876, async () => {
|
|
127
|
-
spinner.text = 'Opening browser...'
|
|
128
|
-
const loginUrl = `${FRONTEND_URL}/signin?cli=1&callback=http://localhost:9876`
|
|
129
|
-
await open(loginUrl)
|
|
130
|
-
spinner.text = 'Waiting for login in browser...'
|
|
131
|
-
})
|
|
132
|
-
|
|
133
|
-
server.on('error', () => {
|
|
134
|
-
spinner.fail('Could not start local server on port 9876')
|
|
135
|
-
console.log(chalk.yellow('\n Manual login: Go to ' + FRONTEND_URL + '/signin'))
|
|
136
|
-
console.log(chalk.yellow(' Then run: novacloud22 login --token YOUR_TOKEN\n'))
|
|
137
|
-
})
|
|
138
|
-
})
|
|
139
|
-
|
|
140
|
-
// LOGIN WITH TOKEN (manual fallback)
|
|
141
|
-
program
|
|
142
|
-
.command('login:token <token>')
|
|
143
|
-
.description('Login with a token directly')
|
|
144
|
-
.action(async (token) => {
|
|
145
|
-
const spinner = ora('Verifying token...').start()
|
|
146
|
-
try {
|
|
147
|
-
const res = await axios.get(`${API_BASE}/user/profile`, {
|
|
148
|
-
headers: { Authorization: `Bearer ${token}` }
|
|
149
|
-
})
|
|
150
|
-
saveToken(token)
|
|
151
|
-
spinner.succeed(chalk.green(`Logged in as ${res.data.email}`))
|
|
152
|
-
} catch {
|
|
153
|
-
spinner.fail('Invalid token')
|
|
154
156
|
}
|
|
155
|
-
})
|
|
156
157
|
|
|
158
|
+
spinner.fail('Login timed out. Please try again.')
|
|
159
|
+
process.exit(1)
|
|
160
|
+
})
|
|
157
161
|
// INIT
|
|
158
162
|
program
|
|
159
163
|
.command('init')
|
package/package.json
CHANGED