@revoengine/cli 1.0.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 (46) hide show
  1. package/README.md +211 -0
  2. package/dist/bin/revo.d.ts +2 -0
  3. package/dist/bin/revo.js +19 -0
  4. package/dist/src/cli.d.ts +3 -0
  5. package/dist/src/cli.js +213 -0
  6. package/dist/src/client.d.ts +72 -0
  7. package/dist/src/client.js +315 -0
  8. package/dist/src/commands/auth.d.ts +2 -0
  9. package/dist/src/commands/auth.js +131 -0
  10. package/dist/src/commands/component.d.ts +2 -0
  11. package/dist/src/commands/component.js +905 -0
  12. package/dist/src/commands/endpoints.d.ts +2 -0
  13. package/dist/src/commands/endpoints.js +4 -0
  14. package/dist/src/commands/index.d.ts +7 -0
  15. package/dist/src/commands/index.js +7 -0
  16. package/dist/src/commands/info.d.ts +2 -0
  17. package/dist/src/commands/info.js +6 -0
  18. package/dist/src/commands/project.d.ts +2 -0
  19. package/dist/src/commands/project.js +80 -0
  20. package/dist/src/commands/request.d.ts +2 -0
  21. package/dist/src/commands/request.js +59 -0
  22. package/dist/src/commands/search.d.ts +2 -0
  23. package/dist/src/commands/search.js +22 -0
  24. package/dist/src/config.d.ts +54 -0
  25. package/dist/src/config.js +356 -0
  26. package/dist/src/index.d.ts +4 -0
  27. package/dist/src/index.js +4 -0
  28. package/dist/src/legacy.d.ts +8 -0
  29. package/dist/src/legacy.js +88 -0
  30. package/dist/src/project.d.ts +102 -0
  31. package/dist/src/project.js +475 -0
  32. package/dist/src/prompt.d.ts +4 -0
  33. package/dist/src/prompt.js +64 -0
  34. package/dist/src/runtime-view.d.ts +17 -0
  35. package/dist/src/runtime-view.js +80 -0
  36. package/dist/src/spinner.d.ts +14 -0
  37. package/dist/src/spinner.js +46 -0
  38. package/dist/src/types.d.ts +36 -0
  39. package/dist/src/types.js +1 -0
  40. package/dist/src/ui.d.ts +26 -0
  41. package/dist/src/ui.js +182 -0
  42. package/dist/src/utils.d.ts +10 -0
  43. package/dist/src/utils.js +86 -0
  44. package/package.json +32 -0
  45. package/tsconfig.build.json +15 -0
  46. package/tsconfig.json +19 -0
