@round2ai/r2-cli 1.0.10 → 1.0.11
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/dist/package.json +1 -0
- package/dist/r2-cli.js +45 -1757
- package/package.json +2 -1
- package/scripts/build.js +176 -0
- package/scripts/dev.js +22 -0
- package/scripts/install-skills.js +54 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@round2ai/r2-cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.11",
|
|
4
4
|
"description": "R2-CLI,向 AI 开放二手潮奢交易全链路能力",
|
|
5
5
|
"main": "dist/r2-cli.js",
|
|
6
6
|
"type": "module",
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
],
|
|
26
26
|
"files": [
|
|
27
27
|
"dist",
|
|
28
|
+
"scripts",
|
|
28
29
|
"skills",
|
|
29
30
|
"package.json",
|
|
30
31
|
"README.md"
|
package/scripts/build.js
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* 构建脚本 - 使用 esbuild 打包
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import esbuild from "esbuild";
|
|
7
|
+
import fs from "node:fs/promises";
|
|
8
|
+
import path from "path";
|
|
9
|
+
import { fileURLToPath } from "url";
|
|
10
|
+
import * as dotenv from "dotenv";
|
|
11
|
+
|
|
12
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
13
|
+
const __dirname = path.dirname(__filename);
|
|
14
|
+
const rootDir = path.dirname(__dirname);
|
|
15
|
+
|
|
16
|
+
const nodeEnv = process.env.NODE_ENV || "development";
|
|
17
|
+
const isProd = nodeEnv === "production";
|
|
18
|
+
const envFile = isProd ? ".env.production" : ".env";
|
|
19
|
+
dotenv.config({ path: path.join(rootDir, envFile) });
|
|
20
|
+
const serverBaseUrl = process.env.SERVER_BASEURL || "https://api.qiuxietang.com";
|
|
21
|
+
|
|
22
|
+
// 入口点配置
|
|
23
|
+
const entryPoints = {
|
|
24
|
+
"r2-cli": "src/entrypoints/r2-cli.tsx",
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
// esbuild 配置
|
|
28
|
+
const esbuildConfig = {
|
|
29
|
+
bundle: true,
|
|
30
|
+
platform: "node",
|
|
31
|
+
format: "esm",
|
|
32
|
+
sourcemap: false,
|
|
33
|
+
minify: isProd,
|
|
34
|
+
external: [
|
|
35
|
+
"commander",
|
|
36
|
+
"chalk",
|
|
37
|
+
"figlet",
|
|
38
|
+
"@inquirer/prompts",
|
|
39
|
+
"@inquirer/core",
|
|
40
|
+
"@inquirer/input",
|
|
41
|
+
"@inquirer/select",
|
|
42
|
+
"@inquirer/confirm",
|
|
43
|
+
"@inquirer/checkbox",
|
|
44
|
+
"mute-stream",
|
|
45
|
+
"qrcode",
|
|
46
|
+
"ora",
|
|
47
|
+
"react",
|
|
48
|
+
"ink",
|
|
49
|
+
"react-dom",
|
|
50
|
+
"react-devtools-core",
|
|
51
|
+
"uuid",
|
|
52
|
+
],
|
|
53
|
+
banner: {
|
|
54
|
+
js: '"use strict";',
|
|
55
|
+
},
|
|
56
|
+
define: {
|
|
57
|
+
"process.env.NODE_ENV": JSON.stringify(nodeEnv),
|
|
58
|
+
"process.env.SERVER_BASEURL": JSON.stringify(serverBaseUrl),
|
|
59
|
+
},
|
|
60
|
+
treeShaking: true,
|
|
61
|
+
splitting: false,
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* 清理输出目录
|
|
66
|
+
*/
|
|
67
|
+
async function cleanDist() {
|
|
68
|
+
const distDir = path.join(rootDir, "dist");
|
|
69
|
+
try {
|
|
70
|
+
await fs.rm(distDir, { recursive: true, force: true });
|
|
71
|
+
} catch (e) {
|
|
72
|
+
if (e.code === "EBUSY" || e.code === "EPERM") {
|
|
73
|
+
const files = await fs.readdir(distDir);
|
|
74
|
+
for (const file of files) {
|
|
75
|
+
await fs.rm(path.join(distDir, file), { recursive: true, force: true });
|
|
76
|
+
}
|
|
77
|
+
} else {
|
|
78
|
+
throw e;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
await fs.mkdir(distDir, { recursive: true });
|
|
82
|
+
console.log("🧹 清理输出目录完成");
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* 构建单个入口点
|
|
87
|
+
*/
|
|
88
|
+
async function buildEntryPoint(name, entry) {
|
|
89
|
+
const outputDir = path.join(rootDir, "dist");
|
|
90
|
+
console.log(`🔨 构建 ${name} -> ${path.relative(rootDir, outputDir)}`);
|
|
91
|
+
|
|
92
|
+
const config = { ...esbuildConfig };
|
|
93
|
+
if (name === "r2-cli") {
|
|
94
|
+
config.banner = {
|
|
95
|
+
js: "#!/usr/bin/env node\n",
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
try {
|
|
100
|
+
await esbuild.build({
|
|
101
|
+
...config,
|
|
102
|
+
entryPoints: [path.join(rootDir, entry)],
|
|
103
|
+
outdir: outputDir,
|
|
104
|
+
// 保持原始文件名,不加 outdir 的 hash 前缀
|
|
105
|
+
// outdir 会自动生成文件名,不需要 outfile
|
|
106
|
+
});
|
|
107
|
+
console.log(`✅ ${name} 构建成功`);
|
|
108
|
+
} catch (error) {
|
|
109
|
+
console.error(`❌ ${name} 构建失败:`, error.message);
|
|
110
|
+
throw error;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* 复制必要文件
|
|
116
|
+
*/
|
|
117
|
+
async function copyFiles() {
|
|
118
|
+
const filesToCopy = ["package.json", "README.md"];
|
|
119
|
+
|
|
120
|
+
for (const file of filesToCopy) {
|
|
121
|
+
const src = path.join(rootDir, file);
|
|
122
|
+
const dest = path.join(rootDir, "dist", file);
|
|
123
|
+
|
|
124
|
+
try {
|
|
125
|
+
await fs.copyFile(src, dest);
|
|
126
|
+
console.log(`📄 已复制 ${file}`);
|
|
127
|
+
} catch {
|
|
128
|
+
// file doesn't exist, skip
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// 复制 QR 页面 HTML 文件到 dist/pages/
|
|
133
|
+
const pagesDir = path.join(rootDir, "dist", "pages");
|
|
134
|
+
await fs.mkdir(pagesDir, { recursive: true });
|
|
135
|
+
const htmlSrcDir = path.join(rootDir, "src", "qr-server", "pages");
|
|
136
|
+
const htmlFiles = await fs.readdir(htmlSrcDir);
|
|
137
|
+
for (const file of htmlFiles) {
|
|
138
|
+
if (file.endsWith(".html")) {
|
|
139
|
+
await fs.copyFile(path.join(htmlSrcDir, file), path.join(pagesDir, file));
|
|
140
|
+
console.log(`📄 已复制 pages/${file}`);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* 构建项目
|
|
147
|
+
*/
|
|
148
|
+
async function build() {
|
|
149
|
+
console.log("🚀 开始构建 R2-CLI...\n");
|
|
150
|
+
|
|
151
|
+
try {
|
|
152
|
+
// 1. 清理输出目录
|
|
153
|
+
await cleanDist();
|
|
154
|
+
|
|
155
|
+
// 2. 构建所有入口点
|
|
156
|
+
console.log("🔨 开始构建入口点...\n");
|
|
157
|
+
for (const [name, entry] of Object.entries(entryPoints)) {
|
|
158
|
+
await buildEntryPoint(name, entry);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// 3. 复制必要文件
|
|
162
|
+
await copyFiles();
|
|
163
|
+
|
|
164
|
+
console.log("\n✅ 构建完成!");
|
|
165
|
+
console.log("\n📦 输出文件:");
|
|
166
|
+
Object.keys(entryPoints).forEach((name) => {
|
|
167
|
+
console.log(` • dist/${name}.js`);
|
|
168
|
+
});
|
|
169
|
+
} catch (error) {
|
|
170
|
+
console.error("❌ 构建失败:", error);
|
|
171
|
+
process.exit(1);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// 运行构建
|
|
176
|
+
build();
|
package/scripts/dev.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* 开发模式 - 直接运行源码
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { spawn } from 'child_process';
|
|
7
|
+
import path from 'path';
|
|
8
|
+
import { fileURLToPath } from 'url';
|
|
9
|
+
|
|
10
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
11
|
+
const __dirname = path.dirname(__filename);
|
|
12
|
+
const rootDir = path.dirname(__dirname);
|
|
13
|
+
|
|
14
|
+
const child = spawn('npx', ['tsx', 'src/entrypoints/r2-cli.tsx', ...process.argv.slice(2)], {
|
|
15
|
+
cwd: rootDir,
|
|
16
|
+
stdio: 'inherit',
|
|
17
|
+
shell: true,
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
child.on('exit', (code) => {
|
|
21
|
+
process.exit(code ?? 1);
|
|
22
|
+
});
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* postinstall: 将 skills/ 复制到 ~/.agents/skills/ + 确保 ~/.claude/skills/ 符号链接存在
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import fs from "node:fs";
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
import os from "node:os";
|
|
8
|
+
import { fileURLToPath } from "node:url";
|
|
9
|
+
|
|
10
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
11
|
+
const pkgSkillsDir = path.join(__dirname, "..", "skills");
|
|
12
|
+
const agentsDir = path.join(os.homedir(), ".agents", "skills");
|
|
13
|
+
const claudeDir = path.join(os.homedir(), ".claude", "skills");
|
|
14
|
+
|
|
15
|
+
if (!fs.existsSync(pkgSkillsDir)) process.exit(0);
|
|
16
|
+
|
|
17
|
+
fs.mkdirSync(agentsDir, { recursive: true });
|
|
18
|
+
|
|
19
|
+
for (const name of fs.readdirSync(pkgSkillsDir)) {
|
|
20
|
+
const rawSrc = path.join(pkgSkillsDir, name);
|
|
21
|
+
// npm 包内是真实目录,本地开发可能是 symlink → 解析真实路径
|
|
22
|
+
let src;
|
|
23
|
+
try { src = fs.realpathSync(rawSrc); } catch { continue; }
|
|
24
|
+
if (!fs.statSync(src).isDirectory()) continue;
|
|
25
|
+
|
|
26
|
+
const dest = path.join(agentsDir, name);
|
|
27
|
+
// 清空目标目录中的旧残留文件
|
|
28
|
+
if (fs.existsSync(dest)) {
|
|
29
|
+
for (const old of fs.readdirSync(dest)) {
|
|
30
|
+
fs.unlinkSync(path.join(dest, old));
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
34
|
+
|
|
35
|
+
for (const file of fs.readdirSync(src)) {
|
|
36
|
+
fs.copyFileSync(path.join(src, file), path.join(dest, file));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// 确保 ~/.claude/skills/<name> 指向 ~/.agents/skills/<name>
|
|
40
|
+
const link = path.join(claudeDir, name);
|
|
41
|
+
if (!fs.existsSync(link)) {
|
|
42
|
+
fs.mkdirSync(claudeDir, { recursive: true });
|
|
43
|
+
const target = dest;
|
|
44
|
+
try {
|
|
45
|
+
fs.symlinkSync(target, link, "junction");
|
|
46
|
+
} catch {
|
|
47
|
+
// Windows 权限不足时 fallback:直接复制
|
|
48
|
+
fs.mkdirSync(link, { recursive: true });
|
|
49
|
+
for (const file of fs.readdirSync(dest)) {
|
|
50
|
+
fs.copyFileSync(path.join(dest, file), path.join(link, file));
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|