@yahoo/uds 1.2.0 → 1.3.0

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.
Files changed (84) hide show
  1. package/cli/README.md +21 -0
  2. package/cli/cli.ts +27 -4
  3. package/cli/commands/login.ts +28 -0
  4. package/cli/commands/logout.ts +24 -0
  5. package/cli/commands/uds.ts +4 -1
  6. package/cli/env.d.ts +5 -0
  7. package/cli/exec.ts +1 -1
  8. package/cli/preload.ts +16 -0
  9. package/cli/uds-cli +3 -3
  10. package/cli/utils/auth.test.ts +107 -0
  11. package/cli/utils/auth.ts +158 -0
  12. package/cli/utils/client_secrets.json.enc +0 -0
  13. package/cli/utils/secrets.ts +40 -0
  14. package/cli/utils/types.ts +8 -0
  15. package/package.json +163 -155
  16. package/cli/bin/uds-darwin-arm64-baseline +0 -0
  17. package/cli/bin/uds-linux-x64-baseline +0 -0
  18. package/dist/chunk-2JIYRTEY.js +0 -3
  19. package/dist/chunk-3VTRSTDI.cjs +0 -2
  20. package/dist/chunk-4J7ZJHCN.js +0 -2
  21. package/dist/chunk-5X3JN6IH.cjs +0 -3
  22. package/dist/chunk-6K5IORZA.cjs +0 -2
  23. package/dist/chunk-AIRVDEVS.cjs +0 -3
  24. package/dist/chunk-BQTYVXIC.js +0 -3
  25. package/dist/chunk-CE5NDFHI.js +0 -2
  26. package/dist/chunk-E3Z3A4MR.cjs +0 -2
  27. package/dist/chunk-OJR4RBBW.js +0 -3
  28. package/dist/chunk-PCSDVJE6.js +0 -2
  29. package/dist/chunk-SLCISFAV.cjs +0 -3
  30. package/dist/chunk-TWTUXY76.cjs +0 -2
  31. package/dist/chunk-TYKFUQUM.cjs +0 -1
  32. package/dist/chunk-W5AGPUNC.cjs +0 -4
  33. package/dist/chunk-XFTF7IHG.js +0 -2
  34. package/dist/chunk-Y2TGGXMH.js +0 -3
  35. package/dist/chunk-ZJE5XRN2.js +0 -4
  36. package/dist/client/index.cjs +0 -4
  37. package/dist/client/index.d.cts +0 -97
  38. package/dist/client/index.d.ts +0 -97
  39. package/dist/client/index.js +0 -6
  40. package/dist/experimental/index.cjs +0 -3
  41. package/dist/experimental/index.d.cts +0 -451
  42. package/dist/experimental/index.d.ts +0 -451
  43. package/dist/experimental/index.js +0 -3
  44. package/dist/fixtures.cjs +0 -1506
  45. package/dist/fixtures.d.cts +0 -94
  46. package/dist/fixtures.d.ts +0 -94
  47. package/dist/fixtures.js +0 -1449
  48. package/dist/index.cjs +0 -1
  49. package/dist/index.d.cts +0 -300
  50. package/dist/index.d.ts +0 -300
  51. package/dist/index.js +0 -2
  52. package/dist/metafile-cjs.json +0 -1
  53. package/dist/metafile-esm.json +0 -1
  54. package/dist/motionFeatures-M3DOZMRC.cjs +0 -1
  55. package/dist/motionFeatures-QDISAARM.js +0 -1
  56. package/dist/styles/toast.css +0 -3
  57. package/dist/styles/toast.d.cts +0 -2
  58. package/dist/styles/toast.d.ts +0 -2
  59. package/dist/tailwind/plugin.cjs +0 -3
  60. package/dist/tailwind/plugin.d.cts +0 -60
  61. package/dist/tailwind/plugin.d.ts +0 -60
  62. package/dist/tailwind/plugin.js +0 -3
  63. package/dist/tailwind/purger.cjs +0 -6
  64. package/dist/tailwind/purger.d.cts +0 -17
  65. package/dist/tailwind/purger.d.ts +0 -17
  66. package/dist/tailwind/purger.js +0 -6
  67. package/dist/tailwind/tsMorph.cjs +0 -3
  68. package/dist/tailwind/tsMorph.d.cts +0 -31
  69. package/dist/tailwind/tsMorph.d.ts +0 -31
  70. package/dist/tailwind/tsMorph.js +0 -2
  71. package/dist/tailwind/utils.cjs +0 -1
  72. package/dist/tailwind/utils.d.cts +0 -99
  73. package/dist/tailwind/utils.d.ts +0 -99
  74. package/dist/tailwind/utils.js +0 -1
  75. package/dist/tokens/index.cjs +0 -1
  76. package/dist/tokens/index.d.cts +0 -1127
  77. package/dist/tokens/index.d.ts +0 -1127
  78. package/dist/tokens/index.js +0 -1
  79. package/dist/tokens/parseTokens.cjs +0 -1
  80. package/dist/tokens/parseTokens.d.cts +0 -59
  81. package/dist/tokens/parseTokens.d.ts +0 -59
  82. package/dist/tokens/parseTokens.js +0 -1
  83. package/dist/types-CpZ2ERLZ.d.cts +0 -949
  84. package/dist/types-CpZ2ERLZ.d.ts +0 -949
