@cvtoolman/huawei-lts-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/LICENSE +21 -0
- package/README.md +193 -0
- package/dist/cli/commands/config.js +56 -0
- package/dist/cli/commands/init.js +156 -0
- package/dist/cli/commands/query.js +126 -0
- package/dist/cli/index.js +168 -0
- package/dist/cli/options.js +15 -0
- package/dist/config/index.js +57 -0
- package/dist/config/store.js +67 -0
- package/dist/lts/client.js +88 -0
- package/dist/lts/index.js +20 -0
- package/dist/lts/types.js +5 -0
- package/dist/output/index.js +41 -0
- package/dist/output/json.js +6 -0
- package/dist/output/table.js +24 -0
- package/dist/types/index.js +2 -0
- package/package.json +55 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
# huawei-lts-cli
|
|
2
|
+
|
|
3
|
+
Command-line tool for querying [Huawei Cloud Log Tank Service (LTS)](https://www.huaweicloud.com/intl/en-us/product/lts.html) logs from the terminal.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/huawei-lts-cli)
|
|
6
|
+
[](LICENSE)
|
|
7
|
+
|
|
8
|
+
## Features
|
|
9
|
+
|
|
10
|
+
- ๐ Query LTS logs by time range, keyword, and more
|
|
11
|
+
- ๐ Multiple output formats: pretty (colored), JSON, table
|
|
12
|
+
- ๐ Auto-pagination โ fetches all matching logs in one command
|
|
13
|
+
- โ๏ธ Persistent configuration โ set credentials once, use forever
|
|
14
|
+
- ๐ง Interactive setup wizard โ `lts-cli init` guides you step by step
|
|
15
|
+
- ๐ Environment variable support โ CI/CD friendly
|
|
16
|
+
- ๐ Shell completion for bash, zsh, and PowerShell
|
|
17
|
+
|
|
18
|
+
## Quick Start
|
|
19
|
+
|
|
20
|
+
### 1. Install
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npm install -g huawei-lts-cli
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### 2. Configure
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
lts-cli init
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
The wizard will ask for your AK, SK, Project ID, and Region. You can find your credentials in the Huawei Cloud console under **My Credentials > Access Keys**.
|
|
33
|
+
|
|
34
|
+
Or set manually:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
lts-cli config --set ak=YOUR_ACCESS_KEY
|
|
38
|
+
lts-cli config --set sk=YOUR_SECRET_KEY
|
|
39
|
+
lts-cli config --set projectId=YOUR_PROJECT_ID
|
|
40
|
+
lts-cli config --set region=cn-south-1
|
|
41
|
+
lts-cli config --set endpoint=https://lts.cn-south-1.myhuaweicloud.com
|
|
42
|
+
lts-cli config --set groupId=YOUR_LOG_GROUP_ID # optional โ default
|
|
43
|
+
lts-cli config --set streamId=YOUR_LOG_STREAM_ID # optional โ default
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### 3. Query
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
# Basic query
|
|
50
|
+
lts-cli query --st 2024-01-01T00:00:00Z --et 2024-01-02T00:00:00Z
|
|
51
|
+
|
|
52
|
+
# With keyword filter
|
|
53
|
+
lts-cli query --st 2024-01-01T00:00:00Z --et 2024-01-02T00:00:00Z -k "ERROR"
|
|
54
|
+
|
|
55
|
+
# With explicit group/stream (overrides config defaults)
|
|
56
|
+
lts-cli query -g <group-id> -s <stream-id> --st 2024-01-01T00:00:00Z --et 2024-01-02T00:00:00Z
|
|
57
|
+
|
|
58
|
+
# JSON output (useful for piping to jq)
|
|
59
|
+
lts-cli query --st 2024-01-01T00:00:00Z --et 2024-01-02T00:00:00Z -f json
|
|
60
|
+
|
|
61
|
+
# Table output
|
|
62
|
+
lts-cli query --st 2024-01-01T00:00:00Z --et 2024-01-02T00:00:00Z -f table
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Command Reference
|
|
66
|
+
|
|
67
|
+
### `lts-cli query` โ Query Logs
|
|
68
|
+
|
|
69
|
+
```
|
|
70
|
+
Usage: lts-cli query [options]
|
|
71
|
+
|
|
72
|
+
Options:
|
|
73
|
+
-g, --group-id <groupId> Log group ID (can be set in config)
|
|
74
|
+
-s, --stream-id <streamId> Log stream ID (can be set in config)
|
|
75
|
+
--start-time, --st <time> Start time in ISO 8601 (required)
|
|
76
|
+
--end-time, --et <time> End time in ISO 8601 (required)
|
|
77
|
+
-k, --keyword <keyword> Search keyword
|
|
78
|
+
-l, --limit <limit> Results per page (default: 100)
|
|
79
|
+
-o, --offset <offset> Pagination offset (default: 0)
|
|
80
|
+
-r, --reverse Show newest logs first
|
|
81
|
+
-f, --format <format> Output format: pretty, json, table (default: pretty)
|
|
82
|
+
--no-paginate Disable auto-pagination (single page only)
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### `lts-cli config` โ Manage Configuration
|
|
86
|
+
|
|
87
|
+
```
|
|
88
|
+
Usage: lts-cli config [options]
|
|
89
|
+
|
|
90
|
+
Options:
|
|
91
|
+
--set <key=value> Set a configuration value
|
|
92
|
+
--get <key> Get a configuration value
|
|
93
|
+
--list List all configuration values
|
|
94
|
+
--unset <key> Remove a configuration value
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### `lts-cli init` โ Setup Wizard
|
|
98
|
+
|
|
99
|
+
```
|
|
100
|
+
Usage: lts-cli init
|
|
101
|
+
|
|
102
|
+
Interactive wizard that guides you through setting up AK, SK, Project ID,
|
|
103
|
+
region, endpoint, and optional default log group/stream.
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### `lts-cli completion` โ Shell Completion
|
|
107
|
+
|
|
108
|
+
```
|
|
109
|
+
Usage: lts-cli completion <shell>
|
|
110
|
+
|
|
111
|
+
Generate completion script for bash, zsh, or powershell.
|
|
112
|
+
|
|
113
|
+
Examples:
|
|
114
|
+
lts-cli completion bash >> ~/.bashrc
|
|
115
|
+
lts-cli completion zsh >> ~/.zshrc
|
|
116
|
+
lts-cli completion powershell >> $PROFILE
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## Configuration
|
|
120
|
+
|
|
121
|
+
Configuration is stored in `~/.lts-cli/config.json` (Linux/macOS) or `%USERPROFILE%\.lts-cli\config.json` (Windows).
|
|
122
|
+
|
|
123
|
+
### Available Keys
|
|
124
|
+
|
|
125
|
+
| Key | Required | Description |
|
|
126
|
+
|---|---|---|
|
|
127
|
+
| `ak` | Yes | Huawei Cloud Access Key |
|
|
128
|
+
| `sk` | Yes | Huawei Cloud Secret Key |
|
|
129
|
+
| `projectId` | Yes | Huawei Cloud Project ID |
|
|
130
|
+
| `region` | No | Region (e.g. `cn-south-1`) |
|
|
131
|
+
| `endpoint` | No | LTS service endpoint. Must include `https://` |
|
|
132
|
+
| `groupId` | No | Default log group ID |
|
|
133
|
+
| `streamId` | No | Default log stream ID |
|
|
134
|
+
|
|
135
|
+
### Environment Variables
|
|
136
|
+
|
|
137
|
+
All config keys can also be set via environment variables (useful for CI/CD):
|
|
138
|
+
|
|
139
|
+
| Config Key | Environment Variable |
|
|
140
|
+
|---|---|
|
|
141
|
+
| `ak` | `LTS_AK` |
|
|
142
|
+
| `sk` | `LTS_SK` |
|
|
143
|
+
| `projectId` | `LTS_PROJECT_ID` |
|
|
144
|
+
| `region` | `LTS_REGION` |
|
|
145
|
+
| `endpoint` | `LTS_ENDPOINT` |
|
|
146
|
+
| `groupId` | `LTS_GROUP_ID` |
|
|
147
|
+
| `streamId` | `LTS_STREAM_ID` |
|
|
148
|
+
|
|
149
|
+
Environment variables take precedence over config file values.
|
|
150
|
+
|
|
151
|
+
## Supported Regions
|
|
152
|
+
|
|
153
|
+
| Region ID | Region Name | Endpoint |
|
|
154
|
+
|---|---|---|
|
|
155
|
+
| `cn-north-1` | CN North-Beijing1 | `lts.cn-north-1.myhuaweicloud.com` |
|
|
156
|
+
| `cn-north-4` | CN North-Beijing4 | `lts.cn-north-4.myhuaweicloud.com` |
|
|
157
|
+
| `cn-east-2` | CN East-Shanghai2 | `lts.cn-east-2.myhuaweicloud.com` |
|
|
158
|
+
| `cn-east-3` | CN East-Shanghai3 | `lts.cn-east-3.myhuaweicloud.com` |
|
|
159
|
+
| `cn-south-1` | CN South-Guangzhou | `lts.cn-south-1.myhuaweicloud.com` |
|
|
160
|
+
| `cn-south-4` | CN South-Shenzhen | `lts.cn-south-4.myhuaweicloud.com` |
|
|
161
|
+
| `cn-southwest-2` | CN Southwest-Guiyang | `lts.cn-southwest-2.myhuaweicloud.com` |
|
|
162
|
+
| `ap-southeast-1` | AP-Singapore | `lts.ap-southeast-1.myhuaweicloud.com` |
|
|
163
|
+
| `ap-southeast-2` | AP-Bangkok | `lts.ap-southeast-2.myhuaweicloud.com` |
|
|
164
|
+
| `ap-southeast-3` | AP-Jakarta | `lts.ap-southeast-3.myhuaweicloud.com` |
|
|
165
|
+
| `af-south-1` | AF-Johannesburg | `lts.af-south-1.myhuaweicloud.com` |
|
|
166
|
+
|
|
167
|
+
> For a complete list, see [Huawei Cloud LTS Endpoints](https://support.huaweicloud.com/intl/en-us/api-lts/lts_api_0011.html).
|
|
168
|
+
|
|
169
|
+
## Development
|
|
170
|
+
|
|
171
|
+
```bash
|
|
172
|
+
# Clone and install
|
|
173
|
+
git clone <repo-url>
|
|
174
|
+
cd huawei-lts-cli
|
|
175
|
+
npm install
|
|
176
|
+
|
|
177
|
+
# Build
|
|
178
|
+
npm run build
|
|
179
|
+
|
|
180
|
+
# Run during development (no build step needed)
|
|
181
|
+
npm run dev -- query --st 2024-01-01T00:00:00Z --et 2024-01-02T00:00:00Z
|
|
182
|
+
|
|
183
|
+
# Run compiled version
|
|
184
|
+
npm start -- query --st 2024-01-01T00:00:00Z --et 2024-01-02T00:00:00Z
|
|
185
|
+
|
|
186
|
+
# Link for local testing
|
|
187
|
+
npm link
|
|
188
|
+
lts-cli query --st 2024-01-01T00:00:00Z --et 2024-01-02T00:00:00Z
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
## License
|
|
192
|
+
|
|
193
|
+
[MIT](LICENSE)
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createConfigCommand = createConfigCommand;
|
|
4
|
+
const commander_1 = require("commander");
|
|
5
|
+
const config_1 = require("../../config");
|
|
6
|
+
function createConfigCommand() {
|
|
7
|
+
const cmd = new commander_1.Command('config');
|
|
8
|
+
cmd.description('Manage LTS CLI configuration')
|
|
9
|
+
.option('--set <key=value>', 'Set a configuration value')
|
|
10
|
+
.option('--get <key>', 'Get a configuration value')
|
|
11
|
+
.option('--list', 'List all configuration values')
|
|
12
|
+
.option('--unset <key>', 'Remove a configuration value')
|
|
13
|
+
.action((options) => {
|
|
14
|
+
if (options.set) {
|
|
15
|
+
const parts = options.set.split('=');
|
|
16
|
+
if (parts.length !== 2) {
|
|
17
|
+
console.error('Usage: config --set key=value');
|
|
18
|
+
process.exit(1);
|
|
19
|
+
}
|
|
20
|
+
const [key, value] = parts;
|
|
21
|
+
(0, config_1.setConfig)(key, value);
|
|
22
|
+
console.log(`Set ${key}=${value}`);
|
|
23
|
+
}
|
|
24
|
+
else if (options.get) {
|
|
25
|
+
const value = (0, config_1.getConfig)(options.get);
|
|
26
|
+
if (value !== undefined) {
|
|
27
|
+
console.log(value);
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
console.error(`Config key '${options.get}' not found`);
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
else if (options.list) {
|
|
35
|
+
const config = (0, config_1.listConfig)();
|
|
36
|
+
const entries = Object.entries(config);
|
|
37
|
+
if (entries.length === 0) {
|
|
38
|
+
console.log('No configuration values set');
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
for (const [key, value] of entries) {
|
|
42
|
+
console.log(`${key}=${value}`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
else if (options.unset) {
|
|
47
|
+
(0, config_1.unsetConfig)(options.unset);
|
|
48
|
+
console.log(`Removed ${options.unset}`);
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
console.error('Usage: lts-cli config [--set key=value | --get key | --list | --unset key]');
|
|
52
|
+
process.exit(1);
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
return cmd;
|
|
56
|
+
}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.createInitCommand = createInitCommand;
|
|
37
|
+
const commander_1 = require("commander");
|
|
38
|
+
const readline = __importStar(require("readline"));
|
|
39
|
+
const config_1 = require("../../config");
|
|
40
|
+
function ask(rl, question) {
|
|
41
|
+
return new Promise((resolve) => {
|
|
42
|
+
rl.question(question, (answer) => resolve(answer.trim()));
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
const ENDPOINT_MAP = {
|
|
46
|
+
'cn-north-1': 'lts.cn-north-1.myhuaweicloud.com',
|
|
47
|
+
'cn-north-2': 'lts.cn-north-2.myhuaweicloud.com',
|
|
48
|
+
'cn-north-4': 'lts.cn-north-4.myhuaweicloud.com',
|
|
49
|
+
'cn-north-5': 'lts.cn-north-5.myhuaweicloud.com',
|
|
50
|
+
'cn-north-9': 'lts.cn-north-9.myhuaweicloud.com',
|
|
51
|
+
'cn-east-2': 'lts.cn-east-2.myhuaweicloud.com',
|
|
52
|
+
'cn-east-3': 'lts.cn-east-3.myhuaweicloud.com',
|
|
53
|
+
'cn-east-4': 'lts.cn-east-4.myhuaweicloud.com',
|
|
54
|
+
'cn-south-1': 'lts.cn-south-1.myhuaweicloud.com',
|
|
55
|
+
'cn-south-2': 'lts.cn-south-2.myhuaweicloud.com',
|
|
56
|
+
'cn-south-4': 'lts.cn-south-4.myhuaweicloud.com',
|
|
57
|
+
'cn-southwest-2': 'lts.cn-southwest-2.myhuaweicloud.com',
|
|
58
|
+
'ap-southeast-1': 'lts.ap-southeast-1.myhuaweicloud.com',
|
|
59
|
+
'ap-southeast-2': 'lts.ap-southeast-2.myhuaweicloud.com',
|
|
60
|
+
'ap-southeast-3': 'lts.ap-southeast-3.myhuaweicloud.com',
|
|
61
|
+
'af-south-1': 'lts.af-south-1.myhuaweicloud.com',
|
|
62
|
+
'la-south-2': 'lts.la-south-2.myhuaweicloud.com',
|
|
63
|
+
'sa-brazil-1': 'lts.sa-brazil-1.myhuaweicloud.com',
|
|
64
|
+
'na-mexico-1': 'lts.na-mexico-1.myhuaweicloud.com',
|
|
65
|
+
'la-north-2': 'lts.la-north-2.myhuaweicloud.com',
|
|
66
|
+
};
|
|
67
|
+
function createInitCommand() {
|
|
68
|
+
const cmd = new commander_1.Command('init');
|
|
69
|
+
cmd.description('Interactive setup wizard โ configure credentials step by step')
|
|
70
|
+
.action(async () => {
|
|
71
|
+
const rl = readline.createInterface({
|
|
72
|
+
input: process.stdin,
|
|
73
|
+
output: process.stdout,
|
|
74
|
+
});
|
|
75
|
+
console.log('');
|
|
76
|
+
console.log('โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ');
|
|
77
|
+
console.log('โ Huawei Cloud LTS CLI โ Setup Wizard โ');
|
|
78
|
+
console.log('โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ');
|
|
79
|
+
console.log('');
|
|
80
|
+
console.log('This wizard will guide you through setting up your credentials.');
|
|
81
|
+
console.log('You can find AK/SK in the Huawei Cloud console under:');
|
|
82
|
+
console.log(' My Credentials > Access Keys');
|
|
83
|
+
console.log('');
|
|
84
|
+
console.log('Press Enter to skip any optional field.');
|
|
85
|
+
console.log('');
|
|
86
|
+
try {
|
|
87
|
+
// Required: AK
|
|
88
|
+
const ak = await ask(rl, 'Access Key (AK): ');
|
|
89
|
+
if (ak) {
|
|
90
|
+
(0, config_1.setConfig)('ak', ak);
|
|
91
|
+
}
|
|
92
|
+
// Required: SK
|
|
93
|
+
const sk = await ask(rl, 'Secret Key (SK): ');
|
|
94
|
+
if (sk) {
|
|
95
|
+
(0, config_1.setConfig)('sk', sk);
|
|
96
|
+
}
|
|
97
|
+
// Required: Project ID
|
|
98
|
+
const projectId = await ask(rl, 'Project ID: ');
|
|
99
|
+
if (projectId) {
|
|
100
|
+
(0, config_1.setConfig)('projectId', projectId);
|
|
101
|
+
}
|
|
102
|
+
// Required: Region
|
|
103
|
+
console.log('');
|
|
104
|
+
console.log('Common regions: cn-north-1, cn-north-4, cn-east-2, cn-east-3, cn-south-1');
|
|
105
|
+
const region = await ask(rl, 'Region (e.g. cn-south-1): ');
|
|
106
|
+
if (region) {
|
|
107
|
+
(0, config_1.setConfig)('region', region);
|
|
108
|
+
// Auto-suggest endpoint based on region
|
|
109
|
+
const autoEndpoint = ENDPOINT_MAP[region];
|
|
110
|
+
if (autoEndpoint) {
|
|
111
|
+
const useDefault = await ask(rl, `Endpoint [https://${autoEndpoint}]: `);
|
|
112
|
+
if (!useDefault) {
|
|
113
|
+
(0, config_1.setConfig)('endpoint', `https://${autoEndpoint}`);
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
const customEndpoint = useDefault.startsWith('http') ? useDefault : `https://${useDefault}`;
|
|
117
|
+
(0, config_1.setConfig)('endpoint', customEndpoint);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
const endpoint = await ask(rl, 'Endpoint (e.g. https://lts.cn-south-1.myhuaweicloud.com): ');
|
|
122
|
+
if (endpoint) {
|
|
123
|
+
const normalized = endpoint.startsWith('http') ? endpoint : `https://${endpoint}`;
|
|
124
|
+
(0, config_1.setConfig)('endpoint', normalized);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
// Optional: default groupId & streamId
|
|
129
|
+
console.log('');
|
|
130
|
+
console.log('Optional โ set default log group & stream so you can query without -g / -s:');
|
|
131
|
+
const groupId = await ask(rl, 'Default Log Group ID (optional): ');
|
|
132
|
+
if (groupId) {
|
|
133
|
+
(0, config_1.setConfig)('groupId', groupId);
|
|
134
|
+
}
|
|
135
|
+
const streamId = await ask(rl, 'Default Log Stream ID (optional): ');
|
|
136
|
+
if (streamId) {
|
|
137
|
+
(0, config_1.setConfig)('streamId', streamId);
|
|
138
|
+
}
|
|
139
|
+
console.log('');
|
|
140
|
+
console.log('โ
Configuration saved!');
|
|
141
|
+
console.log('');
|
|
142
|
+
console.log('Try it out:');
|
|
143
|
+
if (groupId && streamId) {
|
|
144
|
+
console.log(' lts-cli query --start-time 2024-01-01T00:00:00Z --end-time 2024-01-02T00:00:00Z');
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
console.log(' lts-cli query -g <group-id> -s <stream-id> --start-time 2024-01-01T00:00:00Z --end-time 2024-01-02T00:00:00Z');
|
|
148
|
+
}
|
|
149
|
+
console.log('');
|
|
150
|
+
}
|
|
151
|
+
finally {
|
|
152
|
+
rl.close();
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
return cmd;
|
|
156
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createQueryCommand = createQueryCommand;
|
|
4
|
+
const commander_1 = require("commander");
|
|
5
|
+
const client_1 = require("../../lts/client");
|
|
6
|
+
const config_1 = require("../../config");
|
|
7
|
+
const output_1 = require("../../output");
|
|
8
|
+
function createQueryCommand() {
|
|
9
|
+
const cmd = new commander_1.Command('query');
|
|
10
|
+
cmd.description('Query LTS logs')
|
|
11
|
+
.option('-g, --group-id <groupId>', 'Log group ID (can also be set via config)')
|
|
12
|
+
.option('-s, --stream-id <streamId>', 'Log stream ID (can also be set via config)')
|
|
13
|
+
.requiredOption('--start-time <startTime>', 'Start time (ISO 8601, e.g. 2024-01-01T00:00:00Z)')
|
|
14
|
+
.requiredOption('--end-time <endTime>', 'End time (ISO 8601, e.g. 2024-01-02T00:00:00Z)')
|
|
15
|
+
.option('--st <startTime>', 'Alias for --start-time')
|
|
16
|
+
.option('--et <endTime>', 'Alias for --end-time')
|
|
17
|
+
.option('-k, --keyword <keyword>', 'Search keyword')
|
|
18
|
+
.option('-l, --limit <limit>', 'Limit number of results', parseInt, 100)
|
|
19
|
+
.option('-o, --offset <offset>', 'Offset for pagination', parseInt, 0)
|
|
20
|
+
.option('-r, --reverse', 'Reverse order (newest first)')
|
|
21
|
+
.option('-f, --format <format>', 'Output format: json, table, pretty', 'pretty')
|
|
22
|
+
.option('--no-paginate', 'Disable auto-pagination (single page only)')
|
|
23
|
+
.action(async (options) => {
|
|
24
|
+
const config = (0, config_1.loadConfig)();
|
|
25
|
+
// Validate required credentials
|
|
26
|
+
if (!config.ak || !config.sk || !config.projectId) {
|
|
27
|
+
console.error('Error: Missing required credentials. Please configure them first:');
|
|
28
|
+
console.error('');
|
|
29
|
+
console.error(' Quick setup (recommended):');
|
|
30
|
+
console.error(' lts-cli init');
|
|
31
|
+
console.error('');
|
|
32
|
+
console.error(' Or set individually:');
|
|
33
|
+
console.error(' lts-cli config --set ak=<your-access-key>');
|
|
34
|
+
console.error(' lts-cli config --set sk=<your-secret-key>');
|
|
35
|
+
console.error(' lts-cli config --set projectId=<your-project-id>');
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
// groupId & streamId: CLI args take priority over config
|
|
39
|
+
const groupId = options.groupId || config.groupId;
|
|
40
|
+
const streamId = options.streamId || config.streamId;
|
|
41
|
+
if (!groupId || !streamId) {
|
|
42
|
+
console.error('Error: groupId and streamId are required. Provide them via:');
|
|
43
|
+
console.error(' lts-cli config --set groupId=<your-group-id>');
|
|
44
|
+
console.error(' lts-cli config --set streamId=<your-stream-id>');
|
|
45
|
+
console.error(' or use -g <groupId> -s <streamId> flags');
|
|
46
|
+
process.exit(1);
|
|
47
|
+
}
|
|
48
|
+
// --st / --et are aliases for --start-time / --end-time
|
|
49
|
+
const startTime = options.startTime || options.st;
|
|
50
|
+
const endTime = options.endTime || options.et;
|
|
51
|
+
if (!startTime || !endTime) {
|
|
52
|
+
console.error('Error: --start-time and --end-time are required.');
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
const format = ['json', 'table', 'pretty'].includes(options.format)
|
|
56
|
+
? options.format
|
|
57
|
+
: 'pretty';
|
|
58
|
+
const client = new client_1.LTSClient(config);
|
|
59
|
+
const params = {
|
|
60
|
+
groupId,
|
|
61
|
+
streamId,
|
|
62
|
+
startTime,
|
|
63
|
+
endTime,
|
|
64
|
+
keyword: options.keyword,
|
|
65
|
+
limit: options.limit,
|
|
66
|
+
offset: options.offset,
|
|
67
|
+
reverse: options.reverse,
|
|
68
|
+
};
|
|
69
|
+
try {
|
|
70
|
+
if (options.paginate) {
|
|
71
|
+
const result = await client.queryLogs(params);
|
|
72
|
+
console.log((0, output_1.formatOutput)(result, format));
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
// Auto-pagination: fetch all logs
|
|
76
|
+
const allLogs = [];
|
|
77
|
+
let offset = options.offset || 0;
|
|
78
|
+
const limit = options.limit || 100;
|
|
79
|
+
let hasMore = true;
|
|
80
|
+
while (hasMore) {
|
|
81
|
+
const pageParams = {
|
|
82
|
+
...params,
|
|
83
|
+
offset,
|
|
84
|
+
limit,
|
|
85
|
+
};
|
|
86
|
+
const result = await client.queryLogs(pageParams);
|
|
87
|
+
allLogs.push(...result.logs);
|
|
88
|
+
if (result.logs.length < limit) {
|
|
89
|
+
hasMore = false;
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
offset += limit;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
const result = {
|
|
96
|
+
logs: allLogs,
|
|
97
|
+
total: allLogs.length,
|
|
98
|
+
};
|
|
99
|
+
console.log((0, output_1.formatOutput)(result, format));
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
catch (error) {
|
|
103
|
+
const msg = error.message || String(error);
|
|
104
|
+
// Friendlier error messages for common issues
|
|
105
|
+
if (msg.includes('Invalid URL')) {
|
|
106
|
+
console.error('Error: Invalid endpoint URL. Make sure it starts with https://');
|
|
107
|
+
console.error(' Example: https://lts.cn-south-1.myhuaweicloud.com');
|
|
108
|
+
}
|
|
109
|
+
else if (msg.includes('Authentication failed') || msg.includes('401') || msg.includes('403')) {
|
|
110
|
+
console.error('Error: Authentication failed. Please check your AK/SK credentials:');
|
|
111
|
+
console.error(' lts-cli config --list');
|
|
112
|
+
}
|
|
113
|
+
else if (msg.includes('ENOTFOUND') || msg.includes('ECONNREFUSED')) {
|
|
114
|
+
console.error('Error: Cannot reach the LTS service. Please check your endpoint and network connection.');
|
|
115
|
+
}
|
|
116
|
+
else if (msg.includes('Request failed after retries')) {
|
|
117
|
+
console.error('Error: The service is temporarily unavailable. Please try again later.');
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
console.error('Error querying logs:', msg);
|
|
121
|
+
}
|
|
122
|
+
process.exit(1);
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
return cmd;
|
|
126
|
+
}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
const commander_1 = require("commander");
|
|
5
|
+
const query_1 = require("./commands/query");
|
|
6
|
+
const config_1 = require("./commands/config");
|
|
7
|
+
const init_1 = require("./commands/init");
|
|
8
|
+
const program = new commander_1.Command();
|
|
9
|
+
program
|
|
10
|
+
.name('lts-cli')
|
|
11
|
+
.description('Huawei Cloud LTS (Log Tank Service) CLI โ query logs from the terminal')
|
|
12
|
+
.version('1.0.0')
|
|
13
|
+
.addHelpText('after', `
|
|
14
|
+
Examples:
|
|
15
|
+
$ lts-cli init Setup credentials interactively
|
|
16
|
+
$ lts-cli config --list Show current configuration
|
|
17
|
+
$ lts-cli query --st 2024-01-01T00:00:00Z --et 2024-01-02T00:00:00Z
|
|
18
|
+
|
|
19
|
+
Environment variables:
|
|
20
|
+
LTS_AK Access Key
|
|
21
|
+
LTS_SK Secret Key
|
|
22
|
+
LTS_PROJECT_ID Project ID
|
|
23
|
+
LTS_REGION Region
|
|
24
|
+
LTS_ENDPOINT Service endpoint
|
|
25
|
+
LTS_GROUP_ID Default log group ID
|
|
26
|
+
LTS_STREAM_ID Default log stream ID
|
|
27
|
+
`);
|
|
28
|
+
program.addCommand((0, init_1.createInitCommand)());
|
|
29
|
+
program.addCommand((0, query_1.createQueryCommand)());
|
|
30
|
+
program.addCommand((0, config_1.createConfigCommand)());
|
|
31
|
+
// Shell completion support
|
|
32
|
+
const completionCmd = new commander_1.Command('completion')
|
|
33
|
+
.description('Generate shell completion script')
|
|
34
|
+
.argument('<shell>', 'Shell type: bash, zsh, or powershell')
|
|
35
|
+
.action((shell) => {
|
|
36
|
+
switch (shell) {
|
|
37
|
+
case 'bash':
|
|
38
|
+
console.log(`# lts-cli bash completion โ add to ~/.bashrc or ~/.bash_profile
|
|
39
|
+
_lts_cli_completion() {
|
|
40
|
+
local words cword
|
|
41
|
+
_get_comp_words_by_ref -n : words cword
|
|
42
|
+
local cur="\${words[cword]}"
|
|
43
|
+
|
|
44
|
+
if (( cword == 1 )); then
|
|
45
|
+
COMPREPLY=( $(compgen -W "query config init completion" -- "\$cur") )
|
|
46
|
+
return
|
|
47
|
+
fi
|
|
48
|
+
|
|
49
|
+
local cmd="\${words[1]}"
|
|
50
|
+
case "\$cmd" in
|
|
51
|
+
query)
|
|
52
|
+
case "\$prev" in
|
|
53
|
+
-g|--group-id|-s|--stream-id|-k|--keyword|-l|--limit|-o|--offset|-f|--format)
|
|
54
|
+
COMPREPLY=()
|
|
55
|
+
return
|
|
56
|
+
;;
|
|
57
|
+
*)
|
|
58
|
+
COMPREPLY=( $(compgen -W "-g --group-id -s --stream-id --start-time --end-time --st --et -k --keyword -l --limit -o --offset -r --reverse -f --format --no-paginate" -- "\$cur") )
|
|
59
|
+
return
|
|
60
|
+
;;
|
|
61
|
+
esac
|
|
62
|
+
;;
|
|
63
|
+
config)
|
|
64
|
+
COMPREPLY=( $(compgen -W "--set --get --list --unset" -- "\$cur") )
|
|
65
|
+
return
|
|
66
|
+
;;
|
|
67
|
+
completion)
|
|
68
|
+
COMPREPLY=( $(compgen -W "bash zsh powershell" -- "\$cur") )
|
|
69
|
+
return
|
|
70
|
+
;;
|
|
71
|
+
*)
|
|
72
|
+
COMPREPLY=()
|
|
73
|
+
return
|
|
74
|
+
;;
|
|
75
|
+
esac
|
|
76
|
+
}
|
|
77
|
+
complete -F _lts_cli_completion lts-cli`);
|
|
78
|
+
break;
|
|
79
|
+
case 'zsh':
|
|
80
|
+
console.log(`# lts-cli zsh completion โ add to ~/.zshrc
|
|
81
|
+
#compdef lts-cli
|
|
82
|
+
_lts_cli() {
|
|
83
|
+
local -a commands
|
|
84
|
+
commands=(
|
|
85
|
+
'query:Query LTS logs'
|
|
86
|
+
'config:Manage configuration'
|
|
87
|
+
'init:Interactive setup wizard'
|
|
88
|
+
'completion:Generate shell completion script'
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
local context state state_descr line
|
|
92
|
+
typeset -A opt_args
|
|
93
|
+
|
|
94
|
+
_arguments -C \\
|
|
95
|
+
'1: :->command' \\
|
|
96
|
+
'*:: :->args'
|
|
97
|
+
|
|
98
|
+
case "\$state" in
|
|
99
|
+
command)
|
|
100
|
+
_describe 'command' commands
|
|
101
|
+
;;
|
|
102
|
+
args)
|
|
103
|
+
case "\$line[1]" in
|
|
104
|
+
query)
|
|
105
|
+
_arguments \\
|
|
106
|
+
'-g[Log group ID]:group ID:' \\
|
|
107
|
+
'-s[Log stream ID]:stream ID:' \\
|
|
108
|
+
'--start-time[Start time (ISO 8601)]:time:' \\
|
|
109
|
+
'--end-time[End time (ISO 8601)]:time:' \\
|
|
110
|
+
'--st[Alias for --start-time]:time:' \\
|
|
111
|
+
'--et[Alias for --end-time]:time:' \\
|
|
112
|
+
'-k[Search keyword]:keyword:' \\
|
|
113
|
+
'-l[Limit]:number:' \\
|
|
114
|
+
'-o[Offset]:number:' \\
|
|
115
|
+
'-r[Reverse order]' \\
|
|
116
|
+
'-f[Output format]:format:(json table pretty)' \\
|
|
117
|
+
'--no-paginate[Disable pagination]'
|
|
118
|
+
;;
|
|
119
|
+
config)
|
|
120
|
+
_arguments \\
|
|
121
|
+
'--set[Set config value]:key=value:' \\
|
|
122
|
+
'--get[Get config value]:key:' \\
|
|
123
|
+
'--list[List all config]' \\
|
|
124
|
+
'--unset[Remove config value]:key:'
|
|
125
|
+
;;
|
|
126
|
+
completion)
|
|
127
|
+
_values 'shell' bash zsh powershell
|
|
128
|
+
;;
|
|
129
|
+
esac
|
|
130
|
+
;;
|
|
131
|
+
esac
|
|
132
|
+
}
|
|
133
|
+
compdef _lts_cli lts-cli`);
|
|
134
|
+
break;
|
|
135
|
+
case 'powershell':
|
|
136
|
+
console.log(`# lts-cli PowerShell completion โ add to $PROFILE
|
|
137
|
+
Register-ArgumentCompleter -CommandName lts-cli -ScriptBlock {
|
|
138
|
+
param(\$wordToComplete, \$commandAst, \$cursorPosition)
|
|
139
|
+
|
|
140
|
+
\$commands = @('query', 'config', 'init', 'completion')
|
|
141
|
+
|
|
142
|
+
\$args = \$commandAst.ToString() -split '\\s+'
|
|
143
|
+
if (\$args.Count -le 1) {
|
|
144
|
+
return \$commands | Where-Object { \$_ -like "\$wordToComplete*" }
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
switch (\$args[1]) {
|
|
148
|
+
'query' {
|
|
149
|
+
\$opts = @('-g', '-s', '--start-time', '--end-time', '--st', '--et', '-k', '-l', '-o', '-r', '-f', '--no-paginate')
|
|
150
|
+
return \$opts | Where-Object { \$_ -like "\$wordToComplete*" }
|
|
151
|
+
}
|
|
152
|
+
'config' {
|
|
153
|
+
\$opts = @('--set', '--get', '--list', '--unset')
|
|
154
|
+
return \$opts | Where-Object { \$_ -like "\$wordToComplete*" }
|
|
155
|
+
}
|
|
156
|
+
'completion' {
|
|
157
|
+
return @('bash', 'zsh', 'powershell') | Where-Object { \$_ -like "\$wordToComplete*" }
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}`);
|
|
161
|
+
break;
|
|
162
|
+
default:
|
|
163
|
+
console.error(`Unknown shell: ${shell}. Supported: bash, zsh, powershell`);
|
|
164
|
+
process.exit(1);
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
program.addCommand(completionCmd);
|
|
168
|
+
program.parse();
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.queryOptions = void 0;
|
|
4
|
+
exports.queryOptions = {
|
|
5
|
+
groupId: { short: '-g', long: '--group-id <groupId>', description: 'Log group ID', required: true },
|
|
6
|
+
streamId: { short: '-s', long: '--stream-id <streamId>', description: 'Log stream ID', required: true },
|
|
7
|
+
startTime: { short: '--start-time <startTime>', long: '--start-time <startTime>', description: 'Start time (ISO 8601)', required: true },
|
|
8
|
+
endTime: { short: '--end-time <endTime>', long: '--end-time <endTime>', description: 'End time (ISO 8601)', required: true },
|
|
9
|
+
keyword: { short: '-k', long: '--keyword <keyword>', description: 'Search keyword', required: false },
|
|
10
|
+
limit: { short: '-l', long: '--limit <limit>', description: 'Limit number of results', required: false },
|
|
11
|
+
offset: { short: '-o', long: '--offset <offset>', description: 'Offset for pagination', required: false },
|
|
12
|
+
reverse: { short: '-r', long: '--reverse', description: 'Reverse order', required: false },
|
|
13
|
+
format: { short: '-f', long: '--format <format>', description: 'Output format: json, table, pretty', required: false },
|
|
14
|
+
noPaginate: { short: '', long: '--no-paginate', description: 'Disable pagination', required: false },
|
|
15
|
+
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.loadConfig = loadConfig;
|
|
4
|
+
exports.getConfig = getConfig;
|
|
5
|
+
exports.setConfig = setConfig;
|
|
6
|
+
exports.listConfig = listConfig;
|
|
7
|
+
exports.unsetConfig = unsetConfig;
|
|
8
|
+
const store_1 = require("./store");
|
|
9
|
+
const ENV_MAP = {
|
|
10
|
+
ak: 'LTS_AK',
|
|
11
|
+
sk: 'LTS_SK',
|
|
12
|
+
projectId: 'LTS_PROJECT_ID',
|
|
13
|
+
region: 'LTS_REGION',
|
|
14
|
+
endpoint: 'LTS_ENDPOINT',
|
|
15
|
+
groupId: 'LTS_GROUP_ID',
|
|
16
|
+
streamId: 'LTS_STREAM_ID',
|
|
17
|
+
};
|
|
18
|
+
function getEnvValue(key) {
|
|
19
|
+
const envKey = ENV_MAP[key];
|
|
20
|
+
if (envKey) {
|
|
21
|
+
return process.env[envKey];
|
|
22
|
+
}
|
|
23
|
+
return undefined;
|
|
24
|
+
}
|
|
25
|
+
function loadConfig() {
|
|
26
|
+
const config = (0, store_1.readConfigFile)();
|
|
27
|
+
return {
|
|
28
|
+
ak: getEnvValue('ak') ?? config.ak ?? '',
|
|
29
|
+
sk: getEnvValue('sk') ?? config.sk ?? '',
|
|
30
|
+
projectId: getEnvValue('projectId') ?? config.projectId ?? '',
|
|
31
|
+
region: getEnvValue('region') ?? config.region ?? '',
|
|
32
|
+
endpoint: getEnvValue('endpoint') ?? config.endpoint ?? '',
|
|
33
|
+
groupId: getEnvValue('groupId') ?? config.groupId,
|
|
34
|
+
streamId: getEnvValue('streamId') ?? config.streamId,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
function getConfig(key) {
|
|
38
|
+
const envValue = getEnvValue(key);
|
|
39
|
+
if (envValue !== undefined) {
|
|
40
|
+
return envValue;
|
|
41
|
+
}
|
|
42
|
+
const config = (0, store_1.readConfigFile)();
|
|
43
|
+
return config[key];
|
|
44
|
+
}
|
|
45
|
+
function setConfig(key, value) {
|
|
46
|
+
const config = (0, store_1.readConfigFile)();
|
|
47
|
+
config[key] = value;
|
|
48
|
+
(0, store_1.writeConfigFile)(config);
|
|
49
|
+
}
|
|
50
|
+
function listConfig() {
|
|
51
|
+
return (0, store_1.readConfigFile)();
|
|
52
|
+
}
|
|
53
|
+
function unsetConfig(key) {
|
|
54
|
+
const config = (0, store_1.readConfigFile)();
|
|
55
|
+
delete config[key];
|
|
56
|
+
(0, store_1.writeConfigFile)(config);
|
|
57
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.readConfigFile = readConfigFile;
|
|
37
|
+
exports.writeConfigFile = writeConfigFile;
|
|
38
|
+
exports.getConfigFilePath = getConfigFilePath;
|
|
39
|
+
const fs = __importStar(require("fs"));
|
|
40
|
+
const path = __importStar(require("path"));
|
|
41
|
+
const os = __importStar(require("os"));
|
|
42
|
+
const CONFIG_DIR = path.join(os.homedir(), '.lts-cli');
|
|
43
|
+
const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
|
|
44
|
+
function ensureDir(dir) {
|
|
45
|
+
if (!fs.existsSync(dir)) {
|
|
46
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
function readConfigFile() {
|
|
50
|
+
try {
|
|
51
|
+
if (!fs.existsSync(CONFIG_FILE)) {
|
|
52
|
+
return {};
|
|
53
|
+
}
|
|
54
|
+
const data = fs.readFileSync(CONFIG_FILE, 'utf-8');
|
|
55
|
+
return JSON.parse(data);
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
return {};
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function writeConfigFile(config) {
|
|
62
|
+
ensureDir(CONFIG_DIR);
|
|
63
|
+
fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2), 'utf-8');
|
|
64
|
+
}
|
|
65
|
+
function getConfigFilePath() {
|
|
66
|
+
return CONFIG_FILE;
|
|
67
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.LTSClient = void 0;
|
|
4
|
+
const LtsClient_1 = require("@huaweicloud/huaweicloud-sdk-lts/v2/LtsClient");
|
|
5
|
+
const ClientBuilder_1 = require("@huaweicloud/huaweicloud-sdk-core/ClientBuilder");
|
|
6
|
+
const BasicCredentials_1 = require("@huaweicloud/huaweicloud-sdk-core/auth/BasicCredentials");
|
|
7
|
+
const region_1 = require("@huaweicloud/huaweicloud-sdk-core/region/region");
|
|
8
|
+
const ListLogsRequest_1 = require("@huaweicloud/huaweicloud-sdk-lts/v2/model/ListLogsRequest");
|
|
9
|
+
const QueryLtsLogParams_1 = require("@huaweicloud/huaweicloud-sdk-lts/v2/model/QueryLtsLogParams");
|
|
10
|
+
function sleep(ms) {
|
|
11
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
12
|
+
}
|
|
13
|
+
class LTSClient {
|
|
14
|
+
client;
|
|
15
|
+
config;
|
|
16
|
+
constructor(config) {
|
|
17
|
+
this.config = config;
|
|
18
|
+
const credentials = new BasicCredentials_1.BasicCredentials()
|
|
19
|
+
.withAk(config.ak)
|
|
20
|
+
.withSk(config.sk)
|
|
21
|
+
.withProjectId(config.projectId);
|
|
22
|
+
const builder = new ClientBuilder_1.ClientBuilder((hcClient) => new LtsClient_1.LtsClient(hcClient));
|
|
23
|
+
if (config.endpoint) {
|
|
24
|
+
builder.withEndpoint(config.endpoint);
|
|
25
|
+
}
|
|
26
|
+
if (config.region) {
|
|
27
|
+
builder.withRegion(new region_1.Region(config.region, config.endpoint || ''));
|
|
28
|
+
}
|
|
29
|
+
builder.withCredential(credentials);
|
|
30
|
+
this.client = builder.build();
|
|
31
|
+
}
|
|
32
|
+
async queryLogs(params) {
|
|
33
|
+
const queryParams = new QueryLtsLogParams_1.QueryLtsLogParams()
|
|
34
|
+
.withStartTime(new Date(params.startTime).getTime().toString())
|
|
35
|
+
.withEndTime(new Date(params.endTime).getTime().toString())
|
|
36
|
+
.withLimit(params.limit ?? 100);
|
|
37
|
+
if (params.keyword) {
|
|
38
|
+
queryParams.withKeywords(params.keyword);
|
|
39
|
+
}
|
|
40
|
+
if (params.reverse !== undefined) {
|
|
41
|
+
queryParams.withIsDesc(params.reverse);
|
|
42
|
+
}
|
|
43
|
+
const request = new ListLogsRequest_1.ListLogsRequest(params.groupId, params.streamId, 'application/json')
|
|
44
|
+
.withBody(queryParams);
|
|
45
|
+
const response = await this.retryRequest(() => this.client.listLogs(request));
|
|
46
|
+
const logs = (response.logs || []).map((log) => ({
|
|
47
|
+
logTime: log.lineNum ?? '',
|
|
48
|
+
content: log.content ?? '',
|
|
49
|
+
labels: log.labels ?? {},
|
|
50
|
+
}));
|
|
51
|
+
return {
|
|
52
|
+
logs,
|
|
53
|
+
total: response.count ?? logs.length,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
async retryRequest(fn, maxRetries = 3) {
|
|
57
|
+
let lastError;
|
|
58
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
59
|
+
try {
|
|
60
|
+
return await fn();
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
lastError = error;
|
|
64
|
+
const statusCode = error.statusCode || error.httpStatusCode || 0;
|
|
65
|
+
if (statusCode === 401 || statusCode === 403) {
|
|
66
|
+
throw new Error(`Authentication failed (HTTP ${statusCode}). Please check your AK/SK credentials.`);
|
|
67
|
+
}
|
|
68
|
+
if (statusCode === 429) {
|
|
69
|
+
if (attempt < maxRetries) {
|
|
70
|
+
const delay = Math.pow(2, attempt) * 1000;
|
|
71
|
+
await sleep(delay);
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
if (statusCode >= 500) {
|
|
76
|
+
if (attempt < maxRetries) {
|
|
77
|
+
const delay = Math.pow(2, attempt) * 1000;
|
|
78
|
+
await sleep(delay);
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
throw error;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
throw lastError ?? new Error('Request failed after retries');
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
exports.LTSClient = LTSClient;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.LTSClient = void 0;
|
|
18
|
+
var client_1 = require("./client");
|
|
19
|
+
Object.defineProperty(exports, "LTSClient", { enumerable: true, get: function () { return client_1.LTSClient; } });
|
|
20
|
+
__exportStar(require("./types"), exports);
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.formatOutput = formatOutput;
|
|
7
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
+
const json_1 = require("./json");
|
|
9
|
+
const table_1 = require("./table");
|
|
10
|
+
function formatOutput(data, format) {
|
|
11
|
+
switch (format) {
|
|
12
|
+
case 'json':
|
|
13
|
+
return (0, json_1.formatJson)(data);
|
|
14
|
+
case 'table':
|
|
15
|
+
return (0, table_1.formatTable)(data);
|
|
16
|
+
case 'pretty':
|
|
17
|
+
return formatPretty(data);
|
|
18
|
+
default:
|
|
19
|
+
return (0, json_1.formatJson)(data);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function formatPretty(data) {
|
|
23
|
+
if (data.logs.length === 0) {
|
|
24
|
+
return chalk_1.default.yellow('No logs found.');
|
|
25
|
+
}
|
|
26
|
+
const lines = [];
|
|
27
|
+
lines.push(chalk_1.default.bold.green(`Found ${data.total} logs:`));
|
|
28
|
+
lines.push('');
|
|
29
|
+
for (const log of data.logs) {
|
|
30
|
+
lines.push(chalk_1.default.cyan(`[${log.logTime}]`));
|
|
31
|
+
lines.push(chalk_1.default.white(log.content));
|
|
32
|
+
if (Object.keys(log.labels).length > 0) {
|
|
33
|
+
const labels = Object.entries(log.labels)
|
|
34
|
+
.map(([k, v]) => `${k}=${v}`)
|
|
35
|
+
.join(', ');
|
|
36
|
+
lines.push(chalk_1.default.gray(` labels: ${labels}`));
|
|
37
|
+
}
|
|
38
|
+
lines.push('');
|
|
39
|
+
}
|
|
40
|
+
return lines.join('\n');
|
|
41
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.formatTable = formatTable;
|
|
7
|
+
const cli_table3_1 = __importDefault(require("cli-table3"));
|
|
8
|
+
function formatTable(data) {
|
|
9
|
+
if (data.logs.length === 0) {
|
|
10
|
+
return 'No logs found.';
|
|
11
|
+
}
|
|
12
|
+
const table = new cli_table3_1.default({
|
|
13
|
+
head: ['Time', 'Content', 'Labels'],
|
|
14
|
+
colWidths: [25, 50, 30],
|
|
15
|
+
wordWrap: true,
|
|
16
|
+
});
|
|
17
|
+
for (const log of data.logs) {
|
|
18
|
+
const labelsStr = Object.entries(log.labels)
|
|
19
|
+
.map(([k, v]) => `${k}=${v}`)
|
|
20
|
+
.join(', ');
|
|
21
|
+
table.push([log.logTime, log.content, labelsStr]);
|
|
22
|
+
}
|
|
23
|
+
return table.toString();
|
|
24
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@cvtoolman/huawei-lts-cli",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Command-line tool for querying Huawei Cloud Log Tank Service (LTS) logs",
|
|
5
|
+
"main": "dist/cli/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"lts-cli": "./dist/cli/index.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist/"
|
|
11
|
+
],
|
|
12
|
+
"scripts": {
|
|
13
|
+
"build": "tsc",
|
|
14
|
+
"dev": "tsx src/cli/index.ts",
|
|
15
|
+
"start": "node dist/cli/index.js",
|
|
16
|
+
"clean": "node -e \"require('fs').rmSync('dist', {recursive: true, force: true})\"",
|
|
17
|
+
"prepublishOnly": "npm run clean && npm run build"
|
|
18
|
+
},
|
|
19
|
+
"keywords": [
|
|
20
|
+
"huawei-cloud",
|
|
21
|
+
"huawei",
|
|
22
|
+
"lts",
|
|
23
|
+
"log-tank-service",
|
|
24
|
+
"logs",
|
|
25
|
+
"cli",
|
|
26
|
+
"log-query",
|
|
27
|
+
"cloud-logging"
|
|
28
|
+
],
|
|
29
|
+
"author": "cvtoolman",
|
|
30
|
+
"license": "MIT",
|
|
31
|
+
"repository": {
|
|
32
|
+
"type": "git",
|
|
33
|
+
"url": "git+https://github.com/jcxy/huawei-lts-cli.git"
|
|
34
|
+
},
|
|
35
|
+
"bugs": {
|
|
36
|
+
"url": "https://github.com/jcxy/huawei-lts-cli/issues"
|
|
37
|
+
},
|
|
38
|
+
"homepage": "https://github.com/jcxy/huawei-lts-cli#readme",
|
|
39
|
+
"engines": {
|
|
40
|
+
"node": ">=16.0.0"
|
|
41
|
+
},
|
|
42
|
+
"dependencies": {
|
|
43
|
+
"@huaweicloud/huaweicloud-sdk-core": "latest",
|
|
44
|
+
"@huaweicloud/huaweicloud-sdk-lts": "latest",
|
|
45
|
+
"chalk": "^4.1.2",
|
|
46
|
+
"cli-table3": "latest",
|
|
47
|
+
"commander": "latest",
|
|
48
|
+
"uuid": "^9.0.0"
|
|
49
|
+
},
|
|
50
|
+
"devDependencies": {
|
|
51
|
+
"@types/node": "latest",
|
|
52
|
+
"tsx": "latest",
|
|
53
|
+
"typescript": "latest"
|
|
54
|
+
}
|
|
55
|
+
}
|