@lesscap/baton-cli 0.7.3 → 0.7.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/baton.mjs +96 -16
- package/package.json +1 -1
package/dist/baton.mjs
CHANGED
|
@@ -5024,6 +5024,7 @@ var sessionsClient = (baseUrl) => {
|
|
|
5024
5024
|
const matches = all.filter((s) => s.name === name);
|
|
5025
5025
|
return matches.length === 0 ? null : matches[matches.length - 1] ?? null;
|
|
5026
5026
|
},
|
|
5027
|
+
listEvents: (id) => request(u(`/sessions/${id}/events`), { method: "GET" }),
|
|
5027
5028
|
sendMessage: (id, text, attachments, planMode) => request(u(`/sessions/${id}/messages`), {
|
|
5028
5029
|
method: "POST",
|
|
5029
5030
|
body: {
|
|
@@ -6462,6 +6463,7 @@ var findTranscriptPath = (agentSessionId) => {
|
|
|
6462
6463
|
};
|
|
6463
6464
|
var isRecord = (v) => typeof v === "object" && v !== null;
|
|
6464
6465
|
var textOf = (rec) => {
|
|
6466
|
+
if (typeof rec.text === "string") return rec.text.trim();
|
|
6465
6467
|
const message = rec.message;
|
|
6466
6468
|
const content = isRecord(message) ? message.content : rec.content;
|
|
6467
6469
|
if (typeof content === "string") return content.trim();
|
|
@@ -6492,6 +6494,38 @@ var parseFirstExchange = (content) => {
|
|
|
6492
6494
|
}
|
|
6493
6495
|
return userText || assistantText ? { userText, assistantText } : null;
|
|
6494
6496
|
};
|
|
6497
|
+
var canonicalAgentMessage = (payload) => {
|
|
6498
|
+
if (!isRecord(payload) || !["item.started", "item.updated", "item.completed"].includes(String(payload.type)))
|
|
6499
|
+
return null;
|
|
6500
|
+
const item = payload.item;
|
|
6501
|
+
if (!isRecord(item) || item.type !== "agent_message" || typeof item.id !== "string") return null;
|
|
6502
|
+
const text = textOf(item);
|
|
6503
|
+
return text ? { id: item.id, text } : null;
|
|
6504
|
+
};
|
|
6505
|
+
var parseFirstExchangeFromEvents = (events) => {
|
|
6506
|
+
let userText = "";
|
|
6507
|
+
const itemOrder = [];
|
|
6508
|
+
const agentText = /* @__PURE__ */ new Map();
|
|
6509
|
+
const legacyText = [];
|
|
6510
|
+
for (const event of events) {
|
|
6511
|
+
if (!userText && event.type === "user_message" && isRecord(event.payload)) {
|
|
6512
|
+
if (typeof event.payload.text === "string") userText = event.payload.text.trim();
|
|
6513
|
+
continue;
|
|
6514
|
+
}
|
|
6515
|
+
if (!userText) continue;
|
|
6516
|
+
if (event.type === "turn_complete" || event.type === "turn_error") break;
|
|
6517
|
+
const agent = event.type === "agent_event" ? canonicalAgentMessage(event.payload) : null;
|
|
6518
|
+
if (agent) {
|
|
6519
|
+
if (!agentText.has(agent.id)) itemOrder.push(agent.id);
|
|
6520
|
+
agentText.set(agent.id, agent.text);
|
|
6521
|
+
} else if (event.type === "sdk_event" && isRecord(event.payload)) {
|
|
6522
|
+
const text = event.payload.type === "assistant" ? textOf(event.payload) : "";
|
|
6523
|
+
if (text) legacyText.push(text);
|
|
6524
|
+
}
|
|
6525
|
+
}
|
|
6526
|
+
const assistantText = itemOrder.map((id) => agentText.get(id) ?? "").filter(Boolean).join("\n") || legacyText.join("\n");
|
|
6527
|
+
return userText || assistantText ? { userText, assistantText } : null;
|
|
6528
|
+
};
|
|
6495
6529
|
var readFirstExchange = (agentSessionId) => {
|
|
6496
6530
|
const path = findTranscriptPath(agentSessionId);
|
|
6497
6531
|
if (!path) return null;
|
|
@@ -7785,16 +7819,26 @@ var createLiveness = (deps) => {
|
|
|
7785
7819
|
import { spawn } from "node:child_process";
|
|
7786
7820
|
import { randomUUID } from "node:crypto";
|
|
7787
7821
|
import { existsSync as existsSync5 } from "node:fs";
|
|
7788
|
-
import { dirname as dirname2, join as
|
|
7822
|
+
import { dirname as dirname2, join as join8 } from "node:path";
|
|
7789
7823
|
import { fileURLToPath } from "node:url";
|
|
7790
7824
|
import { query as query2 } from "@anthropic-ai/claude-agent-sdk";
|
|
7791
7825
|
|
|
7826
|
+
// src/session/runner/title-codex.ts
|
|
7827
|
+
import { mkdtemp, rm } from "node:fs/promises";
|
|
7828
|
+
import { tmpdir as tmpdir3 } from "node:os";
|
|
7829
|
+
import { join as join7 } from "node:path";
|
|
7830
|
+
import { Codex as Codex2 } from "@openai/codex-sdk";
|
|
7831
|
+
|
|
7792
7832
|
// src/session/runner/title.ts
|
|
7793
7833
|
var clip = (s, max) => s.length > max ? s.slice(0, max) : s;
|
|
7794
7834
|
var isDeclined = (title) => title.replace(/[^a-z]/gi, "").toUpperCase() === "NONE";
|
|
7795
7835
|
var sanitizeTitle = (raw) => raw.replace(/[\r\n]+/g, " ").replace(/[*_`~#>]/g, "").replace(/["']/g, "").replace(/^\s*(?:title|session|标题)\s*[::-]\s*/i, "").replace(/^\s*(?:[-*•]|\d+[.)、])\s+/, "").replace(/\s+/g, " ").trim().replace(/(?:[。.!?,、;;]|[.!?;](?=\s|$)).*$/s, "").trim().slice(0, 30).replace(/[\s。.,,、;;::!!??.]+$/, "").trim();
|
|
7836
|
+
var titleOutcome = (raw) => {
|
|
7837
|
+
const title = sanitizeTitle(raw);
|
|
7838
|
+
return !title || isDeclined(title) ? { kind: "declined" } : { kind: "titled", title };
|
|
7839
|
+
};
|
|
7796
7840
|
var TITLE_TIMEOUT_MS = 9e4;
|
|
7797
|
-
var
|
|
7841
|
+
var buildTitlePrompt = (userText, assistantText) => {
|
|
7798
7842
|
const exchange = [
|
|
7799
7843
|
`User: ${clip(userText, 800)}`,
|
|
7800
7844
|
assistantText ? `Assistant: ${clip(assistantText, 800)}` : ""
|
|
@@ -7829,7 +7873,7 @@ var generateTitle = async (input) => {
|
|
|
7829
7873
|
let failure = "";
|
|
7830
7874
|
try {
|
|
7831
7875
|
const messages = queryFn({
|
|
7832
|
-
prompt:
|
|
7876
|
+
prompt: buildTitlePrompt(userText, assistantText),
|
|
7833
7877
|
options: {
|
|
7834
7878
|
cwd: worktreePath,
|
|
7835
7879
|
// A one-shot summarizer, not an agent: replace the ~18k-token Claude
|
|
@@ -7869,8 +7913,41 @@ var generateTitle = async (input) => {
|
|
|
7869
7913
|
const tail = stderrTail.length > 0 ? ` | stderr: ${stderrTail.join(" / ")}` : "";
|
|
7870
7914
|
return { kind: "error", reason: `${failure}${tail}` };
|
|
7871
7915
|
}
|
|
7872
|
-
|
|
7873
|
-
|
|
7916
|
+
return titleOutcome(out);
|
|
7917
|
+
};
|
|
7918
|
+
|
|
7919
|
+
// src/session/runner/title-codex.ts
|
|
7920
|
+
var generateTitleWithCodex = async (input) => {
|
|
7921
|
+
const { userText, assistantText } = input;
|
|
7922
|
+
if (`${userText}${assistantText}`.trim().length < 4) return { kind: "declined" };
|
|
7923
|
+
const abort = new AbortController();
|
|
7924
|
+
const timer = setTimeout(() => abort.abort(), TITLE_TIMEOUT_MS);
|
|
7925
|
+
let cwd = "";
|
|
7926
|
+
try {
|
|
7927
|
+
cwd = await mkdtemp(join7(tmpdir3(), "baton-title-"));
|
|
7928
|
+
const client = input.client ?? new Codex2();
|
|
7929
|
+
const thread = client.startThread({
|
|
7930
|
+
workingDirectory: cwd,
|
|
7931
|
+
skipGitRepoCheck: true,
|
|
7932
|
+
sandboxMode: "read-only",
|
|
7933
|
+
approvalPolicy: "never",
|
|
7934
|
+
networkAccessEnabled: false,
|
|
7935
|
+
webSearchMode: "disabled"
|
|
7936
|
+
});
|
|
7937
|
+
const result = await thread.run(buildTitlePrompt(userText, assistantText), {
|
|
7938
|
+
signal: abort.signal
|
|
7939
|
+
});
|
|
7940
|
+
return titleOutcome(result.finalResponse);
|
|
7941
|
+
} catch (error) {
|
|
7942
|
+
return {
|
|
7943
|
+
kind: "error",
|
|
7944
|
+
reason: abort.signal.aborted ? `timeout after ${TITLE_TIMEOUT_MS / 1e3}s` : String(error).slice(0, 300)
|
|
7945
|
+
};
|
|
7946
|
+
} finally {
|
|
7947
|
+
clearTimeout(timer);
|
|
7948
|
+
if (cwd) await rm(cwd, { recursive: true, force: true }).catch(() => {
|
|
7949
|
+
});
|
|
7950
|
+
}
|
|
7874
7951
|
};
|
|
7875
7952
|
|
|
7876
7953
|
// src/worker/proc.ts
|
|
@@ -7885,7 +7962,7 @@ var killProcessGroup = (proc) => {
|
|
|
7885
7962
|
// src/worker/session-supervisor.ts
|
|
7886
7963
|
var binPath = () => {
|
|
7887
7964
|
const here = dirname2(fileURLToPath(import.meta.url));
|
|
7888
|
-
const devShim =
|
|
7965
|
+
const devShim = join8(here, "..", "..", "bin", "baton.mjs");
|
|
7889
7966
|
return existsSync5(devShim) ? devShim : fileURLToPath(import.meta.url);
|
|
7890
7967
|
};
|
|
7891
7968
|
var createSessionSupervisor = (deps) => {
|
|
@@ -7939,7 +8016,7 @@ var createSessionSupervisor = (deps) => {
|
|
|
7939
8016
|
if (!session2.agentSessionId || !worktreePath) {
|
|
7940
8017
|
const sessionCode = randomUUID();
|
|
7941
8018
|
const agentSessionId = session2.agentKind === "codex" ? `pending:${sessionCode}` : sessionCode;
|
|
7942
|
-
worktreePath =
|
|
8019
|
+
worktreePath = join8(worktreeDir, slug(`${name}-${sessionCode.slice(0, 8)}`));
|
|
7943
8020
|
const base = await syncedBase();
|
|
7944
8021
|
if (wasCanceled()) return log(`session #${sessionId} start canceled during git sync`);
|
|
7945
8022
|
createWorktree({
|
|
@@ -7957,7 +8034,7 @@ var createSessionSupervisor = (deps) => {
|
|
|
7957
8034
|
log(`recreated session #${sessionId} worktree (was missing) \u2192 ${worktreePath}`);
|
|
7958
8035
|
}
|
|
7959
8036
|
ensureExcluded(repo, PROJECT_CONFIG_NAME);
|
|
7960
|
-
saveProjectConfig(
|
|
8037
|
+
saveProjectConfig(join8(worktreePath, PROJECT_CONFIG_NAME), worktreeConfig(cfg));
|
|
7961
8038
|
if (hasTerminal(sessionId))
|
|
7962
8039
|
return log(`session #${sessionId} terminal opened mid-start \u2014 skipping headless start`);
|
|
7963
8040
|
spawnChild(sessionId, worktreePath);
|
|
@@ -8000,10 +8077,13 @@ var createSessionSupervisor = (deps) => {
|
|
|
8000
8077
|
};
|
|
8001
8078
|
const title = async (sessionId, agentSessionId, worktreePath) => {
|
|
8002
8079
|
const session2 = await client.sessions.get(sessionId);
|
|
8003
|
-
|
|
8004
|
-
const exchange = readFirstExchange(agentSessionId);
|
|
8080
|
+
const events = await client.sessions.listEvents(sessionId).catch(() => []);
|
|
8081
|
+
const exchange = parseFirstExchangeFromEvents(events) ?? (session2.agentKind === "claude-code" ? readFirstExchange(agentSessionId) : null);
|
|
8005
8082
|
if (!exchange) return log(`title #${sessionId}: no transcript yet, skipping`);
|
|
8006
|
-
const outcome = await
|
|
8083
|
+
const outcome = session2.agentKind === "codex" ? await generateTitleWithCodex({
|
|
8084
|
+
userText: exchange.userText,
|
|
8085
|
+
assistantText: exchange.assistantText
|
|
8086
|
+
}) : await generateTitle({
|
|
8007
8087
|
worktreePath,
|
|
8008
8088
|
userText: exchange.userText,
|
|
8009
8089
|
assistantText: exchange.assistantText,
|
|
@@ -8058,14 +8138,14 @@ var import_websocket_server = __toESM(require_websocket_server(), 1);
|
|
|
8058
8138
|
// src/worker/pty.ts
|
|
8059
8139
|
import { existsSync as existsSync6, readdirSync as readdirSync2 } from "node:fs";
|
|
8060
8140
|
import { homedir as homedir3 } from "node:os";
|
|
8061
|
-
import { join as
|
|
8141
|
+
import { join as join9 } from "node:path";
|
|
8062
8142
|
var MAX_TERMINALS = 10;
|
|
8063
8143
|
var ptyArgs = (agentSessionId, hasJsonl) => hasJsonl ? ["--resume", agentSessionId] : ["--session-id", agentSessionId];
|
|
8064
8144
|
var hasSessionJsonl = (agentSessionId) => {
|
|
8065
|
-
const root =
|
|
8145
|
+
const root = join9(homedir3(), ".claude", "projects");
|
|
8066
8146
|
if (!existsSync6(root)) return false;
|
|
8067
8147
|
for (const dir of readdirSync2(root))
|
|
8068
|
-
if (existsSync6(
|
|
8148
|
+
if (existsSync6(join9(root, dir, `${agentSessionId}.jsonl`))) return true;
|
|
8069
8149
|
return false;
|
|
8070
8150
|
};
|
|
8071
8151
|
var serverTerminalWsUrl = (server, sessionId) => `${server.replace(/^http/, "ws")}/workers/me/terminal/ws?sessionId=${sessionId}`;
|
|
@@ -8233,8 +8313,8 @@ var runWorkerDaemon = async (cfg, client, signal) => {
|
|
|
8233
8313
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
8234
8314
|
import { existsSync as existsSync8, mkdirSync as mkdirSync4, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "node:fs";
|
|
8235
8315
|
import { homedir as homedir4 } from "node:os";
|
|
8236
|
-
import { dirname as dirname3, join as
|
|
8237
|
-
var machineIdPath = (env = process.env) =>
|
|
8316
|
+
import { dirname as dirname3, join as join10 } from "node:path";
|
|
8317
|
+
var machineIdPath = (env = process.env) => join10(env.XDG_DATA_HOME ?? join10(env.HOME ?? homedir4(), ".local/share"), "baton", "machine-id");
|
|
8238
8318
|
var readOrCreateMachineId = (path = machineIdPath()) => {
|
|
8239
8319
|
if (existsSync8(path)) {
|
|
8240
8320
|
const s = readFileSync6(path, "utf8").trim();
|