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.
- package/README.md +48 -12
- package/bin/not-manage.js +15 -2
- package/package.json +1 -2
- package/src/agent-input.js +101 -0
- package/src/cli-errors.js +109 -0
- package/src/cli.js +835 -64
- package/src/clio-api.js +108 -6
- package/src/commands-agent-context.js +119 -0
- package/src/commands-auth.js +336 -148
- package/src/commands-doctor.js +178 -0
- package/src/commands-request.js +144 -0
- package/src/compact-output.js +89 -0
- package/src/prompt.js +48 -0
- package/src/redaction.js +62 -25
- package/src/resource-command-runner.js +75 -4
- package/src/resource-handlers.js +15 -6
- package/src/resource-metadata.js +18 -0
- package/src/store.js +1 -1
- package/src/postinstall.js +0 -140
package/src/commands-auth.js
CHANGED
|
@@ -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
|
|
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
|
-
`
|
|
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("
|
|
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
|
|
182
|
-
|
|
183
|
-
console.log("
|
|
184
|
-
console.log(
|
|
185
|
-
console.log(
|
|
186
|
-
console.log(
|
|
187
|
-
console.log(
|
|
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
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
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
|
-
|
|
196
|
-
|
|
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
|
-
|
|
201
|
-
|
|
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
|
-
|
|
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
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
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
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
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
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
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
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
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
|
-
"
|
|
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
|
|
295
|
-
throw new
|
|
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(
|
|
445
|
+
printSetupIntro();
|
|
303
446
|
console.log("");
|
|
304
447
|
|
|
305
|
-
const
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
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
|
-
|
|
518
|
+
if (configChanged) {
|
|
519
|
+
await clearTokenSet();
|
|
520
|
+
}
|
|
346
521
|
|
|
347
522
|
console.log("");
|
|
348
|
-
console.log("
|
|
349
|
-
|
|
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
|
|
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;
|