@cvtoolman/huawei-lts-cli 2.0.0 → 2.1.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/README.md CHANGED
@@ -3,6 +3,7 @@
3
3
  **简体中文** | [English](README_en.md)
4
4
 
5
5
  [![npm version](https://img.shields.io/npm/v/@cvtoolman/huawei-lts-cli.svg)](https://www.npmjs.com/package/@cvtoolman/huawei-lts-cli)
6
+ [![CI](https://github.com/jcxy/huawei-lts-cli/actions/workflows/ci.yml/badge.svg)](https://github.com/jcxy/huawei-lts-cli/actions/workflows/ci.yml)
6
7
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
7
8
 
8
9
  > 让你的 AI 编程助手(Claude Code、Cursor、Copilot、Qoder 等)通过自然语言查询华为云日志服务(LTS)。
@@ -60,14 +61,15 @@ npm install -g @cvtoolman/huawei-lts-cli
60
61
  lts-cli init
61
62
  ```
62
63
 
63
- 向导会依次询问 AK、SK、Project ID、Region,一次配好。同时会生成两个文件:
64
+ 向导会依次询问 AK、SK、Project ID、Region,一次配好,只写一个文件:
64
65
 
65
66
  | 文件 | 内容 | 谁读 |
66
67
  |---|---|---|
67
68
  | `~/.lts-cli/config.json` | 凭证、默认日志组/流、`traceField`(权限 0600) | 仅 CLI |
68
- | `~/.lts-cli/profile.md` | 业务词表:把「订单服务」映射到日志里的实际取值 | 仅 AI |
69
69
 
70
- **CLI 不读 `profile.md`,AI 不读 `config.json`。** 后者含明文 AK/SK,任何让 AI 读它的设计都是错的。
70
+ **这个目录不要整个交给 AI**,里面是明文 AK/SK。要看配置用 `lts-cli config --list`(已脱敏),不需要打开文件。
71
+
72
+ 日志组有哪些、字段叫什么、值长什么样——这些事实全部靠 `lts-cli groups` / `streams` / `fields` 现查,仓库和配置里都不存第二份。
71
73
 
72
74
  拿到控制台日志搜索页的 URL 时,可以直接导入 region / groupId / streamId:
73
75
 
@@ -77,27 +79,34 @@ lts-cli init --from-url "<控制台URL>"
77
79
 
78
80
  ### 3. 安装 Skill(给 AI 工具用)
79
81
 
80
- skill 目录含 `SKILL.md` 和 2 个参考文档,整目录复制:
82
+ 用 [skills CLI](https://skills.sh) 一条命令装到对应工具的目录(支持 75+ 种 agent):
81
83
 
82
84
  ```bash
83
- # Claude Code
84
- git clone --depth 1 https://github.com/jcxy/huawei-lts-cli.git /tmp/huawei-lts-cli
85
- mkdir -p .claude/skills/huawei-lts-log-query
86
- cp -r /tmp/huawei-lts-cli/skills/huawei-lts-log-query/. .claude/skills/huawei-lts-log-query/
87
-
88
- # Qoder:把 .claude/skills/ 换成 .qoder/skills/
89
- # 其他工具:换成对应目录(.cursor/skills/ 等)
85
+ npx skills add jcxy/huawei-lts-cli -a qoder # Qoder
86
+ npx skills add jcxy/huawei-lts-cli -a claude-code # Claude Code
87
+ npx skills add jcxy/huawei-lts-cli -a cursor # Cursor
88
+ npx skills add jcxy/huawei-lts-cli # 交互式选 agent
90
89
  ```
91
90
 
92
- Windows PowerShell:
91
+ 装完重启 AI 工具:skill 在会话启动时读进上下文,已经开着的会话不会热加载。日常用 `npx skills update` 更新、`npx skills remove` 卸载。
93
92
 
94
- ```powershell
95
- git clone --depth 1 https://github.com/jcxy/huawei-lts-cli.git $env:TEMP\huawei-lts-cli
96
- New-Item -ItemType Directory -Force .claude\skills\huawei-lts-log-query | Out-Null
97
- Copy-Item -Recurse -Force $env:TEMP\huawei-lts-cli\skills\huawei-lts-log-query\* .claude\skills\huawei-lts-log-query\
93
+ 不想用 npx 也可以手动复制(任何工具都适用):
94
+
95
+ ```bash
96
+ git clone --depth 1 https://github.com/jcxy/huawei-lts-cli.git /tmp/huawei-lts-cli
97
+ cp -r /tmp/huawei-lts-cli/skills/huawei-lts-log-query <目标 skills 目录>/
98
98
  ```
99
99
 
100
- ### 4. 命令行直接使用(不依赖 AI)
100
+ > `npx skills` 默认用软链指向一份 git clone,更新时一起同步。用 `--copy`(Windows 无软链权限时的退路)或插件方式安装时,升级会整目录替换——如果你在里面放了业务词表,先备份。
101
+
102
+ ### 4. 业务词表(可选)
103
+
104
+ AI 不知道「订单服务」在你的日志里叫什么。两条路,都不需要配置文件:
105
+
106
+ - **默认行为**:AI 用管道 SQL 实时枚举候选值(`* | SELECT appName, count(*) GROUP BY appName`),再问你挑哪一个。不落盘,也不会过期。
107
+ - **想省这一步**:在 skill 的 `references/vocab.md` 里写映射。这个文件默认不存在,自己建;已被 `.gitignore` 排除,不会被误提交。
108
+
109
+ ### 5. 命令行直接使用(不依赖 AI)
101
110
 
102
111
  ```bash
103
112
  # 最近 2 小时最新 50 条
@@ -216,7 +225,19 @@ lts-cli config --set traceField=REQUEST_ID
216
225
 
217
226
  不在表里时用 `--set endpoint=https://...` 手动指定,或参考 [华为云 LTS 终端节点](https://support.huaweicloud.com/api-lts/lts_api_0011.html)。
218
227
 
219
- ## 从 1.x 升级到 2.0
228
+ ## 升级说明
229
+
230
+ ### 从 2.0 升级到 2.1
231
+
232
+ | 变更 | 说明 |
233
+ |---|---|
234
+ | `init` 不再生成 `~/.lts-cli/profile.md` | 业务词表改为 skill 目录下的 `references/vocab.md`。旧文件可以删,留着也没影响(CLI 从来没读过它) |
235
+ | SKILL.md 增加运行时枚举候选值 | 没有词表也能定位应用名 |
236
+ | 新增 `npx skills` 安装方式 | 见上文「安装 Skill」 |
237
+
238
+ 命令行参数没有变化,脚本不需要改。
239
+
240
+ ### 从 1.x 升级到 2.0
220
241
 
221
242
  2.0 修了三个会静默出错的问题,因此有破坏性变更:
222
243
 
@@ -243,9 +264,15 @@ npm test # node:test,覆盖纯函数与游标推进
243
264
  npm run typecheck # tsc --noEmit
244
265
  ```
245
266
 
246
- 测试使用 Node 内置的 `node:test` + `tsx`,不引入额外测试依赖。跑测试需要 Node >= 20.6(`--import` 的要求)。
267
+ 测试使用 Node 内置的 `node:test` + `tsx`,不引入额外测试依赖。跑测试需要 Node >= 22——`node --test` 的通配符支持直到 22 才落地,20 上会报 `Could not find`。CLI 本身在 Node 18 上可以正常运行,CI 的 typecheck/build 覆盖 18/20/22。
268
+
269
+ 架构约定见 [CONTEXT.md](CONTEXT.md) 与 [docs/adr/](docs/adr/)。提交前请读 [CONTRIBUTING.md](CONTRIBUTING.md)——里面写了这个仓库唯一不能被破坏的原则:**同一个事实只允许有一个来源**。
270
+
271
+ ## 参与
247
272
 
248
- 架构约定见 [CONTEXT.md](CONTEXT.md) 与 [docs/adr/](docs/adr/)。
273
+ - 报告问题:[issue 模板](https://github.com/jcxy/huawei-lts-cli/issues/new/choose)(请勿贴真实凭证、日志组 ID 或内部业务名)
274
+ - 安全问题:[SECURITY.md](SECURITY.md),走私密通道,不要开公开 issue
275
+ - 版本变更:[CHANGELOG.md](CHANGELOG.md)
249
276
 
250
277
  ## License
251
278
 
@@ -35,24 +35,28 @@ var __importStar = (this && this.__importStar) || (function () {
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.createInitCommand = createInitCommand;
37
37
  const commander_1 = require("commander");
38
+ const fs = __importStar(require("fs"));
39
+ const path = __importStar(require("path"));
38
40
  const readline = __importStar(require("readline"));
39
41
  const config_1 = require("../../config");
40
- const profile_1 = require("../../config/profile");
42
+ const store_1 = require("../../config/store");
41
43
  function ask(rl, question) {
42
44
  return new Promise((resolve) => {
43
45
  rl.question(question, (answer) => resolve(answer.trim()));
44
46
  });
45
47
  }
46
- /** 写 profile.md 骨架并告知路径。已存在则绝不覆盖——那是用户手写的内容。 */
47
- function reportProfileSkeleton() {
48
- const { path, created } = (0, profile_1.writeProfileSkeleton)();
49
- if (created) {
50
- console.log(` profile.md = ${path} (skeleton created)`);
51
- console.log(' 业务词表:把「订单服务」这类称呼映射到日志里的实际取值');
52
- }
53
- else {
54
- console.log(` profile.md = ${path} (existing, left untouched)`);
48
+ /**
49
+ * 2.0 及更早版本会在凭证目录里生成一份业务词表骨架,那是把 AI 引进
50
+ * 明文 AK/SK 所在目录的入口。2.1 起不再生成,业务词表改由 skill 的
51
+ * `references/` 承载。这里只对老用户的残留文件做一次性提示,绝不代删。
52
+ */
53
+ function reportLegacyProfile() {
54
+ const legacy = path.join((0, store_1.getConfigDir)(), 'profile.md');
55
+ if (!fs.existsSync(legacy)) {
56
+ return;
55
57
  }
58
+ console.log(` note: ${legacy} is no longer used since 2.1 — safe to delete.`);
59
+ console.log(' Business vocabulary now lives in the skill: references/vocab.md');
56
60
  }
57
61
  const ENDPOINT_MAP = {
58
62
  'cn-north-1': 'lts.cn-north-1.myhuaweicloud.com',
@@ -156,7 +160,7 @@ function createInitCommand() {
156
160
  }
157
161
  console.log('');
158
162
  console.log('✅ Configuration saved!');
159
- reportProfileSkeleton();
163
+ reportLegacyProfile();
160
164
  console.log('');
161
165
  console.log('Try it out:');
162
166
  if (groupId && streamId) {
@@ -219,7 +223,7 @@ function importFromConsoleUrl(raw) {
219
223
  }
220
224
  console.log('');
221
225
  console.log('✅ Configuration saved!');
222
- reportProfileSkeleton();
226
+ reportLegacyProfile();
223
227
  console.log('');
224
228
  console.log('Next steps:');
225
229
  console.log(' 1. Configure credentials if not done yet: lts-cli init');
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@cvtoolman/huawei-lts-cli",
3
- "version": "2.0.0",
3
+ "version": "2.1.0",
4
4
  "description": "Command-line tool for querying Huawei Cloud Log Tank Service (LTS) logs",
5
5
  "main": "dist/cli/index.js",
6
6
  "bin": {
7
- "lts-cli": "./dist/cli/index.js"
7
+ "lts-cli": "dist/cli/index.js"
8
8
  },
9
9
  "files": [
10
10
  "dist/"
@@ -1,108 +0,0 @@
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.getProfilePath = getProfilePath;
37
- exports.profileExists = profileExists;
38
- exports.writeProfileSkeleton = writeProfileSkeleton;
39
- const fs = __importStar(require("fs"));
40
- const path = __importStar(require("path"));
41
- const store_1 = require("./store");
42
- /**
43
- * `profile.md` 是给人写、给 AI 读的业务词表。CLI 从不读它。
44
- *
45
- * 分界线:`config.json` 存 CLI 的行为(凭证、traceField、默认 group/stream);
46
- * `profile.md` 存 API 拿不到的东西(业务词 → 日志里的值、命名约定、黑话)。
47
- * 字段清单、日志组/流 ID 一律不进 profile —— 那是第二份会过期的真相,
48
- * 用 `lts-cli groups` / `streams` / `fields` 现查。
49
- */
50
- function getProfilePath() {
51
- return path.join((0, store_1.getConfigDir)(), 'profile.md');
52
- }
53
- function profileExists() {
54
- return fs.existsSync(getProfilePath());
55
- }
56
- const SKELETON = `# LTS 业务词表(本机私有)
57
-
58
- > 这份文件给人改、给 AI 读。\`lts-cli\` 本身**不读**它——CLI 的行为配置在 \`~/.lts-cli/config.json\`。
59
- >
60
- > 只写 API 拿不到的东西:业务词怎么落到日志里的值、团队自己的命名约定、黑话。
61
- > **不要**把日志组/日志流 ID、字段清单写进来——用 \`lts-cli groups\` / \`lts-cli streams\` /
62
- > \`lts-cli fields\` 现查。抄进文档的那一刻它就开始过期。
63
-
64
- ## 1. 业务称呼 → 日志里的取值
65
-
66
- 查日志前先在这里翻译,避免每次重新猜应用名。
67
-
68
- | 业务称呼 | 日志中的取值 | 说明 |
69
- | --- | --- | --- |
70
- | 订单服务 | (填字段值和值) | 例如 appName 或 service_name 的取值 |
71
-
72
- ## 2. 字段含义
73
-
74
- 只写「这个字段在我们这里表示什么」。类型、是否大小写敏感、分词符请用 \`lts-cli fields\` 查。
75
-
76
- | 字段 | 含义 |
77
- | --- | --- |
78
- | (字段名) | (说明) |
79
-
80
- ## 3. 命名约定与分词陷阱
81
-
82
- - 哪些值含连字符/下划线,必须加双引号才会被当成一个词
83
- - 值的大小写约定(全大写?小写?)
84
- - 哪些字段看起来该有索引、实际上没建(没建索引的字段只能全文检索命中)
85
-
86
- ## 4. 团队黑话
87
-
88
- | 说法 | 实际指什么 |
89
- | --- | --- |
90
- | (黑话) | (含义) |
91
-
92
- ## 5. 常用排查入口
93
-
94
- | 想查什么 | 建议命令 |
95
- | --- | --- |
96
- | 某个 ID 的完整链路 | \`lts-cli trace <id>\` |
97
- | 某业务最近的报错 | \`lts-cli query --last 1h -q '(字段):(值)'\` |
98
- | 先看清楚这条流有什么字段 | \`lts-cli fields\` |
99
- `;
100
- function writeProfileSkeleton() {
101
- const target = getProfilePath();
102
- if (fs.existsSync(target)) {
103
- return { path: target, created: false };
104
- }
105
- fs.mkdirSync(path.dirname(target), { recursive: true, mode: 0o700 });
106
- fs.writeFileSync(target, SKELETON, { encoding: 'utf-8', mode: 0o600 });
107
- return { path: target, created: true };
108
- }