@linktr.ee/create-link-app 2.2.1 → 2.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 +2 -2
- package/dist/commands/login.js +114 -23
- package/dist/commands/logout.js +32 -11
- package/dist/lib/auth/access-token.js +25 -12
- package/dist/lib/auth/device-auth.js +12 -4
- package/dist/lib/fetch-app-config.js +20 -2
- package/dist/lib/utils/write-line.js +7 -0
- package/oclif.manifest.json +2 -2
- package/package.json +4 -1
package/README.md
CHANGED
|
@@ -173,14 +173,14 @@ DESCRIPTION
|
|
|
173
173
|
|
|
174
174
|
## `create-link-app logout`
|
|
175
175
|
|
|
176
|
-
Logout and clear
|
|
176
|
+
Logout and clear local credentials
|
|
177
177
|
|
|
178
178
|
```
|
|
179
179
|
USAGE
|
|
180
180
|
$ create-link-app logout
|
|
181
181
|
|
|
182
182
|
DESCRIPTION
|
|
183
|
-
Logout and clear
|
|
183
|
+
Logout and clear local credentials
|
|
184
184
|
```
|
|
185
185
|
|
|
186
186
|
## `create-link-app storybook`
|
package/dist/commands/login.js
CHANGED
|
@@ -1,37 +1,128 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
2
25
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
26
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
27
|
};
|
|
5
28
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
+
exports.loginCommand = void 0;
|
|
6
30
|
const core_1 = require("@oclif/core");
|
|
31
|
+
const p = __importStar(require("@clack/prompts"));
|
|
32
|
+
const axios_1 = __importDefault(require("axios"));
|
|
33
|
+
const open_1 = __importDefault(require("open"));
|
|
34
|
+
const picocolors_1 = __importDefault(require("picocolors"));
|
|
7
35
|
const base_1 = __importDefault(require("../base"));
|
|
8
|
-
const
|
|
36
|
+
const fetch_app_config_1 = __importDefault(require("../lib/fetch-app-config"));
|
|
9
37
|
const device_auth_1 = require("../lib/auth/device-auth");
|
|
38
|
+
const access_token_1 = require("../lib/auth/access-token");
|
|
39
|
+
const write_line_1 = require("../lib/utils/write-line");
|
|
40
|
+
function fixVerificationUrl(url, correctProjectId) {
|
|
41
|
+
try {
|
|
42
|
+
const parsed = new URL(url);
|
|
43
|
+
parsed.pathname = parsed.pathname.replace(/^\/login\/[A-Za-z0-9]+/, `/login/${correctProjectId}`);
|
|
44
|
+
return parsed.toString();
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
return url;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
async function getDescopeHostedConfigStatus(projectId) {
|
|
51
|
+
const hostedConfigUrl = `https://api.descope.com/pages/${projectId}/v2-beta/config.json`;
|
|
52
|
+
try {
|
|
53
|
+
const response = await axios_1.default.get(hostedConfigUrl, {
|
|
54
|
+
validateStatus: () => true,
|
|
55
|
+
});
|
|
56
|
+
return response.status;
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
async function loginCommand(options) {
|
|
63
|
+
const env = options.qa ? 'qa' : 'production';
|
|
64
|
+
p.intro(picocolors_1.default.cyan('Login to Linktree'));
|
|
65
|
+
const s = p.spinner();
|
|
66
|
+
s.start('Fetching configuration');
|
|
67
|
+
const config = await (0, fetch_app_config_1.default)(env);
|
|
68
|
+
s.stop('Configuration loaded');
|
|
69
|
+
// Initiate device authorization
|
|
70
|
+
s.start('Starting device authorization');
|
|
71
|
+
const handle = await (0, device_auth_1.initDeviceAuthorization)(config.auth);
|
|
72
|
+
s.stop('Device authorization initiated');
|
|
73
|
+
const { expires_in, user_code, verification_uri, verification_uri_complete } = handle;
|
|
74
|
+
const expiryMinutes = Math.floor(expires_in / 60);
|
|
75
|
+
// The device authorization response may contain a project ID in the URL path that differs
|
|
76
|
+
// from config.auth.project_id (e.g. when overridden via env var or hardcoded fallback).
|
|
77
|
+
// Replace it so the browser opens the correct hosted login flow.
|
|
78
|
+
const fixedUri = config.auth.project_id ? fixVerificationUrl(verification_uri, config.auth.project_id) : verification_uri;
|
|
79
|
+
const fixedUriComplete = config.auth.project_id
|
|
80
|
+
? fixVerificationUrl(verification_uri_complete, config.auth.project_id)
|
|
81
|
+
: verification_uri_complete;
|
|
82
|
+
(0, write_line_1.writeLine)();
|
|
83
|
+
(0, write_line_1.writeLine)(`${picocolors_1.default.green('✓ Authorization code:')} ${picocolors_1.default.bold(user_code)}`);
|
|
84
|
+
(0, write_line_1.writeLine)(picocolors_1.default.dim(` ${fixedUriComplete}`));
|
|
85
|
+
if (fixedUri && fixedUri !== fixedUriComplete) {
|
|
86
|
+
(0, write_line_1.writeLine)(picocolors_1.default.dim(` ${fixedUri}`));
|
|
87
|
+
}
|
|
88
|
+
(0, write_line_1.writeLine)(picocolors_1.default.dim(` Expires in ${expiryMinutes} minutes`));
|
|
89
|
+
(0, write_line_1.writeLine)();
|
|
90
|
+
// Open browser first so the user can start authenticating immediately
|
|
91
|
+
await (0, open_1.default)(fixedUriComplete);
|
|
92
|
+
(0, write_line_1.writeLine)(picocolors_1.default.cyan('🌐 Browser opened for authentication'));
|
|
93
|
+
(0, write_line_1.writeLine)(picocolors_1.default.dim(' If browser did not open, visit the link above'));
|
|
94
|
+
(0, write_line_1.writeLine)();
|
|
95
|
+
// Fire-and-forget diagnostic check — don't block the auth flow
|
|
96
|
+
const projectId = config.auth.project_id;
|
|
97
|
+
if (projectId) {
|
|
98
|
+
void getDescopeHostedConfigStatus(projectId).then((status) => {
|
|
99
|
+
if (status && status >= 400) {
|
|
100
|
+
(0, write_line_1.writeLine)(picocolors_1.default.yellow('⚠ Descope hosted login may be misconfigured for this project.'));
|
|
101
|
+
(0, write_line_1.writeLine)(picocolors_1.default.dim(` https://api.descope.com/pages/${projectId}/v2-beta/config.json returned HTTP ${status}`));
|
|
102
|
+
(0, write_line_1.writeLine)(picocolors_1.default.dim(' This usually means an outdated auth project_id or missing Descope flow hosting config.'));
|
|
103
|
+
(0, write_line_1.writeLine)(picocolors_1.default.dim(' Ask your platform team to set auth.project_id in create-link-app.json, or set LINKAPP_DESCOPE_[QA|PRODUCTION]_PROJECT_ID.'));
|
|
104
|
+
(0, write_line_1.writeLine)();
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
// Poll for token
|
|
109
|
+
s.start('Waiting for authentication');
|
|
110
|
+
const token = await (0, device_auth_1.pollAccessToken)(handle);
|
|
111
|
+
s.stop('Authentication successful');
|
|
112
|
+
if ((0, access_token_1.isTokenValid)(token)) {
|
|
113
|
+
(0, access_token_1.saveAccessToken)(token.access_token);
|
|
114
|
+
(0, write_line_1.writeLine)(picocolors_1.default.green('✓ Login successful'));
|
|
115
|
+
if (token.expires_at) {
|
|
116
|
+
(0, write_line_1.writeLine)(picocolors_1.default.dim(` Token expires at ${new Date(token.expires_at * 1000).toString()}`));
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
p.outro(picocolors_1.default.green('You are now logged in'));
|
|
120
|
+
}
|
|
121
|
+
exports.loginCommand = loginCommand;
|
|
10
122
|
class Login extends base_1.default {
|
|
11
123
|
async run() {
|
|
12
124
|
const { flags } = await this.parse(Login);
|
|
13
|
-
|
|
14
|
-
const appConfig = await this.getAppConfig(flags.qa ? 'qa' : 'production');
|
|
15
|
-
const handle = await (0, device_auth_1.initDeviceAuthorization)(appConfig.auth);
|
|
16
|
-
const { expires_in, user_code, verification_uri_complete } = handle;
|
|
17
|
-
const expiryTime = expires_in % 60 === 0 ? `${expires_in / 60} minutes` : `${expires_in} seconds`;
|
|
18
|
-
this.log(`🌐 Please login via the opened web browser link. The browser window should display the following code: ${user_code}`);
|
|
19
|
-
this.log(`🔗 If browser does not open automatically, please go to the following link: ${verification_uri_complete}`);
|
|
20
|
-
this.log(`⏰ This link expires in ${expiryTime}. Press Ctrl-C to abort.`);
|
|
21
|
-
core_1.CliUx.ux.open(verification_uri_complete);
|
|
22
|
-
core_1.CliUx.ux.action.start('⏳ Waiting for user to authorize device from browser');
|
|
23
|
-
const token = await (0, device_auth_1.pollAccessToken)(handle);
|
|
24
|
-
core_1.CliUx.ux.action.stop();
|
|
25
|
-
if ((0, access_token_1.isTokenValid)(token)) {
|
|
26
|
-
(0, access_token_1.saveAccessToken)(token.access_token);
|
|
27
|
-
if (flags.qa) {
|
|
28
|
-
this.log('🔑 TOKEN: ', token.access_token);
|
|
29
|
-
}
|
|
30
|
-
this.log('✅ Device has been authorized. Login successful.');
|
|
31
|
-
if (token.expires_at) {
|
|
32
|
-
this.log(`⏰ Login will expire at ${new Date(token.expires_at * 1000).toString()}`);
|
|
33
|
-
}
|
|
34
|
-
}
|
|
125
|
+
await loginCommand({ qa: flags.qa });
|
|
35
126
|
}
|
|
36
127
|
}
|
|
37
128
|
exports.default = Login;
|
package/dist/commands/logout.js
CHANGED
|
@@ -3,26 +3,47 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.logoutCommand = void 0;
|
|
6
7
|
const core_1 = require("@oclif/core");
|
|
8
|
+
const open_1 = __importDefault(require("open"));
|
|
9
|
+
const picocolors_1 = __importDefault(require("picocolors"));
|
|
7
10
|
const base_1 = __importDefault(require("../base"));
|
|
11
|
+
const fetch_app_config_1 = __importDefault(require("../lib/fetch-app-config"));
|
|
8
12
|
const access_token_1 = require("../lib/auth/access-token");
|
|
13
|
+
const write_line_1 = require("../lib/utils/write-line");
|
|
14
|
+
async function logoutCommand(options) {
|
|
15
|
+
const env = options.qa ? 'qa' : 'production';
|
|
16
|
+
try {
|
|
17
|
+
const config = await (0, fetch_app_config_1.default)(env);
|
|
18
|
+
const { audience } = config.auth;
|
|
19
|
+
(0, access_token_1.removeAccessToken)(audience);
|
|
20
|
+
(0, write_line_1.writeLine)(picocolors_1.default.green('✓ Logged out successfully'));
|
|
21
|
+
(0, write_line_1.writeLine)(picocolors_1.default.dim(' Local credentials have been removed'));
|
|
22
|
+
if (config.logout_redirect_url) {
|
|
23
|
+
try {
|
|
24
|
+
await (0, open_1.default)(config.logout_redirect_url);
|
|
25
|
+
(0, write_line_1.writeLine)(picocolors_1.default.cyan('🌐 Browser opened to complete provider sign-out'));
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
(0, write_line_1.writeLine)(picocolors_1.default.yellow('! Browser could not be opened automatically'));
|
|
29
|
+
(0, write_line_1.writeLine)(picocolors_1.default.dim(` Complete sign-out here: ${config.logout_redirect_url}`));
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
(0, write_line_1.writeLine)();
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
console.error(picocolors_1.default.red('✗ Logout failed:'), error);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
exports.logoutCommand = logoutCommand;
|
|
9
39
|
class Logout extends base_1.default {
|
|
10
40
|
async run() {
|
|
11
41
|
const { flags } = await this.parse(Logout);
|
|
12
|
-
|
|
13
|
-
const appConfig = await this.getAppConfig(flags.qa ? 'qa' : 'production');
|
|
14
|
-
const { auth, logout_redirect_url } = appConfig;
|
|
15
|
-
const { domain, client_id, audience } = auth;
|
|
16
|
-
(0, access_token_1.removeAccessToken)(audience);
|
|
17
|
-
// clear Auth0 session cookies so user gets prompted to enter credentials again for next login
|
|
18
|
-
const url = `${domain}/v2/logout?client_id=${client_id}&returnTo=${logout_redirect_url}`;
|
|
19
|
-
core_1.CliUx.ux.open(url);
|
|
20
|
-
this.log('✅ Logout successful.');
|
|
21
|
-
this.log(`🌐 If browser did not open automatically, please go to the following link to logout from the browser: ${url}`);
|
|
42
|
+
await logoutCommand({ qa: flags.qa });
|
|
22
43
|
}
|
|
23
44
|
}
|
|
24
45
|
exports.default = Logout;
|
|
25
|
-
Logout.description = 'Logout and clear
|
|
46
|
+
Logout.description = 'Logout and clear local credentials';
|
|
26
47
|
Logout.flags = {
|
|
27
48
|
qa: core_1.Flags.boolean({
|
|
28
49
|
description: 'Use QA environment. Admin use only.',
|
|
@@ -7,6 +7,16 @@ exports.getAccessToken = exports.removeAccessToken = exports.saveAccessToken = e
|
|
|
7
7
|
const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
|
|
8
8
|
const netrc_parser_1 = __importDefault(require("netrc-parser"));
|
|
9
9
|
const TokenExpiryThresholdSeconds = 60; // Used to consider tokens as 'expired' within the last x seconds of its actual expiry to ensure slow requests still use a valid token
|
|
10
|
+
function normalizeClaimValues(claim) {
|
|
11
|
+
if (!claim) {
|
|
12
|
+
return [];
|
|
13
|
+
}
|
|
14
|
+
const values = Array.isArray(claim) ? claim : [claim];
|
|
15
|
+
return values
|
|
16
|
+
.flatMap((value) => value.split(/[,\s]+/))
|
|
17
|
+
.map((value) => value.trim())
|
|
18
|
+
.filter((value) => value.length > 0);
|
|
19
|
+
}
|
|
10
20
|
function isTokenValid(token) {
|
|
11
21
|
if (!token.access_token || token.token_type !== 'Bearer') {
|
|
12
22
|
throw new Error('Received access token is invalid');
|
|
@@ -15,16 +25,21 @@ function isTokenValid(token) {
|
|
|
15
25
|
if (!payload) {
|
|
16
26
|
throw new Error('There was an error parsing the access token, please try logging in again');
|
|
17
27
|
}
|
|
18
|
-
const permissions = payload.permissions
|
|
28
|
+
const permissions = normalizeClaimValues(payload.permissions);
|
|
19
29
|
if (permissions.length === 0) {
|
|
20
|
-
|
|
30
|
+
// Descope and other providers may encode permissions in scope-like claims.
|
|
31
|
+
const scopePermissions = [...normalizeClaimValues(payload.scope), ...normalizeClaimValues(payload.scp)].filter((v) => /[:.]/u.test(v));
|
|
32
|
+
if (scopePermissions.length === 0) {
|
|
33
|
+
throw new Error('Login request was successful, but the authenticated user does not have appropriate permissions to view or modify Link Apps');
|
|
34
|
+
}
|
|
21
35
|
}
|
|
22
36
|
return true;
|
|
23
37
|
}
|
|
24
38
|
exports.isTokenValid = isTokenValid;
|
|
25
39
|
function saveAccessToken(tokenString) {
|
|
26
40
|
const token = jsonwebtoken_1.default.decode(tokenString, { json: true });
|
|
27
|
-
const
|
|
41
|
+
const rawAudience = token?.aud;
|
|
42
|
+
const audience = Array.isArray(rawAudience) ? rawAudience[0] : rawAudience;
|
|
28
43
|
if (audience && typeof audience === 'string') {
|
|
29
44
|
netrc_parser_1.default.loadSync();
|
|
30
45
|
netrc_parser_1.default.machines[audience] = {
|
|
@@ -53,15 +68,13 @@ function getAccessToken(audience) {
|
|
|
53
68
|
if (!tokenString) {
|
|
54
69
|
throw new Error('Not logged in. Please login using command: login');
|
|
55
70
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
throw new Error('Login expired. Please login again using command: login');
|
|
63
|
-
}
|
|
64
|
-
return tokenString;
|
|
71
|
+
const token = jsonwebtoken_1.default.decode(tokenString, { json: true });
|
|
72
|
+
if (!token?.exp) {
|
|
73
|
+
throw new Error('Cached login token is invalid. Please login again using command: login');
|
|
74
|
+
}
|
|
75
|
+
if (token.exp - TokenExpiryThresholdSeconds < Math.floor(Date.now() / 1000)) {
|
|
76
|
+
throw new Error('Login expired. Please login again using command: login');
|
|
65
77
|
}
|
|
78
|
+
return tokenString;
|
|
66
79
|
}
|
|
67
80
|
exports.getAccessToken = getAccessToken;
|
|
@@ -3,13 +3,21 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.pollAccessToken = exports.initDeviceAuthorization = void 0;
|
|
4
4
|
const openid_client_1 = require("openid-client");
|
|
5
5
|
async function initDeviceAuthorization(config) {
|
|
6
|
-
const { domain, client_id, audience } = config;
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
const { domain, client_id, audience, project_id } = config;
|
|
7
|
+
if (!project_id) {
|
|
8
|
+
throw new Error('Authentication configuration is missing the project_id. Please update to the latest CLI version and try again.');
|
|
9
|
+
}
|
|
10
|
+
const issuer = new openid_client_1.Issuer({
|
|
11
|
+
issuer: `${domain}/v1/apps/customized/${project_id}`,
|
|
12
|
+
device_authorization_endpoint: `${domain}/oauth2/v1/apps/${project_id}/device`,
|
|
13
|
+
token_endpoint: `${domain}/oauth2/v1/apps/${project_id}/token`,
|
|
14
|
+
jwks_uri: `${domain}/${project_id}/.well-known/jwks.json`,
|
|
15
|
+
});
|
|
16
|
+
const client = new issuer.Client({
|
|
9
17
|
client_id,
|
|
10
18
|
token_endpoint_auth_method: 'none',
|
|
11
19
|
});
|
|
12
|
-
return
|
|
20
|
+
return client.deviceAuthorization({ scope: 'openid', audience });
|
|
13
21
|
}
|
|
14
22
|
exports.initDeviceAuthorization = initDeviceAuthorization;
|
|
15
23
|
async function pollAccessToken(handle) {
|
|
@@ -4,15 +4,33 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
const axios_1 = __importDefault(require("axios"));
|
|
7
|
+
const PROJECT_IDS = {
|
|
8
|
+
'https://ciam.qa.linktr.ee': 'P32AChDvEswJtnBOsX26vHeCXTws',
|
|
9
|
+
'https://ciam.linktr.ee': 'P32ACVpudk8MNftmpf1LR1pW5k8s',
|
|
10
|
+
};
|
|
11
|
+
const PROJECT_ID_ENV_VARS = {
|
|
12
|
+
qa: 'LINKAPP_DESCOPE_QA_PROJECT_ID',
|
|
13
|
+
production: 'LINKAPP_DESCOPE_PRODUCTION_PROJECT_ID',
|
|
14
|
+
};
|
|
7
15
|
const fetchAppConfig = async (stage = 'production') => {
|
|
8
16
|
const configUrl = `https://link-types-config.${stage}.linktr.ee/create-link-app.json`;
|
|
9
17
|
try {
|
|
10
18
|
const response = await axios_1.default.get(configUrl);
|
|
11
|
-
|
|
19
|
+
const config = response.data;
|
|
20
|
+
const projectIdOverride = process.env[PROJECT_ID_ENV_VARS[stage]] ?? process.env.LINKAPP_DESCOPE_PROJECT_ID;
|
|
21
|
+
if (projectIdOverride) {
|
|
22
|
+
config.auth.project_id = projectIdOverride;
|
|
23
|
+
return config;
|
|
24
|
+
}
|
|
25
|
+
// Inject project_id if not present in remote config (temporary until Davis config is deployed)
|
|
26
|
+
if (!config.auth.project_id && config.auth.domain) {
|
|
27
|
+
config.auth.project_id = PROJECT_IDS[config.auth.domain];
|
|
28
|
+
}
|
|
29
|
+
return config;
|
|
12
30
|
}
|
|
13
31
|
catch (err) {
|
|
14
32
|
if (axios_1.default.isAxiosError(err)) {
|
|
15
|
-
throw new Error(err.message);
|
|
33
|
+
throw new Error(`Failed to fetch config from ${configUrl}: ${err.message}`);
|
|
16
34
|
}
|
|
17
35
|
throw err;
|
|
18
36
|
}
|
package/oclif.manifest.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "2.2.
|
|
2
|
+
"version": "2.2.3",
|
|
3
3
|
"commands": {
|
|
4
4
|
"build": {
|
|
5
5
|
"id": "build",
|
|
@@ -233,7 +233,7 @@
|
|
|
233
233
|
},
|
|
234
234
|
"logout": {
|
|
235
235
|
"id": "logout",
|
|
236
|
-
"description": "Logout and clear
|
|
236
|
+
"description": "Logout and clear local credentials",
|
|
237
237
|
"strict": true,
|
|
238
238
|
"pluginName": "@linktr.ee/create-link-app",
|
|
239
239
|
"pluginAlias": "@linktr.ee/create-link-app",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@linktr.ee/create-link-app",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.3",
|
|
4
4
|
"description": "Create a Link App on Linktr.ee.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"author": "Linktree",
|
|
@@ -33,6 +33,7 @@
|
|
|
33
33
|
"@babel/preset-react": "^7.17.12",
|
|
34
34
|
"@babel/preset-typescript": "^7.17.12",
|
|
35
35
|
"@babel/runtime": "^7.18.3",
|
|
36
|
+
"@clack/prompts": "^0.7.0",
|
|
36
37
|
"@linktr.ee/ui-link-kit": "latest",
|
|
37
38
|
"@oclif/core": "^1.9.0",
|
|
38
39
|
"@oclif/plugin-help": "5.1.23",
|
|
@@ -62,7 +63,9 @@
|
|
|
62
63
|
"jsonwebtoken": "^8.5.1",
|
|
63
64
|
"mini-css-extract-plugin": "^2.7.5",
|
|
64
65
|
"netrc-parser": "^3.1.6",
|
|
66
|
+
"open": "^8.4.0",
|
|
65
67
|
"openid-client": "^5.1.6",
|
|
68
|
+
"picocolors": "^1.0.0",
|
|
66
69
|
"postcss": "^8.4.21",
|
|
67
70
|
"postcss-loader": "^4.3.0",
|
|
68
71
|
"prettier": "^2.8.8",
|