@suronai/cli 2.0.2 → 2.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/cli.js +51 -40
- package/package.json +4 -4
- package/suron +49 -24
package/cli.js
CHANGED
|
@@ -46,8 +46,8 @@ const c = {
|
|
|
46
46
|
|
|
47
47
|
// ── Logo ──────────────────────────────────────────────────────────────────────
|
|
48
48
|
const logo = `
|
|
49
|
-
${c.orange("
|
|
50
|
-
${c.orange("
|
|
49
|
+
${c.orange("▄▀▀ █ █ █▀▄ █▀█ █▄ █")}
|
|
50
|
+
${c.orange("▀▀▄ ▀▄█ █▀▄ █▄█ █ ▀█")} ${c.gray("secrets manager · v2")}
|
|
51
51
|
`;
|
|
52
52
|
|
|
53
53
|
// ── Config ────────────────────────────────────────────────────────────────────
|
|
@@ -75,7 +75,7 @@ function getConfig() {
|
|
|
75
75
|
function requireConfig() {
|
|
76
76
|
const cfg = getConfig();
|
|
77
77
|
if (!cfg.url) {
|
|
78
|
-
console.error(c.red("✗ SURON_URL not set — run: suron
|
|
78
|
+
console.error(c.red("✗ SURON_URL not set — run: suron login"));
|
|
79
79
|
process.exit(1);
|
|
80
80
|
}
|
|
81
81
|
return cfg;
|
|
@@ -154,10 +154,10 @@ function printHelp() {
|
|
|
154
154
|
console.log(logo);
|
|
155
155
|
const sections = [
|
|
156
156
|
{ heading: "Setup", cmds: [
|
|
157
|
-
["suron
|
|
158
|
-
["suron login", "", "Authenticate CLI via browser (opens dashboard)"],
|
|
157
|
+
["suron login", "", "Authenticate CLI — asks for URLs + opens approval page in browser"],
|
|
159
158
|
["suron logout", "", "Remove saved credentials"],
|
|
160
159
|
["suron whoami", "", "Show current auth status"],
|
|
160
|
+
["suron init", "", "(legacy) — just runs: suron login"],
|
|
161
161
|
]},
|
|
162
162
|
{ heading: "Scaffold", cmds: [
|
|
163
163
|
["suron new <directory>", "", "Scaffold a new Node.js project with SURON pre-wired"],
|
|
@@ -196,57 +196,65 @@ function printHelp() {
|
|
|
196
196
|
const cfg = getConfig();
|
|
197
197
|
console.log(` ${c.gray("Config:")} ${cfg.url ? c.cyan(cfg.url) : c.red("not set — run: suron init")}`);
|
|
198
198
|
console.log(` ${c.gray("Version:")} ${c.gray(VERSION)}`);
|
|
199
|
-
console.log(` ${c.gray("Docs: ")} ${c.cyan("https://github.com/
|
|
199
|
+
console.log(` ${c.gray("Docs: ")} ${c.cyan("https://github.com/Cryptoistaken/Suron")}\n`);
|
|
200
200
|
}
|
|
201
201
|
|
|
202
202
|
// ── INIT ──────────────────────────────────────────────────────────────────────
|
|
203
203
|
async function cmdInit() {
|
|
204
204
|
console.log(logo);
|
|
205
|
-
console.log(`${c.bold("Setup Wizard")}
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
const dashUrl = await prompt(`Dashboard URL ${ex.SURON_DASHBOARD_URL ? c.gray("("+ex.SURON_DASHBOARD_URL+")") : c.gray("e.g. https://suron.vercel.app")}: `);
|
|
210
|
-
|
|
211
|
-
const cfg = {
|
|
212
|
-
...ex,
|
|
213
|
-
...(url ? { SURON_URL: url } : {}),
|
|
214
|
-
...(dashUrl ? { SURON_DASHBOARD_URL: dashUrl } : {}),
|
|
215
|
-
};
|
|
216
|
-
saveConfig(cfg);
|
|
217
|
-
console.log(c.green(`\n✓ Config saved`));
|
|
218
|
-
console.log(c.gray(` SURON_URL = ${cfg.SURON_URL}`));
|
|
219
|
-
if (cfg.SURON_DASHBOARD_URL) console.log(c.gray(` SURON_DASHBOARD_URL = ${cfg.SURON_DASHBOARD_URL}`));
|
|
220
|
-
console.log(`\n${c.dim(" Next: suron login or suron apps create --name my-app")}\n`);
|
|
205
|
+
console.log(`${c.bold("Setup Wizard")}\n`);
|
|
206
|
+
console.log(c.gray(" suron init is no longer needed for URL setup."));
|
|
207
|
+
console.log(c.gray(" Run ") + c.cyan("suron login") + c.gray(" — it will ask for your Convex URL and Dashboard URL automatically.\n"));
|
|
208
|
+
console.log(c.dim(" Or run: suron whoami to check current status\n"));
|
|
221
209
|
}
|
|
222
210
|
|
|
223
211
|
// ── LOGIN ─────────────────────────────────────────────────────────────────────
|
|
224
212
|
async function cmdLogin() {
|
|
225
|
-
const { url } = requireConfig();
|
|
226
|
-
const client = getClient();
|
|
227
213
|
console.log(logo);
|
|
228
214
|
console.log(`${c.bold("CLI Authentication")}\n`);
|
|
229
215
|
|
|
216
|
+
// ── Step 1: ensure Convex URL is set
|
|
217
|
+
let cfg = loadConfig();
|
|
218
|
+
let convexUrl = process.env.SURON_URL ?? cfg.SURON_URL;
|
|
219
|
+
if (!convexUrl) {
|
|
220
|
+
console.log(c.gray(" No Convex URL found. Let's set it up.\n"));
|
|
221
|
+
convexUrl = await prompt(` Convex URL ${c.gray("(e.g. https://xxx.eu-west-1.convex.cloud)")}: `);
|
|
222
|
+
if (!convexUrl) { console.error(c.red("✗ Convex URL is required")); await new Promise(r => setTimeout(r, 50)); process.exit(1); }
|
|
223
|
+
cfg = { ...cfg, SURON_URL: convexUrl };
|
|
224
|
+
saveConfig(cfg);
|
|
225
|
+
console.log(c.green(" ✓ Convex URL saved\n"));
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// ── Step 2: ensure Dashboard URL is set
|
|
229
|
+
let dashUrl = process.env.SURON_DASHBOARD_URL ?? cfg.SURON_DASHBOARD_URL;
|
|
230
|
+
if (!dashUrl) {
|
|
231
|
+
console.log(c.gray(" No Dashboard URL found. Where is your SURON dashboard deployed?\n"));
|
|
232
|
+
dashUrl = await prompt(` Dashboard URL ${c.gray("(e.g. https://suron.vercel.app)")}: `);
|
|
233
|
+
if (!dashUrl) { console.error(c.red("✗ Dashboard URL is required")); await new Promise(r => setTimeout(r, 50)); process.exit(1); }
|
|
234
|
+
cfg = { ...cfg, SURON_DASHBOARD_URL: dashUrl };
|
|
235
|
+
saveConfig(cfg);
|
|
236
|
+
console.log(c.green(" ✓ Dashboard URL saved\n"));
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// ── Step 3: create session and open browser
|
|
240
|
+
const client = new ConvexHttpClient(convexUrl);
|
|
230
241
|
let sessionId, code;
|
|
231
242
|
try {
|
|
232
243
|
const r = await client.mutation("auth:createCliSession", {});
|
|
233
244
|
sessionId = r.sessionId; code = r.code;
|
|
234
|
-
} catch(e) { console.error(c.red(`✗ ${e.message}`)); process.exit(1); }
|
|
245
|
+
} catch(e) { console.error(c.red(`✗ ${e.message}`)); await new Promise(r => setTimeout(r, 50)); process.exit(1); }
|
|
235
246
|
|
|
236
|
-
const
|
|
237
|
-
if (!dashUrl) {
|
|
238
|
-
console.error(c.red("✗ SURON_DASHBOARD_URL not set — run: suron init"));
|
|
239
|
-
process.exit(1);
|
|
240
|
-
}
|
|
247
|
+
const approvalUrl = `${dashUrl.replace(/\/$/, "")}/approve?code=${code}`;
|
|
241
248
|
|
|
242
|
-
|
|
243
|
-
console.log(` ${c.
|
|
244
|
-
console.log(` ${c.
|
|
245
|
-
console.log(c.dim(" Press Enter to open in browser
|
|
249
|
+
console.log(` ${c.bold("Your code")}: ${c.orange(c.bold(code))}\n`);
|
|
250
|
+
console.log(` ${c.gray("Opening approval page in browser…")}`);
|
|
251
|
+
console.log(` ${c.dim(approvalUrl)}\n`);
|
|
252
|
+
console.log(c.dim(" Press Enter to open in browser, or visit the URL above manually."));
|
|
246
253
|
await prompt("");
|
|
247
254
|
openBrowser(approvalUrl);
|
|
248
|
-
console.log(c.gray("\n Waiting for
|
|
255
|
+
console.log(c.gray("\n Waiting for approval… (Ctrl+C to cancel)\n"));
|
|
249
256
|
|
|
257
|
+
// ── Step 4: poll
|
|
250
258
|
const deadline = Date.now() + 10 * 60 * 1000;
|
|
251
259
|
let dots = 0;
|
|
252
260
|
while (Date.now() < deadline) {
|
|
@@ -257,20 +265,23 @@ async function cmdLogin() {
|
|
|
257
265
|
process.stdout.write(`\r ${c.gray("Waiting" + ".".repeat(dots).padEnd(3))}`);
|
|
258
266
|
if (result.status === "approved" && result.adminToken) {
|
|
259
267
|
process.stdout.write("\n");
|
|
260
|
-
const cfg = loadConfig();
|
|
261
268
|
cfg.SURON_ADMIN_TOKEN = result.adminToken;
|
|
262
269
|
saveConfig(cfg);
|
|
263
270
|
console.log(`\n${c.green("✓ Authenticated!")} Token saved to ${c.cyan(CONFIG_FILE)}\n`);
|
|
271
|
+
await new Promise(r => setTimeout(r, 50));
|
|
264
272
|
process.exit(0);
|
|
265
273
|
}
|
|
266
274
|
if (result.status === "denied") {
|
|
267
|
-
|
|
275
|
+
process.stdout.write("\n"); console.log(c.red("✗ Denied."));
|
|
276
|
+
await new Promise(r => setTimeout(r, 50)); process.exit(1);
|
|
268
277
|
}
|
|
269
278
|
if (["expired","not_found"].includes(result.status)) {
|
|
270
|
-
|
|
279
|
+
process.stdout.write("\n"); console.log(c.red("✗ Session expired. Run: suron login"));
|
|
280
|
+
await new Promise(r => setTimeout(r, 50)); process.exit(1);
|
|
271
281
|
}
|
|
272
282
|
}
|
|
273
|
-
|
|
283
|
+
process.stdout.write("\n"); console.log(c.red("✗ Timed out."));
|
|
284
|
+
await new Promise(r => setTimeout(r, 50)); process.exit(1);
|
|
274
285
|
}
|
|
275
286
|
|
|
276
287
|
// ── WHOAMI ────────────────────────────────────────────────────────────────────
|
|
@@ -731,7 +742,7 @@ try {
|
|
|
731
742
|
case "--help":
|
|
732
743
|
case "-h": printHelp(); break;
|
|
733
744
|
case "version": console.log(`suron v${VERSION}`); break;
|
|
734
|
-
case "init": await
|
|
745
|
+
case "init": await cmdLogin(); break; // init now just runs login
|
|
735
746
|
case "login": await cmdLogin(); break;
|
|
736
747
|
case "logout": cmdLogout(); break;
|
|
737
748
|
case "whoami": await cmdWhoami(); break;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@suronai/cli",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.3",
|
|
4
4
|
"description": "SURON CLI — manage secrets, push .env files, scaffold Node.js projects with Telegram approval",
|
|
5
5
|
"main": "cli.js",
|
|
6
6
|
"type": "module",
|
|
@@ -26,14 +26,14 @@
|
|
|
26
26
|
],
|
|
27
27
|
"license": "MIT",
|
|
28
28
|
"author": "SuronAI",
|
|
29
|
-
"homepage": "https://github.com/
|
|
29
|
+
"homepage": "https://github.com/Cryptoistaken/Suron",
|
|
30
30
|
"repository": {
|
|
31
31
|
"type": "git",
|
|
32
|
-
"url": "git+https://github.com/
|
|
32
|
+
"url": "git+https://github.com/Cryptoistaken/Suron.git",
|
|
33
33
|
"directory": "SDK/CLI"
|
|
34
34
|
},
|
|
35
35
|
"bugs": {
|
|
36
|
-
"url": "https://github.com/
|
|
36
|
+
"url": "https://github.com/Cryptoistaken/Suron/issues"
|
|
37
37
|
},
|
|
38
38
|
"files": [
|
|
39
39
|
"suron",
|
package/suron
CHANGED
|
@@ -203,50 +203,69 @@ function printHelp() {
|
|
|
203
203
|
async function cmdInit() {
|
|
204
204
|
console.log(logo);
|
|
205
205
|
console.log(`${c.bold("Setup Wizard")} — saves config to ${c.cyan(CONFIG_FILE)}\n`);
|
|
206
|
+
console.log(c.dim(" Tip: suron login will also ask for these URLs if not set.\n"));
|
|
206
207
|
const ex = loadConfig();
|
|
207
208
|
|
|
208
|
-
const url
|
|
209
|
-
const dashUrl = await prompt(`Dashboard URL ${ex.SURON_DASHBOARD_URL ? c.gray("("+ex.SURON_DASHBOARD_URL+")") : c.gray("e.g. https://suron.vercel.app")}: `);
|
|
209
|
+
const url = await prompt(`Convex URL ${ex.SURON_URL ? c.gray("("+ex.SURON_URL+")") : c.gray("e.g. https://xxx.convex.cloud")}: `);
|
|
210
210
|
|
|
211
211
|
const cfg = {
|
|
212
212
|
...ex,
|
|
213
|
-
...(url
|
|
214
|
-
...(dashUrl ? { SURON_DASHBOARD_URL: dashUrl } : {}),
|
|
213
|
+
...(url ? { SURON_URL: url } : {}),
|
|
215
214
|
};
|
|
216
215
|
saveConfig(cfg);
|
|
217
216
|
console.log(c.green(`\n✓ Config saved`));
|
|
218
|
-
console.log(c.gray(` SURON_URL
|
|
219
|
-
|
|
220
|
-
console.log(`\n${c.dim(" Next: suron login or suron apps create --name my-app")}\n`);
|
|
217
|
+
console.log(c.gray(` SURON_URL = ${cfg.SURON_URL ?? c.red("(not set)")}`));
|
|
218
|
+
console.log(`\n${c.dim(" Next: suron login")}\n`);
|
|
221
219
|
}
|
|
222
220
|
|
|
223
221
|
// ── LOGIN ─────────────────────────────────────────────────────────────────────
|
|
224
222
|
async function cmdLogin() {
|
|
225
|
-
const { url } = requireConfig();
|
|
226
|
-
const client = getClient();
|
|
227
223
|
console.log(logo);
|
|
228
224
|
console.log(`${c.bold("CLI Authentication")}\n`);
|
|
229
225
|
|
|
226
|
+
// ── Step 1: ensure Convex URL is set ────────────────────────────────────────
|
|
227
|
+
let cfg = loadConfig();
|
|
228
|
+
let convexUrl = process.env.SURON_URL ?? cfg.SURON_URL;
|
|
229
|
+
if (!convexUrl) {
|
|
230
|
+
console.log(c.gray(" No Convex URL found. Let's set it up.\n"));
|
|
231
|
+
convexUrl = await prompt(` Convex URL ${c.gray("(e.g. https://xxx.eu-west-1.convex.cloud)")}: `);
|
|
232
|
+
if (!convexUrl) { console.error(c.red("✗ Convex URL is required")); await new Promise(r => setTimeout(r, 50)); process.exit(1); }
|
|
233
|
+
cfg = { ...cfg, SURON_URL: convexUrl };
|
|
234
|
+
saveConfig(cfg);
|
|
235
|
+
console.log(c.green(" ✓ Convex URL saved\n"));
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// ── Step 2: ensure Dashboard URL is set ─────────────────────────────────────
|
|
239
|
+
let dashUrl = process.env.SURON_DASHBOARD_URL ?? cfg.SURON_DASHBOARD_URL;
|
|
240
|
+
if (!dashUrl) {
|
|
241
|
+
console.log(c.gray(" No Dashboard URL found. Where is your SURON dashboard deployed?\n"));
|
|
242
|
+
dashUrl = await prompt(` Dashboard URL ${c.gray("(e.g. https://suron.vercel.app)")}: `);
|
|
243
|
+
if (!dashUrl) { console.error(c.red("✗ Dashboard URL is required")); await new Promise(r => setTimeout(r, 50)); process.exit(1); }
|
|
244
|
+
cfg = { ...cfg, SURON_DASHBOARD_URL: dashUrl };
|
|
245
|
+
saveConfig(cfg);
|
|
246
|
+
console.log(c.green(" ✓ Dashboard URL saved\n"));
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// ── Step 3: create session and open browser ──────────────────────────────────
|
|
250
|
+
const client = new ConvexHttpClient(convexUrl);
|
|
230
251
|
let sessionId, code;
|
|
231
252
|
try {
|
|
232
253
|
const r = await client.mutation("auth:createCliSession", {});
|
|
233
254
|
sessionId = r.sessionId; code = r.code;
|
|
234
|
-
} catch(e) { console.error(c.red(`✗ ${e.message}`)); process.exit(1); }
|
|
255
|
+
} catch(e) { console.error(c.red(`✗ ${e.message}`)); await new Promise(r => setTimeout(r, 50)); process.exit(1); }
|
|
235
256
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
console.error(c.red("✗ SURON_DASHBOARD_URL not set — run: suron init"));
|
|
239
|
-
process.exit(1);
|
|
240
|
-
}
|
|
257
|
+
// Open a clean standalone approval page (not the full dashboard)
|
|
258
|
+
const approvalUrl = `${dashUrl.replace(/\/$/, "")}/approve?code=${code}`;
|
|
241
259
|
|
|
242
|
-
|
|
243
|
-
console.log(` ${c.
|
|
244
|
-
console.log(` ${c.
|
|
245
|
-
console.log(c.dim(" Press Enter to open in browser
|
|
260
|
+
console.log(` ${c.bold("Your code")}: ${c.orange(c.bold(code))}\n`);
|
|
261
|
+
console.log(` ${c.gray("Opening approval page in browser…")}`);
|
|
262
|
+
console.log(` ${c.dim(approvalUrl)}\n`);
|
|
263
|
+
console.log(c.dim(" Press Enter to open in browser, or visit the URL above manually."));
|
|
246
264
|
await prompt("");
|
|
247
265
|
openBrowser(approvalUrl);
|
|
248
|
-
console.log(c.gray("\n Waiting for
|
|
266
|
+
console.log(c.gray("\n Waiting for approval… (Ctrl+C to cancel)\n"));
|
|
249
267
|
|
|
268
|
+
// ── Step 4: poll until approved/denied/expired ───────────────────────────────
|
|
250
269
|
const deadline = Date.now() + 10 * 60 * 1000;
|
|
251
270
|
let dots = 0;
|
|
252
271
|
while (Date.now() < deadline) {
|
|
@@ -257,20 +276,26 @@ async function cmdLogin() {
|
|
|
257
276
|
process.stdout.write(`\r ${c.gray("Waiting" + ".".repeat(dots).padEnd(3))}`);
|
|
258
277
|
if (result.status === "approved" && result.adminToken) {
|
|
259
278
|
process.stdout.write("\n");
|
|
260
|
-
const cfg = loadConfig();
|
|
261
279
|
cfg.SURON_ADMIN_TOKEN = result.adminToken;
|
|
262
280
|
saveConfig(cfg);
|
|
263
281
|
console.log(`\n${c.green("✓ Authenticated!")} Token saved to ${c.cyan(CONFIG_FILE)}\n`);
|
|
282
|
+
await new Promise(r => setTimeout(r, 50));
|
|
264
283
|
process.exit(0);
|
|
265
284
|
}
|
|
266
285
|
if (result.status === "denied") {
|
|
267
|
-
|
|
286
|
+
process.stdout.write("\n");
|
|
287
|
+
console.log(c.red("✗ Denied."));
|
|
288
|
+
await new Promise(r => setTimeout(r, 50)); process.exit(1);
|
|
268
289
|
}
|
|
269
290
|
if (["expired","not_found"].includes(result.status)) {
|
|
270
|
-
|
|
291
|
+
process.stdout.write("\n");
|
|
292
|
+
console.log(c.red("✗ Session expired. Run: suron login"));
|
|
293
|
+
await new Promise(r => setTimeout(r, 50)); process.exit(1);
|
|
271
294
|
}
|
|
272
295
|
}
|
|
273
|
-
|
|
296
|
+
process.stdout.write("\n");
|
|
297
|
+
console.log(c.red("✗ Timed out."));
|
|
298
|
+
await new Promise(r => setTimeout(r, 50)); process.exit(1);
|
|
274
299
|
}
|
|
275
300
|
|
|
276
301
|
// ── WHOAMI ────────────────────────────────────────────────────────────────────
|