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,119 @@
|
|
|
1
|
+
import globals from "globals";
|
|
2
|
+
import typescriptEslint from "@typescript-eslint/eslint-plugin";
|
|
3
|
+
import tsParser from "@typescript-eslint/parser";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
import js from "@eslint/js";
|
|
7
|
+
import { FlatCompat } from "@eslint/eslintrc";
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
11
|
+
const __dirname = path.dirname(__filename);
|
|
12
|
+
const compat = new FlatCompat({
|
|
13
|
+
baseDirectory: __dirname,
|
|
14
|
+
recommendedConfig: js.configs.recommended,
|
|
15
|
+
allConfig: js.configs.all
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
export default [
|
|
19
|
+
{
|
|
20
|
+
ignores: ["build/**", "dist/**", "node_modules/**", "scripts/**", "rules/**", "docs/**"], // Excludes all files and subdirectories in `dest`
|
|
21
|
+
},
|
|
22
|
+
...compat.extends(
|
|
23
|
+
"eslint:recommended",
|
|
24
|
+
"plugin:vitest-globals/recommended",
|
|
25
|
+
"plugin:@typescript-eslint/recommended",
|
|
26
|
+
"plugin:import/recommended",
|
|
27
|
+
"plugin:import/typescript"
|
|
28
|
+
),
|
|
29
|
+
{
|
|
30
|
+
plugins: {
|
|
31
|
+
"@typescript-eslint": typescriptEslint,
|
|
32
|
+
},
|
|
33
|
+
languageOptions: {
|
|
34
|
+
parser: tsParser,
|
|
35
|
+
ecmaVersion: 2020,
|
|
36
|
+
sourceType: "module",
|
|
37
|
+
globals: {
|
|
38
|
+
...globals.node,
|
|
39
|
+
vitest: "readonly",
|
|
40
|
+
},
|
|
41
|
+
parserOptions: {
|
|
42
|
+
"ecmaVersion": 2020,
|
|
43
|
+
"sourceType": "module",
|
|
44
|
+
"project": "./tsconfig.json"
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
files: ["**/*.ts"],
|
|
48
|
+
rules: {
|
|
49
|
+
|
|
50
|
+
"@typescript-eslint/no-explicit-any": "off",
|
|
51
|
+
"@typescript-eslint/no-unused-vars": ["warn", {
|
|
52
|
+
argsIgnorePattern: "^_",
|
|
53
|
+
varsIgnorePattern: "^_",
|
|
54
|
+
caughtErrors: "none",
|
|
55
|
+
}],
|
|
56
|
+
indent: ["error", "tab", {
|
|
57
|
+
SwitchCase: 1,
|
|
58
|
+
}],
|
|
59
|
+
|
|
60
|
+
"import/no-unresolved": "error",
|
|
61
|
+
"import/named": "error",
|
|
62
|
+
"import/default": "error",
|
|
63
|
+
"import/namespace": "error",
|
|
64
|
+
|
|
65
|
+
"no-tabs": 0,
|
|
66
|
+
quotes: ["error", "double"],
|
|
67
|
+
"max-len": 0,
|
|
68
|
+
semi: "error",
|
|
69
|
+
"no-debugger": "warn",
|
|
70
|
+
"object-curly-spacing": ["error", "always"],
|
|
71
|
+
|
|
72
|
+
"comma-spacing": ["error", {
|
|
73
|
+
before: false,
|
|
74
|
+
after: true,
|
|
75
|
+
}],
|
|
76
|
+
|
|
77
|
+
"no-multiple-empty-lines": "error",
|
|
78
|
+
"no-implicit-any": "off",
|
|
79
|
+
"prefer-const": "off",
|
|
80
|
+
"no-undef": "error",
|
|
81
|
+
|
|
82
|
+
// No console.log
|
|
83
|
+
"no-console": "warn",
|
|
84
|
+
|
|
85
|
+
},
|
|
86
|
+
"settings": {
|
|
87
|
+
"import/resolver": {
|
|
88
|
+
"typescript": {
|
|
89
|
+
"alwaysTryTypes": true,
|
|
90
|
+
"project": "./tsconfig.json"
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
files: ["**/*.cjs"],
|
|
97
|
+
languageOptions: {
|
|
98
|
+
ecmaVersion: 2020,
|
|
99
|
+
sourceType: "script", // CommonJS files are treated as scripts
|
|
100
|
+
globals: {
|
|
101
|
+
require: "readonly",
|
|
102
|
+
module: "readonly",
|
|
103
|
+
exports: "readonly",
|
|
104
|
+
process: "readonly",
|
|
105
|
+
__dirname: "readonly",
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
rules: {
|
|
109
|
+
"@typescript-eslint/no-require-imports": "off",
|
|
110
|
+
// "no-undef": "off"
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
files: ["eslint.config.mjs"],
|
|
115
|
+
rules: {
|
|
116
|
+
"import/no-unresolved": "off"
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
];
|
package/jest.config.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
module.exports = {
|
|
2
|
+
preset: 'ts-jest',
|
|
3
|
+
testEnvironment: 'node',
|
|
4
|
+
roots: ['<rootDir>/src'],
|
|
5
|
+
testMatch: ['**/__tests__/**/*.ts', '**/?(*.)+(spec|test).ts'],
|
|
6
|
+
transform: {
|
|
7
|
+
'^.+\\.ts$': 'ts-jest',
|
|
8
|
+
},
|
|
9
|
+
collectCoverageFrom: [
|
|
10
|
+
'src/**/*.ts',
|
|
11
|
+
'!src/**/*.d.ts',
|
|
12
|
+
'!src/**/__tests__/**',
|
|
13
|
+
],
|
|
14
|
+
coverageDirectory: 'coverage',
|
|
15
|
+
coverageReporters: ['text', 'lcov', 'html'],
|
|
16
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "buildx-cli",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "CLI tool for BuildX API with authentication and schema synchronization",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"buildx": "dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"clean": "rimraf build",
|
|
12
|
+
"build": "yarn clean && rollup -c",
|
|
13
|
+
"dev": "tsx src/index.ts",
|
|
14
|
+
"start": "node dist/index.js",
|
|
15
|
+
"test": "jest",
|
|
16
|
+
"lint": "eslint src/**/*.ts",
|
|
17
|
+
"format": "prettier --write src/**/*.ts",
|
|
18
|
+
"prepublishOnly": "npm run build"
|
|
19
|
+
},
|
|
20
|
+
"keywords": [
|
|
21
|
+
"cli",
|
|
22
|
+
"api",
|
|
23
|
+
"buildx",
|
|
24
|
+
"typescript",
|
|
25
|
+
"schema"
|
|
26
|
+
],
|
|
27
|
+
"author": "BuildX Team",
|
|
28
|
+
"license": "MIT",
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@rollup/plugin-json": "^6.1.0",
|
|
31
|
+
"axios": "^1.10.0",
|
|
32
|
+
"chalk": "^5.3.0",
|
|
33
|
+
"commander": "^11.1.0",
|
|
34
|
+
"conf": "^14.0.0",
|
|
35
|
+
"fs-extra": "^11.1.1",
|
|
36
|
+
"inquirer": "^9.2.12",
|
|
37
|
+
"ora": "^7.0.1"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@rollup/plugin-commonjs": "^28.0.6",
|
|
41
|
+
"@rollup/plugin-node-resolve": "^16.0.1",
|
|
42
|
+
"@rollup/plugin-terser": "^0.4.4",
|
|
43
|
+
"@types/fs-extra": "^11.0.4",
|
|
44
|
+
"@types/inquirer": "^9.0.7",
|
|
45
|
+
"@types/jest": "^29.5.6",
|
|
46
|
+
"@types/node": "^20.8.0",
|
|
47
|
+
"@typescript-eslint/eslint-plugin": "^8.14.0",
|
|
48
|
+
"@typescript-eslint/parser": "^8.14.0",
|
|
49
|
+
"@typescript-eslint/rule-tester": "^8.19.1",
|
|
50
|
+
"concurrently": "^9.1.2",
|
|
51
|
+
"esbuild": "^0.25.5",
|
|
52
|
+
"eslint": "^9.29.0",
|
|
53
|
+
"eslint-import-resolver-typescript": "^3.7.0",
|
|
54
|
+
"eslint-plugin-import": "^2.31.0",
|
|
55
|
+
"eslint-plugin-vitest-globals": "^1.5.0",
|
|
56
|
+
"jest": "^29.7.0",
|
|
57
|
+
"madge": "^8.0.0",
|
|
58
|
+
"prettier": "^3.0.3",
|
|
59
|
+
"rimraf": "^6.0.1",
|
|
60
|
+
"rollup": "^4.44.0",
|
|
61
|
+
"rollup-plugin-dts": "^6.2.1",
|
|
62
|
+
"rollup-plugin-esbuild": "^6.2.1",
|
|
63
|
+
"tsx": "^4.20.3",
|
|
64
|
+
"typescript": "^5.0.0"
|
|
65
|
+
},
|
|
66
|
+
"engines": {
|
|
67
|
+
"node": ">=16.0.0"
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import esbuild from 'rollup-plugin-esbuild';
|
|
2
|
+
import dts from 'rollup-plugin-dts';
|
|
3
|
+
import { nodeResolve } from '@rollup/plugin-node-resolve';
|
|
4
|
+
import commonjs from '@rollup/plugin-commonjs';
|
|
5
|
+
import terser from '@rollup/plugin-terser';
|
|
6
|
+
import json from '@rollup/plugin-json';
|
|
7
|
+
|
|
8
|
+
const useTerser = true;
|
|
9
|
+
|
|
10
|
+
const external = []
|
|
11
|
+
|
|
12
|
+
const plugins = [
|
|
13
|
+
nodeResolve({
|
|
14
|
+
preferBuiltins: true,
|
|
15
|
+
exportConditions: ['node']
|
|
16
|
+
}),
|
|
17
|
+
commonjs({
|
|
18
|
+
ignoreDynamicRequires: true
|
|
19
|
+
}),
|
|
20
|
+
esbuild({
|
|
21
|
+
target: 'node16',
|
|
22
|
+
platform: 'node'
|
|
23
|
+
}),
|
|
24
|
+
json()
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
if (useTerser) {
|
|
28
|
+
plugins.push(terser(
|
|
29
|
+
{
|
|
30
|
+
mangle: false, // Keep variable names
|
|
31
|
+
module: true, // Tells terser you're using ES modules
|
|
32
|
+
keep_classnames: true,
|
|
33
|
+
keep_fnames: true,
|
|
34
|
+
}
|
|
35
|
+
))
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export default [
|
|
39
|
+
{
|
|
40
|
+
input: 'src/index.ts',
|
|
41
|
+
output: [{
|
|
42
|
+
dir: 'dist',
|
|
43
|
+
format: 'esm',
|
|
44
|
+
entryFileNames: 'index.js',
|
|
45
|
+
exports: 'named',
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
dir: 'dist',
|
|
49
|
+
format: 'cjs',
|
|
50
|
+
entryFileNames: 'index.cjs',
|
|
51
|
+
exports: 'named',
|
|
52
|
+
}],
|
|
53
|
+
external,
|
|
54
|
+
plugins
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
input: 'src/index.ts',
|
|
58
|
+
output: [{
|
|
59
|
+
dir: 'dist',
|
|
60
|
+
format: 'esm',
|
|
61
|
+
entryFileNames: 'index.d.ts',
|
|
62
|
+
}],
|
|
63
|
+
plugins: [dts()]
|
|
64
|
+
}]
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { ConfigManager } from '../config';
|
|
2
|
+
|
|
3
|
+
describe('ConfigManager', () => {
|
|
4
|
+
let configManager: ConfigManager;
|
|
5
|
+
|
|
6
|
+
beforeEach(() => {
|
|
7
|
+
configManager = new ConfigManager();
|
|
8
|
+
configManager.clear();
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
afterEach(() => {
|
|
12
|
+
configManager.clear();
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
describe('Authentication', () => {
|
|
16
|
+
it('should store and retrieve auth token', () => {
|
|
17
|
+
const auth = {
|
|
18
|
+
token: 'test-token',
|
|
19
|
+
expiresAt: new Date().toISOString()
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
configManager.setAuth(auth);
|
|
23
|
+
const retrieved = configManager.getAuth();
|
|
24
|
+
|
|
25
|
+
expect(retrieved).toEqual(auth);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('should clear auth token', () => {
|
|
29
|
+
const auth = {
|
|
30
|
+
token: 'test-token',
|
|
31
|
+
expiresAt: new Date().toISOString()
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
configManager.setAuth(auth);
|
|
35
|
+
configManager.clearAuth();
|
|
36
|
+
|
|
37
|
+
expect(configManager.getAuth()).toBeUndefined();
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('should check if authenticated', () => {
|
|
41
|
+
expect(configManager.isAuthenticated()).toBe(false);
|
|
42
|
+
|
|
43
|
+
const auth = {
|
|
44
|
+
token: 'test-token',
|
|
45
|
+
expiresAt: new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString() // 1 day from now
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
configManager.setAuth(auth);
|
|
49
|
+
expect(configManager.isAuthenticated()).toBe(true);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('should handle expired token', () => {
|
|
53
|
+
const auth = {
|
|
54
|
+
token: 'test-token',
|
|
55
|
+
expiresAt: new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString() // 1 day ago
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
configManager.setAuth(auth);
|
|
59
|
+
expect(configManager.isAuthenticated()).toBe(false);
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
describe('Projects', () => {
|
|
64
|
+
it('should store and retrieve projects', () => {
|
|
65
|
+
const projects = {
|
|
66
|
+
default: 'project-1',
|
|
67
|
+
list: [
|
|
68
|
+
{ id: 'project-1', name: 'Project 1' },
|
|
69
|
+
{ id: 'project-2', name: 'Project 2' }
|
|
70
|
+
]
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
configManager.setProjects(projects);
|
|
74
|
+
const retrieved = configManager.getProjects();
|
|
75
|
+
|
|
76
|
+
expect(retrieved).toEqual(projects);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it('should set default project', () => {
|
|
80
|
+
const projects = {
|
|
81
|
+
list: [
|
|
82
|
+
{ id: 'project-1', name: 'Project 1' },
|
|
83
|
+
{ id: 'project-2', name: 'Project 2' }
|
|
84
|
+
]
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
configManager.setProjects(projects);
|
|
88
|
+
configManager.setDefaultProject('project-2');
|
|
89
|
+
|
|
90
|
+
const updated = configManager.getProjects();
|
|
91
|
+
expect(updated?.default).toBe('project-2');
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('should add project', () => {
|
|
95
|
+
const project = { id: 'project-1', name: 'Project 1' };
|
|
96
|
+
configManager.addProject(project);
|
|
97
|
+
|
|
98
|
+
const retrieved = configManager.getProject('project-1');
|
|
99
|
+
expect(retrieved).toEqual(project);
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
});
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import inquirer from 'inquirer';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
import ora from 'ora';
|
|
5
|
+
import { configManager } from '../../config/index';
|
|
6
|
+
import { apiService } from '../../services/api';
|
|
7
|
+
import { LoginOptions } from '../../types/index';
|
|
8
|
+
|
|
9
|
+
export const loginCommand = new Command('login')
|
|
10
|
+
.description('Authenticate with BuildX API')
|
|
11
|
+
.option('-t, --token <token>', 'API token for authentication')
|
|
12
|
+
.option('-u, --username <username>', 'Username for authentication')
|
|
13
|
+
.option('-p, --password <password>', 'Password for authentication')
|
|
14
|
+
.option('-i, --interactive', 'Interactive login mode')
|
|
15
|
+
.action(async (options: LoginOptions) => {
|
|
16
|
+
try {
|
|
17
|
+
// Check if API is configured
|
|
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
|
+
|
|
25
|
+
let token = options.token;
|
|
26
|
+
let username = options.username;
|
|
27
|
+
let password = options.password;
|
|
28
|
+
|
|
29
|
+
// If no credentials provided, prompt for them
|
|
30
|
+
if (!token && !username && !password) {
|
|
31
|
+
const answers = await inquirer.prompt([
|
|
32
|
+
{
|
|
33
|
+
type: 'list',
|
|
34
|
+
name: 'authMethod',
|
|
35
|
+
message: 'Choose authentication method:',
|
|
36
|
+
choices: [
|
|
37
|
+
{ name: 'Username & Password', value: 'credentials' },
|
|
38
|
+
{ name: 'API Token', value: 'token' }
|
|
39
|
+
]
|
|
40
|
+
}
|
|
41
|
+
]);
|
|
42
|
+
|
|
43
|
+
if (answers.authMethod === 'credentials') {
|
|
44
|
+
const credentialAnswers = await inquirer.prompt([
|
|
45
|
+
{
|
|
46
|
+
type: 'input',
|
|
47
|
+
name: 'username',
|
|
48
|
+
message: 'Enter your username:',
|
|
49
|
+
validate: (input: string) => {
|
|
50
|
+
if (!input || input.trim().length === 0) {
|
|
51
|
+
return 'Username is required';
|
|
52
|
+
}
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
type: 'password',
|
|
58
|
+
name: 'password',
|
|
59
|
+
message: 'Enter your password:',
|
|
60
|
+
validate: (input: string) => {
|
|
61
|
+
if (!input || input.trim().length === 0) {
|
|
62
|
+
return 'Password is required';
|
|
63
|
+
}
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
]);
|
|
68
|
+
username = credentialAnswers.username;
|
|
69
|
+
password = credentialAnswers.password;
|
|
70
|
+
} else {
|
|
71
|
+
const tokenAnswer = await inquirer.prompt([
|
|
72
|
+
{
|
|
73
|
+
type: 'password',
|
|
74
|
+
name: 'token',
|
|
75
|
+
message: 'Enter your API token:',
|
|
76
|
+
validate: (input: string) => {
|
|
77
|
+
if (!input || input.trim().length === 0) {
|
|
78
|
+
return 'Token is required';
|
|
79
|
+
}
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
]);
|
|
84
|
+
token = tokenAnswer.token;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const spinner = ora('Authenticating...').start();
|
|
89
|
+
|
|
90
|
+
try {
|
|
91
|
+
// Only support username/password authentication
|
|
92
|
+
if (!username || !password) {
|
|
93
|
+
const credentialAnswers = await inquirer.prompt([
|
|
94
|
+
{
|
|
95
|
+
type: 'input',
|
|
96
|
+
name: 'username',
|
|
97
|
+
message: 'Enter your username:',
|
|
98
|
+
validate: (input: string) => {
|
|
99
|
+
if (!input || input.trim().length === 0) {
|
|
100
|
+
return 'Username is required';
|
|
101
|
+
}
|
|
102
|
+
return true;
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
type: 'password',
|
|
107
|
+
name: 'password',
|
|
108
|
+
message: 'Enter your password:',
|
|
109
|
+
validate: (input: string) => {
|
|
110
|
+
if (!input || input.trim().length === 0) {
|
|
111
|
+
return 'Password is required';
|
|
112
|
+
}
|
|
113
|
+
return true;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
]);
|
|
117
|
+
username = credentialAnswers.username;
|
|
118
|
+
password = credentialAnswers.password;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Username/password authentication only
|
|
122
|
+
const loginResponse = await apiService.login({ username, password });
|
|
123
|
+
let authToken = loginResponse.token;
|
|
124
|
+
let expiresAt = loginResponse.expiresAt;
|
|
125
|
+
username = username ?? '';
|
|
126
|
+
authToken = authToken ?? '';
|
|
127
|
+
expiresAt = expiresAt ?? new Date(Date.now() + 30 * 24 * 60 * 60 * 1000).toISOString();
|
|
128
|
+
configManager.setAuth({
|
|
129
|
+
token: authToken,
|
|
130
|
+
expiresAt: expiresAt,
|
|
131
|
+
username: username
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
spinner.succeed('Successfully authenticated!');
|
|
135
|
+
console.log(chalk.green('✓ Authentication stored securely'));
|
|
136
|
+
console.log(chalk.blue('You can now use other BuildX CLI commands'));
|
|
137
|
+
|
|
138
|
+
} catch (error) {
|
|
139
|
+
spinner.fail('Authentication failed');
|
|
140
|
+
console.error(chalk.red('Error:'), error instanceof Error ? error.message : 'Unknown error');
|
|
141
|
+
process.exit(1);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
} catch (error) {
|
|
145
|
+
console.error(chalk.red('Error:'), error instanceof Error ? error.message : 'Unknown error');
|
|
146
|
+
process.exit(1);
|
|
147
|
+
}
|
|
148
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import { configManager } from '../../config/index';
|
|
4
|
+
|
|
5
|
+
export const logoutCommand = new Command('logout')
|
|
6
|
+
.description('Logout and clear stored authentication')
|
|
7
|
+
.action(() => {
|
|
8
|
+
try {
|
|
9
|
+
configManager.clearAuth();
|
|
10
|
+
console.log(chalk.green('✓ Successfully logged out'));
|
|
11
|
+
console.log(chalk.blue('Authentication token has been cleared'));
|
|
12
|
+
} catch (error) {
|
|
13
|
+
console.error(chalk.red('Error:'), error instanceof Error ? error.message : 'Unknown error');
|
|
14
|
+
process.exit(1);
|
|
15
|
+
}
|
|
16
|
+
});
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import { configManager } from '../../config/index';
|
|
4
|
+
import { apiService } from '../../services/api';
|
|
5
|
+
import ora from 'ora';
|
|
6
|
+
|
|
7
|
+
export const authStatusCommand = new Command('auth:status')
|
|
8
|
+
.description('Check authentication status')
|
|
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
|
+
const isAuthenticated = configManager.isAuthenticated();
|
|
19
|
+
const auth = configManager.getAuth();
|
|
20
|
+
|
|
21
|
+
if (isAuthenticated && auth) {
|
|
22
|
+
const spinner = ora('Validating session...').start();
|
|
23
|
+
try {
|
|
24
|
+
// Call /auth/me endpoint
|
|
25
|
+
const me = await apiService.getMe();
|
|
26
|
+
spinner.succeed('Authenticated');
|
|
27
|
+
console.log(chalk.green('✓ Authenticated'));
|
|
28
|
+
console.log(chalk.blue('Username:'), me.username);
|
|
29
|
+
if (auth.token) {
|
|
30
|
+
console.log(chalk.blue('Token:'), auth.token.substring(0, 8) + '...');
|
|
31
|
+
}
|
|
32
|
+
if (auth.expiresAt) {
|
|
33
|
+
console.log(chalk.blue('Expires:'), new Date(auth.expiresAt).toLocaleString());
|
|
34
|
+
}
|
|
35
|
+
// console.log(chalk.gray('Raw response:'), JSON.stringify(me, null, 2));
|
|
36
|
+
if (me.username) {
|
|
37
|
+
console.log(chalk.blue(`You are authenticated as ${me.source}:${me.username}`));
|
|
38
|
+
}
|
|
39
|
+
} catch (error) {
|
|
40
|
+
spinner.fail('Session invalid');
|
|
41
|
+
console.error(chalk.red('Error:'), error instanceof Error ? error.message : 'Unknown error');
|
|
42
|
+
process.exit(1);
|
|
43
|
+
}
|
|
44
|
+
} else {
|
|
45
|
+
console.log(chalk.red('✗ Not authenticated'));
|
|
46
|
+
console.log(chalk.yellow('Run "buildx login" to authenticate'));
|
|
47
|
+
}
|
|
48
|
+
} catch (error) {
|
|
49
|
+
console.error(chalk.red('Error:'), error instanceof Error ? error.message : 'Unknown error');
|
|
50
|
+
process.exit(1);
|
|
51
|
+
}
|
|
52
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import { configManager } from '../../config/index';
|
|
4
|
+
|
|
5
|
+
export const configClearCommand = new Command('clear')
|
|
6
|
+
.description('Clear all CLI configuration (API, auth, projects, sync)')
|
|
7
|
+
.action(() => {
|
|
8
|
+
try {
|
|
9
|
+
configManager.clear();
|
|
10
|
+
console.log(chalk.green('✅ All configuration cleared.'));
|
|
11
|
+
console.log(chalk.yellow('You must run "buildx config setup" before using the CLI again.'));
|
|
12
|
+
} catch (error) {
|
|
13
|
+
console.error(chalk.red('❌ Failed to clear configuration:'), error instanceof Error ? error.message : 'Unknown error');
|
|
14
|
+
process.exit(1);
|
|
15
|
+
}
|
|
16
|
+
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { setupCommand } from './setup';
|
|
3
|
+
import { configShowCommand } from './show';
|
|
4
|
+
import { configClearCommand } from './clear';
|
|
5
|
+
|
|
6
|
+
export const configCommand = new Command('config')
|
|
7
|
+
.description('Manage CLI configuration')
|
|
8
|
+
.addCommand(setupCommand)
|
|
9
|
+
.addCommand(configShowCommand)
|
|
10
|
+
.addCommand(configClearCommand);
|