@lark-people/cli 0.0.0-beta.1
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/CHANGELOG.md +324 -0
- package/LICENSE +21 -0
- package/README.md +232 -0
- package/README.zh.md +232 -0
- package/bin/people-cli_darwin_amd64 +0 -0
- package/bin/people-cli_darwin_arm64 +0 -0
- package/bin/people-cli_linux_amd64 +0 -0
- package/bin/people-cli_linux_arm64 +0 -0
- package/bin/people-cli_windows_amd64.exe +0 -0
- package/bin/people-cli_windows_arm64.exe +0 -0
- package/package.json +41 -0
- package/scripts/install-wizard.js +700 -0
- package/scripts/install.js +56 -0
- package/scripts/run.js +36 -0
- package/skills/people-attendance/SKILL.md +667 -0
- package/skills/people-attendance/references/people-attendance-submission.md +426 -0
- package/skills/people-attendance/scripts/resolve_date.py +894 -0
- package/skills/people-corehr/references/people-corehr-pre-hire-search.md +0 -0
- package/skills/people-corehr-employee/SKILL.md +111 -0
- package/skills/people-corehr-employee/references/people-corehr-employee-basic-info-query.md +97 -0
- package/skills/people-corehr-employee/references/people-corehr-employee-fields-list.md +87 -0
- package/skills/people-corehr-employee/references/people-corehr-employee-filter.md +403 -0
- package/skills/people-corehr-employee/references/people-corehr-employee-search.md +73 -0
- package/skills/people-corehr-job-change/SKILL.md +212 -0
- package/skills/people-corehr-job-change/references/people-job-change-business-rules.md +113 -0
- package/skills/people-corehr-job-change/references/people-job-change-lookup-filters.md +82 -0
- package/skills/people-corehr-job-change/references/people-job-change-output-and-empty.md +207 -0
- package/skills/people-corehr-job-change/references/people-job-change-search-advanced.md +226 -0
- package/skills/people-corehr-job-change/references/people-job-change-search-fields.md +119 -0
- package/skills/people-corehr-job-change/references/people-job-change-search-option.md +72 -0
- package/skills/people-corehr-job-change/references/people-job-change-search.md +215 -0
- package/skills/people-corehr-offboarding/SKILL.md +188 -0
- package/skills/people-corehr-offboarding/references/people-corehr-offboarding-business-knowledge.md +27 -0
- package/skills/people-corehr-offboarding/references/people-corehr-offboarding-search-option.md +187 -0
- package/skills/people-corehr-offboarding/references/people-corehr-offboarding-search.md +54 -0
- package/skills/people-corehr-organization/SKILL.md +42 -0
- package/skills/people-corehr-organization/references/aggregate.md +11 -0
- package/skills/people-corehr-organization/references/detail.md +39 -0
- package/skills/people-corehr-organization/references/field-values.md +44 -0
- package/skills/people-corehr-organization/references/hierarchy.md +61 -0
- package/skills/people-corehr-organization/references/lookup.md +41 -0
- package/skills/people-corehr-organization/references/metadata.md +40 -0
- package/skills/people-corehr-organization/references/tree.md +45 -0
- package/skills/people-corehr-prehire/SKILL.md +160 -0
- package/skills/people-corehr-prehire/references/command-resolution.md +48 -0
- package/skills/people-corehr-prehire/references/filter-fields.md +151 -0
- package/skills/people-corehr-prehire/references/return-fields.md +153 -0
- package/skills/people-corehr-prehire/references/templates.md +202 -0
- package/skills/people-corehr-prehire/references/troubleshooting.md +93 -0
- package/skills/people-corehr-query-id-by-name/SKILL.md +154 -0
- package/skills/people-corehr-renewal/SKILL.md +186 -0
- package/skills/people-corehr-renewal/references/people-corehr-renewal-fields-query.md +62 -0
- package/skills/people-corehr-renewal/references/people-corehr-renewal-routing.md +54 -0
- package/skills/people-corehr-renewal/references/people-corehr-renewal-search.md +137 -0
- package/skills/people-corehr-renewal/references/people-corehr-renewal.md +39 -0
- package/skills/people-corehr-workforce-plan/SKILL.md +52 -0
- package/skills/people-corehr-workforce-plan/references/people-corehr-workforce-plan-detail-list.md +238 -0
- package/skills/people-corehr-workforce-plan/references/people-corehr-workforce-plan-detail-summary.md +221 -0
- package/skills/people-corehr-workforce-plan/references/people-corehr-workforce-plan.md +79 -0
- package/skills/people-hire/SKILL.md +255 -0
- package/skills/people-hire/references/people-hire-application.md +334 -0
- package/skills/people-hire/references/people-hire-evaluation-task-list.md +233 -0
- package/skills/people-hire/references/people-hire-evaluation.md +127 -0
- package/skills/people-hire/references/people-hire-interview-record.md +109 -0
- package/skills/people-hire/references/people-hire-interview-task-list.md +250 -0
- package/skills/people-hire/references/people-hire-interview.md +146 -0
- package/skills/people-hire/references/people-hire-job-process.md +93 -0
- package/skills/people-hire/references/people-hire-job-requirement.md +227 -0
- package/skills/people-hire/references/people-hire-job.md +277 -0
- package/skills/people-hire/references/people-hire-minutes.md +91 -0
- package/skills/people-hire/references/people-hire-talent.md +105 -0
- package/skills/people-hire/references/people-hire-test.md +197 -0
- package/skills/people-overtime/SKILL.md +992 -0
- package/skills/people-overtime/references/people-attendance-overtime-apply.md +491 -0
- package/skills/people-overtime/references/people-attendance-overtime.md +51 -0
- package/skills/people-overtime/references/people-attendance-submission.md +203 -0
- package/skills/people-overtime/scripts/resolve_date.py +894 -0
- package/skills/people-shared/SKILL.md +94 -0
|
@@ -0,0 +1,700 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const fs = require("fs");
|
|
3
|
+
const path = require("path");
|
|
4
|
+
const { execFileSync, execFile } = require("child_process");
|
|
5
|
+
|
|
6
|
+
let p;
|
|
7
|
+
|
|
8
|
+
const BIN_NAME = "people-cli";
|
|
9
|
+
const CURRENT_PACKAGE_DIR = path.resolve(__dirname, "..");
|
|
10
|
+
const DEFAULT_PKG = "@lark-people/cli";
|
|
11
|
+
const DEFAULT_SKILLS_DIR = path.join(__dirname, "..", "skills");
|
|
12
|
+
const isWindows = process.platform === "win32";
|
|
13
|
+
const EXCLUDED_BUNDLED_SKILLS = new Set(["people-skill-maker"]);
|
|
14
|
+
|
|
15
|
+
function readPackageMeta(packageDir = CURRENT_PACKAGE_DIR) {
|
|
16
|
+
try {
|
|
17
|
+
const packageJSON = path.join(packageDir, "package.json");
|
|
18
|
+
return JSON.parse(fs.readFileSync(packageJSON, "utf8"));
|
|
19
|
+
} catch (_) {
|
|
20
|
+
return {};
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const PKG = readPackageMeta().name || DEFAULT_PKG;
|
|
25
|
+
|
|
26
|
+
const messages = {
|
|
27
|
+
zh: {
|
|
28
|
+
setup: "正在设置 People CLI...",
|
|
29
|
+
step1: "正在安装 %s...",
|
|
30
|
+
step1Upgrade: "正在升级 %s (v%s → v%s)...",
|
|
31
|
+
step1Skip: "已安装 (v%s),跳过",
|
|
32
|
+
step1Done: "已全局安装",
|
|
33
|
+
step1Upgraded: "已升级到 v%s",
|
|
34
|
+
step1Fail: "全局安装失败。运行以下命令重试: npm install -g %s",
|
|
35
|
+
step2Spinner: "正在安装 Skills...",
|
|
36
|
+
step2Skip: "官方 Skills 已齐全,跳过",
|
|
37
|
+
step2Done: "Skills 已安装",
|
|
38
|
+
step2Fail: "Skills 安装失败。运行以下命令重试: npx skills add <本地skill目录> -g -y",
|
|
39
|
+
step2Empty: "未发现可安装的官方 Skills",
|
|
40
|
+
step3: "正在配置应用...",
|
|
41
|
+
step3NotFound: "未找到 people-cli,终止",
|
|
42
|
+
step3Found: "发现已配置应用 (App ID: %s),继续使用?",
|
|
43
|
+
step3Reinit: "检测到已有应用配置不可用,将重新初始化",
|
|
44
|
+
step3Skip: "跳过应用配置",
|
|
45
|
+
step3Done: "应用已配置",
|
|
46
|
+
step3Fail: "应用配置失败。运行以下命令重试: people-cli config init --new",
|
|
47
|
+
step4: "授权",
|
|
48
|
+
step4NotFound: "未找到 people-cli,跳过授权",
|
|
49
|
+
step4Confirm: "是否现在执行登录授权,让 CLI 能访问你已授权的 People 数据?",
|
|
50
|
+
step4Skip: "跳过授权。后续运行 people-cli auth login 完成授权",
|
|
51
|
+
step4Done: "授权完成",
|
|
52
|
+
step4Fail: "授权失败。运行以下命令重试: people-cli auth login",
|
|
53
|
+
done: "安装完成!现在可以开始使用 People CLI 及其官方 Skills。",
|
|
54
|
+
cancelled: "安装已取消",
|
|
55
|
+
nonTtyHint: "要完成配置,请在终端中运行:\n people-cli config init --new\n people-cli auth login",
|
|
56
|
+
},
|
|
57
|
+
en: {
|
|
58
|
+
setup: "Setting up People CLI...",
|
|
59
|
+
step1: "Installing %s globally...",
|
|
60
|
+
step1Upgrade: "Upgrading %s (v%s -> v%s)...",
|
|
61
|
+
step1Skip: "Already installed (v%s). Skipped",
|
|
62
|
+
step1Done: "Installed globally",
|
|
63
|
+
step1Upgraded: "Upgraded to v%s",
|
|
64
|
+
step1Fail: "Failed to install globally. Run manually: npm install -g %s",
|
|
65
|
+
step2Spinner: "Installing skills...",
|
|
66
|
+
step2Skip: "Official skills already present. Skipped",
|
|
67
|
+
step2Done: "Skills installed",
|
|
68
|
+
step2Fail: "Failed to install skills. Run manually: npx skills add <local-skill-dir> -g -y",
|
|
69
|
+
step2Empty: "No bundled official skills found",
|
|
70
|
+
step3: "Configuring app...",
|
|
71
|
+
step3NotFound: "people-cli not found. Aborting",
|
|
72
|
+
step3Found: "Found existing app (App ID: %s). Use this app?",
|
|
73
|
+
step3Reinit: "Detected stale app configuration. Re-initializing",
|
|
74
|
+
step3Skip: "Skipped app configuration",
|
|
75
|
+
step3Done: "App configured",
|
|
76
|
+
step3Fail: "Failed to configure app. Run manually: people-cli config init --new",
|
|
77
|
+
step4: "Authorization",
|
|
78
|
+
step4NotFound: "people-cli not found. Skipping authorization",
|
|
79
|
+
step4Confirm: "Authorize now so People CLI can access your approved People data?",
|
|
80
|
+
step4Skip: "Skipped. Run people-cli auth login to authorize later",
|
|
81
|
+
step4Done: "Authorization complete",
|
|
82
|
+
step4Fail: "Failed to authorize. Run people-cli auth login to retry",
|
|
83
|
+
done: "You are all set! People CLI and its official skills are ready.",
|
|
84
|
+
cancelled: "Installation cancelled",
|
|
85
|
+
nonTtyHint: "To complete setup, run interactively:\n people-cli config init --new\n people-cli auth login",
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
function handleCancel(value, msg) {
|
|
90
|
+
if (p.isCancel(value)) {
|
|
91
|
+
p.cancel(msg.cancelled);
|
|
92
|
+
process.exit(0);
|
|
93
|
+
}
|
|
94
|
+
return value;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function execCmd(cmd, args, opts) {
|
|
98
|
+
if (isWindows) {
|
|
99
|
+
return execFileSync("cmd.exe", ["/c", cmd, ...args], opts);
|
|
100
|
+
}
|
|
101
|
+
return execFileSync(cmd, args, opts);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function run(cmd, args, opts = {}) {
|
|
105
|
+
execCmd(cmd, args, { stdio: "inherit", ...opts });
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function runSilent(cmd, args, opts = {}) {
|
|
109
|
+
return execCmd(cmd, args, {
|
|
110
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
111
|
+
...opts,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function runSilentAsync(cmd, args, opts = {}) {
|
|
116
|
+
const actualCmd = isWindows ? "cmd.exe" : cmd;
|
|
117
|
+
const actualArgs = isWindows ? ["/c", cmd, ...args] : args;
|
|
118
|
+
return new Promise((resolve, reject) => {
|
|
119
|
+
execFile(actualCmd, actualArgs, {
|
|
120
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
121
|
+
...opts,
|
|
122
|
+
}, (err, stdout, stderr) => {
|
|
123
|
+
if (err) {
|
|
124
|
+
err.stdout = stdout;
|
|
125
|
+
err.stderr = stderr;
|
|
126
|
+
reject(err);
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
resolve(stdout);
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function hasExecutable(bin) {
|
|
135
|
+
try {
|
|
136
|
+
const cmd = isWindows ? "where" : "which";
|
|
137
|
+
const output = runSilent(cmd, [bin], { timeout: 5000 }).toString().trim();
|
|
138
|
+
return output.length > 0;
|
|
139
|
+
} catch (_) {
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function resolveSkillsLauncher(check = hasExecutable) {
|
|
145
|
+
if (check("npx")) {
|
|
146
|
+
return {
|
|
147
|
+
cmd: "npx",
|
|
148
|
+
prefix: ["-y", "skills"],
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
if (check("npm")) {
|
|
152
|
+
return {
|
|
153
|
+
cmd: "npm",
|
|
154
|
+
prefix: ["exec", "--yes", "--", "skills"],
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function buildSkillsCommandArgs(skillArgs, check = hasExecutable) {
|
|
161
|
+
const launcher = resolveSkillsLauncher(check);
|
|
162
|
+
if (!launcher) {
|
|
163
|
+
throw new Error("Neither npx nor npm is available in PATH");
|
|
164
|
+
}
|
|
165
|
+
return {
|
|
166
|
+
cmd: launcher.cmd,
|
|
167
|
+
args: launcher.prefix.concat(skillArgs),
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function runSkillsCommandAsync(skillArgs, opts = {}) {
|
|
172
|
+
const command = buildSkillsCommandArgs(skillArgs);
|
|
173
|
+
return runSilentAsync(command.cmd, command.args, opts);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function formatCommandFailure(err) {
|
|
177
|
+
for (const key of ["stderr", "stdout"]) {
|
|
178
|
+
const value = err && err[key] ? String(err[key]).trim() : "";
|
|
179
|
+
if (value) {
|
|
180
|
+
return value;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return err && err.message ? String(err.message).trim() : "";
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function buildInstallAuthLoginArgs() {
|
|
187
|
+
return ["auth", "login", "--install-scope-level", "all"];
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function fmt(template, ...values) {
|
|
191
|
+
let i = 0;
|
|
192
|
+
return template.replace(/%s/g, () => values[i++] ?? "");
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function getCurrentPackageName(packageDir = CURRENT_PACKAGE_DIR) {
|
|
196
|
+
return readPackageMeta(packageDir).name || DEFAULT_PKG;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function getCurrentPackageVersion(packageDir = CURRENT_PACKAGE_DIR) {
|
|
200
|
+
return readPackageMeta(packageDir).version || null;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function resolveGlobalPackageDir(packageName = PKG) {
|
|
204
|
+
try {
|
|
205
|
+
const prefix = runSilent("npm", ["prefix", "-g"], { timeout: 15000 }).toString().trim();
|
|
206
|
+
const parts = packageName.split("/");
|
|
207
|
+
return isWindows
|
|
208
|
+
? path.join(prefix, "node_modules", ...parts)
|
|
209
|
+
: path.join(prefix, "lib", "node_modules", ...parts);
|
|
210
|
+
} catch (_) {
|
|
211
|
+
return null;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function sameResolvedPath(a, b) {
|
|
216
|
+
if (!a || !b) {
|
|
217
|
+
return false;
|
|
218
|
+
}
|
|
219
|
+
try {
|
|
220
|
+
return fs.realpathSync(a) === fs.realpathSync(b);
|
|
221
|
+
} catch (_) {
|
|
222
|
+
return path.resolve(a) === path.resolve(b);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function parseVersion(version) {
|
|
227
|
+
if (!version) {
|
|
228
|
+
return null;
|
|
229
|
+
}
|
|
230
|
+
const match = String(version).trim().match(/^v?(\d+)\.(\d+)\.(\d+)(?:-([A-Za-z]+)\.(\d+))?$/);
|
|
231
|
+
if (!match) {
|
|
232
|
+
return null;
|
|
233
|
+
}
|
|
234
|
+
return {
|
|
235
|
+
major: Number(match[1]),
|
|
236
|
+
minor: Number(match[2]),
|
|
237
|
+
patch: Number(match[3]),
|
|
238
|
+
prereleaseType: match[4] || null,
|
|
239
|
+
prereleaseNumber: match[5] ? Number(match[5]) : null,
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function compareVersions(a, b) {
|
|
244
|
+
const left = parseVersion(a);
|
|
245
|
+
const right = parseVersion(b);
|
|
246
|
+
if (!left || !right) {
|
|
247
|
+
return 0;
|
|
248
|
+
}
|
|
249
|
+
if (left.major !== right.major) return left.major - right.major;
|
|
250
|
+
if (left.minor !== right.minor) return left.minor - right.minor;
|
|
251
|
+
if (left.patch !== right.patch) return left.patch - right.patch;
|
|
252
|
+
|
|
253
|
+
const leftStable = left.prereleaseType === null;
|
|
254
|
+
const rightStable = right.prereleaseType === null;
|
|
255
|
+
if (leftStable && rightStable) return 0;
|
|
256
|
+
if (leftStable) return 1;
|
|
257
|
+
if (rightStable) return -1;
|
|
258
|
+
if (left.prereleaseType !== right.prereleaseType) {
|
|
259
|
+
return left.prereleaseType.localeCompare(right.prereleaseType);
|
|
260
|
+
}
|
|
261
|
+
return (left.prereleaseNumber || 0) - (right.prereleaseNumber || 0);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
function planGlobalInstall({
|
|
265
|
+
currentPackageName = PKG,
|
|
266
|
+
installedVersion,
|
|
267
|
+
latestVersion,
|
|
268
|
+
currentVersion,
|
|
269
|
+
currentPackageDir = CURRENT_PACKAGE_DIR,
|
|
270
|
+
globalPackageDir = resolveGlobalPackageDir(currentPackageName),
|
|
271
|
+
} = {}) {
|
|
272
|
+
const useCurrentPackage = Boolean(
|
|
273
|
+
currentPackageDir &&
|
|
274
|
+
globalPackageDir &&
|
|
275
|
+
!sameResolvedPath(currentPackageDir, globalPackageDir),
|
|
276
|
+
);
|
|
277
|
+
|
|
278
|
+
if (useCurrentPackage) {
|
|
279
|
+
return {
|
|
280
|
+
skip: false,
|
|
281
|
+
needsUpgrade: false,
|
|
282
|
+
installTarget: currentPackageDir,
|
|
283
|
+
installVersion: currentVersion || latestVersion || installedVersion || null,
|
|
284
|
+
useCurrentPackage: true,
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
const installVersion = latestVersion || currentVersion || installedVersion || null;
|
|
289
|
+
const needsUpgrade = Boolean(
|
|
290
|
+
installedVersion &&
|
|
291
|
+
installVersion &&
|
|
292
|
+
compareVersions(installedVersion, installVersion) < 0,
|
|
293
|
+
);
|
|
294
|
+
|
|
295
|
+
return {
|
|
296
|
+
skip: Boolean(installedVersion && !needsUpgrade),
|
|
297
|
+
needsUpgrade,
|
|
298
|
+
installTarget: currentPackageName,
|
|
299
|
+
installVersion,
|
|
300
|
+
useCurrentPackage: false,
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
function parseLangArg(argv = process.argv.slice(2)) {
|
|
305
|
+
for (let i = 0; i < argv.length; i++) {
|
|
306
|
+
if (argv[i] === "--lang" && argv[i + 1]) {
|
|
307
|
+
const value = argv[i + 1].toLowerCase();
|
|
308
|
+
if (value === "zh" || value === "en") return value;
|
|
309
|
+
}
|
|
310
|
+
if (argv[i].startsWith("--lang=")) {
|
|
311
|
+
const value = argv[i].split("=")[1].toLowerCase();
|
|
312
|
+
if (value === "zh" || value === "en") return value;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
return null;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
function whichPeopleCli() {
|
|
319
|
+
try {
|
|
320
|
+
const prefix = runSilent("npm", ["prefix", "-g"], { timeout: 15000 }).toString().trim();
|
|
321
|
+
const bin = isWindows
|
|
322
|
+
? path.join(prefix, "people-cli.cmd")
|
|
323
|
+
: path.join(prefix, "bin", "people-cli");
|
|
324
|
+
if (fs.existsSync(bin)) return bin;
|
|
325
|
+
} catch (_) {
|
|
326
|
+
// fall through
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
try {
|
|
330
|
+
const cmd = isWindows ? "where" : "which";
|
|
331
|
+
return runSilent(cmd, [BIN_NAME], { timeout: 5000 })
|
|
332
|
+
.toString()
|
|
333
|
+
.split("\n")[0]
|
|
334
|
+
.trim();
|
|
335
|
+
} catch (_) {
|
|
336
|
+
return null;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
function getLatestVersion(packageName = PKG) {
|
|
341
|
+
try {
|
|
342
|
+
const out = runSilent("npm", ["view", packageName, "version"], { timeout: 15000 });
|
|
343
|
+
const version = out.toString().trim();
|
|
344
|
+
return parseVersion(version) ? version : null;
|
|
345
|
+
} catch (_) {
|
|
346
|
+
return null;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
function getGloballyInstalledVersion(packageName = PKG) {
|
|
351
|
+
try {
|
|
352
|
+
const out = runSilent("npm", ["list", "-g", packageName], { timeout: 15000 });
|
|
353
|
+
const match = out.toString().match(/@(\d+\.\d+\.\d+[^\s]*)/);
|
|
354
|
+
return match ? match[1] : "unknown";
|
|
355
|
+
} catch (_) {
|
|
356
|
+
return null;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
function getExistingAppId(binPath) {
|
|
361
|
+
try {
|
|
362
|
+
const out = runSilent(binPath, ["config", "show"], { timeout: 10000 });
|
|
363
|
+
const parsed = JSON.parse(out.toString());
|
|
364
|
+
return parsed.appId || parsed.app_id || null;
|
|
365
|
+
} catch (_) {
|
|
366
|
+
return null;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
function inspectExistingAppConfig(binPath, deps = {}) {
|
|
371
|
+
const runSilentFn = deps.runSilent || runSilent;
|
|
372
|
+
try {
|
|
373
|
+
const out = runSilentFn(binPath, ["config", "show"], { timeout: 10000 });
|
|
374
|
+
const parsed = JSON.parse(out.toString());
|
|
375
|
+
const appId = parsed.appId || parsed.app_id || null;
|
|
376
|
+
if (!appId) {
|
|
377
|
+
return { appId: null, reusable: false };
|
|
378
|
+
}
|
|
379
|
+
try {
|
|
380
|
+
runSilentFn(binPath, ["auth", "status"], { timeout: 10000 });
|
|
381
|
+
return { appId, reusable: true };
|
|
382
|
+
} catch (_) {
|
|
383
|
+
return { appId, reusable: false };
|
|
384
|
+
}
|
|
385
|
+
} catch (_) {
|
|
386
|
+
return { appId: null, reusable: false };
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
function listBundledSkills(skillsDir = DEFAULT_SKILLS_DIR) {
|
|
391
|
+
if (!fs.existsSync(skillsDir)) {
|
|
392
|
+
return [];
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
return fs
|
|
396
|
+
.readdirSync(skillsDir, { withFileTypes: true })
|
|
397
|
+
.filter(entry => entry.isDirectory())
|
|
398
|
+
.map(entry => {
|
|
399
|
+
const dir = path.join(skillsDir, entry.name);
|
|
400
|
+
return {
|
|
401
|
+
name: entry.name,
|
|
402
|
+
dir,
|
|
403
|
+
skillFile: path.join(dir, "SKILL.md"),
|
|
404
|
+
};
|
|
405
|
+
})
|
|
406
|
+
.filter(skill => !EXCLUDED_BUNDLED_SKILLS.has(skill.name))
|
|
407
|
+
.filter(skill => fs.existsSync(skill.skillFile))
|
|
408
|
+
.sort((a, b) => a.name.localeCompare(b.name));
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
function parseGlobalSkillsJSON(text) {
|
|
412
|
+
try {
|
|
413
|
+
const parsed = JSON.parse(text);
|
|
414
|
+
if (!Array.isArray(parsed)) return [];
|
|
415
|
+
return parsed
|
|
416
|
+
.map(item => (item && typeof item.name === "string" ? item.name.trim() : ""))
|
|
417
|
+
.filter(Boolean)
|
|
418
|
+
.sort();
|
|
419
|
+
} catch (_) {
|
|
420
|
+
return [];
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
function parseGlobalSkillsText(text) {
|
|
425
|
+
const seen = new Set();
|
|
426
|
+
const lines = String(text).split("\n");
|
|
427
|
+
for (const line of lines) {
|
|
428
|
+
const trimmed = line.trim();
|
|
429
|
+
if (!trimmed || trimmed === "Global Skills" || trimmed === "General" || trimmed === "Project" || trimmed === "Local") {
|
|
430
|
+
continue;
|
|
431
|
+
}
|
|
432
|
+
if (trimmed.startsWith("Tip:") || trimmed.startsWith("Agents:")) {
|
|
433
|
+
continue;
|
|
434
|
+
}
|
|
435
|
+
const candidate = trimmed.split(/\s+/)[0];
|
|
436
|
+
if (/^[A-Za-z0-9][A-Za-z0-9_:-]*(@[^\s]+)?$/.test(candidate)) {
|
|
437
|
+
seen.add(candidate);
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
return Array.from(seen).sort();
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
async function listInstalledGlobalSkills() {
|
|
444
|
+
try {
|
|
445
|
+
const jsonText = await runSkillsCommandAsync(["ls", "-g", "--json"], {
|
|
446
|
+
timeout: 120000,
|
|
447
|
+
});
|
|
448
|
+
const parsed = parseGlobalSkillsJSON(jsonText.toString());
|
|
449
|
+
if (parsed.length > 0) {
|
|
450
|
+
return parsed;
|
|
451
|
+
}
|
|
452
|
+
} catch (_) {
|
|
453
|
+
// fall through
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
try {
|
|
457
|
+
const text = await runSkillsCommandAsync(["ls", "-g"], {
|
|
458
|
+
timeout: 120000,
|
|
459
|
+
});
|
|
460
|
+
return parseGlobalSkillsText(text.toString());
|
|
461
|
+
} catch (_) {
|
|
462
|
+
return [];
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
function diffMissingSkills(bundledSkills, installedNames) {
|
|
467
|
+
const installed = new Set(installedNames);
|
|
468
|
+
return bundledSkills.filter(skill => !installed.has(skill.name));
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
async function stepSelectLang() {
|
|
472
|
+
const fromArg = parseLangArg();
|
|
473
|
+
if (fromArg) return fromArg;
|
|
474
|
+
|
|
475
|
+
const lang = await p.select({
|
|
476
|
+
message: "请选择语言 / Select language",
|
|
477
|
+
options: [
|
|
478
|
+
{ value: "zh", label: "中文" },
|
|
479
|
+
{ value: "en", label: "English" },
|
|
480
|
+
],
|
|
481
|
+
});
|
|
482
|
+
return handleCancel(lang, messages.zh);
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
async function stepInstallGlobally(msg) {
|
|
486
|
+
const currentPkg = getCurrentPackageName();
|
|
487
|
+
const installedVer = getGloballyInstalledVersion(currentPkg);
|
|
488
|
+
const currentVer = getCurrentPackageVersion();
|
|
489
|
+
const latestVer = getLatestVersion(currentPkg);
|
|
490
|
+
const plan = planGlobalInstall({
|
|
491
|
+
currentPackageName: currentPkg,
|
|
492
|
+
installedVersion: installedVer,
|
|
493
|
+
latestVersion: latestVer,
|
|
494
|
+
currentVersion: currentVer,
|
|
495
|
+
});
|
|
496
|
+
|
|
497
|
+
if (plan.skip) {
|
|
498
|
+
if (p) {
|
|
499
|
+
p.log.info(fmt(msg.step1Skip, installedVer));
|
|
500
|
+
} else {
|
|
501
|
+
console.log(fmt(msg.step1Skip, installedVer));
|
|
502
|
+
}
|
|
503
|
+
return false;
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
const spinner = p ? p.spinner() : null;
|
|
507
|
+
if (spinner) {
|
|
508
|
+
spinner.start(
|
|
509
|
+
plan.needsUpgrade
|
|
510
|
+
? fmt(msg.step1Upgrade, PKG, installedVer, plan.installVersion)
|
|
511
|
+
: fmt(msg.step1, PKG),
|
|
512
|
+
);
|
|
513
|
+
}
|
|
514
|
+
try {
|
|
515
|
+
await runSilentAsync("npm", ["install", "-g", plan.installTarget], { timeout: 120000 });
|
|
516
|
+
if (spinner) {
|
|
517
|
+
spinner.stop(plan.needsUpgrade ? fmt(msg.step1Upgraded, plan.installVersion) : msg.step1Done);
|
|
518
|
+
}
|
|
519
|
+
return true;
|
|
520
|
+
} catch (_) {
|
|
521
|
+
if (spinner) {
|
|
522
|
+
spinner.stop(fmt(msg.step1Fail, PKG));
|
|
523
|
+
} else {
|
|
524
|
+
console.error(fmt(msg.step1Fail, PKG));
|
|
525
|
+
}
|
|
526
|
+
process.exit(1);
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
async function stepInstallSkills(msg, deps = {}) {
|
|
531
|
+
const listBundled = deps.listBundledSkills || listBundledSkills;
|
|
532
|
+
const installSkill = deps.installSkill || (skill => runSkillsCommandAsync(["add", skill.dir, "-g", "-y"], {
|
|
533
|
+
timeout: 120000,
|
|
534
|
+
}));
|
|
535
|
+
const spinner = p ? p.spinner() : null;
|
|
536
|
+
if (spinner) {
|
|
537
|
+
spinner.start(msg.step2Spinner);
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
try {
|
|
541
|
+
const bundled = listBundled();
|
|
542
|
+
if (bundled.length === 0) {
|
|
543
|
+
if (spinner) {
|
|
544
|
+
spinner.stop(msg.step2Empty);
|
|
545
|
+
} else {
|
|
546
|
+
console.log(msg.step2Empty);
|
|
547
|
+
}
|
|
548
|
+
return;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
for (const skill of bundled) {
|
|
552
|
+
await installSkill(skill);
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
if (spinner) {
|
|
556
|
+
spinner.stop(msg.step2Done);
|
|
557
|
+
} else {
|
|
558
|
+
console.log(msg.step2Done);
|
|
559
|
+
}
|
|
560
|
+
} catch (err) {
|
|
561
|
+
if (spinner) {
|
|
562
|
+
spinner.stop(msg.step2Fail);
|
|
563
|
+
} else {
|
|
564
|
+
console.error(msg.step2Fail);
|
|
565
|
+
}
|
|
566
|
+
const detail = formatCommandFailure(err);
|
|
567
|
+
if (detail) {
|
|
568
|
+
if (p) {
|
|
569
|
+
p.log.error(detail);
|
|
570
|
+
} else {
|
|
571
|
+
console.error(detail);
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
process.exit(1);
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
async function stepConfigInit(msg, lang) {
|
|
579
|
+
const spinner = p ? p.spinner() : null;
|
|
580
|
+
if (spinner) {
|
|
581
|
+
spinner.start(msg.step3);
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
const peopleCli = whichPeopleCli();
|
|
585
|
+
if (!peopleCli) {
|
|
586
|
+
if (spinner) {
|
|
587
|
+
spinner.stop(msg.step3NotFound);
|
|
588
|
+
}
|
|
589
|
+
process.exit(1);
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
const existing = inspectExistingAppConfig(peopleCli);
|
|
593
|
+
if (spinner) {
|
|
594
|
+
spinner.stop(msg.step3);
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
if (existing.appId && existing.reusable) {
|
|
598
|
+
const reuse = await p.confirm({
|
|
599
|
+
message: fmt(msg.step3Found, existing.appId),
|
|
600
|
+
});
|
|
601
|
+
if (handleCancel(reuse, msg) && reuse) {
|
|
602
|
+
p.log.info(msg.step3Skip);
|
|
603
|
+
return;
|
|
604
|
+
}
|
|
605
|
+
} else if (existing.appId) {
|
|
606
|
+
p.log.warn(msg.step3Reinit);
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
try {
|
|
610
|
+
run(peopleCli, ["config", "init", "--new", "--lang", lang]);
|
|
611
|
+
p.log.success(msg.step3Done);
|
|
612
|
+
} catch (_) {
|
|
613
|
+
p.log.error(msg.step3Fail);
|
|
614
|
+
process.exit(1);
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
async function stepAuthLogin(msg) {
|
|
619
|
+
const peopleCli = whichPeopleCli();
|
|
620
|
+
if (!peopleCli) {
|
|
621
|
+
p.log.warn(msg.step4NotFound);
|
|
622
|
+
return;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
const yes = await p.confirm({
|
|
626
|
+
message: msg.step4Confirm,
|
|
627
|
+
});
|
|
628
|
+
if (p.isCancel(yes)) {
|
|
629
|
+
p.cancel(msg.cancelled);
|
|
630
|
+
process.exit(0);
|
|
631
|
+
}
|
|
632
|
+
if (!yes) {
|
|
633
|
+
p.log.info(msg.step4Skip);
|
|
634
|
+
return;
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
p.log.step(msg.step4);
|
|
638
|
+
try {
|
|
639
|
+
run(peopleCli, buildInstallAuthLoginArgs());
|
|
640
|
+
p.log.success(msg.step4Done);
|
|
641
|
+
} catch (_) {
|
|
642
|
+
p.log.warn(msg.step4Fail);
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
async function main() {
|
|
647
|
+
const isInteractive = !!process.stdin.isTTY;
|
|
648
|
+
const lang = isInteractive ? await stepSelectLang() : (parseLangArg() || "zh");
|
|
649
|
+
const msg = messages[lang];
|
|
650
|
+
|
|
651
|
+
if (isInteractive) {
|
|
652
|
+
p.intro(msg.setup);
|
|
653
|
+
await stepInstallGlobally(msg);
|
|
654
|
+
await stepInstallSkills(msg);
|
|
655
|
+
await stepConfigInit(msg, lang);
|
|
656
|
+
await stepAuthLogin(msg);
|
|
657
|
+
p.outro(msg.done);
|
|
658
|
+
} else {
|
|
659
|
+
console.log(msg.setup);
|
|
660
|
+
await stepInstallGlobally(msg);
|
|
661
|
+
await stepInstallSkills(msg);
|
|
662
|
+
console.log(msg.nonTtyHint);
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
async function runCLI() {
|
|
667
|
+
p = await import("@clack/prompts");
|
|
668
|
+
await main();
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
module.exports = {
|
|
672
|
+
buildInstallAuthLoginArgs,
|
|
673
|
+
DEFAULT_SKILLS_DIR,
|
|
674
|
+
diffMissingSkills,
|
|
675
|
+
formatCommandFailure,
|
|
676
|
+
buildSkillsCommandArgs,
|
|
677
|
+
inspectExistingAppConfig,
|
|
678
|
+
listBundledSkills,
|
|
679
|
+
listInstalledGlobalSkills,
|
|
680
|
+
parseGlobalSkillsJSON,
|
|
681
|
+
parseGlobalSkillsText,
|
|
682
|
+
parseLangArg,
|
|
683
|
+
planGlobalInstall,
|
|
684
|
+
resolveSkillsLauncher,
|
|
685
|
+
stepInstallSkills,
|
|
686
|
+
main,
|
|
687
|
+
runCLI,
|
|
688
|
+
};
|
|
689
|
+
|
|
690
|
+
if (require.main === module) {
|
|
691
|
+
runCLI().catch(err => {
|
|
692
|
+
const message = "Unexpected error: " + (err.message || err);
|
|
693
|
+
if (p) {
|
|
694
|
+
p.cancel(message);
|
|
695
|
+
} else {
|
|
696
|
+
console.error(message);
|
|
697
|
+
}
|
|
698
|
+
process.exit(1);
|
|
699
|
+
});
|
|
700
|
+
}
|