package/cli/README.md CHANGED
@@ -128,6 +128,27 @@ Any file added to commands/codemod will be available in prompt land.
128
128
  uds codemod
129
129
  ```
130
130
 
131
+ ### Login
132
+
133
+ The `uds login` command is used to authenticate the user to the CLI. We use
134
+ Google's OAuth2 flow, which is the same login flow used by the [Configurator](/docs/getting-started/using-configurator) and the documentation site. Logging in to the CLI helps us identify and track CLI usage and metrics.
135
+
136
+ #### Flags
137
+
138
+ | Flag | Description | Default | Required |
139
+ | ---------- | ----------------------------------------- | ------- | -------- |
140
+ | `skipAuth` | Skip auth checks (for CI/CD environments) | `false` | |
141
+
142
+ **Example:**
143
+
144
+ ```shell
145
+ uds login
146
+ uds login --skipAuth
147
+
148
+ # Sign out current user.
149
+ uds logout
150
+ ```
151
+
131
152
  ### Expo (WIP)
132
153
 
133
154
  The `uds expo` command is for building and launching React Native apps using Expo.
package/cli/cli.ts CHANGED
@@ -2,25 +2,48 @@
2
2
  // Start bluebun to run the correct CLI command
3
3
  import { parseArgs } from 'node:util';
4
4
 
5
- import { run } from 'bluebun';
5
+ import { getFeatureFlags } from '@yahoo/uds/flags';
6
+ import { print, red, run } from 'bluebun';
6
7
 
7
- function main() {
8
+ import { getAuthenticatedUser } from './utils/auth';
9
+
10
+ async function main() {
8
11
  // We need to pass the path from Node to the Bun binaries
9
12
  // because there's an issue where the Bun binaries struggle to resolve
10
- const { values } = parseArgs({
13
+ const { values, positionals } = parseArgs({
11
14
  args: Bun.argv,
12
15
  options: {
13
16
  commandsPath: {
14
17
  type: 'string',
15
18
  },
19
+ skipAuth: {
20
+ type: 'boolean',
21
+ default: false,
22
+ },
16
23
  },
17
24
  strict: false,
18
25
  allowPositionals: true,
19
26
  });
20
27
 
28
+ const cliPath = values.commandsPath!.toString();
29
+ const isLoginCmd = positionals.includes('login');
30
+ const { useCLIAuth } = getFeatureFlags();
31
+
32
+ if (useCLIAuth && !values.skipAuth && !isLoginCmd) {
33
+ const user = await getAuthenticatedUser(cliPath);
34
+ if (!user) {
35
+ print(
36
+ red(
37
+ '🚨 Sign-in required. Please run `uds login` and use a @yahooinc.com email during sign-in.',
38
+ ),
39
+ );
40
+ return;
41
+ }
42
+ }
43
+
21
44
  run({
22
45
  name: 'uds',
23
- cliPath: values.commandsPath!.toString(),
46
+ cliPath,
24
47
  });
25
48
  }
26
49
 
@@ -0,0 +1,28 @@
1
+ import { magenta, print, red } from 'bluebun';
2
+
3
+ import { login } from '../utils/auth';
4
+
5
+ export default {
6
+ name: 'login',
7
+ description: '👤 Log in to UDS CLI',
8
+ alias: ['auth'],
9
+ run: async () => {
10
+ try {
11
+ const user = await login();
12
+ if (user) {
13
+ print(magenta(`🔒 Logged in as ${user.email}`));
14
+ }
15
+ } catch (error) {
16
+ if (error instanceof Error) {
17
+ print(red(error.message));
18
+ } else {
19
+ print(
20
+ red(
21
+ `❌ An error occurred while logging in. Please ask in the #uds-ask channel for support.`,
22
+ ),
23
+ );
24
+ process.exitCode = 1;
25
+ }
26
+ }
27
+ },
28
+ };
@@ -0,0 +1,24 @@
1
+ import { magenta, print, red } from 'bluebun';
2
+
3
+ import { logout } from '../utils/auth';
4
+
5
+ export default {
6
+ name: 'logout',
7
+ description: '👤 Sign out of UDS CLI',
8
+ run: async () => {
9
+ try {
10
+ await logout();
11
+ print(magenta('👋 You have been logged out.'));
12
+ } catch (error) {
13
+ if (error instanceof Error) {
14
+ print(red(error.message));
15
+ } else {
16
+ print(
17
+ red(
18
+ `❌ An error occurred while logging out. Please ask in the #uds-ask channel for support.`,
19
+ ),
20
+ );
21
+ }
22
+ }
23
+ },
24
+ };
@@ -1,5 +1,6 @@
1
1
  import { print, type Props, red } from 'bluebun';
