@4pm/cli 1.4.3 → 1.4.4
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 +42 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -33663,6 +33663,10 @@ var WsChannels = {
|
|
|
33663
33663
|
USAGE_REPORT: "usage.report",
|
|
33664
33664
|
// cli → server: Claude subscription usage snapshot (5h / weekly — ADR-0072)
|
|
33665
33665
|
MACHINE_USAGE: "machine.usage",
|
|
33666
|
+
// cli → server: last-run result of an external tool (claude/codex/gh/glab/git — ADR-0223).
|
|
33667
|
+
// One-way, sent immediately after each direct tool invocation (last-wins per tool); the
|
|
33668
|
+
// server merges it into `MachineLink.usageSnapshot.toolHealth` for the admin pool lists.
|
|
33669
|
+
TOOL_HEALTH: "tool.health",
|
|
33666
33670
|
// cli → server: durable per-command history record on finish (ADR-0072)
|
|
33667
33671
|
COMMAND_HISTORY: "command.history",
|
|
33668
33672
|
// cli → server: periodic upload of the cli's own JSONL log file (ADR-0122) — stored
|
|
@@ -34011,6 +34015,24 @@ async function runCommand(dispatch, emit) {
|
|
|
34011
34015
|
});
|
|
34012
34016
|
}
|
|
34013
34017
|
|
|
34018
|
+
// src/core/tool-health.ts
|
|
34019
|
+
var sink = null;
|
|
34020
|
+
function setToolHealthSink(fn) {
|
|
34021
|
+
sink = fn;
|
|
34022
|
+
}
|
|
34023
|
+
function reportToolResult(tool, ok, message) {
|
|
34024
|
+
try {
|
|
34025
|
+
const reason = (message ?? "").trim().slice(0, 200);
|
|
34026
|
+
sink?.({
|
|
34027
|
+
tool,
|
|
34028
|
+
ok,
|
|
34029
|
+
message: ok ? null : reason || "failed",
|
|
34030
|
+
at: (/* @__PURE__ */ new Date()).toISOString()
|
|
34031
|
+
});
|
|
34032
|
+
} catch {
|
|
34033
|
+
}
|
|
34034
|
+
}
|
|
34035
|
+
|
|
34014
34036
|
// src/core/ai-stream.ts
|
|
34015
34037
|
function assistantText(ev) {
|
|
34016
34038
|
const parts = ev.message?.content ?? [];
|
|
@@ -34117,8 +34139,11 @@ function runAttempt(commandId, cmd, args, cwd2, env3, onChunk) {
|
|
|
34117
34139
|
async function runAiFailover(plan, commandId, cwd2, handlers) {
|
|
34118
34140
|
const total = plan.attempts.length;
|
|
34119
34141
|
let finalExit = -1;
|
|
34142
|
+
let lastCmd = plan.attempts[0]?.cmd ?? "claude";
|
|
34143
|
+
let lastCaptured = "";
|
|
34120
34144
|
for (let i = 0; i < total; i++) {
|
|
34121
34145
|
const attempt = plan.attempts[i];
|
|
34146
|
+
lastCmd = attempt.cmd;
|
|
34122
34147
|
if (total > 1) handlers.onAttemptStart(attempt.label, i, total, attempt.cmd);
|
|
34123
34148
|
const parser = createAiStreamParser(attempt.cmd);
|
|
34124
34149
|
let captured = "";
|
|
@@ -34135,17 +34160,26 @@ async function runAiFailover(plan, commandId, cwd2, handlers) {
|
|
|
34135
34160
|
captured += tail;
|
|
34136
34161
|
handlers.onChunk(tail);
|
|
34137
34162
|
}
|
|
34163
|
+
lastCaptured = captured;
|
|
34138
34164
|
if (finalExit === 0) {
|
|
34139
34165
|
const usage = parser.usage();
|
|
34140
34166
|
if (usage.tokens === 0) usage.tokens = estimateTokens(captured);
|
|
34167
|
+
reportToolResult(attempt.cmd, true);
|
|
34141
34168
|
return { exitCode: 0, workedDir: attempt.dir, workedKey: attempt.key, workedCmd: attempt.cmd, usage };
|
|
34142
34169
|
}
|
|
34143
34170
|
const reason = isAuthFailure(captured) ? "auth" : isSessionLimit(captured) ? "limit" : null;
|
|
34144
34171
|
if (!reason || i === total - 1) break;
|
|
34145
34172
|
handlers.onAttemptFail(attempt.label, reason);
|
|
34146
34173
|
}
|
|
34174
|
+
reportToolResult(lastCmd, false, summarizeAiFailure(lastCaptured, finalExit));
|
|
34147
34175
|
return { exitCode: finalExit, workedDir: null, workedKey: null, workedCmd: null, usage: { tokens: 0, input: 0, output: 0, cacheRead: 0, cacheCreation: 0 } };
|
|
34148
34176
|
}
|
|
34177
|
+
function summarizeAiFailure(captured, exitCode) {
|
|
34178
|
+
if (isAuthFailure(captured)) return "Not logged in";
|
|
34179
|
+
if (isSessionLimit(captured)) return "Usage limit reached";
|
|
34180
|
+
const firstLine = captured.split("\n").map((l) => l.trim()).find((l) => l.length > 0);
|
|
34181
|
+
return firstLine || `exited ${exitCode}`;
|
|
34182
|
+
}
|
|
34149
34183
|
|
|
34150
34184
|
// src/core/ai-profile-state.ts
|
|
34151
34185
|
import { existsSync as existsSync5, readFileSync as readFileSync7, writeFileSync as writeFileSync4 } from "fs";
|
|
@@ -35068,7 +35102,10 @@ async function runKnowledgeCompose(req, ai, physicRoot) {
|
|
|
35068
35102
|
for (let i = 0; i < attempts.length; i++) {
|
|
35069
35103
|
try {
|
|
35070
35104
|
const { code, out, err } = await runOnce(ai.cmd, attempts[i], prompt, physicRoot, ai.env);
|
|
35071
|
-
if (code === 0 && out.trim())
|
|
35105
|
+
if (code === 0 && out.trim()) {
|
|
35106
|
+
reportToolResult(ai.cmd, true);
|
|
35107
|
+
return { bodyMarkdown: out.trim() };
|
|
35108
|
+
}
|
|
35072
35109
|
const combined = err || out;
|
|
35073
35110
|
lastReason = `exited ${code}: ${combined.slice(0, 500)}`;
|
|
35074
35111
|
if (!isAuthFailure(combined) && !isSessionLimit(combined) || i === attempts.length - 1) break;
|
|
@@ -35077,6 +35114,7 @@ async function runKnowledgeCompose(req, ai, physicRoot) {
|
|
|
35077
35114
|
break;
|
|
35078
35115
|
}
|
|
35079
35116
|
}
|
|
35117
|
+
reportToolResult(ai.cmd, false, lastReason);
|
|
35080
35118
|
return { bodyMarkdown: "", error: lastReason };
|
|
35081
35119
|
}
|
|
35082
35120
|
|
|
@@ -36961,6 +36999,7 @@ var WsClient = class {
|
|
|
36961
36999
|
socket.on("close", (code) => {
|
|
36962
37000
|
this.clearHeartbeat();
|
|
36963
37001
|
this.sessionKey = null;
|
|
37002
|
+
setToolHealthSink(null);
|
|
36964
37003
|
logger.debug("ws.close", { code });
|
|
36965
37004
|
if (code === CLOSE_DRAINING) {
|
|
36966
37005
|
this.bus.log("Server draining \u2014 will reconnect (backoff + jitter).");
|
|
@@ -37002,6 +37041,7 @@ var WsClient = class {
|
|
|
37002
37041
|
this.startUsagePolling();
|
|
37003
37042
|
this.startLogUpload();
|
|
37004
37043
|
this.startKbRefresh();
|
|
37044
|
+
setToolHealthSink((report) => this.send(WsChannels.TOOL_HEALTH, report));
|
|
37005
37045
|
this.connectWaiters.splice(0).forEach((fn) => fn());
|
|
37006
37046
|
return;
|
|
37007
37047
|
}
|
|
@@ -37174,6 +37214,7 @@ var WsClient = class {
|
|
|
37174
37214
|
this.bus.push({ source: "server", kind: "aires", text: `${supportCmd} \u203A` });
|
|
37175
37215
|
this.bus.push({ source: "server", kind: "out", text: reply.body });
|
|
37176
37216
|
}
|
|
37217
|
+
reportToolResult(supportCmd, !reply.error, reply.error);
|
|
37177
37218
|
this.send(WsChannels.SUPPORT_ANSWER, reply, message.id);
|
|
37178
37219
|
}).finally(() => this.bus.endBusy("support-answer"));
|
|
37179
37220
|
break;
|