@quonfig/cli 0.0.1
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/LICENSE +21 -0
- package/README.md +707 -0
- package/bin/dev.cmd +3 -0
- package/bin/dev.js +9 -0
- package/bin/run.cmd +3 -0
- package/bin/run.js +8 -0
- package/dist/api/get-environments.d.ts +10 -0
- package/dist/api/get-environments.js +11 -0
- package/dist/codegen/code-generators/base-generator.d.ts +11 -0
- package/dist/codegen/code-generators/base-generator.js +8 -0
- package/dist/codegen/code-generators/base-typescript-generator.d.ts +17 -0
- package/dist/codegen/code-generators/base-typescript-generator.js +35 -0
- package/dist/codegen/code-generators/node-typescript-generator.d.ts +8 -0
- package/dist/codegen/code-generators/node-typescript-generator.js +96 -0
- package/dist/codegen/code-generators/react-typescript-generator.d.ts +11 -0
- package/dist/codegen/code-generators/react-typescript-generator.js +116 -0
- package/dist/codegen/config-downloader.d.ts +7 -0
- package/dist/codegen/config-downloader.js +26 -0
- package/dist/codegen/json-schema-to-zod.d.ts +3 -0
- package/dist/codegen/json-schema-to-zod.js +170 -0
- package/dist/codegen/language-mappers/json-to-zod-mapper.d.ts +4 -0
- package/dist/codegen/language-mappers/json-to-zod-mapper.js +55 -0
- package/dist/codegen/language-mappers/zod-base-mapper.d.ts +35 -0
- package/dist/codegen/language-mappers/zod-base-mapper.js +111 -0
- package/dist/codegen/language-mappers/zod-to-string-mapper.d.ts +25 -0
- package/dist/codegen/language-mappers/zod-to-string-mapper.js +87 -0
- package/dist/codegen/language-mappers/zod-to-typescript-mapper.d.ts +35 -0
- package/dist/codegen/language-mappers/zod-to-typescript-mapper.js +133 -0
- package/dist/codegen/language-mappers/zod-to-typescript-return-value-mapper.d.ts +36 -0
- package/dist/codegen/language-mappers/zod-to-typescript-return-value-mapper.js +150 -0
- package/dist/codegen/mustache-extractor.d.ts +6 -0
- package/dist/codegen/mustache-extractor.js +83 -0
- package/dist/codegen/schema-evaluator.d.ts +37 -0
- package/dist/codegen/schema-evaluator.js +283 -0
- package/dist/codegen/schema-extractor.d.ts +24 -0
- package/dist/codegen/schema-extractor.js +265 -0
- package/dist/codegen/types.d.ts +44 -0
- package/dist/codegen/types.js +5 -0
- package/dist/codegen/zod-introspection.d.ts +85 -0
- package/dist/codegen/zod-introspection.js +205 -0
- package/dist/commands/create.d.ts +19 -0
- package/dist/commands/create.js +175 -0
- package/dist/commands/download.d.ts +13 -0
- package/dist/commands/download.js +63 -0
- package/dist/commands/generate-new-hex-key.d.ts +7 -0
- package/dist/commands/generate-new-hex-key.js +10 -0
- package/dist/commands/generate.d.ts +16 -0
- package/dist/commands/generate.js +220 -0
- package/dist/commands/get.d.ts +13 -0
- package/dist/commands/get.js +141 -0
- package/dist/commands/info.d.ts +21 -0
- package/dist/commands/info.js +464 -0
- package/dist/commands/interactive.d.ts +5 -0
- package/dist/commands/interactive.js +8 -0
- package/dist/commands/list.d.ts +13 -0
- package/dist/commands/list.js +61 -0
- package/dist/commands/login.d.ts +10 -0
- package/dist/commands/login.js +124 -0
- package/dist/commands/logout.d.ts +7 -0
- package/dist/commands/logout.js +12 -0
- package/dist/commands/mcp.d.ts +13 -0
- package/dist/commands/mcp.js +145 -0
- package/dist/commands/override.d.ts +17 -0
- package/dist/commands/override.js +170 -0
- package/dist/commands/profile.d.ts +7 -0
- package/dist/commands/profile.js +47 -0
- package/dist/commands/schema.d.ts +21 -0
- package/dist/commands/schema.js +168 -0
- package/dist/commands/serve.d.ts +13 -0
- package/dist/commands/serve.js +99 -0
- package/dist/commands/set-default.d.ts +20 -0
- package/dist/commands/set-default.js +273 -0
- package/dist/commands/verify.d.ts +12 -0
- package/dist/commands/verify.js +48 -0
- package/dist/commands/whoami.d.ts +7 -0
- package/dist/commands/whoami.js +52 -0
- package/dist/commands/workspace.d.ts +7 -0
- package/dist/commands/workspace.js +38 -0
- package/dist/constants.d.ts +2 -0
- package/dist/constants.js +2 -0
- package/dist/help.d.ts +4 -0
- package/dist/help.js +10 -0
- package/dist/index.d.ts +40 -0
- package/dist/index.js +112 -0
- package/dist/interactive-prompt.d.ts +2 -0
- package/dist/interactive-prompt.js +162 -0
- package/dist/quonfig.d.ts +15 -0
- package/dist/quonfig.js +75 -0
- package/dist/result.d.ts +35 -0
- package/dist/result.js +9 -0
- package/dist/services/mcp.d.ts +15 -0
- package/dist/services/mcp.js +59 -0
- package/dist/ui/get-confirmation.d.ts +10 -0
- package/dist/ui/get-confirmation.js +4 -0
- package/dist/ui/get-environment.d.ts +11 -0
- package/dist/ui/get-environment.js +42 -0
- package/dist/ui/get-key.d.ts +27 -0
- package/dist/ui/get-key.js +15 -0
- package/dist/ui/get-string.d.ts +5 -0
- package/dist/ui/get-string.js +8 -0
- package/dist/ui/get-value.d.ts +18 -0
- package/dist/ui/get-value.js +46 -0
- package/dist/util/autocomplete.d.ts +6 -0
- package/dist/util/autocomplete.js +25 -0
- package/dist/util/coerce.d.ts +7 -0
- package/dist/util/coerce.js +62 -0
- package/dist/util/color.d.ts +3 -0
- package/dist/util/color.js +4 -0
- package/dist/util/config-value-dto.d.ts +16 -0
- package/dist/util/config-value-dto.js +82 -0
- package/dist/util/context.d.ts +18 -0
- package/dist/util/context.js +10 -0
- package/dist/util/domain-urls.d.ts +3 -0
- package/dist/util/domain-urls.js +18 -0
- package/dist/util/encryption.d.ts +20 -0
- package/dist/util/encryption.js +153 -0
- package/dist/util/file-manager.d.ts +12 -0
- package/dist/util/file-manager.js +16 -0
- package/dist/util/get-client.d.ts +7 -0
- package/dist/util/get-client.js +127 -0
- package/dist/util/is-interactive.d.ts +5 -0
- package/dist/util/is-interactive.js +7 -0
- package/dist/util/json-maybe.d.ts +3 -0
- package/dist/util/json-maybe.js +14 -0
- package/dist/util/name-arg.d.ts +4 -0
- package/dist/util/name-arg.js +3 -0
- package/dist/util/oauth-client.d.ts +30 -0
- package/dist/util/oauth-client.js +99 -0
- package/dist/util/secret-flags.d.ts +13 -0
- package/dist/util/secret-flags.js +13 -0
- package/dist/util/token-storage.d.ts +25 -0
- package/dist/util/token-storage.js +90 -0
- package/dist/validations/value.d.ts +4 -0
- package/dist/validations/value.js +11 -0
- package/dist/verify/standalone.d.ts +14 -0
- package/dist/verify/standalone.js +133 -0
- package/dist/verify/validate.d.ts +43 -0
- package/dist/verify/validate.js +756 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.js +2 -0
- package/oclif.manifest.json +1429 -0
- package/package.json +104 -0
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import { Flags } from '@oclif/core';
|
|
2
|
+
import * as fs from 'node:fs';
|
|
3
|
+
import * as path from 'node:path';
|
|
4
|
+
import { z } from 'zod';
|
|
5
|
+
import { NodeTypeScriptGenerator } from '../codegen/code-generators/node-typescript-generator.js';
|
|
6
|
+
import { ReactTypeScriptGenerator } from '../codegen/code-generators/react-typescript-generator.js';
|
|
7
|
+
import { ConfigDownloader } from '../codegen/config-downloader.js';
|
|
8
|
+
import { SupportedLanguage } from '../codegen/types.js';
|
|
9
|
+
import { APICommand } from '../index.js';
|
|
10
|
+
import { createFileManager } from '../util/file-manager.js';
|
|
11
|
+
// base types
|
|
12
|
+
const nodeOrReactLanguageSpecificSchema = z.object({
|
|
13
|
+
clientFileName: z.string().optional(),
|
|
14
|
+
declarationFileName: z.string().optional(),
|
|
15
|
+
outputDirectory: z.string().optional(),
|
|
16
|
+
});
|
|
17
|
+
/**
|
|
18
|
+
* Types for parsed config schema
|
|
19
|
+
*
|
|
20
|
+
* {
|
|
21
|
+
* <react|node>: {
|
|
22
|
+
* clientFileName: string;
|
|
23
|
+
* declarationFileName: string;
|
|
24
|
+
* outputDirectory: string;
|
|
25
|
+
* },
|
|
26
|
+
* }
|
|
27
|
+
*/
|
|
28
|
+
const requiredNodeOrReactLanguageSpecificSchema = nodeOrReactLanguageSpecificSchema.required();
|
|
29
|
+
const allLanguageEntries = {
|
|
30
|
+
[SupportedLanguage.Node]: requiredNodeOrReactLanguageSpecificSchema,
|
|
31
|
+
[SupportedLanguage.React]: requiredNodeOrReactLanguageSpecificSchema,
|
|
32
|
+
};
|
|
33
|
+
const parsedConfigSchema = z.object(allLanguageEntries);
|
|
34
|
+
/**
|
|
35
|
+
* Types for input config schema
|
|
36
|
+
*
|
|
37
|
+
* {
|
|
38
|
+
* outputDiretory?: string;
|
|
39
|
+
* targets?: {
|
|
40
|
+
* <supported language>?: {
|
|
41
|
+
* outputDirectory: string;
|
|
42
|
+
* outputFileName: string;
|
|
43
|
+
* },
|
|
44
|
+
* },
|
|
45
|
+
* }
|
|
46
|
+
*/
|
|
47
|
+
const allTargetsSchema = z.object({
|
|
48
|
+
[SupportedLanguage.Node]: nodeOrReactLanguageSpecificSchema.optional(),
|
|
49
|
+
[SupportedLanguage.React]: nodeOrReactLanguageSpecificSchema.optional(),
|
|
50
|
+
});
|
|
51
|
+
const inputConfigSchema = z.object({
|
|
52
|
+
outputDirectory: z.string().optional(),
|
|
53
|
+
targets: allTargetsSchema.optional(),
|
|
54
|
+
});
|
|
55
|
+
const CONFIG_NAME = 'quonfig.config.json';
|
|
56
|
+
const DEFAULT_CONFIG = {
|
|
57
|
+
outputDirectory: 'generated',
|
|
58
|
+
targets: {
|
|
59
|
+
'node-ts': {
|
|
60
|
+
clientFileName: 'quonfig-server.ts',
|
|
61
|
+
declarationFileName: 'quonfig-server-types.d.ts',
|
|
62
|
+
},
|
|
63
|
+
'react-ts': {
|
|
64
|
+
clientFileName: 'quonfig-client.ts',
|
|
65
|
+
declarationFileName: 'quonfig-client-types.d.ts',
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
export default class Generate extends APICommand {
|
|
70
|
+
/* eslint-disable no-irregular-whitespace */
|
|
71
|
+
static description = `You can use the default type-generation configuration, or by provide your own via a quonfig.config.json file:
|
|
72
|
+
|
|
73
|
+
Format:
|
|
74
|
+
{
|
|
75
|
+
outputDirectory?: string;
|
|
76
|
+
targets?: {
|
|
77
|
+
<language key>?: {
|
|
78
|
+
outputDirectory?: string;
|
|
79
|
+
outputFileName?: string;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
Example quonfig.config.json:
|
|
85
|
+
\`\`\`json
|
|
86
|
+
{
|
|
87
|
+
"outputDirectory": "path/to/your/directory",
|
|
88
|
+
"targets": {
|
|
89
|
+
"react-ts": {
|
|
90
|
+
"outputDirectory": "diff/path/to/your/directory",
|
|
91
|
+
"declarationFileName": "quonfig-client-types.d.ts",
|
|
92
|
+
"clientFileName": "quonfig-client.ts",
|
|
93
|
+
},
|
|
94
|
+
"node-ts": {
|
|
95
|
+
"declarationFileName": "quonfig-server-types.d.ts",
|
|
96
|
+
"clientFileName": "quonfig-server.ts",
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
\`\`\`
|
|
101
|
+
`;
|
|
102
|
+
/* eslint-enable no-irregular-whitespace */
|
|
103
|
+
static examples = [
|
|
104
|
+
'<%= config.bin %> <%= command.id %> # react-ts only by default',
|
|
105
|
+
'<%= config.bin %> <%= command.id %> --targets node-ts # node-ts only',
|
|
106
|
+
'<%= config.bin %> <%= command.id %> --targets react-ts,node-ts # both node + react-ts',
|
|
107
|
+
'<%= config.bin %> <%= command.id %> -o ./src/generated # specify output directory',
|
|
108
|
+
'<%= config.bin %> <%= command.id %> --targets node-ts -o ./dist # combine with targets',
|
|
109
|
+
];
|
|
110
|
+
static flags = {
|
|
111
|
+
'output-directory': Flags.string({
|
|
112
|
+
char: 'o',
|
|
113
|
+
description: 'Override the output directory for generated files',
|
|
114
|
+
}),
|
|
115
|
+
targets: Flags.string({
|
|
116
|
+
default: SupportedLanguage.React,
|
|
117
|
+
description: `Determines for language/framework to generate code for (${Object.values(SupportedLanguage).join(', ')})`,
|
|
118
|
+
}),
|
|
119
|
+
};
|
|
120
|
+
static summary = 'Generate type definitions for your Quonfig configuration';
|
|
121
|
+
async run() {
|
|
122
|
+
const { flags } = await this.parse(Generate);
|
|
123
|
+
this.verboseLog('=== GENERATE COMMAND START ===');
|
|
124
|
+
this.verboseLog(`Base API URL: ${process.env.QUONFIG_API_URL || 'Default'}`);
|
|
125
|
+
try {
|
|
126
|
+
// Look for and read local quonfig.config.json file
|
|
127
|
+
const localConfig = await this.readLocalConfig(flags['output-directory']);
|
|
128
|
+
// Use tarets flag override, otherwise fall back to local config
|
|
129
|
+
const targets = flags.targets.split(',') || Object.keys(localConfig);
|
|
130
|
+
this.verboseLog(`Language(s): ${targets.join(', ')}`);
|
|
131
|
+
// Download the configuration using the APICommand's client
|
|
132
|
+
const downloader = new ConfigDownloader(this);
|
|
133
|
+
this.verboseLog('Downloading config...');
|
|
134
|
+
const configFile = await downloader.downloadConfig();
|
|
135
|
+
this.verboseLog('Config download complete.');
|
|
136
|
+
const fileCreationPromises = [];
|
|
137
|
+
for (const target of targets) {
|
|
138
|
+
// Resolve the language input
|
|
139
|
+
const language = this.resolveLanguage(target);
|
|
140
|
+
// Get language-specific config or fall back to global config
|
|
141
|
+
const targetConfig = localConfig[language];
|
|
142
|
+
const outputDir = targetConfig.outputDirectory;
|
|
143
|
+
this.verboseLog(`Output directory for ${target}: ${outputDir}`);
|
|
144
|
+
this.verboseLog('Resolving generator...');
|
|
145
|
+
const generator = this.resolveGenerator(language, configFile);
|
|
146
|
+
console.log(`Generating ${language} code for configs...`);
|
|
147
|
+
const generatedCode = generator.generate();
|
|
148
|
+
this.verboseLog(`Code generation complete. Size: ${generatedCode.length}`);
|
|
149
|
+
const fileManager = createFileManager({ outputDirectory: outputDir, verboseLog: this.verboseLog.bind(this) });
|
|
150
|
+
fileCreationPromises.push(fileManager.writeFile({ data: generatedCode, filename: targetConfig.clientFileName }));
|
|
151
|
+
if ([SupportedLanguage.Node, SupportedLanguage.React].includes(language)) {
|
|
152
|
+
const declarationGeneratedCode = generator.declarationGenerate();
|
|
153
|
+
this.verboseLog(`Code generation complete. Size: ${declarationGeneratedCode.length}`);
|
|
154
|
+
fileCreationPromises.push(fileManager.writeFile({ data: declarationGeneratedCode, filename: targetConfig.declarationFileName }));
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
await Promise.all(fileCreationPromises);
|
|
158
|
+
}
|
|
159
|
+
catch (error) {
|
|
160
|
+
console.error('ERROR:', error);
|
|
161
|
+
this.error(error);
|
|
162
|
+
}
|
|
163
|
+
this.verboseLog('=== GENERATE COMMAND END ===');
|
|
164
|
+
return { success: true };
|
|
165
|
+
}
|
|
166
|
+
generateParsedConfig(jsonConfig = {}, outputDirectoryOverride) {
|
|
167
|
+
const parsedConfig = inputConfigSchema.parse(jsonConfig);
|
|
168
|
+
return Object.values(SupportedLanguage).reduce((agg, language) => {
|
|
169
|
+
const parsedLanguageConfig = parsedConfig.targets?.[language] || {};
|
|
170
|
+
const languageConfig = {
|
|
171
|
+
clientFileName: parsedLanguageConfig.clientFileName || DEFAULT_CONFIG.targets[language].clientFileName,
|
|
172
|
+
declarationFileName: parsedLanguageConfig.declarationFileName || DEFAULT_CONFIG.targets[language].declarationFileName,
|
|
173
|
+
outputDirectory: outputDirectoryOverride ||
|
|
174
|
+
parsedLanguageConfig.outputDirectory ||
|
|
175
|
+
parsedConfig.outputDirectory ||
|
|
176
|
+
DEFAULT_CONFIG.outputDirectory,
|
|
177
|
+
};
|
|
178
|
+
agg[language] = languageConfig;
|
|
179
|
+
return agg;
|
|
180
|
+
}, {});
|
|
181
|
+
}
|
|
182
|
+
async readLocalConfig(outputDirectoryOverride) {
|
|
183
|
+
const configPath = path.join(process.cwd(), CONFIG_NAME);
|
|
184
|
+
try {
|
|
185
|
+
// Check if file exists
|
|
186
|
+
await fs.promises.access(configPath, fs.constants.F_OK);
|
|
187
|
+
this.verboseLog(`Found local ${CONFIG_NAME}`);
|
|
188
|
+
// Read and parse the file
|
|
189
|
+
const configContent = await fs.promises.readFile(configPath, 'utf8');
|
|
190
|
+
const parsedConfig = JSON.parse(configContent);
|
|
191
|
+
this.verboseLog(`Local config loaded from ${CONFIG_NAME}: ${JSON.stringify(parsedConfig, null, 2)}`);
|
|
192
|
+
return this.generateParsedConfig(parsedConfig, outputDirectoryOverride);
|
|
193
|
+
}
|
|
194
|
+
catch (error) {
|
|
195
|
+
// File doesn't exist or can't be read/parsed
|
|
196
|
+
if (error.code === 'ENOENT') {
|
|
197
|
+
this.verboseLog(`No ${CONFIG_NAME} file found in current directory.`);
|
|
198
|
+
}
|
|
199
|
+
else {
|
|
200
|
+
this.error(`Error reading ${CONFIG_NAME}: ${error}`);
|
|
201
|
+
}
|
|
202
|
+
return this.generateParsedConfig({}, outputDirectoryOverride);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
resolveGenerator(language, configFile) {
|
|
206
|
+
switch (language) {
|
|
207
|
+
case SupportedLanguage.Node:
|
|
208
|
+
return new NodeTypeScriptGenerator({ configFile, log: this.verboseLog });
|
|
209
|
+
case SupportedLanguage.React:
|
|
210
|
+
return new ReactTypeScriptGenerator({ configFile, log: this.verboseLog });
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
resolveLanguage(languageTarget) {
|
|
214
|
+
const target = languageTarget?.toLowerCase();
|
|
215
|
+
if (!target || !Object.values(SupportedLanguage).includes(target)) {
|
|
216
|
+
throw new Error(`Unsupported target: ${languageTarget}`);
|
|
217
|
+
}
|
|
218
|
+
return target;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { APICommand } from '../index.js';
|
|
2
|
+
import { JsonObj } from '../result.js';
|
|
3
|
+
export default class Get extends APICommand {
|
|
4
|
+
static args: {
|
|
5
|
+
name: import("@oclif/core/lib/interfaces/parser.js").Arg<string | undefined, Record<string, unknown>>;
|
|
6
|
+
};
|
|
7
|
+
static description: string;
|
|
8
|
+
static examples: string[];
|
|
9
|
+
static flags: {
|
|
10
|
+
environment: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
|
|
11
|
+
};
|
|
12
|
+
run(): Promise<JsonObj | void>;
|
|
13
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { Flags } from '@oclif/core';
|
|
2
|
+
import { APICommand } from '../index.js';
|
|
3
|
+
import autocomplete from '../util/autocomplete.js';
|
|
4
|
+
import getEnvironment from '../ui/get-environment.js';
|
|
5
|
+
import isInteractive from '../util/is-interactive.js';
|
|
6
|
+
import nameArg from '../util/name-arg.js';
|
|
7
|
+
import { decrypt } from '../util/encryption.js';
|
|
8
|
+
export default class Get extends APICommand {
|
|
9
|
+
static args = { ...nameArg };
|
|
10
|
+
static description = 'Get the value of a config/feature-flag/etc.';
|
|
11
|
+
static examples = [
|
|
12
|
+
'<%= config.bin %> <%= command.id %> my.config.name',
|
|
13
|
+
'<%= config.bin %> <%= command.id %> my.config.name --environment=production',
|
|
14
|
+
];
|
|
15
|
+
static flags = {
|
|
16
|
+
environment: Flags.string({ description: 'environment to evaluate in' }),
|
|
17
|
+
};
|
|
18
|
+
async run() {
|
|
19
|
+
const { args, flags } = await this.parse(Get);
|
|
20
|
+
// Fetch metadata first for validation and autocomplete
|
|
21
|
+
const metadataRequest = await this.apiClient.post('/api/v1/metadata/list', { workspaceId: this.workspaceId });
|
|
22
|
+
if (!metadataRequest.ok) {
|
|
23
|
+
const errorMsg = metadataRequest.error?.error || `Failed to fetch configs: ${metadataRequest.status}`;
|
|
24
|
+
return this.err(errorMsg, { serverError: metadataRequest.error });
|
|
25
|
+
}
|
|
26
|
+
const metadataResponse = metadataRequest.json;
|
|
27
|
+
const configKeys = metadataResponse.configs.map((c) => c.key);
|
|
28
|
+
// Get the key with autocomplete
|
|
29
|
+
let key = args.name;
|
|
30
|
+
if (!key && isInteractive(flags)) {
|
|
31
|
+
const selectedKey = await autocomplete({
|
|
32
|
+
message: 'Config key',
|
|
33
|
+
source: configKeys,
|
|
34
|
+
});
|
|
35
|
+
if (selectedKey) {
|
|
36
|
+
key = selectedKey;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
if (!key) {
|
|
40
|
+
return this.err('Key is required');
|
|
41
|
+
}
|
|
42
|
+
// Validate key exists
|
|
43
|
+
const configExists = metadataResponse.configs.some((config) => config.key === key);
|
|
44
|
+
if (!configExists) {
|
|
45
|
+
return this.err(`${key} does not exist`);
|
|
46
|
+
}
|
|
47
|
+
// Get the environment
|
|
48
|
+
const environment = await getEnvironment({
|
|
49
|
+
command: this,
|
|
50
|
+
flags,
|
|
51
|
+
message: 'Which environment would you like to evaluate in?',
|
|
52
|
+
providedEnvironment: flags.environment,
|
|
53
|
+
});
|
|
54
|
+
if (!environment) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
const request = await this.apiClient.post('/api/v1/evaluations/evaluate', {
|
|
58
|
+
workspaceId: this.workspaceId,
|
|
59
|
+
environmentId: environment.id,
|
|
60
|
+
context: {},
|
|
61
|
+
});
|
|
62
|
+
if (!request.ok) {
|
|
63
|
+
const errorMsg = request.error?.error || `Failed to get config: ${request.status}`;
|
|
64
|
+
return this.err(errorMsg, { serverError: request.error });
|
|
65
|
+
}
|
|
66
|
+
// oRPC returns EvaluationResult[] — find the matching key
|
|
67
|
+
const results = (Array.isArray(request.json) ? request.json : []);
|
|
68
|
+
const result = results.find((r) => r.key === key);
|
|
69
|
+
if (!result) {
|
|
70
|
+
return this.err(`${key} could not be evaluated in this environment`);
|
|
71
|
+
}
|
|
72
|
+
// Adapt to the legacy response shape for downstream compatibility
|
|
73
|
+
const config = {
|
|
74
|
+
key: result.key,
|
|
75
|
+
type: result.configType,
|
|
76
|
+
value: result.displayValue,
|
|
77
|
+
metadata: {
|
|
78
|
+
conditionalValueIndex: 0,
|
|
79
|
+
configRowIndex: 0,
|
|
80
|
+
id: 0,
|
|
81
|
+
type: result.configType,
|
|
82
|
+
valueType: result.valueType,
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
let value = config.value;
|
|
86
|
+
// Check if this config has dependencies
|
|
87
|
+
if (config.dependencies && config.dependencies.length > 0) {
|
|
88
|
+
// Check for providedBy dependency (config value from env var)
|
|
89
|
+
const providedByDep = config.dependencies.find((dep) => dep.dependencyType === 'providedBy');
|
|
90
|
+
if (providedByDep) {
|
|
91
|
+
const envVarName = providedByDep.source;
|
|
92
|
+
this.log(`This config is provided by env var '${envVarName}'`);
|
|
93
|
+
// Check if the env var is present
|
|
94
|
+
const envValue = process.env[envVarName];
|
|
95
|
+
if (!envValue) {
|
|
96
|
+
return this.err(`Environment variable '${envVarName}' is not set. Cannot resolve config '${key}'.`, {
|
|
97
|
+
[key]: value,
|
|
98
|
+
provided: true,
|
|
99
|
+
missingEnvVar: envVarName,
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
value = envValue;
|
|
103
|
+
this.log(`Successfully resolved config '${key}' from env var`);
|
|
104
|
+
}
|
|
105
|
+
// Check for decryptWith dependency (encrypted config)
|
|
106
|
+
const decryptWithDep = config.dependencies.find((dep) => dep.dependencyType === 'decryptWith');
|
|
107
|
+
if (decryptWithDep && decryptWithDep.config) {
|
|
108
|
+
const encryptionKeyConfig = decryptWithDep.config;
|
|
109
|
+
// Find the providedBy dependency to get the env var name for the encryption key
|
|
110
|
+
const keyProvidedByDep = encryptionKeyConfig.dependencies?.find((dep) => dep.dependencyType === 'providedBy');
|
|
111
|
+
if (keyProvidedByDep) {
|
|
112
|
+
const envVarName = keyProvidedByDep.source;
|
|
113
|
+
this.log(`This config is encrypted by key '${encryptionKeyConfig.key}' that should be found in env var '${envVarName}'`);
|
|
114
|
+
// Check if the env var is present
|
|
115
|
+
const encryptionKey = process.env[envVarName];
|
|
116
|
+
if (!encryptionKey) {
|
|
117
|
+
return this.err(`Environment variable '${envVarName}' is not set. Cannot decrypt config '${key}'.`, {
|
|
118
|
+
[key]: value,
|
|
119
|
+
encrypted: true,
|
|
120
|
+
missingEnvVar: envVarName,
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
// Attempt decryption
|
|
124
|
+
try {
|
|
125
|
+
value = decrypt(value, encryptionKey);
|
|
126
|
+
this.log(`Successfully decrypted config '${key}'`);
|
|
127
|
+
}
|
|
128
|
+
catch (error) {
|
|
129
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
130
|
+
return this.err(`Failed to decrypt config '${key}': ${errorMessage}`, {
|
|
131
|
+
[key]: value,
|
|
132
|
+
encrypted: true,
|
|
133
|
+
error: errorMessage,
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return this.ok(this.toSuccessJson(value), { [key]: value });
|
|
140
|
+
}
|
|
141
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { APICommand } from '../index.js';
|
|
2
|
+
import { JsonObj } from '../result.js';
|
|
3
|
+
export default class Info extends APICommand {
|
|
4
|
+
static args: {
|
|
5
|
+
name: import("@oclif/core/lib/interfaces/parser.js").Arg<string | undefined, Record<string, unknown>>;
|
|
6
|
+
};
|
|
7
|
+
static description: string;
|
|
8
|
+
static examples: string[];
|
|
9
|
+
static flags: {
|
|
10
|
+
'exclude-evaluations': import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
|
|
11
|
+
};
|
|
12
|
+
run(): Promise<JsonObj | void>;
|
|
13
|
+
private displayEvaluationStats;
|
|
14
|
+
private extractSimpleValue;
|
|
15
|
+
private fetchEvaluationStats;
|
|
16
|
+
private formatValue;
|
|
17
|
+
private hasNonTrivialCriteria;
|
|
18
|
+
private parseConfig;
|
|
19
|
+
private transformEvaluationStatsForJson;
|
|
20
|
+
private transformValueForJson;
|
|
21
|
+
}
|