@newlandnpt/pep-cli 0.9.2 → 0.9.4
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/pep.cjs +125 -14
- package/package.json +1 -1
package/dist/pep.cjs
CHANGED
|
@@ -23,6 +23,17 @@ var DEFAULT_SCOPES = [
|
|
|
23
23
|
var DEFAULT_RESOURCES = ["urn:newland:pep:docs"];
|
|
24
24
|
var LEGACY_PACKAGE = "semi-integration";
|
|
25
25
|
var DEFAULT_DOCS_URL = "https://docs.newlandnpt.us";
|
|
26
|
+
var DOCS_BY_ISSUER = {
|
|
27
|
+
"https://pep.newlandnpt.us": "https://docs.newlandnpt.us",
|
|
28
|
+
"https://pep-webapp-view.onrender.com": "https://pep-developer-docs.onrender.com"
|
|
29
|
+
};
|
|
30
|
+
function docsUrlForIssuer(issuer) {
|
|
31
|
+
try {
|
|
32
|
+
return DOCS_BY_ISSUER[normalizeIssuer(issuer)];
|
|
33
|
+
} catch {
|
|
34
|
+
return void 0;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
26
37
|
var DEFAULT_SKILLS_GROUP = "distribution/skills";
|
|
27
38
|
var EXAMPLE_SKILLS_REPO = `${DEFAULT_SKILLS_GROUP}/semi-integration-skill`;
|
|
28
39
|
function issuerForEnvironment(environment) {
|
|
@@ -684,6 +695,7 @@ public static class PepCredential {
|
|
|
684
695
|
function encodedPowerShell(script) {
|
|
685
696
|
return Buffer.from(script, "utf16le").toString("base64");
|
|
686
697
|
}
|
|
698
|
+
var CREDENTIAL_TOOL_TIMEOUT_MS = 6e4;
|
|
687
699
|
async function runPowerShell(script, input) {
|
|
688
700
|
return new Promise((resolve5, reject) => {
|
|
689
701
|
const child = (0, import_node_child_process2.spawn)(
|
|
@@ -699,7 +711,11 @@ async function runPowerShell(script, input) {
|
|
|
699
711
|
);
|
|
700
712
|
let stdout = "";
|
|
701
713
|
let stderr = "";
|
|
702
|
-
|
|
714
|
+
let killed = false;
|
|
715
|
+
const timeout = setTimeout(() => {
|
|
716
|
+
killed = true;
|
|
717
|
+
child.kill();
|
|
718
|
+
}, CREDENTIAL_TOOL_TIMEOUT_MS);
|
|
703
719
|
child.stdout.setEncoding("utf8").on("data", (chunk) => stdout += chunk);
|
|
704
720
|
child.stderr.setEncoding("utf8").on("data", (chunk) => stderr += chunk);
|
|
705
721
|
child.once(
|
|
@@ -708,6 +724,14 @@ async function runPowerShell(script, input) {
|
|
|
708
724
|
);
|
|
709
725
|
child.once("close", (code) => {
|
|
710
726
|
clearTimeout(timeout);
|
|
727
|
+
if (killed) {
|
|
728
|
+
reject(
|
|
729
|
+
new Error(
|
|
730
|
+
`Windows Credential Manager did not answer within ${CREDENTIAL_TOOL_TIMEOUT_MS / 1e3}s. The machine may be heavily loaded \u2014 try again.`
|
|
731
|
+
)
|
|
732
|
+
);
|
|
733
|
+
return;
|
|
734
|
+
}
|
|
711
735
|
if (code === 0) resolve5(stdout.trim());
|
|
712
736
|
else
|
|
713
737
|
reject(
|
|
@@ -768,7 +792,11 @@ async function runSecurity(args) {
|
|
|
768
792
|
});
|
|
769
793
|
let stdout = "";
|
|
770
794
|
let stderr = "";
|
|
771
|
-
|
|
795
|
+
let killed = false;
|
|
796
|
+
const timeout = setTimeout(() => {
|
|
797
|
+
killed = true;
|
|
798
|
+
child.kill();
|
|
799
|
+
}, CREDENTIAL_TOOL_TIMEOUT_MS);
|
|
772
800
|
child.stdout.setEncoding("utf8").on("data", (chunk) => stdout += chunk);
|
|
773
801
|
child.stderr.setEncoding("utf8").on("data", (chunk) => stderr += chunk);
|
|
774
802
|
child.once(
|
|
@@ -777,6 +805,14 @@ async function runSecurity(args) {
|
|
|
777
805
|
);
|
|
778
806
|
child.once("close", (code) => {
|
|
779
807
|
clearTimeout(timeout);
|
|
808
|
+
if (killed) {
|
|
809
|
+
reject(
|
|
810
|
+
new Error(
|
|
811
|
+
`macOS Keychain did not answer within ${CREDENTIAL_TOOL_TIMEOUT_MS / 1e3}s. The machine may be heavily loaded \u2014 try again.`
|
|
812
|
+
)
|
|
813
|
+
);
|
|
814
|
+
return;
|
|
815
|
+
}
|
|
780
816
|
resolve5({ code: code ?? -1, stdout, stderr });
|
|
781
817
|
});
|
|
782
818
|
});
|
|
@@ -1117,6 +1153,30 @@ async function blobShaOfFile(path) {
|
|
|
1117
1153
|
});
|
|
1118
1154
|
return digest.digest("hex");
|
|
1119
1155
|
}
|
|
1156
|
+
function humanBytes(n) {
|
|
1157
|
+
if (n < 1024) return `${n} B`;
|
|
1158
|
+
const units = ["KB", "MB", "GB"];
|
|
1159
|
+
let value = n / 1024;
|
|
1160
|
+
let unit = 0;
|
|
1161
|
+
while (value >= 1024 && unit < units.length - 1) {
|
|
1162
|
+
value /= 1024;
|
|
1163
|
+
unit += 1;
|
|
1164
|
+
}
|
|
1165
|
+
return `${value.toFixed(value < 10 ? 1 : 0)} ${units[unit]}`;
|
|
1166
|
+
}
|
|
1167
|
+
function formatProgress(progress, width = 24) {
|
|
1168
|
+
if (progress.phase === "verifying") return " verifying\u2026";
|
|
1169
|
+
const { bytes, total, elapsedMs } = progress;
|
|
1170
|
+
const speed = elapsedMs > 0 && bytes > 0 ? ` \xB7 ${humanBytes(bytes / elapsedMs * 1e3)}/s` : "";
|
|
1171
|
+
if (total === void 0 || total <= 0) {
|
|
1172
|
+
return ` ${humanBytes(bytes)}${speed}`;
|
|
1173
|
+
}
|
|
1174
|
+
const ratio = Math.min(1, bytes / total);
|
|
1175
|
+
const filled = Math.round(ratio * width);
|
|
1176
|
+
const bar = `${"\u2588".repeat(filled)}${"\u2591".repeat(width - filled)}`;
|
|
1177
|
+
const pct = `${Math.floor(ratio * 100)}`.padStart(3);
|
|
1178
|
+
return ` ${bar} ${pct}% ${humanBytes(bytes)} / ${humanBytes(total)}${speed}`;
|
|
1179
|
+
}
|
|
1120
1180
|
async function downloadResource(dependencies, resource, options) {
|
|
1121
1181
|
const file = (0, import_node_path3.basename)(resource.file);
|
|
1122
1182
|
if (file !== resource.file || file === "." || file === "..") {
|
|
@@ -1149,8 +1209,26 @@ async function downloadResource(dependencies, resource, options) {
|
|
|
1149
1209
|
await (0, import_promises3.mkdir)(directory, { recursive: true });
|
|
1150
1210
|
const temporary = `${target}.${process.pid}.part`;
|
|
1151
1211
|
try {
|
|
1212
|
+
const report = options.onProgress;
|
|
1213
|
+
const startedAt = Date.now();
|
|
1214
|
+
let seen = 0;
|
|
1152
1215
|
await (0, import_promises4.pipeline)(
|
|
1153
1216
|
import_node_stream.Readable.fromWeb(response.body),
|
|
1217
|
+
// ⚠ 这一层**无条件**插着,没有回调时它只是加一个数。本来想「没人听就不插」,
|
|
1218
|
+
// 但那要往 `pipeline(...)` 里展开一个数组,TS 的重载当场推不出来;为省一层
|
|
1219
|
+
// generator 去跟类型较劲不划算。
|
|
1220
|
+
async function* (source) {
|
|
1221
|
+
for await (const chunk of source) {
|
|
1222
|
+
seen += chunk.length;
|
|
1223
|
+
report?.({
|
|
1224
|
+
phase: "downloading",
|
|
1225
|
+
bytes: seen,
|
|
1226
|
+
...Number.isFinite(declared) && declared > 0 ? { total: declared } : {},
|
|
1227
|
+
elapsedMs: Date.now() - startedAt
|
|
1228
|
+
});
|
|
1229
|
+
yield chunk;
|
|
1230
|
+
}
|
|
1231
|
+
},
|
|
1154
1232
|
(0, import_node_fs.createWriteStream)(temporary)
|
|
1155
1233
|
);
|
|
1156
1234
|
const bytes = (await (0, import_promises3.stat)(temporary)).size;
|
|
@@ -1161,6 +1239,7 @@ async function downloadResource(dependencies, resource, options) {
|
|
|
1161
1239
|
}
|
|
1162
1240
|
let verified = "none";
|
|
1163
1241
|
if (expected !== void 0) {
|
|
1242
|
+
options.onProgress?.({ phase: "verifying" });
|
|
1164
1243
|
const actual = (await blobShaOfFile(temporary)).slice(0, 12);
|
|
1165
1244
|
if (actual !== expected) {
|
|
1166
1245
|
throw new Error(
|
|
@@ -1308,6 +1387,7 @@ async function persistMavenEnv(credential, target = resolveTarget(), now = () =>
|
|
|
1308
1387
|
var EXAMPLE_PATH = EXAMPLE_SKILLS_REPO;
|
|
1309
1388
|
var EXAMPLE_REPO = `https://git.newlandpayment.com/${EXAMPLE_PATH}.git`;
|
|
1310
1389
|
var PROJECT_FLAG = "-p, --project Install into ./.agents/skills of the current directory instead";
|
|
1390
|
+
var DOCS_URL_FLAG = `--docs-url <url> This run only; login picks the one paired with your PEP (default: ${DEFAULT_DOCS_URL})`;
|
|
1311
1391
|
var REGION_FLAG = "--region <id> Documentation region for this run only (see pep docs regions)";
|
|
1312
1392
|
var groups = {
|
|
1313
1393
|
auth: {
|
|
@@ -1435,7 +1515,7 @@ pep skills update will not bring the skills back. Run pep skills add to do that.
|
|
|
1435
1515
|
usage: "pep docs list [flags]",
|
|
1436
1516
|
description: "Requires a saved PEP login. Prints each document's path and description.\nPass a path from this list to pep docs get to read its Markdown content.\n\nWithout --region you get the Global English documentation. The region applies to\nthis run only and is never remembered.",
|
|
1437
1517
|
flags: [
|
|
1438
|
-
|
|
1518
|
+
DOCS_URL_FLAG,
|
|
1439
1519
|
REGION_FLAG
|
|
1440
1520
|
],
|
|
1441
1521
|
examples: [
|
|
@@ -1449,7 +1529,7 @@ pep skills update will not bring the skills back. Run pep skills add to do that.
|
|
|
1449
1529
|
usage: "pep docs get <path> [flags]",
|
|
1450
1530
|
description: "Requires a saved PEP login. Use a document path returned by pep docs list.\nWrites the document's Markdown to stdout, ready to pipe or save to a file.\n\nThe path already names its region, so a path from pep docs list --region na reads\nthe North American version. Pass --region to read that same document from another\nregion instead.",
|
|
1451
1531
|
flags: [
|
|
1452
|
-
|
|
1532
|
+
DOCS_URL_FLAG,
|
|
1453
1533
|
REGION_FLAG
|
|
1454
1534
|
],
|
|
1455
1535
|
examples: [
|
|
@@ -1462,7 +1542,7 @@ pep skills update will not bring the skills back. Run pep skills add to do that.
|
|
|
1462
1542
|
summary: "List the documentation regions this deployment serves.",
|
|
1463
1543
|
usage: "pep docs regions [flags]",
|
|
1464
1544
|
description: 'Prints one region per line: id, aliases, default language, name.\nThe list comes from the documentation server, so a region added there shows up\nwithout upgrading the CLI.\n\nThis is the one docs command that needs no login: "what do I pass to --region?"\nis a question you have before signing in.',
|
|
1465
|
-
flags: [
|
|
1545
|
+
flags: [DOCS_URL_FLAG],
|
|
1466
1546
|
examples: ["pep docs regions"]
|
|
1467
1547
|
}
|
|
1468
1548
|
}
|
|
@@ -1477,7 +1557,7 @@ pep skills update will not bring the skills back. Run pep skills add to do that.
|
|
|
1477
1557
|
flags: [
|
|
1478
1558
|
`--platform <name> Only this platform (${PLATFORMS.join(", ")})`,
|
|
1479
1559
|
REGION_FLAG,
|
|
1480
|
-
|
|
1560
|
+
DOCS_URL_FLAG
|
|
1481
1561
|
],
|
|
1482
1562
|
examples: [
|
|
1483
1563
|
"pep resources list",
|
|
@@ -1493,7 +1573,7 @@ pep skills update will not bring the skills back. Run pep skills add to do that.
|
|
|
1493
1573
|
"--dir <path> Directory to save into; created if missing (default: the current directory)",
|
|
1494
1574
|
"--force Replace an existing file of the same name",
|
|
1495
1575
|
REGION_FLAG,
|
|
1496
|
-
|
|
1576
|
+
DOCS_URL_FLAG
|
|
1497
1577
|
],
|
|
1498
1578
|
examples: [
|
|
1499
1579
|
"pep resources download SaleSystemDemo-4.8.0.2-win-x64.zip",
|
|
@@ -2484,8 +2564,29 @@ async function planSetup(dependencies) {
|
|
|
2484
2564
|
}
|
|
2485
2565
|
|
|
2486
2566
|
// src/cli.ts
|
|
2487
|
-
var VERSION = false ? "0.0.0-dev" : "0.9.
|
|
2567
|
+
var VERSION = false ? "0.0.0-dev" : "0.9.4";
|
|
2488
2568
|
var SKILLS_ADD_DEPRECATION = "\u26A0 `pep skills add` will be removed in a future release.";
|
|
2569
|
+
function createProgressReporter(stream = process.stderr) {
|
|
2570
|
+
if (!stream.isTTY) return void 0;
|
|
2571
|
+
const INTERVAL_MS = 100;
|
|
2572
|
+
let lastAt = 0;
|
|
2573
|
+
let painted = false;
|
|
2574
|
+
const paint = (line) => {
|
|
2575
|
+
stream.write(`\r${" ".repeat(78)}\r${line}`);
|
|
2576
|
+
painted = true;
|
|
2577
|
+
};
|
|
2578
|
+
return {
|
|
2579
|
+
report(progress) {
|
|
2580
|
+
const now = Date.now();
|
|
2581
|
+
if (progress.phase === "downloading" && now - lastAt < INTERVAL_MS) return;
|
|
2582
|
+
lastAt = now;
|
|
2583
|
+
paint(formatProgress(progress));
|
|
2584
|
+
},
|
|
2585
|
+
done() {
|
|
2586
|
+
if (painted) stream.write(`\r${" ".repeat(78)}\r`);
|
|
2587
|
+
}
|
|
2588
|
+
};
|
|
2589
|
+
}
|
|
2489
2590
|
function reportUpdate(result) {
|
|
2490
2591
|
const conflicts = () => {
|
|
2491
2592
|
for (const { skill, owner } of result.conflicts) {
|
|
@@ -2565,14 +2666,14 @@ async function loginConfig(args, store = fileConfigStore()) {
|
|
|
2565
2666
|
const explicit = repeatedOption(args, "--resource");
|
|
2566
2667
|
const resources = explicit.length > 0 ? explicit : [...DEFAULT_RESOURCES];
|
|
2567
2668
|
if (args.length > 0) throw new Error(`Unknown option: ${args[0]}`);
|
|
2568
|
-
const
|
|
2669
|
+
const paired = docsUrlForIssuer(issuer);
|
|
2569
2670
|
return {
|
|
2570
2671
|
version: 1,
|
|
2571
2672
|
issuer,
|
|
2572
2673
|
clientId,
|
|
2573
2674
|
redirectUri: DEFAULT_REDIRECT_URI,
|
|
2574
2675
|
resources,
|
|
2575
|
-
...
|
|
2676
|
+
...paired !== void 0 ? { docsUrl: paired } : {}
|
|
2576
2677
|
};
|
|
2577
2678
|
}
|
|
2578
2679
|
function takeLoginOptions(args) {
|
|
@@ -2880,9 +2981,6 @@ ${usage(VERSION, [group])}`);
|
|
|
2880
2981
|
}
|
|
2881
2982
|
const saved = await configStore.read();
|
|
2882
2983
|
const docsUrl = explicitDocsUrl ? normalizeDocsUrl(explicitDocsUrl) : saved?.docsUrl ?? DEFAULT_DOCS_URL;
|
|
2883
|
-
if (explicitDocsUrl && saved && saved.docsUrl !== docsUrl) {
|
|
2884
|
-
await configStore.write({ ...saved, docsUrl });
|
|
2885
|
-
}
|
|
2886
2984
|
if (action === "docs:regions") {
|
|
2887
2985
|
const known = await fetchRegions({ docsUrl });
|
|
2888
2986
|
for (const one of known.regions) {
|
|
@@ -2922,11 +3020,14 @@ ${usage(VERSION, [group])}`);
|
|
|
2922
3020
|
return;
|
|
2923
3021
|
}
|
|
2924
3022
|
const target = pickResource(all, wantedFile);
|
|
3023
|
+
const progress = createProgressReporter();
|
|
2925
3024
|
const result = await downloadResource(dependencies, target, {
|
|
2926
3025
|
// 默认落在**执行命令时的当前目录**,不是 CLI 装在哪儿。
|
|
2927
3026
|
directory: intoDirectory ?? process.cwd(),
|
|
2928
|
-
force
|
|
3027
|
+
force,
|
|
3028
|
+
...progress ? { onProgress: progress.report } : {}
|
|
2929
3029
|
});
|
|
3030
|
+
progress?.done();
|
|
2930
3031
|
console.error(
|
|
2931
3032
|
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
3033
|
);
|
|
@@ -3070,6 +3171,16 @@ ${usage(VERSION, ["skills"])}`);
|
|
|
3070
3171
|
${url}`)
|
|
3071
3172
|
);
|
|
3072
3173
|
console.log(`Logged in. Granted scopes: ${authorization.scopes.join(" ")}`);
|
|
3174
|
+
if (config.docsUrl !== void 0) {
|
|
3175
|
+
console.error(`Documentation site: ${config.docsUrl}`);
|
|
3176
|
+
} else {
|
|
3177
|
+
console.error(
|
|
3178
|
+
`\u26A0 No documentation site is paired with this PEP.
|
|
3179
|
+
docs and resources commands will use ${DEFAULT_DOCS_URL}, which checks
|
|
3180
|
+
tokens against a different PEP and will answer 401. Pass --docs-url to
|
|
3181
|
+
point them at the right one for this run.`
|
|
3182
|
+
);
|
|
3183
|
+
}
|
|
3073
3184
|
return;
|
|
3074
3185
|
}
|
|
3075
3186
|
if (args.length > 0) throw new Error(`Unexpected argument: ${args[0]}`);
|