@lxzc-tech/star-cli 0.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 ADDED
@@ -0,0 +1,35 @@
1
+ # @lxzc-tech/star-cli
2
+
3
+ 面向 AI Agent 的数据仓库指标披露 CLI。
4
+
5
+ ## 安装
6
+
7
+ ```bash
8
+ npm install -g @lxzc-tech/star-cli
9
+ ```
10
+
11
+ 安装时会自动下载适配当前系统(macOS / Linux / Windows,x64 / arm64)的原生二进制,并校验 checksum。
12
+
13
+ ## 使用
14
+
15
+ ```bash
16
+ star-cli --help
17
+ star-cli auth set --server https://... --cert ... --key ...
18
+ star-cli metrics list --format json
19
+ ```
20
+
21
+ 完整命令说明见 `star-cli <command> --help`。
22
+
23
+ ## 支持的平台
24
+
25
+ - macOS (Intel / Apple Silicon)
26
+ - Linux (x64 / arm64)
27
+ - Windows (x64 / arm64)
28
+
29
+ 其他平台需要从源码构建。
30
+
31
+ ## 卸载
32
+
33
+ ```bash
34
+ npm uninstall -g @lxzc-tech/star-cli
35
+ ```
@@ -0,0 +1,32 @@
1
+ #!/usr/bin/env node
2
+ // npm 的 bin 入口。真正的工作由 postinstall(scripts/install.js)下载到本目录
3
+ // 的原生二进制(star-cli / star-cli.exe)完成,这里只做参数与 stdio 转发。
4
+ //
5
+ // 用 Node 脚本作为 bin 入口(而不是直接把 package.json 的 bin 指向原生二进制)
6
+ // 是跨平台 npm 包的标准做法:npm 在 Windows 上为 bin 生成的是 .cmd 包装器,
7
+ // 需要能被当作脚本执行的文件,不能直接 exec 一个原生可执行文件。
8
+ 'use strict';
9
+
10
+ const path = require('path');
11
+ const os = require('os');
12
+ const { spawnSync } = require('child_process');
13
+
14
+ const binName = os.platform() === 'win32' ? 'star-cli.exe' : 'star-cli';
15
+ const binPath = path.join(__dirname, binName);
16
+
17
+ const result = spawnSync(binPath, process.argv.slice(2), { stdio: 'inherit' });
18
+
19
+ if (result.error) {
20
+ if (result.error.code === 'ENOENT') {
21
+ console.error(
22
+ '[star-cli] 未找到已安装的二进制文件。postinstall 可能被跳过了' +
23
+ '(常见于 --ignore-scripts 安装),请重新运行:\n' +
24
+ ` npm rebuild @lxzc-tech/star-cli`
25
+ );
26
+ } else {
27
+ console.error(`[star-cli] 启动失败: ${result.error.message}`);
28
+ }
29
+ process.exit(1);
30
+ }
31
+
32
+ process.exit(result.status === null ? 1 : result.status);
package/package.json ADDED
@@ -0,0 +1,22 @@
1
+ {
2
+ "name": "@lxzc-tech/star-cli",
3
+ "version": "0.1.0",
4
+ "description": "面向 AI Agent 的数据仓库指标披露 CLI(star-cli)",
5
+ "license": "UNLICENSED",
6
+ "os": ["darwin", "linux", "win32"],
7
+ "cpu": ["x64", "arm64"],
8
+ "bin": {
9
+ "star-cli": "bin/star-cli.js"
10
+ },
11
+ "files": [
12
+ "bin/star-cli.js",
13
+ "scripts/install.js",
14
+ "README.md"
15
+ ],
16
+ "scripts": {
17
+ "postinstall": "node scripts/install.js"
18
+ },
19
+ "publishConfig": {
20
+ "access": "public"
21
+ }
22
+ }
@@ -0,0 +1,143 @@
1
+ #!/usr/bin/env node
2
+ // postinstall:下载对应平台的 star-cli 二进制并放到 npm bin 机制能找到的位置。
3
+ //
4
+ // 版本对应规则:本 npm 包的版本号(package.json 的 version)必须与 GitHub
5
+ // Release 的 tag 一一对应(npm 包 1.2.3 对应 tag v1.2.3)。每次发布新版
6
+ // star-cli,必须同步发布同版本号的 npm 包,否则 npm install 会去下载一个
7
+ // 不存在的 tag 而失败。
8
+ //
9
+ // 不引入任何 npm 依赖(避免供应链风险):只用 Node 内置模块下载/校验,
10
+ // 用系统自带的 tar 命令解压(macOS/Linux 原生支持 tar.gz;Windows 10+
11
+ // 自带的 tar.exe 同时支持 tar.gz 和 zip)。
12
+ 'use strict';
13
+
14
+ const fs = require('fs');
15
+ const path = require('path');
16
+ const os = require('os');
17
+ const https = require('https');
18
+ const crypto = require('crypto');
19
+ const { execFileSync } = require('child_process');
20
+
21
+ const GITHUB_REPO = 'lxzc-tech/star-cli-releases';
22
+ const PKG = require('../package.json');
23
+ const VERSION = PKG.version;
24
+ const TAG = `v${VERSION}`;
25
+
26
+ const PLATFORM_MAP = { darwin: 'darwin', linux: 'linux', win32: 'windows' };
27
+ const ARCH_MAP = { x64: 'amd64', arm64: 'arm64' };
28
+
29
+ function fail(msg) {
30
+ console.error(`[star-cli] 安装失败: ${msg}`);
31
+ process.exit(1);
32
+ }
33
+
34
+ function resolveTarget() {
35
+ const goos = PLATFORM_MAP[os.platform()];
36
+ const goarch = ARCH_MAP[os.arch()];
37
+ if (!goos || !goarch) {
38
+ fail(
39
+ `不支持的平台 ${os.platform()}/${os.arch()}。` +
40
+ `star-cli 目前提供 darwin/linux/windows × amd64/arm64 的预编译产物。` +
41
+ `如需其他平台,请从源码构建: https://github.com/${GITHUB_REPO}`
42
+ );
43
+ }
44
+ const ext = goos === 'windows' ? 'zip' : 'tar.gz';
45
+ const archiveName = `star-cli_${goos}_${goarch}.${ext}`;
46
+ return { goos, goarch, ext, archiveName };
47
+ }
48
+
49
+ function download(url, destPath) {
50
+ return new Promise((resolve, reject) => {
51
+ const file = fs.createWriteStream(destPath);
52
+ const req = https.get(url, { headers: { 'User-Agent': 'star-cli-npm-installer' } }, (res) => {
53
+ // GitHub Releases 下载地址会 302 到 objects.githubusercontent.com
54
+ if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
55
+ file.close();
56
+ fs.unlinkSync(destPath);
57
+ return download(res.headers.location, destPath).then(resolve, reject);
58
+ }
59
+ if (res.statusCode !== 200) {
60
+ file.close();
61
+ fs.unlinkSync(destPath);
62
+ return reject(new Error(`下载失败,HTTP ${res.statusCode}: ${url}`));
63
+ }
64
+ res.pipe(file);
65
+ file.on('finish', () => file.close(resolve));
66
+ });
67
+ req.on('error', (err) => {
68
+ file.close();
69
+ if (fs.existsSync(destPath)) fs.unlinkSync(destPath);
70
+ reject(err);
71
+ });
72
+ });
73
+ }
74
+
75
+ function sha256(filePath) {
76
+ const hash = crypto.createHash('sha256');
77
+ hash.update(fs.readFileSync(filePath));
78
+ return hash.digest('hex');
79
+ }
80
+
81
+ async function verifyChecksum(archivePath, archiveName, tmpDir) {
82
+ const checksumsPath = path.join(tmpDir, 'checksums.txt');
83
+ const checksumsUrl = `https://github.com/${GITHUB_REPO}/releases/download/${TAG}/checksums.txt`;
84
+ await download(checksumsUrl, checksumsPath);
85
+
86
+ const checksumsContent = fs.readFileSync(checksumsPath, 'utf8');
87
+ const line = checksumsContent.split('\n').find((l) => l.trim().endsWith(archiveName));
88
+ if (!line) {
89
+ fail(`checksums.txt 中找不到 ${archiveName} 的条目,拒绝安装未经校验的产物`);
90
+ }
91
+ const expected = line.trim().split(/\s+/)[0];
92
+ const actual = sha256(archivePath);
93
+ if (expected.toLowerCase() !== actual.toLowerCase()) {
94
+ fail(
95
+ `checksum 不匹配(可能下载损坏或被篡改)\n` +
96
+ ` 期望: ${expected}\n` +
97
+ ` 实际: ${actual}`
98
+ );
99
+ }
100
+ }
101
+
102
+ function extract(archivePath, ext, destDir) {
103
+ if (ext === 'zip') {
104
+ // Windows 10+ 自带的 tar.exe 也能解 zip;这里显式走 tar -xf 统一路径。
105
+ execFileSync('tar', ['-xf', archivePath, '-C', destDir], { stdio: 'inherit' });
106
+ } else {
107
+ execFileSync('tar', ['-xzf', archivePath, '-C', destDir], { stdio: 'inherit' });
108
+ }
109
+ }
110
+
111
+ async function main() {
112
+ const { goos, ext, archiveName } = resolveTarget();
113
+ const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'star-cli-install-'));
114
+ const archivePath = path.join(tmpDir, archiveName);
115
+ const downloadUrl = `https://github.com/${GITHUB_REPO}/releases/download/${TAG}/${archiveName}`;
116
+
117
+ console.log(`[star-cli] 下载 ${TAG} (${archiveName}) ...`);
118
+ try {
119
+ await download(downloadUrl, archivePath);
120
+ await verifyChecksum(archivePath, archiveName, tmpDir);
121
+
122
+ console.log('[star-cli] 校验通过,解压中 ...');
123
+ extract(archivePath, ext, tmpDir);
124
+
125
+ const binName = goos === 'windows' ? 'star-cli.exe' : 'star-cli';
126
+ const extractedBin = path.join(tmpDir, binName);
127
+ if (!fs.existsSync(extractedBin)) {
128
+ fail(`解压后未找到预期的二进制文件: ${extractedBin}`);
129
+ }
130
+
131
+ const binDir = path.join(__dirname, '..', 'bin');
132
+ fs.mkdirSync(binDir, { recursive: true });
133
+ const targetBin = path.join(binDir, binName);
134
+ fs.copyFileSync(extractedBin, targetBin);
135
+ fs.chmodSync(targetBin, 0o755);
136
+
137
+ console.log(`[star-cli] 安装完成: ${targetBin}`);
138
+ } finally {
139
+ fs.rmSync(tmpDir, { recursive: true, force: true });
140
+ }
141
+ }
142
+
143
+ main().catch((err) => fail(err.message || String(err)));