@lesscap/baton-cli 0.7.3 → 0.7.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/baton.mjs +99 -16
- package/package.json +3 -3
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: {
|
|
@@ -5375,6 +5376,17 @@ var labelAttachments = (attachments) => {
|
|
|
5375
5376
|
var isMessageFor = (msg, name) => !msg.to || msg.to.length === 0 || msg.to.includes(name);
|
|
5376
5377
|
|
|
5377
5378
|
// ../shared/src/domain/session-event.ts
|
|
5379
|
+
var agentMessageText = (payload) => {
|
|
5380
|
+
if (typeof payload !== "object" || payload === null) return null;
|
|
5381
|
+
const event = payload;
|
|
5382
|
+
if (!["item.started", "item.updated", "item.completed"].includes(String(event.type))) return null;
|
|
5383
|
+
const item = event.item;
|
|
5384
|
+
if (typeof item !== "object" || item === null) return null;
|
|
5385
|
+
const { type, id, text } = item;
|
|
5386
|
+
if (type !== "agent_message" || typeof id !== "string" || typeof text !== "string") return null;
|
|
5387
|
+
const trimmed = text.trim();
|
|
5388
|
+
return trimmed ? { id, text: trimmed } : null;
|
|
5389
|
+
};
|
|
5378
5390
|
var startedMessageIds = (events) => {
|
|
5379
5391
|
const ids = /* @__PURE__ */ new Set();
|
|
5380
5392
|
for (const e of events) {
|
|
@@ -6462,6 +6474,7 @@ var findTranscriptPath = (agentSessionId) => {
|
|
|
6462
6474
|
};
|
|
6463
6475
|
var isRecord = (v) => typeof v === "object" && v !== null;
|
|
6464
6476
|
var textOf = (rec) => {
|
|
6477
|
+
if (typeof rec.text === "string") return rec.text.trim();
|
|
6465
6478
|
const message = rec.message;
|
|
6466
6479
|
const content = isRecord(message) ? message.content : rec.content;
|
|
6467
6480
|
if (typeof content === "string") return content.trim();
|
|
@@ -6492,6 +6505,30 @@ var parseFirstExchange = (content) => {
|
|
|
6492
6505
|
}
|
|
6493
6506
|
return userText || assistantText ? { userText, assistantText } : null;
|
|
6494
6507
|
};
|
|
6508
|
+
var parseFirstExchangeFromEvents = (events) => {
|
|
6509
|
+
let userText = "";
|
|
6510
|
+
const itemOrder = [];
|
|
6511
|
+
const agentText = /* @__PURE__ */ new Map();
|
|
6512
|
+
const legacyText = [];
|
|
6513
|
+
for (const event of events) {
|
|
6514
|
+
if (!userText && event.type === "user_message" && isRecord(event.payload)) {
|
|
6515
|
+
if (typeof event.payload.text === "string") userText = event.payload.text.trim();
|
|
6516
|
+
continue;
|
|
6517
|
+
}
|
|
6518
|
+
if (!userText) continue;
|
|
6519
|
+
if (event.type === "turn_complete" || event.type === "turn_error") break;
|
|
6520
|
+
const agent = event.type === "agent_event" ? agentMessageText(event.payload) : null;
|
|
6521
|
+
if (agent) {
|
|
6522
|
+
if (!agentText.has(agent.id)) itemOrder.push(agent.id);
|
|
6523
|
+
agentText.set(agent.id, agent.text);
|
|
6524
|
+
} else if (event.type === "sdk_event" && isRecord(event.payload)) {
|
|
6525
|
+
const text = event.payload.type === "assistant" ? textOf(event.payload) : "";
|
|
6526
|
+
if (text) legacyText.push(text);
|
|
6527
|
+
}
|
|
6528
|
+
}
|
|
6529
|
+
const assistantText = itemOrder.map((id) => agentText.get(id) ?? "").filter(Boolean).join("\n") || legacyText.join("\n");
|
|
6530
|
+
return userText || assistantText ? { userText, assistantText } : null;
|
|
6531
|
+
};
|
|
6495
6532
|
var readFirstExchange = (agentSessionId) => {
|
|
6496
6533
|
const path = findTranscriptPath(agentSessionId);
|
|
6497
6534
|
if (!path) return null;
|
|
@@ -7785,16 +7822,26 @@ var createLiveness = (deps) => {
|
|
|
7785
7822
|
import { spawn } from "node:child_process";
|
|
7786
7823
|
import { randomUUID } from "node:crypto";
|
|
7787
7824
|
import { existsSync as existsSync5 } from "node:fs";
|
|
7788
|
-
import { dirname as dirname2, join as
|
|
7825
|
+
import { dirname as dirname2, join as join8 } from "node:path";
|
|
7789
7826
|
import { fileURLToPath } from "node:url";
|
|
7790
7827
|
import { query as query2 } from "@anthropic-ai/claude-agent-sdk";
|
|
7791
7828
|
|
|
7829
|
+
// src/session/runner/title-codex.ts
|
|
7830
|
+
import { mkdtemp, rm } from "node:fs/promises";
|
|
7831
|
+
import { tmpdir as tmpdir3 } from "node:os";
|
|
7832
|
+
import { join as join7 } from "node:path";
|
|
7833
|
+
import { Codex as Codex2 } from "@openai/codex-sdk";
|
|
7834
|
+
|
|
7792
7835
|
// src/session/runner/title.ts
|
|
7793
7836
|
var clip = (s, max) => s.length > max ? s.slice(0, max) : s;
|
|
7794
7837
|
var isDeclined = (title) => title.replace(/[^a-z]/gi, "").toUpperCase() === "NONE";
|
|
7795
7838
|
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();
|
|
7839
|
+
var titleOutcome = (raw) => {
|
|
7840
|
+
const title = sanitizeTitle(raw);
|
|
7841
|
+
return !title || isDeclined(title) ? { kind: "declined" } : { kind: "titled", title };
|
|
7842
|
+
};
|
|
7796
7843
|
var TITLE_TIMEOUT_MS = 9e4;
|
|
7797
|
-
var
|
|
7844
|
+
var buildTitlePrompt = (userText, assistantText) => {
|
|
7798
7845
|
const exchange = [
|
|
7799
7846
|
`User: ${clip(userText, 800)}`,
|
|
7800
7847
|
assistantText ? `Assistant: ${clip(assistantText, 800)}` : ""
|
|
@@ -7829,7 +7876,7 @@ var generateTitle = async (input) => {
|
|
|
7829
7876
|
let failure = "";
|
|
7830
7877
|
try {
|
|
7831
7878
|
const messages = queryFn({
|
|
7832
|
-
prompt:
|
|
7879
|
+
prompt: buildTitlePrompt(userText, assistantText),
|
|
7833
7880
|
options: {
|
|
7834
7881
|
cwd: worktreePath,
|
|
7835
7882
|
// A one-shot summarizer, not an agent: replace the ~18k-token Claude
|
|
@@ -7869,8 +7916,41 @@ var generateTitle = async (input) => {
|
|
|
7869
7916
|
const tail = stderrTail.length > 0 ? ` | stderr: ${stderrTail.join(" / ")}` : "";
|
|
7870
7917
|
return { kind: "error", reason: `${failure}${tail}` };
|
|
7871
7918
|
}
|
|
7872
|
-
|
|
7873
|
-
|
|
7919
|
+
return titleOutcome(out);
|
|
7920
|
+
};
|
|
7921
|
+
|
|
7922
|
+
// src/session/runner/title-codex.ts
|
|
7923
|
+
var generateTitleWithCodex = async (input) => {
|
|
7924
|
+
const { userText, assistantText } = input;
|
|
7925
|
+
if (`${userText}${assistantText}`.trim().length < 4) return { kind: "declined" };
|
|
7926
|
+
const abort = new AbortController();
|
|
7927
|
+
const timer = setTimeout(() => abort.abort(), TITLE_TIMEOUT_MS);
|
|
7928
|
+
let cwd = "";
|
|
7929
|
+
try {
|
|
7930
|
+
cwd = await mkdtemp(join7(tmpdir3(), "baton-title-"));
|
|
7931
|
+
const client = input.client ?? new Codex2();
|
|
7932
|
+
const thread = client.startThread({
|
|
7933
|
+
workingDirectory: cwd,
|
|
7934
|
+
skipGitRepoCheck: true,
|
|
7935
|
+
sandboxMode: "read-only",
|
|
7936
|
+
approvalPolicy: "never",
|
|
7937
|
+
networkAccessEnabled: false,
|
|
7938
|
+
webSearchMode: "disabled"
|
|
7939
|
+
});
|
|
7940
|
+
const result = await thread.run(buildTitlePrompt(userText, assistantText), {
|
|
7941
|
+
signal: abort.signal
|
|
7942
|
+
});
|
|
7943
|
+
return titleOutcome(result.finalResponse);
|
|
7944
|
+
} catch (error) {
|
|
7945
|
+
return {
|
|
7946
|
+
kind: "error",
|
|
7947
|
+
reason: abort.signal.aborted ? `timeout after ${TITLE_TIMEOUT_MS / 1e3}s` : String(error).slice(0, 300)
|
|
7948
|
+
};
|
|
7949
|
+
} finally {
|
|
7950
|
+
clearTimeout(timer);
|
|
7951
|
+
if (cwd) await rm(cwd, { recursive: true, force: true }).catch(() => {
|
|
7952
|
+
});
|
|
7953
|
+
}
|
|
7874
7954
|
};
|
|
7875
7955
|
|
|
7876
7956
|
// src/worker/proc.ts
|
|
@@ -7885,7 +7965,7 @@ var killProcessGroup = (proc) => {
|
|
|
7885
7965
|
// src/worker/session-supervisor.ts
|
|
7886
7966
|
var binPath = () => {
|
|
7887
7967
|
const here = dirname2(fileURLToPath(import.meta.url));
|
|
7888
|
-
const devShim =
|
|
7968
|
+
const devShim = join8(here, "..", "..", "bin", "baton.mjs");
|
|
7889
7969
|
return existsSync5(devShim) ? devShim : fileURLToPath(import.meta.url);
|
|
7890
7970
|
};
|
|
7891
7971
|
var createSessionSupervisor = (deps) => {
|
|
@@ -7939,7 +8019,7 @@ var createSessionSupervisor = (deps) => {
|
|
|
7939
8019
|
if (!session2.agentSessionId || !worktreePath) {
|
|
7940
8020
|
const sessionCode = randomUUID();
|
|
7941
8021
|
const agentSessionId = session2.agentKind === "codex" ? `pending:${sessionCode}` : sessionCode;
|
|
7942
|
-
worktreePath =
|
|
8022
|
+
worktreePath = join8(worktreeDir, slug(`${name}-${sessionCode.slice(0, 8)}`));
|
|
7943
8023
|
const base = await syncedBase();
|
|
7944
8024
|
if (wasCanceled()) return log(`session #${sessionId} start canceled during git sync`);
|
|
7945
8025
|
createWorktree({
|
|
@@ -7957,7 +8037,7 @@ var createSessionSupervisor = (deps) => {
|
|
|
7957
8037
|
log(`recreated session #${sessionId} worktree (was missing) \u2192 ${worktreePath}`);
|
|
7958
8038
|
}
|
|
7959
8039
|
ensureExcluded(repo, PROJECT_CONFIG_NAME);
|
|
7960
|
-
saveProjectConfig(
|
|
8040
|
+
saveProjectConfig(join8(worktreePath, PROJECT_CONFIG_NAME), worktreeConfig(cfg));
|
|
7961
8041
|
if (hasTerminal(sessionId))
|
|
7962
8042
|
return log(`session #${sessionId} terminal opened mid-start \u2014 skipping headless start`);
|
|
7963
8043
|
spawnChild(sessionId, worktreePath);
|
|
@@ -8000,10 +8080,13 @@ var createSessionSupervisor = (deps) => {
|
|
|
8000
8080
|
};
|
|
8001
8081
|
const title = async (sessionId, agentSessionId, worktreePath) => {
|
|
8002
8082
|
const session2 = await client.sessions.get(sessionId);
|
|
8003
|
-
|
|
8004
|
-
const exchange = readFirstExchange(agentSessionId);
|
|
8083
|
+
const events = await client.sessions.listEvents(sessionId).catch(() => []);
|
|
8084
|
+
const exchange = parseFirstExchangeFromEvents(events) ?? (session2.agentKind === "claude-code" ? readFirstExchange(agentSessionId) : null);
|
|
8005
8085
|
if (!exchange) return log(`title #${sessionId}: no transcript yet, skipping`);
|
|
8006
|
-
const outcome = await
|
|
8086
|
+
const outcome = session2.agentKind === "codex" ? await generateTitleWithCodex({
|
|
8087
|
+
userText: exchange.userText,
|
|
8088
|
+
assistantText: exchange.assistantText
|
|
8089
|
+
}) : await generateTitle({
|
|
8007
8090
|
worktreePath,
|
|
8008
8091
|
userText: exchange.userText,
|
|
8009
8092
|
assistantText: exchange.assistantText,
|
|
@@ -8058,14 +8141,14 @@ var import_websocket_server = __toESM(require_websocket_server(), 1);
|
|
|
8058
8141
|
// src/worker/pty.ts
|
|
8059
8142
|
import { existsSync as existsSync6, readdirSync as readdirSync2 } from "node:fs";
|
|
8060
8143
|
import { homedir as homedir3 } from "node:os";
|
|
8061
|
-
import { join as
|
|
8144
|
+
import { join as join9 } from "node:path";
|
|
8062
8145
|
var MAX_TERMINALS = 10;
|
|
8063
8146
|
var ptyArgs = (agentSessionId, hasJsonl) => hasJsonl ? ["--resume", agentSessionId] : ["--session-id", agentSessionId];
|
|
8064
8147
|
var hasSessionJsonl = (agentSessionId) => {
|
|
8065
|
-
const root =
|
|
8148
|
+
const root = join9(homedir3(), ".claude", "projects");
|
|
8066
8149
|
if (!existsSync6(root)) return false;
|
|
8067
8150
|
for (const dir of readdirSync2(root))
|
|
8068
|
-
if (existsSync6(
|
|
8151
|
+
if (existsSync6(join9(root, dir, `${agentSessionId}.jsonl`))) return true;
|
|
8069
8152
|
return false;
|
|
8070
8153
|
};
|
|
8071
8154
|
var serverTerminalWsUrl = (server, sessionId) => `${server.replace(/^http/, "ws")}/workers/me/terminal/ws?sessionId=${sessionId}`;
|
|
@@ -8233,8 +8316,8 @@ var runWorkerDaemon = async (cfg, client, signal) => {
|
|
|
8233
8316
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
8234
8317
|
import { existsSync as existsSync8, mkdirSync as mkdirSync4, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "node:fs";
|
|
8235
8318
|
import { homedir as homedir4 } from "node:os";
|
|
8236
|
-
import { dirname as dirname3, join as
|
|
8237
|
-
var machineIdPath = (env = process.env) =>
|
|
8319
|
+
import { dirname as dirname3, join as join10 } from "node:path";
|
|
8320
|
+
var machineIdPath = (env = process.env) => join10(env.XDG_DATA_HOME ?? join10(env.HOME ?? homedir4(), ".local/share"), "baton", "machine-id");
|
|
8238
8321
|
var readOrCreateMachineId = (path = machineIdPath()) => {
|
|
8239
8322
|
if (existsSync8(path)) {
|
|
8240
8323
|
const s = readFileSync6(path, "utf8").trim();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lesscap/baton-cli",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.5",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -25,8 +25,8 @@
|
|
|
25
25
|
"@baton/shared": "0.0.0"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@anthropic-ai/claude-agent-sdk": "^0.3.
|
|
29
|
-
"@openai/codex-sdk": "^0.
|
|
28
|
+
"@anthropic-ai/claude-agent-sdk": "^0.3.220",
|
|
29
|
+
"@openai/codex-sdk": "^0.146.0",
|
|
30
30
|
"node-pty": "^1.1.0",
|
|
31
31
|
"ws": "^8.21.0"
|
|
32
32
|
},
|