@newlandnpt/pep-cli 0.9.0 → 0.9.2
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 +505 -71
- package/package.json +1 -1
package/dist/pep.cjs
CHANGED
|
@@ -305,12 +305,12 @@ async function waitForAuthorizationCallback(args) {
|
|
|
305
305
|
throw new Error("CLI redirect URI must be an http://localhost URL with an explicit port.");
|
|
306
306
|
}
|
|
307
307
|
let server;
|
|
308
|
-
return new Promise((
|
|
308
|
+
return new Promise((resolve5, reject) => {
|
|
309
309
|
const finish = (error, code) => {
|
|
310
310
|
clearTimeout(timeout);
|
|
311
311
|
server?.close();
|
|
312
312
|
if (error) reject(error);
|
|
313
|
-
else
|
|
313
|
+
else resolve5(code);
|
|
314
314
|
};
|
|
315
315
|
const timeout = setTimeout(
|
|
316
316
|
() => finish(new Error("Login timed out while waiting for the browser callback.")),
|
|
@@ -353,7 +353,7 @@ async function waitForAuthorizationCallback(args) {
|
|
|
353
353
|
function openBrowser(url) {
|
|
354
354
|
const command = process.platform === "win32" ? "rundll32.exe" : process.platform === "darwin" ? "open" : "xdg-open";
|
|
355
355
|
const commandArgs = process.platform === "win32" ? ["url.dll,FileProtocolHandler", url] : [url];
|
|
356
|
-
return new Promise((
|
|
356
|
+
return new Promise((resolve5, reject) => {
|
|
357
357
|
const child = (0, import_node_child_process.spawn)(command, commandArgs, {
|
|
358
358
|
detached: true,
|
|
359
359
|
stdio: "ignore",
|
|
@@ -362,7 +362,7 @@ function openBrowser(url) {
|
|
|
362
362
|
child.once("error", reject);
|
|
363
363
|
child.once("spawn", () => {
|
|
364
364
|
child.unref();
|
|
365
|
-
|
|
365
|
+
resolve5();
|
|
366
366
|
});
|
|
367
367
|
});
|
|
368
368
|
}
|
|
@@ -473,7 +473,7 @@ var REFRESH_EARLY_MS = 60 * 1e3;
|
|
|
473
473
|
var LOCK_STALE_MS = 30 * 1e3;
|
|
474
474
|
var LOCK_WAIT_MS = 10 * 1e3;
|
|
475
475
|
async function delay(milliseconds) {
|
|
476
|
-
await new Promise((
|
|
476
|
+
await new Promise((resolve5) => setTimeout(resolve5, milliseconds));
|
|
477
477
|
}
|
|
478
478
|
async function releaseIfMine(path, stamp2) {
|
|
479
479
|
try {
|
|
@@ -608,7 +608,7 @@ function createAuthService(dependencies) {
|
|
|
608
608
|
}
|
|
609
609
|
|
|
610
610
|
// src/cli.ts
|
|
611
|
-
var
|
|
611
|
+
var import_node_path6 = require("node:path");
|
|
612
612
|
|
|
613
613
|
// src/credential-store.ts
|
|
614
614
|
var import_node_child_process2 = require("node:child_process");
|
|
@@ -685,7 +685,7 @@ function encodedPowerShell(script) {
|
|
|
685
685
|
return Buffer.from(script, "utf16le").toString("base64");
|
|
686
686
|
}
|
|
687
687
|
async function runPowerShell(script, input) {
|
|
688
|
-
return new Promise((
|
|
688
|
+
return new Promise((resolve5, reject) => {
|
|
689
689
|
const child = (0, import_node_child_process2.spawn)(
|
|
690
690
|
"powershell.exe",
|
|
691
691
|
[
|
|
@@ -708,7 +708,7 @@ async function runPowerShell(script, input) {
|
|
|
708
708
|
);
|
|
709
709
|
child.once("close", (code) => {
|
|
710
710
|
clearTimeout(timeout);
|
|
711
|
-
if (code === 0)
|
|
711
|
+
if (code === 0) resolve5(stdout.trim());
|
|
712
712
|
else
|
|
713
713
|
reject(
|
|
714
714
|
new Error(
|
|
@@ -762,7 +762,7 @@ $raw = [Console]::In.ReadToEnd()
|
|
|
762
762
|
}
|
|
763
763
|
var SECURITY_NOT_FOUND = 44;
|
|
764
764
|
async function runSecurity(args) {
|
|
765
|
-
return new Promise((
|
|
765
|
+
return new Promise((resolve5, reject) => {
|
|
766
766
|
const child = (0, import_node_child_process2.spawn)("/usr/bin/security", [...args], {
|
|
767
767
|
stdio: ["ignore", "pipe", "pipe"]
|
|
768
768
|
});
|
|
@@ -777,7 +777,7 @@ async function runSecurity(args) {
|
|
|
777
777
|
);
|
|
778
778
|
child.once("close", (code) => {
|
|
779
779
|
clearTimeout(timeout);
|
|
780
|
-
|
|
780
|
+
resolve5({ code: code ?? -1, stdout, stderr });
|
|
781
781
|
});
|
|
782
782
|
});
|
|
783
783
|
}
|
|
@@ -874,6 +874,8 @@ async function fetchOrExplain(doFetch, url, init, what, timeoutMs = HEADERS_TIME
|
|
|
874
874
|
|
|
875
875
|
// src/docs-service.ts
|
|
876
876
|
var ENTRY = /^-\s*\[([^\]]*)\]\(([^)]+)\)(?::\s*(.*))?$/;
|
|
877
|
+
var INDEX_PATH = "/llms.txt";
|
|
878
|
+
var REGIONS_PATH = "/api/docs/regions";
|
|
877
879
|
function parseDocsIndex(text) {
|
|
878
880
|
const entries = [];
|
|
879
881
|
for (const line of text.split("\n")) {
|
|
@@ -900,6 +902,50 @@ function contentPathFor(path) {
|
|
|
900
902
|
const after = segments.slice(docsAt);
|
|
901
903
|
return `/${[...before, "llms.mdx", ...after, "content.md"].join("/")}`;
|
|
902
904
|
}
|
|
905
|
+
async function fetchRegions(dependencies) {
|
|
906
|
+
const doFetch = dependencies.fetch ?? globalThis.fetch;
|
|
907
|
+
const response = await fetchOrExplain(
|
|
908
|
+
doFetch,
|
|
909
|
+
`${dependencies.docsUrl.replace(/\/+$/, "")}${REGIONS_PATH}`,
|
|
910
|
+
{ headers: { Accept: "application/json" } },
|
|
911
|
+
"Could not reach the documentation platform"
|
|
912
|
+
);
|
|
913
|
+
if (!response.ok) {
|
|
914
|
+
await response.body?.cancel();
|
|
915
|
+
if (response.status === 404) {
|
|
916
|
+
throw new Error(
|
|
917
|
+
"This documentation deployment has no /api/docs/regions endpoint \u2014 it predates region support. Ask an operator to deploy a newer build."
|
|
918
|
+
);
|
|
919
|
+
}
|
|
920
|
+
throw new Error(describeFailure(response.status, REGIONS_PATH));
|
|
921
|
+
}
|
|
922
|
+
const body = await response.json();
|
|
923
|
+
const regions = body?.regions;
|
|
924
|
+
if (!Array.isArray(regions) || regions.length === 0) {
|
|
925
|
+
throw new Error("The documentation platform returned no regions.");
|
|
926
|
+
}
|
|
927
|
+
return body;
|
|
928
|
+
}
|
|
929
|
+
function resolveRegion(known, ref) {
|
|
930
|
+
const wanted = ref.trim().toLowerCase();
|
|
931
|
+
if (!wanted) return void 0;
|
|
932
|
+
return known.regions.find(
|
|
933
|
+
(region) => region.id.toLowerCase() === wanted || (region.aliases ?? []).some((alias) => alias.toLowerCase() === wanted)
|
|
934
|
+
);
|
|
935
|
+
}
|
|
936
|
+
function unknownRegion(known, ref) {
|
|
937
|
+
const names = known.regions.map((one) => one.aliases?.length ? `${one.id} (${one.aliases.join(", ")})` : one.id).join(", ");
|
|
938
|
+
return new Error(`No such documentation region: ${ref}. Available: ${names}.`);
|
|
939
|
+
}
|
|
940
|
+
function withRegion(path, region) {
|
|
941
|
+
const clean = `/${path.replace(/^\/+/, "").replace(/\/+$/, "")}`;
|
|
942
|
+
const segments = clean.split("/").filter(Boolean);
|
|
943
|
+
const marker = segments.findIndex((one) => one === "llms.mdx" || one === "docs");
|
|
944
|
+
if (marker === -1) {
|
|
945
|
+
throw new Error(`Not a documentation path (expected \u2026/docs/\u2026): ${path}`);
|
|
946
|
+
}
|
|
947
|
+
return `/${[region.id, region.defaultLanguage, ...segments.slice(marker)].join("/")}`;
|
|
948
|
+
}
|
|
903
949
|
function describeFailure(status, what) {
|
|
904
950
|
if (status === 401) {
|
|
905
951
|
return `The documentation platform rejected the access token. Run \`pep auth login\` again.`;
|
|
@@ -910,12 +956,15 @@ function describeFailure(status, what) {
|
|
|
910
956
|
if (status === 404) {
|
|
911
957
|
return `No such document, or your account cannot read it: ${what}`;
|
|
912
958
|
}
|
|
959
|
+
if (status === 400) {
|
|
960
|
+
return `The documentation platform rejected that request: ${what}`;
|
|
961
|
+
}
|
|
913
962
|
if (status === 502) {
|
|
914
963
|
return "The documentation platform could not reach PEP to verify the token. Retry shortly.";
|
|
915
964
|
}
|
|
916
965
|
return `The documentation platform answered ${status} for ${what}.`;
|
|
917
966
|
}
|
|
918
|
-
async function get(dependencies, path, accept) {
|
|
967
|
+
async function get(dependencies, path, accept, explain) {
|
|
919
968
|
const doFetch = dependencies.fetch ?? globalThis.fetch;
|
|
920
969
|
const response = await fetchOrExplain(
|
|
921
970
|
doFetch,
|
|
@@ -924,13 +973,22 @@ async function get(dependencies, path, accept) {
|
|
|
924
973
|
"Could not reach the documentation platform"
|
|
925
974
|
);
|
|
926
975
|
if (!response.ok) {
|
|
927
|
-
await response
|
|
928
|
-
|
|
976
|
+
const custom = explain ? await explain(response) : void 0;
|
|
977
|
+
if (!response.bodyUsed) await response.body?.cancel().catch(() => void 0);
|
|
978
|
+
throw new Error(custom ?? describeFailure(response.status, path));
|
|
929
979
|
}
|
|
930
980
|
return response;
|
|
931
981
|
}
|
|
932
|
-
async function fetchDocsIndex(dependencies) {
|
|
933
|
-
const
|
|
982
|
+
async function fetchDocsIndex(dependencies, region) {
|
|
983
|
+
const path = region === void 0 ? INDEX_PATH : `${INDEX_PATH}?site=${encodeURIComponent(region)}`;
|
|
984
|
+
const response = await get(dependencies, path, "text/plain", async (bad) => {
|
|
985
|
+
if (bad.status !== 400) return void 0;
|
|
986
|
+
const available = await bad.json().then((body) => body?.available).catch(() => void 0);
|
|
987
|
+
if (!Array.isArray(available) || available.length === 0) {
|
|
988
|
+
return `No such documentation region: ${region}.`;
|
|
989
|
+
}
|
|
990
|
+
return `No such documentation region: ${region}. Available: ${available.join(", ")}.`;
|
|
991
|
+
});
|
|
934
992
|
return parseDocsIndex(await response.text());
|
|
935
993
|
}
|
|
936
994
|
async function fetchDocContent(dependencies, path) {
|
|
@@ -938,10 +996,208 @@ async function fetchDocContent(dependencies, path) {
|
|
|
938
996
|
return response.text();
|
|
939
997
|
}
|
|
940
998
|
|
|
941
|
-
// src/
|
|
999
|
+
// src/docs-resources.ts
|
|
1000
|
+
var import_node_crypto3 = require("node:crypto");
|
|
1001
|
+
var import_node_fs = require("node:fs");
|
|
942
1002
|
var import_promises3 = require("node:fs/promises");
|
|
943
|
-
var import_node_os2 = require("node:os");
|
|
944
1003
|
var import_node_path3 = require("node:path");
|
|
1004
|
+
var import_node_stream = require("node:stream");
|
|
1005
|
+
var import_promises4 = require("node:stream/promises");
|
|
1006
|
+
var RESOURCES_SLUG = "docs/Terminal/References/Developer-Resources";
|
|
1007
|
+
var PLATFORMS = ["Android", "Windows", "iOS"];
|
|
1008
|
+
function resolvePlatform(ref) {
|
|
1009
|
+
const wanted = ref.trim().toLowerCase();
|
|
1010
|
+
return PLATFORMS.find((one) => one.toLowerCase() === wanted);
|
|
1011
|
+
}
|
|
1012
|
+
function extractResourceLinks(markdown, platform3) {
|
|
1013
|
+
const text = markdown.replace(/!\[[^\]]*\]\([^)]*\)/g, "").replace(/<img\b[^>]*>/gi, "");
|
|
1014
|
+
const found = [];
|
|
1015
|
+
for (const match of text.matchAll(/\[[^\]]*\]\(([^)\s]+)/g)) {
|
|
1016
|
+
const url = match[1];
|
|
1017
|
+
if (url.startsWith("#")) continue;
|
|
1018
|
+
const external = /^https?:\/\//i.test(url);
|
|
1019
|
+
const pathPart = external ? safePath(url) : url;
|
|
1020
|
+
if (pathPart === void 0) continue;
|
|
1021
|
+
if (!external && !pathPart.startsWith("/assets/")) continue;
|
|
1022
|
+
const inSitePool = pathPart.startsWith("/assets/");
|
|
1023
|
+
const file = decodeURIComponent((0, import_node_path3.basename)(pathPart));
|
|
1024
|
+
if (!file) continue;
|
|
1025
|
+
found.push({ file, url, platforms: [platform3], external: external && !inSitePool });
|
|
1026
|
+
}
|
|
1027
|
+
return found;
|
|
1028
|
+
}
|
|
1029
|
+
function safePath(url) {
|
|
1030
|
+
try {
|
|
1031
|
+
return new URL(url).pathname;
|
|
1032
|
+
} catch {
|
|
1033
|
+
return void 0;
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1036
|
+
function mergeResources(all) {
|
|
1037
|
+
const byUrl = /* @__PURE__ */ new Map();
|
|
1038
|
+
for (const one of all) {
|
|
1039
|
+
const seen = byUrl.get(one.url);
|
|
1040
|
+
if (!seen) {
|
|
1041
|
+
byUrl.set(one.url, { ...one, platforms: [...one.platforms] });
|
|
1042
|
+
continue;
|
|
1043
|
+
}
|
|
1044
|
+
for (const p of one.platforms) if (!seen.platforms.includes(p)) seen.platforms.push(p);
|
|
1045
|
+
}
|
|
1046
|
+
return [...byUrl.values()].sort((a, b) => a.file.localeCompare(b.file));
|
|
1047
|
+
}
|
|
1048
|
+
async function fetchResources(dependencies, platform3) {
|
|
1049
|
+
const page = withRegion(`/${RESOURCES_SLUG}`, dependencies.region);
|
|
1050
|
+
const wanted = platform3 ? [platform3] : [...PLATFORMS];
|
|
1051
|
+
const all = [];
|
|
1052
|
+
for (const one of wanted) {
|
|
1053
|
+
const markdown = await fetchTab(dependencies, page, one);
|
|
1054
|
+
all.push(...extractResourceLinks(markdown, one));
|
|
1055
|
+
}
|
|
1056
|
+
return mergeResources(all);
|
|
1057
|
+
}
|
|
1058
|
+
async function fetchTab(dependencies, page, platform3) {
|
|
1059
|
+
const doFetch = dependencies.fetch ?? globalThis.fetch;
|
|
1060
|
+
const url = new URL(
|
|
1061
|
+
`${dependencies.docsUrl.replace(/\/+$/, "")}${contentPathFor(page)}`
|
|
1062
|
+
);
|
|
1063
|
+
url.searchParams.set("lang", platform3);
|
|
1064
|
+
const response = await fetchOrExplain(
|
|
1065
|
+
doFetch,
|
|
1066
|
+
url,
|
|
1067
|
+
{
|
|
1068
|
+
headers: {
|
|
1069
|
+
Authorization: `Bearer ${dependencies.accessToken}`,
|
|
1070
|
+
Accept: "text/markdown"
|
|
1071
|
+
}
|
|
1072
|
+
},
|
|
1073
|
+
"Could not read the developer resources page"
|
|
1074
|
+
);
|
|
1075
|
+
if (!response.ok) {
|
|
1076
|
+
await response.body?.cancel().catch(() => void 0);
|
|
1077
|
+
if (response.status === 404) {
|
|
1078
|
+
throw new Error(
|
|
1079
|
+
`The developer resources page is not available to this account (or this region has no such page): ${page}`
|
|
1080
|
+
);
|
|
1081
|
+
}
|
|
1082
|
+
throw new Error(`The documentation platform answered ${response.status} for ${page}.`);
|
|
1083
|
+
}
|
|
1084
|
+
return response.text();
|
|
1085
|
+
}
|
|
1086
|
+
function pickResource(all, file) {
|
|
1087
|
+
const hits = all.filter((one) => one.file === file);
|
|
1088
|
+
if (hits.length === 0) {
|
|
1089
|
+
throw new Error(
|
|
1090
|
+
`No such file on the developer resources page: ${file}. Run \`pep docs resources\` to see what is there.`
|
|
1091
|
+
);
|
|
1092
|
+
}
|
|
1093
|
+
if (hits.length > 1) {
|
|
1094
|
+
const urls = hits.map((one) => ` ${one.url}`).join("\n");
|
|
1095
|
+
throw new Error(
|
|
1096
|
+
`${file} appears more than once with different addresses:
|
|
1097
|
+
${urls}
|
|
1098
|
+
The page is ambiguous \u2014 ask an operator which one is current.`
|
|
1099
|
+
);
|
|
1100
|
+
}
|
|
1101
|
+
const only = hits[0];
|
|
1102
|
+
if (only.external) {
|
|
1103
|
+
throw new Error(
|
|
1104
|
+
`${file} is hosted outside the documentation platform (${only.url}). Open that page in a browser \u2014 pep does not send your PEP token to third parties.`
|
|
1105
|
+
);
|
|
1106
|
+
}
|
|
1107
|
+
return only;
|
|
1108
|
+
}
|
|
1109
|
+
function expectedBlobSha(url) {
|
|
1110
|
+
return /\/assets\/_shared\/([0-9a-f]{12})\//.exec(url)?.[1];
|
|
1111
|
+
}
|
|
1112
|
+
async function blobShaOfFile(path) {
|
|
1113
|
+
const size = (await (0, import_promises3.stat)(path)).size;
|
|
1114
|
+
const digest = (0, import_node_crypto3.createHash)("sha1").update(`blob ${size}\0`);
|
|
1115
|
+
await (0, import_promises4.pipeline)((0, import_node_fs.createReadStream)(path), async function* (source) {
|
|
1116
|
+
for await (const chunk of source) digest.update(chunk);
|
|
1117
|
+
});
|
|
1118
|
+
return digest.digest("hex");
|
|
1119
|
+
}
|
|
1120
|
+
async function downloadResource(dependencies, resource, options) {
|
|
1121
|
+
const file = (0, import_node_path3.basename)(resource.file);
|
|
1122
|
+
if (file !== resource.file || file === "." || file === "..") {
|
|
1123
|
+
throw new Error(`Refusing to write a file named ${resource.file}.`);
|
|
1124
|
+
}
|
|
1125
|
+
const directory = (0, import_node_path3.resolve)(options.directory);
|
|
1126
|
+
const target = (0, import_node_path3.resolve)(directory, file);
|
|
1127
|
+
if (!target.startsWith(directory + "/") && !target.startsWith(directory + "\\")) {
|
|
1128
|
+
throw new Error(`Refusing to write outside ${directory}.`);
|
|
1129
|
+
}
|
|
1130
|
+
if (!options.force && await (0, import_promises3.stat)(target).catch(() => null)) {
|
|
1131
|
+
throw new Error(
|
|
1132
|
+
`${target} already exists. Pass --force to replace it (the existing file is kept until the new one has downloaded completely).`
|
|
1133
|
+
);
|
|
1134
|
+
}
|
|
1135
|
+
const doFetch = dependencies.fetch ?? globalThis.fetch;
|
|
1136
|
+
const absolute = resource.url.startsWith("/") ? `${dependencies.docsUrl.replace(/\/+$/, "")}${resource.url}` : resource.url;
|
|
1137
|
+
const response = await fetchOrExplain(
|
|
1138
|
+
doFetch,
|
|
1139
|
+
absolute,
|
|
1140
|
+
{ headers: { Authorization: `Bearer ${dependencies.accessToken}` } },
|
|
1141
|
+
`Could not download ${file}`
|
|
1142
|
+
);
|
|
1143
|
+
if (!response.ok || !response.body) {
|
|
1144
|
+
await response.body?.cancel().catch(() => void 0);
|
|
1145
|
+
throw new Error(describeDownloadFailure(response.status, file));
|
|
1146
|
+
}
|
|
1147
|
+
const declared = Number(response.headers.get("content-length") ?? "");
|
|
1148
|
+
const expected = expectedBlobSha(resource.url);
|
|
1149
|
+
await (0, import_promises3.mkdir)(directory, { recursive: true });
|
|
1150
|
+
const temporary = `${target}.${process.pid}.part`;
|
|
1151
|
+
try {
|
|
1152
|
+
await (0, import_promises4.pipeline)(
|
|
1153
|
+
import_node_stream.Readable.fromWeb(response.body),
|
|
1154
|
+
(0, import_node_fs.createWriteStream)(temporary)
|
|
1155
|
+
);
|
|
1156
|
+
const bytes = (await (0, import_promises3.stat)(temporary)).size;
|
|
1157
|
+
if (Number.isFinite(declared) && declared > 0 && bytes !== declared) {
|
|
1158
|
+
throw new Error(
|
|
1159
|
+
`${file} stopped after ${bytes} of ${declared} bytes. Nothing was written \u2014 run this again.`
|
|
1160
|
+
);
|
|
1161
|
+
}
|
|
1162
|
+
let verified = "none";
|
|
1163
|
+
if (expected !== void 0) {
|
|
1164
|
+
const actual = (await blobShaOfFile(temporary)).slice(0, 12);
|
|
1165
|
+
if (actual !== expected) {
|
|
1166
|
+
throw new Error(
|
|
1167
|
+
`${file} does not match the content hash in its address (expected ${expected}, got ${actual}). Nothing was written.`
|
|
1168
|
+
);
|
|
1169
|
+
}
|
|
1170
|
+
verified = "blob-sha";
|
|
1171
|
+
} else if (Number.isFinite(declared) && declared > 0) {
|
|
1172
|
+
verified = "content-length";
|
|
1173
|
+
}
|
|
1174
|
+
await (0, import_promises3.rename)(temporary, target);
|
|
1175
|
+
return { path: target, bytes, verified };
|
|
1176
|
+
} catch (error) {
|
|
1177
|
+
await (0, import_promises3.unlink)(temporary).catch(() => void 0);
|
|
1178
|
+
throw error;
|
|
1179
|
+
}
|
|
1180
|
+
}
|
|
1181
|
+
function describeDownloadFailure(status, file) {
|
|
1182
|
+
if (status === 401) {
|
|
1183
|
+
return "The documentation platform rejected the access token. Run `pep auth login` again.";
|
|
1184
|
+
}
|
|
1185
|
+
if (status === 403) {
|
|
1186
|
+
return "This client is not allowed to read documentation \u2014 it needs the `docs:read` scope.";
|
|
1187
|
+
}
|
|
1188
|
+
if (status === 404) {
|
|
1189
|
+
return `${file} is no longer on the documentation platform. Run \`pep docs resources\` for the current list.`;
|
|
1190
|
+
}
|
|
1191
|
+
if (status === 502) {
|
|
1192
|
+
return "The documentation platform could not reach PEP to verify the token. Retry shortly.";
|
|
1193
|
+
}
|
|
1194
|
+
return `The documentation platform answered ${status} for ${file}.`;
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1197
|
+
// src/maven-env.ts
|
|
1198
|
+
var import_promises5 = require("node:fs/promises");
|
|
1199
|
+
var import_node_os2 = require("node:os");
|
|
1200
|
+
var import_node_path4 = require("node:path");
|
|
945
1201
|
var import_node_child_process3 = require("node:child_process");
|
|
946
1202
|
var import_node_util = require("node:util");
|
|
947
1203
|
var run = (0, import_node_util.promisify)(import_node_child_process3.execFile);
|
|
@@ -952,7 +1208,7 @@ var END = "# <<< pep-cli: Newland Maven credentials <<<";
|
|
|
952
1208
|
function resolveTarget(os = (0, import_node_os2.platform)(), shell = process.env.SHELL) {
|
|
953
1209
|
if (os === "win32") return { kind: "windows" };
|
|
954
1210
|
const file = shell?.endsWith("/bash") ? ".bash_profile" : ".zshrc";
|
|
955
|
-
return { kind: "profile", path: (0,
|
|
1211
|
+
return { kind: "profile", path: (0, import_node_path4.join)((0, import_node_os2.homedir)(), file) };
|
|
956
1212
|
}
|
|
957
1213
|
function shellQuote(value) {
|
|
958
1214
|
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
@@ -1020,7 +1276,7 @@ async function persistMavenEnv(credential, target = resolveTarget(), now = () =>
|
|
|
1020
1276
|
}
|
|
1021
1277
|
return { status: "windows" };
|
|
1022
1278
|
}
|
|
1023
|
-
const existing = await (0,
|
|
1279
|
+
const existing = await (0, import_promises5.readFile)(target.path, "utf8").catch((error) => {
|
|
1024
1280
|
if (error.code === "ENOENT") return null;
|
|
1025
1281
|
throw error;
|
|
1026
1282
|
});
|
|
@@ -1032,7 +1288,7 @@ async function persistMavenEnv(credential, target = resolveTarget(), now = () =>
|
|
|
1032
1288
|
if (existing !== null) {
|
|
1033
1289
|
backupPath = `${target.path}.bak-${stamp(now())}`;
|
|
1034
1290
|
try {
|
|
1035
|
-
await (0,
|
|
1291
|
+
await (0, import_promises5.writeFile)(backupPath, existing, { encoding: "utf8", flag: "wx" });
|
|
1036
1292
|
} catch (error) {
|
|
1037
1293
|
return {
|
|
1038
1294
|
status: "manual",
|
|
@@ -1041,10 +1297,10 @@ async function persistMavenEnv(credential, target = resolveTarget(), now = () =>
|
|
|
1041
1297
|
};
|
|
1042
1298
|
}
|
|
1043
1299
|
}
|
|
1044
|
-
const originalMode = await (0,
|
|
1300
|
+
const originalMode = await (0, import_promises5.stat)(target.path).then((info) => info.mode & 511).catch(() => 384);
|
|
1045
1301
|
const temporary = `${target.path}.${process.pid}.tmp`;
|
|
1046
|
-
await (0,
|
|
1047
|
-
await (0,
|
|
1302
|
+
await (0, import_promises5.writeFile)(temporary, plan.content, { encoding: "utf8", mode: originalMode });
|
|
1303
|
+
await (0, import_promises5.rename)(temporary, target.path);
|
|
1048
1304
|
return plan.action === "replace" ? { status: "profile-replaced", path: target.path, backupPath } : { status: "profile-appended", path: target.path, ...backupPath ? { backupPath } : {} };
|
|
1049
1305
|
}
|
|
1050
1306
|
|
|
@@ -1052,6 +1308,7 @@ async function persistMavenEnv(credential, target = resolveTarget(), now = () =>
|
|
|
1052
1308
|
var EXAMPLE_PATH = EXAMPLE_SKILLS_REPO;
|
|
1053
1309
|
var EXAMPLE_REPO = `https://git.newlandpayment.com/${EXAMPLE_PATH}.git`;
|
|
1054
1310
|
var PROJECT_FLAG = "-p, --project Install into ./.agents/skills of the current directory instead";
|
|
1311
|
+
var REGION_FLAG = "--region <id> Documentation region for this run only (see pep docs regions)";
|
|
1055
1312
|
var groups = {
|
|
1056
1313
|
auth: {
|
|
1057
1314
|
summary: "Sign in to PEP and manage your local authorization.",
|
|
@@ -1176,16 +1433,72 @@ pep skills update will not bring the skills back. Run pep skills add to do that.
|
|
|
1176
1433
|
list: {
|
|
1177
1434
|
summary: "List the documents your account can read.",
|
|
1178
1435
|
usage: "pep docs list [flags]",
|
|
1179
|
-
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.",
|
|
1180
|
-
flags: [
|
|
1181
|
-
|
|
1436
|
+
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
|
+
flags: [
|
|
1438
|
+
`--docs-url <url> Documentation server; saved for later use (default: ${DEFAULT_DOCS_URL})`,
|
|
1439
|
+
REGION_FLAG
|
|
1440
|
+
],
|
|
1441
|
+
examples: [
|
|
1442
|
+
"pep docs list",
|
|
1443
|
+
"pep docs list --region na",
|
|
1444
|
+
"pep docs list --docs-url http://localhost:3001"
|
|
1445
|
+
]
|
|
1182
1446
|
},
|
|
1183
1447
|
get: {
|
|
1184
1448
|
summary: "Read a document as Markdown.",
|
|
1185
1449
|
usage: "pep docs get <path> [flags]",
|
|
1186
|
-
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.",
|
|
1450
|
+
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
|
+
flags: [
|
|
1452
|
+
`--docs-url <url> Documentation server; saved for later use (default: ${DEFAULT_DOCS_URL})`,
|
|
1453
|
+
REGION_FLAG
|
|
1454
|
+
],
|
|
1455
|
+
examples: [
|
|
1456
|
+
'pep docs get "<path-from-pep-docs-list>"',
|
|
1457
|
+
'pep docs get "<path-from-pep-docs-list>" --region na',
|
|
1458
|
+
'pep docs get "<path-from-pep-docs-list>" > document.md'
|
|
1459
|
+
]
|
|
1460
|
+
},
|
|
1461
|
+
regions: {
|
|
1462
|
+
summary: "List the documentation regions this deployment serves.",
|
|
1463
|
+
usage: "pep docs regions [flags]",
|
|
1464
|
+
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.',
|
|
1187
1465
|
flags: [`--docs-url <url> Documentation server; saved for later use (default: ${DEFAULT_DOCS_URL})`],
|
|
1188
|
-
examples: [
|
|
1466
|
+
examples: ["pep docs regions"]
|
|
1467
|
+
}
|
|
1468
|
+
}
|
|
1469
|
+
},
|
|
1470
|
+
resources: {
|
|
1471
|
+
summary: "List and download SDKs, demos and tools.",
|
|
1472
|
+
commands: {
|
|
1473
|
+
list: {
|
|
1474
|
+
summary: "List the SDKs, demos and tools you can download.",
|
|
1475
|
+
usage: "pep resources list [flags]",
|
|
1476
|
+
description: 'Requires a saved PEP login. Prints one file per line: name, platforms, address.\nThe list is read from the documentation page itself, so a new release shows up as\nsoon as that page is updated \u2014 the CLI keeps no version list of its own.\n\nWithout --platform it reads all three platform tabs and merges them; a file that\nappears in several is listed once with every platform named. Entries marked\n"external" are hosted elsewhere and cannot be fetched with pep resources download.',
|
|
1477
|
+
flags: [
|
|
1478
|
+
`--platform <name> Only this platform (${PLATFORMS.join(", ")})`,
|
|
1479
|
+
REGION_FLAG,
|
|
1480
|
+
`--docs-url <url> Documentation server; saved for later use (default: ${DEFAULT_DOCS_URL})`
|
|
1481
|
+
],
|
|
1482
|
+
examples: [
|
|
1483
|
+
"pep resources list",
|
|
1484
|
+
"pep resources list --platform windows",
|
|
1485
|
+
"pep resources list --region na --platform android"
|
|
1486
|
+
]
|
|
1487
|
+
},
|
|
1488
|
+
download: {
|
|
1489
|
+
summary: "Download a file from the developer resources page.",
|
|
1490
|
+
usage: "pep resources download <file> [flags]",
|
|
1491
|
+
description: "Requires a saved PEP login. Use a file name exactly as pep resources list prints\nit. Saves into the current directory unless --dir says otherwise, and prints the\nfull path of the finished file to stdout.\n\nThe download goes to a temporary file beside the target and is renamed only once\nit is complete and verified, so an interrupted run never leaves a half file \u2014 and\nwith --force your existing copy survives until the new one has arrived.\n\nWhere the address carries a content hash the downloaded bytes are checked against\nit; otherwise the declared length is checked. Which one happened is reported on\nstderr.",
|
|
1492
|
+
flags: [
|
|
1493
|
+
"--dir <path> Directory to save into; created if missing (default: the current directory)",
|
|
1494
|
+
"--force Replace an existing file of the same name",
|
|
1495
|
+
REGION_FLAG,
|
|
1496
|
+
`--docs-url <url> Documentation server; saved for later use (default: ${DEFAULT_DOCS_URL})`
|
|
1497
|
+
],
|
|
1498
|
+
examples: [
|
|
1499
|
+
"pep resources download SaleSystemDemo-4.8.0.2-win-x64.zip",
|
|
1500
|
+
"pep resources download PayExplorerECRDemo.apk --region na --dir ./downloads"
|
|
1501
|
+
]
|
|
1189
1502
|
}
|
|
1190
1503
|
}
|
|
1191
1504
|
},
|
|
@@ -1299,6 +1612,7 @@ ${flagBlock([...command.flags ?? [], "-h, --help Show help for this command"])}
|
|
|
1299
1612
|
EXAMPLES
|
|
1300
1613
|
${command.examples.map((example) => ` ${example}`).join("\n")}`;
|
|
1301
1614
|
}
|
|
1615
|
+
var GROUP_NAMES = Object.keys(groups);
|
|
1302
1616
|
function usage(version, path = []) {
|
|
1303
1617
|
const [groupName, commandName] = path;
|
|
1304
1618
|
if (!groupName) {
|
|
@@ -1309,8 +1623,8 @@ USAGE
|
|
|
1309
1623
|
pep <command> <subcommand> [flags]
|
|
1310
1624
|
|
|
1311
1625
|
COMMANDS
|
|
1312
|
-
${Object.entries(topLevel).map(([name, command]) => ` ${name.padEnd(
|
|
1313
|
-
${Object.entries(groups).map(([name, group2]) => ` ${name.padEnd(
|
|
1626
|
+
${Object.entries(topLevel).map(([name, command]) => ` ${name.padEnd(11)}${command.summary}`).join("\n")}
|
|
1627
|
+
${Object.entries(groups).map(([name, group2]) => ` ${name.padEnd(11)}${group2.summary}`).join("\n")}
|
|
1314
1628
|
|
|
1315
1629
|
FLAGS
|
|
1316
1630
|
-h, --help Show help for a command
|
|
@@ -1319,6 +1633,7 @@ FLAGS
|
|
|
1319
1633
|
EXAMPLES
|
|
1320
1634
|
pep setup
|
|
1321
1635
|
pep docs list
|
|
1636
|
+
pep resources list
|
|
1322
1637
|
pep nexus setup --help
|
|
1323
1638
|
|
|
1324
1639
|
LEARN MORE
|
|
@@ -1341,7 +1656,7 @@ USAGE
|
|
|
1341
1656
|
pep ${groupName} <command> [flags]
|
|
1342
1657
|
|
|
1343
1658
|
COMMANDS
|
|
1344
|
-
${Object.entries(group.commands).map(([name, command]) => ` ${name.padEnd(
|
|
1659
|
+
${Object.entries(group.commands).map(([name, command]) => ` ${name.padEnd(11)}${command.summary}`).join("\n")}
|
|
1345
1660
|
|
|
1346
1661
|
FLAGS
|
|
1347
1662
|
-h, --help Show help for this command
|
|
@@ -1430,10 +1745,10 @@ async function setupNexusCredential(dependencies) {
|
|
|
1430
1745
|
}
|
|
1431
1746
|
|
|
1432
1747
|
// src/skills-service.ts
|
|
1433
|
-
var
|
|
1748
|
+
var import_promises6 = require("node:fs/promises");
|
|
1434
1749
|
var import_node_os3 = require("node:os");
|
|
1435
|
-
var
|
|
1436
|
-
var
|
|
1750
|
+
var import_node_path5 = require("node:path");
|
|
1751
|
+
var import_node_crypto4 = require("node:crypto");
|
|
1437
1752
|
var import_node_zlib = require("node:zlib");
|
|
1438
1753
|
|
|
1439
1754
|
// src/tar.ts
|
|
@@ -1524,6 +1839,11 @@ function tooBig(what, limit) {
|
|
|
1524
1839
|
);
|
|
1525
1840
|
}
|
|
1526
1841
|
var SKILL_MANIFEST = "SKILL.md";
|
|
1842
|
+
function occupiedByOutsider(skill, path) {
|
|
1843
|
+
return new Error(
|
|
1844
|
+
`${path} already exists and pep did not install it. Installing "${skill}" would delete it, so this repository was left alone. Move that directory aside (or delete it yourself), then run this again \u2014 pep never overwrites what it does not track.`
|
|
1845
|
+
);
|
|
1846
|
+
}
|
|
1527
1847
|
function nameClash(owner, skill, directory) {
|
|
1528
1848
|
return new Error(
|
|
1529
1849
|
`${owner} already installs a skill named "${skill}" in ${directory}. Two repositories cannot both provide "${skill}" there \u2014 the second would silently overwrite the first. Install this one elsewhere (-p, or --dir <path>), or run \`pep skills remove ${owner}\` first.`
|
|
@@ -1587,8 +1907,8 @@ async function readCapped(response, limit) {
|
|
|
1587
1907
|
return out;
|
|
1588
1908
|
}
|
|
1589
1909
|
function assertInside(base, target) {
|
|
1590
|
-
const rel = (0,
|
|
1591
|
-
if (rel === "" || rel.startsWith("..") || (0,
|
|
1910
|
+
const rel = (0, import_node_path5.relative)((0, import_node_path5.resolve)(base), (0, import_node_path5.resolve)(target));
|
|
1911
|
+
if (rel === "" || rel.startsWith("..") || (0, import_node_path5.isAbsolute)(rel)) {
|
|
1592
1912
|
throw new Error(`Refusing an archive entry that escapes its directory: ${target}`);
|
|
1593
1913
|
}
|
|
1594
1914
|
}
|
|
@@ -1691,32 +2011,32 @@ function groupMemberPath(ledgerKey, group) {
|
|
|
1691
2011
|
return path;
|
|
1692
2012
|
}
|
|
1693
2013
|
async function physicalDir(path) {
|
|
1694
|
-
const absolute = (0,
|
|
2014
|
+
const absolute = (0, import_node_path5.resolve)(path);
|
|
1695
2015
|
try {
|
|
1696
|
-
return (0,
|
|
2016
|
+
return (0, import_node_path5.join)(await (0, import_promises6.realpath)((0, import_node_path5.dirname)(absolute)), (0, import_node_path5.basename)(absolute));
|
|
1697
2017
|
} catch {
|
|
1698
2018
|
return absolute;
|
|
1699
2019
|
}
|
|
1700
2020
|
}
|
|
1701
2021
|
async function linkSkill(canonicalSkillDir, linkPath) {
|
|
1702
|
-
await (0,
|
|
1703
|
-
const linkDir = (0,
|
|
1704
|
-
await (0,
|
|
2022
|
+
await (0, import_promises6.rm)(linkPath, { recursive: true, force: true });
|
|
2023
|
+
const linkDir = (0, import_node_path5.dirname)(linkPath);
|
|
2024
|
+
await (0, import_promises6.mkdir)(linkDir, { recursive: true });
|
|
1705
2025
|
try {
|
|
1706
2026
|
const junction = (0, import_node_os3.platform)() === "win32";
|
|
1707
|
-
await (0,
|
|
1708
|
-
junction ? canonicalSkillDir : (0,
|
|
2027
|
+
await (0, import_promises6.symlink)(
|
|
2028
|
+
junction ? canonicalSkillDir : (0, import_node_path5.relative)(await physicalDir(linkDir), canonicalSkillDir),
|
|
1709
2029
|
linkPath,
|
|
1710
2030
|
junction ? "junction" : void 0
|
|
1711
2031
|
);
|
|
1712
2032
|
return "linked";
|
|
1713
2033
|
} catch {
|
|
1714
|
-
await (0,
|
|
2034
|
+
await (0, import_promises6.cp)(canonicalSkillDir, linkPath, { recursive: true });
|
|
1715
2035
|
return "copied";
|
|
1716
2036
|
}
|
|
1717
2037
|
}
|
|
1718
2038
|
function hashSkill(files) {
|
|
1719
|
-
const hash = (0,
|
|
2039
|
+
const hash = (0, import_node_crypto4.createHash)("sha256");
|
|
1720
2040
|
for (const file of [...files].sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0)) {
|
|
1721
2041
|
hash.update(file.path);
|
|
1722
2042
|
hash.update("\0");
|
|
@@ -1753,9 +2073,9 @@ async function removeSkills(name, store) {
|
|
|
1753
2073
|
keptForOthers.push({ skill, owner });
|
|
1754
2074
|
continue;
|
|
1755
2075
|
}
|
|
1756
|
-
await (0,
|
|
2076
|
+
await (0, import_promises6.rm)((0, import_node_path5.join)(pkg.directory, skill), { recursive: true, force: true });
|
|
1757
2077
|
if (pkg.linkedInto !== void 0) {
|
|
1758
|
-
await (0,
|
|
2078
|
+
await (0, import_promises6.rm)((0, import_node_path5.join)(pkg.linkedInto, skill), { recursive: true, force: true });
|
|
1759
2079
|
}
|
|
1760
2080
|
deleted.push(skill);
|
|
1761
2081
|
}
|
|
@@ -1768,10 +2088,19 @@ function pickSkipped(previous, skipped) {
|
|
|
1768
2088
|
skipped.map((skill) => [skill, previous?.skills[skill] ?? ""])
|
|
1769
2089
|
);
|
|
1770
2090
|
}
|
|
2091
|
+
async function outsidersAt(directory, skills, ours) {
|
|
2092
|
+
const blocked = [];
|
|
2093
|
+
for (const skill of skills) {
|
|
2094
|
+
if (ours.has(skill)) continue;
|
|
2095
|
+
const path = (0, import_node_path5.join)(directory, skill);
|
|
2096
|
+
if (await (0, import_promises6.stat)(path).catch(() => null)) blocked.push({ skill, path });
|
|
2097
|
+
}
|
|
2098
|
+
return blocked;
|
|
2099
|
+
}
|
|
1771
2100
|
async function missingSkills(directory, skills) {
|
|
1772
2101
|
const gone = /* @__PURE__ */ new Set();
|
|
1773
2102
|
for (const skill of skills) {
|
|
1774
|
-
if (!await (0,
|
|
2103
|
+
if (!await (0, import_promises6.stat)((0, import_node_path5.join)(directory, skill)).catch(() => null)) gone.add(skill);
|
|
1775
2104
|
}
|
|
1776
2105
|
return gone;
|
|
1777
2106
|
}
|
|
@@ -1809,7 +2138,14 @@ async function updateSkills(dependencies) {
|
|
|
1809
2138
|
}
|
|
1810
2139
|
if (commit !== void 0 && commit === previous?.commit && sameTarget && nothingToRestore) {
|
|
1811
2140
|
await response.body?.cancel();
|
|
1812
|
-
return {
|
|
2141
|
+
return {
|
|
2142
|
+
status: "unchanged",
|
|
2143
|
+
name,
|
|
2144
|
+
commit,
|
|
2145
|
+
conflicts: standing,
|
|
2146
|
+
skipped: [...gone],
|
|
2147
|
+
blocked: []
|
|
2148
|
+
};
|
|
1813
2149
|
}
|
|
1814
2150
|
const gzipped = await readCapped(response, dependencies.maxArchiveBytes ?? MAX_ARCHIVE_BYTES);
|
|
1815
2151
|
let unpacked;
|
|
@@ -1844,7 +2180,22 @@ async function updateSkills(dependencies) {
|
|
|
1844
2180
|
if (dependencies.restoreMissing) throw nameClash(owner, skill, dependencies.directory);
|
|
1845
2181
|
conflicts.push({ skill, owner });
|
|
1846
2182
|
}
|
|
1847
|
-
const
|
|
2183
|
+
const unclashed = candidates.filter((skill) => !conflicts.some((one) => one.skill === skill));
|
|
2184
|
+
const ours = new Set(
|
|
2185
|
+
sameTarget ? Object.keys(previous?.skills ?? {}) : []
|
|
2186
|
+
);
|
|
2187
|
+
const blocked = [];
|
|
2188
|
+
for (const one of [
|
|
2189
|
+
...await outsidersAt(dependencies.directory, unclashed, ours),
|
|
2190
|
+
...dependencies.linkInto !== void 0 ? await outsidersAt(dependencies.linkInto, unclashed, ours) : []
|
|
2191
|
+
]) {
|
|
2192
|
+
if (!blocked.some((seen) => seen.skill === one.skill)) blocked.push(one);
|
|
2193
|
+
}
|
|
2194
|
+
if (dependencies.restoreMissing && blocked.length > 0) {
|
|
2195
|
+
const [first] = blocked;
|
|
2196
|
+
throw occupiedByOutsider(first.skill, first.path);
|
|
2197
|
+
}
|
|
2198
|
+
const skills = unclashed.filter((skill) => !blocked.some((one) => one.skill === skill));
|
|
1848
2199
|
const hashes = {};
|
|
1849
2200
|
const updated = [];
|
|
1850
2201
|
const unchanged = [];
|
|
@@ -1863,29 +2214,33 @@ async function updateSkills(dependencies) {
|
|
|
1863
2214
|
if (otherClaimant(state?.packages ?? {}, name, previous.directory, skill)) continue;
|
|
1864
2215
|
const stalePlace = deletableLedgerDirectory(previous.directory);
|
|
1865
2216
|
if (stalePlace === void 0) continue;
|
|
1866
|
-
await (0,
|
|
2217
|
+
await (0, import_promises6.rm)((0, import_node_path5.join)(stalePlace, skill), { recursive: true, force: true });
|
|
1867
2218
|
if (previous.linkedInto !== void 0) {
|
|
1868
|
-
await (0,
|
|
2219
|
+
await (0, import_promises6.rm)((0, import_node_path5.join)(previous.linkedInto, skill), { recursive: true, force: true });
|
|
1869
2220
|
}
|
|
1870
2221
|
}
|
|
1871
2222
|
}
|
|
1872
2223
|
for (const skill of skills) {
|
|
1873
|
-
await (0,
|
|
2224
|
+
await (0, import_promises6.rm)((0, import_node_path5.join)(dependencies.directory, skill), { recursive: true, force: true });
|
|
1874
2225
|
}
|
|
1875
|
-
const untouched = /* @__PURE__ */ new Set([
|
|
2226
|
+
const untouched = /* @__PURE__ */ new Set([
|
|
2227
|
+
...skipped,
|
|
2228
|
+
...conflicts.map((one) => one.skill),
|
|
2229
|
+
...blocked.map((one) => one.skill)
|
|
2230
|
+
]);
|
|
1876
2231
|
for (const file of files) {
|
|
1877
2232
|
if (untouched.has(file.skill)) continue;
|
|
1878
|
-
const target = (0,
|
|
2233
|
+
const target = (0, import_node_path5.join)(dependencies.directory, file.skill, ...file.path.split("/"));
|
|
1879
2234
|
assertInside(dependencies.directory, target);
|
|
1880
|
-
await (0,
|
|
1881
|
-
await (0,
|
|
2235
|
+
await (0, import_promises6.mkdir)((0, import_node_path5.dirname)(target), { recursive: true });
|
|
2236
|
+
await (0, import_promises6.writeFile)(target, file.data);
|
|
1882
2237
|
}
|
|
1883
2238
|
let copiedCount = 0;
|
|
1884
2239
|
if (dependencies.linkInto !== void 0) {
|
|
1885
2240
|
for (const skill of skills) {
|
|
1886
2241
|
const how = await linkSkill(
|
|
1887
|
-
(0,
|
|
1888
|
-
(0,
|
|
2242
|
+
(0, import_node_path5.join)(dependencies.directory, skill),
|
|
2243
|
+
(0, import_node_path5.join)(dependencies.linkInto, skill)
|
|
1889
2244
|
);
|
|
1890
2245
|
if (how === "copied") copiedCount += 1;
|
|
1891
2246
|
}
|
|
@@ -1923,6 +2278,7 @@ async function updateSkills(dependencies) {
|
|
|
1923
2278
|
removed,
|
|
1924
2279
|
skipped,
|
|
1925
2280
|
conflicts,
|
|
2281
|
+
blocked,
|
|
1926
2282
|
directory: dependencies.directory,
|
|
1927
2283
|
...dependencies.linkInto !== void 0 ? { linkedInto: dependencies.linkInto } : {},
|
|
1928
2284
|
...copiedCount > 0 ? { copiedCount } : {},
|
|
@@ -2128,7 +2484,7 @@ async function planSetup(dependencies) {
|
|
|
2128
2484
|
}
|
|
2129
2485
|
|
|
2130
2486
|
// src/cli.ts
|
|
2131
|
-
var VERSION = false ? "0.0.0-dev" : "0.9.
|
|
2487
|
+
var VERSION = false ? "0.0.0-dev" : "0.9.2";
|
|
2132
2488
|
var SKILLS_ADD_DEPRECATION = "\u26A0 `pep skills add` will be removed in a future release.";
|
|
2133
2489
|
function reportUpdate(result) {
|
|
2134
2490
|
const conflicts = () => {
|
|
@@ -2137,6 +2493,12 @@ function reportUpdate(result) {
|
|
|
2137
2493
|
console.error(` each other. \`pep skills remove\` one of them to settle it.`);
|
|
2138
2494
|
}
|
|
2139
2495
|
};
|
|
2496
|
+
const blocked = () => {
|
|
2497
|
+
for (const { skill, path } of result.blocked) {
|
|
2498
|
+
console.error(` \u26A0 "${skill}" was not installed \u2014 ${path} already exists`);
|
|
2499
|
+
console.error(` and pep did not put it there. Move it aside, then run this again.`);
|
|
2500
|
+
}
|
|
2501
|
+
};
|
|
2140
2502
|
const skipped = (list, name) => {
|
|
2141
2503
|
if (list.length === 0) return;
|
|
2142
2504
|
console.log(` deleted locally, left alone: ${list.join(" ")}`);
|
|
@@ -2146,6 +2508,7 @@ function reportUpdate(result) {
|
|
|
2146
2508
|
console.log(`${result.name}: already at ${result.commit}. Nothing to do.`);
|
|
2147
2509
|
skipped(result.skipped, result.name);
|
|
2148
2510
|
conflicts();
|
|
2511
|
+
blocked();
|
|
2149
2512
|
return;
|
|
2150
2513
|
}
|
|
2151
2514
|
console.log(
|
|
@@ -2156,6 +2519,7 @@ function reportUpdate(result) {
|
|
|
2156
2519
|
}
|
|
2157
2520
|
skipped(result.skipped, result.name);
|
|
2158
2521
|
conflicts();
|
|
2522
|
+
blocked();
|
|
2159
2523
|
console.log(` ${result.fileCount} file(s) in ${result.directory}`);
|
|
2160
2524
|
if (result.linkedInto !== void 0) {
|
|
2161
2525
|
console.log(
|
|
@@ -2253,7 +2617,7 @@ function parseSkillsArgs(command, args, cwd) {
|
|
|
2253
2617
|
// `rm -rf` 到 B 头上。`--project` 没有这个问题,因为它走 `join(cwd, …)`。
|
|
2254
2618
|
// 基准用 `cwd` 而不是 `process.cwd()`:与同一处的 `projectSkillsTarget(cwd)` 同源,
|
|
2255
2619
|
// 也让这条分支可测。
|
|
2256
|
-
{ directory: (0,
|
|
2620
|
+
{ directory: (0, import_node_path6.resolve)(cwd ?? process.cwd(), explicitDirectory) }
|
|
2257
2621
|
) : project ? projectSkillsTarget(cwd) : void 0;
|
|
2258
2622
|
return { ...requested !== void 0 ? { requested } : {}, named, ...chosenTarget ? { chosenTarget } : {} };
|
|
2259
2623
|
}
|
|
@@ -2468,7 +2832,8 @@ ${url}`)
|
|
|
2468
2832
|
if (failure) throw new Error(failure);
|
|
2469
2833
|
return;
|
|
2470
2834
|
}
|
|
2471
|
-
|
|
2835
|
+
const GROUPS = ["auth", "skills", "docs", "resources", "nexus"];
|
|
2836
|
+
if (group === void 0 || !GROUPS.includes(group))
|
|
2472
2837
|
throw new Error(`Unknown command.
|
|
2473
2838
|
|
|
2474
2839
|
${usage(VERSION)}`);
|
|
@@ -2478,25 +2843,59 @@ ${usage(VERSION)}`);
|
|
|
2478
2843
|
return;
|
|
2479
2844
|
}
|
|
2480
2845
|
const { configStore, auth } = createCliServices();
|
|
2481
|
-
if (group === "docs") {
|
|
2482
|
-
|
|
2483
|
-
|
|
2846
|
+
if (group === "docs" || group === "resources") {
|
|
2847
|
+
const allowed = group === "docs" ? ["list", "get", "regions"] : ["list", "download"];
|
|
2848
|
+
if (!allowed.includes(command)) {
|
|
2849
|
+
throw new Error(`Unknown ${group} command.
|
|
2484
2850
|
|
|
2485
|
-
${usage(VERSION, [
|
|
2851
|
+
${usage(VERSION, [group])}`);
|
|
2486
2852
|
}
|
|
2853
|
+
const action = `${group}:${command}`;
|
|
2487
2854
|
const explicitDocsUrl = option(args, "--docs-url");
|
|
2488
|
-
const
|
|
2855
|
+
const region = option(args, "--region");
|
|
2856
|
+
const platform3 = option(args, "--platform");
|
|
2857
|
+
const intoDirectory = option(args, "--dir");
|
|
2858
|
+
const force = flag(args, "--force");
|
|
2859
|
+
const path = action === "docs:get" ? args.shift() : void 0;
|
|
2860
|
+
const wantedFile = action === "resources:download" ? args.shift() : void 0;
|
|
2489
2861
|
if (args.length > 0) throw new Error(`Unknown option: ${args[0]}`);
|
|
2490
|
-
if (
|
|
2862
|
+
if (action === "docs:get" && !path) throw new Error("pep docs get needs a document path.");
|
|
2863
|
+
if (action === "docs:regions" && region !== void 0) {
|
|
2864
|
+
throw new Error("pep docs regions lists every region \u2014 it takes no --region.");
|
|
2865
|
+
}
|
|
2866
|
+
if (action === "resources:download" && !wantedFile) {
|
|
2867
|
+
throw new Error(
|
|
2868
|
+
"pep resources download needs a file name. Run `pep resources list` to see what is there."
|
|
2869
|
+
);
|
|
2870
|
+
}
|
|
2871
|
+
if (platform3 !== void 0 && group !== "resources") {
|
|
2872
|
+
throw new Error(`pep ${group} ${command} takes no --platform.`);
|
|
2873
|
+
}
|
|
2874
|
+
if ((intoDirectory !== void 0 || force) && action !== "resources:download") {
|
|
2875
|
+
throw new Error(`pep ${group} ${command} takes no --dir or --force.`);
|
|
2876
|
+
}
|
|
2877
|
+
const wantedPlatform = platform3 === void 0 ? void 0 : resolvePlatform(platform3);
|
|
2878
|
+
if (platform3 !== void 0 && wantedPlatform === void 0) {
|
|
2879
|
+
throw new Error(`No such platform: ${platform3}. Available: ${PLATFORMS.join(", ")}.`);
|
|
2880
|
+
}
|
|
2491
2881
|
const saved = await configStore.read();
|
|
2492
2882
|
const docsUrl = explicitDocsUrl ? normalizeDocsUrl(explicitDocsUrl) : saved?.docsUrl ?? DEFAULT_DOCS_URL;
|
|
2493
2883
|
if (explicitDocsUrl && saved && saved.docsUrl !== docsUrl) {
|
|
2494
2884
|
await configStore.write({ ...saved, docsUrl });
|
|
2495
2885
|
}
|
|
2886
|
+
if (action === "docs:regions") {
|
|
2887
|
+
const known = await fetchRegions({ docsUrl });
|
|
2888
|
+
for (const one of known.regions) {
|
|
2889
|
+
const aliases = one.aliases?.length ? ` ${one.aliases.join(",")}` : " ";
|
|
2890
|
+
const mark = one.id === known.defaultRegion ? " (default)" : "";
|
|
2891
|
+
console.log(`${one.id}${aliases} ${one.defaultLanguage} ${one.name}${mark}`);
|
|
2892
|
+
}
|
|
2893
|
+
return;
|
|
2894
|
+
}
|
|
2496
2895
|
const authorization = await auth.currentAuthorization();
|
|
2497
2896
|
const dependencies = { docsUrl, accessToken: authorization.accessToken };
|
|
2498
|
-
if (
|
|
2499
|
-
const entries = await fetchDocsIndex(dependencies);
|
|
2897
|
+
if (action === "docs:list") {
|
|
2898
|
+
const entries = await fetchDocsIndex(dependencies, region);
|
|
2500
2899
|
if (entries.length === 0) {
|
|
2501
2900
|
console.error("No documents are readable with this account.");
|
|
2502
2901
|
return;
|
|
@@ -2506,7 +2905,42 @@ ${usage(VERSION, ["docs"])}`);
|
|
|
2506
2905
|
}
|
|
2507
2906
|
return;
|
|
2508
2907
|
}
|
|
2509
|
-
|
|
2908
|
+
if (group === "resources") {
|
|
2909
|
+
const known = await fetchRegions({ docsUrl });
|
|
2910
|
+
const picked = region === void 0 ? resolveRegion(known, known.defaultRegion) : resolveRegion(known, region);
|
|
2911
|
+
if (!picked) throw unknownRegion(known, region ?? known.defaultRegion);
|
|
2912
|
+
const all = await fetchResources({ ...dependencies, region: picked }, wantedPlatform);
|
|
2913
|
+
if (action === "resources:list") {
|
|
2914
|
+
if (all.length === 0) {
|
|
2915
|
+
console.error("The developer resources page lists no files.");
|
|
2916
|
+
return;
|
|
2917
|
+
}
|
|
2918
|
+
for (const one of all) {
|
|
2919
|
+
const where = one.external ? "external" : one.platforms.join(",");
|
|
2920
|
+
console.log(`${one.file} ${where} ${one.url}`);
|
|
2921
|
+
}
|
|
2922
|
+
return;
|
|
2923
|
+
}
|
|
2924
|
+
const target = pickResource(all, wantedFile);
|
|
2925
|
+
const result = await downloadResource(dependencies, target, {
|
|
2926
|
+
// 默认落在**执行命令时的当前目录**,不是 CLI 装在哪儿。
|
|
2927
|
+
directory: intoDirectory ?? process.cwd(),
|
|
2928
|
+
force
|
|
2929
|
+
});
|
|
2930
|
+
console.error(
|
|
2931
|
+
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
|
+
);
|
|
2933
|
+
console.log(result.path);
|
|
2934
|
+
return;
|
|
2935
|
+
}
|
|
2936
|
+
let wanted = path;
|
|
2937
|
+
if (region !== void 0) {
|
|
2938
|
+
const known = await fetchRegions({ docsUrl });
|
|
2939
|
+
const picked = resolveRegion(known, region);
|
|
2940
|
+
if (!picked) throw unknownRegion(known, region);
|
|
2941
|
+
wanted = withRegion(wanted, picked);
|
|
2942
|
+
}
|
|
2943
|
+
process.stdout.write(await fetchDocContent(dependencies, wanted));
|
|
2510
2944
|
return;
|
|
2511
2945
|
}
|
|
2512
2946
|
if (group === "nexus") {
|