2
2
 
3
+ import { getAuthenticatedUser } from '../utils/auth';
3
4
  import { getCommandHelp } from '../utils/getCommandHelp';
4
5
 
5
6
  export default {
@@ -9,6 +10,8 @@ export default {
9
10
  if (props.first) {
10
11
  print(red(`Unknown command: ${props.first}`));
11
12
  }
12
- await getCommandHelp(props);
13
+ const user = await getAuthenticatedUser();
14
+ const notes = user ? `🔒 Logged in as ${user.email}` : undefined;
15
+ await getCommandHelp({ ...props, notes });
13
16
  },
14
17
  };
package/cli/env.d.ts CHANGED
@@ -14,6 +14,11 @@ declare module 'bun' {
14
14
  EXPO_USE_METRO_WORKSPACE_ROOT?: string;
15
15
  EXPO_BUNDLE_APP?: string;
16
16
  ESLINT_USE_FLAT_CONFIG?: string;
17
+ /** UDS purge command - which color and scale modes to keep */
17
18
  ENABLED_SCALE_AND_COLOR_MODES?: string;
19
+ /** UDS config id */
20
+ UDS_ID?: string;
21
+ /** UDS sync command - filename to write the config output */
22
+ UDS_OUT_FILE?: string;
18
23
  }
19
24
  }
package/cli/exec.ts CHANGED
@@ -28,7 +28,7 @@ const detectPlatform = () => {
28
28
  });
29
29
  } else {
30
30
  throw new Error(
31
- 'Unsupported platform 😢, pleaes reach out to the UDS team for support #uds-ask.',
31
+ 'Unsupported platform 😢, please reach out to the UDS team for support #uds-ask.',
32
32
  );
33
33
  }
34
34
  };
package/cli/preload.ts CHANGED
@@ -44,3 +44,19 @@ mock.module('@yahoo/uds/tailwind/purger', () => ({
44
44
  Button: '',
45
45
  },
46
46
  }));
47
+
48
+ mock.module('googleapis', () => ({
49
+ google: {
50
+ auth: {
51
+ OAuth2: mock(() => {}).mockImplementation(() => ({
52
+ getToken: () => Promise.resolve({ tokens: 'test' }),
53
+ setCredentials: mock(() => {}),
54
+ })),
55
+ },
56
+ oauth2: () => ({
57
+ userinfo: {
58
+ get: () => Promise.resolve({ data: { email: 'foo@yahooinc.com' } }),
59
+ },
60
+ }),
61
+ },
62
+ }));
package/cli/uds-cli CHANGED
@@ -2,9 +2,9 @@
2
2
 
3
3
  // Note: This file is generated, do not edit directly! Edit exec.ts instead.
4
4
  // cli/exec.ts
5
- import {execFileSync} from "child_process";
5
+ import { execFileSync } from "child_process";
6
6
  import path from "path";
7
- import {fileURLToPath} from "url";
7
+ import { fileURLToPath } from "url";
8
8
 
9
9
  // cli/consts.ts
10
10
  var SUPPORTED_BUN_PLATFORMS = ["linux-x64-baseline", "darwin-arm64-baseline"];
@@ -23,7 +23,7 @@ var detectPlatform = () => {
23
23
  stdio: "inherit"
24
24
  });
