@ktmcp-cli/points-of-interest 1.1.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/README.md +81 -0
- package/bin/points-of-interest.js +58 -0
- package/package.json +33 -0
- package/src/commands/config.js +42 -0
- package/src/commands/retrieve.js +57 -0
- package/src/commands/search.js +121 -0
- package/src/lib/api.js +59 -0
- package/src/lib/config.js +58 -0
package/README.md
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# Points of Interest CLI
|
|
2
|
+
|
|
3
|
+
Production-ready command-line interface for the Points of Interest API.
|
|
4
|
+
|
|
5
|
+
> **⚠️ Unofficial CLI** - This tool is an independent project built on the public API.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- Complete coverage of Points of Interest API endpoints
|
|
10
|
+
- Simple, intuitive command structure
|
|
11
|
+
- JSON and pretty-print output formats
|
|
12
|
+
- Persistent configuration storage
|
|
13
|
+
- Progress indicators for async operations
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install -g @ktmcp-cli/points-of-interest
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Or install locally:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
cd points-of-interest
|
|
25
|
+
npm install
|
|
26
|
+
npm link
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Configuration
|
|
30
|
+
|
|
31
|
+
### Set API Key
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
points-of-interest config set apiKey YOUR_KEY_HERE
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Environment Variables
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
export API_KEY=your_key_here
|
|
41
|
+
export API_BASE_URL=https://api.example.com
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### View Configuration
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
points-of-interest config list
|
|
48
|
+
points-of-interest config get apiKey
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Usage
|
|
52
|
+
|
|
53
|
+
### search
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
# List items
|
|
57
|
+
points-of-interest search list
|
|
58
|
+
|
|
59
|
+
# Get item by ID
|
|
60
|
+
points-of-interest search get <id>
|
|
61
|
+
|
|
62
|
+
# Output as JSON
|
|
63
|
+
points-of-interest search list --json
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Available Resources
|
|
67
|
+
|
|
68
|
+
- `search`
|
|
69
|
+
- `retrieve`
|
|
70
|
+
|
|
71
|
+
## JSON Output
|
|
72
|
+
|
|
73
|
+
All commands support `--json` flag for machine-readable output:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
points-of-interest search list --json
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## License
|
|
80
|
+
|
|
81
|
+
MIT License - see LICENSE file for details.
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { Command } from 'commander';
|
|
4
|
+
import { readFileSync } from 'fs';
|
|
5
|
+
import { fileURLToPath } from 'url';
|
|
6
|
+
import { dirname, join } from 'path';
|
|
7
|
+
import chalk from 'chalk';
|
|
8
|
+
|
|
9
|
+
import config from '../src/commands/config.js';
|
|
10
|
+
import search from '../src/commands/search.js';
|
|
11
|
+
import retrieve from '../src/commands/retrieve.js';
|
|
12
|
+
|
|
13
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
14
|
+
const __dirname = dirname(__filename);
|
|
15
|
+
|
|
16
|
+
const packageJson = JSON.parse(
|
|
17
|
+
readFileSync(join(__dirname, '../package.json'), 'utf-8')
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
const program = new Command();
|
|
21
|
+
|
|
22
|
+
program
|
|
23
|
+
.name('points-of-interest')
|
|
24
|
+
.description(chalk.cyan('
|
|
25
|
+
Before using this API, we recommend you read our **[Authorization Guide](https://developers.amadeus.com/self-service/apis-docs/guides/authorization-262)** for more information on how to generate an access token.
|
|
26
|
+
|
|
27
|
+
Please also be aware that our test environment is based on a subset of the production, this API in test only returns a few selected cities. You can find the list in our **[data collection](https://github.com/amadeus4dev/data-collection)**.
|
|
28
|
+
'))
|
|
29
|
+
.version(packageJson.version, '-v, --version', 'Output the current version')
|
|
30
|
+
.addHelpText('after', `
|
|
31
|
+
${chalk.bold('Examples:')}
|
|
32
|
+
$ points-of-interest config set apiKey YOUR_KEY
|
|
33
|
+
$ points-of-interest search list
|
|
34
|
+
$ points-of-interest search list --json
|
|
35
|
+
|
|
36
|
+
${chalk.bold('Environment Variables:')}
|
|
37
|
+
API_KEY API authentication key
|
|
38
|
+
API_BASE_URL API base URL
|
|
39
|
+
API_TIMEOUT Request timeout in ms (default: 30000)
|
|
40
|
+
|
|
41
|
+
${chalk.bold('Documentation:')}
|
|
42
|
+
GitHub: ${chalk.blue('https://github.com/ktmcp-cli/points-of-interest')}
|
|
43
|
+
`);
|
|
44
|
+
|
|
45
|
+
program.addCommand(config);
|
|
46
|
+
program.addCommand(search);
|
|
47
|
+
program.addCommand(retrieve);
|
|
48
|
+
|
|
49
|
+
process.on('unhandledRejection', (error) => {
|
|
50
|
+
console.error(chalk.red('Unhandled error:'), error);
|
|
51
|
+
process.exit(1);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
program.parse(process.argv);
|
|
55
|
+
|
|
56
|
+
if (!process.argv.slice(2).length) {
|
|
57
|
+
program.outputHelp();
|
|
58
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ktmcp-cli/points-of-interest",
|
|
3
|
+
"version": "1.1.1",
|
|
4
|
+
"description": "\nBefore using this API, we recommend you read our **[Authorization Guide](https://developers.amadeus.com/self-service/apis-docs/guides/authorization-262)** for more information on how to generate an access token.\n\nPlease also be aware that our test environment is based on a subset of the production, this API in test only returns a few selected cities. You can find the list in our **[data collection](https://github.com/amadeus4dev/data-collection)**.\n",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "bin/points-of-interest.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"points-of-interest": "./bin/points-of-interest.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"start": "node bin/points-of-interest.js",
|
|
12
|
+
"dev": "node bin/points-of-interest.js"
|
|
13
|
+
},
|
|
14
|
+
"keywords": [
|
|
15
|
+
"cli",
|
|
16
|
+
"api",
|
|
17
|
+
"points-of-interest",
|
|
18
|
+
"ktmcp"
|
|
19
|
+
],
|
|
20
|
+
"author": "KTMCP",
|
|
21
|
+
"license": "MIT",
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"commander": "^12.0.0",
|
|
24
|
+
"axios": "^1.6.0",
|
|
25
|
+
"chalk": "^5.3.0",
|
|
26
|
+
"conf": "^12.0.0",
|
|
27
|
+
"dotenv": "^16.4.0",
|
|
28
|
+
"ora": "^8.0.1"
|
|
29
|
+
},
|
|
30
|
+
"engines": {
|
|
31
|
+
"node": ">=18.0.0"
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import { loadConfig, setConfig, getConfig, saveConfig } from '../lib/config.js';
|
|
4
|
+
|
|
5
|
+
const config = new Command('config');
|
|
6
|
+
|
|
7
|
+
config.description('Manage CLI configuration');
|
|
8
|
+
|
|
9
|
+
config
|
|
10
|
+
.command('set <key> <value>')
|
|
11
|
+
.description('Set a configuration value')
|
|
12
|
+
.action((key, value) => {
|
|
13
|
+
if (setConfig(key, value)) {
|
|
14
|
+
console.log(chalk.green(`✓ Set ${key} = ${value}`));
|
|
15
|
+
} else {
|
|
16
|
+
console.error(chalk.red('Failed to save configuration'));
|
|
17
|
+
process.exit(1);
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
config
|
|
22
|
+
.command('get <key>')
|
|
23
|
+
.description('Get a configuration value')
|
|
24
|
+
.action((key) => {
|
|
25
|
+
const value = getConfig(key);
|
|
26
|
+
if (value !== undefined) {
|
|
27
|
+
console.log(chalk.cyan(`${key} = ${value}`));
|
|
28
|
+
} else {
|
|
29
|
+
console.log(chalk.yellow(`${key} is not set`));
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
config
|
|
34
|
+
.command('list')
|
|
35
|
+
.description('List all configuration values')
|
|
36
|
+
.action(() => {
|
|
37
|
+
const all = loadConfig();
|
|
38
|
+
console.log(chalk.cyan('Current configuration:'));
|
|
39
|
+
console.log(JSON.stringify(all, null, 2));
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
export default config;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import ora from 'ora';
|
|
4
|
+
import { createClient, handleError } from '../lib/api.js';
|
|
5
|
+
|
|
6
|
+
const retrieve = new Command('retrieve');
|
|
7
|
+
|
|
8
|
+
retrieve
|
|
9
|
+
.description('Manage retrieve');
|
|
10
|
+
|
|
11
|
+
retrieve
|
|
12
|
+
.command('get <pois-id>')
|
|
13
|
+
.description('Retieve one point of interest by its Id.')
|
|
14
|
+
.option('--json', 'Output as JSON')
|
|
15
|
+
.option('-v, --verbose', 'Verbose output')
|
|
16
|
+
.action(async (poisId, options) => {
|
|
17
|
+
const spinner = ora('Retieve one point of interest by its Id....').start();
|
|
18
|
+
try {
|
|
19
|
+
const client = createClient();
|
|
20
|
+
const response = await client.get(`/reference-data/locations/pois/${poisId}`);
|
|
21
|
+
const result = { success: true, status: response.status, data: response.data };
|
|
22
|
+
|
|
23
|
+
spinner.succeed('Success');
|
|
24
|
+
|
|
25
|
+
if (result.success) {
|
|
26
|
+
if (options.json) {
|
|
27
|
+
console.log(JSON.stringify(result.data, null, 2));
|
|
28
|
+
} else {
|
|
29
|
+
// Detail output
|
|
30
|
+
const item = result.data;
|
|
31
|
+
console.log(chalk.bold('Details:'));
|
|
32
|
+
console.log(chalk.gray('─'.repeat(60)));
|
|
33
|
+
Object.entries(item).slice(0, 10).forEach(([key, value]) => {
|
|
34
|
+
console.log(chalk.cyan(key + ':'), String(value).slice(0, 100));
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
process.exit(0);
|
|
38
|
+
} else {
|
|
39
|
+
spinner.fail(`Failed (${result.status})`);
|
|
40
|
+
console.error(chalk.red(result.message));
|
|
41
|
+
if (options.verbose && result.data) {
|
|
42
|
+
console.error(chalk.yellow('\nResponse data:'));
|
|
43
|
+
console.error(JSON.stringify(result.data, null, 2));
|
|
44
|
+
}
|
|
45
|
+
process.exit(1);
|
|
46
|
+
}
|
|
47
|
+
} catch (error) {
|
|
48
|
+
spinner.fail('Error occurred');
|
|
49
|
+
console.error(chalk.red('Error:'), error.message);
|
|
50
|
+
if (options.verbose) {
|
|
51
|
+
console.error(error.stack);
|
|
52
|
+
}
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
export default retrieve;
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import ora from 'ora';
|
|
4
|
+
import { createClient, handleError } from '../lib/api.js';
|
|
5
|
+
|
|
6
|
+
const search = new Command('search');
|
|
7
|
+
|
|
8
|
+
search
|
|
9
|
+
.description('Manage search');
|
|
10
|
+
|
|
11
|
+
search
|
|
12
|
+
.command('get')
|
|
13
|
+
.description('Returns points of interest for a given location and radius.')
|
|
14
|
+
.requiredOption('--latitude <value>', 'Latitude (decimal coordinates)')
|
|
15
|
+
.requiredOption('--longitude <value>', 'Longitude (decimal coordinates)')
|
|
16
|
+
.option('--radius <value>', 'radius of the search in Kilometer. Can be from 0 to 20, default value is 1 Km.')
|
|
17
|
+
.option('--json', 'Output as JSON')
|
|
18
|
+
.option('-v, --verbose', 'Verbose output')
|
|
19
|
+
.action(async (options) => {
|
|
20
|
+
const spinner = ora('Returns points of interest for a given location and radius....').start();
|
|
21
|
+
try {
|
|
22
|
+
const client = createClient();
|
|
23
|
+
const queryParams = {};
|
|
24
|
+
if (options.latitude) queryParams.latitude = options.latitude;
|
|
25
|
+
if (options.longitude) queryParams.longitude = options.longitude;
|
|
26
|
+
if (options.radius) queryParams.radius = options.radius;
|
|
27
|
+
|
|
28
|
+
const response = await client.get(`/reference-data/locations/pois`, { params: queryParams });
|
|
29
|
+
const result = { success: true, status: response.status, data: response.data };
|
|
30
|
+
|
|
31
|
+
spinner.succeed('Success');
|
|
32
|
+
|
|
33
|
+
if (result.success) {
|
|
34
|
+
if (options.json) {
|
|
35
|
+
console.log(JSON.stringify(result.data, null, 2));
|
|
36
|
+
} else {
|
|
37
|
+
// Detail output
|
|
38
|
+
const item = result.data;
|
|
39
|
+
console.log(chalk.bold('Details:'));
|
|
40
|
+
console.log(chalk.gray('─'.repeat(60)));
|
|
41
|
+
Object.entries(item).slice(0, 10).forEach(([key, value]) => {
|
|
42
|
+
console.log(chalk.cyan(key + ':'), String(value).slice(0, 100));
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
process.exit(0);
|
|
46
|
+
} else {
|
|
47
|
+
spinner.fail(`Failed (${result.status})`);
|
|
48
|
+
console.error(chalk.red(result.message));
|
|
49
|
+
if (options.verbose && result.data) {
|
|
50
|
+
console.error(chalk.yellow('\nResponse data:'));
|
|
51
|
+
console.error(JSON.stringify(result.data, null, 2));
|
|
52
|
+
}
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
} catch (error) {
|
|
56
|
+
spinner.fail('Error occurred');
|
|
57
|
+
console.error(chalk.red('Error:'), error.message);
|
|
58
|
+
if (options.verbose) {
|
|
59
|
+
console.error(error.stack);
|
|
60
|
+
}
|
|
61
|
+
process.exit(1);
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
search
|
|
66
|
+
.command('get')
|
|
67
|
+
.description('Returns points of interest for a given area')
|
|
68
|
+
.requiredOption('--north <value>', 'Latitude north of bounding box (decimal coordinates)')
|
|
69
|
+
.requiredOption('--west <value>', 'Longitude west of bounding box (decimal coordinates)')
|
|
70
|
+
.requiredOption('--south <value>', 'Latitude south of bounding box (decimal coordinates)')
|
|
71
|
+
.requiredOption('--east <value>', 'Longitude east of bounding box (decimal coordinates)')
|
|
72
|
+
.option('--json', 'Output as JSON')
|
|
73
|
+
.option('-v, --verbose', 'Verbose output')
|
|
74
|
+
.action(async (options) => {
|
|
75
|
+
const spinner = ora('Returns points of interest for a given area...').start();
|
|
76
|
+
try {
|
|
77
|
+
const client = createClient();
|
|
78
|
+
const queryParams = {};
|
|
79
|
+
if (options.north) queryParams.north = options.north;
|
|
80
|
+
if (options.west) queryParams.west = options.west;
|
|
81
|
+
if (options.south) queryParams.south = options.south;
|
|
82
|
+
if (options.east) queryParams.east = options.east;
|
|
83
|
+
|
|
84
|
+
const response = await client.get(`/reference-data/locations/pois/by-square`, { params: queryParams });
|
|
85
|
+
const result = { success: true, status: response.status, data: response.data };
|
|
86
|
+
|
|
87
|
+
spinner.succeed('Success');
|
|
88
|
+
|
|
89
|
+
if (result.success) {
|
|
90
|
+
if (options.json) {
|
|
91
|
+
console.log(JSON.stringify(result.data, null, 2));
|
|
92
|
+
} else {
|
|
93
|
+
// Detail output
|
|
94
|
+
const item = result.data;
|
|
95
|
+
console.log(chalk.bold('Details:'));
|
|
96
|
+
console.log(chalk.gray('─'.repeat(60)));
|
|
97
|
+
Object.entries(item).slice(0, 10).forEach(([key, value]) => {
|
|
98
|
+
console.log(chalk.cyan(key + ':'), String(value).slice(0, 100));
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
process.exit(0);
|
|
102
|
+
} else {
|
|
103
|
+
spinner.fail(`Failed (${result.status})`);
|
|
104
|
+
console.error(chalk.red(result.message));
|
|
105
|
+
if (options.verbose && result.data) {
|
|
106
|
+
console.error(chalk.yellow('\nResponse data:'));
|
|
107
|
+
console.error(JSON.stringify(result.data, null, 2));
|
|
108
|
+
}
|
|
109
|
+
process.exit(1);
|
|
110
|
+
}
|
|
111
|
+
} catch (error) {
|
|
112
|
+
spinner.fail('Error occurred');
|
|
113
|
+
console.error(chalk.red('Error:'), error.message);
|
|
114
|
+
if (options.verbose) {
|
|
115
|
+
console.error(error.stack);
|
|
116
|
+
}
|
|
117
|
+
process.exit(1);
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
export default search;
|
package/src/lib/api.js
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
import { loadConfig } from './config.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Create axios instance with configuration
|
|
6
|
+
*/
|
|
7
|
+
function createClient(options = {}) {
|
|
8
|
+
const config = loadConfig();
|
|
9
|
+
const baseURL = options.baseUrl || config.baseUrl || 'https://api.example.com';
|
|
10
|
+
|
|
11
|
+
const instance = axios.create({
|
|
12
|
+
baseURL,
|
|
13
|
+
timeout: options.timeout || config.timeout || 30000,
|
|
14
|
+
headers: {
|
|
15
|
+
'User-Agent': '@ktmcp-cli/points-of-interest/1.0.0',
|
|
16
|
+
'Content-Type': 'application/json'
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
// Add authentication
|
|
21
|
+
instance.interceptors.request.use((config) => {
|
|
22
|
+
// No authentication configured
|
|
23
|
+
return config;
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
return instance;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Handle API errors consistently
|
|
31
|
+
*/
|
|
32
|
+
function handleError(error) {
|
|
33
|
+
if (error.response) {
|
|
34
|
+
return {
|
|
35
|
+
success: false,
|
|
36
|
+
status: error.response.status,
|
|
37
|
+
statusText: error.response.statusText,
|
|
38
|
+
message: error.response.data?.message ||
|
|
39
|
+
error.response.data?.error ||
|
|
40
|
+
'API request failed',
|
|
41
|
+
data: error.response.data
|
|
42
|
+
};
|
|
43
|
+
} else if (error.request) {
|
|
44
|
+
return {
|
|
45
|
+
success: false,
|
|
46
|
+
status: 0,
|
|
47
|
+
message: 'No response from server',
|
|
48
|
+
error: error.message
|
|
49
|
+
};
|
|
50
|
+
} else {
|
|
51
|
+
return {
|
|
52
|
+
success: false,
|
|
53
|
+
status: 0,
|
|
54
|
+
message: error.message
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export { createClient, handleError };
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';
|
|
2
|
+
import { homedir } from 'os';
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
import dotenv from 'dotenv';
|
|
5
|
+
|
|
6
|
+
dotenv.config();
|
|
7
|
+
|
|
8
|
+
const CONFIG_DIR = join(homedir(), '.points-of-interest');
|
|
9
|
+
const CONFIG_FILE = join(CONFIG_DIR, 'config.json');
|
|
10
|
+
|
|
11
|
+
const DEFAULT_CONFIG = {
|
|
12
|
+
baseUrl: process.env.API_BASE_URL || 'https://api.example.com',
|
|
13
|
+
apiKey: process.env.API_KEY || '',
|
|
14
|
+
timeout: parseInt(process.env.API_TIMEOUT || '30000', 10)
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export function loadConfig() {
|
|
18
|
+
if (existsSync(CONFIG_FILE)) {
|
|
19
|
+
try {
|
|
20
|
+
const fileConfig = JSON.parse(readFileSync(CONFIG_FILE, 'utf8'));
|
|
21
|
+
return { ...DEFAULT_CONFIG, ...fileConfig };
|
|
22
|
+
} catch (error) {
|
|
23
|
+
console.error('Error reading config file:', error.message);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return DEFAULT_CONFIG;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function saveConfig(config) {
|
|
30
|
+
try {
|
|
31
|
+
if (!existsSync(CONFIG_DIR)) {
|
|
32
|
+
mkdirSync(CONFIG_DIR, { recursive: true });
|
|
33
|
+
}
|
|
34
|
+
writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
|
|
35
|
+
return true;
|
|
36
|
+
} catch (error) {
|
|
37
|
+
console.error('Error saving config:', error.message);
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function getConfig(key) {
|
|
43
|
+
const config = loadConfig();
|
|
44
|
+
return config[key];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function setConfig(key, value) {
|
|
48
|
+
const config = loadConfig();
|
|
49
|
+
config[key] = value;
|
|
50
|
+
return saveConfig(config);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export default {
|
|
54
|
+
loadConfig,
|
|
55
|
+
saveConfig,
|
|
56
|
+
getConfig,
|
|
57
|
+
setConfig
|
|
58
|
+
};
|