@4pm/cli 1.2.4 → 1.2.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 +47 -9
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -28211,7 +28211,6 @@ import { join as join2 } from "path";
|
|
|
28211
28211
|
// src/config/profile.ts
|
|
28212
28212
|
import {
|
|
28213
28213
|
existsSync,
|
|
28214
|
-
mkdirSync,
|
|
28215
28214
|
readdirSync,
|
|
28216
28215
|
readFileSync,
|
|
28217
28216
|
rmSync,
|
|
@@ -28219,6 +28218,30 @@ import {
|
|
|
28219
28218
|
} from "fs";
|
|
28220
28219
|
import { homedir } from "os";
|
|
28221
28220
|
import { join } from "path";
|
|
28221
|
+
|
|
28222
|
+
// src/common/io/ensure-dir.ts
|
|
28223
|
+
import { mkdirSync } from "fs";
|
|
28224
|
+
var CLI_IMAGE = "ghcr.io/4azuo/4pm-cli:full";
|
|
28225
|
+
function ensureDir(dir2) {
|
|
28226
|
+
try {
|
|
28227
|
+
mkdirSync(dir2, { recursive: true });
|
|
28228
|
+
} catch (err) {
|
|
28229
|
+
const code = err.code;
|
|
28230
|
+
if (code === "EACCES" || code === "EPERM") {
|
|
28231
|
+
throw new Error(
|
|
28232
|
+
`Cannot write "${dir2}" \u2014 the mounted home is owned by root. A reused Docker volume or a host bind-mount shadows the image's node-owned home, and the cli runs as the unprivileged "node" user (uid 1000), so it cannot chown it.
|
|
28233
|
+
Fix the mount ONCE, then retry:
|
|
28234
|
+
docker run --rm --user 0 --cap-drop=ALL --cap-add=CHOWN --cap-add=DAC_OVERRIDE \\
|
|
28235
|
+
--security-opt=no-new-privileges --entrypoint chown \\
|
|
28236
|
+
-v <volume>:/home/node/.4pm ${CLI_IMAGE} -R node:node /home/node/.4pm
|
|
28237
|
+
(host bind-mount: run \`sudo chown -R 1000:1000 <host-path>\` on the host instead; a FRESH named volume needs none of this.)`
|
|
28238
|
+
);
|
|
28239
|
+
}
|
|
28240
|
+
throw err;
|
|
28241
|
+
}
|
|
28242
|
+
}
|
|
28243
|
+
|
|
28244
|
+
// src/config/profile.ts
|
|
28222
28245
|
var DEFAULT_PROFILE = "default";
|
|
28223
28246
|
function defaultPointerPath() {
|
|
28224
28247
|
return join(homedir(), ".4pm", "default");
|
|
@@ -28232,7 +28255,7 @@ function defaultProfileName() {
|
|
|
28232
28255
|
return DEFAULT_PROFILE;
|
|
28233
28256
|
}
|
|
28234
28257
|
function writeDefaultProfile(userId) {
|
|
28235
|
-
|
|
28258
|
+
ensureDir(join(homedir(), ".4pm"));
|
|
28236
28259
|
writeFileSync(defaultPointerPath(), userId, "utf8");
|
|
28237
28260
|
}
|
|
28238
28261
|
function clearDefaultProfileIf(name) {
|
|
@@ -28269,7 +28292,7 @@ function listProfiles() {
|
|
|
28269
28292
|
}
|
|
28270
28293
|
function profileDir(name) {
|
|
28271
28294
|
const dir2 = join(homedir(), ".4pm", "profiles", name);
|
|
28272
|
-
|
|
28295
|
+
ensureDir(dir2);
|
|
28273
28296
|
return dir2;
|
|
28274
28297
|
}
|
|
28275
28298
|
function defaultProfileConfig() {
|
|
@@ -28492,7 +28515,7 @@ ${INSECURE_URL_WARNING}
|
|
|
28492
28515
|
`);
|
|
28493
28516
|
console.log("2. The web will show a confirmation code \u2014 paste it here.");
|
|
28494
28517
|
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
28495
|
-
const hashcode2 = (await rl.question("
|
|
28518
|
+
const hashcode2 = (await rl.question("Confirmation code: ")).trim();
|
|
28496
28519
|
rl.close();
|
|
28497
28520
|
result = await confirmPairing(serverUrl, hashcode2, collectFingerprint());
|
|
28498
28521
|
}
|
|
@@ -29803,7 +29826,7 @@ async function runAiFailover(plan, commandId, cwd2, handlers) {
|
|
|
29803
29826
|
let finalExit = -1;
|
|
29804
29827
|
for (let i = 0; i < total; i++) {
|
|
29805
29828
|
const attempt = plan.attempts[i];
|
|
29806
|
-
if (total > 1) handlers.onAttemptStart(attempt.label, i, total);
|
|
29829
|
+
if (total > 1) handlers.onAttemptStart(attempt.label, i, total, attempt.cmd);
|
|
29807
29830
|
const parser = createAiStreamParser(attempt.cmd);
|
|
29808
29831
|
let captured = "";
|
|
29809
29832
|
const emit = (raw) => {
|
|
@@ -29911,6 +29934,10 @@ function resolveClaudeAuthMode(config) {
|
|
|
29911
29934
|
function isUnifiedConfig(config) {
|
|
29912
29935
|
return Array.isArray(config.aiProfiles) && config.aiProfiles.some(isUsableCredential);
|
|
29913
29936
|
}
|
|
29937
|
+
function activeProvider(config) {
|
|
29938
|
+
const v = config.aiCli?.trim();
|
|
29939
|
+
return v && AI_PROVIDERS.includes(v) ? v : null;
|
|
29940
|
+
}
|
|
29914
29941
|
function claudeProfilesOf(config) {
|
|
29915
29942
|
if (isUnifiedConfig(config)) return config.aiProfiles.filter((c) => c.provider === "claude");
|
|
29916
29943
|
return config.claudeHome ?? [];
|
|
@@ -29984,7 +30011,18 @@ function planAiRun(prompt, config, hint = {}) {
|
|
|
29984
30011
|
}
|
|
29985
30012
|
function planUnifiedRun(prompt, config, workingCredential) {
|
|
29986
30013
|
const baseEnv = config.aiEnv ?? {};
|
|
29987
|
-
const
|
|
30014
|
+
const active = activeProvider(config);
|
|
30015
|
+
const usable = config.aiProfiles.filter(isUsableCredential);
|
|
30016
|
+
const scoped = active ? usable.filter((c) => c.provider === active) : usable;
|
|
30017
|
+
if (active && scoped.length === 0) {
|
|
30018
|
+
return {
|
|
30019
|
+
cmd: active,
|
|
30020
|
+
attempts: [
|
|
30021
|
+
{ cmd: active, label: "default", dir: null, key: null, args: buildRunArgs({ profile: "" }, active, prompt), env: { ...baseEnv } }
|
|
30022
|
+
]
|
|
30023
|
+
};
|
|
30024
|
+
}
|
|
30025
|
+
const resolved = scoped.map((cred) => {
|
|
29988
30026
|
const cmd = cred.provider;
|
|
29989
30027
|
const dir2 = resolveHomePath(cred.profile);
|
|
29990
30028
|
return { cred, cmd, dir: dir2, key: credentialKey(cmd, dir2), envVar: profileEnvVar(cmd) };
|
|
@@ -33080,8 +33118,8 @@ var WsClient = class {
|
|
|
33080
33118
|
this.bus.push({ source: origin, kind: "out", text: resultBuf });
|
|
33081
33119
|
}
|
|
33082
33120
|
},
|
|
33083
|
-
onAttemptStart: (label, index, total) => {
|
|
33084
|
-
const text = `\u2192 trying ${
|
|
33121
|
+
onAttemptStart: (label, index, total, cmd2) => {
|
|
33122
|
+
const text = `\u2192 trying ${cmd2} profile "${label}" (${index + 1}/${total})\u2026`;
|
|
33085
33123
|
this.bus.push({ source: origin, kind: "log", text });
|
|
33086
33124
|
this.send(WsChannels.COMMAND_OUTPUT, { commandId, seq: seq++, chunk: `${text}
|
|
33087
33125
|
`, log: true });
|
|
@@ -33596,7 +33634,7 @@ function isAlive(pid) {
|
|
|
33596
33634
|
function lockHolder(profileDir2) {
|
|
33597
33635
|
try {
|
|
33598
33636
|
const pid = Number.parseInt(readFileSync15(lockPath(profileDir2), "utf8").trim(), 10);
|
|
33599
|
-
if (Number.isInteger(pid) && pid > 0 && isAlive(pid)) return pid;
|
|
33637
|
+
if (Number.isInteger(pid) && pid > 0 && pid !== process.pid && isAlive(pid)) return pid;
|
|
33600
33638
|
} catch {
|
|
33601
33639
|
}
|
|
33602
33640
|
return null;
|