@4pm/cli 1.4.4 → 1.4.5
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 +32 -8
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -34906,6 +34906,7 @@ import { createHash as createHash3 } from "crypto";
|
|
|
34906
34906
|
import { existsSync as existsSync8, mkdirSync as mkdirSync4, readdirSync as readdirSync4, readFileSync as readFileSync12, statSync as statSync3 } from "fs";
|
|
34907
34907
|
import { join as join12 } from "path";
|
|
34908
34908
|
import { promisify as promisify2 } from "util";
|
|
34909
|
+
var NO_USAGE = { tokens: 0, input: 0, output: 0, cacheRead: 0, cacheCreation: 0 };
|
|
34909
34910
|
var execFileAsync = promisify2(execFile2);
|
|
34910
34911
|
var MAX_CONTEXT_CHARS = 12e4;
|
|
34911
34912
|
var ANSWER_TIMEOUT_MS = 12e4;
|
|
@@ -34992,13 +34993,19 @@ function buildPrompt(docs, question, askerRole) {
|
|
|
34992
34993
|
}
|
|
34993
34994
|
function runClaudeOnce(cmd, profile, prompt, extraEnv) {
|
|
34994
34995
|
return new Promise((resolve8, reject) => {
|
|
34995
|
-
const
|
|
34996
|
+
const isClaude = cmd.includes("claude");
|
|
34997
|
+
const args = [
|
|
34998
|
+
"-p",
|
|
34999
|
+
...profile?.model ? ["--model", profile.model] : [],
|
|
35000
|
+
...isClaude ? ["--output-format", "stream-json", "--verbose"] : []
|
|
35001
|
+
];
|
|
34996
35002
|
const env3 = {
|
|
34997
35003
|
...process.env,
|
|
34998
35004
|
...extraEnv,
|
|
34999
35005
|
...profile ? { CLAUDE_CONFIG_DIR: profile.dir } : {}
|
|
35000
35006
|
};
|
|
35001
35007
|
const child = spawn4(cmd, args, { stdio: ["pipe", "pipe", "pipe"], env: env3 });
|
|
35008
|
+
const parser = createAiStreamParser(cmd);
|
|
35002
35009
|
let out = "";
|
|
35003
35010
|
let err = "";
|
|
35004
35011
|
const timer = setTimeout(() => {
|
|
@@ -35006,7 +35013,7 @@ function runClaudeOnce(cmd, profile, prompt, extraEnv) {
|
|
|
35006
35013
|
reject(new Error("claude answer timed out"));
|
|
35007
35014
|
}, ANSWER_TIMEOUT_MS);
|
|
35008
35015
|
timer.unref();
|
|
35009
|
-
child.stdout.on("data", (d) => out += d.toString());
|
|
35016
|
+
child.stdout.on("data", (d) => out += parser.push(d.toString()));
|
|
35010
35017
|
child.stderr.on("data", (d) => err += d.toString());
|
|
35011
35018
|
child.on("error", (e) => {
|
|
35012
35019
|
clearTimeout(timer);
|
|
@@ -35014,7 +35021,8 @@ function runClaudeOnce(cmd, profile, prompt, extraEnv) {
|
|
|
35014
35021
|
});
|
|
35015
35022
|
child.on("close", (code) => {
|
|
35016
35023
|
clearTimeout(timer);
|
|
35017
|
-
|
|
35024
|
+
out += parser.flush();
|
|
35025
|
+
resolve8({ code: code ?? -1, out, err, usage: parser.usage() });
|
|
35018
35026
|
});
|
|
35019
35027
|
child.stdin.write(prompt);
|
|
35020
35028
|
child.stdin.end();
|
|
@@ -35024,8 +35032,8 @@ async function runClaudeWithFailover(prompt, ai) {
|
|
|
35024
35032
|
const attempts = ai.profiles.length > 0 ? ai.profiles : [null];
|
|
35025
35033
|
let lastReason = "no attempt";
|
|
35026
35034
|
for (let i = 0; i < attempts.length; i++) {
|
|
35027
|
-
const { code, out, err } = await runClaudeOnce(ai.cmd, attempts[i], prompt, ai.env);
|
|
35028
|
-
if (code === 0 && out.trim()) return out.trim();
|
|
35035
|
+
const { code, out, err, usage } = await runClaudeOnce(ai.cmd, attempts[i], prompt, ai.env);
|
|
35036
|
+
if (code === 0 && out.trim()) return { text: out.trim(), usage };
|
|
35029
35037
|
const combined = err || out;
|
|
35030
35038
|
lastReason = `exited ${code}: ${combined.slice(0, 500)}`;
|
|
35031
35039
|
if (!isAuthFailure(combined) && !isSessionLimit(combined) || i === attempts.length - 1) break;
|
|
@@ -35037,9 +35045,25 @@ async function runSupportAnswer(req, ai, profileDir2) {
|
|
|
35037
35045
|
const dir2 = await ensureRepo(profileDir2, req.repo);
|
|
35038
35046
|
const docs = collectDocs(dir2);
|
|
35039
35047
|
if (!docs.trim()) return { body: "", error: "KB repo has no documentation" };
|
|
35040
|
-
const
|
|
35041
|
-
|
|
35042
|
-
|
|
35048
|
+
const { text, usage } = await runClaudeWithFailover(
|
|
35049
|
+
buildPrompt(docs, req.question, req.askerRole),
|
|
35050
|
+
ai
|
|
35051
|
+
);
|
|
35052
|
+
if (!text) return { body: "", error: "empty answer" };
|
|
35053
|
+
const u = usage.tokens > 0 ? usage : { ...NO_USAGE, tokens: estimateTokens(text) };
|
|
35054
|
+
return {
|
|
35055
|
+
body: text,
|
|
35056
|
+
tokens: u.tokens,
|
|
35057
|
+
...usage.tokens > 0 ? {
|
|
35058
|
+
tokensBreakdown: {
|
|
35059
|
+
input: usage.input,
|
|
35060
|
+
output: usage.output,
|
|
35061
|
+
cacheRead: usage.cacheRead,
|
|
35062
|
+
cacheCreation: usage.cacheCreation
|
|
35063
|
+
}
|
|
35064
|
+
} : {},
|
|
35065
|
+
finishedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
35066
|
+
};
|
|
35043
35067
|
} catch (err) {
|
|
35044
35068
|
logger.warn("support.answer.failed", { error: String(err) });
|
|
35045
35069
|
return { body: "", error: String(err) };
|