@whobot/cli 2.0.1 → 2.0.3
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/bin/install.mjs +41 -2
- package/package.json +1 -1
package/bin/install.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { createHash } from "node:crypto";
|
|
2
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
3
3
|
import { spawnSync } from "node:child_process";
|
|
4
4
|
import {
|
|
5
5
|
accessSync,
|
|
@@ -678,9 +678,14 @@ export async function runExplicitInstall(argv, env) {
|
|
|
678
678
|
const staging = await fs.mkdtemp(path.join(os.tmpdir(), "whobot-install-preflight-"));
|
|
679
679
|
const stagedNative = path.join(staging, path.basename(packageNativeBinary(prefix)));
|
|
680
680
|
const updateArgs = ["update", "--yes", "--target-version", pkg.version];
|
|
681
|
+
const installId = randomUUID();
|
|
682
|
+
const reportAttrs = { installId, version: pkg.version, channel: bakedChannel(), previousVersion: registered };
|
|
683
|
+
let staged = false;
|
|
681
684
|
let prepared;
|
|
682
685
|
try {
|
|
683
686
|
await ensureNativeBinary(env, { force: true, dest: stagedNative, version: pkg.version });
|
|
687
|
+
staged = true;
|
|
688
|
+
reportViaNative(stagedNative, "install_started", "ok", { attrs: reportAttrs }, env);
|
|
684
689
|
verifyNpmEntryVersion(stagedNative, pkg.version, env);
|
|
685
690
|
if (resetLocal) runResetLocal(stagedNative, env);
|
|
686
691
|
runInstallPhase(stagedNative, ["update", "check-install", "--previous-binary", packageNativeBinary(prefix)], env, "binding", registered);
|
|
@@ -695,17 +700,43 @@ export async function runExplicitInstall(argv, env) {
|
|
|
695
700
|
const installed = await verifyInstalledCLI(tools, npmEnv, prefix, pkg.version, env, ignoreLauncher);
|
|
696
701
|
runInstallPhase(installed.native, updateArgs, env, "verify", registered);
|
|
697
702
|
await prepared.discard();
|
|
703
|
+
reportViaNative(installed.native, "install_succeeded", "ok", { attrs: reportAttrs }, env);
|
|
698
704
|
if (resetLocal) logStderr("本机登录已清理,请重新登录。");
|
|
699
705
|
logStderr(`CLI 软件安装已完成;请更新 Skill 至对应渠道最新版,重载宿主或新建任务后重新预检。v2 后续更新请运行 ${COMMAND} update,并执行确认卡中的命令。`);
|
|
700
706
|
return entry;
|
|
701
707
|
} catch (err) {
|
|
702
708
|
if (prepared) await prepared.restore();
|
|
709
|
+
// Before the program is staged there is nothing to report with.
|
|
710
|
+
if (staged) {
|
|
711
|
+
reportViaNative(stagedNative, "install_failed", "failed", { errorCode: failureCode(err) || "INSTALL_FAILED", attrs: reportAttrs }, env);
|
|
712
|
+
}
|
|
703
713
|
throw err;
|
|
704
714
|
} finally {
|
|
705
715
|
await fs.rm(staging, { recursive: true, force: true });
|
|
706
716
|
}
|
|
707
717
|
}
|
|
708
718
|
|
|
719
|
+
// reportViaNative records an install/update fact for analytics (ADR-0020)
|
|
720
|
+
// through the native program's hidden reporter, which carries its compiled API
|
|
721
|
+
// address, client id and AI host. Its outcome is ignored and it is bounded, so
|
|
722
|
+
// it never fails or stalls an install or update.
|
|
723
|
+
const REPORT_TIMEOUT_MS = 5000;
|
|
724
|
+
export function reportViaNative(native, name, result, { errorCode = "", attrs = {} } = {}, env = process.env) {
|
|
725
|
+
const args = ["telemetry", "report", "--name", name, "--result", result];
|
|
726
|
+
if (errorCode) args.push("--error-code", errorCode);
|
|
727
|
+
for (const [key, value] of Object.entries(attrs)) {
|
|
728
|
+
if (value !== undefined && value !== null && value !== "") args.push("--attr", `${key}=${value}`);
|
|
729
|
+
}
|
|
730
|
+
const reportEnv = { ...env };
|
|
731
|
+
delete reportEnv.WHOBOT_UPDATE_PHASE;
|
|
732
|
+
spawnSync(native, args, { env: reportEnv, stdio: "ignore", timeout: REPORT_TIMEOUT_MS, windowsHide: true });
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
function failureCode(err) {
|
|
736
|
+
const code = err && typeof err.code === "string" ? err.code : "";
|
|
737
|
+
return /^[A-Z][A-Z0-9_]{0,63}$/.test(code) ? code : "";
|
|
738
|
+
}
|
|
739
|
+
|
|
709
740
|
function runInstallPhase(native, args, env, phase, previous, wantVersion = pkg.version) {
|
|
710
741
|
const result = spawnSync(native, args, {
|
|
711
742
|
encoding: "utf8",
|
|
@@ -856,10 +887,12 @@ export async function runWhobot(argv, env) {
|
|
|
856
887
|
}
|
|
857
888
|
await recoverNativeBackup(nativeBinaryPath(pkgRoot));
|
|
858
889
|
if (isUpdateApply(argv)) {
|
|
890
|
+
const updateAttrs = { updateId: randomUUID(), fromVersion: pkg.version };
|
|
891
|
+
let started = false;
|
|
859
892
|
try {
|
|
860
893
|
rejectExternalChannel(env);
|
|
861
894
|
parseChannelAndForward(argv);
|
|
862
|
-
confirmedTargetVersion(argv);
|
|
895
|
+
updateAttrs.toVersion = confirmedTargetVersion(argv);
|
|
863
896
|
let currentNative = nativeBinaryPath(pkgRoot);
|
|
864
897
|
let preflightDir;
|
|
865
898
|
let pre;
|
|
@@ -902,7 +935,10 @@ export async function runWhobot(argv, env) {
|
|
|
902
935
|
if (!preflight.ok || preflight.data?.installedVersion !== pkg.version || preflight.next?.action !== "done") {
|
|
903
936
|
throw new InstallerError("更新前检查没有确认可以替换程序", "UPDATE_VERIFY_FAILED");
|
|
904
937
|
}
|
|
938
|
+
reportViaNative(nativeBinaryPath(pkgRoot), "update_started", "ok", { attrs: updateAttrs }, env);
|
|
939
|
+
started = true;
|
|
905
940
|
const updated = await runExplicitUpdate(argv, env);
|
|
941
|
+
reportViaNative(updated.native, "update_succeeded", "replaced", { attrs: { ...updateAttrs, fromVersion: updated.previous, toVersion: updated.installedVersion } }, env);
|
|
906
942
|
spawnNative(
|
|
907
943
|
updated.native,
|
|
908
944
|
updated.forwarded,
|
|
@@ -916,6 +952,9 @@ export async function runWhobot(argv, env) {
|
|
|
916
952
|
);
|
|
917
953
|
} catch (err) {
|
|
918
954
|
const code = err?.code === "CHANNEL_INVALID" ? "CHANNEL_INVALID" : "UPDATE_VERIFY_FAILED";
|
|
955
|
+
if (started) {
|
|
956
|
+
reportViaNative(nativeBinaryPath(pkgRoot), "update_failed", "failed", { errorCode: failureCode(err) || code, attrs: updateAttrs }, env);
|
|
957
|
+
}
|
|
919
958
|
const message = err && err.message ? err.message : String(err);
|
|
920
959
|
writeJSONFailure(code, message);
|
|
921
960
|
process.stderr.write(`${message}\n`);
|