@newlandnpt/pep-cli 0.9.2 → 0.9.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.
Files changed (2) hide show
  1. package/dist/pep.cjs +96 -4
  2. package/package.json +1 -1
package/dist/pep.cjs CHANGED
@@ -684,6 +684,7 @@ public static class PepCredential {
684
684
  function encodedPowerShell(script) {
685
685
  return Buffer.from(script, "utf16le").toString("base64");
686
686
  }
687
+ var CREDENTIAL_TOOL_TIMEOUT_MS = 6e4;
687
688
  async function runPowerShell(script, input) {
688
689
  return new Promise((resolve5, reject) => {
689
690
  const child = (0, import_node_child_process2.spawn)(
@@ -699,7 +700,11 @@ async function runPowerShell(script, input) {
699
700
  );
700
701
  let stdout = "";
701
702
  let stderr = "";
702
- const timeout = setTimeout(() => child.kill(), 1e4);
703
+ let killed = false;
704
+ const timeout = setTimeout(() => {
705
+ killed = true;
706
+ child.kill();
707
+ }, CREDENTIAL_TOOL_TIMEOUT_MS);
703
708
  child.stdout.setEncoding("utf8").on("data", (chunk) => stdout += chunk);
704
709
  child.stderr.setEncoding("utf8").on("data", (chunk) => stderr += chunk);
705
710
  child.once(
@@ -708,6 +713,14 @@ async function runPowerShell(script, input) {
708
713
  );
709
714
  child.once("close", (code) => {
710
715
  clearTimeout(timeout);
716
+ if (killed) {
717
+ reject(
718
+ new Error(
719
+ `Windows Credential Manager did not answer within ${CREDENTIAL_TOOL_TIMEOUT_MS / 1e3}s. The machine may be heavily loaded \u2014 try again.`
720
+ )
721
+ );
722
+ return;
723
+ }
711
724
  if (code === 0) resolve5(stdout.trim());
712
725
  else
713
726
  reject(
@@ -768,7 +781,11 @@ async function runSecurity(args) {
768
781
  });
769
782
  let stdout = "";
770
783
  let stderr = "";
771
- const timeout = setTimeout(() => child.kill(), 1e4);
784
+ let killed = false;
785
+ const timeout = setTimeout(() => {
786
+ killed = true;
787
+ child.kill();
788
+ }, CREDENTIAL_TOOL_TIMEOUT_MS);
772
789
  child.stdout.setEncoding("utf8").on("data", (chunk) => stdout += chunk);
773
790
  child.stderr.setEncoding("utf8").on("data", (chunk) => stderr += chunk);
774
791
  child.once(
@@ -777,6 +794,14 @@ async function runSecurity(args) {
777
794
  );
778
795
  child.once("close", (code) => {
779
796
  clearTimeout(timeout);
797
+ if (killed) {
798
+ reject(
799
+ new Error(
800
+ `macOS Keychain did not answer within ${CREDENTIAL_TOOL_TIMEOUT_MS / 1e3}s. The machine may be heavily loaded \u2014 try again.`
801
+ )
802
+ );
803
+ return;
804
+ }
780
805
  resolve5({ code: code ?? -1, stdout, stderr });
781
806
  });
782
807
  });
@@ -1117,6 +1142,30 @@ async function blobShaOfFile(path) {
1117
1142
  });
1118
1143
  return digest.digest("hex");
1119
1144
  }
1145
+ function humanBytes(n) {
1146
+ if (n < 1024) return `${n} B`;
1147
+ const units = ["KB", "MB", "GB"];
1148
+ let value = n / 1024;
1149
+ let unit = 0;
1150
+ while (value >= 1024 && unit < units.length - 1) {
1151
+ value /= 1024;
1152
+ unit += 1;
1153
+ }
1154
+ return `${value.toFixed(value < 10 ? 1 : 0)} ${units[unit]}`;
1155
+ }
1156
+ function formatProgress(progress, width = 24) {
1157
+ if (progress.phase === "verifying") return " verifying\u2026";
1158
+ const { bytes, total, elapsedMs } = progress;
1159
+ const speed = elapsedMs > 0 && bytes > 0 ? ` \xB7 ${humanBytes(bytes / elapsedMs * 1e3)}/s` : "";
1160
+ if (total === void 0 || total <= 0) {
1161
+ return ` ${humanBytes(bytes)}${speed}`;
1162
+ }
1163
+ const ratio = Math.min(1, bytes / total);
1164
+ const filled = Math.round(ratio * width);
1165
+ const bar = `${"\u2588".repeat(filled)}${"\u2591".repeat(width - filled)}`;
1166
+ const pct = `${Math.floor(ratio * 100)}`.padStart(3);
1167
+ return ` ${bar} ${pct}% ${humanBytes(bytes)} / ${humanBytes(total)}${speed}`;
1168
+ }
1120
1169
  async function downloadResource(dependencies, resource, options) {
1121
1170
  const file = (0, import_node_path3.basename)(resource.file);
1122
1171
  if (file !== resource.file || file === "." || file === "..") {
@@ -1149,8 +1198,26 @@ async function downloadResource(dependencies, resource, options) {
1149
1198
  await (0, import_promises3.mkdir)(directory, { recursive: true });
1150
1199
  const temporary = `${target}.${process.pid}.part`;
1151
1200
  try {
1201
+ const report = options.onProgress;
1202
+ const startedAt = Date.now();
1203
+ let seen = 0;
1152
1204
  await (0, import_promises4.pipeline)(
1153
1205
  import_node_stream.Readable.fromWeb(response.body),
1206
+ // ⚠ 这一层**无条件**插着,没有回调时它只是加一个数。本来想「没人听就不插」,
1207
+ // 但那要往 `pipeline(...)` 里展开一个数组,TS 的重载当场推不出来;为省一层
1208
+ // generator 去跟类型较劲不划算。
1209
+ async function* (source) {
1210
+ for await (const chunk of source) {
1211
+ seen += chunk.length;
1212
+ report?.({
1213
+ phase: "downloading",
1214
+ bytes: seen,
1215
+ ...Number.isFinite(declared) && declared > 0 ? { total: declared } : {},
1216
+ elapsedMs: Date.now() - startedAt
1217
+ });
1218
+ yield chunk;
1219
+ }
1220
+ },
1154
1221
  (0, import_node_fs.createWriteStream)(temporary)
1155
1222
  );
1156
1223
  const bytes = (await (0, import_promises3.stat)(temporary)).size;
@@ -1161,6 +1228,7 @@ async function downloadResource(dependencies, resource, options) {
1161
1228
  }
1162
1229
  let verified = "none";
1163
1230
  if (expected !== void 0) {
1231
+ options.onProgress?.({ phase: "verifying" });
1164
1232
  const actual = (await blobShaOfFile(temporary)).slice(0, 12);
1165
1233
  if (actual !== expected) {
1166
1234
  throw new Error(
@@ -2484,8 +2552,29 @@ async function planSetup(dependencies) {
2484
2552
  }
2485
2553
 
2486
2554
  // src/cli.ts
2487
- var VERSION = false ? "0.0.0-dev" : "0.9.2";
2555
+ var VERSION = false ? "0.0.0-dev" : "0.9.3";
2488
2556
  var SKILLS_ADD_DEPRECATION = "\u26A0 `pep skills add` will be removed in a future release.";
2557
+ function createProgressReporter(stream = process.stderr) {
2558
+ if (!stream.isTTY) return void 0;
2559
+ const INTERVAL_MS = 100;
2560
+ let lastAt = 0;
2561
+ let painted = false;
2562
+ const paint = (line) => {
2563
+ stream.write(`\r${" ".repeat(78)}\r${line}`);
2564
+ painted = true;
2565
+ };
2566
+ return {
2567
+ report(progress) {
2568
+ const now = Date.now();
2569
+ if (progress.phase === "downloading" && now - lastAt < INTERVAL_MS) return;
2570
+ lastAt = now;
2571
+ paint(formatProgress(progress));
2572
+ },
2573
+ done() {
2574
+ if (painted) stream.write(`\r${" ".repeat(78)}\r`);
2575
+ }
2576
+ };
2577
+ }
2489
2578
  function reportUpdate(result) {
2490
2579
  const conflicts = () => {
2491
2580
  for (const { skill, owner } of result.conflicts) {
@@ -2922,11 +3011,14 @@ ${usage(VERSION, [group])}`);
2922
3011
  return;
2923
3012
  }
2924
3013
  const target = pickResource(all, wantedFile);
3014
+ const progress = createProgressReporter();
2925
3015
  const result = await downloadResource(dependencies, target, {
2926
3016
  // 默认落在**执行命令时的当前目录**,不是 CLI 装在哪儿。
2927
3017
  directory: intoDirectory ?? process.cwd(),
2928
- force
3018
+ force,
3019
+ ...progress ? { onProgress: progress.report } : {}
2929
3020
  });
3021
+ progress?.done();
2930
3022
  console.error(
2931
3023
  result.verified === "blob-sha" ? ` ${result.bytes} bytes, content hash verified` : result.verified === "content-length" ? ` ${result.bytes} bytes, length verified (no content hash in the address)` : ` ${result.bytes} bytes \u2014 the server declared neither length nor content hash`
2932
3024
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@newlandnpt/pep-cli",
3
- "version": "0.9.2",
3
+ "version": "0.9.3",
4
4
  "description": "PEP CLI —— 凭 PEP 身份读取 Newland 开发者文档与 agent skills",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",