@better-auth/cli 1.3.7 → 1.3.8-beta.10
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/index.mjs +666 -42
- package/package.json +8 -11
package/dist/index.mjs
CHANGED
|
@@ -4,13 +4,12 @@ import { parse } from 'dotenv';
|
|
|
4
4
|
import semver from 'semver';
|
|
5
5
|
import prettier, { format } from 'prettier';
|
|
6
6
|
import * as z from 'zod/v4';
|
|
7
|
-
import fs
|
|
7
|
+
import fs, { existsSync, readFileSync } from 'fs';
|
|
8
8
|
import path from 'path';
|
|
9
9
|
import fs$1 from 'fs/promises';
|
|
10
|
-
import fs from 'fs-extra';
|
|
11
10
|
import chalk from 'chalk';
|
|
12
11
|
import { intro, log, outro, confirm, isCancel, cancel, spinner, text, select, multiselect } from '@clack/prompts';
|
|
13
|
-
import { exec } from 'child_process';
|
|
12
|
+
import { exec, execSync } from 'child_process';
|
|
14
13
|
import { logger, BetterAuthError, createTelemetry, getTelemetryAuthConfig, capitalizeFirstLetter } from 'better-auth';
|
|
15
14
|
import Crypto from 'crypto';
|
|
16
15
|
import yoctoSpinner from 'yocto-spinner';
|
|
@@ -20,15 +19,15 @@ import { loadConfig } from 'c12';
|
|
|
20
19
|
import babelPresetTypeScript from '@babel/preset-typescript';
|
|
21
20
|
import babelPresetReact from '@babel/preset-react';
|
|
22
21
|
import { produceSchema } from '@mrleebo/prisma-ast';
|
|
22
|
+
import { createAuthClient } from 'better-auth/client';
|
|
23
|
+
import { deviceAuthorizationClient } from 'better-auth/client/plugins';
|
|
24
|
+
import open from 'open';
|
|
25
|
+
import os from 'os';
|
|
23
26
|
import 'dotenv/config';
|
|
24
27
|
|
|
25
28
|
function getPackageInfo(cwd) {
|
|
26
29
|
const packageJsonPath = cwd ? path.join(cwd, "package.json") : path.join("package.json");
|
|
27
|
-
|
|
28
|
-
return fs.readJSONSync(packageJsonPath);
|
|
29
|
-
} catch (error) {
|
|
30
|
-
throw error;
|
|
31
|
-
}
|
|
30
|
+
return JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
|
|
32
31
|
}
|
|
33
32
|
|
|
34
33
|
function installDependencies({
|
|
@@ -177,22 +176,15 @@ async function generateAuthConfig({
|
|
|
177
176
|
insert_content: `${opts.pluginFunctionName}(${opts.pluginContents}),`
|
|
178
177
|
});
|
|
179
178
|
} else {
|
|
180
|
-
|
|
181
|
-
const
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
if (char === ",") {
|
|
185
|
-
has_found_comma = true;
|
|
186
|
-
}
|
|
187
|
-
if (char === ")") {
|
|
188
|
-
break;
|
|
189
|
-
}
|
|
190
|
-
}
|
|
179
|
+
const pluginArrayContent = opts.config.slice(start_of_plugins.index, end_of_plugins.index).trim();
|
|
180
|
+
const isPluginArrayEmpty = pluginArrayContent === "";
|
|
181
|
+
const isPluginArrayEndsWithComma = pluginArrayContent.endsWith(",");
|
|
182
|
+
const needsComma = !isPluginArrayEmpty && !isPluginArrayEndsWithComma;
|
|
191
183
|
new_content = insertContent({
|
|
192
184
|
line: end_of_plugins.line,
|
|
193
185
|
character: end_of_plugins.character,
|
|
194
186
|
content: opts.config,
|
|
195
|
-
insert_content: `${
|
|
187
|
+
insert_content: `${needsComma ? "," : ""}${opts.pluginFunctionName}(${opts.pluginContents})`
|
|
196
188
|
});
|
|
197
189
|
}
|
|
198
190
|
try {
|
|
@@ -203,7 +195,7 @@ async function generateAuthConfig({
|
|
|
203
195
|
`Failed to generate new auth config during plugin addition phase.`
|
|
204
196
|
);
|
|
205
197
|
}
|
|
206
|
-
return { code:
|
|
198
|
+
return { code: new_content, dependencies: [], envs: [] };
|
|
207
199
|
},
|
|
208
200
|
add_import: async (opts) => {
|
|
209
201
|
let importString = "";
|
|
@@ -1070,7 +1062,7 @@ async function initAction(opts) {
|
|
|
1070
1062
|
process.exit(0);
|
|
1071
1063
|
}
|
|
1072
1064
|
if (packageManagerPreference === void 0) {
|
|
1073
|
-
packageManagerPreference = await getPackageManager();
|
|
1065
|
+
packageManagerPreference = await getPackageManager$1();
|
|
1074
1066
|
}
|
|
1075
1067
|
if (shouldInstallBetterAuthDep) {
|
|
1076
1068
|
s2.start(
|
|
@@ -1110,7 +1102,7 @@ async function initAction(opts) {
|
|
|
1110
1102
|
}
|
|
1111
1103
|
if (shouldInstallBetterAuthDep) {
|
|
1112
1104
|
if (packageManagerPreference === void 0) {
|
|
1113
|
-
packageManagerPreference = await getPackageManager();
|
|
1105
|
+
packageManagerPreference = await getPackageManager$1();
|
|
1114
1106
|
}
|
|
1115
1107
|
const s2 = spinner({ indicator: "dots" });
|
|
1116
1108
|
s2.start(
|
|
@@ -1273,7 +1265,7 @@ async function initAction(opts) {
|
|
|
1273
1265
|
const { dependencies, envs, generatedCode } = await generateAuthConfig({
|
|
1274
1266
|
current_user_config,
|
|
1275
1267
|
format: format$1,
|
|
1276
|
-
//@ts-
|
|
1268
|
+
//@ts-expect-error
|
|
1277
1269
|
s,
|
|
1278
1270
|
plugins: add_plugins,
|
|
1279
1271
|
database
|
|
@@ -1338,7 +1330,7 @@ async function initAction(opts) {
|
|
|
1338
1330
|
if (shouldInstallDeps) {
|
|
1339
1331
|
const s2 = spinner({ indicator: "dots" });
|
|
1340
1332
|
if (packageManagerPreference === void 0) {
|
|
1341
|
-
packageManagerPreference = await getPackageManager();
|
|
1333
|
+
packageManagerPreference = await getPackageManager$1();
|
|
1342
1334
|
}
|
|
1343
1335
|
s2.start(
|
|
1344
1336
|
`Installing dependencies using ${chalk.bold(
|
|
@@ -1587,7 +1579,7 @@ async function getLatestNpmVersion(packageName) {
|
|
|
1587
1579
|
throw error?.message;
|
|
1588
1580
|
}
|
|
1589
1581
|
}
|
|
1590
|
-
async function getPackageManager() {
|
|
1582
|
+
async function getPackageManager$1() {
|
|
1591
1583
|
const { hasBun, hasPnpm } = await checkPackageManagers();
|
|
1592
1584
|
if (!hasBun && !hasPnpm) return "npm";
|
|
1593
1585
|
const packageManagerOptions = [];
|
|
@@ -1690,9 +1682,9 @@ function getSvelteKitPathAliases(cwd) {
|
|
|
1690
1682
|
const svelteConfigPath = path.join(cwd, "svelte.config.js");
|
|
1691
1683
|
const svelteConfigTsPath = path.join(cwd, "svelte.config.ts");
|
|
1692
1684
|
let isSvelteKitProject = false;
|
|
1693
|
-
if (fs
|
|
1685
|
+
if (fs.existsSync(packageJsonPath)) {
|
|
1694
1686
|
try {
|
|
1695
|
-
const packageJson = JSON.parse(fs
|
|
1687
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
|
|
1696
1688
|
const deps = {
|
|
1697
1689
|
...packageJson.dependencies,
|
|
1698
1690
|
...packageJson.devDependencies
|
|
@@ -1702,19 +1694,19 @@ function getSvelteKitPathAliases(cwd) {
|
|
|
1702
1694
|
}
|
|
1703
1695
|
}
|
|
1704
1696
|
if (!isSvelteKitProject) {
|
|
1705
|
-
isSvelteKitProject = fs
|
|
1697
|
+
isSvelteKitProject = fs.existsSync(svelteConfigPath) || fs.existsSync(svelteConfigTsPath);
|
|
1706
1698
|
}
|
|
1707
1699
|
if (!isSvelteKitProject) {
|
|
1708
1700
|
return aliases;
|
|
1709
1701
|
}
|
|
1710
1702
|
const libPaths = [path.join(cwd, "src", "lib"), path.join(cwd, "lib")];
|
|
1711
1703
|
for (const libPath of libPaths) {
|
|
1712
|
-
if (fs
|
|
1704
|
+
if (fs.existsSync(libPath)) {
|
|
1713
1705
|
aliases["$lib"] = libPath;
|
|
1714
1706
|
const commonSubPaths = ["server", "utils", "components", "stores"];
|
|
1715
1707
|
for (const subPath of commonSubPaths) {
|
|
1716
1708
|
const subDir = path.join(libPath, subPath);
|
|
1717
|
-
if (fs
|
|
1709
|
+
if (fs.existsSync(subDir)) {
|
|
1718
1710
|
aliases[`$lib/${subPath}`] = subDir;
|
|
1719
1711
|
}
|
|
1720
1712
|
}
|
|
@@ -1733,9 +1725,9 @@ function getSvelteConfigAliases(cwd) {
|
|
|
1733
1725
|
path.join(cwd, "svelte.config.ts")
|
|
1734
1726
|
];
|
|
1735
1727
|
for (const configPath of configPaths) {
|
|
1736
|
-
if (fs
|
|
1728
|
+
if (fs.existsSync(configPath)) {
|
|
1737
1729
|
try {
|
|
1738
|
-
const content = fs
|
|
1730
|
+
const content = fs.readFileSync(configPath, "utf-8");
|
|
1739
1731
|
const aliasMatch = content.match(/alias\s*:\s*\{([^}]+)\}/);
|
|
1740
1732
|
if (aliasMatch && aliasMatch[1]) {
|
|
1741
1733
|
const aliasContent = aliasMatch[1];
|
|
@@ -1871,9 +1863,9 @@ function resolveReferencePath(configDir, refPath) {
|
|
|
1871
1863
|
if (refPath.endsWith(".json")) {
|
|
1872
1864
|
return resolvedPath;
|
|
1873
1865
|
}
|
|
1874
|
-
if (fs
|
|
1866
|
+
if (fs.existsSync(resolvedPath)) {
|
|
1875
1867
|
try {
|
|
1876
|
-
const stats = fs
|
|
1868
|
+
const stats = fs.statSync(resolvedPath);
|
|
1877
1869
|
if (stats.isFile()) {
|
|
1878
1870
|
return resolvedPath;
|
|
1879
1871
|
}
|
|
@@ -1887,7 +1879,7 @@ function getPathAliasesRecursive(tsconfigPath, visited = /* @__PURE__ */ new Set
|
|
|
1887
1879
|
return {};
|
|
1888
1880
|
}
|
|
1889
1881
|
visited.add(tsconfigPath);
|
|
1890
|
-
if (!fs
|
|
1882
|
+
if (!fs.existsSync(tsconfigPath)) {
|
|
1891
1883
|
logger.warn(`Referenced tsconfig not found: ${tsconfigPath}`);
|
|
1892
1884
|
return {};
|
|
1893
1885
|
}
|
|
@@ -1924,7 +1916,7 @@ function getPathAliasesRecursive(tsconfigPath, visited = /* @__PURE__ */ new Set
|
|
|
1924
1916
|
}
|
|
1925
1917
|
function getPathAliases(cwd) {
|
|
1926
1918
|
const tsConfigPath = path.join(cwd, "tsconfig.json");
|
|
1927
|
-
if (!fs
|
|
1919
|
+
if (!fs.existsSync(tsConfigPath)) {
|
|
1928
1920
|
return null;
|
|
1929
1921
|
}
|
|
1930
1922
|
try {
|
|
@@ -1957,6 +1949,9 @@ const jitiOptions = (cwd) => {
|
|
|
1957
1949
|
alias
|
|
1958
1950
|
};
|
|
1959
1951
|
};
|
|
1952
|
+
const isDefaultExport = (object) => {
|
|
1953
|
+
return typeof object === "object" && object !== null && !Array.isArray(object) && Object.keys(object).length > 0 && "options" in object;
|
|
1954
|
+
};
|
|
1960
1955
|
async function getConfig({
|
|
1961
1956
|
cwd,
|
|
1962
1957
|
configPath,
|
|
@@ -1972,7 +1967,7 @@ async function getConfig({
|
|
|
1972
1967
|
dotenv: true,
|
|
1973
1968
|
jitiOptions: jitiOptions(cwd)
|
|
1974
1969
|
});
|
|
1975
|
-
if (!
|
|
1970
|
+
if (!("auth" in config) && !isDefaultExport(config)) {
|
|
1976
1971
|
if (shouldThrowOnError) {
|
|
1977
1972
|
throw new Error(
|
|
1978
1973
|
`Couldn't read your auth config in ${resolvedPath}. Make sure to default export your auth instance or to export as a variable named auth.`
|
|
@@ -1983,7 +1978,7 @@ async function getConfig({
|
|
|
1983
1978
|
);
|
|
1984
1979
|
process.exit(1);
|
|
1985
1980
|
}
|
|
1986
|
-
configFile = config.auth?.options
|
|
1981
|
+
configFile = "auth" in config ? config.auth?.options : config.options;
|
|
1987
1982
|
}
|
|
1988
1983
|
if (!configFile) {
|
|
1989
1984
|
for (const possiblePath of possiblePaths) {
|
|
@@ -2326,7 +2321,7 @@ const generateDrizzleSchema = async ({
|
|
|
2326
2321
|
${Object.keys(fields).map((field) => {
|
|
2327
2322
|
const attr = fields[field];
|
|
2328
2323
|
let type = getType(field, attr);
|
|
2329
|
-
if (attr.defaultValue) {
|
|
2324
|
+
if (attr.defaultValue !== null && typeof attr.defaultValue !== "undefined") {
|
|
2330
2325
|
if (typeof attr.defaultValue === "function") {
|
|
2331
2326
|
type += `.$defaultFn(${attr.defaultValue})`;
|
|
2332
2327
|
} else if (typeof attr.defaultValue === "string") {
|
|
@@ -2335,8 +2330,13 @@ const generateDrizzleSchema = async ({
|
|
|
2335
2330
|
type += `.default(${attr.defaultValue})`;
|
|
2336
2331
|
}
|
|
2337
2332
|
}
|
|
2333
|
+
if (attr.onUpdate && attr.type === "date") {
|
|
2334
|
+
if (typeof attr.onUpdate === "function") {
|
|
2335
|
+
type += `.$onUpdate(${attr.onUpdate})`;
|
|
2336
|
+
}
|
|
2337
|
+
}
|
|
2338
2338
|
return `${field}: ${type}${attr.required ? ".notNull()" : ""}${attr.unique ? ".unique()" : ""}${attr.references ? `.references(()=> ${getModelName(
|
|
2339
|
-
attr.references.model,
|
|
2339
|
+
tables[attr.references.model]?.modelName || attr.references.model,
|
|
2340
2340
|
adapter.options
|
|
2341
2341
|
)}.${attr.references.field}, { onDelete: '${attr.references.onDelete || "cascade"}' })` : ""}`;
|
|
2342
2342
|
}).join(",\n ")}
|
|
@@ -2789,6 +2789,630 @@ const generate = new Command("generate").option(
|
|
|
2789
2789
|
"the path to the configuration file. defaults to the first configuration file found."
|
|
2790
2790
|
).option("--output <output>", "the file to output to the generated schema").option("-y, --yes", "automatically answer yes to all prompts", false).option("--y", "(deprecated) same as --yes", false).action(generateAction);
|
|
2791
2791
|
|
|
2792
|
+
const DEMO_URL = "https://demo.better-auth.com";
|
|
2793
|
+
const CLIENT_ID = "better-auth-cli";
|
|
2794
|
+
const CONFIG_DIR = path.join(os.homedir(), ".better-auth");
|
|
2795
|
+
const TOKEN_FILE = path.join(CONFIG_DIR, "token.json");
|
|
2796
|
+
async function loginAction(opts) {
|
|
2797
|
+
const options = z.object({
|
|
2798
|
+
serverUrl: z.string().optional(),
|
|
2799
|
+
clientId: z.string().optional()
|
|
2800
|
+
}).parse(opts);
|
|
2801
|
+
const serverUrl = options.serverUrl || DEMO_URL;
|
|
2802
|
+
const clientId = options.clientId || CLIENT_ID;
|
|
2803
|
+
intro(chalk.bold("\u{1F510} Better Auth CLI Login (Demo)"));
|
|
2804
|
+
console.log(
|
|
2805
|
+
chalk.yellow(
|
|
2806
|
+
"\u26A0\uFE0F This is a demo feature for testing device authorization flow."
|
|
2807
|
+
)
|
|
2808
|
+
);
|
|
2809
|
+
console.log(
|
|
2810
|
+
chalk.gray(
|
|
2811
|
+
" It connects to the Better Auth demo server for testing purposes.\n"
|
|
2812
|
+
)
|
|
2813
|
+
);
|
|
2814
|
+
const existingToken = await getStoredToken();
|
|
2815
|
+
if (existingToken) {
|
|
2816
|
+
const shouldReauth = await confirm({
|
|
2817
|
+
message: "You're already logged in. Do you want to log in again?",
|
|
2818
|
+
initialValue: false
|
|
2819
|
+
});
|
|
2820
|
+
if (isCancel(shouldReauth) || !shouldReauth) {
|
|
2821
|
+
cancel("Login cancelled");
|
|
2822
|
+
process.exit(0);
|
|
2823
|
+
}
|
|
2824
|
+
}
|
|
2825
|
+
const authClient = createAuthClient({
|
|
2826
|
+
baseURL: serverUrl,
|
|
2827
|
+
plugins: [deviceAuthorizationClient()]
|
|
2828
|
+
});
|
|
2829
|
+
const spinner = yoctoSpinner({ text: "Requesting device authorization..." });
|
|
2830
|
+
spinner.start();
|
|
2831
|
+
try {
|
|
2832
|
+
const { data, error } = await authClient.device.code({
|
|
2833
|
+
client_id: clientId,
|
|
2834
|
+
scope: "openid profile email"
|
|
2835
|
+
});
|
|
2836
|
+
spinner.stop();
|
|
2837
|
+
if (error || !data) {
|
|
2838
|
+
logger.error(
|
|
2839
|
+
`Failed to request device authorization: ${error?.error_description || "Unknown error"}`
|
|
2840
|
+
);
|
|
2841
|
+
process.exit(1);
|
|
2842
|
+
}
|
|
2843
|
+
const {
|
|
2844
|
+
device_code,
|
|
2845
|
+
user_code,
|
|
2846
|
+
verification_uri,
|
|
2847
|
+
verification_uri_complete,
|
|
2848
|
+
interval = 5,
|
|
2849
|
+
expires_in
|
|
2850
|
+
} = data;
|
|
2851
|
+
console.log("");
|
|
2852
|
+
console.log(chalk.cyan("\u{1F4F1} Device Authorization Required"));
|
|
2853
|
+
console.log("");
|
|
2854
|
+
console.log(`Please visit: ${chalk.underline.blue(verification_uri)}`);
|
|
2855
|
+
console.log(`Enter code: ${chalk.bold.green(user_code)}`);
|
|
2856
|
+
console.log("");
|
|
2857
|
+
const shouldOpen = await confirm({
|
|
2858
|
+
message: "Open browser automatically?",
|
|
2859
|
+
initialValue: true
|
|
2860
|
+
});
|
|
2861
|
+
if (!isCancel(shouldOpen) && shouldOpen) {
|
|
2862
|
+
const urlToOpen = verification_uri_complete || verification_uri;
|
|
2863
|
+
await open(urlToOpen);
|
|
2864
|
+
}
|
|
2865
|
+
console.log(
|
|
2866
|
+
chalk.gray(
|
|
2867
|
+
`Waiting for authorization (expires in ${Math.floor(expires_in / 60)} minutes)...`
|
|
2868
|
+
)
|
|
2869
|
+
);
|
|
2870
|
+
const token = await pollForToken(
|
|
2871
|
+
authClient,
|
|
2872
|
+
device_code,
|
|
2873
|
+
clientId,
|
|
2874
|
+
interval
|
|
2875
|
+
);
|
|
2876
|
+
if (token) {
|
|
2877
|
+
await storeToken(token);
|
|
2878
|
+
const { data: session } = await authClient.getSession({
|
|
2879
|
+
fetchOptions: {
|
|
2880
|
+
headers: {
|
|
2881
|
+
Authorization: `Bearer ${token.access_token}`
|
|
2882
|
+
}
|
|
2883
|
+
}
|
|
2884
|
+
});
|
|
2885
|
+
outro(
|
|
2886
|
+
chalk.green(
|
|
2887
|
+
`\u2705 Demo login successful! Logged in as ${session?.user?.name || session?.user?.email || "User"}`
|
|
2888
|
+
)
|
|
2889
|
+
);
|
|
2890
|
+
console.log(
|
|
2891
|
+
chalk.gray(
|
|
2892
|
+
"\n\u{1F4DD} Note: This was a demo authentication for testing purposes."
|
|
2893
|
+
)
|
|
2894
|
+
);
|
|
2895
|
+
console.log(
|
|
2896
|
+
chalk.blue(
|
|
2897
|
+
"\nFor more information, visit: https://better-auth.com/docs/plugins/device-authorization"
|
|
2898
|
+
)
|
|
2899
|
+
);
|
|
2900
|
+
}
|
|
2901
|
+
} catch (err) {
|
|
2902
|
+
spinner.stop();
|
|
2903
|
+
logger.error(
|
|
2904
|
+
`Login failed: ${err instanceof Error ? err.message : "Unknown error"}`
|
|
2905
|
+
);
|
|
2906
|
+
process.exit(1);
|
|
2907
|
+
}
|
|
2908
|
+
}
|
|
2909
|
+
async function pollForToken(authClient, deviceCode, clientId, initialInterval) {
|
|
2910
|
+
let pollingInterval = initialInterval;
|
|
2911
|
+
const spinner = yoctoSpinner({ text: "", color: "cyan" });
|
|
2912
|
+
let dots = 0;
|
|
2913
|
+
return new Promise((resolve, reject) => {
|
|
2914
|
+
const poll = async () => {
|
|
2915
|
+
dots = (dots + 1) % 4;
|
|
2916
|
+
spinner.text = chalk.gray(
|
|
2917
|
+
`Polling for authorization${".".repeat(dots)}${" ".repeat(3 - dots)}`
|
|
2918
|
+
);
|
|
2919
|
+
if (!spinner.isSpinning) spinner.start();
|
|
2920
|
+
try {
|
|
2921
|
+
const { data, error } = await authClient.device.token({
|
|
2922
|
+
grant_type: "urn:ietf:params:oauth:grant-type:device_code",
|
|
2923
|
+
device_code: deviceCode,
|
|
2924
|
+
client_id: clientId,
|
|
2925
|
+
fetchOptions: {
|
|
2926
|
+
headers: {
|
|
2927
|
+
"user-agent": `Better Auth CLI`
|
|
2928
|
+
}
|
|
2929
|
+
}
|
|
2930
|
+
});
|
|
2931
|
+
if (data?.access_token) {
|
|
2932
|
+
spinner.stop();
|
|
2933
|
+
resolve(data);
|
|
2934
|
+
return;
|
|
2935
|
+
} else if (error) {
|
|
2936
|
+
switch (error.error) {
|
|
2937
|
+
case "authorization_pending":
|
|
2938
|
+
break;
|
|
2939
|
+
case "slow_down":
|
|
2940
|
+
pollingInterval += 5;
|
|
2941
|
+
spinner.text = chalk.yellow(
|
|
2942
|
+
`Slowing down polling to ${pollingInterval}s`
|
|
2943
|
+
);
|
|
2944
|
+
break;
|
|
2945
|
+
case "access_denied":
|
|
2946
|
+
spinner.stop();
|
|
2947
|
+
logger.error("Access was denied by the user");
|
|
2948
|
+
process.exit(1);
|
|
2949
|
+
break;
|
|
2950
|
+
case "expired_token":
|
|
2951
|
+
spinner.stop();
|
|
2952
|
+
logger.error("The device code has expired. Please try again.");
|
|
2953
|
+
process.exit(1);
|
|
2954
|
+
break;
|
|
2955
|
+
default:
|
|
2956
|
+
spinner.stop();
|
|
2957
|
+
logger.error(`Error: ${error.error_description}`);
|
|
2958
|
+
process.exit(1);
|
|
2959
|
+
}
|
|
2960
|
+
}
|
|
2961
|
+
} catch (err) {
|
|
2962
|
+
spinner.stop();
|
|
2963
|
+
logger.error(
|
|
2964
|
+
`Network error: ${err instanceof Error ? err.message : "Unknown error"}`
|
|
2965
|
+
);
|
|
2966
|
+
process.exit(1);
|
|
2967
|
+
}
|
|
2968
|
+
setTimeout(poll, pollingInterval * 1e3);
|
|
2969
|
+
};
|
|
2970
|
+
setTimeout(poll, pollingInterval * 1e3);
|
|
2971
|
+
});
|
|
2972
|
+
}
|
|
2973
|
+
async function storeToken(token) {
|
|
2974
|
+
try {
|
|
2975
|
+
await fs$1.mkdir(CONFIG_DIR, { recursive: true });
|
|
2976
|
+
const tokenData = {
|
|
2977
|
+
access_token: token.access_token,
|
|
2978
|
+
token_type: token.token_type || "Bearer",
|
|
2979
|
+
scope: token.scope,
|
|
2980
|
+
created_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
2981
|
+
};
|
|
2982
|
+
await fs$1.writeFile(TOKEN_FILE, JSON.stringify(tokenData, null, 2), "utf-8");
|
|
2983
|
+
} catch (error) {
|
|
2984
|
+
logger.warn("Failed to store authentication token locally");
|
|
2985
|
+
}
|
|
2986
|
+
}
|
|
2987
|
+
async function getStoredToken() {
|
|
2988
|
+
try {
|
|
2989
|
+
const data = await fs$1.readFile(TOKEN_FILE, "utf-8");
|
|
2990
|
+
return JSON.parse(data);
|
|
2991
|
+
} catch {
|
|
2992
|
+
return null;
|
|
2993
|
+
}
|
|
2994
|
+
}
|
|
2995
|
+
const login = new Command("login").description(
|
|
2996
|
+
"Demo: Test device authorization flow with Better Auth demo server"
|
|
2997
|
+
).option("--server-url <url>", "The Better Auth server URL", DEMO_URL).option("--client-id <id>", "The OAuth client ID", CLIENT_ID).action(loginAction);
|
|
2998
|
+
|
|
2999
|
+
function getSystemInfo() {
|
|
3000
|
+
const platform = os.platform();
|
|
3001
|
+
const arch = os.arch();
|
|
3002
|
+
const version = os.version();
|
|
3003
|
+
const release = os.release();
|
|
3004
|
+
const cpus = os.cpus();
|
|
3005
|
+
const memory = os.totalmem();
|
|
3006
|
+
const freeMemory = os.freemem();
|
|
3007
|
+
return {
|
|
3008
|
+
platform,
|
|
3009
|
+
arch,
|
|
3010
|
+
version,
|
|
3011
|
+
release,
|
|
3012
|
+
cpuCount: cpus.length,
|
|
3013
|
+
cpuModel: cpus[0]?.model || "Unknown",
|
|
3014
|
+
totalMemory: `${(memory / 1024 / 1024 / 1024).toFixed(2)} GB`,
|
|
3015
|
+
freeMemory: `${(freeMemory / 1024 / 1024 / 1024).toFixed(2)} GB`
|
|
3016
|
+
};
|
|
3017
|
+
}
|
|
3018
|
+
function getNodeInfo() {
|
|
3019
|
+
return {
|
|
3020
|
+
version: process.version,
|
|
3021
|
+
env: process.env.NODE_ENV || "development"
|
|
3022
|
+
};
|
|
3023
|
+
}
|
|
3024
|
+
function getPackageManager() {
|
|
3025
|
+
const userAgent = process.env.npm_config_user_agent || "";
|
|
3026
|
+
if (userAgent.includes("yarn")) {
|
|
3027
|
+
return { name: "yarn", version: getVersion("yarn") };
|
|
3028
|
+
}
|
|
3029
|
+
if (userAgent.includes("pnpm")) {
|
|
3030
|
+
return { name: "pnpm", version: getVersion("pnpm") };
|
|
3031
|
+
}
|
|
3032
|
+
if (userAgent.includes("bun")) {
|
|
3033
|
+
return { name: "bun", version: getVersion("bun") };
|
|
3034
|
+
}
|
|
3035
|
+
return { name: "npm", version: getVersion("npm") };
|
|
3036
|
+
}
|
|
3037
|
+
function getVersion(command) {
|
|
3038
|
+
try {
|
|
3039
|
+
const output = execSync(`${command} --version`, { encoding: "utf8" });
|
|
3040
|
+
return output.trim();
|
|
3041
|
+
} catch {
|
|
3042
|
+
return "Not installed";
|
|
3043
|
+
}
|
|
3044
|
+
}
|
|
3045
|
+
function getFrameworkInfo(projectRoot) {
|
|
3046
|
+
const packageJsonPath = path.join(projectRoot, "package.json");
|
|
3047
|
+
if (!existsSync(packageJsonPath)) {
|
|
3048
|
+
return null;
|
|
3049
|
+
}
|
|
3050
|
+
try {
|
|
3051
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf8"));
|
|
3052
|
+
const deps = {
|
|
3053
|
+
...packageJson.dependencies,
|
|
3054
|
+
...packageJson.devDependencies
|
|
3055
|
+
};
|
|
3056
|
+
const frameworks = {
|
|
3057
|
+
next: deps["next"],
|
|
3058
|
+
react: deps["react"],
|
|
3059
|
+
vue: deps["vue"],
|
|
3060
|
+
nuxt: deps["nuxt"],
|
|
3061
|
+
svelte: deps["svelte"],
|
|
3062
|
+
"@sveltejs/kit": deps["@sveltejs/kit"],
|
|
3063
|
+
express: deps["express"],
|
|
3064
|
+
fastify: deps["fastify"],
|
|
3065
|
+
hono: deps["hono"],
|
|
3066
|
+
remix: deps["@remix-run/react"],
|
|
3067
|
+
astro: deps["astro"],
|
|
3068
|
+
solid: deps["solid-js"],
|
|
3069
|
+
qwik: deps["@builder.io/qwik"]
|
|
3070
|
+
};
|
|
3071
|
+
const installedFrameworks = Object.entries(frameworks).filter(([_, version]) => version).map(([name, version]) => ({ name, version }));
|
|
3072
|
+
return installedFrameworks.length > 0 ? installedFrameworks : null;
|
|
3073
|
+
} catch {
|
|
3074
|
+
return null;
|
|
3075
|
+
}
|
|
3076
|
+
}
|
|
3077
|
+
function getDatabaseInfo(projectRoot) {
|
|
3078
|
+
const packageJsonPath = path.join(projectRoot, "package.json");
|
|
3079
|
+
if (!existsSync(packageJsonPath)) {
|
|
3080
|
+
return null;
|
|
3081
|
+
}
|
|
3082
|
+
try {
|
|
3083
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf8"));
|
|
3084
|
+
const deps = {
|
|
3085
|
+
...packageJson.dependencies,
|
|
3086
|
+
...packageJson.devDependencies
|
|
3087
|
+
};
|
|
3088
|
+
const databases = {
|
|
3089
|
+
"better-sqlite3": deps["better-sqlite3"],
|
|
3090
|
+
"@libsql/client": deps["@libsql/client"],
|
|
3091
|
+
"@libsql/kysely-libsql": deps["@libsql/kysely-libsql"],
|
|
3092
|
+
mysql2: deps["mysql2"],
|
|
3093
|
+
pg: deps["pg"],
|
|
3094
|
+
postgres: deps["postgres"],
|
|
3095
|
+
"@prisma/client": deps["@prisma/client"],
|
|
3096
|
+
drizzle: deps["drizzle-orm"],
|
|
3097
|
+
kysely: deps["kysely"],
|
|
3098
|
+
mongodb: deps["mongodb"],
|
|
3099
|
+
"@neondatabase/serverless": deps["@neondatabase/serverless"],
|
|
3100
|
+
"@vercel/postgres": deps["@vercel/postgres"],
|
|
3101
|
+
"@planetscale/database": deps["@planetscale/database"]
|
|
3102
|
+
};
|
|
3103
|
+
const installedDatabases = Object.entries(databases).filter(([_, version]) => version).map(([name, version]) => ({ name, version }));
|
|
3104
|
+
return installedDatabases.length > 0 ? installedDatabases : null;
|
|
3105
|
+
} catch {
|
|
3106
|
+
return null;
|
|
3107
|
+
}
|
|
3108
|
+
}
|
|
3109
|
+
function sanitizeBetterAuthConfig(config) {
|
|
3110
|
+
if (!config) return null;
|
|
3111
|
+
const sanitized = JSON.parse(JSON.stringify(config));
|
|
3112
|
+
const sensitiveKeys = [
|
|
3113
|
+
"secret",
|
|
3114
|
+
"clientSecret",
|
|
3115
|
+
"clientId",
|
|
3116
|
+
"authToken",
|
|
3117
|
+
"apiKey",
|
|
3118
|
+
"apiSecret",
|
|
3119
|
+
"privateKey",
|
|
3120
|
+
"publicKey",
|
|
3121
|
+
"password",
|
|
3122
|
+
"token",
|
|
3123
|
+
"webhook",
|
|
3124
|
+
"connectionString",
|
|
3125
|
+
"databaseUrl",
|
|
3126
|
+
"databaseURL",
|
|
3127
|
+
"TURSO_AUTH_TOKEN",
|
|
3128
|
+
"TURSO_DATABASE_URL",
|
|
3129
|
+
"MYSQL_DATABASE_URL",
|
|
3130
|
+
"DATABASE_URL",
|
|
3131
|
+
"POSTGRES_URL",
|
|
3132
|
+
"MONGODB_URI",
|
|
3133
|
+
"stripeKey",
|
|
3134
|
+
"stripeWebhookSecret"
|
|
3135
|
+
];
|
|
3136
|
+
const allowedKeys = [
|
|
3137
|
+
"baseURL",
|
|
3138
|
+
"callbackURL",
|
|
3139
|
+
"redirectURL",
|
|
3140
|
+
"trustedOrigins",
|
|
3141
|
+
"appName"
|
|
3142
|
+
];
|
|
3143
|
+
function redactSensitive(obj, parentKey) {
|
|
3144
|
+
if (typeof obj !== "object" || obj === null) {
|
|
3145
|
+
if (parentKey && typeof obj === "string" && obj.length > 0) {
|
|
3146
|
+
if (allowedKeys.some(
|
|
3147
|
+
(allowed) => parentKey.toLowerCase() === allowed.toLowerCase()
|
|
3148
|
+
)) {
|
|
3149
|
+
return obj;
|
|
3150
|
+
}
|
|
3151
|
+
const lowerKey = parentKey.toLowerCase();
|
|
3152
|
+
if (sensitiveKeys.some((key) => {
|
|
3153
|
+
const lowerSensitiveKey = key.toLowerCase();
|
|
3154
|
+
return lowerKey === lowerSensitiveKey || lowerKey.endsWith(lowerSensitiveKey);
|
|
3155
|
+
})) {
|
|
3156
|
+
return "[REDACTED]";
|
|
3157
|
+
}
|
|
3158
|
+
}
|
|
3159
|
+
return obj;
|
|
3160
|
+
}
|
|
3161
|
+
if (Array.isArray(obj)) {
|
|
3162
|
+
return obj.map((item) => redactSensitive(item, parentKey));
|
|
3163
|
+
}
|
|
3164
|
+
const result = {};
|
|
3165
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
3166
|
+
if (allowedKeys.some(
|
|
3167
|
+
(allowed) => key.toLowerCase() === allowed.toLowerCase()
|
|
3168
|
+
)) {
|
|
3169
|
+
result[key] = value;
|
|
3170
|
+
continue;
|
|
3171
|
+
}
|
|
3172
|
+
const lowerKey = key.toLowerCase();
|
|
3173
|
+
if (sensitiveKeys.some((sensitiveKey) => {
|
|
3174
|
+
const lowerSensitiveKey = sensitiveKey.toLowerCase();
|
|
3175
|
+
return lowerKey === lowerSensitiveKey || lowerKey.endsWith(lowerSensitiveKey);
|
|
3176
|
+
})) {
|
|
3177
|
+
if (typeof value === "string" && value.length > 0) {
|
|
3178
|
+
result[key] = "[REDACTED]";
|
|
3179
|
+
} else if (typeof value === "object" && value !== null) {
|
|
3180
|
+
result[key] = redactSensitive(value, key);
|
|
3181
|
+
} else {
|
|
3182
|
+
result[key] = value;
|
|
3183
|
+
}
|
|
3184
|
+
} else {
|
|
3185
|
+
result[key] = redactSensitive(value, key);
|
|
3186
|
+
}
|
|
3187
|
+
}
|
|
3188
|
+
return result;
|
|
3189
|
+
}
|
|
3190
|
+
if (sanitized.database) {
|
|
3191
|
+
if (typeof sanitized.database === "string") {
|
|
3192
|
+
sanitized.database = "[REDACTED]";
|
|
3193
|
+
} else if (sanitized.database.url) {
|
|
3194
|
+
sanitized.database.url = "[REDACTED]";
|
|
3195
|
+
}
|
|
3196
|
+
if (sanitized.database.authToken) {
|
|
3197
|
+
sanitized.database.authToken = "[REDACTED]";
|
|
3198
|
+
}
|
|
3199
|
+
}
|
|
3200
|
+
if (sanitized.socialProviders) {
|
|
3201
|
+
for (const provider in sanitized.socialProviders) {
|
|
3202
|
+
if (sanitized.socialProviders[provider]) {
|
|
3203
|
+
sanitized.socialProviders[provider] = redactSensitive(
|
|
3204
|
+
sanitized.socialProviders[provider],
|
|
3205
|
+
provider
|
|
3206
|
+
);
|
|
3207
|
+
}
|
|
3208
|
+
}
|
|
3209
|
+
}
|
|
3210
|
+
if (sanitized.emailAndPassword?.sendResetPassword) {
|
|
3211
|
+
sanitized.emailAndPassword.sendResetPassword = "[Function]";
|
|
3212
|
+
}
|
|
3213
|
+
if (sanitized.emailVerification?.sendVerificationEmail) {
|
|
3214
|
+
sanitized.emailVerification.sendVerificationEmail = "[Function]";
|
|
3215
|
+
}
|
|
3216
|
+
if (sanitized.plugins && Array.isArray(sanitized.plugins)) {
|
|
3217
|
+
sanitized.plugins = sanitized.plugins.map((plugin) => {
|
|
3218
|
+
if (typeof plugin === "function") {
|
|
3219
|
+
return "[Plugin Function]";
|
|
3220
|
+
}
|
|
3221
|
+
if (plugin && typeof plugin === "object") {
|
|
3222
|
+
const pluginName = plugin.id || plugin.name || "unknown";
|
|
3223
|
+
return {
|
|
3224
|
+
name: pluginName,
|
|
3225
|
+
config: redactSensitive(plugin.config || plugin)
|
|
3226
|
+
};
|
|
3227
|
+
}
|
|
3228
|
+
return plugin;
|
|
3229
|
+
});
|
|
3230
|
+
}
|
|
3231
|
+
return redactSensitive(sanitized);
|
|
3232
|
+
}
|
|
3233
|
+
async function getBetterAuthInfo(projectRoot, configPath, suppressLogs = false) {
|
|
3234
|
+
try {
|
|
3235
|
+
const originalLog = console.log;
|
|
3236
|
+
const originalWarn = console.warn;
|
|
3237
|
+
const originalError = console.error;
|
|
3238
|
+
if (suppressLogs) {
|
|
3239
|
+
console.log = () => {
|
|
3240
|
+
};
|
|
3241
|
+
console.warn = () => {
|
|
3242
|
+
};
|
|
3243
|
+
console.error = () => {
|
|
3244
|
+
};
|
|
3245
|
+
}
|
|
3246
|
+
try {
|
|
3247
|
+
const config = await getConfig({
|
|
3248
|
+
cwd: projectRoot,
|
|
3249
|
+
configPath,
|
|
3250
|
+
shouldThrowOnError: false
|
|
3251
|
+
});
|
|
3252
|
+
const packageInfo = await getPackageInfo();
|
|
3253
|
+
return {
|
|
3254
|
+
version: packageInfo.version || "Unknown",
|
|
3255
|
+
config: sanitizeBetterAuthConfig(config)
|
|
3256
|
+
};
|
|
3257
|
+
} finally {
|
|
3258
|
+
if (suppressLogs) {
|
|
3259
|
+
console.log = originalLog;
|
|
3260
|
+
console.warn = originalWarn;
|
|
3261
|
+
console.error = originalError;
|
|
3262
|
+
}
|
|
3263
|
+
}
|
|
3264
|
+
} catch (error) {
|
|
3265
|
+
return {
|
|
3266
|
+
version: "Unknown",
|
|
3267
|
+
config: null,
|
|
3268
|
+
error: error instanceof Error ? error.message : "Failed to load Better Auth config"
|
|
3269
|
+
};
|
|
3270
|
+
}
|
|
3271
|
+
}
|
|
3272
|
+
function formatOutput(data, indent = 0) {
|
|
3273
|
+
const spaces = " ".repeat(indent);
|
|
3274
|
+
if (data === null || data === void 0) {
|
|
3275
|
+
return `${spaces}${chalk.gray("N/A")}`;
|
|
3276
|
+
}
|
|
3277
|
+
if (typeof data === "string" || typeof data === "number" || typeof data === "boolean") {
|
|
3278
|
+
return `${spaces}${data}`;
|
|
3279
|
+
}
|
|
3280
|
+
if (Array.isArray(data)) {
|
|
3281
|
+
if (data.length === 0) {
|
|
3282
|
+
return `${spaces}${chalk.gray("[]")}`;
|
|
3283
|
+
}
|
|
3284
|
+
return data.map((item) => formatOutput(item, indent)).join("\n");
|
|
3285
|
+
}
|
|
3286
|
+
if (typeof data === "object") {
|
|
3287
|
+
const entries = Object.entries(data);
|
|
3288
|
+
if (entries.length === 0) {
|
|
3289
|
+
return `${spaces}${chalk.gray("{}")}`;
|
|
3290
|
+
}
|
|
3291
|
+
return entries.map(([key, value]) => {
|
|
3292
|
+
if (typeof value === "object" && value !== null && !Array.isArray(value)) {
|
|
3293
|
+
return `${spaces}${chalk.cyan(key)}:
|
|
3294
|
+
${formatOutput(value, indent + 2)}`;
|
|
3295
|
+
}
|
|
3296
|
+
return `${spaces}${chalk.cyan(key)}: ${formatOutput(value, 0)}`;
|
|
3297
|
+
}).join("\n");
|
|
3298
|
+
}
|
|
3299
|
+
return `${spaces}${JSON.stringify(data)}`;
|
|
3300
|
+
}
|
|
3301
|
+
const info = new Command("info").description("Display system and Better Auth configuration information").option("--cwd <cwd>", "The working directory", process.cwd()).option("--config <config>", "Path to the Better Auth configuration file").option("-j, --json", "Output as JSON").option("-c, --copy", "Copy output to clipboard (requires pbcopy/xclip)").action(async (options) => {
|
|
3302
|
+
const projectRoot = path.resolve(options.cwd || process.cwd());
|
|
3303
|
+
const systemInfo = getSystemInfo();
|
|
3304
|
+
const nodeInfo = getNodeInfo();
|
|
3305
|
+
const packageManager = getPackageManager();
|
|
3306
|
+
const frameworks = getFrameworkInfo(projectRoot);
|
|
3307
|
+
const databases = getDatabaseInfo(projectRoot);
|
|
3308
|
+
const betterAuthInfo = await getBetterAuthInfo(
|
|
3309
|
+
projectRoot,
|
|
3310
|
+
options.config,
|
|
3311
|
+
options.json
|
|
3312
|
+
);
|
|
3313
|
+
const fullInfo = {
|
|
3314
|
+
system: systemInfo,
|
|
3315
|
+
node: nodeInfo,
|
|
3316
|
+
packageManager,
|
|
3317
|
+
frameworks,
|
|
3318
|
+
databases,
|
|
3319
|
+
betterAuth: betterAuthInfo
|
|
3320
|
+
};
|
|
3321
|
+
if (options.json) {
|
|
3322
|
+
const jsonOutput = JSON.stringify(fullInfo, null, 2);
|
|
3323
|
+
console.log(jsonOutput);
|
|
3324
|
+
if (options.copy) {
|
|
3325
|
+
try {
|
|
3326
|
+
const platform = os.platform();
|
|
3327
|
+
if (platform === "darwin") {
|
|
3328
|
+
execSync("pbcopy", { input: jsonOutput });
|
|
3329
|
+
console.log(chalk.green("\n\u2713 Copied to clipboard"));
|
|
3330
|
+
} else if (platform === "linux") {
|
|
3331
|
+
execSync("xclip -selection clipboard", { input: jsonOutput });
|
|
3332
|
+
console.log(chalk.green("\n\u2713 Copied to clipboard"));
|
|
3333
|
+
} else if (platform === "win32") {
|
|
3334
|
+
execSync("clip", { input: jsonOutput });
|
|
3335
|
+
console.log(chalk.green("\n\u2713 Copied to clipboard"));
|
|
3336
|
+
}
|
|
3337
|
+
} catch {
|
|
3338
|
+
console.log(chalk.yellow("\n\u26A0 Could not copy to clipboard"));
|
|
3339
|
+
}
|
|
3340
|
+
}
|
|
3341
|
+
return;
|
|
3342
|
+
}
|
|
3343
|
+
console.log(chalk.bold("\n\u{1F4CA} Better Auth System Information\n"));
|
|
3344
|
+
console.log(chalk.gray("=".repeat(50)));
|
|
3345
|
+
console.log(chalk.bold.white("\n\u{1F5A5}\uFE0F System Information:"));
|
|
3346
|
+
console.log(formatOutput(systemInfo, 2));
|
|
3347
|
+
console.log(chalk.bold.white("\n\u{1F4E6} Node.js:"));
|
|
3348
|
+
console.log(formatOutput(nodeInfo, 2));
|
|
3349
|
+
console.log(chalk.bold.white("\n\u{1F4E6} Package Manager:"));
|
|
3350
|
+
console.log(formatOutput(packageManager, 2));
|
|
3351
|
+
if (frameworks) {
|
|
3352
|
+
console.log(chalk.bold.white("\n\u{1F680} Frameworks:"));
|
|
3353
|
+
console.log(formatOutput(frameworks, 2));
|
|
3354
|
+
}
|
|
3355
|
+
if (databases) {
|
|
3356
|
+
console.log(chalk.bold.white("\n\u{1F4BE} Database Clients:"));
|
|
3357
|
+
console.log(formatOutput(databases, 2));
|
|
3358
|
+
}
|
|
3359
|
+
console.log(chalk.bold.white("\n\u{1F510} Better Auth:"));
|
|
3360
|
+
if (betterAuthInfo.error) {
|
|
3361
|
+
console.log(` ${chalk.red("Error:")} ${betterAuthInfo.error}`);
|
|
3362
|
+
} else {
|
|
3363
|
+
console.log(` ${chalk.cyan("Version")}: ${betterAuthInfo.version}`);
|
|
3364
|
+
if (betterAuthInfo.config) {
|
|
3365
|
+
console.log(` ${chalk.cyan("Configuration")}:`);
|
|
3366
|
+
console.log(formatOutput(betterAuthInfo.config, 4));
|
|
3367
|
+
}
|
|
3368
|
+
}
|
|
3369
|
+
console.log(chalk.gray("\n" + "=".repeat(50)));
|
|
3370
|
+
console.log(chalk.gray("\n\u{1F4A1} Tip: Use --json flag for JSON output"));
|
|
3371
|
+
console.log(chalk.gray("\u{1F4A1} Use --copy flag to copy output to clipboard"));
|
|
3372
|
+
console.log(
|
|
3373
|
+
chalk.gray("\u{1F4A1} When reporting issues, include this information\n")
|
|
3374
|
+
);
|
|
3375
|
+
if (options.copy) {
|
|
3376
|
+
const textOutput = `
|
|
3377
|
+
Better Auth System Information
|
|
3378
|
+
==============================
|
|
3379
|
+
|
|
3380
|
+
System Information:
|
|
3381
|
+
${JSON.stringify(systemInfo, null, 2)}
|
|
3382
|
+
|
|
3383
|
+
Node.js:
|
|
3384
|
+
${JSON.stringify(nodeInfo, null, 2)}
|
|
3385
|
+
|
|
3386
|
+
Package Manager:
|
|
3387
|
+
${JSON.stringify(packageManager, null, 2)}
|
|
3388
|
+
|
|
3389
|
+
Frameworks:
|
|
3390
|
+
${JSON.stringify(frameworks, null, 2)}
|
|
3391
|
+
|
|
3392
|
+
Database Clients:
|
|
3393
|
+
${JSON.stringify(databases, null, 2)}
|
|
3394
|
+
|
|
3395
|
+
Better Auth:
|
|
3396
|
+
${JSON.stringify(betterAuthInfo, null, 2)}
|
|
3397
|
+
`;
|
|
3398
|
+
try {
|
|
3399
|
+
const platform = os.platform();
|
|
3400
|
+
if (platform === "darwin") {
|
|
3401
|
+
execSync("pbcopy", { input: textOutput });
|
|
3402
|
+
console.log(chalk.green("\u2713 Copied to clipboard"));
|
|
3403
|
+
} else if (platform === "linux") {
|
|
3404
|
+
execSync("xclip -selection clipboard", { input: textOutput });
|
|
3405
|
+
console.log(chalk.green("\u2713 Copied to clipboard"));
|
|
3406
|
+
} else if (platform === "win32") {
|
|
3407
|
+
execSync("clip", { input: textOutput });
|
|
3408
|
+
console.log(chalk.green("\u2713 Copied to clipboard"));
|
|
3409
|
+
}
|
|
3410
|
+
} catch {
|
|
3411
|
+
console.log(chalk.yellow("\u26A0 Could not copy to clipboard"));
|
|
3412
|
+
}
|
|
3413
|
+
}
|
|
3414
|
+
});
|
|
3415
|
+
|
|
2792
3416
|
process.on("SIGINT", () => process.exit(0));
|
|
2793
3417
|
process.on("SIGTERM", () => process.exit(0));
|
|
2794
3418
|
async function main() {
|
|
@@ -2798,7 +3422,7 @@ async function main() {
|
|
|
2798
3422
|
packageInfo = await getPackageInfo();
|
|
2799
3423
|
} catch (error) {
|
|
2800
3424
|
}
|
|
2801
|
-
program.addCommand(init).addCommand(migrate).addCommand(generate).addCommand(generateSecret).version(packageInfo.version || "1.1.2").description("Better Auth CLI").action(() => program.help());
|
|
3425
|
+
program.addCommand(init).addCommand(migrate).addCommand(generate).addCommand(generateSecret).addCommand(info).addCommand(login).version(packageInfo.version || "1.1.2").description("Better Auth CLI").action(() => program.help());
|
|
2802
3426
|
program.parse();
|
|
2803
3427
|
}
|
|
2804
3428
|
main();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@better-auth/cli",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.8-beta.10",
|
|
4
4
|
"description": "The CLI for Better Auth",
|
|
5
5
|
"module": "dist/index.mjs",
|
|
6
6
|
"repository": {
|
|
@@ -26,11 +26,9 @@
|
|
|
26
26
|
"exports": "./dist/index.mjs",
|
|
27
27
|
"bin": "./dist/index.mjs",
|
|
28
28
|
"devDependencies": {
|
|
29
|
-
"
|
|
30
|
-
"@types/fs-extra": "^11.0.4",
|
|
29
|
+
"tsx": "^4.20.4",
|
|
31
30
|
"typescript": "^5.9.2",
|
|
32
|
-
"unbuild": "^3.5.0"
|
|
33
|
-
"zod": "^4.0.0"
|
|
31
|
+
"unbuild": "^3.5.0"
|
|
34
32
|
},
|
|
35
33
|
"dependencies": {
|
|
36
34
|
"@babel/core": "^7.0.0",
|
|
@@ -42,23 +40,21 @@
|
|
|
42
40
|
"@types/better-sqlite3": "^7.6.12",
|
|
43
41
|
"@types/prompts": "^2.4.9",
|
|
44
42
|
"better-sqlite3": "^11.6.0",
|
|
45
|
-
"c12": "^2.0
|
|
43
|
+
"c12": "^3.2.0",
|
|
46
44
|
"chalk": "^5.3.0",
|
|
47
45
|
"commander": "^12.1.0",
|
|
48
46
|
"dotenv": "^16.4.7",
|
|
49
47
|
"drizzle-orm": "^0.33.0",
|
|
50
|
-
"fs-extra": "^11.3.0",
|
|
51
48
|
"get-tsconfig": "^4.8.1",
|
|
49
|
+
"open": "^10.1.0",
|
|
52
50
|
"prettier": "^3.4.2",
|
|
53
51
|
"prisma": "^5.22.0",
|
|
54
52
|
"prompts": "^2.4.2",
|
|
55
53
|
"semver": "^7.7.1",
|
|
56
54
|
"tinyexec": "^0.3.1",
|
|
57
55
|
"yocto-spinner": "^0.1.1",
|
|
58
|
-
"
|
|
59
|
-
|
|
60
|
-
"peerDependencies": {
|
|
61
|
-
"zod": "3.25.0 || ^4.0.0"
|
|
56
|
+
"zod": "^4.0.0",
|
|
57
|
+
"better-auth": "1.3.8-beta.10"
|
|
62
58
|
},
|
|
63
59
|
"files": [
|
|
64
60
|
"dist"
|
|
@@ -67,6 +63,7 @@
|
|
|
67
63
|
"build": "unbuild",
|
|
68
64
|
"stub": "unbuild --stub",
|
|
69
65
|
"start": "node ./dist/index.mjs",
|
|
66
|
+
"dev": "tsx ./src/index.ts",
|
|
70
67
|
"test": "vitest"
|
|
71
68
|
}
|
|
72
69
|
}
|