@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.
Files changed (2) hide show
  1. package/dist/index.js +32 -8
  2. 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 args = ["-p", ...profile?.model ? ["--model", profile.model] : []];
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
- resolve8({ code: code ?? -1, out, err });
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 body = await runClaudeWithFailover(buildPrompt(docs, req.question, req.askerRole), ai);
35041
- if (!body) return { body: "", error: "empty answer" };
35042
- return { body };
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) };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@4pm/cli",
3
- "version": "1.4.4",
3
+ "version": "1.4.5",
4
4
  "private": false,
5
5
  "description": "4PM CLI — drives Claude/Codex CLIs on AI worker machines (Node + TypeScript)",
6
6
  "license": "LicenseRef-4PM-Source-Available",