@@ -0,0 +1,2 @@
1
+ import type { CommandContext } from '../types.ts';
2
+ export declare function handleEndpointsCommand(context: CommandContext): Promise<void>;
@@ -0,0 +1,4 @@
1
+ export async function handleEndpointsCommand(context) {
2
+ const endpoints = await context.client.listEndpoints();
3
+ context.print(endpoints);
4
+ }
@@ -0,0 +1,7 @@
1
+ export * from './auth.ts';
2
+ export * from './component.ts';
3
+ export * from './endpoints.ts';
4
+ export * from './info.ts';
5
+ export * from './project.ts';
6
+ export * from './request.ts';
7
+ export * from './search.ts';
@@ -0,0 +1,7 @@
1
+ export * from "./auth.js";
2
+ export * from "./component.js";
3
+ export * from "./endpoints.js";
4
+ export * from "./info.js";
5
+ export * from "./project.js";
6
+ export * from "./request.js";
7
+ export * from "./search.js";
@@ -0,0 +1,2 @@
1
+ import type { CommandContext } from '../types.ts';
2
+ export declare function handleInfoCommand(context: CommandContext): Promise<void>;
@@ -0,0 +1,6 @@
1
+ import { buildRuntimeViewModel } from "../runtime-view.js";
2
+ import { renderRuntimeInfo } from "../ui.js";
3
+ export async function handleInfoCommand(context) {
4
+ const view = await buildRuntimeViewModel(context);
5
+ context.print(renderRuntimeInfo(view));
6
+ }
@@ -0,0 +1,2 @@
1
+ import type { CommandContext } from '../types.ts';
2
+ export declare function handleProjectCommand(context: CommandContext): Promise<void>;
@@ -0,0 +1,80 @@
1
+ import path from 'node:path';
2
+ import { isUuid, loadStoredConfigIndex } from "../config.js";
3
+ import { buildProjectSyncState, buildEditorTypesUrl, extractSandboxEndpoint, extractEditorTypesBundle, findProjectMetadataFile, resolveProjectInvocation, resolveProjectRoot, resolveProjectTarget, syncProjectFiles, } from "../project.js";
4
+ function printSyncSummary(println, targetDir, result, prefix) {
5
+ println(`${prefix} ${targetDir}`);
6
+ println(`Wrote ${path.relative(targetDir, result.typesFile)}`);
7
+ println(`Wrote ${path.relative(targetDir, result.metadataFile)}`);
8
+ println(`${result.configResult.action === 'created' ? 'Created' : 'Patched'} ${path.basename(result.configResult.filePath)}`);
9
+ if (result.gitignoreResult.action === 'created') {
10
+ println('Created .gitignore');
11
+ }
12
+ else if (result.gitignoreResult.action === 'patched') {
13
+ println('Patched .gitignore');
14
+ }
15
+ println('Restart TypeScript server in VSCode if IntelliSense does not appear');
16
+ }
17
+ async function resolveProjectSyncInput(context) {
18
+ const { client } = context;
19
+ client.assertReady();
20
+ const profile = await client.me();
21
+ const sandboxEndpoint = extractSandboxEndpoint(profile);
22
+ if (!sandboxEndpoint) {
23
+ throw new Error('Authenticated profile did not include `endpoints.sandbox`, so editor types cannot be initialized for this instance.');
24
+ }
25
+ const bundle = extractEditorTypesBundle(await client.getEditorTypes(buildEditorTypesUrl(sandboxEndpoint)));
26
+ return buildProjectSyncState({
27
+ fallbackEndpoint: sandboxEndpoint,
28
+ fallbackInstanceId: client.instance,
29
+ bundle,
30
+ });
31
+ }
32
+ export async function handleProjectCommand(context) {
33
+ const { args, client, cwd, println } = context;
34
+ const invocation = resolveProjectInvocation(args);
35
+ if (invocation.action === 'update') {
36
+ throw new Error('`revo project update` is not implemented yet.');
37
+ }
38
+ if (invocation.action === 'switch') {
39
+ if (invocation.extraArgs.length > 0) {
40
+ throw new Error('Project switch accepts exactly one instance ID.');
41
+ }
42
+ const instanceId = invocation.targetArg || '';
43
+ if (!instanceId) {
44
+ throw new Error('Missing instance UUID. Usage: `revo project switch <instanceId>`.');
45
+ }
46
+ if (!isUuid(instanceId)) {
47
+ throw new Error('Instance ID must be a UUID.');
48
+ }
49
+ const metadataFile = findProjectMetadataFile(cwd);
50
+ if (!metadataFile) {
51
+ throw new Error('Current directory is not inside an initialized Revo project.');
52
+ }
53
+ const projectRoot = resolveProjectRoot(cwd);
54
+ if (!projectRoot) {
55
+ throw new Error('Unable to resolve the current Revo project root.');
56
+ }
57
+ const storedConfigs = loadStoredConfigIndex();
58
+ const selected = storedConfigs.instances[instanceId];
59
+ if (!selected) {
60
+ throw new Error(`No stored credentials found for instance ${instanceId}. Run \`revo auth login --instance ${instanceId}\` first.`);
61
+ }
62
+ if (!selected.token) {
63
+ throw new Error(`Stored credentials for instance ${instanceId} are incomplete. Run \`revo auth login --instance ${instanceId}\` again.`);
64
+ }
65
+ client.baseUrl = selected.baseUrl;
66
+ client.instance = instanceId;
67
+ client.token = selected.token;
68
+ const syncInput = await resolveProjectSyncInput(context);
69
+ const result = syncProjectFiles(projectRoot, syncInput);
70
+ printSyncSummary(println, projectRoot, result, `Switched Revo project to ${instanceId} in`);
71
+ return;
72
+ }
73
+ if (invocation.extraArgs.length > 0) {
74
+ throw new Error('Project init accepts at most one path argument.');
75
+ }
76
+ const targetDir = resolveProjectTarget(cwd, invocation.targetArg);
77
+ const syncInput = await resolveProjectSyncInput(context);
78
+ const result = syncProjectFiles(targetDir, syncInput);
79
+ printSyncSummary(println, targetDir, result, 'Initialized Revo project in');
80
+ }
@@ -0,0 +1,2 @@
1
+ import type { CommandContext } from '../types.ts';
2
+ export declare function handleRequestCommand(context: CommandContext): Promise<void>;
@@ -0,0 +1,59 @@
1
+ import { readFlag, readValues } from "../utils.js";
2
+ function parseJsonMaybe(value) {
3
+ if (!value) {
4
+ return undefined;
5
+ }
6
+ try {
7
+ return JSON.parse(value);
8
+ }
9
+ catch {
10
+ return value;
11
+ }
12
+ }
13
+ function parsePositionalJson(value) {
14
+ try {
15
+ return JSON.parse(value);
16
+ }
17
+ catch {
18
+ throw new Error('Positional request body must be valid JSON.');
19
+ }
20
+ }
21
+ function parseQueryValue(value) {
22
+ const query = {};
23
+ const parts = value.split(/[,&]/).map((part) => part.trim()).filter(Boolean);
24
+ for (const part of parts) {
25
+ const [key, raw] = part.split('=');
26
+ if (!key) {
27
+ continue;
28
+ }
29
+ query[key] = raw ?? '';
30
+ }
31
+ return query;
32
+ }
33
+ export async function handleRequestCommand(context) {
34
+ const { args, client } = context;
35
+ const method = (args._[1] || readFlag(args, ['method', 'm']) || 'GET').toUpperCase();
36
+ const requestPath = args._[2] || readFlag(args, ['path', 'p']) || '';
37
+ const bodyFlag = readFlag(args, ['body', 'd']);
38
+ const positionalBody = args._[3] || '';
39
+ const body = positionalBody ? parsePositionalJson(positionalBody) : parseJsonMaybe(bodyFlag);
40
+ const queryValues = readValues(args, ['query', 'q']);
41
+ if (!requestPath) {
42
+ throw new Error('Missing request path.');
43
+ }
44
+ const query = queryValues.reduce((acc, entry) => {
45
+ return {
46
+ ...acc,
47
+ ...parseQueryValue(entry),
48
+ };
49
+ }, {});
50
+ const response = await client.request(method, requestPath, {
51
+ query: Object.keys(query).length > 0 ? query : undefined,
52
+ body,
53
+ });
54
+ context.print({
55
+ status: response.status,
56
+ ok: response.ok,
57
+ data: response.data,
58
+ });
59
+ }
@@ -0,0 +1,2 @@
1
+ import type { CommandContext } from '../types.ts';
2
+ export declare function handleSearchCommand(context: CommandContext): Promise<void>;
@@ -0,0 +1,22 @@
1
+ import { readFlag } from "../utils.js";
2
+ const SEARCH_TYPES = new Set(['CODE', 'SIMPLE']);
3
+ export async function handleSearchCommand(context) {
4
+ const { args, client } = context;
5
+ const type = (readFlag(args, ['type', 't']) || args._[1] || '').toUpperCase();
6
+ const term = readFlag(args, ['term']) || args._[2] || '';
7
+ if (!type || !term) {
8
+ throw new Error('Search requires a type and a term.');
9
+ }
10
+ if (!SEARCH_TYPES.has(type)) {
11
+ throw new Error('Search type must be CODE or SIMPLE.');
12
+ }
13
+ const result = await client.search({
14
+ type,
15
+ term,
16
+ componentType: readFlag(args, ['componentType']),
17
+ caseSensitive: readFlag(args, ['caseSensitive']),
18
+ take: readFlag(args, ['take']),
19
+ skip: readFlag(args, ['skip']),
20
+ });
21
+ context.print(result);
22
+ }
@@ -0,0 +1,54 @@
1
+ export declare const APP_NAME = "revoengine";
2
+ export declare const DEFAULT_BASE_URL = "https://api.revoengine.com";
3
+ export type RuntimeConfig = {
4
+ baseUrl: string;
5
+ instance: string;
6
+ token: string;
7
+ };
8
+ export type RuntimeConfigOptions = {
9
+ baseUrl?: string;
10
+ instance?: string;
11
+ token?: string;
12
+ };
13
+ type StoredConfig = RuntimeConfig;
14
+ type StoredInstanceConfig = {
15
+ baseUrl: string;
16
+ token: string;
17
+ };
18
+ type StoredConfigFile = {
19
+ defaultInstance: string;
20
+ instances: Record<string, StoredInstanceConfig>;
21
+ };
22
+ export type AuthValidationState = {
23
+ key: string;
24
+ status: 'authenticated' | 'not_authenticated';
25
+ checkedAt: number;
26
+ profile?: unknown;
27
+ };
28
+ export declare const AUTH_VALIDATION_TTL_MS = 60000;
29
+ export declare function getConfigDir(): string;
30
+ export declare function getConfigPaths(): {
31
+ dir: string;
32
+ configFile: string;
33
+ credentialsFile: string;
34
+ authStateFile: string;
35
+ };
36
+ export declare function readProjectInstanceId(startDir?: string): string;
37
+ export declare function isUuid(value: unknown): value is string;
38
+ export declare function saveStoredConfig(nextConfig: Partial<StoredConfig>): void;
39
+ export declare function clearStoredConfig(): void;
40
+ export declare function loadStoredConfigIndex(): StoredConfigFile;
41
+ export declare function loadStoredConfig(options?: {
42
+ instance?: string;
43
+ allowLegacy?: boolean;
44
+ }): RuntimeConfig;
45
+ export declare function buildAuthValidationKey(runtime: RuntimeConfigOptions): string;
46
+ export declare function readAuthValidationState(key?: string): AuthValidationState;
47
+ export declare function saveAuthValidationState(state: AuthValidationState): void;
48
+ export declare function clearAuthValidationState(key?: string): void;
49
+ export declare function resolveRuntimeConfig(options?: RuntimeConfigOptions): {
50
+ baseUrl: string;
51
+ instance: string;
52
+ token: string;
53
+ };
54
+ export {};
@@ -0,0 +1,356 @@
1
+ import fs from 'node:fs';
2
+ import os from 'node:os';
3
+ import path from 'node:path';
4
+ import { createHash } from 'node:crypto';
5
+ import { readLegacyImportFromProject } from "./legacy.js";
6
+ import { readJsonFile, writeJsonFile } from "./utils.js";
7
+ export const APP_NAME = 'revoengine';
8
+ export const DEFAULT_BASE_URL = 'https://api.revoengine.com';
9
+ export const AUTH_VALIDATION_TTL_MS = 60_000;
10
+ function ensureDirectory(dir) {
11
+ fs.mkdirSync(dir, { recursive: true });
12
+ }
13
+ export function getConfigDir() {
14
+ if (process.platform === 'win32' && process.env.APPDATA) {
15
+ return path.join(process.env.APPDATA, APP_NAME);
16
+ }
17
+ const homeConfig = process.env.XDG_CONFIG_HOME || path.join(os.homedir(), '.config');
18
+ return path.join(homeConfig, APP_NAME);
19
+ }
20
+ export function getConfigPaths() {
21
+ const dir = getConfigDir();
22
+ return {
23
+ dir,
24
+ configFile: path.join(dir, 'config.json'),
25
+ credentialsFile: path.join(dir, 'credentials.json'),
26
+ authStateFile: path.join(dir, 'auth-state.json'),
27
+ };
28
+ }
29
+ function findProjectMetadataFile(startDir = process.cwd()) {
30
+ if (!startDir) {
31
+ return '';
32
+ }
33
+ let currentDir = path.resolve(startDir);
34
+ const { root } = path.parse(currentDir);
35
+ while (true) {
36
+ const candidate = path.join(currentDir, '.revoengine', 'revo.json');
37
+ if (fs.existsSync(candidate)) {
38
+ return candidate;
39
+ }
40
+ if (currentDir === root) {
41
+ return '';
42
+ }
43
+ currentDir = path.dirname(currentDir);
44
+ }
45
+ }
46
+ export function readProjectInstanceId(startDir = process.cwd()) {
47
+ const metadataFile = findProjectMetadataFile(startDir);
48
+ if (!metadataFile) {
49
+ return '';
50
+ }
51
+ try {
52
+ const metadata = readJsonFile(metadataFile);
53
+ return typeof metadata.instanceId === 'string' ? metadata.instanceId : '';
54
+ }
55
+ catch {
56
+ return '';
57
+ }
58
+ }
59
+ function resolveEnvValue(keys) {
60
+ for (const key of keys) {
61
+ const value = process.env[key];
62
+ if (value) {
63
+ return value;
64
+ }
65
+ }
66
+ return '';
67
+ }
68
+ export function isUuid(value) {
69
+ return typeof value === 'string'
70
+ && /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(value);
71
+ }
72
+ export function saveStoredConfig(nextConfig) {
73
+ const { dir, configFile, credentialsFile } = getConfigPaths();
74
+ ensureDirectory(dir);
75
+ const current = readStoredConfigFile();
76
+ const fallback = current.defaultInstance ? current.instances[current.defaultInstance] : undefined;
77
+ const instance = nextConfig.instance || current.defaultInstance || '';
78
+ if (!instance) {
79
+ writeJsonFile(configFile, current);
80
+ removeFileIfExists(credentialsFile);
81
+ return;
82
+ }
83
+ const currentEntry = current.instances[instance];
84
+ current.instances[instance] = {
85
+ baseUrl: nextConfig.baseUrl || currentEntry?.baseUrl || fallback?.baseUrl || DEFAULT_BASE_URL,
86
+ token: nextConfig.token || currentEntry?.token || fallback?.token || '',
87
+ };
88
+ current.defaultInstance = instance;
89
+ writeJsonFile(configFile, current);
90
+ removeFileIfExists(credentialsFile);
91
+ }
92
+ export function clearStoredConfig() {
93
+ const { configFile, credentialsFile, authStateFile } = getConfigPaths();
94
+ for (const filePath of [configFile, credentialsFile, authStateFile]) {
95
+ try {
96
+ fs.unlinkSync(filePath);
97
+ }
98
+ catch (error) {
99
+ if (!error || error.code !== 'ENOENT') {
100
+ throw error;
101
+ }
102
+ }
103
+ }
104
+ }
105
+ function removeFileIfExists(filePath) {
106
+ try {
107
+ fs.unlinkSync(filePath);
108
+ }
109
+ catch (error) {
110
+ if (!error || error.code !== 'ENOENT') {
111
+ throw error;
112
+ }
113
+ }
114
+ }
115
+ function canIgnoreWriteError(error) {
116
+ return error !== null
117
+ && typeof error === 'object'
118
+ && 'code' in error
119
+ && ['EACCES', 'EPERM', 'EROFS'].includes(String(error.code));
120
+ }
121
+ function migrateStoredConfigBestEffort(configFile, credentialsFile, nextConfig) {
122
+ try {
123
+ writeJsonFile(configFile, nextConfig);
124
+ removeFileIfExists(credentialsFile);
125
+ }
126
+ catch (error) {
127
+ if (!canIgnoreWriteError(error)) {
128
+ throw error;
129
+ }
130
+ }
131
+ }
132
+ function emptyStoredConfigFile() {
133
+ return {
134
+ defaultInstance: '',
135
+ instances: {},
136
+ };
137
+ }
138
+ function isRecord(value) {
139
+ return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
140
+ }
141
+ function normalizeInstanceConfig(value) {
142
+ if (!isRecord(value)) {
143
+ return null;
144
+ }
145
+ return {
146
+ baseUrl: typeof value.baseUrl === 'string'
147
+ ? value.baseUrl
148
+ : typeof value.url === 'string'
149
+ ? value.url
150
+ : DEFAULT_BASE_URL,
151
+ token: typeof value.token === 'string' ? value.token : '',
152
+ };
153
+ }
154
+ function parseMappedConfig(raw) {
155
+ if (!isRecord(raw) || !isRecord(raw.instances)) {
156
+ return null;
157
+ }
158
+ const instances = {};
159
+ for (const [instanceId, value] of Object.entries(raw.instances)) {
160
+ if (!isUuid(instanceId)) {
161
+ continue;
162
+ }
163
+ const normalized = normalizeInstanceConfig(value);
164
+ if (normalized) {
165
+ instances[instanceId] = normalized;
166
+ }
167
+ }
168
+ const configuredDefault = typeof raw.defaultInstance === 'string' ? raw.defaultInstance : '';
169
+ const defaultInstance = isUuid(configuredDefault) && instances[configuredDefault]
170
+ ? configuredDefault
171
+ : Object.keys(instances)[0] || '';
172
+ return {
173
+ defaultInstance,
174
+ instances,
175
+ };
176
+ }
177
+ function parseLegacyConfig(config, credentials) {
178
+ const baseUrl = isRecord(config) && typeof config.baseUrl === 'string'
179
+ ? config.baseUrl
180
+ : isRecord(config) && typeof config.url === 'string'
181
+ ? config.url
182
+ : DEFAULT_BASE_URL;
183
+ const instance = isRecord(config) && typeof config.instance === 'string' ? config.instance : '';
184
+ const token = isRecord(credentials) && typeof credentials.token === 'string' ? credentials.token : '';
185
+ if (!instance) {
186
+ return emptyStoredConfigFile();
187
+ }
188
+ return {
189
+ defaultInstance: instance,
190
+ instances: {
191
+ [instance]: {
192
+ baseUrl,
193
+ token,
194
+ },
195
+ },
196
+ };
197
+ }
198
+ function readStoredConfigFile() {
199
+ const { configFile, credentialsFile } = getConfigPaths();
200
+ const config = fs.existsSync(configFile) ? readJsonFile(configFile) : {};
201
+ const credentials = fs.existsSync(credentialsFile) ? readJsonFile(credentialsFile) : {};
202
+ const mapped = parseMappedConfig(config);
203
+ if (mapped) {
204
+ if (fs.existsSync(credentialsFile)) {
205
+ migrateStoredConfigBestEffort(configFile, credentialsFile, mapped);
206
+ }
207
+ return mapped;
208
+ }
209
+ const legacy = parseLegacyConfig(config, credentials);
210
+ if (legacy.defaultInstance) {
211
+ migrateStoredConfigBestEffort(configFile, credentialsFile, legacy);
212
+ }
213
+ return legacy;
214
+ }
215
+ export function loadStoredConfigIndex() {
216
+ return readStoredConfigFile();
217
+ }
218
+ function readStoredConfig(options = {}) {
219
+ const stored = readStoredConfigFile();
220
+ const selectedInstance = options.instance && stored.instances[options.instance]
221
+ ? options.instance
222
+ : stored.defaultInstance;
223
+ const selected = selectedInstance ? stored.instances[selectedInstance] : undefined;
224
+ return {
225
+ baseUrl: selected?.baseUrl || DEFAULT_BASE_URL,
226
+ instance: selectedInstance || '',
227
+ token: selected?.token || '',
228
+ };
229
+ }
230
+ function mergeWithLegacy(stored, options = {}) {
231
+ if (options.allowLegacy === false) {
232
+ return stored;
233
+ }
234
+ if ((stored.token && stored.instance) || !process.cwd()) {
235
+ return stored;
236
+ }
237
+ const legacy = readLegacyImportFromProject(process.cwd());
238
+ if (!legacy) {
239
+ return stored;
240
+ }
241
+ const merged = {
242
+ baseUrl: stored.baseUrl || legacy.baseUrl || DEFAULT_BASE_URL,
243
+ instance: stored.instance || options.instance || legacy.instance || '',
244
+ token: stored.token || legacy.token || '',
245
+ };
246
+ if (merged.token || merged.instance || merged.baseUrl !== DEFAULT_BASE_URL) {
247
+ saveStoredConfig(merged);
248
+ }
249
+ return merged;
250
+ }
251
+ export function loadStoredConfig(options = {}) {
252
+ return mergeWithLegacy(readStoredConfig(options), options);
253
+ }
254
+ export function buildAuthValidationKey(runtime) {
255
+ return createHash('sha256')
256
+ .update(JSON.stringify({
257
+ baseUrl: runtime.baseUrl || DEFAULT_BASE_URL,
258
+ instance: runtime.instance || '',
259
+ token: runtime.token || '',
260
+ }))
261
+ .digest('hex');
262
+ }
263
+ function emptyAuthValidationStateStore() {
264
+ return {
265
+ entries: {},
266
+ };
267
+ }
268
+ function parseAuthValidationStateStore(raw) {
269
+ if (isRecord(raw) && isRecord(raw.entries)) {
270
+ const entries = {};
271
+ for (const [key, value] of Object.entries(raw.entries)) {
272
+ if (!isRecord(value)) {
273
+ continue;
274
+ }
275
+ const candidate = value;
276
+ if (candidate.key === key
277
+ && (candidate.status === 'authenticated' || candidate.status === 'not_authenticated')
278
+ && typeof candidate.checkedAt === 'number') {
279
+ entries[key] = candidate;
280
+ }
281
+ }
282
+ return { entries };
283
+ }
284
+ if (isRecord(raw)) {
285
+ const candidate = raw;
286
+ if (typeof candidate.key === 'string'
287
+ && (candidate.status === 'authenticated' || candidate.status === 'not_authenticated')
288
+ && typeof candidate.checkedAt === 'number') {
289
+ return {
290
+ entries: {
291
+ [candidate.key]: candidate,
292
+ },
293
+ };
294
+ }
295
+ }
296
+ return emptyAuthValidationStateStore();
297
+ }
298
+ function readAuthValidationStateStore() {
299
+ const { authStateFile } = getConfigPaths();
300
+ if (!fs.existsSync(authStateFile)) {
301
+ return emptyAuthValidationStateStore();
302
+ }
303
+ const raw = readJsonFile(authStateFile);
304
+ return parseAuthValidationStateStore(raw);
305
+ }
306
+ export function readAuthValidationState(key) {
307
+ const store = readAuthValidationStateStore();
308
+ if (key) {
309
+ return store.entries[key] || null;
310
+ }
311
+ return Object.values(store.entries)[0] || null;
312
+ }
313
+ export function saveAuthValidationState(state) {
314
+ const { dir, authStateFile } = getConfigPaths();
315
+ ensureDirectory(dir);
316
+ const store = readAuthValidationStateStore();
317
+ store.entries[state.key] = state;
318
+ writeJsonFile(authStateFile, store);
319
+ }
320
+ export function clearAuthValidationState(key) {
321
+ const { authStateFile } = getConfigPaths();
322
+ if (key) {
323
+ const store = readAuthValidationStateStore();
324
+ delete store.entries[key];
325
+ if (Object.keys(store.entries).length > 0) {
326
+ writeJsonFile(authStateFile, store);
327
+ return;
328
+ }
329
+ }
330
+ try {
331
+ fs.unlinkSync(authStateFile);
332
+ }
333
+ catch (error) {
334
+ if (!error || error.code !== 'ENOENT') {
335
+ throw error;
336
+ }
337
+ }
338
+ }
339
+ export function resolveRuntimeConfig(options = {}) {
340
+ const env = {
341
+ baseUrl: resolveEnvValue(['REVO_URL', 'REVO_BASE_URL', 'REVOENGINE_URL', 'REVOENGINE_BASE_URL']),
342
+ instance: resolveEnvValue(['REVO_INSTANCE', 'REVOENGINE_INSTANCE']),
343
+ token: resolveEnvValue(['REVO_TOKEN', 'REVO_API_KEY', 'REVOENGINE_TOKEN', 'REVOENGINE_API_KEY']),
344
+ };
345
+ const projectInstance = !options.instance && !env.instance ? readProjectInstanceId() : '';
346
+ const requestedInstance = options.instance || env.instance || projectInstance || undefined;
347
+ const stored = loadStoredConfig({
348
+ instance: requestedInstance,
349
+ allowLegacy: !requestedInstance,
350
+ });
351
+ return {
352
+ baseUrl: options.baseUrl || env.baseUrl || stored.baseUrl || DEFAULT_BASE_URL,
353
+ instance: options.instance || env.instance || stored.instance || '',
354
+ token: options.token || env.token || stored.token || '',
355
+ };
356
+ }
@@ -0,0 +1,4 @@
1
+ export * from './config.ts';
2
+ export * from './client.ts';
3
+ export * from './cli.ts';
4
+ export * from './commands/index.ts';
@@ -0,0 +1,4 @@
1
+ export * from "./config.js";
2
+ export * from "./client.js";
3
+ export * from "./cli.js";
4
+ export * from "./commands/index.js";
@@ -0,0 +1,8 @@
1
+ export type LegacyRuntimeImport = {
2
+ baseUrl: string;
3
+ instance: string;
4
+ token: string;
5
+ source: string;
6
+ };
7
+ export declare function readLegacyImportFromFile(filePath: string, preferredInstance?: string | null): LegacyRuntimeImport | null;
8
+ export declare function readLegacyImportFromProject(cwd?: string): LegacyRuntimeImport | null;