not-manage 0.2.1 → 0.2.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.
@@ -7,6 +7,7 @@ const {
7
7
  DEFAULT_REGION,
8
8
  REGIONS,
9
9
  } = require("./constants");
10
+ const { AuthError, UsageError } = require("./cli-errors");
10
11
  const {
11
12
  authorizeUrl,
12
13
  deauthorize,
@@ -17,7 +18,7 @@ const {
17
18
  } = require("./clio-api");
18
19
  const { openBrowser } = require("./open-browser");
19
20
  const { waitForOAuthCallback } = require("./oauth-callback");
20
- const { ask, askSecret, withPrompt } = require("./prompt");
21
+ const { ask, askSecret, bold, dim, selectOption, withPrompt } = require("./prompt");
21
22
  const {
22
23
  clearTokenSet,
23
24
  findConfig,
@@ -25,6 +26,7 @@ const {
25
26
  getTokenSet,
26
27
  normalizeRegion,
27
28
  parseRedirectUri,
29
+
28
30
  saveConfig,
29
31
  saveTokenSet,
30
32
  } = require("./store");
@@ -146,9 +148,11 @@ function formatConfigSummary(config) {
146
148
 
147
149
  function rewriteOAuthError(error, config) {
148
150
  const message = error && error.message ? error.message : String(error);
151
+ const clioErrorCode =
152
+ error && typeof error.clioErrorCode === "string" ? error.clioErrorCode : null;
149
153
 
150
- if (message.includes("invalid_client")) {
151
- return new Error(
154
+ if (clioErrorCode === "invalid_client" || message.includes("invalid_client")) {
155
+ return new AuthError(
152
156
  [
153
157
  "Clio rejected the app credentials during OAuth token exchange (`invalid_client`).",
154
158
  ...formatConfigSummary(config),
@@ -158,8 +162,12 @@ function rewriteOAuthError(error, config) {
158
162
  "- The App Key/App Secret pair does not belong to the selected Clio region.",
159
163
  "- The redirect URI registered in the Clio app does not exactly match the value above.",
160
164
  "Run `not-manage auth setup` again and copy the App Key and App Secret from the same Clio developer app.",
161
- `Original error: ${message}`,
162
- ].join("\n")
165
+ `Clio response: ${message}`,
166
+ ].join("\n"),
167
+ {
168
+ code: "invalid_client_credentials",
169
+ hint: "Run `not-manage auth setup` with the App Key and App Secret from the same Clio developer app.",
170
+ }
163
171
  );
164
172
  }
165
173
 
@@ -171,184 +179,354 @@ function collectSecurityWarnings(config, tokenSet) {
171
179
  }
172
180
 
173
181
  function printSetupBanner() {
174
- console.log("+===========================================+");
175
- console.log("| WELCOME TO NOT MANAGE |");
176
- console.log("+===========================================+");
177
- console.log("| Local OAuth setup for this CLI |");
178
- console.log("+===========================================+");
182
+ console.log(bold("not-manage setup"));
183
+ console.log(dim("Connect not-manage to your Clio account so you can get your Clio data into an AI."));
179
184
  }
180
185
 
181
- function printSetupSteps() {
182
- console.log("Setup flow:");
183
- console.log(" [1] Choose your Clio region");
184
- console.log(" [2] Open the Clio developer portal for that region and sign in");
185
- console.log(" [3] Open your Clio developer app, or create one if you do not have one yet");
186
- console.log(" [4] Choose the Clio Manage permissions this CLI should access");
187
- console.log(" [5] Add the local callback URL to Redirect URIs, then copy the App Key and App Secret back here");
186
+ function printSetupLinks(region, redirectUri) {
187
+ const regionInfo = REGIONS[region];
188
+ console.log(bold("Links"));
189
+ console.log(` Portal ${dim(regionInfo.developerPortalUrl)}`);
190
+ console.log(` Guides ${dim(CLIO_DEVELOPER_ACCOUNT_GUIDE_URL)}`);
191
+ console.log(` ${dim(CLIO_APP_CREATION_GUIDE_URL)}`);
192
+ console.log(` ${dim(CLIO_AUTHORIZATION_GUIDE_URL)}`);
193
+ console.log(` Redirect ${redirectUri}`);
188
194
  }
189
195
 
190
- async function maybeOpenDeveloperPortal(rl, region) {
191
- const regionInfo = REGIONS[region];
192
- const promptLabel = "Press Enter to open the developer portal now, or type skip to continue here";
193
- const answer = String(await ask(rl, promptLabel, "")).trim().toLowerCase();
196
+ function printPortalSteps(redirectUri) {
197
+ console.log(bold("In the developer portal:"));
198
+ console.log(" 1. Open or create a Clio developer app");
199
+ console.log(" 2. Set permissions (scopes) for this CLI");
200
+ console.log(" 3. Add this redirect URI:");
201
+ console.log(` ${bold(redirectUri)}`);
202
+ console.log(" 4. Copy the App Key and App Secret back here");
203
+ }
194
204
 
195
- if (answer === "skip") {
196
- console.log("Continuing without opening the browser.");
205
+ function printConfidentialityNotice() {
206
+ console.log(dim("Output may contain confidential client data."));
207
+ console.log(dim("Redaction (--redacted) is best-effort. Review all output before sharing."));
208
+ }
209
+
210
+ function printSetupIntro() {
211
+ printSetupBanner();
212
+ console.log("");
213
+ printConfidentialityNotice();
214
+ }
215
+
216
+ function normalizeOptionalText(value) {
217
+ if (value === undefined || value === null) {
218
+ return undefined;
219
+ }
220
+
221
+ const text = String(value).trim();
222
+ return text || undefined;
223
+ }
224
+
225
+ function buildAuthSetupUsage() {
226
+ return [
227
+ "Usage: not-manage auth setup --confirm-confidentiality --client-id <value> --client-secret <value> [--region <code>] [--redirect-uri <url>] [--open-browser true|false]",
228
+ "Run `not-manage auth setup --help` for command details.",
229
+ ].join("\n");
230
+ }
231
+
232
+ function requireInteractiveSetupFlag(flagName, details) {
233
+ const prefix = `\`${flagName}\` is required outside an interactive terminal.`;
234
+ return new UsageError(details ? `${prefix}\n${details}` : prefix, {
235
+ code: "missing_interactive_setup_flag",
236
+ hint: "not-manage auth setup --help",
237
+ details: { missing_flags: [flagName] },
238
+ });
239
+ }
240
+
241
+ function buildRegionOptions() {
242
+ return Object.values(REGIONS).map((region) => ({
243
+ label: `${region.code} - ${region.label} ${dim(`(${region.host})`)}`,
244
+ value: region.code,
245
+ }));
246
+ }
247
+
248
+ function normalizeSetupOptions(options = {}) {
249
+ const region = normalizeOptionalText(options.region);
250
+ const redirectUri = normalizeOptionalText(options.redirectUri);
251
+
252
+ return {
253
+ clientId: normalizeOptionalText(options.clientId),
254
+ clientSecret: normalizeOptionalText(options.clientSecret),
255
+ confirmConfidentiality: options.confirmConfidentiality === true,
256
+ noInput: options.noInput === true,
257
+ openBrowser: typeof options.openBrowser === "boolean" ? options.openBrowser : undefined,
258
+ redirectUri: redirectUri ? parseRedirectUri(redirectUri) : DEFAULT_REDIRECT_URI,
259
+ region: region ? normalizeRegion(region) : undefined,
260
+ };
261
+ }
262
+
263
+ function buildMissingSetupOptionsError(missingFlags) {
264
+ return new UsageError(
265
+ [
266
+ `Missing required setup options: ${missingFlags.join(", ")}.`,
267
+ buildAuthSetupUsage(),
268
+ ].join("\n"),
269
+ {
270
+ code: "missing_setup_options",
271
+ hint: "not-manage auth setup --help",
272
+ details: { missing_flags: missingFlags },
273
+ }
274
+ );
275
+ }
276
+
277
+ function buildMissingRevokeConfirmationError() {
278
+ return new UsageError(
279
+ [
280
+ "`not-manage auth revoke` needs confirmation before it clears tokens.",
281
+ "Re-run with `--yes` to perform the revoke, or `--dry-run` to inspect what would happen.",
282
+ ].join("\n"),
283
+ {
284
+ code: "confirmation_required",
285
+ hint: "not-manage auth revoke --dry-run",
286
+ }
287
+ );
288
+ }
289
+
290
+ function buildEmptySetupValueError(label, flagName) {
291
+ return new UsageError(`${label} is required.`, {
292
+ code: "missing_setup_options",
293
+ hint: "not-manage auth setup --help",
294
+ details: { missing_flags: [flagName] },
295
+ });
296
+ }
297
+
298
+ function sameSavedConfig(existingConfig, configInput) {
299
+ return Boolean(
300
+ existingConfig &&
301
+ existingConfig.region === configInput.region &&
302
+ existingConfig.clientId === configInput.clientId &&
303
+ existingConfig.clientSecret === configInput.clientSecret &&
304
+ existingConfig.redirectUri === configInput.redirectUri
305
+ );
306
+ }
307
+
308
+ async function ensureConfidentialityNoticeAcknowledged(rl, options) {
309
+ if (options.confirmConfidentiality) {
197
310
  return;
198
311
  }
199
312
 
200
- try {
201
- await openBrowser(regionInfo.developerPortalUrl);
202
- console.log(`Opened the ${regionInfo.label} Clio developer portal in your browser.`);
203
- } catch (_error) {
204
- console.log("Could not open the Clio developer portal automatically.");
205
- console.log(`Open this URL manually: ${regionInfo.developerPortalUrl}`);
313
+ if (!rl) {
314
+ throw requireInteractiveSetupFlag("--confirm-confidentiality", buildAuthSetupUsage());
206
315
  }
207
- }
208
316
 
209
- function printSetupLinks(region, redirectUri) {
210
- const regionInfo = REGIONS[region];
211
- console.log("Clio setup links:");
212
- console.log(`- Developer portal: ${regionInfo.developerPortalUrl}`);
213
- console.log(`- Developer account guide: ${CLIO_DEVELOPER_ACCOUNT_GUIDE_URL}`);
214
- console.log(`- App creation guide: ${CLIO_APP_CREATION_GUIDE_URL}`);
215
- console.log(`- Authorization guide: ${CLIO_AUTHORIZATION_GUIDE_URL}`);
216
- console.log(`- Add this redirect URI in your Clio app: ${redirectUri}`);
317
+ await confirmConfidentialityNotice(rl);
217
318
  }
218
319
 
219
- function printClioAppFieldGuide(redirectUri) {
220
- console.log("Clio app form guide:");
221
- console.log("");
222
- console.log(" Required:");
223
- console.log(" - Website URL: use your firm website, company site, or GitHub repo.");
224
- console.log(" - Do not put the local callback URL in Website URL.");
225
- console.log(" - Clio Manage permissions / scopes: select only the permissions this CLI will actually use.");
226
- console.log(" - Redirect URIs: copy this exact URL on its own line:");
227
- console.log(` ${redirectUri}`);
228
- console.log("");
229
- console.log(" Optional:");
230
- console.log(" - Support URL and Deauthorization callback URL can stay blank unless you already use them.");
320
+ async function resolveSetupClientId(rl, options) {
321
+ if (options.clientId) {
322
+ return options.clientId;
323
+ }
324
+
325
+ if (!rl) {
326
+ throw buildMissingSetupOptionsError(["--client-id"]);
327
+ }
328
+
329
+ const clientId = normalizeOptionalText(await ask(rl, "App Key / Client ID"));
330
+ if (!clientId) {
331
+ throw buildEmptySetupValueError("App Key / Client ID", "--client-id");
332
+ }
333
+
334
+ return clientId;
231
335
  }
232
336
 
233
- function printDeveloperPortalReminder(redirectUri) {
234
- console.log("In the developer portal:");
235
- console.log("");
236
- console.log(" First:");
237
- console.log(" - Sign in, then open the Clio developer app you want this CLI to use.");
238
- console.log(" - Use an existing app in this region, or create a new one.");
239
- console.log("");
240
- console.log(" Permissions:");
241
- console.log(" - Select only the Clio Manage permissions (OAuth scopes) this CLI should access.");
242
- console.log("");
243
- console.log(" Redirect URI:");
244
- console.log(" - Register this exact URL in your Clio developer app:");
245
- console.log(` ${redirectUri}`);
246
- console.log("");
247
- console.log(" Then:");
248
- console.log(" - Copy the App Key and App Secret from that same app back here.");
337
+ async function resolveSetupClientSecret(rl, options) {
338
+ if (options.clientSecret) {
339
+ return options.clientSecret;
340
+ }
341
+
342
+ if (!rl) {
343
+ throw buildMissingSetupOptionsError(["--client-secret"]);
344
+ }
345
+
346
+ const clientSecret = normalizeOptionalText(await askSecret(rl, "App Secret / Client Secret"));
347
+ if (!clientSecret) {
348
+ throw buildEmptySetupValueError("App Secret / Client Secret", "--client-secret");
349
+ }
350
+
351
+ return clientSecret;
249
352
  }
250
353
 
251
- function printConfidentialityNotice() {
252
- console.log("Confidentiality notice:");
253
- console.log(" not-manage can display client-identifying, confidential, or privileged matter data.");
254
- console.log(" `--redacted` is best-effort only and may miss identifiers in labels, custom fields, or free text.");
255
- console.log(" Review all output before sharing it with AI tools, tickets, chats, or other third parties.");
256
- console.log(" Use only with workflows and vendors your firm has approved.");
354
+ async function resolveSetupRegion(rl, options) {
355
+ if (options.region) {
356
+ return options.region;
357
+ }
358
+
359
+ if (!rl) {
360
+ return DEFAULT_REGION;
361
+ }
362
+
363
+ console.log(dim("Choose a region by number or code."));
364
+ const regionOptions = buildRegionOptions();
365
+ return selectOption(
366
+ rl,
367
+ "Region",
368
+ regionOptions,
369
+ regionOptions.findIndex((option) => option.value === DEFAULT_REGION)
370
+ );
257
371
  }
258
372
 
259
- function printSetupIntro(redirectUri) {
260
- printSetupBanner();
261
- console.log("");
262
- console.log("This setup is for developers who are connecting the CLI to their own Clio app.");
263
- console.log("If this is your first time doing that, this guide will walk you through it.");
264
- console.log("");
265
- printConfidentialityNotice();
266
- console.log("");
267
- printSetupSteps();
268
- console.log("");
269
- console.log("Useful links:");
270
- console.log(`- Developer account guide: ${CLIO_DEVELOPER_ACCOUNT_GUIDE_URL}`);
271
- console.log(`- App creation guide: ${CLIO_APP_CREATION_GUIDE_URL}`);
272
- console.log(`- OAuth guide: ${CLIO_AUTHORIZATION_GUIDE_URL}`);
273
- console.log("");
274
- printClioAppFieldGuide(redirectUri);
275
- console.log("");
276
- console.log("You do not need to paste the redirect URI back into this CLI unless you want to override it.");
277
- console.log("");
278
- console.log("Region options:");
279
- Object.values(REGIONS).forEach((region) => {
280
- console.log(`- ${region.code}: ${region.label} (${region.host})`);
373
+ async function maybePromptRevokeConfirmation() {
374
+ if (!process.stdin.isTTY || !process.stdout.isTTY) {
375
+ throw buildMissingRevokeConfirmationError();
376
+ }
377
+
378
+ return withPrompt(async (rl) => {
379
+ const answer = String(await ask(rl, "Type revoke to continue, or press Enter to cancel", ""))
380
+ .trim()
381
+ .toLowerCase();
382
+
383
+ if (answer !== "revoke") {
384
+ console.log("Revoke cancelled.");
385
+ return false;
386
+ }
387
+
388
+ return true;
281
389
  });
282
390
  }
283
391
 
392
+ async function maybeOpenDeveloperPortal(rl, region, openBrowserPreference) {
393
+ if (openBrowserPreference === false) {
394
+ console.log("Continuing without opening the browser.");
395
+ return;
396
+ }
397
+
398
+ if (openBrowserPreference === undefined) {
399
+ if (!rl) {
400
+ return;
401
+ }
402
+
403
+ const promptLabel = "Press Enter to open the developer portal, or type skip";
404
+ const answer = String(await ask(rl, promptLabel, "")).trim().toLowerCase();
405
+
406
+ if (answer === "skip") {
407
+ console.log("Continuing without opening the browser.");
408
+ return;
409
+ }
410
+ }
411
+
412
+ const regionInfo = REGIONS[region];
413
+
414
+ try {
415
+ await openBrowser(regionInfo.developerPortalUrl);
416
+ console.log(`Opened ${regionInfo.label} developer portal in your browser.`);
417
+ } catch (_error) {
418
+ console.log("Could not open browser automatically.");
419
+ console.log(`Open this URL manually: ${regionInfo.developerPortalUrl}`);
420
+ }
421
+ }
422
+
284
423
  async function confirmConfidentialityNotice(rl) {
285
424
  const answer = String(
286
425
  await ask(
287
426
  rl,
288
- "Type yes to confirm you will review output before sharing it outside your firm"
427
+ "Press Enter to confirm, or type no to abort"
289
428
  )
290
429
  )
291
430
  .trim()
292
431
  .toLowerCase();
293
432
 
294
- if (answer !== "yes") {
295
- throw new Error(
296
- "Setup aborted. Review your confidentiality and client-sharing requirements, then rerun `not-manage auth setup`."
433
+ if (answer === "no") {
434
+ throw new UsageError(
435
+ "Setup aborted. Review your confidentiality and client-sharing requirements, then rerun `not-manage auth setup`.",
436
+ {
437
+ code: "setup_aborted",
438
+ hint: "not-manage auth setup --help",
439
+ }
297
440
  );
298
441
  }
299
442
  }
300
443
 
301
444
  async function authSetup(options = {}) {
302
- printSetupIntro(DEFAULT_REDIRECT_URI);
445
+ printSetupIntro();
303
446
  console.log("");
304
447
 
305
- const configInput = await withPrompt(async (rl) => {
306
- await confirmConfidentialityNotice(rl);
307
- const regionRaw = await ask(rl, "Region", DEFAULT_REGION);
308
- const region = normalizeRegion(regionRaw);
309
- const regionInfo = REGIONS[region];
310
-
311
- console.log(`Using ${regionInfo.label} (${regionInfo.host}).`);
312
- console.log(`Developer portal: ${regionInfo.developerPortalUrl}`);
313
- console.log("If you already have a Clio developer app in this region, you can use it.");
314
- console.log("If not, create one there first, then come back here.");
315
- await maybeOpenDeveloperPortal(rl, region);
316
- printDeveloperPortalReminder(DEFAULT_REDIRECT_URI);
317
-
318
- const clientId = await ask(rl, "App Key / Client ID (from your Clio developer app)");
319
- if (!clientId) {
320
- throw new Error("App Key / Client ID is required.");
321
- }
322
-
323
- const clientSecret = await askSecret(
324
- rl,
325
- "App Secret / Client Secret (from the same Clio app)"
326
- );
327
- if (!clientSecret) {
328
- throw new Error("App Secret / Client Secret is required.");
329
- }
330
-
331
- const redirectUriOverride = await ask(
332
- rl,
333
- "Custom redirect URI override (optional; press Enter to keep the default)"
334
- );
335
- const redirectUri = parseRedirectUri(redirectUriOverride || DEFAULT_REDIRECT_URI);
336
- return {
337
- region,
338
- clientId,
339
- clientSecret,
340
- redirectUri,
341
- };
342
- });
343
-
448
+ const normalizedOptions = normalizeSetupOptions(options);
449
+ const needsPrompt =
450
+ !normalizedOptions.confirmConfidentiality ||
451
+ !normalizedOptions.clientId ||
452
+ !normalizedOptions.clientSecret;
453
+
454
+ const configInput = await (needsPrompt &&
455
+ !normalizedOptions.noInput &&
456
+ process.stdin.isTTY &&
457
+ process.stdout.isTTY
458
+ ? withPrompt(async (rl) => {
459
+ await ensureConfidentialityNoticeAcknowledged(rl, normalizedOptions);
460
+
461
+ console.log("");
462
+ const region = await resolveSetupRegion(rl, normalizedOptions);
463
+ const regionInfo = REGIONS[region];
464
+
465
+ console.log("");
466
+ printPortalSteps(normalizedOptions.redirectUri);
467
+ console.log("");
468
+ await maybeOpenDeveloperPortal(rl, region, normalizedOptions.openBrowser);
469
+
470
+ console.log("");
471
+ console.log(dim(`Using ${regionInfo.label} (${regionInfo.host}).`));
472
+
473
+ const clientId = await resolveSetupClientId(rl, normalizedOptions);
474
+ const clientSecret = await resolveSetupClientSecret(rl, normalizedOptions);
475
+
476
+ return {
477
+ region,
478
+ clientId,
479
+ clientSecret,
480
+ redirectUri: normalizedOptions.redirectUri,
481
+ };
482
+ })
483
+ : (async () => {
484
+ await ensureConfidentialityNoticeAcknowledged(null, normalizedOptions);
485
+
486
+ const missingFlags = [];
487
+ if (!normalizedOptions.clientId) {
488
+ missingFlags.push("--client-id");
489
+ }
490
+ if (!normalizedOptions.clientSecret) {
491
+ missingFlags.push("--client-secret");
492
+ }
493
+ if (missingFlags.length > 0) {
494
+ throw buildMissingSetupOptionsError(missingFlags);
495
+ }
496
+
497
+ const region = normalizedOptions.region || DEFAULT_REGION;
498
+ const regionInfo = REGIONS[region];
499
+
500
+ printPortalSteps(normalizedOptions.redirectUri);
501
+ console.log("");
502
+ await maybeOpenDeveloperPortal(null, region, normalizedOptions.openBrowser);
503
+
504
+ console.log("");
505
+ console.log(dim(`Using ${regionInfo.label} (${regionInfo.host}).`));
506
+
507
+ return {
508
+ region,
509
+ clientId: normalizedOptions.clientId,
510
+ clientSecret: normalizedOptions.clientSecret,
511
+ redirectUri: normalizedOptions.redirectUri,
512
+ };
513
+ })());
514
+
515
+ const existingConfig = await findConfig();
516
+ const configChanged = !sameSavedConfig(existingConfig, configInput);
344
517
  const saved = await saveConfig(configInput);
345
- await clearTokenSet();
518
+ if (configChanged) {
519
+ await clearTokenSet();
520
+ }
346
521
 
347
522
  console.log("");
348
- console.log("Saved credentials to secure keychain.");
349
- console.log(`Region: ${saved.region} (${REGIONS[saved.region].label})`);
523
+ console.log(bold("Your credentials have been securely saved on your local machine in the keychain."));
524
+ if (!configChanged) {
525
+ console.log(dim("Existing OAuth token preserved because the saved app credentials did not change."));
526
+ }
527
+ console.log(dim(`Region: ${saved.region} (${REGIONS[saved.region].label})`));
528
+ console.log("");
350
529
  printSetupLinks(saved.region, saved.redirectUri);
351
- printClioAppFieldGuide(saved.redirectUri);
352
530
 
353
531
  if (!options.skipNextStepHint) {
354
532
  console.log("");
@@ -446,8 +624,7 @@ async function authStatus(options = {}) {
446
624
  });
447
625
  }
448
626
 
449
- async function authRevoke() {
450
- const config = await getConfig();
627
+ async function authRevoke(options = {}) {
451
628
  const tokenSet = await getTokenSet();
452
629
 
453
630
  if (!tokenSet || !tokenSet.accessToken) {
@@ -455,6 +632,18 @@ async function authRevoke() {
455
632
  return;
456
633
  }
457
634
 
635
+ if (options.dryRun) {
636
+ console.log("Dry run: would revoke the current token in Clio and clear the local keychain token.");
637
+ return;
638
+ }
639
+
640
+ const confirmed = options.yes === true ? true : await maybePromptRevokeConfirmation();
641
+ if (!confirmed) {
642
+ return;
643
+ }
644
+
645
+ const config = await getConfig();
646
+
458
647
  try {
459
648
  const accessToken = await getValidAccessToken(config, tokenSet);
460
649
  await deauthorize(config, accessToken);
@@ -484,8 +673,8 @@ async function whoAmI(options = {}) {
484
673
  console.log(`ID: ${user.id}`);
485
674
  }
486
675
 
487
- async function setupWizard() {
488
- const config = await authSetup({ skipNextStepHint: true });
676
+ async function setupWizard(options = {}) {
677
+ const config = await authSetup({ ...options, skipNextStepHint: true });
489
678
  console.log("");
490
679
  console.log("Continuing with OAuth login...");
491
680
  await authLogin({ config });
@@ -501,8 +690,7 @@ async function maybeRunSetupOnFirstUse() {
501
690
  return false;
502
691
  }
503
692
 
504
- console.log("No Clio app credentials are configured yet.");
505
- console.log("Starting guided setup...");
693
+ console.log("No credentials found. Starting setup...");
506
694
  console.log("");
507
695
  await setupWizard();
508
696
  return true;