@scadable/wizard 0.1.0 → 0.1.1
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/dist/cli.js +89 -38
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -169,19 +169,36 @@ function parseArgs(argv) {
|
|
|
169
169
|
return args;
|
|
170
170
|
}
|
|
171
171
|
var HELP = `
|
|
172
|
-
${pc.
|
|
172
|
+
${pc.bgCyan(pc.black(" SCADABLE "))} ${pc.bold("privacy wizard")}
|
|
173
|
+
Add a live privacy page to your app in about 20 seconds. Commit, deploy, done.
|
|
173
174
|
|
|
174
175
|
${pc.bold("Usage")}
|
|
175
176
|
npx @scadable/wizard@latest --token <TEMP_TOKEN> [options]
|
|
176
177
|
|
|
178
|
+
${pc.dim("Get your token from Settings in the SCADABLE app, then paste the whole command here.")}
|
|
179
|
+
|
|
177
180
|
${pc.bold("Options")}
|
|
178
181
|
--token <token> Install token from the SCADABLE app (required).
|
|
179
182
|
--api <url> API base. Default ${DEFAULT_API}.
|
|
180
|
-
--dry-run
|
|
181
|
-
--yes, -y Skip
|
|
182
|
-
--patch <file>
|
|
183
|
+
--dry-run Preview the plan without writing anything.
|
|
184
|
+
--yes, -y Skip the prompts (non-interactive).
|
|
185
|
+
--patch <file> Add the policy to an existing page instead of creating one.
|
|
183
186
|
--help, -h Show this help.
|
|
187
|
+
|
|
188
|
+
${pc.dim("Your code stays private: only package.json and folder names are read.")}
|
|
184
189
|
`;
|
|
190
|
+
var BANNER = [
|
|
191
|
+
" _______. ______ ___ _______ ___ .______ __ _______ ",
|
|
192
|
+
" / | / | / \\ | \\ / \\ | _ \\ | | | ____|",
|
|
193
|
+
" | (----`| ,----' / ^ \\ | .--. | / ^ \\ | |_) | | | | |__ ",
|
|
194
|
+
" \\ \\ | | / /_\\ \\ | | | | / /_\\ \\ | _ < | | | __| ",
|
|
195
|
+
".----) | | `----./ _____ \\ | '--' | / _____ \\ | |_) | | `----.| |____ ",
|
|
196
|
+
"|_______/ \\______/__/ \\__\\ |_______/ /__/ \\__\\ |______/ |_______||_______|"
|
|
197
|
+
];
|
|
198
|
+
function printBanner() {
|
|
199
|
+
for (const line of BANNER) console.log(pc.cyan(line));
|
|
200
|
+
console.log("");
|
|
201
|
+
}
|
|
185
202
|
function fail(message) {
|
|
186
203
|
log.error(message);
|
|
187
204
|
process.exit(1);
|
|
@@ -201,12 +218,12 @@ function showPlan(installCmd, edits, deterministic) {
|
|
|
201
218
|
const blocks = [`${pc.bold("Install")}
|
|
202
219
|
${pc.cyan(installCmd)}`];
|
|
203
220
|
for (const edit of edits) {
|
|
204
|
-
const verb = edit.action === "patch" ? "
|
|
221
|
+
const verb = edit.action === "patch" ? "Update" : "Create";
|
|
205
222
|
blocks.push(`${pc.bold(`${verb} ${edit.path}`)}
|
|
206
223
|
${previewContents(edit.contents)}`);
|
|
207
224
|
}
|
|
208
|
-
const source = deterministic ? pc.dim("
|
|
209
|
-
note(blocks.join("\n\n") + "\n\n" + source, "
|
|
225
|
+
const source = deterministic ? pc.dim("Built from a vetted template. Same result every time.") : pc.dim("Tailored to your project.");
|
|
226
|
+
note(blocks.join("\n\n") + "\n\n" + source, "Here's the plan");
|
|
210
227
|
}
|
|
211
228
|
async function main() {
|
|
212
229
|
const args = parseArgs(process.argv.slice(2));
|
|
@@ -214,56 +231,63 @@ async function main() {
|
|
|
214
231
|
console.log(HELP);
|
|
215
232
|
return;
|
|
216
233
|
}
|
|
217
|
-
|
|
234
|
+
printBanner();
|
|
235
|
+
intro(`${pc.bgCyan(pc.black(" SCADABLE "))} ${pc.bold("privacy wizard")}`);
|
|
236
|
+
log.message(pc.dim("Setting up your privacy page. About 20 seconds."));
|
|
218
237
|
if (!args.token) {
|
|
219
|
-
fail(
|
|
238
|
+
fail(
|
|
239
|
+
"No install token yet. Open Settings in SCADABLE, then copy and paste the whole install command."
|
|
240
|
+
);
|
|
220
241
|
}
|
|
221
242
|
const token = args.token;
|
|
222
243
|
const api = args.api.replace(/\/$/, "");
|
|
223
244
|
const cwd = process.cwd();
|
|
224
245
|
const spin = spinner();
|
|
225
|
-
spin.start("
|
|
246
|
+
spin.start("Checking your install token");
|
|
226
247
|
let session;
|
|
227
248
|
try {
|
|
228
249
|
session = await getSession(api, token);
|
|
229
250
|
} catch (err) {
|
|
230
|
-
spin.stop("Token check failed");
|
|
251
|
+
spin.stop("Token check failed", 2);
|
|
231
252
|
if (err instanceof WizardApiError && err.status === 401) {
|
|
232
253
|
fail(
|
|
233
|
-
"
|
|
254
|
+
"That install token has expired. Reopen Settings in SCADABLE and copy a fresh install command, then run it again."
|
|
234
255
|
);
|
|
235
256
|
}
|
|
236
257
|
if (err instanceof WizardNetworkError) {
|
|
237
|
-
fail(`Could not reach
|
|
258
|
+
fail(`Could not reach SCADABLE at ${api}. Check your connection and try again.`);
|
|
238
259
|
}
|
|
239
260
|
fail(`Could not verify your token: ${reason(err)}`);
|
|
240
261
|
}
|
|
241
|
-
spin.stop(`Connected to ${pc.bold(session.scope_name)} (${session.domain})`);
|
|
262
|
+
spin.stop(`Connected to ${pc.bold(session.scope_name)} ${pc.dim(`(${session.domain})`)}`);
|
|
263
|
+
spin.start("Reading your project");
|
|
242
264
|
let ctx;
|
|
243
265
|
try {
|
|
244
266
|
ctx = detectRepo(cwd);
|
|
245
267
|
} catch (err) {
|
|
268
|
+
spin.stop("Could not read your project", 2);
|
|
246
269
|
fail(reason(err));
|
|
247
270
|
}
|
|
271
|
+
spin.stop(`Found ${pc.bold(ctx.framework)}`);
|
|
272
|
+
log.message(pc.dim("Only package.json and folder names were read. Your code stays on your machine."));
|
|
248
273
|
if (ctx.warning) log.warn(ctx.warning);
|
|
249
274
|
let mode = "create";
|
|
250
275
|
let target;
|
|
251
276
|
if (args.patch) {
|
|
252
277
|
const abs = resolve(cwd, args.patch);
|
|
253
278
|
if (!existsSync3(abs)) {
|
|
254
|
-
fail(
|
|
279
|
+
fail(`Could not find the file to patch: ${args.patch}`);
|
|
255
280
|
}
|
|
256
281
|
const contents = readFileSync2(abs, "utf8");
|
|
257
282
|
if (looksLikeSecret(contents)) {
|
|
258
283
|
fail(
|
|
259
|
-
`
|
|
284
|
+
`Not uploading ${args.patch}: it looks like it holds secrets. Drop --patch and the wizard will create a fresh privacy page instead.`
|
|
260
285
|
);
|
|
261
286
|
}
|
|
262
287
|
mode = "patch";
|
|
263
288
|
target = { path: args.patch, contents };
|
|
264
289
|
}
|
|
265
|
-
|
|
266
|
-
spin.start("Building your install plan");
|
|
290
|
+
spin.start("Building your plan");
|
|
267
291
|
let plan;
|
|
268
292
|
try {
|
|
269
293
|
plan = await postPlan(api, token, {
|
|
@@ -274,12 +298,12 @@ async function main() {
|
|
|
274
298
|
target
|
|
275
299
|
});
|
|
276
300
|
} catch (err) {
|
|
277
|
-
spin.stop("Could not build a plan");
|
|
301
|
+
spin.stop("Could not build a plan", 2);
|
|
278
302
|
if (err instanceof WizardApiError && err.status === 503) {
|
|
279
|
-
fail("SCADABLE could not
|
|
303
|
+
fail("SCADABLE is busy and could not build your plan right now. Give it a moment and try again.");
|
|
280
304
|
}
|
|
281
305
|
if (err instanceof WizardNetworkError) {
|
|
282
|
-
fail(`Could not reach
|
|
306
|
+
fail(`Could not reach SCADABLE at ${api}. Check your connection and try again.`);
|
|
283
307
|
}
|
|
284
308
|
fail(`Could not build a plan: ${reason(err)}`);
|
|
285
309
|
}
|
|
@@ -287,50 +311,77 @@ async function main() {
|
|
|
287
311
|
const pm = detectPackageManager(cwd);
|
|
288
312
|
const packages = resolvePackages(plan.install);
|
|
289
313
|
const installCmd = formatInstall(pm, packages);
|
|
314
|
+
const liveUrl = `${api}/policy/${plan.public_id}`;
|
|
290
315
|
showPlan(installCmd, plan.edits, plan.deterministic);
|
|
291
316
|
if (args.dryRun) {
|
|
292
|
-
|
|
317
|
+
log.message(`${pc.dim("Your page would live at")} ${pc.cyan(liveUrl)}`);
|
|
318
|
+
outro(pc.dim("Dry run. Nothing was written. Run again without --dry-run to set it up."));
|
|
293
319
|
return;
|
|
294
320
|
}
|
|
295
321
|
if (!args.yes) {
|
|
296
|
-
const ok = await confirm({
|
|
322
|
+
const ok = await confirm({
|
|
323
|
+
message: "Set up your privacy page?",
|
|
324
|
+
active: "Yes, do it",
|
|
325
|
+
inactive: "Not now",
|
|
326
|
+
initialValue: true
|
|
327
|
+
});
|
|
297
328
|
if (isCancel(ok) || !ok) {
|
|
298
|
-
cancel("No
|
|
329
|
+
cancel("No problem. Nothing was changed. Run this again whenever you are ready.");
|
|
299
330
|
process.exit(0);
|
|
300
331
|
}
|
|
301
332
|
}
|
|
302
|
-
spin.start(
|
|
333
|
+
spin.start("Installing @scadable/privacy");
|
|
303
334
|
try {
|
|
304
335
|
runInstall(cwd, pm, packages);
|
|
305
|
-
spin.stop(
|
|
336
|
+
spin.stop(`Installed @scadable/privacy ${pc.dim(`(${pm})`)}`);
|
|
306
337
|
} catch (err) {
|
|
307
|
-
spin.stop("Install failed");
|
|
338
|
+
spin.stop("Install failed", 2);
|
|
308
339
|
fail(`Could not run "${installCmd}": ${reason(err)}`);
|
|
309
340
|
}
|
|
341
|
+
const toWrite = [];
|
|
310
342
|
for (const edit of plan.edits) {
|
|
311
343
|
const abs = resolve(cwd, edit.path);
|
|
312
344
|
if (edit.action === "create" && existsSync3(abs) && !args.yes) {
|
|
313
|
-
const overwrite = await confirm({
|
|
345
|
+
const overwrite = await confirm({
|
|
346
|
+
message: `${edit.path} already exists. Replace it?`,
|
|
347
|
+
active: "Replace it",
|
|
348
|
+
inactive: "Keep mine",
|
|
349
|
+
initialValue: false
|
|
350
|
+
});
|
|
314
351
|
if (isCancel(overwrite) || !overwrite) {
|
|
315
|
-
log.warn(`
|
|
352
|
+
log.warn(`Kept your existing ${edit.path}`);
|
|
316
353
|
continue;
|
|
317
354
|
}
|
|
318
355
|
}
|
|
356
|
+
toWrite.push(edit);
|
|
357
|
+
}
|
|
358
|
+
spin.start("Writing your privacy page");
|
|
359
|
+
for (const edit of toWrite) {
|
|
319
360
|
writeEdit(cwd, edit);
|
|
320
|
-
log.success(`${edit.action === "patch" ? "Patched" : "Wrote"} ${edit.path}`);
|
|
321
361
|
}
|
|
322
|
-
spin.
|
|
362
|
+
spin.stop(toWrite.length ? "Privacy page written" : "Nothing new to write");
|
|
363
|
+
spin.start("Finishing up");
|
|
323
364
|
try {
|
|
324
365
|
await postComplete(api, token);
|
|
325
|
-
spin.stop("
|
|
366
|
+
spin.stop("Connected to SCADABLE");
|
|
326
367
|
} catch (err) {
|
|
327
|
-
spin.stop("
|
|
328
|
-
log.warn(`
|
|
368
|
+
spin.stop("Files are in place, but could not reach SCADABLE to finish", 1);
|
|
369
|
+
log.warn(`Your files are written. Marking the connection failed: ${reason(err)}`);
|
|
329
370
|
}
|
|
330
|
-
|
|
331
|
-
`${pc.
|
|
332
|
-
${pc.
|
|
333
|
-
)
|
|
371
|
+
const payoff = [
|
|
372
|
+
`${pc.bold("Next:")} commit and deploy. That's it.`,
|
|
373
|
+
` ${pc.cyan('git add -A && git commit -m "Add privacy policy"')}`,
|
|
374
|
+
` ${pc.dim("then push, or run your usual deploy")}`,
|
|
375
|
+
``,
|
|
376
|
+
`${pc.bold("Where it lives")}`,
|
|
377
|
+
` ${pc.dim("Page ")} ${pc.cyan(plan.route_hint)}`,
|
|
378
|
+
` ${pc.dim("Live ")} ${pc.cyan(liveUrl)}`,
|
|
379
|
+
``,
|
|
380
|
+
pc.dim("It stays current. Update your policy in SCADABLE and every page"),
|
|
381
|
+
pc.dim("updates automatically. No redeploy.")
|
|
382
|
+
].join("\n");
|
|
383
|
+
note(payoff, pc.green("Your privacy page is ready"));
|
|
384
|
+
outro(`${pc.green("Done.")} ${pc.dim("Commit and deploy, and you are live.")}`);
|
|
334
385
|
}
|
|
335
386
|
main().catch((err) => {
|
|
336
387
|
log.error(reason(err));
|