@suronai/cli 0.1.40 → 0.1.41
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/package.json +1 -1
- package/src/commands.js +325 -67
- package/src/index.js +2 -1
package/package.json
CHANGED
package/src/commands.js
CHANGED
|
@@ -1,24 +1,29 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
|
-
import {
|
|
2
|
+
import { spinner, select, text, isCancel } from "@clack/prompts";
|
|
3
3
|
import { existsSync, readFileSync, writeFileSync } from "fs";
|
|
4
|
-
import { join } from "path";
|
|
5
|
-
import { spawn } from "child_process";
|
|
4
|
+
import { join, basename, relative } from "path";
|
|
5
|
+
import { spawn, execSync } from "child_process";
|
|
6
6
|
import ui from "./utils/colors.js";
|
|
7
7
|
import { getToken, requireToken, saveConfig, clearToken } from "./utils/config.js";
|
|
8
8
|
import { api, BASE_URL } from "./utils/api.js";
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
// ── Shared helpers ────────────────────────────────────────────────────────────
|
|
11
|
+
|
|
12
|
+
function cancel(msg, hint) {
|
|
11
13
|
console.log(ui.blank());
|
|
12
|
-
console.log(ui.errBlock(msg));
|
|
14
|
+
console.log(ui.errBlock(msg, hint));
|
|
13
15
|
console.log(ui.blank());
|
|
14
16
|
process.exit(1);
|
|
15
17
|
}
|
|
16
18
|
|
|
19
|
+
function sanitiseName(name) {
|
|
20
|
+
return name.replace(/[^a-zA-Z0-9]/g, "");
|
|
21
|
+
}
|
|
22
|
+
|
|
17
23
|
function openBrowser(url) {
|
|
18
|
-
|
|
19
|
-
if (platform === "win32") {
|
|
24
|
+
if (process.platform === "win32") {
|
|
20
25
|
spawn("cmd", ["/c", "start", "", url], { detached: true, stdio: "ignore" }).unref();
|
|
21
|
-
} else if (platform === "darwin") {
|
|
26
|
+
} else if (process.platform === "darwin") {
|
|
22
27
|
spawn("open", [url], { detached: true, stdio: "ignore" }).unref();
|
|
23
28
|
} else {
|
|
24
29
|
spawn("xdg-open", [url], { detached: true, stdio: "ignore" }).unref();
|
|
@@ -41,6 +46,190 @@ function waitForEnter(prompt) {
|
|
|
41
46
|
});
|
|
42
47
|
}
|
|
43
48
|
|
|
49
|
+
function detectPackageManager(cwd) {
|
|
50
|
+
if (existsSync(join(cwd, "bun.lockb")) || existsSync(join(cwd, "bun.lock"))) return "bun";
|
|
51
|
+
if (existsSync(join(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
52
|
+
if (existsSync(join(cwd, "yarn.lock"))) return "yarn";
|
|
53
|
+
return "npm";
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function pmAddCmd(pm) {
|
|
57
|
+
return pm === "npm" ? "install" : "add";
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// ── .gitignore ────────────────────────────────────────────────────────────────
|
|
61
|
+
|
|
62
|
+
const GITIGNORE_ENTRIES = [".env", ".suron.json"];
|
|
63
|
+
|
|
64
|
+
function ensureGitignore(cwd) {
|
|
65
|
+
const gitignorePath = join(cwd, ".gitignore");
|
|
66
|
+
if (!existsSync(gitignorePath)) {
|
|
67
|
+
writeFileSync(gitignorePath, GITIGNORE_ENTRIES.join("\n") + "\n", "utf-8");
|
|
68
|
+
return "created";
|
|
69
|
+
}
|
|
70
|
+
const content = readFileSync(gitignorePath, "utf-8");
|
|
71
|
+
const existing = new Set(content.split("\n").map(l => l.trim()));
|
|
72
|
+
const missing = GITIGNORE_ENTRIES.filter(e => !existing.has(e));
|
|
73
|
+
if (missing.length === 0) return "ok";
|
|
74
|
+
const sep = content.endsWith("\n") ? "" : "\n";
|
|
75
|
+
writeFileSync(gitignorePath, content + sep + missing.join("\n") + "\n", "utf-8");
|
|
76
|
+
return `added ${missing.join(", ")}`;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// ── SDK install ───────────────────────────────────────────────────────────────
|
|
80
|
+
|
|
81
|
+
function installSdk(cwd) {
|
|
82
|
+
const pkgJsonPath = join(cwd, "package.json");
|
|
83
|
+
if (!existsSync(pkgJsonPath)) return { status: "skip", note: "no package.json" };
|
|
84
|
+
|
|
85
|
+
let alreadyInstalled = false;
|
|
86
|
+
let isEsm = false;
|
|
87
|
+
try {
|
|
88
|
+
const pkg = JSON.parse(readFileSync(pkgJsonPath, "utf-8"));
|
|
89
|
+
alreadyInstalled = !!(pkg.dependencies?.["@suronai/sdk"] || pkg.devDependencies?.["@suronai/sdk"]);
|
|
90
|
+
isEsm = pkg.type === "module";
|
|
91
|
+
} catch {
|
|
92
|
+
return { status: "skip", note: "could not read package.json" };
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (alreadyInstalled) return { status: "skip", note: "already installed", isEsm };
|
|
96
|
+
|
|
97
|
+
const pm = detectPackageManager(cwd);
|
|
98
|
+
try {
|
|
99
|
+
execSync(`${pm} ${pmAddCmd(pm)} @suronai/sdk`, { cwd, stdio: "pipe" });
|
|
100
|
+
return { status: "done", isEsm };
|
|
101
|
+
} catch {
|
|
102
|
+
return { status: "fail", note: `run: npm install @suronai/sdk`, isEsm };
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// ── Entry-point patching ──────────────────────────────────────────────────────
|
|
107
|
+
|
|
108
|
+
async function patchEntryPoint(cwd, isEsm) {
|
|
109
|
+
const candidates = isEsm
|
|
110
|
+
? ["index.js", "index.mjs", "src/index.js", "src/index.mjs"]
|
|
111
|
+
: ["index.js", "index.cjs", "src/index.js", "src/index.cjs"];
|
|
112
|
+
|
|
113
|
+
let entryPath = null;
|
|
114
|
+
for (const rel of candidates) {
|
|
115
|
+
const abs = join(cwd, rel);
|
|
116
|
+
if (existsSync(abs)) { entryPath = abs; break; }
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (!entryPath) { printSnippet(isEsm); return; }
|
|
120
|
+
|
|
121
|
+
let src;
|
|
122
|
+
try { src = readFileSync(entryPath, "utf-8"); } catch { return; }
|
|
123
|
+
|
|
124
|
+
const lines = src.split("\n");
|
|
125
|
+
const toReplace = [];
|
|
126
|
+
const seenIndices = new Set();
|
|
127
|
+
|
|
128
|
+
for (let i = 0; i < lines.length; i++) {
|
|
129
|
+
const trimmed = lines[i].trim();
|
|
130
|
+
const indent = lines[i].match(/^(\s*)/)[1];
|
|
131
|
+
|
|
132
|
+
if (lines[i].includes("dotenv")) {
|
|
133
|
+
seenIndices.add(i);
|
|
134
|
+
let replacement = null;
|
|
135
|
+
if (isEsm) {
|
|
136
|
+
if (trimmed.startsWith("import")) {
|
|
137
|
+
replacement = indent + trimmed.replace(/(from\s+)['"]dotenv(?:\/config)?['"]/, '$1"@suronai/sdk"');
|
|
138
|
+
}
|
|
139
|
+
} else {
|
|
140
|
+
if (trimmed.includes("require")) {
|
|
141
|
+
replacement = indent + 'const { config } = require("@suronai/sdk");\n' + indent + "await config();";
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
toReplace.push({ index: i, content: lines[i], replacement });
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (isEsm) {
|
|
149
|
+
for (let i = 0; i < lines.length; i++) {
|
|
150
|
+
if (seenIndices.has(i)) continue;
|
|
151
|
+
const trimmed = lines[i].trim();
|
|
152
|
+
const indent = lines[i].match(/^(\s*)/)[1];
|
|
153
|
+
if (/^config\s*\(.*\);?$/.test(trimmed) && !trimmed.startsWith("//")) {
|
|
154
|
+
toReplace.push({
|
|
155
|
+
index: i,
|
|
156
|
+
content: lines[i],
|
|
157
|
+
replacement: indent + trimmed.replace(/^config\s*\(/, "await config("),
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
toReplace.sort((a, b) => a.index - b.index);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if (toReplace.length === 0) { printSnippet(isEsm); return; }
|
|
165
|
+
|
|
166
|
+
const relEntry = relative(cwd, entryPath);
|
|
167
|
+
|
|
168
|
+
console.log(ui.kv("ENTRY", relEntry, 8));
|
|
169
|
+
console.log(ui.blank());
|
|
170
|
+
|
|
171
|
+
for (const { content, replacement } of toReplace) {
|
|
172
|
+
console.log(ui.diff("remove", content.trim()));
|
|
173
|
+
if (replacement !== null) {
|
|
174
|
+
for (const l of replacement.split("\n")) console.log(ui.diff("add", l.trim()));
|
|
175
|
+
} else {
|
|
176
|
+
console.log(ui.diff("unknown", ""));
|
|
177
|
+
}
|
|
178
|
+
console.log(ui.blank());
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
await waitForEnter(ui.promptLine("apply patch? [Y/n]"));
|
|
182
|
+
const answer = "";
|
|
183
|
+
const confirmed = answer === "" || /^y(es)?$/i.test(answer);
|
|
184
|
+
console.log(ui.blank());
|
|
185
|
+
console.log(ui.hr());
|
|
186
|
+
console.log(ui.blank());
|
|
187
|
+
|
|
188
|
+
if (!confirmed) { printSnippet(isEsm); return; }
|
|
189
|
+
|
|
190
|
+
const indexMap = new Map(toReplace.map(r => [r.index, r]));
|
|
191
|
+
const outLines = lines.map((line, i) => {
|
|
192
|
+
const r = indexMap.get(i);
|
|
193
|
+
return (r && r.replacement !== null) ? r.replacement : line;
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
if (isEsm) {
|
|
197
|
+
const callLineIndex = outLines.findIndex(l => /^\s*await config\s*\(/.test(l));
|
|
198
|
+
const lastImportIndex = outLines.reduce((last, l, i) => l.trimStart().startsWith("import ") ? i : last, -1);
|
|
199
|
+
if (callLineIndex !== -1 && callLineIndex > lastImportIndex + 2) {
|
|
200
|
+
const callLine = outLines[callLineIndex];
|
|
201
|
+
outLines.splice(callLineIndex, 1);
|
|
202
|
+
if (outLines[callLineIndex]?.trim() === "") outLines.splice(callLineIndex, 1);
|
|
203
|
+
const newLastImport = outLines.reduce((last, l, i) => l.trimStart().startsWith("import ") ? i : last, -1);
|
|
204
|
+
outLines.splice(newLastImport + 1, 0, "", callLine, "");
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
try {
|
|
209
|
+
writeFileSync(entryPath, outLines.join("\n"), "utf-8");
|
|
210
|
+
} catch (err) {
|
|
211
|
+
console.error(ui.errBlock("could not write " + relEntry, err.message));
|
|
212
|
+
printSnippet(isEsm);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function printSnippet(isEsm) {
|
|
217
|
+
console.log(ui.infoBlock("add to your entry point:"));
|
|
218
|
+
console.log(ui.blank());
|
|
219
|
+
if (isEsm) {
|
|
220
|
+
console.log(ui.INDENT + " " + ui.amber('import { config } from "@suronai/sdk"'));
|
|
221
|
+
console.log(ui.INDENT + " " + ui.amber("await config()"));
|
|
222
|
+
} else {
|
|
223
|
+
console.log(ui.INDENT + " " + ui.amber('const { config } = require("@suronai/sdk")'));
|
|
224
|
+
console.log(ui.INDENT + " " + ui.amber("await config()"));
|
|
225
|
+
}
|
|
226
|
+
console.log(ui.blank());
|
|
227
|
+
console.log(ui.hr());
|
|
228
|
+
console.log(ui.blank());
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// ── Commands ──────────────────────────────────────────────────────────────────
|
|
232
|
+
|
|
44
233
|
export const loginCommand = new Command("login")
|
|
45
234
|
.description("Authenticate via browser")
|
|
46
235
|
.option("--force", "Re-authenticate even if already logged in")
|
|
@@ -69,12 +258,12 @@ export const loginCommand = new Command("login")
|
|
|
69
258
|
|
|
70
259
|
const loginUrl = `${BASE_URL}/clilogin?code=${code}`;
|
|
71
260
|
|
|
72
|
-
console.log(ui.infoBlock("
|
|
261
|
+
console.log(ui.infoBlock("press Enter to open login in your browser"));
|
|
73
262
|
console.log(ui.blank());
|
|
74
263
|
console.log(ui.kv("URL", loginUrl, 6));
|
|
75
264
|
console.log(ui.blank());
|
|
76
265
|
|
|
77
|
-
await waitForEnter("
|
|
266
|
+
await waitForEnter(ui.promptLine("open browser"));
|
|
78
267
|
openBrowser(loginUrl);
|
|
79
268
|
console.log(ui.blank());
|
|
80
269
|
|
|
@@ -119,9 +308,7 @@ export const logoutCommand = new Command("logout")
|
|
|
119
308
|
.description("Clear local session token")
|
|
120
309
|
.action(async () => {
|
|
121
310
|
clearToken();
|
|
122
|
-
try {
|
|
123
|
-
await api("/auth/logout", { method: "POST" });
|
|
124
|
-
} catch {}
|
|
311
|
+
try { await api("/auth/logout", { method: "POST" }); } catch {}
|
|
125
312
|
console.log(ui.blank());
|
|
126
313
|
console.log(ui.okBlock("logged out"));
|
|
127
314
|
console.log(ui.blank());
|
|
@@ -129,19 +316,21 @@ export const logoutCommand = new Command("logout")
|
|
|
129
316
|
|
|
130
317
|
export const initCommand = new Command("init")
|
|
131
318
|
.description("Initialize Suron in this project")
|
|
132
|
-
.
|
|
133
|
-
|
|
319
|
+
.option("--name <n>", "App name (skips interactive prompt)")
|
|
320
|
+
.action(async (opts) => {
|
|
321
|
+
requireToken();
|
|
322
|
+
const cwd = process.cwd();
|
|
134
323
|
|
|
135
|
-
if (existsSync(".suron.json")) {
|
|
324
|
+
if (existsSync(join(cwd, ".suron.json"))) {
|
|
136
325
|
console.log(ui.blank());
|
|
137
326
|
console.log(ui.errBlock(".suron.json already exists", "use suron up to push a new version"));
|
|
138
327
|
console.log(ui.blank());
|
|
139
328
|
process.exit(1);
|
|
140
329
|
}
|
|
141
330
|
|
|
142
|
-
if (!existsSync(".env")) {
|
|
331
|
+
if (!existsSync(join(cwd, ".env"))) {
|
|
143
332
|
console.log(ui.blank());
|
|
144
|
-
console.log(ui.errBlock(".env not found", "create a .env file first"));
|
|
333
|
+
console.log(ui.errBlock(".env not found", "create a .env file with your secrets first"));
|
|
145
334
|
console.log(ui.blank());
|
|
146
335
|
process.exit(1);
|
|
147
336
|
}
|
|
@@ -151,40 +340,49 @@ export const initCommand = new Command("init")
|
|
|
151
340
|
console.log(ui.hr());
|
|
152
341
|
console.log(ui.blank());
|
|
153
342
|
|
|
343
|
+
// ── App selection ─────────────────────────────────────────────────────────
|
|
344
|
+
|
|
154
345
|
const mode = await select({
|
|
155
346
|
message: "How do you want to link this project?",
|
|
156
347
|
options: [
|
|
157
|
-
{ value: "new",
|
|
348
|
+
{ value: "new", label: "Create new app" },
|
|
158
349
|
{ value: "existing", label: "Link to existing app" },
|
|
159
350
|
],
|
|
160
351
|
});
|
|
161
|
-
|
|
162
352
|
if (isCancel(mode)) cancel("cancelled");
|
|
163
353
|
|
|
164
354
|
let app_id, app_name;
|
|
165
355
|
|
|
166
356
|
if (mode === "new") {
|
|
167
|
-
const
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
357
|
+
const suggested = sanitiseName(basename(cwd) || "myapp");
|
|
358
|
+
let rawName;
|
|
359
|
+
|
|
360
|
+
if (opts.name) {
|
|
361
|
+
rawName = opts.name;
|
|
362
|
+
} else {
|
|
363
|
+
rawName = await text({
|
|
364
|
+
message: `App name [${suggested}]`,
|
|
365
|
+
placeholder: suggested,
|
|
366
|
+
validate: v => {
|
|
367
|
+
const s = sanitiseName(v || suggested);
|
|
368
|
+
return s.length ? undefined : "Alphanumeric only";
|
|
369
|
+
},
|
|
370
|
+
});
|
|
371
|
+
if (isCancel(rawName)) cancel("cancelled");
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
const name = sanitiseName(rawName || suggested);
|
|
172
375
|
|
|
173
376
|
const result = await api("/apps", {
|
|
174
377
|
method: "POST",
|
|
175
378
|
body: JSON.stringify({ name }),
|
|
176
379
|
}).catch(err => cancel(err.message));
|
|
177
380
|
|
|
178
|
-
app_id
|
|
381
|
+
app_id = result.app_id;
|
|
179
382
|
app_name = result.name;
|
|
180
383
|
} else {
|
|
181
384
|
const apps = await api("/apps").catch(err => cancel(err.message));
|
|
182
|
-
|
|
183
|
-
if (!apps.length) {
|
|
184
|
-
console.log(ui.blank());
|
|
185
|
-
console.log(ui.errBlock("no apps found", "use 'Create new app' instead"));
|
|
186
|
-
process.exit(1);
|
|
187
|
-
}
|
|
385
|
+
if (!apps.length) cancel("no apps found", "use 'Create new app' instead");
|
|
188
386
|
|
|
189
387
|
const chosen = await select({
|
|
190
388
|
message: "Select an app",
|
|
@@ -193,59 +391,119 @@ export const initCommand = new Command("init")
|
|
|
193
391
|
if (isCancel(chosen)) cancel("cancelled");
|
|
194
392
|
|
|
195
393
|
const found = apps.find(a => a.app_id === chosen);
|
|
196
|
-
app_id
|
|
394
|
+
app_id = found.app_id;
|
|
197
395
|
app_name = found.name;
|
|
198
396
|
}
|
|
199
397
|
|
|
200
|
-
|
|
398
|
+
console.log(ui.blank());
|
|
399
|
+
console.log(ui.kv("APP", app_name));
|
|
400
|
+
console.log(ui.kv("DIR", ui.slate(cwd)));
|
|
401
|
+
console.log(ui.blank());
|
|
402
|
+
console.log(ui.hr());
|
|
403
|
+
console.log(ui.blank());
|
|
404
|
+
|
|
405
|
+
// ── Step tracker ──────────────────────────────────────────────────────────
|
|
201
406
|
|
|
202
|
-
const
|
|
203
|
-
|
|
407
|
+
const steps = {
|
|
408
|
+
upload: { label: "upload .env", status: "pending" },
|
|
409
|
+
gitignore: { label: "gitignore .gitignore", status: "pending" },
|
|
410
|
+
sdk: { label: "sdk @suronai/sdk", status: "pending" },
|
|
411
|
+
patch: { label: "patch entry point", status: "pending" },
|
|
412
|
+
};
|
|
204
413
|
|
|
205
|
-
const
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
414
|
+
const printSteps = () => {
|
|
415
|
+
for (const s of Object.values(steps)) {
|
|
416
|
+
console.log(ui.step(s.status, s.label, s.note));
|
|
417
|
+
}
|
|
418
|
+
};
|
|
209
419
|
|
|
210
|
-
|
|
420
|
+
// ── Upload ────────────────────────────────────────────────────────────────
|
|
211
421
|
|
|
212
|
-
|
|
213
|
-
|
|
422
|
+
steps.upload.status = "run";
|
|
423
|
+
const env_plaintext = readFileSync(join(cwd, ".env"), "utf-8");
|
|
214
424
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
425
|
+
let uploadResult;
|
|
426
|
+
try {
|
|
427
|
+
uploadResult = await api(`/apps/${app_id}/versions`, {
|
|
428
|
+
method: "POST",
|
|
429
|
+
body: JSON.stringify({ env_plaintext }),
|
|
430
|
+
});
|
|
431
|
+
steps.upload.status = "done";
|
|
432
|
+
steps.upload.note = `v${uploadResult.version}`;
|
|
433
|
+
} catch (err) {
|
|
434
|
+
steps.upload.status = "fail";
|
|
435
|
+
printSteps();
|
|
436
|
+
console.log(ui.blank());
|
|
437
|
+
cancel(err.message);
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
// ── .suron.json ───────────────────────────────────────────────────────────
|
|
441
|
+
|
|
442
|
+
writeFileSync(
|
|
443
|
+
join(cwd, ".suron.json"),
|
|
444
|
+
JSON.stringify({ app_name, app_id, version: uploadResult.version }, null, 2) + "\n",
|
|
445
|
+
"utf-8"
|
|
446
|
+
);
|
|
447
|
+
|
|
448
|
+
// ── .gitignore ────────────────────────────────────────────────────────────
|
|
449
|
+
|
|
450
|
+
steps.gitignore.status = "run";
|
|
451
|
+
const gitResult = ensureGitignore(cwd);
|
|
452
|
+
steps.gitignore.status = "done";
|
|
453
|
+
steps.gitignore.note = gitResult;
|
|
454
|
+
|
|
455
|
+
// ── SDK install ───────────────────────────────────────────────────────────
|
|
456
|
+
|
|
457
|
+
steps.sdk.status = "run";
|
|
458
|
+
const sdkResult = installSdk(cwd);
|
|
459
|
+
steps.sdk.status = sdkResult.status;
|
|
460
|
+
steps.sdk.note = sdkResult.note;
|
|
461
|
+
|
|
462
|
+
// ── Print steps before patch (patch is interactive) ───────────────────────
|
|
463
|
+
|
|
464
|
+
steps.patch.status = "run";
|
|
465
|
+
printSteps();
|
|
219
466
|
console.log(ui.blank());
|
|
220
467
|
console.log(ui.hr());
|
|
221
468
|
console.log(ui.blank());
|
|
222
|
-
|
|
469
|
+
|
|
470
|
+
// ── Entry-point patch ─────────────────────────────────────────────────────
|
|
471
|
+
|
|
472
|
+
await patchEntryPoint(cwd, sdkResult.isEsm ?? false);
|
|
473
|
+
steps.patch.status = "done";
|
|
474
|
+
|
|
475
|
+
// ── Ready ─────────────────────────────────────────────────────────────────
|
|
476
|
+
|
|
477
|
+
console.log(ui.readyBlock([
|
|
478
|
+
{ label: ".env", note: "uploaded · keep out of git" },
|
|
479
|
+
{ label: ".suron.json", note: "safe to commit" },
|
|
480
|
+
{ label: ".gitignore", note: gitResult },
|
|
481
|
+
]));
|
|
223
482
|
console.log(ui.blank());
|
|
224
483
|
});
|
|
225
484
|
|
|
226
|
-
export const
|
|
227
|
-
.description("
|
|
485
|
+
export const whoamiCommand = new Command("whoami")
|
|
486
|
+
.description("Show the current logged-in user")
|
|
228
487
|
.action(async () => {
|
|
229
488
|
requireToken();
|
|
489
|
+
const user = await api("/auth/me").catch(err => cancel(err.message));
|
|
490
|
+
console.log(ui.blank());
|
|
491
|
+
console.log(ui.kv("EMAIL", user.email));
|
|
492
|
+
console.log(ui.kv("ROLE", user.role));
|
|
493
|
+
console.log(ui.blank());
|
|
494
|
+
});
|
|
230
495
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
process.exit(1);
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
if (!existsSync(".env")) {
|
|
239
|
-
console.log(ui.blank());
|
|
240
|
-
console.log(ui.errBlock(".env not found"));
|
|
241
|
-
console.log(ui.blank());
|
|
242
|
-
process.exit(1);
|
|
243
|
-
}
|
|
496
|
+
export const upCommand = new Command("up")
|
|
497
|
+
.action(async () => {
|
|
498
|
+
requireToken();
|
|
499
|
+
const cwd = process.cwd();
|
|
244
500
|
|
|
245
|
-
|
|
246
|
-
|
|
501
|
+
if (!existsSync(join(cwd, ".suron.json"))) cancel(".suron.json not found", "run: suron init");
|
|
502
|
+
if (!existsSync(join(cwd, ".env"))) cancel(".env not found");
|
|
247
503
|
|
|
248
|
-
const
|
|
504
|
+
const suronJson = JSON.parse(readFileSync(join(cwd, ".suron.json"), "utf-8"));
|
|
505
|
+
const { app_id } = suronJson;
|
|
506
|
+
const env_plaintext = readFileSync(join(cwd, ".env"), "utf-8");
|
|
249
507
|
|
|
250
508
|
const s = spinner();
|
|
251
509
|
s.start("Pushing new version");
|
|
@@ -258,7 +516,7 @@ export const upCommand = new Command("up")
|
|
|
258
516
|
s.stop("Done");
|
|
259
517
|
|
|
260
518
|
suronJson.version = result.version;
|
|
261
|
-
writeFileSync(".suron.json", JSON.stringify(suronJson, null, 2) + "\n");
|
|
519
|
+
writeFileSync(join(cwd, ".suron.json"), JSON.stringify(suronJson, null, 2) + "\n");
|
|
262
520
|
|
|
263
521
|
console.log(ui.blank());
|
|
264
522
|
console.log(ui.step("done", `version ${result.version} pushed`));
|
package/src/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { Command } from "commander";
|
|
4
4
|
import { createRequire } from "module";
|
|
5
|
-
import { loginCommand, logoutCommand, initCommand, upCommand } from "./commands.js";
|
|
5
|
+
import { loginCommand, logoutCommand, initCommand, upCommand, whoamiCommand } from "./commands.js";
|
|
6
6
|
|
|
7
7
|
const require = createRequire(import.meta.url);
|
|
8
8
|
const { version } = require("../package.json");
|
|
@@ -16,6 +16,7 @@ program
|
|
|
16
16
|
|
|
17
17
|
program.addCommand(loginCommand);
|
|
18
18
|
program.addCommand(logoutCommand);
|
|
19
|
+
program.addCommand(whoamiCommand);
|
|
19
20
|
program.addCommand(initCommand);
|
|
20
21
|
program.addCommand(upCommand);
|
|
21
22
|
|