buildx-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.
- package/.github/workflows/auto-publish.yml +242 -0
- package/.github/workflows/create-pr.yml +182 -0
- package/.prettierrc +8 -0
- package/README.md +179 -0
- package/dist/index.cjs +21 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +21 -0
- package/eslint.config.mjs +119 -0
- package/jest.config.js +16 -0
- package/package.json +69 -0
- package/rollup.config.mjs +64 -0
- package/src/__tests__/config.test.ts +102 -0
- package/src/commands/auth/login.ts +148 -0
- package/src/commands/auth/logout.ts +16 -0
- package/src/commands/auth/status.ts +52 -0
- package/src/commands/config/clear.ts +16 -0
- package/src/commands/config/index.ts +10 -0
- package/src/commands/config/setup.ts +75 -0
- package/src/commands/config/show.ts +70 -0
- package/src/commands/projects/current.ts +36 -0
- package/src/commands/projects/list.ts +61 -0
- package/src/commands/projects/set-default.ts +33 -0
- package/src/commands/sync.ts +64 -0
- package/src/config/index.ts +154 -0
- package/src/index.ts +49 -0
- package/src/services/api.ts +132 -0
- package/src/services/schema-generator.ts +132 -0
- package/src/types/index.ts +91 -0
- package/src/utils/logger.ts +29 -0
- package/tsconfig.json +29 -0
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import inquirer from 'inquirer';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
import { configManager } from '../../config/index';
|
|
5
|
+
|
|
6
|
+
export const setupCommand = new Command('setup')
|
|
7
|
+
.description('Configure API endpoint and API key')
|
|
8
|
+
.option('-e, --endpoint <endpoint>', 'API endpoint URL')
|
|
9
|
+
.option('-k, --api-key <apiKey>', 'API key')
|
|
10
|
+
.action(async (options) => {
|
|
11
|
+
try {
|
|
12
|
+
let endpoint = options.endpoint;
|
|
13
|
+
let apiKey = options.apiKey;
|
|
14
|
+
|
|
15
|
+
// If not provided via options, prompt interactively
|
|
16
|
+
if (!endpoint || !apiKey) {
|
|
17
|
+
console.log(chalk.blue('š§ BuildX CLI Setup'));
|
|
18
|
+
console.log(chalk.gray('Configure your API endpoint and API key to get started.\n'));
|
|
19
|
+
|
|
20
|
+
const currentConfig = configManager.getApiConfig();
|
|
21
|
+
|
|
22
|
+
const answers = await inquirer.prompt([
|
|
23
|
+
{
|
|
24
|
+
type: 'input',
|
|
25
|
+
name: 'endpoint',
|
|
26
|
+
message: 'Enter your API endpoint URL:',
|
|
27
|
+
default: currentConfig?.endpoint || 'https://api.buildx.com',
|
|
28
|
+
validate: (input: string) => {
|
|
29
|
+
if (!input.trim()) {
|
|
30
|
+
return 'Endpoint URL is required';
|
|
31
|
+
}
|
|
32
|
+
try {
|
|
33
|
+
new URL(input);
|
|
34
|
+
return true;
|
|
35
|
+
} catch {
|
|
36
|
+
return 'Please enter a valid URL';
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
type: 'password',
|
|
42
|
+
name: 'apiKey',
|
|
43
|
+
message: 'Enter your API key:',
|
|
44
|
+
default: currentConfig?.apiKey || '',
|
|
45
|
+
validate: (input: string) => {
|
|
46
|
+
if (!input.trim()) {
|
|
47
|
+
return 'API key is required';
|
|
48
|
+
}
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
]);
|
|
53
|
+
|
|
54
|
+
endpoint = answers.endpoint;
|
|
55
|
+
apiKey = answers.apiKey;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Save the configuration
|
|
59
|
+
configManager.setApiConfig({
|
|
60
|
+
endpoint: endpoint.trim(),
|
|
61
|
+
apiKey: apiKey.trim()
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
console.log(chalk.green('ā
Configuration saved successfully!'));
|
|
65
|
+
console.log(chalk.gray(`Endpoint: ${endpoint}`));
|
|
66
|
+
console.log(chalk.gray(`API Key: ${'*'.repeat(Math.min(apiKey.length, 8))}...`));
|
|
67
|
+
console.log(chalk.blue('\nYou can now use the CLI commands. Start with:'));
|
|
68
|
+
console.log(chalk.cyan(' buildx login'));
|
|
69
|
+
console.log(chalk.cyan(' buildx projects:list'));
|
|
70
|
+
|
|
71
|
+
} catch (error) {
|
|
72
|
+
console.error(chalk.red('ā Setup failed:'), error instanceof Error ? error.message : 'Unknown error');
|
|
73
|
+
process.exit(1);
|
|
74
|
+
}
|
|
75
|
+
});
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import { configManager } from '../../config/index';
|
|
4
|
+
|
|
5
|
+
export const configShowCommand = new Command('show')
|
|
6
|
+
.description('Show current configuration')
|
|
7
|
+
.action(() => {
|
|
8
|
+
try {
|
|
9
|
+
const apiConfig = configManager.getApiConfig();
|
|
10
|
+
const authConfig = configManager.getAuth();
|
|
11
|
+
const projectsConfig = configManager.getProjects();
|
|
12
|
+
const syncConfig = configManager.getSyncConfig();
|
|
13
|
+
|
|
14
|
+
console.log(chalk.blue('š§ BuildX CLI Configuration'));
|
|
15
|
+
console.log(chalk.gray('='.repeat(50)));
|
|
16
|
+
|
|
17
|
+
// API Configuration
|
|
18
|
+
console.log(chalk.yellow('\nš” API Configuration:'));
|
|
19
|
+
if (apiConfig) {
|
|
20
|
+
console.log(chalk.green(' ā
Configured'));
|
|
21
|
+
console.log(chalk.gray(` Endpoint: ${apiConfig.endpoint}`));
|
|
22
|
+
console.log(chalk.gray(` API Key: ${'*'.repeat(Math.min(apiConfig.apiKey.length, 8))}...`));
|
|
23
|
+
} else {
|
|
24
|
+
console.log(chalk.red(' ā Not configured'));
|
|
25
|
+
console.log(chalk.gray(' Run "buildx setup" to configure'));
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Authentication
|
|
29
|
+
console.log(chalk.yellow('\nš Authentication:'));
|
|
30
|
+
if (authConfig && configManager.isAuthenticated()) {
|
|
31
|
+
console.log(chalk.green(' ā
Authenticated'));
|
|
32
|
+
console.log(chalk.gray(` Token: ${authConfig.token.substring(0, 20)}...`));
|
|
33
|
+
if (authConfig.expiresAt) {
|
|
34
|
+
console.log(chalk.gray(` Expires: ${new Date(authConfig.expiresAt).toLocaleString()}`));
|
|
35
|
+
}
|
|
36
|
+
} else {
|
|
37
|
+
console.log(chalk.red(' ā Not authenticated'));
|
|
38
|
+
console.log(chalk.gray(' Run "buildx login" to authenticate'));
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Projects
|
|
42
|
+
console.log(chalk.yellow('\nš Projects:'));
|
|
43
|
+
if (projectsConfig?.list && projectsConfig.list.length > 0) {
|
|
44
|
+
console.log(chalk.green(` ā
${projectsConfig.list.length} project(s) available`));
|
|
45
|
+
if (projectsConfig.default) {
|
|
46
|
+
const defaultProject = projectsConfig.list.find(p => p.id === projectsConfig.default);
|
|
47
|
+
console.log(chalk.gray(` Default: ${defaultProject?.name || projectsConfig.default}`));
|
|
48
|
+
}
|
|
49
|
+
} else {
|
|
50
|
+
console.log(chalk.red(' ā No projects available'));
|
|
51
|
+
console.log(chalk.gray(' Run "buildx projects:list" to fetch projects'));
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Sync Configuration
|
|
55
|
+
console.log(chalk.yellow('\nš Sync Configuration:'));
|
|
56
|
+
if (syncConfig) {
|
|
57
|
+
console.log(chalk.gray(` Output Path: ${syncConfig.outputPath}`));
|
|
58
|
+
} else {
|
|
59
|
+
console.log(chalk.gray(' Using default sync configuration'));
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Configuration file location
|
|
63
|
+
console.log(chalk.yellow('\nš Configuration File:'));
|
|
64
|
+
console.log(chalk.gray(` ${configManager.getConfigPath()}`));
|
|
65
|
+
|
|
66
|
+
} catch (error) {
|
|
67
|
+
console.error(chalk.red('ā Failed to show configuration:'), error instanceof Error ? error.message : 'Unknown error');
|
|
68
|
+
process.exit(1);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import { configManager } from '../../config/index';
|
|
4
|
+
|
|
5
|
+
export const projectsCurrentCommand = new Command('projects:current')
|
|
6
|
+
.description('Show current default project')
|
|
7
|
+
.action(() => {
|
|
8
|
+
try {
|
|
9
|
+
const defaultProjectId = configManager.getDefaultProject();
|
|
10
|
+
|
|
11
|
+
if (!defaultProjectId) {
|
|
12
|
+
console.log(chalk.yellow('No default project set'));
|
|
13
|
+
console.log(chalk.blue('Use "buildx projects:set-default <project-id>" to set a default project'));
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const project = configManager.getProject(defaultProjectId);
|
|
18
|
+
|
|
19
|
+
if (!project) {
|
|
20
|
+
console.log(chalk.red('Error: Default project not found in configuration'));
|
|
21
|
+
console.log(chalk.blue('Project ID:'), defaultProjectId);
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
console.log(chalk.green('Current default project:'));
|
|
26
|
+
console.log(chalk.blue('Name:'), project.name);
|
|
27
|
+
console.log(chalk.blue('ID:'), project.id);
|
|
28
|
+
if (project.apiUrl) {
|
|
29
|
+
console.log(chalk.blue('API URL:'), project.apiUrl);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
} catch (error) {
|
|
33
|
+
console.error(chalk.red('Error:'), error instanceof Error ? error.message : 'Unknown error');
|
|
34
|
+
process.exit(1);
|
|
35
|
+
}
|
|
36
|
+
});
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import ora from 'ora';
|
|
4
|
+
import { configManager } from '../../config/index';
|
|
5
|
+
import { apiService } from '../../services/api';
|
|
6
|
+
|
|
7
|
+
export const projectsListCommand = new Command('projects:list')
|
|
8
|
+
.description('List available projects')
|
|
9
|
+
.action(async () => {
|
|
10
|
+
try {
|
|
11
|
+
// Check API configuration
|
|
12
|
+
if (!apiService.isConfigured()) {
|
|
13
|
+
console.error(chalk.red('ā API not configured'));
|
|
14
|
+
console.log(chalk.yellow('Please configure your API endpoint and API key first:'));
|
|
15
|
+
console.log(chalk.cyan(' buildx config setup'));
|
|
16
|
+
process.exit(1);
|
|
17
|
+
}
|
|
18
|
+
// Check authentication
|
|
19
|
+
if (!configManager.isAuthenticated()) {
|
|
20
|
+
console.error(chalk.red('Error: Not authenticated'));
|
|
21
|
+
console.log(chalk.yellow('Run "buildx login" to authenticate first'));
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const spinner = ora('Fetching projects...').start();
|
|
26
|
+
|
|
27
|
+
try {
|
|
28
|
+
const projects = await apiService.getProjects();
|
|
29
|
+
spinner.succeed(`Found ${projects.length} projects`);
|
|
30
|
+
|
|
31
|
+
if (projects.length === 0) {
|
|
32
|
+
console.log(chalk.yellow('No projects found'));
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const defaultProject = configManager.getDefaultProject();
|
|
37
|
+
|
|
38
|
+
console.log('\n' + chalk.blue.bold('Available Projects:'));
|
|
39
|
+
projects.forEach(project => {
|
|
40
|
+
const isDefault = project.id === defaultProject;
|
|
41
|
+
const prefix = isDefault ? chalk.green('ā
') : ' ';
|
|
42
|
+
console.log(`${prefix}${chalk.bold(project.name)} (${project.id})`);
|
|
43
|
+
if (project.apiUrl) {
|
|
44
|
+
console.log(` API: ${chalk.gray(project.apiUrl)}`);
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
if (defaultProject) {
|
|
49
|
+
console.log(chalk.green('\nā
Default project'));
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
} catch (error) {
|
|
53
|
+
spinner.fail('Failed to fetch projects');
|
|
54
|
+
throw error;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
} catch (error) {
|
|
58
|
+
console.error(chalk.red('Error:'), error instanceof Error ? error.message : 'Unknown error');
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
});
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import { configManager } from '../../config/index';
|
|
4
|
+
|
|
5
|
+
export const projectsSetDefaultCommand = new Command('projects:set-default')
|
|
6
|
+
.description('Set default project')
|
|
7
|
+
.argument('<project-id>', 'Project ID to set as default')
|
|
8
|
+
.action(async (projectId: string) => {
|
|
9
|
+
try {
|
|
10
|
+
// Check authentication
|
|
11
|
+
if (!configManager.isAuthenticated()) {
|
|
12
|
+
console.error(chalk.red('Error: Not authenticated'));
|
|
13
|
+
console.log(chalk.yellow('Run "buildx login" to authenticate first'));
|
|
14
|
+
process.exit(1);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Check if project exists in config
|
|
18
|
+
const project = configManager.getProject(projectId);
|
|
19
|
+
if (!project) {
|
|
20
|
+
console.error(chalk.red('Error: Project not found in configuration'));
|
|
21
|
+
console.log(chalk.yellow('Run "buildx projects:list" to see available projects'));
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
configManager.setDefaultProject(projectId);
|
|
26
|
+
console.log(chalk.green('ā Default project set to:'), project.name);
|
|
27
|
+
console.log(chalk.blue('Project ID:'), projectId);
|
|
28
|
+
|
|
29
|
+
} catch (error) {
|
|
30
|
+
console.error(chalk.red('Error:'), error instanceof Error ? error.message : 'Unknown error');
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
33
|
+
});
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
import ora from "ora";
|
|
4
|
+
import fs from "fs-extra";
|
|
5
|
+
import path from "path";
|
|
6
|
+
import { configManager } from "../config/index";
|
|
7
|
+
import { apiService } from "../services/api";
|
|
8
|
+
|
|
9
|
+
export const syncCommand = new Command("sync")
|
|
10
|
+
.description("Sync schema and generate TypeScript types")
|
|
11
|
+
.requiredOption("-p, --project-id <project-id>", "Project ID to sync schema from")
|
|
12
|
+
.option("-o, --output <path>", "Output path for generated types", "./generated/types.ts")
|
|
13
|
+
.option("-u, --api-url <url>", "Custom API base URL")
|
|
14
|
+
.option("-f, --force", "Force overwrite existing files")
|
|
15
|
+
.action(async (options) => {
|
|
16
|
+
try {
|
|
17
|
+
// Check API configuration
|
|
18
|
+
if (!apiService.isConfigured()) {
|
|
19
|
+
console.error(chalk.red('ā API not configured'));
|
|
20
|
+
console.log(chalk.yellow('Please configure your API endpoint and API key first:'));
|
|
21
|
+
console.log(chalk.cyan(' buildx config setup'));
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
// Check authentication
|
|
25
|
+
if (!configManager.isAuthenticated()) {
|
|
26
|
+
console.error(chalk.red("Error: Not authenticated"));
|
|
27
|
+
console.log(chalk.yellow("Run \"buildx login\" to authenticate first"));
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const { projectId, output, apiUrl, force } = options;
|
|
32
|
+
|
|
33
|
+
// Set custom API URL if provided
|
|
34
|
+
if (apiUrl) {
|
|
35
|
+
apiService.setBaseUrl(apiUrl);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Fetch schema
|
|
39
|
+
const spinner = ora("Fetching TypeScript types...").start();
|
|
40
|
+
|
|
41
|
+
try {
|
|
42
|
+
const typescriptCode = await apiService.getSchema(projectId);
|
|
43
|
+
spinner.succeed("TypeScript types fetched successfully");
|
|
44
|
+
|
|
45
|
+
// Ensure output directory exists
|
|
46
|
+
const outputDir = path.dirname(output);
|
|
47
|
+
await fs.ensureDir(outputDir);
|
|
48
|
+
|
|
49
|
+
// Write the TypeScript code directly to file
|
|
50
|
+
await fs.writeFile(output, typescriptCode, "utf8");
|
|
51
|
+
|
|
52
|
+
console.log(chalk.green("ā Types generated at:"), output);
|
|
53
|
+
console.log(chalk.blue("File size:"), `${(typescriptCode.length / 1024).toFixed(2)} KB`);
|
|
54
|
+
|
|
55
|
+
} catch (error) {
|
|
56
|
+
spinner.fail("Failed to fetch TypeScript types");
|
|
57
|
+
throw error;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
} catch (error) {
|
|
61
|
+
console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Unknown error");
|
|
62
|
+
process.exit(1);
|
|
63
|
+
}
|
|
64
|
+
});
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import Conf from "conf";
|
|
2
|
+
import { GlobalConfig, AuthConfig, ProjectsConfig, SyncConfig, ApiConfig } from "../types/index";
|
|
3
|
+
|
|
4
|
+
export class ConfigManager {
|
|
5
|
+
private config: Conf<GlobalConfig>;
|
|
6
|
+
|
|
7
|
+
constructor() {
|
|
8
|
+
this.config = new Conf<GlobalConfig>({
|
|
9
|
+
projectName: "buildx-cli",
|
|
10
|
+
schema: {
|
|
11
|
+
api: {
|
|
12
|
+
type: "object",
|
|
13
|
+
properties: {
|
|
14
|
+
endpoint: { type: "string" },
|
|
15
|
+
apiKey: { type: "string" }
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
auth: {
|
|
19
|
+
type: "object",
|
|
20
|
+
properties: {
|
|
21
|
+
token: { type: "string" },
|
|
22
|
+
expiresAt: { type: "string" }
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
projects: {
|
|
26
|
+
type: "object",
|
|
27
|
+
properties: {
|
|
28
|
+
default: { type: "string" },
|
|
29
|
+
list: { type: "array" }
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
sync: {
|
|
33
|
+
type: "object",
|
|
34
|
+
properties: {
|
|
35
|
+
outputPath: { type: "string" }
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
defaults: {
|
|
40
|
+
sync: {
|
|
41
|
+
outputPath: "./generated/types.ts"
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// API configuration methods
|
|
48
|
+
getApiConfig(): ApiConfig | undefined {
|
|
49
|
+
return this.config.get("api");
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
setApiConfig(api: ApiConfig): void {
|
|
53
|
+
this.config.set("api", api);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
clearApiConfig(): void {
|
|
57
|
+
this.config.delete("api");
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
isApiConfigured(): boolean {
|
|
61
|
+
const api = this.getApiConfig();
|
|
62
|
+
return !!(api?.endpoint && api?.apiKey);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Auth methods
|
|
66
|
+
getAuth(): AuthConfig | undefined {
|
|
67
|
+
return this.config.get("auth");
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
setAuth(auth: AuthConfig): void {
|
|
71
|
+
this.config.set("auth", auth);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
clearAuth(): void {
|
|
75
|
+
this.config.delete("auth");
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
isAuthenticated(): boolean {
|
|
79
|
+
const auth = this.getAuth();
|
|
80
|
+
if (!auth || !auth.token) {
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Check if token is expired
|
|
85
|
+
if (auth.expiresAt) {
|
|
86
|
+
const expiresAt = new Date(auth.expiresAt);
|
|
87
|
+
if (expiresAt < new Date()) {
|
|
88
|
+
this.clearAuth();
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return true;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Projects methods
|
|
97
|
+
getProjects(): ProjectsConfig | undefined {
|
|
98
|
+
return this.config.get("projects");
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
setProjects(projects: ProjectsConfig): void {
|
|
102
|
+
this.config.set("projects", projects);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
getDefaultProject(): string | undefined {
|
|
106
|
+
const projects = this.getProjects();
|
|
107
|
+
return projects?.default;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
setDefaultProject(projectId: string): void {
|
|
111
|
+
const projects = this.getProjects() || { list: [] };
|
|
112
|
+
projects.default = projectId;
|
|
113
|
+
this.setProjects(projects);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
addProject(project: { id: string; name: string; apiUrl?: string }): void {
|
|
117
|
+
const projects = this.getProjects() || { list: [] };
|
|
118
|
+
const existingIndex = projects.list.findIndex(p => p.id === project.id);
|
|
119
|
+
|
|
120
|
+
if (existingIndex >= 0) {
|
|
121
|
+
projects.list[existingIndex] = project;
|
|
122
|
+
} else {
|
|
123
|
+
projects.list.push(project);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
this.setProjects(projects);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
getProject(projectId: string): { id: string; name: string; apiUrl?: string } | undefined {
|
|
130
|
+
const projects = this.getProjects();
|
|
131
|
+
return projects?.list.find(p => p.id === projectId);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Sync methods
|
|
135
|
+
getSyncConfig(): SyncConfig | undefined {
|
|
136
|
+
return this.config.get("sync");
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
setSyncConfig(sync: SyncConfig): void {
|
|
140
|
+
this.config.set("sync", sync);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Utility methods
|
|
144
|
+
getConfigPath(): string {
|
|
145
|
+
return this.config.path;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
clear(): void {
|
|
149
|
+
this.config.clear();
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Export singleton instance
|
|
154
|
+
export const configManager = new ConfigManager();
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { Command } from 'commander';
|
|
4
|
+
import chalk from 'chalk';
|
|
5
|
+
import fs from 'fs';
|
|
6
|
+
|
|
7
|
+
// Import commands
|
|
8
|
+
import { loginCommand } from './commands/auth/login';
|
|
9
|
+
import { logoutCommand } from './commands/auth/logout';
|
|
10
|
+
import { authStatusCommand } from './commands/auth/status';
|
|
11
|
+
import { syncCommand } from './commands/sync';
|
|
12
|
+
import { projectsListCommand } from './commands/projects/list';
|
|
13
|
+
import { projectsSetDefaultCommand } from './commands/projects/set-default';
|
|
14
|
+
import { projectsCurrentCommand } from './commands/projects/current';
|
|
15
|
+
import { configCommand } from './commands/config/index';
|
|
16
|
+
|
|
17
|
+
const program = new Command();
|
|
18
|
+
const packageJson = JSON.parse(fs.readFileSync('./package.json', 'utf8'));
|
|
19
|
+
const version = packageJson.version;
|
|
20
|
+
|
|
21
|
+
// Set up the CLI
|
|
22
|
+
program
|
|
23
|
+
.name('buildx')
|
|
24
|
+
.description('BuildX CLI - API authentication and schema synchronization tool')
|
|
25
|
+
.version(version, '-v, --version');
|
|
26
|
+
|
|
27
|
+
// Add commands
|
|
28
|
+
program.addCommand(loginCommand);
|
|
29
|
+
program.addCommand(logoutCommand);
|
|
30
|
+
program.addCommand(authStatusCommand);
|
|
31
|
+
program.addCommand(syncCommand);
|
|
32
|
+
program.addCommand(projectsListCommand);
|
|
33
|
+
program.addCommand(projectsSetDefaultCommand);
|
|
34
|
+
program.addCommand(projectsCurrentCommand);
|
|
35
|
+
program.addCommand(configCommand);
|
|
36
|
+
|
|
37
|
+
// Global error handler
|
|
38
|
+
program.exitOverride();
|
|
39
|
+
|
|
40
|
+
try {
|
|
41
|
+
program.parse();
|
|
42
|
+
} catch (err) {
|
|
43
|
+
if (err instanceof Error) {
|
|
44
|
+
console.error(chalk.red('Error:'), err.message);
|
|
45
|
+
} else {
|
|
46
|
+
console.error(chalk.red('An unexpected error occurred'));
|
|
47
|
+
}
|
|
48
|
+
process.exit(1);
|
|
49
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import axios, { AxiosInstance, AxiosRequestConfig, InternalAxiosRequestConfig } from "axios";
|
|
2
|
+
import { configManager } from "../config/index";
|
|
3
|
+
import { ApiResponse, LoginCredentials, LoginResponse } from "../types/index";
|
|
4
|
+
|
|
5
|
+
export class ApiService {
|
|
6
|
+
private client: AxiosInstance;
|
|
7
|
+
|
|
8
|
+
constructor() {
|
|
9
|
+
this.client = axios.create({
|
|
10
|
+
timeout: 30000,
|
|
11
|
+
headers: {
|
|
12
|
+
"Content-Type": "application/json",
|
|
13
|
+
"User-Agent": "buildx-cli/1.0.0"
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
// Add request interceptor to include auth token and API key
|
|
18
|
+
this.client.interceptors.request.use((config: InternalAxiosRequestConfig) => {
|
|
19
|
+
// Ensure client is initialized with current config before each request
|
|
20
|
+
this.initializeClient();
|
|
21
|
+
|
|
22
|
+
// Add API key from configuration
|
|
23
|
+
const apiConfig = configManager.getApiConfig();
|
|
24
|
+
if (apiConfig?.apiKey) {
|
|
25
|
+
config.headers["X-API-Key"] = apiConfig.apiKey;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Add auth token
|
|
29
|
+
const auth = configManager.getAuth();
|
|
30
|
+
if (auth?.token) {
|
|
31
|
+
config.headers.Authorization = `Bearer ${auth.token}`;
|
|
32
|
+
}
|
|
33
|
+
return config;
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// Add response interceptor for error handling
|
|
37
|
+
this.client.interceptors.response.use(
|
|
38
|
+
(response) => response,
|
|
39
|
+
(error: any) => {
|
|
40
|
+
if (error.response?.status === 401) {
|
|
41
|
+
configManager.clearAuth();
|
|
42
|
+
throw new Error("Authentication failed. Please login again.");
|
|
43
|
+
}
|
|
44
|
+
throw error;
|
|
45
|
+
}
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
private initializeClient(): void {
|
|
50
|
+
const apiConfig = configManager.getApiConfig();
|
|
51
|
+
if (apiConfig?.endpoint) {
|
|
52
|
+
this.client.defaults.baseURL = apiConfig.endpoint;
|
|
53
|
+
console.log(`Debug: Setting base URL to ${apiConfig.endpoint}`);
|
|
54
|
+
} else {
|
|
55
|
+
throw new Error("API endpoint not configured. Please run 'buildx config setup' first.");
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
private async request<T>(config: AxiosRequestConfig): Promise<ApiResponse<T>> {
|
|
60
|
+
// Always set baseURL from config before making the request
|
|
61
|
+
const apiConfig = configManager.getApiConfig();
|
|
62
|
+
if (!apiConfig?.endpoint) {
|
|
63
|
+
throw new Error("API endpoint not configured. Please run 'buildx config setup' first.");
|
|
64
|
+
}
|
|
65
|
+
config.baseURL = apiConfig.endpoint;
|
|
66
|
+
try {
|
|
67
|
+
const response = await this.client.request(config);
|
|
68
|
+
return {
|
|
69
|
+
data: response.data,
|
|
70
|
+
status: response.status,
|
|
71
|
+
message: response.statusText
|
|
72
|
+
};
|
|
73
|
+
} catch (error: any) {
|
|
74
|
+
if (error.response) {
|
|
75
|
+
throw new Error(`API Error: ${error.response.status} - ${error.response.data?.message || error.response.statusText}`);
|
|
76
|
+
} else if (error.request) {
|
|
77
|
+
throw new Error("Network error: Unable to connect to the API");
|
|
78
|
+
} else {
|
|
79
|
+
throw new Error(`Request error: ${error.message}`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async login(credentials: LoginCredentials): Promise<LoginResponse> {
|
|
85
|
+
const response = await this.request<LoginResponse>({
|
|
86
|
+
method: "POST",
|
|
87
|
+
url: "/auth/login",
|
|
88
|
+
data: credentials
|
|
89
|
+
});
|
|
90
|
+
return response.data;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async getProjects(): Promise<{ id: string; name: string; apiUrl?: string }[]> {
|
|
94
|
+
const response = await this.request<{ projects: { id: string; name: string; apiUrl?: string }[] }>({
|
|
95
|
+
method: "GET",
|
|
96
|
+
url: "/projects"
|
|
97
|
+
});
|
|
98
|
+
return response.data.projects;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
async getSchema(projectId: string): Promise<string> {
|
|
102
|
+
const response = await this.request<string>({
|
|
103
|
+
method: "GET",
|
|
104
|
+
url: `/project/${projectId}/collections/schema`
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
return response.data;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async getMe(): Promise<{ username: string;[key: string]: any }> {
|
|
111
|
+
const response = await this.request<{ username: string;[key: string]: any }>({
|
|
112
|
+
method: "GET",
|
|
113
|
+
url: "/auth/me"
|
|
114
|
+
});
|
|
115
|
+
return response.data;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
setBaseUrl(baseUrl: string): void {
|
|
119
|
+
this.client.defaults.baseURL = baseUrl;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
getBaseUrl(): string {
|
|
123
|
+
return this.client.defaults.baseURL || '';
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
isConfigured(): boolean {
|
|
127
|
+
return configManager.isApiConfigured();
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Export singleton instance
|
|
132
|
+
export const apiService = new ApiService();
|