@memberjunction/cli 1.0.11

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 ADDED
@@ -0,0 +1,87 @@
1
+ MemberJunction CLI
2
+ ==================
3
+
4
+ <!-- toc -->
5
+ * [Usage](#usage)
6
+ * [Commands](#commands)
7
+ <!-- tocstop -->
8
+ # Usage
9
+ <!-- usage -->
10
+ ```sh-session
11
+ $ npm install -g @memberjunction/cli
12
+ $ mj COMMAND
13
+ running command...
14
+ $ mj (--version)
15
+ @memberjunction/cli/1.0.11 darwin-arm64 node-v20.12.2
16
+ $ mj --help [COMMAND]
17
+ USAGE
18
+ $ mj COMMAND
19
+ ...
20
+ ```
21
+ <!-- usagestop -->
22
+ # Commands
23
+ <!-- commands -->
24
+ * [`mj help [COMMAND]`](#mj-help-command)
25
+ * [`mj install`](#mj-install)
26
+ * [`mj version`](#mj-version)
27
+
28
+ ## `mj help [COMMAND]`
29
+
30
+ Display help for mj.
31
+
32
+ ```
33
+ USAGE
34
+ $ mj help [COMMAND...] [-n]
35
+
36
+ ARGUMENTS
37
+ COMMAND... Command to show help for.
38
+
39
+ FLAGS
40
+ -n, --nested-commands Include all nested commands in the output.
41
+
42
+ DESCRIPTION
43
+ Display help for mj.
44
+ ```
45
+
46
+ _See code: [@oclif/plugin-help](https://github.com/oclif/plugin-help/blob/v6.0.21/src/commands/help.ts)_
47
+
48
+ ## `mj install`
49
+
50
+ Install MemberJunction
51
+
52
+ ```
53
+ USAGE
54
+ $ mj install [-v]
55
+
56
+ FLAGS
57
+ -v, --verbose Enable additional logging
58
+
59
+ DESCRIPTION
60
+ Install MemberJunction
61
+
62
+ EXAMPLES
63
+ $ mj install
64
+ ```
65
+
66
+ _See code: [src/commands/install/index.ts](https://github.com/MemberJunction/MJ/blob/v1.0.11/src/commands/install/index.ts)_
67
+
68
+ ## `mj version`
69
+
70
+ ```
71
+ USAGE
72
+ $ mj version [--json] [--verbose]
73
+
74
+ FLAGS
75
+ --verbose Show additional information about the CLI.
76
+
77
+ GLOBAL FLAGS
78
+ --json Format output as json.
79
+
80
+ FLAG DESCRIPTIONS
81
+ --verbose Show additional information about the CLI.
82
+
83
+ Additionally shows the architecture, node version, operating system, and versions of plugins that the CLI is using.
84
+ ```
85
+
86
+ _See code: [@oclif/plugin-version](https://github.com/oclif/plugin-version/blob/v2.0.17/src/commands/version.ts)_
87
+ <!-- commandsstop -->
package/bin/dev.cmd ADDED
@@ -0,0 +1,3 @@
1
+ @echo off
2
+
3
+ node "%~dp0\dev" %*
package/bin/dev.js ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node_modules/.bin/ts-node
2
+ ;(async () => {
3
+ const oclif = await import('@oclif/core')
4
+ await oclif.execute({ development: true, dir: __dirname })
5
+ })()
package/bin/run.cmd ADDED
@@ -0,0 +1,3 @@
1
+ @echo off
2
+
3
+ node "%~dp0\run" %*
package/bin/run.js ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env node
2
+
3
+ ;(async () => {
4
+ const oclif = await import('@oclif/core')
5
+ await oclif.execute({development: false, dir: __dirname})
6
+ })()
@@ -0,0 +1,144 @@
1
+ import { Command } from '@oclif/core';
2
+ import { ParserOutput } from '@oclif/core/lib/interfaces/parser';
3
+ import { z } from 'zod';
4
+ type Config = z.infer<typeof configSchema>;
5
+ declare const configSchema: z.ZodObject<{
6
+ dbUrl: z.ZodString;
7
+ dbInstance: z.ZodString;
8
+ dbTrustServerCertificate: z.ZodEnum<["Y", "N"]>;
9
+ dbDatabase: z.ZodString;
10
+ dbPort: z.ZodNumber;
11
+ codeGenLogin: z.ZodString;
12
+ codeGenPwD: z.ZodString;
13
+ mjAPILogin: z.ZodString;
14
+ mjAPIPwD: z.ZodString;
15
+ graphQLPort: z.ZodOptional<z.ZodNumber>;
16
+ authType: z.ZodEnum<["MSAL", "AUTH0", "BOTH"]>;
17
+ msalWebClientId: z.ZodOptional<z.ZodString>;
18
+ msalTenantId: z.ZodOptional<z.ZodString>;
19
+ auth0ClientId: z.ZodOptional<z.ZodString>;
20
+ auth0ClientSecret: z.ZodOptional<z.ZodString>;
21
+ auth0Domain: z.ZodOptional<z.ZodString>;
22
+ createNewUser: z.ZodOptional<z.ZodEnum<["Y", "N"]>>;
23
+ userEmail: z.ZodDefault<z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodLiteral<"">]>>>;
24
+ userFirstName: z.ZodOptional<z.ZodString>;
25
+ userLastName: z.ZodOptional<z.ZodString>;
26
+ userName: z.ZodOptional<z.ZodString>;
27
+ openAIAPIKey: z.ZodOptional<z.ZodString>;
28
+ anthropicAPIKey: z.ZodOptional<z.ZodString>;
29
+ mistralAPIKey: z.ZodOptional<z.ZodString>;
30
+ }, "strip", z.ZodTypeAny, {
31
+ dbUrl: string;
32
+ dbInstance: string;
33
+ dbTrustServerCertificate: "N" | "Y";
34
+ dbDatabase: string;
35
+ dbPort: number;
36
+ codeGenLogin: string;
37
+ codeGenPwD: string;
38
+ mjAPILogin: string;
39
+ mjAPIPwD: string;
40
+ authType: "MSAL" | "AUTH0" | "BOTH";
41
+ userEmail: string;
42
+ graphQLPort?: number | undefined;
43
+ msalWebClientId?: string | undefined;
44
+ msalTenantId?: string | undefined;
45
+ auth0ClientId?: string | undefined;
46
+ auth0ClientSecret?: string | undefined;
47
+ auth0Domain?: string | undefined;
48
+ createNewUser?: "N" | "Y" | undefined;
49
+ userFirstName?: string | undefined;
50
+ userLastName?: string | undefined;
51
+ userName?: string | undefined;
52
+ openAIAPIKey?: string | undefined;
53
+ anthropicAPIKey?: string | undefined;
54
+ mistralAPIKey?: string | undefined;
55
+ }, {
56
+ dbUrl: string;
57
+ dbInstance: string;
58
+ dbTrustServerCertificate: "N" | "Y";
59
+ dbDatabase: string;
60
+ dbPort: number;
61
+ codeGenLogin: string;
62
+ codeGenPwD: string;
63
+ mjAPILogin: string;
64
+ mjAPIPwD: string;
65
+ authType: "MSAL" | "AUTH0" | "BOTH";
66
+ graphQLPort?: number | undefined;
67
+ msalWebClientId?: string | undefined;
68
+ msalTenantId?: string | undefined;
69
+ auth0ClientId?: string | undefined;
70
+ auth0ClientSecret?: string | undefined;
71
+ auth0Domain?: string | undefined;
72
+ createNewUser?: "N" | "Y" | undefined;
73
+ userEmail?: string | undefined;
74
+ userFirstName?: string | undefined;
75
+ userLastName?: string | undefined;
76
+ userName?: string | undefined;
77
+ openAIAPIKey?: string | undefined;
78
+ anthropicAPIKey?: string | undefined;
79
+ mistralAPIKey?: string | undefined;
80
+ }>;
81
+ export default class Install extends Command {
82
+ static description: string;
83
+ static examples: string[];
84
+ static flags: {
85
+ verbose: import("@oclif/core/lib/interfaces/parser").BooleanFlag<boolean>;
86
+ };
87
+ flags: ParserOutput<Install>['flags'];
88
+ userConfig: Config;
89
+ run(): Promise<void>;
90
+ getUserConfiguration(): Promise<{
91
+ dbUrl: string;
92
+ dbInstance: string;
93
+ dbTrustServerCertificate: "N" | "Y";
94
+ dbDatabase: string;
95
+ dbPort: number;
96
+ codeGenLogin: string;
97
+ codeGenPwD: string;
98
+ mjAPILogin: string;
99
+ mjAPIPwD: string;
100
+ authType: "MSAL" | "AUTH0" | "BOTH";
101
+ userEmail: string;
102
+ graphQLPort?: number | undefined;
103
+ msalWebClientId?: string | undefined;
104
+ msalTenantId?: string | undefined;
105
+ auth0ClientId?: string | undefined;
106
+ auth0ClientSecret?: string | undefined;
107
+ auth0Domain?: string | undefined;
108
+ createNewUser?: "N" | "Y" | undefined;
109
+ userFirstName?: string | undefined;
110
+ userLastName?: string | undefined;
111
+ userName?: string | undefined;
112
+ openAIAPIKey?: string | undefined;
113
+ anthropicAPIKey?: string | undefined;
114
+ mistralAPIKey?: string | undefined;
115
+ }>;
116
+ /**
117
+ * Verifies that the specified directories exist.
118
+ * @param {...string} dirs - The directories to check.
119
+ */
120
+ verifyDirs(...dirs: string[]): void;
121
+ /**
122
+ * Checks if there is at least `numGB` GB of free disk space.
123
+ * @param {number} numGB - The number of GB to check for.
124
+ * @returns {boolean} True if there is enough free disk space, false otherwise.
125
+ */
126
+ checkAvailableDiskSpace(numGB?: number): boolean;
127
+ /**
128
+ * Updates environment files in a given directory.
129
+ * @param {string} dirPath - The path to the directory containing environment files.
130
+ * @param {object} config - The configuration object with values to update.
131
+ */
132
+ updateEnvironmentFiles(dirPath: string, config: Record<string, unknown>): Promise<void>;
133
+ renameFolderToMJ_BASE(dbDatabase: string): void;
134
+ /**
135
+ * Updates newUserSetup in the config.json file.
136
+ * @param {string} dirPath - The path to the directory containing the config.json file.
137
+ * @param {string} userName - The new UserName to set.
138
+ * @param {string} firstName - The new FirstName to set.
139
+ * @param {string} lastName - The new LastName to set.
140
+ * @param {string} email - The new Email to set.
141
+ */
142
+ updateConfigNewUserSetup(dirPath: string, userName?: string, firstName?: string, lastName?: string, email?: string): Promise<void>;
143
+ }
144
+ export {};
@@ -0,0 +1,438 @@
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
+ };
25
+ var __importDefault = (this && this.__importDefault) || function (mod) {
26
+ return (mod && mod.__esModule) ? mod : { "default": mod };
27
+ };
28
+ Object.defineProperty(exports, "__esModule", { value: true });
29
+ const prompts_1 = require("@inquirer/prompts");
30
+ const core_1 = require("@oclif/core");
31
+ const fs = __importStar(require("fs-extra"));
32
+ const node_child_process_1 = require("node:child_process");
33
+ const node_os_1 = __importDefault(require("node:os"));
34
+ const node_path_1 = __importDefault(require("node:path"));
35
+ const zod_1 = require("zod");
36
+ // Directories are relative to execution cwd
37
+ const GENERATED_ENTITIES_DIR = 'GeneratedEntities';
38
+ const CODEGEN_DIR = 'CodeGen';
39
+ const SQL_SCRIPTS_DIR = 'SQL Scripts';
40
+ const GENERATED_DIR = 'generated';
41
+ const MJ_BASE_DIR = 'MJ_BASE';
42
+ const MJAPI_DIR = 'MJAPI';
43
+ const MJEXPLORER_DIR = 'MJExplorer';
44
+ const configSchema = zod_1.z.object({
45
+ dbUrl: zod_1.z.string().url(),
46
+ dbInstance: zod_1.z.string(),
47
+ dbTrustServerCertificate: zod_1.z.enum(['Y', 'N']),
48
+ dbDatabase: zod_1.z.string(),
49
+ dbPort: zod_1.z.number({ coerce: true }).int().positive(),
50
+ codeGenLogin: zod_1.z.string(),
51
+ codeGenPwD: zod_1.z.string(),
52
+ mjAPILogin: zod_1.z.string(),
53
+ mjAPIPwD: zod_1.z.string(),
54
+ graphQLPort: zod_1.z.number({ coerce: true }).int().positive().optional(),
55
+ authType: zod_1.z.enum(['MSAL', 'AUTH0', 'BOTH']),
56
+ msalWebClientId: zod_1.z.string().optional(),
57
+ msalTenantId: zod_1.z.string().optional(),
58
+ auth0ClientId: zod_1.z.string().optional(),
59
+ auth0ClientSecret: zod_1.z.string().optional(),
60
+ auth0Domain: zod_1.z.string().optional(),
61
+ createNewUser: zod_1.z.enum(['Y', 'N']).optional(),
62
+ userEmail: zod_1.z.string().email().or(zod_1.z.literal('')).optional().default(''),
63
+ userFirstName: zod_1.z.string().optional(),
64
+ userLastName: zod_1.z.string().optional(),
65
+ userName: zod_1.z.string().optional(),
66
+ openAIAPIKey: zod_1.z.string().optional(),
67
+ anthropicAPIKey: zod_1.z.string().optional(),
68
+ mistralAPIKey: zod_1.z.string().optional(),
69
+ });
70
+ class Install extends core_1.Command {
71
+ static description = 'Install MemberJunction';
72
+ static examples = [
73
+ `<%= config.bin %> <%= command.id %>
74
+ `,
75
+ ];
76
+ static flags = {
77
+ verbose: core_1.Flags.boolean({ char: 'v', description: 'Enable additional logging' }),
78
+ };
79
+ flags;
80
+ userConfig;
81
+ async run() {
82
+ const parsed = await this.parse(Install);
83
+ this.flags = parsed.flags;
84
+ this.checkAvailableDiskSpace(2);
85
+ this.verifyDirs(CODEGEN_DIR, GENERATED_ENTITIES_DIR, SQL_SCRIPTS_DIR, MJAPI_DIR, MJEXPLORER_DIR);
86
+ this.userConfig = await this.getUserConfiguration();
87
+ this.log('Setting up MemberJunction Distribution...');
88
+ if (this.flags.verbose) {
89
+ this.logJson({ userConfig: this.userConfig, flags: this.flags });
90
+ }
91
+ // if the user asked for a new user via our config file, need to push that info down to the CodeGen config.json file
92
+ if (this.userConfig.createNewUser === 'Y') {
93
+ this.log(' Setting up config.json...');
94
+ await this.updateConfigNewUserSetup(CODEGEN_DIR, this.userConfig.userName, this.userConfig.userFirstName, this.userConfig.userLastName, this.userConfig.userEmail);
95
+ }
96
+ //*******************************************************************
97
+ // Process GeneratedEntities
98
+ //*******************************************************************
99
+ this.log('\nBootstrapping GeneratedEntities...');
100
+ this.log('Running npm install...');
101
+ (0, node_child_process_1.execSync)('npm install', { stdio: 'inherit', cwd: GENERATED_ENTITIES_DIR });
102
+ //*******************************************************************
103
+ // Process CodeGen
104
+ //*******************************************************************
105
+ this.log('\nProcessing CodeGen...');
106
+ this.log(' Updating ');
107
+ this.log(' Setting up .env and config.json...');
108
+ const codeGenENV = `#Database Setup
109
+ DB_HOST='${this.userConfig.dbUrl}'
110
+ DB_PORT=${this.userConfig.dbPort}
111
+ DB_USERNAME='${this.userConfig.codeGenLogin}'
112
+ DB_PASSWORD='${this.userConfig.codeGenPwD}'
113
+ DB_DATABASE='${this.userConfig.dbDatabase}'
114
+ ${this.userConfig.dbInstance ? "DB_INSTANCE_NAME='" + this.userConfig.dbInstance + "'" : ''}
115
+ ${this.userConfig.dbTrustServerCertificate === 'Y' ? 'DB_TRUST_SERVER_CERTIFICATE=1' : ''}
116
+
117
+ #OUTPUT CODE is used for output directories like SQL Scripts
118
+ OUTPUT_CODE='${this.userConfig.dbDatabase}'
119
+
120
+ # Name of the schema that MJ has been setup in. This defaults to __mj
121
+ MJ_CORE_SCHEMA='__mj'
122
+
123
+ # If using Advanced Generation, populate this with the API key for the AI vendor you are using
124
+ # Also, you need to configure the settings under advancedGeneration in the config.json file, including choosing the vendor.
125
+ AI_VENDOR_API_KEY__OpenAILLM='${this.userConfig.openAIAPIKey}'
126
+ AI_VENDOR_API_KEY__MistralLLM='${this.userConfig.mistralAPIKey}'
127
+ AI_VENDOR_API_KEY__AnthropicLLM='${this.userConfig.anthropicAPIKey}'
128
+
129
+ #CONFIG_FILE is the name of the file that has the configuration parameters for CodeGen
130
+ CONFIG_FILE='config.json'
131
+ `;
132
+ fs.writeFileSync(node_path_1.default.join(CODEGEN_DIR, '.env'), codeGenENV);
133
+ this.log(' Running npm install...');
134
+ (0, node_child_process_1.execSync)('npm install', { stdio: 'inherit', cwd: CODEGEN_DIR });
135
+ this.log(' Running npm link for GeneratedEntities...');
136
+ (0, node_child_process_1.execSync)('npm link ../GeneratedEntities', { stdio: 'inherit', cwd: CODEGEN_DIR });
137
+ //*******************************************************************
138
+ // Process MJAPI
139
+ //*******************************************************************
140
+ this.log('\n\nBootstrapping MJAPI...');
141
+ this.log(' Running npm install...');
142
+ (0, node_child_process_1.execSync)('npm install', { stdio: 'inherit', cwd: MJAPI_DIR });
143
+ this.log(' Setting up MJAPI .env file...');
144
+ const mjAPIENV = `#Database Setup
145
+ DB_HOST='${this.userConfig.dbUrl}'
146
+ DB_PORT=${this.userConfig.dbPort}
147
+ DB_USERNAME='${this.userConfig.mjAPILogin}'
148
+ DB_PASSWORD='${this.userConfig.mjAPIPwD}'
149
+ DB_DATABASE='${this.userConfig.dbDatabase}'
150
+ ${this.userConfig.dbInstance ? "DB_INSTANCE_NAME='" + this.userConfig.dbInstance + "'" : ''}
151
+ ${this.userConfig.dbTrustServerCertificate === 'Y' ? 'DB_TRUST_SERVER_CERTIFICATE=1' : ''}
152
+
153
+ PORT=${this.userConfig.graphQLPort}
154
+
155
+ UPDATE_USER_CACHE_WHEN_NOT_FOUND=1
156
+ UPDATE_USER_CACHE_WHEN_NOT_FOUND_DELAY=5000
157
+
158
+ # AUTHENTICATION SECTION - you can use MSAL or Auth0 or both for authentication services for MJAPI
159
+ # MSAL Section
160
+ WEB_CLIENT_ID=${this.userConfig.msalWebClientId}
161
+ TENANT_ID=${this.userConfig.msalTenantId}
162
+
163
+ # Auth0 Section
164
+ AUTH0_CLIENT_ID=${this.userConfig.auth0ClientId}
165
+ AUTH0_CLIENT_SECRET=${this.userConfig.auth0ClientSecret}
166
+ AUTH0_DOMAIN=${this.userConfig.auth0Domain}
167
+
168
+ # Name of the schema that MJ has been setup in. This defaults to __mj
169
+ MJ_CORE_SCHEMA='__mj'
170
+
171
+ # If you are using MJAI library, provide your API KEYS here for the various services
172
+ # Format is AI_VENDOR_API_KEY__<DriverClass> Where DriverClass is the DriverClass field from the AI Models Entity in MemberJunction
173
+ AI_VENDOR_API_KEY__OpenAILLM = '${this.userConfig.openAIAPIKey}'
174
+ AI_VENDOR_API_KEY__AnthropicLLM = '${this.userConfig.anthropicAPIKey}'
175
+ AI_VENDOR_API_KEY__MistralLLM = '${this.userConfig.mistralAPIKey}'
176
+
177
+ # Skip API URL, KEY and Org ID
178
+ # YOU MUST ENTER IN THE CORRECT URL and ORG ID for your Skip API USE BELOW
179
+ ASK_SKIP_API_URL = 'http://localhost:8000'
180
+ ASK_SKIP_ORGANIZATION_ID = 1
181
+
182
+ CONFIG_FILE='config.json'
183
+ `;
184
+ fs.writeFileSync(node_path_1.default.join(MJAPI_DIR, '.env'), mjAPIENV);
185
+ this.log(' Running npm link for GeneratedEntities...');
186
+ (0, node_child_process_1.execSync)('npm link ../GeneratedEntities', { stdio: 'inherit', cwd: MJAPI_DIR });
187
+ this.log('Running CodeGen...');
188
+ this.renameFolderToMJ_BASE(this.userConfig.dbDatabase);
189
+ // next, run CodeGen
190
+ // We do not manually run the compilation for GeneratedEntities because CodeGen handles that, but notice above that we did npm install for GeneratedEntities otherwise when CodeGen attempts to compile it, it will fail.
191
+ (0, node_child_process_1.execSync)('npx tsx src/index.ts', { stdio: 'inherit', cwd: CODEGEN_DIR });
192
+ // Process MJExplorer
193
+ this.log('\nProcessing MJExplorer...');
194
+ this.log('\n Updating environment files...');
195
+ const config = {
196
+ CLIENT_ID: this.userConfig.msalWebClientId,
197
+ TENANT_ID: this.userConfig.msalTenantId,
198
+ CLIENT_AUTHORITY: this.userConfig.msalTenantId ? `https://login.microsoftonline.com/${this.userConfig.msalTenantId}` : '',
199
+ AUTH0_DOMAIN: this.userConfig.auth0Domain,
200
+ AUTH0_CLIENTID: this.userConfig.auth0ClientId,
201
+ };
202
+ await this.updateEnvironmentFiles(MJEXPLORER_DIR, config);
203
+ // keep on going with MJ Explorer - do the rest of the stuff
204
+ this.log(' Running npm install...');
205
+ (0, node_child_process_1.execSync)('npm install', { stdio: 'inherit', cwd: MJEXPLORER_DIR });
206
+ this.log(' Running npm link for GeneratedEntities...');
207
+ (0, node_child_process_1.execSync)('npm link ../GeneratedEntities', { stdio: 'inherit', cwd: MJEXPLORER_DIR });
208
+ this.log('Installation complete!');
209
+ }
210
+ async getUserConfiguration() {
211
+ let userConfig;
212
+ try {
213
+ const configObject = await fs.readJSON('install.config.json');
214
+ userConfig = configSchema.parse(configObject);
215
+ }
216
+ catch (e) {
217
+ if (e instanceof zod_1.ZodError) {
218
+ this.log(`Invalid config file found at '${node_path_1.default.join(fs.realpathSync('.'), 'install.config.json')}'${this.flags.verbose ? '' : ', retry with --verbose for details'}`);
219
+ if (this.flags.verbose) {
220
+ console.table(e.issues);
221
+ }
222
+ }
223
+ else {
224
+ this.log(`No config file found at '${node_path_1.default.join(fs.realpathSync('.'), 'install.config.json')}'`);
225
+ }
226
+ }
227
+ if (!userConfig) {
228
+ this.log('\n>>> Please answer the following questions to setup the .env files for CodeGen. After this process you can manually edit the .env file in CodeGen as desired.');
229
+ const dbUrl = await (0, prompts_1.input)({ message: 'Enter the database server URL:' });
230
+ const dbInstance = await (0, prompts_1.input)({
231
+ message: 'If you are using a named instance on that server, if so, enter the name here, if not leave blank:',
232
+ });
233
+ const dbTrustServerCertificate = (await (0, prompts_1.confirm)({
234
+ message: 'Does the database server use a self-signed certificate? If you are using a local instance, enter Y:',
235
+ }))
236
+ ? 'Y'
237
+ : 'N';
238
+ const dbDatabase = await (0, prompts_1.input)({ message: 'Enter the database name on that server:' });
239
+ const dbPort = await (0, prompts_1.input)({
240
+ message: 'Enter the port the database server listens on',
241
+ validate: (v) => configSchema.shape.dbPort.safeParse(v).success,
242
+ default: '1433',
243
+ });
244
+ const codeGenLogin = await (0, prompts_1.input)({ message: 'Enter the database login for CodeGen:' });
245
+ const codeGenPwD = await (0, prompts_1.input)({ message: 'Enter the database password for CodeGen:' });
246
+ this.log('\n>>> Please answer the following questions to setup the .env files for MJAPI. After this process you can manually edit the .env file in CodeGen as desired.');
247
+ const mjAPILogin = await (0, prompts_1.input)({ message: 'Enter the database login for MJAPI:' });
248
+ const mjAPIPwD = await (0, prompts_1.input)({ message: 'Enter the database password for MJAPI:' });
249
+ const graphQLPort = await (0, prompts_1.input)({
250
+ message: 'Enter the port to use for the GraphQL API',
251
+ validate: (v) => configSchema.shape.graphQLPort.safeParse(v).success,
252
+ default: '4000',
253
+ });
254
+ const authType = await (0, prompts_1.select)({
255
+ message: 'Will you be using Microsoft Entra (formerly Azure AD), Auth0, or both for authentication services for MJAPI:',
256
+ choices: [
257
+ { name: 'Microsoft Entra', value: 'MSAL' },
258
+ { name: 'Auth0', value: 'AUTH0' },
259
+ { name: 'Both', value: 'BOTH' },
260
+ ],
261
+ });
262
+ const msalTenantId = ['BOTH', 'MSAL'].includes(authType) ? await (0, prompts_1.input)({ message: 'Enter the web client ID for Entra:' }) : '';
263
+ const msalWebClientId = ['BOTH', 'MSAL'].includes(authType) ? await (0, prompts_1.input)({ message: 'Enter the tenant ID for Entra:' }) : '';
264
+ const auth0ClientId = ['BOTH', 'AUTH0'].includes(authType) ? await (0, prompts_1.input)({ message: 'Enter the client ID for Auth0:' }) : '';
265
+ const auth0ClientSecret = ['BOTH', 'AUTH0'].includes(authType) ? await (0, prompts_1.input)({ message: 'Enter the client secret for Auth0:' }) : '';
266
+ const auth0Domain = ['BOTH', 'AUTH0'].includes(authType) ? await (0, prompts_1.input)({ message: 'Enter the domain for Auth0:' }) : '';
267
+ const createNewUser = await (0, prompts_1.confirm)({ message: 'Do you want to create a new user in the database? (Y/N):' });
268
+ const userEmail = createNewUser
269
+ ? await (0, prompts_1.input)({
270
+ message: 'Enter the new user email',
271
+ validate: (v) => configSchema.shape.userEmail.safeParse(v).success,
272
+ })
273
+ : '';
274
+ const userFirstName = createNewUser ? await (0, prompts_1.input)({ message: 'Enter the new user first name:' }) : '';
275
+ const userLastName = createNewUser ? await (0, prompts_1.input)({ message: 'Enter the new user last name::' }) : '';
276
+ const userName = createNewUser
277
+ ? await (0, prompts_1.input)({ message: 'Enter the new user name (leave blank to use email):', default: userEmail })
278
+ : '';
279
+ const openAIAPIKey = await (0, prompts_1.input)({ message: 'Enter the OpenAI API Key (leave blank if not using):' });
280
+ const anthropicAPIKey = await (0, prompts_1.input)({ message: 'Enter the Anthropic API Key (leave blank if not using):' });
281
+ const mistralAPIKey = await (0, prompts_1.input)({ message: 'Enter the Mistral API Key (leave blank if not using):' });
282
+ userConfig = configSchema.parse({
283
+ dbUrl,
284
+ dbInstance,
285
+ dbTrustServerCertificate,
286
+ dbDatabase,
287
+ dbPort,
288
+ codeGenLogin,
289
+ codeGenPwD,
290
+ mjAPILogin,
291
+ mjAPIPwD,
292
+ graphQLPort,
293
+ authType,
294
+ msalWebClientId,
295
+ msalTenantId,
296
+ auth0ClientId,
297
+ auth0ClientSecret,
298
+ auth0Domain,
299
+ createNewUser: createNewUser ? 'Y' : 'N',
300
+ userEmail,
301
+ userFirstName,
302
+ userLastName,
303
+ userName,
304
+ openAIAPIKey,
305
+ anthropicAPIKey,
306
+ mistralAPIKey,
307
+ });
308
+ }
309
+ return userConfig;
310
+ }
311
+ /**
312
+ * Verifies that the specified directories exist.
313
+ * @param {...string} dirs - The directories to check.
314
+ */
315
+ verifyDirs(...dirs) {
316
+ dirs.forEach((dir) => {
317
+ if (!fs.existsSync(dir)) {
318
+ this.error(`Unable to locate required package at '${node_path_1.default.join(fs.realpathSync('.'), dir)}'`, {
319
+ exit: 1,
320
+ suggestions: ['Run the install from the same directory as the extracted MemberJunction distribution'],
321
+ });
322
+ }
323
+ });
324
+ }
325
+ /**
326
+ * Checks if there is at least `numGB` GB of free disk space.
327
+ * @param {number} numGB - The number of GB to check for.
328
+ * @returns {boolean} True if there is enough free disk space, false otherwise.
329
+ */
330
+ checkAvailableDiskSpace(numGB = 2) {
331
+ try {
332
+ this.log(`Checking for at least ${numGB}GB of free disk space...`);
333
+ // Define numGB GB in bytes
334
+ const GBToBytes = 1024 * 1024 * 1024;
335
+ const requiredSpace = numGB * GBToBytes;
336
+ let freeSpace;
337
+ if (node_os_1.default.platform() === 'win32') {
338
+ // For Windows, check the C: drive
339
+ const command = `wmic LogicalDisk where DeviceID="C:" get FreeSpace`;
340
+ const output = (0, node_child_process_1.execSync)(command).toString();
341
+ const lines = output.trim().split('\n');
342
+ freeSpace = parseInt(lines[1].trim());
343
+ }
344
+ else {
345
+ // For POSIX systems, check the root directory
346
+ const command = `df -k / | tail -1 | awk '{ print $4; }'`;
347
+ freeSpace = parseInt((0, node_child_process_1.execSync)(command).toString().trim()) * 1024;
348
+ }
349
+ if (freeSpace >= requiredSpace) {
350
+ this.log(` Sufficient disk space available: ${Math.round(freeSpace / GBToBytes)} GB`);
351
+ return true;
352
+ }
353
+ else {
354
+ this.error(`Insufficient disk space. Required: ${requiredSpace} bytes, Available: ${Math.round(freeSpace / GBToBytes)} GB`, {
355
+ exit: 1,
356
+ });
357
+ }
358
+ }
359
+ catch (error) {
360
+ this.logToStderr(this.toErrorJson(error));
361
+ this.error('Error checking disk space', { exit: 1 });
362
+ }
363
+ }
364
+ /**
365
+ * Updates environment files in a given directory.
366
+ * @param {string} dirPath - The path to the directory containing environment files.
367
+ * @param {object} config - The configuration object with values to update.
368
+ */
369
+ async updateEnvironmentFiles(dirPath, config) {
370
+ try {
371
+ // Define the pattern for environment files.
372
+ const envFilePattern = /environment.*\.ts$/;
373
+ // Read all files in the directory.
374
+ const files = await fs.readdir(dirPath);
375
+ // Filter for environment files.
376
+ const envFiles = files.filter((file) => envFilePattern.test(file));
377
+ // Update each environment file.
378
+ for (const file of envFiles) {
379
+ const filePath = node_path_1.default.join(dirPath, file);
380
+ const data = await fs.readFile(filePath, 'utf8');
381
+ // Replace the values in the file.
382
+ let updatedData = data;
383
+ Object.keys(config).forEach((key) => {
384
+ const regex = new RegExp(`${key}:\\s*'.*?'`, 'g');
385
+ updatedData = updatedData.replace(regex, `${key}: '${config[key]}'`);
386
+ });
387
+ // Write the updated data back to the file.
388
+ await fs.writeFile(filePath, updatedData, 'utf8');
389
+ this.log(`Updated ${file}`);
390
+ }
391
+ }
392
+ catch (err) {
393
+ console.error('Error:', err);
394
+ }
395
+ }
396
+ renameFolderToMJ_BASE(dbDatabase) {
397
+ // rename the MJ_BASE set of SQL Scripts to our new dbname
398
+ const oldFolderPath = node_path_1.default.join(SQL_SCRIPTS_DIR, GENERATED_DIR, MJ_BASE_DIR);
399
+ const newFolderPath = node_path_1.default.join(SQL_SCRIPTS_DIR, GENERATED_DIR, dbDatabase); // Assuming dbDatabase holds the new name
400
+ try {
401
+ fs.moveSync(oldFolderPath, newFolderPath);
402
+ this.log(`Renamed ${oldFolderPath} to ${newFolderPath} successfully.`);
403
+ }
404
+ catch (err) {
405
+ this.logToStderr(`An error occurred while renaming the '${oldFolderPath}' folder:`, err);
406
+ }
407
+ }
408
+ /**
409
+ * Updates newUserSetup in the config.json file.
410
+ * @param {string} dirPath - The path to the directory containing the config.json file.
411
+ * @param {string} userName - The new UserName to set.
412
+ * @param {string} firstName - The new FirstName to set.
413
+ * @param {string} lastName - The new LastName to set.
414
+ * @param {string} email - The new Email to set.
415
+ */
416
+ async updateConfigNewUserSetup(dirPath, userName, firstName, lastName, email) {
417
+ try {
418
+ const configFilePath = node_path_1.default.join(dirPath, 'config.json');
419
+ // Read the config.json file
420
+ const data = await fs.readFile(configFilePath, 'utf8');
421
+ const config = JSON.parse(data);
422
+ // Update the newUserSetup object
423
+ if (!config.newUserSetup)
424
+ config.newUserSetup = {};
425
+ config.newUserSetup.UserName = userName;
426
+ config.newUserSetup.FirstName = firstName;
427
+ config.newUserSetup.LastName = lastName;
428
+ config.newUserSetup.Email = email;
429
+ // Write the updated configuration back to config.json
430
+ await fs.writeFile(configFilePath, JSON.stringify(config, null, 2), 'utf8');
431
+ this.log(` Updated config.json in ${dirPath}`);
432
+ }
433
+ catch (err) {
434
+ this.logToStderr('Error:', err);
435
+ }
436
+ }
437
+ }
438
+ exports.default = Install;
@@ -0,0 +1 @@
1
+ export { run } from '@oclif/core';
package/dist/index.js ADDED
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.run = void 0;
4
+ var core_1 = require("@oclif/core");
5
+ Object.defineProperty(exports, "run", { enumerable: true, get: function () { return core_1.run; } });
@@ -0,0 +1,37 @@
1
+ {
2
+ "commands": {
3
+ "install": {
4
+ "aliases": [],
5
+ "args": {},
6
+ "description": "Install MemberJunction",
7
+ "examples": [
8
+ "<%= config.bin %> <%= command.id %>\n"
9
+ ],
10
+ "flags": {
11
+ "verbose": {
12
+ "char": "v",
13
+ "description": "Enable additional logging",
14
+ "name": "verbose",
15
+ "allowNo": false,
16
+ "type": "boolean"
17
+ }
18
+ },
19
+ "hasDynamicHelp": false,
20
+ "hiddenAliases": [],
21
+ "id": "install",
22
+ "pluginAlias": "@memberjunction/cli",
23
+ "pluginName": "@memberjunction/cli",
24
+ "pluginType": "core",
25
+ "strict": true,
26
+ "enableJsonFlag": false,
27
+ "isESM": false,
28
+ "relativePath": [
29
+ "dist",
30
+ "commands",
31
+ "install",
32
+ "index.js"
33
+ ]
34
+ }
35
+ },
36
+ "version": "1.0.11"
37
+ }
package/package.json ADDED
@@ -0,0 +1,67 @@
1
+ {
2
+ "name": "@memberjunction/cli",
3
+ "version": "1.0.11",
4
+ "description": "MemberJunction command line tools",
5
+ "keywords": [
6
+ "oclif"
7
+ ],
8
+ "homepage": "https://github.com/MemberJunction/MJ",
9
+ "bugs": {
10
+ "url": "https://github.com/MemberJunction/MJ/issues"
11
+ },
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "git+https://github.com/MemberJunction/MJ.git"
15
+ },
16
+ "license": "ISC",
17
+ "author": "MemberJunction",
18
+ "main": "dist/index.js",
19
+ "types": "dist/index.d.ts",
20
+ "bin": {
21
+ "mj": "bin/run.js"
22
+ },
23
+ "files": [
24
+ "/bin",
25
+ "/dist",
26
+ "/oclif.manifest.json"
27
+ ],
28
+ "scripts": {
29
+ "build": "rimraf dist && tsc -b",
30
+ "prepack": "npm run build && oclif manifest && oclif readme",
31
+ "postpack": "rimraf oclif.manifest.json",
32
+ "version": "oclif readme && git add README.md"
33
+ },
34
+ "oclif": {
35
+ "bin": "mj",
36
+ "commands": "./dist/commands",
37
+ "dirname": "mj",
38
+ "plugins": [
39
+ "@oclif/plugin-help",
40
+ "@oclif/plugin-warn-if-update-available",
41
+ "@oclif/plugin-version"
42
+ ],
43
+ "warn-if-update-available": {
44
+ "timeoutInDays": 1
45
+ }
46
+ },
47
+ "dependencies": {
48
+ "@inquirer/prompts": "^5.0.1",
49
+ "@oclif/core": "^3",
50
+ "@oclif/plugin-help": "^6",
51
+ "@oclif/plugin-version": "^2.0.17",
52
+ "@oclif/plugin-warn-if-update-available": "^3.0.16",
53
+ "fs-extra": "^11.2.0",
54
+ "zod": "^3.23.4"
55
+ },
56
+ "devDependencies": {
57
+ "@oclif/prettier-config": "^0.2.1",
58
+ "@types/fs-extra": "^11.0.4",
59
+ "oclif": "^4",
60
+ "rimraf": "^5.0.5",
61
+ "ts-node": "^10.9.2",
62
+ "typescript": "^5"
63
+ },
64
+ "engines": {
65
+ "node": ">=20.0.0"
66
+ }
67
+ }