@4pm/cli 1.4.5 → 1.4.6
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/index.js +45 -36
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -28420,6 +28420,31 @@ function collectFingerprint() {
|
|
|
28420
28420
|
return { fingerprint, hostname: host };
|
|
28421
28421
|
}
|
|
28422
28422
|
|
|
28423
|
+
// src/utils/secure-url.ts
|
|
28424
|
+
function isLocalHost(hostname2) {
|
|
28425
|
+
return hostname2 === "localhost" || hostname2 === "127.0.0.1" || hostname2 === "::1" || hostname2.endsWith(".localhost");
|
|
28426
|
+
}
|
|
28427
|
+
function isInsecureRemoteUrl(url) {
|
|
28428
|
+
let parsed;
|
|
28429
|
+
try {
|
|
28430
|
+
parsed = new URL(url);
|
|
28431
|
+
} catch {
|
|
28432
|
+
return false;
|
|
28433
|
+
}
|
|
28434
|
+
if (parsed.protocol === "https:" || parsed.protocol === "wss:") return false;
|
|
28435
|
+
return !isLocalHost(parsed.hostname);
|
|
28436
|
+
}
|
|
28437
|
+
function insecureTransportAllowed() {
|
|
28438
|
+
const flag = (process.env.FOURPM_ALLOW_INSECURE_TRANSPORT ?? "").trim().toLowerCase();
|
|
28439
|
+
return flag === "1" || flag === "true" || flag === "yes";
|
|
28440
|
+
}
|
|
28441
|
+
var INSECURE_URL_BLOCKED = "\u2717 Refusing to connect over plaintext (http/ws) to a non-local host: credentials (pairing codes / ws_token) would travel in the clear to an unauthenticated server. Use an https://server URL. To override on a trusted private network only, set FOURPM_ALLOW_INSECURE_TRANSPORT=1.";
|
|
28442
|
+
function assertSecureRemoteUrl(url) {
|
|
28443
|
+
if (isInsecureRemoteUrl(url) && !insecureTransportAllowed()) {
|
|
28444
|
+
throw new Error(INSECURE_URL_BLOCKED);
|
|
28445
|
+
}
|
|
28446
|
+
}
|
|
28447
|
+
|
|
28423
28448
|
// src/services/api.ts
|
|
28424
28449
|
var CliApiError = class extends Error {
|
|
28425
28450
|
constructor(errorCode, message, status) {
|
|
@@ -28443,6 +28468,7 @@ async function unwrap(res) {
|
|
|
28443
28468
|
return envelope.data;
|
|
28444
28469
|
}
|
|
28445
28470
|
async function post(serverUrl, path, body) {
|
|
28471
|
+
assertSecureRemoteUrl(serverUrl);
|
|
28446
28472
|
const res = await fetch(`${serverUrl}${path}`, {
|
|
28447
28473
|
method: "POST",
|
|
28448
28474
|
headers: { "Content-Type": "application/json" },
|
|
@@ -28478,30 +28504,12 @@ async function fetchCliVersion(serverUrl) {
|
|
|
28478
28504
|
return unwrap(res);
|
|
28479
28505
|
}
|
|
28480
28506
|
|
|
28481
|
-
// src/utils/secure-url.ts
|
|
28482
|
-
function isLocalHost(hostname2) {
|
|
28483
|
-
return hostname2 === "localhost" || hostname2 === "127.0.0.1" || hostname2 === "::1" || hostname2.endsWith(".localhost");
|
|
28484
|
-
}
|
|
28485
|
-
function isInsecureRemoteUrl(url) {
|
|
28486
|
-
let parsed;
|
|
28487
|
-
try {
|
|
28488
|
-
parsed = new URL(url);
|
|
28489
|
-
} catch {
|
|
28490
|
-
return false;
|
|
28491
|
-
}
|
|
28492
|
-
if (parsed.protocol === "https:" || parsed.protocol === "wss:") return false;
|
|
28493
|
-
return !isLocalHost(parsed.hostname);
|
|
28494
|
-
}
|
|
28495
|
-
var INSECURE_URL_WARNING = "\u26A0 Insecure transport: connecting over plaintext (http/ws) to a non-local host. Pairing codes and the ws_token are sent in cleartext and the server is not authenticated (the app-layer encryption does not protect against a man-in-the-middle). Use an https:// server URL in production.";
|
|
28496
|
-
|
|
28497
28507
|
// src/commands/link.ts
|
|
28498
28508
|
function sanitizeProfileName(name) {
|
|
28499
28509
|
return name.replace(/[^a-zA-Z0-9._-]/g, "_") || "profile";
|
|
28500
28510
|
}
|
|
28501
28511
|
async function runLink(serverUrl, explicitProfile, token = null) {
|
|
28502
|
-
|
|
28503
|
-
${INSECURE_URL_WARNING}
|
|
28504
|
-
`);
|
|
28512
|
+
assertSecureRemoteUrl(serverUrl);
|
|
28505
28513
|
let result;
|
|
28506
28514
|
if (token) {
|
|
28507
28515
|
console.log(`\u2500\u2500 4PM cli headless pairing (profile: ${explicitProfile ?? "default"}) \u2500\u2500`);
|
|
@@ -28782,17 +28790,17 @@ function isValidRoleSet(roles) {
|
|
|
28782
28790
|
|
|
28783
28791
|
// vendor/@4pm/constants/worker-tools.ts
|
|
28784
28792
|
var WORKER_TOOL_CATALOG = [
|
|
28785
|
-
{ id: "git", label: "Git", category: "vcs", versionArg: "--version", installPackage: null },
|
|
28786
|
-
{ id: "node", label: "Node.js", category: "runtime", versionArg: "--version", installPackage: null },
|
|
28787
|
-
{ id: "npm", label: "npm", category: "runtime", versionArg: "--version", installPackage: null },
|
|
28788
|
-
{ id: "pnpm", label: "pnpm", category: "runtime", versionArg: "--version", installPackage: "pnpm" },
|
|
28789
|
-
{ id: "claude", label: "Claude Code", category: "ai-cli", versionArg: "--version", installPackage: "@anthropic-ai/claude-code" },
|
|
28790
|
-
{ id: "codex", label: "Codex", category: "ai-cli", versionArg: "--version", installPackage: "@openai/codex" },
|
|
28791
|
-
{ id: "gh", label: "GitHub CLI", category: "vcs", versionArg: "--version", installPackage: null },
|
|
28792
|
-
{ id: "glab", label: "GitLab CLI", category: "vcs", versionArg: "--version", installPackage: null }
|
|
28793
|
+
{ id: "git", label: "Git", category: "vcs", versionArg: "--version", installPackage: null, installable: false },
|
|
28794
|
+
{ id: "node", label: "Node.js", category: "runtime", versionArg: "--version", installPackage: null, installable: false },
|
|
28795
|
+
{ id: "npm", label: "npm", category: "runtime", versionArg: "--version", installPackage: null, installable: false },
|
|
28796
|
+
{ id: "pnpm", label: "pnpm", category: "runtime", versionArg: "--version", installPackage: "pnpm", installable: false },
|
|
28797
|
+
{ id: "claude", label: "Claude Code", category: "ai-cli", versionArg: "--version", installPackage: "@anthropic-ai/claude-code", installable: false },
|
|
28798
|
+
{ id: "codex", label: "Codex", category: "ai-cli", versionArg: "--version", installPackage: "@openai/codex", installable: false },
|
|
28799
|
+
{ id: "gh", label: "GitHub CLI", category: "vcs", versionArg: "--version", installPackage: null, installable: false },
|
|
28800
|
+
{ id: "glab", label: "GitLab CLI", category: "vcs", versionArg: "--version", installPackage: null, installable: false }
|
|
28793
28801
|
];
|
|
28794
28802
|
var WORKER_TOOL_PREREQUISITE_IDS = WORKER_TOOL_CATALOG.filter(
|
|
28795
|
-
(t) => t.
|
|
28803
|
+
(t) => !t.installable
|
|
28796
28804
|
).map((t) => t.id);
|
|
28797
28805
|
var WORKER_TOOL_BUNDLED_GLOBALS = ["npm", "corepack"];
|
|
28798
28806
|
|
|
@@ -36660,7 +36668,7 @@ async function detectWorkerTools() {
|
|
|
36660
36668
|
category: t.category,
|
|
36661
36669
|
installed: version !== null,
|
|
36662
36670
|
version,
|
|
36663
|
-
installable: t.
|
|
36671
|
+
installable: t.installable
|
|
36664
36672
|
};
|
|
36665
36673
|
})
|
|
36666
36674
|
);
|
|
@@ -36670,10 +36678,10 @@ async function detectWorkerTools() {
|
|
|
36670
36678
|
function resolvePackage(name, op) {
|
|
36671
36679
|
const entry = WORKER_TOOL_CATALOG.find((t) => t.id === name);
|
|
36672
36680
|
if (entry) {
|
|
36673
|
-
if (entry.
|
|
36681
|
+
if (!entry.installable) {
|
|
36674
36682
|
return { error: `"${name}" is a prerequisite and cannot be ${op}ed from here.` };
|
|
36675
36683
|
}
|
|
36676
|
-
return { pkg: entry.installPackage };
|
|
36684
|
+
return { pkg: entry.installPackage ?? name };
|
|
36677
36685
|
}
|
|
36678
36686
|
if (WORKER_TOOL_PREREQUISITE_IDS.includes(name)) {
|
|
36679
36687
|
return { error: `"${name}" is a prerequisite and cannot be ${op}ed from here.` };
|
|
@@ -36749,7 +36757,6 @@ var WsClient = class {
|
|
|
36749
36757
|
ecdh = null;
|
|
36750
36758
|
wsToken = "";
|
|
36751
36759
|
/** Warn about a plaintext-remote server URL only once per process (ADR-0194 finding #1). */
|
|
36752
|
-
insecureWarned = false;
|
|
36753
36760
|
heartbeat = null;
|
|
36754
36761
|
usageTimer = null;
|
|
36755
36762
|
logUploadTimer = null;
|
|
@@ -36993,10 +37000,12 @@ var WsClient = class {
|
|
|
36993
37000
|
}
|
|
36994
37001
|
const wsBase = this.credential.wsUrl ?? this.credential.serverUrl;
|
|
36995
37002
|
const wsUrl = wsBase.replace(/^http/, "ws") + "/ws";
|
|
36996
|
-
if (
|
|
36997
|
-
this.
|
|
36998
|
-
this.bus.log(
|
|
36999
|
-
|
|
37003
|
+
if (isInsecureRemoteUrl(wsUrl) && !insecureTransportAllowed()) {
|
|
37004
|
+
this.stopped = true;
|
|
37005
|
+
this.bus.log(INSECURE_URL_BLOCKED, "error");
|
|
37006
|
+
this.bus.setStatus("stopped");
|
|
37007
|
+
logger.error("ws.insecure-transport-blocked", { url: wsBase });
|
|
37008
|
+
throw new Error(INSECURE_URL_BLOCKED);
|
|
37000
37009
|
}
|
|
37001
37010
|
const socket = new wrapper_default(wsUrl);
|
|
37002
37011
|
this.socket = socket;
|