25
25
  } else {
26
- throw new Error("Unsupported platform \uD83D\uDE22, pleaes reach out to the UDS team for support #uds-ask.");
26
+ throw new Error("Unsupported platform \uD83D\uDE22, please reach out to the UDS team for support #uds-ask.");
27
27
  }
28
28
  };
29
29
  detectPlatform();
@@ -0,0 +1,107 @@
1
+ import path from 'node:path';
2
+
3
+ import { afterAll, describe, expect, it, mock } from 'bun:test';
4
+ import http from 'http';
5
+ // eslint-disable-next-line n/no-unpublished-import
6
+ import httpMocks from 'node-mocks-http';
7
+
8
+ import {
9
+ getAuthenticatedUser,
10
+ isYahooEmployee,
11
+ login,
12
+ logout,
13
+ onRequestHandler,
14
+ type User,
15
+ } from './auth';
16
+
17
+ const __dirname = path.resolve(import.meta.dir);
18
+ const filepath = path.resolve(__dirname, '.uds/user.json');
19
+
20
+ const writeCache = async (filePath: string, user: User) => {
21
+ return Bun.write(filePath, JSON.stringify(user));
22
+ };
23
+
24
+ describe('auth', () => {
25
+ describe('isYahooEmployee', () => {
26
+ it('does not return a user on first use', async () => {
27
+ expect(isYahooEmployee({ email: 'user@yahooinc.com' })).toBe(true);
28
+ expect(isYahooEmployee({ email: 'user@test.com' })).toBe(false);
29
+ });
30
+ });
31
+
32
+ describe('getAuthenticatedUser', () => {
33
+ it('does not return a user on first use', async () => {
34
+ expect(await getAuthenticatedUser(__dirname)).toBeUndefined();
35
+ });
36
+
37
+ it('reads user info from cache when one exists', async () => {
38
+ const user: User = { email: 'user@yahooinc.com', name: 'User' };
39
+ await writeCache(filepath, user);
40
+ const userInfo = await getAuthenticatedUser(__dirname);
41
+ expect(userInfo).toEqual(user);
42
+ });
43
+
44
+ it('resets user when non @yahooinc.com email is used', async () => {
45
+ await writeCache(filepath, { email: 'user@test.com', name: 'User' });
46
+ expect(await Bun.file(filepath).exists()).toBeTrue();
47
+ const userInfo = await getAuthenticatedUser(__dirname);
48
+ expect(userInfo).toBeUndefined();
49
+ expect(await Bun.file(filepath).exists()).toBeFalse();
50
+ });
51
+ });
52
+
53
+ describe('getAuthenticatedClient', () => {
54
+ // mock.module('open', () => ({}));
55
+ const mockServer = Object.assign(Object.create(http.Server.prototype), {
56
+ listen: mock(),
57
+ on: mock(),
58
+ close: mock(),
59
+ });
60
+ const resolve = mock();
61
+ const reject = mock();
62
+
63
+ it('returns error when no code is on callback url', async () => {
64
+ const request = httpMocks.createRequest({ method: 'GET', url: '/oauth2callback' });
65
+ const response = httpMocks.createResponse();
66
+ await onRequestHandler.bind(mockServer)(request, response, resolve, reject);
67
+ expect(response._getData()).toInclude('no code parameter');
68
+ });
69
+
70
+ it('can successfully login in user', async () => {
71
+ const request = httpMocks.createRequest({ method: 'GET', url: '/oauth2callback?code=123' });
72
+ const response = httpMocks.createResponse();
73
+ await onRequestHandler.bind(mockServer)(request, response, resolve, reject);
74
+ expect(response._getData()).toInclude('Authentication successful!');
75
+ expect(resolve).toHaveBeenCalled();
76
+ });
77
+ });
78
+
79
+ describe('logout', () => {
80
+ it('removes the user cache', async () => {
81
+ await writeCache(filepath, { email: 'user@yahooinc.com', name: 'User' });
82
+ expect(await Bun.file(filepath).exists()).toBeTrue();
83
+ await logout(filepath);
84
+ expect(await Bun.file(filepath).exists()).toBeFalse();
85
+ });
86
+ });
87
+
88
+ describe('login', () => {
89
+ afterAll(async () => {
90
+ await logout(); // cleanup
91
+ });
92
+
93
+ const mockFunc = mock(() => Promise.resolve({ email: 'foo@yahooinc.com' }));
94
+
95
+ mock.module('./auth', () => ({
96
+ getAuthenticatedClient: mockFunc,
97
+ }));
98
+
99
+ it('uses cache when user previously logged in', async () => {
100
+ // Test multiple calls to login
101
+ await login();
102
+ await login();
103
+ await login();
104
+ expect(mockFunc).toHaveBeenCalledTimes(1);
105
+ });
106
+ });
107
+ });
@@ -0,0 +1,158 @@
1
+ import { unlink } from 'node:fs/promises';
2
+ import path from 'node:path';
3
+
4
+ import { print, red } from 'bluebun';
5
+ import Bun from 'bun';
6
+ import { Auth, google, oauth2_v2 } from 'googleapis';
7
+ import http from 'http';
8
+ import open from 'open';
9
+
10
+ import clientSecrets from './client_secrets.json';
11
+
12
+ const { redirect_uris, client_id, client_secret } = clientSecrets.web;
13
+ const REDIRECT_URL = redirect_uris[0];
14
+ const { port: PORT, origin: BASE_URL } = new URL(REDIRECT_URL);
15
+
16
+ const cacheFilePath = '.uds/user.json';
17
+ const DEFAULT_CLI_PATH = path.resolve(import.meta.dir, '..');
18
+ const CACHED_USER_FILE = path.resolve(DEFAULT_CLI_PATH, cacheFilePath);
19
+
20
+ // Credentials can be found in https://console.cloud.google.com/apis/credentials/oauthclient/700524957090-6uju02gp6i0rlm3p17nt1v6vbrretfka.apps.googleusercontent.com?project=uds-poc.
21
+ const oauth2Client = new google.auth.OAuth2(client_id, client_secret, REDIRECT_URL);
22
+
23
+ type User = oauth2_v2.Schema$Userinfo;
24
+
25
+ function isYahooEmployee(user?: User) {
26
+ return user?.email?.endsWith('@yahooinc.com');
27
+ }
28
+
29
+ async function onRequestHandler(
30
+ this: http.Server,
31
+ req: http.IncomingMessage,
32
+ res: http.ServerResponse,
33
+ resolve: (value: typeof oauth2Client) => void,
34
+ reject: (reason?: unknown) => void,
35
+ ) {
36
+ try {
37
+ const code = new URL(req.url || '', BASE_URL).searchParams.get('code');
38
+ if (!code) {
39
+ res.end('There was no code parameter on the url.');
40
+ this.close();
41
+ return;
42
+ }
43
+
44
+ res.end('Authentication successful! You can close this window.');
45
+
46
+ this.close();
47
+
48
+ const { tokens } = await oauth2Client.getToken(code);
49
+ oauth2Client.setCredentials(tokens);
50
+
51
+ resolve(oauth2Client);
52
+ } catch (e) {
53
+ reject(e);
54
+ }
55
+ }
56
+
57
+ async function getAuthenticatedClient(): Promise<Auth.OAuth2Client> {
58
+ return new Promise((resolve, reject) => {
59
+ const authorizeUrl = oauth2Client.generateAuthUrl({
60
+ access_type: 'offline',
61
+ scope: [
62
+ 'https://www.googleapis.com/auth/userinfo.profile',
63
+ 'https://www.googleapis.com/auth/userinfo.email',
64
+ ].join(' '),
65
+ hd: 'yahooinc.com',
66
+ include_granted_scopes: true,
67
+ });
68
+
69
+ // TODO: If port (3000) is already in use, this will fail.
70
+ // Setup https://www.npmjs.com/package/find-free-ports, but that won't
71
+ // play well with the pre-configured redirect_uris in the Google Cloud Console.
72
+ const server = http
73
+ .createServer()
74
+ .listen(PORT, async () => {
75
+ const childProcess = await open(authorizeUrl, { wait: false });
76
+ childProcess.unref();
77
+ })
78
+ .on('request', (req, res) => onRequestHandler.call(server, req, res, resolve, reject))
79
+ .on('error', (err: NodeJS.ErrnoException) => {
80
+ if (err.code && err.code.includes('EADDRINUSE')) {
81
+ print(
82
+ red(`🚨 Port ${PORT} already in use. Cannot start local server to handle OAuth flow.`),
83
+ );
84
+ server.close();
85
+ }
86
+ });
87
+ });
88
+ }
89
+
90
+ async function logout(cachePath?: string) {
91
+ // 1. TODO: Revoke token access
92
+ try {
93
+ // 2. Remove cached user data.
94
+ return await unlink(cachePath ?? CACHED_USER_FILE);
95
+ } catch {
96
+ // noop
97
+ }
98
+ }
99
+
100
+ /**
101
+ * Takes the user through the OAuth2 flow, creating a server for the oauth callback.
102
+ * If the user has previously authenticated, it returns the cached user.
103
+ * @returns
104
+ */
105
+ async function login() {
106
+ let user = await getAuthenticatedUser();
107
+ // Authenticate if there's no user cached.
108
+ if (!user) {
109
+ try {
110
+ const auth = await getAuthenticatedClient();
111
+ const oauth2 = google.oauth2({ version: 'v2', auth });
112
+ user = (await oauth2.userinfo.get()).data;
113
+ await Bun.write(CACHED_USER_FILE, JSON.stringify(user, null, 2));
114
+ } catch (err) {
115
+ console.error('Error:', err);
116
+ }
117
+ }
118
+
119
+ return user;
120
+ }
121
+
122
+ /**
123
+ * The authenticated user or undefined if the user is not authenticated.
124
+ * @param cliPath Root folder of cli commands. Defaults to 'packages/uds/cli'.
125
+ * Note: this is only needed when calling this method in cli.ts. The compiled
126
+ * platform-specific binaries create a virtual file system and we need
127
+ * to forward the package libPath from exec.ts.
128
+ * @returns user info
129
+ */
130
+ async function getAuthenticatedUser(cliPath = DEFAULT_CLI_PATH): Promise<User | undefined> {
131
+ try {
132
+ const cachePath = path.resolve(cliPath, cacheFilePath);
133
+ const file = Bun.file(cachePath);
134
+ const user = await file.json();
135
+
136
+ if (isYahooEmployee(user)) {
137
+ return user;
138
+ }
139
+
140
+ await logout(cachePath); // remove cached user.
141
+ } catch (err) {
142
+ if ((err as NodeJS.ErrnoException).code !== 'ENOENT') {
143
+ console.error(err);
144
+ }
145
+ }
146
+
147
+ return undefined;
148
+ }
149
+
150
+ export {
151
+ getAuthenticatedClient,
152
+ getAuthenticatedUser,
153
+ isYahooEmployee,
154
+ login,
155
+ logout,
156
+ onRequestHandler,
157
+ type User,
158
+ };
@@ -0,0 +1,40 @@
1
+ import path from 'node:path';
2
+
3
+ // eslint-disable-next-line n/no-unpublished-import
4
+ import { loadEnvConfig } from '@next/env';
5
+ import { $ } from 'bun';
6
+
7
+ loadEnvConfig(process.cwd());
8
+
9
+ const secretsFile = path.resolve(import.meta.dir, 'client_secrets.json');
10
+ const encryptedFilename = `${secretsFile}.enc`;
11
+
12
+ async function encrypt() {
13
+ await $`openssl enc -aes-256-cbc -in ${secretsFile} -out ${encryptedFilename}`;
14
+ }
15
+
16
+ async function decrypt() {
17
+ const f = Bun.file(secretsFile);
18
+ if (!(await f.exists())) {
19
+ await $`openssl enc -aes-256-cbc -in ${encryptedFilename} -out ${secretsFile} -d -pass env:UDS_CLI_SECRETS_PW`;
20
+ }
21
+ }
22
+
23
+ async function main() {
24
+ if (!process.env.UDS_CLI_SECRETS_PW) {
25
+ console.error('Please set UDS_CLI_SECRETS_PW env variable. The password is in LastPass.');
26
+ process.exitCode = 1;
27
+ return;
28
+ }
29
+
30
+ const cmd = Bun.argv[2];
31
+ if (cmd === 'encrypt') {
32
+ await encrypt();
33
+ console.log(`Secrets file encrypted to ${encryptedFilename}`);
34
+ } else if (cmd === 'decrypt') {
35
+ await decrypt();
36
+ console.log(`Secrets file saved to ${secretsFile}`);
37
+ }
38
+ }
39
+
40
+ main();
@@ -17,3 +17,11 @@ export type SyncOptions = {
17
17
  /** Enable watch mode? */
18
18
  watch?: boolean;
19
19
  };
20
+
21
+ export type LoginOptions = {
22
+ /**
23
+ * Opt-out of authentication. Typically used in a CI/CD environment.
24
+ * @default false
25
+ */
26
+ skipAuth?: boolean;
27
+ };