airail 0.1.7 → 0.1.8
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/cli/add.js +65 -63
- package/dist/cli/config.js +7 -47
- package/dist/utils.js +32 -3
- package/package.json +1 -1
package/dist/cli/add.js
CHANGED
|
@@ -38,27 +38,24 @@ const fs = __importStar(require("fs"));
|
|
|
38
38
|
const path = __importStar(require("path"));
|
|
39
39
|
const os = __importStar(require("os"));
|
|
40
40
|
const child_process_1 = require("child_process");
|
|
41
|
+
const prompts_1 = require("@inquirer/prompts");
|
|
41
42
|
const utils_1 = require("../utils");
|
|
42
43
|
const colors_1 = require("./colors");
|
|
43
44
|
async function cmdAdd(arg) {
|
|
44
45
|
const claudeDir = path.join(process.cwd(), '.claude');
|
|
45
46
|
const config = (0, utils_1.requireAirailJson)(claudeDir);
|
|
46
|
-
if (!arg) {
|
|
47
|
-
console.error((0, colors_1.err)('用法: airail add <包名[@版本]|路径>'));
|
|
48
|
-
throw new Error('');
|
|
49
|
-
}
|
|
50
|
-
if (config.pack) {
|
|
51
|
-
console.log((0, colors_1.warn)(`当前已安装 pack: ${config.pack}`));
|
|
52
|
-
console.log((0, colors_1.warn)('一个项目只能安装一个 pack,新 pack 将覆盖现有内容。'));
|
|
53
|
-
}
|
|
54
47
|
// 本地路径(用于开发测试)
|
|
55
|
-
if (arg.startsWith('./') || arg.startsWith('../') || path.isAbsolute(arg)) {
|
|
48
|
+
if (arg && (arg.startsWith('./') || arg.startsWith('../') || path.isAbsolute(arg))) {
|
|
49
|
+
if (config.pack) {
|
|
50
|
+
console.log((0, colors_1.warn)(`当前已安装 pack: ${config.pack}`));
|
|
51
|
+
console.log((0, colors_1.warn)('一个项目只能安装一个 pack,新 pack 将覆盖现有内容。'));
|
|
52
|
+
}
|
|
56
53
|
const localPath = path.resolve(arg);
|
|
57
54
|
if (!fs.existsSync(localPath)) {
|
|
58
55
|
console.error((0, colors_1.err)(`路径不存在: ${localPath}`));
|
|
59
56
|
throw new Error('');
|
|
60
57
|
}
|
|
61
|
-
const packName =
|
|
58
|
+
const packName = path.basename(localPath);
|
|
62
59
|
(0, utils_1.installPackFromDir)(localPath, packName, claudeDir);
|
|
63
60
|
config.pack = packName;
|
|
64
61
|
(0, utils_1.writeAirailJson)(claudeDir, config);
|
|
@@ -71,22 +68,58 @@ async function cmdAdd(arg) {
|
|
|
71
68
|
console.error((0, colors_1.err)('未配置配置仓库,请先执行: airail config setup'));
|
|
72
69
|
throw new Error('');
|
|
73
70
|
}
|
|
74
|
-
|
|
71
|
+
// 同步本地配置仓库(静默)
|
|
72
|
+
try {
|
|
73
|
+
(0, utils_1.ensureConfigRepo)(rc.configServer, rc.configToken);
|
|
74
|
+
}
|
|
75
|
+
catch (e) {
|
|
76
|
+
console.error((0, colors_1.err)(e.message));
|
|
77
|
+
throw new Error('');
|
|
78
|
+
}
|
|
79
|
+
// 读取本地 packs/index.json
|
|
80
|
+
const indexPath = path.join(utils_1.CONFIG_REPO_DIR, 'packs', 'index.json');
|
|
81
|
+
if (!fs.existsSync(indexPath)) {
|
|
82
|
+
console.error((0, colors_1.err)('配置仓库中未找到 packs/index.json'));
|
|
83
|
+
throw new Error('');
|
|
84
|
+
}
|
|
85
|
+
let packs;
|
|
86
|
+
try {
|
|
87
|
+
packs = JSON.parse(fs.readFileSync(indexPath, 'utf-8'));
|
|
88
|
+
}
|
|
89
|
+
catch (e) {
|
|
90
|
+
console.error((0, colors_1.err)(`读取规范包列表失败: ${e.message}`));
|
|
91
|
+
throw new Error('');
|
|
92
|
+
}
|
|
93
|
+
if (packs.length === 0) {
|
|
94
|
+
console.log((0, colors_1.warn)('暂无可用规范包。'));
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
// 无包名时交互选择
|
|
98
|
+
let packNameWithVersion = arg;
|
|
99
|
+
if (!packNameWithVersion) {
|
|
100
|
+
packNameWithVersion = await (0, prompts_1.select)({
|
|
101
|
+
message: '选择要安装的规范包',
|
|
102
|
+
choices: packs.map(p => ({
|
|
103
|
+
name: `${p.name}${p.version ? ` (v${p.version})` : ''} ${p.description}`,
|
|
104
|
+
value: p.name,
|
|
105
|
+
})),
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
if (config.pack) {
|
|
109
|
+
console.log((0, colors_1.warn)(`当前已安装 pack: ${config.pack}`));
|
|
110
|
+
console.log((0, colors_1.warn)('一个项目只能安装一个 pack,新 pack 将覆盖现有内容。'));
|
|
111
|
+
}
|
|
112
|
+
await installPackFromConfigCenter(packNameWithVersion, claudeDir, config, packs);
|
|
75
113
|
(0, utils_1.writeAirailJson)(claudeDir, config);
|
|
76
114
|
}
|
|
77
|
-
function
|
|
78
|
-
return path.basename(dir);
|
|
79
|
-
}
|
|
80
|
-
async function installGitPack(url, claudeDir, config) {
|
|
115
|
+
async function installGitPack(url, packName, claudeDir, config) {
|
|
81
116
|
const [repoUrl, ref] = url.split('#');
|
|
82
117
|
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'airail-git-'));
|
|
83
118
|
try {
|
|
84
|
-
console.log((0, colors_1.info)(`正在克隆 ${repoUrl}${ref ? ` (${ref})` : ''}...`));
|
|
85
119
|
const cloneCmd = ref
|
|
86
|
-
? `git clone --depth 1 --branch ${ref} ${repoUrl} ${tmpDir}`
|
|
87
|
-
: `git clone --depth 1 ${repoUrl} ${tmpDir}`;
|
|
120
|
+
? `git clone --depth 1 --branch ${ref} "${repoUrl}" "${tmpDir}"`
|
|
121
|
+
: `git clone --depth 1 "${repoUrl}" "${tmpDir}"`;
|
|
88
122
|
(0, child_process_1.execSync)(cloneCmd, { stdio: 'pipe' });
|
|
89
|
-
const packName = readPackName(tmpDir);
|
|
90
123
|
(0, utils_1.installPackFromDir)(tmpDir, packName, claudeDir);
|
|
91
124
|
config.pack = packName;
|
|
92
125
|
console.log((0, colors_1.ok)(`已安装: ${packName} (来自 ${repoUrl})`));
|
|
@@ -95,19 +128,8 @@ async function installGitPack(url, claudeDir, config) {
|
|
|
95
128
|
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
96
129
|
}
|
|
97
130
|
}
|
|
98
|
-
async function installPackFromConfigCenter(packNameWithVersion, claudeDir, config,
|
|
99
|
-
// 解析包名和版本号
|
|
131
|
+
async function installPackFromConfigCenter(packNameWithVersion, claudeDir, config, packs) {
|
|
100
132
|
const [packName, userVersion] = packNameWithVersion.split('@');
|
|
101
|
-
const indexUrl = `${rc.configServer}/packs/index.json`;
|
|
102
|
-
console.log((0, colors_1.info)(`正在从配置仓库获取规范包列表...`));
|
|
103
|
-
let packs;
|
|
104
|
-
try {
|
|
105
|
-
packs = JSON.parse(await (0, utils_1.fetchText)(indexUrl, rc.configToken));
|
|
106
|
-
}
|
|
107
|
-
catch (e) {
|
|
108
|
-
console.error((0, colors_1.err)(`获取规范包列表失败: ${e.message}`));
|
|
109
|
-
throw new Error('');
|
|
110
|
-
}
|
|
111
133
|
const pack = packs.find(p => p.name === packName);
|
|
112
134
|
if (!pack) {
|
|
113
135
|
console.error((0, colors_1.err)(`规范包 "${packName}" 不存在。`));
|
|
@@ -116,44 +138,24 @@ async function installPackFromConfigCenter(packNameWithVersion, claudeDir, confi
|
|
|
116
138
|
}
|
|
117
139
|
// 本地规范包(gitUrl 为空)
|
|
118
140
|
if (!pack.gitUrl) {
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
141
|
+
const packDir = path.join(utils_1.CONFIG_REPO_DIR, 'packs', packName);
|
|
142
|
+
if (!fs.existsSync(packDir)) {
|
|
143
|
+
console.error((0, colors_1.err)(`配置仓库中不存在本地规范包: packs/${packName}/`));
|
|
144
|
+
throw new Error('');
|
|
145
|
+
}
|
|
146
|
+
(0, utils_1.installPackFromDir)(packDir, packName, claudeDir);
|
|
147
|
+
config.pack = packName;
|
|
148
|
+
console.log((0, colors_1.ok)(`已安装: ${pack.name}${pack.version ? ` v${pack.version}` : ''} (来自配置仓库)`));
|
|
122
149
|
return;
|
|
123
150
|
}
|
|
124
|
-
//
|
|
151
|
+
// 第三方规范包(有 gitUrl)
|
|
125
152
|
const gitRef = userVersion || pack.gitRef;
|
|
126
153
|
const gitUrl = gitRef ? `${pack.gitUrl}#${gitRef}` : pack.gitUrl;
|
|
127
154
|
const versionInfo = userVersion
|
|
128
155
|
? `v${userVersion} (用户指定)`
|
|
129
156
|
: pack.version
|
|
130
|
-
? `v${pack.version}
|
|
157
|
+
? `v${pack.version}`
|
|
131
158
|
: '(默认分支)';
|
|
132
|
-
console.log((0, colors_1.
|
|
133
|
-
|
|
134
|
-
console.log((0, colors_1.info)(`正在从 Git 仓库安装...`));
|
|
135
|
-
await installGitPack(gitUrl, claudeDir, config);
|
|
136
|
-
}
|
|
137
|
-
async function installLocalPackFromConfigRepo(packName, claudeDir, config, rc) {
|
|
138
|
-
const configRepoDir = path.join(os.homedir(), '.airail', 'config-repo');
|
|
139
|
-
// 确保配置仓库已克隆
|
|
140
|
-
if (!fs.existsSync(configRepoDir)) {
|
|
141
|
-
console.log((0, colors_1.info)('正在克隆配置仓库...'));
|
|
142
|
-
const cloneCmd = rc.configToken
|
|
143
|
-
? `git clone https://oauth2:${rc.configToken}@${rc.configServer.replace(/^https?:\/\//, '')} ${configRepoDir}`
|
|
144
|
-
: `git clone ${rc.configServer} ${configRepoDir}`;
|
|
145
|
-
(0, child_process_1.execSync)(cloneCmd, { stdio: 'pipe' });
|
|
146
|
-
}
|
|
147
|
-
else {
|
|
148
|
-
console.log((0, colors_1.info)('正在更新配置仓库...'));
|
|
149
|
-
(0, child_process_1.execSync)('git pull', { cwd: configRepoDir, stdio: 'pipe' });
|
|
150
|
-
}
|
|
151
|
-
const packDir = path.join(configRepoDir, 'packs', packName);
|
|
152
|
-
if (!fs.existsSync(packDir)) {
|
|
153
|
-
console.error((0, colors_1.err)(`配置仓库中不存在本地规范包: packs/${packName}/`));
|
|
154
|
-
throw new Error('');
|
|
155
|
-
}
|
|
156
|
-
(0, utils_1.installPackFromDir)(packDir, packName, claudeDir);
|
|
157
|
-
config.pack = packName;
|
|
158
|
-
console.log((0, colors_1.ok)(`已安装本地规范包: ${packName} (来自配置仓库)`));
|
|
159
|
+
console.log((0, colors_1.info)(`正在安装: ${pack.name} ${versionInfo}`));
|
|
160
|
+
await installGitPack(gitUrl, packName, claudeDir, config);
|
|
159
161
|
}
|
package/dist/cli/config.js
CHANGED
|
@@ -37,49 +37,9 @@ exports.cmdConfig = cmdConfig;
|
|
|
37
37
|
const fs = __importStar(require("fs"));
|
|
38
38
|
const path = __importStar(require("path"));
|
|
39
39
|
const os = __importStar(require("os"));
|
|
40
|
-
const child_process_1 = require("child_process");
|
|
41
40
|
const prompts_1 = require("@inquirer/prompts");
|
|
42
41
|
const utils_1 = require("../utils");
|
|
43
42
|
const colors_1 = require("./colors");
|
|
44
|
-
// ─── 本地配置仓库管理 ──────────────────────────────────────────────────────────
|
|
45
|
-
const CONFIG_REPO_DIR = path.join(os.homedir(), '.airail', 'config-repo');
|
|
46
|
-
/** 确保本地配置仓库存在且是最新的 */
|
|
47
|
-
function ensureConfigRepo(gitUrl, token) {
|
|
48
|
-
// 构建带认证的 Git URL(如果有 token)
|
|
49
|
-
let authUrl = gitUrl;
|
|
50
|
-
if (token && gitUrl.startsWith('https://')) {
|
|
51
|
-
authUrl = gitUrl.replace('https://', `https://oauth2:${token}@`);
|
|
52
|
-
}
|
|
53
|
-
if (!fs.existsSync(CONFIG_REPO_DIR)) {
|
|
54
|
-
// 首次克隆
|
|
55
|
-
console.log((0, colors_1.info)('正在克隆配置仓库...'));
|
|
56
|
-
try {
|
|
57
|
-
(0, child_process_1.execSync)(`git clone "${authUrl}" "${CONFIG_REPO_DIR}"`, {
|
|
58
|
-
stdio: 'inherit',
|
|
59
|
-
encoding: 'utf-8'
|
|
60
|
-
});
|
|
61
|
-
console.log((0, colors_1.ok)('配置仓库克隆成功'));
|
|
62
|
-
}
|
|
63
|
-
catch (e) {
|
|
64
|
-
throw new Error(`克隆失败: ${e.message}`);
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
else {
|
|
68
|
-
// 更新现有仓库
|
|
69
|
-
console.log((0, colors_1.info)('正在更新配置仓库...'));
|
|
70
|
-
try {
|
|
71
|
-
(0, child_process_1.execSync)('git pull', {
|
|
72
|
-
cwd: CONFIG_REPO_DIR,
|
|
73
|
-
stdio: 'inherit',
|
|
74
|
-
encoding: 'utf-8'
|
|
75
|
-
});
|
|
76
|
-
console.log((0, colors_1.ok)('配置仓库已更新'));
|
|
77
|
-
}
|
|
78
|
-
catch (e) {
|
|
79
|
-
console.log((0, colors_1.warn)(`更新失败,使用本地缓存: ${e.message}`));
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
43
|
// ─── 子命令 ───────────────────────────────────────────────────────────────────
|
|
84
44
|
async function setup() {
|
|
85
45
|
const rc = (0, utils_1.readRc)();
|
|
@@ -93,9 +53,9 @@ async function setup() {
|
|
|
93
53
|
default: rc.configToken,
|
|
94
54
|
});
|
|
95
55
|
// 清理旧的本地仓库(如果 URL 变了)
|
|
96
|
-
if (rc.configServer && rc.configServer !== server && fs.existsSync(CONFIG_REPO_DIR)) {
|
|
56
|
+
if (rc.configServer && rc.configServer !== server && fs.existsSync(utils_1.CONFIG_REPO_DIR)) {
|
|
97
57
|
console.log((0, colors_1.info)('检测到仓库地址变更,清理旧仓库...'));
|
|
98
|
-
fs.rmSync(CONFIG_REPO_DIR, { recursive: true, force: true });
|
|
58
|
+
fs.rmSync(utils_1.CONFIG_REPO_DIR, { recursive: true, force: true });
|
|
99
59
|
}
|
|
100
60
|
(0, utils_1.writeRc)({ configServer: server.replace(/\/$/, ''), configToken: token || undefined });
|
|
101
61
|
console.log((0, colors_1.ok)('配置已保存到 ~/.airailrc'));
|
|
@@ -107,13 +67,13 @@ async function listConfigs() {
|
|
|
107
67
|
throw new Error('');
|
|
108
68
|
}
|
|
109
69
|
try {
|
|
110
|
-
ensureConfigRepo(rc.configServer, rc.configToken);
|
|
70
|
+
(0, utils_1.ensureConfigRepo)(rc.configServer, rc.configToken);
|
|
111
71
|
}
|
|
112
72
|
catch (e) {
|
|
113
73
|
console.error((0, colors_1.err)(e.message));
|
|
114
74
|
throw new Error('');
|
|
115
75
|
}
|
|
116
|
-
const indexPath = path.join(CONFIG_REPO_DIR, 'settings', 'index.json');
|
|
76
|
+
const indexPath = path.join(utils_1.CONFIG_REPO_DIR, 'settings', 'index.json');
|
|
117
77
|
if (!fs.existsSync(indexPath)) {
|
|
118
78
|
console.error((0, colors_1.err)('配置仓库中未找到 settings/index.json'));
|
|
119
79
|
throw new Error('');
|
|
@@ -142,14 +102,14 @@ async function useConfig(name) {
|
|
|
142
102
|
throw new Error('');
|
|
143
103
|
}
|
|
144
104
|
try {
|
|
145
|
-
ensureConfigRepo(rc.configServer, rc.configToken);
|
|
105
|
+
(0, utils_1.ensureConfigRepo)(rc.configServer, rc.configToken);
|
|
146
106
|
}
|
|
147
107
|
catch (e) {
|
|
148
108
|
console.error((0, colors_1.err)(e.message));
|
|
149
109
|
throw new Error('');
|
|
150
110
|
}
|
|
151
111
|
// 获取配置清单
|
|
152
|
-
const indexPath = path.join(CONFIG_REPO_DIR, 'settings', 'index.json');
|
|
112
|
+
const indexPath = path.join(utils_1.CONFIG_REPO_DIR, 'settings', 'index.json');
|
|
153
113
|
if (!fs.existsSync(indexPath)) {
|
|
154
114
|
console.error((0, colors_1.err)('配置仓库中未找到 settings/index.json'));
|
|
155
115
|
throw new Error('');
|
|
@@ -185,7 +145,7 @@ async function useConfig(name) {
|
|
|
185
145
|
});
|
|
186
146
|
}
|
|
187
147
|
// 读取配置文件
|
|
188
|
-
const settingsPath = path.join(CONFIG_REPO_DIR, 'settings', `${chosen}.json`);
|
|
148
|
+
const settingsPath = path.join(utils_1.CONFIG_REPO_DIR, 'settings', `${chosen}.json`);
|
|
189
149
|
if (!fs.existsSync(settingsPath)) {
|
|
190
150
|
console.error((0, colors_1.err)(`配置文件不存在: settings/${chosen}.json`));
|
|
191
151
|
throw new Error('');
|
package/dist/utils.js
CHANGED
|
@@ -33,8 +33,9 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.PKG_ROOT = void 0;
|
|
36
|
+
exports.CONFIG_REPO_DIR = exports.PKG_ROOT = void 0;
|
|
37
37
|
exports.fetchText = fetchText;
|
|
38
|
+
exports.ensureConfigRepo = ensureConfigRepo;
|
|
38
39
|
exports.readAirailJson = readAirailJson;
|
|
39
40
|
exports.writeAirailJson = writeAirailJson;
|
|
40
41
|
exports.requireAirailJson = requireAirailJson;
|
|
@@ -55,6 +56,7 @@ const path = __importStar(require("path"));
|
|
|
55
56
|
const os = __importStar(require("os"));
|
|
56
57
|
const https = __importStar(require("https"));
|
|
57
58
|
const http = __importStar(require("http"));
|
|
59
|
+
const child_process_1 = require("child_process");
|
|
58
60
|
exports.PKG_ROOT = path.join(__dirname, '..');
|
|
59
61
|
// ─── HTTP 工具 ────────────────────────────────────────────────────────────────
|
|
60
62
|
function fetchText(url, token) {
|
|
@@ -83,6 +85,34 @@ function fetchText(url, token) {
|
|
|
83
85
|
}).on('error', reject);
|
|
84
86
|
});
|
|
85
87
|
}
|
|
88
|
+
// ─── 配置仓库工具 ──────────────────────────────────────────────────────────────
|
|
89
|
+
exports.CONFIG_REPO_DIR = path.join(os.homedir(), '.airail', 'config-repo');
|
|
90
|
+
/**
|
|
91
|
+
* 确保本地配置仓库存在且是最新的(静默模式,不输出 git 日志)
|
|
92
|
+
* 克隆失败时抛出错误;pull 失败时静默使用本地缓存
|
|
93
|
+
*/
|
|
94
|
+
function ensureConfigRepo(gitUrl, token) {
|
|
95
|
+
let authUrl = gitUrl;
|
|
96
|
+
if (token && gitUrl.startsWith('https://')) {
|
|
97
|
+
authUrl = gitUrl.replace('https://', `https://oauth2:${token}@`);
|
|
98
|
+
}
|
|
99
|
+
if (!fs.existsSync(exports.CONFIG_REPO_DIR)) {
|
|
100
|
+
try {
|
|
101
|
+
(0, child_process_1.execSync)(`git clone "${authUrl}" "${exports.CONFIG_REPO_DIR}"`, { stdio: 'pipe' });
|
|
102
|
+
}
|
|
103
|
+
catch (e) {
|
|
104
|
+
throw new Error(`克隆配置仓库失败: ${e.stderr?.toString().trim() || e.message}`);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
try {
|
|
109
|
+
(0, child_process_1.execSync)('git pull', { cwd: exports.CONFIG_REPO_DIR, stdio: 'pipe' });
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
// 更新失败时静默使用本地缓存
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
86
116
|
function readAirailJson(claudeDir) {
|
|
87
117
|
const p = path.join(claudeDir, 'airail.json');
|
|
88
118
|
return JSON.parse(fs.readFileSync(p, 'utf-8'));
|
|
@@ -92,8 +122,7 @@ function writeAirailJson(claudeDir, data) {
|
|
|
92
122
|
}
|
|
93
123
|
function requireAirailJson(claudeDir) {
|
|
94
124
|
if (!fs.existsSync(path.join(claudeDir, 'airail.json'))) {
|
|
95
|
-
|
|
96
|
-
process.exit(1);
|
|
125
|
+
throw new Error('未初始化,请先执行 "airail init"。');
|
|
97
126
|
}
|
|
98
127
|
return readAirailJson(claudeDir);
|
|
99
128
|
}
|