@metabase/rde 0.1.14 → 0.1.15
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.mjs +210 -139
- package/package.json +1 -1
package/dist/cli.mjs
CHANGED
|
@@ -50,6 +50,7 @@ var StepFailedError = class extends Error {
|
|
|
50
50
|
var ExternalToolError = class extends Error {
|
|
51
51
|
tool;
|
|
52
52
|
argv;
|
|
53
|
+
envNames;
|
|
53
54
|
exitCode;
|
|
54
55
|
stderrTail;
|
|
55
56
|
constructor(failure) {
|
|
@@ -57,15 +58,21 @@ var ExternalToolError = class extends Error {
|
|
|
57
58
|
this.name = "ExternalToolError";
|
|
58
59
|
this.tool = failure.tool;
|
|
59
60
|
this.argv = failure.argv;
|
|
61
|
+
this.envNames = failure.envNames;
|
|
60
62
|
this.exitCode = failure.exitCode;
|
|
61
63
|
this.stderrTail = failure.stderrTail;
|
|
62
64
|
}
|
|
63
65
|
};
|
|
66
|
+
function commandLabel(failure) {
|
|
67
|
+
const command = `\`${[failure.tool, ...failure.argv].join(" ")}\``;
|
|
68
|
+
if (failure.envNames.length === 0) return command;
|
|
69
|
+
return `${command}, run by rde with ${failure.envNames.join(" and ")} set,`;
|
|
70
|
+
}
|
|
64
71
|
function describeToolFailure(failure) {
|
|
65
|
-
const command =
|
|
72
|
+
const command = commandLabel(failure);
|
|
66
73
|
const exit = failure.exitCode === null ? "was killed" : `exited with ${failure.exitCode}`;
|
|
67
74
|
const tail = failure.stderrTail.trim();
|
|
68
|
-
return tail.length === 0 ?
|
|
75
|
+
return tail.length === 0 ? `${command} ${exit}` : `${command} ${exit}: ${tail}`;
|
|
69
76
|
}
|
|
70
77
|
var MetabaseApiError = class extends Error {
|
|
71
78
|
status;
|
|
@@ -103,6 +110,9 @@ var MetabaseUnreachableError = class extends Error {
|
|
|
103
110
|
this.reason = reason;
|
|
104
111
|
}
|
|
105
112
|
};
|
|
113
|
+
function isMetabaseNotServing(error) {
|
|
114
|
+
return error instanceof MetabaseUnreachableError || error instanceof MetabaseInitializingError;
|
|
115
|
+
}
|
|
106
116
|
var DownloadError = class extends Error {
|
|
107
117
|
url;
|
|
108
118
|
constructor(url, reason) {
|
|
@@ -1348,9 +1358,6 @@ function createMetabaseClient(baseUrl, auth, signal, options = {}) {
|
|
|
1348
1358
|
user: userResource(http)
|
|
1349
1359
|
};
|
|
1350
1360
|
}
|
|
1351
|
-
function isNotReadyYet(error) {
|
|
1352
|
-
return error instanceof MetabaseInitializingError || error instanceof MetabaseUnreachableError;
|
|
1353
|
-
}
|
|
1354
1361
|
async function waitForHealth(client, options) {
|
|
1355
1362
|
const deadline = Date.now() + options.timeoutMs;
|
|
1356
1363
|
const interval = options.pollIntervalMs ?? HEALTH_POLL_INTERVAL_MS;
|
|
@@ -1359,7 +1366,7 @@ async function waitForHealth(client, options) {
|
|
|
1359
1366
|
await client.health.get();
|
|
1360
1367
|
return;
|
|
1361
1368
|
} catch (error) {
|
|
1362
|
-
if (!
|
|
1369
|
+
if (!isMetabaseNotServing(error)) throw error;
|
|
1363
1370
|
if (error instanceof MetabaseInitializingError) options.onProgress(error.progress);
|
|
1364
1371
|
}
|
|
1365
1372
|
if (!await options.isRunning()) throw new MetabaseUnreachableError(client.baseUrl, "it exited before it became healthy");
|
|
@@ -1386,15 +1393,12 @@ function storedApiKey(state, secrets) {
|
|
|
1386
1393
|
function hostCredentialsClient(signal) {
|
|
1387
1394
|
return (baseUrl, auth) => createMetabaseClient(baseUrl, auth, signal);
|
|
1388
1395
|
}
|
|
1389
|
-
function isNotReady(error) {
|
|
1390
|
-
return error instanceof MetabaseUnreachableError || error instanceof MetabaseInitializingError;
|
|
1391
|
-
}
|
|
1392
1396
|
async function hasAdminOutsideRde(state, client) {
|
|
1393
1397
|
if (state.url === null) return false;
|
|
1394
1398
|
try {
|
|
1395
1399
|
return (await client(state.url, NO_AUTH).session.properties())["has-user-setup"];
|
|
1396
1400
|
} catch (error) {
|
|
1397
|
-
if (
|
|
1401
|
+
if (isMetabaseNotServing(error)) return false;
|
|
1398
1402
|
throw error;
|
|
1399
1403
|
}
|
|
1400
1404
|
}
|
|
@@ -1830,10 +1834,11 @@ function signalProcess(pid, signal) {
|
|
|
1830
1834
|
function stderrTail(stderr) {
|
|
1831
1835
|
return stderr.trim().split("\n").slice(-5).join("\n");
|
|
1832
1836
|
}
|
|
1833
|
-
function toolFailure(tool, argv, result) {
|
|
1837
|
+
function toolFailure(tool, argv, result, options = {}) {
|
|
1834
1838
|
return new ExternalToolError({
|
|
1835
1839
|
tool,
|
|
1836
1840
|
argv,
|
|
1841
|
+
envNames: options.addedEnv === void 0 ? [] : Object.keys(options.addedEnv),
|
|
1837
1842
|
exitCode: result.exitCode,
|
|
1838
1843
|
stderrTail: stderrTail(result.stderr)
|
|
1839
1844
|
});
|
|
@@ -1845,6 +1850,7 @@ async function runShown(runner, output, command, args) {
|
|
|
1845
1850
|
if (exitCode !== 0) throw new ExternalToolError({
|
|
1846
1851
|
tool: command,
|
|
1847
1852
|
argv: args,
|
|
1853
|
+
envNames: [],
|
|
1848
1854
|
exitCode,
|
|
1849
1855
|
stderrTail: ""
|
|
1850
1856
|
});
|
|
@@ -1856,6 +1862,7 @@ async function runShown(runner, output, command, args) {
|
|
|
1856
1862
|
throw new ExternalToolError({
|
|
1857
1863
|
tool: command,
|
|
1858
1864
|
argv: args,
|
|
1865
|
+
envNames: [],
|
|
1859
1866
|
exitCode: result.exitCode,
|
|
1860
1867
|
stderrTail: stderrTail(printed.filter((line) => HAS_TEXT.test(line)).join("\n"))
|
|
1861
1868
|
});
|
|
@@ -1869,6 +1876,7 @@ function parseJsonOutput(tool, argv, stdout, schema) {
|
|
|
1869
1876
|
throw new ExternalToolError({
|
|
1870
1877
|
tool,
|
|
1871
1878
|
argv,
|
|
1879
|
+
envNames: [],
|
|
1872
1880
|
exitCode: 0,
|
|
1873
1881
|
stderrTail: `output is not JSON: ${error instanceof Error ? error.message : String(error)}`
|
|
1874
1882
|
});
|
|
@@ -1879,6 +1887,7 @@ function parseJsonOutput(tool, argv, stdout, schema) {
|
|
|
1879
1887
|
if (error instanceof ZodError) throw new ExternalToolError({
|
|
1880
1888
|
tool,
|
|
1881
1889
|
argv,
|
|
1890
|
+
envNames: [],
|
|
1882
1891
|
exitCode: 0,
|
|
1883
1892
|
stderrTail: `unexpected output shape:\n${z.prettifyError(error)}`
|
|
1884
1893
|
});
|
|
@@ -2344,6 +2353,7 @@ function dockerTool(runner) {
|
|
|
2344
2353
|
throw new ExternalToolError({
|
|
2345
2354
|
tool: DOCKER,
|
|
2346
2355
|
argv: args,
|
|
2356
|
+
envNames: [],
|
|
2347
2357
|
exitCode: 0,
|
|
2348
2358
|
stderrTail: `no port published on ${LOOPBACK_HOST} in: ${result.stdout.trim()}`
|
|
2349
2359
|
});
|
|
@@ -3095,7 +3105,7 @@ function hostDeps$14(ctx) {
|
|
|
3095
3105
|
};
|
|
3096
3106
|
}
|
|
3097
3107
|
function isNotMetabaseYet(error) {
|
|
3098
|
-
return error
|
|
3108
|
+
return isMetabaseNotServing(error) || error instanceof MetabaseApiError;
|
|
3099
3109
|
}
|
|
3100
3110
|
function validateEmail(value) {
|
|
3101
3111
|
return EMAIL.test(value) ? void 0 : "Enter an email address";
|
|
@@ -3455,11 +3465,12 @@ const LogoutResult = z.object({
|
|
|
3455
3465
|
cleared: z.boolean(),
|
|
3456
3466
|
aborted: z.boolean()
|
|
3457
3467
|
}).loose();
|
|
3468
|
+
const MbAuthMethod = z.enum(["oauth", "apiKey"]);
|
|
3458
3469
|
const AuthStatus = z.object({
|
|
3459
3470
|
profile: z.string(),
|
|
3460
3471
|
present: z.boolean(),
|
|
3461
3472
|
url: z.string().nullable(),
|
|
3462
|
-
method:
|
|
3473
|
+
method: MbAuthMethod.nullable(),
|
|
3463
3474
|
user: ProbedUser.nullable()
|
|
3464
3475
|
}).loose();
|
|
3465
3476
|
const AuthProfileStatus = z.enum([
|
|
@@ -3506,11 +3517,41 @@ const RemoteChanges = z.object({
|
|
|
3506
3517
|
remote_version: z.string().nullable(),
|
|
3507
3518
|
local_version: z.string().nullable()
|
|
3508
3519
|
}).loose();
|
|
3520
|
+
function comparableUrl(url) {
|
|
3521
|
+
const parsed = new URL(url);
|
|
3522
|
+
return `${parsed.origin}${parsed.pathname.replace(/\/+$/, "")}`;
|
|
3523
|
+
}
|
|
3524
|
+
function sameMetabaseUrl(a, b) {
|
|
3525
|
+
return comparableUrl(a) === comparableUrl(b);
|
|
3526
|
+
}
|
|
3527
|
+
function isLoggedInAt(status, url) {
|
|
3528
|
+
return status !== null && status.present && status.url !== null && sameMetabaseUrl(status.url, url);
|
|
3529
|
+
}
|
|
3530
|
+
function isOAuthLoginAt(status, url) {
|
|
3531
|
+
return status !== null && status.method === MbAuthMethod.enum.oauth && isLoggedInAt(status, url);
|
|
3532
|
+
}
|
|
3533
|
+
function isApiKeyLoginAt(status, url) {
|
|
3534
|
+
return status !== null && status.method === MbAuthMethod.enum.apiKey && isLoggedInAt(status, url);
|
|
3535
|
+
}
|
|
3536
|
+
function mbInstanceOf(state, secrets) {
|
|
3537
|
+
const configured = configuredRuntime(state);
|
|
3538
|
+
if (configured === null || secrets === null || secrets.apiKey === null) return null;
|
|
3539
|
+
return {
|
|
3540
|
+
url: configured.url,
|
|
3541
|
+
apiKey: secrets.apiKey
|
|
3542
|
+
};
|
|
3543
|
+
}
|
|
3544
|
+
function instanceEnv(instance) {
|
|
3545
|
+
return { addedEnv: {
|
|
3546
|
+
[ENV_MB_URL]: instance.url,
|
|
3547
|
+
[ENV_MB_API_KEY]: instance.apiKey
|
|
3548
|
+
} };
|
|
3549
|
+
}
|
|
3509
3550
|
function mbTool(runner) {
|
|
3510
|
-
async function mbJson(args, schema,
|
|
3551
|
+
async function mbJson(args, schema, options = {}) {
|
|
3511
3552
|
const argv = [...args, "--json"];
|
|
3512
|
-
const result = await runner.run(MB, argv,
|
|
3513
|
-
if (result.exitCode !== 0) throw toolFailure(MB, argv, result);
|
|
3553
|
+
const result = await runner.run(MB, argv, options);
|
|
3554
|
+
if (result.exitCode !== 0) throw toolFailure(MB, argv, result, options);
|
|
3514
3555
|
return parseJsonOutput(MB, argv, result.stdout, schema);
|
|
3515
3556
|
}
|
|
3516
3557
|
async function version(command = MB) {
|
|
@@ -3533,7 +3574,7 @@ function mbTool(runner) {
|
|
|
3533
3574
|
options.url,
|
|
3534
3575
|
"--profile",
|
|
3535
3576
|
options.profile
|
|
3536
|
-
], LoginResult, `${options.apiKey}\n`);
|
|
3577
|
+
], LoginResult, { stdin: `${options.apiKey}\n` });
|
|
3537
3578
|
}
|
|
3538
3579
|
async function authStatus(profile) {
|
|
3539
3580
|
return mbJson([
|
|
@@ -3543,6 +3584,14 @@ function mbTool(runner) {
|
|
|
3543
3584
|
profile
|
|
3544
3585
|
], AuthStatus);
|
|
3545
3586
|
}
|
|
3587
|
+
async function authStatusIfInstalled(profile) {
|
|
3588
|
+
try {
|
|
3589
|
+
return await authStatus(profile);
|
|
3590
|
+
} catch (error) {
|
|
3591
|
+
if (error instanceof ProcessNotFoundError) return null;
|
|
3592
|
+
throw error;
|
|
3593
|
+
}
|
|
3594
|
+
}
|
|
3546
3595
|
async function authLogout(profile) {
|
|
3547
3596
|
return mbJson([
|
|
3548
3597
|
"auth",
|
|
@@ -3562,50 +3611,37 @@ function mbTool(runner) {
|
|
|
3562
3611
|
"0"
|
|
3563
3612
|
], AuthList)).data;
|
|
3564
3613
|
}
|
|
3565
|
-
async function gitSyncStatus(
|
|
3566
|
-
return mbJson([
|
|
3567
|
-
"git-sync",
|
|
3568
|
-
"status",
|
|
3569
|
-
"--profile",
|
|
3570
|
-
profile
|
|
3571
|
-
], GitSyncStatus);
|
|
3614
|
+
async function gitSyncStatus(instance) {
|
|
3615
|
+
return mbJson(["git-sync", "status"], GitSyncStatus, instanceEnv(instance));
|
|
3572
3616
|
}
|
|
3573
|
-
async function gitSyncHasRemoteChanges(
|
|
3574
|
-
return mbJson([
|
|
3575
|
-
"git-sync",
|
|
3576
|
-
"has-remote-changes",
|
|
3577
|
-
"--profile",
|
|
3578
|
-
profile
|
|
3579
|
-
], RemoteChanges);
|
|
3617
|
+
async function gitSyncHasRemoteChanges(instance) {
|
|
3618
|
+
return mbJson(["git-sync", "has-remote-changes"], RemoteChanges, instanceEnv(instance));
|
|
3580
3619
|
}
|
|
3581
|
-
async function gitSyncImport(
|
|
3620
|
+
async function gitSyncImport(instance, options) {
|
|
3582
3621
|
return mbJson([
|
|
3583
3622
|
"git-sync",
|
|
3584
3623
|
"import",
|
|
3585
|
-
"--profile",
|
|
3586
|
-
profile,
|
|
3587
3624
|
"--branch",
|
|
3588
3625
|
options.branch,
|
|
3589
3626
|
"--wait"
|
|
3590
|
-
], GitSyncOutcome);
|
|
3627
|
+
], GitSyncOutcome, instanceEnv(instance));
|
|
3591
3628
|
}
|
|
3592
|
-
async function gitSyncExport(
|
|
3629
|
+
async function gitSyncExport(instance, options) {
|
|
3593
3630
|
return mbJson([
|
|
3594
3631
|
"git-sync",
|
|
3595
3632
|
"export",
|
|
3596
|
-
"--profile",
|
|
3597
|
-
profile,
|
|
3598
3633
|
"--branch",
|
|
3599
3634
|
options.branch,
|
|
3600
3635
|
"--message",
|
|
3601
3636
|
options.message,
|
|
3602
3637
|
"--wait"
|
|
3603
|
-
], GitSyncOutcome);
|
|
3638
|
+
], GitSyncOutcome, instanceEnv(instance));
|
|
3604
3639
|
}
|
|
3605
3640
|
return {
|
|
3606
3641
|
version,
|
|
3607
3642
|
authLogin,
|
|
3608
3643
|
authStatus,
|
|
3644
|
+
authStatusIfInstalled,
|
|
3609
3645
|
authLogout,
|
|
3610
3646
|
authList,
|
|
3611
3647
|
gitSyncStatus,
|
|
@@ -3626,7 +3662,8 @@ const ADMINISTRATORS_GROUP_NAME = "Administrators";
|
|
|
3626
3662
|
function hostDeps$13(ctx) {
|
|
3627
3663
|
return {
|
|
3628
3664
|
client: (baseUrl, auth) => createMetabaseClient(baseUrl, auth, ctx.signal),
|
|
3629
|
-
runner: processRunner(ctx.signal)
|
|
3665
|
+
runner: processRunner(ctx.signal),
|
|
3666
|
+
instanceRunning: () => isInstanceRunning(ctx.state, hostRuntimeDeps(ctx.state.name, ctx.signal))
|
|
3630
3667
|
};
|
|
3631
3668
|
}
|
|
3632
3669
|
async function adminCredentials(ctx) {
|
|
@@ -3682,6 +3719,13 @@ async function authenticates(client) {
|
|
|
3682
3719
|
throw error;
|
|
3683
3720
|
}
|
|
3684
3721
|
}
|
|
3722
|
+
async function forgetCompletion(ctx) {
|
|
3723
|
+
const { [STEP_ID$9]: record, ...steps } = ctx.state.steps;
|
|
3724
|
+
if (record !== void 0) await ctx.saveState({
|
|
3725
|
+
...ctx.state,
|
|
3726
|
+
steps
|
|
3727
|
+
});
|
|
3728
|
+
}
|
|
3685
3729
|
async function workingApiKey(ctx, deps, url) {
|
|
3686
3730
|
const stored = ctx.secrets.apiKey;
|
|
3687
3731
|
if (stored !== null) {
|
|
@@ -3691,6 +3735,7 @@ async function workingApiKey(ctx, deps, url) {
|
|
|
3691
3735
|
}))) return stored;
|
|
3692
3736
|
ctx.ui.warn("Metabase rejects the stored API key; replacing it");
|
|
3693
3737
|
}
|
|
3738
|
+
await forgetCompletion(ctx);
|
|
3694
3739
|
const sessionId = await superuserSession(ctx, deps, url);
|
|
3695
3740
|
const apiKey = await freshApiKey(ctx, deps.client(url, {
|
|
3696
3741
|
kind: "session",
|
|
@@ -3702,20 +3747,24 @@ async function workingApiKey(ctx, deps, url) {
|
|
|
3702
3747
|
});
|
|
3703
3748
|
return apiKey;
|
|
3704
3749
|
}
|
|
3705
|
-
|
|
3706
|
-
|
|
3707
|
-
|
|
3708
|
-
|
|
3709
|
-
|
|
3710
|
-
throw error;
|
|
3711
|
-
}
|
|
3750
|
+
function elsewhereUrl(status, url, movedFrom) {
|
|
3751
|
+
if (status === null || !status.present || status.url === null) return null;
|
|
3752
|
+
if (sameMetabaseUrl(status.url, url)) return null;
|
|
3753
|
+
if (movedFrom !== null && sameMetabaseUrl(status.url, movedFrom)) return null;
|
|
3754
|
+
return status.url;
|
|
3712
3755
|
}
|
|
3713
3756
|
async function chooseProfile(ctx, mb, url) {
|
|
3714
3757
|
const profile = ctx.state.mbProfile;
|
|
3715
|
-
|
|
3716
|
-
|
|
3717
|
-
|
|
3718
|
-
|
|
3758
|
+
const status = await mb.authStatusIfInstalled(profile);
|
|
3759
|
+
if (profile !== "default") return {
|
|
3760
|
+
profile,
|
|
3761
|
+
status
|
|
3762
|
+
};
|
|
3763
|
+
const previous = elsewhereUrl(status, url, ctx.state.movedFrom);
|
|
3764
|
+
if (previous === null) return {
|
|
3765
|
+
profile,
|
|
3766
|
+
status
|
|
3767
|
+
};
|
|
3719
3768
|
const named = namedMbProfile(ctx.state.name);
|
|
3720
3769
|
if (await ctx.prompts.select({
|
|
3721
3770
|
key: API_KEY_KEYS.profile,
|
|
@@ -3730,13 +3779,19 @@ async function chooseProfile(ctx, mb, url) {
|
|
|
3730
3779
|
recommended: "default"
|
|
3731
3780
|
}) === "default") {
|
|
3732
3781
|
ctx.ui.info(`Replacing mb profile ${DEFAULT_MB_PROFILE}, which pointed at ${previous}`);
|
|
3733
|
-
return
|
|
3782
|
+
return {
|
|
3783
|
+
profile,
|
|
3784
|
+
status
|
|
3785
|
+
};
|
|
3734
3786
|
}
|
|
3735
3787
|
await ctx.saveState({
|
|
3736
3788
|
...ctx.state,
|
|
3737
3789
|
mbProfile: named
|
|
3738
3790
|
});
|
|
3739
|
-
return
|
|
3791
|
+
return {
|
|
3792
|
+
profile: named,
|
|
3793
|
+
status: await mb.authStatusIfInstalled(named)
|
|
3794
|
+
};
|
|
3740
3795
|
}
|
|
3741
3796
|
function usageNote(profile) {
|
|
3742
3797
|
if (profile === "default") return `${mbCommand(profile)} <command> uses this instance`;
|
|
@@ -3748,31 +3803,34 @@ function apiKeyStep(deps = hostDeps$13) {
|
|
|
3748
3803
|
title: "Connect the Metabase CLI",
|
|
3749
3804
|
redoHint: "`rde status` shows the mb profile it uses.",
|
|
3750
3805
|
async detect(ctx) {
|
|
3751
|
-
|
|
3752
|
-
const
|
|
3753
|
-
|
|
3806
|
+
const configured = configuredRuntime(ctx.state);
|
|
3807
|
+
const apiKey = ctx.secrets.apiKey;
|
|
3808
|
+
if (configured === null || apiKey === null || ctx.state.steps[STEP_ID$9]?.status !== "done") return "todo";
|
|
3809
|
+
const resolved = deps(ctx);
|
|
3810
|
+
if (!await resolved.instanceRunning()) return "todo";
|
|
3811
|
+
if (!isLoggedInAt(await mbTool(resolved.runner).authStatusIfInstalled(ctx.state.mbProfile), configured.url)) return "todo";
|
|
3754
3812
|
try {
|
|
3755
|
-
|
|
3813
|
+
return await authenticates(resolved.client(configured.url, {
|
|
3814
|
+
kind: "api-key",
|
|
3815
|
+
key: apiKey
|
|
3816
|
+
})) ? "done" : "todo";
|
|
3756
3817
|
} catch (error) {
|
|
3757
|
-
if (error instanceof
|
|
3818
|
+
if (isMetabaseNotServing(error) || error instanceof MetabaseApiError) return "todo";
|
|
3758
3819
|
throw error;
|
|
3759
3820
|
}
|
|
3760
|
-
return (await mb.authList()).find((candidate) => candidate.profile === profile)?.status === "ok" ? "done" : "todo";
|
|
3761
3821
|
},
|
|
3762
3822
|
async run(ctx) {
|
|
3763
3823
|
const resolved = deps(ctx);
|
|
3764
3824
|
const configured = requireConfiguredRuntime(ctx.state);
|
|
3765
3825
|
const apiKey = await workingApiKey(ctx, resolved, configured.url);
|
|
3766
3826
|
const mb = mbTool(resolved.runner);
|
|
3767
|
-
const profile = await chooseProfile(ctx, mb, configured.url);
|
|
3768
|
-
|
|
3827
|
+
const { profile, status } = await chooseProfile(ctx, mb, configured.url);
|
|
3828
|
+
if (isOAuthLoginAt(status, configured.url)) ctx.ui.info(`mb profile ${profile} is logged in to this instance with OAuth; leaving it as it is (\`mb auth logout --profile ${profile}\` first to use rde's API key instead)`);
|
|
3829
|
+
else await mb.authLogin({
|
|
3769
3830
|
url: configured.url,
|
|
3770
3831
|
profile,
|
|
3771
3832
|
apiKey
|
|
3772
3833
|
});
|
|
3773
|
-
const entry = (await mb.authList()).find((candidate) => candidate.profile === profile);
|
|
3774
|
-
if (entry === void 0) throw new ConfigError(`\`mb auth list\` does not show profile ${profile} after login`);
|
|
3775
|
-
if (entry.status !== "ok") throw new ConfigError(`mb profile ${profile} reports ${entry.status} after login`);
|
|
3776
3834
|
if (ctx.state.movedFrom !== null) await ctx.saveState({
|
|
3777
3835
|
...ctx.state,
|
|
3778
3836
|
movedFrom: null
|
|
@@ -6019,7 +6077,7 @@ async function applySettings(run, configured) {
|
|
|
6019
6077
|
async function remoteHasContent(run, gitRemote) {
|
|
6020
6078
|
switch (gitRemote.kind) {
|
|
6021
6079
|
case "local-bare": return run.git.hasMetabaseContent(gitRemote.workdir);
|
|
6022
|
-
case "hosted": return (await mbTool(run.deps.runner).gitSyncHasRemoteChanges(run.
|
|
6080
|
+
case "hosted": return (await mbTool(run.deps.runner).gitSyncHasRemoteChanges(run.instance)).has_changes;
|
|
6023
6081
|
}
|
|
6024
6082
|
}
|
|
6025
6083
|
function reportImport(run, outcome) {
|
|
@@ -6041,7 +6099,7 @@ async function importExistingContent(run, gitRemote) {
|
|
|
6041
6099
|
const spinner = run.ctx.ui.spinner("Importing the repository's content into Metabase…");
|
|
6042
6100
|
let outcome;
|
|
6043
6101
|
try {
|
|
6044
|
-
outcome = await mbTool(run.deps.runner).gitSyncImport(run.
|
|
6102
|
+
outcome = await mbTool(run.deps.runner).gitSyncImport(run.instance, { branch: gitRemote.branch });
|
|
6045
6103
|
} catch (error) {
|
|
6046
6104
|
spinner.fail("Import failed");
|
|
6047
6105
|
throw error;
|
|
@@ -6085,15 +6143,17 @@ function isMbUnavailable(error) {
|
|
|
6085
6143
|
}
|
|
6086
6144
|
async function runGitSync(ctx, deps) {
|
|
6087
6145
|
const configured = requireConfiguredRuntime(ctx.state);
|
|
6088
|
-
|
|
6146
|
+
const instance = mbInstanceOf(ctx.state, ctx.secrets);
|
|
6147
|
+
if (instance === null) throw new ConfigError("no API key yet; the api-key step comes first");
|
|
6089
6148
|
const run = {
|
|
6090
6149
|
ctx,
|
|
6091
6150
|
deps,
|
|
6092
6151
|
git: gitTool(deps.runner),
|
|
6093
|
-
client: deps.client(
|
|
6152
|
+
client: deps.client(instance.url, {
|
|
6094
6153
|
kind: "api-key",
|
|
6095
|
-
key:
|
|
6096
|
-
})
|
|
6154
|
+
key: instance.apiKey
|
|
6155
|
+
}),
|
|
6156
|
+
instance
|
|
6097
6157
|
};
|
|
6098
6158
|
const flow = await runFlow({
|
|
6099
6159
|
remote: remoteScreen(run),
|
|
@@ -6123,8 +6183,10 @@ function gitSyncStep(deps = hostDeps$12) {
|
|
|
6123
6183
|
if (!hasRemoteSync(ctx.state.license)) return "not-applicable";
|
|
6124
6184
|
if (ctx.state.steps[STEP_ID$8]?.status === "skipped") return "not-applicable";
|
|
6125
6185
|
if (ctx.state.gitRemote === null || ctx.state.steps[STEP_ID$8]?.status !== "done") return "todo";
|
|
6186
|
+
const instance = mbInstanceOf(ctx.state, ctx.secrets);
|
|
6187
|
+
if (instance === null) return "todo";
|
|
6126
6188
|
try {
|
|
6127
|
-
return (await mbTool(deps(ctx).runner).gitSyncStatus(
|
|
6189
|
+
return (await mbTool(deps(ctx).runner).gitSyncStatus(instance)).branch === null ? "todo" : "done";
|
|
6128
6190
|
} catch (error) {
|
|
6129
6191
|
if (isMbUnavailable(error)) return "todo";
|
|
6130
6192
|
throw error;
|
|
@@ -6564,9 +6626,6 @@ function hostDatabasesDeps(signal) {
|
|
|
6564
6626
|
now: () => /* @__PURE__ */ new Date()
|
|
6565
6627
|
};
|
|
6566
6628
|
}
|
|
6567
|
-
function isNotServingYet$2(error) {
|
|
6568
|
-
return error instanceof MetabaseUnreachableError || error instanceof MetabaseInitializingError;
|
|
6569
|
-
}
|
|
6570
6629
|
function requireApiKey$2(state, apiKey) {
|
|
6571
6630
|
if (apiKey === null) throw new ConfigError(`instance ${state.name} has no API key yet; run \`rde init\``);
|
|
6572
6631
|
return apiKey;
|
|
@@ -6707,7 +6766,7 @@ function databasesStep(options, deps = hostDeps$10) {
|
|
|
6707
6766
|
try {
|
|
6708
6767
|
return (await client.database.list()).data.some((database) => isOwn(database.id)) ? "done" : "todo";
|
|
6709
6768
|
} catch (error) {
|
|
6710
|
-
if (
|
|
6769
|
+
if (isMetabaseNotServing(error) || isRejectedCredential(error)) return "todo";
|
|
6711
6770
|
throw error;
|
|
6712
6771
|
}
|
|
6713
6772
|
},
|
|
@@ -6886,6 +6945,7 @@ async function stillWrong(install, after, deps) {
|
|
|
6886
6945
|
const fail = (stderrTail) => new ExternalToolError({
|
|
6887
6946
|
tool: install.command,
|
|
6888
6947
|
argv: install.args,
|
|
6948
|
+
envNames: [],
|
|
6889
6949
|
exitCode: 0,
|
|
6890
6950
|
stderrTail
|
|
6891
6951
|
});
|
|
@@ -6978,7 +7038,7 @@ function hostDeps$8(ctx) {
|
|
|
6978
7038
|
}
|
|
6979
7039
|
function isNoTokenYet(error) {
|
|
6980
7040
|
if (error instanceof MetabaseApiError) return error.status === NOT_FOUND_STATUS$2;
|
|
6981
|
-
return error
|
|
7041
|
+
return isMetabaseNotServing(error);
|
|
6982
7042
|
}
|
|
6983
7043
|
function validateToken(value) {
|
|
6984
7044
|
if (value.length === 0 || TOKEN_PATTERN.test(value)) return;
|
|
@@ -7054,7 +7114,8 @@ function licenseStep(deps = hostDeps$8) {
|
|
|
7054
7114
|
validThru: status["valid-thru"] ?? null
|
|
7055
7115
|
}
|
|
7056
7116
|
});
|
|
7057
|
-
|
|
7117
|
+
const mb = mbTool(resolved.runner);
|
|
7118
|
+
if (isApiKeyLoginAt(await mb.authStatus(ctx.state.mbProfile), configured.url)) await mb.authLogin({
|
|
7058
7119
|
url: configured.url,
|
|
7059
7120
|
profile: ctx.state.mbProfile,
|
|
7060
7121
|
apiKey
|
|
@@ -7413,9 +7474,6 @@ function hostDeps$5(ctx) {
|
|
|
7413
7474
|
now: () => /* @__PURE__ */ new Date()
|
|
7414
7475
|
};
|
|
7415
7476
|
}
|
|
7416
|
-
function isNotServingYet$1(error) {
|
|
7417
|
-
return error instanceof MetabaseUnreachableError || error instanceof MetabaseInitializingError;
|
|
7418
|
-
}
|
|
7419
7477
|
function isNotFound$2(error) {
|
|
7420
7478
|
return error instanceof MetabaseApiError && error.status === NOT_FOUND_STATUS$1;
|
|
7421
7479
|
}
|
|
@@ -7576,7 +7634,7 @@ function sampleDatabaseStep(deps = hostDeps$5) {
|
|
|
7576
7634
|
try {
|
|
7577
7635
|
return await recordedDatabase(record, client) === null ? "todo" : "done";
|
|
7578
7636
|
} catch (error) {
|
|
7579
|
-
if (
|
|
7637
|
+
if (isMetabaseNotServing(error) || isRejectedCredential(error)) return "todo";
|
|
7580
7638
|
throw error;
|
|
7581
7639
|
}
|
|
7582
7640
|
},
|
|
@@ -7613,7 +7671,7 @@ function hostDeps$4(ctx) {
|
|
|
7613
7671
|
};
|
|
7614
7672
|
}
|
|
7615
7673
|
function isNotHealthy(error) {
|
|
7616
|
-
return error
|
|
7674
|
+
return isMetabaseNotServing(error) || error instanceof MetabaseApiError;
|
|
7617
7675
|
}
|
|
7618
7676
|
async function runningVersion(client) {
|
|
7619
7677
|
return (await client.session.properties()).version.tag;
|
|
@@ -7718,9 +7776,6 @@ function hostDeps$3(ctx) {
|
|
|
7718
7776
|
probe: hostAgentProbe()
|
|
7719
7777
|
};
|
|
7720
7778
|
}
|
|
7721
|
-
function isNotServing$1(error) {
|
|
7722
|
-
return error instanceof MetabaseUnreachableError || error instanceof MetabaseInitializingError;
|
|
7723
|
-
}
|
|
7724
7779
|
function loginLine(kind, email) {
|
|
7725
7780
|
switch (kind) {
|
|
7726
7781
|
case "generated":
|
|
@@ -7737,7 +7792,7 @@ async function databaseLines$1(state, apiKey, deps) {
|
|
|
7737
7792
|
key: apiKey
|
|
7738
7793
|
}).database.listWithStubs()).data;
|
|
7739
7794
|
} catch (error) {
|
|
7740
|
-
if (
|
|
7795
|
+
if (isMetabaseNotServing(error)) return ["Databases: Metabase is not running; `rde start`"];
|
|
7741
7796
|
if (isRejectedCredential(error)) return ["Databases: Metabase rejects the stored API key; `rde init --only api-key`"];
|
|
7742
7797
|
throw error;
|
|
7743
7798
|
}
|
|
@@ -7819,9 +7874,6 @@ function hostDeps$2(ctx) {
|
|
|
7819
7874
|
instanceRunning: () => isInstanceRunning(ctx.state, hostRuntimeDeps(ctx.state.name, ctx.signal))
|
|
7820
7875
|
};
|
|
7821
7876
|
}
|
|
7822
|
-
function isNotServingYet(error) {
|
|
7823
|
-
return error instanceof MetabaseUnreachableError || error instanceof MetabaseInitializingError;
|
|
7824
|
-
}
|
|
7825
7877
|
function transformsStep(deps = hostDeps$2) {
|
|
7826
7878
|
return {
|
|
7827
7879
|
id: STEP_ID,
|
|
@@ -7838,7 +7890,7 @@ function transformsStep(deps = hostDeps$2) {
|
|
|
7838
7890
|
try {
|
|
7839
7891
|
return await client.setting.transformsDecided() ? "done" : "todo";
|
|
7840
7892
|
} catch (error) {
|
|
7841
|
-
if (
|
|
7893
|
+
if (isMetabaseNotServing(error) || isRejectedCredential(error)) return "todo";
|
|
7842
7894
|
throw error;
|
|
7843
7895
|
}
|
|
7844
7896
|
},
|
|
@@ -9365,31 +9417,46 @@ async function credentialRows(state, secrets, apiKey, deps) {
|
|
|
9365
9417
|
stateFileRow(files)
|
|
9366
9418
|
];
|
|
9367
9419
|
}
|
|
9368
|
-
function
|
|
9369
|
-
|
|
9420
|
+
function brokenProfileRepair(profile, method) {
|
|
9421
|
+
if (method === MbAuthMethod.enum.oauth) return {
|
|
9422
|
+
hint: `\`mb auth login --profile ${profile}\` logs it in again; or \`mb auth logout --profile ${profile}\`, then \`rde init --only api-key\` hands the profile to rde's API key`,
|
|
9423
|
+
step: null
|
|
9424
|
+
};
|
|
9425
|
+
return {
|
|
9426
|
+
hint: "`rde init --only api-key` logs it in again",
|
|
9427
|
+
step: API_KEY_STEP
|
|
9428
|
+
};
|
|
9370
9429
|
}
|
|
9371
|
-
|
|
9430
|
+
function unprobedReason(method, healthy, access) {
|
|
9431
|
+
if (access.kind === "accepted") return null;
|
|
9432
|
+
if (method !== MbAuthMethod.enum.oauth) return access.reason;
|
|
9433
|
+
return healthy ? null : NOT_HEALTHY_REASON;
|
|
9434
|
+
}
|
|
9435
|
+
async function mbProfileRow(state, url, deps, profiles, mbUnusableReason, healthy, access) {
|
|
9436
|
+
const id = "mb-profile";
|
|
9372
9437
|
const label = "mb profile";
|
|
9373
|
-
|
|
9374
|
-
if (
|
|
9375
|
-
const
|
|
9376
|
-
if (
|
|
9377
|
-
detail: `${
|
|
9438
|
+
const profile = state.mbProfile;
|
|
9439
|
+
if (mbUnusableReason !== null) return skipped(id, label, mbUnusableReason);
|
|
9440
|
+
const status = await mbTool(deps.runner).authStatus(profile);
|
|
9441
|
+
if (!status.present || status.url === null) return failed(id, label, {
|
|
9442
|
+
detail: `${profile} is not configured`,
|
|
9378
9443
|
hint: "`rde init --only api-key`",
|
|
9379
|
-
step:
|
|
9444
|
+
step: API_KEY_STEP
|
|
9380
9445
|
});
|
|
9381
|
-
if (
|
|
9382
|
-
detail: `${
|
|
9446
|
+
if (!sameMetabaseUrl(status.url, url)) return failed(id, label, {
|
|
9447
|
+
detail: `${profile} points at ${status.url}, not at this instance (${url})`,
|
|
9383
9448
|
hint: "`rde init --only api-key` logs it back in against the instance",
|
|
9384
|
-
step:
|
|
9449
|
+
step: API_KEY_STEP
|
|
9385
9450
|
});
|
|
9386
|
-
|
|
9387
|
-
|
|
9388
|
-
|
|
9389
|
-
|
|
9451
|
+
const unprobed = unprobedReason(status.method, healthy, access);
|
|
9452
|
+
if (unprobed !== null) return skipped(id, label, unprobed);
|
|
9453
|
+
const how = status.method === MbAuthMethod.enum.oauth ? " (OAuth)" : "";
|
|
9454
|
+
const entry = (await profiles()).find((candidate) => candidate.profile === profile);
|
|
9455
|
+
if (entry === void 0 || entry.status !== "ok") return failed(id, label, {
|
|
9456
|
+
detail: `${profile}${how}: ${entry === void 0 ? "not listed by `mb auth list`" : entry.status}`,
|
|
9457
|
+
...brokenProfileRepair(profile, status.method)
|
|
9390
9458
|
});
|
|
9391
|
-
|
|
9392
|
-
return ok("mb-profile", label, `${state.mbProfile}: ok${user}`);
|
|
9459
|
+
return ok(id, label, `${profile}${how}: ok${entry.user === null ? "" : ` as ${entry.user.name}`}`);
|
|
9393
9460
|
}
|
|
9394
9461
|
function hostOf(url) {
|
|
9395
9462
|
return URL.canParse(url) ? new URL(url).host : "an address that is not a URL";
|
|
@@ -9410,8 +9477,8 @@ async function mbProfileEnvRow(name, rdeProfile, profiles, mbUnusableReason) {
|
|
|
9410
9477
|
async function environmentRows(env, rdeProfile, profiles, mbUnusableReason) {
|
|
9411
9478
|
const rows = [];
|
|
9412
9479
|
if (env.profile !== null) rows.push(await mbProfileEnvRow(env.profile, rdeProfile, profiles, mbUnusableReason));
|
|
9413
|
-
if (env.url !== null) rows.push(note$1(`env.${ENV_MB_URL}`, ENV_MB_URL, `every \`mb\` call goes to ${hostOf(env.url)}, whatever \`--profile\` says
|
|
9414
|
-
if (env.apiKeySet) rows.push(note$1(`env.${ENV_MB_API_KEY}`, ENV_MB_API_KEY, "set (value not shown): every `mb` call sends it, whatever `--profile` says
|
|
9480
|
+
if (env.url !== null) rows.push(note$1(`env.${ENV_MB_URL}`, ENV_MB_URL, `every \`mb\` call goes to ${hostOf(env.url)}, whatever \`--profile\` says`, unsetHint(ENV_MB_URL)));
|
|
9481
|
+
if (env.apiKeySet) rows.push(note$1(`env.${ENV_MB_API_KEY}`, ENV_MB_API_KEY, "set (value not shown): every `mb` call sends it, whatever `--profile` says", unsetHint(ENV_MB_API_KEY)));
|
|
9415
9482
|
return rows;
|
|
9416
9483
|
}
|
|
9417
9484
|
const PROFILE_STATUS_WORDS = {
|
|
@@ -9513,12 +9580,13 @@ async function sampleDatabaseRow(record, deps, dockerOk, access) {
|
|
|
9513
9580
|
function isMbFailure$1(error) {
|
|
9514
9581
|
return error instanceof ProcessNotFoundError || error instanceof ExternalToolError;
|
|
9515
9582
|
}
|
|
9516
|
-
async function gitSyncRow(state, deps) {
|
|
9583
|
+
async function gitSyncRow(state, instance, deps) {
|
|
9517
9584
|
const label = "remote sync";
|
|
9518
9585
|
if (!hasRemoteSync(state.license)) return skipped("git-sync", label, `needs a license with ${REMOTE_SYNC_FEATURE}`);
|
|
9519
9586
|
if (state.gitRemote === null) return skipped("git-sync", label, "not configured; `rde init --from git-sync`");
|
|
9587
|
+
if (instance === null) return skipped("git-sync", label, "no API key in the secrets file");
|
|
9520
9588
|
try {
|
|
9521
|
-
const status = await mbTool(deps.runner).gitSyncStatus(
|
|
9589
|
+
const status = await mbTool(deps.runner).gitSyncStatus(instance);
|
|
9522
9590
|
if (status.branch === null) return failed("git-sync", label, {
|
|
9523
9591
|
detail: "Metabase reports no branch",
|
|
9524
9592
|
hint: "`rde init --only git-sync`",
|
|
@@ -9676,13 +9744,13 @@ async function instanceRows(state, secrets, deps, dockerOk, profiles, mbUnusable
|
|
|
9676
9744
|
properties === null ? skipped("version", "Metabase version", NOT_HEALTHY_REASON) : versionRow(state, properties),
|
|
9677
9745
|
properties === null ? skipped("admin", "admin user", NOT_HEALTHY_REASON) : adminRow(state, properties),
|
|
9678
9746
|
...await credentialRows(state, secrets, apiKey, deps),
|
|
9679
|
-
await mbProfileRow(state, configured.url, profiles, mbUnusableReason, healthy)
|
|
9747
|
+
await mbProfileRow(state, configured.url, deps, profiles, mbUnusableReason, healthy, apiKey.access)
|
|
9680
9748
|
];
|
|
9681
9749
|
const access = apiKey.access;
|
|
9682
9750
|
if (access.kind === "unusable") rows.push(skipped("license", "license", access.reason), skipped("databases", "databases", access.reason));
|
|
9683
9751
|
else rows.push(await licenseRow(access.client), await databasesRow(state, access.client));
|
|
9684
9752
|
rows.push(state.sampleDatabase === null ? skipped("sample-database", SAMPLE_LABEL, "not connected; `rde init` adds one") : await sampleDatabaseRow(state.sampleDatabase, deps, dockerOk, access));
|
|
9685
|
-
rows.push(await gitSyncRow(state, deps));
|
|
9753
|
+
rows.push(await gitSyncRow(state, mbInstanceOf(state, secrets), deps));
|
|
9686
9754
|
return rows;
|
|
9687
9755
|
}
|
|
9688
9756
|
async function collectDoctor(instance, state, secrets, deps) {
|
|
@@ -10257,20 +10325,12 @@ async function moveToPort(ctx, port, deps) {
|
|
|
10257
10325
|
await runtimeFromState(next, deps.runtime).reconfigure();
|
|
10258
10326
|
ctx.ui.info(`Moved instance ${ctx.state.name} from port ${previous} to ${port}`);
|
|
10259
10327
|
}
|
|
10260
|
-
async function profileStatus(runner, profile) {
|
|
10261
|
-
try {
|
|
10262
|
-
return await mbTool(runner).authStatus(profile);
|
|
10263
|
-
} catch (error) {
|
|
10264
|
-
if (error instanceof ProcessNotFoundError) return null;
|
|
10265
|
-
throw error;
|
|
10266
|
-
}
|
|
10267
|
-
}
|
|
10268
10328
|
async function followMove(ctx, from, runner) {
|
|
10269
10329
|
const configured = requireConfiguredRuntime(ctx.state);
|
|
10270
10330
|
const apiKey = ctx.secrets.apiKey;
|
|
10271
10331
|
const profile = ctx.state.mbProfile;
|
|
10272
|
-
const status = apiKey === null ? null : await
|
|
10273
|
-
if (apiKey !== null && status
|
|
10332
|
+
const status = apiKey === null ? null : await mbTool(runner).authStatusIfInstalled(profile);
|
|
10333
|
+
if (apiKey !== null && isLoggedInAt(status, from)) {
|
|
10274
10334
|
await mbTool(runner).authLogin({
|
|
10275
10335
|
url: configured.url,
|
|
10276
10336
|
profile,
|
|
@@ -10353,15 +10413,12 @@ function setupOf(ctx, steps) {
|
|
|
10353
10413
|
resume: `rde init --from ${pending.id}`
|
|
10354
10414
|
};
|
|
10355
10415
|
}
|
|
10356
|
-
function isNotServing(error) {
|
|
10357
|
-
return error instanceof MetabaseUnreachableError || error instanceof MetabaseInitializingError;
|
|
10358
|
-
}
|
|
10359
10416
|
async function databasesOf(ctx, deps, health) {
|
|
10360
10417
|
if (health !== "healthy" || ctx.secrets.apiKey === null) return null;
|
|
10361
10418
|
try {
|
|
10362
10419
|
return await listDatabases(ctx.state, ctx.secrets.apiKey, { client: deps.client });
|
|
10363
10420
|
} catch (error) {
|
|
10364
|
-
if (
|
|
10421
|
+
if (isMetabaseNotServing(error) || isRejectedCredential(error)) return null;
|
|
10365
10422
|
throw error;
|
|
10366
10423
|
}
|
|
10367
10424
|
}
|
|
@@ -10391,10 +10448,13 @@ function isMbFailure(error) {
|
|
|
10391
10448
|
}
|
|
10392
10449
|
async function gitSyncOf(ctx, deps) {
|
|
10393
10450
|
if (ctx.state.gitRemote === null) return null;
|
|
10451
|
+
if (configuredRuntime(ctx.state) === null) return { error: "no Metabase runtime configured; `rde init`" };
|
|
10452
|
+
const instance = mbInstanceOf(ctx.state, ctx.secrets);
|
|
10453
|
+
if (instance === null) return { error: "no API key in the secrets file; `rde init --only api-key`" };
|
|
10394
10454
|
try {
|
|
10395
10455
|
const mb = mbTool(deps.runner);
|
|
10396
|
-
const status = await mb.gitSyncStatus(
|
|
10397
|
-
const changes = await mb.gitSyncHasRemoteChanges(
|
|
10456
|
+
const status = await mb.gitSyncStatus(instance);
|
|
10457
|
+
const changes = await mb.gitSyncHasRemoteChanges(instance);
|
|
10398
10458
|
return {
|
|
10399
10459
|
branch: status.branch,
|
|
10400
10460
|
dirty: status.is_dirty,
|
|
@@ -10555,7 +10615,6 @@ const stop = defineRdeCommand({
|
|
|
10555
10615
|
});
|
|
10556
10616
|
//#endregion
|
|
10557
10617
|
//#region src/steps/uninstall.ts
|
|
10558
|
-
const API_KEY_METHOD = "apiKey";
|
|
10559
10618
|
function hostUninstallDeps(signal, childOutput) {
|
|
10560
10619
|
const runner = processRunner(signal);
|
|
10561
10620
|
return {
|
|
@@ -10714,8 +10773,8 @@ async function findJar(source, removeImage) {
|
|
|
10714
10773
|
}
|
|
10715
10774
|
function isInstanceProfile(status, urls) {
|
|
10716
10775
|
if (!status.present) return false;
|
|
10717
|
-
if (urls !== null) return
|
|
10718
|
-
return status.method ===
|
|
10776
|
+
if (urls !== null) return urls.some((url) => isLoggedInAt(status, url));
|
|
10777
|
+
return status.method === MbAuthMethod.enum.apiKey && status.user?.name === "rde";
|
|
10719
10778
|
}
|
|
10720
10779
|
function instanceUrls(state) {
|
|
10721
10780
|
if (state.url === null) return null;
|
|
@@ -10723,23 +10782,34 @@ function instanceUrls(state) {
|
|
|
10723
10782
|
}
|
|
10724
10783
|
async function findMbProfiles(candidates, urls, runner) {
|
|
10725
10784
|
const mb = mbTool(runner);
|
|
10726
|
-
const
|
|
10785
|
+
const found = {
|
|
10786
|
+
removals: [],
|
|
10787
|
+
kept: []
|
|
10788
|
+
};
|
|
10727
10789
|
for (const profile of candidates) {
|
|
10728
10790
|
let status;
|
|
10729
10791
|
try {
|
|
10730
10792
|
status = await mb.authStatus(profile);
|
|
10731
10793
|
} catch (error) {
|
|
10732
|
-
if (error instanceof ProcessNotFoundError) return
|
|
10794
|
+
if (error instanceof ProcessNotFoundError) return {
|
|
10795
|
+
removals: [],
|
|
10796
|
+
kept: []
|
|
10797
|
+
};
|
|
10733
10798
|
throw error;
|
|
10734
10799
|
}
|
|
10735
|
-
if (isInstanceProfile(status, urls))
|
|
10800
|
+
if (!isInstanceProfile(status, urls)) continue;
|
|
10801
|
+
if (status.method === MbAuthMethod.enum.oauth) {
|
|
10802
|
+
found.kept.push(`mb profile ${profile}, logged in with OAuth; \`mb auth logout --profile ${profile}\` logs it out`);
|
|
10803
|
+
continue;
|
|
10804
|
+
}
|
|
10805
|
+
found.removals.push({
|
|
10736
10806
|
label: `mb profile ${profile}`,
|
|
10737
10807
|
remove: async () => {
|
|
10738
10808
|
await mb.authLogout(profile);
|
|
10739
10809
|
}
|
|
10740
10810
|
});
|
|
10741
10811
|
}
|
|
10742
|
-
return
|
|
10812
|
+
return found;
|
|
10743
10813
|
}
|
|
10744
10814
|
function skillLabel(names, entries) {
|
|
10745
10815
|
const agents = [...new Set(entries.flatMap((entry) => entry.agents))];
|
|
@@ -10821,7 +10891,8 @@ async function planUninstall(options, deps) {
|
|
|
10821
10891
|
if (jar.kept !== null) kept.push(jar.kept);
|
|
10822
10892
|
}
|
|
10823
10893
|
const profiles = state === null ? await findMbProfiles([.../* @__PURE__ */ new Set([mbProfileFor(options.instance), namedMbProfile(options.instance)])], null, deps.runner) : await findMbProfiles([state.mbProfile], instanceUrls(state), deps.runner);
|
|
10824
|
-
removals.push(...profiles);
|
|
10894
|
+
removals.push(...profiles.removals);
|
|
10895
|
+
kept.push(...profiles.kept);
|
|
10825
10896
|
if (options.removeSkill) {
|
|
10826
10897
|
const skill = await findSkill(deps.runner, deps.childOutput);
|
|
10827
10898
|
if (skill !== null) removals.push(skill);
|
|
@@ -11238,7 +11309,7 @@ const upgradeCommand = defineRdeCommand({
|
|
|
11238
11309
|
const main = defineCommand({
|
|
11239
11310
|
meta: {
|
|
11240
11311
|
name: "rde",
|
|
11241
|
-
version: "0.1.
|
|
11312
|
+
version: "0.1.15",
|
|
11242
11313
|
description: "Prepares a local machine for data-engineering work with Metabase and coding agents"
|
|
11243
11314
|
},
|
|
11244
11315
|
subCommands: {
|